@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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// scripts/lib/mappers/incremental-discover.d.cts — types for incremental-discover.cjs (Phase 53 DISC-01).
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the .d.cts sidecar convention used by concurrency-tuner.cjs so the
|
|
4
|
+
// .ts explore-parallel-runner can import planIncremental without a TS7016
|
|
5
|
+
// implicit-any. The runtime module is dep-free CJS (it dynamic-import()s the
|
|
6
|
+
// .mjs batchers + the .ts fingerprint engine internally).
|
|
7
|
+
|
|
8
|
+
/** A community batch (the A-subsystem `computeBatches` shape). */
|
|
9
|
+
export interface Batch {
|
|
10
|
+
id: string;
|
|
11
|
+
members: string[];
|
|
12
|
+
mergeable: boolean;
|
|
13
|
+
kind: 'code' | 'token' | 'motion' | 'a11y' | 'misc';
|
|
14
|
+
source: 'louvain' | 'fallback' | 'subsplit' | 'merge';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A single per-node fingerprint-compare entry fed to the classifier. */
|
|
18
|
+
export interface CompareResult {
|
|
19
|
+
id: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
change: 'NONE' | 'COSMETIC' | 'STRUCTURAL';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The change-classifier result (sdk/fingerprint/classify.cjs). */
|
|
25
|
+
export interface Classification {
|
|
26
|
+
action: 'SKIP' | 'PARTIAL_UPDATE' | 'ARCHITECTURE_UPDATE' | 'FULL_UPDATE';
|
|
27
|
+
structuralCount: number;
|
|
28
|
+
pct: number;
|
|
29
|
+
dirChanged: boolean;
|
|
30
|
+
majorRestructure: boolean;
|
|
31
|
+
affectedBatchHints: string[];
|
|
32
|
+
reason: string;
|
|
33
|
+
thresholds: { fullFileCount: number; fullPct: number; archPctMax: number };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A per-node fingerprint pair plus its node type, as persisted to the store. */
|
|
37
|
+
export interface StoredFingerprint {
|
|
38
|
+
full: string;
|
|
39
|
+
structural: string;
|
|
40
|
+
type?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The neighborMap sidecar for one batch (neighbor-map.mjs shape). */
|
|
44
|
+
export interface NeighborMap {
|
|
45
|
+
batchId: string | null;
|
|
46
|
+
neighbors: Record<string, unknown[]>;
|
|
47
|
+
truncated: Record<string, { omitted: number }>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Options bag for planIncremental. */
|
|
51
|
+
export interface PlanIncrementalOptions {
|
|
52
|
+
/** The `--full` opt-out: force a FULL re-map regardless of the classifier. */
|
|
53
|
+
forceFull?: boolean;
|
|
54
|
+
/** Forwarded to computeBatches (resolution, maxCommunitySize, configCwd, …). */
|
|
55
|
+
computeBatchesOpts?: unknown;
|
|
56
|
+
/** buildNeighborMap cap (default 50). */
|
|
57
|
+
neighborCap?: number;
|
|
58
|
+
/** Forwarded into classify's projectStats.thresholds. */
|
|
59
|
+
thresholds?: unknown;
|
|
60
|
+
/** Force the bootstrap signal off when the store had a snapshot but it was empty. */
|
|
61
|
+
hadPriorBaseline?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The incremental plan returned by planIncremental. */
|
|
65
|
+
export interface IncrementalPlan {
|
|
66
|
+
action: 'SKIP' | 'PARTIAL_UPDATE' | 'ARCHITECTURE_UPDATE' | 'FULL_UPDATE';
|
|
67
|
+
batches: Batch[];
|
|
68
|
+
batchesToMap: Batch[];
|
|
69
|
+
neighborMaps: Record<string, NeighborMap>;
|
|
70
|
+
fingerprints: Record<string, StoredFingerprint>;
|
|
71
|
+
compareResults: CompareResult[];
|
|
72
|
+
classification: Classification;
|
|
73
|
+
method: 'louvain' | 'count-fallback';
|
|
74
|
+
modularity: number | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A DirShape derived from node-id provenance. */
|
|
78
|
+
export interface DirShape {
|
|
79
|
+
dirs: string[];
|
|
80
|
+
counts: Record<string, number>;
|
|
81
|
+
layerHist: Record<string, number>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Plan an incremental discover/explore cycle: compute community batches, run the
|
|
86
|
+
* change classifier against the prior fingerprint snapshot, and elect which
|
|
87
|
+
* batches to re-map (SKIP=[], PARTIAL=affected, FULL=all). Async (it
|
|
88
|
+
* dynamic-import()s the .mjs batchers + the .ts fingerprint engine).
|
|
89
|
+
*/
|
|
90
|
+
export function planIncremental(args: {
|
|
91
|
+
graph: unknown;
|
|
92
|
+
prevFingerprints?: unknown;
|
|
93
|
+
opts?: PlanIncrementalOptions;
|
|
94
|
+
}): Promise<IncrementalPlan>;
|
|
95
|
+
|
|
96
|
+
/** Pure batch-selection matrix: SKIP→[], FULL→all, PARTIAL/ARCH→hint intersection. */
|
|
97
|
+
export function selectBatches(
|
|
98
|
+
batches: Batch[],
|
|
99
|
+
action: string,
|
|
100
|
+
affectedBatchHints: string[],
|
|
101
|
+
): Batch[];
|
|
102
|
+
|
|
103
|
+
/** Derive the current DirShape (+ totalFiles) from a graph's file-nodes. */
|
|
104
|
+
export function deriveDirShape(
|
|
105
|
+
graph: unknown,
|
|
106
|
+
): DirShape & { totalFiles: number };
|
|
107
|
+
|
|
108
|
+
/** Reconstruct the prior DirShape from a prevFingerprints map (null when empty). */
|
|
109
|
+
export function derivePrevDirShape(
|
|
110
|
+
prevFingerprints: unknown,
|
|
111
|
+
): DirShape | null;
|
|
112
|
+
|
|
113
|
+
/** Build current fingerprints + the compareResults change set for a graph. */
|
|
114
|
+
export function buildCompareResults(
|
|
115
|
+
graph: unknown,
|
|
116
|
+
prevFingerprints: unknown,
|
|
117
|
+
fingerprint: (input: unknown, type: string) => { full: string; structural: string },
|
|
118
|
+
compareFingerprints: (
|
|
119
|
+
a: { full: string; structural: string } | null,
|
|
120
|
+
b: { full: string; structural: string } | null,
|
|
121
|
+
) => string,
|
|
122
|
+
): { fingerprints: Record<string, StoredFingerprint>; compareResults: CompareResult[] };
|
|
123
|
+
|
|
124
|
+
/** Project one fingerprintable node into its per-type fingerprint input. */
|
|
125
|
+
export function projectNode(node: unknown, fpType: string, idx: unknown): unknown;
|
|
126
|
+
|
|
127
|
+
/** Build the once-per-graph relationship index used by projectNode. */
|
|
128
|
+
export function indexForProjection(
|
|
129
|
+
graph: unknown,
|
|
130
|
+
): { byId: Map<string, unknown>; tokensOf: Map<string, Set<string>>; variantsOf: Map<string, Set<string>> };
|
|
131
|
+
|
|
132
|
+
/** The graph node `type` values that are independently fingerprinted. */
|
|
133
|
+
export const FINGERPRINTABLE: Map<string, string>;
|
|
Binary file
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* scripts/lib/new-addendum.cjs — Phase 54 (Composable Reference Addendums), REG-01.
|
|
4
|
+
*
|
|
5
|
+
* Pure, dependency-free generator behind the `/gdd:new-addendum <kind> <name>`
|
|
6
|
+
* scaffolder skill (source/skills/new-addendum/SKILL.md). The SKILL.md drives
|
|
7
|
+
* the prompts; this module is the deterministic core it (and the test suite)
|
|
8
|
+
* call. Mirrors scripts/lib/manifest/scaffolder.cjs (the Phase 50 skill
|
|
9
|
+
* scaffolder): same ReDoS-safe NAME_RE, same throw-on-invalid contract, same
|
|
10
|
+
* "render a skeleton string, never touch the manifest" boundary.
|
|
11
|
+
*
|
|
12
|
+
* A stack addendum is a REGISTRY ENTRY, not a skill (CONTEXT R4). This
|
|
13
|
+
* scaffolder writes ONE reference/{systems|frameworks|motion}/<name>.md file
|
|
14
|
+
* with the locked frontmatter + the 4 mandatory sections. It does NOT touch
|
|
15
|
+
* reference/registry.json (the maintainer/orchestrator adds the entry + runs
|
|
16
|
+
* the registry round-trip), exactly as new-skill does not touch skills.json.
|
|
17
|
+
*
|
|
18
|
+
* Exports:
|
|
19
|
+
* buildAddendumRecord({ kind, name, composesInto }) -> a normalized record
|
|
20
|
+
* { name, kind, composes_into, phase, dir, path }. Validates kind against
|
|
21
|
+
* KINDS and name against NAME_RE; defaults composes_into by kind. Throws on
|
|
22
|
+
* invalid input.
|
|
23
|
+
* renderAddendumMd(record) -> the addendum skeleton string (frontmatter +
|
|
24
|
+
* the 4 mandatory sections: Conventions / File patterns / Gotchas /
|
|
25
|
+
* Example output). Em-dash-free (lint:prose-clean).
|
|
26
|
+
* targetPathFor(kind, name) -> the repo-root-relative path the file is
|
|
27
|
+
* written to (e.g. "reference/systems/<name>.md").
|
|
28
|
+
*
|
|
29
|
+
* Dependency-free of any third party (node:path only; no fs writes here — the
|
|
30
|
+
* SKILL.md writes the rendered string with the Write tool, same as new-skill).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// Slug rule mirrors scripts/lib/manifest/scaffolder.cjs NAME_RE and the
|
|
34
|
+
// registry.schema.json entry-name pattern: kebab-case, starts alnum,
|
|
35
|
+
// ^[a-z0-9][a-z0-9-._]*$. The `\w`-free char class is linear-time (no ReDoS).
|
|
36
|
+
const NAME_RE = /^[a-z0-9][a-z0-9-._]*$/;
|
|
37
|
+
|
|
38
|
+
// The three addendum categories (CONTEXT R4 / shared contracts). Maps each
|
|
39
|
+
// kind to its reference subdir and the default composes_into mapper list.
|
|
40
|
+
// The defaults mirror the round-1 addendum frontmatter that executors C/D/E
|
|
41
|
+
// shipped (systems + frameworks vs motion), so a scaffolded addendum is wired
|
|
42
|
+
// the same way the hand-authored ones are.
|
|
43
|
+
const KIND_SPEC = {
|
|
44
|
+
system: {
|
|
45
|
+
dir: 'reference/systems',
|
|
46
|
+
composesInto: ['token-mapper', 'component-taxonomy-mapper'],
|
|
47
|
+
label: 'design-system',
|
|
48
|
+
},
|
|
49
|
+
framework: {
|
|
50
|
+
dir: 'reference/frameworks',
|
|
51
|
+
composesInto: ['component-taxonomy-mapper', 'visual-hierarchy-mapper'],
|
|
52
|
+
label: 'framework',
|
|
53
|
+
},
|
|
54
|
+
motion: {
|
|
55
|
+
dir: 'reference/motion',
|
|
56
|
+
composesInto: ['motion-mapper'],
|
|
57
|
+
label: 'motion library',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const KINDS = Object.keys(KIND_SPEC);
|
|
62
|
+
const PHASE = 54;
|
|
63
|
+
|
|
64
|
+
function fail(msg) {
|
|
65
|
+
throw new Error(`new-addendum: ${msg}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Normalize a composes_into input (string or array) to a clean mapper list. */
|
|
69
|
+
function normalizeComposesInto(composesInto) {
|
|
70
|
+
if (composesInto == null) return undefined;
|
|
71
|
+
let parts;
|
|
72
|
+
if (Array.isArray(composesInto)) parts = composesInto;
|
|
73
|
+
else if (typeof composesInto === 'string') parts = composesInto.split(',');
|
|
74
|
+
else fail('composesInto must be an array or comma-separated string of mapper names');
|
|
75
|
+
const cleaned = parts.map((s) => String(s).trim()).filter(Boolean);
|
|
76
|
+
if (cleaned.length === 0) return undefined;
|
|
77
|
+
// Mapper names follow the same slug rule as everything else here.
|
|
78
|
+
for (const m of cleaned) {
|
|
79
|
+
if (!NAME_RE.test(m)) fail(`composesInto entry "${m}" is not a valid mapper slug`);
|
|
80
|
+
}
|
|
81
|
+
// De-dupe, preserve first-seen order.
|
|
82
|
+
return [...new Set(cleaned)];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Repo-root-relative target path for a (kind, name). Throws on invalid kind/name. */
|
|
86
|
+
function targetPathFor(kind, name) {
|
|
87
|
+
const spec = KIND_SPEC[kind];
|
|
88
|
+
if (!spec) fail(`kind "${kind}" must be one of: ${KINDS.join(', ')}`);
|
|
89
|
+
const n = typeof name === 'string' ? name.trim() : name;
|
|
90
|
+
if (!n || typeof n !== 'string' || !NAME_RE.test(n)) {
|
|
91
|
+
fail(`name "${name}" must match ${NAME_RE} (lower-case, starts alnum, kebab/dot/underscore)`);
|
|
92
|
+
}
|
|
93
|
+
return `${spec.dir}/${n}.md`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Build a normalized addendum record from scaffolder inputs.
|
|
98
|
+
* @param {{ kind: string, name: string, composesInto?: string|string[] }} input
|
|
99
|
+
* @returns {{ name, kind, composes_into: string[], phase: number, dir: string, path: string }}
|
|
100
|
+
* @throws on an invalid kind, an invalid name, or a malformed composesInto.
|
|
101
|
+
*/
|
|
102
|
+
function buildAddendumRecord(input) {
|
|
103
|
+
const opts = input || {};
|
|
104
|
+
const kind = typeof opts.kind === 'string' ? opts.kind.trim().toLowerCase() : opts.kind;
|
|
105
|
+
if (!kind || typeof kind !== 'string' || !KIND_SPEC[kind]) {
|
|
106
|
+
fail(`kind is required and must be one of: ${KINDS.join(', ')}`);
|
|
107
|
+
}
|
|
108
|
+
const name = typeof opts.name === 'string' ? opts.name.trim() : opts.name;
|
|
109
|
+
if (!name || typeof name !== 'string') fail('name is required (a kebab-case slug)');
|
|
110
|
+
if (!NAME_RE.test(name)) {
|
|
111
|
+
fail(`name "${name}" must match ${NAME_RE} (lower-case, starts alnum, kebab/dot/underscore)`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const spec = KIND_SPEC[kind];
|
|
115
|
+
const composesInto = normalizeComposesInto(opts.composesInto) || spec.composesInto.slice();
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
kind,
|
|
120
|
+
composes_into: composesInto,
|
|
121
|
+
phase: PHASE,
|
|
122
|
+
dir: spec.dir,
|
|
123
|
+
path: `${spec.dir}/${name}.md`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Render the addendum skeleton string for a record.
|
|
129
|
+
* Frontmatter keys are emitted in the canonical order the round-1 addendums
|
|
130
|
+
* use (name, kind, composes_into, phase). The body carries the 4 mandatory
|
|
131
|
+
* sections (Conventions / File patterns / Gotchas / Example output) with TODO
|
|
132
|
+
* placeholders + a vendor-attribution comment slot (house style). Em-dash-free.
|
|
133
|
+
* @param {object} record a buildAddendumRecord result (or raw {kind,name,...})
|
|
134
|
+
*/
|
|
135
|
+
function renderAddendumMd(record) {
|
|
136
|
+
if (!record || typeof record !== 'object') fail('renderAddendumMd requires a record object');
|
|
137
|
+
// Validate / normalize defensively so renderAddendumMd(buildAddendumRecord(x))
|
|
138
|
+
// and renderAddendumMd(rawObject) both produce a contract-valid file.
|
|
139
|
+
const rec = buildAddendumRecord({
|
|
140
|
+
kind: record.kind,
|
|
141
|
+
name: record.name,
|
|
142
|
+
composesInto: record.composes_into,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const title = rec.name
|
|
146
|
+
.split(/[-._]/)
|
|
147
|
+
.filter(Boolean)
|
|
148
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
149
|
+
.join(' ');
|
|
150
|
+
|
|
151
|
+
const composes = `[${rec.composes_into.join(', ')}]`;
|
|
152
|
+
|
|
153
|
+
const lines = [
|
|
154
|
+
'---',
|
|
155
|
+
`name: ${rec.name}`,
|
|
156
|
+
`kind: ${rec.kind}`,
|
|
157
|
+
`composes_into: ${composes}`,
|
|
158
|
+
`phase: ${rec.phase}`,
|
|
159
|
+
'---',
|
|
160
|
+
`<!-- Vendor docs: TODO add the canonical ${rec.kind} documentation URL. -->`,
|
|
161
|
+
'',
|
|
162
|
+
`# ${title}`,
|
|
163
|
+
'',
|
|
164
|
+
'## Conventions',
|
|
165
|
+
'',
|
|
166
|
+
`- TODO: how this ${rec.kind} names and structures its tokens and components.`,
|
|
167
|
+
'- TODO: the one rule a mapper most often gets wrong here.',
|
|
168
|
+
'',
|
|
169
|
+
'## File patterns',
|
|
170
|
+
'',
|
|
171
|
+
'- TODO: the config files and source-file shapes that identify this stack.',
|
|
172
|
+
`- Identify via: TODO the detectStack signal (dep name or config file) for ${rec.name}.`,
|
|
173
|
+
'',
|
|
174
|
+
'## Gotchas',
|
|
175
|
+
'',
|
|
176
|
+
'- TODO: the usage that looks like a token but is not (flag as an anti-pattern node).',
|
|
177
|
+
'- TODO: a unit or naming trap a mapper must not mis-classify.',
|
|
178
|
+
'',
|
|
179
|
+
'## Example output',
|
|
180
|
+
'',
|
|
181
|
+
'```json',
|
|
182
|
+
'{',
|
|
183
|
+
' "schema_version": "52.0",',
|
|
184
|
+
' "nodes": [',
|
|
185
|
+
' { "id": "tok.color.primary", "type": "token", "subtype": "color", "name": "TODO", "summary": "TODO brand primary token.", "complexity": "simple", "tags": ["color", "brand"] }',
|
|
186
|
+
' ],',
|
|
187
|
+
' "edges": []',
|
|
188
|
+
'}',
|
|
189
|
+
'```',
|
|
190
|
+
'',
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
return lines.join('\n');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
buildAddendumRecord,
|
|
198
|
+
renderAddendumMd,
|
|
199
|
+
targetPathFor,
|
|
200
|
+
NAME_RE,
|
|
201
|
+
KINDS,
|
|
202
|
+
KIND_SPEC,
|
|
203
|
+
PHASE,
|
|
204
|
+
};
|