feat: add manual compact command
This commit is contained in:
@@ -138,6 +138,15 @@ impl CommandRegistry {
|
||||
can_execute: always_available,
|
||||
executor: noop_command,
|
||||
});
|
||||
registry.register(CommandSpec {
|
||||
name: "compact",
|
||||
aliases: &[],
|
||||
usage: "compact",
|
||||
description: "Request immediate Pod context compaction.",
|
||||
argument_parser: compact_args,
|
||||
can_execute: compact_available,
|
||||
executor: compact_command,
|
||||
});
|
||||
registry
|
||||
}
|
||||
|
||||
@@ -266,6 +275,34 @@ fn help_args(raw: &str) -> Result<CommandArgs, CommandDiagnostic> {
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_args(raw: &str) -> Result<CommandArgs, CommandDiagnostic> {
|
||||
let args = CommandArgs::parse_whitespace(raw);
|
||||
if args.argv().is_empty() {
|
||||
Ok(args)
|
||||
} else {
|
||||
Err(CommandDiagnostic::new("Invalid arguments. Usage: compact"))
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_available(environment: &CommandEnvironment) -> Result<(), CommandDiagnostic> {
|
||||
if !environment.connected {
|
||||
return Err(CommandDiagnostic::new(
|
||||
"Cannot compact: not connected to a Pod.",
|
||||
));
|
||||
}
|
||||
if environment.running {
|
||||
return Err(CommandDiagnostic::new(
|
||||
"Cannot compact while the Pod is running.",
|
||||
));
|
||||
}
|
||||
if environment.paused {
|
||||
return Err(CommandDiagnostic::new(
|
||||
"Cannot compact while the Pod is paused; resume or start a fresh turn first.",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn help_command(invocation: CommandInvocation<'_>) -> CommandExecution {
|
||||
if let Some(name) = invocation.args.argv().first() {
|
||||
let Some(command) = invocation.registry.find(name) else {
|
||||
@@ -301,6 +338,18 @@ fn noop_command(invocation: CommandInvocation<'_>) -> CommandExecution {
|
||||
CommandExecution::notice("noop: no action")
|
||||
}
|
||||
|
||||
fn compact_command(invocation: CommandInvocation<'_>) -> CommandExecution {
|
||||
let _ = invocation.command;
|
||||
let _ = invocation.environment;
|
||||
let _ = invocation.args.raw();
|
||||
CommandExecution {
|
||||
method: Some(Method::Compact),
|
||||
diagnostics: vec![CommandDiagnostic::new("compact requested")],
|
||||
exit_command_mode: true,
|
||||
clear_input: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -337,4 +386,39 @@ mod tests {
|
||||
assert!(!result.exit_command_mode);
|
||||
assert!(result.diagnostics[0].message.contains("Invalid arguments"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_command_returns_compact_method_not_run() {
|
||||
let registry = CommandRegistry::builtins();
|
||||
let result = registry.dispatch("compact", &env());
|
||||
assert!(matches!(result.method, Some(Method::Compact)));
|
||||
assert!(result.exit_command_mode);
|
||||
assert!(result.clear_input);
|
||||
assert!(result.diagnostics[0].message.contains("compact requested"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_invalid_arguments_are_local_diagnostic() {
|
||||
let registry = CommandRegistry::builtins();
|
||||
let result = registry.dispatch("compact now", &env());
|
||||
assert!(result.method.is_none());
|
||||
assert!(!result.exit_command_mode);
|
||||
assert!(result.diagnostics[0].message.contains("Invalid arguments"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_rejects_running_and_paused_locally() {
|
||||
let registry = CommandRegistry::builtins();
|
||||
let mut running = env();
|
||||
running.running = true;
|
||||
let result = registry.dispatch("compact", &running);
|
||||
assert!(result.method.is_none());
|
||||
assert!(result.diagnostics[0].message.contains("running"));
|
||||
|
||||
let mut paused = env();
|
||||
paused.paused = true;
|
||||
let result = registry.dispatch("compact", &paused);
|
||||
assert!(result.method.is_none());
|
||||
assert!(result.diagnostics[0].message.contains("paused"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,6 +1234,38 @@ mod tests {
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_command_sends_compact_method_without_run() {
|
||||
let mut app = App::new("agent".to_string());
|
||||
app.connected = true;
|
||||
assert!(
|
||||
handle_key(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(':'), KeyModifiers::NONE)
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
for c in "compact".chars() {
|
||||
assert!(
|
||||
handle_key(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE)
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
let method = handle_key(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
assert!(matches!(method, Some(protocol::Method::Compact)));
|
||||
assert!(!app.is_command_mode());
|
||||
assert_eq!(input_text(&app), "");
|
||||
assert_eq!(app.queued_input_count(), 0);
|
||||
assert!(app.blocks.iter().any(|block| match block {
|
||||
crate::block::Block::Alert { message, .. } => message.contains("compact requested"),
|
||||
_ => false,
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_registry_suggestions_are_available() {
|
||||
let mut app = App::new("agent".to_string());
|
||||
|
||||
Reference in New Issue
Block a user