mycontext-cli 0.2.39 → 0.4.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/dist/agents/implementations/CodeGenSubAgent.d.ts +2 -0
- package/dist/agents/implementations/CodeGenSubAgent.d.ts.map +1 -1
- package/dist/agents/implementations/CodeGenSubAgent.js +344 -24
- package/dist/agents/implementations/CodeGenSubAgent.js.map +1 -1
- package/dist/agents/implementations/DocsSubAgent.js +1 -1
- package/dist/agents/implementations/DocsSubAgent.js.map +1 -1
- package/dist/agents/implementations/EnhancementAgent.d.ts.map +1 -1
- package/dist/agents/implementations/EnhancementAgent.js +31 -44
- package/dist/agents/implementations/EnhancementAgent.js.map +1 -1
- package/dist/agents/implementations/QASubAgent.js +1 -1
- package/dist/agents/orchestrator/SubAgentOrchestrator.js +1 -1
- package/dist/agents/orchestrator/SubAgentOrchestrator.js.map +1 -1
- package/dist/cli.js +38 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/auth.d.ts +8 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +63 -22
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/core.d.ts +12 -0
- package/dist/commands/core.d.ts.map +1 -0
- package/dist/commands/core.js +133 -0
- package/dist/commands/core.js.map +1 -0
- package/dist/commands/enhance.d.ts +1 -1
- package/dist/commands/enhance.d.ts.map +1 -1
- package/dist/commands/enhance.js +54 -59
- package/dist/commands/enhance.js.map +1 -1
- package/dist/commands/generate-components.d.ts +15 -0
- package/dist/commands/generate-components.d.ts.map +1 -1
- package/dist/commands/generate-components.js +289 -53
- package/dist/commands/generate-components.js.map +1 -1
- package/dist/commands/generate.d.ts +4 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +239 -31
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +25 -9
- package/dist/commands/list.js.map +1 -1
- package/dist/config/ai-providers.json +25 -0
- package/dist/utils/apiKeyManager.d.ts +1 -1
- package/dist/utils/apiKeyManager.js +1 -1
- package/dist/utils/errorHandler.d.ts +4 -0
- package/dist/utils/errorHandler.d.ts.map +1 -1
- package/dist/utils/errorHandler.js +4 -0
- package/dist/utils/errorHandler.js.map +1 -1
- package/dist/utils/hybridAIClient.d.ts +1 -0
- package/dist/utils/hybridAIClient.d.ts.map +1 -1
- package/dist/utils/hybridAIClient.js +13 -0
- package/dist/utils/hybridAIClient.js.map +1 -1
- package/dist/utils/xaiClient.d.ts +19 -0
- package/dist/utils/xaiClient.d.ts.map +1 -0
- package/dist/utils/xaiClient.js +108 -0
- package/dist/utils/xaiClient.js.map +1 -0
- package/package.json +8 -2
|
@@ -40,40 +40,10 @@ exports.GenerateComponentsCommand = void 0;
|
|
|
40
40
|
const fileSystem_1 = require("../utils/fileSystem");
|
|
41
41
|
const spinner_1 = require("../utils/spinner");
|
|
42
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
43
44
|
const fs = __importStar(require("fs-extra"));
|
|
44
45
|
const path = __importStar(require("path"));
|
|
45
46
|
const child_process_1 = require("child_process");
|
|
46
|
-
// Code Generation Sub-Agent
|
|
47
|
-
class CodeGenSubAgent {
|
|
48
|
-
constructor() {
|
|
49
|
-
this.name = "CodeGenSubAgent";
|
|
50
|
-
}
|
|
51
|
-
async run({ component, group, options }) {
|
|
52
|
-
// Use the existing generateComponentCode logic
|
|
53
|
-
return new GenerateComponentsCommand().generateComponentCode(component, group, options);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// QA Sub-Agent (stub for demonstration)
|
|
57
|
-
class QASubAgent {
|
|
58
|
-
constructor() {
|
|
59
|
-
this.name = "QASubAgent";
|
|
60
|
-
}
|
|
61
|
-
async run({ code, component }) {
|
|
62
|
-
// Placeholder: In real implementation, run static analysis, lint, or type checks
|
|
63
|
-
// For now, always return true (pass)
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// Docs Writer Sub-Agent (stub for demonstration)
|
|
68
|
-
class DocsSubAgent {
|
|
69
|
-
constructor() {
|
|
70
|
-
this.name = "DocsSubAgent";
|
|
71
|
-
}
|
|
72
|
-
async run({ component, group }) {
|
|
73
|
-
// Placeholder: Generate markdown or JSDoc for the component
|
|
74
|
-
return `# ${component.name}\n\n${component.description}\n`;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
47
|
// --- Orchestration in GenerateComponentsCommand ---
|
|
78
48
|
class GenerateComponentsCommand {
|
|
79
49
|
constructor() {
|
|
@@ -117,6 +87,8 @@ class GenerateComponentsCommand {
|
|
|
117
87
|
async execute(target, options) {
|
|
118
88
|
const spinner = new spinner_1.EnhancedSpinner("Initializing component generation...");
|
|
119
89
|
try {
|
|
90
|
+
// System reminder: high-level plan
|
|
91
|
+
console.log(chalk_1.default.gray("\n[mycontext] Plan: plan → generate → QA → docs → preview (→ checks)\n"));
|
|
120
92
|
// Check authentication unless local mode is enabled
|
|
121
93
|
let userInfo = null;
|
|
122
94
|
if (!options.local) {
|
|
@@ -146,6 +118,36 @@ class GenerateComponentsCommand {
|
|
|
146
118
|
// Determine if we're generating a specific group or all components
|
|
147
119
|
const isAll = target === "all" || options.all;
|
|
148
120
|
const groupName = isAll ? undefined : target;
|
|
121
|
+
// Core-first flow: generate a single BrandComp then exit
|
|
122
|
+
if (isAll && options.coreFirst) {
|
|
123
|
+
await this.generateCoreBrandComp(options, spinner);
|
|
124
|
+
// Update preview registry and ensure /preview route
|
|
125
|
+
if (options.updatePreview !== false) {
|
|
126
|
+
const componentsDir = options.output || path.join("components", ".mycontext");
|
|
127
|
+
await this.updatePreviewRegistry(componentsDir);
|
|
128
|
+
await this.ensurePreviewRoute();
|
|
129
|
+
}
|
|
130
|
+
// Guidance for refinement step
|
|
131
|
+
console.log(chalk_1.default.blue("\n🧩 Core-first step complete: edit 'components/.mycontext/core/BrandComp.tsx' until you're happy."));
|
|
132
|
+
console.log(chalk_1.default.gray(" You can also run: mycontext enhance components/.mycontext/core/BrandComp.tsx --prompt 'Tweak spacing/colors'\n"));
|
|
133
|
+
// If non-interactive, stop here
|
|
134
|
+
if (options.yes) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// Ask whether to proceed with remaining components now
|
|
138
|
+
const answer = await (0, prompts_1.default)({
|
|
139
|
+
type: "toggle",
|
|
140
|
+
name: "proceed",
|
|
141
|
+
message: "Proceed to generate the remaining components now? (You can rerun later)",
|
|
142
|
+
initial: false,
|
|
143
|
+
active: "yes",
|
|
144
|
+
inactive: "no",
|
|
145
|
+
});
|
|
146
|
+
if (answer?.proceed) {
|
|
147
|
+
await this.generateAllComponents(options, spinner, userInfo.userId);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
149
151
|
if (isAll) {
|
|
150
152
|
await this.generateAllComponents(options, spinner, userInfo.userId);
|
|
151
153
|
}
|
|
@@ -285,6 +287,7 @@ class GenerateComponentsCommand {
|
|
|
285
287
|
let generatedGroups = 0;
|
|
286
288
|
for (const group of groups) {
|
|
287
289
|
spinner.updateText(`Generating ${group.name} components...`);
|
|
290
|
+
console.log(chalk_1.default.gray(`\n[mycontext] Reminder: generate → QA → docs for group '${group.name}'`));
|
|
288
291
|
const groupDir = path.join(componentsDir, this.toKebabCase(group.name));
|
|
289
292
|
await this.fs.ensureDir(groupDir);
|
|
290
293
|
const components = group.components || [];
|
|
@@ -306,6 +309,8 @@ class GenerateComponentsCommand {
|
|
|
306
309
|
await this.updatePreviewRegistry(componentsDir);
|
|
307
310
|
await this.ensurePreviewRoute();
|
|
308
311
|
}
|
|
312
|
+
// Post-generation: scan actual imports and ensure missing shadcn primitives are installed
|
|
313
|
+
await this.scanAndInstallShadcnFromComponents(componentsDir, spinner);
|
|
309
314
|
spinner.success({
|
|
310
315
|
text: `Generated ${totalComponents} components across ${generatedGroups} groups!`,
|
|
311
316
|
});
|
|
@@ -322,6 +327,21 @@ class GenerateComponentsCommand {
|
|
|
322
327
|
});
|
|
323
328
|
// Post-run hints
|
|
324
329
|
this.printNextStepsAfterComponents(options);
|
|
330
|
+
// Optionally open preview in browser
|
|
331
|
+
if (options.openPreview !== false) {
|
|
332
|
+
try {
|
|
333
|
+
const url = "http://localhost:3000/preview";
|
|
334
|
+
console.log(chalk_1.default.blue(`\n🌐 Opening preview: ${url}`));
|
|
335
|
+
// best-effort open using the OS default opener
|
|
336
|
+
const opener = process.platform === "darwin"
|
|
337
|
+
? "open"
|
|
338
|
+
: process.platform === "win32"
|
|
339
|
+
? "start"
|
|
340
|
+
: "xdg-open";
|
|
341
|
+
(0, child_process_1.execSync)(`${opener} ${url}`, { stdio: "ignore" });
|
|
342
|
+
}
|
|
343
|
+
catch { }
|
|
344
|
+
}
|
|
325
345
|
}
|
|
326
346
|
async generateComponentGroup(groupName, options, spinner, userId) {
|
|
327
347
|
spinner.updateText(`Generating ${groupName} components...`);
|
|
@@ -370,6 +390,8 @@ class GenerateComponentsCommand {
|
|
|
370
390
|
await this.updatePreviewRegistry(compBaseDir);
|
|
371
391
|
await this.ensurePreviewRoute();
|
|
372
392
|
}
|
|
393
|
+
// Post-generation: scan actual imports and ensure missing shadcn primitives are installed
|
|
394
|
+
await this.scanAndInstallShadcnFromComponents(compBaseDir, spinner);
|
|
373
395
|
spinner.success({
|
|
374
396
|
text: `Generated ${components.length} components in ${group.name}!`,
|
|
375
397
|
});
|
|
@@ -380,6 +402,204 @@ class GenerateComponentsCommand {
|
|
|
380
402
|
});
|
|
381
403
|
console.log(chalk_1.default.gray(` • index.ts`));
|
|
382
404
|
console.log(chalk_1.default.gray(` • page.tsx`));
|
|
405
|
+
// Optionally open preview in browser
|
|
406
|
+
if (options.openPreview !== false) {
|
|
407
|
+
try {
|
|
408
|
+
const url = "http://localhost:3000/preview";
|
|
409
|
+
console.log(chalk_1.default.blue(`\n🌐 Opening preview: ${url}`));
|
|
410
|
+
const opener = process.platform === "darwin"
|
|
411
|
+
? "open"
|
|
412
|
+
: process.platform === "win32"
|
|
413
|
+
? "start"
|
|
414
|
+
: "xdg-open";
|
|
415
|
+
(0, child_process_1.execSync)(`${opener} ${url}`, { stdio: "ignore" });
|
|
416
|
+
}
|
|
417
|
+
catch { }
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Generate a single core BrandComp using PRD/types/brand context to act as the design anchor.
|
|
422
|
+
*/
|
|
423
|
+
async generateCoreBrandComp(options, spinner) {
|
|
424
|
+
spinner.updateText("Generating core BrandComp...");
|
|
425
|
+
const componentsDir = options.output || path.join("components", ".mycontext");
|
|
426
|
+
const groupDir = path.join(componentsDir, "core");
|
|
427
|
+
await this.fs.ensureDir(groupDir);
|
|
428
|
+
// Ensure shadcn initialized
|
|
429
|
+
await this.ensureShadcnComponentsInstalled([
|
|
430
|
+
{
|
|
431
|
+
name: "Core",
|
|
432
|
+
components: [
|
|
433
|
+
{
|
|
434
|
+
name: "BrandComp",
|
|
435
|
+
type: "layout",
|
|
436
|
+
description: "Core brand canvas",
|
|
437
|
+
tags: ["layout", "canvas"],
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
], spinner);
|
|
442
|
+
// Best-effort brand token application
|
|
443
|
+
await this.applyBrandTokens();
|
|
444
|
+
const brandComp = {
|
|
445
|
+
name: "BrandComp",
|
|
446
|
+
type: "layout",
|
|
447
|
+
description: "A core brand canvas showcasing typography, primary/secondary colors, interactive states, and spacing. Acts as a reference for other components.",
|
|
448
|
+
userStories: [
|
|
449
|
+
"As a designer, I can see brand colors and typography applied consistently.",
|
|
450
|
+
"As a developer, I can reference spacing, radius, and interactive states.",
|
|
451
|
+
],
|
|
452
|
+
actionFunctions: [],
|
|
453
|
+
dependencies: ["react"],
|
|
454
|
+
tags: ["brand", "layout", "canvas"],
|
|
455
|
+
};
|
|
456
|
+
// Try sub-agent generation first
|
|
457
|
+
try {
|
|
458
|
+
const { orchestrator } = await Promise.resolve().then(() => __importStar(require("../agents/orchestrator/SubAgentOrchestrator")));
|
|
459
|
+
const codeResult = (await orchestrator.executeAgent("CodeGenSubAgent", {
|
|
460
|
+
component: brandComp,
|
|
461
|
+
group: { name: "Core" },
|
|
462
|
+
options: {
|
|
463
|
+
...options,
|
|
464
|
+
context: {
|
|
465
|
+
prd: this.contextArtifacts.prd,
|
|
466
|
+
types: this.contextArtifacts.types,
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
}));
|
|
470
|
+
await this.fs.writeFile(path.join(groupDir, `BrandComp.tsx`), codeResult.code);
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
const code = this.generateComponentCode(brandComp, { name: "Core" }, {
|
|
474
|
+
...options,
|
|
475
|
+
context: {
|
|
476
|
+
prd: this.contextArtifacts.prd,
|
|
477
|
+
types: this.contextArtifacts.types,
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
await this.fs.writeFile(path.join(groupDir, `BrandComp.tsx`), code);
|
|
481
|
+
}
|
|
482
|
+
// Group index and preview
|
|
483
|
+
await this.generateGroupIndex({
|
|
484
|
+
name: "Core",
|
|
485
|
+
description: "Core brand canvas",
|
|
486
|
+
components: [brandComp],
|
|
487
|
+
}, groupDir);
|
|
488
|
+
await this.generatePreviewPage({
|
|
489
|
+
name: "Core",
|
|
490
|
+
description: "Core brand canvas",
|
|
491
|
+
components: [brandComp],
|
|
492
|
+
}, groupDir);
|
|
493
|
+
spinner.success({ text: "Generated core BrandComp." });
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Scan generated TSX files for imports from '@/components/ui/*' and ensure those shadcn primitives are installed.
|
|
497
|
+
*/
|
|
498
|
+
async scanAndInstallShadcnFromComponents(componentsBaseDir, spinner) {
|
|
499
|
+
try {
|
|
500
|
+
const projectRoot = process.cwd();
|
|
501
|
+
const pkgJsonPath = path.join(projectRoot, "package.json");
|
|
502
|
+
if (!(await fs.pathExists(pkgJsonPath)))
|
|
503
|
+
return;
|
|
504
|
+
const needed = new Set();
|
|
505
|
+
const walk = async (dir) => {
|
|
506
|
+
const entries = await fs.readdir(dir);
|
|
507
|
+
for (const entry of entries) {
|
|
508
|
+
const full = path.join(dir, entry);
|
|
509
|
+
const stat = await fs.stat(full);
|
|
510
|
+
if (stat.isDirectory())
|
|
511
|
+
await walk(full);
|
|
512
|
+
else if (entry.endsWith(".tsx")) {
|
|
513
|
+
const src = await fs.readFile(full, "utf8");
|
|
514
|
+
const re = /from\s+["']@\/components\/ui\/([^"']+)["']/g;
|
|
515
|
+
let m;
|
|
516
|
+
while ((m = re.exec(src))) {
|
|
517
|
+
const mod = m[1];
|
|
518
|
+
if (mod)
|
|
519
|
+
needed.add(mod);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
await walk(componentsBaseDir);
|
|
525
|
+
if (needed.size === 0)
|
|
526
|
+
return;
|
|
527
|
+
// Filter out already-present ui files
|
|
528
|
+
const uiDir = path.join(projectRoot, "components", "ui");
|
|
529
|
+
const missing = [];
|
|
530
|
+
for (const name of needed) {
|
|
531
|
+
const p = path.join(uiDir, `${name}.tsx`);
|
|
532
|
+
if (!(await fs.pathExists(p)))
|
|
533
|
+
missing.push(name);
|
|
534
|
+
}
|
|
535
|
+
if (missing.length === 0)
|
|
536
|
+
return;
|
|
537
|
+
spinner.updateText(`Installing missing shadcn primitives from imports (${missing.length})...`);
|
|
538
|
+
const pm = await this.detectPackageManager(projectRoot);
|
|
539
|
+
try {
|
|
540
|
+
if (pm === "pnpm") {
|
|
541
|
+
(0, child_process_1.execSync)(`pnpm dlx shadcn@latest add ${missing.join(" ")}`.trim(), {
|
|
542
|
+
cwd: projectRoot,
|
|
543
|
+
stdio: "inherit",
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
(0, child_process_1.execSync)(`npx shadcn@latest add ${missing.join(" ")}`.trim(), {
|
|
548
|
+
cwd: projectRoot,
|
|
549
|
+
stdio: "inherit",
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
catch {
|
|
554
|
+
console.log(chalk_1.default.yellow(" ⚠️ shadcn add (post-scan) failed; you can add components manually."));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
console.log(chalk_1.default.yellow(` ⚠️ shadcn post-scan encountered an issue: ${error instanceof Error ? error.message : String(error)}`));
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Apply brand tokens to globals.css (:root variables) if branding exists.
|
|
563
|
+
*/
|
|
564
|
+
async applyBrandTokens() {
|
|
565
|
+
try {
|
|
566
|
+
const projectRoot = process.cwd();
|
|
567
|
+
const appDir = (await fs.pathExists(path.join(projectRoot, "src", "app")))
|
|
568
|
+
? path.join(projectRoot, "src", "app")
|
|
569
|
+
: path.join(projectRoot, "app");
|
|
570
|
+
const globalsPath = path.join(appDir, "globals.css");
|
|
571
|
+
if (!(await fs.pathExists(globalsPath)))
|
|
572
|
+
return;
|
|
573
|
+
const brandPath = path.join(projectRoot, ".mycontext", "03-branding.md");
|
|
574
|
+
if (!(await fs.pathExists(brandPath)))
|
|
575
|
+
return;
|
|
576
|
+
const brand = await fs.readFile(brandPath, "utf8");
|
|
577
|
+
const pick = (label) => {
|
|
578
|
+
const m = brand.match(new RegExp(label + ".*?(#[0-9a-fA-F]{6})"));
|
|
579
|
+
return m ? m[1] : null;
|
|
580
|
+
};
|
|
581
|
+
const primary = pick("primary") || pick("Primary") || null;
|
|
582
|
+
const secondary = pick("secondary") || pick("Secondary") || null;
|
|
583
|
+
const accent = pick("accent") || pick("Accent") || null;
|
|
584
|
+
if (!primary && !secondary && !accent)
|
|
585
|
+
return;
|
|
586
|
+
let css = await fs.readFile(globalsPath, "utf8");
|
|
587
|
+
const ensureVar = (name, value) => {
|
|
588
|
+
const re = new RegExp(`(--${name}:\s*)([^;]+)(;)`);
|
|
589
|
+
if (re.test(css))
|
|
590
|
+
css = css.replace(re, `$1${value}$3`);
|
|
591
|
+
else
|
|
592
|
+
css = css.replace(/:root\s*\{/, (m) => `${m}\n --${name}: ${value};`);
|
|
593
|
+
};
|
|
594
|
+
if (primary)
|
|
595
|
+
ensureVar("primary", primary);
|
|
596
|
+
if (secondary)
|
|
597
|
+
ensureVar("secondary", secondary);
|
|
598
|
+
if (accent)
|
|
599
|
+
ensureVar("accent", accent);
|
|
600
|
+
await fs.writeFile(globalsPath, css);
|
|
601
|
+
}
|
|
602
|
+
catch { }
|
|
383
603
|
}
|
|
384
604
|
async generateComponent(component, group, groupDir, options, userId) {
|
|
385
605
|
try {
|
|
@@ -441,15 +661,23 @@ class GenerateComponentsCommand {
|
|
|
441
661
|
.replace(new RegExp(`${safeName} Default`, "g"), `${safeName}Default`);
|
|
442
662
|
await this.fs.writeFile(componentPath, fixedCode);
|
|
443
663
|
// Execute QA and docs in parallel
|
|
664
|
+
const componentWithContext = {
|
|
665
|
+
...component,
|
|
666
|
+
_context: {
|
|
667
|
+
group: group?.name,
|
|
668
|
+
prd: this.contextArtifacts.prd,
|
|
669
|
+
types: this.contextArtifacts.types,
|
|
670
|
+
},
|
|
671
|
+
};
|
|
444
672
|
const [qaResult, docsResult] = (await Promise.all([
|
|
445
673
|
orchestrator.executeAgent("QASubAgent", {
|
|
446
674
|
code: codeResult.code,
|
|
447
|
-
component,
|
|
675
|
+
component: componentWithContext,
|
|
448
676
|
standards: ["typescript", "react", "accessibility"],
|
|
449
677
|
}),
|
|
450
678
|
orchestrator.executeAgent("DocsSubAgent", {
|
|
451
679
|
code: codeResult.code,
|
|
452
|
-
component,
|
|
680
|
+
component: componentWithContext,
|
|
453
681
|
format: "readme",
|
|
454
682
|
}),
|
|
455
683
|
]));
|
|
@@ -521,6 +749,11 @@ class GenerateComponentsCommand {
|
|
|
521
749
|
async ensureShadcnComponentsInstalled(groups, spinner) {
|
|
522
750
|
try {
|
|
523
751
|
const projectRoot = process.cwd();
|
|
752
|
+
const pkgJsonPath = path.join(projectRoot, "package.json");
|
|
753
|
+
if (!(await fs.pathExists(pkgJsonPath))) {
|
|
754
|
+
console.log(chalk_1.default.gray(" Skipping shadcn setup: no package.json in current directory. Run inside an existing Next.js project."));
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
524
757
|
const componentsJsonPath = path.join(projectRoot, "components.json");
|
|
525
758
|
// Initialize shadcn if components.json is missing
|
|
526
759
|
if (!(await fs.pathExists(componentsJsonPath))) {
|
|
@@ -557,6 +790,10 @@ class GenerateComponentsCommand {
|
|
|
557
790
|
const names = Array.from(needed);
|
|
558
791
|
spinner.updateText(`Installing shadcn components (${names.length})...`);
|
|
559
792
|
try {
|
|
793
|
+
if (!(await fs.pathExists(pkgJsonPath))) {
|
|
794
|
+
console.log(chalk_1.default.gray(" Skipping 'shadcn add' because no package.json was found."));
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
560
797
|
if (pkgManager === "pnpm") {
|
|
561
798
|
(0, child_process_1.execSync)(`pnpm dlx shadcn@latest add ${names.join(" ")}`, {
|
|
562
799
|
cwd: projectRoot,
|
|
@@ -1058,7 +1295,6 @@ export default ${name};
|
|
|
1058
1295
|
e.preventDefault();
|
|
1059
1296
|
setIsSubmitting(true);
|
|
1060
1297
|
try {
|
|
1061
|
-
// TODO: Implement form submission logic
|
|
1062
1298
|
onSubmit?.(formData);
|
|
1063
1299
|
} catch (error) {
|
|
1064
1300
|
console.error("Form submission error:", error);
|
|
@@ -1173,14 +1409,14 @@ export default ${name};
|
|
|
1173
1409
|
case "handleLogin":
|
|
1174
1410
|
return `
|
|
1175
1411
|
export async function handleLogin(email: string, password: string) {
|
|
1176
|
-
//
|
|
1412
|
+
// Implement login logic in application layer
|
|
1177
1413
|
console.log("Logging in with:", { email, password });
|
|
1178
1414
|
return { success: true };
|
|
1179
1415
|
}`;
|
|
1180
1416
|
case "handleSignup":
|
|
1181
1417
|
return `
|
|
1182
1418
|
export async function handleSignup(email: string, password: string, name: string) {
|
|
1183
|
-
//
|
|
1419
|
+
// Implement signup logic in application layer
|
|
1184
1420
|
console.log("Signing up with:", { email, password, name });
|
|
1185
1421
|
return { success: true };
|
|
1186
1422
|
}`;
|
|
@@ -1193,14 +1429,14 @@ export function validateEmail(email: string): boolean {
|
|
|
1193
1429
|
case "checkUsername":
|
|
1194
1430
|
return `
|
|
1195
1431
|
export async function checkUsername(username: string): Promise<boolean> {
|
|
1196
|
-
//
|
|
1432
|
+
// Implement username availability check in application layer
|
|
1197
1433
|
console.log("Checking username:", username);
|
|
1198
1434
|
return true;
|
|
1199
1435
|
}`;
|
|
1200
1436
|
default:
|
|
1201
1437
|
return `
|
|
1202
1438
|
export function ${func}() {
|
|
1203
|
-
//
|
|
1439
|
+
// Implement ${func} logic in application layer
|
|
1204
1440
|
console.log("${func} called");
|
|
1205
1441
|
}`;
|
|
1206
1442
|
}
|
|
@@ -1407,14 +1643,14 @@ function groupBy<T, K extends string | number>(
|
|
|
1407
1643
|
try {
|
|
1408
1644
|
const projectRoot = process.cwd();
|
|
1409
1645
|
const typesPathCandidates = [
|
|
1410
|
-
path.join(projectRoot,
|
|
1411
|
-
path.join(projectRoot,
|
|
1412
|
-
path.join(projectRoot,
|
|
1646
|
+
path.join(projectRoot, ".mycontext", "02-types.ts"),
|
|
1647
|
+
path.join(projectRoot, ".mycontext", "types.ts"),
|
|
1648
|
+
path.join(projectRoot, "context", "types.ts"),
|
|
1413
1649
|
];
|
|
1414
|
-
let typesSource =
|
|
1650
|
+
let typesSource = "";
|
|
1415
1651
|
for (const p of typesPathCandidates) {
|
|
1416
1652
|
if (await fs.pathExists(p)) {
|
|
1417
|
-
typesSource = await fs.readFile(p,
|
|
1653
|
+
typesSource = await fs.readFile(p, "utf8");
|
|
1418
1654
|
break;
|
|
1419
1655
|
}
|
|
1420
1656
|
}
|
|
@@ -1427,7 +1663,7 @@ function groupBy<T, K extends string | number>(
|
|
|
1427
1663
|
const body = match[2];
|
|
1428
1664
|
const fields = {};
|
|
1429
1665
|
body
|
|
1430
|
-
.split(
|
|
1666
|
+
.split("\n")
|
|
1431
1667
|
.map((l) => l.trim())
|
|
1432
1668
|
.filter(Boolean)
|
|
1433
1669
|
.forEach((line) => {
|
|
@@ -1445,19 +1681,19 @@ function groupBy<T, K extends string | number>(
|
|
|
1445
1681
|
const stat = await fs.stat(groupPath);
|
|
1446
1682
|
if (!stat.isDirectory())
|
|
1447
1683
|
continue;
|
|
1448
|
-
const files = (await fs.readdir(groupPath)).filter((f) => f.endsWith(
|
|
1684
|
+
const files = (await fs.readdir(groupPath)).filter((f) => f.endsWith(".tsx"));
|
|
1449
1685
|
for (const file of files) {
|
|
1450
|
-
if (file ===
|
|
1686
|
+
if (file === "page.tsx")
|
|
1451
1687
|
continue;
|
|
1452
|
-
const base = file.replace(/\.tsx$/,
|
|
1688
|
+
const base = file.replace(/\.tsx$/, "");
|
|
1453
1689
|
const full = path.join(groupPath, file);
|
|
1454
|
-
const src = await fs.readFile(full,
|
|
1690
|
+
const src = await fs.readFile(full, "utf8");
|
|
1455
1691
|
const propsInterfaceMatch = src.match(/interface\s+(\w+)Props\s*\{([\s\S]*?)\}/);
|
|
1456
1692
|
if (!propsInterfaceMatch)
|
|
1457
1693
|
continue;
|
|
1458
1694
|
const propsBody = propsInterfaceMatch[2];
|
|
1459
1695
|
const entries = propsBody
|
|
1460
|
-
.split(
|
|
1696
|
+
.split("\n")
|
|
1461
1697
|
.map((l) => l.trim())
|
|
1462
1698
|
.filter((l) => /:\s*/.test(l));
|
|
1463
1699
|
const propsObj = {};
|
|
@@ -1476,7 +1712,7 @@ function groupBy<T, K extends string | number>(
|
|
|
1476
1712
|
}
|
|
1477
1713
|
}
|
|
1478
1714
|
const out = `export const previewProps: Record<string, any> = ${JSON.stringify(propsMap, null, 2)};\n`;
|
|
1479
|
-
await fs.writeFile(path.join(componentsDir,
|
|
1715
|
+
await fs.writeFile(path.join(componentsDir, "preview-props.ts"), out);
|
|
1480
1716
|
}
|
|
1481
1717
|
catch (error) {
|
|
1482
1718
|
console.log(chalk_1.default.yellow(` ⚠️ Failed to build preview props: ${error instanceof Error ? error.message : String(error)}`));
|
|
@@ -1485,14 +1721,14 @@ function groupBy<T, K extends string | number>(
|
|
|
1485
1721
|
generateSampleValue(typeStr, interfaces, depth) {
|
|
1486
1722
|
if (depth > 2)
|
|
1487
1723
|
return undefined;
|
|
1488
|
-
const t = typeStr.replace(/\s+/g,
|
|
1489
|
-
if (t.endsWith(
|
|
1724
|
+
const t = typeStr.replace(/\s+/g, "");
|
|
1725
|
+
if (t.endsWith("[]")) {
|
|
1490
1726
|
const inner = t.slice(0, -2);
|
|
1491
1727
|
const v = this.generateSampleValue(inner, interfaces, depth + 1);
|
|
1492
1728
|
return v === undefined ? undefined : [v];
|
|
1493
1729
|
}
|
|
1494
1730
|
if (/^string\b/.test(t))
|
|
1495
|
-
return
|
|
1731
|
+
return "Sample";
|
|
1496
1732
|
if (/^number\b/.test(t))
|
|
1497
1733
|
return 1;
|
|
1498
1734
|
if (/^boolean\b/.test(t))
|