Skip to content

Commit 2ed2b70

Browse files
jgarzikclaude
andcommitted
Add context compaction, slash commands, and HTTP/SSE MCP transport
Features implemented: - Context compaction: /compact command to summarize older messages and reclaim context space when conversations grow long - Slash commands: User-defined markdown commands in .yo/commands/<name>.md with $ARGUMENTS placeholder and optional YAML frontmatter - HTTP/SSE MCP transport: Support for HTTP and SSE transports in addition to stdio for Model Context Protocol servers - force_continue: Stop hooks can now request continuation with a new prompt New files: - src/compact.rs: Context compaction/summarization module - src/commands.rs: Slash commands system 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 99a044d commit 2ed2b70

11 files changed

Lines changed: 968 additions & 66 deletions

File tree

src/agent.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::{
44
cli::Context,
5-
llm,
5+
llm::{self, LlmClient},
66
plan::{self, PlanPhase},
77
policy::Decision,
88
tools,
@@ -34,6 +34,15 @@ impl CommandStats {
3434
}
3535
}
3636

37+
/// Result of a turn, including stats and continuation info
38+
#[derive(Debug, Default, Clone)]
39+
pub struct TurnResult {
40+
pub stats: CommandStats,
41+
/// If true, a Stop hook requested continuation with the given prompt
42+
pub force_continue: bool,
43+
pub continue_prompt: Option<String>,
44+
}
45+
3746
const SYSTEM_PROMPT: &str = r#"You are an agentic coding assistant running locally.
3847
You can only access files via tools. All paths are relative to the project root.
3948
Use Glob/Grep to find files before Read. Before Edit/Write, explain what you will change.
@@ -53,12 +62,8 @@ fn verbose(ctx: &Context, message: &str) {
5362
}
5463
}
5564

56-
pub fn run_turn(
57-
ctx: &Context,
58-
user_input: &str,
59-
messages: &mut Vec<Value>,
60-
) -> Result<CommandStats> {
61-
let mut stats = CommandStats::default();
65+
pub fn run_turn(ctx: &Context, user_input: &str, messages: &mut Vec<Value>) -> Result<TurnResult> {
66+
let mut result = TurnResult::default();
6267
let _ = ctx.transcript.borrow_mut().user_message(user_input);
6368

6469
messages.push(json!({
@@ -228,8 +233,8 @@ pub fn run_turn(
228233

229234
// Track token usage from this LLM call
230235
if let Some(usage) = &response.usage {
231-
stats.input_tokens += usage.prompt_tokens;
232-
stats.output_tokens += usage.completion_tokens;
236+
result.stats.input_tokens += usage.prompt_tokens;
237+
result.stats.output_tokens += usage.completion_tokens;
233238

234239
// Record cost for this operation
235240
let turn_number = *ctx.turn_counter.borrow();
@@ -334,7 +339,7 @@ pub fn run_turn(
334339
let args: Value = serde_json::from_str(&tc.function.arguments).unwrap_or(json!({}));
335340

336341
// Count this tool use
337-
stats.tool_uses += 1;
342+
result.stats.tool_uses += 1;
338343

339344
trace(
340345
ctx,
@@ -434,9 +439,9 @@ pub fn run_turn(
434439
}
435440
} else if name == "Task" {
436441
// Execute Task tool (subagent delegation)
437-
let (result, sub_stats) = tools::task::execute(args.clone(), ctx)?;
438-
stats.merge(&sub_stats);
439-
result
442+
let (task_result, sub_stats) = tools::task::execute(args.clone(), ctx)?;
443+
result.stats.merge(&sub_stats);
444+
task_result
440445
} else if name.starts_with("mcp.") {
441446
// Execute MCP tool
442447
let start = std::time::Instant::now();
@@ -531,8 +536,28 @@ pub fn run_turn(
531536
}
532537
}
533538

534-
// Run Stop hooks (note: force_continue not implemented yet)
535-
let _ = ctx.hooks.borrow().on_stop("end_turn", None);
539+
// Run Stop hooks - may request continuation
540+
let last_assistant_message = messages.iter().rev().find_map(|m| {
541+
if m["role"].as_str() == Some("assistant") {
542+
m["content"].as_str().map(|s| s.to_string())
543+
} else {
544+
None
545+
}
546+
});
547+
548+
let (force_continue, continue_prompt) = ctx
549+
.hooks
550+
.borrow()
551+
.on_stop("end_turn", last_assistant_message.as_deref());
552+
553+
// If force_continue is requested, signal to caller to run another turn
554+
if force_continue {
555+
if let Some(prompt) = continue_prompt {
556+
result.force_continue = true;
557+
result.continue_prompt = Some(prompt);
558+
verbose(ctx, "Stop hook requested continuation");
559+
}
560+
}
536561

537-
Ok(stats)
562+
Ok(result)
538563
}

0 commit comments

Comments
 (0)