@opengsd/gsd-core 1.5.0-rc.1 → 1.5.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/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +3 -2
- package/agents/gsd-debug-session-manager.md +2 -1
- package/agents/gsd-debugger.md +4 -3
- package/agents/gsd-executor.md +16 -15
- package/agents/gsd-intel-updater.md +38 -41
- package/agents/gsd-mempalace-curator.md +47 -0
- package/agents/gsd-phase-researcher.md +8 -8
- package/agents/gsd-plan-checker.md +12 -11
- package/agents/gsd-planner.md +21 -181
- package/agents/gsd-project-researcher.md +5 -4
- package/agents/gsd-research-synthesizer.md +2 -1
- package/agents/gsd-ui-researcher.md +2 -1
- package/agents/gsd-verifier.md +9 -8
- package/bin/install.js +230 -1409
- package/commands/gsd/mempalace-capture.md +71 -0
- package/commands/gsd/mempalace-recall.md +102 -0
- package/commands/gsd/ns-context.md +4 -2
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/gsd-tools.cjs +110 -92
- package/gsd-core/bin/gsd_run +20 -0
- package/gsd-core/bin/lib/capability-activation.cjs +86 -0
- package/gsd-core/bin/lib/capability-registry.cjs +3321 -47
- package/gsd-core/bin/lib/capability-state.cjs +31 -18
- package/gsd-core/bin/lib/check-command-router.cjs +460 -2
- package/gsd-core/bin/lib/clusters.cjs +2 -0
- package/gsd-core/bin/lib/command-roster.cjs +19 -0
- package/gsd-core/bin/lib/commands.cjs +33 -10
- package/gsd-core/bin/lib/config-loader.cjs +7 -8
- package/gsd-core/bin/lib/config-schema.cjs +32 -3
- package/gsd-core/bin/lib/config.cjs +1 -2
- package/gsd-core/bin/lib/core.cjs +56 -3
- package/gsd-core/bin/lib/edge-probe.cjs +173 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +63 -25
- package/gsd-core/bin/lib/init.cjs +8 -5
- package/gsd-core/bin/lib/install-profiles.cjs +2 -1
- package/gsd-core/bin/lib/intel.cjs +3 -3
- package/gsd-core/bin/lib/io.cjs +61 -6
- package/gsd-core/bin/lib/loop-resolver.cjs +157 -16
- package/gsd-core/bin/lib/model-resolver.cjs +47 -5
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -0
- package/gsd-core/bin/lib/phase.cjs +88 -11
- package/gsd-core/bin/lib/probe-core.cjs +257 -0
- package/gsd-core/bin/lib/profile-output.cjs +45 -4
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +138 -0
- package/gsd-core/bin/lib/roadmap.cjs +137 -7
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +1700 -0
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +67 -165
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +77 -31
- package/gsd-core/bin/lib/runtime-homes.cjs +156 -114
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +1439 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +1 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +7 -2
- package/gsd-core/bin/lib/state-command-router.cjs +15 -3
- package/gsd-core/bin/lib/state-document.cjs +54 -1
- package/gsd-core/bin/lib/state.cjs +221 -35
- package/gsd-core/bin/lib/uat-predicate.cjs +329 -0
- package/gsd-core/bin/lib/update-context.cjs +4 -1
- package/gsd-core/bin/lib/verify.cjs +122 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +33 -8
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -2
- package/gsd-core/bin/shared/config-schema.manifest.json +0 -18
- package/gsd-core/bin/shared/model-catalog.json +12 -6
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +2 -1
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +7 -0
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/requirements.json +1 -0
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +8 -0
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/requirements.json +1 -0
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +7 -0
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/requirements.json +1 -0
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +7 -0
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/requirements.json +1 -0
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +8 -0
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/requirements.json +1 -0
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +8 -0
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/requirements.json +1 -0
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/resolutions.json +4 -0
- package/gsd-core/references/edge-probe.md +261 -0
- package/gsd-core/references/loop-hook-dispatch.md +61 -0
- package/gsd-core/references/planner-antipatterns.md +41 -0
- package/gsd-core/references/planner-guidance.md +186 -0
- package/gsd-core/templates/config.json +1 -1
- package/gsd-core/templates/spec.md +12 -0
- 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 +6 -4
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +36 -42
- 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 +10 -8
- package/gsd-core/workflows/code-review.md +59 -22
- 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 +15 -1
- 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 +83 -144
- 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 +3 -1
- 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-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 +9 -1
- package/gsd-core/workflows/new-project.md +12 -4
- 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.md +120 -240
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/profile-user.md +4 -2
- package/gsd-core/workflows/progress.md +59 -2
- package/gsd-core/workflows/quick.md +10 -7
- 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 +20 -3
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +5 -3
- package/gsd-core/workflows/settings-advanced.md +20 -14
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +2 -2
- package/gsd-core/workflows/ship.md +58 -1
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +130 -1
- 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 +2 -2
- package/gsd-core/workflows/validate-phase.md +5 -3
- package/gsd-core/workflows/verify-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +28 -12
- package/package.json +7 -3
- package/scripts/affected-tests-lib.cjs +16 -4
- package/scripts/gen-capability-registry.cjs +857 -61
- package/scripts/gen-inventory-manifest.cjs +1 -4
- package/scripts/gen-loop-host-contract.cjs +55 -0
- package/scripts/issue-version-gate.cjs +140 -0
- package/scripts/lib/allowlist-ratchet.cjs +101 -1
- package/scripts/lint-allow-test-rule-refs.allowlist.json +326 -0
- package/scripts/lint-allow-test-rule-refs.cjs +162 -0
- package/scripts/lint-test-file-count.allowlist.json +23 -2
- package/scripts/lint-windows-test-portability.cjs +178 -0
- package/scripts/release-tarball-smoke.cjs +7 -1
- package/scripts/research-profiles.cjs +10 -10
- package/scripts/run-tests.cjs +128 -27
- package/scripts/sync-next-version.cjs +133 -0
- package/scripts/sync-runtime-launcher.cjs +21 -5
- package/scripts/update-size-baseline.cjs +68 -0
- package/scripts/workflow-policy.cjs +42 -9
- package/scripts/workflow-size.cjs +90 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsd:mempalace-capture
|
|
3
|
+
description: "File a phase artifact into MemPalace; mirror decision facts into its temporal KG"
|
|
4
|
+
argument-hint: "[CONTEXT.md|PLAN.md|SUMMARY.md]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
requires: [config]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**STOP -- DO NOT READ THIS FILE. You are already reading it. This prompt was injected into your context by the command system. Using the Read tool on this file wastes tokens. Begin executing Step 0 immediately.**
|
|
12
|
+
|
|
13
|
+
## Step 0 -- Banner
|
|
14
|
+
|
|
15
|
+
**Before ANY tool calls**, display this banner:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
GSD > MEMPALACE CAPTURE
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then proceed to Step 1.
|
|
22
|
+
|
|
23
|
+
## Step 1 -- Config Gate
|
|
24
|
+
|
|
25
|
+
Check whether the MemPalace capability is enabled by reading `.planning/config.json` directly with the Read tool.
|
|
26
|
+
|
|
27
|
+
1. Read `.planning/config.json` with the Read tool.
|
|
28
|
+
2. If the file does not exist, or `config.mempalace` is absent, or `config.mempalace.enabled !== true`, or `config.mempalace.capture_artifacts !== true`: display the disabled message and **STOP**.
|
|
29
|
+
3. Otherwise proceed to Step 2.
|
|
30
|
+
|
|
31
|
+
**Disabled message:**
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
GSD > MEMPALACE CAPTURE
|
|
35
|
+
|
|
36
|
+
MemPalace capture is disabled (mempalace.enabled / mempalace.capture_artifacts).
|
|
37
|
+
Nothing was filed; the loop proceeds normally.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This step is `onError: skip` at `discuss:post` / `plan:post` / `verify:post` -- capture never fails a phase.
|
|
41
|
+
|
|
42
|
+
## Step 2 -- Resolve target
|
|
43
|
+
|
|
44
|
+
1. **Artifact.** Take the artifact from `$ARGUMENTS`. If absent, infer from the loop point: `discuss:post` → `CONTEXT.md`, `plan:post` → `PLAN.md`, `verify:post` → `SUMMARY.md`.
|
|
45
|
+
2. **Room.** Map artifact → room:
|
|
46
|
+
- `CONTEXT.md` → `decisions`
|
|
47
|
+
- `PLAN.md` → `planning`
|
|
48
|
+
- `SUMMARY.md` → `milestones`
|
|
49
|
+
(Confirmed problem→fix pairs go to `problems` — see the `capture-problems` fragment used at `execute:wave:post`.)
|
|
50
|
+
3. **Wing.** `config.mempalace.wing` if non-empty, else `config.project_code`, else the repo directory name.
|
|
51
|
+
4. **Mode / transport.** Read `config.mempalace.memory_mode`. Prefer MCP (`mempalace_*`) when your MemPalace MCP server is registered and your runtime permits those tools; otherwise use the `mempalace` CLI (covered by this skill's `Bash` allow-tool), as in `mempalace-recall`.
|
|
52
|
+
|
|
53
|
+
## Step 3 -- File verbatim (idempotent)
|
|
54
|
+
|
|
55
|
+
On any error or timeout, stop and let the phase continue -- capture is best-effort.
|
|
56
|
+
|
|
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
|
+
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.)
|
|
60
|
+
4. Re-running a phase MUST NOT create duplicate drawers (deterministic ids + `check_duplicate`).
|
|
61
|
+
|
|
62
|
+
## Step 4 -- Report
|
|
63
|
+
|
|
64
|
+
Print a one-line summary: `Filed <artifact> → <wing>/<room> (<n> KG facts)` or `MemPalace unavailable — capture skipped`.
|
|
65
|
+
|
|
66
|
+
## Anti-Patterns
|
|
67
|
+
|
|
68
|
+
1. DO NOT let any MemPalace error fail the step -- capture is `onError: skip`.
|
|
69
|
+
2. DO NOT write lossy summaries -- store the verbatim artifact text (AAAK compression is a separate, optional index).
|
|
70
|
+
3. DO NOT prune or delete drawers here -- pruning (`sync --apply`) is the curator agent's job at `ship:post`, wing-scoped only.
|
|
71
|
+
4. DO NOT skip the config gate or the dedup check.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsd:mempalace-recall
|
|
3
|
+
description: "Recall decisions, patterns, and surprises from MemPalace before planning"
|
|
4
|
+
argument-hint: "[phase-slug]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
requires: [config]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
**STOP -- DO NOT READ THIS FILE. You are already reading it. This prompt was injected into your context by the command system. Using the Read tool on this file wastes tokens. Begin executing Step 0 immediately.**
|
|
13
|
+
|
|
14
|
+
## Step 0 -- Banner
|
|
15
|
+
|
|
16
|
+
**Before ANY tool calls**, display this banner:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
GSD > MEMPALACE RECALL
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then proceed to Step 1.
|
|
23
|
+
|
|
24
|
+
## Step 1 -- Config Gate
|
|
25
|
+
|
|
26
|
+
Check whether the MemPalace capability is enabled by reading `.planning/config.json` directly with the Read tool.
|
|
27
|
+
|
|
28
|
+
**DO NOT use `gsd-tools config get-value`** -- it hard-exits on missing keys.
|
|
29
|
+
|
|
30
|
+
1. Read `.planning/config.json` with the Read tool.
|
|
31
|
+
2. If the file does not exist: write the "unavailable" stub (Step 4) and **STOP**.
|
|
32
|
+
3. Parse the JSON. Proceed to Step 2 only if `config.mempalace && config.mempalace.enabled === true` **and** `config.mempalace.recall_on_plan !== false`. Otherwise display the disabled message and **STOP** (`recall_on_plan: false` turns plan-time recall off while leaving the rest of the capability enabled).
|
|
33
|
+
|
|
34
|
+
**Disabled message:**
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
GSD > MEMPALACE RECALL
|
|
38
|
+
|
|
39
|
+
MemPalace memory is disabled. To activate:
|
|
40
|
+
|
|
41
|
+
node <runtime-home>/gsd-core/bin/gsd-tools.cjs config-set mempalace.enabled true
|
|
42
|
+
|
|
43
|
+
Recall is opt-in; the loop proceeds normally without it.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This step is `onError: skip` at `plan:pre` -- recall never blocks planning.
|
|
47
|
+
|
|
48
|
+
## Step 2 -- Resolve wing, mode, and transport
|
|
49
|
+
|
|
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`.
|
|
52
|
+
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
|
+
4. **Topic.** Read the phase `CONTEXT.md` (the consumed artifact). Derive a short search query from its title, goal, and key decisions.
|
|
54
|
+
|
|
55
|
+
## Step 3 -- Retrieve (read-only)
|
|
56
|
+
|
|
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.
|
|
58
|
+
|
|
59
|
+
1. **Wake up** (cheap, ~600--900 tokens):
|
|
60
|
+
- Interactive: read the wing identity/summary, then `mempalace_search`.
|
|
61
|
+
- Headless: `mempalace wake-up --wing <wing>`.
|
|
62
|
+
2. **Targeted search:**
|
|
63
|
+
- Interactive: `mempalace_search(query=<topic>, wing=<wing>)`.
|
|
64
|
+
- 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.)
|
|
66
|
+
4. **Dedup** the returned drawers/facts; keep the top results.
|
|
67
|
+
|
|
68
|
+
## Step 4 -- Write MEMORY-RECALL.md
|
|
69
|
+
|
|
70
|
+
Write `MEMORY-RECALL.md` in the current phase directory. The planner consumes it.
|
|
71
|
+
|
|
72
|
+
When recall succeeded, structure it as:
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
# Memory Recall (MemPalace)
|
|
76
|
+
|
|
77
|
+
_Wing: <wing> · Mode: <mode> · Transport: <mcp|cli>_
|
|
78
|
+
|
|
79
|
+
## Prior decisions
|
|
80
|
+
- <decision> — <provenance: drawer id / kg fact, valid_from>
|
|
81
|
+
|
|
82
|
+
## Patterns
|
|
83
|
+
- <pattern> — <provenance>
|
|
84
|
+
|
|
85
|
+
## Surprises / gotchas
|
|
86
|
+
- <surprise> — <provenance>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
When MemPalace is unreachable, write the stub and continue:
|
|
90
|
+
|
|
91
|
+
```markdown
|
|
92
|
+
# Memory Recall (MemPalace)
|
|
93
|
+
|
|
94
|
+
_MemPalace unavailable at recall time — proceeding without recalled memory._
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Anti-Patterns
|
|
98
|
+
|
|
99
|
+
1. DO NOT let any MemPalace error fail the step -- recall is `onError: skip`.
|
|
100
|
+
2. DO NOT write to the palace from this skill -- recall is read-only; capture is a separate skill.
|
|
101
|
+
3. DO NOT paste raw search output into the file -- distil to decisions/patterns/surprises with provenance.
|
|
102
|
+
4. DO NOT skip the config gate.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsd-context
|
|
3
|
-
description: "codebase
|
|
3
|
+
description: "codebase intel | map graphify docs learnings mempalace"
|
|
4
4
|
argument-hint: ""
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Skill
|
|
8
|
-
requires: [map-codebase, graphify, docs-update, extract-learnings]
|
|
8
|
+
requires: [map-codebase, graphify, docs-update, extract-learnings, mempalace-recall, mempalace-capture]
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
Route to the appropriate codebase-intelligence skill based on the user's intent.
|
|
@@ -19,5 +19,7 @@ Route to the appropriate codebase-intelligence skill based on the user's intent.
|
|
|
19
19
|
| Generate a knowledge graph | gsd-graphify |
|
|
20
20
|
| Update project documentation | gsd-docs-update |
|
|
21
21
|
| Extract learnings from a completed phase | gsd-extract-learnings |
|
|
22
|
+
| Recall prior decisions and patterns before planning | gsd-mempalace-recall |
|
|
23
|
+
| File a phase artifact into MemPalace | gsd-mempalace-capture |
|
|
22
24
|
|
|
23
25
|
Invoke the matched skill directly using the Skill tool.
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-core",
|
|
3
|
-
"version": "1.5.0-rc.
|
|
3
|
+
"version": "1.5.0-rc.3",
|
|
4
4
|
"description": "GSD Core — a meta-prompting, context engineering, and spec-driven development system for AI coding agents. Loads gsd's operating context into every Gemini CLI session.",
|
|
5
5
|
"contextFileName": "GEMINI.md"
|
|
6
6
|
}
|
|
@@ -58,6 +58,11 @@
|
|
|
58
58
|
* [--name <name>]
|
|
59
59
|
* [--archive-phases] Move phase dirs to milestones/vX.Y-phases/
|
|
60
60
|
*
|
|
61
|
+
* User Story Validation:
|
|
62
|
+
* user-story validate --story "..." Validate "As a / I want to / so that" format
|
|
63
|
+
* Returns JSON { valid, errors[], slots: {role,capability,outcome} | null }
|
|
64
|
+
* --pick valid Emit bare boolean (for workflow boolean checks)
|
|
65
|
+
*
|
|
61
66
|
* Validation:
|
|
62
67
|
* validate consistency Check phase numbering, disk/roadmap sync
|
|
63
68
|
* validate health [--repair] Check .planning/ integrity, optionally repair
|
|
@@ -201,8 +206,6 @@ const milestone = require('./lib/milestone.cjs');
|
|
|
201
206
|
const commands = require('./lib/commands.cjs');
|
|
202
207
|
const init = require('./lib/init.cjs');
|
|
203
208
|
const frontmatter = require('./lib/frontmatter.cjs');
|
|
204
|
-
const profilePipeline = require('./lib/profile-pipeline.cjs');
|
|
205
|
-
const profileOutput = require('./lib/profile-output.cjs');
|
|
206
209
|
const workstream = require('./lib/workstream.cjs');
|
|
207
210
|
const docs = require('./lib/docs.cjs');
|
|
208
211
|
const learnings = require('./lib/learnings.cjs');
|
|
@@ -510,7 +513,7 @@ async function main() {
|
|
|
510
513
|
'generate-dev-preferences, generate-slug, graphify, history-digest, init, intel, ' +
|
|
511
514
|
'capability, classify-confidence, learnings, list-todos, loop, milestone, package-legitimacy, phase, phase-plan-index, phases, profile-questionnaire, ' +
|
|
512
515
|
'profile-sample, progress, prompt-budget, requirements, research-plan, research-store, resolve-granularity, resolve-model, roadmap, scaffold, state, ' +
|
|
513
|
-
'task, template, validate, verify, verify-path-exists, verify-summary, workstream, worktree\n\n' +
|
|
516
|
+
'task, template, user-story, validate, verify, verify-path-exists, verify-summary, workstream, worktree\n\n' +
|
|
514
517
|
'Global flags:\n' +
|
|
515
518
|
' --raw Emit raw output without post-processing\n' +
|
|
516
519
|
' --pick <field> Extract a single field from JSON output (dot/bracket notation)\n' +
|
|
@@ -559,6 +562,7 @@ async function main() {
|
|
|
559
562
|
'verify-summary', 'template', 'frontmatter', 'detect-custom-files',
|
|
560
563
|
'worktree', 'prompt-budget',
|
|
561
564
|
'research-store', 'research-plan', 'package-legitimacy', 'classify-confidence',
|
|
565
|
+
'user-story', // pure string validation — no .planning/ access needed
|
|
562
566
|
]);
|
|
563
567
|
if (!SKIP_ROOT_RESOLUTION.has(command)) {
|
|
564
568
|
cwd = findProjectRoot(cwd);
|
|
@@ -1236,7 +1240,39 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1236
1240
|
// loop render-hooks <point>
|
|
1237
1241
|
const loopSubcommand = args[1];
|
|
1238
1242
|
if (loopSubcommand === 'render-hooks') {
|
|
1239
|
-
|
|
1243
|
+
let loopConfigDir = null;
|
|
1244
|
+
const configDirEqArg = args.find(arg => arg.startsWith('--config-dir='));
|
|
1245
|
+
const configDirIdx = args.indexOf('--config-dir');
|
|
1246
|
+
if (configDirEqArg) {
|
|
1247
|
+
const value = configDirEqArg.slice('--config-dir='.length).trim();
|
|
1248
|
+
if (!value) error('Missing value for --config-dir', core.ERROR_REASON ? core.ERROR_REASON.USAGE : undefined);
|
|
1249
|
+
loopConfigDir = value;
|
|
1250
|
+
} else if (configDirIdx !== -1) {
|
|
1251
|
+
const value = args[configDirIdx + 1];
|
|
1252
|
+
if (!value || value.startsWith('--')) {
|
|
1253
|
+
error('Missing value for --config-dir', core.ERROR_REASON ? core.ERROR_REASON.USAGE : undefined);
|
|
1254
|
+
}
|
|
1255
|
+
loopConfigDir = value;
|
|
1256
|
+
}
|
|
1257
|
+
// --active-cap <capId>: parse and validate before delegating
|
|
1258
|
+
let loopActiveCap = undefined;
|
|
1259
|
+
const activeCapEqArg = args.find(arg => arg.startsWith('--active-cap='));
|
|
1260
|
+
const activeCapIdx = args.indexOf('--active-cap');
|
|
1261
|
+
if (activeCapEqArg) {
|
|
1262
|
+
const value = activeCapEqArg.slice('--active-cap='.length).trim();
|
|
1263
|
+
if (!value) error('Missing value for --active-cap (e.g. --active-cap tdd)', core.ERROR_REASON ? core.ERROR_REASON.USAGE : undefined);
|
|
1264
|
+
loopActiveCap = value;
|
|
1265
|
+
} else if (activeCapIdx !== -1) {
|
|
1266
|
+
const value = args[activeCapIdx + 1];
|
|
1267
|
+
if (!value || value.startsWith('--')) {
|
|
1268
|
+
error('Missing value for --active-cap (e.g. --active-cap tdd)', core.ERROR_REASON ? core.ERROR_REASON.USAGE : undefined);
|
|
1269
|
+
}
|
|
1270
|
+
loopActiveCap = value;
|
|
1271
|
+
}
|
|
1272
|
+
loopResolver.cmdLoopRenderHooks(cwd, args[2], raw, {
|
|
1273
|
+
configDir: loopConfigDir ? path.resolve(loopConfigDir) : undefined,
|
|
1274
|
+
activeCap: loopActiveCap,
|
|
1275
|
+
});
|
|
1240
1276
|
} else {
|
|
1241
1277
|
error(
|
|
1242
1278
|
`Unknown loop subcommand: ${loopSubcommand}. Available: render-hooks`,
|
|
@@ -1305,94 +1341,6 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1305
1341
|
break;
|
|
1306
1342
|
}
|
|
1307
1343
|
|
|
1308
|
-
// ─── Profiling Pipeline ────────────────────────────────────────────────
|
|
1309
|
-
|
|
1310
|
-
case 'scan-sessions': {
|
|
1311
|
-
const pathIdx = args.indexOf('--path');
|
|
1312
|
-
const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
|
|
1313
|
-
const verboseFlag = args.includes('--verbose');
|
|
1314
|
-
const jsonFlag = args.includes('--json');
|
|
1315
|
-
await profilePipeline.cmdScanSessions(sessionsPath, { verbose: verboseFlag, json: jsonFlag }, raw);
|
|
1316
|
-
break;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
case 'extract-messages': {
|
|
1320
|
-
const sessionIdx = args.indexOf('--session');
|
|
1321
|
-
const sessionId = sessionIdx !== -1 ? args[sessionIdx + 1] : null;
|
|
1322
|
-
const limitIdx = args.indexOf('--limit');
|
|
1323
|
-
const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : null;
|
|
1324
|
-
const pathIdx = args.indexOf('--path');
|
|
1325
|
-
const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
|
|
1326
|
-
const projectArg = args[1];
|
|
1327
|
-
if (!projectArg || projectArg.startsWith('--')) {
|
|
1328
|
-
error('Usage: gsd-tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.', ERROR_REASON.USAGE);
|
|
1329
|
-
}
|
|
1330
|
-
await profilePipeline.cmdExtractMessages(projectArg, { sessionId, limit }, raw, sessionsPath);
|
|
1331
|
-
break;
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
case 'profile-sample': {
|
|
1335
|
-
const pathIdx = args.indexOf('--path');
|
|
1336
|
-
const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
|
|
1337
|
-
const limitIdx = args.indexOf('--limit');
|
|
1338
|
-
const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 150;
|
|
1339
|
-
const maxPerIdx = args.indexOf('--max-per-project');
|
|
1340
|
-
const maxPerProject = maxPerIdx !== -1 ? parseInt(args[maxPerIdx + 1], 10) : null;
|
|
1341
|
-
const maxCharsIdx = args.indexOf('--max-chars');
|
|
1342
|
-
const maxChars = maxCharsIdx !== -1 ? parseInt(args[maxCharsIdx + 1], 10) : 500;
|
|
1343
|
-
await profilePipeline.cmdProfileSample(sessionsPath, { limit, maxPerProject, maxChars }, raw);
|
|
1344
|
-
break;
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
// ─── Profile Output ──────────────────────────────────────────────────
|
|
1348
|
-
|
|
1349
|
-
case 'write-profile': {
|
|
1350
|
-
const inputIdx = args.indexOf('--input');
|
|
1351
|
-
const inputPath = inputIdx !== -1 ? args[inputIdx + 1] : null;
|
|
1352
|
-
if (!inputPath) error('--input <analysis-json-path> is required', ERROR_REASON.USAGE);
|
|
1353
|
-
const outputIdx = args.indexOf('--output');
|
|
1354
|
-
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
1355
|
-
profileOutput.cmdWriteProfile(cwd, { input: inputPath, output: outputPath }, raw);
|
|
1356
|
-
break;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
case 'profile-questionnaire': {
|
|
1360
|
-
const answersIdx = args.indexOf('--answers');
|
|
1361
|
-
const answers = answersIdx !== -1 ? args[answersIdx + 1] : null;
|
|
1362
|
-
profileOutput.cmdProfileQuestionnaire({ answers }, raw);
|
|
1363
|
-
break;
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
case 'generate-dev-preferences': {
|
|
1367
|
-
const analysisIdx = args.indexOf('--analysis');
|
|
1368
|
-
const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
|
|
1369
|
-
const outputIdx = args.indexOf('--output');
|
|
1370
|
-
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
1371
|
-
const stackIdx = args.indexOf('--stack');
|
|
1372
|
-
const stack = stackIdx !== -1 ? args[stackIdx + 1] : null;
|
|
1373
|
-
profileOutput.cmdGenerateDevPreferences(cwd, { analysis: analysisPath, output: outputPath, stack }, raw);
|
|
1374
|
-
break;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
case 'generate-claude-profile': {
|
|
1378
|
-
const analysisIdx = args.indexOf('--analysis');
|
|
1379
|
-
const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
|
|
1380
|
-
const outputIdx = args.indexOf('--output');
|
|
1381
|
-
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
1382
|
-
const globalFlag = args.includes('--global');
|
|
1383
|
-
profileOutput.cmdGenerateClaudeProfile(cwd, { analysis: analysisPath, output: outputPath, global: globalFlag }, raw);
|
|
1384
|
-
break;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
case 'generate-claude-md': {
|
|
1388
|
-
const outputIdx = args.indexOf('--output');
|
|
1389
|
-
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
1390
|
-
const autoFlag = args.includes('--auto');
|
|
1391
|
-
const forceFlag = args.includes('--force');
|
|
1392
|
-
profileOutput.cmdGenerateClaudeMd(cwd, { output: outputPath, auto: autoFlag, force: forceFlag }, raw);
|
|
1393
|
-
break;
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1344
|
case 'workstream': {
|
|
1397
1345
|
const subcommand = args[1];
|
|
1398
1346
|
if (subcommand === 'create') {
|
|
@@ -1984,6 +1932,76 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1984
1932
|
break;
|
|
1985
1933
|
}
|
|
1986
1934
|
|
|
1935
|
+
// ─── User Story Validation (bug #1145) ────────────────────────────────────
|
|
1936
|
+
//
|
|
1937
|
+
// Invocation shapes (from mvp-phase.md and verify-work.md):
|
|
1938
|
+
// gsd_run query user-story.validate --story "$USER_STORY"
|
|
1939
|
+
// gsd_run query user-story.validate --story "$PHASE_GOAL" --pick valid
|
|
1940
|
+
//
|
|
1941
|
+
// Returns JSON: { valid: boolean, errors: string[], slots: { role, capability, outcome } | null }
|
|
1942
|
+
// - valid: true only when the story fully matches the canonical format
|
|
1943
|
+
// - errors: per-slot diagnostic strings (empty on success)
|
|
1944
|
+
// - slots: extracted role/capability/outcome on success; null on failure
|
|
1945
|
+
//
|
|
1946
|
+
// Canonical format (user-story-template.md):
|
|
1947
|
+
// "As a [user role], I want to [capability], so that [outcome]."
|
|
1948
|
+
// Each slot must be non-empty and contain non-whitespace content.
|
|
1949
|
+
//
|
|
1950
|
+
// No .planning/ access needed — pure string validation.
|
|
1951
|
+
case 'user-story': {
|
|
1952
|
+
const subcommand = args[1];
|
|
1953
|
+
if (subcommand !== 'validate') {
|
|
1954
|
+
error(`Unknown user-story subcommand: ${subcommand || '(none)'}. Available: validate`, ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
1955
|
+
break;
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
const storyIdx = args.indexOf('--story');
|
|
1959
|
+
const story = (storyIdx !== -1 && args[storyIdx + 1] && !args[storyIdx + 1].startsWith('--'))
|
|
1960
|
+
? args[storyIdx + 1]
|
|
1961
|
+
: '';
|
|
1962
|
+
|
|
1963
|
+
// Canonical extraction regex — requires non-whitespace content in each slot
|
|
1964
|
+
// (\S.*? ensures the slot isn't whitespace-only).
|
|
1965
|
+
// Named groups: role / capability / outcome.
|
|
1966
|
+
const USER_STORY_RE = /^As a (\S.*?), I want to (\S.*?), so that (\S.*?)\.$/;
|
|
1967
|
+
|
|
1968
|
+
const errors = [];
|
|
1969
|
+
const trimmed = story.trim();
|
|
1970
|
+
let slots = null;
|
|
1971
|
+
|
|
1972
|
+
if (!trimmed) {
|
|
1973
|
+
errors.push('Story is empty. Required format: "As a [role], I want to [capability], so that [outcome]."');
|
|
1974
|
+
} else {
|
|
1975
|
+
// Per-clause guards produce targeted, actionable error messages before
|
|
1976
|
+
// attempting the full regex. Guards are ordered: role → capability → outcome → period.
|
|
1977
|
+
if (!/^As a \S/i.test(trimmed)) {
|
|
1978
|
+
errors.push('Story must start with "As a [user role]," (role must be non-empty).');
|
|
1979
|
+
}
|
|
1980
|
+
if (!/, I want to \S/i.test(trimmed)) {
|
|
1981
|
+
errors.push('Story must include ", I want to [capability]," (capability must be non-empty).');
|
|
1982
|
+
}
|
|
1983
|
+
if (!/, so that \S/i.test(trimmed)) {
|
|
1984
|
+
errors.push('Story must include ", so that [outcome]." (outcome must be non-empty).');
|
|
1985
|
+
}
|
|
1986
|
+
if (!trimmed.endsWith('.')) {
|
|
1987
|
+
errors.push('Story must end with a period (.).');
|
|
1988
|
+
}
|
|
1989
|
+
// Full-regex check only when per-clause guards all passed — avoids
|
|
1990
|
+
// redundant "format mismatch" noise on top of specific error messages.
|
|
1991
|
+
if (errors.length === 0) {
|
|
1992
|
+
const m = USER_STORY_RE.exec(trimmed);
|
|
1993
|
+
if (!m) {
|
|
1994
|
+
errors.push('Story does not match the canonical format: "As a [role], I want to [capability], so that [outcome]."');
|
|
1995
|
+
} else {
|
|
1996
|
+
slots = { role: m[1], capability: m[2], outcome: m[3] };
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
core.output({ valid: errors.length === 0, errors, slots }, raw);
|
|
2002
|
+
break;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1987
2005
|
default: {
|
|
1988
2006
|
// ADR-959: try capability-registry dispatch before emitting the unknown-command error.
|
|
1989
2007
|
// An unmigrated command still hits its hardcoded `case` above — untouched.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# gsd_run — standalone launcher so workflow bash blocks can invoke the GSD tools
|
|
3
|
+
# in a fresh shell. Claude Code (and similar runtimes) runs each fenced bash block
|
|
4
|
+
# in a separate process, so an inline gsd_run() function defined in an earlier
|
|
5
|
+
# block is undefined in later ones (issue #381). This executable is shipped beside
|
|
6
|
+
# gsd-tools.cjs in gsd-core/bin/ and exposed on PATH via the npm "bin" field and
|
|
7
|
+
# the per-file preamble's CLAUDE_ENV_FILE export, so later blocks resolve it.
|
|
8
|
+
# It resolves its own real location (following symlinks) and delegates to gsd-tools.cjs.
|
|
9
|
+
set -e
|
|
10
|
+
src="$0"
|
|
11
|
+
while [ -h "$src" ]; do
|
|
12
|
+
dir="$(cd -P "$(dirname "$src")" && pwd)"
|
|
13
|
+
src="$(readlink "$src")"
|
|
14
|
+
case "$src" in
|
|
15
|
+
/*) ;;
|
|
16
|
+
*) src="$dir/$src" ;;
|
|
17
|
+
esac
|
|
18
|
+
done
|
|
19
|
+
dir="$(cd -P "$(dirname "$src")" && pwd)"
|
|
20
|
+
exec node "$dir/gsd-tools.cjs" "$@"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Capability activation helpers.
|
|
4
|
+
*
|
|
5
|
+
* Shared by the Capability State Resolver and Loop Resolver so config-key
|
|
6
|
+
* activation uses one precedence chain and one prototype-pollution guard.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
14
|
+
const planningWorkspaceMod = require("./planning-workspace.cjs");
|
|
15
|
+
const { planningDir, planningRoot } = planningWorkspaceMod;
|
|
16
|
+
function _getNestedConfigValue(config, dotKey) {
|
|
17
|
+
const segments = dotKey.split('.');
|
|
18
|
+
let current = config;
|
|
19
|
+
for (const seg of segments) {
|
|
20
|
+
if (seg === '__proto__' || seg === 'constructor' || seg === 'prototype') {
|
|
21
|
+
return { found: false, value: undefined };
|
|
22
|
+
}
|
|
23
|
+
if (typeof current !== 'object' || current === null) {
|
|
24
|
+
return { found: false, value: undefined };
|
|
25
|
+
}
|
|
26
|
+
const cur = current;
|
|
27
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg)) {
|
|
28
|
+
return { found: false, value: undefined };
|
|
29
|
+
}
|
|
30
|
+
current = cur[seg];
|
|
31
|
+
}
|
|
32
|
+
return { found: true, value: current };
|
|
33
|
+
}
|
|
34
|
+
const _warnedRawConfigPaths = new Set();
|
|
35
|
+
function _readRawConfigKey(filePath, dotKey) {
|
|
36
|
+
try {
|
|
37
|
+
const raw = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
if (!_warnedRawConfigPaths.has(filePath)) {
|
|
44
|
+
_warnedRawConfigPaths.add(filePath);
|
|
45
|
+
try {
|
|
46
|
+
process.stderr.write(`gsd-tools: warning: failed to parse ${filePath} as JSON — skipping for activation resolution\n`);
|
|
47
|
+
}
|
|
48
|
+
catch { /* stderr might be closed */ }
|
|
49
|
+
}
|
|
50
|
+
return { found: false, value: undefined };
|
|
51
|
+
}
|
|
52
|
+
return _getNestedConfigValue(parsed, dotKey);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { found: false, value: undefined };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function _resolveActivationValue(dotKey, config, cwd, registry) {
|
|
59
|
+
const fromConfig = _getNestedConfigValue(config, dotKey);
|
|
60
|
+
if (fromConfig.found)
|
|
61
|
+
return Boolean(fromConfig.value);
|
|
62
|
+
if (cwd) {
|
|
63
|
+
const wsConfigPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
64
|
+
const rootConfigPath = node_path_1.default.join(planningRoot(cwd), 'config.json');
|
|
65
|
+
const fromWs = _readRawConfigKey(wsConfigPath, dotKey);
|
|
66
|
+
if (fromWs.found)
|
|
67
|
+
return Boolean(fromWs.value);
|
|
68
|
+
if (wsConfigPath !== rootConfigPath) {
|
|
69
|
+
const fromRoot = _readRawConfigKey(rootConfigPath, dotKey);
|
|
70
|
+
if (fromRoot.found)
|
|
71
|
+
return Boolean(fromRoot.value);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const schemaEntry = registry['configSchema']?.[dotKey];
|
|
75
|
+
if (schemaEntry && typeof schemaEntry === 'object' && schemaEntry !== null) {
|
|
76
|
+
const def = schemaEntry['default'];
|
|
77
|
+
if (def !== undefined)
|
|
78
|
+
return Boolean(def);
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
module.exports = {
|
|
83
|
+
_getNestedConfigValue,
|
|
84
|
+
_readRawConfigKey,
|
|
85
|
+
_resolveActivationValue,
|
|
86
|
+
};
|