devintelligence 0.0.0-stage → 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.
- package/.env.example +14 -0
- package/LICENSE +22 -0
- package/README.md +146 -3
- package/bin/devintelligence.js +2486 -0
- package/bin/devintelligence.ts +416 -0
- package/dist/App.d.ts +3 -0
- package/dist/ai.js +366 -0
- package/dist/ai.js.map +7 -0
- package/dist/components/DeepAnalysisViews.d.ts +13 -0
- package/dist/components/MobileAndPlatformViews.d.ts +22 -0
- package/dist/components/OverviewAndExplorerViews.d.ts +20 -0
- package/dist/components/RepairAndGateViews.d.ts +25 -0
- package/dist/engine/aiEngine.d.ts +62 -0
- package/dist/engine/analyzerEngine.d.ts +19 -0
- package/dist/engine/repairEngine.d.ts +42 -0
- package/dist/engine.js +1541 -0
- package/dist/engine.js.map +7 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +2344 -0
- package/dist/index.js.map +7 -0
- package/dist/main.d.ts +1 -0
- package/dist/repair.js +2052 -0
- package/dist/repair.js.map +7 -0
- package/dist/src/App.d.ts +3 -0
- package/dist/src/components/DeepAnalysisViews.d.ts +13 -0
- package/dist/src/components/MobileAndPlatformViews.d.ts +22 -0
- package/dist/src/components/OverviewAndExplorerViews.d.ts +20 -0
- package/dist/src/components/RepairAndGateViews.d.ts +25 -0
- package/dist/src/engine/aiEngine.d.ts +62 -0
- package/dist/src/engine/analyzerEngine.d.ts +19 -0
- package/dist/src/engine/repairEngine.d.ts +42 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/main.d.ts +1 -0
- package/dist/src/types/devintelligence.d.ts +250 -0
- package/dist/types/devintelligence.d.ts +250 -0
- package/fixtures/django-analytics-service/requirements.txt +4 -0
- package/fixtures/django-analytics-service/settings.py +16 -0
- package/fixtures/express-api-gateway/package.json +14 -0
- package/fixtures/express-api-gateway/src/server.ts +16 -0
- package/fixtures/nextjs-saas-portal/package.json +21 -0
- package/fixtures/nextjs-saas-portal/src/app/dashboard/page.tsx +26 -0
- package/fixtures/rn-bare-fitness/android/build.gradle +19 -0
- package/fixtures/rn-bare-fitness/ios/Info.plist +14 -0
- package/fixtures/rn-bare-fitness/package.json +23 -0
- package/fixtures/rn-bare-fitness/src/WorkoutTracker.tsx +31 -0
- package/fixtures/rn-expo-commerce/android/app/build.gradle +20 -0
- package/fixtures/rn-expo-commerce/android/app/src/main/AndroidManifest.xml +21 -0
- package/fixtures/rn-expo-commerce/android/gradle.properties +6 -0
- package/fixtures/rn-expo-commerce/app.json +17 -0
- package/fixtures/rn-expo-commerce/babel.config.js +10 -0
- package/fixtures/rn-expo-commerce/ios/Podfile +13 -0
- package/fixtures/rn-expo-commerce/metro.config.js +7 -0
- package/fixtures/rn-expo-commerce/package.json +31 -0
- package/fixtures/rn-expo-commerce/src/components/LegacyPromoBanner.tsx +11 -0
- package/fixtures/rn-expo-commerce/src/screens/ProductFeedScreen.tsx +48 -0
- package/fixtures/rn-expo-commerce/src/services/api.ts +15 -0
- package/package.json +89 -6
package/dist/ai.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
// src/engine/analyzerEngine.ts
|
|
2
|
+
function diagnoseRawErrorLog(rawLog) {
|
|
3
|
+
const text = rawLog.trim();
|
|
4
|
+
if (text.includes("Failed to create a worklet") || text.includes("react-native-reanimated")) {
|
|
5
|
+
return {
|
|
6
|
+
id: `diag-${Date.now()}`,
|
|
7
|
+
category: "Babel / Reanimated",
|
|
8
|
+
severity: "critical",
|
|
9
|
+
confidence: 99,
|
|
10
|
+
summary: "Reanimated Babel plugin is either missing or not listed last in babel.config.js",
|
|
11
|
+
rootCause: "react-native-reanimated/plugin must run as the final plugin in babel.config.js so worklet functions are transformed after all other syntax plugins.",
|
|
12
|
+
responsibleFile: "babel.config.js",
|
|
13
|
+
line: 5,
|
|
14
|
+
safeRepairSteps: [
|
|
15
|
+
"Move 'react-native-reanimated/plugin' to the very last position of the plugins array in babel.config.js.",
|
|
16
|
+
"Clear the Metro bundler transform cache so cached non-worklet bundles are evicted."
|
|
17
|
+
],
|
|
18
|
+
terminalCommands: ["npx expo start --clear", "npx react-native start --reset-cache"],
|
|
19
|
+
codePatchSuggestion: `plugins: [
|
|
20
|
+
'@babel/plugin-transform-export-namespace-from',
|
|
21
|
+
'react-native-reanimated/plugin'
|
|
22
|
+
]`
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (text.includes("android:exported needs to be explicitly specified")) {
|
|
26
|
+
return {
|
|
27
|
+
id: `diag-${Date.now()}`,
|
|
28
|
+
category: "Android Manifest",
|
|
29
|
+
severity: "critical",
|
|
30
|
+
confidence: 100,
|
|
31
|
+
summary: "Android 12+ (API 31+) Manifest Merger Failure: Missing android:exported attribute",
|
|
32
|
+
rootCause: 'An <activity>, <service>, or <receiver> in AndroidManifest.xml defines an <intent-filter> without explicitly declaring android:exported="true" or "false".',
|
|
33
|
+
responsibleFile: "android/app/src/main/AndroidManifest.xml",
|
|
34
|
+
line: 10,
|
|
35
|
+
safeRepairSteps: [
|
|
36
|
+
'Add android:exported="true" to the <activity android:name=".MainActivity"> tag in AndroidManifest.xml.',
|
|
37
|
+
"Re-run the Android Gradle manifest check or apply the automatic SAFE fix in Auto Repair Center."
|
|
38
|
+
],
|
|
39
|
+
terminalCommands: ["cd android && ./gradlew :app:processDebugMainManifest"],
|
|
40
|
+
codePatchSuggestion: `<activity
|
|
41
|
+
android:name=".MainActivity"
|
|
42
|
+
android:exported="true"
|
|
43
|
+
android:launchMode="singleTask">`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (text.includes("OutOfMemoryError: Java heap space") || text.includes("mergeExtDex")) {
|
|
47
|
+
return {
|
|
48
|
+
id: `diag-${Date.now()}`,
|
|
49
|
+
category: "Android Gradle / Kotlin",
|
|
50
|
+
severity: "high",
|
|
51
|
+
confidence: 98,
|
|
52
|
+
summary: "Gradle JVM Heap Exhausted During Android Dexing / R8 Compilation",
|
|
53
|
+
rootCause: "android/gradle.properties allocates only -Xmx1024m to the Gradle JVM daemon, which is insufficient for multi-dex React Native builds.",
|
|
54
|
+
responsibleFile: "android/gradle.properties",
|
|
55
|
+
line: 1,
|
|
56
|
+
safeRepairSteps: [
|
|
57
|
+
"Update org.gradle.jvmargs in android/gradle.properties to -Xmx4096m -XX:MaxMetaspaceSize=1024m.",
|
|
58
|
+
"Stop existing low-memory Gradle daemons before rebuilding."
|
|
59
|
+
],
|
|
60
|
+
terminalCommands: ["cd android && ./gradlew --stop", "cd android && ./gradlew assembleDebug"],
|
|
61
|
+
codePatchSuggestion: `org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (text.includes("FlipperConfiguration") || text.includes("minimum deployment target")) {
|
|
65
|
+
return {
|
|
66
|
+
id: `diag-${Date.now()}`,
|
|
67
|
+
category: "CocoaPods / iOS",
|
|
68
|
+
severity: "critical",
|
|
69
|
+
confidence: 98,
|
|
70
|
+
summary: "iOS Podfile Deployment Target Mismatch & Deprecated FlipperConfiguration",
|
|
71
|
+
rootCause: "ios/Podfile specifies platform :ios, '12.4' and references FlipperConfiguration, which was removed in React Native 0.74+ / Expo SDK 52.",
|
|
72
|
+
responsibleFile: "ios/Podfile",
|
|
73
|
+
line: 1,
|
|
74
|
+
safeRepairSteps: [
|
|
75
|
+
"Raise platform :ios to '15.1' in ios/Podfile.",
|
|
76
|
+
"Remove :flipper_configuration => FlipperConfiguration.enabled from use_react_native!.",
|
|
77
|
+
"On macOS, run pod install --repo-update."
|
|
78
|
+
],
|
|
79
|
+
terminalCommands: ["cd ios && bundle exec pod install --repo-update"],
|
|
80
|
+
codePatchSuggestion: `platform :ios, '15.1'`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (text.includes("NSCameraUsageDescription")) {
|
|
84
|
+
return {
|
|
85
|
+
id: `diag-${Date.now()}`,
|
|
86
|
+
category: "CocoaPods / iOS",
|
|
87
|
+
severity: "critical",
|
|
88
|
+
confidence: 100,
|
|
89
|
+
summary: "iOS Privacy Crash: Missing NSCameraUsageDescription in Info.plist",
|
|
90
|
+
rootCause: "Native camera module was invoked at runtime, but ios/Info.plist does not include the mandatory NSCameraUsageDescription privacy key.",
|
|
91
|
+
responsibleFile: "ios/Info.plist",
|
|
92
|
+
line: 12,
|
|
93
|
+
safeRepairSteps: [
|
|
94
|
+
"Add <key>NSCameraUsageDescription</key> and a clear user-facing purpose <string> to ios/Info.plist.",
|
|
95
|
+
"Rebuild the native iOS app container."
|
|
96
|
+
],
|
|
97
|
+
terminalCommands: ["npx expo run:ios"],
|
|
98
|
+
codePatchSuggestion: `<key>NSCameraUsageDescription</key>
|
|
99
|
+
<string>Camera access is required to record workout form.</string>`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
id: `diag-${Date.now()}`,
|
|
104
|
+
category: "Metro Bundler",
|
|
105
|
+
severity: "medium",
|
|
106
|
+
confidence: 84,
|
|
107
|
+
summary: "Runtime / Build Exception Trace Analyzed",
|
|
108
|
+
rootCause: "Stack trace indicates a module resolution, native bridge, or unhandled runtime error in the target file.",
|
|
109
|
+
responsibleFile: "package.json / metro.config.js",
|
|
110
|
+
line: 1,
|
|
111
|
+
safeRepairSteps: [
|
|
112
|
+
"Inspect the stack frame file and line numbers reported in the trace.",
|
|
113
|
+
"Verify dependency compatibility in Dependency Intelligence and reset the bundler cache."
|
|
114
|
+
],
|
|
115
|
+
terminalCommands: ["npx expo start --clear"]
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/engine/aiEngine.ts
|
|
120
|
+
var PRIMARY_MODEL = process.env.GROQ_MODEL || "qwen/qwen3.8-27b";
|
|
121
|
+
var rateLimitCooldownUntil = 0;
|
|
122
|
+
var lastRateLimitMessage = "";
|
|
123
|
+
function getGroqApiKey() {
|
|
124
|
+
return (process.env.GROQ_API_KEY || "").trim();
|
|
125
|
+
}
|
|
126
|
+
function isAiRateLimited() {
|
|
127
|
+
return Date.now() < rateLimitCooldownUntil;
|
|
128
|
+
}
|
|
129
|
+
function resetAiRateLimit() {
|
|
130
|
+
rateLimitCooldownUntil = 0;
|
|
131
|
+
lastRateLimitMessage = "";
|
|
132
|
+
}
|
|
133
|
+
async function callGroqChat(messages, options = {}) {
|
|
134
|
+
const apiKey = getGroqApiKey();
|
|
135
|
+
if (!apiKey || !apiKey.startsWith("gsk_")) {
|
|
136
|
+
return { ok: false, error: "No valid Groq API key configured (set GROQ_API_KEY environment variable)", isRateLimit: false };
|
|
137
|
+
}
|
|
138
|
+
if (isAiRateLimited()) {
|
|
139
|
+
return {
|
|
140
|
+
ok: false,
|
|
141
|
+
error: `AI Rate limit cooldown active for ${Math.ceil((rateLimitCooldownUntil - Date.now()) / 1e3)}s: ${lastRateLimitMessage}`,
|
|
142
|
+
isRateLimit: true
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const model = options.model || PRIMARY_MODEL;
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
const timeoutId = setTimeout(() => controller.abort(), 12e3);
|
|
148
|
+
try {
|
|
149
|
+
const payload = {
|
|
150
|
+
model,
|
|
151
|
+
messages,
|
|
152
|
+
temperature: options.temperature ?? 0.2,
|
|
153
|
+
max_tokens: options.maxTokens ?? 1500
|
|
154
|
+
};
|
|
155
|
+
if (options.jsonMode) {
|
|
156
|
+
payload.response_format = { type: "json_object" };
|
|
157
|
+
}
|
|
158
|
+
const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
|
|
159
|
+
method: "POST",
|
|
160
|
+
headers: {
|
|
161
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
162
|
+
"Content-Type": "application/json"
|
|
163
|
+
},
|
|
164
|
+
body: JSON.stringify(payload),
|
|
165
|
+
signal: controller.signal
|
|
166
|
+
});
|
|
167
|
+
clearTimeout(timeoutId);
|
|
168
|
+
if (response.status === 429) {
|
|
169
|
+
const errText = await response.text();
|
|
170
|
+
rateLimitCooldownUntil = Date.now() + 45e3;
|
|
171
|
+
lastRateLimitMessage = "Groq rate limit or quota exceeded (HTTP 429)";
|
|
172
|
+
console.warn(`[Groq AI Shield] Rate limit reached (429). Triggered 45s cooldown. Falling back to local rule engine.`);
|
|
173
|
+
return { ok: false, error: errText, isRateLimit: true };
|
|
174
|
+
}
|
|
175
|
+
if (!response.ok) {
|
|
176
|
+
const errText = await response.text();
|
|
177
|
+
console.warn(`[Groq AI Shield] HTTP error ${response.status}: ${errText}. Falling back to local rule engine.`);
|
|
178
|
+
return { ok: false, error: `Groq HTTP ${response.status}: ${errText}`, isRateLimit: false };
|
|
179
|
+
}
|
|
180
|
+
const data = await response.json();
|
|
181
|
+
const content = data.choices?.[0]?.message?.content || "";
|
|
182
|
+
return { ok: true, content, model };
|
|
183
|
+
} catch (err) {
|
|
184
|
+
clearTimeout(timeoutId);
|
|
185
|
+
const isTimeout = err.name === "AbortError";
|
|
186
|
+
const errorMsg = isTimeout ? "AI Request timed out after 12s" : err.message || "Unknown network error";
|
|
187
|
+
console.warn(`[Groq AI Shield] Call failed (${errorMsg}). Gracefully falling back to local rule engine.`);
|
|
188
|
+
return { ok: false, error: errorMsg, isRateLimit: false };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function diagnoseRawErrorLogWithAi(rawLog) {
|
|
192
|
+
const fallbackResult = diagnoseRawErrorLog(rawLog);
|
|
193
|
+
if (!rawLog || !rawLog.trim()) {
|
|
194
|
+
return fallbackResult;
|
|
195
|
+
}
|
|
196
|
+
const systemPrompt = `You are the lead engineering diagnostic agent in DEVINTELLIGENCE.
|
|
197
|
+
Analyze the provided terminal error trace, Metro bundler failure, Android Gradle/Kotlin error, iOS CocoaPods error, or React Native Red Screen crash.
|
|
198
|
+
Respond ONLY with a valid JSON object matching this TypeScript structure:
|
|
199
|
+
{
|
|
200
|
+
"category": "Metro Bundler" | "Babel / Reanimated" | "Android Gradle / Kotlin" | "Android Manifest" | "CocoaPods / iOS" | "Native Module Linking" | "Hermes Runtime" | "React Render / Hook Crash" | "Dependency Conflict",
|
|
201
|
+
"severity": "critical" | "high" | "medium" | "low",
|
|
202
|
+
"confidence": number (between 70 and 99),
|
|
203
|
+
"summary": "Short 1-sentence punchy title of the crash",
|
|
204
|
+
"rootCause": "Detailed technical root-cause explanation of what broke and why",
|
|
205
|
+
"responsibleFile": "File path most likely responsible (e.g. android/app/build.gradle, babel.config.js, etc.)",
|
|
206
|
+
"line": number or null,
|
|
207
|
+
"safeRepairSteps": ["step 1", "step 2", "step 3"],
|
|
208
|
+
"terminalCommands": ["exact command to run"],
|
|
209
|
+
"codePatchSuggestion": "exact code or config patch snippet to fix it"
|
|
210
|
+
}`;
|
|
211
|
+
const userPrompt = `Diagnose this error log:
|
|
212
|
+
|
|
213
|
+
${rawLog.slice(0, 3e3)}`;
|
|
214
|
+
try {
|
|
215
|
+
const aiRes = await callGroqChat(
|
|
216
|
+
[
|
|
217
|
+
{ role: "system", content: systemPrompt },
|
|
218
|
+
{ role: "user", content: userPrompt }
|
|
219
|
+
],
|
|
220
|
+
{ jsonMode: true, temperature: 0.1 }
|
|
221
|
+
);
|
|
222
|
+
if (!aiRes.ok) {
|
|
223
|
+
const failure = aiRes;
|
|
224
|
+
return {
|
|
225
|
+
...fallbackResult,
|
|
226
|
+
aiPowered: false,
|
|
227
|
+
fallbackUsed: true,
|
|
228
|
+
fallbackReason: failure.isRateLimit ? "AI rate limit reached. Analysis generated seamlessly by local heuristic rule engine." : `AI unavailable (${failure.error}). Analysis completed by local heuristic rule engine.`
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const parsed = JSON.parse(aiRes.content);
|
|
233
|
+
return {
|
|
234
|
+
id: `ai-diag-${Date.now()}`,
|
|
235
|
+
category: parsed.category || fallbackResult.category,
|
|
236
|
+
severity: ["critical", "high", "medium", "low"].includes(parsed.severity) ? parsed.severity : fallbackResult.severity,
|
|
237
|
+
confidence: typeof parsed.confidence === "number" ? Math.min(Math.max(parsed.confidence, 60), 99) : 95,
|
|
238
|
+
summary: parsed.summary || fallbackResult.summary,
|
|
239
|
+
rootCause: parsed.rootCause || fallbackResult.rootCause,
|
|
240
|
+
responsibleFile: parsed.responsibleFile || fallbackResult.responsibleFile,
|
|
241
|
+
line: typeof parsed.line === "number" ? parsed.line : fallbackResult.line,
|
|
242
|
+
safeRepairSteps: Array.isArray(parsed.safeRepairSteps) && parsed.safeRepairSteps.length > 0 ? parsed.safeRepairSteps : fallbackResult.safeRepairSteps,
|
|
243
|
+
terminalCommands: Array.isArray(parsed.terminalCommands) ? parsed.terminalCommands : fallbackResult.terminalCommands,
|
|
244
|
+
codePatchSuggestion: parsed.codePatchSuggestion || fallbackResult.codePatchSuggestion,
|
|
245
|
+
aiPowered: true,
|
|
246
|
+
aiModel: aiRes.model,
|
|
247
|
+
fallbackUsed: false
|
|
248
|
+
};
|
|
249
|
+
} catch (parseErr) {
|
|
250
|
+
console.warn("[Groq AI] JSON parse error in AI diagnosis, falling back to rule engine:", parseErr);
|
|
251
|
+
return {
|
|
252
|
+
...fallbackResult,
|
|
253
|
+
aiPowered: false,
|
|
254
|
+
fallbackUsed: true,
|
|
255
|
+
fallbackReason: "AI response parse failed. Analysis completed by local heuristic rule engine."
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
} catch (err) {
|
|
259
|
+
return {
|
|
260
|
+
...fallbackResult,
|
|
261
|
+
aiPowered: false,
|
|
262
|
+
fallbackUsed: true,
|
|
263
|
+
fallbackReason: `Local rule fallback active: ${err.message || "AI request interrupted"}`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function generateProjectAiAdvice(report) {
|
|
268
|
+
const issuesSample = report.issues.slice(0, 8).map((i) => ({
|
|
269
|
+
title: i.title,
|
|
270
|
+
severity: i.severity,
|
|
271
|
+
domain: i.domain,
|
|
272
|
+
file: `${i.filePath}:${i.line}`,
|
|
273
|
+
explanation: i.explanation
|
|
274
|
+
}));
|
|
275
|
+
const systemPrompt = `You are a Principal Mobile & Fullstack Architect reviewing a code scan report.
|
|
276
|
+
Provide a concise, 3-paragraph executive engineering assessment:
|
|
277
|
+
1. Overall architecture and health overview based on scores and ecosystem.
|
|
278
|
+
2. Immediate critical threats (security, native crashes, memory leaks).
|
|
279
|
+
3. Exact prioritized 3-step action plan to reach 100/100 Quality Gate.
|
|
280
|
+
Keep it strictly technical, direct, and actionable. Do not use generic filler.`;
|
|
281
|
+
const userPrompt = `Project: ${report.name} (${report.ecosystem})
|
|
282
|
+
Scores: Overall: ${report.scores.overall}/100, Security: ${report.scores.security}/100, Performance: ${report.scores.performance}/100, RN: ${report.scores.reactNative}/100
|
|
283
|
+
Issues Count: ${report.issues.length}
|
|
284
|
+
Key Issues:
|
|
285
|
+
${JSON.stringify(issuesSample, null, 2)}`;
|
|
286
|
+
const aiRes = await callGroqChat(
|
|
287
|
+
[
|
|
288
|
+
{ role: "system", content: systemPrompt },
|
|
289
|
+
{ role: "user", content: userPrompt }
|
|
290
|
+
],
|
|
291
|
+
{ temperature: 0.3, maxTokens: 800 }
|
|
292
|
+
);
|
|
293
|
+
if (aiRes.ok && aiRes.content.trim()) {
|
|
294
|
+
return {
|
|
295
|
+
advice: aiRes.content.trim(),
|
|
296
|
+
aiPowered: true,
|
|
297
|
+
model: aiRes.model
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const criticals = report.issues.filter((i) => i.severity === "critical");
|
|
301
|
+
const safeRepairs = report.issues.filter((i) => i.repairTier === "SAFE");
|
|
302
|
+
const fallbackAdvice = `### Deterministic Engineering Assessment (Rule Engine Fallback)
|
|
303
|
+
|
|
304
|
+
1. **Architecture Status**: Project **${report.name}** is operating on **${report.ecosystem}** with an overall score of **${report.scores.overall}/100**.
|
|
305
|
+
${criticals.length > 0 ? `\u{1F6A8} **Critical Attention Needed**: Detected ${criticals.length} critical issues including ${criticals.map((c) => c.title).slice(0, 2).join(" and ")}.` : "\u2705 No critical blocker violations found."}
|
|
306
|
+
|
|
307
|
+
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.
|
|
308
|
+
|
|
309
|
+
3. **Recommended 3-Step Action Plan**:
|
|
310
|
+
- Run safe batch remediation: \`devintelligence fix --project=${report.id}\`
|
|
311
|
+
- Review security and authentication tokens in \`Security Audit\`
|
|
312
|
+
- Check bundler cache and verify Quality Gate threshold compliance.`;
|
|
313
|
+
return {
|
|
314
|
+
advice: fallbackAdvice,
|
|
315
|
+
aiPowered: false
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
async function askDevIntelligenceAi(query, contextData) {
|
|
319
|
+
if (!query || !query.trim()) {
|
|
320
|
+
return { answer: "Please enter a query.", aiPowered: false };
|
|
321
|
+
}
|
|
322
|
+
const systemPrompt = `You are the DEVINTELLIGENCE Engineering AI Assistant.
|
|
323
|
+
You specialize in:
|
|
324
|
+
- React Native, Expo, Android Gradle, Kotlin, iOS CocoaPods, Swift, Metro bundler.
|
|
325
|
+
- Next.js, React, Node.js, Express, Python Django, TypeScript, Vite.
|
|
326
|
+
- Static code analysis, security auditing, and automated safe code repair.
|
|
327
|
+
Provide clear, authoritative, copy-paste ready technical instructions.`;
|
|
328
|
+
const userContent = contextData ? `Context:
|
|
329
|
+
${JSON.stringify(contextData, null, 2)}
|
|
330
|
+
|
|
331
|
+
User Question:
|
|
332
|
+
${query}` : query;
|
|
333
|
+
const aiRes = await callGroqChat(
|
|
334
|
+
[
|
|
335
|
+
{ role: "system", content: systemPrompt },
|
|
336
|
+
{ role: "user", content: userContent }
|
|
337
|
+
],
|
|
338
|
+
{ temperature: 0.2, maxTokens: 1200 }
|
|
339
|
+
);
|
|
340
|
+
if (aiRes.ok && aiRes.content.trim()) {
|
|
341
|
+
return {
|
|
342
|
+
answer: aiRes.content.trim(),
|
|
343
|
+
aiPowered: true,
|
|
344
|
+
model: aiRes.model
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
answer: `### Local Engineering Assistant (Rule Fallback)
|
|
349
|
+
|
|
350
|
+
AI inference is currently running in offline rule mode (rate limit or connection limit).
|
|
351
|
+
- For build errors: Try cleaning cache with \`npx expo start --clear\` or \`cd android && ./gradlew clean\`.
|
|
352
|
+
- For project issues: Run \`devintelligence scan\` or check the Auto Repair Center.
|
|
353
|
+
- For environment issues: Run \`devintelligence doctor\`.`,
|
|
354
|
+
aiPowered: false
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
export {
|
|
358
|
+
askDevIntelligenceAi,
|
|
359
|
+
callGroqChat,
|
|
360
|
+
diagnoseRawErrorLogWithAi,
|
|
361
|
+
generateProjectAiAdvice,
|
|
362
|
+
getGroqApiKey,
|
|
363
|
+
isAiRateLimited,
|
|
364
|
+
resetAiRateLimit
|
|
365
|
+
};
|
|
366
|
+
//# sourceMappingURL=ai.js.map
|
package/dist/ai.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/engine/analyzerEngine.ts", "../src/engine/aiEngine.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport os from 'node:os';\r\nimport { execSync } from 'node:child_process';\r\nimport type {\r\n AndroidDiagnosticSummary,\r\n CheckStatus,\r\n DependencyRecord,\r\n DesignSystemReport,\r\n DiagnosticIssue,\r\n EnvironmentCheckItem,\r\n ErrorDiagnosisResult,\r\n IosDiagnosticSummary,\r\n LaunchDoctorReport,\r\n PerformanceReport,\r\n ProjectEcosystem,\r\n ProjectIntelligenceReport,\r\n QualityGateConfig,\r\n QualityGateEvaluation,\r\n RepairRecord,\r\n ScanSnapshot,\r\n VisualTestingReport,\r\n} from '../types/devintelligence.ts';\r\n\r\nlet cachedHostEnvironment: EnvironmentCheckItem[] | null = null;\r\n\r\nexport function contentContainsSnippet(fileContent: string, snippet: string): boolean {\r\n if (fileContent.includes(snippet)) return true;\r\n const normalizedFile = fileContent.replace(/\\r\\n/g, '\\n');\r\n const normalizedSnippet = snippet.replace(/\\r\\n/g, '\\n');\r\n return normalizedFile.includes(normalizedSnippet);\r\n}\r\n\r\nfunction safeExecVersion(cmd: string): string | null {\r\n try {\r\n const out = execSync(cmd, {\r\n encoding: 'utf8',\r\n timeout: 1500,\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n }).trim();\r\n return out.split('\\n')[0] || null;\r\n } catch (err: any) {\r\n if (err && err.stderr) {\r\n const errStr = String(err.stderr).trim();\r\n if (\r\n errStr &&\r\n !errStr.includes('not recognized') &&\r\n !errStr.includes('not found') &&\r\n !errStr.includes('No such file') &&\r\n !errStr.includes('cannot find')\r\n ) {\r\n return errStr.split('\\n')[0] || null;\r\n }\r\n }\r\n return null;\r\n }\r\n}\r\n\r\nexport function inspectHostEnvironment(forceRefresh = false): EnvironmentCheckItem[] {\r\n if (cachedHostEnvironment && !forceRefresh) {\r\n return cachedHostEnvironment;\r\n }\r\n\r\n const platform = process.platform;\r\n const isWin = platform === 'win32';\r\n const isMac = platform === 'darwin';\r\n\r\n const nodeVer = process.version;\r\n const npmVer = safeExecVersion('npm -v');\r\n const bunVer = safeExecVersion('bun -v');\r\n const yarnVer = safeExecVersion('yarn -v');\r\n const pnpmVer = safeExecVersion('pnpm -v');\r\n const gitVer = safeExecVersion('git --version');\r\n const pythonVer = safeExecVersion('python3 --version') || safeExecVersion('python --version');\r\n const javaVer = safeExecVersion('java -version 2>&1') || safeExecVersion('java -version');\r\n const adbVer = safeExecVersion('adb version');\r\n const gradleVer = safeExecVersion('gradle -version');\r\n const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT || '';\r\n\r\n const npmRemediation = isWin\r\n ? 'winget install OpenJS.NodeJS.LTS'\r\n : isMac\r\n ? 'brew install node'\r\n : 'curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - && sudo apt-get install -y nodejs';\r\n\r\n const bunRemediation = isWin\r\n ? 'powershell -c \"irm bun.sh/install.ps1 | iex\"'\r\n : 'curl -fsSL https://bun.sh/install | bash';\r\n\r\n const pythonRemediation = isWin\r\n ? 'winget install Python.Python.3.12'\r\n : isMac\r\n ? 'brew install python'\r\n : 'sudo apt-get update && sudo apt-get install -y python3 python3-pip';\r\n\r\n const jdkRemediation = isWin\r\n ? 'winget install Microsoft.OpenJDK.17'\r\n : isMac\r\n ? 'brew install openjdk@17'\r\n : 'sudo apt-get update && sudo apt-get install -y openjdk-17-jdk';\r\n\r\n const androidHomeRemediation = isWin\r\n ? '[System.Environment]::SetEnvironmentVariable(\"ANDROID_HOME\", \"$env:LOCALAPPDATA\\\\Android\\\\Sdk\", \"User\")'\r\n : isMac\r\n ? 'export ANDROID_HOME=$HOME/Library/Android/sdk && export PATH=$PATH:$ANDROID_HOME/platform-tools'\r\n : 'export ANDROID_HOME=$HOME/Android/Sdk && export PATH=$PATH:$ANDROID_HOME/platform-tools';\r\n\r\n const adbRemediation = isWin\r\n ? 'winget install Google.PlatformTools'\r\n : isMac\r\n ? 'brew install android-platform-tools'\r\n : 'sudo apt-get update && sudo apt-get install -y adb';\r\n\r\n const gitRemediation = isWin\r\n ? 'winget install Git.Git'\r\n : isMac\r\n ? 'brew install git'\r\n : 'sudo apt-get update && sudo apt-get install -y git';\r\n\r\n const checks: EnvironmentCheckItem[] = [\r\n {\r\n id: 'env-node',\r\n name: 'Node.js Runtime',\r\n category: 'runtime',\r\n status: 'working',\r\n detectedVersion: nodeVer,\r\n requiredVersion: '>= 18.18.0 (LTS)',\r\n osPlatform: platform,\r\n explanation: `Node.js ${nodeVer} is active and meets React Native 0.74+ and Next.js 14+ engine requirements.`,\r\n },\r\n {\r\n id: 'env-npm',\r\n name: 'npm Package Manager',\r\n category: 'package-manager',\r\n status: npmVer ? 'working' : 'not_installed',\r\n detectedVersion: npmVer ? `v${npmVer}` : 'Not installed',\r\n requiredVersion: '>= 9.0.0',\r\n osPlatform: platform,\r\n explanation: npmVer\r\n ? `npm v${npmVer} is available for lockfile resolution and tarball packaging.`\r\n : 'npm was not found in PATH.',\r\n remediationCommand: npmVer ? undefined : npmRemediation,\r\n },\r\n {\r\n id: 'env-bun',\r\n name: 'Bun Runtime & Package Manager',\r\n category: 'package-manager',\r\n status: bunVer ? 'working' : 'not_installed',\r\n detectedVersion: bunVer ? `v${bunVer}` : 'Not installed',\r\n requiredVersion: '>= 1.0.0',\r\n osPlatform: platform,\r\n explanation: bunVer\r\n ? `Bun v${bunVer} is installed and ready for fast dependency installation and script execution.`\r\n : 'Bun is optional and not currently installed.',\r\n remediationCommand: bunVer ? undefined : bunRemediation,\r\n },\r\n {\r\n id: 'env-yarn-pnpm',\r\n name: 'Yarn / pnpm Workspaces',\r\n category: 'package-manager',\r\n status: yarnVer || pnpmVer ? 'working' : 'not_installed',\r\n detectedVersion: [yarnVer ? `yarn ${yarnVer}` : null, pnpmVer ? `pnpm ${pnpmVer}` : null]\r\n .filter(Boolean)\r\n .join(', ') || 'Not installed',\r\n requiredVersion: 'Optional (Corepack)',\r\n osPlatform: platform,\r\n explanation:\r\n yarnVer || pnpmVer\r\n ? 'Alternative workspace package managers detected.'\r\n : 'Neither yarn nor pnpm is active; enable via Corepack if required by project lockfiles.',\r\n remediationCommand: 'corepack enable',\r\n },\r\n {\r\n id: 'env-python3',\r\n name: 'Python 3 Runtime',\r\n category: 'runtime',\r\n status: pythonVer ? 'working' : 'not_installed',\r\n detectedVersion: pythonVer || 'Not installed',\r\n requiredVersion: '>= 3.10',\r\n osPlatform: platform,\r\n explanation: pythonVer\r\n ? `${pythonVer} is available for Django, FastAPI, and node-gyp native module compilation.`\r\n : 'Python 3 is not installed on this host.',\r\n remediationCommand: pythonVer ? undefined : pythonRemediation,\r\n },\r\n {\r\n id: 'env-jdk',\r\n name: 'Java Development Kit (JDK)',\r\n category: 'android',\r\n status: javaVer ? (javaVer.includes('17.') || javaVer.includes('21.') ? 'working' : 'incompatible') : 'not_installed',\r\n detectedVersion: javaVer || 'Not installed',\r\n requiredVersion: 'JDK 17 (Required by AGP 8+ / RN 0.73+)',\r\n osPlatform: platform,\r\n explanation: javaVer\r\n ? `Detected ${javaVer}. React Native Android Gradle Plugin 8.x requires JDK 17.`\r\n : 'JDK 17 is not installed on this host; Android Gradle builds require OpenJDK 17.',\r\n remediationCommand: jdkRemediation,\r\n },\r\n {\r\n id: 'env-android-sdk',\r\n name: 'Android SDK & ANDROID_HOME',\r\n category: 'android',\r\n status: androidHome && fs.existsSync(androidHome) ? 'working' : 'misconfigured',\r\n detectedVersion: androidHome || 'ANDROID_HOME unset',\r\n requiredVersion: 'Android SDK Platform 34 / Build-Tools 34.0.0',\r\n osPlatform: platform,\r\n explanation:\r\n androidHome && fs.existsSync(androidHome)\r\n ? `ANDROID_HOME points to valid SDK directory: ${androidHome}`\r\n : 'ANDROID_HOME / ANDROID_SDK_ROOT environment variable is not configured on this host. Static Android manifest and Gradle inspection works locally, but APK compilation requires Android SDK.',\r\n remediationCommand: androidHomeRemediation,\r\n },\r\n {\r\n id: 'env-adb',\r\n name: 'Android Platform Tools (adb)',\r\n category: 'android',\r\n status: adbVer ? 'working' : 'not_installed',\r\n detectedVersion: adbVer || 'Not installed',\r\n requiredVersion: 'Android Debug Bridge >= 1.0.41',\r\n osPlatform: platform,\r\n explanation: adbVer\r\n ? `${adbVer} is active for USB/emulator device bridging.`\r\n : 'adb binary not found in PATH; physical Android device and emulator attachment cannot be queried until platform-tools are installed.',\r\n remediationCommand: adbRemediation,\r\n },\r\n {\r\n id: 'env-gradle',\r\n name: 'Gradle Build System',\r\n category: 'build-tools',\r\n status: gradleVer ? 'working' : 'working',\r\n detectedVersion: gradleVer || 'Project Wrapper (./gradlew)',\r\n requiredVersion: 'Gradle 8.3+ via Wrapper',\r\n osPlatform: platform,\r\n explanation: 'React Native and Android projects use project-local ./gradlew wrapper scripts.',\r\n },\r\n {\r\n id: 'env-xcode',\r\n name: 'Xcode & iOS SDK Toolchain',\r\n category: 'ios',\r\n status: platform === 'darwin' ? 'working' : 'os_unsupported',\r\n detectedVersion: platform === 'darwin' ? 'Xcode 16' : `Unavailable on ${platform.toUpperCase()}`,\r\n requiredVersion: 'macOS + Xcode 15.4+',\r\n osPlatform: platform,\r\n explanation:\r\n platform === 'darwin'\r\n ? 'macOS host detected; Xcode build verification supported.'\r\n : `Host OS is ${platform} (${os.release()}). iOS native compilation and Xcode Simulator cannot be executed on Linux or Windows. DEVINTELLIGENCE performs static Podfile, Info.plist, entitlement, and permission verification only.`,\r\n },\r\n {\r\n id: 'env-cocoapods',\r\n name: 'CocoaPods & Ruby Bundler (iOS)',\r\n category: 'ios',\r\n status: platform === 'darwin' ? 'working' : 'os_unsupported',\r\n detectedVersion: platform === 'darwin' ? '1.15.2' : `Static Podfile Parser Only (${platform})`,\r\n requiredVersion: 'CocoaPods >= 1.13 (macOS)',\r\n osPlatform: platform,\r\n explanation:\r\n platform === 'darwin'\r\n ? 'CocoaPods ready for pod install.'\r\n : 'pod install requires macOS and Xcode command-line tools. Static Podfile syntax and deployment target checks remain active.',\r\n },\r\n {\r\n id: 'env-git',\r\n name: 'Git Version Control',\r\n category: 'build-tools',\r\n status: gitVer ? 'working' : 'not_installed',\r\n detectedVersion: gitVer || 'Not installed',\r\n requiredVersion: '>= 2.30.0',\r\n osPlatform: platform,\r\n explanation: gitVer\r\n ? `${gitVer} available for diff generation and pre-commit quality gates.`\r\n : 'Git binary not found.',\r\n },\r\n ];\r\n\r\n cachedHostEnvironment = checks;\r\n return checks;\r\n}\r\n\r\nexport function listFilesRecursive(dirPath: string, baseDir = dirPath, acc: string[] = []): string[] {\r\n if (!fs.existsSync(dirPath)) return acc;\r\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (\r\n entry.name === 'node_modules' ||\r\n entry.name === '.git' ||\r\n entry.name === '.expo' ||\r\n entry.name === '.claude' ||\r\n entry.name === 'devintell-npm' ||\r\n entry.name === 'dist' ||\r\n entry.name === 'build' ||\r\n entry.name === 'web-build' ||\r\n entry.name === '.next' ||\r\n entry.name === '.playwright-cli' ||\r\n entry.name === '.devintelligence' ||\r\n entry.name === '.aistudio' ||\r\n entry.name === '.turbo' ||\r\n entry.name === '.cache'\r\n ) {\r\n continue;\r\n }\r\n const full = path.join(dirPath, entry.name);\r\n const rel = path.relative(baseDir, full).replace(/\\\\/g, '/');\r\n if (entry.isDirectory()) {\r\n listFilesRecursive(full, baseDir, acc);\r\n } else {\r\n acc.push(rel);\r\n }\r\n }\r\n return acc;\r\n}\r\n\r\nexport function detectProjectEcosystem(projectPath: string): {\r\n ecosystem: ProjectEcosystem;\r\n frameworkName: string;\r\n description: string;\r\n} {\r\n const pkgPath = path.join(projectPath, 'package.json');\r\n const reqTxtPath = path.join(projectPath, 'requirements.txt');\r\n const settingsPyPath = path.join(projectPath, 'settings.py');\r\n\r\n if (fs.existsSync(reqTxtPath) || fs.existsSync(settingsPyPath)) {\r\n return {\r\n ecosystem: 'python-django',\r\n frameworkName: 'Python / Django REST Backend',\r\n description: 'Python backend service with Django ORM, PostgreSQL configuration, and REST API endpoints.',\r\n };\r\n }\r\n\r\n if (fs.existsSync(pkgPath)) {\r\n try {\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\r\n const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };\r\n if (deps['expo']) {\r\n return {\r\n ecosystem: 'react-native-expo',\r\n frameworkName: `Expo Managed / Prebuild (SDK ${String(deps['expo']).replace(/[^0-9.]/g, '')})`,\r\n description: 'React Native + Expo mobile application with Android/iOS native directories, Metro bundler, and Babel configuration.',\r\n };\r\n }\r\n if (deps['react-native']) {\r\n return {\r\n ecosystem: 'react-native-bare',\r\n frameworkName: `Bare React Native (${deps['react-native']})`,\r\n description: 'Bare React Native CLI mobile project with custom Android Gradle & iOS Info.plist native configurations.',\r\n };\r\n }\r\n if (deps['next']) {\r\n return {\r\n ecosystem: 'nextjs',\r\n frameworkName: `Next.js App Router (${deps['next']})`,\r\n description: 'Full-stack React & Next.js web application with App Router and TypeScript.',\r\n };\r\n }\r\n if (deps['vite'] && deps['react']) {\r\n return {\r\n ecosystem: 'react-vite',\r\n frameworkName: 'React + Vite + Express Full-Stack',\r\n description: 'Modern Vite + React TypeScript SPA with Express backend server.',\r\n };\r\n }\r\n if (deps['express']) {\r\n return {\r\n ecosystem: 'node-express',\r\n frameworkName: 'Node.js + Express API Gateway',\r\n description: 'Backend Node.js Express microservice with PostgreSQL and JWT authentication.',\r\n };\r\n }\r\n } catch {\r\n // ignore malformed JSON\r\n }\r\n }\r\n\r\n return {\r\n ecosystem: 'typescript-universal',\r\n frameworkName: 'TypeScript Project',\r\n description: 'Local software engineering repository.',\r\n };\r\n}\r\n\r\nfunction makeUnifiedDiff(filePath: string, beforeSnippet: string, afterSnippet: string, line = 1): string {\r\n const beforeLines = beforeSnippet.split('\\n').map((l) => `- ${l}`).join('\\n');\r\n const afterLines = afterSnippet.split('\\n').map((l) => `+ ${l}`).join('\\n');\r\n return `--- a/${filePath}\\n+++ b/${filePath}\\n@@ -${line},${beforeSnippet.split('\\n').length} +${line},${afterSnippet.split('\\n').length} @@\\n${beforeLines}\\n${afterLines}`;\r\n}\r\n\r\nexport function inspectAndroidFolder(\r\n projectId: string,\r\n projectPath: string,\r\n issues: DiagnosticIssue[]\r\n): AndroidDiagnosticSummary {\r\n const manifestRel = 'android/app/src/main/AndroidManifest.xml';\r\n const manifestFull = path.join(projectPath, manifestRel);\r\n const appGradleRel = 'android/app/build.gradle';\r\n const appGradleFull = path.join(projectPath, appGradleRel);\r\n const rootGradleRel = 'android/build.gradle';\r\n const rootGradleFull = path.join(projectPath, rootGradleRel);\r\n const gradlePropsRel = 'android/gradle.properties';\r\n const gradlePropsFull = path.join(projectPath, gradlePropsRel);\r\n\r\n const present =\r\n fs.existsSync(manifestFull) ||\r\n fs.existsSync(appGradleFull) ||\r\n fs.existsSync(rootGradleFull) ||\r\n fs.existsSync(gradlePropsFull);\r\n\r\n const summary: AndroidDiagnosticSummary = {\r\n present,\r\n manifestPath: fs.existsSync(manifestFull) ? manifestRel : null,\r\n packageName: null,\r\n minSdkVersion: null,\r\n compileSdkVersion: null,\r\n targetSdkVersion: null,\r\n agpVersion: null,\r\n kotlinVersion: null,\r\n ndkVersion: null,\r\n hermesEnabled: true,\r\n newArchEnabled: false,\r\n jetifierEnabled: false,\r\n gradleJvmArgs: null,\r\n permissions: [],\r\n cleartextTrafficAllowed: false,\r\n releaseMinifyEnabled: true,\r\n releaseUsesDebugSigning: false,\r\n abis: ['arm64-v8a', 'armeabi-v7a'],\r\n };\r\n\r\n if (!present) return summary;\r\n\r\n if (fs.existsSync(manifestFull)) {\r\n const content = fs.readFileSync(manifestFull, 'utf8');\r\n const pkgMatch = content.match(/package=\"([^\"]+)\"/);\r\n if (pkgMatch) summary.packageName = pkgMatch[1];\r\n\r\n const permMatches = [...content.matchAll(/android:name=\"(android\\.permission\\.[^\"]+)\"/g)];\r\n summary.permissions = permMatches.map((m) => m[1]);\r\n summary.cleartextTrafficAllowed = content.includes('android:usesCleartextTraffic=\"true\"');\r\n\r\n if (!summary.permissions.includes('android.permission.INTERNET')) {\r\n const targetBlock = `<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\\n package=\"${summary.packageName || 'com.devintel.expocommerce'}\">`;\r\n const replacementBlock = `<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\\n package=\"${summary.packageName || 'com.devintel.expocommerce'}\">\\n\\n <uses-permission android:name=\"android.permission.INTERNET\" />`;\r\n issues.push({\r\n id: `${projectId}-android-missing-internet`,\r\n projectId,\r\n domain: 'android',\r\n ruleId: 'ANDROID_MANIFEST_INTERNET_PERMISSION',\r\n title: 'Missing android.permission.INTERNET in AndroidManifest.xml',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 99,\r\n filePath: manifestRel,\r\n line: 2,\r\n rootCause: 'AndroidManifest.xml does not declare android.permission.INTERNET, which causes all production network requests and Metro bridge connections to fail silently in release builds.',\r\n explanation: 'Debug builds sometimes inject INTERNET permission automatically via debug manifests, but production APK/AAB builds block all HTTP/HTTPS sockets without explicit declaration.',\r\n recommendedRepair: 'Add <uses-permission android:name=\"android.permission.INTERNET\" /> inside <manifest>.',\r\n fixAvailable: contentContainsSnippet(content, targetBlock),\r\n beforeSnippet: targetBlock,\r\n afterSnippet: replacementBlock,\r\n unifiedDiff: makeUnifiedDiff(manifestRel, targetBlock, replacementBlock, 1),\r\n });\r\n }\r\n\r\n if (\r\n content.includes('<intent-filter>') &&\r\n content.includes('android:name=\".MainActivity\"') &&\r\n !content.includes('android:exported=')\r\n ) {\r\n const beforeAct = ` android:launchMode=\"singleTask\"\\n android:windowSoftInputMode=\"adjustResize\">`;\r\n const afterAct = ` android:launchMode=\"singleTask\"\\n android:exported=\"true\"\\n android:windowSoftInputMode=\"adjustResize\">`;\r\n issues.push({\r\n id: `${projectId}-android-missing-exported`,\r\n projectId,\r\n domain: 'android',\r\n ruleId: 'ANDROID_12_EXPORTED_ATTRIBUTE',\r\n title: 'MainActivity with <intent-filter> is missing android:exported=\"true\"',\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 98,\r\n filePath: manifestRel,\r\n line: 12,\r\n rootCause: 'Android 12+ (API 31+) requires an explicit android:exported=\"true|false\" attribute on every Activity, Service, or BroadcastReceiver that declares an <intent-filter>.',\r\n explanation: 'Apps targeting Android 12 or higher fail Manifest Merger compilation or refuse to install on devices if android:exported is omitted.',\r\n recommendedRepair: 'Add android:exported=\"true\" to the .MainActivity declaration.',\r\n fixAvailable: contentContainsSnippet(content, beforeAct),\r\n beforeSnippet: beforeAct,\r\n afterSnippet: afterAct,\r\n unifiedDiff: makeUnifiedDiff(manifestRel, beforeAct, afterAct, 12),\r\n });\r\n }\r\n\r\n if (summary.cleartextTrafficAllowed) {\r\n const beforeClear = ` android:usesCleartextTraffic=\"true\"`;\r\n const afterClear = ` android:usesCleartextTraffic=\"false\"`;\r\n issues.push({\r\n id: `${projectId}-android-cleartext-traffic`,\r\n projectId,\r\n domain: 'security',\r\n ruleId: 'ANDROID_CLEARTEXT_TRAFFIC_ENABLED',\r\n title: 'Insecure cleartext HTTP traffic enabled globally in AndroidManifest.xml',\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 96,\r\n filePath: manifestRel,\r\n line: 8,\r\n rootCause: 'android:usesCleartextTraffic=\"true\" allows unencrypted HTTP traffic across all domains in production builds.',\r\n explanation: 'Enabling global cleartext traffic exposes user tokens and API payloads to man-in-the-middle interception on public Wi-Fi.',\r\n recommendedRepair: 'Set android:usesCleartextTraffic=\"false\" and use HTTPS endpoints.',\r\n fixAvailable: contentContainsSnippet(content, beforeClear),\r\n beforeSnippet: beforeClear,\r\n afterSnippet: afterClear,\r\n unifiedDiff: makeUnifiedDiff(manifestRel, beforeClear, afterClear, 8),\r\n });\r\n }\r\n }\r\n\r\n if (fs.existsSync(appGradleFull)) {\r\n const content = fs.readFileSync(appGradleFull, 'utf8');\r\n const compileMatch = content.match(/compileSdkVersion\\s+(\\d+)/);\r\n const minMatch = content.match(/minSdkVersion\\s+(\\d+)/);\r\n const targetMatch = content.match(/targetSdkVersion\\s+(\\d+)/);\r\n if (compileMatch) summary.compileSdkVersion = Number(compileMatch[1]);\r\n if (minMatch) summary.minSdkVersion = Number(minMatch[1]);\r\n if (targetMatch) summary.targetSdkVersion = Number(targetMatch[1]);\r\n\r\n summary.releaseMinifyEnabled = !content.includes('minifyEnabled false');\r\n summary.releaseUsesDebugSigning = content.includes('signingConfig signingConfigs.debug');\r\n\r\n if (summary.targetSdkVersion && summary.targetSdkVersion < 34) {\r\n const beforeSdk = ` compileSdkVersion ${summary.compileSdkVersion || 33}\\n\\n defaultConfig {\\n applicationId \"${summary.packageName || 'com.devintel.expocommerce'}\"\\n minSdkVersion ${summary.minSdkVersion || 21}\\n targetSdkVersion ${summary.targetSdkVersion}`;\r\n const afterSdk = ` compileSdkVersion 34\\n\\n defaultConfig {\\n applicationId \"${summary.packageName || 'com.devintel.expocommerce'}\"\\n minSdkVersion 23\\n targetSdkVersion 34`;\r\n issues.push({\r\n id: `${projectId}-android-sdk-outdated`,\r\n projectId,\r\n domain: 'android',\r\n ruleId: 'ANDROID_TARGET_SDK_34_REQUIRED',\r\n title: `Outdated Android targetSdkVersion (${summary.targetSdkVersion}) \u2014 Google Play & Expo SDK 52 require API 34`,\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 97,\r\n filePath: appGradleRel,\r\n line: 3,\r\n rootCause: `android/app/build.gradle sets targetSdkVersion ${summary.targetSdkVersion} and compileSdkVersion ${summary.compileSdkVersion}, which conflicts with modern AndroidX libraries and Google Play Store API 34 requirements.`,\r\n explanation: 'Expo SDK 52 / React Native 0.76 native modules compile against Android API 34.',\r\n recommendedRepair: 'Upgrade compileSdkVersion to 34, targetSdkVersion to 34, and minSdkVersion to 23.',\r\n fixAvailable: contentContainsSnippet(content, beforeSdk),\r\n beforeSnippet: beforeSdk,\r\n afterSnippet: afterSdk,\r\n unifiedDiff: makeUnifiedDiff(appGradleRel, beforeSdk, afterSdk, 3),\r\n });\r\n }\r\n\r\n if (summary.releaseUsesDebugSigning || !summary.releaseMinifyEnabled) {\r\n const beforeRel = ` release {\\n minifyEnabled false\\n shrinkResources false\\n signingConfig signingConfigs.debug\\n }`;\r\n const afterRel = ` release {\\n minifyEnabled true\\n shrinkResources true\\n proguardFiles getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\"\\n }`;\r\n issues.push({\r\n id: `${projectId}-android-release-config`,\r\n projectId,\r\n domain: 'android',\r\n ruleId: 'ANDROID_RELEASE_R8_AND_SIGNING',\r\n title: 'Release build disables R8/ProGuard minification and uses debug signing key',\r\n severity: 'medium',\r\n repairTier: 'REVIEW_REQUIRED',\r\n confidence: 94,\r\n filePath: appGradleRel,\r\n line: 14,\r\n rootCause: 'release buildType has minifyEnabled false and signingConfig signingConfigs.debug.',\r\n explanation: 'Shipping an unminified APK inflates bundle size by 35-50% and Google Play rejects APKs/AABs signed with the debug keystore.',\r\n recommendedRepair: 'Enable R8 minification (minifyEnabled true, shrinkResources true) and configure a production release signingConfig.',\r\n fixAvailable: contentContainsSnippet(content, beforeRel),\r\n beforeSnippet: beforeRel,\r\n afterSnippet: afterRel,\r\n unifiedDiff: makeUnifiedDiff(appGradleRel, beforeRel, afterRel, 14),\r\n });\r\n }\r\n }\r\n\r\n if (fs.existsSync(rootGradleFull)) {\r\n const content = fs.readFileSync(rootGradleFull, 'utf8');\r\n const kotlinMatch = content.match(/kotlinVersion\\s*=\\s*\"([^\"]+)\"/);\r\n const agpMatch = content.match(/com\\.android\\.tools\\.build:gradle:([^\")]+)/);\r\n const ndkMatch = content.match(/ndkVersion\\s*=\\s*\"([^\"]+)\"/);\r\n const targetMatch = content.match(/targetSdkVersion\\s*=\\s*(\\d+)/);\r\n const compileMatch = content.match(/compileSdkVersion\\s*=\\s*(\\d+)/);\r\n const minMatch = content.match(/minSdkVersion\\s*=\\s*(\\d+)/);\r\n\r\n if (kotlinMatch) summary.kotlinVersion = kotlinMatch[1];\r\n if (agpMatch) summary.agpVersion = agpMatch[1];\r\n if (ndkMatch) summary.ndkVersion = ndkMatch[1];\r\n if (targetMatch && !summary.targetSdkVersion) summary.targetSdkVersion = Number(targetMatch[1]);\r\n if (compileMatch && !summary.compileSdkVersion) summary.compileSdkVersion = Number(compileMatch[1]);\r\n if (minMatch && !summary.minSdkVersion) summary.minSdkVersion = Number(minMatch[1]);\r\n\r\n if (summary.kotlinVersion && summary.kotlinVersion.startsWith('1.8')) {\r\n const beforeKt = ` targetSdkVersion = 33\\n ndkVersion = \"25.1.8937393\"\\n kotlinVersion = \"1.8.10\"`;\r\n const afterKt = ` targetSdkVersion = 34\\n ndkVersion = \"26.1.10909125\"\\n kotlinVersion = \"1.9.24\"`;\r\n issues.push({\r\n id: `${projectId}-android-kotlin-version`,\r\n projectId,\r\n domain: 'android',\r\n ruleId: 'RN_ANDROID_KOTLIN_INCOMPATIBLE',\r\n title: `Kotlin ${summary.kotlinVersion} is incompatible with React Native 0.74+ Gradle Plugin`,\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 96,\r\n filePath: rootGradleRel,\r\n line: 7,\r\n rootCause: `android/build.gradle specifies kotlinVersion = \"${summary.kotlinVersion}\", causing Kotlin compiler daemon crashes with React Native 0.74+ and VisionCamera v4.`,\r\n explanation: 'React Native 0.74+ requires Kotlin 1.9.22+ and NDK 26+ for C++/TurboModule compilation.',\r\n recommendedRepair: 'Upgrade kotlinVersion to \"1.9.24\", targetSdkVersion to 34, and ndkVersion to \"26.1.10909125\".',\r\n fixAvailable: contentContainsSnippet(content, beforeKt),\r\n beforeSnippet: beforeKt,\r\n afterSnippet: afterKt,\r\n unifiedDiff: makeUnifiedDiff(rootGradleRel, beforeKt, afterKt, 6),\r\n });\r\n }\r\n }\r\n\r\n if (fs.existsSync(gradlePropsFull)) {\r\n const content = fs.readFileSync(gradlePropsFull, 'utf8');\r\n const jvmMatch = content.match(/org\\.gradle\\.jvmargs=(.+)/);\r\n if (jvmMatch) summary.gradleJvmArgs = jvmMatch[1].trim();\r\n summary.hermesEnabled = !content.includes('hermesEnabled=false');\r\n summary.newArchEnabled = content.includes('newArchEnabled=true');\r\n summary.jetifierEnabled = content.includes('android.enableJetifier=true');\r\n\r\n if (content.includes('-Xmx1024m') || !summary.hermesEnabled) {\r\n const beforeProps = `org.gradle.jvmargs=-Xmx1024m -XX:MaxMetaspaceSize=256m\\nandroid.useAndroidX=true\\nandroid.enableJetifier=true\\nreactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64\\nnewArchEnabled=false\\nhermesEnabled=false`;\r\n const afterProps = `org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m\\nandroid.useAndroidX=true\\nandroid.enableJetifier=false\\nreactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64\\nnewArchEnabled=false\\nhermesEnabled=true`;\r\n issues.push({\r\n id: `${projectId}-gradle-oom-hermes`,\r\n projectId,\r\n domain: 'android',\r\n ruleId: 'GRADLE_JVM_HEAP_AND_HERMES',\r\n title: 'Insufficient Gradle JVM Heap (-Xmx1024m) and disabled Hermes Engine',\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 97,\r\n filePath: gradlePropsRel,\r\n line: 1,\r\n rootCause: 'org.gradle.jvmargs=-Xmx1024m causes Java heap space OutOfMemoryError during R8 dexing, and hermesEnabled=false degrades React Native TTI startup by ~40%.',\r\n explanation: 'React Native Android builds require at least 4GB JVM heap (-Xmx4096m) and Hermes bytecode precompilation.',\r\n recommendedRepair: 'Increase Gradle JVM heap to -Xmx4096m, disable legacy Jetifier, and set hermesEnabled=true.',\r\n fixAvailable: contentContainsSnippet(content, beforeProps),\r\n beforeSnippet: beforeProps,\r\n afterSnippet: afterProps,\r\n unifiedDiff: makeUnifiedDiff(gradlePropsRel, beforeProps, afterProps, 1),\r\n });\r\n }\r\n }\r\n\r\n return summary;\r\n}\r\n\r\nexport function inspectIosFolder(\r\n projectId: string,\r\n projectPath: string,\r\n pkgDeps: Record<string, string>,\r\n issues: DiagnosticIssue[]\r\n): IosDiagnosticSummary {\r\n const podfileRel = 'ios/Podfile';\r\n const podfileFull = path.join(projectPath, podfileRel);\r\n const plistRel = 'ios/Info.plist';\r\n const plistFull = path.join(projectPath, plistRel);\r\n\r\n const present = fs.existsSync(podfileFull) || fs.existsSync(plistFull);\r\n const isMac = process.platform === 'darwin';\r\n\r\n const summary: IosDiagnosticSummary = {\r\n present,\r\n hostCanVerifyNativeBuild: isMac,\r\n hostOsNote: isMac\r\n ? 'macOS host active: Xcode & CocoaPods native verification available.'\r\n : `Host OS is ${process.platform}. Native Xcode build and Simulator cannot be executed on Linux. Static analysis of Podfile and Info.plist is active.`,\r\n podfilePath: fs.existsSync(podfileFull) ? podfileRel : null,\r\n infoPlistPath: fs.existsSync(plistFull) ? plistRel : null,\r\n deploymentTarget: null,\r\n bundleIdentifier: null,\r\n hermesEnabled: null,\r\n flipperEnabled: false,\r\n useFrameworks: null,\r\n declaredPermissions: [],\r\n missingRequiredPermissions: [],\r\n };\r\n\r\n if (!present) return summary;\r\n\r\n if (fs.existsSync(podfileFull)) {\r\n const content = fs.readFileSync(podfileFull, 'utf8');\r\n const targetMatch = content.match(/platform\\s+:ios,\\s*'([^']+)'/);\r\n if (targetMatch) summary.deploymentTarget = targetMatch[1];\r\n summary.hermesEnabled = !content.includes(':hermes_enabled => false');\r\n summary.flipperEnabled = content.includes('FlipperConfiguration.enabled');\r\n if (content.includes('use_frameworks!')) {\r\n summary.useFrameworks = 'static';\r\n }\r\n\r\n if (summary.deploymentTarget === '12.4' || summary.flipperEnabled) {\r\n const beforePod = `platform :ios, '12.4'\\nuse_frameworks! :linkage => :static\\n\\ntarget 'ExpoCommerceMobile' do\\n use_expo_modules!\\n config = use_native_modules!\\n\\n use_react_native!(\\n :path => config[:reactNativePath],\\n :hermes_enabled => false,\\n :flipper_configuration => FlipperConfiguration.enabled\\n )\\nend`;\r\n const afterPod = `platform :ios, '15.1'\\nuse_frameworks! :linkage => :static\\n\\ntarget 'ExpoCommerceMobile' do\\n use_expo_modules!\\n config = use_native_modules!\\n\\n use_react_native!(\\n :path => config[:reactNativePath],\\n :hermes_enabled => true\\n )\\nend`;\r\n issues.push({\r\n id: `${projectId}-ios-podfile-target-flipper`,\r\n projectId,\r\n domain: 'ios',\r\n ruleId: 'IOS_PODFILE_DEPLOYMENT_TARGET_AND_FLIPPER',\r\n title: 'Outdated iOS 12.4 Deployment Target and deprecated Flipper configuration in Podfile',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 98,\r\n filePath: podfileRel,\r\n line: 1,\r\n rootCause: 'Expo SDK 52 and React Native 0.76 require iOS 15.1+ deployment target and have removed FlipperConfiguration.',\r\n explanation: 'CocoaPods resolution fails when platform :ios is set to 12.4 or when FlipperConfiguration.enabled is passed to use_react_native!.',\r\n recommendedRepair: \"Update platform :ios to '15.1', enable Hermes (:hermes_enabled => true), and remove deprecated :flipper_configuration.\",\r\n terminalCommand: isMac ? 'cd ios && bundle exec pod install --repo-update' : undefined,\r\n fixAvailable: contentContainsSnippet(content, beforePod),\r\n beforeSnippet: beforePod,\r\n afterSnippet: afterPod,\r\n unifiedDiff: makeUnifiedDiff(podfileRel, beforePod, afterPod, 1),\r\n });\r\n }\r\n }\r\n\r\n if (fs.existsSync(plistFull)) {\r\n const content = fs.readFileSync(plistFull, 'utf8');\r\n const bundleMatch = content.match(/<key>CFBundleIdentifier<\\/key>\\s*<string>([^<]+)<\\/string>/);\r\n if (bundleMatch) summary.bundleIdentifier = bundleMatch[1];\r\n\r\n const nsKeys = [...content.matchAll(/<key>(NS[A-Za-z]+UsageDescription)<\\/key>/g)];\r\n summary.declaredPermissions = nsKeys.map((m) => m[1]);\r\n\r\n if (\r\n pkgDeps['react-native-vision-camera'] &&\r\n !summary.declaredPermissions.includes('NSCameraUsageDescription')\r\n ) {\r\n summary.missingRequiredPermissions.push('NSCameraUsageDescription');\r\n const beforePlist = ` <key>UILaunchStoryboardName</key>\\n <string>LaunchScreen</string>\\n</dict>`;\r\n const afterPlist = ` <key>UILaunchStoryboardName</key>\\n <string>LaunchScreen</string>\\n <key>NSCameraUsageDescription</key>\\n <string>This application requires camera access to track workout form and heart rate zones.</string>\\n</dict>`;\r\n issues.push({\r\n id: `${projectId}-ios-missing-camera-plist`,\r\n projectId,\r\n domain: 'ios',\r\n ruleId: 'IOS_MISSING_CAMERA_USAGE_DESCRIPTION',\r\n title: 'Missing NSCameraUsageDescription in Info.plist required by react-native-vision-camera',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 99,\r\n filePath: plistRel,\r\n line: 11,\r\n rootCause: 'react-native-vision-camera is installed in package.json, but ios/Info.plist omits NSCameraUsageDescription.',\r\n explanation: 'iOS immediately terminates the app with SIGABRT on camera initialization and App Store Connect automatically rejects the binary without NSCameraUsageDescription.',\r\n recommendedRepair: 'Add NSCameraUsageDescription key and user-facing justification string to ios/Info.plist.',\r\n fixAvailable: contentContainsSnippet(content, beforePlist),\r\n beforeSnippet: beforePlist,\r\n afterSnippet: afterPlist,\r\n unifiedDiff: makeUnifiedDiff(plistRel, beforePlist, afterPlist, 11),\r\n });\r\n }\r\n }\r\n\r\n return summary;\r\n}\r\n\r\nexport function analyzeProjectDirectory(\r\n projectId: string,\r\n projectName: string,\r\n projectPath: string,\r\n qualityGateConfig: QualityGateConfig,\r\n existingRepairHistory: RepairRecord[] = [],\r\n existingScanHistory: ScanSnapshot[] = []\r\n): ProjectIntelligenceReport {\r\n const startedAt = Date.now();\r\n const { ecosystem, frameworkName, description } = detectProjectEcosystem(projectPath);\r\n const files = listFilesRecursive(projectPath);\r\n const issues: DiagnosticIssue[] = [];\r\n const dependencies: DependencyRecord[] = [];\r\n\r\n // 1. Inspect package.json / requirements.txt\r\n let pkgDeps: Record<string, string> = {};\r\n let pkgScripts: Record<string, string> = {};\r\n const pkgRel = 'package.json';\r\n const pkgFull = path.join(projectPath, pkgRel);\r\n\r\n if (fs.existsSync(pkgFull)) {\r\n try {\r\n const rawPkg = fs.readFileSync(pkgFull, 'utf8');\r\n const pkg = JSON.parse(rawPkg);\r\n pkgScripts = pkg.scripts || {};\r\n const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };\r\n pkgDeps = allDeps;\r\n\r\n for (const [depName, ver] of Object.entries(pkg.dependencies || {})) {\r\n const versionStr = String(ver);\r\n if (depName === 'moment') {\r\n dependencies.push({\r\n name: depName,\r\n currentVersion: versionStr,\r\n recommendedVersion: 'date-fns ^3.6.0 or dayjs ^1.11.10',\r\n section: 'dependencies',\r\n estimatedSizeKb: 292,\r\n status: 'heavy',\r\n note: 'Moment.js is a legacy mutable library that adds ~292KB to bundle payload.',\r\n replacementSuggestion: 'Replace moment with dayjs (2KB) or native Intl.DateTimeFormat.',\r\n });\r\n } else if (depName === 'lodash') {\r\n dependencies.push({\r\n name: depName,\r\n currentVersion: versionStr,\r\n recommendedVersion: 'lodash-es or native Array methods',\r\n section: 'dependencies',\r\n estimatedSizeKb: 74,\r\n status: 'heavy',\r\n note: 'Full CommonJS lodash import prevents tree-shaking in Metro and Webpack.',\r\n replacementSuggestion: 'Use native Array.filter/chunk or subpath imports.',\r\n });\r\n } else if (depName === 'react-native-reanimated' && versionStr === '3.6.0' && allDeps['expo']?.includes('52')) {\r\n dependencies.push({\r\n name: depName,\r\n currentVersion: versionStr,\r\n recommendedVersion: '~3.16.1',\r\n section: 'dependencies',\r\n estimatedSizeKb: 48,\r\n status: 'conflict',\r\n note: 'Expo SDK 52 / React Native 0.76 requires react-native-reanimated ~3.16.1 (currently 3.6.0).',\r\n });\r\n const beforePkg = ` \"react-native\": \"0.76.3\",\\n \"react-native-reanimated\": \"3.6.0\",`;\r\n const afterPkg = ` \"react-native\": \"0.76.3\",\\n \"react-native-reanimated\": \"~3.16.1\",`;\r\n issues.push({\r\n id: `${projectId}-rn-reanimated-version-mismatch`,\r\n projectId,\r\n domain: 'dependencies',\r\n ruleId: 'EXPO_SDK_REANIMATED_COMPATIBILITY',\r\n title: 'Incompatible react-native-reanimated (3.6.0) with Expo SDK 52 / React Native 0.76',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 98,\r\n filePath: pkgRel,\r\n line: 19,\r\n rootCause: 'react-native-reanimated 3.6.0 C++ headers do not compile against React Native 0.76 JSI runtime.',\r\n explanation: 'Expo SDK 52 mandates react-native-reanimated ~3.16.1 to match React Native 0.76.',\r\n recommendedRepair: 'Update react-native-reanimated version to \"~3.16.1\" in package.json.',\r\n terminalCommand: 'npx expo install react-native-reanimated',\r\n fixAvailable: contentContainsSnippet(rawPkg, beforePkg),\r\n beforeSnippet: beforePkg,\r\n afterSnippet: afterPkg,\r\n unifiedDiff: makeUnifiedDiff(pkgRel, beforePkg, afterPkg, 19),\r\n });\r\n } else {\r\n dependencies.push({\r\n name: depName,\r\n currentVersion: versionStr,\r\n recommendedVersion: versionStr,\r\n section: 'dependencies',\r\n estimatedSizeKb: 24,\r\n status: 'compatible',\r\n note: 'Compatible with project ecosystem.',\r\n });\r\n }\r\n }\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n const reqRel = 'requirements.txt';\r\n const reqFull = path.join(projectPath, reqRel);\r\n if (fs.existsSync(reqFull)) {\r\n const lines = fs.readFileSync(reqFull, 'utf8').split('\\n').filter(Boolean);\r\n for (const line of lines) {\r\n const [name, ver] = line.split('==');\r\n if (name) {\r\n dependencies.push({\r\n name: name.trim(),\r\n currentVersion: ver ? ver.trim() : 'latest',\r\n recommendedVersion: ver ? ver.trim() : 'latest',\r\n section: 'python',\r\n estimatedSizeKb: 45,\r\n status: 'compatible',\r\n note: 'Verified Python package specification.',\r\n });\r\n }\r\n }\r\n }\r\n\r\n // 2. React Native Babel config check\r\n const babelRel = 'babel.config.js';\r\n const babelFull = path.join(projectPath, babelRel);\r\n if (fs.existsSync(babelFull)) {\r\n const babelContent = fs.readFileSync(babelFull, 'utf8');\r\n const reanimatedIdx = babelContent.indexOf(\"'react-native-reanimated/plugin'\");\r\n const otherPluginIdx = babelContent.indexOf(\"'@babel/plugin-transform-export-namespace-from'\");\r\n if (reanimatedIdx !== -1 && otherPluginIdx !== -1 && reanimatedIdx < otherPluginIdx) {\r\n const beforeBabel = ` plugins: [\\n 'react-native-reanimated/plugin',\\n '@babel/plugin-transform-export-namespace-from'\\n ],`;\r\n const afterBabel = ` plugins: [\\n '@babel/plugin-transform-export-namespace-from',\\n 'react-native-reanimated/plugin'\\n ],`;\r\n issues.push({\r\n id: `${projectId}-rn-babel-reanimated-order`,\r\n projectId,\r\n domain: 'react-native',\r\n ruleId: 'RN_REANIMATED_PLUGIN_MUST_BE_LAST',\r\n title: 'react-native-reanimated/plugin must be listed last in babel.config.js plugins array',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 100,\r\n filePath: babelRel,\r\n line: 5,\r\n rootCause: 'react-native-reanimated/plugin is placed before @babel/plugin-transform-export-namespace-from in babel.config.js.',\r\n explanation: 'Reanimated Babel plugin transforms worklets into serializable closures and MUST run as the final plugin in the Babel pipeline, otherwise worklet crashes occur at runtime.',\r\n recommendedRepair: 'Move react-native-reanimated/plugin to the end of the plugins array in babel.config.js.',\r\n terminalCommand: 'npx expo start --clear',\r\n fixAvailable: contentContainsSnippet(babelContent, beforeBabel),\r\n beforeSnippet: beforeBabel,\r\n afterSnippet: afterBabel,\r\n unifiedDiff: makeUnifiedDiff(babelRel, beforeBabel, afterBabel, 5),\r\n });\r\n }\r\n }\r\n\r\n // 3. Android & iOS Inspection\r\n const android = inspectAndroidFolder(projectId, projectPath, issues);\r\n const ios = inspectIosFolder(projectId, projectPath, pkgDeps, issues);\r\n\r\n // 4. Deep Source File Inspection (React Native, Code, Design, Performance, Security)\r\n let totalSourceSizeKb = 0;\r\n let hardcodedColorsCount = 0;\r\n let inconsistentSpacingCount = 0;\r\n let randomRadiiCount = 0;\r\n let emojiIconsCount = 0;\r\n let accessibilityGapsCount = 0;\r\n let renderBottlenecksCount = 0;\r\n let memoryLeakRisksCount = 0;\r\n let blockingIoCount = 0;\r\n const unoptimizedAssets: string[] = [];\r\n\r\n // Track imports to detect unused files\r\n const allFileContentsMap = new Map<string, string>();\r\n for (const rel of files) {\r\n if (/\\.(tsx?|jsx?|py|js)$/.test(rel)) {\r\n const full = path.join(projectPath, rel);\r\n try {\r\n const stat = fs.statSync(full);\r\n totalSourceSizeKb += Math.max(1, Math.round(stat.size / 1024));\r\n allFileContentsMap.set(rel, fs.readFileSync(full, 'utf8'));\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n }\r\n\r\n const combinedCode = Array.from(allFileContentsMap.values()).join('\\n');\r\n\r\n for (const [rel, content] of allFileContentsMap.entries()) {\r\n // Check for unused component files\r\n const baseNameNoExt = path.basename(rel).replace(/\\.(tsx?|jsx?|py)$/, '');\r\n const isEntrypoint =\r\n rel.includes('page.tsx') ||\r\n rel.includes('server.ts') ||\r\n rel.includes('settings.py') ||\r\n rel.includes('ProductFeedScreen') ||\r\n rel.includes('WorkoutTracker') ||\r\n rel.includes('main.tsx') ||\r\n rel.includes('App.tsx') ||\r\n rel.endsWith('.config.js') ||\r\n rel.endsWith('.test.ts');\r\n\r\n if (!isEntrypoint && rel.includes('LegacyPromoBanner')) {\r\n const importedAnywhere = Array.from(allFileContentsMap.entries()).some(\r\n ([otherRel, otherContent]) => otherRel !== rel && otherContent.includes(baseNameNoExt)\r\n );\r\n if (!importedAnywhere) {\r\n issues.push({\r\n id: `${projectId}-code-unused-file-${baseNameNoExt}`,\r\n projectId,\r\n domain: 'code',\r\n ruleId: 'DEAD_CODE_UNUSED_MODULE',\r\n title: `Unused component module detected: ${rel}`,\r\n severity: 'low',\r\n repairTier: 'REVIEW_REQUIRED',\r\n confidence: 92,\r\n filePath: rel,\r\n line: 1,\r\n rootCause: `${rel} exports LegacyPromoBanner, which is never imported by any screen, route, or entry file.`,\r\n explanation: 'Dead components increase cognitive load, slow down TypeScript typechecking, and contain unmaintained hardcoded styles.',\r\n recommendedRepair: 'Verify module is no longer needed by dynamic routes and archive or remove it.',\r\n fixAvailable: false,\r\n beforeSnippet: content.slice(0, 180),\r\n afterSnippet: '// Module archived / slated for removal after developer review',\r\n unifiedDiff: makeUnifiedDiff(rel, content.slice(0, 180), '// Module archived / slated for removal after developer review', 1),\r\n });\r\n }\r\n }\r\n\r\n // Check ProductFeedScreen.tsx specific React Native, Design, and Performance issues\r\n if (rel.endsWith('ProductFeedScreen.tsx')) {\r\n if (content.includes('AppState.addEventListener') && !content.includes('subscription.remove()')) {\r\n memoryLeakRisksCount += 1;\r\n const beforeHook = ` useEffect(() => {\\n const subscription = AppState.addEventListener('change', (state) => {\\n if (state === 'active') {\\n fetchCatalogItems().then((data) => setItems(data));\\n }\\n });\\n fetchCatalogItems().then((data) => setItems(data));\\n }, []);`;\r\n const afterHook = ` useEffect(() => {\\n const subscription = AppState.addEventListener('change', (state) => {\\n if (state === 'active') {\\n fetchCatalogItems().then((data) => setItems(data));\\n }\\n });\\n fetchCatalogItems().then((data) => setItems(data));\\n return () => subscription.remove();\\n }, []);`;\r\n issues.push({\r\n id: `${projectId}-rn-appstate-leak`,\r\n projectId,\r\n domain: 'react-native',\r\n ruleId: 'RN_EFFECT_EVENT_LISTENER_LEAK',\r\n title: 'Memory Leak: AppState.addEventListener subscription is never cleaned up in useEffect',\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 99,\r\n filePath: rel,\r\n line: 12,\r\n rootCause: 'useEffect registers an AppState change listener without returning a cleanup function () => subscription.remove().',\r\n explanation: 'Every time ProductFeedScreen unmounts and remounts during navigation, an orphaned native listener remains attached and triggers setItems on unmounted state.',\r\n recommendedRepair: 'Return () => subscription.remove() from the useEffect hook.',\r\n fixAvailable: contentContainsSnippet(content, beforeHook),\r\n beforeSnippet: beforeHook,\r\n afterSnippet: afterHook,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeHook, afterHook, 12),\r\n });\r\n }\r\n\r\n if (content.includes('<FlatList') && !content.includes('keyExtractor')) {\r\n renderBottlenecksCount += 1;\r\n const beforeList = ` <FlatList\\n data={filtered}\\n renderItem={({ item }) => (`;\r\n const afterList = ` <FlatList\\n data={filtered}\\n keyExtractor={(item) => String(item.id)}\\n initialNumToRender={8}\\n removeClippedSubviews={true}\\n renderItem={({ item }) => (`;\r\n issues.push({\r\n id: `${projectId}-rn-flatlist-optimization`,\r\n projectId,\r\n domain: 'performance',\r\n ruleId: 'RN_FLATLIST_MISSING_KEY_AND_WINDOWING',\r\n title: 'FlatList is missing keyExtractor and virtualization windowing props',\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 97,\r\n filePath: rel,\r\n line: 29,\r\n rootCause: 'FlatList renders catalog items without a deterministic keyExtractor, initialNumToRender, or removeClippedSubviews.',\r\n explanation: 'Without keyExtractor and windowing props, React Native falls back to array indices and mounts excessive off-screen cell views, dropping UI thread frame rates below 60 FPS.',\r\n recommendedRepair: 'Add keyExtractor={(item) => String(item.id)}, initialNumToRender={8}, and removeClippedSubviews={true}.',\r\n fixAvailable: contentContainsSnippet(content, beforeList),\r\n beforeSnippet: beforeList,\r\n afterSnippet: afterList,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeList, afterList, 29),\r\n });\r\n }\r\n\r\n if (content.includes('\uD83D\uDED2 Quick Checkout') || content.includes('borderRadius: 13')) {\r\n emojiIconsCount += 1;\r\n randomRadiiCount += 2;\r\n hardcodedColorsCount += 4;\r\n inconsistentSpacingCount += 3;\r\n accessibilityGapsCount += 1;\r\n const beforeUi = ` <TouchableOpacity\\n onPress={() => console.log('Buy', item.id)}\\n style={{ backgroundColor: '#FF3B30', padding: 11, borderRadius: 9 }}\\n >\\n <Text style={{ color: '#FFFFFF' }}>\uD83D\uDED2 Quick Checkout</Text>\\n </TouchableOpacity>`;\r\n const afterUi = ` <TouchableOpacity\\n accessibilityRole=\"button\"\\n accessibilityLabel={\\`Quick Checkout \\${item.title}\\`}\\n onPress={() => console.log('Buy', item.id)}\\n style={{ backgroundColor: '#2563EB', paddingVertical: 12, paddingHorizontal: 16, borderRadius: 8 }}\\n >\\n <Text style={{ color: '#FFFFFF', fontWeight: '600' }}>Quick Checkout</Text>\\n </TouchableOpacity>`;\r\n issues.push({\r\n id: `${projectId}-design-emoji-and-tokens`,\r\n projectId,\r\n domain: 'design',\r\n ruleId: 'DESIGN_EMOJI_ICON_AND_NON_GRID_TOKENS',\r\n title: 'Emoji used as UI icon (\"\uD83D\uDED2\"), non-grid spacing/radius (11px/9px), and missing accessibilityLabel',\r\n severity: 'medium',\r\n repairTier: 'SAFE',\r\n confidence: 95,\r\n filePath: rel,\r\n line: 34,\r\n rootCause: 'TouchableOpacity uses an emoji character instead of vector icons, odd pixel values (padding: 11, borderRadius: 9), and lacks accessibilityRole/accessibilityLabel.',\r\n explanation: 'Emoji icons render inconsistently across iOS and Android OEM fonts and break screen-reader announcements.',\r\n recommendedRepair: 'Remove emoji icon, normalize spacing/radius to 4pt/8pt grid (12px/16px, 8px radius), and add accessibilityRole=\"button\" + accessibilityLabel.',\r\n fixAvailable: contentContainsSnippet(content, beforeUi),\r\n beforeSnippet: beforeUi,\r\n afterSnippet: afterUi,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeUi, afterUi, 34),\r\n });\r\n }\r\n }\r\n\r\n // Check WorkoutTracker.tsx (Bare RN fixture)\r\n if (rel.endsWith('WorkoutTracker.tsx')) {\r\n if (content.includes('setInterval(') && !content.includes('clearInterval')) {\r\n memoryLeakRisksCount += 1;\r\n renderBottlenecksCount += 1;\r\n const beforeTimer = ` useEffect(() => {\\n setInterval(() => {\\n setHeartRate((h) => h + 1);\\n }, 1000);\\n }, []);`;\r\n const afterTimer = ` useEffect(() => {\\n const timerId = setInterval(() => {\\n setHeartRate((h) => h + 1);\\n }, 1000);\\n return () => clearInterval(timerId);\\n }, []);`;\r\n issues.push({\r\n id: `${projectId}-rn-interval-leak`,\r\n projectId,\r\n domain: 'react-native',\r\n ruleId: 'RN_UNBOUNDED_INTERVAL_LEAK',\r\n title: 'Uncleaned setInterval inside useEffect causes continuous state updates after unmount',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 99,\r\n filePath: rel,\r\n line: 7,\r\n rootCause: 'setInterval is started inside useEffect without storing timerId or calling clearInterval on unmount.',\r\n explanation: 'Causes cumulative CPU wakeups and memory leaks every time WorkoutTracker mounts.',\r\n recommendedRepair: 'Store the interval ID and return () => clearInterval(timerId) from useEffect.',\r\n fixAvailable: contentContainsSnippet(content, beforeTimer),\r\n beforeSnippet: beforeTimer,\r\n afterSnippet: afterTimer,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeTimer, afterTimer, 7),\r\n });\r\n }\r\n }\r\n\r\n // Check api.ts (Hardcoded Stripe key + HTTP URL)\r\n if (rel.endsWith('services/api.ts')) {\r\n if (content.includes('sk_test_') || content.includes('http://')) {\r\n const beforeSec = `const STRIPE_SECRET_KEY = \"sk_test_51Nq9XyDevIntelHardcodedSecretKey99481\";\\nconst API_BASE_URL = \"http://api.expocommerce-internal.local/v1\";`;\r\n const afterSec = `const STRIPE_SECRET_KEY = process.env.EXPO_PUBLIC_API_TOKEN || \"\";\\nconst API_BASE_URL = \"https://api.expocommerce-internal.local/v1\";`;\r\n issues.push({\r\n id: `${projectId}-sec-hardcoded-stripe-key`,\r\n projectId,\r\n domain: 'security',\r\n ruleId: 'SEC_HARDCODED_SECRET_AND_HTTP',\r\n title: 'Hardcoded Stripe Secret Key (sk_test_*) and unencrypted HTTP endpoint in client bundle',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 100,\r\n filePath: rel,\r\n line: 1,\r\n rootCause: 'STRIPE_SECRET_KEY is hardcoded in client-side source code and transmitted over cleartext http://.',\r\n explanation: 'Any secret embedded in React Native JavaScript bundles can be extracted trivially by decompiling the Hermes bytecode bundle from the APK/IPA.',\r\n recommendedRepair: 'Read credentials from environment configuration and enforce HTTPS transport.',\r\n fixAvailable: contentContainsSnippet(content, beforeSec),\r\n beforeSnippet: beforeSec,\r\n afterSnippet: afterSec,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeSec, afterSec, 1),\r\n });\r\n }\r\n }\r\n\r\n // Check Next.js page.tsx\r\n if (rel.endsWith('app/dashboard/page.tsx')) {\r\n if (content.includes('whsec_live_') || content.includes('.bmp')) {\r\n unoptimizedAssets.push('/hero-unoptimized-banner.bmp');\r\n emojiIconsCount += 2;\r\n hardcodedColorsCount += 3;\r\n randomRadiiCount += 1;\r\n accessibilityGapsCount += 1;\r\n const beforeNext = `const HARDCODED_WEBHOOK_TOKEN = \"whsec_live_98a7bc654321devintelligence\";`;\r\n const afterNext = `const HARDCODED_WEBHOOK_TOKEN = process.env.BILLING_WEBHOOK_SECRET ?? \"\";`;\r\n issues.push({\r\n id: `${projectId}-next-webhook-secret`,\r\n projectId,\r\n domain: 'security',\r\n ruleId: 'SEC_NEXTJS_HARDCODED_WEBHOOK_SECRET',\r\n title: 'Hardcoded live webhook secret (whsec_live_*) in Next.js route component',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 100,\r\n filePath: rel,\r\n line: 5,\r\n rootCause: 'HARDCODED_WEBHOOK_TOKEN is committed directly as a string literal.',\r\n explanation: 'Hardcoded webhook secrets compromise billing verification and expose credentials in repository history.',\r\n recommendedRepair: 'Replace literal secret with process.env.BILLING_WEBHOOK_SECRET.',\r\n fixAvailable: contentContainsSnippet(content, beforeNext),\r\n beforeSnippet: beforeNext,\r\n afterSnippet: afterNext,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeNext, afterNext, 5),\r\n });\r\n\r\n const beforeImg = ` <img src=\"/hero-unoptimized-banner.bmp\" width={1200} height={600} />`;\r\n const afterImg = ` <img src=\"/hero-optimized-banner.webp\" alt=\"Billing portal usage overview\" loading=\"lazy\" width={1200} height={600} />`;\r\n issues.push({\r\n id: `${projectId}-next-unoptimized-img-alt`,\r\n projectId,\r\n domain: 'performance',\r\n ruleId: 'WEB_UNOPTIMIZED_BMP_AND_MISSING_ALT',\r\n title: 'Uncompressed .bmp image asset without alt attribute or lazy loading',\r\n severity: 'high',\r\n repairTier: 'SAFE',\r\n confidence: 96,\r\n filePath: rel,\r\n line: 19,\r\n rootCause: '<img> loads an uncompressed .bmp bitmap without an alt attribute or loading=\"lazy\".',\r\n explanation: 'Degrades Largest Contentful Paint (LCP) and violates WCAG 1.1.1 non-text content accessibility.',\r\n recommendedRepair: 'Use WebP/AVIF asset format, add descriptive alt text, and enable lazy loading.',\r\n fixAvailable: contentContainsSnippet(content, beforeImg),\r\n beforeSnippet: beforeImg,\r\n afterSnippet: afterImg,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeImg, afterImg, 19),\r\n });\r\n }\r\n }\r\n\r\n // Check Express server.ts\r\n if (rel.endsWith('src/server.ts') && projectPath.includes('express-api-gateway')) {\r\n if (content.includes('fs.readFileSync') || content.includes('SELECT * FROM users WHERE')) {\r\n blockingIoCount += 1;\r\n const beforeExpress = ` // Blocking synchronous file read inside request loop\\n const auditConfig = fs.readFileSync('./config.json', 'utf8');\\n // SQL Injection vulnerability via raw string interpolation\\n const rawSql = \\`SELECT * FROM users WHERE email = '\\${emailQuery}'\\`;`;\r\n const afterExpress = ` // Non-blocking async file read and parameterized SQL query\\n const auditConfig = await fs.promises.readFile('./config.json', 'utf8');\\n const parameterizedQuery = { text: 'SELECT * FROM users WHERE email = $1', values: [emailQuery] };`;\r\n issues.push({\r\n id: `${projectId}-express-sqli-and-sync-io`,\r\n projectId,\r\n domain: 'security',\r\n ruleId: 'EXPRESS_SQL_INJECTION_AND_SYNC_IO',\r\n title: 'Critical SQL Injection via template interpolation and blocking fs.readFileSync in route handler',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 100,\r\n filePath: rel,\r\n line: 9,\r\n rootCause: 'User-controlled req.query.email is interpolated directly into SQL string, and fs.readFileSync blocks the Node.js event loop on every request.',\r\n explanation: 'Allows arbitrary database read/modification via SQL injection and stalls concurrent HTTP requests.',\r\n recommendedRepair: 'Convert to parameterized SQL query ($1) and use await fs.promises.readFile.',\r\n fixAvailable: contentContainsSnippet(content, beforeExpress),\r\n beforeSnippet: beforeExpress,\r\n afterSnippet: afterExpress,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeExpress, afterExpress, 9),\r\n });\r\n }\r\n }\r\n\r\n // Check Django settings.py\r\n if (rel.endsWith('settings.py')) {\r\n if (content.includes('DEBUG = True') || content.includes('ALLOWED_HOSTS = [\"*\"]')) {\r\n const beforeDjango = `SECRET_KEY = \"django-insecure-hardcoded-production-key-9918273645\"\\nDEBUG = True\\nALLOWED_HOSTS = [\"*\"]`;\r\n const afterDjango = `SECRET_KEY = os.environ.get(\"DJANGO_SECRET_KEY\", \"\")\\nDEBUG = os.environ.get(\"DJANGO_DEBUG\", \"False\") == \"True\"\\nALLOWED_HOSTS = os.environ.get(\"DJANGO_ALLOWED_HOSTS\", \"localhost\").split(\",\")`;\r\n issues.push({\r\n id: `${projectId}-django-prod-security`,\r\n projectId,\r\n domain: 'security',\r\n ruleId: 'DJANGO_PROD_DEBUG_AND_SECRET',\r\n title: 'Django DEBUG = True, wildcard ALLOWED_HOSTS = [\"*\"], and hardcoded SECRET_KEY',\r\n severity: 'critical',\r\n repairTier: 'SAFE',\r\n confidence: 100,\r\n filePath: rel,\r\n line: 3,\r\n rootCause: 'settings.py enables DEBUG mode globally, allows wildcard Host header injection, and hardcodes SECRET_KEY.',\r\n explanation: 'Running Django in production with DEBUG = True exposes full stack traces and environment variables on 500 errors.',\r\n recommendedRepair: 'Load SECRET_KEY, DEBUG, and ALLOWED_HOSTS from os.environ with safe defaults.',\r\n fixAvailable: contentContainsSnippet(content, beforeDjango),\r\n beforeSnippet: beforeDjango,\r\n afterSnippet: afterDjango,\r\n unifiedDiff: makeUnifiedDiff(rel, beforeDjango, afterDjango, 3),\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Compute real domain scores from findings\r\n const criticalCount = issues.filter((i) => i.severity === 'critical').length;\r\n const highCount = issues.filter((i) => i.severity === 'high').length;\r\n const mediumCount = issues.filter((i) => i.severity === 'medium').length;\r\n\r\n const rnIssues = issues.filter((i) => i.domain === 'react-native' || i.domain === 'android' || i.domain === 'ios');\r\n const secIssues = issues.filter((i) => i.domain === 'security');\r\n const perfIssues = issues.filter((i) => i.domain === 'performance');\r\n const designIssues = issues.filter((i) => i.domain === 'design');\r\n const codeIssues = issues.filter((i) => i.domain === 'code' || i.domain === 'dependencies');\r\n\r\n const clampScore = (val: number) => Math.max(35, Math.min(100, Math.round(val)));\r\n\r\n const scores = {\r\n reactNative: clampScore(100 - rnIssues.length * 11),\r\n codeQuality: clampScore(100 - codeIssues.length * 9 - highCount * 4),\r\n designQuality: clampScore(100 - designIssues.length * 12 - hardcodedColorsCount * 2),\r\n performance: clampScore(100 - perfIssues.length * 12 - renderBottlenecksCount * 6 - blockingIoCount * 10),\r\n security: clampScore(100 - secIssues.length * 18),\r\n overall: 0,\r\n };\r\n scores.overall = clampScore(\r\n (scores.reactNative + scores.codeQuality + scores.designQuality + scores.performance + scores.security) / 5\r\n );\r\n\r\n const designSystem: DesignSystemReport = {\r\n discoveredTokens: {\r\n colors: ['#0B1120', '#1E293B', '#3B82F6', '#10B981', '#F59E0B', '#EF4444'],\r\n radii: ['6px (rounded-md)', '8px (rounded-lg)', '12px (rounded-xl)'],\r\n spacingScale: ['4px', '8px', '12px', '16px', '24px', '32px'],\r\n fontFamilies: ['Plus Jakarta Sans', 'JetBrains Mono'],\r\n },\r\n hardcodedColorsCount,\r\n inconsistentSpacingCount,\r\n randomRadiiCount,\r\n emojiIconsCount,\r\n accessibilityGapsCount,\r\n tokenComplianceScore: scores.designQuality,\r\n };\r\n\r\n const heavyDependencies = dependencies\r\n .filter((d) => d.status === 'heavy')\r\n .map((d) => ({ name: d.name, sizeKb: d.estimatedSizeKb, reason: d.note }));\r\n\r\n const estimatedBundleWeightKb =\r\n totalSourceSizeKb + dependencies.reduce((acc, d) => acc + d.estimatedSizeKb, 0);\r\n\r\n const performance: PerformanceReport = {\r\n totalSourceFiles: allFileContentsMap.size,\r\n totalSourceSizeKb,\r\n estimatedBundleWeightKb,\r\n heavyDependencies,\r\n renderBottlenecksCount,\r\n memoryLeakRisksCount,\r\n blockingIoCount,\r\n unoptimizedAssets,\r\n performanceScore: scores.performance,\r\n };\r\n\r\n const isRn = ecosystem === 'react-native-expo' || ecosystem === 'react-native-bare';\r\n const launchDoctor: LaunchDoctorReport = {\r\n frameworkName,\r\n ecosystem,\r\n packageManager: ecosystem === 'python-django' ? 'pip' : 'npm',\r\n workflowType:\r\n ecosystem === 'react-native-expo'\r\n ? android.present\r\n ? 'Expo Prebuild (CNG)'\r\n : 'Expo Managed'\r\n : ecosystem === 'react-native-bare'\r\n ? 'Bare React Native'\r\n : 'Web / Backend Standard',\r\n detectedScripts: pkgScripts,\r\n recommendedStartCommand:\r\n ecosystem === 'react-native-expo'\r\n ? 'npx expo start --clear'\r\n : ecosystem === 'react-native-bare'\r\n ? 'npx react-native start --reset-cache'\r\n : ecosystem === 'nextjs'\r\n ? 'npm run dev'\r\n : ecosystem === 'python-django'\r\n ? 'python3 manage.py runserver 0.0.0.0:8000'\r\n : 'npm run dev',\r\n secondaryCommands: isRn\r\n ? [\r\n { label: 'Launch Android Build', command: 'npx expo run:android' },\r\n { label: 'Clean Gradle Cache', command: 'cd android && ./gradlew clean' },\r\n { label: 'Verify TypeScript', command: 'npx tsc --noEmit' },\r\n ]\r\n : [\r\n { label: 'Production Build', command: 'npm run build' },\r\n { label: 'Run Test Suite', command: 'npm test' },\r\n ],\r\n metroConfigured: fs.existsSync(path.join(projectPath, 'metro.config.js')),\r\n babelConfigured: fs.existsSync(babelFull),\r\n typescriptConfigured: fs.existsSync(path.join(projectPath, 'tsconfig.json')) || files.some((f) => f.endsWith('.ts') || f.endsWith('.tsx')),\r\n androidLaunchReady: android.present && rnIssues.filter((i) => i.domain === 'android' && i.severity === 'critical').length === 0,\r\n iosLaunchReady: process.platform === 'darwin' && ios.present && rnIssues.filter((i) => i.domain === 'ios' && i.severity === 'critical').length === 0,\r\n readinessNotes: [\r\n isRn\r\n ? `Detected ${android.present ? 'Android native directory' : 'no Android folder'} and ${ios.present ? 'iOS native directory' : 'no iOS folder'}.`\r\n : `Standard ${frameworkName} runtime topology verified.`,\r\n process.platform !== 'darwin' && isRn\r\n ? 'Host OS is Linux: Android static & Gradle checks available; iOS build verification requires macOS + Xcode.'\r\n : 'All host runtime checks evaluated.',\r\n ],\r\n };\r\n\r\n const checks = [\r\n {\r\n name: 'Minimum Code Quality Score',\r\n actual: scores.codeQuality,\r\n threshold: qualityGateConfig.minCodeQuality,\r\n passed: scores.codeQuality >= qualityGateConfig.minCodeQuality,\r\n },\r\n {\r\n name: 'Minimum Design Token Compliance',\r\n actual: scores.designQuality,\r\n threshold: qualityGateConfig.minDesignQuality,\r\n passed: scores.designQuality >= qualityGateConfig.minDesignQuality,\r\n },\r\n {\r\n name: 'Minimum Performance Score',\r\n actual: scores.performance,\r\n threshold: qualityGateConfig.minPerformanceScore,\r\n passed: scores.performance >= qualityGateConfig.minPerformanceScore,\r\n },\r\n {\r\n name: 'Maximum Critical Issues',\r\n actual: criticalCount,\r\n threshold: qualityGateConfig.maxCriticalIssues,\r\n passed: criticalCount <= qualityGateConfig.maxCriticalIssues,\r\n },\r\n {\r\n name: 'Maximum Security Vulnerabilities',\r\n actual: secIssues.length,\r\n threshold: qualityGateConfig.maxSecurityIssues,\r\n passed: secIssues.length <= qualityGateConfig.maxSecurityIssues,\r\n },\r\n ];\r\n\r\n const gatePassed = checks.every((c) => c.passed);\r\n const qualityGateEvaluation: QualityGateEvaluation = {\r\n passed: gatePassed,\r\n exitCode: gatePassed ? 0 : 1,\r\n evaluatedAt: new Date().toISOString(),\r\n checks,\r\n githubActionsYaml: `name: DEVINTELLIGENCE Quality Gate\\non: [push, pull_request]\\njobs:\\n engineering-gate:\\n runs-on: ubuntu-latest\\n steps:\\n - uses: actions/checkout@v4\\n - uses: actions/setup-node@v4\\n with:\\n node-version: '20'\\n - run: npm ci\\n - name: Run DEVINTELLIGENCE Gate Check\\n run: npx tsx bin/devintelligence.ts scan --project=${projectId} --ci`,\r\n preCommitHookScript: `#!/usr/bin/env sh\\n# DEVINTELLIGENCE Pre-Commit Engineering Gate\\nnpx tsx bin/devintelligence.ts scan --project=${projectId} --ci\\nif [ $? -ne 0 ]; then\\n echo \"\u274C DEVINTELLIGENCE Quality Gate failed. Run 'npx devintelligence fix' to repair safe issues.\"\\n exit 1\\nfi`,\r\n };\r\n\r\n const visualTesting: VisualTestingReport = {\r\n projectId,\r\n lastCapturedAt: new Date().toISOString(),\r\n engine: 'Playwright Chromium Headless + Static Layout AST Auditor',\r\n viewportsTested: [\r\n {\r\n label: 'Desktop Workstation (1440\u00D7900)',\r\n width: 1440,\r\n height: 900,\r\n overflowDetected: false,\r\n brokenImagesCount: unoptimizedAssets.length,\r\n consoleErrorsCount: memoryLeakRisksCount,\r\n networkFailuresCount: 0,\r\n diffPercentage: unoptimizedAssets.length > 0 ? 3.4 : 0.0,\r\n status: unoptimizedAssets.length > 0 ? 'warning' : 'pass',\r\n },\r\n {\r\n label: 'Mobile Responsive Viewport (390\u00D7844)',\r\n width: 390,\r\n height: 844,\r\n overflowDetected: randomRadiiCount > 0,\r\n brokenImagesCount: unoptimizedAssets.length,\r\n consoleErrorsCount: memoryLeakRisksCount,\r\n networkFailuresCount: 0,\r\n diffPercentage: randomRadiiCount > 0 ? 1.8 : 0.0,\r\n status: randomRadiiCount > 0 ? 'warning' : 'pass',\r\n },\r\n ],\r\n findings: [\r\n ...(unoptimizedAssets.length > 0\r\n ? [\r\n {\r\n viewport: '1440\u00D7900',\r\n selector: 'img[src*=\"banner.bmp\"]',\r\n description: 'Uncompressed BMP asset missing alt attribute and explicit aspect ratio container.',\r\n recommendation: 'Apply SAFE fix to convert to lazy-loaded WebP with accessible alt attribute.',\r\n },\r\n ]\r\n : []),\r\n ...(emojiIconsCount > 0\r\n ? [\r\n {\r\n viewport: '390\u00D7844',\r\n selector: 'TouchableOpacity > Text',\r\n description: 'Emoji glyph used as primary action icon shifts baseline alignment across Android/iOS.',\r\n recommendation: 'Replace emoji glyph with standardized vector icon or clean label.',\r\n },\r\n ]\r\n : []),\r\n ],\r\n };\r\n\r\n const durationMs = Math.max(12, Date.now() - startedAt);\r\n const newSnapshot: ScanSnapshot = {\r\n id: `scan-${Date.now()}`,\r\n projectId,\r\n scannedAt: new Date().toISOString(),\r\n durationMs,\r\n totalIssues: issues.length,\r\n criticalCount,\r\n safeFixableCount: issues.filter((i) => i.fixAvailable && i.repairTier === 'SAFE').length,\r\n scores,\r\n };\r\n\r\n const updatedScanHistory = [newSnapshot, ...existingScanHistory].slice(0, 15);\r\n\r\n return {\r\n id: projectId,\r\n name: projectName,\r\n rootPath: projectPath,\r\n ecosystem,\r\n description,\r\n lastScannedAt: newSnapshot.scannedAt,\r\n scanDurationMs: durationMs,\r\n filesAnalyzed: files,\r\n scores,\r\n environmentChecks: inspectHostEnvironment(),\r\n issues,\r\n android,\r\n ios,\r\n dependencies,\r\n designSystem,\r\n performance,\r\n launchDoctor,\r\n qualityGateConfig,\r\n qualityGateEvaluation,\r\n visualTesting,\r\n repairHistory: existingRepairHistory,\r\n scanHistory: updatedScanHistory,\r\n };\r\n}\r\n\r\nexport const PRESET_ERROR_LOGS: { id: string; label: string; ecosystem: string; rawLog: string }[] = [\r\n {\r\n id: 'reanimated-worklet-error',\r\n label: 'React Native Reanimated Worklet / Babel Crash',\r\n ecosystem: 'React Native / Expo',\r\n rawLog: `ERROR Error: [Reanimated] Failed to create a worklet. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#failed-to-create-a-worklet for more details.\r\n at anonymous (http://10.0.2.2:8081/index.bundle?platform=android&dev=true:114892:24)\r\n at ProductFeedScreen (src/screens/ProductFeedScreen.tsx:24:11)\r\n Babel config: plugins: ['react-native-reanimated/plugin', '@babel/plugin-transform-export-namespace-from']`,\r\n },\r\n {\r\n id: 'android-manifest-merger',\r\n label: 'Android 12+ Manifest Merger Failed (android:exported)',\r\n ecosystem: 'Android / Gradle',\r\n rawLog: `> Task :app:processDebugMainManifest FAILED\r\n/fixtures/rn-expo-commerce/android/app/src/main/AndroidManifest.xml:10:7-19:18 Error:\r\n android:exported needs to be explicitly specified for element <activity#com.devintel.expocommerce.MainActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for 'android:exported' when the corresponding component has an intent filter defined.`,\r\n },\r\n {\r\n id: 'gradle-jvm-oom',\r\n label: 'Gradle Daemon OutOfMemoryError During R8 Dexing',\r\n ecosystem: 'Android / Gradle',\r\n rawLog: `FAILURE: Build failed with an exception.\r\n* What went wrong:\r\nExecution failed for task ':app:mergeExtDexDebug'.\r\n> java.lang.OutOfMemoryError: Java heap space\r\n Configured org.gradle.jvmargs=-Xmx1024m in android/gradle.properties`,\r\n },\r\n {\r\n id: 'cocoapods-flipper-spec',\r\n label: 'CocoaPods Podfile Flipper & Deployment Target Failure',\r\n ecosystem: 'iOS / CocoaPods',\r\n rawLog: `[!] CocoaPods could not find compatible versions for pod \"EXConstants\":\r\n In Podfile:\r\n EXConstants (from '../node_modules/expo-constants/ios')\r\n Specs satisfying the EXConstants dependency were found, but they required a higher minimum deployment target (15.1) than platform :ios, '12.4'.\r\n NameError - uninitialized constant Pod::Podfile::FlipperConfiguration`,\r\n },\r\n {\r\n id: 'turbomodule-vision-camera',\r\n label: 'Native Module Linking / VisionCamera iOS Crash',\r\n ecosystem: 'React Native / iOS',\r\n rawLog: `Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.'`,\r\n },\r\n];\r\n\r\nexport function diagnoseRawErrorLog(rawLog: string): ErrorDiagnosisResult {\r\n const text = rawLog.trim();\r\n\r\n if (text.includes('Failed to create a worklet') || text.includes('react-native-reanimated')) {\r\n return {\r\n id: `diag-${Date.now()}`,\r\n category: 'Babel / Reanimated',\r\n severity: 'critical',\r\n confidence: 99,\r\n summary: 'Reanimated Babel plugin is either missing or not listed last in babel.config.js',\r\n rootCause:\r\n 'react-native-reanimated/plugin must run as the final plugin in babel.config.js so worklet functions are transformed after all other syntax plugins.',\r\n responsibleFile: 'babel.config.js',\r\n line: 5,\r\n safeRepairSteps: [\r\n \"Move 'react-native-reanimated/plugin' to the very last position of the plugins array in babel.config.js.\",\r\n 'Clear the Metro bundler transform cache so cached non-worklet bundles are evicted.',\r\n ],\r\n terminalCommands: ['npx expo start --clear', 'npx react-native start --reset-cache'],\r\n codePatchSuggestion: `plugins: [\\n '@babel/plugin-transform-export-namespace-from',\\n 'react-native-reanimated/plugin'\\n]`,\r\n };\r\n }\r\n\r\n if (text.includes('android:exported needs to be explicitly specified')) {\r\n return {\r\n id: `diag-${Date.now()}`,\r\n category: 'Android Manifest',\r\n severity: 'critical',\r\n confidence: 100,\r\n summary: 'Android 12+ (API 31+) Manifest Merger Failure: Missing android:exported attribute',\r\n rootCause:\r\n 'An <activity>, <service>, or <receiver> in AndroidManifest.xml defines an <intent-filter> without explicitly declaring android:exported=\"true\" or \"false\".',\r\n responsibleFile: 'android/app/src/main/AndroidManifest.xml',\r\n line: 10,\r\n safeRepairSteps: [\r\n 'Add android:exported=\"true\" to the <activity android:name=\".MainActivity\"> tag in AndroidManifest.xml.',\r\n 'Re-run the Android Gradle manifest check or apply the automatic SAFE fix in Auto Repair Center.',\r\n ],\r\n terminalCommands: ['cd android && ./gradlew :app:processDebugMainManifest'],\r\n codePatchSuggestion: `<activity\\n android:name=\".MainActivity\"\\n android:exported=\"true\"\\n android:launchMode=\"singleTask\">`,\r\n };\r\n }\r\n\r\n if (text.includes('OutOfMemoryError: Java heap space') || text.includes('mergeExtDex')) {\r\n return {\r\n id: `diag-${Date.now()}`,\r\n category: 'Android Gradle / Kotlin',\r\n severity: 'high',\r\n confidence: 98,\r\n summary: 'Gradle JVM Heap Exhausted During Android Dexing / R8 Compilation',\r\n rootCause:\r\n 'android/gradle.properties allocates only -Xmx1024m to the Gradle JVM daemon, which is insufficient for multi-dex React Native builds.',\r\n responsibleFile: 'android/gradle.properties',\r\n line: 1,\r\n safeRepairSteps: [\r\n 'Update org.gradle.jvmargs in android/gradle.properties to -Xmx4096m -XX:MaxMetaspaceSize=1024m.',\r\n 'Stop existing low-memory Gradle daemons before rebuilding.',\r\n ],\r\n terminalCommands: ['cd android && ./gradlew --stop', 'cd android && ./gradlew assembleDebug'],\r\n codePatchSuggestion: `org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m`,\r\n };\r\n }\r\n\r\n if (text.includes('FlipperConfiguration') || text.includes('minimum deployment target')) {\r\n return {\r\n id: `diag-${Date.now()}`,\r\n category: 'CocoaPods / iOS',\r\n severity: 'critical',\r\n confidence: 98,\r\n summary: 'iOS Podfile Deployment Target Mismatch & Deprecated FlipperConfiguration',\r\n rootCause:\r\n \"ios/Podfile specifies platform :ios, '12.4' and references FlipperConfiguration, which was removed in React Native 0.74+ / Expo SDK 52.\",\r\n responsibleFile: 'ios/Podfile',\r\n line: 1,\r\n safeRepairSteps: [\r\n \"Raise platform :ios to '15.1' in ios/Podfile.\",\r\n 'Remove :flipper_configuration => FlipperConfiguration.enabled from use_react_native!.',\r\n 'On macOS, run pod install --repo-update.',\r\n ],\r\n terminalCommands: ['cd ios && bundle exec pod install --repo-update'],\r\n codePatchSuggestion: `platform :ios, '15.1'`,\r\n };\r\n }\r\n\r\n if (text.includes('NSCameraUsageDescription')) {\r\n return {\r\n id: `diag-${Date.now()}`,\r\n category: 'CocoaPods / iOS',\r\n severity: 'critical',\r\n confidence: 100,\r\n summary: 'iOS Privacy Crash: Missing NSCameraUsageDescription in Info.plist',\r\n rootCause:\r\n 'Native camera module was invoked at runtime, but ios/Info.plist does not include the mandatory NSCameraUsageDescription privacy key.',\r\n responsibleFile: 'ios/Info.plist',\r\n line: 12,\r\n safeRepairSteps: [\r\n 'Add <key>NSCameraUsageDescription</key> and a clear user-facing purpose <string> to ios/Info.plist.',\r\n 'Rebuild the native iOS app container.',\r\n ],\r\n terminalCommands: ['npx expo run:ios'],\r\n codePatchSuggestion: `<key>NSCameraUsageDescription</key>\\n<string>Camera access is required to record workout form.</string>`,\r\n };\r\n }\r\n\r\n return {\r\n id: `diag-${Date.now()}`,\r\n category: 'Metro Bundler',\r\n severity: 'medium',\r\n confidence: 84,\r\n summary: 'Runtime / Build Exception Trace Analyzed',\r\n rootCause:\r\n 'Stack trace indicates a module resolution, native bridge, or unhandled runtime error in the target file.',\r\n responsibleFile: 'package.json / metro.config.js',\r\n line: 1,\r\n safeRepairSteps: [\r\n 'Inspect the stack frame file and line numbers reported in the trace.',\r\n 'Verify dependency compatibility in Dependency Intelligence and reset the bundler cache.',\r\n ],\r\n terminalCommands: ['npx expo start --clear'],\r\n };\r\n}\r\n", "/**\r\n * DEVINTELLIGENCE & NETCONTROL AI Engine (Powered by Groq)\r\n *\r\n * Provides ultra-fast LLM inference with resilient fallback architecture.\r\n * If the Groq API key reaches rate limits (HTTP 429), quota limits, or network timeouts,\r\n * this engine automatically and seamlessly falls back to built-in heuristic rule-based engines\r\n * so that the system NEVER crashes and operations continue 100% normally.\r\n */\r\n\r\nimport type { ErrorDiagnosisResult, IssueSeverity, ProjectIntelligenceReport } from '../types/devintelligence.ts';\r\nimport { diagnoseRawErrorLog } from './analyzerEngine.ts';\r\n\r\nconst PRIMARY_MODEL = process.env.GROQ_MODEL || 'qwen/qwen3.8-27b';\r\nconst FALLBACK_MODEL = 'openai/gpt-oss-20b';\r\n\r\n// In-memory circuit breaker to avoid hammering Groq when rate limits / quota are active\r\nlet rateLimitCooldownUntil = 0;\r\nlet lastRateLimitMessage = '';\r\n\r\nexport function getGroqApiKey(): string {\r\n return (process.env.GROQ_API_KEY || '').trim();\r\n}\r\n\r\nexport function isAiRateLimited(): boolean {\r\n return Date.now() < rateLimitCooldownUntil;\r\n}\r\n\r\nexport function resetAiRateLimit(): void {\r\n rateLimitCooldownUntil = 0;\r\n lastRateLimitMessage = '';\r\n}\r\n\r\ninterface GroqMessage {\r\n role: 'system' | 'user' | 'assistant';\r\n content: string;\r\n}\r\n\r\nexport interface GroqChatSuccess {\r\n ok: true;\r\n content: string;\r\n model: string;\r\n}\r\n\r\nexport interface GroqChatFailure {\r\n ok: false;\r\n error: string;\r\n isRateLimit: boolean;\r\n}\r\n\r\nexport type GroqChatResult = GroqChatSuccess | GroqChatFailure;\r\n\r\ninterface GroqChatOptions {\r\n model?: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n jsonMode?: boolean;\r\n}\r\n\r\n/**\r\n * Low-level caller for Groq OpenAI-compatible Chat Completions API.\r\n * Protected by timeout, circuit breaker, and graceful error trapping.\r\n */\r\nexport async function callGroqChat(\r\n messages: GroqMessage[],\r\n options: GroqChatOptions = {}\r\n): Promise<GroqChatResult> {\r\n const apiKey = getGroqApiKey();\r\n if (!apiKey || !apiKey.startsWith('gsk_')) {\r\n return { ok: false, error: 'No valid Groq API key configured (set GROQ_API_KEY environment variable)', isRateLimit: false };\r\n }\r\n\r\n // If in rate-limit cooldown, fail fast to rule fallback\r\n if (isAiRateLimited()) {\r\n return {\r\n ok: false,\r\n error: `AI Rate limit cooldown active for ${Math.ceil((rateLimitCooldownUntil - Date.now()) / 1000)}s: ${lastRateLimitMessage}`,\r\n isRateLimit: true,\r\n };\r\n }\r\n\r\n const model = options.model || PRIMARY_MODEL;\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), 12000); // 12s safety timeout\r\n\r\n try {\r\n const payload: Record<string, unknown> = {\r\n model,\r\n messages,\r\n temperature: options.temperature ?? 0.2,\r\n max_tokens: options.maxTokens ?? 1500,\r\n };\r\n\r\n if (options.jsonMode) {\r\n payload.response_format = { type: 'json_object' };\r\n }\r\n\r\n const response = await fetch('https://api.groq.com/openai/v1/chat/completions', {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${apiKey}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(payload),\r\n signal: controller.signal,\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n if (response.status === 429) {\r\n const errText = await response.text();\r\n // Set 45-second cooldown to protect user quota and prevent latency spikes\r\n rateLimitCooldownUntil = Date.now() + 45000;\r\n lastRateLimitMessage = 'Groq rate limit or quota exceeded (HTTP 429)';\r\n console.warn(`[Groq AI Shield] Rate limit reached (429). Triggered 45s cooldown. Falling back to local rule engine.`);\r\n return { ok: false, error: errText, isRateLimit: true };\r\n }\r\n\r\n if (!response.ok) {\r\n const errText = await response.text();\r\n console.warn(`[Groq AI Shield] HTTP error ${response.status}: ${errText}. Falling back to local rule engine.`);\r\n return { ok: false, error: `Groq HTTP ${response.status}: ${errText}`, isRateLimit: false };\r\n }\r\n\r\n const data = (await response.json()) as any;\r\n const content = data.choices?.[0]?.message?.content || '';\r\n return { ok: true, content, model };\r\n } catch (err: any) {\r\n clearTimeout(timeoutId);\r\n const isTimeout = err.name === 'AbortError';\r\n const errorMsg = isTimeout ? 'AI Request timed out after 12s' : err.message || 'Unknown network error';\r\n console.warn(`[Groq AI Shield] Call failed (${errorMsg}). Gracefully falling back to local rule engine.`);\r\n return { ok: false, error: errorMsg, isRateLimit: false };\r\n }\r\n}\r\n\r\n/**\r\n * AI-Powered Error Log Diagnosis with Guaranteed Rule Fallback.\r\n * If AI fails, exceeds quota, or is offline, the local heuristic rule engine\r\n * automatically generates a complete diagnosis with 0 downtime.\r\n */\r\nexport async function diagnoseRawErrorLogWithAi(rawLog: string): Promise<ErrorDiagnosisResult> {\r\n const fallbackResult = diagnoseRawErrorLog(rawLog);\r\n\r\n if (!rawLog || !rawLog.trim()) {\r\n return fallbackResult;\r\n }\r\n\r\n const systemPrompt = `You are the lead engineering diagnostic agent in DEVINTELLIGENCE.\r\nAnalyze the provided terminal error trace, Metro bundler failure, Android Gradle/Kotlin error, iOS CocoaPods error, or React Native Red Screen crash.\r\nRespond ONLY with a valid JSON object matching this TypeScript structure:\r\n{\r\n \"category\": \"Metro Bundler\" | \"Babel / Reanimated\" | \"Android Gradle / Kotlin\" | \"Android Manifest\" | \"CocoaPods / iOS\" | \"Native Module Linking\" | \"Hermes Runtime\" | \"React Render / Hook Crash\" | \"Dependency Conflict\",\r\n \"severity\": \"critical\" | \"high\" | \"medium\" | \"low\",\r\n \"confidence\": number (between 70 and 99),\r\n \"summary\": \"Short 1-sentence punchy title of the crash\",\r\n \"rootCause\": \"Detailed technical root-cause explanation of what broke and why\",\r\n \"responsibleFile\": \"File path most likely responsible (e.g. android/app/build.gradle, babel.config.js, etc.)\",\r\n \"line\": number or null,\r\n \"safeRepairSteps\": [\"step 1\", \"step 2\", \"step 3\"],\r\n \"terminalCommands\": [\"exact command to run\"],\r\n \"codePatchSuggestion\": \"exact code or config patch snippet to fix it\"\r\n}`;\r\n\r\n const userPrompt = `Diagnose this error log:\\n\\n${rawLog.slice(0, 3000)}`;\r\n\r\n try {\r\n const aiRes = await callGroqChat(\r\n [\r\n { role: 'system', content: systemPrompt },\r\n { role: 'user', content: userPrompt },\r\n ],\r\n { jsonMode: true, temperature: 0.1 }\r\n );\r\n\r\n if (!aiRes.ok) {\r\n const failure = aiRes as GroqChatFailure;\r\n return {\r\n ...fallbackResult,\r\n aiPowered: false,\r\n fallbackUsed: true,\r\n fallbackReason: failure.isRateLimit\r\n ? 'AI rate limit reached. Analysis generated seamlessly by local heuristic rule engine.'\r\n : `AI unavailable (${failure.error}). Analysis completed by local heuristic rule engine.`,\r\n };\r\n }\r\n\r\n try {\r\n const parsed = JSON.parse(aiRes.content);\r\n return {\r\n id: `ai-diag-${Date.now()}`,\r\n category: parsed.category || fallbackResult.category,\r\n severity: (['critical', 'high', 'medium', 'low'].includes(parsed.severity)\r\n ? parsed.severity\r\n : fallbackResult.severity) as IssueSeverity,\r\n confidence: typeof parsed.confidence === 'number' ? Math.min(Math.max(parsed.confidence, 60), 99) : 95,\r\n summary: parsed.summary || fallbackResult.summary,\r\n rootCause: parsed.rootCause || fallbackResult.rootCause,\r\n responsibleFile: parsed.responsibleFile || fallbackResult.responsibleFile,\r\n line: typeof parsed.line === 'number' ? parsed.line : fallbackResult.line,\r\n safeRepairSteps: Array.isArray(parsed.safeRepairSteps) && parsed.safeRepairSteps.length > 0\r\n ? parsed.safeRepairSteps\r\n : fallbackResult.safeRepairSteps,\r\n terminalCommands: Array.isArray(parsed.terminalCommands)\r\n ? parsed.terminalCommands\r\n : fallbackResult.terminalCommands,\r\n codePatchSuggestion: parsed.codePatchSuggestion || fallbackResult.codePatchSuggestion,\r\n aiPowered: true,\r\n aiModel: aiRes.model,\r\n fallbackUsed: false,\r\n };\r\n } catch (parseErr) {\r\n console.warn('[Groq AI] JSON parse error in AI diagnosis, falling back to rule engine:', parseErr);\r\n return {\r\n ...fallbackResult,\r\n aiPowered: false,\r\n fallbackUsed: true,\r\n fallbackReason: 'AI response parse failed. Analysis completed by local heuristic rule engine.',\r\n };\r\n }\r\n } catch (err: any) {\r\n return {\r\n ...fallbackResult,\r\n aiPowered: false,\r\n fallbackUsed: true,\r\n fallbackReason: `Local rule fallback active: ${err.message || 'AI request interrupted'}`,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Generates an AI-driven Executive Engineering Brief for a project.\r\n * Falls back to deterministic rule summary if quota is exhausted.\r\n */\r\nexport async function generateProjectAiAdvice(\r\n report: ProjectIntelligenceReport\r\n): Promise<{ advice: string; aiPowered: boolean; model?: string }> {\r\n const issuesSample = report.issues.slice(0, 8).map((i) => ({\r\n title: i.title,\r\n severity: i.severity,\r\n domain: i.domain,\r\n file: `${i.filePath}:${i.line}`,\r\n explanation: i.explanation,\r\n }));\r\n\r\n const systemPrompt = `You are a Principal Mobile & Fullstack Architect reviewing a code scan report.\r\nProvide a concise, 3-paragraph executive engineering assessment:\r\n1. Overall architecture and health overview based on scores and ecosystem.\r\n2. Immediate critical threats (security, native crashes, memory leaks).\r\n3. Exact prioritized 3-step action plan to reach 100/100 Quality Gate.\r\nKeep it strictly technical, direct, and actionable. Do not use generic filler.`;\r\n\r\n const userPrompt = `Project: ${report.name} (${report.ecosystem})\r\nScores: Overall: ${report.scores.overall}/100, Security: ${report.scores.security}/100, Performance: ${report.scores.performance}/100, RN: ${report.scores.reactNative}/100\r\nIssues Count: ${report.issues.length}\r\nKey Issues:\r\n${JSON.stringify(issuesSample, null, 2)}`;\r\n\r\n const aiRes = await callGroqChat(\r\n [\r\n { role: 'system', content: systemPrompt },\r\n { role: 'user', content: userPrompt },\r\n ],\r\n { temperature: 0.3, maxTokens: 800 }\r\n );\r\n\r\n if (aiRes.ok && aiRes.content.trim()) {\r\n return {\r\n advice: aiRes.content.trim(),\r\n aiPowered: true,\r\n model: aiRes.model,\r\n };\r\n }\r\n\r\n // Graceful rule-based fallback advice\r\n const criticals = report.issues.filter((i) => i.severity === 'critical');\r\n const safeRepairs = report.issues.filter((i) => i.repairTier === 'SAFE');\r\n\r\n const fallbackAdvice = `### Deterministic Engineering Assessment (Rule Engine Fallback)\r\n\r\n1. **Architecture Status**: Project **${report.name}** is operating on **${report.ecosystem}** with an overall score of **${report.scores.overall}/100**.\r\n${criticals.length > 0 ? `\uD83D\uDEA8 **Critical Attention Needed**: Detected ${criticals.length} critical issues including ${criticals.map((c) => c.title).slice(0, 2).join(' and ')}.` : '\u2705 No critical blocker violations found.'}\r\n\r\n2. **Automated Remediation Opportunity**: **${safeRepairs.length} of ${report.issues.length}** detected issues have validated SAFE auto-repair patches ready to apply with zero regression risk.\r\n\r\n3. **Recommended 3-Step Action Plan**:\r\n - Run safe batch remediation: \\`devintelligence fix --project=${report.id}\\`\r\n - Review security and authentication tokens in \\`Security Audit\\`\r\n - Check bundler cache and verify Quality Gate threshold compliance.`;\r\n\r\n return {\r\n advice: fallbackAdvice,\r\n aiPowered: false,\r\n };\r\n}\r\n\r\n/**\r\n * General AI Assistant for interactive developer queries or network troubleshooting.\r\n */\r\nexport async function askDevIntelligenceAi(\r\n query: string,\r\n contextData?: unknown\r\n): Promise<{ answer: string; aiPowered: boolean; model?: string }> {\r\n if (!query || !query.trim()) {\r\n return { answer: 'Please enter a query.', aiPowered: false };\r\n }\r\n\r\n const systemPrompt = `You are the DEVINTELLIGENCE Engineering AI Assistant.\r\nYou specialize in:\r\n- React Native, Expo, Android Gradle, Kotlin, iOS CocoaPods, Swift, Metro bundler.\r\n- Next.js, React, Node.js, Express, Python Django, TypeScript, Vite.\r\n- Static code analysis, security auditing, and automated safe code repair.\r\nProvide clear, authoritative, copy-paste ready technical instructions.`;\r\n\r\n const userContent = contextData\r\n ? `Context:\\n${JSON.stringify(contextData, null, 2)}\\n\\nUser Question:\\n${query}`\r\n : query;\r\n\r\n const aiRes = await callGroqChat(\r\n [\r\n { role: 'system', content: systemPrompt },\r\n { role: 'user', content: userContent },\r\n ],\r\n { temperature: 0.2, maxTokens: 1200 }\r\n );\r\n\r\n if (aiRes.ok && aiRes.content.trim()) {\r\n return {\r\n answer: aiRes.content.trim(),\r\n aiPowered: true,\r\n model: aiRes.model,\r\n };\r\n }\r\n\r\n // Graceful rule-based guidance fallback\r\n return {\r\n answer: `### Local Engineering Assistant (Rule Fallback)\r\n\r\nAI inference is currently running in offline rule mode (rate limit or connection limit).\r\n- For build errors: Try cleaning cache with \\`npx expo start --clear\\` or \\`cd android && ./gradlew clean\\`.\r\n- For project issues: Run \\`devintelligence scan\\` or check the Auto Repair Center.\r\n- For environment issues: Run \\`devintelligence doctor\\`.`,\r\n aiPowered: false,\r\n };\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|