@hegemonart/get-design-done 1.31.0 → 1.32.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 +75 -0
- package/NOTICE +262 -0
- package/README.md +13 -1
- package/SKILL.md +4 -0
- package/agents/design-authority-watcher.md +1 -1
- package/agents/perf-analyzer.md +2 -2
- package/bin/gdd-mcp +78 -0
- package/bin/gdd-sdk +34 -24
- package/bin/gdd-state-mcp +78 -0
- package/{README.de.md → docs/i18n/README.de.md} +1 -1
- package/{README.fr.md → docs/i18n/README.fr.md} +1 -1
- package/{README.it.md → docs/i18n/README.it.md} +1 -1
- package/{README.ja.md → docs/i18n/README.ja.md} +1 -1
- package/{README.ko.md → docs/i18n/README.ko.md} +1 -1
- package/{README.zh-CN.md → docs/i18n/README.zh-CN.md} +1 -1
- package/hooks/_hook-emit.js +1 -1
- package/hooks/budget-enforcer.ts +5 -5
- package/hooks/context-exhaustion.ts +2 -2
- package/hooks/gdd-precompact-snapshot.js +3 -3
- package/hooks/gdd-read-injection-scanner.ts +2 -2
- package/hooks/gdd-sessionstart-recap.js +1 -1
- package/hooks/gdd-turn-closeout.js +1 -1
- package/hooks/hooks.json +9 -0
- package/hooks/inject-using-gdd.sh +72 -0
- package/hooks/run-hook.cmd +35 -0
- package/package.json +20 -9
- package/recipes/.gitkeep +0 -0
- package/reference/schemas/events.schema.json +63 -1
- package/reference/schemas/recipe.schema.json +33 -0
- package/scripts/cli/gdd-events.mjs +5 -5
- package/scripts/lib/cache/gdd-cache-manager.cjs +1 -1
- package/scripts/lib/cli/index.ts +22 -160
- package/scripts/lib/connection-probe/index.cjs +1 -1
- package/scripts/lib/discuss-parallel-runner/aggregator.ts +1 -1
- package/scripts/lib/discuss-parallel-runner/index.ts +1 -1
- package/scripts/lib/error-classifier.cjs +24 -227
- package/scripts/lib/event-stream/index.ts +25 -193
- package/scripts/lib/gdd-errors/index.ts +24 -213
- package/scripts/lib/gdd-state/index.ts +23 -161
- package/scripts/lib/health-mirror/index.cjs +79 -1
- package/scripts/lib/iteration-budget.cjs +23 -199
- package/scripts/lib/jittered-backoff.cjs +24 -107
- package/scripts/lib/lockfile.cjs +23 -195
- package/scripts/lib/logger/index.ts +1 -1
- package/scripts/lib/parallelism-engine/concurrency-tuner.cjs +1 -1
- package/scripts/lib/perf-analyzer/index.cjs +1 -1
- package/scripts/lib/pipeline-runner/index.ts +4 -4
- package/scripts/lib/pipeline-runner/state-machine.ts +1 -1
- package/scripts/lib/prompt-dedup/index.cjs +1 -1
- package/scripts/lib/rate-guard.cjs +2 -2
- package/scripts/lib/recipe-loader.cjs +142 -0
- package/scripts/lib/session-runner/errors.ts +3 -3
- package/scripts/lib/session-runner/index.ts +3 -3
- package/scripts/lib/session-runner/transcript.ts +1 -1
- package/scripts/lib/tool-scoping/index.ts +1 -1
- package/scripts/mcp-servers/gdd-mcp/server.ts +29 -311
- package/scripts/mcp-servers/gdd-state/server.ts +28 -282
- package/sdk/README.md +45 -0
- package/{scripts/lib → sdk}/cli/commands/audit.ts +3 -3
- package/{scripts/lib → sdk}/cli/commands/init.ts +3 -3
- package/{scripts/lib → sdk}/cli/commands/query.ts +4 -4
- package/{scripts/lib → sdk}/cli/commands/run.ts +5 -5
- package/{scripts/lib → sdk}/cli/commands/stage.ts +5 -5
- package/sdk/cli/index.js +8091 -0
- package/sdk/cli/index.ts +172 -0
- package/{scripts/lib → sdk}/cli/parse-args.ts +2 -2
- package/{scripts/lib/gdd-errors → sdk/errors}/classification.ts +1 -1
- package/sdk/errors/index.ts +218 -0
- package/{scripts/lib → sdk}/event-stream/emitter.ts +1 -1
- package/sdk/event-stream/index.ts +197 -0
- package/{scripts/lib → sdk}/event-stream/reader.ts +1 -1
- package/{scripts/lib → sdk}/event-stream/types.ts +2 -2
- package/{scripts/lib → sdk}/event-stream/writer.ts +1 -1
- package/sdk/index.ts +19 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/README.md +3 -3
- package/sdk/mcp/gdd-mcp/server.js +1966 -0
- package/sdk/mcp/gdd-mcp/server.ts +325 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_cycle_recap.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_decisions_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_events_tail.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_health.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_intel_get.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_learnings_digest.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phase_current.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phases_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_plans_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_reflections_latest.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_status.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_telemetry_query.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/index.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/shared.ts +3 -3
- package/sdk/mcp/gdd-state/server.js +2790 -0
- package/sdk/mcp/gdd-state/server.ts +294 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_blocker.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_decision.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_must_have.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/checkpoint.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/frontmatter_update.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/get.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/index.ts +1 -1
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/probe_connections.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/resolve_blocker.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/set_status.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/shared.ts +8 -8
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/transition_stage.ts +4 -4
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/update_progress.ts +2 -2
- package/sdk/primitives/error-classifier.cjs +232 -0
- package/sdk/primitives/iteration-budget.cjs +205 -0
- package/sdk/primitives/jittered-backoff.cjs +112 -0
- package/sdk/primitives/lockfile.cjs +201 -0
- package/{scripts/lib/gdd-state → sdk/state}/gates.ts +1 -1
- package/sdk/state/index.ts +167 -0
- package/{scripts/lib/gdd-state → sdk/state}/lockfile.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/mutator.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/parser.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/types.ts +4 -4
- package/skills/audit/SKILL.md +13 -0
- package/skills/brief/SKILL.md +25 -0
- package/skills/design/SKILL.md +17 -0
- package/skills/discuss/SKILL.md +13 -0
- package/skills/explore/SKILL.md +17 -0
- package/skills/health/SKILL.md +6 -0
- package/skills/plan/SKILL.md +25 -0
- package/skills/quality-gate/SKILL.md +2 -2
- package/skills/router/SKILL.md +4 -0
- package/skills/router/router-pick-emitter.md +78 -0
- package/skills/using-gdd/SKILL.md +78 -0
- package/skills/verify/SKILL.md +17 -0
- package/scripts/aggregate-agent-metrics.ts +0 -282
- package/scripts/bootstrap-manifest.txt +0 -3
- package/scripts/bootstrap.sh +0 -80
- package/scripts/build-distribution-bundles.cjs +0 -549
- package/scripts/build-intel.cjs +0 -486
- package/scripts/codegen-schema-types.ts +0 -149
- package/scripts/detect-stale-refs.cjs +0 -107
- package/scripts/e2e/run-headless.ts +0 -514
- package/scripts/extract-changelog-section.cjs +0 -58
- package/scripts/gsd-cleanup-incubator.cjs +0 -367
- package/scripts/injection-patterns.cjs +0 -58
- package/scripts/lint-agentskills-spec.cjs +0 -457
- package/scripts/release-smoke-test.cjs +0 -200
- package/scripts/rollback-release.sh +0 -42
- package/scripts/run-injection-scanner-ci.cjs +0 -83
- package/scripts/tests/test-authority-rejected-kinds.sh +0 -58
- package/scripts/tests/test-authority-watcher-diff.sh +0 -113
- package/scripts/tests/test-motion-provenance.sh +0 -64
- package/scripts/validate-frontmatter.ts +0 -409
- package/scripts/validate-incubator-scope.cjs +0 -133
- package/scripts/validate-schemas.ts +0 -401
- package/scripts/validate-skill-length.cjs +0 -283
- package/scripts/verify-version-sync.cjs +0 -30
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_cycle_recap.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_decisions_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_events_tail.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_health.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_intel_get.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_learnings_digest.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phase_current.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phases_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_plans_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_reflections_latest.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_status.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_telemetry_query.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_blocker.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_decision.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_must_have.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/checkpoint.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/frontmatter_update.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/get.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/probe_connections.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/resolve_blocker.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/set_status.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/transition_stage.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/update_progress.schema.json +0 -0
- /package/{scripts/lib → sdk/primitives}/error-classifier.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/iteration-budget.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/jittered-backoff.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/lockfile.d.cts +0 -0
package/scripts/build-intel.cjs
DELETED
|
@@ -1,486 +0,0 @@
|
|
|
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 { spawnSync } = 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
|
-
const r = spawnSync('git', ['log', '-1', '--format=%h', '--', filePath], {
|
|
50
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
51
|
-
encoding: 'utf8',
|
|
52
|
-
timeout: 5000,
|
|
53
|
-
});
|
|
54
|
-
return r.stdout.trim() || 'untracked';
|
|
55
|
-
} catch { return 'untracked'; }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function headHash() {
|
|
59
|
-
try {
|
|
60
|
-
const r = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {
|
|
61
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
62
|
-
encoding: 'utf8',
|
|
63
|
-
timeout: 5000,
|
|
64
|
-
});
|
|
65
|
-
return r.stdout.trim() || 'unknown';
|
|
66
|
-
} catch { return 'unknown'; }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function getMtime(filePath) {
|
|
70
|
-
try { return fs.statSync(filePath).mtime.toISOString(); }
|
|
71
|
-
catch { return null; }
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function getSize(filePath) {
|
|
75
|
-
try { return fs.statSync(filePath).size; }
|
|
76
|
-
catch { return 0; }
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ── File discovery ───────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
function classifyFile(rel) {
|
|
82
|
-
if (rel.startsWith('skills/')) return 'skill';
|
|
83
|
-
if (rel.startsWith('agents/')) return 'agent';
|
|
84
|
-
if (rel.startsWith('reference/')) return 'reference';
|
|
85
|
-
if (rel.startsWith('connections/')) return 'connection';
|
|
86
|
-
if (rel.startsWith('scripts/')) return 'script';
|
|
87
|
-
if (rel.startsWith('hooks/')) return 'hook';
|
|
88
|
-
if (rel.startsWith('tests/')) return 'test';
|
|
89
|
-
if (rel.endsWith('.json') || rel.endsWith('.yaml') || rel.endsWith('.yml')) return 'config';
|
|
90
|
-
return 'other';
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function discoverFiles() {
|
|
94
|
-
const DIRS = ['skills', 'agents', 'reference', 'connections', 'scripts', 'hooks', 'tests'];
|
|
95
|
-
const EXTS = new Set(['.md', '.cjs', '.js', '.json', '.yaml', '.yml']);
|
|
96
|
-
const results = [];
|
|
97
|
-
|
|
98
|
-
for (const dir of DIRS) {
|
|
99
|
-
const abs = path.join(ROOT, dir);
|
|
100
|
-
if (!fs.existsSync(abs)) continue;
|
|
101
|
-
walkDir(abs, ROOT, EXTS, results);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Also include top-level config files
|
|
105
|
-
for (const f of ['CLAUDE.md', 'README.md']) {
|
|
106
|
-
const abs = path.join(ROOT, f);
|
|
107
|
-
if (fs.existsSync(abs)) results.push(path.relative(ROOT, abs).replace(/\\/g, '/'));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return results;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function walkDir(dir, root, exts, out) {
|
|
114
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
115
|
-
const full = path.join(dir, entry.name);
|
|
116
|
-
if (entry.isDirectory()) {
|
|
117
|
-
walkDir(full, root, exts, out);
|
|
118
|
-
} else if (exts.has(path.extname(entry.name))) {
|
|
119
|
-
out.push(path.relative(root, full).replace(/\\/g, '/'));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ── Changed-file detection ───────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
function changedFiles(allFiles, existingFilesSlice) {
|
|
127
|
-
if (FORCE || !existingFilesSlice) return allFiles;
|
|
128
|
-
|
|
129
|
-
const index = {};
|
|
130
|
-
for (const f of (existingFilesSlice.files || [])) {
|
|
131
|
-
index[f.path] = { mtime: f.mtime, git_hash: f.git_hash };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return allFiles.filter(rel => {
|
|
135
|
-
const abs = path.join(ROOT, rel);
|
|
136
|
-
const prev = index[rel];
|
|
137
|
-
if (!prev) return true;
|
|
138
|
-
const curMtime = getMtime(abs);
|
|
139
|
-
const curHash = gitHash(rel);
|
|
140
|
-
return curMtime !== prev.mtime || curHash !== prev.git_hash;
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// ── Frontmatter parser ───────────────────────────────────────────────────────
|
|
145
|
-
|
|
146
|
-
function parseFrontmatter(content) {
|
|
147
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
148
|
-
if (!match) return {};
|
|
149
|
-
const fm = {};
|
|
150
|
-
for (const line of match[1].split('\n')) {
|
|
151
|
-
const kv = line.match(/^(\w[\w-]*):\s*(.+)$/);
|
|
152
|
-
if (kv) fm[kv[1]] = kv[2].trim().replace(/^["']|["']$/g, '');
|
|
153
|
-
}
|
|
154
|
-
return fm;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ── Slice builders ───────────────────────────────────────────────────────────
|
|
158
|
-
|
|
159
|
-
function buildFilesSlice(allFiles) {
|
|
160
|
-
return {
|
|
161
|
-
generated: now(),
|
|
162
|
-
git_hash: headHash(),
|
|
163
|
-
files: allFiles.map(rel => {
|
|
164
|
-
const abs = path.join(ROOT, rel);
|
|
165
|
-
return {
|
|
166
|
-
path: rel,
|
|
167
|
-
type: classifyFile(rel),
|
|
168
|
-
mtime: getMtime(abs),
|
|
169
|
-
size_bytes: getSize(abs),
|
|
170
|
-
git_hash: gitHash(rel),
|
|
171
|
-
};
|
|
172
|
-
}),
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function buildExportsSlice(allFiles) {
|
|
177
|
-
const exports = [];
|
|
178
|
-
for (const rel of allFiles) {
|
|
179
|
-
if (!rel.endsWith('.md')) continue;
|
|
180
|
-
const abs = path.join(ROOT, rel);
|
|
181
|
-
let content;
|
|
182
|
-
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
183
|
-
const fm = parseFrontmatter(content);
|
|
184
|
-
if (!fm.name) continue;
|
|
185
|
-
|
|
186
|
-
const kind = classifyFile(rel);
|
|
187
|
-
const entry = { file: rel, kind, name: fm.name };
|
|
188
|
-
// Skills have a command derived from name (gdd-foo → /gdd:foo)
|
|
189
|
-
if (kind === 'skill' && fm.name.startsWith('gdd-')) {
|
|
190
|
-
entry.command = '/gdd:' + fm.name.replace(/^gdd-/, '');
|
|
191
|
-
}
|
|
192
|
-
exports.push(entry);
|
|
193
|
-
}
|
|
194
|
-
return { generated: now(), exports };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function buildSymbolsSlice(allFiles) {
|
|
198
|
-
const symbols = [];
|
|
199
|
-
const HEADING_RE = /^(#{1,4})\s+(.+)$/gm;
|
|
200
|
-
for (const rel of allFiles) {
|
|
201
|
-
if (!rel.endsWith('.md')) continue;
|
|
202
|
-
const abs = path.join(ROOT, rel);
|
|
203
|
-
let content;
|
|
204
|
-
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
205
|
-
const lines = content.split('\n');
|
|
206
|
-
lines.forEach((line, i) => {
|
|
207
|
-
const m = line.match(/^(#{1,4})\s+(.+)$/);
|
|
208
|
-
if (!m) return;
|
|
209
|
-
const heading = m[0];
|
|
210
|
-
const level = m[1].length;
|
|
211
|
-
const text = m[2].trim();
|
|
212
|
-
const anchor = text.toLowerCase().replace(/[^\w\s-]/g, '').replace(/\s+/g, '-');
|
|
213
|
-
symbols.push({ file: rel, heading, level, anchor, line: i + 1 });
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
return { generated: now(), symbols };
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function buildTokensSlice(allFiles) {
|
|
220
|
-
const tokens = [];
|
|
221
|
-
const TOKEN_RE = /--[\w-]+(color|space|spacing|font|radius|shadow|motion|size|weight|line)[^\s,;)']*/gi;
|
|
222
|
-
const CAT_MAP = {
|
|
223
|
-
color: 'color', space: 'spacing', spacing: 'spacing',
|
|
224
|
-
font: 'typography', radius: 'radius', shadow: 'shadow', motion: 'motion',
|
|
225
|
-
};
|
|
226
|
-
for (const rel of allFiles) {
|
|
227
|
-
if (!rel.endsWith('.md') && !rel.endsWith('.cjs') && !rel.endsWith('.js')) continue;
|
|
228
|
-
const abs = path.join(ROOT, rel);
|
|
229
|
-
let content;
|
|
230
|
-
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
231
|
-
const lines = content.split('\n');
|
|
232
|
-
lines.forEach((line, i) => {
|
|
233
|
-
let m;
|
|
234
|
-
const re = /--[\w-]+/g;
|
|
235
|
-
while ((m = re.exec(line)) !== null) {
|
|
236
|
-
const token = m[0];
|
|
237
|
-
// Categorise by suffix keywords
|
|
238
|
-
let category = 'other';
|
|
239
|
-
for (const [key, cat] of Object.entries(CAT_MAP)) {
|
|
240
|
-
if (token.includes(key)) { category = cat; break; }
|
|
241
|
-
}
|
|
242
|
-
if (category === 'other' && !token.includes('-')) continue;
|
|
243
|
-
tokens.push({ file: rel, token, category, line: i + 1, context: line.trim().slice(0, 80) });
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
return { generated: now(), tokens };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function buildComponentsSlice(allFiles) {
|
|
251
|
-
const components = [];
|
|
252
|
-
// PascalCase component names in markdown
|
|
253
|
-
const COMP_RE = /\b([A-Z][a-zA-Z0-9]{2,})\b/g;
|
|
254
|
-
const SKIP = new Set(['WCAG', 'CSS', 'JSON', 'HTML', 'URL', 'API', 'MCP', 'GDD', 'UI', 'UX', 'ISO']);
|
|
255
|
-
for (const rel of allFiles) {
|
|
256
|
-
if (!rel.endsWith('.md')) continue;
|
|
257
|
-
const abs = path.join(ROOT, rel);
|
|
258
|
-
let content;
|
|
259
|
-
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
260
|
-
const lines = content.split('\n');
|
|
261
|
-
lines.forEach((line, i) => {
|
|
262
|
-
// Skip code blocks
|
|
263
|
-
if (line.trim().startsWith('```') || line.trim().startsWith('`')) return;
|
|
264
|
-
let m;
|
|
265
|
-
while ((m = COMP_RE.exec(line)) !== null) {
|
|
266
|
-
const name = m[1];
|
|
267
|
-
if (SKIP.has(name)) continue;
|
|
268
|
-
const role = line.includes('define') || line.includes('export') ? 'definition' : 'reference';
|
|
269
|
-
components.push({ file: rel, component: name, role, line: i + 1 });
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
// Deduplicate by file+component+line
|
|
274
|
-
const seen = new Set();
|
|
275
|
-
return {
|
|
276
|
-
generated: now(),
|
|
277
|
-
components: components.filter(c => {
|
|
278
|
-
const key = `${c.file}:${c.component}:${c.line}`;
|
|
279
|
-
if (seen.has(key)) return false;
|
|
280
|
-
seen.add(key);
|
|
281
|
-
return true;
|
|
282
|
-
}),
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function buildPatternsSlice(allFiles) {
|
|
287
|
-
const patterns = [];
|
|
288
|
-
const PATTERN_CATS = ['color-system', 'spacing-system', 'typography-system', 'component-styling', 'layout', 'interaction'];
|
|
289
|
-
for (const rel of allFiles) {
|
|
290
|
-
if (!rel.endsWith('.md')) continue;
|
|
291
|
-
const abs = path.join(ROOT, rel);
|
|
292
|
-
let content;
|
|
293
|
-
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
294
|
-
for (const cat of PATTERN_CATS) {
|
|
295
|
-
if (content.includes(cat)) {
|
|
296
|
-
// Find the line
|
|
297
|
-
const lines = content.split('\n');
|
|
298
|
-
lines.forEach((line, i) => {
|
|
299
|
-
if (line.includes(cat)) {
|
|
300
|
-
const name = cat;
|
|
301
|
-
patterns.push({
|
|
302
|
-
name,
|
|
303
|
-
category: cat,
|
|
304
|
-
source_file: rel,
|
|
305
|
-
description: line.trim().slice(0, 100),
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
// Deduplicate
|
|
313
|
-
const seen = new Set();
|
|
314
|
-
return {
|
|
315
|
-
generated: now(),
|
|
316
|
-
patterns: patterns.filter(p => {
|
|
317
|
-
const key = `${p.source_file}:${p.category}:${p.description}`;
|
|
318
|
-
if (seen.has(key)) return false;
|
|
319
|
-
seen.add(key);
|
|
320
|
-
return true;
|
|
321
|
-
}),
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function buildDependenciesSlice(allFiles) {
|
|
326
|
-
const dependencies = [];
|
|
327
|
-
// @-references in markdown
|
|
328
|
-
const AT_REF_RE = /@([\w./\-]+\.md)/g;
|
|
329
|
-
// reads-from in agent frontmatter (reads: field)
|
|
330
|
-
for (const rel of allFiles) {
|
|
331
|
-
if (!rel.endsWith('.md')) continue;
|
|
332
|
-
const abs = path.join(ROOT, rel);
|
|
333
|
-
let content;
|
|
334
|
-
try { content = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
335
|
-
const lines = content.split('\n');
|
|
336
|
-
lines.forEach((line, i) => {
|
|
337
|
-
let m;
|
|
338
|
-
while ((m = AT_REF_RE.exec(line)) !== null) {
|
|
339
|
-
const target = m[1];
|
|
340
|
-
dependencies.push({ from: rel, to: target, kind: 'at-reference', line: i + 1 });
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
// Check frontmatter reads field
|
|
344
|
-
const fm = parseFrontmatter(content);
|
|
345
|
-
if (fm.reads) {
|
|
346
|
-
// reads: comma or space separated list
|
|
347
|
-
const targets = fm.reads.split(/[\s,]+/).filter(Boolean);
|
|
348
|
-
for (const t of targets) {
|
|
349
|
-
if (t.endsWith('.md') || t.endsWith('.json')) {
|
|
350
|
-
dependencies.push({ from: rel, to: t, kind: 'reads-from', line: 1 });
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return { generated: now(), dependencies };
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function buildDecisionsSlice() {
|
|
359
|
-
// Decisions come from .design/DESIGN-CONTEXT.md (runtime, may not exist at build time)
|
|
360
|
-
const decisions = [];
|
|
361
|
-
const CONTEXT_FILE = path.join(ROOT, '.design', 'DESIGN-CONTEXT.md');
|
|
362
|
-
if (!fs.existsSync(CONTEXT_FILE)) {
|
|
363
|
-
return { generated: now(), decisions };
|
|
364
|
-
}
|
|
365
|
-
const content = fs.readFileSync(CONTEXT_FILE, 'utf8');
|
|
366
|
-
const lines = content.split('\n');
|
|
367
|
-
const DECISION_RE = /\b(D-\d+)[:\s]+(.+)/;
|
|
368
|
-
lines.forEach((line, i) => {
|
|
369
|
-
const m = line.match(DECISION_RE);
|
|
370
|
-
if (m) {
|
|
371
|
-
decisions.push({
|
|
372
|
-
id: m[1],
|
|
373
|
-
summary: m[2].trim().slice(0, 120),
|
|
374
|
-
source_file: '.design/DESIGN-CONTEXT.md',
|
|
375
|
-
line: i + 1,
|
|
376
|
-
date: now().slice(0, 10),
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
return { generated: now(), decisions };
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function buildDebtSlice() {
|
|
384
|
-
const debt = [];
|
|
385
|
-
const DEBT_FILE = path.join(ROOT, '.design', 'DESIGN-DEBT.md');
|
|
386
|
-
if (!fs.existsSync(DEBT_FILE)) {
|
|
387
|
-
return { generated: now(), debt };
|
|
388
|
-
}
|
|
389
|
-
const content = fs.readFileSync(DEBT_FILE, 'utf8');
|
|
390
|
-
const lines = content.split('\n');
|
|
391
|
-
const ITEM_RE = /[-*]\s+(?:\*\*(DEBT-\d+|high|medium|low)\*\*[:\s]+)?(.+)/i;
|
|
392
|
-
const SEV_RE = /high|medium|low/i;
|
|
393
|
-
let debtId = 0;
|
|
394
|
-
lines.forEach((line, i) => {
|
|
395
|
-
const m = line.match(ITEM_RE);
|
|
396
|
-
if (!m) return;
|
|
397
|
-
const sevMatch = line.match(SEV_RE);
|
|
398
|
-
const severity = sevMatch ? sevMatch[0].toLowerCase() : 'medium';
|
|
399
|
-
debtId++;
|
|
400
|
-
const id = line.match(/DEBT-\d+/) ? line.match(/DEBT-\d+/)[0] : `DEBT-${String(debtId).padStart(2, '0')}`;
|
|
401
|
-
debt.push({
|
|
402
|
-
id,
|
|
403
|
-
summary: m[2].trim().slice(0, 120),
|
|
404
|
-
severity,
|
|
405
|
-
source_file: '.design/DESIGN-DEBT.md',
|
|
406
|
-
line: i + 1,
|
|
407
|
-
});
|
|
408
|
-
});
|
|
409
|
-
return { generated: now(), debt };
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function buildGraphSlice(filesSlice, depsSlice) {
|
|
413
|
-
const nodes = (filesSlice.files || []).map(f => ({
|
|
414
|
-
id: f.path,
|
|
415
|
-
type: f.type,
|
|
416
|
-
name: path.basename(f.path, path.extname(f.path)),
|
|
417
|
-
}));
|
|
418
|
-
const edges = (depsSlice.dependencies || []).map(d => ({
|
|
419
|
-
from: d.from,
|
|
420
|
-
to: d.to,
|
|
421
|
-
kind: d.kind,
|
|
422
|
-
}));
|
|
423
|
-
return { generated: now(), nodes, edges };
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
427
|
-
|
|
428
|
-
async function main() {
|
|
429
|
-
console.log('build-intel: starting' + (FORCE ? ' (--force)' : ''));
|
|
430
|
-
ensureDir(INTEL_DIR);
|
|
431
|
-
|
|
432
|
-
const existingFiles = readJson(path.join(INTEL_DIR, 'files.json'));
|
|
433
|
-
const allFiles = discoverFiles();
|
|
434
|
-
const changed = changedFiles(allFiles, existingFiles);
|
|
435
|
-
|
|
436
|
-
console.log(` discovered ${allFiles.length} files, ${changed.length} changed`);
|
|
437
|
-
|
|
438
|
-
// Phase 14.5: validate reference registry round-trip whenever any reference/*
|
|
439
|
-
// changed (or on --force). Fail the build on dangling/missing/duplicate.
|
|
440
|
-
const referenceChanged = FORCE || changed.some(f => f.startsWith('reference/'));
|
|
441
|
-
if (referenceChanged) {
|
|
442
|
-
try {
|
|
443
|
-
const { validateRegistry } = require(path.join(__dirname, 'lib', 'reference-registry.cjs'));
|
|
444
|
-
const v = validateRegistry({ cwd: ROOT });
|
|
445
|
-
if (!v.ok) {
|
|
446
|
-
console.error('build-intel: reference registry validation failed:');
|
|
447
|
-
if (v.missingInRegistry.length) console.error(' missing in registry:', v.missingInRegistry);
|
|
448
|
-
if (v.danglingInRegistry.length) console.error(' dangling entries:', v.danglingInRegistry);
|
|
449
|
-
if (v.duplicates.length) console.error(' duplicate entries:', v.duplicates);
|
|
450
|
-
process.exit(1);
|
|
451
|
-
}
|
|
452
|
-
console.log(' reference registry: ok');
|
|
453
|
-
} catch (err) {
|
|
454
|
-
if (err && err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Always rebuild files slice when any file changed
|
|
459
|
-
const filesSlice = buildFilesSlice(allFiles);
|
|
460
|
-
writeSlice('files.json', filesSlice);
|
|
461
|
-
|
|
462
|
-
// Rebuild content slices only for changed files (or all if force)
|
|
463
|
-
const targetFiles = FORCE ? allFiles : changed;
|
|
464
|
-
|
|
465
|
-
// For simplicity, rebuild all content slices when any file changed.
|
|
466
|
-
// A production version would merge existing slices with updated entries.
|
|
467
|
-
if (targetFiles.length > 0 || FORCE) {
|
|
468
|
-
writeSlice('exports.json', buildExportsSlice(allFiles));
|
|
469
|
-
writeSlice('symbols.json', buildSymbolsSlice(allFiles));
|
|
470
|
-
writeSlice('tokens.json', buildTokensSlice(allFiles));
|
|
471
|
-
writeSlice('components.json', buildComponentsSlice(allFiles));
|
|
472
|
-
writeSlice('patterns.json', buildPatternsSlice(allFiles));
|
|
473
|
-
const depsSlice = buildDependenciesSlice(allFiles);
|
|
474
|
-
writeSlice('dependencies.json', depsSlice);
|
|
475
|
-
writeSlice('decisions.json', buildDecisionsSlice());
|
|
476
|
-
writeSlice('debt.json', buildDebtSlice());
|
|
477
|
-
writeSlice('graph.json', buildGraphSlice(filesSlice, depsSlice));
|
|
478
|
-
} else {
|
|
479
|
-
console.log(' no changes detected — skipping content slices');
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
console.log('build-intel: done');
|
|
483
|
-
console.log(` output: ${INTEL_DIR}`);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
main().catch(err => { console.error(err); process.exit(1); });
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* codegen-schema-types.ts — Generate TypeScript interface declarations from
|
|
4
|
-
* every Draft-07 JSON Schema under `reference/schemas/*.schema.json`.
|
|
5
|
-
*
|
|
6
|
-
* Output: `reference/schemas/generated.d.ts` — single file containing one
|
|
7
|
-
* `export interface XSchema` per schema, named from the filename stem.
|
|
8
|
-
*
|
|
9
|
-
* Invoked: `npm run codegen:schemas` (requires repo-root cwd, which npm sets
|
|
10
|
-
* automatically). If invoked directly, `--repo-root <path>` can override.
|
|
11
|
-
*
|
|
12
|
-
* Exit codes:
|
|
13
|
-
* 0 — success
|
|
14
|
-
* 1 — any read/parse/compile failure
|
|
15
|
-
*/
|
|
16
|
-
import { readdirSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
17
|
-
import { resolve, join, dirname, basename } from 'node:path';
|
|
18
|
-
import { compile } from 'json-schema-to-typescript';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Resolve the repo root. Priority:
|
|
22
|
-
* 1. `--repo-root <path>` CLI arg.
|
|
23
|
-
* 2. `process.cwd()` — npm scripts run from the package root, so this is
|
|
24
|
-
* the common case.
|
|
25
|
-
*
|
|
26
|
-
* We deliberately avoid `import.meta.url` / `__dirname` so this module stays
|
|
27
|
-
* valid under both CommonJS type-checking (Node16 + no package "type") and
|
|
28
|
-
* the Node 22+ `--experimental-strip-types` runtime, which auto-detects ESM.
|
|
29
|
-
*/
|
|
30
|
-
function resolveRepoRoot(): string {
|
|
31
|
-
const argv = process.argv.slice(2);
|
|
32
|
-
const idx = argv.indexOf('--repo-root');
|
|
33
|
-
if (idx !== -1 && idx + 1 < argv.length) {
|
|
34
|
-
const v = argv[idx + 1];
|
|
35
|
-
if (typeof v === 'string' && v.length > 0) return resolve(v);
|
|
36
|
-
}
|
|
37
|
-
return resolve(process.cwd());
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const REPO_ROOT = resolveRepoRoot();
|
|
41
|
-
const SCHEMA_DIR = join(REPO_ROOT, 'reference', 'schemas');
|
|
42
|
-
const OUTPUT_PATH = join(SCHEMA_DIR, 'generated.d.ts');
|
|
43
|
-
|
|
44
|
-
const HEADER =
|
|
45
|
-
'// AUTO-GENERATED from reference/schemas/*.schema.json — DO NOT EDIT.\n' +
|
|
46
|
-
'// Regenerate: npm run codegen:schemas\n' +
|
|
47
|
-
'/* eslint-disable */\n';
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Map a schema filename stem (e.g. "authority-snapshot" from
|
|
51
|
-
* "authority-snapshot.schema.json") to the canonical interface name per
|
|
52
|
-
* Plan 20-00: PascalCase + `Schema` suffix. Hyphens split word boundaries.
|
|
53
|
-
*/
|
|
54
|
-
function stemToInterfaceName(stem: string): string {
|
|
55
|
-
const pascal = stem
|
|
56
|
-
.split(/[-_.]/)
|
|
57
|
-
.filter(Boolean)
|
|
58
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
59
|
-
.join('');
|
|
60
|
-
return `${pascal}Schema`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function main(): Promise<void> {
|
|
64
|
-
const entries = readdirSync(SCHEMA_DIR)
|
|
65
|
-
.filter((f) => f.endsWith('.schema.json'))
|
|
66
|
-
.sort();
|
|
67
|
-
|
|
68
|
-
if (entries.length === 0) {
|
|
69
|
-
console.error(`codegen-schema-types: no *.schema.json files found in ${SCHEMA_DIR}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const chunks: string[] = [HEADER];
|
|
74
|
-
|
|
75
|
-
for (const file of entries) {
|
|
76
|
-
const stem = basename(file, '.schema.json');
|
|
77
|
-
const interfaceName = stemToInterfaceName(stem);
|
|
78
|
-
const schemaPath = join(SCHEMA_DIR, file);
|
|
79
|
-
|
|
80
|
-
let schema: unknown;
|
|
81
|
-
try {
|
|
82
|
-
schema = JSON.parse(readFileSync(schemaPath, 'utf8'));
|
|
83
|
-
} catch (err) {
|
|
84
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
85
|
-
console.error(`codegen-schema-types: failed to parse ${file}: ${msg}`);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// compile() expects a JSONSchema; we pass our parsed object.
|
|
91
|
-
// bannerComment: '' — we add our own header once at the top.
|
|
92
|
-
const ts = await compile(schema as Parameters<typeof compile>[0], interfaceName, {
|
|
93
|
-
bannerComment: '',
|
|
94
|
-
additionalProperties: false,
|
|
95
|
-
style: { singleQuote: true, trailingComma: 'all' },
|
|
96
|
-
unreachableDefinitions: false,
|
|
97
|
-
});
|
|
98
|
-
chunks.push(`// ---- ${file} ----\n`);
|
|
99
|
-
// json-schema-to-typescript emits the top-level as the requested name,
|
|
100
|
-
// but when the schema's own `title` differs it may prefix. We rename
|
|
101
|
-
// the top-level export to our canonical name for stability.
|
|
102
|
-
const renamed = ensureExportInterface(ts, interfaceName);
|
|
103
|
-
chunks.push(renamed);
|
|
104
|
-
chunks.push('\n');
|
|
105
|
-
} catch (err) {
|
|
106
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
107
|
-
console.error(`codegen-schema-types: failed to compile ${file}: ${msg}`);
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
mkdirSync(dirname(OUTPUT_PATH), { recursive: true });
|
|
113
|
-
writeFileSync(OUTPUT_PATH, chunks.join(''), 'utf8');
|
|
114
|
-
console.log(
|
|
115
|
-
`codegen-schema-types: wrote ${OUTPUT_PATH} (${entries.length} schema(s))`,
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Ensure that the compiled TS output exports an interface/type alias with the
|
|
121
|
-
* exact canonical name. `json-schema-to-typescript` normally emits this
|
|
122
|
-
* already, but some schemas whose `title` field contains non-identifier
|
|
123
|
-
* characters (e.g. ".design/config.json") get their interface named from a
|
|
124
|
-
* cleaned title rather than our requested name. We add an `export` alias at
|
|
125
|
-
* the end so every generated chunk guarantees `export interface XSchema` (or
|
|
126
|
-
* `export type XSchema = ...`) is available.
|
|
127
|
-
*/
|
|
128
|
-
function ensureExportInterface(ts: string, canonical: string): string {
|
|
129
|
-
const hasCanonical = new RegExp(
|
|
130
|
-
`export\\s+(interface|type)\\s+${canonical}\\b`,
|
|
131
|
-
).test(ts);
|
|
132
|
-
if (hasCanonical) return ts;
|
|
133
|
-
|
|
134
|
-
const firstExport = ts.match(
|
|
135
|
-
/export\s+(interface|type)\s+([A-Za-z_][A-Za-z0-9_]*)/,
|
|
136
|
-
);
|
|
137
|
-
if (!firstExport) {
|
|
138
|
-
return ts + `\nexport type ${canonical} = unknown;\n`;
|
|
139
|
-
}
|
|
140
|
-
const firstName = firstExport[2];
|
|
141
|
-
if (firstName === canonical) return ts;
|
|
142
|
-
return ts + `\nexport type ${canonical} = ${firstName};\n`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
main().catch((err) => {
|
|
146
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
147
|
-
console.error(`codegen-schema-types: ${msg}`);
|
|
148
|
-
process.exit(1);
|
|
149
|
-
});
|