@hegemonart/get-design-done 1.59.7 → 1.59.9
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 +59 -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 +104 -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/reference/prices/claude.md +11 -0
- package/reference/runtime-models.md +9 -9
- package/reference/schemas/generated.d.ts +4 -0
- package/reference/schemas/runtime-models.schema.json +5 -0
- package/scripts/bootstrap.cjs +40 -8
- package/scripts/install.cjs +23 -1
- package/scripts/lib/bandit-router.cjs +47 -5
- package/scripts/lib/budget-enforcer.cjs +34 -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/parse-runtime-models.cjs +9 -1
- 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/lib/model-id.cjs +141 -0
- package/scripts/lib/session-runner/index.ts +87 -16
- 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 +132 -55
- 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
|
@@ -122,6 +122,21 @@ const adaptiveModeLib = _nodeRequire(
|
|
|
122
122
|
getMode: (opts?: { baseDir?: string; budgetPath?: string; quiet?: boolean }) => 'static' | 'hedge' | 'full';
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
+
// ── Phase 59-9 — model-id normalization + tiering (single source of truth) ───
|
|
126
|
+
//
|
|
127
|
+
// `scripts/lib/model-id.cjs` is the canonical id parser shared with the
|
|
128
|
+
// budget-enforcer. We route BOTH tier-labeling (`tierFromModel`) and pricing
|
|
129
|
+
// (`rateFor`) through it so a new model family is a DATA edit there / in the
|
|
130
|
+
// price tables, never scattered substring logic here. `tierForModelId` returns
|
|
131
|
+
// `null` for an unknown family — callers MUST treat that as "price
|
|
132
|
+
// conservatively + loudly", never as a tier or as free.
|
|
133
|
+
const modelId = _nodeRequire(
|
|
134
|
+
_resolve(_REPO_ROOT, 'scripts/lib/model-id.cjs'),
|
|
135
|
+
) as {
|
|
136
|
+
normalizeModelId: (id: string | null | undefined) => { base: string; variant: string | null };
|
|
137
|
+
tierForModelId: (id: string | null | undefined) => 'opus' | 'sonnet' | 'haiku' | null;
|
|
138
|
+
};
|
|
139
|
+
|
|
125
140
|
/** Rate-guard provider key for the Anthropic Agent SDK. */
|
|
126
141
|
const RATE_GUARD_PROVIDER = 'anthropic';
|
|
127
142
|
|
|
@@ -144,16 +159,24 @@ const SESSION_RUNNER_DEFAULT_BIN = 'medium';
|
|
|
144
159
|
*
|
|
145
160
|
* Used at the 4 terminal-emit sites where the final tier isn't already
|
|
146
161
|
* carried on `opts` — we fall back to inspecting `usage.model` (folded
|
|
147
|
-
* during the run loop from SDK chunks).
|
|
148
|
-
*
|
|
149
|
-
*
|
|
162
|
+
* during the run loop from SDK chunks). Delegates to the shared
|
|
163
|
+
* `model-id.cjs` resolver (variant suffix like `[1m]` is stripped, known
|
|
164
|
+
* ids classified identically to before).
|
|
165
|
+
*
|
|
166
|
+
* The shared resolver returns `null` for an UNKNOWN family. For tier
|
|
167
|
+
* LABELING (telemetry / posterior arms) we map null → 'sonnet' as the
|
|
168
|
+
* safest middle tier so the bandit arms stay well-defined. This is a
|
|
169
|
+
* TELEMETRY default only — it does NOT influence PRICING. Pricing of an
|
|
170
|
+
* unknown family uses the conservative OPUS ceiling, resolved separately in
|
|
171
|
+
* `rateFor` (see DEFAULT_MODEL_RATE / tier fallback there). Keep the two
|
|
172
|
+
* concerns distinct: a wrong tier label mis-attributes a posterior arm; a
|
|
173
|
+
* wrong price under-bills a frontier model.
|
|
150
174
|
*/
|
|
151
175
|
function tierFromModel(modelName: string | null | undefined): 'opus' | 'sonnet' | 'haiku' {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return 'sonnet';
|
|
176
|
+
const tier = modelId.tierForModelId(modelName);
|
|
177
|
+
// null = unknown family → conservative TELEMETRY default (pricing handled
|
|
178
|
+
// separately + conservatively in rateFor).
|
|
179
|
+
return tier ?? 'sonnet';
|
|
157
180
|
}
|
|
158
181
|
|
|
159
182
|
/**
|
|
@@ -539,29 +562,72 @@ function _logPeerCallComplete(args: {
|
|
|
539
562
|
const RETRY_BACKOFF = { baseMs: 1000, maxMs: 30_000 } as const;
|
|
540
563
|
|
|
541
564
|
/**
|
|
542
|
-
* Per-million-token USD rates.
|
|
543
|
-
*
|
|
565
|
+
* Per-million-token USD rates.
|
|
566
|
+
*
|
|
567
|
+
* Canonical price source is `reference/prices/claude.md`; this table mirrors
|
|
568
|
+
* it for the sync headless path — keep in lockstep.
|
|
569
|
+
*
|
|
570
|
+
* Unknown FAMILIES default to the OPUS ceiling (see DEFAULT_MODEL_RATE) — a
|
|
571
|
+
* conservative overestimate. We'd rather cap early than silently under-bill a
|
|
572
|
+
* frontier model. Known families fall back to their per-tier representative
|
|
573
|
+
* rate (PER_TIER_RATE) so a dated/variant sku still prices correctly.
|
|
544
574
|
*/
|
|
545
575
|
const MODEL_RATES: Readonly<Record<string, { input: number; output: number }>> = Object.freeze({
|
|
576
|
+
'claude-opus-4-8': { input: 15, output: 75 },
|
|
546
577
|
'claude-opus-4-7': { input: 15, output: 75 },
|
|
547
578
|
'claude-sonnet-4-5': { input: 3, output: 15 },
|
|
548
579
|
'claude-haiku-4-5': { input: 0.8, output: 4 },
|
|
549
580
|
});
|
|
550
|
-
const DEFAULT_MODEL_RATE = Object.freeze({ input: 3, output: 15 });
|
|
551
581
|
|
|
552
|
-
/**
|
|
582
|
+
/** Per-tier representative rates (match reference/prices/claude.md). Used as
|
|
583
|
+
* the fallback when an exact/prefix MODEL_RATES match is absent but the
|
|
584
|
+
* family tier is known. */
|
|
585
|
+
const PER_TIER_RATE: Readonly<Record<'opus' | 'sonnet' | 'haiku', { input: number; output: number }>> =
|
|
586
|
+
Object.freeze({
|
|
587
|
+
opus: { input: 15, output: 75 },
|
|
588
|
+
sonnet: { input: 3, output: 15 },
|
|
589
|
+
haiku: { input: 1, output: 5 },
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* DEFAULT_MODEL_RATE — conservative ceiling for a GENUINELY UNKNOWN family
|
|
594
|
+
* (tier resolves to null). Set to the OPUS rate, matching this file's own
|
|
595
|
+
* "safer overestimate" intent. The previous sonnet default UNDER-billed any
|
|
596
|
+
* frontier model whose id we did not yet recognize.
|
|
597
|
+
*/
|
|
598
|
+
const DEFAULT_MODEL_RATE = Object.freeze({ input: 15, output: 75 });
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Resolve a per-M-token rate for a model name.
|
|
602
|
+
*
|
|
603
|
+
* Resolution order (conservative + robust):
|
|
604
|
+
* 1. normalize the id (strip `[1m]`/`[200k]` variant suffix) → work on base;
|
|
605
|
+
* 2. exact match in MODEL_RATES;
|
|
606
|
+
* 3. prefix match (e.g. "claude-opus-4-7-20250101" → "claude-opus-4-7");
|
|
607
|
+
* 4. per-tier fallback via `tierForModelId(base)` (opus/sonnet/haiku → that
|
|
608
|
+
* tier's representative rate) — keeps dated/variant skus of a known
|
|
609
|
+
* family priced correctly;
|
|
610
|
+
* 5. ONLY if the tier is null (genuinely unknown family) → DEFAULT_MODEL_RATE
|
|
611
|
+
* (opus ceiling — price LOUDLY + CONSERVATIVELY, never $0 or sonnet).
|
|
612
|
+
*/
|
|
553
613
|
function rateFor(modelName: string | null): { input: number; output: number } {
|
|
554
614
|
if (modelName === null || modelName === '') return DEFAULT_MODEL_RATE;
|
|
555
|
-
|
|
556
|
-
|
|
615
|
+
const { base } = modelId.normalizeModelId(modelName);
|
|
616
|
+
if (base === '') return DEFAULT_MODEL_RATE;
|
|
617
|
+
// (2) Direct match first.
|
|
618
|
+
const direct = MODEL_RATES[base];
|
|
557
619
|
if (direct !== undefined) return direct;
|
|
558
|
-
// Prefix match
|
|
620
|
+
// (3) Prefix match.
|
|
559
621
|
for (const key of Object.keys(MODEL_RATES)) {
|
|
560
|
-
if (
|
|
622
|
+
if (base.startsWith(key)) {
|
|
561
623
|
const hit = MODEL_RATES[key];
|
|
562
624
|
if (hit !== undefined) return hit;
|
|
563
625
|
}
|
|
564
626
|
}
|
|
627
|
+
// (4) Per-tier fallback for a known family.
|
|
628
|
+
const tier = modelId.tierForModelId(base);
|
|
629
|
+
if (tier !== null) return PER_TIER_RATE[tier];
|
|
630
|
+
// (5) Unknown family → conservative opus ceiling.
|
|
565
631
|
return DEFAULT_MODEL_RATE;
|
|
566
632
|
}
|
|
567
633
|
|
|
@@ -1281,3 +1347,8 @@ function buildResult(args: BuildResultArgs): SessionResult {
|
|
|
1281
1347
|
// invariant: session-runner consumers can rely on these constants being
|
|
1282
1348
|
// stable across minor releases.
|
|
1283
1349
|
export { MODEL_RATES, DEFAULT_MODEL_RATE, RATE_GUARD_PROVIDER };
|
|
1350
|
+
|
|
1351
|
+
// Pricing internals exported for regression tests (Phase 59-9 model-cost-truth):
|
|
1352
|
+
// verify unknown families resolve to the conservative opus ceiling while known
|
|
1353
|
+
// families price correctly via the per-tier fallback.
|
|
1354
|
+
export { rateFor, usdCost, tierFromModel, PER_TIER_RATE };
|
|
@@ -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;
|
|
93
96
|
}
|
|
94
|
-
|
|
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
|
+
}
|
|
106
|
+
}
|
|
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
|
);
|
|
@@ -5375,15 +5442,15 @@ var banditIntegration = _nodeRequire(
|
|
|
5375
5442
|
var adaptiveModeLib = _nodeRequire(
|
|
5376
5443
|
(0, import_node_path9.resolve)(_REPO_ROOT, "scripts/lib/adaptive-mode.cjs")
|
|
5377
5444
|
);
|
|
5445
|
+
var modelId = _nodeRequire(
|
|
5446
|
+
(0, import_node_path9.resolve)(_REPO_ROOT, "scripts/lib/model-id.cjs")
|
|
5447
|
+
);
|
|
5378
5448
|
var RATE_GUARD_PROVIDER = "anthropic";
|
|
5379
5449
|
var DEFAULT_MAX_RETRIES = 2;
|
|
5380
5450
|
var SESSION_RUNNER_DEFAULT_BIN = "medium";
|
|
5381
5451
|
function tierFromModel(modelName) {
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
if (lower.includes("opus")) return "opus";
|
|
5385
|
-
if (lower.includes("haiku")) return "haiku";
|
|
5386
|
-
return "sonnet";
|
|
5452
|
+
const tier = modelId.tierForModelId(modelName);
|
|
5453
|
+
return tier ?? "sonnet";
|
|
5387
5454
|
}
|
|
5388
5455
|
function _recordBanditOutcome(input) {
|
|
5389
5456
|
try {
|
|
@@ -5592,21 +5659,31 @@ function _logPeerCallComplete(args) {
|
|
|
5592
5659
|
}
|
|
5593
5660
|
var RETRY_BACKOFF = { baseMs: 1e3, maxMs: 3e4 };
|
|
5594
5661
|
var MODEL_RATES = Object.freeze({
|
|
5662
|
+
"claude-opus-4-8": { input: 15, output: 75 },
|
|
5595
5663
|
"claude-opus-4-7": { input: 15, output: 75 },
|
|
5596
5664
|
"claude-sonnet-4-5": { input: 3, output: 15 },
|
|
5597
5665
|
"claude-haiku-4-5": { input: 0.8, output: 4 }
|
|
5598
5666
|
});
|
|
5599
|
-
var
|
|
5667
|
+
var PER_TIER_RATE = Object.freeze({
|
|
5668
|
+
opus: { input: 15, output: 75 },
|
|
5669
|
+
sonnet: { input: 3, output: 15 },
|
|
5670
|
+
haiku: { input: 1, output: 5 }
|
|
5671
|
+
});
|
|
5672
|
+
var DEFAULT_MODEL_RATE = Object.freeze({ input: 15, output: 75 });
|
|
5600
5673
|
function rateFor(modelName) {
|
|
5601
5674
|
if (modelName === null || modelName === "") return DEFAULT_MODEL_RATE;
|
|
5602
|
-
const
|
|
5675
|
+
const { base } = modelId.normalizeModelId(modelName);
|
|
5676
|
+
if (base === "") return DEFAULT_MODEL_RATE;
|
|
5677
|
+
const direct = MODEL_RATES[base];
|
|
5603
5678
|
if (direct !== void 0) return direct;
|
|
5604
5679
|
for (const key of Object.keys(MODEL_RATES)) {
|
|
5605
|
-
if (
|
|
5680
|
+
if (base.startsWith(key)) {
|
|
5606
5681
|
const hit = MODEL_RATES[key];
|
|
5607
5682
|
if (hit !== void 0) return hit;
|
|
5608
5683
|
}
|
|
5609
5684
|
}
|
|
5685
|
+
const tier = modelId.tierForModelId(base);
|
|
5686
|
+
if (tier !== null) return PER_TIER_RATE[tier];
|
|
5610
5687
|
return DEFAULT_MODEL_RATE;
|
|
5611
5688
|
}
|
|
5612
5689
|
function usdCost(inputTokens, outputTokens, modelName) {
|
|
@@ -9731,7 +9808,7 @@ ${BUILD_USAGE}`);
|
|
|
9731
9808
|
|
|
9732
9809
|
// sdk/cli/commands/dashboard.ts
|
|
9733
9810
|
var import_node_child_process2 = require("node:child_process");
|
|
9734
|
-
var
|
|
9811
|
+
var import_node_module5 = require("node:module");
|
|
9735
9812
|
var import_node_http = require("node:http");
|
|
9736
9813
|
var import_node_fs23 = require("node:fs");
|
|
9737
9814
|
var import_node_path22 = require("node:path");
|
|
@@ -9767,7 +9844,7 @@ function anchorDirs() {
|
|
|
9767
9844
|
return out;
|
|
9768
9845
|
}
|
|
9769
9846
|
function climbToMarker(startDir) {
|
|
9770
|
-
const req = (0,
|
|
9847
|
+
const req = (0, import_node_module5.createRequire)((0, import_node_path22.join)(startDir, "noop.js"));
|
|
9771
9848
|
let dir = startDir;
|
|
9772
9849
|
let firstWithPkg = null;
|
|
9773
9850
|
for (let i = 0; i < 12; i++) {
|
|
@@ -9803,7 +9880,7 @@ function findPackageRoot() {
|
|
|
9803
9880
|
}
|
|
9804
9881
|
function requireFromRoot(relPath) {
|
|
9805
9882
|
const root = findPackageRoot();
|
|
9806
|
-
const req = (0,
|
|
9883
|
+
const req = (0, import_node_module5.createRequire)((0, import_node_path22.join)(root, "noop.js"));
|
|
9807
9884
|
return req((0, import_node_path22.join)(root, relPath));
|
|
9808
9885
|
}
|
|
9809
9886
|
function resolveRoot(deps, flags) {
|