@opengsd/gsd-pi 1.0.2-dev.867e002 → 1.0.2-dev.a271e2d

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 (337) hide show
  1. package/dist/onboarding.js +22 -3
  2. package/dist/resource-loader.js +3 -1
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/context7/index.js +12 -2
  5. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  6. package/dist/resources/extensions/google-cli/index.js +30 -0
  7. package/dist/resources/extensions/google-cli/models.js +55 -0
  8. package/dist/resources/extensions/google-cli/package.json +11 -0
  9. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  10. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +62 -1
  12. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  13. package/dist/resources/extensions/gsd/auto/phases.js +37 -0
  14. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  15. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -11
  18. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  19. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  20. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  21. package/dist/resources/extensions/gsd/auto.js +6 -1
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  27. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  29. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  31. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  32. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  33. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  34. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  35. package/dist/resources/extensions/gsd/doctor.js +6 -1
  36. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  37. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  38. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  39. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  40. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  41. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  42. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  43. package/dist/resources/extensions/gsd/preferences.js +14 -2
  44. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  45. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  46. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/system.md +1 -3
  48. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  49. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  50. package/dist/resources/extensions/gsd/safety/evidence-collector.js +11 -4
  51. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  52. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  53. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  54. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  55. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  56. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  57. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  58. package/dist/resources/extensions/gsd/skills.js +60 -0
  59. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  61. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  63. package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
  64. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  65. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  66. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  67. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  68. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  69. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  70. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  71. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  72. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  73. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  74. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  75. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  76. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  77. package/dist/web/standalone/.next/BUILD_ID +1 -1
  78. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  79. package/dist/web/standalone/.next/build-manifest.json +2 -2
  80. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  81. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.html +1 -1
  98. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  105. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  106. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  108. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  109. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  110. package/package.json +1 -1
  111. package/packages/cloud-mcp-gateway/package.json +2 -2
  112. package/packages/contracts/dist/rpc.test.js +5 -0
  113. package/packages/contracts/dist/rpc.test.js.map +1 -1
  114. package/packages/contracts/dist/workflow.d.ts +7 -0
  115. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  116. package/packages/contracts/dist/workflow.js +8 -0
  117. package/packages/contracts/dist/workflow.js.map +1 -1
  118. package/packages/contracts/dist/workflow.test.js +1 -0
  119. package/packages/contracts/dist/workflow.test.js.map +1 -1
  120. package/packages/contracts/package.json +1 -1
  121. package/packages/daemon/package.json +4 -4
  122. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  123. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  124. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  125. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  138. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  140. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  142. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  144. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  148. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  150. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  151. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  152. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  153. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  154. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  155. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  156. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  157. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  158. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  159. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  160. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  161. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  162. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  164. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  165. package/packages/gsd-agent-modes/package.json +7 -7
  166. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  167. package/packages/mcp-server/dist/workflow-tools.js +28 -5
  168. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  169. package/packages/mcp-server/package.json +3 -3
  170. package/packages/native/package.json +1 -1
  171. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  172. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  173. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  174. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  175. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  176. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  177. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  178. package/packages/pi-agent-core/package.json +1 -1
  179. package/packages/pi-ai/dist/models.generated.d.ts +8 -59
  180. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  181. package/packages/pi-ai/dist/models.generated.js +21 -72
  182. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  183. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  184. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  185. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  186. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  188. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  189. package/packages/pi-ai/dist/types.d.ts +2 -0
  190. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  191. package/packages/pi-ai/dist/types.js.map +1 -1
  192. package/packages/pi-ai/package.json +1 -1
  193. package/packages/pi-coding-agent/README.md +1 -1
  194. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  195. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  198. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  201. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  203. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  205. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  206. package/packages/pi-coding-agent/package.json +7 -7
  207. package/packages/pi-tui/package.json +1 -1
  208. package/packages/rpc-client/package.json +2 -2
  209. package/pkg/package.json +1 -1
  210. package/src/resources/extensions/context7/index.ts +15 -2
  211. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  212. package/src/resources/extensions/google-cli/index.ts +34 -0
  213. package/src/resources/extensions/google-cli/models.ts +57 -0
  214. package/src/resources/extensions/google-cli/package.json +11 -0
  215. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  216. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  217. package/src/resources/extensions/gsd/auto/loop.ts +74 -1
  218. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  219. package/src/resources/extensions/gsd/auto/phases.ts +46 -0
  220. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  221. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  222. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  223. package/src/resources/extensions/gsd/auto-post-unit.ts +43 -14
  224. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  225. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  226. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  227. package/src/resources/extensions/gsd/auto.ts +6 -1
  228. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  229. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
  230. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  231. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
  232. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  233. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  234. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  235. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  236. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  237. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  238. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  239. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  240. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  241. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  242. package/src/resources/extensions/gsd/doctor.ts +6 -1
  243. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  244. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  245. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  246. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  247. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  248. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  249. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  250. package/src/resources/extensions/gsd/preferences.ts +17 -2
  251. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  252. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  253. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  254. package/src/resources/extensions/gsd/prompts/system.md +1 -3
  255. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  256. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  257. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -4
  258. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  259. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  260. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  261. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  262. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  263. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  264. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  265. package/src/resources/extensions/gsd/skills.ts +75 -0
  266. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  267. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  268. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  269. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  270. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  271. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +21 -0
  272. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  273. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
  274. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  275. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +12 -0
  276. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  277. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  278. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  279. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  280. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  281. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  282. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  283. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  284. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  285. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  286. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  287. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  288. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  289. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
  290. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  291. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  292. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  293. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  294. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  295. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  296. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  297. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  298. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  299. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  300. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  301. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  302. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  303. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  304. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  305. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +19 -0
  306. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  307. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  308. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  309. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  310. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  311. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  312. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  313. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +74 -1
  314. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  315. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  316. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  317. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  318. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  319. package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
  320. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  321. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  322. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  323. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  324. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  325. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  326. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  327. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  328. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  329. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  330. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  331. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  332. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  333. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  334. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  335. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  336. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → QKed8_bmMc7zc3WLM7MW9}/_buildManifest.js +0 -0
  337. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → QKed8_bmMc7zc3WLM7MW9}/_ssgManifest.js +0 -0
@@ -19,6 +19,7 @@ import {
19
19
  type PostUnitContext,
20
20
  type PreVerificationOpts,
21
21
  } from "../auto-post-unit.js";
22
+ import { lastAssistantText } from "../user-input-boundary.js";
22
23
  import type { Phase } from "../types.js";
23
24
  import {
24
25
  MAX_RECOVERY_CHARS,
@@ -93,9 +94,24 @@ import {
93
94
  detectRootWriteLeak,
94
95
  formatRootWriteLeakMessage,
95
96
  } from "../root-write-leak-guard.js";
97
+ import { classifyError, isTransient } from "../error-classifier.js";
96
98
 
97
99
  export const STUCK_WINDOW_SIZE = 6;
98
100
  const STUCK_RECOVERY_ATTEMPTS_KEY = "stuck_recovery_attempts";
101
+ const ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE =
102
+ /^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) hit your limit\b|usage limit\b|out of extra usage\b|service.?unavailable\b|internal(?: server)? error(?::|$)|internal(?:[_-]server)?[_-]error\b|server[_-]error\b|(?:provider|server|api|model|codex|claude|openai|anthropic|gemini)\b.{0,80}\boverloaded\b|overloaded\b.{0,80}\b(?:provider|server|api|model)\b|context (?:window|length) exceed|context window exceed)/i;
103
+ const ZERO_TOOL_PROVIDER_ERROR_SIGNAL_RE =
104
+ /(?:\b(?:http|status(?: code)?|code|error:)\s*(?:429|500|502|503)\b|\b(?:api|provider) error\s*[:(]?\s*(?:429|500|502|503)\b|\b(?:typeerror|error):\s*(?:fetch failed\b|socket hang up\b|terminated(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|(?:network|connection|server) error(?::|$)|stream idle timeout\b|partial response received\b|unexpected eof\b)|\b(?:server_error|api_error|stream_exhausted(?:_without_result)?)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|context (?:window|length) exceed|context window exceed)/i;
105
+
106
+ function classifyZeroToolProviderMessage(message: string): ReturnType<typeof classifyError> | null {
107
+ const firstLine = message.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
108
+ if (
109
+ !firstLine ||
110
+ (!ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE.test(firstLine) &&
111
+ !ZERO_TOOL_PROVIDER_ERROR_SIGNAL_RE.test(firstLine))
112
+ ) return null;
113
+ return classifyError(firstLine);
114
+ }
99
115
 
100
116
  export function resolveDispatchRecoveryAttempts(
101
117
  unitRecoveryCount: Map<string, number>,
@@ -2637,6 +2653,36 @@ export async function runUnitPhase(
2637
2653
  (u: { type: string; id: string; startedAt: number; toolCalls: number }) => u.type === unitType && u.id === unitId && u.startedAt === _resolveCurrentUnitStartedAtForTest(s.currentUnit),
2638
2654
  );
2639
2655
  if (lastUnit && lastUnit.toolCalls === 0) {
2656
+ const lastAssistantMessage = lastAssistantText(s.lastUnitAgentEndMessages);
2657
+ const providerMessageClass = classifyZeroToolProviderMessage(lastAssistantMessage);
2658
+ if (providerMessageClass && isTransient(providerMessageClass)) {
2659
+ const retryAfterMs = "retryAfterMs" in providerMessageClass ? providerMessageClass.retryAfterMs : 15_000;
2660
+ await pauseAutoForProviderError(
2661
+ ctx.ui,
2662
+ ` for ${unitType} ${unitId}`,
2663
+ () => deps.pauseAuto(ctx, pi),
2664
+ {
2665
+ isRateLimit: providerMessageClass.kind === "rate-limit",
2666
+ isTransient: true,
2667
+ retryAfterMs,
2668
+ resume: () => {
2669
+ void resumeAutoAfterProviderDelay(pi, ctx).catch((err) => {
2670
+ logWarning("engine", `Provider error auto-resume failed: ${err instanceof Error ? err.message : String(err)}`);
2671
+ });
2672
+ },
2673
+ },
2674
+ );
2675
+ await emitCancelledUnitEnd(ic, unitType, unitId, unitStartSeq, {
2676
+ message: lastAssistantMessage.slice(0, 200),
2677
+ category: "provider",
2678
+ isTransient: true,
2679
+ retryAfterMs,
2680
+ });
2681
+ return {
2682
+ action: "break",
2683
+ reason: providerMessageClass.kind === "rate-limit" ? "rate-limit" : "api-timeout",
2684
+ };
2685
+ }
2640
2686
  if (USER_DRIVEN_DEEP_UNITS.has(unitType) && isAwaitingUserInput(s.lastUnitAgentEndMessages ?? undefined)) {
2641
2687
  debugLog("runUnitPhase", {
2642
2688
  phase: "zero-tool-calls-awaiting-user-input",
@@ -27,6 +27,7 @@ import { logWarning, logError } from "../workflow-logger.js";
27
27
  import { resolveAutoSupervisorConfig } from "../preferences.js";
28
28
  import { readUnitRuntimeRecord, type AutoUnitRuntimeRecord } from "../unit-runtime.js";
29
29
  import { consumeAutoWakeup } from "./schedule-wakeup.js";
30
+ import { applyUnitSkillVisibility } from "../skill-scope.js";
30
31
 
31
32
  const UNIT_FAILSAFE_BUFFER_MS = 30_000;
32
33
  const UNIT_FAILSAFE_RECHECK_MS = 30_000;
@@ -165,6 +166,12 @@ export async function runUnit(
165
166
  }
166
167
  }
167
168
 
169
+ const setVisibleSkills =
170
+ typeof pi.setVisibleSkills === "function" ? pi.setVisibleSkills.bind(pi) : undefined;
171
+ if (setVisibleSkills) {
172
+ applyUnitSkillVisibility({ setVisibleSkills }, unitType);
173
+ }
174
+
168
175
  // ── Create the agent_end promise (per-unit one-shot) ──
169
176
  // This happens after newSession completes so session-switch agent_end events
170
177
  // from the previous session cannot resolve the new unit.
@@ -176,6 +183,7 @@ export async function runUnit(
176
183
  const pendingSwitchCancellation = _consumePendingSwitchCancellation();
177
184
  if (pendingSwitchCancellation) {
178
185
  _clearCurrentResolve();
186
+ setVisibleSkills?.(undefined);
179
187
  return {
180
188
  status: "cancelled",
181
189
  ...(pendingSwitchCancellation.errorContext ? { errorContext: pendingSwitchCancellation.errorContext } : {}),
@@ -200,6 +208,7 @@ export async function runUnit(
200
208
 
201
209
  if (!ready) {
202
210
  _clearCurrentResolve();
211
+ setVisibleSkills?.(undefined);
203
212
  return {
204
213
  status: "cancelled",
205
214
  errorContext: {
@@ -351,6 +360,7 @@ export async function runUnit(
351
360
  } finally {
352
361
  if (unitTimeoutHandle) clearTimeout(unitTimeoutHandle);
353
362
  ctx.ui.setWorkingMessage?.(undefined);
363
+ setVisibleSkills?.(undefined);
354
364
  }
355
365
  debugLog("runUnit", {
356
366
  phase: "agent-end-received",
@@ -218,6 +218,8 @@ export class AutoSession {
218
218
  // ── Isolation degradation ────────────────────────────────────────────
219
219
  /** Set to true when worktree creation fails; prevents merge of nonexistent branch. */
220
220
  isolationDegraded = false;
221
+ /** Temporary recovery mode for stranded work adopted from physical git evidence. */
222
+ strandedRecoveryIsolationMode: "worktree" | "branch" | null = null;
221
223
  /** Project-root dirty snapshot captured before an isolated worktree unit runs. */
222
224
  rootWriteBaseline: RootDirtySnapshot | null = null;
223
225
 
@@ -382,6 +384,7 @@ export class AutoSession {
382
384
  this.lastGitActionFailure = null;
383
385
  this.lastGitActionStatus = null;
384
386
  this.isolationDegraded = false;
387
+ this.strandedRecoveryIsolationMode = null;
385
388
  this.rootWriteBaseline = null;
386
389
  this.milestoneMergedInPhases = false;
387
390
  this.milestoneStartShas = new Map();
@@ -94,6 +94,7 @@ import { resolveWorktreeProjectRoot } from "./worktree-root.js";
94
94
  import { probeGitConflictState } from "./git-conflict-state.js";
95
95
  import { runTurnGitAction } from "./git-service.js";
96
96
  import { parseUnitId } from "./unit-id.js";
97
+ import { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
97
98
 
98
99
  // ─── Types ────────────────────────────────────────────────────────────────
99
100
 
@@ -132,6 +133,15 @@ export interface DispatchContext {
132
133
  sessionProvider?: string;
133
134
  }
134
135
 
136
+ function resolveExistingExpectedArtifact(
137
+ unitType: string,
138
+ unitId: string,
139
+ basePath: string,
140
+ ): string | null {
141
+ const artifactPath = resolveExpectedArtifactPath(unitType, unitId, basePath);
142
+ return artifactPath && existsSync(artifactPath) ? artifactPath : null;
143
+ }
144
+
135
145
  type ReassessmentChecker = typeof checkNeedsReassessment;
136
146
  type ResearchProjectPromptBuilder = typeof buildResearchProjectPrompt;
137
147
 
@@ -971,13 +981,17 @@ export const DISPATCH_RULES: DispatchRule[] = [
971
981
  if (await getMilestonePipelineVariant(mid) === "trivial") return null;
972
982
 
973
983
  // Load roadmap to find all slices
974
- const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
984
+ const roadmapFile =
985
+ resolveExistingExpectedArtifact("plan-milestone", mid, basePath) ??
986
+ resolveMilestoneFile(basePath, mid, "ROADMAP");
975
987
  const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
976
988
  if (!roadmapContent) return null;
977
989
  const roadmap = parseRoadmap(roadmapContent);
978
990
 
979
991
  // Find slices that need research (no RESEARCH file, dependencies done)
980
- const milestoneResearchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
992
+ const milestoneResearchFile =
993
+ resolveExistingExpectedArtifact("research-milestone", mid, basePath) ??
994
+ resolveMilestoneFile(basePath, mid, "RESEARCH");
981
995
  const researchReadySlices: Array<{ id: string; title: string }> = [];
982
996
 
983
997
  for (const slice of roadmap.slices) {
@@ -985,10 +999,10 @@ export const DISPATCH_RULES: DispatchRule[] = [
985
999
  // Skip S01 when milestone research exists
986
1000
  if (milestoneResearchFile && slice.id === "S01") continue;
987
1001
  // Skip if already has research
988
- if (resolveSliceFile(basePath, mid, slice.id, "RESEARCH")) continue;
1002
+ if (resolveExistingExpectedArtifact("research-slice", `${mid}/${slice.id}`, basePath)) continue;
989
1003
  // Skip if dependencies aren't done (check for SUMMARY files)
990
1004
  const depsComplete = (slice.depends ?? []).every((depId) =>
991
- !!resolveSliceFile(basePath, mid, depId, "SUMMARY"),
1005
+ !!resolveExistingExpectedArtifact("complete-slice", `${mid}/${depId}`, basePath),
992
1006
  );
993
1007
  if (!depsComplete) continue;
994
1008
 
@@ -1001,7 +1015,9 @@ export const DISPATCH_RULES: DispatchRule[] = [
1001
1015
  // #4414: If a previous parallel-research attempt escalated to a blocker
1002
1016
  // placeholder, skip this rule and fall through to per-slice research
1003
1017
  // (or other rules) rather than re-dispatching the same failing unit.
1004
- const parallelBlocker = resolveMilestoneFile(basePath, mid, "PARALLEL-BLOCKER");
1018
+ const parallelBlocker =
1019
+ resolveExistingExpectedArtifact("research-slice", `${mid}/parallel-research`, basePath) ??
1020
+ resolveMilestoneFile(basePath, mid, "PARALLEL-BLOCKER");
1005
1021
  if (parallelBlocker) return null;
1006
1022
 
1007
1023
  return {
@@ -1030,15 +1046,19 @@ export const DISPATCH_RULES: DispatchRule[] = [
1030
1046
  if (!state.activeSlice) return missingSliceStop(mid, state.phase);
1031
1047
  const sid = state.activeSlice!.id;
1032
1048
  const sTitle = state.activeSlice!.title;
1033
- const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
1049
+ const researchFile =
1050
+ resolveExistingExpectedArtifact("research-slice", `${mid}/${sid}`, basePath) ??
1051
+ resolveSliceFile(basePath, mid, sid, "RESEARCH");
1034
1052
  if (researchFile) return null; // has research, fall through
1035
1053
  // Skip slice research for S01 when milestone research already exists —
1036
1054
  // the milestone research already covers the same ground for the first slice.
1037
- const milestoneResearchFile = resolveMilestoneFile(
1038
- basePath,
1039
- mid,
1040
- "RESEARCH",
1041
- );
1055
+ const milestoneResearchFile =
1056
+ resolveExistingExpectedArtifact("research-milestone", mid, basePath) ??
1057
+ resolveMilestoneFile(
1058
+ basePath,
1059
+ mid,
1060
+ "RESEARCH",
1061
+ );
1042
1062
  if (milestoneResearchFile && sid === "S01") return null; // fall through to plan-slice
1043
1063
  return {
1044
1064
  action: "dispatch",
@@ -68,7 +68,7 @@ import {
68
68
  runMilestoneCloseoutGitHub,
69
69
  } from "./milestone-closeout.js";
70
70
  import type { AutoSession, SidecarItem } from "./auto/session.js";
71
- import { getEvidence, clearEvidenceFromDisk } from "./safety/evidence-collector.js";
71
+ import { getEvidence, clearEvidenceFromDisk, isExecutionToolName } from "./safety/evidence-collector.js";
72
72
  import { validateFileChanges } from "./safety/file-change-validator.js";
73
73
  import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
74
74
  import { validateContent } from "./safety/content-validator.js";
@@ -112,6 +112,27 @@ const MAX_VERIFICATION_RETRIES = 3;
112
112
  const MAX_NOTIFICATION_DETAILS = 3;
113
113
  const NOTIFICATION_BULLET = "•";
114
114
 
115
+ function isParallelResearchUnit(unitType: string, unitId: string): boolean {
116
+ return unitType === "research-slice" && unitId.endsWith("/parallel-research");
117
+ }
118
+
119
+ export function maybeWriteParallelResearchCostSpikeBlocker(
120
+ unitType: string,
121
+ unitId: string,
122
+ basePath: string,
123
+ unitCostUsd: number,
124
+ rollingAvgUsd: number,
125
+ ): string | null {
126
+ if (!isParallelResearchUnit(unitType, unitId)) return null;
127
+ return writeBlockerPlaceholder(
128
+ unitType,
129
+ unitId,
130
+ basePath,
131
+ `Parallel slice research cost spike detected (${unitCostUsd.toFixed(2)} vs avg ${rollingAvgUsd.toFixed(2)}). ` +
132
+ "Skipping the aggregate sentinel so dispatch can fall back to per-slice research.",
133
+ );
134
+ }
135
+
115
136
  export function resolveCloseoutGitAction(
116
137
  uokFlags: ReturnType<typeof resolveUokFlags>,
117
138
  ): TurnGitActionMode | null {
@@ -469,12 +490,6 @@ export function _shouldDispatchQuickTaskForTest(
469
490
  state.currentUnit.type !== "quick-task";
470
491
  }
471
492
 
472
- function isExecutionToolName(name: unknown): boolean {
473
- if (typeof name !== "string") return false;
474
- const normalized = name.trim().toLowerCase();
475
- return normalized === "bash" || normalized === "gsd_exec";
476
- }
477
-
478
493
  export function _hasExecutionToolCallsInSessionForTest(entries: readonly unknown[]): boolean {
479
494
  for (const entry of entries) {
480
495
  const e = entry as any;
@@ -1449,11 +1464,11 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1449
1464
  suppressedWarning: "evidence-empty-but-session-has-exec-calls",
1450
1465
  });
1451
1466
  } else {
1452
- logWarning("safety", `evidence mismatch: ${missingCommandMismatches.length} claimed command(s) not found in bash calls`);
1453
- ctx.ui.notify(
1454
- `Safety: task ${sTid} claimed ${missingCommandMismatches.length} command(s) not found in recorded bash calls`,
1455
- "warning",
1456
- );
1467
+ logWarning("safety", `evidence mismatch: ${missingCommandMismatches.length} claimed command(s) not found in recorded execution calls`);
1468
+ ctx.ui.notify(
1469
+ `Safety: task ${sTid} claimed ${missingCommandMismatches.length} command(s) not found in recorded execution calls`,
1470
+ "warning",
1471
+ );
1457
1472
  }
1458
1473
  }
1459
1474
 
@@ -1591,7 +1606,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1591
1606
  if (!triggerArtifactVerified) {
1592
1607
  try {
1593
1608
  const { milestone: mid, slice: sid } = parseUnitId(s.currentUnit.id);
1594
- if (mid && sid) {
1609
+ if (mid && sid && !isParallelResearchUnit(s.currentUnit.type, s.currentUnit.id)) {
1595
1610
  // Phase C: write to the canonical project root (#5236 scope)
1596
1611
  // so non-symlinked worktrees no longer maintain a separate
1597
1612
  // local .gsd/ projection. copyPlanningArtifacts has been
@@ -1860,8 +1875,17 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1860
1875
  );
1861
1876
  return "continue";
1862
1877
  }
1878
+ const parallelBlocker = maybeWriteParallelResearchCostSpikeBlocker(
1879
+ s.currentUnit.type,
1880
+ s.currentUnit.id,
1881
+ verificationBasePath,
1882
+ unitCostUsd,
1883
+ rollingAvgUsd,
1884
+ );
1863
1885
  ctx.ui.notify(
1864
- `Unit ${s.currentUnit.id} cost spike detected (${unitCostUsd.toFixed(2)} vs avg ${rollingAvgUsd.toFixed(2)}) — pausing auto-mode.`,
1886
+ parallelBlocker
1887
+ ? `Unit ${s.currentUnit.id} cost spike detected (${unitCostUsd.toFixed(2)} vs avg ${rollingAvgUsd.toFixed(2)}) — wrote parallel blocker and pausing auto-mode.`
1888
+ : `Unit ${s.currentUnit.id} cost spike detected (${unitCostUsd.toFixed(2)} vs avg ${rollingAvgUsd.toFixed(2)}) — pausing auto-mode.`,
1865
1889
  "error",
1866
1890
  );
1867
1891
  await pauseAuto(ctx, pi);
@@ -2031,6 +2055,11 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2031
2055
  "info",
2032
2056
  );
2033
2057
 
2058
+ await s.orchestration?.retryActiveUnit({
2059
+ unitType: trigger.unitType,
2060
+ unitId: trigger.unitId,
2061
+ });
2062
+
2034
2063
  // ── State reset: undo the completion so deriveState re-derives the unit ──
2035
2064
  try {
2036
2065
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(trigger.unitId);
@@ -19,12 +19,11 @@ import {
19
19
  relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath,
20
20
  resolveGsdRootFile, relGsdRootFile, resolveRuntimeFile,
21
21
  } from "./paths.js";
22
- import { resolveSkillDiscoveryMode, resolveInlineLevel, loadEffectiveGSDPreferences, resolveAllSkillReferences } from "./preferences.js";
22
+ import { resolveInlineLevel, loadEffectiveGSDPreferences } from "./preferences.js";
23
23
  import { isContextModeEnabled } from "./preferences-types.js";
24
24
  import { parseRoadmap } from "./parsers-legacy.js";
25
25
  import type { GSDState, InlineLevel } from "./types.js";
26
26
  import type { GSDPreferences } from "./preferences.js";
27
- import { getLoadedSkills, type Skill } from "@gsd/pi-coding-agent";
28
27
  import { join, basename } from "node:path";
29
28
  import { existsSync } from "node:fs";
30
29
  import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary, type MinimalModelRegistry } from "./context-budget.js";
@@ -42,10 +41,12 @@ import { readCompactionSnapshot } from "./compaction-snapshot.js";
42
41
  import { logWarning } from "./workflow-logger.js";
43
42
  import { inlineGraphSubgraph } from "./graph-context.js";
44
43
  import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
45
- import { resolveSkillManifest, warnIfManifestHasMissingSkills } from "./skill-manifest.js";
46
44
  import { classifyProject, type ProjectClassification } from "./detection.js";
47
45
  import { hasBrowserRequiredText } from "./browser-evidence.js";
48
46
  import { debugLog } from "./debug-logger.js";
47
+ import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
48
+
49
+ export { buildSkillActivationBlock, buildSkillDiscoveryVars };
49
50
 
50
51
  // ─── Preamble Cap ─────────────────────────────────────────────────────────────
51
52
 
@@ -1172,287 +1173,6 @@ export async function inlineRoadmapExcerpt(
1172
1173
  return `### Milestone Roadmap (excerpt)\nSource: \`${roadmapRel}\`\n\n${excerpt}`;
1173
1174
  }
1174
1175
 
1175
- // ─── Skill Activation & Discovery ─────────────────────────────────────────
1176
-
1177
- function normalizeSkillReference(ref: string): string {
1178
- const normalized = ref.replace(/\\/g, "/").trim();
1179
- const base = basename(normalized).replace(/\.md$/i, "");
1180
- const name = /^SKILL$/i.test(base)
1181
- ? basename(normalized.replace(/\/SKILL(?:\.md)?$/i, ""))
1182
- : base;
1183
- return name.trim().toLowerCase();
1184
- }
1185
-
1186
- function tokenizeSkillContext(...parts: Array<string | null | undefined>): Set<string> {
1187
- const tokens = new Set<string>();
1188
- const addVariants = (raw: string) => {
1189
- const value = raw.trim().toLowerCase();
1190
- if (!value || value.length < 2) return;
1191
- tokens.add(value);
1192
- tokens.add(value.replace(/[-_]+/g, " "));
1193
- tokens.add(value.replace(/\s+/g, "-"));
1194
- tokens.add(value.replace(/\s+/g, ""));
1195
- };
1196
-
1197
- for (const part of parts) {
1198
- if (!part) continue;
1199
- const text = part.toLowerCase();
1200
- const phraseMatches = text.match(/[a-z0-9][a-z0-9+.#/_-]{1,}/g) ?? [];
1201
- for (const match of phraseMatches) {
1202
- addVariants(match);
1203
- for (const piece of match.split(/[^a-z0-9+.#]+/g)) {
1204
- if (piece.length >= 3) addVariants(piece);
1205
- }
1206
- }
1207
- }
1208
-
1209
- return tokens;
1210
- }
1211
-
1212
- function skillMatchesContext(skill: Skill, contextTokens: Set<string>): boolean {
1213
- const haystacks = [
1214
- skill.name.toLowerCase(),
1215
- skill.name.toLowerCase().replace(/[-_]+/g, " "),
1216
- skill.description.toLowerCase(),
1217
- ];
1218
-
1219
- return [...contextTokens].some(token =>
1220
- token.length >= 3 && haystacks.some(haystack => haystack.includes(token)),
1221
- );
1222
- }
1223
-
1224
- function resolvePreferenceSkillNames(refs: string[], base: string): string[] {
1225
- if (refs.length === 0) return [];
1226
- const prefs: GSDPreferences = { always_use_skills: refs };
1227
- const report = resolveAllSkillReferences(prefs, base);
1228
- return refs.map(ref => {
1229
- const resolution = report.resolutions.get(ref);
1230
- return normalizeSkillReference(resolution?.resolvedPath ?? ref);
1231
- }).filter(Boolean);
1232
- }
1233
-
1234
- function ruleMatchesContext(when: string, contextTokens: Set<string>): boolean {
1235
- const whenTokens = tokenizeSkillContext(when);
1236
- return [...whenTokens].some(token =>
1237
- contextTokens.has(token) || [...contextTokens].some(ctx => ctx.includes(token) || token.includes(ctx)),
1238
- );
1239
- }
1240
-
1241
- function resolveSkillRuleMatches(
1242
- prefs: GSDPreferences | undefined,
1243
- contextTokens: Set<string>,
1244
- base: string,
1245
- ): { include: string[]; avoid: string[] } {
1246
- if (!prefs?.skill_rules?.length) return { include: [], avoid: [] };
1247
-
1248
- const include: string[] = [];
1249
- const avoid: string[] = [];
1250
- for (const rule of prefs.skill_rules) {
1251
- if (!ruleMatchesContext(rule.when, contextTokens)) continue;
1252
- include.push(...resolvePreferenceSkillNames([...(rule.use ?? []), ...(rule.prefer ?? [])], base));
1253
- avoid.push(...resolvePreferenceSkillNames(rule.avoid ?? [], base));
1254
- }
1255
- return { include, avoid };
1256
- }
1257
-
1258
- function resolvePreferredSkillNames(
1259
- prefs: GSDPreferences | undefined,
1260
- visibleSkills: Skill[],
1261
- contextTokens: Set<string>,
1262
- base: string,
1263
- ): string[] {
1264
- if (!prefs?.prefer_skills?.length) return [];
1265
- const preferred = new Set(resolvePreferenceSkillNames(prefs.prefer_skills, base));
1266
- return visibleSkills
1267
- .filter(skill => preferred.has(normalizeSkillReference(skill.name)) && skillMatchesContext(skill, contextTokens))
1268
- .map(skill => normalizeSkillReference(skill.name));
1269
- }
1270
-
1271
- /** Skill names must be lowercase alphanumeric with hyphens — reject anything else
1272
- * to prevent prompt injection via crafted directory names. */
1273
- const SAFE_SKILL_NAME = /^[a-z0-9][a-z0-9-]*$/;
1274
-
1275
- function formatSkillActivationBlock(skillNames: string[]): string {
1276
- const safe = skillNames.filter(name => SAFE_SKILL_NAME.test(name));
1277
- if (safe.length === 0) return "";
1278
- // Use explicit parameter syntax so LLMs pass { skill: "..." } instead of { name: "..." }.
1279
- // The function-call-like syntax `Skill('name')` led LLMs to infer a positional
1280
- // parameter name, causing tool validation failures — see #2224.
1281
- const calls = safe.map(name => `Call Skill({ skill: '${name}' })`).join('. ');
1282
- return `<skill_activation>${calls}.</skill_activation>`;
1283
- }
1284
-
1285
- /**
1286
- * Manifest-driven recommendations block — informational only, does NOT
1287
- * auto-invoke. Lists per-unit-type skills that are installed but not already
1288
- * activated by explicit user intent (always_use_skills / prefer_skills /
1289
- * skill_rules / task-plan skills_used). Surfaces relevant skills to the
1290
- * model so they can be invoked when the model judges them useful.
1291
- *
1292
- * This is the additive complement to the existing activation directive:
1293
- * activation force-invokes (explicit intent), recommendations remind
1294
- * (manifest defaults). User intent is preserved as the stronger signal
1295
- * (RFC #4779 design principle); this block only adds visibility.
1296
- */
1297
- function formatSkillRecommendationsBlock(unitType: string | undefined, skillNames: string[]): string {
1298
- if (!unitType) return "";
1299
- const safe = skillNames.filter(name => SAFE_SKILL_NAME.test(name));
1300
- if (safe.length === 0) return "";
1301
- return `<skill_recommendations unit="${unitType}">For this unit type, also consider invoking: ${safe.join(", ")}. Use Skill({ skill: 'name' }) when relevant — these are recommendations, not requirements.</skill_recommendations>`;
1302
- }
1303
-
1304
- export function buildSkillActivationBlock(params: {
1305
- base: string;
1306
- milestoneId: string;
1307
- milestoneTitle?: string;
1308
- sliceId?: string;
1309
- sliceTitle?: string;
1310
- taskId?: string;
1311
- taskTitle?: string;
1312
- extraContext?: string[];
1313
- taskPlanContent?: string | null;
1314
- preferences?: GSDPreferences;
1315
- /**
1316
- * Unit type dispatching this prompt. When provided, skills are filtered
1317
- * through the per-unit-type manifest (see `skill-manifest.ts`). Unknown
1318
- * or omitted values retain the pre-manifest behavior (all skills eligible).
1319
- */
1320
- unitType?: string;
1321
- }): string {
1322
- const prefs = params.preferences ?? loadEffectiveGSDPreferences(params.base)?.preferences;
1323
- const contextTokens = tokenizeSkillContext(
1324
- params.milestoneId,
1325
- params.milestoneTitle,
1326
- params.sliceId,
1327
- params.sliceTitle,
1328
- params.taskId,
1329
- params.taskTitle,
1330
- );
1331
-
1332
- const loaded = (typeof getLoadedSkills === 'function' ? getLoadedSkills() : []).filter(skill => !skill.disableModelInvocation);
1333
-
1334
- // Skill activation here is driven entirely by explicit sources
1335
- // (always_use_skills, prefer_skills, skill_rules, task-plan skills_used).
1336
- // Every match is an explicit user/project intent and must not be dropped
1337
- // by the unit-type manifest — user intent is stronger signal than
1338
- // defaults. The manifest's real home is the skill catalog rendering
1339
- // layer (pi-coding-agent `formatSkillsForPrompt`); that wiring is tracked
1340
- // as the "load-time short-circuit" follow-up to RFC #4779.
1341
- //
1342
- // `unitType` stays plumbed so the strict-mode warning can surface
1343
- // manifest entries that reference uninstalled skills, and so the
1344
- // activation-block site is ready to opt in once PR B lands.
1345
- const visibleSkills = loaded;
1346
- const installedNames = new Set(visibleSkills.map(skill => normalizeSkillReference(skill.name)));
1347
- warnIfManifestHasMissingSkills(params.unitType, installedNames);
1348
- const avoided = new Set(resolvePreferenceSkillNames(prefs?.avoid_skills ?? [], params.base));
1349
- const matched = new Set<string>();
1350
-
1351
- for (const name of resolvePreferenceSkillNames(prefs?.always_use_skills ?? [], params.base)) {
1352
- matched.add(name);
1353
- }
1354
-
1355
- const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base);
1356
- for (const name of ruleMatches.include) matched.add(name);
1357
- for (const name of ruleMatches.avoid) avoided.add(name);
1358
-
1359
- for (const name of resolvePreferredSkillNames(prefs, visibleSkills, contextTokens, params.base)) {
1360
- matched.add(name);
1361
- }
1362
-
1363
- if (params.taskPlanContent) {
1364
- try {
1365
- const taskPlan = parseTaskPlanFile(params.taskPlanContent);
1366
- for (const skillName of taskPlan.frontmatter.skills_used) {
1367
- matched.add(normalizeSkillReference(skillName));
1368
- }
1369
- } catch (err) {
1370
- logWarning("prompt", `parseTaskPlanFile failed: ${err instanceof Error ? err.message : String(err)}`);
1371
- }
1372
- }
1373
-
1374
- // Heuristic auto-match (gated on skill_discovery: "auto").
1375
- // For each installed skill, check if its name or description appears in the
1376
- // unit's context tokens (milestone/slice/task titles). Only consider skills
1377
- // already on the unit-type manifest allowlist — this keeps the heuristic
1378
- // narrow and avoids wildly off-topic activations.
1379
- // Users who set `skill_discovery: "off"` or "suggest" do not get
1380
- // auto-matched skills (the recommendations block still surfaces manifest
1381
- // skills passively); only "auto" actually adds them to the activation
1382
- // directive set. Default `skill_discovery` is "suggest", so this is opt-in.
1383
- if ((prefs?.skill_discovery ?? "suggest") === "auto") {
1384
- const manifestAllow = resolveSkillManifest(params.unitType);
1385
- const allowSet = manifestAllow ? new Set(manifestAllow) : null;
1386
- for (const skill of visibleSkills) {
1387
- const normalized = normalizeSkillReference(skill.name);
1388
- if (matched.has(normalized) || avoided.has(normalized)) continue;
1389
- // Respect the manifest allowlist when present; wildcard (null) lets all
1390
- // installed skills compete for keyword match.
1391
- if (allowSet && !allowSet.has(normalized)) continue;
1392
- if (skillMatchesContext(skill, contextTokens)) {
1393
- matched.add(normalized);
1394
- }
1395
- }
1396
- }
1397
-
1398
- const ordered = [...matched]
1399
- .filter(name => installedNames.has(name) && !avoided.has(name))
1400
- .sort();
1401
- const activationBlock = formatSkillActivationBlock(ordered);
1402
-
1403
- // Manifest-driven recommendations (additive, does not override explicit intent).
1404
- // Only surface skills the manifest declares for this unit type that are
1405
- // installed and not already in matched/avoided.
1406
- const matchedSet = new Set(ordered);
1407
- const manifestList = resolveSkillManifest(params.unitType);
1408
- const recommendations = (manifestList ?? [])
1409
- .filter(name => installedNames.has(name) && !avoided.has(name) && !matchedSet.has(name))
1410
- .sort();
1411
- const recommendationsBlock = formatSkillRecommendationsBlock(params.unitType, recommendations);
1412
-
1413
- if (!activationBlock && !recommendationsBlock) return "";
1414
- if (!activationBlock) return recommendationsBlock;
1415
- if (!recommendationsBlock) return activationBlock;
1416
- return `${activationBlock}\n${recommendationsBlock}`;
1417
- }
1418
-
1419
- /**
1420
- * Build the skill discovery template variables for research prompts.
1421
- * Returns { skillDiscoveryMode, skillDiscoveryInstructions } for template substitution.
1422
- */
1423
- export function buildSkillDiscoveryVars(): { skillDiscoveryMode: string; skillDiscoveryInstructions: string } {
1424
- const mode = resolveSkillDiscoveryMode();
1425
-
1426
- if (mode === "off") {
1427
- return {
1428
- skillDiscoveryMode: "off",
1429
- skillDiscoveryInstructions: " Skill discovery is disabled. Skip this step.",
1430
- };
1431
- }
1432
-
1433
- const autoInstall = mode === "auto";
1434
- const instructions = `
1435
- Identify the key technologies, frameworks, and services this work depends on (e.g. Stripe, Clerk, Supabase, JUCE, SwiftUI).
1436
- For each, check if a professional agent skill already exists:
1437
- - First check \`<available_skills>\` in your system prompt — a skill may already be installed.
1438
- - For technologies without an installed skill, run: \`npx skills find "<technology>"\`
1439
- - Only consider skills that are **directly relevant** to core technologies — not tangentially related.
1440
- - Evaluate results by install count and relevance to the actual work.${autoInstall
1441
- ? `
1442
- - Install relevant skills: \`npx skills add <owner/repo@skill> -g -y\`
1443
- - Record installed skills in the "Skills Discovered" section of your research output.
1444
- - Installed skills will automatically appear in subsequent units' system prompts — no manual steps needed.`
1445
- : `
1446
- - Note promising skills in your research output with their install commands, but do NOT install them.
1447
- - The user will decide which to install.`
1448
- }`;
1449
-
1450
- return {
1451
- skillDiscoveryMode: mode,
1452
- skillDiscoveryInstructions: instructions,
1453
- };
1454
- }
1455
-
1456
1176
  // ─── Text Helpers ──────────────────────────────────────────────────────────
1457
1177
 
1458
1178
  export function extractMarkdownSection(content: string, heading: string): string | null {