gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.5a170d0

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 (291) hide show
  1. package/README.md +1 -1
  2. package/dist/cli-web-branch.d.ts +6 -0
  3. package/dist/cli-web-branch.js +17 -0
  4. package/dist/onboarding.js +2 -1
  5. package/dist/resources/extensions/gsd/auto/loop.js +89 -1
  6. package/dist/resources/extensions/gsd/auto/phases.js +28 -10
  7. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  8. package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
  11. package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
  12. package/dist/resources/extensions/gsd/auto-start.js +8 -3
  13. package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
  14. package/dist/resources/extensions/gsd/auto.js +64 -2
  15. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
  16. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
  17. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
  19. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
  20. package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
  21. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  23. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
  24. package/dist/resources/extensions/gsd/context-injector.js +74 -0
  25. package/dist/resources/extensions/gsd/context-store.js +4 -3
  26. package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
  27. package/dist/resources/extensions/gsd/custom-verification.js +145 -0
  28. package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
  29. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
  30. package/dist/resources/extensions/gsd/db-writer.js +5 -2
  31. package/dist/resources/extensions/gsd/definition-loader.js +352 -0
  32. package/dist/resources/extensions/gsd/detection.js +1 -1
  33. package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
  34. package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
  35. package/dist/resources/extensions/gsd/doctor.js +11 -1
  36. package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
  37. package/dist/resources/extensions/gsd/engine-types.js +8 -0
  38. package/dist/resources/extensions/gsd/execution-policy.js +8 -0
  39. package/dist/resources/extensions/gsd/exit-command.js +12 -2
  40. package/dist/resources/extensions/gsd/export.js +9 -13
  41. package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
  42. package/dist/resources/extensions/gsd/files.js +28 -11
  43. package/dist/resources/extensions/gsd/forensics.js +10 -3
  44. package/dist/resources/extensions/gsd/git-service.js +5 -1
  45. package/dist/resources/extensions/gsd/graph.js +225 -0
  46. package/dist/resources/extensions/gsd/gsd-db.js +25 -8
  47. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  48. package/dist/resources/extensions/gsd/guided-flow.js +7 -3
  49. package/dist/resources/extensions/gsd/journal.js +85 -0
  50. package/dist/resources/extensions/gsd/md-importer.js +5 -0
  51. package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
  52. package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
  53. package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
  54. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  55. package/dist/resources/extensions/gsd/preferences.js +1 -0
  56. package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
  57. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  58. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  59. package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  61. package/dist/resources/extensions/gsd/repo-identity.js +46 -2
  62. package/dist/resources/extensions/gsd/rule-registry.js +489 -0
  63. package/dist/resources/extensions/gsd/rule-types.js +6 -0
  64. package/dist/resources/extensions/gsd/run-manager.js +134 -0
  65. package/dist/resources/extensions/gsd/service-tier.js +138 -0
  66. package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
  67. package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
  68. package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
  69. package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
  70. package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
  71. package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
  72. package/dist/resources/extensions/subagent/index.js +7 -3
  73. package/dist/resources/extensions/voice/index.js +4 -4
  74. package/dist/resources/skills/create-workflow/SKILL.md +103 -0
  75. package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  76. package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
  77. package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  78. package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  79. package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  80. package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  81. package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  82. package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  83. package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  84. package/dist/web/standalone/.next/BUILD_ID +1 -1
  85. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  86. package/dist/web/standalone/.next/build-manifest.json +3 -3
  87. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  88. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  90. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/index.html +1 -1
  121. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  128. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  129. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  132. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  133. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  134. package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
  135. package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
  136. package/dist/web-mode.d.ts +2 -0
  137. package/dist/web-mode.js +29 -7
  138. package/package.json +1 -1
  139. package/packages/native/src/__tests__/text.test.mjs +33 -0
  140. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
  141. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  148. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
  149. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
  150. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  151. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
  152. package/src/resources/extensions/gsd/auto/loop.ts +101 -1
  153. package/src/resources/extensions/gsd/auto/phases.ts +30 -10
  154. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  155. package/src/resources/extensions/gsd/auto/types.ts +4 -0
  156. package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
  157. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
  158. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
  159. package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
  160. package/src/resources/extensions/gsd/auto-start.ts +8 -3
  161. package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
  162. package/src/resources/extensions/gsd/auto.ts +71 -2
  163. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
  164. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
  165. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  166. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
  167. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
  168. package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
  169. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  170. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  171. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
  172. package/src/resources/extensions/gsd/context-injector.ts +100 -0
  173. package/src/resources/extensions/gsd/context-store.ts +4 -3
  174. package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
  175. package/src/resources/extensions/gsd/custom-verification.ts +180 -0
  176. package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
  177. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
  178. package/src/resources/extensions/gsd/db-writer.ts +6 -2
  179. package/src/resources/extensions/gsd/definition-loader.ts +462 -0
  180. package/src/resources/extensions/gsd/detection.ts +1 -1
  181. package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
  182. package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
  183. package/src/resources/extensions/gsd/doctor.ts +12 -1
  184. package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
  185. package/src/resources/extensions/gsd/engine-types.ts +71 -0
  186. package/src/resources/extensions/gsd/execution-policy.ts +43 -0
  187. package/src/resources/extensions/gsd/exit-command.ts +14 -2
  188. package/src/resources/extensions/gsd/export.ts +8 -15
  189. package/src/resources/extensions/gsd/extension-manifest.json +2 -2
  190. package/src/resources/extensions/gsd/files.ts +29 -12
  191. package/src/resources/extensions/gsd/forensics.ts +9 -3
  192. package/src/resources/extensions/gsd/git-service.ts +5 -4
  193. package/src/resources/extensions/gsd/graph.ts +312 -0
  194. package/src/resources/extensions/gsd/gsd-db.ts +37 -8
  195. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  196. package/src/resources/extensions/gsd/guided-flow.ts +7 -3
  197. package/src/resources/extensions/gsd/journal.ts +134 -0
  198. package/src/resources/extensions/gsd/md-importer.ts +6 -0
  199. package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
  200. package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
  201. package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
  202. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  203. package/src/resources/extensions/gsd/preferences.ts +1 -0
  204. package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
  205. package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  206. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  207. package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
  208. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  209. package/src/resources/extensions/gsd/repo-identity.ts +47 -2
  210. package/src/resources/extensions/gsd/rule-registry.ts +599 -0
  211. package/src/resources/extensions/gsd/rule-types.ts +68 -0
  212. package/src/resources/extensions/gsd/run-manager.ts +180 -0
  213. package/src/resources/extensions/gsd/service-tier.ts +171 -0
  214. package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
  215. package/src/resources/extensions/gsd/templates/decisions.md +2 -2
  216. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
  217. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
  218. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
  219. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
  220. package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
  221. package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
  222. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
  223. package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
  224. package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
  225. package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
  226. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
  227. package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
  228. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
  229. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
  230. package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
  231. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
  232. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
  233. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
  234. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
  235. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
  236. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
  237. package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
  238. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
  239. package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
  240. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
  241. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
  242. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
  243. package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
  244. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
  245. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
  246. package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
  247. package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
  248. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  249. package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
  250. package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
  251. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
  252. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
  253. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
  254. package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
  255. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
  256. package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
  257. package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
  258. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
  259. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
  260. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
  261. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
  262. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
  263. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
  264. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
  265. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
  266. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
  267. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
  268. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
  269. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
  270. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
  271. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
  272. package/src/resources/extensions/gsd/types.ts +3 -0
  273. package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
  274. package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
  275. package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
  276. package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
  277. package/src/resources/extensions/subagent/index.ts +7 -3
  278. package/src/resources/extensions/voice/index.ts +4 -4
  279. package/src/resources/skills/create-workflow/SKILL.md +103 -0
  280. package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  281. package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
  282. package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  283. package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  284. package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  285. package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  286. package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  287. package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  288. package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  289. package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
  290. /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_buildManifest.js +0 -0
  291. /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_ssgManifest.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { afterEach, beforeEach, describe, it } from "node:test";
@@ -59,7 +59,9 @@ describe("ModelDiscoveryCache — basic operations", () => {
59
59
 
60
60
  cache.clear("openai");
61
61
  assert.equal(cache.get("openai"), undefined);
62
- assert.ok(cache.get("google"));
62
+ const googleEntry = cache.get("google");
63
+ assert.ok(googleEntry);
64
+ assert.equal(googleEntry.models[0].id, "gemini-pro");
63
65
  });
64
66
 
65
67
  it("clear without provider removes all entries", () => {
@@ -1,7 +1,7 @@
1
1
  // GSD Login Dialog Component — OAuth login flow UI
2
2
  // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
3
  import { getOAuthProviders } from "@gsd/pi-ai/oauth";
4
- import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@gsd/pi-tui";
4
+ import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, truncateToWidth, type TUI } from "@gsd/pi-tui";
5
5
  import { execFile } from "child_process";
6
6
  import { theme } from "../theme/theme.js";
7
7
  import { DynamicBorder } from "./dynamic-border.js";
@@ -121,21 +121,25 @@ export class LoginDialogComponent extends Container implements Focusable {
121
121
  showAuth(url: string, instructions?: string): void {
122
122
  this.contentContainer.clear();
123
123
  this.contentContainer.addChild(new Spacer(1));
124
- this.contentContainer.addChild(new Text(theme.fg("accent", url), 1, 0));
124
+
125
+ // Truncate the visible URL text so it never wraps (which would break
126
+ // the OSC 8 hyperlink). The full URL is still the link target.
127
+ const maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);
128
+ const displayUrl = truncateToWidth(url, maxUrlWidth);
129
+ const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
130
+ this.contentContainer.addChild(new Text(urlLink, 1, 0));
125
131
 
126
132
  const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
127
- const hyperlink = `\x1b]8;;${url}\x07${clickHint}\x1b]8;;\x07`;
128
- this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
133
+ this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
129
134
 
130
135
  if (instructions) {
131
136
  this.contentContainer.addChild(new Spacer(1));
132
137
  this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
133
138
  }
134
139
 
135
- // Try to open browser on Windows, `start` needs an empty title arg
136
- // so it treats the URL as a target, not a window title
140
+ // PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
137
141
  if (process.platform === "win32") {
138
- execFile("cmd", ["/c", "start", "", url], () => {});
142
+ execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => {});
139
143
  } else {
140
144
  const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
141
145
  execFile(openCmd, [url], () => {});
@@ -2321,6 +2321,11 @@ export class InteractiveMode {
2321
2321
  }
2322
2322
 
2323
2323
  private handleCtrlZ(): void {
2324
+ // On Windows, SIGTSTP doesn't exist - Ctrl+Z is not supported
2325
+ if (process.platform === "win32") {
2326
+ return;
2327
+ }
2328
+
2324
2329
  // Ignore SIGINT while suspended so Ctrl+C in the terminal does not
2325
2330
  // kill the backgrounded process. The handler is removed on resume.
2326
2331
  const ignoreSigint = () => {};
@@ -19,6 +19,7 @@ import type {
19
19
  import type { DispatchAction } from "../auto-dispatch.js";
20
20
  import type { WorktreeResolver } from "../worktree-resolver.js";
21
21
  import type { CmuxLogLevel } from "../../cmux/index.js";
22
+ import type { JournalEntry } from "../journal.js";
22
23
 
23
24
  /**
24
25
  * Dependencies injected by the caller (auto.ts startAuto) so autoLoop
@@ -102,7 +103,7 @@ export interface LoopDeps {
102
103
  basePath: string,
103
104
  milestoneId: string,
104
105
  roadmapContent: string,
105
- ) => { pushed: boolean };
106
+ ) => { pushed: boolean; codeFilesChanged: boolean };
106
107
  teardownAutoWorktree: (basePath: string, milestoneId: string) => void;
107
108
  createAutoWorktree: (basePath: string, milestoneId: string) => string;
108
109
  captureIntegrationBranch: (
@@ -285,4 +286,7 @@ export interface LoopDeps {
285
286
 
286
287
  // Session manager
287
288
  getSessionFile: (ctx: ExtensionContext) => string;
289
+
290
+ // Journal
291
+ emitJournalEvent: (entry: JournalEntry) => void;
288
292
  }
@@ -9,6 +9,7 @@
9
9
 
10
10
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
11
11
 
12
+ import { randomUUID } from "node:crypto";
12
13
  import type { AutoSession, SidecarItem } from "./session.js";
13
14
  import type { LoopDeps } from "./loop-deps.js";
14
15
  import {
@@ -27,6 +28,7 @@ import {
27
28
  } from "./phases.js";
28
29
  import { debugLog } from "../debug-logger.js";
29
30
  import { isInfrastructureError } from "./infra-errors.js";
31
+ import { resolveEngine } from "../engine-resolver.js";
30
32
 
31
33
  /**
32
34
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
@@ -51,6 +53,11 @@ export async function autoLoop(
51
53
  iteration++;
52
54
  debugLog("autoLoop", { phase: "loop-top", iteration });
53
55
 
56
+ // ── Journal: per-iteration flow grouping ──
57
+ const flowId = randomUUID();
58
+ let seqCounter = 0;
59
+ const nextSeq = () => ++seqCounter;
60
+
54
61
  if (iteration > MAX_LOOP_ITERATIONS) {
55
62
  debugLog("autoLoop", {
56
63
  phase: "exit",
@@ -84,6 +91,7 @@ export async function autoLoop(
84
91
  unitType: sidecarItem.unitType,
85
92
  unitId: sidecarItem.unitId,
86
93
  });
94
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
87
95
  }
88
96
 
89
97
  const sessionLockBase = deps.lockBase();
@@ -106,9 +114,100 @@ export async function autoLoop(
106
114
  }
107
115
  }
108
116
 
109
- const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration };
117
+ const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
118
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
110
119
  let iterData: IterationData;
111
120
 
121
+ // ── Custom engine path ──────────────────────────────────────────────
122
+ // When activeEngineId is a non-dev value, bypass runPreDispatch and
123
+ // runDispatch entirely — the custom engine drives its own state via
124
+ // GRAPH.yaml. Shares runGuards and runUnitPhase with the dev path.
125
+ // After unit execution, verifies then reconciles via the engine layer.
126
+ //
127
+ // GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
128
+ // to the dev path below.
129
+ if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
130
+ debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
131
+
132
+ const { engine, policy } = resolveEngine({
133
+ activeEngineId: s.activeEngineId,
134
+ activeRunDir: s.activeRunDir,
135
+ });
136
+
137
+ const engineState = await engine.deriveState(s.basePath);
138
+ if (engineState.isComplete) {
139
+ await deps.stopAuto(ctx, pi, "Workflow complete");
140
+ break;
141
+ }
142
+
143
+ debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
144
+ const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
145
+
146
+ if (dispatch.action === "stop") {
147
+ await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
148
+ break;
149
+ }
150
+ if (dispatch.action === "skip") {
151
+ continue;
152
+ }
153
+
154
+ // dispatch.action === "dispatch"
155
+ const step = dispatch.step!;
156
+ const gsdState = await deps.deriveState(s.basePath);
157
+
158
+ iterData = {
159
+ unitType: step.unitType,
160
+ unitId: step.unitId,
161
+ prompt: step.prompt,
162
+ finalPrompt: step.prompt,
163
+ pauseAfterUatDispatch: false,
164
+ observabilityIssues: [],
165
+ state: gsdState,
166
+ mid: s.currentMilestoneId ?? "workflow",
167
+ midTitle: "Workflow",
168
+ isRetry: false,
169
+ previousTier: undefined,
170
+ };
171
+
172
+ // ── Progress widget (mirrors dev path in runDispatch) ──
173
+ deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
174
+
175
+ // ── Guards (shared with dev path) ──
176
+ const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
177
+ if (guardsResult.action === "break") break;
178
+
179
+ // ── Unit execution (shared with dev path) ──
180
+ const unitPhaseResult = await runUnitPhase(ic, iterData, loopState);
181
+ if (unitPhaseResult.action === "break") break;
182
+
183
+ // ── Verify first, then reconcile (only mark complete on pass) ──
184
+ debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
185
+ const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
186
+ if (verifyResult === "pause") {
187
+ await deps.pauseAuto(ctx, pi);
188
+ break;
189
+ }
190
+ if (verifyResult === "retry") {
191
+ debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId });
192
+ continue;
193
+ }
194
+
195
+ // Verification passed — mark step complete
196
+ debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
197
+ await engine.reconcile(engineState, {
198
+ unitType: iterData.unitType,
199
+ unitId: iterData.unitId,
200
+ startedAt: s.currentUnit?.startedAt ?? Date.now(),
201
+ finishedAt: Date.now(),
202
+ });
203
+
204
+ deps.clearUnitTimeout();
205
+ consecutiveErrors = 0;
206
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
207
+ debugLog("autoLoop", { phase: "iteration-complete", iteration });
208
+ continue;
209
+ }
210
+
112
211
  if (!sidecarItem) {
113
212
  // ── Phase 1: Pre-dispatch ─────────────────────────────────────────
114
213
  const preDispatchResult = await runPreDispatch(ic, loopState);
@@ -153,6 +252,7 @@ export async function autoLoop(
153
252
  if (finalizeResult.action === "continue") continue;
154
253
 
155
254
  consecutiveErrors = 0; // Iteration completed successfully
255
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
156
256
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
157
257
  } catch (loopErr) {
158
258
  // ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──
@@ -26,6 +26,7 @@ import { runUnit } from "./run-unit.js";
26
26
  import { debugLog } from "../debug-logger.js";
27
27
  import { gsdRoot } from "../paths.js";
28
28
  import { atomicWriteSync } from "../atomic-write.js";
29
+ import { PROJECT_FILES } from "../detection.js";
29
30
  import { join } from "node:path";
30
31
 
31
32
  // ─── generateMilestoneReport ──────────────────────────────────────────────────
@@ -192,6 +193,7 @@ export async function runPreDispatch(
192
193
 
193
194
  // ── Milestone transition ────────────────────────────────────────────
194
195
  if (mid && s.currentMilestoneId && mid !== s.currentMilestoneId) {
196
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "milestone-transition", data: { from: s.currentMilestoneId, to: mid } });
195
197
  ctx.ui.notify(
196
198
  `Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}: ${midTitle}.`,
197
199
  "info",
@@ -386,6 +388,7 @@ export async function runPreDispatch(
386
388
  );
387
389
  }
388
390
  debugLog("autoLoop", { phase: "exit", reason: "no-active-milestone" });
391
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "no-active-milestone" } });
389
392
  return { action: "break", reason: "no-active-milestone" };
390
393
  }
391
394
 
@@ -454,6 +457,7 @@ export async function runPreDispatch(
454
457
  );
455
458
  await closeoutAndStop(ctx, pi, s, deps, `Milestone ${mid} complete`);
456
459
  debugLog("autoLoop", { phase: "exit", reason: "milestone-complete" });
460
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "milestone-complete", milestoneId: mid } });
457
461
  return { action: "break", reason: "milestone-complete" };
458
462
  }
459
463
 
@@ -465,6 +469,7 @@ export async function runPreDispatch(
465
469
  deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention");
466
470
  deps.logCmuxEvent(prefs, blockerMsg, "error");
467
471
  debugLog("autoLoop", { phase: "exit", reason: "blocked" });
472
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
468
473
  return { action: "break", reason: "blocked" };
469
474
  }
470
475
 
@@ -497,6 +502,7 @@ export async function runDispatch(
497
502
  });
498
503
 
499
504
  if (dispatchResult.action === "stop") {
505
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
500
506
  await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
501
507
  debugLog("autoLoop", { phase: "exit", reason: "dispatch-stop" });
502
508
  return { action: "break", reason: "dispatch-stop" };
@@ -508,6 +514,8 @@ export async function runDispatch(
508
514
  return { action: "continue" };
509
515
  }
510
516
 
517
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-match", rule: dispatchResult.matchedRule, data: { unitType: dispatchResult.unitType, unitId: dispatchResult.unitId } });
518
+
511
519
  let unitType = dispatchResult.unitType;
512
520
  let unitId = dispatchResult.unitId;
513
521
  let prompt = dispatchResult.prompt;
@@ -600,6 +608,7 @@ export async function runDispatch(
600
608
  `Pre-dispatch hook${preDispatchResult.firedHooks.length > 1 ? "s" : ""}: ${preDispatchResult.firedHooks.join(", ")}`,
601
609
  "info",
602
610
  );
611
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "pre-dispatch-hook", data: { firedHooks: preDispatchResult.firedHooks, action: preDispatchResult.action } });
603
612
  }
604
613
  if (preDispatchResult.action === "skip") {
605
614
  ctx.ui.notify(
@@ -809,25 +818,27 @@ export async function runUnitPhase(
809
818
  unitId,
810
819
  });
811
820
 
812
- // ── Worktree health check (#1833) ───────────────────────────────────
821
+ // ── Worktree health check (#1833, #1843) ────────────────────────────
813
822
  // Verify the working directory is a valid git checkout with project
814
823
  // files before dispatching work. A broken worktree causes agents to
815
824
  // hallucinate summaries since they cannot read or write any files.
825
+ // Uses the shared PROJECT_FILES list from detection.ts to support all
826
+ // ecosystems (Rust, Go, Python, Java, etc.), not just JS.
816
827
  if (s.basePath && unitType === "execute-task") {
817
828
  const gitMarker = join(s.basePath, ".git");
818
829
  const hasGit = deps.existsSync(gitMarker);
819
- const hasPackageJson = deps.existsSync(join(s.basePath, "package.json"));
820
- const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
821
830
  if (!hasGit) {
822
831
  const msg = `Worktree health check failed: ${s.basePath} has no .git — refusing to dispatch ${unitType} ${unitId}`;
823
- debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit, hasPackageJson, hasSrcDir });
832
+ debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit });
824
833
  ctx.ui.notify(msg, "error");
825
834
  await deps.stopAuto(ctx, pi, msg);
826
835
  return { action: "break", reason: "worktree-invalid" };
827
836
  }
828
- if (!hasPackageJson && !hasSrcDir) {
829
- const msg = `Worktree health check failed: ${s.basePath} has no package.json or src/ — refusing to dispatch ${unitType} ${unitId}`;
830
- debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit, hasPackageJson, hasSrcDir });
837
+ const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
838
+ const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
839
+ if (!hasProjectFile && !hasSrcDir) {
840
+ const msg = `Worktree health check failed: ${s.basePath} has no recognized project files — refusing to dispatch ${unitType} ${unitId}`;
841
+ debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasProjectFile, hasSrcDir });
831
842
  ctx.ui.notify(msg, "error");
832
843
  await deps.stopAuto(ctx, pi, msg);
833
844
  return { action: "break", reason: "worktree-invalid" };
@@ -843,6 +854,8 @@ export async function runUnitPhase(
843
854
  const previousTier = s.currentUnitRouting?.tier;
844
855
 
845
856
  s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
857
+ const unitStartSeq = ic.nextSeq();
858
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
846
859
  deps.captureAvailableSkills();
847
860
  deps.writeUnitRuntimeRecord(
848
861
  s.basePath,
@@ -988,7 +1001,12 @@ export async function runUnitPhase(
988
1001
  unitId,
989
1002
  prefs,
990
1003
  buildSnapshotOpts: () => deps.buildSnapshotOpts(unitType, unitId),
991
- buildRecoveryContext: () => ({}),
1004
+ buildRecoveryContext: () => ({
1005
+ basePath: s.basePath,
1006
+ verbose: s.verbose,
1007
+ currentUnitStartedAt: s.currentUnit?.startedAt ?? Date.now(),
1008
+ unitRecoveryCount: s.unitRecoveryCount,
1009
+ }),
992
1010
  pauseAuto: deps.pauseAuto,
993
1011
  });
994
1012
 
@@ -1115,9 +1133,9 @@ export async function runUnitPhase(
1115
1133
  );
1116
1134
  }
1117
1135
 
1118
- const isHookUnit = unitType.startsWith("hook/");
1136
+ const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
1119
1137
  const artifactVerified =
1120
- isHookUnit ||
1138
+ skipArtifactVerification ||
1121
1139
  deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
1122
1140
  if (artifactVerified) {
1123
1141
  s.completedUnits.push({
@@ -1141,6 +1159,8 @@ export async function runUnitPhase(
1141
1159
  s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
1142
1160
  }
1143
1161
 
1162
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
1163
+
1144
1164
  return { action: "next", data: { unitStartedAt: s.currentUnit.startedAt } };
1145
1165
  }
1146
1166
 
@@ -83,6 +83,8 @@ export class AutoSession {
83
83
  paused = false;
84
84
  stepMode = false;
85
85
  verbose = false;
86
+ activeEngineId: string | null = null;
87
+ activeRunDir: string | null = null;
86
88
  cmdCtx: ExtensionCommandContext | null = null;
87
89
 
88
90
  // ── Paths ────────────────────────────────────────────────────────────────
@@ -174,6 +176,8 @@ export class AutoSession {
174
176
  this.paused = false;
175
177
  this.stepMode = false;
176
178
  this.verbose = false;
179
+ this.activeEngineId = null;
180
+ this.activeRunDir = null;
177
181
  this.cmdCtx = null;
178
182
 
179
183
  // Paths
@@ -226,6 +230,8 @@ export class AutoSession {
226
230
  paused: this.paused,
227
231
  stepMode: this.stepMode,
228
232
  basePath: this.basePath,
233
+ activeEngineId: this.activeEngineId,
234
+ activeRunDir: this.activeRunDir,
229
235
  currentMilestoneId: this.currentMilestoneId,
230
236
  currentUnit: this.currentUnit,
231
237
  completedUnits: this.completedUnits.length,
@@ -69,6 +69,10 @@ export interface IterationContext {
69
69
  deps: LoopDeps;
70
70
  prefs: GSDPreferences | undefined;
71
71
  iteration: number;
72
+ /** UUID grouping all journal events for this iteration. */
73
+ flowId: string;
74
+ /** Returns the next monotonically increasing sequence number (1-based, reset per iteration). */
75
+ nextSeq: () => number;
72
76
  }
73
77
 
74
78
  export interface LoopState {
@@ -24,6 +24,7 @@ import { GLYPH, INDENT } from "../shared/mod.js";
24
24
  import { computeProgressScore } from "./progress-score.js";
25
25
  import { getActiveWorktreeName } from "./worktree-command.js";
26
26
  import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
27
+ import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
27
28
 
28
29
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
29
30
 
@@ -78,6 +79,7 @@ export function unitVerb(unitType: string): string {
78
79
  case "rewrite-docs": return "rewriting";
79
80
  case "reassess-roadmap": return "reassessing";
80
81
  case "run-uat": return "running UAT";
82
+ case "custom-step": return "executing workflow step";
81
83
  default: return unitType;
82
84
  }
83
85
  }
@@ -96,6 +98,7 @@ export function unitPhaseLabel(unitType: string): string {
96
98
  case "rewrite-docs": return "REWRITE";
97
99
  case "reassess-roadmap": return "REASSESS";
98
100
  case "run-uat": return "UAT";
101
+ case "custom-step": return "WORKFLOW";
99
102
  default: return unitType.toUpperCase();
100
103
  }
101
104
  }
@@ -460,6 +463,9 @@ export function updateProgressWidget(
460
463
  // Pre-fetch last commit for display
461
464
  refreshLastCommit(accessors.getBasePath());
462
465
 
466
+ // Cache the effective service tier at widget creation time (reads preferences)
467
+ const effectiveServiceTier = getEffectiveServiceTier();
468
+
463
469
  ctx.ui.setWidget("gsd-progress", (tui, theme) => {
464
470
  let pulseBright = true;
465
471
  let cachedLines: string[] | undefined;
@@ -572,9 +578,10 @@ export function updateProgressWidget(
572
578
  // Model display — shown in context section, not stats
573
579
  const modelId = cmdCtx?.model?.id ?? "";
574
580
  const modelProvider = cmdCtx?.model?.provider ?? "";
575
- const modelDisplay = modelProvider && modelId
581
+ const tierIcon = resolveServiceTierIcon(effectiveServiceTier, modelId);
582
+ const modelDisplay = (modelProvider && modelId
576
583
  ? `${modelProvider}/${modelId}`
577
- : modelId;
584
+ : modelId) + (tierIcon ? ` ${tierIcon}` : "");
578
585
 
579
586
  // ── Mode: off — return empty ──────────────────────────────────
580
587
  if (widgetMode === "off") {
@@ -54,9 +54,11 @@ export type DispatchAction =
54
54
  unitId: string;
55
55
  prompt: string;
56
56
  pauseAfterDispatch?: boolean;
57
+ /** Name of the matched dispatch rule from the unified registry (journal provenance). */
58
+ matchedRule?: string;
57
59
  }
58
- | { action: "stop"; reason: string; level: "info" | "warning" | "error" }
59
- | { action: "skip" };
60
+ | { action: "stop"; reason: string; level: "info" | "warning" | "error"; matchedRule?: string }
61
+ | { action: "skip"; matchedRule?: string };
60
62
 
61
63
  export interface DispatchContext {
62
64
  basePath: string;
@@ -67,7 +69,7 @@ export interface DispatchContext {
67
69
  session?: import("./auto/session.js").AutoSession;
68
70
  }
69
71
 
70
- interface DispatchRule {
72
+ export interface DispatchRule {
71
73
  /** Human-readable name for debugging and test identification */
72
74
  name: string;
73
75
  /** Return a DispatchAction if this rule matches, null to fall through */
@@ -88,7 +90,7 @@ const MAX_REWRITE_ATTEMPTS = 3;
88
90
 
89
91
  // ─── Rules ────────────────────────────────────────────────────────────────
90
92
 
91
- const DISPATCH_RULES: DispatchRule[] = [
93
+ export const DISPATCH_RULES: DispatchRule[] = [
92
94
  {
93
95
  name: "rewrite-docs (override gate)",
94
96
  match: async ({ mid, midTitle, state, basePath, session }) => {
@@ -608,18 +610,35 @@ const DISPATCH_RULES: DispatchRule[] = [
608
610
  },
609
611
  ];
610
612
 
613
+ import { getRegistry } from "./rule-registry.js";
614
+
611
615
  // ─── Resolver ─────────────────────────────────────────────────────────────
612
616
 
613
617
  /**
614
618
  * Evaluate dispatch rules in order. Returns the first matching action,
615
619
  * or a "stop" action if no rule matches (unhandled phase).
620
+ *
621
+ * Delegates to the RuleRegistry when initialized; falls back to inline
622
+ * loop over DISPATCH_RULES for backward compatibility (tests that import
623
+ * resolveDispatch directly without registry initialization).
616
624
  */
617
625
  export async function resolveDispatch(
618
626
  ctx: DispatchContext,
619
627
  ): Promise<DispatchAction> {
628
+ // Delegate to registry when available
629
+ try {
630
+ const registry = getRegistry();
631
+ return await registry.evaluateDispatch(ctx);
632
+ } catch {
633
+ // Registry not initialized — fall back to inline loop
634
+ }
635
+
620
636
  for (const rule of DISPATCH_RULES) {
621
637
  const result = await rule.match(ctx);
622
- if (result) return result;
638
+ if (result) {
639
+ if (result.action !== "skip") result.matchedRule = rule.name;
640
+ return result;
641
+ }
623
642
  }
624
643
 
625
644
  // No rule matched — unhandled phase
@@ -627,6 +646,7 @@ export async function resolveDispatch(
627
646
  action: "stop",
628
647
  reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
629
648
  level: "info",
649
+ matchedRule: "<no-match>",
630
650
  };
631
651
  }
632
652
 
@@ -59,6 +59,7 @@ import { existsSync, unlinkSync } from "node:fs";
59
59
  import { join } from "node:path";
60
60
  import { uncheckTaskInPlan } from "./undo.js";
61
61
  import { atomicWriteSync } from "./atomic-write.js";
62
+ import { _resetHasChangesCache } from "./native-git-bridge.js";
62
63
 
63
64
  /** Throttle STATE.md rebuilds — at most once per 30 seconds */
64
65
  const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
@@ -156,6 +157,13 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
156
157
  }
157
158
  }
158
159
 
160
+ // Invalidate the nativeHasChanges cache before auto-commit (#1853).
161
+ // The cache has a 10-second TTL and is keyed by basePath. A stale
162
+ // `false` result causes autoCommit to skip staging entirely, leaving
163
+ // code files only in the working tree where they are destroyed by
164
+ // `git worktree remove --force` during teardown.
165
+ _resetHasChangesCache();
166
+
159
167
  const commitMsg = autoCommitCurrentBranch(s.basePath, s.currentUnit.type, s.currentUnit.id, taskContext);
160
168
  if (commitMsg) {
161
169
  ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
@@ -319,10 +319,15 @@ export function verifyExpectedArtifact(
319
319
  // plan has no tasks, creating an infinite skip loop (#699).
320
320
  if (unitType === "plan-slice") {
321
321
  const planContent = readFileSync(absPath, "utf-8");
322
- if (!/^- \[[xX ]\] \*\*T\d+:/m.test(planContent)) return false;
322
+ // Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
323
+ const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
324
+ const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
325
+ if (!hasCheckboxTask && !hasHeadingTask) return false;
323
326
  }
324
327
 
325
- // execute-task must also have its checkbox marked [x] in the slice plan
328
+ // execute-task must also have its checkbox marked [x] in the slice plan.
329
+ // Heading-style plans (### T01 -- Title) have no checkbox — the task summary
330
+ // file existence (checked above via resolveExpectedArtifactPath) is sufficient.
326
331
  if (unitType === "execute-task") {
327
332
  const parts = unitId.split("/");
328
333
  const mid = parts[0];
@@ -333,8 +338,11 @@ export function verifyExpectedArtifact(
333
338
  if (planAbs && existsSync(planAbs)) {
334
339
  const planContent = readFileSync(planAbs, "utf-8");
335
340
  const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
336
- const re = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
337
- if (!re.test(planContent)) return false;
341
+ const cbRe = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
342
+ const hdRe = new RegExp(`^#{2,4}\\s+${escapedTid}\\s*(?:--|—|:)`, "m");
343
+ // Heading-style entries count as verified (no checkbox to toggle);
344
+ // checkbox-style entries require [x].
345
+ if (!cbRe.test(planContent) && !hdRe.test(planContent)) return false;
338
346
  }
339
347
  }
340
348
  }
@@ -20,7 +20,7 @@ import {
20
20
  resolveSkillDiscoveryMode,
21
21
  getIsolationMode,
22
22
  } from "./preferences.js";
23
- import { ensureGsdSymlink, validateProjectId } from "./repo-identity.js";
23
+ import { ensureGsdSymlink, isInheritedRepo, validateProjectId } from "./repo-identity.js";
24
24
  import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
25
25
  import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
26
26
  import { gsdRoot, resolveMilestoneFile, milestonesDir } from "./paths.js";
@@ -140,8 +140,13 @@ export async function bootstrapAutoSession(
140
140
  return releaseLockAndReturn();
141
141
  }
142
142
 
143
- // Ensure git repo exists
144
- if (!nativeIsRepo(base)) {
143
+ // Ensure git repo exists.
144
+ // Guard against inherited repos: if `base` is a subdirectory of another
145
+ // git repo that has no .gsd (i.e. the parent project was never initialised
146
+ // with GSD), create a fresh git repo at `base` so it gets its own identity
147
+ // hash. Without this, repoIdentity() resolves to the parent repo's hash
148
+ // and loads milestones from an unrelated project (#1639).
149
+ if (!nativeIsRepo(base) || isInheritedRepo(base)) {
145
150
  const mainBranch =
146
151
  loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
147
152
  nativeInit(base, mainBranch);