gsd-pi 2.29.0-dev.77f06e2 → 2.29.0-dev.f08b4fe

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/extension-registry.d.ts +63 -0
  2. package/dist/extension-registry.js +166 -0
  3. package/dist/headless.js +4 -0
  4. package/dist/loader.js +10 -1
  5. package/dist/resource-loader.js +11 -1
  6. package/dist/resources/extensions/async-jobs/extension-manifest.json +13 -0
  7. package/dist/resources/extensions/bg-shell/extension-manifest.json +14 -0
  8. package/dist/resources/extensions/browser-tools/extension-manifest.json +37 -0
  9. package/dist/resources/extensions/context7/extension-manifest.json +12 -0
  10. package/dist/resources/extensions/google-search/extension-manifest.json +12 -0
  11. package/dist/resources/extensions/gsd/auto-dashboard.ts +31 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
  13. package/dist/resources/extensions/gsd/auto-post-unit.ts +39 -10
  14. package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
  15. package/dist/resources/extensions/gsd/auto-recovery.ts +2 -1
  16. package/dist/resources/extensions/gsd/auto-start.ts +18 -32
  17. package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
  18. package/dist/resources/extensions/gsd/auto.ts +2 -9
  19. package/dist/resources/extensions/gsd/captures.ts +4 -10
  20. package/dist/resources/extensions/gsd/commands-extensions.ts +328 -0
  21. package/dist/resources/extensions/gsd/commands-handlers.ts +2 -1
  22. package/dist/resources/extensions/gsd/commands.ts +53 -2
  23. package/dist/resources/extensions/gsd/detection.ts +2 -1
  24. package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
  25. package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
  26. package/dist/resources/extensions/gsd/extension-manifest.json +18 -0
  27. package/dist/resources/extensions/gsd/forensics.ts +2 -2
  28. package/dist/resources/extensions/gsd/git-service.ts +3 -2
  29. package/dist/resources/extensions/gsd/gitignore.ts +9 -63
  30. package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
  31. package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
  32. package/dist/resources/extensions/gsd/index.ts +3 -3
  33. package/dist/resources/extensions/gsd/md-importer.ts +3 -2
  34. package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
  35. package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
  36. package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
  37. package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
  38. package/dist/resources/extensions/gsd/paths.ts +24 -2
  39. package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  40. package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
  41. package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
  42. package/dist/resources/extensions/gsd/preferences.ts +10 -5
  43. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  47. package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
  48. package/dist/resources/extensions/gsd/resource-version.ts +99 -0
  49. package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
  50. package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  51. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  52. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  53. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  54. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  55. package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  56. package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  57. package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  58. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  59. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  60. package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
  61. package/dist/resources/extensions/gsd/types.ts +2 -0
  62. package/dist/resources/extensions/gsd/worktree-command.ts +1 -11
  63. package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
  64. package/dist/resources/extensions/gsd/worktree.ts +42 -5
  65. package/dist/resources/extensions/mac-tools/extension-manifest.json +16 -0
  66. package/dist/resources/extensions/mcporter/extension-manifest.json +12 -0
  67. package/dist/resources/extensions/remote-questions/extension-manifest.json +11 -0
  68. package/dist/resources/extensions/search-the-web/extension-manifest.json +13 -0
  69. package/dist/resources/extensions/slash-commands/extension-manifest.json +11 -0
  70. package/dist/resources/extensions/subagent/extension-manifest.json +13 -0
  71. package/dist/resources/extensions/ttsr/extension-manifest.json +11 -0
  72. package/dist/resources/extensions/universal-config/extension-manifest.json +13 -0
  73. package/dist/resources/extensions/voice/extension-manifest.json +12 -0
  74. package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
  75. package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
  76. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
  77. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
  78. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
  79. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
  80. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
  81. package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
  82. package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
  83. package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
  84. package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
  85. package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
  86. package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
  87. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
  88. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
  89. package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
  90. package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
  91. package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
  92. package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
  93. package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
  94. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
  95. package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
  96. package/dist/resources/skills/create-skill/SKILL.md +184 -0
  97. package/dist/resources/skills/create-skill/references/api-security.md +226 -0
  98. package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
  99. package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
  100. package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
  101. package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
  102. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
  103. package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
  104. package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
  105. package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
  106. package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
  107. package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
  108. package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
  109. package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
  110. package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
  111. package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
  112. package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
  113. package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
  114. package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
  115. package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
  116. package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
  117. package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
  118. package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
  119. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
  120. package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
  121. package/dist/resources/skills/react-best-practices/SKILL.md +1 -1
  122. package/package.json +1 -1
  123. package/packages/native/dist/native.d.ts +2 -0
  124. package/packages/native/dist/native.js +19 -5
  125. package/packages/native/src/native.ts +23 -9
  126. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
  128. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  129. package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
  130. package/src/resources/extensions/async-jobs/extension-manifest.json +13 -0
  131. package/src/resources/extensions/bg-shell/extension-manifest.json +14 -0
  132. package/src/resources/extensions/browser-tools/extension-manifest.json +37 -0
  133. package/src/resources/extensions/context7/extension-manifest.json +12 -0
  134. package/src/resources/extensions/google-search/extension-manifest.json +12 -0
  135. package/src/resources/extensions/gsd/auto-dashboard.ts +31 -0
  136. package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
  137. package/src/resources/extensions/gsd/auto-post-unit.ts +39 -10
  138. package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
  139. package/src/resources/extensions/gsd/auto-recovery.ts +2 -1
  140. package/src/resources/extensions/gsd/auto-start.ts +18 -32
  141. package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
  142. package/src/resources/extensions/gsd/auto.ts +2 -9
  143. package/src/resources/extensions/gsd/captures.ts +4 -10
  144. package/src/resources/extensions/gsd/commands-extensions.ts +328 -0
  145. package/src/resources/extensions/gsd/commands-handlers.ts +2 -1
  146. package/src/resources/extensions/gsd/commands.ts +53 -2
  147. package/src/resources/extensions/gsd/detection.ts +2 -1
  148. package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
  149. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  150. package/src/resources/extensions/gsd/extension-manifest.json +18 -0
  151. package/src/resources/extensions/gsd/forensics.ts +2 -2
  152. package/src/resources/extensions/gsd/git-service.ts +3 -2
  153. package/src/resources/extensions/gsd/gitignore.ts +9 -63
  154. package/src/resources/extensions/gsd/gsd-db.ts +1 -165
  155. package/src/resources/extensions/gsd/guided-flow.ts +8 -5
  156. package/src/resources/extensions/gsd/index.ts +3 -3
  157. package/src/resources/extensions/gsd/md-importer.ts +3 -2
  158. package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
  159. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  160. package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
  161. package/src/resources/extensions/gsd/migrate-external.ts +123 -0
  162. package/src/resources/extensions/gsd/paths.ts +24 -2
  163. package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  164. package/src/resources/extensions/gsd/preferences-models.ts +7 -1
  165. package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
  166. package/src/resources/extensions/gsd/preferences.ts +10 -5
  167. package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  168. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  169. package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  170. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  171. package/src/resources/extensions/gsd/repo-identity.ts +148 -0
  172. package/src/resources/extensions/gsd/resource-version.ts +99 -0
  173. package/src/resources/extensions/gsd/session-forensics.ts +4 -3
  174. package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  175. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  176. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  177. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  178. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  179. package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  180. package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  181. package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  182. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  183. package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  184. package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
  185. package/src/resources/extensions/gsd/types.ts +2 -0
  186. package/src/resources/extensions/gsd/worktree-command.ts +1 -11
  187. package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
  188. package/src/resources/extensions/gsd/worktree.ts +42 -5
  189. package/src/resources/extensions/mac-tools/extension-manifest.json +16 -0
  190. package/src/resources/extensions/mcporter/extension-manifest.json +12 -0
  191. package/src/resources/extensions/remote-questions/extension-manifest.json +11 -0
  192. package/src/resources/extensions/search-the-web/extension-manifest.json +13 -0
  193. package/src/resources/extensions/slash-commands/extension-manifest.json +11 -0
  194. package/src/resources/extensions/subagent/extension-manifest.json +13 -0
  195. package/src/resources/extensions/ttsr/extension-manifest.json +11 -0
  196. package/src/resources/extensions/universal-config/extension-manifest.json +13 -0
  197. package/src/resources/extensions/voice/extension-manifest.json +12 -0
  198. package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
  199. package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
  200. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
  201. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
  202. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
  203. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
  204. package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
  205. package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
  206. package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
  207. package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
  208. package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
  209. package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
  210. package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
  211. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
  212. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
  213. package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
  214. package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
  215. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
  216. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
  217. package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
  218. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
  219. package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
  220. package/src/resources/skills/create-skill/SKILL.md +184 -0
  221. package/src/resources/skills/create-skill/references/api-security.md +226 -0
  222. package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
  223. package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
  224. package/src/resources/skills/create-skill/references/core-principles.md +437 -0
  225. package/src/resources/skills/create-skill/references/executable-code.md +175 -0
  226. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
  227. package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
  228. package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
  229. package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
  230. package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
  231. package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
  232. package/src/resources/skills/create-skill/references/using-templates.md +112 -0
  233. package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
  234. package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
  235. package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
  236. package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
  237. package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
  238. package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
  239. package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
  240. package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
  241. package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
  242. package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
  243. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
  244. package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
  245. package/src/resources/skills/react-best-practices/SKILL.md +1 -1
  246. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
  247. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  248. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
  249. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
  250. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  251. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
@@ -530,11 +530,21 @@ export async function checkNeedsRunUat(
530
530
  const uatContent = await loadFile(uatFile);
531
531
  if (!uatContent) return null;
532
532
 
533
- // If UAT result already exists, skip (idempotent)
533
+ // If UAT result already exists with a PASS verdict, skip (idempotent).
534
+ // Non-PASS verdicts (FAIL, surfaced-for-human-review) should block slice
535
+ // progression — return the slice for re-evaluation (#1231).
534
536
  const uatResultFile = resolveSliceFile(base, mid, sid, "UAT-RESULT");
535
537
  if (uatResultFile) {
536
- const hasResult = !!(await loadFile(uatResultFile));
537
- if (hasResult) return null;
538
+ const resultContent = await loadFile(uatResultFile);
539
+ if (resultContent) {
540
+ const verdictMatch = resultContent.match(/verdict:\s*([\w-]+)/i);
541
+ const verdict = verdictMatch?.[1]?.toLowerCase();
542
+ if (verdict === "pass" || verdict === "passed") return null; // PASS — skip
543
+ // Non-PASS verdict exists — don't re-run UAT, but don't advance either.
544
+ // Return null here since the UAT already ran; the dispatch table's
545
+ // complete-slice rule should check the verdict before advancing.
546
+ // For now, returning the slice signals it still needs attention.
547
+ }
538
548
  }
539
549
 
540
550
  // Classify UAT type; unknown type → treat as human-experience (human review)
@@ -589,14 +599,18 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
589
599
  const { inlinePriorMilestoneSummary } = await import("./files.js");
590
600
  const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
591
601
  if (priorSummaryInline) inlined.push(priorSummaryInline);
592
- if (inlineLevel !== "minimal") {
593
- const projectInline = await inlineProjectFromDb(base);
594
- if (projectInline) inlined.push(projectInline);
595
- const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
596
- if (requirementsInline) inlined.push(requirementsInline);
597
- const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
598
- if (decisionsInline) inlined.push(decisionsInline);
599
- }
602
+ // Build source file paths for the planner to read on demand (reduces inlining)
603
+ const sourcePaths: string[] = [];
604
+ if (existsSync(resolveGsdRootFile(base, "PROJECT")))
605
+ sourcePaths.push(`- **Project**: \`${relGsdRootFile("PROJECT")}\``);
606
+ if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
607
+ sourcePaths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
608
+ if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
609
+ sourcePaths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
610
+ const sourceFilePaths = sourcePaths.length > 0
611
+ ? sourcePaths.join("\n")
612
+ : "_No project/requirements/decisions files found._";
613
+
600
614
  const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
601
615
  if (knowledgeInlinePM) inlined.push(knowledgeInlinePM);
602
616
  inlined.push(inlineTemplate("roadmap", "Roadmap"));
@@ -615,6 +629,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
615
629
 
616
630
  const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
617
631
  const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS"));
632
+ const researchOutputRelPath = relMilestoneFile(base, mid, "RESEARCH");
618
633
  return loadPrompt("plan-milestone", {
619
634
  workingDirectory: base,
620
635
  milestoneId: mid, milestoneTitle: midTitle,
@@ -624,6 +639,9 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
624
639
  outputPath: join(base, outputRelPath),
625
640
  secretsOutputPath,
626
641
  inlinedContext,
642
+ sourceFilePaths,
643
+ researchOutputPath: join(base, researchOutputRelPath),
644
+ ...buildSkillDiscoveryVars(),
627
645
  });
628
646
  }
629
647
 
@@ -686,12 +704,16 @@ export async function buildPlanSlicePrompt(
686
704
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
687
705
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
688
706
  if (researchInline) inlined.push(researchInline);
689
- if (inlineLevel !== "minimal") {
690
- const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
691
- if (decisionsInline) inlined.push(decisionsInline);
692
- const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
693
- if (requirementsInline) inlined.push(requirementsInline);
694
- }
707
+ // Build source file paths for the planner to read on demand (reduces inlining)
708
+ const sliceSourcePaths: string[] = [];
709
+ if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
710
+ sliceSourcePaths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
711
+ if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
712
+ sliceSourcePaths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
713
+ const sliceSourceFilePaths = sliceSourcePaths.length > 0
714
+ ? sliceSourcePaths.join("\n")
715
+ : "_No requirements/decisions files found._";
716
+
695
717
  const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
696
718
  if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
697
719
  inlined.push(inlineTemplate("plan", "Slice Plan"));
@@ -726,6 +748,7 @@ export async function buildPlanSlicePrompt(
726
748
  dependencySummaries: depContent,
727
749
  executorContextConstraints,
728
750
  commitInstruction,
751
+ sourceFilePaths: sliceSourceFilePaths,
729
752
  });
730
753
  }
731
754
 
@@ -35,6 +35,7 @@ import {
35
35
  resolveMilestoneFile,
36
36
  clearPathCache,
37
37
  resolveGsdRootFile,
38
+ gsdRoot,
38
39
  } from "./paths.js";
39
40
  import { isValidationTerminal } from "./state.js";
40
41
  import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
@@ -361,7 +362,7 @@ function isStringArray(data: unknown): data is string[] {
361
362
 
362
363
  /** Path to the persisted completed-unit keys file. */
363
364
  export function completedKeysPath(base: string): string {
364
- return join(base, ".gsd", "completed-units.json");
365
+ return join(gsdRoot(base), "completed-units.json");
365
366
  }
366
367
 
367
368
  /** Write a completed unit key to disk (read-modify-write append to set). */
@@ -16,6 +16,8 @@ import type {
16
16
  import { deriveState } from "./state.js";
17
17
  import { loadFile, getManifestStatus } from "./files.js";
18
18
  import { loadEffectiveGSDPreferences, resolveSkillDiscoveryMode, getIsolationMode } from "./preferences.js";
19
+ import { isInsideWorktree, ensureGsdSymlink } from "./repo-identity.js";
20
+ import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
19
21
  import { sendDesktopNotification } from "./notifications.js";
20
22
  import { sendRemoteNotification } from "../remote-questions/notify.js";
21
23
  import {
@@ -48,7 +50,7 @@ import {
48
50
  getAutoWorktreePath,
49
51
  isInAutoWorktree,
50
52
  } from "./auto-worktree.js";
51
- import { readResourceVersion } from "./auto-worktree-sync.js";
53
+ import { readResourceVersion } from "./resource-version.js";
52
54
  import { initMetrics, getLedger } from "./metrics.js";
53
55
  import { initRoutingHistory } from "./routing-history.js";
54
56
  import { restoreHookState, resetHookState, clearPersistedHookState } from "./post-unit-hooks.js";
@@ -61,7 +63,6 @@ import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath } from "./debug-
61
63
  import type { AutoSession } from "./auto/session.js";
62
64
  import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
63
65
  import { join } from "node:path";
64
- import { sep as pathSep } from "node:path";
65
66
 
66
67
  export interface BootstrapDeps {
67
68
  shouldUseWorktreeIsolation: () => boolean;
@@ -113,8 +114,17 @@ export async function bootstrapAutoSession(
113
114
  ensureGitignore(base, { commitDocs, manageGitignore });
114
115
  if (manageGitignore !== false) untrackRuntimeFiles(base);
115
116
 
117
+ // Migrate legacy in-project .gsd/ to external state directory
118
+ recoverFailedMigration(base);
119
+ const migration = migrateToExternalState(base);
120
+ if (migration.error) {
121
+ ctx.ui.notify(`External state migration warning: ${migration.error}`, "warning");
122
+ }
123
+ // Ensure symlink exists (handles fresh projects and post-migration)
124
+ ensureGsdSymlink(base);
125
+
116
126
  // Bootstrap .gsd/ if it doesn't exist
117
- const gsdDir = join(base, ".gsd");
127
+ const gsdDir = gsdRoot(base);
118
128
  if (!existsSync(gsdDir)) {
119
129
  mkdirSync(join(gsdDir, "milestones"), { recursive: true });
120
130
  if (commitDocs !== false) {
@@ -204,18 +214,6 @@ export async function bootstrapAutoSession(
204
214
 
205
215
  let state = await deriveState(base);
206
216
 
207
- // Stale worktree state recovery (#654)
208
- if (
209
- state.activeMilestone &&
210
- shouldUseWorktreeIsolation() &&
211
- !detectWorktreeName(base)
212
- ) {
213
- const wtPath = getAutoWorktreePath(base, state.activeMilestone.id);
214
- if (wtPath) {
215
- state = await deriveState(wtPath);
216
- }
217
- }
218
-
219
217
  // Milestone branch recovery (#601)
220
218
  let hasSurvivorBranch = false;
221
219
  if (
@@ -223,7 +221,7 @@ export async function bootstrapAutoSession(
223
221
  (state.phase === "pre-planning" || state.phase === "needs-discussion") &&
224
222
  shouldUseWorktreeIsolation() &&
225
223
  !detectWorktreeName(base) &&
226
- !base.includes(`${pathSep}.gsd${pathSep}worktrees${pathSep}`)
224
+ !isInsideWorktree(base)
227
225
  ) {
228
226
  const milestoneBranch = `milestone/${state.activeMilestone.id}`;
229
227
  const { nativeBranchExists } = await import("./native-git-bridge.js");
@@ -333,14 +331,7 @@ export async function bootstrapAutoSession(
333
331
  // ── Auto-worktree setup ──
334
332
  s.originalBasePath = base;
335
333
 
336
- const isUnderGsdWorktrees = (p: string): boolean => {
337
- const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
338
- if (p.includes(marker)) return true;
339
- const worktreesSuffix = `${pathSep}.gsd${pathSep}worktrees`;
340
- return p.endsWith(worktreesSuffix);
341
- };
342
-
343
- if (s.currentMilestoneId && shouldUseWorktreeIsolation() && !detectWorktreeName(base) && !isUnderGsdWorktrees(base)) {
334
+ if (s.currentMilestoneId && shouldUseWorktreeIsolation() && !detectWorktreeName(base) && !isInsideWorktree(base)) {
344
335
  try {
345
336
  const existingWtPath = getAutoWorktreePath(base, s.currentMilestoneId);
346
337
  if (existingWtPath) {
@@ -355,11 +346,6 @@ export async function bootstrapAutoSession(
355
346
  ctx.ui.notify(`Created auto-worktree at ${wtPath}`, "info");
356
347
  }
357
348
  registerSigtermHandler(s.originalBasePath);
358
-
359
- // Load completed keys from BOTH locations
360
- if (s.basePath !== s.originalBasePath) {
361
- loadPersistedKeys(s.basePath, s.completedKeySet);
362
- }
363
349
  } catch (err) {
364
350
  ctx.ui.notify(
365
351
  `Auto-worktree setup failed: ${err instanceof Error ? err.message : String(err)}. Continuing in project root.`,
@@ -369,8 +355,8 @@ export async function bootstrapAutoSession(
369
355
  }
370
356
 
371
357
  // ── DB lifecycle ──
372
- const gsdDbPath = join(s.basePath, ".gsd", "gsd.db");
373
- const gsdDirPath = join(s.basePath, ".gsd");
358
+ const gsdDbPath = join(gsdRoot(s.basePath), "gsd.db");
359
+ const gsdDirPath = gsdRoot(s.basePath);
374
360
  if (existsSync(gsdDirPath) && !existsSync(gsdDbPath)) {
375
361
  const hasDecisions = existsSync(join(gsdDirPath, "DECISIONS.md"));
376
362
  const hasRequirements = existsSync(join(gsdDirPath, "REQUIREMENTS.md"));
@@ -476,7 +462,7 @@ export async function bootstrapAutoSession(
476
462
 
477
463
  // Pre-flight: validate milestone queue
478
464
  try {
479
- const msDir = join(base, ".gsd", "milestones");
465
+ const msDir = join(gsdRoot(base), "milestones");
480
466
  if (existsSync(msDir)) {
481
467
  const milestoneIds = readdirSync(msDir, { withFileTypes: true })
482
468
  .filter(d => d.isDirectory() && /^M\d{3}/.test(d.name))
@@ -6,25 +6,24 @@
6
6
  * manages create, enter, detect, and teardown for auto-mode worktrees.
7
7
  */
8
8
 
9
- import { existsSync, cpSync, readFileSync, readdirSync, mkdirSync, realpathSync, unlinkSync, statSync } from "node:fs";
9
+ import { existsSync, readFileSync, realpathSync, unlinkSync, statSync } from "node:fs";
10
10
  import { isAbsolute, join, sep } from "node:path";
11
11
  import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
12
- import { copyWorktreeDb, reconcileWorktreeDb, isDbAvailable } from "./gsd-db.js";
13
- import { atomicWriteSync } from "./atomic-write.js";
14
12
  import { execSync, execFileSync } from "node:child_process";
15
- import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
16
13
  import {
17
14
  createWorktree,
18
15
  removeWorktree,
19
16
  worktreePath,
20
17
  } from "./worktree-manager.js";
21
18
  import { detectWorktreeName, resolveGitHeadPath, nudgeGitBranchCache } from "./worktree.js";
19
+ import { ensureGsdSymlink } from "./repo-identity.js";
22
20
  import {
23
21
  MergeConflictError,
24
22
  readIntegrationBranch,
25
23
  } from "./git-service.js";
26
24
  import { parseRoadmap } from "./files.js";
27
25
  import { loadEffectiveGSDPreferences } from "./preferences.js";
26
+ import { gsdRoot } from "./paths.js";
28
27
  import {
29
28
  nativeGetCurrentBranch,
30
29
  nativeWorkingTreeStatus,
@@ -103,111 +102,6 @@ export function autoWorktreeBranch(milestoneId: string): string {
103
102
  * to prevent split-brain.
104
103
  */
105
104
 
106
- /**
107
- * Forward-merge plan checkbox state from the project root into a freshly
108
- * re-attached worktree (#778).
109
- *
110
- * When auto-mode stops via crash (not graceful stop), the milestone branch
111
- * HEAD may be behind the filesystem state at the project root because
112
- * syncStateToProjectRoot() runs after every task completion but the final
113
- * git commit may not have happened before the crash. On restart the worktree
114
- * is re-attached to the branch HEAD, which has [ ] for the crashed task,
115
- * causing verifyExpectedArtifact() to fail and triggering an infinite
116
- * dispatch/skip loop.
117
- *
118
- * Fix: after re-attaching, read every *.md plan file in the milestone
119
- * directory at the project root and apply any [x] checkbox states that are
120
- * ahead of the worktree version (forward-only: never downgrade [x] → [ ]).
121
- *
122
- * This is safe because syncStateToProjectRoot() is the authoritative source
123
- * of post-task state at the project root — it writes the same [x] the LLM
124
- * produced, then the auto-commit follows. If the commit never happened, the
125
- * filesystem copy is still valid and correct.
126
- */
127
- function reconcilePlanCheckboxes(projectRoot: string, wtPath: string, milestoneId: string): void {
128
- const srcMilestone = join(projectRoot, ".gsd", "milestones", milestoneId);
129
- const dstMilestone = join(wtPath, ".gsd", "milestones", milestoneId);
130
- if (!existsSync(srcMilestone) || !existsSync(dstMilestone)) return;
131
-
132
- // Walk all markdown files in the milestone directory (plans, summaries, etc.)
133
- function walkMd(dir: string): string[] {
134
- const results: string[] = [];
135
- try {
136
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
137
- const full = join(dir, entry.name);
138
- if (entry.isDirectory()) {
139
- results.push(...walkMd(full));
140
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
141
- results.push(full);
142
- }
143
- }
144
- } catch { /* non-fatal */ }
145
- return results;
146
- }
147
-
148
- for (const srcFile of walkMd(srcMilestone)) {
149
- const rel = srcFile.slice(srcMilestone.length);
150
- const dstFile = dstMilestone + rel;
151
- if (!existsSync(dstFile)) continue; // only reconcile existing files
152
-
153
- let srcContent: string;
154
- let dstContent: string;
155
- try {
156
- srcContent = readFileSync(srcFile, "utf-8");
157
- dstContent = readFileSync(dstFile, "utf-8");
158
- } catch { continue; }
159
-
160
- if (srcContent === dstContent) continue;
161
-
162
- // Extract all checked task IDs from the source (project root)
163
- // Pattern: - [x] **T<id>: or - [x] **S<id>: (case-insensitive x)
164
- const checkedRe = /^- \[[xX]\] \*\*([TS]\d+):/gm;
165
- const srcChecked = new Set<string>();
166
- for (const m of srcContent.matchAll(checkedRe)) srcChecked.add(m[1]);
167
-
168
- if (srcChecked.size === 0) continue;
169
-
170
- // Forward-apply: replace [ ] → [x] for any IDs that are checked in src
171
- let updated = dstContent;
172
- let changed = false;
173
- for (const id of srcChecked) {
174
- const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
175
- const uncheckedRe = new RegExp(`^(- )\\[ \\]( \\*\\*${escapedId}:)`, "gm");
176
- if (uncheckedRe.test(updated)) {
177
- updated = updated.replace(
178
- new RegExp(`^(- )\\[ \\]( \\*\\*${escapedId}:)`, "gm"),
179
- "$1[x]$2",
180
- );
181
- changed = true;
182
- }
183
- }
184
-
185
- if (changed) {
186
- try {
187
- atomicWriteSync(dstFile, updated, "utf-8");
188
- } catch { /* non-fatal */ }
189
- }
190
- }
191
-
192
- // Also forward-merge completed-units.json (set-union)
193
- const srcKeys = join(projectRoot, ".gsd", "completed-units.json");
194
- const dstKeys = join(wtPath, ".gsd", "completed-units.json");
195
- if (existsSync(srcKeys)) {
196
- try {
197
- const src: string[] = JSON.parse(readFileSync(srcKeys, "utf-8"));
198
- let dst: string[] = [];
199
- if (existsSync(dstKeys)) {
200
- try { dst = JSON.parse(readFileSync(dstKeys, "utf-8")); } catch { /* ignore corrupt */ }
201
- }
202
- const merged = [...new Set([...dst, ...src])];
203
- if (merged.length > dst.length) {
204
- mkdirSync(join(wtPath, ".gsd"), { recursive: true });
205
- atomicWriteSync(dstKeys, JSON.stringify(merged), "utf-8");
206
- }
207
- } catch { /* non-fatal */ }
208
- }
209
- }
210
-
211
105
  export function createAutoWorktree(basePath: string, milestoneId: string): string {
212
106
  const branch = autoWorktreeBranch(milestoneId);
213
107
 
@@ -227,32 +121,8 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
227
121
  info = createWorktree(basePath, milestoneId, { branch, startPoint: integrationBranch });
228
122
  }
229
123
 
230
- // Copy .gsd/ planning artifacts from the source repo into the new worktree.
231
- // Worktrees are fresh git checkouts — untracked files don't carry over.
232
- // Planning artifacts may be untracked if the project's .gitignore had a
233
- // blanket .gsd/ rule (pre-v2.14.0). Without this copy, auto-mode loops
234
- // on plan-slice because the plan file doesn't exist in the worktree.
235
- //
236
- // IMPORTANT: Skip when re-attaching to an existing branch (#759).
237
- // The branch checkout already has committed artifacts with correct state
238
- // (e.g. [x] for completed slices). Copying from the project root would
239
- // overwrite them with stale data ([ ] checkboxes) because the root is
240
- // not always fully synced.
241
- if (!branchExists) {
242
- copyPlanningArtifacts(basePath, info.path);
243
- } else {
244
- // Re-attaching to an existing branch: forward-merge any plan checkpoint
245
- // state from the project root into the worktree (#778).
246
- //
247
- // If auto-mode stopped via crash, the milestone branch HEAD may lag behind
248
- // the project root filesystem because syncStateToProjectRoot() ran after
249
- // task completion but the auto-commit never fired. On restart the worktree
250
- // is re-created from the branch HEAD (which has [ ] for the crashed task),
251
- // causing verifyExpectedArtifact() to return false → stale-key eviction →
252
- // infinite dispatch/skip loop. Reconciling here ensures the worktree sees
253
- // the same [x] state that syncStateToProjectRoot() wrote to the root.
254
- reconcilePlanCheckboxes(basePath, info.path, milestoneId);
255
- }
124
+ // Ensure worktree shares external state via symlink
125
+ ensureGsdSymlink(info.path);
256
126
 
257
127
  // Run user-configured post-create hook (#597) — e.g. copy .env, symlink assets
258
128
  const hookError = runWorktreePostCreateHook(basePath, info.path);
@@ -279,36 +149,6 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
279
149
  return info.path;
280
150
  }
281
151
 
282
- /**
283
- * Copy .gsd/ planning artifacts from source repo to a new worktree.
284
- * Copies milestones/, DECISIONS.md, REQUIREMENTS.md, PROJECT.md, QUEUE.md,
285
- * STATE.md, KNOWLEDGE.md, and OVERRIDES.md.
286
- * Skips runtime files (auto.lock, metrics.json, etc.) and the worktrees/ dir.
287
- * Best-effort — failures are non-fatal since auto-mode can recreate artifacts.
288
- */
289
- function copyPlanningArtifacts(srcBase: string, wtPath: string): void {
290
- const srcGsd = join(srcBase, ".gsd");
291
- const dstGsd = join(wtPath, ".gsd");
292
- if (!existsSync(srcGsd)) return;
293
-
294
- // Copy milestones/ directory (planning files, roadmaps, plans, research)
295
- safeCopyRecursive(join(srcGsd, "milestones"), join(dstGsd, "milestones"), { force: true });
296
-
297
- // Copy top-level planning files
298
- for (const file of ["DECISIONS.md", "REQUIREMENTS.md", "PROJECT.md", "QUEUE.md", "STATE.md", "KNOWLEDGE.md", "OVERRIDES.md"]) {
299
- safeCopy(join(srcGsd, file), join(dstGsd, file), { force: true });
300
- }
301
-
302
- // Copy gsd.db if present in source
303
- const srcDb = join(srcGsd, "gsd.db");
304
- const destDb = join(dstGsd, "gsd.db");
305
- if (existsSync(srcDb)) {
306
- try {
307
- copyWorktreeDb(srcDb, destDb);
308
- } catch { /* non-fatal */ }
309
- }
310
- }
311
-
312
152
  /**
313
153
  * Teardown an auto-worktree: chdir back to original base, then remove
314
154
  * the worktree and its branch.
@@ -346,7 +186,7 @@ export function isInAutoWorktree(basePath: string): boolean {
346
186
  // Primary check: use originalBase if available (fast path)
347
187
  if (originalBase) {
348
188
  const resolvedBase = existsSync(basePath) ? realpathSync(basePath) : basePath;
349
- const wtDir = join(resolvedBase, ".gsd", "worktrees");
189
+ const wtDir = join(gsdRoot(resolvedBase), "worktrees");
350
190
  if (!cwd.startsWith(wtDir)) return false;
351
191
  const branch = nativeGetCurrentBranch(cwd);
352
192
  return branch.startsWith("milestone/");
@@ -364,8 +204,8 @@ export function isInAutoWorktree(basePath: string): boolean {
364
204
  // Worktrees have a .git file with "gitdir: ..." pointing to the main repo
365
205
  const gitContent = readFileSync(worktreeMarker, "utf-8").trim();
366
206
  if (!gitContent.startsWith("gitdir:")) return false;
367
- // Verify cwd path contains .gsd/worktrees/
368
- if (!cwd.includes(`${sep}.gsd${sep}worktrees${sep}`) && !cwd.includes("/.gsd/worktrees/")) return false;
207
+ // Verify we're inside a GSD-managed worktree
208
+ if (!detectWorktreeName(cwd)) return false;
369
209
  const branch = nativeGetCurrentBranch(cwd);
370
210
  return branch.startsWith("milestone/");
371
211
  } catch {
@@ -458,7 +298,7 @@ export function getActiveAutoWorktreeContext(): {
458
298
  if (!originalBase) return null;
459
299
  const cwd = process.cwd();
460
300
  const resolvedBase = existsSync(originalBase) ? realpathSync(originalBase) : originalBase;
461
- const wtDir = join(resolvedBase, ".gsd", "worktrees");
301
+ const wtDir = join(gsdRoot(resolvedBase), "worktrees");
462
302
  if (!cwd.startsWith(wtDir)) return null;
463
303
  const worktreeName = detectWorktreeName(cwd);
464
304
  if (!worktreeName) return null;
@@ -518,15 +358,6 @@ export function mergeMilestoneToMain(
518
358
  // 1. Auto-commit dirty state in worktree before leaving
519
359
  autoCommitDirtyState(worktreeCwd);
520
360
 
521
- // Reconcile worktree DB into main DB before leaving worktree context
522
- if (isDbAvailable()) {
523
- try {
524
- const worktreeDbPath = join(worktreeCwd, ".gsd", "gsd.db");
525
- const mainDbPath = join(originalBasePath_, ".gsd", "gsd.db");
526
- reconcileWorktreeDb(mainDbPath, worktreeDbPath);
527
- } catch { /* non-fatal */ }
528
- }
529
-
530
361
  // 2. Parse roadmap for slice listing
531
362
  const roadmap = parseRoadmap(roadmapContent);
532
363
  const completedSlices = roadmap.slices.filter(s => s.done);
@@ -535,11 +366,19 @@ export function mergeMilestoneToMain(
535
366
  const previousCwd = process.cwd();
536
367
  process.chdir(originalBasePath_);
537
368
 
538
- // 3a. Auto-commit any dirty state in the project root that syncStateToProjectRoot
539
- // wrote during execution. Without this, the squash merge can fail with
540
- // "Your local changes to the following files would be overwritten by merge" (#1127).
369
+ // 3a. Auto-commit any dirty state in the project root. Without this, the
370
+ // squash merge can fail with "Your local changes would be overwritten" (#1127).
541
371
  autoCommitDirtyState(originalBasePath_);
542
372
 
373
+ // 3b. Remove untracked .gsd/ files that syncStateToProjectRoot copied.
374
+ // autoCommitDirtyState stages and commits everything (git add -A), but if
375
+ // the project root branch has no .gsd/ tracking (e.g., .gsd/ is gitignored),
376
+ // these files remain untracked and cause "untracked working tree files would
377
+ // be overwritten by merge" during squash-merge (#1237).
378
+ try {
379
+ execFileSync("git", ["clean", "-fd", ".gsd/"], { cwd: originalBasePath_, stdio: "pipe" });
380
+ } catch { /* non-fatal — clean failure shouldn't block merge attempt */ }
381
+
543
382
  // 4. Resolve integration branch — prefer milestone metadata, fall back to preferences / "main"
544
383
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
545
384
  const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
@@ -556,7 +395,7 @@ export function mergeMilestoneToMain(
556
395
  // "Your local changes would be overwritten" (#827).
557
396
  const gsdStateFiles = ["STATE.md", "completed-units.json", "auto.lock"];
558
397
  for (const f of gsdStateFiles) {
559
- const p = join(originalBasePath_, ".gsd", f);
398
+ const p = join(gsdRoot(originalBasePath_), f);
560
399
  try { unlinkSync(p); } catch { /* non-fatal — file may not exist */ }
561
400
  }
562
401
  nativeCheckoutBranch(originalBasePath_, mainBranch);
@@ -69,12 +69,10 @@ import { closeoutUnit } from "./auto-unit-closeout.js";
69
69
  import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
70
70
  import { selectAndApplyModel } from "./auto-model-selection.js";
71
71
  import {
72
- syncProjectRootToWorktree,
73
- syncStateToProjectRoot,
74
72
  readResourceVersion,
75
73
  checkResourcesStale,
76
74
  escapeStaleWorktree,
77
- } from "./auto-worktree-sync.js";
75
+ } from "./resource-version.js";
78
76
  import { initRoutingHistory, resetRoutingHistory, recordOutcome } from "./routing-history.js";
79
77
  import {
80
78
  checkPostUnitHooks,
@@ -180,7 +178,7 @@ import { runPostUnitVerification, type VerificationContext } from "./auto-verifi
180
178
  import { postUnitPreVerification, postUnitPostVerification, type PostUnitContext } from "./auto-post-unit.js";
181
179
  import { bootstrapAutoSession, type BootstrapDeps } from "./auto-start.js";
182
180
 
183
- // Worktree sync, resource staleness, stale worktree escape → auto-worktree-sync.ts
181
+ // Resource staleness, stale worktree escape → resource-version.ts
184
182
 
185
183
  // ─── Session State ─────────────────────────────────────────────────────────
186
184
 
@@ -1018,11 +1016,6 @@ async function dispatchNextUnit(
1018
1016
  // Non-fatal
1019
1017
  }
1020
1018
 
1021
- // ── Sync project root artifacts into worktree ──
1022
- if (s.originalBasePath && s.basePath !== s.originalBasePath && s.currentMilestoneId) {
1023
- syncProjectRootToWorktree(s.originalBasePath, s.basePath, s.currentMilestoneId);
1024
- }
1025
-
1026
1019
  const stopDeriveTimer = debugTime("derive-state");
1027
1020
  let state = await deriveState(s.basePath);
1028
1021
  stopDeriveTimer({
@@ -9,9 +9,10 @@
9
9
  */
10
10
 
11
11
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
12
- import { join, resolve, sep } from "node:path";
12
+ import { join, resolve } from "node:path";
13
13
  import { randomUUID } from "node:crypto";
14
14
  import { gsdRoot } from "./paths.js";
15
+ import { resolveProjectRoot } from "./worktree.js";
15
16
 
16
17
  // ─── Types ────────────────────────────────────────────────────────────────────
17
18
 
@@ -58,15 +59,8 @@ const VALID_CLASSIFICATIONS: readonly string[] = [
58
59
  * directory that contains `.gsd/worktrees/` — that's the project root.
59
60
  */
60
61
  export function resolveCapturesPath(basePath: string): string {
61
- const resolved = resolve(basePath);
62
- const worktreeMarker = `${sep}.gsd${sep}worktrees${sep}`;
63
- const idx = resolved.indexOf(worktreeMarker);
64
- if (idx !== -1) {
65
- // basePath is inside a worktree — resolve to project root
66
- const projectRoot = resolved.slice(0, idx);
67
- return join(projectRoot, ".gsd", CAPTURES_FILENAME);
68
- }
69
- return join(gsdRoot(basePath), CAPTURES_FILENAME);
62
+ const projectRoot = resolveProjectRoot(resolve(basePath));
63
+ return join(gsdRoot(projectRoot), CAPTURES_FILENAME);
70
64
  }
71
65
 
72
66
  // ─── File I/O ─────────────────────────────────────────────────────────────────