@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
@@ -17,13 +17,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  const node_path_1 = __importDefault(require("node:path"));
18
18
  const node_fs_1 = __importDefault(require("node:fs"));
19
19
  const node_os_1 = __importDefault(require("node:os"));
20
+ // #2874 (ADR-58 cleanup phase): route this module's fs calls through the
21
+ // installRuntimeArtifacts call tree's injectable seam — see
22
+ // install-fs-adapter.cts's module doc. Resolves to real `node:fs` (the
23
+ // `fs` import above stays for type-only references, e.g. `fs.Dirent`)
24
+ // unless the top-level installRuntimeArtifacts call injected a `deps.fs`.
25
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
26
+ const installFsAdapter = require("./install-fs-adapter.cjs");
27
+ const { installFs, mkInstallTempDir } = installFsAdapter;
20
28
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
29
  const installProfiles = require("./install-profiles.cjs");
22
30
  const { stageSkillsForProfile, stageAgentsForRuntimeWithConverter, stageSkillsForRuntimeAsSkills, stageCommandsForRuntimeFlat, } = installProfiles;
23
31
  // eslint-disable-next-line @typescript-eslint/no-require-imports
24
32
  const runtimeArtifactConversion = require("./runtime-artifact-conversion.cjs");
25
33
  const conversionExports = runtimeArtifactConversion;
34
+ // #2875 Part 2 (J8): shared model-override precedence resolver — see its
35
+ // module doc for why kilo/opencode MUST resolve through this ONE function
36
+ // rather than re-deriving the chain per runtime.
37
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
38
+ const installModelOverrideResolver = require("./install-model-override-resolver.cjs");
39
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- export= CommonJS module, same as the sibling resolver import above
40
+ const installEffortResolver = require("./install-effort-resolver.cjs");
41
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- export= CommonJS module, same as the sibling resolver import above
42
+ const modelCatalog = require("./model-catalog.cjs");
26
43
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
44
+ // #2870: `isGlobalScope` centralizes the `scope === 'global'` boolean
45
+ // projection both kind-builder closures below need at the converters'
46
+ // positional `isGlobal` boundary (see its doc comment in install-scope.cts
47
+ // for why the projection is centralized rather than eliminated).
48
+ const install_scope_cjs_1 = require("./install-scope.cjs");
27
49
  // In .cts (CommonJS output) files, `require` is available as a global.
28
50
  const _require = require;
29
51
  // ---------------------------------------------------------------------------
@@ -38,24 +60,51 @@ const _require = require;
38
60
  * 3. Throw a descriptive error if neither succeeds.
39
61
  */
40
62
  function findInstallSourceRoot(runtimeConfigDir) {
41
- // Step 1: marker check
63
+ // Step 1: marker check — reads `<runtimeConfigDir>/.gsd-source`, a path
64
+ // under the INSTALL DESTINATION, so this probe goes through the injected
65
+ // adapter (installFs()).
42
66
  if (runtimeConfigDir) {
43
67
  const markerPath = node_path_1.default.join(runtimeConfigDir, '.gsd-source');
44
- if (node_fs_1.default.existsSync(markerPath)) {
68
+ if (installFs().existsSync(markerPath)) {
45
69
  try {
46
- const src = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
47
- if (src && node_fs_1.default.existsSync(src))
70
+ const src = installFs().readFileSync(markerPath, 'utf8').trim();
71
+ if (src && installFs().existsSync(src))
48
72
  return src;
49
73
  }
50
74
  catch { /* fall through */ }
51
75
  }
52
76
  }
53
- // Step 2: walk up from __dirname
77
+ // Step 2: walk up from __dirname to locate the GSD PACKAGE'S OWN source
78
+ // tree (commands/gsd/) — this resolves where the installer's own code is
79
+ // running FROM, not anything under the install destination, so it is
80
+ // deliberately NOT routed through the injected fs adapter (#2874): a fake
81
+ // "destination" adapter has no reason to know about the real package's own
82
+ // on-disk layout (an injected adapter's store starts empty and is never
83
+ // seeded with real repo paths), and routing it through would make this
84
+ // resolution unconditionally throw rather than gracefully staging nothing.
85
+ //
86
+ // Uses `fs.statSync` in a try/catch rather than `fs.existsSync` — this is
87
+ // LOAD-BEARING, not a style choice: tests/executed-plan.test.cjs's F2 cases
88
+ // poison every method on the ROUTED fs surface (including `existsSync`,
89
+ // since installFs()'s REAL_ADAPTER also calls it) to prove nothing on the
90
+ // installRuntimeArtifacts call tree reaches real fs. The F2 "nativePlugin
91
+ // runtime: pi" test calls this function (via findInstallSourceRoot()) AFTER
92
+ // installing that poison, specifically to resolve the pi nativePlugin
93
+ // source path against this repo's own real layout — an operation this
94
+ // function must still be able to perform even while `existsSync` is
95
+ // poisoned, because this Step 2 walk is real-fs-only by design and was
96
+ // never meant to be covered by that poison list. `statSync` is not on the
97
+ // poisoned surface, so this probe survives; switching back to `existsSync`
98
+ // makes that F2 test throw (verified: reverting this to `existsSync` trips
99
+ // the poison and breaks the pi nativePlugin case).
54
100
  let dir = __dirname;
55
101
  for (let i = 0; i < 6; i++) {
56
102
  const candidate = node_path_1.default.join(dir, 'commands', 'gsd');
57
- if (node_fs_1.default.existsSync(candidate))
103
+ try {
104
+ node_fs_1.default.statSync(candidate);
58
105
  return candidate;
106
+ }
107
+ catch { /* not here — keep walking up */ }
59
108
  const parent = node_path_1.default.dirname(dir);
60
109
  if (parent === dir)
61
110
  break;
@@ -72,28 +121,35 @@ function findInstallSourceRoot(runtimeConfigDir) {
72
121
  * 3. Throw a descriptive error if neither succeeds.
73
122
  */
74
123
  function findAgentsSourceRoot(runtimeConfigDir) {
75
- // Step 1: marker check
124
+ // Step 1: marker check (destination-relative — routed through installFs()).
76
125
  if (runtimeConfigDir) {
77
126
  const markerPath = node_path_1.default.join(runtimeConfigDir, '.gsd-source');
78
- if (node_fs_1.default.existsSync(markerPath)) {
127
+ if (installFs().existsSync(markerPath)) {
79
128
  try {
80
- const src = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
81
- if (src && node_fs_1.default.existsSync(src)) {
129
+ const src = installFs().readFileSync(markerPath, 'utf8').trim();
130
+ if (src && installFs().existsSync(src)) {
82
131
  // Marker points to commands/gsd; agents/ is a sibling of commands/
83
132
  const agentsCandidate = node_path_1.default.resolve(node_path_1.default.dirname(src), '..', 'agents');
84
- if (node_fs_1.default.existsSync(agentsCandidate))
133
+ if (installFs().existsSync(agentsCandidate))
85
134
  return agentsCandidate;
86
135
  }
87
136
  }
88
137
  catch { /* fall through */ }
89
138
  }
90
139
  }
91
- // Step 2: walk up from __dirname
140
+ // Step 2: walk up from __dirname — locates THIS package's own agents/
141
+ // source tree, not the install destination. See findInstallSourceRoot's
142
+ // Step 2 comment (#2874) for why this stays unrouted, real-fs-only, and why
143
+ // it uses `statSync` rather than `existsSync` (load-bearing against F2's
144
+ // poison of the routed fs surface, not a style choice).
92
145
  let dir = __dirname;
93
146
  for (let i = 0; i < 6; i++) {
94
147
  const candidate = node_path_1.default.join(dir, 'agents');
95
- if (node_fs_1.default.existsSync(candidate))
148
+ try {
149
+ node_fs_1.default.statSync(candidate);
96
150
  return candidate;
151
+ }
152
+ catch { /* not here — keep walking up */ }
97
153
  const parent = node_path_1.default.dirname(dir);
98
154
  if (parent === dir)
99
155
  break;
@@ -127,9 +183,53 @@ function agentsKind(destSubpath, prefix, configDir) {
127
183
  // inline agent loop, and installCodexConfig's per-agent .toml writer. The
128
184
  // exhaustive per-runtime sweep in tests/agent-fragments-emission.install.test.cjs
129
185
  // is what keeps a fourth from appearing uncomposed.
130
- stage: (resolved) => stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, (content) => content),
186
+ // #2875 Part 2 (row I2): agentCtx threaded through so a runtime using this
187
+ // converter:null builder (claude, plus any future identity-copy runtime)
188
+ // ALSO gets path-rewrites/attribution/frontmatter-extensions/normalize
189
+ // when a caller supplies agentCtx (createRuntimeArtifactInstallPlan /
190
+ // applySurface's agentCtx build). Previously this closure's `(resolved) =>`
191
+ // signature silently dropped the second arg every caller already passed —
192
+ // a caller with NO agentCtx in scope is unaffected (row I2: converter-only,
193
+ // as today), matching stageAgentsForRuntimeWithConverter's own contract.
194
+ stage: (resolved, agentCtx) => stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, (content) => content, false, agentCtx),
131
195
  };
132
196
  }
197
+ /**
198
+ * Runtime allowlist check for a descriptor-declared `converter` name, applied
199
+ * at DISPATCH time (security fix). `VALID_CONVERTER_NAMES` (capability-
200
+ * validator.cjs) is otherwise enforced ONLY at lint/build time
201
+ * (`check:contract-drift`) — every `conversionExports[converterName]`
202
+ * dynamic-property read below trusted that a `capability.json` reaching this
203
+ * far had already passed that check. It had not, in general: a hand-edited
204
+ * or malformed descriptor naming an Object-prototype member (`"constructor"`,
205
+ * `"toString"`, `"hasOwnProperty"`, ...) resolves to that member instead of
206
+ * throwing, producing garbage staged content rather than a loud failure —
207
+ * pre-existing, but promoted from the `/gsd-surface`-only path to the real
208
+ * install path for seven runtimes by #2875 Part 2's agents-bypass closure.
209
+ * Required lazily (call-time, not module-top) to avoid a load-time circular
210
+ * require, the same pattern install-engine.cts's `_hostBehaviors` already
211
+ * uses for `capability-registry.cjs`. Fails CLOSED: any error loading the
212
+ * allowlist itself (missing module, exotic bundling) is treated as "nothing
213
+ * is allowed", never as "skip the check".
214
+ */
215
+ function _resolveNamedConverter(converterName, kindLabel) {
216
+ let validNames;
217
+ try {
218
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
219
+ validNames = require('./capability-validator.cjs').VALID_CONVERTER_NAMES;
220
+ }
221
+ catch {
222
+ validNames = undefined;
223
+ }
224
+ if (!validNames || !validNames.has(converterName)) {
225
+ throw new Error(`Unknown converter "${converterName}" declared for a ${kindLabel} kind — refusing to dispatch (not in capability-validator.cjs's VALID_CONVERTER_NAMES allowlist).`);
226
+ }
227
+ const fn = conversionExports[converterName];
228
+ if (typeof fn !== 'function') {
229
+ throw new Error(`Converter "${converterName}" is allowlisted but is not an exported function of runtime-artifact-conversion.cjs.`);
230
+ }
231
+ return fn;
232
+ }
133
233
  /**
134
234
  * Build a converted-agents kind descriptor for runtimes whose agent `.md` files
135
235
  * need runtime-specific frontmatter/body conversion (e.g. Copilot, Cursor, Codex).
@@ -141,27 +241,80 @@ function agentsKind(destSubpath, prefix, configDir) {
141
241
  * Agent filenames are preserved verbatim (the prefix is already embedded in the
142
242
  * agent stem — e.g. `gsd-planner.md`).
143
243
  *
144
- * #1173 SCOPE — plumbing only (real install still elsewhere): this provides
145
- * the converter dispatch + `isGlobal` scope threading for the descriptor's
146
- * `agents` kind. As of #2092, 8 non-Claude runtimes DO declare a converted
147
- * `agents` kind in their `capability.json` — qwen (`convertClaudeAgentToQwenAgent`)
148
- * plus the 7 that already declared one before it (antigravity, augment,
149
- * codebuddy, copilot, cursor, trae, windsurf) — so the descriptor-level
150
- * declaration is no longer deferred. What IS still deferred is wiring
151
- * `resolveRuntimeArtifactLayout`'s `agents` kind into the REAL install:
152
- * `bin/install.js`'s agent-staging loop does not consume this module's
153
- * `convertedAgentsKind` resolution at all — it dispatches the very same
154
- * converter functions directly via `_hostBehaviors(runtime)` checks
155
- * (`frontmatterDialect`, `brandingRewrites`, `isCopilot`/`isAntigravity`/…),
156
- * duplicating the mapping declared here. That duplication is deliberate until
157
- * the second `layout.kinds` consumer — `applySurface` / `/gsd:surface` /
158
- * `--materialize` (`src/surface.cts`) — mirrors the legacy agent pipeline
159
- * (Copilot's `.agent.md` filename rename, the cross-cutting path-prefix
160
- * rewrite + attribution, stale-file cleanup, config-reading steps); declaring
161
- * `bin/install.js` itself against this resolver before then would risk
162
- * regressing the surface path. Until that follow-up lands, `bin/install.js`
163
- * remains authoritative for the real install, and this `convertedAgentsKind`
164
- * is exercised only by `/gsd:surface` and synthetic-descriptor seam tests.
244
+ * #1173 SCOPE, updated by #2875 Part 2 (the agents-bypass closure) — measured
245
+ * against the tree, not the ADR-3574 framing that preceded it:
246
+ *
247
+ * Of the four blockers this comment used to name for wiring `bin/install.js`'s
248
+ * inline agent loop against this resolver, THREE were already stale by the
249
+ * time #2875 measured them and are not re-litigated here: Copilot's
250
+ * `.agent.md` rename (the loop's own `destName = entry.name` comment records
251
+ * the ternary dropped in #2099; the descriptor fold applies it via
252
+ * `hostBehaviors.agentFileExtension`), the cross-cutting path-prefix rewrite +
253
+ * attribution (`stageAgentsForRuntimeWithConverter` already applies
254
+ * `applyAgentPathRewrites` -> `processAttribution` when `agentCtx` is
255
+ * present), and stale-file cleanup (`_removeGsdEntries` prunes every
256
+ * `gsd-`-prefixed entry in a kind's destSubpath, broader than the loop's own
257
+ * extension-gated check).
258
+ *
259
+ * The fourth — config-reading steps — was the real gap, and #2875 Part 2
260
+ * closed it: `stageAgentsForRuntimeWithConverter` now takes a per-file
261
+ * `agentName` (`agentCtx.agentName`, ADR-1235 §1) and a `targetDir`
262
+ * (`agentCtx.targetDir`), which together let it (a) run a post-converter
263
+ * frontmatter-extensions step (`applyAgentFrontmatterExtensions`, driven by
264
+ * `hostBehaviors.agentFrontmatterExtensions` — Claude's `effort` +
265
+ * `disallowedTools` injection) and (b) let THIS function resolve a per-agent
266
+ * model override (`installModelOverrideResolver.resolveAgentModelOverride`,
267
+ * `model_overrides[agent]` > `model_profile_overrides.<rt>.<tier>` > omit)
268
+ * before invoking a converter that needs it (kilo/opencode). Both pieces —
269
+ * plus a data-driven Hermes branding converter
270
+ * (`convertClaudeAgentToHermesAgent`, reading `hostBehaviors.brandingRewrites`
271
+ * rather than a hardcoded string table) — are single-sourced: `bin/install.js`
272
+ * requires the SAME functions this module does, so its inline loop and the
273
+ * descriptor path can no longer independently drift (the CLAUDE.md
274
+ * "Generative Fix Divergence" class the prior duplication risked).
275
+ *
276
+ * `tests/agent-descriptor-parity.test.cjs` proves byte-identical output
277
+ * between the inline loop and a SYNTHETIC descriptor registry (the same
278
+ * override seam `resolveRuntimeArtifactLayoutFromRegistry` exposes) for all
279
+ * six runtimes the inline loop still served: claude, cline, codex, hermes,
280
+ * kilo, opencode.
281
+ *
282
+ * Both findings the prior revision of this comment named as STILL deferred
283
+ * are now CLOSED (#2875 Part 2 Task A/B/C), measured against the real
284
+ * `capability.json` entries and the real production entry points, not
285
+ * argued from this module alone:
286
+ *
287
+ * 1. **kilo/opencode reaching `layout.kinds`.** `installEngine.
288
+ * installAgentsKindStandalone` (install-engine.cts) is called from inside
289
+ * `installOpencodeFamilyArtifacts` and resolves the agents kind through
290
+ * THIS SAME `resolveRuntimeArtifactLayout`/`convertedAgentsKind` path —
291
+ * `installOpencodeFamilyArtifacts` no longer stages only `commands` +
292
+ * `skills`. `bin/install.js`'s legacy-flat local path (claude-local,
293
+ * `hostBehaviors.localInstallStyle === 'legacy-flat'`) reaches the SAME
294
+ * generic loop only via `installRuntimeArtifacts`'s conditional
295
+ * `_isSkillsRuntime` branch; a call to `installAgentsKindStandalone` was
296
+ * added at claude-local's own call site to cover that scope too — the
297
+ * install-tree golden fixture (`tests/fixtures/install-tree/claude-local.json`)
298
+ * is what caught the gap when it was first missed.
299
+ * 2. **`/gsd:surface` / `applySurface` activation.** Confirmed convergent,
300
+ * not merely non-broken: for all six runtimes (claude, cline, codex,
301
+ * hermes, kilo, opencode), staging via `applySurface` into a freshly
302
+ * wiped `agents/` directory produces byte-identical output (including
303
+ * filenames) to `installRuntimeArtifacts`'s own write — verified directly
304
+ * against the built registry, not inferred.
305
+ *
306
+ * The inline loop (`_DESCRIPTOR_AGENTS_RUNTIMES` and the `bin/install.js`
307
+ * agent-staging block it gated) is DELETED — every runtime the registry
308
+ * declares an `agents` kind for is descriptor-driven now, including a
309
+ * seventh runtime (`kimi-code`) this comment's own prior measurement missed
310
+ * (it fell through the inline loop's generic `else if` branch, same as
311
+ * claude, with no dedicated dialect arm — caught by the same golden fixture).
312
+ *
313
+ * Codex's `config.toml [agents.gsd-*]` strip (`bin/install.js`, under
314
+ * `isMinimalMode` + `hostBehaviors.tomlConfigInstall`) remains the one
315
+ * genuinely out-of-scope constraint: it mutates a host config file, not the
316
+ * agents directory, and no descriptor kind models host-config mutation. It
317
+ * stays exactly where it is.
165
318
  *
166
319
  * Mirrors the `convertedCommandsKind` pattern (#785).
167
320
  *
@@ -176,15 +329,74 @@ function convertedAgentsKind(destSubpath, prefix, converterName, configDir, scop
176
329
  destSubpath,
177
330
  prefix,
178
331
  stage: (resolved, agentCtx) => {
179
- // isGlobal is threaded so scope-aware agent converters (copilot, antigravity)
180
- // choose global-home vs workspace-relative paths; converters that only take
181
- // (content) ignore the extra positional arg. Mirrors skillsKind's scope
182
- // threading (#1173).
183
- const converter = conversionExports[converterName];
332
+ // #2870: `scope` is this function's own parameter (default `'global'`,
333
+ // so it is never undefined here), sourced upstream from the Install
334
+ // Scope Module's resolved id. `isGlobalScope` projects it to the
335
+ // boolean `stageAgentsForRuntimeWithConverter`'s positional API
336
+ // requires — see its doc comment in install-scope.cts.
337
+ const rawConverter = _resolveNamedConverter(converterName, 'agents');
338
+ // #2875 Part 2 (J5-J8): kilo/opencode agent converters take an options
339
+ // bag (`{isAgent, modelOverride}`), not the `isGlobal` boolean every
340
+ // other agent converter's 2nd positional arg means — mirrors the
341
+ // inline loop's per-runtime `frontmatterDialect === 'opencode' | 'kilo'`
342
+ // branches (bin/install.js), which resolve model_overrides[agent] >
343
+ // model_profile_overrides.<runtime>.<tier> > omit BEFORE calling the
344
+ // converter. Resolved ONCE per stage() call (not per file — a pure
345
+ // function of configDir/targetDir) via the single shared precedence
346
+ // resolver so kilo and opencode can never diverge (J8).
347
+ const needsModelOverride = converterName === 'convertClaudeToOpencodeFrontmatter' || converterName === 'convertClaudeToKiloFrontmatter';
348
+ let converter;
349
+ if (needsModelOverride) {
350
+ const overrideTargetDir = agentCtx?.targetDir ?? configDir;
351
+ const modelOverrides = installModelOverrideResolver.readGsdEffectiveModelOverrides(overrideTargetDir);
352
+ const runtimeResolver = installModelOverrideResolver.readGsdRuntimeProfileResolver(overrideTargetDir);
353
+ // #3706: the resolved reasoning effort, threaded exactly as the model is —
354
+ // config read ONCE per stage(), per-agent value resolved per file.
355
+ //
356
+ // Gated on the effort config being PRESENT, not on a value coming back.
357
+ // `resolveInstallTimeEffort` always returns something (measured: 'high'
358
+ // even with no project config and no effort block), so "skip when
359
+ // resolution yields no value" has no trigger and would stamp `variant:`
360
+ // into every generated agent file for every existing install. OpenCode's
361
+ // built-in variant sets are provider-specific upstream (Anthropic ships
362
+ // only `high`/`max`), so a level GSD resolved is not guaranteed to name a
363
+ // variant the user's provider actually has — emitting one unasked-for is
364
+ // the risk this gate avoids. #1156's rule for `model: inherit` is the
365
+ // precedent: do not emit a key the runtime may not understand.
366
+ //
367
+ // OpenCode only; the kilo converter ignores the field (no EFFORT_ARGV.kilo).
368
+ const effortConfig = converterName === 'convertClaudeToOpencodeFrontmatter'
369
+ ? installEffortResolver.readGsdEffectiveEffortConfig(overrideTargetDir)
370
+ : null;
371
+ converter = (content, _isGlobal, meta) => {
372
+ const modelOverride = meta
373
+ ? installModelOverrideResolver.resolveAgentModelOverride(meta.agentName, modelOverrides, runtimeResolver)
374
+ : null;
375
+ // The universal level is NOT emitted raw. `clampEffortForHost` is the
376
+ // declared OpenCode effort capability (EFFORT_ARGV.opencode: its own
377
+ // `supported` set + `clamp`), and it is what rejects a level that is
378
+ // not a wire value — most importantly `inherit`, which per #3533 (10d)
379
+ // means "omit the key and follow the host default" and must never be
380
+ // written literally. Anything unsupported clamps to null, which omits
381
+ // the key rather than inventing one.
382
+ const universal = effortConfig && meta
383
+ ? installEffortResolver.resolveInstallTimeEffort(effortConfig, meta.agentName)
384
+ : null;
385
+ const variant = universal ? modelCatalog.clampEffortForHost('opencode', universal) : null;
386
+ return rawConverter(content, { isAgent: true, modelOverride, variant });
387
+ };
388
+ }
389
+ else {
390
+ // isGlobal is threaded so scope-aware agent converters (copilot, antigravity)
391
+ // choose global-home vs workspace-relative paths; converters that only take
392
+ // (content) ignore the extra positional arg. Mirrors skillsKind's scope
393
+ // threading (#1173).
394
+ converter = (content) => rawConverter(content, (0, install_scope_cjs_1.isGlobalScope)(scope));
395
+ }
184
396
  // ADR-1235 §1: when agentCtx is provided (by createRuntimeArtifactInstallPlan
185
397
  // for descriptor-driven runtimes), thread it through so stageAgentsForRuntimeWithConverter
186
398
  // can apply the full pre-converter + post-converter sequence in the correct order.
187
- return stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, converter, scope === 'global', agentCtx);
399
+ return stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, converter, (0, install_scope_cjs_1.isGlobalScope)(scope), agentCtx);
188
400
  },
189
401
  };
190
402
  }
@@ -199,28 +411,28 @@ function kimiAgentsKind(destSubpath, prefix, configDir) {
199
411
  // sees marker-free content — same single composing stager as agentsKind.
200
412
  const stagedAgents = stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, (content) => content);
201
413
  const subagents = [];
202
- if (node_fs_1.default.existsSync(stagedAgents)) {
203
- for (const entry of node_fs_1.default.readdirSync(stagedAgents, { withFileTypes: true })) {
414
+ if (installFs().existsSync(stagedAgents)) {
415
+ for (const entry of installFs().readdirSync(stagedAgents, { withFileTypes: true })) {
204
416
  if (!entry.isFile() || !entry.name.endsWith('.md'))
205
417
  continue;
206
418
  const agentPath = node_path_1.default.join(stagedAgents, entry.name);
207
419
  subagents.push({
208
420
  path: (0, shell_command_projection_cjs_1.posixNormalize)(node_path_1.default.join('agents', entry.name)),
209
- content: node_fs_1.default.readFileSync(agentPath, 'utf8'),
421
+ content: installFs().readFileSync(agentPath, 'utf8'),
210
422
  });
211
423
  }
212
424
  }
213
425
  const rootAgent = `---\nname: gsd\ndescription: Run GSD workflows in Kimi CLI.\ntools: Agent\n---\n\n# GSD for Kimi CLI\n\nCoordinate installed /skill:gsd-* workflows and route work to generated GSD subagents when a workflow requires an agent handoff.\n`;
214
426
  const artifacts = buildKimiAgentArtifacts({ rootAgent, subagents });
215
- const stageDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'gsd-kimi-agents-'));
427
+ const stageDir = mkInstallTempDir('gsd-kimi-agents-');
216
428
  installProfiles.STAGED_DIRS.add(stageDir);
217
- node_fs_1.default.writeFileSync(node_path_1.default.join(stageDir, 'gsd.yaml'), artifacts.root.yaml);
218
- node_fs_1.default.writeFileSync(node_path_1.default.join(stageDir, 'gsd.md'), artifacts.root.prompt);
429
+ installFs().writeFileSync(node_path_1.default.join(stageDir, 'gsd.yaml'), artifacts.root.yaml);
430
+ installFs().writeFileSync(node_path_1.default.join(stageDir, 'gsd.md'), artifacts.root.prompt);
219
431
  const subagentsDir = node_path_1.default.join(stageDir, 'subagents');
220
- node_fs_1.default.mkdirSync(subagentsDir, { recursive: true });
432
+ installFs().mkdirSync(subagentsDir, { recursive: true });
221
433
  for (const artifact of artifacts.subagents) {
222
- node_fs_1.default.writeFileSync(node_path_1.default.join(subagentsDir, `${artifact.name}.yaml`), artifact.yaml);
223
- node_fs_1.default.writeFileSync(node_path_1.default.join(subagentsDir, `${artifact.name}.md`), artifact.prompt);
434
+ installFs().writeFileSync(node_path_1.default.join(subagentsDir, `${artifact.name}.yaml`), artifact.yaml);
435
+ installFs().writeFileSync(node_path_1.default.join(subagentsDir, `${artifact.name}.md`), artifact.prompt);
224
436
  }
225
437
  return stageDir;
226
438
  },
@@ -251,7 +463,7 @@ function skillsKind(destSubpath, prefix, converterName, runtime, configDir, nest
251
463
  prefix,
252
464
  converter: converterName,
253
465
  stage: (resolved) => {
254
- const realConverter = conversionExports[converterName];
466
+ const realConverter = _resolveNamedConverter(converterName, 'skills');
255
467
  // Compute cmdNames once per stage call for performance (#3583).
256
468
  // Extra trailing args are ignored by converters that don't need them. The
257
469
  // isGlobal flag is the 5th positional (NOT the 3rd): the 3rd positional is
@@ -261,7 +473,21 @@ function skillsKind(destSubpath, prefix, converterName, runtime, configDir, nest
261
473
  const cmdNames = conversionExports.readGsdCommandNames
262
474
  ? conversionExports.readGsdCommandNames()
263
475
  : [];
264
- const isGlobal = scope === 'global';
476
+ // #2870: same judgment as convertedAgentsKind above — `scope` is this
477
+ // function's own parameter (default `'global'`, so it is never
478
+ // undefined here); `isGlobalScope` projects it to the boolean
479
+ // `realConverter`'s positional `isGlobal` arg requires.
480
+ const isGlobal = (0, install_scope_cjs_1.isGlobalScope)(scope);
481
+ // #2873 (4b): spec-root reachability is applied LATER in the pipeline —
482
+ // see `rewriteStagedSkillBodies` in runtime-artifact-conversion.cts, not
483
+ // here. This stage() closure runs BEFORE the staged directory's generic
484
+ // path-prefix rewrite pass (`applyRuntimeContentRewritesInPlace`'s
485
+ // `case 'claude'`), which unconditionally rewrites any bare (non-`@`)
486
+ // `~/.claude/` substring to the undocumented `$HOME/.claude/` form and
487
+ // only restores the `@`-prefixed form. Emitting the imperative
488
+ // tilde-path prose here would get silently mangled by that later pass;
489
+ // it must run AFTER it instead, once the `@`-include is in its final
490
+ // rewritten shape.
265
491
  const wrappedConverter = (content, skillName) => realConverter(content, skillName, runtime, cmdNames, isGlobal);
266
492
  return stageSkillsForRuntimeAsSkills(findInstallSourceRoot(configDir), resolved, wrappedConverter, prefix, nested, capabilityRegistry);
267
493
  },
@@ -289,7 +515,7 @@ function convertedCommandsKind(destSubpath, prefix, converterName, configDir) {
289
515
  destSubpath,
290
516
  prefix,
291
517
  stage: (resolved) => {
292
- const converter = conversionExports[converterName];
518
+ const converter = _resolveNamedConverter(converterName, 'commands');
293
519
  return stageCommandsForRuntimeFlat(findInstallSourceRoot(configDir), resolved, converter, prefix);
294
520
  },
295
521
  };
@@ -328,7 +554,11 @@ function dispatchKindEntry(entry, runtime, configDir, scope, capabilityRegistry)
328
554
  default:
329
555
  throw new TypeError(`resolveRuntimeArtifactLayout: unknown kind '${kind}' in descriptor for runtime '${runtime}'`);
330
556
  }
331
- if (scope === 'global' && typeof entry.home === 'string' && entry.home !== '') {
557
+ // scope is guaranteed 'local' | 'global' here: resolveRuntimeArtifactLayoutFromRegistry
558
+ // (the only caller of dispatchKindEntry) throws TypeError before this point if scope is
559
+ // anything else (see the `scope !== 'local' && scope !== 'global'` guard above its
560
+ // dispatchKindEntry call), so isGlobalScope's throw-on-invalid-input never fires here.
561
+ if ((0, install_scope_cjs_1.isGlobalScope)(scope) && typeof entry.home === 'string' && entry.home !== '') {
332
562
  result.home = node_path_1.default.join(node_os_1.default.homedir(), entry.home);
333
563
  }
334
564
  return result;
@@ -367,4 +597,178 @@ function resolveRuntimeArtifactLayoutFromRegistry(registry, runtime, configDir,
367
597
  const kinds = entries.map((entry) => dispatchKindEntry(entry, runtime, configDir, scope, capabilityRegistry));
368
598
  return { runtime, configDir, scope, kinds };
369
599
  }
370
- module.exports = { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, findInstallSourceRoot };
600
+ function getTriggerRegistry() {
601
+ return _require('./capability-registry.cjs');
602
+ }
603
+ /**
604
+ * capability-validator.cjs is a COMMITTED plain .cjs (not built from a .cts
605
+ * source — see its own header comment), so it is required the same way
606
+ * capability-registry.cjs is above: a lazy `_require` rather than a static
607
+ * ES import. DEFAULT_TRIGGER_PRECEDENCE is the single source of truth for
608
+ * "what applies when a descriptor omits triggerPrecedence"; this module
609
+ * reads it rather than re-declaring `['skills', 'commands']` as a second
610
+ * literal that could silently drift from the validator's own default.
611
+ */
612
+ function getDefaultTriggerPrecedence() {
613
+ const capValidator = _require('./capability-validator.cjs');
614
+ return capValidator.DEFAULT_TRIGGER_PRECEDENCE;
615
+ }
616
+ /**
617
+ * True when a `commands` kind entry is namespaced by its destination
618
+ * directory rather than by a filename prefix — i.e. `destSubpath`'s basename
619
+ * equals `prefix` with its trailing hyphen stripped (e.g. `commands/gsd` +
620
+ * `gsd-`). When true, `_copyStaged` (`install-engine.cts`) and `surface.cts`
621
+ * both write the bare stem filename (no prefix) because the directory itself
622
+ * already carries the namespace; `resolveTriggerSurface` mirrors that in its
623
+ * own `destPath` computation. Single source of truth for the three sites
624
+ * that used to compute this independently (#2871 Phase 2 review finding) —
625
+ * a new caller MUST reuse this rather than re-deriving the rule.
626
+ */
627
+ function isNamespacedByDir(kind, destSubpath, prefix) {
628
+ const destLast = node_path_1.default.posix.basename((0, shell_command_projection_cjs_1.posixNormalize)(destSubpath));
629
+ const prefixStem = prefix ? prefix.replace(/-$/, '') : '';
630
+ return kind === 'commands' && destLast === prefixStem;
631
+ }
632
+ /**
633
+ * Compose the destination filename `_copyStaged` (install-engine.cts) writes
634
+ * for a `commands` kind entry, given `isNamespacedByDir`'s result, the
635
+ * kind's prefix, and the file's `.md`-stripped stem. Single source of truth
636
+ * alongside `isNamespacedByDir` for the FILENAME COMPOSITION itself (#2871
637
+ * Phase 2 review finding — the boolean was single-sourced first, but the
638
+ * `${stem}.md` / `${prefix}${stem}.md` string-building around it stayed
639
+ * duplicated between `_copyStaged` and `resolveTriggerSurface`'s `destPath`
640
+ * prediction below, so a divergence in the write convention would not have
641
+ * failed anything).
642
+ *
643
+ * Byte-identical to `_copyStaged`'s prior separate branches: when
644
+ * `namespacedByDir` is true this returns `${stem}.md`. `_copyStaged` always
645
+ * derives `stem` as `entry.name.slice(0, -3)` for an `entry.name` that has
646
+ * already been filtered to end in `.md`, so `${stem}.md` is always exactly
647
+ * `entry.name` again — `_copyStaged` can pass this helper's result in place
648
+ * of the `entry.name` it used to write directly, with no behavior change.
649
+ */
650
+ function composeCommandFilename(namespacedByDir, prefix, stem) {
651
+ return namespacedByDir ? `${stem}.md` : `${prefix}${stem}.md`;
652
+ }
653
+ /**
654
+ * True when candidate `a` should win over the current best `b` for the same
655
+ * trigger. Scope rank first (Phase 1's `install-scope.cts#scopeRank` —
656
+ * global outranks local; NOT re-derived here), then the runtime's
657
+ * `triggerPrecedence` kind ordering (lower index = higher priority). A kind
658
+ * absent from `precedenceRank` (should not happen — every entry's kind is
659
+ * validated against the same closed vocabulary the precedence list draws
660
+ * from) sorts last rather than throwing, so a malformed precedence value
661
+ * degrades to "leaves the incumbent standing" instead of corrupting the
662
+ * whole resolution.
663
+ */
664
+ function isHigherPriority(a, b, precedenceRank) {
665
+ const rankA = (0, install_scope_cjs_1.scopeRank)(a.scope);
666
+ const rankB = (0, install_scope_cjs_1.scopeRank)(b.scope);
667
+ if (rankA !== rankB)
668
+ return rankA > rankB;
669
+ const pa = precedenceRank.get(a.kind) ?? Number.POSITIVE_INFINITY;
670
+ const pb = precedenceRank.get(b.kind) ?? Number.POSITIVE_INFINITY;
671
+ return pa < pb;
672
+ }
673
+ /**
674
+ * Resolve the `/gsd-<name>`-style trigger surface for a runtime: what the
675
+ * user types, at which scope, whether it wins or is shadowed, and (for
676
+ * nested-router runtimes) whether the host registers it directly or only
677
+ * reaches it through a router. Pure — no filesystem, no mutation of `scopes`
678
+ * or `opts`, and safe against a caller mutating the returned array/objects
679
+ * (a fresh array/objects are built on every call; nothing is cached or
680
+ * shared across calls beyond the read-only registry module).
681
+ *
682
+ * Only `commands` and `skills` kind entries are considered — see the
683
+ * module-level comment above. `resolveRuntimeArtifactLayout` is untouched by
684
+ * this function; they are independent readers of the same descriptor.
685
+ *
686
+ * @throws {TypeError} for an unknown runtime — same contract (and message
687
+ * shape) as `resolveRuntimeArtifactLayoutFromRegistry`.
688
+ * @throws {TypeError} for an unrecognized entry in `scopes` — reuses
689
+ * `install-scope.cts`'s shared `validateScopeId`, the same validator
690
+ * `scopeRank`/`resolveScope`/`isGlobalScope` already throw through, so this
691
+ * sibling of theirs cannot silently fail open on a bad scope (#2871 Phase 2
692
+ * review finding). `scopes: []` is untouched — an empty array has no
693
+ * entries to validate and still resolves to `[]`.
694
+ */
695
+ function resolveTriggerSurface(runtime, scopes, opts) {
696
+ const registry = opts.registry ?? getTriggerRegistry();
697
+ const runtimeDescriptor = registry.runtimes[runtime]?.runtime;
698
+ const layout = runtimeDescriptor?.artifactLayout;
699
+ if (!layout) {
700
+ throw new TypeError(`Unknown runtime: '${runtime}' — add to runtime-artifact-layout.cjs table`);
701
+ }
702
+ for (const scope of scopes) {
703
+ (0, install_scope_cjs_1.validateScopeId)(scope, 'resolveTriggerSurface');
704
+ }
705
+ const scopeSet = new Set(scopes);
706
+ const stems = opts.stems ?? [];
707
+ const routerStemSet = new Set(opts.routerStems ?? []);
708
+ const childToRouters = opts.childToRouters ?? {};
709
+ const precedence = runtimeDescriptor?.triggerPrecedence ?? getDefaultTriggerPrecedence();
710
+ const precedenceRank = new Map(precedence.map((kind, index) => [kind, index]));
711
+ const surfaces = [];
712
+ for (const scope of install_scope_cjs_1.SCOPE_ORDER) {
713
+ if (!scopeSet.has(scope))
714
+ continue;
715
+ const entries = layout[scope] ?? [];
716
+ for (const entry of entries) {
717
+ if (entry.kind !== 'commands' && entry.kind !== 'skills')
718
+ continue; // excludes agents/kimi-agents
719
+ const kind = entry.kind;
720
+ const destSubpath = (0, shell_command_projection_cjs_1.posixNormalize)(entry.destSubpath);
721
+ const namespacedByDir = isNamespacedByDir(kind, entry.destSubpath, entry.prefix);
722
+ const nested = entry.nesting === 'nested';
723
+ for (const stem of stems) {
724
+ const trigger = `${entry.prefix}${stem}`;
725
+ let destPath;
726
+ if (kind === 'skills') {
727
+ destPath = `${destSubpath}/${entry.prefix}${stem}`;
728
+ }
729
+ else {
730
+ destPath = `${destSubpath}/${composeCommandFilename(namespacedByDir, entry.prefix, stem)}`;
731
+ }
732
+ let registration = 'direct';
733
+ let routerTrigger = null;
734
+ if (nested && routerStemSet.size > 0 && !routerStemSet.has(stem)) {
735
+ const owningRouters = childToRouters[stem];
736
+ const routerStem = owningRouters && owningRouters.length > 0 ? owningRouters[0] : undefined;
737
+ if (routerStem !== undefined && routerStemSet.has(routerStem)) {
738
+ registration = 'via-router';
739
+ routerTrigger = `${entry.prefix}${routerStem}`;
740
+ }
741
+ }
742
+ surfaces.push({ trigger, kind, scope, destPath, registration, routerTrigger, shadowedBy: null });
743
+ }
744
+ }
745
+ }
746
+ // Winner computation, per trigger string, across every scope/kind candidate.
747
+ const groups = new Map();
748
+ for (const surface of surfaces) {
749
+ const group = groups.get(surface.trigger);
750
+ if (group) {
751
+ group.push(surface);
752
+ }
753
+ else {
754
+ groups.set(surface.trigger, [surface]);
755
+ }
756
+ }
757
+ for (const group of groups.values()) {
758
+ if (group.length <= 1)
759
+ continue; // sole candidate: unshadowed by construction
760
+ let winner = group[0];
761
+ for (let i = 1; i < group.length; i++) {
762
+ const candidate = group[i];
763
+ if (isHigherPriority(candidate, winner, precedenceRank))
764
+ winner = candidate;
765
+ }
766
+ for (const surface of group) {
767
+ if (surface !== winner) {
768
+ surface.shadowedBy = { kind: winner.kind, scope: winner.scope };
769
+ }
770
+ }
771
+ }
772
+ return surfaces;
773
+ }
774
+ module.exports = { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, findInstallSourceRoot, resolveTriggerSurface, isNamespacedByDir, composeCommandFilename };