@buoy-design/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/bin.d.ts +3 -0
  2. package/dist/bin.d.ts.map +1 -0
  3. package/dist/bin.js +5 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/commands/__tests__/ci.test.d.ts +2 -0
  6. package/dist/commands/__tests__/ci.test.d.ts.map +1 -0
  7. package/dist/commands/__tests__/ci.test.js +33 -0
  8. package/dist/commands/__tests__/ci.test.js.map +1 -0
  9. package/dist/commands/bootstrap.d.ts +3 -0
  10. package/dist/commands/bootstrap.d.ts.map +1 -0
  11. package/dist/commands/bootstrap.js +458 -0
  12. package/dist/commands/bootstrap.js.map +1 -0
  13. package/dist/commands/build.d.ts +3 -0
  14. package/dist/commands/build.d.ts.map +1 -0
  15. package/dist/commands/build.js +314 -0
  16. package/dist/commands/build.js.map +1 -0
  17. package/dist/commands/ci.d.ts +24 -0
  18. package/dist/commands/ci.d.ts.map +1 -0
  19. package/dist/commands/ci.js +273 -0
  20. package/dist/commands/ci.js.map +1 -0
  21. package/dist/commands/ci.logic.d.ts +20 -0
  22. package/dist/commands/ci.logic.d.ts.map +1 -0
  23. package/dist/commands/ci.logic.js +28 -0
  24. package/dist/commands/ci.logic.js.map +1 -0
  25. package/dist/commands/drift.d.ts +3 -0
  26. package/dist/commands/drift.d.ts.map +1 -0
  27. package/dist/commands/drift.js +178 -0
  28. package/dist/commands/drift.js.map +1 -0
  29. package/dist/commands/index.d.ts +9 -0
  30. package/dist/commands/index.d.ts.map +1 -0
  31. package/dist/commands/index.js +9 -0
  32. package/dist/commands/index.js.map +1 -0
  33. package/dist/commands/init.d.ts +3 -0
  34. package/dist/commands/init.d.ts.map +1 -0
  35. package/dist/commands/init.js +490 -0
  36. package/dist/commands/init.js.map +1 -0
  37. package/dist/commands/plugins.d.ts +3 -0
  38. package/dist/commands/plugins.d.ts.map +1 -0
  39. package/dist/commands/plugins.js +72 -0
  40. package/dist/commands/plugins.js.map +1 -0
  41. package/dist/commands/scan.d.ts +3 -0
  42. package/dist/commands/scan.d.ts.map +1 -0
  43. package/dist/commands/scan.js +266 -0
  44. package/dist/commands/scan.js.map +1 -0
  45. package/dist/commands/status.d.ts +3 -0
  46. package/dist/commands/status.d.ts.map +1 -0
  47. package/dist/commands/status.js +205 -0
  48. package/dist/commands/status.js.map +1 -0
  49. package/dist/config/index.d.ts +3 -0
  50. package/dist/config/index.d.ts.map +1 -0
  51. package/dist/config/index.js +3 -0
  52. package/dist/config/index.js.map +1 -0
  53. package/dist/config/loader.d.ts +8 -0
  54. package/dist/config/loader.d.ts.map +1 -0
  55. package/dist/config/loader.js +67 -0
  56. package/dist/config/loader.js.map +1 -0
  57. package/dist/config/schema.d.ts +1040 -0
  58. package/dist/config/schema.d.ts.map +1 -0
  59. package/dist/config/schema.js +116 -0
  60. package/dist/config/schema.js.map +1 -0
  61. package/dist/detect/frameworks.d.ts +18 -0
  62. package/dist/detect/frameworks.d.ts.map +1 -0
  63. package/dist/detect/frameworks.js +168 -0
  64. package/dist/detect/frameworks.js.map +1 -0
  65. package/dist/detect/index.d.ts +3 -0
  66. package/dist/detect/index.d.ts.map +1 -0
  67. package/dist/detect/index.js +3 -0
  68. package/dist/detect/index.js.map +1 -0
  69. package/dist/detect/project-detector.d.ts +61 -0
  70. package/dist/detect/project-detector.d.ts.map +1 -0
  71. package/dist/detect/project-detector.js +849 -0
  72. package/dist/detect/project-detector.js.map +1 -0
  73. package/dist/index.d.ts +5 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +22 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/output/formatters.d.ts +21 -0
  78. package/dist/output/formatters.d.ts.map +1 -0
  79. package/dist/output/formatters.js +421 -0
  80. package/dist/output/formatters.js.map +1 -0
  81. package/dist/output/index.d.ts +3 -0
  82. package/dist/output/index.d.ts.map +1 -0
  83. package/dist/output/index.js +3 -0
  84. package/dist/output/index.js.map +1 -0
  85. package/dist/output/reporters.d.ts +23 -0
  86. package/dist/output/reporters.d.ts.map +1 -0
  87. package/dist/output/reporters.js +147 -0
  88. package/dist/output/reporters.js.map +1 -0
  89. package/dist/plugins/index.d.ts +3 -0
  90. package/dist/plugins/index.d.ts.map +1 -0
  91. package/dist/plugins/index.js +3 -0
  92. package/dist/plugins/index.js.map +1 -0
  93. package/dist/plugins/loader.d.ts +11 -0
  94. package/dist/plugins/loader.d.ts.map +1 -0
  95. package/dist/plugins/loader.js +77 -0
  96. package/dist/plugins/loader.js.map +1 -0
  97. package/dist/plugins/registry.d.ts +15 -0
  98. package/dist/plugins/registry.d.ts.map +1 -0
  99. package/dist/plugins/registry.js +32 -0
  100. package/dist/plugins/registry.js.map +1 -0
  101. package/package.json +48 -0
@@ -0,0 +1,147 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ // Output configuration for JSON mode
4
+ // When set to true, all non-JSON output is suppressed or redirected to stderr
5
+ let jsonMode = false;
6
+ export function setJsonMode(enabled) {
7
+ jsonMode = enabled;
8
+ }
9
+ export function isJsonMode() {
10
+ return jsonMode;
11
+ }
12
+ // Create a spinner
13
+ // In JSON mode, spinner output goes to stderr to keep stdout clean for JSON
14
+ export function spinner(text) {
15
+ return ora({
16
+ text,
17
+ color: 'cyan',
18
+ stream: jsonMode ? process.stderr : process.stdout,
19
+ }).start();
20
+ }
21
+ // Success message
22
+ export function success(message) {
23
+ console.log(chalk.green('✓') + ' ' + message);
24
+ }
25
+ // Warning message
26
+ export function warning(message) {
27
+ console.log(chalk.yellow('!') + ' ' + message);
28
+ }
29
+ // Error message
30
+ export function error(message) {
31
+ console.log(chalk.red('✗') + ' ' + message);
32
+ }
33
+ // Info message
34
+ export function info(message) {
35
+ console.log(chalk.blue('i') + ' ' + message);
36
+ }
37
+ // Debug message (only in verbose mode)
38
+ export function debug(message, verbose = false) {
39
+ if (verbose) {
40
+ console.log(chalk.dim(' ' + message));
41
+ }
42
+ }
43
+ // Header
44
+ export function header(text) {
45
+ console.log('');
46
+ console.log(chalk.bold(text));
47
+ console.log(chalk.dim('─'.repeat(text.length)));
48
+ }
49
+ // Newline
50
+ export function newline() {
51
+ console.log('');
52
+ }
53
+ // Divider
54
+ export function divider() {
55
+ console.log(chalk.dim('─'.repeat(50)));
56
+ }
57
+ // List item
58
+ export function listItem(text, indent = 0) {
59
+ const prefix = ' '.repeat(indent) + chalk.dim('•') + ' ';
60
+ console.log(prefix + text);
61
+ }
62
+ // Key-value pair
63
+ export function keyValue(key, value, indent = 0) {
64
+ const prefix = ' '.repeat(indent);
65
+ console.log(prefix + chalk.dim(key + ':') + ' ' + value);
66
+ }
67
+ // Progress bar (simple)
68
+ // Always writes to stderr to avoid corrupting JSON output on stdout
69
+ export function progress(current, total, label) {
70
+ // In JSON mode, suppress progress output entirely
71
+ if (jsonMode) {
72
+ return;
73
+ }
74
+ // Handle division by zero: if total is 0, show 100% (nothing to do = complete)
75
+ const percent = total > 0 ? Math.round((current / total) * 100) : 100;
76
+ const filled = Math.round(percent / 5);
77
+ const empty = 20 - filled;
78
+ const bar = chalk.green('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
79
+ process.stderr.write(`\r${bar} ${percent}% ${label}`);
80
+ if (current === total) {
81
+ process.stderr.write('\n');
82
+ }
83
+ }
84
+ // Coverage grid symbols
85
+ const SYMBOLS = {
86
+ filled: '⛁', // Aligned
87
+ partial: '⛀', // Drifting
88
+ empty: '⛶', // Untracked/empty
89
+ };
90
+ const GRID_COLS = 10;
91
+ const GRID_ROWS = 10;
92
+ const TOTAL_SLOTS = GRID_COLS * GRID_ROWS;
93
+ // Render the coverage grid with legend
94
+ export function coverageGrid(stats) {
95
+ const { aligned, drifting, untracked, total } = stats;
96
+ // Calculate percentages
97
+ const alignedPct = total > 0 ? Math.round((aligned / total) * 100) : 0;
98
+ const driftingPct = total > 0 ? Math.round((drifting / total) * 100) : 0;
99
+ const untrackedPct = total > 0 ? Math.round((untracked / total) * 100) : 0;
100
+ // Scale to grid slots
101
+ const scale = total > 0 ? TOTAL_SLOTS / total : 0;
102
+ const alignedSlots = Math.round(aligned * scale);
103
+ const driftingSlots = Math.round(drifting * scale);
104
+ const untrackedSlots = TOTAL_SLOTS - alignedSlots - driftingSlots;
105
+ // Build the grid array
106
+ const grid = [];
107
+ for (let i = 0; i < alignedSlots; i++) {
108
+ grid.push(chalk.green(SYMBOLS.filled));
109
+ }
110
+ for (let i = 0; i < driftingSlots; i++) {
111
+ grid.push(chalk.yellow(SYMBOLS.partial));
112
+ }
113
+ for (let i = 0; i < untrackedSlots; i++) {
114
+ grid.push(chalk.dim(SYMBOLS.empty));
115
+ }
116
+ // Pad to TOTAL_SLOTS if needed
117
+ while (grid.length < TOTAL_SLOTS) {
118
+ grid.push(chalk.dim(SYMBOLS.empty));
119
+ }
120
+ // Build legend lines
121
+ const summaryLine = `${aligned}/${total} components · ${alignedPct}% aligned`;
122
+ const legendLines = [
123
+ '',
124
+ '',
125
+ `${chalk.green(SYMBOLS.filled)} Aligned: ${aligned} (${alignedPct}%)`,
126
+ `${chalk.yellow(SYMBOLS.partial)} Drifting: ${drifting} (${driftingPct}%)`,
127
+ `${chalk.dim(SYMBOLS.empty)} Untracked: ${untracked} (${untrackedPct}%)`,
128
+ '',
129
+ '',
130
+ '',
131
+ '',
132
+ '',
133
+ ];
134
+ // Print header
135
+ console.log('');
136
+ console.log(chalk.bold('Component Alignment'));
137
+ console.log('');
138
+ // Print grid with legend
139
+ for (let row = 0; row < GRID_ROWS; row++) {
140
+ const start = row * GRID_COLS;
141
+ const rowSymbols = grid.slice(start, start + GRID_COLS).join(' ');
142
+ const legend = row === 0 ? ` ${summaryLine}` : (legendLines[row] ? ` ${legendLines[row]}` : '');
143
+ console.log(rowSymbols + legend);
144
+ }
145
+ console.log('');
146
+ }
147
+ //# sourceMappingURL=reporters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporters.js","sourceRoot":"","sources":["../../src/output/reporters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { loadPlugin, discoverPlugins, loadDiscoveredPlugins, registry } from './loader.js';
2
+ export { PluginRegistry } from './registry.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { loadPlugin, discoverPlugins, loadDiscoveredPlugins, registry } from './loader.js';
2
+ export { PluginRegistry } from './registry.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BuoyPlugin } from '@buoy-design/core';
2
+ import { registry } from './registry.js';
3
+ interface LoaderOptions {
4
+ projectRoot?: string;
5
+ autoDiscover?: boolean;
6
+ }
7
+ export declare function loadPlugin(nameOrPath: string): Promise<BuoyPlugin>;
8
+ export declare function discoverPlugins(options?: LoaderOptions): Promise<string[]>;
9
+ export declare function loadDiscoveredPlugins(options?: LoaderOptions): Promise<BuoyPlugin[]>;
10
+ export { registry };
11
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AA4BzC,UAAU,aAAa;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA4BxE;AAED,wBAAsB,eAAe,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAepF;AAED,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAc9F;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { resolve } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { registry } from './registry.js';
5
+ const PLUGIN_PREFIX = '@buoy-design/plugin-';
6
+ // Security: Only allow safe characters in plugin names to prevent arbitrary package imports
7
+ const VALID_SIMPLE_NAME = /^[a-z0-9-]+$/;
8
+ const VALID_SCOPED_PACKAGE = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
9
+ function validatePluginName(nameOrPath) {
10
+ if (nameOrPath.startsWith('@')) {
11
+ // Scoped package: must match @org/name pattern with safe characters only
12
+ if (!VALID_SCOPED_PACKAGE.test(nameOrPath)) {
13
+ throw new Error(`Invalid plugin name "${nameOrPath}". Scoped packages must match @org/name ` +
14
+ `with only lowercase letters, numbers, and hyphens.`);
15
+ }
16
+ }
17
+ else {
18
+ // Simple name: only allow lowercase letters, numbers, and hyphens
19
+ if (!VALID_SIMPLE_NAME.test(nameOrPath)) {
20
+ throw new Error(`Invalid plugin name "${nameOrPath}". Plugin names must contain only ` +
21
+ `lowercase letters, numbers, and hyphens.`);
22
+ }
23
+ }
24
+ }
25
+ export async function loadPlugin(nameOrPath) {
26
+ // Security: Validate plugin name before dynamic import
27
+ validatePluginName(nameOrPath);
28
+ // Handle shorthand: "react" -> "@buoy-design/plugin-react"
29
+ const moduleName = nameOrPath.startsWith('@')
30
+ ? nameOrPath
31
+ : `${PLUGIN_PREFIX}${nameOrPath}`;
32
+ try {
33
+ const imported = await import(moduleName);
34
+ const factory = imported.default || imported.plugin || imported;
35
+ if (typeof factory !== 'function') {
36
+ throw new Error(`Plugin ${moduleName} does not export a valid factory function`);
37
+ }
38
+ const plugin = await factory();
39
+ registry.register(plugin);
40
+ return plugin;
41
+ }
42
+ catch (err) {
43
+ if (err.code === 'ERR_MODULE_NOT_FOUND') {
44
+ throw new Error(`Plugin "${moduleName}" not found. Install it with: npm install ${moduleName}`);
45
+ }
46
+ throw err;
47
+ }
48
+ }
49
+ export async function discoverPlugins(options = {}) {
50
+ const projectRoot = options.projectRoot || process.cwd();
51
+ const pkgPath = resolve(projectRoot, 'package.json');
52
+ if (!existsSync(pkgPath)) {
53
+ return [];
54
+ }
55
+ const pkgJson = JSON.parse(await readFile(pkgPath, 'utf-8'));
56
+ const allDeps = {
57
+ ...pkgJson.dependencies,
58
+ ...pkgJson.devDependencies,
59
+ };
60
+ return Object.keys(allDeps).filter((dep) => dep.startsWith(PLUGIN_PREFIX));
61
+ }
62
+ export async function loadDiscoveredPlugins(options = {}) {
63
+ const pluginNames = await discoverPlugins(options);
64
+ const plugins = [];
65
+ for (const name of pluginNames) {
66
+ try {
67
+ const plugin = await loadPlugin(name);
68
+ plugins.push(plugin);
69
+ }
70
+ catch (err) {
71
+ console.warn(`Warning: Failed to load plugin ${name}:`, err.message);
72
+ }
73
+ }
74
+ return plugins;
75
+ }
76
+ export { registry };
77
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { BuoyPlugin, PluginMetadata } from '@buoy-design/core';
2
+ export declare class PluginRegistry {
3
+ private plugins;
4
+ register(plugin: BuoyPlugin): void;
5
+ get(name: string): BuoyPlugin | undefined;
6
+ getAll(): BuoyPlugin[];
7
+ getScanners(): BuoyPlugin[];
8
+ getReporters(): BuoyPlugin[];
9
+ getByDetection(framework: string): BuoyPlugin | undefined;
10
+ list(): PluginMetadata[];
11
+ unregister(name: string): boolean;
12
+ clear(): void;
13
+ }
14
+ export declare const registry: PluginRegistry;
15
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAsC;IAErD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIlC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIzC,MAAM,IAAI,UAAU,EAAE;IAItB,WAAW,IAAI,UAAU,EAAE;IAI3B,YAAY,IAAI,UAAU,EAAE;IAI5B,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAMzD,IAAI,IAAI,cAAc,EAAE;IAIxB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,QAAQ,gBAAuB,CAAC"}
@@ -0,0 +1,32 @@
1
+ export class PluginRegistry {
2
+ plugins = new Map();
3
+ register(plugin) {
4
+ this.plugins.set(plugin.metadata.name, plugin);
5
+ }
6
+ get(name) {
7
+ return this.plugins.get(name);
8
+ }
9
+ getAll() {
10
+ return Array.from(this.plugins.values());
11
+ }
12
+ getScanners() {
13
+ return this.getAll().filter((p) => typeof p.scan === 'function');
14
+ }
15
+ getReporters() {
16
+ return this.getAll().filter((p) => typeof p.report === 'function');
17
+ }
18
+ getByDetection(framework) {
19
+ return this.getAll().find((p) => p.metadata.detects?.includes(framework.toLowerCase()));
20
+ }
21
+ list() {
22
+ return this.getAll().map((p) => p.metadata);
23
+ }
24
+ unregister(name) {
25
+ return this.plugins.delete(name);
26
+ }
27
+ clear() {
28
+ this.plugins.clear();
29
+ }
30
+ }
31
+ export const registry = new PluginRegistry();
32
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,cAAc;IACjB,OAAO,GAA4B,IAAI,GAAG,EAAE,CAAC;IAErD,QAAQ,CAAC,MAAkB;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACnE,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IACrE,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9B,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CACtD,CAAC;IACJ,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@buoy-design/cli",
3
+ "version": "0.1.0",
4
+ "description": "Buoy CLI - Design drift detection for the AI era",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Buoy <hello@buoy.design>",
8
+ "homepage": "https://buoy.design",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/dylantarre/buoy-design.git",
12
+ "directory": "apps/cli"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/dylantarre/buoy-design/issues"
16
+ },
17
+ "keywords": ["buoy", "design-system", "drift-detection", "cli", "design-tokens"],
18
+ "files": ["dist", "README.md"],
19
+ "bin": {
20
+ "buoy": "./dist/bin.js"
21
+ },
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "dev": "tsc --watch",
27
+ "typecheck": "tsc --noEmit",
28
+ "clean": "rm -rf dist",
29
+ "start": "node ./dist/bin/buoy.js",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest"
32
+ },
33
+ "dependencies": {
34
+ "@buoy-design/core": "workspace:*",
35
+ "@buoy-design/db": "workspace:*",
36
+ "@buoy-design/scanners": "workspace:*",
37
+ "chalk": "^5.3.0",
38
+ "cli-table3": "^0.6.5",
39
+ "commander": "^12.1.0",
40
+ "glob": "^11.0.0",
41
+ "ora": "^8.1.1",
42
+ "zod": "^3.24.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.10.2",
46
+ "typescript": "^5.7.2"
47
+ }
48
+ }