mycontext-cli 4.2.6 → 4.2.8

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 (117) 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/generate.d.ts.map +1 -1
  6. package/dist/commands/generate.js +19 -47
  7. package/dist/commands/generate.js.map +1 -1
  8. package/dist/commands/init-interactive.d.ts +147 -0
  9. package/dist/commands/init-interactive.d.ts.map +1 -0
  10. package/dist/commands/init-interactive.js +917 -0
  11. package/dist/commands/init-interactive.js.map +1 -0
  12. package/dist/config/shadcn-catalog.json +93 -0
  13. package/dist/core/brain/BrainClient.d.ts +1 -1
  14. package/dist/core/brain/BrainClient.d.ts.map +1 -1
  15. package/dist/core/brain/BrainClient.js +5 -5
  16. package/dist/core/brain/BrainClient.js.map +1 -1
  17. package/dist/doctor/DoctorEngine.d.ts.map +1 -1
  18. package/dist/doctor/DoctorEngine.js +21 -11
  19. package/dist/doctor/DoctorEngine.js.map +1 -1
  20. package/dist/doctor/rules/dead-code-rules.d.ts.map +1 -1
  21. package/dist/doctor/rules/dead-code-rules.js +33 -0
  22. package/dist/doctor/rules/dead-code-rules.js.map +1 -1
  23. package/dist/doctor/rules/index.d.ts +3 -1
  24. package/dist/doctor/rules/index.d.ts.map +1 -1
  25. package/dist/doctor/rules/index.js +7 -1
  26. package/dist/doctor/rules/index.js.map +1 -1
  27. package/dist/doctor/rules/instantdb-rules.d.ts +3 -0
  28. package/dist/doctor/rules/instantdb-rules.d.ts.map +1 -0
  29. package/dist/doctor/rules/instantdb-rules.js +544 -0
  30. package/dist/doctor/rules/instantdb-rules.js.map +1 -0
  31. package/dist/doctor/rules/nextjs-rules.d.ts.map +1 -1
  32. package/dist/doctor/rules/nextjs-rules.js +53 -3
  33. package/dist/doctor/rules/nextjs-rules.js.map +1 -1
  34. package/dist/doctor/rules/typescript-rules.d.ts +3 -0
  35. package/dist/doctor/rules/typescript-rules.d.ts.map +1 -0
  36. package/dist/doctor/rules/typescript-rules.js +177 -0
  37. package/dist/doctor/rules/typescript-rules.js.map +1 -0
  38. package/dist/package.json +4 -2
  39. package/dist/services/ComponentInferenceEngine.d.ts +66 -0
  40. package/dist/services/ComponentInferenceEngine.d.ts.map +1 -0
  41. package/dist/services/ComponentInferenceEngine.js +302 -0
  42. package/dist/services/ComponentInferenceEngine.js.map +1 -0
  43. package/dist/services/ComponentRegistry.d.ts +61 -0
  44. package/dist/services/ComponentRegistry.d.ts.map +1 -0
  45. package/dist/services/ComponentRegistry.js +128 -0
  46. package/dist/services/ComponentRegistry.js.map +1 -0
  47. package/dist/services/InferenceEngine.d.ts +41 -0
  48. package/dist/services/InferenceEngine.d.ts.map +1 -0
  49. package/dist/services/InferenceEngine.js +307 -0
  50. package/dist/services/InferenceEngine.js.map +1 -0
  51. package/dist/services/Planner.d.ts +77 -0
  52. package/dist/services/Planner.d.ts.map +1 -0
  53. package/dist/services/Planner.js +828 -0
  54. package/dist/services/Planner.js.map +1 -0
  55. package/dist/services/ProjectScanner.d.ts.map +1 -1
  56. package/dist/services/ProjectScanner.js +15 -1
  57. package/dist/services/ProjectScanner.js.map +1 -1
  58. package/dist/services/ScaffoldEngine.d.ts +87 -0
  59. package/dist/services/ScaffoldEngine.d.ts.map +1 -0
  60. package/dist/services/ScaffoldEngine.js +409 -0
  61. package/dist/services/ScaffoldEngine.js.map +1 -0
  62. package/dist/services/ScaffoldPreview.d.ts +62 -0
  63. package/dist/services/ScaffoldPreview.d.ts.map +1 -0
  64. package/dist/services/ScaffoldPreview.js +292 -0
  65. package/dist/services/ScaffoldPreview.js.map +1 -0
  66. package/dist/services/TemplateEngine.d.ts +136 -0
  67. package/dist/services/TemplateEngine.d.ts.map +1 -0
  68. package/dist/services/TemplateEngine.js +483 -0
  69. package/dist/services/TemplateEngine.js.map +1 -0
  70. package/dist/services/TemplateHelpers.d.ts +9 -0
  71. package/dist/services/TemplateHelpers.d.ts.map +1 -0
  72. package/dist/services/TemplateHelpers.js +212 -0
  73. package/dist/services/TemplateHelpers.js.map +1 -0
  74. package/dist/templates/actions/auth-actions.ts.hbs +140 -0
  75. package/dist/templates/actions/crud-actions.ts.hbs +113 -0
  76. package/dist/templates/components/auth/login-form.tsx.hbs +67 -0
  77. package/dist/templates/components/auth/login-skeleton.tsx.hbs +24 -0
  78. package/dist/templates/components/auth/register-form.tsx.hbs +116 -0
  79. package/dist/templates/components/crud/entity-card.tsx.hbs +71 -0
  80. package/dist/templates/components/crud/entity-form.tsx.hbs +158 -0
  81. package/dist/templates/components/crud/entity-skeleton.tsx.hbs +90 -0
  82. package/dist/templates/components/crud/entity-table.tsx.hbs +129 -0
  83. package/dist/templates/components/ui/button.tsx.hbs +53 -0
  84. package/dist/templates/components/ui/card.tsx.hbs +68 -0
  85. package/dist/templates/components/ui/input.tsx.hbs +33 -0
  86. package/dist/templates/components/ui/label.tsx.hbs +20 -0
  87. package/dist/templates/components/ui/skeleton.tsx.hbs +15 -0
  88. package/dist/templates/components/ui/theme-provider.tsx.hbs +66 -0
  89. package/dist/templates/components/ui/theme-toggle.tsx.hbs +30 -0
  90. package/dist/templates/config/app.css.hbs +150 -0
  91. package/dist/templates/layouts/dashboard-layout.tsx.hbs +69 -0
  92. package/dist/templates/layouts/error.tsx.hbs +51 -0
  93. package/dist/templates/layouts/loading.tsx.hbs +22 -0
  94. package/dist/templates/layouts/not-found.tsx.hbs +24 -0
  95. package/dist/templates/layouts/root-layout.tsx.hbs +40 -0
  96. package/dist/templates/lib/instant.ts.hbs +19 -0
  97. package/dist/templates/lib/utils.ts.hbs +24 -0
  98. package/dist/templates/pages/auth/login-page.tsx.hbs +30 -0
  99. package/dist/templates/pages/auth/register-page.tsx.hbs +38 -0
  100. package/dist/templates/pages/crud/create-page.tsx.hbs +42 -0
  101. package/dist/templates/pages/crud/detail-page.tsx.hbs +90 -0
  102. package/dist/templates/pages/crud/list-page.tsx.hbs +60 -0
  103. package/dist/templates/pages/crud/loading.tsx.hbs +13 -0
  104. package/dist/templates/pages/landing-page.tsx.hbs +111 -0
  105. package/dist/types/asl.d.ts +387 -0
  106. package/dist/types/asl.d.ts.map +1 -0
  107. package/dist/types/asl.js +139 -0
  108. package/dist/types/asl.js.map +1 -0
  109. package/dist/types/living-context.d.ts +1 -1
  110. package/dist/types/living-context.d.ts.map +1 -1
  111. package/dist/utils/FileGenerator.js +3 -3
  112. package/dist/utils/FileGenerator.js.map +1 -1
  113. package/dist/utils/generateTypesFromSchema.d.ts +47 -0
  114. package/dist/utils/generateTypesFromSchema.d.ts.map +1 -0
  115. package/dist/utils/generateTypesFromSchema.js +298 -0
  116. package/dist/utils/generateTypesFromSchema.js.map +1 -0
  117. package/package.json +4 -2
@@ -0,0 +1,917 @@
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
+ const ScaffoldEngine_1 = require("../services/ScaffoldEngine");
37
+ const ScaffoldPreview_1 = require("../services/ScaffoldPreview");
38
+ class InitInteractiveCommand {
39
+ constructor() {
40
+ this.planner = new Planner_1.Planner();
41
+ this.inferenceEngine = new InferenceEngine_1.InferenceEngine();
42
+ this.ai = AICore_1.AICore.getInstance({
43
+ fallbackEnabled: true,
44
+ workingDirectory: process.cwd(),
45
+ });
46
+ this.asl = {
47
+ version: "1.0",
48
+ project: {
49
+ name: "my-app",
50
+ description: "",
51
+ framework: "nextjs",
52
+ backend: "instantdb",
53
+ styling: "tailwind-shadcn",
54
+ packageManager: "pnpm",
55
+ typescript: true
56
+ }
57
+ };
58
+ }
59
+ /**
60
+ * Main entry point - NEW INFERENCE-BASED FLOW
61
+ */
62
+ async execute() {
63
+ console.log(chalk_1.default.bold.cyan("\nšŸŽÆ MyContext Interactive Setup\n"));
64
+ console.log(chalk_1.default.dim("Describe your project and I'll build a complete specification with minimal questions.\n"));
65
+ try {
66
+ // Step 1: Get initial input
67
+ const initialInput = await this.askInitialQuestion();
68
+ // Step 2: Decompose into tasks
69
+ console.log(chalk_1.default.cyan("\nšŸ¤– Breaking down into tasks...\n"));
70
+ const tasks = await this.planner.decompose(initialInput);
71
+ // Seed project name from input if possible (basic heuristic)
72
+ if (this.asl.project) {
73
+ const nameMatch = initialInput.match(/^([A-Z][a-zA-Z0-9]+):/);
74
+ if (nameMatch && nameMatch[1]) {
75
+ this.asl.project.name = nameMatch[1].toLowerCase();
76
+ }
77
+ else {
78
+ // Default to a sanitized version of first 2 words
79
+ const words = initialInput.split(' ').slice(0, 2).map(w => w.replace(/[^a-zA-Z0-9]/g, '').toLowerCase());
80
+ this.asl.project.name = words.join('-') || "my-app";
81
+ }
82
+ }
83
+ this.displayTaskDecomposition(tasks);
84
+ // Step 3: Recursive inference loop
85
+ console.log(chalk_1.default.cyan("\nšŸ¤– Auto-inferring high-confidence tasks...\n"));
86
+ await this.recursiveInferenceLoop();
87
+ // Step 4: Show checkpoints and get approval
88
+ await this.showCheckpointsAndConfirm();
89
+ // Step 5: Show final diff preview
90
+ await this.showDiffPreview();
91
+ // Step 6: Get final approval
92
+ const approved = await this.getApproval();
93
+ if (approved) {
94
+ // Step 7: Save ASL
95
+ await this.saveASL();
96
+ console.log(chalk_1.default.green("\nāœ“ Specification complete!\n"));
97
+ console.log(chalk_1.default.cyan("šŸ“Š Summary:"));
98
+ console.log(chalk_1.default.gray(` Auto-inferred: ${this.getAutoInferredCount()} items`));
99
+ console.log(chalk_1.default.gray(` User prompted: ${this.getUserPromptedCount()} items`));
100
+ console.log(chalk_1.default.gray(` Overall confidence: ${this.planner.getState().confidenceScore}%\n`));
101
+ // Step 8: Ask about scaffolding
102
+ const shouldScaffold = await this.askScaffold();
103
+ if (shouldScaffold) {
104
+ // Step 9: Show full scaffold preview
105
+ await this.showScaffoldPreview();
106
+ // Step 10: Get scaffold confirmation
107
+ const scaffoldConfirmed = await this.confirmScaffold();
108
+ if (scaffoldConfirmed) {
109
+ // Step 11: Run scaffold
110
+ await this.runScaffold();
111
+ }
112
+ else {
113
+ console.log(chalk_1.default.cyan("\nYou can run scaffolding later with: ") + chalk_1.default.bold("mycontext scaffold --from-manifest\n"));
114
+ }
115
+ }
116
+ else {
117
+ console.log(chalk_1.default.cyan("\nNext step: Run ") + chalk_1.default.bold("mycontext scaffold --from-manifest\n"));
118
+ }
119
+ }
120
+ else {
121
+ console.log(chalk_1.default.yellow("\n⚠ Setup cancelled\n"));
122
+ }
123
+ }
124
+ catch (error) {
125
+ console.error(chalk_1.default.red("\nāœ— Setup failed:"), error);
126
+ throw error;
127
+ }
128
+ }
129
+ // ============================================================================
130
+ // NEW INFERENCE-BASED METHODS
131
+ // ============================================================================
132
+ /**
133
+ * Ask single initial question
134
+ */
135
+ async askInitialQuestion() {
136
+ const answers = await inquirer_1.default.prompt([
137
+ {
138
+ type: "input",
139
+ name: "description",
140
+ message: "What are you building?",
141
+ validate: (input) => {
142
+ if (!input || input.length < 10) {
143
+ return "Please provide at least 10 characters describing your project";
144
+ }
145
+ return true;
146
+ },
147
+ },
148
+ ]);
149
+ return answers.description;
150
+ }
151
+ /**
152
+ * Display task decomposition
153
+ */
154
+ displayTaskDecomposition(tasks) {
155
+ console.log(chalk_1.default.bold("šŸ“‹ Task decomposition:\n"));
156
+ tasks.forEach((task, idx) => {
157
+ const confidenceColor = task.confidence >= 90 ? chalk_1.default.green :
158
+ task.confidence >= 70 ? chalk_1.default.yellow :
159
+ chalk_1.default.red;
160
+ console.log(chalk_1.default.gray(` ${idx + 1}.`) +
161
+ ` ${task.description} - ` +
162
+ confidenceColor(`${task.confidence}% confidence`));
163
+ });
164
+ console.log(chalk_1.default.gray(`\n Total: ${tasks.length} tasks\n`));
165
+ }
166
+ /**
167
+ * Recursive inference loop
168
+ */
169
+ async recursiveInferenceLoop() {
170
+ const state = this.planner.getState();
171
+ const completedTasks = [];
172
+ const autoInferredTasks = [];
173
+ while (true) {
174
+ // Select next task
175
+ const task = this.planner.selectNextTask();
176
+ if (!task)
177
+ break;
178
+ if (task.autoInfer) {
179
+ // Auto-infer with spinner
180
+ const spinner = (0, ora_1.default)(`Inferring: ${task.description}`).start();
181
+ try {
182
+ // Run inference
183
+ const inference = await this.inferenceEngine.infer(task, this.asl, completedTasks);
184
+ // Self-critique
185
+ const critique = await this.inferenceEngine.selfCritique(inference, this.asl);
186
+ spinner.stop();
187
+ if (critique.confidence >= 90) {
188
+ // Accept inference
189
+ this.mergeInference(inference.result);
190
+ task.inference = inference.result;
191
+ task.reasoning = inference.reasoning;
192
+ // Reveal context
193
+ const revelations = this.planner.revealContext(task, inference.result);
194
+ revelations.forEach(rev => {
195
+ console.log(chalk_1.default.green("āœ“") +
196
+ ` ${rev.message} ` +
197
+ chalk_1.default.gray(`(${rev.confidence}% confidence)`));
198
+ });
199
+ autoInferredTasks.push(task);
200
+ this.planner.markTaskComplete(task.id);
201
+ completedTasks.push(task);
202
+ // Update dependent tasks
203
+ this.planner.updateDependentTasks(task);
204
+ }
205
+ else {
206
+ // Drop to confirmation
207
+ task.needsConfirmation = true;
208
+ await this.confirmInference(task, inference, critique);
209
+ }
210
+ }
211
+ catch (error) {
212
+ spinner.fail(`Failed to infer: ${task.description}`);
213
+ console.error(chalk_1.default.red(error));
214
+ }
215
+ }
216
+ else if (task.needsConfirmation) {
217
+ // Suggest with confirmation
218
+ await this.confirmInference(task, null);
219
+ }
220
+ else {
221
+ // Direct user prompt
222
+ await this.promptUser(task);
223
+ }
224
+ }
225
+ // Create checkpoint if we auto-inferred anything
226
+ if (autoInferredTasks.length > 0) {
227
+ this.planner.createCheckpoint(autoInferredTasks);
228
+ }
229
+ }
230
+ /**
231
+ * Confirm inference with user
232
+ */
233
+ async confirmInference(task, inference, critique) {
234
+ console.log(chalk_1.default.yellow("\nā“ Need confirmation:\n"));
235
+ console.log(chalk_1.default.dim(` ${task.description}\n`));
236
+ if (inference) {
237
+ console.log(chalk_1.default.dim(" Suggested:"));
238
+ console.log(chalk_1.default.dim(` ${JSON.stringify(inference.result, null, 2)}\n`));
239
+ if (critique && critique.issues.length > 0) {
240
+ console.log(chalk_1.default.yellow(" ⚠ Potential issues:"));
241
+ critique.issues.forEach((issue) => {
242
+ console.log(chalk_1.default.yellow(` • ${issue.message}`));
243
+ });
244
+ console.log();
245
+ }
246
+ }
247
+ const { confirmed } = await inquirer_1.default.prompt([
248
+ {
249
+ type: "confirm",
250
+ name: "confirmed",
251
+ message: "Accept this inference?",
252
+ default: true,
253
+ },
254
+ ]);
255
+ if (confirmed && inference) {
256
+ this.mergeInference(inference.result);
257
+ task.inference = inference.result;
258
+ this.planner.markTaskComplete(task.id);
259
+ }
260
+ else {
261
+ // Fall back to direct prompt
262
+ await this.promptUser(task);
263
+ }
264
+ }
265
+ /**
266
+ * Prompt user directly for a task
267
+ */
268
+ async promptUser(task) {
269
+ console.log(chalk_1.default.cyan(`\nā“ ${task.description}\n`));
270
+ // Generic text input for now
271
+ // TODO: Add type-specific prompts (select, multi-select, etc.)
272
+ const { answer } = await inquirer_1.default.prompt([
273
+ {
274
+ type: "input",
275
+ name: "answer",
276
+ message: "Your answer:",
277
+ },
278
+ ]);
279
+ // Parse answer with AI
280
+ const spinner = (0, ora_1.default)("Processing your answer...").start();
281
+ try {
282
+ const parsedInference = await this.inferenceEngine.infer({
283
+ ...task,
284
+ description: `${task.description}. User said: "${answer}"`,
285
+ }, this.asl, []);
286
+ this.mergeInference(parsedInference.result);
287
+ task.inference = parsedInference.result;
288
+ spinner.succeed("Answer processed");
289
+ this.planner.markTaskComplete(task.id);
290
+ }
291
+ catch (error) {
292
+ spinner.fail("Failed to process answer");
293
+ console.error(chalk_1.default.red(error));
294
+ }
295
+ }
296
+ /**
297
+ * Helper to merge inference results into current ASL
298
+ */
299
+ mergeInference(result) {
300
+ if (!result)
301
+ return;
302
+ // Handle entities (special case for array to object)
303
+ if (result.entities) {
304
+ if (!this.asl.entities)
305
+ this.asl.entities = {};
306
+ if (Array.isArray(result.entities)) {
307
+ result.entities.forEach((entity) => {
308
+ this.asl.entities[entity.name] = entity;
309
+ });
310
+ }
311
+ else {
312
+ this.asl.entities = { ...this.asl.entities, ...result.entities };
313
+ }
314
+ }
315
+ // Merge other sections
316
+ if (result.project)
317
+ this.asl.project = { ...this.asl.project, ...result.project };
318
+ if (result.auth)
319
+ this.asl.auth = { ...this.asl.auth, ...result.auth };
320
+ if (result.permissions) {
321
+ this.asl.permissions = [...(this.asl.permissions || []), ...result.permissions];
322
+ }
323
+ if (result.pages) {
324
+ this.asl.pages = [...(this.asl.pages || []), ...result.pages];
325
+ }
326
+ if (result.design)
327
+ this.asl.design = { ...this.asl.design, ...result.design };
328
+ }
329
+ /**
330
+ * Show checkpoints and get confirmation
331
+ */
332
+ async showCheckpointsAndConfirm() {
333
+ const state = this.planner.getState();
334
+ if (state.checkpoints.length === 0)
335
+ return;
336
+ console.log(chalk_1.default.cyan("\nšŸ’” Checkpoint - Review Auto-Inferred Items:\n"));
337
+ state.checkpoints.forEach(checkpoint => {
338
+ console.log(chalk_1.default.bold(` Checkpoint ${checkpoint.id}:`));
339
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.entitiesCreated.length} entities`));
340
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.fieldsAdded} fields`));
341
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.rolesCreated.length} roles`));
342
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.permissionsAdded} permissions`));
343
+ console.log(chalk_1.default.gray(` āœ“ ${checkpoint.summary.pagesCreated.length} pages`));
344
+ console.log(chalk_1.default.gray(` Overall confidence: ${checkpoint.summary.totalConfidence}%\n`));
345
+ });
346
+ const { action } = await inquirer_1.default.prompt([
347
+ {
348
+ type: "list",
349
+ name: "action",
350
+ message: "What would you like to do?",
351
+ choices: [
352
+ { name: "Continue", value: "continue" },
353
+ { name: "Edit inferences", value: "edit" },
354
+ { name: "Cancel", value: "cancel" },
355
+ ],
356
+ },
357
+ ]);
358
+ if (action === "cancel") {
359
+ throw new Error("User cancelled");
360
+ }
361
+ if (action === "edit") {
362
+ // TODO: Implement interactive editing
363
+ console.log(chalk_1.default.yellow("\n⚠ Interactive editing coming soon. For now, you can edit in the final diff.\n"));
364
+ }
365
+ }
366
+ /**
367
+ * Get counts for summary
368
+ */
369
+ getAutoInferredCount() {
370
+ const state = this.planner.getState();
371
+ return state.tasks.filter(t => t.autoInfer && t.completed).length;
372
+ }
373
+ getUserPromptedCount() {
374
+ const state = this.planner.getState();
375
+ return state.tasks.filter(t => !t.autoInfer && t.completed).length;
376
+ }
377
+ // ============================================================================
378
+ // OLD METHODS (KEEP FOR COMPATIBILITY)
379
+ // ============================================================================
380
+ /**
381
+ * OLD: Recursive clarification loop (legacy, not used in new flow)
382
+ */
383
+ async recursiveClarificationLoop_OLD() {
384
+ let iteration = 0;
385
+ const maxIterations = 20; // Safety limit
386
+ while (!this.planner.isComplete(this.asl) && iteration < maxIterations) {
387
+ iteration++;
388
+ // Validate current ASL
389
+ const validation = this.planner.validate(this.asl);
390
+ console.log(chalk_1.default.cyan(`\nšŸ“Š Completeness: ${chalk_1.default.bold(validation.completeness + "%")}\n`));
391
+ // Generate questions for gaps
392
+ const questions = this.planner.generateQuestions(this.asl);
393
+ if (questions.length === 0) {
394
+ // No more questions, but still not complete
395
+ // This shouldn't happen, but handle gracefully
396
+ console.log(chalk_1.default.yellow("⚠ No more questions, but specification may be incomplete"));
397
+ break;
398
+ }
399
+ // Ask questions in batches by category
400
+ const categorized = this.categorizeQuestions(questions);
401
+ for (const [category, categoryQuestions] of Object.entries(categorized)) {
402
+ await this.askQuestionBatch(category, categoryQuestions);
403
+ }
404
+ }
405
+ if (iteration >= maxIterations) {
406
+ console.log(chalk_1.default.yellow("\n⚠ Reached maximum iterations. Proceeding with current state.\n"));
407
+ }
408
+ }
409
+ /**
410
+ * Ask a batch of questions for a specific category
411
+ */
412
+ async askQuestionBatch(category, questions) {
413
+ console.log(chalk_1.default.bold.cyan(`\nšŸ“ ${this.categoryLabel(category)}\n`));
414
+ for (const question of questions) {
415
+ const answer = await this.askQuestion(question);
416
+ await this.processAnswer(question, answer);
417
+ }
418
+ }
419
+ /**
420
+ * Ask a single question
421
+ */
422
+ async askQuestion(question) {
423
+ const inquirerQuestion = {
424
+ type: this.mapQuestionType(question.type),
425
+ name: "answer",
426
+ message: question.text,
427
+ };
428
+ if (question.options) {
429
+ inquirerQuestion.choices = question.options.map(opt => ({
430
+ name: opt.description ? `${opt.label} - ${opt.description}` : opt.label,
431
+ value: opt.value,
432
+ }));
433
+ }
434
+ if (question.validation?.required) {
435
+ inquirerQuestion.validate = (input) => {
436
+ if (!input)
437
+ return "This field is required";
438
+ return true;
439
+ };
440
+ }
441
+ const { answer } = await inquirer_1.default.prompt([inquirerQuestion]);
442
+ return answer;
443
+ }
444
+ /**
445
+ * Process answer and update ASL
446
+ */
447
+ async processAnswer(question, answer) {
448
+ const [section, ...rest] = question.id.split(".");
449
+ switch (section) {
450
+ case "project":
451
+ this.updateProject(rest.join("."), answer);
452
+ break;
453
+ case "entities":
454
+ await this.updateEntities(rest, answer);
455
+ break;
456
+ case "auth":
457
+ this.updateAuth(rest.join("."), answer);
458
+ break;
459
+ case "permissions":
460
+ this.updatePermissions(rest.join("."), answer);
461
+ break;
462
+ case "pages":
463
+ await this.updatePages(rest, answer);
464
+ break;
465
+ case "design":
466
+ this.updateDesign(rest.join("."), answer);
467
+ break;
468
+ }
469
+ }
470
+ /**
471
+ * Update project section
472
+ */
473
+ updateProject(field, value) {
474
+ if (!this.asl.project) {
475
+ this.asl.project = {
476
+ name: "",
477
+ description: "",
478
+ framework: "nextjs",
479
+ backend: "instantdb",
480
+ };
481
+ }
482
+ this.asl.project[field] = value;
483
+ }
484
+ /**
485
+ * Update entities section (uses AI to parse)
486
+ */
487
+ async updateEntities(path, value) {
488
+ if (!this.asl.entities)
489
+ this.asl.entities = {};
490
+ if (path[0] === "list") {
491
+ // Parse entity list
492
+ const entityNames = value.split(",").map((e) => e.trim());
493
+ entityNames.forEach((name) => {
494
+ if (!this.asl.entities[name]) {
495
+ this.asl.entities[name] = {
496
+ name,
497
+ fields: [],
498
+ timestamps: true,
499
+ };
500
+ }
501
+ });
502
+ }
503
+ else if (path[1] === "fields") {
504
+ // Parse fields for specific entity
505
+ const entityName = path[0];
506
+ if (!entityName)
507
+ return;
508
+ const fields = await this.parseFieldsWithAI(entityName, value);
509
+ if (this.asl.entities && this.asl.entities[entityName]) {
510
+ this.asl.entities[entityName].fields = fields;
511
+ }
512
+ }
513
+ }
514
+ /**
515
+ * Use AI to parse field definitions from natural language
516
+ */
517
+ async parseFieldsWithAI(entityName, userInput) {
518
+ const spinner = (0, ora_1.default)(`Parsing fields for ${entityName}...`).start();
519
+ try {
520
+ const prompt = `Parse the following field definitions for entity "${entityName}" into structured JSON.
521
+
522
+ User input: ${userInput}
523
+
524
+ Return a JSON array of fields with this structure:
525
+ {
526
+ "fields": [
527
+ {
528
+ "name": "fieldName",
529
+ "type": "string" | "number" | "boolean" | "date" | "json" | "ref",
530
+ "required": true | false,
531
+ "description": "field description"
532
+ }
533
+ ]
534
+ }
535
+
536
+ Examples:
537
+ - "title (string, required), content (string), published (boolean)"
538
+ - "email (unique string), age (number), bio (optional string)"
539
+
540
+ Return only valid JSON, no markdown.`;
541
+ const response = await this.ai.generateText(prompt, {
542
+ temperature: 0.1,
543
+ maxTokens: 1000,
544
+ });
545
+ const parsed = JSON.parse(response);
546
+ spinner.succeed(`Parsed ${parsed.fields.length} fields for ${entityName}`);
547
+ return parsed.fields;
548
+ }
549
+ catch (error) {
550
+ spinner.fail("Failed to parse fields");
551
+ console.error(error);
552
+ return [];
553
+ }
554
+ }
555
+ /**
556
+ * Update auth section
557
+ */
558
+ updateAuth(field, value) {
559
+ if (!this.asl.auth) {
560
+ this.asl.auth = {
561
+ provider: "email",
562
+ roles: [],
563
+ };
564
+ }
565
+ if (field === "provider") {
566
+ this.asl.auth.provider = value;
567
+ }
568
+ else if (field === "roles") {
569
+ // Convert array of role names to RoleSpec[]
570
+ if (Array.isArray(value)) {
571
+ this.asl.auth.roles = value.map(name => ({
572
+ name,
573
+ description: `${name} role`,
574
+ }));
575
+ }
576
+ }
577
+ else if (field === "needed") {
578
+ if (!value) {
579
+ delete this.asl.auth;
580
+ }
581
+ }
582
+ }
583
+ /**
584
+ * Update permissions section
585
+ */
586
+ updatePermissions(field, value) {
587
+ if (!this.asl.permissions)
588
+ this.asl.permissions = [];
589
+ if (field === "needed" && value) {
590
+ // Generate default permissions based on roles and entities
591
+ if (this.asl.auth?.roles && this.asl.entities) {
592
+ this.asl.auth.roles.forEach(role => {
593
+ Object.keys(this.asl.entities).forEach(entity => {
594
+ if (role.name === "admin") {
595
+ // Admins get full access
596
+ this.asl.permissions.push({
597
+ role: role.name,
598
+ resource: entity,
599
+ actions: ["create", "read", "update", "delete"],
600
+ });
601
+ }
602
+ else {
603
+ // Regular users get limited access
604
+ this.asl.permissions.push({
605
+ role: role.name,
606
+ resource: entity,
607
+ actions: ["read"],
608
+ condition: { type: "own", field: "user_id" },
609
+ });
610
+ }
611
+ });
612
+ });
613
+ }
614
+ }
615
+ }
616
+ /**
617
+ * Update pages section (uses AI to parse)
618
+ */
619
+ async updatePages(path, value) {
620
+ if (!this.asl.pages)
621
+ this.asl.pages = [];
622
+ if (path[0] === "list") {
623
+ // Parse page list
624
+ const pages = await this.parsePagesWithAI(value);
625
+ this.asl.pages = pages;
626
+ }
627
+ else {
628
+ // Update specific page field
629
+ const match = path[0]?.match(/\d+/);
630
+ const pageIndex = parseInt(match?.[0] || "0");
631
+ const field = path[1];
632
+ if (!this.asl.pages || !this.asl.pages[pageIndex] || !field)
633
+ return;
634
+ this.asl.pages[pageIndex][field] = value;
635
+ }
636
+ }
637
+ /**
638
+ * Use AI to parse page definitions
639
+ */
640
+ async parsePagesWithAI(userInput) {
641
+ const spinner = (0, ora_1.default)("Parsing pages...").start();
642
+ try {
643
+ const prompt = `Parse the following page definitions into structured JSON.
644
+
645
+ User input: ${userInput}
646
+
647
+ Return a JSON array of pages with this structure:
648
+ {
649
+ "pages": [
650
+ {
651
+ "path": "/path",
652
+ "name": "PageName",
653
+ "type": "page",
654
+ "title": "Page Title",
655
+ "public": true | false
656
+ }
657
+ ]
658
+ }
659
+
660
+ Examples:
661
+ - "Home (/), Posts (/posts), Profile (/profile)"
662
+ - "Landing page (public), Dashboard (/dashboard, protected), Settings"
663
+
664
+ Return only valid JSON, no markdown.`;
665
+ const response = await this.ai.generateText(prompt, {
666
+ temperature: 0.1,
667
+ maxTokens: 1000,
668
+ });
669
+ const parsed = JSON.parse(response);
670
+ spinner.succeed(`Parsed ${parsed.pages.length} pages`);
671
+ return parsed.pages;
672
+ }
673
+ catch (error) {
674
+ spinner.fail("Failed to parse pages");
675
+ console.error(error);
676
+ return [];
677
+ }
678
+ }
679
+ /**
680
+ * Update design section
681
+ */
682
+ updateDesign(field, value) {
683
+ if (!this.asl.design) {
684
+ this.asl.design = {
685
+ theme: "light",
686
+ };
687
+ }
688
+ this.asl.design[field] = value;
689
+ }
690
+ /**
691
+ * Show diff preview
692
+ */
693
+ async showDiffPreview() {
694
+ console.log(chalk_1.default.bold.cyan("\nšŸ“‹ Generation Preview\n"));
695
+ const diff = this.planner.generateDiff(this.asl);
696
+ // Summary
697
+ console.log(chalk_1.default.bold("Summary:"));
698
+ console.log(chalk_1.default.green(` āœ“ ${diff.summary.totalFiles} files will be generated`));
699
+ console.log(chalk_1.default.cyan(` āœ“ ~${Math.round(diff.summary.linesAdded)} lines of code`));
700
+ console.log();
701
+ // File breakdown
702
+ console.log(chalk_1.default.bold("Files:"));
703
+ const filesByType = this.groupFilesByType(diff.files);
704
+ Object.entries(filesByType).forEach(([type, files]) => {
705
+ console.log(chalk_1.default.cyan(` ${this.fileTypeLabel(type)}: ${files.length}`));
706
+ });
707
+ console.log();
708
+ // Registries
709
+ if (diff.registries.length > 0) {
710
+ console.log(chalk_1.default.bold("Registries:"));
711
+ diff.registries.forEach(reg => {
712
+ console.log(chalk_1.default.cyan(` ${reg.type}: ${reg.added.length} items`));
713
+ });
714
+ console.log();
715
+ }
716
+ // Warnings
717
+ if (diff.warnings && diff.warnings.length > 0) {
718
+ console.log(chalk_1.default.bold.yellow("⚠ Warnings:"));
719
+ diff.warnings.forEach(warning => {
720
+ console.log(chalk_1.default.yellow(` - ${warning}`));
721
+ });
722
+ console.log();
723
+ }
724
+ // Show sample files
725
+ console.log(chalk_1.default.bold("Sample files:\n"));
726
+ diff.files.slice(0, 5).forEach(file => {
727
+ console.log(chalk_1.default.dim(` ${file.path}`));
728
+ if (file.preview) {
729
+ console.log(chalk_1.default.dim(` ${file.preview.split("\n").slice(0, 2).join("\n ")}`));
730
+ }
731
+ });
732
+ if (diff.files.length > 5) {
733
+ console.log(chalk_1.default.dim(` ... and ${diff.files.length - 5} more files`));
734
+ }
735
+ console.log();
736
+ }
737
+ /**
738
+ * Get user approval
739
+ */
740
+ async getApproval() {
741
+ const { approve } = await inquirer_1.default.prompt([
742
+ {
743
+ type: "confirm",
744
+ name: "approve",
745
+ message: "Generate these files?",
746
+ default: true,
747
+ },
748
+ ]);
749
+ return approve;
750
+ }
751
+ /**
752
+ * Save ASL to .mycontext/asl.json
753
+ */
754
+ async saveASL() {
755
+ const spinner = (0, ora_1.default)("Saving manifest...").start();
756
+ try {
757
+ // Ensure .mycontext directory exists
758
+ const mycontextDir = path_1.default.join(process.cwd(), ".mycontext");
759
+ await promises_1.default.mkdir(mycontextDir, { recursive: true });
760
+ // Save ASL
761
+ const aslPath = path_1.default.join(mycontextDir, "asl.json");
762
+ await promises_1.default.writeFile(aslPath, JSON.stringify(this.asl, null, 2), "utf-8");
763
+ spinner.succeed(`Manifest saved to ${chalk_1.default.cyan(".mycontext/asl.json")}`);
764
+ }
765
+ catch (error) {
766
+ spinner.fail("Failed to save manifest");
767
+ throw error;
768
+ }
769
+ }
770
+ // ============================================================================
771
+ // HELPER METHODS
772
+ // ============================================================================
773
+ categorizeQuestions(questions) {
774
+ const categorized = {};
775
+ questions.forEach(q => {
776
+ if (!categorized[q.category]) {
777
+ categorized[q.category] = [];
778
+ }
779
+ categorized[q.category].push(q);
780
+ });
781
+ return categorized;
782
+ }
783
+ categoryLabel(category) {
784
+ const labels = {
785
+ project: "Project Information",
786
+ entities: "Data Models (Entities)",
787
+ auth: "Authentication",
788
+ permissions: "Permissions (RBAC)",
789
+ pages: "Pages & Routes",
790
+ design: "Design & Styling",
791
+ };
792
+ return labels[category] || category;
793
+ }
794
+ mapQuestionType(type) {
795
+ const mapping = {
796
+ text: "input",
797
+ number: "number",
798
+ boolean: "confirm",
799
+ select: "list",
800
+ "multi-select": "checkbox",
801
+ "entity-builder": "input",
802
+ "field-builder": "input",
803
+ };
804
+ return mapping[type] || "input";
805
+ }
806
+ groupFilesByType(files) {
807
+ const grouped = {};
808
+ files.forEach(file => {
809
+ if (!grouped[file.type]) {
810
+ grouped[file.type] = [];
811
+ }
812
+ grouped[file.type].push(file);
813
+ });
814
+ return grouped;
815
+ }
816
+ fileTypeLabel(type) {
817
+ const labels = {
818
+ schema: "Schema",
819
+ type: "Types",
820
+ page: "Pages",
821
+ component: "Components",
822
+ action: "Server Actions",
823
+ config: "Configuration",
824
+ };
825
+ return labels[type] || type;
826
+ }
827
+ // ============================================================================
828
+ // SCAFFOLD FLOW METHODS
829
+ // ============================================================================
830
+ /**
831
+ * Ask if user wants to scaffold now
832
+ */
833
+ async askScaffold() {
834
+ const { shouldScaffold } = await inquirer_1.default.prompt([
835
+ {
836
+ type: "confirm",
837
+ name: "shouldScaffold",
838
+ message: "Ready to scaffold your project?",
839
+ default: true,
840
+ },
841
+ ]);
842
+ return shouldScaffold;
843
+ }
844
+ /**
845
+ * Show full scaffold preview (per user preference for "full manifest")
846
+ */
847
+ async showScaffoldPreview() {
848
+ const spinner = (0, ora_1.default)("Generating scaffold preview...").start();
849
+ try {
850
+ const preview = new ScaffoldPreview_1.ScaffoldPreview(this.asl);
851
+ const manifest = preview.generateManifest();
852
+ spinner.stop();
853
+ console.log(chalk_1.default.bold.cyan("\nšŸ“¦ Scaffold Preview\n"));
854
+ // File tree
855
+ console.log(chalk_1.default.bold("Project Structure:\n"));
856
+ console.log(manifest.fileTree);
857
+ console.log();
858
+ // Components summary
859
+ console.log(chalk_1.default.bold("Components:"));
860
+ console.log(chalk_1.default.cyan(` shadCN: ${manifest.componentsSummary.shadcn.length} components`));
861
+ console.log(chalk_1.default.gray(` ${manifest.componentsSummary.shadcn.join(", ")}`));
862
+ console.log(chalk_1.default.cyan(` Custom: ${manifest.componentsSummary.custom} components`));
863
+ console.log();
864
+ // Features
865
+ console.log(chalk_1.default.bold("Features:"));
866
+ manifest.features.forEach(feature => {
867
+ console.log(chalk_1.default.green(` āœ“ ${feature}`));
868
+ });
869
+ console.log();
870
+ // Stats
871
+ console.log(chalk_1.default.bold("Stats:"));
872
+ console.log(chalk_1.default.cyan(` Total files: ${manifest.totalFiles}`));
873
+ console.log(chalk_1.default.cyan(` Estimated time: ${manifest.estimatedTime}`));
874
+ console.log();
875
+ }
876
+ catch (error) {
877
+ spinner.fail("Failed to generate preview");
878
+ console.error(chalk_1.default.red(error));
879
+ throw error;
880
+ }
881
+ }
882
+ /**
883
+ * Get confirmation before scaffolding
884
+ */
885
+ async confirmScaffold() {
886
+ const { confirmed } = await inquirer_1.default.prompt([
887
+ {
888
+ type: "confirm",
889
+ name: "confirmed",
890
+ message: "Proceed with scaffolding?",
891
+ default: true,
892
+ },
893
+ ]);
894
+ return confirmed;
895
+ }
896
+ /**
897
+ * Run scaffold engine
898
+ */
899
+ async runScaffold() {
900
+ console.log(chalk_1.default.cyan("\nšŸš€ Starting deterministic compilation...\n"));
901
+ try {
902
+ const scaffoldEngine = new ScaffoldEngine_1.ScaffoldEngine();
903
+ await scaffoldEngine.scaffold(this.asl);
904
+ console.log(chalk_1.default.green("\nāœ“ Project scaffolded successfully!\n"));
905
+ console.log(chalk_1.default.bold("Next steps:\n"));
906
+ console.log(chalk_1.default.cyan(` 1. cd ${this.asl.project?.name || "your-project"}`));
907
+ console.log(chalk_1.default.cyan(" 2. pnpm install"));
908
+ console.log(chalk_1.default.cyan(" 3. pnpm dev\n"));
909
+ }
910
+ catch (error) {
911
+ console.error(chalk_1.default.red("\nāœ— Scaffold failed:"), error);
912
+ throw error;
913
+ }
914
+ }
915
+ }
916
+ exports.InitInteractiveCommand = InitInteractiveCommand;
917
+ //# sourceMappingURL=init-interactive.js.map