gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714

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 (429) hide show
  1. package/README.md +1 -1
  2. package/dist/claude-cli-check.js +5 -1
  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 +5 -1
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/gsd/auto/loop.js +43 -0
  15. package/dist/resources/extensions/gsd/auto/phases.js +15 -21
  16. package/dist/resources/extensions/gsd/auto/session.js +0 -2
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
  18. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  19. package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
  20. package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
  21. package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
  22. package/dist/resources/extensions/gsd/auto-start.js +34 -24
  23. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  24. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  25. package/dist/resources/extensions/gsd/auto.js +31 -20
  26. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  28. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  29. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  30. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  31. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  32. package/dist/resources/extensions/gsd/component-types.js +69 -0
  33. package/dist/resources/extensions/gsd/detection.js +49 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  35. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  36. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  37. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  38. package/dist/resources/extensions/gsd/git-service.js +126 -2
  39. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  40. package/dist/resources/extensions/gsd/guided-flow.js +17 -5
  41. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  42. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  43. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  44. package/dist/resources/extensions/gsd/model-router.js +6 -0
  45. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  46. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  47. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  48. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  49. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  50. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  51. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  52. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  53. package/dist/resources/extensions/gsd/state.js +44 -33
  54. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  55. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  56. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  57. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  59. package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
  60. package/dist/resources/extensions/mcp-client/index.js +3 -1
  61. package/dist/resources/extensions/ollama/index.js +5 -1
  62. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  63. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  64. package/dist/web/standalone/.next/BUILD_ID +1 -1
  65. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  66. package/dist/web/standalone/.next/build-manifest.json +2 -2
  67. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/index.html +1 -1
  86. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  93. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  94. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  97. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  98. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  99. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  100. package/package.json +2 -3
  101. package/packages/daemon/src/logger.ts +4 -3
  102. package/packages/mcp-server/dist/server.d.ts +24 -0
  103. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  104. package/packages/mcp-server/dist/server.js +88 -87
  105. package/packages/mcp-server/dist/server.js.map +1 -1
  106. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  107. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  108. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  109. package/packages/mcp-server/src/server.ts +131 -105
  110. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  111. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  112. package/packages/native/package.json +1 -1
  113. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  114. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  115. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  116. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  117. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  118. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  119. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  120. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  121. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  123. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  124. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  125. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  127. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  128. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  129. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  130. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  131. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  133. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  135. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  136. package/packages/pi-ai/dist/models.test.js +36 -11
  137. package/packages/pi-ai/dist/models.test.js.map +1 -1
  138. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  139. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  140. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  141. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  142. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  143. package/packages/pi-ai/src/models.test.ts +48 -11
  144. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  145. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  146. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  148. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  150. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  152. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  154. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  156. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  158. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  160. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  163. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  165. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  167. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  169. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  170. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  171. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  173. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  175. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/index.js +1 -0
  177. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  191. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  195. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  196. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  197. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  198. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  199. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  200. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  201. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  202. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  203. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  204. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  205. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  206. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  207. package/packages/pi-coding-agent/src/index.ts +1 -0
  208. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  213. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  214. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  215. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  216. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  217. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  218. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  219. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  220. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  221. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  222. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  223. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  224. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  225. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  226. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  227. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  228. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  229. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  230. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  231. package/packages/pi-tui/dist/components/editor.js +19 -0
  232. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  233. package/packages/pi-tui/dist/components/image.test.js +6 -5
  234. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  235. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  236. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  237. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  239. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  240. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  241. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  242. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  243. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  244. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  245. package/packages/pi-tui/src/components/editor.ts +22 -0
  246. package/packages/pi-tui/src/components/image.test.ts +10 -5
  247. package/packages/pi-tui/src/editor-component.ts +3 -0
  248. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  249. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  250. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  251. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  252. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  253. package/scripts/install.js +15 -1
  254. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  255. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  256. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  257. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  258. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  259. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  260. package/src/resources/extensions/claude-code-cli/readiness.ts +5 -1
  261. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  262. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  263. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  264. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  265. package/src/resources/extensions/gsd/auto/loop.ts +47 -0
  266. package/src/resources/extensions/gsd/auto/phases.ts +16 -20
  267. package/src/resources/extensions/gsd/auto/session.ts +0 -2
  268. package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
  269. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  270. package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
  271. package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
  272. package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
  273. package/src/resources/extensions/gsd/auto-start.ts +54 -6
  274. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  275. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  276. package/src/resources/extensions/gsd/auto.ts +43 -22
  277. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  278. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  279. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  280. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  281. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  282. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  283. package/src/resources/extensions/gsd/component-types.ts +362 -0
  284. package/src/resources/extensions/gsd/detection.ts +58 -1
  285. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  286. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  287. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  288. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  289. package/src/resources/extensions/gsd/git-service.ts +133 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  291. package/src/resources/extensions/gsd/guided-flow.ts +20 -5
  292. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  293. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  294. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  295. package/src/resources/extensions/gsd/model-router.ts +6 -0
  296. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  297. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  299. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  300. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  301. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  302. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  303. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  304. package/src/resources/extensions/gsd/state.ts +49 -44
  305. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  306. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  307. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  308. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  309. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +94 -289
  310. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  311. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  312. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  313. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  314. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -197
  315. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  316. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  317. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  318. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  319. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  320. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  321. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  322. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  323. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  324. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  325. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  326. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  327. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -3
  328. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  329. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  330. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
  331. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  332. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  334. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -57
  335. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  336. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  337. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  338. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  339. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  340. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  341. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  342. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  343. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  344. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -62
  345. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  346. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  347. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  348. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -49
  349. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  350. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
  351. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  352. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  353. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  354. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  355. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  356. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  357. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  358. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  359. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  360. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  361. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  363. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  364. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  365. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  366. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  367. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  368. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
  369. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  370. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  371. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  372. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +12 -61
  373. package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
  374. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  375. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  376. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  377. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  378. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  379. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  380. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  381. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -81
  382. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  383. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  384. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  385. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  386. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  387. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  388. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  389. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  390. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  391. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  392. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  393. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  394. package/src/resources/extensions/gsd/types.ts +3 -3
  395. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  396. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  397. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  398. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  399. package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
  400. package/src/resources/extensions/mcp-client/index.ts +3 -1
  401. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  402. package/src/resources/extensions/ollama/index.ts +5 -1
  403. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  404. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  405. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  406. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  407. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  408. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  409. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  410. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  411. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  412. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  413. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  414. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -144
  415. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  416. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  417. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  418. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  419. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  420. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  421. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -75
  422. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  423. package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
  424. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  425. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  426. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -130
  427. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
  428. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
  429. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Unified Component Type Definitions
3
+ *
4
+ * Shared metadata for installable/discoverable skills and agents.
5
+ *
6
+ * Replaces the separate type systems in:
7
+ * - packages/pi-coding-agent/src/core/skills.ts (SkillFrontmatter, Skill)
8
+ * - src/resources/extensions/subagent/agents.ts (AgentConfig)
9
+ *
10
+ * Legacy skill and agent formats are supported via backward-compatible loading.
11
+ */
12
+ // ============================================================================
13
+ // Validation
14
+ // ============================================================================
15
+ /** Max name length per spec */
16
+ export const MAX_NAME_LENGTH = 64;
17
+ /** Max description length per spec */
18
+ export const MAX_DESCRIPTION_LENGTH = 1024;
19
+ /** Valid name pattern: lowercase a-z, 0-9, hyphens, no leading/trailing/consecutive hyphens */
20
+ export const NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
21
+ /**
22
+ * Validate a component name.
23
+ * @returns Array of error messages (empty if valid).
24
+ */
25
+ export function validateComponentName(name) {
26
+ const errors = [];
27
+ if (!name || name.trim() === '') {
28
+ errors.push('name is required');
29
+ return errors;
30
+ }
31
+ if (name.length > MAX_NAME_LENGTH) {
32
+ errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);
33
+ }
34
+ if (name.includes('--')) {
35
+ errors.push('name must not contain consecutive hyphens');
36
+ }
37
+ if (!NAME_PATTERN.test(name)) {
38
+ if (/[A-Z]/.test(name)) {
39
+ errors.push('name must be lowercase');
40
+ }
41
+ else if (name.startsWith('-') || name.endsWith('-')) {
42
+ errors.push('name must not start or end with a hyphen');
43
+ }
44
+ else if (!name.includes('--')) {
45
+ errors.push('name must contain only lowercase a-z, 0-9, and hyphens');
46
+ }
47
+ }
48
+ return errors;
49
+ }
50
+ /**
51
+ * Validate a component description.
52
+ * @returns Array of error messages (empty if valid).
53
+ */
54
+ export function validateComponentDescription(description) {
55
+ const errors = [];
56
+ if (!description || description.trim() === '') {
57
+ errors.push('description is required');
58
+ }
59
+ else if (description.length > MAX_DESCRIPTION_LENGTH) {
60
+ errors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);
61
+ }
62
+ return errors;
63
+ }
64
+ /**
65
+ * Compute the canonical ID for a component.
66
+ */
67
+ export function computeComponentId(name, namespace) {
68
+ return namespace ? `${namespace}:${name}` : name;
69
+ }
@@ -6,7 +6,7 @@
6
6
  * flow to show when entering a project directory.
7
7
  */
8
8
  import { existsSync, openSync, readSync, closeSync, readdirSync, readFileSync, statSync } from "node:fs";
9
- import { join } from "node:path";
9
+ import { dirname, join, parse as parsePath } from "node:path";
10
10
  import { homedir } from "node:os";
11
11
  import { gsdRoot } from "./paths.js";
12
12
  const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
@@ -951,6 +951,54 @@ function resolveVersionCatalogAccessors(basePath, versionCatalogFiles, settingsF
951
951
  }
952
952
  return accessors;
953
953
  }
954
+ /**
955
+ * Walk ancestor directories of `startDir` looking for any file in
956
+ * `PROJECT_FILES`. Stops at the filesystem root or at a `.git` boundary
957
+ * (so ancestors above a git repo root — e.g. `$HOME` or `/usr/local` —
958
+ * can't trigger false positives). Returns true if an ancestor contains
959
+ * one of the project markers.
960
+ *
961
+ * Used by the worktree health check (#2347) to avoid warning about
962
+ * monorepos where package.json / Cargo.toml / etc. live in a parent
963
+ * directory rather than in the worktree's own checkout.
964
+ *
965
+ * `existsFn` is injectable so this remains deterministically testable
966
+ * without touching the real filesystem; defaults to `fs.existsSync`.
967
+ */
968
+ export function hasProjectFileInAncestor(startDir, existsFn = existsSync) {
969
+ let checkDir = dirname(startDir);
970
+ const { root } = parsePath(checkDir);
971
+ while (checkDir !== root) {
972
+ if (PROJECT_FILES.some((f) => existsFn(join(checkDir, f)))) {
973
+ return true;
974
+ }
975
+ // Stop at git repository boundary — ancestors above the repo root
976
+ // may contain unrelated project files. Check AFTER project-file scan
977
+ // so a repo root containing both .git and a marker is still recognized.
978
+ if (existsFn(join(checkDir, ".git")))
979
+ return false;
980
+ checkDir = dirname(checkDir);
981
+ }
982
+ return false;
983
+ }
984
+ /**
985
+ * Check whether a project's `.gsd/` directory contains the bootstrap artifacts
986
+ * (`PREFERENCES.md` or `milestones/`) that indicate a completed init run.
987
+ *
988
+ * A zombie `.gsd/` state — symlink exists but neither artifact is present —
989
+ * must be treated as "needs init wizard". The previous guard checked only
990
+ * `existsSync(gsdRoot(basePath))`, which accepted zombie states and skipped
991
+ * the wizard (#2942).
992
+ *
993
+ * `existsFn` is injectable so tests can run deterministically; defaults to
994
+ * `fs.existsSync`.
995
+ */
996
+ export function hasGsdBootstrapArtifacts(gsdPath, existsFn = existsSync) {
997
+ return (existsFn(gsdPath) &&
998
+ (existsFn(join(gsdPath, "PREFERENCES.md")) ||
999
+ existsFn(join(gsdPath, "preferences.md")) ||
1000
+ existsFn(join(gsdPath, "milestones"))));
1001
+ }
954
1002
  export function scanProjectFiles(basePath) {
955
1003
  const files = [];
956
1004
  const queue = [{ path: basePath, depth: 0 }];
@@ -159,7 +159,7 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
159
159
 
160
160
  - `phases`: fine-grained control over which phases run. Usually set by `token_profile`, but can be overridden. Keys:
161
161
  - `skip_research`: boolean — skip milestone-level research. Default: `false`.
162
- - `reassess_after_slice`: boolean — run roadmap reassessment after each completed slice. Default: `true`.
162
+ - `reassess_after_slice`: boolean — run a dedicated roadmap-reassessment unit after each completed slice. Default: `false` (per ADR-003 §4). The plan-slice agent for the next slice performs JIT reassessment via a prompt preamble at zero additional token cost; a dedicated reassess session is opt-in. Set to `true` (e.g. via the `burn-max` profile) if you want the explicit session.
163
163
  - `skip_reassess`: boolean — force-disable roadmap reassessment even if `reassess_after_slice` is enabled. Default: `false`.
164
164
  - `skip_slice_research`: boolean — skip per-slice research. Default: `false`.
165
165
 
@@ -37,9 +37,9 @@ export const GATE_REGISTRY = {
37
37
  id: "Q4",
38
38
  scope: "slice",
39
39
  ownerTurn: "gate-evaluate",
40
- question: "What existing promises does this break?",
40
+ question: "Which existing requirements (R-IDs) does this slice touch, and which must be re-tested?",
41
41
  guidance: [
42
- "List which existing requirements (R001, R003, etc.) are touched by this slice.",
42
+ "List the R-IDs (e.g. R001, R003) touched by this slice; see the milestone requirements artifact at .gsd/milestones/<id>/REQUIREMENTS.md.",
43
43
  "Identify what must be re-tested after shipping.",
44
44
  "Flag decisions that should be revisited given the new scope.",
45
45
  "If no existing requirements are affected, return verdict 'omitted'.",
@@ -1,9 +1,36 @@
1
1
  /**
2
2
  * Shared git constants used across git-service and native-git-bridge.
3
3
  */
4
+ /**
5
+ * Parent process env vars that, if leaked into a git child process, can
6
+ * silently redirect every operation to a different repo or index.
7
+ *
8
+ * Stripped from GIT_NO_PROMPT_ENV so a GSD invoked from inside a git hook,
9
+ * a different worktree's terminal, or any context that pre-set these vars
10
+ * cannot redirect GSD's git operations to the wrong target.
11
+ * (Issue #4980 NEW-1)
12
+ */
13
+ const LEAKING_GIT_ENV_VARS = [
14
+ "GIT_DIR",
15
+ "GIT_WORK_TREE",
16
+ "GIT_INDEX_FILE",
17
+ "GIT_OBJECT_DIRECTORY",
18
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
19
+ "GIT_COMMON_DIR",
20
+ "GIT_NAMESPACE",
21
+ ];
22
+ function buildSafeParentEnv() {
23
+ const safe = {};
24
+ for (const [k, v] of Object.entries(process.env)) {
25
+ if (!LEAKING_GIT_ENV_VARS.includes(k)) {
26
+ safe[k] = v;
27
+ }
28
+ }
29
+ return safe;
30
+ }
4
31
  /** Env overlay that suppresses interactive git credential prompts and git-svn noise. */
5
32
  export const GIT_NO_PROMPT_ENV = {
6
- ...process.env,
33
+ ...buildSafeParentEnv(),
7
34
  GIT_TERMINAL_PROMPT: "0",
8
35
  GIT_ASKPASS: "",
9
36
  GIT_SVN_ID: "",
@@ -9,12 +9,25 @@
9
9
  * what actions were taken. `formatGitError` maps raw git errors to
10
10
  * user-friendly messages suggesting `/gsd doctor`.
11
11
  */
12
+ import { execFileSync } from "node:child_process";
12
13
  import { existsSync, unlinkSync } from "node:fs";
13
14
  import { join } from "node:path";
14
15
  import { MergeConflictError } from "./git-service.js";
15
16
  import { nativeMergeAbort, nativeRebaseAbort, nativeResetHard } from "./native-git-bridge.js";
16
17
  // Re-export for consumers
17
18
  export { MergeConflictError };
19
+ function hasWorkingTreeChanges(cwd) {
20
+ try {
21
+ return execFileSync("git", ["status", "--porcelain"], {
22
+ cwd,
23
+ stdio: ["ignore", "pipe", "pipe"],
24
+ encoding: "utf-8",
25
+ }).trim().length > 0;
26
+ }
27
+ catch {
28
+ return true;
29
+ }
30
+ }
18
31
  /**
19
32
  * Detect and clean up leftover merge/rebase state, then hard-reset.
20
33
  *
@@ -60,6 +73,20 @@ export function abortAndReset(cwd) {
60
73
  cleaned.push("rebase abort attempted (may have failed)");
61
74
  }
62
75
  }
76
+ // Preserve any staged or untracked user work before the hard reset.
77
+ // Reset --hard discards staged changes (reflog only covers committed
78
+ // state), so a labeled stash gives the user a recovery handle if their
79
+ // in-flight inspection work would otherwise be silently lost.
80
+ // (Issue #4980 HIGH-5)
81
+ if (hasWorkingTreeChanges(cwd)) {
82
+ try {
83
+ execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd: pre-self-heal-reset ${new Date().toISOString()}`], { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
84
+ cleaned.push("stashed working tree before reset");
85
+ }
86
+ catch {
87
+ /* nothing to stash, or stash refused (e.g. unresolved conflicts) — proceed */
88
+ }
89
+ }
63
90
  // Always hard-reset to HEAD
64
91
  try {
65
92
  nativeResetHard(cwd);
@@ -7,7 +7,7 @@
7
7
  * This module centralizes the GitPreferences interface, runtime exclusion
8
8
  * paths, commit type inference, and the runGit shell helper.
9
9
  */
10
- import { execFileSync, execSync } from "node:child_process";
10
+ import { execFileSync } from "node:child_process";
11
11
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
12
12
  import { join } from "node:path";
13
13
  import { gsdRoot } from "./paths.js";
@@ -243,6 +243,107 @@ export function resolveMilestoneIntegrationBranch(basePath, milestoneId, prefs =
243
243
  reason: `Recorded integration branch "${recordedBranch}" for milestone ${milestoneId} no longer exists, and no safe fallback branch could be determined.`,
244
244
  };
245
245
  }
246
+ // ─── Pre-Merge Command Tokenizer ──────────────────────────────────────────
247
+ /**
248
+ * Tokenize a user-supplied pre-merge command string into argv form, with
249
+ * minimal support for double- and single-quoted strings. Designed to be
250
+ * sufficient for typical commands ("npm test", `npm run lint:ci`,
251
+ * `pnpm run tsc --noEmit`) without spawning a shell.
252
+ *
253
+ * Returns [] when the input is empty or whitespace-only.
254
+ * Throws when quoting is malformed.
255
+ *
256
+ * Used by GitServiceImpl.runPreMergeCheck to eliminate the shell-injection
257
+ * surface that running an arbitrary user string through a shell would create.
258
+ * (Issue #4980 HIGH-2)
259
+ */
260
+ export function tokenizePreMergeCommand(input) {
261
+ const tokens = [];
262
+ let current = "";
263
+ let i = 0;
264
+ let quote = "";
265
+ let hasContent = false;
266
+ while (i < input.length) {
267
+ const ch = input[i];
268
+ if (quote) {
269
+ if (ch === quote) {
270
+ quote = "";
271
+ }
272
+ else if (ch === "\\" && quote === '"' && i + 1 < input.length) {
273
+ current += input[i + 1];
274
+ i += 2;
275
+ continue;
276
+ }
277
+ else {
278
+ current += ch;
279
+ }
280
+ i++;
281
+ continue;
282
+ }
283
+ if (ch === '"' || ch === "'") {
284
+ quote = ch;
285
+ hasContent = true;
286
+ i++;
287
+ continue;
288
+ }
289
+ if (ch === " " || ch === "\t") {
290
+ if (hasContent) {
291
+ tokens.push(current);
292
+ current = "";
293
+ hasContent = false;
294
+ }
295
+ i++;
296
+ continue;
297
+ }
298
+ if (ch === "\\" && i + 1 < input.length) {
299
+ current += input[i + 1];
300
+ i += 2;
301
+ hasContent = true;
302
+ continue;
303
+ }
304
+ current += ch;
305
+ hasContent = true;
306
+ i++;
307
+ }
308
+ if (quote) {
309
+ throw new Error(`Unterminated ${quote === '"' ? "double" : "single"} quote in pre-merge command`);
310
+ }
311
+ if (hasContent)
312
+ tokens.push(current);
313
+ return tokens;
314
+ }
315
+ function containsUnquotedShellControl(input) {
316
+ let i = 0;
317
+ let quote = "";
318
+ while (i < input.length) {
319
+ const ch = input[i];
320
+ if (quote) {
321
+ if (ch === quote) {
322
+ quote = "";
323
+ }
324
+ else if (ch === "\\" && quote === '"' && i + 1 < input.length) {
325
+ i += 2;
326
+ continue;
327
+ }
328
+ i++;
329
+ continue;
330
+ }
331
+ if (ch === '"' || ch === "'") {
332
+ quote = ch;
333
+ i++;
334
+ continue;
335
+ }
336
+ if (ch === "\\" && i + 1 < input.length) {
337
+ i += 2;
338
+ continue;
339
+ }
340
+ if (ch === ";" || ch === "&" || ch === "|" || ch === "`" || ch === "$" || ch === "<" || ch === ">") {
341
+ return true;
342
+ }
343
+ i++;
344
+ }
345
+ return false;
346
+ }
246
347
  // ─── Git Helper ────────────────────────────────────────────────────────────
247
348
  /**
248
349
  * Strip git-svn noise from error messages.
@@ -601,8 +702,31 @@ export class GitServiceImpl {
601
702
  return { passed: true, skipped: true };
602
703
  }
603
704
  }
705
+ // Tokenize and run via execFileSync (no shell). Shell metacharacters in
706
+ // user-supplied prefs.pre_merge_check would otherwise be interpreted as
707
+ // chaining/redirection (e.g. `;`, `&&`, `|`, backticks) — a privesc
708
+ // surface in repos with a checked-in `.gsd/PREFERENCES.md`.
709
+ // (Issue #4980 HIGH-2)
710
+ if (containsUnquotedShellControl(command)) {
711
+ return {
712
+ passed: false,
713
+ skipped: false,
714
+ command,
715
+ error: "pre_merge_check contains shell metacharacters (;, &&, |, $, backticks, redirects). " +
716
+ "Put complex commands in a script file (e.g. './scripts/pre-merge.sh') and reference the script path instead.",
717
+ };
718
+ }
719
+ const tokens = tokenizePreMergeCommand(command);
720
+ if (tokens.length === 0) {
721
+ return { passed: true, skipped: true };
722
+ }
604
723
  try {
605
- execSync(command, { cwd: this.basePath, stdio: "pipe", encoding: "utf-8" });
724
+ execFileSync(tokens[0], tokens.slice(1), {
725
+ cwd: this.basePath,
726
+ stdio: "pipe",
727
+ encoding: "utf-8",
728
+ env: GIT_NO_PROMPT_ENV,
729
+ });
606
730
  return { passed: true, skipped: false, command };
607
731
  }
608
732
  catch (err) {
@@ -135,7 +135,7 @@ function openRawDb(path) {
135
135
  const Database = providerModule;
136
136
  return new Database(path);
137
137
  }
138
- const SCHEMA_VERSION = 22;
138
+ export const SCHEMA_VERSION = 22;
139
139
  function indexExists(db, name) {
140
140
  return !!db.prepare("SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?").get(name);
141
141
  }
@@ -2556,6 +2556,9 @@ export function insertReplanHistory(entry) {
2556
2556
  export function insertAssessment(entry) {
2557
2557
  if (!currentDb)
2558
2558
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
2559
+ // Idempotent: PRIMARY KEY is `path`, which is deterministic given (milestone_id, scope) per
2560
+ // the artifact-path resolver. Retrying the same reassess-roadmap silently overwrites the row
2561
+ // instead of accumulating duplicates.
2559
2562
  currentDb.prepare(`INSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at)
2560
2563
  VALUES (:path, :milestone_id, :slice_id, :task_id, :status, :scope, :full_content, :created_at)`).run({
2561
2564
  ":path": entry.path,
@@ -2655,7 +2658,7 @@ function rowToGate(row) {
2655
2658
  scope: row["scope"],
2656
2659
  task_id: row["task_id"] ?? "",
2657
2660
  status: row["status"],
2658
- verdict: row["verdict"] || "",
2661
+ verdict: row["status"] === "pending" ? null : row["verdict"],
2659
2662
  rationale: row["rationale"] || "",
2660
2663
  findings: row["findings"] || "",
2661
2664
  evaluated_at: row["evaluated_at"] ?? null,
@@ -2739,7 +2742,7 @@ export function getGateResults(milestoneId, sliceId, scope) {
2739
2742
  export function markAllGatesOmitted(milestoneId, sliceId) {
2740
2743
  if (!currentDb)
2741
2744
  return;
2742
- currentDb.prepare(`UPDATE quality_gates SET status = 'omitted', verdict = 'omitted', evaluated_at = :now
2745
+ currentDb.prepare(`UPDATE quality_gates SET status = 'complete', verdict = 'omitted', evaluated_at = :now
2743
2746
  WHERE milestone_id = :mid AND slice_id = :sid AND status = 'pending'`).run({
2744
2747
  ":mid": milestoneId,
2745
2748
  ":sid": sliceId,
@@ -28,7 +28,7 @@ import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitig
28
28
  import { loadEffectiveGSDPreferences } from "./preferences.js";
29
29
  import { resolveUokFlags } from "./uok/flags.js";
30
30
  import { ensurePlanV2Graph, isMissingFinalizedContextResult } from "./uok/plan-v2.js";
31
- import { detectProjectState } from "./detection.js";
31
+ import { detectProjectState, hasGsdBootstrapArtifacts } from "./detection.js";
32
32
  import { showProjectInit, offerMigration } from "./init-wizard.js";
33
33
  import { validateDirectory } from "./validate-directory.js";
34
34
  import { showConfirm } from "../shared/tui.js";
@@ -258,6 +258,18 @@ function extractAssistantText(msg) {
258
258
  }
259
259
  /**
260
260
  * Return true if the assistant message contains any tool-use block.
261
+ *
262
+ * The canonical pi-ai `AssistantMessage.content` (see packages/pi-ai/src/types.ts)
263
+ * uses `type: "toolCall"` and `type: "serverToolUse"` for tool invocations —
264
+ * every provider (anthropic-direct, claude-code-cli, openai, etc.) normalizes
265
+ * incoming tool blocks into these two shapes before they reach guided-flow.
266
+ *
267
+ * The Anthropic API wire shape `"tool_use"` / `"server_tool_use"` does NOT appear
268
+ * in the internal AssistantMessage — those literals are only used when sending
269
+ * messages back out to the Anthropic API. Matching them here was a latent bug:
270
+ * `hasToolUse` returned `false` for every real tool call, which let the
271
+ * empty-turn nudge fire and pre-empt MCP tools that block on the user
272
+ * (e.g. `ask_user_questions`). See investigation in PR for #4658.
261
273
  */
262
274
  function hasToolUse(msg) {
263
275
  if (!msg)
@@ -265,7 +277,9 @@ function hasToolUse(msg) {
265
277
  const content = msg.content;
266
278
  if (!Array.isArray(content))
267
279
  return false;
268
- return content.some((b) => b && typeof b === "object" && (b.type === "tool_use" || b.type === "tool-use"));
280
+ return content.some((b) => b &&
281
+ typeof b === "object" &&
282
+ (b.type === "toolCall" || b.type === "serverToolUse"));
269
283
  }
270
284
  /**
271
285
  * #4573 — Detect and recover from the "ready phrase without files" failure mode.
@@ -1244,9 +1258,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1244
1258
  // A zombie .gsd/ state (symlink exists but missing PREFERENCES.md and
1245
1259
  // milestones/) must trigger the init wizard, not skip it (#2942).
1246
1260
  const gsdPath = gsdRoot(basePath);
1247
- const hasBootstrapArtifacts = existsSync(gsdPath)
1248
- && (existsSync(join(gsdPath, "PREFERENCES.md"))
1249
- || existsSync(join(gsdPath, "milestones")));
1261
+ const hasBootstrapArtifacts = hasGsdBootstrapArtifacts(gsdPath);
1250
1262
  if (!hasBootstrapArtifacts) {
1251
1263
  const detection = detectProjectState(basePath);
1252
1264
  // v1 .planning/ detected — offer migration before anything else
@@ -62,7 +62,9 @@ export function buildMemoryLLMCall(ctx) {
62
62
  // which returns undefined for OAuth users (Claude Max / Claude Pro).
63
63
  // See: https://github.com/gsd-build/gsd-2/issues/2959
64
64
  const resolvedKeyPromise = ctx.modelRegistry.getApiKey(selectedModel).catch(() => undefined);
65
- return async (system, user) => {
65
+ // Expose on the returned fn so tests can await resolution deterministically
66
+ // (avoids arbitrary setTimeout polling for an internal microtask).
67
+ const llmCall = async (system, user) => {
66
68
  const { completeSimple } = await import('@gsd/pi-ai');
67
69
  const resolvedApiKey = await resolvedKeyPromise;
68
70
  const result = await completeSimple(selectedModel, {
@@ -79,6 +81,10 @@ export function buildMemoryLLMCall(ctx) {
79
81
  .map(c => c.text);
80
82
  return textParts.join('');
81
83
  };
84
+ // Attach the in-flight API-key resolution so tests (and callers) can
85
+ // `await llmCall.apiKeyReady` rather than relying on setTimeout polling.
86
+ llmCall.apiKeyReady = resolvedKeyPromise;
87
+ return llmCall;
82
88
  }
83
89
  catch {
84
90
  return null;