@hegemonart/get-design-done 1.0.7
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 +63 -0
- package/.claude-plugin/plugin.json +54 -0
- package/CHANGELOG.md +221 -0
- package/LICENSE +21 -0
- package/README.md +724 -0
- package/SKILL.md +232 -0
- package/agents/README.md +226 -0
- package/agents/a11y-mapper.md +118 -0
- package/agents/component-taxonomy-mapper.md +88 -0
- package/agents/design-advisor.md +139 -0
- package/agents/design-assumptions-analyzer.md +171 -0
- package/agents/design-auditor.md +383 -0
- package/agents/design-context-builder.md +544 -0
- package/agents/design-context-checker-gate.md +90 -0
- package/agents/design-context-checker.md +260 -0
- package/agents/design-discussant.md +98 -0
- package/agents/design-doc-writer.md +229 -0
- package/agents/design-executor.md +452 -0
- package/agents/design-figma-writer.md +302 -0
- package/agents/design-fixer.md +180 -0
- package/agents/design-integration-checker-gate.md +93 -0
- package/agents/design-integration-checker.md +326 -0
- package/agents/design-pattern-mapper.md +206 -0
- package/agents/design-phase-researcher.md +229 -0
- package/agents/design-plan-checker.md +164 -0
- package/agents/design-planner.md +352 -0
- package/agents/design-reflector.md +175 -0
- package/agents/design-research-synthesizer.md +127 -0
- package/agents/design-verifier-gate.md +97 -0
- package/agents/design-verifier.md +605 -0
- package/agents/gdd-graphify-sync.md +100 -0
- package/agents/gdd-intel-updater.md +88 -0
- package/agents/gdd-learnings-extractor.md +85 -0
- package/agents/motion-mapper.md +103 -0
- package/agents/token-mapper.md +103 -0
- package/agents/visual-hierarchy-mapper.md +95 -0
- package/connections/chromatic.md +247 -0
- package/connections/claude-design.md +190 -0
- package/connections/connections.md +218 -0
- package/connections/figma-writer.md +139 -0
- package/connections/figma.md +146 -0
- package/connections/graphify.md +197 -0
- package/connections/pinterest.md +153 -0
- package/connections/preview.md +173 -0
- package/connections/refero.md +189 -0
- package/connections/storybook.md +280 -0
- package/hooks/budget-enforcer.js +318 -0
- package/hooks/context-exhaustion.js +127 -0
- package/hooks/gdd-read-injection-scanner.js +44 -0
- package/hooks/hooks.json +44 -0
- package/package.json +60 -0
- package/reference/BRANCH-PROTECTION.md +65 -0
- package/reference/DEPRECATIONS.md +41 -0
- package/reference/STATE-TEMPLATE.md +200 -0
- package/reference/accessibility.md +190 -0
- package/reference/anti-patterns.md +336 -0
- package/reference/audit-scoring.md +205 -0
- package/reference/checklists.md +137 -0
- package/reference/config-schema.md +319 -0
- package/reference/debugger-philosophy.md +32 -0
- package/reference/heuristics.md +201 -0
- package/reference/intel-schema.md +266 -0
- package/reference/model-prices.md +37 -0
- package/reference/model-tiers.md +118 -0
- package/reference/motion.md +285 -0
- package/reference/parallelism-rules.md +108 -0
- package/reference/priority-matrix.md +31 -0
- package/reference/project-skills-guide.md +42 -0
- package/reference/review-format.md +107 -0
- package/reference/schemas/config.schema.json +41 -0
- package/reference/schemas/hooks.schema.json +55 -0
- package/reference/schemas/intel.schema.json +191 -0
- package/reference/schemas/marketplace.schema.json +72 -0
- package/reference/schemas/plugin.schema.json +59 -0
- package/reference/shared-preamble.md +82 -0
- package/reference/typography.md +229 -0
- package/scripts/aggregate-agent-metrics.js +144 -0
- package/scripts/apply-branch-protection.sh +75 -0
- package/scripts/bootstrap-manifest.txt +3 -0
- package/scripts/bootstrap.sh +80 -0
- package/scripts/build-intel.cjs +458 -0
- package/scripts/detect-stale-refs.cjs +101 -0
- package/scripts/extract-changelog-section.cjs +57 -0
- package/scripts/release-smoke-test.cjs +169 -0
- package/scripts/rollback-release.sh +42 -0
- package/scripts/run-injection-scanner-ci.cjs +92 -0
- package/scripts/validate-frontmatter.cjs +68 -0
- package/scripts/validate-schemas.cjs +225 -0
- package/scripts/verify-version-sync.cjs +30 -0
- package/skills/add-backlog/SKILL.md +47 -0
- package/skills/analyze-dependencies/SKILL.md +184 -0
- package/skills/apply-reflections/SKILL.md +112 -0
- package/skills/audit/SKILL.md +54 -0
- package/skills/brief/SKILL.md +75 -0
- package/skills/cache-manager/SKILL.md +120 -0
- package/skills/compare/SKILL.md +322 -0
- package/skills/complete-cycle/SKILL.md +33 -0
- package/skills/darkmode/SKILL.md +331 -0
- package/skills/debug/SKILL.md +38 -0
- package/skills/design/SKILL.md +281 -0
- package/skills/discover/SKILL.md +172 -0
- package/skills/discuss/SKILL.md +67 -0
- package/skills/do/SKILL.md +45 -0
- package/skills/explore/SKILL.md +109 -0
- package/skills/extract-learnings/SKILL.md +98 -0
- package/skills/fast/SKILL.md +44 -0
- package/skills/figma-write/SKILL.md +40 -0
- package/skills/graphify/SKILL.md +48 -0
- package/skills/health/SKILL.md +48 -0
- package/skills/help/SKILL.md +76 -0
- package/skills/list-assumptions/SKILL.md +60 -0
- package/skills/map/SKILL.md +112 -0
- package/skills/new-cycle/SKILL.md +35 -0
- package/skills/new-project/SKILL.md +53 -0
- package/skills/next/SKILL.md +42 -0
- package/skills/note/SKILL.md +47 -0
- package/skills/optimize/SKILL.md +120 -0
- package/skills/pause/SKILL.md +41 -0
- package/skills/plan/SKILL.md +251 -0
- package/skills/plant-seed/SKILL.md +47 -0
- package/skills/pr-branch/SKILL.md +31 -0
- package/skills/progress/SKILL.md +60 -0
- package/skills/quick/SKILL.md +43 -0
- package/skills/reapply-patches/SKILL.md +31 -0
- package/skills/reflect/SKILL.md +73 -0
- package/skills/resume/SKILL.md +37 -0
- package/skills/review-backlog/SKILL.md +45 -0
- package/skills/router/SKILL.md +67 -0
- package/skills/scan/SKILL.md +721 -0
- package/skills/settings/SKILL.md +78 -0
- package/skills/ship/SKILL.md +31 -0
- package/skills/sketch/SKILL.md +78 -0
- package/skills/sketch-wrap-up/SKILL.md +88 -0
- package/skills/skill-manifest/SKILL.md +79 -0
- package/skills/spike/SKILL.md +67 -0
- package/skills/spike-wrap-up/SKILL.md +81 -0
- package/skills/stats/SKILL.md +50 -0
- package/skills/style/SKILL.md +193 -0
- package/skills/synthesize/SKILL.md +93 -0
- package/skills/todo/SKILL.md +54 -0
- package/skills/undo/SKILL.md +30 -0
- package/skills/update/SKILL.md +36 -0
- package/skills/verify/SKILL.md +452 -0
- package/skills/warm-cache/SKILL.md +113 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build-intel.cjs — Full initial index builder for .design/intel/ store
|
|
4
|
+
*
|
|
5
|
+
* Usage: node scripts/build-intel.cjs [--force]
|
|
6
|
+
*
|
|
7
|
+
* Scans all skill, agent, reference, connection, script, hook files in the
|
|
8
|
+
* project and writes ten JSON slices to .design/intel/.
|
|
9
|
+
*
|
|
10
|
+
* On subsequent runs, only re-extracts slices for files whose mtime or git
|
|
11
|
+
* hash has changed (incremental). Pass --force to rebuild all slices.
|
|
12
|
+
*
|
|
13
|
+
* .design/intel/ is gitignored — this script ships tracked, data does not.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { execSync } = require('child_process');
|
|
21
|
+
|
|
22
|
+
const ROOT = process.cwd();
|
|
23
|
+
const INTEL_DIR = path.join(ROOT, '.design', 'intel');
|
|
24
|
+
const FORCE = process.argv.includes('--force');
|
|
25
|
+
|
|
26
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function ensureDir(dir) {
|
|
29
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function now() {
|
|
33
|
+
return new Date().toISOString();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readJson(filePath) {
|
|
37
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
|
|
38
|
+
catch { return null; }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function writeSlice(name, data) {
|
|
42
|
+
const dest = path.join(INTEL_DIR, name);
|
|
43
|
+
fs.writeFileSync(dest, JSON.stringify(data, null, 2), 'utf8');
|
|
44
|
+
console.log(` wrote ${name}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function gitHash(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
return execSync(`git log -1 --format=%h -- "${filePath}"`, { stdio: ['pipe', 'pipe', 'ignore'] })
|
|
50
|
+
.toString().trim() || 'untracked';
|
|
51
|
+
} catch { return 'untracked'; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function headHash() {
|
|
55
|
+
try {
|
|
56
|
+
return execSync('git rev-parse --short HEAD', { stdio: ['pipe', 'pipe', 'ignore'] })
|
|
57
|
+
.toString().trim();
|
|
58
|
+
} catch { return 'unknown'; }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getMtime(filePath) {
|
|
62
|
+
try { return fs.statSync(filePath).mtime.toISOString(); }
|
|
63
|
+
catch { return null; }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getSize(filePath) {
|
|
67
|
+
try { return fs.statSync(filePath).size; }
|
|
68
|
+
catch { return 0; }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── File discovery ───────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
function classifyFile(rel) {
|
|
74
|
+
if (rel.startsWith('skills/')) return 'skill';
|
|
75
|
+
if (rel.startsWith('agents/')) return 'agent';
|
|
76
|
+
if (rel.startsWith('reference/')) return 'reference';
|
|
77
|
+
if (rel.startsWith('connections/')) return 'connection';
|
|
78
|
+
if (rel.startsWith('scripts/')) return 'script';
|
|
79
|
+
if (rel.startsWith('hooks/')) return 'hook';
|
|
80
|
+
if (rel.startsWith('tests/')) return 'test';
|
|
81
|
+
if (rel.endsWith('.json') || rel.endsWith('.yaml') || rel.endsWith('.yml')) return 'config';
|
|
82
|
+
return 'other';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function discoverFiles() {
|
|
86
|
+
const DIRS = ['skills', 'agents', 'reference', 'connections', 'scripts', 'hooks', 'tests'];
|
|
87
|
+
const EXTS = new Set(['.md', '.cjs', '.js', '.json', '.yaml', '.yml']);
|
|
88
|
+
const results = [];
|
|
89
|
+
|
|
90
|
+
for (const dir of DIRS) {
|
|
91
|
+
const abs = path.join(ROOT, dir);
|
|
92
|
+
if (!fs.existsSync(abs)) continue;
|
|
93
|
+
walkDir(abs, ROOT, EXTS, results);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Also include top-level config files
|
|
97
|
+
for (const f of ['CLAUDE.md', 'README.md']) {
|
|
98
|
+
const abs = path.join(ROOT, f);
|
|
99
|
+
if (fs.existsSync(abs)) results.push(path.relative(ROOT, abs).replace(/\\/g, '/'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return results;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function walkDir(dir, root, exts, out) {
|
|
106
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
107
|
+
const full = path.join(dir, entry.name);
|
|
108
|
+
if (entry.isDirectory()) {
|
|
109
|
+
walkDir(full, root, exts, out);
|
|
110
|
+
} else if (exts.has(path.extname(entry.name))) {
|
|
111
|
+
out.push(path.relative(root, full).replace(/\\/g, '/'));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Changed-file detection ───────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
function changedFiles(allFiles, existingFilesSlice) {
|
|
119
|
+
if (FORCE || !existingFilesSlice) return allFiles;
|
|
120
|
+
|
|
121
|
+
const index = {};
|
|
122
|
+
for (const f of (existingFilesSlice.files || [])) {
|
|
123
|
+
index[f.path] = { mtime: f.mtime, git_hash: f.git_hash };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return allFiles.filter(rel => {
|
|
127
|
+
const abs = path.join(ROOT, rel);
|
|
128
|
+
const prev = index[rel];
|
|
129
|
+
if (!prev) return true;
|
|
130
|
+
const curMtime = getMtime(abs);
|
|
131
|
+
const curHash = gitHash(rel);
|
|
132
|
+
return curMtime !== prev.mtime || curHash !== prev.git_hash;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── Frontmatter parser ───────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
function parseFrontmatter(content) {
|
|
139
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
140
|
+
if (!match) return {};
|
|
141
|
+
const fm = {};
|
|
142
|
+
for (const line of match[1].split('\n')) {
|
|
143
|
+
const kv = line.match(/^(\w[\w-]*):\s*(.+)$/);
|
|
144
|
+
if (kv) fm[kv[1]] = kv[2].trim().replace(/^["']|["']$/g, '');
|
|
145
|
+
}
|
|
146
|
+
return fm;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Slice builders ───────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
function buildFilesSlice(allFiles) {
|
|
152
|
+
return {
|
|
153
|
+
generated: now(),
|
|
154
|
+
git_hash: headHash(),
|
|
155
|
+
files: allFiles.map(rel => {
|
|
156
|
+
const abs = path.join(ROOT, rel);
|
|
157
|
+
return {
|
|
158
|
+
path: rel,
|
|
159
|
+
type: classifyFile(rel),
|
|
160
|
+
mtime: getMtime(abs),
|
|
161
|
+
size_bytes: getSize(abs),
|
|
162
|
+
git_hash: gitHash(rel),
|
|
163
|
+
};
|
|
164
|
+
}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildExportsSlice(allFiles) {
|
|
169
|
+
const exports = [];
|
|
170
|
+
for (const rel of allFiles) {
|
|
171
|
+
if (!rel.endsWith('.md')) continue;
|
|
172
|
+
const abs = path.join(ROOT, rel);
|
|
173
|
+
let content;
|
|
174
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
175
|
+
const fm = parseFrontmatter(content);
|
|
176
|
+
if (!fm.name) continue;
|
|
177
|
+
|
|
178
|
+
const kind = classifyFile(rel);
|
|
179
|
+
const entry = { file: rel, kind, name: fm.name };
|
|
180
|
+
// Skills have a command derived from name (gdd-foo → /gdd:foo)
|
|
181
|
+
if (kind === 'skill' && fm.name.startsWith('gdd-')) {
|
|
182
|
+
entry.command = '/gdd:' + fm.name.replace(/^gdd-/, '');
|
|
183
|
+
}
|
|
184
|
+
exports.push(entry);
|
|
185
|
+
}
|
|
186
|
+
return { generated: now(), exports };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function buildSymbolsSlice(allFiles) {
|
|
190
|
+
const symbols = [];
|
|
191
|
+
const HEADING_RE = /^(#{1,4})\s+(.+)$/gm;
|
|
192
|
+
for (const rel of allFiles) {
|
|
193
|
+
if (!rel.endsWith('.md')) continue;
|
|
194
|
+
const abs = path.join(ROOT, rel);
|
|
195
|
+
let content;
|
|
196
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
197
|
+
const lines = content.split('\n');
|
|
198
|
+
lines.forEach((line, i) => {
|
|
199
|
+
const m = line.match(/^(#{1,4})\s+(.+)$/);
|
|
200
|
+
if (!m) return;
|
|
201
|
+
const heading = m[0];
|
|
202
|
+
const level = m[1].length;
|
|
203
|
+
const text = m[2].trim();
|
|
204
|
+
const anchor = text.toLowerCase().replace(/[^\w\s-]/g, '').replace(/\s+/g, '-');
|
|
205
|
+
symbols.push({ file: rel, heading, level, anchor, line: i + 1 });
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return { generated: now(), symbols };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function buildTokensSlice(allFiles) {
|
|
212
|
+
const tokens = [];
|
|
213
|
+
const TOKEN_RE = /--[\w-]+(color|space|spacing|font|radius|shadow|motion|size|weight|line)[^\s,;)']*/gi;
|
|
214
|
+
const CAT_MAP = {
|
|
215
|
+
color: 'color', space: 'spacing', spacing: 'spacing',
|
|
216
|
+
font: 'typography', radius: 'radius', shadow: 'shadow', motion: 'motion',
|
|
217
|
+
};
|
|
218
|
+
for (const rel of allFiles) {
|
|
219
|
+
if (!rel.endsWith('.md') && !rel.endsWith('.cjs') && !rel.endsWith('.js')) continue;
|
|
220
|
+
const abs = path.join(ROOT, rel);
|
|
221
|
+
let content;
|
|
222
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
223
|
+
const lines = content.split('\n');
|
|
224
|
+
lines.forEach((line, i) => {
|
|
225
|
+
let m;
|
|
226
|
+
const re = /--[\w-]+/g;
|
|
227
|
+
while ((m = re.exec(line)) !== null) {
|
|
228
|
+
const token = m[0];
|
|
229
|
+
// Categorise by suffix keywords
|
|
230
|
+
let category = 'other';
|
|
231
|
+
for (const [key, cat] of Object.entries(CAT_MAP)) {
|
|
232
|
+
if (token.includes(key)) { category = cat; break; }
|
|
233
|
+
}
|
|
234
|
+
if (category === 'other' && !token.includes('-')) continue;
|
|
235
|
+
tokens.push({ file: rel, token, category, line: i + 1, context: line.trim().slice(0, 80) });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return { generated: now(), tokens };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function buildComponentsSlice(allFiles) {
|
|
243
|
+
const components = [];
|
|
244
|
+
// PascalCase component names in markdown
|
|
245
|
+
const COMP_RE = /\b([A-Z][a-zA-Z0-9]{2,})\b/g;
|
|
246
|
+
const SKIP = new Set(['WCAG', 'CSS', 'JSON', 'HTML', 'URL', 'API', 'MCP', 'GDD', 'UI', 'UX', 'ISO']);
|
|
247
|
+
for (const rel of allFiles) {
|
|
248
|
+
if (!rel.endsWith('.md')) continue;
|
|
249
|
+
const abs = path.join(ROOT, rel);
|
|
250
|
+
let content;
|
|
251
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
252
|
+
const lines = content.split('\n');
|
|
253
|
+
lines.forEach((line, i) => {
|
|
254
|
+
// Skip code blocks
|
|
255
|
+
if (line.trim().startsWith('```') || line.trim().startsWith('`')) return;
|
|
256
|
+
let m;
|
|
257
|
+
while ((m = COMP_RE.exec(line)) !== null) {
|
|
258
|
+
const name = m[1];
|
|
259
|
+
if (SKIP.has(name)) continue;
|
|
260
|
+
const role = line.includes('define') || line.includes('export') ? 'definition' : 'reference';
|
|
261
|
+
components.push({ file: rel, component: name, role, line: i + 1 });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// Deduplicate by file+component+line
|
|
266
|
+
const seen = new Set();
|
|
267
|
+
return {
|
|
268
|
+
generated: now(),
|
|
269
|
+
components: components.filter(c => {
|
|
270
|
+
const key = `${c.file}:${c.component}:${c.line}`;
|
|
271
|
+
if (seen.has(key)) return false;
|
|
272
|
+
seen.add(key);
|
|
273
|
+
return true;
|
|
274
|
+
}),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function buildPatternsSlice(allFiles) {
|
|
279
|
+
const patterns = [];
|
|
280
|
+
const PATTERN_CATS = ['color-system', 'spacing-system', 'typography-system', 'component-styling', 'layout', 'interaction'];
|
|
281
|
+
for (const rel of allFiles) {
|
|
282
|
+
if (!rel.endsWith('.md')) continue;
|
|
283
|
+
const abs = path.join(ROOT, rel);
|
|
284
|
+
let content;
|
|
285
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
286
|
+
for (const cat of PATTERN_CATS) {
|
|
287
|
+
if (content.includes(cat)) {
|
|
288
|
+
// Find the line
|
|
289
|
+
const lines = content.split('\n');
|
|
290
|
+
lines.forEach((line, i) => {
|
|
291
|
+
if (line.includes(cat)) {
|
|
292
|
+
const name = cat;
|
|
293
|
+
patterns.push({
|
|
294
|
+
name,
|
|
295
|
+
category: cat,
|
|
296
|
+
source_file: rel,
|
|
297
|
+
description: line.trim().slice(0, 100),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Deduplicate
|
|
305
|
+
const seen = new Set();
|
|
306
|
+
return {
|
|
307
|
+
generated: now(),
|
|
308
|
+
patterns: patterns.filter(p => {
|
|
309
|
+
const key = `${p.source_file}:${p.category}:${p.description}`;
|
|
310
|
+
if (seen.has(key)) return false;
|
|
311
|
+
seen.add(key);
|
|
312
|
+
return true;
|
|
313
|
+
}),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function buildDependenciesSlice(allFiles) {
|
|
318
|
+
const dependencies = [];
|
|
319
|
+
// @-references in markdown
|
|
320
|
+
const AT_REF_RE = /@([\w./\-]+\.md)/g;
|
|
321
|
+
// reads-from in agent frontmatter (reads: field)
|
|
322
|
+
for (const rel of allFiles) {
|
|
323
|
+
if (!rel.endsWith('.md')) continue;
|
|
324
|
+
const abs = path.join(ROOT, rel);
|
|
325
|
+
let content;
|
|
326
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
327
|
+
const lines = content.split('\n');
|
|
328
|
+
lines.forEach((line, i) => {
|
|
329
|
+
let m;
|
|
330
|
+
while ((m = AT_REF_RE.exec(line)) !== null) {
|
|
331
|
+
const target = m[1];
|
|
332
|
+
dependencies.push({ from: rel, to: target, kind: 'at-reference', line: i + 1 });
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
// Check frontmatter reads field
|
|
336
|
+
const fm = parseFrontmatter(content);
|
|
337
|
+
if (fm.reads) {
|
|
338
|
+
// reads: comma or space separated list
|
|
339
|
+
const targets = fm.reads.split(/[\s,]+/).filter(Boolean);
|
|
340
|
+
for (const t of targets) {
|
|
341
|
+
if (t.endsWith('.md') || t.endsWith('.json')) {
|
|
342
|
+
dependencies.push({ from: rel, to: t, kind: 'reads-from', line: 1 });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return { generated: now(), dependencies };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function buildDecisionsSlice() {
|
|
351
|
+
// Decisions come from .design/DESIGN-CONTEXT.md (runtime, may not exist at build time)
|
|
352
|
+
const decisions = [];
|
|
353
|
+
const CONTEXT_FILE = path.join(ROOT, '.design', 'DESIGN-CONTEXT.md');
|
|
354
|
+
if (!fs.existsSync(CONTEXT_FILE)) {
|
|
355
|
+
return { generated: now(), decisions };
|
|
356
|
+
}
|
|
357
|
+
const content = fs.readFileSync(CONTEXT_FILE, 'utf8');
|
|
358
|
+
const lines = content.split('\n');
|
|
359
|
+
const DECISION_RE = /\b(D-\d+)[:\s]+(.+)/;
|
|
360
|
+
lines.forEach((line, i) => {
|
|
361
|
+
const m = line.match(DECISION_RE);
|
|
362
|
+
if (m) {
|
|
363
|
+
decisions.push({
|
|
364
|
+
id: m[1],
|
|
365
|
+
summary: m[2].trim().slice(0, 120),
|
|
366
|
+
source_file: '.design/DESIGN-CONTEXT.md',
|
|
367
|
+
line: i + 1,
|
|
368
|
+
date: now().slice(0, 10),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
return { generated: now(), decisions };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function buildDebtSlice() {
|
|
376
|
+
const debt = [];
|
|
377
|
+
const DEBT_FILE = path.join(ROOT, '.design', 'DESIGN-DEBT.md');
|
|
378
|
+
if (!fs.existsSync(DEBT_FILE)) {
|
|
379
|
+
return { generated: now(), debt };
|
|
380
|
+
}
|
|
381
|
+
const content = fs.readFileSync(DEBT_FILE, 'utf8');
|
|
382
|
+
const lines = content.split('\n');
|
|
383
|
+
const ITEM_RE = /[-*]\s+(?:\*\*(DEBT-\d+|high|medium|low)\*\*[:\s]+)?(.+)/i;
|
|
384
|
+
const SEV_RE = /high|medium|low/i;
|
|
385
|
+
let debtId = 0;
|
|
386
|
+
lines.forEach((line, i) => {
|
|
387
|
+
const m = line.match(ITEM_RE);
|
|
388
|
+
if (!m) return;
|
|
389
|
+
const sevMatch = line.match(SEV_RE);
|
|
390
|
+
const severity = sevMatch ? sevMatch[0].toLowerCase() : 'medium';
|
|
391
|
+
debtId++;
|
|
392
|
+
const id = line.match(/DEBT-\d+/) ? line.match(/DEBT-\d+/)[0] : `DEBT-${String(debtId).padStart(2, '0')}`;
|
|
393
|
+
debt.push({
|
|
394
|
+
id,
|
|
395
|
+
summary: m[2].trim().slice(0, 120),
|
|
396
|
+
severity,
|
|
397
|
+
source_file: '.design/DESIGN-DEBT.md',
|
|
398
|
+
line: i + 1,
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
return { generated: now(), debt };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function buildGraphSlice(filesSlice, depsSlice) {
|
|
405
|
+
const nodes = (filesSlice.files || []).map(f => ({
|
|
406
|
+
id: f.path,
|
|
407
|
+
type: f.type,
|
|
408
|
+
name: path.basename(f.path, path.extname(f.path)),
|
|
409
|
+
}));
|
|
410
|
+
const edges = (depsSlice.dependencies || []).map(d => ({
|
|
411
|
+
from: d.from,
|
|
412
|
+
to: d.to,
|
|
413
|
+
kind: d.kind,
|
|
414
|
+
}));
|
|
415
|
+
return { generated: now(), nodes, edges };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
async function main() {
|
|
421
|
+
console.log('build-intel: starting' + (FORCE ? ' (--force)' : ''));
|
|
422
|
+
ensureDir(INTEL_DIR);
|
|
423
|
+
|
|
424
|
+
const existingFiles = readJson(path.join(INTEL_DIR, 'files.json'));
|
|
425
|
+
const allFiles = discoverFiles();
|
|
426
|
+
const changed = changedFiles(allFiles, existingFiles);
|
|
427
|
+
|
|
428
|
+
console.log(` discovered ${allFiles.length} files, ${changed.length} changed`);
|
|
429
|
+
|
|
430
|
+
// Always rebuild files slice when any file changed
|
|
431
|
+
const filesSlice = buildFilesSlice(allFiles);
|
|
432
|
+
writeSlice('files.json', filesSlice);
|
|
433
|
+
|
|
434
|
+
// Rebuild content slices only for changed files (or all if force)
|
|
435
|
+
const targetFiles = FORCE ? allFiles : changed;
|
|
436
|
+
|
|
437
|
+
// For simplicity, rebuild all content slices when any file changed.
|
|
438
|
+
// A production version would merge existing slices with updated entries.
|
|
439
|
+
if (targetFiles.length > 0 || FORCE) {
|
|
440
|
+
writeSlice('exports.json', buildExportsSlice(allFiles));
|
|
441
|
+
writeSlice('symbols.json', buildSymbolsSlice(allFiles));
|
|
442
|
+
writeSlice('tokens.json', buildTokensSlice(allFiles));
|
|
443
|
+
writeSlice('components.json', buildComponentsSlice(allFiles));
|
|
444
|
+
writeSlice('patterns.json', buildPatternsSlice(allFiles));
|
|
445
|
+
const depsSlice = buildDependenciesSlice(allFiles);
|
|
446
|
+
writeSlice('dependencies.json', depsSlice);
|
|
447
|
+
writeSlice('decisions.json', buildDecisionsSlice());
|
|
448
|
+
writeSlice('debt.json', buildDebtSlice());
|
|
449
|
+
writeSlice('graph.json', buildGraphSlice(filesSlice, depsSlice));
|
|
450
|
+
} else {
|
|
451
|
+
console.log(' no changes detected — skipping content slices');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
console.log('build-intel: done');
|
|
455
|
+
console.log(` output: ${INTEL_DIR}`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
main().catch(err => { console.error(err); process.exit(1); });
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Detect deprecated namespace and stage/agent references in shipped markdown.
|
|
4
|
+
// Reads reference/DEPRECATIONS.md as the authoritative list of stale tokens.
|
|
5
|
+
// Exits 0 if clean, 1 on any match.
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
11
|
+
const DEPRECATIONS_PATH = path.join(REPO_ROOT, 'reference/DEPRECATIONS.md');
|
|
12
|
+
|
|
13
|
+
// Patterns checked against every .md file body (not against DEPRECATIONS.md itself).
|
|
14
|
+
// Scope: only truly unambiguous deprecations — names that must not appear anywhere
|
|
15
|
+
// outside DEPRECATIONS.md. Current-but-historically-renamed agents/skills
|
|
16
|
+
// (design-context-builder, design-pattern-mapper, scan/, discover/) are NOT
|
|
17
|
+
// flagged here because they still exist as live files in the tree; the
|
|
18
|
+
// rename/split documented in DEPRECATIONS.md was partial, so static detection
|
|
19
|
+
// would over-fire. Cover those cases via targeted review rather than grep.
|
|
20
|
+
const PATTERNS = [
|
|
21
|
+
{ name: '/design: namespace (replaced by /gdd:)', regex: /\/design:[a-z-]+/g },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const EXCLUDE_DIRS = new Set([
|
|
25
|
+
'node_modules',
|
|
26
|
+
'.planning',
|
|
27
|
+
'.claude',
|
|
28
|
+
'.design',
|
|
29
|
+
'test-fixture',
|
|
30
|
+
'.git',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const EXCLUDE_FILES = new Set([
|
|
34
|
+
// Normalize to forward-slashes so Windows matches this set correctly.
|
|
35
|
+
path.relative(REPO_ROOT, DEPRECATIONS_PATH).split(path.sep).join('/'),
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
function ensureDeprecationsExists() {
|
|
39
|
+
if (fs.existsSync(DEPRECATIONS_PATH)) return;
|
|
40
|
+
const stub = [
|
|
41
|
+
'# Deprecated Namespaces and Names',
|
|
42
|
+
'',
|
|
43
|
+
'Auto-generated stub — edit this file to declare deprecations authoritatively.',
|
|
44
|
+
'',
|
|
45
|
+
'## Stale command namespaces',
|
|
46
|
+
'- `/design:*` — replaced by `/gdd:*`',
|
|
47
|
+
'',
|
|
48
|
+
'## Stale agent names',
|
|
49
|
+
'- `design-context-builder` — replaced',
|
|
50
|
+
'- `design-pattern-mapper` (as single blob) — replaced',
|
|
51
|
+
'',
|
|
52
|
+
'## Stale stage names',
|
|
53
|
+
'- `scan` — folded into `/gdd:explore`',
|
|
54
|
+
'- `discover` — folded into `/gdd:explore`',
|
|
55
|
+
'',
|
|
56
|
+
].join('\n');
|
|
57
|
+
fs.mkdirSync(path.dirname(DEPRECATIONS_PATH), { recursive: true });
|
|
58
|
+
fs.writeFileSync(DEPRECATIONS_PATH, stub, 'utf8');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function walk(dir, out) {
|
|
62
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
if (EXCLUDE_DIRS.has(entry.name)) continue;
|
|
65
|
+
walk(path.join(dir, entry.name), out);
|
|
66
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
67
|
+
const full = path.join(dir, entry.name);
|
|
68
|
+
const rel = path.relative(REPO_ROOT, full).split(path.sep).join('/');
|
|
69
|
+
if (EXCLUDE_FILES.has(rel)) continue;
|
|
70
|
+
out.push(full);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function main() {
|
|
76
|
+
ensureDeprecationsExists();
|
|
77
|
+
const files = [];
|
|
78
|
+
walk(REPO_ROOT, files);
|
|
79
|
+
|
|
80
|
+
let findings = 0;
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const body = fs.readFileSync(file, 'utf8');
|
|
83
|
+
const lines = body.split('\n');
|
|
84
|
+
for (let i = 0; i < lines.length; i++) {
|
|
85
|
+
for (const { name, regex } of PATTERNS) {
|
|
86
|
+
regex.lastIndex = 0;
|
|
87
|
+
let m;
|
|
88
|
+
while ((m = regex.exec(lines[i])) !== null) {
|
|
89
|
+
const rel = path.relative(REPO_ROOT, file);
|
|
90
|
+
console.log(`${rel}:${i + 1}: ${name} → ${m[0]}`);
|
|
91
|
+
findings++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`summary: ${files.length} files scanned, ${findings} stale refs found`);
|
|
98
|
+
process.exit(findings === 0 ? 0 : 1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Extract the `## [<version>]` section of CHANGELOG.md and print to stdout.
|
|
4
|
+
// Used by .github/workflows/release.yml to build the GitHub Release body.
|
|
5
|
+
//
|
|
6
|
+
// Usage: node scripts/extract-changelog-section.cjs <version>
|
|
7
|
+
// Example: node scripts/extract-changelog-section.cjs 1.0.7
|
|
8
|
+
//
|
|
9
|
+
// Exit codes:
|
|
10
|
+
// 0 — section found, body printed
|
|
11
|
+
// 1 — no matching section
|
|
12
|
+
// 2 — missing or empty version argument
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const version = (process.argv[2] || '').trim();
|
|
18
|
+
if (!version) {
|
|
19
|
+
console.error('Usage: node scripts/extract-changelog-section.cjs <version>');
|
|
20
|
+
process.exit(2);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const changelogPath = path.join(__dirname, '..', 'CHANGELOG.md');
|
|
24
|
+
if (!fs.existsSync(changelogPath)) {
|
|
25
|
+
console.error(`ERROR: CHANGELOG.md not found at ${changelogPath}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const body = fs.readFileSync(changelogPath, 'utf8').replace(/\r\n/g, '\n');
|
|
30
|
+
const lines = body.split('\n');
|
|
31
|
+
const headingRe = new RegExp(`^##\\s*\\[${version.replace(/\./g, '\\.')}\\]`);
|
|
32
|
+
const nextHeadingRe = /^##\s*\[/;
|
|
33
|
+
|
|
34
|
+
let capture = false;
|
|
35
|
+
const out = [];
|
|
36
|
+
for (let i = 0; i < lines.length; i++) {
|
|
37
|
+
const line = lines[i];
|
|
38
|
+
if (!capture) {
|
|
39
|
+
if (headingRe.test(line)) capture = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Stop at next version heading or a standalone horizontal rule
|
|
43
|
+
if (nextHeadingRe.test(line)) break;
|
|
44
|
+
if (line.trim() === '---') break;
|
|
45
|
+
out.push(line);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!capture) {
|
|
49
|
+
console.error(`ERROR: no section for version ${version}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Trim leading + trailing blank lines
|
|
54
|
+
while (out.length && out[0].trim() === '') out.shift();
|
|
55
|
+
while (out.length && out[out.length - 1].trim() === '') out.pop();
|
|
56
|
+
|
|
57
|
+
process.stdout.write(out.join('\n') + '\n');
|