@hegemonart/get-design-done 1.52.0 → 1.54.0
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 +90 -0
- package/README.md +4 -0
- package/SKILL.md +2 -1
- package/agents/component-taxonomy-mapper.md +3 -0
- package/agents/design-context-reviewer-gate.md +102 -0
- package/agents/design-context-reviewer.md +186 -0
- package/agents/motion-mapper.md +1 -0
- package/agents/token-mapper.md +3 -0
- package/dist/claude-code/.claude/skills/discover/SKILL.md +7 -1
- package/dist/claude-code/.claude/skills/explore/SKILL.md +3 -1
- package/dist/claude-code/.claude/skills/new-addendum/SKILL.md +81 -0
- package/package.json +1 -1
- package/reference/frameworks/astro.md +43 -0
- package/reference/frameworks/nextjs.md +44 -0
- package/reference/frameworks/remix.md +44 -0
- package/reference/frameworks/storybook.md +44 -0
- package/reference/frameworks/sveltekit.md +43 -0
- package/reference/frameworks/vite-react.md +43 -0
- package/reference/interaction.md +1 -0
- package/reference/motion/framer-motion.md +45 -0
- package/reference/motion/gsap.md +45 -0
- package/reference/motion/motion-one.md +44 -0
- package/reference/motion/react-spring.md +44 -0
- package/reference/motion.md +1 -0
- package/reference/registry.json +163 -1
- package/reference/registry.schema.json +18 -1
- package/reference/skill-graph.md +2 -1
- package/reference/systems/chakra.md +44 -0
- package/reference/systems/css-modules.md +44 -0
- package/reference/systems/mui.md +44 -0
- package/reference/systems/radix-themes.md +43 -0
- package/reference/systems/shadcn.md +45 -0
- package/reference/systems/styled-components.md +44 -0
- package/reference/systems/tailwind.md +44 -0
- package/reference/systems/vanilla-extract.md +44 -0
- package/scripts/lib/detect/stack.cjs +455 -0
- package/scripts/lib/detect/stack.d.cts +44 -0
- package/scripts/lib/explore-parallel-runner/index.ts +196 -1
- package/scripts/lib/explore-parallel-runner/types.ts +85 -0
- package/scripts/lib/health-mirror/index.cjs +73 -1
- package/scripts/lib/manifest/skills.json +10 -2
- package/scripts/lib/mapper-spawn.cjs +257 -0
- package/scripts/lib/mapper-spawn.d.cts +60 -0
- package/scripts/lib/mappers/compute-batches.mjs +625 -0
- package/scripts/lib/mappers/graph-adjacency.mjs +129 -0
- package/scripts/lib/mappers/incremental-discover.cjs +617 -0
- package/scripts/lib/mappers/incremental-discover.d.cts +133 -0
- package/scripts/lib/mappers/neighbor-map.mjs +0 -0
- package/scripts/lib/new-addendum.cjs +204 -0
- package/sdk/cli/index.js +1504 -3
- package/sdk/fingerprint/classify.cjs +406 -0
- package/sdk/fingerprint/index.ts +405 -0
- package/sdk/fingerprint/store.cjs +523 -0
- package/sdk/index.ts +1 -0
- package/sdk/mcp/gdd-mcp/server.js +1047 -0
- package/skills/discover/SKILL.md +7 -1
- package/skills/explore/SKILL.md +3 -1
- package/skills/new-addendum/SKILL.md +81 -0
|
@@ -26,6 +26,16 @@ import { resolve as resolvePath } from 'node:path';
|
|
|
26
26
|
|
|
27
27
|
import { getLogger } from '../logger/index.ts';
|
|
28
28
|
import { resolveConcurrency } from '../parallelism-engine/concurrency-tuner.cjs';
|
|
29
|
+
// Phase 53 (DISC-01): the incremental batching composer. CJS, imported the same
|
|
30
|
+
// way as concurrency-tuner.cjs above. Only invoked when opts.incremental.graph
|
|
31
|
+
// is supplied — the default explore path never loads its ESM/TS dependencies.
|
|
32
|
+
import { planIncremental } from '../mappers/incremental-discover.cjs';
|
|
33
|
+
// Phase 54 (REG-01): stack detection + addendum composition. Both CJS, imported
|
|
34
|
+
// the same way. The pre-spawn step (composeMapperSpecs below) calls detectStack
|
|
35
|
+
// ONCE and applyAddendums per spec; both are wrapped in try/catch so a failure
|
|
36
|
+
// degrades to the unmodified Phase-21 spec roster.
|
|
37
|
+
import { detectStack } from '../detect/stack.cjs';
|
|
38
|
+
import { applyAddendums } from '../mapper-spawn.cjs';
|
|
29
39
|
|
|
30
40
|
import {
|
|
31
41
|
isParallelismSafe,
|
|
@@ -101,6 +111,129 @@ export const DEFAULT_MAPPERS: readonly MapperSpec[] = Object.freeze([
|
|
|
101
111
|
}),
|
|
102
112
|
]);
|
|
103
113
|
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Phase 54 (REG-01) — pre-spawn stack-addendum composition
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
/** Derive the agent name an addendum's composes_into list matches against. */
|
|
119
|
+
function agentNameOf(spec: MapperSpec): string {
|
|
120
|
+
// Addendum composes_into uses the AGENT filename (token-mapper,
|
|
121
|
+
// component-taxonomy-mapper, motion-mapper, visual-hierarchy-mapper), not the
|
|
122
|
+
// short MapperSpec.name (token, component-taxonomy, ...). Derive it from the
|
|
123
|
+
// agentPath basename so the registry match keys line up.
|
|
124
|
+
const base = spec.agentPath
|
|
125
|
+
.replace(/\\/g, '/')
|
|
126
|
+
.split('/')
|
|
127
|
+
.pop()
|
|
128
|
+
?.replace(/\.md$/i, '');
|
|
129
|
+
return base && base.length > 0 ? base : spec.name;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Compose stack-specific guidance into each mapper spec BEFORE spawn.
|
|
134
|
+
*
|
|
135
|
+
* Detects the project stack ONCE (detect/stack.cjs#detectStack) and, for each
|
|
136
|
+
* spec, appends the matching `type:"stack-addendum"` reference bodies to the
|
|
137
|
+
* prompt (mapper-spawn.cjs#applyAddendums, cap 1 system + 1 framework + 1
|
|
138
|
+
* motion). The match is keyed by the spec's AGENT name against the registry
|
|
139
|
+
* `composes_into` list.
|
|
140
|
+
*
|
|
141
|
+
* Contract:
|
|
142
|
+
* * ADDITIVE + BACKWARD-COMPATIBLE: a spec with no matching addendum (or no
|
|
143
|
+
* detected stack) is returned with a byte-for-byte unchanged prompt. When
|
|
144
|
+
* NOTHING matches across all specs, the original `specs` array is returned
|
|
145
|
+
* unchanged (same reference), so the Phase-21 path is identical.
|
|
146
|
+
* * NEVER THROWS: detection / registry / file-read failures degrade to the
|
|
147
|
+
* unmodified roster. Dispatch is never blocked by this step.
|
|
148
|
+
* * The frozen DEFAULT_MAPPERS entries are never mutated — a spec that gains
|
|
149
|
+
* an addendum is returned as a fresh object.
|
|
150
|
+
*
|
|
151
|
+
* @returns `{ specs, missingByMapper }` — the (possibly) recomposed roster +
|
|
152
|
+
* a per-agent-name list of detected stack values that had NO addendum
|
|
153
|
+
* for that mapper (drives the R6 fallback flag / health coverage row).
|
|
154
|
+
*/
|
|
155
|
+
function composeMapperSpecs(
|
|
156
|
+
specs: readonly MapperSpec[],
|
|
157
|
+
cwd: string,
|
|
158
|
+
addendumOpts: ExploreRunnerOptions['addendums'],
|
|
159
|
+
logger: ReturnType<typeof getLogger>,
|
|
160
|
+
): { specs: readonly MapperSpec[]; missingByMapper: Record<string, string[]> } {
|
|
161
|
+
const missingByMapper: Record<string, string[]> = {};
|
|
162
|
+
const opt = addendumOpts ?? {};
|
|
163
|
+
if (opt.enabled === false) return { specs, missingByMapper };
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const root: string = typeof opt.root === 'string' ? opt.root : cwd;
|
|
167
|
+
const detect = typeof opt.detectStack === 'function' ? opt.detectStack : detectStack;
|
|
168
|
+
const stack = detect(root) as {
|
|
169
|
+
ds?: string | null;
|
|
170
|
+
framework?: string | null;
|
|
171
|
+
motion_libs?: string[];
|
|
172
|
+
} | null;
|
|
173
|
+
|
|
174
|
+
// Resolve the registry + refDir. Defaults read the shipped registry and the
|
|
175
|
+
// repo reference/ dir; tests inject both. A missing registry simply yields
|
|
176
|
+
// no matches (applyAddendums degrades to an empty block).
|
|
177
|
+
let registry: unknown = opt.registry;
|
|
178
|
+
const refDir: string =
|
|
179
|
+
typeof opt.refDir === 'string'
|
|
180
|
+
? opt.refDir
|
|
181
|
+
: resolvePath(cwd, 'reference');
|
|
182
|
+
if (registry === undefined) {
|
|
183
|
+
try {
|
|
184
|
+
// Lazy require so the default path only touches disk when enabled.
|
|
185
|
+
const { loadRegistry } = require('../reference-registry.cjs') as {
|
|
186
|
+
loadRegistry: (o: { cwd?: string }) => unknown;
|
|
187
|
+
};
|
|
188
|
+
registry = loadRegistry({ cwd });
|
|
189
|
+
} catch {
|
|
190
|
+
registry = undefined; // no registry ⇒ no addendums (unchanged prompts)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let anyChanged = false;
|
|
195
|
+
const recomposed: MapperSpec[] = specs.map((spec) => {
|
|
196
|
+
const agentName = agentNameOf(spec);
|
|
197
|
+
// applyAddendums mutates a spec-shaped object's `.prompt` in place; we feed
|
|
198
|
+
// it a throwaway carrying the AGENT name so the registry match keys align,
|
|
199
|
+
// then copy the (possibly) augmented prompt back onto a fresh spec.
|
|
200
|
+
const carrier = { name: agentName, prompt: spec.prompt };
|
|
201
|
+
const { block, missing } = applyAddendums(carrier, stack, {
|
|
202
|
+
registry,
|
|
203
|
+
refDir,
|
|
204
|
+
}) as { block: string; used: string[]; missing: string[] };
|
|
205
|
+
|
|
206
|
+
if (Array.isArray(missing) && missing.length > 0) {
|
|
207
|
+
missingByMapper[agentName] = missing;
|
|
208
|
+
}
|
|
209
|
+
if (block && block.length > 0 && carrier.prompt !== spec.prompt) {
|
|
210
|
+
anyChanged = true;
|
|
211
|
+
return Object.freeze({ ...spec, prompt: carrier.prompt });
|
|
212
|
+
}
|
|
213
|
+
return spec;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (anyChanged) {
|
|
217
|
+
logger.info('explore.runner.addendums_composed', {
|
|
218
|
+
mappers_augmented: recomposed.filter((s, i) => s !== specs[i]).length,
|
|
219
|
+
ds: stack && stack.ds ? stack.ds : null,
|
|
220
|
+
framework: stack && stack.framework ? stack.framework : null,
|
|
221
|
+
motion_libs:
|
|
222
|
+
stack && Array.isArray(stack.motion_libs) ? stack.motion_libs.length : 0,
|
|
223
|
+
});
|
|
224
|
+
return { specs: Object.freeze(recomposed), missingByMapper };
|
|
225
|
+
}
|
|
226
|
+
// Nothing matched — return the original roster reference unchanged.
|
|
227
|
+
return { specs, missingByMapper };
|
|
228
|
+
} catch (err) {
|
|
229
|
+
// The addendum step must NEVER break dispatch. Degrade to the unmodified
|
|
230
|
+
// roster + surface a warn for observability.
|
|
231
|
+
const message: string = err instanceof Error ? err.message : String(err);
|
|
232
|
+
logger.warn('explore.runner.addendums_failed', { message });
|
|
233
|
+
return { specs, missingByMapper };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
104
237
|
// ---------------------------------------------------------------------------
|
|
105
238
|
// run — main orchestrator
|
|
106
239
|
// ---------------------------------------------------------------------------
|
|
@@ -119,7 +252,7 @@ export const DEFAULT_MAPPERS: readonly MapperSpec[] = Object.freeze([
|
|
|
119
252
|
export async function run(
|
|
120
253
|
opts: ExploreRunnerOptions,
|
|
121
254
|
): Promise<ExploreRunnerResult> {
|
|
122
|
-
const
|
|
255
|
+
const baseSpecs: readonly MapperSpec[] = opts.mappers ?? DEFAULT_MAPPERS;
|
|
123
256
|
const cwd: string = opts.cwd ?? process.cwd();
|
|
124
257
|
// Phase 27.6 D-07: data-driven concurrency default. Falls back to
|
|
125
258
|
// min(cpu-1, 8) when no `parallelism.verdict` events exist in
|
|
@@ -128,8 +261,68 @@ export async function run(
|
|
|
128
261
|
|
|
129
262
|
const logger = getLogger().child('explore.runner');
|
|
130
263
|
|
|
264
|
+
// --- Phase 54 (REG-01): compose stack addendums into mapper prompts -------
|
|
265
|
+
//
|
|
266
|
+
// Fingerprint the project ONCE and append the matching stack-addendum bodies
|
|
267
|
+
// to each mapper's prompt BEFORE partitioning / spawn. ADDITIVE +
|
|
268
|
+
// backward-compatible: no detected stack / no matching addendum ⇒ `specs` is
|
|
269
|
+
// the unchanged roster reference. NEVER throws (composeMapperSpecs guards).
|
|
270
|
+
const { specs } = composeMapperSpecs(baseSpecs, cwd, opts.addendums, logger);
|
|
271
|
+
|
|
131
272
|
const outputPath: string = resolvePath(cwd, '.design/DESIGN-PATTERNS.md');
|
|
132
273
|
|
|
274
|
+
// --- Phase 53 (DISC-01): incremental batching ----------------------------
|
|
275
|
+
//
|
|
276
|
+
// ONLY runs when a Phase-52 graph is supplied. Groups the graph into Louvain
|
|
277
|
+
// community batches, runs the change classifier against the prior fingerprint
|
|
278
|
+
// snapshot, and elects which batches to re-map (SKIP=0, PARTIAL=affected,
|
|
279
|
+
// FULL=all). The result rides on `batching`; the spec roster + rolling
|
|
280
|
+
// semaphore below are UNCHANGED. Backward-compatible: absent graph ⇒ undefined
|
|
281
|
+
// batching ⇒ the Phase-21 path is byte-for-byte the same. Never throws —
|
|
282
|
+
// batching is advisory metadata, and a planning failure must not abort mappers.
|
|
283
|
+
let batching: ExploreRunnerResult['batching'] = undefined;
|
|
284
|
+
if (opts.incremental && opts.incremental.graph !== undefined && opts.incremental.graph !== null) {
|
|
285
|
+
try {
|
|
286
|
+
const plan = await planIncremental({
|
|
287
|
+
graph: opts.incremental.graph,
|
|
288
|
+
prevFingerprints: opts.incremental.prevFingerprints,
|
|
289
|
+
opts: {
|
|
290
|
+
...(opts.incremental.forceFull !== undefined ? { forceFull: opts.incremental.forceFull } : {}),
|
|
291
|
+
...(opts.incremental.computeBatchesOpts !== undefined ? { computeBatchesOpts: opts.incremental.computeBatchesOpts } : {}),
|
|
292
|
+
...(opts.incremental.neighborCap !== undefined ? { neighborCap: opts.incremental.neighborCap } : {}),
|
|
293
|
+
...(opts.incremental.thresholds !== undefined ? { thresholds: opts.incremental.thresholds } : {}),
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
batching = Object.freeze({
|
|
297
|
+
action: plan.action,
|
|
298
|
+
method: plan.method,
|
|
299
|
+
modularity: plan.modularity,
|
|
300
|
+
batches: Object.freeze(plan.batches.map((b: { id: string; members: string[]; mergeable: boolean; kind: string; source: string }) => Object.freeze({
|
|
301
|
+
id: b.id,
|
|
302
|
+
members: Object.freeze([...b.members]),
|
|
303
|
+
mergeable: b.mergeable,
|
|
304
|
+
kind: b.kind,
|
|
305
|
+
source: b.source,
|
|
306
|
+
}))),
|
|
307
|
+
batchesToMap: Object.freeze(plan.batchesToMap.map((b: { id: string }) => b.id)),
|
|
308
|
+
neighborMaps: Object.freeze({ ...plan.neighborMaps }),
|
|
309
|
+
classification: Object.freeze({ ...plan.classification }),
|
|
310
|
+
});
|
|
311
|
+
logger.info('explore.runner.batching', {
|
|
312
|
+
action: plan.action,
|
|
313
|
+
method: plan.method,
|
|
314
|
+
batch_count: plan.batches.length,
|
|
315
|
+
batches_to_map: plan.batchesToMap.length,
|
|
316
|
+
structural_count: plan.classification.structuralCount,
|
|
317
|
+
});
|
|
318
|
+
} catch (err) {
|
|
319
|
+
// Planning failure degrades to "no batching" — the mappers still run.
|
|
320
|
+
const message: string = err instanceof Error ? err.message : String(err);
|
|
321
|
+
logger.warn('explore.runner.batching_failed', { message });
|
|
322
|
+
batching = undefined;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
133
326
|
logger.info('explore.runner.started', {
|
|
134
327
|
mapper_count: specs.length,
|
|
135
328
|
concurrency,
|
|
@@ -154,6 +347,7 @@ export async function run(
|
|
|
154
347
|
parallel_count: 0,
|
|
155
348
|
serial_count: 0,
|
|
156
349
|
total_usage: { input_tokens: 0, output_tokens: 0, usd_cost: 0 },
|
|
350
|
+
...(batching !== undefined ? { batching } : {}),
|
|
157
351
|
});
|
|
158
352
|
}
|
|
159
353
|
|
|
@@ -294,5 +488,6 @@ export async function run(
|
|
|
294
488
|
output_tokens: totalOutput,
|
|
295
489
|
usd_cost: totalCost,
|
|
296
490
|
},
|
|
491
|
+
...(batching !== undefined ? { batching } : {}),
|
|
297
492
|
});
|
|
298
493
|
}
|
|
@@ -109,6 +109,62 @@ export interface ExploreRunnerOptions {
|
|
|
109
109
|
readonly pollIntervalMs?: number;
|
|
110
110
|
/** Override the file-watch timeout (ms). Default 600_000 (10 min). */
|
|
111
111
|
readonly timeoutMs?: number;
|
|
112
|
+
/**
|
|
113
|
+
* Phase 53 (DISC-01) — OPTIONAL incremental batching. When supplied with a
|
|
114
|
+
* Phase-52 DesignContext `graph`, the runner runs the change classifier
|
|
115
|
+
* (`scripts/lib/mappers/incremental-discover.cjs#planIncremental`) FIRST,
|
|
116
|
+
* groups the graph into Louvain community batches, attaches a per-batch
|
|
117
|
+
* neighborMap sidecar, and dispatches only the batches the classifier elects
|
|
118
|
+
* to re-map (SKIP=0, PARTIAL=affected, FULL=all). The mapper SPEC roster is
|
|
119
|
+
* unchanged — batching is metadata threaded onto the result so the explore
|
|
120
|
+
* stage can scope each mapper to its community; the rolling semaphore still
|
|
121
|
+
* spawns the spec roster at the tuned concurrency.
|
|
122
|
+
*
|
|
123
|
+
* BACKWARD-COMPATIBLE: when `incremental` is absent (or `graph` is missing),
|
|
124
|
+
* the runner behaves EXACTLY as before — no batching, no classifier, the
|
|
125
|
+
* Phase-21 partition-then-spawn path runs untouched.
|
|
126
|
+
*/
|
|
127
|
+
readonly incremental?: {
|
|
128
|
+
/** Phase-52 DesignContext graph ({ nodes, edges }). Required to batch. */
|
|
129
|
+
readonly graph: unknown;
|
|
130
|
+
/** Prior fingerprint snapshot (store.readCurrent().fingerprints). Absent ⇒ bootstrap ⇒ FULL. */
|
|
131
|
+
readonly prevFingerprints?: unknown;
|
|
132
|
+
/** The `--full` opt-out: force a FULL re-map regardless of the classifier. */
|
|
133
|
+
readonly forceFull?: boolean;
|
|
134
|
+
/** Forwarded to computeBatches (resolution, maxCommunitySize, configCwd, …). */
|
|
135
|
+
readonly computeBatchesOpts?: unknown;
|
|
136
|
+
/** buildNeighborMap cap (default 50). */
|
|
137
|
+
readonly neighborCap?: number;
|
|
138
|
+
/** Forwarded into classify's projectStats.thresholds. */
|
|
139
|
+
readonly thresholds?: unknown;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Phase 54 (REG-01) — OPTIONAL composable stack addendums. Before spawning,
|
|
143
|
+
* the runner fingerprints the project (`scripts/lib/detect/stack.cjs#detectStack`)
|
|
144
|
+
* ONCE and appends the matching `type:"stack-addendum"` reference bodies to
|
|
145
|
+
* each mapper's prompt via `scripts/lib/mapper-spawn.cjs#applyAddendums`
|
|
146
|
+
* (cap 1 design-system + 1 framework + 1 motion per spawn). The addendum is
|
|
147
|
+
* selected by the mapper's AGENT name (e.g. token-mapper) against the
|
|
148
|
+
* registry `composes_into` list.
|
|
149
|
+
*
|
|
150
|
+
* BACKWARD-COMPATIBLE + ADDITIVE: defaults ON, but a project with no
|
|
151
|
+
* detected stack (or no matching addendum) gets a byte-for-byte unchanged
|
|
152
|
+
* prompt. The whole step is wrapped in try/catch so a detection/registry
|
|
153
|
+
* failure NEVER aborts dispatch. Set `enabled: false` to opt out entirely.
|
|
154
|
+
* The `detectStack` / `registry` / `refDir` fields are test-injection seams.
|
|
155
|
+
*/
|
|
156
|
+
readonly addendums?: {
|
|
157
|
+
/** Opt out of stack-addendum composition entirely. Default: enabled. */
|
|
158
|
+
readonly enabled?: boolean;
|
|
159
|
+
/** Detection root. Defaults to `cwd`. */
|
|
160
|
+
readonly root?: string;
|
|
161
|
+
/** Injected detectStack (tests). Defaults to detect/stack.cjs#detectStack. */
|
|
162
|
+
readonly detectStack?: (root: string) => unknown;
|
|
163
|
+
/** Injected registry object (tests). Defaults to reference/registry.json. */
|
|
164
|
+
readonly registry?: unknown;
|
|
165
|
+
/** Reference dir addendum paths resolve against. Defaults to repo reference/. */
|
|
166
|
+
readonly refDir?: string;
|
|
167
|
+
};
|
|
112
168
|
}
|
|
113
169
|
|
|
114
170
|
/**
|
|
@@ -136,4 +192,33 @@ export interface ExploreRunnerResult {
|
|
|
136
192
|
readonly output_tokens: number;
|
|
137
193
|
readonly usd_cost: number;
|
|
138
194
|
};
|
|
195
|
+
/**
|
|
196
|
+
* Phase 53 (DISC-01) — present ONLY when `ExploreRunnerOptions.incremental`
|
|
197
|
+
* was supplied. Carries the classifier verdict + the community batch plan so
|
|
198
|
+
* the explore stage can scope each mapper to its community and skip re-mapping
|
|
199
|
+
* unchanged batches. Absent (undefined) on the backward-compatible default
|
|
200
|
+
* path — existing consumers that never set `incremental` never see this key.
|
|
201
|
+
*/
|
|
202
|
+
readonly batching?: {
|
|
203
|
+
/** SKIP | PARTIAL_UPDATE | ARCHITECTURE_UPDATE | FULL_UPDATE (post `--full`). */
|
|
204
|
+
readonly action: string;
|
|
205
|
+
/** Batching method actually used. */
|
|
206
|
+
readonly method: 'louvain' | 'count-fallback';
|
|
207
|
+
/** Modularity of the Louvain partition (null on the count-fallback). */
|
|
208
|
+
readonly modularity: number | null;
|
|
209
|
+
/** Every community batch (opaque ids + members). */
|
|
210
|
+
readonly batches: ReadonlyArray<{
|
|
211
|
+
readonly id: string;
|
|
212
|
+
readonly members: readonly string[];
|
|
213
|
+
readonly mergeable: boolean;
|
|
214
|
+
readonly kind: string;
|
|
215
|
+
readonly source: string;
|
|
216
|
+
}>;
|
|
217
|
+
/** The batch ids elected for re-map this cycle (SKIP ⇒ empty). */
|
|
218
|
+
readonly batchesToMap: readonly string[];
|
|
219
|
+
/** Per-batch neighborMap sidecar, keyed by batch id (selected batches only). */
|
|
220
|
+
readonly neighborMaps: Readonly<Record<string, unknown>>;
|
|
221
|
+
/** The change classifier's full result (structuralCount, pct, hints, reason, …). */
|
|
222
|
+
readonly classification: Readonly<Record<string, unknown>>;
|
|
223
|
+
};
|
|
139
224
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Surface:
|
|
9
9
|
// async getHealthChecks(rootDir) → { checks: HealthCheck[] }
|
|
10
10
|
//
|
|
11
|
-
// The
|
|
11
|
+
// The 9 checks (in stable order) are:
|
|
12
12
|
// 1. claude_md — CLAUDE.md presence
|
|
13
13
|
// 2. planning_dir — .planning/ presence
|
|
14
14
|
// 3. design_dir — .design/ presence
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
// 5. issue_reporter — kill-switch state (Plan 30-06 / D-08)
|
|
17
17
|
// 6. figma_extract — extract readiness + Free-tier signal (Plan 31-09)
|
|
18
18
|
// 7. skill_discipline — using-gdd bootstrap + SessionStart inject (Plan 32-07)
|
|
19
|
+
// 8. harness_freshness — per-harness last_verified age (Phase 44)
|
|
20
|
+
// 9. stack_addendums — Phase 54 coverage: N/M detected stacks have addendums
|
|
19
21
|
//
|
|
20
22
|
// Check 5 was added in Plan 30-06 — surfaces the report-issue kill-switch
|
|
21
23
|
// (env or config disable) so users can verify why the command is
|
|
@@ -238,6 +240,76 @@ async function getHealthChecks(rootDir) {
|
|
|
238
240
|
checks.push({ name: 'harness_freshness', status, detail });
|
|
239
241
|
}
|
|
240
242
|
|
|
243
|
+
// 9. stack_addendums — Phase 54 coverage row. Fingerprints the project
|
|
244
|
+
// (detect/stack.cjs) and reports how many DETECTED stacks (design-system +
|
|
245
|
+
// framework + motion libs) have a registered type:"stack-addendum" entry in
|
|
246
|
+
// reference/registry.json. PURE read-only (detection reads files but never
|
|
247
|
+
// networks; registry is a local JSON read). GRACEFUL-ABSENT: no detected
|
|
248
|
+
// stack -> "no stacks detected"; an unreadable registry -> "registry
|
|
249
|
+
// unavailable". status is 'ok' on full coverage or nothing-to-cover,
|
|
250
|
+
// otherwise 'warn'. NEVER throws.
|
|
251
|
+
{
|
|
252
|
+
let status = 'ok';
|
|
253
|
+
let detail;
|
|
254
|
+
try {
|
|
255
|
+
const { detectStack } = require('../detect/stack.cjs');
|
|
256
|
+
const stack = detectStack(rootDir) || { ds: null, framework: null, motion_libs: [] };
|
|
257
|
+
|
|
258
|
+
// The detected stack values to cover, paired with the category an
|
|
259
|
+
// addendum must classify into to count as coverage.
|
|
260
|
+
const wanted = [];
|
|
261
|
+
if (typeof stack.ds === 'string' && stack.ds) wanted.push({ category: 'system', key: stack.ds });
|
|
262
|
+
if (typeof stack.framework === 'string' && stack.framework) {
|
|
263
|
+
wanted.push({ category: 'framework', key: stack.framework });
|
|
264
|
+
}
|
|
265
|
+
if (Array.isArray(stack.motion_libs)) {
|
|
266
|
+
for (const m of stack.motion_libs) {
|
|
267
|
+
if (typeof m === 'string' && m) wanted.push({ category: 'motion', key: m });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (wanted.length === 0) {
|
|
272
|
+
detail = 'stack addendums: no stacks detected';
|
|
273
|
+
status = 'ok';
|
|
274
|
+
} else {
|
|
275
|
+
// Load the registry + classify its stack-addendum entries. A missing or
|
|
276
|
+
// malformed registry counts as zero coverage (warn), never a throw.
|
|
277
|
+
let entries = [];
|
|
278
|
+
try {
|
|
279
|
+
const reg = JSON.parse(
|
|
280
|
+
fs.readFileSync(path.join(rootDir, 'reference', 'registry.json'), 'utf8')
|
|
281
|
+
);
|
|
282
|
+
entries = Array.isArray(reg.entries) ? reg.entries : [];
|
|
283
|
+
} catch {
|
|
284
|
+
entries = null; // distinguish "registry unavailable" from "zero coverage"
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (entries === null) {
|
|
288
|
+
detail = 'stack addendums: registry unavailable';
|
|
289
|
+
status = 'warn';
|
|
290
|
+
} else {
|
|
291
|
+
const { classifyEntry } = require('../mapper-spawn.cjs');
|
|
292
|
+
// Build the set of {category|key} an addendum exists for.
|
|
293
|
+
const covered = new Set();
|
|
294
|
+
for (const e of entries) {
|
|
295
|
+
if (!e || e.type !== 'stack-addendum') continue;
|
|
296
|
+
const { category, key } = classifyEntry(e);
|
|
297
|
+
if (category && key) covered.add(category + '|' + key);
|
|
298
|
+
}
|
|
299
|
+
const have = wanted.filter((w) => covered.has(w.category + '|' + String(w.key).toLowerCase())).length;
|
|
300
|
+
const total = wanted.length;
|
|
301
|
+
status = have >= total ? 'ok' : 'warn';
|
|
302
|
+
detail = `stack addendums: ${have}/${total} detected stacks have addendums`;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
// Absolute safety net — the health probe must never crash on this check.
|
|
307
|
+
status = 'warn';
|
|
308
|
+
detail = 'stack addendums: unavailable';
|
|
309
|
+
}
|
|
310
|
+
checks.push({ name: 'stack_addendums', status, detail });
|
|
311
|
+
}
|
|
312
|
+
|
|
241
313
|
return { checks };
|
|
242
314
|
}
|
|
243
315
|
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"name": "discover",
|
|
136
136
|
"frontmatter_name": "discover",
|
|
137
137
|
"description": "Stage 1.5 of 4 orchestrator that probes Figma / Refero / Pinterest connections, spawns design-context-builder (auto-detect + interview) and (via lazy gate) design-context-checker (6-dimension validator), producing .design/DESIGN-CONTEXT.md. Use after {{command_prefix}}scan when a fast-path context build is wanted instead of the full {{command_prefix}}explore. Activates for requests involving detecting an existing design system, inventorying tokens and components, or onboarding a brownfield repo.",
|
|
138
|
-
"argument_hint": "[--auto]",
|
|
138
|
+
"argument_hint": "[--auto] [--incremental] [--full]",
|
|
139
139
|
"user_invocable": true
|
|
140
140
|
},
|
|
141
141
|
{
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
{
|
|
154
154
|
"name": "explore",
|
|
155
155
|
"description": "Stage 2 of 5 - unified exploration merging inventory grep + design interview. Probes 6 connections, scans the codebase, conducts the AskUserQuestion interview, and writes .design/DESIGN.md + DESIGN-DEBT.md + DESIGN-CONTEXT.md. Use after {{command_prefix}}brief to map the existing system and lock decisions before planning. Activates for requests involving researching design direction, gathering references, or exploring visual options.",
|
|
156
|
-
"argument_hint": "[--skip-interview] [--skip-scan]",
|
|
156
|
+
"argument_hint": "[--skip-interview] [--skip-scan] [--incremental] [--full]",
|
|
157
157
|
"tools": "Read, Write, Bash, Grep, Glob, Task, AskUserQuestion, mcp__gdd_state__get, mcp__gdd_state__transition_stage, mcp__gdd_state__probe_connections, mcp__gdd_state__update_progress, mcp__gdd_state__set_status, mcp__gdd_state__add_blocker, mcp__gdd_state__checkpoint, mcp__gdd_state__add_decision",
|
|
158
158
|
"next_skills": [
|
|
159
159
|
"plan"
|
|
@@ -260,6 +260,14 @@
|
|
|
260
260
|
"user_invocable": true,
|
|
261
261
|
"registered_in_phase": "52"
|
|
262
262
|
},
|
|
263
|
+
{
|
|
264
|
+
"name": "new-addendum",
|
|
265
|
+
"description": "Scaffolds a new Phase-54 composable reference addendum for a design-system, framework, or motion library: validates the kind and the slug, defaults composes_into by kind, and writes a 4-section skeleton at reference/{systems|frameworks|motion}/<name>.md from the pure generator. Use when adding stack-specific guidance that an explore mapper should compose at spawn time and you want the frontmatter, the composes_into wiring, and the mandatory sections correct from the first commit. Activates for requests involving authoring a reference addendum, adding stack-specific mapper guidance, scaffolding a systems or frameworks or motion doc, or registering a new design-system.",
|
|
266
|
+
"argument_hint": "<kind> <name>",
|
|
267
|
+
"tools": "Read, Write, Bash, AskUserQuestion",
|
|
268
|
+
"user_invocable": true,
|
|
269
|
+
"registered_in_phase": "54"
|
|
270
|
+
},
|
|
263
271
|
{
|
|
264
272
|
"name": "new-cycle",
|
|
265
273
|
"description": "Start a new design cycle. Creates cycle scope in STATE.md, initializes .design/CYCLES.md entry. Each cycle has its own goal and tracks its own decisions/tasks/pipeline runs.",
|