design-brain-memory 0.9.2 → 0.9.4
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/README.md +273 -0
- package/dist/agentBrowser.js +1 -1
- package/dist/agentBrowser.js.map +1 -1
- package/dist/aggregate.d.ts +9 -0
- package/dist/aggregate.js +53 -0
- package/dist/aggregate.js.map +1 -0
- package/dist/batch.d.ts +16 -0
- package/dist/batch.js +44 -0
- package/dist/batch.js.map +1 -0
- package/dist/cli.js +250 -216
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +60 -1
- package/dist/commands.js +323 -10
- package/dist/commands.js.map +1 -1
- package/dist/compare.d.ts +33 -0
- package/dist/compare.js +83 -0
- package/dist/compare.js.map +1 -0
- package/dist/componentGraph.d.ts +22 -0
- package/dist/componentGraph.js +106 -0
- package/dist/componentGraph.js.map +1 -0
- package/dist/contextLayer.d.ts +12 -0
- package/dist/contextLayer.js +263 -0
- package/dist/contextLayer.js.map +1 -0
- package/dist/cssInJs.d.ts +9 -0
- package/dist/cssInJs.js +124 -0
- package/dist/cssInJs.js.map +1 -0
- package/dist/extractFromUrl.d.ts +8 -0
- package/dist/extractFromUrl.js +104 -355
- package/dist/extractFromUrl.js.map +1 -1
- package/dist/graphView.d.ts +21 -0
- package/dist/graphView.js +492 -0
- package/dist/graphView.js.map +1 -0
- package/dist/index.d.ts +26 -11
- package/dist/index.js +17 -10
- package/dist/index.js.map +1 -1
- package/dist/knowledge.d.ts +20 -0
- package/dist/knowledge.js +208 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/liveView.d.ts +15 -0
- package/dist/liveView.js +475 -0
- package/dist/liveView.js.map +1 -0
- package/dist/llm.js +1 -9
- package/dist/llm.js.map +1 -1
- package/dist/moodboard.d.ts +3 -0
- package/dist/moodboard.js +152 -0
- package/dist/moodboard.js.map +1 -0
- package/dist/persona.d.ts +2 -2
- package/dist/persona.js +82 -210
- package/dist/persona.js.map +1 -1
- package/dist/query.js +1 -6
- package/dist/query.js.map +1 -1
- package/dist/render.d.ts +2 -10
- package/dist/render.js +80 -175
- package/dist/render.js.map +1 -1
- package/dist/reviewChecklist.d.ts +17 -0
- package/dist/reviewChecklist.js +126 -0
- package/dist/reviewChecklist.js.map +1 -0
- package/dist/scan.d.ts +19 -7
- package/dist/scan.js +132 -374
- package/dist/scan.js.map +1 -1
- package/dist/scorecard.d.ts +53 -0
- package/dist/scorecard.js +325 -0
- package/dist/scorecard.js.map +1 -0
- package/dist/skillPrompt.d.ts +1 -3
- package/dist/skillPrompt.js +22 -148
- package/dist/skillPrompt.js.map +1 -1
- package/dist/store.d.ts +2 -2
- package/dist/store.js +7 -9
- package/dist/store.js.map +1 -1
- package/dist/styleDictionary.d.ts +16 -0
- package/dist/styleDictionary.js +89 -0
- package/dist/styleDictionary.js.map +1 -0
- package/dist/svg.d.ts +5 -0
- package/dist/svg.js +162 -0
- package/dist/svg.js.map +1 -0
- package/dist/systemDiff.d.ts +28 -0
- package/dist/systemDiff.js +107 -0
- package/dist/systemDiff.js.map +1 -0
- package/dist/tailwind.d.ts +2 -0
- package/dist/tailwind.js +122 -0
- package/dist/tailwind.js.map +1 -0
- package/dist/taste.d.ts +3 -2
- package/dist/taste.js +349 -536
- package/dist/taste.js.map +1 -1
- package/dist/tasteRenderer.d.ts +2 -3
- package/dist/tasteRenderer.js +123 -119
- package/dist/tasteRenderer.js.map +1 -1
- package/dist/tokenNaming.d.ts +5 -0
- package/dist/tokenNaming.js +229 -0
- package/dist/tokenNaming.js.map +1 -0
- package/dist/tokens.d.ts +17 -0
- package/dist/tokens.js +44 -0
- package/dist/tokens.js.map +1 -0
- package/dist/trends.d.ts +12 -0
- package/dist/trends.js +178 -0
- package/dist/trends.js.map +1 -0
- package/dist/types.d.ts +47 -101
- package/dist/wiki.d.ts +10 -0
- package/dist/wiki.js +346 -0
- package/dist/wiki.js.map +1 -0
- package/dist/writingStyle.d.ts +38 -0
- package/dist/writingStyle.js +224 -0
- package/dist/writingStyle.js.map +1 -0
- package/package.json +5 -4
- package/dist/classify.d.ts +0 -21
- package/dist/classify.js +0 -205
- package/dist/classify.js.map +0 -1
- package/dist/scanRenderer.d.ts +0 -2
- package/dist/scanRenderer.js +0 -155
- package/dist/scanRenderer.js.map +0 -1
- package/dist/tasteDiff.d.ts +0 -19
- package/dist/tasteDiff.js +0 -340
- package/dist/tasteDiff.js.map +0 -1
- package/dist/tasteGenerate.d.ts +0 -12
- package/dist/tasteGenerate.js +0 -140
- package/dist/tasteGenerate.js.map +0 -1
- package/dist/tasteRefine.d.ts +0 -13
- package/dist/tasteRefine.js +0 -351
- package/dist/tasteRefine.js.map +0 -1
- package/dist/theatrical.d.ts +0 -5
- package/dist/theatrical.js +0 -258
- package/dist/theatrical.js.map +0 -1
- package/skills/SKILL.md +0 -36
- package/skills/design-brain/SKILL.md +0 -77
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
3
5
|
import { Command } from 'commander';
|
|
4
|
-
import {
|
|
5
|
-
import { askBrain, initBrain, ingestInspiration, recordOutcome, reindexBrain, searchBrain, } from './commands.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { cherryPickComponent, scoreTaste } from './tasteDiff.js';
|
|
9
|
-
import { generateFromTaste } from './tasteGenerate.js';
|
|
10
|
-
import { renderTasteDiff, renderTasteProfile } from './tasteRenderer.js';
|
|
11
|
-
import { confirmPrompt, shouldSkipPrompts } from './interactive.js';
|
|
6
|
+
import { batchCapture } from './batch.js';
|
|
7
|
+
import { askBrain, compareCaptures, detectAndWriteTrends, exportDesignSystem, generateComponentGraphCmd, generateMoodboard, generateReviewChecklist, initBrain, ingestInspiration, nameTokensCmd, recordOutcome, reindexBrain, runSystemDiff, searchBrain, analyzeWritingStyleCmd, buildGraphView, buildWiki, generateContext, } from './commands.js';
|
|
8
|
+
import { runScorecard } from './scorecard.js';
|
|
9
|
+
import { shouldSkipPrompts } from './interactive.js';
|
|
12
10
|
import { resolveLlmConfig } from './llm.js';
|
|
13
|
-
import { maybePromptSkillInstall } from './skillPrompt.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import { getDefaultSkillRepo, installSkill, maybePromptSkillInstall } from './skillPrompt.js';
|
|
12
|
+
import { buildTasteProfile } from './taste.js';
|
|
13
|
+
import { renderTasteProfile } from './tasteRenderer.js';
|
|
16
14
|
function toStringList(value) {
|
|
17
15
|
if (!value) {
|
|
18
16
|
return [];
|
|
@@ -46,42 +44,44 @@ function parseViewports(values) {
|
|
|
46
44
|
});
|
|
47
45
|
return parsed.length > 0 ? parsed : undefined;
|
|
48
46
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
return options[index];
|
|
55
|
-
}
|
|
47
|
+
function getVersion() {
|
|
48
|
+
try {
|
|
49
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
50
|
+
const pkg = JSON.parse(readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
51
|
+
return pkg.version ?? '0.0.0';
|
|
56
52
|
}
|
|
57
|
-
|
|
53
|
+
catch {
|
|
54
|
+
return '0.0.0';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function printBanner(version) {
|
|
58
|
+
const DIM = '\x1b[2m';
|
|
59
|
+
const BOLD = '\x1b[1m';
|
|
60
|
+
const CYAN = '\x1b[36m';
|
|
61
|
+
const RESET = '\x1b[0m';
|
|
62
|
+
const banner = [
|
|
63
|
+
'',
|
|
64
|
+
`${BOLD}${CYAN} ┌─────────────────────────────────────────┐${RESET}`,
|
|
65
|
+
`${BOLD}${CYAN} │${RESET} ${BOLD}${CYAN}│${RESET}`,
|
|
66
|
+
`${BOLD}${CYAN} │${RESET} ${BOLD}design-brain${RESET} ${DIM}v${version}${RESET} ${BOLD}${CYAN}│${RESET}`,
|
|
67
|
+
`${BOLD}${CYAN} │${RESET} ${DIM}Relational design memory${RESET} ${BOLD}${CYAN}│${RESET}`,
|
|
68
|
+
`${BOLD}${CYAN} │${RESET} ${BOLD}${CYAN}│${RESET}`,
|
|
69
|
+
`${BOLD}${CYAN} └─────────────────────────────────────────┘${RESET}`,
|
|
70
|
+
'',
|
|
71
|
+
];
|
|
72
|
+
console.log(banner.join('\n'));
|
|
58
73
|
}
|
|
59
74
|
async function main() {
|
|
75
|
+
const version = getVersion();
|
|
60
76
|
const program = new Command();
|
|
61
77
|
program
|
|
62
78
|
.name('design-brain-memory')
|
|
63
79
|
.description('Relational markdown design memory powered by Agent Browser CLI')
|
|
64
|
-
.version(
|
|
65
|
-
.option('-y, --yes', 'Skip interactive prompts')
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.command('scan [path]')
|
|
69
|
-
.description('Scan codebase for design tokens and compute health score')
|
|
70
|
-
.action(async (scanPath) => {
|
|
71
|
-
const input = scanPath ?? process.cwd();
|
|
72
|
-
let result;
|
|
73
|
-
if (looksLikeUrl(input)) {
|
|
74
|
-
const url = normalizeToUrl(input);
|
|
75
|
-
result = await scanFromUrl(url);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
result = await scanDesignSystem(path.resolve(input));
|
|
79
|
-
}
|
|
80
|
-
renderScanResults(result);
|
|
81
|
-
const globalOptions = program.opts();
|
|
82
|
-
await maybePromptSkillInstall(shouldSkipPrompts(Boolean(globalOptions.yes)));
|
|
80
|
+
.version(version)
|
|
81
|
+
.option('-y, --yes', 'Skip interactive prompts')
|
|
82
|
+
.hook('preAction', () => {
|
|
83
|
+
printBanner(version);
|
|
83
84
|
});
|
|
84
|
-
/* ─── init ─── */
|
|
85
85
|
program
|
|
86
86
|
.command('init')
|
|
87
87
|
.description('Initialize .design-brain in the given workspace')
|
|
@@ -90,7 +90,6 @@ async function main() {
|
|
|
90
90
|
await initBrain(path.resolve(options.root));
|
|
91
91
|
console.log(`Initialized design brain at ${path.resolve(options.root, '.design-brain')}`);
|
|
92
92
|
});
|
|
93
|
-
/* ─── ingest ─── */
|
|
94
93
|
program
|
|
95
94
|
.command('ingest')
|
|
96
95
|
.description('Ingest a URL or screenshot into a project design wiki')
|
|
@@ -110,6 +109,8 @@ async function main() {
|
|
|
110
109
|
.option('--llm-model <model>', 'LLM model id')
|
|
111
110
|
.option('--llm-timeout-ms <ms>', 'LLM timeout in milliseconds', '30000')
|
|
112
111
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
112
|
+
.option('--no-visuals', 'Skip SVG visual generation')
|
|
113
|
+
.option('--live', 'Show live extraction visualization in terminal')
|
|
113
114
|
.action(async (options) => {
|
|
114
115
|
const llm = resolveLlmConfig({
|
|
115
116
|
baseUrl: options.llmBaseUrl,
|
|
@@ -131,12 +132,13 @@ async function main() {
|
|
|
131
132
|
llm,
|
|
132
133
|
journeySteps: parseInteger(options.journeySteps, 3),
|
|
133
134
|
responsiveViewports: parseViewports(options.viewport),
|
|
135
|
+
skipVisuals: !options.visuals,
|
|
136
|
+
live: options.live ?? false,
|
|
134
137
|
});
|
|
135
138
|
console.log(`Captured inspiration ${result.inspirationId} in project ${result.projectId}`);
|
|
136
139
|
const globalOptions = program.opts();
|
|
137
140
|
await maybePromptSkillInstall(shouldSkipPrompts(Boolean(globalOptions.yes)));
|
|
138
141
|
});
|
|
139
|
-
/* ─── outcome ─── */
|
|
140
142
|
program
|
|
141
143
|
.command('outcome')
|
|
142
144
|
.description('Record what the team built and link it to inspirations')
|
|
@@ -159,7 +161,6 @@ async function main() {
|
|
|
159
161
|
});
|
|
160
162
|
console.log(`Recorded outcome ${result.outcomeId} in project ${result.projectId}`);
|
|
161
163
|
});
|
|
162
|
-
/* ─── search ─── */
|
|
163
164
|
program
|
|
164
165
|
.command('search')
|
|
165
166
|
.description('Keyword search over relational design memory')
|
|
@@ -188,7 +189,6 @@ async function main() {
|
|
|
188
189
|
console.log(` ${result.snippet}`);
|
|
189
190
|
}
|
|
190
191
|
});
|
|
191
|
-
/* ─── ask ─── */
|
|
192
192
|
program
|
|
193
193
|
.command('ask')
|
|
194
194
|
.description('Ask a design question against the design brain')
|
|
@@ -222,212 +222,246 @@ async function main() {
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
.
|
|
228
|
-
.
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
.
|
|
238
|
-
.
|
|
225
|
+
program
|
|
226
|
+
.command('install-skill')
|
|
227
|
+
.description('Install Design Brain skill into your local skills registry')
|
|
228
|
+
.option('--repo <repo>', 'Skill repository override', getDefaultSkillRepo())
|
|
229
|
+
.action(async (options) => {
|
|
230
|
+
const installed = installSkill(options.repo);
|
|
231
|
+
if (!installed) {
|
|
232
|
+
throw new Error(`Skill install failed. Try manually: npx -y skills add ${options.repo}`);
|
|
233
|
+
}
|
|
234
|
+
console.log(`Installed skill from ${options.repo}`);
|
|
235
|
+
});
|
|
236
|
+
program
|
|
237
|
+
.command('reindex')
|
|
238
|
+
.description('Regenerate markdown wiki and relation graph from database.json')
|
|
239
239
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
240
|
-
.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
.option('--no-visuals', 'Skip SVG visual generation')
|
|
241
|
+
.action(async (options) => {
|
|
242
|
+
await reindexBrain(path.resolve(options.root), !options.visuals);
|
|
243
|
+
console.log(`Reindexed ${path.resolve(options.root, '.design-brain')}`);
|
|
244
|
+
});
|
|
245
|
+
program
|
|
246
|
+
.command('export')
|
|
247
|
+
.description('Export design system from captured data')
|
|
248
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
249
|
+
.option('--format <format>', 'Export format (tailwind, style-dictionary, css-in-js)', 'tailwind')
|
|
250
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
251
|
+
.action(async (options) => {
|
|
252
|
+
const outPath = await exportDesignSystem({
|
|
253
|
+
rootDir: path.resolve(options.root),
|
|
254
|
+
project: options.project,
|
|
255
|
+
format: options.format,
|
|
246
256
|
});
|
|
247
|
-
|
|
257
|
+
console.log(`Exported ${options.format} config to ${outPath}`);
|
|
258
|
+
});
|
|
259
|
+
program
|
|
260
|
+
.command('compare')
|
|
261
|
+
.description('Compare two captures or two versions of the same URL')
|
|
262
|
+
.option('--a <id>', 'First inspiration ID')
|
|
263
|
+
.option('--b <id>', 'Second inspiration ID')
|
|
264
|
+
.option('--inspo <id>', 'Inspiration ID (compares against previous version)')
|
|
265
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
266
|
+
.action(async (options) => {
|
|
267
|
+
const outPath = await compareCaptures({
|
|
248
268
|
rootDir: path.resolve(options.root),
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
headed: options.headed,
|
|
253
|
-
llm,
|
|
269
|
+
inspoA: options.a,
|
|
270
|
+
inspoB: options.b,
|
|
271
|
+
inspo: options.inspo,
|
|
254
272
|
});
|
|
255
|
-
|
|
273
|
+
console.log(`Comparison written to ${outPath}`);
|
|
256
274
|
});
|
|
257
|
-
|
|
258
|
-
.command('
|
|
259
|
-
.description('
|
|
260
|
-
.
|
|
261
|
-
.
|
|
275
|
+
program
|
|
276
|
+
.command('batch')
|
|
277
|
+
.description('Batch capture from a file of URLs')
|
|
278
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
279
|
+
.requiredOption('--file <path>', 'Path to URL list file (tab-separated: URL, name, tags)')
|
|
262
280
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
281
|
+
.option('--no-visuals', 'Skip SVG visual generation')
|
|
263
282
|
.action(async (options) => {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
283
|
+
const result = await batchCapture({
|
|
284
|
+
rootDir: path.resolve(options.root),
|
|
285
|
+
project: options.project,
|
|
286
|
+
filePath: path.resolve(options.file),
|
|
287
|
+
skipVisuals: !options.visuals,
|
|
288
|
+
});
|
|
289
|
+
console.log(`Batch complete: ${result.succeeded}/${result.total} captured`);
|
|
290
|
+
if (result.failed.length > 0) {
|
|
291
|
+
console.log(`Failed: ${result.failed.join(', ')}`);
|
|
271
292
|
}
|
|
272
|
-
renderTasteProfile(profile);
|
|
273
293
|
});
|
|
274
|
-
|
|
275
|
-
.command('
|
|
276
|
-
.description('
|
|
277
|
-
.requiredOption('--
|
|
278
|
-
.requiredOption('--from <source>', 'Source inspiration ID, URL, or hostname')
|
|
279
|
-
.option('--project <project>', 'Project ID/slug', 'default')
|
|
280
|
-
.option('--index <n>', 'Pick Nth matched component', '0')
|
|
281
|
-
.option('--note <text>', 'Optional note for why this was picked')
|
|
294
|
+
program
|
|
295
|
+
.command('moodboard')
|
|
296
|
+
.description('Generate visual moodboard from project captures')
|
|
297
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
282
298
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
283
299
|
.action(async (options) => {
|
|
284
|
-
const
|
|
300
|
+
const outPath = await generateMoodboard({
|
|
285
301
|
rootDir: path.resolve(options.root),
|
|
286
|
-
|
|
287
|
-
componentKind: options.component,
|
|
288
|
-
sourceUrlOrId: options.from,
|
|
289
|
-
index: parseInteger(options.index, 0),
|
|
290
|
-
note: options.note,
|
|
302
|
+
project: options.project,
|
|
291
303
|
});
|
|
292
|
-
console.log(`
|
|
304
|
+
console.log(`Moodboard generated at ${outPath}`);
|
|
293
305
|
});
|
|
294
|
-
|
|
295
|
-
.command('
|
|
296
|
-
.description('
|
|
297
|
-
.option('--project <project>', '
|
|
298
|
-
.option('--json', 'Output as JSON', false)
|
|
306
|
+
program
|
|
307
|
+
.command('trends')
|
|
308
|
+
.description('Detect design trends across captures')
|
|
309
|
+
.option('--project <project>', 'Filter to project')
|
|
299
310
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
300
|
-
.action(async (
|
|
301
|
-
const result = await
|
|
311
|
+
.action(async (options) => {
|
|
312
|
+
const result = await detectAndWriteTrends({
|
|
302
313
|
rootDir: path.resolve(options.root),
|
|
303
|
-
|
|
304
|
-
scanPath: scanPath ?? process.cwd(),
|
|
314
|
+
project: options.project,
|
|
305
315
|
});
|
|
306
|
-
|
|
307
|
-
console.log(JSON.stringify(result, null, 2));
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
renderTasteDiff(result.diff);
|
|
316
|
+
console.log(`Detected ${result.trends} trends, wrote ${result.written} notes`);
|
|
311
317
|
});
|
|
312
|
-
|
|
313
|
-
.command('
|
|
314
|
-
.description('
|
|
315
|
-
.
|
|
316
|
-
.
|
|
318
|
+
program
|
|
319
|
+
.command('scorecard')
|
|
320
|
+
.description('Audit local codebase against captured design tokens')
|
|
321
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
322
|
+
.requiredOption('--scan <path>', 'Directory to scan')
|
|
317
323
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
318
324
|
.action(async (options) => {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
const next = nextClarifyingQuestion(profile);
|
|
325
|
-
if (!next) {
|
|
326
|
-
console.log('No unresolved taste conflicts.');
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
const unresolvedIndex = profile.conflicts.findIndex((conflict) => !conflict.resolved);
|
|
330
|
-
if (unresolvedIndex < 0) {
|
|
331
|
-
console.log('No unresolved taste conflicts.');
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
let answer = options.answer ? resolveTasteAnswer(options.answer, next.options) : '';
|
|
335
|
-
if (!answer) {
|
|
336
|
-
console.log(next.question);
|
|
337
|
-
for (let i = 0; i < next.options.length; i += 1) {
|
|
338
|
-
const shouldUse = await confirmPrompt(`Use option ${i + 1}: ${next.options[i]}?`, false);
|
|
339
|
-
if (shouldUse) {
|
|
340
|
-
answer = next.options[i];
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (!answer) {
|
|
346
|
-
console.log('No decision applied.');
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const decision = await applyDecision({
|
|
350
|
-
rootDir,
|
|
351
|
-
projectId: options.project,
|
|
352
|
-
conflictIndex: unresolvedIndex,
|
|
353
|
-
answer,
|
|
325
|
+
const outPath = await runScorecard({
|
|
326
|
+
rootDir: path.resolve(options.root),
|
|
327
|
+
project: options.project,
|
|
328
|
+
scanPath: path.resolve(options.scan),
|
|
354
329
|
});
|
|
355
|
-
console.log(`
|
|
330
|
+
console.log(`Scorecard written to ${outPath}`);
|
|
356
331
|
});
|
|
357
|
-
|
|
358
|
-
.command('
|
|
359
|
-
.description('Generate
|
|
360
|
-
.
|
|
361
|
-
.option('--target <target>', 'Generation target: component, page, or tokens', 'component')
|
|
362
|
-
.option('--component <kind>', 'Component kind when target=component')
|
|
363
|
-
.option('--framework <name>', 'Framework hint (react, next, vue, etc.)')
|
|
364
|
-
.option('--llm-base-url <url>', 'OpenAI-compatible LLM base URL')
|
|
365
|
-
.option('--llm-api-key <key>', 'LLM API key')
|
|
366
|
-
.option('--llm-model <model>', 'LLM model id')
|
|
367
|
-
.option('--llm-timeout-ms <ms>', 'LLM timeout in milliseconds', '30000')
|
|
332
|
+
program
|
|
333
|
+
.command('component-graph')
|
|
334
|
+
.description('Generate component relationship graph for a project')
|
|
335
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
368
336
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
369
337
|
.action(async (options) => {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
taste: profile,
|
|
388
|
-
target,
|
|
389
|
-
componentKind: options.component,
|
|
390
|
-
framework: options.framework,
|
|
391
|
-
llm,
|
|
338
|
+
const outPath = await generateComponentGraphCmd({
|
|
339
|
+
rootDir: path.resolve(options.root),
|
|
340
|
+
project: options.project,
|
|
341
|
+
});
|
|
342
|
+
console.log(`Component graph written to ${outPath}`);
|
|
343
|
+
});
|
|
344
|
+
program
|
|
345
|
+
.command('review')
|
|
346
|
+
.description('Generate design review checklist')
|
|
347
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
348
|
+
.option('--scan <path>', 'Directory to scan for codebase audit')
|
|
349
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
350
|
+
.action(async (options) => {
|
|
351
|
+
const outPath = await generateReviewChecklist({
|
|
352
|
+
rootDir: path.resolve(options.root),
|
|
353
|
+
project: options.project,
|
|
354
|
+
scanPath: options.scan ? path.resolve(options.scan) : undefined,
|
|
392
355
|
});
|
|
393
|
-
console.log(
|
|
394
|
-
|
|
395
|
-
|
|
356
|
+
console.log(`Review checklist written to ${outPath}`);
|
|
357
|
+
});
|
|
358
|
+
program
|
|
359
|
+
.command('name-tokens')
|
|
360
|
+
.description('Preview human-readable token names for a project')
|
|
361
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
362
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
363
|
+
.action(async (options) => {
|
|
364
|
+
const map = await nameTokensCmd({
|
|
365
|
+
rootDir: path.resolve(options.root),
|
|
366
|
+
project: options.project,
|
|
367
|
+
});
|
|
368
|
+
for (const [raw, name] of map) {
|
|
369
|
+
console.log(`${raw} → ${name}`);
|
|
396
370
|
}
|
|
371
|
+
console.log(`\n${map.size} tokens named`);
|
|
397
372
|
});
|
|
398
|
-
/* ─── install-skill ─── */
|
|
399
373
|
program
|
|
400
|
-
.command('
|
|
401
|
-
.description('
|
|
402
|
-
.
|
|
403
|
-
|
|
374
|
+
.command('wiki')
|
|
375
|
+
.description('Generate design wiki with per-project pages and shared context space')
|
|
376
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
377
|
+
.action(async (options) => {
|
|
378
|
+
const result = await buildWiki({
|
|
379
|
+
rootDir: path.resolve(options.root),
|
|
380
|
+
});
|
|
381
|
+
console.log(`Wiki generated: ${result.projectPages} project pages, ${result.sharedTokens} shared tokens`);
|
|
382
|
+
console.log(`Browse at ${result.wikiDir}`);
|
|
404
383
|
});
|
|
405
|
-
/* ─── reindex ─── */
|
|
406
384
|
program
|
|
407
|
-
.command('
|
|
408
|
-
.description('
|
|
385
|
+
.command('graph')
|
|
386
|
+
.description('Generate interactive knowledge graph visualization')
|
|
409
387
|
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
410
388
|
.action(async (options) => {
|
|
411
|
-
await
|
|
412
|
-
|
|
389
|
+
const outPath = await buildGraphView({
|
|
390
|
+
rootDir: path.resolve(options.root),
|
|
391
|
+
});
|
|
392
|
+
console.log(`Knowledge graph generated at ${outPath}`);
|
|
393
|
+
console.log('Open in your browser to explore.');
|
|
394
|
+
});
|
|
395
|
+
program
|
|
396
|
+
.command('context')
|
|
397
|
+
.description('Generate design context file for AI tools (Claude Code, Cursor)')
|
|
398
|
+
.option('--project <project>', 'Project ID (omit for unified cross-project context)')
|
|
399
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
400
|
+
.action(async (options) => {
|
|
401
|
+
const outPath = await generateContext({
|
|
402
|
+
rootDir: path.resolve(options.root),
|
|
403
|
+
project: options.project,
|
|
404
|
+
});
|
|
405
|
+
console.log(`Design context written to ${outPath}`);
|
|
406
|
+
console.log('Drop this file into .claude/ or .cursorrules for AI-assisted development.');
|
|
407
|
+
});
|
|
408
|
+
program
|
|
409
|
+
.command('writing-style')
|
|
410
|
+
.description('Analyze writing style patterns across captured content')
|
|
411
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
412
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
413
|
+
.action(async (options) => {
|
|
414
|
+
const outPath = await analyzeWritingStyleCmd({
|
|
415
|
+
rootDir: path.resolve(options.root),
|
|
416
|
+
project: options.project,
|
|
417
|
+
});
|
|
418
|
+
console.log(`Writing style analysis written to ${outPath}`);
|
|
419
|
+
});
|
|
420
|
+
program
|
|
421
|
+
.command('system-diff')
|
|
422
|
+
.description('Diff design systems between projects or over time')
|
|
423
|
+
.option('--project <project>', 'Project ID for temporal diff')
|
|
424
|
+
.option('--project-a <project>', 'First project for cross-project diff')
|
|
425
|
+
.option('--project-b <project>', 'Second project for cross-project diff')
|
|
426
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
427
|
+
.action(async (options) => {
|
|
428
|
+
const outPath = await runSystemDiff({
|
|
429
|
+
rootDir: path.resolve(options.root),
|
|
430
|
+
project: options.project,
|
|
431
|
+
projectA: options.projectA,
|
|
432
|
+
projectB: options.projectB,
|
|
433
|
+
});
|
|
434
|
+
console.log(`System diff written to ${outPath}`);
|
|
435
|
+
});
|
|
436
|
+
program
|
|
437
|
+
.command('taste')
|
|
438
|
+
.description('Build a taste profile from multiple design inspirations')
|
|
439
|
+
.argument('<urls...>', 'URLs or domains to analyze')
|
|
440
|
+
.requiredOption('--project <project>', 'Project ID/slug')
|
|
441
|
+
.option('--project-name <name>', 'Readable project name')
|
|
442
|
+
.option('--headed', 'Use visible browser for capture', false)
|
|
443
|
+
.option('--llm-base-url <url>', 'OpenAI-compatible LLM base URL')
|
|
444
|
+
.option('--llm-api-key <key>', 'LLM API key')
|
|
445
|
+
.option('--llm-model <model>', 'LLM model id')
|
|
446
|
+
.option('--llm-timeout-ms <ms>', 'LLM timeout in milliseconds', '30000')
|
|
447
|
+
.option('--root <dir>', 'Workspace root', process.cwd())
|
|
448
|
+
.action(async (urls, options) => {
|
|
449
|
+
const llm = resolveLlmConfig({
|
|
450
|
+
baseUrl: options.llmBaseUrl,
|
|
451
|
+
apiKey: options.llmApiKey,
|
|
452
|
+
model: options.llmModel,
|
|
453
|
+
timeoutMs: parseInteger(options.llmTimeoutMs, 30000),
|
|
454
|
+
});
|
|
455
|
+
const profile = await buildTasteProfile({
|
|
456
|
+
rootDir: path.resolve(options.root),
|
|
457
|
+
projectId: options.project,
|
|
458
|
+
projectName: options.projectName,
|
|
459
|
+
urls,
|
|
460
|
+
headed: options.headed,
|
|
461
|
+
llm,
|
|
462
|
+
});
|
|
463
|
+
console.log(renderTasteProfile(profile));
|
|
413
464
|
});
|
|
414
|
-
/* ─── Default command routing ─── */
|
|
415
|
-
// Bare URLs/domains route to `taste build`.
|
|
416
|
-
// Bare local paths route to `scan`.
|
|
417
|
-
// No args defaults to `scan`.
|
|
418
|
-
const commandNames = program.commands.map((command) => command.name());
|
|
419
|
-
const firstArg = process.argv[2];
|
|
420
|
-
if (!firstArg) {
|
|
421
|
-
process.argv.splice(2, 0, 'scan');
|
|
422
|
-
}
|
|
423
|
-
else if (!firstArg.startsWith('-') && !commandNames.includes(firstArg)) {
|
|
424
|
-
if (looksLikeUrl(firstArg)) {
|
|
425
|
-
process.argv.splice(2, 0, 'taste', 'build');
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
process.argv.splice(2, 0, 'scan');
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
465
|
await program.parseAsync(process.argv);
|
|
432
466
|
}
|
|
433
467
|
main().catch((error) => {
|