mycontext-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +440 -0
- package/dist/cli/src/agents/implementations/CodeGenSubAgent.d.ts +43 -0
- package/dist/cli/src/agents/implementations/CodeGenSubAgent.d.ts.map +1 -0
- package/dist/cli/src/agents/implementations/CodeGenSubAgent.js +1440 -0
- package/dist/cli/src/agents/implementations/CodeGenSubAgent.js.map +1 -0
- package/dist/cli/src/agents/implementations/DocsSubAgent.d.ts +35 -0
- package/dist/cli/src/agents/implementations/DocsSubAgent.d.ts.map +1 -0
- package/dist/cli/src/agents/implementations/DocsSubAgent.js +351 -0
- package/dist/cli/src/agents/implementations/DocsSubAgent.js.map +1 -0
- package/dist/cli/src/agents/implementations/QASubAgent.d.ts +31 -0
- package/dist/cli/src/agents/implementations/QASubAgent.d.ts.map +1 -0
- package/dist/cli/src/agents/implementations/QASubAgent.js +190 -0
- package/dist/cli/src/agents/implementations/QASubAgent.js.map +1 -0
- package/dist/cli/src/agents/interfaces/SubAgent.d.ts +157 -0
- package/dist/cli/src/agents/interfaces/SubAgent.d.ts.map +1 -0
- package/dist/cli/src/agents/interfaces/SubAgent.js +7 -0
- package/dist/cli/src/agents/interfaces/SubAgent.js.map +1 -0
- package/dist/cli/src/agents/orchestrator/SubAgentOrchestrator.d.ts +59 -0
- package/dist/cli/src/agents/orchestrator/SubAgentOrchestrator.d.ts.map +1 -0
- package/dist/cli/src/agents/orchestrator/SubAgentOrchestrator.js +305 -0
- package/dist/cli/src/agents/orchestrator/SubAgentOrchestrator.js.map +1 -0
- package/dist/cli/src/agents/personalities/definitions.d.ts +34 -0
- package/dist/cli/src/agents/personalities/definitions.d.ts.map +1 -0
- package/dist/cli/src/agents/personalities/definitions.js +360 -0
- package/dist/cli/src/agents/personalities/definitions.js.map +1 -0
- package/dist/cli/src/cli.d.ts +3 -0
- package/dist/cli/src/cli.d.ts.map +1 -0
- package/dist/cli/src/cli.js +286 -0
- package/dist/cli/src/cli.js.map +1 -0
- package/dist/cli/src/commands/auth.d.ts +23 -0
- package/dist/cli/src/commands/auth.d.ts.map +1 -0
- package/dist/cli/src/commands/auth.js +212 -0
- package/dist/cli/src/commands/auth.js.map +1 -0
- package/dist/cli/src/commands/generate-components.d.ts +28 -0
- package/dist/cli/src/commands/generate-components.d.ts.map +1 -0
- package/dist/cli/src/commands/generate-components.js +680 -0
- package/dist/cli/src/commands/generate-components.js.map +1 -0
- package/dist/cli/src/commands/generate.d.ts +108 -0
- package/dist/cli/src/commands/generate.d.ts.map +1 -0
- package/dist/cli/src/commands/generate.js +1984 -0
- package/dist/cli/src/commands/generate.js.map +1 -0
- package/dist/cli/src/commands/init.d.ts +13 -0
- package/dist/cli/src/commands/init.d.ts.map +1 -0
- package/dist/cli/src/commands/init.js +91 -0
- package/dist/cli/src/commands/init.js.map +1 -0
- package/dist/cli/src/commands/list.d.ts +17 -0
- package/dist/cli/src/commands/list.d.ts.map +1 -0
- package/dist/cli/src/commands/list.js +209 -0
- package/dist/cli/src/commands/list.js.map +1 -0
- package/dist/cli/src/commands/preview.d.ts +23 -0
- package/dist/cli/src/commands/preview.d.ts.map +1 -0
- package/dist/cli/src/commands/preview.js +1200 -0
- package/dist/cli/src/commands/preview.js.map +1 -0
- package/dist/cli/src/commands/status.d.ts +21 -0
- package/dist/cli/src/commands/status.d.ts.map +1 -0
- package/dist/cli/src/commands/status.js +287 -0
- package/dist/cli/src/commands/status.js.map +1 -0
- package/dist/cli/src/commands/validate.d.ts +22 -0
- package/dist/cli/src/commands/validate.d.ts.map +1 -0
- package/dist/cli/src/commands/validate.js +259 -0
- package/dist/cli/src/commands/validate.js.map +1 -0
- package/dist/cli/src/types/index.d.ts +152 -0
- package/dist/cli/src/types/index.d.ts.map +1 -0
- package/dist/cli/src/types/index.js +3 -0
- package/dist/cli/src/types/index.js.map +1 -0
- package/dist/cli/src/utils/apiKeyManager.d.ts +137 -0
- package/dist/cli/src/utils/apiKeyManager.d.ts.map +1 -0
- package/dist/cli/src/utils/apiKeyManager.js +471 -0
- package/dist/cli/src/utils/apiKeyManager.js.map +1 -0
- package/dist/cli/src/utils/errorHandler.d.ts +105 -0
- package/dist/cli/src/utils/errorHandler.d.ts.map +1 -0
- package/dist/cli/src/utils/errorHandler.js +332 -0
- package/dist/cli/src/utils/errorHandler.js.map +1 -0
- package/dist/cli/src/utils/fileSystem.d.ts +58 -0
- package/dist/cli/src/utils/fileSystem.d.ts.map +1 -0
- package/dist/cli/src/utils/fileSystem.js +230 -0
- package/dist/cli/src/utils/fileSystem.js.map +1 -0
- package/dist/cli/src/utils/githubModels.d.ts +53 -0
- package/dist/cli/src/utils/githubModels.d.ts.map +1 -0
- package/dist/cli/src/utils/githubModels.js +239 -0
- package/dist/cli/src/utils/githubModels.js.map +1 -0
- package/dist/cli/src/utils/spinner.d.ts +28 -0
- package/dist/cli/src/utils/spinner.d.ts.map +1 -0
- package/dist/cli/src/utils/spinner.js +112 -0
- package/dist/cli/src/utils/spinner.js.map +1 -0
- package/dist/cli/src/utils/xaiClient.d.ts +59 -0
- package/dist/cli/src/utils/xaiClient.d.ts.map +1 -0
- package/dist/cli/src/utils/xaiClient.js +244 -0
- package/dist/cli/src/utils/xaiClient.js.map +1 -0
- package/dist/lib/analytics/usage-tracker.d.ts +125 -0
- package/dist/lib/analytics/usage-tracker.d.ts.map +1 -0
- package/dist/lib/analytics/usage-tracker.js +429 -0
- package/dist/lib/analytics/usage-tracker.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.GenerateComponentsCommand = void 0;
|
|
40
|
+
const fileSystem_1 = require("../utils/fileSystem");
|
|
41
|
+
const spinner_1 = require("../utils/spinner");
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const fs = __importStar(require("fs-extra"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
// Code Generation Sub-Agent
|
|
46
|
+
class CodeGenSubAgent {
|
|
47
|
+
constructor() {
|
|
48
|
+
this.name = "CodeGenSubAgent";
|
|
49
|
+
}
|
|
50
|
+
async run({ component, group, options }) {
|
|
51
|
+
// Use the existing generateComponentCode logic
|
|
52
|
+
return new GenerateComponentsCommand().generateComponentCode(component, group, options);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// QA Sub-Agent (stub for demonstration)
|
|
56
|
+
class QASubAgent {
|
|
57
|
+
constructor() {
|
|
58
|
+
this.name = "QASubAgent";
|
|
59
|
+
}
|
|
60
|
+
async run({ code, component }) {
|
|
61
|
+
// Placeholder: In real implementation, run static analysis, lint, or type checks
|
|
62
|
+
// For now, always return true (pass)
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Docs Writer Sub-Agent (stub for demonstration)
|
|
67
|
+
class DocsSubAgent {
|
|
68
|
+
constructor() {
|
|
69
|
+
this.name = "DocsSubAgent";
|
|
70
|
+
}
|
|
71
|
+
async run({ component, group }) {
|
|
72
|
+
// Placeholder: Generate markdown or JSDoc for the component
|
|
73
|
+
return `# ${component.name}\n\n${component.description}\n`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// --- Orchestration in GenerateComponentsCommand ---
|
|
77
|
+
class GenerateComponentsCommand {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.fs = new fileSystem_1.FileSystemManager();
|
|
80
|
+
}
|
|
81
|
+
async execute(target, options) {
|
|
82
|
+
const spinner = new spinner_1.EnhancedSpinner("Initializing component generation...");
|
|
83
|
+
try {
|
|
84
|
+
// Check authentication first
|
|
85
|
+
const { AuthCommand } = await Promise.resolve().then(() => __importStar(require("./auth")));
|
|
86
|
+
const authCommand = new AuthCommand();
|
|
87
|
+
if (!(await authCommand.requireAuth())) {
|
|
88
|
+
console.log(chalk_1.default.yellow("🔐 Authentication required"));
|
|
89
|
+
console.log(chalk_1.default.gray("Please run 'mycontext auth' to authenticate first"));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Get user info for component storage
|
|
93
|
+
const userInfo = await authCommand.getUserInfo();
|
|
94
|
+
if (!userInfo) {
|
|
95
|
+
console.log(chalk_1.default.red("❌ Authentication failed"));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk_1.default.green(`✅ Authenticated as ${userInfo.email}`));
|
|
99
|
+
// Determine if we're generating a specific group or all components
|
|
100
|
+
const isAll = target === "all" || options.all;
|
|
101
|
+
const groupName = isAll ? undefined : target;
|
|
102
|
+
if (isAll) {
|
|
103
|
+
await this.generateAllComponents(options, spinner, userInfo.userId);
|
|
104
|
+
}
|
|
105
|
+
else if (groupName) {
|
|
106
|
+
await this.generateComponentGroup(groupName, options, spinner, userInfo.userId);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
throw new Error("Please specify a group name or 'all' to generate components");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
spinner.error({ text: "Component generation failed" });
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async generateAllComponents(options, spinner, userId) {
|
|
118
|
+
spinner.updateText("Generating all component groups...");
|
|
119
|
+
// Read component list
|
|
120
|
+
const componentListPath = "context/component-list.json";
|
|
121
|
+
if (!(await this.fs.exists(componentListPath))) {
|
|
122
|
+
throw new Error("Component list not found. Run 'mycontext generate components-list' first.");
|
|
123
|
+
}
|
|
124
|
+
const componentList = JSON.parse(await this.fs.readFile(componentListPath));
|
|
125
|
+
const groups = componentList.groups || [];
|
|
126
|
+
if (groups.length === 0) {
|
|
127
|
+
throw new Error("No component groups found in component-list.json");
|
|
128
|
+
}
|
|
129
|
+
// Create components directory
|
|
130
|
+
const componentsDir = options.output || "components";
|
|
131
|
+
await this.fs.ensureDir(componentsDir);
|
|
132
|
+
let totalComponents = 0;
|
|
133
|
+
let generatedGroups = 0;
|
|
134
|
+
for (const group of groups) {
|
|
135
|
+
spinner.updateText(`Generating ${group.name} components...`);
|
|
136
|
+
const groupDir = path.join(componentsDir, group.name.toLowerCase());
|
|
137
|
+
await this.fs.ensureDir(groupDir);
|
|
138
|
+
const components = group.components || [];
|
|
139
|
+
for (const component of components) {
|
|
140
|
+
await this.generateComponent(component, group, groupDir, options, userId);
|
|
141
|
+
totalComponents++;
|
|
142
|
+
}
|
|
143
|
+
// Generate group index file
|
|
144
|
+
await this.generateGroupIndex(group, groupDir);
|
|
145
|
+
// Generate preview page
|
|
146
|
+
await this.generatePreviewPage(group, groupDir);
|
|
147
|
+
generatedGroups++;
|
|
148
|
+
}
|
|
149
|
+
spinner.success({
|
|
150
|
+
text: `Generated ${totalComponents} components across ${generatedGroups} groups!`,
|
|
151
|
+
});
|
|
152
|
+
console.log(chalk_1.default.green("\n✅ Generated Files:"));
|
|
153
|
+
console.log(chalk_1.default.gray(` • ${componentsDir}/`));
|
|
154
|
+
groups.forEach((group) => {
|
|
155
|
+
console.log(chalk_1.default.gray(` • ${group.name.toLowerCase()}/`));
|
|
156
|
+
const components = group.components || [];
|
|
157
|
+
components.forEach((comp) => {
|
|
158
|
+
console.log(chalk_1.default.gray(` • ${comp.name}.tsx`));
|
|
159
|
+
});
|
|
160
|
+
console.log(chalk_1.default.gray(` • index.ts`));
|
|
161
|
+
console.log(chalk_1.default.gray(` • page.tsx`));
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async generateComponentGroup(groupName, options, spinner, userId) {
|
|
165
|
+
spinner.updateText(`Generating ${groupName} components...`);
|
|
166
|
+
// Read component list
|
|
167
|
+
const componentListPath = "context/component-list.json";
|
|
168
|
+
if (!(await this.fs.exists(componentListPath))) {
|
|
169
|
+
throw new Error("Component list not found. Run 'mycontext generate components-list' first.");
|
|
170
|
+
}
|
|
171
|
+
const componentList = JSON.parse(await this.fs.readFile(componentListPath));
|
|
172
|
+
const groups = componentList.groups || [];
|
|
173
|
+
const group = groups.find((g) => g.name.toLowerCase() === groupName.toLowerCase());
|
|
174
|
+
if (!group) {
|
|
175
|
+
throw new Error(`Group '${groupName}' not found in component-list.json`);
|
|
176
|
+
}
|
|
177
|
+
// Create group directory
|
|
178
|
+
const componentsDir = options.output || "components";
|
|
179
|
+
const groupDir = path.join(componentsDir, group.name.toLowerCase());
|
|
180
|
+
await this.fs.ensureDir(groupDir);
|
|
181
|
+
const components = group.components || [];
|
|
182
|
+
for (const component of components) {
|
|
183
|
+
await this.generateComponent(component, group, groupDir, options, userId);
|
|
184
|
+
}
|
|
185
|
+
// Generate group index file
|
|
186
|
+
await this.generateGroupIndex(group, groupDir);
|
|
187
|
+
// Generate preview page
|
|
188
|
+
await this.generatePreviewPage(group, groupDir);
|
|
189
|
+
spinner.success({
|
|
190
|
+
text: `Generated ${components.length} components in ${group.name}!`,
|
|
191
|
+
});
|
|
192
|
+
console.log(chalk_1.default.green("\n✅ Generated Files:"));
|
|
193
|
+
console.log(chalk_1.default.gray(` • ${groupDir}/`));
|
|
194
|
+
components.forEach((comp) => {
|
|
195
|
+
console.log(chalk_1.default.gray(` • ${comp.name}.tsx`));
|
|
196
|
+
});
|
|
197
|
+
console.log(chalk_1.default.gray(` • index.ts`));
|
|
198
|
+
console.log(chalk_1.default.gray(` • page.tsx`));
|
|
199
|
+
}
|
|
200
|
+
async generateComponent(component, group, groupDir, options, userId) {
|
|
201
|
+
try {
|
|
202
|
+
// Use sub-agent orchestration for component generation
|
|
203
|
+
const { orchestrator } = await Promise.resolve().then(() => __importStar(require("../agents/orchestrator/SubAgentOrchestrator")));
|
|
204
|
+
// Execute code generation first
|
|
205
|
+
const codeResult = (await orchestrator.executeAgent("CodeGenSubAgent", {
|
|
206
|
+
component,
|
|
207
|
+
group,
|
|
208
|
+
options: {
|
|
209
|
+
...options,
|
|
210
|
+
useXAI: options.useXAI || false,
|
|
211
|
+
},
|
|
212
|
+
}));
|
|
213
|
+
const componentPath = path.join(groupDir, `${component.name}.tsx`);
|
|
214
|
+
await this.fs.writeFile(componentPath, codeResult.code);
|
|
215
|
+
// Execute QA and docs in parallel
|
|
216
|
+
const [qaResult, docsResult] = (await Promise.all([
|
|
217
|
+
orchestrator.executeAgent("QASubAgent", {
|
|
218
|
+
code: codeResult.code,
|
|
219
|
+
component,
|
|
220
|
+
standards: ["typescript", "react", "accessibility"],
|
|
221
|
+
}),
|
|
222
|
+
orchestrator.executeAgent("DocsSubAgent", {
|
|
223
|
+
code: codeResult.code,
|
|
224
|
+
component,
|
|
225
|
+
format: "readme",
|
|
226
|
+
}),
|
|
227
|
+
]));
|
|
228
|
+
// Write documentation
|
|
229
|
+
const docsPath = path.join(groupDir, `${component.name}.md`);
|
|
230
|
+
await this.fs.writeFile(docsPath, docsResult.content);
|
|
231
|
+
// Log QA results
|
|
232
|
+
if (qaResult.score < 70) {
|
|
233
|
+
console.log(chalk_1.default.yellow(`⚠️ QA Score for ${component.name}: ${qaResult.score}/100`));
|
|
234
|
+
if (qaResult.issues.length > 0) {
|
|
235
|
+
console.log(chalk_1.default.gray(` Issues found: ${qaResult.issues.length}`));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
console.log(chalk_1.default.green(`✅ QA Score for ${component.name}: ${qaResult.score}/100`));
|
|
240
|
+
}
|
|
241
|
+
// Store component in InstantDB
|
|
242
|
+
await this.storeComponent({
|
|
243
|
+
userId,
|
|
244
|
+
name: component.name,
|
|
245
|
+
code: codeResult.code,
|
|
246
|
+
metadata: {
|
|
247
|
+
prompt: component.description || "",
|
|
248
|
+
model: "claude-sonnet",
|
|
249
|
+
executionTime: Date.now(),
|
|
250
|
+
qualityScore: qaResult.score,
|
|
251
|
+
group: group.name,
|
|
252
|
+
dependencies: codeResult.metadata?.dependencies || [],
|
|
253
|
+
tags: codeResult.metadata?.tags || [],
|
|
254
|
+
},
|
|
255
|
+
group: group.name,
|
|
256
|
+
qualityScore: qaResult.score,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
// Fallback to original method if sub-agent system fails
|
|
261
|
+
console.log(chalk_1.default.yellow(`⚠️ Sub-agent system failed for ${component.name}, using fallback method`));
|
|
262
|
+
const code = this.generateComponentCode(component, group, options);
|
|
263
|
+
const componentPath = path.join(groupDir, `${component.name}.tsx`);
|
|
264
|
+
await this.fs.writeFile(componentPath, code);
|
|
265
|
+
// Store component with fallback data
|
|
266
|
+
await this.storeComponent({
|
|
267
|
+
userId,
|
|
268
|
+
name: component.name,
|
|
269
|
+
code,
|
|
270
|
+
metadata: {
|
|
271
|
+
prompt: component.description || "",
|
|
272
|
+
model: "fallback",
|
|
273
|
+
executionTime: Date.now(),
|
|
274
|
+
qualityScore: null,
|
|
275
|
+
group: group.name,
|
|
276
|
+
},
|
|
277
|
+
group: group.name,
|
|
278
|
+
qualityScore: null,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async storeComponent(componentData) {
|
|
283
|
+
try {
|
|
284
|
+
const response = await fetch("http://localhost:3000/api/components/store", {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
"Content-Type": "application/json",
|
|
288
|
+
},
|
|
289
|
+
body: JSON.stringify(componentData),
|
|
290
|
+
});
|
|
291
|
+
const result = (await response.json());
|
|
292
|
+
if (result.success) {
|
|
293
|
+
console.log(chalk_1.default.green(`💾 Component stored: ${componentData.name}`));
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
console.log(chalk_1.default.yellow(`⚠️ Failed to store component: ${result.error}`));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
console.log(chalk_1.default.yellow(`⚠️ Component storage failed: ${error}`));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
generateComponentCode(component, group, options) {
|
|
304
|
+
// Handle both string and object inputs
|
|
305
|
+
let name, description, type, userStories, actionFunctions, dependencies, tags;
|
|
306
|
+
if (typeof component === "string") {
|
|
307
|
+
// Simple string input - create a basic component structure
|
|
308
|
+
name = this.generateComponentName(component);
|
|
309
|
+
description = component;
|
|
310
|
+
type = "form"; // Default type
|
|
311
|
+
userStories = [
|
|
312
|
+
`As a user, I want to use ${name} to ${component.toLowerCase()}`,
|
|
313
|
+
];
|
|
314
|
+
actionFunctions = ["handleSubmit", "handleChange"];
|
|
315
|
+
dependencies = ["react", "tailwindcss"];
|
|
316
|
+
tags = ["ui", "component"];
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
// Structured component object
|
|
320
|
+
({
|
|
321
|
+
name,
|
|
322
|
+
description,
|
|
323
|
+
type,
|
|
324
|
+
userStories,
|
|
325
|
+
actionFunctions,
|
|
326
|
+
dependencies,
|
|
327
|
+
tags,
|
|
328
|
+
} = component);
|
|
329
|
+
}
|
|
330
|
+
// Read branding for styling
|
|
331
|
+
const brandingPath = "context/branding.json";
|
|
332
|
+
let branding = {};
|
|
333
|
+
try {
|
|
334
|
+
if (fs.existsSync(brandingPath)) {
|
|
335
|
+
branding = JSON.parse(fs.readFileSync(brandingPath, "utf8"));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
// Use default branding if file doesn't exist
|
|
340
|
+
}
|
|
341
|
+
return `"use client";
|
|
342
|
+
|
|
343
|
+
import React from "react";
|
|
344
|
+
import { cn } from "@/lib/utils";
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* ${name} Component
|
|
348
|
+
*
|
|
349
|
+
* ${description}
|
|
350
|
+
*
|
|
351
|
+
* User Stories:
|
|
352
|
+
${userStories.map((story) => ` * - ${story}`).join("\n")}
|
|
353
|
+
*
|
|
354
|
+
* Action Functions:
|
|
355
|
+
${actionFunctions.map((func) => ` * - ${func}`).join("\n")}
|
|
356
|
+
*
|
|
357
|
+
* Dependencies: ${dependencies.join(", ")}
|
|
358
|
+
* Tags: ${tags.join(", ")}
|
|
359
|
+
*/
|
|
360
|
+
|
|
361
|
+
interface ${name}Props {
|
|
362
|
+
className?: string;
|
|
363
|
+
// Add specific props based on component type
|
|
364
|
+
${this.generatePropsForType(type)}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function ${name}({
|
|
368
|
+
className,
|
|
369
|
+
${this.generatePropsDestructuring(type)}
|
|
370
|
+
...props
|
|
371
|
+
}: ${name}Props) {
|
|
372
|
+
${this.generateComponentLogic(type, actionFunctions)}
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<div className={cn("${this.generateBaseClasses(type, branding)}", className)} {...props}>
|
|
376
|
+
${this.generateComponentJSX(type, name)}
|
|
377
|
+
</div>
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
${this.generateActionFunctions(actionFunctions, name)}
|
|
382
|
+
|
|
383
|
+
export default ${name};
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
generateComponentName(description) {
|
|
387
|
+
// Convert description to PascalCase component name
|
|
388
|
+
return description
|
|
389
|
+
.split(" ")
|
|
390
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
391
|
+
.join("")
|
|
392
|
+
.replace(/[^a-zA-Z0-9]/g, "");
|
|
393
|
+
}
|
|
394
|
+
generatePropsForType(type) {
|
|
395
|
+
switch (type) {
|
|
396
|
+
case "form":
|
|
397
|
+
return `
|
|
398
|
+
onSubmit?: (data: any) => void;
|
|
399
|
+
loading?: boolean;
|
|
400
|
+
error?: string;`;
|
|
401
|
+
case "layout":
|
|
402
|
+
return `
|
|
403
|
+
children: React.ReactNode;
|
|
404
|
+
sidebar?: React.ReactNode;`;
|
|
405
|
+
case "card":
|
|
406
|
+
return `
|
|
407
|
+
title?: string;
|
|
408
|
+
content?: React.ReactNode;
|
|
409
|
+
actions?: React.ReactNode;`;
|
|
410
|
+
case "button":
|
|
411
|
+
return `
|
|
412
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
|
|
413
|
+
size?: "default" | "sm" | "lg" | "icon";
|
|
414
|
+
disabled?: boolean;
|
|
415
|
+
onClick?: () => void;`;
|
|
416
|
+
default:
|
|
417
|
+
return `
|
|
418
|
+
// Add component-specific props here`;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
generatePropsDestructuring(type) {
|
|
422
|
+
switch (type) {
|
|
423
|
+
case "form":
|
|
424
|
+
return `
|
|
425
|
+
onSubmit,
|
|
426
|
+
loading = false,
|
|
427
|
+
error,`;
|
|
428
|
+
case "layout":
|
|
429
|
+
return `
|
|
430
|
+
children,
|
|
431
|
+
sidebar,`;
|
|
432
|
+
case "card":
|
|
433
|
+
return `
|
|
434
|
+
title,
|
|
435
|
+
content,
|
|
436
|
+
actions,`;
|
|
437
|
+
case "button":
|
|
438
|
+
return `
|
|
439
|
+
variant = "default",
|
|
440
|
+
size = "default",
|
|
441
|
+
disabled = false,
|
|
442
|
+
onClick,`;
|
|
443
|
+
default:
|
|
444
|
+
return "";
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
generateComponentLogic(type, actionFunctions) {
|
|
448
|
+
const logic = [];
|
|
449
|
+
if (type === "form") {
|
|
450
|
+
logic.push(`
|
|
451
|
+
const [formData, setFormData] = React.useState({});
|
|
452
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);`);
|
|
453
|
+
}
|
|
454
|
+
if (type === "form") {
|
|
455
|
+
logic.push(`
|
|
456
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
457
|
+
e.preventDefault();
|
|
458
|
+
setIsSubmitting(true);
|
|
459
|
+
try {
|
|
460
|
+
// TODO: Implement form submission logic
|
|
461
|
+
onSubmit?.(formData);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
console.error("Form submission error:", error);
|
|
464
|
+
} finally {
|
|
465
|
+
setIsSubmitting(false);
|
|
466
|
+
}
|
|
467
|
+
};`);
|
|
468
|
+
}
|
|
469
|
+
return logic.join("\n");
|
|
470
|
+
}
|
|
471
|
+
generateBaseClasses(type, branding) {
|
|
472
|
+
const baseClasses = [];
|
|
473
|
+
switch (type) {
|
|
474
|
+
case "form":
|
|
475
|
+
baseClasses.push("space-y-4", "w-full", "max-w-md");
|
|
476
|
+
break;
|
|
477
|
+
case "layout":
|
|
478
|
+
baseClasses.push("min-h-screen", "flex", "flex-col");
|
|
479
|
+
break;
|
|
480
|
+
case "card":
|
|
481
|
+
baseClasses.push("rounded-lg", "border", "bg-card", "p-6", "shadow-sm");
|
|
482
|
+
break;
|
|
483
|
+
case "button":
|
|
484
|
+
baseClasses.push("inline-flex", "items-center", "justify-center", "rounded-md", "text-sm", "font-medium", "transition-colors", "focus-visible:outline-none", "focus-visible:ring-2", "focus-visible:ring-ring", "focus-visible:ring-offset-2", "disabled:opacity-50", "disabled:pointer-events-none");
|
|
485
|
+
break;
|
|
486
|
+
default:
|
|
487
|
+
baseClasses.push("w-full");
|
|
488
|
+
}
|
|
489
|
+
return baseClasses.join(" ");
|
|
490
|
+
}
|
|
491
|
+
generateComponentJSX(type, name) {
|
|
492
|
+
switch (type) {
|
|
493
|
+
case "form":
|
|
494
|
+
return `
|
|
495
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
496
|
+
<div className="space-y-2">
|
|
497
|
+
<label htmlFor="email" className="text-sm font-medium">
|
|
498
|
+
Email
|
|
499
|
+
</label>
|
|
500
|
+
<input
|
|
501
|
+
id="email"
|
|
502
|
+
type="email"
|
|
503
|
+
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
504
|
+
placeholder="Enter your email"
|
|
505
|
+
required
|
|
506
|
+
/>
|
|
507
|
+
</div>
|
|
508
|
+
<div className="space-y-2">
|
|
509
|
+
<label htmlFor="password" className="text-sm font-medium">
|
|
510
|
+
Password
|
|
511
|
+
</label>
|
|
512
|
+
<input
|
|
513
|
+
id="password"
|
|
514
|
+
type="password"
|
|
515
|
+
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
516
|
+
placeholder="Enter your password"
|
|
517
|
+
required
|
|
518
|
+
/>
|
|
519
|
+
</div>
|
|
520
|
+
{error && (
|
|
521
|
+
<div className="text-sm text-destructive">
|
|
522
|
+
{error}
|
|
523
|
+
</div>
|
|
524
|
+
)}
|
|
525
|
+
<button
|
|
526
|
+
type="submit"
|
|
527
|
+
disabled={loading || isSubmitting}
|
|
528
|
+
className="inline-flex h-10 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
529
|
+
>
|
|
530
|
+
{loading || isSubmitting ? "Loading..." : "Submit"}
|
|
531
|
+
</button>
|
|
532
|
+
</form>`;
|
|
533
|
+
case "layout":
|
|
534
|
+
return `
|
|
535
|
+
<header className="border-b bg-background">
|
|
536
|
+
<div className="container mx-auto px-4 py-4">
|
|
537
|
+
<h1 className="text-2xl font-bold">${name}</h1>
|
|
538
|
+
</div>
|
|
539
|
+
</header>
|
|
540
|
+
<main className="flex-1">
|
|
541
|
+
<div className="container mx-auto px-4 py-8">
|
|
542
|
+
{children}
|
|
543
|
+
</div>
|
|
544
|
+
</main>
|
|
545
|
+
<footer className="border-t bg-background">
|
|
546
|
+
<div className="container mx-auto px-4 py-4 text-center text-sm text-muted-foreground">
|
|
547
|
+
© 2024 MyContext. All rights reserved.
|
|
548
|
+
</div>
|
|
549
|
+
</footer>`;
|
|
550
|
+
case "card":
|
|
551
|
+
return `
|
|
552
|
+
{title && (
|
|
553
|
+
<h3 className="text-lg font-semibold leading-none tracking-tight">
|
|
554
|
+
{title}
|
|
555
|
+
</h3>
|
|
556
|
+
)}
|
|
557
|
+
{content && (
|
|
558
|
+
<div className="mt-4">
|
|
559
|
+
{content}
|
|
560
|
+
</div>
|
|
561
|
+
)}
|
|
562
|
+
{actions && (
|
|
563
|
+
<div className="mt-4 flex justify-end space-x-2">
|
|
564
|
+
{actions}
|
|
565
|
+
</div>
|
|
566
|
+
)}`;
|
|
567
|
+
case "button":
|
|
568
|
+
return `
|
|
569
|
+
<span className="sr-only">${name}</span>
|
|
570
|
+
<span>${name}</span>`;
|
|
571
|
+
default:
|
|
572
|
+
return `
|
|
573
|
+
<div className="p-4">
|
|
574
|
+
<h3 className="text-lg font-semibold">${name}</h3>
|
|
575
|
+
<p className="text-sm text-muted-foreground">
|
|
576
|
+
This is a ${type} component. Customize it based on your needs.
|
|
577
|
+
</p>
|
|
578
|
+
</div>`;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
generateActionFunctions(actionFunctions, componentName) {
|
|
582
|
+
if (actionFunctions.length === 0)
|
|
583
|
+
return "";
|
|
584
|
+
const functions = actionFunctions.map((func) => {
|
|
585
|
+
switch (func) {
|
|
586
|
+
case "handleSubmit":
|
|
587
|
+
return `// Already implemented in component logic`;
|
|
588
|
+
case "handleLogin":
|
|
589
|
+
return `
|
|
590
|
+
export async function handleLogin(email: string, password: string) {
|
|
591
|
+
// TODO: Implement login logic
|
|
592
|
+
console.log("Logging in with:", { email, password });
|
|
593
|
+
return { success: true };
|
|
594
|
+
}`;
|
|
595
|
+
case "handleSignup":
|
|
596
|
+
return `
|
|
597
|
+
export async function handleSignup(email: string, password: string, name: string) {
|
|
598
|
+
// TODO: Implement signup logic
|
|
599
|
+
console.log("Signing up with:", { email, password, name });
|
|
600
|
+
return { success: true };
|
|
601
|
+
}`;
|
|
602
|
+
case "validateEmail":
|
|
603
|
+
return `
|
|
604
|
+
export function validateEmail(email: string): boolean {
|
|
605
|
+
const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
|
|
606
|
+
return emailRegex.test(email);
|
|
607
|
+
}`;
|
|
608
|
+
case "checkUsername":
|
|
609
|
+
return `
|
|
610
|
+
export async function checkUsername(username: string): Promise<boolean> {
|
|
611
|
+
// TODO: Implement username availability check
|
|
612
|
+
console.log("Checking username:", username);
|
|
613
|
+
return true;
|
|
614
|
+
}`;
|
|
615
|
+
default:
|
|
616
|
+
return `
|
|
617
|
+
export function ${func}() {
|
|
618
|
+
// TODO: Implement ${func} logic
|
|
619
|
+
console.log("${func} called");
|
|
620
|
+
}`;
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
return functions.join("\n");
|
|
624
|
+
}
|
|
625
|
+
async generateGroupIndex(group, groupDir) {
|
|
626
|
+
const components = group.components || [];
|
|
627
|
+
const exports = components.map((comp) => `export { ${comp.name} } from "./${comp.name}";`);
|
|
628
|
+
const indexContent = `/**
|
|
629
|
+
* ${group.name} Components
|
|
630
|
+
*
|
|
631
|
+
* ${group.description}
|
|
632
|
+
*
|
|
633
|
+
* Generated components: ${components.length}
|
|
634
|
+
*/
|
|
635
|
+
|
|
636
|
+
${exports.join("\n")}
|
|
637
|
+
|
|
638
|
+
// Re-export default exports
|
|
639
|
+
${components
|
|
640
|
+
.map((comp) => `export { default as ${comp.name}Default } from "./${comp.name}";`)
|
|
641
|
+
.join("\n")}
|
|
642
|
+
`;
|
|
643
|
+
await this.fs.writeFile(path.join(groupDir, "index.ts"), indexContent);
|
|
644
|
+
}
|
|
645
|
+
async generatePreviewPage(group, groupDir) {
|
|
646
|
+
const components = group.components || [];
|
|
647
|
+
const previewContent = `import React from "react";
|
|
648
|
+
${components
|
|
649
|
+
.map((comp) => `import { ${comp.name} } from "./${comp.name}";`)
|
|
650
|
+
.join("\n")}
|
|
651
|
+
|
|
652
|
+
export default function ${group.name}Preview() {
|
|
653
|
+
return (
|
|
654
|
+
<div className="container mx-auto p-8 space-y-8">
|
|
655
|
+
<div className="text-center">
|
|
656
|
+
<h1 className="text-3xl font-bold mb-2">${group.name} Components</h1>
|
|
657
|
+
<p className="text-muted-foreground">${group.description}</p>
|
|
658
|
+
</div>
|
|
659
|
+
|
|
660
|
+
<div className="grid gap-8">
|
|
661
|
+
${components
|
|
662
|
+
.map((comp) => `
|
|
663
|
+
<section className="space-y-4">
|
|
664
|
+
<h2 className="text-2xl font-semibold">${comp.name}</h2>
|
|
665
|
+
<p className="text-muted-foreground">${comp.description}</p>
|
|
666
|
+
<div className="border rounded-lg p-6 bg-card">
|
|
667
|
+
<${comp.name} />
|
|
668
|
+
</div>
|
|
669
|
+
</section>`)
|
|
670
|
+
.join("\n")}
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
`;
|
|
676
|
+
await this.fs.writeFile(path.join(groupDir, "page.tsx"), previewContent);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
exports.GenerateComponentsCommand = GenerateComponentsCommand;
|
|
680
|
+
//# sourceMappingURL=generate-components.js.map
|