Commit Graph

552 Commits

Author SHA1 Message Date
YeonGyu-Kim 2f9429cbf0 fix: slash-command guard errors now emit error_kind:interactive_only instead of unknown; covers memory, permissions, review, and any bare_slash_command_guidance path 2026-05-25 22:02:30 +09:00
Yeachan-Heo 4daefc7bd5 Stabilize allowedTools rejection contract in CI
Serialize the allowedTools rejection tests with the existing environment/current-directory test guards so full parallel cargo test runs cannot observe a transient project config or cwd from another test while building the tool registry.\n\nConstraint: Post-merge Rust build workflow run 26399647443 failed only in the full cargo test job while the focused test passed locally.\nRejected: Changing allowedTools parser output | the product contract remains correct and focused reproduction preserves the expected unsupported-tool error.\nConfidence: high\nScope-risk: narrow\nDirective: Keep this as test isolation only; do not bundle inventory provenance or ROADMAP work into this follow-up.\nTested: cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli tests::rejects_unknown_allowed_tools -- --exact --nocapture; cargo test --verbose -p rusty-claude-cli --bin claw; cargo test --verbose; cargo fmt --check; cargo check --workspace --locked; cargo build --workspace --locked\nNot-tested: GitHub Actions rerun before opening PR
2026-05-25 12:55:53 +00:00
YeonGyu-Kim 5bca9ef039
Merge pull request #3103 from Yeachan-Heo/fix/issue-702-inventory-provenance
Fix #702 inventory provenance schema
2026-05-25 21:10:18 +09:00
YeonGyu-Kim b8eca2a68e fix(#349): plugins unknown action emits status:error + error_kind:unknown_plugins_action + exit 1 instead of status:ok with prose 2026-05-25 21:08:14 +09:00
Yeachan-Heo 566992c331 Unify inventory provenance for generic parsers
Expose a stable source object on agent and skill inventory entries with the same id/label/detail_label keys while preserving skill origin for compatibility.\n\nConstraint: ROADMAP #702 scope only; keep existing skills origin field for compatibility.\nRejected: Rename agent source to origin | would break existing agents consumers and still require per-resource branching during migration.\nConfidence: high\nScope-risk: narrow\nDirective: Future inventory resources should expose provenance through source.id, source.label, and source.detail_label.\nTested: cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo test --manifest-path rust/Cargo.toml -p commands renders_skills_reports_as_json -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract -- --nocapture; cargo build --manifest-path rust/Cargo.toml --workspace --locked; git diff --check\nNot-tested: full cargo test --manifest-path rust/Cargo.toml --workspace
2026-05-25 12:05:50 +00:00
YeonGyu-Kim 36b36267ec fix(#458): add status:ok to config JSON envelope; unknown section now emits status:error + error_kind:unsupported_config_section 2026-05-25 20:33:36 +09:00
YeonGyu-Kim ee24ff2d83
Merge pull request #3102 from Yeachan-Heo/fix/issue-696-compact-nontty
Fix compact non-TTY hang
2026-05-25 19:41:27 +09:00
Yeachan-Heo 9e6f753640 Fail closed for compact without an interactive session
Route bare compact invocations to a typed interactive-only error before prompt/provider startup so non-TTY JSON probes terminate predictably. Keep the existing resume-session /compact path as the supported automation surface.\n\nConstraint: ROADMAP pinpoint #696 requires no spinner/hang for closed stdin and --output-format json.\nRejected: Broad help-schema rewrites | #699/#700/#701 are outside this PR scope.\nConfidence: high\nScope-risk: narrow\nDirective: Do not route bare slash-command names through the prompt fallback when they require a session.\nTested: cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test compact_output compact_subcommand_ -- --nocapture; cargo build --manifest-path rust/Cargo.toml --workspace; cargo build --manifest-path rust/Cargo.toml --workspace --locked; timeout probes for compact JSON/text with stdin closed.\nNot-tested: Full workspace test suite.
2026-05-25 10:37:12 +00:00
YeonGyu-Kim de2e32c5d4 fix: skills install nonexistent path emits skill_not_found error kind with descriptive message; classify_error_kind adds skill_not_found branch 2026-05-25 19:34:25 +09:00
YeonGyu-Kim 9d1998b3fd test(#458/#700/#701/#702): add status:ok assertions for help/bootstrap-plan/export-help contracts; add diff/export JSON shape tests 2026-05-25 19:07:03 +09:00
YeonGyu-Kim 181b12f0a9 fix: mcp show <nonexistent> now returns status:error + error_kind:server_not_found + exit 1; extend ok:false gate to also check status:error 2026-05-25 18:34:43 +09:00
YeonGyu-Kim 47521cf178 fix(#701): add detail_entries structured key/value to doctor check JSON; booleans/ints emitted as JSON scalars 2026-05-25 18:02:03 +09:00
YeonGyu-Kim f9e98a2634 fix(#700): add status:ok to all help JSON envelopes; rename session_list kind to sessions with action:list 2026-05-25 17:05:28 +09:00
YeonGyu-Kim eb7c14c4ae fix(#458): add status:ok to bootstrap-plan JSON envelope; all 12 JSON surfaces now have uniform status field 2026-05-25 16:34:33 +09:00
YeonGyu-Kim 11a6e081a2 fix(#458): add status field to export and diff JSON envelopes 2026-05-25 16:07:16 +09:00
YeonGyu-Kim f2a90228fb fix: doctor boot preflight detail shows Some(false) for trust_gate_allowed; use Display instead of Debug 2026-05-25 15:21:15 +09:00
YeonGyu-Kim 5b79413e87 fix(#458): add status field to version/init/system-prompt JSON envelopes; all 9 subcommands now have uniform status field 2026-05-25 14:36:12 +09:00
YeonGyu-Kim 85e736c73f fix: add status field to sandbox JSON envelope (ok/warn/error derived from enabled+active+supported) 2026-05-25 14:34:00 +09:00
YeonGyu-Kim 91a0681ae9 fix(#697): agents unknown subcommand exits 1 with typed error; plugins remove aliases uninstall and errors on not-found 2026-05-25 13:39:23 +09:00
YeonGyu-Kim 63a5a87471 fix(#696): exit with typed error when stdin is not a TTY and no prompt piped; fix anthropic/ prefix detection in metadata_for_model 2026-05-25 13:16:12 +09:00
YeonGyu-Kim e7d5d08892 fix: ChunkDelta thinking field in test initializers; fix parse_local_help_action ? operator 2026-05-25 12:49:37 +09:00
YeonGyu-Kim a76dda2b19 chore: cargo fmt --all on fix-683 branch 2026-05-25 12:49:37 +09:00
YeonGyu-Kim 3d02baf567 fix(#683): claw skills remove/add/uninstall/delete emits typed error, exit 1
- Add unsupported skills action guard in parse_args for remove/add/uninstall/delete
- Add unsupported_skills_action to classify_error_kind for structured JSON errors
- Fix pre-existing compile errors (stale retry_after field, missing Team variant)
- Add regression test unsupported_skills_actions_return_typed_error_683
2026-05-25 12:49:37 +09:00
YeonGyu-Kim 1f572ff8de fix: add missing config_load_error_kind to test StatusContext initializers; remove stale retry_after refs again 2026-05-25 12:15:57 +09:00
YeonGyu-Kim 03bd461984 fix: ChunkDelta thinking field in tests, remove residual retry_after refs, fix parse_local_help_action return type 2026-05-25 12:09:29 +09:00
YeonGyu-Kim 495e7a015c fix: remove stale retry_after field, Team variant, config_load_error_kind, denied_tools initializer errors
- Remove retry_after: None from ApiError::Api structs in openai_compat.rs (field was removed)
- Remove SlashCommand::Team parse arm (variant was removed from enum)
- Add config_load_error_kind: None to doctor path StatusContext initializer
- Add Thinking arm to all ContentBlock match blocks in trident.rs
- Remove cargo fmt drift across commands, config, compact, tools, trident
2026-05-25 12:01:09 +09:00
YeonGyu-Kim 3364dc4bee chore: fix conflict markers and cargo fmt drift in main (commands, openai_compat, trident, config, tools) 2026-05-25 11:51:44 +09:00
TheArchitectit b43a6f2d29
feat: auto-compact and retry on context window errors
Adds automatic compaction and retry when context window is exceeded.
2026-05-25 11:24:44 +09:00
TheArchitectit f1a55a211e
fix: /resume latest searches all workspaces
Fixes /resume latest to search all workspaces instead of just the current one.
2026-05-25 11:24:41 +09:00
TheArchitectit cef45efc16
feat: interactive provider wizard with fast model selection
Adds interactive provider setup wizard.
2026-05-25 11:24:20 +09:00
Psy-lzh fdcb05b2c4
fix: echo reasoning_content back for DeepSeek V4 multi-turn tool calls
Threads reasoning_content back into Thinking blocks for DeepSeek V4 multi-turn calls. Adds pending_thinking accumulator to capture thinking/signature delta events during streaming, and converts ContentBlock::Thinking to InputContentBlock::Thinking in convert_messages to preserve reasoning between turns, fixing the 'reasoning_content must be passed back' error.
2026-05-25 11:21:33 +09:00
Nils fc26e16ce2
fix: resolve model aliases before syntax validation
Fixes alias resolution ordering: aliases (opus/sonnet/haiku) are now resolved to their full provider/model form BEFORE syntax validation. Previously, aliases bypassed validation via an early-return check. Also adds the 'log' crate for debug tracing of alias resolution and wraps PermissionsExt import in #[cfg(unix)] for portability.
2026-05-25 11:21:32 +09:00
Emre Kerem Celenli f72681f998
fix: recognize OPENAI_API_KEY as valid auth for OpenAI-compatible endpoints
Adds OPENAI_API_KEY detection to check_auth_health() alongside existing api_key and auth_token checks, creating a combined any_auth_present variable. Also displays openai_key presence in the environment details.
2026-05-25 11:21:14 +09:00
bellman 04c2abb412 Stabilize final gate before release checkpoint
Resolve the G012 evidence gate by fixing permission-mode regressions, platform-sensitive tests, and the clippy surface that blocked an all-targets verification run.

Constraint: G012 final gate required docs, board, full workspace tests, and clippy -D warnings evidence before checkpointing.

Rejected: documenting the worker-2 gate failure as an accepted gap | the failing tests and lints were locally reproducible and fixable.

Confidence: high

Scope-risk: moderate

Directive: Preserve read-only permission requirements for read/glob/grep tools; write/edit remain workspace-write or danger-full-access when outside the workspace.

Tested: python3 .github/scripts/check_doc_source_of_truth.py; python3 .github/scripts/check_release_readiness.py; python3 scripts/validate_cc2_board.py --board .omx/cc2/board.json; python3 .omx/cc2/validate_issue_parity_intake.py .omx/cc2/issue-parity-intake.json; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml --workspace; cargo test --manifest-path rust/Cargo.toml --workspace -- --nocapture; cargo clippy --manifest-path rust/Cargo.toml --workspace --all-targets -- -D warnings

Not-tested: live network provider smoke tests and remote PR/issue mutations.
2026-05-15 13:34:57 +09:00
bellman 4cd2bb859b omx(team): auto-checkpoint worker-1 [1] 2026-05-15 11:49:01 +09:00
bellman 62bc7b6a17 Stabilize G011 integrated evidence
Keep the integrated G011 worker outputs buildable and whitespace-clean before final stream verification.

Constraint: worker auto-checkpoints left a test-scope import gap and committed trailing whitespace.

Rejected: waiting for another worker auto-merge | leader verification already identified the narrow repair.

Confidence: high

Scope-risk: narrow

Directive: preserve the ACP unsupported JSON contract and anti-slop template cleanliness when editing G011 artifacts.

Tested: git diff --check; python3 .github/scripts/check_doc_source_of_truth.py; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml -p rusty-claude-cli; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli parses_acp_command_surfaces; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli acp_status_json_is_truthful_unsupported_contract

Not-tested: full workspace tests pending G011 final gate.
2026-05-15 11:47:28 +09:00
bellman 7b63c0a2eb omx(team): auto-checkpoint worker-1 [1] 2026-05-15 11:40:54 +09:00
bellman 21bbbb7f1f Route resumed session commands exhaustively
Keep G010 resumed session UX compilable after worker integrations by routing every /session action through the shared resumed-session command handler.\n\nConstraint: Rust exhaustive matching rejected partial /session arms after task integrations introduced action-without-target cases.\nRejected: adding ad hoc match arms per action | the shared handler already owns list, exists, delete, and unsupported action behavior.\nConfidence: high\nScope-risk: narrow\nDirective: Preserve run_resumed_session_command as the single resumed /session dispatch point.\nTested: cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --bin claw session_exists_resume_command_reports_json_contract -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --bin claw resumed_session_exists_and_delete_have_json_contracts -- --nocapture; cargo check --manifest-path rust/Cargo.toml --workspace; git diff --check\nNot-tested: full cargo test --workspace not run.
2026-05-15 11:19:14 +09:00
bellman eb7a2088e2 omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 11:16:58 +09:00
bellman 11c6a6007f omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 11:16:58 +09:00
bellman 4ccbd8f97c Keep resumed session handling exhaustive
The resumed session command matcher now owns every /session variant in one arm so adding supported actions does not leave a redundant fallback that triggers unreachable-pattern warnings.\n\nConstraint: G010 session hygiene verification requires clean focused tests around resume/session recovery UX.\nRejected: Leave the warning in place | it obscures real regressions in the focused recovery checks.\nConfidence: high\nScope-risk: narrow\nDirective: Keep new /session resume-mode actions inside run_resumed_session_command rather than duplicating match arms.\nTested: cargo test -p rusty-claude-cli context_window_preflight_errors_render_recovery_steps\nNot-tested: full workspace test suite

Co-authored-by: OmX <omx@local>
2026-05-15 11:16:11 +09:00
bellman 2221dd4f0f omx(team): auto-checkpoint worker-1 [1] 2026-05-15 11:16:04 +09:00
bellman c5a18e1864 Preserve resumed session command exhaustiveness
Resolve an overlap introduced while adding G010 session command UX so resumed /session handling remains exhaustive without an unreachable fallback arm.\n\nConstraint: G010 leader verification found rusty-claude-cli failed to compile because SlashCommand::Session was both handled and still listed in the unsupported command union.\nRejected: leaving worker lanes to rediscover the compile failure | leader had direct verification evidence and a one-line localized fix.\nConfidence: high\nScope-risk: narrow\nDirective: Keep resumed /session subcommands routed through run_resumed_session_command; do not re-add Session to the unsupported slash-command union.\nTested: cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test resume_slash_commands -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test compact_output -- --nocapture; git diff --check\nNot-tested: full cargo test --workspace not rerun for this one-line compile fix; G010 leader verification continues separately.
2026-05-15 11:15:53 +09:00
bellman d7f1ad7139 omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 11:12:25 +09:00
bellman d04a74cc97 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 11:12:10 +09:00
bellman 0f8717834f omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 11:09:06 +09:00
bellman fb9095c611 omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 11:09:06 +09:00
bellman 33ac5c30d3 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 11:05:36 +09:00
bellman 3767addd11 omx(team): auto-checkpoint worker-2 [unknown] 2026-05-15 10:43:35 +09:00
bellman 1a110bd870 omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 10:23:46 +09:00
bellman e4ef0f7f19 omx(team): auto-checkpoint worker-4 [unknown] 2026-05-15 10:22:03 +09:00
bellman 7ed1cabc14 Prove observable MCP required optional contracts
Added CLI JSON regression coverage for MCP required versus optional flags, redacted env/header values, degraded malformed config reporting, and failed unsupported usage reporting without touching runtime internals.

Constraint: Task 12 scope preferred rusty-claude-cli tests and avoid worker-1/3 MCP internals.

Rejected: Runtime lifecycle edits | existing observable JSON contracts already expose required, redacted keys, degraded config, and unsupported-action failure semantics.

Confidence: high

Scope-risk: narrow

Directive: Preserve secret-value redaction by exposing env/header keys only; keep degraded config distinct from usage errors.

Tested: cargo fmt --manifest-path Cargo.toml -p rusty-claude-cli --check; cargo test --manifest-path Cargo.toml -p rusty-claude-cli --test output_format_contract mcp_ -- --nocapture; cargo check --manifest-path Cargo.toml -p rusty-claude-cli.

Not-tested: Full output_format_contract currently has unrelated pre-existing failures in plugin/doctor contract tests.
2026-05-15 10:07:47 +09:00
bellman 1f00771fd2 Keep plugin lifecycle JSON complete after team merges
Preserve the direct and resumed plugin JSON arrays that downstream G007 regressions require while retaining the degraded config envelope from the malformed-MCP work.

Constraint: G007 verification requires plugins[] and load_failures[] on plugin JSON plus status/config_load_error consistency.
Rejected: Only relaxing output_format_contract tests | that would hide a shipped CLI JSON regression.
Confidence: high
Scope-risk: narrow
Directive: Keep direct, resumed, and interactive plugin command payloads routed through one payload helper before changing plugin JSON fields.
Tested: cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract inventory_commands_emit_structured_json_when_requested -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract plugins_json_surfaces_lifecycle_contract_when_plugin_is_installed -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract structured_json_when_requested -- --nocapture; cargo check --manifest-path rust/Cargo.toml -p runtime -p tools -p rusty-claude-cli -p commands -p plugins
Not-tested: Full workspace clippy remains blocked by pre-existing LaneContext struct_excessive_bools lint.

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-15 10:06:23 +09:00
bellman 39568feff6 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 10:01:04 +09:00
bellman 686cc89a36 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 10:00:40 +09:00
bellman ace260139e omx(team): auto-checkpoint worker-3 [4] 2026-05-15 09:59:06 +09:00
bellman db6f30fa33 verify plugin lifecycle JSON contract
Lock the plugin inventory JSON contract so lifecycle state and lifecycle summary fields stay visible to orchestrators while allowing bundled plugins to coexist in isolated inventories.

Constraint: G007-plugin-mcp Task 1 requires plugin/MCP lifecycle contract evidence without mutating .omx/ultragoal.

Rejected: Assuming an empty plugin inventory in tests | bundled plugins are auto-synced and should not make lifecycle contract verification brittle.

Confidence: high

Scope-risk: narrow

Directive: Keep plugin inventory JSON machine-readable for lifecycle_state, lifecycle, status, and load_failures; do not collapse it back to message-only JSON.

Tested: cargo test -p plugins plugin_registry_report_collects_load_failures_without_dropping_valid_plugins -- --nocapture; cargo test -p commands renders_plugins_report -- --nocapture; cargo test -p rusty-claude-cli --test output_format_contract plugins_json_surfaces_lifecycle_contract_when_plugin_is_installed -- --nocapture; cargo test -p rusty-claude-cli --test output_format_contract inventory_commands_emit_structured_json_when_requested -- --nocapture; cargo check --workspace; cargo fmt --all -- --check

Not-tested: cargo clippy -p rusty-claude-cli --test output_format_contract -- -D warnings is blocked by pre-existing runtime::policy_engine::LaneContext clippy::struct_excessive_bools.

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-15 09:59:02 +09:00
bellman 983ceb939c omx(team): auto-checkpoint worker-1 [1] 2026-05-15 09:57:03 +09:00
bellman cac73b4410 omx(team): auto-checkpoint worker-3 [4] 2026-05-15 09:57:00 +09:00
bellman 9ae6aa3f30 Keep plugin introspection available when MCP config is malformed
Route plugin command rendering through the same degraded config envelope used by status and MCP, falling back to empty runtime config when config loading fails so local plugin listing remains inspectable.

Constraint: Task 4 requires malformed MCP config consistency across status, doctor, mcp, and plugins surfaces.

Rejected: Hard-failing plugins on ConfigLoader errors | inconsistent with status/mcp degraded-mode contract and hides local plugin diagnostics.

Confidence: high

Scope-risk: narrow

Directive: Keep config_load_error/status fields aligned across local introspection commands when adding new config-dependent surfaces.

Tested: cargo test -p rusty-claude-cli malformed_mcp_config -- --nocapture; cargo test -p commands mcp_degrades_gracefully_on_malformed_mcp_config_144 -- --nocapture; cargo check -p rusty-claude-cli; cargo fmt --all -- --check; claw plugins --output-format json malformed-MCP smoke.

Not-tested: full workspace clippy remains blocked by pre-existing clippy warnings in runtime and rusty-claude-cli unrelated to this change.
2026-05-15 09:56:56 +09:00
bellman 985c6e97f9 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 09:56:50 +09:00
bellman c522dc970f Preserve plugin lifecycle JSON in G007 CLI output
Constraint: G007 worker integrations made plugin command JSON degraded-aware but omitted the structured plugin/load-failure arrays expected by inventory contracts.\nRejected: Drop lifecycle arrays from tests | G007 requires plugin lifecycle state to stay machine-readable across plugin surfaces.\nConfidence: high\nScope-risk: narrow\nDirective: Keep  carrying plugin entries, lifecycle state, and load failures even when config loading degrades.\nTested: cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml -p runtime -p tools -p rusty-claude-cli -p commands -p plugins; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract plugins_json_surfaces_lifecycle_contract_when_plugin_is_installed -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract inventory_commands_emit_structured_json_when_requested -- --nocapture; git diff --check\nNot-tested: full workspace suite\n\nCo-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-15 09:56:46 +09:00
bellman 2454f012b6 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 09:52:29 +09:00
bellman 80b8984b62 omx(team): auto-checkpoint worker-4 [5] 2026-05-15 09:49:36 +09:00
bellman b01192dde7 omx(team): auto-checkpoint worker-3 [4] 2026-05-15 09:49:33 +09:00
bellman 1a6e475f74 omx(team): auto-checkpoint worker-1 [1] 2026-05-15 09:49:22 +09:00
bellman 0cd1eabb5d Keep G007 plugin command integration compiling
Constraint: G007 worker integrations added plugin command surfaces but left the REPL handler referencing a pre-refactor variable.\nRejected: Revert the worker plugin-command surface | the parser/degraded-config changes are part of the G007 scope and only needed a narrow compile repair.\nConfidence: high\nScope-risk: narrow\nDirective: Keep plugin CLI and REPL command paths routed through plugins_command_payload_for so malformed config can degrade consistently.\nTested: cargo check --manifest-path rust/Cargo.toml -p runtime -p tools -p rusty-claude-cli -p commands -p plugins; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli parse_args_plugins -- --nocapture\nNot-tested: full G007 team suite pending worker completion\n\nCo-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-15 09:49:16 +09:00
bellman f2ba3648d6 omx(team): auto-checkpoint worker-3 [4] 2026-05-15 09:45:57 +09:00
bellman 0a14f8511e omx(team): auto-checkpoint worker-4 [5] 2026-05-15 09:45:33 +09:00
bellman f7235ca932 Make G006 task policy state machine executable
Typed task packets, policy decisions, lane board status, and session liveness now have concrete runtime contracts and focused regressions for Stream 4.

Constraint: G006 requires task/lane operation without pane scraping while preserving legacy task packet callers.
Rejected: waiting on stale worker worktrees | all G006 worker worktrees remained at main with no commits, so leader integrated the verified slice directly.
Confidence: high
Scope-risk: moderate
Directive: Keep task packet serde defaults when adding fields so older packets continue to deserialize.
Tested: git diff --check; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml -p runtime -p tools -p rusty-claude-cli; cargo test --manifest-path rust/Cargo.toml -p runtime task_packet -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p runtime policy_engine -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p runtime task_registry -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p runtime session_heartbeat -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p tools run_task_packet_creates_packet_backed_task -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p tools lane_completion -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli status_json_surfaces -- --nocapture
Not-tested: full workspace test suite; PR/issue reconciliation deferred to G011/G012

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-15 09:29:26 +09:00
bellman 8f7eaffcef Close the G005 verification gaps before checkpoint
Constraint: G005 requires stale-base doctor consistency, green-contract policy integration, hung-test evidence, and a durable verification map before ultragoal checkpointing.\nRejected: Treat worker task status alone as complete | worker-2 lifecycle was stale-failed despite landed recovery evidence, so leader verification and explicit map are required.\nConfidence: medium\nScope-risk: moderate\nDirective: Keep PR/issue reconciliation deferred to G011/G012; do not mutate .omx/ultragoal outside checkpoint commands.\nTested: git diff --check; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml -p rusty-claude-cli; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli workspace_health_warns_when_stale_base_diverged -- --nocapture; cargo check --manifest-path rust/Cargo.toml -p tools\nNot-tested: full workspace test suite due known unrelated permission/lifecycle failures from worker evidence.\n\nCo-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-14 18:38:22 +09:00
bellman aec291caab omx(team): auto-checkpoint worker-4 [unknown] 2026-05-14 17:51:53 +09:00
bellman 43b182882a Lock doctor JSON boot preflight contract
Constraint: G003 boot/session work adds a structured doctor boot-preflight check that must be visible in JSON output.
Rejected: reducing the doctor check count back to six | boot preflight is an explicit G003 acceptance surface.
Confidence: high
Scope-risk: narrow
Directive: Keep doctor/status JSON contract tests aligned with boot_preflight schema fields when extending preflight diagnostics.
Tested: git diff --check; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo test --manifest-path rust/Cargo.toml -p runtime trusted_roots -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p runtime startup -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p runtime worker_boot -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p tools path_scope -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli --test output_format_contract -- --nocapture; cargo check --manifest-path rust/Cargo.toml --workspace
Not-tested: full cargo test --workspace remains deferred during active G003 team reconciliation.

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-14 17:51:47 +09:00
bellman 307b23d27f omx(team): auto-checkpoint worker-4 [unknown] 2026-05-14 17:50:36 +09:00
bellman 79d3b809f9 omx(team): auto-checkpoint worker-4 [unknown] 2026-05-14 17:46:16 +09:00
bellman 9ab569e626 omx(team): auto-checkpoint worker-2 [3] 2026-05-14 17:18:55 +09:00
YeonGyu-Kim 75c08bc982 fix: REPL display, /compact panic, identity leak, DeepSeek reasoning, thinking blocks
Five interrelated fixes from parallel Hephaestus sessions:

1. fix(repl): display assistant text after spinner (#2981, #2982, #2937)
   - Added final_assistant_text() call after run_turn spinner completes
   - REPL now shows response text like run_prompt_json does

2. fix(compact): handle Thinking content blocks (#2985)
   - Added ContentBlock::Thinking variant throughout compact summarizer
   - Prevents panic when /compact encounters thinking blocks

3. fix(prompt): provider-aware model identity (#2822)
   - New ModelFamilyIdentity enum (Claude vs Generic)
   - Non-Anthropic models no longer say 'I am Claude'
   - model_family_identity_for() detects provider and sets identity

4. fix(openai): preserve DeepSeek reasoning_content (#2821)
   - Stream parser now captures reasoning_content from OpenAI-compat
   - Emits ThinkingDelta/SignatureDelta events for reasoning models
   - Thinking blocks included in conversation history for re-send

5. feat(runtime): Thinking block support across codebase
   - AssistantEvent::Thinking variant in conversation.rs
   - ContentBlock::Thinking in session serialization
   - Thinking-aware compact summarization
   - Tests for thinking block ordering and content

Closes #2981, #2982, #2937, #2985, #2822, #2821
2026-05-06 15:32:34 +09:00
YeonGyu-Kim 28998422e2
Merge pull request #2984 from andhai/pr/openai-token-limit-hardening
openai: harden token-limit handling and default output-token caps
2026-05-06 14:53:24 +09:00
YeonGyu-Kim b4733b67a6
Merge pull request #2949 from Yeachan-Heo/fix/export-help-json
Fix export help JSON output
2026-05-06 14:52:09 +09:00
YeonGyu-Kim d074d1c046
fix(mcp): exit 1 when JSON envelope contains ok:false (#2995)
* fix(mcp): exit 1 when JSON envelope contains ok:false

mcp info, mcp describe, and mcp list-filter all return
{"action":"error","ok":false,...} but previously exited 0,
requiring automation callers to inspect the envelope field.

After this fix: print_mcp detects ok:false in the rendered JSON
value and calls process::exit(1) after printing, so the exit code
reflects the semantic error in the envelope.

Unaffected: mcp list, mcp show, mcp help all have no ok field and
continue to exit 0 (they are not error paths).

Closes ROADMAP #68 (partial — agents bogus/mcp show nonexistent
found:false remain exit:0 as they use different envelope shapes).

* feat(scripts): add dogfood-build.sh — build from checkout and verify provenance

Builds claw from the current HEAD, then checks that the binary's
git_sha matches git rev-parse --short HEAD. Exits non-zero if the
binary is stale or provenance is opaque (git_sha: null).

Usage:
  CLAW=$(bash scripts/dogfood-build.sh)   # fail-fast if stale
  $CLAW version --output-format json       # provenance confirmed

Addresses ROADMAP #69: dogfooders using a stale installed binary
cannot attribute behavior to specific commits. This script makes
dogfood round zero unambiguous.

Also documents the safe workaround for contributors who have a
stale system-installed binary.
2026-05-05 06:09:11 +09:00
YeonGyu-Kim caeac828b5
fix(permissions): return guidance for multi-word forms instead of falling through to LLM (#2994)
claw permissions list / claw permissions allow <tool> / claw permissions deny <tool>
all fell through to the prompt/LLM path because parse_subcommand had no
arm for "permissions". The single-word bare form was already intercepted
by bare_slash_command_guidance, but any form with rest.len() > 1 bypassed
the single-word guard and landed in the _other => CliAction::Prompt branch.

Fix: add a "permissions" arm in parse_subcommand that returns a structured
guidance Err so all multi-word forms get the same exit:1 + JSON error as
the bare single-word form, without any LLM call or session creation.

Verified: all invocation forms (bare, list, read-only, workspace-write,
allow/deny <tool>) exit 1 with kind:unknown guidance JSON. Zero sessions.
2026-05-05 05:35:50 +09:00
YeonGyu-Kim 85435ad4b5
fix(plugins): route plugin and marketplace aliases through local handler (#2993)
claw plugin list / claw marketplace / claw marketplace list all fell
through to the prompt/LLM path because parse_subcommand only matched
"plugins" (the primary name) while the canonical spec aliases
"plugin" and "marketplace" were unhandled.

This manifested as auth errors and session creation on direct
invocation — dogfood confirmed Gaebal's binary created one session
via plugin prompt fallback.

Fix: extend the plugins arm in parse_subcommand to also match
"plugin" | "marketplace" so all three forms route to the same
CliAction::Plugins without network calls or session creation.

Verified: all six forms (bare + list subcommand for each name) return
kind:plugin JSON, exit 0, and create zero sessions.

Closes ROADMAP #55 partial (plugins/marketplace bypass complete).
2026-05-05 05:16:00 +09:00
YeonGyu-Kim 65aa559733
fix: support /plugins slash command in resume mode (#2973)
* fix: support /plugins slash command in resume mode

Move SlashCommand::Plugins out of the 'unsupported resumed slash
command' catch-all and add a handler arm in run_resume_command that
calls handle_plugins_slash_command for list/help actions.

Mutation actions (install/uninstall/enable/disable) are rejected with
a clear error since there is no runtime to reload in resume mode.

Add /plugins coverage to resumed_inventory_commands test in
output_format_contract.rs: kind, action, reload_runtime, target.

Before: claw --resume session.jsonl /plugins --output-format json
-> {error: 'unsupported resumed slash command', type: 'error'}, exit 1

After: claw --resume session.jsonl /plugins --output-format json
-> {kind: 'plugin', action: 'list', ...}, exit 0

* style: cargo fmt line wrap in run_resume_command plugins handler

* fix: block /plugins update in resume mode, fix comment

Address REQUEST_CHANGES from OMX review:
1. Add 'update' to the blocked mutation actions in resume mode
   (previously only install/uninstall/enable/disable were blocked)
2. Fix comment: 'Only list is supported' instead of 'Only list/help'
   since /plugins help doesn't actually parse as a valid action

* style: cargo fmt after conflict resolution
2026-05-05 04:55:39 +09:00
YeonGyu-Kim ac8a24b30b
fix(config): emit section and section_value in JSON output for config subcommands (#2990)
`claw config model --output-format json` and all other section subcommands
(`env`, `hooks`, `plugins`) returned identical output with no section field
— the section arg was parsed but discarded (_section parameter).

Fix: render_config_json now:
- Passes section through to handler
- Looks up the section value via runtime_config.get(), converting the
  internal JsonValue to serde_json::Value via render()+parse
- Emits `section` (string) and `section_value` (JSON value or null)
  in the response envelope
- Returns ok:false + error for unsupported section tokens

Test: config_section_json_emits_section_and_value asserts:
- No section field when no section arg
- section + section_value fields present for all known sections
- ok:false + error for unknown section

Pinpoint: ROADMAP #126
2026-05-05 04:50:33 +09:00
YeonGyu-Kim 9b97c4d832
fix(tests): isolate CLAW_CONFIG_HOME in resumed_status JSON test (#2992)
resumed_status_command_emits_structured_json_when_requested was reading
the real ~/.claw/settings.json, causing loaded_config_files to be 1
instead of the expected 0 on machines with user config present.

Root cause: unlike other tests (e.g. resumed_config_command_loads_settings_files),
this test did not pass an isolated CLAW_CONFIG_HOME env var to run_claw,
so claw fell back to the real HOME and loaded the developer's settings file.

Fix: create a temp config-home dir and pass it as CLAW_CONFIG_HOME via
run_claw_with_env. This gives the assertion a clean 0-file baseline.

Unblocks PRs #2973, #2988, #2990 which all failed this same test on main.

Ref: ROADMAP #65
2026-05-05 04:49:46 +09:00
YeonGyu-Kim 1206f4131d
fix(resume): emit structured JSON for /agents --output-format json (#2987)
Resumed /agents --output-format json was returning a human-readable
text render wrapped in a JSON envelope field instead of the actual
structured agent list. The run_resume_command handler was calling
handle_agents_slash_command (text) for the json field instead of
handle_agents_slash_command_json.

Fix: use handle_agents_slash_command_json for the json outcome field,
matching the pattern already used by /skills and /plugins.

Test: extended resumed_inventory_commands_emit_structured_json_when_requested
to cover /agents, asserting kind=="agents", action=="list",
agents is an array, and count is a number (not a text render).
2026-05-05 04:20:52 +09:00
YeonGyu-Kim c99330372c
fix(version): add build_date and executable_path to version JSON output
`claw version --output-format json` was missing build_date and
executable_path, making it impossible to identify which binary is
running or correlate it with a specific build/commit.

Fix: version_json_value() now includes:
- build_date: compile-time BUILD_DATE env (already in text output)
- executable_path: std::env::current_exe() at runtime

Test: version_emits_json_when_requested extended to assert both fields
are strings in the JSON envelope.

Pinpoint: ROADMAP #507
2026-05-05 04:20:12 +09:00
Andreas Haida 9a512633a5 Cap OpenAI default output tokens using model metadata 2026-05-03 22:16:12 +02:00
Andreas Haida 6ac13ffdad Handle OpenAI token-limit errors as context-window failures 2026-05-03 22:16:12 +02:00
YeonGyu-Kim 8e45f1850c
test(output_format_contract): add plugins json coverage to inventory_commands test (#2972)
Add four assertions to inventory_commands_emit_structured_json_when_requested:
- kind == "plugin"
- action == "list"
- reload_runtime is boolean
- target is null when no plugin is targeted

Closes the only major --output-format json surface with zero contract
coverage. All other surfaces (agents, mcp, skills, status, sandbox,
doctor, help, version, acp, bootstrap-plan, system-prompt, init, diff,
config) already had test assertions.
2026-05-01 06:03:31 +09:00
Yeachan-Heo 51b9e6b37f Fix export help JSON output 2026-04-30 09:04:11 +00:00
Sigrid Jin (ง'̀-'́)ง oO 1011a83823
Merge pull request #2829 from ultraworkers/fix/issue-320-session-lifecycle-classification
Fix session lifecycle classification for idle tmux shells
2026-04-29 16:11:58 +09:00
Yeachan-Heo 1376d92064 Filter stub commands from resume-safe help
Keep claw --help's resume-safe slash command summary aligned with the interactive command list by filtering STUB_COMMANDS and adding regression coverage.
2026-04-29 03:31:34 +00:00
Yeachan-Heo be53e04671 Classify saved sessions by live work rather than pane existence
Operator status previously treated any tmux pane in a workspace as equivalent to active work. The new classifier uses tmux pane command/path metadata as a soft signal, treats plain shells as idle, and adds dirty-worktree abandoned markers to status and session-list output for clawhip consumers.

Constraint: Keep issue #320 prototype minimal and additive without new dependencies

Rejected: Screen-scraping pane output | fragile and broader than needed for lifecycle classification

Confidence: high

Scope-risk: narrow

Tested: cargo test -p rusty-claude-cli

Tested: cargo check -p rusty-claude-cli

Not-tested: cargo clippy -p rusty-claude-cli --all-targets -- -D warnings is blocked by pre-existing commands crate clippy::unnecessary_wraps warnings
2026-04-28 13:12:37 +00:00
Yeachan-Heo 74ea754d29 Restore Rust formatting compliance
Run rustfmt from the Rust workspace so CI format checks pass without changing behavior.

Constraint: Scope is formatting-only across tracked Rust files

Confidence: high

Scope-risk: narrow

Tested: cd rust && cargo fmt --check

Tested: git diff --check
2026-04-28 09:19:16 +00:00
Yeachan-Heo 77afde768c Clarify allowed tool status handling
Reject empty --allowedTools inputs instead of treating them as an empty restriction, and surface status JSON metadata that distinguishes default unrestricted tools from flag-provided allow lists.

Confidence: high
Scope-risk: narrow
Tested: cargo test -p rusty-claude-cli rejects_empty_allowed_tools_flag -- --nocapture
Tested: cargo test -p tools allowed_tools_rejects_empty_token_lists -- --nocapture
Tested: cargo check -p rusty-claude-cli -p tools
Tested: cargo test -p rusty-claude-cli -p tools
Not-tested: full workspace cargo fmt --check is blocked by pre-existing unrelated formatting drift
2026-04-28 05:44:14 +00:00
YeonGyu-Kim f1e4ad7574 feat: #156 — error classification for text-mode output (Phase 2 of #77)
## Problem

#77 Phase 1 added machine-readable error `kind` discriminants to JSON error
payloads. Text-mode (stderr) errors still emit prose-only output with no
structured classification.

Observability tools (log aggregators, CI error parsers) parsing stderr can't
distinguish error classes without regex-scraping the prose.

## Fix

Added `[error-kind: <class>]` prefix line to all text-mode error output.
The prefix appears before the error prose, making it immediately parseable by
line-based log tools without any substring matching.

**Examples:**

## Impact

- Stderr observers (log aggregators, CI systems) can now parse error class
  from the first line without regex or substring scraping
- Same classifier function used for JSON (#77 P1) and text modes
- Text-mode output remains human-readable (error prose unchanged)
- Prefix format follows syslog/structured-logging conventions

## Tests

All 179 rusty-claude-cli tests pass. Verified on 3 different error classes.

Closes ROADMAP #156.
2026-04-22 00:21:32 +09:00
YeonGyu-Kim 9362900b1b feat: #77 Phase 1 — machine-readable error classification in JSON error payloads
## Problem

All JSON error payloads had the same three-field envelope:
```json
{"type": "error", "error": "<prose with hint baked in>"}
```

Five distinct error classes were indistinguishable at the schema level:
- missing_credentials (no API key)
- missing_worker_state (no state file)
- session_not_found / session_load_failed
- cli_parse (unrecognized args)
- invalid_model_syntax

Downstream claws had to regex-scrape the prose to route failures.

## Fix

1. **Added `classify_error_kind()`** — prefix/keyword classifier that returns a
   snake_case discriminant token for 12 known error classes:
   `missing_credentials`, `missing_manifests`, `missing_worker_state`,
   `session_not_found`, `session_load_failed`, `no_managed_sessions`,
   `cli_parse`, `invalid_model_syntax`, `unsupported_command`,
   `unsupported_resumed_command`, `confirmation_required`, `api_http_error`,
   plus `unknown` fallback.

2. **Added `split_error_hint()`** — splits multi-line error messages into
   (short_reason, optional_hint) so the runbook prose stops being stuffed
   into the `error` field.

3. **Extended JSON envelope** at 4 emit sites:
   - Main error sink (line ~213)
   - Session load failure in resume_session
   - Stub command (unsupported_command)
   - Unknown resumed command (unsupported_resumed_command)

## New JSON shape

```json
{
  "type": "error",
  "error": "short reason (first line)",
  "kind": "missing_credentials",
  "hint": "Hint: export ANTHROPIC_API_KEY..."
}
```

`kind` is always present. `hint` is null when no runbook follows.
`error` now carries only the short reason, not the full multi-line prose.

## Tests

Added 2 new regression tests:
- `classify_error_kind_returns_correct_discriminants` — all 9 known classes + fallback
- `split_error_hint_separates_reason_from_runbook` — with and without hints

All 179 rusty-claude-cli tests pass. Full workspace green.

Closes ROADMAP #77 Phase 1.
2026-04-21 22:38:13 +09:00
YeonGyu-Kim 3cfe6e2b14 feat: #154 — hint provider prefix and env var when model name looks like different provider
## Problem

When a user types `claw --model gpt-4` or `--model qwen-plus`, they get:
```
error: invalid model syntax: 'gpt-4'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias
```

USAGE.md documents that "The error message now includes a hint that names the detected env var" — but this hint does not actually exist. The user has to re-read USAGE.md or guess the correct prefix.

## Fix

Enhance `validate_model_syntax` to detect when a model name looks like it belongs to a different provider:

1. **OpenAI models** (starts with `gpt-` or `gpt_`):
   ```
   Did you mean `openai/gpt-4`? (Requires OPENAI_API_KEY env var)
   ```

2. **Qwen/DashScope models** (starts with `qwen`):
   ```
   Did you mean `qwen/qwen-plus`? (Requires DASHSCOPE_API_KEY env var)
   ```

3. **Grok/xAI models** (starts with `grok`):
   ```
   Did you mean `xai/grok-3`? (Requires XAI_API_KEY env var)
   ```

Unrelated invalid models (e.g., `asdfgh`) do not get a spurious hint.

## Verification

- `claw --model gpt-4` → hints `openai/gpt-4` + `OPENAI_API_KEY`
- `claw --model qwen-plus` → hints `qwen/qwen-plus` + `DASHSCOPE_API_KEY`
- `claw --model grok-3` → hints `xai/grok-3` + `XAI_API_KEY`
- `claw --model asdfgh` → generic error (no hint)

## Tests

Added 3 new assertions in `parses_multiple_diagnostic_subcommands`:
- GPT model error hints openai/ prefix and OPENAI_API_KEY
- Qwen model error hints qwen/ prefix and DASHSCOPE_API_KEY
- Unrelated models don't get a spurious hint

All 177 rusty-claude-cli tests pass.

Closes ROADMAP #154.
2026-04-21 21:40:48 +09:00
YeonGyu-Kim 79352a2d20 feat: #152 — hint `--output-format json` when user types `--json` on diagnostic verbs
## Problem

Users commonly type `claw doctor --json`, `claw status --json`, or
`claw system-prompt --json` expecting JSON output. These fail with
`unrecognized argument \`--json\` for subcommand` with no hint that
`--output-format json` is the correct flag.

## Discovery

Filed as #152 during 21:17 dogfood nudge. The #127 worktree contained
a more comprehensive patch but conflicted with #141 (unified --help).
On re-investigation of main, Bugs 1 and 3 from #127 are already closed
(positional arg rejection works, no double "error:" prefix). Only
Bug 2 (the `--json` hint) remained.

## Fix

Two call sites add the hint:

1. `parse_single_word_command_alias`'s diagnostic-verb suffix path:
   when rest[1] == "--json", append "Did you mean \`--output-format json\`?"

2. `parse_system_prompt_options` unknown-option path: same hint when
   the option is exactly `--json`.

## Verification

Before:
  $ claw doctor --json
  error: unrecognized argument `--json` for subcommand `doctor`
  Run `claw --help` for usage.

After:
  $ claw doctor --json
  error: unrecognized argument `--json` for subcommand `doctor`
  Did you mean `--output-format json`?
  Run `claw --help` for usage.

Covers: `doctor --json`, `status --json`, `sandbox --json`,
`system-prompt --json`, and any other diagnostic verb that routes
through `parse_single_word_command_alias`.

Other unrecognized args (`claw doctor garbage`) correctly don't
trigger the hint.

## Tests

- 2 new assertions in `parses_multiple_diagnostic_subcommands`:
  - `claw doctor --json` produces hint
  - `claw doctor garbage` does NOT produce hint
- 177 rusty-claude-cli tests pass
- Workspace tests green

Closes ROADMAP #152.
2026-04-21 21:23:17 +09:00
YeonGyu-Kim eaa077bf91 fix: #150 — eliminate symlink canonicalization flake in resume_latest test + file #246 (reminder outcome ambiguity)
## #150 Fix: resume_latest test flake

**Problem:** `resume_latest_restores_the_most_recent_managed_session` intermittently
fails when run in the workspace suite or multiple times in sequence, but passes in
isolation.

**Root cause:** `workspace_fingerprint(path)` hashes the path string without
canonicalization. On macOS, `/tmp` is a symlink to `/private/tmp`. The test
creates a temp dir via `std::env::temp_dir().join(...)` which returns
`/var/folders/...` (non-canonical). When the subprocess spawns,
`env::current_dir()` returns the canonical path `/private/var/folders/...`.
The two fingerprints differ, so the subprocess looks in
`.claw/sessions/<hash1>` while files are in `.claw/sessions/<hash2>`.
Session discovery fails.

**Fix:** Call `fs::canonicalize(&project_dir)` after creating the directory
to ensure test and subprocess use identical path representations.

**Verification:** 5 consecutive runs of the full test suite — all pass.
Previously: 5/5 failed when run in sequence.

## #246 Filing: Reminder cron outcome ambiguity (control-loop blocker)

The `clawcode-dogfood-cycle-reminder` cron times out repeatedly with no
structured feedback on whether the nudge was delivered, skipped, or died in-flight.

**Phase 1 outcome schema** — add explicit field to cron result:
- `delivered` — nudge posted to Discord
- `timed_out_before_send` — died before posting
- `timed_out_after_send` — posted but cleanup timed out
- `skipped_due_to_active_cycle` — previous cycle active
- `aborted_gateway_draining` — daemon shutdown

Assigned to gaebal-gajae (cron/orchestration domain). Unblocks trustworthy
dogfood cycle observability.

Closes ROADMAP #150. Filed ROADMAP #246.
2026-04-21 21:01:09 +09:00
YeonGyu-Kim f84c7c4ed5 feat: #148 + #128 closure — model provenance in claw status JSON/text
## Scope

Two deltas in one commit:

### #128 closure (docs)

Re-verified on main HEAD `4cb8fa0`: malformed `--model` strings already
rejected at parse time (`validate_model_syntax` in parse_args). All
historical repro cases now produce specific errors:

  claw --model ''                       → error: model string cannot be empty
  claw --model 'bad model'              → error: invalid model syntax: 'bad model' contains spaces
  claw --model 'sonet'                  → error: invalid model syntax: 'sonet'. Expected provider/model or known alias
  claw --model '@invalid'               → error: invalid model syntax: '@invalid'. Expected provider/model ...
  claw --model 'totally-not-real-xyz'   → error: invalid model syntax: ...
  claw --model sonnet                   → ok, resolves to claude-sonnet-4-6
  claw --model anthropic/claude-opus-4-6 → ok, passes through

Marked #128 CLOSED in ROADMAP with repro block. Residual provenance gap
split off as #148.

### #148 implementation

**Problem.** After #128 closure, `claw status --output-format json`
still surfaces only the resolved model string. No way for a claw to
distinguish whether `claude-sonnet-4-6` came from `--model sonnet`
(alias resolution) vs `--model claude-sonnet-4-6` (pass-through) vs
`ANTHROPIC_MODEL` env vs `.claw.json` config vs compiled-in default.

Debug forensics had to re-read argv instead of reading a structured
field. Clawhip orchestrators sending `--model` couldn't confirm the
flag was honored vs falling back to default.

**Fix.** Added two fields to status JSON envelope:
- `model_source`: "flag" | "env" | "config" | "default"
- `model_raw`: user's input before alias resolution (null on default)

Text mode appends a `Model source` line under `Model`, showing the
source and raw input (e.g. `Model source     flag (raw: sonnet)`).

**Resolution order** (mirrors resolve_repl_model but with source
attribution):
1. If `--model` / `--model=` flag supplied → source: flag, raw: flag value
2. Else if ANTHROPIC_MODEL set → source: env, raw: env value
3. Else if `.claw.json` model key set → source: config, raw: config value
4. Else → source: default, raw: null

## Changes

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

- Added `ModelSource` enum (Flag/Env/Config/Default) with `as_str()`.
- Added `ModelProvenance` struct (resolved, raw, source) with
  three constructors: `default_fallback()`, `from_flag(raw)`, and
  `from_env_or_config_or_default(cli_model)`.
- Added `model_flag_raw: Option<String>` field to `CliAction::Status`.
- Parse loop captures raw input in `--model` and `--model=` arms.
- Extended `parse_single_word_command_alias` to thread
  `model_flag_raw: Option<&str>` through.
- Extended `print_status_snapshot` signature to accept
  `model_flag_raw: Option<&str>`. Resolves provenance at dispatch time
  (flag provenance from arg; else probe env/config/default).
- Extended `status_json_value` signature with
  `provenance: Option<&ModelProvenance>`. On Some, adds `model_source`
  and `model_raw` fields; on None (legacy resume paths), omits them
  for backward compat.
- Extended `format_status_report` signature with optional provenance.
  On Some, renders `Model source` line after `Model`.
- Updated all existing callers (REPL /status, resume /status, tests)
  to pass None (legacy paths don't carry flag provenance).
- Added 2 regression assertions in parse_args test covering both
  `--model sonnet` and `--model=...` forms.

### ROADMAP.md

- Marked #128 CLOSED with re-verification block.
- Filed #148 documenting the provenance gap split, fix shape, and
  acceptance criteria.

## Live verification

$ claw --model sonnet --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-sonnet-4-6", "model_source": "flag", "model_raw": "sonnet"}

$ claw --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-opus-4-6", "model_source": "default", "model_raw": null}

$ ANTHROPIC_MODEL=haiku claw --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-haiku-4-5-20251213", "model_source": "env", "model_raw": "haiku"}

$ echo '{"model":"claude-opus-4-7"}' > .claw.json && claw --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-opus-4-7", "model_source": "config", "model_raw": "claude-opus-4-7"}

$ claw --model sonnet status
Status
  Model            claude-sonnet-4-6
  Model source     flag (raw: sonnet)
  Permission mode  danger-full-access
  ...

## Tests

- rusty-claude-cli bin: 177 tests pass (2 new assertions for #148)
- Full workspace green except pre-existing resume_latest flake (unrelated)

Closes ROADMAP #128, #148.
2026-04-21 20:48:46 +09:00
YeonGyu-Kim 4cb8fa059a feat: #147 — reject empty / whitespace-only prompts at CLI fallthrough
## Problem

The `"prompt"` subcommand arm enforced `if prompt.trim().is_empty()`
and returned a specific error. The fallthrough `other` arm in the same
match block — which routes any unrecognized first positional arg to
`CliAction::Prompt` — had no such guard. Result:

$ claw ""
error: missing Anthropic credentials; export ANTHROPIC_AUTH_TOKEN ...

$ claw "   "
error: missing Anthropic credentials; ...

$ claw "" ""
error: missing Anthropic credentials; ...

$ claw --output-format json ""
{"error":"missing Anthropic credentials; ...","type":"error"}

An empty prompt should never reach the credentials check. Worse: with
valid credentials, the literal empty string gets sent to Claude as a
user prompt, either burning tokens for nothing or triggering a model-
side refusal. Same prompt-misdelivery family as #145.

## Root cause

In `parse_subcommand()`, the final `other =>` arm in the top-level
match only guards against typos (#108 guard via `looks_like_subcommand_typo`)
and then unconditionally builds `CliAction::Prompt { prompt: rest.join(" ") }`.
An empty/whitespace-only join passes through.

## Changes

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

Added the same `if joined.trim().is_empty()` guard already used in the
`"prompt"` arm to the fallthrough path. Error message distinguishes it
from the `prompt` subcommand path:

  empty prompt: provide a subcommand (run `claw --help`) or a
  non-empty prompt string

Runs AFTER the typo guard (so `claw sttaus` still suggests `status`)
and BEFORE CliAction::Prompt construction (so no network call ever
happens for empty inputs).

### Regression tests

Added 4 assertions in the existing parse_args test:
- parse_args([""]) → Err("empty prompt: ...")
- parse_args(["   "]) → Err("empty prompt: ...")
- parse_args(["", ""]) → Err("empty prompt: ...")
- parse_args(["sttaus"]) → Err("unknown subcommand: ...") [verifies #108 typo guard still takes precedence]

### ROADMAP.md

Added Pinpoint #147 documenting the gap, verification, root cause,
fix shape, and acceptance. Joins the prompt-misdelivery cluster
alongside #145.

## Live verification

$ claw ""
error: empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string

$ claw "   "
error: empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string

$ claw --output-format json ""
{"error":"empty prompt: provide a subcommand ...","type":"error"}

$ claw prompt ""   # unchanged: subcommand-specific error preserved
error: prompt subcommand requires a prompt string

$ claw hello        # unchanged: typo guard still fires
error: unknown subcommand: hello.
  Did you mean     help

$ claw "real prompt here"   # unchanged: real prompts still reach API
error: api returned 401 Unauthorized (with dummy key, as expected)

All empty/whitespace-only paths exit 1. No network call. No misleading
credentials error.

## Tests

- rusty-claude-cli bin: 177 tests pass (4 new assertions)
- Full workspace green except pre-existing resume_latest flake (unrelated)

Closes ROADMAP #147.
2026-04-21 20:35:17 +09:00
YeonGyu-Kim f877acacbf feat: #146 — wire `claw config` and `claw diff` as standalone subcommands
## Problem

`claw config` and `claw diff` are pure-local read-only introspection
commands (config merges .claw.json + .claw/settings.json from disk; diff
shells out to `git diff --cached` + `git diff`). Neither needs a session
context, yet both rejected direct CLI invocation:

$ claw config
error: `claw config` is a slash command. Use `claw --resume SESSION.jsonl /config` ...

$ claw diff
error: `claw diff` is a slash command. ...

This forced clawing operators to spin up a full session just to inspect
static disk state, and broke natural pipelines like
`claw config --output-format json | jq`.

## Root cause

Sibling of #145: `SlashCommand::Config { section }` and
`SlashCommand::Diff` had working renderers (`render_config_report`,
`render_config_json`, `render_diff_report`, `render_diff_json_for`)
exposed for resume sessions, but the top-level CLI parser in
`parse_subcommand()` had no arms for them. Zero-arg `config`/`diff`
hit `parse_single_word_command_alias`'s fallback to
`bare_slash_command_guidance`, producing the misleading guidance.

## Changes

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

- Added `CliAction::Config { section, output_format }` and
  `CliAction::Diff { output_format }` variants.
- Added `"config"` / `"diff"` arms to the top-level parser in
  `parse_subcommand()`. `config` accepts an optional section name
  (env|hooks|model|plugins) matching SlashCommand::Config semantics.
  `diff` takes no positional args. Both reject extra trailing args
  with a clear error.
- Added `"config" | "diff" => None` to
  `parse_single_word_command_alias` so bare invocations fall through
  to the new parser arms instead of the slash-guidance error.
- Added dispatch in run() that calls existing renderers: text mode uses
  `render_config_report` / `render_diff_report`; JSON mode uses
  `render_config_json` / `render_diff_json_for` with
  `serde_json::to_string_pretty`.
- Added 5 regression assertions in parse_args test covering:
  parse_args(["config"]), parse_args(["config", "env"]),
  parse_args(["config", "--output-format", "json"]),
  parse_args(["diff"]), parse_args(["diff", "--output-format", "json"]).

### ROADMAP.md

Added Pinpoint #146 documenting the gap, verification, root cause,
fix shape, and acceptance. Explicitly notes which other slash commands
(`hooks`, `usage`, `context`, etc.) are NOT candidates because they
are session-state-modifying.

## Live verification

$ claw config   # no config files
Config
  Working directory /private/tmp/cd-146-verify
  Loaded files      0
  Merged keys       0
Discovered files
  user    missing ...
  project missing ...
  local   missing ...
Exit 0.

$ claw config --output-format json
{
  "cwd": "...",
  "files": [...],
  ...
}

$ claw diff   # no git
Diff
  Result           no git repository
  Detail           ...
Exit 0.

$ claw diff --output-format json   # inside claw-code
{
  "kind": "diff",
  "result": "changes",
  "staged": "",
  "unstaged": "diff --git ..."
}
Exit 0.

## Tests

- rusty-claude-cli bin: 177 tests pass (5 new assertions in parse_args)
- Full workspace green except pre-existing resume_latest flake (unrelated)

## Not changed

`hooks`, `usage`, `context`, `tasks`, `theme`, `voice`, `rename`,
`copy`, `color`, `effort`, `branch`, `rewind`, `ide`, `tag`,
`output-style`, `add-dir` — all session-mutating or interactive-only;
correctly remain slash-only.

Closes ROADMAP #146.
2026-04-21 20:07:28 +09:00
YeonGyu-Kim 7d63699f9f feat: #145 — wire `claw plugins` subcommand to CLI parser (prompt misdelivery fix)
## Problem

`claw plugins` (and `claw plugins list`, `claw plugins --help`,
`claw plugins info <name>`, etc.) fell through the top-level subcommand
match and got routed into the prompt-execution path. Result: a purely
local introspection command triggered an Anthropic API call and surfaced
`missing Anthropic credentials` to the user. With valid credentials, it
would actually send the literal string "plugins" as a user prompt to
Claude, burning tokens for a local query.

$ claw plugins
error: missing Anthropic credentials; export ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY before calling the Anthropic API

$ ANTHROPIC_API_KEY=dummy claw plugins
⠋ 🦀 Thinking...
✘  Request failed
error: api returned 401 Unauthorized

Meanwhile siblings (`agents`, `mcp`, `skills`) all worked correctly:

$ claw agents
No agents found.
$ claw mcp
MCP
  Working directory ...
  Configured servers 0

## Root cause

`CliAction::Plugins` exists, has a working dispatcher
(`LiveCli::print_plugins`), and is produced inside the REPL via
`SlashCommand::Plugins`. But the top-level CLI parser in
`parse_subcommand()` had arms for `agents`, `mcp`, `skills`, `status`,
`doctor`, `init`, `export`, `prompt`, etc., and **no arm for
`plugins`**. The dispatch never ran from the CLI entry point.

## Changes

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

Added a `"plugins"` arm to the top-level match in `parse_subcommand()`
that produces `CliAction::Plugins { action, target, output_format }`,
following the same positional convention as `mcp` (`action` = first
positional, `target` = second). Rejects >2 positional args with a clear
error.

Added four regression assertions in the existing `parse_args` test:
- `plugins` alone → `CliAction::Plugins { action: None, target: None }`
- `plugins list` → action: Some("list"), target: None
- `plugins enable <name>` → action: Some("enable"), target: Some(...)
- `plugins --output-format json` → action: None, output_format: Json

### ROADMAP.md

Added Pinpoint #145 documenting the gap, verification, root cause,
fix shape, and acceptance.

## Live verification

$ claw plugins   # no credentials set
Plugins
  example-bundled      v0.1.0      disabled
  sample-hooks         v0.1.0      disabled

$ claw plugins --output-format json   # no credentials set
{
  "action": "list",
  "kind": "plugin",
  "message": "Plugins\n  example-bundled ...\n  sample-hooks ...",
  "reload_runtime": false,
  "target": null
}

Exit 0 in all modes. No network call. No "missing credentials" error.

## Tests

- rusty-claude-cli bin: 177 tests pass (new plugin assertions included)
- Full workspace green except pre-existing resume_latest flake (unrelated)

Closes ROADMAP #145.
2026-04-21 19:36:49 +09:00
YeonGyu-Kim e2a43fcd49 feat: #143 phase 1 — claw status degrades gracefully on malformed config
Previously `claw status` hard-failed on any config parse error, emitting
a bare error string and exiting 1. This took down the entire health
surface for a single malformed MCP entry, even though workspace, git,
model, permission, and sandbox state could all be reported independently.

`claw doctor` already degraded gracefully on the exact same input.
This commit matches `claw status` to that contract.

Changes:
- Add `StatusContext::config_load_error: Option<String>` to capture parse
  errors without aborting.
- Rewrite `status_context()` to match on `ConfigLoader::load()`: on Err,
  fall back to default `SandboxConfig` for sandbox resolution and record
  the parse error, then continue populating workspace/git/memory fields.
- JSON output gains top-level `status: "ok" | "degraded"` marker and a
  `config_load_error` string (null on clean runs). All other existing
  fields preserved for backward compat.
- Text output prepends a "Config load error" block with Details + Hint
  when config failed to parse, then a "Status (degraded)" header on the
  main block. Clean runs show the usual "Status" header.
- Doctor path updated to pass the config load error through StatusContext.

Regression test `status_degrades_gracefully_on_malformed_mcp_config_143`:
- Injects a .claw.json with one valid + one malformed mcpServers entry
- Asserts status_context() returns Ok (not Err)
- Asserts config_load_error names the malformed field path
- Asserts workspace/sandbox fields still populated in JSON
- Asserts top-level status is 'degraded'
- Asserts clean config path still returns status: 'ok'

Verified live on /Users/yeongyu/clawd (contains deliberately broken MCP entries):
  $ claw status --output-format json
  { "status": "degraded",
    "config_load_error": ".../mcpServers.missing-command: missing string field command",
    "model": "claude-opus-4-6",
    "workspace": {...},
    "sandbox": {...},
    ... }

Phase 2 (typed error object joining #4.44 taxonomy) tracked separately.

Full workspace test green except pre-existing resume_latest flake (unrelated).

Closes ROADMAP #143 phase 1.
2026-04-21 18:37:42 +09:00
YeonGyu-Kim 541c5bb95d feat: #139 actionable worker-state guidance in claw state error + help
Previously `claw state` errored with "no worker state file found ... — run a
worker first" but there is no `claw worker` subcommand, so claws had no
discoverable path from the error to a fix.

Changes:
- Rewrite the missing-state error to name the two concrete commands that
  produce .claw/worker-state.json:
    * `claw` (interactive REPL, writes state on first turn)
    * `claw prompt <text>` (one non-interactive turn)
  Also tell the user what to rerun: `claw state [--output-format json]`.
- Expand the State --help topic with "Produces state", "Observes state",
  and "Exit codes" lines so the worker-state contract is discoverable
  before the user hits the error.
- Add regression test state_error_surfaces_actionable_worker_commands_139
  asserting the error contains `claw prompt`, REPL mention, and the
  rerun path, plus that the help topic documents the producer contract.

Verified live:
  $ claw state
  error: no worker state file found at .claw/worker-state.json
    Hint: worker state is written by the interactive REPL or a non-interactive prompt.
    Run:   claw               # start the REPL (writes state on first turn)
    Or:    claw prompt <text> # run one non-interactive turn
    Then rerun: claw state [--output-format json]

JSON mode preserves the full hint inside the error envelope so CI/claws
can match on `claw prompt` without losing the canonical prefix.

Full workspace test green except pre-existing resume_latest flake (unrelated).

Closes ROADMAP #139.
2026-04-21 18:04:04 +09:00
YeonGyu-Kim 611eed1537 feat: #142 structured fields in claw init --output-format json
Previously `claw init --output-format json` emitted a valid JSON envelope but
packed the entire human-formatted output into a single `message` string. Claw
scripts had to substring-match human language to tell `created` from `skipped`.

Changes:
- Add InitStatus::json_tag() returning machine-stable "created"|"updated"|"skipped"
  (unlike label() which includes the human " (already exists)" suffix).
- Add InitReport::NEXT_STEP constant so claws can read the next-step hint
  without grepping the message string.
- Add InitReport::artifacts_with_status() to partition artifacts by state.
- Add InitReport::artifact_json_entries() for the structured artifacts[] array.
- Rewrite run_init + init_json_value to emit first-class fields alongside the
  legacy message string (kept for text consumers): project_path, created[],
  updated[], skipped[], artifacts[], next_step, message.
- Update the slash-command Init dispatch to use the same structured JSON.
- Add regression test artifacts_with_status_partitions_fresh_and_idempotent_runs
  asserting both fresh + idempotent runs produce the right partitioning and
  that the machine-stable tag is bare 'skipped' not label()'s phrasing.

Verified output:
- Fresh dir: created[] has 4 entries, skipped[] empty
- Idempotent call: created[] empty, skipped[] has 4 entries
- project_path, next_step as first-class keys
- message preserved verbatim for backward compat

Full workspace test green except pre-existing resume_latest flake (unrelated).

Closes ROADMAP #142.
2026-04-21 17:42:00 +09:00
YeonGyu-Kim 7763ca3260 feat: #141 unify claw <subcommand> --help contract across all 14 subcommands
Previously, `claw <subcommand> --help` had 5 different behaviors:
- 7 subcommands returned subcommand-specific help (correct)
- init/export/state/version silently fell back to global `claw --help`
- system-prompt/dump-manifests errored with `unknown <cmd> option: --help`
- bootstrap-plan printed its phase list instead of help text

Changes:
- Extend LocalHelpTopic enum with Init, State, Export, Version, SystemPrompt,
  DumpManifests, BootstrapPlan variants.
- Extend parse_local_help_action() to resolve those 7 subcommands to their
  local help topic instead of falling through to the main dispatch.
- Remove init/state/export/version from the explicit wants_help=true matcher
  so they reach parse_local_help_action() before being routed to global help.
- Add render_help_topic() entries for the 7 new topics with consistent
  Usage/Purpose/Output/Formats/Related structure.
- Add regression test subcommand_help_flag_has_one_contract_across_all_subcommands_141
  asserting every documented subcommand + both --help and -h variants resolve
  to a HelpTopic with non-empty text that contains a Usage line.

Verification:
- All 14 subcommands now return subcommand-specific help (live dogfood).
- Full workspace test green except pre-existing resume_latest flake.

Closes ROADMAP #141.
2026-04-21 17:36:48 +09:00
YeonGyu-Kim 27ffd75f03 fix: #140 isolate test cwd + env in punctuation_bearing_single_token test
Previously this test inherited the cargo test runner's CWD, which could contain
a stale .claw/settings.json with "permissionMode": "acceptEdits" written by
another test. The deprecated-field resolver then silently downgraded the
default permission mode to WorkspaceWrite, breaking the test's assertion.

Fix: wrap the assertion in with_current_dir() + env_lock() so the test runs in
an isolated temp directory with no stale config.

Full workspace test now passes except for pre-existing resume_latest flake
(unrelated to #140, environment-dependent, tracked separately).

Closes ROADMAP #140.
2026-04-21 16:34:58 +09:00
YeonGyu-Kim f3f6643fb9 feat: #108 add did-you-mean guard for subcommand typos (prevents silent LLM dispatch)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-21 15:37:58 +09:00
YeonGyu-Kim a8beca1463 fix: #136 support --output-format json with --compact flag
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-21 14:47:15 +09:00
YeonGyu-Kim 21adae9570 fix: #137 update test fixtures to use canonical 'opus' alias for main branch consistency
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-21 14:32:49 +09:00
YeonGyu-Kim 50e3fa3a83 docs: add --output-format to diagnostic verb help text
Updated LocalHelpTopic help strings to surface --output-format support:
- Status, Sandbox, Doctor, Acp all now show [--output-format <format>]
- Added 'Formats: text (default), json' line to each

Diagnostic verbs support JSON output but help text didn't advertise it.
Post-#127 fix: help text now matches actual CLI surface.

Verified: cargo build passes, claw doctor --help shows output-format.

Refs: #127
2026-04-20 21:32:02 +09:00
YeonGyu-Kim a3270db602 fix: #127 reject unrecognized suffix args for diagnostic verbs
Diagnostic verbs (help, version, status, sandbox, doctor, state) now
reject unrecognized suffix arguments at parse time instead of silently
falling through to Prompt dispatch.

Fixes: claw doctor --json (and similar) no longer accepts --json silently
and attempts to send it to the LLM as a prompt. Now properly emits:
'unrecognized argument `--json` for subcommand `doctor`'

Joined parser-level trust gap quintet #108 + #117 + #119 + #122 + #127.
Prevents token burn on rejected arguments.

Verified: cargo build --workspace passes, claw doctor --json errors cleanly.

Refs: #127, ROADMAP
2026-04-20 19:23:35 +09:00
YeonGyu-Kim 2678fa0af5 fix: #124 --model validation rejects malformed syntax at parse time
Adds validate_model_syntax() that rejects:
- Empty strings
- Strings with spaces (e.g., 'bad model')
- Invalid provider/model format

Accepts:
- Known aliases (opus, sonnet, haiku)
- Valid provider/model format (provider/model)

Wired into parse_args for both --model <value> and --model=<value> forms.
Errors exit with clear message before any API calls (no token burn).

Verified:
- 'claw --model "bad model" version' → error, exit 1
- 'claw --model "" version' → error, exit 1
- 'claw --model opus version' → works
- 'claw --model anthropic/claude-opus-4-6 version' → works

Refs: ROADMAP #124 (debbcbe cluster — parser-level trust gap family)
2026-04-20 16:32:17 +09:00
YeonGyu-Kim b9990bb27c fix: #122 + #125 doctor consistency and git_state clarity
#122: doctor invocation now checks stale-base condition
- Calls run_stale_base_preflight(None) in render_doctor_report()
- Emits stale-base warnings to stderr when branch is behind main
- Fixes inconsistency: doctor 'ok' vs prompt 'stale base' warning

#125: git_state field reflects non-git directories
- When !in_git_repo, git_state = 'not in git repo' instead of 'clean'
- Fixes contradiction: in_git_repo: false but git_state: 'clean'
- Applied in both doctor text output and status JSON

Verified: cargo build --workspace passes.

Refs: ROADMAP #122 (dd73962), #125 (debbcbe)
2026-04-20 16:13:43 +09:00
YeonGyu-Kim f33c315c93 fix: #122 doctor invocation now checks stale-base condition
Adds run_stale_base_preflight(None) call to render_doctor_report() so that
claw doctor emits stale-base warnings to stderr when the current branch is
behind main. Previously doctor reported 'ok' even when branch was stale,
creating inconsistency with prompt path warnings.

Fixes silent-state inventory gap: doctor now consistent with prompt/repl
stale-base checking. No behavior change for non-stale branches.

Verified: cargo build --workspace passes, no test failures.

Ref: ROADMAP #122 dogfood filing @ dd73962
2026-04-20 15:49:56 +09:00
YeonGyu-Kim c956f78e8a ROADMAP #4.44.5: Ship/provenance opacity — filed from dogfood
Added structured delivery-path contract to surface branch → merge → main-push
provenance as first-class events. Filed from the 56-commit 2026-04-20 push
that exposed the gap.

Also fixes: ApiError test compilation — add suggested_action: None to 4 sites

- Line ~8414: opaque_provider_wrapper_surfaces_failure_class_session_and_trace
- Line ~8436: retry_exhaustion_uses_retry_failure_class_for_generic_provider_wrapper
- Line ~8499: provider_context_window_errors_are_reframed_with_same_guidance
- Line ~8533: retry_wrapped_context_window_errors_keep_recovery_guidance
2026-04-20 14:35:07 +09:00
Yeachan-Heo ac45bbec15 Make ACP/Zed status obvious before users go source-diving
ROADMAP #21, #22, and #23 were already closed on current main, so the next real repo-local backlog item was the ACP/Zed discoverability gap. This adds a local `claw acp` status surface plus aliases, updates help/docs, and separates the shipped discoverability fix from the still-open daemon/protocol follow-up so editor-first users get a crisp answer immediately.

Constraint: No ACP/Zed daemon or protocol server exists in claw-code yet, so the new surface must be explicit status guidance rather than a fake implementation
Rejected: Add a pretend `acp serve` daemon path | would imply supported protocol behavior that does not exist
Rejected: Docs-only clarification | still leaves `claw --help` unable to answer the editor-launch question directly
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep ROADMAP discoverability fixes separate from future ACP daemon/protocol work so help text and backlog IDs stay unambiguous
Tested: cargo fmt --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; cargo run -q -p rusty-claude-cli -- acp; cargo run -q -p rusty-claude-cli -- --output-format json acp; architect review APPROVED
Not-tested: Real ACP/Zed daemon launch because no protocol-serving surface exists yet
2026-04-16 03:13:50 +00:00
Yeachan-Heo 42bb6cdba6 Keep local clawhip artifacts from tripping routine repo work
Dogfooding kept reproducing OMX team merge conflicts on
`.clawhip/state/prompt-submit.json`, so the init bootstrap now
teaches repos to ignore `.clawhip/` alongside the existing local
`.claw/` artifacts. This also updates the current repo ignore list
so the fix helps immediately instead of only on future `claw init`
runs.

Constraint: Keep the fix narrow and centered on repo-local ignore hygiene
Rejected: Broader team merge-hygiene changes | unnecessary for the proven local root cause
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If more runtime-local artifact directories appear, extend the shared init gitignore list instead of patching repos ad hoc
Tested: cargo fmt --all --check
Tested: cargo clippy --workspace --all-targets -- -D warnings
Tested: cargo test --workspace
Tested: Architect review (APPROVE)
Not-tested: Existing clones with already-tracked `.clawhip` files still need manual cleanup
Related: ROADMAP #75
2026-04-12 14:47:40 +00:00
Yeachan-Heo f91d156f85 Keep poisoned test locks from cascading across unrelated regressions
The repo-local backlog was effectively exhausted, so this sweep promoted the
newly observed test-lock poisoning pain point into ROADMAP #74 and fixed it in
place. Test-only env/cwd lock acquisition now recovers poisoned mutexes in the
remaining strict call sites, and each affected surface has a regression that
proves a panic no longer permanently poisons later tests.

Constraint: Keep the fix test-only and avoid widening runtime behavior changes
Rejected: Refactor shared helper signatures across broader call paths | unnecessary churn beyond the remaining strict test sites
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: These guards only recover the mutex; tests that mutate env or cwd still must restore process-global state explicitly
Tested: cargo fmt --all --check
Tested: cargo clippy --workspace --all-targets -- -D warnings
Tested: cargo test --workspace
Tested: Architect review (APPROVE)
Not-tested: Additional fault-injection around partially restored env/cwd state after panic
Related: ROADMAP #74
2026-04-12 13:52:41 +00:00
Yeachan-Heo 2e34949507 Keep latest-session timestamps increasing under tight loops
The next repo-local sweep target was ROADMAP #73: repeated backlog
sweeps exposed that session writes could share the same wall-clock
millisecond, which made semantic recency fragile and forced the
resume-latest regression to sleep between saves. The fix makes session
timestamps monotonic within the process and removes the timing hack
from the test so latest-session selection stays stable under tight
loops.

Constraint: Preserve the existing session file format while changing only the timestamp source semantics
Rejected: Keep the sleep-based test workaround | hides the real ordering hazard instead of fixing timestamp generation
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any future session-recency logic must keep `current_time_millis`, ordering tests, and latest-session expectations aligned
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE
Not-tested: Cross-process monotonicity when multiple binaries write sessions concurrently
2026-04-12 10:51:19 +00:00
Yeachan-Heo 8f53524bd3 Make backlog-scan lanes say what they actually selected
The next repo-local sweep target was ROADMAP #65: backlog-scanning
lanes could stop with prose-only summaries naming roadmap items, but
there was no machine-readable record of which items were chosen,
which were skipped, or whether the lane intended to execute, review,
or no-op. The fix teaches completed lane persistence to extract a
structured selection outcome while preserving the existing quality-
floor and review-verdict behavior for other lanes.

Constraint: Keep selection-outcome extraction on the existing `lane.finished` metadata path instead of inventing a separate event stream
Rejected: Add a dedicated selection event type first | unnecessary for this focused closeout because `lane.finished` already persists structured data downstream can read
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If backlog-scan summary conventions change later, update `extract_selection_outcome`, its regression test, and the ROADMAP closeout wording together
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE after roadmap closeout update
Not-tested: Downstream consumers that may still ignore `lane.finished.data.selectionOutcome`
2026-04-12 09:54:37 +00:00
Yeachan-Heo dbc2824a3e Keep latest session selection tied to real session recency
The next repo-local sweep target was ROADMAP #72: the `latest`
managed-session alias could depend on filesystem mtime before the
session's own persisted recency markers, which made the selection
path vulnerable to coarse or misleading file timestamps. The fix
promotes `updated_at_ms` into the summary/order path, keeps CLI
wrappers in sync, and locks the mtime-vs-session-recency case with
regression coverage.

Constraint: Preserve existing managed-session storage layout while changing only the ordering signal
Rejected: Keep sorting by filesystem mtime and just sleep longer in tests | hides the semantic ordering bug instead of fixing it
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any future managed-session ordering change must keep runtime and CLI summary structs aligned on the same recency fields
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE
Not-tested: Cross-filesystem behavior where persisted session JSON cannot be read and fallback ordering uses mtime only
2026-04-12 07:49:32 +00:00
Yeachan-Heo 3b806702e7 Make the CLI point users at the real install source
The next repo-local backlog item was ROADMAP #70: users could
mistake third-party pages or the deprecated `cargo install
claw-code` path for the official install route. The CLI now
surfaces the source of truth directly in `claw doctor` and
`claw --help`, and the roadmap closeout records the change.

Constraint: Keep the fix inside repo-local Rust CLI surfaces instead of relying on docs alone
Rejected: Close #70 with README-only wording | the bug was user-facing CLI ambiguity, so the warning needed to appear in runtime help/doctor output
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If install guidance changes later, update both the doctor check payload and the help-text warning together
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE
Not-tested: Third-party websites outside this repo that may still present stale install instructions
2026-04-12 04:50:03 +00:00
Yeachan-Heo 4f83a81cf6 Make dump-manifests recoverable outside the inferred build tree
The backlog sweep found that the user-cited #21-#23 items were already
closed, and the next real pain point was `claw dump-manifests` failing
without a direct way to point at the upstream manifest source. This adds
an explicit `--manifests-dir` path, upgrades the failure messages to say
whether the source root or required files are missing, and updates the
ROADMAP closeout to reflect that #45 is now fixed.

Constraint: Preserve existing dump-manifests behavior when no explicit override is supplied
Rejected: Require CLAUDE_CODE_UPSTREAM for every invocation | breaks existing build-tree workflows and is unnecessarily rigid
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep manifest-source override guidance centralized so future error-path edits do not drift
Tested: cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE
Not-tested: Manual invocation against every legacy env-based manifest lookup layout
2026-04-12 02:57:11 +00:00
Yeachan-Heo 264fdc214e Retire the stale bare-skill dispatch backlog item
ROADMAP #36 remained open even though current main already dispatches bare
skill names in the REPL through skill resolution instead of forwarding them
to the model. This change adds a direct regression test for that behavior
and marks the backlog item done with fresh verification evidence.

Constraint: User required fresh cargo fmt, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace before closeout
Rejected: Leave #36 open because the implementation already existed | keeps the immediate backlog inaccurate and invites duplicate work
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Reopen #36 only with a fresh repro showing a listed project skill still falls through to plain prompt handling on current main
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace
Not-tested: No interactive manual REPL session beyond the new bare-skill unit coverage
2026-04-11 22:50:28 +00:00
Yeachan-Heo a7b1fef176 Keep the rebased workspace green after the backlog closeout
The ROADMAP #38 closeout was rebased onto a moving main branch. That pulled in
new workspace files whose clippy/rustfmt fixes were required for the exact
verification gate the user asked for. This follow-up records those remaining
cleanups so the pushed branch matches the green tree that was actually tested.

Constraint: The user-required full-workspace fmt/clippy/test sequence had to stay green after rebasing onto newer origin/main
Rejected: Leave the rebase cleanup uncommitted locally | working tree would stay dirty and the pushed branch would not match the verified code
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When rebasing onto a moving main, commit any gate-fixing follow-up so pushed history matches the verified tree
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace
Not-tested: No additional behavior beyond the already-green verification sweep
2026-04-11 18:52:48 +00:00
YeonGyu-Kim 16b9febdae feat: ultraclaw droid batch — ROADMAP #41 test isolation + #50 PowerShell permissions
Merged late-arriving droid output from 10 parallel ultraclaw sessions.

ROADMAP #41 — Test isolation for plugin regression checks:
- Add test_isolation.rs module with env_lock() for test environment isolation
- Redirect HOME/XDG_CONFIG_HOME/XDG_DATA_HOME to unique temp dirs per test
- Prevent host ~/.claude/plugins/ from bleeding into test runs
- Auto-cleanup temp directories on drop via RAII pattern
- Tests: 39 plugin tests passing

ROADMAP #50 — PowerShell workspace-aware permissions:
- Add is_safe_powershell_command() for command-level permission analysis
- Add is_path_within_workspace() for workspace boundary validation
- Classify read-only vs write-requiring bash commands (60+ commands)
- Dynamic permission requirements based on command type and target path
- Tests: permission enforcer and workspace boundary tests passing

Additional improvements:
- runtime/src/permission_enforcer.rs: Dynamic permission enforcement layer
  - check_with_required_mode() for dynamically-determined permissions
  - 60+ read-only command patterns (cat, find, grep, cargo, git, jq, yq, etc.)
  - Workspace-path detection for safe commands
- compat-harness/src/lib.rs: Compat harness updates for permission testing
- rusty-claude-cli/src/main.rs: CLI integration for permission modes
- plugins/src/lib.rs: Updated imports for test isolation module

Total: +410 lines across 5 files
Workspace tests: 448+ passed
Droid source: ultraclaw-04-test-isolation, ultraclaw-08-powershell-permissions

Ultraclaw total: 4 ROADMAP items committed (38, 40, 41, 50)
2026-04-12 03:06:24 +09:00
Yeachan-Heo 124e8661ed Remove the deprecated Claude subscription login path and restore a green Rust workspace
ROADMAP #37 was still open even though several earlier backlog items were
already closed. This change removes the local login/logout surface, stops
startup auth resolution from treating saved OAuth credentials as a supported
path, and updates diagnostics/help to point users at ANTHROPIC_API_KEY or
ANTHROPIC_AUTH_TOKEN only.

While proving the change with the user-requested workspace gates, clippy
surfaced additional pre-existing warning failures across the Rust workspace.
Those were cleaned up in-place so the required `cargo fmt`, `cargo clippy
--workspace --all-targets -- -D warnings`, and `cargo test --workspace`
sequence now passes end to end.

Constraint: User explicitly required full-workspace fmt/clippy/test before commit/push
Constraint: Existing dirty leader worktree had to be stashed before attempted OMX team worktree launch
Rejected: Keep login/logout but hide them from help | left unsupported auth flow and saved OAuth fallback intact
Rejected: Stop after ROADMAP #37 targeted tests | did not satisfy required full-workspace verification gate
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Do not reintroduce saved OAuth as a silent Anthropic startup fallback without an explicit supported auth policy
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace
Not-tested: Remote push effects beyond origin/main update
2026-04-11 17:24:44 +00:00
Yeachan-Heo 61c01ff7da Prevent cross-worktree session bleed during managed session resume/load
ROADMAP #41 was still leaving a phantom-completion class open: managed
sessions could be resumed from the wrong workspace, and the CLI/runtime
paths were split between partially isolated storage and older helper
flows. This squashes the verified team work into one deliverable that
routes managed session operations through the per-worktree SessionStore,
rejects workspace mismatches explicitly, extends lane-event taxonomy for
workspace mismatch reporting, and updates the affected CLI regression
fixtures/docs so the new contract is enforced without losing same-
workspace legacy coverage.

Constraint: Keep same-workspace legacy flat sessions readable while blocking cross-worktree misuse
Constraint: No new dependencies; stay within the ROADMAP #41 changed-file scope
Rejected: Leave team auto-checkpoint history as final branch state | noisy/non-lore history for a single roadmap fix
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Preserve workspace_root validation on future resume/load helpers; do not reintroduce path-only fallback without equivalent mismatch checks
Tested: cargo test -p runtime session_control -- --nocapture; cargo test -p rusty-claude-cli resume -- --nocapture; cargo test -p rusty-claude-cli --test cli_flags_and_config_defaults; cargo test -p rusty-claude-cli --test output_format_contract; cargo test -p rusty-claude-cli --test resume_slash_commands; cargo test --workspace --exclude compat-harness; cargo check --workspace --all-targets; git diff --check
Not-tested: cargo clippy --workspace --all-targets -- -D warnings (pre-existing failures in unchanged rust/crates/rusty-claude-cli/build.rs)
Related: ROADMAP #41
2026-04-11 16:08:28 +00:00
YeonGyu-Kim 810036bf09 test(cli): add integration test for model persistence in resumed /status
New test: resumed_status_surfaces_persisted_model
- Creates session with model='claude-sonnet-4-6'
- Resumes with --output-format json /status
- Asserts model round-trips through session metadata

Resume integration tests: 11 → 12.
2026-04-10 10:31:05 +09:00
YeonGyu-Kim 0f34c66acd feat(session): persist model in session metadata — ROADMAP #59
Add 'model: Option<String>' to Session struct. The model used is now
saved in the session_meta JSONL record and surfaced in resumed /status:
- JSON mode: {model: 'claude-sonnet-4-6'} instead of null
- Text mode: shows actual model instead of 'restored-session'

Model is set in build_runtime_with_plugin_state() before the runtime
is constructed, and only when not already set (preserves model through
fork/resume cycles).

Backward compatible: old sessions without a model field load cleanly
with model: None (shown as null in JSON, 'restored-session' in text).

All workspace tests pass.
2026-04-10 10:05:42 +09:00
YeonGyu-Kim b95d330310 fix(startup): fall back to USERPROFILE when HOME is not set (Windows)
On Windows, HOME is often unset. The CLI crashed at startup with
'error: io error: HOME is not set' because three paths only checked
HOME:
- config_home_dir() in tools crate (config/settings loading)
- credentials_home_dir() in runtime crate (OAuth credentials)
- detect_broad_cwd() in CLI (CWD-is-home-dir check)
- skill lookup roots in tools crate

All now fall through to USERPROFILE when HOME is absent. Error message
updated to suggest USERPROFILE or CLAW_CONFIG_HOME on Windows.

Source: MaxDerVerpeilte in #claw-code (Windows user, 2026-04-10).
2026-04-10 08:33:35 +09:00
YeonGyu-Kim 74311cc511 test(cli): add 5 integration tests for resume JSON parity
New integration tests covering recent JSON parity work:
- resumed_version_command_emits_structured_json
- resumed_export_command_emits_structured_json
- resumed_help_command_emits_structured_json
- resumed_no_command_emits_restored_json
- resumed_stub_command_emits_not_implemented_json

Prevents regression on ROADMAP #54 (stub command error), #55 (session
list), #56 (--resume no-command JSON), #57 (session load errors).

Resume integration tests: 6 → 11.
2026-04-10 08:03:17 +09:00
YeonGyu-Kim 4f670e5513 fix(cli): emit JSON for --resume with no command in --output-format json mode
claw --output-format json --resume <session> (no command) was printing:
  'Restored session from <path> (N messages).'
to stdout as prose, regardless of output format.

Now emits:
  {"kind":"restored","session_id":"...","path":"...","message_count":N}

159 CLI tests pass.
2026-04-10 06:31:16 +09:00
YeonGyu-Kim 8dcf10361f fix(cli): implement /session list in resume mode — ROADMAP #21 partial
/session list previously returned 'unsupported resumed slash command' in
--output-format json --resume mode. It only reads the sessions directory
so does not need a live runtime session.

Adds a Session{action:"list"} arm in run_resume_command() before the
unsupported catchall. Emits:
  {kind:session_list, sessions:[...ids], active:<current-session-id>}

159 CLI tests pass.
2026-04-10 06:03:29 +09:00
YeonGyu-Kim cf129c8793 fix(cli): emit JSON error when session fails to load in --output-format json mode
'failed to restore session' errors from both the path-resolution step
and the JSONL-load step now check output_format and emit:
  {"type":"error","error":"failed to restore session: <detail>"}
instead of bare eprintln prose.

Covers: session not found, corrupt JSONL, permission errors.
2026-04-10 05:01:56 +09:00
YeonGyu-Kim c0248253ac fix(cli): remove 'stats' from STUB_COMMANDS — it is implemented
/stats was accidentally listed in STUB_COMMANDS (both in the original list
and overlooked in 1e14d59). Since SlashCommand::Stats is fully implemented
with REPL and resume dispatch, it should not be intercepted as unimplemented.

/tokens and /cache alias to Stats and were already working correctly.
/stats now works again in all modes.
2026-04-10 04:32:05 +09:00
YeonGyu-Kim 1e14d59a71 fix(cli): stop circular 'Did you mean /X?' for spec commands with no parse arm
23 spec-registered commands had no parse arm in validate_slash_command_input,
causing the circular error 'Unknown slash command: /X — Did you mean /X?'
when users typed them in --resume mode.

Two fixes:
1. Add the 23 confirmed parse-armless commands to STUB_COMMANDS (excluded
   from REPL completions and help output).
2. In resume dispatch, intercept STUB_COMMANDS before SlashCommand::parse
   and emit a clean '{error: "/X is not yet implemented in this build"}'
   instead of the confusing error from the Err parse path.

Affected: /allowed-tools, /bookmarks, /workspace, /reasoning, /budget,
/rate-limit, /changelog, /diagnostics, /metrics, /tool-details, /focus,
/unfocus, /pin, /unpin, /language, /profile, /max-tokens, /temperature,
/system-prompt, /notifications, /telemetry, /env, /project, plus ~40
additional unreachable spec names.

159 CLI tests pass.
2026-04-10 04:05:41 +09:00
YeonGyu-Kim 11e2353585 fix(cli): JSON parity for /export and /agents in resume mode
/export now emits: {kind:export, file:<path>, message_count:<n>}
/agents now emits: {kind:agents, text:<agents report>}

Previously both returned json:None and fell through to prose output even
in --output-format json --resume mode. 159 CLI tests pass.
2026-04-10 03:32:24 +09:00
YeonGyu-Kim 0845705639 fix(tests): update test assertions for null model in resume /status; drop unused import
Two integration tests expected 'model':'restored-session' in the /status
JSON output but dc4fa55 changed resume mode to emit null for model.
Updated both assertions to assert model is null (correct behavior).

Also remove unused 'estimate_session_tokens' import in compact.rs tests
(surfaced as warning in CI, kept failing CI green noise).

All workspace tests pass.
2026-04-10 03:21:58 +09:00
YeonGyu-Kim 316864227c fix(cli): JSON parity for /help and /diff in resume mode
/help now emits: {kind:help, text:<full help text>}
/diff now emits:
  - no git repo: {kind:diff, result:no_git_repo, detail:...}
  - clean tree:  {kind:diff, result:clean, staged:'', unstaged:''}
  - changes:     {kind:diff, result:changes, staged:..., unstaged:...}

Previously both returned json:None and fell through to prose output even
in --output-format json --resume mode. 159 CLI tests pass.
2026-04-10 03:02:00 +09:00
YeonGyu-Kim c8cac7cae8 fix(cli): doctor config check hides non-existent candidate paths
Before: doctor reported 'loaded 0/5' and listed 5 'Discovered file'
entries for paths that don't exist on disk. This looked like 5 files
failed to load, when in fact they are just standard search locations.

After: only paths that actually exist on disk are shown as 'Discovered
file'. 'loaded N/M' denominator is now the count of present files, not
candidate paths. With no config files present: 'loaded 0/0' +
'Discovered files  <none> (defaults active)'.

159 CLI tests pass.
2026-04-10 02:32:47 +09:00
YeonGyu-Kim dc4fa55d64 fix(cli): /status JSON emits null model and correct session_id in resume mode
Two bugs in --output-format json --resume /status:

1. 'model' field emitted 'restored-session' (a run-mode label) instead of
   the actual model or null. Fixed: status_json_value now takes Option<&str>
   for model; resume path passes None; live REPL path passes Some(model).

2. 'session_id' extracted parent dir name ('sessions') instead of the file
   stem. Session files are session-<id>.jsonl directly under .claw/sessions/,
   not in a subdirectory. Fixed: extract file_stem() instead of
   parent().file_name().

159 CLI tests pass.
2026-04-10 02:03:14 +09:00
YeonGyu-Kim 78dca71f3f fix(cli): JSON parity for /compact and /clear in resume mode
/compact now emits: {kind:compact, skipped, removed_messages, kept_messages}
/clear now emits:  {kind:clear, previous_session_id, new_session_id, backup, session_file}
/clear (no --confirm) now emits: {kind:error, error:..., hint:...}

Previously both returned json:None and fell through to prose output even
in --output-format json --resume mode. 159 CLI tests pass.
2026-04-10 01:31:21 +09:00
YeonGyu-Kim d95149b347 fix(cli): surface resolved path in dump-manifests error — ROADMAP #45 partial
Before:
  error: failed to extract manifests: No such file or directory (os error 2)

After:
  error: failed to extract manifests: No such file or directory (os error 2)
    looked in: /Users/yeongyu/clawd/claw-code/rust

The workspace_dir is computed from CARGO_MANIFEST_DIR at compile time and
only resolves correctly when running from the build tree. Surfacing the
resolved path lets users understand immediately why it fails outside the
build context.

ROADMAP #45 root cause (build-tree-only path) remains open.
2026-04-10 01:01:53 +09:00
YeonGyu-Kim 47aa1a57ca fix(cli): surface command name in 'not yet implemented' REPL message
Add SlashCommand::slash_name() to the commands crate — returns the
canonical '/name' string for any variant. Used in the REPL's stub
catch-all arm to surface which command was typed instead of printing
the opaque 'Command registered but not yet implemented.'

Before: typing /rewind → 'Command registered but not yet implemented.'
After:  typing /rewind → '/rewind is not yet implemented in this build.'

Also update the compacts_sessions_via_slash_command test assertion to
tolerate the boundary-guard fix from 6e301c8 (removed_message_count
can be 1 or 2 depending on whether the boundary falls on a tool-result
pair). All 159 CLI + 431 runtime + 115 api tests pass.
2026-04-10 00:39:16 +09:00
YeonGyu-Kim 6e301c8bb3 fix(runtime): prevent orphaned tool-result at compaction boundary; /cost JSON
Two fixes:

1. compact.rs: When the compaction boundary falls at the start of a
   tool-result turn, the preceding assistant turn with ToolUse would be
   removed — leaving an orphaned role:tool message with no preceding
   assistant tool_calls. OpenAI-compat backends reject this with 400.

   Fix: after computing raw_keep_from, walk the boundary back until the
   first preserved message is not a ToolResult (or its preceding assistant
   has been included). Regression test added:
   compaction_does_not_split_tool_use_tool_result_pair.

   Source: gaebal-gajae multi-turn tool-call 400 repro 2026-04-09.

2. /cost resume: add JSON output:
   {kind:cost, input_tokens, output_tokens, cache_creation_input_tokens,
    cache_read_input_tokens, total_tokens}

159 CLI + 431 runtime tests pass. Fmt clean.
2026-04-10 00:13:45 +09:00