Conversation
Enable a Claude Code agent to programmatically create and orchestrate other tasks in Parallel Code. Adds an MCP server with 9 tools (create_task, send_prompt, wait_for_idle, get_task_diff, merge_task, etc.), a main-process orchestrator, REST API endpoints, and UI support including coordinator mode toggle, sub-task status strip, and "via Coordinator" sidebar labels. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@johannesjo I tried to bring it up to the same commit as Main, looks like I still ran into the npm version issue and a few more dangling conflicts. You should be able to run this branch - let me know if you want me to update it to fix these last conflicts. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a “coordinating agent” mode that can orchestrate sub-tasks via an MCP server backed by new remote REST endpoints and a main-process orchestrator, alongside UI/store refactors to support coordinator tasks, nested sidebar rendering, and additional isolation/runtime options.
Changes:
- Add MCP server + main-process orchestrator + remote REST endpoints to programmatically create/monitor/merge/close tasks.
- Update task model/persistence/UI to support coordinator tasks (sub-task strip, sidebar nesting/labels) and new git isolation/base-branch plumbing.
- Refactor and modernize multiple UI/store utilities (task panel components consolidation, diff viewer improvements, notifications, font loading).
Reviewed changes
Copilot reviewed 65 out of 67 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/store/ui.ts | Store setters updated (terminal font as string) and docker-related UI state setters added. |
| src/store/types.ts | Expand task/project/persisted types (git isolation, base branch, coordinator metadata, docker fields, MCP status). |
| src/store/terminals.ts | Remove window title updates from terminal lifecycle code paths. |
| src/store/tasks.ts | Unify task creation for worktree/direct, coordinator MCP integration, plan-watcher cleanup, and child-task unparenting. |
| src/store/store.ts | Export updated store APIs (remove direct task APIs, add MCP + docker setters/status). |
| src/store/sidebar-order.ts | Add coordinator child grouping and filter children from flat sidebar list. |
| src/store/projects.ts | Add git-repo validation on project add/relink; add default isolation/base-branch project settings. |
| src/store/persistence.ts | Persist new task fields (git isolation/base branch/coordinator/docker) and migrate legacy direct-mode state. |
| src/store/navigation.ts | Remove title updates; re-focus moved tasks after reordering. |
| src/store/mcpStatus.ts | Add polling for MCP/remote connectivity status. |
| src/store/focus.ts | Refactor adjacent-task focus navigation logic. |
| src/store/desktopNotifications.ts | Improve debouncing/deduplication and route notifications through IPC helper. |
| src/store/core.ts | Add docker/MCP defaults; adjust cleanup of per-task persisted UI state. |
| src/store/coordinator-preamble.ts | Add coordinator-mode system preamble injected into initial prompt. |
| src/store/autosave.ts | Update autosave snapshot schema for git isolation/base branch and notification setting. |
| src/lib/theme.ts | Add shared banner/section label styles. |
| src/lib/terminalFitManager.ts | Simplify terminal registration (no longer stores Terminal instance). |
| src/lib/ipc.ts | Add Channel.dispose(); tighten renderer IPC surface. |
| src/lib/fonts.ts | Switch to string font names and add async system font enumeration (IPC + canvas fallback). |
| src/lib/focus-registration.ts | Add helper hook to register/unregister focus functions automatically. |
| src/components/TilingLayout.tsx | Layout polish + git isolation-aware close messaging. |
| src/components/TerminalView.tsx | Pass docker flags to backend spawn and update terminal fit registration. |
| src/components/TaskTitleBar.tsx | New consolidated title bar UI with badges (direct branch, docker) and actions. |
| src/components/TaskShellSection.tsx | New consolidated shell toolbar + multi-shell rendering and focus management. |
| src/components/TaskNotesPanel.tsx | New notes/plan + changed-files split panel and keyboard navigation. |
| src/components/TaskClosingOverlay.tsx | New overlay shown during close/error states. |
| src/components/TaskBranchInfoBar.tsx | New branch/project/path bar with editor/reveal and GitHub link support. |
| src/components/TaskAITerminal.tsx | New consolidated AI terminal panel with restart/switch-agent UI. |
| src/components/SubTaskStrip.tsx | Add coordinator sub-task status chips (clickable navigation). |
| src/components/SidebarFooter.tsx | Display MCP connectivity when coordinator tasks exist; start/stop polling. |
| src/components/SettingsDialog.tsx | Async system font fetching; add docker image setting section; use shared section label style. |
| src/components/SegmentedButtons.tsx | (Re)introduce segmented button component used by project isolation settings. |
| src/components/ScrollingDiffView.tsx | Add base-branch support, improve gaps/expansion, and search highlighting styles. |
| src/components/PushDialog.tsx | Use shared banner styling for errors. |
| src/components/PromptInput.tsx | Improve auto-send stability checks; handle agent exit; reduce marker scan magic numbers. |
| src/components/MergeDialog.tsx | Thread base-branch through merge/rebase/status flows; use shared banners; simplify cancel reset. |
| src/components/EditProjectDialog.tsx | Add default git isolation + base branch settings and update styling. |
| src/components/DiffViewerDialog.tsx | Thread base-branch through diff fetching and rendering. |
| src/components/ConnectPhoneModal.tsx | Migrate modal to shared Dialog component and make QR import more robust. |
| src/components/CloseTaskDialog.tsx | Git isolation-aware messaging + coordinator orphan-warning; use shared banners for warnings/errors. |
| src/components/ChangedFilesList.tsx | Add base-branch support and keyboard/selection scroll behavior improvements. |
| src/App.tsx | Initialize MCP listeners and docker availability; add defensive IPC payload check; adjust shortcut scope. |
| package.json | Add MCP SDK + esbuild bundle step; package MCP server/docker resources; adjust build identity. |
| electron/remote/server.ts | Add orchestrator REST API routes + MCP log ring buffer. |
| electron/preload.cjs | Expand allowed IPC channels; remove ipcRenderer.send exposure. |
| electron/mcp/types.ts | Define shared orchestrator/MCP tool and API types. |
| electron/mcp/server.ts | Add standalone MCP stdio server that delegates to Electron via HTTP. |
| electron/mcp/prompt-detect.ts | Shared prompt detection helpers for renderer/main process. |
| electron/mcp/orchestrator.ts | Implement main-process task lifecycle/orchestration (PTY + git integration). |
| electron/mcp/client.ts | HTTP client used by MCP server process to call remote API. |
| electron/ipc/tasks.ts | Add base-branch support and change deleteTask signature; improve branch naming uniqueness. |
| electron/ipc/system-fonts.ts | Add fc-list based monospace font discovery with caching. |
| electron/ipc/register.ts | Register new IPC handlers (docker, MCP, system fonts, git checks) and refactor window event throttling. |
| electron/ipc/pty.ts | Add Docker mode execution path, container lifecycle management, and docker image build helpers. |
| electron/ipc/plans.ts | Ensure watcher registration order is correct. |
| electron/ipc/persistence.ts | Make persistence write atomic with error cleanup; validate JSON and fall back to backup. |
| electron/ipc/channels.ts | Add new IPC channel definitions (docker/system/MCP/git checks). |
| docker/Dockerfile | Add bundled Docker agent image definition used for docker-isolated tasks. |
| COORDINATING-AGENT.md | Document coordinating agent architecture, tools, and manual test flows. |
| .prettierignore | Ignore .letta/ artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const root = this.projectRoot; | ||
| if (root) { | ||
| try { | ||
| await deleteTask([task.agentId], task.branchName, true, root); |
There was a problem hiding this comment.
deleteTask was refactored to take a single options object (see electron/ipc/tasks.ts / electron/ipc/register.ts), but the orchestrator still calls it with the old positional signature. This will fail to typecheck/compile and prevents orchestrated tasks from cleaning up their worktrees. Update the call to pass the new { agentIds, branchName, deleteBranch, projectRoot, taskId? } shape (and consider passing taskId so the plan watcher is stopped as well).
| await deleteTask([task.agentId], task.branchName, true, root); | |
| await deleteTask({ | |
| agentIds: [task.agentId], | |
| branchName: task.branchName, | |
| deleteBranch: true, | |
| projectRoot: root, | |
| taskId, | |
| }); |
| // Scroll selected item into view reactively | ||
| createEffect(() => { | ||
| const idx = selectedIndex(); | ||
| if (idx >= 0) rowRefs[idx]?.scrollIntoView({ block: 'nearest', behavior: 'instant' }); |
There was a problem hiding this comment.
scrollIntoView's behavior option is typed as 'auto' | 'smooth' in the DOM lib; 'instant' will cause a TypeScript error and may not be supported consistently at runtime. Use behavior: 'auto' (or omit behavior) if you want immediate scrolling.
| if (idx >= 0) rowRefs[idx]?.scrollIntoView({ block: 'nearest', behavior: 'instant' }); | |
| if (idx >= 0) rowRefs[idx]?.scrollIntoView({ block: 'nearest', behavior: 'auto' }); |
| @@ -483,14 +516,12 @@ export async function collapseTask(taskId: string): Promise<void> { | |||
| const agentIds = [...task.agentIds]; | |||
| const shellAgentIds = [...task.shellAgentIds]; | |||
|
|
|||
| for (const agentId of agentIds) { | |||
| await invoke(IPC.KillAgent, { agentId }).catch(console.error); | |||
| clearAgentActivity(agentId); | |||
| } | |||
| for (const shellId of shellAgentIds) { | |||
| await invoke(IPC.KillAgent, { agentId: shellId }).catch(console.error); | |||
| clearAgentActivity(shellId); | |||
| } | |||
| invoke(IPC.StopPlanWatcher, { taskId }).catch(console.error); | |||
| const allIds = [...agentIds, ...shellAgentIds]; | |||
There was a problem hiding this comment.
collapseTask invokes IPC.StopPlanWatcher twice back-to-back for the same taskId. This is redundant work (and makes the intent harder to follow). Keep a single call (either before killing agents or just before), relying on backend idempotency if needed.
| if (url.pathname === '/api/tasks' && req.method === 'POST') { | ||
| readBody() | ||
| .then(async (body) => { | ||
| mcpLog('info', `create_task name=${body.name as string}`); | ||
| const result = await orch.createTask({ | ||
| name: body.name as string, | ||
| prompt: body.prompt as string | undefined, | ||
| coordinatorTaskId: (body.coordinatorTaskId as string) ?? 'api', | ||
| projectId: body.projectId as string | undefined, | ||
| }); | ||
| mcpLog('info', `create_task OK id=${result.id}`); | ||
| jsonReply(201, orch.getTaskStatus(result.id)); | ||
| }) | ||
| .catch((err) => { | ||
| mcpLog('error', `create_task FAIL: ${String(err)}`); | ||
| jsonReply(500, { error: String(err) }); | ||
| }); |
There was a problem hiding this comment.
These orchestrator API routes trust body.name, body.prompt, etc. via direct casts. If a client sends invalid JSON (or missing/incorrect fields), this can propagate undefined into orchestrator calls and result in confusing 500s or runtime errors. Consider validating request bodies (type checks + required fields) and returning a 400 with a clear error for bad input.
| const prefix = id + ':'; | ||
| for (const key of Object.keys(s.fontScales)) { | ||
| if (key === id || key.startsWith(prefix)) delete s.fontScales[key]; | ||
| } | ||
| for (const key of Object.keys(s.panelSizes)) { | ||
| if (key.includes(id)) delete s.panelSizes[key]; | ||
| if (key === id || key.startsWith(prefix)) delete s.panelSizes[key]; | ||
| } | ||
| s.taskOrder = s.taskOrder.filter((x) => x !== id); | ||
| s.collapsedTaskOrder = s.collapsedTaskOrder.filter((x) => x !== id); |
There was a problem hiding this comment.
cleanupPanelEntries now only deletes panelSizes keys that equal id or start with ${id}:, but panel sizes are also persisted under keys like task:${id}:... (e.g. ResizablePanel uses persistKey={"task:${taskId}"}). With the current predicate, those entries will never be cleaned up when a task is removed, causing persisted-state growth over time. Consider deleting keys that contain the task ID as a delimited segment (e.g. key.startsWith(task:${id}) / key.includes(:${id}:)), while still avoiding accidental substring matches.
| <div | ||
| style={{ | ||
| 'margin-bottom': '12px', | ||
| 'font-size': '12px', | ||
| color: theme.warning, | ||
| background: `color-mix(in srgb, ${theme.warning} 8%, transparent)`, | ||
| padding: '8px 12px', | ||
| 'border-radius': '8px', | ||
| border: `1px solid color-mix(in srgb, ${theme.warning} 20%, transparent)`, | ||
| 'font-weight': '600', | ||
| }} | ||
| > |
There was a problem hiding this comment.
The coordinator warning banner duplicates the styling that bannerStyle(theme.warning) already centralizes (and bannerStyle is already imported in this file). Using bannerStyle here would keep banner visuals consistent and reduce future style drift.
| <div | |
| style={{ | |
| 'margin-bottom': '12px', | |
| 'font-size': '12px', | |
| color: theme.warning, | |
| background: `color-mix(in srgb, ${theme.warning} 8%, transparent)`, | |
| padding: '8px 12px', | |
| 'border-radius': '8px', | |
| border: `1px solid color-mix(in srgb, ${theme.warning} 20%, transparent)`, | |
| 'font-weight': '600', | |
| }} | |
| > | |
| <div style={bannerStyle(theme.warning)}> |
| @@ -82,12 +85,17 @@ | |||
| "electron/preload.cjs" | |||
| ], | |||
| "asarUnpack": [ | |||
| "**/node-pty/**" | |||
| "**/node-pty/**", | |||
| "dist-electron/mcp-server.cjs" | |||
| ], | |||
| "extraResources": [ | |||
| { | |||
| "from": "build/icon.png", | |||
| "to": "icon.png" | |||
| }, | |||
| { | |||
| "from": "docker/", | |||
| "to": "docker/" | |||
| } | |||
| ], | |||
There was a problem hiding this comment.
PR description says Docker isolation mode is removed, but this PR adds/expands Docker isolation support (e.g. Dockerfile under docker/, docker image settings in persisted state, and bundling docker/ as extraResources). Please reconcile the description with the implemented changes (or clarify that an older Docker mode was removed and replaced with the new one).
|
Thank you very much! <3 |
|
Thanks for this — it's a genuinely interesting feature and a well-structured implementation. Here's an honest assessment to help move it forward. State of the branchThe branch does not appear to rebase cleanly on current Architecture & SecurityThe good:
Issues to address:
ScopeThis is a very large PR (67 files, ~13 000 diff lines). The coordinator feature itself is the clearly novel piece. The refactor of SummaryRebase on main, fix the hardcoded port and the missing body validation on the REST endpoint, and this will be in good shape for a proper review. |
johannesjo
left a comment
There was a problem hiding this comment.
Please rebase onto main before anything else — the branch has known merge conflicts that block review.
After rebase, a few items to address:
- Hardcoded port 7777 — no fallback if the port is in use; port conflicts will silently break the MCP server at runtime.
- Missing REST input validation — the orchestrator REST endpoints lack the
assertString-style validation that IPC handlers enforce; user-controlled inputs should be validated on the main-process side. - Bundled scope — Docker isolation, base-branch plumbing, TaskPanel consolidation, and the coordinator feature are all in one PR, making it very difficult to review. Splitting would make each concern reviewable on its own merits.
The core MCP/orchestration concept is solid. Details in the review comment.
Summary
electron/mcp/) with tools (create_task,send_prompt,wait_for_idle,get_task_diff,merge_task, etc.) enabling a Claude Code agent to programmatically orchestrate tasks in Parallel CodeTaskPanelcomponentTaskAITerminal,TaskBranchInfoBar,TaskClosingOverlay,TaskNotesPanel,TaskShellSection, andTaskTitleBarinto a singleTaskPanelSegmentedButtons, system fonts IPC, and theme utilitiesNewTaskDialog,SettingsDialog,ScrollingDiffView, and multiple store modulesTest plan
send_prompt,wait_for_idle,get_task_diff, andmerge_taskend-to-end