@opengsd/gsd-core 1.16.0 → 1.18.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 (211) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -2
  3. package/.opencode/plugins/gsd-core.js +251 -1
  4. package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
  5. package/GEMINI.md +1 -1
  6. package/agents/gsd-advisor-researcher.compact.md +1 -1
  7. package/agents/gsd-advisor-researcher.md +1 -1
  8. package/agents/gsd-assumptions-analyzer.compact.md +1 -1
  9. package/agents/gsd-assumptions-analyzer.md +1 -1
  10. package/agents/gsd-code-fixer.compact.md +1 -1
  11. package/agents/gsd-code-fixer.md +1 -1
  12. package/agents/gsd-code-reviewer.compact.md +1 -1
  13. package/agents/gsd-code-reviewer.md +1 -1
  14. package/agents/gsd-codebase-mapper.compact.md +1 -1
  15. package/agents/gsd-codebase-mapper.md +1 -1
  16. package/agents/gsd-debug-session-manager.compact.md +6 -0
  17. package/agents/gsd-debug-session-manager.md +6 -0
  18. package/agents/gsd-debugger.md +1 -1
  19. package/agents/gsd-doc-verifier.compact.md +4 -4
  20. package/agents/gsd-doc-verifier.md +4 -3
  21. package/agents/gsd-doc-writer.compact.md +23 -5
  22. package/agents/gsd-doc-writer.md +19 -6
  23. package/agents/gsd-eval-auditor.compact.md +1 -1
  24. package/agents/gsd-eval-auditor.md +1 -1
  25. package/agents/gsd-executor.md +7 -3
  26. package/agents/gsd-integration-checker.compact.md +1 -1
  27. package/agents/gsd-integration-checker.md +1 -1
  28. package/agents/gsd-nyquist-auditor.compact.md +1 -1
  29. package/agents/gsd-nyquist-auditor.md +1 -1
  30. package/agents/gsd-phase-researcher.md +1 -1
  31. package/agents/gsd-plan-checker.md +2 -1
  32. package/agents/gsd-planner.md +5 -4
  33. package/agents/gsd-project-researcher.compact.md +1 -1
  34. package/agents/gsd-project-researcher.md +1 -1
  35. package/agents/gsd-research-synthesizer.compact.md +5 -5
  36. package/agents/gsd-research-synthesizer.md +5 -5
  37. package/agents/gsd-roadmapper.compact.md +1 -1
  38. package/agents/gsd-roadmapper.md +1 -1
  39. package/agents/gsd-security-auditor.compact.md +1 -1
  40. package/agents/gsd-security-auditor.md +1 -1
  41. package/agents/gsd-ui-auditor.compact.md +1 -1
  42. package/agents/gsd-ui-auditor.md +1 -1
  43. package/agents/gsd-ui-checker.compact.md +1 -1
  44. package/agents/gsd-ui-checker.md +1 -1
  45. package/agents/gsd-ui-researcher.compact.md +13 -1
  46. package/agents/gsd-ui-researcher.md +28 -1
  47. package/agents/gsd-verifier.md +1 -1
  48. package/bin/gsd-mcp-server.js +3 -1
  49. package/bin/install.js +398 -95
  50. package/commands/gsd/code-review.md +8 -0
  51. package/commands/gsd/pause-work.md +1 -1
  52. package/gsd-core/bin/gsd-tools.cjs +34 -12
  53. package/gsd-core/bin/lib/adr-parser.cjs +37 -5
  54. package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
  55. package/gsd-core/bin/lib/capability-lock.cjs +29 -7
  56. package/gsd-core/bin/lib/capability-registry.cjs +138 -80
  57. package/gsd-core/bin/lib/capability-source.cjs +3 -1
  58. package/gsd-core/bin/lib/capability-state.cjs +6 -3
  59. package/gsd-core/bin/lib/capability-validator.cjs +8 -5
  60. package/gsd-core/bin/lib/capability-writer.cjs +2 -1
  61. package/gsd-core/bin/lib/commands.cjs +33 -9
  62. package/gsd-core/bin/lib/config-loader.cjs +3 -3
  63. package/gsd-core/bin/lib/decision-coverage-support.cjs +19 -0
  64. package/gsd-core/bin/lib/gate-context-drift.cjs +5 -4
  65. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +15 -0
  66. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +13 -0
  67. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
  68. package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
  69. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +60 -5
  70. package/gsd-core/bin/lib/gate-ui-safety.cjs +2 -1
  71. package/gsd-core/bin/lib/graphify.cjs +3 -2
  72. package/gsd-core/bin/lib/init.cjs +43 -16
  73. package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
  74. package/gsd-core/bin/lib/install-engine.cjs +11 -0
  75. package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
  76. package/gsd-core/bin/lib/install-profiles.cjs +5 -3
  77. package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
  78. package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
  79. package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
  80. package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
  81. package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
  82. package/gsd-core/bin/lib/mcp-server.cjs +5 -1
  83. package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
  84. package/gsd-core/bin/lib/opencode-version.cjs +486 -0
  85. package/gsd-core/bin/lib/phase.cjs +141 -11
  86. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +2 -1
  87. package/gsd-core/bin/lib/report-parser.cjs +57 -1
  88. package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
  89. package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
  90. package/gsd-core/bin/lib/roadmap.cjs +56 -5
  91. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +142 -6
  92. package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
  93. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +58 -3
  94. package/gsd-core/bin/lib/runtime-slash.cjs +13 -0
  95. package/gsd-core/bin/lib/shell-command-projection.cjs +2 -0
  96. package/gsd-core/bin/lib/smart-entry.cjs +100 -53
  97. package/gsd-core/bin/lib/state-document.cjs +37 -3
  98. package/gsd-core/bin/lib/state-transition.cjs +98 -19
  99. package/gsd-core/bin/lib/state.cjs +544 -126
  100. package/gsd-core/bin/lib/task-command-router.cjs +2 -3
  101. package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
  102. package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
  103. package/gsd-core/bin/lib/update-context.cjs +18 -1
  104. package/gsd-core/bin/lib/verification.cjs +107 -17
  105. package/gsd-core/bin/lib/verify.cjs +36 -1
  106. package/gsd-core/references/agent-contracts.md +1 -1
  107. package/gsd-core/references/agent-skills-bootstrap.md +26 -7
  108. package/gsd-core/references/git-integration.md +3 -3
  109. package/gsd-core/references/planner-reversibility.md +18 -4
  110. package/gsd-core/references/planner-verify-command-grounding.md +18 -0
  111. package/gsd-core/references/tdd.md +4 -3
  112. package/gsd-core/references/verifier-phase-gates.md +8 -1
  113. package/gsd-core/workflows/add-backlog.md +10 -2
  114. package/gsd-core/workflows/add-phase.md +5 -1
  115. package/gsd-core/workflows/add-tests.md +5 -1
  116. package/gsd-core/workflows/add-todo.md +5 -1
  117. package/gsd-core/workflows/ai-integration-phase.md +5 -1
  118. package/gsd-core/workflows/audit-milestone.md +21 -9
  119. package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
  120. package/gsd-core/workflows/autonomous.md +52 -64
  121. package/gsd-core/workflows/check-todos.md +5 -1
  122. package/gsd-core/workflows/code-review-fix.md +5 -1
  123. package/gsd-core/workflows/code-review.md +21 -2
  124. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
  125. package/gsd-core/workflows/complete-milestone.md +25 -5
  126. package/gsd-core/workflows/debug.md +5 -1
  127. package/gsd-core/workflows/diagnose-issues.md +5 -1
  128. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
  129. package/gsd-core/workflows/discuss-phase-assumptions.md +11 -3
  130. package/gsd-core/workflows/discuss-phase.md +5 -1
  131. package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
  132. package/gsd-core/workflows/docs-update.md +27 -5
  133. package/gsd-core/workflows/edit-phase.md +5 -1
  134. package/gsd-core/workflows/eval-review.md +11 -3
  135. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
  136. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
  137. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +12 -6
  138. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +19 -2
  139. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
  140. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +5 -1
  141. package/gsd-core/workflows/execute-phase.md +40 -7
  142. package/gsd-core/workflows/execute-plan.md +8 -4
  143. package/gsd-core/workflows/extract-learnings.md +6 -2
  144. package/gsd-core/workflows/forensics.md +5 -1
  145. package/gsd-core/workflows/import.md +5 -1
  146. package/gsd-core/workflows/insert-phase.md +5 -1
  147. package/gsd-core/workflows/manager.md +10 -2
  148. package/gsd-core/workflows/map-codebase.md +5 -1
  149. package/gsd-core/workflows/milestone-summary.md +10 -2
  150. package/gsd-core/workflows/new-milestone.md +13 -5
  151. package/gsd-core/workflows/new-project.md +9 -5
  152. package/gsd-core/workflows/next.md +8 -4
  153. package/gsd-core/workflows/pause-work.md +3 -3
  154. package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
  155. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +5 -1
  156. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
  157. package/gsd-core/workflows/plan-phase.md +41 -73
  158. package/gsd-core/workflows/plan-review-convergence.md +17 -5
  159. package/gsd-core/workflows/plant-seed.md +8 -4
  160. package/gsd-core/workflows/progress.md +6 -2
  161. package/gsd-core/workflows/quick-batch.md +5 -1
  162. package/gsd-core/workflows/quick.md +22 -7
  163. package/gsd-core/workflows/remove-phase.md +5 -1
  164. package/gsd-core/workflows/resume-project.md +19 -11
  165. package/gsd-core/workflows/review.md +6 -2
  166. package/gsd-core/workflows/scan.md +5 -1
  167. package/gsd-core/workflows/secure-phase.md +6 -2
  168. package/gsd-core/workflows/settings.md +3 -3
  169. package/gsd-core/workflows/ship.md +13 -10
  170. package/gsd-core/workflows/thread.md +10 -2
  171. package/gsd-core/workflows/transition.md +5 -1
  172. package/gsd-core/workflows/ui-phase.md +5 -1
  173. package/gsd-core/workflows/ui-review.md +6 -2
  174. package/gsd-core/workflows/ultraplan-phase.md +5 -1
  175. package/gsd-core/workflows/update.md +5 -1
  176. package/gsd-core/workflows/validate-phase.md +6 -2
  177. package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
  178. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
  179. package/gsd-core/workflows/verify-work.md +11 -3
  180. package/hooks/dist/gsd-agent-isolation-guard.js +3 -1
  181. package/hooks/dist/gsd-check-update.js +26 -20
  182. package/hooks/dist/gsd-context-monitor.js +14 -5
  183. package/hooks/dist/gsd-cursor-subagent-start.js +2 -1
  184. package/hooks/dist/gsd-secret-read-guard.js +189 -15
  185. package/hooks/dist/gsd-statusline.js +321 -22
  186. package/hooks/dist/gsd-validate-commit.sh +27 -5
  187. package/hooks/gsd-agent-isolation-guard.js +3 -1
  188. package/hooks/gsd-check-update.js +26 -20
  189. package/hooks/gsd-context-monitor.js +14 -5
  190. package/hooks/gsd-cursor-subagent-start.js +2 -1
  191. package/hooks/gsd-secret-read-guard.js +189 -15
  192. package/hooks/gsd-statusline.js +321 -22
  193. package/hooks/gsd-validate-commit.sh +27 -5
  194. package/package.json +1 -1
  195. package/scripts/audit-workflow-script-paths.cjs +1 -1
  196. package/scripts/diagnose-validate-commit-in-tier.cjs +270 -0
  197. package/scripts/diagnose-validate-commit-phases.cjs +348 -0
  198. package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
  199. package/scripts/docs-guard-registry.cjs +2 -0
  200. package/scripts/gen-capability-registry.cjs +7 -1
  201. package/scripts/gen-loop-host-contract.cjs +4 -1
  202. package/scripts/lib/macos-conformance-tier.generated.cjs +13 -0
  203. package/scripts/lib/platform-conformance-tier.generated.cjs +26 -0
  204. package/scripts/lint-phase-arg-assignment.cjs +2 -2
  205. package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
  206. package/scripts/mutation-matrix.cjs +3 -1
  207. package/scripts/release-tarball-smoke.cjs +15 -7
  208. package/scripts/xtrace-phase-timing.cjs +205 -0
  209. package/skills/gsd-code-review/SKILL.md +8 -0
  210. package/skills/gsd-pause-work/SKILL.md +1 -1
  211. package/vscode/package.json +1 -1
@@ -20,6 +20,8 @@ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
20
20
  const io = require("./io.cjs");
21
21
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
22
22
  const configLoader = require("./config-loader.cjs");
23
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- capability-ledger.cjs is an export= CommonJS module
24
+ const capabilityLedger = require("./capability-ledger.cjs");
23
25
  const project_root_cjs_1 = require("./project-root.cjs");
24
26
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- model-resolver.cjs is an export= CommonJS module
25
27
  const modelResolver = require("./model-resolver.cjs");
@@ -157,6 +159,24 @@ function slugifyPhaseName(phaseName) {
157
159
  // the same reported phase_slug.
158
160
  return phaseName ? coreUtils.generateSlugInternal(phaseName, null) : null;
159
161
  }
162
+ /**
163
+ * #4854: the one place that decides which name an `init` verb emits as
164
+ * `phase_name`. When a phase directory exists, `searchPhaseInDir` derives
165
+ * `phaseInfo.phase_name` from the directory-name remainder — already slugified
166
+ * (`phase.add` writes `${num}-${slug}` dirs), so it comes out byte-identical to
167
+ * `phase_slug`. The ROADMAP carries the curated display name
168
+ * (`### Phase N: <Name>`), so it wins; the disk-derived name is the fallback
169
+ * for a phase the ROADMAP does not describe, then `null`. #3171 fixed this
170
+ * inline for `init execute-phase` only; plan-phase (whose `phase_name` reaches
171
+ * STATE.md through `state planned-phase --name`), verify-work, code-review,
172
+ * discuss-phase-assumptions and phase-op read the raw disk value. `phase_slug`
173
+ * is deliberately not routed through here — it stays disk-derived.
174
+ */
175
+ function resolvePhaseDisplayName(phaseInfo, roadmapPhase) {
176
+ return roadmapPhase?.['phase_name']
177
+ || phaseInfo?.['phase_name']
178
+ || null;
179
+ }
160
180
  /**
161
181
  * #2994 (review finding, DEFECT.GENERATIVE-FIX): shared archived/not-found
162
182
  * fallback applied identically by `cmdInitExecutePhase`, `cmdInitPlanPhase`,
@@ -164,10 +184,11 @@ function slugifyPhaseName(phaseName) {
164
184
  * `cmdInitDiscussPhaseAssumptions` — 6 call sites previously reproducing the
165
185
  * exact same two-branch control flow verbatim (only the synthetic
166
186
  * replacement object's field set differs per caller, supplied here via
167
- * `buildFallback`). `cmdInitPhaseOp` is deliberately left untouched (CRITICAL
168
- * blast radius, 179 dependents) even though it follows the same shape, since
169
- * its own fallback object differs by one field (`has_reviews` absent) and is
170
- * not a byte-identical copy.
187
+ * `buildFallback`). `cmdInitPhaseOp` does not use this helper (its own fallback
188
+ * object differs by one field, `has_reviews` absent, so it is not a
189
+ * byte-identical copy), but since #4854 it resolves `roadmapPhase` once up
190
+ * front like the others, so `resolvePhaseDisplayName` sees it in the ordinary
191
+ * case too.
171
192
  *
172
193
  * Behavior-preserving by construction: every original call site either (a)
173
194
  * unconditionally computed `roadmapPhase` once up front and then applied
@@ -924,7 +945,7 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
924
945
  // the human-curated display name (`### Phase N: <Name>`); prefer it,
925
946
  // matching the no-disk fallback above. phase_slug stays disk-derived — it
926
947
  // correctly feeds branch-name construction below and is unchanged here.
927
- phase_name: (roadmapPhase?.['phase_name']) || (phaseInfo?.['phase_name']) || null,
948
+ phase_name: resolvePhaseDisplayName(phaseInfo, roadmapPhase),
928
949
  phase_slug: phaseInfo?.['phase_slug'] || null,
929
950
  phase_req_ids,
930
951
  plans: phaseInfo?.['plans'] || [],
@@ -1043,7 +1064,7 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
1043
1064
  const phase_req_ids = reqExtracted && reqExtracted !== 'TBD' ? reqExtracted : null;
1044
1065
  const phaseDirPlan = phaseInfo?.['directory'] || null;
1045
1066
  const phaseNumberPlan = phaseInfo?.['phase_number'] || null;
1046
- const phaseNamePlan = phaseInfo?.['phase_name'] || null;
1067
+ const phaseNamePlan = resolvePhaseDisplayName(phaseInfo, roadmapPhase);
1047
1068
  const rawProjectCodePlan = config.project_code || '';
1048
1069
  let expectedPhaseDirPlan = null;
1049
1070
  if (!phaseDirPlan && phaseNumberPlan && phaseNamePlan) {
@@ -1689,7 +1710,7 @@ function cmdInitVerifyWork(cwd, phase, raw) {
1689
1710
  // itself stays relative — evaluateUatPassed above still joins it against cwd.
1690
1711
  phase_dir: phaseDir ? toPosixPath(node_path_1.default.join(cwd, phaseDir)) : null,
1691
1712
  phase_number: phaseInfo?.['phase_number'] || null,
1692
- phase_name: phaseInfo?.['phase_name'] || null,
1713
+ phase_name: resolvePhaseDisplayName(phaseInfo, roadmapPhase),
1693
1714
  // #2376: verify-work.md's plan_gap_closure step reads these instead of
1694
1715
  // hardcoding '.planning/STATE.md' / '.planning/ROADMAP.md' literals.
1695
1716
  state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
@@ -1761,7 +1782,7 @@ function cmdInitCodeReview(cwd, phase, raw, options = {}) {
1761
1782
  });
1762
1783
  const phaseDir = phaseInfo?.['directory'] || null;
1763
1784
  const phaseNumber = phaseInfo?.['phase_number'] || null;
1764
- const phaseName = phaseInfo?.['phase_name'] || null;
1785
+ const phaseName = resolvePhaseDisplayName(phaseInfo, roadmapPhase);
1765
1786
  const fallow = detectFallowConfig(cwd);
1766
1787
  const result = {
1767
1788
  commit_docs: config.commit_docs,
@@ -1890,7 +1911,7 @@ function cmdInitDiscussPhaseAssumptions(cwd, phase, raw, options = {}) {
1890
1911
  });
1891
1912
  const phaseDir = phaseInfo?.['directory'] || null;
1892
1913
  const phaseNumber = phaseInfo?.['phase_number'] || null;
1893
- const phaseName = phaseInfo?.['phase_name'] || null;
1914
+ const phaseName = resolvePhaseDisplayName(phaseInfo, roadmapPhase);
1894
1915
  // #2994: mirrors discuss-phase-assumptions.md's own auto_advance step
1895
1916
  // resolver — `--auto` flag OR the consolidated `check auto-mode --pick
1896
1917
  // active` fact (workflow._auto_chain_active OR workflow.auto_advance).
@@ -1942,8 +1963,10 @@ function cmdInitPhaseOp(cwd, phase, raw) {
1942
1963
  output(withProjectRoot(cwd, result), raw);
1943
1964
  return;
1944
1965
  }
1966
+ // #4854: resolved once, up front (a pure read — guardedGetRoadmapPhase has no side effects),
1967
+ // so the ordinary case (phase dir on disk, not archived) also sees the ROADMAP display name.
1968
+ const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
1945
1969
  if (phaseInfo?.['archived']) {
1946
- const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
1947
1970
  if (roadmapPhase?.['found']) {
1948
1971
  const phaseName = roadmapPhase['phase_name'];
1949
1972
  phaseInfo = {
@@ -1966,7 +1989,6 @@ function cmdInitPhaseOp(cwd, phase, raw) {
1966
1989
  }
1967
1990
  }
1968
1991
  if (!phaseInfo) {
1969
- const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
1970
1992
  if (roadmapPhase?.['found']) {
1971
1993
  const phaseName = roadmapPhase['phase_name'];
1972
1994
  phaseInfo = {
@@ -1990,7 +2012,7 @@ function cmdInitPhaseOp(cwd, phase, raw) {
1990
2012
  }
1991
2013
  const phaseDir = phaseInfo?.['directory'] || null;
1992
2014
  const phaseNumber = phaseInfo?.['phase_number'] || null;
1993
- const phaseName = phaseInfo?.['phase_name'] || null;
2015
+ const phaseName = resolvePhaseDisplayName(phaseInfo, roadmapPhase);
1994
2016
  const rawProjectCode = config.project_code || '';
1995
2017
  let expectedPhaseDir = null;
1996
2018
  if (!phaseDir && phaseNumber && phaseName) {
@@ -3753,7 +3775,7 @@ function buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics) {
3753
3775
  }).join('\n');
3754
3776
  return `<agent_skills>\nRead these user-configured skills:\n${lines}\n</agent_skills>`;
3755
3777
  }
3756
- function cmdAgentSkills(cwd, agentType, raw, jsonMode) {
3778
+ function cmdAgentSkills(cwd, agentType, raw, jsonMode, skillsOnly = false) {
3757
3779
  if (!agentType) {
3758
3780
  output('', raw, '');
3759
3781
  return;
@@ -3784,8 +3806,13 @@ function cmdAgentSkills(cwd, agentType, raw, jsonMode) {
3784
3806
  // (a real exit code) instead of a prose config-get gate. Compact is tried
3785
3807
  // first when requested; a missing compact sibling falls back to canonical
3786
3808
  // with the fallback disclosed in the payload itself, never a silent switch.
3809
+ //
3810
+ // #5122: `skillsOnly` (the CLI's `--skills-only`) is the agent's own
3811
+ // self-load. It wants configured skills and nothing else, so it skips the
3812
+ // fallback on every runtime — otherwise a non-Claude agent reads its own
3813
+ // persona back into itself as if it were a skills block.
3787
3814
  let agentPayloadVariant = null;
3788
- if (!block) {
3815
+ if (!block && !skillsOnly) {
3789
3816
  const runtime = (config && config['runtime']) || process.env['GSD_RUNTIME'] || 'claude';
3790
3817
  if (runtime !== 'claude') {
3791
3818
  const agentCheck = checkAgentsInstalled(runtime, projectRoot);
@@ -4051,7 +4078,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
4051
4078
  if (!entry.isDirectory())
4052
4079
  continue;
4053
4080
  const skillMdPath = node_path_1.default.join(rootPath, entry.name, 'SKILL.md');
4054
- const content = (0, shell_command_projection_cjs_1.platformReadSync)(skillMdPath);
4081
+ const content = capabilityLedger.readSkillFile(skillMdPath);
4055
4082
  if (content !== null) {
4056
4083
  if (pushSkillEntry(`${entry.name}/SKILL.md`, content, skillMdPath))
4057
4084
  skillCount++;
@@ -4076,7 +4103,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
4076
4103
  if (!nested.isDirectory())
4077
4104
  continue;
4078
4105
  const nestedSkillMd = node_path_1.default.join(nestedSkillsDir, nested.name, 'SKILL.md');
4079
- const nestedContent = (0, shell_command_projection_cjs_1.platformReadSync)(nestedSkillMd);
4106
+ const nestedContent = capabilityLedger.readSkillFile(nestedSkillMd);
4080
4107
  if (nestedContent === null)
4081
4108
  continue;
4082
4109
  // Use forward-slash separator explicitly so manifest paths are posix-style
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  */
36
36
  const node_fs_1 = __importDefault(require("node:fs"));
37
37
  const node_path_1 = __importDefault(require("node:path"));
38
- const node_os_1 = __importDefault(require("node:os"));
38
+ const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
39
39
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- install-fs-adapter.cjs is an export= CommonJS module
40
40
  const installFsAdapter = require("./install-fs-adapter.cjs");
41
41
  const { installFs } = installFsAdapter;
@@ -154,7 +154,7 @@ function _findAncestorGsdConfigPath(targetDir) {
154
154
  * @param targetDir Runtime install root (walks up to find .planning/).
155
155
  */
156
156
  function readGsdEffectiveEffortConfig(targetDir = null) {
157
- const homeDefaults = _readGsdConfigFile(node_path_1.default.join(node_os_1.default.homedir(), '.gsd', 'defaults.json'), '~/.gsd/defaults.json');
157
+ const homeDefaults = _readGsdConfigFile(node_path_1.default.join((0, runtime_homes_cjs_1.resolveGsdHome)(), '.gsd', 'defaults.json'), '~/.gsd/defaults.json');
158
158
  let projectConfig = null;
159
159
  if (targetDir) {
160
160
  const candidate = _findAncestorGsdConfigPath(targetDir);
@@ -1741,6 +1741,17 @@ function _installNativePluginIfDeclared(runtime, configDir, behaviors, src) {
1741
1741
  }
1742
1742
  }
1743
1743
  }
1744
+ // OpenCode 2.x terminal-client plugin (ADR-5331 Phase 3): a TUI entrypoint kept in its own directory under
1745
+ // plugins/. OpenCode 1.x scans plugins/*.js|ts only, so it never loads this directory.
1746
+ const cli = behaviors.nativeCliPlugin;
1747
+ if (cli && cli.source) {
1748
+ const cliSrc = node_path_1.default.join(src, cli.source);
1749
+ if (installFs().existsSync(cliSrc)) {
1750
+ const cliDest = runtimeArtifactInstallPlan.assertDestWithinConfigHome(configDir, node_path_1.default.join(cli.dir, cli.file));
1751
+ installFs().mkdirSync(node_path_1.default.dirname(cliDest), { recursive: true });
1752
+ installFs().copyFileSync(cliSrc, cliDest);
1753
+ }
1754
+ }
1744
1755
  }
1745
1756
  // ---------------------------------------------------------------------------
1746
1757
  // _migrateLegacyOpencodeCommandDir
@@ -34,6 +34,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  */
35
35
  const node_path_1 = __importDefault(require("node:path"));
36
36
  const node_os_1 = __importDefault(require("node:os"));
37
+ const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
37
38
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- install-fs-adapter.cjs is an export= CommonJS module
38
39
  const installFsAdapter = require("./install-fs-adapter.cjs");
39
40
  const { installFs } = installFsAdapter;
@@ -57,7 +58,7 @@ const { resolveTierEntry: gsdResolveTierEntry, resolveModelPolicy: gsdResolveMod
57
58
  */
58
59
  function readGsdGlobalModelOverrides(options = {}) {
59
60
  try {
60
- const home = options.homedir ? options.homedir() : node_os_1.default.homedir();
61
+ const home = (0, runtime_homes_cjs_1.resolveGsdHome)(options.homedir ? options.homedir() : node_os_1.default.homedir());
61
62
  const defaultsPath = node_path_1.default.join(home, '.gsd', 'defaults.json');
62
63
  if (!installFs().existsSync(defaultsPath))
63
64
  return null;
@@ -139,7 +140,7 @@ function readGsdAgentTools(config) {
139
140
  * model override resolver.
140
141
  */
141
142
  function readGsdEffectiveAgentTools(targetDir = null, options = {}) {
142
- const home = options.homedir ? options.homedir() : node_os_1.default.homedir();
143
+ const home = (0, runtime_homes_cjs_1.resolveGsdHome)(options.homedir ? options.homedir() : node_os_1.default.homedir());
143
144
  const globalConfig = _readGsdConfigFile(node_path_1.default.join(home, '.gsd', 'defaults.json'), 'global defaults');
144
145
  let projectConfig = null;
145
146
  if (targetDir) {
@@ -170,7 +171,7 @@ function readGsdEffectiveAgentTools(targetDir = null, options = {}) {
170
171
  * baked, letting the runtime's own default/session model govern).
171
172
  */
172
173
  function readGsdRuntimeProfileResolver(targetDir = null) {
173
- const homeDefaults = _readGsdConfigFile(node_path_1.default.join(node_os_1.default.homedir(), '.gsd', 'defaults.json'), '~/.gsd/defaults.json');
174
+ const homeDefaults = _readGsdConfigFile(node_path_1.default.join((0, runtime_homes_cjs_1.resolveGsdHome)(), '.gsd', 'defaults.json'), '~/.gsd/defaults.json');
174
175
  // #2875 defect fix (Generative Fix Divergence): same shared walk as
175
176
  // readGsdEffectiveModelOverrides above — see that call site's comment.
176
177
  let projectConfig = null;
@@ -31,6 +31,8 @@ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
31
31
  // #2322: reuse the existing pure path-containment seam (ADR-1239 Phase C-2)
32
32
  // instead of hand-rolling a new traversal check for capability skill stems.
33
33
  const external_descriptor_trust_cjs_1 = require("./external-descriptor-trust.cjs");
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- capability-ledger.cjs is an export= CommonJS module
35
+ const capabilityLedger = require("./capability-ledger.cjs");
34
36
  // eslint-disable-next-line @typescript-eslint/no-require-imports
35
37
  const conversionModule = require("./runtime-artifact-conversion.cjs");
36
38
  const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _processAttribution, normalizeAgentBodyForRuntime: _normalizeAgentBodyForRuntime, readGsdCommandNames: _readGsdCommandNames, deriveAgentName: _deriveAgentName, applyAgentFrontmatterExtensions: _applyAgentFrontmatterExtensions, appendAgentTools: _appendAgentTools, } = conversionModule;
@@ -748,9 +750,9 @@ function readInstalledCapabilitySkill(stem, registry) {
748
750
  // installed as a capability skill (epic #4636).
749
751
  if (node_fs_1.default.lstatSync(skillPath).isSymbolicLink())
750
752
  return null;
751
- if (!node_fs_1.default.statSync(skillPath).isFile())
752
- return null;
753
- return { capId, content: node_fs_1.default.readFileSync(skillPath, 'utf8') };
753
+ // #5121: the bounded regular-file read — a FIFO/device/oversized SKILL.md is skipped like a missing one.
754
+ const content = capabilityLedger.readSkillFile(skillPath);
755
+ return content === null ? null : { capId, content };
754
756
  }
755
757
  catch {
756
758
  return null; // missing / unreadable / corrupt entry -> skip
@@ -23,6 +23,10 @@
23
23
  * (design row #5 / "Not-corruption"). `kindsDiffer` on `ShadowReport` is what
24
24
  * lets `renderShadowReport` word the two cases correctly.
25
25
  *
26
+ * Same-scope pairs are not reported: a runtime whose layout installs `commands` and `skills` side by side at one
27
+ * scope (OpenCode, Kilo, Augment, CodeBuddy, ZCode) writes both on purpose, and `isGenuinelyShadowed` requires the
28
+ * winner and the loser to sit in different scopes (#5344).
29
+ *
26
30
  * ── Report, don't correct (mirrors the resolver's own law) ─────────────────
27
31
  * `mismatches` surfaces a declared runtime/scope that disagrees with the
28
32
  * probed one (Postel's Law, design doc: liberal in what is accepted, but the
@@ -218,6 +222,14 @@ function stemFromTrigger(trigger, prefix) {
218
222
  function isGenuinelyShadowed(t, scopeRecords, prefixFor) {
219
223
  if (t.shadowedBy === null)
220
224
  return false;
225
+ // This is a CROSS-SCOPE report (see the module comment). A winner and a loser in the SAME scope are the two
226
+ // kinds one install deliberately wrote side by side — OpenCode, Kilo, Augment, CodeBuddy and ZCode install
227
+ // `commands` and `skills` for every trigger at one scope. GSD wrote both, the operator cannot act on it, and
228
+ // the host's own dedupe (OpenCode 1.x lists the command and drops the same-named skill; 2.x keeps them on
229
+ // separate surfaces) is not a loss of reachability. Reporting it printed "72 triggers shadowed" on every
230
+ // install of those five runtimes (#5344).
231
+ if (t.shadowedBy.scope === t.scope)
232
+ return false;
221
233
  const winnerRecord = scopeRecords.get(t.shadowedBy.scope);
222
234
  const shadowedRecord = scopeRecords.get(t.scope);
223
235
  const winnerStem = stemFromTrigger(t.trigger, prefixFor(t.shadowedBy.scope, t.shadowedBy.kind));
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * Installer migration: stop the GSD MCP companion from duplicating every GSD command in OpenCode
4
+ * (#5338; epic #5331, ADR-5331 D6).
5
+ *
6
+ * OpenCode turns each MCP prompt into a `<server>:<prompt>` slash command. The GSD companion server
7
+ * advertised its whole prompt catalog, so with the command files the installer already writes every GSD
8
+ * command appeared twice (`gsd-plan-phase` and `gsd:plan-phase`; 143 entries against 72 observed on
9
+ * OpenCode 2.0.27). New installs register the server with `--no-prompts`. This migration converges an
10
+ * existing install by adding that flag to the `gsd` MCP entry GSD itself wrote — and only that entry: a
11
+ * `gsd` server the user edited or defined does not match the installer's shape and is never touched.
12
+ *
13
+ * It applies to OpenCode on both generations (the entry is the same shape), covers the 1.x `mcp.gsd` and
14
+ * the 2.x native `mcp.servers.gsd` locations, and edits the config as text so a commented `opencode.jsonc`
15
+ * keeps its comments and layout (#5334).
16
+ *
17
+ * Is the action safe in non-interactive install?
18
+ * Yes. `rewrite-json` is non-interactive and journaled; the rewrite is a one-token append to a command
19
+ * array the installer wrote.
20
+ *
21
+ * See docs/installer-migrations.md#shipped-migrations and the OpenCode row of
22
+ * docs/installer-migrations.md#runtime-configuration-contract-registry.
23
+ */
24
+ var __importDefault = (this && this.__importDefault) || function (mod) {
25
+ return (mod && mod.__esModule) ? mod : { "default": mod };
26
+ };
27
+ const node_fs_1 = __importDefault(require("node:fs"));
28
+ const node_path_1 = __importDefault(require("node:path"));
29
+ const jsonc_edit_cjs_1 = require("../jsonc-edit.cjs");
30
+ const opencode_mcp_entry_cjs_1 = require("../opencode-mcp-entry.cjs");
31
+ /** OpenCode reads `opencode.jsonc` in preference to `opencode.json` when both exist. */
32
+ const CONFIG_CANDIDATES = ['opencode.jsonc', 'opencode.json'];
33
+ function isPlainObject(v) {
34
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
35
+ }
36
+ const migration = {
37
+ id: '2026-10-12-opencode-mcp-no-prompts',
38
+ title: 'Stop the GSD MCP companion duplicating GSD commands in OpenCode',
39
+ description: 'Add --no-prompts to the GSD-written `gsd` MCP server entry in opencode.json(c) (mcp.gsd, and the ' +
40
+ 'OpenCode 2.x native mcp.servers.gsd) so OpenCode no longer lists every GSD command a second time as ' +
41
+ 'a `gsd:<name>` MCP-prompt command. Only the entry the installer wrote is changed; comments and ' +
42
+ 'layout in a JSONC config are preserved (#5338, #5334).',
43
+ introducedIn: '1.17.0',
44
+ runtimes: ['opencode'],
45
+ scopes: ['global', 'local'],
46
+ destructive: false,
47
+ runtimeContract: 'docs/installer-migrations.md#runtime-configuration-contract-registry OpenCode row',
48
+ plan: (ctx) => {
49
+ if (ctx.runtime !== 'opencode')
50
+ return [];
51
+ // The file OpenCode (and the installer's resolveOpencodeConfigPath) treats as THE config.
52
+ const relPath = CONFIG_CANDIDATES.find((name) => node_fs_1.default.existsSync(node_path_1.default.join(ctx.configDir, name)));
53
+ if (!relPath)
54
+ return [];
55
+ let config;
56
+ try {
57
+ config = (0, jsonc_edit_cjs_1.parseJsonc)(node_fs_1.default.readFileSync(node_path_1.default.join(ctx.configDir, relPath), 'utf8'));
58
+ }
59
+ catch {
60
+ return []; // unparseable: never touch a config we cannot read (same posture as the installer)
61
+ }
62
+ if (!isPlainObject(config) || !isPlainObject(config['mcp']))
63
+ return [];
64
+ const mcp = config['mcp'];
65
+ const edits = [];
66
+ const next = { ...config, mcp: { ...mcp } };
67
+ const nextMcp = next['mcp'];
68
+ const legacy = mcp['gsd'];
69
+ if ((0, opencode_mcp_entry_cjs_1.isGsdMcpEntry)(legacy) && !(0, opencode_mcp_entry_cjs_1.hasNoPromptsFlag)(legacy)) {
70
+ const updated = (0, opencode_mcp_entry_cjs_1.withNoPromptsFlag)(legacy);
71
+ nextMcp['gsd'] = updated;
72
+ edits.push({ path: ['mcp', 'gsd', 'command'], value: updated.command });
73
+ }
74
+ const servers = mcp['servers'];
75
+ if (isPlainObject(servers)) {
76
+ const native = servers['gsd'];
77
+ if ((0, opencode_mcp_entry_cjs_1.isGsdMcpEntry)(native) && !(0, opencode_mcp_entry_cjs_1.hasNoPromptsFlag)(native)) {
78
+ const updated = (0, opencode_mcp_entry_cjs_1.withNoPromptsFlag)(native);
79
+ nextMcp['servers'] = { ...servers, gsd: updated };
80
+ edits.push({ path: ['mcp', 'servers', 'gsd', 'command'], value: updated.command });
81
+ }
82
+ }
83
+ if (edits.length === 0)
84
+ return [];
85
+ return [
86
+ {
87
+ type: 'rewrite-json',
88
+ relPath,
89
+ value: next,
90
+ edits,
91
+ reason: 'the GSD MCP companion advertised its prompt catalog, which OpenCode lists as a second set of ' +
92
+ '`gsd:<name>` slash commands next to the installed command files',
93
+ ownershipEvidence: "the entry matches the exact shape configureOpencodePermissions writes: type 'local' and a command " +
94
+ "array of `npx … <package> gsd-mcp-server`; a user-edited or user-defined `gsd` server does not match",
95
+ },
96
+ ];
97
+ },
98
+ };
99
+ module.exports = migration;
@@ -18,6 +18,7 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
18
18
  const clock_cjs_1 = require("./clock.cjs");
19
19
  const install_scope_cjs_1 = require("./install-scope.cjs");
20
20
  const security_cjs_1 = require("./security.cjs");
21
+ const jsonc_edit_cjs_1 = require("./jsonc-edit.cjs");
21
22
  // #2874 (ADR-58 cleanup phase): this file is the ~1200-line migration
22
23
  // plan/apply/rollback/lock/journal engine — almost none of it is on the
23
24
  // installRuntimeArtifacts call tree. Only `readInstallManifest` and
@@ -293,15 +294,25 @@ function readInstallManifest(configDir) {
293
294
  scope: (0, install_scope_cjs_1.isInstallScopeId)(m.scope) ? m.scope : null,
294
295
  };
295
296
  }
297
+ function normalizeHostGeneration(raw) {
298
+ if (!raw || typeof raw !== 'object')
299
+ return undefined;
300
+ const r = raw;
301
+ if (r.runtime !== 'opencode' || (r.generation !== 1 && r.generation !== 2))
302
+ return undefined;
303
+ return { runtime: 'opencode', generation: r.generation, version: typeof r.version === 'string' ? r.version : null };
304
+ }
296
305
  function readInstallState(configDir) {
297
306
  const state = readJsonIfPresent(node_path_1.default.join(configDir, INSTALL_STATE_NAME), STRICT_JSON);
298
307
  if (!state || typeof state !== 'object') {
299
308
  return { schemaVersion: 1, appliedMigrations: [] };
300
309
  }
301
310
  const s = state;
311
+ const hostGeneration = normalizeHostGeneration(s.hostGeneration);
302
312
  return {
303
313
  schemaVersion: typeof s.schemaVersion === 'number' ? s.schemaVersion : 1,
304
314
  appliedMigrations: Array.isArray(s.appliedMigrations) ? s.appliedMigrations : [],
315
+ ...(hostGeneration ? { hostGeneration } : {}),
305
316
  };
306
317
  }
307
318
  // Strict atomic write for the install state: must never be left half-written.
@@ -654,6 +665,31 @@ function journalAction(action, status, extras = {}) {
654
665
  const { value: _value, ...safeAction } = action;
655
666
  return { ...safeAction, ...extras, status };
656
667
  }
668
+ /**
669
+ * The text a `rewrite-json` action writes. A file with JSONC syntax (comments, trailing commas, or a
670
+ * `.jsonc` name) is edited in place from the action's `edits`, so the user's annotations survive (#5334);
671
+ * everything else, and any edit the text editor cannot place, is written as the action's whole `value`.
672
+ */
673
+ function renderRewrittenJson(fullPath, action) {
674
+ if (Array.isArray(action.edits) && action.edits.length > 0) {
675
+ let original = null;
676
+ try {
677
+ original = node_fs_1.default.readFileSync(fullPath, 'utf8');
678
+ }
679
+ catch { /* fall through to the value rewrite */ }
680
+ if (original !== null && (fullPath.endsWith('.jsonc') || (0, jsonc_edit_cjs_1.hasJsoncFeatures)(original))) {
681
+ let text = original;
682
+ for (const edit of action.edits) {
683
+ text = (0, jsonc_edit_cjs_1.setJsoncValue)(text, edit.path, edit.value);
684
+ if (text === null)
685
+ break;
686
+ }
687
+ if (text !== null)
688
+ return text;
689
+ }
690
+ }
691
+ return JSON.stringify(action.value, null, 2) + '\n';
692
+ }
657
693
  function planInstallerMigrations({ configDir, runtime = null, scope = null, migrations, baselineScan = false, now = () => new Date().toISOString(), }) {
658
694
  if (!configDir)
659
695
  throw new Error('configDir is required');
@@ -725,6 +761,8 @@ function planInstallerMigrations({ configDir, runtime = null, scope = null, migr
725
761
  }
726
762
  if (action.type === 'rewrite-json') {
727
763
  action.value = rawAction.value;
764
+ if (Array.isArray(rawAction.edits))
765
+ action.edits = rawAction.edits;
728
766
  action.deleteIfEmpty = rawAction.deleteIfEmpty === true;
729
767
  }
730
768
  if (rawAction.prompt)
@@ -896,7 +934,7 @@ function applyInstallerMigrationPlan({ configDir, plan, now = () => new Date().t
896
934
  }));
897
935
  }
898
936
  else {
899
- (0, shell_command_projection_cjs_1.platformWriteSync)(fullPath, JSON.stringify(action.value, null, 2) + '\n');
937
+ (0, shell_command_projection_cjs_1.platformWriteSync)(fullPath, renderRewrittenJson(fullPath, action));
900
938
  journal.actions.push(journalAction(action, 'rewritten', {
901
939
  rollbackRelPath: node_path_1.default.posix.join(rollbackRootRelPath, normalized),
902
940
  }));
@@ -945,6 +983,7 @@ function applyInstallerMigrationPlan({ configDir, plan, now = () => new Date().t
945
983
  writeInstallState(configDir, {
946
984
  schemaVersion: 1,
947
985
  appliedMigrations: nextApplied,
986
+ ...(state.hostGeneration ? { hostGeneration: state.hostGeneration } : {}),
948
987
  });
949
988
  return {
950
989
  appliedMigrationIds: journal.appliedMigrationIds,
@@ -1017,6 +1056,7 @@ function markPendingMigrationsApplied({ configDir, plan, now = () => new Date().
1017
1056
  writeInstallState(configDir, {
1018
1057
  schemaVersion: 1,
1019
1058
  appliedMigrations: nextApplied,
1059
+ ...(state.hostGeneration ? { hostGeneration: state.hostGeneration } : {}),
1020
1060
  });
1021
1061
  }
1022
1062
  return newlyApplied;
@@ -1073,6 +1113,18 @@ function runInstallerMigrations({ configDir, runtime = null, scope = null, migra
1073
1113
  // Unused but kept to satisfy eslint — sleepSync is referenced in the original
1074
1114
  // and may be used by test code that patches this module.
1075
1115
  void sleepSync;
1116
+ /**
1117
+ * Record which OpenCode generation this install targets (ADR-5331 D2; #5337). Idempotent and
1118
+ * atomic; leaves the applied-migrations journal untouched.
1119
+ */
1120
+ function recordHostGeneration(configDir, info) {
1121
+ const state = readInstallState(configDir);
1122
+ const next = { runtime: 'opencode', generation: info.generation, version: info.version ?? null };
1123
+ const prev = state.hostGeneration;
1124
+ if (prev && prev.generation === next.generation && prev.version === next.version)
1125
+ return;
1126
+ writeInstallState(configDir, { ...state, hostGeneration: next });
1127
+ }
1076
1128
  module.exports = {
1077
1129
  DEFAULT_MIGRATIONS_DIR,
1078
1130
  INSTALL_MIGRATION_LOCK_NAME,
@@ -1089,6 +1141,7 @@ module.exports = {
1089
1141
  planInstallerMigrations,
1090
1142
  readInstallManifest,
1091
1143
  readInstallState,
1144
+ recordHostGeneration,
1092
1145
  runInstallerMigrations,
1093
1146
  writeInstallState,
1094
1147
  };