mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-24 13:08:11 +08:00
Restore Rust formatting compliance for origin/main
Run cargo fmt across the Rust workspace after rebasing onto origin/main so the branch matches rustfmt's current output and the CI formatter gate can pass again. This commit intentionally contains formatter output only and keeps it separate from the README brand refresh. Constraint: origin/main was failing cargo fmt --all --check Rejected: Manually format only the initially reported files | cargo fmt --all updated additional workspace files that are part of the formatter gate Confidence: high Scope-risk: narrow Reversibility: clean Directive: Do not mix behavior edits into formatter-only commits Tested: cargo fmt --all --check Tested: cargo test --all Tested: git diff --check
This commit is contained in:
parent
e43c6b81db
commit
cc8da08ad3
@ -753,14 +753,14 @@ mod tests {
|
||||
#[test]
|
||||
fn returns_context_window_metadata_for_kimi_models() {
|
||||
// kimi-k2.5
|
||||
let k25_limit = model_token_limit("kimi-k2.5")
|
||||
.expect("kimi-k2.5 should have token limit metadata");
|
||||
let k25_limit =
|
||||
model_token_limit("kimi-k2.5").expect("kimi-k2.5 should have token limit metadata");
|
||||
assert_eq!(k25_limit.max_output_tokens, 16_384);
|
||||
assert_eq!(k25_limit.context_window_tokens, 256_000);
|
||||
|
||||
// kimi-k1.5
|
||||
let k15_limit = model_token_limit("kimi-k1.5")
|
||||
.expect("kimi-k1.5 should have token limit metadata");
|
||||
let k15_limit =
|
||||
model_token_limit("kimi-k1.5").expect("kimi-k1.5 should have token limit metadata");
|
||||
assert_eq!(k15_limit.max_output_tokens, 16_384);
|
||||
assert_eq!(k15_limit.context_window_tokens, 256_000);
|
||||
}
|
||||
@ -768,11 +768,13 @@ mod tests {
|
||||
#[test]
|
||||
fn kimi_alias_resolves_to_kimi_k25_token_limits() {
|
||||
// The "kimi" alias resolves to "kimi-k2.5" via resolve_model_alias()
|
||||
let alias_limit = model_token_limit("kimi")
|
||||
.expect("kimi alias should resolve to kimi-k2.5 limits");
|
||||
let direct_limit = model_token_limit("kimi-k2.5")
|
||||
.expect("kimi-k2.5 should have limits");
|
||||
assert_eq!(alias_limit.max_output_tokens, direct_limit.max_output_tokens);
|
||||
let alias_limit =
|
||||
model_token_limit("kimi").expect("kimi alias should resolve to kimi-k2.5 limits");
|
||||
let direct_limit = model_token_limit("kimi-k2.5").expect("kimi-k2.5 should have limits");
|
||||
assert_eq!(
|
||||
alias_limit.max_output_tokens,
|
||||
direct_limit.max_output_tokens
|
||||
);
|
||||
assert_eq!(
|
||||
alias_limit.context_window_tokens,
|
||||
direct_limit.context_window_tokens
|
||||
|
||||
@ -2195,9 +2195,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn provider_specific_size_limits_are_correct() {
|
||||
assert_eq!(OpenAiCompatConfig::dashscope().max_request_body_bytes, 6_291_456); // 6MB
|
||||
assert_eq!(OpenAiCompatConfig::openai().max_request_body_bytes, 104_857_600); // 100MB
|
||||
assert_eq!(OpenAiCompatConfig::xai().max_request_body_bytes, 52_428_800); // 50MB
|
||||
assert_eq!(
|
||||
OpenAiCompatConfig::dashscope().max_request_body_bytes,
|
||||
6_291_456
|
||||
); // 6MB
|
||||
assert_eq!(
|
||||
OpenAiCompatConfig::openai().max_request_body_bytes,
|
||||
104_857_600
|
||||
); // 100MB
|
||||
assert_eq!(OpenAiCompatConfig::xai().max_request_body_bytes, 52_428_800);
|
||||
// 50MB
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -2623,10 +2623,8 @@ fn render_mcp_report_json_for(
|
||||
// runs, the existing serializer adds `status: "ok"` below.
|
||||
match loader.load() {
|
||||
Ok(runtime_config) => {
|
||||
let mut value = render_mcp_summary_report_json(
|
||||
cwd,
|
||||
runtime_config.mcp().servers(),
|
||||
);
|
||||
let mut value =
|
||||
render_mcp_summary_report_json(cwd, runtime_config.mcp().servers());
|
||||
if let Some(map) = value.as_object_mut() {
|
||||
map.insert("status".to_string(), Value::String("ok".to_string()));
|
||||
map.insert("config_load_error".to_string(), Value::Null);
|
||||
|
||||
@ -172,7 +172,7 @@ async fn execute_bash_async(
|
||||
) -> io::Result<BashCommandOutput> {
|
||||
// Detect and emit ship provenance for git push operations
|
||||
detect_and_emit_ship_prepared(&input.command);
|
||||
|
||||
|
||||
let mut command = prepare_tokio_command(&input.command, &cwd, &sandbox_status, true);
|
||||
|
||||
let output_result = if let Some(timeout_ms) = input.timeout {
|
||||
|
||||
@ -405,7 +405,10 @@ pub enum BlockedSubphase {
|
||||
#[serde(rename = "blocked.branch_freshness")]
|
||||
BranchFreshness { behind_main: u32 },
|
||||
#[serde(rename = "blocked.test_hang")]
|
||||
TestHang { elapsed_secs: u32, test_name: Option<String> },
|
||||
TestHang {
|
||||
elapsed_secs: u32,
|
||||
test_name: Option<String>,
|
||||
},
|
||||
#[serde(rename = "blocked.report_pending")]
|
||||
ReportPending { since_secs: u32 },
|
||||
}
|
||||
@ -543,7 +546,8 @@ impl LaneEvent {
|
||||
.with_failure_class(blocker.failure_class)
|
||||
.with_detail(blocker.detail.clone());
|
||||
if let Some(ref subphase) = blocker.subphase {
|
||||
event = event.with_data(serde_json::to_value(subphase).expect("subphase should serialize"));
|
||||
event =
|
||||
event.with_data(serde_json::to_value(subphase).expect("subphase should serialize"));
|
||||
}
|
||||
event
|
||||
}
|
||||
@ -554,7 +558,8 @@ impl LaneEvent {
|
||||
.with_failure_class(blocker.failure_class)
|
||||
.with_detail(blocker.detail.clone());
|
||||
if let Some(ref subphase) = blocker.subphase {
|
||||
event = event.with_data(serde_json::to_value(subphase).expect("subphase should serialize"));
|
||||
event =
|
||||
event.with_data(serde_json::to_value(subphase).expect("subphase should serialize"));
|
||||
}
|
||||
event
|
||||
}
|
||||
@ -562,8 +567,12 @@ impl LaneEvent {
|
||||
/// Ship prepared — §4.44.5
|
||||
#[must_use]
|
||||
pub fn ship_prepared(emitted_at: impl Into<String>, provenance: &ShipProvenance) -> Self {
|
||||
Self::new(LaneEventName::ShipPrepared, LaneEventStatus::Ready, emitted_at)
|
||||
.with_data(serde_json::to_value(provenance).expect("ship provenance should serialize"))
|
||||
Self::new(
|
||||
LaneEventName::ShipPrepared,
|
||||
LaneEventStatus::Ready,
|
||||
emitted_at,
|
||||
)
|
||||
.with_data(serde_json::to_value(provenance).expect("ship provenance should serialize"))
|
||||
}
|
||||
|
||||
/// Ship commits selected — §4.44.5
|
||||
@ -573,22 +582,34 @@ impl LaneEvent {
|
||||
commit_count: u32,
|
||||
commit_range: impl Into<String>,
|
||||
) -> Self {
|
||||
Self::new(LaneEventName::ShipCommitsSelected, LaneEventStatus::Ready, emitted_at)
|
||||
.with_detail(format!("{} commits: {}", commit_count, commit_range.into()))
|
||||
Self::new(
|
||||
LaneEventName::ShipCommitsSelected,
|
||||
LaneEventStatus::Ready,
|
||||
emitted_at,
|
||||
)
|
||||
.with_detail(format!("{} commits: {}", commit_count, commit_range.into()))
|
||||
}
|
||||
|
||||
/// Ship merged — §4.44.5
|
||||
#[must_use]
|
||||
pub fn ship_merged(emitted_at: impl Into<String>, provenance: &ShipProvenance) -> Self {
|
||||
Self::new(LaneEventName::ShipMerged, LaneEventStatus::Completed, emitted_at)
|
||||
.with_data(serde_json::to_value(provenance).expect("ship provenance should serialize"))
|
||||
Self::new(
|
||||
LaneEventName::ShipMerged,
|
||||
LaneEventStatus::Completed,
|
||||
emitted_at,
|
||||
)
|
||||
.with_data(serde_json::to_value(provenance).expect("ship provenance should serialize"))
|
||||
}
|
||||
|
||||
/// Ship pushed to main — §4.44.5
|
||||
#[must_use]
|
||||
pub fn ship_pushed_main(emitted_at: impl Into<String>, provenance: &ShipProvenance) -> Self {
|
||||
Self::new(LaneEventName::ShipPushedMain, LaneEventStatus::Completed, emitted_at)
|
||||
.with_data(serde_json::to_value(provenance).expect("ship provenance should serialize"))
|
||||
Self::new(
|
||||
LaneEventName::ShipPushedMain,
|
||||
LaneEventStatus::Completed,
|
||||
emitted_at,
|
||||
)
|
||||
.with_data(serde_json::to_value(provenance).expect("ship provenance should serialize"))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@ -58,8 +58,8 @@ impl SessionStore {
|
||||
let workspace_root = workspace_root.as_ref();
|
||||
// #151: canonicalize workspace_root for consistent fingerprinting
|
||||
// across equivalent path representations.
|
||||
let canonical_workspace = fs::canonicalize(workspace_root)
|
||||
.unwrap_or_else(|_| workspace_root.to_path_buf());
|
||||
let canonical_workspace =
|
||||
fs::canonicalize(workspace_root).unwrap_or_else(|_| workspace_root.to_path_buf());
|
||||
let sessions_root = data_dir
|
||||
.as_ref()
|
||||
.join("sessions")
|
||||
@ -158,10 +158,9 @@ impl SessionStore {
|
||||
}
|
||||
|
||||
pub fn latest_session(&self) -> Result<ManagedSessionSummary, SessionControlError> {
|
||||
self.list_sessions()?
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| SessionControlError::Format(format_no_managed_sessions(&self.sessions_root)))
|
||||
self.list_sessions()?.into_iter().next().ok_or_else(|| {
|
||||
SessionControlError::Format(format_no_managed_sessions(&self.sessions_root))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_session(
|
||||
|
||||
@ -228,8 +228,10 @@ fn main() {
|
||||
// don't need to regex-scrape the prose.
|
||||
let kind = classify_error_kind(&message);
|
||||
if message.contains("`claw --help`") {
|
||||
eprintln!("[error-kind: {kind}]
|
||||
error: {message}");
|
||||
eprintln!(
|
||||
"[error-kind: {kind}]
|
||||
error: {message}"
|
||||
);
|
||||
} else {
|
||||
eprintln!(
|
||||
"[error-kind: {kind}]
|
||||
@ -372,7 +374,12 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
model_flag_raw,
|
||||
permission_mode,
|
||||
output_format,
|
||||
} => print_status_snapshot(&model, model_flag_raw.as_deref(), permission_mode, output_format)?,
|
||||
} => print_status_snapshot(
|
||||
&model,
|
||||
model_flag_raw.as_deref(),
|
||||
permission_mode,
|
||||
output_format,
|
||||
)?,
|
||||
CliAction::Sandbox { output_format } => print_sandbox_status_snapshot(output_format)?,
|
||||
CliAction::Prompt {
|
||||
prompt,
|
||||
@ -412,19 +419,17 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CliAction::Config {
|
||||
section,
|
||||
output_format,
|
||||
} => {
|
||||
match output_format {
|
||||
CliOutputFormat::Text => {
|
||||
println!("{}", render_config_report(section.as_deref())?);
|
||||
}
|
||||
CliOutputFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&render_config_json(section.as_deref())?)?
|
||||
);
|
||||
}
|
||||
} => match output_format {
|
||||
CliOutputFormat::Text => {
|
||||
println!("{}", render_config_report(section.as_deref())?);
|
||||
}
|
||||
}
|
||||
CliOutputFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&render_config_json(section.as_deref())?)?
|
||||
);
|
||||
}
|
||||
},
|
||||
CliAction::Diff { output_format } => match output_format {
|
||||
CliOutputFormat::Text => {
|
||||
println!("{}", render_diff_report()?);
|
||||
@ -627,13 +632,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
||||
}
|
||||
"--help" | "-h"
|
||||
if !rest.is_empty()
|
||||
&& matches!(
|
||||
rest[0].as_str(),
|
||||
"prompt"
|
||||
| "commit"
|
||||
| "pr"
|
||||
| "issue"
|
||||
) =>
|
||||
&& matches!(rest[0].as_str(), "prompt" | "commit" | "pr" | "issue") =>
|
||||
{
|
||||
// `--help` following a subcommand that would otherwise forward
|
||||
// the arg to the API (e.g. `claw prompt --help`) should show
|
||||
@ -844,9 +843,13 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
||||
if let Some(action) = parse_local_help_action(&rest) {
|
||||
return action;
|
||||
}
|
||||
if let Some(action) =
|
||||
parse_single_word_command_alias(&rest, &model, model_flag_raw.as_deref(), permission_mode_override, output_format)
|
||||
{
|
||||
if let Some(action) = parse_single_word_command_alias(
|
||||
&rest,
|
||||
&model,
|
||||
model_flag_raw.as_deref(),
|
||||
permission_mode_override,
|
||||
output_format,
|
||||
) {
|
||||
return action;
|
||||
}
|
||||
|
||||
@ -1312,7 +1315,6 @@ fn suggest_closest_term<'a>(input: &str, candidates: &'a [&'a str]) -> Option<&'
|
||||
ranked_suggestions(input, candidates).into_iter().next()
|
||||
}
|
||||
|
||||
|
||||
fn suggest_similar_subcommand(input: &str) -> Option<Vec<String>> {
|
||||
const KNOWN_SUBCOMMANDS: &[&str] = &[
|
||||
"help",
|
||||
@ -1342,8 +1344,7 @@ fn suggest_similar_subcommand(input: &str) -> Option<Vec<String>> {
|
||||
let prefix_match = common_prefix_len(&normalized_input, &normalized_candidate) >= 4;
|
||||
let substring_match = normalized_candidate.contains(&normalized_input)
|
||||
|| normalized_input.contains(&normalized_candidate);
|
||||
((distance <= 2) || prefix_match || substring_match)
|
||||
.then_some((distance, *candidate))
|
||||
((distance <= 2) || prefix_match || substring_match).then_some((distance, *candidate))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
ranked.sort_by(|left, right| left.cmp(right).then_with(|| left.1.cmp(right.1)));
|
||||
@ -1363,7 +1364,6 @@ fn common_prefix_len(left: &str, right: &str) -> usize {
|
||||
.count()
|
||||
}
|
||||
|
||||
|
||||
fn looks_like_subcommand_typo(input: &str) -> bool {
|
||||
!input.is_empty()
|
||||
&& input
|
||||
@ -1472,13 +1472,11 @@ fn validate_model_syntax(model: &str) -> Result<(), String> {
|
||||
err_msg.push_str("\nDid you mean `openai/");
|
||||
err_msg.push_str(trimmed);
|
||||
err_msg.push_str("`? (Requires OPENAI_API_KEY env var)");
|
||||
}
|
||||
else if trimmed.starts_with("qwen") {
|
||||
} else if trimmed.starts_with("qwen") {
|
||||
err_msg.push_str("\nDid you mean `qwen/");
|
||||
err_msg.push_str(trimmed);
|
||||
err_msg.push_str("`? (Requires DASHSCOPE_API_KEY env var)");
|
||||
}
|
||||
else if trimmed.starts_with("grok") {
|
||||
} else if trimmed.starts_with("grok") {
|
||||
err_msg.push_str("\nDid you mean `xai/");
|
||||
err_msg.push_str(trimmed);
|
||||
err_msg.push_str("`? (Requires XAI_API_KEY env var)");
|
||||
@ -4322,7 +4320,6 @@ impl LiveCli {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn run_prompt_compact_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);
|
||||
@ -5440,7 +5437,13 @@ fn print_status_snapshot(
|
||||
match output_format {
|
||||
CliOutputFormat::Text => println!(
|
||||
"{}",
|
||||
format_status_report(&provenance.resolved, usage, permission_mode.as_str(), &context, Some(&provenance))
|
||||
format_status_report(
|
||||
&provenance.resolved,
|
||||
usage,
|
||||
permission_mode.as_str(),
|
||||
&context,
|
||||
Some(&provenance)
|
||||
)
|
||||
),
|
||||
CliOutputFormat::Json => println!(
|
||||
"{}",
|
||||
@ -9006,26 +9009,24 @@ fn print_help(output_format: CliOutputFormat) -> Result<(), Box<dyn std::error::
|
||||
mod tests {
|
||||
use super::{
|
||||
build_runtime_plugin_state_with_loader, build_runtime_with_plugin_state,
|
||||
collect_session_prompt_history, create_managed_session_handle, describe_tool_progress,
|
||||
filter_tool_specs, format_bughunter_report, format_commit_preflight_report,
|
||||
format_commit_skipped_report, format_compact_report, format_connected_line,
|
||||
format_cost_report, format_history_timestamp, format_internal_prompt_progress_line,
|
||||
format_issue_report, format_model_report, format_model_switch_report,
|
||||
format_permissions_report, format_permissions_switch_report, format_pr_report,
|
||||
format_resume_report, format_status_report, format_tool_call_start, format_tool_result,
|
||||
format_ultraplan_report, format_unknown_slash_command,
|
||||
classify_error_kind, collect_session_prompt_history, create_managed_session_handle,
|
||||
describe_tool_progress, filter_tool_specs, format_bughunter_report,
|
||||
format_commit_preflight_report, format_commit_skipped_report, format_compact_report,
|
||||
format_connected_line, format_cost_report, format_history_timestamp,
|
||||
format_internal_prompt_progress_line, format_issue_report, format_model_report,
|
||||
format_model_switch_report, format_permissions_report, format_permissions_switch_report,
|
||||
format_pr_report, format_resume_report, format_status_report, format_tool_call_start,
|
||||
format_tool_result, format_ultraplan_report, format_unknown_slash_command,
|
||||
format_unknown_slash_command_message, format_user_visible_api_error,
|
||||
classify_error_kind,
|
||||
merge_prompt_with_stdin, normalize_permission_mode, parse_args, parse_export_args,
|
||||
parse_git_status_branch, parse_git_status_metadata_for, parse_git_workspace_summary,
|
||||
parse_history_count, permission_policy, print_help_to, push_output_block,
|
||||
render_config_report, render_diff_report, render_diff_report_for, render_memory_report,
|
||||
split_error_hint,
|
||||
render_help_topic, render_prompt_history_report, render_repl_help, render_resume_usage,
|
||||
render_config_report, render_diff_report, render_diff_report_for, render_help_topic,
|
||||
render_memory_report, render_prompt_history_report, render_repl_help, render_resume_usage,
|
||||
render_session_markdown, resolve_model_alias, resolve_model_alias_with_config,
|
||||
resolve_repl_model, resolve_session_reference, response_to_events,
|
||||
resume_supported_slash_commands, run_resume_command, short_tool_id,
|
||||
slash_command_completion_candidates_with_sessions, status_context,
|
||||
slash_command_completion_candidates_with_sessions, split_error_hint, status_context,
|
||||
summarize_tool_payload_for_markdown, try_resolve_bare_skill_prompt, validate_no_args,
|
||||
write_mcp_server_fixture, CliAction, CliOutputFormat, CliToolExecutor, GitWorkspaceSummary,
|
||||
InternalPromptProgressEvent, InternalPromptProgressState, LiveCli, LocalHelpTopic,
|
||||
@ -10006,8 +10007,8 @@ mod tests {
|
||||
// with a specific error instead of falling through to the prompt
|
||||
// path (where they surface a misleading "missing Anthropic
|
||||
// credentials" error or burn API tokens on an empty prompt).
|
||||
let empty_err = parse_args(&["".to_string()])
|
||||
.expect_err("empty positional arg should be rejected");
|
||||
let empty_err =
|
||||
parse_args(&["".to_string()]).expect_err("empty positional arg should be rejected");
|
||||
assert!(
|
||||
empty_err.starts_with("empty prompt:"),
|
||||
"empty-arg error should be specific, got: {empty_err}"
|
||||
@ -10224,7 +10225,8 @@ mod tests {
|
||||
.expect("write malformed .claw.json");
|
||||
|
||||
let context = with_current_dir(&cwd, || {
|
||||
super::status_context(None).expect("status_context should not hard-fail on config parse errors (#143)")
|
||||
super::status_context(None)
|
||||
.expect("status_context should not hard-fail on config parse errors (#143)")
|
||||
});
|
||||
|
||||
// Phase 1 contract: config_load_error is populated with the parse error.
|
||||
@ -10261,7 +10263,8 @@ mod tests {
|
||||
cumulative: runtime::TokenUsage::default(),
|
||||
estimated_tokens: 0,
|
||||
};
|
||||
let json = super::status_json_value(Some("test-model"), usage, "workspace-write", &context, None);
|
||||
let json =
|
||||
super::status_json_value(Some("test-model"), usage, "workspace-write", &context, None);
|
||||
assert_eq!(
|
||||
json.get("status").and_then(|v| v.as_str()),
|
||||
Some("degraded"),
|
||||
@ -10278,8 +10281,14 @@ mod tests {
|
||||
json.get("model").and_then(|v| v.as_str()),
|
||||
Some("test-model")
|
||||
);
|
||||
assert!(json.get("workspace").is_some(), "workspace field still reported");
|
||||
assert!(json.get("sandbox").is_some(), "sandbox field still reported");
|
||||
assert!(
|
||||
json.get("workspace").is_some(),
|
||||
"workspace field still reported"
|
||||
);
|
||||
assert!(
|
||||
json.get("sandbox").is_some(),
|
||||
"sandbox field still reported"
|
||||
);
|
||||
|
||||
// Clean path: no config error → status: "ok", config_load_error: null.
|
||||
let clean_cwd = root.join("project-with-clean-config");
|
||||
@ -10288,8 +10297,13 @@ mod tests {
|
||||
super::status_context(None).expect("clean status_context should succeed")
|
||||
});
|
||||
assert!(clean_context.config_load_error.is_none());
|
||||
let clean_json =
|
||||
super::status_json_value(Some("test-model"), usage, "workspace-write", &clean_context, None);
|
||||
let clean_json = super::status_json_value(
|
||||
Some("test-model"),
|
||||
usage,
|
||||
"workspace-write",
|
||||
&clean_context,
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
clean_json.get("status").and_then(|v| v.as_str()),
|
||||
Some("ok"),
|
||||
@ -10388,11 +10402,18 @@ mod tests {
|
||||
// Other unrecognized args should NOT trigger the --json hint.
|
||||
let err_other = parse_args(&["doctor".to_string(), "garbage".to_string()])
|
||||
.expect_err("`doctor garbage` should fail without --json hint");
|
||||
assert!(!err_other.contains("--output-format json"),
|
||||
"unrelated args should not trigger --json hint: {err_other}");
|
||||
assert!(
|
||||
!err_other.contains("--output-format json"),
|
||||
"unrelated args should not trigger --json hint: {err_other}"
|
||||
);
|
||||
// #154: model syntax error should hint at provider prefix when applicable
|
||||
let err_gpt = parse_args(&["prompt".to_string(), "test".to_string(), "--model".to_string(), "gpt-4".to_string()])
|
||||
.expect_err("`--model gpt-4` should fail with OpenAI hint");
|
||||
let err_gpt = parse_args(&[
|
||||
"prompt".to_string(),
|
||||
"test".to_string(),
|
||||
"--model".to_string(),
|
||||
"gpt-4".to_string(),
|
||||
])
|
||||
.expect_err("`--model gpt-4` should fail with OpenAI hint");
|
||||
assert!(
|
||||
err_gpt.contains("Did you mean `openai/gpt-4`?"),
|
||||
"GPT model error should hint openai/ prefix: {err_gpt}"
|
||||
@ -10401,8 +10422,13 @@ mod tests {
|
||||
err_gpt.contains("OPENAI_API_KEY"),
|
||||
"GPT model error should mention env var: {err_gpt}"
|
||||
);
|
||||
let err_qwen = parse_args(&["prompt".to_string(), "test".to_string(), "--model".to_string(), "qwen-plus".to_string()])
|
||||
.expect_err("`--model qwen-plus` should fail with DashScope hint");
|
||||
let err_qwen = parse_args(&[
|
||||
"prompt".to_string(),
|
||||
"test".to_string(),
|
||||
"--model".to_string(),
|
||||
"qwen-plus".to_string(),
|
||||
])
|
||||
.expect_err("`--model qwen-plus` should fail with DashScope hint");
|
||||
assert!(
|
||||
err_qwen.contains("Did you mean `qwen/qwen-plus`?"),
|
||||
"Qwen model error should hint qwen/ prefix: {err_qwen}"
|
||||
@ -10412,8 +10438,13 @@ mod tests {
|
||||
"Qwen model error should mention env var: {err_qwen}"
|
||||
);
|
||||
// Unrelated invalid model should NOT get a hint
|
||||
let err_garbage = parse_args(&["prompt".to_string(), "test".to_string(), "--model".to_string(), "asdfgh".to_string()])
|
||||
.expect_err("`--model asdfgh` should fail");
|
||||
let err_garbage = parse_args(&[
|
||||
"prompt".to_string(),
|
||||
"test".to_string(),
|
||||
"--model".to_string(),
|
||||
"asdfgh".to_string(),
|
||||
])
|
||||
.expect_err("`--model asdfgh` should fail");
|
||||
assert!(
|
||||
!err_garbage.contains("Did you mean"),
|
||||
"Unrelated model errors should not get a hint: {err_garbage}"
|
||||
@ -10423,15 +10454,42 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_error_kind_returns_correct_discriminants() {
|
||||
// #77: error kind classification for JSON error payloads
|
||||
assert_eq!(classify_error_kind("missing Anthropic credentials; export ..."), "missing_credentials");
|
||||
assert_eq!(classify_error_kind("no worker state file found at /tmp/..."), "missing_worker_state");
|
||||
assert_eq!(classify_error_kind("session not found: abc123"), "session_not_found");
|
||||
assert_eq!(classify_error_kind("failed to restore session: no managed sessions found"), "session_load_failed");
|
||||
assert_eq!(classify_error_kind("unrecognized argument `--foo` for subcommand `doctor`"), "cli_parse");
|
||||
assert_eq!(classify_error_kind("invalid model syntax: 'gpt-4'. Expected ..."), "invalid_model_syntax");
|
||||
assert_eq!(classify_error_kind("unsupported resumed command: /blargh"), "unsupported_resumed_command");
|
||||
assert_eq!(classify_error_kind("api failed after 3 attempts: ..."), "api_http_error");
|
||||
assert_eq!(classify_error_kind("something completely unknown"), "unknown");
|
||||
assert_eq!(
|
||||
classify_error_kind("missing Anthropic credentials; export ..."),
|
||||
"missing_credentials"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("no worker state file found at /tmp/..."),
|
||||
"missing_worker_state"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("session not found: abc123"),
|
||||
"session_not_found"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("failed to restore session: no managed sessions found"),
|
||||
"session_load_failed"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("unrecognized argument `--foo` for subcommand `doctor`"),
|
||||
"cli_parse"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("invalid model syntax: 'gpt-4'. Expected ..."),
|
||||
"invalid_model_syntax"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("unsupported resumed command: /blargh"),
|
||||
"unsupported_resumed_command"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("api failed after 3 attempts: ..."),
|
||||
"api_http_error"
|
||||
);
|
||||
assert_eq!(
|
||||
classify_error_kind("something completely unknown"),
|
||||
"unknown"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -10902,7 +10960,6 @@ mod tests {
|
||||
assert!(report.contains("Use /help"));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn typoed_doctor_subcommand_returns_did_you_mean_error() {
|
||||
let error = parse_args(&["doctorr".to_string()]).expect_err("doctorr should error");
|
||||
@ -10985,7 +11042,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn punctuation_bearing_single_token_still_dispatches_to_prompt() {
|
||||
// #140: Guard against test pollution — isolate cwd + env so this test
|
||||
|
||||
@ -172,7 +172,10 @@ stderr:
|
||||
);
|
||||
let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8");
|
||||
let parsed: Value = serde_json::from_str(&stdout).expect("compact json stdout should parse");
|
||||
assert_eq!(parsed["message"], "Mock streaming says hello from the parity harness.");
|
||||
assert_eq!(
|
||||
parsed["message"],
|
||||
"Mock streaming says hello from the parity harness."
|
||||
);
|
||||
assert_eq!(parsed["compact"], true);
|
||||
assert_eq!(parsed["model"], "claude-sonnet-4-6");
|
||||
assert!(parsed["usage"].is_object());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user