gsd-pi 2.59.0 → 2.60.0-dev.2580e65

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 (354) hide show
  1. package/dist/resources/extensions/ask-user-questions.js +7 -4
  2. package/dist/resources/extensions/gsd/auto/phases.js +62 -1
  3. package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +57 -3
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +43 -3
  7. package/dist/resources/extensions/gsd/auto-prompts.js +49 -20
  8. package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
  9. package/dist/resources/extensions/gsd/auto-start.js +9 -5
  10. package/dist/resources/extensions/gsd/auto-timers.js +11 -5
  11. package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
  12. package/dist/resources/extensions/gsd/auto-verification.js +3 -2
  13. package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
  14. package/dist/resources/extensions/gsd/auto.js +39 -17
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +72 -2
  17. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
  18. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +58 -5
  20. package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
  21. package/dist/resources/extensions/gsd/captures.js +54 -1
  22. package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
  23. package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
  24. package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
  25. package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
  26. package/dist/resources/extensions/gsd/complexity-classifier.js +9 -5
  27. package/dist/resources/extensions/gsd/context-masker.js +68 -0
  28. package/dist/resources/extensions/gsd/custom-verification.js +3 -2
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +7 -0
  30. package/dist/resources/extensions/gsd/gsd-db.js +35 -15
  31. package/dist/resources/extensions/gsd/guided-flow.js +19 -9
  32. package/dist/resources/extensions/gsd/init-wizard.js +12 -0
  33. package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
  34. package/dist/resources/extensions/gsd/md-importer.js +5 -4
  35. package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
  36. package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
  37. package/dist/resources/extensions/gsd/model-router.js +199 -45
  38. package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
  39. package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
  40. package/dist/resources/extensions/gsd/phase-anchor.js +56 -0
  41. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  42. package/dist/resources/extensions/gsd/preferences-validation.js +91 -0
  43. package/dist/resources/extensions/gsd/preferences.js +15 -3
  44. package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
  45. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/rethink.md +7 -0
  47. package/dist/resources/extensions/gsd/prompts/triage-captures.md +6 -1
  48. package/dist/resources/extensions/gsd/rethink.js +5 -2
  49. package/dist/resources/extensions/gsd/rule-registry.js +7 -6
  50. package/dist/resources/extensions/gsd/safe-fs.js +6 -8
  51. package/dist/resources/extensions/gsd/state.js +1 -1
  52. package/dist/resources/extensions/gsd/status-guards.js +4 -3
  53. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  54. package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
  55. package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
  56. package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
  57. package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
  58. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
  59. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
  60. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
  61. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
  62. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
  63. package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
  64. package/dist/resources/extensions/gsd/triage-resolution.js +135 -1
  65. package/dist/resources/extensions/gsd/triage-ui.js +12 -3
  66. package/dist/resources/extensions/gsd/workflow-events.js +2 -1
  67. package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
  68. package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
  69. package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
  70. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
  71. package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
  72. package/dist/resources/extensions/shared/interview-ui.js +3 -1
  73. package/dist/resources/skills/btw/SKILL.md +42 -0
  74. package/dist/web/standalone/.next/BUILD_ID +1 -1
  75. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  76. package/dist/web/standalone/.next/build-manifest.json +3 -3
  77. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  78. package/dist/web/standalone/.next/required-server-files.json +3 -3
  79. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  80. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  90. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  146. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  152. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  170. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  172. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/index.html +1 -1
  182. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  183. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  184. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  185. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  187. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  188. package/dist/web/standalone/.next/server/app/page.js +2 -2
  189. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  191. package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
  192. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  193. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/middleware.js +2 -2
  195. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  197. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  198. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  199. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  200. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  202. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  205. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  206. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  207. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  208. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  209. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  210. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  211. package/dist/web/standalone/server.js +1 -1
  212. package/package.json +1 -1
  213. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  214. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  215. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  217. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
  219. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
  221. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
  225. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
  227. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
  228. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
  229. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
  230. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +6 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.d.ts +2 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.d.ts.map +1 -0
  237. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +122 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
  240. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +30 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +1 -7
  245. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  246. package/packages/pi-coding-agent/package.json +1 -1
  247. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  248. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
  249. package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
  250. package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
  251. package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
  252. package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
  253. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +156 -0
  254. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  255. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +38 -0
  256. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +1 -8
  257. package/pkg/package.json +1 -1
  258. package/src/resources/extensions/ask-user-questions.ts +7 -3
  259. package/src/resources/extensions/gsd/auto/phases.ts +70 -1
  260. package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
  261. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
  262. package/src/resources/extensions/gsd/auto-model-selection.ts +77 -6
  263. package/src/resources/extensions/gsd/auto-post-unit.ts +52 -5
  264. package/src/resources/extensions/gsd/auto-prompts.ts +54 -20
  265. package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
  266. package/src/resources/extensions/gsd/auto-start.ts +10 -9
  267. package/src/resources/extensions/gsd/auto-timers.ts +12 -5
  268. package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
  269. package/src/resources/extensions/gsd/auto-verification.ts +3 -6
  270. package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
  271. package/src/resources/extensions/gsd/auto.ts +40 -17
  272. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
  273. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +80 -2
  274. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
  275. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
  276. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +61 -4
  277. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
  278. package/src/resources/extensions/gsd/captures.ts +71 -2
  279. package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
  280. package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
  281. package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
  282. package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
  283. package/src/resources/extensions/gsd/complexity-classifier.ts +10 -5
  284. package/src/resources/extensions/gsd/context-masker.ts +74 -0
  285. package/src/resources/extensions/gsd/custom-verification.ts +3 -2
  286. package/src/resources/extensions/gsd/docs/preferences-reference.md +7 -0
  287. package/src/resources/extensions/gsd/gsd-db.ts +14 -16
  288. package/src/resources/extensions/gsd/guided-flow.ts +9 -8
  289. package/src/resources/extensions/gsd/init-wizard.ts +12 -0
  290. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
  291. package/src/resources/extensions/gsd/md-importer.ts +5 -4
  292. package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
  293. package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
  294. package/src/resources/extensions/gsd/model-router.ts +245 -56
  295. package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
  296. package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
  297. package/src/resources/extensions/gsd/phase-anchor.ts +71 -0
  298. package/src/resources/extensions/gsd/preferences-types.ts +22 -0
  299. package/src/resources/extensions/gsd/preferences-validation.ts +83 -0
  300. package/src/resources/extensions/gsd/preferences.ts +16 -3
  301. package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  303. package/src/resources/extensions/gsd/prompts/rethink.md +7 -0
  304. package/src/resources/extensions/gsd/prompts/triage-captures.md +6 -1
  305. package/src/resources/extensions/gsd/rethink.ts +5 -2
  306. package/src/resources/extensions/gsd/rule-registry.ts +7 -6
  307. package/src/resources/extensions/gsd/safe-fs.ts +6 -5
  308. package/src/resources/extensions/gsd/state.ts +1 -1
  309. package/src/resources/extensions/gsd/status-guards.ts +4 -3
  310. package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
  311. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
  312. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
  313. package/src/resources/extensions/gsd/tests/context-masker.test.ts +122 -0
  314. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  315. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
  316. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
  317. package/src/resources/extensions/gsd/tests/model-router.test.ts +488 -2
  318. package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +83 -0
  319. package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
  320. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
  321. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
  322. package/src/resources/extensions/gsd/tests/status-guards.test.ts +4 -0
  323. package/src/resources/extensions/gsd/tests/stop-backtrack.test.ts +216 -0
  324. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
  325. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
  326. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
  327. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
  328. package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
  329. package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
  330. package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
  331. package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
  332. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
  333. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
  334. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
  335. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
  336. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
  337. package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
  338. package/src/resources/extensions/gsd/triage-resolution.ts +151 -1
  339. package/src/resources/extensions/gsd/triage-ui.ts +12 -3
  340. package/src/resources/extensions/gsd/types.ts +1 -0
  341. package/src/resources/extensions/gsd/workflow-events.ts +2 -1
  342. package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
  343. package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
  344. package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
  345. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
  346. package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
  347. package/src/resources/extensions/shared/interview-ui.ts +3 -1
  348. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
  349. package/src/resources/skills/btw/SKILL.md +42 -0
  350. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  351. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  352. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  353. /package/dist/web/standalone/.next/static/{DGvT_c5Vb7Wu3X-fEOVUU → ogyMN7M-3bGGuRY08L5HR}/_buildManifest.js +0 -0
  354. /package/dist/web/standalone/.next/static/{DGvT_c5Vb7Wu3X-fEOVUU → ogyMN7M-3bGGuRY08L5HR}/_ssgManifest.js +0 -0
@@ -8,6 +8,7 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
8
8
  showError: (message: string) => void;
9
9
  updateEditorBorderColor: () => void;
10
10
  isExtensionCommand: (text: string) => boolean;
11
+ isKnownSlashCommand: (text: string) => boolean;
11
12
  queueCompactionMessage: (text: string, mode: "steer" | "followUp") => void;
12
13
  updatePendingMessagesDisplay: () => void;
13
14
  flushPendingBashComponents: () => void;
@@ -23,6 +24,12 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
23
24
  host.editor.setText("");
24
25
  return;
25
26
  }
27
+ if (!host.isKnownSlashCommand(text)) {
28
+ const command = text.split(/\s/)[0];
29
+ host.showError(`Unknown command: ${command}. Use slash autocomplete to see available commands.`);
30
+ host.editor.setText("");
31
+ return;
32
+ }
26
33
  }
27
34
 
28
35
  if (text.startsWith("!")) {
@@ -2371,6 +2371,12 @@ export class InteractiveMode {
2371
2371
  const text = (this.editor.getExpandedText?.() ?? this.editor.getText()).trim();
2372
2372
  if (!text) return;
2373
2373
 
2374
+ if (text.startsWith("/") && !this.isKnownSlashCommand(text)) {
2375
+ const command = text.split(/\s/)[0];
2376
+ this.showError(`Unknown command: ${command}. Use slash autocomplete to see available commands.`);
2377
+ return;
2378
+ }
2379
+
2374
2380
  // Queue input during compaction (extension commands execute immediately)
2375
2381
  if (this.session.isCompacting) {
2376
2382
  if (this.isExtensionCommand(text)) {
@@ -2653,6 +2659,12 @@ export class InteractiveMode {
2653
2659
  }
2654
2660
 
2655
2661
  private queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
2662
+ if (text.startsWith("/") && !this.isKnownSlashCommand(text)) {
2663
+ const command = text.split(/\s/)[0];
2664
+ this.showError(`Unknown command: ${command}. Use slash autocomplete to see available commands.`);
2665
+ return;
2666
+ }
2667
+
2656
2668
  this.compactionQueuedMessages.push({ text, mode });
2657
2669
  this.editor.addToHistory?.(text);
2658
2670
  this.editor.setText("");
@@ -2671,6 +2683,32 @@ export class InteractiveMode {
2671
2683
  return !!extensionRunner.getCommand(commandName);
2672
2684
  }
2673
2685
 
2686
+ private isKnownSlashCommand(text: string): boolean {
2687
+ if (!text.startsWith("/")) return false;
2688
+
2689
+ const spaceIndex = text.indexOf(" ");
2690
+ const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
2691
+
2692
+ if (BUILTIN_SLASH_COMMANDS.some((command) => command.name === commandName)) {
2693
+ return true;
2694
+ }
2695
+
2696
+ if (this.isExtensionCommand(text)) {
2697
+ return true;
2698
+ }
2699
+
2700
+ if (this.session.promptTemplates.some((template) => template.name === commandName)) {
2701
+ return true;
2702
+ }
2703
+
2704
+ if (commandName.startsWith("skill:") && this.settingsManager.getEnableSkillCommands()) {
2705
+ const skillName = commandName.slice("skill:".length);
2706
+ return this.session.resourceLoader.getSkills().skills.some((skill) => skill.name === skillName);
2707
+ }
2708
+
2709
+ return false;
2710
+ }
2711
+
2674
2712
  private async flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void> {
2675
2713
  if (this.compactionQueuedMessages.length === 0) {
2676
2714
  return;
@@ -136,7 +136,7 @@ export async function dispatchSlashCommand(
136
136
  await ctx.handleModelCommand(searchTerm);
137
137
  return true;
138
138
  }
139
- if (text.startsWith("/export")) {
139
+ if (text === "/export" || text.startsWith("/export ")) {
140
140
  await handleExportCommand(text, ctx);
141
141
  return true;
142
142
  }
@@ -236,13 +236,6 @@ export async function dispatchSlashCommand(
236
236
  return true;
237
237
  }
238
238
 
239
- // If input starts with "/" but no command matched, show unknown command feedback
240
- if (text.startsWith("/")) {
241
- const command = text.split(/\s/)[0];
242
- ctx.showError(`Unknown command: ${command}. Type /help for available commands.`);
243
- return true;
244
- }
245
-
246
239
  return false;
247
240
  }
248
241
 
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.59.0",
3
+ "version": "2.60.0",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -135,10 +135,14 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
135
135
  }
136
136
  }
137
137
 
138
+ // Try remote first if configured (works in both interactive and headless modes).
139
+ // tryRemoteQuestions returns null when no remote channel is configured, so
140
+ // this is a no-op when the user has not set up Slack/Discord/Telegram.
141
+ const { tryRemoteQuestions } = await import("./remote-questions/manager.js");
142
+ const remoteResult = await tryRemoteQuestions(params.questions, signal);
143
+ if (remoteResult) return { ...remoteResult, details: remoteResult.details as unknown };
144
+
138
145
  if (!ctx.hasUI) {
139
- const { tryRemoteQuestions } = await import("./remote-questions/manager.js");
140
- const remoteResult = await tryRemoteQuestions(params.questions, signal);
141
- if (remoteResult) return { ...remoteResult, details: remoteResult.details as unknown };
142
146
  return errorResult("Error: UI not available (non-interactive mode)", params.questions);
143
147
  }
144
148
 
@@ -709,7 +709,7 @@ export async function runDispatch(
709
709
  // ─── runGuards ────────────────────────────────────────────────────────────────
710
710
 
711
711
  /**
712
- * Phase 2: Guards — budget ceiling, context window, secrets re-check.
712
+ * Phase 2: Guards — stop directives, budget ceiling, context window, secrets re-check.
713
713
  * Returns break to exit the loop, or next to proceed to dispatch.
714
714
  */
715
715
  export async function runGuards(
@@ -718,6 +718,55 @@ export async function runGuards(
718
718
  ): Promise<PhaseResult> {
719
719
  const { ctx, pi, s, deps, prefs } = ic;
720
720
 
721
+ // ── Stop/Backtrack directive guard (#3487) ──
722
+ // Check for unexecuted stop or backtrack captures BEFORE dispatching any unit.
723
+ // This ensures user "halt" directives are honored immediately.
724
+ // IMPORTANT: Fail-closed — any exception during stop handling still breaks the loop
725
+ // to ensure user halt intent is never silently dropped.
726
+ try {
727
+ const { loadStopCaptures, markCaptureExecuted } = await import("../captures.js");
728
+ const stopCaptures = loadStopCaptures(s.basePath);
729
+ if (stopCaptures.length > 0) {
730
+ const first = stopCaptures[0];
731
+ const isBacktrack = first.classification === "backtrack";
732
+ const label = isBacktrack
733
+ ? `Backtrack directive: ${first.text}`
734
+ : `Stop directive: ${first.text}`;
735
+
736
+ ctx.ui.notify(label, "warning");
737
+ deps.sendDesktopNotification(
738
+ "GSD", label, "warning", "stop-directive",
739
+ basename(s.originalBasePath || s.basePath),
740
+ );
741
+
742
+ // Pause first — ensures auto-mode stops even if later steps fail
743
+ await deps.pauseAuto(ctx, pi);
744
+
745
+ // For backtrack captures, write the backtrack trigger after pausing
746
+ if (isBacktrack) {
747
+ try {
748
+ const { executeBacktrack } = await import("../triage-resolution.js");
749
+ executeBacktrack(s.basePath, mid, first);
750
+ } catch (e) {
751
+ debugLog("guards", { phase: "backtrack-execution-error", error: String(e) });
752
+ }
753
+ }
754
+
755
+ // Mark captures as executed only after successful pause/transition
756
+ for (const cap of stopCaptures) {
757
+ markCaptureExecuted(s.basePath, cap.id);
758
+ }
759
+
760
+ debugLog("autoLoop", { phase: "exit", reason: isBacktrack ? "user-backtrack" : "user-stop" });
761
+ return { action: "break", reason: isBacktrack ? "user-backtrack" : "user-stop" };
762
+ }
763
+ } catch (e) {
764
+ // Fail-closed: if anything in the stop guard throws, break the loop
765
+ // rather than silently continuing and dropping user halt intent
766
+ debugLog("guards", { phase: "stop-guard-error", error: String(e) });
767
+ return { action: "break", reason: "stop-guard-error" };
768
+ }
769
+
721
770
  // Budget ceiling guard
722
771
  const budgetCeiling = prefs?.budget_ceiling;
723
772
  if (budgetCeiling !== undefined && budgetCeiling > 0) {
@@ -1205,6 +1254,25 @@ export async function runUnitPhase(
1205
1254
  s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
1206
1255
  }
1207
1256
 
1257
+ // Write phase handoff anchor after successful research/planning completion
1258
+ const anchorPhases = new Set(["research-milestone", "research-slice", "plan-milestone", "plan-slice"]);
1259
+ if (artifactVerified && mid && anchorPhases.has(unitType)) {
1260
+ try {
1261
+ const { writePhaseAnchor } = await import("../phase-anchor.js");
1262
+ writePhaseAnchor(s.basePath, mid, {
1263
+ phase: unitType,
1264
+ milestoneId: mid,
1265
+ generatedAt: new Date().toISOString(),
1266
+ intent: `Completed ${unitType} for ${unitId}`,
1267
+ decisions: [],
1268
+ blockers: [],
1269
+ nextSteps: [],
1270
+ });
1271
+ } catch (err) { /* non-fatal — anchor is advisory */
1272
+ logWarning("engine", `phase anchor failed: ${err instanceof Error ? err.message : String(err)}`);
1273
+ }
1274
+ }
1275
+
1208
1276
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified, ...(unitResult.errorContext ? { errorContext: unitResult.errorContext } : {}) }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
1209
1277
 
1210
1278
  return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
@@ -1325,3 +1393,4 @@ export async function runFinalize(
1325
1393
 
1326
1394
  return { action: "next", data: undefined as void };
1327
1395
  }
1396
+
@@ -31,6 +31,7 @@ import {
31
31
  getRtkSessionSavings,
32
32
  type RtkSessionSavings,
33
33
  } from "../shared/rtk-session-stats.js";
34
+ import { logWarning } from "./workflow-logger.js";
34
35
 
35
36
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
36
37
 
@@ -285,8 +286,9 @@ export function updateSliceProgressCache(base: string, mid: string, activeSid?:
285
286
  taskDetails = dbTasks.map(t => ({ id: t.id, title: t.title, done: t.status === "complete" || t.status === "done" }));
286
287
  }
287
288
  }
288
- } catch {
289
+ } catch (err) {
289
290
  // Non-fatal — just omit task count
291
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
290
292
  }
291
293
  }
292
294
 
@@ -297,8 +299,9 @@ export function updateSliceProgressCache(base: string, mid: string, activeSid?:
297
299
  activeSliceTasks,
298
300
  taskDetails,
299
301
  };
300
- } catch {
302
+ } catch (err) {
301
303
  // Non-fatal — widget just won't show progress bar
304
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
302
305
  }
303
306
  }
304
307
 
@@ -332,8 +335,9 @@ function refreshLastCommit(basePath: string): void {
332
335
  };
333
336
  }
334
337
  lastCommitFetchedAt = Date.now();
335
- } catch {
338
+ } catch (err) {
336
339
  // Non-fatal — just skip last commit display
340
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
337
341
  }
338
342
  }
339
343
 
@@ -376,7 +380,9 @@ function ensureWidgetModeLoaded(): void {
376
380
  if (saved && WIDGET_MODES.includes(saved as WidgetMode)) {
377
381
  widgetMode = saved as WidgetMode;
378
382
  }
379
- } catch { /* non-fatal — use default */ }
383
+ } catch (err) { /* non-fatal — use default */
384
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
385
+ }
380
386
  }
381
387
 
382
388
  /** Persist widget mode to global preferences YAML. */
@@ -395,7 +401,9 @@ function persistWidgetMode(mode: WidgetMode): void {
395
401
  content = content.trimEnd() + "\n" + line + "\n";
396
402
  }
397
403
  writeFileSync(prefsPath, content, "utf-8");
398
- } catch { /* non-fatal — mode still set in memory */ }
404
+ } catch (err) { /* non-fatal — mode still set in memory */
405
+ logWarning("dashboard", `file write failed: ${err instanceof Error ? err.message : String(err)}`);
406
+ }
399
407
  }
400
408
 
401
409
  /** Cycle to the next widget mode. Returns the new mode. */
@@ -458,7 +466,9 @@ export function updateProgressWidget(
458
466
 
459
467
  // Cache git branch at widget creation time (not per render)
460
468
  let cachedBranch: string | null = null;
461
- try { cachedBranch = getCurrentBranch(accessors.getBasePath()); } catch { /* not in git repo */ }
469
+ try { cachedBranch = getCurrentBranch(accessors.getBasePath()); } catch (err) { /* not in git repo */
470
+ logWarning("dashboard", `git branch detection failed: ${err instanceof Error ? err.message : String(err)}`);
471
+ }
462
472
 
463
473
  // Cache short pwd (last 2 path segments only) + worktree/branch info
464
474
  let widgetPwd: string;
@@ -495,7 +505,8 @@ export function updateProgressWidget(
495
505
  const sessionId = ctx.sessionManager.getSessionId();
496
506
  const savings = sessionId ? getRtkSessionSavings(accessors.getBasePath(), sessionId) : null;
497
507
  cachedRtkLabel = formatRtkSavingsLabel(savings);
498
- } catch {
508
+ } catch (err) {
509
+ logWarning("dashboard", `RTK savings lookup failed: ${err instanceof Error ? (err as Error).message : String(err)}`);
499
510
  cachedRtkLabel = null;
500
511
  }
501
512
  };
@@ -519,7 +530,9 @@ export function updateProgressWidget(
519
530
  }
520
531
  refreshRtkLabel();
521
532
  cachedLines = undefined;
522
- } catch { /* non-fatal */ }
533
+ } catch (err) { /* non-fatal */
534
+ logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
535
+ }
523
536
  }, 15_000);
524
537
 
525
538
  return {
@@ -878,3 +891,4 @@ function padToWidth(s: string, colWidth: number): string {
878
891
  if (vis >= colWidth) return truncateToWidth(s, colWidth, "…");
879
892
  return s + " ".repeat(colWidth - vis);
880
893
  }
894
+
@@ -28,7 +28,7 @@ import {
28
28
  buildSliceFileName,
29
29
  } from "./paths.js";
30
30
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
31
- import { logError } from "./workflow-logger.js";
31
+ import { logWarning, logError } from "./workflow-logger.js";
32
32
  import { join } from "node:path";
33
33
  import { hasImplementationArtifacts } from "./auto-recovery.js";
34
34
  import {
@@ -712,7 +712,9 @@ export const DISPATCH_RULES: DispatchRule[] = [
712
712
  }
713
713
  }
714
714
  }
715
- } catch { /* fall through — don't block on DB errors */ }
715
+ } catch (err) { /* fall through — don't block on DB errors */
716
+ logWarning("dispatch", `verification class check failed: ${err instanceof Error ? err.message : String(err)}`);
717
+ }
716
718
 
717
719
  return {
718
720
  action: "dispatch",
@@ -754,8 +756,9 @@ export async function resolveDispatch(
754
756
  try {
755
757
  const registry = getRegistry();
756
758
  return await registry.evaluateDispatch(ctx);
757
- } catch {
759
+ } catch (err) {
758
760
  // Registry not initialized — fall back to inline loop
761
+ logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
759
762
  }
760
763
 
761
764
  for (const rule of DISPATCH_RULES) {
@@ -779,3 +782,4 @@ export async function resolveDispatch(
779
782
  export function getDispatchRuleNames(): string[] {
780
783
  return DISPATCH_RULES.map((r) => r.name);
781
784
  }
785
+
@@ -10,7 +10,7 @@ import type { GSDPreferences } from "./preferences.js";
10
10
  import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
11
11
  import type { ComplexityTier } from "./complexity-classifier.js";
12
12
  import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
13
- import { resolveModelForComplexity, escalateTier } from "./model-router.js";
13
+ import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides } from "./model-router.js";
14
14
  import { getLedger, getProjectTotals } from "./metrics.js";
15
15
  import { unitPhaseLabel } from "./auto-dashboard.js";
16
16
 
@@ -107,7 +107,65 @@ export async function selectAndApplyModel(
107
107
  }
108
108
  }
109
109
 
110
- const routingResult = resolveModelForComplexity(classification, modelConfig, routingConfig, availableModelIds);
110
+ // Load user capability overrides from preferences (D-17: deep-merged with built-in profiles)
111
+ const capabilityOverrides = loadCapabilityOverrides(
112
+ (prefs as { modelOverrides?: Record<string, { capabilities?: Record<string, number> }> } | undefined) ?? {},
113
+ );
114
+
115
+ // Fire before_model_select hook (ADR-004, D-03)
116
+ // Hook can override model selection entirely by returning { modelId }
117
+ let hookOverride: string | undefined;
118
+ if (routingConfig.hooks !== false) {
119
+ const eligible = getEligibleModels(
120
+ classification.tier,
121
+ availableModelIds,
122
+ routingConfig,
123
+ );
124
+ const hookResult = await pi.emitBeforeModelSelect({
125
+ unitType,
126
+ unitId,
127
+ classification: {
128
+ tier: classification.tier,
129
+ reason: classification.reason,
130
+ downgraded: classification.downgraded,
131
+ },
132
+ taskMetadata: classification.taskMetadata as Record<string, unknown> | undefined,
133
+ eligibleModels: eligible,
134
+ phaseConfig: modelConfig ? {
135
+ primary: modelConfig.primary,
136
+ fallbacks: modelConfig.fallbacks ?? [],
137
+ } : undefined,
138
+ });
139
+ if (hookResult?.modelId) {
140
+ hookOverride = hookResult.modelId;
141
+ }
142
+ }
143
+
144
+ let routingResult: ReturnType<typeof resolveModelForComplexity>;
145
+ if (hookOverride) {
146
+ // Hook override bypasses capability scoring entirely
147
+ routingResult = {
148
+ modelId: hookOverride,
149
+ fallbacks: [
150
+ ...(modelConfig?.fallbacks ?? []).filter(f => f !== hookOverride),
151
+ ...(modelConfig?.primary && modelConfig.primary !== hookOverride ? [modelConfig.primary] : []),
152
+ ],
153
+ tier: classification.tier,
154
+ wasDowngraded: hookOverride !== modelConfig?.primary,
155
+ reason: `hook override: ${hookOverride}`,
156
+ selectionMethod: "tier-only",
157
+ };
158
+ } else {
159
+ routingResult = resolveModelForComplexity(
160
+ classification,
161
+ modelConfig,
162
+ routingConfig,
163
+ availableModelIds,
164
+ unitType,
165
+ classification.taskMetadata,
166
+ capabilityOverrides,
167
+ );
168
+ }
111
169
 
112
170
  if (routingResult.wasDowngraded) {
113
171
  effectiveModelConfig = {
@@ -115,10 +173,23 @@ export async function selectAndApplyModel(
115
173
  fallbacks: routingResult.fallbacks,
116
174
  };
117
175
  if (verbose) {
118
- ctx.ui.notify(
119
- `Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`,
120
- "info",
121
- );
176
+ if (routingResult.selectionMethod === "capability-scored" && routingResult.capabilityScores) {
177
+ // Verbose scoring breakdown for capability-scored decisions (D-20)
178
+ const tierLbl = tierLabel(classification.tier);
179
+ const scores = Object.entries(routingResult.capabilityScores)
180
+ .sort(([, a], [, b]) => b - a)
181
+ .map(([id, score]) => `${id}: ${score.toFixed(1)}`)
182
+ .join(", ");
183
+ ctx.ui.notify(
184
+ `Dynamic routing [${tierLbl}]: ${routingResult.modelId} (capability-scored) — ${scores}`,
185
+ "info",
186
+ );
187
+ } else {
188
+ ctx.ui.notify(
189
+ `Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`,
190
+ "info",
191
+ );
192
+ }
122
193
  }
123
194
  }
124
195
  routingTierLabel = ` [${tierLabel(classification.tier)}]`;
@@ -46,7 +46,7 @@ import {
46
46
  persistHookState,
47
47
  resolveHookArtifactPath,
48
48
  } from "./post-unit-hooks.js";
49
- import { hasPendingCaptures, loadPendingCaptures } from "./captures.js";
49
+ import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures } from "./captures.js";
50
50
  import { debugLog } from "./debug-logger.js";
51
51
  import { runSafely } from "./auto-utils.js";
52
52
  import type { AutoSession, SidecarItem } from "./auto/session.js";
@@ -279,8 +279,9 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
279
279
  try {
280
280
  const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
281
281
  ghIssueNumber = getTaskIssueNumberForCommit(s.basePath, mid, sid, tid) ?? undefined;
282
- } catch {
282
+ } catch (err) {
283
283
  // GitHub sync not available — skip
284
+ logWarning("engine", `GitHub issue lookup failed: ${err instanceof Error ? err.message : String(err)}`);
284
285
  }
285
286
 
286
287
  taskContext = {
@@ -558,9 +559,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
558
559
  } catch (dbErr) {
559
560
  // DB unavailable — fail explicitly rather than silently reverting to markdown mutation.
560
561
  // Use 'gsd recover' to rebuild DB state from disk if needed.
561
- process.stderr.write(
562
- `gsd: retry state-reset failed (DB unavailable): ${(dbErr as Error).message}. Run 'gsd recover' to reconcile.\n`,
563
- );
562
+ logError("engine", `retry state-reset failed (DB unavailable): ${(dbErr as Error).message}. Run 'gsd recover' to reconcile.`);
564
563
  }
565
564
  }
566
565
 
@@ -594,6 +593,53 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
594
593
  }
595
594
  }
596
595
 
596
+ // ── Fast-path stop detection (#3487) ──
597
+ // Before waiting for triage, check if any PENDING captures contain explicit
598
+ // stop/halt language. If so, pause immediately — don't wait for triage.
599
+ if (s.currentUnit && s.currentUnit.type !== "triage-captures") {
600
+ try {
601
+ const pending = loadPendingCaptures(s.basePath);
602
+ // Match only when the capture text starts with a stop/halt directive word,
603
+ // or the entire text is short and dominated by such a word. This avoids
604
+ // false positives on captures like "add a pause button" or "stop the timer
605
+ // from re-rendering" — those are feature descriptions, not halt directives.
606
+ const STOP_PATTERN = /^(stop|halt|abort|don'?t continue|pause|cease)\b/i;
607
+ const stopCapture = pending.find(c => STOP_PATTERN.test(c.text.trim()));
608
+ if (stopCapture) {
609
+ ctx.ui.notify(
610
+ `Stop directive detected in pending capture ${stopCapture.id}: "${stopCapture.text}" — pausing auto-mode.`,
611
+ "warning",
612
+ );
613
+ debugLog("postUnit", { phase: "fast-stop", captureId: stopCapture.id });
614
+ await pauseAuto(ctx, pi);
615
+ return "stopped";
616
+ }
617
+ } catch (e) {
618
+ debugLog("postUnit", { phase: "fast-stop-error", error: String(e) });
619
+ }
620
+ }
621
+
622
+ // ── Capture protection: revert executor-silenced captures (#3487) ──
623
+ // Non-triage agents can write **Status:** resolved to CAPTURES.md, bypassing
624
+ // the triage pipeline. Revert those to pending before the triage check.
625
+ if (
626
+ s.currentUnit &&
627
+ s.currentUnit.type !== "triage-captures"
628
+ ) {
629
+ try {
630
+ const reverted = revertExecutorResolvedCaptures(s.basePath);
631
+ if (reverted > 0) {
632
+ debugLog("postUnit", { phase: "capture-protection", reverted });
633
+ ctx.ui.notify(
634
+ `Reverted ${reverted} capture${reverted === 1 ? "" : "s"} silenced by executor — re-queuing for triage.`,
635
+ "warning",
636
+ );
637
+ }
638
+ } catch (e) {
639
+ debugLog("postUnit", { phase: "capture-protection-error", error: String(e) });
640
+ }
641
+ }
642
+
597
643
  // ── Triage check ──
598
644
  if (
599
645
  !s.stepMode &&
@@ -685,3 +731,4 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
685
731
 
686
732
  return "continue";
687
733
  }
734
+