@hegemonart/get-design-done 1.59.7 → 1.59.8
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +33 -0
- package/README.md +2 -2
- package/SKILL.md +1 -1
- package/agents/design-authority-watcher.md +24 -5
- package/bin/gdd-graph +4 -1
- package/hooks/_hook-emit.js +113 -29
- package/hooks/budget-enforcer.ts +44 -5
- package/hooks/gdd-mcp-circuit-breaker.js +72 -3
- package/hooks/gdd-sessionstart-recap.js +23 -14
- package/hooks/hooks.json +2 -2
- package/package.json +2 -2
- package/reference/bandit-integration.md +13 -2
- package/scripts/bootstrap.cjs +40 -8
- package/scripts/install.cjs +23 -1
- package/scripts/lib/bandit-router.cjs +47 -5
- package/scripts/lib/detect/cli.cjs +13 -3
- package/scripts/lib/install/converters/cursor.cjs +11 -19
- package/scripts/lib/install/installer.cjs +72 -21
- package/scripts/lib/install/merge.cjs +31 -3
- package/scripts/lib/install/runtime-artifact-layout.cjs +42 -8
- package/scripts/lib/manifest/harnesses.json +29 -1
- package/scripts/lib/manifest/skills.json +1 -1
- package/scripts/skill-templates/bandit-reset/SKILL.md +2 -0
- package/scripts/skill-templates/bandit-status/SKILL.md +4 -1
- package/scripts/skill-templates/darkmode/SKILL.md +1 -1
- package/scripts/skill-templates/graphify/SKILL.md +6 -6
- package/scripts/skill-templates/quick/SKILL.md +3 -1
- package/scripts/skill-templates/reflect/SKILL.md +1 -1
- package/scripts/skill-templates/router/SKILL.md +4 -2
- package/sdk/cli/index.js +114 -47
- package/sdk/dashboard/data/source.cjs +50 -4
- package/sdk/event-stream/writer.ts +112 -30
- package/sdk/mcp/gdd-mcp/server.js +49 -36
- package/sdk/mcp/gdd-mcp/tools/shared.ts +20 -2
- package/sdk/mcp/gdd-state/server.js +107 -41
- package/sdk/primitives/lockfile.cjs +26 -5
- package/sdk/state/index.ts +91 -17
- package/sdk/state/lockfile.ts +47 -8
- package/skills/bandit-reset/SKILL.md +2 -0
- package/skills/bandit-status/SKILL.md +4 -1
- package/skills/darkmode/SKILL.md +1 -1
- package/skills/graphify/SKILL.md +6 -6
- package/skills/quick/SKILL.md +3 -1
- package/skills/reflect/SKILL.md +1 -1
- package/skills/router/SKILL.md +4 -2
|
@@ -226,9 +226,21 @@ function commandsKind(destSubpath, prefix, converterPath, runtime) {
|
|
|
226
226
|
/**
|
|
227
227
|
* Build an `agents` artifact-kind descriptor.
|
|
228
228
|
*
|
|
229
|
-
* claude local only — passthrough copy from `<repo>/agents
|
|
229
|
+
* claude local only — passthrough copy from `<repo>/agents/*.md` into
|
|
230
230
|
* `<configDir>/agents/`. No converter.
|
|
231
231
|
*
|
|
232
|
+
* AR7 fix (Phase 59.8): the agent set is ENUMERATED from the `agents/`
|
|
233
|
+
* directory on disk — NOT derived from `ctx.skillNames`. Real agent files
|
|
234
|
+
* are named after agent roles (`design-planner.md`, `a11y-mapper.md`, …),
|
|
235
|
+
* which never coincide with skill directory names. The old skill-name-derived
|
|
236
|
+
* path read `agents/<skillName>.md`, found nothing for any skill, and staged
|
|
237
|
+
* ~96 empty `gdd-<skillName>.md` artifacts while installing ZERO real agents.
|
|
238
|
+
*
|
|
239
|
+
* Enumeration rules:
|
|
240
|
+
* - top-level `*.md` files in `agents/` only (no nested dirs),
|
|
241
|
+
* - `README.md` is excluded (it is documentation, not an agent),
|
|
242
|
+
* - empty / unreadable files are skipped (best-effort; never throws).
|
|
243
|
+
*
|
|
232
244
|
* @param {string} destSubpath
|
|
233
245
|
* @param {string} prefix
|
|
234
246
|
* @returns {ArtifactKind}
|
|
@@ -243,13 +255,35 @@ function agentsKind(destSubpath, prefix) {
|
|
|
243
255
|
path.dirname(ctx.skillsRoot),
|
|
244
256
|
'agents'
|
|
245
257
|
);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return
|
|
252
|
-
}
|
|
258
|
+
let entries;
|
|
259
|
+
try {
|
|
260
|
+
entries = fs.readdirSync(agentsRoot, { withFileTypes: true });
|
|
261
|
+
} catch {
|
|
262
|
+
// No agents/ dir on disk — stage nothing (never throw).
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
const staged = [];
|
|
266
|
+
for (const ent of entries) {
|
|
267
|
+
if (!ent.isFile()) continue;
|
|
268
|
+
if (!ent.name.toLowerCase().endsWith('.md')) continue;
|
|
269
|
+
if (ent.name.toLowerCase() === 'readme.md') continue;
|
|
270
|
+
// Strip any pre-existing gdd-/gsd- prefix on the agent filename before
|
|
271
|
+
// re-applying `prefix`, so an agent already named `gdd-foo.md` does not
|
|
272
|
+
// become `gdd-gdd-foo.md`. Real agents ship un-prefixed
|
|
273
|
+
// (`a11y-mapper.md`); this guard keeps both shapes correct.
|
|
274
|
+
const fileBase = ent.name.slice(0, -'.md'.length);
|
|
275
|
+
const bareName = fileBase.replace(/^(gdd-|gsd-)/i, '');
|
|
276
|
+
const srcPath = path.join(agentsRoot, ent.name);
|
|
277
|
+
let raw = '';
|
|
278
|
+
try {
|
|
279
|
+
raw = fs.readFileSync(srcPath, 'utf8');
|
|
280
|
+
} catch {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (!raw.trim()) continue; // skip empty agent files
|
|
284
|
+
staged.push({ srcPath, content: raw, name: prefix + bareName });
|
|
285
|
+
}
|
|
286
|
+
return staged;
|
|
253
287
|
},
|
|
254
288
|
};
|
|
255
289
|
}
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
"command_syntax": "/gdd:<skill>",
|
|
21
21
|
"mcp_support": true,
|
|
22
22
|
"placeholder_substitution": true,
|
|
23
|
+
"agents_support": true,
|
|
24
|
+
"hooks_support": true,
|
|
23
25
|
"install_path": "dist/claude-code/.claude/skills/",
|
|
24
26
|
"status": "tested"
|
|
25
27
|
},
|
|
26
28
|
"last_verified": "2026-06-02",
|
|
27
|
-
"capability_notes": "Host runtime. Marketplace-registered, end-to-end documented, Phase 42 golden baseline.",
|
|
29
|
+
"capability_notes": "Host runtime. Marketplace-registered, end-to-end documented, Phase 42 golden baseline. Sole runtime that receives the 64 sub-agents (claude --local installs agents/) and the hook layer (SessionStart / PostToolUse / statusLine).",
|
|
28
30
|
"fragment_links": [
|
|
29
31
|
"reference/runtime-models.md#claude---claude-code"
|
|
30
32
|
]
|
|
@@ -44,6 +46,8 @@
|
|
|
44
46
|
"command_syntax": "/gdd-<skill>",
|
|
45
47
|
"mcp_support": true,
|
|
46
48
|
"placeholder_substitution": true,
|
|
49
|
+
"agents_support": false,
|
|
50
|
+
"hooks_support": false,
|
|
47
51
|
"install_path": "dist/codex/.codex/skills/",
|
|
48
52
|
"status": "experimental"
|
|
49
53
|
},
|
|
@@ -71,6 +75,8 @@
|
|
|
71
75
|
"command_syntax": "/gdd:<skill>",
|
|
72
76
|
"mcp_support": true,
|
|
73
77
|
"placeholder_substitution": true,
|
|
78
|
+
"agents_support": false,
|
|
79
|
+
"hooks_support": false,
|
|
74
80
|
"install_path": "dist/gemini/.gemini/skills/",
|
|
75
81
|
"status": "experimental"
|
|
76
82
|
},
|
|
@@ -98,6 +104,8 @@
|
|
|
98
104
|
"command_syntax": "/gdd:<skill>",
|
|
99
105
|
"mcp_support": false,
|
|
100
106
|
"placeholder_substitution": true,
|
|
107
|
+
"agents_support": false,
|
|
108
|
+
"hooks_support": false,
|
|
101
109
|
"install_path": "dist/qwen/.qwen/skills/",
|
|
102
110
|
"status": "experimental"
|
|
103
111
|
},
|
|
@@ -124,6 +132,8 @@
|
|
|
124
132
|
"command_syntax": "/gdd:<skill>",
|
|
125
133
|
"mcp_support": false,
|
|
126
134
|
"placeholder_substitution": true,
|
|
135
|
+
"agents_support": false,
|
|
136
|
+
"hooks_support": false,
|
|
127
137
|
"install_path": "dist/kilo/.kilo/skills/",
|
|
128
138
|
"status": "untested"
|
|
129
139
|
},
|
|
@@ -148,6 +158,8 @@
|
|
|
148
158
|
"command_syntax": "/gdd:<skill>",
|
|
149
159
|
"mcp_support": false,
|
|
150
160
|
"placeholder_substitution": true,
|
|
161
|
+
"agents_support": false,
|
|
162
|
+
"hooks_support": false,
|
|
151
163
|
"install_path": "dist/copilot/.copilot/skills/",
|
|
152
164
|
"status": "experimental"
|
|
153
165
|
},
|
|
@@ -174,6 +186,8 @@
|
|
|
174
186
|
"command_syntax": "/gdd:<skill>",
|
|
175
187
|
"mcp_support": false,
|
|
176
188
|
"placeholder_substitution": true,
|
|
189
|
+
"agents_support": false,
|
|
190
|
+
"hooks_support": false,
|
|
177
191
|
"install_path": "dist/cursor/.cursor/skills/",
|
|
178
192
|
"status": "experimental"
|
|
179
193
|
},
|
|
@@ -200,6 +214,8 @@
|
|
|
200
214
|
"command_syntax": "/gdd:<skill>",
|
|
201
215
|
"mcp_support": false,
|
|
202
216
|
"placeholder_substitution": true,
|
|
217
|
+
"agents_support": false,
|
|
218
|
+
"hooks_support": false,
|
|
203
219
|
"install_path": "dist/windsurf/.windsurf/skills/",
|
|
204
220
|
"status": "untested"
|
|
205
221
|
},
|
|
@@ -224,6 +240,8 @@
|
|
|
224
240
|
"command_syntax": "/gdd:<skill>",
|
|
225
241
|
"mcp_support": false,
|
|
226
242
|
"placeholder_substitution": true,
|
|
243
|
+
"agents_support": false,
|
|
244
|
+
"hooks_support": false,
|
|
227
245
|
"install_path": "dist/antigravity/.antigravity/skills/",
|
|
228
246
|
"status": "untested"
|
|
229
247
|
},
|
|
@@ -248,6 +266,8 @@
|
|
|
248
266
|
"command_syntax": "/gdd:<skill>",
|
|
249
267
|
"mcp_support": false,
|
|
250
268
|
"placeholder_substitution": true,
|
|
269
|
+
"agents_support": false,
|
|
270
|
+
"hooks_support": false,
|
|
251
271
|
"install_path": "dist/augment/.augment/skills/",
|
|
252
272
|
"status": "untested"
|
|
253
273
|
},
|
|
@@ -272,6 +292,8 @@
|
|
|
272
292
|
"command_syntax": "/gdd:<skill>",
|
|
273
293
|
"mcp_support": false,
|
|
274
294
|
"placeholder_substitution": true,
|
|
295
|
+
"agents_support": false,
|
|
296
|
+
"hooks_support": false,
|
|
275
297
|
"install_path": "dist/trae/.trae/skills/",
|
|
276
298
|
"status": "untested"
|
|
277
299
|
},
|
|
@@ -296,6 +318,8 @@
|
|
|
296
318
|
"command_syntax": "/gdd:<skill>",
|
|
297
319
|
"mcp_support": false,
|
|
298
320
|
"placeholder_substitution": true,
|
|
321
|
+
"agents_support": false,
|
|
322
|
+
"hooks_support": false,
|
|
299
323
|
"install_path": "dist/codebuddy/.codebuddy/skills/",
|
|
300
324
|
"status": "untested"
|
|
301
325
|
},
|
|
@@ -320,6 +344,8 @@
|
|
|
320
344
|
"command_syntax": "/gdd:<skill>",
|
|
321
345
|
"mcp_support": false,
|
|
322
346
|
"placeholder_substitution": true,
|
|
347
|
+
"agents_support": false,
|
|
348
|
+
"hooks_support": false,
|
|
323
349
|
"install_path": "dist/cline/.cline/skills/",
|
|
324
350
|
"status": "untested"
|
|
325
351
|
},
|
|
@@ -344,6 +370,8 @@
|
|
|
344
370
|
"command_syntax": "/gdd:<skill>",
|
|
345
371
|
"mcp_support": false,
|
|
346
372
|
"placeholder_substitution": true,
|
|
373
|
+
"agents_support": false,
|
|
374
|
+
"hooks_support": false,
|
|
347
375
|
"install_path": "dist/opencode/.opencode/skills/",
|
|
348
376
|
"status": "untested"
|
|
349
377
|
},
|
|
@@ -541,7 +541,7 @@
|
|
|
541
541
|
},
|
|
542
542
|
{
|
|
543
543
|
"name": "router",
|
|
544
|
-
"description": "Routes a /gdd command to fast|quick|full path + S|M|L|XL complexity_class and returns {path, complexity_class, model_tier_overrides, resolved_models, estimated_cost_usd, cache_hits}.
|
|
544
|
+
"description": "Routes a /gdd command to fast|quick|full path + S|M|L|XL complexity_class and returns {path, complexity_class, model_tier_overrides, resolved_models, estimated_cost_usd, cache_hits}. A SKILL.md prompt the model executes to emit a routing-decision JSON from rule tables (no separate agent spawn). Optional/advisory - invoked only by the skills that opt into routing; the budget-enforcer hook tolerates its absence. Read by hooks/budget-enforcer.ts.",
|
|
545
545
|
"argument_hint": "<intent-string> [<target-artifacts-csv>]",
|
|
546
546
|
"tools": "Read, Bash, Grep"
|
|
547
547
|
},
|
|
@@ -31,6 +31,8 @@ No posterior file found at `.design/telemetry/posterior.json` — nothing to res
|
|
|
31
31
|
The next bandit pull with `adaptive_mode: full` will bootstrap a fresh posterior from informed priors. See `reference/bandit-integration.md`.
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
> Note: the posterior only learns (updates from outcomes) on the SDK / headless `session-runner` path. In interactive Claude Code with `adaptive_mode: full`, the bandit samples from the configured priors but does not currently update them in-session. A reset therefore re-bootstraps the priors the SDK path will subsequently learn from. See `reference/bandit-integration.md` ("Where adaptive routing actually learns").
|
|
35
|
+
|
|
34
36
|
If present, count the arms (`arms.length`, treating a missing/non-array `arms` as `0`) so the confirmation and receipt can report what will be cleared. A corrupted/unparseable file is still resettable - report `arms: unknown (file unparseable)` and continue.
|
|
35
37
|
|
|
36
38
|
### 2. Require explicit confirmation
|
|
@@ -33,10 +33,13 @@ Possible reasons:
|
|
|
33
33
|
- `adaptive_mode` is `static` or `hedge` (bandit silent — see `.design/budget.json`).
|
|
34
34
|
- No spawns have fired since Phase 27.5 wiring landed.
|
|
35
35
|
- Posterior was cleared via `{{command_prefix}}bandit-reset`.
|
|
36
|
+
- You are running in interactive Claude Code: the posterior is updated (learns) only on the SDK / headless `session-runner` path. In interactive `adaptive_mode: full` the bandit samples from configured priors but does not learn from in-session outcomes.
|
|
36
37
|
|
|
37
|
-
See `reference/bandit-integration.md` for setup guidance.
|
|
38
|
+
See `reference/bandit-integration.md` ("Where adaptive routing actually learns") for setup guidance.
|
|
38
39
|
```
|
|
39
40
|
|
|
41
|
+
> Note: the posterior only moves (learns) on the SDK / headless `session-runner` path. In interactive Claude Code with `adaptive_mode: full`, the bandit samples from the configured priors but does not currently update them in-session. See `reference/bandit-integration.md`.
|
|
42
|
+
|
|
40
43
|
Skip to Section 4 (Record). Parse failure (truncated/corrupted) → emit `Posterior file exists but is unparseable. Run {{command_prefix}}bandit-reset to start fresh, or restore from a backup.`
|
|
41
44
|
|
|
42
45
|
### 2. Parse the posterior
|
|
@@ -29,7 +29,7 @@ Output artifact prefix `DARKMODE-AUDIT` is distinct from the pipeline namespace
|
|
|
29
29
|
|
|
30
30
|
## Pre-Flight
|
|
31
31
|
|
|
32
|
-
Confirm source root exists. Try in order: `src/` (preferred), `app/` (Next.js App Router), `lib/` (libraries), `pages/` (Next.js Pages Router). Set `SRC_ROOT` to the first that exists. If none exist, abort: `"No source directory detected. Run /get-design-done
|
|
32
|
+
Confirm source root exists. Try in order: `src/` (preferred), `app/` (Next.js App Router), `lib/` (libraries), `pages/` (Next.js Pages Router). Set `SRC_ROOT` to the first that exists. If none exist, abort: `"No source directory detected. Run /get-design-done explore first."`
|
|
33
33
|
|
|
34
34
|
Confirm `.design/` exists (create if absent: `mkdir -p .design/`).
|
|
35
35
|
|
|
@@ -5,7 +5,7 @@ description: "Manage the Graphify knowledge graph for the current project. Build
|
|
|
5
5
|
|
|
6
6
|
# gdd-graphify
|
|
7
7
|
|
|
8
|
-
Thin command wrapper around the
|
|
8
|
+
Thin command wrapper around the get-design-done (GDD) graphify tools integration.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
@@ -30,10 +30,10 @@ Thin command wrapper around the GSD graphify tools integration.
|
|
|
30
30
|
```
|
|
31
31
|
STOP.
|
|
32
32
|
4. Execute the requested subcommand via the native CLI:
|
|
33
|
-
- build: `node bin/gdd-graph build`
|
|
34
|
-
- query: `node bin/gdd-graph query "<term>" --budget 2000`
|
|
35
|
-
- status: `node bin/gdd-graph status`
|
|
36
|
-
- diff: `node bin/gdd-graph diff`
|
|
33
|
+
- build: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" build`
|
|
34
|
+
- query: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" query "<term>" --budget 2000`
|
|
35
|
+
- status: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" status`
|
|
36
|
+
- diff: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" diff`
|
|
37
37
|
5. After `build` completes, update `.design/STATE.md` `<connections>`: `graphify: available`
|
|
38
38
|
|
|
39
39
|
## Required Reading
|
|
@@ -43,7 +43,7 @@ Thin command wrapper around the GSD graphify tools integration.
|
|
|
43
43
|
|
|
44
44
|
## Notes
|
|
45
45
|
|
|
46
|
-
- Graphify is optional. The native CLI ships
|
|
46
|
+
- Graphify is optional. The native CLI ships with the plugin at `${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph` (no external install - Node only).
|
|
47
47
|
- Graph is stored at `.design/graph/graph.json` (Ajv-validated against `scripts/lib/graph/schema.json`).
|
|
48
48
|
- Graph covers source code (`src/`, `components/`). It does NOT index `.design/` artifacts by default.
|
|
49
49
|
- Use `query` with node IDs from the graph schema: `component:<name>`, `token:color/<name>`, `decision:D-<nn>`, etc.
|
|
@@ -26,10 +26,12 @@ Fast pipeline run. Skips optional-quality agents for speed while keeping the cor
|
|
|
26
26
|
- Optional stage name (defaults to full pipeline from the current STATE.md position).
|
|
27
27
|
- `--skip <agent-name>` (repeatable) adds to the skip list.
|
|
28
28
|
2. Read `.design/STATE.md` to determine entry stage if none was passed.
|
|
29
|
-
3. For each stage to execute,
|
|
29
|
+
3. For each stage to execute, invoke the stage skill but spawn it with the optional agents in the effective skip list **omitted from the spawn graph** - this skill is the orchestrator, so it simply does not call those agents (the stage skills do not read a `quick_mode` flag; the skipping happens here, by not spawning them). The kept agents run exactly as in the full pipeline.
|
|
30
30
|
4. After each stage, print: "Stage <name> done. Skipped: <list>."
|
|
31
31
|
5. Final summary prints which agents were skipped across the full run.
|
|
32
32
|
|
|
33
|
+
Mechanism note: `{{command_prefix}}quick` is a lighter-touch *invocation* of the normal stages, not a special stage mode. It reduces ceremony by leaving the listed optional-quality agents out of the spawn graph it orchestrates. There is no flag the stage skills parse - if invoked directly (not via this skill) the stages run their full agent set.
|
|
34
|
+
|
|
33
35
|
## Use When
|
|
34
36
|
|
|
35
37
|
- You trust the problem scope (no need for fresh research).
|
|
@@ -37,7 +37,7 @@ Run `design-reflector` on demand against the current (or specified) cycle. Produ
|
|
|
37
37
|
See @skills/reflect/procedures/capability-gap-scan.md for the full procedure.
|
|
38
38
|
The `design-reflector` agent runs the scan automatically as part of its reflection pass; this step lets users dry-run it independently with:
|
|
39
39
|
```
|
|
40
|
-
node scripts/lib/reflector/capability-gap-scan.cjs --dry-run
|
|
40
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/lib/reflector/capability-gap-scan.cjs" --dry-run
|
|
41
41
|
```
|
|
42
42
|
The scan emits `capability_gap` events (`source: "reflector_pattern"`) for recurring patterns lacking a dedicated executable owner; Plan 29-03 aggregates these for `{{command_prefix}}apply-reflections`.
|
|
43
43
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gdd-router
|
|
3
|
-
description: "Routes a /gdd command to fast|quick|full path + S|M|L|XL complexity_class and returns {path, complexity_class, model_tier_overrides, resolved_models, estimated_cost_usd, cache_hits}.
|
|
3
|
+
description: "Routes a /gdd command to fast|quick|full path + S|M|L|XL complexity_class and returns {path, complexity_class, model_tier_overrides, resolved_models, estimated_cost_usd, cache_hits}. A SKILL.md prompt the model executes to emit a routing-decision JSON from rule tables (no separate agent spawn). Optional/advisory - invoked only by the skills that opt into routing; the budget-enforcer hook tolerates its absence. Read by hooks/budget-enforcer.ts."
|
|
4
4
|
argument-hint: "<intent-string> [<target-artifacts-csv>]"
|
|
5
5
|
tools: Read, Bash, Grep
|
|
6
6
|
---
|
|
@@ -69,7 +69,9 @@ Delegate to `skills/cache-manager/SKILL.md` (Plan 10.1-02). The router lists can
|
|
|
69
69
|
|
|
70
70
|
## Integration Point
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
The router is **optional and advisory**, not a universal first step. Only the handful of skills that explicitly opt into routing reference it (today: the root pipeline `SKILL.md` / `{{command_prefix}}handoff`, and `{{command_prefix}}style` documents that it deliberately does *not* invoke the router because it is a leaf invocation). The pipeline stage skills (explore / plan / design / verify) do **not** spawn the router. When a skill does invoke it, the flow is: invoke the router via `Task` or inline invocation; receive the JSON blob; pass it to downstream agents as context so the budget-enforcer hook has the router decision available in tool_input metadata when the first Agent spawn fires.
|
|
73
|
+
|
|
74
|
+
When no skill supplies a router decision, the budget-enforcer hook reads `tool_input.context.router_decision` as absent and falls back to its legacy back-compat path - the router's absence is tolerated by design, never an error.
|
|
73
75
|
|
|
74
76
|
## Failure Modes
|
|
75
77
|
|
package/sdk/cli/index.js
CHANGED
|
@@ -82,44 +82,69 @@ var init_emitter = __esm({
|
|
|
82
82
|
|
|
83
83
|
// sdk/event-stream/writer.ts
|
|
84
84
|
function _findRepoRoot() {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
return _walkToPackageJson(process.cwd());
|
|
86
|
+
}
|
|
87
|
+
function _walkToPackageJson(startDir) {
|
|
88
|
+
let dir = startDir;
|
|
89
|
+
for (let i = 0; i < 12; i++) {
|
|
87
90
|
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "package.json"))) return dir;
|
|
88
91
|
const parent = (0, import_node_path.dirname)(dir);
|
|
89
92
|
if (parent === dir) break;
|
|
90
93
|
dir = parent;
|
|
91
94
|
}
|
|
92
|
-
return
|
|
95
|
+
return startDir;
|
|
96
|
+
}
|
|
97
|
+
function _warnRedactUnavailable() {
|
|
98
|
+
if (_redactWarned) return;
|
|
99
|
+
_redactWarned = true;
|
|
100
|
+
try {
|
|
101
|
+
process.stderr.write(
|
|
102
|
+
"[event-stream] WARNING: scripts/lib/redact.cjs could not be loaded \u2014 failing CLOSED: event payloads are dropped (envelope-only) to avoid writing unscrubbed secrets. Run the event writer from inside the plugin tree or set the redact lib on PATH to restore full payloads.\n"
|
|
103
|
+
);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
93
106
|
}
|
|
94
|
-
|
|
107
|
+
function _loadRedact() {
|
|
108
|
+
const candidates = [];
|
|
109
|
+
const entry = process.argv[1];
|
|
110
|
+
if (typeof entry === "string" && entry.length > 0) {
|
|
111
|
+
const entryAbs = (0, import_node_path.isAbsolute)(entry) ? entry : (0, import_node_path.resolve)(entry);
|
|
112
|
+
const entryRoot = _walkToPackageJson((0, import_node_path.dirname)(entryAbs));
|
|
113
|
+
candidates.push((0, import_node_path.resolve)(entryRoot, "scripts/lib/redact.cjs"));
|
|
114
|
+
}
|
|
115
|
+
const repoRoot = _findRepoRoot();
|
|
116
|
+
candidates.push((0, import_node_path.resolve)(repoRoot, "scripts/lib/redact.cjs"));
|
|
117
|
+
candidates.push((0, import_node_path.resolve)(repoRoot, "..", "..", "scripts/lib/redact.cjs"));
|
|
118
|
+
for (const candidate of candidates) {
|
|
119
|
+
try {
|
|
120
|
+
if (!(0, import_node_fs.existsSync)(candidate)) continue;
|
|
121
|
+
const req = (0, import_node_module.createRequire)(candidate);
|
|
122
|
+
const mod = req(candidate);
|
|
123
|
+
if (mod && typeof mod.redact === "function") return mod.redact;
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
var import_node_fs, import_node_path, import_node_module, _redactWarned, _realRedact, redact, DEFAULT_EVENTS_PATH, DEFAULT_MAX_LINE_BYTES, EventWriter;
|
|
95
130
|
var init_writer = __esm({
|
|
96
131
|
"sdk/event-stream/writer.ts"() {
|
|
97
132
|
"use strict";
|
|
98
133
|
import_node_fs = require("node:fs");
|
|
99
134
|
import_node_path = require("node:path");
|
|
100
135
|
import_node_module = require("node:module");
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const _altCandidate = (0, import_node_path.resolve)(_altRoot, "scripts/lib/redact.cjs");
|
|
111
|
-
if ((0, import_node_fs.existsSync)(_altCandidate)) {
|
|
112
|
-
const _altRequire = (0, import_node_module.createRequire)((0, import_node_path.join)(_altRoot, "package.json"));
|
|
113
|
-
const _altMod = _altRequire(_altCandidate);
|
|
114
|
-
_redact = _altMod.redact;
|
|
115
|
-
} else {
|
|
116
|
-
_redact = (v) => v;
|
|
117
|
-
}
|
|
136
|
+
_redactWarned = false;
|
|
137
|
+
_realRedact = _loadRedact();
|
|
138
|
+
redact = _realRedact !== null ? _realRedact : (v) => {
|
|
139
|
+
_warnRedactUnavailable();
|
|
140
|
+
if (v !== null && typeof v === "object") {
|
|
141
|
+
const ev = v;
|
|
142
|
+
const out = { ...ev };
|
|
143
|
+
out["payload"] = { _redaction_unavailable: true };
|
|
144
|
+
return out;
|
|
118
145
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
redact = _redact;
|
|
146
|
+
return { _redaction_unavailable: true };
|
|
147
|
+
};
|
|
123
148
|
DEFAULT_EVENTS_PATH = ".design/telemetry/events.jsonl";
|
|
124
149
|
DEFAULT_MAX_LINE_BYTES = 64 * 1024;
|
|
125
150
|
EventWriter = class {
|
|
@@ -2435,6 +2460,7 @@ function getLogger() {
|
|
|
2435
2460
|
// sdk/state/index.ts
|
|
2436
2461
|
var import_node_fs5 = require("node:fs");
|
|
2437
2462
|
var import_node_path4 = require("node:path");
|
|
2463
|
+
var import_node_module2 = require("node:module");
|
|
2438
2464
|
|
|
2439
2465
|
// sdk/state/lockfile.ts
|
|
2440
2466
|
var import_node_fs4 = require("node:fs");
|
|
@@ -2501,6 +2527,14 @@ async function acquire(path, opts = {}) {
|
|
|
2501
2527
|
}
|
|
2502
2528
|
const parsed = parseLock(existing);
|
|
2503
2529
|
if (parsed !== null && isStale(parsed, staleMs)) {
|
|
2530
|
+
const confirm = readLockSafe(lockPath);
|
|
2531
|
+
if (confirm === null) {
|
|
2532
|
+
continue;
|
|
2533
|
+
}
|
|
2534
|
+
if (confirm !== existing) {
|
|
2535
|
+
await sleep(pollMs);
|
|
2536
|
+
continue;
|
|
2537
|
+
}
|
|
2504
2538
|
try {
|
|
2505
2539
|
(0, import_node_fs4.unlinkSync)(lockPath);
|
|
2506
2540
|
} catch (delErr) {
|
|
@@ -2559,10 +2593,14 @@ function parseLock(raw) {
|
|
|
2559
2593
|
}
|
|
2560
2594
|
}
|
|
2561
2595
|
function isStale(payload, staleMs) {
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2596
|
+
const pidRecorded = typeof payload.pid === "number" && Number.isInteger(payload.pid) && payload.pid > 0;
|
|
2597
|
+
if (!pidRecorded) {
|
|
2598
|
+
const acquiredAt = Date.parse(payload.acquired_at);
|
|
2599
|
+
if (!Number.isFinite(acquiredAt)) return true;
|
|
2600
|
+
return Date.now() - acquiredAt > staleMs;
|
|
2601
|
+
}
|
|
2602
|
+
if (isPidAlive(payload.pid, payload.host)) return false;
|
|
2603
|
+
return true;
|
|
2566
2604
|
}
|
|
2567
2605
|
function isPidAlive(pid, host) {
|
|
2568
2606
|
if (host !== (0, import_node_os2.hostname)()) {
|
|
@@ -3969,6 +4007,8 @@ function gateFor(from, to) {
|
|
|
3969
4007
|
}
|
|
3970
4008
|
|
|
3971
4009
|
// sdk/state/index.ts
|
|
4010
|
+
var _moduleDir = typeof __dirname !== "undefined" ? __dirname : (0, import_node_path4.dirname)(process.argv[1] || process.cwd());
|
|
4011
|
+
var _require = typeof require !== "undefined" ? require : (0, import_node_module2.createRequire)(process.argv[1] || process.cwd());
|
|
3972
4012
|
function _findPackageRoot(startDir) {
|
|
3973
4013
|
let dir = (0, import_node_path4.resolve)(startDir);
|
|
3974
4014
|
let firstWithPkg = null;
|
|
@@ -3976,7 +4016,7 @@ function _findPackageRoot(startDir) {
|
|
|
3976
4016
|
const pkgPath = (0, import_node_path4.join)(dir, "package.json");
|
|
3977
4017
|
if ((0, import_node_fs5.existsSync)(pkgPath)) {
|
|
3978
4018
|
try {
|
|
3979
|
-
const pkg =
|
|
4019
|
+
const pkg = _require(pkgPath);
|
|
3980
4020
|
if (firstWithPkg === null) firstWithPkg = dir;
|
|
3981
4021
|
if (pkg.name === "@hegemonart/get-design-done") return dir;
|
|
3982
4022
|
} catch {
|
|
@@ -3993,7 +4033,7 @@ var _backendCache = null;
|
|
|
3993
4033
|
function _loadBackend() {
|
|
3994
4034
|
if (_backendCache !== null) return _backendCache === false ? null : _backendCache;
|
|
3995
4035
|
try {
|
|
3996
|
-
const pkgRoot = _findPackageRoot(
|
|
4036
|
+
const pkgRoot = _findPackageRoot(_moduleDir);
|
|
3997
4037
|
if (pkgRoot === null) {
|
|
3998
4038
|
_backendCache = false;
|
|
3999
4039
|
return null;
|
|
@@ -4003,7 +4043,7 @@ function _loadBackend() {
|
|
|
4003
4043
|
_backendCache = false;
|
|
4004
4044
|
return null;
|
|
4005
4045
|
}
|
|
4006
|
-
_backendCache =
|
|
4046
|
+
_backendCache = _require(backendPath);
|
|
4007
4047
|
return _backendCache;
|
|
4008
4048
|
} catch {
|
|
4009
4049
|
_backendCache = false;
|
|
@@ -4128,14 +4168,41 @@ async function transition(path, toStage) {
|
|
|
4128
4168
|
throw new TransitionGateFailed(toStage, gateResult.blockers);
|
|
4129
4169
|
}
|
|
4130
4170
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4171
|
+
let lockedFailure = null;
|
|
4172
|
+
let lockedBlockers = gateResult.blockers;
|
|
4173
|
+
try {
|
|
4174
|
+
const nextState = await mutate(path, (s) => {
|
|
4175
|
+
const fromNow = s.position.stage;
|
|
4176
|
+
if (!isStage(fromNow)) {
|
|
4177
|
+
lockedFailure = new TransitionGateFailed(toStage, [
|
|
4178
|
+
`Invalid transition: from="${fromNow}" is not a recognized Stage (changed under lock)`
|
|
4179
|
+
]);
|
|
4180
|
+
throw lockedFailure;
|
|
4181
|
+
}
|
|
4182
|
+
const gateNow = gateFor(fromNow, toStage);
|
|
4183
|
+
if (gateNow === null) {
|
|
4184
|
+
lockedFailure = new TransitionGateFailed(toStage, [
|
|
4185
|
+
`Invalid transition: ${fromNow} \u2192 ${toStage} (changed under lock)`
|
|
4186
|
+
]);
|
|
4187
|
+
throw lockedFailure;
|
|
4188
|
+
}
|
|
4189
|
+
const resultNow = gateNow(s);
|
|
4190
|
+
if (!resultNow.pass) {
|
|
4191
|
+
lockedFailure = new TransitionGateFailed(toStage, resultNow.blockers);
|
|
4192
|
+
throw lockedFailure;
|
|
4193
|
+
}
|
|
4194
|
+
lockedBlockers = resultNow.blockers;
|
|
4195
|
+
s.frontmatter.stage = toStage;
|
|
4196
|
+
s.frontmatter.last_checkpoint = nowIso;
|
|
4197
|
+
s.position.stage = toStage;
|
|
4198
|
+
s.timestamps[`${toStage}_started_at`] = nowIso;
|
|
4199
|
+
return s;
|
|
4200
|
+
});
|
|
4201
|
+
return { pass: true, blockers: lockedBlockers, state: nextState };
|
|
4202
|
+
} catch (err) {
|
|
4203
|
+
if (lockedFailure !== null && err === lockedFailure) throw lockedFailure;
|
|
4204
|
+
throw err;
|
|
4205
|
+
}
|
|
4139
4206
|
}
|
|
4140
4207
|
|
|
4141
4208
|
// scripts/lib/pipeline-runner/state-machine.ts
|
|
@@ -4982,7 +5049,7 @@ function collapseBlankLines(text) {
|
|
|
4982
5049
|
}
|
|
4983
5050
|
|
|
4984
5051
|
// scripts/lib/session-runner/errors.ts
|
|
4985
|
-
var
|
|
5052
|
+
var import_node_module3 = require("node:module");
|
|
4986
5053
|
var import_node_fs8 = require("node:fs");
|
|
4987
5054
|
var import_node_path7 = require("node:path");
|
|
4988
5055
|
function findRepoRoot() {
|
|
@@ -4996,7 +5063,7 @@ function findRepoRoot() {
|
|
|
4996
5063
|
return process.cwd();
|
|
4997
5064
|
}
|
|
4998
5065
|
var REPO_ROOT = findRepoRoot();
|
|
4999
|
-
var nodeRequire = (0,
|
|
5066
|
+
var nodeRequire = (0, import_node_module3.createRequire)((0, import_node_path7.join)(REPO_ROOT, "package.json"));
|
|
5000
5067
|
var transportClassifier = nodeRequire(
|
|
5001
5068
|
(0, import_node_path7.resolve)(REPO_ROOT, "sdk/primitives/error-classifier.cjs")
|
|
5002
5069
|
);
|
|
@@ -5348,7 +5415,7 @@ var TranscriptWriter = class {
|
|
|
5348
5415
|
};
|
|
5349
5416
|
|
|
5350
5417
|
// scripts/lib/session-runner/index.ts
|
|
5351
|
-
var
|
|
5418
|
+
var import_node_module4 = require("node:module");
|
|
5352
5419
|
var import_node_fs10 = require("node:fs");
|
|
5353
5420
|
var import_node_path9 = require("node:path");
|
|
5354
5421
|
function _findRepoRoot2() {
|
|
@@ -5362,7 +5429,7 @@ function _findRepoRoot2() {
|
|
|
5362
5429
|
return process.cwd();
|
|
5363
5430
|
}
|
|
5364
5431
|
var _REPO_ROOT = _findRepoRoot2();
|
|
5365
|
-
var _nodeRequire = (0,
|
|
5432
|
+
var _nodeRequire = (0, import_node_module4.createRequire)((0, import_node_path9.join)(_REPO_ROOT, "package.json"));
|
|
5366
5433
|
var jitteredBackoff = _nodeRequire(
|
|
5367
5434
|
(0, import_node_path9.resolve)(_REPO_ROOT, "sdk/primitives/jittered-backoff.cjs")
|
|
5368
5435
|
);
|
|
@@ -9731,7 +9798,7 @@ ${BUILD_USAGE}`);
|
|
|
9731
9798
|
|
|
9732
9799
|
// sdk/cli/commands/dashboard.ts
|
|
9733
9800
|
var import_node_child_process2 = require("node:child_process");
|
|
9734
|
-
var
|
|
9801
|
+
var import_node_module5 = require("node:module");
|
|
9735
9802
|
var import_node_http = require("node:http");
|
|
9736
9803
|
var import_node_fs23 = require("node:fs");
|
|
9737
9804
|
var import_node_path22 = require("node:path");
|
|
@@ -9767,7 +9834,7 @@ function anchorDirs() {
|
|
|
9767
9834
|
return out;
|
|
9768
9835
|
}
|
|
9769
9836
|
function climbToMarker(startDir) {
|
|
9770
|
-
const req = (0,
|
|
9837
|
+
const req = (0, import_node_module5.createRequire)((0, import_node_path22.join)(startDir, "noop.js"));
|
|
9771
9838
|
let dir = startDir;
|
|
9772
9839
|
let firstWithPkg = null;
|
|
9773
9840
|
for (let i = 0; i < 12; i++) {
|
|
@@ -9803,7 +9870,7 @@ function findPackageRoot() {
|
|
|
9803
9870
|
}
|
|
9804
9871
|
function requireFromRoot(relPath) {
|
|
9805
9872
|
const root = findPackageRoot();
|
|
9806
|
-
const req = (0,
|
|
9873
|
+
const req = (0, import_node_module5.createRequire)((0, import_node_path22.join)(root, "noop.js"));
|
|
9807
9874
|
return req((0, import_node_path22.join)(root, relPath));
|
|
9808
9875
|
}
|
|
9809
9876
|
function resolveRoot(deps, flags) {
|