gsd-pi 2.72.0 → 2.73.0-dev.27730dc

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 (344) hide show
  1. package/README.md +12 -2
  2. package/dist/cli.js +59 -50
  3. package/dist/help-text.js +1 -1
  4. package/dist/onboarding.js +10 -0
  5. package/dist/resources/extensions/async-jobs/await-tool.js +7 -4
  6. package/dist/resources/extensions/async-jobs/job-manager.js +28 -3
  7. package/dist/resources/extensions/claude-code-cli/partial-builder.js +40 -12
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +48 -23
  9. package/dist/resources/extensions/gsd/auto/loop.js +84 -1
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +5 -3
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +6 -0
  12. package/dist/resources/extensions/gsd/auto-prompts.js +9 -6
  13. package/dist/resources/extensions/gsd/auto-recovery.js +11 -0
  14. package/dist/resources/extensions/gsd/auto.js +30 -20
  15. package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
  16. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -11
  18. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -1
  19. package/dist/resources/extensions/gsd/commands-handlers.js +4 -1
  20. package/dist/resources/extensions/gsd/context-injector.js +1 -1
  21. package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
  22. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -7
  23. package/dist/resources/extensions/gsd/definition-io.js +15 -0
  24. package/dist/resources/extensions/gsd/dispatch-guard.js +4 -0
  25. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +6 -3
  26. package/dist/resources/extensions/gsd/git-service.js +11 -8
  27. package/dist/resources/extensions/gsd/gitignore.js +12 -6
  28. package/dist/resources/extensions/gsd/gsd-db.js +85 -8
  29. package/dist/resources/extensions/gsd/key-manager.js +2 -0
  30. package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
  31. package/dist/resources/extensions/gsd/preferences-skills.js +2 -34
  32. package/dist/resources/extensions/gsd/preferences-types.js +15 -0
  33. package/dist/resources/extensions/gsd/preferences.js +16 -3
  34. package/dist/resources/extensions/gsd/prompt-loader.js +4 -1
  35. package/dist/resources/extensions/gsd/prompts/discuss.md +122 -13
  36. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  37. package/dist/resources/extensions/gsd/state.js +21 -1
  38. package/dist/resources/extensions/gsd/workflow-projections.js +7 -0
  39. package/dist/resources/extensions/gsd/worktree-manager.js +30 -3
  40. package/dist/resources/extensions/gsd/write-intercept.js +10 -1
  41. package/dist/resources/extensions/ollama/index.js +4 -5
  42. package/dist/resources/extensions/ollama/ollama-client.js +35 -6
  43. package/dist/resources/extensions/ollama/ollama-discovery.js +32 -6
  44. package/dist/startup-model-validation.js +8 -5
  45. package/dist/web/standalone/.next/BUILD_ID +1 -1
  46. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  47. package/dist/web/standalone/.next/build-manifest.json +3 -3
  48. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  49. package/dist/web/standalone/.next/required-server-files.json +3 -3
  50. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  51. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  61. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  77. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  89. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  109. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  119. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  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/browser/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  125. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  139. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  141. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  143. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +3 -3
  145. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/index.html +1 -1
  155. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  156. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  157. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  158. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  159. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  160. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  161. package/dist/web/standalone/.next/server/app/page.js +2 -2
  162. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  164. package/dist/web/standalone/.next/server/chunks/2331.js +16 -16
  165. package/dist/web/standalone/.next/server/chunks/4741.js +12 -12
  166. package/dist/web/standalone/.next/server/chunks/5822.js +2 -2
  167. package/dist/web/standalone/.next/server/chunks/63.js +8 -8
  168. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  169. package/dist/web/standalone/.next/server/edge-runtime-webpack.js +2 -0
  170. package/dist/web/standalone/.next/server/functions-config-manifest.json +0 -9
  171. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/middleware-manifest.json +29 -2
  173. package/dist/web/standalone/.next/server/middleware.js +4 -12
  174. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  176. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  177. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  178. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  179. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  180. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  181. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  182. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  183. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  184. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  185. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  186. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  187. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  188. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  189. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  190. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  191. package/dist/web/standalone/server.js +1 -1
  192. package/package.json +1 -1
  193. package/packages/pi-ai/dist/env-api-keys.js +1 -0
  194. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  195. package/packages/pi-ai/dist/models.custom.d.ts +105 -0
  196. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -1
  197. package/packages/pi-ai/dist/models.custom.js +97 -0
  198. package/packages/pi-ai/dist/models.custom.js.map +1 -1
  199. package/packages/pi-ai/dist/models.generated.d.ts +648 -140
  200. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  201. package/packages/pi-ai/dist/models.generated.js +867 -370
  202. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  203. package/packages/pi-ai/dist/models.generated.test.d.ts +2 -0
  204. package/packages/pi-ai/dist/models.generated.test.d.ts.map +1 -0
  205. package/packages/pi-ai/dist/models.generated.test.js +334 -0
  206. package/packages/pi-ai/dist/models.generated.test.js.map +1 -0
  207. package/packages/pi-ai/dist/models.test.js +105 -0
  208. package/packages/pi-ai/dist/models.test.js.map +1 -1
  209. package/packages/pi-ai/dist/types.d.ts +1 -1
  210. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  211. package/packages/pi-ai/dist/types.js.map +1 -1
  212. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  213. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +5 -1
  214. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  215. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts +2 -0
  216. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts.map +1 -0
  217. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +57 -0
  218. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -0
  219. package/packages/pi-ai/src/env-api-keys.ts +1 -0
  220. package/packages/pi-ai/src/models.custom.ts +98 -0
  221. package/packages/pi-ai/src/models.generated.test.ts +373 -0
  222. package/packages/pi-ai/src/models.generated.ts +867 -370
  223. package/packages/pi-ai/src/models.test.ts +135 -0
  224. package/packages/pi-ai/src/types.ts +1 -0
  225. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +71 -0
  226. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +4 -1
  227. package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -1
  228. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
  230. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  231. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  232. package/packages/pi-coding-agent/dist/core/model-resolver.js +25 -67
  233. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  234. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  236. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +87 -12
  241. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +22 -9
  245. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
  248. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
  249. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -0
  250. package/packages/pi-coding-agent/package.json +1 -1
  251. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +38 -0
  252. package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -1
  253. package/packages/pi-coding-agent/src/core/model-resolver.ts +26 -69
  254. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  255. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +72 -0
  256. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -12
  257. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
  258. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +23 -9
  259. package/packages/pi-tui/dist/components/__tests__/editor.test.js +12 -0
  260. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -1
  261. package/packages/pi-tui/dist/components/__tests__/input.test.js +12 -0
  262. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  263. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  264. package/packages/pi-tui/dist/keys.js +27 -0
  265. package/packages/pi-tui/dist/keys.js.map +1 -1
  266. package/packages/pi-tui/src/components/__tests__/editor.test.ts +18 -0
  267. package/packages/pi-tui/src/components/__tests__/input.test.ts +18 -0
  268. package/packages/pi-tui/src/keys.ts +32 -0
  269. package/pkg/package.json +1 -1
  270. package/src/resources/extensions/async-jobs/await-tool.test.ts +40 -7
  271. package/src/resources/extensions/async-jobs/await-tool.ts +7 -4
  272. package/src/resources/extensions/async-jobs/job-manager.ts +33 -3
  273. package/src/resources/extensions/claude-code-cli/partial-builder.ts +45 -12
  274. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +49 -24
  275. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +91 -2
  276. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +112 -0
  277. package/src/resources/extensions/gsd/auto/loop.ts +89 -1
  278. package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
  279. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -0
  280. package/src/resources/extensions/gsd/auto-prompts.ts +9 -3
  281. package/src/resources/extensions/gsd/auto-recovery.ts +10 -0
  282. package/src/resources/extensions/gsd/auto.ts +30 -20
  283. package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
  284. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
  285. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -10
  286. package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -1
  287. package/src/resources/extensions/gsd/commands-handlers.ts +5 -1
  288. package/src/resources/extensions/gsd/context-injector.ts +1 -1
  289. package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
  290. package/src/resources/extensions/gsd/custom-workflow-engine.ts +4 -8
  291. package/src/resources/extensions/gsd/definition-io.ts +18 -0
  292. package/src/resources/extensions/gsd/dispatch-guard.ts +5 -0
  293. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +6 -3
  294. package/src/resources/extensions/gsd/git-service.ts +11 -8
  295. package/src/resources/extensions/gsd/gitignore.ts +12 -6
  296. package/src/resources/extensions/gsd/gsd-db.ts +106 -8
  297. package/src/resources/extensions/gsd/key-manager.ts +2 -0
  298. package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
  299. package/src/resources/extensions/gsd/preferences-skills.ts +2 -36
  300. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  301. package/src/resources/extensions/gsd/preferences.ts +19 -6
  302. package/src/resources/extensions/gsd/prompt-loader.ts +6 -1
  303. package/src/resources/extensions/gsd/prompts/discuss.md +122 -13
  304. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  305. package/src/resources/extensions/gsd/state.ts +20 -0
  306. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +27 -0
  307. package/src/resources/extensions/gsd/tests/block-db-writes.test.ts +63 -0
  308. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
  309. package/src/resources/extensions/gsd/tests/definition-io.test.ts +57 -0
  310. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +26 -0
  311. package/src/resources/extensions/gsd/tests/doctor-heal-fixable-warnings.test.ts +14 -0
  312. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +104 -0
  313. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +165 -5
  314. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +8 -6
  315. package/src/resources/extensions/gsd/tests/key-manager.test.ts +63 -0
  316. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +54 -0
  317. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
  318. package/src/resources/extensions/gsd/tests/plan-milestone-artifact-verification.test.ts +62 -0
  319. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +34 -0
  320. package/src/resources/extensions/gsd/tests/preferences-formatting.test.ts +87 -0
  321. package/src/resources/extensions/gsd/tests/preferences.test.ts +53 -0
  322. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +96 -1
  323. package/src/resources/extensions/gsd/tests/prompt-loader-working-directory.test.ts +19 -0
  324. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +97 -0
  325. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +41 -0
  326. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +267 -0
  327. package/src/resources/extensions/gsd/workflow-projections.ts +8 -0
  328. package/src/resources/extensions/gsd/worktree-manager.ts +29 -3
  329. package/src/resources/extensions/gsd/write-intercept.ts +10 -1
  330. package/src/resources/extensions/ollama/index.ts +4 -5
  331. package/src/resources/extensions/ollama/ollama-client.ts +35 -6
  332. package/src/resources/extensions/ollama/ollama-discovery.ts +37 -6
  333. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +54 -0
  334. package/dist/resources/extensions/gsd/auto-observability.js +0 -54
  335. package/dist/resources/extensions/gsd/file-watcher.js +0 -80
  336. package/dist/resources/extensions/gsd/rtk-status.js +0 -43
  337. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  338. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  339. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  340. package/src/resources/extensions/gsd/auto-observability.ts +0 -72
  341. package/src/resources/extensions/gsd/file-watcher.ts +0 -100
  342. package/src/resources/extensions/gsd/rtk-status.ts +0 -53
  343. /package/dist/web/standalone/.next/static/{Y0I7CjXJl-tWoV__KidV4 → jNiH700EcljeLnbQ2_RCv}/_buildManifest.js +0 -0
  344. /package/dist/web/standalone/.next/static/{Y0I7CjXJl-tWoV__KidV4 → jNiH700EcljeLnbQ2_RCv}/_ssgManifest.js +0 -0
@@ -121,6 +121,25 @@ function openRawDb(path) {
121
121
  return new Database(path);
122
122
  }
123
123
  const SCHEMA_VERSION = 14;
124
+ function indexExists(db, name) {
125
+ return !!db.prepare("SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?").get(name);
126
+ }
127
+ function dedupeVerificationEvidenceRows(db) {
128
+ db.exec(`
129
+ DELETE FROM verification_evidence
130
+ WHERE rowid NOT IN (
131
+ SELECT MIN(rowid)
132
+ FROM verification_evidence
133
+ GROUP BY task_id, slice_id, milestone_id, command, verdict
134
+ )
135
+ `);
136
+ }
137
+ function ensureVerificationEvidenceDedupIndex(db) {
138
+ if (indexExists(db, "idx_verification_evidence_dedup"))
139
+ return;
140
+ dedupeVerificationEvidenceRows(db);
141
+ db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
142
+ }
124
143
  function initSchema(db, fileBacked) {
125
144
  if (fileBacked)
126
145
  db.exec("PRAGMA journal_mode=WAL");
@@ -132,7 +151,7 @@ function initSchema(db, fileBacked) {
132
151
  db.exec("PRAGMA auto_vacuum = INCREMENTAL");
133
152
  if (fileBacked)
134
153
  db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
135
- if (fileBacked)
154
+ if (fileBacked && process.platform !== "darwin")
136
155
  db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
137
156
  db.exec("PRAGMA temp_store = MEMORY");
138
157
  db.exec("PRAGMA foreign_keys = ON");
@@ -358,7 +377,7 @@ function initSchema(db, fileBacked) {
358
377
  db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
359
378
  db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
360
379
  db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
361
- db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
380
+ ensureVerificationEvidenceDedupIndex(db);
362
381
  // v14 index — slice dependency lookups
363
382
  db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
364
383
  db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
@@ -668,7 +687,7 @@ function migrateSchema(db) {
668
687
  db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
669
688
  db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
670
689
  db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
671
- db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
690
+ ensureVerificationEvidenceDedupIndex(db);
672
691
  db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
673
692
  ":version": 13,
674
693
  ":applied_at": new Date().toISOString(),
@@ -800,6 +819,7 @@ export function closeDatabase() {
800
819
  currentDb = null;
801
820
  currentPath = null;
802
821
  currentPid = 0;
822
+ _dbOpenAttempted = false;
803
823
  }
804
824
  }
805
825
  /** Run a full VACUUM — call sparingly (e.g. after milestone completion). */
@@ -1332,7 +1352,49 @@ export function setSliceSummaryMd(milestoneId, sliceId, summaryMd, uatMd) {
1332
1352
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1333
1353
  currentDb.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
1334
1354
  }
1355
+ function parseTaskArrayColumn(raw) {
1356
+ if (typeof raw !== "string" || raw.trim() === "")
1357
+ return [];
1358
+ try {
1359
+ const parsed = JSON.parse(raw);
1360
+ if (Array.isArray(parsed))
1361
+ return parsed.map((value) => String(value));
1362
+ if (parsed === null || parsed === undefined || parsed === "")
1363
+ return [];
1364
+ return [String(parsed)];
1365
+ }
1366
+ catch {
1367
+ // Older/corrupt rows may contain comma-separated strings instead of JSON.
1368
+ return raw
1369
+ .split(",")
1370
+ .map((value) => value.trim())
1371
+ .filter(Boolean);
1372
+ }
1373
+ }
1335
1374
  function rowToTask(row) {
1375
+ const parseTaskArray = (value) => {
1376
+ if (Array.isArray(value)) {
1377
+ return value.filter((entry) => typeof entry === "string");
1378
+ }
1379
+ if (typeof value !== "string")
1380
+ return [];
1381
+ const trimmed = value.trim();
1382
+ if (!trimmed)
1383
+ return [];
1384
+ try {
1385
+ const parsed = JSON.parse(trimmed);
1386
+ if (Array.isArray(parsed)) {
1387
+ return parsed.filter((entry) => typeof entry === "string");
1388
+ }
1389
+ if (typeof parsed === "string" && parsed.trim()) {
1390
+ return [parsed.trim()];
1391
+ }
1392
+ }
1393
+ catch {
1394
+ // Older/corrupt DB rows may contain raw comma-separated paths instead of JSON arrays.
1395
+ }
1396
+ return trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
1397
+ };
1336
1398
  return {
1337
1399
  milestone_id: row["milestone_id"],
1338
1400
  slice_id: row["slice_id"],
@@ -1347,15 +1409,15 @@ function rowToTask(row) {
1347
1409
  blocker_discovered: row["blocker_discovered"] === 1,
1348
1410
  deviations: row["deviations"],
1349
1411
  known_issues: row["known_issues"],
1350
- key_files: JSON.parse(row["key_files"] || "[]"),
1351
- key_decisions: JSON.parse(row["key_decisions"] || "[]"),
1412
+ key_files: parseTaskArrayColumn(row["key_files"]),
1413
+ key_decisions: parseTaskArrayColumn(row["key_decisions"]),
1352
1414
  full_summary_md: row["full_summary_md"],
1353
1415
  description: row["description"] ?? "",
1354
1416
  estimate: row["estimate"] ?? "",
1355
- files: JSON.parse(row["files"] || "[]"),
1417
+ files: parseTaskArray(row["files"]),
1356
1418
  verify: row["verify"] ?? "",
1357
- inputs: JSON.parse(row["inputs"] || "[]"),
1358
- expected_output: JSON.parse(row["expected_output"] || "[]"),
1419
+ inputs: parseTaskArray(row["inputs"]),
1420
+ expected_output: parseTaskArray(row["expected_output"]),
1359
1421
  observability_impact: row["observability_impact"] ?? "",
1360
1422
  full_plan_md: row["full_plan_md"] ?? "",
1361
1423
  sequence: row["sequence"] ?? 0,
@@ -1812,6 +1874,21 @@ export function deleteSlice(milestoneId, sliceId) {
1812
1874
  currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
1813
1875
  });
1814
1876
  }
1877
+ export function deleteMilestone(milestoneId) {
1878
+ if (!currentDb)
1879
+ throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1880
+ transaction(() => {
1881
+ currentDb.prepare(`DELETE FROM verification_evidence WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1882
+ currentDb.prepare(`DELETE FROM quality_gates WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1883
+ currentDb.prepare(`DELETE FROM tasks WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1884
+ currentDb.prepare(`DELETE FROM slice_dependencies WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1885
+ currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1886
+ currentDb.prepare(`DELETE FROM replan_history WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1887
+ currentDb.prepare(`DELETE FROM assessments WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1888
+ currentDb.prepare(`DELETE FROM artifacts WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1889
+ currentDb.prepare(`DELETE FROM milestones WHERE id = :mid`).run({ ":mid": milestoneId });
1890
+ });
1891
+ }
1815
1892
  export function updateSliceFields(milestoneId, sliceId, fields) {
1816
1893
  if (!currentDb)
1817
1894
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
@@ -26,6 +26,8 @@ export const PROVIDER_REGISTRY = [
26
26
  { id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
27
27
  { id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
28
28
  { id: "azure-openai-responses", label: "Azure OpenAI", category: "llm", envVar: "AZURE_OPENAI_API_KEY" },
29
+ { id: "alibaba-coding-plan", label: "Alibaba Coding Plan", category: "llm", envVar: "ALIBABA_API_KEY", dashboardUrl: "bailian.console.aliyun.com" },
30
+ { id: "alibaba-dashscope", label: "Alibaba DashScope", category: "llm", envVar: "DASHSCOPE_API_KEY", dashboardUrl: "dashscope.console.aliyun.com" },
29
31
  // Tool Keys
30
32
  { id: "context7", label: "Context7 Docs", category: "tool", envVar: "CONTEXT7_API_KEY", dashboardUrl: "context7.com/dashboard" },
31
33
  { id: "jina", label: "Jina Page Extract", category: "tool", envVar: "JINA_API_KEY", dashboardUrl: "jina.ai/api" },
@@ -15,7 +15,8 @@ import { join } from "node:path";
15
15
  import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } from "./paths.js";
16
16
  import { invalidateAllCaches } from "./cache.js";
17
17
  import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
18
- import { getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
18
+ import { deleteMilestone, getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
19
+ import { removeWorktree } from "./worktree-manager.js";
19
20
  import { logWarning } from "./workflow-logger.js";
20
21
  // ─── Park ──────────────────────────────────────────────────────────────────
21
22
  /**
@@ -99,12 +100,29 @@ export function discardMilestone(basePath, milestoneId) {
99
100
  const mDir = resolveMilestonePath(basePath, milestoneId);
100
101
  if (!mDir || !existsSync(mDir))
101
102
  return false;
103
+ try {
104
+ removeWorktree(basePath, milestoneId, {
105
+ branch: `milestone/${milestoneId}`,
106
+ deleteBranch: true,
107
+ });
108
+ }
109
+ catch (err) {
110
+ logWarning("engine", `discardMilestone worktree cleanup failed for ${milestoneId}: ${err.message}`);
111
+ }
102
112
  rmSync(mDir, { recursive: true, force: true });
103
113
  // Prune from queue order if present
104
114
  const order = loadQueueOrder(basePath);
105
115
  if (order && order.includes(milestoneId)) {
106
116
  saveQueueOrder(basePath, order.filter(id => id !== milestoneId));
107
117
  }
118
+ if (isDbAvailable()) {
119
+ try {
120
+ deleteMilestone(milestoneId);
121
+ }
122
+ catch (err) {
123
+ logWarning("engine", `discardMilestone DB cleanup failed for ${milestoneId}: ${err.message}`);
124
+ }
125
+ }
108
126
  invalidateAllCaches();
109
127
  return true;
110
128
  }
@@ -9,7 +9,6 @@ import { homedir } from "node:os";
9
9
  import { isAbsolute, join } from "node:path";
10
10
  import { statSync } from "node:fs";
11
11
  import { validatePreferences } from "./preferences-validation.js";
12
- import { loadEffectiveGSDPreferences } from "./preferences.js";
13
12
  /**
14
13
  * Known skill directories, in priority order.
15
14
  * Searches both the skills.sh ecosystem directory (~/.agents/skills/) and
@@ -130,36 +129,5 @@ export function resolveAllSkillReferences(preferences, cwd) {
130
129
  }
131
130
  return { resolutions, warnings };
132
131
  }
133
- /**
134
- * Format a skill reference for the system prompt.
135
- * If resolved, shows the path so the agent knows exactly where to read.
136
- * If unresolved, marks it clearly.
137
- */
138
- export function formatSkillRef(ref, resolutions) {
139
- const resolution = resolutions.get(ref);
140
- if (!resolution || resolution.method === "unresolved") {
141
- return `${ref} (⚠ not found — check skill name or path)`;
142
- }
143
- // For absolute paths where SKILL.md is just appended, don't clutter the output
144
- if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
145
- return ref;
146
- }
147
- // For bare names resolved from skill directories, show the resolved path
148
- return `${ref} → \`${resolution.resolvedPath}\``;
149
- }
150
- /**
151
- * Resolve the skill discovery mode from effective preferences.
152
- * Defaults to "suggest" -- skills are identified during research but not installed automatically.
153
- */
154
- export function resolveSkillDiscoveryMode() {
155
- const prefs = loadEffectiveGSDPreferences();
156
- return prefs?.preferences.skill_discovery ?? "suggest";
157
- }
158
- /**
159
- * Resolve the skill staleness threshold in days.
160
- * Returns 0 if disabled, default 60 if not configured.
161
- */
162
- export function resolveSkillStalenessDays() {
163
- const prefs = loadEffectiveGSDPreferences();
164
- return prefs?.preferences.skill_staleness_days ?? 60;
165
- }
132
+ // resolveSkillDiscoveryMode and resolveSkillStalenessDays moved to
133
+ // preferences.ts to break circular dependency (they need loadEffectiveGSDPreferences).
@@ -92,3 +92,18 @@ export const KNOWN_UNIT_TYPES = [
92
92
  "discuss-milestone", "discuss-slice", "worktree-merge",
93
93
  ];
94
94
  export const SKILL_ACTIONS = new Set(["use", "prefer", "avoid"]);
95
+ /**
96
+ * Format a skill reference for the system prompt.
97
+ * If resolved, shows the path so the agent knows exactly where to read.
98
+ * If unresolved, marks it clearly.
99
+ */
100
+ export function formatSkillRef(ref, resolutions) {
101
+ const resolution = resolutions.get(ref);
102
+ if (!resolution || resolution.method === "unresolved") {
103
+ return `${ref} (⚠ not found — check skill name or path)`;
104
+ }
105
+ if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
106
+ return ref;
107
+ }
108
+ return `${ref} → \`${resolution.resolvedPath}\``;
109
+ }
@@ -17,13 +17,23 @@ import { parse as parseYaml } from "yaml";
17
17
  import { normalizeStringArray } from "../shared/format-utils.js";
18
18
  import { logWarning } from "./workflow-logger.js";
19
19
  import { resolveProfileDefaults as _resolveProfileDefaults } from "./preferences-models.js";
20
- import { KNOWN_PREFERENCE_KEYS, MODE_DEFAULTS, } from "./preferences-types.js";
20
+ import { KNOWN_PREFERENCE_KEYS, MODE_DEFAULTS, formatSkillRef, } from "./preferences-types.js";
21
21
  import { validatePreferences } from "./preferences-validation.js";
22
- import { formatSkillRef } from "./preferences-skills.js";
23
22
  // ─── Re-exports: validation ─────────────────────────────────────────────────
24
23
  export { validatePreferences } from "./preferences-validation.js";
25
24
  // ─── Re-exports: skills ─────────────────────────────────────────────────────
26
- export { resolveAllSkillReferences, resolveSkillDiscoveryMode, resolveSkillStalenessDays, } from "./preferences-skills.js";
25
+ export { resolveAllSkillReferences } from "./preferences-skills.js";
26
+ // These lived in preferences-skills.ts but imported loadEffectiveGSDPreferences
27
+ // back from this file, creating a circular dependency. Moved here since they
28
+ // are trivial wrappers over loadEffectiveGSDPreferences.
29
+ export function resolveSkillDiscoveryMode() {
30
+ const prefs = loadEffectiveGSDPreferences();
31
+ return prefs?.preferences.skill_discovery ?? "suggest";
32
+ }
33
+ export function resolveSkillStalenessDays() {
34
+ const prefs = loadEffectiveGSDPreferences();
35
+ return prefs?.preferences.skill_staleness_days ?? 60;
36
+ }
27
37
  // ─── Re-exports: models ─────────────────────────────────────────────────────
28
38
  export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, isTransientNetworkError, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
29
39
  // ─── Path Constants & Getters ───────────────────────────────────────────────
@@ -304,6 +314,9 @@ function mergePreferences(base, override) {
304
314
  github: (base.github || override.github)
305
315
  ? { ...(base.github ?? {}), ...(override.github ?? {}) }
306
316
  : undefined,
317
+ experimental: (base.experimental || override.experimental)
318
+ ? { ...(base.experimental ?? {}), ...(override.experimental ?? {}) }
319
+ : undefined,
307
320
  service_tier: override.service_tier ?? base.service_tier,
308
321
  forensics_dedup: override.forensics_dedup ?? base.forensics_dedup,
309
322
  show_token_cost: override.show_token_cost ?? base.show_token_cost,
@@ -132,10 +132,13 @@ export function loadPrompt(name, vars = {}) {
132
132
  }
133
133
  }
134
134
  for (const [key, value] of Object.entries(effectiveVars)) {
135
+ const safeValue = key === "workingDirectory" && typeof value === "string"
136
+ ? value.replaceAll("\\", "/")
137
+ : value;
135
138
  // Use split/join instead of replaceAll to avoid JavaScript's special
136
139
  // replacement patterns ($', $`, $&) being interpreted in the value.
137
140
  // See: https://github.com/gsd-build/gsd-2/issues/2968
138
- content = content.split(`{{${key}}}`).join(value);
141
+ content = content.split(`{{${key}}}`).join(safeValue);
139
142
  }
140
143
  return content.trim();
141
144
  }
@@ -49,31 +49,132 @@ This happens ONCE, before the first round. The goal: your first questions should
49
49
 
50
50
  For subsequent rounds, continue investigating between rounds — check docs, search, or scout as needed to make each round's questions smarter. But the first-round investigation is mandatory and explicit. Distribute searches across turns rather than clustering them in one turn.
51
51
 
52
- ## Question Rounds
52
+ ## Layered Question Rounds
53
53
 
54
- Ask **1–3 questions per round**. Keep each round tightly focused on one or two of the depth checklist dimensions do not try to cover all six in one round.
54
+ Questions are organized into four layers. Each layer targets a specific depth dimension. At each layer: ask 1-3 open questions per round, investigate between rounds as needed, and gate before advancing.
55
55
 
56
- **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` for each round. 1–3 questions per call, each as a separate question object. Keep option labels short (3–5 words). Always include a freeform "Other / let me explain" option. When the user picks that option or writes a long freeform answer, switch to plain text follow-up for that thread before resuming structured questions. **IMPORTANT: Call `ask_user_questions` exactly once per turn. Never make multiple calls with the same or overlapping questions — wait for the user's response before asking the next round.**
56
+ **Default to open questions.** Use `ask_user_questions` only when there are 2-3 genuinely distinct paths with clear tradeoffs (e.g., "REST vs GraphQL" or "Postgres vs SQLite"). For nuanced design questions, ask in plain text and let the user explain.
57
57
 
58
- **If `{{structuredQuestionsAvailable}}` is `false`:** ask questions in plain text. Keep each round to 1–3 focused questions. Wait for answers before asking the next round.
58
+ **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` for binary/ternary choices. Keep option labels short (3-5 words). Always include a freeform "Other / let me explain" option. When the user picks that option or writes a long freeform answer, switch to plain text follow-up for that thread before resuming structured questions. **IMPORTANT: Call `ask_user_questions` exactly once per turn. Never make multiple calls with the same or overlapping questions — wait for the user's response before asking the next round.**
59
59
 
60
- After each answer set, investigate further if any answer opens a new unknown, then ask the next round.
60
+ **If `{{structuredQuestionsAvailable}}` is `false`:** ask questions in plain text. Keep each round to 1-3 focused questions. Wait for answers before asking the next round.
61
61
 
62
- ### Round cadence
62
+ **Incremental persistence:** After every 2 question rounds (across any layer), silently save a `{{milestoneId}}-CONTEXT-DRAFT.md` using `gsd_summary_save` with `artifact_type: "CONTEXT-DRAFT"` and `milestone_id: "{{milestoneId}}"`. This protects confirmed work against session crashes. Do NOT mention this save to the user.
63
63
 
64
- After each round of answers, decide whether you already have enough depth to write strong output.
64
+ ### Identify Work Type
65
65
 
66
- - **Incremental persistence:** After every 2 question rounds, silently save a `{{milestoneId}}-CONTEXT-DRAFT.md` using `gsd_summary_save` with `artifact_type: "CONTEXT-DRAFT"` and `milestone_id: "{{milestoneId}}"`. This protects confirmed work against session crashes. Do NOT mention this save to the user.
67
- - If not ready, continue to the next round immediately. Do **not** ask a meta "ready to wrap up?" question after every round.
68
- - **Depth-matching rule:** Simple, well-defined work needs fewer rounds — maybe 1–2. Large, ambiguous visions need more maybe 4+. Do not pad rounds to hit a number. Stop when the Depth Enforcement checklist below is fully satisfied.
69
- - Do not count the reflection step as a question round. Rounds start after reflection is confirmed.
70
- - When you genuinely believe the depth checklist is satisfied, move to the Depth Verification step below. Do not ask a separate "ready to wrap up?" gate the depth verification IS the gate.
66
+ Before starting Layer 1, identify the primary work type and state it:
67
+
68
+ "Based on your description and the codebase, this is primarily **[work type]** work."
69
+
70
+ Work types include: API/backend, UI/frontend, CLI/developer tool, data pipeline, ML/AI, infrastructure/platform, refactoring/migration, or a combination. The user can correct this. The classification shapes which questions deserve deep exploration at each layer.
71
+
72
+ ### Layer 1 — Scope
73
+
74
+ Resolve what's in and what's out. Ask about:
75
+ - Feature boundaries — what exactly ships in this milestone vs later
76
+ - Ambiguities in the user's description — anything you're unsure about, ask
77
+ - Dependencies — what does this work depend on, what depends on it
78
+ - Priority — if scope needs trimming, what matters most
79
+
80
+ Adapt depth to work type:
81
+ - **CLI work:** Focus on user mental model, command grammar, what existing commands do
82
+ - **Refactoring:** Focus on what changes vs what must stay identical
83
+
84
+ **Depth-matching:** Simple, well-defined scope may need 1 round. Ambiguous or large scope may need 3-4 rounds. Don't pad rounds to hit a number.
85
+
86
+ #### Layer 1 Gate
87
+
88
+ Summarize scope decisions in the user's own terminology:
89
+ - What's included, what's excluded, what's deferred
90
+ - Key boundaries and constraints
91
+
92
+ Then ask: **"Does this capture the scope? Adjust anything before we move on."**
93
+
94
+ If the user adjusts, reflect the updated understanding and ask again. Do not advance until the user explicitly confirms. If the user says "looks good, let's move faster" at any gate, respect that and advance.
95
+
96
+ ---
97
+
98
+ ### Layer 2 — Architecture
99
+
100
+ Resolve how it's built. Ask about:
101
+ - Per-slice technical decisions — what approach for each major piece
102
+ - Inter-slice contracts — how do the pieces connect
103
+ - Library/framework choices — with evidence from investigation, not assumptions
104
+ - Integration with existing code — what patterns to follow, what to change
105
+
106
+ Adapt depth to work type:
107
+ - **API work:** Contracts, versioning, backwards compatibility, auth boundaries
108
+ - **UI work:** Component boundaries, state management, data flow
109
+ - **Infrastructure:** Deployment topology, failure domains, rollback
110
+
111
+ Between rounds, use your available web search tools to research technologies from the Codebase Brief. Search for "[technology] [version] best practices [current year]" and "[technology] [version] known issues". Present findings alongside your questions.
112
+
113
+ #### Layer 2 Gate
114
+
115
+ Summarize architecture decisions, each with:
116
+ - The decision and rationale
117
+ - Evidence source (codebase patterns, library docs, web research)
118
+ - Alternatives considered
119
+
120
+ Then ask: **"Does this capture the architecture? Adjust anything before we move on."**
121
+
122
+ Same gate rules: reflect adjustments, wait for confirmation.
123
+
124
+ ---
125
+
126
+ ### Layer 3 — Error States
127
+
128
+ Resolve what happens when things fail. Present this layer with an option:
129
+
130
+ "We can go deep on error handling and failure modes, or I can apply sensible defaults based on the architecture decisions above. Which do you prefer?"
131
+
132
+ If the user chooses defaults, summarize what the defaults are and gate. If the user chooses to go deep, ask about:
133
+ - Failure modes for each major component
134
+ - Error propagation between layers (API → frontend, service → database)
135
+ - Timeout, retry, and circuit-breaker strategies
136
+ - What the user sees when something fails
137
+
138
+ Adapt depth to work type:
139
+ - **API work:** Rate limiting, timeout cascades, partial failure, status codes
140
+ - **UI work:** Loading states, optimistic updates, offline behavior, error boundaries
141
+ - **Data pipelines:** Data corruption, checkpoint recovery, idempotency
142
+
143
+ #### Layer 3 Gate
144
+
145
+ Summarize error handling strategy. Then ask: **"Does this capture how errors should be handled? Adjust anything before we move on."**
146
+
147
+ ---
148
+
149
+ ### Layer 4 — Quality Bar
150
+
151
+ Resolve what "done" means concretely. Ask about:
152
+ - Per-slice acceptance criteria — specific enough for automated verification
153
+ - Test strategy — what types of tests, what coverage expectations
154
+ - Definition of done — what must be true before the milestone ships
155
+ - Non-functional requirements — performance, accessibility, security if relevant
156
+
157
+ Adapt depth to work type:
158
+ - **CLI work:** Shell compatibility, error message clarity, exit code semantics
159
+ - **Refactoring:** Behavioral equivalence tests, not just code coverage
160
+ - **UI work:** Visual regression criteria, responsive breakpoints
161
+
162
+ #### Layer 4 Gate
163
+
164
+ Summarize quality bar: acceptance criteria, test strategy, definition of done. Then ask: **"Does this capture the quality bar? Adjust anything before we move on to requirements and roadmap?"**
165
+
166
+ ---
167
+
168
+ ### Layer cadence
169
+
170
+ - Do not count the reflection step as a question round. Rounds start at Layer 1 after reflection is confirmed.
171
+ - When all four layer gates have been confirmed (or skipped by the user), move to the Depth Verification step below. Do not ask a separate "ready to wrap up?" gate — the depth verification confirms the full picture.
71
172
 
72
173
  ## Questioning Philosophy
73
174
 
74
175
  You are a thinking partner, not an interviewer.
75
176
 
76
- **Turn-taking contract (non-bypassable).** Never fabricate, simulate, or role-play user responses. Never generate fake transcript markers like `[User]`, `[Human]`, or `User:` to invent input. Ask one question round (1-3 questions) per turn, then stop and wait for the user's actual response before continuing. If you use `ask_user_questions`, call it at most once per turn and treat its returned response as the only valid structured user input for that round.
177
+ **Turn-taking contract (non-bypassable).** Never fabricate, simulate, or role-play user responses. Never generate fake transcript markers like `[User]`, `[Human]`, or `User:` to invent input. Prior conversation context may be provided to you inside `<conversation_history>` with `<user_message>` / `<assistant_message>` XML tags — treat those as read-only context and never emit those tags in your response. Ask one question round (1-3 questions) per turn, then stop and wait for the user's actual response before continuing. If you use `ask_user_questions`, call it at most once per turn and treat its returned response as the only valid structured user input for that round.
77
178
 
78
179
  **Start open, follow energy.** Let the user's enthusiasm guide where you dig deeper. If they light up about a particular aspect, explore it. If they're vague about something, that's where you probe.
79
180
 
@@ -225,6 +326,14 @@ Once the user is satisfied, in a single pass:
225
326
  **Depth-Preservation Guidance for context.md:**
226
327
  When writing context.md, preserve the user's exact terminology, emphasis, and specific framing from the discussion. Do not paraphrase user nuance into generic summaries. If the user said "craft feel," write "craft feel" — not "high-quality user experience." If they emphasized a specific constraint or negative requirement, carry that emphasis through verbatim. The context file is downstream agents' only window into this conversation — flattening specifics into generics loses the signal that shaped every decision.
227
328
 
329
+ **Structured sections from discussion layers:**
330
+ When writing CONTEXT.md, include structured sections that map to the discussion layers:
331
+ - **Scope** — what's in, what's out, what's deferred (from Layer 1 gate summary)
332
+ - **Architectural Decisions** — each with rationale, evidence source, alternatives considered (from Layer 2 gate summary)
333
+ - **Error Handling Strategy** — failure modes, propagation, user-facing error behavior (from Layer 3 gate summary)
334
+ - **Acceptance Criteria** — per-slice criteria specific enough for the planner to use directly (from Layer 4 gate summary)
335
+ These sections are in addition to whatever other context the discussion surfaced.
336
+
228
337
  4. Write `{{contextPath}}` — use the **Context** output template below. Preserve key risks, unknowns, existing codebase constraints, integration points, and relevant requirements surfaced during discussion.
229
338
  5. Call `gsd_plan_milestone` to create the roadmap. Decompose into demoable vertical slices with risk, depends, demo sentences, proof strategy, verification classes, milestone definition of done, requirement coverage, and a boundary map. If the milestone crosses multiple runtime boundaries, include an explicit final integration slice that proves the assembled system works end-to-end in a real environment. Use the **Roadmap** output template below to structure the tool call parameters.
230
339
  6. For each architectural or pattern decision made during discussion, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
@@ -35,7 +35,7 @@ GSD ships with bundled skills. Load the relevant skill file with the `read` tool
35
35
  - Read before edit.
36
36
  - Reproduce before fix when possible.
37
37
  - Work is not done until the relevant verification has passed.
38
- - **Never fabricate, simulate, or role-play user responses.** Never generate markers like `[User]`, `[Human]`, `User:`, or similar to represent user input inside your own output. Ask one question round (1-3 questions), then stop and wait for the user's actual response before continuing. If `ask_user_questions` is available, treat its returned response as the only valid structured user input for that round.
38
+ - **Never fabricate, simulate, or role-play user responses.** Never generate markers like `[User]`, `[Human]`, `User:`, or similar to represent user input inside your own output. Prior conversation context may be provided to you inside `<conversation_history>` with `<user_message>` / `<assistant_message>` XML tags — treat those as read-only context and never emit those tags in your response. Ask one question round (1-3 questions), then stop and wait for the user's actual response before continuing. If `ask_user_questions` is available, treat its returned response as the only valid structured user input for that round.
39
39
  - Never print, echo, log, or restate secrets or credentials. Report only key names and applied/skipped status.
40
40
  - Never ask the user to edit `.env` files or set secrets manually. Use `secure_env_collect`.
41
41
  - In enduring files, write current state only unless the file is explicitly historical.
@@ -13,7 +13,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
13
13
  import { debugCount, debugTime } from './debug-logger.js';
14
14
  import { logWarning, logError } from './workflow-logger.js';
15
15
  import { extractVerdict } from './verdict-parser.js';
16
- import { isDbAvailable, wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateTaskStatus, getPendingGateCountForTurn, } from './gsd-db.js';
16
+ import { isDbAvailable, wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateSliceStatus, updateTaskStatus, getPendingGateCountForTurn, } from './gsd-db.js';
17
17
  /**
18
18
  * A "ghost" milestone directory contains only META.json (and no substantive
19
19
  * files like CONTEXT, CONTEXT-DRAFT, ROADMAP, or SUMMARY). These appear when
@@ -286,6 +286,26 @@ function reconcileDiskToDb(basePath) {
286
286
  depends: s.depends, demo: s.demo,
287
287
  });
288
288
  }
289
+ // Reconcile stale *existing* slice rows (#3599): a slice row may exist in
290
+ // the DB with status "pending" even though disk artifacts (SUMMARY) prove
291
+ // completion — the same class of desync that task-level reconciliation
292
+ // (further below) already handles. Without this, the dependency resolver
293
+ // builds doneSliceIds from stale DB rows and downstream slices stay blocked
294
+ // forever with "No slice eligible".
295
+ for (const dbSlice of dbSlices) {
296
+ if (isStatusDone(dbSlice.status))
297
+ continue;
298
+ const summaryPath = resolveSliceFile(basePath, mid, dbSlice.id, "SUMMARY");
299
+ if (summaryPath) {
300
+ try {
301
+ updateSliceStatus(mid, dbSlice.id, "complete");
302
+ logWarning("reconcile", `slice ${mid}/${dbSlice.id} status reconciled from "${dbSlice.status}" to "complete" (#3599)`, { mid, sid: dbSlice.id });
303
+ }
304
+ catch (e) {
305
+ logError("reconcile", `failed to update slice ${dbSlice.id}`, { sid: dbSlice.id, error: e.message });
306
+ }
307
+ }
308
+ }
289
309
  }
290
310
  return allMilestones;
291
311
  }
@@ -138,6 +138,13 @@ export function renderRoadmapProjection(basePath, milestoneId) {
138
138
  * regeneration, they are queried from the DB by renderSummaryProjection.
139
139
  */
140
140
  export function renderSummaryContent(taskRow, sliceId, milestoneId, evidence) {
141
+ // If the task already has a fully rendered summary (written by handleCompleteTask's
142
+ // renderSummaryMarkdown), use it as-is. That content already includes frontmatter,
143
+ // heading, and all sections. Re-wrapping it inside a second frontmatter/heading
144
+ // envelope produces double frontmatter and duplicate sections.
145
+ if (taskRow.full_summary_md && taskRow.full_summary_md.trimStart().startsWith("---")) {
146
+ return taskRow.full_summary_md;
147
+ }
141
148
  // ── Frontmatter (YAML list format, matches parseSummary() expectations) ──
142
149
  const keyFilesYaml = taskRow.key_files && taskRow.key_files.length > 0
143
150
  ? taskRow.key_files.map(f => ` - ${f}`).join("\n")
@@ -468,14 +468,41 @@ export function removeWorktree(basePath, name, opts = {}) {
468
468
  }
469
469
  }
470
470
  }
471
- /** Paths to skip in all worktree diffs (internal/runtime artifacts). */
472
- const SKIP_PATHS = [".gsd/worktrees/", ".gsd/runtime/", ".gsd/activity/"];
473
- const SKIP_EXACT = [".gsd/STATE.md", ".gsd/auto.lock", ".gsd/metrics.json"];
471
+ /**
472
+ * Paths to skip in all worktree diffs (internal/runtime artifacts).
473
+ *
474
+ * NOTE: These arrays must stay synchronized with GSD_RUNTIME_PATTERNS in gitignore.ts.
475
+ * That file is the canonical source of truth for runtime ignore patterns.
476
+ * This module uses a split representation (paths/exact/prefixes) for efficient matching.
477
+ */
478
+ const SKIP_PATHS = [
479
+ ".gsd/worktrees/",
480
+ ".gsd/runtime/",
481
+ ".gsd/activity/",
482
+ ".gsd/forensics/",
483
+ ".gsd/parallel/",
484
+ ".gsd/journal/",
485
+ ];
486
+ const SKIP_EXACT = [
487
+ ".gsd/STATE.md",
488
+ ".gsd/auto.lock",
489
+ ".gsd/metrics.json",
490
+ ".gsd/state-manifest.json",
491
+ ".gsd/doctor-history.jsonl",
492
+ ".gsd/event-log.jsonl",
493
+ ];
494
+ /** File prefixes to skip (for wildcard patterns like completed-units*.json, gsd.db*). */
495
+ const SKIP_PREFIXES = [
496
+ ".gsd/completed-units",
497
+ ".gsd/gsd.db",
498
+ ];
474
499
  function shouldSkipPath(filePath) {
475
500
  if (SKIP_PATHS.some(p => filePath.startsWith(p)))
476
501
  return true;
477
502
  if (SKIP_EXACT.includes(filePath))
478
503
  return true;
504
+ if (SKIP_PREFIXES.some(p => filePath.startsWith(p)))
505
+ return true;
479
506
  return false;
480
507
  }
481
508
  function parseDiffNameStatus(entries) {
@@ -22,6 +22,9 @@ const BLOCKED_PATTERNS = [
22
22
  /(^|[/\\])\.gsd[/\\]STATE\.md$/i,
23
23
  // Also match resolved symlink paths under ~/.gsd/projects/ (Pitfall #6)
24
24
  /(^|[/\\])\.gsd[/\\]projects[/\\][^/\\]+[/\\]STATE\.md$/i,
25
+ // gsd.db and WAL/SHM files — single-writer WAL connection managed by engine (#3625)
26
+ /(^|[/\\])\.gsd[/\\]gsd\.db(-wal|-shm)?$/i,
27
+ /(^|[/\\])\.gsd[/\\]projects[/\\][^/\\]+[/\\]gsd\.db(-wal|-shm)?$/i,
25
28
  ];
26
29
  /**
27
30
  * Bash command patterns that target STATE.md.
@@ -38,6 +41,12 @@ const BASH_STATE_PATTERNS = [
38
41
  /\bsed\b.*-i.*STATE\.md/i,
39
42
  // dd output to STATE.md
40
43
  /\bdd\b.*of=\S*STATE\.md/i,
44
+ // Direct DB access via sqlite3/sql.js/better-sqlite3 targeting gsd.db (#3625)
45
+ /\b(sqlite3|sql\.js|better-sqlite3|node:sqlite)\b.*gsd\.db/i,
46
+ /\bgsd\.db\b.*\b(sqlite3|sql\.js|better-sqlite3)\b/i,
47
+ // Shell writes targeting gsd.db files
48
+ /[>|]+\s*\S*gsd\.db/i,
49
+ /\b(cp|mv|dd)\b.*gsd\.db/i,
41
50
  ];
42
51
  /**
43
52
  * Tests whether the given file path matches a blocked authoritative .gsd/ state file.
@@ -75,7 +84,7 @@ function matchesBlockedPattern(path) {
75
84
  * Error message returned when an agent attempts to directly write an authoritative .gsd/ state file.
76
85
  * Directs the agent to use engine tool calls instead.
77
86
  */
78
- export const BLOCKED_WRITE_ERROR = `Direct writes to .gsd/STATE.md are blocked. Use engine tool calls instead:
87
+ export const BLOCKED_WRITE_ERROR = `Direct writes to .gsd/STATE.md and .gsd/gsd.db are blocked. Use engine tool calls instead:
79
88
  - To complete a task: call gsd_complete_task(milestone_id, slice_id, task_id, summary)
80
89
  - To complete a slice: call gsd_complete_slice(milestone_id, slice_id, summary, uat_result)
81
90
  - To save a decision: call gsd_save_decision(scope, decision, choice, rationale)