Part of @sahil87's open source toolkit — see all projects there.
One command to install, update, and shell-wire every tool in the @sahil87 toolkit (wt, idea, tu, rk, hop, fab-kit). shll doesn't replace the per-tool CLIs — it composes them.
- One-shot install —
shll installrunsbrew install sahil87/tap/<formula>for every roster tool you don't already have. Idempotent and safe to re-run. - One-line shell integration —
shll shell-setupappends a single eval line to your rc file that wires uphop,wt, and any future toolkit shell-init in one block. No more managing four eval lines. - One update for everything —
shll updaterunsbrew updateonce, then upgrades every installed roster tool in sequence. Skips ones you don't have. Skips itself if it wasn't installed via brew. - Paste-friendly version dump —
shll versionprints one row per tool, ideal for bug reports. - At-a-glance roster —
shll listshows every managed tool with its install status, a one-line description, and its repo (plus--jsonfor scripting). - One-command health check —
shll doctorverifies each tool is installed, runnable, and shell-wired, with an actionable fix on every problem line.
Per-tool CLIs continue to work standalone — shll wraps them, it does not replace them.
From a clean 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 effectThat's it. hop, wt, and the other tools are now installed and their shell integration is live.
For the deeper install guide — brew vs the all meta-formula, from-source builds, the full shll shell-setup rc-wiring, and the tap-trust matrix — see docs/site/install.md.
--trust-tap records genuine Homebrew trust for sahil87/tap so brew stops nagging about non-official taps — drop it (shll shell-setup) to leave brew's tap-trust posture unchanged. See --trust-tap for what it does and the side effects, or Troubleshooting for the lighter alternatives.
brew install sahil87/tap/shllshll is also installed transitively via the all meta-formula (brew install sahil87/tap/all), which pulls in every roster tool at once.
For the full guide — brew vs all, from-source builds, shell wiring, and the --trust-tap ceremony — see docs/site/install.md.
git clone https://github.com/sahil87/shll.git
cd shll
just installBuilds the binary and copies it to ~/.local/bin/shll. Make sure that directory is on your $PATH.
shll install # install every missing roster tool
shll install hop wt # install only a named subset
shll install --dry-run # preview the brew install plan, change nothingIterates the roster in leaves-first order (wt, idea, tu, rk, hop, fab-kit) and runs brew install sahil87/tap/<formula> for each one that's missing. Already-installed tools are skipped silently. Does NOT upgrade — use shll update for that.
Pass one or more tool names to install only that subset (processed in roster order regardless of arg order); an unknown name is a hard error. Unlike shll update, shll itself is NOT a valid install target — you can't brew-install the running orchestrator. --dry-run runs the read-only install-status probes, prints the exact brew install commands the real run would execute, then exits without installing anything.
Each install prints a [N/M] progress header, and a timing summary tail closes the run.
shll update # upgrade shll + every installed roster tool
shll update shll # upgrade only shll itself
shll update hop wt # upgrade only a named subset
shll update --dry-run # preview the upgrade plan, change nothingRuns brew update --quiet once, then brew upgrade sahil87/tap/shll (when shll itself was installed via brew), then delegates to each installed roster tool's own update subcommand (passing --skip-brew-update when the tool advertises it) so each tool's post-upgrade side effects — e.g. rk's daemon restart — are preserved. A roster tool that exposes no update subcommand falls back to brew upgrade sahil87/tap/<formula>. Uninstalled tools are skipped silently, and the loop is best-effort — one tool's failure doesn't abort the rest. Brew and per-tool progress stream directly to your terminal.
Pass one or more tool names to scope the run to a subset (valid targets: shll, wt, idea, tu, rk, hop, fab-kit), processed in roster order regardless of arg order. A named-but-not-installed target is a hard error here (unlike the whole-roster sweep, which silently skips it). --dry-run runs the read-only probes, prints the exact commands the real run would execute (shll (self) first when brew-installed), then exits without writing anything.
Each tool gets a [N/M] progress header, and a timing summary tail (Done — N of M tools succeeded in <dur>.) closes the run.
Still works under the legacy alias
shll shell-install— same command, unchanged behavior.
shll shell-setup # auto-detect shell, append eval block to your rc file
shll shell-setup --print # dry-run: print the block to stdout, modify nothing
shll shell-setup --uninstall # clean removal of the block
shll shell-setup --trust-tap # also record genuine Homebrew trust for sahil87/tap
shll shell-setup --rc-file ~/.zshrc.local # override the target pathThe appended block is sentinel-wrapped and idempotent — re-running is a no-op when the lines are already present:
# >>> shll >>>
eval "$(shll shell-init zsh)"
# <<< shll <<<The rc file is opened with plain O_APPEND, so dotfile-manager symlinks (chezmoi, dotbot, stow, yadm) are preserved. Default targets: ${ZDOTDIR:-$HOME}/.zshrc for zsh, $HOME/.bash_profile (macOS) or $HOME/.bashrc (Linux) for bash.
--trust-tap is not a mode — it composes with the default, --print, and --uninstall paths. On a normal install it does the full genuine-trust setup in one command:
- Runs
brew trust --tap sahil87/tap(Homebrew's own trust ceremony — idempotent, safe to re-run). - Adds
export HOMEBREW_REQUIRE_TAP_TRUST=1to the shll block, so brew enforces explicit trust:
# >>> shll >>>
export HOMEBREW_REQUIRE_TAP_TRUST=1
eval "$(shll shell-init zsh)"
# <<< shll <<<It works whether or not you've already run shll shell-setup — the export line is merged into your existing block (no duplicates, no second block). --trust-tap --print shows the resulting combined block without touching anything. --uninstall removes the whole block (both lines) but does not run brew untrust — the trust record is inert without the policy line and harmless to leave; reverse it yourself with brew untrust --tap sahil87/tap if you want.
If your Homebrew is too old to ship brew trust (or brew isn't installed), --trust-tap degrades gracefully: it writes the eval line so you still get shell integration, skips the export line (setting it without a trust record would make brew block the tap), and tells you about the lighter env-var alternatives below.
If you'd rather wire the eval line by hand, this is what shll shell-setup writes to your rc file:
eval "$(shll shell-init zsh)" # in ~/.zshrc
eval "$(shll shell-init bash)" # in ~/.bashrcThe output is the concatenation (in roster order — leaves-first: wt, idea, tu, rk, hop, fab-kit) of every installed sahil87 tool's own shell-init, with a # ── <tool> ── comment separator before each block. What each roster tool is for, and what it adds to your shell:
| Tool | What it's for | What it adds to your shell |
|---|---|---|
wt |
git worktree manager — create, switch, and clean up worktrees | wt shell function wrapper (so the "Open here" menu option can cd your shell), completion |
idea |
worktree-aware idea / backlog capture from the terminal (markdown-first) | completion |
tu |
AI coding-assistant cost/usage tracker (Claude Code, Codex, OpenCode) | completion |
rk |
run-kit — web-based tmux orchestration for parallel agent workspaces | completion |
hop |
fast directory navigation / bookmarks (cd on steroids) |
hop shell function (bare-name cd, verb dispatch, tool-form), h / hi aliases, completion |
fab-kit |
fab — spec-driven change workflow (this repo's own pipeline) |
completion |
hop and wt are the only tools that ship shell functions — those need eval-time installation because a function defined inside the binary can't escape into the parent shell. Everything else is completion, which the shell sources lazily on tab. The output is eval-safe: a tool that isn't installed is silently omitted, and a tool whose shell-init errors has its output dropped (the error goes to stderr only). Per-tool <tool> shell-init <shell> continues to work standalone if you'd rather wire them up individually.
$ 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.4One row for shll itself plus each roster tool, in roster order. Uninstalled tools render as not installed. Each tool's --version call has a 2-second timeout, so one hung tool can't block the dump — a timeout also shows as not installed. Drop the whole block into a bug report.
$ shll list
ok shll the manager for the shll toolkit https://github.com/sahil87/shll
ok wt Git worktree management — create, list, open, delete worktrees https://github.com/sahil87/wt
ok idea Backlog idea management from the terminal https://github.com/sahil87/idea
ok tu Token-usage tracker for AI coding tools (Claude Code, Codex, OpenCode) https://github.com/sahil87/tu
ok rk Run-kit — tmux session manager with a web UI https://github.com/sahil87/run-kit
ok hop Fast directory/project jumping across worktrees https://github.com/sahil87/hop
ok fab-kit Spec-driven workspace & workflow toolkit (the `fab` CLI) https://github.com/sahil87/fab-kitshll leads, then one row per managed tool in roster order: an install-status marker (ok / --, or a green ✓ / red ✗ on a terminal), the name, a one-line description, and the source-repo URL. The leading shll row is the manager itself — it's surfaced so the toolkit reads as one family with shll as its manager-member (the same shll-first ordering shll version and shll update already use). Install status reuses the same PATH probe as shll version (it's install-mechanism agnostic, not a Homebrew check); a missing tool is shown as missing, never an error, so shll list always exits 0.
shll list --json # JSON array, no color — pipe into jq--json emits a {name, description, repo, installed} array (repo is the full resolved URL), suitable for shll list --json | jq. The leading shll object additionally carries "self": true (absent on the six managed tools), so a script driving brew install can recover just the managed set with jq 'map(select(.self != true))'.
$ shll doctor
shll OK v0.0.16
wt OK v0.0.16 wired
idea OK v0.0.7
tu OK v0.4.17 wired
rk OK v2.2.3
hop OK v0.1.16 wired
fab-kit OK v2.1.1shll leads with an always-OK row (it's the running binary; version from the build, no wiring check), then for every roster tool doctor checks that (1) the binary is on PATH, (2) it reports a version (so a half-installed or stale brew link is caught), and (3) — for the tools that ship shell integration (wt, tu, hop) — shll's composed eval block is present in your rc file. Each tool gets one line with an OK / WARN / FAIL marker, and every non-OK line carries an actionable suggestion (e.g. run 'brew install …', or not wired — run 'shll shell-setup' then 'exec $SHELL'). The always-OK shll row never affects the exit code or the problem count (a single roster failure still reads 1 of 6, never 1 of 7).
A missing or non-running binary is FAIL; an installed-but-unwired tool is WARN (it still works when invoked directly). doctor is strictly read-only — it never installs, upgrades, or edits your rc file — and it exits non-zero if any tool is FAIL, so it's scriptable in CI. Pass --json for a machine-readable array (one object per tool) under the same checks and exit contract.
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:
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 |
shll list |
probes each tool's install status, renders the roster (name, description, repo) |
shll doctor |
probes <tool> --version + reads your rc file, reports install + wiring health |
Per 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.
Running shll update (or any shll command that touches brew) may print something like:
Warning: Tap sahil87/tap is allowed by default.
Homebrew will require explicit trust for non-official taps in a future release.
Set `HOMEBREW_REQUIRE_TAP_TRUST=1` to require explicit trust now or
`HOMEBREW_NO_REQUIRE_TAP_TRUST=1` to keep allowing by default.
Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
This is a Homebrew env-hint, not a shll error. shll surfaces it only because it wraps brew — and because shll update shells out to brew several times (brew update, the shll self-upgrade, per-tool upgrades), the same hint can print 2–3× per command. It means brew hasn't been told whether you trust the non-official sahil87/tap.
Recommended fix — record genuine trust:
shll shell-setup --trust-tapSee shll shell-setup --trust-tap above for what this does (the brew trust ceremony, the HOMEBREW_REQUIRE_TAP_TRUST=1 export, and how it composes with --print/--uninstall).
Lighter alternatives (set these yourself if you prefer):
| Env var | Effect |
|---|---|
export HOMEBREW_NO_REQUIRE_TAP_TRUST=1 |
Keep allowing non-official taps by default; stop nagging. Punts the trust decision. |
export HOMEBREW_NO_ENV_HINTS=1 |
Silence all brew env-hints (blunt — hides future hints too). |
shll will not set these for you. Trusting a tap — or opting out of the warning — is your decision; --trust-tap only persists a choice you made by typing it.
- docs/site/install.md — install & shell-wiring guide (brew vs
all, from-source,shll shell-setup, tap-trust) - docs/site/workflows.md — task-oriented walkthroughs (clean-machine bootstrap, day-to-day
shll update, version dumps, the composition model) shll --help— full subcommand listing- Command reference at shll.ai/tools/shll/commands — a browsable, always-current command tree. On every release, shll's CI exports its CLI help tree as a machine-readable
help/shll.jsonand publishes it to shll.ai, which renders it at that page. The export is produced by a hiddenhelp-dumpsubcommand (internal build tooling, not a user command). - Per-tool repos for the wrapped CLIs: fab-kit · run-kit · tu · hop · wt · idea