@opengsd/gsd-core 1.7.0-rc.2 → 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.
Files changed (155) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/GEMINI.md +25 -23
  4. package/README.md +3 -3
  5. package/agents/gsd-advisor-researcher.md +1 -1
  6. package/agents/gsd-ai-researcher.md +1 -1
  7. package/agents/gsd-code-fixer.md +1 -1
  8. package/agents/gsd-debug-session-manager.md +1 -1
  9. package/agents/gsd-debugger.md +1 -1
  10. package/agents/gsd-domain-researcher.md +1 -1
  11. package/agents/gsd-eval-auditor.md +1 -1
  12. package/agents/gsd-executor.md +4 -4
  13. package/agents/gsd-intel-updater.md +1 -1
  14. package/agents/gsd-mempalace-curator.md +1 -1
  15. package/agents/gsd-phase-researcher.md +2 -2
  16. package/agents/gsd-plan-checker.md +1 -1
  17. package/agents/gsd-planner.md +2 -2
  18. package/agents/gsd-project-researcher.md +2 -2
  19. package/agents/gsd-research-synthesizer.md +1 -1
  20. package/agents/gsd-ui-researcher.md +2 -2
  21. package/agents/gsd-verifier.md +2 -2
  22. package/bin/install.js +108 -458
  23. package/commands/gsd/mempalace-capture.md +1 -1
  24. package/commands/gsd/mempalace-recall.md +9 -5
  25. package/commands/gsd/next.md +29 -0
  26. package/commands/gsd/ns-workflow.md +5 -2
  27. package/gsd-core/bin/ensure-runtime-build.cjs +246 -0
  28. package/gsd-core/bin/gsd-tools.cjs +25 -1
  29. package/gsd-core/bin/lib/capability-registry.cjs +207 -205
  30. package/gsd-core/bin/lib/check-command-router.cjs +111 -1
  31. package/gsd-core/bin/lib/clusters.cjs +1 -0
  32. package/gsd-core/bin/lib/config.cjs +19 -1
  33. package/gsd-core/bin/lib/core-utils.cjs +41 -0
  34. package/gsd-core/bin/lib/external-job.cjs +287 -0
  35. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +151 -0
  36. package/gsd-core/bin/lib/graphify.cjs +41 -9
  37. package/gsd-core/bin/lib/milestone.cjs +7 -1
  38. package/gsd-core/bin/lib/phase.cjs +14 -2
  39. package/gsd-core/bin/lib/plan-scan.cjs +9 -1
  40. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  41. package/gsd-core/bin/lib/prohibition-enforcement.cjs +21 -19
  42. package/gsd-core/bin/lib/roadmap.cjs +17 -2
  43. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +18 -13
  44. package/gsd-core/bin/lib/runtime-name-policy.cjs +8 -10
  45. package/gsd-core/bin/lib/shell-command-projection.cjs +12 -8
  46. package/gsd-core/bin/lib/smart-entry.cjs +521 -0
  47. package/gsd-core/bin/lib/surface.cjs +18 -9
  48. package/gsd-core/bin/lib/update-context.cjs +0 -5
  49. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  50. package/gsd-core/bin/shared/model-catalog.json +0 -5
  51. package/gsd-core/bin/shared/runtime-aliases.manifest.json +0 -5
  52. package/gsd-core/references/gates.md +1 -1
  53. package/gsd-core/references/planning-config.md +1 -1
  54. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  55. package/gsd-core/workflows/add-backlog.md +1 -1
  56. package/gsd-core/workflows/add-phase.md +1 -1
  57. package/gsd-core/workflows/add-tests.md +1 -1
  58. package/gsd-core/workflows/add-todo.md +1 -1
  59. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  60. package/gsd-core/workflows/audit-fix.md +1 -1
  61. package/gsd-core/workflows/audit-milestone.md +1 -1
  62. package/gsd-core/workflows/audit-uat.md +1 -1
  63. package/gsd-core/workflows/autonomous.md +1 -1
  64. package/gsd-core/workflows/check-todos.md +1 -1
  65. package/gsd-core/workflows/cleanup.md +1 -1
  66. package/gsd-core/workflows/code-review-fix.md +1 -1
  67. package/gsd-core/workflows/code-review.md +1 -1
  68. package/gsd-core/workflows/complete-milestone.md +1 -1
  69. package/gsd-core/workflows/debug.md +1 -1
  70. package/gsd-core/workflows/diagnose-issues.md +1 -1
  71. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  72. package/gsd-core/workflows/discuss-phase/modes/auto.md +1 -1
  73. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  74. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  75. package/gsd-core/workflows/discuss-phase.md +2 -2
  76. package/gsd-core/workflows/do.md +1 -1
  77. package/gsd-core/workflows/docs-update.md +1 -1
  78. package/gsd-core/workflows/edit-phase.md +1 -1
  79. package/gsd-core/workflows/eval-review.md +1 -1
  80. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  81. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  82. package/gsd-core/workflows/execute-phase.md +4 -5
  83. package/gsd-core/workflows/execute-plan.md +1 -1
  84. package/gsd-core/workflows/explore.md +1 -1
  85. package/gsd-core/workflows/extract-learnings.md +1 -1
  86. package/gsd-core/workflows/forensics.md +1 -1
  87. package/gsd-core/workflows/graduation.md +1 -1
  88. package/gsd-core/workflows/health.md +1 -1
  89. package/gsd-core/workflows/help/modes/full.md +14 -0
  90. package/gsd-core/workflows/help/modes/topic.md +1 -0
  91. package/gsd-core/workflows/import.md +1 -1
  92. package/gsd-core/workflows/ingest-docs.md +1 -1
  93. package/gsd-core/workflows/insert-phase.md +1 -1
  94. package/gsd-core/workflows/list-seeds.md +1 -1
  95. package/gsd-core/workflows/list-workspaces.md +1 -1
  96. package/gsd-core/workflows/manager.md +1 -1
  97. package/gsd-core/workflows/map-codebase.md +1 -1
  98. package/gsd-core/workflows/milestone-summary.md +1 -1
  99. package/gsd-core/workflows/mvp-phase.md +1 -1
  100. package/gsd-core/workflows/new-milestone.md +1 -1
  101. package/gsd-core/workflows/new-project.md +1 -1
  102. package/gsd-core/workflows/new-workspace.md +1 -1
  103. package/gsd-core/workflows/next.md +1 -1
  104. package/gsd-core/workflows/pause-work.md +1 -1
  105. package/gsd-core/workflows/plan-milestone-gaps.md +1 -1
  106. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  107. package/gsd-core/workflows/plan-phase.md +9 -2
  108. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  109. package/gsd-core/workflows/plant-seed.md +1 -1
  110. package/gsd-core/workflows/pr-branch.md +1 -1
  111. package/gsd-core/workflows/profile-user.md +1 -1
  112. package/gsd-core/workflows/progress.md +1 -1
  113. package/gsd-core/workflows/quick.md +1 -1
  114. package/gsd-core/workflows/reapply-patches.md +1 -1
  115. package/gsd-core/workflows/remove-phase.md +1 -1
  116. package/gsd-core/workflows/remove-workspace.md +1 -1
  117. package/gsd-core/workflows/resume-project.md +1 -1
  118. package/gsd-core/workflows/review.md +43 -7
  119. package/gsd-core/workflows/scan.md +1 -1
  120. package/gsd-core/workflows/secure-phase.md +1 -1
  121. package/gsd-core/workflows/settings-advanced.md +3 -1
  122. package/gsd-core/workflows/settings-integrations.md +1 -1
  123. package/gsd-core/workflows/settings.md +1 -1
  124. package/gsd-core/workflows/ship.md +1 -1
  125. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  126. package/gsd-core/workflows/sketch.md +1 -1
  127. package/gsd-core/workflows/smart-entry.md +120 -0
  128. package/gsd-core/workflows/spec-phase.md +8 -6
  129. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  130. package/gsd-core/workflows/spike.md +1 -1
  131. package/gsd-core/workflows/stats.md +1 -1
  132. package/gsd-core/workflows/thread.md +1 -1
  133. package/gsd-core/workflows/transition.md +1 -1
  134. package/gsd-core/workflows/ui-phase.md +1 -1
  135. package/gsd-core/workflows/ui-review.md +1 -1
  136. package/gsd-core/workflows/ultraplan-phase.md +1 -1
  137. package/gsd-core/workflows/update.md +3 -6
  138. package/gsd-core/workflows/validate-phase.md +1 -1
  139. package/gsd-core/workflows/verify-phase.md +1 -1
  140. package/gsd-core/workflows/verify-work.md +62 -4
  141. package/package.json +3 -5
  142. package/scripts/ci-test-scope.cjs +19 -1
  143. package/scripts/generate-package-identity.cjs +27 -2
  144. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -148
  145. package/scripts/lint-regression-test-names.allowlist.json +1 -273
  146. package/scripts/lint-test-file-count.allowlist.json +7 -91
  147. package/scripts/research-profiles.cjs +6 -6
  148. package/scripts/run-tests.cjs +16 -3
  149. package/scripts/slurm-adapter.cjs +269 -0
  150. package/scripts/sync-manifest-versions.cjs +3 -2
  151. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  152. package/skills/gsd-mempalace-recall/SKILL.md +9 -5
  153. package/skills/gsd-next/SKILL.md +30 -0
  154. package/skills/gsd-ns-workflow/SKILL.md +4 -1
  155. 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). Only `augment` is currently wired, so these are an *additive* mirror of `.planning/graphs/`. (`kg_backend`/`replace` are forward-declared and behave as `augment` today.)
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`). Only `augment` is wired today, so recall always treats the palace as additive; `kg_backend`/`replace` are forward-declared and behave as `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. Only `augment` is currently wired, so the palace KG *supplements* GSD's native `.planning/graphs/` — do not treat it as the sole source. (`kg_backend`/`replace` are forward-declared and behave as `augment` today.)
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 — proceeding without recalled memory._
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
+ };
@@ -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;