Skip to content

Terminal context, ,, repair, stdin piping, autostart, and per-tier model routing - #4

Merged
FrancoisChastel merged 11 commits into
mainfrom
feat/cli-redesign-recall
Jun 12, 2026
Merged

Terminal context, ,, repair, stdin piping, autostart, and per-tier model routing#4
FrancoisChastel merged 11 commits into
mainfrom
feat/cli-redesign-recall

Conversation

@FrancoisChastel

Copy link
Copy Markdown
Owner

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 to cmd (previous command + exit status) when sourcing the zsh layer:

$ git push origin amin
error: src refspec amin does not match any
$ ? why did that fail        # the model sees the command and its status
  • Captures pass a secret scrubber (keyed assignments, Bearer headers, AWS/GitHub/Slack/Stripe tokens, JWTs — git SHAs survive) and hard caps.
  • Passed as one-shot env per invocation; never exported, never persisted to sessions or the archive; rebuilt per turn.
  • Both sides enforce the ladder independently; --no-ctx skips one call; off disables 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=1 brings 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 via SHELLLM_BASE_URL. ?? --status and ?? --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.
  • JS rendering via Firecrawl + Bearer auth for hosted LLM APIs (53f1cb8), and the VHS flow-narrative demo.tape (1b00b46, 3bb7064).

Test plan

  • 257 tests pass (pytest -q) — new suites for the ladder, redaction, --fix, stdin piping, and ask/comma plumbing
  • ruff check clean
  • Manual: fail a command, ,, proposes a fix (default cmd level)
  • Manual: ?? --start fast alongside balanced; , --fast … routes; ?? --status / ?? --stop fast
  • Manual: SHELLLM_AUTOSTART=1 with the server down → ? autostarts
  • Manual: make 2>&1 | ? what broke with a real failing build

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.
@FrancoisChastel
FrancoisChastel merged commit b186a22 into main Jun 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant