From db80c9b96e0831476282a076b48ddf5269c050f6 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 26 May 2026 13:05:44 +0900 Subject: [PATCH] fix(#733): diff JSON adds changed_file_count; run git diff --name-only for staged+unstaged and deduplicate into BTreeSet --- ROADMAP.md | 2 ++ rust/crates/rusty-claude-cli/src/main.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/ROADMAP.md b/ROADMAP.md index acf3ab77..8ac7c6f7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7631,3 +7631,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed) 731. **`claw sandbox --output-format json` returned `status:"error"` when namespace isolation is unsupported on macOS but filesystem sandbox is active — automation treating `status != "ok"` as a hard error would block on a fully-functional degraded sandbox** — dogfooded 2026-05-26 on `425d94ee`. `sandbox_json_value` derived `status:"error"` when `!status.supported` regardless of whether `filesystem_active:true` (workspace-write containment working). On macOS the typical state is `{supported:false, filesystem_active:true, active_namespace:false}` — namespace isolation is unsupported but the filesystem sandbox IS active. This is degradation, not failure. Fix: added `else if status.filesystem_active { "warn" }` branch before the hard `"error"` arm — `status:"error"` is now reserved for the case where sandbox is enabled, unsupported, AND no filesystem containment is active either. macOS default now correctly returns `status:"warn"`. Source: Jobdori dogfood on `425d94ee`, 2026-05-26. 732. **`claw status --output-format json` `allowed_tools.entries` was `null` when no `--allowed-tools` flag was passed — callers doing `.allowed_tools.entries | length > 0` or trying to iterate got a null-dereference instead of an empty array** — dogfooded 2026-05-26 on `29dcd478`. `allowed_tool_entries` was computed as `allowed_tools.map(|tools| tools.iter().cloned().collect())` — `None` when unrestricted, serialized to JSON `null`. Fix: `.unwrap_or_default()` so unrestricted invocations emit `entries: []` instead of `entries: null`. Callers can now use `.entries | length > 0` uniformly without a null guard. Source: Jobdori dogfood on `29dcd478`, 2026-05-26. + +733. **`claw diff --output-format json` returned no `changed_file_count` field — callers seeing `result:"changes"` had to parse the raw `staged`/`unstaged` diff text to count affected files** — dogfooded 2026-05-26 on `4c16a42f`. `render_diff_json_for` ran `git diff --cached` and `git diff` and exposed them as raw strings but didn't compute a file count. Fix: run two additional `git diff --name-only` passes (staged + unstaged), deduplicate across both sets using a `BTreeSet`, and expose `changed_file_count: usize` in the envelope. Clean repos emit `changed_file_count: 0`, dirty repos emit the true unique-file count. Source: Jobdori dogfood on `4c16a42f`, 2026-05-26. diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 318815ca..454e8988 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -7973,12 +7973,25 @@ fn render_diff_json_for(cwd: &Path) -> Result = std::collections::BTreeSet::new(); + for line in staged_files.lines().chain(unstaged_files.lines()) { + let t = line.trim(); + if !t.is_empty() { + changed.insert(t); + } + } + let changed_file_count = changed.len(); Ok(serde_json::json!({ "kind": "diff", "action": "diff", "status": "ok", "working_directory": cwd.display().to_string(), "result": if staged.trim().is_empty() && unstaged.trim().is_empty() { "clean" } else { "changes" }, + "changed_file_count": changed_file_count, "staged": staged.trim(), "unstaged": unstaged.trim(), }))