Workflows
Task-oriented walkthroughs for shll, the meta-CLI for the shll toolkit. Each section starts from a goal and shows which shll command gets you there and what it actually does under the hood. For install paths and shell wiring, see Install & shell wiring.
Clean-machine bootstrap
From a fresh machine to a fully wired toolkit:
curl -fsSL https://shll.ai/install | sh # bootstrap: trust + install shll, then install the roster
shll setup shell # pure rc wiring — no trust flag
exec $SHELL # reload so the shell integration takes effect
The one-liner runs a preflight and a Homebrew bootstrap before expanding to the trust + install steps below (pass a subset with | sh -s -- hop wt). Minimal Ubuntu/Debian images ship without curl — sudo apt-get install -y curl before the one-liner. Step by step:
- Preflight. The script probes git (on macOS via
xcode-select -p— the/usr/bin/gitCLT shim false-positives), curl, and tmux, reporting every miss at once with its per-platform fix command. Missing curl (or git on a Linux machine without Homebrew) is fatal; missing tmux only warns, with its install hint. - Homebrew bootstrap (only when brew is absent). The official installer runs headlessly with
NONINTERACTIVE=1— on macOS this installs the Command Line Tools viasoftwareupdate; on Linux the preflight has already guaranteed git. The script then uses the absolute brew path, evalsbrew shellenvin-process, and prints the rc line to keep for future shells. An existing Homebrew (≥ 6.0.4; on 6.0.0–6.0.3,brew updatefirst) is used as-is and no bootstrap runs. brew trust --formula sahil87/tap/shll && brew install sahil87/tap/shllputs theshllbinary onPATH. Thebrew trustis required on Homebrew 6.0+ (which makes tap-trust a hard install requirement) — shll’s formula runs a sandboxed install that needs a real trust record. shll can’t trust its own formula before it exists, so this one-time bootstrap usesbrew trustdirectly.shll installwalks the roster (run-kit,rk-desktop,fab-kit,wt,idea,tu,hop) and, for each brew-managed tool you don’t already have, runsbrew trust --formula sahil87/tap/<formula>thenbrew install— so it owns trust for the six brew tools. rk-desktop is not a brew formula: it delegates tork desktop install(and skips with a note whenrkis absent or the platform is unsupported — never a failure). Idempotent — re-running installs only what’s still missing. Pass--no-trustto skip the trust step if you manage trust yourself; see the tap-trust troubleshooting. (Formula names are always tap-qualifiedsahil87/tap/<formula>— homebrew/core carries an unrelatedrun-kit, so a barebrew install run-kitinstalls the wrong software.)shll setup shellappends a single sentinel-wrapped eval block to your rc file. It is pure rc-wiring — trust isshll install’s job, not the shell half’s (there is no--trust-tapflag).exec $SHELLreloads the shell so the eval line takes effect;hop,wt, and the rest are now live.
Day-to-day: shll update
shll update
One command to upgrade everything you have installed. The sequence:
brew update --quiet, exactly once. A single metadata refresh for the whole run —shlltells each delegated per-tool update that advertises--skip-brew-updateto skip its own internalbrew update, so there’s no redundant refresh. (A tool predating that flag runs its ownbrew update, costing one extra refresh for that tool — seeshll update.)- Self-upgrade. If
shllitself was installed via brew, it runsbrew upgrade sahil87/tap/shllfirst. A from-sourceshllis skipped here (no formula to upgrade). - Per-tool upgrade, by delegation. For each installed roster tool,
shll updateinvokes that tool’s ownupdatesubcommand (with--skip-brew-updatewhen the tool advertises it) rather than callingbrew upgradedirectly. This preserves each tool’s post-upgrade side effects — e.g.rk’s daemon restart — that a barebrew upgradewould silently drop. rk-desktop delegates tork desktop update(it has no brew formula). A brew-managed tool that exposes noupdatesubcommand falls back tobrew upgrade.
Uninstalled tools are skipped (graceful degradation), and the loop is best-effort: a single tool’s failure doesn’t abort the rest. brew’s progress streams straight to your terminal.
shll update prints a [N/M] progress header before each tool and a timing summary tail at the end (Done — N of M tools succeeded in <dur>., or a X succeeded, Y failed in <dur> form on partial failure). The tail reports exit-code outcomes and run duration — it never claims “updated” vs. “up-to-date”, since the sub-tools’ own output streamed past.
Preview without changing anything:
shll update --dry-run
--dry-run runs the read-only probes (so the preview is accurate) but performs no writes — no brew update, no brew upgrade, no <tool> update. It prints an aligned table of the exact commands the real run would execute, in roster order (shll (self) first when brew-installed), then exits 0. The same flag exists on shll install.
You can also scope a run to specific tools: shll update hop wt upgrades just those (plus shll update shll for the self-upgrade alone). A named-but-not-installed target is an error here (unlike a whole-roster run, which silently skips it).
Composing shell-init
eval "$(shll shell-init zsh)"
shll shell-init <shell> concatenates the shell-init output of every installed shll tool, in roster order, into a single blob — replacing what would otherwise be one eval line per tool. (You normally don’t run this by hand; shll setup shell writes the eval line for you.)
The composition is eval-safe by construction, which matters because the output is fed straight to eval:
- A tool that isn’t installed (binary not on
PATH) is silently omitted — no error, no partial output. - A tool whose
shell-initerrors has its output dropped; the error note goes to stderr only, never into the eval’d stdout. shllinjects only#-prefixed comment separators (# ── <tool> ──) between blocks — shell no-ops, never executable code or color escapes.
So eval "$(shll shell-init zsh)" is safe even when shll exits non-zero or a sub-tool is broken: at worst you get a shell with one fewer integration loaded, never a parse error. The order is deterministic (roster order), so a composed blob reads the same way every time.
Version dump for bug reports
$ shll version
shll v0.0.5
run-kit v1.5.3
rk-desktop not installed
fab-kit v1.9.4
wt v0.0.5
idea v0.0.2
tu v0.4.13
hop v0.1.5
One column-aligned row for shll itself plus each roster tool — plain text, no colors, designed to paste cleanly into a Slack thread or GitHub issue. An uninstalled tool renders as not installed. Each tool’s version probe has a 2-second timeout (<tool> --version; rk-desktop probes rk desktop status instead), so one hung tool can’t block the dump (a timeout also shows as not installed); worst case the whole table finishes in well under 15 seconds even if every tool hangs.
The composition model
shll has no state, no database, and no special knowledge of the tools it wraps. Every subcommand is a thin coordinator over the per-tool CLIs and brew:
shll command | What it actually runs |
|---|---|
shll install | brew trust --formula sahil87/tap/<formula> then brew install sahil87/tap/<formula> per missing brew-managed tool (--no-trust skips the trust step); rk desktop install for rk-desktop |
shll update | brew update --quiet once, self-upgrade, then each installed tool’s own update (delegated; rk desktop update for rk-desktop; brew upgrade fallback only when a brew-managed tool has no update) |
shll shell-init zsh | concatenates the stdout of each installed tool’s <tool> shell-init zsh |
shll version | invokes <tool> --version per tool (rk desktop status for rk-desktop), formats as a table |
This is Constitution Principle IV — Composition, Not Replacement: hop update, wt shell-init, etc. continue to work standalone. shll’s only job is to fan out, collect output, and degrade gracefully when a tool is missing.
See also
- Install & shell wiring — every install path and the full
shll setup shellrc-wiring contract. - shll.ai — the always-current command reference.