Skip to content

Commit 992681c

Browse files
committed
Prevent long sessions from stalling and expose the requested internal command surface
The runtime now auto-compacts completed conversations once cumulative input usage crosses a configurable threshold, preserving recent context while surfacing an explicit user notice. The CLI also publishes the requested ant-only slash commands through the shared commands crate and main dispatch, using meaningful local implementations for commit/PR/issue/teleport/debug workflows. Constraint: Reuse the existing Rust compaction pipeline instead of introducing a new summarization stack Constraint: No new dependencies or broad command-framework rewrite Rejected: Implement API-driven compaction inside ConversationRuntime now | too much new plumbing for this delivery Rejected: Expose new commands as parse-only stubs | would not satisfy the requested command availability Confidence: medium Scope-risk: moderate Reversibility: clean Directive: If runtime later gains true API-backed compaction, preserve the TurnSummary auto-compaction metadata shape so CLI call sites stay stable Tested: cargo test; cargo build --release; cargo fmt --all; git diff --check; LSP diagnostics directory check Not-tested: Live Anthropic-backed specialist command flows; gh-authenticated PR/issue creation in a real repo
1 parent a94ef61 commit 992681c

4 files changed

Lines changed: 733 additions & 9 deletions

File tree

rust/crates/commands/src/lib.rs

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,48 @@ const SLASH_COMMAND_SPECS: &[SlashCommandSpec] = &[
117117
argument_hint: None,
118118
resume_supported: true,
119119
},
120+
SlashCommandSpec {
121+
name: "bughunter",
122+
summary: "Inspect the codebase for likely bugs",
123+
argument_hint: Some("[scope]"),
124+
resume_supported: false,
125+
},
126+
SlashCommandSpec {
127+
name: "commit",
128+
summary: "Generate a commit message and create a git commit",
129+
argument_hint: None,
130+
resume_supported: false,
131+
},
132+
SlashCommandSpec {
133+
name: "pr",
134+
summary: "Draft or create a pull request from the conversation",
135+
argument_hint: Some("[context]"),
136+
resume_supported: false,
137+
},
138+
SlashCommandSpec {
139+
name: "issue",
140+
summary: "Draft or create a GitHub issue from the conversation",
141+
argument_hint: Some("[context]"),
142+
resume_supported: false,
143+
},
144+
SlashCommandSpec {
145+
name: "ultraplan",
146+
summary: "Run a deep planning prompt with multi-step reasoning",
147+
argument_hint: Some("[task]"),
148+
resume_supported: false,
149+
},
150+
SlashCommandSpec {
151+
name: "teleport",
152+
summary: "Jump to a file or symbol by searching the workspace",
153+
argument_hint: Some("<symbol-or-path>"),
154+
resume_supported: false,
155+
},
156+
SlashCommandSpec {
157+
name: "debug-tool-call",
158+
summary: "Replay the last tool call with debug details",
159+
argument_hint: None,
160+
resume_supported: false,
161+
},
120162
SlashCommandSpec {
121163
name: "export",
122164
summary: "Export the current conversation to a file",
@@ -136,6 +178,23 @@ pub enum SlashCommand {
136178
Help,
137179
Status,
138180
Compact,
181+
Bughunter {
182+
scope: Option<String>,
183+
},
184+
Commit,
185+
Pr {
186+
context: Option<String>,
187+
},
188+
Issue {
189+
context: Option<String>,
190+
},
191+
Ultraplan {
192+
task: Option<String>,
193+
},
194+
Teleport {
195+
target: Option<String>,
196+
},
197+
DebugToolCall,
139198
Model {
140199
model: Option<String>,
141200
},
@@ -180,6 +239,23 @@ impl SlashCommand {
180239
"help" => Self::Help,
181240
"status" => Self::Status,
182241
"compact" => Self::Compact,
242+
"bughunter" => Self::Bughunter {
243+
scope: remainder_after_command(trimmed, command),
244+
},
245+
"commit" => Self::Commit,
246+
"pr" => Self::Pr {
247+
context: remainder_after_command(trimmed, command),
248+
},
249+
"issue" => Self::Issue {
250+
context: remainder_after_command(trimmed, command),
251+
},
252+
"ultraplan" => Self::Ultraplan {
253+
task: remainder_after_command(trimmed, command),
254+
},
255+
"teleport" => Self::Teleport {
256+
target: remainder_after_command(trimmed, command),
257+
},
258+
"debug-tool-call" => Self::DebugToolCall,
183259
"model" => Self::Model {
184260
model: parts.next().map(ToOwned::to_owned),
185261
},
@@ -212,6 +288,15 @@ impl SlashCommand {
212288
}
213289
}
214290

291+
fn remainder_after_command(input: &str, command: &str) -> Option<String> {
292+
input
293+
.trim()
294+
.strip_prefix(&format!("/{command}"))
295+
.map(str::trim)
296+
.filter(|value| !value.is_empty())
297+
.map(ToOwned::to_owned)
298+
}
299+
215300
#[must_use]
216301
pub fn slash_command_specs() -> &'static [SlashCommandSpec] {
217302
SLASH_COMMAND_SPECS
@@ -279,6 +364,13 @@ pub fn handle_slash_command(
279364
session: session.clone(),
280365
}),
281366
SlashCommand::Status
367+
| SlashCommand::Bughunter { .. }
368+
| SlashCommand::Commit
369+
| SlashCommand::Pr { .. }
370+
| SlashCommand::Issue { .. }
371+
| SlashCommand::Ultraplan { .. }
372+
| SlashCommand::Teleport { .. }
373+
| SlashCommand::DebugToolCall
282374
| SlashCommand::Model { .. }
283375
| SlashCommand::Permissions { .. }
284376
| SlashCommand::Clear { .. }
@@ -307,6 +399,41 @@ mod tests {
307399
fn parses_supported_slash_commands() {
308400
assert_eq!(SlashCommand::parse("/help"), Some(SlashCommand::Help));
309401
assert_eq!(SlashCommand::parse(" /status "), Some(SlashCommand::Status));
402+
assert_eq!(
403+
SlashCommand::parse("/bughunter runtime"),
404+
Some(SlashCommand::Bughunter {
405+
scope: Some("runtime".to_string())
406+
})
407+
);
408+
assert_eq!(SlashCommand::parse("/commit"), Some(SlashCommand::Commit));
409+
assert_eq!(
410+
SlashCommand::parse("/pr ready for review"),
411+
Some(SlashCommand::Pr {
412+
context: Some("ready for review".to_string())
413+
})
414+
);
415+
assert_eq!(
416+
SlashCommand::parse("/issue flaky test"),
417+
Some(SlashCommand::Issue {
418+
context: Some("flaky test".to_string())
419+
})
420+
);
421+
assert_eq!(
422+
SlashCommand::parse("/ultraplan ship both features"),
423+
Some(SlashCommand::Ultraplan {
424+
task: Some("ship both features".to_string())
425+
})
426+
);
427+
assert_eq!(
428+
SlashCommand::parse("/teleport conversation.rs"),
429+
Some(SlashCommand::Teleport {
430+
target: Some("conversation.rs".to_string())
431+
})
432+
);
433+
assert_eq!(
434+
SlashCommand::parse("/debug-tool-call"),
435+
Some(SlashCommand::DebugToolCall)
436+
);
310437
assert_eq!(
311438
SlashCommand::parse("/model claude-opus"),
312439
Some(SlashCommand::Model {
@@ -374,6 +501,13 @@ mod tests {
374501
assert!(help.contains("/help"));
375502
assert!(help.contains("/status"));
376503
assert!(help.contains("/compact"));
504+
assert!(help.contains("/bughunter [scope]"));
505+
assert!(help.contains("/commit"));
506+
assert!(help.contains("/pr [context]"));
507+
assert!(help.contains("/issue [context]"));
508+
assert!(help.contains("/ultraplan [task]"));
509+
assert!(help.contains("/teleport <symbol-or-path>"));
510+
assert!(help.contains("/debug-tool-call"));
377511
assert!(help.contains("/model [model]"));
378512
assert!(help.contains("/permissions [read-only|workspace-write|danger-full-access]"));
379513
assert!(help.contains("/clear [--confirm]"));
@@ -386,7 +520,7 @@ mod tests {
386520
assert!(help.contains("/version"));
387521
assert!(help.contains("/export [file]"));
388522
assert!(help.contains("/session [list|switch <session-id>]"));
389-
assert_eq!(slash_command_specs().len(), 15);
523+
assert_eq!(slash_command_specs().len(), 22);
390524
assert_eq!(resume_supported_slash_commands().len(), 11);
391525
}
392526

@@ -434,6 +568,22 @@ mod tests {
434568
let session = Session::new();
435569
assert!(handle_slash_command("/unknown", &session, CompactionConfig::default()).is_none());
436570
assert!(handle_slash_command("/status", &session, CompactionConfig::default()).is_none());
571+
assert!(
572+
handle_slash_command("/bughunter", &session, CompactionConfig::default()).is_none()
573+
);
574+
assert!(handle_slash_command("/commit", &session, CompactionConfig::default()).is_none());
575+
assert!(handle_slash_command("/pr", &session, CompactionConfig::default()).is_none());
576+
assert!(handle_slash_command("/issue", &session, CompactionConfig::default()).is_none());
577+
assert!(
578+
handle_slash_command("/ultraplan", &session, CompactionConfig::default()).is_none()
579+
);
580+
assert!(
581+
handle_slash_command("/teleport foo", &session, CompactionConfig::default()).is_none()
582+
);
583+
assert!(
584+
handle_slash_command("/debug-tool-call", &session, CompactionConfig::default())
585+
.is_none()
586+
);
437587
assert!(
438588
handle_slash_command("/model claude", &session, CompactionConfig::default()).is_none()
439589
);

0 commit comments

Comments
 (0)