@opengsd/gsd-core 1.7.0-rc.1 → 1.7.0-rc.3
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.
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +711 -0
- package/GEMINI.md +25 -23
- package/README.md +3 -3
- package/agents/gsd-advisor-researcher.md +3 -1
- package/agents/gsd-ai-researcher.md +2 -2
- package/agents/gsd-assumptions-analyzer.md +2 -0
- package/agents/gsd-code-fixer.md +3 -1
- package/agents/gsd-code-reviewer.md +2 -0
- package/agents/gsd-codebase-mapper.md +2 -0
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +3 -1
- package/agents/gsd-doc-writer.md +2 -0
- package/agents/gsd-domain-researcher.md +1 -1
- package/agents/gsd-eval-auditor.md +3 -1
- package/agents/gsd-executor.md +12 -9
- package/agents/gsd-integration-checker.md +2 -0
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +1 -1
- package/agents/gsd-nyquist-auditor.md +2 -0
- package/agents/gsd-phase-researcher.md +4 -2
- package/agents/gsd-plan-checker.md +3 -1
- package/agents/gsd-planner.md +4 -2
- package/agents/gsd-project-researcher.md +4 -2
- package/agents/gsd-research-synthesizer.md +3 -1
- package/agents/gsd-roadmapper.md +2 -0
- package/agents/gsd-security-auditor.md +2 -0
- package/agents/gsd-ui-auditor.md +2 -0
- package/agents/gsd-ui-checker.md +2 -0
- package/agents/gsd-ui-researcher.md +4 -2
- package/agents/gsd-verifier.md +6 -4
- package/bin/install.js +226 -459
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +9 -5
- package/commands/gsd/next.md +29 -0
- package/commands/gsd/ns-workflow.md +5 -2
- package/gsd-core/bin/ensure-runtime-build.cjs +246 -0
- package/gsd-core/bin/gsd-tools.cjs +43 -8
- package/gsd-core/bin/lib/capability-loader.cjs +27 -9
- package/gsd-core/bin/lib/capability-registry.cjs +209 -205
- package/gsd-core/bin/lib/capability-source.cjs +22 -7
- package/gsd-core/bin/lib/capability-validator.cjs +24 -2
- package/gsd-core/bin/lib/check-command-router.cjs +111 -1
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/commands.cjs +2 -1
- package/gsd-core/bin/lib/config.cjs +19 -1
- package/gsd-core/bin/lib/core-utils.cjs +41 -0
- package/gsd-core/bin/lib/external-job.cjs +287 -0
- package/gsd-core/bin/lib/frontmatter.cjs +53 -6
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +151 -0
- package/gsd-core/bin/lib/graphify.cjs +41 -9
- package/gsd-core/bin/lib/handshake-serialized.cjs +70 -0
- package/gsd-core/bin/lib/host-integration-sdk.cjs +53 -0
- package/gsd-core/bin/lib/host-integration.cjs +61 -0
- package/gsd-core/bin/lib/init.cjs +34 -6
- package/gsd-core/bin/lib/milestone.cjs +56 -11
- package/gsd-core/bin/lib/phase-id.cjs +18 -0
- package/gsd-core/bin/lib/phase.cjs +51 -29
- package/gsd-core/bin/lib/phases-command-router.cjs +4 -3
- package/gsd-core/bin/lib/plan-scan.cjs +9 -1
- package/gsd-core/bin/lib/probe-core.cjs +44 -4
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +21 -19
- package/gsd-core/bin/lib/roadmap-command-router.cjs +3 -2
- package/gsd-core/bin/lib/roadmap-parser.cjs +21 -11
- package/gsd-core/bin/lib/roadmap.cjs +45 -22
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +18 -13
- package/gsd-core/bin/lib/runtime-name-policy.cjs +8 -10
- package/gsd-core/bin/lib/shell-command-projection.cjs +12 -8
- package/gsd-core/bin/lib/smart-entry.cjs +521 -0
- package/gsd-core/bin/lib/state-transition.cjs +15 -0
- package/gsd-core/bin/lib/state.cjs +27 -8
- package/gsd-core/bin/lib/surface.cjs +18 -9
- package/gsd-core/bin/lib/update-context.cjs +0 -5
- package/gsd-core/bin/lib/validate.cjs +2 -1
- package/gsd-core/bin/lib/verify.cjs +6 -4
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +12 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +28 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -13
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +0 -5
- package/gsd-core/references/agent-skills-bootstrap.md +60 -0
- package/gsd-core/references/gates.md +1 -1
- package/gsd-core/references/model-profiles.md +27 -0
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +1 -1
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +23 -25
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +1 -1
- package/gsd-core/workflows/complete-milestone.md +7 -11
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/auto.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +1 -1
- package/gsd-core/workflows/docs-update.md +1 -1
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase.md +5 -6
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/forensics.md +4 -4
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/full.md +15 -1
- package/gsd-core/workflows/help/modes/topic.md +1 -0
- package/gsd-core/workflows/import.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +1 -1
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +4 -4
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +7 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-milestone-gaps.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +42 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +102 -0
- package/gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md +23 -0
- package/gsd-core/workflows/plan-phase.md +12 -160
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick.md +1 -1
- package/gsd-core/workflows/reapply-patches.md +1 -1
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +50 -9
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +13 -11
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +1 -1
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +120 -0
- package/gsd-core/workflows/spec-phase.md +8 -6
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +1 -1
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/update.md +3 -6
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +63 -6
- package/package.json +5 -5
- package/scripts/ci-test-scope.cjs +19 -1
- package/scripts/generate-package-identity.cjs +27 -2
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -148
- package/scripts/lint-regression-test-names.allowlist.json +1 -273
- package/scripts/lint-test-file-count.allowlist.json +7 -91
- package/scripts/research-profiles.cjs +6 -6
- package/scripts/run-tests.cjs +67 -4
- package/scripts/slurm-adapter.cjs +269 -0
- package/scripts/sync-manifest-versions.cjs +67 -14
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +9 -5
- package/skills/gsd-next/SKILL.md +30 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -1
- package/skills/gsd-review/SKILL.md +6 -0
- package/gemini-extension.json +0 -6
|
@@ -56,7 +56,7 @@ On any error or timeout, stop and let the phase continue -- capture is best-effo
|
|
|
56
56
|
|
|
57
57
|
1. **Dedup first.** Interactive: `mempalace_check_duplicate` on the artifact's deterministic drawer id. Headless: rely on `mempalace mine`'s content-hash idempotency.
|
|
58
58
|
2. **Add the drawer (verbatim).** File the exact artifact text into `room: <room>` of `wing: <wing>` with provenance (`source_file`, phase id). Interactive: `mempalace_add_drawer`. Headless: `mempalace mine <path> --wing <wing> --room <room>`.
|
|
59
|
-
3. **Mirror KG facts** when `config.mempalace.mirror_kg` is true: extract decision/delivery facts and `mempalace_kg_add` them with `valid_from` = the phase date (e.g. `(<project>, decided, <decision>)` from CONTEXT; `(<phase>, delivered, <capability>)` from SUMMARY).
|
|
59
|
+
3. **Mirror KG facts** when `config.mempalace.mirror_kg` is true: extract decision/delivery facts and `mempalace_kg_add` them with `valid_from` = the phase date (e.g. `(<project>, decided, <decision>)` from CONTEXT; `(<phase>, delivered, <capability>)` from SUMMARY). Under `augment` these are an *additive* mirror of GSD's native `.planning/graphs/`. Under `kg_backend`/`replace` the palace KG is the *authoritative* fact store — GSD still produces `.planning/graphs/` through its normal graphify, so an unreachable palace never loses a fact.
|
|
60
60
|
4. Re-running a phase MUST NOT create duplicate drawers (deterministic ids + `check_duplicate`).
|
|
61
61
|
|
|
62
62
|
## Step 4 -- Report
|
|
@@ -48,13 +48,17 @@ This step is `onError: skip` at `plan:pre` -- recall never blocks planning.
|
|
|
48
48
|
## Step 2 -- Resolve wing, mode, and transport
|
|
49
49
|
|
|
50
50
|
1. **Wing.** Use `config.mempalace.wing` if non-empty; otherwise derive from `config.project_code`; otherwise fall back to the repository directory name.
|
|
51
|
-
2. **Mode.** Read `config.mempalace.memory_mode` (`augment` | `kg_backend` | `replace`, default `augment`).
|
|
51
|
+
2. **Mode.** Read `config.mempalace.memory_mode` (`augment` | `kg_backend` | `replace`, default `augment`). It sets how authoritative the palace is during recall:
|
|
52
|
+
- `augment` — the palace is an *additional* layer; read native memory (`.planning/graphs/`, STATE) too and treat the palace as supplementary.
|
|
53
|
+
- `kg_backend` — for knowledge-graph facts, query the palace's temporal KG *first, as the primary source*; fall back to `.planning/graphs/` when the palace is unreachable. Non-KG drawer recall stays additive.
|
|
54
|
+
- `replace` — resolve recall *through the palace as the source of truth*; consult native artifacts only as a fallback when the palace is unreachable.
|
|
55
|
+
In every mode an unreachable palace degrades to native memory — recall never blocks (`onError: skip`).
|
|
52
56
|
3. **Transport.** Prefer the **MCP tools** (`mempalace_*`) in interactive runs *when your MemPalace MCP server is registered and your runtime permits those tools*. Otherwise — headless/cron/autonomous runs, or runtimes that don't grant the MemPalace MCP tools — use the **CLI** (`mempalace wake-up`, `mempalace search`), which this skill's `Bash` allow-tool always covers. If neither is reachable, go to Step 4.
|
|
53
57
|
4. **Topic.** Read the phase `CONTEXT.md` (the consumed artifact). Derive a short search query from its title, goal, and key decisions.
|
|
54
58
|
|
|
55
59
|
## Step 3 -- Retrieve (read-only)
|
|
56
60
|
|
|
57
|
-
All calls in this step are side-effect-free. On any error or timeout, stop retrieving and write whatever was gathered (or the stub) -- never raise.
|
|
61
|
+
All calls in this step are side-effect-free. On any error or timeout, stop retrieving and write whatever was gathered (or the stub) -- never raise. This skill reads only the palace; GSD's planner reads native memory (`.planning/graphs/`, STATE) regardless. So under `kg_backend`/`replace` an unreachable palace falls back to that native memory automatically — reflect that in the stub (Step 4) rather than implying memory is gone.
|
|
58
62
|
|
|
59
63
|
1. **Wake up** (cheap, ~600--900 tokens):
|
|
60
64
|
- Interactive: read the wing identity/summary, then `mempalace_search`.
|
|
@@ -62,7 +66,7 @@ All calls in this step are side-effect-free. On any error or timeout, stop retri
|
|
|
62
66
|
2. **Targeted search:**
|
|
63
67
|
- Interactive: `mempalace_search(query=<topic>, wing=<wing>)`.
|
|
64
68
|
- Headless: `mempalace search "<topic>" --wing <wing>`.
|
|
65
|
-
3. **Knowledge-graph facts** (when `config.mempalace.mirror_kg` is true): `mempalace_kg_query` / `mempalace_kg_timeline` for decisions relevant to the topic and their validity windows.
|
|
69
|
+
3. **Knowledge-graph facts** (when `config.mempalace.mirror_kg` is true): `mempalace_kg_query` / `mempalace_kg_timeline` for decisions relevant to the topic and their validity windows. Under `augment` the palace KG *supplements* GSD's native `.planning/graphs/` — combine both, do not treat the palace as the sole source. Under `kg_backend` or `replace` the palace KG is the *primary* graph source — query it first and use `.planning/graphs/` only as a fallback when the palace is unreachable.
|
|
66
70
|
4. **Dedup** the returned drawers/facts; keep the top results.
|
|
67
71
|
|
|
68
72
|
## Step 4 -- Write MEMORY-RECALL.md
|
|
@@ -86,12 +90,12 @@ _Wing: <wing> · Mode: <mode> · Transport: <mcp|cli>_
|
|
|
86
90
|
- <surprise> — <provenance>
|
|
87
91
|
```
|
|
88
92
|
|
|
89
|
-
When MemPalace is unreachable, write the stub and continue:
|
|
93
|
+
When MemPalace is unreachable, write the stub and continue. Under `kg_backend`/`replace`, name the native fallback so the planner knows memory is not gone — only un-augmented by the palace this run:
|
|
90
94
|
|
|
91
95
|
```markdown
|
|
92
96
|
# Memory Recall (MemPalace)
|
|
93
97
|
|
|
94
|
-
_MemPalace unavailable at recall time —
|
|
98
|
+
_MemPalace unavailable at recall time — falling back to GSD native memory (`.planning/graphs/`, STATE). No palace recall this run._
|
|
95
99
|
```
|
|
96
100
|
|
|
97
101
|
## Anti-Patterns
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsd:next
|
|
3
|
+
description: Smart entry — detect project state and route to the right next GSD action.
|
|
4
|
+
effort: low
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
- Glob
|
|
9
|
+
- SlashCommand
|
|
10
|
+
- AskUserQuestion
|
|
11
|
+
---
|
|
12
|
+
<objective>
|
|
13
|
+
GSD smart entry — the state-aware front door. Detect what's going on in this project, then present a short menu of the right next actions and dispatch to one.
|
|
14
|
+
|
|
15
|
+
This is a launcher/router only. It never does the work itself. It reads project + workflow state via `gsd-tools smart-entry --json`, shows a situation-appropriate menu, and hands off to an existing GSD command.
|
|
16
|
+
</objective>
|
|
17
|
+
|
|
18
|
+
<execution_context>
|
|
19
|
+
@~/.claude/gsd-core/workflows/smart-entry.md
|
|
20
|
+
@~/.claude/gsd-core/references/ui-brand.md
|
|
21
|
+
</execution_context>
|
|
22
|
+
|
|
23
|
+
<context>
|
|
24
|
+
Arguments: $ARGUMENTS
|
|
25
|
+
</context>
|
|
26
|
+
|
|
27
|
+
<process>
|
|
28
|
+
Follow ~/.claude/gsd-core/workflows/smart-entry.md. Detect the situation, present the menu, and dispatch exactly one command. Then stop.
|
|
29
|
+
</process>
|
|
@@ -5,13 +5,15 @@ argument-hint: ""
|
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Skill
|
|
8
|
-
requires: [discuss-phase, spec-phase, plan-phase, execute-phase, verify-work, phase, progress, ultraplan-phase, plan-review-convergence, add-tests, ai-integration-phase, autonomous, fast, mvp-phase, quick]
|
|
8
|
+
requires: [discuss-phase, spec-phase, plan-phase, execute-phase, verify-work, phase, progress, next, ultraplan-phase, plan-review-convergence, add-tests, ai-integration-phase, autonomous, fast, mvp-phase, quick]
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
Route to the appropriate phase-pipeline skill based on the user's intent.
|
|
12
12
|
Sub-skill names below are post-#2790 consolidated targets — `gsd-phase`
|
|
13
13
|
absorbs the former add/insert/remove/edit-phase commands and `gsd-progress`
|
|
14
|
-
absorbs the former next/do commands.
|
|
14
|
+
absorbs the former next/do workflow-advance commands. The reclaimed
|
|
15
|
+
`gsd-next` target is the state-aware smart-entry launcher, not the retired
|
|
16
|
+
workflow-advance command.
|
|
15
17
|
|
|
16
18
|
| User wants | Invoke |
|
|
17
19
|
|---|---|
|
|
@@ -22,6 +24,7 @@ absorbs the former next/do commands.
|
|
|
22
24
|
| Verify built features through UAT | gsd-verify-work |
|
|
23
25
|
| Add / insert / remove / edit a phase | gsd-phase |
|
|
24
26
|
| Advance to the next logical step | gsd-progress |
|
|
27
|
+
| Open the state-aware smart-entry launcher | gsd-next |
|
|
25
28
|
| Offload planning to the ultraplan cloud | gsd-ultraplan-phase |
|
|
26
29
|
| Cross-AI plan review convergence loop | gsd-plan-review-convergence |
|
|
27
30
|
| Generate tests for a completed phase | gsd-add-tests |
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Self-healing runtime build (#2002).
|
|
5
|
+
*
|
|
6
|
+
* The GSD runtime CLI (gsd-tools.cjs) require()s ~150 compiled modules from
|
|
7
|
+
* ./lib/*.cjs. Per ADR-457 ("build-at-publish") those are build artifacts:
|
|
8
|
+
* compiled from src/*.cts by `npm run build:lib` (tsc -p tsconfig.build.json),
|
|
9
|
+
* gitignored, and shipped prebuilt in the npm tarball via the prepack /
|
|
10
|
+
* prepublishOnly lifecycle scripts.
|
|
11
|
+
*
|
|
12
|
+
* The Claude Code plugin-marketplace channel does NOT go through `npm publish`
|
|
13
|
+
* or bin/install.js. Claude Code materializes the git tag tree into its plugin
|
|
14
|
+
* cache and at most runs `npm install --ignore-scripts`, so neither `prepare`
|
|
15
|
+
* nor `build:lib` ever fires. The compiled ./lib/*.cjs therefore never exist on
|
|
16
|
+
* that path and every CLI command dies at module load with
|
|
17
|
+
* `Cannot find module './lib/cli-exit.cjs'`.
|
|
18
|
+
*
|
|
19
|
+
* This module heals that: before gsd-tools.cjs require()s ./lib, if the compiled
|
|
20
|
+
* output is absent it invokes tsc once — lock-guarded so the many parallel
|
|
21
|
+
* gsd-tools shell-outs a workflow performs do not race the build — then lets the
|
|
22
|
+
* requires proceed. On the npm path the artifacts already exist, so the common
|
|
23
|
+
* case is a single fs.existsSync check and a no-op.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately depends on nothing under ./lib (that tree is precisely what may
|
|
26
|
+
* be missing) — only on Node built-ins.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const fs = require('fs');
|
|
30
|
+
const path = require('path');
|
|
31
|
+
const { spawnSync } = require('child_process');
|
|
32
|
+
|
|
33
|
+
// The first module gsd-tools.cjs require()s, and a gitignored build artifact
|
|
34
|
+
// (so it is absent on a raw git-tag checkout even though ~11 other bin/lib/*.cjs
|
|
35
|
+
// are git-tracked). tsconfig.build.json sets noEmitOnError:true, so tsc is
|
|
36
|
+
// all-or-nothing: if this file exists, the whole tree was emitted.
|
|
37
|
+
const SENTINEL = 'cli-exit.cjs';
|
|
38
|
+
|
|
39
|
+
/** Directory holding the compiled ./lib/*.cjs, relative to this file. */
|
|
40
|
+
function defaultLibDir() {
|
|
41
|
+
return path.join(__dirname, 'lib');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Package root (holds tsconfig.build.json + node_modules). This file lives at
|
|
46
|
+
* <root>/gsd-core/bin/ensure-runtime-build.cjs, so the root is two levels up —
|
|
47
|
+
* true for both the dev repo layout and the marketplace plugin-cache checkout
|
|
48
|
+
* (…/<version>/gsd-core/bin/ensure-runtime-build.cjs).
|
|
49
|
+
*/
|
|
50
|
+
function defaultPackageRoot() {
|
|
51
|
+
return path.resolve(__dirname, '..', '..');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the tsc entry SCRIPT (not the .bin/tsc shim). We run it as
|
|
56
|
+
* `node <tsc.js>` so behaviour is identical on POSIX and Windows and does not
|
|
57
|
+
* depend on a shell or on the .cmd shim. Returns null when TypeScript is not
|
|
58
|
+
* installed under packageRoot.
|
|
59
|
+
*/
|
|
60
|
+
function resolveTscScript(packageRoot) {
|
|
61
|
+
try {
|
|
62
|
+
return require.resolve('typescript/bin/tsc', { paths: [packageRoot] });
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isBuilt(libDir) {
|
|
69
|
+
return fs.existsSync(path.join(libDir, SENTINEL));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Synchronous sleep with no dependencies, portable across platforms. */
|
|
73
|
+
function sleepSync(ms) {
|
|
74
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, Math.max(0, ms));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Raised when the runtime library is missing and cannot be auto-built. The
|
|
79
|
+
* message is user-facing and actionable; callers print `.message` (not a stack).
|
|
80
|
+
*/
|
|
81
|
+
class RuntimeBuildError extends Error {
|
|
82
|
+
constructor(message) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.name = 'RuntimeBuildError';
|
|
85
|
+
this.code = 'GSD_RUNTIME_BUILD_FAILED';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Ensure the compiled ./lib/*.cjs exist, building them once if absent.
|
|
91
|
+
*
|
|
92
|
+
* Idempotent and safe to call concurrently from many processes. When another
|
|
93
|
+
* process holds the build lock, this one waits for the build to finish rather
|
|
94
|
+
* than launching a competing tsc.
|
|
95
|
+
*
|
|
96
|
+
* @param {object} [opts]
|
|
97
|
+
* @param {string} [opts.libDir] Override the compiled-output directory.
|
|
98
|
+
* @param {string} [opts.packageRoot] Override the tsconfig/node_modules root.
|
|
99
|
+
* @param {string|null} [opts.tscScript] Override tsc resolution (null = "absent").
|
|
100
|
+
* @param {function} [opts.spawn] Override spawnSync (for tests).
|
|
101
|
+
* @param {function} [opts.log] Override the progress logger.
|
|
102
|
+
* @param {number} [opts.waitTimeoutMs] Max wait for a peer build (default 120s).
|
|
103
|
+
* @param {number} [opts.pollMs] Poll interval while waiting (default 100ms).
|
|
104
|
+
* @param {function} [opts.onPoll] Test hook invoked before each wait-poll.
|
|
105
|
+
* @returns {{built: true, healed: boolean, waited?: boolean}}
|
|
106
|
+
* @throws {RuntimeBuildError} when the library is absent and cannot be built.
|
|
107
|
+
*/
|
|
108
|
+
function ensureRuntimeBuild(opts = {}) {
|
|
109
|
+
const libDir = opts.libDir || defaultLibDir();
|
|
110
|
+
const packageRoot = opts.packageRoot || defaultPackageRoot();
|
|
111
|
+
const spawn = opts.spawn || spawnSync;
|
|
112
|
+
const log = opts.log || ((m) => process.stderr.write(m + '\n'));
|
|
113
|
+
const waitTimeoutMs = opts.waitTimeoutMs != null ? opts.waitTimeoutMs : 120000;
|
|
114
|
+
const pollMs = opts.pollMs != null ? opts.pollMs : 100;
|
|
115
|
+
|
|
116
|
+
// Fast path: already built (the npm-registry install and every subsequent run).
|
|
117
|
+
if (isBuilt(libDir)) return { built: true, healed: false };
|
|
118
|
+
|
|
119
|
+
const tsconfig = path.join(packageRoot, 'tsconfig.build.json');
|
|
120
|
+
if (!fs.existsSync(tsconfig)) {
|
|
121
|
+
throw new RuntimeBuildError(
|
|
122
|
+
'GSD runtime library is not built and cannot be auto-built: ' +
|
|
123
|
+
`${tsconfig} not found. Run \`npm run build:lib\` in the gsd-core package.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const tscScript =
|
|
128
|
+
opts.tscScript !== undefined ? opts.tscScript : resolveTscScript(packageRoot);
|
|
129
|
+
if (!tscScript) {
|
|
130
|
+
throw new RuntimeBuildError(
|
|
131
|
+
`GSD runtime library is not built (missing ${path.join(libDir, SENTINEL)}) ` +
|
|
132
|
+
'and TypeScript is unavailable to build it. Run ' +
|
|
133
|
+
`\`npm install && npm run build:lib\` in the gsd-core package (${packageRoot}).`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// libDir may not exist yet on a totally fresh tree; create it so the lock and
|
|
138
|
+
// tsc output have a home.
|
|
139
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
140
|
+
const lockDir = path.join(libDir, '.build.lock');
|
|
141
|
+
|
|
142
|
+
let haveLock = acquireLock(lockDir);
|
|
143
|
+
if (!haveLock) {
|
|
144
|
+
// A peer process is building. Wait for the sentinel rather than racing tsc.
|
|
145
|
+
const deadline = Date.now() + waitTimeoutMs;
|
|
146
|
+
while (!isBuilt(libDir)) {
|
|
147
|
+
if (typeof opts.onPoll === 'function') opts.onPoll();
|
|
148
|
+
if (isBuilt(libDir)) break;
|
|
149
|
+
if (Date.now() > deadline) break; // peer wedged/crashed — take over below
|
|
150
|
+
sleepSync(pollMs);
|
|
151
|
+
}
|
|
152
|
+
if (isBuilt(libDir)) return { built: true, healed: true, waited: true };
|
|
153
|
+
// Peer never finished; try to take over the (stale) lock.
|
|
154
|
+
haveLock = acquireLock(lockDir);
|
|
155
|
+
if (!haveLock && !isBuilt(libDir)) {
|
|
156
|
+
throw new RuntimeBuildError(
|
|
157
|
+
`GSD runtime build did not complete within ${waitTimeoutMs}ms and the ` +
|
|
158
|
+
`build lock (${lockDir}) is held by another process. Remove it and run ` +
|
|
159
|
+
'`npm run build:lib` if this persists.',
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
if (isBuilt(libDir)) return { built: true, healed: true, waited: true };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
log('gsd: runtime library not built — compiling once (tsc -p tsconfig.build.json)…');
|
|
167
|
+
// Heal only runs when the build is genuinely absent/broken, so force a full
|
|
168
|
+
// emit: a stale incremental cache from a partial build would make tsc report
|
|
169
|
+
// success without re-emitting the (still-missing) sentinel.
|
|
170
|
+
forceFullEmit(packageRoot, tsconfig);
|
|
171
|
+
const res = spawn(process.execPath, [tscScript, '-p', tsconfig], {
|
|
172
|
+
cwd: packageRoot,
|
|
173
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
174
|
+
encoding: 'utf8',
|
|
175
|
+
});
|
|
176
|
+
if (!res || res.status !== 0) {
|
|
177
|
+
const detail = ((res && (res.stderr || res.stdout)) || '').toString().trim();
|
|
178
|
+
throw new RuntimeBuildError(
|
|
179
|
+
`GSD runtime build failed (tsc exit ${res ? res.status : 'unknown'}). ` +
|
|
180
|
+
`Run \`npm run build:lib\` in ${packageRoot} to see the error.` +
|
|
181
|
+
(detail ? `\n${detail}` : ''),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
} finally {
|
|
185
|
+
releaseLock(lockDir);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!isBuilt(libDir)) {
|
|
189
|
+
throw new RuntimeBuildError(
|
|
190
|
+
`GSD runtime build ran but ${path.join(libDir, SENTINEL)} is still missing. ` +
|
|
191
|
+
`Run \`npm run build:lib\` in ${packageRoot}.`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return { built: true, healed: true };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Delete tsc's incremental build cache so the heal re-emits every module. The
|
|
199
|
+
* cache path defaults to tsconfig.build.tsbuildinfo (per tsconfig.build.json)
|
|
200
|
+
* but is read from the config when overridden. Best-effort: a missing or
|
|
201
|
+
* unreadable config just falls back to the default and a missing cache is fine.
|
|
202
|
+
*/
|
|
203
|
+
function forceFullEmit(packageRoot, tsconfigPath) {
|
|
204
|
+
let rel = 'tsconfig.build.tsbuildinfo';
|
|
205
|
+
try {
|
|
206
|
+
const cfg = JSON.parse(fs.readFileSync(tsconfigPath, 'utf8'));
|
|
207
|
+
if (cfg && cfg.compilerOptions && cfg.compilerOptions.tsBuildInfoFile) {
|
|
208
|
+
rel = cfg.compilerOptions.tsBuildInfoFile;
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
/* unreadable/JSONC config — use the default cache path */
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
fs.rmSync(path.resolve(packageRoot, rel), { force: true });
|
|
215
|
+
} catch {
|
|
216
|
+
/* best effort */
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Atomically acquire the build lock. Returns true on success. */
|
|
221
|
+
function acquireLock(lockDir) {
|
|
222
|
+
try {
|
|
223
|
+
fs.mkdirSync(lockDir); // atomic — throws EEXIST if a peer holds it
|
|
224
|
+
return true;
|
|
225
|
+
} catch (e) {
|
|
226
|
+
if (e && e.code === 'EEXIST') return false;
|
|
227
|
+
throw e;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Best-effort lock release. */
|
|
232
|
+
function releaseLock(lockDir) {
|
|
233
|
+
try {
|
|
234
|
+
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
235
|
+
} catch {
|
|
236
|
+
/* best effort — a leftover lock is recovered by the wait-then-takeover path */
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = {
|
|
241
|
+
ensureRuntimeBuild,
|
|
242
|
+
resolveTscScript,
|
|
243
|
+
isBuilt,
|
|
244
|
+
RuntimeBuildError,
|
|
245
|
+
SENTINEL,
|
|
246
|
+
};
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
* Milestone Operations:
|
|
58
58
|
* milestone complete <version> Archive milestone, create MILESTONES.md
|
|
59
59
|
* [--name <name>]
|
|
60
|
-
* [--archive-phases]
|
|
60
|
+
* [--no-archive-phases] Skip moving phase dirs to milestones/vX.Y-phases/ (archived by default)
|
|
61
61
|
*
|
|
62
62
|
* User Story Validation:
|
|
63
63
|
* user-story validate --story "..." Validate "As a / I want to / so that" format
|
|
@@ -195,6 +195,24 @@
|
|
|
195
195
|
|
|
196
196
|
const fs = require('fs');
|
|
197
197
|
const path = require('path');
|
|
198
|
+
|
|
199
|
+
// #2002 — self-healing runtime build. The compiled ./lib/*.cjs modules this
|
|
200
|
+
// entrypoint require()s below are gitignored build artifacts (ADR-457), shipped
|
|
201
|
+
// prebuilt in the npm tarball. The Claude Code plugin-marketplace channel never
|
|
202
|
+
// runs `npm run build:lib` or bin/install.js, so on that path they can be
|
|
203
|
+
// absent and every command dies at module load. Compile them once (lock-guarded,
|
|
204
|
+
// idempotent, a no-op when already built) before the ./lib requires run.
|
|
205
|
+
const { ensureRuntimeBuild } = require('./ensure-runtime-build.cjs');
|
|
206
|
+
try {
|
|
207
|
+
ensureRuntimeBuild();
|
|
208
|
+
} catch (bootErr) {
|
|
209
|
+
process.stderr.write((bootErr && bootErr.message ? bootErr.message : String(bootErr)) + '\n');
|
|
210
|
+
// Fatal bootstrap failure before the CLI's ExitError/runMain machinery (which
|
|
211
|
+
// lives in ./lib) is available to load, so a direct exit is the only option.
|
|
212
|
+
// eslint-disable-next-line n/no-process-exit
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
|
|
198
216
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
199
217
|
const io = require('./lib/io.cjs');
|
|
200
218
|
const { error, ERROR_REASON, setJsonErrorMode, output } = io;
|
|
@@ -259,6 +277,7 @@ const { routePhasesCommand } = require('./lib/phases-command-router.cjs');
|
|
|
259
277
|
const { routeValidateCommand } = require('./lib/validate-command-router.cjs');
|
|
260
278
|
const { routeRoadmapCommand } = require('./lib/roadmap-command-router.cjs');
|
|
261
279
|
const { routeAgentCommand } = require('./lib/agent-command-router.cjs');
|
|
280
|
+
const smartEntryMod = require('./lib/smart-entry.cjs');
|
|
262
281
|
const { routeCheckCommand } = require('./lib/check-command-router.cjs');
|
|
263
282
|
const { routeTaskCommand } = require('./lib/task-command-router.cjs');
|
|
264
283
|
const { parseNamedArgs, parseMultiwordArg } = require('./lib/command-arg-projection.cjs');
|
|
@@ -665,7 +684,7 @@ async function main() {
|
|
|
665
684
|
'from-gsd2, frontmatter, gap-analysis, generate-claude-md, generate-claude-profile, ' +
|
|
666
685
|
'generate-dev-preferences, generate-slug, graphify, history-digest, init, intel, ' +
|
|
667
686
|
'capability, classify-confidence, git, learnings, list-seeds, list-todos, loop, milestone, package-legitimacy, phase, phase-plan-index, phases, profile-questionnaire, ' +
|
|
668
|
-
'profile-sample, progress, project-instruction-file, prompt-budget, requirements, research-plan, research-store, resolve-granularity, resolve-model, roadmap, scaffold, state, ' +
|
|
687
|
+
'profile-sample, progress, project-instruction-file, prompt-budget, requirements, research-plan, research-store, resolve-granularity, resolve-model, roadmap, scaffold, smart-entry, state, ' +
|
|
669
688
|
'task, template, user-story, validate, verify, verify-path-exists, verify-summary, eval, workstream, worktree\n\n' +
|
|
670
689
|
'Global flags:\n' +
|
|
671
690
|
' --raw Emit raw output without post-processing\n' +
|
|
@@ -833,6 +852,11 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
833
852
|
break;
|
|
834
853
|
}
|
|
835
854
|
|
|
855
|
+
case 'smart-entry': {
|
|
856
|
+
smartEntryMod.runSmartEntry(cwd, args, raw);
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
|
|
836
860
|
case 'check': {
|
|
837
861
|
routeCheckCommand({ args, cwd, raw });
|
|
838
862
|
break;
|
|
@@ -1453,7 +1477,9 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1453
1477
|
const subcommand = args[1];
|
|
1454
1478
|
if (subcommand === 'complete') {
|
|
1455
1479
|
const milestoneName = parseMultiwordArg(args, 'name');
|
|
1456
|
-
|
|
1480
|
+
// #1871: archive phase dirs by default on milestone complete so the next
|
|
1481
|
+
// new-milestone never inherits un-archived dirs. --no-archive-phases opts out.
|
|
1482
|
+
const archivePhases = !args.includes('--no-archive-phases');
|
|
1457
1483
|
const force = args.includes('--force');
|
|
1458
1484
|
milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force }, raw);
|
|
1459
1485
|
} else {
|
|
@@ -1665,13 +1691,22 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1665
1691
|
return undefined;
|
|
1666
1692
|
};
|
|
1667
1693
|
// Running GSD version (hard gate for engines.gsd at install/load); fail-closed to 0.0.0.
|
|
1694
|
+
// #1920: prefer the authoritative gsd-core/VERSION the installer writes for EVERY runtime
|
|
1695
|
+
// (gsd-core/bin/ -> ../VERSION), so installed layouts report the true version even when the
|
|
1696
|
+
// walked-up ../../package.json is the versionless CommonJS marker or the user's own project.
|
|
1697
|
+
// Fall back to the runtime-root package.json (dev/source tree), then fail-closed. Mirrors
|
|
1698
|
+
// readHostVersion() in capability-loader.cts.
|
|
1668
1699
|
const capHostVersion = () => {
|
|
1700
|
+
const SEMVER_PREFIX = /^\d+\.\d+\.\d+/;
|
|
1669
1701
|
try {
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
} catch {
|
|
1673
|
-
|
|
1674
|
-
|
|
1702
|
+
const v = fs.readFileSync(path.join(__dirname, '..', 'VERSION'), 'utf8').trim();
|
|
1703
|
+
if (SEMVER_PREFIX.test(v)) return v;
|
|
1704
|
+
} catch { /* not an installed tree (no gsd-core/VERSION) */ }
|
|
1705
|
+
try {
|
|
1706
|
+
const pkg = require(path.join(__dirname, '..', '..', 'package.json')); // gsd-core/bin/ -> repo root is two up
|
|
1707
|
+
if (pkg && typeof pkg.version === 'string' && SEMVER_PREFIX.test(pkg.version)) return pkg.version;
|
|
1708
|
+
} catch { /* runtime root has no package.json */ }
|
|
1709
|
+
return '0.0.0';
|
|
1675
1710
|
};
|
|
1676
1711
|
// #1459: the USER-OWNED consent home (GSD_HOME||homedir()) where project-scope consent records
|
|
1677
1712
|
// live — OUTSIDE any repo. SAME rule as the loader/consent-store path resolution so a record
|
|
@@ -67,6 +67,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
67
67
|
};
|
|
68
68
|
})();
|
|
69
69
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
|
+
exports.readHostVersion = readHostVersion;
|
|
70
71
|
exports.loadRegistry = loadRegistry;
|
|
71
72
|
const fs = __importStar(require("node:fs"));
|
|
72
73
|
const os = __importStar(require("node:os"));
|
|
@@ -110,17 +111,33 @@ function _setValidatorForTest(v) {
|
|
|
110
111
|
function _setGeneratorForTest(g) {
|
|
111
112
|
_generatorOverride = g;
|
|
112
113
|
}
|
|
113
|
-
/**
|
|
114
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Resolve the running GSD version; fail-closed to '0.0.0' if it cannot be read.
|
|
116
|
+
*
|
|
117
|
+
* Prefer the authoritative `gsd-core/VERSION` the installer writes for EVERY runtime
|
|
118
|
+
* (libDir = gsd-core/bin/lib/, so `../../VERSION` = gsd-core/VERSION). This is reliable
|
|
119
|
+
* across all installed layouts — including runtimes that get no marker package.json, and
|
|
120
|
+
* local installs where the walked-up `../../../package.json` would resolve to the USER's
|
|
121
|
+
* own project and report a wrong version (#1920). Fall back to the runtime-root
|
|
122
|
+
* package.json for the dev/source tree, then fail-closed. Mirrors resolveVersionFrom()
|
|
123
|
+
* (#1383). `libDir` is injectable for tests; it defaults to this module's directory.
|
|
124
|
+
*/
|
|
125
|
+
function readHostVersion(libDir = __dirname) {
|
|
126
|
+
const SEMVER_PREFIX = /^\d+\.\d+\.\d+/;
|
|
115
127
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return typeof pkg.version === 'string' && pkg.version ? pkg.version : '0.0.0';
|
|
128
|
+
const v = fs.readFileSync(path.join(libDir, '..', '..', 'VERSION'), 'utf8').trim();
|
|
129
|
+
if (SEMVER_PREFIX.test(v))
|
|
130
|
+
return v;
|
|
120
131
|
}
|
|
121
|
-
catch {
|
|
122
|
-
|
|
132
|
+
catch { /* not an installed tree (no gsd-core/VERSION) */ }
|
|
133
|
+
try {
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
135
|
+
const pkg = require(path.join(libDir, '..', '..', '..', 'package.json'));
|
|
136
|
+
if (pkg && typeof pkg.version === 'string' && SEMVER_PREFIX.test(pkg.version))
|
|
137
|
+
return pkg.version;
|
|
123
138
|
}
|
|
139
|
+
catch { /* runtime root has no package.json */ }
|
|
140
|
+
return '0.0.0';
|
|
124
141
|
}
|
|
125
142
|
/**
|
|
126
143
|
* Canonicalize a directory path for dedup/scope-escalation comparison. #1459 finding 1 (HIGH): the dedup
|
|
@@ -779,4 +796,5 @@ function loadRegistry(options = {}) {
|
|
|
779
796
|
return withOverlayMeta(base, meta);
|
|
780
797
|
}
|
|
781
798
|
}
|
|
782
|
-
|
|
799
|
+
// readHostVersion is exported for the #1920 regression (VERSION-first host-version resolution).
|
|
800
|
+
module.exports = { loadRegistry, readHostVersion, _setValidatorForTest, _setGeneratorForTest };
|