diff --git a/frontend/src/renderer/components/CenterPane.tsx b/frontend/src/renderer/components/CenterPane.tsx index 96c058e4..613ca2dd 100644 --- a/frontend/src/renderer/components/CenterPane.tsx +++ b/frontend/src/renderer/components/CenterPane.tsx @@ -1,4 +1,6 @@ +import { ChevronLeft, Shield } from "lucide-react"; import type { Theme } from "../stores/ui-store"; +import type { TerminalTarget } from "../types/terminal"; import type { WorkspaceSession } from "../types/workspace"; import { TerminalPane } from "./TerminalPane"; @@ -6,13 +8,35 @@ type CenterPaneProps = { session?: WorkspaceSession; theme: Theme; daemonReady: boolean; + terminalTarget?: TerminalTarget; + onSelectWorkerTerminal?: () => void; }; -export function CenterPane({ session, theme, daemonReady }: CenterPaneProps) { +export function CenterPane({ session, theme, daemonReady, terminalTarget, onSelectWorkerTerminal }: CenterPaneProps) { + const target = terminalTarget ?? { kind: "worker" }; + return (
+ {target.kind === "reviewer" ? ( +
+ + + + {target.harness} +
+ ) : null}
- +
); diff --git a/frontend/src/renderer/components/ProjectSettingsForm.test.tsx b/frontend/src/renderer/components/ProjectSettingsForm.test.tsx index b37c18ca..4e387c98 100644 --- a/frontend/src/renderer/components/ProjectSettingsForm.test.tsx +++ b/frontend/src/renderer/components/ProjectSettingsForm.test.tsx @@ -77,6 +77,7 @@ describe("ProjectSettingsForm", () => { model: "claude-opus-4-5", permissions: "auto", }, + reviewers: [{ harness: "claude-code" }], }, }, }, @@ -93,9 +94,11 @@ describe("ProjectSettingsForm", () => { const workerAgent = screen.getByRole("combobox", { name: "Default worker agent" }); const orchestratorAgent = screen.getByRole("combobox", { name: "Default orchestrator agent" }); const permissionMode = screen.getByRole("combobox", { name: "Permission mode" }); + const reviewerAgent = screen.getByRole("combobox", { name: "Default reviewer agent" }); expect(workerAgent).toHaveTextContent("codex"); expect(orchestratorAgent).toHaveTextContent("claude-code"); expect(permissionMode).toHaveTextContent("Auto"); + expect(reviewerAgent).toHaveTextContent("claude-code"); await userEvent.clear(screen.getByLabelText("Default branch")); await userEvent.type(screen.getByLabelText("Default branch"), "release"); @@ -128,11 +131,12 @@ describe("ProjectSettingsForm", () => { model: "gpt-5-codex", permissions: "bypass-permissions", }, + reviewers: [{ harness: "claude-code" }], }, }, }); expect(await screen.findByText("Saved.")).toBeInTheDocument(); - }); + }, 10_000); it("shows the daemon validation message when save fails", async () => { getMock.mockResolvedValue({ diff --git a/frontend/src/renderer/components/ProjectSettingsForm.tsx b/frontend/src/renderer/components/ProjectSettingsForm.tsx index a2ed7a48..673204e1 100644 --- a/frontend/src/renderer/components/ProjectSettingsForm.tsx +++ b/frontend/src/renderer/components/ProjectSettingsForm.tsx @@ -22,6 +22,8 @@ const PERMISSION_MODE_OPTIONS = [ { value: "bypass-permissions", label: "Bypass permissions" }, ] as const; +const REVIEWER_OPTIONS = ["claude-code"] as const; + const projectQueryKey = (id: string) => ["project", id] as const; export function ProjectSettingsForm({ projectId }: { projectId: string }) { @@ -73,6 +75,7 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje orchestratorAgent: config.orchestrator?.agent ?? "", model: config.agentConfig?.model ?? "", permissions: config.agentConfig?.permissions ?? "", + reviewerHarness: config.reviewers?.[0]?.harness ?? "", }); const [savedAt, setSavedAt] = useState(null); @@ -91,6 +94,7 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje model: form.model || undefined, permissions: form.permissions || undefined, }), + reviewers: form.reviewerHarness ? [{ harness: form.reviewerHarness }] : undefined, }; const { error } = await apiClient.PUT("/api/v1/projects/{id}/config", { params: { path: { id: projectId } }, @@ -188,6 +192,21 @@ function SettingsBody({ project, projectId, onSaved }: { project: Project; proje + + + Reviewers + + + + setForm((f) => ({ ...f, reviewerHarness: v }))} + /> + + + +
+ {review ? ( + + ) : null} +
+ + ); +} + +function reviewStatus(review?: ReviewRun): { + label: string; + tone: "neutral" | "running" | "success" | "danger"; + icon: ReactNode; +} { + if (!review) return { label: "Not run", tone: "neutral", icon: null }; + if (review.status === "running") { + return { label: "Running", tone: "running", icon: