refactor(dispatch): SlashCmd enum + parser, replace 34-branch chain (#827 stage 1)#832
refactor(dispatch): SlashCmd enum + parser, replace 34-branch chain (#827 stage 1)#832NikolayS wants to merge 5 commits into
Conversation
Red phase of #827 stage 1 — TDD discipline mandated by CLAUDE.md. Introduces src/slashcmd.rs with parser tests for every / command currently recognised by dispatch_ai_command. The enum SlashCmd, struct ParsedSlash, parse(), and requires_ai_budget() do not yet exist; the tests fail to compile. The green phase commit will add the implementation. Tests cover: - Each /-command's basic recognition (mirrors metacmd::parse tests). - Argument parsing edge cases (e.g., /dba+, /session save [name], /log-file [path], /explain-share <service>, /n+ vs /n). - requires_ai_budget() returns true for AI commands (/ask, /fix, /explain, /optimize, /describe) and false for the 25 non-AI commands plus the AI-management ones (/clear, /compact, /budget, /init). - Unknown / commands fall through to SlashCmd::Unknown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Green phase of #827 stage 1 — make the parser tests from the previous commit pass. Adds: - SlashCmd enum with one variant per /-command currently dispatched in ai_commands.rs::dispatch_ai_command (mirrors the shape of MetaCmd in src/metacmd.rs). - ParsedSlash carrying the recognised command plus the trimmed raw input. - parse(input) recognising every /-command, parsing arguments into typed variant fields (e.g. /dba <subcommand>, /session <sub> [arg], /log-file [path], /explain-share <service>). - SlashCmd::requires_ai_budget() — true only for /ask, /fix, /explain, /optimize, /describe; false for the 25 non-AI commands and the four AI-management commands (/clear, /compact, /budget, /init). This will replace the hand-curated is_budget_exempt list at ai_commands.rs:399 in the next commit. Order-sensitive prefix checks: /explain-share is matched before /explain; /ns, /nd, /np match before /n so /n+ (handled as a bare match earlier) and /n don't swallow them. All 62 parser tests in src/slashcmd.rs::tests now pass. The new types are not yet wired into dispatch_ai_command; that follows in the refactor commit. Dead-code lint is suppressed there by the integration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor phase of #827 stage 1 — wire SlashCmd into dispatch_ai_command. Replaces the 34-branch \`if let Some(...) = input.strip_prefix(...)\` chain with a single \`match parsed.cmd { ... }\` block, and replaces the hand-curated \`is_budget_exempt\` list with \`parsed.cmd.requires_ai_budget()\`. Mechanics: - Call slashcmd::parse(input) once at the top of the function. - Gate AI commands on parsed.cmd.requires_ai_budget() && check_token_budget(settings) instead of the 25-line OR chain. - Each former \`else if\` branch becomes one match arm. Argument parsing moves out of the dispatcher into slashcmd::parse, so every arm receives typed fields (e.g. SlashCmd::Dba { subcommand, plus }) instead of re-parsing input strings. No observable behaviour change: the same handlers run on the same inputs and produce the same output. All 2125 lib + bin tests pass, including the 62 slashcmd parser tests added in the previous commit. cargo fmt --check and cargo clippy --all-targets --all-features -- -D warnings are clean. Stages 2 (Command trait + CommandRegistry) and 3 (per-command modules under src/commands/) are out of scope and remain tracked in #827 for follow-up PRs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
REV review — PR #832Stage 1 of the dispatch refactor (#827) — typed Bugs + correctness
Tests + docs + guidelines
VerdictPASS (both axes). Recommend a one-line note in the PR description that no-space command forms ( End-to-end test evidenceSystematic — full test suite(Was 2063 + 62 new parser tests in Live smoke testBuilt a fresh All |
Summary
Stage 1 of the three-stage command-dispatch refactor in #827.
Slash commands (
/-prefix) were dispatched through a 34-branchif let Some(...) = input.strip_prefix(...)chain indispatch_ai_command, with a parallel hand-curated 25-entry "budget exempt" list that had to be kept in sync. This PR introduces a typedSlashCmdenum andparse()mirroringMetaCmd/metacmd::parse, eliminating both forms of duplication while preserving every existing observable behaviour.What changed
New module
src/slashcmd.rs(296 LOC of impl + ~600 LOC of parser tests):pub enum SlashCmd— one variant per/-command currently dispatched (35 variants includingUnknown). Mirrors the shape ofMetaCmd.pub struct ParsedSlash { cmd, raw }— analogous toParsedMeta, carries only the fields handlers actually need.pub fn parse(input) -> ParsedSlash— recognises every/-command, parses arguments into typed fields (e.g.SlashCmd::Dba { subcommand, plus },SlashCmd::Session*,SlashCmd::LogFile { path },SlashCmd::ExplainShare { service }).SlashCmd::requires_ai_budget()— returnstruefor/ask,/fix,/explain,/optimize,/describe;falsefor the 25 non-AI commands plus the four AI-management commands (/clear,/compact,/budget,/init). Replaces the hand-curatedis_budget_exemptchain.src/repl/ai_commands.rs::dispatch_ai_command:slashcmd::parse(input)once at the top.parsed.cmd.requires_ai_budget() && check_token_budget(settings).if let Some / else ifchain with a singlematch parsed.cmdblock. Each arm calls the existinghandle_*function with typed args.Module wiring:
mod slashcmd;added tosrc/main.rsand (cfg-gated)src/lib.rs.LOC delta
dispatch_ai_commandbody: 404 → 362 lines (-42, -10%). The budget chain (30 lines) is gone; argument parsing moved into the parser.src/repl/ai_commands.rsoverall: 3155 → 3113 (-42).src/slashcmd.rs: 0 → 976 (296 impl + 680 tests).src/slashcmd.rsnew,src/repl/ai_commands.rs,src/main.rs,src/lib.rs).Test plan
Red/green/refactor TDD as mandated by
CLAUDE.md:test(slashcmd): add failing parser tests for SlashCmd enum. Tests fail to compile becauseSlashCmddoesn't exist yet.feat(slashcmd): implement SlashCmd enum and parser. All 62 parser tests pass.refactor(dispatch): route slash commands through SlashCmd. All 2125 lib + bin tests pass; integration tests untouched and still green.Parser test coverage (62 tests in
src/slashcmd.rs::tests):parse("/ask hello")→SlashCmd::Ask { prompt: "hello" })/dba+plus flag,/session save [name],/log-file [path],/explain-share <service>disambiguated from/explain/askvs/ask hello,/n+vs/nvs/nsrequires_ai_budget()returns the correct value for AI commands (true), AI-management commands (false), all 25 non-AI commands (false), andUnknown(false)SlashCmd::Unknown { input }Verification commands:
cargo test --bin rpg— 2125 passed, 0 failedcargo test(full suite incl. integration) — all greencargo clippy --all-targets --all-features -- -D warnings— cleancargo fmt -- --check— cleancargo check --target wasm32-unknown-unknown --lib— cleanOut of scope (per #827)
Commandtrait +CommandRegistry, unifying both dispatchers behind a singledispatch()entry point. Tracked at refactor: unify slash and backslash command dispatch behind a registry #827.repl/mod.rsandrepl/ai_commands.rsinto per-category modules undersrc/commands/. Tracked at refactor: unify slash and backslash command dispatch behind a registry #827.MetaCmd/dispatch_metaare untouched.ReplSettingsgod-struct refactor is separate work.