@hegemonart/get-design-done 1.53.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 +41 -0
- package/README.md +2 -0
- package/SKILL.md +2 -1
- package/agents/component-taxonomy-mapper.md +3 -0
- package/agents/motion-mapper.md +1 -0
- package/agents/token-mapper.md +3 -0
- 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 +138 -1
- package/scripts/lib/explore-parallel-runner/types.ts +27 -0
- package/scripts/lib/health-mirror/index.cjs +73 -1
- package/scripts/lib/manifest/skills.json +8 -0
- package/scripts/lib/mapper-spawn.cjs +257 -0
- package/scripts/lib/mapper-spawn.d.cts +60 -0
- package/scripts/lib/new-addendum.cjs +204 -0
- package/sdk/cli/index.js +1135 -1
- package/sdk/mcp/gdd-mcp/server.js +1047 -0
- package/skills/new-addendum/SKILL.md +81 -0
|
@@ -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
|
+
};
|