gsd-pi 2.41.0 → 2.42.0-dev.eedc83f

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 (483) hide show
  1. package/README.md +92 -29
  2. package/dist/cli-web-branch.d.ts +6 -0
  3. package/dist/cli-web-branch.js +17 -0
  4. package/dist/cli.js +15 -1
  5. package/dist/onboarding.js +2 -1
  6. package/dist/resource-loader.js +39 -6
  7. package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
  8. package/dist/resources/extensions/gsd/auto/loop.js +89 -1
  9. package/dist/resources/extensions/gsd/auto/phases.js +28 -10
  10. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  11. package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
  14. package/dist/resources/extensions/gsd/auto-prompts.js +1 -1
  15. package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
  16. package/dist/resources/extensions/gsd/auto-start.js +8 -3
  17. package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
  18. package/dist/resources/extensions/gsd/auto.js +64 -2
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
  20. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
  21. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +25 -3
  23. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
  24. package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
  25. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  26. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  27. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
  28. package/dist/resources/extensions/gsd/context-injector.js +74 -0
  29. package/dist/resources/extensions/gsd/context-store.js +4 -3
  30. package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
  31. package/dist/resources/extensions/gsd/custom-verification.js +145 -0
  32. package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
  33. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
  34. package/dist/resources/extensions/gsd/db-writer.js +5 -2
  35. package/dist/resources/extensions/gsd/definition-loader.js +352 -0
  36. package/dist/resources/extensions/gsd/detection.js +20 -1
  37. package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
  38. package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
  39. package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
  40. package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
  41. package/dist/resources/extensions/gsd/doctor.js +11 -1
  42. package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
  43. package/dist/resources/extensions/gsd/engine-types.js +8 -0
  44. package/dist/resources/extensions/gsd/execution-policy.js +8 -0
  45. package/dist/resources/extensions/gsd/exit-command.js +12 -2
  46. package/dist/resources/extensions/gsd/export.js +9 -13
  47. package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
  48. package/dist/resources/extensions/gsd/files.js +28 -11
  49. package/dist/resources/extensions/gsd/forensics.js +94 -3
  50. package/dist/resources/extensions/gsd/git-constants.js +1 -0
  51. package/dist/resources/extensions/gsd/git-service.js +73 -3
  52. package/dist/resources/extensions/gsd/graph.js +225 -0
  53. package/dist/resources/extensions/gsd/gsd-db.js +25 -8
  54. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  55. package/dist/resources/extensions/gsd/guided-flow.js +7 -3
  56. package/dist/resources/extensions/gsd/journal.js +85 -0
  57. package/dist/resources/extensions/gsd/md-importer.js +5 -0
  58. package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
  59. package/dist/resources/extensions/gsd/native-git-bridge.js +3 -2
  60. package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
  61. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  62. package/dist/resources/extensions/gsd/preferences.js +60 -8
  63. package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
  64. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  65. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  66. package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
  68. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  69. package/dist/resources/extensions/gsd/repo-identity.js +92 -7
  70. package/dist/resources/extensions/gsd/rule-registry.js +489 -0
  71. package/dist/resources/extensions/gsd/rule-types.js +6 -0
  72. package/dist/resources/extensions/gsd/run-manager.js +134 -0
  73. package/dist/resources/extensions/gsd/service-tier.js +147 -0
  74. package/dist/resources/extensions/gsd/session-lock.js +2 -2
  75. package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
  76. package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
  77. package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
  78. package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
  79. package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
  80. package/dist/resources/extensions/gsd/worktree-resolver.js +21 -4
  81. package/dist/resources/extensions/mcp-client/index.js +2 -1
  82. package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
  83. package/dist/resources/extensions/subagent/index.js +7 -3
  84. package/dist/resources/extensions/voice/index.js +4 -4
  85. package/dist/resources/skills/create-workflow/SKILL.md +103 -0
  86. package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  87. package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
  88. package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  89. package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  90. package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  91. package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  92. package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  93. package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  94. package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  95. package/dist/web/standalone/.next/BUILD_ID +1 -1
  96. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  97. package/dist/web/standalone/.next/build-manifest.json +4 -4
  98. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  99. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  100. package/dist/web/standalone/.next/required-server-files.json +3 -3
  101. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  102. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  104. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  112. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  118. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  166. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  172. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  186. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  188. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  190. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/app/index.html +1 -1
  200. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  201. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  202. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  203. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  204. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  205. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  206. package/dist/web/standalone/.next/server/app/page.js +2 -2
  207. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  209. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  210. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  211. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/middleware.js +2 -2
  214. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  215. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  216. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  217. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  218. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  219. package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
  220. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
  222. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
  224. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
  226. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  227. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  228. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  229. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  230. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  231. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  232. package/dist/web/standalone/server.js +1 -1
  233. package/dist/web-mode.d.ts +4 -0
  234. package/dist/web-mode.js +69 -11
  235. package/package.json +1 -1
  236. package/packages/native/src/__tests__/text.test.mjs +33 -0
  237. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  238. package/packages/pi-agent-core/dist/agent.js +2 -0
  239. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  240. package/packages/pi-agent-core/dist/types.d.ts +6 -0
  241. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  242. package/packages/pi-agent-core/dist/types.js.map +1 -1
  243. package/packages/pi-agent-core/src/agent.test.ts +53 -0
  244. package/packages/pi-agent-core/src/agent.ts +3 -0
  245. package/packages/pi-agent-core/src/types.ts +6 -0
  246. package/packages/pi-agent-core/tsconfig.json +1 -1
  247. package/packages/pi-ai/dist/models.d.ts +5 -3
  248. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  249. package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
  250. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  251. package/packages/pi-ai/dist/models.generated.js +1135 -1588
  252. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  253. package/packages/pi-ai/dist/models.js.map +1 -1
  254. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  255. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
  256. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  257. package/packages/pi-ai/scripts/generate-models.ts +1543 -0
  258. package/packages/pi-ai/src/models.generated.ts +1140 -1593
  259. package/packages/pi-ai/src/models.ts +7 -4
  260. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
  261. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
  263. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  264. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
  265. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
  267. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
  269. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
  271. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  272. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
  274. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  276. package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
  277. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  278. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -0
  280. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  281. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
  282. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
  284. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
  286. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
  288. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
  291. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  293. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
  294. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
  295. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  296. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
  297. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  298. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  299. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
  300. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  301. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  302. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +34 -10
  303. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  304. package/packages/pi-coding-agent/package.json +1 -1
  305. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
  306. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
  307. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
  308. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
  309. package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
  310. package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
  311. package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
  312. package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
  313. package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
  314. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
  315. package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
  316. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
  317. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
  318. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +36 -11
  319. package/pkg/package.json +1 -1
  320. package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
  321. package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
  322. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
  323. package/src/resources/extensions/gsd/auto/loop.ts +101 -1
  324. package/src/resources/extensions/gsd/auto/phases.ts +30 -10
  325. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  326. package/src/resources/extensions/gsd/auto/types.ts +4 -0
  327. package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
  328. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
  329. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
  330. package/src/resources/extensions/gsd/auto-prompts.ts +1 -1
  331. package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
  332. package/src/resources/extensions/gsd/auto-start.ts +8 -3
  333. package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
  334. package/src/resources/extensions/gsd/auto.ts +71 -2
  335. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
  336. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
  337. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  338. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +25 -4
  339. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
  340. package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
  341. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  342. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  343. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
  344. package/src/resources/extensions/gsd/context-injector.ts +100 -0
  345. package/src/resources/extensions/gsd/context-store.ts +4 -3
  346. package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
  347. package/src/resources/extensions/gsd/custom-verification.ts +180 -0
  348. package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
  349. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
  350. package/src/resources/extensions/gsd/db-writer.ts +6 -2
  351. package/src/resources/extensions/gsd/definition-loader.ts +462 -0
  352. package/src/resources/extensions/gsd/detection.ts +20 -1
  353. package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
  354. package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
  355. package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
  356. package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
  357. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  358. package/src/resources/extensions/gsd/doctor.ts +12 -1
  359. package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
  360. package/src/resources/extensions/gsd/engine-types.ts +71 -0
  361. package/src/resources/extensions/gsd/execution-policy.ts +43 -0
  362. package/src/resources/extensions/gsd/exit-command.ts +14 -2
  363. package/src/resources/extensions/gsd/export.ts +8 -15
  364. package/src/resources/extensions/gsd/extension-manifest.json +2 -2
  365. package/src/resources/extensions/gsd/files.ts +29 -12
  366. package/src/resources/extensions/gsd/forensics.ts +101 -3
  367. package/src/resources/extensions/gsd/git-constants.ts +1 -0
  368. package/src/resources/extensions/gsd/git-service.ts +76 -6
  369. package/src/resources/extensions/gsd/graph.ts +312 -0
  370. package/src/resources/extensions/gsd/gsd-db.ts +37 -8
  371. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  372. package/src/resources/extensions/gsd/guided-flow.ts +7 -3
  373. package/src/resources/extensions/gsd/journal.ts +134 -0
  374. package/src/resources/extensions/gsd/md-importer.ts +6 -0
  375. package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
  376. package/src/resources/extensions/gsd/native-git-bridge.ts +3 -2
  377. package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
  378. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  379. package/src/resources/extensions/gsd/preferences.ts +63 -6
  380. package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
  381. package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  382. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  383. package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
  384. package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
  385. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  386. package/src/resources/extensions/gsd/repo-identity.ts +95 -7
  387. package/src/resources/extensions/gsd/rule-registry.ts +599 -0
  388. package/src/resources/extensions/gsd/rule-types.ts +68 -0
  389. package/src/resources/extensions/gsd/run-manager.ts +180 -0
  390. package/src/resources/extensions/gsd/service-tier.ts +184 -0
  391. package/src/resources/extensions/gsd/session-lock.ts +2 -2
  392. package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
  393. package/src/resources/extensions/gsd/templates/decisions.md +2 -2
  394. package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
  395. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
  396. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
  397. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
  398. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
  399. package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
  400. package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
  401. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
  402. package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
  403. package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
  404. package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
  405. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
  406. package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
  407. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
  408. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
  409. package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
  410. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
  411. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
  412. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
  413. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
  414. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
  415. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
  416. package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
  417. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
  418. package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
  419. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
  420. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
  421. package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
  422. package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
  423. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
  424. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
  425. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
  426. package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
  427. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
  428. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
  429. package/src/resources/extensions/gsd/tests/journal.test.ts +341 -0
  430. package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
  431. package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
  432. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  433. package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
  434. package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
  435. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
  436. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
  437. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
  438. package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
  439. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
  440. package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
  441. package/src/resources/extensions/gsd/tests/service-tier.test.ts +127 -0
  442. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
  443. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
  444. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
  445. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
  446. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
  447. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
  448. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
  449. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
  450. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
  451. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
  452. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
  453. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +135 -0
  454. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +203 -106
  455. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
  456. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
  457. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
  458. package/src/resources/extensions/gsd/types.ts +3 -0
  459. package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
  460. package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
  461. package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
  462. package/src/resources/extensions/gsd/worktree-resolver.ts +32 -11
  463. package/src/resources/extensions/mcp-client/index.ts +5 -1
  464. package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
  465. package/src/resources/extensions/subagent/index.ts +7 -3
  466. package/src/resources/extensions/voice/index.ts +4 -4
  467. package/src/resources/skills/create-workflow/SKILL.md +103 -0
  468. package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  469. package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
  470. package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  471. package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  472. package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  473. package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  474. package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  475. package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  476. package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  477. package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
  478. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
  479. package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
  480. package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
  481. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
  482. /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → JUBX5FUR73jiViQU5a-Cx}/_buildManifest.js +0 -0
  483. /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → JUBX5FUR73jiViQU5a-Cx}/_ssgManifest.js +0 -0
@@ -0,0 +1,599 @@
1
+ // GSD Extension — Unified Rule Registry
2
+ //
3
+ // Holds all dispatch rules and hooks as a flat list of UnifiedRule objects.
4
+ // Provides evaluation methods for each phase (dispatch, post-unit, pre-dispatch)
5
+ // and encapsulates mutable hook state as instance fields.
6
+ //
7
+ // A module-level singleton accessor allows existing code to migrate incrementally.
8
+
9
+ import type { UnifiedRule, RulePhase } from "./rule-types.js";
10
+ import type { DispatchAction, DispatchContext, DispatchRule } from "./auto-dispatch.js";
11
+ import type {
12
+ PostUnitHookConfig,
13
+ PreDispatchHookConfig,
14
+ HookDispatchResult,
15
+ PreDispatchResult,
16
+ HookExecutionState,
17
+ PersistedHookState,
18
+ HookStatusEntry,
19
+ } from "./types.js";
20
+ import { resolvePostUnitHooks, resolvePreDispatchHooks } from "./preferences.js";
21
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
22
+ import { join } from "node:path";
23
+
24
+ // ─── Artifact Path Resolution ──────────────────────────────────────────────
25
+
26
+ export function resolveHookArtifactPath(basePath: string, unitId: string, artifactName: string): string {
27
+ const parts = unitId.split("/");
28
+ if (parts.length === 3) {
29
+ const [mid, sid, tid] = parts;
30
+ return join(basePath, ".gsd", "milestones", mid, "slices", sid, "tasks", `${tid}-${artifactName}`);
31
+ }
32
+ if (parts.length === 2) {
33
+ const [mid, sid] = parts;
34
+ return join(basePath, ".gsd", "milestones", mid, "slices", sid, artifactName);
35
+ }
36
+ return join(basePath, ".gsd", "milestones", parts[0], artifactName);
37
+ }
38
+
39
+ // ─── Dispatch Rule Conversion ──────────────────────────────────────────────
40
+
41
+ /**
42
+ * Convert an array of DispatchRule objects to UnifiedRule[] format.
43
+ * Preserves exact array order — dispatch is order-dependent (first-match-wins).
44
+ */
45
+ export function convertDispatchRules(rules: DispatchRule[]): UnifiedRule[] {
46
+ return rules.map((rule) => ({
47
+ name: rule.name,
48
+ when: "dispatch" as const,
49
+ evaluation: "first-match" as const,
50
+ where: rule.match,
51
+ then: (result: any) => result,
52
+ description: `Dispatch rule: ${rule.name}`,
53
+ }));
54
+ }
55
+
56
+ // ─── RuleRegistry ─────────────────────────────────────────────────────────
57
+
58
+ const HOOK_STATE_FILE = "hook-state.json";
59
+
60
+ export class RuleRegistry {
61
+ /** Static dispatch rules provided at construction time. */
62
+ private readonly dispatchRules: UnifiedRule[];
63
+
64
+ // ── Mutable hook state (encapsulated, not module-level) ──────────────
65
+
66
+ activeHook: HookExecutionState | null = null;
67
+ hookQueue: Array<{
68
+ config: PostUnitHookConfig;
69
+ triggerUnitType: string;
70
+ triggerUnitId: string;
71
+ }> = [];
72
+ cycleCounts: Map<string, number> = new Map();
73
+ retryPending: boolean = false;
74
+ retryTrigger: { unitType: string; unitId: string; retryArtifact: string } | null = null;
75
+
76
+ constructor(dispatchRules: UnifiedRule[]) {
77
+ this.dispatchRules = dispatchRules;
78
+ }
79
+
80
+ // ── Core query ───────────────────────────────────────────────────────
81
+
82
+ /**
83
+ * Returns all rules: static dispatch rules + dynamically loaded hook rules.
84
+ * Hook rules are loaded fresh from preferences on each call (not cached).
85
+ */
86
+ listRules(): UnifiedRule[] {
87
+ const rules: UnifiedRule[] = [...this.dispatchRules];
88
+
89
+ // Convert post-unit hooks to unified rules
90
+ const postHooks = resolvePostUnitHooks();
91
+ for (const hook of postHooks) {
92
+ rules.push({
93
+ name: hook.name,
94
+ when: "post-unit",
95
+ evaluation: "all-matching",
96
+ where: (unitType: string) => hook.after.includes(unitType),
97
+ then: () => hook,
98
+ description: `Post-unit hook: fires after ${hook.after.join(", ")}`,
99
+ lifecycle: {
100
+ artifact: hook.artifact,
101
+ retry_on: hook.retry_on,
102
+ max_cycles: hook.max_cycles,
103
+ },
104
+ });
105
+ }
106
+
107
+ // Convert pre-dispatch hooks to unified rules
108
+ const preHooks = resolvePreDispatchHooks();
109
+ for (const hook of preHooks) {
110
+ rules.push({
111
+ name: hook.name,
112
+ when: "pre-dispatch",
113
+ evaluation: "all-matching",
114
+ where: (unitType: string) => hook.before.includes(unitType),
115
+ then: () => hook,
116
+ description: `Pre-dispatch hook: fires before ${hook.before.join(", ")}`,
117
+ });
118
+ }
119
+
120
+ return rules;
121
+ }
122
+
123
+ // ── Dispatch evaluation (async, first-match-wins) ───────────────────
124
+
125
+ /**
126
+ * Iterate dispatch rules in order. First match wins.
127
+ * Returns stop action if no rule matches (unhandled phase).
128
+ */
129
+ async evaluateDispatch(ctx: DispatchContext): Promise<DispatchAction> {
130
+ for (const rule of this.dispatchRules) {
131
+ const result = await rule.where(ctx);
132
+ if (result) {
133
+ if (result.action !== "skip") result.matchedRule = rule.name;
134
+ return result;
135
+ }
136
+ }
137
+ return {
138
+ action: "stop",
139
+ reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
140
+ level: "info",
141
+ matchedRule: "<no-match>",
142
+ };
143
+ }
144
+
145
+ // ── Post-unit hook evaluation (sync, all-matching with lifecycle) ────
146
+
147
+ /**
148
+ * Replicate exact semantics of checkPostUnitHooks from post-unit-hooks.ts:
149
+ * hook-on-hook prevention, idempotency, cycle limits, retry_on, dequeue.
150
+ */
151
+ evaluatePostUnit(
152
+ completedUnitType: string,
153
+ completedUnitId: string,
154
+ basePath: string,
155
+ ): HookDispatchResult | null {
156
+ // If we just completed a hook unit, handle its result
157
+ if (this.activeHook) {
158
+ return this._handleHookCompletion(basePath);
159
+ }
160
+
161
+ // Don't trigger hooks for other hook units (prevent hook-on-hook chains)
162
+ // Don't trigger hooks for triage units or quick-task units
163
+ if (
164
+ completedUnitType.startsWith("hook/") ||
165
+ completedUnitType === "triage-captures" ||
166
+ completedUnitType === "quick-task"
167
+ ) {
168
+ return null;
169
+ }
170
+
171
+ // Check if any hooks are configured for this unit type
172
+ const hooks = resolvePostUnitHooks().filter(h =>
173
+ h.after.includes(completedUnitType),
174
+ );
175
+ if (hooks.length === 0) return null;
176
+
177
+ // Build hook queue for this trigger
178
+ this.hookQueue = hooks.map(config => ({
179
+ config,
180
+ triggerUnitType: completedUnitType,
181
+ triggerUnitId: completedUnitId,
182
+ }));
183
+
184
+ return this._dequeueNextHook(basePath);
185
+ }
186
+
187
+ private _dequeueNextHook(basePath: string): HookDispatchResult | null {
188
+ while (this.hookQueue.length > 0) {
189
+ const entry = this.hookQueue.shift()!;
190
+ const { config, triggerUnitType, triggerUnitId } = entry;
191
+
192
+ // Check idempotency — if artifact already exists, skip
193
+ if (config.artifact) {
194
+ const artifactPath = resolveHookArtifactPath(basePath, triggerUnitId, config.artifact);
195
+ if (existsSync(artifactPath)) continue;
196
+ }
197
+
198
+ // Check cycle limit
199
+ const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
200
+ const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
201
+ const maxCycles = config.max_cycles ?? 1;
202
+ if (currentCycle > maxCycles) continue;
203
+
204
+ this.cycleCounts.set(cycleKey, currentCycle);
205
+
206
+ this.activeHook = {
207
+ hookName: config.name,
208
+ triggerUnitType,
209
+ triggerUnitId,
210
+ cycle: currentCycle,
211
+ pendingRetry: false,
212
+ };
213
+
214
+ // Build prompt with variable substitution
215
+ const [mid, sid, tid] = triggerUnitId.split("/");
216
+ let prompt = config.prompt
217
+ .replace(/\{milestoneId\}/g, mid ?? "")
218
+ .replace(/\{sliceId\}/g, sid ?? "")
219
+ .replace(/\{taskId\}/g, tid ?? "");
220
+
221
+ // Inject browser safety instruction
222
+ prompt += "\n\n**Browser tool safety:** Do NOT use `browser_wait_for` with `condition: \"network_idle\"` — it hangs indefinitely when dev servers keep persistent connections (Vite HMR, WebSocket). Use `selector_visible`, `text_visible`, or `delay` instead.";
223
+
224
+ return {
225
+ hookName: config.name,
226
+ prompt,
227
+ model: config.model,
228
+ unitType: `hook/${config.name}`,
229
+ unitId: triggerUnitId,
230
+ };
231
+ }
232
+
233
+ // No more hooks — clear active state
234
+ this.activeHook = null;
235
+ return null;
236
+ }
237
+
238
+ private _handleHookCompletion(basePath: string): HookDispatchResult | null {
239
+ const hook = this.activeHook!;
240
+ const hooks = resolvePostUnitHooks();
241
+ const config = hooks.find(h => h.name === hook.hookName);
242
+
243
+ // Check if retry was requested via retry_on artifact
244
+ if (config?.retry_on) {
245
+ const retryArtifactPath = resolveHookArtifactPath(basePath, hook.triggerUnitId, config.retry_on);
246
+ if (existsSync(retryArtifactPath)) {
247
+ const cycleKey = `${config.name}/${hook.triggerUnitType}/${hook.triggerUnitId}`;
248
+ const currentCycle = this.cycleCounts.get(cycleKey) ?? 1;
249
+ const maxCycles = config.max_cycles ?? 1;
250
+
251
+ if (currentCycle < maxCycles) {
252
+ this.activeHook = null;
253
+ this.hookQueue = [];
254
+ this.retryPending = true;
255
+ this.retryTrigger = {
256
+ unitType: hook.triggerUnitType,
257
+ unitId: hook.triggerUnitId,
258
+ retryArtifact: config.retry_on,
259
+ };
260
+ return null;
261
+ }
262
+ }
263
+ }
264
+
265
+ // Hook completed normally — try next hook in queue
266
+ this.activeHook = null;
267
+ return this._dequeueNextHook(basePath);
268
+ }
269
+
270
+ // ── Pre-dispatch hook evaluation (sync, all-matching with compose) ──
271
+
272
+ /**
273
+ * Replicate exact semantics of runPreDispatchHooks from post-unit-hooks.ts:
274
+ * modify/skip/replace compose semantics.
275
+ */
276
+ evaluatePreDispatch(
277
+ unitType: string,
278
+ unitId: string,
279
+ prompt: string,
280
+ basePath: string,
281
+ ): PreDispatchResult {
282
+ // Don't intercept hook units
283
+ if (unitType.startsWith("hook/")) {
284
+ return { action: "proceed", prompt, firedHooks: [] };
285
+ }
286
+
287
+ const hooks = resolvePreDispatchHooks().filter(h =>
288
+ h.before.includes(unitType),
289
+ );
290
+ if (hooks.length === 0) {
291
+ return { action: "proceed", prompt, firedHooks: [] };
292
+ }
293
+
294
+ const [mid, sid, tid] = unitId.split("/");
295
+ const substitute = (text: string): string =>
296
+ text
297
+ .replace(/\{milestoneId\}/g, mid ?? "")
298
+ .replace(/\{sliceId\}/g, sid ?? "")
299
+ .replace(/\{taskId\}/g, tid ?? "");
300
+
301
+ const firedHooks: string[] = [];
302
+ let currentPrompt = prompt;
303
+
304
+ for (const hook of hooks) {
305
+ if (hook.action === "skip") {
306
+ if (hook.skip_if) {
307
+ const conditionPath = resolveHookArtifactPath(basePath, unitId, hook.skip_if);
308
+ if (!existsSync(conditionPath)) continue;
309
+ }
310
+ firedHooks.push(hook.name);
311
+ return { action: "skip", firedHooks };
312
+ }
313
+
314
+ if (hook.action === "replace") {
315
+ firedHooks.push(hook.name);
316
+ return {
317
+ action: "replace",
318
+ prompt: substitute(hook.prompt ?? ""),
319
+ unitType: hook.unit_type,
320
+ model: hook.model,
321
+ firedHooks,
322
+ };
323
+ }
324
+
325
+ if (hook.action === "modify") {
326
+ firedHooks.push(hook.name);
327
+ if (hook.prepend) {
328
+ currentPrompt = `${substitute(hook.prepend)}\n\n${currentPrompt}`;
329
+ }
330
+ if (hook.append) {
331
+ currentPrompt = `${currentPrompt}\n\n${substitute(hook.append)}`;
332
+ }
333
+ }
334
+ }
335
+
336
+ return {
337
+ action: "proceed",
338
+ prompt: currentPrompt,
339
+ model: hooks.find(h => h.action === "modify" && h.model)?.model,
340
+ firedHooks,
341
+ };
342
+ }
343
+
344
+ // ── State accessors ─────────────────────────────────────────────────
345
+
346
+ getActiveHook(): HookExecutionState | null {
347
+ return this.activeHook;
348
+ }
349
+
350
+ isRetryPending(): boolean {
351
+ return this.retryPending;
352
+ }
353
+
354
+ /**
355
+ * Returns the trigger unit info for a pending retry, or null.
356
+ * Clears the retry state after reading.
357
+ */
358
+ consumeRetryTrigger(): { unitType: string; unitId: string; retryArtifact: string } | null {
359
+ if (!this.retryPending || !this.retryTrigger) return null;
360
+ const trigger = { ...this.retryTrigger };
361
+ this.retryPending = false;
362
+ this.retryTrigger = null;
363
+ return trigger;
364
+ }
365
+
366
+ /** Clear all mutable state (activeHook, hookQueue, cycleCounts, retryPending, retryTrigger). */
367
+ resetState(): void {
368
+ this.activeHook = null;
369
+ this.hookQueue = [];
370
+ this.cycleCounts.clear();
371
+ this.retryPending = false;
372
+ this.retryTrigger = null;
373
+ }
374
+
375
+ // ── Persistence ─────────────────────────────────────────────────────
376
+
377
+ private _hookStatePath(basePath: string): string {
378
+ return join(basePath, ".gsd", HOOK_STATE_FILE);
379
+ }
380
+
381
+ /** Persist current hook cycle counts to disk. */
382
+ persistState(basePath: string): void {
383
+ const state: PersistedHookState = {
384
+ cycleCounts: Object.fromEntries(this.cycleCounts),
385
+ savedAt: new Date().toISOString(),
386
+ };
387
+ try {
388
+ const dir = join(basePath, ".gsd");
389
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
390
+ writeFileSync(this._hookStatePath(basePath), JSON.stringify(state, null, 2), "utf-8");
391
+ } catch {
392
+ // Non-fatal — state is recreatable from artifacts
393
+ }
394
+ }
395
+
396
+ /** Restore hook cycle counts from disk after a crash/restart. */
397
+ restoreState(basePath: string): void {
398
+ try {
399
+ const filePath = this._hookStatePath(basePath);
400
+ if (!existsSync(filePath)) return;
401
+ const raw = readFileSync(filePath, "utf-8");
402
+ const state: PersistedHookState = JSON.parse(raw);
403
+ if (state.cycleCounts && typeof state.cycleCounts === "object") {
404
+ this.cycleCounts.clear();
405
+ for (const [key, value] of Object.entries(state.cycleCounts)) {
406
+ if (typeof value === "number") {
407
+ this.cycleCounts.set(key, value);
408
+ }
409
+ }
410
+ }
411
+ } catch {
412
+ // Non-fatal — fresh state is fine
413
+ }
414
+ }
415
+
416
+ /** Clear persisted hook state file from disk. */
417
+ clearPersistedState(basePath: string): void {
418
+ try {
419
+ const filePath = this._hookStatePath(basePath);
420
+ if (existsSync(filePath)) {
421
+ writeFileSync(
422
+ filePath,
423
+ JSON.stringify({ cycleCounts: {}, savedAt: new Date().toISOString() }, null, 2),
424
+ "utf-8",
425
+ );
426
+ }
427
+ } catch {
428
+ // Non-fatal
429
+ }
430
+ }
431
+
432
+ // ── Hook status reporting ───────────────────────────────────────────
433
+
434
+ /** Get status of all configured hooks for display. */
435
+ getHookStatus(): HookStatusEntry[] {
436
+ const entries: HookStatusEntry[] = [];
437
+
438
+ const postHooks = resolvePostUnitHooks();
439
+ for (const hook of postHooks) {
440
+ const activeCycles: Record<string, number> = {};
441
+ for (const [key, count] of this.cycleCounts) {
442
+ if (key.startsWith(`${hook.name}/`)) {
443
+ activeCycles[key] = count;
444
+ }
445
+ }
446
+ entries.push({
447
+ name: hook.name,
448
+ type: "post",
449
+ enabled: hook.enabled !== false,
450
+ targets: hook.after,
451
+ activeCycles,
452
+ });
453
+ }
454
+
455
+ const preHooks = resolvePreDispatchHooks();
456
+ for (const hook of preHooks) {
457
+ entries.push({
458
+ name: hook.name,
459
+ type: "pre",
460
+ enabled: hook.enabled !== false,
461
+ targets: hook.before,
462
+ activeCycles: {},
463
+ });
464
+ }
465
+
466
+ return entries;
467
+ }
468
+
469
+ /**
470
+ * Manually trigger a specific hook for a unit.
471
+ * Bypasses normal flow — forces hook to run even if artifact exists.
472
+ */
473
+ triggerHookManually(
474
+ hookName: string,
475
+ unitType: string,
476
+ unitId: string,
477
+ basePath: string,
478
+ ): HookDispatchResult | null {
479
+ const hook = resolvePostUnitHooks().find(h => h.name === hookName);
480
+ if (!hook) {
481
+ console.error(`[triggerHookManually] Hook "${hookName}" not found in post_unit_hooks`);
482
+ return null;
483
+ }
484
+
485
+ if (!hook.prompt || typeof hook.prompt !== "string" || hook.prompt.trim().length === 0) {
486
+ console.error(`[triggerHookManually] Hook "${hookName}" has empty prompt`);
487
+ return null;
488
+ }
489
+
490
+ this.activeHook = {
491
+ hookName: hook.name,
492
+ triggerUnitType: unitType,
493
+ triggerUnitId: unitId,
494
+ cycle: 1,
495
+ pendingRetry: false,
496
+ };
497
+
498
+ this.hookQueue = [{
499
+ config: hook,
500
+ triggerUnitType: unitType,
501
+ triggerUnitId: unitId,
502
+ }];
503
+
504
+ const cycleKey = `${hook.name}/${unitType}/${unitId}`;
505
+ const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
506
+ this.cycleCounts.set(cycleKey, currentCycle);
507
+ this.activeHook.cycle = currentCycle;
508
+
509
+ const [mid, sid, tid] = unitId.split("/");
510
+ const prompt = hook.prompt
511
+ .replace(/\{milestoneId\}/g, mid ?? "")
512
+ .replace(/\{sliceId\}/g, sid ?? "")
513
+ .replace(/\{taskId\}/g, tid ?? "");
514
+
515
+ return {
516
+ hookName: hook.name,
517
+ prompt,
518
+ model: hook.model,
519
+ unitType: `hook/${hook.name}`,
520
+ unitId,
521
+ };
522
+ }
523
+
524
+ /** Format hook status for terminal display. */
525
+ formatHookStatus(): string {
526
+ const entries = this.getHookStatus();
527
+ if (entries.length === 0) {
528
+ return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md";
529
+ }
530
+
531
+ const lines: string[] = ["Configured Hooks:", ""];
532
+
533
+ const postHooks = entries.filter(e => e.type === "post");
534
+ const preHooks = entries.filter(e => e.type === "pre");
535
+
536
+ if (postHooks.length > 0) {
537
+ lines.push("Post-Unit Hooks (run after unit completes):");
538
+ for (const hook of postHooks) {
539
+ const status = hook.enabled ? "enabled" : "disabled";
540
+ const cycles = Object.keys(hook.activeCycles).length;
541
+ const cycleInfo = cycles > 0 ? ` (${cycles} active cycle${cycles === 1 ? "" : "s"})` : "";
542
+ lines.push(` ${hook.name} [${status}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
543
+ }
544
+ lines.push("");
545
+ }
546
+
547
+ if (preHooks.length > 0) {
548
+ lines.push("Pre-Dispatch Hooks (run before unit dispatches):");
549
+ for (const hook of preHooks) {
550
+ const status = hook.enabled ? "enabled" : "disabled";
551
+ lines.push(` ${hook.name} [${status}] → before: ${hook.targets.join(", ")}`);
552
+ }
553
+ lines.push("");
554
+ }
555
+
556
+ return lines.join("\n");
557
+ }
558
+ }
559
+
560
+ // ─── Module-level Singleton ─────────────────────────────────────────────────
561
+
562
+ let _registry: RuleRegistry | null = null;
563
+
564
+ /** Get the singleton registry. Throws if not initialized. */
565
+ export function getRegistry(): RuleRegistry {
566
+ if (!_registry) {
567
+ throw new Error("RuleRegistry not initialized — call initRegistry() or setRegistry() first.");
568
+ }
569
+ return _registry;
570
+ }
571
+
572
+ /** Set the singleton registry instance. */
573
+ export function setRegistry(r: RuleRegistry): void {
574
+ _registry = r;
575
+ }
576
+
577
+ /** Create and set the singleton registry with the given dispatch rules. */
578
+ export function initRegistry(dispatchRules: UnifiedRule[]): RuleRegistry {
579
+ const registry = new RuleRegistry(dispatchRules);
580
+ setRegistry(registry);
581
+ return registry;
582
+ }
583
+
584
+ /**
585
+ * Get the singleton registry, lazily creating one with empty dispatch rules
586
+ * if not yet initialized. This ensures facade functions work even when
587
+ * the full registry hasn't been set up (e.g. during testing).
588
+ */
589
+ export function getOrCreateRegistry(): RuleRegistry {
590
+ if (!_registry) {
591
+ _registry = new RuleRegistry([]);
592
+ }
593
+ return _registry;
594
+ }
595
+
596
+ /** Reset the singleton (for testing). */
597
+ export function resetRegistry(): void {
598
+ _registry = null;
599
+ }
@@ -0,0 +1,68 @@
1
+ // GSD Extension — Unified Rule Type Definitions
2
+ //
3
+ // Every dispatch rule and hook is expressed as a `UnifiedRule` with a
4
+ // consistent when/where/then shape. This file defines the type system;
5
+ // the `RuleRegistry` class in rule-registry.ts holds instances at runtime.
6
+
7
+ import type { DispatchAction, DispatchContext } from "./auto-dispatch.js";
8
+ import type {
9
+ PostUnitHookConfig,
10
+ PreDispatchHookConfig,
11
+ HookDispatchResult,
12
+ PreDispatchResult,
13
+ HookExecutionState,
14
+ HookStatusEntry,
15
+ } from "./types.js";
16
+
17
+ // ─── Phase & Evaluation Strategy ────────────────────────────────────────────
18
+
19
+ /** Which phase/event a rule responds to. */
20
+ export type RulePhase = "dispatch" | "post-unit" | "pre-dispatch";
21
+
22
+ /** How a rule is evaluated relative to peers in the same phase. */
23
+ export type RuleEvaluation = "first-match" | "all-matching";
24
+
25
+ // ─── Lifecycle Metadata (hooks only) ────────────────────────────────────────
26
+
27
+ /** Optional lifecycle metadata attached to hook-derived rules. */
28
+ export interface RuleLifecycle {
29
+ /** Expected output file name (relative to unit dir). Used for idempotency. */
30
+ artifact?: string;
31
+ /** If this file is produced instead of artifact, re-run the trigger unit. */
32
+ retry_on?: string;
33
+ /** Max times this hook can fire for the same trigger unit. */
34
+ max_cycles?: number;
35
+ /** Idempotency key pattern for this hook. */
36
+ idempotency_key?: string;
37
+ }
38
+
39
+ // ─── Unified Rule ───────────────────────────────────────────────────────────
40
+
41
+ /**
42
+ * A single entry in the rule registry. Dispatch rules, post-unit hooks,
43
+ * and pre-dispatch hooks all share this shape.
44
+ */
45
+ export interface UnifiedRule {
46
+ /** Stable human-readable identifier (existing names preserved per D005). */
47
+ name: string;
48
+ /** Which phase/event this rule responds to. */
49
+ when: RulePhase;
50
+ /** How this rule is evaluated relative to peers. */
51
+ evaluation: RuleEvaluation;
52
+ /**
53
+ * Predicate/match function.
54
+ * - Dispatch rules: async, receives DispatchContext, returns DispatchAction | null.
55
+ * - Post-unit hooks: sync, receives (unitType, unitId, basePath).
56
+ * - Pre-dispatch hooks: sync, receives (unitType, unitId, prompt, basePath).
57
+ */
58
+ where: (...args: any[]) => Promise<any> | any;
59
+ /**
60
+ * Action builder. May be merged with `where` for dispatch rules where
61
+ * the match function returns the action directly.
62
+ */
63
+ then: (...args: any[]) => any;
64
+ /** Optional human-readable summary for LLM inspection. */
65
+ description?: string;
66
+ /** Optional hook lifecycle metadata. */
67
+ lifecycle?: RuleLifecycle;
68
+ }