@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,765 @@
1
+ "use strict";
2
+ /**
3
+ * Error Analyzer
4
+ *
5
+ * Intelligently analyzes errors reported during the build process
6
+ * and provides actionable fixes, migrations, or new build steps.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ErrorAnalyzer = void 0;
10
+ class ErrorAnalyzer {
11
+ patterns = [];
12
+ constructor() {
13
+ this.registerPatterns();
14
+ }
15
+ /**
16
+ * Analyze an error and provide diagnosis + fixes
17
+ */
18
+ async analyzeError(error, context) {
19
+ // Try to match against known patterns
20
+ for (const pattern of this.patterns) {
21
+ const matches = typeof pattern.pattern === 'string'
22
+ ? error.errorMessage.includes(pattern.pattern)
23
+ : pattern.pattern.test(error.errorMessage);
24
+ if (matches) {
25
+ const partialResult = await pattern.analyzer(error, context);
26
+ return this.buildFullResult(error, context, partialResult, pattern.type);
27
+ }
28
+ }
29
+ // Fallback: Generic analysis
30
+ return this.genericAnalysis(error, context);
31
+ }
32
+ /**
33
+ * Register known error patterns
34
+ */
35
+ registerPatterns() {
36
+ // Null/undefined reference errors
37
+ this.patterns.push({
38
+ pattern: /Cannot read propert(y|ies) .* of (undefined|null)/i,
39
+ type: 'null_reference',
40
+ analyzer: this.analyzeNullReference.bind(this),
41
+ });
42
+ this.patterns.push({
43
+ pattern: /TypeError:.*is (undefined|null)/i,
44
+ type: 'null_reference',
45
+ analyzer: this.analyzeNullReference.bind(this),
46
+ });
47
+ // Module/import errors
48
+ this.patterns.push({
49
+ pattern: /Module not found|Cannot find module/i,
50
+ type: 'missing_module',
51
+ analyzer: this.analyzeMissingModule.bind(this),
52
+ });
53
+ this.patterns.push({
54
+ pattern: /is not exported from/i,
55
+ type: 'export_error',
56
+ analyzer: this.analyzeExportError.bind(this),
57
+ });
58
+ // Schema/field mismatch errors
59
+ this.patterns.push({
60
+ pattern: /field .* does not exist|unknown field|property .* does not exist on type/i,
61
+ type: 'schema_mismatch',
62
+ analyzer: this.analyzeSchemaMismatch.bind(this),
63
+ });
64
+ this.patterns.push({
65
+ pattern: /Expected .* but received|Type .* is not assignable/i,
66
+ type: 'type_mismatch',
67
+ analyzer: this.analyzeTypeMismatch.bind(this),
68
+ });
69
+ // Firebase/Firestore errors
70
+ this.patterns.push({
71
+ pattern: /PERMISSION_DENIED|Missing or insufficient permissions/i,
72
+ type: 'permission_error',
73
+ analyzer: this.analyzePermissionError.bind(this),
74
+ });
75
+ this.patterns.push({
76
+ pattern: /FirebaseError|Firebase:.*Error/i,
77
+ type: 'firebase_error',
78
+ analyzer: this.analyzeFirebaseError.bind(this),
79
+ });
80
+ // API/Network errors
81
+ this.patterns.push({
82
+ pattern: /fetch failed|network error|ECONNREFUSED/i,
83
+ type: 'network_error',
84
+ analyzer: this.analyzeNetworkError.bind(this),
85
+ });
86
+ this.patterns.push({
87
+ pattern: /4\d{2}|5\d{2}.*error|HTTP Error/i,
88
+ type: 'http_error',
89
+ analyzer: this.analyzeHTTPError.bind(this),
90
+ });
91
+ // Test failures
92
+ this.patterns.push({
93
+ pattern: /test failed|assertion.*failed|expect.*to/i,
94
+ type: 'test_failure',
95
+ analyzer: this.analyzeTestFailure.bind(this),
96
+ });
97
+ // Syntax/compilation errors
98
+ this.patterns.push({
99
+ pattern: /SyntaxError|Unexpected token/i,
100
+ type: 'syntax_error',
101
+ analyzer: this.analyzeSyntaxError.bind(this),
102
+ });
103
+ }
104
+ // ============================================================================
105
+ // PATTERN-SPECIFIC ANALYZERS
106
+ // ============================================================================
107
+ async analyzeNullReference(error, context) {
108
+ // Extract property name from error
109
+ const propertyMatch = error.errorMessage.match(/Cannot read propert(?:y|ies) ['"]?(\w+)['"]?/i);
110
+ const propertyName = propertyMatch?.[1];
111
+ // Check if property exists in any schema
112
+ let relatedSchema;
113
+ let fieldExists = false;
114
+ if (propertyName) {
115
+ for (const schema of context.projectData.schemas) {
116
+ const field = schema.fields?.find((f) => f.fieldName.toLowerCase() === propertyName.toLowerCase());
117
+ if (field) {
118
+ relatedSchema = schema;
119
+ fieldExists = true;
120
+ break;
121
+ }
122
+ }
123
+ }
124
+ const diagnosis = {
125
+ rootCause: fieldExists
126
+ ? `The field "${propertyName}" is defined in schema but the object is null/undefined at runtime`
127
+ : `Attempting to access property "${propertyName || 'unknown'}" on a null or undefined object`,
128
+ explanation: fieldExists
129
+ ? 'This usually means the data hasn\'t been fetched yet, or the query returned no results. Add null checks or ensure data is loaded before accessing.'
130
+ : 'The code assumes an object exists but it\'s actually null/undefined. Add defensive checks.',
131
+ errorCategory: 'null_reference',
132
+ severity: 'medium',
133
+ };
134
+ const fixes = [
135
+ {
136
+ approach: 'Optional Chaining',
137
+ steps: [
138
+ `Use optional chaining (?.) when accessing ${propertyName || 'the property'}`,
139
+ 'Add nullish coalescing (??) for default values',
140
+ ],
141
+ code: propertyName
142
+ ? {
143
+ filename: 'fix.ts',
144
+ content: `// Instead of:\nconst value = obj.${propertyName};\n\n// Use:\nconst value = obj?.${propertyName} ?? defaultValue;`,
145
+ language: 'typescript',
146
+ }
147
+ : undefined,
148
+ confidence: 0.9,
149
+ },
150
+ {
151
+ approach: 'Early Return Guard',
152
+ steps: [
153
+ 'Add a guard clause at the start of the function',
154
+ 'Return early or throw a meaningful error if data is missing',
155
+ ],
156
+ code: {
157
+ filename: 'fix.ts',
158
+ content: `// Add at the start of your function:\nif (!data) {\n console.warn('Data not loaded yet');\n return null; // or throw new Error('Data required');\n}`,
159
+ language: 'typescript',
160
+ },
161
+ confidence: 0.85,
162
+ },
163
+ ];
164
+ // If it's a schema field issue, suggest checking data loading
165
+ if (fieldExists && relatedSchema) {
166
+ fixes.unshift({
167
+ approach: 'Verify Data Loading',
168
+ steps: [
169
+ `Check that ${relatedSchema.collectionName} data is being fetched correctly`,
170
+ 'Verify the document exists in Firestore',
171
+ 'Ensure the query is returning the expected document',
172
+ ],
173
+ confidence: 0.95,
174
+ });
175
+ }
176
+ return {
177
+ diagnosis,
178
+ suggestedFixes: fixes,
179
+ relatedDocs: relatedSchema ? { schemas: [relatedSchema] } : {},
180
+ shouldUncheckStep: false,
181
+ shouldInjectNewStep: false,
182
+ };
183
+ }
184
+ async analyzeMissingModule(error, context) {
185
+ // Extract module name
186
+ const moduleMatch = error.errorMessage.match(/Cannot find module ['"]([^'"]+)['"]/i);
187
+ const moduleName = moduleMatch?.[1] || 'unknown';
188
+ const isLocalModule = moduleName.startsWith('.') || moduleName.startsWith('@/');
189
+ const isNodeModule = !isLocalModule;
190
+ const diagnosis = {
191
+ rootCause: isNodeModule
192
+ ? `The npm package "${moduleName}" is not installed`
193
+ : `The local module "${moduleName}" doesn't exist or path is incorrect`,
194
+ explanation: isNodeModule
195
+ ? 'You need to install this dependency using npm or yarn'
196
+ : 'Check the file path and ensure the file exists at that location',
197
+ errorCategory: 'missing_module',
198
+ severity: 'high',
199
+ };
200
+ const fixes = [];
201
+ if (isNodeModule) {
202
+ // Check if it's in tech stack
203
+ const techItem = context.projectData.techStack.find((t) => moduleName.toLowerCase().includes(t.name.toLowerCase()) ||
204
+ t.name.toLowerCase().includes(moduleName.split('/')[0].replace('@', '')));
205
+ fixes.push({
206
+ approach: 'Install Package',
207
+ steps: [
208
+ `Run: npm install ${moduleName}`,
209
+ 'Or with yarn: yarn add ' + moduleName,
210
+ 'Restart your development server after installation',
211
+ ],
212
+ code: {
213
+ filename: 'terminal',
214
+ content: `npm install ${moduleName}`,
215
+ language: 'bash',
216
+ },
217
+ confidence: 0.95,
218
+ });
219
+ if (techItem) {
220
+ fixes[0].steps.push(`Note: "${techItem.name}" is in your tech stack - ensure version matches: ${techItem.version || 'latest'}`);
221
+ }
222
+ }
223
+ else {
224
+ fixes.push({
225
+ approach: 'Fix Import Path',
226
+ steps: [
227
+ 'Verify the file exists at the specified path',
228
+ 'Check for typos in the filename or path',
229
+ 'Ensure the file extension is correct (.ts, .tsx, .js)',
230
+ 'If using path aliases (@/), check tsconfig.json paths configuration',
231
+ ],
232
+ confidence: 0.8,
233
+ });
234
+ fixes.push({
235
+ approach: 'Create Missing File',
236
+ steps: [
237
+ `Create the missing file at: ${moduleName}`,
238
+ 'Export the required functions/components',
239
+ ],
240
+ confidence: 0.7,
241
+ });
242
+ }
243
+ return {
244
+ diagnosis,
245
+ suggestedFixes: fixes,
246
+ shouldUncheckStep: false,
247
+ shouldInjectNewStep: false,
248
+ };
249
+ }
250
+ async analyzeExportError(error, context) {
251
+ const exportMatch = error.errorMessage.match(/['"](\w+)['"] is not exported from ['"]([^'"]+)['"]/i);
252
+ const exportName = exportMatch?.[1] || 'unknown';
253
+ const modulePath = exportMatch?.[2] || 'unknown';
254
+ const diagnosis = {
255
+ rootCause: `The export "${exportName}" doesn't exist in module "${modulePath}"`,
256
+ explanation: 'Either the export name is wrong, or the module needs to export this item',
257
+ errorCategory: 'export_error',
258
+ severity: 'medium',
259
+ };
260
+ const fixes = [
261
+ {
262
+ approach: 'Check Export Name',
263
+ steps: [
264
+ `Open ${modulePath} and check available exports`,
265
+ 'Look for typos in the export name',
266
+ 'Check if it\'s a default vs named export',
267
+ ],
268
+ confidence: 0.85,
269
+ },
270
+ {
271
+ approach: 'Add Missing Export',
272
+ steps: [
273
+ `Add the export to ${modulePath}`,
274
+ `export const ${exportName} = ...;`,
275
+ 'Or: export { ${exportName} };',
276
+ ],
277
+ code: {
278
+ filename: modulePath,
279
+ content: `// Add this export to ${modulePath}:\nexport const ${exportName} = /* your implementation */;`,
280
+ language: 'typescript',
281
+ },
282
+ confidence: 0.8,
283
+ },
284
+ ];
285
+ return {
286
+ diagnosis,
287
+ suggestedFixes: fixes,
288
+ shouldUncheckStep: false,
289
+ shouldInjectNewStep: false,
290
+ };
291
+ }
292
+ async analyzeSchemaMismatch(error, context) {
293
+ // Extract field name
294
+ const fieldMatch = error.errorMessage.match(/(?:field|property) ['"]?(\w+)['"]?/i);
295
+ const fieldName = fieldMatch?.[1];
296
+ // Find schema that might have this field
297
+ let relatedSchema;
298
+ let fieldDefinedInSchema = false;
299
+ if (fieldName) {
300
+ for (const schema of context.projectData.schemas) {
301
+ const field = schema.fields?.find((f) => f.fieldName.toLowerCase() === fieldName.toLowerCase());
302
+ if (field) {
303
+ relatedSchema = schema;
304
+ fieldDefinedInSchema = true;
305
+ break;
306
+ }
307
+ }
308
+ }
309
+ const diagnosis = {
310
+ rootCause: fieldDefinedInSchema
311
+ ? `Field "${fieldName}" is defined in schema but not present in the actual database document`
312
+ : `Code references field "${fieldName || 'unknown'}" which doesn't exist in any schema`,
313
+ explanation: fieldDefinedInSchema
314
+ ? 'The schema definition is ahead of the actual database state. You need to migrate existing documents or add the field to new documents.'
315
+ : 'Either add this field to the appropriate schema, or fix the code to use an existing field.',
316
+ errorCategory: 'schema_mismatch',
317
+ severity: fieldDefinedInSchema ? 'high' : 'medium',
318
+ };
319
+ const fixes = [];
320
+ if (fieldDefinedInSchema && relatedSchema) {
321
+ // Field is in schema but not in DB - need migration
322
+ fixes.push({
323
+ approach: 'Database Migration',
324
+ steps: [
325
+ `Create a migration script for ${relatedSchema.collectionName} collection`,
326
+ `Add default value for "${fieldName}" to existing documents`,
327
+ 'Run the migration before continuing',
328
+ ],
329
+ code: {
330
+ filename: `migrations/add-${fieldName}-to-${relatedSchema.collectionName}.ts`,
331
+ content: `// Migration script\nimport { getFirestore } from 'firebase-admin/firestore';\n\nconst db = getFirestore();\n\nexport async function migrate() {\n const snapshot = await db.collection('${relatedSchema.collectionName}').get();\n \n const batch = db.batch();\n snapshot.docs.forEach(doc => {\n if (doc.data().${fieldName} === undefined) {\n batch.update(doc.ref, { ${fieldName}: /* default value */ });\n }\n });\n \n await batch.commit();\n console.log('Migration complete');\n}`,
332
+ language: 'typescript',
333
+ },
334
+ confidence: 0.9,
335
+ });
336
+ // Should inject a migration step
337
+ return {
338
+ diagnosis,
339
+ suggestedFixes: fixes,
340
+ relatedDocs: { schemas: [relatedSchema] },
341
+ shouldUncheckStep: true,
342
+ shouldInjectNewStep: true,
343
+ newStep: {
344
+ title: `Database Migration: Add ${fieldName} to ${relatedSchema.collectionName}`,
345
+ category: 'database',
346
+ instruction: `Run a migration to add the "${fieldName}" field to existing documents in the ${relatedSchema.collectionName} collection.\n\nSteps:\n1. Create migration script\n2. Test on a few documents first\n3. Run for all documents\n4. Verify the field exists in documents`,
347
+ },
348
+ };
349
+ }
350
+ // Field not in schema - add defensive coding
351
+ fixes.push({
352
+ approach: 'Defensive Coding',
353
+ steps: [
354
+ 'Add null check before accessing the field',
355
+ 'Provide a sensible default value',
356
+ ],
357
+ code: fieldName
358
+ ? {
359
+ filename: 'fix.ts',
360
+ content: `// Safe access with default:\nconst ${fieldName} = document?.${fieldName} ?? defaultValue;`,
361
+ language: 'typescript',
362
+ }
363
+ : undefined,
364
+ confidence: 0.85,
365
+ });
366
+ return {
367
+ diagnosis,
368
+ suggestedFixes: fixes,
369
+ relatedDocs: relatedSchema ? { schemas: [relatedSchema] } : {},
370
+ shouldUncheckStep: false,
371
+ shouldInjectNewStep: false,
372
+ };
373
+ }
374
+ async analyzeTypeMismatch(error, context) {
375
+ const diagnosis = {
376
+ rootCause: 'Type mismatch between expected and actual values',
377
+ explanation: 'TypeScript is detecting that the types don\'t match. This could be a data transformation issue or incorrect type definition.',
378
+ errorCategory: 'type_mismatch',
379
+ severity: 'medium',
380
+ };
381
+ const fixes = [
382
+ {
383
+ approach: 'Type Assertion',
384
+ steps: [
385
+ 'If you\'re sure about the type, use type assertion',
386
+ 'But first verify the data actually matches',
387
+ ],
388
+ code: {
389
+ filename: 'fix.ts',
390
+ content: `// Type assertion (use carefully):\nconst value = data as ExpectedType;\n\n// Or with validation:\nif (isExpectedType(data)) {\n const value = data;\n}`,
391
+ language: 'typescript',
392
+ },
393
+ confidence: 0.7,
394
+ },
395
+ {
396
+ approach: 'Add Type Guard',
397
+ steps: [
398
+ 'Create a type guard function to validate the data',
399
+ 'Use it before processing',
400
+ ],
401
+ code: {
402
+ filename: 'fix.ts',
403
+ content: `function isExpectedType(data: unknown): data is ExpectedType {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'requiredField' in data\n );\n}`,
404
+ language: 'typescript',
405
+ },
406
+ confidence: 0.85,
407
+ },
408
+ ];
409
+ return {
410
+ diagnosis,
411
+ suggestedFixes: fixes,
412
+ shouldUncheckStep: false,
413
+ shouldInjectNewStep: false,
414
+ };
415
+ }
416
+ async analyzePermissionError(error, context) {
417
+ const diagnosis = {
418
+ rootCause: 'Firestore security rules are blocking this operation',
419
+ explanation: 'The current user doesn\'t have permission to read/write this data. Check your Firestore security rules.',
420
+ errorCategory: 'permission_error',
421
+ severity: 'high',
422
+ };
423
+ // Find relevant tech stack (Firebase)
424
+ const firebaseTech = context.projectData.techStack.find((t) => t.name.toLowerCase().includes('firebase'));
425
+ const fixes = [
426
+ {
427
+ approach: 'Update Security Rules',
428
+ steps: [
429
+ 'Review firestore.rules file',
430
+ 'Ensure the rule allows the current user to perform this operation',
431
+ 'Check if authentication is required and user is logged in',
432
+ ],
433
+ code: {
434
+ filename: 'firestore.rules',
435
+ content: `rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n // Example rule - adjust for your needs:\n match /collection/{docId} {\n allow read: if request.auth != null;\n allow write: if request.auth != null && request.auth.uid == resource.data.userId;\n }\n }\n}`,
436
+ language: 'text',
437
+ },
438
+ confidence: 0.85,
439
+ },
440
+ {
441
+ approach: 'Verify Authentication',
442
+ steps: [
443
+ 'Ensure user is authenticated before this operation',
444
+ 'Check if auth state is initialized',
445
+ 'Verify the auth token hasn\'t expired',
446
+ ],
447
+ code: {
448
+ filename: 'auth-check.ts',
449
+ content: `// Verify auth before Firestore operation:\nimport { auth } from './firebase';\n\nconst user = auth.currentUser;\nif (!user) {\n throw new Error('User must be authenticated');\n}\n\n// Now proceed with Firestore operation`,
450
+ language: 'typescript',
451
+ },
452
+ confidence: 0.8,
453
+ },
454
+ ];
455
+ return {
456
+ diagnosis,
457
+ suggestedFixes: fixes,
458
+ shouldUncheckStep: false,
459
+ shouldInjectNewStep: false,
460
+ };
461
+ }
462
+ async analyzeFirebaseError(error, context) {
463
+ // Try to extract specific Firebase error code
464
+ const codeMatch = error.errorMessage.match(/auth\/[\w-]+/);
465
+ const errorCode = codeMatch?.[0];
466
+ let specificFix;
467
+ // Common Firebase Auth errors
468
+ const authErrorFixes = {
469
+ 'auth/user-not-found': {
470
+ approach: 'Handle User Not Found',
471
+ steps: ['Check if user exists before operation', 'Provide user-friendly error message'],
472
+ confidence: 0.9,
473
+ },
474
+ 'auth/wrong-password': {
475
+ approach: 'Handle Wrong Password',
476
+ steps: ['Show generic "invalid credentials" message for security', 'Offer password reset'],
477
+ confidence: 0.9,
478
+ },
479
+ 'auth/email-already-in-use': {
480
+ approach: 'Handle Existing Email',
481
+ steps: ['Inform user the email is taken', 'Offer login option instead'],
482
+ confidence: 0.9,
483
+ },
484
+ 'auth/weak-password': {
485
+ approach: 'Enforce Password Requirements',
486
+ steps: ['Show password requirements to user', 'Validate before submission'],
487
+ confidence: 0.9,
488
+ },
489
+ };
490
+ if (errorCode && authErrorFixes[errorCode]) {
491
+ specificFix = authErrorFixes[errorCode];
492
+ }
493
+ const diagnosis = {
494
+ rootCause: errorCode
495
+ ? `Firebase error: ${errorCode}`
496
+ : 'Firebase operation failed',
497
+ explanation: 'A Firebase service encountered an error. Check the specific error code for details.',
498
+ errorCategory: 'firebase_error',
499
+ severity: 'medium',
500
+ };
501
+ const fixes = specificFix
502
+ ? [specificFix]
503
+ : [
504
+ {
505
+ approach: 'Add Error Handling',
506
+ steps: [
507
+ 'Wrap Firebase calls in try-catch',
508
+ 'Check error.code for specific handling',
509
+ 'Provide user-friendly error messages',
510
+ ],
511
+ code: {
512
+ filename: 'firebase-error-handling.ts',
513
+ content: `try {\n await firebaseOperation();\n} catch (error: any) {\n switch (error.code) {\n case 'auth/user-not-found':\n // Handle specific error\n break;\n default:\n console.error('Firebase error:', error);\n }\n}`,
514
+ language: 'typescript',
515
+ },
516
+ confidence: 0.75,
517
+ },
518
+ ];
519
+ return {
520
+ diagnosis,
521
+ suggestedFixes: fixes,
522
+ shouldUncheckStep: false,
523
+ shouldInjectNewStep: false,
524
+ };
525
+ }
526
+ async analyzeNetworkError(error, context) {
527
+ const diagnosis = {
528
+ rootCause: 'Network request failed - server unreachable or connection issue',
529
+ explanation: 'The API endpoint or server is not responding. This could be a server issue, CORS problem, or network connectivity.',
530
+ errorCategory: 'network_error',
531
+ severity: 'high',
532
+ };
533
+ const fixes = [
534
+ {
535
+ approach: 'Check API Server',
536
+ steps: [
537
+ 'Verify the API server is running',
538
+ 'Check the URL is correct',
539
+ 'Try the endpoint directly in browser or Postman',
540
+ ],
541
+ confidence: 0.8,
542
+ },
543
+ {
544
+ approach: 'Handle Network Errors',
545
+ steps: [
546
+ 'Add retry logic for transient failures',
547
+ 'Show user-friendly offline message',
548
+ 'Consider using a library like axios with interceptors',
549
+ ],
550
+ code: {
551
+ filename: 'network-retry.ts',
552
+ content: `async function fetchWithRetry(url: string, retries = 3): Promise<Response> {\n for (let i = 0; i < retries; i++) {\n try {\n return await fetch(url);\n } catch (error) {\n if (i === retries - 1) throw error;\n await new Promise(r => setTimeout(r, 1000 * (i + 1)));\n }\n }\n throw new Error('Max retries reached');\n}`,
553
+ language: 'typescript',
554
+ },
555
+ confidence: 0.85,
556
+ },
557
+ ];
558
+ return {
559
+ diagnosis,
560
+ suggestedFixes: fixes,
561
+ shouldUncheckStep: false,
562
+ shouldInjectNewStep: false,
563
+ };
564
+ }
565
+ async analyzeHTTPError(error, context) {
566
+ // Extract status code
567
+ const statusMatch = error.errorMessage.match(/\b([45]\d{2})\b/);
568
+ const statusCode = statusMatch ? parseInt(statusMatch[1]) : 500;
569
+ const statusMessages = {
570
+ 400: 'Bad request - check your request payload',
571
+ 401: 'Unauthorized - authentication required or token expired',
572
+ 403: 'Forbidden - user lacks permission',
573
+ 404: 'Not found - check the endpoint URL',
574
+ 422: 'Validation error - check request data format',
575
+ 429: 'Too many requests - implement rate limiting',
576
+ 500: 'Server error - check server logs',
577
+ 502: 'Bad gateway - upstream server issue',
578
+ 503: 'Service unavailable - server may be down',
579
+ };
580
+ const diagnosis = {
581
+ rootCause: `HTTP ${statusCode}: ${statusMessages[statusCode] || 'Request failed'}`,
582
+ explanation: 'The API returned an error status code. Check the response body for details.',
583
+ errorCategory: 'http_error',
584
+ severity: statusCode >= 500 ? 'high' : 'medium',
585
+ };
586
+ const fixes = [];
587
+ if (statusCode === 401) {
588
+ fixes.push({
589
+ approach: 'Refresh Authentication',
590
+ steps: [
591
+ 'Check if auth token exists and is valid',
592
+ 'Implement token refresh logic',
593
+ 'Redirect to login if refresh fails',
594
+ ],
595
+ confidence: 0.9,
596
+ });
597
+ }
598
+ else if (statusCode === 404) {
599
+ fixes.push({
600
+ approach: 'Verify Endpoint',
601
+ steps: [
602
+ 'Check the API URL is correct',
603
+ 'Verify the resource ID exists',
604
+ 'Check if the endpoint is deployed',
605
+ ],
606
+ confidence: 0.85,
607
+ });
608
+ }
609
+ else {
610
+ fixes.push({
611
+ approach: 'Add Error Response Handling',
612
+ steps: [
613
+ 'Parse the error response body',
614
+ 'Display specific error message to user',
615
+ 'Log detailed error for debugging',
616
+ ],
617
+ code: {
618
+ filename: 'http-error-handling.ts',
619
+ content: `const response = await fetch(url);\nif (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.message || \`HTTP \${response.status}\`);\n}`,
620
+ language: 'typescript',
621
+ },
622
+ confidence: 0.8,
623
+ });
624
+ }
625
+ return {
626
+ diagnosis,
627
+ suggestedFixes: fixes,
628
+ shouldUncheckStep: false,
629
+ shouldInjectNewStep: false,
630
+ };
631
+ }
632
+ async analyzeTestFailure(error, context) {
633
+ const diagnosis = {
634
+ rootCause: 'Test assertion failed',
635
+ explanation: 'A test expectation was not met. Either the test is wrong or the implementation needs fixing.',
636
+ errorCategory: 'test_failure',
637
+ severity: 'medium',
638
+ };
639
+ const fixes = [
640
+ {
641
+ approach: 'Review Test vs Implementation',
642
+ steps: [
643
+ 'Read the test to understand expected behavior',
644
+ 'Check if implementation matches requirements',
645
+ 'Verify test data and mocks are correct',
646
+ ],
647
+ confidence: 0.7,
648
+ },
649
+ {
650
+ approach: 'Debug with Console Logs',
651
+ steps: [
652
+ 'Add console.log to see actual values',
653
+ 'Compare with expected values',
654
+ 'Check for edge cases',
655
+ ],
656
+ confidence: 0.75,
657
+ },
658
+ ];
659
+ return {
660
+ diagnosis,
661
+ suggestedFixes: fixes,
662
+ shouldUncheckStep: false, // Tests failing doesn't necessarily mean step should be unchecked
663
+ shouldInjectNewStep: false,
664
+ };
665
+ }
666
+ async analyzeSyntaxError(error, context) {
667
+ // Try to extract line info
668
+ const lineMatch = error.stackTrace?.match(/:(\d+):\d+/);
669
+ const lineNumber = lineMatch?.[1];
670
+ const diagnosis = {
671
+ rootCause: 'Syntax error in code',
672
+ explanation: lineNumber
673
+ ? `There's a syntax error around line ${lineNumber}. Check for missing brackets, semicolons, or typos.`
674
+ : 'There\'s a syntax error in the code. Check for missing brackets, semicolons, or typos.',
675
+ errorCategory: 'syntax_error',
676
+ severity: 'high',
677
+ };
678
+ const fixes = [
679
+ {
680
+ approach: 'Check Common Syntax Issues',
681
+ steps: [
682
+ 'Look for unmatched brackets: { }, ( ), [ ]',
683
+ 'Check for missing commas in objects/arrays',
684
+ 'Verify template literal backticks are paired',
685
+ 'Check for missing semicolons (if required)',
686
+ ],
687
+ confidence: 0.9,
688
+ },
689
+ {
690
+ approach: 'Use IDE Features',
691
+ steps: [
692
+ 'Let your IDE highlight the syntax error',
693
+ 'Use "Format Document" to auto-fix some issues',
694
+ 'Check the Problems panel for details',
695
+ ],
696
+ confidence: 0.85,
697
+ },
698
+ ];
699
+ return {
700
+ diagnosis,
701
+ suggestedFixes: fixes,
702
+ shouldUncheckStep: false,
703
+ shouldInjectNewStep: false,
704
+ };
705
+ }
706
+ // ============================================================================
707
+ // GENERIC FALLBACK ANALYZER
708
+ // ============================================================================
709
+ async genericAnalysis(error, context) {
710
+ const diagnosis = {
711
+ rootCause: 'Error encountered during build step',
712
+ explanation: `An error occurred: "${error.errorMessage.substring(0, 200)}". Review the stack trace for more details.`,
713
+ errorCategory: error.errorType,
714
+ severity: 'medium',
715
+ };
716
+ const fixes = [
717
+ {
718
+ approach: 'Debug the Error',
719
+ steps: [
720
+ 'Review the full stack trace',
721
+ 'Add logging around the failing code',
722
+ 'Check recent changes that might have caused this',
723
+ 'Search for the error message online',
724
+ ],
725
+ confidence: 0.5,
726
+ },
727
+ {
728
+ approach: 'Isolate the Problem',
729
+ steps: [
730
+ 'Comment out recent code changes',
731
+ 'Test each piece individually',
732
+ 'Narrow down to the specific line causing issues',
733
+ ],
734
+ confidence: 0.6,
735
+ },
736
+ ];
737
+ return {
738
+ diagnosis,
739
+ suggestedFixes: fixes,
740
+ relatedDocs: {},
741
+ shouldUncheckStep: false,
742
+ shouldInjectNewStep: false,
743
+ };
744
+ }
745
+ // ============================================================================
746
+ // HELPER METHODS
747
+ // ============================================================================
748
+ buildFullResult(error, context, partialResult, errorType) {
749
+ return {
750
+ diagnosis: partialResult.diagnosis || {
751
+ rootCause: 'Unknown error',
752
+ explanation: error.errorMessage,
753
+ errorCategory: errorType,
754
+ severity: 'medium',
755
+ },
756
+ suggestedFixes: partialResult.suggestedFixes || [],
757
+ relatedDocs: partialResult.relatedDocs || {},
758
+ shouldUncheckStep: partialResult.shouldUncheckStep || false,
759
+ shouldInjectNewStep: partialResult.shouldInjectNewStep || false,
760
+ newStep: partialResult.newStep,
761
+ };
762
+ }
763
+ }
764
+ exports.ErrorAnalyzer = ErrorAnalyzer;
765
+ //# sourceMappingURL=error-analyzer.js.map