gsd-pi 2.71.0-dev.e17e0ce → 2.72.0-dev.3118184

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 (352) hide show
  1. package/README.md +46 -3
  2. package/dist/cli.js +76 -3
  3. package/dist/mcp-server.js +37 -14
  4. package/dist/onboarding.js +10 -0
  5. package/dist/resources/agents/debugger.md +58 -0
  6. package/dist/resources/agents/doc-writer.md +43 -0
  7. package/dist/resources/agents/git-ops.md +56 -0
  8. package/dist/resources/agents/javascript-pro.md +46 -271
  9. package/dist/resources/agents/planner.md +55 -0
  10. package/dist/resources/agents/refactorer.md +47 -0
  11. package/dist/resources/agents/reviewer.md +48 -0
  12. package/dist/resources/agents/security.md +59 -0
  13. package/dist/resources/agents/tester.md +50 -0
  14. package/dist/resources/agents/typescript-pro.md +41 -235
  15. package/dist/resources/extensions/async-jobs/await-tool.js +7 -4
  16. package/dist/resources/extensions/async-jobs/job-manager.js +28 -3
  17. package/dist/resources/extensions/claude-code-cli/partial-builder.js +40 -12
  18. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +132 -10
  19. package/dist/resources/extensions/gsd/auto/loop.js +84 -1
  20. package/dist/resources/extensions/gsd/auto/phases.js +4 -0
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +6 -0
  22. package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
  23. package/dist/resources/extensions/gsd/auto-recovery.js +11 -0
  24. package/dist/resources/extensions/gsd/auto-start.js +24 -4
  25. package/dist/resources/extensions/gsd/auto.js +29 -19
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  27. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -11
  28. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +2 -5
  29. package/dist/resources/extensions/gsd/commands-handlers.js +4 -1
  30. package/dist/resources/extensions/gsd/context-injector.js +1 -1
  31. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -7
  32. package/dist/resources/extensions/gsd/definition-io.js +15 -0
  33. package/dist/resources/extensions/gsd/dispatch-guard.js +4 -0
  34. package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
  35. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +6 -3
  36. package/dist/resources/extensions/gsd/error-classifier.js +4 -1
  37. package/dist/resources/extensions/gsd/gate-registry.js +208 -0
  38. package/dist/resources/extensions/gsd/git-service.js +11 -8
  39. package/dist/resources/extensions/gsd/gitignore.js +12 -6
  40. package/dist/resources/extensions/gsd/gsd-db.js +90 -6
  41. package/dist/resources/extensions/gsd/key-manager.js +2 -0
  42. package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
  43. package/dist/resources/extensions/gsd/notification-overlay.js +26 -12
  44. package/dist/resources/extensions/gsd/notification-store.js +5 -4
  45. package/dist/resources/extensions/gsd/preferences-skills.js +2 -34
  46. package/dist/resources/extensions/gsd/preferences-types.js +15 -0
  47. package/dist/resources/extensions/gsd/preferences.js +16 -3
  48. package/dist/resources/extensions/gsd/prompt-loader.js +4 -1
  49. package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
  50. package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -1
  51. package/dist/resources/extensions/gsd/prompts/discuss.md +122 -13
  52. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  53. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  55. package/dist/resources/extensions/gsd/shortcut-defs.js +7 -1
  56. package/dist/resources/extensions/gsd/state.js +29 -2
  57. package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
  58. package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
  59. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +4 -1
  60. package/dist/resources/extensions/gsd/workflow-projections.js +7 -0
  61. package/dist/resources/extensions/gsd/worktree-manager.js +30 -3
  62. package/dist/resources/extensions/gsd/write-intercept.js +10 -1
  63. package/dist/resources/extensions/ollama/index.js +17 -10
  64. package/dist/resources/extensions/ollama/ollama-client.js +35 -6
  65. package/dist/resources/extensions/ollama/ollama-discovery.js +32 -6
  66. package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
  67. package/dist/resources/extensions/subagent/agents.js +8 -0
  68. package/dist/resources/extensions/subagent/index.js +17 -0
  69. package/dist/startup-model-validation.d.ts +0 -1
  70. package/dist/startup-model-validation.js +6 -2
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  73. package/dist/web/standalone/.next/build-manifest.json +2 -2
  74. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  75. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  76. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  86. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  115. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  120. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  130. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  131. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +3 -3
  133. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/index.html +1 -1
  138. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/page.js +2 -2
  145. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  147. package/dist/web/standalone/.next/server/chunks/2331.js +16 -16
  148. package/dist/web/standalone/.next/server/chunks/4741.js +12 -12
  149. package/dist/web/standalone/.next/server/chunks/5822.js +2 -2
  150. package/dist/web/standalone/.next/server/chunks/63.js +8 -8
  151. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  152. package/dist/web/standalone/.next/server/edge-runtime-webpack.js +2 -0
  153. package/dist/web/standalone/.next/server/functions-config-manifest.json +0 -9
  154. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/middleware-manifest.json +29 -2
  156. package/dist/web/standalone/.next/server/middleware.js +4 -12
  157. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  158. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  159. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  160. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  161. package/package.json +1 -1
  162. package/packages/mcp-server/dist/server.d.ts +12 -1
  163. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  164. package/packages/mcp-server/dist/server.js +90 -42
  165. package/packages/mcp-server/dist/server.js.map +1 -1
  166. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  167. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  168. package/packages/mcp-server/src/server.ts +110 -38
  169. package/packages/mcp-server/src/workflow-tools.ts +1 -1
  170. package/packages/pi-ai/dist/env-api-keys.js +1 -0
  171. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  172. package/packages/pi-ai/dist/models.custom.d.ts +105 -0
  173. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -1
  174. package/packages/pi-ai/dist/models.custom.js +97 -0
  175. package/packages/pi-ai/dist/models.custom.js.map +1 -1
  176. package/packages/pi-ai/dist/models.generated.d.ts +648 -140
  177. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  178. package/packages/pi-ai/dist/models.generated.js +867 -370
  179. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  180. package/packages/pi-ai/dist/models.generated.test.d.ts +2 -0
  181. package/packages/pi-ai/dist/models.generated.test.d.ts.map +1 -0
  182. package/packages/pi-ai/dist/models.generated.test.js +334 -0
  183. package/packages/pi-ai/dist/models.generated.test.js.map +1 -0
  184. package/packages/pi-ai/dist/models.test.js +105 -0
  185. package/packages/pi-ai/dist/models.test.js.map +1 -1
  186. package/packages/pi-ai/dist/types.d.ts +1 -1
  187. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  188. package/packages/pi-ai/dist/types.js.map +1 -1
  189. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  190. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +5 -1
  191. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  192. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts +2 -0
  193. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts.map +1 -0
  194. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +57 -0
  195. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -0
  196. package/packages/pi-ai/src/env-api-keys.ts +1 -0
  197. package/packages/pi-ai/src/models.custom.ts +98 -0
  198. package/packages/pi-ai/src/models.generated.test.ts +373 -0
  199. package/packages/pi-ai/src/models.generated.ts +867 -370
  200. package/packages/pi-ai/src/models.test.ts +135 -0
  201. package/packages/pi-ai/src/types.ts +1 -0
  202. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +71 -0
  203. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +4 -1
  204. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  206. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  208. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  209. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  210. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  211. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +5 -0
  212. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/retry-handler.js +55 -1
  214. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +57 -0
  216. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  217. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  219. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +87 -12
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  231. package/packages/pi-coding-agent/package.json +1 -1
  232. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  233. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  234. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +83 -0
  235. package/packages/pi-coding-agent/src/core/retry-handler.ts +60 -1
  236. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  237. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +72 -0
  238. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  239. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -12
  240. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  241. package/packages/pi-tui/dist/components/__tests__/editor.test.js +12 -0
  242. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -1
  243. package/packages/pi-tui/dist/components/__tests__/input.test.js +12 -0
  244. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  245. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  246. package/packages/pi-tui/dist/keys.js +27 -0
  247. package/packages/pi-tui/dist/keys.js.map +1 -1
  248. package/packages/pi-tui/src/components/__tests__/editor.test.ts +18 -0
  249. package/packages/pi-tui/src/components/__tests__/input.test.ts +18 -0
  250. package/packages/pi-tui/src/keys.ts +32 -0
  251. package/pkg/package.json +1 -1
  252. package/src/resources/agents/debugger.md +58 -0
  253. package/src/resources/agents/doc-writer.md +43 -0
  254. package/src/resources/agents/git-ops.md +56 -0
  255. package/src/resources/agents/javascript-pro.md +46 -271
  256. package/src/resources/agents/planner.md +55 -0
  257. package/src/resources/agents/refactorer.md +47 -0
  258. package/src/resources/agents/reviewer.md +48 -0
  259. package/src/resources/agents/security.md +59 -0
  260. package/src/resources/agents/tester.md +50 -0
  261. package/src/resources/agents/typescript-pro.md +41 -235
  262. package/src/resources/extensions/async-jobs/await-tool.test.ts +40 -7
  263. package/src/resources/extensions/async-jobs/await-tool.ts +7 -4
  264. package/src/resources/extensions/async-jobs/job-manager.ts +33 -3
  265. package/src/resources/extensions/claude-code-cli/partial-builder.ts +45 -12
  266. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +139 -8
  267. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +91 -2
  268. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +245 -2
  269. package/src/resources/extensions/gsd/auto/loop.ts +89 -1
  270. package/src/resources/extensions/gsd/auto/phases.ts +4 -0
  271. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -0
  272. package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
  273. package/src/resources/extensions/gsd/auto-recovery.ts +10 -0
  274. package/src/resources/extensions/gsd/auto-start.ts +31 -4
  275. package/src/resources/extensions/gsd/auto.ts +29 -20
  276. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  277. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -10
  278. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +2 -5
  279. package/src/resources/extensions/gsd/commands-handlers.ts +5 -1
  280. package/src/resources/extensions/gsd/context-injector.ts +1 -1
  281. package/src/resources/extensions/gsd/custom-workflow-engine.ts +4 -8
  282. package/src/resources/extensions/gsd/definition-io.ts +18 -0
  283. package/src/resources/extensions/gsd/dispatch-guard.ts +5 -0
  284. package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
  285. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +6 -3
  286. package/src/resources/extensions/gsd/error-classifier.ts +4 -1
  287. package/src/resources/extensions/gsd/gate-registry.ts +251 -0
  288. package/src/resources/extensions/gsd/git-service.ts +11 -8
  289. package/src/resources/extensions/gsd/gitignore.ts +12 -6
  290. package/src/resources/extensions/gsd/gsd-db.ts +105 -6
  291. package/src/resources/extensions/gsd/key-manager.ts +2 -0
  292. package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
  293. package/src/resources/extensions/gsd/notification-overlay.ts +27 -11
  294. package/src/resources/extensions/gsd/notification-store.ts +5 -4
  295. package/src/resources/extensions/gsd/preferences-skills.ts +2 -36
  296. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  297. package/src/resources/extensions/gsd/preferences.ts +19 -6
  298. package/src/resources/extensions/gsd/prompt-loader.ts +6 -1
  299. package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
  300. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -1
  301. package/src/resources/extensions/gsd/prompts/discuss.md +122 -13
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  303. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  304. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  305. package/src/resources/extensions/gsd/shortcut-defs.ts +8 -1
  306. package/src/resources/extensions/gsd/state.ts +33 -2
  307. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +27 -0
  308. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +14 -0
  309. package/src/resources/extensions/gsd/tests/block-db-writes.test.ts +63 -0
  310. package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
  311. package/src/resources/extensions/gsd/tests/definition-io.test.ts +57 -0
  312. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +26 -0
  313. package/src/resources/extensions/gsd/tests/doctor-heal-fixable-warnings.test.ts +14 -0
  314. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
  315. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +104 -0
  316. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +16 -0
  317. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
  318. package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
  319. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +107 -5
  320. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +8 -6
  321. package/src/resources/extensions/gsd/tests/key-manager.test.ts +63 -0
  322. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +54 -0
  323. package/src/resources/extensions/gsd/tests/plan-milestone-artifact-verification.test.ts +62 -0
  324. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +34 -0
  325. package/src/resources/extensions/gsd/tests/preferences-formatting.test.ts +87 -0
  326. package/src/resources/extensions/gsd/tests/preferences.test.ts +53 -0
  327. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +96 -1
  328. package/src/resources/extensions/gsd/tests/prompt-loader-working-directory.test.ts +19 -0
  329. package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
  330. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  331. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +97 -0
  332. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +3 -2
  333. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +41 -0
  334. package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
  335. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
  336. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +4 -1
  337. package/src/resources/extensions/gsd/types.ts +26 -0
  338. package/src/resources/extensions/gsd/workflow-projections.ts +8 -0
  339. package/src/resources/extensions/gsd/worktree-manager.ts +29 -3
  340. package/src/resources/extensions/gsd/write-intercept.ts +10 -1
  341. package/src/resources/extensions/ollama/index.ts +17 -8
  342. package/src/resources/extensions/ollama/ollama-client.ts +35 -6
  343. package/src/resources/extensions/ollama/ollama-discovery.ts +37 -6
  344. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  345. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +54 -0
  346. package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
  347. package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
  348. package/src/resources/extensions/subagent/agents.ts +10 -0
  349. package/src/resources/extensions/subagent/index.ts +18 -0
  350. package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
  351. /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → NzO79SOz9jHX-VY5-0t2O}/_buildManifest.js +0 -0
  352. /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → NzO79SOz9jHX-VY5-0t2O}/_ssgManifest.js +0 -0
@@ -82,3 +82,19 @@ test("shortcut-defs: formats shortcut pair using platform symbols", () => {
82
82
  assert.equal(pair, "Ctrl+Alt+N / Ctrl+Shift+N");
83
83
  }
84
84
  });
85
+
86
+ test("shortcut-defs: parallel shortcut omits fallback (hasFallback: false)", () => {
87
+ const pair = formattedShortcutPair("parallel");
88
+ if (process.platform === "darwin") {
89
+ assert.equal(pair, "⌃⌥P", "parallel should only show primary combo");
90
+ } else {
91
+ assert.equal(pair, "Ctrl+Alt+P", "parallel should only show primary combo");
92
+ }
93
+ // Verify it does NOT contain the fallback separator
94
+ assert.ok(!pair.includes("/"), "parallel pair should not contain fallback separator");
95
+ });
96
+
97
+ test("shortcut-defs: dashboard shortcut includes fallback (hasFallback: true)", () => {
98
+ const pair = formattedShortcutPair("dashboard");
99
+ assert.ok(pair.includes("/"), "dashboard pair should contain fallback separator");
100
+ });
@@ -186,4 +186,31 @@ describe("evaluating-gates phase", () => {
186
186
  insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q5", scope: "task", taskId: "T01" });
187
187
  assert.equal(getPendingSliceGateCount("M001", "S01"), 1);
188
188
  });
189
+
190
+ test("Q8 (owned by complete-slice) does not block evaluating-gates phase", async () => {
191
+ // Regression: Q8 is stored with scope:"slice" but owned by the
192
+ // complete-slice turn. Before the gate registry landed, deriveState
193
+ // counted Q8 as a blocker for evaluating-gates while the gate-evaluate
194
+ // prompt silently dropped Q8 — an unrecoverable stall. After the
195
+ // registry change, deriveState filters by owner turn, so Q8 never
196
+ // blocks evaluating-gates.
197
+ planSlice(tmpDir);
198
+ await renderPlanFromDb(tmpDir, "M001", "S01");
199
+
200
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
201
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q4", scope: "slice" });
202
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q8", scope: "slice" });
203
+
204
+ saveGateResult({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", verdict: "pass", rationale: "OK", findings: "" });
205
+ saveGateResult({ milestoneId: "M001", sliceId: "S01", gateId: "Q4", verdict: "omitted", rationale: "N/A", findings: "" });
206
+ // Q8 deliberately left pending — it's complete-slice's problem.
207
+
208
+ invalidateStateCache();
209
+ const state = await deriveState(tmpDir);
210
+ assert.equal(
211
+ state.phase,
212
+ "executing",
213
+ `pending Q8 must not stall evaluating-gates — got phase=${state.phase}`,
214
+ );
215
+ });
189
216
  });
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Gate registry tests — enforce that every declared GateId has a registry
3
+ * entry, that every owner-turn bucket is non-empty, and that coverage
4
+ * assertions fail loudly instead of silently skipping unknown gates.
5
+ */
6
+
7
+ import { describe, test } from "node:test";
8
+ import assert from "node:assert/strict";
9
+
10
+ import {
11
+ GATE_REGISTRY,
12
+ assertGateCoverage,
13
+ getGateDefinition,
14
+ getGateIdsForTurn,
15
+ getGatesForTurn,
16
+ getOwnerTurn,
17
+ type OwnerTurn,
18
+ } from "../gate-registry.ts";
19
+ import type { GateId } from "../types.ts";
20
+
21
+ /** Authoritative list of GateIds as declared in types.ts. */
22
+ const ALL_GATE_IDS: readonly GateId[] = [
23
+ "Q3", "Q4", "Q5", "Q6", "Q7", "Q8",
24
+ "MV01", "MV02", "MV03", "MV04",
25
+ ];
26
+
27
+ const ALL_OWNER_TURNS: readonly OwnerTurn[] = [
28
+ "gate-evaluate",
29
+ "execute-task",
30
+ "complete-slice",
31
+ "validate-milestone",
32
+ ];
33
+
34
+ describe("gate-registry", () => {
35
+ test("every declared GateId has a registry entry", () => {
36
+ for (const id of ALL_GATE_IDS) {
37
+ const def = GATE_REGISTRY[id];
38
+ assert.ok(def, `missing registry entry for gate ${id}`);
39
+ assert.equal(def.id, id);
40
+ assert.ok(def.question.length > 0, `${id} missing question`);
41
+ assert.ok(def.guidance.length > 0, `${id} missing guidance`);
42
+ assert.ok(def.promptSection.length > 0, `${id} missing promptSection`);
43
+ }
44
+ });
45
+
46
+ test("registry contains no extra gate entries", () => {
47
+ const registryIds = new Set(Object.keys(GATE_REGISTRY));
48
+ const declaredIds = new Set<string>(ALL_GATE_IDS);
49
+ for (const id of registryIds) {
50
+ assert.ok(declaredIds.has(id), `registry has unknown gate ${id}`);
51
+ }
52
+ });
53
+
54
+ test("every owner turn owns at least one gate", () => {
55
+ for (const turn of ALL_OWNER_TURNS) {
56
+ const gates = getGatesForTurn(turn);
57
+ assert.ok(
58
+ gates.length > 0,
59
+ `owner turn "${turn}" has no gates — likely a registry mistake`,
60
+ );
61
+ }
62
+ });
63
+
64
+ test("owner turn buckets are disjoint", () => {
65
+ const seen = new Set<string>();
66
+ for (const turn of ALL_OWNER_TURNS) {
67
+ for (const def of getGatesForTurn(turn)) {
68
+ assert.ok(!seen.has(def.id), `gate ${def.id} claimed by two turns`);
69
+ seen.add(def.id);
70
+ }
71
+ }
72
+ // Every gate should appear in exactly one bucket.
73
+ assert.equal(seen.size, ALL_GATE_IDS.length);
74
+ });
75
+
76
+ test("getOwnerTurn round-trips against GATE_REGISTRY", () => {
77
+ for (const id of ALL_GATE_IDS) {
78
+ const turn = getOwnerTurn(id);
79
+ const idsForTurn = getGateIdsForTurn(turn);
80
+ assert.ok(idsForTurn.has(id), `${id} not in ${turn} bucket`);
81
+ }
82
+ });
83
+
84
+ test("getGateDefinition returns undefined for unknown ids", () => {
85
+ assert.equal(getGateDefinition("Q99"), undefined);
86
+ assert.equal(getGateDefinition("not-a-gate"), undefined);
87
+ });
88
+ });
89
+
90
+ describe("assertGateCoverage", () => {
91
+ test("throws when a row is owned by a different turn", () => {
92
+ // Q8 is owned by complete-slice, not gate-evaluate — this used to be
93
+ // silently dropped by the old `if (!meta) continue;` filter, causing
94
+ // the evaluating-gates phase to stall.
95
+ assert.throws(
96
+ () => assertGateCoverage([{ gate_id: "Q8" }], "gate-evaluate"),
97
+ (err: Error) =>
98
+ err.message.includes("Q8") && err.message.includes("gate-evaluate"),
99
+ );
100
+ });
101
+
102
+ test("throws when a row has an unknown gate id", () => {
103
+ assert.throws(
104
+ () => assertGateCoverage([{ gate_id: "Q999" as GateId }], "gate-evaluate", { requireAll: false }),
105
+ (err: Error) => err.message.includes("Q999"),
106
+ );
107
+ });
108
+
109
+ test("throws when requireAll is true and an owned gate is missing", () => {
110
+ // gate-evaluate owns Q3 and Q4. Passing only Q3 should fail.
111
+ assert.throws(
112
+ () => assertGateCoverage([{ gate_id: "Q3" }], "gate-evaluate", { requireAll: true }),
113
+ (err: Error) => err.message.includes("Q4"),
114
+ );
115
+ });
116
+
117
+ test("passes when requireAll is false and only a subset is pending", () => {
118
+ // execute-task owns Q5/Q6/Q7, but a task with no external dependencies
119
+ // may only have Q7 seeded. That's still valid coverage.
120
+ assert.doesNotThrow(() =>
121
+ assertGateCoverage([{ gate_id: "Q7" }], "execute-task", { requireAll: false }),
122
+ );
123
+ });
124
+
125
+ test("passes when requireAll is true and every owned gate is pending", () => {
126
+ assert.doesNotThrow(() =>
127
+ assertGateCoverage(
128
+ [{ gate_id: "Q3" }, { gate_id: "Q4" }],
129
+ "gate-evaluate",
130
+ { requireAll: true },
131
+ ),
132
+ );
133
+ });
134
+
135
+ test("empty pending list passes when requireAll is false", () => {
136
+ assert.doesNotThrow(() =>
137
+ assertGateCoverage([], "complete-slice", { requireAll: false }),
138
+ );
139
+ });
140
+ });
@@ -15,6 +15,7 @@ import {
15
15
  getRequirementById,
16
16
  getActiveDecisions,
17
17
  getActiveRequirements,
18
+ getTask,
18
19
  transaction,
19
20
  _getAdapter,
20
21
  _resetProvider,
@@ -43,6 +44,16 @@ function cleanup(dbPath: string): void {
43
44
  }
44
45
  }
45
46
 
47
+ function withPlatform<T>(platform: NodeJS.Platform, fn: () => T): T {
48
+ const original = process.platform;
49
+ Object.defineProperty(process, 'platform', { value: platform });
50
+ try {
51
+ return fn();
52
+ } finally {
53
+ Object.defineProperty(process, 'platform', { value: original });
54
+ }
55
+ }
56
+
46
57
  // ═══════════════════════════════════════════════════════════════════════════
47
58
  // gsd-db tests
48
59
  // ═══════════════════════════════════════════════════════════════════════════
@@ -279,6 +290,26 @@ describe('gsd-db', () => {
279
290
  cleanup(dbPath);
280
291
  });
281
292
 
293
+ test('gsd-db: mmap stays disabled on darwin file-backed DBs', () => {
294
+ const darwinDbPath = tempDbPath();
295
+ withPlatform('darwin', () => {
296
+ openDatabase(darwinDbPath);
297
+ const adapter = _getAdapter()!;
298
+ const mmap = adapter.prepare('PRAGMA mmap_size').get();
299
+ assert.deepStrictEqual(mmap?.['mmap_size'], 0, 'darwin should leave mmap_size disabled');
300
+ cleanup(darwinDbPath);
301
+ });
302
+
303
+ const linuxDbPath = tempDbPath();
304
+ withPlatform('linux', () => {
305
+ openDatabase(linuxDbPath);
306
+ const adapter = _getAdapter()!;
307
+ const mmap = adapter.prepare('PRAGMA mmap_size').get();
308
+ assert.deepStrictEqual(mmap?.['mmap_size'], 67108864, 'non-darwin should still enable mmap_size');
309
+ cleanup(linuxDbPath);
310
+ });
311
+ });
312
+
282
313
  test('gsd-db: transaction rollback on error', () => {
283
314
  openDatabase(':memory:');
284
315
 
@@ -329,6 +360,79 @@ describe('gsd-db', () => {
329
360
  closeDatabase();
330
361
  });
331
362
 
363
+ test('gsd-db: recreates missing verification evidence dedup index after removing duplicate rows', () => {
364
+ const dbPath = tempDbPath();
365
+ openDatabase(dbPath);
366
+
367
+ let adapter = _getAdapter()!;
368
+ adapter.prepare("INSERT INTO milestones (id, created_at) VALUES (?, '')").run('M001');
369
+ adapter.prepare("INSERT INTO slices (milestone_id, id, created_at) VALUES (?, ?, '')").run('M001', 'S01');
370
+ adapter.prepare("INSERT INTO tasks (milestone_id, slice_id, id) VALUES (?, ?, ?)").run('M001', 'S01', 'T01');
371
+ adapter.exec('DROP INDEX IF EXISTS idx_verification_evidence_dedup');
372
+
373
+ const insertEvidence = adapter.prepare(
374
+ `INSERT INTO verification_evidence (
375
+ task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
376
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
377
+ );
378
+ insertEvidence.run('T01', 'S01', 'M001', 'npm test', 1, 'fail', 125, '2026-04-12T00:00:00.000Z');
379
+ insertEvidence.run('T01', 'S01', 'M001', 'npm test', 1, 'fail', 125, '2026-04-12T00:00:01.000Z');
380
+ insertEvidence.run('T01', 'S01', 'M001', 'npm run lint', 0, 'pass', 90, '2026-04-12T00:00:02.000Z');
381
+
382
+ closeDatabase();
383
+
384
+ assert.equal(openDatabase(dbPath), true, 'openDatabase should repair legacy duplicate evidence rows');
385
+
386
+ adapter = _getAdapter()!;
387
+ const countRow = adapter.prepare(
388
+ `SELECT count(*) as cnt
389
+ FROM verification_evidence
390
+ WHERE task_id = ? AND slice_id = ? AND milestone_id = ? AND command = ? AND verdict = ?`,
391
+ ).get('T01', 'S01', 'M001', 'npm test', 'fail');
392
+ assert.equal(countRow?.['cnt'], 1, 'duplicate verification evidence rows should be deduplicated before index creation');
393
+
394
+ const indexRow = adapter.prepare(
395
+ "SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx_verification_evidence_dedup'",
396
+ ).get();
397
+ assert.equal(indexRow?.['name'], 'idx_verification_evidence_dedup', 'dedup index should be recreated on reopen');
398
+
399
+ cleanup(dbPath);
400
+ });
401
+
402
+ test('gsd-db: rowToTask tolerates legacy comma-separated task arrays', () => {
403
+ openDatabase(':memory:');
404
+
405
+ const adapter = _getAdapter()!;
406
+ adapter.prepare("INSERT INTO milestones (id, created_at) VALUES (?, '')").run('M001');
407
+ adapter.prepare("INSERT INTO slices (milestone_id, id, created_at) VALUES (?, ?, '')").run('M001', 'S01');
408
+ adapter.prepare(
409
+ `INSERT INTO tasks (
410
+ milestone_id, slice_id, id, key_files, key_decisions, files, inputs, expected_output
411
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
412
+ ).run(
413
+ 'M001',
414
+ 'S01',
415
+ 'T01',
416
+ '[]',
417
+ '[]',
418
+ 'tests/test_verify.py, config.yaml, configs/roster_2026-05-11.yaml',
419
+ 'tests/test_verify.py',
420
+ 'reports/summary.md, artifacts/output.json',
421
+ );
422
+
423
+ const task = getTask('M001', 'S01', 'T01');
424
+ assert.ok(task, 'task should load successfully from DB');
425
+ assert.deepEqual(task?.files, [
426
+ 'tests/test_verify.py',
427
+ 'config.yaml',
428
+ 'configs/roster_2026-05-11.yaml',
429
+ ]);
430
+ assert.deepEqual(task?.inputs, ['tests/test_verify.py']);
431
+ assert.deepEqual(task?.expected_output, ['reports/summary.md', 'artifacts/output.json']);
432
+
433
+ closeDatabase();
434
+ });
435
+
332
436
  test('gsd-db: query wrappers return null/empty when DB unavailable', () => {
333
437
  // Ensure DB is closed
334
438
  closeDatabase();
@@ -347,15 +451,13 @@ describe('gsd-db', () => {
347
451
  assert.deepStrictEqual(ar, [], 'getActiveRequirements returns [] when DB closed');
348
452
  });
349
453
 
350
- test('gsd-db: wasDbOpenAttempted tracks openDatabase calls', () => {
351
- // wasDbOpenAttempted should return true once openDatabase has been called
352
- // (previous tests in this suite already called openDatabase, so the flag is set)
454
+ test('gsd-db: closeDatabase resets wasDbOpenAttempted after an intentional close', () => {
455
+ openDatabase(':memory:');
353
456
  assert.ok(wasDbOpenAttempted(), 'wasDbOpenAttempted should be true after openDatabase was called');
354
457
 
355
- // Verify the flag persists even after closeDatabase
356
458
  closeDatabase();
357
459
  assert.ok(!isDbAvailable(), 'DB should not be available after close');
358
- assert.ok(wasDbOpenAttempted(), 'wasDbOpenAttempted should remain true after closeDatabase');
460
+ assert.ok(!wasDbOpenAttempted(), 'wasDbOpenAttempted should reset after closeDatabase');
359
461
  });
360
462
 
361
463
  // ─── Final Report ──────────────────────────────────────────────────────────
@@ -248,23 +248,25 @@ describe('git-service', async () => {
248
248
 
249
249
  assert.deepStrictEqual(
250
250
  RUNTIME_EXCLUSION_PATHS.length,
251
- 13,
252
- "exactly 13 runtime exclusion paths"
251
+ 15,
252
+ "exactly 15 runtime exclusion paths"
253
253
  );
254
254
 
255
255
  const expectedPaths = [
256
256
  ".gsd/activity/",
257
+ ".gsd/forensics/",
257
258
  ".gsd/runtime/",
258
259
  ".gsd/worktrees/",
260
+ ".gsd/parallel/",
259
261
  ".gsd/auto.lock",
260
262
  ".gsd/metrics.json",
261
- ".gsd/completed-units.json",
263
+ ".gsd/completed-units*.json",
264
+ ".gsd/state-manifest.json",
262
265
  ".gsd/STATE.md",
263
- ".gsd/gsd.db",
264
- ".gsd/gsd.db-shm",
265
- ".gsd/gsd.db-wal",
266
+ ".gsd/gsd.db*",
266
267
  ".gsd/journal/",
267
268
  ".gsd/doctor-history.jsonl",
269
+ ".gsd/event-log.jsonl",
268
270
  ".gsd/DISCUSSION-MANIFEST.json",
269
271
  ];
270
272
 
@@ -427,3 +427,66 @@ test("formatDoctorFindings shows findings with appropriate icons", () => {
427
427
  assert.ok(output.includes("1 warning"));
428
428
  assert.ok(output.includes("1 fixed"));
429
429
  });
430
+
431
+ // ─── Regression #3891 — alibaba-coding-plan missing from PROVIDER_REGISTRY ───────
432
+ //
433
+ // Before this fix, `alibaba-coding-plan` was not in PROVIDER_REGISTRY, causing
434
+ // `/gsd keys add alibaba-coding-plan` to silently fail (provider not found).
435
+ // alibaba-dashscope is the new standalone provider added in the same PR.
436
+
437
+ test("regression #3891 — alibaba-coding-plan is in PROVIDER_REGISTRY", () => {
438
+ const provider = findProvider("alibaba-coding-plan");
439
+ assert.ok(provider, "alibaba-coding-plan must be in PROVIDER_REGISTRY for /gsd keys add to work");
440
+ assert.equal(provider.id, "alibaba-coding-plan");
441
+ assert.equal(provider.category, "llm");
442
+ assert.equal(provider.envVar, "ALIBABA_API_KEY");
443
+ });
444
+
445
+ test("alibaba-dashscope is in PROVIDER_REGISTRY", () => {
446
+ const provider = findProvider("alibaba-dashscope");
447
+ assert.ok(provider, "alibaba-dashscope must be in PROVIDER_REGISTRY for /gsd keys add to work");
448
+ assert.equal(provider.id, "alibaba-dashscope");
449
+ assert.equal(provider.category, "llm");
450
+ assert.equal(provider.envVar, "DASHSCOPE_API_KEY");
451
+ });
452
+
453
+ test("alibaba-coding-plan and alibaba-dashscope are separate providers (different env vars)", () => {
454
+ const codingPlan = findProvider("alibaba-coding-plan");
455
+ const dashscope = findProvider("alibaba-dashscope");
456
+ assert.ok(codingPlan, "alibaba-coding-plan must exist");
457
+ assert.ok(dashscope, "alibaba-dashscope must exist");
458
+ assert.notEqual(
459
+ codingPlan.envVar,
460
+ dashscope.envVar,
461
+ "alibaba-coding-plan and alibaba-dashscope must use different env vars",
462
+ );
463
+ });
464
+
465
+ test("getAllKeyStatuses includes alibaba-coding-plan", () => {
466
+ const auth = makeAuth();
467
+ const statuses = getAllKeyStatuses(auth);
468
+ const found = statuses.find((s) => s.provider.id === "alibaba-coding-plan");
469
+ assert.ok(found, "getAllKeyStatuses must include alibaba-coding-plan");
470
+ });
471
+
472
+ test("getAllKeyStatuses includes alibaba-dashscope", () => {
473
+ const auth = makeAuth();
474
+ const statuses = getAllKeyStatuses(auth);
475
+ const found = statuses.find((s) => s.provider.id === "alibaba-dashscope");
476
+ assert.ok(found, "getAllKeyStatuses must include alibaba-dashscope");
477
+ });
478
+
479
+ test("getAllKeyStatuses detects DASHSCOPE_API_KEY for alibaba-dashscope (failure path: missing key shows not configured)", () => {
480
+ const saved = process.env.DASHSCOPE_API_KEY;
481
+ delete process.env.DASHSCOPE_API_KEY;
482
+ try {
483
+ const auth = makeAuth();
484
+ const statuses = getAllKeyStatuses(auth);
485
+ const found = statuses.find((s) => s.provider.id === "alibaba-dashscope");
486
+ assert.ok(found);
487
+ assert.equal(found.configured, false);
488
+ assert.equal(found.source, "none");
489
+ } finally {
490
+ if (saved !== undefined) process.env.DASHSCOPE_API_KEY = saved;
491
+ }
492
+ });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Regression tests for memory pressure monitoring (#3331) and
3
+ * stuck detection persistence (#3704) in auto/loop.ts.
4
+ */
5
+
6
+ import { describe, test } from "node:test";
7
+ import assert from "node:assert/strict";
8
+ import { readFileSync } from "node:fs";
9
+ import { join, dirname } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const loopSource = readFileSync(join(__dirname, "..", "auto", "loop.ts"), "utf-8");
14
+
15
+ describe("memory pressure monitoring (#3331)", () => {
16
+ test("checkMemoryPressure function exists", () => {
17
+ assert.match(loopSource, /function checkMemoryPressure/);
18
+ });
19
+
20
+ test("MEMORY_PRESSURE_THRESHOLD constant is defined", () => {
21
+ assert.match(loopSource, /MEMORY_PRESSURE_THRESHOLD\s*=\s*0\.\d+/);
22
+ });
23
+
24
+ test("memory check runs every MEMORY_CHECK_INTERVAL iterations", () => {
25
+ assert.match(loopSource, /iteration\s*%\s*MEMORY_CHECK_INTERVAL\s*===\s*0/);
26
+ });
27
+
28
+ test("memory pressure triggers graceful stopAuto", () => {
29
+ assert.match(loopSource, /mem\.pressured/);
30
+ assert.match(loopSource, /Stopping gracefully to prevent OOM/);
31
+ });
32
+ });
33
+
34
+ describe("stuck detection persistence (#3704)", () => {
35
+ test("loadStuckState function exists", () => {
36
+ assert.match(loopSource, /function loadStuckState/);
37
+ });
38
+
39
+ test("saveStuckState function exists", () => {
40
+ assert.match(loopSource, /function saveStuckState/);
41
+ });
42
+
43
+ test("loopState initialized from persisted state", () => {
44
+ assert.match(loopSource, /loadStuckState\(s\.basePath\)/);
45
+ });
46
+
47
+ test("stuck state saved after each iteration", () => {
48
+ assert.match(loopSource, /saveStuckState\(s\.basePath,\s*loopState\)/);
49
+ });
50
+
51
+ test("stuck state file path uses runtime directory", () => {
52
+ assert.match(loopSource, /stuck-state\.json/);
53
+ });
54
+ });
@@ -0,0 +1,62 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+
7
+ import { verifyExpectedArtifact } from "../auto-recovery.ts";
8
+
9
+ function createFixtureBase(): string {
10
+ const base = mkdtempSync(join(tmpdir(), "gsd-plan-milestone-artifact-"));
11
+ mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
12
+ return base;
13
+ }
14
+
15
+ function writeRoadmap(base: string, milestoneId: string, content: string): void {
16
+ const milestoneDir = join(base, ".gsd", "milestones", milestoneId);
17
+ mkdirSync(milestoneDir, { recursive: true });
18
+ writeFileSync(join(milestoneDir, `${milestoneId}-ROADMAP.md`), content, "utf-8");
19
+ }
20
+
21
+ test("#3405: plan-milestone roadmap stub does not count as a verified artifact", () => {
22
+ const base = createFixtureBase();
23
+ try {
24
+ writeRoadmap(base, "M001", [
25
+ "# M001: Placeholder",
26
+ "",
27
+ "**Vision:** Stub only.",
28
+ "",
29
+ "## Slices",
30
+ "",
31
+ "_TBD_",
32
+ "",
33
+ ].join("\n"));
34
+
35
+ const result = verifyExpectedArtifact("plan-milestone", "M001", base);
36
+ assert.equal(result, false, "zero-slice roadmap stubs must fail verification");
37
+ } finally {
38
+ rmSync(base, { recursive: true, force: true });
39
+ }
40
+ });
41
+
42
+ test("#3405: plan-milestone roadmap with real slices still passes artifact verification", () => {
43
+ const base = createFixtureBase();
44
+ try {
45
+ writeRoadmap(base, "M001", [
46
+ "# M001: Real roadmap",
47
+ "",
48
+ "**Vision:** Real work.",
49
+ "",
50
+ "## Slices",
51
+ "",
52
+ "- [ ] **S01: First slice** `risk:low` `depends:[]`",
53
+ " > After this: a real slice exists.",
54
+ "",
55
+ ].join("\n"));
56
+
57
+ const result = verifyExpectedArtifact("plan-milestone", "M001", base);
58
+ assert.equal(result, true, "real roadmap slices should keep passing verification");
59
+ } finally {
60
+ rmSync(base, { recursive: true, force: true });
61
+ }
62
+ });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Regression test for #3869: normal post-unit flow should rebuild STATE.md
3
+ * before syncing worktree state back to the project root.
4
+ */
5
+
6
+ import test from "node:test";
7
+ import assert from "node:assert/strict";
8
+ import { readFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+
11
+ const source = readFileSync(join(import.meta.dirname, "..", "auto-post-unit.ts"), "utf-8");
12
+
13
+ test("auto-post-unit imports rebuildState", () => {
14
+ assert.ok(
15
+ source.includes('import { rebuildState } from "./doctor.js";'),
16
+ "auto-post-unit.ts should import rebuildState from doctor.ts",
17
+ );
18
+ });
19
+
20
+ test("postUnitPreVerification rebuilds STATE.md before worktree sync", () => {
21
+ const fnStart = source.indexOf("export async function postUnitPreVerification");
22
+ assert.ok(fnStart > 0, "postUnitPreVerification should exist");
23
+
24
+ const section = source.slice(fnStart, fnStart + 8000);
25
+ const rebuildIdx = section.indexOf('await runSafely("postUnit", "state-rebuild"');
26
+ const syncIdx = section.indexOf('await runSafely("postUnit", "worktree-sync"');
27
+
28
+ assert.ok(rebuildIdx > 0, "postUnitPreVerification should rebuild STATE.md after unit completion");
29
+ assert.ok(syncIdx > 0, "postUnitPreVerification should sync worktree state back to the project root");
30
+ assert.ok(
31
+ rebuildIdx < syncIdx,
32
+ "STATE.md rebuild should happen before worktree sync so synced state is fresh",
33
+ );
34
+ });