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.
/clearis not currently possible at all. There is noClearorResetonChatClient.History()'s own documentation tells a caller to "reset the client" — meaning discard it and construct another, which throws away the registered tools, the cumulativeUsage()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./compactdepends 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
- What a command returns.
Chatreturns(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. - Whether
Askparticipates at all.Askpromises a structured answer unmarshalled into a target; a command returning prose cannot honour that. Plausibly commands are aChat/StreamChatfeature only. - The streaming shape. Does
StreamChatemit synthetic events for a command, or return without streaming? - Escaping. How does a user send a literal message that begins with
/without triggering a command? - Arguments. Is it bare verbs, or does
/compact 20parse? Who validates? - 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.
- Collisions and overriding. May a consumer replace a built-in? What happens when a custom name shadows one?
- Whether a command turn is recorded. Does
/clearappear in the history it just cleared? Does a compaction leave a trace a later reader can see? 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,/restore—PersistentChatClientalready 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.