@nikkory/vibe-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +234 -0
- package/README.md +261 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +4106 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2371 -0
- package/nikkory-vibe.config.example.d.ts +23 -0
- package/nikkory-vibe.config.example.ts +143 -0
- package/package.json +60 -0
- package/tsup.config.d.ts +3 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2371 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var import_commander7 = require("commander");
|
|
28
|
+
|
|
29
|
+
// src/commands/add.ts
|
|
30
|
+
var fs = __toESM(require("fs/promises"));
|
|
31
|
+
var path = __toESM(require("path"));
|
|
32
|
+
var import_commander = require("commander");
|
|
33
|
+
var import_inquirer = __toESM(require("inquirer"));
|
|
34
|
+
|
|
35
|
+
// src/utils/logger.ts
|
|
36
|
+
var import_boxen = __toESM(require("boxen"));
|
|
37
|
+
var import_chalk = __toESM(require("chalk"));
|
|
38
|
+
var import_ora = __toESM(require("ora"));
|
|
39
|
+
var Logger = class {
|
|
40
|
+
spinner = null;
|
|
41
|
+
/**
|
|
42
|
+
* Success message (green checkmark)
|
|
43
|
+
*/
|
|
44
|
+
success(message) {
|
|
45
|
+
console.log(import_chalk.default.green("\u2713"), message);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Error message (red cross)
|
|
49
|
+
*/
|
|
50
|
+
error(message) {
|
|
51
|
+
console.log(import_chalk.default.red("\u2717"), message);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Warning message (yellow exclamation)
|
|
55
|
+
*/
|
|
56
|
+
warn(message) {
|
|
57
|
+
console.log(import_chalk.default.yellow("\u26A0"), message);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Info message (blue dot)
|
|
61
|
+
*/
|
|
62
|
+
info(message) {
|
|
63
|
+
console.log(import_chalk.default.blue("\u2139"), message);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Debug message (gray)
|
|
67
|
+
*/
|
|
68
|
+
debug(message) {
|
|
69
|
+
if (process.env["DEBUG"]) {
|
|
70
|
+
console.log(import_chalk.default.gray("\u2022"), import_chalk.default.gray(message));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Start loading spinner
|
|
75
|
+
*/
|
|
76
|
+
startSpinner(message) {
|
|
77
|
+
this.spinner = (0, import_ora.default)(message).start();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Update spinner message
|
|
81
|
+
*/
|
|
82
|
+
updateSpinner(message) {
|
|
83
|
+
if (this.spinner) {
|
|
84
|
+
this.spinner.text = message;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Stop spinner with success
|
|
89
|
+
*/
|
|
90
|
+
stopSpinnerSuccess(message) {
|
|
91
|
+
if (this.spinner) {
|
|
92
|
+
this.spinner.succeed(message);
|
|
93
|
+
this.spinner = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Stop spinner with failure
|
|
98
|
+
*/
|
|
99
|
+
stopSpinnerFail(message) {
|
|
100
|
+
if (this.spinner) {
|
|
101
|
+
this.spinner.fail(message);
|
|
102
|
+
this.spinner = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Stop spinner
|
|
107
|
+
*/
|
|
108
|
+
stopSpinner() {
|
|
109
|
+
if (this.spinner) {
|
|
110
|
+
this.spinner.stop();
|
|
111
|
+
this.spinner = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Print box message
|
|
116
|
+
*/
|
|
117
|
+
box(message, options) {
|
|
118
|
+
console.log(
|
|
119
|
+
(0, import_boxen.default)(message, {
|
|
120
|
+
padding: 1,
|
|
121
|
+
margin: 1,
|
|
122
|
+
borderStyle: "round",
|
|
123
|
+
borderColor: options?.borderColor || "cyan",
|
|
124
|
+
title: options?.title,
|
|
125
|
+
titleAlignment: "center"
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Print separator line
|
|
131
|
+
*/
|
|
132
|
+
separator() {
|
|
133
|
+
console.log(import_chalk.default.gray("\u2500".repeat(50)));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Print newline
|
|
137
|
+
*/
|
|
138
|
+
newline() {
|
|
139
|
+
console.log();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Print header
|
|
143
|
+
*/
|
|
144
|
+
header(title) {
|
|
145
|
+
console.log();
|
|
146
|
+
console.log(import_chalk.default.bold.cyan(title));
|
|
147
|
+
this.separator();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Print code block
|
|
151
|
+
*/
|
|
152
|
+
code(code) {
|
|
153
|
+
console.log(import_chalk.default.gray(code));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Print table
|
|
157
|
+
*/
|
|
158
|
+
table(data) {
|
|
159
|
+
if (data.length === 0) return;
|
|
160
|
+
const firstRow = data[0];
|
|
161
|
+
if (!firstRow) return;
|
|
162
|
+
const headers = Object.keys(firstRow);
|
|
163
|
+
const columnWidths = headers.map((header) => {
|
|
164
|
+
const maxDataWidth = Math.max(...data.map((row) => String(row[header] ?? "").length));
|
|
165
|
+
return Math.max(header.length, maxDataWidth);
|
|
166
|
+
});
|
|
167
|
+
const headerRow = headers.map((header, i) => import_chalk.default.bold(header.padEnd(columnWidths[i] ?? 10))).join(" \u2502 ");
|
|
168
|
+
console.log(headerRow);
|
|
169
|
+
const separator = columnWidths.map((width) => "\u2500".repeat(width)).join("\u2500\u253C\u2500");
|
|
170
|
+
console.log(import_chalk.default.gray(separator));
|
|
171
|
+
data.forEach((row) => {
|
|
172
|
+
const dataRow = headers.map((header, i) => String(row[header] ?? "").padEnd(columnWidths[i] ?? 10)).join(" \u2502 ");
|
|
173
|
+
console.log(dataRow);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var logger = new Logger();
|
|
178
|
+
|
|
179
|
+
// src/commands/add.ts
|
|
180
|
+
var AVAILABLE_COMPONENTS = [
|
|
181
|
+
"button",
|
|
182
|
+
"input",
|
|
183
|
+
"card",
|
|
184
|
+
"badge",
|
|
185
|
+
"toast",
|
|
186
|
+
"modal",
|
|
187
|
+
"dropdown",
|
|
188
|
+
"tabs",
|
|
189
|
+
"accordion",
|
|
190
|
+
"avatar"
|
|
191
|
+
];
|
|
192
|
+
var COLORS = [
|
|
193
|
+
"primary",
|
|
194
|
+
"secondary",
|
|
195
|
+
"success",
|
|
196
|
+
"warning",
|
|
197
|
+
"error",
|
|
198
|
+
"info",
|
|
199
|
+
"blue",
|
|
200
|
+
"red",
|
|
201
|
+
"green",
|
|
202
|
+
"yellow",
|
|
203
|
+
"purple",
|
|
204
|
+
"gray",
|
|
205
|
+
"orange",
|
|
206
|
+
"amber",
|
|
207
|
+
"lime",
|
|
208
|
+
"emerald",
|
|
209
|
+
"teal",
|
|
210
|
+
"cyan",
|
|
211
|
+
"sky",
|
|
212
|
+
"indigo",
|
|
213
|
+
"violet",
|
|
214
|
+
"fuchsia",
|
|
215
|
+
"pink",
|
|
216
|
+
"rose",
|
|
217
|
+
"slate",
|
|
218
|
+
"zinc",
|
|
219
|
+
"neutral",
|
|
220
|
+
"stone"
|
|
221
|
+
];
|
|
222
|
+
var SIZES = ["xs", "sm", "md", "lg", "xl", "2xl"];
|
|
223
|
+
var VARIANTS = {
|
|
224
|
+
button: ["solid", "outline", "ghost", "soft", "link"],
|
|
225
|
+
input: ["outlined", "filled", "underlined", "ghost"],
|
|
226
|
+
card: ["elevated", "outlined", "filled", "ghost"],
|
|
227
|
+
badge: ["solid", "soft", "outline", "dot"],
|
|
228
|
+
toast: ["solid", "soft", "outlined", "minimal"]
|
|
229
|
+
};
|
|
230
|
+
var SHAPES = ["square", "rounded", "pill", "circle"];
|
|
231
|
+
var POSITIONS = [
|
|
232
|
+
"top-left",
|
|
233
|
+
"top-center",
|
|
234
|
+
"top-right",
|
|
235
|
+
"bottom-left",
|
|
236
|
+
"bottom-center",
|
|
237
|
+
"bottom-right",
|
|
238
|
+
"left",
|
|
239
|
+
"center",
|
|
240
|
+
"right"
|
|
241
|
+
];
|
|
242
|
+
var ANIMATIONS = ["none", "subtle", "standard", "smooth", "bounce", "pulse"];
|
|
243
|
+
var ELEVATIONS = ["none", "xs", "sm", "md", "lg", "xl", "2xl", "glow"];
|
|
244
|
+
var A11Y_LEVELS = ["standard", "enhanced", "maximum"];
|
|
245
|
+
var PRESETS = {
|
|
246
|
+
primary: { color: "blue", variant: "solid", size: "md" },
|
|
247
|
+
secondary: { color: "gray", variant: "outline", size: "md" },
|
|
248
|
+
danger: { color: "red", variant: "solid", size: "md" },
|
|
249
|
+
success: { color: "green", variant: "solid", size: "md" },
|
|
250
|
+
warning: { color: "yellow", variant: "solid", size: "md" },
|
|
251
|
+
ghost: { color: "gray", variant: "ghost", size: "md" },
|
|
252
|
+
pill: { color: "blue", variant: "solid", shape: "pill", size: "md" },
|
|
253
|
+
large: { color: "blue", variant: "solid", size: "lg" },
|
|
254
|
+
small: { color: "blue", variant: "solid", size: "sm" }
|
|
255
|
+
};
|
|
256
|
+
var addCommand = new import_commander.Command("add").description("Add a component with variant configuration").argument("[component]", "Component to add (button, input, card, etc.)").option("-c, --color <color>", "Color theme", "blue").option("-s, --size <size>", "Component size", "md").option("-v, --variant <variant>", "Visual style").option("--shape <shape>", "Border radius style", "rounded").option("-p, --position <position>", "Position (for toast, modal)").option("-a, --animation <animation>", "Animation style", "standard").option("-e, --elevation <elevation>", "Shadow depth", "md").option("--a11y <level>", "Accessibility level", "standard").option("--preset <preset>", "Use a preset configuration").option("-i, --interactive", "Interactive mode").option("-o, --output <path>", "Output directory", "./components").option("--dry-run", "Preview without writing files").action(async (component, options) => {
|
|
257
|
+
try {
|
|
258
|
+
logger.header("\u{1F3A8} Nikkory Vibe - Add Component");
|
|
259
|
+
if (options.interactive || !component) {
|
|
260
|
+
const config = await promptVariantConfig();
|
|
261
|
+
component = config.component;
|
|
262
|
+
Object.assign(options, config);
|
|
263
|
+
}
|
|
264
|
+
if (!component || !AVAILABLE_COMPONENTS.includes(component)) {
|
|
265
|
+
logger.error(`Unknown component: ${component ?? "none"}`);
|
|
266
|
+
logger.info(`Available: ${AVAILABLE_COMPONENTS.join(", ")}`);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
const validatedComponent = component;
|
|
270
|
+
if (options.preset) {
|
|
271
|
+
const preset = PRESETS[options.preset];
|
|
272
|
+
if (!preset) {
|
|
273
|
+
logger.error(`Unknown preset: ${options.preset}`);
|
|
274
|
+
logger.info(`Available: ${Object.keys(PRESETS).join(", ")}`);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
Object.assign(options, preset);
|
|
278
|
+
}
|
|
279
|
+
if (options.color && !COLORS.includes(options.color)) {
|
|
280
|
+
logger.warn(`Unknown color: ${options.color}. Using default.`);
|
|
281
|
+
}
|
|
282
|
+
if (options.size && !SIZES.includes(options.size)) {
|
|
283
|
+
logger.warn(`Unknown size: ${options.size}. Using default.`);
|
|
284
|
+
}
|
|
285
|
+
if (options.position && !POSITIONS.includes(options.position)) {
|
|
286
|
+
logger.warn(`Unknown position: ${options.position}. Using default.`);
|
|
287
|
+
}
|
|
288
|
+
if (options.a11y && !A11Y_LEVELS.includes(options.a11y)) {
|
|
289
|
+
logger.warn(`Unknown accessibility level: ${options.a11y}. Using default.`);
|
|
290
|
+
}
|
|
291
|
+
logger.newline();
|
|
292
|
+
logger.info(`Component: ${validatedComponent}`);
|
|
293
|
+
logger.info(`Color: ${options.color ?? "default"}`);
|
|
294
|
+
logger.info(`Size: ${options.size ?? "default"}`);
|
|
295
|
+
logger.info(`Variant: ${options.variant ?? "default"}`);
|
|
296
|
+
logger.info(`Shape: ${options.shape ?? "default"}`);
|
|
297
|
+
if (options.position) {
|
|
298
|
+
logger.info(`Position: ${options.position}`);
|
|
299
|
+
}
|
|
300
|
+
logger.info(`Animation: ${options.animation ?? "default"}`);
|
|
301
|
+
logger.info(`Elevation: ${options.elevation ?? "default"}`);
|
|
302
|
+
logger.info(`Accessibility: ${options.a11y ?? "default"}`);
|
|
303
|
+
logger.newline();
|
|
304
|
+
logger.startSpinner("Generating component...");
|
|
305
|
+
const variantConfig = {
|
|
306
|
+
color: options.color,
|
|
307
|
+
size: options.size,
|
|
308
|
+
variant: options.variant,
|
|
309
|
+
shape: options.shape,
|
|
310
|
+
position: options.position,
|
|
311
|
+
animation: options.animation,
|
|
312
|
+
elevation: options.elevation,
|
|
313
|
+
a11y: options.a11y
|
|
314
|
+
};
|
|
315
|
+
const code = generateComponentCode(validatedComponent, variantConfig);
|
|
316
|
+
logger.stopSpinnerSuccess("Component generated");
|
|
317
|
+
if (options.dryRun) {
|
|
318
|
+
logger.newline();
|
|
319
|
+
logger.header("Generated Code (Preview)");
|
|
320
|
+
logger.code(code);
|
|
321
|
+
logger.newline();
|
|
322
|
+
logger.info("Dry run mode - No files written");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const componentName = capitalize(validatedComponent);
|
|
326
|
+
const outputPath = path.join(process.cwd(), options.output, `${componentName}.tsx`);
|
|
327
|
+
logger.startSpinner(`Writing to ${outputPath}...`);
|
|
328
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
329
|
+
await fs.writeFile(outputPath, code, "utf-8");
|
|
330
|
+
logger.stopSpinnerSuccess(`Created: ${outputPath}`);
|
|
331
|
+
logger.newline();
|
|
332
|
+
logger.box(
|
|
333
|
+
`\u2728 ${componentName} component added!
|
|
334
|
+
|
|
335
|
+
File: ${outputPath}
|
|
336
|
+
Color: ${options.color ?? "default"}
|
|
337
|
+
Size: ${options.size ?? "default"}`,
|
|
338
|
+
{ title: "Success", borderColor: "green" }
|
|
339
|
+
);
|
|
340
|
+
logger.newline();
|
|
341
|
+
logger.info("Usage:");
|
|
342
|
+
logger.code(` import { ${componentName} } from './components/${componentName}';`);
|
|
343
|
+
logger.code(
|
|
344
|
+
` <${componentName} color="${options.color ?? "primary"}" size="${options.size ?? "md"}" />`
|
|
345
|
+
);
|
|
346
|
+
logger.newline();
|
|
347
|
+
} catch (error) {
|
|
348
|
+
logger.stopSpinner();
|
|
349
|
+
logger.error("Failed to add component");
|
|
350
|
+
if (error instanceof Error) {
|
|
351
|
+
logger.debug(error.stack || error.message);
|
|
352
|
+
}
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
async function promptVariantConfig() {
|
|
357
|
+
const answers = await import_inquirer.default.prompt([
|
|
358
|
+
{
|
|
359
|
+
type: "list",
|
|
360
|
+
name: "component",
|
|
361
|
+
message: "Select component:",
|
|
362
|
+
choices: AVAILABLE_COMPONENTS.map((c) => ({ name: capitalize(c), value: c }))
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
type: "list",
|
|
366
|
+
name: "color",
|
|
367
|
+
message: "Color:",
|
|
368
|
+
choices: [
|
|
369
|
+
new import_inquirer.default.Separator("--- Semantic ---"),
|
|
370
|
+
{ name: "Primary (Blue)", value: "primary" },
|
|
371
|
+
{ name: "Secondary (Purple)", value: "secondary" },
|
|
372
|
+
{ name: "Success (Green)", value: "success" },
|
|
373
|
+
{ name: "Warning (Yellow)", value: "warning" },
|
|
374
|
+
{ name: "Error (Red)", value: "error" },
|
|
375
|
+
{ name: "Info (Cyan)", value: "info" },
|
|
376
|
+
new import_inquirer.default.Separator("--- Palette ---"),
|
|
377
|
+
{ name: "Blue", value: "blue" },
|
|
378
|
+
{ name: "Red", value: "red" },
|
|
379
|
+
{ name: "Green", value: "green" },
|
|
380
|
+
{ name: "Yellow", value: "yellow" },
|
|
381
|
+
{ name: "Purple", value: "purple" },
|
|
382
|
+
{ name: "Gray", value: "gray" },
|
|
383
|
+
{ name: "Orange", value: "orange" },
|
|
384
|
+
{ name: "Pink", value: "pink" }
|
|
385
|
+
],
|
|
386
|
+
default: "blue"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
type: "list",
|
|
390
|
+
name: "size",
|
|
391
|
+
message: "Size:",
|
|
392
|
+
choices: [
|
|
393
|
+
{ name: "Extra Small (xs)", value: "xs" },
|
|
394
|
+
{ name: "Small (sm)", value: "sm" },
|
|
395
|
+
{ name: "Medium (md)", value: "md" },
|
|
396
|
+
{ name: "Large (lg)", value: "lg" },
|
|
397
|
+
{ name: "Extra Large (xl)", value: "xl" },
|
|
398
|
+
{ name: "2X Large (2xl)", value: "2xl" }
|
|
399
|
+
],
|
|
400
|
+
default: "md"
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
type: "list",
|
|
404
|
+
name: "variant",
|
|
405
|
+
message: "Variant:",
|
|
406
|
+
choices: (answers2) => {
|
|
407
|
+
const comp = answers2.component;
|
|
408
|
+
return (VARIANTS[comp] || ["solid", "outline", "ghost"]).map((v) => ({
|
|
409
|
+
name: capitalize(v),
|
|
410
|
+
value: v
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
type: "list",
|
|
416
|
+
name: "shape",
|
|
417
|
+
message: "Shape:",
|
|
418
|
+
choices: SHAPES.map((s) => ({ name: capitalize(s), value: s })),
|
|
419
|
+
default: "rounded"
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
type: "list",
|
|
423
|
+
name: "animation",
|
|
424
|
+
message: "Animation:",
|
|
425
|
+
choices: ANIMATIONS.map((a) => ({ name: capitalize(a), value: a })),
|
|
426
|
+
default: "standard"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
type: "list",
|
|
430
|
+
name: "elevation",
|
|
431
|
+
message: "Elevation/Shadow:",
|
|
432
|
+
choices: ELEVATIONS.map((e) => ({ name: e === "none" ? "None" : e.toUpperCase(), value: e })),
|
|
433
|
+
default: "md"
|
|
434
|
+
}
|
|
435
|
+
]);
|
|
436
|
+
return answers;
|
|
437
|
+
}
|
|
438
|
+
function capitalize(str) {
|
|
439
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
440
|
+
}
|
|
441
|
+
function generateComponentCode(component, config) {
|
|
442
|
+
const name = capitalize(component);
|
|
443
|
+
const propsInterface = `${name}Props`;
|
|
444
|
+
return `/**
|
|
445
|
+
* ${name} Component
|
|
446
|
+
*
|
|
447
|
+
* Generated with Nikkory Vibe CLI
|
|
448
|
+
* Configuration: color=${config.color}, size=${config.size}, variant=${config.variant || "default"}
|
|
449
|
+
*/
|
|
450
|
+
|
|
451
|
+
'use client';
|
|
452
|
+
|
|
453
|
+
import React, { forwardRef } from 'react';
|
|
454
|
+
import { ${name} as Vibe${name} } from '@nikkory/vibe-library/core';
|
|
455
|
+
|
|
456
|
+
export interface ${propsInterface} {
|
|
457
|
+
children?: React.ReactNode;
|
|
458
|
+
className?: string;
|
|
459
|
+
// Override default config
|
|
460
|
+
color?: string;
|
|
461
|
+
size?: string;
|
|
462
|
+
variant?: string;
|
|
463
|
+
shape?: string;
|
|
464
|
+
animation?: string;
|
|
465
|
+
elevation?: string;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Pre-configured ${name} with:
|
|
470
|
+
* - Color: ${config.color}
|
|
471
|
+
* - Size: ${config.size}
|
|
472
|
+
* - Variant: ${config.variant || "default"}
|
|
473
|
+
* - Shape: ${config.shape}
|
|
474
|
+
* - Animation: ${config.animation}
|
|
475
|
+
* - Elevation: ${config.elevation}
|
|
476
|
+
*/
|
|
477
|
+
export const ${name} = forwardRef<HTMLElement, ${propsInterface}>(
|
|
478
|
+
(
|
|
479
|
+
{
|
|
480
|
+
children,
|
|
481
|
+
className = '',
|
|
482
|
+
color = '${config.color}',
|
|
483
|
+
size = '${config.size}',
|
|
484
|
+
variant = '${config.variant || "solid"}',
|
|
485
|
+
shape = '${config.shape}',
|
|
486
|
+
animation = '${config.animation}',
|
|
487
|
+
elevation = '${config.elevation}',
|
|
488
|
+
...props
|
|
489
|
+
},
|
|
490
|
+
ref
|
|
491
|
+
) => {
|
|
492
|
+
return (
|
|
493
|
+
<Vibe${name}
|
|
494
|
+
ref={ref}
|
|
495
|
+
color={color}
|
|
496
|
+
size={size}
|
|
497
|
+
variant={variant}
|
|
498
|
+
shape={shape}
|
|
499
|
+
animation={animation}
|
|
500
|
+
elevation={elevation}
|
|
501
|
+
className={className}
|
|
502
|
+
{...props}
|
|
503
|
+
>
|
|
504
|
+
{children}
|
|
505
|
+
</Vibe${name}>
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
${name}.displayName = '${name}';
|
|
511
|
+
|
|
512
|
+
export default ${name};
|
|
513
|
+
`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/commands/copy.ts
|
|
517
|
+
var fsSync = __toESM(require("fs"));
|
|
518
|
+
var fs2 = __toESM(require("fs/promises"));
|
|
519
|
+
var path2 = __toESM(require("path"));
|
|
520
|
+
var import_commander2 = require("commander");
|
|
521
|
+
var DESIGN_SYSTEMS = [
|
|
522
|
+
"material-design",
|
|
523
|
+
"ios-hig",
|
|
524
|
+
"glassmorphism",
|
|
525
|
+
"neumorphism",
|
|
526
|
+
"minimalism",
|
|
527
|
+
"brutalism"
|
|
528
|
+
];
|
|
529
|
+
var TIERS = ["basic", "standard", "enterprise"];
|
|
530
|
+
var COMPONENTS = [
|
|
531
|
+
// Core
|
|
532
|
+
"button",
|
|
533
|
+
"input",
|
|
534
|
+
"textarea",
|
|
535
|
+
"select",
|
|
536
|
+
"checkbox",
|
|
537
|
+
"radio",
|
|
538
|
+
"switch",
|
|
539
|
+
"slider",
|
|
540
|
+
// Display
|
|
541
|
+
"badge",
|
|
542
|
+
"avatar",
|
|
543
|
+
"card",
|
|
544
|
+
"tooltip",
|
|
545
|
+
"popover",
|
|
546
|
+
// Feedback
|
|
547
|
+
"alert",
|
|
548
|
+
"toast",
|
|
549
|
+
"progress",
|
|
550
|
+
"spinner",
|
|
551
|
+
"skeleton",
|
|
552
|
+
// Navigation
|
|
553
|
+
"tabs",
|
|
554
|
+
"breadcrumb",
|
|
555
|
+
"pagination",
|
|
556
|
+
"stepper",
|
|
557
|
+
"menu",
|
|
558
|
+
// Overlay
|
|
559
|
+
"modal",
|
|
560
|
+
"drawer",
|
|
561
|
+
"dropdown",
|
|
562
|
+
"dialog",
|
|
563
|
+
// Data
|
|
564
|
+
"table",
|
|
565
|
+
"list",
|
|
566
|
+
"tree",
|
|
567
|
+
"timeline",
|
|
568
|
+
// Media
|
|
569
|
+
"image",
|
|
570
|
+
"video",
|
|
571
|
+
"audio",
|
|
572
|
+
"carousel",
|
|
573
|
+
"gallery",
|
|
574
|
+
// Form
|
|
575
|
+
"form",
|
|
576
|
+
"field",
|
|
577
|
+
"label",
|
|
578
|
+
"error-message",
|
|
579
|
+
// Layout
|
|
580
|
+
"container",
|
|
581
|
+
"grid",
|
|
582
|
+
"flex",
|
|
583
|
+
"divider",
|
|
584
|
+
"spacer",
|
|
585
|
+
// Typography
|
|
586
|
+
"heading",
|
|
587
|
+
"text",
|
|
588
|
+
"link",
|
|
589
|
+
"code",
|
|
590
|
+
// Charts
|
|
591
|
+
"chart",
|
|
592
|
+
"sparkline",
|
|
593
|
+
"gauge",
|
|
594
|
+
// Special
|
|
595
|
+
"rating",
|
|
596
|
+
"transfer",
|
|
597
|
+
"color-picker",
|
|
598
|
+
"date-picker",
|
|
599
|
+
"file-upload"
|
|
600
|
+
];
|
|
601
|
+
var DESIGN_ALIASES = {
|
|
602
|
+
material: "material-design",
|
|
603
|
+
md: "material-design",
|
|
604
|
+
ios: "ios-hig",
|
|
605
|
+
hig: "ios-hig",
|
|
606
|
+
glass: "glassmorphism",
|
|
607
|
+
neu: "neumorphism",
|
|
608
|
+
neomorphism: "neumorphism",
|
|
609
|
+
minimal: "minimalism",
|
|
610
|
+
min: "minimalism",
|
|
611
|
+
brutal: "brutalism"
|
|
612
|
+
};
|
|
613
|
+
var copyCommand = new import_commander2.Command("copy").alias("cp").description("Copy pre-built components from the library (zero-token)").argument(
|
|
614
|
+
"[component]",
|
|
615
|
+
"Component to copy (button, card, etc. or path like button/glassmorphism/enterprise)"
|
|
616
|
+
).option("-d, --design <design>", "Design system", "material-design").option("-t, --tier <tier>", "Quality tier", "standard").option("-o, --output <path>", "Output directory", "./components").option("--overwrite", "Overwrite existing files").option("--dry-run", "Preview without copying").option("--all", "Copy all components for the design/tier").action(async (component, options) => {
|
|
617
|
+
try {
|
|
618
|
+
logger.header("\u{1F4CB} Nikkory Vibe - Zero-Token Copy");
|
|
619
|
+
if (options.all) {
|
|
620
|
+
await copyAllComponents(options);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (!component) {
|
|
624
|
+
logger.error("Please specify a component to copy");
|
|
625
|
+
logger.newline();
|
|
626
|
+
logger.info("Usage:");
|
|
627
|
+
logger.code(" vibe copy button");
|
|
628
|
+
logger.code(" vibe copy button/glassmorphism");
|
|
629
|
+
logger.code(" vibe copy button/glassmorphism/enterprise");
|
|
630
|
+
logger.code(" vibe copy --all --design=brutalism");
|
|
631
|
+
logger.newline();
|
|
632
|
+
logger.info("Available components:");
|
|
633
|
+
logger.code(` ${COMPONENTS.slice(0, 10).join(", ")}...`);
|
|
634
|
+
logger.info(` Run 'vibe list templates' for full list`);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
const parsed = parseComponentPath(component, options);
|
|
638
|
+
if (!validateComponentPath(parsed)) {
|
|
639
|
+
process.exit(1);
|
|
640
|
+
}
|
|
641
|
+
logger.newline();
|
|
642
|
+
logger.info(`Component: ${capitalize2(parsed.component)}`);
|
|
643
|
+
logger.info(`Design System: ${formatDesignSystem(parsed.designSystem)}`);
|
|
644
|
+
logger.info(`Tier: ${capitalize2(parsed.tier)}`);
|
|
645
|
+
logger.newline();
|
|
646
|
+
await copyComponent(parsed, options);
|
|
647
|
+
} catch (error) {
|
|
648
|
+
logger.stopSpinner();
|
|
649
|
+
logger.error("Copy failed");
|
|
650
|
+
if (error instanceof Error) {
|
|
651
|
+
logger.debug(error.stack || error.message);
|
|
652
|
+
}
|
|
653
|
+
process.exit(1);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
function parseComponentPath(input, options) {
|
|
657
|
+
const parts = input.split("/").filter(Boolean);
|
|
658
|
+
const component = parts[0] ?? "";
|
|
659
|
+
let designSystem = options.design ?? "material-design";
|
|
660
|
+
let tier = options.tier ?? "standard";
|
|
661
|
+
if (parts.length === 2 && parts[1]) {
|
|
662
|
+
designSystem = normalizeDesignSystem(parts[1]);
|
|
663
|
+
} else if (parts.length >= 3 && parts[1] && parts[2]) {
|
|
664
|
+
designSystem = normalizeDesignSystem(parts[1]);
|
|
665
|
+
tier = parts[2];
|
|
666
|
+
}
|
|
667
|
+
designSystem = normalizeDesignSystem(designSystem);
|
|
668
|
+
return { component, designSystem, tier };
|
|
669
|
+
}
|
|
670
|
+
function normalizeDesignSystem(design) {
|
|
671
|
+
const lower = design.toLowerCase();
|
|
672
|
+
return DESIGN_ALIASES[lower] || lower;
|
|
673
|
+
}
|
|
674
|
+
function validateComponentPath(parsed) {
|
|
675
|
+
if (!COMPONENTS.includes(parsed.component)) {
|
|
676
|
+
logger.error(`Unknown component: ${parsed.component}`);
|
|
677
|
+
logger.info(`Available: ${COMPONENTS.slice(0, 8).join(", ")}...`);
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
if (!DESIGN_SYSTEMS.includes(parsed.designSystem)) {
|
|
681
|
+
logger.error(`Unknown design system: ${parsed.designSystem}`);
|
|
682
|
+
logger.info(`Available: ${DESIGN_SYSTEMS.join(", ")}`);
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
if (!TIERS.includes(parsed.tier)) {
|
|
686
|
+
logger.error(`Unknown tier: ${parsed.tier}`);
|
|
687
|
+
logger.info(`Available: ${TIERS.join(", ")}`);
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
async function copyComponent(parsed, options) {
|
|
693
|
+
const { component, designSystem, tier } = parsed;
|
|
694
|
+
const libraryRoot = findLibraryRoot();
|
|
695
|
+
const sourcePath = path2.join(
|
|
696
|
+
libraryRoot,
|
|
697
|
+
"src",
|
|
698
|
+
"components",
|
|
699
|
+
component,
|
|
700
|
+
designSystem,
|
|
701
|
+
`${tier}.tsx`
|
|
702
|
+
);
|
|
703
|
+
const sourceExists = await fileExists(sourcePath);
|
|
704
|
+
if (!sourceExists) {
|
|
705
|
+
logger.error(`Component not found: ${sourcePath}`);
|
|
706
|
+
logger.info("This component may not be available yet.");
|
|
707
|
+
logger.info("Run `vibe list templates` to see available components.");
|
|
708
|
+
process.exit(1);
|
|
709
|
+
}
|
|
710
|
+
const outputDir = path2.join(process.cwd(), options.output || "./components");
|
|
711
|
+
const fileName = `${capitalize2(component)}.tsx`;
|
|
712
|
+
const outputPath = path2.join(outputDir, fileName);
|
|
713
|
+
const outputExists = await fileExists(outputPath);
|
|
714
|
+
if (outputExists && !options.overwrite) {
|
|
715
|
+
logger.error(`File already exists: ${outputPath}`);
|
|
716
|
+
logger.info("Use --overwrite to replace it");
|
|
717
|
+
process.exit(1);
|
|
718
|
+
}
|
|
719
|
+
logger.startSpinner(`Reading ${component}...`);
|
|
720
|
+
const sourceCode = await fs2.readFile(sourcePath, "utf-8");
|
|
721
|
+
logger.stopSpinnerSuccess("Source loaded");
|
|
722
|
+
const transformedCode = transformComponent(sourceCode, parsed);
|
|
723
|
+
if (options.dryRun) {
|
|
724
|
+
logger.newline();
|
|
725
|
+
logger.header("Preview (Dry Run)");
|
|
726
|
+
logger.code(`${transformedCode.slice(0, 1e3)}
|
|
727
|
+
...`);
|
|
728
|
+
logger.newline();
|
|
729
|
+
logger.info(`Would write to: ${outputPath}`);
|
|
730
|
+
logger.info(`Lines: ${transformedCode.split("\n").length}`);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
logger.startSpinner(`Writing to ${outputPath}...`);
|
|
734
|
+
await fs2.mkdir(outputDir, { recursive: true });
|
|
735
|
+
await fs2.writeFile(outputPath, transformedCode, "utf-8");
|
|
736
|
+
logger.stopSpinnerSuccess("Component copied");
|
|
737
|
+
logger.newline();
|
|
738
|
+
logger.box(
|
|
739
|
+
`\u2728 ${capitalize2(component)} copied successfully!
|
|
740
|
+
|
|
741
|
+
File: ${outputPath}
|
|
742
|
+
Design: ${formatDesignSystem(designSystem)}
|
|
743
|
+
Tier: ${capitalize2(tier)}
|
|
744
|
+
Lines: ${transformedCode.split("\n").length}`,
|
|
745
|
+
{ title: "Success", borderColor: "green" }
|
|
746
|
+
);
|
|
747
|
+
logger.newline();
|
|
748
|
+
logger.info("Usage:");
|
|
749
|
+
logger.code(
|
|
750
|
+
` import { ${capitalize2(component)} } from './components/${capitalize2(component)}';`
|
|
751
|
+
);
|
|
752
|
+
logger.newline();
|
|
753
|
+
}
|
|
754
|
+
async function copyAllComponents(options) {
|
|
755
|
+
const designSystem = normalizeDesignSystem(options.design || "material-design");
|
|
756
|
+
const tier = options.tier || "standard";
|
|
757
|
+
logger.newline();
|
|
758
|
+
logger.info(`Copying all components...`);
|
|
759
|
+
logger.info(`Design System: ${formatDesignSystem(designSystem)}`);
|
|
760
|
+
logger.info(`Tier: ${capitalize2(tier)}`);
|
|
761
|
+
logger.newline();
|
|
762
|
+
const libraryRoot = findLibraryRoot();
|
|
763
|
+
let copied = 0;
|
|
764
|
+
let skipped = 0;
|
|
765
|
+
const errors = [];
|
|
766
|
+
for (const component of COMPONENTS) {
|
|
767
|
+
const sourcePath = path2.join(
|
|
768
|
+
libraryRoot,
|
|
769
|
+
"src",
|
|
770
|
+
"components",
|
|
771
|
+
component,
|
|
772
|
+
designSystem,
|
|
773
|
+
`${tier}.tsx`
|
|
774
|
+
);
|
|
775
|
+
const exists = await fileExists(sourcePath);
|
|
776
|
+
if (!exists) {
|
|
777
|
+
skipped++;
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
await copyComponent({ component, designSystem, tier }, { ...options, all: false });
|
|
782
|
+
copied++;
|
|
783
|
+
} catch (error) {
|
|
784
|
+
errors.push(component);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
logger.newline();
|
|
788
|
+
logger.box(
|
|
789
|
+
`\u{1F4E6} Bulk Copy Complete
|
|
790
|
+
|
|
791
|
+
Copied: ${copied} components
|
|
792
|
+
Skipped: ${skipped} (not available)
|
|
793
|
+
Errors: ${errors.length}`,
|
|
794
|
+
{ title: "Summary", borderColor: copied > 0 ? "green" : "yellow" }
|
|
795
|
+
);
|
|
796
|
+
if (errors.length > 0) {
|
|
797
|
+
logger.warn(`Failed: ${errors.join(", ")}`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function transformComponent(code, parsed) {
|
|
801
|
+
const { component, designSystem, tier } = parsed;
|
|
802
|
+
const header = `/**
|
|
803
|
+
* ${capitalize2(component)} Component
|
|
804
|
+
*
|
|
805
|
+
* Copied from @nikkory/vibe-library
|
|
806
|
+
* Design System: ${formatDesignSystem(designSystem)}
|
|
807
|
+
* Tier: ${capitalize2(tier)}
|
|
808
|
+
*
|
|
809
|
+
* Zero-token generation - No AI needed
|
|
810
|
+
* Just copy, paste, and customize!
|
|
811
|
+
*/
|
|
812
|
+
|
|
813
|
+
`;
|
|
814
|
+
let transformed = code;
|
|
815
|
+
transformed = transformed.replace(/from ['"]@nikkory\/vibe-library\/core['"]/g, "from './core'");
|
|
816
|
+
transformed = transformed.replace(/from ['"]@nikkory\/vibe-library['"]/g, "from './index'");
|
|
817
|
+
if (!transformed.includes("'use client'")) {
|
|
818
|
+
transformed = `'use client';
|
|
819
|
+
|
|
820
|
+
${transformed}`;
|
|
821
|
+
}
|
|
822
|
+
return `${header}${transformed}`;
|
|
823
|
+
}
|
|
824
|
+
function findLibraryRoot() {
|
|
825
|
+
const possiblePaths = [
|
|
826
|
+
path2.join(process.cwd(), "node_modules", "@nikkory", "vibe-library"),
|
|
827
|
+
path2.join(process.cwd(), "..", "..", "packages", "library"),
|
|
828
|
+
path2.join(__dirname, "..", "..", "..", "library"),
|
|
829
|
+
// Development path
|
|
830
|
+
"m:/AI Workspace/nikkory-vibe/packages/library"
|
|
831
|
+
];
|
|
832
|
+
for (const p of possiblePaths) {
|
|
833
|
+
try {
|
|
834
|
+
fsSync.accessSync(p);
|
|
835
|
+
return p;
|
|
836
|
+
} catch {
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return possiblePaths[possiblePaths.length - 1] ?? "m:/AI Workspace/nikkory-vibe/packages/library";
|
|
840
|
+
}
|
|
841
|
+
async function fileExists(filePath) {
|
|
842
|
+
try {
|
|
843
|
+
await fs2.access(filePath);
|
|
844
|
+
return true;
|
|
845
|
+
} catch {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function capitalize2(str) {
|
|
850
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
851
|
+
}
|
|
852
|
+
function formatDesignSystem(design) {
|
|
853
|
+
const names = {
|
|
854
|
+
"material-design": "Material Design",
|
|
855
|
+
"ios-hig": "iOS HIG",
|
|
856
|
+
glassmorphism: "Glassmorphism",
|
|
857
|
+
neumorphism: "Neumorphism",
|
|
858
|
+
minimalism: "Minimalism",
|
|
859
|
+
brutalism: "Brutalism"
|
|
860
|
+
};
|
|
861
|
+
return names[design] || capitalize2(design);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/commands/generate.ts
|
|
865
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
866
|
+
var path3 = __toESM(require("path"));
|
|
867
|
+
var import_vibe_core = require("@nikkory/vibe-core");
|
|
868
|
+
var import_commander3 = require("commander");
|
|
869
|
+
|
|
870
|
+
// src/utils/prompts.ts
|
|
871
|
+
var import_inquirer2 = __toESM(require("inquirer"));
|
|
872
|
+
async function promptComponentConfig() {
|
|
873
|
+
const answers = await import_inquirer2.default.prompt([
|
|
874
|
+
{
|
|
875
|
+
type: "input",
|
|
876
|
+
name: "name",
|
|
877
|
+
message: "Component name (PascalCase):",
|
|
878
|
+
validate: (input) => {
|
|
879
|
+
if (!input) return "Component name is required";
|
|
880
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(input)) {
|
|
881
|
+
return "Component name must be PascalCase (e.g., MyButton)";
|
|
882
|
+
}
|
|
883
|
+
return true;
|
|
884
|
+
}
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
type: "list",
|
|
888
|
+
name: "template",
|
|
889
|
+
message: "Template type:",
|
|
890
|
+
choices: [
|
|
891
|
+
{ name: "Button", value: "button" },
|
|
892
|
+
{ name: "Card", value: "card" },
|
|
893
|
+
{ name: "Input", value: "input" },
|
|
894
|
+
{ name: "Modal", value: "modal" },
|
|
895
|
+
{ name: "Table", value: "table" }
|
|
896
|
+
]
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
type: "list",
|
|
900
|
+
name: "designSystem",
|
|
901
|
+
message: "Design system:",
|
|
902
|
+
choices: [
|
|
903
|
+
{ name: "Material Design 3", value: "material-design" },
|
|
904
|
+
{ name: "iOS Human Interface Guidelines", value: "ios-hig" },
|
|
905
|
+
{ name: "Glassmorphism", value: "glassmorphism" },
|
|
906
|
+
{ name: "Neumorphism", value: "neumorphism" },
|
|
907
|
+
{ name: "Minimalism", value: "minimalism" }
|
|
908
|
+
]
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
type: "list",
|
|
912
|
+
name: "tier",
|
|
913
|
+
message: "Quality tier:",
|
|
914
|
+
choices: [
|
|
915
|
+
{ name: "Basic (Prototype)", value: "basic" },
|
|
916
|
+
{ name: "Standard (Production)", value: "standard" },
|
|
917
|
+
{ name: "Enterprise (Mission-critical)", value: "enterprise" }
|
|
918
|
+
],
|
|
919
|
+
default: "standard"
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
type: "list",
|
|
923
|
+
name: "framework",
|
|
924
|
+
message: "Framework:",
|
|
925
|
+
choices: [
|
|
926
|
+
{ name: "React", value: "react" },
|
|
927
|
+
{ name: "Vue", value: "vue" },
|
|
928
|
+
{ name: "Angular", value: "angular" },
|
|
929
|
+
{ name: "Svelte", value: "svelte" },
|
|
930
|
+
{ name: "Solid", value: "solid" }
|
|
931
|
+
],
|
|
932
|
+
default: "react"
|
|
933
|
+
}
|
|
934
|
+
]);
|
|
935
|
+
return answers;
|
|
936
|
+
}
|
|
937
|
+
async function promptProjectInit() {
|
|
938
|
+
const answers = await import_inquirer2.default.prompt([
|
|
939
|
+
{
|
|
940
|
+
type: "input",
|
|
941
|
+
name: "projectName",
|
|
942
|
+
message: "Project name:",
|
|
943
|
+
validate: (input) => {
|
|
944
|
+
if (!input) return "Project name is required";
|
|
945
|
+
if (!/^[a-z][a-z0-9-]*$/.test(input)) {
|
|
946
|
+
return "Project name must be lowercase kebab-case (e.g., my-app)";
|
|
947
|
+
}
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
type: "list",
|
|
953
|
+
name: "framework",
|
|
954
|
+
message: "Frontend framework:",
|
|
955
|
+
choices: [
|
|
956
|
+
{ name: "React (Next.js)", value: "react" },
|
|
957
|
+
{ name: "Vue (Nuxt)", value: "vue" },
|
|
958
|
+
{ name: "Angular", value: "angular" },
|
|
959
|
+
{ name: "Svelte (SvelteKit)", value: "svelte" },
|
|
960
|
+
{ name: "Solid (SolidStart)", value: "solid" }
|
|
961
|
+
],
|
|
962
|
+
default: "react"
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
type: "list",
|
|
966
|
+
name: "designSystem",
|
|
967
|
+
message: "Default design system:",
|
|
968
|
+
choices: [
|
|
969
|
+
{ name: "Material Design 3", value: "material-design" },
|
|
970
|
+
{ name: "iOS HIG", value: "ios-hig" },
|
|
971
|
+
{ name: "Glassmorphism", value: "glassmorphism" },
|
|
972
|
+
{ name: "Custom", value: "custom" }
|
|
973
|
+
],
|
|
974
|
+
default: "material-design"
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
type: "confirm",
|
|
978
|
+
name: "includeBackend",
|
|
979
|
+
message: "Include backend (NestJS)?",
|
|
980
|
+
default: false
|
|
981
|
+
}
|
|
982
|
+
]);
|
|
983
|
+
return answers;
|
|
984
|
+
}
|
|
985
|
+
async function promptSectionConfig() {
|
|
986
|
+
const answers = await import_inquirer2.default.prompt([
|
|
987
|
+
{
|
|
988
|
+
type: "input",
|
|
989
|
+
name: "name",
|
|
990
|
+
message: "Section name (PascalCase):",
|
|
991
|
+
validate: (input) => {
|
|
992
|
+
if (!input) return "Section name is required";
|
|
993
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(input)) {
|
|
994
|
+
return "Section name must be PascalCase (e.g., HeroSection)";
|
|
995
|
+
}
|
|
996
|
+
return true;
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
type: "list",
|
|
1001
|
+
name: "template",
|
|
1002
|
+
message: "Section template:",
|
|
1003
|
+
choices: [
|
|
1004
|
+
// Hero sections
|
|
1005
|
+
{ name: "Hero - Centered", value: "hero-centered" },
|
|
1006
|
+
{ name: "Hero - Split (Image + Text)", value: "hero-split" },
|
|
1007
|
+
{ name: "Hero - Background Image", value: "hero-background" },
|
|
1008
|
+
{ name: "Hero - Video Background", value: "hero-video" },
|
|
1009
|
+
// Features sections
|
|
1010
|
+
{ name: "Features - Grid", value: "features-grid" },
|
|
1011
|
+
{ name: "Features - List", value: "features-list" },
|
|
1012
|
+
{ name: "Features - Alternating", value: "features-alternating" },
|
|
1013
|
+
// CTA sections
|
|
1014
|
+
{ name: "CTA - Banner", value: "cta-banner" },
|
|
1015
|
+
{ name: "CTA - Centered", value: "cta-centered" },
|
|
1016
|
+
{ name: "CTA - Split", value: "cta-split" },
|
|
1017
|
+
// Pricing sections
|
|
1018
|
+
{ name: "Pricing - Cards", value: "pricing-cards" },
|
|
1019
|
+
{ name: "Pricing - Table", value: "pricing-table" },
|
|
1020
|
+
// Testimonial sections
|
|
1021
|
+
{ name: "Testimonials - Grid", value: "testimonials-grid" },
|
|
1022
|
+
{ name: "Testimonials - Carousel", value: "testimonials-carousel" },
|
|
1023
|
+
// Other sections
|
|
1024
|
+
{ name: "FAQ - Accordion", value: "faq-accordion" },
|
|
1025
|
+
{ name: "Team - Grid", value: "team-grid" },
|
|
1026
|
+
{ name: "Stats - Grid", value: "stats-grid" },
|
|
1027
|
+
{ name: "Logo Cloud", value: "logo-cloud" },
|
|
1028
|
+
{ name: "Newsletter", value: "newsletter" },
|
|
1029
|
+
{ name: "Contact Form", value: "contact-form" }
|
|
1030
|
+
]
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
type: "list",
|
|
1034
|
+
name: "designSystem",
|
|
1035
|
+
message: "Design system:",
|
|
1036
|
+
choices: [
|
|
1037
|
+
{ name: "Material Design 3", value: "material-design" },
|
|
1038
|
+
{ name: "iOS Human Interface Guidelines", value: "ios-hig" },
|
|
1039
|
+
{ name: "Glassmorphism", value: "glassmorphism" },
|
|
1040
|
+
{ name: "Neumorphism", value: "neumorphism" },
|
|
1041
|
+
{ name: "Brutalism", value: "brutalism" },
|
|
1042
|
+
{ name: "Minimalism", value: "minimalism" }
|
|
1043
|
+
]
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
type: "list",
|
|
1047
|
+
name: "tier",
|
|
1048
|
+
message: "Quality tier:",
|
|
1049
|
+
choices: [
|
|
1050
|
+
{ name: "Basic (Prototype)", value: "basic" },
|
|
1051
|
+
{ name: "Standard (Production)", value: "standard" },
|
|
1052
|
+
{ name: "Enterprise (Mission-critical)", value: "enterprise" }
|
|
1053
|
+
],
|
|
1054
|
+
default: "standard"
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
type: "list",
|
|
1058
|
+
name: "framework",
|
|
1059
|
+
message: "Framework:",
|
|
1060
|
+
choices: [
|
|
1061
|
+
{ name: "React", value: "react" },
|
|
1062
|
+
{ name: "Vue", value: "vue" },
|
|
1063
|
+
{ name: "Angular", value: "angular" },
|
|
1064
|
+
{ name: "Svelte", value: "svelte" },
|
|
1065
|
+
{ name: "Solid", value: "solid" }
|
|
1066
|
+
],
|
|
1067
|
+
default: "react"
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
type: "input",
|
|
1071
|
+
name: "heading",
|
|
1072
|
+
message: "Section heading:",
|
|
1073
|
+
default: "Welcome to Your App"
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
type: "input",
|
|
1077
|
+
name: "subheading",
|
|
1078
|
+
message: "Section subheading (optional):",
|
|
1079
|
+
default: ""
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
type: "input",
|
|
1083
|
+
name: "description",
|
|
1084
|
+
message: "Section description (optional):",
|
|
1085
|
+
default: ""
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
type: "input",
|
|
1089
|
+
name: "ctaText",
|
|
1090
|
+
message: "Call-to-action button text:",
|
|
1091
|
+
default: "Get Started"
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
type: "input",
|
|
1095
|
+
name: "ctaHref",
|
|
1096
|
+
message: "Call-to-action button link:",
|
|
1097
|
+
default: "#"
|
|
1098
|
+
}
|
|
1099
|
+
]);
|
|
1100
|
+
return answers;
|
|
1101
|
+
}
|
|
1102
|
+
async function promptGenerationType() {
|
|
1103
|
+
const answer = await import_inquirer2.default.prompt([
|
|
1104
|
+
{
|
|
1105
|
+
type: "list",
|
|
1106
|
+
name: "type",
|
|
1107
|
+
message: "What do you want to generate?",
|
|
1108
|
+
choices: [
|
|
1109
|
+
{ name: "Component (Button, Card, Input, etc.)", value: "component" },
|
|
1110
|
+
{ name: "Section (Hero, Features, CTA, etc.)", value: "section" },
|
|
1111
|
+
{ name: "Page (Landing, Dashboard, Auth, etc.)", value: "page" }
|
|
1112
|
+
]
|
|
1113
|
+
}
|
|
1114
|
+
]);
|
|
1115
|
+
return answer.type;
|
|
1116
|
+
}
|
|
1117
|
+
async function promptPageConfig() {
|
|
1118
|
+
const answers = await import_inquirer2.default.prompt([
|
|
1119
|
+
{
|
|
1120
|
+
type: "input",
|
|
1121
|
+
name: "name",
|
|
1122
|
+
message: "Page name (PascalCase):",
|
|
1123
|
+
validate: (input) => {
|
|
1124
|
+
if (!input) return "Page name is required";
|
|
1125
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(input)) {
|
|
1126
|
+
return "Page name must be PascalCase (e.g., HomePage)";
|
|
1127
|
+
}
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
type: "list",
|
|
1133
|
+
name: "template",
|
|
1134
|
+
message: "Page template:",
|
|
1135
|
+
choices: [
|
|
1136
|
+
// Landing pages
|
|
1137
|
+
{ name: "Landing - SaaS Product", value: "landing-saas" },
|
|
1138
|
+
{ name: "Landing - Product Showcase", value: "landing-product" },
|
|
1139
|
+
{ name: "Landing - Agency", value: "landing-agency" },
|
|
1140
|
+
{ name: "Landing - Startup", value: "landing-startup" },
|
|
1141
|
+
{ name: "Landing - Mobile App", value: "landing-app" },
|
|
1142
|
+
// Marketing pages
|
|
1143
|
+
{ name: "Marketing - Simple", value: "marketing-simple" },
|
|
1144
|
+
{ name: "Marketing - Feature", value: "marketing-feature" },
|
|
1145
|
+
{ name: "Marketing - Pricing", value: "marketing-pricing" },
|
|
1146
|
+
// Dashboard pages
|
|
1147
|
+
{ name: "Dashboard - Overview", value: "dashboard-overview" },
|
|
1148
|
+
{ name: "Dashboard - Analytics", value: "dashboard-analytics" },
|
|
1149
|
+
{ name: "Dashboard - Settings", value: "dashboard-settings" },
|
|
1150
|
+
// Auth pages
|
|
1151
|
+
{ name: "Auth - Login", value: "auth-login" },
|
|
1152
|
+
{ name: "Auth - Register", value: "auth-register" },
|
|
1153
|
+
{ name: "Auth - Forgot Password", value: "auth-forgot-password" },
|
|
1154
|
+
// E-commerce pages
|
|
1155
|
+
{ name: "E-commerce - Home", value: "ecommerce-home" },
|
|
1156
|
+
{ name: "E-commerce - Product", value: "ecommerce-product" },
|
|
1157
|
+
{ name: "E-commerce - Cart", value: "ecommerce-cart" },
|
|
1158
|
+
// Error pages
|
|
1159
|
+
{ name: "Error - 404 Not Found", value: "error-404" },
|
|
1160
|
+
{ name: "Error - 500 Server Error", value: "error-500" },
|
|
1161
|
+
{ name: "Error - Maintenance", value: "error-maintenance" },
|
|
1162
|
+
// Content pages
|
|
1163
|
+
{ name: "Content - Blog List", value: "content-blog-list" },
|
|
1164
|
+
{ name: "Content - Blog Post", value: "content-blog-post" },
|
|
1165
|
+
{ name: "Content - About", value: "content-about" },
|
|
1166
|
+
{ name: "Content - Contact", value: "content-contact" }
|
|
1167
|
+
]
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
type: "list",
|
|
1171
|
+
name: "designSystem",
|
|
1172
|
+
message: "Design system:",
|
|
1173
|
+
choices: [
|
|
1174
|
+
{ name: "Material Design 3", value: "material-design" },
|
|
1175
|
+
{ name: "iOS Human Interface Guidelines", value: "ios-hig" },
|
|
1176
|
+
{ name: "Glassmorphism", value: "glassmorphism" },
|
|
1177
|
+
{ name: "Neumorphism", value: "neumorphism" },
|
|
1178
|
+
{ name: "Brutalism", value: "brutalism" },
|
|
1179
|
+
{ name: "Minimalism", value: "minimalism" }
|
|
1180
|
+
]
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
type: "list",
|
|
1184
|
+
name: "tier",
|
|
1185
|
+
message: "Quality tier:",
|
|
1186
|
+
choices: [
|
|
1187
|
+
{ name: "Basic (Prototype)", value: "basic" },
|
|
1188
|
+
{ name: "Standard (Production)", value: "standard" },
|
|
1189
|
+
{ name: "Enterprise (Mission-critical)", value: "enterprise" }
|
|
1190
|
+
],
|
|
1191
|
+
default: "standard"
|
|
1192
|
+
},
|
|
1193
|
+
{
|
|
1194
|
+
type: "list",
|
|
1195
|
+
name: "framework",
|
|
1196
|
+
message: "Framework:",
|
|
1197
|
+
choices: [
|
|
1198
|
+
{ name: "React", value: "react" },
|
|
1199
|
+
{ name: "Next.js", value: "nextjs" },
|
|
1200
|
+
{ name: "Remix", value: "remix" },
|
|
1201
|
+
{ name: "Astro", value: "astro" }
|
|
1202
|
+
],
|
|
1203
|
+
default: "react"
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
type: "input",
|
|
1207
|
+
name: "title",
|
|
1208
|
+
message: "Page title (for SEO):",
|
|
1209
|
+
default: "My Page"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
type: "input",
|
|
1213
|
+
name: "description",
|
|
1214
|
+
message: "Page description (for SEO):",
|
|
1215
|
+
default: "A great page built with Nikkory Vibe"
|
|
1216
|
+
}
|
|
1217
|
+
]);
|
|
1218
|
+
return answers;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// src/commands/generate.ts
|
|
1222
|
+
var generateCommand = new import_commander3.Command("generate").alias("g").description("Generate components, sections, or pages from templates").argument("[type]", "Type to generate (component, section, page)").option("-n, --name <name>", "Name (PascalCase)").option("-t, --template <template>", "Template type").option("-d, --design <system>", "Design system", "material-design").option("--tier <tier>", "Quality tier (basic, standard, enterprise)", "standard").option("-f, --framework <framework>", "Framework (react, vue, etc.)", "react").option("-o, --output <path>", "Output file path").option("--dry-run", "Preview generated code without writing").option("--interactive", "Interactive mode with prompts").option("--heading <text>", "Section heading (for sections)").option("--subheading <text>", "Section subheading (for sections)").option("--description <text>", "Section description (for sections)").option("--cta-text <text>", "CTA button text (for sections)").option("--cta-href <url>", "CTA button link (for sections)").option("--title <text>", "Page title for SEO (for pages)").action(async (type, options) => {
|
|
1223
|
+
try {
|
|
1224
|
+
logger.header("\u{1F3A8} Nikkory Vibe - Code Generator");
|
|
1225
|
+
let generationType = type;
|
|
1226
|
+
if (!generationType || options.interactive) {
|
|
1227
|
+
generationType = await promptGenerationType();
|
|
1228
|
+
}
|
|
1229
|
+
switch (generationType) {
|
|
1230
|
+
case "component":
|
|
1231
|
+
await generateComponent(options);
|
|
1232
|
+
break;
|
|
1233
|
+
case "section":
|
|
1234
|
+
await generateSection(options);
|
|
1235
|
+
break;
|
|
1236
|
+
case "page":
|
|
1237
|
+
await generatePage(options);
|
|
1238
|
+
break;
|
|
1239
|
+
default:
|
|
1240
|
+
await generateComponent(options);
|
|
1241
|
+
}
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
logger.stopSpinner();
|
|
1244
|
+
logger.error("Unexpected error occurred");
|
|
1245
|
+
if (error instanceof Error) {
|
|
1246
|
+
logger.debug(error.stack || error.message);
|
|
1247
|
+
}
|
|
1248
|
+
process.exit(1);
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
async function generateComponent(options) {
|
|
1252
|
+
let config;
|
|
1253
|
+
if (options.interactive ?? !options.name) {
|
|
1254
|
+
config = await promptComponentConfig();
|
|
1255
|
+
} else {
|
|
1256
|
+
config = {
|
|
1257
|
+
name: options.name ?? "",
|
|
1258
|
+
template: options.template ?? "button",
|
|
1259
|
+
framework: options.framework ?? "react",
|
|
1260
|
+
designSystem: options.design ?? "material-design",
|
|
1261
|
+
tier: options.tier ?? "standard"
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
if (!config.name) {
|
|
1265
|
+
logger.error("Component name is required");
|
|
1266
|
+
process.exit(1);
|
|
1267
|
+
}
|
|
1268
|
+
logger.newline();
|
|
1269
|
+
logger.info(`Generating ${config.tier} ${config.template} component...`);
|
|
1270
|
+
logger.info(`Design System: ${config.designSystem}`);
|
|
1271
|
+
logger.info(`Framework: ${config.framework}`);
|
|
1272
|
+
logger.newline();
|
|
1273
|
+
logger.startSpinner("Loading template...");
|
|
1274
|
+
const generator = new import_vibe_core.ComponentGenerator();
|
|
1275
|
+
const result = await generator.generate({
|
|
1276
|
+
name: config.name,
|
|
1277
|
+
template: config.template,
|
|
1278
|
+
designSystem: config.designSystem,
|
|
1279
|
+
tier: config.tier,
|
|
1280
|
+
framework: config.framework,
|
|
1281
|
+
outputPath: options.output
|
|
1282
|
+
});
|
|
1283
|
+
if (result.isFailure()) {
|
|
1284
|
+
logger.stopSpinnerFail("Generation failed");
|
|
1285
|
+
logger.error(result.getError().message);
|
|
1286
|
+
process.exit(1);
|
|
1287
|
+
}
|
|
1288
|
+
logger.stopSpinnerSuccess("Template loaded");
|
|
1289
|
+
const code = result.getValue();
|
|
1290
|
+
if (options.dryRun) {
|
|
1291
|
+
logger.newline();
|
|
1292
|
+
logger.header("Generated Code (Preview)");
|
|
1293
|
+
logger.code(code);
|
|
1294
|
+
logger.newline();
|
|
1295
|
+
logger.info("Dry run mode - No files written");
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
const outputPath = options.output ?? generateComponentPath(config);
|
|
1299
|
+
logger.startSpinner(`Writing to ${outputPath}...`);
|
|
1300
|
+
await fs3.mkdir(path3.dirname(outputPath), { recursive: true });
|
|
1301
|
+
await fs3.writeFile(outputPath, code, "utf-8");
|
|
1302
|
+
logger.stopSpinnerSuccess(`Component created: ${outputPath}`);
|
|
1303
|
+
logger.newline();
|
|
1304
|
+
logger.box(
|
|
1305
|
+
`\u2728 ${config.name} component generated successfully!
|
|
1306
|
+
|
|
1307
|
+
File: ${outputPath}
|
|
1308
|
+
Lines: ${code.split("\n").length}
|
|
1309
|
+
Design: ${config.designSystem}
|
|
1310
|
+
Tier: ${config.tier}`,
|
|
1311
|
+
{ title: "Success", borderColor: "green" }
|
|
1312
|
+
);
|
|
1313
|
+
printNextSteps("component");
|
|
1314
|
+
}
|
|
1315
|
+
async function generateSection(options) {
|
|
1316
|
+
let config;
|
|
1317
|
+
if (options.interactive ?? !options.name) {
|
|
1318
|
+
config = await promptSectionConfig();
|
|
1319
|
+
} else {
|
|
1320
|
+
config = {
|
|
1321
|
+
name: options.name ?? "",
|
|
1322
|
+
template: options.template ?? "hero-centered",
|
|
1323
|
+
framework: options.framework ?? "react",
|
|
1324
|
+
designSystem: options.design ?? "material-design",
|
|
1325
|
+
tier: options.tier ?? "standard",
|
|
1326
|
+
heading: options.heading ?? "Welcome to Your App",
|
|
1327
|
+
subheading: options.subheading ?? "",
|
|
1328
|
+
description: options.description ?? "",
|
|
1329
|
+
ctaText: options.ctaText ?? "Get Started",
|
|
1330
|
+
ctaHref: options.ctaHref ?? "#"
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
if (!config.name) {
|
|
1334
|
+
logger.error("Section name is required");
|
|
1335
|
+
process.exit(1);
|
|
1336
|
+
}
|
|
1337
|
+
logger.newline();
|
|
1338
|
+
logger.info(`Generating ${config.tier} ${config.template} section...`);
|
|
1339
|
+
logger.info(`Design System: ${config.designSystem}`);
|
|
1340
|
+
logger.info(`Framework: ${config.framework}`);
|
|
1341
|
+
logger.newline();
|
|
1342
|
+
const sectionConfig = {
|
|
1343
|
+
name: config.name,
|
|
1344
|
+
template: config.template,
|
|
1345
|
+
designSystem: config.designSystem,
|
|
1346
|
+
tier: config.tier,
|
|
1347
|
+
framework: config.framework,
|
|
1348
|
+
outputPath: options.output,
|
|
1349
|
+
content: {
|
|
1350
|
+
heading: config.heading,
|
|
1351
|
+
subheading: config.subheading || void 0,
|
|
1352
|
+
description: config.description || void 0,
|
|
1353
|
+
cta: config.ctaText ? {
|
|
1354
|
+
text: config.ctaText,
|
|
1355
|
+
href: config.ctaHref || "#"
|
|
1356
|
+
} : void 0
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
logger.startSpinner("Generating section...");
|
|
1360
|
+
const generator = new import_vibe_core.SectionGenerator();
|
|
1361
|
+
const result = await generator.generateSection(sectionConfig);
|
|
1362
|
+
if (result.isFailure()) {
|
|
1363
|
+
logger.stopSpinnerFail("Generation failed");
|
|
1364
|
+
logger.error(result.getError().message);
|
|
1365
|
+
process.exit(1);
|
|
1366
|
+
}
|
|
1367
|
+
logger.stopSpinnerSuccess("Section generated");
|
|
1368
|
+
const sectionResult = result.getValue();
|
|
1369
|
+
if (options.dryRun) {
|
|
1370
|
+
logger.newline();
|
|
1371
|
+
logger.header("Generated Code (Preview)");
|
|
1372
|
+
logger.code(sectionResult.code);
|
|
1373
|
+
logger.newline();
|
|
1374
|
+
logger.info("Dry run mode - No files written");
|
|
1375
|
+
logger.newline();
|
|
1376
|
+
logger.info("Metadata:");
|
|
1377
|
+
logger.info(` Template: ${sectionResult.metadata.template}`);
|
|
1378
|
+
logger.info(` Lines of code: ${sectionResult.metadata.linesOfCode}`);
|
|
1379
|
+
logger.info(` Child components: ${sectionResult.metadata.childComponentCount}`);
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
const outputPath = options.output ?? generateSectionPath(config);
|
|
1383
|
+
logger.startSpinner(`Writing to ${outputPath}...`);
|
|
1384
|
+
await fs3.mkdir(path3.dirname(outputPath), { recursive: true });
|
|
1385
|
+
await fs3.writeFile(outputPath, sectionResult.code, "utf-8");
|
|
1386
|
+
logger.stopSpinnerSuccess(`Section created: ${outputPath}`);
|
|
1387
|
+
logger.newline();
|
|
1388
|
+
logger.box(
|
|
1389
|
+
`\u2728 ${config.name} section generated successfully!
|
|
1390
|
+
|
|
1391
|
+
File: ${outputPath}
|
|
1392
|
+
Template: ${config.template}
|
|
1393
|
+
Lines: ${sectionResult.metadata.linesOfCode}
|
|
1394
|
+
Design: ${config.designSystem}
|
|
1395
|
+
Tier: ${config.tier}`,
|
|
1396
|
+
{ title: "Success", borderColor: "green" }
|
|
1397
|
+
);
|
|
1398
|
+
printNextSteps("section");
|
|
1399
|
+
}
|
|
1400
|
+
async function generatePage(options) {
|
|
1401
|
+
let config;
|
|
1402
|
+
if (options.interactive ?? !options.name) {
|
|
1403
|
+
config = await promptPageConfig();
|
|
1404
|
+
} else {
|
|
1405
|
+
config = {
|
|
1406
|
+
name: options.name ?? "",
|
|
1407
|
+
template: options.template ?? "landing-saas",
|
|
1408
|
+
framework: options.framework ?? "react",
|
|
1409
|
+
designSystem: options.design ?? "material-design",
|
|
1410
|
+
tier: options.tier ?? "standard",
|
|
1411
|
+
title: options.title ?? "My Page",
|
|
1412
|
+
description: options.description ?? ""
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
if (!config.name) {
|
|
1416
|
+
logger.error("Page name is required");
|
|
1417
|
+
process.exit(1);
|
|
1418
|
+
}
|
|
1419
|
+
logger.newline();
|
|
1420
|
+
logger.info(`Generating ${config.tier} ${config.template} page...`);
|
|
1421
|
+
logger.info(`Design System: ${config.designSystem}`);
|
|
1422
|
+
logger.info(`Framework: ${config.framework}`);
|
|
1423
|
+
logger.newline();
|
|
1424
|
+
const pageConfig = {
|
|
1425
|
+
name: config.name,
|
|
1426
|
+
template: config.template,
|
|
1427
|
+
designSystem: config.designSystem,
|
|
1428
|
+
tier: config.tier,
|
|
1429
|
+
framework: config.framework,
|
|
1430
|
+
outputPath: options.output,
|
|
1431
|
+
meta: {
|
|
1432
|
+
title: config.title,
|
|
1433
|
+
description: config.description || void 0
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
logger.startSpinner("Generating page...");
|
|
1437
|
+
const generator = new import_vibe_core.PageGenerator();
|
|
1438
|
+
const result = await generator.generatePage(pageConfig);
|
|
1439
|
+
if (result.isFailure()) {
|
|
1440
|
+
logger.stopSpinnerFail("Generation failed");
|
|
1441
|
+
logger.error(result.getError().message);
|
|
1442
|
+
process.exit(1);
|
|
1443
|
+
}
|
|
1444
|
+
logger.stopSpinnerSuccess("Page generated");
|
|
1445
|
+
const pageResult = result.getValue();
|
|
1446
|
+
if (options.dryRun) {
|
|
1447
|
+
logger.newline();
|
|
1448
|
+
logger.header("Generated Code (Preview)");
|
|
1449
|
+
logger.code(pageResult.code);
|
|
1450
|
+
logger.newline();
|
|
1451
|
+
logger.info("Dry run mode - No files written");
|
|
1452
|
+
logger.newline();
|
|
1453
|
+
logger.info("Metadata:");
|
|
1454
|
+
logger.info(` Template: ${pageResult.metadata.template}`);
|
|
1455
|
+
logger.info(` Framework: ${pageResult.metadata.framework}`);
|
|
1456
|
+
logger.info(` Lines of code: ${pageResult.metadata.linesOfCode}`);
|
|
1457
|
+
logger.info(` Sections: ${pageResult.metadata.sectionCount}`);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
const outputPath = options.output ?? generatePagePath(config);
|
|
1461
|
+
logger.startSpinner(`Writing to ${outputPath}...`);
|
|
1462
|
+
await fs3.mkdir(path3.dirname(outputPath), { recursive: true });
|
|
1463
|
+
await fs3.writeFile(outputPath, pageResult.code, "utf-8");
|
|
1464
|
+
logger.stopSpinnerSuccess(`Page created: ${outputPath}`);
|
|
1465
|
+
logger.newline();
|
|
1466
|
+
logger.box(
|
|
1467
|
+
`\u2728 ${config.name} page generated successfully!
|
|
1468
|
+
|
|
1469
|
+
File: ${outputPath}
|
|
1470
|
+
Template: ${config.template}
|
|
1471
|
+
Lines: ${pageResult.metadata.linesOfCode}
|
|
1472
|
+
Framework: ${config.framework}
|
|
1473
|
+
Design: ${config.designSystem}
|
|
1474
|
+
Tier: ${config.tier}`,
|
|
1475
|
+
{ title: "Success", borderColor: "green" }
|
|
1476
|
+
);
|
|
1477
|
+
printNextSteps("page");
|
|
1478
|
+
}
|
|
1479
|
+
function generateComponentPath(config) {
|
|
1480
|
+
const ext = getFileExtension(config.framework);
|
|
1481
|
+
const fileName = `${config.name}.${ext}`;
|
|
1482
|
+
return path3.join(process.cwd(), "src", "components", fileName);
|
|
1483
|
+
}
|
|
1484
|
+
function generateSectionPath(config) {
|
|
1485
|
+
const ext = getFileExtension(config.framework);
|
|
1486
|
+
const fileName = `${config.name}.${ext}`;
|
|
1487
|
+
return path3.join(process.cwd(), "src", "sections", fileName);
|
|
1488
|
+
}
|
|
1489
|
+
function generatePagePath(config) {
|
|
1490
|
+
const ext = getFileExtension(config.framework);
|
|
1491
|
+
const fileName = `${config.name}.${ext}`;
|
|
1492
|
+
const pageDir = config.framework === "nextjs" ? "app" : "pages";
|
|
1493
|
+
return path3.join(process.cwd(), "src", pageDir, fileName);
|
|
1494
|
+
}
|
|
1495
|
+
function getFileExtension(framework) {
|
|
1496
|
+
const extensions = {
|
|
1497
|
+
react: "tsx",
|
|
1498
|
+
vue: "vue",
|
|
1499
|
+
angular: "component.ts",
|
|
1500
|
+
svelte: "svelte",
|
|
1501
|
+
solid: "tsx"
|
|
1502
|
+
};
|
|
1503
|
+
return extensions[framework] || "tsx";
|
|
1504
|
+
}
|
|
1505
|
+
function printNextSteps(type) {
|
|
1506
|
+
logger.newline();
|
|
1507
|
+
logger.info("Next steps:");
|
|
1508
|
+
if (type === "component") {
|
|
1509
|
+
logger.code(" 1. Import the component in your app");
|
|
1510
|
+
logger.code(" 2. Review and customize as needed");
|
|
1511
|
+
logger.code(" 3. Run your dev server to test");
|
|
1512
|
+
} else if (type === "section") {
|
|
1513
|
+
logger.code(" 1. Import the section in your page");
|
|
1514
|
+
logger.code(" 2. Customize content props (heading, description, etc.)");
|
|
1515
|
+
logger.code(" 3. Add your own images and assets");
|
|
1516
|
+
logger.code(" 4. Run your dev server to preview");
|
|
1517
|
+
} else {
|
|
1518
|
+
logger.code(" 1. Set up routing for your page");
|
|
1519
|
+
logger.code(" 2. Customize page content and sections");
|
|
1520
|
+
logger.code(" 3. Add SEO meta tags if needed");
|
|
1521
|
+
logger.code(" 4. Run your dev server to preview");
|
|
1522
|
+
}
|
|
1523
|
+
logger.newline();
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// src/commands/init.ts
|
|
1527
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
1528
|
+
var path4 = __toESM(require("path"));
|
|
1529
|
+
var import_commander4 = require("commander");
|
|
1530
|
+
function generateClaudeMd(config) {
|
|
1531
|
+
const tier = config.tier || "standard";
|
|
1532
|
+
return `# Claude Code Instructions - ${config.projectName}
|
|
1533
|
+
|
|
1534
|
+
> Auto-generated by Nikkory Vibe. AI Agent will automatically follow these quality rules.
|
|
1535
|
+
|
|
1536
|
+
## Project Overview
|
|
1537
|
+
|
|
1538
|
+
- **Framework**: ${config.framework}
|
|
1539
|
+
- **Design System**: ${config.designSystem}
|
|
1540
|
+
- **Styling**: TailwindCSS
|
|
1541
|
+
- **Quality Tier**: ${tier}
|
|
1542
|
+
|
|
1543
|
+
---
|
|
1544
|
+
|
|
1545
|
+
# \u{1F534} AI AGENT INSPECTOR - MANDATORY QUALITY RULES
|
|
1546
|
+
|
|
1547
|
+
**CRITICAL**: These rules are AUTOMATICALLY ENFORCED. AI Agent MUST follow ALL rules below for EVERY code change. No exceptions. No simplification.
|
|
1548
|
+
|
|
1549
|
+
## ESLint Zero-Error Rules (MANDATORY)
|
|
1550
|
+
|
|
1551
|
+
### Import Order (ALWAYS enforce)
|
|
1552
|
+
|
|
1553
|
+
\`\`\`typescript
|
|
1554
|
+
// \u2705 CORRECT - React first, blank line between groups
|
|
1555
|
+
import React, { useState, useCallback, memo, forwardRef } from 'react';
|
|
1556
|
+
|
|
1557
|
+
import { cn } from '@/lib/utils';
|
|
1558
|
+
import { Button } from '@/components';
|
|
1559
|
+
|
|
1560
|
+
// \u274C WRONG - No blank lines, wrong order
|
|
1561
|
+
import { Button } from '@/components';
|
|
1562
|
+
import React from 'react';
|
|
1563
|
+
import { cn } from '@/lib/utils';
|
|
1564
|
+
\`\`\`
|
|
1565
|
+
|
|
1566
|
+
### Explicit Return Types (ALWAYS add)
|
|
1567
|
+
|
|
1568
|
+
\`\`\`typescript
|
|
1569
|
+
// \u2705 CORRECT
|
|
1570
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>): void => {
|
|
1571
|
+
console.log(e);
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
// \u274C WRONG - Missing return type
|
|
1575
|
+
const handleClick = (e) => {
|
|
1576
|
+
console.log(e);
|
|
1577
|
+
};
|
|
1578
|
+
\`\`\`
|
|
1579
|
+
|
|
1580
|
+
### No Unused Variables (ALWAYS prefix with _)
|
|
1581
|
+
|
|
1582
|
+
\`\`\`typescript
|
|
1583
|
+
// \u2705 CORRECT
|
|
1584
|
+
const { name, age: _age, email } = user; // _age unused but valid
|
|
1585
|
+
|
|
1586
|
+
// \u274C WRONG
|
|
1587
|
+
const { name, age, email } = user; // age unused = ERROR
|
|
1588
|
+
\`\`\`
|
|
1589
|
+
|
|
1590
|
+
### No \`any\` Type (ALWAYS use \`unknown\`)
|
|
1591
|
+
|
|
1592
|
+
\`\`\`typescript
|
|
1593
|
+
// \u2705 CORRECT
|
|
1594
|
+
const handleData = (data: unknown): void => {};
|
|
1595
|
+
|
|
1596
|
+
// \u274C WRONG
|
|
1597
|
+
const handleData = (data: any): void => {};
|
|
1598
|
+
\`\`\`
|
|
1599
|
+
|
|
1600
|
+
### No Non-Null Assertion (ALWAYS use optional chaining)
|
|
1601
|
+
|
|
1602
|
+
\`\`\`typescript
|
|
1603
|
+
// \u2705 CORRECT
|
|
1604
|
+
ref.current?.focus();
|
|
1605
|
+
|
|
1606
|
+
// \u274C WRONG
|
|
1607
|
+
ref.current!.focus();
|
|
1608
|
+
\`\`\`
|
|
1609
|
+
|
|
1610
|
+
## TypeScript Strict Mode (MANDATORY)
|
|
1611
|
+
|
|
1612
|
+
| Pattern | Required | Example |
|
|
1613
|
+
|---------|----------|---------|
|
|
1614
|
+
| Explicit return types | YES | \`(): void\`, \`(): string\` |
|
|
1615
|
+
| No implicit any | YES | Always type parameters |
|
|
1616
|
+
| Strict null checks | YES | Use \`?.\` and \`??\` |
|
|
1617
|
+
| Interface over type | YES | For object shapes |
|
|
1618
|
+
|
|
1619
|
+
## React Component Patterns
|
|
1620
|
+
|
|
1621
|
+
### Basic Tier
|
|
1622
|
+
|
|
1623
|
+
\`\`\`typescript
|
|
1624
|
+
import React from 'react';
|
|
1625
|
+
|
|
1626
|
+
interface ComponentProps {
|
|
1627
|
+
children?: React.ReactNode;
|
|
1628
|
+
className?: string;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
export const Component: React.FC<ComponentProps> = ({
|
|
1632
|
+
children,
|
|
1633
|
+
className,
|
|
1634
|
+
}) => {
|
|
1635
|
+
return <div className={className}>{children}</div>;
|
|
1636
|
+
};
|
|
1637
|
+
\`\`\`
|
|
1638
|
+
|
|
1639
|
+
### Standard Tier (forwardRef + displayName)
|
|
1640
|
+
|
|
1641
|
+
\`\`\`typescript
|
|
1642
|
+
import React, { forwardRef } from 'react';
|
|
1643
|
+
|
|
1644
|
+
interface ComponentProps {
|
|
1645
|
+
children?: React.ReactNode;
|
|
1646
|
+
variant?: 'primary' | 'secondary';
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
export const Component = forwardRef<HTMLDivElement, ComponentProps>(
|
|
1650
|
+
({ children, variant = 'primary', ...props }, ref) => {
|
|
1651
|
+
return <div ref={ref} {...props}>{children}</div>;
|
|
1652
|
+
}
|
|
1653
|
+
);
|
|
1654
|
+
|
|
1655
|
+
Component.displayName = 'Component'; // REQUIRED!
|
|
1656
|
+
\`\`\`
|
|
1657
|
+
|
|
1658
|
+
### Enterprise Tier (memo + forwardRef + displayName)
|
|
1659
|
+
|
|
1660
|
+
\`\`\`typescript
|
|
1661
|
+
import React, { memo, forwardRef, useCallback } from 'react';
|
|
1662
|
+
|
|
1663
|
+
interface ComponentProps {
|
|
1664
|
+
children?: React.ReactNode;
|
|
1665
|
+
analyticsEvent?: string;
|
|
1666
|
+
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
export const Component = memo(
|
|
1670
|
+
forwardRef<HTMLDivElement, ComponentProps>(
|
|
1671
|
+
({ children, analyticsEvent, onClick, ...props }, ref) => {
|
|
1672
|
+
const handleClick = useCallback(
|
|
1673
|
+
(e: React.MouseEvent<HTMLDivElement>): void => {
|
|
1674
|
+
if (analyticsEvent && typeof window !== 'undefined') {
|
|
1675
|
+
(window as unknown as { dataLayer?: unknown[] }).dataLayer?.push({
|
|
1676
|
+
event: analyticsEvent,
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
onClick?.(e);
|
|
1680
|
+
},
|
|
1681
|
+
[analyticsEvent, onClick]
|
|
1682
|
+
);
|
|
1683
|
+
|
|
1684
|
+
return <div ref={ref} onClick={handleClick} {...props}>{children}</div>;
|
|
1685
|
+
}
|
|
1686
|
+
)
|
|
1687
|
+
);
|
|
1688
|
+
|
|
1689
|
+
Component.displayName = 'Component'; // REQUIRED!
|
|
1690
|
+
\`\`\`
|
|
1691
|
+
|
|
1692
|
+
## Debugging Rules (NEVER SKIP)
|
|
1693
|
+
|
|
1694
|
+
When debugging or fixing issues:
|
|
1695
|
+
|
|
1696
|
+
1. **NEVER simplify code** - Fix the actual issue, don't remove features
|
|
1697
|
+
2. **NEVER remove types** - Keep all TypeScript annotations
|
|
1698
|
+
3. **NEVER use \`any\`** - Use \`unknown\` if type is unclear
|
|
1699
|
+
4. **ALWAYS preserve patterns** - forwardRef, memo, displayName
|
|
1700
|
+
5. **ALWAYS run lint check** - \`pnpm lint\` before considering done
|
|
1701
|
+
6. **ALWAYS check types** - \`pnpm type-check\` before considering done
|
|
1702
|
+
|
|
1703
|
+
## Pre-Commit Checklist (AUTO-ENFORCE)
|
|
1704
|
+
|
|
1705
|
+
Before ANY commit, AI Agent MUST verify:
|
|
1706
|
+
|
|
1707
|
+
- [ ] All imports have correct order with blank lines
|
|
1708
|
+
- [ ] All functions have explicit return types
|
|
1709
|
+
- [ ] No unused variables (prefix with _ if intentionally unused)
|
|
1710
|
+
- [ ] No \`any\` types (use \`unknown\` instead)
|
|
1711
|
+
- [ ] No non-null assertions (use \`?.\` instead)
|
|
1712
|
+
- [ ] forwardRef components have displayName
|
|
1713
|
+
- [ ] Enterprise components use memo()
|
|
1714
|
+
- [ ] useEffect cleanup has \`: void\` return type
|
|
1715
|
+
- [ ] Event handlers have proper React event types
|
|
1716
|
+
|
|
1717
|
+
## Quick Fix Reference
|
|
1718
|
+
|
|
1719
|
+
| Error | Quick Fix |
|
|
1720
|
+
|-------|-----------|
|
|
1721
|
+
| \`import/order\` | Add blank line between import groups |
|
|
1722
|
+
| \`explicit-function-return-type\` | Add \`: void\`, \`: string\`, etc. |
|
|
1723
|
+
| \`no-unused-vars\` | Prefix with \`_\` |
|
|
1724
|
+
| \`no-explicit-any\` | Use \`unknown\` |
|
|
1725
|
+
| \`no-non-null-assertion\` | Use \`?.\` optional chaining |
|
|
1726
|
+
| \`TS2304: Cannot find name\` | Add missing import |
|
|
1727
|
+
| \`TS2322: Type not assignable\` | Fix type or cast properly |
|
|
1728
|
+
| \`TS6133: Unused declaration\` | Remove or prefix with \`_\` |
|
|
1729
|
+
|
|
1730
|
+
---
|
|
1731
|
+
|
|
1732
|
+
**Powered by Nikkory Vibe**
|
|
1733
|
+
`;
|
|
1734
|
+
}
|
|
1735
|
+
var initCommand = new import_commander4.Command("init").description("Initialize a new Nikkory Vibe project with AI Agent Inspector").argument("[project-name]", "Project name (kebab-case)").option("-f, --framework <framework>", "Frontend framework", "react").option("-d, --design <system>", "Default design system", "material-design").option("-t, --tier <tier>", "Quality tier (basic, standard, enterprise)", "standard").option("--backend", "Include backend (NestJS)").option("--no-install", "Skip dependency installation").option("--no-claude", "Skip CLAUDE.md generation").action(
|
|
1736
|
+
async (projectName, options) => {
|
|
1737
|
+
try {
|
|
1738
|
+
logger.header("\u{1F3A8} Nikkory Vibe - Project Initialization");
|
|
1739
|
+
let config;
|
|
1740
|
+
if (!projectName) {
|
|
1741
|
+
const promptConfig = await promptProjectInit();
|
|
1742
|
+
projectName = promptConfig.projectName;
|
|
1743
|
+
config = {
|
|
1744
|
+
...promptConfig,
|
|
1745
|
+
tier: options.tier ?? "standard"
|
|
1746
|
+
};
|
|
1747
|
+
} else {
|
|
1748
|
+
config = {
|
|
1749
|
+
projectName,
|
|
1750
|
+
framework: options.framework ?? "react",
|
|
1751
|
+
designSystem: options.design ?? "material-design",
|
|
1752
|
+
tier: options.tier ?? "standard",
|
|
1753
|
+
includeBackend: options.backend ?? false
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
const projectPath = path4.join(process.cwd(), projectName);
|
|
1757
|
+
try {
|
|
1758
|
+
await fs4.access(projectPath);
|
|
1759
|
+
logger.error(`Directory "${projectName}" already exists`);
|
|
1760
|
+
process.exit(1);
|
|
1761
|
+
} catch {
|
|
1762
|
+
}
|
|
1763
|
+
logger.newline();
|
|
1764
|
+
logger.info(`Creating project: ${projectName}`);
|
|
1765
|
+
logger.info(`Framework: ${config.framework}`);
|
|
1766
|
+
logger.info(`Design System: ${config.designSystem}`);
|
|
1767
|
+
logger.info(`Quality Tier: ${config.tier}`);
|
|
1768
|
+
if (config.includeBackend) {
|
|
1769
|
+
logger.info(`Backend: NestJS`);
|
|
1770
|
+
}
|
|
1771
|
+
logger.newline();
|
|
1772
|
+
logger.startSpinner("Creating project structure...");
|
|
1773
|
+
await fs4.mkdir(projectPath, { recursive: true });
|
|
1774
|
+
await createProjectStructure(projectPath, config);
|
|
1775
|
+
logger.stopSpinnerSuccess("Project structure created");
|
|
1776
|
+
logger.startSpinner("Creating package.json...");
|
|
1777
|
+
await createPackageJson(projectPath, config);
|
|
1778
|
+
logger.stopSpinnerSuccess("package.json created");
|
|
1779
|
+
logger.startSpinner("Creating configuration files...");
|
|
1780
|
+
await createConfigFiles(projectPath, config);
|
|
1781
|
+
logger.stopSpinnerSuccess("Configuration files created");
|
|
1782
|
+
if (options.claude !== false) {
|
|
1783
|
+
logger.startSpinner("Creating CLAUDE.md (AI Agent Inspector)...");
|
|
1784
|
+
await createClaudeMd(projectPath, config);
|
|
1785
|
+
logger.stopSpinnerSuccess("CLAUDE.md created - AI Agent Inspector enabled");
|
|
1786
|
+
}
|
|
1787
|
+
logger.startSpinner("Creating README.md...");
|
|
1788
|
+
await createReadme(projectPath, config);
|
|
1789
|
+
logger.stopSpinnerSuccess("README.md created");
|
|
1790
|
+
logger.newline();
|
|
1791
|
+
logger.box(
|
|
1792
|
+
`\u2728 Project "${projectName}" created successfully!
|
|
1793
|
+
|
|
1794
|
+
Location: ${projectPath}
|
|
1795
|
+
Framework: ${config.framework}
|
|
1796
|
+
Design: ${config.designSystem}
|
|
1797
|
+
Tier: ${config.tier}
|
|
1798
|
+
AI Inspector: ${options.claude !== false ? "Enabled" : "Disabled"}`,
|
|
1799
|
+
{ title: "Success", borderColor: "green" }
|
|
1800
|
+
);
|
|
1801
|
+
if (options.claude !== false) {
|
|
1802
|
+
logger.newline();
|
|
1803
|
+
logger.info("\u{1F534} AI Agent Inspector is enabled!");
|
|
1804
|
+
logger.info(" Claude Code will automatically enforce quality rules.");
|
|
1805
|
+
logger.info(" No manual commands needed - just code!");
|
|
1806
|
+
}
|
|
1807
|
+
logger.newline();
|
|
1808
|
+
logger.info("Next steps:");
|
|
1809
|
+
logger.code(` cd ${projectName}`);
|
|
1810
|
+
if (options.install ?? true) {
|
|
1811
|
+
logger.code(` pnpm install`);
|
|
1812
|
+
}
|
|
1813
|
+
logger.code(` pnpm dev`);
|
|
1814
|
+
logger.newline();
|
|
1815
|
+
logger.info("Generate your first component:");
|
|
1816
|
+
logger.code(` vibe smart button --style glassmorphism --tier ${config.tier}`);
|
|
1817
|
+
logger.newline();
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
logger.stopSpinner();
|
|
1820
|
+
logger.error("Project initialization failed");
|
|
1821
|
+
if (error instanceof Error) {
|
|
1822
|
+
logger.debug(error.stack || error.message);
|
|
1823
|
+
}
|
|
1824
|
+
process.exit(1);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
);
|
|
1828
|
+
async function createProjectStructure(projectPath, config) {
|
|
1829
|
+
const dirs = ["src/components", "src/pages", "src/styles", "src/utils", "public", ".claude"];
|
|
1830
|
+
if (config.includeBackend) {
|
|
1831
|
+
dirs.push("api/src/modules", "api/src/common", "api/prisma");
|
|
1832
|
+
}
|
|
1833
|
+
for (const dir of dirs) {
|
|
1834
|
+
await fs4.mkdir(path4.join(projectPath, dir), { recursive: true });
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
async function createClaudeMd(projectPath, config) {
|
|
1838
|
+
const content = generateClaudeMd(config);
|
|
1839
|
+
await fs4.writeFile(path4.join(projectPath, "CLAUDE.md"), content, "utf-8");
|
|
1840
|
+
}
|
|
1841
|
+
async function createPackageJson(projectPath, config) {
|
|
1842
|
+
const packageJson = {
|
|
1843
|
+
name: config.projectName,
|
|
1844
|
+
version: "0.1.0",
|
|
1845
|
+
private: true,
|
|
1846
|
+
scripts: {
|
|
1847
|
+
dev: config.framework === "react" ? "next dev" : "vite dev",
|
|
1848
|
+
build: config.framework === "react" ? "next build" : "vite build",
|
|
1849
|
+
start: config.framework === "react" ? "next start" : "vite preview",
|
|
1850
|
+
lint: "eslint . --ext .ts,.tsx",
|
|
1851
|
+
"type-check": "tsc --noEmit"
|
|
1852
|
+
},
|
|
1853
|
+
dependencies: getFrameworkDependencies(config.framework),
|
|
1854
|
+
devDependencies: {
|
|
1855
|
+
"@types/node": "^20.0.0",
|
|
1856
|
+
"@types/react": "^18.0.0",
|
|
1857
|
+
"@types/react-dom": "^18.0.0",
|
|
1858
|
+
typescript: "^5.0.0",
|
|
1859
|
+
eslint: "^8.0.0",
|
|
1860
|
+
"@nikkory/vibe-cli": "^1.0.0"
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
await fs4.writeFile(
|
|
1864
|
+
path4.join(projectPath, "package.json"),
|
|
1865
|
+
JSON.stringify(packageJson, null, 2),
|
|
1866
|
+
"utf-8"
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
function getFrameworkDependencies(framework) {
|
|
1870
|
+
const deps = {
|
|
1871
|
+
react: {
|
|
1872
|
+
react: "^18.2.0",
|
|
1873
|
+
"react-dom": "^18.2.0",
|
|
1874
|
+
next: "^14.0.0",
|
|
1875
|
+
tailwindcss: "^3.4.0"
|
|
1876
|
+
},
|
|
1877
|
+
vue: {
|
|
1878
|
+
vue: "^3.3.0",
|
|
1879
|
+
nuxt: "^3.8.0",
|
|
1880
|
+
tailwindcss: "^3.4.0"
|
|
1881
|
+
},
|
|
1882
|
+
angular: {
|
|
1883
|
+
"@angular/core": "^17.0.0",
|
|
1884
|
+
"@angular/common": "^17.0.0",
|
|
1885
|
+
"@angular/platform-browser": "^17.0.0"
|
|
1886
|
+
},
|
|
1887
|
+
svelte: {
|
|
1888
|
+
svelte: "^4.0.0",
|
|
1889
|
+
"@sveltejs/kit": "^2.0.0",
|
|
1890
|
+
tailwindcss: "^3.4.0"
|
|
1891
|
+
},
|
|
1892
|
+
solid: {
|
|
1893
|
+
"solid-js": "^1.8.0",
|
|
1894
|
+
"solid-start": "^0.4.0",
|
|
1895
|
+
tailwindcss: "^3.4.0"
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
return deps[framework] ?? deps["react"] ?? {};
|
|
1899
|
+
}
|
|
1900
|
+
async function createConfigFiles(projectPath, _config) {
|
|
1901
|
+
const tsConfig = {
|
|
1902
|
+
compilerOptions: {
|
|
1903
|
+
target: "ES2020",
|
|
1904
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
1905
|
+
jsx: "preserve",
|
|
1906
|
+
module: "ESNext",
|
|
1907
|
+
moduleResolution: "bundler",
|
|
1908
|
+
resolveJsonModule: true,
|
|
1909
|
+
allowJs: true,
|
|
1910
|
+
strict: true,
|
|
1911
|
+
noImplicitAny: true,
|
|
1912
|
+
strictNullChecks: true,
|
|
1913
|
+
esModuleInterop: true,
|
|
1914
|
+
skipLibCheck: true,
|
|
1915
|
+
forceConsistentCasingInFileNames: true,
|
|
1916
|
+
incremental: true,
|
|
1917
|
+
paths: {
|
|
1918
|
+
"@/*": ["./src/*"]
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
include: ["src/**/*"],
|
|
1922
|
+
exclude: ["node_modules"]
|
|
1923
|
+
};
|
|
1924
|
+
await fs4.writeFile(
|
|
1925
|
+
path4.join(projectPath, "tsconfig.json"),
|
|
1926
|
+
JSON.stringify(tsConfig, null, 2),
|
|
1927
|
+
"utf-8"
|
|
1928
|
+
);
|
|
1929
|
+
const gitignore = `# Dependencies
|
|
1930
|
+
node_modules
|
|
1931
|
+
.pnpm-store
|
|
1932
|
+
|
|
1933
|
+
# Build output
|
|
1934
|
+
dist
|
|
1935
|
+
.next
|
|
1936
|
+
out
|
|
1937
|
+
build
|
|
1938
|
+
|
|
1939
|
+
# Environment
|
|
1940
|
+
.env
|
|
1941
|
+
.env.local
|
|
1942
|
+
.env.*.local
|
|
1943
|
+
|
|
1944
|
+
# IDE
|
|
1945
|
+
.vscode
|
|
1946
|
+
.idea
|
|
1947
|
+
*.swp
|
|
1948
|
+
*.swo
|
|
1949
|
+
|
|
1950
|
+
# OS
|
|
1951
|
+
.DS_Store
|
|
1952
|
+
Thumbs.db
|
|
1953
|
+
|
|
1954
|
+
# Logs
|
|
1955
|
+
*.log
|
|
1956
|
+
npm-debug.log*
|
|
1957
|
+
yarn-debug.log*
|
|
1958
|
+
yarn-error.log*
|
|
1959
|
+
`;
|
|
1960
|
+
await fs4.writeFile(path4.join(projectPath, ".gitignore"), gitignore, "utf-8");
|
|
1961
|
+
}
|
|
1962
|
+
async function createReadme(projectPath, config) {
|
|
1963
|
+
const readme = `# ${config.projectName}
|
|
1964
|
+
|
|
1965
|
+
Generated with **Nikkory Vibe** \u2728
|
|
1966
|
+
|
|
1967
|
+
## Tech Stack
|
|
1968
|
+
|
|
1969
|
+
- **Framework**: ${config.framework}
|
|
1970
|
+
- **Design System**: ${config.designSystem}
|
|
1971
|
+
- **Quality Tier**: ${config.tier}
|
|
1972
|
+
- **Styling**: TailwindCSS
|
|
1973
|
+
- **TypeScript**: 5.0+
|
|
1974
|
+
|
|
1975
|
+
## AI Agent Inspector
|
|
1976
|
+
|
|
1977
|
+
This project includes **AI Agent Inspector** via \`CLAUDE.md\`.
|
|
1978
|
+
When using Claude Code, AI will automatically:
|
|
1979
|
+
|
|
1980
|
+
- \u2705 Follow ESLint zero-error rules
|
|
1981
|
+
- \u2705 Use TypeScript strict mode patterns
|
|
1982
|
+
- \u2705 Apply correct React component patterns for ${config.tier} tier
|
|
1983
|
+
- \u2705 Never simplify code when debugging
|
|
1984
|
+
- \u2705 Run lint/type checks before completing tasks
|
|
1985
|
+
|
|
1986
|
+
**No manual commands needed - just code!**
|
|
1987
|
+
|
|
1988
|
+
## Getting Started
|
|
1989
|
+
|
|
1990
|
+
\`\`\`bash
|
|
1991
|
+
# Install dependencies
|
|
1992
|
+
pnpm install
|
|
1993
|
+
|
|
1994
|
+
# Run development server
|
|
1995
|
+
pnpm dev
|
|
1996
|
+
|
|
1997
|
+
# Build for production
|
|
1998
|
+
pnpm build
|
|
1999
|
+
\`\`\`
|
|
2000
|
+
|
|
2001
|
+
## Generate Components
|
|
2002
|
+
|
|
2003
|
+
\`\`\`bash
|
|
2004
|
+
# Generate a component with Smart Generator
|
|
2005
|
+
vibe smart button --style glassmorphism --tier ${config.tier}
|
|
2006
|
+
|
|
2007
|
+
# List all 50 design styles
|
|
2008
|
+
vibe smart --list-styles
|
|
2009
|
+
|
|
2010
|
+
# Generate all 3 tiers at once
|
|
2011
|
+
vibe smart card --style brutalism --all-tiers
|
|
2012
|
+
|
|
2013
|
+
# Preview without writing
|
|
2014
|
+
vibe smart input --style neumorphism --dry-run
|
|
2015
|
+
\`\`\`
|
|
2016
|
+
|
|
2017
|
+
## Code Quality
|
|
2018
|
+
|
|
2019
|
+
\`\`\`bash
|
|
2020
|
+
# Run ESLint
|
|
2021
|
+
pnpm lint
|
|
2022
|
+
|
|
2023
|
+
# Run TypeScript check
|
|
2024
|
+
pnpm type-check
|
|
2025
|
+
|
|
2026
|
+
# Run Code Doctor
|
|
2027
|
+
vibe doctor scan ./src
|
|
2028
|
+
\`\`\`
|
|
2029
|
+
|
|
2030
|
+
## Project Structure
|
|
2031
|
+
|
|
2032
|
+
\`\`\`
|
|
2033
|
+
${config.projectName}/
|
|
2034
|
+
\u251C\u2500\u2500 src/
|
|
2035
|
+
\u2502 \u251C\u2500\u2500 components/ # React components
|
|
2036
|
+
\u2502 \u251C\u2500\u2500 pages/ # Page components
|
|
2037
|
+
\u2502 \u251C\u2500\u2500 styles/ # Global styles
|
|
2038
|
+
\u2502 \u2514\u2500\u2500 utils/ # Utilities
|
|
2039
|
+
\u251C\u2500\u2500 public/ # Static assets
|
|
2040
|
+
\u251C\u2500\u2500 CLAUDE.md # AI Agent Inspector rules
|
|
2041
|
+
\u2514\u2500\u2500 package.json
|
|
2042
|
+
\`\`\`
|
|
2043
|
+
|
|
2044
|
+
## Learn More
|
|
2045
|
+
|
|
2046
|
+
- [Nikkory Vibe Documentation](https://vibe.nikkory.com/docs)
|
|
2047
|
+
- [Design Systems](https://vibe.nikkory.com/docs/design-systems)
|
|
2048
|
+
- [CLI Reference](https://vibe.nikkory.com/docs/cli)
|
|
2049
|
+
|
|
2050
|
+
---
|
|
2051
|
+
|
|
2052
|
+
Powered by Nikkory
|
|
2053
|
+
`;
|
|
2054
|
+
await fs4.writeFile(path4.join(projectPath, "README.md"), readme, "utf-8");
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/commands/list.ts
|
|
2058
|
+
var import_commander5 = require("commander");
|
|
2059
|
+
var listCommand = new import_commander5.Command("list").alias("ls").description("List available templates and design systems").argument("[category]", "Category to list (templates, designs, tiers)", "all").option("--json", "Output as JSON").action((category, options) => {
|
|
2060
|
+
try {
|
|
2061
|
+
if (options.json) {
|
|
2062
|
+
const data = getAllData();
|
|
2063
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
logger.header("\u{1F3A8} Nikkory Vibe - Available Options");
|
|
2067
|
+
switch (category) {
|
|
2068
|
+
case "templates":
|
|
2069
|
+
listTemplates();
|
|
2070
|
+
break;
|
|
2071
|
+
case "designs":
|
|
2072
|
+
case "design-systems":
|
|
2073
|
+
listDesignSystems();
|
|
2074
|
+
break;
|
|
2075
|
+
case "tiers":
|
|
2076
|
+
listTiers();
|
|
2077
|
+
break;
|
|
2078
|
+
case "frameworks":
|
|
2079
|
+
listFrameworks();
|
|
2080
|
+
break;
|
|
2081
|
+
case "all":
|
|
2082
|
+
default:
|
|
2083
|
+
listTemplates();
|
|
2084
|
+
logger.newline();
|
|
2085
|
+
listDesignSystems();
|
|
2086
|
+
logger.newline();
|
|
2087
|
+
listTiers();
|
|
2088
|
+
logger.newline();
|
|
2089
|
+
listFrameworks();
|
|
2090
|
+
break;
|
|
2091
|
+
}
|
|
2092
|
+
logger.newline();
|
|
2093
|
+
logger.info("Generate a component:");
|
|
2094
|
+
logger.code(" vibe generate component MyButton --design material-design");
|
|
2095
|
+
logger.newline();
|
|
2096
|
+
} catch (error) {
|
|
2097
|
+
logger.error("Failed to list options");
|
|
2098
|
+
if (error instanceof Error) {
|
|
2099
|
+
logger.debug(error.stack || error.message);
|
|
2100
|
+
}
|
|
2101
|
+
process.exit(1);
|
|
2102
|
+
}
|
|
2103
|
+
});
|
|
2104
|
+
function listTemplates() {
|
|
2105
|
+
logger.newline();
|
|
2106
|
+
logger.info("\u{1F4E6} Available Templates:");
|
|
2107
|
+
logger.separator();
|
|
2108
|
+
const templates = [
|
|
2109
|
+
{ name: "Button", value: "button", description: "Interactive button components" },
|
|
2110
|
+
{ name: "Card", value: "card", description: "Content container cards" },
|
|
2111
|
+
{ name: "Input", value: "input", description: "Form input fields" },
|
|
2112
|
+
{ name: "Modal", value: "modal", description: "Dialog/modal overlays" },
|
|
2113
|
+
{ name: "Table", value: "table", description: "Data tables" },
|
|
2114
|
+
{ name: "Form", value: "form", description: "Form layouts" },
|
|
2115
|
+
{ name: "Navigation", value: "navigation", description: "Nav bars and menus" },
|
|
2116
|
+
{ name: "Hero", value: "hero", description: "Hero sections" },
|
|
2117
|
+
{ name: "Feature", value: "feature", description: "Feature showcases" },
|
|
2118
|
+
{ name: "Pricing", value: "pricing", description: "Pricing tables" }
|
|
2119
|
+
];
|
|
2120
|
+
logger.table(
|
|
2121
|
+
templates.map((t) => ({
|
|
2122
|
+
Template: t.name,
|
|
2123
|
+
ID: t.value,
|
|
2124
|
+
Description: t.description
|
|
2125
|
+
}))
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
function listDesignSystems() {
|
|
2129
|
+
logger.newline();
|
|
2130
|
+
logger.info("\u{1F3A8} Available Design Systems:");
|
|
2131
|
+
logger.separator();
|
|
2132
|
+
const designs = [
|
|
2133
|
+
{
|
|
2134
|
+
name: "Material Design 3",
|
|
2135
|
+
value: "material-design",
|
|
2136
|
+
category: "Modern"
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
name: "iOS Human Interface Guidelines",
|
|
2140
|
+
value: "ios-hig",
|
|
2141
|
+
category: "Mobile"
|
|
2142
|
+
},
|
|
2143
|
+
{
|
|
2144
|
+
name: "Glassmorphism",
|
|
2145
|
+
value: "glassmorphism",
|
|
2146
|
+
category: "Modern"
|
|
2147
|
+
},
|
|
2148
|
+
{
|
|
2149
|
+
name: "Neumorphism",
|
|
2150
|
+
value: "neumorphism",
|
|
2151
|
+
category: "Modern"
|
|
2152
|
+
},
|
|
2153
|
+
{
|
|
2154
|
+
name: "Minimalism",
|
|
2155
|
+
value: "minimalism",
|
|
2156
|
+
category: "Clean"
|
|
2157
|
+
},
|
|
2158
|
+
{
|
|
2159
|
+
name: "Brutalism",
|
|
2160
|
+
value: "brutalism",
|
|
2161
|
+
category: "Bold"
|
|
2162
|
+
},
|
|
2163
|
+
{
|
|
2164
|
+
name: "Cyberpunk",
|
|
2165
|
+
value: "cyberpunk",
|
|
2166
|
+
category: "Futuristic"
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
name: "Retro",
|
|
2170
|
+
value: "retro",
|
|
2171
|
+
category: "Vintage"
|
|
2172
|
+
}
|
|
2173
|
+
];
|
|
2174
|
+
logger.table(
|
|
2175
|
+
designs.map((d) => ({
|
|
2176
|
+
"Design System": d.name,
|
|
2177
|
+
ID: d.value,
|
|
2178
|
+
Category: d.category
|
|
2179
|
+
}))
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
function listTiers() {
|
|
2183
|
+
logger.newline();
|
|
2184
|
+
logger.info("\u2B50 Quality Tiers:");
|
|
2185
|
+
logger.separator();
|
|
2186
|
+
const tiers = [
|
|
2187
|
+
{
|
|
2188
|
+
tier: "Basic",
|
|
2189
|
+
id: "basic",
|
|
2190
|
+
description: "Prototype/demo quality",
|
|
2191
|
+
features: "2-3 variants, basic styling",
|
|
2192
|
+
loc: "~60 LOC"
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
tier: "Standard",
|
|
2196
|
+
id: "standard",
|
|
2197
|
+
description: "Production-ready",
|
|
2198
|
+
features: "5+ variants, accessibility, dark mode",
|
|
2199
|
+
loc: "~200 LOC"
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
tier: "Enterprise",
|
|
2203
|
+
id: "enterprise",
|
|
2204
|
+
description: "Mission-critical",
|
|
2205
|
+
features: "All features + analytics + advanced animations",
|
|
2206
|
+
loc: "~350 LOC"
|
|
2207
|
+
}
|
|
2208
|
+
];
|
|
2209
|
+
logger.table(
|
|
2210
|
+
tiers.map((t) => ({
|
|
2211
|
+
Tier: t.tier,
|
|
2212
|
+
ID: t.id,
|
|
2213
|
+
Description: t.description,
|
|
2214
|
+
Features: t.features,
|
|
2215
|
+
Size: t.loc
|
|
2216
|
+
}))
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
function listFrameworks() {
|
|
2220
|
+
logger.newline();
|
|
2221
|
+
logger.info("\u{1F680} Supported Frameworks:");
|
|
2222
|
+
logger.separator();
|
|
2223
|
+
const frameworks = [
|
|
2224
|
+
{ name: "React", value: "react", extension: ".tsx" },
|
|
2225
|
+
{ name: "Vue", value: "vue", extension: ".vue" },
|
|
2226
|
+
{ name: "Angular", value: "angular", extension: ".component.ts" },
|
|
2227
|
+
{ name: "Svelte", value: "svelte", extension: ".svelte" },
|
|
2228
|
+
{ name: "Solid", value: "solid", extension: ".tsx" }
|
|
2229
|
+
];
|
|
2230
|
+
logger.table(
|
|
2231
|
+
frameworks.map((f) => ({
|
|
2232
|
+
Framework: f.name,
|
|
2233
|
+
ID: f.value,
|
|
2234
|
+
Extension: f.extension
|
|
2235
|
+
}))
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
function getAllData() {
|
|
2239
|
+
return {
|
|
2240
|
+
templates: [
|
|
2241
|
+
"button",
|
|
2242
|
+
"card",
|
|
2243
|
+
"input",
|
|
2244
|
+
"modal",
|
|
2245
|
+
"table",
|
|
2246
|
+
"form",
|
|
2247
|
+
"navigation",
|
|
2248
|
+
"hero",
|
|
2249
|
+
"feature",
|
|
2250
|
+
"pricing"
|
|
2251
|
+
],
|
|
2252
|
+
designSystems: [
|
|
2253
|
+
"material-design",
|
|
2254
|
+
"ios-hig",
|
|
2255
|
+
"glassmorphism",
|
|
2256
|
+
"neumorphism",
|
|
2257
|
+
"minimalism",
|
|
2258
|
+
"brutalism",
|
|
2259
|
+
"cyberpunk",
|
|
2260
|
+
"retro"
|
|
2261
|
+
],
|
|
2262
|
+
tiers: ["basic", "standard", "enterprise"],
|
|
2263
|
+
frameworks: ["react", "vue", "angular", "svelte", "solid"]
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
// src/commands/matrix-generate.ts
|
|
2268
|
+
var import_commander6 = require("commander");
|
|
2269
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
2270
|
+
var import_ora2 = __toESM(require("ora"));
|
|
2271
|
+
var import_vibe_engine = require("@nikkory/vibe-engine");
|
|
2272
|
+
|
|
2273
|
+
// src/generators/component-generator.ts
|
|
2274
|
+
var import_promises = require("fs/promises");
|
|
2275
|
+
var import_path = require("path");
|
|
2276
|
+
var ComponentGenerator2 = class {
|
|
2277
|
+
/**
|
|
2278
|
+
* Write generated code to file
|
|
2279
|
+
*
|
|
2280
|
+
* @param filePath - Absolute file path
|
|
2281
|
+
* @param code - Generated code content
|
|
2282
|
+
* @returns Promise<void>
|
|
2283
|
+
*/
|
|
2284
|
+
async writeToFile(filePath, code) {
|
|
2285
|
+
await (0, import_promises.mkdir)((0, import_path.dirname)(filePath), { recursive: true });
|
|
2286
|
+
await (0, import_promises.writeFile)(filePath, code, "utf-8");
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Format code with syntax highlighting (for terminal output)
|
|
2290
|
+
*
|
|
2291
|
+
* @param code - Code to format
|
|
2292
|
+
* @returns Formatted code string
|
|
2293
|
+
*
|
|
2294
|
+
* Note: Simple pass-through for now. Could use chalk for syntax highlighting.
|
|
2295
|
+
*/
|
|
2296
|
+
formatCode(code) {
|
|
2297
|
+
return code;
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
|
|
2301
|
+
// src/commands/matrix-generate.ts
|
|
2302
|
+
var matrixGenerateCommand = new import_commander6.Command("matrix-generate").description("Generate UI component using Matrix Multiplication (24-Factor System)").argument("<component>", "Component ID (e.g., button, input, card)").option(
|
|
2303
|
+
"-d, --design-system <system>",
|
|
2304
|
+
"Design system (material-design, ios-hig, glassmorphism, neumorphism, brutalism, minimalism, fluent, carbon, ant-design, chakra, atlassian, blueprint)",
|
|
2305
|
+
"material-design"
|
|
2306
|
+
).option(
|
|
2307
|
+
"-t, --tier <tier>",
|
|
2308
|
+
"Quality tier (basic, standard, enterprise)",
|
|
2309
|
+
"standard"
|
|
2310
|
+
).option(
|
|
2311
|
+
"-f, --factor-overrides <json>",
|
|
2312
|
+
`Factor overrides as JSON (e.g., '{"4":"xl","17":"lg"}')`
|
|
2313
|
+
).option(
|
|
2314
|
+
"-o, --output <path>",
|
|
2315
|
+
"Output file path"
|
|
2316
|
+
).action(async (componentId, options) => {
|
|
2317
|
+
const spinner = (0, import_ora2.default)("Generating component...").start();
|
|
2318
|
+
try {
|
|
2319
|
+
const resolver = new import_vibe_engine.MatrixResolver();
|
|
2320
|
+
const templateEngine = new import_vibe_engine.TemplateEngine();
|
|
2321
|
+
let factor24Config;
|
|
2322
|
+
if (options.factorOverrides) {
|
|
2323
|
+
try {
|
|
2324
|
+
factor24Config = JSON.parse(options.factorOverrides);
|
|
2325
|
+
} catch (parseError) {
|
|
2326
|
+
spinner.fail(import_chalk2.default.red("Invalid JSON in --factor-overrides"));
|
|
2327
|
+
if (parseError instanceof Error) {
|
|
2328
|
+
console.error(import_chalk2.default.gray(parseError.message));
|
|
2329
|
+
}
|
|
2330
|
+
process.exit(1);
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
const input = {
|
|
2334
|
+
componentId,
|
|
2335
|
+
designSystem: options.designSystem,
|
|
2336
|
+
tier: options.tier,
|
|
2337
|
+
factor24Config
|
|
2338
|
+
};
|
|
2339
|
+
const config = resolver.resolve(input);
|
|
2340
|
+
const code = templateEngine.generate(config);
|
|
2341
|
+
const generator = new ComponentGenerator2();
|
|
2342
|
+
if (options.output) {
|
|
2343
|
+
await generator.writeToFile(options.output, code);
|
|
2344
|
+
spinner.succeed(import_chalk2.default.green(`Generated ${config.componentName} \u2192 ${options.output}`));
|
|
2345
|
+
} else {
|
|
2346
|
+
spinner.succeed(import_chalk2.default.green(`Generated ${config.componentName}`));
|
|
2347
|
+
console.log(code);
|
|
2348
|
+
}
|
|
2349
|
+
console.log(import_chalk2.default.cyan("\nComponent Info:"));
|
|
2350
|
+
console.log(` ${import_chalk2.default.gray("Name:")} ${config.componentName}`);
|
|
2351
|
+
console.log(` ${import_chalk2.default.gray("Tier:")} ${config.tier}`);
|
|
2352
|
+
console.log(` ${import_chalk2.default.gray("Design System:")} ${config.designSystem}`);
|
|
2353
|
+
console.log(` ${import_chalk2.default.gray("HTML Element:")} <${config.elementType}>`);
|
|
2354
|
+
console.log(` ${import_chalk2.default.gray("Class Names:")} ${config.classNames.join(" ")}`);
|
|
2355
|
+
console.log("");
|
|
2356
|
+
} catch (error) {
|
|
2357
|
+
spinner.fail(import_chalk2.default.red(`Error: ${error.message}`));
|
|
2358
|
+
process.exit(1);
|
|
2359
|
+
}
|
|
2360
|
+
});
|
|
2361
|
+
|
|
2362
|
+
// src/index.ts
|
|
2363
|
+
var program = new import_commander7.Command();
|
|
2364
|
+
program.name("vibe").description("Nikkory Vibe - Production-ready code in seconds").version("1.0.0");
|
|
2365
|
+
program.addCommand(generateCommand);
|
|
2366
|
+
program.addCommand(matrixGenerateCommand);
|
|
2367
|
+
program.addCommand(initCommand);
|
|
2368
|
+
program.addCommand(listCommand);
|
|
2369
|
+
program.addCommand(addCommand);
|
|
2370
|
+
program.addCommand(copyCommand);
|
|
2371
|
+
program.parse(process.argv);
|