@buoy-design/cli 0.3.32 → 0.3.33

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.
Files changed (40) hide show
  1. package/dist/commands/show.d.ts.map +1 -1
  2. package/dist/commands/show.js +6 -0
  3. package/dist/commands/show.js.map +1 -1
  4. package/package.json +3 -3
  5. package/dist/commands/audit.d.ts +0 -3
  6. package/dist/commands/audit.d.ts.map +0 -1
  7. package/dist/commands/audit.js +0 -235
  8. package/dist/commands/audit.js.map +0 -1
  9. package/dist/commands/baseline.d.ts +0 -47
  10. package/dist/commands/baseline.d.ts.map +0 -1
  11. package/dist/commands/baseline.js +0 -327
  12. package/dist/commands/baseline.js.map +0 -1
  13. package/dist/commands/begin.d.ts +0 -9
  14. package/dist/commands/begin.d.ts.map +0 -1
  15. package/dist/commands/begin.js +0 -827
  16. package/dist/commands/begin.js.map +0 -1
  17. package/dist/commands/build.d.ts +0 -8
  18. package/dist/commands/build.d.ts.map +0 -1
  19. package/dist/commands/build.js +0 -26
  20. package/dist/commands/build.js.map +0 -1
  21. package/dist/commands/commands.d.ts +0 -8
  22. package/dist/commands/commands.d.ts.map +0 -1
  23. package/dist/commands/commands.js +0 -148
  24. package/dist/commands/commands.js.map +0 -1
  25. package/dist/commands/components-query.d.ts +0 -13
  26. package/dist/commands/components-query.d.ts.map +0 -1
  27. package/dist/commands/components-query.js +0 -263
  28. package/dist/commands/components-query.js.map +0 -1
  29. package/dist/commands/components.d.ts +0 -8
  30. package/dist/commands/components.d.ts.map +0 -1
  31. package/dist/commands/components.js +0 -14
  32. package/dist/commands/components.js.map +0 -1
  33. package/dist/commands/history.d.ts +0 -3
  34. package/dist/commands/history.d.ts.map +0 -1
  35. package/dist/commands/history.js +0 -352
  36. package/dist/commands/history.js.map +0 -1
  37. package/dist/commands/plugins.d.ts +0 -3
  38. package/dist/commands/plugins.d.ts.map +0 -1
  39. package/dist/commands/plugins.js +0 -63
  40. package/dist/commands/plugins.js.map +0 -1
@@ -1,827 +0,0 @@
1
- /**
2
- * buoy begin - Interactive wizard for new users.
3
- *
4
- * Explains what Buoy does, scans the project, and guides users through setup
5
- * with clear, jargon-free language.
6
- */
7
- import { Command } from 'commander';
8
- import chalk from 'chalk';
9
- import { existsSync, readFileSync } from 'fs';
10
- import { join } from 'path';
11
- import { glob } from 'glob';
12
- import { loadConfig, getConfigPath } from '../config/loader.js';
13
- import { buildAutoConfig } from '../config/auto-detect.js';
14
- import { spinner, error as errorLog } from '../output/reporters.js';
15
- import { ScanOrchestrator } from '../scan/orchestrator.js';
16
- import { ProjectDetector } from '../detect/project-detector.js';
17
- import { showMenu, } from '../wizard/menu.js';
18
- import { reviewIssues } from '../wizard/issue-reviewer.js';
19
- import { setupCI } from '../wizard/ci-generator.js';
20
- import { setupAIGuardrails } from '../wizard/ai-guardrails-generator.js';
21
- import { setupHooks as installHooks, generateStandaloneHook, detectHookSystem, } from '../hooks/index.js';
22
- /**
23
- * Detect if project has design tokens.
24
- */
25
- async function detectTokens(cwd) {
26
- const patterns = [
27
- '**/design-tokens.json',
28
- '**/tokens.json',
29
- '**/.tokens.json',
30
- '**/tokens.css',
31
- '**/variables.css',
32
- '**/design-tokens.css',
33
- '**/_tokens.scss',
34
- '**/_variables.scss',
35
- '**/theme.json',
36
- '**/style-dictionary/**/*.json',
37
- ];
38
- const files = [];
39
- for (const pattern of patterns) {
40
- const matches = await glob(pattern, {
41
- cwd,
42
- nodir: true,
43
- ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
44
- });
45
- files.push(...matches);
46
- }
47
- // Also check if tailwind.config exists (counts as having tokens)
48
- const tailwindConfigs = await glob('tailwind.config.{js,ts,mjs}', { cwd });
49
- if (tailwindConfigs.length > 0) {
50
- files.push(...tailwindConfigs);
51
- }
52
- return { hasTokens: files.length > 0, files: [...new Set(files)] };
53
- }
54
- /**
55
- * Detect if pre-commit hooks are set up.
56
- */
57
- function detectHooks(cwd) {
58
- const gitHookPath = join(cwd, '.git', 'hooks', 'pre-commit');
59
- if (existsSync(gitHookPath)) {
60
- try {
61
- const content = readFileSync(gitHookPath, 'utf-8');
62
- if (content.includes('buoy')) {
63
- return true;
64
- }
65
- }
66
- catch {
67
- // Ignore read errors
68
- }
69
- }
70
- // Check for husky
71
- const huskyPath = join(cwd, '.husky', 'pre-commit');
72
- if (existsSync(huskyPath)) {
73
- try {
74
- const content = readFileSync(huskyPath, 'utf-8');
75
- if (content.includes('buoy')) {
76
- return true;
77
- }
78
- }
79
- catch {
80
- // Ignore read errors
81
- }
82
- }
83
- // Check for lint-staged config with buoy
84
- const packageJsonPath = join(cwd, 'package.json');
85
- if (existsSync(packageJsonPath)) {
86
- try {
87
- const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
88
- if (pkg['lint-staged'] && JSON.stringify(pkg['lint-staged']).includes('buoy')) {
89
- return true;
90
- }
91
- }
92
- catch {
93
- // Ignore read errors
94
- }
95
- }
96
- return false;
97
- }
98
- /**
99
- * Detect if baseline is set up.
100
- */
101
- function detectBaseline(cwd) {
102
- return existsSync(join(cwd, '.buoy', 'baseline.json')) ||
103
- existsSync(join(cwd, 'buoy-baseline.json'));
104
- }
105
- /**
106
- * Detect if AI guardrails are set up.
107
- */
108
- function detectAISetup(cwd) {
109
- // Check for skill file
110
- if (existsSync(join(cwd, '.claude', 'skills', 'design-system', 'SKILL.md'))) {
111
- return true;
112
- }
113
- // Check for design system section in CLAUDE.md
114
- const claudeMdPath = join(cwd, 'CLAUDE.md');
115
- if (existsSync(claudeMdPath)) {
116
- try {
117
- const content = readFileSync(claudeMdPath, 'utf-8');
118
- if (content.includes('Design System') || content.includes('design-system') || content.includes('buoy')) {
119
- return true;
120
- }
121
- }
122
- catch {
123
- // Ignore read errors
124
- }
125
- }
126
- return false;
127
- }
128
- /**
129
- * Run all setup steps automatically without prompts.
130
- * This is the "just make it work" mode for users who want quick setup.
131
- */
132
- async function runQuickOnboard(cwd) {
133
- console.log('');
134
- console.log(chalk.cyan.bold('🛟 Buoy Quick Setup'));
135
- console.log('');
136
- // Step 1: Detect project state
137
- const spin = spinner('Analyzing project...');
138
- const tokenResult = await detectTokens(cwd);
139
- const hasAISetup = detectAISetup(cwd);
140
- const existingConfig = getConfigPath();
141
- spin.stop();
142
- console.log(` ${chalk.green('✓')} Project analyzed`);
143
- // Step 2: Create tokens if needed
144
- if (!tokenResult.hasTokens) {
145
- console.log('');
146
- console.log(chalk.cyan(' Creating design tokens...'));
147
- const { spawn } = await import('child_process');
148
- await new Promise((resolve) => {
149
- const child = spawn('npx', ['ahoybuoy', 'anchor', '-y'], {
150
- cwd,
151
- stdio: 'inherit',
152
- });
153
- child.on('close', () => resolve());
154
- });
155
- }
156
- else {
157
- console.log(` ${chalk.green('✓')} Design tokens found`);
158
- }
159
- // Step 3: Load or create config
160
- let config;
161
- if (existingConfig) {
162
- const result = await loadConfig();
163
- config = result.config;
164
- }
165
- else {
166
- const autoResult = await buildAutoConfig(cwd);
167
- config = autoResult.config;
168
- }
169
- // Step 4: Run scan
170
- const scanSpin = spinner('Scanning for drift...');
171
- try {
172
- const orchestrator = new ScanOrchestrator(config);
173
- const { components } = await orchestrator.scanComponents({});
174
- const detector = new ProjectDetector(cwd);
175
- const projectInfo = await detector.detect();
176
- const { SemanticDiffEngine } = await import('@buoy-design/core/analysis');
177
- const engine = new SemanticDiffEngine();
178
- const diffResult = engine.analyzeComponents(components, {
179
- checkDeprecated: true,
180
- checkNaming: true,
181
- checkDocumentation: true,
182
- });
183
- scanSpin.stop();
184
- console.log(` ${chalk.green('✓')} Scanned ${components.length} components`);
185
- const critical = diffResult.drifts.filter(d => d.severity === 'critical').length;
186
- const warning = diffResult.drifts.filter(d => d.severity === 'warning').length;
187
- if (diffResult.drifts.length > 0) {
188
- console.log(` ${chalk.yellow('!')} Found ${diffResult.drifts.length} drift issue${diffResult.drifts.length === 1 ? '' : 's'}`);
189
- if (critical > 0)
190
- console.log(` ${chalk.red('•')} ${critical} critical`);
191
- if (warning > 0)
192
- console.log(` ${chalk.yellow('•')} ${warning} warnings`);
193
- }
194
- else {
195
- console.log(` ${chalk.green('✓')} No drift detected`);
196
- }
197
- // Log frameworks detected
198
- if (projectInfo.frameworks.length > 0) {
199
- console.log(` ${chalk.green('✓')} Detected: ${projectInfo.frameworks.map(f => f.name).join(', ')}`);
200
- }
201
- }
202
- catch (err) {
203
- scanSpin.stop();
204
- console.log(` ${chalk.yellow('!')} Scan skipped: ${err instanceof Error ? err.message : String(err)}`);
205
- }
206
- // Step 5: Set up AI guardrails if needed
207
- if (!hasAISetup) {
208
- console.log('');
209
- console.log(chalk.cyan(' Setting up AI guardrails...'));
210
- const result = await setupAIGuardrails(cwd, config);
211
- if (result.skillExported) {
212
- console.log(` ${chalk.green('✓')} AI skill exported`);
213
- }
214
- if (result.contextGenerated) {
215
- console.log(` ${chalk.green('✓')} CLAUDE.md updated`);
216
- }
217
- }
218
- else {
219
- console.log(` ${chalk.green('✓')} AI guardrails already configured`);
220
- }
221
- // Summary
222
- console.log('');
223
- console.log(chalk.green.bold(' ━'.repeat(24)));
224
- console.log('');
225
- console.log(chalk.green.bold(' ✓ Setup complete!'));
226
- console.log('');
227
- console.log(chalk.green.bold(' ━'.repeat(24)));
228
- console.log('');
229
- console.log(chalk.dim(' Next steps:'));
230
- console.log(` ${chalk.cyan('buoy show all')} Check for drift`);
231
- console.log(` ${chalk.cyan('buoy drift check')} Pre-commit validation`);
232
- console.log(` ${chalk.cyan('buoy begin')} Interactive setup`);
233
- console.log('');
234
- }
235
- export function createBeginCommand() {
236
- return new Command('begin')
237
- .description('Interactive wizard to get started with Buoy')
238
- .option('-y, --yes', 'Run all setup steps automatically without prompts')
239
- .action(async (options) => {
240
- const cwd = process.cwd();
241
- // Quick onboard mode - run all steps automatically
242
- if (options.yes) {
243
- await runQuickOnboard(cwd);
244
- return;
245
- }
246
- // Check if we're in an interactive terminal
247
- if (!process.stdin.isTTY) {
248
- // Run scan and output AI-friendly results so the AI can walk the user through it
249
- await runAIGuidedWizard(cwd);
250
- return;
251
- }
252
- // Welcome
253
- console.log('');
254
- console.log(chalk.cyan.bold('🛟 Welcome to Buoy'));
255
- console.log('');
256
- // Detect project state
257
- const spin = spinner('Analyzing your project...');
258
- const tokenResult = await detectTokens(cwd);
259
- const hasAISetup = detectAISetup(cwd);
260
- const hasHooks = detectHooks(cwd);
261
- const hasBaseline = detectBaseline(cwd);
262
- const hasCISetup = existsSync(join(cwd, '.github', 'workflows', 'buoy.yml')) ||
263
- existsSync(join(cwd, '.gitlab-ci.yml'));
264
- const hasConfig = !!getConfigPath();
265
- // Initialize state
266
- const state = {
267
- hasTokens: tokenResult.hasTokens,
268
- tokenFiles: tokenResult.files,
269
- hasAISetup,
270
- hasHooks,
271
- hasCISetup,
272
- hasConfig,
273
- hasBaseline,
274
- hasScanned: false,
275
- components: [],
276
- tokens: [],
277
- drifts: [],
278
- issuesReviewed: false,
279
- };
280
- spin.stop();
281
- // Show what we found
282
- showProjectStatus(state);
283
- // Main menu loop
284
- await menuLoop(cwd, state);
285
- // Exit message
286
- showExitMessage();
287
- });
288
- }
289
- /**
290
- * Show project status based on detection.
291
- */
292
- function showProjectStatus(state) {
293
- console.log(chalk.dim(' What we found:'));
294
- console.log('');
295
- // Tokens
296
- if (state.hasTokens) {
297
- console.log(` ${chalk.green('✓')} Design tokens found`);
298
- if (state.tokenFiles.length <= 3) {
299
- state.tokenFiles.forEach(f => console.log(chalk.dim(` ${f}`)));
300
- }
301
- else {
302
- console.log(chalk.dim(` ${state.tokenFiles.length} token files`));
303
- }
304
- }
305
- else {
306
- console.log(` ${chalk.yellow('○')} No design tokens found`);
307
- }
308
- // AI Setup
309
- if (state.hasAISetup) {
310
- console.log(` ${chalk.green('✓')} AI guardrails configured`);
311
- }
312
- else {
313
- console.log(` ${chalk.yellow('○')} AI guardrails not set up`);
314
- }
315
- // Pre-commit hooks
316
- if (state.hasHooks) {
317
- console.log(` ${chalk.green('✓')} Pre-commit hooks active`);
318
- }
319
- else {
320
- console.log(` ${chalk.dim('○')} Pre-commit hooks not set up`);
321
- }
322
- // CI Setup
323
- if (state.hasCISetup) {
324
- console.log(` ${chalk.green('✓')} CI/CD integration active`);
325
- }
326
- else {
327
- console.log(` ${chalk.dim('○')} CI/CD not configured`);
328
- }
329
- // Baseline
330
- if (state.hasBaseline) {
331
- console.log(` ${chalk.green('✓')} Baseline established`);
332
- }
333
- console.log('');
334
- }
335
- /**
336
- * Scan the project and show results in a user-friendly way.
337
- */
338
- async function runScan(cwd) {
339
- const spin = spinner('Looking at your project...');
340
- try {
341
- // Load or auto-detect config
342
- const existingConfig = getConfigPath();
343
- let config;
344
- let autoResult;
345
- if (existingConfig) {
346
- const result = await loadConfig();
347
- config = result.config;
348
- }
349
- else {
350
- autoResult = await buildAutoConfig(cwd);
351
- config = autoResult.config;
352
- }
353
- // Run scan
354
- const orchestrator = new ScanOrchestrator(config);
355
- const { components } = await orchestrator.scanComponents({
356
- onProgress: (msg) => {
357
- spin.text = msg;
358
- },
359
- });
360
- // Detect frameworks
361
- spin.text = 'Detecting frameworks...';
362
- const detector = new ProjectDetector(cwd);
363
- const projectInfo = await detector.detect();
364
- // Run drift analysis
365
- spin.text = 'Analyzing for drift...';
366
- const { SemanticDiffEngine } = await import('@buoy-design/core/analysis');
367
- const engine = new SemanticDiffEngine();
368
- const diffResult = engine.analyzeComponents(components, {
369
- checkDeprecated: true,
370
- checkNaming: true,
371
- checkDocumentation: true,
372
- });
373
- const drifts = [...diffResult.drifts];
374
- // Check framework sprawl
375
- const sprawlSignal = engine.checkFrameworkSprawl(projectInfo.frameworks.map(f => ({ name: f.name, version: f.version })));
376
- if (sprawlSignal) {
377
- drifts.push(sprawlSignal);
378
- }
379
- spin.stop();
380
- return { components, drifts, config, projectInfo, autoResult };
381
- }
382
- catch (err) {
383
- spin.stop();
384
- throw err;
385
- }
386
- }
387
- /**
388
- * Show scan results in a friendly way with clear transparency.
389
- */
390
- function showScanResults(components, drifts, projectInfo) {
391
- console.log('');
392
- console.log(chalk.bold(' Scan Results'));
393
- console.log('');
394
- // Framework detection
395
- if (projectInfo.frameworks.length > 0) {
396
- const frameworkNames = projectInfo.frameworks.map(f => f.name).join(', ');
397
- console.log(` ${chalk.green('✓')} Detected: ${chalk.cyan(frameworkNames)}`);
398
- }
399
- // Components - be transparent about what was scanned
400
- if (components.length > 0) {
401
- console.log(` ${chalk.green('✓')} Scanned ${components.length} component${components.length === 1 ? '' : 's'}`);
402
- }
403
- else {
404
- console.log(` ${chalk.yellow('○')} No components found to scan`);
405
- console.log(chalk.dim(' Tip: Run buoy show all --verbose to see what paths are being searched'));
406
- }
407
- // Drift summary - clearer language about severity
408
- const critical = drifts.filter(d => d.severity === 'critical').length;
409
- const warning = drifts.filter(d => d.severity === 'warning').length;
410
- const total = drifts.length;
411
- console.log('');
412
- if (components.length === 0) {
413
- console.log(chalk.dim(' (No components to analyze for drift)'));
414
- }
415
- else if (total === 0) {
416
- console.log(` ${chalk.green('✓')} No drift detected — your code follows consistent patterns!`);
417
- }
418
- else {
419
- console.log(` ${chalk.yellow('!')} Found ${total} inconsistenc${total === 1 ? 'y' : 'ies'}:`);
420
- if (critical > 0) {
421
- console.log(` ${chalk.red('•')} ${critical} ${chalk.red('should fix')} — hardcoded values that bypass design tokens`);
422
- }
423
- if (warning > 0) {
424
- console.log(` ${chalk.yellow('•')} ${warning} ${chalk.yellow('could improve')} — naming or pattern suggestions`);
425
- }
426
- }
427
- console.log('');
428
- }
429
- /**
430
- * Setup pre-commit hooks with celebration!
431
- * This is the "lock in your gains" moment.
432
- */
433
- async function setupHooks(cwd) {
434
- console.log('');
435
- console.log(chalk.cyan.bold(' 🔒 Locking in your design system...'));
436
- console.log('');
437
- const hookSystem = detectHookSystem(cwd);
438
- let success = false;
439
- if (hookSystem) {
440
- console.log(chalk.dim(` Detected: ${hookSystem}`));
441
- const result = installHooks(cwd);
442
- success = result.success;
443
- if (result.success) {
444
- console.log(` ${chalk.green('✓')} ${result.message}`);
445
- }
446
- else {
447
- console.log(` ${chalk.yellow('!')} ${result.message}`);
448
- }
449
- }
450
- else {
451
- // Generate standalone hook
452
- const result = generateStandaloneHook(cwd);
453
- success = result.success;
454
- if (result.success) {
455
- console.log(` ${chalk.green('✓')} ${result.message}`);
456
- }
457
- else {
458
- console.log(` ${chalk.yellow('!')} ${result.message}`);
459
- }
460
- }
461
- if (success) {
462
- // Celebration!
463
- console.log('');
464
- console.log(chalk.green.bold(' ━'.repeat(24)));
465
- console.log('');
466
- console.log(chalk.green.bold(' 🎉 Your design system is protected!'));
467
- console.log('');
468
- console.log(chalk.green.bold(' ━'.repeat(24)));
469
- console.log('');
470
- console.log(chalk.dim(' Every commit will now be checked for drift.'));
471
- console.log(chalk.dim(' Use --no-verify to bypass if needed.'));
472
- console.log('');
473
- }
474
- return { success };
475
- }
476
- /**
477
- * Main menu loop - smart recommendations based on project state.
478
- */
479
- async function menuLoop(cwd, state) {
480
- let config;
481
- while (true) {
482
- const action = await showMainMenu(state);
483
- switch (action) {
484
- case 'scan': {
485
- // Run scan to discover components and tokens
486
- console.log('');
487
- console.log(chalk.cyan(' Scanning your codebase...'));
488
- console.log('');
489
- try {
490
- const scanResult = await runScan(cwd);
491
- state.hasScanned = true;
492
- state.components = scanResult.components;
493
- state.drifts = scanResult.drifts;
494
- config = scanResult.config;
495
- console.log('');
496
- console.log(chalk.green(' ✓ Scan complete!'));
497
- console.log('');
498
- }
499
- catch (err) {
500
- const message = err instanceof Error ? err.message : String(err);
501
- errorLog(`Scan failed: ${message}`);
502
- }
503
- break;
504
- }
505
- case 'dock-agents': {
506
- // Run dock agents to set up AI integration
507
- console.log('');
508
- console.log(chalk.cyan(' Setting up AI integration...'));
509
- console.log('');
510
- // Load config if needed
511
- if (!config) {
512
- const configPath = getConfigPath();
513
- if (configPath) {
514
- const result = await loadConfig();
515
- config = result.config;
516
- }
517
- else {
518
- const autoResult = await buildAutoConfig(cwd);
519
- config = autoResult.config;
520
- }
521
- }
522
- const result = await setupAIGuardrails(cwd, config);
523
- if (result.skillExported || result.contextGenerated) {
524
- state.hasAISetup = true;
525
- }
526
- break;
527
- }
528
- case 'check-drift': {
529
- // Run scan and show drift
530
- try {
531
- const scanResult = await runScan(cwd);
532
- state.hasScanned = true;
533
- state.components = scanResult.components;
534
- state.drifts = scanResult.drifts;
535
- config = scanResult.config;
536
- showScanResults(scanResult.components, scanResult.drifts, scanResult.projectInfo);
537
- }
538
- catch (err) {
539
- const message = err instanceof Error ? err.message : String(err);
540
- errorLog(`Scan failed: ${message}`);
541
- }
542
- break;
543
- }
544
- case 'review-issues': {
545
- if (state.drifts.length === 0) {
546
- console.log(chalk.green('\n No issues to review!\n'));
547
- break;
548
- }
549
- const result = await reviewIssues(state.drifts);
550
- if (result.completed) {
551
- state.issuesReviewed = true;
552
- }
553
- break;
554
- }
555
- case 'setup-hooks': {
556
- const result = await setupHooks(cwd);
557
- if (result.success) {
558
- state.hasHooks = true;
559
- }
560
- break;
561
- }
562
- case 'setup-ci': {
563
- await setupCI(cwd);
564
- state.hasCISetup = true;
565
- break;
566
- }
567
- case 'learn-more': {
568
- showLearnMore();
569
- break;
570
- }
571
- case 'exit':
572
- return;
573
- }
574
- }
575
- }
576
- /**
577
- * Show the main menu with smart recommendations based on project state.
578
- */
579
- async function showMainMenu(state) {
580
- const options = [];
581
- // Smart recommendations based on state
582
- if (!state.hasTokens) {
583
- // No tokens - primary action is scan
584
- options.push({
585
- label: '🔍 Scan your codebase',
586
- value: 'scan',
587
- description: 'Find components and design values in your code',
588
- });
589
- options.push({
590
- label: 'Check for drift anyway',
591
- value: 'check-drift',
592
- description: 'Find hardcoded values without a token file',
593
- });
594
- }
595
- else if (!state.hasAISetup) {
596
- // Has tokens but no AI setup - primary action is dock agents
597
- options.push({
598
- label: '🛟 Set up AI integration',
599
- value: 'dock-agents',
600
- description: 'Help AI tools follow your tokens and patterns',
601
- });
602
- options.push({
603
- label: 'Check for drift',
604
- value: 'check-drift',
605
- description: 'Find code that diverges from your tokens',
606
- });
607
- }
608
- else {
609
- // Has tokens + AI setup - primary action is drift check
610
- options.push({
611
- label: 'Check for drift',
612
- value: 'check-drift',
613
- description: 'Find code that diverges from your design system',
614
- });
615
- }
616
- // Show issues if we have them
617
- if (state.hasScanned && state.drifts.length > 0 && !state.issuesReviewed) {
618
- options.push({
619
- label: `Review ${state.drifts.length} issue${state.drifts.length === 1 ? '' : 's'}`,
620
- value: 'review-issues',
621
- description: 'See details and how to fix them',
622
- });
623
- }
624
- // Hook setup - only show after baseline is set OR no drift OR issues reviewed
625
- // This is the "lock in your gains" moment
626
- const isCleanState = state.hasBaseline ||
627
- (state.hasScanned && state.drifts.length === 0) ||
628
- state.issuesReviewed;
629
- if (!state.hasHooks && state.hasTokens && isCleanState) {
630
- options.push({
631
- label: '🔒 Lock it in with pre-commit hooks',
632
- value: 'setup-hooks',
633
- description: 'Catch new drift before it\'s committed',
634
- });
635
- }
636
- // CI setup - show after hooks are set up, or if they explicitly want it
637
- if (!state.hasCISetup && state.hasTokens && (state.hasHooks || isCleanState)) {
638
- options.push({
639
- label: '🚦 Add to CI/CD',
640
- value: 'setup-ci',
641
- description: 'Block PRs that introduce drift',
642
- });
643
- }
644
- // Learn more always available
645
- options.push({
646
- label: 'Learn more',
647
- value: 'learn-more',
648
- description: 'What is design drift?',
649
- });
650
- options.push({
651
- label: 'Exit',
652
- value: 'exit',
653
- });
654
- return showMenu('What would you like to do?', options);
655
- }
656
- /**
657
- * Show learn more section - explains drift in plain language.
658
- */
659
- function showLearnMore() {
660
- console.log('');
661
- console.log(chalk.bold(' What is Design Drift?'));
662
- console.log('');
663
- console.log(chalk.dim(' When you have a design system, you want code to follow it.'));
664
- console.log(chalk.dim(' But over time, inconsistencies creep in:'));
665
- console.log('');
666
- console.log(` ${chalk.red('•')} Someone writes ${chalk.yellow('color: #3b82f6')} instead of using your blue token`);
667
- console.log(` ${chalk.red('•')} A developer uses ${chalk.yellow('padding: 17px')} instead of your 4px spacing scale`);
668
- console.log(` ${chalk.red('•')} AI tools generate code that ignores your design system`);
669
- console.log('');
670
- console.log(chalk.dim(' This is "design drift" — and it adds up fast.'));
671
- console.log('');
672
- console.log(chalk.bold(' How Buoy Helps'));
673
- console.log('');
674
- console.log(` ${chalk.green('1.')} ${chalk.cyan('Scan')} your code to find drift`);
675
- console.log(` ${chalk.green('2.')} ${chalk.cyan('Review')} issues and see suggested fixes`);
676
- console.log(` ${chalk.green('3.')} ${chalk.cyan('Prevent')} future drift with CI checks and AI guardrails`);
677
- console.log('');
678
- console.log(chalk.dim(` Docs: ${chalk.cyan('https://buoy.design/docs')}`));
679
- console.log('');
680
- }
681
- /**
682
- * Show exit message.
683
- */
684
- function showExitMessage() {
685
- console.log('');
686
- console.log(chalk.green(' ✓ You\'re all set!'));
687
- console.log('');
688
- console.log(chalk.dim(' Quick commands:'));
689
- console.log(` ${chalk.cyan('buoy show drift')} See all drift issues`);
690
- console.log(` ${chalk.cyan('buoy show health')} Quick health overview`);
691
- console.log(` ${chalk.cyan('buoy drift check')} Pre-commit validation`);
692
- console.log('');
693
- console.log(chalk.dim(` Run ${chalk.cyan('buoy begin')} anytime to return here.`));
694
- console.log('');
695
- }
696
- /**
697
- * Run the wizard in AI-guided mode - scans project and outputs everything
698
- * the AI needs to walk the user through setup conversationally.
699
- */
700
- async function runAIGuidedWizard(cwd) {
701
- console.log(`
702
- 🛟 Buoy - Design Drift Detection
703
-
704
- Analyzing your project...
705
- `);
706
- try {
707
- // Detect project state
708
- const tokenResult = await detectTokens(cwd);
709
- const hasAISetup = detectAISetup(cwd);
710
- const hasCISetup = existsSync(join(cwd, '.github', 'workflows', 'buoy.yml')) ||
711
- existsSync(join(cwd, '.gitlab-ci.yml'));
712
- const existingConfig = getConfigPath();
713
- // Load config
714
- let config;
715
- if (existingConfig) {
716
- const result = await loadConfig();
717
- config = result.config;
718
- }
719
- else {
720
- const autoResult = await buildAutoConfig(cwd);
721
- config = autoResult.config;
722
- }
723
- // Run scan
724
- const orchestrator = new ScanOrchestrator(config);
725
- const { components } = await orchestrator.scanComponents({});
726
- // Detect frameworks
727
- const detector = new ProjectDetector(cwd);
728
- const projectInfo = await detector.detect();
729
- // Run drift analysis
730
- const { SemanticDiffEngine } = await import('@buoy-design/core/analysis');
731
- const engine = new SemanticDiffEngine();
732
- const diffResult = engine.analyzeComponents(components, {
733
- checkDeprecated: true,
734
- checkNaming: true,
735
- checkDocumentation: true,
736
- });
737
- const drifts = [...diffResult.drifts];
738
- // Output project state
739
- console.log('─'.repeat(50));
740
- console.log('PROJECT STATE');
741
- console.log('─'.repeat(50));
742
- console.log('');
743
- // Framework
744
- if (projectInfo.frameworks.length > 0) {
745
- console.log(`Framework: ${projectInfo.frameworks.map(f => f.name).join(', ')}`);
746
- }
747
- console.log(`Components: ${components.length} found`);
748
- // Tokens
749
- if (tokenResult.hasTokens) {
750
- console.log(`Design tokens: ✓ Found (${tokenResult.files.slice(0, 2).join(', ')}${tokenResult.files.length > 2 ? '...' : ''})`);
751
- }
752
- else {
753
- console.log('Design tokens: ✗ Not found');
754
- }
755
- // AI Setup
756
- console.log(`AI guardrails: ${hasAISetup ? '✓ Configured' : '✗ Not set up'}`);
757
- // CI Setup
758
- console.log(`CI/CD: ${hasCISetup ? '✓ Active' : '○ Not configured'}`);
759
- // Drift
760
- console.log('');
761
- if (drifts.length > 0) {
762
- const critical = drifts.filter(d => d.severity === 'critical').length;
763
- const warning = drifts.filter(d => d.severity === 'warning').length;
764
- console.log(`Drift: ${drifts.length} issue${drifts.length === 1 ? '' : 's'}`);
765
- if (critical > 0)
766
- console.log(` • ${critical} critical`);
767
- if (warning > 0)
768
- console.log(` • ${warning} warnings`);
769
- }
770
- else {
771
- console.log('Drift: None detected');
772
- }
773
- // Smart recommendations based on state
774
- console.log('');
775
- console.log('─'.repeat(50));
776
- console.log('RECOMMENDED ACTION');
777
- console.log('─'.repeat(50));
778
- console.log('');
779
- if (!tokenResult.hasTokens) {
780
- // No tokens - recommend scanning to find values
781
- console.log('This project has no design tokens.');
782
- console.log('');
783
- console.log('PRIMARY: Scan your codebase');
784
- console.log(' → Run: buoy drift scan');
785
- console.log(' This finds components and design values in your code.');
786
- console.log('');
787
- console.log('ALTERNATIVE: Check for drift without tokens');
788
- console.log(' → Run: buoy show drift');
789
- console.log(' Find hardcoded values that should be tokens.');
790
- }
791
- else if (!hasAISetup) {
792
- // Has tokens but no AI setup - recommend dock agents
793
- console.log('This project has tokens but AI tools aren\'t configured to use them.');
794
- console.log('');
795
- console.log('PRIMARY: Set up AI integration');
796
- console.log(' → Run: buoy dock agents');
797
- console.log(' This creates skill files and updates CLAUDE.md so AI follows your tokens.');
798
- console.log('');
799
- console.log('ALTERNATIVE: Check for drift');
800
- console.log(' → Run: buoy show drift');
801
- console.log(' See if code follows your design system.');
802
- }
803
- else {
804
- // Has tokens + AI - recommend drift check
805
- console.log('This project is set up! AI tools know about your design system.');
806
- console.log('');
807
- console.log('PRIMARY: Check for drift');
808
- console.log(' → Run: buoy show drift');
809
- console.log(' Find code that diverges from your design system.');
810
- console.log('');
811
- console.log('OTHER OPTIONS:');
812
- console.log(' • buoy dock hooks Interactive setup for Claude or git hooks');
813
- console.log(' • buoy dock hooks --claude Claude hooks (design system in every session)');
814
- console.log(' • buoy dock hooks --commit Git pre-commit hooks (catch drift before commit)');
815
- console.log(' • buoy ahoy github GitHub PR bot (comments on drift)');
816
- }
817
- console.log('');
818
- console.log('─'.repeat(50));
819
- }
820
- catch (err) {
821
- const message = err instanceof Error ? err.message : String(err);
822
- console.log(`Analysis failed: ${message}`);
823
- console.log('');
824
- console.log('Try running: buoy show all');
825
- }
826
- }
827
- //# sourceMappingURL=begin.js.map