Read a local folder on the Mac instead of a GitHub clone for plans
Merged
CH
16d ago+1415+1415+0123456789012345678901234567890123456789-1426-1426-0123456789012345678901234567890123456789
Plans needed a GitHub OAuth connection: the service resolved the repository through it, and run_in_repository ran bash in a Vercel sandbox clone. This PR changes a plan to name a folder on the developer's Mac, and runs the planning model's commands there.
CI could not verify the Mac side. No verify.sh run yet. The desktop loop, the folder picker, and the form are tested with fakes and a real child process on Linux only.
How a command travels
- The model calls
run_in_repository({ command }). The tool inserts aplan_commandrow and reads it every 250 ms, for up to 60 s (apps/web/server/hosted/repository-shell.ts). - While a plan is open, the Mac long-polls
POST /api/plans/{id}/commands/claim. The service holds the request up to 20 s and claims the oldest unclaimed row for the caller's plan. - The Mac runs
bash -lc <command>in the plan's folder, which only the Mac knows (30 s timeout, output cut to 20,000 characters each) and posts the result toPOST /api/plans/{id}/commands/{commandId}(packages/host/src/planning-commands.ts). - The tool answers the model the exit code, stdout, and stderr. If no answer arrives in 60 s, it answers
not-run.
The tool keeps its name and its { command } input, so the voice activity line, the REPOSITORY_READ slow step, and the voice instructions are unchanged.
Changes
- Data: migration
0054_plan_command.sqldropsrepository_owner/name/branch/commitand adds theplan_commandtable. The service stores no folder path or name for a plan. - Wire:
planRepositorySchemais gone, and a plan is its name and document. The claim and result schemas are new. - Server: plan creation no longer calls GitHub.
apps/web/eve/sandbox.ts,cloneRepository, and thebashandgithubAccessbrain-host seams are deleted. - Mac: each plan's folder is kept in
plan-folders.jsonin the state root (planFoldersFileincompose-planning.ts) and carried to the renderer as the planning view'sfolders. The new-plan form and theplanning.chooseFolderact open the system folder picker. A plan with no folder on this Mac shows "Choose folder…" (planning.setFolder), and its commands answer that no folder is chosen. Plan rows and headers show the path, with~for the home folder. - GitHub sign-in: asks for
read:useranduser:emailonly;repois dropped (auth-policy.ts). PRIVACY.md: the GitHub sandbox lines are replaced by "How the planning model reads your folder".- GitHub code: the repository list and Connect GitHub acts stay in place, unused by plans. The knip check (unused exports) required deleting
resolveRepositoryandnoGitHubConnectionsand makinggithubFailureResponseprivate.
Goldens
The JSON Schema goldens under packages/hosted/fixtures/json-schema/ were re-recorded with LUKE_UPDATE_FIXTURES=1:
plan-wire-planRepositorySchemais removed.- The plan summary, plan, list, answer, and create-request goldens drop
repositoryand hold no folder. - Four new command goldens are added.
Not in this PR
docs/PLANNING.mdstill describes the GitHub flow.
Testing
./scripts/check.shexits 0.pnpm --filter @luke/web test:store: 76 files pass.- New tests:
repository-shell.test.ts: claim, settle, and answer; the deadline answersnot-run; a command is claimed once and never by another account.plans-app.test.ts: the claim and settle routes.compose-planning.test.ts: the open plan's command runs in a temp folder and its exit code, stdout, and stderr come back.planning-acts.test.ts: the folder picker act.
- To test on a Mac: run
./scripts/run.sh, start a plan on a local folder, answer one question, and check that "Planning model · " shows and the model names real files from the folder.
🤖 Generated with Claude Code