Add working-directory commands to the TUI (#38894)

## What changed

- Add `/cd [path]` for changing an idle local session's working directory while preserving conversation history. Relative paths resolve from the current directory, and omitting the path selects `~`.
- Reload project configuration, instructions, permissions, keybindings, file search, and hooks for the destination before replacing the active session.
- Reject unsafe transitions, including active or queued work, background terminals, remote environments, untrusted destinations, and incompatible permission profiles.
- Add `/pwd` and its `/cwd` alias to display the current working directory, and discard asynchronous results left over from the previous directory.

## Testing

- Cover command parsing, aliases, default paths, session eligibility, transition failures, history preservation, destination policy loading, hooks, and stale events.

GitOrigin-RevId: 1a987c029f157a087ba02c0f26e022260409132b
This commit is contained in:
Benjamin Carlsson authored and copyberry committed 2026-08-16 19:48:07 +00:00
1 parent 375996d3f5
commit ed32158e90
18 files changed
+776 -24

No files matched your search

+20 -2
View File
@@ -138,7 +138,7 @@ impl ChatWidget {
|| (self.bottom_pane.is_task_running()
&& (self.mcp_startup_status.is_none()
|| self.input_queue.user_turn_pending_start))))
|| (cmd == SlashCommand::Resume
|| (matches!(cmd, SlashCommand::Resume | SlashCommand::Cd)
&& (self.input_queue.user_turn_pending_start
|| self.turn_lifecycle.agent_turn_running))
|| (cmd == SlashCommand::Export && self.input_queue.suppress_queue_autosend)
@@ -430,7 +430,7 @@ impl ChatWidget {
None => "Failed to compute diff: workspace command runner unavailable"
.to_string(),
};
tx.send(AppEvent::DiffResult(text));
tx.send(AppEvent::DiffResult(cwd, text));
});
}
SlashCommand::Mention => {
@@ -461,6 +461,15 @@ impl ChatWidget {
);
}
}
SlashCommand::Cd => {
self.dispatch_command_with_args(SlashCommand::Cd, "~".to_string(), Vec::new());
}
SlashCommand::Pwd => {
self.add_info_message(
format!("Current working directory: {}", self.config.cwd.display()),
/*hint*/ None,
);
}
SlashCommand::Usage => {
if self.ensure_usage_command_available() {
self.open_usage_menu();
@@ -694,6 +703,10 @@ impl ChatWidget {
),
});
}
SlashCommand::Cd => self.request_working_directory_change(trimmed),
SlashCommand::Pwd => {
self.add_error_message("Usage: /pwd".to_string());
}
SlashCommand::Usage => {
if self.ensure_usage_command_available() {
match tokens::TokenActivityView::parse(trimmed) {
@@ -1085,6 +1098,7 @@ impl ChatWidget {
match cmd {
SlashCommand::Ide
| SlashCommand::Status
| SlashCommand::Pwd
| SlashCommand::Usage
| SlashCommand::DebugConfig
| SlashCommand::Ps
@@ -1102,6 +1116,10 @@ impl ChatWidget {
| SlashCommand::App
| SlashCommand::Rename
| SlashCommand::TestApproval => QueueDrain::Continue,
SlashCommand::Cd => match self.thread_id {
Some(thread_id) if self.can_change_working_directory(thread_id) => QueueDrain::Stop,
_ => QueueDrain::Continue,
},
SlashCommand::Feedback
| SlashCommand::Export
| SlashCommand::New