gsd-pi 2.41.0-dev.cac69f9 → 2.42.0-dev.1df898f

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 (263) hide show
  1. package/README.md +23 -0
  2. package/dist/cli.js +18 -3
  3. package/dist/loader.js +3 -1
  4. package/dist/resource-loader.js +39 -6
  5. package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
  6. package/dist/resources/extensions/async-jobs/await-tool.js +5 -0
  7. package/dist/resources/extensions/async-jobs/index.js +2 -0
  8. package/dist/resources/extensions/gsd/auto/loop.js +80 -0
  9. package/dist/resources/extensions/gsd/auto/phases.js +3 -5
  10. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  11. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -0
  12. package/dist/resources/extensions/gsd/auto-prompts.js +3 -16
  13. package/dist/resources/extensions/gsd/auto-start.js +8 -11
  14. package/dist/resources/extensions/gsd/auto.js +28 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -5
  16. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
  17. package/dist/resources/extensions/gsd/commands/catalog.js +32 -0
  18. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
  19. package/dist/resources/extensions/gsd/context-injector.js +74 -0
  20. package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
  21. package/dist/resources/extensions/gsd/custom-verification.js +145 -0
  22. package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
  23. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
  24. package/dist/resources/extensions/gsd/definition-loader.js +352 -0
  25. package/dist/resources/extensions/gsd/detection.js +19 -0
  26. package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
  27. package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
  28. package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
  29. package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
  30. package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
  31. package/dist/resources/extensions/gsd/engine-types.js +8 -0
  32. package/dist/resources/extensions/gsd/execution-policy.js +8 -0
  33. package/dist/resources/extensions/gsd/forensics.js +84 -0
  34. package/dist/resources/extensions/gsd/git-constants.js +1 -0
  35. package/dist/resources/extensions/gsd/git-service.js +1 -1
  36. package/dist/resources/extensions/gsd/graph.js +225 -0
  37. package/dist/resources/extensions/gsd/native-git-bridge.js +1 -0
  38. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  39. package/dist/resources/extensions/gsd/preferences.js +59 -8
  40. package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
  41. package/dist/resources/extensions/gsd/repo-identity.js +46 -5
  42. package/dist/resources/extensions/gsd/run-manager.js +134 -0
  43. package/dist/resources/extensions/gsd/service-tier.js +13 -4
  44. package/dist/resources/extensions/gsd/session-lock.js +2 -2
  45. package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
  46. package/dist/resources/extensions/gsd/worktree-resolver.js +2 -2
  47. package/dist/resources/extensions/gsd/worktree.js +2 -2
  48. package/dist/resources/extensions/mcp-client/index.js +2 -1
  49. package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
  50. package/dist/resources/skills/create-workflow/SKILL.md +103 -0
  51. package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  52. package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
  53. package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  54. package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  55. package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  56. package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  57. package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  58. package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  59. package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  60. package/dist/web/standalone/.next/BUILD_ID +1 -1
  61. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  62. package/dist/web/standalone/.next/build-manifest.json +2 -2
  63. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  64. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  65. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/index.html +1 -1
  82. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  89. package/dist/web/standalone/.next/server/chunks/229.js +2 -2
  90. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  91. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  92. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  93. package/dist/web-mode.d.ts +2 -0
  94. package/dist/web-mode.js +40 -4
  95. package/package.json +1 -1
  96. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  97. package/packages/pi-agent-core/dist/agent.js +2 -0
  98. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  99. package/packages/pi-agent-core/dist/types.d.ts +6 -0
  100. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  101. package/packages/pi-agent-core/dist/types.js.map +1 -1
  102. package/packages/pi-agent-core/src/agent.test.ts +53 -0
  103. package/packages/pi-agent-core/src/agent.ts +3 -0
  104. package/packages/pi-agent-core/src/types.ts +6 -0
  105. package/packages/pi-agent-core/tsconfig.json +1 -1
  106. package/packages/pi-ai/dist/models.d.ts +5 -3
  107. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  108. package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
  109. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  110. package/packages/pi-ai/dist/models.generated.js +1135 -1588
  111. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  112. package/packages/pi-ai/dist/models.js.map +1 -1
  113. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  114. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
  115. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  116. package/packages/pi-ai/scripts/generate-models.ts +1543 -0
  117. package/packages/pi-ai/src/models.generated.ts +1140 -1593
  118. package/packages/pi-ai/src/models.ts +7 -4
  119. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
  120. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
  122. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
  124. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
  126. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
  128. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
  131. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
  134. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -0
  137. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
  139. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
  141. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
  143. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
  145. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
  148. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +30 -10
  157. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  158. package/packages/pi-coding-agent/package.json +1 -1
  159. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
  160. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
  161. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
  163. package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
  164. package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
  165. package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
  166. package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
  167. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
  168. package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
  169. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
  170. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +31 -11
  171. package/pkg/package.json +1 -1
  172. package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
  173. package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
  174. package/src/resources/extensions/async-jobs/await-tool.test.ts +47 -0
  175. package/src/resources/extensions/async-jobs/await-tool.ts +5 -0
  176. package/src/resources/extensions/async-jobs/index.ts +1 -0
  177. package/src/resources/extensions/async-jobs/job-manager.ts +2 -0
  178. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -1
  179. package/src/resources/extensions/gsd/auto/loop.ts +91 -0
  180. package/src/resources/extensions/gsd/auto/phases.ts +3 -5
  181. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  182. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -0
  183. package/src/resources/extensions/gsd/auto-prompts.ts +2 -18
  184. package/src/resources/extensions/gsd/auto-start.ts +7 -10
  185. package/src/resources/extensions/gsd/auto.ts +31 -1
  186. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -5
  187. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
  188. package/src/resources/extensions/gsd/commands/catalog.ts +32 -0
  189. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
  190. package/src/resources/extensions/gsd/context-injector.ts +100 -0
  191. package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
  192. package/src/resources/extensions/gsd/custom-verification.ts +180 -0
  193. package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
  194. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
  195. package/src/resources/extensions/gsd/definition-loader.ts +462 -0
  196. package/src/resources/extensions/gsd/detection.ts +19 -0
  197. package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
  198. package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
  199. package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
  200. package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
  201. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  202. package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
  203. package/src/resources/extensions/gsd/engine-types.ts +71 -0
  204. package/src/resources/extensions/gsd/execution-policy.ts +43 -0
  205. package/src/resources/extensions/gsd/forensics.ts +92 -0
  206. package/src/resources/extensions/gsd/git-constants.ts +1 -0
  207. package/src/resources/extensions/gsd/git-service.ts +0 -1
  208. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  209. package/src/resources/extensions/gsd/graph.ts +312 -0
  210. package/src/resources/extensions/gsd/native-git-bridge.ts +1 -0
  211. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  212. package/src/resources/extensions/gsd/preferences.ts +62 -6
  213. package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
  214. package/src/resources/extensions/gsd/repo-identity.ts +48 -5
  215. package/src/resources/extensions/gsd/run-manager.ts +180 -0
  216. package/src/resources/extensions/gsd/service-tier.ts +17 -4
  217. package/src/resources/extensions/gsd/session-lock.ts +2 -2
  218. package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
  219. package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
  220. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
  221. package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
  222. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
  223. package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
  224. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
  225. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
  226. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
  227. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
  228. package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
  229. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
  230. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
  231. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
  233. package/src/resources/extensions/gsd/tests/git-service.test.ts +44 -0
  234. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
  235. package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
  236. package/src/resources/extensions/gsd/tests/journal.test.ts +82 -127
  237. package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
  238. package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
  239. package/src/resources/extensions/gsd/tests/service-tier.test.ts +30 -1
  240. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +56 -3
  241. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
  242. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
  243. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
  244. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -78
  245. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +81 -74
  246. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +1 -2
  247. package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
  248. package/src/resources/extensions/gsd/worktree-resolver.ts +2 -3
  249. package/src/resources/extensions/gsd/worktree.ts +2 -2
  250. package/src/resources/extensions/mcp-client/index.ts +5 -1
  251. package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
  252. package/src/resources/skills/create-workflow/SKILL.md +103 -0
  253. package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  254. package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
  255. package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  256. package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  257. package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  258. package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  259. package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  260. package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  261. package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  262. /package/dist/web/standalone/.next/static/{EnGUNqHeGbE0tuuUkTJVA → qw8qDHXOTLUXBq1vEknSz}/_buildManifest.js +0 -0
  263. /package/dist/web/standalone/.next/static/{EnGUNqHeGbE0tuuUkTJVA → qw8qDHXOTLUXBq1vEknSz}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -24,6 +24,29 @@ One command. Walk away. Come back to a built project with clean git history.
24
24
 
25
25
  ---
26
26
 
27
+ ## What's New in v2.42.0
28
+
29
+ ### New Features
30
+
31
+ - **Declarative workflow engine** — define YAML workflows that execute through auto-loop, enabling repeatable multi-step automations without code. (#2024)
32
+ - **Unified rule registry & event journal** — centralized rule registry, event journal with query tool, and standardized tool naming convention. (#1928)
33
+ - **PR risk checker** — CI classifies changed files by system area and surfaces risk level on pull requests. (#1930)
34
+ - **`/gsd fast`** — toggle service tier for supported models, enabling prioritized API routing for faster responses. (#1862)
35
+ - **Web mode CLI flags** — `--host`, `--port`, and `--allowed-origins` flags give full control over the web server bind address and CORS policy. (#1873)
36
+ - **ADR attribution** — architecture decision records now distinguish human, agent, and collaborative authorship. (#1830)
37
+
38
+ ### Key Fixes
39
+
40
+ - **Node v24 web boot** — resolved `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING` that prevented `gsd --web` from starting on Node v24. (#1864)
41
+ - **Worktree health check for all ecosystems** — broadened from JS-only to 17+ ecosystems (Rust, Go, Python, Java, etc.). (#1860)
42
+ - **Doctor roadmap atomicity** — roadmap checkbox gating now checks summary on disk, not issue detection, preventing false unchecks. (#1915)
43
+ - **Windows path handling** — 8.3 short path resolution, backslash normalization in bash commands, PowerShell browser launch, and parenthesis escaping. (#1960, #1863, #1870, #1872)
44
+ - **Auth token persistence** — web UI auth token survives page refreshes via sessionStorage. (#1877)
45
+ - **German/non-English locale git errors** — git commands now force `LC_ALL=C` to prevent locale-dependent parse failures.
46
+ - **Orphan web server process** — stale web server processes on port 3000 are now cleaned up automatically.
47
+
48
+ ---
49
+
27
50
  ## What's New in v2.41.0
28
51
 
29
52
  ### New Features
package/dist/cli.js CHANGED
@@ -14,6 +14,14 @@ import { parseCliArgs as parseWebCliArgs, runWebCliBranch, migrateLegacyFlatSess
14
14
  import { stopWebMode } from './web-mode.js';
15
15
  import { getProjectSessionsDir } from './project-sessions.js';
16
16
  import { markStartup, printStartupTimings } from './startup-timings.js';
17
+ // ---------------------------------------------------------------------------
18
+ // V8 compile cache — Node 22+ can cache compiled bytecode across runs,
19
+ // eliminating repeated parse/compile overhead for unchanged modules.
20
+ // Must be set early so dynamic imports (extensions, lazy subcommands) benefit.
21
+ // ---------------------------------------------------------------------------
22
+ if (parseInt(process.versions.node) >= 22) {
23
+ process.env.NODE_COMPILE_CACHE ??= join(agentDir, '.compile-cache');
24
+ }
17
25
  function exitIfManagedResourcesAreNewer(currentAgentDir) {
18
26
  const currentVersion = process.env.GSD_VERSION || '0.0.0';
19
27
  const managedVersion = getNewerManagedResourceVersion(currentAgentDir, currentVersion);
@@ -461,8 +469,14 @@ const sessionManager = cliFlags._selectedSessionPath
461
469
  exitIfManagedResourcesAreNewer(agentDir);
462
470
  initResources(agentDir);
463
471
  markStartup('initResources');
472
+ // Overlap resource loading with session manager setup — both are independent.
473
+ // resourceLoader.reload() is the most expensive step (jiti compilation), so
474
+ // starting it early shaves ~50-200ms off interactive startup.
464
475
  const resourceLoader = buildResourceLoader(agentDir);
465
- await resourceLoader.reload();
476
+ const resourceLoadPromise = resourceLoader.reload();
477
+ // While resources load, let session manager finish any async I/O it needs.
478
+ // Then await the resource promise before creating the agent session.
479
+ await resourceLoadPromise;
466
480
  markStartup('resourceLoader.reload');
467
481
  const { session, extensionsResult } = await createAgentSession({
468
482
  authStorage,
@@ -530,8 +544,9 @@ if (!process.stdin.isTTY) {
530
544
  process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n');
531
545
  process.exit(1);
532
546
  }
533
- // Welcome screen — shown on every fresh interactive session before TUI takes over
534
- {
547
+ // Welcome screen — shown on every fresh interactive session before TUI takes over.
548
+ // Skip when the first-run banner was already printed in loader.ts (prevents double banner).
549
+ if (!process.env.GSD_FIRST_RUN_BANNER) {
535
550
  const { printWelcomeScreen } = await import('./welcome-screen.js');
536
551
  printWelcomeScreen({
537
552
  version: process.env.GSD_VERSION || '0.0.0',
package/dist/loader.js CHANGED
@@ -42,7 +42,8 @@ const pkgDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'pkg');
42
42
  process.env.PI_PACKAGE_DIR = pkgDir;
43
43
  process.env.PI_SKIP_VERSION_CHECK = '1'; // GSD runs its own update check in cli.ts — suppress pi's
44
44
  process.title = 'gsd';
45
- // Print branded banner on first launch (before ~/.gsd/ exists)
45
+ // Print branded banner on first launch (before ~/.gsd/ exists).
46
+ // Set GSD_FIRST_RUN_BANNER so cli.ts skips the duplicate welcome screen.
46
47
  if (!existsSync(appRoot)) {
47
48
  const cyan = '\x1b[36m';
48
49
  const green = '\x1b[32m';
@@ -53,6 +54,7 @@ if (!existsSync(appRoot)) {
53
54
  '\n' +
54
55
  ` Get Shit Done ${dim}v${gsdVersion}${reset}\n` +
55
56
  ` ${green}Welcome.${reset} Setting up your environment...\n\n`);
57
+ process.env.GSD_FIRST_RUN_BANNER = '1';
56
58
  }
57
59
  // GSD_CODING_AGENT_DIR — tells pi's getAgentDir() to return ~/.gsd/agent/ instead of ~/.gsd/agent/
58
60
  process.env.GSD_CODING_AGENT_DIR = agentDir;
@@ -48,14 +48,25 @@ function getBundledGsdVersion() {
48
48
  }
49
49
  }
50
50
  function writeManagedResourceManifest(agentDir) {
51
- // Record root-level files currently in the bundled extensions source so that
52
- // future upgrades can detect and prune any that get removed or moved.
51
+ // Record root-level files and subdirectory extension names currently in the
52
+ // bundled extensions source so that future upgrades can detect and prune any
53
+ // that get removed or moved.
53
54
  let installedExtensionRootFiles = [];
55
+ let installedExtensionDirs = [];
54
56
  try {
55
57
  if (existsSync(bundledExtensionsDir)) {
56
- installedExtensionRootFiles = readdirSync(bundledExtensionsDir, { withFileTypes: true })
58
+ const entries = readdirSync(bundledExtensionsDir, { withFileTypes: true });
59
+ installedExtensionRootFiles = entries
57
60
  .filter(e => e.isFile())
58
61
  .map(e => e.name);
62
+ installedExtensionDirs = entries
63
+ .filter(e => e.isDirectory())
64
+ .filter(e => {
65
+ // Only track directories that are actual extensions (contain index.js or index.ts)
66
+ const dirPath = join(bundledExtensionsDir, e.name);
67
+ return existsSync(join(dirPath, 'index.js')) || existsSync(join(dirPath, 'index.ts'));
68
+ })
69
+ .map(e => e.name);
59
70
  }
60
71
  }
61
72
  catch { /* non-fatal */ }
@@ -64,6 +75,7 @@ function writeManagedResourceManifest(agentDir) {
64
75
  syncedAt: Date.now(),
65
76
  contentHash: computeResourceFingerprint(),
66
77
  installedExtensionRootFiles,
78
+ installedExtensionDirs,
67
79
  };
68
80
  writeFileSync(getManagedResourceManifestPath(agentDir), JSON.stringify(manifest));
69
81
  }
@@ -284,16 +296,20 @@ function pruneRemovedBundledExtensions(manifest, agentDir) {
284
296
  return;
285
297
  // Current bundled root-level files (what the new version provides)
286
298
  const currentSourceFiles = new Set();
299
+ // Current bundled subdirectory extensions
300
+ const currentSourceDirs = new Set();
287
301
  try {
288
302
  if (existsSync(bundledExtensionsDir)) {
289
303
  for (const e of readdirSync(bundledExtensionsDir, { withFileTypes: true })) {
290
304
  if (e.isFile())
291
305
  currentSourceFiles.add(e.name);
306
+ if (e.isDirectory())
307
+ currentSourceDirs.add(e.name);
292
308
  }
293
309
  }
294
310
  }
295
311
  catch { /* non-fatal */ }
296
- const removeIfStale = (fileName) => {
312
+ const removeFileIfStale = (fileName) => {
297
313
  if (currentSourceFiles.has(fileName))
298
314
  return; // still in bundle, not stale
299
315
  const stale = join(extensionsDir, fileName);
@@ -303,17 +319,33 @@ function pruneRemovedBundledExtensions(manifest, agentDir) {
303
319
  }
304
320
  catch { /* non-fatal */ }
305
321
  };
322
+ const removeDirIfStale = (dirName) => {
323
+ if (currentSourceDirs.has(dirName))
324
+ return; // still in bundle, not stale
325
+ const stale = join(extensionsDir, dirName);
326
+ try {
327
+ if (existsSync(stale))
328
+ rmSync(stale, { recursive: true, force: true });
329
+ }
330
+ catch { /* non-fatal */ }
331
+ };
306
332
  if (manifest?.installedExtensionRootFiles) {
307
333
  // Manifest-based: remove previously-installed root files that are no longer bundled
308
334
  for (const prevFile of manifest.installedExtensionRootFiles) {
309
- removeIfStale(prevFile);
335
+ removeFileIfStale(prevFile);
336
+ }
337
+ }
338
+ if (manifest?.installedExtensionDirs) {
339
+ // Manifest-based: remove previously-installed subdirectory extensions that are no longer bundled
340
+ for (const prevDir of manifest.installedExtensionDirs) {
341
+ removeDirIfStale(prevDir);
310
342
  }
311
343
  }
312
344
  // Always remove known stale files regardless of manifest state.
313
345
  // These were installed by pre-manifest versions so they may not appear in
314
346
  // installedExtensionRootFiles even when a manifest exists.
315
347
  // env-utils.js was moved from extensions/ root → gsd/ in v2.39.x (#1634)
316
- removeIfStale('env-utils.js');
348
+ removeFileIfStale('env-utils.js');
317
349
  }
318
350
  /**
319
351
  * Syncs all bundled resources to agentDir (~/.gsd/agent/) on every launch.
@@ -416,5 +448,6 @@ export function buildResourceLoader(agentDir) {
416
448
  return new DefaultResourceLoader({
417
449
  agentDir,
418
450
  additionalExtensionPaths: piExtensionPaths,
451
+ bundledExtensionNames: bundledKeys,
419
452
  });
420
453
  }
@@ -83,6 +83,15 @@ export function createAsyncBashTool(getManager, getCwd) {
83
83
  */
84
84
  function executeBashInBackground(command, cwd, signal, timeout) {
85
85
  return new Promise((resolve, reject) => {
86
+ let settled = false;
87
+ const safeResolve = (value) => { if (!settled) {
88
+ settled = true;
89
+ resolve(value);
90
+ } };
91
+ const safeReject = (err) => { if (!settled) {
92
+ settled = true;
93
+ reject(err);
94
+ } };
86
95
  const { shell, args } = getShellConfig();
87
96
  const resolvedCommand = sanitizeCommand(command);
88
97
  const child = spawn(shell, [...args, resolvedCommand], {
@@ -93,11 +102,42 @@ function executeBashInBackground(command, cwd, signal, timeout) {
93
102
  });
94
103
  let timedOut = false;
95
104
  let timeoutHandle;
105
+ let sigkillHandle;
106
+ let hardDeadlineHandle;
107
+ /** Grace period (ms) between SIGTERM and SIGKILL. */
108
+ const SIGKILL_GRACE_MS = 5_000;
109
+ /** Hard deadline (ms) after SIGKILL to force-resolve the promise. */
110
+ const HARD_DEADLINE_MS = 3_000;
96
111
  if (timeout !== undefined && timeout > 0) {
97
112
  timeoutHandle = setTimeout(() => {
98
113
  timedOut = true;
99
114
  if (child.pid)
100
115
  killTree(child.pid);
116
+ // If the process ignores SIGTERM, escalate to SIGKILL
117
+ sigkillHandle = setTimeout(() => {
118
+ if (child.pid) {
119
+ try {
120
+ process.kill(-child.pid, "SIGKILL");
121
+ }
122
+ catch { /* ignore */ }
123
+ try {
124
+ process.kill(child.pid, "SIGKILL");
125
+ }
126
+ catch { /* ignore */ }
127
+ }
128
+ // Hard deadline: if even SIGKILL doesn't trigger 'close',
129
+ // force-resolve so the job doesn't hang forever (#2186).
130
+ hardDeadlineHandle = setTimeout(() => {
131
+ const output = Buffer.concat(chunks).toString("utf-8");
132
+ safeResolve(output
133
+ ? `${output}\n\nCommand timed out after ${timeout} seconds (force-killed)`
134
+ : `Command timed out after ${timeout} seconds (force-killed)`);
135
+ }, HARD_DEADLINE_MS);
136
+ if (typeof hardDeadlineHandle === "object" && "unref" in hardDeadlineHandle)
137
+ hardDeadlineHandle.unref();
138
+ }, SIGKILL_GRACE_MS);
139
+ if (typeof sigkillHandle === "object" && "unref" in sigkillHandle)
140
+ sigkillHandle.unref();
101
141
  }, timeout * 1000);
102
142
  }
103
143
  const chunks = [];
@@ -139,23 +179,31 @@ function executeBashInBackground(command, cwd, signal, timeout) {
139
179
  child.on("error", (err) => {
140
180
  if (timeoutHandle)
141
181
  clearTimeout(timeoutHandle);
182
+ if (sigkillHandle)
183
+ clearTimeout(sigkillHandle);
184
+ if (hardDeadlineHandle)
185
+ clearTimeout(hardDeadlineHandle);
142
186
  signal.removeEventListener("abort", onAbort);
143
- reject(err);
187
+ safeReject(err);
144
188
  });
145
189
  child.on("close", (code) => {
146
190
  if (timeoutHandle)
147
191
  clearTimeout(timeoutHandle);
192
+ if (sigkillHandle)
193
+ clearTimeout(sigkillHandle);
194
+ if (hardDeadlineHandle)
195
+ clearTimeout(hardDeadlineHandle);
148
196
  signal.removeEventListener("abort", onAbort);
149
197
  if (spillStream)
150
198
  spillStream.end();
151
199
  if (signal.aborted) {
152
200
  const output = Buffer.concat(chunks).toString("utf-8");
153
- resolve(output ? `${output}\n\nCommand aborted` : "Command aborted");
201
+ safeResolve(output ? `${output}\n\nCommand aborted` : "Command aborted");
154
202
  return;
155
203
  }
156
204
  if (timedOut) {
157
205
  const output = Buffer.concat(chunks).toString("utf-8");
158
- resolve(output ? `${output}\n\nCommand timed out after ${timeout} seconds` : `Command timed out after ${timeout} seconds`);
206
+ safeResolve(output ? `${output}\n\nCommand timed out after ${timeout} seconds` : `Command timed out after ${timeout} seconds`);
159
207
  return;
160
208
  }
161
209
  const fullOutput = Buffer.concat(chunks).toString("utf-8");
@@ -176,7 +224,7 @@ function executeBashInBackground(command, cwd, signal, timeout) {
176
224
  if (code !== 0 && code !== null) {
177
225
  text += `\n\nCommand exited with code ${code}`;
178
226
  }
179
- resolve(text);
227
+ safeResolve(text);
180
228
  });
181
229
  });
182
230
  }
@@ -54,6 +54,11 @@ export function createAwaitTool(getManager) {
54
54
  };
55
55
  }
56
56
  }
57
+ // Mark all watched jobs as awaited upfront so the onJobComplete
58
+ // callback (which fires synchronously in the promise .then()) knows
59
+ // to suppress the follow-up message.
60
+ for (const j of watched)
61
+ j.awaited = true;
57
62
  // If all watched jobs are already done, return immediately
58
63
  const running = watched.filter((j) => j.status === "running");
59
64
  if (running.length === 0) {
@@ -34,6 +34,8 @@ export default function AsyncJobs(pi) {
34
34
  latestCwd = ctx.cwd;
35
35
  manager = new AsyncJobManager({
36
36
  onJobComplete: (job) => {
37
+ if (job.awaited)
38
+ return;
37
39
  const statusEmoji = job.status === "completed" ? "done" : "error";
38
40
  const elapsed = ((Date.now() - job.startTime) / 1000).toFixed(1);
39
41
  const output = job.status === "completed"
@@ -12,6 +12,7 @@ import { _clearCurrentResolve } from "./resolve.js";
12
12
  import { runPreDispatch, runDispatch, runGuards, runUnitPhase, runFinalize, } from "./phases.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
14
  import { isInfrastructureError } from "./infra-errors.js";
15
+ import { resolveEngine } from "../engine-resolver.js";
15
16
  /**
16
17
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
17
18
  * runUnit → finalize → repeat. Exits when s.active becomes false or a
@@ -82,6 +83,85 @@ export async function autoLoop(ctx, pi, s, deps) {
82
83
  const ic = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
83
84
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
84
85
  let iterData;
86
+ // ── Custom engine path ──────────────────────────────────────────────
87
+ // When activeEngineId is a non-dev value, bypass runPreDispatch and
88
+ // runDispatch entirely — the custom engine drives its own state via
89
+ // GRAPH.yaml. Shares runGuards and runUnitPhase with the dev path.
90
+ // After unit execution, verifies then reconciles via the engine layer.
91
+ //
92
+ // GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
93
+ // to the dev path below.
94
+ if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
95
+ debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
96
+ const { engine, policy } = resolveEngine({
97
+ activeEngineId: s.activeEngineId,
98
+ activeRunDir: s.activeRunDir,
99
+ });
100
+ const engineState = await engine.deriveState(s.basePath);
101
+ if (engineState.isComplete) {
102
+ await deps.stopAuto(ctx, pi, "Workflow complete");
103
+ break;
104
+ }
105
+ debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
106
+ const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
107
+ if (dispatch.action === "stop") {
108
+ await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
109
+ break;
110
+ }
111
+ if (dispatch.action === "skip") {
112
+ continue;
113
+ }
114
+ // dispatch.action === "dispatch"
115
+ const step = dispatch.step;
116
+ const gsdState = await deps.deriveState(s.basePath);
117
+ iterData = {
118
+ unitType: step.unitType,
119
+ unitId: step.unitId,
120
+ prompt: step.prompt,
121
+ finalPrompt: step.prompt,
122
+ pauseAfterUatDispatch: false,
123
+ observabilityIssues: [],
124
+ state: gsdState,
125
+ mid: s.currentMilestoneId ?? "workflow",
126
+ midTitle: "Workflow",
127
+ isRetry: false,
128
+ previousTier: undefined,
129
+ };
130
+ // ── Progress widget (mirrors dev path in runDispatch) ──
131
+ deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
132
+ // ── Guards (shared with dev path) ──
133
+ const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
134
+ if (guardsResult.action === "break")
135
+ break;
136
+ // ── Unit execution (shared with dev path) ──
137
+ const unitPhaseResult = await runUnitPhase(ic, iterData, loopState);
138
+ if (unitPhaseResult.action === "break")
139
+ break;
140
+ // ── Verify first, then reconcile (only mark complete on pass) ──
141
+ debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
142
+ const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
143
+ if (verifyResult === "pause") {
144
+ await deps.pauseAuto(ctx, pi);
145
+ break;
146
+ }
147
+ if (verifyResult === "retry") {
148
+ debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId });
149
+ continue;
150
+ }
151
+ // Verification passed — mark step complete
152
+ debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
153
+ await engine.reconcile(engineState, {
154
+ unitType: iterData.unitType,
155
+ unitId: iterData.unitId,
156
+ startedAt: s.currentUnit?.startedAt ?? Date.now(),
157
+ finishedAt: Date.now(),
158
+ });
159
+ deps.clearUnitTimeout();
160
+ consecutiveErrors = 0;
161
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
162
+ debugLog("autoLoop", { phase: "iteration-complete", iteration });
163
+ continue;
164
+ }
85
165
  if (!sidecarItem) {
86
166
  // ── Phase 1: Pre-dispatch ─────────────────────────────────────────
87
167
  const preDispatchResult = await runPreDispatch(ic, loopState);
@@ -165,9 +165,7 @@ export async function runPreDispatch(ic, loopState) {
165
165
  midTitle = state.activeMilestone?.title;
166
166
  if (mid) {
167
167
  if (deps.getIsolationMode() !== "none") {
168
- deps.captureIntegrationBranch(s.basePath, mid, {
169
- commitDocs: prefs?.git?.commit_docs,
170
- });
168
+ deps.captureIntegrationBranch(s.basePath, mid);
171
169
  }
172
170
  deps.resolver.enterMilestone(mid, ctx.ui);
173
171
  }
@@ -776,8 +774,8 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
776
774
  if (s.currentUnitRouting) {
777
775
  deps.recordOutcome(unitType, s.currentUnitRouting.tier, true);
778
776
  }
779
- const isHookUnit = unitType.startsWith("hook/");
780
- const artifactVerified = isHookUnit ||
777
+ const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
778
+ const artifactVerified = skipArtifactVerification ||
781
779
  deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
782
780
  if (artifactVerified) {
783
781
  s.completedUnits.push({
@@ -27,6 +27,8 @@ export class AutoSession {
27
27
  paused = false;
28
28
  stepMode = false;
29
29
  verbose = false;
30
+ activeEngineId = null;
31
+ activeRunDir = null;
30
32
  cmdCtx = null;
31
33
  // ── Paths ────────────────────────────────────────────────────────────────
32
34
  basePath = "";
@@ -113,6 +115,8 @@ export class AutoSession {
113
115
  this.paused = false;
114
116
  this.stepMode = false;
115
117
  this.verbose = false;
118
+ this.activeEngineId = null;
119
+ this.activeRunDir = null;
116
120
  this.cmdCtx = null;
117
121
  // Paths
118
122
  this.basePath = "";
@@ -156,6 +160,8 @@ export class AutoSession {
156
160
  paused: this.paused,
157
161
  stepMode: this.stepMode,
158
162
  basePath: this.basePath,
163
+ activeEngineId: this.activeEngineId,
164
+ activeRunDir: this.activeRunDir,
159
165
  currentMilestoneId: this.currentMilestoneId,
160
166
  currentUnit: this.currentUnit,
161
167
  completedUnits: this.completedUnits.length,
@@ -46,6 +46,7 @@ export function unitVerb(unitType) {
46
46
  case "rewrite-docs": return "rewriting";
47
47
  case "reassess-roadmap": return "reassessing";
48
48
  case "run-uat": return "running UAT";
49
+ case "custom-step": return "executing workflow step";
49
50
  default: return unitType;
50
51
  }
51
52
  }
@@ -64,6 +65,7 @@ export function unitPhaseLabel(unitType) {
64
65
  case "rewrite-docs": return "REWRITE";
65
66
  case "reassess-roadmap": return "REASSESS";
66
67
  case "run-uat": return "UAT";
68
+ case "custom-step": return "WORKFLOW";
67
69
  default: return unitType.toUpperCase();
68
70
  }
69
71
  }
@@ -337,7 +337,7 @@ function formatSkillActivationBlock(skillNames) {
337
337
  }
338
338
  export function buildSkillActivationBlock(params) {
339
339
  const prefs = params.preferences ?? loadEffectiveGSDPreferences()?.preferences;
340
- const contextTokens = tokenizeSkillContext(params.milestoneId, params.milestoneTitle, params.sliceId, params.sliceTitle, params.taskId, params.taskTitle, ...(params.extraContext ?? []), params.taskPlanContent ?? undefined);
340
+ const contextTokens = tokenizeSkillContext(params.milestoneId, params.milestoneTitle, params.sliceId, params.sliceTitle, params.taskId, params.taskTitle);
341
341
  const visibleSkills = (typeof getLoadedSkills === 'function' ? getLoadedSkills() : []).filter(skill => !skill.disableModelInvocation);
342
342
  const installedNames = new Set(visibleSkills.map(skill => normalizeSkillReference(skill.name)));
343
343
  const avoided = new Set(resolvePreferenceSkillNames(prefs?.avoid_skills ?? [], params.base));
@@ -364,11 +364,6 @@ export function buildSkillActivationBlock(params) {
364
364
  // Non-fatal — malformed task plan should not break prompt construction
365
365
  }
366
366
  }
367
- for (const skill of visibleSkills) {
368
- if (skillMatchesContext(skill, contextTokens)) {
369
- matched.add(normalizeSkillReference(skill.name));
370
- }
371
- }
372
367
  const ordered = [...matched]
373
368
  .filter(name => installedNames.has(name) && !avoided.has(name))
374
369
  .sort();
@@ -846,11 +841,7 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
846
841
  // Build executor context constraints from the budget engine
847
842
  const executorContextConstraints = formatExecutorConstraints();
848
843
  const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
849
- const prefs = loadEffectiveGSDPreferences();
850
- const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
851
- const commitInstruction = commitDocsEnabled
852
- ? `Commit the plan files only: \`git add ${relSlicePath(base, mid, sid)}/ .gsd/DECISIONS.md .gitignore && git commit -m "docs(${sid}): add slice plan"\`. Do not stage .gsd/STATE.md or other runtime files — the system manages those.`
853
- : "Do not commit — planning docs are not tracked in git for this project.";
844
+ const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
854
845
  return loadPrompt("plan-slice", {
855
846
  workingDirectory: base,
856
847
  milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
@@ -1281,11 +1272,7 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
1281
1272
  catch {
1282
1273
  // Non-fatal — captures module may not be available
1283
1274
  }
1284
- const reassessPrefs = loadEffectiveGSDPreferences();
1285
- const reassessCommitDocsEnabled = reassessPrefs?.preferences?.git?.commit_docs !== false;
1286
- const reassessCommitInstruction = reassessCommitDocsEnabled
1287
- ? `Commit: \`docs(${mid}): reassess roadmap after ${completedSliceId}\`. Stage only the .gsd/milestones/ files you changed — do not stage .gsd/STATE.md or other runtime files.`
1288
- : "Do not commit — planning docs are not tracked in git for this project.";
1275
+ const reassessCommitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
1289
1276
  return loadPrompt("reassess-roadmap", {
1290
1277
  workingDirectory: base,
1291
1278
  milestoneId: mid,
@@ -93,23 +93,20 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
93
93
  // ensureGitignore checks for git-tracked .gsd/ files and skips the
94
94
  // ".gsd" pattern if the project intentionally tracks .gsd/ in git.
95
95
  const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
96
- const commitDocs = gitPrefs?.commit_docs;
97
96
  const manageGitignore = gitPrefs?.manage_gitignore;
98
- ensureGitignore(base, { commitDocs, manageGitignore });
97
+ ensureGitignore(base, { manageGitignore });
99
98
  if (manageGitignore !== false)
100
99
  untrackRuntimeFiles(base);
101
100
  // Bootstrap .gsd/ if it doesn't exist
102
101
  const gsdDir = join(base, ".gsd");
103
102
  if (!existsSync(gsdDir)) {
104
103
  mkdirSync(join(gsdDir, "milestones"), { recursive: true });
105
- if (commitDocs !== false) {
106
- try {
107
- nativeAddAll(base);
108
- nativeCommit(base, "chore: init gsd");
109
- }
110
- catch {
111
- /* nothing to commit */
112
- }
104
+ try {
105
+ nativeAddAll(base);
106
+ nativeCommit(base, "chore: init gsd");
107
+ }
108
+ catch {
109
+ /* nothing to commit */
113
110
  }
114
111
  }
115
112
  // Initialize GitServiceImpl
@@ -347,7 +344,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
347
344
  // Capture integration branch
348
345
  if (s.currentMilestoneId) {
349
346
  if (getIsolationMode() !== "none") {
350
- captureIntegrationBranch(base, s.currentMilestoneId, { commitDocs });
347
+ captureIntegrationBranch(base, s.currentMilestoneId);
351
348
  }
352
349
  setActiveMilestoneId(base, s.currentMilestoneId);
353
350
  }
@@ -161,6 +161,18 @@ export function isAutoActive() {
161
161
  export function isAutoPaused() {
162
162
  return s.paused;
163
163
  }
164
+ export function setActiveEngineId(id) {
165
+ s.activeEngineId = id;
166
+ }
167
+ export function getActiveEngineId() {
168
+ return s.activeEngineId;
169
+ }
170
+ export function setActiveRunDir(runDir) {
171
+ s.activeRunDir = runDir;
172
+ }
173
+ export function getActiveRunDir() {
174
+ return s.activeRunDir;
175
+ }
164
176
  /**
165
177
  * Return the model captured at auto-mode start for this session.
166
178
  * Used by error-recovery to fall back to the session's own model
@@ -531,6 +543,8 @@ export async function pauseAuto(ctx, _pi) {
531
543
  stepMode: s.stepMode,
532
544
  pausedAt: new Date().toISOString(),
533
545
  sessionFile: s.pausedSessionFile,
546
+ activeEngineId: s.activeEngineId,
547
+ activeRunDir: s.activeRunDir,
534
548
  };
535
549
  const runtimeDir = join(gsdRoot(s.originalBasePath || s.basePath), "runtime");
536
550
  mkdirSync(runtimeDir, { recursive: true });
@@ -725,7 +739,20 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
725
739
  const pausedPath = join(gsdRoot(base), "runtime", "paused-session.json");
726
740
  if (existsSync(pausedPath)) {
727
741
  const meta = JSON.parse(readFileSync(pausedPath, "utf-8"));
728
- if (meta.milestoneId) {
742
+ if (meta.activeEngineId && meta.activeEngineId !== "dev") {
743
+ // Custom workflow resume — restore engine state
744
+ s.activeEngineId = meta.activeEngineId;
745
+ s.activeRunDir = meta.activeRunDir ?? null;
746
+ s.originalBasePath = meta.originalBasePath || base;
747
+ s.stepMode = meta.stepMode ?? requestedStepMode;
748
+ s.paused = true;
749
+ try {
750
+ unlinkSync(pausedPath);
751
+ }
752
+ catch { /* non-fatal */ }
753
+ ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
754
+ }
755
+ else if (meta.milestoneId) {
729
756
  // Validate the milestone still exists and isn't already complete (#1664).
730
757
  const mDir = resolveMilestonePath(base, meta.milestoneId);
731
758
  const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");