@visualprd/mcp-server 1.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.
Files changed (58) hide show
  1. package/README.md +396 -0
  2. package/dist/cli.d.ts +9 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +27 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/index.d.ts +20 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +243 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/intelligence/context-optimizer.d.ts +93 -0
  11. package/dist/intelligence/context-optimizer.d.ts.map +1 -0
  12. package/dist/intelligence/context-optimizer.js +481 -0
  13. package/dist/intelligence/context-optimizer.js.map +1 -0
  14. package/dist/intelligence/error-analyzer.d.ts +49 -0
  15. package/dist/intelligence/error-analyzer.d.ts.map +1 -0
  16. package/dist/intelligence/error-analyzer.js +765 -0
  17. package/dist/intelligence/error-analyzer.js.map +1 -0
  18. package/dist/intelligence/gap-filler.d.ts +56 -0
  19. package/dist/intelligence/gap-filler.d.ts.map +1 -0
  20. package/dist/intelligence/gap-filler.js +410 -0
  21. package/dist/intelligence/gap-filler.js.map +1 -0
  22. package/dist/intelligence/guidance-generator.d.ts +43 -0
  23. package/dist/intelligence/guidance-generator.d.ts.map +1 -0
  24. package/dist/intelligence/guidance-generator.js +314 -0
  25. package/dist/intelligence/guidance-generator.js.map +1 -0
  26. package/dist/intelligence/index.d.ts +132 -0
  27. package/dist/intelligence/index.d.ts.map +1 -0
  28. package/dist/intelligence/index.js +683 -0
  29. package/dist/intelligence/index.js.map +1 -0
  30. package/dist/server-http.d.ts +9 -0
  31. package/dist/server-http.d.ts.map +1 -0
  32. package/dist/server-http.js +141 -0
  33. package/dist/server-http.js.map +1 -0
  34. package/dist/services/api-key-service.d.ts +68 -0
  35. package/dist/services/api-key-service.d.ts.map +1 -0
  36. package/dist/services/api-key-service.js +298 -0
  37. package/dist/services/api-key-service.js.map +1 -0
  38. package/dist/services/llm-client.d.ts +66 -0
  39. package/dist/services/llm-client.d.ts.map +1 -0
  40. package/dist/services/llm-client.js +141 -0
  41. package/dist/services/llm-client.js.map +1 -0
  42. package/dist/services/model-registry.d.ts +135 -0
  43. package/dist/services/model-registry.d.ts.map +1 -0
  44. package/dist/services/model-registry.js +276 -0
  45. package/dist/services/model-registry.js.map +1 -0
  46. package/dist/services/visualprd-client.d.ts +191 -0
  47. package/dist/services/visualprd-client.d.ts.map +1 -0
  48. package/dist/services/visualprd-client.js +805 -0
  49. package/dist/services/visualprd-client.js.map +1 -0
  50. package/dist/tools/index.d.ts +803 -0
  51. package/dist/tools/index.d.ts.map +1 -0
  52. package/dist/tools/index.js +570 -0
  53. package/dist/tools/index.js.map +1 -0
  54. package/dist/types/index.d.ts +497 -0
  55. package/dist/types/index.d.ts.map +1 -0
  56. package/dist/types/index.js +8 -0
  57. package/dist/types/index.js.map +1 -0
  58. package/package.json +48 -0
@@ -0,0 +1,683 @@
1
+ "use strict";
2
+ /**
3
+ * Intelligence Layer
4
+ *
5
+ * The brain of the MCP server - orchestrates context optimization,
6
+ * error analysis, gap filling, and guidance generation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.GuidanceGenerator = exports.GapFiller = exports.ErrorAnalyzer = exports.ContextOptimizer = exports.IntelligenceLayer = void 0;
10
+ const context_optimizer_js_1 = require("./context-optimizer.js");
11
+ const error_analyzer_js_1 = require("./error-analyzer.js");
12
+ const gap_filler_js_1 = require("./gap-filler.js");
13
+ const guidance_generator_js_1 = require("./guidance-generator.js");
14
+ const llm_client_js_1 = require("../services/llm-client.js");
15
+ class IntelligenceLayer {
16
+ contextOptimizer;
17
+ errorAnalyzer;
18
+ gapFiller;
19
+ guidanceGenerator;
20
+ client;
21
+ llmClient = null;
22
+ config;
23
+ // Intelligence cache
24
+ cache = new Map();
25
+ CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
26
+ constructor(client, config) {
27
+ this.client = client;
28
+ this.config = config || { projectId: '', apiKey: '', apiUrl: '' };
29
+ this.contextOptimizer = new context_optimizer_js_1.ContextOptimizer();
30
+ this.errorAnalyzer = new error_analyzer_js_1.ErrorAnalyzer();
31
+ this.gapFiller = new gap_filler_js_1.GapFiller();
32
+ this.guidanceGenerator = new guidance_generator_js_1.GuidanceGenerator();
33
+ // Initialize LLM client if API key is available
34
+ const openRouterKey = process.env.OPENROUTER_API_KEY;
35
+ if (openRouterKey) {
36
+ this.llmClient = new llm_client_js_1.LLMClient({
37
+ apiKey: openRouterKey,
38
+ model: config?.thinkingModel || 'anthropic/claude-3.5-sonnet',
39
+ });
40
+ console.error(`[Intelligence] LLM initialized with model: ${this.llmClient.getModel()}`);
41
+ }
42
+ else {
43
+ console.error('[Intelligence] No OPENROUTER_API_KEY - running in rule-based mode only');
44
+ }
45
+ }
46
+ /**
47
+ * Update the thinking model dynamically
48
+ */
49
+ setThinkingModel(model) {
50
+ if (this.llmClient) {
51
+ this.llmClient.setModel(model);
52
+ this.config.thinkingModel = model;
53
+ }
54
+ else {
55
+ console.error('[Intelligence] Cannot set model - LLM client not initialized');
56
+ }
57
+ }
58
+ /**
59
+ * Set intelligence mode (fast = rule-based, deep = AI)
60
+ */
61
+ setIntelligenceMode(mode) {
62
+ this.config.intelligenceMode = mode;
63
+ console.error(`[Intelligence] Mode set to: ${mode}`);
64
+ }
65
+ /**
66
+ * Get the underlying VisualPRD client for direct V2 method access
67
+ */
68
+ getClient() {
69
+ return this.client;
70
+ }
71
+ /**
72
+ * Check if AI mode is available and enabled
73
+ */
74
+ isAIModeEnabled() {
75
+ return this.llmClient !== null && this.config.intelligenceMode === 'deep';
76
+ }
77
+ // ============================================================================
78
+ // MAIN INTELLIGENCE METHODS
79
+ // ============================================================================
80
+ /**
81
+ * Get the next build step with full intelligent context
82
+ */
83
+ async getNextBuildStep() {
84
+ const startTime = Date.now();
85
+ // 1. Load project data
86
+ const projectData = await this.client.loadProjectData();
87
+ // 2. Get next available prompt
88
+ const nextPrompt = await this.client.getNextBuildPrompt();
89
+ if (!nextPrompt) {
90
+ // All steps complete!
91
+ return {
92
+ step: null,
93
+ relatedEntities: {
94
+ pages: [],
95
+ schemas: [],
96
+ endpoints: [],
97
+ techStack: [],
98
+ },
99
+ contextualGuidance: {
100
+ technicalConsiderations: [],
101
+ potentialIssues: [],
102
+ testingCriteria: [],
103
+ },
104
+ buildProgress: await this.client.getBuildProgress(),
105
+ message: '🎉 All build steps complete! Your project is ready.',
106
+ };
107
+ }
108
+ // 3. Decide on intelligence tier for this request
109
+ const decision = this.selectIntelligenceTier('get_next_step', nextPrompt);
110
+ // 4. Optimize context for this prompt
111
+ const optimizedContext = await this.contextOptimizer.optimizeContext(nextPrompt, projectData);
112
+ // 5. Analyze for gaps (lightweight)
113
+ const gapReport = await this.gapFiller.analyzeGaps(nextPrompt, projectData);
114
+ // 6. Generate guidance (rule-based or AI-powered)
115
+ let guidance;
116
+ if (this.isAIModeEnabled()) {
117
+ guidance = await this.generateAIGuidance(nextPrompt, projectData, optimizedContext, gapReport);
118
+ }
119
+ else {
120
+ guidance = this.guidanceGenerator.generateGuidance({
121
+ prompt: nextPrompt,
122
+ projectData,
123
+ relatedPages: optimizedContext.pages,
124
+ relatedSchemas: optimizedContext.schemas,
125
+ relatedEndpoints: optimizedContext.endpoints,
126
+ relatedTechStack: optimizedContext.techStack,
127
+ });
128
+ }
129
+ // 7. Add gap warnings to guidance
130
+ if (gapReport.gaps.length > 0) {
131
+ const highPriorityGaps = gapReport.gaps.filter(g => g.severity === 'high');
132
+ if (highPriorityGaps.length > 0) {
133
+ guidance.potentialIssues.unshift(...highPriorityGaps.map(g => `⚠️ ${g.description}`));
134
+ }
135
+ }
136
+ // 8. Get build progress
137
+ const buildProgress = await this.client.getBuildProgress();
138
+ const processingTime = Date.now() - startTime;
139
+ return {
140
+ step: nextPrompt,
141
+ relatedEntities: {
142
+ pages: optimizedContext.pages,
143
+ schemas: optimizedContext.schemas,
144
+ endpoints: optimizedContext.endpoints,
145
+ techStack: optimizedContext.techStack,
146
+ designSystem: optimizedContext.designSystem,
147
+ },
148
+ contextualGuidance: guidance,
149
+ buildProgress,
150
+ message: `Ready to work on step ${buildProgress.currentStep} of ${buildProgress.totalSteps}: "${nextPrompt.title}"`,
151
+ };
152
+ }
153
+ /**
154
+ * Validate and mark a step as complete
155
+ */
156
+ async markStepComplete(promptId, completionData) {
157
+ const validationIssues = [];
158
+ // 1. Get project data and prompt
159
+ const projectData = await this.client.loadProjectData();
160
+ const prompt = projectData.buildPrompts.find(p => p.promptId === promptId);
161
+ if (!prompt) {
162
+ return {
163
+ success: false,
164
+ validationIssues: [{ type: 'error', message: 'Prompt not found' }],
165
+ message: `Could not find prompt with ID: ${promptId}`,
166
+ };
167
+ }
168
+ // 2. Validate completion (lightweight checks)
169
+ if (!completionData.filesCreated?.length) {
170
+ validationIssues.push({
171
+ type: 'warning',
172
+ message: 'No files were created - are you sure this step is complete?'
173
+ });
174
+ }
175
+ if (completionData.testResults?.failed && completionData.testResults.failed > 0) {
176
+ validationIssues.push({
177
+ type: 'warning',
178
+ message: `${completionData.testResults.failed} tests are failing - consider fixing before marking complete`
179
+ });
180
+ }
181
+ // 3. Check test criteria (if any)
182
+ if (prompt.testCriteria?.length && !completionData.completionNotes) {
183
+ validationIssues.push({
184
+ type: 'warning',
185
+ message: 'This step has test criteria - please provide completion notes about how they were met'
186
+ });
187
+ }
188
+ // 4. Mark complete (even with warnings - user can override)
189
+ const success = await this.client.markPromptComplete(promptId, completionData);
190
+ if (!success) {
191
+ return {
192
+ success: false,
193
+ validationIssues: [{ type: 'error', message: 'Failed to update database' }],
194
+ message: 'Could not mark step as complete. Please try again.',
195
+ };
196
+ }
197
+ // 5. Get next step automatically
198
+ const nextStepResponse = await this.getNextBuildStep();
199
+ return {
200
+ success: true,
201
+ validationIssues,
202
+ nextStep: nextStepResponse,
203
+ message: validationIssues.length > 0
204
+ ? `✅ Step "${prompt.title}" marked complete with ${validationIssues.length} warning(s).`
205
+ : `✅ Step "${prompt.title}" marked complete!`,
206
+ };
207
+ }
208
+ /**
209
+ * Analyze an error and provide fixes
210
+ */
211
+ async analyzeError(error) {
212
+ // 1. Get project data and prompt
213
+ const projectData = await this.client.loadProjectData();
214
+ const prompt = projectData.buildPrompts.find(p => p.promptId === error.promptId);
215
+ if (!prompt) {
216
+ return {
217
+ diagnosis: {
218
+ rootCause: 'Unknown prompt',
219
+ explanation: `Could not find prompt ${error.promptId} for context`,
220
+ errorCategory: error.errorType,
221
+ severity: 'medium',
222
+ },
223
+ suggestedFixes: [{
224
+ approach: 'Provide Context',
225
+ steps: ['Ensure the promptId is correct', 'Try again with valid prompt ID'],
226
+ confidence: 0.5,
227
+ }],
228
+ relatedDocs: {},
229
+ shouldUncheckStep: false,
230
+ shouldInjectNewStep: false,
231
+ message: 'Could not find prompt for error context.',
232
+ };
233
+ }
234
+ // 2. Analyze the error
235
+ const analysis = await this.errorAnalyzer.analyzeError(error, {
236
+ prompt,
237
+ projectData,
238
+ });
239
+ // 3. Should we inject a new step?
240
+ if (analysis.shouldInjectNewStep && analysis.newStep) {
241
+ const newStep = await this.client.injectBuildPrompt(error.promptId, {
242
+ title: analysis.newStep.title || 'Fix Required Issue',
243
+ category: analysis.newStep.category || 'bugfix',
244
+ instruction: analysis.newStep.instruction || 'Address the error before continuing',
245
+ reason: `Automatically injected due to error: ${error.errorMessage.substring(0, 100)}`,
246
+ });
247
+ if (newStep) {
248
+ analysis.newStep = newStep;
249
+ }
250
+ }
251
+ // 4. Should we uncheck current step?
252
+ if (analysis.shouldUncheckStep) {
253
+ await this.client.uncheckPrompt(error.promptId);
254
+ }
255
+ // 5. Log the error
256
+ await this.client.logError('current-session', // TODO: Get actual session ID
257
+ error.promptId, error, analysis.diagnosis);
258
+ return {
259
+ ...analysis,
260
+ message: this.formatErrorMessage(analysis),
261
+ };
262
+ }
263
+ /**
264
+ * Get detailed information about an entity
265
+ */
266
+ async getEntityDetails(entityType, entityId, options = {}) {
267
+ const projectData = await this.client.loadProjectData();
268
+ let entity;
269
+ // Find the entity
270
+ switch (entityType) {
271
+ case 'page':
272
+ entity = await this.client.getPage(entityId);
273
+ break;
274
+ case 'schema':
275
+ entity = await this.client.getSchema(entityId);
276
+ break;
277
+ case 'endpoint':
278
+ entity = await this.client.getEndpoint(entityId);
279
+ break;
280
+ case 'tech':
281
+ entity = await this.client.getTechStackItem(entityId);
282
+ break;
283
+ }
284
+ if (!entity) {
285
+ throw new Error(`${entityType} with ID "${entityId}" not found`);
286
+ }
287
+ const result = { entity, entityType };
288
+ // Get relationships if requested
289
+ if (options.includeRelationships && entityType === 'schema') {
290
+ const schema = entity;
291
+ result.relationships = schema.relationships || [];
292
+ // Find reverse relationships
293
+ const reverseRels = projectData.schemas
294
+ .filter(s => s.id !== entity.id)
295
+ .flatMap(s => (s.relationships || [])
296
+ .filter(r => this.normalize(r.target) === this.normalize(schema.collectionName))
297
+ .map(r => ({
298
+ ...r,
299
+ source: s.collectionName,
300
+ })));
301
+ result.relationships.push(...reverseRels);
302
+ }
303
+ // Get usage information
304
+ if (options.includeRelationships) {
305
+ const usedBy = {};
306
+ // Find prompts that reference this entity
307
+ const relatedPrompts = projectData.buildPrompts.filter(p => {
308
+ const refs = p.relatedDocuments || {};
309
+ switch (entityType) {
310
+ case 'page':
311
+ return refs.pages?.some(ref => this.normalize(ref) === this.normalize(entityId));
312
+ case 'schema':
313
+ return refs.schemas?.some(ref => this.normalize(ref) === this.normalize(entityId));
314
+ case 'endpoint':
315
+ return refs.endpoints?.some(ref => this.normalize(ref) === this.normalize(entityId));
316
+ case 'tech':
317
+ return refs.techStack?.some(ref => this.normalize(ref) === this.normalize(entityId));
318
+ default:
319
+ return false;
320
+ }
321
+ });
322
+ if (relatedPrompts.length > 0) {
323
+ usedBy.prompts = relatedPrompts.map(p => p.title);
324
+ }
325
+ result.usedBy = usedBy;
326
+ }
327
+ // Generate usage examples if requested
328
+ if (options.includeUsageExamples) {
329
+ result.usageExamples = this.generateUsageExamples(entityType, entity);
330
+ }
331
+ return result;
332
+ }
333
+ /**
334
+ * Get debugging suggestions when stuck
335
+ */
336
+ async getDebugSuggestions(currentTask, issue, context) {
337
+ const projectData = await this.client.loadProjectData();
338
+ const suggestions = [];
339
+ const fullContext = `${currentTask} ${issue} ${context || ''}`.toLowerCase();
340
+ // Pattern-based suggestions
341
+ if (fullContext.includes('not loading') || fullContext.includes("doesn't load")) {
342
+ suggestions.push({
343
+ approach: 'Check Data Fetching',
344
+ reasoning: 'Loading issues are often related to async data fetching',
345
+ steps: [
346
+ 'Add console.log before and after fetch calls',
347
+ 'Check browser Network tab for failed requests',
348
+ 'Verify the endpoint URL is correct',
349
+ 'Check for CORS issues in console',
350
+ ],
351
+ confidence: 0.85,
352
+ });
353
+ }
354
+ if (fullContext.includes('style') || fullContext.includes('css') || fullContext.includes('layout')) {
355
+ const designSystem = await this.client.getDesignSystem();
356
+ suggestions.push({
357
+ approach: 'Review Design System',
358
+ reasoning: 'Styling issues may be due to design system misalignment',
359
+ steps: [
360
+ 'Check design-system.md for correct color/spacing values',
361
+ 'Use browser DevTools to inspect computed styles',
362
+ 'Verify Tailwind classes are correct',
363
+ 'Check for CSS specificity conflicts',
364
+ ],
365
+ confidence: 0.8,
366
+ });
367
+ }
368
+ if (fullContext.includes('auth') || fullContext.includes('login') || fullContext.includes('permission')) {
369
+ suggestions.push({
370
+ approach: 'Debug Authentication',
371
+ reasoning: 'Auth issues often stem from token or permission problems',
372
+ steps: [
373
+ 'Check if auth token exists: console.log(auth.currentUser)',
374
+ 'Verify token hasn\'t expired',
375
+ 'Review Firestore security rules',
376
+ 'Check if user has required role/permissions',
377
+ ],
378
+ confidence: 0.9,
379
+ });
380
+ }
381
+ if (fullContext.includes('upload') || fullContext.includes('file') || fullContext.includes('image')) {
382
+ const firebaseTech = projectData.techStack.find(t => t.name.toLowerCase().includes('firebase'));
383
+ suggestions.push({
384
+ approach: 'Debug File Upload',
385
+ reasoning: 'File uploads require Storage configuration',
386
+ steps: [
387
+ 'Verify Firebase Storage is initialized',
388
+ 'Check Storage security rules allow uploads',
389
+ 'Ensure file size is within limits',
390
+ 'Verify correct MIME type handling',
391
+ ],
392
+ confidence: 0.85,
393
+ });
394
+ }
395
+ // Fallback suggestion
396
+ if (suggestions.length === 0) {
397
+ suggestions.push({
398
+ approach: 'Systematic Debugging',
399
+ reasoning: 'General debugging approach for unknown issues',
400
+ steps: [
401
+ 'Add console.log statements to trace execution flow',
402
+ 'Check browser console for errors',
403
+ 'Review recent code changes',
404
+ 'Try to reproduce with minimal example',
405
+ 'Search error message online for solutions',
406
+ ],
407
+ confidence: 0.6,
408
+ });
409
+ }
410
+ // Add relevant context from project
411
+ const additionalContext = {};
412
+ // Find relevant schemas based on keywords
413
+ const relevantSchemas = projectData.schemas.filter(s => fullContext.includes(this.normalize(s.collectionName)));
414
+ if (relevantSchemas.length > 0) {
415
+ additionalContext.schemas = relevantSchemas.map(s => ({
416
+ collectionName: s.collectionName,
417
+ fields: s.fields?.map(f => f.fieldName),
418
+ }));
419
+ }
420
+ // Find relevant endpoints
421
+ const relevantEndpoints = projectData.endpoints.filter(e => fullContext.includes(this.normalize(e.name)) ||
422
+ fullContext.includes(this.normalize(e.path || '')));
423
+ if (relevantEndpoints.length > 0) {
424
+ additionalContext.endpoints = relevantEndpoints.map(e => ({
425
+ name: e.name,
426
+ method: e.method,
427
+ path: e.path,
428
+ }));
429
+ }
430
+ return { suggestions, additionalContext, missingInformation: [] };
431
+ }
432
+ /**
433
+ * Inject a new build step dynamically
434
+ */
435
+ async injectDynamicStep(insertAfterPromptId, stepData) {
436
+ const newStep = await this.client.injectBuildPrompt(insertAfterPromptId, {
437
+ title: stepData.title,
438
+ category: stepData.category,
439
+ instruction: stepData.instruction,
440
+ reason: stepData.reason,
441
+ estimatedMinutes: stepData.estimatedMinutes,
442
+ });
443
+ if (!newStep) {
444
+ return {
445
+ success: false,
446
+ message: 'Failed to inject new step. Check that the insertAfter prompt exists.',
447
+ };
448
+ }
449
+ return {
450
+ success: true,
451
+ newStep,
452
+ message: `✅ New step "${stepData.title}" injected after prompt ${insertAfterPromptId}`,
453
+ };
454
+ }
455
+ // ============================================================================
456
+ // INTELLIGENCE DECISION MAKING
457
+ // ============================================================================
458
+ /**
459
+ * Select appropriate intelligence tier based on task
460
+ */
461
+ selectIntelligenceTier(task, context) {
462
+ // Simple tasks use rule-based logic (free)
463
+ const simpleTaskPatterns = [
464
+ 'get_next_step',
465
+ 'mark_complete',
466
+ 'get_entity',
467
+ 'get_progress',
468
+ ];
469
+ if (simpleTaskPatterns.some(p => task.includes(p))) {
470
+ return {
471
+ action: 'rule_based',
472
+ reasoning: 'Simple lookup or status change - no AI needed',
473
+ cost: 'free',
474
+ };
475
+ }
476
+ // Pattern matching for known error types
477
+ if (task === 'analyze_error' && context?.errorType) {
478
+ return {
479
+ action: 'pattern_match',
480
+ reasoning: 'Error analysis using known patterns',
481
+ cost: 'free',
482
+ };
483
+ }
484
+ // Complex reasoning tasks may need AI
485
+ if (task === 'debug_suggestions' || task === 'gap_analysis') {
486
+ return {
487
+ action: 'lightweight_ai',
488
+ reasoning: 'Requires contextual understanding',
489
+ cost: 'cheap',
490
+ };
491
+ }
492
+ // Default to rule-based
493
+ return {
494
+ action: 'rule_based',
495
+ reasoning: 'Default to rules to minimize cost',
496
+ cost: 'free',
497
+ };
498
+ }
499
+ // ============================================================================
500
+ // UTILITY METHODS
501
+ // ============================================================================
502
+ normalize(str) {
503
+ return (str || '')
504
+ .toLowerCase()
505
+ .replace(/[^a-z0-9]/g, '')
506
+ .trim();
507
+ }
508
+ formatErrorMessage(analysis) {
509
+ const parts = [];
510
+ parts.push(`**Diagnosis:** ${analysis.diagnosis.rootCause}`);
511
+ parts.push(`**Severity:** ${analysis.diagnosis.severity}`);
512
+ parts.push('');
513
+ parts.push('**Suggested Fixes:**');
514
+ for (const fix of analysis.suggestedFixes.slice(0, 3)) {
515
+ parts.push(`- ${fix.approach} (${Math.round(fix.confidence * 100)}% confidence)`);
516
+ for (const step of fix.steps.slice(0, 3)) {
517
+ parts.push(` • ${step}`);
518
+ }
519
+ }
520
+ if (analysis.shouldInjectNewStep && analysis.newStep) {
521
+ parts.push('');
522
+ parts.push(`⚠️ **New step injected:** "${analysis.newStep.title}"`);
523
+ }
524
+ if (analysis.shouldUncheckStep) {
525
+ parts.push('');
526
+ parts.push('⚠️ **Current step unchecked** - needs to be completed after fix');
527
+ }
528
+ return parts.join('\n');
529
+ }
530
+ generateUsageExamples(entityType, entity) {
531
+ const examples = [];
532
+ switch (entityType) {
533
+ case 'schema':
534
+ examples.push({
535
+ context: 'Fetching documents',
536
+ code: `const snapshot = await db.collection('${entity.collectionName}').get();\nconst items = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));`,
537
+ });
538
+ examples.push({
539
+ context: 'Adding a document',
540
+ code: `await db.collection('${entity.collectionName}').add({\n // ${entity.fields?.map((f) => f.fieldName).join(', ')}\n});`,
541
+ });
542
+ break;
543
+ case 'endpoint':
544
+ examples.push({
545
+ context: 'Calling the API',
546
+ code: `const response = await fetch('${entity.path}', {\n method: '${entity.method}',\n headers: { 'Content-Type': 'application/json' },\n ${entity.method !== 'GET' ? 'body: JSON.stringify(data),' : ''}\n});\nconst result = await response.json();`,
547
+ });
548
+ break;
549
+ case 'page':
550
+ examples.push({
551
+ context: 'Routing to page',
552
+ code: entity.route
553
+ ? `import { useRouter } from 'next/navigation';\n// ...\nrouter.push('${entity.route}');`
554
+ : '// Define route in app router',
555
+ });
556
+ break;
557
+ }
558
+ return examples;
559
+ }
560
+ /**
561
+ * Generate AI-powered guidance for a build step
562
+ * Uses completely generic prompts that adapt to any project type
563
+ */
564
+ async generateAIGuidance(prompt, projectData, context, gapReport) {
565
+ if (!this.llmClient) {
566
+ // Fallback to rule-based if LLM not available
567
+ return this.guidanceGenerator.generateGuidance({
568
+ prompt,
569
+ projectData,
570
+ relatedPages: context.pages,
571
+ relatedSchemas: context.schemas,
572
+ relatedEndpoints: context.endpoints,
573
+ relatedTechStack: context.techStack,
574
+ });
575
+ }
576
+ // Generic system prompt - no project-type assumptions
577
+ const systemPrompt = `You are an expert software engineer assistant.
578
+
579
+ Your role is to provide practical, actionable guidance for development tasks.
580
+ Analyze the provided context carefully and identify:
581
+ 1. What technical considerations apply to this specific task
582
+ 2. What issues the developer might face based on the actual project structure
583
+ 3. How to verify the task is complete correctly
584
+ 4. What implicit requirements exist that aren't explicitly stated
585
+
586
+ Be specific to the actual project data provided. Do not make assumptions about the project type.
587
+ Do not provide generic advice - every suggestion must be grounded in the actual context.
588
+ Focus on what's relevant to THIS specific task in THIS specific project.`;
589
+ // Build context dynamically from actual project data
590
+ const techStackList = projectData.techStack.map(t => `${t.name}${t.version ? ` v${t.version}` : ''} (${t.category})`).join('\n- ') || 'Not specified';
591
+ const schemaContext = context.schemas.length > 0
592
+ ? context.schemas.map(s => `${s.collectionName}: fields=[${s.fields.map(f => `${f.fieldName}:${f.dataType}${f.required ? '*' : ''}`).join(', ')}]`).join('\n- ')
593
+ : 'No schemas relevant to this task';
594
+ const pageContext = context.pages.length > 0
595
+ ? context.pages.map(p => `${p.pageName}${p.route ? ` (${p.route})` : ''}: ${p.purpose || p.description || 'No description'}`).join('\n- ')
596
+ : 'No pages relevant to this task';
597
+ const endpointContext = context.endpoints.length > 0
598
+ ? context.endpoints.map(e => `${e.method} ${e.path}: ${e.description || e.name}`).join('\n- ')
599
+ : 'No endpoints relevant to this task';
600
+ const gapContext = gapReport.gaps.length > 0
601
+ ? gapReport.gaps.map(g => `[${g.severity.toUpperCase()}] ${g.description}`).join('\n- ')
602
+ : 'None identified';
603
+ const userPrompt = `# Current Task
604
+ Title: ${prompt.title}
605
+ Category: ${prompt.category}
606
+ Instruction: ${prompt.instruction || 'Complete this step as described'}
607
+ ${prompt.expectedOutcome ? `Expected Outcome: ${prompt.expectedOutcome}` : ''}
608
+ ${prompt.testCriteria?.length ? `Test Criteria:\n- ${prompt.testCriteria.join('\n- ')}` : ''}
609
+
610
+ # Project Context
611
+ Name: ${projectData.projectName}
612
+ Description: ${projectData.description || 'No description provided'}
613
+
614
+ ## Technology Stack
615
+ - ${techStackList}
616
+
617
+ ## Relevant Database Schemas
618
+ - ${schemaContext}
619
+
620
+ ## Relevant Pages/Views
621
+ - ${pageContext}
622
+
623
+ ## Relevant API Endpoints
624
+ - ${endpointContext}
625
+
626
+ ## Identified Gaps
627
+ - ${gapContext}
628
+
629
+ # Your Task
630
+ Provide guidance in JSON format:
631
+ {
632
+ "technicalConsiderations": ["specific considerations for this task based on the context"],
633
+ "potentialIssues": ["specific issues that could arise based on the actual project structure"],
634
+ "testingCriteria": ["specific ways to verify this task is complete"],
635
+ "suggestedApproach": "A step-by-step approach specific to this task",
636
+ "implicitRequirements": ["Things not stated but implied by the context"]
637
+ }`;
638
+ try {
639
+ const { result } = await this.llmClient.analyzeJSON(systemPrompt, userPrompt, { maxTokens: 2000 });
640
+ // Add implicit requirements as potential issues
641
+ const allIssues = [...(result.potentialIssues || [])];
642
+ if (result.implicitRequirements?.length) {
643
+ allIssues.push(...result.implicitRequirements.map(r => `⚠️ Implicit: ${r}`));
644
+ }
645
+ return {
646
+ technicalConsiderations: result.technicalConsiderations || [],
647
+ potentialIssues: allIssues,
648
+ testingCriteria: result.testingCriteria || [],
649
+ suggestedApproach: result.suggestedApproach,
650
+ relatedPatterns: result.relatedPatterns,
651
+ };
652
+ }
653
+ catch (error) {
654
+ console.error('[Intelligence] AI guidance failed, falling back to rules:', error);
655
+ return this.guidanceGenerator.generateGuidance({
656
+ prompt,
657
+ projectData,
658
+ relatedPages: context.pages,
659
+ relatedSchemas: context.schemas,
660
+ relatedEndpoints: context.endpoints,
661
+ relatedTechStack: context.techStack,
662
+ });
663
+ }
664
+ }
665
+ /**
666
+ * Clear intelligence cache
667
+ */
668
+ clearCache() {
669
+ this.cache.clear();
670
+ this.client.clearCache();
671
+ }
672
+ }
673
+ exports.IntelligenceLayer = IntelligenceLayer;
674
+ // Re-export individual components
675
+ var context_optimizer_js_2 = require("./context-optimizer.js");
676
+ Object.defineProperty(exports, "ContextOptimizer", { enumerable: true, get: function () { return context_optimizer_js_2.ContextOptimizer; } });
677
+ var error_analyzer_js_2 = require("./error-analyzer.js");
678
+ Object.defineProperty(exports, "ErrorAnalyzer", { enumerable: true, get: function () { return error_analyzer_js_2.ErrorAnalyzer; } });
679
+ var gap_filler_js_2 = require("./gap-filler.js");
680
+ Object.defineProperty(exports, "GapFiller", { enumerable: true, get: function () { return gap_filler_js_2.GapFiller; } });
681
+ var guidance_generator_js_2 = require("./guidance-generator.js");
682
+ Object.defineProperty(exports, "GuidanceGenerator", { enumerable: true, get: function () { return guidance_generator_js_2.GuidanceGenerator; } });
683
+ //# sourceMappingURL=index.js.map