@opengsd/gsd-core 1.15.0 → 1.16.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 (487) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +85 -5
  4. package/agents/gsd-code-fixer.compact.md +1 -1
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-code-reviewer.compact.md +5 -3
  7. package/agents/gsd-code-reviewer.md +8 -6
  8. package/agents/gsd-debug-session-manager.compact.md +1 -1
  9. package/agents/gsd-debug-session-manager.md +1 -1
  10. package/agents/gsd-debugger.md +2 -2
  11. package/agents/gsd-eval-auditor.compact.md +1 -1
  12. package/agents/gsd-eval-auditor.md +1 -1
  13. package/agents/gsd-executor.md +4 -4
  14. package/agents/gsd-intel-updater.compact.md +1 -1
  15. package/agents/gsd-intel-updater.md +1 -1
  16. package/agents/gsd-mempalace-curator.md +2 -2
  17. package/agents/gsd-phase-researcher.md +1 -1
  18. package/agents/gsd-plan-checker.md +7 -2
  19. package/agents/gsd-planner.md +3 -3
  20. package/agents/gsd-project-researcher.compact.md +1 -1
  21. package/agents/gsd-project-researcher.md +1 -1
  22. package/agents/gsd-research-synthesizer.compact.md +1 -1
  23. package/agents/gsd-research-synthesizer.md +1 -1
  24. package/agents/gsd-ui-auditor.compact.md +21 -30
  25. package/agents/gsd-ui-auditor.md +42 -51
  26. package/agents/gsd-ui-researcher.compact.md +1 -1
  27. package/agents/gsd-ui-researcher.md +1 -1
  28. package/agents/gsd-verifier.md +31 -9
  29. package/bin/install.js +126 -196
  30. package/commands/gsd/add-tests.md +6 -1
  31. package/commands/gsd/ai-integration-phase.md +6 -1
  32. package/commands/gsd/audit-fix.md +5 -0
  33. package/commands/gsd/audit-milestone.md +6 -1
  34. package/commands/gsd/autonomous.md +5 -0
  35. package/commands/gsd/capture.md +8 -4
  36. package/commands/gsd/code-review.md +7 -2
  37. package/commands/gsd/complete-milestone.md +4 -0
  38. package/commands/gsd/config.md +7 -3
  39. package/commands/gsd/debug.md +11 -7
  40. package/commands/gsd/discuss-phase.md +7 -3
  41. package/commands/gsd/docs-update.md +12 -7
  42. package/commands/gsd/eval-review.md +6 -1
  43. package/commands/gsd/execute-phase.md +12 -7
  44. package/commands/gsd/extract-learnings.md +5 -0
  45. package/commands/gsd/fast.md +4 -0
  46. package/commands/gsd/forensics.md +5 -1
  47. package/commands/gsd/graphify.md +10 -6
  48. package/commands/gsd/health.md +5 -0
  49. package/commands/gsd/help.md +7 -2
  50. package/commands/gsd/import.md +7 -3
  51. package/commands/gsd/inbox.md +5 -0
  52. package/commands/gsd/ingest-docs.md +5 -1
  53. package/commands/gsd/manager.md +6 -1
  54. package/commands/gsd/map-codebase.md +7 -3
  55. package/commands/gsd/mempalace-capture.md +5 -1
  56. package/commands/gsd/mempalace-recall.md +5 -1
  57. package/commands/gsd/milestone-summary.md +5 -1
  58. package/commands/gsd/mvp-phase.md +8 -3
  59. package/commands/gsd/new-milestone.md +6 -1
  60. package/commands/gsd/new-project.md +5 -0
  61. package/commands/gsd/next.md +6 -1
  62. package/commands/gsd/ns-context.md +4 -0
  63. package/commands/gsd/ns-ideate.md +4 -0
  64. package/commands/gsd/ns-manage.md +4 -0
  65. package/commands/gsd/ns-project.md +4 -0
  66. package/commands/gsd/ns-review.md +4 -0
  67. package/commands/gsd/ns-workflow.md +4 -0
  68. package/commands/gsd/onboard.md +6 -1
  69. package/commands/gsd/pause-work.md +5 -1
  70. package/commands/gsd/phase.md +8 -4
  71. package/commands/gsd/plan-phase.md +6 -1
  72. package/commands/gsd/plan-review-convergence.md +5 -1
  73. package/commands/gsd/pr-branch.md +4 -0
  74. package/commands/gsd/profile-user.md +5 -1
  75. package/commands/gsd/progress.md +6 -1
  76. package/commands/gsd/quick-batch.md +20 -8
  77. package/commands/gsd/quick.md +12 -7
  78. package/commands/gsd/review.md +5 -1
  79. package/commands/gsd/secure-phase.md +6 -1
  80. package/commands/gsd/ship.md +5 -0
  81. package/commands/gsd/sketch.md +7 -2
  82. package/commands/gsd/spec-phase.md +5 -1
  83. package/commands/gsd/spike.md +8 -3
  84. package/commands/gsd/surface.md +5 -1
  85. package/commands/gsd/thread.md +4 -0
  86. package/commands/gsd/ui-phase.md +6 -1
  87. package/commands/gsd/ui-review.md +6 -1
  88. package/commands/gsd/ultraplan-phase.md +5 -1
  89. package/commands/gsd/undo.md +5 -1
  90. package/commands/gsd/update.md +6 -2
  91. package/commands/gsd/validate-phase.md +6 -1
  92. package/commands/gsd/verify-work.md +6 -1
  93. package/commands/gsd/workspace.md +7 -3
  94. package/gsd-core/bin/gsd-tools.cjs +142 -56
  95. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  96. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  97. package/gsd-core/bin/lib/audit.cjs +78 -44
  98. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  99. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  100. package/gsd-core/bin/lib/capability-registry.cjs +183 -103
  101. package/gsd-core/bin/lib/capability-validator.cjs +16 -0
  102. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  103. package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
  104. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  105. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  106. package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
  107. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  108. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  109. package/gsd-core/bin/lib/commands.cjs +173 -170
  110. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  111. package/gsd-core/bin/lib/config.cjs +43 -12
  112. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  113. package/gsd-core/bin/lib/coverage.cjs +4 -8
  114. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  115. package/gsd-core/bin/lib/drift.cjs +177 -42
  116. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  117. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  118. package/gsd-core/bin/lib/frontmatter.cjs +413 -234
  119. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  120. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  121. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  122. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  123. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  124. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  125. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  126. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  127. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  128. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  129. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  130. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  131. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  132. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  133. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  134. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  135. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  136. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  137. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  138. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  139. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  140. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  141. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  142. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  143. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  144. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  145. package/gsd-core/bin/lib/init.cjs +162 -96
  146. package/gsd-core/bin/lib/install-engine.cjs +19 -35
  147. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  148. package/gsd-core/bin/lib/io.cjs +122 -3
  149. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  150. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  151. package/gsd-core/bin/lib/milestone.cjs +19 -1
  152. package/gsd-core/bin/lib/model-resolver.cjs +18 -18
  153. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  154. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  155. package/gsd-core/bin/lib/pattern.cjs +10 -0
  156. package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
  157. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  158. package/gsd-core/bin/lib/phase-id.cjs +1 -1
  159. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  160. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  161. package/gsd-core/bin/lib/phase.cjs +280 -80
  162. package/gsd-core/bin/lib/plan-document.cjs +93 -19
  163. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  164. package/gsd-core/bin/lib/planning-document.cjs +273 -40
  165. package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
  166. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  167. package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
  168. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  169. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  170. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  171. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  172. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  173. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  174. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  175. package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
  176. package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
  177. package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
  178. package/gsd-core/bin/lib/roadmap.cjs +169 -72
  179. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
  180. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
  181. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  182. package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
  183. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
  184. package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
  185. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  186. package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
  187. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  188. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  189. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  190. package/gsd-core/bin/lib/state-document.cjs +100 -22
  191. package/gsd-core/bin/lib/state.cjs +214 -105
  192. package/gsd-core/bin/lib/surface.cjs +2 -1
  193. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
  194. package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
  195. package/gsd-core/bin/lib/uat.cjs +424 -7
  196. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
  197. package/gsd-core/bin/lib/vendor/README.md +31 -9
  198. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  199. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  200. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  201. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  202. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  203. package/gsd-core/bin/lib/verification.cjs +1165 -314
  204. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  205. package/gsd-core/bin/lib/verify.cjs +286 -567
  206. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  207. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  208. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  209. package/gsd-core/bin/lib/workstream.cjs +11 -2
  210. package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
  211. package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
  212. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  213. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  214. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  215. package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
  216. package/gsd-core/references/execute-phase-response-language.md +1 -1
  217. package/gsd-core/references/gsd-run-resolver.md +1 -1
  218. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  219. package/gsd-core/references/offer-next.md +1 -1
  220. package/gsd-core/references/planning-config.md +1 -1
  221. package/gsd-core/references/spidr-splitting.md +1 -1
  222. package/gsd-core/references/tdd.md +10 -4
  223. package/gsd-core/references/verifier-phase-gates.md +5 -2
  224. package/gsd-core/references/verify-mvp-mode.md +2 -2
  225. package/gsd-core/references/workstream-flag.md +33 -3
  226. package/gsd-core/templates/README.md +1 -1
  227. package/gsd-core/templates/UAT.md +17 -1
  228. package/gsd-core/templates/config.json +2 -11
  229. package/gsd-core/templates/verification-report.md +1 -1
  230. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  231. package/gsd-core/workflows/add-backlog.md +1 -1
  232. package/gsd-core/workflows/add-phase.md +8 -7
  233. package/gsd-core/workflows/add-tests.md +3 -2
  234. package/gsd-core/workflows/add-todo.md +4 -3
  235. package/gsd-core/workflows/ai-integration-phase.md +3 -2
  236. package/gsd-core/workflows/audit-fix.md +1 -1
  237. package/gsd-core/workflows/audit-milestone.md +4 -3
  238. package/gsd-core/workflows/audit-uat.md +1 -1
  239. package/gsd-core/workflows/autonomous.md +28 -18
  240. package/gsd-core/workflows/check-todos.md +6 -5
  241. package/gsd-core/workflows/cleanup.md +1 -1
  242. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  243. package/gsd-core/workflows/code-review-fix.md +5 -4
  244. package/gsd-core/workflows/code-review.md +84 -90
  245. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  246. package/gsd-core/workflows/complete-milestone.md +12 -7
  247. package/gsd-core/workflows/debug.md +30 -5
  248. package/gsd-core/workflows/diagnose-issues.md +3 -2
  249. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  250. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  252. package/gsd-core/workflows/discuss-phase.md +4 -3
  253. package/gsd-core/workflows/do.md +1 -1
  254. package/gsd-core/workflows/docs-update.md +4 -3
  255. package/gsd-core/workflows/edit-phase.md +4 -3
  256. package/gsd-core/workflows/eval-review.md +5 -3
  257. package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
  258. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
  259. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  260. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  261. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  263. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  264. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  265. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  266. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  267. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  268. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  269. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  270. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  271. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  272. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  273. package/gsd-core/workflows/execute-phase.md +78 -170
  274. package/gsd-core/workflows/execute-plan.md +13 -19
  275. package/gsd-core/workflows/explore.md +2 -2
  276. package/gsd-core/workflows/extract-learnings.md +3 -2
  277. package/gsd-core/workflows/fast.md +1 -1
  278. package/gsd-core/workflows/forensics.md +1 -1
  279. package/gsd-core/workflows/graduation.md +1 -1
  280. package/gsd-core/workflows/health.md +2 -1
  281. package/gsd-core/workflows/import.md +3 -2
  282. package/gsd-core/workflows/inbox.md +1 -1
  283. package/gsd-core/workflows/ingest-docs.md +1 -1
  284. package/gsd-core/workflows/insert-phase.md +4 -3
  285. package/gsd-core/workflows/list-seeds.md +1 -1
  286. package/gsd-core/workflows/list-workspaces.md +1 -1
  287. package/gsd-core/workflows/manager.md +5 -3
  288. package/gsd-core/workflows/map-codebase.md +4 -3
  289. package/gsd-core/workflows/milestone-summary.md +3 -2
  290. package/gsd-core/workflows/mvp-phase.md +14 -14
  291. package/gsd-core/workflows/new-milestone.md +10 -10
  292. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  293. package/gsd-core/workflows/new-project.md +1 -1
  294. package/gsd-core/workflows/new-workspace.md +1 -1
  295. package/gsd-core/workflows/next.md +1 -1
  296. package/gsd-core/workflows/pause-work.md +2 -2
  297. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  298. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  299. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  300. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  301. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
  302. package/gsd-core/workflows/plan-phase.md +27 -18
  303. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  304. package/gsd-core/workflows/plant-seed.md +1 -1
  305. package/gsd-core/workflows/pr-branch.md +1 -1
  306. package/gsd-core/workflows/profile-user.md +1 -1
  307. package/gsd-core/workflows/progress.md +19 -49
  308. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
  309. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  310. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  311. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  312. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  313. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  314. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  315. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  316. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  317. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  318. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  319. package/gsd-core/workflows/quick-batch.md +15 -10
  320. package/gsd-core/workflows/quick.md +42 -31
  321. package/gsd-core/workflows/remove-phase.md +3 -2
  322. package/gsd-core/workflows/remove-workspace.md +1 -1
  323. package/gsd-core/workflows/resume-project.md +3 -2
  324. package/gsd-core/workflows/review.md +3 -2
  325. package/gsd-core/workflows/scan.md +3 -2
  326. package/gsd-core/workflows/secure-phase.md +11 -12
  327. package/gsd-core/workflows/settings-advanced.md +1 -1
  328. package/gsd-core/workflows/settings-integrations.md +1 -1
  329. package/gsd-core/workflows/settings.md +1 -1
  330. package/gsd-core/workflows/ship.md +12 -12
  331. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  332. package/gsd-core/workflows/sketch.md +1 -1
  333. package/gsd-core/workflows/smart-entry.md +1 -1
  334. package/gsd-core/workflows/spec-phase.md +1 -1
  335. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  336. package/gsd-core/workflows/spike.md +1 -1
  337. package/gsd-core/workflows/stats.md +1 -1
  338. package/gsd-core/workflows/sync-skills.md +1 -1
  339. package/gsd-core/workflows/thread.md +2 -2
  340. package/gsd-core/workflows/transition.md +13 -23
  341. package/gsd-core/workflows/ui-phase.md +5 -4
  342. package/gsd-core/workflows/ui-review.md +4 -3
  343. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  344. package/gsd-core/workflows/undo.md +1 -1
  345. package/gsd-core/workflows/validate-phase.md +10 -12
  346. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  347. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  348. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  349. package/gsd-core/workflows/verify-work.md +68 -59
  350. package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
  351. package/hooks/dist/gsd-check-update-worker.js +8 -0
  352. package/hooks/dist/gsd-check-update.js +8 -0
  353. package/hooks/dist/gsd-context-monitor.js +31 -8
  354. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  355. package/hooks/dist/gsd-secret-read-guard.js +147 -16
  356. package/hooks/dist/gsd-statusline.js +15 -7
  357. package/hooks/dist/gsd-update-banner.js +8 -0
  358. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  359. package/hooks/dist/gsd-workflow-guard.js +5 -4
  360. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  361. package/hooks/dist/lib/cli-exit.js +12 -0
  362. package/hooks/dist/lib/git-probe.js +17 -1
  363. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  364. package/hooks/gsd-agent-isolation-guard.js +8 -0
  365. package/hooks/gsd-check-update-worker.js +8 -0
  366. package/hooks/gsd-check-update.js +8 -0
  367. package/hooks/gsd-context-monitor.js +31 -8
  368. package/hooks/gsd-cursor-subagent-start.js +8 -0
  369. package/hooks/gsd-secret-read-guard.js +147 -16
  370. package/hooks/gsd-statusline.js +15 -7
  371. package/hooks/gsd-update-banner.js +8 -0
  372. package/hooks/gsd-windsurf-pre-write.js +11 -2
  373. package/hooks/gsd-workflow-guard.js +5 -4
  374. package/hooks/gsd-worktree-path-guard.js +6 -2
  375. package/hooks/hooks.json +5 -5
  376. package/hooks/lib/cli-exit.js +12 -0
  377. package/hooks/lib/git-probe.js +17 -1
  378. package/hooks/lib/isolation-sentinel.js +2 -2
  379. package/package.json +21 -4
  380. package/scripts/changeset/parse.cjs +52 -4
  381. package/scripts/ci-timeout-report.cjs +770 -4
  382. package/scripts/command-contract-helpers.cjs +12 -8
  383. package/scripts/docs-guard-registry.cjs +6 -0
  384. package/scripts/gen-features.cjs +13 -8
  385. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  386. package/scripts/gen-loop-host-contract.cjs +10 -1
  387. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  388. package/scripts/gen-plugin-skills.cjs +87 -1
  389. package/scripts/gen-research-agents.cjs +24 -31
  390. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  391. package/scripts/gen-test-timings.cjs +32 -7
  392. package/scripts/lib/cli-exit.cjs +12 -0
  393. package/scripts/lib/macos-conformance-tier.generated.cjs +20 -2
  394. package/scripts/lib/ndjson-reporter.cjs +28 -3
  395. package/scripts/lib/platform-conformance-tier.generated.cjs +34 -5
  396. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  397. package/scripts/lib/vendor-bundle.cjs +59 -0
  398. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  399. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  400. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  401. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  402. package/scripts/lint-descriptions.cjs +7 -3
  403. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
  404. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  405. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  406. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  407. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  408. package/scripts/lint-phase-id-drift.cjs +29 -0
  409. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  410. package/scripts/lint-response-language-coverage.cjs +3 -0
  411. package/scripts/lint-skill-deps.cjs +7 -3
  412. package/scripts/lint-test-file-count.allowlist.json +11 -1
  413. package/scripts/lint-test-file-count.cjs +34 -1
  414. package/scripts/lint-vendored-deps.cjs +41 -5
  415. package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
  416. package/scripts/mutation-matrix.cjs +50 -5
  417. package/scripts/require-issue-link-policy.cjs +6 -2
  418. package/scripts/sync-runtime-launcher.cjs +184 -2
  419. package/scripts/verify-npm-publish.cjs +76 -20
  420. package/skills/gsd-add-tests/SKILL.md +6 -1
  421. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  422. package/skills/gsd-audit-fix/SKILL.md +5 -0
  423. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  424. package/skills/gsd-autonomous/SKILL.md +5 -0
  425. package/skills/gsd-capture/SKILL.md +8 -4
  426. package/skills/gsd-code-review/SKILL.md +7 -2
  427. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  428. package/skills/gsd-config/SKILL.md +8 -4
  429. package/skills/gsd-debug/SKILL.md +11 -7
  430. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  431. package/skills/gsd-docs-update/SKILL.md +12 -7
  432. package/skills/gsd-eval-review/SKILL.md +6 -1
  433. package/skills/gsd-execute-phase/SKILL.md +12 -7
  434. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  435. package/skills/gsd-fast/SKILL.md +4 -0
  436. package/skills/gsd-forensics/SKILL.md +5 -1
  437. package/skills/gsd-graphify/SKILL.md +10 -6
  438. package/skills/gsd-health/SKILL.md +5 -0
  439. package/skills/gsd-help/SKILL.md +7 -2
  440. package/skills/gsd-import/SKILL.md +7 -3
  441. package/skills/gsd-inbox/SKILL.md +5 -0
  442. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  443. package/skills/gsd-manager/SKILL.md +6 -1
  444. package/skills/gsd-map-codebase/SKILL.md +7 -3
  445. package/skills/gsd-mempalace-capture/SKILL.md +5 -1
  446. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  447. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  448. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  449. package/skills/gsd-new-milestone/SKILL.md +6 -1
  450. package/skills/gsd-new-project/SKILL.md +5 -0
  451. package/skills/gsd-next/SKILL.md +6 -1
  452. package/skills/gsd-ns-context/SKILL.md +4 -0
  453. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  454. package/skills/gsd-ns-manage/SKILL.md +4 -0
  455. package/skills/gsd-ns-project/SKILL.md +4 -0
  456. package/skills/gsd-ns-review/SKILL.md +4 -0
  457. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  458. package/skills/gsd-onboard/SKILL.md +6 -1
  459. package/skills/gsd-pause-work/SKILL.md +5 -1
  460. package/skills/gsd-phase/SKILL.md +8 -4
  461. package/skills/gsd-plan-phase/SKILL.md +6 -1
  462. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  463. package/skills/gsd-pr-branch/SKILL.md +4 -0
  464. package/skills/gsd-profile-user/SKILL.md +5 -1
  465. package/skills/gsd-progress/SKILL.md +6 -1
  466. package/skills/gsd-quick/SKILL.md +16 -10
  467. package/skills/gsd-quick-batch/SKILL.md +20 -8
  468. package/skills/gsd-review/SKILL.md +5 -1
  469. package/skills/gsd-review-backlog/SKILL.md +3 -2
  470. package/skills/gsd-secure-phase/SKILL.md +6 -1
  471. package/skills/gsd-ship/SKILL.md +5 -0
  472. package/skills/gsd-sketch/SKILL.md +7 -2
  473. package/skills/gsd-spec-phase/SKILL.md +5 -1
  474. package/skills/gsd-spike/SKILL.md +8 -3
  475. package/skills/gsd-surface/SKILL.md +5 -1
  476. package/skills/gsd-thread/SKILL.md +4 -0
  477. package/skills/gsd-ui-phase/SKILL.md +6 -1
  478. package/skills/gsd-ui-review/SKILL.md +6 -1
  479. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  480. package/skills/gsd-undo/SKILL.md +5 -1
  481. package/skills/gsd-update/SKILL.md +6 -2
  482. package/skills/gsd-validate-phase/SKILL.md +6 -1
  483. package/skills/gsd-verify-work/SKILL.md +6 -1
  484. package/skills/gsd-workspace/SKILL.md +7 -3
  485. package/skills/gsd-workstreams/SKILL.md +6 -6
  486. package/vscode/package.json +1 -1
  487. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
package/bin/install.js CHANGED
@@ -49,7 +49,7 @@ const { isTestHomeGuardRefusal } = require('../gsd-core/bin/lib/real-home-guard.
49
49
  // (getConfigDirFromHome and the runtime-content-rewrite loops below) — #2876
50
50
  // retired the re-export; tests now import getDirName directly from
51
51
  // gsd-core/bin/lib/runtime-name-policy.cjs.
52
- const { getDirName, getRuntimeLabel, getGlobalConfigHomeFragment, runtimeFlags, getRuntimeNewProjectCommand } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
52
+ const { getDirName, getRuntimeLabel, getGlobalConfigHomeFragment, runtimeFlags, hostBehaviorsFor } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
53
53
  const {
54
54
  applyWorktreeBaseRef,
55
55
  readBaseRefFromSettings,
@@ -104,7 +104,7 @@ const hooksSurface = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
104
104
  */
105
105
  function shouldNormalizeHyphenNamespaceInAgentBody(runtime) {
106
106
  if (typeof runtime !== 'string' || runtime === '') return false;
107
- return _hostBehaviors(runtime).hyphenNameAgentBody === true;
107
+ return hostBehaviorsFor(runtime).hyphenNameAgentBody === true;
108
108
  }
109
109
 
110
110
  /**
@@ -436,7 +436,7 @@ const GSD_CHANGESET_FILES = [
436
436
  'github-release-notes.cjs', 'lint.cjs', 'new.cjs',
437
437
  'README.md', // documentation only — not user-authored
438
438
  ];
439
- const GSD_SCRIPTS_LIB_FILES = ['cli-exit.cjs', 'allowlist-ratchet.cjs', 'drift-scan.cjs', 'alias-drift-families.cjs', 'exit-code-registry.cjs', 'ndjson-reporter.cjs', 'ci-job-timing.cjs', 'shellcheck-fetch.cjs', 'npm-version-check-diagnosis.cjs', 'platform-conformance-tier.generated.cjs', 'suite-detection.cjs', 'macos-conformance-tier.generated.cjs'];
439
+ const GSD_SCRIPTS_LIB_FILES = ['cli-exit.cjs', 'allowlist-ratchet.cjs', 'drift-scan.cjs', 'alias-drift-families.cjs', 'exit-code-registry.cjs', 'ndjson-reporter.cjs', 'ci-job-timing.cjs', 'shellcheck-fetch.cjs', 'npm-version-check-diagnosis.cjs', 'platform-conformance-tier.generated.cjs', 'suite-detection.cjs', 'macos-conformance-tier.generated.cjs', 'vendor-bundle.cjs', 'registration-ledger-preload.cjs'];
440
440
 
441
441
  // #4544 — the Codex hook payload the install stages into <targetDir>/hooks/.
442
442
  // Hoisted to module scope (the #3184 precedent above) so the rollback's
@@ -473,7 +473,7 @@ const CODEX_HOOKS_TO_COPY = [
473
473
  * @returns {string}
474
474
  */
475
475
  function resolveSharedHooksDirName(runtime) {
476
- const raw = _hostBehaviors(runtime).sharedHooksDirName;
476
+ const raw = hostBehaviorsFor(runtime).sharedHooksDirName;
477
477
  if (typeof raw !== 'string') return SHARED_HOOKS_DIR_DEFAULT;
478
478
  const name = raw.trim();
479
479
  if (name === '') return SHARED_HOOKS_DIR_DEFAULT;
@@ -628,68 +628,6 @@ try {
628
628
  _installedCapabilityRegistry = _capabilityRegistry;
629
629
  }
630
630
 
631
- // Fail-safe floor for the reference host's #338-privacy-critical behaviors, used
632
- // ONLY when the first-party capability registry cannot be loaded (a broken bundle).
633
- // Without it, a registry-load failure would make `_hostBehaviors('claude')` return
634
- // {} and silently route a claude LOCAL install to the repo-shared, committed
635
- // `settings.json` instead of the gitignored `settings.local.json` (#338) — leaking
636
- // engineer-specific absolute paths. Keyed by runtime id (a DATA lookup, not a
637
- // hardcoded string-equality branch) so behavior degrades CLOSED (safe), never open.
638
- // The live descriptor (capabilities/claude/capability.json) remains the source of
639
- // truth; this mirrors only the privacy-load-bearing subset. (ADR-1239 / #2086)
640
- //
641
- // #2870: NOT routed through the Install Scope Module (resolveScope,
642
- // src/install-scope.cts) despite that module owning per-scope settings-file
643
- // resolution elsewhere in this file. resolveScope's own descriptor lookup
644
- // goes through the SAME capability registry require this floor exists to
645
- // survive the failure of (see getRegistry() in install-scope.cts) — so on
646
- // exactly the "registry failed to load" path this constant is for,
647
- // resolveScope would throw too. Routing through it here would trade a
648
- // graceful, documented degrade for a crash in the one case this floor was
649
- // added to prevent. This hardcoded literal is the correct, honest answer,
650
- // not an un-migrated leftover.
651
- const FALLBACK_HOST_BEHAVIORS = Object.freeze({
652
- claude: Object.freeze({
653
- settingsFileByScope: Object.freeze({ local: 'settings.local.json', global: 'settings.json' }),
654
- permissionsSchema: 'claude',
655
- sourceMarkerFile: '.gsd-source',
656
- hyphenNameAgentBody: true,
657
- legacyCommandsGsdInstallMigration: true,
658
- legacyCommandsGsdUninstall: 'global',
659
- }),
660
- // antigravity's global config dir is resolved dynamically (env-overridable,
661
- // multi-segment) via resolveAntigravityGlobalDir in getConfigDirFromHome. If the
662
- // registry fails to load, this floor keeps that routing intact instead of
663
- // silently falling through to the generic getGlobalConfigHomeFragment default
664
- // (which would return the wrong '.claude' fragment). (ADR-1239 / #2096)
665
- antigravity: Object.freeze({ globalDirResolver: 'antigravity' }),
666
- });
667
-
668
- /**
669
- * Resolve a runtime's host behaviors from a capability registry, with the
670
- * #338-privacy fail-safe floor when the registry (or the runtime's descriptor)
671
- * is unavailable. Registry is passed in so this is unit-testable under a
672
- * simulated registry-load failure. (ADR-1239 / #2086)
673
- */
674
- function _resolveHostBehaviors(runtime, registry) {
675
- const cap = registry && registry.runtimes && registry.runtimes[runtime];
676
- const declared = cap && cap.runtime && cap.runtime.hostBehaviors;
677
- if (declared) return declared;
678
- return FALLBACK_HOST_BEHAVIORS[runtime] || {};
679
- }
680
-
681
- /**
682
- * Host-specific install behaviors, declared on the runtime descriptor
683
- * (capabilities/<runtime>/capability.json -> runtime.hostBehaviors) instead of
684
- * scattered `runtime === '<id>'` string checks (ADR-1239 / #2086). Returns {}
685
- * for runtimes that declare none, so every behavior branch degrades to the
686
- * generic path by default — EXCEPT the reference host's #338-critical keys, which
687
- * fall back to FALLBACK_HOST_BEHAVIORS if the registry failed to load.
688
- */
689
- function _hostBehaviors(runtime) {
690
- return _resolveHostBehaviors(runtime, _capabilityRegistry);
691
- }
692
-
693
631
  /**
694
632
  * Read a runtime's documentation-sourced `hostIntegration.dispatch` axes
695
633
  * (ADR-1239 Phase A — `capabilities/<runtime>/capability.json`
@@ -697,7 +635,7 @@ function _hostBehaviors(runtime) {
697
635
  * background, backgroundDispatch, subagentToolkit}`. These are validated,
698
636
  * closed-vocabulary FACTS about what the runtime's real dispatch primitive
699
637
  * supports (never inferred) — see `docs/reference/host-integration-capability-
700
- * matrix.md` for citations. Unlike `_hostBehaviors` (install *policy*), this is
638
+ * matrix.md` for citations. Unlike `hostBehaviorsFor` (install *policy*), this is
701
639
  * the negotiated *capability* surface; #2284 is its first content-projection
702
640
  * consumer (previously read only by `shouldFlattenDispatch`). Returns `{}` if
703
641
  * the registry or the runtime's descriptor is unavailable, so callers must
@@ -833,6 +771,7 @@ const {
833
771
  } = require(path.join(_gsdLibDir, 'installer-migration-report.cjs'));
834
772
  const {
835
773
  resolveRuntimeArtifactLayout,
774
+ resolveAdvertisedNewProject,
836
775
  } = require(path.join(_gsdLibDir, 'runtime-artifact-layout.cjs'));
837
776
  const {
838
777
  readSurface,
@@ -1178,13 +1117,13 @@ function getConfigDirFromHome(runtime, isGlobal) {
1178
1117
  // !isGlobal returns at the top of this function.)
1179
1118
  // Descriptor-driven (ADR-1239 / #2096): folded from a hardcoded
1180
1119
  // `runtime === 'antigravity'` literal into a read of the runtime's
1181
- // `hostBehaviors.globalDirResolver` descriptor field (via _hostBehaviors, which
1182
- // also degrades to FALLBACK_HOST_BEHAVIORS on registry-load failure). This is
1120
+ // `hostBehaviors.globalDirResolver` descriptor field (via hostBehaviorsFor, which
1121
+ // also degrades to its #338 floor on registry-load failure). This is
1183
1122
  // antigravity-unique: unlike `configHome.kind === 'dot-home-nested'` (which
1184
1123
  // windsurf also declares — see capabilities/windsurf/capability.json — and
1185
1124
  // would wrongly route windsurf's global dir through
1186
1125
  // resolveAntigravityGlobalDir), `globalDirResolver` is only set by antigravity.
1187
- if (_hostBehaviors(runtime).globalDirResolver === 'antigravity') {
1126
+ if (hostBehaviorsFor(runtime).globalDirResolver === 'antigravity') {
1188
1127
  const antigravityDir = resolveAntigravityGlobalDir();
1189
1128
  const rel = path.relative(os.homedir(), antigravityDir);
1190
1129
  const segments = rel.split(path.sep).filter(Boolean);
@@ -1594,12 +1533,12 @@ function getCommitAttribution(runtime) {
1594
1533
 
1595
1534
  let result;
1596
1535
 
1597
- const _attrResolverKey = _hostBehaviors(runtime).attributionConfigResolver;
1536
+ const _attrResolverKey = hostBehaviorsFor(runtime).attributionConfigResolver;
1598
1537
  if (_attrResolverKey && ATTRIBUTION_CONFIG_RESOLVERS[_attrResolverKey]) {
1599
1538
  const resolveConfigPath = ATTRIBUTION_CONFIG_RESOLVERS[_attrResolverKey];
1600
1539
  const config = readSettings(resolveConfigPath(getGlobalConfigDir(runtime, null)));
1601
1540
  result = (config && config.disable_ai_attribution === true) ? null : undefined;
1602
- } else if (_hostBehaviors(runtime).attributionSource === 'settings-json-commit') {
1541
+ } else if (hostBehaviorsFor(runtime).attributionSource === 'settings-json-commit') {
1603
1542
  // Claude Code
1604
1543
  const settings = readSettings(path.join(getGlobalConfigDir(runtime, explicitConfigDir), 'settings.json'));
1605
1544
  if (!settings || !settings.attribution || settings.attribution.commit === undefined) {
@@ -2084,13 +2023,13 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
2084
2023
  // Hermes' SKILL.md spec lists `version` as a required frontmatter field.
2085
2024
  // Track GSD's package version so Hermes' skill_view() reports a stable
2086
2025
  // identifier per install.
2087
- if (_hostBehaviors(runtime).skillFrontmatterVersion) fm += `version: ${yamlQuote(pkg.version)}\n`;
2026
+ if (hostBehaviorsFor(runtime).skillFrontmatterVersion) fm += `version: ${yamlQuote(pkg.version)}\n`;
2088
2027
  // #778 (b) — numeric priority for /skills ordering, declared on the runtime
2089
2028
  // descriptor (runtime.hostBehaviors.skillPriorityFrontmatter). Scoped to
2090
2029
  // runtimes that declare the flag so Claude/Hermes skill frontmatter is
2091
2030
  // unchanged (they ignore the field, but we keep their output byte-stable).
2092
2031
  // skillName is the `gsd-<stem>` dir name. (ADR-1239 / #2086)
2093
- if (_hostBehaviors(runtime).skillPriorityFrontmatter) {
2032
+ if (hostBehaviorsFor(runtime).skillPriorityFrontmatter) {
2094
2033
  const stem = typeof skillName === 'string' && skillName.startsWith('gsd-')
2095
2034
  ? skillName.slice(4)
2096
2035
  : skillName;
@@ -2555,20 +2494,11 @@ function yamlIdentifier(value) {
2555
2494
  return yamlQuote(text);
2556
2495
  }
2557
2496
 
2497
+ // The frontmatter block is the one the one fence owner (`locateFrontmatterFence`) finds, read
2498
+ // through the conversion module's reader — never a local `indexOf('---', 3)` scan, which ended
2499
+ // the block at the first `---` anywhere, even inside a value (found while implementing #5105).
2558
2500
  function extractFrontmatterAndBody(content) {
2559
- if (!content.startsWith('---')) {
2560
- return { frontmatter: null, body: content };
2561
- }
2562
-
2563
- const endIndex = content.indexOf('---', 3);
2564
- if (endIndex === -1) {
2565
- return { frontmatter: null, body: content };
2566
- }
2567
-
2568
- return {
2569
- frontmatter: content.substring(3, endIndex).trim(),
2570
- body: content.substring(endIndex + 3),
2571
- };
2501
+ return runtimeArtifactConversion.extractFrontmatterAndBody(content);
2572
2502
  }
2573
2503
 
2574
2504
  function extractFrontmatterField(frontmatter, fieldName) {
@@ -2813,7 +2743,7 @@ function convertClaudeCommandToTraeSkill(content, skillName) {
2813
2743
  // is not formally documented (thin SPA docs) — descriptor-driven, single
2814
2744
  // fixed GSD-side value (runtime.hostBehaviors.soloStageMetadata), inferred/
2815
2745
  // best-effort.
2816
- const soloStage = _hostBehaviors('trae').soloStageMetadata;
2746
+ const soloStage = hostBehaviorsFor('trae').soloStageMetadata;
2817
2747
  if (soloStage) fm += `stage: ${soloStage}\n`;
2818
2748
  fm += '---';
2819
2749
  return `${fm}\n${body}`;
@@ -3806,7 +3736,7 @@ const HERMES_DISPATCH_TOOL_CONFIG = Object.freeze({
3806
3736
  */
3807
3737
  function convertClaudeToHermesMarkdown(content, ctx) {
3808
3738
  const runtime = (ctx && ctx.runtime) || 'hermes';
3809
- const b = _hostBehaviors(runtime).brandingRewrites;
3739
+ const b = hostBehaviorsFor(runtime).brandingRewrites;
3810
3740
  let converted = content;
3811
3741
  if (b) {
3812
3742
  converted = converted.replace(/CLAUDE\.md/g, b['CLAUDE.md']);
@@ -7040,7 +6970,7 @@ function writeCopilotHookConfig(targetDir) {
7040
6970
  * model aliases). Preserves an explicit `true` opt-in and existing values.
7041
6971
  */
7042
6972
  function writeNonClaudeDefaults(runtime) {
7043
- if (_hostBehaviors(runtime).nativeModelAliases || process.env.GSD_TEST_MODE) return;
6973
+ if (hostBehaviorsFor(runtime).nativeModelAliases || process.env.GSD_TEST_MODE) return;
7044
6974
  const gsdDir = path.join(os.homedir(), '.gsd');
7045
6975
  const defaultsPath = path.join(gsdDir, 'defaults.json');
7046
6976
  let releaseLock = null;
@@ -7271,20 +7201,12 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
7271
7201
  // Runtime-neutral agent name replacement (#766)
7272
7202
  convertedContent = neutralizeAgentReferences(convertedContent, 'AGENTS.md');
7273
7203
 
7274
- // Check if content has frontmatter
7275
- if (!convertedContent.startsWith('---')) {
7276
- return convertedContent;
7277
- }
7278
-
7279
- // Find the end of frontmatter
7280
- const endIndex = convertedContent.indexOf('---', 3);
7281
- if (endIndex === -1) {
7204
+ // The frontmatter block, as the one fence owner finds it (none → nothing to convert).
7205
+ const { frontmatter, body } = extractFrontmatterAndBody(convertedContent);
7206
+ if (frontmatter === null) {
7282
7207
  return convertedContent;
7283
7208
  }
7284
7209
 
7285
- const frontmatter = convertedContent.substring(3, endIndex).trim();
7286
- const body = convertedContent.substring(endIndex + 3);
7287
-
7288
7210
  // Parse frontmatter line by line (simple YAML parsing)
7289
7211
  const lines = frontmatter.split('\n');
7290
7212
  const newLines = [];
@@ -7438,20 +7360,12 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
7438
7360
  // Runtime-neutral agent name replacement (#766)
7439
7361
  convertedContent = neutralizeAgentReferences(convertedContent, 'AGENTS.md');
7440
7362
 
7441
- // Check if content has frontmatter
7442
- if (!convertedContent.startsWith('---')) {
7443
- return convertedContent;
7444
- }
7445
-
7446
- // Find the end of frontmatter
7447
- const endIndex = convertedContent.indexOf('---', 3);
7448
- if (endIndex === -1) {
7363
+ // The frontmatter block, as the one fence owner finds it (none → nothing to convert).
7364
+ const { frontmatter, body } = extractFrontmatterAndBody(convertedContent);
7365
+ if (frontmatter === null) {
7449
7366
  return convertedContent;
7450
7367
  }
7451
7368
 
7452
- const frontmatter = convertedContent.substring(3, endIndex).trim();
7453
- const body = convertedContent.substring(endIndex + 3);
7454
-
7455
7369
  // Parse frontmatter line by line (simple YAML parsing)
7456
7370
  const lines = frontmatter.split('\n');
7457
7371
  const newLines = [];
@@ -7825,7 +7739,7 @@ const RUNTIME_CONTENT_DISPATCH = {
7825
7739
  },
7826
7740
  },
7827
7741
  // qwen/hermes: brand VALUES are descriptor-driven (ADR-1239 / #2092) via
7828
- // _hostBehaviors(ctx.runtime).brandingRewrites — EXACT regexes/ordering
7742
+ // hostBehaviorsFor(ctx.runtime).brandingRewrites — EXACT regexes/ordering
7829
7743
  // preserved from the prior hardcoded-literal versions (including the
7830
7744
  // qwen-specific `.claude/skills/` -> `.qwen/skills/` pre-rewrite, whose
7831
7745
  // target is derived as `${b['.claude/']}skills/`).
@@ -7833,7 +7747,7 @@ const RUNTIME_CONTENT_DISPATCH = {
7833
7747
  md: (content, ctx) => {
7834
7748
  // Guarded (post-review #2092): degrade closed to a no-op if the
7835
7749
  // registry fails to load, instead of throwing on `b['CLAUDE.md']`.
7836
- const b = _hostBehaviors(ctx.runtime).brandingRewrites;
7750
+ const b = hostBehaviorsFor(ctx.runtime).brandingRewrites;
7837
7751
  if (b) {
7838
7752
  content = content.replace(/CLAUDE\.md/g, b['CLAUDE.md']);
7839
7753
  // #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
@@ -7843,7 +7757,7 @@ const RUNTIME_CONTENT_DISPATCH = {
7843
7757
  return content;
7844
7758
  },
7845
7759
  js: (content, ctx) => {
7846
- const b = _hostBehaviors(ctx.runtime).brandingRewrites;
7760
+ const b = hostBehaviorsFor(ctx.runtime).brandingRewrites;
7847
7761
  if (b) {
7848
7762
  content = content.replace(/\.claude\/skills\//g, `${b['.claude/']}skills/`);
7849
7763
  content = content.replace(/\.claude\//g, b['.claude/']);
@@ -7862,7 +7776,7 @@ const RUNTIME_CONTENT_DISPATCH = {
7862
7776
  // hostIntegration.dispatch facts.
7863
7777
  md: (content, ctx) => convertClaudeToHermesMarkdown(content, ctx),
7864
7778
  js: (content, ctx) => {
7865
- const b = _hostBehaviors(ctx.runtime).brandingRewrites;
7779
+ const b = hostBehaviorsFor(ctx.runtime).brandingRewrites;
7866
7780
  if (b) {
7867
7781
  content = content.replace(/\.claude\/skills\//g, `${b['.claude/']}skills/`);
7868
7782
  content = content.replace(/\.claude\//g, b['.claude/']);
@@ -8025,7 +7939,7 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
8025
7939
  // emit path, which never had it: every @~/.claude/gsd-core/… include
8026
7940
  // in a global install's workflows/references tree silently resolved
8027
7941
  // to nothing (54 includes across 22 files on a live install).
8028
- if (runtime === 'claude') {
7942
+ if (hostBehaviorsFor(runtime).restoreAtRefTildeInSpecTree) {
8029
7943
  content = runtimeArtifactConversion._restoreClaudeGlobalAtRefTilde(content, pathPrefix);
8030
7944
  }
8031
7945
  return content;
@@ -8041,7 +7955,7 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
8041
7955
  // copyWithPathReplacement is the emit path for gsd-core/workflows/*.md;
8042
7956
  // _applyRuntimeRewrites is NOT invoked here, so this is what makes the fix
8043
7957
  // live in real installs (it is a no-op for files without those lines).
8044
- if (!_hostBehaviors(runtime).authorsCanonicalWorkflow) {
7958
+ if (!hostBehaviorsFor(runtime).authorsCanonicalWorkflow) {
8045
7959
  content = _stampNonClaudeRuntimeDefaults(content, runtime);
8046
7960
  }
8047
7961
 
@@ -8391,7 +8305,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8391
8305
  // `runtime === 'cline'` branch into hostBehaviors.localTargetIsProjectRoot.
8392
8306
  const targetDir = isGlobal
8393
8307
  ? getGlobalConfigDir(runtime, explicitConfigDir)
8394
- : _hostBehaviors(runtime).localTargetIsProjectRoot
8308
+ : hostBehaviorsFor(runtime).localTargetIsProjectRoot
8395
8309
  ? process.cwd()
8396
8310
  : path.join(process.cwd(), dirName);
8397
8311
 
@@ -8499,7 +8413,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8499
8413
  }
8500
8414
 
8501
8415
  // 1a. Non-layout Codex side-effects: agent .toml files, config.toml sections, hooks.json
8502
- if (_hostBehaviors(runtime).tomlConfigInstall) {
8416
+ if (hostBehaviorsFor(runtime).tomlConfigInstall) {
8503
8417
  const codexAgentsDir = path.join(targetDir, 'agents');
8504
8418
  if (fs.existsSync(codexAgentsDir)) {
8505
8419
  const tomlFiles = fs.readdirSync(codexAgentsDir);
@@ -8613,7 +8527,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8613
8527
  // global cross-tool ~/.agents/AGENTS.md target.
8614
8528
  // Descriptor-driven (ADR-1239 / #2090): folded from `runtime === 'cline'`
8615
8529
  // into hostBehaviors.clineRulesSurface.
8616
- if (_hostBehaviors(runtime).clineRulesSurface) {
8530
+ if (hostBehaviorsFor(runtime).clineRulesSurface) {
8617
8531
  const clinerulesDir = path.join(targetDir, '.clinerules');
8618
8532
  for (const rel of ['gsd.md', path.join('hooks', 'PreToolUse')]) {
8619
8533
  const p = path.join(clinerulesDir, rel);
@@ -8663,7 +8577,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8663
8577
  // GSD-managed hook entries from hooks.json and clean up the managed hook
8664
8578
  // scripts. Gated by the hostBehaviors.hooksJsonSurface descriptor axis, not a
8665
8579
  // hardcoded `isCursor` branch.
8666
- if (_hostBehaviors(runtime).hooksJsonSurface) {
8580
+ if (hostBehaviorsFor(runtime).hooksJsonSurface) {
8667
8581
  const hooksJsonCleanup = removeCursorHooksJson(targetDir);
8668
8582
  if (hooksJsonCleanup.changed) {
8669
8583
  removedCount++;
@@ -8723,7 +8637,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8723
8637
 
8724
8638
  // 1c. Claude local: remove flat gsd-*.md commands from commands/ (current layout,
8725
8639
  // #1367 fix). Also remove legacy commands/gsd/ subdirectory from prior installs.
8726
- if (!isGlobal && _hostBehaviors(runtime).localInstallStyle === 'legacy-flat') {
8640
+ if (!isGlobal && hostBehaviorsFor(runtime).localInstallStyle === 'legacy-flat') {
8727
8641
  const commandsDir = path.join(targetDir, 'commands');
8728
8642
  // Remove flat gsd-*.md files (current layout after #1367 fix)
8729
8643
  if (fs.existsSync(commandsDir)) {
@@ -8789,7 +8703,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
8789
8703
  // removes the directory; we must preserve/restore user artifacts before that path.
8790
8704
  // This block runs AFTER uninstallRuntimeArtifacts, so we check if the directory
8791
8705
  // was already removed and skip if so (idempotent).
8792
- if (_hostBehaviors(runtime).legacyCommandsGsdCleanup === true) {
8706
+ if (hostBehaviorsFor(runtime).legacyCommandsGsdCleanup === true) {
8793
8707
  // dev-preferences may have survived in skills/ as SKILL.md — nothing to do for
8794
8708
  // that case. If a stale commands/gsd/ still exists (e.g. legacy was not removed),
8795
8709
  // attempt migration. In practice _runLegacyUninstallCleanup removes it first,
@@ -9004,7 +8918,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
9004
8918
  // that declares the block (OpenCode, Kilo, ...), not just OpenCode. Only
9005
8919
  // GSD's own plugin file is removed; the plugins/ dir is pruned only if it
9006
8920
  // becomes empty, preserving any user-authored plugins for that host.
9007
- const _np = _hostBehaviors(runtime).nativePlugin;
8921
+ const _np = hostBehaviorsFor(runtime).nativePlugin;
9008
8922
  if (_np) {
9009
8923
  const pluginsDir = path.join(targetDir, _np.dir);
9010
8924
  const pluginPath = path.join(pluginsDir, _np.file);
@@ -9169,7 +9083,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
9169
9083
  // to preserve any user-added allow/deny entries.
9170
9084
  // Uses a local flag to avoid the shared `settingsModified` producing a false
9171
9085
  // "Removed GSD permissions" message when only hooks/statusline changed.
9172
- if (_hostBehaviors(runtime).permissionsSchema === 'claude' && settings.permissions) {
9086
+ if (hostBehaviorsFor(runtime).permissionsSchema === 'claude' && settings.permissions) {
9173
9087
  let permissionsModified = false;
9174
9088
  if (Array.isArray(settings.permissions.allow)) {
9175
9089
  const before = settings.permissions.allow.length;
@@ -9240,7 +9154,7 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
9240
9154
  // runtimes that host MCP there (Augment), symmetric to the mcp_config.json
9241
9155
  // removal for Antigravity below. Only the GSD-owned mcpServers.gsd key is
9242
9156
  // removed — any other user-configured MCP servers are preserved.
9243
- if (_hostBehaviors(runtime).mcpCompanion === 'settings-json' &&
9157
+ if (hostBehaviorsFor(runtime).mcpCompanion === 'settings-json' &&
9244
9158
  settings.mcpServers && typeof settings.mcpServers === 'object' &&
9245
9159
  settings.mcpServers.gsd !== undefined) {
9246
9160
  delete settings.mcpServers.gsd;
@@ -9929,7 +9843,7 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
9929
9843
  // #1367: Claude local now writes flat gsd-*.md files at commands/ (not commands/gsd/).
9930
9844
  // Claude local uses flatCommandsDir instead for manifest recording.
9931
9845
  const flatCommandsDir = path.join(configDir, 'commands');
9932
- const opencodeCommandDir = path.join(configDir, _hostBehaviors(runtime).flatCommandDir || 'command');
9846
+ const opencodeCommandDir = path.join(configDir, hostBehaviorsFor(runtime).flatCommandDir || 'command');
9933
9847
  // Hermes nests GSD skills under skills/gsd/ as a single category (#2841) —
9934
9848
  // already encoded in its layout descriptor's destSubpath ('skills/gsd').
9935
9849
  // All other runtimes that use the Codex-style skills layout use a flat skills/ root.
@@ -9944,7 +9858,7 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
9944
9858
  // `options.scope` could drift; one cannot.
9945
9859
  const resolvedScope = options.scope === 'local' ? 'local' : 'global';
9946
9860
  const codexSkillsDir = _resolveSkillsRootDir(runtime, configDir, resolvedScope);
9947
- const codexSkillsManifestPrefix = _hostBehaviors(runtime).skillsManifestPrefix || 'skills/';
9861
+ const codexSkillsManifestPrefix = hostBehaviorsFor(runtime).skillsManifestPrefix || 'skills/';
9948
9862
  // #3738: resolve the ACTUAL agents-install dir honoring an agents-kind `home`
9949
9863
  // override (antigravity global → $HOME/.gemini/config/agents), mirroring
9950
9864
  // _resolveSkillsRootDir for skills. Hardcoding configDir/agents left the
@@ -9996,26 +9910,26 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
9996
9910
  // Claude local (#1367): flat gsd-*.md files at commands/ level.
9997
9911
  // Only claude local writes gsd-*.md here; global installs don't emit commands,
9998
9912
  // so this branch is a no-op for global (no matching files to find).
9999
- if (_hostBehaviors(runtime).localInstallStyle === 'legacy-flat' && fs.existsSync(flatCommandsDir)) {
9913
+ if (hostBehaviorsFor(runtime).localInstallStyle === 'legacy-flat' && fs.existsSync(flatCommandsDir)) {
10000
9914
  for (const file of fs.readdirSync(flatCommandsDir)) {
10001
9915
  if (file.startsWith('gsd-') && file.endsWith('.md')) {
10002
9916
  manifest.files['commands/' + file] = fileHash(path.join(flatCommandsDir, file));
10003
9917
  }
10004
9918
  }
10005
9919
  }
10006
- if (_hostBehaviors(runtime).flatCommandDir && fs.existsSync(opencodeCommandDir)) {
9920
+ if (hostBehaviorsFor(runtime).flatCommandDir && fs.existsSync(opencodeCommandDir)) {
10007
9921
  // #2329: derive the manifest key prefix from the SAME descriptor value used
10008
9922
  // to compute opencodeCommandDir above, instead of a separately-hardcoded
10009
9923
  // literal — a divergence here would silently break the manifest even after
10010
9924
  // the destSubpath descriptor is corrected (Generative Fix Divergence guard).
10011
- const flatCommandDirPrefix = _hostBehaviors(runtime).flatCommandDir || 'command';
9925
+ const flatCommandDirPrefix = hostBehaviorsFor(runtime).flatCommandDir || 'command';
10012
9926
  for (const file of fs.readdirSync(opencodeCommandDir)) {
10013
9927
  if (file.startsWith('gsd-') && file.endsWith('.md')) {
10014
9928
  manifest.files[flatCommandDirPrefix + '/' + file] = fileHash(path.join(opencodeCommandDir, file));
10015
9929
  }
10016
9930
  }
10017
9931
  }
10018
- if (!_hostBehaviors(runtime).skipCodexSkillsManifest && fs.existsSync(codexSkillsDir)) {
9932
+ if (!hostBehaviorsFor(runtime).skipCodexSkillsManifest && fs.existsSync(codexSkillsDir)) {
10019
9933
  // All runtimes (including Hermes post-#947) use the canonical 'gsd-' prefix.
10020
9934
  const skillListPrefix = 'gsd-';
10021
9935
  for (const skillName of listCodexSkillNames(codexSkillsDir, skillListPrefix)) {
@@ -10026,14 +9940,14 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
10026
9940
  }
10027
9941
  }
10028
9942
  // Descriptor-driven (#2090): hash the category DESCRIPTION.md so reinstall detects drift.
10029
- if (_hostBehaviors(runtime).trackCategoryDescription) {
9943
+ if (hostBehaviorsFor(runtime).trackCategoryDescription) {
10030
9944
  const descPath = path.join(codexSkillsDir, 'DESCRIPTION.md');
10031
9945
  if (fs.existsSync(descPath)) {
10032
9946
  manifest.files['skills/gsd/DESCRIPTION.md'] = fileHash(descPath);
10033
9947
  }
10034
9948
  }
10035
9949
  }
10036
- if (_hostBehaviors(runtime).agentManifestStyle === 'kimi-nested' && fs.existsSync(agentsDir)) {
9950
+ if (hostBehaviorsFor(runtime).agentManifestStyle === 'kimi-nested' && fs.existsSync(agentsDir)) {
10037
9951
  const agentHashes = generateManifest(agentsDir);
10038
9952
  for (const [rel, hash] of Object.entries(agentHashes)) {
10039
9953
  const isRootAgent = rel === 'gsd.yaml' || rel === 'gsd.md';
@@ -10054,7 +9968,7 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
10054
9968
  // marker block, not the per-configDir manifest, since it lives outside it.)
10055
9969
  // Descriptor-driven (ADR-1239 / #2090): folded from `isCline` into
10056
9970
  // hostBehaviors.clineRulesSurface.
10057
- if (_hostBehaviors(runtime).clineRulesSurface) {
9971
+ if (hostBehaviorsFor(runtime).clineRulesSurface) {
10058
9972
  for (const rel of ['.clinerules/gsd.md', '.clinerules/hooks/PreToolUse']) {
10059
9973
  const dest = path.join(configDir, rel);
10060
9974
  if (fs.existsSync(dest)) {
@@ -10075,7 +9989,7 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
10075
9989
  // skipSharedHooksInstall:true) — the redundant `&& !isCopilot` was removed.
10076
9990
  // #2100: Windsurf's exclusion is likewise descriptor-driven (windsurf declares
10077
9991
  // skipSharedHooksInstall:true) — the redundant `&& !isWindsurf` was removed.
10078
- if (!isCodex && _hostBehaviors(runtime).skipSharedHooksInstall !== true) {
9992
+ if (!isCodex && hostBehaviorsFor(runtime).skipSharedHooksInstall !== true) {
10079
9993
  // #3023: manifest keys must track the bundle wherever the descriptor put it,
10080
9994
  // or uninstall/saveLocalPatches silently orphan the tree.
10081
9995
  const sharedHooksDirName = resolveSharedHooksDirName(runtime);
@@ -10142,7 +10056,7 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
10142
10056
 
10143
10057
  // Track the OpenCode native plugin adapter (#1914) so update/drift detection
10144
10058
  // and uninstall can account for it.
10145
- const _npM = _hostBehaviors(runtime).nativePlugin;
10059
+ const _npM = hostBehaviorsFor(runtime).nativePlugin;
10146
10060
  if (_npM) {
10147
10061
  const pluginInstallPath = path.join(configDir, _npM.dir, _npM.file);
10148
10062
  if (fs.existsSync(pluginInstallPath)) {
@@ -10373,7 +10287,7 @@ function saveLocalPatches(configDir, pristineCtx) {
10373
10287
  skillsRoot !== resolvedConfig &&
10374
10288
  !skillsRoot.startsWith(resolvedConfig + path.sep)
10375
10289
  ) {
10376
- const prefix = _hostBehaviors(patchRuntime).skillsManifestPrefix || 'skills/';
10290
+ const prefix = hostBehaviorsFor(patchRuntime).skillsManifestPrefix || 'skills/';
10377
10291
  skillsRedirect = { root: skillsRoot, prefix };
10378
10292
  }
10379
10293
  }
@@ -10596,7 +10510,7 @@ function reportLocalPatches(configDir, runtime = DEFAULT_RUNTIME) {
10596
10510
  try { meta = JSON.parse(fs.readFileSync(metaPath, 'utf8')); } catch { return []; }
10597
10511
 
10598
10512
  if (meta.files && meta.files.length > 0) {
10599
- const reapplyCommand = _hostBehaviors(runtime).reapplyCommand || '/gsd-update --reapply';
10513
+ const reapplyCommand = hostBehaviorsFor(runtime).reapplyCommand || '/gsd-update --reapply';
10600
10514
  console.log('');
10601
10515
  console.log(' ' + yellow + 'Local patches detected' + reset + ' (from v' + meta.from_version + '):');
10602
10516
  for (const f of meta.files) {
@@ -10624,12 +10538,12 @@ function reportInstallerMigrationResult(result) {
10624
10538
 
10625
10539
  function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10626
10540
  // #2093: isKilo dropped — Kilo's agent/model-override handling below reads
10627
- // _hostBehaviors(runtime).frontmatterDialect === 'kilo' instead of this flag.
10541
+ // hostBehaviorsFor(runtime).frontmatterDialect === 'kilo' instead of this flag.
10628
10542
  // #2095: isKimi dropped — kimi is now a hooks/ consumer like every other
10629
10543
  // settings-json-adjacent runtime; the two `&& !isKimi` hooks-copy guards
10630
10544
  // below were removed, leaving isKimi unused in this function (the kimi
10631
10545
  // local-install-deferred branch above already reads
10632
- // _hostBehaviors(runtime).localInstallDeferred instead of this flag).
10546
+ // hostBehaviorsFor(runtime).localInstallDeferred instead of this flag).
10633
10547
  // #2096: isAntigravity dropped — antigravity's agents were already
10634
10548
  // descriptor-driven (installRuntimeArtifacts), so its two legacy-agent-loop
10635
10549
  // branches (the path-rewrite skip and the converter dispatch) were
@@ -10669,7 +10583,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10669
10583
  // re-running install()) can warn again if the same condition recurs.
10670
10584
  _codexResolverModelOmittedWarned = false;
10671
10585
 
10672
- if (_hostBehaviors(runtime).localInstallDeferred && !isGlobal) {
10586
+ if (hostBehaviorsFor(runtime).localInstallDeferred && !isGlobal) {
10673
10587
  console.log(` ${yellow}⚠${reset} Kimi local install is deferred for Phase 2.`);
10674
10588
  console.log(` No .kimi-code/skills or .agents/skills project artifacts were written.`);
10675
10589
  console.log(` Project-level Kimi install semantics remain deferred.`);
@@ -10740,7 +10654,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10740
10654
  // auto-removed on reinstall (dual-read fallback per issue #791 spec).
10741
10655
  const targetDir = isGlobal
10742
10656
  ? getGlobalConfigDir(runtime, explicitConfigDir)
10743
- : _hostBehaviors(runtime).localTargetIsProjectRoot
10657
+ : hostBehaviorsFor(runtime).localTargetIsProjectRoot
10744
10658
  ? process.cwd()
10745
10659
  : path.join(process.cwd(), dirName);
10746
10660
 
@@ -10855,7 +10769,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10855
10769
  const isWindowsHost = process.platform === 'win32';
10856
10770
  const pathPrefix = computePathPrefix({
10857
10771
  isGlobal,
10858
- isOpencode: _hostBehaviors(runtime).skipHomePrefixSubstitution === true,
10772
+ isOpencode: hostBehaviorsFor(runtime).skipHomePrefixSubstitution === true,
10859
10773
  isWindowsHost,
10860
10774
  resolvedTarget,
10861
10775
  homeDir,
@@ -10863,7 +10777,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10863
10777
  // copyWithPathReplacement, i.e. the one actually written into every
10864
10778
  // emitted command/skill/workflow body — the rewrite-engine seams below
10865
10779
  // handle re-applied surfaces, not the first install.
10866
- localDirName: _hostBehaviors(runtime).localTargetIsProjectRoot === true ? undefined : getDirName(runtime),
10780
+ localDirName: hostBehaviorsFor(runtime).localTargetIsProjectRoot === true ? undefined : getDirName(runtime),
10867
10781
  });
10868
10782
 
10869
10783
  // runtimeLabel is now the single-source getRuntimeLabel lookup (ADR-1239
@@ -10967,7 +10881,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
10967
10881
  // a core/--minimal install too (#2695), and its pass-2 sweeps remove every
10968
10882
  // gsd-* skill dir / agent file the snapshot does not claim — so an empty
10969
10883
  // minimal-mode snapshot deleted the whole surface with nothing to restore.
10970
- if (_hostBehaviors(runtime).tomlConfigInstall) {
10884
+ if (hostBehaviorsFor(runtime).tomlConfigInstall) {
10971
10885
  codexManagedSnapshotCaptured = true;
10972
10886
  const _preSkillsDir = _resolveSkillsRootDir(runtime, targetDir, _installScopeId);
10973
10887
  if (fs.existsSync(_preSkillsDir)) {
@@ -11216,7 +11130,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11216
11130
  // The full restoreCodexSnapshot() (defined inside the config block) additionally
11217
11131
  // handles config.toml and the staged hooks/ tree, which are not yet touched
11218
11132
  // at this point in the pipeline.
11219
- const _codexPreConfigRollback = !_hostBehaviors(runtime).tomlConfigInstall || isMinimalMode(_effectiveInstallMode) ? null : () => {
11133
+ const _codexPreConfigRollback = !hostBehaviorsFor(runtime).tomlConfigInstall || isMinimalMode(_effectiveInstallMode) ? null : () => {
11220
11134
  rollbackInstallerMigrations();
11221
11135
  // skills/gsd-* — pass 1: restore snapshot entries (may be absent if deleted mid-install).
11222
11136
  const _earlySkillsDir = _resolveSkillsRootDir(runtime, targetDir, _installScopeId);
@@ -11406,7 +11320,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11406
11320
  // used to describe. Claude-local remains the one special-cased path
11407
11321
  // (copyWithPathReplacement + stale-skills cleanup).
11408
11322
  const _isSkillsRuntime = (() => {
11409
- if (_hostBehaviors(runtime).localInstallStyle === 'legacy-flat' && !isGlobal) return false; // legacy flat local path (descriptor-driven; #2086)
11323
+ if (hostBehaviorsFor(runtime).localInstallStyle === 'legacy-flat' && !isGlobal) return false; // legacy flat local path (descriptor-driven; #2086)
11410
11324
  // #2875 Part 2 defect fix: a runtime whose LOCAL commands are embedded in a
11411
11325
  // rules file rather than materialized as files (hostBehaviors.localCommandsViaRules
11412
11326
  // — cline is the only declarant, capabilities/cline/capability.json) must not
@@ -11417,7 +11331,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11417
11331
  // spuriously fail; the `localCommandsViaRules` branch below (unchanged
11418
11332
  // messaging) and the unconditional agents-materialization block further down
11419
11333
  // (installAgentsKindStandalone) already cover this runtime/scope correctly.
11420
- if (!isGlobal && _hostBehaviors(runtime).localCommandsViaRules) return false;
11334
+ if (!isGlobal && hostBehaviorsFor(runtime).localCommandsViaRules) return false;
11421
11335
  const cap = _capabilityRegistry && _capabilityRegistry.runtimes && _capabilityRegistry.runtimes[runtime];
11422
11336
  const layout = cap && cap.runtime && cap.runtime.artifactLayout;
11423
11337
  if (!layout) return false;
@@ -11456,13 +11370,13 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11456
11370
  // marker. Write failure is non-fatal (install proceeds; warn so /gsd-surface breakage is
11457
11371
  // diagnosable) — the same contract the late write had.
11458
11372
  function _writeGsdSourceMarker(runtime, targetDir, src, isGlobal) {
11459
- if (_hostBehaviors(runtime).sourceMarkerFile && isGlobal) {
11373
+ if (hostBehaviorsFor(runtime).sourceMarkerFile && isGlobal) {
11460
11374
  const gsdSourceCommands = path.join(src, 'commands', 'gsd');
11461
11375
  if (fs.existsSync(gsdSourceCommands)) {
11462
11376
  try {
11463
11377
  // ADR-1239 Phase B write-confinement: the descriptor-sourced marker filename
11464
11378
  // must resolve under targetDir (parity with the other descriptor-driven writes).
11465
- const _markerPath = assertDestWithinConfigHome(targetDir, _hostBehaviors(runtime).sourceMarkerFile);
11379
+ const _markerPath = assertDestWithinConfigHome(targetDir, hostBehaviorsFor(runtime).sourceMarkerFile);
11466
11380
  if (hasExistingSymlinkBetween(path.resolve(targetDir), _markerPath, { allowOptInFollow: isSymlinkedDestOptIn() })) {
11467
11381
  throw new Error(`compatibility marker "${_markerPath}" contains an untrusted symlink`);
11468
11382
  }
@@ -11537,7 +11451,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11537
11451
  // index BOTH SKILL.md and the sidecar, causing each GSD skill to appear twice
11538
11452
  // in autocomplete. Cleaning them up fixes the duplication; SKILL.md alone is
11539
11453
  // sufficient for Codex discovery. User-owned dirs are never touched.
11540
- if (_hostBehaviors(runtime).cleanupSkillSidecars) {
11454
+ if (hostBehaviorsFor(runtime).cleanupSkillSidecars) {
11541
11455
  cleanupCodexSkillMetadataSidecars(_skillsRootDir);
11542
11456
  }
11543
11457
 
@@ -11563,7 +11477,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11563
11477
  // Descriptor-driven (ADR-1239 / #2100): folded from `isWindsurf` into
11564
11478
  // hostBehaviors.legacyDevinSkillsCleanup (windsurf is the only runtime that
11565
11479
  // declares it, so this is byte-parity).
11566
- if (_hostBehaviors(runtime).legacyDevinSkillsCleanup && !isGlobal) {
11480
+ if (hostBehaviorsFor(runtime).legacyDevinSkillsCleanup && !isGlobal) {
11567
11481
  const removedCount = cleanupWindsurfLegacyDevinSkills(process.cwd());
11568
11482
  if (removedCount > 0) {
11569
11483
  console.log(` ${green}✓${reset} Removed ${removedCount} legacy .devin/skills/gsd-* dir(s) (pre-#1615 Windsurf layout)`);
@@ -11571,12 +11485,12 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11571
11485
  }
11572
11486
 
11573
11487
  // Descriptor-driven (#2090): write DESCRIPTION.md for the gsd/ category after layout install
11574
- if (_hostBehaviors(runtime).writeCategoryDescription) {
11488
+ if (hostBehaviorsFor(runtime).writeCategoryDescription) {
11575
11489
  writeHermesCategoryDescription(path.join(targetDir, 'skills', 'gsd'));
11576
11490
  }
11577
11491
 
11578
11492
  // Verify installed artifacts and report
11579
- if (_hostBehaviors(runtime).reportSkillsCount) {
11493
+ if (hostBehaviorsFor(runtime).reportSkillsCount) {
11580
11494
  const hermesSkillsDir = path.join(targetDir, 'skills', 'gsd');
11581
11495
  if (fs.existsSync(hermesSkillsDir)) {
11582
11496
  // Hermes layout uses prefix: 'gsd-' (#947) — skill dirs have gsd-<stem> names
@@ -11590,7 +11504,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11590
11504
  } else {
11591
11505
  failures.push('skills/gsd/*');
11592
11506
  }
11593
- } else if (_hostBehaviors(runtime).verificationStyle === 'kimi') {
11507
+ } else if (hostBehaviorsFor(runtime).verificationStyle === 'kimi') {
11594
11508
  const skillsDir = path.join(targetDir, 'skills');
11595
11509
  const rootAgentPath = path.join(targetDir, 'agents', 'gsd.yaml');
11596
11510
  if (fs.existsSync(skillsDir)) {
@@ -11614,7 +11528,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11614
11528
  // hostBehaviors.verificationStyle === 'windsurf-workflows' (extends the
11615
11529
  // same mechanism the 'kimi' verificationStyle branch above uses; windsurf
11616
11530
  // is the only runtime that declares this value, so this is byte-parity).
11617
- } else if (_hostBehaviors(runtime).verificationStyle === 'windsurf-workflows') {
11531
+ } else if (hostBehaviorsFor(runtime).verificationStyle === 'windsurf-workflows') {
11618
11532
  if (isGlobal) {
11619
11533
  console.log(` ${green}✓${reset} Windsurf global install skipped workflow artifacts (workspace-only)`);
11620
11534
  } else {
@@ -11663,7 +11577,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11663
11577
  // Descriptor-driven commands/ output report (currently CodeBuddy).
11664
11578
  // Cursor retired this parallel surface in #2644 because its skills are
11665
11579
  // already slash-menu entries as well as model-invocable context.
11666
- if (_hostBehaviors(runtime).reportCommandsDir) {
11580
+ if (hostBehaviorsFor(runtime).reportCommandsDir) {
11667
11581
  const commandsDir = path.join(targetDir, 'commands');
11668
11582
  if (fs.existsSync(commandsDir)) {
11669
11583
  const cmdCount = fs.readdirSync(commandsDir)
@@ -11678,14 +11592,14 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11678
11592
  }
11679
11593
  }
11680
11594
  }
11681
- } else if (_hostBehaviors(runtime).localCommandsViaRules) {
11595
+ } else if (hostBehaviorsFor(runtime).localCommandsViaRules) {
11682
11596
  // Cline local install: rules-based only — commands are embedded in .clinerules (generated below).
11683
11597
  // No skills/commands directory needed for local installs.
11684
11598
  // Global installs are handled above by _isSkillsRuntime (#782).
11685
11599
  // Descriptor-driven (ADR-1239 / #2090): folded from `isCline` into
11686
11600
  // hostBehaviors.localCommandsViaRules.
11687
11601
  console.log(` ${green}✓${reset} Cline: commands will be available via .clinerules`);
11688
- } else if (_hostBehaviors(runtime).pluginOnlyInstall) {
11602
+ } else if (hostBehaviorsFor(runtime).pluginOnlyInstall) {
11689
11603
  // pi (ADR-1239 / #2102 Stage 1): plugin-only install — pi's /gsd command is
11690
11604
  // registered programmatically by the native extension (pi/gsd.cjs →
11691
11605
  // extensions/gsd.js, staged separately below; the dest suffix must be
@@ -11793,8 +11707,8 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11793
11707
  // double-stages their plugin file. A runtime like pi, whose artifactLayout is
11794
11708
  // intentionally empty for both scopes (`_isSkillsRuntime` is false), still
11795
11709
  // needs its declared hostBehaviors.nativePlugin file copied into targetDir.
11796
- if (!_isSkillsRuntime && _hostBehaviors(runtime).nativePlugin) {
11797
- _installNativePluginIfDeclared(runtime, targetDir, _hostBehaviors(runtime), src);
11710
+ if (!_isSkillsRuntime && hostBehaviorsFor(runtime).nativePlugin) {
11711
+ _installNativePluginIfDeclared(runtime, targetDir, hostBehaviorsFor(runtime), src);
11798
11712
  }
11799
11713
 
11800
11714
  // #2624: the .gsd-source marker is now written by _writeGsdSourceMarker()
@@ -11815,7 +11729,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11815
11729
  // hostBehaviors.installsCommandBodiesForWorkflowDelegation (windsurf is the
11816
11730
  // only runtime that declares it, so this is byte-parity — the #1629 fix
11817
11731
  // itself is unchanged).
11818
- if (_hostBehaviors(runtime).installsCommandBodiesForWorkflowDelegation && !isGlobal) {
11732
+ if (hostBehaviorsFor(runtime).installsCommandBodiesForWorkflowDelegation && !isGlobal) {
11819
11733
  const commandsSrc = path.join(src, 'commands', 'gsd');
11820
11734
  const commandsDest = path.join(skillDest, 'commands', 'gsd');
11821
11735
  if (fs.existsSync(commandsSrc)) {
@@ -11879,7 +11793,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11879
11793
  // scopes (programmatic dispatch, no named-dispatch subagent toolkit, no
11880
11794
  // host-read markdown surface), so the resolved layout has no `agents` kind
11881
11795
  // to stage and the function returns `null` without writing anything.
11882
- if (_hostBehaviors(runtime).pluginOnlyInstall) {
11796
+ if (hostBehaviorsFor(runtime).pluginOnlyInstall) {
11883
11797
  console.log(` ${green}✓${reset} pi: no subagent files (programmatic dispatch, no named-dispatch toolkit)`);
11884
11798
  } else if (_isSkillsRuntime) {
11885
11799
  console.log(` ${dim}↳${reset} Agents installed via descriptor-driven layout (${runtime})`);
@@ -11914,7 +11828,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
11914
11828
  // advertising the old full agent surface even though the descriptor-driven
11915
11829
  // write above already skipped writing the .md files for a minimal-tier
11916
11830
  // resolvedProfile. Reuse the same helper that powers `--uninstall`.
11917
- if (isMinimalMode(_effectiveInstallMode) && _hostBehaviors(runtime).tomlConfigInstall) {
11831
+ if (isMinimalMode(_effectiveInstallMode) && hostBehaviorsFor(runtime).tomlConfigInstall) {
11918
11832
  const codexConfigPath = path.join(targetDir, 'config.toml');
11919
11833
  if (fs.existsSync(codexConfigPath)) {
11920
11834
  const existing = fs.readFileSync(codexConfigPath, 'utf8');
@@ -12033,7 +11947,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
12033
11947
  // runtime.hostBehaviors.brandingRewrites. This site only
12034
11948
  // rewrites the two brand-name keys (no `.claude/` here — the
12035
11949
  // config-dir replace above already handled path fragments).
12036
- const _b2 = _hostBehaviors(runtime).brandingRewrites;
11950
+ const _b2 = hostBehaviorsFor(runtime).brandingRewrites;
12037
11951
  if (_b2) {
12038
11952
  content = content.replace(/CLAUDE\.md/g, _b2['CLAUDE.md']);
12039
11953
  content = content.replace(/\bClaude Code\b/g, _b2['Claude Code']);
@@ -12192,7 +12106,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
12192
12106
  // skipSharedHooksInstall:true) — the redundant `&& !isCopilot` was removed.
12193
12107
  // #2100: Windsurf's exclusion is likewise descriptor-driven (windsurf declares
12194
12108
  // skipSharedHooksInstall:true) — the redundant `&& !isWindsurf` was removed.
12195
- if (!isCodex && _hostBehaviors(runtime).skipSharedHooksInstall !== true) {
12109
+ if (!isCodex && hostBehaviorsFor(runtime).skipSharedHooksInstall !== true) {
12196
12110
  if (!installSharedHooksBundle(targetDir)) {
12197
12111
  failures.push('hooks');
12198
12112
  }
@@ -12359,7 +12273,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
12359
12273
  }
12360
12274
 
12361
12275
  // Verify no leaked .claude paths in non-Claude runtimes (manifest-scoped)
12362
- if (!_hostBehaviors(runtime).ownsClaudePaths) {
12276
+ if (!hostBehaviorsFor(runtime).ownsClaudePaths) {
12363
12277
  const leakedPaths = [];
12364
12278
  // Only scan files that were written by this install (manifest-tracked).
12365
12279
  // Scanning the entire targetDir can match user-authored content that
@@ -12392,7 +12306,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
12392
12306
  // verification backstop. The `_GSD_RUNTIME_ROOT`/`$PREFERRED_CONFIG_DIR`
12393
12307
  // fallback chains and prose `.claude` mentions carry no `@~/` prefix and
12394
12308
  // are deliberately untouched, as is CHANGELOG.md.
12395
- if (runtime === 'codex') {
12309
+ if (hostBehaviorsFor(runtime).rewriteClaudeAtIncludes) {
12396
12310
  for (const relPath of manifestFiles) {
12397
12311
  const fileName = path.basename(relPath);
12398
12312
  if (!(fileName.endsWith('.md') || fileName.endsWith('.toml'))) continue;
@@ -13041,7 +12955,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
13041
12955
  // stop, subagentStart, subagentStop) via runtime-hooks-surface.cts, which reads
13042
12956
  // the event list from the descriptor-driven adapter module.
13043
12957
  const cursorHookResult = writeCursorHooksJson(targetDir, src, {
13044
- managedHookEvents: _hostBehaviors(runtime).managedHookEvents,
12958
+ managedHookEvents: hostBehaviorsFor(runtime).managedHookEvents,
13045
12959
  });
13046
12960
  if (cursorHookResult.changed) {
13047
12961
  console.log(` ${green}✓${reset} Configured Cursor lifecycle hooks (sessionStart, postToolUse, preToolUse, stop, subagentStart, subagentStop)`);
@@ -13134,9 +13048,9 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
13134
13048
  // reports success and the user is left with the very breakage the opt-in
13135
13049
  // exists to prevent. Verified reproducible before this guard existed.
13136
13050
  const kimiInstalledThisRun = selectedRuntimes.includes('kimi');
13137
- if (hasReclaimKimiLegacy && runtime === 'kimi-code' && kimiInstalledThisRun) {
13051
+ if (hasReclaimKimiLegacy && hostBehaviorsFor(runtime).reclaimsKimiLegacyHooksRoot && kimiInstalledThisRun) {
13138
13052
  console.log(` ${dim}•${reset} Skipped --reclaim-kimi-legacy: this run also installs --kimi, so ${resolveKimiHooksTomlDir({ runtime: 'kimi' })} is a live Kimi CLI install`);
13139
- } else if (hasReclaimKimiLegacy && runtime === 'kimi-code') {
13053
+ } else if (hasReclaimKimiLegacy && hostBehaviorsFor(runtime).reclaimsKimiLegacyHooksRoot) {
13140
13054
  const legacyKimiRoot = resolveKimiHooksTomlDir({ runtime: 'kimi' });
13141
13055
  // Both roots honor their own env override (KIMI_SHARE_DIR /
13142
13056
  // KIMI_CODE_HOME). A user who points both at ONE directory collapses
@@ -13212,15 +13126,15 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
13212
13126
  // #2870: the CURRENT scope's settings filename is sourced from the Install
13213
13127
  // Scope Module (_installScope.settingsFile, resolveScope's per-scope field)
13214
13128
  // instead of indexing _scopedSettings by hand. _scopedSettings itself is
13215
- // retained unchanged as the #338-privacy fail-safe path: _hostBehaviors
13216
- // already degrades to FALLBACK_HOST_BEHAVIORS (see that constant's comment
13217
- // above) when the registry fails to load, whereas resolveScope's registry
13129
+ // retained unchanged as the #338-privacy fail-safe path: hostBehaviorsFor
13130
+ // already degrades to its #338 floor (see runtime-name-policy.cts)
13131
+ // when the registry fails to load, whereas resolveScope's registry
13218
13132
  // lookup throws in that same scenario (_installScope is null when it did).
13219
13133
  // Falling back to _scopedSettings[_installScopeId] there — and keeping the
13220
13134
  // non-local-claude branch's expression untouched — means this is
13221
13135
  // byte-identical to the pre-migration computation in every case, including
13222
13136
  // the broken-registry fail-safe floor.
13223
- const _scopedSettings = _hostBehaviors(runtime).settingsFileByScope || null;
13137
+ const _scopedSettings = hostBehaviorsFor(runtime).settingsFileByScope || null;
13224
13138
  const _currentScopeSettingsFile = _installScope
13225
13139
  ? _installScope.settingsFile
13226
13140
  : (_scopedSettings ? (_scopedSettings[_installScopeId] ?? null) : null);
@@ -13346,7 +13260,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
13346
13260
  // Descriptor-driven (ADR-1239 / #2096): hookPathStyle comes from the
13347
13261
  // runtime's hostBehaviors instead of a hardcoded `runtime === 'antigravity'`
13348
13262
  // check inside projectLocalHookPrefix.
13349
- const localPrefix = projectLocalHookPrefix({ runtime, dirName, hookPathStyle: _hostBehaviors(runtime).hookPathStyle });
13263
+ const localPrefix = projectLocalHookPrefix({ runtime, dirName, hookPathStyle: hostBehaviorsFor(runtime).hookPathStyle });
13350
13264
  const settingsEntrypoints = [];
13351
13265
  const hookOpts = {
13352
13266
  portableHooks: hasPortableHooks,
@@ -13449,7 +13363,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
13449
13363
  // installAllRuntimes can register it at finalize time when the user opts
13450
13364
  // in (#2795). Computed here (not in finishInstall) so the same buildHookCommand
13451
13365
  // / localCmd resolution logic is shared with the other JS hooks.
13452
- const updateBannerCommand = _hostBehaviors(runtime).skipUpdateBannerCommand
13366
+ const updateBannerCommand = hostBehaviorsFor(runtime).skipUpdateBannerCommand
13453
13367
  ? null
13454
13368
  : (isGlobal
13455
13369
  ? buildHookCommand(targetDir, 'gsd-update-banner.js', hookOpts)
@@ -13612,7 +13526,7 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13612
13526
  // on plan.finishPermissionWriter === 'kilo' (descriptor-driven), not this flag.
13613
13527
  // #2094: isTrae dropped — unused in this function.
13614
13528
  // #2095: isKimi dropped — the Kimi "Done!" banner below reads
13615
- // _hostBehaviors(runtime).doneBannerStyle === 'kimi-agent-file' (descriptor-driven), not this flag.
13529
+ // hostBehaviorsFor(runtime).doneBannerStyle === 'kimi-agent-file' (descriptor-driven), not this flag.
13616
13530
  // #2096: isAntigravity dropped — unused in this function.
13617
13531
  // #2098: isCodebuddy dropped — unused in this function.
13618
13532
  // #2099: isCopilot dropped — unused in this function.
@@ -13633,7 +13547,7 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13633
13547
  // runtime — kept for a caller that invokes finishInstall directly.
13634
13548
  assertConfiguredEntrypoints(bannerOpts.configuredEntrypoints);
13635
13549
 
13636
- if (shouldInstallStatusline && plan.writesSharedSettings && !_hostBehaviors(runtime).skipSettingsUi) {
13550
+ if (shouldInstallStatusline && plan.writesSharedSettings && !hostBehaviorsFor(runtime).skipSettingsUi) {
13637
13551
  if (!isGlobal && !forceStatusline) {
13638
13552
  // Local installs skip statusLine by default: repo settings.json takes precedence over
13639
13553
  // profile-level settings.json in Claude Code, so writing here would silently clobber
@@ -13659,7 +13573,7 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13659
13573
  // settings.json hooks block — opencode/kilo/codex/cursor/windsurf/trae/
13660
13574
  // cline either lack the surface or use a different config schema.
13661
13575
  const { shouldInstallBanner, bannerCommand } = bannerOpts;
13662
- if (shouldInstallBanner && settings && plan.writesSharedSettings && !_hostBehaviors(runtime).skipSettingsUi) {
13576
+ if (shouldInstallBanner && settings && plan.writesSharedSettings && !hostBehaviorsFor(runtime).skipSettingsUi) {
13663
13577
  if (!bannerCommand) {
13664
13578
  console.warn(` ${yellow}⚠${reset} Skipped update banner registration — Node executable path unavailable. See #2979 / #3002.`);
13665
13579
  } else {
@@ -13688,13 +13602,13 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13688
13602
  // Merges GSD-owned entries non-destructively (preserves existing user permissions).
13689
13603
  // Scoped to Claude only: antigravity/qwen/hermes/codebuddy also write
13690
13604
  // settings.json but use different runtimes and do not use these permission strings.
13691
- if (_hostBehaviors(runtime).permissionsSchema === 'claude') {
13605
+ if (hostBehaviorsFor(runtime).permissionsSchema === 'claude') {
13692
13606
  mergeClaudePermissions(settings);
13693
13607
  }
13694
13608
 
13695
13609
  // #2097 UPGRADE 3 (transport:mcp): companion MCP server for runtimes that host
13696
13610
  // MCP in settings.json (Augment). settings.json is golden-excluded, so no golden change.
13697
- if (_hostBehaviors(runtime).mcpCompanion === 'settings-json' && settings && plan.writesSharedSettings) {
13611
+ if (hostBehaviorsFor(runtime).mcpCompanion === 'settings-json' && settings && plan.writesSharedSettings) {
13698
13612
  mergeGsdMcpServerIntoSettings(settings);
13699
13613
  }
13700
13614
 
@@ -13734,18 +13648,29 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13734
13648
  // generation reads it). This call is idempotent (preserves existing values).
13735
13649
  writeNonClaudeDefaults(runtime);
13736
13650
 
13737
- // program + command are now single-source lookups (ADR-1239 Phase B / #1679):
13738
- // program is the runtime display label; command is the per-host /gsd-new-project
13739
- // invocation syntax.
13651
+ // program is the runtime display label (ADR-1239 Phase B / #1679). The command
13652
+ // is generated from the surface this runtime registered in this install scope
13653
+ // (#5215, ADR-5057 §5 Phase 12) — a runtime that registers no new-project
13654
+ // trigger is told so instead of being sent to a command that does not exist (#4567).
13740
13655
  const program = getRuntimeLabel(runtime);
13741
- const command = getRuntimeNewProjectCommand(runtime);
13656
+ const advertised = resolveAdvertisedNewProject(runtime, isGlobal ? 'global' : 'local');
13657
+ const command = advertised.kind === 'command' ? advertised.command : null;
13658
+ // Host-specific launch/restart steps below stay; only the new-project clause
13659
+ // changes when the runtime registered no such command.
13660
+ const noNewProject = advertised.kind === 'unregistered'
13661
+ ? `${program} registers no new-project command in this scope${advertised.nativeCommand ? ` (its native extension registers ${cyan}${advertised.nativeCommand}${reset})` : ''}.`
13662
+ : '';
13742
13663
 
13743
13664
  // Claude Code global installs use the skills/ format (CC 2.1.88+).
13744
13665
  // Restart is required for CC to pick up newly-installed skills, and the
13745
13666
  // slash-menu surface depends on CC version — so the instruction needs to
13746
13667
  // cover both invocation paths to avoid #2957-style "no commands appear".
13747
- if (_hostBehaviors(runtime).skillsGlobalOnboarding && isGlobal) {
13748
- console.log(`
13668
+ if (hostBehaviorsFor(runtime).skillsGlobalOnboarding && isGlobal) {
13669
+ console.log(command === null ? `
13670
+ ${green}Done!${reset} Restart ${program}. ${noNewProject}
13671
+
13672
+ ${cyan}Join the community:${reset} https://discord.gg/mYgfVNfA2r
13673
+ ` : `
13749
13674
  ${green}Done!${reset} Restart ${program}, then in any directory either type ${cyan}${command}${reset} or ask Claude to run the ${cyan}gsd-new-project${reset} skill.
13750
13675
 
13751
13676
  ${cyan}Join the community:${reset} https://discord.gg/mYgfVNfA2r
@@ -13753,9 +13678,13 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13753
13678
  return;
13754
13679
  }
13755
13680
 
13756
- if (_hostBehaviors(runtime).doneBannerStyle === 'kimi-agent-file') {
13681
+ if (hostBehaviorsFor(runtime).doneBannerStyle === 'kimi-agent-file') {
13757
13682
  const agentPath = configDir ? path.join(configDir, 'agents', 'gsd.yaml') : 'agents/gsd.yaml';
13758
- console.log(`
13683
+ console.log(command === null ? `
13684
+ ${green}Done!${reset} Start ${program} with ${cyan}kimi --agent-file ${agentPath}${reset}. ${noNewProject}
13685
+
13686
+ ${cyan}Join the community:${reset} https://discord.gg/mYgfVNfA2r
13687
+ ` : `
13759
13688
  ${green}Done!${reset} Start ${program} with ${cyan}kimi --agent-file ${agentPath}${reset}, then run ${cyan}${command}${reset}.
13760
13689
 
13761
13690
  ${cyan}Join the community:${reset} https://discord.gg/mYgfVNfA2r
@@ -13763,7 +13692,11 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
13763
13692
  return;
13764
13693
  }
13765
13694
 
13766
- console.log(`
13695
+ console.log(command === null ? `
13696
+ ${green}Done!${reset} GSD is installed for ${program}, which registers no new-project command in this scope${advertised.nativeCommand ? ` (its native extension registers ${cyan}${advertised.nativeCommand}${reset})` : ''}.
13697
+
13698
+ ${cyan}Join the community:${reset} https://discord.gg/mYgfVNfA2r
13699
+ ` : `
13767
13700
  ${green}Done!${reset} Open a blank directory in ${program} and run ${cyan}${command}${reset}.
13768
13701
 
13769
13702
  ${cyan}Join the community:${reset} https://discord.gg/mYgfVNfA2r
@@ -14713,9 +14646,6 @@ module.exports = {
14713
14646
  install,
14714
14647
  installAllRuntimes,
14715
14648
  uninstall,
14716
- // #2086 — host-behavior resolution + the #338 privacy fail-safe floor (exported for tests)
14717
- _resolveHostBehaviors,
14718
- FALLBACK_HOST_BEHAVIORS,
14719
14649
  // #3023 — shared hook bundle directory name, descriptor-driven
14720
14650
  SHARED_HOOKS_DIR_DEFAULT,
14721
14651
  resolveSharedHooksDirName,