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
@@ -13,6 +13,7 @@ import { runUnit } from "./run-unit.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
14
  import { gsdRoot } from "../paths.js";
15
15
  import { atomicWriteSync } from "../atomic-write.js";
16
+ import { PROJECT_FILES } from "../detection.js";
16
17
  import { join } from "node:path";
17
18
  // ─── generateMilestoneReport ──────────────────────────────────────────────────
18
19
  /**
@@ -121,6 +122,7 @@ export async function runPreDispatch(ic, loopState) {
121
122
  });
122
123
  // ── Milestone transition ────────────────────────────────────────────
123
124
  if (mid && s.currentMilestoneId && mid !== s.currentMilestoneId) {
125
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "milestone-transition", data: { from: s.currentMilestoneId, to: mid } });
124
126
  ctx.ui.notify(`Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}: ${midTitle}.`, "info");
125
127
  deps.sendDesktopNotification("GSD", `Milestone ${s.currentMilestoneId} complete!`, "success", "milestone");
126
128
  deps.logCmuxEvent(prefs, `Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}.`, "success");
@@ -248,6 +250,7 @@ export async function runPreDispatch(ic, loopState) {
248
250
  await deps.stopAuto(ctx, pi, `No active milestone — ${incomplete.length} incomplete (${ids}), see diagnostic above`);
249
251
  }
250
252
  debugLog("autoLoop", { phase: "exit", reason: "no-active-milestone" });
253
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "no-active-milestone" } });
251
254
  return { action: "break", reason: "no-active-milestone" };
252
255
  }
253
256
  if (!midTitle) {
@@ -295,6 +298,7 @@ export async function runPreDispatch(ic, loopState) {
295
298
  deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
296
299
  await closeoutAndStop(ctx, pi, s, deps, `Milestone ${mid} complete`);
297
300
  debugLog("autoLoop", { phase: "exit", reason: "milestone-complete" });
301
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "milestone-complete", milestoneId: mid } });
298
302
  return { action: "break", reason: "milestone-complete" };
299
303
  }
300
304
  // Terminal: blocked
@@ -305,6 +309,7 @@ export async function runPreDispatch(ic, loopState) {
305
309
  deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention");
306
310
  deps.logCmuxEvent(prefs, blockerMsg, "error");
307
311
  debugLog("autoLoop", { phase: "exit", reason: "blocked" });
312
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
308
313
  return { action: "break", reason: "blocked" };
309
314
  }
310
315
  return { action: "next", data: { state, mid, midTitle } };
@@ -328,6 +333,7 @@ export async function runDispatch(ic, preData, loopState) {
328
333
  session: s,
329
334
  });
330
335
  if (dispatchResult.action === "stop") {
336
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
331
337
  await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
332
338
  debugLog("autoLoop", { phase: "exit", reason: "dispatch-stop" });
333
339
  return { action: "break", reason: "dispatch-stop" };
@@ -337,6 +343,7 @@ export async function runDispatch(ic, preData, loopState) {
337
343
  await new Promise((r) => setImmediate(r));
338
344
  return { action: "continue" };
339
345
  }
346
+ 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 } });
340
347
  let unitType = dispatchResult.unitType;
341
348
  let unitId = dispatchResult.unitId;
342
349
  let prompt = dispatchResult.prompt;
@@ -402,6 +409,7 @@ export async function runDispatch(ic, preData, loopState) {
402
409
  const preDispatchResult = deps.runPreDispatchHooks(unitType, unitId, prompt, s.basePath);
403
410
  if (preDispatchResult.firedHooks.length > 0) {
404
411
  ctx.ui.notify(`Pre-dispatch hook${preDispatchResult.firedHooks.length > 1 ? "s" : ""}: ${preDispatchResult.firedHooks.join(", ")}`, "info");
412
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "pre-dispatch-hook", data: { firedHooks: preDispatchResult.firedHooks, action: preDispatchResult.action } });
405
413
  }
406
414
  if (preDispatchResult.action === "skip") {
407
415
  ctx.ui.notify(`Skipping ${unitType} ${unitId} (pre-dispatch hook).`, "info");
@@ -544,25 +552,27 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
544
552
  unitType,
545
553
  unitId,
546
554
  });
547
- // ── Worktree health check (#1833) ───────────────────────────────────
555
+ // ── Worktree health check (#1833, #1843) ────────────────────────────
548
556
  // Verify the working directory is a valid git checkout with project
549
557
  // files before dispatching work. A broken worktree causes agents to
550
558
  // hallucinate summaries since they cannot read or write any files.
559
+ // Uses the shared PROJECT_FILES list from detection.ts to support all
560
+ // ecosystems (Rust, Go, Python, Java, etc.), not just JS.
551
561
  if (s.basePath && unitType === "execute-task") {
552
562
  const gitMarker = join(s.basePath, ".git");
553
563
  const hasGit = deps.existsSync(gitMarker);
554
- const hasPackageJson = deps.existsSync(join(s.basePath, "package.json"));
555
- const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
556
564
  if (!hasGit) {
557
565
  const msg = `Worktree health check failed: ${s.basePath} has no .git — refusing to dispatch ${unitType} ${unitId}`;
558
- debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit, hasPackageJson, hasSrcDir });
566
+ debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit });
559
567
  ctx.ui.notify(msg, "error");
560
568
  await deps.stopAuto(ctx, pi, msg);
561
569
  return { action: "break", reason: "worktree-invalid" };
562
570
  }
563
- if (!hasPackageJson && !hasSrcDir) {
564
- const msg = `Worktree health check failed: ${s.basePath} has no package.json or src/ — refusing to dispatch ${unitType} ${unitId}`;
565
- debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit, hasPackageJson, hasSrcDir });
571
+ const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
572
+ const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
573
+ if (!hasProjectFile && !hasSrcDir) {
574
+ const msg = `Worktree health check failed: ${s.basePath} has no recognized project files — refusing to dispatch ${unitType} ${unitId}`;
575
+ debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasProjectFile, hasSrcDir });
566
576
  ctx.ui.notify(msg, "error");
567
577
  await deps.stopAuto(ctx, pi, msg);
568
578
  return { action: "break", reason: "worktree-invalid" };
@@ -574,6 +584,8 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
574
584
  s.currentUnit.id === unitId);
575
585
  const previousTier = s.currentUnitRouting?.tier;
576
586
  s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
587
+ const unitStartSeq = ic.nextSeq();
588
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
577
589
  deps.captureAvailableSkills();
578
590
  deps.writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
579
591
  phase: "dispatched",
@@ -683,7 +695,12 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
683
695
  unitId,
684
696
  prefs,
685
697
  buildSnapshotOpts: () => deps.buildSnapshotOpts(unitType, unitId),
686
- buildRecoveryContext: () => ({}),
698
+ buildRecoveryContext: () => ({
699
+ basePath: s.basePath,
700
+ verbose: s.verbose,
701
+ currentUnitStartedAt: s.currentUnit?.startedAt ?? Date.now(),
702
+ unitRecoveryCount: s.unitRecoveryCount,
703
+ }),
687
704
  pauseAuto: deps.pauseAuto,
688
705
  });
689
706
  // Write preliminary lock (no session path yet — runUnit creates a new session).
@@ -759,8 +776,8 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
759
776
  if (s.currentUnitRouting) {
760
777
  deps.recordOutcome(unitType, s.currentUnitRouting.tier, true);
761
778
  }
762
- const isHookUnit = unitType.startsWith("hook/");
763
- const artifactVerified = isHookUnit ||
779
+ const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
780
+ const artifactVerified = skipArtifactVerification ||
764
781
  deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
765
782
  if (artifactVerified) {
766
783
  s.completedUnits.push({
@@ -783,6 +800,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
783
800
  s.unitDispatchCount.delete(`${unitType}/${unitId}`);
784
801
  s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
785
802
  }
803
+ 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 } });
786
804
  return { action: "next", data: { unitStartedAt: s.currentUnit.startedAt } };
787
805
  }
788
806
  // ─── runFinalize ──────────────────────────────────────────────────────────────
@@ -27,6 +27,8 @@ export class AutoSession {
27
27
  paused = false;
28
28
  stepMode = false;
29
29
  verbose = false;
30
+ activeEngineId = null;
31
+ activeRunDir = null;
30
32
  cmdCtx = null;
31
33
  // ── Paths ────────────────────────────────────────────────────────────────
32
34
  basePath = "";
@@ -113,6 +115,8 @@ export class AutoSession {
113
115
  this.paused = false;
114
116
  this.stepMode = false;
115
117
  this.verbose = false;
118
+ this.activeEngineId = null;
119
+ this.activeRunDir = null;
116
120
  this.cmdCtx = null;
117
121
  // Paths
118
122
  this.basePath = "";
@@ -156,6 +160,8 @@ export class AutoSession {
156
160
  paused: this.paused,
157
161
  stepMode: this.stepMode,
158
162
  basePath: this.basePath,
163
+ activeEngineId: this.activeEngineId,
164
+ activeRunDir: this.activeRunDir,
159
165
  currentMilestoneId: this.currentMilestoneId,
160
166
  currentUnit: this.currentUnit,
161
167
  completedUnits: this.completedUnits.length,
@@ -18,6 +18,7 @@ import { GLYPH, INDENT } from "../shared/mod.js";
18
18
  import { computeProgressScore } from "./progress-score.js";
19
19
  import { getActiveWorktreeName } from "./worktree-command.js";
20
20
  import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
21
+ import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
21
22
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
22
23
  /**
23
24
  * Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01").
@@ -45,6 +46,7 @@ export function unitVerb(unitType) {
45
46
  case "rewrite-docs": return "rewriting";
46
47
  case "reassess-roadmap": return "reassessing";
47
48
  case "run-uat": return "running UAT";
49
+ case "custom-step": return "executing workflow step";
48
50
  default: return unitType;
49
51
  }
50
52
  }
@@ -63,6 +65,7 @@ export function unitPhaseLabel(unitType) {
63
65
  case "rewrite-docs": return "REWRITE";
64
66
  case "reassess-roadmap": return "REASSESS";
65
67
  case "run-uat": return "UAT";
68
+ case "custom-step": return "WORKFLOW";
66
69
  default: return unitType.toUpperCase();
67
70
  }
68
71
  }
@@ -370,6 +373,8 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
370
373
  }
371
374
  // Pre-fetch last commit for display
372
375
  refreshLastCommit(accessors.getBasePath());
376
+ // Cache the effective service tier at widget creation time (reads preferences)
377
+ const effectiveServiceTier = getEffectiveServiceTier();
373
378
  ctx.ui.setWidget("gsd-progress", (tui, theme) => {
374
379
  let pulseBright = true;
375
380
  let cachedLines;
@@ -471,9 +476,10 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
471
476
  // Model display — shown in context section, not stats
472
477
  const modelId = cmdCtx?.model?.id ?? "";
473
478
  const modelProvider = cmdCtx?.model?.provider ?? "";
474
- const modelDisplay = modelProvider && modelId
479
+ const tierIcon = resolveServiceTierIcon(effectiveServiceTier, modelId);
480
+ const modelDisplay = (modelProvider && modelId
475
481
  ? `${modelProvider}/${modelId}`
476
- : modelId;
482
+ : modelId) + (tierIcon ? ` ${tierIcon}` : "");
477
483
  // ── Mode: off — return empty ──────────────────────────────────
478
484
  if (widgetMode === "off") {
479
485
  cachedLines = [];
@@ -24,7 +24,7 @@ function missingSliceStop(mid, phase) {
24
24
  // ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
25
25
  const MAX_REWRITE_ATTEMPTS = 3;
26
26
  // ─── Rules ────────────────────────────────────────────────────────────────
27
- const DISPATCH_RULES = [
27
+ export const DISPATCH_RULES = [
28
28
  {
29
29
  name: "rewrite-docs (override gate)",
30
30
  match: async ({ mid, midTitle, state, basePath, session }) => {
@@ -480,22 +480,39 @@ const DISPATCH_RULES = [
480
480
  },
481
481
  },
482
482
  ];
483
+ import { getRegistry } from "./rule-registry.js";
483
484
  // ─── Resolver ─────────────────────────────────────────────────────────────
484
485
  /**
485
486
  * Evaluate dispatch rules in order. Returns the first matching action,
486
487
  * or a "stop" action if no rule matches (unhandled phase).
488
+ *
489
+ * Delegates to the RuleRegistry when initialized; falls back to inline
490
+ * loop over DISPATCH_RULES for backward compatibility (tests that import
491
+ * resolveDispatch directly without registry initialization).
487
492
  */
488
493
  export async function resolveDispatch(ctx) {
494
+ // Delegate to registry when available
495
+ try {
496
+ const registry = getRegistry();
497
+ return await registry.evaluateDispatch(ctx);
498
+ }
499
+ catch {
500
+ // Registry not initialized — fall back to inline loop
501
+ }
489
502
  for (const rule of DISPATCH_RULES) {
490
503
  const result = await rule.match(ctx);
491
- if (result)
504
+ if (result) {
505
+ if (result.action !== "skip")
506
+ result.matchedRule = rule.name;
492
507
  return result;
508
+ }
493
509
  }
494
510
  // No rule matched — unhandled phase
495
511
  return {
496
512
  action: "stop",
497
513
  reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
498
514
  level: "info",
515
+ matchedRule: "<no-match>",
499
516
  };
500
517
  }
501
518
  /** Exposed for testing — returns the rule names in evaluation order. */
@@ -31,6 +31,7 @@ import { existsSync, unlinkSync } from "node:fs";
31
31
  import { join } from "node:path";
32
32
  import { uncheckTaskInPlan } from "./undo.js";
33
33
  import { atomicWriteSync } from "./atomic-write.js";
34
+ import { _resetHasChangesCache } from "./native-git-bridge.js";
34
35
  /** Throttle STATE.md rebuilds — at most once per 30 seconds */
35
36
  const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
36
37
  /**
@@ -103,6 +104,12 @@ export async function postUnitPreVerification(pctx, opts) {
103
104
  }
104
105
  }
105
106
  }
107
+ // Invalidate the nativeHasChanges cache before auto-commit (#1853).
108
+ // The cache has a 10-second TTL and is keyed by basePath. A stale
109
+ // `false` result causes autoCommit to skip staging entirely, leaving
110
+ // code files only in the working tree where they are destroyed by
111
+ // `git worktree remove --force` during teardown.
112
+ _resetHasChangesCache();
106
113
  const commitMsg = autoCommitCurrentBranch(s.basePath, s.currentUnit.type, s.currentUnit.id, taskContext);
107
114
  if (commitMsg) {
108
115
  ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
@@ -849,7 +849,7 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
849
849
  const prefs = loadEffectiveGSDPreferences();
850
850
  const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
851
851
  const commitInstruction = commitDocsEnabled
852
- ? `Commit the plan files only: \`git add ${relSlicePath(base, mid, sid)}/ .gsd/DECISIONS.md .gitignore && git commit -m "docs(${sid}): add slice plan"\`. Do not stage .gsd/STATE.md or other runtime files — the system manages those.`
852
+ ? `Commit the plan files only: \`git add --force ${relSlicePath(base, mid, sid)}/ .gsd/DECISIONS.md .gitignore && git commit -m "docs(${sid}): add slice plan"\`. Do not stage .gsd/STATE.md or other runtime files — the system manages those.`
853
853
  : "Do not commit — planning docs are not tracked in git for this project.";
854
854
  return loadPrompt("plan-slice", {
855
855
  workingDirectory: base,
@@ -263,10 +263,15 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
263
263
  // plan has no tasks, creating an infinite skip loop (#699).
264
264
  if (unitType === "plan-slice") {
265
265
  const planContent = readFileSync(absPath, "utf-8");
266
- if (!/^- \[[xX ]\] \*\*T\d+:/m.test(planContent))
266
+ // Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
267
+ const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
268
+ const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
269
+ if (!hasCheckboxTask && !hasHeadingTask)
267
270
  return false;
268
271
  }
269
- // execute-task must also have its checkbox marked [x] in the slice plan
272
+ // execute-task must also have its checkbox marked [x] in the slice plan.
273
+ // Heading-style plans (### T01 -- Title) have no checkbox — the task summary
274
+ // file existence (checked above via resolveExpectedArtifactPath) is sufficient.
270
275
  if (unitType === "execute-task") {
271
276
  const parts = unitId.split("/");
272
277
  const mid = parts[0];
@@ -277,8 +282,11 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
277
282
  if (planAbs && existsSync(planAbs)) {
278
283
  const planContent = readFileSync(planAbs, "utf-8");
279
284
  const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
280
- const re = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
281
- if (!re.test(planContent))
285
+ const cbRe = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
286
+ const hdRe = new RegExp(`^#{2,4}\\s+${escapedTid}\\s*(?:--|—|:)`, "m");
287
+ // Heading-style entries count as verified (no checkbox to toggle);
288
+ // checkbox-style entries require [x].
289
+ if (!cbRe.test(planContent) && !hdRe.test(planContent))
282
290
  return false;
283
291
  }
284
292
  }
@@ -11,7 +11,7 @@
11
11
  import { deriveState } from "./state.js";
12
12
  import { loadFile, getManifestStatus } from "./files.js";
13
13
  import { loadEffectiveGSDPreferences, resolveSkillDiscoveryMode, getIsolationMode, } from "./preferences.js";
14
- import { ensureGsdSymlink, validateProjectId } from "./repo-identity.js";
14
+ import { ensureGsdSymlink, isInheritedRepo, validateProjectId } from "./repo-identity.js";
15
15
  import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
16
16
  import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
17
17
  import { gsdRoot, resolveMilestoneFile } from "./paths.js";
@@ -69,8 +69,13 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
69
69
  ctx.ui.notify(`GSD_PROJECT_ID must contain only alphanumeric characters, hyphens, and underscores. Got: "${customProjectId}"`, "error");
70
70
  return releaseLockAndReturn();
71
71
  }
72
- // Ensure git repo exists
73
- if (!nativeIsRepo(base)) {
72
+ // Ensure git repo exists.
73
+ // Guard against inherited repos: if `base` is a subdirectory of another
74
+ // git repo that has no .gsd (i.e. the parent project was never initialised
75
+ // with GSD), create a fresh git repo at `base` so it gets its own identity
76
+ // hash. Without this, repoIdentity() resolves to the parent repo's hash
77
+ // and loads milestones from an unrelated project (#1639).
78
+ if (!nativeIsRepo(base) || isInheritedRepo(base)) {
74
79
  const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
75
80
  nativeInit(base, mainBranch);
76
81
  }
@@ -5,21 +5,21 @@
5
5
  * manual `/worktree` which uses `worktree/<name>` branches). This module
6
6
  * manages create, enter, detect, and teardown for auto-mode worktrees.
7
7
  */
8
- import { existsSync, cpSync, readFileSync, readdirSync, mkdirSync, realpathSync, unlinkSync, lstatSync as lstatSyncFn, } from "node:fs";
8
+ import { existsSync, cpSync, readFileSync, readdirSync, mkdirSync, realpathSync, rmSync, unlinkSync, lstatSync as lstatSyncFn, } from "node:fs";
9
9
  import { isAbsolute, join } from "node:path";
10
10
  import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
11
11
  import { copyWorktreeDb, reconcileWorktreeDb, isDbAvailable, } from "./gsd-db.js";
12
12
  import { atomicWriteSync } from "./atomic-write.js";
13
- import { execSync, execFileSync } from "node:child_process";
13
+ import { execFileSync } from "node:child_process";
14
14
  import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
15
15
  import { gsdRoot } from "./paths.js";
16
- import { createWorktree, removeWorktree, worktreePath, } from "./worktree-manager.js";
16
+ import { createWorktree, removeWorktree, resolveGitDir, worktreePath, } from "./worktree-manager.js";
17
17
  import { detectWorktreeName, nudgeGitBranchCache, } from "./worktree.js";
18
18
  import { MergeConflictError, readIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
19
19
  import { debugLog } from "./debug-logger.js";
20
20
  import { parseRoadmap } from "./files.js";
21
21
  import { loadEffectiveGSDPreferences } from "./preferences.js";
22
- import { nativeGetCurrentBranch, nativeDetectMainBranch, nativeWorkingTreeStatus, nativeAddAllWithExclusions, nativeCommit, nativeCheckoutBranch, nativeMergeSquash, nativeConflictFiles, nativeCheckoutTheirs, nativeAddPaths, nativeRmForce, nativeBranchDelete, nativeBranchExists, nativeDiffNumstat, } from "./native-git-bridge.js";
22
+ import { nativeGetCurrentBranch, nativeDetectMainBranch, nativeWorkingTreeStatus, nativeAddAllWithExclusions, nativeCommit, nativeCheckoutBranch, nativeMergeSquash, nativeConflictFiles, nativeCheckoutTheirs, nativeAddPaths, nativeRmForce, nativeBranchDelete, nativeBranchExists, nativeDiffNumstat, nativeUpdateRef, nativeIsAncestor, } from "./native-git-bridge.js";
23
23
  // ─── Module State ──────────────────────────────────────────────────────────
24
24
  /** Original project root before chdir into auto-worktree. */
25
25
  let originalBase = null;
@@ -133,7 +133,7 @@ export function syncGsdStateToWorktree(mainBasePath, worktreePath_) {
133
133
  const mainMilestones = readdirSync(mainMilestonesDir, {
134
134
  withFileTypes: true,
135
135
  })
136
- .filter((d) => d.isDirectory() && /^M\d{3}/.test(d.name))
136
+ .filter((d) => d.isDirectory())
137
137
  .map((d) => d.name);
138
138
  for (const mid of mainMilestones) {
139
139
  const srcDir = join(mainMilestonesDir, mid);
@@ -287,7 +287,7 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
287
287
  return { synced };
288
288
  try {
289
289
  const wtMilestones = readdirSync(wtMilestonesDir, { withFileTypes: true })
290
- .filter((d) => d.isDirectory() && /^M\d{3}/.test(d.name))
290
+ .filter((d) => d.isDirectory())
291
291
  .map((d) => d.name);
292
292
  for (const mid of wtMilestones) {
293
293
  syncMilestoneDir(wtGsd, mainGsd, mid, synced);
@@ -405,13 +405,24 @@ export function runWorktreePostCreateHook(sourceDir, worktreeDir, hookPath) {
405
405
  }
406
406
  if (!hookPath)
407
407
  return null;
408
- // Resolve relative paths against the source project root
409
- const resolved = isAbsolute(hookPath) ? hookPath : join(sourceDir, hookPath);
408
+ // Resolve relative paths against the source project root.
409
+ // On Windows, convert 8.3 short paths (e.g. RUNNER~1) to long paths
410
+ // so execFileSync can locate the file correctly.
411
+ let resolved = isAbsolute(hookPath) ? hookPath : join(sourceDir, hookPath);
410
412
  if (!existsSync(resolved)) {
411
413
  return `Worktree post-create hook not found: ${resolved}`;
412
414
  }
415
+ if (process.platform === "win32") {
416
+ try {
417
+ resolved = realpathSync.native(resolved);
418
+ }
419
+ catch { /* keep original */ }
420
+ }
413
421
  try {
414
- execSync(resolved, {
422
+ // .bat/.cmd files on Windows require shell mode — execFileSync cannot
423
+ // spawn them directly (EINVAL).
424
+ const needsShell = process.platform === "win32" && /\.(bat|cmd)$/i.test(resolved);
425
+ execFileSync(resolved, [], {
415
426
  cwd: worktreeDir,
416
427
  env: {
417
428
  ...process.env,
@@ -421,6 +432,7 @@ export function runWorktreePostCreateHook(sourceDir, worktreeDir, hookPath) {
421
432
  stdio: ["ignore", "pipe", "pipe"],
422
433
  encoding: "utf-8",
423
434
  timeout: 30_000, // 30 second timeout
435
+ shell: needsShell,
424
436
  });
425
437
  return null;
426
438
  }
@@ -661,6 +673,22 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
661
673
  branch,
662
674
  deleteBranch: !preserveBranch,
663
675
  });
676
+ // Verify cleanup succeeded — warn if the worktree directory is still on disk.
677
+ // On Windows, bash-based cleanup can silently fail when paths contain
678
+ // backslashes (#1436), leaving ~1 GB+ orphaned directories.
679
+ const wtDir = worktreePath(originalBasePath, milestoneId);
680
+ if (existsSync(wtDir)) {
681
+ console.error(`[GSD] WARNING: Worktree directory still exists after teardown: ${wtDir}\n` +
682
+ ` This is likely an orphaned directory consuming disk space.\n` +
683
+ ` Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`);
684
+ // Attempt a direct filesystem removal as a fallback
685
+ try {
686
+ rmSync(wtDir, { recursive: true, force: true });
687
+ }
688
+ catch {
689
+ // Non-fatal — the warning above tells the user how to clean up
690
+ }
691
+ }
664
692
  }
665
693
  /**
666
694
  * Detect if the process is currently inside an auto-worktree.
@@ -858,6 +886,56 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
858
886
  body = `\n\nCompleted slices:\n${sliceLines}\n\nBranch: ${milestoneBranch}`;
859
887
  }
860
888
  const commitMessage = subject + body;
889
+ // 6b. Reconcile worktree HEAD with milestone branch ref (#1846).
890
+ // When the worktree HEAD detaches and advances past the named branch,
891
+ // the branch ref becomes stale. Squash-merging the stale ref silently
892
+ // orphans all commits between the branch ref and the actual worktree HEAD.
893
+ // Fix: fast-forward the branch ref to the worktree HEAD before merging.
894
+ // Only applies when merging from an actual worktree (worktreeCwd differs
895
+ // from originalBasePath_).
896
+ if (worktreeCwd !== originalBasePath_) {
897
+ try {
898
+ const worktreeHead = execFileSync("git", ["rev-parse", "HEAD"], {
899
+ cwd: worktreeCwd,
900
+ stdio: ["ignore", "pipe", "pipe"],
901
+ encoding: "utf-8",
902
+ }).trim();
903
+ const branchHead = execFileSync("git", ["rev-parse", milestoneBranch], {
904
+ cwd: originalBasePath_,
905
+ stdio: ["ignore", "pipe", "pipe"],
906
+ encoding: "utf-8",
907
+ }).trim();
908
+ if (worktreeHead && branchHead && worktreeHead !== branchHead) {
909
+ if (nativeIsAncestor(originalBasePath_, branchHead, worktreeHead)) {
910
+ // Worktree HEAD is strictly ahead — fast-forward the branch ref
911
+ nativeUpdateRef(originalBasePath_, `refs/heads/${milestoneBranch}`, worktreeHead);
912
+ debugLog("mergeMilestoneToMain", {
913
+ action: "fast-forward-branch-ref",
914
+ milestoneBranch,
915
+ oldRef: branchHead.slice(0, 8),
916
+ newRef: worktreeHead.slice(0, 8),
917
+ });
918
+ }
919
+ else {
920
+ // Diverged — fail loudly rather than silently losing commits
921
+ process.chdir(previousCwd);
922
+ throw new GSDError(GSD_GIT_ERROR, `Worktree HEAD (${worktreeHead.slice(0, 8)}) diverged from ` +
923
+ `${milestoneBranch} (${branchHead.slice(0, 8)}). ` +
924
+ `Manual reconciliation required before merge.`);
925
+ }
926
+ }
927
+ }
928
+ catch (err) {
929
+ // Re-throw GSDError (divergence); swallow rev-parse failures
930
+ // (e.g. worktree dir already removed by external cleanup)
931
+ if (err instanceof GSDError)
932
+ throw err;
933
+ debugLog("mergeMilestoneToMain", {
934
+ action: "reconcile-skipped",
935
+ reason: String(err),
936
+ });
937
+ }
938
+ }
861
939
  // 7. Squash merge — auto-resolve .gsd/ state file conflicts (#530)
862
940
  const mergeResult = nativeMergeSquash(originalBasePath_, milestoneBranch);
863
941
  if (!mergeResult.success) {
@@ -905,6 +983,17 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
905
983
  // 8. Commit (handle nothing-to-commit gracefully)
906
984
  const commitResult = nativeCommit(originalBasePath_, commitMessage);
907
985
  const nothingToCommit = commitResult === null;
986
+ // 8a. Clean up SQUASH_MSG left by git merge --squash (#1853).
987
+ // git only removes SQUASH_MSG when the commit reads it directly (plain
988
+ // `git commit`). nativeCommit uses `-F -` (stdin) or libgit2, neither
989
+ // of which trigger git's SQUASH_MSG cleanup. If left on disk, doctor
990
+ // reports `corrupt_merge_state` on every subsequent run.
991
+ try {
992
+ const squashMsgPath = join(resolveGitDir(originalBasePath_), "SQUASH_MSG");
993
+ if (existsSync(squashMsgPath))
994
+ unlinkSync(squashMsgPath);
995
+ }
996
+ catch { /* best-effort */ }
908
997
  // 8b. Safety check (#1792): if nothing was committed, verify the milestone
909
998
  // work is already on the integration branch before allowing teardown.
910
999
  // Compare only non-.gsd/ paths — .gsd/ state files diverge normally and
@@ -920,12 +1009,27 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
920
1009
  `Aborting worktree teardown to prevent data loss.`);
921
1010
  }
922
1011
  }
1012
+ // 8c. Detect whether any non-.gsd/ code files were actually merged (#1906).
1013
+ // When a milestone only produced .gsd/ metadata (summaries, roadmaps) but no
1014
+ // real code, the user sees "milestone complete" but nothing changed in their
1015
+ // codebase. Surface this so the caller can warn the user.
1016
+ let codeFilesChanged = false;
1017
+ if (!nothingToCommit) {
1018
+ try {
1019
+ const mergedFiles = nativeDiffNumstat(originalBasePath_, "HEAD~1", "HEAD");
1020
+ codeFilesChanged = mergedFiles.some((entry) => !entry.path.startsWith(".gsd/"));
1021
+ }
1022
+ catch {
1023
+ // If HEAD~1 doesn't exist (first commit), assume code was changed
1024
+ codeFilesChanged = true;
1025
+ }
1026
+ }
923
1027
  // 9. Auto-push if enabled
924
1028
  let pushed = false;
925
1029
  if (prefs.auto_push === true && !nothingToCommit) {
926
1030
  const remote = prefs.remote ?? "origin";
927
1031
  try {
928
- execSync(`git push ${remote} ${mainBranch}`, {
1032
+ execFileSync("git", ["push", remote, mainBranch], {
929
1033
  cwd: originalBasePath_,
930
1034
  stdio: ["ignore", "pipe", "pipe"],
931
1035
  encoding: "utf-8",
@@ -943,13 +1047,19 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
943
1047
  const prTarget = prefs.pr_target_branch ?? mainBranch;
944
1048
  try {
945
1049
  // Push the milestone branch to remote first
946
- execSync(`git push ${remote} ${milestoneBranch}`, {
1050
+ execFileSync("git", ["push", remote, milestoneBranch], {
947
1051
  cwd: originalBasePath_,
948
1052
  stdio: ["ignore", "pipe", "pipe"],
949
1053
  encoding: "utf-8",
950
1054
  });
951
1055
  // Create PR via gh CLI
952
- execSync(`gh pr create --base "${prTarget}" --head "${milestoneBranch}" --title "Milestone ${milestoneId} complete" --body "Auto-created by GSD on milestone completion."`, {
1056
+ execFileSync("gh", [
1057
+ "pr", "create",
1058
+ "--base", prTarget,
1059
+ "--head", milestoneBranch,
1060
+ "--title", `Milestone ${milestoneId} complete`,
1061
+ "--body", "Auto-created by GSD on milestone completion.",
1062
+ ], {
953
1063
  cwd: originalBasePath_,
954
1064
  stdio: ["ignore", "pipe", "pipe"],
955
1065
  encoding: "utf-8",
@@ -963,6 +1073,30 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
963
1073
  // 10. Guard removed — step 8b (#1792) now handles this with a smarter check:
964
1074
  // throws only when the milestone has unanchored code changes, passes
965
1075
  // through when the code is genuinely already on the integration branch.
1076
+ // 10a. Pre-teardown safety net (#1853): if the worktree still has uncommitted
1077
+ // changes (e.g. nativeHasChanges cache returned stale false, or auto-commit
1078
+ // silently failed), force one final commit so code is not destroyed by
1079
+ // `git worktree remove --force`.
1080
+ if (existsSync(worktreeCwd)) {
1081
+ try {
1082
+ const dirtyCheck = nativeWorkingTreeStatus(worktreeCwd);
1083
+ if (dirtyCheck) {
1084
+ debugLog("mergeMilestoneToMain", {
1085
+ phase: "pre-teardown-dirty",
1086
+ worktreeCwd,
1087
+ status: dirtyCheck.slice(0, 200),
1088
+ });
1089
+ nativeAddAllWithExclusions(worktreeCwd, RUNTIME_EXCLUSION_PATHS);
1090
+ nativeCommit(worktreeCwd, "chore: pre-teardown auto-commit of uncommitted worktree changes");
1091
+ }
1092
+ }
1093
+ catch (e) {
1094
+ debugLog("mergeMilestoneToMain", {
1095
+ phase: "pre-teardown-commit-error",
1096
+ error: String(e),
1097
+ });
1098
+ }
1099
+ }
966
1100
  // 11. Remove worktree directory first (must happen before branch deletion)
967
1101
  try {
968
1102
  removeWorktree(originalBasePath_, milestoneId, {
@@ -983,5 +1117,5 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
983
1117
  // 13. Clear module state
984
1118
  originalBase = null;
985
1119
  nudgeGitBranchCache(previousCwd);
986
- return { commitMessage, pushed, prCreated };
1120
+ return { commitMessage, pushed, prCreated, codeFilesChanged };
987
1121
  }