mirror of
https://github.com/instructkr/claw-code.git
synced 2026-05-18 21:41:26 +08:00
style: cargo fmt fix (ci)
This commit is contained in:
@@ -273,7 +273,10 @@ fn classify_error_kind(message: &str) -> &'static str {
|
|||||||
// Check specific patterns first (more specific before generic)
|
// Check specific patterns first (more specific before generic)
|
||||||
if message.contains("missing Anthropic credentials") {
|
if message.contains("missing Anthropic credentials") {
|
||||||
"missing_credentials"
|
"missing_credentials"
|
||||||
} else if message.contains("export failed:") || message.contains("diff failed:") || message.contains("config failed:") {
|
} else if message.contains("export failed:")
|
||||||
|
|| message.contains("diff failed:")
|
||||||
|
|| message.contains("config failed:")
|
||||||
|
{
|
||||||
// #130b: Filesystem operation errors enriched with operation+path context.
|
// #130b: Filesystem operation errors enriched with operation+path context.
|
||||||
"filesystem_io_error"
|
"filesystem_io_error"
|
||||||
} else if message.contains("Manifest source files are missing") {
|
} else if message.contains("Manifest source files are missing") {
|
||||||
@@ -840,13 +843,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
"--help" | "-h"
|
"--help" | "-h"
|
||||||
if !rest.is_empty()
|
if !rest.is_empty() && matches!(rest[0].as_str(), "commit" | "pr" | "issue") =>
|
||||||
&& matches!(
|
|
||||||
rest[0].as_str(),
|
|
||||||
"commit"
|
|
||||||
| "pr"
|
|
||||||
| "issue"
|
|
||||||
) =>
|
|
||||||
{
|
{
|
||||||
// `--help` following a subcommand that would otherwise forward
|
// `--help` following a subcommand that would otherwise forward
|
||||||
// the arg to the API should show top-level help instead.
|
// the arg to the API should show top-level help instead.
|
||||||
@@ -2654,63 +2651,61 @@ fn check_workspace_health(context: &StatusContext) -> DiagnosticCheck {
|
|||||||
"current directory is not inside a git project".to_string(),
|
"current directory is not inside a git project".to_string(),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
DiagnosticCheck::new(
|
DiagnosticCheck::new("Workspace", level, summary)
|
||||||
"Workspace",
|
.with_details(vec![
|
||||||
level,
|
format!("Cwd {}", context.cwd.display()),
|
||||||
summary,
|
format!(
|
||||||
)
|
"Project root {}",
|
||||||
.with_details(vec![
|
context
|
||||||
format!("Cwd {}", context.cwd.display()),
|
.project_root
|
||||||
format!(
|
.as_ref()
|
||||||
"Project root {}",
|
.map_or_else(|| "<none>".to_string(), |path| path.display().to_string())
|
||||||
context
|
),
|
||||||
.project_root
|
format!(
|
||||||
.as_ref()
|
"Git branch {}",
|
||||||
.map_or_else(|| "<none>".to_string(), |path| path.display().to_string())
|
context.git_branch.as_deref().unwrap_or("unknown")
|
||||||
),
|
),
|
||||||
format!(
|
format!("Git state {}", context.git_summary.headline()),
|
||||||
"Git branch {}",
|
format!("Changed files {}", context.git_summary.changed_files),
|
||||||
context.git_branch.as_deref().unwrap_or("unknown")
|
format!(
|
||||||
),
|
"Memory files {} · config files loaded {}/{}",
|
||||||
format!("Git state {}", context.git_summary.headline()),
|
context.memory_file_count,
|
||||||
format!("Changed files {}", context.git_summary.changed_files),
|
context.loaded_config_files,
|
||||||
format!(
|
context.discovered_config_files
|
||||||
"Memory files {} · config files loaded {}/{}",
|
),
|
||||||
context.memory_file_count, context.loaded_config_files, context.discovered_config_files
|
])
|
||||||
),
|
.with_data(Map::from_iter([
|
||||||
])
|
("cwd".to_string(), json!(context.cwd.display().to_string())),
|
||||||
.with_data(Map::from_iter([
|
(
|
||||||
("cwd".to_string(), json!(context.cwd.display().to_string())),
|
"project_root".to_string(),
|
||||||
(
|
json!(context
|
||||||
"project_root".to_string(),
|
.project_root
|
||||||
json!(context
|
.as_ref()
|
||||||
.project_root
|
.map(|path| path.display().to_string())),
|
||||||
.as_ref()
|
),
|
||||||
.map(|path| path.display().to_string())),
|
("in_git_repo".to_string(), json!(in_repo)),
|
||||||
),
|
("git_branch".to_string(), json!(context.git_branch)),
|
||||||
("in_git_repo".to_string(), json!(in_repo)),
|
(
|
||||||
("git_branch".to_string(), json!(context.git_branch)),
|
"git_state".to_string(),
|
||||||
(
|
json!(context.git_summary.headline()),
|
||||||
"git_state".to_string(),
|
),
|
||||||
json!(context.git_summary.headline()),
|
(
|
||||||
),
|
"changed_files".to_string(),
|
||||||
(
|
json!(context.git_summary.changed_files),
|
||||||
"changed_files".to_string(),
|
),
|
||||||
json!(context.git_summary.changed_files),
|
(
|
||||||
),
|
"memory_file_count".to_string(),
|
||||||
(
|
json!(context.memory_file_count),
|
||||||
"memory_file_count".to_string(),
|
),
|
||||||
json!(context.memory_file_count),
|
(
|
||||||
),
|
"loaded_config_files".to_string(),
|
||||||
(
|
json!(context.loaded_config_files),
|
||||||
"loaded_config_files".to_string(),
|
),
|
||||||
json!(context.loaded_config_files),
|
(
|
||||||
),
|
"discovered_config_files".to_string(),
|
||||||
(
|
json!(context.discovered_config_files),
|
||||||
"discovered_config_files".to_string(),
|
),
|
||||||
json!(context.discovered_config_files),
|
]))
|
||||||
),
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_sandbox_health(status: &runtime::SandboxStatus) -> DiagnosticCheck {
|
fn check_sandbox_health(status: &runtime::SandboxStatus) -> DiagnosticCheck {
|
||||||
@@ -9467,9 +9462,9 @@ fn convert_messages(messages: &[ConversationMessage]) -> Vec<InputMessage> {
|
|||||||
.blocks
|
.blocks
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|block| match block {
|
.filter_map(|block| match block {
|
||||||
ContentBlock::Text { text } => Some(InputContentBlock::Text {
|
ContentBlock::Text { text } => {
|
||||||
text: text.clone(),
|
Some(InputContentBlock::Text { text: text.clone() })
|
||||||
}),
|
}
|
||||||
ContentBlock::ToolUse { id, name, input } if has_all_tool_results => {
|
ContentBlock::ToolUse { id, name, input } if has_all_tool_results => {
|
||||||
Some(InputContentBlock::ToolUse {
|
Some(InputContentBlock::ToolUse {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
@@ -9509,9 +9504,9 @@ fn convert_messages(messages: &[ConversationMessage]) -> Vec<InputMessage> {
|
|||||||
.blocks
|
.blocks
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|block| match block {
|
.filter_map(|block| match block {
|
||||||
ContentBlock::Text { text } => Some(InputContentBlock::Text {
|
ContentBlock::Text { text } => {
|
||||||
text: text.clone(),
|
Some(InputContentBlock::Text { text: text.clone() })
|
||||||
}),
|
}
|
||||||
ContentBlock::ToolUse { .. } | ContentBlock::ToolResult { .. } => None,
|
ContentBlock::ToolUse { .. } | ContentBlock::ToolResult { .. } => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -10748,8 +10743,7 @@ mod tests {
|
|||||||
// through to `_other => Prompt` and emitted `missing_credentials`
|
// through to `_other => Prompt` and emitted `missing_credentials`
|
||||||
// for purely-local operations.
|
// for purely-local operations.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_args(&["list-sessions".to_string()])
|
parse_args(&["list-sessions".to_string()]).expect("list-sessions should parse"),
|
||||||
.expect("list-sessions should parse"),
|
|
||||||
CliAction::ListSessions {
|
CliAction::ListSessions {
|
||||||
output_format: CliOutputFormat::Text,
|
output_format: CliOutputFormat::Text,
|
||||||
},
|
},
|
||||||
@@ -10767,11 +10761,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_args(&[
|
parse_args(&["load-session".to_string(), "my-session-id".to_string(),])
|
||||||
"load-session".to_string(),
|
.expect("load-session <id> should parse"),
|
||||||
"my-session-id".to_string(),
|
|
||||||
])
|
|
||||||
.expect("load-session <id> should parse"),
|
|
||||||
CliAction::LoadSession {
|
CliAction::LoadSession {
|
||||||
session_reference: "my-session-id".to_string(),
|
session_reference: "my-session-id".to_string(),
|
||||||
output_format: CliOutputFormat::Text,
|
output_format: CliOutputFormat::Text,
|
||||||
@@ -10779,11 +10770,8 @@ mod tests {
|
|||||||
"load-session must dispatch to LoadSession, not fall through to Prompt"
|
"load-session must dispatch to LoadSession, not fall through to Prompt"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_args(&[
|
parse_args(&["delete-session".to_string(), "my-session-id".to_string(),])
|
||||||
"delete-session".to_string(),
|
.expect("delete-session <id> should parse"),
|
||||||
"my-session-id".to_string(),
|
|
||||||
])
|
|
||||||
.expect("delete-session <id> should parse"),
|
|
||||||
CliAction::DeleteSession {
|
CliAction::DeleteSession {
|
||||||
session_id: "my-session-id".to_string(),
|
session_id: "my-session-id".to_string(),
|
||||||
output_format: CliOutputFormat::Text,
|
output_format: CliOutputFormat::Text,
|
||||||
@@ -10791,11 +10779,8 @@ mod tests {
|
|||||||
"delete-session must dispatch to DeleteSession, not fall through to Prompt"
|
"delete-session must dispatch to DeleteSession, not fall through to Prompt"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_args(&[
|
parse_args(&["flush-transcript".to_string(), "my-session-id".to_string(),])
|
||||||
"flush-transcript".to_string(),
|
.expect("flush-transcript <id> should parse"),
|
||||||
"my-session-id".to_string(),
|
|
||||||
])
|
|
||||||
.expect("flush-transcript <id> should parse"),
|
|
||||||
CliAction::FlushTranscript {
|
CliAction::FlushTranscript {
|
||||||
session_id: "my-session-id".to_string(),
|
session_id: "my-session-id".to_string(),
|
||||||
output_format: CliOutputFormat::Text,
|
output_format: CliOutputFormat::Text,
|
||||||
@@ -10816,11 +10801,8 @@ mod tests {
|
|||||||
"missing session-id error should be specific, got: {delete_err}"
|
"missing session-id error should be specific, got: {delete_err}"
|
||||||
);
|
);
|
||||||
// #251: extra arguments must be rejected
|
// #251: extra arguments must be rejected
|
||||||
let extra_err = parse_args(&[
|
let extra_err = parse_args(&["list-sessions".to_string(), "unexpected".to_string()])
|
||||||
"list-sessions".to_string(),
|
.expect_err("list-sessions with extra args should be rejected");
|
||||||
"unexpected".to_string(),
|
|
||||||
])
|
|
||||||
.expect_err("list-sessions with extra args should be rejected");
|
|
||||||
assert!(
|
assert!(
|
||||||
extra_err.contains("unexpected extra arguments"),
|
extra_err.contains("unexpected extra arguments"),
|
||||||
"extra-args error should be specific, got: {extra_err}"
|
"extra-args error should be specific, got: {extra_err}"
|
||||||
@@ -10830,7 +10812,8 @@ mod tests {
|
|||||||
// "filesystem_io_error" kind, not default "unknown". This closes the
|
// "filesystem_io_error" kind, not default "unknown". This closes the
|
||||||
// context-loss chain (run_export -> fs::write -> ? -> to_string ->
|
// context-loss chain (run_export -> fs::write -> ? -> to_string ->
|
||||||
// classify miss -> unknown) that #130b identified.
|
// classify miss -> unknown) that #130b identified.
|
||||||
let export_err_msg = "export failed: /tmp/bad/path (No such file or directory (os error 2))";
|
let export_err_msg =
|
||||||
|
"export failed: /tmp/bad/path (No such file or directory (os error 2))";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind(export_err_msg),
|
classify_error_kind(export_err_msg),
|
||||||
"filesystem_io_error",
|
"filesystem_io_error",
|
||||||
@@ -10868,77 +10851,58 @@ mod tests {
|
|||||||
// Regression: `diff` was the outlier among local introspection commands
|
// Regression: `diff` was the outlier among local introspection commands
|
||||||
// (status/config/mcp all accepted --help) because its parser arm rejected
|
// (status/config/mcp all accepted --help) because its parser arm rejected
|
||||||
// all extra args before help detection could run.
|
// all extra args before help detection could run.
|
||||||
let diff_help_action = parse_args(&[
|
let diff_help_action = parse_args(&["diff".to_string(), "--help".to_string()])
|
||||||
"diff".to_string(),
|
.expect("diff --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("diff --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(diff_help_action, CliAction::HelpTopic(LocalHelpTopic::Diff)),
|
matches!(diff_help_action, CliAction::HelpTopic(LocalHelpTopic::Diff)),
|
||||||
"#130c: diff --help must route to LocalHelpTopic::Diff, got: {diff_help_action:?}"
|
"#130c: diff --help must route to LocalHelpTopic::Diff, got: {diff_help_action:?}"
|
||||||
);
|
);
|
||||||
let diff_h_action = parse_args(&[
|
let diff_h_action = parse_args(&["diff".to_string(), "-h".to_string()])
|
||||||
"diff".to_string(),
|
.expect("diff -h must parse as help action");
|
||||||
"-h".to_string(),
|
|
||||||
])
|
|
||||||
.expect("diff -h must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(diff_h_action, CliAction::HelpTopic(LocalHelpTopic::Diff)),
|
matches!(diff_h_action, CliAction::HelpTopic(LocalHelpTopic::Diff)),
|
||||||
"#130c: diff -h (short form) must route to LocalHelpTopic::Diff"
|
"#130c: diff -h (short form) must route to LocalHelpTopic::Diff"
|
||||||
);
|
);
|
||||||
// #130c: bare `claw diff` still routes to Diff action, not help.
|
// #130c: bare `claw diff` still routes to Diff action, not help.
|
||||||
let diff_action = parse_args(&[
|
let diff_action =
|
||||||
"diff".to_string(),
|
parse_args(&["diff".to_string()]).expect("bare diff must parse as diff action");
|
||||||
])
|
|
||||||
.expect("bare diff must parse as diff action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(diff_action, CliAction::Diff { .. }),
|
matches!(diff_action, CliAction::Diff { .. }),
|
||||||
"#130c: bare diff must still route to Diff action, got: {diff_action:?}"
|
"#130c: bare diff must still route to Diff action, got: {diff_action:?}"
|
||||||
);
|
);
|
||||||
// #130c: unknown args still rejected (non-regression).
|
// #130c: unknown args still rejected (non-regression).
|
||||||
let diff_bad_arg = parse_args(&[
|
let diff_bad_arg = parse_args(&["diff".to_string(), "foo".to_string()])
|
||||||
"diff".to_string(),
|
.expect_err("diff foo must still be rejected as extra args");
|
||||||
"foo".to_string(),
|
|
||||||
])
|
|
||||||
.expect_err("diff foo must still be rejected as extra args");
|
|
||||||
assert!(
|
assert!(
|
||||||
diff_bad_arg.contains("unexpected extra arguments"),
|
diff_bad_arg.contains("unexpected extra arguments"),
|
||||||
"#130c: diff with unknown arg must still error, got: {diff_bad_arg}"
|
"#130c: diff with unknown arg must still error, got: {diff_bad_arg}"
|
||||||
);
|
);
|
||||||
// #130d: `claw config --help` must route to help topic, not silently run config.
|
// #130d: `claw config --help` must route to help topic, not silently run config.
|
||||||
let config_help_action = parse_args(&[
|
let config_help_action = parse_args(&["config".to_string(), "--help".to_string()])
|
||||||
"config".to_string(),
|
.expect("config --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("config --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config_help_action, CliAction::HelpTopic(LocalHelpTopic::Config)),
|
matches!(config_help_action, CliAction::HelpTopic(LocalHelpTopic::Config)),
|
||||||
"#130d: config --help must route to LocalHelpTopic::Config, got: {config_help_action:?}"
|
"#130d: config --help must route to LocalHelpTopic::Config, got: {config_help_action:?}"
|
||||||
);
|
);
|
||||||
let config_h_action = parse_args(&[
|
let config_h_action = parse_args(&["config".to_string(), "-h".to_string()])
|
||||||
"config".to_string(),
|
.expect("config -h must parse as help action");
|
||||||
"-h".to_string(),
|
|
||||||
])
|
|
||||||
.expect("config -h must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config_h_action, CliAction::HelpTopic(LocalHelpTopic::Config)),
|
matches!(
|
||||||
|
config_h_action,
|
||||||
|
CliAction::HelpTopic(LocalHelpTopic::Config)
|
||||||
|
),
|
||||||
"#130d: config -h (short form) must route to LocalHelpTopic::Config"
|
"#130d: config -h (short form) must route to LocalHelpTopic::Config"
|
||||||
);
|
);
|
||||||
// #130d: bare `claw config` still routes to Config action with no section
|
// #130d: bare `claw config` still routes to Config action with no section
|
||||||
let config_action = parse_args(&[
|
let config_action =
|
||||||
"config".to_string(),
|
parse_args(&["config".to_string()]).expect("bare config must parse as config action");
|
||||||
])
|
|
||||||
.expect("bare config must parse as config action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config_action, CliAction::Config { section: None, .. }),
|
matches!(config_action, CliAction::Config { section: None, .. }),
|
||||||
"#130d: bare config must still route to Config action with section=None"
|
"#130d: bare config must still route to Config action with section=None"
|
||||||
);
|
);
|
||||||
// #130d: config with section still works (non-regression)
|
// #130d: config with section still works (non-regression)
|
||||||
let config_section = parse_args(&[
|
let config_section = parse_args(&["config".to_string(), "permissions".to_string()])
|
||||||
"config".to_string(),
|
.expect("config permissions must parse");
|
||||||
"permissions".to_string(),
|
|
||||||
])
|
|
||||||
.expect("config permissions must parse");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config_section, CliAction::Config { section: Some(ref s), .. } if s == "permissions"),
|
matches!(config_section, CliAction::Config { section: Some(ref s), .. } if s == "permissions"),
|
||||||
"#130d: config with section must still work"
|
"#130d: config with section must still work"
|
||||||
@@ -10947,76 +10911,70 @@ mod tests {
|
|||||||
// These previously emitted `missing_credentials` instead of showing help,
|
// These previously emitted `missing_credentials` instead of showing help,
|
||||||
// because parse_local_help_action() didn't route them. Now they route
|
// because parse_local_help_action() didn't route them. Now they route
|
||||||
// to dedicated help topics before credential check.
|
// to dedicated help topics before credential check.
|
||||||
let help_help = parse_args(&[
|
let help_help = parse_args(&["help".to_string(), "--help".to_string()])
|
||||||
"help".to_string(),
|
.expect("help --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("help --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(help_help, CliAction::HelpTopic(LocalHelpTopic::Meta)),
|
matches!(help_help, CliAction::HelpTopic(LocalHelpTopic::Meta)),
|
||||||
"#130e: help --help must route to LocalHelpTopic::Meta, got: {help_help:?}"
|
"#130e: help --help must route to LocalHelpTopic::Meta, got: {help_help:?}"
|
||||||
);
|
);
|
||||||
let submit_help = parse_args(&[
|
let submit_help = parse_args(&["submit".to_string(), "--help".to_string()])
|
||||||
"submit".to_string(),
|
.expect("submit --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("submit --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(submit_help, CliAction::HelpTopic(LocalHelpTopic::Submit)),
|
matches!(submit_help, CliAction::HelpTopic(LocalHelpTopic::Submit)),
|
||||||
"#130e: submit --help must route to LocalHelpTopic::Submit"
|
"#130e: submit --help must route to LocalHelpTopic::Submit"
|
||||||
);
|
);
|
||||||
let resume_help = parse_args(&[
|
let resume_help = parse_args(&["resume".to_string(), "--help".to_string()])
|
||||||
"resume".to_string(),
|
.expect("resume --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("resume --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(resume_help, CliAction::HelpTopic(LocalHelpTopic::Resume)),
|
matches!(resume_help, CliAction::HelpTopic(LocalHelpTopic::Resume)),
|
||||||
"#130e: resume --help must route to LocalHelpTopic::Resume"
|
"#130e: resume --help must route to LocalHelpTopic::Resume"
|
||||||
);
|
);
|
||||||
// Short form `-h` works for all three
|
// Short form `-h` works for all three
|
||||||
let help_h = parse_args(&["help".to_string(), "-h".to_string()])
|
let help_h =
|
||||||
.expect("help -h must parse");
|
parse_args(&["help".to_string(), "-h".to_string()]).expect("help -h must parse");
|
||||||
assert!(matches!(help_h, CliAction::HelpTopic(LocalHelpTopic::Meta)));
|
assert!(matches!(help_h, CliAction::HelpTopic(LocalHelpTopic::Meta)));
|
||||||
let submit_h = parse_args(&["submit".to_string(), "-h".to_string()])
|
let submit_h =
|
||||||
.expect("submit -h must parse");
|
parse_args(&["submit".to_string(), "-h".to_string()]).expect("submit -h must parse");
|
||||||
assert!(matches!(submit_h, CliAction::HelpTopic(LocalHelpTopic::Submit)));
|
assert!(matches!(
|
||||||
let resume_h = parse_args(&["resume".to_string(), "-h".to_string()])
|
submit_h,
|
||||||
.expect("resume -h must parse");
|
CliAction::HelpTopic(LocalHelpTopic::Submit)
|
||||||
assert!(matches!(resume_h, CliAction::HelpTopic(LocalHelpTopic::Resume)));
|
));
|
||||||
|
let resume_h =
|
||||||
|
parse_args(&["resume".to_string(), "-h".to_string()]).expect("resume -h must parse");
|
||||||
|
assert!(matches!(
|
||||||
|
resume_h,
|
||||||
|
CliAction::HelpTopic(LocalHelpTopic::Resume)
|
||||||
|
));
|
||||||
// #130e-B: surface-level help fixes for plugins and prompt.
|
// #130e-B: surface-level help fixes for plugins and prompt.
|
||||||
// These previously emitted "Unknown action" (plugins) or wrong help (prompt).
|
// These previously emitted "Unknown action" (plugins) or wrong help (prompt).
|
||||||
let plugins_help = parse_args(&[
|
let plugins_help = parse_args(&["plugins".to_string(), "--help".to_string()])
|
||||||
"plugins".to_string(),
|
.expect("plugins --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("plugins --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(plugins_help, CliAction::HelpTopic(LocalHelpTopic::Plugins)),
|
matches!(plugins_help, CliAction::HelpTopic(LocalHelpTopic::Plugins)),
|
||||||
"#130e-B: plugins --help must route to LocalHelpTopic::Plugins, got: {plugins_help:?}"
|
"#130e-B: plugins --help must route to LocalHelpTopic::Plugins, got: {plugins_help:?}"
|
||||||
);
|
);
|
||||||
let prompt_help = parse_args(&[
|
let prompt_help = parse_args(&["prompt".to_string(), "--help".to_string()])
|
||||||
"prompt".to_string(),
|
.expect("prompt --help must parse as help action");
|
||||||
"--help".to_string(),
|
|
||||||
])
|
|
||||||
.expect("prompt --help must parse as help action");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(prompt_help, CliAction::HelpTopic(LocalHelpTopic::Prompt)),
|
matches!(prompt_help, CliAction::HelpTopic(LocalHelpTopic::Prompt)),
|
||||||
"#130e-B: prompt --help must route to LocalHelpTopic::Prompt, got: {prompt_help:?}"
|
"#130e-B: prompt --help must route to LocalHelpTopic::Prompt, got: {prompt_help:?}"
|
||||||
);
|
);
|
||||||
// Short forms
|
// Short forms
|
||||||
let plugins_h = parse_args(&["plugins".to_string(), "-h".to_string()])
|
let plugins_h =
|
||||||
.expect("plugins -h must parse");
|
parse_args(&["plugins".to_string(), "-h".to_string()]).expect("plugins -h must parse");
|
||||||
assert!(matches!(plugins_h, CliAction::HelpTopic(LocalHelpTopic::Plugins)));
|
assert!(matches!(
|
||||||
let prompt_h = parse_args(&["prompt".to_string(), "-h".to_string()])
|
plugins_h,
|
||||||
.expect("prompt -h must parse");
|
CliAction::HelpTopic(LocalHelpTopic::Plugins)
|
||||||
assert!(matches!(prompt_h, CliAction::HelpTopic(LocalHelpTopic::Prompt)));
|
));
|
||||||
|
let prompt_h =
|
||||||
|
parse_args(&["prompt".to_string(), "-h".to_string()]).expect("prompt -h must parse");
|
||||||
|
assert!(matches!(
|
||||||
|
prompt_h,
|
||||||
|
CliAction::HelpTopic(LocalHelpTopic::Prompt)
|
||||||
|
));
|
||||||
// Non-regression: `prompt "actual text"` still parses as Prompt action
|
// Non-regression: `prompt "actual text"` still parses as Prompt action
|
||||||
let prompt_action = parse_args(&[
|
let prompt_action = parse_args(&["prompt".to_string(), "hello world".to_string()])
|
||||||
"prompt".to_string(),
|
.expect("prompt with real text must parse");
|
||||||
"hello world".to_string(),
|
|
||||||
])
|
|
||||||
.expect("prompt with real text must parse");
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(prompt_action, CliAction::Prompt { ref prompt, .. } if prompt == "hello world"),
|
matches!(prompt_action, CliAction::Prompt { ref prompt, .. } if prompt == "hello world"),
|
||||||
"#130e-B: prompt with real text must route to Prompt action"
|
"#130e-B: prompt with real text must route to Prompt action"
|
||||||
@@ -11598,9 +11556,7 @@ mod tests {
|
|||||||
"invalid --output-format value must classify as cli_parse"
|
"invalid --output-format value must classify as cli_parse"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind(
|
classify_error_kind("unsupported value for --permission-mode: bogus (expected ...)"),
|
||||||
"unsupported value for --permission-mode: bogus (expected ...)"
|
|
||||||
),
|
|
||||||
"cli_parse",
|
"cli_parse",
|
||||||
"invalid --permission-mode value must classify as cli_parse"
|
"invalid --permission-mode value must classify as cli_parse"
|
||||||
);
|
);
|
||||||
@@ -11681,16 +11637,12 @@ mod tests {
|
|||||||
// Before #171, these were classified `unknown`, breaking typed-error
|
// Before #171, these were classified `unknown`, breaking typed-error
|
||||||
// consumer dispatch on what is clearly a CLI parse error.
|
// consumer dispatch on what is clearly a CLI parse error.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind(
|
classify_error_kind("unexpected extra arguments after `claw list-sessions`: --help"),
|
||||||
"unexpected extra arguments after `claw list-sessions`: --help"
|
|
||||||
),
|
|
||||||
"cli_parse",
|
"cli_parse",
|
||||||
"list-sessions extra args must classify as cli_parse"
|
"list-sessions extra args must classify as cli_parse"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind(
|
classify_error_kind("unexpected extra arguments after `claw plugins list`: --foo"),
|
||||||
"unexpected extra arguments after `claw plugins list`: --foo"
|
|
||||||
),
|
|
||||||
"cli_parse",
|
"cli_parse",
|
||||||
"plugins subcommand extra args must classify as cli_parse"
|
"plugins subcommand extra args must classify as cli_parse"
|
||||||
);
|
);
|
||||||
@@ -11700,9 +11652,7 @@ mod tests {
|
|||||||
"diff extra args must classify as cli_parse"
|
"diff extra args must classify as cli_parse"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind(
|
classify_error_kind("unexpected extra arguments after `claw config show`: --baz"),
|
||||||
"unexpected extra arguments after `claw config show`: --baz"
|
|
||||||
),
|
|
||||||
"cli_parse",
|
"cli_parse",
|
||||||
"config subcommand extra args must classify as cli_parse"
|
"config subcommand extra args must classify as cli_parse"
|
||||||
);
|
);
|
||||||
@@ -11710,9 +11660,7 @@ mod tests {
|
|||||||
// match, so unrelated prose with "unexpected extra arguments" in a
|
// match, so unrelated prose with "unexpected extra arguments" in a
|
||||||
// different structure falls through.
|
// different structure falls through.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
classify_error_kind(
|
classify_error_kind("the API returned unexpected extra arguments in some response"),
|
||||||
"the API returned unexpected extra arguments in some response"
|
|
||||||
),
|
|
||||||
"unknown",
|
"unknown",
|
||||||
"generic prose with 'unexpected extra arguments' should fall through"
|
"generic prose with 'unexpected extra arguments' should fall through"
|
||||||
);
|
);
|
||||||
@@ -14681,18 +14629,17 @@ mod doctor_broad_cwd_tests {
|
|||||||
let check = check_workspace_health(&ctx);
|
let check = check_workspace_health(&ctx);
|
||||||
// Use rendered output as the contract surface.
|
// Use rendered output as the contract surface.
|
||||||
let rendered = render_diagnostic_check(&check);
|
let rendered = render_diagnostic_check(&check);
|
||||||
assert!(rendered.contains("Status ok"),
|
assert!(
|
||||||
"project dir should be OK; got:\n{rendered}");
|
rendered.contains("Status ok"),
|
||||||
|
"project dir should be OK; got:\n{rendered}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn workspace_check_outside_project_reports_warn() {
|
fn workspace_check_outside_project_reports_warn() {
|
||||||
// #122b non-regression: non-broad, non-git dir stays as Warn with the
|
// #122b non-regression: non-broad, non-git dir stays as Warn with the
|
||||||
// "not inside a git project" summary.
|
// "not inside a git project" summary.
|
||||||
let ctx = make_ctx(
|
let ctx = make_ctx(PathBuf::from("/tmp/random-dir-not-project"), None);
|
||||||
PathBuf::from("/tmp/random-dir-not-project"),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
let check = check_workspace_health(&ctx);
|
let check = check_workspace_health(&ctx);
|
||||||
let rendered = render_diagnostic_check(&check);
|
let rendered = render_diagnostic_check(&check);
|
||||||
assert!(
|
assert!(
|
||||||
|
|||||||
@@ -495,8 +495,7 @@ fn prompt_subcommand_without_arg_emits_cli_parse_envelope_with_hint_247() {
|
|||||||
"short reason should match the raw error, envelope: {envelope}"
|
"short reason should match the raw error, envelope: {envelope}"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
envelope["hint"],
|
envelope["hint"], "Run `claw --help` for usage.",
|
||||||
"Run `claw --help` for usage.",
|
|
||||||
"JSON envelope must carry the same help-runbook hint as text mode, envelope: {envelope}"
|
"JSON envelope must carry the same help-runbook hint as text mode, envelope: {envelope}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -677,7 +676,14 @@ fn v1_5_emission_baseline_shape_parity_168c_task4() {
|
|||||||
(
|
(
|
||||||
"doctor",
|
"doctor",
|
||||||
&["doctor"],
|
&["doctor"],
|
||||||
&["checks", "has_failures", "kind", "message", "report", "summary"],
|
&[
|
||||||
|
"checks",
|
||||||
|
"has_failures",
|
||||||
|
"kind",
|
||||||
|
"message",
|
||||||
|
"report",
|
||||||
|
"summary",
|
||||||
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"skills",
|
"skills",
|
||||||
@@ -687,7 +693,14 @@ fn v1_5_emission_baseline_shape_parity_168c_task4() {
|
|||||||
(
|
(
|
||||||
"agents",
|
"agents",
|
||||||
&["agents"],
|
&["agents"],
|
||||||
&["action", "agents", "count", "kind", "summary", "working_directory"],
|
&[
|
||||||
|
"action",
|
||||||
|
"agents",
|
||||||
|
"count",
|
||||||
|
"kind",
|
||||||
|
"summary",
|
||||||
|
"working_directory",
|
||||||
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"system-prompt",
|
"system-prompt",
|
||||||
@@ -736,7 +749,8 @@ fn v1_5_emission_baseline_shape_parity_168c_task4() {
|
|||||||
let mut actual_sorted = actual_keys.clone();
|
let mut actual_sorted = actual_keys.clone();
|
||||||
actual_sorted.sort();
|
actual_sorted.sort();
|
||||||
|
|
||||||
let mut expected_sorted: Vec<String> = expected_keys.iter().map(|s| s.to_string()).collect();
|
let mut expected_sorted: Vec<String> =
|
||||||
|
expected_keys.iter().map(|s| s.to_string()).collect();
|
||||||
expected_sorted.sort();
|
expected_sorted.sort();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -816,8 +830,7 @@ fn unrecognized_argument_still_classifies_as_cli_parse_247_regression_guard() {
|
|||||||
"unrecognized-argument must remain cli_parse, envelope: {envelope}"
|
"unrecognized-argument must remain cli_parse, envelope: {envelope}"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
envelope["hint"],
|
envelope["hint"], "Run `claw --help` for usage.",
|
||||||
"Run `claw --help` for usage.",
|
|
||||||
"unrecognized-argument hint should stay intact, envelope: {envelope}"
|
"unrecognized-argument hint should stay intact, envelope: {envelope}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user