mycontext-cli 4.2.5 → 4.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +531 -144
  2. package/dist/README.md +531 -144
  3. package/dist/cli.js +14 -5
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/doctor.d.ts.map +1 -1
  6. package/dist/commands/doctor.js +2 -0
  7. package/dist/commands/doctor.js.map +1 -1
  8. package/dist/commands/generate.d.ts.map +1 -1
  9. package/dist/commands/generate.js +17 -4
  10. package/dist/commands/generate.js.map +1 -1
  11. package/dist/commands/init-interactive.d.ts +127 -0
  12. package/dist/commands/init-interactive.d.ts.map +1 -0
  13. package/dist/commands/init-interactive.js +754 -0
  14. package/dist/commands/init-interactive.js.map +1 -0
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +22 -31
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/doctor/DoctorEngine.d.ts.map +1 -1
  19. package/dist/doctor/DoctorEngine.js +9 -3
  20. package/dist/doctor/DoctorEngine.js.map +1 -1
  21. package/dist/doctor/rules/dead-code-rules.d.ts.map +1 -1
  22. package/dist/doctor/rules/dead-code-rules.js +29 -3
  23. package/dist/doctor/rules/dead-code-rules.js.map +1 -1
  24. package/dist/doctor/rules/index.d.ts +3 -1
  25. package/dist/doctor/rules/index.d.ts.map +1 -1
  26. package/dist/doctor/rules/index.js +7 -1
  27. package/dist/doctor/rules/index.js.map +1 -1
  28. package/dist/doctor/rules/instantdb-rules.d.ts +3 -0
  29. package/dist/doctor/rules/instantdb-rules.d.ts.map +1 -0
  30. package/dist/doctor/rules/instantdb-rules.js +335 -0
  31. package/dist/doctor/rules/instantdb-rules.js.map +1 -0
  32. package/dist/doctor/rules/typescript-rules.d.ts +3 -0
  33. package/dist/doctor/rules/typescript-rules.d.ts.map +1 -0
  34. package/dist/doctor/rules/typescript-rules.js +177 -0
  35. package/dist/doctor/rules/typescript-rules.js.map +1 -0
  36. package/dist/doctor/types.d.ts +1 -0
  37. package/dist/doctor/types.d.ts.map +1 -1
  38. package/dist/package.json +1 -1
  39. package/dist/services/InferenceEngine.d.ts +41 -0
  40. package/dist/services/InferenceEngine.d.ts.map +1 -0
  41. package/dist/services/InferenceEngine.js +307 -0
  42. package/dist/services/InferenceEngine.js.map +1 -0
  43. package/dist/services/Planner.d.ts +77 -0
  44. package/dist/services/Planner.d.ts.map +1 -0
  45. package/dist/services/Planner.js +828 -0
  46. package/dist/services/Planner.js.map +1 -0
  47. package/dist/services/ProjectScanner.d.ts.map +1 -1
  48. package/dist/services/ProjectScanner.js +7 -3
  49. package/dist/services/ProjectScanner.js.map +1 -1
  50. package/dist/types/asl.d.ts +387 -0
  51. package/dist/types/asl.d.ts.map +1 -0
  52. package/dist/types/asl.js +139 -0
  53. package/dist/types/asl.js.map +1 -0
  54. package/package.json +1 -1
@@ -0,0 +1,754 @@
1
+ "use strict";
2
+ /**
3
+ * Init Interactive Command
4
+ *
5
+ * The user-facing entry point for deterministic compilation with AI-powered inference.
6
+ * This command implements the "Self-Organizing Planner" where the AI:
7
+ * 1. Decomposes the project into inference tasks
8
+ * 2. Auto-infers high-confidence tasks (≄90%)
9
+ * 3. Self-critiques its work
10
+ * 4. Learns from user corrections
11
+ * 5. Only prompts for ambiguous items
12
+ *
13
+ * Flow:
14
+ * 1. Ask: "What are you building?"
15
+ * 2. Decompose into tasks with confidence scores
16
+ * 3. Auto-infer high-confidence tasks
17
+ * 4. Show checkpoints for review
18
+ * 5. Prompt for low-confidence items
19
+ * 6. Show final diff preview
20
+ * 7. Get user approval
21
+ * 8. Save ASL to .mycontext/asl.json
22
+ */
23
+ var __importDefault = (this && this.__importDefault) || function (mod) {
24
+ return (mod && mod.__esModule) ? mod : { "default": mod };
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.InitInteractiveCommand = void 0;
28
+ const inquirer_1 = __importDefault(require("inquirer"));
29
+ const chalk_1 = __importDefault(require("chalk"));
30
+ const ora_1 = __importDefault(require("ora"));
31
+ const promises_1 = __importDefault(require("fs/promises"));
32
+ const path_1 = __importDefault(require("path"));
33
+ const Planner_1 = require("../services/Planner");
34
+ const InferenceEngine_1 = require("../services/InferenceEngine");
35
+ const AICore_1 = require("../core/ai/AICore");
36
+ class InitInteractiveCommand {
37
+ constructor() {
38
+ this.planner = new Planner_1.Planner();
39
+ this.inferenceEngine = new InferenceEngine_1.InferenceEngine();
40
+ this.ai = AICore_1.AICore.getInstance({
41
+ fallbackEnabled: true,
42
+ workingDirectory: process.cwd(),
43
+ });
44
+ this.asl = { version: "1.0" };
45
+ }
46
+ /**
47
+ * Main entry point - NEW INFERENCE-BASED FLOW
48
+ */
49
+ async execute() {
50
+ console.log(chalk_1.default.bold.cyan("\nšŸŽÆ MyContext Interactive Setup\n"));
51
+ console.log(chalk_1.default.dim("Describe your project and I'll build a complete specification with minimal questions.\n"));
52
+ try {
53
+ // Step 1: Get initial input
54
+ const initialInput = await this.askInitialQuestion();
55
+ // Step 2: Decompose into tasks
56
+ console.log(chalk_1.default.cyan("\nšŸ¤– Breaking down into tasks...\n"));
57
+ const tasks = await this.planner.decompose(initialInput);
58
+ this.displayTaskDecomposition(tasks);
59
+ // Step 3: Recursive inference loop
60
+ console.log(chalk_1.default.cyan("\nšŸ¤– Auto-inferring high-confidence tasks...\n"));
61
+ await this.recursiveInferenceLoop();
62
+ // Step 4: Show checkpoints and get approval
63
+ await this.showCheckpointsAndConfirm();
64
+ // Step 5: Show final diff preview
65
+ await this.showDiffPreview();
66
+ // Step 6: Get final approval
67
+ const approved = await this.getApproval();
68
+ if (approved) {
69
+ // Step 7: Save ASL
70
+ await this.saveASL();
71
+ console.log(chalk_1.default.green("\nāœ“ Specification complete!\n"));
72
+ console.log(chalk_1.default.cyan("šŸ“Š Summary:"));
73
+ console.log(chalk_1.default.gray(` Auto-inferred: ${this.getAutoInferredCount()} items`));
74
+ console.log(chalk_1.default.gray(` User prompted: ${this.getUserPromptedCount()} items`));
75
+ console.log(chalk_1.default.gray(` Overall confidence: ${this.planner.getState().confidenceScore}%\n`));
76
+ console.log(chalk_1.default.cyan("Next step: Run ") + chalk_1.default.bold("mycontext scaffold --from-manifest\n"));
77
+ }
78
+ else {
79
+ console.log(chalk_1.default.yellow("\n⚠ Setup cancelled\n"));
80
+ }
81
+ }
82
+ catch (error) {
83
+ console.error(chalk_1.default.red("\nāœ— Setup failed:"), error);
84
+ throw error;
85
+ }
86
+ }
87
+ // ============================================================================
88
+ // NEW INFERENCE-BASED METHODS
89
+ // ============================================================================
90
+ /**
91
+ * Ask single initial question
92
+ */
93
+ async askInitialQuestion() {
94
+ const answers = await inquirer_1.default.prompt([
95
+ {
96
+ type: "input",
97
+ name: "description",
98
+ message: "What are you building?",
99
+ validate: (input) => {
100
+ if (!input || input.length < 10) {
101
+ return "Please provide at least 10 characters describing your project";
102
+ }
103
+ return true;
104
+ },
105
+ },
106
+ ]);
107
+ return answers.description;
108
+ }
109
+ /**
110
+ * Display task decomposition
111
+ */
112
+ displayTaskDecomposition(tasks) {
113
+ console.log(chalk_1.default.bold("šŸ“‹ Task decomposition:\n"));
114
+ tasks.forEach((task, idx) => {
115
+ const confidenceColor = task.confidence >= 90 ? chalk_1.default.green :
116
+ task.confidence >= 70 ? chalk_1.default.yellow :
117
+ chalk_1.default.red;
118
+ console.log(chalk_1.default.gray(` ${idx + 1}.`) +
119
+ ` ${task.description} - ` +
120
+ confidenceColor(`${task.confidence}% confidence`));
121
+ });
122
+ console.log(chalk_1.default.gray(`\n Total: ${tasks.length} tasks\n`));
123
+ }
124
+ /**
125
+ * Recursive inference loop
126
+ */
127
+ async recursiveInferenceLoop() {
128
+ const state = this.planner.getState();
129
+ const completedTasks = [];
130
+ const autoInferredTasks = [];
131
+ while (true) {
132
+ // Select next task
133
+ const task = this.planner.selectNextTask();
134
+ if (!task)
135
+ break;
136
+ if (task.autoInfer) {
137
+ // Auto-infer with spinner
138
+ const spinner = (0, ora_1.default)(`Inferring: ${task.description}`).start();
139
+ try {
140
+ // Run inference
141
+ const inference = await this.inferenceEngine.infer(task, this.asl, completedTasks);
142
+ // Self-critique
143
+ const critique = await this.inferenceEngine.selfCritique(inference, this.asl);
144
+ spinner.stop();
145
+ if (critique.confidence >= 90) {
146
+ // Accept inference
147
+ this.asl = { ...this.asl, ...inference.result };
148
+ task.inference = inference.result;
149
+ task.reasoning = inference.reasoning;
150
+ // Reveal context
151
+ const revelations = this.planner.revealContext(task, inference.result);
152
+ revelations.forEach(rev => {
153
+ console.log(chalk_1.default.green("āœ“") +
154
+ ` ${rev.message} ` +
155
+ chalk_1.default.gray(`(${rev.confidence}% confidence)`));
156
+ });
157
+ autoInferredTasks.push(task);
158
+ this.planner.markTaskComplete(task.id);
159
+ completedTasks.push(task);
160
+ // Update dependent tasks
161
+ this.planner.updateDependentTasks(task);
162
+ }
163
+ else {
164
+ // Drop to confirmation
165
+ task.needsConfirmation = true;
166
+ await this.confirmInference(task, inference, critique);
167
+ }
168
+ }
169
+ catch (error) {
170
+ spinner.fail(`Failed to infer: ${task.description}`);
171
+ console.error(chalk_1.default.red(error));
172
+ }
173
+ }
174
+ else if (task.needsConfirmation) {
175
+ // Suggest with confirmation
176
+ await this.confirmInference(task, null);
177
+ }
178
+ else {
179
+ // Direct user prompt
180
+ await this.promptUser(task);
181
+ }
182
+ }
183
+ // Create checkpoint if we auto-inferred anything
184
+ if (autoInferredTasks.length > 0) {
185
+ this.planner.createCheckpoint(autoInferredTasks);
186
+ }
187
+ }
188
+ /**
189
+ * Confirm inference with user
190
+ */
191
+ async confirmInference(task, inference, critique) {
192
+ console.log(chalk_1.default.yellow("\nā“ Need confirmation:\n"));
193
+ console.log(chalk_1.default.dim(` ${task.description}\n`));
194
+ if (inference) {
195
+ console.log(chalk_1.default.dim(" Suggested:"));
196
+ console.log(chalk_1.default.dim(` ${JSON.stringify(inference.result, null, 2)}\n`));
197
+ if (critique && critique.issues.length > 0) {
198
+ console.log(chalk_1.default.yellow(" ⚠ Potential issues:"));
199
+ critique.issues.forEach((issue) => {
200
+ console.log(chalk_1.default.yellow(` • ${issue.message}`));
201
+ });
202
+ console.log();
203
+ }
204
+ }
205
+ const { confirmed } = await inquirer_1.default.prompt([
206
+ {
207
+ type: "confirm",
208
+ name: "confirmed",
209
+ message: "Accept this inference?",
210
+ default: true,
211
+ },
212
+ ]);
213
+ if (confirmed && inference) {
214
+ this.asl = { ...this.asl, ...inference.result };
215
+ task.inference = inference.result;
216
+ this.planner.markTaskComplete(task.id);
217
+ }
218
+ else {
219
+ // Fall back to direct prompt
220
+ await this.promptUser(task);
221
+ }
222
+ }
223
+ /**
224
+ * Prompt user directly for a task
225
+ */
226
+ async promptUser(task) {
227
+ console.log(chalk_1.default.cyan(`\nā“ ${task.description}\n`));
228
+ // Generic text input for now
229
+ // TODO: Add type-specific prompts (select, multi-select, etc.)
230
+ const { answer } = await inquirer_1.default.prompt([
231
+ {
232
+ type: "input",
233
+ name: "answer",
234
+ message: "Your answer:",
235
+ },
236
+ ]);
237
+ // Parse answer with AI
238
+ const spinner = (0, ora_1.default)("Processing your answer...").start();
239
+ try {
240
+ const parsedInference = await this.inferenceEngine.infer({
241
+ ...task,
242
+ description: `${task.description}. User said: "${answer}"`,
243
+ }, this.asl, []);
244
+ this.asl = { ...this.asl, ...parsedInference.result };
245
+ task.inference = parsedInference.result;
246
+ spinner.succeed("Answer processed");
247
+ this.planner.markTaskComplete(task.id);
248
+ }
249
+ catch (error) {
250
+ spinner.fail("Failed to process answer");
251
+ console.error(chalk_1.default.red(error));
252
+ }
253
+ }
254
+ /**
255
+ * Show checkpoints and get confirmation
256
+ */
257
+ async showCheckpointsAndConfirm() {
258
+ const state = this.planner.getState();
259
+ if (state.checkpoints.length === 0)
260
+ return;
261
+ console.log(chalk_1.default.cyan("\nšŸ’” Checkpoint - Review Auto-Inferred Items:\n"));
262
+ state.checkpoints.forEach(checkpoint => {
263
+ console.log(chalk_1.default.bold(` Checkpoint ${checkpoint.id}:`));
264
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.entitiesCreated.length} entities`));
265
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.fieldsAdded} fields`));
266
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.rolesCreated.length} roles`));
267
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.permissionsAdded} permissions`));
268
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.pagesCreated.length} pages`));
269
+ console.log(chalk_1.default.gray(` Overall confidence: ${checkpoint.summary.totalConfidence}%\n`));
270
+ });
271
+ const { action } = await inquirer_1.default.prompt([
272
+ {
273
+ type: "list",
274
+ name: "action",
275
+ message: "What would you like to do?",
276
+ choices: [
277
+ { name: "Continue", value: "continue" },
278
+ { name: "Edit inferences", value: "edit" },
279
+ { name: "Cancel", value: "cancel" },
280
+ ],
281
+ },
282
+ ]);
283
+ if (action === "cancel") {
284
+ throw new Error("User cancelled");
285
+ }
286
+ if (action === "edit") {
287
+ // TODO: Implement interactive editing
288
+ console.log(chalk_1.default.yellow("\n⚠ Interactive editing coming soon. For now, you can edit in the final diff.\n"));
289
+ }
290
+ }
291
+ /**
292
+ * Get counts for summary
293
+ */
294
+ getAutoInferredCount() {
295
+ const state = this.planner.getState();
296
+ return state.tasks.filter(t => t.autoInfer && t.completed).length;
297
+ }
298
+ getUserPromptedCount() {
299
+ const state = this.planner.getState();
300
+ return state.tasks.filter(t => !t.autoInfer && t.completed).length;
301
+ }
302
+ // ============================================================================
303
+ // OLD METHODS (KEEP FOR COMPATIBILITY)
304
+ // ============================================================================
305
+ /**
306
+ * OLD: Recursive clarification loop (legacy, not used in new flow)
307
+ */
308
+ async recursiveClarificationLoop_OLD() {
309
+ let iteration = 0;
310
+ const maxIterations = 20; // Safety limit
311
+ while (!this.planner.isComplete(this.asl) && iteration < maxIterations) {
312
+ iteration++;
313
+ // Validate current ASL
314
+ const validation = this.planner.validate(this.asl);
315
+ console.log(chalk_1.default.cyan(`\nšŸ“Š Completeness: ${chalk_1.default.bold(validation.completeness + "%")}\n`));
316
+ // Generate questions for gaps
317
+ const questions = this.planner.generateQuestions(this.asl);
318
+ if (questions.length === 0) {
319
+ // No more questions, but still not complete
320
+ // This shouldn't happen, but handle gracefully
321
+ console.log(chalk_1.default.yellow("⚠ No more questions, but specification may be incomplete"));
322
+ break;
323
+ }
324
+ // Ask questions in batches by category
325
+ const categorized = this.categorizeQuestions(questions);
326
+ for (const [category, categoryQuestions] of Object.entries(categorized)) {
327
+ await this.askQuestionBatch(category, categoryQuestions);
328
+ }
329
+ }
330
+ if (iteration >= maxIterations) {
331
+ console.log(chalk_1.default.yellow("\n⚠ Reached maximum iterations. Proceeding with current state.\n"));
332
+ }
333
+ }
334
+ /**
335
+ * Ask a batch of questions for a specific category
336
+ */
337
+ async askQuestionBatch(category, questions) {
338
+ console.log(chalk_1.default.bold.cyan(`\nšŸ“ ${this.categoryLabel(category)}\n`));
339
+ for (const question of questions) {
340
+ const answer = await this.askQuestion(question);
341
+ await this.processAnswer(question, answer);
342
+ }
343
+ }
344
+ /**
345
+ * Ask a single question
346
+ */
347
+ async askQuestion(question) {
348
+ const inquirerQuestion = {
349
+ type: this.mapQuestionType(question.type),
350
+ name: "answer",
351
+ message: question.text,
352
+ };
353
+ if (question.options) {
354
+ inquirerQuestion.choices = question.options.map(opt => ({
355
+ name: opt.description ? `${opt.label} - ${opt.description}` : opt.label,
356
+ value: opt.value,
357
+ }));
358
+ }
359
+ if (question.validation?.required) {
360
+ inquirerQuestion.validate = (input) => {
361
+ if (!input)
362
+ return "This field is required";
363
+ return true;
364
+ };
365
+ }
366
+ const { answer } = await inquirer_1.default.prompt([inquirerQuestion]);
367
+ return answer;
368
+ }
369
+ /**
370
+ * Process answer and update ASL
371
+ */
372
+ async processAnswer(question, answer) {
373
+ const [section, ...rest] = question.id.split(".");
374
+ switch (section) {
375
+ case "project":
376
+ this.updateProject(rest.join("."), answer);
377
+ break;
378
+ case "entities":
379
+ await this.updateEntities(rest, answer);
380
+ break;
381
+ case "auth":
382
+ this.updateAuth(rest.join("."), answer);
383
+ break;
384
+ case "permissions":
385
+ this.updatePermissions(rest.join("."), answer);
386
+ break;
387
+ case "pages":
388
+ await this.updatePages(rest, answer);
389
+ break;
390
+ case "design":
391
+ this.updateDesign(rest.join("."), answer);
392
+ break;
393
+ }
394
+ }
395
+ /**
396
+ * Update project section
397
+ */
398
+ updateProject(field, value) {
399
+ if (!this.asl.project) {
400
+ this.asl.project = {
401
+ name: "",
402
+ description: "",
403
+ framework: "nextjs",
404
+ backend: "instantdb",
405
+ };
406
+ }
407
+ this.asl.project[field] = value;
408
+ }
409
+ /**
410
+ * Update entities section (uses AI to parse)
411
+ */
412
+ async updateEntities(path, value) {
413
+ if (!this.asl.entities)
414
+ this.asl.entities = {};
415
+ if (path[0] === "list") {
416
+ // Parse entity list
417
+ const entityNames = value.split(",").map((e) => e.trim());
418
+ entityNames.forEach((name) => {
419
+ if (!this.asl.entities[name]) {
420
+ this.asl.entities[name] = {
421
+ name,
422
+ fields: [],
423
+ timestamps: true,
424
+ };
425
+ }
426
+ });
427
+ }
428
+ else if (path[1] === "fields") {
429
+ // Parse fields for specific entity
430
+ const entityName = path[0];
431
+ if (!entityName)
432
+ return;
433
+ const fields = await this.parseFieldsWithAI(entityName, value);
434
+ if (this.asl.entities && this.asl.entities[entityName]) {
435
+ this.asl.entities[entityName].fields = fields;
436
+ }
437
+ }
438
+ }
439
+ /**
440
+ * Use AI to parse field definitions from natural language
441
+ */
442
+ async parseFieldsWithAI(entityName, userInput) {
443
+ const spinner = (0, ora_1.default)(`Parsing fields for ${entityName}...`).start();
444
+ try {
445
+ const prompt = `Parse the following field definitions for entity "${entityName}" into structured JSON.
446
+
447
+ User input: ${userInput}
448
+
449
+ Return a JSON array of fields with this structure:
450
+ {
451
+ "fields": [
452
+ {
453
+ "name": "fieldName",
454
+ "type": "string" | "number" | "boolean" | "date" | "json" | "ref",
455
+ "required": true | false,
456
+ "description": "field description"
457
+ }
458
+ ]
459
+ }
460
+
461
+ Examples:
462
+ - "title (string, required), content (string), published (boolean)"
463
+ - "email (unique string), age (number), bio (optional string)"
464
+
465
+ Return only valid JSON, no markdown.`;
466
+ const response = await this.ai.generateText(prompt, {
467
+ temperature: 0.1,
468
+ maxTokens: 1000,
469
+ });
470
+ const parsed = JSON.parse(response);
471
+ spinner.succeed(`Parsed ${parsed.fields.length} fields for ${entityName}`);
472
+ return parsed.fields;
473
+ }
474
+ catch (error) {
475
+ spinner.fail("Failed to parse fields");
476
+ console.error(error);
477
+ return [];
478
+ }
479
+ }
480
+ /**
481
+ * Update auth section
482
+ */
483
+ updateAuth(field, value) {
484
+ if (!this.asl.auth) {
485
+ this.asl.auth = {
486
+ provider: "email",
487
+ roles: [],
488
+ };
489
+ }
490
+ if (field === "provider") {
491
+ this.asl.auth.provider = value;
492
+ }
493
+ else if (field === "roles") {
494
+ // Convert array of role names to RoleSpec[]
495
+ if (Array.isArray(value)) {
496
+ this.asl.auth.roles = value.map(name => ({
497
+ name,
498
+ description: `${name} role`,
499
+ }));
500
+ }
501
+ }
502
+ else if (field === "needed") {
503
+ if (!value) {
504
+ delete this.asl.auth;
505
+ }
506
+ }
507
+ }
508
+ /**
509
+ * Update permissions section
510
+ */
511
+ updatePermissions(field, value) {
512
+ if (!this.asl.permissions)
513
+ this.asl.permissions = [];
514
+ if (field === "needed" && value) {
515
+ // Generate default permissions based on roles and entities
516
+ if (this.asl.auth?.roles && this.asl.entities) {
517
+ this.asl.auth.roles.forEach(role => {
518
+ Object.keys(this.asl.entities).forEach(entity => {
519
+ if (role.name === "admin") {
520
+ // Admins get full access
521
+ this.asl.permissions.push({
522
+ role: role.name,
523
+ resource: entity,
524
+ actions: ["create", "read", "update", "delete"],
525
+ });
526
+ }
527
+ else {
528
+ // Regular users get limited access
529
+ this.asl.permissions.push({
530
+ role: role.name,
531
+ resource: entity,
532
+ actions: ["read"],
533
+ condition: { type: "own", field: "user_id" },
534
+ });
535
+ }
536
+ });
537
+ });
538
+ }
539
+ }
540
+ }
541
+ /**
542
+ * Update pages section (uses AI to parse)
543
+ */
544
+ async updatePages(path, value) {
545
+ if (!this.asl.pages)
546
+ this.asl.pages = [];
547
+ if (path[0] === "list") {
548
+ // Parse page list
549
+ const pages = await this.parsePagesWithAI(value);
550
+ this.asl.pages = pages;
551
+ }
552
+ else {
553
+ // Update specific page field
554
+ const match = path[0]?.match(/\d+/);
555
+ const pageIndex = parseInt(match?.[0] || "0");
556
+ const field = path[1];
557
+ if (!this.asl.pages || !this.asl.pages[pageIndex] || !field)
558
+ return;
559
+ this.asl.pages[pageIndex][field] = value;
560
+ }
561
+ }
562
+ /**
563
+ * Use AI to parse page definitions
564
+ */
565
+ async parsePagesWithAI(userInput) {
566
+ const spinner = (0, ora_1.default)("Parsing pages...").start();
567
+ try {
568
+ const prompt = `Parse the following page definitions into structured JSON.
569
+
570
+ User input: ${userInput}
571
+
572
+ Return a JSON array of pages with this structure:
573
+ {
574
+ "pages": [
575
+ {
576
+ "path": "/path",
577
+ "name": "PageName",
578
+ "type": "page",
579
+ "title": "Page Title",
580
+ "public": true | false
581
+ }
582
+ ]
583
+ }
584
+
585
+ Examples:
586
+ - "Home (/), Posts (/posts), Profile (/profile)"
587
+ - "Landing page (public), Dashboard (/dashboard, protected), Settings"
588
+
589
+ Return only valid JSON, no markdown.`;
590
+ const response = await this.ai.generateText(prompt, {
591
+ temperature: 0.1,
592
+ maxTokens: 1000,
593
+ });
594
+ const parsed = JSON.parse(response);
595
+ spinner.succeed(`Parsed ${parsed.pages.length} pages`);
596
+ return parsed.pages;
597
+ }
598
+ catch (error) {
599
+ spinner.fail("Failed to parse pages");
600
+ console.error(error);
601
+ return [];
602
+ }
603
+ }
604
+ /**
605
+ * Update design section
606
+ */
607
+ updateDesign(field, value) {
608
+ if (!this.asl.design) {
609
+ this.asl.design = {
610
+ theme: "light",
611
+ };
612
+ }
613
+ this.asl.design[field] = value;
614
+ }
615
+ /**
616
+ * Show diff preview
617
+ */
618
+ async showDiffPreview() {
619
+ console.log(chalk_1.default.bold.cyan("\nšŸ“‹ Generation Preview\n"));
620
+ const diff = this.planner.generateDiff(this.asl);
621
+ // Summary
622
+ console.log(chalk_1.default.bold("Summary:"));
623
+ console.log(chalk_1.default.green(` āœ“ ${diff.summary.totalFiles} files will be generated`));
624
+ console.log(chalk_1.default.cyan(` āœ“ ~${Math.round(diff.summary.linesAdded)} lines of code`));
625
+ console.log();
626
+ // File breakdown
627
+ console.log(chalk_1.default.bold("Files:"));
628
+ const filesByType = this.groupFilesByType(diff.files);
629
+ Object.entries(filesByType).forEach(([type, files]) => {
630
+ console.log(chalk_1.default.cyan(` ${this.fileTypeLabel(type)}: ${files.length}`));
631
+ });
632
+ console.log();
633
+ // Registries
634
+ if (diff.registries.length > 0) {
635
+ console.log(chalk_1.default.bold("Registries:"));
636
+ diff.registries.forEach(reg => {
637
+ console.log(chalk_1.default.cyan(` ${reg.type}: ${reg.added.length} items`));
638
+ });
639
+ console.log();
640
+ }
641
+ // Warnings
642
+ if (diff.warnings && diff.warnings.length > 0) {
643
+ console.log(chalk_1.default.bold.yellow("⚠ Warnings:"));
644
+ diff.warnings.forEach(warning => {
645
+ console.log(chalk_1.default.yellow(` - ${warning}`));
646
+ });
647
+ console.log();
648
+ }
649
+ // Show sample files
650
+ console.log(chalk_1.default.bold("Sample files:\n"));
651
+ diff.files.slice(0, 5).forEach(file => {
652
+ console.log(chalk_1.default.dim(` ${file.path}`));
653
+ if (file.preview) {
654
+ console.log(chalk_1.default.dim(` ${file.preview.split("\n").slice(0, 2).join("\n ")}`));
655
+ }
656
+ });
657
+ if (diff.files.length > 5) {
658
+ console.log(chalk_1.default.dim(` ... and ${diff.files.length - 5} more files`));
659
+ }
660
+ console.log();
661
+ }
662
+ /**
663
+ * Get user approval
664
+ */
665
+ async getApproval() {
666
+ const { approve } = await inquirer_1.default.prompt([
667
+ {
668
+ type: "confirm",
669
+ name: "approve",
670
+ message: "Generate these files?",
671
+ default: true,
672
+ },
673
+ ]);
674
+ return approve;
675
+ }
676
+ /**
677
+ * Save ASL to .mycontext/asl.json
678
+ */
679
+ async saveASL() {
680
+ const spinner = (0, ora_1.default)("Saving manifest...").start();
681
+ try {
682
+ // Ensure .mycontext directory exists
683
+ const mycontextDir = path_1.default.join(process.cwd(), ".mycontext");
684
+ await promises_1.default.mkdir(mycontextDir, { recursive: true });
685
+ // Save ASL
686
+ const aslPath = path_1.default.join(mycontextDir, "asl.json");
687
+ await promises_1.default.writeFile(aslPath, JSON.stringify(this.asl, null, 2), "utf-8");
688
+ spinner.succeed(`Manifest saved to ${chalk_1.default.cyan(".mycontext/asl.json")}`);
689
+ }
690
+ catch (error) {
691
+ spinner.fail("Failed to save manifest");
692
+ throw error;
693
+ }
694
+ }
695
+ // ============================================================================
696
+ // HELPER METHODS
697
+ // ============================================================================
698
+ categorizeQuestions(questions) {
699
+ const categorized = {};
700
+ questions.forEach(q => {
701
+ if (!categorized[q.category]) {
702
+ categorized[q.category] = [];
703
+ }
704
+ categorized[q.category].push(q);
705
+ });
706
+ return categorized;
707
+ }
708
+ categoryLabel(category) {
709
+ const labels = {
710
+ project: "Project Information",
711
+ entities: "Data Models (Entities)",
712
+ auth: "Authentication",
713
+ permissions: "Permissions (RBAC)",
714
+ pages: "Pages & Routes",
715
+ design: "Design & Styling",
716
+ };
717
+ return labels[category] || category;
718
+ }
719
+ mapQuestionType(type) {
720
+ const mapping = {
721
+ text: "input",
722
+ number: "number",
723
+ boolean: "confirm",
724
+ select: "list",
725
+ "multi-select": "checkbox",
726
+ "entity-builder": "input",
727
+ "field-builder": "input",
728
+ };
729
+ return mapping[type] || "input";
730
+ }
731
+ groupFilesByType(files) {
732
+ const grouped = {};
733
+ files.forEach(file => {
734
+ if (!grouped[file.type]) {
735
+ grouped[file.type] = [];
736
+ }
737
+ grouped[file.type].push(file);
738
+ });
739
+ return grouped;
740
+ }
741
+ fileTypeLabel(type) {
742
+ const labels = {
743
+ schema: "Schema",
744
+ type: "Types",
745
+ page: "Pages",
746
+ component: "Components",
747
+ action: "Server Actions",
748
+ config: "Configuration",
749
+ };
750
+ return labels[type] || type;
751
+ }
752
+ }
753
+ exports.InitInteractiveCommand = InitInteractiveCommand;
754
+ //# sourceMappingURL=init-interactive.js.map