You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
commandRunner.exec() (synchronous) is currently used in 72 places across 9 files. This blocks two things:
Out-of-process execution contexts. Any future support for SSH, Docker, Lima, GitHub Codespaces, or similar requires the command to round-trip across a process boundary or network — both of which are inherently async. The sync API is a hard wall against any of those.
Event loop responsiveness.execSync blocks the Electron main process. For long-running git operations (large repos), this manifests as UI freezes during diff loads, dashboard refreshes, and spotlight indexing.
This issue is a pure refactor with zero new functionality. It exists to unblock Remote SSH support and to remove a class of UI-freeze bugs.
Scope
Migrate every sync `commandRunner.exec()` call to `commandRunner.execAsync()`, cascading `async`/`await` upward through callers as needed. Per-file call counts from `grep -rn 'commandRunner.exec(' main/src`:
`main/src/services/gitDiffManager.ts` — 18 calls (highest risk: this is the entire diff view backend; any race condition here will manifest as a broken or stale diff view)
`main/src/ipc/git.ts` — 23 calls (mechanical; handlers are already declared `async`, the bodies just use sync exec for historical reasons)
Walk each file in the order above (lowest-risk first, leaving `gitDiffManager.ts` for last when the pattern is fully understood).
For each call site:
Convert `const out = commandRunner.exec(cmd, cwd)` → `const { stdout: out } = await commandRunner.execAsync(cmd, cwd)`
Walk up the call stack and add `async`/`await` until you hit a function that's already async (usually an IPC handler).
If a caller genuinely cannot be made async (deep synchronous code path with no obvious entry point), document the constraint and leave a TODO referencing this issue. Try hard to avoid this — there should be zero exceptions.
After each file is migrated, run `pnpm typecheck` and `pnpm lint` and manually smoke the affected feature before moving to the next file.
Validation
Automated
`pnpm typecheck` — clean
`pnpm lint` — clean
`grep -rn 'commandRunner.exec(' main/src` returns zero matches outside the `CommandRunner` class definition itself
Manual regression smoke (REQUIRED — typecheck does not prove behavior)
Test on a real local project with multiple sessions and a non-trivial git history:
Diff view: open a session, view a file diff, switch files, refresh — diff loads and updates correctly
Git log: open git history view, scroll through commits, click a commit to view its diff
Git status: edit a file, see uncommitted changes appear; commit it, see them clear
Git rebase from main: works end-to-end without errors
Git squash and rebase: works end-to-end
Dashboard refresh: open dashboard, click refresh, all counters update
Spotlight search: type a query, see results from worktree contents
Project init: create a brand new project in a fresh directory; `git init` runs, initial commit lands
Run command from file:execute-project handler: trigger via the script execution UI
GitFileWatcher: edit a file outside Pane, see the change reflected
No UI freezes: scroll through a session with a large diff — should remain responsive
Out of Scope
Any SSH-related code
Any new abstractions (`RemoteFs`, `ExecutionContext`, etc.)
Any new features
WSL changes (the WSL routing already works through `execAsync` and is unaffected)
Success Criteria
All 72 call sites migrated to async
Manual regression smoke passes for every feature listed above
Zero typecheck or lint errors
PR description includes a per-file summary of what was changed
Diff is purely a refactor — no behavior changes intended
Why This Is a Prerequisite for Remote SSH
The Remote SSH support effort needs to add an `if (sshContext)` branch to `CommandExecutor.execAsync` that delegates to a network-backed exec call. The sync version (`execSync`) cannot delegate to a network call because there's no way to block on async I/O from a sync function. Any caller that uses `commandRunner.exec()` (sync) will therefore need to either:
Throw a hard error at runtime when invoked on an SSH project, or
Be migrated to async
Doing the migration as part of the SSH PR couples a feature to a refactor and makes regressions impossible to bisect. Doing it as a standalone refactor lets us validate it independently and ship the SSH feature additively on top.
The Remote SSH plan is at `tmp/ready-plans/2026-04-10-remote-ssh-support.md` in the `remote-ssh-like-vs-code` branch.
Why
commandRunner.exec()(synchronous) is currently used in 72 places across 9 files. This blocks two things:execSyncblocks the Electron main process. For long-running git operations (large repos), this manifests as UI freezes during diff loads, dashboard refreshes, and spotlight indexing.This issue is a pure refactor with zero new functionality. It exists to unblock Remote SSH support and to remove a class of UI-freeze bugs.
Scope
Migrate every sync `commandRunner.exec()` call to `commandRunner.execAsync()`, cascading `async`/`await` upward through callers as needed. Per-file call counts from `grep -rn 'commandRunner.exec(' main/src`:
Total: 72 calls.
Approach
Validation
Automated
Manual regression smoke (REQUIRED — typecheck does not prove behavior)
Test on a real local project with multiple sessions and a non-trivial git history:
Out of Scope
Success Criteria
Why This Is a Prerequisite for Remote SSH
The Remote SSH support effort needs to add an `if (sshContext)` branch to `CommandExecutor.execAsync` that delegates to a network-backed exec call. The sync version (`execSync`) cannot delegate to a network call because there's no way to block on async I/O from a sync function. Any caller that uses `commandRunner.exec()` (sync) will therefore need to either:
Doing the migration as part of the SSH PR couples a feature to a refactor and makes regressions impossible to bisect. Doing it as a standalone refactor lets us validate it independently and ship the SSH feature additively on top.
The Remote SSH plan is at `tmp/ready-plans/2026-04-10-remote-ssh-support.md` in the `remote-ssh-like-vs-code` branch.