gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6

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 (368) hide show
  1. package/dist/headless.js +25 -4
  2. package/dist/resource-loader.d.ts +40 -0
  3. package/dist/resource-loader.js +32 -13
  4. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  5. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  6. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  7. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  8. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  9. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +5 -18
  11. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
  14. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  15. package/dist/resources/extensions/gsd/auto-start.js +75 -24
  16. package/dist/resources/extensions/gsd/auto.js +34 -0
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  18. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
  19. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  20. package/dist/resources/extensions/gsd/component-types.js +69 -0
  21. package/dist/resources/extensions/gsd/context-store.js +23 -7
  22. package/dist/resources/extensions/gsd/detection.js +49 -1
  23. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  24. package/dist/resources/extensions/gsd/forensics.js +106 -0
  25. package/dist/resources/extensions/gsd/gsd-db.js +1 -1
  26. package/dist/resources/extensions/gsd/guided-flow.js +2 -4
  27. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  28. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  29. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  30. package/dist/resources/extensions/gsd/model-router.js +6 -0
  31. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  32. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  33. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  34. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  35. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  36. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  37. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  38. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  39. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  40. package/dist/resources/extensions/gsd/unit-context-manifest.js +334 -0
  41. package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
  42. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  43. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  44. package/dist/resources/extensions/mcp-client/index.js +3 -1
  45. package/dist/resources/extensions/ollama/index.js +5 -1
  46. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  47. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  48. package/dist/web/standalone/.next/BUILD_ID +1 -1
  49. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  50. package/dist/web/standalone/.next/build-manifest.json +2 -2
  51. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  76. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  77. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  79. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  80. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  81. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  82. package/package.json +1 -3
  83. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  84. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  85. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  86. package/packages/native/package.json +1 -1
  87. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  88. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  89. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  90. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  91. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  92. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  93. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  94. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  95. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  96. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  98. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  99. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  100. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  101. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  102. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  103. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  104. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  105. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  106. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  107. package/packages/pi-ai/dist/models.test.js +29 -11
  108. package/packages/pi-ai/dist/models.test.js.map +1 -1
  109. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  110. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  111. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  112. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  113. package/packages/pi-ai/src/models.test.ts +39 -11
  114. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  115. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  116. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  118. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  120. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  122. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  124. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  126. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  128. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  130. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  133. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  135. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  137. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  139. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  140. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  141. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  143. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/index.js +1 -0
  147. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  156. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  157. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  158. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  159. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  160. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  161. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  163. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  164. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  165. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  166. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  167. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  168. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  169. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  170. package/packages/pi-coding-agent/src/index.ts +1 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  172. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  173. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  174. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  175. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  176. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  177. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  178. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  179. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  180. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  181. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  182. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  183. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  184. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  185. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  186. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  187. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  188. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  189. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  190. package/packages/pi-tui/dist/components/image.test.js +6 -5
  191. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  192. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  193. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  194. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  195. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  196. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  197. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  198. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  199. package/packages/pi-tui/src/components/image.test.ts +10 -5
  200. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  201. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  202. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  203. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  204. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  205. package/scripts/install.js +15 -1
  206. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  207. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  208. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  209. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  210. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  211. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  212. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -72
  213. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  214. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  215. package/src/resources/extensions/gsd/auto/phases.ts +6 -17
  216. package/src/resources/extensions/gsd/auto/session.ts +7 -0
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
  218. package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
  219. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  220. package/src/resources/extensions/gsd/auto-start.ts +97 -4
  221. package/src/resources/extensions/gsd/auto.ts +37 -0
  222. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  223. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
  224. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  225. package/src/resources/extensions/gsd/component-types.ts +362 -0
  226. package/src/resources/extensions/gsd/context-store.ts +25 -8
  227. package/src/resources/extensions/gsd/detection.ts +58 -1
  228. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  229. package/src/resources/extensions/gsd/forensics.ts +118 -1
  230. package/src/resources/extensions/gsd/git-service.ts +16 -0
  231. package/src/resources/extensions/gsd/gsd-db.ts +1 -1
  232. package/src/resources/extensions/gsd/guided-flow.ts +2 -4
  233. package/src/resources/extensions/gsd/journal.ts +11 -1
  234. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  235. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  236. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  237. package/src/resources/extensions/gsd/model-router.ts +6 -0
  238. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  239. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  240. package/src/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  241. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  242. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  243. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  244. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  245. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
  247. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  248. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  249. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  250. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  251. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  252. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  253. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  254. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  255. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  256. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  257. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  258. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  259. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  260. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  261. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -4
  262. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  263. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  264. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  265. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  266. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  267. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  268. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  269. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  270. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  271. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  272. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  273. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  274. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  275. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  276. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1 -1
  277. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  278. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  279. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  280. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  281. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  282. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  283. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  284. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  285. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  287. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  288. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  289. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  290. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  291. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  292. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  294. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +22 -16
  295. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  296. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  297. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  298. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  299. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  300. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  301. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  303. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  304. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  305. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  306. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  307. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  308. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  309. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  310. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  311. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  312. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  313. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  314. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  315. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  316. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  317. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  318. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  319. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  320. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -0
  321. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  322. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  323. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  324. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  325. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  326. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  327. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  328. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  329. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  330. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  331. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  332. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  333. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  334. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  335. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  336. package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
  337. package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
  338. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  339. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  340. package/src/resources/extensions/mcp-client/index.ts +3 -1
  341. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  342. package/src/resources/extensions/ollama/index.ts +5 -1
  343. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  344. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  345. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  346. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  347. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  348. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  349. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  350. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  351. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  352. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  353. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  354. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  355. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  356. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  357. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  358. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  359. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  360. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  361. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  362. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  363. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  364. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  365. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  366. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  367. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
  368. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
@@ -0,0 +1,175 @@
1
+ // GSD2 + skill-manifest — per-unit-type skill allowlist resolver (RFC #4779)
2
+ //
3
+ // Each auto-mode unit type can declare which skills are relevant to it. This
4
+ // trims the set of skills considered for activation in the per-unit prompt,
5
+ // reducing prompt token bloat and sharpening model focus.
6
+ //
7
+ // Contract:
8
+ // - Unknown unit types fall through to "all skills" (current behavior).
9
+ // - A manifest entry referencing a skill that is not installed is a silent
10
+ // no-op at filter time — the filter passes through installed skills only.
11
+ // - The allowlist is an inclusion list: only skills whose normalized name
12
+ // appears in the allowlist are retained. Order is not preserved.
13
+ //
14
+ // Phase 1 scope: seed manifests for a small number of unit types as proof.
15
+ // Additional unit types can be added incrementally; each addition is a pure
16
+ // data change with no wiring cost.
17
+
18
+ import { logWarning } from "./workflow-logger.js";
19
+
20
+ /** Normalize a skill reference the same way callers do (lowercase, trim). */
21
+ function normalize(name: string): string {
22
+ return name.trim().toLowerCase();
23
+ }
24
+
25
+ /**
26
+ * Allowlist per unit type. Keys match unit type identifiers used by auto-mode
27
+ * dispatch. Values are normalized skill names.
28
+ *
29
+ * Wildcard semantics: a unit type absent from this map resolves to `null`
30
+ * (wildcard) — meaning "all installed skills are eligible". Prefer absence
31
+ * over an exhaustive list when uncertain.
32
+ */
33
+ const UNIT_TYPE_SKILL_MANIFEST: Record<string, string[]> = {
34
+ // Milestone-level planning / meta flows — predictable skill sets.
35
+ "research-milestone": [
36
+ "write-docs",
37
+ "write-milestone-brief",
38
+ "decompose-into-slices",
39
+ "grill-me",
40
+ "design-an-interface",
41
+ "api-design",
42
+ "observability",
43
+ ],
44
+ "plan-milestone": [
45
+ "write-milestone-brief",
46
+ "decompose-into-slices",
47
+ "design-an-interface",
48
+ "grill-me",
49
+ "write-docs",
50
+ "api-design",
51
+ "tdd",
52
+ "verify-before-complete",
53
+ ],
54
+ "complete-milestone": [
55
+ "verify-before-complete",
56
+ "write-docs",
57
+ "handoff",
58
+ "forensics",
59
+ "observability",
60
+ "security-review",
61
+ ],
62
+ "validate-milestone": [
63
+ "verify-before-complete",
64
+ "review",
65
+ "test",
66
+ "lint",
67
+ "security-review",
68
+ "accessibility",
69
+ "forensics",
70
+ "observability",
71
+ ],
72
+ "reassess-roadmap": [
73
+ "decompose-into-slices",
74
+ "grill-me",
75
+ "write-milestone-brief",
76
+ "write-docs",
77
+ "forensics",
78
+ ],
79
+ // Slice-level research / planning.
80
+ "research-slice": [
81
+ "write-docs",
82
+ "decompose-into-slices",
83
+ "design-an-interface",
84
+ "grill-me",
85
+ "api-design",
86
+ "observability",
87
+ ],
88
+ "plan-slice": [
89
+ "decompose-into-slices",
90
+ "design-an-interface",
91
+ "grill-me",
92
+ "write-docs",
93
+ "api-design",
94
+ "tdd",
95
+ "verify-before-complete",
96
+ ],
97
+ "refine-slice": [
98
+ "decompose-into-slices",
99
+ "design-an-interface",
100
+ "grill-me",
101
+ "write-docs",
102
+ "api-design",
103
+ "tdd",
104
+ "verify-before-complete",
105
+ ],
106
+ "replan-slice": [
107
+ "decompose-into-slices",
108
+ "grill-me",
109
+ "design-an-interface",
110
+ "write-docs",
111
+ "api-design",
112
+ ],
113
+ "run-uat": [
114
+ "verify-before-complete",
115
+ "test",
116
+ "review",
117
+ "accessibility",
118
+ ],
119
+ // `execute-task` intentionally omitted — implementation hot path covers a
120
+ // wide surface of technologies; wildcard fallback preserves today's
121
+ // behavior until per-task skill hints can be derived from task-plan
122
+ // frontmatter. See RFC #4779.
123
+ };
124
+
125
+ /**
126
+ * Resolve the skill allowlist for a unit type.
127
+ *
128
+ * @returns Array of normalized skill names when an entry exists, or `null`
129
+ * when the unit type is unknown (wildcard — caller should not filter).
130
+ */
131
+ export function resolveSkillManifest(unitType: string | undefined): string[] | null {
132
+ if (!unitType) return null;
133
+ const entry = UNIT_TYPE_SKILL_MANIFEST[unitType];
134
+ if (!entry) return null;
135
+ return entry.map(normalize);
136
+ }
137
+
138
+ /**
139
+ * Filter a skill list by the manifest for `unitType`. Pass-through when the
140
+ * manifest is wildcard (unknown unit type) or `unitType` is undefined.
141
+ */
142
+ export function filterSkillsByManifest<T extends { name: string }>(
143
+ skills: T[],
144
+ unitType: string | undefined,
145
+ ): T[] {
146
+ const allowlist = resolveSkillManifest(unitType);
147
+ if (allowlist === null) return skills;
148
+ const allowed = new Set(allowlist);
149
+ return skills.filter(skill => allowed.has(normalize(skill.name)));
150
+ }
151
+
152
+ /**
153
+ * Dev-mode guard: warn once per process if a manifest entry references a name
154
+ * that is not currently installed. Silent in production.
155
+ */
156
+ const warnedMissing = new Set<string>();
157
+
158
+ export function warnIfManifestHasMissingSkills(
159
+ unitType: string | undefined,
160
+ installedNames: Set<string>,
161
+ ): void {
162
+ // Strict mode is intentionally opt-in via exactly "1"; values like "0" or
163
+ // "false" must preserve the normal silent manifest behavior.
164
+ if (process.env.GSD_SKILL_MANIFEST_STRICT !== "1") return;
165
+ const allowlist = resolveSkillManifest(unitType);
166
+ if (!allowlist) return;
167
+ for (const name of allowlist) {
168
+ const key = `${unitType}:${name}`;
169
+ if (warnedMissing.has(key)) continue;
170
+ if (!installedNames.has(name)) {
171
+ warnedMissing.add(key);
172
+ logWarning("prompt", `skill-manifest: references uninstalled skill '${name}' for unit '${unitType}'`);
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Slice-cadence collapse — #4765.
3
+ *
4
+ * When `git.collapse_cadence: "slice"` is set, each slice's commits are
5
+ * squash-merged from the milestone branch to main as soon as the slice
6
+ * passes validation. Shrinks the orphan window (#4761) from milestone-size
7
+ * to slice-size and surfaces merge conflicts per-slice rather than all at
8
+ * once at milestone end.
9
+ *
10
+ * This module is deliberately focused and narrower than mergeMilestoneToMain:
11
+ * - No worktree teardown (worktree is reused for the next slice)
12
+ * - No DB reconciliation (modern worktrees share the main DB via path resolver)
13
+ * - No roadmap/summary/gate handling (that's still the milestone's job)
14
+ * - Fails loudly on dirty main — caller is responsible for cleanliness
15
+ *
16
+ * Kernighan: the v1 surface handles the happy path + conflict. Edge cases
17
+ * that mergeMilestoneToMain covers (concurrent merges, shared DB paths,
18
+ * submodules) are explicit non-goals; users opt in via preference and early-
19
+ * adopter scenarios are scoped narrow.
20
+ */
21
+
22
+ import { existsSync, unlinkSync } from "node:fs";
23
+ import { join } from "node:path";
24
+ import { execFileSync } from "node:child_process";
25
+
26
+ import { GSDError, GSD_GIT_ERROR } from "./errors.js";
27
+ import { MergeConflictError } from "./git-service.js";
28
+ import {
29
+ nativeBranchForceReset,
30
+ nativeCheckoutBranch,
31
+ nativeCommit,
32
+ nativeCommitCountBetween,
33
+ nativeConflictFiles,
34
+ nativeDetectMainBranch,
35
+ nativeMergeSquash,
36
+ } from "./native-git-bridge.js";
37
+ import { resolveGitDir } from "./worktree-manager.js";
38
+ import { logWarning } from "./workflow-logger.js";
39
+ import { emitSliceMerged, emitMilestoneResquash } from "./worktree-telemetry.js";
40
+
41
+ /**
42
+ * Auto-worktree milestone branch name. Must match autoWorktreeBranch() in
43
+ * auto-worktree.ts; duplicated here to avoid a cyclic import.
44
+ */
45
+ function milestoneBranchName(milestoneId: string): string {
46
+ return `milestone/${milestoneId}`;
47
+ }
48
+
49
+ function cleanupMergeArtifacts(projectRoot: string): void {
50
+ try {
51
+ const gitDir = resolveGitDir(projectRoot);
52
+ for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
53
+ const p = join(gitDir, f);
54
+ if (existsSync(p)) unlinkSync(p);
55
+ }
56
+ } catch (err) {
57
+ logWarning("worktree", `merge artifact cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
58
+ }
59
+ }
60
+
61
+ export interface SliceMergeResult {
62
+ commitSha: string | null;
63
+ mainBranch: string;
64
+ milestoneBranch: string;
65
+ durationMs: number;
66
+ skipped: boolean;
67
+ skippedReason?: string;
68
+ }
69
+
70
+ /**
71
+ * Squash-merge one slice's commits from the milestone branch to main.
72
+ *
73
+ * Preconditions:
74
+ * - Caller is on the milestone branch inside the worktree
75
+ * - `projectRoot` points at the real project root (not the worktree)
76
+ *
77
+ * Post-conditions on success:
78
+ * - Slice's commits are a single squash commit on main
79
+ * - `milestone/<MID>` is fast-forwarded to main (so next slice's work
80
+ * starts from a clean base)
81
+ * - caller's process.cwd is restored
82
+ *
83
+ * Throws MergeConflictError on conflicts; caller should surface and stop.
84
+ * Throws GSDError on dirty main / detection failures.
85
+ */
86
+ export function mergeSliceToMain(
87
+ projectRoot: string,
88
+ milestoneId: string,
89
+ sliceId: string,
90
+ ): SliceMergeResult {
91
+ const started = Date.now();
92
+ const worktreeCwd = process.cwd();
93
+ const milestoneBranch = milestoneBranchName(milestoneId);
94
+ const mainBranch = nativeDetectMainBranch(projectRoot);
95
+
96
+ // Fast path: if the milestone branch has no commits ahead of main, there
97
+ // is nothing to merge. Return a skip result instead of no-op'ing silently
98
+ // so the caller's telemetry shows the decision.
99
+ let commitsAhead = 0;
100
+ try {
101
+ commitsAhead = nativeCommitCountBetween(projectRoot, mainBranch, milestoneBranch);
102
+ } catch {
103
+ // If we can't count, assume there's work and let the merge proceed —
104
+ // a failing merge is more informative than a silent skip.
105
+ commitsAhead = 1;
106
+ }
107
+ if (commitsAhead === 0) {
108
+ // Do NOT emit slice-merged here — this is a no-op, not a merge. Emitting
109
+ // would inflate slicesMerged in telemetry/forensics and distort the
110
+ // conflict rate denominator.
111
+ return {
112
+ commitSha: null,
113
+ mainBranch,
114
+ milestoneBranch,
115
+ durationMs: Date.now() - started,
116
+ skipped: true,
117
+ skippedReason: "no-commits-ahead",
118
+ };
119
+ }
120
+
121
+ process.chdir(projectRoot);
122
+ try {
123
+ // Dirty-main check — v1 fails loudly rather than auto-stashing. Users
124
+ // running slice-cadence opt in knowing main stays clean between merges.
125
+ const status = execFileSync("git", ["status", "--porcelain"], {
126
+ cwd: projectRoot,
127
+ stdio: ["ignore", "pipe", "pipe"],
128
+ encoding: "utf-8",
129
+ }).trim();
130
+ if (status) {
131
+ throw new GSDError(
132
+ GSD_GIT_ERROR,
133
+ `slice-cadence merge requires a clean project root; uncommitted changes detected. ` +
134
+ `Commit or stash at ${projectRoot} before retrying. Status:\n${status}`,
135
+ );
136
+ }
137
+
138
+ nativeCheckoutBranch(projectRoot, mainBranch);
139
+
140
+ // Clean any stale merge artifacts before attempting the squash (#2912 pattern)
141
+ cleanupMergeArtifacts(projectRoot);
142
+
143
+ const mergeResult = nativeMergeSquash(projectRoot, milestoneBranch);
144
+ if (!mergeResult.success) {
145
+ const conflictedFiles = mergeResult.conflicts.length > 0
146
+ ? mergeResult.conflicts
147
+ : nativeConflictFiles(projectRoot);
148
+ cleanupMergeArtifacts(projectRoot);
149
+ try {
150
+ emitSliceMerged(projectRoot, milestoneId, sliceId, {
151
+ durationMs: Date.now() - started,
152
+ conflict: true,
153
+ });
154
+ } catch { /* silent */ }
155
+ throw new MergeConflictError(
156
+ conflictedFiles,
157
+ "squash",
158
+ milestoneBranch,
159
+ mainBranch,
160
+ );
161
+ }
162
+
163
+ // Commit the squash with a slice-scoped message
164
+ const commitSha = nativeCommit(
165
+ projectRoot,
166
+ `gsd: merge ${sliceId} of ${milestoneId} (slice-cadence)`,
167
+ );
168
+
169
+ // Advance the milestone branch to main so the next slice's commits start
170
+ // from a clean base. Force-reset is safe because we just merged this
171
+ // branch's entire delta.
172
+ nativeBranchForceReset(projectRoot, milestoneBranch, mainBranch);
173
+
174
+ const durationMs = Date.now() - started;
175
+ try {
176
+ emitSliceMerged(projectRoot, milestoneId, sliceId, {
177
+ durationMs,
178
+ conflict: false,
179
+ commitSha: commitSha ?? undefined,
180
+ });
181
+ } catch { /* silent */ }
182
+
183
+ return {
184
+ commitSha,
185
+ mainBranch,
186
+ milestoneBranch,
187
+ durationMs,
188
+ skipped: false,
189
+ };
190
+ } finally {
191
+ // Always restore cwd even if anything above threw.
192
+ try { process.chdir(worktreeCwd); } catch { /* best-effort */ }
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Re-squash per-slice commits on main into a single milestone commit.
198
+ *
199
+ * Runs at milestone completion when `collapse_cadence: "slice"` AND
200
+ * `milestone_resquash: true`. The `startSha` is the SHA of main immediately
201
+ * before the milestone's first slice merge — the caller is responsible for
202
+ * recording this (AutoSession field, git ref, or DB row).
203
+ *
204
+ * Strategy: soft-reset main to startSha, then commit the net diff. The
205
+ * N slice commits between startSha and HEAD are collapsed into one.
206
+ *
207
+ * No-op (returns false) if startSha equals HEAD (nothing to re-squash).
208
+ */
209
+ export function resquashMilestoneOnMain(
210
+ projectRoot: string,
211
+ milestoneId: string,
212
+ startSha: string,
213
+ ): { resquashed: boolean; newSha: string | null } {
214
+ const mainBranch = nativeDetectMainBranch(projectRoot);
215
+ const worktreeCwd = process.cwd();
216
+
217
+ process.chdir(projectRoot);
218
+ try {
219
+ nativeCheckoutBranch(projectRoot, mainBranch);
220
+
221
+ // Verify the startSha..HEAD range contains ONLY this milestone's slice-
222
+ // cadence commits. If any unrelated commits landed on main since the
223
+ // milestone started (e.g. concurrent work, cherry-picks, hotfixes), a
224
+ // blind `git reset --soft` would fold them into the re-squash and rewrite
225
+ // their attribution. Fail closed — the user can resolve manually.
226
+ const expectedSuffix = `(slice-cadence)`;
227
+ const expectedMilestoneToken = ` of ${milestoneId} `;
228
+ let subjectsRaw = "";
229
+ try {
230
+ subjectsRaw = execFileSync(
231
+ "git",
232
+ ["log", "--format=%s", `${startSha}..HEAD`],
233
+ { cwd: projectRoot, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
234
+ );
235
+ } catch {
236
+ return { resquashed: false, newSha: null };
237
+ }
238
+ const subjects = subjectsRaw.split("\n").filter((s) => s.length > 0);
239
+ const sliceCount = subjects.length;
240
+ if (sliceCount === 0) {
241
+ return { resquashed: false, newSha: null };
242
+ }
243
+ const foreign = subjects.filter(
244
+ (s) => !(s.endsWith(expectedSuffix) && s.includes(expectedMilestoneToken)),
245
+ );
246
+ if (foreign.length > 0) {
247
+ logWarning(
248
+ "worktree",
249
+ `slice-cadence: skipping milestone resquash for ${milestoneId} — ` +
250
+ `${foreign.length} non-slice-cadence commit(s) in ${startSha}..HEAD ` +
251
+ `would be folded in. First: "${foreign[0]}". Resolve history manually.`,
252
+ );
253
+ return { resquashed: false, newSha: null };
254
+ }
255
+
256
+ // Safe to collapse: all commits in the range are this milestone's slices.
257
+ execFileSync("git", ["reset", "--soft", startSha], {
258
+ cwd: projectRoot,
259
+ stdio: ["ignore", "pipe", "pipe"],
260
+ encoding: "utf-8",
261
+ });
262
+
263
+ const newSha = nativeCommit(
264
+ projectRoot,
265
+ `gsd: complete milestone ${milestoneId} (${sliceCount} slices re-squashed)`,
266
+ { allowEmpty: true },
267
+ );
268
+
269
+ try {
270
+ emitMilestoneResquash(projectRoot, milestoneId, {
271
+ sliceCount,
272
+ startSha,
273
+ endSha: newSha ?? undefined,
274
+ });
275
+ } catch { /* silent */ }
276
+
277
+ return { resquashed: true, newSha };
278
+ } finally {
279
+ try { process.chdir(worktreeCwd); } catch { /* best-effort */ }
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Read the effective collapse cadence from validated preferences. Accepts
285
+ * a raw preferences object (the shape loadEffectiveGSDPreferences returns).
286
+ */
287
+ export function getCollapseCadence(
288
+ prefs: { git?: { collapse_cadence?: "milestone" | "slice" } } | undefined | null,
289
+ ): "milestone" | "slice" {
290
+ return prefs?.git?.collapse_cadence ?? "milestone";
291
+ }
292
+
293
+ export function getMilestoneResquash(
294
+ prefs: { git?: { milestone_resquash?: boolean } } | undefined | null,
295
+ ): boolean {
296
+ // Default true when cadence is slice — resquash preserves the milestone-
297
+ // level history shape users expect.
298
+ return prefs?.git?.milestone_resquash !== false;
299
+ }
@@ -22,6 +22,7 @@ import { readFileSync } from "node:fs";
22
22
  import { resolve } from "node:path";
23
23
 
24
24
  import { invalidateAllCaches } from "../cache.ts";
25
+ import { extractSourceRegion } from "./test-helpers.ts";
25
26
  import {
26
27
  openDatabase,
27
28
  closeDatabase,
@@ -160,7 +161,7 @@ describe("cache.ts must not re-import clearArtifacts into invalidateAllCaches",
160
161
  test("invalidateAllCaches does not call clearArtifacts", () => {
161
162
  const fnIdx = src.indexOf("function invalidateAllCaches");
162
163
  assert.ok(fnIdx !== -1);
163
- const body = src.slice(fnIdx, fnIdx + 1000);
164
+ const body = extractSourceRegion(src, "function invalidateAllCaches", { fromIdx: fnIdx });
164
165
  assert.ok(
165
166
  !/\bclearArtifacts\s*\(/.test(body),
166
167
  "invalidateAllCaches must not call clearArtifacts() — it wipes the write-through store",