feat: b5-git-aware — batch 5 upstream parity

This commit is contained in:
YeonGyu-Kim
2026-04-07 14:51:12 +09:00
parent 506ff55e53
commit 4be4b46bd9
2 changed files with 250 additions and 1 deletions

View File

@@ -86,6 +86,7 @@ const CLI_OPTION_SUGGESTIONS: &[&str] = &[
"--allowed-tools",
"--resume",
"--print",
"--compact",
"-p",
];
@@ -156,8 +157,9 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
output_format,
allowed_tools,
permission_mode,
compact,
} => LiveCli::new(model, true, allowed_tools, permission_mode)?
.run_turn_with_output(&prompt, output_format)?,
.run_turn_with_output(&prompt, output_format, compact)?,
CliAction::Login { output_format } => run_login(output_format)?,
CliAction::Logout { output_format } => run_logout(output_format)?,
CliAction::Doctor { output_format } => run_doctor(output_format)?,
@@ -225,6 +227,7 @@ enum CliAction {
output_format: CliOutputFormat,
allowed_tools: Option<AllowedToolSet>,
permission_mode: PermissionMode,
compact: bool,
},
Login {
output_format: CliOutputFormat,
@@ -283,6 +286,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
let mut wants_help = false;
let mut wants_version = false;
let mut allowed_tool_values = Vec::new();
let mut compact = false;
let mut rest = Vec::new();
let mut index = 0;
@@ -333,6 +337,10 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
permission_mode_override = Some(PermissionMode::DangerFullAccess);
index += 1;
}
"--compact" => {
compact = true;
index += 1;
}
"-p" => {
// Claw Code compat: -p "prompt" = one-shot prompt
let prompt = args[index + 1..].join(" ");
@@ -346,6 +354,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
allowed_tools: normalize_allowed_tools(&allowed_tool_values)?,
permission_mode: permission_mode_override
.unwrap_or_else(default_permission_mode),
compact,
});
}
"--print" => {
@@ -439,6 +448,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
output_format,
allowed_tools,
permission_mode,
compact,
}),
SkillSlashDispatch::Local => Ok(CliAction::Skills {
args,
@@ -461,6 +471,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
output_format,
allowed_tools,
permission_mode,
compact,
})
}
other if other.starts_with('/') => parse_direct_slash_cli_action(
@@ -469,6 +480,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
output_format,
allowed_tools,
permission_mode,
compact,
),
_other => Ok(CliAction::Prompt {
prompt: rest.join(" "),
@@ -476,6 +488,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
output_format,
allowed_tools,
permission_mode,
compact,
}),
}
}
@@ -565,6 +578,7 @@ fn parse_direct_slash_cli_action(
output_format: CliOutputFormat,
allowed_tools: Option<AllowedToolSet>,
permission_mode: PermissionMode,
compact: bool,
) -> Result<CliAction, String> {
let raw = rest.join(" ");
match SlashCommand::parse(&raw) {
@@ -590,6 +604,7 @@ fn parse_direct_slash_cli_action(
output_format,
allowed_tools,
permission_mode,
compact,
}),
SkillSlashDispatch::Local => Ok(CliAction::Skills {
args,
@@ -3204,13 +3219,28 @@ impl LiveCli {
&mut self,
input: &str,
output_format: CliOutputFormat,
compact: bool,
) -> Result<(), Box<dyn std::error::Error>> {
match output_format {
CliOutputFormat::Text if compact => self.run_prompt_compact(input),
CliOutputFormat::Text => self.run_turn(input),
CliOutputFormat::Json => self.run_prompt_json(input),
}
}
fn run_prompt_compact(&mut self, input: &str) -> Result<(), Box<dyn std::error::Error>> {
let (mut runtime, hook_abort_monitor) = self.prepare_turn_runtime(false)?;
let mut permission_prompter = CliPermissionPrompter::new(self.permission_mode);
let result = runtime.run_turn(input, Some(&mut permission_prompter));
hook_abort_monitor.stop();
let summary = result?;
self.replace_runtime(runtime)?;
self.persist_session()?;
let final_text = final_assistant_text(&summary);
println!("{final_text}");
Ok(())
}
fn run_prompt_json(&mut self, input: &str) -> Result<(), Box<dyn std::error::Error>> {
let (mut runtime, hook_abort_monitor) = self.prepare_turn_runtime(false)?;
let mut permission_prompter = CliPermissionPrompter::new(self.permission_mode);
@@ -7007,6 +7037,10 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> {
out,
" --output-format FORMAT Non-interactive output format: text or json"
)?;
writeln!(
out,
" --compact Strip tool call details; print only the final assistant text (text mode only; useful for piping)"
)?;
writeln!(
out,
" --permission-mode MODE Set read-only, workspace-write, or danger-full-access"
@@ -7053,6 +7087,10 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> {
out,
" claw --output-format json prompt \"explain src/main.rs\""
)?;
writeln!(
out,
" claw --compact \"summarize Cargo.toml\" | wc -l"
)?;
writeln!(
out,
" claw --allowedTools read,glob \"summarize Cargo.toml\""
@@ -7595,6 +7633,7 @@ mod tests {
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: PermissionMode::DangerFullAccess,
compact: false,
}
);
}
@@ -7618,10 +7657,56 @@ mod tests {
output_format: CliOutputFormat::Json,
allowed_tools: None,
permission_mode: PermissionMode::DangerFullAccess,
compact: false,
}
);
}
#[test]
fn parses_compact_flag_for_prompt_mode() {
// given a bare prompt invocation that includes the --compact flag
let _guard = env_lock();
std::env::remove_var("RUSTY_CLAUDE_PERMISSION_MODE");
let args = vec![
"--compact".to_string(),
"summarize".to_string(),
"this".to_string(),
];
// when parse_args interprets the flag
let parsed = parse_args(&args).expect("args should parse");
// then compact mode is propagated and other defaults stay unchanged
assert_eq!(
parsed,
CliAction::Prompt {
prompt: "summarize this".to_string(),
model: DEFAULT_MODEL.to_string(),
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: PermissionMode::DangerFullAccess,
compact: true,
}
);
}
#[test]
fn prompt_subcommand_defaults_compact_to_false() {
// given a `prompt` subcommand invocation without --compact
let _guard = env_lock();
std::env::remove_var("RUSTY_CLAUDE_PERMISSION_MODE");
let args = vec!["prompt".to_string(), "hello".to_string()];
// when parse_args runs
let parsed = parse_args(&args).expect("args should parse");
// then compact stays false (opt-in flag)
match parsed {
CliAction::Prompt { compact, .. } => assert!(!compact),
other => panic!("expected Prompt action, got {other:?}"),
}
}
#[test]
fn resolves_model_aliases_in_args() {
let _guard = env_lock();
@@ -7640,6 +7725,7 @@ mod tests {
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: PermissionMode::DangerFullAccess,
compact: false,
}
);
}
@@ -7791,6 +7877,7 @@ mod tests {
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: crate::default_permission_mode(),
compact: false,
}
);
assert_eq!(
@@ -7898,6 +7985,7 @@ mod tests {
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: PermissionMode::DangerFullAccess,
compact: false,
}
);
}
@@ -7962,6 +8050,7 @@ mod tests {
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: crate::default_permission_mode(),
compact: false,
}
);
assert_eq!(
@@ -7985,6 +8074,7 @@ mod tests {
output_format: CliOutputFormat::Text,
allowed_tools: None,
permission_mode: crate::default_permission_mode(),
compact: false,
}
);
let error = parse_args(&["/status".to_string()])