devintelligence 1.0.0 → 1.0.1

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 (75) hide show
  1. package/.env.example +14 -14
  2. package/LICENSE +22 -22
  3. package/README.md +146 -146
  4. package/bin/devintelligence.js +2481 -12
  5. package/bin/devintelligence.ts +244 -103
  6. package/dist/App.d.ts +3 -0
  7. package/dist/ai.js +366 -0
  8. package/dist/ai.js.map +7 -0
  9. package/dist/components/DeepAnalysisViews.d.ts +13 -0
  10. package/dist/components/MobileAndPlatformViews.d.ts +22 -0
  11. package/dist/components/OverviewAndExplorerViews.d.ts +20 -0
  12. package/dist/components/RepairAndGateViews.d.ts +25 -0
  13. package/dist/engine/aiEngine.d.ts +62 -0
  14. package/dist/engine/analyzerEngine.d.ts +19 -0
  15. package/dist/engine/repairEngine.d.ts +42 -0
  16. package/dist/engine.js +1541 -0
  17. package/dist/engine.js.map +7 -0
  18. package/dist/index.d.ts +10 -0
  19. package/dist/index.js +2344 -0
  20. package/dist/index.js.map +7 -0
  21. package/dist/main.d.ts +1 -0
  22. package/dist/repair.js +2052 -0
  23. package/dist/repair.js.map +7 -0
  24. package/dist/src/App.d.ts +3 -0
  25. package/dist/src/components/DeepAnalysisViews.d.ts +13 -0
  26. package/dist/src/components/MobileAndPlatformViews.d.ts +22 -0
  27. package/dist/src/components/OverviewAndExplorerViews.d.ts +20 -0
  28. package/dist/src/components/RepairAndGateViews.d.ts +25 -0
  29. package/dist/src/engine/aiEngine.d.ts +62 -0
  30. package/dist/src/engine/analyzerEngine.d.ts +19 -0
  31. package/dist/src/engine/repairEngine.d.ts +42 -0
  32. package/dist/src/index.d.ts +10 -0
  33. package/dist/src/main.d.ts +1 -0
  34. package/dist/src/types/devintelligence.d.ts +250 -0
  35. package/dist/types/devintelligence.d.ts +250 -0
  36. package/fixtures/django-analytics-service/requirements.txt +4 -4
  37. package/fixtures/django-analytics-service/settings.py +16 -16
  38. package/fixtures/express-api-gateway/package.json +14 -14
  39. package/fixtures/express-api-gateway/src/server.ts +16 -16
  40. package/fixtures/nextjs-saas-portal/package.json +21 -21
  41. package/fixtures/nextjs-saas-portal/src/app/dashboard/page.tsx +26 -26
  42. package/fixtures/rn-bare-fitness/android/build.gradle +19 -19
  43. package/fixtures/rn-bare-fitness/ios/Info.plist +14 -14
  44. package/fixtures/rn-bare-fitness/package.json +23 -23
  45. package/fixtures/rn-bare-fitness/src/WorkoutTracker.tsx +31 -31
  46. package/fixtures/rn-expo-commerce/android/app/build.gradle +20 -20
  47. package/fixtures/rn-expo-commerce/android/app/src/main/AndroidManifest.xml +21 -21
  48. package/fixtures/rn-expo-commerce/android/gradle.properties +6 -6
  49. package/fixtures/rn-expo-commerce/app.json +17 -17
  50. package/fixtures/rn-expo-commerce/babel.config.js +10 -10
  51. package/fixtures/rn-expo-commerce/ios/Podfile +13 -13
  52. package/fixtures/rn-expo-commerce/metro.config.js +7 -7
  53. package/fixtures/rn-expo-commerce/package.json +31 -31
  54. package/fixtures/rn-expo-commerce/src/components/LegacyPromoBanner.tsx +11 -11
  55. package/fixtures/rn-expo-commerce/src/screens/ProductFeedScreen.tsx +48 -48
  56. package/fixtures/rn-expo-commerce/src/services/api.ts +15 -15
  57. package/package.json +89 -77
  58. package/index.html +0 -21
  59. package/server.ts +0 -275
  60. package/src/App.tsx +0 -783
  61. package/src/components/DeepAnalysisViews.tsx +0 -526
  62. package/src/components/MobileAndPlatformViews.tsx +0 -625
  63. package/src/components/OverviewAndExplorerViews.tsx +0 -753
  64. package/src/components/RepairAndGateViews.tsx +0 -814
  65. package/src/engine/aiEngine.test.ts +0 -86
  66. package/src/engine/aiEngine.ts +0 -346
  67. package/src/engine/analyzerEngine.test.ts +0 -110
  68. package/src/engine/analyzerEngine.ts +0 -1579
  69. package/src/engine/repairEngine.ts +0 -545
  70. package/src/index.css +0 -44
  71. package/src/index.ts +0 -59
  72. package/src/main.tsx +0 -5
  73. package/src/types/devintelligence.ts +0 -310
  74. package/tsconfig.json +0 -28
  75. package/vite.config.ts +0 -22
@@ -1,86 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import {
4
- diagnoseRawErrorLogWithAi,
5
- generateProjectAiAdvice,
6
- askDevIntelligenceAi,
7
- isAiRateLimited,
8
- resetAiRateLimit,
9
- callGroqChat,
10
- } from './aiEngine.ts';
11
- import { getProjectReportById } from './repairEngine.ts';
12
-
13
- test('AI Engine: Successfully analyzes React Native error using Groq AI with rich diagnosis', async () => {
14
- const sampleLog = 'Error: Objects are not valid as a React child (found: object with keys {title, id}). If you meant to render a collection of children, use an array instead.';
15
- const diag = await diagnoseRawErrorLogWithAi(sampleLog);
16
-
17
- assert.ok(diag.summary);
18
- assert.ok(diag.rootCause);
19
- assert.ok(diag.confidence >= 50);
20
- assert.ok(Array.isArray(diag.safeRepairSteps));
21
- assert.ok(diag.safeRepairSteps.length > 0);
22
- assert.equal(typeof diag.aiPowered, 'boolean');
23
- });
24
-
25
- test('AI Engine Shield: Guaranteed zero-downtime fallback when quota is exhausted or key is invalid', async () => {
26
- const originalKey = process.env.GROQ_API_KEY;
27
- try {
28
- // Deliberately simulate invalid key / quota exhaustion
29
- process.env.GROQ_API_KEY = 'gsk_EXHAUSTED_LIMIT_TEST_KEY_12345';
30
- resetAiRateLimit();
31
-
32
- const sampleLog = 'android:exported needs to be explicitly specified for <activity>';
33
- const diag = await diagnoseRawErrorLogWithAi(sampleLog);
34
-
35
- assert.equal(diag.aiPowered, false);
36
- assert.equal(diag.fallbackUsed, true);
37
- assert.ok(diag.fallbackReason?.length);
38
- assert.equal(diag.category, 'Android Manifest');
39
- assert.ok(diag.summary.includes('Manifest'));
40
- assert.ok(diag.safeRepairSteps.length > 0);
41
- } finally {
42
- process.env.GROQ_API_KEY = originalKey;
43
- resetAiRateLimit();
44
- }
45
- });
46
-
47
- test('AI Engine Shield: Circuit breaker prevents network spam during rate limit cooldown', async () => {
48
- const originalKey = process.env.GROQ_API_KEY;
49
- try {
50
- process.env.GROQ_API_KEY = 'gsk_TEST_COOLDOWN_KEY';
51
- // Simulate active rate-limit cooldown
52
- const sampleLog = 'Failed to create a worklet';
53
-
54
- // Call low level to verify circuit breaker detection
55
- const res = await callGroqChat([{ role: 'user', content: 'test' }]);
56
- assert.equal(typeof res.ok, 'boolean');
57
-
58
- const diag = await diagnoseRawErrorLogWithAi(sampleLog);
59
- assert.equal(typeof diag.aiPowered, 'boolean');
60
- assert.ok(diag.summary.includes('Reanimated') || diag.summary.includes('worklet') || diag.summary.includes('Babel'));
61
- } finally {
62
- process.env.GROQ_API_KEY = originalKey;
63
- resetAiRateLimit();
64
- }
65
- });
66
-
67
- test('AI Engine: Gracefully handles empty or blank error log without throwing', async () => {
68
- const diag = await diagnoseRawErrorLogWithAi(' ');
69
- assert.ok(diag.summary);
70
- assert.ok(diag.category);
71
- assert.equal(diag.aiPowered, undefined); // Returns pure heuristic default
72
- });
73
-
74
- test('AI Engine: Generates project executive advice with fallback support', async () => {
75
- const report = getProjectReportById('rn-expo-commerce', false);
76
- const advice = await generateProjectAiAdvice(report);
77
-
78
- assert.ok(advice.advice.length > 50);
79
- assert.equal(typeof advice.aiPowered, 'boolean');
80
- });
81
-
82
- test('AI Engine: Ask assistant returns meaningful technical answer', async () => {
83
- const res = await askDevIntelligenceAi('How to clear Metro bundler cache?');
84
- assert.ok(res.answer.includes('start') || res.answer.includes('cache') || res.answer.includes('Metro'));
85
- assert.equal(typeof res.aiPowered, 'boolean');
86
- });
@@ -1,346 +0,0 @@
1
- /**
2
- * DEVINTELLIGENCE & NETCONTROL AI Engine (Powered by Groq)
3
- *
4
- * Provides ultra-fast LLM inference with resilient fallback architecture.
5
- * If the Groq API key reaches rate limits (HTTP 429), quota limits, or network timeouts,
6
- * this engine automatically and seamlessly falls back to built-in heuristic rule-based engines
7
- * so that the system NEVER crashes and operations continue 100% normally.
8
- */
9
-
10
- import type { ErrorDiagnosisResult, IssueSeverity, ProjectIntelligenceReport } from '../types/devintelligence.ts';
11
- import { diagnoseRawErrorLog } from './analyzerEngine.ts';
12
-
13
- // Hardcoded fallback key provided by user, overridden by process.env.GROQ_API_KEY
14
- const DEFAULT_GROQ_KEY = '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';
15
- const PRIMARY_MODEL = process.env.GROQ_MODEL || 'qwen/qwen3.8-27b';
16
- const FALLBACK_MODEL = 'openai/gpt-oss-20b';
17
-
18
- // In-memory circuit breaker to avoid hammering Groq when rate limits / quota are active
19
- let rateLimitCooldownUntil = 0;
20
- let lastRateLimitMessage = '';
21
-
22
- export function getGroqApiKey(): string {
23
- return process.env.GROQ_API_KEY || DEFAULT_GROQ_KEY;
24
- }
25
-
26
- export function isAiRateLimited(): boolean {
27
- return Date.now() < rateLimitCooldownUntil;
28
- }
29
-
30
- export function resetAiRateLimit(): void {
31
- rateLimitCooldownUntil = 0;
32
- lastRateLimitMessage = '';
33
- }
34
-
35
- interface GroqMessage {
36
- role: 'system' | 'user' | 'assistant';
37
- content: string;
38
- }
39
-
40
- export interface GroqChatSuccess {
41
- ok: true;
42
- content: string;
43
- model: string;
44
- }
45
-
46
- export interface GroqChatFailure {
47
- ok: false;
48
- error: string;
49
- isRateLimit: boolean;
50
- }
51
-
52
- export type GroqChatResult = GroqChatSuccess | GroqChatFailure;
53
-
54
- interface GroqChatOptions {
55
- model?: string;
56
- temperature?: number;
57
- maxTokens?: number;
58
- jsonMode?: boolean;
59
- }
60
-
61
- /**
62
- * Low-level caller for Groq OpenAI-compatible Chat Completions API.
63
- * Protected by timeout, circuit breaker, and graceful error trapping.
64
- */
65
- export async function callGroqChat(
66
- messages: GroqMessage[],
67
- options: GroqChatOptions = {}
68
- ): Promise<GroqChatResult> {
69
- const apiKey = getGroqApiKey();
70
- if (!apiKey || !apiKey.startsWith('gsk_')) {
71
- return { ok: false, error: 'No valid Groq API key configured', isRateLimit: false };
72
- }
73
-
74
- // If in rate-limit cooldown, fail fast to rule fallback
75
- if (isAiRateLimited()) {
76
- return {
77
- ok: false,
78
- error: `AI Rate limit cooldown active for ${Math.ceil((rateLimitCooldownUntil - Date.now()) / 1000)}s: ${lastRateLimitMessage}`,
79
- isRateLimit: true,
80
- };
81
- }
82
-
83
- const model = options.model || PRIMARY_MODEL;
84
- const controller = new AbortController();
85
- const timeoutId = setTimeout(() => controller.abort(), 12000); // 12s safety timeout
86
-
87
- try {
88
- const payload: Record<string, unknown> = {
89
- model,
90
- messages,
91
- temperature: options.temperature ?? 0.2,
92
- max_tokens: options.maxTokens ?? 1500,
93
- };
94
-
95
- if (options.jsonMode) {
96
- payload.response_format = { type: 'json_object' };
97
- }
98
-
99
- const response = await fetch('https://api.groq.com/openai/v1/chat/completions', {
100
- method: 'POST',
101
- headers: {
102
- 'Authorization': `Bearer ${apiKey}`,
103
- 'Content-Type': 'application/json',
104
- },
105
- body: JSON.stringify(payload),
106
- signal: controller.signal,
107
- });
108
-
109
- clearTimeout(timeoutId);
110
-
111
- if (response.status === 429) {
112
- const errText = await response.text();
113
- // Set 45-second cooldown to protect user quota and prevent latency spikes
114
- rateLimitCooldownUntil = Date.now() + 45000;
115
- lastRateLimitMessage = 'Groq rate limit or quota exceeded (HTTP 429)';
116
- console.warn(`[Groq AI Shield] Rate limit reached (429). Triggered 45s cooldown. Falling back to local rule engine.`);
117
- return { ok: false, error: errText, isRateLimit: true };
118
- }
119
-
120
- if (!response.ok) {
121
- const errText = await response.text();
122
- console.warn(`[Groq AI Shield] HTTP error ${response.status}: ${errText}. Falling back to local rule engine.`);
123
- return { ok: false, error: `Groq HTTP ${response.status}: ${errText}`, isRateLimit: false };
124
- }
125
-
126
- const data = (await response.json()) as any;
127
- const content = data.choices?.[0]?.message?.content || '';
128
- return { ok: true, content, model };
129
- } catch (err: any) {
130
- clearTimeout(timeoutId);
131
- const isTimeout = err.name === 'AbortError';
132
- const errorMsg = isTimeout ? 'AI Request timed out after 12s' : err.message || 'Unknown network error';
133
- console.warn(`[Groq AI Shield] Call failed (${errorMsg}). Gracefully falling back to local rule engine.`);
134
- return { ok: false, error: errorMsg, isRateLimit: false };
135
- }
136
- }
137
-
138
- /**
139
- * AI-Powered Error Log Diagnosis with Guaranteed Rule Fallback.
140
- * If AI fails, exceeds quota, or is offline, the local heuristic rule engine
141
- * automatically generates a complete diagnosis with 0 downtime.
142
- */
143
- export async function diagnoseRawErrorLogWithAi(rawLog: string): Promise<ErrorDiagnosisResult> {
144
- const fallbackResult = diagnoseRawErrorLog(rawLog);
145
-
146
- if (!rawLog || !rawLog.trim()) {
147
- return fallbackResult;
148
- }
149
-
150
- const systemPrompt = `You are the lead engineering diagnostic agent in DEVINTELLIGENCE.
151
- Analyze the provided terminal error trace, Metro bundler failure, Android Gradle/Kotlin error, iOS CocoaPods error, or React Native Red Screen crash.
152
- Respond ONLY with a valid JSON object matching this TypeScript structure:
153
- {
154
- "category": "Metro Bundler" | "Babel / Reanimated" | "Android Gradle / Kotlin" | "Android Manifest" | "CocoaPods / iOS" | "Native Module Linking" | "Hermes Runtime" | "React Render / Hook Crash" | "Dependency Conflict",
155
- "severity": "critical" | "high" | "medium" | "low",
156
- "confidence": number (between 70 and 99),
157
- "summary": "Short 1-sentence punchy title of the crash",
158
- "rootCause": "Detailed technical root-cause explanation of what broke and why",
159
- "responsibleFile": "File path most likely responsible (e.g. android/app/build.gradle, babel.config.js, etc.)",
160
- "line": number or null,
161
- "safeRepairSteps": ["step 1", "step 2", "step 3"],
162
- "terminalCommands": ["exact command to run"],
163
- "codePatchSuggestion": "exact code or config patch snippet to fix it"
164
- }`;
165
-
166
- const userPrompt = `Diagnose this error log:\n\n${rawLog.slice(0, 3000)}`;
167
-
168
- try {
169
- const aiRes = await callGroqChat(
170
- [
171
- { role: 'system', content: systemPrompt },
172
- { role: 'user', content: userPrompt },
173
- ],
174
- { jsonMode: true, temperature: 0.1 }
175
- );
176
-
177
- if (!aiRes.ok) {
178
- const failure = aiRes as GroqChatFailure;
179
- return {
180
- ...fallbackResult,
181
- aiPowered: false,
182
- fallbackUsed: true,
183
- fallbackReason: failure.isRateLimit
184
- ? 'AI rate limit reached. Analysis generated seamlessly by local heuristic rule engine.'
185
- : `AI unavailable (${failure.error}). Analysis completed by local heuristic rule engine.`,
186
- };
187
- }
188
-
189
- try {
190
- const parsed = JSON.parse(aiRes.content);
191
- return {
192
- id: `ai-diag-${Date.now()}`,
193
- category: parsed.category || fallbackResult.category,
194
- severity: (['critical', 'high', 'medium', 'low'].includes(parsed.severity)
195
- ? parsed.severity
196
- : fallbackResult.severity) as IssueSeverity,
197
- confidence: typeof parsed.confidence === 'number' ? Math.min(Math.max(parsed.confidence, 60), 99) : 95,
198
- summary: parsed.summary || fallbackResult.summary,
199
- rootCause: parsed.rootCause || fallbackResult.rootCause,
200
- responsibleFile: parsed.responsibleFile || fallbackResult.responsibleFile,
201
- line: typeof parsed.line === 'number' ? parsed.line : fallbackResult.line,
202
- safeRepairSteps: Array.isArray(parsed.safeRepairSteps) && parsed.safeRepairSteps.length > 0
203
- ? parsed.safeRepairSteps
204
- : fallbackResult.safeRepairSteps,
205
- terminalCommands: Array.isArray(parsed.terminalCommands)
206
- ? parsed.terminalCommands
207
- : fallbackResult.terminalCommands,
208
- codePatchSuggestion: parsed.codePatchSuggestion || fallbackResult.codePatchSuggestion,
209
- aiPowered: true,
210
- aiModel: aiRes.model,
211
- fallbackUsed: false,
212
- };
213
- } catch (parseErr) {
214
- console.warn('[Groq AI] JSON parse error in AI diagnosis, falling back to rule engine:', parseErr);
215
- return {
216
- ...fallbackResult,
217
- aiPowered: false,
218
- fallbackUsed: true,
219
- fallbackReason: 'AI response parse failed. Analysis completed by local heuristic rule engine.',
220
- };
221
- }
222
- } catch (err: any) {
223
- return {
224
- ...fallbackResult,
225
- aiPowered: false,
226
- fallbackUsed: true,
227
- fallbackReason: `Local rule fallback active: ${err.message || 'AI request interrupted'}`,
228
- };
229
- }
230
- }
231
-
232
- /**
233
- * Generates an AI-driven Executive Engineering Brief for a project.
234
- * Falls back to deterministic rule summary if quota is exhausted.
235
- */
236
- export async function generateProjectAiAdvice(
237
- report: ProjectIntelligenceReport
238
- ): Promise<{ advice: string; aiPowered: boolean; model?: string }> {
239
- const issuesSample = report.issues.slice(0, 8).map((i) => ({
240
- title: i.title,
241
- severity: i.severity,
242
- domain: i.domain,
243
- file: `${i.filePath}:${i.line}`,
244
- explanation: i.explanation,
245
- }));
246
-
247
- const systemPrompt = `You are a Principal Mobile & Fullstack Architect reviewing a code scan report.
248
- Provide a concise, 3-paragraph executive engineering assessment:
249
- 1. Overall architecture and health overview based on scores and ecosystem.
250
- 2. Immediate critical threats (security, native crashes, memory leaks).
251
- 3. Exact prioritized 3-step action plan to reach 100/100 Quality Gate.
252
- Keep it strictly technical, direct, and actionable. Do not use generic filler.`;
253
-
254
- const userPrompt = `Project: ${report.name} (${report.ecosystem})
255
- Scores: Overall: ${report.scores.overall}/100, Security: ${report.scores.security}/100, Performance: ${report.scores.performance}/100, RN: ${report.scores.reactNative}/100
256
- Issues Count: ${report.issues.length}
257
- Key Issues:
258
- ${JSON.stringify(issuesSample, null, 2)}`;
259
-
260
- const aiRes = await callGroqChat(
261
- [
262
- { role: 'system', content: systemPrompt },
263
- { role: 'user', content: userPrompt },
264
- ],
265
- { temperature: 0.3, maxTokens: 800 }
266
- );
267
-
268
- if (aiRes.ok && aiRes.content.trim()) {
269
- return {
270
- advice: aiRes.content.trim(),
271
- aiPowered: true,
272
- model: aiRes.model,
273
- };
274
- }
275
-
276
- // Graceful rule-based fallback advice
277
- const criticals = report.issues.filter((i) => i.severity === 'critical');
278
- const safeRepairs = report.issues.filter((i) => i.repairTier === 'SAFE');
279
-
280
- const fallbackAdvice = `### Deterministic Engineering Assessment (Rule Engine Fallback)
281
-
282
- 1. **Architecture Status**: Project **${report.name}** is operating on **${report.ecosystem}** with an overall score of **${report.scores.overall}/100**.
283
- ${criticals.length > 0 ? `🚨 **Critical Attention Needed**: Detected ${criticals.length} critical issues including ${criticals.map((c) => c.title).slice(0, 2).join(' and ')}.` : '✅ No critical blocker violations found.'}
284
-
285
- 2. **Automated Remediation Opportunity**: **${safeRepairs.length} of ${report.issues.length}** detected issues have validated SAFE auto-repair patches ready to apply with zero regression risk.
286
-
287
- 3. **Recommended 3-Step Action Plan**:
288
- - Run safe batch remediation: \`devintelligence fix --project=${report.id}\`
289
- - Review security and authentication tokens in \`Security Audit\`
290
- - Check bundler cache and verify Quality Gate threshold compliance.`;
291
-
292
- return {
293
- advice: fallbackAdvice,
294
- aiPowered: false,
295
- };
296
- }
297
-
298
- /**
299
- * General AI Assistant for interactive developer queries or network troubleshooting.
300
- */
301
- export async function askDevIntelligenceAi(
302
- query: string,
303
- contextData?: unknown
304
- ): Promise<{ answer: string; aiPowered: boolean; model?: string }> {
305
- if (!query || !query.trim()) {
306
- return { answer: 'Please enter a query.', aiPowered: false };
307
- }
308
-
309
- const systemPrompt = `You are the DEVINTELLIGENCE Engineering AI Assistant.
310
- You specialize in:
311
- - React Native, Expo, Android Gradle, Kotlin, iOS CocoaPods, Swift, Metro bundler.
312
- - Next.js, React, Node.js, Express, Python Django, TypeScript, Vite.
313
- - Static code analysis, security auditing, and automated safe code repair.
314
- Provide clear, authoritative, copy-paste ready technical instructions.`;
315
-
316
- const userContent = contextData
317
- ? `Context:\n${JSON.stringify(contextData, null, 2)}\n\nUser Question:\n${query}`
318
- : query;
319
-
320
- const aiRes = await callGroqChat(
321
- [
322
- { role: 'system', content: systemPrompt },
323
- { role: 'user', content: userContent },
324
- ],
325
- { temperature: 0.2, maxTokens: 1200 }
326
- );
327
-
328
- if (aiRes.ok && aiRes.content.trim()) {
329
- return {
330
- answer: aiRes.content.trim(),
331
- aiPowered: true,
332
- model: aiRes.model,
333
- };
334
- }
335
-
336
- // Graceful rule-based guidance fallback
337
- return {
338
- answer: `### Local Engineering Assistant (Rule Fallback)
339
-
340
- AI inference is currently running in offline rule mode (rate limit or connection limit).
341
- - For build errors: Try cleaning cache with \`npx expo start --clear\` or \`cd android && ./gradlew clean\`.
342
- - For project issues: Run \`devintelligence scan\` or check the Auto Repair Center.
343
- - For environment issues: Run \`devintelligence doctor\`.`,
344
- aiPowered: false,
345
- };
346
- }
@@ -1,110 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import path from 'node:path';
4
- import {
5
- inspectHostEnvironment,
6
- detectProjectEcosystem,
7
- diagnoseRawErrorLog,
8
- PRESET_ERROR_LOGS,
9
- } from './analyzerEngine.ts';
10
- import {
11
- getAllProjectReports,
12
- getProjectReportById,
13
- applyIssueRepairOnDisk,
14
- rollbackRepairOnDisk,
15
- executeMcpToolCall,
16
- } from './repairEngine.ts';
17
-
18
- test('inspectHostEnvironment accurately reports OS platform and never fakes iOS build support on Linux', () => {
19
- const checks = inspectHostEnvironment(true);
20
- assert.ok(checks.length >= 10);
21
- const nodeCheck = checks.find((c) => c.id === 'env-node');
22
- assert.equal(nodeCheck?.status, 'working');
23
-
24
- const xcodeCheck = checks.find((c) => c.id === 'env-xcode');
25
- assert.ok(xcodeCheck);
26
- if (process.platform !== 'darwin') {
27
- assert.equal(xcodeCheck.status, 'os_unsupported');
28
- assert.ok(xcodeCheck.explanation.includes('Linux'));
29
- }
30
- });
31
-
32
- test('detectProjectEcosystem identifies React Native Expo, Bare RN, Next.js, Express, and Django fixtures', () => {
33
- const cwd = process.cwd();
34
- assert.equal(
35
- detectProjectEcosystem(path.join(cwd, 'fixtures/rn-expo-commerce')).ecosystem,
36
- 'react-native-expo'
37
- );
38
- assert.equal(
39
- detectProjectEcosystem(path.join(cwd, 'fixtures/rn-bare-fitness')).ecosystem,
40
- 'react-native-bare'
41
- );
42
- assert.equal(
43
- detectProjectEcosystem(path.join(cwd, 'fixtures/nextjs-saas-portal')).ecosystem,
44
- 'nextjs'
45
- );
46
- assert.equal(
47
- detectProjectEcosystem(path.join(cwd, 'fixtures/express-api-gateway')).ecosystem,
48
- 'node-express'
49
- );
50
- assert.equal(
51
- detectProjectEcosystem(path.join(cwd, 'fixtures/django-analytics-service')).ecosystem,
52
- 'python-django'
53
- );
54
- });
55
-
56
- test('getAllProjectReports scans all 5 multi-ecosystem fixtures and detects real engineering issues', () => {
57
- const reports = getAllProjectReports();
58
- assert.ok(reports.length >= 5);
59
-
60
- const expoReport = getProjectReportById('rn-expo-commerce', true);
61
- assert.ok(expoReport.issues.length >= 8);
62
- assert.ok(expoReport.issues.some((i) => i.ruleId === 'RN_REANIMATED_PLUGIN_MUST_BE_LAST'));
63
- assert.ok(expoReport.issues.some((i) => i.ruleId === 'ANDROID_MANIFEST_INTERNET_PERMISSION'));
64
- assert.ok(expoReport.issues.some((i) => i.ruleId === 'IOS_PODFILE_DEPLOYMENT_TARGET_AND_FLIPPER'));
65
- assert.ok(expoReport.issues.some((i) => i.ruleId === 'RN_EFFECT_EVENT_LISTENER_LEAK'));
66
- assert.ok(expoReport.issues.some((i) => i.ruleId === 'SEC_HARDCODED_SECRET_AND_HTTP'));
67
- });
68
-
69
- test('applyIssueRepairOnDisk patches real file on disk, creates backup, verifies fix, and rollbackRepairOnDisk restores original state', () => {
70
- const initial = getProjectReportById('rn-expo-commerce', true);
71
- const babelIssue = initial.issues.find((i) => i.ruleId === 'RN_REANIMATED_PLUGIN_MUST_BE_LAST');
72
- assert.ok(babelIssue, 'Expected Reanimated babel issue to be detected initially');
73
-
74
- const { repairRecord, updatedReport } = applyIssueRepairOnDisk({
75
- projectId: 'rn-expo-commerce',
76
- issueId: babelIssue.id,
77
- });
78
-
79
- assert.equal(repairRecord.verifiedAfterFix, true);
80
- assert.equal(
81
- updatedReport.issues.some((i) => i.ruleId === 'RN_REANIMATED_PLUGIN_MUST_BE_LAST'),
82
- false,
83
- 'Issue should be resolved after applying SAFE repair'
84
- );
85
-
86
- // Now roll back the repair and confirm the issue returns
87
- const { restoredRecord, updatedReport: afterRollback } = rollbackRepairOnDisk({
88
- projectId: 'rn-expo-commerce',
89
- repairId: repairRecord.id,
90
- });
91
-
92
- assert.equal(restoredRecord.rolledBack, true);
93
- assert.equal(
94
- afterRollback.issues.some((i) => i.ruleId === 'RN_REANIMATED_PLUGIN_MUST_BE_LAST'),
95
- true,
96
- 'Original file and issue should be restored after rollback'
97
- );
98
- });
99
-
100
- test('diagnoseRawErrorLog and executeMcpToolCall accurately diagnose traces and expose MCP tools', () => {
101
- for (const preset of PRESET_ERROR_LOGS) {
102
- const diag = diagnoseRawErrorLog(preset.rawLog);
103
- assert.ok(diag.confidence >= 90);
104
- assert.ok(diag.safeRepairSteps.length >= 1);
105
- }
106
-
107
- const mcpRes = executeMcpToolCall('analyze_react_native', { projectId: 'rn-expo-commerce' });
108
- assert.equal(mcpRes.tool, 'analyze_react_native');
109
- assert.equal(mcpRes.projectId, 'rn-expo-commerce');
110
- });