Skip to main content

max / alloy_tui

6.1 KB · 69 lines History Blame Raw
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