@opengsd/gsd-core 1.9.1 → 1.11.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 (426) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/.opencode/plugins/gsd-core.js +8 -1
  4. package/agents/gsd-code-fixer.md +27 -3
  5. package/agents/gsd-debug-session-manager.md +11 -0
  6. package/agents/gsd-debugger.md +12 -246
  7. package/agents/gsd-doc-synthesizer.md +2 -4
  8. package/agents/gsd-executor.md +12 -10
  9. package/agents/gsd-integration-checker.md +3 -0
  10. package/agents/gsd-mempalace-curator.md +5 -2
  11. package/agents/gsd-phase-researcher.md +20 -1
  12. package/agents/gsd-plan-checker.md +46 -0
  13. package/agents/gsd-planner.md +49 -54
  14. package/agents/gsd-roadmapper.md +21 -3
  15. package/agents/gsd-user-profiler.md +3 -0
  16. package/agents/gsd-verifier.md +26 -73
  17. package/bin/install.js +1272 -1238
  18. package/bin/lib/ui-safety-gate.cjs +2 -0
  19. package/commands/gsd/code-review.md +1 -1
  20. package/commands/gsd/execute-phase.md +1 -1
  21. package/commands/gsd/map-codebase.md +1 -1
  22. package/commands/gsd/mempalace-capture.md +2 -2
  23. package/commands/gsd/mempalace-recall.md +1 -1
  24. package/commands/gsd/new-milestone.md +2 -2
  25. package/commands/gsd/plan-phase.md +1 -1
  26. package/commands/gsd/quick.md +1 -1
  27. package/commands/gsd/review-backlog.md +2 -1
  28. package/commands/gsd/verify-work.md +1 -1
  29. package/gsd-core/bin/gsd-tools.cjs +1009 -115
  30. package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
  31. package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
  32. package/gsd-core/bin/lib/api-coverage.cjs +123 -5
  33. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  34. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  35. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  36. package/gsd-core/bin/lib/audit.cjs +926 -202
  37. package/gsd-core/bin/lib/broken-windows.cjs +36 -6
  38. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  39. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  40. package/gsd-core/bin/lib/capability-registry.cjs +608 -148
  41. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  42. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  43. package/gsd-core/bin/lib/capability-validator.cjs +507 -24
  44. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  45. package/gsd-core/bin/lib/check-command-router.cjs +114 -38
  46. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  47. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  48. package/gsd-core/bin/lib/command-aliases.cjs +94 -0
  49. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  50. package/gsd-core/bin/lib/commands.cjs +665 -99
  51. package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
  52. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  53. package/gsd-core/bin/lib/config-loader.cjs +76 -0
  54. package/gsd-core/bin/lib/config.cjs +22 -2
  55. package/gsd-core/bin/lib/context-composer.cjs +278 -0
  56. package/gsd-core/bin/lib/context-predicates.cjs +506 -0
  57. package/gsd-core/bin/lib/core-utils.cjs +217 -40
  58. package/gsd-core/bin/lib/decisions.cjs +23 -0
  59. package/gsd-core/bin/lib/docs.cjs +3 -2
  60. package/gsd-core/bin/lib/external-job.cjs +19 -4
  61. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  62. package/gsd-core/bin/lib/frontmatter.cjs +239 -32
  63. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  64. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
  65. package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
  66. package/gsd-core/bin/lib/graphify.cjs +142 -27
  67. package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
  68. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  69. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  71. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  72. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  73. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  74. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  75. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  76. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  77. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  78. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  79. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  80. package/gsd-core/bin/lib/host-integration.cjs +13 -1
  81. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  82. package/gsd-core/bin/lib/init-command-router.cjs +83 -8
  83. package/gsd-core/bin/lib/init.cjs +1325 -169
  84. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  85. package/gsd-core/bin/lib/install-engine.cjs +805 -264
  86. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  87. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  88. package/gsd-core/bin/lib/install-profiles.cjs +160 -57
  89. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  90. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  91. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  92. package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
  93. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  94. package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
  95. package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
  96. package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
  97. package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
  98. package/gsd-core/bin/lib/io.cjs +38 -3
  99. package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
  100. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  101. package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
  102. package/gsd-core/bin/lib/mcp-server.cjs +135 -3
  103. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  104. package/gsd-core/bin/lib/milestone.cjs +821 -109
  105. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  106. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  107. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  108. package/gsd-core/bin/lib/pattern.cjs +122 -0
  109. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  110. package/gsd-core/bin/lib/phase-id.cjs +507 -36
  111. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  112. package/gsd-core/bin/lib/phase-locator.cjs +258 -58
  113. package/gsd-core/bin/lib/phase.cjs +891 -156
  114. package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
  115. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  116. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  117. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  118. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  119. package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
  120. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  121. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  122. package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
  123. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  124. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
  125. package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
  126. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  127. package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
  128. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  129. package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
  130. package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
  131. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  132. package/gsd-core/bin/lib/roadmap.cjs +405 -84
  133. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
  134. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  135. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
  136. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  137. package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
  138. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
  139. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  140. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  141. package/gsd-core/bin/lib/section-manifest.cjs +209 -0
  142. package/gsd-core/bin/lib/security.cjs +104 -5
  143. package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
  144. package/gsd-core/bin/lib/smart-entry.cjs +154 -22
  145. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  146. package/gsd-core/bin/lib/state-document.cjs +152 -8
  147. package/gsd-core/bin/lib/state-transition.cjs +424 -105
  148. package/gsd-core/bin/lib/state.cjs +1927 -401
  149. package/gsd-core/bin/lib/surface.cjs +35 -10
  150. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  151. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  152. package/gsd-core/bin/lib/uat-predicate.cjs +20 -4
  153. package/gsd-core/bin/lib/uat.cjs +706 -64
  154. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  155. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  156. package/gsd-core/bin/lib/unusable-input.cjs +33 -0
  157. package/gsd-core/bin/lib/update-context.cjs +8 -2
  158. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  159. package/gsd-core/bin/lib/validate.cjs +20 -6
  160. package/gsd-core/bin/lib/vendor/README.md +37 -0
  161. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  162. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  163. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  164. package/gsd-core/bin/lib/verification.cjs +287 -20
  165. package/gsd-core/bin/lib/verify.cjs +368 -880
  166. package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
  167. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
  169. package/gsd-core/bin/lib/workstream.cjs +8 -2
  170. package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
  171. package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
  172. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  173. package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
  174. package/gsd-core/references/agent-contracts.md +43 -26
  175. package/gsd-core/references/artifact-types.md +10 -3
  176. package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
  177. package/gsd-core/references/checkpoints.md +2 -2
  178. package/gsd-core/references/context-budget.md +1 -1
  179. package/gsd-core/references/debugger-techniques.md +255 -0
  180. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  181. package/gsd-core/references/doc-conflict-engine.md +1 -1
  182. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  184. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  185. package/gsd-core/references/execute-phase-response-language.md +1 -1
  186. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  187. package/gsd-core/references/gate-prompts.md +1 -1
  188. package/gsd-core/references/git-planning-commit.md +2 -1
  189. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  190. package/gsd-core/references/model-profiles.md +12 -4
  191. package/gsd-core/references/mvp-concepts.md +9 -9
  192. package/gsd-core/references/planner-guidance.md +3 -9
  193. package/gsd-core/references/planner-preconditions.md +1 -1
  194. package/gsd-core/references/planner-reviews.md +1 -1
  195. package/gsd-core/references/planning-config.md +8 -6
  196. package/gsd-core/references/research-documentation-lookup.md +5 -3
  197. package/gsd-core/references/revision-loop.md +1 -1
  198. package/gsd-core/references/specless-probe-fallback.md +8 -7
  199. package/gsd-core/references/universal-anti-patterns.md +3 -3
  200. package/gsd-core/references/verifier-phase-gates.md +192 -0
  201. package/gsd-core/references/verifier-wiring-patterns.md +100 -0
  202. package/gsd-core/references/verify-mvp-mode.md +1 -1
  203. package/gsd-core/references/workstream-flag.md +22 -6
  204. package/gsd-core/references/worktree-branch-check.md +2 -2
  205. package/gsd-core/templates/discussion-log.md +1 -1
  206. package/gsd-core/templates/phase-prompt.md +2 -4
  207. package/gsd-core/templates/state.md +4 -4
  208. package/gsd-core/templates/summary-complex.md +2 -0
  209. package/gsd-core/templates/summary-minimal.md +2 -0
  210. package/gsd-core/templates/summary-standard.md +2 -0
  211. package/gsd-core/templates/summary.md +2 -0
  212. package/gsd-core/templates/verification-report.md +9 -1
  213. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  214. package/gsd-core/workflows/audit-milestone.md +3 -0
  215. package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
  216. package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
  217. package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
  218. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
  219. package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
  220. package/gsd-core/workflows/autonomous.md +33 -70
  221. package/gsd-core/workflows/cleanup.md +62 -3
  222. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
  223. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
  224. package/gsd-core/workflows/code-review-fix.md +37 -10
  225. package/gsd-core/workflows/code-review.md +74 -166
  226. package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
  227. package/gsd-core/workflows/complete-milestone.md +160 -95
  228. package/gsd-core/workflows/debug.md +16 -17
  229. package/gsd-core/workflows/diagnose-issues.md +56 -8
  230. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  231. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  232. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
  233. package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
  234. package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
  235. package/gsd-core/workflows/docs-update.md +8 -51
  236. package/gsd-core/workflows/edit-phase.md +26 -1
  237. package/gsd-core/workflows/eval-review.md +3 -5
  238. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
  239. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
  240. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
  241. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  242. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +21 -0
  243. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
  245. package/gsd-core/workflows/execute-phase.md +103 -187
  246. package/gsd-core/workflows/execute-plan.md +36 -4
  247. package/gsd-core/workflows/explore.md +131 -4
  248. package/gsd-core/workflows/fast.md +10 -2
  249. package/gsd-core/workflows/health.md +73 -4
  250. package/gsd-core/workflows/help/modes/full.md +6 -1
  251. package/gsd-core/workflows/import.md +4 -4
  252. package/gsd-core/workflows/ingest-docs.md +7 -6
  253. package/gsd-core/workflows/mvp-phase.md +6 -3
  254. package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
  255. package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
  256. package/gsd-core/workflows/new-milestone.md +35 -47
  257. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
  258. package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
  259. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
  260. package/gsd-core/workflows/new-project.md +27 -240
  261. package/gsd-core/workflows/next.md +12 -0
  262. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
  263. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
  264. package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
  265. package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
  266. package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
  267. package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
  268. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
  269. package/gsd-core/workflows/plan-phase.md +89 -209
  270. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  271. package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
  272. package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
  273. package/gsd-core/workflows/progress.md +45 -159
  274. package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
  275. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
  276. package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
  277. package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
  278. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
  279. package/gsd-core/workflows/quick.md +55 -405
  280. package/gsd-core/workflows/resume-project.md +3 -0
  281. package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
  282. package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
  283. package/gsd-core/workflows/review.md +41 -13
  284. package/gsd-core/workflows/section-manifest.json +219 -0
  285. package/gsd-core/workflows/secure-phase.md +1 -1
  286. package/gsd-core/workflows/session-report.md +2 -1
  287. package/gsd-core/workflows/settings.md +66 -2
  288. package/gsd-core/workflows/ship.md +104 -44
  289. package/gsd-core/workflows/sketch.md +1 -1
  290. package/gsd-core/workflows/spec-phase.md +41 -20
  291. package/gsd-core/workflows/spike-wrap-up.md +20 -5
  292. package/gsd-core/workflows/spike.md +50 -16
  293. package/gsd-core/workflows/sync-skills.md +106 -13
  294. package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
  295. package/gsd-core/workflows/transition.md +53 -31
  296. package/gsd-core/workflows/ui-phase.md +13 -12
  297. package/gsd-core/workflows/ui-review.md +2 -2
  298. package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
  299. package/gsd-core/workflows/update.md +19 -8
  300. package/gsd-core/workflows/validate-phase.md +1 -1
  301. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
  302. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
  303. package/gsd-core/workflows/verify-work.md +17 -65
  304. package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
  305. package/hooks/dist/gsd-check-update-worker.js +64 -12
  306. package/hooks/dist/gsd-check-update.js +19 -1
  307. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  308. package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
  309. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  310. package/hooks/dist/gsd-prompt-guard.js +21 -20
  311. package/hooks/dist/gsd-read-injection-scanner.js +45 -24
  312. package/hooks/dist/gsd-statusline.js +90 -6
  313. package/hooks/dist/gsd-update-banner.js +22 -1
  314. package/hooks/dist/gsd-workflow-guard.js +134 -36
  315. package/hooks/dist/gsd-worktree-path-guard.js +2 -1
  316. package/hooks/dist/gsd-write-guard.js +359 -0
  317. package/hooks/dist/lib/git-cmd.js +92 -59
  318. package/hooks/dist/lib/injection-patterns.js +45 -0
  319. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  320. package/hooks/dist/lib/isolation-sentinel.js +277 -0
  321. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  322. package/hooks/gsd-agent-isolation-guard.js +517 -0
  323. package/hooks/gsd-check-update-worker.js +64 -12
  324. package/hooks/gsd-check-update.js +19 -1
  325. package/hooks/gsd-cursor-pre-tool.js +0 -3
  326. package/hooks/gsd-cursor-subagent-start.js +607 -26
  327. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  328. package/hooks/gsd-prompt-guard.js +21 -20
  329. package/hooks/gsd-read-injection-scanner.js +45 -24
  330. package/hooks/gsd-statusline.js +90 -6
  331. package/hooks/gsd-update-banner.js +22 -1
  332. package/hooks/gsd-workflow-guard.js +134 -36
  333. package/hooks/gsd-worktree-path-guard.js +2 -1
  334. package/hooks/gsd-write-guard.js +359 -0
  335. package/hooks/hooks.json +12 -0
  336. package/hooks/lib/git-cmd.js +92 -59
  337. package/hooks/lib/injection-patterns.js +45 -0
  338. package/hooks/lib/isolation-deny-reason.js +39 -0
  339. package/hooks/lib/isolation-sentinel.js +277 -0
  340. package/hooks/managed-hooks-registry.cjs +2 -0
  341. package/package.json +31 -10
  342. package/pi/gsd.cjs +71 -12
  343. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  344. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  345. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  346. package/scripts/build-hooks.js +9 -0
  347. package/scripts/changeset/lint.cjs +68 -6
  348. package/scripts/changeset/serialize.cjs +5 -1
  349. package/scripts/check-alias-drift.cjs +7 -43
  350. package/scripts/check-contract-drift.cjs +297 -0
  351. package/scripts/ci-test-scope.cjs +19 -2
  352. package/scripts/command-contract-helpers.cjs +903 -1
  353. package/scripts/gen-adr-index.cjs +728 -38
  354. package/scripts/gen-capability-matrix.cjs +1 -1
  355. package/scripts/gen-capability-registry.cjs +3 -15
  356. package/scripts/gen-context-index.cjs +439 -0
  357. package/scripts/gen-health-docs.cjs +390 -0
  358. package/scripts/gen-inventory-manifest.cjs +150 -4
  359. package/scripts/gen-loop-host-contract.cjs +4 -24
  360. package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
  361. package/scripts/gen-registry.cjs +3 -14
  362. package/scripts/gen-section-manifest.cjs +638 -0
  363. package/scripts/generate-package-identity.cjs +4 -2
  364. package/scripts/lib/alias-drift-families.cjs +46 -0
  365. package/scripts/lib/drift-scan.cjs +278 -0
  366. package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
  367. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  368. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  369. package/scripts/lint-canary-version-leak.cjs +73 -0
  370. package/scripts/lint-command-contract.cjs +96 -13
  371. package/scripts/lint-compiled-artifact-sync.cjs +6 -1
  372. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  373. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  374. package/scripts/lint-default-flip-documentation.cjs +193 -0
  375. package/scripts/lint-docs-command-form.cjs +195 -0
  376. package/scripts/lint-docs-required.cjs +9 -1
  377. package/scripts/lint-emitted-drift-ack.cjs +215 -20
  378. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  379. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  380. package/scripts/lint-example-parser-parity.cjs +395 -0
  381. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  382. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  383. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  384. package/scripts/lint-milestone-window-drift.cjs +468 -0
  385. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  386. package/scripts/lint-plan-count-drift.cjs +318 -0
  387. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  388. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  389. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  390. package/scripts/lint-regression-test-names.cjs +15 -13
  391. package/scripts/lint-removed-but-needed.cjs +320 -0
  392. package/scripts/lint-state-field-drift.cjs +805 -0
  393. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  394. package/scripts/lint-test-file-count.allowlist.json +40 -3
  395. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  396. package/scripts/lint-vendored-deps.cjs +124 -0
  397. package/scripts/mutation-matrix.cjs +13 -0
  398. package/scripts/pr-changed-files.cjs +63 -0
  399. package/scripts/pr-template-policy.cjs +14 -4
  400. package/scripts/prompt-injection-scan.sh +52 -6
  401. package/scripts/require-issue-link-policy.cjs +192 -0
  402. package/scripts/state-write-path-drift-baseline.json +19 -0
  403. package/scripts/sync-runtime-launcher.cjs +2 -4
  404. package/skills/gsd-autonomous/SKILL.md +0 -1
  405. package/skills/gsd-code-review/SKILL.md +1 -1
  406. package/skills/gsd-execute-phase/SKILL.md +1 -2
  407. package/skills/gsd-map-codebase/SKILL.md +1 -1
  408. package/skills/gsd-mempalace-capture/SKILL.md +2 -2
  409. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  410. package/skills/gsd-new-milestone/SKILL.md +2 -2
  411. package/skills/gsd-next/SKILL.md +0 -1
  412. package/skills/gsd-plan-phase/SKILL.md +1 -2
  413. package/skills/gsd-progress/SKILL.md +0 -1
  414. package/skills/gsd-quick/SKILL.md +1 -1
  415. package/skills/gsd-review-backlog/SKILL.md +2 -1
  416. package/skills/gsd-stats/SKILL.md +0 -1
  417. package/skills/gsd-verify-work/SKILL.md +1 -1
  418. package/vscode/package.json +1 -1
  419. package/gsd-core/workflows/discovery-phase.md +0 -298
  420. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  421. package/gsd-core/workflows/verify-phase.md +0 -577
  422. package/scripts/affected-tests-lib.cjs +0 -554
  423. package/scripts/gen-emitted-baseline.cjs +0 -145
  424. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  425. package/scripts/run-affected-tests.cjs +0 -7
  426. package/scripts/run-tests.cjs +0 -1050
@@ -0,0 +1,381 @@
1
+ "use strict";
2
+ /**
3
+ * installed-surface-resolver.cts — Installed Surface Resolver Module (#2872,
4
+ * ADR-2866, Phase 3 — governed by
5
+ * `.gsd/phase/feat-2872-manifest-scope-runtime/40-design.md`).
6
+ *
7
+ * `resolveInstalledSurfaces(runtime?, opts?)` answers a question no existing
8
+ * module answers: "which surfaces actually exist on THIS machine, across
9
+ * BOTH install scopes, right now?" `capability-state.cts` answers "which
10
+ * capabilities are enabled in THIS config dir" — a different question at a
11
+ * different altitude. This module is a new leaf: it reads two scopes at
12
+ * once, composing three already-shipped, already-tested pieces
13
+ * (`resolveScope`, Phase 1; `resolveTriggerSurface`, Phase 2; and
14
+ * `readInstallManifest`, widened in this same phase) rather than
15
+ * re-implementing any of their rules.
16
+ *
17
+ * ── Probe-not-declared keying ───────────────────────────────────────────────
18
+ * A scope RECORD is keyed by the PROBED scope (`resolveScope`'s own `id`),
19
+ * never by what the manifest itself claims. `manifestVersion`/`runtime`/
20
+ * `scope` recorded inside a v2 manifest are corroboration, not identity: a
21
+ * manifest copied between config dirs, or a `--config-dir` install, must
22
+ * still be reported at the scope this machine actually resolves to.
23
+ *
24
+ * ── Report, don't correct ───────────────────────────────────────────────────
25
+ * When a declared `runtime`/`scope` disagrees with the probe, this module
26
+ * reports the mismatch (`declaredScopeMatchesProbe: false` /
27
+ * `declaredRuntimeMatchesProbe: false`) and otherwise proceeds exactly as it
28
+ * would for an undeclared (v1) manifest. It never silently substitutes the
29
+ * declared value for the probed one, and never throws on a mismatch — see
30
+ * B-row "Postel's Law" discussion in the design doc.
31
+ *
32
+ * ── One trigger call over installed scopes only ─────────────────────────────
33
+ * `resolveTriggerSurface` is called AT MOST once per runtime, with the
34
+ * scopes that are actually installed on this machine (per manifest
35
+ * presence — never per the new corroboration fields, which is what keeps a
36
+ * v1-only install fully functional with no reinstall required). A
37
+ * hypothetical "what if both scopes were installed" answer is deliberately
38
+ * not offered; #2218 is a fact about THIS machine, not a simulation.
39
+ *
40
+ * ── Installed-ness is manifest PRESENCE, never the new fields ───────────────
41
+ * `installed` is `manifestVersion !== null`. A v1 manifest (no
42
+ * `manifestVersion` key at all, `manifestVersion: 1` after normalization) is
43
+ * a correct manifest written by an older GSD — never a broken one, never
44
+ * grounds for `installed: false`, a warning, or a reinstall prompt.
45
+ *
46
+ * ── Stems come from the manifest, not the source tree ───────────────────────
47
+ * `resolveTriggerSurface` needs `stems`. They are derived from the
48
+ * INSTALLED manifest's own `files` keys (see the private stem-derivation
49
+ * helpers below), never from a roster read of the source tree — a global
50
+ * `claude` install ships no `commands/gsd` source, so a roster read would
51
+ * return `[]` in exactly the configuration #2218 is about. The derivation
52
+ * consumes `isNamespacedByDir` and `composeCommandFilename`
53
+ * (`runtime-artifact-layout.cjs`, #2871 Phase 2's exported helpers) rather
54
+ * than re-deriving either rule as a fourth independent copy.
55
+ */
56
+ var __importDefault = (this && this.__importDefault) || function (mod) {
57
+ return (mod && mod.__esModule) ? mod : { "default": mod };
58
+ };
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.resolveInstalledSurfaces = resolveInstalledSurfaces;
61
+ const node_fs_1 = __importDefault(require("node:fs"));
62
+ const node_path_1 = __importDefault(require("node:path"));
63
+ const install_scope_cjs_1 = require("./install-scope.cjs");
64
+ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
65
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
66
+ const runtimeArtifactLayoutMod = require("./runtime-artifact-layout.cjs");
67
+ const { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, resolveTriggerSurface, isNamespacedByDir, composeCommandFilename, } = runtimeArtifactLayoutMod;
68
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
69
+ const installerMigrationsMod = require("./installer-migrations.cjs");
70
+ const { readInstallManifest, MANIFEST_NAME } = installerMigrationsMod;
71
+ // In .cts (CommonJS output) files, `require` is available as a global.
72
+ const _require = require;
73
+ // ── Internals ───────────────────────────────────────────────────────────
74
+ /**
75
+ * A trigger stem is a kebab-case token and nothing else. Verified against the
76
+ * real roster: all 71 `commands/gsd/*.md` stems match this, so it has no false
77
+ * negatives today.
78
+ *
79
+ * This is a SECURITY boundary, not cosmetics. A stem is derived from a manifest
80
+ * key — attacker-influenceable text, since a project-local
81
+ * `gsd-file-manifest.json` lives inside a repository a user may merely have
82
+ * cloned — and Phase 4 (#2873) renders it back to the user as `/gsd-<stem>`.
83
+ * Without this, `skills/gsd-../../../x` yields the stem `..`, and control
84
+ * characters, newlines, ANSI escapes and RTL-override codepoints all survive
85
+ * into that rendered trigger. The `commands` branch happened to be protected by
86
+ * its `composeCommandFilename` round-trip; the `skills` branch had no
87
+ * equivalent, so the rule is stated once here and applied to both.
88
+ */
89
+ const SAFE_STEM = /^[a-z0-9][a-z0-9-]*$/;
90
+ function getDefaultRuntimeRegistry() {
91
+ return _require('./capability-registry.cjs');
92
+ }
93
+ /** C7: every registered runtime id, sorted for determinism. Reads
94
+ * `opts.registry` when provided (so a sweep is assertable without the real
95
+ * capability registry), else the real one. */
96
+ function listRegisteredRuntimeIds(opts) {
97
+ const registry = opts.registry ?? getDefaultRuntimeRegistry();
98
+ return Object.keys(registry.runtimes).sort();
99
+ }
100
+ /**
101
+ * D — the stem inverse for one `commands`/`skills` layout kind entry.
102
+ * Consumes `isNamespacedByDir` (to decide the commands filename shape) and
103
+ * `composeCommandFilename` (to VERIFY a candidate stem round-trips to the
104
+ * exact filename seen) rather than re-deriving either rule locally (design
105
+ * "Rejected" #6). Manifest keys are normalized with an unconditional
106
+ * `.replace(/\\/g, '/')` (D7) — never gated on `path.sep` — matching this
107
+ * repo's recorded path-separator-normalization defect class.
108
+ */
109
+ function deriveStemsForKindEntry(kind, destSubpath, prefix, fileKeys) {
110
+ const destSubpathNorm = (0, shell_command_projection_cjs_1.posixNormalize)(destSubpath);
111
+ const boundary = `${destSubpathNorm}/`;
112
+ const namespacedByDir = isNamespacedByDir(kind, destSubpath, prefix);
113
+ const stems = new Set();
114
+ for (const rawKey of fileKeys) {
115
+ const key = rawKey.replace(/\\/g, '/');
116
+ if (!key.startsWith(boundary))
117
+ continue; // D4: key not under this declared subpath
118
+ const rest = key.slice(boundary.length);
119
+ if (rest === '')
120
+ continue;
121
+ const segments = rest.split('/');
122
+ if (kind === 'skills') {
123
+ const dirSegment = segments[0];
124
+ if (!dirSegment.startsWith(prefix))
125
+ continue; // D5: subpath matches, prefix does not
126
+ const stem = dirSegment.slice(prefix.length);
127
+ if (stem === '')
128
+ continue; // D7/B7: empty stem never emitted
129
+ if (!SAFE_STEM.test(stem))
130
+ continue; // security: reject anything but a kebab-case token
131
+ stems.add(stem); // D6: several files under one dir -> one stem
132
+ continue;
133
+ }
134
+ // commands: exactly one path segment — the composed filename itself.
135
+ if (segments.length !== 1)
136
+ continue;
137
+ const filename = segments[0];
138
+ if (!filename.endsWith('.md'))
139
+ continue;
140
+ const base = filename.slice(0, -3);
141
+ const candidateStem = namespacedByDir
142
+ ? base
143
+ : (base.startsWith(prefix) ? base.slice(prefix.length) : '');
144
+ if (candidateStem === '')
145
+ continue;
146
+ if (composeCommandFilename(namespacedByDir, prefix, candidateStem) !== filename)
147
+ continue;
148
+ if (!SAFE_STEM.test(candidateStem))
149
+ continue; // security: reject anything but a kebab-case token
150
+ stems.add(candidateStem);
151
+ }
152
+ return [...stems];
153
+ }
154
+ /** D — full stem set for one scope: every `commands`/`skills` layout kind
155
+ * entry, unioned. Empty `files` (C13) short-circuits to `[]` without
156
+ * resolving a layout at all. */
157
+ function deriveStemsFromManifest(runtime, scopeId, configHome, files, opts) {
158
+ const fileKeys = Object.keys(files);
159
+ if (fileKeys.length === 0)
160
+ return [];
161
+ const layout = opts.registry !== undefined
162
+ ? resolveRuntimeArtifactLayoutFromRegistry(opts.registry, runtime, configHome, scopeId)
163
+ : resolveRuntimeArtifactLayout(runtime, configHome, scopeId);
164
+ const stems = new Set();
165
+ for (const kindEntry of layout.kinds) {
166
+ if (kindEntry.kind !== 'commands' && kindEntry.kind !== 'skills')
167
+ continue; // excludes agents/kimi-agents (D4)
168
+ for (const stem of deriveStemsForKindEntry(kindEntry.kind, kindEntry.destSubpath, kindEntry.prefix, fileKeys)) {
169
+ stems.add(stem);
170
+ }
171
+ }
172
+ return [...stems].sort();
173
+ }
174
+ /**
175
+ * True when `p` is a symlink. An `lstatSync` throw (ENOENT — nothing at this
176
+ * path) is NOT evidence of a symlink; it is treated as "not a symlink" here
177
+ * and left for `readManifest` to classify (it already owns the absent-file
178
+ * case, per C14 above).
179
+ */
180
+ function isSymlinkPath(p, lstatSync) {
181
+ try {
182
+ return lstatSync(p).isSymbolicLink();
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ }
188
+ /**
189
+ * The shared "not installed" degraded shape (C14's EACCES path, and this
190
+ * phase's new symlink-guard path). A FACTORY, not a module-level constant
191
+ * object: a `const` object spread at each return site would still share the
192
+ * same `stems` ARRAY reference across every call (`...` shallow-copies the
193
+ * object but not the array a property points at), which would violate this
194
+ * module's own "builds fresh arrays/objects on every call" contract (C15) —
195
+ * a caller mutating one degraded record's `stems` must never be visible on
196
+ * another's.
197
+ */
198
+ function notInstalledScopeRecordFields() {
199
+ return {
200
+ installed: false,
201
+ manifestVersion: null,
202
+ declaredRuntime: null,
203
+ declaredScope: null,
204
+ declaredScopeMatchesProbe: null,
205
+ declaredRuntimeMatchesProbe: null,
206
+ stems: [],
207
+ };
208
+ }
209
+ /**
210
+ * Build one scope's record. `resolvedConfigHome` has already been probed
211
+ * successfully by the time this is called (a `resolveScope` `TypeError` is
212
+ * handled by the caller, per C8 — it is never this function's concern).
213
+ *
214
+ * Two distinct failure modes are handled with two distinct, narrowly-scoped
215
+ * try/catches — NOT one catch-all around the whole function:
216
+ *
217
+ * - The manifest READ (`readManifest`) is the only thing that can justify
218
+ * `installed: false` (C14, e.g. EACCES). A failure here means "we could
219
+ * not even tell whether this scope is installed".
220
+ * - The stem DERIVATION (`deriveStemsFromManifest`, which resolves a
221
+ * runtime artifact layout) is a separate concern. A `TypeError` thrown
222
+ * while deriving stems is a layout-lookup failure, not evidence the
223
+ * manifest is absent — the manifest was already read successfully, so
224
+ * `installed` and every declared/*MatchesProbe field stay exactly as the
225
+ * manifest reported. Conflating the two would report a genuinely
226
+ * installed runtime as `installed: false`, hiding a real install from
227
+ * #2218 shadow detection precisely when this module exists to surface it.
228
+ */
229
+ function buildScopeRecord(runtime, scopeId, resolvedConfigHome, opts) {
230
+ // Hardening requirement 2 (#2873 design doc, "Hardening requirements
231
+ // claimed from #2873's comment"): `readInstallManifest` -> `readJsonIfPresent`
232
+ // uses `existsSync` + `readFileSync` and therefore FOLLOWS symlinks, and
233
+ // this module resolves `local` against `process.cwd()` — a directory that,
234
+ // as of this phase, becomes reachable from an arbitrary cloned repository
235
+ // (this is the same phase that makes the local scope's manifest a first
236
+ // read target, not merely a write target). The in-tree precedent is
237
+ // `getAgentsDir` (`agent-install-check.cts`), which probes with
238
+ // `fs.lstatSync(...).isDirectory()`/`.isFile()` and deliberately does not
239
+ // follow. #2872 left this resolver's read un-guarded only because the path
240
+ // had zero callers at the time; refusing to follow a symlinked config dir
241
+ // or manifest here closes that asymmetry rather than carrying it forward.
242
+ // Degrading to `installed: false` reuses the SAME shape the EACCES catch
243
+ // below already returns — no new failure shape is introduced.
244
+ const lstatSync = opts.lstatSync ?? node_fs_1.default.lstatSync;
245
+ const manifestPath = node_path_1.default.join(resolvedConfigHome, MANIFEST_NAME);
246
+ if (isSymlinkPath(resolvedConfigHome, lstatSync) || isSymlinkPath(manifestPath, lstatSync)) {
247
+ return { scope: scopeId, configHome: resolvedConfigHome, ...notInstalledScopeRecordFields() };
248
+ }
249
+ let manifest;
250
+ try {
251
+ const readManifest = opts.readManifest ?? readInstallManifest;
252
+ manifest = readManifest(resolvedConfigHome);
253
+ }
254
+ catch {
255
+ // C14: manifest read/probe failure degrades to not-installed, never throws.
256
+ // Deliberately a BARE catch, unlike the stem-derivation catch below: any
257
+ // read failure at all (EACCES, ENOENT-after-race, a corrupt filesystem)
258
+ // legitimately means "cannot tell whether installed" (design row C14), so
259
+ // there is no error TYPE here that should instead propagate.
260
+ return { scope: scopeId, configHome: resolvedConfigHome, ...notInstalledScopeRecordFields() };
261
+ }
262
+ const installed = manifest.manifestVersion !== null; // C9: presence, never the new fields
263
+ const declaredRuntime = manifest.runtime;
264
+ const declaredScope = manifest.scope;
265
+ const declaredRuntimeMatchesProbe = declaredRuntime === null ? null : declaredRuntime === runtime;
266
+ const declaredScopeMatchesProbe = declaredScope === null ? null : declaredScope === scopeId;
267
+ let stems = [];
268
+ if (installed) {
269
+ try {
270
+ stems = deriveStemsFromManifest(runtime, scopeId, resolvedConfigHome, manifest.files, opts);
271
+ }
272
+ catch (error) {
273
+ if (!(error instanceof TypeError))
274
+ throw error;
275
+ // A layout-lookup failure means "we could not enumerate this scope's
276
+ // triggers", NOT "this scope is not installed" — the manifest read
277
+ // already succeeded above, so `installed` and the declared fields
278
+ // stay as reported. Conflating the two would hide a real install.
279
+ // Anything that is not the expected `TypeError` (a genuine bug in
280
+ // `deriveStemsFromManifest`/`resolveRuntimeArtifactLayout`) is
281
+ // rethrown rather than silently degrading to `stems: []` — this
282
+ // matches `resolveInstalledSurfaces`'s own `TypeError` narrowing
283
+ // below, so the two catches cannot drift apart.
284
+ stems = [];
285
+ }
286
+ }
287
+ return {
288
+ scope: scopeId,
289
+ configHome: resolvedConfigHome,
290
+ installed,
291
+ manifestVersion: manifest.manifestVersion,
292
+ declaredRuntime,
293
+ declaredScope,
294
+ declaredScopeMatchesProbe,
295
+ declaredRuntimeMatchesProbe,
296
+ stems,
297
+ };
298
+ }
299
+ /**
300
+ * Resolve one runtime's full installed surface. A `resolveScope` `TypeError`
301
+ * (unknown runtime, or `configHome.kind === 'none'`, e.g. vscode) propagates
302
+ * from here uncaught — `resolveInstalledSurfaces` decides whether that
303
+ * means "skip" (C7 sweep) or "propagate" (explicit single-runtime ask, C8).
304
+ */
305
+ function resolveOneRuntime(runtime, opts) {
306
+ const scopes = install_scope_cjs_1.SCOPE_ORDER.map((scopeId) => {
307
+ const resolved = (0, install_scope_cjs_1.resolveScope)({
308
+ id: scopeId,
309
+ runtime,
310
+ env: opts.env,
311
+ home: opts.home,
312
+ existsSync: opts.existsSync,
313
+ cwd: opts.cwd,
314
+ });
315
+ return buildScopeRecord(runtime, scopeId, resolved.configHome, opts);
316
+ });
317
+ // C12: dedupe by resolved configHome BEFORE the trigger call, keeping the
318
+ // higher-ranked (global, first in SCOPE_ORDER) scope. Both scope RECORDS
319
+ // above are unaffected — only the scope list handed to resolveTriggerSurface
320
+ // is deduped.
321
+ const seenConfigHomes = new Set();
322
+ const triggerScopeIds = [];
323
+ for (const record of scopes) {
324
+ if (!record.installed)
325
+ continue;
326
+ if (seenConfigHomes.has(record.configHome))
327
+ continue;
328
+ seenConfigHomes.add(record.configHome);
329
+ triggerScopeIds.push(record.scope);
330
+ }
331
+ const stemUnion = new Set();
332
+ for (const scopeId of triggerScopeIds) {
333
+ const record = scopes.find((s) => s.scope === scopeId);
334
+ for (const stem of record?.stems ?? [])
335
+ stemUnion.add(stem);
336
+ }
337
+ const triggers = triggerScopeIds.length > 0
338
+ ? resolveTriggerSurface(runtime, triggerScopeIds, {
339
+ stems: [...stemUnion].sort(),
340
+ registry: opts.registry,
341
+ })
342
+ : [];
343
+ return { runtime, scopes, triggers };
344
+ }
345
+ /**
346
+ * Read-only. Probes both install scopes for a runtime (or every registered
347
+ * runtime, sorted by id, when `runtime` is omitted), reads each scope's
348
+ * manifest, and resolves the trigger surface across the scopes that are
349
+ * actually installed on this machine. See the module-level comment for the
350
+ * non-obvious choices (probe-not-declared keying, report-don't-correct, one
351
+ * trigger call over installed scopes only).
352
+ *
353
+ * Pure with respect to caller-visible state: builds fresh arrays/objects on
354
+ * every call (C15) and performs no writes. Filesystem reads happen only via
355
+ * `resolveScope`'s injected `existsSync`/`env`/`home`/`cwd` and via
356
+ * `readManifest` (default: `readInstallManifest`).
357
+ *
358
+ * @throws {TypeError} when `runtime` is given explicitly and it is unknown,
359
+ * or has no installable config directory (`configHome.kind === 'none'`,
360
+ * e.g. vscode) — same contract `resolveScope` throws. In the all-runtimes
361
+ * sweep (`runtime` omitted), a runtime that would throw this same
362
+ * `TypeError` is skipped instead, so one non-installable runtime cannot
363
+ * kill the sweep (C7/C8). Any other error type is never swallowed here.
364
+ */
365
+ function resolveInstalledSurfaces(runtime, opts = {}) {
366
+ if (typeof runtime === 'string') {
367
+ return [resolveOneRuntime(runtime, opts)];
368
+ }
369
+ const results = [];
370
+ for (const runtimeId of listRegisteredRuntimeIds(opts)) {
371
+ try {
372
+ results.push(resolveOneRuntime(runtimeId, opts));
373
+ }
374
+ catch (error) {
375
+ if (error instanceof TypeError)
376
+ continue; // C8: sweep skips, never dies
377
+ throw error;
378
+ }
379
+ }
380
+ return results;
381
+ }
@@ -109,7 +109,9 @@ function validateInstallerMigrationActions(actions, migration) {
109
109
  // Ownership and runtime-contract evidence are required by
110
110
  // docs/installer-migrations.md#action-types and
111
111
  // docs/adr/0008-installer-migration-module.md#runtime-contract-decision.
112
- if (actType === 'remove-managed' || actType === 'rewrite-json') {
112
+ // `remove-empty-dir` carries the same evidence bar as `remove-managed`: it is
113
+ // still a destructive removal, just of a directory node instead of a file.
114
+ if (actType === 'remove-managed' || actType === 'rewrite-json' || actType === 'remove-empty-dir') {
113
115
  requireActionEvidence(act, 'ownershipEvidence', migration);
114
116
  }
115
117
  if (actType === 'rewrite-json') {
@@ -32,6 +32,7 @@ const VALID_CHOICES = ['keep', 'remove'];
32
32
  // on-disk `hooks/` directory in both directions: whitelist-but-missing
33
33
  // AND shipped-but-not-whitelisted both fail CI.
34
34
  exports.BUNDLED_GSD_HOOK_FILES = Object.freeze(new Set([
35
+ 'hooks/gsd-agent-isolation-guard.js',
35
36
  'hooks/gsd-check-update-worker.js',
36
37
  'hooks/gsd-check-update.js',
37
38
  'hooks/gsd-config-reload.js',
@@ -57,6 +58,7 @@ exports.BUNDLED_GSD_HOOK_FILES = Object.freeze(new Set([
57
58
  'hooks/gsd-validate-commit.sh',
58
59
  'hooks/gsd-workflow-guard.js',
59
60
  'hooks/gsd-worktree-path-guard.js',
61
+ 'hooks/gsd-write-guard.js',
60
62
  ]));
61
63
  // ── Internal helpers ──────────────────────────────────────────────────────────
62
64
  function installerMigrationActionLabel(action) {
@@ -74,6 +76,8 @@ function installerMigrationActionLabel(action) {
74
76
  return 'preserved';
75
77
  if (action.type === 'preserve-user')
76
78
  return 'preserved';
79
+ if (action.type === 'remove-empty-dir')
80
+ return 'removed';
77
81
  if (action.type === 'prompt-user')
78
82
  return 'blocked';
79
83
  return 'skipped';
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ /**
3
+ * Installer migration: retire the config-root `{"type":"commonjs"}` marker that
4
+ * pre-#2544 installs wrote over `<configRoot>/package.json`.
5
+ *
6
+ * What old artifact is being retired?
7
+ * `package.json` at the runtime config root. Before #2544,
8
+ * `installSharedHooksBundle(destRootDir)` wrote `{"type":"commonjs"}` there
9
+ * unconditionally on every install and every re-install, to pin GSD's staged
10
+ * `.js` hook scripts to CommonJS via Node's ancestor walk. #2544 moved that
11
+ * marker into the directories GSD actually fills (`hooks/`, and the
12
+ * `nativePlugin.dir` for runtimes declaring one) and stopped writing the
13
+ * config root at all. Without this migration an upgrader keeps BOTH markers:
14
+ * the new one under `hooks/` and the stale one at the root, so the config
15
+ * root stays pinned to CommonJS and the PR's own claim — that GSD no longer
16
+ * writes the shared config root — is false for every install made since the
17
+ * marker was introduced, until the user uninstalls.
18
+ *
19
+ * How do we prove it is GSD-owned?
20
+ * By exact content match, NOT by the manifest. The config-root marker was
21
+ * never recorded in `gsd-file-manifest.json` — `writeManifest()` records
22
+ * `hooks/`, `agents/`, `commands/`, `scripts/`, and the native plugin, and
23
+ * has never had a `manifest.files['package.json']` entry — so
24
+ * `classifyArtifact('package.json')` answers `unknown` and the planner's
25
+ * own guard would downgrade a `remove-managed` to `preserve-user`.
26
+ * This migration therefore supplies the "purpose-built detector for an old
27
+ * GSD-owned shape" that `docs/installer-migrations.md#remove-managed`
28
+ * sanctions, and declares the resulting classification on the action: the
29
+ * file is removed only when its bytes are exactly the marker GSD writes
30
+ * (`{"type":"commonjs"}`, trailing whitespace tolerated). That is the same
31
+ * predicate `removeCommonJsMarker` has always used on the uninstall side, so
32
+ * install, uninstall, and migration cannot drift apart.
33
+ *
34
+ * What happens if the user modified it?
35
+ * Then it is not the marker, and this migration does not touch it. Any
36
+ * `package.json` carrying a `name`, `dependencies`, `scripts`, or any key
37
+ * beyond the single `type` — i.e. every file the #2544 defect destroyed —
38
+ * fails the exact-content test and is left exactly as found. There is
39
+ * deliberately no `backup-and-remove` branch: a modified file here is not a
40
+ * patched GSD artifact, it is somebody else's file.
41
+ *
42
+ * What happens if it is missing?
43
+ * No actions. Fresh post-#2544 installs never wrote it, already-migrated
44
+ * installs no longer have it, and the executor additionally journals a
45
+ * `missing` outcome if it disappears between plan and apply. Idempotent.
46
+ *
47
+ * What runtime and scope does it affect?
48
+ * Every runtime whose config root received the marker — i.e. every runtime
49
+ * not excluded from `installSharedHooksBundle(targetDir)` by
50
+ * `hostBehaviors.skipSharedHooksInstall` and not Codex: antigravity,
51
+ * augment, claude, claude-local, codebuddy, hermes, qwen, kilo, opencode,
52
+ * and pi. The `runtimes` field is OMITTED — the framework's "all runtimes" —
53
+ * rather than carrying that hand-list: a runtime that never received the
54
+ * marker simply has no file to match, so enumerating them would add a second
55
+ * place for the set to drift out of date without changing behavior. Note it
56
+ * must be omitted and not `[]`; see the field's own comment below.
57
+ *
58
+ * ONE DELIBERATE CARVE-OUT — kimi. Kimi's marker was written to its native
59
+ * hook root (`~/.kimi`, `resolveKimiHooksTomlDir`), which is NOT under
60
+ * kimi's `configDir` (its generic Agent-Skills root). Migration relPaths are
61
+ * structurally confined to `configDir` (`validateSafeRelPath` /
62
+ * `ensureInsideConfig`), so this framework cannot address that path at all.
63
+ * Kimi's stale root marker is retired by the installer instead, at the same
64
+ * call site that writes its replacement — see the `kimi-hooks-toml` branch
65
+ * in `bin/install.js`. Named here so the gap is not mistaken for an
66
+ * oversight.
67
+ *
68
+ * Is the action safe in non-interactive install?
69
+ * Yes. `remove-managed` is non-interactive and journaled, the executor takes
70
+ * a rollback snapshot before unlinking, and no branch of this migration can
71
+ * emit `prompt-user`. A file that is not byte-identical to GSD's marker
72
+ * produces no action at all.
73
+ *
74
+ * See docs/installer-migrations.md#shipped-migrations and #action-types.
75
+ */
76
+ var __importDefault = (this && this.__importDefault) || function (mod) {
77
+ return (mod && mod.__esModule) ? mod : { "default": mod };
78
+ };
79
+ const node_fs_1 = __importDefault(require("node:fs"));
80
+ const node_path_1 = __importDefault(require("node:path"));
81
+ const node_crypto_1 = __importDefault(require("node:crypto"));
82
+ /** The config-root path the pre-#2544 installer wrote, relative to configDir. */
83
+ const STALE_ROOT_MARKER = 'package.json';
84
+ /**
85
+ * The exact marker content GSD wrote. Duplicated as a literal rather than
86
+ * imported from `src/commonjs-marker.cts` on purpose: a migration record is a
87
+ * frozen historical statement about what a PAST version installed, and it must
88
+ * keep matching those bytes even if the live module's constant is ever changed.
89
+ * Importing would silently re-point this detector at a future value.
90
+ */
91
+ const LEGACY_MARKER_CONTENT = '{"type":"commonjs"}';
92
+ const OWNERSHIP_EVIDENCE = 'file content is byte-identical to the {"type":"commonjs"} marker pre-#2544 installs '
93
+ + 'wrote at the config root (installSharedHooksBundle); same exact-content predicate '
94
+ + 'removeCommonJsMarker uses on uninstall. Never manifest-recorded, so this is the '
95
+ + 'purpose-built detector permitted by docs/installer-migrations.md#remove-managed';
96
+ const REASON = 'superseded by the hooks/ and plugin-dir markers (#2544); leaving it pins the shared '
97
+ + 'config root to CommonJS and keeps GSD occupying a file it no longer writes';
98
+ const migration = {
99
+ id: '2026-07-28-retire-config-root-commonjs-marker',
100
+ title: 'Retire the pre-#2544 config-root CommonJS marker',
101
+ description: 'Remove <configRoot>/package.json when it is exactly the {"type":"commonjs"} marker '
102
+ + 'pre-#2544 installs wrote there, now superseded by markers scoped to the directories '
103
+ + 'GSD owns. A package.json with any other content is left untouched.',
104
+ introducedIn: '1.8.0',
105
+ scopes: ['global', 'local'],
106
+ destructive: true,
107
+ plan: (ctx) => {
108
+ const markerPath = node_path_1.default.join(ctx.configDir, STALE_ROOT_MARKER);
109
+ let stat;
110
+ try {
111
+ // lstat, not existsSync: existsSync follows symlinks and reports false for
112
+ // a dangling one. A symlink here is not something GSD wrote, and removing
113
+ // it is never ours to do — mirrors classifyMarker's fail-closed posture.
114
+ stat = node_fs_1.default.lstatSync(markerPath);
115
+ }
116
+ catch {
117
+ return [];
118
+ }
119
+ if (!stat.isFile())
120
+ return [];
121
+ let content;
122
+ try {
123
+ content = node_fs_1.default.readFileSync(markerPath, 'utf8');
124
+ }
125
+ catch {
126
+ // Present but unreadable never downgrades to the permissive answer.
127
+ return [];
128
+ }
129
+ if (content.trim() !== LEGACY_MARKER_CONTENT)
130
+ return [];
131
+ const hash = node_crypto_1.default.createHash('sha256').update(content).digest('hex');
132
+ return [
133
+ {
134
+ type: 'remove-managed',
135
+ relPath: STALE_ROOT_MARKER,
136
+ reason: REASON,
137
+ ownershipEvidence: OWNERSHIP_EVIDENCE,
138
+ // Declared, not derived: classifyArtifact answers 'unknown' for this
139
+ // never-manifested path, and the planner downgrades a remove-managed on
140
+ // an 'unknown' classification to preserve-user. The exact-content match
141
+ // above IS the ownership proof, so the classification is stated here.
142
+ classification: 'managed-pristine',
143
+ originalHash: hash,
144
+ currentHash: hash,
145
+ },
146
+ ];
147
+ },
148
+ };
149
+ module.exports = migration;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * Installer migration: retire Cursor's duplicate commands/ surface (#2644).
4
+ *
5
+ * Cursor discovers GSD skills as slash-menu entries while also keeping them
6
+ * model-invocable. Older GSD releases installed the same workflows again as
7
+ * commands/gsd-*.md, so every action appeared twice. The skills remain the
8
+ * sole workflow surface; this migration removes only old command files proven
9
+ * managed by gsd-file-manifest.json. Modified files are backed up first and
10
+ * unmanifested files are preserved.
11
+ */
12
+ var __importDefault = (this && this.__importDefault) || function (mod) {
13
+ return (mod && mod.__esModule) ? mod : { "default": mod };
14
+ };
15
+ const node_fs_1 = __importDefault(require("node:fs"));
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const COMMANDS_DIR = 'commands';
18
+ const REASON = 'Cursor exposes skills directly in the slash menu; the parallel command file duplicated the same GSD action (#2644)';
19
+ const OWNERSHIP_EVIDENCE = 'pre-#2644 Cursor installs record commands/gsd-*.md in gsd-file-manifest.json';
20
+ const migration = {
21
+ id: '2026-07-29-cursor-retire-commands-surface',
22
+ title: 'Retire Cursor duplicate commands surface',
23
+ description: 'Remove manifest-managed Cursor commands/gsd-*.md files now that skills are the single slash-menu and model-invocation surface.',
24
+ introducedIn: '1.8.1',
25
+ runtimes: ['cursor'],
26
+ scopes: ['global', 'local'],
27
+ destructive: true,
28
+ plan: (ctx) => {
29
+ const commandsDir = node_path_1.default.join(ctx.configDir, COMMANDS_DIR);
30
+ let entries;
31
+ try {
32
+ if (!node_fs_1.default.existsSync(commandsDir) || node_fs_1.default.lstatSync(commandsDir).isSymbolicLink())
33
+ return [];
34
+ entries = node_fs_1.default.readdirSync(commandsDir, { withFileTypes: true });
35
+ }
36
+ catch {
37
+ return [];
38
+ }
39
+ const actions = [];
40
+ for (const entry of entries) {
41
+ if (!entry.isFile() || !entry.name.startsWith('gsd-') || !entry.name.endsWith('.md'))
42
+ continue;
43
+ const relPath = node_path_1.default.posix.join(COMMANDS_DIR, entry.name);
44
+ const artifact = ctx.classifyArtifact(relPath);
45
+ if (artifact.classification === 'managed-pristine') {
46
+ actions.push({ type: 'remove-managed', relPath, reason: REASON, ownershipEvidence: OWNERSHIP_EVIDENCE });
47
+ }
48
+ else if (artifact.classification === 'managed-modified') {
49
+ actions.push({ type: 'backup-and-remove', relPath, reason: REASON, ownershipEvidence: OWNERSHIP_EVIDENCE });
50
+ }
51
+ }
52
+ return actions;
53
+ },
54
+ };
55
+ module.exports = migration;