Forge
markdowndeeb25a2
1# ADR 0119: Slash commands in chat — unified command line (Intercom + IDE)
2
3**Status:** Accepted · Implemented
4**Date:** 2026-05-17
5**Updated:** 2026-05-17 — extended to IDE verbs (`/build run`, `/test run`, `/debug launch`); autocomplete required. [§ History](#adr0119-history)
6
7## Related ADRs
8
9| ADR | Role |
10|-----|------|
11| [0080](0080-intercom-naming-and-multi-party-channel-model.md) | Chat as **Intercom** — central channel, not “bot window” |
12| [0072](0072-chat-topic-cards-intent-melody-keyboard-contract.md) | Topic cards, overview/detail, **intent-first** navigation (Melody/Chords) |
13| [0096](0096-intercom-topic-card-summary-and-product-spine.md) | Card content, spine, carry-forward into thread |
14| [0013](0013-command-surface-and-discoverability.md) | Palette and discoverability — slashes **complement**, do not replace |
15| [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md) | Canonical `command_id`, registry, MCP parity |
16| [0060](0060-keyboard-chord-stack-fms-tactical-strategic.md) | CascadeChord, Command Melody `c:` — **orthogonal** input |
17| [0008](0008-mcp-contracts-and-testable-infrastructure.md) | Agent parity: same effects via `ide_execute_command` |
18| [0048](0048-cursor-acp-chat-ide-parity-and-mcp-tool-surface.md) | What goes to the agent vs local IDE action |
19| [0057](0057-chat-surface-pipeline-adoption.md) | Snapshot/layout after VM state change |
20| [0116](0116-intercom-session-tree-and-agent-message-steering.md) | Session tree, steer — do not mix with slash parser |
21| [0002](0002-debug-human-agent-parity.md) | `/build`, `/test`, `/debug` — same `command_id` as agent via MCP |
22| [0018](0018-ide-commands-canonical-xml-documentation.md) | Canonical `IdeCommands` names for catalog projection |
23| [0120](0120-primary-work-surface-intercom-or-editor.md) | Intercom in Forward — slashes as primary session CLI |
24
25### Outside ADR
26
27| Document | Role |
28|----------|------|
29| [MCP-PROTOCOL.md](../../MCP-PROTOCOL.md) | `ide_execute_command`, `send_chat`, chat_* MCP |
30| [intent-melody-language-v1.md](../../intent-melody-language-v1.md) | `c:` grammar — **not** `/` grammar in chat |
31
32## Summary
33
34- **`ChatInput`** is an **alternative IDE command line**: stay in chat for Intercom *and* frequent actions (`/build run`, `/test run`, `/debug launch`, `/card …`).
35- Slash → **`command_id`** ([0030](0030-command-ids-hotkeys-and-ui-registry-layers.md)); catalog is a **projection** of the registry onto **readable** slash paths (`/build run`, `/overview`), not a second executor in the VM.
36- **Discoverability via autocomplete** (namespace → action hierarchy, hints, `/help`), **not** via short mnemonics like `/br` (compressed forms stay in **`c:`** Melody and chords, [0060](0060-keyboard-chord-stack-fms-tactical-strategic.md)).
37- **Autocomplete is required** — extended catalog is not accepted without it.
38- Palette, Melody `c:`, and chords remain; slash is a **peer input** for those already in the message field ([0013](0013-command-surface-and-discoverability.md)).
39- Rollout **in phases**: Intercom verbs → IDE namespaces → extension from palette.
40
41---
42
43## Context
44
45Intercom in CIDE ([0080](0080-intercom-naming-and-multi-party-channel-model.md)) is increasingly the **central surface**: agent dialogue, topic catalog ([0072](0072-chat-topic-cards-intent-melody-keyboard-contract.md)), product spine ([0096](0096-intercom-topic-card-summary-and-product-spine.md)), clarifications ([0031](0031-agent-chat-clarification-batches-and-threading.md)).
46
47Power users already have:
48
49- **palette** and fuzzy search ([0013](0013-command-surface-and-discoverability.md));
50- **global hotkeys** and **CascadeChord** ([0060](0060-keyboard-chord-stack-fms-tactical-strategic.md));
51- **Command Melody** `c:` in the palette ([0112](0112-command-palette-query-modes-strategy.md));
52- **chat navigation intents** with MCP parity (`chat_show_thread_overview`, `chat_open_selected_thread`, … — [0072 §4](0072-chat-topic-cards-intent-melody-keyboard-contract.md)).
53
54That is **not enough** when the operator **is already typing in the message field** and expects a Slack/Discord-style model or **CLI inside chat**:
55
56- Intercom: `/card New topic`, `/overview`, `/spine focus=…`;
57- IDE: `/build run`, `/test run`, `/debug launch` — **without** switching to palette, toolbar, or mandatory chords.
58
59Product hypothesis: if Intercom is the **central surface**, chat becomes the **single session control point**, not only a channel to the agent.
60
61---
62
63## Problem
64
651. **Discoverability gap:** chat commands exist in the registry and MCP but are **not visible** in the input context where the main thought stream lives.
662. **Duplication risk:** ad-hoc `/card` parsing in `SendChatAsync` bypasses the intent layer [0072 §5](0072-chat-topic-cards-intent-melody-keyboard-contract.md) and diverges from pointer/MCP.
673. **Mixing with the agent:** without a “slash = local” rule, `/export` may go to the LLM as plain text.
684. **Prefix conflict:** `c:` is reserved for palette/Melody ([0112](0112-command-palette-query-modes-strategy.md)); **`/`** is a separate space **only in ChatInput**.
69
70---
71
72## Decision
73
74<a id="adr0119-p1"></a>
75
76### 1. ChatInput as **unified command line** (slash prefix)
77
78- A line starting with **`/`** (after trim) is a **slash command** (one- or two-level; see [§4](#adr0119-p4)).
79- Parse **on send** (Enter) and **incrementally** for autocomplete ([§6](#adr0119-p6)) — autocomplete is **not optional** for the accepted catalog size.
80- Do **not** intercept `/` in the middle of a normal message; ordinary agent dialogue has **no** leading slash.
81
82<a id="adr0119-p2"></a>
83
84### 2. Intent-first: slash → `command_id` → VM → snapshot
85
86**Invariant** (extension of [0072 §5](0072-chat-topic-cards-intent-melody-keyboard-contract.md)):
87
88```text
89ChatInput (/verb args…) → ChatSlashCommandParser → command_id (+ args)
90 → IdeMcpCommandExecutor / ChatPanel handlers (as palette/MCP)
91 → ChatPanelViewModel state → ChatSurfaceCompositor → Skia render
92```
93
94- Slash commands do **not** change Skia directly or read hit-target geometry.
95- Pointer, Melody, Chords, palette, MCP, and slash **converge** on the same `command_id` where possible.
96
97<a id="adr0119-p3"></a>
98
99### 3. Execution modes
100
101| Mode | Behavior | Example |
102|------|----------|---------|
103| **Local** | Message **not** sent to agent; `command_id` runs; input cleared (or one-line status). | `/overview`, `/export` |
104| **Local + echo** | Local + short system line in feed (optional, v1+). | `/card` with “topic created …” |
105| **Reject** | Unknown verb — UI error, **no** agent send. | `/foo` |
106| **Pass-through** *(forbidden by default)* | Send text to agent as-is. | **Do not** use for unrecognized `/` |
107
108**v1 rule:** unrecognized line with leading `/` → **Reject** with hint “unknown command, Tab — list”.
109
110<a id="adr0119-p4"></a>
111
112### 4. v1 grammar
113
114**Two levels** (like “namespace / action”):
115
116```ebnf
117slash_line = "/" head (WS tail)? WS? ;
118head = flat_verb | namespace ;
119flat_verb = letter { letter | digit | "-" } ; (* overview, card, help, export *)
120namespace = letter { letter | digit } ; (* build, test, debug, git, chat *)
121tail = action (WS arg_token)* | arg_tail ; (* run | launch | … OR rest for flat *)
122action = letter { letter | digit | "-" } ;
123arg_tail = { arg_token } ; (* /card Topic name — all after head *)
124arg_token = quoted_string | bare_token ;
125```
126
127**Examples:**
128
129| Input | Parse |
130|-------|--------|
131| `/overview` | flat: `overview` |
132| `/card ADR 0119` | flat: `card`, args: `ADR 0119` |
133| `/build run` | namespace: `build`, action: `run` |
134| `/test run` | namespace: `test`, action: `run` |
135| `/debug launch` | namespace: `debug`, action: `launch` |
136
137- **Case:** case-insensitive.
138- **Named args** (`configuration=Release`) — v2; v1 uses positional tail where needed.
139
140<a id="adr0119-p5"></a>
141
142### 5. Catalog: projection onto `command_id`, not a second registry
143
144<a id="adr0119-p5a"></a>
145
146#### 5a. Source of truth
147
148- **Execution** only through existing `ide_execute_command` / `IdeMcpCommandExecutor` ([0030](0030-command-ids-hotkeys-and-ui-registry-layers.md), [0008](0008-mcp-contracts-and-testable-infrastructure.md)).
149- **Slash catalog** (`ChatSlashCommandCatalog`) is a **mapping** (slash path → `command_id` + arg template), built from:
150 1. **Curated** table in code (v1);
151 2. v2+ — **projection** of a subset of `IdeCommandPaletteCatalog` / `IdeCommands` metadata (palette title need not match slash; slash path is explicit).
152- **Not Melody:** the catalog has **no** separate “2–3 letter” entries (`/br`, `/tr`) as shortcuts to `namespace action` — the operator picks **`/build` → `run`** from autocomplete or types the full form.
153- **Forbidden:** duplicate `dotnet build` / test / debug logic in `ChatPanelViewModel`.
154
155<a id="adr0119-p5b"></a>
156
157#### 5b. Intercom (flat verbs) — phase A
158
159| Slash | `command_id` | Note |
160|-------|----------------|------|
161| `/overview` | `chat_show_thread_overview` | |
162| `/open` | `chat_open_selected_thread` | |
163| `/card <title>` | *new* or `fork_chat_thread` + title | product |
164| `/spine …` | `chat_set_product_spine` | tail → focus / milestones |
165| `/spine-toggle` | `chat_toggle_product_spine_in_agent_context` | |
166| `/export` | `chat_export_readable` | |
167| `/help` | local catalog | no MCP |
168
169<a id="adr0119-p5c"></a>
170
171#### 5c. IDE namespaces — phase B (without leaving chat)
172
173| Slash | `command_id` | Note |
174|-------|----------------|------|
175| `/build run` | `build` or `build_structured` | structured JSON to feed/panel — UI policy |
176| `/build ui` | `build_solution_ui` | toolbar path, text to output |
177| `/test run` | `run_tests` | |
178| `/test affected` | `run_affected_tests` | optional `changed_paths` from git |
179| `/debug launch` | `debug_launch` | target from launch profile / current |
180| `/debug continue` | *debug_start_or_continue* (UI id) | add `IdeCommands` constant if missing |
181| `/git status` | `git_status` | phase C when in registry |
182
183Further namespaces (`nav`, `index`, `palette`) — **as discoverability allows**, not “all of IdeCommands at once”.
184
185**Parity:** agent calls the same `build` / `run_tests` / `debug_launch` via MCP; operator uses `/build run` in chat.
186
187<a id="adr0119-p6"></a>
188
189### 6. Discoverability: autocomplete and help (required)
190
191Without autocomplete, extension to `/build`, `/test`, … is **not accepted** — the operator **must not** memorize namespaces and actions and **must not** rely on compressed slash mnemonics (unlike `c:` in the palette).
192
193**UI behavior (v1 minimum):**
194
195| Input step | Popup shows |
196|------------|-------------|
197| `/` | top-level: flat verbs + namespaces (`build`, `test`, `debug`, `card`, …) |
198| `/build ` | actions: `run`, `ui`, … + one-line description |
199| `/build r` | prefix filter (`run`) |
200| unknown prefix | “no matches” + link to `/help` |
201
202- **Tab** — complete token / select highlighted; **↑↓** — navigate; **Esc** — close popup without clearing line.
203- Under each item — **short help** (from `IdeCommands` doc / curated catalog) and optional **hotkey** from TOML ([0030](0030-command-ids-hotkeys-and-ui-registry-layers.md)).
204- **`/help`** and **`/help build`** — text/interactive list in feed or overlay (local).
205- v1 source: `ChatSlashCommandCatalog` in code; v2 — TOML `chat-slash-aliases.toml` analogous to [0109](0109-declarative-parametric-melody-catalog-toml-and-code-binders.md).
206
207<a id="adr0119-p7"></a>
208
209### 7. Relation to agent and spine ([0096](0096-intercom-topic-card-summary-and-product-spine.md))
210
211- Slash commands are **local by default** — they do **not** expand the agent prompt.
212- `/spine-toggle` and `/spine` change session metadata; including spine in agent context is **explicit** ([0096 §4](0096-intercom-topic-card-summary-and-product-spine.md#adr0096-p4)), not a side effect of any slash.
213- **Carry-forward** into the thread remains a **normal message** or separate intent; slash need **not** generate agent text.
214
215<a id="adr0119-p8"></a>
216
217### 8. Non-goals and boundaries
218
219**Non-goals:**
220
221- **Full replacement** of Command Palette: fuzzy search over *all* commands without namespace structure stays in the palette.
222- Automatic **1:1** “every palette row = slash” without curated aliases and UX filter (too noisy).
223- Slash commands in **other** fields (terminal, editor, palette) — only `ChatInput`.
224- Plugins with arbitrary verbs **without** catalog / `command_id` entry.
225- Pass-through of unrecognized `/…` to the agent.
226- **Short slash aliases** (2–3 characters, “melody after `/”): `/br` instead of `/build run`, auto-generation from [0109](0109-declarative-parametric-melody-catalog-toml-and-code-binders.md) without a dedicated slash path — discoverability is **hierarchical autocomplete** and readable `namespace` / `action` / flat verbs only.
227
228**In scope (deliberately):**
229
230- **Alternative input** to the same IDE actions as palette/chords/MCP — including `/build run`, `/test run`, `/debug launch`.
231- Operator **may stay in chat** for the frequent loop “ask agent → build → run tests → debug”.
232
233---
234
235## Input orthogonality (summary)
236
237| Input | Where | Prefix / form |
238|-------|-------|----------------|
239| Palette | overlay | fuzzy, `c:` Melody |
240| Hotkeys / Chord | global | TOML → `command_id` |
241| Chat Melody aliases | palette / intents | `ato`, `atb`, … ([0072](0072-chat-topic-cards-intent-melody-keyboard-contract.md)) |
242| **Chat slash** | `ChatInput` | `/verb` or `/namespace action` (**this ADR**) |
243| MCP | agent | `ide_execute_command` |
244
245---
246
247## Diagram
248
249```mermaid
250flowchart TD
251 input[ChatInput]
252 input -->|starts with /| parser[ChatSlashCommandParser]
253 input -->|normal text| agent[SendToAgent]
254 parser -->|known verb| cmd[command_id handler]
255 parser -->|unknown| err[Inline error + help]
256 cmd --> vm[ChatPanelViewModel]
257 vm --> snap[ChatSurfaceSnapshot]
258 snap --> skia[SkiaChatSurfaceControl]
259 palette[Palette Melody Chords MCP] --> cmd
260```
261
262---
263
264## Implementation anchors (plan)
265
266| Component | Role |
267|-----------|------|
268| `Features/Chat/ChatSlashCommandCatalog.cs` *(new)* | verb → descriptor (`command_id`, help, arg hint) |
269| `Features/Chat/ChatSlashCommandParser.cs` *(new)* | parse line, validate |
270| [`ChatPanelViewModel`](../../Features/Chat/ChatPanelViewModel.cs) | call parser in `SendChatAsync` **before** `ApplyProductSpineToOutboundMessage` / ACP |
271| [`IdeMcpCommandExecutor`](../../ViewModels/IdeMcpCommandExecutor.cs) | same `command_id` as MCP |
272| [`ChatPanelView.axaml`](../../Views/ChatPanelView.axaml) | autocomplete popup (**required** before phase B) |
273| `ChatSlashAutocompleteControl` *(new)* | hierarchical popup bound to `ChatInput` |
274| [`IdeCommands`](../../Services/IdeCommands.SolutionWorkspace.cs) | new `command_id` only if uncovered (`chat_create_or_rename_topic`) |
275
276**Rollout order:**
277
278| Phase | Content | Done when |
279|-------|---------|-----------|
280| **A** | Parser (flat + namespace/action), Intercom catalog, local execution | `/overview`, `/export` do not go to agent |
281| **A′** | **Autocomplete** for flat + namespace list | hints after `/` and `/build ` |
282| **B** | IDE: `/build run`, `/test run`, `/debug launch` → `command_id` | parity with MCP `build` / `run_tests` / `debug_launch` |
283| **C** | Extend catalog (`/git status`, `/nav`, palette projection) | by discoverability + autocomplete, not big-bang |
284
285Tests: parser unit tests; “slash local” integration; catalog help snapshots.
286
287---
288
289## Rejected alternatives
290
2911. **Parse slashes in the palette** (`/card` in Command Palette) — mixes overlay and Intercom; rejected.
2922. **MCP-only commands without `command_id`** — breaks [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md); rejected.
2933. **Send unrecognized `/` to the agent** — noise and leaks; rejected.
2944. **Short slashes like Melody** (`/br` = build run) — duplicates `c:` / chords, collisions and a second parser; Intercom discoverability is **autocomplete**, not mnemonics; rejected.
295
296---
297
298## Change history
299
300<a id="adr0119-history"></a>
301
302| Date | Change |
303|------|--------|
304| 2026-05-17 | Proposed: slash commands in ChatInput, v1 catalog, intent-first, non-goals. |
305| 2026-05-17 | Extended: unified command line — IDE namespaces (`/build run`, `/test run`, `/debug launch`); autocomplete required; phases A–C. |
306| 2026-05-17 | Clarified: slash discoverability is **autocomplete**; short aliases (`/br`) and “melody after `/`” are **non-goals** (compression — `c:` Melody). |
307| 2026-05-17 | Accepted · Implemented: phases **A**, **A′**, **B** (`ChatSlashCommand*`, autocomplete, IDE namespaces); phase **C** — backlog. |
308
View only · write via MCP/CIDE