Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
68fdb31
feat(remote): add coordinator task API routes to remote server
brooksc May 8, 2026
4fe29da
chore: remove pre-commit hook
brooksc May 8, 2026
6de6f8e
Squash merge
brooksc May 8, 2026
3a4d197
fix(coordinator): four minor bug fixes — taskId guard, REST sentinel,…
brooksc May 8, 2026
b146b07
feat(coordinator): MCP backend improvements and coordinator core
brooksc May 9, 2026
e065672
feat(ctrl): Take Control / Release Control for coordinator and sub-tasks
brooksc May 9, 2026
9a45012
docs: coordinator design document, TODOS, and known open issues
brooksc May 9, 2026
ca1ebc1
test(tasks): add unit tests for MCP_TaskCreated IPC handler
brooksc May 9, 2026
a4bd542
test(autofire): unit tests for controlledBy pause behavior in interva…
brooksc May 9, 2026
7b3e17b
docs: add CONTRIBUTING.md
brooksc May 9, 2026
9408eca
test(TerminalView): add unit tests for disableStdin behavior
brooksc May 9, 2026
0d01cc6
docs: fix merge_task description to say base branch instead of main
brooksc May 9, 2026
c7659b3
fix: eliminate double coordinator preamble by removing lib version
brooksc May 9, 2026
5be7264
fix(mcp): add uncommitted-file marker to get_task_diff and review_and…
brooksc May 9, 2026
ad7a077
fix(coordinator): throw on auto-commit failure with uncommitted changes
brooksc May 9, 2026
0a913c3
fix: reflect needsReview flag in task attention state and dot status
brooksc May 9, 2026
9676908
fix(SubTaskStrip): include collapsed sub-tasks in coordinator strip
brooksc May 9, 2026
a556184
fix(coordinator): restore modified preamble files on createTask failure
brooksc May 9, 2026
5f0bf78
fix(tasks): disallow collapsing coordinator tasks
brooksc May 9, 2026
502a00b
fix(projects): close coordinator children before coordinator in remov…
brooksc May 9, 2026
bede840
fix(coordinator): strip preamble files before auto-commit in mergeTask()
brooksc May 9, 2026
34e46c5
chore: remove completed TODO items, keep remaining edge cases and har…
brooksc May 9, 2026
beb3ee7
feat(coordinator): sliding-window preamble, max concurrent UI, and su…
brooksc May 9, 2026
f876f89
fix(coordinator): preserve pre-existing empty preamble files on strip
brooksc May 9, 2026
afba4c4
fix: update review_and_merge_task description to use get_task_diff->m…
brooksc May 9, 2026
f28f4ab
fix: reject gitIsolation in REST API instead of silently ignoring it
brooksc May 9, 2026
78fcaba
fix: split MCP_CoordinatorOrphanedNotification channel
brooksc May 9, 2026
5de1437
fix: hydrate backend Coordinator.tasks on app restart
brooksc May 9, 2026
29ead45
fix: correct sidebar drag index translation with coordinator children
brooksc May 9, 2026
c83aaf5
fix: restore backend controlMap on app restart
brooksc May 9, 2026
945aee3
fix: uncollapse coordinator when clicking collapsed child in sidebar
brooksc May 9, 2026
cd61e7c
chore: clean up TODOS.md — all actionable items resolved
brooksc May 9, 2026
d2fd0ae
chore: revert README.md changes and remove CONTRIBUTING.md
brooksc May 9, 2026
33a8b25
fix(coordinator): address five lifecycle/correctness issues from review
brooksc May 9, 2026
bab63b4
fix(mcp): two lifecycle gaps vs test plan + regression tests
brooksc May 9, 2026
5d3156f
docs: add three test-plan coverage gaps to TODOS
brooksc May 9, 2026
f242559
fix(docker-coordinator): mount main .git dir + bind MCP server to 0.0…
brooksc May 10, 2026
ca10658
chore: mark TODO #10 complete (tmp config cleanup test added)
brooksc May 10, 2026
0c98413
Squash merge
brooksc May 10, 2026
66dbd80
Squash merge
brooksc May 10, 2026
3a7797b
Squash merge
brooksc May 10, 2026
1b19f06
chore: mark TODO #9 complete (edit suppression tests landed)
brooksc May 10, 2026
15935c6
chore: mark TODO #12 complete (selectMcpJsonDir test added)
brooksc May 10, 2026
0818933
chore: mark TODO #26 complete (MCP status reporting expanded)
brooksc May 10, 2026
76d0bb6
chore: mark TODO #24 complete (agent def derived from agentCommand)
brooksc May 10, 2026
6bbff2f
chore: merge task/todo-26-mcp-status-reporting — expand GetMCPStatus …
brooksc May 10, 2026
3112b31
chore: merge task/todo-12-selectmcpjsondir-test — add unit tests for …
brooksc May 10, 2026
4e42bb0
chore: mark TODO #22 complete (startRemoteServer awaits listen)
brooksc May 10, 2026
d7c1d03
feat: await server listen before resolving startRemoteServer
brooksc May 10, 2026
d457713
chore: mark TODO #27 complete (deregistration cleans child backend re…
brooksc May 10, 2026
29ed5f5
chore: mark TODO #21 complete (detectStaleDockerMCPUrl called on star…
brooksc May 10, 2026
06e51fd
fix: clean up child backend resources on coordinator deregistration (…
brooksc May 10, 2026
9fa80e4
feat: call detectStaleDockerMCPUrl on MCP server startup
brooksc May 10, 2026
f520758
fix: persist initialPrompt across app restarts (TODO #29)
brooksc May 10, 2026
79d2b3c
chore: mark TODO #19 complete (Docker sub-tasks get isolated HOME dirs)
brooksc May 10, 2026
d394ff6
fix: isolate Docker sub-task HOME dirs to prevent config collisions (…
brooksc May 10, 2026
12e433d
chore: mark TODO #20 complete (.claude.json writes serialized)
brooksc May 10, 2026
d36f229
chore: mark TODO #28 complete (setTaskControl awaits IPC before commi…
brooksc May 10, 2026
23726ac
fix: setTaskControl awaits IPC before updating UI state
brooksc May 10, 2026
2fe7179
chore: mark TODO #17 complete (MCP token passed via env var)
brooksc May 10, 2026
d32ca69
fix: pass MCP token via env var instead of CLI arg
brooksc May 10, 2026
2f95e21
chore: mark TODO #13 complete (tasks scoped to coordinator ID)
brooksc May 10, 2026
0c6afe1
fix: scope MCP tool handlers to coordinator ID to prevent cross-conta…
brooksc May 10, 2026
552e649
chore: mark TODO #14 complete (coordinator routes registered lazily)
brooksc May 10, 2026
eea0f27
fix: coordinator routes use lazy getCoordinator() lookup
brooksc May 10, 2026
672551b
chore: mark TODO #15 complete (await StartMCPServer + hydrateTask res…
brooksc May 10, 2026
5d639de
fix: await StartMCPServer before respawn; hydrateTask restores signal…
brooksc May 10, 2026
bb321e4
chore: mark TODO #25 complete (coordinator routes always registered l…
brooksc May 10, 2026
39f4328
fix(remote): register coordinator routes lazily so remote-first start…
brooksc May 10, 2026
b8bd708
chore: mark TODO #16 complete (MCP_CoordinatedTaskClosed IPC cleans b…
brooksc May 10, 2026
fe68af9
fix: IPC notifies coordinator backend when coordinated child is close…
brooksc May 10, 2026
bf2e9d9
fix: kill inner docker exec agent process on sub-task cleanup
brooksc May 10, 2026
68fd635
chore: mark TODO #18 complete (docker exec inner process cleanup)
brooksc May 10, 2026
b399e78
fix: block collapseTask for coordinated children to preserve agent id…
brooksc May 10, 2026
e8673ae
chore: mark TODO #23 complete (collapse blocked for coordinated child…
brooksc May 10, 2026
1a34ab2
chore: remove TODO #13 and #14 sections (already landed)
brooksc May 10, 2026
afa5e0a
fix(git): guard getWorktreeStatus against deleted worktree path
brooksc May 10, 2026
7e57948
fix(autofire): reset miss counter when agent produces output during t…
brooksc May 10, 2026
b7fdc14
docs: add TODO #31 — one container per Docker sub-task
brooksc May 10, 2026
a6b71a0
fix(coordinator): three post-review bug fixes + TODOS additions
brooksc May 10, 2026
2030702
fix: three post-review bug fixes (controlMap leak, TOCTOU port race, …
brooksc May 10, 2026
865241a
fix: four post-review bug fixes (mcpServerInfo isolation, sentinel er…
brooksc May 10, 2026
6d76f99
fix: stale MCP config on restart and Docker cleanup kills sibling agents
brooksc May 10, 2026
8504315
fix: four lifecycle and cleanup bugs from review
brooksc May 10, 2026
b5c6692
fix: branch restore regression, wrong task diff base, stale batch IDs
brooksc May 10, 2026
1dcfdbe
chore: update package-lock.json to sync with package.json
brooksc May 10, 2026
1710b8a
fix(security): write MCP config files with mode 0o600
brooksc May 10, 2026
789fcbe
fix(coordinator): resolve signal/idle waiters on exit, persist preamb…
brooksc May 10, 2026
9f31d06
fix: signal_done ownership check, cleanupTask resolves signal waiters…
brooksc May 10, 2026
d0c06a8
fix(coordinator): revert git add -u back to git add -A in mergeTask a…
brooksc May 10, 2026
7f3ea7c
fix: deregisterCoordinator resolves signal waiters; validate mcpConfi…
brooksc May 10, 2026
e84cb35
fix(security): two-class token, mcpReady gate, propagateSkipPermissio…
brooksc May 10, 2026
e04368a
docs(coordinator): clarify PR 100 follow-up TODOs
brooksc May 10, 2026
77b5c7d
fix: restore MAX_DIFF_BYTES truncation, complete mcpReady finally, cl…
brooksc May 10, 2026
1f30548
fix(#43): surface MCP restore failures with error state and retry UI
brooksc May 10, 2026
1b3124c
test: add StartMCPServer input validation tests (TODO #35)
brooksc May 10, 2026
70d5d27
test(coordinator): add missing tests for #33, #36, #39
brooksc May 10, 2026
f07e3d3
fix(coordinator): async preamble I/O with per-path write serializatio…
brooksc May 10, 2026
0579d57
fix(coordinator): cleanup failure retained — MCP_TaskCleanupFailed, D…
brooksc May 10, 2026
f25dc86
feat(coordinator): Docker sub-tasks each run in their own container (…
brooksc May 10, 2026
2ef2419
chore: document TODO #44 (staged prompt visibility) and #45 (wait_for…
brooksc May 10, 2026
759640a
fix(P1): retry wait_for_signal_done on network error; preserve conten…
brooksc May 10, 2026
e75bc96
fix(P1): idempotent wait_for_signal_done retry; correct preamble diff…
brooksc May 10, 2026
aaf29b9
fix(security): shared validation, fail-closed JSON, staged prompt UI,…
brooksc May 10, 2026
bfe526f
fix: validation parity, fail-closed state, and replay cache scoping
brooksc May 10, 2026
58c6b3b
fix: preamble fail-safe + restart race (coordinator/sub-task spawn de…
brooksc May 10, 2026
8cbc504
refactor: async FS in detectPreambleFiles/stripPreamble, sequential g…
brooksc May 10, 2026
021d4cc
fix(security): mobile token, bind-address, route scoping, validator h…
brooksc May 11, 2026
96ab947
refactor: extract atomic writes, replay cache, preamble module; coord…
brooksc May 11, 2026
d32cd5c
chore: add static analysis tooling (Knip, dependency-cruiser, Semgrep…
brooksc May 11, 2026
0d9f3e9
fix(security): address round-2 review — token scoping, per-task done …
brooksc May 14, 2026
35389af
chore: remove internal working docs before merge
brooksc May 14, 2026
aba40fc
test: fix navigation and focus tests after rebase
brooksc May 14, 2026
2b21610
fix(security): address round-3 review — UUID validation, race fix, fs…
brooksc May 15, 2026
63aed67
chore: remove ask-coordinator proposal.md from PR (keep as local work…
brooksc May 15, 2026
3eec8af
fix(docker): tighten MCP bind address on Linux; warn on macOS when co…
brooksc May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .dependency-cruiser.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
forbidden: [
{
name: 'no-renderer-importing-main',
severity: 'error',
comment:
'Renderer (src/) must never import Electron main-process code. ' +
'Use IPC channels instead.',
from: { path: '^src/' },
to: {
path: '^electron/',
// Allow importing the shared IPC channel enum (channels.ts is a pure enum, no Node/Electron deps)
pathNot: '^electron/ipc/channels\\.ts',
},
},
{
name: 'no-mcp-importing-components',
severity: 'error',
comment: 'MCP coordinator must not import frontend components or store.',
from: { path: '^electron/mcp/' },
to: { path: '^src/(components|store|lib)/' },
},
{
name: 'no-circular',
severity: 'error',
comment:
'Circular dependencies break tree-shaking and make reasoning about startup order impossible.',
from: {},
to: { circular: true },
},
{
name: 'no-orphans',
severity: 'warn',
comment: 'Orphan modules have no importers and no exports used elsewhere — likely dead code.',
from: {
orphan: true,
// Test files, config files, entry points, and pure type modules are expected orphans.
// Type-only modules (types.ts, *.d.ts) are consumed by TypeScript structurally — the
// import graph doesn't capture all type-level usage, so they appear orphaned.
pathNot: [
'\\.test\\.(ts|tsx)$',
'\\.config\\.(ts|js|cjs)$',
'\\.d\\.ts$',
'types\\.ts$',
'types\\.(ts|tsx)$',
'^src/main\\.tsx$',
'^src/remote/main\\.tsx$',
'^electron/main\\.ts$',
'^electron/preload\\.cjs$',
'^electron/mcp/server\\.ts$',
// Vite ambient env declarations
'^src/vite-env\\.d\\.ts$',
],
},
to: {},
},
],

options: {
doNotFollow: {
path: 'node_modules',
},
moduleSystems: ['es6', 'cjs'],
tsConfig: {
fileName: 'tsconfig.json',
},
reporterOptions: {
dot: {
collapsePattern: 'node_modules/[^/]+',
},
archi: {
collapsePattern: '^(node_modules|src/components)/[^/]+',
},
},
},
};
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dist-electron
dist-remote
release
coverage
.parallel-code/
.worktrees
.idea
.claude
Expand Down
43 changes: 43 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
title = "Gitleaks config for Parallel Code"

[extend]
# Use the default Gitleaks ruleset as the base
useDefault = true

[[rules]]
id = "parallel-code-mcp-token"
description = "Parallel Code MCP bearer token"
regex = '''PARALLEL_CODE_MCP_TOKEN\s*[=:]\s*['""]?[A-Za-z0-9+/\-_]{20,}['""]?'''
tags = ["token", "parallel-code"]

[[rules]]
id = "bearer-token-in-url"
description = "Bearer token embedded in a URL query parameter"
regex = '''[?&]token=[A-Za-z0-9+/\-_]{20,}'''
tags = ["token", "url"]
[rules.allowlist]
# Test fixture URLs and documentation are expected to have placeholder tokens
regexes = [
'''token=<[A-Za-z0-9_-]+>''', # placeholder like ?token=<your-token>
'''token=test''', # obvious test value
'''token=abc''', # obvious test value
'''token=tok''', # obvious test value
]

[[rules]]
id = "anthropic-api-key"
description = "Anthropic API key"
regex = '''sk-ant-[A-Za-z0-9\-_]{40,}'''
tags = ["api-key", "anthropic"]

[allowlist]
description = "Global allowlist"
paths = [
# Lock files contain package hashes, not secrets
'''package-lock\.json''',
# Test fixture files with obviously fake values
'''\.test\.(ts|tsx)$''',
# The gitleaks config itself documents patterns
'''\.gitleaks\.toml''',
]
commits = []
49 changes: 49 additions & 0 deletions .semgrep/electron-security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
rules:
- id: no-inner-html-without-sanitize
pattern: $EL.innerHTML = $VAL
pattern-not: $EL.innerHTML = DOMPurify.sanitize(...)
message: |
Direct innerHTML assignment without DOMPurify.sanitize() risks XSS.
Use the sanitizeHtml() helper or DOMPurify.sanitize() explicitly.
languages: [typescript]
severity: ERROR
paths:
include:
- '**/src/**'

- id: no-eval
pattern: eval($X)
message: |
eval() executes arbitrary code. Not allowed in Electron renderer or main process.
languages: [typescript]
severity: ERROR

- id: no-new-function
pattern: new Function($X)
message: |
new Function() executes arbitrary code. Not allowed in Electron renderer or main process.
languages: [typescript]
severity: ERROR

- id: no-shell-true-in-spawn
pattern: |
child_process.spawn($CMD, $ARGS, {shell: true})
message: |
spawn() with shell:true enables shell injection. Use shell:false and
pass arguments as an array.
languages: [typescript]
severity: ERROR
paths:
include:
- '**/electron/**'

- id: pkill-dash-f-broad-kill
pattern: $EXEC("pkill", ["-f", ...], ...)
message: |
pkill -f matches any process whose full command line contains the pattern,
which can accidentally kill unrelated processes. Use kill by PID or docker stop.
languages: [typescript]
severity: WARNING
paths:
include:
- '**/electron/**'
24 changes: 24 additions & 0 deletions .semgrep/filesystem-safety.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
rules:
- id: direct-writefile-in-mcp-coordinator
pattern: writeFileSync($PATH, $DATA, ...)
message: |
Direct writeFileSync in coordinator code risks torn writes on crash.
Use atomicWriteFileSync() from electron/mcp/atomic.ts instead.
languages: [typescript]
severity: WARNING
paths:
include:
- '**/electron/mcp/coordinator.ts'
- '**/electron/ipc/register.ts'

- id: copyfilesync-side-effect
pattern: fs.copyFileSync($SRC, $DST)
message: |
fs.copyFileSync is a filesystem side effect. In StartMCPServer,
ensure all pure computation (mcpConfig, mergedMcpJson) precedes
any copyFileSync calls so validation failures don't leave residue.
languages: [typescript]
severity: INFO
paths:
include:
- '**/electron/ipc/register.ts'
28 changes: 28 additions & 0 deletions .semgrep/ipc-auth.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
rules:
- id: token-embedded-in-url-template
pattern: |
`$PREFIX?token=${$TOKEN}$SUFFIX`
message: |
Token embedded directly in URL template literal. Mobile/shared URLs must use
mobileToken, not the coordinator token. The coordinator token must never appear
in any URL that reaches the renderer or network.
languages: [typescript]
severity: ERROR
paths:
include:
- '**/electron/**'

- id: console-log-token-variable
pattern-either:
- pattern: console.log($A, token, $B)
- pattern: console.warn($A, token, $B)
- pattern: console.log(token)
- pattern: console.warn(token)
message: |
Logging a variable named 'token' directly. Use redactServerUrl() or
ensure this is not a bearer token value.
languages: [typescript]
severity: WARNING
paths:
include:
- '**/electron/**'
48 changes: 24 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,30 +138,30 @@ Requires [Node.js](https://nodejs.org/) v18+.

`Ctrl` = `Cmd` on macOS.

| Shortcut | Action |
| --------------------- | ---------------------------------- |
| **Tasks** | |
| `Ctrl+N` | New task |
| `Ctrl+Shift+A` | New task (alternative) |
| `Ctrl+Enter` | Send prompt |
| `Ctrl+Shift+M` | Merge task to main |
| `Ctrl+Shift+P` | Push to remote |
| `Ctrl+W` | Close focused terminal session |
| `Ctrl+Shift+W` | Close active task |
| **Navigation** | |
| `Alt+Arrows` | Focus pane/task in arrow direction |
| `Ctrl+Alt+Left/Right` | Move task left/right |
| `Ctrl+B` | Toggle sidebar |
| `Ctrl+Shift+F` | Toggle focus mode |
| **Terminals** | |
| `Ctrl+Shift+T` | New shell terminal |
| `Ctrl+Shift+D` | New standalone terminal |
| **App** | |
| `Ctrl+,` | Open settings |
| `Ctrl+/` or `F1` | Show all shortcuts |
| `Ctrl+0` | Reset zoom |
| `Ctrl+Scroll` | Adjust zoom |
| `Escape` | Close dialog |
| Shortcut | Action |
| --------------------- | ------------------------------ |
| **Tasks** | |
| `Ctrl+N` | New task |
| `Ctrl+Shift+A` | New task (alternative) |
| `Ctrl+Enter` | Send prompt |
| `Ctrl+Shift+M` | Merge task to main |
| `Ctrl+Shift+P` | Push to remote |
| `Ctrl+W` | Close focused terminal session |
| `Ctrl+Shift+W` | Close active task |
| **Navigation** | |
| `Alt+Arrows` | Navigate between panels |
| `Ctrl+Alt+Left/Right` | Reorder active task |
| `Ctrl+B` | Toggle sidebar |
| `Ctrl+Shift+F` | Toggle focus mode |
| **Terminals** | |
| `Ctrl+Shift+T` | New shell terminal |
| `Ctrl+Shift+D` | New standalone terminal |
| **App** | |
| `Ctrl+,` | Open settings |
| `Ctrl+/` or `F1` | Show all shortcuts |
| `Ctrl+0` | Reset zoom |
| `Ctrl+Scroll` | Adjust zoom |
| `Escape` | Close dialog |

</details>

Expand Down
27 changes: 27 additions & 0 deletions electron/ipc/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,31 @@ export enum IPC {

// Logging
LogFromRenderer = 'log_from_renderer',

// MCP / Coordinating agent
SetCoordinatorModeEnabled = 'set_coordinator_mode_enabled',
StartMCPServer = 'start_mcp_server',
StopMCPServer = 'stop_mcp_server',
GetMCPStatus = 'get_mcp_status',
GetMCPLogs = 'get_mcp_logs',
MCP_TaskCreated = 'mcp_task_created',
MCP_TaskClosed = 'mcp_task_closed',
MCP_TaskStateSync = 'mcp_task_state_sync',
MCP_ControlChanged = 'mcp_control_changed',
// Coordinator notifications (main → renderer)
MCP_CoordinatorNotificationStaged = 'mcp_coordinator_notification_staged',
MCP_CoordinatorNotificationCleared = 'mcp_coordinator_notification_cleared',
MCP_CoordinatorOrphanedNotification = 'mcp_coordinator_orphaned_notification',
// Coordinator lifecycle (renderer → main)
MCP_CoordinatorRegistered = 'mcp_coordinator_registered',
MCP_CoordinatorDeregistered = 'mcp_coordinator_deregistered',
MCP_CoordinatorNotificationAck = 'mcp_coordinator_notification_ack',
MCP_CoordinatorNotificationDropAck = 'mcp_coordinator_notification_drop_ack',
MCP_CoordinatedTaskPromptDelivered = 'mcp_coordinated_task_prompt_delivered',
MCP_CoordinatorRestageAfterUserSend = 'mcp_coordinator_restage_after_user_send',
MCP_HydrateCoordinatedTask = 'mcp_hydrate_coordinated_task',
MCP_TaskHydrated = 'mcp_task_hydrated',
MCP_StaleUrlWarning = 'mcp_stale_url_warning',
MCP_CoordinatedTaskClosed = 'mcp_coordinated_task_closed',
MCP_TaskCleanupFailed = 'mcp_task_cleanup_failed',
}
Loading