Repository navigation
Offer one-click fixes for build, test and merge failures in terminal output - #1625
Conversation
Solenta-Message-Id: 7114291a-ea93-4ab8-b38e-a9570ad6b765
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d3cbc60fc0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (action === "fork") { | ||
| const t = await forkThread(threadId, { worktree: true, select: false }); | ||
| if (!t) return; |
There was a problem hiding this comment.
Preserve the failing checkout when forking fixes
When the detected failure depends on committed or uncommitted changes in the source thread's worktree, this creates a fresh pending worktree without carrying the source branch or a snapshot. forkThread only records handoffFrom, while worktree materialization starts the new thread from its normal base, so merge conflicts disappear and build/test failures caused by the source changes cannot be reproduced or fixed. The fork action needs to start from the failing checkout's snapshot rather than a clean base.
Useful? React with 👍 / 👎.
| const [handledFixes, setHandledFixes] = useState<string[]>([]); | ||
| const devOutput = onFix ? plainText((state.lastLines ?? []).join("\n")) : ""; | ||
| const found = onFix ? detectFailure(devOutput) : null; | ||
| const fixFailure = found && !handledFixes.includes(failureKey(found)) ? found : null; |
There was a problem hiding this comment.
Clear dev-server fix state when changing threads
When the inspector switches directly between two non-null threads, the load effect leaves state.lastLines from the old thread in place until both async requests for the new thread resolve. During that interval this computes a card from the old output but renders it with the new threadId, so clicking Fix, Edit, or Fork applies thread A's failure prompt to thread B. Reset the output synchronously on a thread change or associate the state with the thread that produced it.
Useful? React with 👍 / 👎.
| threadId={detail?.thread.id ?? null} | ||
| api={terminalApi} | ||
| reveal={terminalReveal} | ||
| onFix={onFixSuggestion} |
There was a problem hiding this comment.
Disable fix actions on archived threads
When an archived thread is opened, its composer is deliberately hard-disabled, but the terminal still receives the unrestricted fix handler here; the dev-server card is wired the same way. A detected failure therefore exposes Fix/Edit/Fork, and Fix calls startRun directly—even though the backend rejects deleted threads but does not reject archived ones—allowing a new agent run to start in an archived thread. Gate the actions by isArchived or enforce the archive check in the handler.
Useful? React with 👍 / 👎.
# Conflicts: # src/App.tsx # src/components/Composer.tsx # src/components/ThreadView.tsx
Closes #253.
When a thread's terminal or dev server prints a build error, a failing test or a merge conflict, a small card now appears where the failure happened. The card turns the failure into a ready-made prompt, so the user doesn't have to copy the output into the composer by hand.
What changed
Detection (
src/fixSuggest.ts)plainText()strips ANSI escapes and applies\roverwrites, so the matching runs on what a reader actually sees.detectFailure()checks the output against a fixed list of line regexes for common toolchains:Automatic merge failedandcould not applyerror TS…, rustcerror[E…], C-stylefile:line:col: error:, esbuild✘ [ERROR], vite internal errors,Failed to compileandModule not foundFAILandTests: N failed, node:testfail N, mochaN failing, Go--- FAIL:, cargotest result: FAILEDand pytestFAILEDThe first matching rule wins, so a conflict beats the build error it causes, and a build error beats the tests it breaks.
failureKey()removes digits from the matched line. A rerun that reports different line numbers or timings counts as the same failure, so a dismissed card doesn't come back.fixPrompt()builds a self-contained prompt: a block of up to 40 lines / 3000 chars taken around the last occurrence of the failing line, followed by an instruction for that kind of failure. It has to be self-contained because a forked thread only gets a digest of the parent thread.UI (
src/components/FixSuggestion.tsx+ CSS)Where the card appears
TerminalPane: only live output is scanned; replayed scrollback is ignored. The pane keeps the last 16k characters of plain output. Each new chunk is checked together with a 300-character overlap, so a failing line split across two chunks is still caught. A failure that was dismissed or acted on stays hidden for as long as that terminal view is mounted.DevServerCard(AgentsPanel): scans the polledlastLinestail. The card shows while the failure is in that tail, until the user dismisses or acts on it.Wiring
useSuggestionHandlersgets a newhandleFixSuggestion. "Draft" goes through the existing restore-draft path. "Fork" callsforkThread(worktree, not selected) and thenstartRun. "Run" callsstartRunon the current thread.restoreDraftgets anappendflag. Normally a restored draft only lands in an empty composer. Withappend, the fix prompt is added after the in-progress draft, separated by a blank line, instead of being dropped.onFixSuggestionis passed down throughApp→ThreadView→TerminalPaneand throughApp→AgentsPanel→GitTab→DevServerCard. If the prop is absent, no cards are shown.Tests
test/fixSuggest.test.ts: escape stripping, detection and rule order, key normalization, and the prompt excerpt.test/terminalPane.test.tsxandtest/devServer.test.tsx: the card appears for failures and the actions/dismiss behave correctly.test/composer.test.tsx:appendadds the prompt after an existing draft.🤖 Generated with Claude Code