Skip to content

Commit f8e714a

Browse files
authored
Merge pull request #4 from sahil87/260510-vul4-shell-install-rc
2 parents c20df6a + d881389 commit f8e714a

13 files changed

Lines changed: 1632 additions & 49 deletions

File tree

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
55
`shll` is a meta-CLI for the sahil87 toolkit. It composes operations that span every per-tool CLI (`hop`, `wt`, `fab-kit`, `rk`, `tu`, `idea`) so you have one entry point for cross-toolkit concerns.
66

7-
Three subcommands today:
7+
Four subcommands today:
88

99
| Command | Purpose |
1010
|---------|---------|
1111
| `shll update` | `brew update` then `brew upgrade` for every installed sahil87 tool |
1212
| `shll shell-init <shell>` | Single eval-safe shell-init blob composed from all installed sahil87 tools that expose one |
13+
| `shll shell-install [shell]` | Append the `shll shell-init` eval line to your shell rc file (idempotent; supports `--print` and `--uninstall`) |
1314
| `shll version` | Versions of `shll` itself plus every installed sahil87 tool, plain text, paste-friendly for bug reports |
1415

1516
Per-tool CLIs continue to work standalone — `shll` wraps them, it does not replace them.
@@ -28,6 +29,14 @@ brew install sahil87/tap/shll
2829
brew install sahil87/tap/all # installs every sahil87 tool, including shll
2930
```
3031

32+
Then wire shell integration into your rc file with one command:
33+
34+
```sh
35+
shll shell-install # auto-detect shell, append eval line to your rc file
36+
```
37+
38+
`shll shell-install` is idempotent — re-running is a no-op when the line is already present. It preserves dotfile-manager symlinks (chezmoi, dotbot, stow, yadm). Supports `--print` (dry-run) and `--uninstall` (clean removal). If you'd rather edit the rc file by hand, the manual fallback is below under [Single-line shell integration](#single-line-shell-integration).
39+
3140
### From source
3241

3342
```sh

docs/memory/cli/commands.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# cli/commands
22

3-
Top-level command surface for the `shll` binary — the cobra root, the three subcommands it wires up, the exit-code translation layer, and the hardcoded tool roster every subcommand consumes.
3+
Top-level command surface for the `shll` binary — the cobra root, the four subcommands it wires up, the exit-code translation layer, and the hardcoded tool roster every subcommand consumes.
44

55
## Binary entry point
66

@@ -15,34 +15,35 @@ Top-level command surface for the `shll` binary — the cobra root, the three su
1515

1616
- `Use: "shll"`
1717
- `Short: "meta-CLI for the sahil87 toolkit"`
18-
- A `Long` block listing the three subcommands and noting that per-tool CLIs continue to work standalone.
18+
- A `Long` block listing the four subcommands and noting that per-tool CLIs continue to work standalone.
1919
- `SilenceUsage: true` and `SilenceErrors: true` — usage is not printed on RunE errors, and cobra's default error printer is suppressed. The `translateExit` layer in `main.go` owns stderr.
20-
- `AddCommand` for `newUpdateCmd()`, `newShellInitCmd()`, `newVersionCmd()`.
20+
- `AddCommand` for `newUpdateCmd()`, `newShellInitCmd()`, `newShellInstallCmd()`, `newVersionCmd()`.
2121

22-
Per Constitution VII (Minimal Surface Area), the v0.1.0 surface is exactly these three subcommands. Adding a new top-level subcommand requires explicit justification in the change's intake.
22+
Per Constitution VII (Minimal Surface Area), the v0.1.0 surface is exactly these four subcommands. Adding a new top-level subcommand requires explicit justification in the change's intake.
2323

2424
### Constitution VII justification per subcommand
2525

26-
These were locked in at spec time (Design Decision #1) and are reproduced here:
26+
These are locked in at spec time and are reproduced here:
2727

2828
- **`update`** — solves the no-single-update-command pain (`brew upgrade sahil87/tap/all` does NOT propagate to deps). Cannot be a flag on an existing tool because the entry point itself is what's missing.
2929
- **`shell-init`** — solves the cold-start cost and N-eval-line burden when multiple shell-integrating tools are installed. Per-tool `shell-init` keeps working standalone (Constitution IV).
30+
- **`shell-install`** — solves the manual-rc-edit cliff in the post-`brew install` onboarding flow. Cannot be a flag on `shell-init` (it *invokes* `shell-init`, so making it a sub-flag is structurally self-referential). Cannot live in a per-tool CLI (per-tool CLIs emit their own shell-init; this command writes the cross-tool composition `eval "$(shll shell-init <shell>)"`, which is exactly what shll exists for).
3031
- **`version`** — solves the bug-report triage pain. Cannot live on a per-tool CLI because the value is the cross-tool aggregation.
3132

3233
## Exit-code translation
3334

3435
`translateExit(err error) int` in `src/cmd/shll/main.go:38` is the single mapping from `RunE` errors to OS exit codes. It uses two error sentinels defined in `src/cmd/shll/main.go`:
3536

3637
- `errSilent = errors.New("shll: silent error")` (`src/cmd/shll/main.go:58`) — returned by subcommands that have already written their own diagnostic to stderr. Maps to exit code 1; `translateExit` does not write anything else.
37-
- `errExitCode{code, msg}` (`src/cmd/shll/main.go:63`) — used when a subcommand needs an exit code other than 0 or 1. Today only `shll shell-init` uses this, exiting 2 on bad/missing shell argument. If `msg` is non-empty, `translateExit` writes it to stderr.
38+
- `errExitCode{code, msg}` (`src/cmd/shll/main.go:63`) — used when a subcommand needs an exit code other than 0 or 1. Today `shll shell-init` and `shll shell-install` use this, exiting 2 on bad/missing shell argument and on related user-invocation errors (missing rc file, mutually-exclusive flags). If `msg` is non-empty, `translateExit` writes it to stderr.
3839

3940
Default fallback: any other error is printed to stderr and exits 1.
4041

4142
This layered design keeps cobra's own error printing out of the way (`SilenceErrors: true`) and concentrates exit-code policy in one place. The hop binary uses the same pattern; shll mirrors it.
4243

4344
## Subcommand factory pattern
4445

45-
Every subcommand follows `newXxxCmd()` returning `*cobra.Command` (no globals, no init() side effects). The cobra command's `RunE` calls a thin top-level helper (`runUpdate`, `runShellInit`, `runVersion`) that takes explicit `io.Writer` arguments — this is the test seam: tests drive these directly with `bytes.Buffer` writers and a fake `proc.Runner`.
46+
Every subcommand follows `newXxxCmd()` returning `*cobra.Command` (no globals, no init() side effects). The cobra command's `RunE` calls a thin top-level helper (`runUpdate`, `runShellInit`, `runShellInstall`, `runVersion`) that takes explicit `io.Writer` arguments — this is the test seam: tests drive these directly with `bytes.Buffer` writers and a fake `proc.Runner` (or, for `shell-install`, no fake — the command does file I/O only).
4647

4748
## Hardcoded tool roster
4849

@@ -76,6 +77,7 @@ Roster invariants:
7677
| `brew.go` | Shared brew helpers used by every subcommand: `hasBrew`, `isInstalled`, `brewBinary`, `brewMissingHint`. See [update](update.md) for details. |
7778
| `update.go` | `newUpdateCmd()` + `runUpdate`. See [update](update.md). |
7879
| `shell_init.go` | `newShellInitCmd()` + `runShellInit`. See [shell-init](shell-init.md). |
80+
| `shell_install.go` | `newShellInstallCmd()` + `runShellInstall`. See [shell-install](shell-install.md). |
7981
| `version.go` | `newVersionCmd()` + `runVersion`. See [version](version.md). |
8082

8183
Each command file has a paired `_test.go` (test-alongside per `code-quality.md`).
@@ -85,4 +87,4 @@ Each command file has a paired `_test.go` (test-alongside per `code-quality.md`)
8587
- Constitution I (Security First) → all subprocesses go through [`internal/proc`](../internal/proc.md).
8688
- Constitution III (Wrap, Don't Reinvent) + IV (Composition, Not Replacement) → every subcommand shells out; nothing reimplements brew or per-tool logic.
8789
- Constitution V (Graceful Degradation) → uninstalled tools never produce errors; missing tools are skipped silently.
88-
- Constitution VII (Minimal Surface Area) → subcommand list is closed at three for v0.1.0.
90+
- Constitution VII (Minimal Surface Area) → subcommand list is closed at four for v0.1.0 (`update`, `shell-init`, `shell-install`, `version`).

docs/memory/cli/shell-init.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,5 @@ Covered scenarios:
9999
- Roster definition and `<shell>` placeholder: [cli/commands](commands.md#hardcoded-tool-roster).
100100
- Subprocess wrapper conventions: [internal/proc](../internal/proc.md).
101101
- Brew detection (`isInstalled`): [cli/update](update.md#detection).
102+
- Rc-file installer: [cli/shell-install](shell-install.md) — wraps `shll shell-init <shell>` in an `eval` line and writes it to the user's rc file (idempotent install / `--print` dry-run / `--uninstall` removal).
102103
- Constitution V (Graceful Degradation) — uninstalled tools omitted silently.

0 commit comments

Comments
 (0)