feat(tui): route /diff through workspace commands (#21001)

Stacked on #20892.

## Why

#20892 adds the TUI workspace command abstraction so branch status
metadata can run through app-server instead of assuming the CLI process
has the active workspace locally. `/diff` still used direct local
process execution, which means remote app-server sessions could compute
the diff against the wrong machine or fail to see the active workspace
at all.

This PR moves `/diff` onto that same app-server-backed command path so
Git runs wherever the active workspace lives.

## What Changed

- Route `/diff` through the TUI `WorkspaceCommandExecutor` using the
active chat cwd.
- Replace direct `tokio::process::Command` usage in `get_git_diff` with
argv-based workspace command requests.
- Preserve the existing `/diff` behavior: tracked diff output, untracked
file diffs, treating Git diff exit code `1` as success, and showing the
existing non-git-repository message.
- Extend `WorkspaceCommand` with caller-set timeouts and an explicit
uncapped-output opt-out. Metadata probes remain capped by default;
`/diff` opts out because its full output is the user-visible payload.

## How to Test

Manual reviewer path:

1. Start the Codex TUI from a Git worktree with one tracked file change
and one untracked file.
2. Run `/diff`.
3. Confirm the rendered diff includes both the tracked diff and the
untracked file diff.
4. Start the TUI outside a Git worktree, or switch to a non-git cwd,
then run `/diff`.
5. Confirm it shows the existing `/diff` not-inside-a-git-repository
message.

Targeted tests run:

- `cargo test -p codex-tui get_git_diff -- --nocapture`
- `cargo test -p codex-tui branch_summary -- --nocapture`
- `cargo test -p codex-tui`
This commit is contained in:
Felipe Coury
2026-05-05 17:09:25 -03:00
committed by GitHub
Unverified
parent 9e0c191c13
commit 52fbbe7cdd
3 changed files with 322 additions and 77 deletions
+17 -8
View File
@@ -328,16 +328,25 @@ impl ChatWidget {
SlashCommand::Diff => {
self.add_diff_in_progress();
let tx = self.app_event_tx.clone();
let runner = self.workspace_command_runner.clone();
let cwd = self
.current_cwd
.clone()
.unwrap_or_else(|| self.config.cwd.to_path_buf());
tokio::spawn(async move {
let text = match get_git_diff().await {
Ok((is_git_repo, diff_text)) => {
if is_git_repo {
diff_text
} else {
"`/diff` — _not inside a git repository_".to_string()
let text = match runner {
Some(runner) => match get_git_diff(runner.as_ref(), &cwd).await {
Ok((is_git_repo, diff_text)) => {
if is_git_repo {
diff_text
} else {
"`/diff` — _not inside a git repository_".to_string()
}
}
}
Err(e) => format!("Failed to compute diff: {e}"),
Err(e) => format!("Failed to compute diff: {e}"),
},
None => "Failed to compute diff: workspace command runner unavailable"
.to_string(),
};
tx.send(AppEvent::DiffResult(text));
});