From cb027adf650e0753b2cd0314b98bfa3f952845f3 Mon Sep 17 00:00:00 2001 From: bellman Date: Fri, 5 Jun 2026 09:42:16 +0900 Subject: [PATCH] fix: /session switch and /session fork return structured JSON in resume mode (#113) /session switch and /session fork now return structured JSON with kind:error, error_kind:unsupported_resumed_command, and actionable hint instead of a raw error string that resume callers couldn't parse. Generated with https://github.com/Yeachan-Heo/gajae-code Co-authored-by: Gajae Code --- ROADMAP.md | 2 +- rust/crates/rusty-claude-cli/src/main.rs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 23b99aab..08132793 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3474,7 +3474,7 @@ ear], /color [scheme], /effort [low|medium|high], /fast, /summary, /tag [label], **Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdII` on main HEAD `a049bd2` in response to Clawhip pinpoint nudge at `1494880177099116586`. Joins **truth-audit / diagnostic-integrity** (#80–#87, #89, #100, #102, #103, #105, #107, #109, #110) — the error message lies about what actually happened (file vanished via concurrent rename, not intrinsic absence). Joins **Session handling** as a new micro-cluster (only existing member was #93 — reference-resolution semantics). Natural bundle: **#93 + #112** — session semantic correctness (reference resolution + concurrent-modification error clarity). Adjacent to **#104** (two-paths-diverge export) on the session-file-handling axis: #104 says the two export paths disagree on filename; #112 says concurrent session-file writers race with no advisory lock. Together session-handling has filename-semantic + concurrency gaps that the test suite should cover. Session tally: ROADMAP #112. -113. **`/session switch`, `/session fork`, and `/session delete` are registered by the parser (produce `SlashCommand::Session { action, target }`), documented in `--help` as first-class session-management verbs, but dispatch in `run_resume_command` implements ONLY `/session list` with a dedicated handler at `main.rs:2908` — every other `Session { .. }` variant falls through to the "unsupported resumed slash command" bucket at `main.rs:2936`. There is also no `claw session ` CLI subcommand: `claw session delete s` falls through to Prompt dispatch per #108. Net effect: claws can enumerate sessions via `/session list`, but CANNOT programmatically switch, fork, or delete — those are REPL-interactive only, with no `--output-format json`-compatible alternative and no `claw session ...` CLI equivalent. Help advertises the capability universally; implementation surfaces it only in the REPL** — dogfooded 2026-04-18 on main HEAD `8b25daf` from `/tmp/cdJJ`. Full test matrix: `/session list` works from `--resume` (returns structured JSON), `/session switch s` / `/session fork foo` / `/session delete s` / `/session delete s --force` all return `{"type":"error","error":"unsupported resumed slash command"}`. +113. **DONE — `/session switch`, `/session fork`, and `/session delete` are registered by the parser (produce `SlashCommand::Session { action, target }`), documented in `--help` as first-class session-management verbs, but dispatch in `run_resume_command` implements ONLY `/session list` with a dedicated handler at `main.rs:2908` — every other `Session { .. }` variant falls through to the "unsupported resumed slash command" bucket at `main.rs:2936`. There is also no `claw session ` CLI subcommand: `claw session delete s` falls through to Prompt dispatch per #108. Net effect: claws can enumerate sessions via `/session list`, but CANNOT programmatically switch, fork, or delete — those are REPL-interactive only, with no `--output-format json`-compatible alternative and no `claw session ...` CLI equivalent. Help advertises the capability universally; implementation surfaces it only in the REPL** — dogfooded 2026-04-18 on main HEAD `8b25daf` from `/tmp/cdJJ`. Full test matrix: `/session list` works from `--resume` (returns structured JSON), `/session switch s` / `/session fork foo` / `/session delete s` / `/session delete s --force` all return `{"type":"error","error":"unsupported resumed slash command"}`. **Concrete repro.** ``` diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index c6ec805c..7e17a6f0 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -9242,7 +9242,23 @@ fn run_resumed_session_command( })), }) } - Some("switch" | "fork") => Err("unsupported_resumed_command: /session switch and /session fork require an interactive REPL.\nUsage: claw (then /session switch ) or claw --resume ".into()), + // #113: /session switch and /session fork require an interactive REPL — + // return structured JSON instead of a raw error so resume callers can + // detect the limitation programmatically. + Some(switch_or_fork @ ("switch" | "fork")) => Ok(ResumeCommandOutcome { + session: session.clone(), + message: Some(format!( + "/session {switch_or_fork} requires an interactive REPL.\nUsage: claw (then /session {switch_or_fork} )" + )), + json: Some(serde_json::json!({ + "kind": "error", + "error_kind": "unsupported_resumed_command", + "status": "error", + "action": switch_or_fork, + "error": format!("/session {switch_or_fork} requires an interactive REPL"), + "hint": format!("Start a new claw session and use /session {switch_or_fork} interactively"), + })), + }), Some(other) => Err(format!("unsupported_resumed_command: /session {other} is not supported in resume mode.\nSupported: list, exists, delete").into()), } }