Skip to content

Collapse ??? web-search and ?: state into one ??? memory verb - #3

Merged
FrancoisChastel merged 5 commits into
mainfrom
feat/cli-redesign-recall
Jun 9, 2026
Merged

Collapse ??? web-search and ?: state into one ??? memory verb#3
FrancoisChastel merged 5 commits into
mainfrom
feat/cli-redesign-recall

Conversation

@FrancoisChastel

@FrancoisChastel FrancoisChastel commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Why

The CLI had grown to five commands — , ? ?? ??? ?: — but two of them weren't orthogonal:

  • ??? was just ? with a "web-first" system prompt
  • ?: (the colon-glyph state verb) split memory ops from the rest

This collapses them into one ??? verb whose meaning matches its shape: three question marks reads as "I'm trying to remember…".

New shape

Four commands, each one job:

,    <english>                propose shell commands (sticky session)
?    <q>                      ask (model picks tools — incl. web)
?    --web <q>                force web-first this turn
??   [start/stop/...]         llama-server control
???  <q>                      memory: bare query = archive search
???  --add <fact>             pin a long-term fact
???  --list / --drop <n>      facts mgmt
???  --status / --help
???  --ask / --comma <q>      narrow recall to one type of session

No bare-word verbs on ??? — everything other than a recall query is a flag. Bare words (??? list) are unambiguously recall queries.

Why this is the win

  1. One asking verb. ? is the question. Web search is a tool the agent has — forcing it is a flag (--web/-w), not a sibling command.
  2. ??? gets a coherent role. "Three question marks" reads as "I'm trying to remember…". Bare ??? what was that grep flag is the most natural recall syntax; flags kick in only for fact management.
  3. Zero new glyphs invented. No ?:, no ?., no ?!. Four commands total.
  4. Drops one entry-point. shellllm-search is gone; the system prompt that made ??? web-first becomes a one-flag variant of ?.
  5. No subcommand/query ambiguity. Every operation on ??? other than recall is a flag.

Filter flags on ???

Recall hits across all session types by default. Filter when needed:

??? --ask docker volumes        # only `?` sessions
??? --comma docker volumes      # only `,` sessions
??? docker volumes              # both

Combining a filter with a mode flag errors (facts are global, not per-command).

Validation

  • Mode flags are mutually exclusive
  • Filter flags are mutually exclusive
  • Filter + mode → error
  • Per-mode extra-arg checks (--list takes no args; --drop takes exactly one; etc.)

Cuts vs. adds

Deleted (no deprecation redirects — clean cut):

  • src/shellllm/search.py
  • src/shellllm/state.py
  • tests/test_state.py
  • Entry points: shellllm-search, shellllm-state
  • Zsh: SHELLLM_SEARCH, _shellllm_search_fn, alias ?:

Added:

  • src/shellllm/recall.py
  • tests/test_recall.py
  • tests/test_ask_web_flag.py
  • Entry point: shellllm-recall
  • Zsh: SHELLLM_RECALL, _shellllm_recall_fn

Test plan

  • ruff check clean
  • ruff format --check clean
  • 181 tests pass
  • End-to-end through real zsh: ??? --add, ??? --list, ??? <bare query>, ??? --ask, mutually-exclusive flag rejection, ?: confirmed removed
  • Manual: drive ? --web against a running llama-server to confirm the web-first system prompt actually fires

? had grown 12 flags that mixed two ideas (asking and managing
state). The state ops now live behind a single meta verb so the
asking commands stay ask-only.

New surface
- `?:` — long-term facts + cross-session recall
  - `?: add <fact>` / `list` / `drop <n>` / `recall <q>` / `status`
- `,` is now session-aware (cmd="comma"): follow-ups refine the
  prior proposal instead of asking from scratch. Same TTL/archive
  hookup as `?` / `???`, so expired `,` sessions are searchable via
  `?: recall`.

Removed from `?` / `???`
- --remember, --memories, --forget, --recall, --mem, --no-mem

Deprecation
- Moved flags print a one-line redirect pointing at the `?:`
  equivalent so muscle memory fails loudly, not silently.

Surfaces that stay
- `? --new / --reset / --history / --compact / --auto-recall / --help`
- `, --new / --reset / --history / --help`
- `??` and its `--start-embed` extension unchanged

168 tests (was 144), ruff clean.
…ory verb

Before this change the CLI had five commands: `,`, `?`, `??`, `???`,
`?:`. `???` was just `?` with a "web-first" system prompt, and `?:`
held the durable layer (facts + recall). Both were artifacts of growth
rather than orthogonal concepts. This commit collapses them.

New shape (four commands, each one job)
- `,` propose shell commands (sticky session, unchanged)
- `?` ask (sticky session, model picks tools — incl. web)
- `??` llama-server control (unchanged)
- `???` memory layer: bare query searches the archive; subcommands
   manage facts (add/list/drop/status/help). Explicit `recall <word>`
   when the query happens to start with a subcommand verb.

`?` gains `--web` / `-w` for the rare case you want to force a
web-first answer this turn — same agent loop, same session bucket
(cmd="ask"), just a stronger system-prompt nudge.

`???` gains `--ask` / `--comma` filter flags so you can scope recall
to one type of session ("only my asks", "only my command proposals").

Deletions (no deprecation hints — clean cut)
- src/shellllm/search.py    — `???` web-search command
- src/shellllm/state.py     — `?:` state command
- tests/test_state.py
- Entry points: shellllm-search, shellllm-state
- Zsh: SHELLLM_SEARCH env, _shellllm_search_fn, alias `?:`

Adds
- src/shellllm/recall.py    — the new `???` command
- tests/test_recall.py      — dispatch + filter flag coverage
- tests/test_ask_web_flag.py — verifies --web swaps system prompt
- Entry point: shellllm-recall
- Zsh: SHELLLM_RECALL env, _shellllm_recall_fn

178 passing tests (was 168), ruff clean.
Mode ops are now flags (--add / --list / --drop / --status) instead of
subcommand verbs. Bare-word input is unambiguously a recall query;
no more "is this a subcommand or a search term?" overload.

Surface
- ??? <q>            bare query → recall
- ??? --add <fact>   was: ??? add
- ??? --list         was: ??? list
- ??? --drop <n>     was: ??? drop
- ??? --status       was: ??? status
- ??? --ask / --comma <q>   filter recall (unchanged)
- ??? --help         was: ??? help

Validation
- Mode flags are mutually exclusive (two → error)
- Filter flags are mutually exclusive (two → error)
- Filter + mode flag → error (facts are global, not per-command)
- Per-mode extra-arg checks (--list takes no args, --drop takes one, etc.)

181 passing tests (was 178), ruff clean.
…n hint

Two new mode flags on ???:
- --archives [n]    list n most-recent archived sessions (default 20),
                    one row per archive with id · cmd · ts · pwd · snippet.
                    Honors --ask / --comma to narrow by command.
- --show <id>       print the full transcript of one archive.

Together they fill the "how do I see what's in here?" gap that --status
left open (it only reported counts).

Filter compatibility
- --archives accepts --ask / --comma (per-command browse makes sense)
- --show <id> targets exactly one row, so filters error there
- Facts ops stay global-only (--add / --list / --drop / --status)

Visual bug fix in `,`
The "↻ refining — turn N" / "↻ idle session expired" hints in comma
were written via `rich.Console.print`, which strips embedded ESC bytes
as a safety measure — leaving `[2m[36m...[0m` visible as literal text.
Replaced with a sys.stderr.write helper matching ask._note. Added a
regression test that asserts ESC bytes survive in stderr output.

197 passing tests (was 181), ruff clean.
Restructured for the GitHub-frontpage skim test:

- Single tagline above the fold
- Hero block showing the three core commands working back-to-back
- Quick-reference table for the four glyphs
- Tiers as a side-by-side comparison, not prose
- 'Why this exists' moved up to anchor the contrarian take (offline,
  local 27B, no per-question cost)
- Configuration as one long table instead of paragraphs

Architecture / hard wall / dev sections still present but moved below
the fold so they don't drown the install path.
@FrancoisChastel
FrancoisChastel merged commit 25676b7 into main Jun 9, 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