@buoy-design/cli 0.3.33 → 0.3.35

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 (93) hide show
  1. package/dist/commands/audit.d.ts +3 -0
  2. package/dist/commands/audit.d.ts.map +1 -0
  3. package/dist/commands/audit.js +235 -0
  4. package/dist/commands/audit.js.map +1 -0
  5. package/dist/commands/baseline.d.ts +47 -0
  6. package/dist/commands/baseline.d.ts.map +1 -0
  7. package/dist/commands/baseline.js +327 -0
  8. package/dist/commands/baseline.js.map +1 -0
  9. package/dist/commands/begin.d.ts +9 -0
  10. package/dist/commands/begin.d.ts.map +1 -0
  11. package/dist/commands/begin.js +827 -0
  12. package/dist/commands/begin.js.map +1 -0
  13. package/dist/commands/build.d.ts +8 -0
  14. package/dist/commands/build.d.ts.map +1 -0
  15. package/dist/commands/build.js +26 -0
  16. package/dist/commands/build.js.map +1 -0
  17. package/dist/commands/check.d.ts.sync-conflict-20260305-170128-6PCZ3ZU.map +1 -0
  18. package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.d.ts +26 -0
  19. package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.d.ts.map +1 -0
  20. package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.js +438 -0
  21. package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.js.map +1 -0
  22. package/dist/commands/commands.d.ts +8 -0
  23. package/dist/commands/commands.d.ts.map +1 -0
  24. package/dist/commands/commands.js +148 -0
  25. package/dist/commands/commands.js.map +1 -0
  26. package/dist/commands/components-query.d.ts +13 -0
  27. package/dist/commands/components-query.d.ts.map +1 -0
  28. package/dist/commands/components-query.js +263 -0
  29. package/dist/commands/components-query.js.map +1 -0
  30. package/dist/commands/components.d.ts +8 -0
  31. package/dist/commands/components.d.ts.map +1 -0
  32. package/dist/commands/components.js +14 -0
  33. package/dist/commands/components.js.map +1 -0
  34. package/dist/commands/dock.sync-conflict-20260309-191923-6PCZ3ZU.js +1006 -0
  35. package/dist/commands/history.d.ts +3 -0
  36. package/dist/commands/history.d.ts.map +1 -0
  37. package/dist/commands/history.js +352 -0
  38. package/dist/commands/history.js.map +1 -0
  39. package/dist/commands/plugins.d.ts +3 -0
  40. package/dist/commands/plugins.d.ts.map +1 -0
  41. package/dist/commands/plugins.js +63 -0
  42. package/dist/commands/plugins.js.map +1 -0
  43. package/dist/commands/show.d.ts.sync-conflict-20260306-165917-6PCZ3ZU.map +1 -0
  44. package/dist/commands/show.sync-conflict-20260305-140755-6PCZ3ZU.js +1735 -0
  45. package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.d.ts +11 -0
  46. package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.d.ts.map +1 -0
  47. package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.js +1735 -0
  48. package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.js.map +1 -0
  49. package/dist/config/loader.js +1 -1
  50. package/dist/config/loader.js.map +1 -1
  51. package/dist/config/loader.js.sync-conflict-20260309-033512-6PCZ3ZU.map +1 -0
  52. package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.d.ts +8 -0
  53. package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.d.ts.map +1 -0
  54. package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.js +162 -0
  55. package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.js.map +1 -0
  56. package/dist/config/schema.d.ts.sync-conflict-20260309-154654-6PCZ3ZU.map +1 -0
  57. package/dist/config/schema.sync-conflict-20260309-135703-6PCZ3ZU.js +214 -0
  58. package/dist/detect/frameworks.js.sync-conflict-20260306-123756-6PCZ3ZU.map +1 -0
  59. package/dist/detect/monorepo-patterns.js.sync-conflict-20260309-155400-6PCZ3ZU.map +1 -0
  60. package/dist/hooks/index.d.ts.sync-conflict-20260306-220901-6PCZ3ZU.map +1 -0
  61. package/dist/output/formatters.js.sync-conflict-20260306-134702-6PCZ3ZU.map +1 -0
  62. package/dist/output/formatters.sync-conflict-20260306-180804-6PCZ3ZU.js +867 -0
  63. package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.d.ts +29 -0
  64. package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.d.ts.map +1 -0
  65. package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.js +867 -0
  66. package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.js.map +1 -0
  67. package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.d.ts +29 -0
  68. package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.d.ts.map +1 -0
  69. package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.js +867 -0
  70. package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.js.map +1 -0
  71. package/dist/output/index.sync-conflict-20260309-222859-6PCZ3ZU.js +5 -0
  72. package/dist/output/reporters.d.sync-conflict-20260309-193820-6PCZ3ZU.ts +38 -0
  73. package/dist/output/reporters.d.ts.sync-conflict-20260306-193811-6PCZ3ZU.map +1 -0
  74. package/dist/output/reporters.sync-conflict-20260309-030558-6PCZ3ZU.js +182 -0
  75. package/dist/output/reports.d.ts.sync-conflict-20260307-172149-6PCZ3ZU.map +1 -0
  76. package/dist/output/reports.js.sync-conflict-20260305-161643-6PCZ3ZU.map +1 -0
  77. package/dist/output/reports.sync-conflict-20260305-211951-6PCZ3ZU.js +393 -0
  78. package/dist/output/visuals.d.ts +53 -0
  79. package/dist/output/visuals.d.ts.map +1 -0
  80. package/dist/output/visuals.js +194 -0
  81. package/dist/output/visuals.js.map +1 -0
  82. package/dist/services/drift-analysis.d.sync-conflict-20260306-151016-6PCZ3ZU.ts +194 -0
  83. package/dist/services/drift-analysis.d.ts.sync-conflict-20260307-175904-6PCZ3ZU.map +1 -0
  84. package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.d.ts +194 -0
  85. package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.d.ts.map +1 -0
  86. package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.js +1022 -0
  87. package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.js.map +1 -0
  88. package/dist/services/skill-export.d.ts.sync-conflict-20260309-171021-6PCZ3ZU.map +1 -0
  89. package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.d.ts +109 -0
  90. package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.d.ts.map +1 -0
  91. package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.js +737 -0
  92. package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.js.map +1 -0
  93. package/package.json +3 -3
@@ -0,0 +1,1006 @@
1
+ /**
2
+ * buoy dock - Dock tools into your project
3
+ *
4
+ * Smart walkthrough that sets up config, agents, hooks, tokens, and graph.
5
+ *
6
+ * Usage:
7
+ * buoy dock # Smart walkthrough: config → agents → hooks
8
+ * buoy dock config # Just create .buoy.yaml
9
+ * buoy dock agents # Set up AI agents (skills + context)
10
+ * buoy dock skills # Just create skill files
11
+ * buoy dock context # Just generate CLAUDE.md section
12
+ * buoy dock hooks # Just set up git hooks
13
+ * buoy dock tokens # Generate/export design tokens
14
+ * buoy dock graph # Build design system knowledge graph
15
+ */
16
+ import { Command } from "commander";
17
+ import { writeFileSync, existsSync, readFileSync, mkdirSync, appendFileSync, readdirSync, copyFileSync, } from "fs";
18
+ import { resolve, dirname, join } from "path";
19
+ import { homedir } from "os";
20
+ import { fileURLToPath } from "url";
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = dirname(__filename);
23
+ import chalk from "chalk";
24
+ import { stringify as stringifyYaml } from "yaml";
25
+ import { createInterface } from "readline";
26
+ import ora from "ora";
27
+ import { success, error, info, warning, setJsonMode, spinner, } from "../output/reporters.js";
28
+ import { loadConfig, getConfigPath } from "../config/loader.js";
29
+ import { buildAutoConfig, findTokenFiles } from "../config/auto-detect.js";
30
+ import { ScanOrchestrator } from "../scan/orchestrator.js";
31
+ import { SkillExportService } from "../services/skill-export.js";
32
+ import { generateContext } from "../services/context-generator.js";
33
+ import { setupHooks, generateStandaloneHook, detectHookSystem, setupClaudeHooks, } from "../hooks/index.js";
34
+ import { ProjectDetector, detectMonorepoConfig, expandPatternsForMonorepo, } from "../detect/index.js";
35
+ import { detectFrameworks, getPluginInstallCommand, BUILTIN_SCANNERS, PLUGIN_INFO, } from "../detect/frameworks.js";
36
+ import { createTokensCommand } from "./tokens.js";
37
+ import { createCompareCommand } from "./compare.js";
38
+ import { createImportCommand } from "./import.js";
39
+ import { createGraphCommand } from "./graph.js";
40
+ import { createLearnCommand } from "./learn.js";
41
+ import { showMenu } from "../wizard/menu.js";
42
+ import { generateAgents, generateCommands } from "../templates/agents.js";
43
+ export function createDockCommand() {
44
+ const cmd = new Command("dock")
45
+ .description("Dock tools into your project")
46
+ .option("-y, --yes", "Auto-accept all defaults")
47
+ .option("--json", "Output results as JSON")
48
+ .option("-f, --force", "Overwrite existing configuration")
49
+ .action(async (options) => {
50
+ await runSmartDock(options);
51
+ });
52
+ // dock config - Just create config
53
+ cmd
54
+ .command("config")
55
+ .description("Create .buoy.yaml configuration file")
56
+ .option("-f, --force", "Overwrite existing configuration")
57
+ .option("-y, --yes", "Auto-install recommended plugins without prompting")
58
+ .option("--skip-detect", "Skip auto-detection and create minimal config")
59
+ .action(async (options) => {
60
+ await runConfigDock(options);
61
+ });
62
+ // dock agents - Full AI agent onboarding
63
+ cmd
64
+ .command("agents")
65
+ .description("Set up AI agents with your design system")
66
+ .option("--dry-run", "Show what would be created without writing files")
67
+ .option("--json", "Output result as JSON")
68
+ .action(async (options) => {
69
+ await runAgentsDock(options);
70
+ });
71
+ // dock skills - Create skill files
72
+ cmd
73
+ .command("skills")
74
+ .description("Create skill files for AI agents")
75
+ .option("-o, --output <path>", "Output directory", ".claude/skills/design-system")
76
+ .option("--global", "Export to global skills directory (~/.claude/skills/)")
77
+ .option("--dry-run", "Show what would be created without writing files")
78
+ .option("--json", "Output result as JSON")
79
+ .action(async (options) => {
80
+ await runSkillsDock(options);
81
+ });
82
+ // dock context - Generate CLAUDE.md section
83
+ cmd
84
+ .command("context")
85
+ .description("Generate design system context for CLAUDE.md")
86
+ .option("-o, --output <path>", "Output file path (default: CLAUDE.md)")
87
+ .option("--stdout", "Output to stdout instead of file")
88
+ .option("-d, --detail <level>", "Detail level: minimal, standard, comprehensive", "standard")
89
+ .option("--json", "Output as JSON with stats")
90
+ .action(async (options) => {
91
+ await runContextDock(options);
92
+ });
93
+ // dock hooks - Set up hooks (interactive by default)
94
+ cmd
95
+ .command("hooks")
96
+ .description("Set up hooks for drift checking")
97
+ .option("--commit", "Set up git pre-commit hooks")
98
+ .option("--claude", "Set up Claude Code hooks (design system in every session)")
99
+ .action(async (options) => {
100
+ await runHooksDock(options);
101
+ });
102
+ // dock commands - Install global Claude Code slash commands
103
+ const commandsCmd = cmd
104
+ .command("commands")
105
+ .description("Manage Claude Code slash commands");
106
+ commandsCmd
107
+ .command("list")
108
+ .description("List available Buoy slash commands")
109
+ .option("--json", "Output as JSON")
110
+ .action((options) => {
111
+ if (options.json)
112
+ setJsonMode(true);
113
+ const available = listAvailableSlashCommands();
114
+ const commandsDir = join(homedir(), ".claude", "commands");
115
+ if (options.json) {
116
+ const result = available.map(name => ({
117
+ name,
118
+ installed: existsSync(join(commandsDir, `${name}.md`)),
119
+ }));
120
+ console.log(JSON.stringify(result, null, 2));
121
+ return;
122
+ }
123
+ console.log("");
124
+ console.log(chalk.cyan.bold(" Available Buoy Commands"));
125
+ console.log("");
126
+ if (available.length === 0) {
127
+ console.log(chalk.dim(" No commands available"));
128
+ return;
129
+ }
130
+ for (const name of available) {
131
+ const isInstalled = existsSync(join(commandsDir, `${name}.md`));
132
+ const status = isInstalled
133
+ ? chalk.green("✓ installed")
134
+ : chalk.dim("not installed");
135
+ console.log(` /${name} ${status}`);
136
+ }
137
+ console.log("");
138
+ console.log(chalk.dim(" Run `buoy dock commands install` to install all commands"));
139
+ console.log("");
140
+ });
141
+ commandsCmd
142
+ .command("install")
143
+ .description("Install Buoy slash commands to ~/.claude/commands/")
144
+ .option("--dry-run", "Show what would be installed")
145
+ .option("--json", "Output as JSON")
146
+ .action((options) => {
147
+ if (options.json)
148
+ setJsonMode(true);
149
+ const result = installGlobalSlashCommands(options.dryRun);
150
+ if (options.json) {
151
+ console.log(JSON.stringify(result, null, 2));
152
+ return;
153
+ }
154
+ console.log("");
155
+ if (options.dryRun) {
156
+ console.log(chalk.cyan.bold(" Dry Run - Would install:"));
157
+ console.log("");
158
+ for (const name of result.installed) {
159
+ console.log(` ${chalk.green("+")} /${name}`);
160
+ }
161
+ for (const name of result.alreadyExisted) {
162
+ console.log(` ${chalk.dim("○")} /${name} (already exists)`);
163
+ }
164
+ }
165
+ else {
166
+ if (result.installed.length > 0) {
167
+ console.log(chalk.green.bold(" ✓ Installed slash commands:"));
168
+ console.log("");
169
+ for (const name of result.installed) {
170
+ console.log(` /${name}`);
171
+ }
172
+ }
173
+ if (result.alreadyExisted.length > 0) {
174
+ console.log("");
175
+ console.log(chalk.dim(` Already installed: ${result.alreadyExisted.map(c => `/${c}`).join(", ")}`));
176
+ }
177
+ if (result.installed.length === 0 && result.alreadyExisted.length > 0) {
178
+ console.log(chalk.dim(" All commands already installed"));
179
+ }
180
+ }
181
+ console.log("");
182
+ console.log(chalk.dim(" Commands installed to: ~/.claude/commands/"));
183
+ console.log(chalk.dim(" Restart Claude Code to use them"));
184
+ console.log("");
185
+ });
186
+ // dock plugins - Show and suggest scanners/plugins
187
+ const pluginsCmd = cmd
188
+ .command("plugins")
189
+ .description("Show available scanners and plugins");
190
+ pluginsCmd
191
+ .command("list")
192
+ .description("List available scanners and plugins")
193
+ .action(async () => {
194
+ console.log(chalk.bold('\nBuilt-in Scanners') + chalk.dim(' (always available)'));
195
+ console.log('');
196
+ for (const [_key, scannerInfo] of Object.entries(BUILTIN_SCANNERS)) {
197
+ console.log(` ${chalk.green('✓')} ${chalk.cyan(scannerInfo.description)}`);
198
+ console.log(` ${chalk.dim(`Detects: ${scannerInfo.detects}`)}`);
199
+ console.log();
200
+ }
201
+ console.log(chalk.bold('Optional Plugins'));
202
+ console.log('');
203
+ for (const [_key, pluginInfo] of Object.entries(PLUGIN_INFO)) {
204
+ console.log(` ${chalk.dim('○')} ${chalk.cyan(pluginInfo.name)}`);
205
+ console.log(` ${chalk.dim(pluginInfo.description)}`);
206
+ console.log();
207
+ }
208
+ });
209
+ pluginsCmd
210
+ .command("suggest")
211
+ .description("Suggest plugins based on detected frameworks")
212
+ .action(async () => {
213
+ try {
214
+ const detected = await detectFrameworks(process.cwd());
215
+ if (detected.length === 0) {
216
+ console.log(chalk.yellow('No frameworks detected.'));
217
+ return;
218
+ }
219
+ const builtIn = detected.filter(fw => fw.scanner);
220
+ const needsPlugin = detected.filter(fw => fw.plugin && !fw.scanner);
221
+ if (builtIn.length > 0) {
222
+ console.log(chalk.bold('\nDetected (built-in support):'));
223
+ for (const fw of builtIn) {
224
+ console.log(` ${chalk.green('✓')} ${fw.name} ${chalk.dim(`- ${fw.evidence}`)}`);
225
+ }
226
+ }
227
+ if (needsPlugin.length > 0) {
228
+ console.log(chalk.bold('\nDetected (optional plugin available):'));
229
+ for (const fw of needsPlugin) {
230
+ console.log(` ${chalk.yellow('○')} ${fw.name} ${chalk.dim(`- ${fw.evidence}`)}`);
231
+ }
232
+ const pluginNames = needsPlugin
233
+ .map(fw => fw.plugin)
234
+ .filter((p, i, arr) => arr.indexOf(p) === i);
235
+ console.log('\n' + chalk.bold('Install optional plugins:'));
236
+ console.log(` ${chalk.cyan(getPluginInstallCommand(pluginNames))}`);
237
+ }
238
+ }
239
+ catch (err) {
240
+ console.error(chalk.red(`Failed to detect frameworks: ${err instanceof Error ? err.message : String(err)}`));
241
+ process.exit(1);
242
+ }
243
+ });
244
+ // dock tokens (with compare and import as subcommands)
245
+ const tokensCmd = createTokensCommand();
246
+ tokensCmd.addCommand(createCompareCommand());
247
+ tokensCmd.addCommand(createImportCommand());
248
+ cmd.addCommand(tokensCmd);
249
+ // dock graph (with learn as subcommand)
250
+ const graphCmd = createGraphCommand();
251
+ graphCmd.addCommand(createLearnCommand());
252
+ cmd.addCommand(graphCmd);
253
+ return cmd;
254
+ }
255
+ function listAvailableSlashCommands() {
256
+ const assetsDir = resolve(__dirname, "..", "..", "assets", "commands");
257
+ if (!existsSync(assetsDir))
258
+ return [];
259
+ return readdirSync(assetsDir)
260
+ .filter(f => f.endsWith(".md"))
261
+ .map(f => f.replace(".md", ""));
262
+ }
263
+ function installGlobalSlashCommands(dryRun = false) {
264
+ const commandsDir = join(homedir(), ".claude", "commands");
265
+ const assetsDir = resolve(__dirname, "..", "..", "assets", "commands");
266
+ const installed = [];
267
+ const alreadyExisted = [];
268
+ if (!existsSync(assetsDir))
269
+ return { installed, alreadyExisted };
270
+ if (!dryRun && !existsSync(commandsDir)) {
271
+ mkdirSync(commandsDir, { recursive: true });
272
+ }
273
+ const commandFiles = readdirSync(assetsDir).filter(f => f.endsWith(".md"));
274
+ for (const file of commandFiles) {
275
+ const srcPath = join(assetsDir, file);
276
+ const destPath = join(commandsDir, file);
277
+ if (existsSync(destPath)) {
278
+ alreadyExisted.push(file.replace(".md", ""));
279
+ }
280
+ else {
281
+ if (!dryRun) {
282
+ copyFileSync(srcPath, destPath);
283
+ }
284
+ installed.push(file.replace(".md", ""));
285
+ }
286
+ }
287
+ return { installed, alreadyExisted };
288
+ }
289
+ /**
290
+ * Smart dock walkthrough - goes through config → agents → hooks
291
+ */
292
+ async function runSmartDock(options) {
293
+ const cwd = process.cwd();
294
+ if (options.json) {
295
+ setJsonMode(true);
296
+ }
297
+ console.log("");
298
+ console.log(chalk.cyan.bold(" Docking Buoy..."));
299
+ console.log(chalk.dim(" ─".repeat(25)));
300
+ console.log("");
301
+ const results = {
302
+ configCreated: false,
303
+ agentsSetup: false,
304
+ hooksSetup: false,
305
+ };
306
+ // Step 1: Check/create config
307
+ const configPath = getConfigPath();
308
+ if (!configPath) {
309
+ console.log(chalk.yellow(" ⚠ No .buoy.yaml found"));
310
+ console.log("");
311
+ const shouldCreateConfig = options.yes || (await promptConfirm(" Create config now?", true));
312
+ if (shouldCreateConfig) {
313
+ console.log("");
314
+ await runConfigDock({ yes: options.yes });
315
+ results.configCreated = true;
316
+ }
317
+ else {
318
+ console.log("");
319
+ info("Run `buoy dock config` later to create config.");
320
+ console.log("");
321
+ return;
322
+ }
323
+ }
324
+ else {
325
+ results.configCreated = true;
326
+ console.log(` ${chalk.green("✓")} Config exists`);
327
+ }
328
+ // Step 2: Check/setup AI agents
329
+ const skillsDir = resolve(cwd, ".claude/skills/design-system");
330
+ const claudeMdPath = join(cwd, "CLAUDE.md");
331
+ const hasSkills = existsSync(skillsDir);
332
+ const hasClaudeMd = existsSync(claudeMdPath) &&
333
+ readFileSync(claudeMdPath, "utf-8").includes("Design System");
334
+ if (!hasSkills || !hasClaudeMd) {
335
+ console.log("");
336
+ console.log(chalk.yellow(" ⚠ AI agents not configured"));
337
+ if (!hasSkills)
338
+ console.log(chalk.dim(" • Missing skill files"));
339
+ if (!hasClaudeMd)
340
+ console.log(chalk.dim(" • Missing CLAUDE.md section"));
341
+ console.log("");
342
+ const shouldSetupAgents = options.yes || (await promptConfirm(" Set up AI agents?", true));
343
+ if (shouldSetupAgents) {
344
+ console.log("");
345
+ await runAgentsDock({ json: options.json });
346
+ results.agentsSetup = true;
347
+ }
348
+ }
349
+ else {
350
+ console.log(` ${chalk.green("✓")} AI agents configured`);
351
+ results.agentsSetup = true;
352
+ }
353
+ // Step 3: Check/setup hooks
354
+ const hasHooks = existsSync(join(cwd, ".husky/pre-commit")) ||
355
+ existsSync(join(cwd, "lefthook.yml")) ||
356
+ existsSync(join(cwd, ".git/hooks/pre-commit"));
357
+ if (!hasHooks) {
358
+ console.log("");
359
+ console.log(chalk.yellow(" ⚠ No pre-commit hooks"));
360
+ console.log("");
361
+ const shouldSetupHooks = options.yes || (await promptConfirm(" Set up hooks?", true));
362
+ if (shouldSetupHooks) {
363
+ await runHooksDock({});
364
+ results.hooksSetup = true;
365
+ }
366
+ }
367
+ else {
368
+ console.log(` ${chalk.green("✓")} Hooks configured`);
369
+ results.hooksSetup = true;
370
+ }
371
+ // Summary
372
+ console.log("");
373
+ console.log(chalk.dim(" ─".repeat(25)));
374
+ console.log("");
375
+ if (options.json) {
376
+ console.log(JSON.stringify(results, null, 2));
377
+ }
378
+ else {
379
+ const allDone = results.configCreated && results.agentsSetup && results.hooksSetup;
380
+ if (allDone) {
381
+ console.log(chalk.green.bold(" ✓ Buoy is docked!"));
382
+ console.log("");
383
+ console.log(chalk.dim(" Try:"));
384
+ console.log(` ${chalk.cyan("buoy show all")} # See your design system`);
385
+ console.log(` ${chalk.cyan("buoy show drift")} # Check for drift`);
386
+ }
387
+ else {
388
+ console.log(chalk.yellow(" Partially docked"));
389
+ console.log("");
390
+ if (!results.configCreated)
391
+ console.log(` Run ${chalk.cyan("buoy dock config")}`);
392
+ if (!results.agentsSetup)
393
+ console.log(` Run ${chalk.cyan("buoy dock agents")}`);
394
+ if (!results.hooksSetup)
395
+ console.log(` Run ${chalk.cyan("buoy dock hooks")}`);
396
+ }
397
+ }
398
+ console.log("");
399
+ }
400
+ /**
401
+ * Config dock - creates .buoy.yaml
402
+ */
403
+ async function runConfigDock(options) {
404
+ const cwd = process.cwd();
405
+ const configFilePath = resolve(cwd, ".buoy.yaml");
406
+ if (existsSync(configFilePath) && !options.force) {
407
+ warning(`Config already exists at ${configFilePath}`);
408
+ info("Use --force to overwrite");
409
+ return;
410
+ }
411
+ let project;
412
+ if (options.skipDetect) {
413
+ const detector = new ProjectDetector(cwd);
414
+ project = {
415
+ name: (await detector.detect()).name,
416
+ root: cwd,
417
+ frameworks: [],
418
+ primaryFramework: null,
419
+ components: [],
420
+ tokens: [],
421
+ storybook: null,
422
+ designSystem: null,
423
+ monorepo: null,
424
+ designSystemDocs: null,
425
+ };
426
+ }
427
+ else {
428
+ const spin = ora("Scanning project...").start();
429
+ try {
430
+ const detector = new ProjectDetector(cwd);
431
+ project = await detector.detect();
432
+ // Also scan for token files using auto-detect patterns
433
+ // This catches files like theme.css, variables.css that ProjectDetector may miss
434
+ const tokenFiles = await findTokenFiles(cwd);
435
+ if (tokenFiles.length > 0) {
436
+ // Get existing token paths for deduplication
437
+ const existingPaths = new Set(project.tokens.map((t) => t.path));
438
+ // Add any new token files not already detected
439
+ for (const tokenPath of tokenFiles) {
440
+ if (!existingPaths.has(tokenPath)) {
441
+ project.tokens.push({
442
+ path: tokenPath,
443
+ type: tokenPath.endsWith(".json") ? "json" : "css",
444
+ name: `Token file: ${tokenPath}`,
445
+ });
446
+ }
447
+ }
448
+ }
449
+ spin.stop();
450
+ printDetectionResults(project);
451
+ }
452
+ catch (err) {
453
+ spin.fail("Detection failed");
454
+ error(err instanceof Error ? err.message : String(err));
455
+ process.exit(1);
456
+ }
457
+ }
458
+ // Show frameworks and offer plugin install
459
+ const detectedFrameworks = await detectFrameworks(cwd);
460
+ if (detectedFrameworks.length > 0) {
461
+ await showFrameworksAndPlugins(detectedFrameworks, options.yes);
462
+ }
463
+ // Generate and write config
464
+ const content = generateConfig(project);
465
+ try {
466
+ writeFileSync(configFilePath, content, "utf-8");
467
+ success("Created .buoy.yaml");
468
+ }
469
+ catch (err) {
470
+ error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
471
+ process.exit(1);
472
+ }
473
+ }
474
+ /**
475
+ * Agents dock - sets up AI agents (skills + context + commands)
476
+ */
477
+ async function runAgentsDock(options) {
478
+ const cwd = process.cwd();
479
+ if (options.json)
480
+ setJsonMode(true);
481
+ const spin = spinner("Setting up AI agents...");
482
+ try {
483
+ const { config, projectName } = await loadOrBuildConfig(cwd);
484
+ spin.text = "Scanning...";
485
+ const orchestrator = new ScanOrchestrator(config, cwd);
486
+ const scanResult = await orchestrator.scan({
487
+ onProgress: (msg) => {
488
+ spin.text = msg;
489
+ },
490
+ });
491
+ spin.text = "Analyzing...";
492
+ const { SemanticDiffEngine } = await import("@buoy-design/core/analysis");
493
+ const engine = new SemanticDiffEngine();
494
+ const diffResult = engine.analyzeComponents(scanResult.components, {
495
+ checkDeprecated: true,
496
+ checkNaming: true,
497
+ checkDocumentation: true,
498
+ });
499
+ spin.stop();
500
+ const results = {
501
+ agentsCreated: [],
502
+ commandsCreated: [],
503
+ skillCreated: false,
504
+ contextUpdated: false,
505
+ stats: { tokens: 0, components: 0 },
506
+ };
507
+ // Create skill files
508
+ const skillPath = resolve(cwd, ".claude/skills/design-system");
509
+ if (options.dryRun) {
510
+ console.log(chalk.dim(" Would create: " + skillPath));
511
+ }
512
+ else {
513
+ const exportService = new SkillExportService(projectName);
514
+ const skillResult = await exportService.export({
515
+ tokens: scanResult.tokens,
516
+ components: scanResult.components,
517
+ drifts: diffResult.drifts,
518
+ projectName,
519
+ }, {
520
+ sections: ["tokens", "components", "patterns", "anti-patterns"],
521
+ outputPath: skillPath,
522
+ });
523
+ for (const file of skillResult.files) {
524
+ const dir = dirname(file.path);
525
+ if (!existsSync(dir))
526
+ mkdirSync(dir, { recursive: true });
527
+ writeFileSync(file.path, file.content);
528
+ }
529
+ results.skillCreated = true;
530
+ results.stats.tokens = skillResult.stats.tokens.total;
531
+ results.stats.components = skillResult.stats.components;
532
+ }
533
+ // Create agents
534
+ const agentsDir = resolve(cwd, ".claude/agents");
535
+ const agents = generateAgents(projectName);
536
+ if (options.dryRun) {
537
+ console.log(chalk.dim(" Would create: .claude/agents/"));
538
+ for (const agent of agents) {
539
+ console.log(chalk.dim(` - ${agent.filename}`));
540
+ }
541
+ }
542
+ else {
543
+ if (!existsSync(agentsDir))
544
+ mkdirSync(agentsDir, { recursive: true });
545
+ for (const agent of agents) {
546
+ writeFileSync(join(agentsDir, agent.filename), agent.content);
547
+ results.agentsCreated.push(agent.filename.replace(".md", ""));
548
+ }
549
+ }
550
+ // Create commands
551
+ const commandsDir = resolve(cwd, ".claude/commands");
552
+ const commands = generateCommands(projectName);
553
+ if (options.dryRun) {
554
+ console.log(chalk.dim(" Would create: .claude/commands/"));
555
+ for (const cmd of commands) {
556
+ console.log(chalk.dim(` - ${cmd.filename}`));
557
+ }
558
+ }
559
+ else {
560
+ if (!existsSync(commandsDir))
561
+ mkdirSync(commandsDir, { recursive: true });
562
+ for (const cmd of commands) {
563
+ writeFileSync(join(commandsDir, cmd.filename), cmd.content);
564
+ results.commandsCreated.push(cmd.filename.replace(".md", ""));
565
+ }
566
+ }
567
+ // Update CLAUDE.md
568
+ const claudeMdPath = join(cwd, "CLAUDE.md");
569
+ if (options.dryRun) {
570
+ console.log(chalk.dim(" Would update: CLAUDE.md"));
571
+ }
572
+ else {
573
+ const contextResult = generateContext({
574
+ tokens: scanResult.tokens,
575
+ components: scanResult.components,
576
+ drifts: diffResult.drifts,
577
+ projectName,
578
+ }, { detailLevel: "standard" });
579
+ let existingContent = "";
580
+ let hasSection = false;
581
+ if (existsSync(claudeMdPath)) {
582
+ existingContent = readFileSync(claudeMdPath, "utf-8");
583
+ hasSection = /^##?\s*[Dd]esign\s*[Ss]ystem/m.test(existingContent.replace(/```[\s\S]*?```/g, ""));
584
+ }
585
+ if (!hasSection) {
586
+ const section = `\n## Design System\n\n${contextResult.content}\n`;
587
+ if (existingContent) {
588
+ appendFileSync(claudeMdPath, section);
589
+ }
590
+ else {
591
+ writeFileSync(claudeMdPath, `# CLAUDE.md\n${section}`);
592
+ }
593
+ results.contextUpdated = true;
594
+ }
595
+ }
596
+ if (options.json) {
597
+ console.log(JSON.stringify(results, null, 2));
598
+ }
599
+ else if (!options.dryRun) {
600
+ console.log("");
601
+ if (results.agentsCreated.length > 0) {
602
+ console.log(` ${chalk.green("✓")} Created agents: ${results.agentsCreated.join(", ")}`);
603
+ }
604
+ if (results.commandsCreated.length > 0) {
605
+ console.log(` ${chalk.green("✓")} Created commands: ${results.commandsCreated.map((c) => `/${c}`).join(", ")}`);
606
+ }
607
+ if (results.skillCreated)
608
+ console.log(` ${chalk.green("✓")} Created skill files`);
609
+ if (results.contextUpdated)
610
+ console.log(` ${chalk.green("✓")} Updated CLAUDE.md`);
611
+ console.log("");
612
+ }
613
+ }
614
+ catch (err) {
615
+ spin.stop();
616
+ error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
617
+ process.exit(1);
618
+ }
619
+ }
620
+ /**
621
+ * Skills dock - creates skill files only
622
+ */
623
+ async function runSkillsDock(options) {
624
+ const cwd = process.cwd();
625
+ if (options.json)
626
+ setJsonMode(true);
627
+ const spin = spinner("Creating skills...");
628
+ try {
629
+ const { config, projectName } = await loadOrBuildConfig(cwd);
630
+ spin.text = "Scanning...";
631
+ const orchestrator = new ScanOrchestrator(config, cwd);
632
+ const scanResult = await orchestrator.scan({
633
+ onProgress: (msg) => {
634
+ spin.text = msg;
635
+ },
636
+ });
637
+ spin.text = "Analyzing...";
638
+ const { SemanticDiffEngine } = await import("@buoy-design/core/analysis");
639
+ const engine = new SemanticDiffEngine();
640
+ const diffResult = engine.analyzeComponents(scanResult.components, {});
641
+ spin.stop();
642
+ let outputPath = options.output || ".claude/skills/design-system";
643
+ if (options.global)
644
+ outputPath = join(homedir(), ".claude", "skills", projectName);
645
+ outputPath = resolve(cwd, outputPath);
646
+ if (options.dryRun) {
647
+ console.log(chalk.dim(`Would create: ${outputPath}`));
648
+ return;
649
+ }
650
+ const exportService = new SkillExportService(projectName);
651
+ const result = await exportService.export({
652
+ tokens: scanResult.tokens,
653
+ components: scanResult.components,
654
+ drifts: diffResult.drifts,
655
+ projectName,
656
+ }, {
657
+ sections: ["tokens", "components", "patterns", "anti-patterns"],
658
+ outputPath,
659
+ });
660
+ for (const file of result.files) {
661
+ const dir = dirname(file.path);
662
+ if (!existsSync(dir))
663
+ mkdirSync(dir, { recursive: true });
664
+ writeFileSync(file.path, file.content);
665
+ }
666
+ if (options.json) {
667
+ console.log(JSON.stringify({ path: outputPath, stats: result.stats }, null, 2));
668
+ }
669
+ else {
670
+ success(`Created skills at ${outputPath}`);
671
+ }
672
+ }
673
+ catch (err) {
674
+ spin.stop();
675
+ error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
676
+ process.exit(1);
677
+ }
678
+ }
679
+ /**
680
+ * Context dock - generates CLAUDE.md section
681
+ */
682
+ async function runContextDock(options) {
683
+ const cwd = process.cwd();
684
+ if (options.json)
685
+ setJsonMode(true);
686
+ const spin = spinner("Generating context...");
687
+ try {
688
+ const { config, projectName } = await loadOrBuildConfig(cwd);
689
+ spin.text = "Scanning...";
690
+ const orchestrator = new ScanOrchestrator(config, cwd);
691
+ const scanResult = await orchestrator.scan();
692
+ spin.text = "Analyzing...";
693
+ const { SemanticDiffEngine } = await import("@buoy-design/core/analysis");
694
+ const engine = new SemanticDiffEngine();
695
+ const diffResult = engine.analyzeComponents(scanResult.components, {
696
+ availableTokens: scanResult.tokens,
697
+ });
698
+ spin.stop();
699
+ const contextResult = generateContext({
700
+ tokens: scanResult.tokens,
701
+ components: scanResult.components,
702
+ drifts: diffResult.drifts,
703
+ projectName,
704
+ }, {
705
+ detailLevel: options.detail ||
706
+ "standard",
707
+ });
708
+ if (options.json) {
709
+ console.log(JSON.stringify({ content: contextResult.content, stats: contextResult.stats }, null, 2));
710
+ return;
711
+ }
712
+ if (options.stdout) {
713
+ console.log(contextResult.content);
714
+ return;
715
+ }
716
+ const claudeMdPath = options.output || join(cwd, "CLAUDE.md");
717
+ const header = "## Design System\n\n";
718
+ if (existsSync(claudeMdPath)) {
719
+ const existing = readFileSync(claudeMdPath, "utf-8");
720
+ if (!existing.includes("## Design System")) {
721
+ appendFileSync(claudeMdPath, `\n${header}${contextResult.content}\n`);
722
+ success("Appended to CLAUDE.md");
723
+ }
724
+ else {
725
+ warning("CLAUDE.md already has Design System section");
726
+ }
727
+ }
728
+ else {
729
+ writeFileSync(claudeMdPath, `# CLAUDE.md\n\n${header}${contextResult.content}\n`);
730
+ success("Created CLAUDE.md");
731
+ }
732
+ }
733
+ catch (err) {
734
+ spin.stop();
735
+ error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
736
+ process.exit(1);
737
+ }
738
+ }
739
+ /**
740
+ * Hooks dock - sets up hooks (interactive by default)
741
+ */
742
+ async function runHooksDock(options) {
743
+ const cwd = process.cwd();
744
+ console.log("");
745
+ // If no flags provided, ask interactively
746
+ let setupCommit = options.commit;
747
+ let setupClaude = options.claude;
748
+ if (!setupCommit && !setupClaude) {
749
+ // Check if we're in an interactive terminal
750
+ if (!process.stdin.isTTY) {
751
+ // Non-interactive: skip hooks setup with a friendly message
752
+ info("Skipping interactive hooks setup (non-interactive terminal)");
753
+ console.log("");
754
+ console.log(chalk.dim(" To set up hooks later, run:"));
755
+ console.log(` ${chalk.cyan("buoy dock hooks --commit")} # Git pre-commit hooks`);
756
+ console.log(` ${chalk.cyan("buoy dock hooks --claude")} # Claude Code hooks`);
757
+ console.log(` ${chalk.cyan("buoy dock hooks")} # Interactive setup`);
758
+ console.log("");
759
+ return;
760
+ }
761
+ try {
762
+ const choice = await showMenu("Which hooks would you like to set up?", [
763
+ {
764
+ label: "🔗 Claude Code hooks",
765
+ value: "claude",
766
+ description: "Inject design system context into every Claude session",
767
+ },
768
+ {
769
+ label: "📝 Git pre-commit hooks",
770
+ value: "commit",
771
+ description: "Check for drift before each commit",
772
+ },
773
+ {
774
+ label: "✨ Both",
775
+ value: "both",
776
+ description: "Set up Claude hooks and git pre-commit hooks",
777
+ },
778
+ ]);
779
+ if (choice === "commit" || choice === "both") {
780
+ setupCommit = true;
781
+ }
782
+ if (choice === "claude" || choice === "both") {
783
+ setupClaude = true;
784
+ }
785
+ }
786
+ catch (err) {
787
+ // Handle user closing the prompt (Ctrl+C, etc.) gracefully
788
+ if (err && typeof err === 'object' && 'name' in err && err.name === 'ExitPromptError') {
789
+ console.log("");
790
+ info("Hooks setup cancelled");
791
+ console.log(chalk.dim(" Run `buoy dock hooks` anytime to set up hooks"));
792
+ console.log("");
793
+ return;
794
+ }
795
+ throw err;
796
+ }
797
+ }
798
+ // Set up git pre-commit hooks
799
+ if (setupCommit) {
800
+ const hookSystem = detectHookSystem(cwd);
801
+ if (hookSystem) {
802
+ info(`Detected: ${hookSystem}`);
803
+ const hookResult = setupHooks(cwd);
804
+ if (hookResult.success) {
805
+ success(hookResult.message);
806
+ }
807
+ else {
808
+ warning(hookResult.message);
809
+ }
810
+ }
811
+ else {
812
+ const standaloneResult = generateStandaloneHook(cwd);
813
+ if (standaloneResult.success) {
814
+ success(standaloneResult.message);
815
+ info("Copy to .git/hooks/pre-commit to activate");
816
+ }
817
+ else {
818
+ warning(standaloneResult.message);
819
+ }
820
+ }
821
+ }
822
+ // Set up Claude Code hooks
823
+ if (setupClaude) {
824
+ if (setupCommit)
825
+ console.log(""); // Add spacing if both
826
+ const { config, projectName } = await loadOrBuildConfig(cwd);
827
+ const orchestrator = new ScanOrchestrator(config, cwd);
828
+ const spin = spinner("Scanning design system for Claude context...");
829
+ const scanResult = await orchestrator.scan();
830
+ spin.stop();
831
+ const { SemanticDiffEngine } = await import("@buoy-design/core/analysis");
832
+ const engine = new SemanticDiffEngine();
833
+ const diffResult = engine.analyzeComponents(scanResult.components, {});
834
+ const exportService = new SkillExportService(projectName);
835
+ const condensedContext = exportService.generateCondensedContext({
836
+ tokens: scanResult.tokens,
837
+ components: scanResult.components,
838
+ drifts: diffResult.drifts,
839
+ projectName,
840
+ });
841
+ const claudeDir = resolve(cwd, ".claude");
842
+ if (!existsSync(claudeDir))
843
+ mkdirSync(claudeDir, { recursive: true });
844
+ writeFileSync(resolve(claudeDir, "buoy-context.md"), condensedContext);
845
+ const claudeResult = setupClaudeHooks(cwd);
846
+ if (claudeResult.success) {
847
+ success("Created Claude hooks");
848
+ info("Your design system will be loaded at the start of each Claude session");
849
+ }
850
+ else {
851
+ warning(`Claude hooks failed: ${claudeResult.message}`);
852
+ }
853
+ }
854
+ console.log("");
855
+ }
856
+ // ============ Helpers ============
857
+ async function loadOrBuildConfig(cwd) {
858
+ const existingConfigPath = getConfigPath();
859
+ if (existingConfigPath) {
860
+ const result = await loadConfig();
861
+ return {
862
+ config: result.config,
863
+ projectName: result.config.project?.name || "design-system",
864
+ };
865
+ }
866
+ const autoResult = await buildAutoConfig(cwd);
867
+ return {
868
+ config: autoResult.config,
869
+ projectName: autoResult.config.project?.name || "design-system",
870
+ };
871
+ }
872
+ function generateConfig(project) {
873
+ const monorepoConfig = detectMonorepoConfig(project.root);
874
+ // Build the config object
875
+ const config = {
876
+ project: { name: project.name },
877
+ sources: {},
878
+ };
879
+ const sources = config.sources;
880
+ for (const framework of project.frameworks) {
881
+ const sourceKey = getSourceKey(framework.name);
882
+ if (sourceKey) {
883
+ const extensions = getExtensions(sourceKey, framework.typescript);
884
+ const defaultPatterns = extensions.map((ext) => `src/**/*.${ext}`);
885
+ const patterns = monorepoConfig.type
886
+ ? expandPatternsForMonorepo(defaultPatterns, monorepoConfig).allPatterns
887
+ : defaultPatterns;
888
+ sources[sourceKey] = {
889
+ enabled: true,
890
+ include: patterns,
891
+ exclude: ["**/*.test.*", "**/*.spec.*", "**/*.stories.*"],
892
+ };
893
+ }
894
+ }
895
+ if (project.tokens.length > 0) {
896
+ sources.tokens = {
897
+ enabled: true,
898
+ files: project.tokens.map((t) => t.path),
899
+ };
900
+ }
901
+ sources.figma = { enabled: false };
902
+ config.output = { format: "table", colors: true };
903
+ // Generate YAML with header comment
904
+ const header = `# Buoy Configuration
905
+ # Auto-generated - customize to match your project
906
+ # Docs: https://buoy.design/docs/configuration
907
+
908
+ `;
909
+ return header + stringifyYaml(config, { indent: 2, lineWidth: 0 });
910
+ }
911
+ function getSourceKey(name) {
912
+ if ([
913
+ "react",
914
+ "nextjs",
915
+ "remix",
916
+ "gatsby",
917
+ "react-native",
918
+ "expo",
919
+ "preact",
920
+ "solid",
921
+ ].includes(name))
922
+ return "react";
923
+ if (["vue", "nuxt"].includes(name))
924
+ return "vue";
925
+ if (["svelte", "sveltekit"].includes(name))
926
+ return "svelte";
927
+ if (name === "angular")
928
+ return "angular";
929
+ if (["lit", "stencil"].includes(name))
930
+ return "webcomponent";
931
+ return null;
932
+ }
933
+ function getExtensions(sourceKey, typescript) {
934
+ switch (sourceKey) {
935
+ case "vue":
936
+ return ["vue"];
937
+ case "svelte":
938
+ return ["svelte"];
939
+ case "angular":
940
+ return ["component.ts"];
941
+ case "webcomponent":
942
+ return ["ts"];
943
+ default:
944
+ return typescript ? ["tsx", "jsx"] : ["jsx", "tsx"];
945
+ }
946
+ }
947
+ function printDetectionResults(project) {
948
+ console.log("");
949
+ console.log(chalk.bold(" Detected:"));
950
+ for (const fw of project.frameworks) {
951
+ console.log(chalk.green(" ✓ ") +
952
+ chalk.bold(fw.name) +
953
+ (fw.typescript ? " + TS" : ""));
954
+ }
955
+ for (const comp of project.components) {
956
+ console.log(chalk.green(" ✓ ") +
957
+ `${comp.fileCount} files in ${chalk.cyan(comp.path)}`);
958
+ }
959
+ for (const token of project.tokens) {
960
+ console.log(chalk.green(" ✓ ") + `${token.name}: ${chalk.cyan(token.path)}`);
961
+ }
962
+ if (project.storybook)
963
+ console.log(chalk.green(" ✓ ") + "Storybook");
964
+ console.log("");
965
+ }
966
+ async function showFrameworksAndPlugins(frameworks, autoInstall) {
967
+ const builtIn = frameworks.filter((fw) => fw.scanner);
968
+ const plugins = frameworks.filter((fw) => fw.plugin && !fw.scanner);
969
+ if (builtIn.length > 0) {
970
+ console.log(chalk.bold(" Built-in Scanners"));
971
+ for (const fw of builtIn)
972
+ console.log(` ${chalk.green("✓")} ${chalk.cyan(fw.name)}`);
973
+ console.log("");
974
+ }
975
+ if (plugins.length > 0) {
976
+ const missing = plugins
977
+ .map((fw) => fw.plugin)
978
+ .filter((p, i, arr) => arr.indexOf(p) === i);
979
+ console.log(chalk.bold(" Optional Plugins"));
980
+ console.log(` ${chalk.dim(getPluginInstallCommand(missing))}`);
981
+ console.log("");
982
+ if (autoInstall) {
983
+ const { execSync } = await import("node:child_process");
984
+ try {
985
+ execSync(getPluginInstallCommand(missing), { stdio: "inherit" });
986
+ success("Plugins installed");
987
+ }
988
+ catch {
989
+ warning("Plugin install failed");
990
+ }
991
+ }
992
+ }
993
+ }
994
+ async function promptConfirm(message, defaultValue = true) {
995
+ if (!process.stdin.isTTY)
996
+ return defaultValue;
997
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
998
+ return new Promise((resolve) => {
999
+ rl.question(`${message} ${defaultValue ? "[Y/n]" : "[y/N]"} `, (answer) => {
1000
+ rl.close();
1001
+ const t = answer.trim().toLowerCase();
1002
+ resolve(t === "" ? defaultValue : t === "y" || t === "yes");
1003
+ });
1004
+ });
1005
+ }
1006
+ //# sourceMappingURL=dock.js.map