Skip to content

Commit 22b423b

Browse files
committed
fix(#786): dump-manifests --manifests-dir missing-value errors now return typed missing_flag_value kind + non-null hint
1 parent 87f4334 commit 22b423b

3 files changed

Lines changed: 80 additions & 2 deletions

File tree

ROADMAP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7737,3 +7737,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
77377737
784. **`claw export` had two opaque arg-error paths returning `error_kind:"unknown"` + `hint:null`** — dogfooded 2026-05-27 on `81fe0ccb` (pinpoint by Gaebal-gajae). `claw export --output` (missing flag value) emitted plain `"missing value for --output"` with no typed prefix; `claw export a.md b.md` (extra positional) emitted plain `"unexpected export argument: second.md"`. Both classified as `unknown+null`. Fix: (1) `--output` missing-value error now uses `missing_flag_value:` prefix + `\n` usage hint; (2) extra positional now uses `unexpected_extra_args:` prefix + `\n` usage hint; (3) classifier `unexpected_extra_args` arm extended to match both `starts_with("unexpected extra arguments")` (prose form, #766) and `starts_with("unexpected_extra_args:")` (typed prefix form, #784). Integration test `export_arg_errors_have_typed_kind_and_hint_784` covers both paths. 43 CLI contract tests pass. [SCOPE: claw-code] Source: Gaebal-gajae pinpoint + Jobdori implementation on `81fe0ccb`, 2026-05-27.
77387738

77397739
785. **`claw dump` (typo/near-miss for dump-manifests) returned `error_kind:"unknown"` — no classifier arm for `"unknown subcommand:"` prose prefix** — dogfooded 2026-05-27 on `e628b4bb`. Any unknown top-level subcommand that triggers the suggestion path emitted `"unknown subcommand: <x>.\nDid you mean <y>"` but `classify_error_kind` had no arm for that prefix; all fell to the `"unknown"` catch-all. The hint was non-null (the suggestion text was extracted by `split_error_hint`) but `error_kind` was undifferentiated. Fix: added `starts_with("unknown subcommand:")` → `"unknown_subcommand"` arm. Unit test assertion + integration test `unknown_subcommand_returns_typed_kind_785` using `claw dump` as the trigger. 44 CLI contract tests pass. [SCOPE: claw-code] Source: Jobdori subcommand-classifier probe on `e628b4bb`, 2026-05-27.
7740+
7741+
786. **`claw dump-manifests --manifests-dir` (missing value) and `--manifests-dir=` (empty) both returned `error_kind:"unknown"` + `hint:null`** — dogfooded 2026-05-27 on `87f43347` (pinpoint by Gaebal-gajae). Both missing `--manifests-dir` branches in `parse_dump_manifests_args` emitted plain `"--manifests-dir requires a path"` with no typed prefix; `classify_error_kind` had no matching arm so they fell to `"unknown"`. Fix: both branches now use `missing_flag_value:` prefix + `\n` usage hint. Integration test `dump_manifests_missing_dir_has_typed_kind_and_hint_786` covers both cases. 45 CLI contract tests pass. [SCOPE: claw-code] Source: Gaebal-gajae pinpoint + Jobdori implementation on `87f43347`, 2026-05-27.

rust/crates/rusty-claude-cli/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,14 +2159,15 @@ fn parse_dump_manifests_args(
21592159
if arg == "--manifests-dir" {
21602160
let value = args
21612161
.get(index + 1)
2162-
.ok_or_else(|| String::from("--manifests-dir requires a path"))?;
2162+
.ok_or_else(|| String::from("missing_flag_value: --manifests-dir requires a path.\nUsage: claw dump-manifests --manifests-dir <path> [--output-format json]"))?;
21632163
manifests_dir = Some(PathBuf::from(value));
21642164
index += 2;
21652165
continue;
21662166
}
21672167
if let Some(value) = arg.strip_prefix("--manifests-dir=") {
21682168
if value.is_empty() {
2169-
return Err(String::from("--manifests-dir requires a path"));
2169+
// #786: empty --manifests-dir= is also a missing value
2170+
return Err(String::from("missing_flag_value: --manifests-dir requires a path.\nUsage: claw dump-manifests --manifests-dir <path> [--output-format json]"));
21702171
}
21712172
manifests_dir = Some(PathBuf::from(value));
21722173
index += 1;

rust/crates/rusty-claude-cli/tests/output_format_contract.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2537,3 +2537,78 @@ fn unknown_subcommand_returns_typed_kind_785() {
25372537
"hint should reference the suggested subcommand or help, got: {hint:?}"
25382538
);
25392539
}
2540+
2541+
#[test]
2542+
fn dump_manifests_missing_dir_has_typed_kind_and_hint_786() {
2543+
// #786: `claw dump-manifests --manifests-dir` (no value) and `--manifests-dir=` (empty)
2544+
// both emitted plain "--manifests-dir requires a path" with error_kind:"unknown" + hint:null.
2545+
// Fix: use missing_flag_value: prefix + \n usage hint.
2546+
let root = unique_temp_dir("dump-manifests-missing-dir-786");
2547+
fs::create_dir_all(&root).expect("temp dir");
2548+
std::process::Command::new("git")
2549+
.args(["init", "-q"])
2550+
.current_dir(&root)
2551+
.output()
2552+
.ok();
2553+
2554+
// Case 1: --manifests-dir with no following value (next arg is --output-format)
2555+
let out1 = run_claw(
2556+
&root,
2557+
&[
2558+
"--output-format",
2559+
"json",
2560+
"dump-manifests",
2561+
"--manifests-dir",
2562+
"--output-format",
2563+
"json",
2564+
],
2565+
&[],
2566+
);
2567+
assert!(!out1.status.success());
2568+
let stderr1 = String::from_utf8_lossy(&out1.stderr);
2569+
let j1: serde_json::Value = stderr1
2570+
.lines()
2571+
.find(|l| l.trim_start().starts_with('{'))
2572+
.and_then(|l| serde_json::from_str(l).ok())
2573+
.expect("missing --manifests-dir value should emit JSON error");
2574+
assert_eq!(
2575+
j1["error_kind"], "missing_flag_value",
2576+
"missing --manifests-dir value should be missing_flag_value, got {:?}",
2577+
j1["error_kind"]
2578+
);
2579+
let h1 = j1["hint"]
2580+
.as_str()
2581+
.expect("missing_flag_value must have hint (#786)");
2582+
assert!(
2583+
h1.contains("dump-manifests") || h1.contains("manifests-dir"),
2584+
"hint should reference dump-manifests usage, got: {h1:?}"
2585+
);
2586+
2587+
// Case 2: --manifests-dir= with empty value
2588+
let out2 = run_claw(
2589+
&root,
2590+
&[
2591+
"--output-format",
2592+
"json",
2593+
"dump-manifests",
2594+
"--manifests-dir=",
2595+
],
2596+
&[],
2597+
);
2598+
assert!(!out2.status.success());
2599+
let stderr2 = String::from_utf8_lossy(&out2.stderr);
2600+
let j2: serde_json::Value = stderr2
2601+
.lines()
2602+
.find(|l| l.trim_start().starts_with('{'))
2603+
.and_then(|l| serde_json::from_str(l).ok())
2604+
.expect("empty --manifests-dir= should emit JSON error");
2605+
assert_eq!(
2606+
j2["error_kind"], "missing_flag_value",
2607+
"empty --manifests-dir= should be missing_flag_value, got {:?}",
2608+
j2["error_kind"]
2609+
);
2610+
let h2 = j2["hint"]
2611+
.as_str()
2612+
.expect("missing_flag_value must have hint (#786)");
2613+
assert!(!h2.is_empty(), "hint must not be empty");
2614+
}

0 commit comments

Comments
 (0)