返回 CodeWhale
command-dispatch.md
根目录 / docs / architecture / command-dispatch.md
1 # Command Dispatch Architecture
2
3 **Target branch:** `main`
4 **Related EPIC:** [#2870](https://github.com/Hmbown/CodeWhale/issues/2870)
5 **Related issue:** [#2791](https://github.com/Hmbown/CodeWhale/issues/2791)
6 **EPIC-002 (Command Single Responsibility Extraction):** Layer 4.x (FEAT-006 through FEAT-008)
7
8 This document records the command-dispatch ownership model after the
9 command-boundary replay landed on `main`, updated through EPIC-002 (command
10 single responsibility extraction). It reflects the final layered ownership:
11 top-level group registration, group-owned command registration, and
12 command-level ownership of metadata and behavior. It is the public reference for the
13 module boundaries, dispatch precedence, and permanent exceptions that remain
14 after the command-boundary refactor.
15
16 ## Dispatch Flow
17
18 `commands::execute()` owns the slash-command dispatch gate. The order is
19 intentional:
20
21 | Step | Source | Behavior |
22 |------|--------|----------|
23 | 0 | `$skill` compatibility | `$name` is resolved as `/skill name` before slash parsing. |
24 | 1 | User commands | `user_registry::try_dispatch()` checks workspace and global markdown commands first, so user commands can shadow built-ins. |
25 | 2 | Permanent mode compatibility aliases | `/jihua` and `/zidong` route through config mode dispatch so each selects its fixed legacy mode. They remain registered aliases for discovery, but bypass normal `/mode` execution. |
26 | 3 | Built-in registry | `CommandRegistry` resolves group-owned built-in commands by canonical name or alias. |
27 | 4 | Legacy migration hints | Retired commands such as `/set` and `/deepseek` return targeted replacement guidance. |
28 | 5 | Skills fallback | If no command matches, a skill with the same name may run before unknown-command suggestions are shown. |
29
30 ## Module Boundaries
31
32 | Module | Responsibility |
33 |--------|----------------|
34 | `crates/tui/src/commands/mod.rs` | Central dispatch gate, registry initialization, public command lookup helpers, and unknown-command suggestions. |
35 | `crates/tui/src/commands/traits.rs` | Built-in command metadata, trait-backed command objects, command groups, and registry lookup. |
36 | `crates/tui/src/commands/groups/` | Group-owned built-in command areas. Each group owns its command metadata and handlers. |
37 | `crates/tui/src/commands/user_registry.rs` | User-command registry boundary: markdown metadata, aliases, hidden entries, validation errors, dispatch state resets, and shadowing behavior. |
38 | `crates/tui/src/commands/discovery.rs` | Shared discovery-shadowing contract consumed by both the command palette and slash completion: canonical-token shadowing, alias-token shadowing, and unshadowed-alias projection. Both discovery surfaces interpret user-command ownership identically through this module. |
39 | `crates/tui/src/commands/user_commands.rs` | Lower-level file scanning, frontmatter parsing, allowed-tools parsing, and template substitution used by the registry. |
40 | `crates/tui/src/tui/command_palette.rs` | Palette entries for built-ins and visible user commands, with user commands shadowing built-ins. |
41 | `crates/tui/src/tui/widgets/mod.rs` | Slash completion, user-command metadata display, and alias-shadowing behavior. |
42
43 ## Built-In Command Groups
44
45 | Group | Scope |
46 |-------|-------|
47 | `core` | Help, model/provider selection, queue, hooks, subagents, links, feedback, voice, and core navigation. |
48 | `config` | Config, settings, status surfaces, mode, theme, trust, logout, and related settings commands. |
49 | `debug` | Token/cost introspection, cache, system/context, diff/edit, undo, and retry. |
50 | `memory` | Persistent memory and notes. |
51 | `plugins` | Read-only bundle discovery/validation plus explicit trust, enable, disable, revoke, and reload lifecycle commands; legacy executable tools remain separate. |
52 | `project` | Project initialization, sharing, LSP, and goal/hunt commands. |
53 | `session` | Rename, save, fork/new/load sessions, compaction, purge, relay, and export. |
54 | `skills` | Skills Manager (`/skills`), text inspect/remote/sync paths, activation (`/skill`), and managed install/update/uninstall/trust. |
55 | `utility` | Attachments, tasks/jobs, MCP, and network. |
56
57 ## User Commands
58
59 User commands are markdown files loaded from these locations in precedence
60 order:
61
62 1. `<workspace>/.codewhale/commands/`
63 2. `<workspace>/.deepseek/commands/`
64 3. `<workspace>/.claude/commands/`
65 4. `<workspace>/.cursor/commands/`
66 5. `~/.codewhale/commands/`
67 6. `~/.deepseek/commands/`
68
69 Supported frontmatter fields:
70
71 | Field | Meaning |
72 |-------|---------|
73 | `name` | Canonical slash-command name. It is normalized without a leading slash and replaces the filename-derived default. |
74 | `description` | Work objective and UI description. |
75 | `usage` | Preferred user-facing invocation syntax shown in the palette and slash completion. |
76 | `arguments` | Argument synopsis and a signal that selection should leave the composer open for input. It does not impose runtime validation. |
77 | `argument-hint` | Backward-compatible palette/completion hint for expected arguments. It remains the display fallback when `usage` is absent. |
78 | `allowed-tools` | Restricts command execution tools. An explicit empty value blocks all tools. |
79 | `pausable` | Marks the command as pause/resume capable. |
80 | `alias` / `aliases` | Additional user-command names that can shadow built-in aliases. |
81 | `hidden` | Hides the command from palette/completion while allowing direct dispatch. |
82
83 The canonical name defaults to the normalized markdown filename. A valid
84 frontmatter `name` replaces that default; the filename is not retained as an
85 implicit alias, so a renamed command must list the old filename under `alias`
86 or `aliases` if both spellings should dispatch. A configured name may include
87 one leading slash for readability, but after normalization it must be one
88 non-empty slash-command token with no whitespace or embedded `/`. An invalid
89 configured name is a recoverable error attached to the filename-derived
90 command, preventing silent fallthrough to a built-in.
91
92 Source precedence is resolved before frontmatter naming: a higher-precedence
93 directory wins when the same filename exists in more than one location. Files
94 inside each directory are ordered by normalized filename. If distinct files
95 then resolve to the same frontmatter `name`, the first file in that stable
96 directory-and-filename order wins and the losing file records a recoverable
97 load error. Aliases cannot replace any canonical user-command name; duplicate
98 aliases also use first-wins order. Errors on a losing duplicate never poison a
99 valid winning definition.
100
101 Presentation metadata has an explicit fallback order: non-empty `usage`, then
102 non-empty legacy `argument-hint`, then non-empty `arguments`. `arguments` and
103 `argument-hint` also cause palette/menu selection to append a space; `usage`
104 does so when it describes more than the bare command name. These fields do not
105 parse, require, or reject invocation arguments. Runtime expansion remains
106 backwards-compatible: `$ARGUMENTS` receives the complete argument tail and
107 `$1`, `$2`, and so on receive whitespace-separated positional values.
108
109 Malformed files remain registered under their resolved name with a
110 dispatch-time error, so they cannot silently fall through to a built-in. Their
111 errors are isolated per file: valid siblings still load, appear, and dispatch.
112 Hidden commands participate in shadowing and remain directly dispatchable, but
113 are removed from palette and slash-completion discovery.
114
115 Explicit `/help <name>` topics and unknown-command typo suggestions resolve
116 through the same user-command precedence as execution. When a user command
117 owns a built-in name or alias, help shows the user command's metadata and typo
118 suggestions point to its canonical name rather than the shadowed built-in.
119
120 Dispatch through `user_registry` resets stale command state before sending the
121 new command body: hunt objective fields, token/time counters, continuation
122 count, allowed tools, pause state, todos, and plan state.
123
124 ## Permanent Exceptions
125
126 | Exception | Rationale |
127 |-----------|-----------|
128 | `/jihua`, `/zidong` | Backward-compatible mode aliases that predate the group-owned registry. They route through config mode dispatch to preserve their fixed mode selection. |
129 | `/set` and `/deepseek` migration hints | Retired commands kept only as direct typed guidance. They are excluded from registry and autocomplete. |
130 | `#[allow(clippy::module_inception)]` in matching group modules | Group directories intentionally contain same-named child modules such as `core/core.rs`. |
131 | `user_commands.rs` lower layer | The registry owns runtime behavior, while this module remains the shared filesystem and parser layer. |
132 | `#[cfg(test)]` helpers in `user_commands.rs` | Deferred test migration compatibility while registry-specific tests are added. |
133
134 ## EPIC-002 Completion Status (closed)
135
136 EPIC-002 (Command Single Responsibility Extraction) extracted commands for
137 all 9 command groups through Layer 4.x sublayers. Layer 4.2 (FEAT-008) landed as
138 PR #3706 (merged) and issue #2870 closed on 2026-08-01.
139
140 | Layer | FEAT | Title | Status |
141 |---|---|---|---|
142 | 4 | FEAT-006 | Core, Config, Session, and Debug Command Extraction | Complete |
143 | 4.1 | FEAT-007 | Project, Memory, Skills, Utility, and Plugins Extraction | Complete |
144 | 4.2 | FEAT-008 | Registry Cleanup, Documentation, and Full Validation | Complete |
145
146 ## Replay Status (EPIC-001)
147
148 FEAT-001's group-owned built-in command direction is represented on `main` by
149 the newer trait-backed registry and nested group tree. FEAT-002 is replayed as
150 the dedicated user-command registry boundary. FEAT-003 is replayed as public
151 architecture and PR/issue evidence documentation, updated for the current
152 `main` target instead of the old `release/v0.8.60` branch.
153
153 lines MARKDOWN