Workflows
Workflows
Task-oriented walkthroughs for shll, the meta-CLI for the @sahil87 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:
brew install sahil87/tap/shll # or: brew install sahil87/tap/all
shll install # brew-installs every roster tool you're missing
shll shell-setup --trust-tap # wire your shell + record trust for sahil87/tap
exec $SHELL # reload so the shell integration takes effect
Step by step:
brew install sahil87/tap/shllputs theshllbinary onPATH. (Orbrew install sahil87/tap/allto pull the whole toolkit at once, in which case the next step is a no-op.)shll installwalks the roster (leaves-first:wt,idea,tu,rk,hop,fab-kit) andbrew installs each tool you don’t already have. Idempotent — re-running installs only what’s still missing.shll shell-setup --trust-tapappends a single sentinel-wrapped eval block to your rc file and records genuine Homebrew trust forsahil87/tap(silencing the “allowed by default” warning). Drop--trust-tapif you’d rather not change brew’s trust posture — see the tap-trust troubleshooting matrix.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. A 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 sahil87 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 shell-setup 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
wt v0.0.5
idea v0.0.2
tu v0.4.13
rk v1.5.3
hop v0.1.5
fab-kit v1.9.4
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 invocation has a 2-second timeout, 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 install sahil87/tap/<formula> per missing tool |
shll update | brew update --quiet once, self-upgrade, then each installed tool’s own update (delegated; brew upgrade fallback only when a 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, 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 shell-setuprc-wiring contract. - shll.ai — the always-current command reference.