gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.84a383f51

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 (383) hide show
  1. package/README.md +7 -7
  2. package/dist/claude-cli-check.js +64 -37
  3. package/dist/cli-policy.d.ts +13 -0
  4. package/dist/cli-policy.js +17 -0
  5. package/dist/cli.js +95 -55
  6. package/dist/headless-query.d.ts +22 -0
  7. package/dist/headless-query.js +24 -4
  8. package/dist/headless.d.ts +10 -0
  9. package/dist/headless.js +16 -1
  10. package/dist/loader.js +7 -10
  11. package/dist/onboarding.d.ts +10 -0
  12. package/dist/onboarding.js +2 -2
  13. package/dist/provider-migrations.d.ts +2 -2
  14. package/dist/provider-migrations.js +5 -2
  15. package/dist/resource-loader.d.ts +5 -2
  16. package/dist/resource-loader.js +28 -5
  17. package/dist/resources/.managed-resources-content-hash +1 -0
  18. package/dist/resources/extensions/claude-code-cli/readiness.js +77 -45
  19. package/dist/resources/extensions/gsd/auto/loop.js +23 -0
  20. package/dist/resources/extensions/gsd/auto/phases.js +2 -2
  21. package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
  22. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  23. package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
  24. package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
  25. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
  27. package/dist/resources/extensions/gsd/auto-worktree.js +30 -0
  28. package/dist/resources/extensions/gsd/auto.js +14 -5
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
  30. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
  33. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +94 -31
  34. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
  35. package/dist/resources/extensions/gsd/bootstrap/system-context.js +34 -8
  36. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -2
  37. package/dist/resources/extensions/gsd/commands/catalog.js +69 -5
  38. package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
  39. package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
  40. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
  41. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  42. package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -0
  43. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
  44. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  45. package/dist/resources/extensions/gsd/forensics.js +2 -2
  46. package/dist/resources/extensions/gsd/git-service.js +12 -5
  47. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  48. package/dist/resources/extensions/gsd/guided-flow.js +23 -23
  49. package/dist/resources/extensions/gsd/memory-store.js +66 -31
  50. package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
  51. package/dist/resources/extensions/gsd/model-router.js +114 -9
  52. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
  53. package/dist/resources/extensions/gsd/preferences-models.js +91 -15
  54. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  55. package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
  56. package/dist/resources/extensions/gsd/preferences.js +5 -3
  57. package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
  58. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
  59. package/dist/resources/extensions/gsd/state.js +42 -0
  60. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  61. package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
  62. package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
  63. package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
  64. package/dist/resources/extensions/gsd/worktree-command.js +26 -46
  65. package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
  66. package/dist/resources/extensions/mcp-client/index.js +6 -3
  67. package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
  68. package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
  69. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  70. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  71. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  72. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  73. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  74. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  75. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  76. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  77. package/dist/rtk-shared.d.ts +3 -0
  78. package/dist/rtk-shared.js +17 -0
  79. package/dist/rtk.d.ts +2 -5
  80. package/dist/rtk.js +3 -20
  81. package/dist/runtime-checks.d.ts +27 -0
  82. package/dist/runtime-checks.js +38 -0
  83. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  84. package/dist/web/standalone/.next/BUILD_ID +1 -1
  85. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  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 +44 -4
  89. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
  110. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/index.html +1 -1
  112. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  113. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  114. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  115. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  120. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  122. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  124. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  125. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  126. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  127. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
  128. package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
  129. package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
  130. package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
  131. package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
  132. package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
  133. package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
  134. package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
  135. package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
  136. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
  137. package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
  138. package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
  139. package/dist/web/standalone/package.json +2 -1
  140. package/dist/worktree-status-banner.d.ts +1 -0
  141. package/dist/worktree-status-banner.js +132 -0
  142. package/package.json +1 -1
  143. package/packages/daemon/package.json +2 -2
  144. package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
  145. package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
  146. package/packages/mcp-server/dist/alias-telemetry.js +30 -0
  147. package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
  148. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/workflow-tools.js +74 -46
  150. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  151. package/packages/mcp-server/package.json +2 -2
  152. package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
  153. package/packages/mcp-server/src/alias-telemetry.ts +30 -0
  154. package/packages/mcp-server/src/workflow-tools.test.ts +26 -0
  155. package/packages/mcp-server/src/workflow-tools.ts +93 -58
  156. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  157. package/packages/native/package.json +1 -1
  158. package/packages/pi-agent-core/package.json +1 -1
  159. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  160. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
  161. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
  162. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
  163. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
  164. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  165. package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
  166. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  167. package/packages/pi-ai/dist/types.d.ts +13 -0
  168. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  169. package/packages/pi-ai/dist/types.js.map +1 -1
  170. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  171. package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
  172. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  173. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
  174. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  175. package/packages/pi-ai/package.json +1 -1
  176. package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
  177. package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
  178. package/packages/pi-ai/src/types.ts +13 -0
  179. package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
  180. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
  181. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  182. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
  184. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/messages.js +4 -0
  187. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  189. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
  191. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
  193. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
  195. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
  197. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
  199. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
  201. package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
  202. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
  211. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
  212. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
  213. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
  214. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
  217. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
  218. package/packages/pi-coding-agent/package.json +1 -1
  219. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
  220. package/packages/pi-coding-agent/src/core/messages.ts +4 -0
  221. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
  222. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
  223. package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
  224. package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
  225. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
  226. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
  227. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
  228. package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
  229. package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
  230. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  231. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
  232. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  233. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
  234. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
  235. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
  236. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
  237. package/packages/pi-tui/package.json +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
  239. package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
  240. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  241. package/packages/rpc-client/package.json +1 -1
  242. package/pkg/package.json +1 -1
  243. package/src/resources/extensions/claude-code-cli/readiness.ts +78 -46
  244. package/src/resources/extensions/gsd/auto/loop.ts +24 -2
  245. package/src/resources/extensions/gsd/auto/phases.ts +3 -3
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
  247. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  248. package/src/resources/extensions/gsd/auto/types.ts +1 -0
  249. package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
  250. package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
  251. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  252. package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
  253. package/src/resources/extensions/gsd/auto-worktree.ts +38 -0
  254. package/src/resources/extensions/gsd/auto.ts +14 -4
  255. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
  256. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
  257. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  258. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
  259. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +102 -31
  260. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
  261. package/src/resources/extensions/gsd/bootstrap/system-context.ts +39 -8
  262. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +39 -11
  263. package/src/resources/extensions/gsd/commands/catalog.ts +75 -5
  264. package/src/resources/extensions/gsd/commands/handlers/core.ts +22 -1
  265. package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
  266. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
  267. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  268. package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -0
  269. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
  270. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  271. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  272. package/src/resources/extensions/gsd/forensics.ts +2 -2
  273. package/src/resources/extensions/gsd/git-service.ts +13 -5
  274. package/src/resources/extensions/gsd/gsd-db.ts +12 -2
  275. package/src/resources/extensions/gsd/guided-flow.ts +25 -25
  276. package/src/resources/extensions/gsd/memory-store.ts +81 -28
  277. package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
  278. package/src/resources/extensions/gsd/model-router.ts +172 -9
  279. package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
  280. package/src/resources/extensions/gsd/preferences-models.ts +101 -15
  281. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  282. package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
  283. package/src/resources/extensions/gsd/preferences.ts +16 -2
  284. package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
  285. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
  286. package/src/resources/extensions/gsd/state.ts +42 -0
  287. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  288. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
  289. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
  290. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +9 -5
  291. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
  292. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
  293. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
  294. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
  295. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
  296. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
  297. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
  298. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
  299. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
  300. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
  301. package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
  302. package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
  303. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
  304. package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
  305. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
  306. package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
  307. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
  308. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
  309. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
  310. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
  311. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
  312. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
  313. package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
  314. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
  315. package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
  316. package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
  317. package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
  318. package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
  319. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -0
  320. package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
  321. package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
  322. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
  323. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
  324. package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
  325. package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
  326. package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
  327. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
  328. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
  329. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
  330. package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
  331. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
  332. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
  334. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
  335. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
  336. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
  337. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
  338. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
  339. package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
  340. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
  341. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
  342. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
  343. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
  344. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  345. package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
  346. package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
  347. package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
  348. package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
  349. package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
  350. package/src/resources/extensions/gsd/worktree-command.ts +31 -44
  351. package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
  352. package/src/resources/extensions/mcp-client/index.ts +6 -3
  353. package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
  354. package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
  355. package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
  356. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  357. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  358. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  359. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  360. package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  361. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  362. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  363. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
  364. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
  365. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
  366. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  367. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
  368. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
  369. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
  370. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
  371. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
  372. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
  373. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
  374. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  375. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
  376. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
  377. package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
  378. package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
  379. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
  380. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
  381. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
  382. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_buildManifest.js +0 -0
  383. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_ssgManifest.js +0 -0
@@ -54,7 +54,7 @@ const API_KEY_PREFIXES = {
54
54
  anthropic: ['sk-ant-'],
55
55
  openai: ['sk-'],
56
56
  };
57
- const OTHER_PROVIDERS = [
57
+ export const OTHER_PROVIDERS = [
58
58
  { value: 'google', label: 'Google (Gemini)', hint: 'aistudio.google.com/app/apikey' },
59
59
  { value: 'groq', label: 'Groq', hint: 'console.groq.com/keys' },
60
60
  { value: 'xai', label: 'xAI (Grok)', hint: 'console.x.ai' },
@@ -148,7 +148,7 @@ function persistDefaultModel(modelId) {
148
148
  // Non-fatal: startup fallback logic will still run.
149
149
  }
150
150
  }
151
- function detectNativeProviderFromBaseUrl(baseUrl) {
151
+ export function detectNativeProviderFromBaseUrl(baseUrl) {
152
152
  try {
153
153
  const hostname = new URL(baseUrl).hostname.toLowerCase();
154
154
  if (hostname === 'api.minimax.io' || hostname.endsWith('.minimax.io')) {
@@ -1,7 +1,7 @@
1
1
  import type { AuthStorage } from "@gsd/pi-coding-agent";
2
2
  type AnthropicMigrationDeps = {
3
3
  authStorage: Pick<AuthStorage, "getCredentialsForProvider">;
4
- isClaudeCodeReady: boolean;
4
+ isClaudeCodeReady: boolean | (() => boolean);
5
5
  defaultProvider: string | undefined;
6
6
  env?: NodeJS.ProcessEnv;
7
7
  };
@@ -11,7 +11,7 @@ type MigrationModel = {
11
11
  };
12
12
  type AnthropicDefaultMigrationDeps = {
13
13
  authStorage: Pick<AuthStorage, "getCredentialsForProvider">;
14
- isClaudeCodeReady: boolean;
14
+ isClaudeCodeReady: boolean | (() => boolean);
15
15
  settingsManager: {
16
16
  getDefaultProvider(): string | undefined;
17
17
  getDefaultModel(): string | undefined;
@@ -5,10 +5,13 @@ export function hasDirectAnthropicApiKey(authStorage, env = process.env) {
5
5
  return authStorage.getCredentialsForProvider("anthropic").some((credential) => credential?.type === "api_key" && typeof credential?.key === "string" && credential.key.trim().length > 0);
6
6
  }
7
7
  export function shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeReady, defaultProvider, env = process.env, }) {
8
- if (!isClaudeCodeReady || defaultProvider !== "anthropic") {
8
+ if (defaultProvider !== "anthropic") {
9
9
  return false;
10
10
  }
11
- return !hasDirectAnthropicApiKey(authStorage, env);
11
+ if (hasDirectAnthropicApiKey(authStorage, env)) {
12
+ return false;
13
+ }
14
+ return typeof isClaudeCodeReady === "function" ? isClaudeCodeReady() : isClaudeCodeReady;
12
15
  }
13
16
  export function migrateAnthropicDefaultToClaudeCode({ authStorage, isClaudeCodeReady, settingsManager, modelRegistry, env = process.env, }) {
14
17
  const defaultProvider = settingsManager.getDefaultProvider();
@@ -1,4 +1,4 @@
1
- import { DefaultResourceLoader } from '@gsd/pi-coding-agent';
1
+ import type { DefaultResourceLoader as DefaultResourceLoaderType } from '@gsd/pi-coding-agent';
2
2
  export { discoverExtensionEntryPaths } from './extension-discovery.js';
3
3
  export declare function getExtensionKey(entryPath: string, extensionsDir: string): string;
4
4
  export declare function readManagedResourceVersion(agentDir: string): string | null;
@@ -63,4 +63,7 @@ export declare function mergedFingerprint(hoisted: string, internal: string): st
63
63
  */
64
64
  export declare function initResources(agentDir: string, skillsDir?: string): void;
65
65
  export declare function hasStaleCompiledExtensionSiblings(extensionsDir: string, sourceDir?: string): boolean;
66
- export declare function buildResourceLoader(agentDir: string): DefaultResourceLoader;
66
+ interface BuildResourceLoaderOptions {
67
+ additionalExtensionPaths?: string[];
68
+ }
69
+ export declare function buildResourceLoader(agentDir: string, options?: BuildResourceLoaderOptions): Promise<DefaultResourceLoaderType>;
@@ -1,4 +1,3 @@
1
- import { DefaultResourceLoader, sortExtensionPaths } from '@gsd/pi-coding-agent';
2
1
  import { createHash } from 'node:crypto';
3
2
  import { homedir } from 'node:os';
4
3
  import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, openSync, closeSync, readFileSync, readlinkSync, readdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
@@ -7,6 +6,10 @@ import { fileURLToPath } from 'node:url';
7
6
  import { compareSemver } from './update-check.js';
8
7
  import { discoverExtensionEntryPaths } from './extension-discovery.js';
9
8
  import { loadRegistry, readManifestFromEntryPath, isExtensionEnabled, ensureRegistryEntries } from './extension-registry.js';
9
+ let piCodingAgentModulePromise;
10
+ function loadPiCodingAgentModule() {
11
+ return (piCodingAgentModulePromise ??= import('@gsd/pi-coding-agent'));
12
+ }
10
13
  // Resolve resources directory — prefer dist/resources/ (stable, set at build time)
11
14
  // over src/resources/ (live working tree, changes with git branch).
12
15
  //
@@ -26,6 +29,7 @@ const resourcesDir = (existsSync(distResources) && existsSync(join(distResources
26
29
  : srcResources;
27
30
  const bundledExtensionsDir = join(resourcesDir, 'extensions');
28
31
  const resourceVersionManifestName = 'managed-resources.json';
32
+ const resourceFingerprintFileName = '.managed-resources-content-hash';
29
33
  export { discoverExtensionEntryPaths } from './extension-discovery.js';
30
34
  export function getExtensionKey(entryPath, extensionsDir) {
31
35
  const relPath = relative(extensionsDir, entryPath);
@@ -77,7 +81,7 @@ function writeManagedResourceManifest(agentDir) {
77
81
  const manifest = {
78
82
  gsdVersion: getBundledGsdVersion(),
79
83
  syncedAt: Date.now(),
80
- contentHash: computeResourceFingerprint(),
84
+ contentHash: getCurrentResourceFingerprint(),
81
85
  installedExtensionRootFiles,
82
86
  installedExtensionDirs,
83
87
  };
@@ -125,10 +129,24 @@ export function computeResourceFingerprint(rootDir = resourcesDir) {
125
129
  entries.sort();
126
130
  return createHash('sha256').update(entries.join('\n')).digest('hex').slice(0, 16);
127
131
  }
132
+ function getCurrentResourceFingerprint() {
133
+ try {
134
+ const precomputed = readFileSync(join(resourcesDir, resourceFingerprintFileName), 'utf-8').trim();
135
+ if (/^[a-f0-9]{16}$/i.test(precomputed)) {
136
+ return precomputed;
137
+ }
138
+ }
139
+ catch {
140
+ // Source-tree and partial-build workflows may not have a precomputed hash.
141
+ }
142
+ return computeResourceFingerprint();
143
+ }
128
144
  function collectFileEntries(dir, root, out) {
129
145
  if (!existsSync(dir))
130
146
  return;
131
147
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
148
+ if (entry.name === resourceFingerprintFileName)
149
+ continue;
132
150
  const fullPath = join(dir, entry.name);
133
151
  if (entry.isDirectory()) {
134
152
  collectFileEntries(fullPath, root, out);
@@ -548,7 +566,7 @@ export function initResources(agentDir, skillsDir = join(homedir(), '.agents', '
548
566
  // hotfixes within a release). The content hash catches those at ~1ms cost.
549
567
  if (manifest && manifest.gsdVersion === currentVersion) {
550
568
  // Version matches — check content fingerprint for same-version staleness.
551
- const currentHash = computeResourceFingerprint();
569
+ const currentHash = getCurrentResourceFingerprint();
552
570
  const hasStaleExtensionFiles = hasStaleCompiledExtensionSiblings(extensionsDir, bundledExtensionsDir);
553
571
  if (manifest.contentHash && manifest.contentHash === currentHash && !hasStaleExtensionFiles) {
554
572
  return;
@@ -736,7 +754,8 @@ function getBundledExtensionKeys() {
736
754
  }
737
755
  return _bundledExtensionKeys;
738
756
  }
739
- export function buildResourceLoader(agentDir) {
757
+ export async function buildResourceLoader(agentDir, options = {}) {
758
+ const { DefaultResourceLoader, sortExtensionPaths } = await loadPiCodingAgentModule();
740
759
  const registry = loadRegistry();
741
760
  const piAgentDir = join(homedir(), '.pi', 'agent');
742
761
  const piExtensionsDir = join(piAgentDir, 'extensions');
@@ -749,9 +768,13 @@ export function buildResourceLoader(agentDir) {
749
768
  return true;
750
769
  return isExtensionEnabled(registry, manifest.id);
751
770
  });
771
+ const additionalExtensionPaths = [
772
+ ...piExtensionPaths,
773
+ ...(options.additionalExtensionPaths ?? []),
774
+ ];
752
775
  return new DefaultResourceLoader({
753
776
  agentDir,
754
- additionalExtensionPaths: piExtensionPaths,
777
+ additionalExtensionPaths,
755
778
  bundledExtensionKeys: bundledKeys,
756
779
  extensionPathsTransform: (paths) => {
757
780
  // 1. Filter community extensions through the GSD registry
@@ -0,0 +1 @@
1
+ afb1073f61989a78
@@ -6,8 +6,12 @@
6
6
  * model-availability check.
7
7
  *
8
8
  * Auth verification runs `claude auth status --json` and inspects the
9
- * `loggedIn` field, falling back to a text heuristic when the JSON shape
10
- * is unavailable (older Claude CLI builds).
9
+ * `loggedIn` field, falling back to plain `claude auth status` and a text
10
+ * heuristic when the JSON shape is unavailable (older Claude CLI builds).
11
+ *
12
+ * Set GSD_CLAUDE_DEBUG=1 to print the probe's binary selection and auth
13
+ * outputs to stderr — useful when diagnosing platform-specific detection
14
+ * failures (Issue #4997).
11
15
  */
12
16
  import { execFileSync } from "node:child_process";
13
17
  /**
@@ -24,42 +28,45 @@ const CLAUDE_COMMAND = process.platform === "win32" ? "claude.cmd" : "claude";
24
28
  * installed" results in readiness checks.
25
29
  */
26
30
  const CLAUDE_COMMAND_CANDIDATES = process.platform === "win32" ? [CLAUDE_COMMAND, "claude.exe", "claude"] : [CLAUDE_COMMAND];
27
- // Codes treated as "this candidate didn't run — try the next one" rather than
28
- // fatal failures. ENOENT/EINVAL cover the original Windows .cmd shim cases.
29
- // ETIMEDOUT and EAGAIN cover slow-spawn cases where cmd.exe wrapping plus
30
- // the Claude CLI startup path together exceed the per-attempt timeout
31
- // (Issue #4997 regression on Windows + Node 25).
32
- const SOFT_FAIL_CODES = new Set(["ENOENT", "EINVAL", "ETIMEDOUT", "EAGAIN"]);
33
31
  // Keep the version probe snappy — `claude --version` is a quick path.
34
32
  const VERSION_TIMEOUT_MS = 5_000;
35
33
  // Auth status can be much slower on Windows because the spawn goes through
36
34
  // cmd.exe → claude.cmd → node → Claude CLI. 15s leaves headroom on cold spawns
37
35
  // without making startup feel hung when the CLI is genuinely missing.
38
36
  const AUTH_TIMEOUT_MS = 15_000;
37
+ function debugLog(...parts) {
38
+ if (process.env.GSD_CLAUDE_DEBUG) {
39
+ process.stderr.write(`[claude-readiness] ${parts.map((p) => (typeof p === "string" ? p : JSON.stringify(p))).join(" ")}\n`);
40
+ }
41
+ }
39
42
  /**
40
- * Run the requested Claude CLI command against each supported executable name.
41
- * Returns the first successful output buffer and rethrows hard failures.
43
+ * Find the first candidate that responds to `--version`. Returns the
44
+ * candidate name on success, null if none worked.
45
+ *
46
+ * On Windows with `shell: true`, a missing candidate surfaces as a
47
+ * non-zero exit from cmd.exe rather than ENOENT — so we cannot rely on
48
+ * the error code to decide "try next". Treat any failure as "try next"
49
+ * for the version probe; the only thing that matters for binary
50
+ * detection is whether *some* candidate produces a `claude --version`
51
+ * line.
42
52
  */
43
- function execClaude(args, timeoutMs) {
44
- let lastError;
53
+ function findWorkingCommand() {
45
54
  for (const command of CLAUDE_COMMAND_CANDIDATES) {
46
55
  try {
47
- return execFileSync(command, args, {
48
- timeout: timeoutMs,
56
+ execFileSync(command, ["--version"], {
57
+ timeout: VERSION_TIMEOUT_MS,
49
58
  stdio: "pipe",
50
59
  shell: process.platform === "win32",
51
60
  });
61
+ debugLog("version probe ok via", command);
62
+ return command;
52
63
  }
53
64
  catch (error) {
54
- lastError = error;
55
- const code = error?.code;
56
- if (code && SOFT_FAIL_CODES.has(code)) {
57
- continue;
58
- }
59
- throw error;
65
+ debugLog("version probe failed for", command, "code=", error?.code);
66
+ continue;
60
67
  }
61
68
  }
62
- throw lastError ?? new Error(`Claude CLI executable not found (tried: ${CLAUDE_COMMAND_CANDIDATES.join(", ")})`);
69
+ return null;
63
70
  }
64
71
  /**
65
72
  * Decide auth state from `claude auth status` output.
@@ -71,6 +78,8 @@ function execClaude(args, timeoutMs) {
71
78
  */
72
79
  function parseAuthStatus(output) {
73
80
  const trimmed = output.trim();
81
+ if (!trimmed)
82
+ return null;
74
83
  if (trimmed.startsWith("{")) {
75
84
  try {
76
85
  const parsed = JSON.parse(trimmed);
@@ -86,8 +95,41 @@ function parseAuthStatus(output) {
86
95
  if (/not logged in|no credentials|unauthenticated|not authenticated/.test(lower)) {
87
96
  return false;
88
97
  }
89
- // Exit-0 with non-error output and no negative markers — treat as authed.
90
- return true;
98
+ if (/logged in|authenticated|signed in|email|subscription/.test(lower)) {
99
+ return true;
100
+ }
101
+ return null;
102
+ }
103
+ function probeAuth(command) {
104
+ // Try --json first (newer CLIs).
105
+ try {
106
+ const out = execFileSync(command, ["auth", "status", "--json"], {
107
+ timeout: AUTH_TIMEOUT_MS,
108
+ stdio: "pipe",
109
+ shell: process.platform === "win32",
110
+ }).toString();
111
+ debugLog("auth status --json output:", out.slice(0, 200));
112
+ const parsed = parseAuthStatus(out);
113
+ if (parsed !== null)
114
+ return parsed;
115
+ }
116
+ catch (error) {
117
+ debugLog("auth status --json threw:", error.message?.slice(0, 200));
118
+ }
119
+ // Fallback: plain `auth status` (older CLIs that don't accept --json).
120
+ try {
121
+ const out = execFileSync(command, ["auth", "status"], {
122
+ timeout: AUTH_TIMEOUT_MS,
123
+ stdio: "pipe",
124
+ shell: process.platform === "win32",
125
+ }).toString();
126
+ debugLog("auth status output:", out.slice(0, 200));
127
+ return parseAuthStatus(out);
128
+ }
129
+ catch (error) {
130
+ debugLog("auth status threw:", error.message?.slice(0, 200));
131
+ return null;
132
+ }
91
133
  }
92
134
  let cachedBinaryPresent = null;
93
135
  let cachedAuthed = null;
@@ -104,33 +146,23 @@ function refreshCache() {
104
146
  }
105
147
  // Set timestamp first to prevent re-entrant checks during the same window
106
148
  lastCheckMs = now;
107
- // Check binary presence
108
- try {
109
- execClaude(["--version"], VERSION_TIMEOUT_MS);
110
- cachedBinaryPresent = true;
111
- }
112
- catch {
149
+ const command = findWorkingCommand();
150
+ if (!command) {
113
151
  cachedBinaryPresent = false;
114
152
  cachedAuthed = false;
115
153
  return;
116
154
  }
117
- // Request JSON explicitly so older CLI builds that defaulted to text and
118
- // newer builds that default to JSON produce a consistent shape.
119
- try {
120
- const output = execClaude(["auth", "status", "--json"], AUTH_TIMEOUT_MS).toString();
121
- cachedAuthed = parseAuthStatus(output);
122
- }
123
- catch (error) {
124
- const code = error?.code;
125
- // Spawn-shape failures (timeout, transient) shouldn't be treated as
126
- // "definitely not authed" — leave the previous value if we have one,
127
- // otherwise default to false. The version probe already established
128
- // the binary works, so a flaky auth probe is more likely transient.
129
- if (code && SOFT_FAIL_CODES.has(code) && cachedAuthed !== null) {
130
- return;
131
- }
132
- cachedAuthed = false;
155
+ cachedBinaryPresent = true;
156
+ const authed = probeAuth(command);
157
+ if (authed === null) {
158
+ // Couldn't determine auth state from CLI output. Don't clobber a
159
+ // previously known-good cache; otherwise default to false so we don't
160
+ // silently route requests to an unauthenticated CLI.
161
+ if (cachedAuthed === null)
162
+ cachedAuthed = false;
163
+ return;
133
164
  }
165
+ cachedAuthed = authed;
134
166
  }
135
167
  /**
136
168
  * Whether the `claude` binary is installed (regardless of auth state).
@@ -163,6 +163,17 @@ function resolveDispatchNodeKind(unitType, sidecarItem) {
163
163
  }
164
164
  return "unit";
165
165
  }
166
+ async function enforceMinRequestInterval(s, prefs) {
167
+ const minInterval = prefs?.min_request_interval_ms ?? 0;
168
+ if (minInterval > 0 && s.lastRequestTimestamp > 0) {
169
+ const elapsed = Date.now() - s.lastRequestTimestamp;
170
+ if (elapsed < minInterval) {
171
+ const waitMs = minInterval - elapsed;
172
+ debugLog("autoLoop", { phase: "rate-limit-wait", waitMs });
173
+ await new Promise(r => setTimeout(r, waitMs));
174
+ }
175
+ }
176
+ }
166
177
  async function runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem) {
167
178
  if (dispatchContract === "legacy-direct") {
168
179
  return runUnitPhase(ic, iterData, loopState, sidecarItem);
@@ -392,7 +403,13 @@ export async function autoLoop(ctx, pi, s, deps, options) {
392
403
  break;
393
404
  }
394
405
  // ── Unit execution (shared with dev path) ──
406
+ await enforceMinRequestInterval(s, prefs);
395
407
  const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState);
408
+ if (unitPhaseResult.action === "next") {
409
+ const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
410
+ if (typeof requestTimestamp === "number")
411
+ s.lastRequestTimestamp = requestTimestamp;
412
+ }
396
413
  deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
397
414
  unitType: iterData.unitType,
398
415
  unitId: iterData.unitId,
@@ -555,7 +572,13 @@ export async function autoLoop(ctx, pi, s, deps, options) {
555
572
  sidecarKind: sidecarItem.kind,
556
573
  });
557
574
  }
575
+ await enforceMinRequestInterval(s, prefs);
558
576
  const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem);
577
+ if (unitPhaseResult.action === "next") {
578
+ const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
579
+ if (typeof requestTimestamp === "number")
580
+ s.lastRequestTimestamp = requestTimestamp;
581
+ }
559
582
  deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
560
583
  unitType: iterData.unitType,
561
584
  unitId: iterData.unitId,
@@ -1365,7 +1365,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1365
1365
  ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls — context exhaustion, will retry`, "warning");
1366
1366
  // Fall through to next iteration where dispatch will re-derive
1367
1367
  // and re-dispatch this unit.
1368
- return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
1368
+ return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt, requestDispatchedAt: unitResult.requestDispatchedAt } };
1369
1369
  }
1370
1370
  }
1371
1371
  }
@@ -1418,7 +1418,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1418
1418
  }
1419
1419
  s.checkpointSha = null;
1420
1420
  }
1421
- return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
1421
+ return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt, requestDispatchedAt: unitResult.requestDispatchedAt } };
1422
1422
  }
1423
1423
  // ─── runFinalize ──────────────────────────────────────────────────────────────
1424
1424
  /**
@@ -138,6 +138,7 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
138
138
  const capturedTurnGen = getCurrentTurnGeneration();
139
139
  // ── Send the prompt ──
140
140
  debugLog("runUnit", { phase: "send-message", unitType, unitId });
141
+ const requestDispatchedAt = Date.now();
141
142
  pi.sendMessage({ customType: "gsd-auto", content: prompt, display: s.verbose }, { triggerTurn: true });
142
143
  // ── Await agent_end with absolute timeout (H4 fix) ──
143
144
  // If supervision fails to resolve unitPromise within 30s, treat as cancelled.
@@ -160,6 +161,7 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
160
161
  unitId,
161
162
  status: result.status,
162
163
  });
164
+ const finalResult = { ...result, requestDispatchedAt };
163
165
  // Discard trailing follow-up messages (e.g. async_job_result notifications)
164
166
  // from the completed unit. Without this, queued follow-ups trigger wasteful
165
167
  // LLM turns before the next session can start (#1642).
@@ -174,5 +176,5 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
174
176
  catch (e) {
175
177
  logWarning("engine", "clearQueue failed after unit completion", { error: String(e) });
176
178
  }
177
- return result;
179
+ return finalResult;
178
180
  }
@@ -118,6 +118,8 @@ export class AutoSession {
118
118
  lastPromptCharCount;
119
119
  lastBaselineCharCount;
120
120
  pendingQuickTasks = [];
121
+ /** Timestamp of the last LLM request dispatch (ms since epoch). Used for proactive rate limiting. */
122
+ lastRequestTimestamp = 0;
121
123
  // ── Safety harness ───────────────────────────────────────────────────────
122
124
  /** SHA of the pre-unit git checkpoint ref. Cleared on success or rollback. */
123
125
  checkpointSha = null;
@@ -215,6 +217,7 @@ export class AutoSession {
215
217
  this.lastPromptCharCount = undefined;
216
218
  this.lastBaselineCharCount = undefined;
217
219
  this.pendingQuickTasks = [];
220
+ this.lastRequestTimestamp = 0;
218
221
  this.sidecarQueue = [];
219
222
  this.rewriteAttemptCount = 0;
220
223
  this.consecutiveCompleteBootstraps = 0;
@@ -7,6 +7,7 @@
7
7
  * globals or AutoContext dependency.
8
8
  */
9
9
  import { parseUnitId } from "./unit-id.js";
10
+ import { MILESTONE_ID_RE } from "./milestone-ids.js";
10
11
  import { appendEvent } from "./workflow-events.js";
11
12
  import { atomicWriteSync } from "./atomic-write.js";
12
13
  import { clearParseCache } from "./files.js";
@@ -168,8 +169,35 @@ function getChangedFilesSinceBranch(basePath, targetBranch) {
168
169
  }
169
170
  }
170
171
  function getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId) {
172
+ // Primary: path-scoped log against .gsd/milestones/<id>. Fast and unbounded
173
+ // by depth when .gsd/ is tracked in git.
174
+ const scoped = scanGsdTaggedCommits(basePath, milestoneId, [
175
+ "log", "--format=%H%x1f%B%x1e", "HEAD", "--", `.gsd/milestones/${milestoneId}`,
176
+ ]);
177
+ if (!scoped.ok)
178
+ return scoped;
179
+ if (scoped.matched)
180
+ return scoped;
181
+ // Fallback (#5033): when .gsd/ is gitignored / external / untracked, the
182
+ // path-scoped scan matches no commits even though GSD-tagged commits
183
+ // referencing the milestone exist on the integration branch. Re-scan all
184
+ // of HEAD's history and rely on commitMatchesMilestone to bind by
185
+ // explicit milestone mention in the message body.
186
+ //
187
+ // Intentionally unbounded — symmetric with the primary scan, and avoids
188
+ // reintroducing the rolling-depth failure class removed in #4699 where
189
+ // milestone evidence aged out behind unrelated activity.
190
+ return scanGsdTaggedCommits(basePath, milestoneId, [
191
+ "log", "--format=%H%x1f%B%x1e", "HEAD",
192
+ ]);
193
+ }
194
+ function scanGsdTaggedCommits(basePath, milestoneId, gitArgs) {
171
195
  try {
172
- const logOutput = execFileSync("git", ["log", "--format=%H%x1f%B%x1e", "HEAD", "--", `.gsd/milestones/${milestoneId}`], { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
196
+ const logOutput = execFileSync("git", [...gitArgs], {
197
+ cwd: basePath,
198
+ stdio: ["ignore", "pipe", "pipe"],
199
+ encoding: "utf-8",
200
+ });
173
201
  const records = logOutput
174
202
  .split("\x1e")
175
203
  .map((record) => record.trim())
@@ -213,13 +241,24 @@ function commitMatchesMilestone(message, milestoneId, files) {
213
241
  if (commitTrailerStartsWithMilestone(message, milestoneId))
214
242
  return true;
215
243
  // Meaningful execute-task commits currently store task scope as Sxx/Tyy
216
- // rather than Mxx/Sxx/Tyy. Bind those commits back to the milestone only
217
- // when the commit also touched this milestone's artifacts.
244
+ // rather than Mxx/Sxx/Tyy. Bind those commits back to the milestone when
245
+ // either the commit touched this milestone's artifacts, or — for projects
246
+ // where .gsd/ is gitignored/external (#5033) — the message explicitly
247
+ // names the milestone.
218
248
  if (/^GSD-Task:\s*S[^/\s]+\/T\S+/m.test(message)) {
219
- return files.some((file) => isMilestoneArtifactPath(file, milestoneId));
249
+ if (files.some((file) => isMilestoneArtifactPath(file, milestoneId)))
250
+ return true;
251
+ if (commitMessageMentionsMilestone(message, milestoneId))
252
+ return true;
220
253
  }
221
254
  return false;
222
255
  }
256
+ function commitMessageMentionsMilestone(message, milestoneId) {
257
+ if (!MILESTONE_ID_RE.test(milestoneId))
258
+ return false;
259
+ const escapedMilestone = milestoneId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
260
+ return new RegExp(`\\b${escapedMilestone}\\b`).test(message);
261
+ }
223
262
  function commitTrailerStartsWithMilestone(message, milestoneId) {
224
263
  const escapedMilestone = milestoneId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
225
264
  const trailerPattern = new RegExp(`^GSD-(?:Task|Unit):\\s*${escapedMilestone}(?:$|[\\s/])`, "m");
@@ -0,0 +1,31 @@
1
+ // GSD auto-mode runtime state
2
+ import { AutoSession } from "./auto/session.js";
3
+ import { isDeterministicPolicyError, isQueuedUserMessageSkip, isToolInvocationError, markToolEnd as markTrackedToolEnd, markToolStart as markTrackedToolStart, } from "./auto-tool-tracking.js";
4
+ export const autoSession = new AutoSession();
5
+ export function getAutoRuntimeSnapshot() {
6
+ return {
7
+ active: autoSession.active,
8
+ paused: autoSession.paused,
9
+ currentUnit: autoSession.currentUnit ? { ...autoSession.currentUnit } : null,
10
+ basePath: autoSession.basePath,
11
+ };
12
+ }
13
+ export function isAutoActive() {
14
+ return autoSession.active;
15
+ }
16
+ export function isAutoPaused() {
17
+ return autoSession.paused;
18
+ }
19
+ export function markToolStart(toolCallId, toolName) {
20
+ markTrackedToolStart(toolCallId, autoSession.active, toolName);
21
+ }
22
+ export function markToolEnd(toolCallId) {
23
+ markTrackedToolEnd(toolCallId);
24
+ }
25
+ export function recordToolInvocationError(toolName, errorMsg) {
26
+ if (!autoSession.active)
27
+ return;
28
+ if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg) || isDeterministicPolicyError(errorMsg)) {
29
+ autoSession.lastToolInvocationError = `${toolName}: ${errorMsg}`;
30
+ }
31
+ }
@@ -208,7 +208,7 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode) {
208
208
  else {
209
209
  // Branch is NOT merged — preserve for safety, warn the user
210
210
  warnings.push(`Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
211
- `This may contain unmerged work. Merge manually or run \`/gsd health --fix\` to resolve.`);
211
+ `This may contain unmerged work. Merge manually or run \`/gsd doctor fix\` to resolve.`);
212
212
  // #4764 telemetry
213
213
  try {
214
214
  emitWorktreeOrphaned(basePath, milestoneId, {
@@ -139,6 +139,6 @@ export const DETERMINISTIC_POLICY_ERROR_STRINGS = [
139
139
  export function isDeterministicPolicyError(errorMsg) {
140
140
  if (!errorMsg)
141
141
  return false;
142
- return (DETERMINISTIC_POLICY_ERROR_RE.test(errorMsg) ||
143
- DETERMINISTIC_POLICY_ERROR_STRINGS.some(s => errorMsg.includes(s)));
142
+ return DETERMINISTIC_POLICY_ERROR_RE.test(errorMsg)
143
+ || DETERMINISTIC_POLICY_ERROR_STRINGS.some(s => errorMsg.includes(s));
144
144
  }
@@ -857,6 +857,11 @@ export function runWorktreePostCreateHook(sourceDir, worktreeDir, hookPath) {
857
857
  export function autoWorktreeBranch(milestoneId) {
858
858
  return `milestone/${milestoneId}`;
859
859
  }
860
+ function normalizeLocalBranchRef(branch) {
861
+ return branch.startsWith("refs/heads/")
862
+ ? branch.slice("refs/heads/".length)
863
+ : branch;
864
+ }
860
865
  // ─── Branch-mode Entry ─────────────────────────────────────────────────────
861
866
  /**
862
867
  * Enter branch isolation mode for a milestone.
@@ -1033,6 +1038,13 @@ function reconcilePlanCheckboxes(projectRoot, wtPath, milestoneId) {
1033
1038
  }
1034
1039
  }
1035
1040
  export function createAutoWorktree(basePath, milestoneId) {
1041
+ // Check if repo has commits — git worktree requires a valid HEAD
1042
+ try {
1043
+ execFileSync("git", ["rev-parse", "--verify", "HEAD"], { cwd: basePath, stdio: "pipe" });
1044
+ }
1045
+ catch {
1046
+ throw new GSDError(GSD_GIT_ERROR, `Cannot create worktree: repository has no commits yet. Worktree isolation requires at least one commit.`);
1047
+ }
1036
1048
  const branch = autoWorktreeBranch(milestoneId);
1037
1049
  // Check if the milestone branch already exists — it survives auto-mode
1038
1050
  // stop/pause and contains committed work from prior sessions. If it exists,
@@ -1408,6 +1420,10 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1408
1420
  }
1409
1421
  }
1410
1422
  // 3. chdir to original base
1423
+ // Note: previousCwd captures the cwd at this point — i.e. the worktree cwd
1424
+ // entering the function. Subsequent throws restore to previousCwd, leaving
1425
+ // the caller in worktree-cwd; callers (worktree-resolver) are responsible
1426
+ // for any further cwd movement on the error path.
1411
1427
  const previousCwd = process.cwd();
1412
1428
  process.chdir(originalBasePath_);
1413
1429
  // 4. Resolve integration branch — prefer milestone metadata, then preferences,
@@ -1422,6 +1438,20 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1422
1438
  ? prefs.main_branch
1423
1439
  : undefined;
1424
1440
  const mainBranch = integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
1441
+ // Fail closed when the resolved integration branch is the milestone branch
1442
+ // itself (#5024). Stale or corrupt metadata (e.g. integrationBranch recorded
1443
+ // as "milestone/<MID>") would otherwise let the squash merge resolve to a
1444
+ // self-merge: nothing-to-commit + empty self-diff in the post-merge safety
1445
+ // check (#1792) collapse to a false success, and the worktree-resolver
1446
+ // emits worktree-merged for work that never landed on a distinct
1447
+ // integration branch.
1448
+ if (normalizeLocalBranchRef(mainBranch) === milestoneBranch) {
1449
+ process.chdir(previousCwd);
1450
+ throw new GSDError(GSD_GIT_ERROR, `Resolved integration branch "${mainBranch}" is the same ref as milestone branch ` +
1451
+ `"${milestoneBranch}" — refusing to self-merge. Integration branch metadata is invalid; ` +
1452
+ `set a distinct main_branch in GSD preferences or repair the milestone integration record ` +
1453
+ `before retrying milestone completion.`);
1454
+ }
1425
1455
  // Remove transient project-root state files before any branch or merge
1426
1456
  // operation. Untracked milestone metadata can otherwise block squash merges.
1427
1457
  clearProjectRootStateFiles(originalBasePath_, milestoneId);