Slash commands: let the end user act on the conversation

What

Let a consumer opt in to slash commands parsed from the end user's own text — /compact, /clear — and register custom ones of their own.

The convention is familiar from agent harnesses: the person typing gets a small verb vocabulary that acts on the conversation rather than being sent to the model. This asks for the same thing as a library feature, with the built-in set deliberately small and everything else supplied by the consumer.

Two built-ins to start: /compact and /clear.

Why this belongs in the library rather than in each consumer

Parsing /word args is trivial and nobody needs help with it. The actions are the part a consumer cannot write.

  • /clear is not currently possible at all. There is no Clear or Reset on ChatClient. History()'s own documentation tells a caller to "reset the client" — meaning discard it and construct another, which throws away the registered tools, the cumulative Usage() counters, any cached prefix, and the provider session with it. A command meaning "start again" needs a real transcript operation, and only a provider can perform one: the core treats a transcript as opaque by design.
  • /compact depends on #12 (closed), which depends on the application path that landed in spec 0013.

So the split is: #12 (closed) gives a consumer a programmatic way to bound a conversation, applied automatically before each request. This issue gives the person in the conversation a way to ask for it on demand. Same underlying operation, different trigger, and the interactive one is missing.

Constraints — not open for the spec to revisit

Opt in, and explicitly. Off by default, and a consumer switching it on names the commands they want. A prompt beginning with / must keep meaning exactly what it means today for every caller who does not opt in — that is not a default worth changing under anyone.

Only the caller-supplied prompt is ever parsed. Model output, tool results and restored transcripts must never be interpreted as commands. Treating model-reachable text as a command vocabulary is a prompt-injection path: a retrieved document containing /clear should be data, not an instruction that destroys a conversation or spends tokens summarising one. The parse happens once, at the boundary where the caller hands text in.

claude-local cannot honour the transcript commands. Its conversation lives in the claude CLI's session and it reports History.Known == false, which is why spec 0013 D6 puts it outside the history-policy path. Whatever /compact and /clear do elsewhere, they cannot be honest there, and the answer should match 0013's rather than invent a second shape.

What the spec has to settle

  1. What a command returns. Chat returns (string, error) and a command produces no model response. Is the command's own output the string? A sentinel error? A distinct method or result type? This is the central API question and everything else is downstream of it.
  2. Whether Ask participates at all. Ask promises a structured answer unmarshalled into a target; a command returning prose cannot honour that. Plausibly commands are a Chat/StreamChat feature only.
  3. The streaming shape. Does StreamChat emit synthetic events for a command, or return without streaming?
  4. Escaping. How does a user send a literal message that begins with / without triggering a command?
  5. Arguments. Is it bare verbs, or does /compact 20 parse? Who validates?
  6. What a custom handler is given, and what it may do. A handler that can reach the client can also send messages, replace tools, or rewrite history. The capability surface handed to consumer code is a design decision, not a detail.
  7. Collisions and overriding. May a consumer replace a built-in? What happens when a custom name shadows one?
  8. Whether a command turn is recorded. Does /clear appear in the history it just cleared? Does a compaction leave a trace a later reader can see?
  9. ProviderFallback. Which client in a composite performs the action, and what happens to the others' state.

Built-ins beyond /compact and /clear — for discussion

Proposed, with the reasoning, because "which commands ship in the box" is worth deciding deliberately rather than accreting.

Recommended. These need no new provider capability — they read state the client already exposes — or fall out of work /clear requires anyway:

Command Why Costs
/help A command registry that cannot list itself is unfriendly, and every consumer would otherwise write the same loop. It is also the only built-in that gets more useful as consumers register their own. nothing new
/usage Usage() and History() already carry cumulative tokens and the last input count. "What has this cost me" is the question a long conversation provokes, and the data is sitting there. nothing new
/tools The registered tool set is known to the client. A user asking what the agent can actually do is asking a question the library can answer exactly. nothing new
/undo Drop the last exchange. It needs the same transcript operation /clear does — once a provider can drop turns on demand, dropping the last two rather than all of them is the same mechanism with a different argument. rides on /clear

Argued against as built-ins, and better left to consumer registration:

  • /model — switching model mid-conversation touches capability tables, cached prefixes and generation config. Which models a user may switch to is the consumer's policy, not the library's.
  • /save, /restorePersistentChatClient already provides the operation, but where a snapshot goes is entirely the consumer's business. A built-in would have to invent a storage opinion.
  • /retry — plausible, but it needs a definition of "the last user turn" that survives tool loops, and it re-sends rather than acting locally. Worth a look once the transcript operations exist, not before.
  • /system — viewing or replacing the system prompt mid-conversation interacts with prompt caching in ways that deserve their own thought.

Scope

A spec before any code. It should come after #12 (closed) settles what compaction is, since /compact is a trigger for it and specifying the trigger before the action would be guessing.

/help, /usage and /tools do not depend on #12 (closed) and could land first, which would also prove the registration and dispatch mechanism against commands that cannot damage anything.