feat(cli): add /cd slash command for in-session workspace switch#1316
Open
jarvis24young wants to merge 1 commit into
Open
feat(cli): add /cd slash command for in-session workspace switch#1316jarvis24young wants to merge 1 commit into
jarvis24young wants to merge 1 commit into
Conversation
b57aa65 to
8a14ed9
Compare
Adds a `/cd <path>` slash command to the CLI chat mode that re-points the active session's working directory without restarting it. The session id, message history, and prompt-cache prefix are preserved; subsequent turns simply execute against the new cwd. Ported from Claude Code's `/cd` semantics (changelog 2.1.169). The key design choice is splitting "logical cwd" from "persistence anchor": - `SessionConfig.workspace_path` (existing) continues to drive tool execution and `build_workspace_binding` — so subsequent turns run in the new cwd. - A new `SessionConfig.storage_workspace_path` (optional, serde-default `None`, backward-compatible) freezes the original workspace on the first `/cd`. Storage helpers (`CoreSessionStorePort::resolve_storage_path_for_config`, `SessionManager::effective_session_workspace_path`) prefer this anchor so the session file, turn snapshots, and prompt cache stay rooted at the original directory and remain discoverable from its workspace listing. - `start_dialog_turn_internal` resolves its fallback finalize storage path via `effective_session_workspace_path` instead of the live workspace binding, so safety-net turn persistence honors the anchor. - Forked subagents reset `storage_workspace_path = None` so a child session's anchor follows its own (current) workspace, not the parent's post-`/cd` history. CLI resolution handles `~`/`~/...` expansion, relative paths joined against the current cwd, ASCII quote stripping, and rejects Windows drive-relative forms (`C:foo`) that would otherwise be silently anchored to the shell's per-drive cwd. The core update runs before the adapter mutation so a core-side failure leaves session state internally consistent. Switching is rejected while a turn is in flight. Pre-`/cd` sessions and existing on-disk session files are unaffected: `storage_workspace_path` deserializes to `None` and the helper falls back to `workspace_path`, preserving prior behavior byte-for-byte.
8a14ed9 to
56b6bc6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
/cd <path>slash command to the CLI chat mode that re-points the active session's working directory without restarting the session. The session id, message history, and prompt-cache prefix are preserved; subsequent turns simply execute against the new cwd.Ported from Claude Code's
/cdsemantics (changelog 2.1.169).Fixes # (open — please link if there is a tracking issue)
Type and Areas
Motivation / Impact
Today, switching the working directory in a CLI session requires
/clear+ restart (losing prompt cache) or relaunching the binary with a different--workspaceflag./cdlets users pivot context mid-session — common when an agent task spans multiple repos, or when the user realizes halfway through that the wrong directory was opened.Key design choice: logical cwd is split from persistence anchor.
SessionConfig.workspace_path(existing) continues to drive tool execution —build_workspace_bindingreads it, so subsequent turns run in the new cwd.SessionConfig.storage_workspace_path(optional, serde-defaultNone, backward-compatible) freezes the original workspace on the first/cd. Storage helpers prefer this anchor, so the session file, turn snapshots, and prompt cache stay rooted at the original directory and remain discoverable from its workspace listing.This matches the documented
/cdsemantics: prompt-cache prefix and session id stay intact, and the session stays listed under its original workspace (UX tradeoff called out in Reviewer Notes).Verification
Build + tests on top of
origin/main(commitb57aa652):New unit tests:
commands::tests::test_cd_registered—/cdis in COMMAND_SPECSmodes::chat::cd_helper_tests::*(10 tests) — quote stripping, tilde expansion, relative/absolute resolution, Windows drive-relative rejectionManual sanity (CLI):
/cdwith no arg prints current workspace;/cd ~/projectsswitches;/cd nonexistentreports an error and leaves state untouched;/cdduring a processing turn is rejected.Reviewer Notes
Design walkthrough
SessionManager::update_session_workspace_pathis the storage-side entry point. On first invocation it freezes the existing non-emptyworkspace_pathintostorage_workspace_path. Subsequent calls only mutateworkspace_path. The in-memory update is followed by a persistence save routed througheffective_session_workspace_path(which now prefers the storage anchor).ConversationCoordinator::update_session_workspace_pathis a thin pass-through with empty-path validation.start_dialog_turn_internalresolves its fallback finalize storage path viaeffective_session_workspace_pathinstead ofsession_workspace.session_storage_path(), so safety-net turn persistence honors the anchor. The livesession_workspace(used for tool execution) still readsconfig.workspace_path— that is the bug fix's whole point.fork_agent::build_child_session_configresetsstorage_workspace_path = Noneso child subagent sessions get their own anchor (rather than inheriting the parent's post-/cdhistory).Backward compatibility
storage_workspace_pathis#[serde(default, skip_serializing_if = "Option::is_none")]. Old session files on disk deserialize toNoneand the storage helper falls back toworkspace_path, byte-for-byte preserving prior behavior.SessionConfigdoes not setdeny_unknown_fields, so older clients reading newer files also work.Known UX tradeoff (by design)
After
/cd /new_dir, the session remains listed under its original workspace directory (so it stays discoverable where the user opened it). It will not appear in/new_dir's session list. This matches Claude Code's behavior and preserves prompt-cache locality. A dual-listing/index-relocation feature is a possible follow-up but is intentionally out of scope here.Review process
The implementation went through 4 rounds of independent review by Codex (GPT-5). Round 1 surfaced that the CLI-only fix was insufficient because
start_dialog_turn_internalbuildsWorkspaceBindingfromsession.config.workspace_path, not the passed-inworkspace_pathparameter — so tool calls would still land in the old cwd. Round 2 caught that turn snapshots and prompt cache writes would diverge from the session file location after/cd, plus a partial-failure inconsistency in the CLI. Rounds 3 and 4 confirmed the split-anchor design closes those issues and validated subagent / remote / serde-compat edge cases. All findings are resolved; the design notes above encode the constraints learned from that process.Checklist