@opengsd/gsd-core 1.10.0 → 1.12.0

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 (544) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +12 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-doc-synthesizer.md +2 -4
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +22 -14
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +5 -3
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +23 -2
  15. package/agents/gsd-plan-checker.md +50 -53
  16. package/agents/gsd-planner.md +50 -50
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +63 -4
  21. package/agents/gsd-ui-researcher.md +41 -3
  22. package/agents/gsd-user-profiler.md +3 -0
  23. package/agents/gsd-verifier.md +13 -4
  24. package/bin/install.js +1448 -1103
  25. package/commands/gsd/code-review.md +1 -1
  26. package/commands/gsd/discuss-phase.md +1 -1
  27. package/commands/gsd/execute-phase.md +1 -1
  28. package/commands/gsd/import.md +1 -1
  29. package/commands/gsd/map-codebase.md +1 -1
  30. package/commands/gsd/mempalace-capture.md +1 -1
  31. package/commands/gsd/mempalace-recall.md +1 -1
  32. package/commands/gsd/new-milestone.md +1 -1
  33. package/commands/gsd/quick.md +9 -5
  34. package/commands/gsd/review-backlog.md +2 -1
  35. package/commands/gsd/verify-work.md +1 -1
  36. package/gsd-core/bin/gsd-tools.cjs +1035 -138
  37. package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
  38. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  39. package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
  40. package/gsd-core/bin/lib/api-coverage.cjs +33 -14
  41. package/gsd-core/bin/lib/artifacts.cjs +5 -0
  42. package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
  43. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  44. package/gsd-core/bin/lib/audit.cjs +1026 -268
  45. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  46. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  47. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  48. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  49. package/gsd-core/bin/lib/capability-registry.cjs +845 -130
  50. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  51. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  52. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  53. package/gsd-core/bin/lib/capability-validator.cjs +700 -40
  54. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  55. package/gsd-core/bin/lib/check-command-router.cjs +216 -42
  56. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  57. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  58. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  59. package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
  60. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  61. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  62. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  63. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  64. package/gsd-core/bin/lib/commands.cjs +1172 -108
  65. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  66. package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
  67. package/gsd-core/bin/lib/config-loader.cjs +187 -23
  68. package/gsd-core/bin/lib/config.cjs +102 -3
  69. package/gsd-core/bin/lib/configuration.cjs +129 -37
  70. package/gsd-core/bin/lib/core-utils.cjs +208 -33
  71. package/gsd-core/bin/lib/decisions.cjs +23 -0
  72. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  73. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  74. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  75. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  76. package/gsd-core/bin/lib/frontmatter.cjs +899 -229
  77. package/gsd-core/bin/lib/gap-checker.cjs +95 -10
  78. package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
  79. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  80. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  81. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  82. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
  83. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  84. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  85. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  86. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
  87. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  88. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  89. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
  90. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  91. package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
  92. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  93. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  94. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  95. package/gsd-core/bin/lib/init.cjs +439 -168
  96. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  97. package/gsd-core/bin/lib/install-engine.cjs +811 -259
  98. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  99. package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
  100. package/gsd-core/bin/lib/install-profiles.cjs +212 -61
  101. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  102. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  103. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  104. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  105. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  106. package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
  107. package/gsd-core/bin/lib/intel.cjs +101 -26
  108. package/gsd-core/bin/lib/io.cjs +170 -15
  109. package/gsd-core/bin/lib/learnings.cjs +85 -14
  110. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  111. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  112. package/gsd-core/bin/lib/markdown-table.cjs +183 -22
  113. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  114. package/gsd-core/bin/lib/milestone.cjs +842 -73
  115. package/gsd-core/bin/lib/model-catalog.cjs +232 -16
  116. package/gsd-core/bin/lib/model-resolver.cjs +193 -68
  117. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  118. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  119. package/gsd-core/bin/lib/pattern.cjs +122 -0
  120. package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
  121. package/gsd-core/bin/lib/phase-id.cjs +514 -40
  122. package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
  123. package/gsd-core/bin/lib/phase-locator.cjs +262 -34
  124. package/gsd-core/bin/lib/phase.cjs +1038 -214
  125. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  126. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  127. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  128. package/gsd-core/bin/lib/plan-scan.cjs +98 -3
  129. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  130. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  131. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  132. package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
  133. package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
  134. package/gsd-core/bin/lib/probe-core.cjs +5 -2
  135. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  136. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  137. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  138. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  139. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
  140. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  141. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
  142. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  143. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  144. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  145. package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
  146. package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
  147. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  148. package/gsd-core/bin/lib/roadmap.cjs +442 -96
  149. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
  150. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  151. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
  152. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  153. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  154. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
  155. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  156. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  157. package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
  158. package/gsd-core/bin/lib/security.cjs +104 -5
  159. package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
  160. package/gsd-core/bin/lib/smart-entry.cjs +133 -23
  161. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  162. package/gsd-core/bin/lib/state-command-router.cjs +52 -19
  163. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  164. package/gsd-core/bin/lib/state-document.cjs +338 -8
  165. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  166. package/gsd-core/bin/lib/state-transition.cjs +846 -176
  167. package/gsd-core/bin/lib/state.cjs +2589 -369
  168. package/gsd-core/bin/lib/surface.cjs +33 -11
  169. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  170. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  171. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  172. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  173. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  174. package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
  175. package/gsd-core/bin/lib/uat.cjs +1761 -167
  176. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  177. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  178. package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
  179. package/gsd-core/bin/lib/unusable-input.cjs +37 -0
  180. package/gsd-core/bin/lib/update-context.cjs +8 -2
  181. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  182. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  183. package/gsd-core/bin/lib/validate.cjs +20 -6
  184. package/gsd-core/bin/lib/vendor/README.md +75 -0
  185. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  186. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  187. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  188. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  189. package/gsd-core/bin/lib/verification.cjs +272 -9
  190. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  191. package/gsd-core/bin/lib/verify.cjs +453 -918
  192. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  193. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  194. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  195. package/gsd-core/bin/lib/workstream.cjs +2 -2
  196. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  197. package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
  198. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
  199. package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
  200. package/gsd-core/bin/shared/exit-codes.json +8 -0
  201. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  202. package/gsd-core/bin/shared/model-catalog.json +8 -1
  203. package/gsd-core/references/agent-contracts.md +44 -26
  204. package/gsd-core/references/api-coverage.md +24 -2
  205. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  206. package/gsd-core/references/checkpoints.md +39 -21
  207. package/gsd-core/references/context-budget.md +1 -1
  208. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  209. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  210. package/gsd-core/references/doc-conflict-engine.md +1 -1
  211. package/gsd-core/references/edge-probe.md +8 -0
  212. package/gsd-core/references/execute-mvp-tdd.md +4 -6
  213. package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
  214. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  215. package/gsd-core/references/execute-phase-response-language.md +1 -1
  216. package/gsd-core/references/execute-phase-wave-guard.md +17 -11
  217. package/gsd-core/references/failing-direction.md +78 -0
  218. package/gsd-core/references/gate-prompts.md +1 -1
  219. package/gsd-core/references/git-integration.md +5 -5
  220. package/gsd-core/references/git-planning-commit.md +5 -4
  221. package/gsd-core/references/gsd-run-resolver.md +1 -1
  222. package/gsd-core/references/loop-hook-dispatch.md +61 -2
  223. package/gsd-core/references/model-profiles.md +12 -4
  224. package/gsd-core/references/mvp-concepts.md +9 -9
  225. package/gsd-core/references/nyquist-compliance.md +74 -0
  226. package/gsd-core/references/offer-next.md +3 -5
  227. package/gsd-core/references/phase-argument-parsing.md +3 -3
  228. package/gsd-core/references/planner-failing-direction.md +53 -0
  229. package/gsd-core/references/planner-guidance.md +3 -9
  230. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  231. package/gsd-core/references/planner-preconditions.md +1 -1
  232. package/gsd-core/references/planner-reviews.md +1 -1
  233. package/gsd-core/references/planner-revision.md +1 -1
  234. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  235. package/gsd-core/references/planning-config.md +44 -13
  236. package/gsd-core/references/reviewer-instances.md +31 -0
  237. package/gsd-core/references/revision-loop.md +1 -1
  238. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  239. package/gsd-core/references/specless-probe-fallback.md +1 -1
  240. package/gsd-core/references/tdd.md +1 -3
  241. package/gsd-core/references/ui-brand.md +65 -21
  242. package/gsd-core/references/ui-consideration-probe.md +1 -1
  243. package/gsd-core/references/universal-anti-patterns.md +5 -5
  244. package/gsd-core/references/verifier-phase-gates.md +192 -0
  245. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  246. package/gsd-core/references/verify-mvp-mode.md +2 -2
  247. package/gsd-core/references/workstream-flag.md +33 -17
  248. package/gsd-core/templates/README.md +1 -1
  249. package/gsd-core/templates/SECURITY.md +3 -3
  250. package/gsd-core/templates/UI-SPEC.md +25 -3
  251. package/gsd-core/templates/VALIDATION.md +3 -3
  252. package/gsd-core/templates/discussion-log.md +1 -1
  253. package/gsd-core/templates/phase-prompt.md +5 -4
  254. package/gsd-core/templates/state.md +11 -4
  255. package/gsd-core/templates/verification-report.md +9 -1
  256. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  257. package/gsd-core/workflows/add-backlog.md +1 -1
  258. package/gsd-core/workflows/add-phase.md +3 -3
  259. package/gsd-core/workflows/add-tests.md +3 -8
  260. package/gsd-core/workflows/add-todo.md +1 -1
  261. package/gsd-core/workflows/ai-integration-phase.md +13 -20
  262. package/gsd-core/workflows/audit-fix.md +12 -3
  263. package/gsd-core/workflows/audit-milestone.md +9 -9
  264. package/gsd-core/workflows/audit-uat.md +17 -2
  265. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  266. package/gsd-core/workflows/autonomous.md +11 -27
  267. package/gsd-core/workflows/check-todos.md +1 -1
  268. package/gsd-core/workflows/cleanup.md +64 -5
  269. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
  270. package/gsd-core/workflows/code-review-fix.md +38 -11
  271. package/gsd-core/workflows/code-review.md +159 -52
  272. package/gsd-core/workflows/complete-milestone.md +151 -23
  273. package/gsd-core/workflows/debug.md +12 -8
  274. package/gsd-core/workflows/diagnose-issues.md +47 -15
  275. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  276. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
  277. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  278. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  279. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  280. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  281. package/gsd-core/workflows/discuss-phase.md +1 -1
  282. package/gsd-core/workflows/do.md +3 -6
  283. package/gsd-core/workflows/docs-update.md +5 -4
  284. package/gsd-core/workflows/edit-phase.md +27 -2
  285. package/gsd-core/workflows/eval-review.md +7 -14
  286. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
  288. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  289. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  290. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  291. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
  292. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  293. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  294. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  295. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  296. package/gsd-core/workflows/execute-phase.md +72 -100
  297. package/gsd-core/workflows/execute-plan.md +52 -15
  298. package/gsd-core/workflows/explore.md +131 -4
  299. package/gsd-core/workflows/extract-learnings.md +1 -1
  300. package/gsd-core/workflows/fast.md +10 -2
  301. package/gsd-core/workflows/forensics.md +1 -1
  302. package/gsd-core/workflows/graduation.md +5 -5
  303. package/gsd-core/workflows/health.md +76 -10
  304. package/gsd-core/workflows/import.md +18 -15
  305. package/gsd-core/workflows/inbox.md +4 -5
  306. package/gsd-core/workflows/ingest-docs.md +49 -16
  307. package/gsd-core/workflows/insert-phase.md +5 -5
  308. package/gsd-core/workflows/list-seeds.md +5 -3
  309. package/gsd-core/workflows/list-workspaces.md +1 -1
  310. package/gsd-core/workflows/manager.md +12 -23
  311. package/gsd-core/workflows/map-codebase.md +1 -1
  312. package/gsd-core/workflows/milestone-summary.md +1 -1
  313. package/gsd-core/workflows/mvp-phase.md +8 -5
  314. package/gsd-core/workflows/new-milestone.md +22 -29
  315. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  316. package/gsd-core/workflows/new-project.md +26 -40
  317. package/gsd-core/workflows/new-workspace.md +1 -1
  318. package/gsd-core/workflows/next.md +14 -2
  319. package/gsd-core/workflows/pause-work.md +1 -1
  320. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  321. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  322. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  323. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  324. package/gsd-core/workflows/plan-phase.md +162 -59
  325. package/gsd-core/workflows/plan-review-convergence.md +96 -11
  326. package/gsd-core/workflows/plant-seed.md +2 -2
  327. package/gsd-core/workflows/pr-branch.md +187 -51
  328. package/gsd-core/workflows/profile-user.md +16 -14
  329. package/gsd-core/workflows/progress.md +61 -18
  330. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  331. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
  332. package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
  333. package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
  334. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  335. package/gsd-core/workflows/quick.md +55 -44
  336. package/gsd-core/workflows/remove-phase.md +4 -4
  337. package/gsd-core/workflows/remove-workspace.md +2 -2
  338. package/gsd-core/workflows/resume-project.md +8 -12
  339. package/gsd-core/workflows/review.md +219 -20
  340. package/gsd-core/workflows/scan.md +1 -1
  341. package/gsd-core/workflows/secure-phase.md +3 -3
  342. package/gsd-core/workflows/session-report.md +2 -1
  343. package/gsd-core/workflows/settings-advanced.md +7 -9
  344. package/gsd-core/workflows/settings-integrations.md +64 -31
  345. package/gsd-core/workflows/settings.md +69 -7
  346. package/gsd-core/workflows/ship.md +116 -50
  347. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  348. package/gsd-core/workflows/sketch.md +12 -18
  349. package/gsd-core/workflows/smart-entry.md +3 -5
  350. package/gsd-core/workflows/spec-phase.md +53 -13
  351. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  352. package/gsd-core/workflows/spike.md +20 -31
  353. package/gsd-core/workflows/stats.md +2 -2
  354. package/gsd-core/workflows/sync-skills.md +64 -9
  355. package/gsd-core/workflows/thread.md +11 -7
  356. package/gsd-core/workflows/transition.md +49 -14
  357. package/gsd-core/workflows/ui-phase.md +15 -21
  358. package/gsd-core/workflows/ui-review.md +8 -12
  359. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  360. package/gsd-core/workflows/undo.md +8 -16
  361. package/gsd-core/workflows/update.md +7 -11
  362. package/gsd-core/workflows/validate-phase.md +3 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  365. package/gsd-core/workflows/verify-work.md +66 -25
  366. package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
  367. package/hooks/dist/gsd-check-update-worker.js +56 -13
  368. package/hooks/dist/gsd-check-update.js +19 -1
  369. package/hooks/dist/gsd-config-reload.js +18 -12
  370. package/hooks/dist/gsd-context-monitor.js +19 -10
  371. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  372. package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
  373. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  374. package/hooks/dist/gsd-cursor-stop.js +2 -1
  375. package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
  376. package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
  377. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  378. package/hooks/dist/gsd-graphify-update.sh +22 -18
  379. package/hooks/dist/gsd-node-runner.sh +76 -0
  380. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  381. package/hooks/dist/gsd-prompt-guard.js +37 -27
  382. package/hooks/dist/gsd-read-guard.js +16 -7
  383. package/hooks/dist/gsd-read-injection-scanner.js +55 -32
  384. package/hooks/dist/gsd-session-state.sh +1 -0
  385. package/hooks/dist/gsd-statusline.js +231 -24
  386. package/hooks/dist/gsd-update-banner.js +22 -1
  387. package/hooks/dist/gsd-validate-commit.sh +80 -6
  388. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  389. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  390. package/hooks/dist/gsd-workflow-guard.js +162 -46
  391. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  392. package/hooks/dist/gsd-write-guard.js +35 -25
  393. package/hooks/dist/lib/cli-exit.js +560 -0
  394. package/hooks/dist/lib/exit-code-registry.js +98 -0
  395. package/hooks/dist/lib/git-cmd.js +92 -59
  396. package/hooks/dist/lib/git-probe.js +84 -0
  397. package/hooks/dist/lib/hook-exit.js +81 -0
  398. package/hooks/dist/lib/injection-patterns.js +45 -0
  399. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  400. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  401. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  402. package/hooks/gsd-agent-isolation-guard.js +158 -30
  403. package/hooks/gsd-check-update-worker.js +56 -13
  404. package/hooks/gsd-check-update.js +19 -1
  405. package/hooks/gsd-config-reload.js +18 -12
  406. package/hooks/gsd-context-monitor.js +19 -10
  407. package/hooks/gsd-cursor-post-tool.js +3 -1
  408. package/hooks/gsd-cursor-pre-tool.js +2 -3
  409. package/hooks/gsd-cursor-session-start.js +2 -1
  410. package/hooks/gsd-cursor-stop.js +2 -1
  411. package/hooks/gsd-cursor-subagent-start.js +83 -3
  412. package/hooks/gsd-cursor-subagent-stop.js +6 -3
  413. package/hooks/gsd-ensure-canonical-path.js +2 -1
  414. package/hooks/gsd-graphify-update.sh +22 -18
  415. package/hooks/gsd-node-runner.sh +76 -0
  416. package/hooks/gsd-phase-boundary.sh +1 -0
  417. package/hooks/gsd-prompt-guard.js +37 -27
  418. package/hooks/gsd-read-guard.js +16 -7
  419. package/hooks/gsd-read-injection-scanner.js +55 -32
  420. package/hooks/gsd-session-state.sh +1 -0
  421. package/hooks/gsd-statusline.js +231 -24
  422. package/hooks/gsd-update-banner.js +22 -1
  423. package/hooks/gsd-validate-commit.sh +80 -6
  424. package/hooks/gsd-windsurf-pre-command.js +16 -11
  425. package/hooks/gsd-windsurf-pre-write.js +22 -13
  426. package/hooks/gsd-workflow-guard.js +162 -46
  427. package/hooks/gsd-worktree-path-guard.js +36 -21
  428. package/hooks/gsd-write-guard.js +35 -25
  429. package/hooks/lib/cli-exit.js +560 -0
  430. package/hooks/lib/exit-code-registry.js +98 -0
  431. package/hooks/lib/git-cmd.js +92 -59
  432. package/hooks/lib/git-probe.js +84 -0
  433. package/hooks/lib/hook-exit.js +81 -0
  434. package/hooks/lib/injection-patterns.js +45 -0
  435. package/hooks/lib/isolation-deny-reason.js +39 -0
  436. package/hooks/lib/isolation-sentinel.js +9 -0
  437. package/hooks/managed-hooks-registry.cjs +3 -0
  438. package/package.json +28 -11
  439. package/pi/gsd.cjs +19 -5
  440. package/scripts/base64-scan.sh +74 -12
  441. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  442. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  443. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  444. package/scripts/build-hooks.js +5 -0
  445. package/scripts/changeset/lint.cjs +60 -5
  446. package/scripts/check-alias-drift.cjs +7 -43
  447. package/scripts/check-contract-drift.cjs +297 -0
  448. package/scripts/check-glossary-refs.cjs +77 -15
  449. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  450. package/scripts/ci-check-job-near-cap.cjs +49 -0
  451. package/scripts/ci-pr-mergeability.cjs +262 -0
  452. package/scripts/ci-test-scope.cjs +64 -14
  453. package/scripts/ci-timeout-report.cjs +230 -0
  454. package/scripts/command-contract-helpers.cjs +903 -1
  455. package/scripts/docs-guard-registry.cjs +396 -0
  456. package/scripts/gen-adr-index.cjs +728 -38
  457. package/scripts/gen-capability-registry.cjs +11 -21
  458. package/scripts/gen-context-index.cjs +2 -11
  459. package/scripts/gen-exit-code-docs.cjs +318 -0
  460. package/scripts/gen-exit-code-registry.cjs +891 -0
  461. package/scripts/gen-features.cjs +836 -0
  462. package/scripts/gen-health-docs.cjs +390 -0
  463. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  464. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  465. package/scripts/gen-inventory-manifest.cjs +50 -4
  466. package/scripts/gen-loop-host-contract.cjs +138 -25
  467. package/scripts/gen-registry.cjs +3 -14
  468. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  469. package/scripts/gen-state-md-docs.cjs +727 -0
  470. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  471. package/scripts/lib/alias-drift-families.cjs +46 -0
  472. package/scripts/lib/ci-job-timing.cjs +72 -0
  473. package/scripts/lib/cli-exit.cjs +546 -44
  474. package/scripts/lib/drift-scan.cjs +308 -0
  475. package/scripts/lib/exit-code-registry.cjs +98 -0
  476. package/scripts/lib/ndjson-reporter.cjs +119 -0
  477. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  478. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  479. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  480. package/scripts/lint-canary-version-leak.cjs +73 -0
  481. package/scripts/lint-command-contract.cjs +96 -13
  482. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  483. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  484. package/scripts/lint-default-flip-documentation.cjs +193 -0
  485. package/scripts/lint-docs-guard-registration.cjs +495 -0
  486. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  487. package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
  488. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  489. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  490. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  491. package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
  492. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  493. package/scripts/lint-milestone-window-drift.cjs +468 -0
  494. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  495. package/scripts/lint-phase-enumeration-drift.cjs +492 -0
  496. package/scripts/lint-plan-count-drift.cjs +318 -0
  497. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  498. package/scripts/lint-planning-prompt-drift.cjs +471 -0
  499. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  500. package/scripts/lint-regression-test-names.cjs +15 -13
  501. package/scripts/lint-removed-but-needed.cjs +488 -0
  502. package/scripts/lint-seam-enforcement.cjs +182 -0
  503. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  504. package/scripts/lint-source-test-name-collision.cjs +241 -0
  505. package/scripts/lint-state-field-drift.cjs +805 -0
  506. package/scripts/lint-state-write-path-drift.cjs +950 -0
  507. package/scripts/lint-test-file-count.allowlist.json +137 -8
  508. package/scripts/lint-test-file-count.cjs +25 -3
  509. package/scripts/lint-unreachable-guard-drift.cjs +830 -0
  510. package/scripts/lint-vendored-deps.cjs +297 -0
  511. package/scripts/mutation-matrix.cjs +599 -50
  512. package/scripts/pr-changed-files.cjs +63 -0
  513. package/scripts/pr-template-policy.cjs +14 -4
  514. package/scripts/prompt-injection-scan.sh +100 -14
  515. package/scripts/require-issue-link-policy.cjs +192 -0
  516. package/scripts/secret-scan.sh +75 -13
  517. package/scripts/select-docs-guards.cjs +56 -0
  518. package/scripts/sync-runtime-launcher.cjs +24 -7
  519. package/skills/gsd-autonomous/SKILL.md +0 -1
  520. package/skills/gsd-code-review/SKILL.md +1 -1
  521. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  522. package/skills/gsd-execute-phase/SKILL.md +1 -2
  523. package/skills/gsd-import/SKILL.md +1 -1
  524. package/skills/gsd-map-codebase/SKILL.md +1 -1
  525. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  526. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  527. package/skills/gsd-new-milestone/SKILL.md +1 -1
  528. package/skills/gsd-next/SKILL.md +0 -1
  529. package/skills/gsd-plan-phase/SKILL.md +0 -1
  530. package/skills/gsd-progress/SKILL.md +0 -1
  531. package/skills/gsd-quick/SKILL.md +9 -5
  532. package/skills/gsd-review-backlog/SKILL.md +2 -1
  533. package/skills/gsd-stats/SKILL.md +0 -1
  534. package/skills/gsd-verify-work/SKILL.md +1 -1
  535. package/vscode/package.json +1 -1
  536. package/bin/lib/ui-safety-gate.cjs +0 -107
  537. package/gsd-core/workflows/discovery-phase.md +0 -298
  538. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  539. package/gsd-core/workflows/verify-phase.md +0 -574
  540. package/scripts/affected-tests-lib.cjs +0 -554
  541. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  542. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  543. package/scripts/run-affected-tests.cjs +0 -7
  544. package/scripts/run-tests.cjs +0 -1051
@@ -13,13 +13,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const node_os_1 = __importDefault(require("node:os"));
16
+ // #2874 (ADR-58 cleanup phase): route the staging functions' fs calls
17
+ // through the installRuntimeArtifacts call tree's injectable seam — see
18
+ // install-fs-adapter.cts's module doc. Resolves to real `node:fs` unless the
19
+ // top-level installRuntimeArtifacts call injected a `deps.fs`. Only the
20
+ // staging functions reachable FROM that call tree are routed
21
+ // (stageSkillsForProfile / stageAgentsForProfile /
22
+ // stageAgentsForRuntimeWithConverter / stageSkillsForRuntimeAsSkills /
23
+ // stageCommandsForRuntimeFlat / buildNamespaceBundleMap) — the profile-marker
24
+ // and manifest-loading helpers below are not on that call tree and keep
25
+ // using real `fs` directly.
26
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
27
+ const installFsAdapter = require("./install-fs-adapter.cjs");
28
+ const { installFs, mkInstallTempDir } = installFsAdapter;
16
29
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
17
30
  // #2322: reuse the existing pure path-containment seam (ADR-1239 Phase C-2)
18
31
  // instead of hand-rolling a new traversal check for capability skill stems.
19
32
  const external_descriptor_trust_cjs_1 = require("./external-descriptor-trust.cjs");
20
33
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
34
  const conversionModule = require("./runtime-artifact-conversion.cjs");
22
- const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _processAttribution, normalizeAgentBodyForRuntime: _normalizeAgentBodyForRuntime, readGsdCommandNames: _readGsdCommandNames, } = conversionModule;
35
+ const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _processAttribution, normalizeAgentBodyForRuntime: _normalizeAgentBodyForRuntime, readGsdCommandNames: _readGsdCommandNames, deriveAgentName: _deriveAgentName, applyAgentFrontmatterExtensions: _applyAgentFrontmatterExtensions, } = conversionModule;
23
36
  // #2995 (epic #1671 Phase 6.4): agent bodies join the fragment model. Markers are
24
37
  // stripped at emit BEFORE any path rewrite or converter runs, so a `.claude/` ->
25
38
  // `.windsurf/` regex can never reach inside a marker attribute and corrupt it —
@@ -124,9 +137,79 @@ function parseCallsAgents(content) {
124
137
  * Also derives calls_agents for each skill by scanning the body text for
125
138
  * `gsd-*` agent name references. Agent stems are stored under the special
126
139
  * key `_calls_agents_<stem>` so they don't conflict with skill stems.
140
+ *
141
+ * #3798: command bodies are thin delegators — the actual `subagent_type=`
142
+ * spawns live in the workflow files each command references
143
+ * (`@…/workflows/<name>.md`). The agent derivation therefore ALSO reads every
144
+ * workflow file the command references (plus the workflow's steps/ fragments
145
+ * when it is split per the progressive-disclosure pattern) and unions their
146
+ * `gsd-*` tokens into the same `_calls_agents_<stem>` set. Without this,
147
+ * tiered profiles omitted agents their own installed skills spawn
148
+ * (gsd-verifier at execute-phase's verify_phase_goal was the filed repro).
149
+ * Over-inclusion fails safe: a tiered profile installing one extra agent
150
+ * costs bytes, never a broken spawn.
127
151
  */
128
152
  const DEFAULT_COMMANDS_DIR = node_path_1.default.resolve(__dirname, '..', '..', '..', 'commands', 'gsd');
129
- function loadSkillsManifest(commandsDir = DEFAULT_COMMANDS_DIR) {
153
+ const DEFAULT_WORKFLOWS_DIR = node_path_1.default.resolve(__dirname, '..', '..', 'workflows');
154
+ /**
155
+ * Collect the `gsd-*` agent tokens from every workflow file a command body
156
+ * references. References are the `workflows/<name>.md` path suffixes the
157
+ * delegating commands embed (`@…/gsd-core/workflows/<name>.md`). A
158
+ * split workflow (workflows/<name>/steps/*.md) contributes its fragments as
159
+ * well, because the parent dispatches into them and the spawns live there.
160
+ */
161
+ function workflowAgentRefs(content, workflowsDir) {
162
+ const refs = content.match(/workflows\/([a-z0-9][a-z0-9-]*)\.md/g) || [];
163
+ const names = [...new Set(refs.map((r) => r.slice('workflows/'.length)))];
164
+ const tokens = new Set();
165
+ for (const name of names) {
166
+ const direct = node_path_1.default.join(workflowsDir, name);
167
+ let body = null;
168
+ try {
169
+ body = node_fs_1.default.readFileSync(direct, 'utf8');
170
+ }
171
+ catch {
172
+ body = null;
173
+ }
174
+ if (body === null)
175
+ continue;
176
+ for (const tok of parseCallsAgents(body))
177
+ tokens.add(tok);
178
+ // Split workflow: union EVERY fragment under the workflow's directory
179
+ // (steps/, modes/, templates/…) — the parent dispatches into these per
180
+ // the progressive-disclosure pattern, and spawns live in all of them
181
+ // (#3798 review: modes/ carried gsd-advisor-researcher's spawn while the
182
+ // parent only named it incidentally in prose).
183
+ const fragDir = node_path_1.default.join(workflowsDir, name.slice(0, -3));
184
+ const walk = (dir) => {
185
+ let frags;
186
+ try {
187
+ frags = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
188
+ }
189
+ catch {
190
+ return;
191
+ }
192
+ for (const frag of frags) {
193
+ const full = node_path_1.default.join(dir, frag.name);
194
+ if (frag.isDirectory()) {
195
+ walk(full);
196
+ continue;
197
+ }
198
+ if (!frag.isFile() || !frag.name.endsWith('.md'))
199
+ continue;
200
+ try {
201
+ const fragBody = node_fs_1.default.readFileSync(full, 'utf8');
202
+ for (const tok of parseCallsAgents(fragBody))
203
+ tokens.add(tok);
204
+ }
205
+ catch { /* unreadable fragment — skip */ }
206
+ }
207
+ };
208
+ walk(fragDir);
209
+ }
210
+ return [...tokens];
211
+ }
212
+ function loadSkillsManifest(commandsDir = DEFAULT_COMMANDS_DIR, workflowsDir = DEFAULT_WORKFLOWS_DIR) {
130
213
  const manifest = new Map();
131
214
  if (!node_fs_1.default.existsSync(commandsDir))
132
215
  return manifest;
@@ -140,9 +223,12 @@ function loadSkillsManifest(commandsDir = DEFAULT_COMMANDS_DIR) {
140
223
  try {
141
224
  const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsDir, entry.name), 'utf8');
142
225
  manifest.set(stem, parseRequires(content));
143
- // Derive agent references from body text
144
- const agentRefs = parseCallsAgents(content);
145
- manifest.set(`_calls_agents_${stem}`, agentRefs);
226
+ // Derive agent references from body text + the workflows it delegates to
227
+ const agentRefs = [
228
+ ...parseCallsAgents(content),
229
+ ...workflowAgentRefs(content, workflowsDir),
230
+ ];
231
+ manifest.set(`_calls_agents_${stem}`, [...new Set(agentRefs)]);
146
232
  }
147
233
  catch {
148
234
  manifest.set(stem, []);
@@ -264,10 +350,41 @@ function resolveProfile({ modes, manifest, _profilesOverride, registry } = {}) {
264
350
  // site to track its own cleanup handle.
265
351
  const STAGED_DIRS = new Set();
266
352
  let exitHandlerRegistered = false;
353
+ // #2874 leak-fix: `cleanupStagedSkills` runs at process exit — AFTER
354
+ // `withInstallFs` has already restored `current` back to the real adapter
355
+ // (install-fs-adapter.cts's module doc, "SYNCHRONOUS-ONLY / RE-ENTRANCY
356
+ // ASSUMPTION" — extended there to reference this map). A dir staged during a
357
+ // fake-adapter call must be cleaned up with THAT SAME fake adapter, not with
358
+ // whatever is ambiently active later, or an exit handler would perform real
359
+ // filesystem IO on a path that only ever existed in the fake's in-memory
360
+ // store. Capturing the adapter OBJECT `installFs()` returns at registration
361
+ // time (not the ambient `current` variable, which changes) means cleanup
362
+ // always replays the exact adapter that created the path. Dirs added to
363
+ // STAGED_DIRS without going through `registerStagedDir` (the deprecated
364
+ // `stageSkillsForMode`, which creates its stage dir via raw `fs.mkdtempSync`
365
+ // and was never on the injectable seam) have no entry here and fall back to
366
+ // real `fs.rmSync` below — the same real fs it always used to create them.
367
+ const STAGED_DIR_ADAPTERS = new Map();
368
+ /**
369
+ * Register a dir just staged through the injectable seam (`installFs()`) for
370
+ * exit-time cleanup, capturing the adapter that staged it alongside the path.
371
+ * See `STAGED_DIR_ADAPTERS`'s comment for why the capture matters.
372
+ */
373
+ function registerStagedDir(dir) {
374
+ STAGED_DIRS.add(dir);
375
+ STAGED_DIR_ADAPTERS.set(dir, installFs());
376
+ ensureExitCleanup();
377
+ }
267
378
  function cleanupStagedSkills() {
268
379
  for (const dir of STAGED_DIRS) {
380
+ const adapter = STAGED_DIR_ADAPTERS.get(dir);
269
381
  try {
270
- node_fs_1.default.rmSync(dir, { recursive: true, force: true });
382
+ if (adapter) {
383
+ adapter.rmSync(dir, { recursive: true, force: true });
384
+ }
385
+ else {
386
+ node_fs_1.default.rmSync(dir, { recursive: true, force: true });
387
+ }
271
388
  }
272
389
  catch {
273
390
  // Best-effort: missing dir or permission error shouldn't crash a
@@ -275,6 +392,7 @@ function cleanupStagedSkills() {
275
392
  }
276
393
  }
277
394
  STAGED_DIRS.clear();
395
+ STAGED_DIR_ADAPTERS.clear();
278
396
  }
279
397
  // Signals we register a cleanup handler for in addition to the natural
280
398
  // 'exit' event. `process.on('exit')` does NOT fire on these — an installer
@@ -303,11 +421,11 @@ function ensureExitCleanup() {
303
421
  function stageSkillsForProfile(srcDir, resolvedProfile) {
304
422
  if (resolvedProfile.skills === '*')
305
423
  return srcDir;
306
- if (!node_fs_1.default.existsSync(srcDir))
424
+ if (!installFs().existsSync(srcDir))
307
425
  return srcDir;
308
- const stageDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'gsd-profile-skills-'));
426
+ const stageDir = mkInstallTempDir('gsd-profile-skills-');
309
427
  try {
310
- const entries = node_fs_1.default.readdirSync(srcDir, { withFileTypes: true });
428
+ const entries = installFs().readdirSync(srcDir, { withFileTypes: true });
311
429
  for (const entry of entries) {
312
430
  if (!entry.isFile())
313
431
  continue;
@@ -316,18 +434,17 @@ function stageSkillsForProfile(srcDir, resolvedProfile) {
316
434
  const stem = entry.name.slice(0, -3);
317
435
  if (!(resolvedProfile.skills).has(stem))
318
436
  continue;
319
- node_fs_1.default.copyFileSync(node_path_1.default.join(srcDir, entry.name), node_path_1.default.join(stageDir, entry.name));
437
+ installFs().copyFileSync(node_path_1.default.join(srcDir, entry.name), node_path_1.default.join(stageDir, entry.name));
320
438
  }
321
439
  }
322
440
  catch (err) {
323
441
  try {
324
- node_fs_1.default.rmSync(stageDir, { recursive: true, force: true });
442
+ installFs().rmSync(stageDir, { recursive: true, force: true });
325
443
  }
326
444
  catch { /* best-effort */ }
327
445
  throw err;
328
446
  }
329
- STAGED_DIRS.add(stageDir);
330
- ensureExitCleanup();
447
+ registerStagedDir(stageDir);
331
448
  return stageDir;
332
449
  }
333
450
  /**
@@ -353,12 +470,12 @@ function stageSkillsForProfile(srcDir, resolvedProfile) {
353
470
  function stageAgentsForProfile(srcAgentsDir, resolvedProfile) {
354
471
  if (resolvedProfile.skills === '*')
355
472
  return srcAgentsDir;
356
- if (!node_fs_1.default.existsSync(srcAgentsDir))
473
+ if (!installFs().existsSync(srcAgentsDir))
357
474
  return srcAgentsDir;
358
- const stageDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'gsd-profile-agents-'));
475
+ const stageDir = mkInstallTempDir('gsd-profile-agents-');
359
476
  try {
360
477
  if (resolvedProfile.agents instanceof Set && resolvedProfile.agents.size > 0) {
361
- const entries = node_fs_1.default.readdirSync(srcAgentsDir, { withFileTypes: true });
478
+ const entries = installFs().readdirSync(srcAgentsDir, { withFileTypes: true });
362
479
  for (const entry of entries) {
363
480
  if (!entry.isFile())
364
481
  continue;
@@ -368,20 +485,19 @@ function stageAgentsForProfile(srcAgentsDir, resolvedProfile) {
368
485
  const stem = entry.name.slice(0, -3);
369
486
  if (!resolvedProfile.agents.has(stem))
370
487
  continue;
371
- node_fs_1.default.copyFileSync(node_path_1.default.join(srcAgentsDir, entry.name), node_path_1.default.join(stageDir, entry.name));
488
+ installFs().copyFileSync(node_path_1.default.join(srcAgentsDir, entry.name), node_path_1.default.join(stageDir, entry.name));
372
489
  }
373
490
  }
374
491
  // If agents is empty Set, we produce an empty stageDir (no agents for this profile)
375
492
  }
376
493
  catch (err) {
377
494
  try {
378
- node_fs_1.default.rmSync(stageDir, { recursive: true, force: true });
495
+ installFs().rmSync(stageDir, { recursive: true, force: true });
379
496
  }
380
497
  catch { /* best-effort */ }
381
498
  throw err;
382
499
  }
383
- STAGED_DIRS.add(stageDir);
384
- ensureExitCleanup();
500
+ registerStagedDir(stageDir);
385
501
  return stageDir;
386
502
  }
387
503
  /**
@@ -395,10 +511,10 @@ function buildNamespaceBundleMap(srcCommandsDir) {
395
511
  const routerStems = new Set();
396
512
  const routerChildren = new Map();
397
513
  const childToRouters = new Map();
398
- if (!node_fs_1.default.existsSync(srcCommandsDir)) {
514
+ if (!installFs().existsSync(srcCommandsDir)) {
399
515
  return { routerStems, routerChildren, childToRouters };
400
516
  }
401
- for (const entry of node_fs_1.default.readdirSync(srcCommandsDir, { withFileTypes: true })) {
517
+ for (const entry of installFs().readdirSync(srcCommandsDir, { withFileTypes: true })) {
402
518
  if (!entry.isFile() || !entry.name.endsWith('.md'))
403
519
  continue;
404
520
  if (!entry.name.startsWith('ns-'))
@@ -406,7 +522,7 @@ function buildNamespaceBundleMap(srcCommandsDir) {
406
522
  const stem = entry.name.slice(0, -3);
407
523
  let children = [];
408
524
  try {
409
- children = parseRequires(node_fs_1.default.readFileSync(node_path_1.default.join(srcCommandsDir, entry.name), 'utf8'));
525
+ children = parseRequires(installFs().readFileSync(node_path_1.default.join(srcCommandsDir, entry.name), 'utf8'));
410
526
  }
411
527
  catch {
412
528
  children = [];
@@ -637,7 +753,7 @@ function readInstalledCapabilitySkill(stem, registry) {
637
753
  * When absent, NOTHING third-party is staged (fail closed).
638
754
  */
639
755
  function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converter, prefix, nested = false, registry) {
640
- if (!node_fs_1.default.existsSync(srcCommandsDir))
756
+ if (!installFs().existsSync(srcCommandsDir))
641
757
  return srcCommandsDir;
642
758
  // Nesting applies to the `full` install AND to any surface whose skill set
643
759
  // still contains every namespace router (a full/reset surface). It must NOT
@@ -660,9 +776,9 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
660
776
  // this call, so the third-party fill-in pass below can enforce "first-party
661
777
  // ALWAYS wins on collision" without re-deriving membership.
662
778
  const firstPartyStems = new Set();
663
- const stageDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'gsd-profile-runtime-skills-'));
779
+ const stageDir = mkInstallTempDir('gsd-profile-runtime-skills-');
664
780
  try {
665
- const entries = node_fs_1.default.readdirSync(srcCommandsDir, { withFileTypes: true });
781
+ const entries = installFs().readdirSync(srcCommandsDir, { withFileTypes: true });
666
782
  for (const entry of entries) {
667
783
  if (!entry.isFile())
668
784
  continue;
@@ -672,15 +788,15 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
672
788
  if (resolvedProfile.skills !== '*' && !(resolvedProfile.skills).has(stem))
673
789
  continue;
674
790
  firstPartyStems.add(stem);
675
- const content = node_fs_1.default.readFileSync(node_path_1.default.join(srcCommandsDir, entry.name), 'utf8');
791
+ const content = installFs().readFileSync(node_path_1.default.join(srcCommandsDir, entry.name), 'utf8');
676
792
  const skillName = `${prefix}${stem}`;
677
793
  const converted = converter(content, skillName);
678
794
  if (doNest && bundles.routerStems.has(stem)) {
679
795
  // Router skill: rewrite its routing table to the nested Read pattern and
680
796
  // emit it as the single top-level bundle entry.
681
797
  const destDir = node_path_1.default.join(stageDir, skillName);
682
- node_fs_1.default.mkdirSync(destDir, { recursive: true });
683
- node_fs_1.default.writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), transformRouterBodyToNested(converted));
798
+ installFs().mkdirSync(destDir, { recursive: true });
799
+ installFs().writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), transformRouterBodyToNested(converted));
684
800
  continue;
685
801
  }
686
802
  if (doNest && bundles.childToRouters.has(stem)) {
@@ -689,16 +805,16 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
689
805
  // top-level eager listing while staying readable by file path (#69).
690
806
  for (const routerStem of bundles.childToRouters.get(stem)) {
691
807
  const destDir = node_path_1.default.join(stageDir, `${prefix}${routerStem}`, 'skills', stem);
692
- node_fs_1.default.mkdirSync(destDir, { recursive: true });
693
- node_fs_1.default.writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), converted);
808
+ installFs().mkdirSync(destDir, { recursive: true });
809
+ installFs().writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), converted);
694
810
  }
695
811
  continue;
696
812
  }
697
813
  // Flat top-level skill (default behaviour; also the unrouted fallback when
698
814
  // nesting is active).
699
815
  const destDir = node_path_1.default.join(stageDir, skillName);
700
- node_fs_1.default.mkdirSync(destDir, { recursive: true });
701
- node_fs_1.default.writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), converted);
816
+ installFs().mkdirSync(destDir, { recursive: true });
817
+ installFs().writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), converted);
702
818
  }
703
819
  // #2322: materialize installed THIRD-PARTY capability skills, bound to
704
820
  // their DECLARING capability via the registry's capabilityClusters view
@@ -740,25 +856,24 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
740
856
  if (!(0, external_descriptor_trust_cjs_1.isPathConfined)(skillName, stageDir))
741
857
  continue; // defense-in-depth
742
858
  const destDir = node_path_1.default.join(stageDir, skillName);
743
- node_fs_1.default.mkdirSync(destDir, { recursive: true });
744
- node_fs_1.default.writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), found.content);
859
+ installFs().mkdirSync(destDir, { recursive: true });
860
+ installFs().writeFileSync(node_path_1.default.join(destDir, 'SKILL.md'), found.content);
745
861
  // #2322 HIGH-3: persist the capability-owned marker so a later prune
746
862
  // pass (surface.cts pruneSkillDirs) can identify — and remove — this
747
863
  // directory even once the owning capability is uninstalled/unsurfaced
748
864
  // and no longer appears in any registry view.
749
- node_fs_1.default.writeFileSync(node_path_1.default.join(destDir, CAPABILITY_SKILL_MARKER), found.capId + '\n', 'utf8');
865
+ installFs().writeFileSync(node_path_1.default.join(destDir, CAPABILITY_SKILL_MARKER), found.capId + '\n', 'utf8');
750
866
  }
751
867
  }
752
868
  }
753
869
  catch (err) {
754
870
  try {
755
- node_fs_1.default.rmSync(stageDir, { recursive: true, force: true });
871
+ installFs().rmSync(stageDir, { recursive: true, force: true });
756
872
  }
757
873
  catch { /* best-effort */ }
758
874
  throw err;
759
875
  }
760
- STAGED_DIRS.add(stageDir);
761
- ensureExitCleanup();
876
+ registerStagedDir(stageDir);
762
877
  return stageDir;
763
878
  }
764
879
  /**
@@ -782,26 +897,57 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
782
897
  * 1. applyAgentPathRewrites (4 base ~/.claude/ regexes; skipped for copilot/antigravity)
783
898
  * 2. processAttribution (Co-Authored-By policy)
784
899
  * 3. converter (runtime-specific frontmatter/body transform)
785
- * 4. normalizeAgentBodyForRuntime (colon→hyphen refs; no-op for trivial group)
900
+ * 4. applyAgentFrontmatterExtensions (#2875 Part 2: effort/disallowedTools,
901
+ * gated by hostBehaviors.agentFrontmatterExtensions — no-op for a runtime
902
+ * that declares nothing, e.g. every non-Claude runtime today)
903
+ * 5. normalizeAgentBodyForRuntime (colon→hyphen refs; no-op for trivial group)
786
904
  * When `agentCtx` is absent, only the converter is applied (backward-compat for
787
905
  * the feat-1173 synthetic-descriptor tests and the copilot/antigravity paths
788
906
  * that handle cross-cutting inside their converters).
789
907
  *
790
908
  * @param srcAgentsDir source agents directory (e.g. agents/)
791
909
  * @param resolvedProfile profile filter from resolveProfile()
792
- * @param converter (content: string, isGlobal?: boolean) → string per-file
793
- * converter; scope-aware converters (copilot/antigravity)
794
- * read isGlobal, single-arg converters ignore it (#1173)
910
+ * @param converter (content: string, isGlobal?: boolean, meta?: {agentName: string}) → string
911
+ * per-file converter; scope-aware converters (copilot/antigravity)
912
+ * read isGlobal, single-arg converters ignore both extra args (#1173).
913
+ * `meta.agentName` (#2875 Part 2) is passed ONLY when `agentCtx` is
914
+ * present, letting a converter close over per-agent config
915
+ * (e.g. kilo/opencode model-override resolution in
916
+ * runtime-artifact-layout.cts's convertedAgentsKind) without widening
917
+ * every OTHER converter's contract — converters that don't declare a
918
+ * 3rd parameter simply never read it.
795
919
  * @param isGlobal install scope passed through to the converter
796
920
  * @param agentCtx optional cross-cutting context (ADR-1235 §1); when absent,
797
921
  * only the converter is applied (backward compat)
798
922
  */
799
923
  function stageAgentsForRuntimeWithConverter(srcAgentsDir, resolvedProfile, converter, isGlobal = false, agentCtx) {
800
- if (!node_fs_1.default.existsSync(srcAgentsDir))
924
+ if (!installFs().existsSync(srcAgentsDir))
801
925
  return srcAgentsDir;
802
- const stageDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'gsd-profile-runtime-agents-'));
926
+ const stageDir = mkInstallTempDir('gsd-profile-runtime-agents-');
927
+ let entries;
928
+ try {
929
+ // #2284/#2875: readdirSync-ing the shipped agents/ source is isolated in
930
+ // its own try/catch so an unreadable source directory (permissions, a
931
+ // corrupted install, or — as the #2284 test suite demonstrates — the
932
+ // fail-closed injection harness) produces the SAME "refusing to install"
933
+ // fail-closed wording every other agents-dir-unreadable path in this
934
+ // codebase uses (bin/install.js's `_resolveAvailableGsdRoles` /
935
+ // `_assertRoleResolvable`), instead of an unrelated raw fs error message
936
+ // escaping uncaught. Hermes's role-dispatch validation depends on the
937
+ // SAME shipped agents/ directory being readable; before this runtime also
938
+ // declared an `agents` kind, this function was never reached on a Hermes
939
+ // install, so an unreadable source here silently surfaced as a raw error
940
+ // rather than the deliberate fail-closed contract #2284 established.
941
+ entries = installFs().readdirSync(srcAgentsDir, { withFileTypes: true });
942
+ }
943
+ catch (err) {
944
+ try {
945
+ installFs().rmSync(stageDir, { recursive: true, force: true });
946
+ }
947
+ catch { /* best-effort */ }
948
+ throw new Error(`stageAgentsForRuntimeWithConverter: could not resolve the shipped agents/ directory "${srcAgentsDir}" to stage — refusing to install (fail-closed, #2284/#2875): ${err.message}`);
949
+ }
803
950
  try {
804
- const entries = node_fs_1.default.readdirSync(srcAgentsDir, { withFileTypes: true });
805
951
  // Resolve cmdNames once per staging call (not per file) for performance.
806
952
  const cmdNames = agentCtx ? _readGsdCommandNames() : [];
807
953
  for (const entry of entries) {
@@ -817,39 +963,44 @@ function stageAgentsForRuntimeWithConverter(srcAgentsDir, resolvedProfile, conve
817
963
  }
818
964
  }
819
965
  const agentSourcePath = node_path_1.default.join(srcAgentsDir, entry.name);
820
- let content = node_fs_1.default.readFileSync(agentSourcePath, 'utf8');
966
+ let content = installFs().readFileSync(agentSourcePath, 'utf8');
821
967
  // #2995: strip gsd:section markers FIRST — before path rewrites, attribution,
822
968
  // and the per-runtime converter. Byte-identical (no-op) for an unmarked agent;
823
969
  // throws loudly naming the file for a malformed marker, never emitting a
824
970
  // half-composed agent.
825
971
  content = _composeWorkflow(content, { sourcePath: agentSourcePath });
826
972
  if (agentCtx) {
973
+ // #2875 Part 2 / row I3: derived exactly as the inline loop does —
974
+ // single-sourced via deriveAgentName (runtime-artifact-conversion.cts).
975
+ const agentName = _deriveAgentName(entry.name);
827
976
  // ADR-1235 §1: pre-converter cross-cutting (matches inline loop order exactly)
828
977
  // Step 1: path rewrites (4 base ~/.claude/ regexes; skipped for copilot/antigravity)
829
978
  content = _applyAgentPathRewrites(content, agentCtx.runtime, agentCtx.pathPrefix);
830
979
  // Step 2: attribution
831
980
  content = _processAttribution(content, agentCtx.attribution);
832
981
  // Step 3: converter (runtime-specific frontmatter/body transform)
833
- content = converter(content, isGlobal);
834
- // Step 4: normalize colon→hyphen refs (no-op for trivial group)
982
+ content = converter(content, isGlobal, { agentName });
983
+ // Step 4: frontmatter extensions (effort/disallowedTools; no-op unless
984
+ // the runtime declares hostBehaviors.agentFrontmatterExtensions)
985
+ content = _applyAgentFrontmatterExtensions(content, { runtime: agentCtx.runtime, agentName, targetDir: agentCtx.targetDir });
986
+ // Step 5: normalize colon→hyphen refs (no-op for trivial group)
835
987
  content = _normalizeAgentBodyForRuntime(content, agentCtx.runtime, cmdNames);
836
988
  }
837
989
  else {
838
990
  // Backward-compat: only apply the converter (no cross-cutting)
839
991
  content = converter(content, isGlobal);
840
992
  }
841
- node_fs_1.default.writeFileSync(node_path_1.default.join(stageDir, entry.name), content, 'utf8');
993
+ installFs().writeFileSync(node_path_1.default.join(stageDir, entry.name), content, 'utf8');
842
994
  }
843
995
  }
844
996
  catch (err) {
845
997
  try {
846
- node_fs_1.default.rmSync(stageDir, { recursive: true, force: true });
998
+ installFs().rmSync(stageDir, { recursive: true, force: true });
847
999
  }
848
1000
  catch { /* best-effort */ }
849
1001
  throw err;
850
1002
  }
851
- STAGED_DIRS.add(stageDir);
852
- ensureExitCleanup();
1003
+ registerStagedDir(stageDir);
853
1004
  return stageDir;
854
1005
  }
855
1006
  /**
@@ -876,11 +1027,11 @@ function stageAgentsForRuntimeWithConverter(srcAgentsDir, resolvedProfile, conve
876
1027
  * @param prefix command name prefix (for converter arg), e.g. 'gsd-'
877
1028
  */
878
1029
  function stageCommandsForRuntimeFlat(srcCommandsDir, resolvedProfile, converter, prefix) {
879
- if (!node_fs_1.default.existsSync(srcCommandsDir))
1030
+ if (!installFs().existsSync(srcCommandsDir))
880
1031
  return srcCommandsDir;
881
- const stageDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'gsd-profile-runtime-commands-'));
1032
+ const stageDir = mkInstallTempDir('gsd-profile-runtime-commands-');
882
1033
  try {
883
- const entries = node_fs_1.default.readdirSync(srcCommandsDir, { withFileTypes: true });
1034
+ const entries = installFs().readdirSync(srcCommandsDir, { withFileTypes: true });
884
1035
  for (const entry of entries) {
885
1036
  if (!entry.isFile())
886
1037
  continue;
@@ -889,24 +1040,23 @@ function stageCommandsForRuntimeFlat(srcCommandsDir, resolvedProfile, converter,
889
1040
  const stem = entry.name.slice(0, -3);
890
1041
  if (resolvedProfile.skills !== '*' && !(resolvedProfile.skills).has(stem))
891
1042
  continue;
892
- const content = node_fs_1.default.readFileSync(node_path_1.default.join(srcCommandsDir, entry.name), 'utf8');
1043
+ const content = installFs().readFileSync(node_path_1.default.join(srcCommandsDir, entry.name), 'utf8');
893
1044
  // Pass the full command name (with prefix) to the converter so it can
894
1045
  // reference the installed command name in the body (e.g. for descriptions).
895
1046
  // The staged file itself is named without the prefix; _copyStaged adds it.
896
1047
  const commandName = `${prefix}${stem}`;
897
1048
  const converted = converter(content, commandName);
898
- node_fs_1.default.writeFileSync(node_path_1.default.join(stageDir, `${stem}.md`), converted);
1049
+ installFs().writeFileSync(node_path_1.default.join(stageDir, `${stem}.md`), converted);
899
1050
  }
900
1051
  }
901
1052
  catch (err) {
902
1053
  try {
903
- node_fs_1.default.rmSync(stageDir, { recursive: true, force: true });
1054
+ installFs().rmSync(stageDir, { recursive: true, force: true });
904
1055
  }
905
1056
  catch { /* best-effort */ }
906
1057
  throw err;
907
1058
  }
908
- STAGED_DIRS.add(stageDir);
909
- ensureExitCleanup();
1059
+ registerStagedDir(stageDir);
910
1060
  return stageDir;
911
1061
  }
912
1062
  // ---------------------------------------------------------------------------
@@ -1079,6 +1229,7 @@ module.exports = {
1079
1229
  // Shared internals
1080
1230
  parseRequires,
1081
1231
  parseCallsAgents,
1232
+ workflowAgentRefs,
1082
1233
  cleanupStagedSkills,
1083
1234
  // #2322: capability-skill security seams — exported for direct unit-testing
1084
1235
  // and for surface.cts's prune pass (CAPABILITY_SKILL_MARKER parity).