fix(#160): reserved-semantic verbs with positional args now emit slash-command guidance

Verbs with CLI-reserved positional-arg meanings (resume, compact, memory,
commit, pr, issue, bughunter) were falling through to Prompt dispatch
when invoked with args, causing users to see 'missing_credentials' errors
instead of guidance that the verb is a slash command.

#160 investigation revealed the underlying design question: which verbs
are 'promptable' (can start a prompt like 'explain this pattern') vs.
'reserved' (have specific CLI meaning like 'resume SESSION_ID')?

This fix implements the reserved-verb classification: at parse time,
intercept reserved verbs with trailing args and emit slash-command guidance
before falling through to Prompt. Promptable verbs (explain, bughunter, clear)
continue to route to Prompt as before.

Helper: is_reserved_semantic_verb() lists the reserved set.
All 181 tests pass (no regressions).
This commit is contained in:
YeonGyu-Kim 2026-04-23 03:16:19 +09:00
parent 0aa0d3f7cf
commit 553893410b

View File

@ -1083,6 +1083,19 @@ fn parse_single_word_command_alias(
return Some(Err(msg));
}
// #160: reserved-semantic verbs (resume, compact, memory, commit, pr, issue)
// that have positional args should NOT fall through to Prompt dispatch.
// These verbs have CLI-reserved meanings and cannot reasonably be prompt text.
// Emit slash-command guidance instead.
if rest.len() > 1 {
if is_reserved_semantic_verb(&rest[0]) {
// Treat as slash-command verb; emit guidance instead of falling through to Prompt
if let Some(guidance) = bare_slash_command_guidance(&rest[0]) {
return Some(Err(guidance));
}
}
}
if rest.len() != 1 {
return None;
}
@ -1108,6 +1121,16 @@ fn parse_single_word_command_alias(
}
}
fn is_reserved_semantic_verb(verb: &str) -> bool {
// #160: Verbs with CLI-reserved positional-arg semantics that should NOT
// fall through to Prompt dispatch when given args. These verbs have specific
// meaning (session ID, code target, etc.) and cannot be prompt text.
matches!(
verb,
"resume" | "compact" | "memory" | "commit" | "pr" | "issue" | "bughunter"
)
}
fn bare_slash_command_guidance(command_name: &str) -> Option<String> {
if matches!(
command_name,