You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
|`brew.go`| Shared brew helpers used by every subcommand: `hasBrew`, `isInstalled`, `brewBinary`, `brewMissingHint`, `installBrewMissingHint`, `shllFormula`. See [update](update.md) for details. |
78
+
|`brew.go`| Shared brew helpers used by the brew-coupled subcommands (`install`, `update`): `hasBrew`, `isInstalled`, `brewBinary`, `brewMissingHint`, `installBrewMissingHint`, `shllFormula`. `shell-init` and `version` are install-mechanism agnostic and do NOT consult brew — they detect runnable tools via `proc.ErrNotFound` from the sub-tool invocation itself. See [update](update.md) for details. |
79
79
|`install.go`|`newInstallCmd()` + `runInstall`. See [install](install.md). |
80
80
|`update.go`|`newUpdateCmd()` + `runUpdate`. See [update](update.md). |
81
81
|`shell_init.go`|`newShellInitCmd()` + `runShellInit`. See [shell-init](shell-init.md). |
Copy file name to clipboardExpand all lines: docs/memory/cli/shell-init.md
+9-9Lines changed: 9 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
`shll shell-init <shell>` — emits a single concatenated shell-init blob composed from every installed roster tool with shell integration.
4
4
5
-
Source: `src/cmd/shll/shell_init.go`. Uses the shared brew helpers in `src/cmd/shll/brew.go` and the `Roster` from `src/cmd/shll/tools.go`.
5
+
Source: `src/cmd/shll/shell_init.go`. Uses the `Roster` from `src/cmd/shll/tools.go`. Does **not** call brew — install-state is detected via `proc.ErrNotFound` from the sub-tool invocation itself (i.e. "is the binary on PATH?"), which is install-mechanism agnostic (brew, from-source via `just install`, etc.).
6
6
7
7
## Usage
8
8
@@ -15,18 +15,18 @@ A single eval line replaces what would otherwise be N per-tool eval lines (today
15
15
16
16
## Behavior contract
17
17
18
-
`runShellInit(ctx, shell, stdout, stderr)` (`src/cmd/shll/shell_init.go:65`) is the implementation seam. The cobra `RunE` wrapper handles argument validation before delegating:
18
+
`runShellInit(ctx, shell, stdout, stderr)` (`src/cmd/shll/shell_init.go:74`) is the implementation seam. The cobra `RunE` wrapper handles argument validation before delegating:
2.**Unsupported shell.** Argument is not in `supportedShells = []string{"zsh", "bash"}` → return `errExitCode{code: 2, msg: ...}`. Exit code: **2**. stdout: empty.
23
23
24
24
3.**Composition loop.** For each tool in `Roster` (in order):
25
25
- Skip if `len(tool.ShellInit) == 0` (tool has no shell integration).
26
-
- Skip silently if `!isInstalled(ctx, tool.Formula)` — Constitution V (graceful degradation).
27
-
- Otherwise build argv via `substituteShell(tool.ShellInit, shell)` (replaces every `"<shell>"` token with the user-supplied shell name).
26
+
- Build argv via `substituteShell(tool.ShellInit, shell)` (replaces every `"<shell>"` token with the user-supplied shell name).
28
27
- Run `proc.Run(ctx, argv[0], argv[1:]...)` (capture transport).
29
-
- On error: write `shll shell-init: <tool>: <err>` to stderr, set `anyFailed = true`, **and skip this tool's stdout** (eval-safety — failing tool's partial output never reaches stdout). Continue with the next tool.
28
+
- On `proc.ErrNotFound` (binary not on PATH): skip silently — Constitution V (graceful degradation). Install-mechanism agnostic: any source-built or non-brew install of the tool participates as long as its binary is on PATH.
29
+
- On any other error: write `shll shell-init: <tool>: <err>` to stderr, set `anyFailed = true`, **and skip this tool's stdout** (eval-safety — failing tool's partial output never reaches stdout). Continue with the next tool.
30
30
- On success: write the captured stdout bytes verbatim to the output writer.
@@ -51,7 +51,7 @@ Output is concatenated in `Roster` order. This is deterministic (Spec: Compositi
51
51
52
52
## Argv substitution
53
53
54
-
`substituteShell(argv, shell)` (`src/cmd/shll/shell_init.go:98`) replaces every literal `<shell>` token with `shell`, returning a fresh slice (does not mutate the roster):
54
+
`substituteShell(argv, shell)` (`src/cmd/shll/shell_init.go:107`) replaces every literal `<shell>` token with `shell`, returning a fresh slice (does not mutate the roster):
55
55
56
56
| Tool | Roster argv | After substitution (zsh) |
57
57
|------|-------------|--------------------------|
@@ -97,7 +97,7 @@ Covered scenarios:
97
97
## Cross-references
98
98
99
99
- Roster definition and `<shell>` placeholder: [cli/commands](commands.md#hardcoded-tool-roster).
- Subprocess wrapper conventions: [internal/proc](../internal/proc.md) — including `proc.ErrNotFound` semantics.
101
+
- Brew detection (`isInstalled`) — used by `install` and `update` only, not here: [cli/update](update.md#detection).
102
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).
103
-
- Constitution V (Graceful Degradation) — uninstalled tools omitted silently.
103
+
- Constitution V (Graceful Degradation) — tools not on PATH are omitted silently.
2. Run `proc.Run(subCtx, tool.Name, "--version")` (capture transport).
37
+
3. On any error (`proc.ErrNotFound` for missing binary, exit non-zero, deadline exceeded, etc.) → return `notInstalledLabel = "not installed"`.
38
+
4. On success → return `normalizeVersion(string(out))`.
40
39
41
-
`normalizeVersion(raw string) string` (`src/cmd/shll/version.go:93`) is the single point of normalization shared by the shll row and every roster row. It is purely shape-based — there is no per-tool branching — so independent upstream `--version` standardization (e.g., tu/rk/fab-kit cleaning up their own output in parallel) is absorbed without shll code changes.
40
+
"Installed" is detected via `proc.ErrNotFound` (binary not on PATH) rather than a brew probe — install-mechanism agnostic, and saves ~400ms per tool (no Homebrew/Ruby startup tax).
41
+
42
+
`normalizeVersion(raw string) string` (`src/cmd/shll/version.go:95`) is the single point of normalization shared by the shll row and every roster row. It is purely shape-based — there is no per-tool branching — so independent upstream `--version` standardization (e.g., tu/rk/fab-kit cleaning up their own output in parallel) is absorbed without shll code changes.
42
43
43
44
The normalization pipeline runs in this order on the input:
- 2s is generous (typical `--version` runs in well under 100ms).
72
73
- Bounds worst-case `shll version` runtime to `len(Roster) * versionTimeout` ≈ 12 seconds even if every tool hangs.
73
74
- A timeout is treated as "not installed" — we don't differentiate hung-but-installed from missing in the output. The user gets a usable table either way.
74
-
- The deadline applies only to the `--version` invocation, not to the `brew list` probe. The probe runs against the parent ctx (typically unbounded for the CLI invocation).
75
+
- The deadline applies only to the `--version` invocation. There is no separate install probe — installation is inferred from `proc.ErrNotFound` returned by the same `--version` call.
75
76
76
77
`TestVersion_TimeoutHandling` simulates the timeout path by having the fake runner return `context.DeadlineExceeded` immediately for the targeted tool (no real wall-clock wait), then asserts the row reads `not installed` and that the test's elapsed time stays under `versionTimeout`.
77
78
@@ -109,6 +110,6 @@ Unit scenarios pinning the normalization contract (12 cases, all named `TestNorm
0 commit comments