Terminal context, ,, repair, stdin piping, autostart, and per-tier model routing - #4
Merged
Merged
Conversation
35-second arc covering the four killer flows: 1. `, find the five largest files` → fzf pick → executes 2. `, the same but…` → sticky-session refinement 3. `? what does git stash do` → streaming markdown answer 4. `??? git stash` → bare-query recall hits the prior session 5. `??? --archives 5` → browse what's been saved Reproducible via `vhs demo.tape` once llama-server is up (??). The GIF itself isn't committed yet — render when the CLI shape is stable and we're ready to push externally.
Two BYOK paths so shellllm can run against more than just a local
llama-server, without giving up the offline-first defaults.
1) JS rendering for fetch_url
SPAs that paint after the initial HTML used to come back empty from
fetch_url. Now, when SHELLLM_RENDER_URL + SHELLLM_RENDER_API_KEY are
set, fetch_url POSTs to {url}/v1/scrape (Firecrawl-compatible) with
Bearer auth and asks for markdown. Any failure — connect refused,
4xx/5xx, timeout, weird payload — falls back transparently to the
existing static fetcher.
Works against:
- hosted firecrawl.dev
- self-hosted mendableai/firecrawl
- any service speaking the same /v1/scrape shape
2) OpenAI-compatible chat + embedding endpoints
SHELLLM_API_KEY → Authorization: Bearer on every chat request.
SHELLLM_EMBED_API_KEY → same, for embeddings (falls back to
SHELLLM_API_KEY when unset so one key powers both).
This lets users point SHELLLM_BASE_URL at OpenAI / OpenRouter /
Groq / Together / Mistral / any other OpenAI-compatible endpoint
and have shellllm just work. The local llama-server still needs no
auth — empty key → no header.
Failure stance
- render: never raises, fall-through to static fetch
- auth: lazy env lookup so tests can monkeypatch cleanly
221 passing tests (was 197):
- tests/test_render.py adapter happy + sad paths, timeout env
- tests/test_api_key.py Bearer header, embed fall-through to shared key
- tests/test_web.py-coverage render-first / static-fallback
README
- New "Use a hosted API instead of llama-server" section with copy-paste
exports for OpenAI / OpenRouter / Groq
- New "JS rendering for fetch_url" section with Firecrawl recipe
- "What's deliberately not built" now reflects that JS rendering is
shipped (just via BYOK, not local Chromium)
- Env vars table grew the new SHELLLM_API_KEY / SHELLLM_RENDER_*
/ SHELLLM_EMBED_API_KEY rows
Tells a 60-second "you're working in the terminal and shellllm catches
you without breaking flow" story instead of a feature parade:
1. , find the five largest files here → fzf pick → run
2. , the same but only ones modified in the last hour
(sticky session — the model remembers what "the same" means)
3. ? in markdown and 3 lines max, what does git stash do
(streaming markdown answer, no browser tab)
4. ??? --add this project uses pnpm and ships via release-build.tar.gz
(pin a fact, used by every future ?)
5. ? --new
(mid-flow segue: archives the prior session)
6. ??? git stash
(recall — the prior conversation lands as a hit)
7. ??? --archives 5
(browse what's been saved)
The Hide block at top sets up /tmp/shellllm-demo with realistically-sized
files for `du` to find and seeds the archive with one prior session
about git stash so the `???` recall has something to surface.
Read the VHS spec and used current syntax: Require, BorderRadius,
WindowBar Colorful, Catppuccin Mocha theme, JetBrains Mono.
'with sqlite3.connect(...)' only scopes the transaction; the WAL connection (3 fds: db, -wal, -shm) lived until GC. The growing test suite exhausted the default macOS fd limit mid-run. _conn() is now a context manager that commits and closes.
New shell_context module: SHELLLM_SHELL_CONTEXT=off|cmd|history|output gates what the zsh layer may capture (previous command + exit status, recent history, tmux pane output). Everything passes a secret scrubber (keyed assignments, Bearer headers, AWS/GitHub/Slack/Stripe tokens, JWTs — git SHAs survive) and hard caps before reaching the model. Off by default; the Python side re-checks the ladder independently. Also provides build_piped_block for stdin-as-context.
The context block rides as a per-turn system message — rebuilt every call, never persisted to the session or archive. --no-ctx skips it for one call. --fix (zsh alias `,,`) turns the previous command, its exit status, and any captured output into a repair prompt through the same fzf picker; without the ladder enabled it explains how to opt in.
`make 2>&1 | ? what broke` injects the piped output (capped, redacted) as an ephemeral system block — piping is explicit consent, so it works regardless of the SHELLLM_SHELL_CONTEXT ladder. The ladder-gated terminal context block joins the same rebuilt-per-turn system prefix; --no-ctx opts out per call.
- _shellllm_with_ctx captures $?, `fc` history, and tmux pane output per the ladder and passes them as one-shot env — nothing exported. - `,,` repairs the last command via `, --fix`. - SHELLLM_AUTOSTART=1 brings the default tier up on demand from `,`/`?`. - Tiers bind to dedicated ports (fast :8091, balanced :$SHELLLM_PORT, smart :8093) so they serve side by side; `, --fast` / `? --smart` (first arg only) route a single call via SHELLLM_BASE_URL. `?? --stop [tier]` and `?? --status` are tier-aware.
Sourcing shellllm.zsh now enables cmd-level context (previous command + exit status) so `,,` and "why did that fail" work out of the box — local-first, nothing leaves the machine on the default setup. The zsh layer also passes the level down per invocation, fixing the case where an unexported shell variable enabled capture but the Python side never saw it. SHELLLM_SHELL_CONTEXT=off disables capture entirely.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
shellllm's pitch is "the LLM that lives at your prompt" — but until now it ignored the most valuable thing the prompt knows: what just happened. This PR makes the terminal itself the context source, while keeping the local-first privacy story intact, and rounds out the multi-model pillar with per-call tier routing.
What's new
Terminal context (privacy ladder)
SHELLLM_SHELL_CONTEXT=off|cmd|history|output— defaults tocmd(previous command + exit status) when sourcing the zsh layer:--no-ctxskips one call;offdisables capture entirely.,,— fix the previous command, --fix(alias,,) builds a repair prompt from that context and proposes corrected commands through the existing fzf picker.Pipe-friendliness
make 2>&1 | ? what broke— piped stdin becomes context (capped, redacted, tail-kept). Explicit consent by construction, so it works at any ladder level.Lazy start
SHELLLM_AUTOSTART=1brings the default tier up on demand from,/?instead of erroring when the server is down.Per-call model routing
Tiers bind to dedicated ports (
fast:8091,balanced:$SHELLLM_PORT,smart:8093) and serve side by side., --fast …/? --smart …(first argument only) route a single call viaSHELLLM_BASE_URL.?? --statusand?? --stop [tier]are tier-aware.Also in this PR
fix(archive): sqlite connections now close deterministically —with sqlite3.connect()only scoped the transaction, and the leaked WAL fds (3 per connection) blew past macOS's default 256-fd limit under the growing test suite.53f1cb8), and the VHS flow-narrativedemo.tape(1b00b46,3bb7064).Test plan
pytest -q) — new suites for the ladder, redaction,--fix, stdin piping, and ask/comma plumbingruff checkclean,,proposes a fix (defaultcmdlevel)?? --start fastalongsidebalanced;, --fast …routes;?? --status/?? --stop fastSHELLLM_AUTOSTART=1with the server down →?autostartsmake 2>&1 | ? what brokewith a real failing build