| 1 |
# Shell |
| 2 |
|
| 3 |
Nushell as the login shell, bash unchanged as `/bin/sh` and `/bin/bash`. Companion to [STACK.md](STACK.md) and [CONSOLE.md](CONSOLE.md). |
| 4 |
|
| 5 |
## Thesis |
| 6 |
|
| 7 |
Alloy's audience writes their own scripts and reaches for the shell as a tool-glue surface, not just an interactive prompt. That reframes the shell pick away from "safe universal default" and toward "which shell makes daily tool-glue actually good." Nushell wins that on structural grounds (structured pipelines with typed values instead of stringly-typed byte streams), and every daily interaction compounds the advantage. |
| 8 |
|
| 9 |
The pick is coherent with the rest of Alloy: `alloy console` treats configs as typed data, `alloy_tui` treats widgets as functions over state, and now the shell treats commands as functions over typed pipelines. Same principle at three layers. |
| 10 |
|
| 11 |
## Architecture: nu is login-only |
| 12 |
|
| 13 |
The pick that keeps the ecosystem tax bounded: |
| 14 |
|
| 15 |
- **Login shell:** `nu`. What the user sees when they open rio, when they SSH in, when they open a scratch pane. |
| 16 |
- **`/bin/sh`:** bash, unchanged. Every `curl | sh` invokes this. Untouched. |
| 17 |
- **`/bin/bash`:** bash, unchanged. Every `#!/bin/bash` script runs here. Untouched. |
| 18 |
- **`/etc/shells`:** contains both nu and bash. `chsh -s /bin/bash` reverts a user to bash cleanly and reversibly. |
| 19 |
|
| 20 |
**Consequence: `curl -fsSL https://example.com/install.sh | sh` works identically to any other distro.** The login shell is not in the invocation path. This was the biggest concern about picking a non-POSIX shell, and it turns out to be a misread of how `curl | sh` actually resolves. |
| 21 |
|
| 22 |
**Consequence: every `#!/bin/bash` or `#!/usr/bin/env bash` script the user writes or downloads runs in bash.** Their own tool-glue scripts can be nu (`#!/usr/bin/env nu`), which is where the structured-pipeline advantage lives, but nothing forces them to. |
| 23 |
|
| 24 |
## What Alloy ships |
| 25 |
|
| 26 |
`etc/skel/.config/nushell/` in the Alloy repo: |
| 27 |
|
| 28 |
- `config.nu`: main nu configuration. Table style, history (sqlite backend), fuzzy completions, Alloy color palette applied to nu's own output, integration hooks for starship / zoxide / direnv (conditional on presence). |
| 29 |
- `env.nu`: environment. `EDITOR=hx`, `VISUAL=hx`, PATH additions for `~/.local/bin` and `~/.cargo/bin`, cache directory prep for integration inits. |
| 30 |
- `aliases.nu`: a small curated set (`ll`, `la`, `..`, `...`, git shortcuts). No plugin framework. |
| 31 |
|
| 32 |
The Alloy image installs these into `~/.config/nushell/` on first boot (or symlinks; packaging decision at v0). |
| 33 |
|
| 34 |
## Interoperability |
| 35 |
|
| 36 |
Where daily-use friction shows up, and how Alloy handles it: |
| 37 |
|
| 38 |
- **Tool init lines.** `eval "$(starship init bash)"` and equivalents. Starship, zoxide, direnv, atuin, and carapace all ship `--init nu`. For the long tail of tools that don't, Alloy ships small wrapper snippets in `etc/skel/.config/nushell/integrations/` as they're needed, added on demand rather than preemptively. |
| 39 |
- **Interactive one-liners.** `FOO=bar cmd && cmd2` becomes `FOO=bar; cmd; cmd2` (or `try / catch` for the conditional flavor). Muscle-memory tax; not a real barrier. |
| 40 |
- **`export`.** `$env.FOO = "bar"` in nu, `export FOO=bar` in bash. Both work in their respective contexts. |
| 41 |
- **Piped stdin.** Third-party CLIs that expect line-oriented text stdin: nu's default output is a table, but piping to an external command auto-flattens to text. Explicit control via `| to text` when needed. |
| 42 |
- **Command-substitution.** `` `foo` `` becomes `(foo)` in nu. Small learn. |
| 43 |
|
| 44 |
## Accepted costs |
| 45 |
|
| 46 |
- **Pre-1.0 velocity.** Nushell is currently in the 0.100.x range. Breaking changes across releases have slowed dramatically since 0.75+ (era of "every release rewrites your config" is over), but a couple of key renames per year still happen. Alloy accepts drift maintenance for `config.nu` the same way it does for `userChrome.css`. |
| 47 |
- **Documentation tax.** Google results for "how do I X on the command line" assume bash. Fish translations are usually a nearby SO answer; nu translations often require a trip to nushell.sh's book. Real cost, absorbed by the "audience that reads docs deliberately" targeting. |
| 48 |
- **Command name collisions.** Nu's built-in `ls`, `cd`, `cp`, `ps` behave differently from GNU equivalents (structured output, different flags). Users learn the mapping. Strictly nicer for interactive use once learned. |
| 49 |
- **Ecosystem depth.** Fewer third-party CLIs ship `--init nu` than `--init fish` or `--init zsh`. Alloy backfills as needed via `etc/skel/.config/nushell/integrations/`. |
| 50 |
|
| 51 |
## Prompt |
| 52 |
|
| 53 |
**starship** (Rust, cross-shell). Boring, works, ships `starship init nu` first-class. Alloy configures starship's own palette to match the tokens. |
| 54 |
|
| 55 |
A custom nu prompt as a small design-system exercise is a legitimate future project (nushell's prompt hook is rich enough to author an Alloy prompt directly), but not v0 scope. Deferred. |
| 56 |
|
| 57 |
## Rejected |
| 58 |
|
| 59 |
- **fish.** Excellent shell, safer pick. Rejected because the audience is script-writers, not paste-and-forget users, so nu's structural advantages earn their cost. If nu turns out to be the wrong call in practice, fish is the fallback and the switch is small (login-shell only, `chsh -s /usr/bin/fish`). |
| 60 |
- **zsh.** Mostly-POSIX and powerful, but raw zsh is unpleasant and requires a config framework (oh-my-zsh, zinit) to be tolerable. Adopting a framework means importing someone else's opinions, which Alloy avoids everywhere else. |
| 61 |
- **bash.** No opinion, works. Best UX-out-of-box is worst-in-class. Alloy is opinionated everywhere; being un-opinionated at the shell layer specifically feels like leaving value on the table. |
| 62 |
- **elvish, xonsh, oil.** Interesting, small communities. Adopting them would put Alloy in a "maintaining half the ecosystem" position. |
| 63 |
|
| 64 |
## Open questions |
| 65 |
|
| 66 |
- [x] **Root login shell: bash.** Recovery is the wrong time to hit a shell surprise. Nu is in `/etc/shells` and reachable via `chsh -s /usr/bin/nu` for users who want consistency. |
| 67 |
- [x] **History file backend: sqlite.** Better interactive search; atuin (if adopted later) reads sqlite natively, so no migration on the future path. |
| 68 |
- [x] **atuin default: defer.** Encrypted history sync is a real feature but sits adjacent to CONTINUITY.md's Tailscale/Syncthing story and wants its own design pass. Users install atuin themselves in the meantime. |
| 69 |
|