@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/GEMINI.md +25 -23
- package/README.md +3 -3
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-ai-researcher.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-domain-researcher.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +1 -1
- package/agents/gsd-phase-researcher.md +2 -2
- package/agents/gsd-plan-checker.md +1 -1
- package/agents/gsd-planner.md +2 -2
- package/agents/gsd-project-researcher.md +2 -2
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-researcher.md +2 -2
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +108 -458
- 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 +25 -1
- package/gsd-core/bin/lib/capability-registry.cjs +207 -205
- 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/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/gate-predicate-evaluator.cjs +151 -0
- package/gsd-core/bin/lib/graphify.cjs +41 -9
- package/gsd-core/bin/lib/milestone.cjs +7 -1
- package/gsd-core/bin/lib/phase.cjs +14 -2
- package/gsd-core/bin/lib/plan-scan.cjs +9 -1
- 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.cjs +17 -2
- 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/surface.cjs +18 -9
- package/gsd-core/bin/lib/update-context.cjs +0 -5
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/bin/shared/model-catalog.json +0 -5
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +0 -5
- package/gsd-core/references/gates.md +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +4 -5
- 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 +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/full.md +14 -0
- 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 +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +1 -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/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase.md +9 -2
- 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 +43 -7
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +3 -1
- 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 +62 -4
- package/package.json +3 -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 +16 -3
- package/scripts/slurm-adapter.cjs +269 -0
- package/scripts/sync-manifest-versions.cjs +3 -2
- 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/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
|
+
};
|
|
@@ -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;
|