gsd-pi 2.77.0-dev.eaa4973bc → 2.78.0-dev.aeeb2ca00

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 (545) hide show
  1. package/README.md +53 -17
  2. package/dist/claude-cli-check.js +46 -10
  3. package/dist/headless.js +49 -4
  4. package/dist/resource-loader.d.ts +40 -0
  5. package/dist/resource-loader.js +32 -13
  6. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  7. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  8. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  9. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  10. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  11. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +72 -16
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/github-sync/templates.js +103 -0
  15. package/dist/resources/extensions/google-search/index.js +3 -2
  16. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  17. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  18. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  19. package/dist/resources/extensions/gsd/auto-dispatch.js +142 -29
  20. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +150 -64
  22. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  23. package/dist/resources/extensions/gsd/auto-recovery.js +197 -48
  24. package/dist/resources/extensions/gsd/auto-start.js +107 -29
  25. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  26. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  27. package/dist/resources/extensions/gsd/auto.js +76 -21
  28. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  30. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  31. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  33. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  34. package/dist/resources/extensions/gsd/component-types.js +69 -0
  35. package/dist/resources/extensions/gsd/context-store.js +23 -7
  36. package/dist/resources/extensions/gsd/detection.js +49 -1
  37. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -17
  38. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  39. package/dist/resources/extensions/gsd/forensics.js +106 -0
  40. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  41. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  42. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  43. package/dist/resources/extensions/gsd/git-service.js +126 -2
  44. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  45. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  46. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  47. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  48. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  49. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  50. package/dist/resources/extensions/gsd/model-router.js +6 -0
  51. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  53. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  54. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  55. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  56. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  58. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  59. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  60. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  61. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  62. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  64. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  65. package/dist/resources/extensions/gsd/state.js +69 -58
  66. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  67. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  68. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  69. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  70. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  71. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  72. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  73. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  74. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  75. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  76. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  77. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  79. package/dist/resources/extensions/gsd/worktree-manager.js +85 -8
  80. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  81. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  82. package/dist/resources/extensions/mcp-client/index.js +3 -1
  83. package/dist/resources/extensions/ollama/index.js +5 -1
  84. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  85. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  88. package/dist/web/standalone/.next/build-manifest.json +2 -2
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  115. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  116. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  117. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  119. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  120. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  121. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  122. package/package.json +2 -3
  123. package/packages/daemon/package.json +2 -2
  124. package/packages/daemon/src/logger.ts +4 -3
  125. package/packages/mcp-server/dist/server.d.ts +24 -0
  126. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  127. package/packages/mcp-server/dist/server.js +88 -87
  128. package/packages/mcp-server/dist/server.js.map +1 -1
  129. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  130. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  131. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  132. package/packages/mcp-server/package.json +2 -2
  133. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  134. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  135. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  136. package/packages/mcp-server/src/server.ts +131 -105
  137. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  138. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  139. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  140. package/packages/native/package.json +2 -2
  141. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  142. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  143. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  144. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  145. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  146. package/packages/pi-agent-core/package.json +1 -1
  147. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  148. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  149. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  150. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  151. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  152. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  153. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  154. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  155. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  156. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  158. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  159. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  160. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  161. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  162. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  163. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  164. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  165. package/packages/pi-ai/dist/models.test.js +36 -11
  166. package/packages/pi-ai/dist/models.test.js.map +1 -1
  167. package/packages/pi-ai/package.json +1 -1
  168. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  169. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  170. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  171. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  172. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  173. package/packages/pi-ai/src/models.test.ts +48 -11
  174. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  175. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  176. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  178. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  180. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  182. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  184. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  186. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  190. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  193. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  195. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  197. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  199. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  201. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  203. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  205. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/index.js +1 -0
  207. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  217. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  226. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  227. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  228. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  229. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  230. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  231. package/packages/pi-coding-agent/package.json +1 -1
  232. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  233. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  234. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  235. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  236. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  237. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  238. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  239. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  240. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  241. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  242. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  243. package/packages/pi-coding-agent/src/index.ts +1 -0
  244. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  245. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  246. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  247. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  248. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  249. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  250. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  251. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  252. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  253. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  254. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  255. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  256. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  257. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +37 -11
  258. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  259. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  260. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  261. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  262. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  263. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  264. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  265. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  266. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  267. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  268. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  269. package/packages/pi-tui/dist/components/editor.js +19 -0
  270. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  271. package/packages/pi-tui/dist/components/image.test.js +6 -5
  272. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  273. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  274. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  275. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  276. package/packages/pi-tui/package.json +1 -1
  277. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  278. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  279. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +42 -11
  280. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  281. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  282. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  283. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  284. package/packages/pi-tui/src/components/editor.ts +22 -0
  285. package/packages/pi-tui/src/components/image.test.ts +10 -5
  286. package/packages/pi-tui/src/editor-component.ts +3 -0
  287. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  288. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  289. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  290. package/packages/rpc-client/package.json +1 -1
  291. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  292. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  293. package/pkg/package.json +1 -1
  294. package/scripts/install.js +15 -1
  295. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  296. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  297. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  298. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  299. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  300. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  301. package/src/resources/extensions/claude-code-cli/readiness.ts +75 -16
  302. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  303. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  304. package/src/resources/extensions/github-sync/templates.ts +151 -0
  305. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  306. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  307. package/src/resources/extensions/google-search/index.ts +3 -2
  308. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  309. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  310. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  311. package/src/resources/extensions/gsd/auto-dispatch.ts +156 -29
  312. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  313. package/src/resources/extensions/gsd/auto-post-unit.ts +163 -73
  314. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  315. package/src/resources/extensions/gsd/auto-recovery.ts +230 -51
  316. package/src/resources/extensions/gsd/auto-start.ts +127 -9
  317. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  318. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  319. package/src/resources/extensions/gsd/auto.ts +90 -23
  320. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  321. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  322. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  323. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  324. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  325. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  326. package/src/resources/extensions/gsd/component-types.ts +362 -0
  327. package/src/resources/extensions/gsd/context-store.ts +25 -8
  328. package/src/resources/extensions/gsd/detection.ts +58 -1
  329. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -20
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  331. package/src/resources/extensions/gsd/forensics.ts +118 -1
  332. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  333. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  334. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  335. package/src/resources/extensions/gsd/git-service.ts +149 -2
  336. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  337. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  338. package/src/resources/extensions/gsd/journal.ts +11 -1
  339. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  340. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  341. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  342. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  343. package/src/resources/extensions/gsd/model-router.ts +6 -0
  344. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  345. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  346. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  347. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  348. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  349. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  350. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  351. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  352. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  353. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  354. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  355. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  356. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  357. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  358. package/src/resources/extensions/gsd/state.ts +76 -66
  359. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  360. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  361. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  362. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  363. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  364. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +133 -292
  365. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  366. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  367. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  368. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  369. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  370. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  371. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  372. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  373. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  374. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  375. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  376. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  377. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  378. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  379. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  380. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  381. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  382. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  383. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  384. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  385. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  386. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  387. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  388. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  389. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  390. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  391. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  392. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  393. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  394. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  395. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  396. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  397. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  398. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +5 -0
  399. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  400. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  401. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  402. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  403. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  404. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  405. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  406. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  407. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  408. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +14 -4
  410. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  411. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  412. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  413. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  414. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  415. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  416. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  417. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  418. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  419. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  420. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  421. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  422. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  423. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  424. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  425. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  426. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  427. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  428. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  429. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  430. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  431. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  432. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  433. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  434. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  435. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  436. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  437. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  438. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  439. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  440. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  442. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  443. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  444. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  445. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  446. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  447. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  448. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  449. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  450. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  451. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  452. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  453. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  454. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  455. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  456. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  457. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  458. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  459. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  460. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  461. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  462. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  463. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  464. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  465. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  466. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  467. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  468. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  469. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  470. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  471. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  472. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  473. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  474. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  475. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  476. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  477. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  478. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  479. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  480. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  481. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  482. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  483. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  484. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  485. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -0
  486. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  487. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  488. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  489. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  490. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  491. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  492. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  493. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  494. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  495. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  496. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  497. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  498. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  499. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  500. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  501. package/src/resources/extensions/gsd/types.ts +3 -3
  502. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  503. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  504. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  505. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  506. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  507. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  508. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  509. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  510. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  511. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  512. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  513. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  514. package/src/resources/extensions/gsd/worktree-manager.ts +108 -7
  515. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  516. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  517. package/src/resources/extensions/mcp-client/index.ts +3 -1
  518. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  519. package/src/resources/extensions/ollama/index.ts +5 -1
  520. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  521. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  522. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  523. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  524. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  525. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  526. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  527. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  528. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  529. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  530. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  531. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  532. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  533. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  534. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  535. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  536. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  537. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  538. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  539. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  540. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  541. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  542. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  543. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  544. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_buildManifest.js +0 -0
  545. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_ssgManifest.js +0 -0
@@ -1,65 +1,244 @@
1
1
  /**
2
2
  * worktree-submodule-safety.test.ts — #2337
3
3
  *
4
- * Worktree teardown (removeWorktree) uses --force which destroys
5
- * uncommitted changes in submodule directories. This test verifies
6
- * that the removal logic detects submodules and preserves their state.
4
+ * The bug (#2337): `git worktree remove --force` destroys uncommitted
5
+ * changes in submodule directories. The fix (in
6
+ * `worktree-manager.removeWorktree`) detects submodules with
7
+ * uncommitted state via `git submodule status`, auto-stashes the
8
+ * worktree before teardown, and attempts non-force removal first —
9
+ * falling back to force only after a stash was taken.
10
+ *
11
+ * This test was previously four `src.includes(...)` source-grep checks
12
+ * that asserted the strings "submodule" / "force" / "--force" appeared
13
+ * in the function body. Test 4 was tautological — it passed whenever
14
+ * both "submodule" and "force" were mentioned anywhere in `removeWorktree`
15
+ * regardless of whether the guard was wired correctly. See #4823 and
16
+ * parent issue #4784.
17
+ *
18
+ * This rewrite builds a real git parent repo + local submodule, creates
19
+ * a worktree, dirties a tracked file inside the submodule, then invokes
20
+ * `removeWorktree` and asserts observable behaviour on stderr (where
21
+ * `logWarning` writes) and on the filesystem (the worktree is gone).
7
22
  */
8
23
 
9
- import { readFileSync } from "node:fs";
24
+ import { describe, test, beforeEach, afterEach } from "node:test";
25
+ import assert from "node:assert/strict";
26
+ import { execFileSync } from "node:child_process";
27
+ import {
28
+ mkdtempSync,
29
+ renameSync,
30
+ rmSync,
31
+ writeFileSync,
32
+ existsSync,
33
+ } from "node:fs";
10
34
  import { join } from "node:path";
11
- import { createTestContext } from "./test-helpers.ts";
12
-
13
- const { assertTrue, report } = createTestContext();
14
-
15
- const srcPath = join(import.meta.dirname, "..", "worktree-manager.ts");
16
- const src = readFileSync(srcPath, "utf-8");
17
-
18
- console.log("\n=== #2337: Worktree teardown preserves submodule state ===");
19
-
20
- // ── Test 1: removeWorktree function exists ──────────────────────────────
21
-
22
- const removeWorktreeIdx = src.indexOf("export function removeWorktree");
23
- assertTrue(removeWorktreeIdx > 0, "worktree-manager.ts exports removeWorktree");
24
-
25
- const fnBody = src.slice(removeWorktreeIdx, removeWorktreeIdx + 6000);
26
-
27
- // ── Test 2: The function checks for submodules before force removal ─────
28
-
29
- const checksSubmodules =
30
- fnBody.includes("submodule") ||
31
- fnBody.includes(".gitmodules");
32
-
33
- assertTrue(
34
- checksSubmodules,
35
- "removeWorktree checks for submodules before force removal (#2337)",
36
- );
37
-
38
- // ── Test 3: Submodule changes are stashed or warned about ───────────────
39
-
40
- const preservesSubmoduleState =
41
- fnBody.includes("stash") ||
42
- fnBody.includes("uncommitted") ||
43
- fnBody.includes("dirty") ||
44
- fnBody.includes("submodule") && (fnBody.includes("warn") || fnBody.includes("preserv"));
45
-
46
- assertTrue(
47
- preservesSubmoduleState,
48
- "removeWorktree preserves or warns about submodule uncommitted changes (#2337)",
49
- );
50
-
51
- // ── Test 4: Force removal is skipped when submodules have changes ───────
52
-
53
- // The key fix: when submodules have dirty state, we should NOT use force
54
- // removal. Instead, use non-force first and fall back to force only after
55
- // submodule state is preserved.
56
- const hasConditionalForce =
57
- fnBody.includes("submodule") &&
58
- (fnBody.includes("force") || fnBody.includes("--force"));
59
-
60
- assertTrue(
61
- hasConditionalForce,
62
- "removeWorktree has conditional force logic around submodules (#2337)",
63
- );
64
-
65
- report();
35
+ import { tmpdir } from "node:os";
36
+
37
+ import { createWorktree, removeWorktree } from "../worktree-manager.ts";
38
+
39
+ interface Harness {
40
+ parent: string;
41
+ subSrc: string;
42
+ cleanup: () => void;
43
+ }
44
+
45
+ function runGit(cwd: string, args: string[]): string {
46
+ return execFileSync("git", args, {
47
+ cwd,
48
+ stdio: ["ignore", "pipe", "pipe"],
49
+ encoding: "utf-8",
50
+ env: {
51
+ ...process.env,
52
+ // Disable user config from polluting the test environment.
53
+ GIT_CONFIG_GLOBAL: "/dev/null",
54
+ GIT_CONFIG_SYSTEM: "/dev/null",
55
+ GIT_AUTHOR_NAME: "gsd-test",
56
+ GIT_AUTHOR_EMAIL: "gsd-test@example.com",
57
+ GIT_COMMITTER_NAME: "gsd-test",
58
+ GIT_COMMITTER_EMAIL: "gsd-test@example.com",
59
+ // Allow local-path submodule URLs. Git 2.38.1+ blocks the `file` transport
60
+ // in `submodule add` by default (CVE-2022-39253). The config also has to
61
+ // propagate to git subprocess env for submodule cloning.
62
+ GIT_ALLOW_PROTOCOL: "file",
63
+ },
64
+ }).trim();
65
+ }
66
+
67
+ function makeHarness(): Harness {
68
+ // Two real git repos: the parent that we will create worktrees of, and
69
+ // the `subSrc` repo that we will add as a submodule.
70
+ const parent = mkdtempSync(join(tmpdir(), "worktree-submodule-parent-"));
71
+ const subSrc = mkdtempSync(join(tmpdir(), "worktree-submodule-source-"));
72
+
73
+ // Bootstrap the submodule source with one committed file.
74
+ runGit(subSrc, ["init", "-b", "main"]);
75
+ writeFileSync(join(subSrc, "tracked.txt"), "initial\n", "utf-8");
76
+ runGit(subSrc, ["add", "."]);
77
+ runGit(subSrc, ["commit", "-m", "initial"]);
78
+
79
+ // Bootstrap the parent with one commit so it has a HEAD for worktrees.
80
+ runGit(parent, ["init", "-b", "main"]);
81
+ // Allow local file:// URLs for submodule add (git 2.38+ blocks by default).
82
+ runGit(parent, ["config", "protocol.file.allow", "always"]);
83
+ writeFileSync(join(parent, "README.md"), "parent\n", "utf-8");
84
+ runGit(parent, ["add", "."]);
85
+ runGit(parent, ["commit", "-m", "initial"]);
86
+
87
+ // Register the subSrc as a submodule named "sub" inside the parent.
88
+ // Use `file://` URL so `protocol.file.allow` governs access; plain path
89
+ // would be blocked by git's local-filesystem safety checks regardless.
90
+ runGit(parent, [
91
+ "-c",
92
+ "protocol.file.allow=always",
93
+ "submodule",
94
+ "add",
95
+ `file://${subSrc}`,
96
+ "sub",
97
+ ]);
98
+ runGit(parent, ["commit", "-m", "add submodule"]);
99
+
100
+ return {
101
+ parent,
102
+ subSrc,
103
+ cleanup: () => {
104
+ for (const dir of [parent, subSrc]) {
105
+ try {
106
+ rmSync(dir, { recursive: true, force: true });
107
+ } catch {
108
+ // Best-effort cleanup.
109
+ }
110
+ }
111
+ },
112
+ };
113
+ }
114
+
115
+ /** Capture stderr writes during `fn`. `logWarning` writes to stderr, so
116
+ * this reveals warnings emitted by the worktree-manager. */
117
+ function captureStderr(fn: () => void): string {
118
+ const streamAny = process.stderr as unknown as {
119
+ write: (chunk: string | Uint8Array, ...rest: unknown[]) => boolean;
120
+ };
121
+ const original = streamAny.write.bind(streamAny);
122
+ const chunks: string[] = [];
123
+ streamAny.write = (chunk: string | Uint8Array): boolean => {
124
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf-8"));
125
+ return true;
126
+ };
127
+ try {
128
+ fn();
129
+ } finally {
130
+ streamAny.write = original;
131
+ }
132
+ return chunks.join("");
133
+ }
134
+
135
+ describe("removeWorktree preserves submodule uncommitted state (#2337)", () => {
136
+ let h: Harness;
137
+
138
+ beforeEach(() => {
139
+ h = makeHarness();
140
+ });
141
+
142
+ afterEach(() => {
143
+ h.cleanup();
144
+ });
145
+
146
+ test("clean submodules: worktree removes without stashing or submodule warnings", () => {
147
+ const wt = createWorktree(h.parent, "cleanwt");
148
+ runGit(wt.path, ["submodule", "update", "--init", "--recursive"]);
149
+
150
+ const stderr = captureStderr(() => {
151
+ removeWorktree(h.parent, "cleanwt");
152
+ });
153
+
154
+ assert.ok(!existsSync(wt.path), "worktree directory should be gone");
155
+ assert.doesNotMatch(
156
+ stderr,
157
+ /Saved uncommitted submodule changes to rescue branch/,
158
+ "clean submodule must not trigger rescue-branch creation",
159
+ );
160
+ assert.doesNotMatch(
161
+ stderr,
162
+ /Submodule rescue branch creation failed/,
163
+ "clean submodule must not trigger rescue-branch failure warning",
164
+ );
165
+ });
166
+
167
+ test("diverged submodule HEAD: removeWorktree detects and warns before tearing down", () => {
168
+ // The code's detection key is `git submodule status` output lines
169
+ // starting with `+` (HEAD diverged from parent's recorded SHA) or
170
+ // `-` (not initialised). Note this is NARROWER than #2337's
171
+ // description ("uncommitted changes") — a plain working-tree edit
172
+ // inside the submodule does NOT trigger the detection. That gap is
173
+ // tracked separately; this test exercises the code path that the
174
+ // current implementation actually guards.
175
+ const wt = createWorktree(h.parent, "dirtywt");
176
+ runGit(wt.path, ["submodule", "update", "--init", "--recursive"]);
177
+
178
+ // Write a new file, commit it inside the submodule — this moves the
179
+ // submodule's HEAD ahead of the parent's recorded SHA, yielding a
180
+ // `+` prefix in `git submodule status`.
181
+ const subPath = join(wt.path, "sub");
182
+ writeFileSync(join(subPath, "new-file.txt"), "divergence\n", "utf-8");
183
+ runGit(subPath, ["add", "."]);
184
+ runGit(subPath, ["commit", "-m", "divergence commit"]);
185
+
186
+ // Sanity: submodule status in the parent worktree must now show `+`.
187
+ const subStatus = runGit(wt.path, ["submodule", "status"]);
188
+ assert.match(
189
+ subStatus,
190
+ /^\+/,
191
+ `precondition: submodule must be diverged, got: "${subStatus}"`,
192
+ );
193
+
194
+ const stderr = captureStderr(() => {
195
+ removeWorktree(h.parent, "dirtywt");
196
+ });
197
+
198
+ // Worktree is gone: force fallback succeeded.
199
+ assert.ok(!existsSync(wt.path), "worktree directory should be removed");
200
+
201
+ // The code path that fires on dirty submodules emits one of these
202
+ // two warnings. Either indicates the detection ran — the ONLY
203
+ // observable that would fail if someone removed the
204
+ // `if (hasSubmoduleChanges)` branch and went straight to force
205
+ // removal. This is the assertion that #4823 demanded in place of
206
+ // the tautological `src.includes("submodule") && src.includes("force")`.
207
+ assert.match(
208
+ stderr,
209
+ /Saved uncommitted submodule changes to rescue branch|Submodule rescue branch creation failed|Submodule changes detected/,
210
+ "dirty submodule must trigger detection-and-warning path",
211
+ );
212
+ });
213
+
214
+ test("missing .gitmodules: detection short-circuits even when submodule content is dirty", () => {
215
+ // Prove the detection is file-based, not identifier-based. If the
216
+ // .gitmodules file is absent, the detection branch must NOT fire
217
+ // regardless of whether "submodule" appears elsewhere in the source.
218
+ const wt = createWorktree(h.parent, "no-gitmodules");
219
+ runGit(wt.path, ["submodule", "update", "--init", "--recursive"]);
220
+
221
+ writeFileSync(
222
+ join(wt.path, "sub", "tracked.txt"),
223
+ "modified\n",
224
+ "utf-8",
225
+ );
226
+
227
+ // Hide .gitmodules so existsSync(gitmodulesPath) returns false.
228
+ const modPath = join(wt.path, ".gitmodules");
229
+ const hiddenPath = join(wt.path, ".gitmodules.hidden");
230
+ if (existsSync(modPath)) {
231
+ renameSync(modPath, hiddenPath);
232
+ }
233
+
234
+ const stderr = captureStderr(() => {
235
+ removeWorktree(h.parent, "no-gitmodules");
236
+ });
237
+
238
+ assert.doesNotMatch(
239
+ stderr,
240
+ /Saved uncommitted submodule changes to rescue branch/,
241
+ "missing .gitmodules should skip submodule detection entirely",
242
+ );
243
+ });
244
+ });
@@ -10,14 +10,10 @@
10
10
  *
11
11
  * 2. completed-units.json is not forward-synced from project root to
12
12
  * worktree, so the worktree never learns about already-completed units.
13
- *
14
- * Covers:
15
- * - syncProjectRootToWorktree does NOT overwrite existing worktree files
16
- * - syncProjectRootToWorktree copies files missing from the worktree
17
- * - completed-units.json is forward-synced from project root to worktree
18
- * - completed-units.json sync uses force:true (project root is authoritative)
19
13
  */
20
14
 
15
+ import { test } from "node:test";
16
+ import assert from "node:assert/strict";
21
17
  import {
22
18
  mkdtempSync,
23
19
  mkdirSync,
@@ -30,9 +26,6 @@ import { join } from "node:path";
30
26
  import { tmpdir } from "node:os";
31
27
 
32
28
  import { syncProjectRootToWorktree } from "../auto-worktree.ts";
33
- import { createTestContext } from "./test-helpers.ts";
34
-
35
- const { assertTrue, assertEq, report } = createTestContext();
36
29
 
37
30
  function createBase(name: string): string {
38
31
  const base = mkdtempSync(join(tmpdir(), `gsd-wt-1886-${name}-`));
@@ -40,165 +33,124 @@ function createBase(name: string): string {
40
33
  return base;
41
34
  }
42
35
 
43
- function cleanup(base: string): void {
44
- rmSync(base, { recursive: true, force: true });
36
+ function registerBases(
37
+ t: { after: (fn: () => void) => void },
38
+ ...dirs: string[]
39
+ ): void {
40
+ t.after(() => {
41
+ for (const d of dirs) {
42
+ rmSync(d, { recursive: true, force: true });
43
+ }
44
+ });
45
45
  }
46
46
 
47
- async function main(): Promise<void> {
48
- // ─── 1. Worktree VALIDATION.md must NOT be overwritten by project root ──
49
- console.log(
50
- "\n=== 1. #1886: worktree VALIDATION.md preserved (not overwritten) ===",
47
+ test("#1886: worktree VALIDATION.md is not overwritten by project root sync", (t) => {
48
+ const mainBase = createBase("main");
49
+ const wtBase = createBase("wt");
50
+ registerBases(t, mainBase, wtBase);
51
+
52
+ // Project root has an older CONTEXT but no VALIDATION
53
+ const prM004 = join(mainBase, ".gsd", "milestones", "M004");
54
+ mkdirSync(prM004, { recursive: true });
55
+ writeFileSync(join(prM004, "M004-CONTEXT.md"), "# old context");
56
+
57
+ // Worktree has CONTEXT + VALIDATION (written by validate-milestone)
58
+ const wtM004 = join(wtBase, ".gsd", "milestones", "M004");
59
+ mkdirSync(wtM004, { recursive: true });
60
+ writeFileSync(join(wtM004, "M004-CONTEXT.md"), "# worktree context");
61
+ writeFileSync(
62
+ join(wtM004, "M004-VALIDATION.md"),
63
+ "verdict: pass\nremediation_round: 1",
51
64
  );
52
- {
53
- const mainBase = createBase("main");
54
- const wtBase = createBase("wt");
55
-
56
- try {
57
- // Project root has an older CONTEXT but no VALIDATION
58
- const prM004 = join(mainBase, ".gsd", "milestones", "M004");
59
- mkdirSync(prM004, { recursive: true });
60
- writeFileSync(join(prM004, "M004-CONTEXT.md"), "# old context");
61
-
62
- // Worktree has CONTEXT + VALIDATION (written by validate-milestone)
63
- const wtM004 = join(wtBase, ".gsd", "milestones", "M004");
64
- mkdirSync(wtM004, { recursive: true });
65
- writeFileSync(join(wtM004, "M004-CONTEXT.md"), "# worktree context");
66
- writeFileSync(
67
- join(wtM004, "M004-VALIDATION.md"),
68
- "verdict: pass\nremediation_round: 1",
69
- );
70
-
71
- syncProjectRootToWorktree(mainBase, wtBase, "M004");
72
-
73
- // VALIDATION.md must still exist in worktree
74
- assertTrue(
75
- existsSync(join(wtM004, "M004-VALIDATION.md")),
76
- "#1886: VALIDATION.md still exists after sync",
77
- );
78
- assertEq(
79
- readFileSync(join(wtM004, "M004-VALIDATION.md"), "utf-8"),
80
- "verdict: pass\nremediation_round: 1",
81
- "#1886: VALIDATION.md content preserved",
82
- );
83
-
84
- // CONTEXT.md should NOT be overwritten — worktree version is authoritative
85
- assertEq(
86
- readFileSync(join(wtM004, "M004-CONTEXT.md"), "utf-8"),
87
- "# worktree context",
88
- "#1886: existing worktree CONTEXT.md not overwritten",
89
- );
90
- } finally {
91
- cleanup(mainBase);
92
- cleanup(wtBase);
93
- }
94
- }
95
-
96
- // ─── 2. Missing files ARE still copied from project root ────────────────
97
- console.log("\n=== 2. #1886: missing worktree files still copied ===");
98
- {
99
- const mainBase = createBase("main");
100
- const wtBase = createBase("wt");
101
-
102
- try {
103
- const prM004 = join(mainBase, ".gsd", "milestones", "M004");
104
- mkdirSync(prM004, { recursive: true });
105
- writeFileSync(join(prM004, "M004-CONTEXT.md"), "# from project root");
106
- writeFileSync(join(prM004, "M004-ROADMAP.md"), "# roadmap");
107
-
108
- // Worktree has no M004 directory at all
109
- syncProjectRootToWorktree(mainBase, wtBase, "M004");
110
-
111
- assertTrue(
112
- existsSync(join(wtBase, ".gsd", "milestones", "M004", "M004-CONTEXT.md")),
113
- "#1886: missing CONTEXT.md copied from project root",
114
- );
115
- assertTrue(
116
- existsSync(join(wtBase, ".gsd", "milestones", "M004", "M004-ROADMAP.md")),
117
- "#1886: missing ROADMAP.md copied from project root",
118
- );
119
- } finally {
120
- cleanup(mainBase);
121
- cleanup(wtBase);
122
- }
123
- }
124
65
 
125
- // ─── 3. completed-units.json forward-synced from project root ───────────
126
- console.log(
127
- "\n=== 3. #1886: completed-units.json forward-synced to worktree ===",
66
+ syncProjectRootToWorktree(mainBase, wtBase, "M004");
67
+
68
+ assert.ok(
69
+ existsSync(join(wtM004, "M004-VALIDATION.md")),
70
+ "VALIDATION.md still exists after sync",
128
71
  );
129
- {
130
- const mainBase = createBase("main");
131
- const wtBase = createBase("wt");
132
-
133
- try {
134
- // Project root has completed units (authoritative after crash recovery)
135
- writeFileSync(
136
- join(mainBase, ".gsd", "completed-units.json"),
137
- JSON.stringify(["validate-milestone/M004"]),
138
- );
139
-
140
- // Worktree has empty completed-units
141
- writeFileSync(
142
- join(wtBase, ".gsd", "completed-units.json"),
143
- JSON.stringify([]),
144
- );
145
-
146
- syncProjectRootToWorktree(mainBase, wtBase, "M004");
147
-
148
- const wtCompleted = JSON.parse(
149
- readFileSync(join(wtBase, ".gsd", "completed-units.json"), "utf-8"),
150
- );
151
- assertEq(
152
- wtCompleted,
153
- ["validate-milestone/M004"],
154
- "#1886: completed-units.json synced from project root (force:true)",
155
- );
156
- } finally {
157
- cleanup(mainBase);
158
- cleanup(wtBase);
159
- }
160
- }
72
+ assert.equal(
73
+ readFileSync(join(wtM004, "M004-VALIDATION.md"), "utf-8"),
74
+ "verdict: pass\nremediation_round: 1",
75
+ "VALIDATION.md content preserved",
76
+ );
77
+ assert.equal(
78
+ readFileSync(join(wtM004, "M004-CONTEXT.md"), "utf-8"),
79
+ "# worktree context",
80
+ "existing worktree CONTEXT.md not overwritten",
81
+ );
82
+ });
83
+
84
+ test("#1886: missing worktree files are still copied from project root", (t) => {
85
+ const mainBase = createBase("main");
86
+ const wtBase = createBase("wt");
87
+ registerBases(t, mainBase, wtBase);
88
+
89
+ const prM004 = join(mainBase, ".gsd", "milestones", "M004");
90
+ mkdirSync(prM004, { recursive: true });
91
+ writeFileSync(join(prM004, "M004-CONTEXT.md"), "# from project root");
92
+ writeFileSync(join(prM004, "M004-ROADMAP.md"), "# roadmap");
161
93
 
162
- // ─── 4. completed-units.json: no-op when project root has no file ───────
163
- console.log(
164
- "\n=== 4. #1886: completed-units.json no-op when missing in project root ===",
94
+ syncProjectRootToWorktree(mainBase, wtBase, "M004");
95
+
96
+ assert.ok(
97
+ existsSync(join(wtBase, ".gsd", "milestones", "M004", "M004-CONTEXT.md")),
98
+ "missing CONTEXT.md copied from project root",
165
99
  );
166
- {
167
- const mainBase = createBase("main");
168
- const wtBase = createBase("wt");
169
-
170
- try {
171
- // Project root milestone dir must exist for sync to run
172
- const prM004 = join(mainBase, ".gsd", "milestones", "M004");
173
- mkdirSync(prM004, { recursive: true });
174
-
175
- // No completed-units.json in project root
176
- // Worktree has its own
177
- writeFileSync(
178
- join(wtBase, ".gsd", "completed-units.json"),
179
- JSON.stringify(["some-unit/M001"]),
180
- );
181
-
182
- syncProjectRootToWorktree(mainBase, wtBase, "M004");
183
-
184
- const wtCompleted = JSON.parse(
185
- readFileSync(join(wtBase, ".gsd", "completed-units.json"), "utf-8"),
186
- );
187
- assertEq(
188
- wtCompleted,
189
- ["some-unit/M001"],
190
- "#1886: worktree completed-units.json untouched when project root has none",
191
- );
192
- } finally {
193
- cleanup(mainBase);
194
- cleanup(wtBase);
195
- }
196
- }
100
+ assert.ok(
101
+ existsSync(join(wtBase, ".gsd", "milestones", "M004", "M004-ROADMAP.md")),
102
+ "missing ROADMAP.md copied from project root",
103
+ );
104
+ });
197
105
 
198
- report();
199
- }
106
+ test("#1886: completed-units.json is forward-synced to worktree", (t) => {
107
+ const mainBase = createBase("main");
108
+ const wtBase = createBase("wt");
109
+ registerBases(t, mainBase, wtBase);
110
+
111
+ writeFileSync(
112
+ join(mainBase, ".gsd", "completed-units.json"),
113
+ JSON.stringify(["validate-milestone/M004"]),
114
+ );
115
+ writeFileSync(
116
+ join(wtBase, ".gsd", "completed-units.json"),
117
+ JSON.stringify([]),
118
+ );
119
+
120
+ syncProjectRootToWorktree(mainBase, wtBase, "M004");
121
+
122
+ const wtCompleted = JSON.parse(
123
+ readFileSync(join(wtBase, ".gsd", "completed-units.json"), "utf-8"),
124
+ );
125
+ assert.deepEqual(
126
+ wtCompleted,
127
+ ["validate-milestone/M004"],
128
+ "completed-units.json synced from project root (force:true)",
129
+ );
130
+ });
131
+
132
+ test("#1886: worktree completed-units.json untouched when project root has none", (t) => {
133
+ const mainBase = createBase("main");
134
+ const wtBase = createBase("wt");
135
+ registerBases(t, mainBase, wtBase);
136
+
137
+ // Project root milestone dir must exist for sync to run
138
+ const prM004 = join(mainBase, ".gsd", "milestones", "M004");
139
+ mkdirSync(prM004, { recursive: true });
140
+
141
+ writeFileSync(
142
+ join(wtBase, ".gsd", "completed-units.json"),
143
+ JSON.stringify(["some-unit/M001"]),
144
+ );
145
+
146
+ syncProjectRootToWorktree(mainBase, wtBase, "M004");
200
147
 
201
- main().catch((error) => {
202
- console.error(error);
203
- process.exit(1);
148
+ const wtCompleted = JSON.parse(
149
+ readFileSync(join(wtBase, ".gsd", "completed-units.json"), "utf-8"),
150
+ );
151
+ assert.deepEqual(
152
+ wtCompleted,
153
+ ["some-unit/M001"],
154
+ "worktree completed-units.json untouched when project root has none",
155
+ );
204
156
  });