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