@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
@@ -33,11 +33,12 @@ const { installFs, mkInstallTempDir } = installFsAdapter;
33
33
  const commandRoster = require("./command-roster.cjs");
34
34
  const { readGsdCommandNames, transformContentToHyphen } = commandRoster;
35
35
  const runtimeNamePolicy = require("./runtime-name-policy.cjs");
36
- const { getDirName } = runtimeNamePolicy;
36
+ const { getDirName, hostBehaviorsFor } = runtimeNamePolicy;
37
37
  const capabilityRegistry = require("./capability-registry.cjs");
38
38
  const hostIntegration = require("./host-integration.cjs");
39
39
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
40
40
  const frontmatterModule = require("./frontmatter.cjs");
41
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
41
42
  const pattern_cjs_1 = require("./pattern.cjs");
42
43
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
43
44
  // #2870: install-scope.cts is a leaf-tier sibling (imports only
@@ -93,24 +94,6 @@ function gsdVersion() {
93
94
  cachedVersion = resolveVersionFrom(__dirname);
94
95
  return cachedVersion;
95
96
  }
96
- /**
97
- * Host-specific install behaviors declared on the runtime descriptor
98
- * (capabilities/<runtime>/capability.json -> runtime.hostBehaviors). Mirrors
99
- * bin/install.js's / install-engine.cts's `_hostBehaviors` (ADR-1239 / #2086
100
- * / #2092). Returns {} for runtimes that declare none, so every behavior
101
- * branch degrades to the generic path by default. Unlike the bin/install.js
102
- * and install-engine.cts variants, this module already imports
103
- * `capabilityRegistry` statically (see NON_CLAUDE_RUNTIMES below), so this
104
- * reads it directly rather than re-require()-ing inside a try/catch.
105
- */
106
- function _hostBehaviors(runtime) {
107
- return ((capabilityRegistry &&
108
- capabilityRegistry.runtimes &&
109
- capabilityRegistry.runtimes[runtime] &&
110
- capabilityRegistry.runtimes[runtime].runtime &&
111
- capabilityRegistry.runtimes[runtime].runtime.hostBehaviors) ||
112
- {});
113
- }
114
97
  /**
115
98
  * Public accessor for the `hostBehaviors.agentFileExtension` descriptor field
116
99
  * (ADR-1239 / #2099 / #2103). Returns the runtime's declared agent-file
@@ -122,7 +105,7 @@ function _hostBehaviors(runtime) {
122
105
  * duplicating a hardcoded `runtime === 'copilot'` check (#2103 fold).
123
106
  */
124
107
  function agentFileExtensionFor(runtime) {
125
- const ext = _hostBehaviors(runtime).agentFileExtension;
108
+ const ext = hostBehaviorsFor(runtime).agentFileExtension;
126
109
  return typeof ext === 'string' ? ext : undefined;
127
110
  }
128
111
  const colorNameToHex = {
@@ -485,7 +468,7 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
485
468
  // Hermes' SKILL.md spec lists `version` as a required frontmatter field.
486
469
  // Track GSD's package version so Hermes' skill_view() reports a stable
487
470
  // identifier per install.
488
- if (runtime === 'hermes') {
471
+ if (hostBehaviorsFor(runtime).skillFrontmatterVersion) {
489
472
  const version = gsdVersion();
490
473
  if (version)
491
474
  fm += `version: ${yamlQuote(version)}\n`;
@@ -495,7 +478,7 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
495
478
  // runtimes that declare the flag so Claude/Hermes skill frontmatter is
496
479
  // unchanged (they ignore the field, but we keep their output byte-stable).
497
480
  // skillName is the `gsd-<stem>` dir name. (ADR-1239 / #2092)
498
- if (_hostBehaviors(runtime).skillPriorityFrontmatter) {
481
+ if (hostBehaviorsFor(runtime).skillPriorityFrontmatter) {
499
482
  const stem = typeof skillName === 'string' && skillName.startsWith('gsd-')
500
483
  ? skillName.slice(4)
501
484
  : skillName;
@@ -730,10 +713,13 @@ function appendAgentTools(content, grants) {
730
713
  return content;
731
714
  const eol = content.includes('\r\n') ? '\r\n' : '\n';
732
715
  const lines = content.split(eol);
733
- if (lines[0] !== '---')
716
+ // The block is the one the one fence owner finds; `frontmatterEnd` is its closing fence's
717
+ // line index in `lines`.
718
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
719
+ if (!fence?.closed)
734
720
  return content;
735
- const frontmatterEnd = lines.indexOf('---', 1);
736
- if (frontmatterEnd === -1)
721
+ const frontmatterEnd = content.slice(0, fence.closingStart).split(eol).length - 1;
722
+ if (frontmatterEnd < 1)
737
723
  return content;
738
724
  const toolsIndex = lines.findIndex((line, index) => index < frontmatterEnd && /^tools:[ \t]*(.*)$/.test(line));
739
725
  if (toolsIndex === -1)
@@ -1080,17 +1066,19 @@ function yamlIdentifier(value) {
1080
1066
  }
1081
1067
  return yamlQuote(text);
1082
1068
  }
1069
+ /**
1070
+ * The frontmatter YAML (trimmed) and everything after the closing fence line's text, for the
1071
+ * block the one fence owner (`locateFrontmatterFence`) finds — so a `---` inside a value (a
1072
+ * description that mentions `a---b`) cannot end the block early.
1073
+ */
1083
1074
  function extractFrontmatterAndBody(content) {
1084
- if (!content.startsWith('---')) {
1085
- return { frontmatter: null, body: content };
1086
- }
1087
- const endIndex = content.indexOf('---', 3);
1088
- if (endIndex === -1) {
1075
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
1076
+ if (!fence?.closed) {
1089
1077
  return { frontmatter: null, body: content };
1090
1078
  }
1091
1079
  return {
1092
- frontmatter: content.substring(3, endIndex).trim(),
1093
- body: content.substring(endIndex + 3),
1080
+ frontmatter: content.slice(fence.openEnd, fence.bodyEnd).trim(),
1081
+ body: content.slice(fence.closingFenceEnd),
1094
1082
  };
1095
1083
  }
1096
1084
  function extractFrontmatterField(frontmatter, fieldName) {
@@ -1539,7 +1527,7 @@ function convertClaudeCommandToTraeSkill(content, skillName) {
1539
1527
  // is not formally documented (thin SPA docs) — descriptor-driven, single
1540
1528
  // fixed GSD-side value (runtime.hostBehaviors.soloStageMetadata), inferred/
1541
1529
  // best-effort.
1542
- const soloStage = _hostBehaviors('trae').soloStageMetadata;
1530
+ const soloStage = hostBehaviorsFor('trae').soloStageMetadata;
1543
1531
  if (soloStage)
1544
1532
  fm += `stage: ${soloStage}\n`;
1545
1533
  fm += '---';
@@ -1955,17 +1943,11 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
1955
1943
  convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, 'subagent_type="general"');
1956
1944
  // Runtime-neutral agent name replacement (#766)
1957
1945
  convertedContent = neutralizeAgentReferences(convertedContent, 'AGENTS.md');
1958
- // Check if content has frontmatter
1959
- if (!convertedContent.startsWith('---')) {
1960
- return convertedContent;
1961
- }
1962
- // Find the end of frontmatter
1963
- const endIndex = convertedContent.indexOf('---', 3);
1964
- if (endIndex === -1) {
1946
+ // The frontmatter block, as the one fence owner finds it (none → nothing to convert).
1947
+ const { frontmatter, body } = extractFrontmatterAndBody(convertedContent);
1948
+ if (frontmatter === null) {
1965
1949
  return convertedContent;
1966
1950
  }
1967
- const frontmatter = convertedContent.substring(3, endIndex).trim();
1968
- const body = convertedContent.substring(endIndex + 3);
1969
1951
  // Parse frontmatter line by line (simple YAML parsing)
1970
1952
  const lines = frontmatter.split('\n');
1971
1953
  const newLines = [];
@@ -2121,17 +2103,11 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
2121
2103
  convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, 'subagent_type="general"');
2122
2104
  // Runtime-neutral agent name replacement (#766)
2123
2105
  convertedContent = neutralizeAgentReferences(convertedContent, 'AGENTS.md');
2124
- // Check if content has frontmatter
2125
- if (!convertedContent.startsWith('---')) {
2126
- return convertedContent;
2127
- }
2128
- // Find the end of frontmatter
2129
- const endIndex = convertedContent.indexOf('---', 3);
2130
- if (endIndex === -1) {
2106
+ // The frontmatter block, as the one fence owner finds it (none → nothing to convert).
2107
+ const { frontmatter, body } = extractFrontmatterAndBody(convertedContent);
2108
+ if (frontmatter === null) {
2131
2109
  return convertedContent;
2132
2110
  }
2133
- const frontmatter = convertedContent.substring(3, endIndex).trim();
2134
- const body = convertedContent.substring(endIndex + 3);
2135
2111
  // Parse frontmatter line by line (simple YAML parsing)
2136
2112
  const lines = frontmatter.split('\n');
2137
2113
  const newLines = [];
@@ -2526,7 +2502,7 @@ function convertClaudeAgentToTraeAgent(content) {
2526
2502
  * forms to avoid double-rewriting the same substring.
2527
2503
  */
2528
2504
  function convertClaudeAgentToQwenAgent(content) {
2529
- const _b = _hostBehaviors('qwen').brandingRewrites || {};
2505
+ const _b = hostBehaviorsFor('qwen').brandingRewrites || {};
2530
2506
  let converted = content;
2531
2507
  if (_b['CLAUDE.md'])
2532
2508
  converted = converted.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
@@ -2591,18 +2567,14 @@ function convertClaudeAgentToZcodeAgent(content) {
2591
2567
  // present. The unchanged scan below still preserves byte-identical content.
2592
2568
  if (!content.includes('mcp__') && !content.includes('\\'))
2593
2569
  return content;
2594
- const lines = content.split('\n');
2595
- if (lines[0] !== '---')
2570
+ // The block is the one the one fence owner finds; unterminated (or absent) → leave verbatim.
2571
+ // The rewrite below re-joins lines on '\n', so a CRLF block is left verbatim too rather than
2572
+ // given mixed line endings.
2573
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
2574
+ if (!fence?.closed || fence.eol !== '\n')
2596
2575
  return content;
2597
- let fmEnd = -1;
2598
- for (let i = 1; i < lines.length; i++) {
2599
- if (lines[i] === '---') {
2600
- fmEnd = i;
2601
- break;
2602
- }
2603
- }
2604
- if (fmEnd === -1)
2605
- return content; // unterminated frontmatter — leave verbatim
2576
+ const lines = content.split('\n');
2577
+ const fmEnd = content.slice(0, fence.closingStart).split('\n').length - 1;
2606
2578
  const out = [];
2607
2579
  let changed = false;
2608
2580
  let i = 1;
@@ -2699,12 +2671,12 @@ function convertClaudeAgentToClineAgent(content) {
2699
2671
  * rewrites applied automatically. A runtime with no `brandingRewrites`
2700
2672
  * declared returns `content` unchanged (no rewrite table to apply).
2701
2673
  *
2702
- * Byte-identical to the inline loop's `else if (_hostBehaviors(runtime).brandingRewrites)`
2674
+ * Byte-identical to the inline loop's `else if (hostBehaviorsFor(runtime).brandingRewrites)`
2703
2675
  * branch, including plain (non-word-boundary) `.replace(/\bClaude Code\b/g, ...)`
2704
2676
  * semantics — J9.
2705
2677
  */
2706
2678
  function applyAgentBrandingRewrites(content, runtime) {
2707
- const _b = _hostBehaviors(runtime).brandingRewrites;
2679
+ const _b = hostBehaviorsFor(runtime).brandingRewrites;
2708
2680
  if (!_b)
2709
2681
  return content;
2710
2682
  let converted = content;
@@ -2890,7 +2862,7 @@ function projectRelativePrefixFromProjectRoot(projectRoot, resolvedTarget) {
2890
2862
  * directory in a project-relative include: that directory was never created.
2891
2863
  */
2892
2864
  function localIncludeDirName(runtime) {
2893
- return _hostBehaviors(runtime).localTargetIsProjectRoot === true ? undefined : getDirName(runtime);
2865
+ return hostBehaviorsFor(runtime).localTargetIsProjectRoot === true ? undefined : getDirName(runtime);
2894
2866
  }
2895
2867
  /**
2896
2868
  * #4377: the project-relative prefix for a local install, or `''` when the
@@ -3194,11 +3166,16 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3194
3166
  // #1521: stamp runtime identity + use_worktrees=false for every non-Claude runtime
3195
3167
  // before brand-specific path rewrites, so the replace operates on the pristine
3196
3168
  // source line and is idempotent regardless of subsequent path substitutions.
3197
- if (runtime !== 'claude') {
3169
+ // #5169: the descriptor declares which hosts skip the stamp
3170
+ // (`hostBehaviors.skipRuntimeDefaultsStamp`) and which rewrite profile applies
3171
+ // (`hostBehaviors.contentRewriteProfile`). The switch below is keyed by PROFILE
3172
+ // name, not runtime id, so two hosts that share a body share one case.
3173
+ const rewriteBehaviors = hostBehaviorsFor(runtime);
3174
+ if (!rewriteBehaviors.skipRuntimeDefaultsStamp) {
3198
3175
  content = _stampNonClaudeRuntimeDefaults(content, runtime);
3199
3176
  }
3200
- switch (runtime) {
3201
- case 'codex':
3177
+ switch (rewriteBehaviors.contentRewriteProfile) {
3178
+ case 'slash-own-tilde':
3202
3179
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3203
3180
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3204
3181
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3206,7 +3183,7 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3206
3183
  // #1515 stamp moved to _stampNonClaudeRuntimeDefaults (#1521 generalisation).
3207
3184
  content = processAttribution(content, attribution);
3208
3185
  break;
3209
- case 'cline':
3186
+ case 'slash-bare-own-tilde':
3210
3187
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3211
3188
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3212
3189
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3218,7 +3195,7 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3218
3195
  content = content.replace(/\$HOME\/\.cline\b/g, normalizedPathPrefix);
3219
3196
  content = processAttribution(content, attribution);
3220
3197
  break;
3221
- case 'cursor':
3198
+ case 'slash-lookahead-own-tilde':
3222
3199
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3223
3200
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3224
3201
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3228,7 +3205,7 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3228
3205
  content = content.replace(/~\/\.cursor\//g, pathPrefix);
3229
3206
  content = processAttribution(content, attribution);
3230
3207
  break;
3231
- case 'windsurf': {
3208
+ case 'slash-lookahead-nested-own-devin': {
3232
3209
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3233
3210
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3234
3211
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3244,7 +3221,7 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3244
3221
  content = processAttribution(content, attribution);
3245
3222
  break;
3246
3223
  }
3247
- case 'augment': {
3224
+ case 'slash-lookahead-own-derived': {
3248
3225
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3249
3226
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3250
3227
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3262,7 +3239,7 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3262
3239
  content = processAttribution(content, attribution);
3263
3240
  break;
3264
3241
  }
3265
- case 'trae':
3242
+ case 'slash-bare-own-derived':
3266
3243
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3267
3244
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3268
3245
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3275,7 +3252,7 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3275
3252
  content = content.replace(new RegExp('~/' + (0, pattern_cjs_1.escapeRegex)(dirName) + '/', 'g'), pathPrefix);
3276
3253
  content = processAttribution(content, attribution);
3277
3254
  break;
3278
- case 'codebuddy':
3255
+ case 'slash-bare-own-tilde-bare':
3279
3256
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3280
3257
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3281
3258
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3288,13 +3265,14 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3288
3265
  content = content.replace(/\$HOME\/\.codebuddy\b/g, normalizedPathPrefix);
3289
3266
  content = processAttribution(content, attribution);
3290
3267
  break;
3291
- case 'copilot':
3268
+ case 'attribution-only':
3292
3269
  content = processAttribution(content, attribution);
3293
3270
  break;
3294
- case 'antigravity':
3295
- content = processAttribution(content, attribution);
3296
- break;
3297
- case 'claude':
3271
+ // claude and zcode share this profile. #4002: ZCode is a Claude-Code-shaped
3272
+ // host (dot-home `.zcode`, `@~`-ref expansion, `~/.zcode/...` documented
3273
+ // paths) whose commands install with `converter: null` — this pass is their
3274
+ // only chance to receive runtime-correct paths, including the tilde restore.
3275
+ case 'slash-tilde-restore':
3298
3276
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3299
3277
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3300
3278
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3308,37 +3286,15 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3308
3286
  // brand VALUES (CLAUDE.md/Claude Code/.claude/ replacements) now read from
3309
3287
  // runtime.hostBehaviors.brandingRewrites instead of hardcoded literals.
3310
3288
  // EXACT regexes/order preserved — only the replacement values changed.
3311
- case 'qwen': {
3289
+ // qwen and hermes share this profile; their own dot-dir is derived from the
3290
+ // descriptor's `localConfigDir` (`getDirName`) rather than spelled per host.
3291
+ case 'branded-lookahead-own-derived': {
3312
3292
  // Guarded (post-review #2092): brandingRewrites is undefined if the
3313
3293
  // capability registry fails to load — degrade closed (skip the
3314
3294
  // brand-literal replacements, still apply the non-branding path
3315
3295
  // rewrites below) instead of throwing on `_b['CLAUDE.md']`.
3316
- const _b = _hostBehaviors(runtime).brandingRewrites;
3317
- if (_b) {
3318
- content = content.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
3319
- // #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
3320
- content = applyClaudeCodeBrandSwap(content, _b['Claude Code']);
3321
- }
3322
- content = content.replace(/~\/\.claude\//g, pathPrefix);
3323
- content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3324
- content = content.replace(/~\/\.qwen\//g, pathPrefix);
3325
- content = content.replace(/\$HOME\/\.qwen\//g, pathPrefix);
3326
- content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
3327
- content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
3328
- content = content.replace(/~\/\.qwen(?![\w-])/g, normalizedPathPrefix);
3329
- content = content.replace(/\$HOME\/\.qwen(?![\w-])/g, normalizedPathPrefix);
3330
- if (_b) {
3331
- content = content.replace(/\.claude\//g, _b['.claude/']);
3332
- }
3333
- content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
3334
- content = content.replace(/\.\/\.qwen\//g, `./${dirName}/`);
3335
- content = processAttribution(content, attribution);
3336
- break;
3337
- }
3338
- case 'hermes': {
3339
- // Guarded (post-review #2092): see qwen case above — same degrade-closed
3340
- // rationale.
3341
- const _b = _hostBehaviors(runtime).brandingRewrites;
3296
+ const _b = rewriteBehaviors.brandingRewrites;
3297
+ const _own = (0, pattern_cjs_1.escapeRegex)(dirName);
3342
3298
  if (_b) {
3343
3299
  content = content.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
3344
3300
  // #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
@@ -3346,21 +3302,21 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3346
3302
  }
3347
3303
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3348
3304
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3349
- content = content.replace(/~\/\.hermes\//g, pathPrefix);
3350
- content = content.replace(/\$HOME\/\.hermes\//g, pathPrefix);
3305
+ content = content.replace(new RegExp('~/' + _own + '/', 'g'), pathPrefix);
3306
+ content = content.replace(new RegExp('\\$HOME/' + _own + '/', 'g'), pathPrefix);
3351
3307
  content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
3352
3308
  content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
3353
- content = content.replace(/~\/\.hermes(?![\w-])/g, normalizedPathPrefix);
3354
- content = content.replace(/\$HOME\/\.hermes(?![\w-])/g, normalizedPathPrefix);
3309
+ content = content.replace(new RegExp('~/' + _own + '(?![\\w-])', 'g'), normalizedPathPrefix);
3310
+ content = content.replace(new RegExp('\\$HOME/' + _own + '(?![\\w-])', 'g'), normalizedPathPrefix);
3355
3311
  if (_b) {
3356
3312
  content = content.replace(/\.claude\//g, _b['.claude/']);
3357
3313
  }
3358
3314
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
3359
- content = content.replace(/\.\/\.hermes\//g, `./${dirName}/`);
3315
+ content = content.replace(new RegExp('\\./' + _own + '/', 'g'), `./${dirName}/`);
3360
3316
  content = processAttribution(content, attribution);
3361
3317
  break;
3362
3318
  }
3363
- case 'kimi':
3319
+ case 'slash-bare':
3364
3320
  content = content.replace(/~\/\.claude\//g, pathPrefix);
3365
3321
  content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3366
3322
  content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
@@ -3369,22 +3325,8 @@ function _applyRuntimeRewritesInner(content, runtime, pathPrefix, isGlobal = fal
3369
3325
  content = content.replace(/\.\/\.claude\b/g, `./${dirName}`);
3370
3326
  content = processAttribution(content, attribution);
3371
3327
  break;
3372
- case 'zcode':
3373
- // #4002: ZCode is a Claude-Code-shaped host (dot-home `.zcode`, `@~`-ref
3374
- // expansion, `~/.zcode/...` documented paths) whose commands install with
3375
- // `converter: null` — this pass is their only chance to receive
3376
- // runtime-correct paths. Same shape as `claude`, including the tilde
3377
- // restore: the tilde form is what ZCode expands and what its docs use.
3378
- // `${_GSD_RUNTIME_ROOT}/.claude/…` matches none of these regexes and
3379
- // survives as the project-local fallback, exactly as on every sibling.
3380
- content = content.replace(/~\/\.claude\//g, pathPrefix);
3381
- content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
3382
- content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
3383
- content = restoreClaudeGlobalAtRefTilde(content, pathPrefix);
3384
- content = processAttribution(content, attribution);
3385
- break;
3386
3328
  default:
3387
- // Unknown runtime — no rewrites (OpenCode/Kilo handled by their own install path).
3329
+ // No profile declared — no rewrites (OpenCode/Kilo handled by their own install path).
3388
3330
  break;
3389
3331
  }
3390
3332
  return content;
@@ -3450,7 +3392,7 @@ function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathP
3450
3392
  content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal, attribution);
3451
3393
  // #2097 (ADR-1239): descriptor-driven — commandBodyConverter name comes
3452
3394
  // from runtime.hostBehaviors instead of a hardcoded runtime-name branch.
3453
- const _cmdConv = _hostBehaviors(runtime).commandBodyConverter;
3395
+ const _cmdConv = hostBehaviorsFor(runtime).commandBodyConverter;
3454
3396
  if (_cmdConv && COMMAND_BODY_CONVERTERS[_cmdConv]) {
3455
3397
  content = COMMAND_BODY_CONVERTERS[_cmdConv](content);
3456
3398
  }
@@ -3539,7 +3481,7 @@ function rewriteStagedSkillBodies(stagedDir, opts) {
3539
3481
  // no skills-kind entry at local scope, so this is already structurally
3540
3482
  // scoped to global (row 23) — the explicit isGlobal check is defense-in-depth
3541
3483
  // against that descriptor wiring ever changing.
3542
- if (runtime === 'claude' && isGlobal) {
3484
+ if (hostBehaviorsFor(runtime).specRootSkillPass && isGlobal) {
3543
3485
  applySpecRootReferenceToStagedSkills(stagedDir);
3544
3486
  }
3545
3487
  }
@@ -3591,7 +3533,7 @@ function rewriteStagedCommandBodies(stagedDir, opts) {
3591
3533
  * @param cmdNames gsd command names from readGsdCommandNames()
3592
3534
  */
3593
3535
  function normalizeAgentBodyForRuntime(content, runtime, cmdNames) {
3594
- if (_hostBehaviors(runtime).hyphenNameAgentBody !== true)
3536
+ if (hostBehaviorsFor(runtime).hyphenNameAgentBody !== true)
3595
3537
  return content;
3596
3538
  return transformContentToHyphen(content, cmdNames);
3597
3539
  }
@@ -3621,7 +3563,7 @@ function normalizeAgentBodyForRuntime(content, runtime, cmdNames) {
3621
3563
  * @returns content with path-prefix rewrites applied (or unchanged for noPathRewrite runtimes, e.g. copilot)
3622
3564
  */
3623
3565
  function applyAgentPathRewrites(content, runtime, pathPrefix) {
3624
- if (_hostBehaviors(runtime).noPathRewrite === true)
3566
+ if (hostBehaviorsFor(runtime).noPathRewrite === true)
3625
3567
  return content;
3626
3568
  // #4377: the agents pipeline is the third emit path that substitutes this
3627
3569
  // prefix (skills/commands via _applyRuntimeRewrites, the gsd-core spec tree
@@ -3663,7 +3605,7 @@ function applyAgentPathRewritesInner(content, runtime, pathPrefix) {
3663
3605
  // anchored to the exact prefix string it is handed — so `restore(pathPrefix)`
3664
3606
  // fixes `@$HOME/.claude/x` and leaves a bare `@$HOME/.claude` broken. The
3665
3607
  // normalized form is a PREFIX of both, so one call covers both.
3666
- if (runtime === 'claude') {
3608
+ if (hostBehaviorsFor(runtime).restoreAtRefTildeInAgents) {
3667
3609
  content = restoreClaudeGlobalAtRefTilde(content, normalizedPathPrefix);
3668
3610
  }
3669
3611
  return content;
@@ -3687,8 +3629,8 @@ function deriveAgentName(fileName) {
3687
3629
  * `applyAgentFrontmatterExtensions` below for the orchestration that calls it.
3688
3630
  *
3689
3631
  * The function:
3690
- * - Detects the file's EOL (CRLF if the first `---` line ends with \r\n,
3691
- * otherwise LF).
3632
+ * - Reads the block the one fence owner (`locateFrontmatterFence`) finds, and
3633
+ * its EOL (CRLF if the opening `---` line ends with \r\n, otherwise LF).
3692
3634
  * - Skips injection if an `effort:` key already exists in the frontmatter
3693
3635
  * (idempotent).
3694
3636
  * - Inserts `effort: <value>` immediately before the closing `---` delimiter,
@@ -3697,19 +3639,15 @@ function deriveAgentName(fileName) {
3697
3639
  * - Returns the original content unchanged when no YAML frontmatter is found.
3698
3640
  */
3699
3641
  function injectEffortFrontmatter(content, effortValue) {
3700
- const eol = /^---\r\n/.test(content) ? '\r\n' : '\n';
3701
- const fmRe = /^---\r?\n([\s\S]*?)^---\r?$/m;
3702
- const match = fmRe.exec(content);
3703
- if (!match)
3642
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
3643
+ if (!fence?.closed)
3704
3644
  return content; // no YAML frontmatter — leave unchanged
3705
- const fmBody = match[1]; // content between the two `---` lines
3645
+ const fmBody = content.slice(fence.openEnd, fence.closingStart); // content between the two fences
3706
3646
  if (/^effort:/m.test(fmBody))
3707
3647
  return content;
3708
- const openLen = 3 + eol.length; // "---" + eol
3709
- const closingStart = match.index + openLen + fmBody.length;
3710
- const before = content.slice(0, closingStart);
3711
- const after = content.slice(closingStart);
3712
- return `${before}effort: ${effortValue}${eol}${after}`;
3648
+ const before = content.slice(0, fence.closingStart);
3649
+ const after = content.slice(fence.closingStart);
3650
+ return `${before}effort: ${effortValue}${fence.eol}${after}`;
3713
3651
  }
3714
3652
  /**
3715
3653
  * #767 — Inject `disallowedTools: <value>` into the YAML frontmatter of a
@@ -3719,19 +3657,15 @@ function injectEffortFrontmatter(content, effortValue) {
3719
3657
  * frontmatter keys. Relocated verbatim from bin/install.js (#2875 Part 2).
3720
3658
  */
3721
3659
  function injectDisallowedToolsFrontmatter(content, disallowedValue) {
3722
- const eol = /^---\r\n/.test(content) ? '\r\n' : '\n';
3723
- const fmRe = /^---\r?\n([\s\S]*?)^---\r?$/m;
3724
- const match = fmRe.exec(content);
3725
- if (!match)
3660
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
3661
+ if (!fence?.closed)
3726
3662
  return content; // no YAML frontmatter — leave unchanged
3727
- const fmBody = match[1]; // content between the two `---` lines
3663
+ const fmBody = content.slice(fence.openEnd, fence.closingStart); // content between the two fences
3728
3664
  if (/^disallowedTools:/m.test(fmBody))
3729
3665
  return content;
3730
- const openLen = 3 + eol.length; // "---" + eol
3731
- const closingStart = match.index + openLen + fmBody.length;
3732
- const before = content.slice(0, closingStart);
3733
- const after = content.slice(closingStart);
3734
- return `${before}disallowedTools: ${disallowedValue}${eol}${after}`;
3666
+ const before = content.slice(0, fence.closingStart);
3667
+ const after = content.slice(fence.closingStart);
3668
+ return `${before}disallowedTools: ${disallowedValue}${fence.eol}${after}`;
3735
3669
  }
3736
3670
  // #767 — Read-only verifier/auditor agents get a Claude-Code disallowedTools deny-list.
3737
3671
  // Group A (pure read-only) deny Write,Edit,MultiEdit. Group B report-writers Write one
@@ -3757,7 +3691,7 @@ const READONLY_AGENT_DISALLOWED_TOOLS = {
3757
3691
  * OpenCode/Qwen/Hermes reject unknown frontmatter keys.
3758
3692
  *
3759
3693
  * Byte-identical to the inline agent loop's
3760
- * `if ((_hostBehaviors(runtime).agentFrontmatterExtensions || []).includes('effort'))`
3694
+ * `if ((hostBehaviorsFor(runtime).agentFrontmatterExtensions || []).includes('effort'))`
3761
3695
  * block (bin/install.js): both the effort injection AND the disallowedTools
3762
3696
  * injection are gated behind the SAME `'effort'` extension flag — there is no
3763
3697
  * separate `'disallowedTools'` extension key, mirroring the loop exactly.
@@ -3772,7 +3706,7 @@ const READONLY_AGENT_DISALLOWED_TOOLS = {
3772
3706
  * @param targetDir install root — resolves .planning/config.json + ~/.gsd/defaults.json
3773
3707
  */
3774
3708
  function applyAgentFrontmatterExtensions(content, { runtime, agentName, targetDir }) {
3775
- const extensions = _hostBehaviors(runtime).agentFrontmatterExtensions || [];
3709
+ const extensions = hostBehaviorsFor(runtime).agentFrontmatterExtensions || [];
3776
3710
  if (!extensions.includes('effort'))
3777
3711
  return content;
3778
3712
  let result = content;
@@ -10,6 +10,7 @@
10
10
  const _require = require;
11
11
  const path = _require('node:path');
12
12
  const { tryWithinRootLexical } = _require('./security.cjs');
13
+ const { hostBehaviorsFor } = _require('./runtime-name-policy.cjs');
13
14
  // #2870: InstallScope is owned by install-scope.cts, not re-declared here.
14
15
  // `isGlobalScope` centralizes the `scope === 'global'` boolean projection
15
16
  // this module needs at `_computePathPrefix`'s `isGlobal: boolean` boundary
@@ -90,7 +91,7 @@ function createRuntimeArtifactInstallPlan(args) {
90
91
  // `undefined` here) — `isGlobalScope` projects it to the boolean
91
92
  // `_computePathPrefix`'s existing `isGlobal: boolean` API requires.
92
93
  const isGlobal = (0, install_scope_cjs_1.isGlobalScope)(scope);
93
- const isOpencode = layout.runtime === 'opencode';
94
+ const isOpencode = hostBehaviorsFor(layout.runtime).opencodePathPrefix === true;
94
95
  const isWindowsHost = (platform ?? process.platform) === 'win32';
95
96
  // #4377: descriptor-derived local dir name, so an opted-in local install
96
97
  // emits a project-relative prefix instead of this checkout's absolute path.
@@ -957,4 +957,60 @@ function resolveTriggerSurface(runtime, scopes, opts) {
957
957
  }
958
958
  return surfaces;
959
959
  }
960
- module.exports = { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, findInstallSourceRoot, resolveTriggerSurface, isNamespacedByDir, composeCommandFilename };
960
+ // ---------------------------------------------------------------------------
961
+ // The advertised "next step" command (#5215, ADR-5057 §5 Phase 12).
962
+ //
963
+ // What the installer tells a user to run is a projection of what the runtime
964
+ // registered: the `new-project` trigger the Trigger Surface resolves for the
965
+ // install scope. It is never a per-runtime literal, so a runtime that registers
966
+ // no such trigger (pi's native extension registers only `/gsd`; windsurf's
967
+ // global layout registers nothing) cannot be told to run one (#4567).
968
+ // ---------------------------------------------------------------------------
969
+ /** The cross-agent default for an id GSD cannot know (ADR-5057 §5 Phase 10 amendment). */
970
+ const DEFAULT_NEW_PROJECT_COMMAND = '/gsd-new-project';
971
+ /**
972
+ * How a host invokes a registered trigger. Presentation only — whether the
973
+ * trigger exists is decided by the registered surface, not by this table. A
974
+ * runtime absent here is invoked as `/<trigger>`.
975
+ */
976
+ const TRIGGER_INVOCATION = {
977
+ codex: (trigger) => `$${trigger}`,
978
+ cursor: (trigger) => `${trigger} (mention the skill name)`,
979
+ kimi: (trigger) => `/skill:${trigger}`,
980
+ };
981
+ /**
982
+ * The command a native-extension runtime registers instead of per-workflow
983
+ * triggers: the extension file's stem as a slash command (pi's `gsd.js`
984
+ * registers `/gsd`; pinned by tests/advertised-command-parity.test.cjs against
985
+ * the extension itself). `null` for a runtime with no native plugin.
986
+ */
987
+ function nativePluginCommand(runtime) {
988
+ const descriptor = getTriggerRegistry().runtimes[runtime]?.runtime;
989
+ const file = descriptor?.hostBehaviors?.nativePlugin?.file;
990
+ if (typeof file !== 'string' || file.length === 0)
991
+ return null;
992
+ const stem = file.replace(/\.[^.]+$/, '');
993
+ return stem.length > 0 ? `/${stem}` : null;
994
+ }
995
+ /**
996
+ * The command the installer advertises for starting a project, generated from
997
+ * the registered trigger surface of `runtime` in `scope`. `unregistered` means
998
+ * the runtime registers no `new-project` trigger there (`nativeCommand` names
999
+ * what a native-extension runtime registers instead). An empty or unregistered
1000
+ * id keeps the documented cross-agent default: it names a runtime GSD cannot
1001
+ * know, so no surface exists to project from. Membership is decided up front,
1002
+ * so an error inside the surface resolution is a bug and propagates.
1003
+ */
1004
+ function resolveAdvertisedNewProject(runtime, scope) {
1005
+ const known = Boolean(runtime) && Object.prototype.hasOwnProperty.call(getTriggerRegistry().runtimes, runtime);
1006
+ if (!known)
1007
+ return { kind: 'command', command: DEFAULT_NEW_PROJECT_COMMAND };
1008
+ const registered = resolveTriggerSurface(runtime, [scope], { stems: ['new-project'] })[0];
1009
+ if (!registered)
1010
+ return { kind: 'unregistered', nativeCommand: nativePluginCommand(runtime) };
1011
+ const render = Object.prototype.hasOwnProperty.call(TRIGGER_INVOCATION, runtime)
1012
+ ? TRIGGER_INVOCATION[runtime]
1013
+ : (trigger) => `/${trigger}`;
1014
+ return { kind: 'command', command: render(registered.trigger) };
1015
+ }
1016
+ module.exports = { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, findInstallSourceRoot, resolveTriggerSurface, resolveAdvertisedNewProject, isNamespacedByDir, composeCommandFilename };