devintelligence 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.env.example +14 -0
  2. package/LICENSE +22 -0
  3. package/README.md +145 -2
  4. package/bin/devintelligence.js +17 -0
  5. package/bin/devintelligence.ts +275 -0
  6. package/fixtures/django-analytics-service/requirements.txt +4 -0
  7. package/fixtures/django-analytics-service/settings.py +16 -0
  8. package/fixtures/express-api-gateway/package.json +14 -0
  9. package/fixtures/express-api-gateway/src/server.ts +16 -0
  10. package/fixtures/nextjs-saas-portal/package.json +21 -0
  11. package/fixtures/nextjs-saas-portal/src/app/dashboard/page.tsx +26 -0
  12. package/fixtures/rn-bare-fitness/android/build.gradle +19 -0
  13. package/fixtures/rn-bare-fitness/ios/Info.plist +14 -0
  14. package/fixtures/rn-bare-fitness/package.json +23 -0
  15. package/fixtures/rn-bare-fitness/src/WorkoutTracker.tsx +31 -0
  16. package/fixtures/rn-expo-commerce/android/app/build.gradle +20 -0
  17. package/fixtures/rn-expo-commerce/android/app/src/main/AndroidManifest.xml +21 -0
  18. package/fixtures/rn-expo-commerce/android/gradle.properties +6 -0
  19. package/fixtures/rn-expo-commerce/app.json +17 -0
  20. package/fixtures/rn-expo-commerce/babel.config.js +10 -0
  21. package/fixtures/rn-expo-commerce/ios/Podfile +13 -0
  22. package/fixtures/rn-expo-commerce/metro.config.js +7 -0
  23. package/fixtures/rn-expo-commerce/package.json +31 -0
  24. package/fixtures/rn-expo-commerce/src/components/LegacyPromoBanner.tsx +11 -0
  25. package/fixtures/rn-expo-commerce/src/screens/ProductFeedScreen.tsx +48 -0
  26. package/fixtures/rn-expo-commerce/src/services/api.ts +15 -0
  27. package/index.html +21 -0
  28. package/package.json +75 -4
  29. package/server.ts +275 -0
  30. package/src/App.tsx +783 -0
  31. package/src/components/DeepAnalysisViews.tsx +526 -0
  32. package/src/components/MobileAndPlatformViews.tsx +625 -0
  33. package/src/components/OverviewAndExplorerViews.tsx +753 -0
  34. package/src/components/RepairAndGateViews.tsx +814 -0
  35. package/src/engine/aiEngine.test.ts +86 -0
  36. package/src/engine/aiEngine.ts +346 -0
  37. package/src/engine/analyzerEngine.test.ts +110 -0
  38. package/src/engine/analyzerEngine.ts +1579 -0
  39. package/src/engine/repairEngine.ts +545 -0
  40. package/src/index.css +44 -0
  41. package/src/index.ts +59 -0
  42. package/src/main.tsx +5 -0
  43. package/src/types/devintelligence.ts +310 -0
  44. package/tsconfig.json +28 -0
  45. package/vite.config.ts +22 -0
package/src/App.tsx ADDED
@@ -0,0 +1,783 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import {
3
+ LayoutDashboard,
4
+ FolderSearch,
5
+ Activity,
6
+ Smartphone,
7
+ Cpu,
8
+ Layers,
9
+ Code2,
10
+ Palette,
11
+ Gauge,
12
+ ShieldAlert,
13
+ PackageCheck,
14
+ Bug,
15
+ Wrench,
16
+ Eye,
17
+ CheckSquare,
18
+ History,
19
+ Server,
20
+ Settings,
21
+ RefreshCw,
22
+ Menu,
23
+ X,
24
+ } from 'lucide-react';
25
+ import {
26
+ DashboardModuleId,
27
+ EnvironmentCheckItem,
28
+ ErrorDiagnosisResult,
29
+ ProjectIntelligenceReport,
30
+ QualityGateConfig,
31
+ } from './types/devintelligence.ts';
32
+ import {
33
+ CommandCenterView,
34
+ ProjectExplorerView,
35
+ ProjectHealthView,
36
+ } from './components/OverviewAndExplorerViews.tsx';
37
+ import {
38
+ ReactNativeDoctorView,
39
+ AndroidDiagnosticsView,
40
+ IosDiagnosticsView,
41
+ ErrorDiagnosticsView,
42
+ } from './components/MobileAndPlatformViews.tsx';
43
+ import {
44
+ CodeIntelligenceView,
45
+ DesignIntelligenceView,
46
+ PerformanceLabView,
47
+ SecurityAuditView,
48
+ DependencyIntelligenceView,
49
+ } from './components/DeepAnalysisViews.tsx';
50
+ import {
51
+ AutoRepairCenterView,
52
+ VisualTestingView,
53
+ QualityGateView,
54
+ ProjectHistoryView,
55
+ EnvironmentManagerView,
56
+ SettingsAndMcpView,
57
+ } from './components/RepairAndGateViews.tsx';
58
+
59
+ interface NavItem {
60
+ id: DashboardModuleId;
61
+ label: string;
62
+ group: 'Core' | 'Mobile & Native' | 'Deep Analysis' | 'Repair & Delivery';
63
+ icon: React.ComponentType<{ className?: string }>;
64
+ }
65
+
66
+ const NAVIGATION_MODULES: NavItem[] = [
67
+ { id: 'command-center', label: 'Command Center', group: 'Core', icon: LayoutDashboard },
68
+ { id: 'project-explorer', label: 'Project Explorer', group: 'Core', icon: FolderSearch },
69
+ { id: 'project-health', label: 'Project Health', group: 'Core', icon: Activity },
70
+ { id: 'rn-doctor', label: 'React Native Doctor', group: 'Mobile & Native', icon: Smartphone },
71
+ { id: 'android-diagnostics', label: 'Android Diagnostics', group: 'Mobile & Native', icon: Cpu },
72
+ { id: 'ios-diagnostics', label: 'iOS Diagnostics', group: 'Mobile & Native', icon: Layers },
73
+ { id: 'code-intelligence', label: 'Code Intelligence', group: 'Deep Analysis', icon: Code2 },
74
+ { id: 'design-intelligence', label: 'Design Intelligence', group: 'Deep Analysis', icon: Palette },
75
+ { id: 'performance-lab', label: 'Performance Lab', group: 'Deep Analysis', icon: Gauge },
76
+ { id: 'security-audit', label: 'Security Audit', group: 'Deep Analysis', icon: ShieldAlert },
77
+ { id: 'dependency-intelligence', label: 'Dependency Intelligence', group: 'Deep Analysis', icon: PackageCheck },
78
+ { id: 'error-diagnostics', label: 'Error Diagnostics', group: 'Repair & Delivery', icon: Bug },
79
+ { id: 'auto-repair', label: 'Auto Repair Center', group: 'Repair & Delivery', icon: Wrench },
80
+ { id: 'visual-testing', label: 'Visual Testing', group: 'Repair & Delivery', icon: Eye },
81
+ { id: 'quality-gate', label: 'Quality Gate', group: 'Repair & Delivery', icon: CheckSquare },
82
+ { id: 'project-history', label: 'Project History', group: 'Repair & Delivery', icon: History },
83
+ { id: 'environment-manager', label: 'Environment Manager', group: 'Repair & Delivery', icon: Server },
84
+ { id: 'settings', label: 'Settings', group: 'Repair & Delivery', icon: Settings },
85
+ ];
86
+
87
+ export function App() {
88
+ const [projects, setProjects] = useState<ProjectIntelligenceReport[]>([]);
89
+ const [selectedProjectId, setSelectedProjectId] = useState<string>('rn-expo-commerce');
90
+ const [activeModule, setActiveModule] = useState<DashboardModuleId>('command-center');
91
+ const [environmentChecks, setEnvironmentChecks] = useState<EnvironmentCheckItem[]>([]);
92
+ const [mcpTools, setMcpTools] = useState<
93
+ { name: string; description: string; parameters: string[] }[]
94
+ >([]);
95
+ const [presetErrorLogs, setPresetErrorLogs] = useState<
96
+ { id: string; label: string; ecosystem: string; rawLog: string }[]
97
+ >([]);
98
+ const [loading, setLoading] = useState<boolean>(true);
99
+ const [isBusy, setIsBusy] = useState<boolean>(false);
100
+ const [statusMessage, setStatusMessage] = useState<string | null>(null);
101
+ const [mobileNavOpen, setMobileNavOpen] = useState<boolean>(false);
102
+
103
+ const loadWorkspace = useCallback(async () => {
104
+ try {
105
+ const res = await fetch('/api/devintel/workspace');
106
+ const data = await res.json();
107
+ if (Array.isArray(data.projects)) {
108
+ setProjects(data.projects);
109
+ }
110
+ if (Array.isArray(data.environmentChecks)) {
111
+ setEnvironmentChecks(data.environmentChecks);
112
+ }
113
+ if (Array.isArray(data.mcpTools)) {
114
+ setMcpTools(data.mcpTools);
115
+ }
116
+ if (Array.isArray(data.presetErrorLogs)) {
117
+ setPresetErrorLogs(data.presetErrorLogs);
118
+ }
119
+ } catch (err) {
120
+ console.error('Failed to load DEVINTELLIGENCE workspace:', err);
121
+ } finally {
122
+ setLoading(false);
123
+ }
124
+ }, []);
125
+
126
+ useEffect(() => {
127
+ loadWorkspace();
128
+ }, [loadWorkspace]);
129
+
130
+ const activeReport =
131
+ projects.find((p) => p.id === selectedProjectId) || projects[0] || null;
132
+
133
+ async function handleRunScan() {
134
+ if (!activeReport) return;
135
+ setIsBusy(true);
136
+ setStatusMessage(`Scanning ${activeReport.name}...`);
137
+ try {
138
+ const res = await fetch(`/api/devintel/projects/${activeReport.id}/scan`, {
139
+ method: 'POST',
140
+ });
141
+ const data = await res.json();
142
+ if (Array.isArray(data.projects)) {
143
+ setProjects(data.projects);
144
+ }
145
+ setStatusMessage(
146
+ `Scan completed in ${data.report?.scanDurationMs || 15}ms — ${data.report?.issues?.length ?? 0} findings verified.`
147
+ );
148
+ } finally {
149
+ setIsBusy(false);
150
+ }
151
+ }
152
+
153
+ async function handleFixIssue(issueId: string, allowReview = false) {
154
+ if (!activeReport) return;
155
+ setIsBusy(true);
156
+ setStatusMessage('Creating backup snapshot and applying repair...');
157
+ try {
158
+ const res = await fetch(`/api/devintel/projects/${activeReport.id}/repair`, {
159
+ method: 'POST',
160
+ headers: { 'Content-Type': 'application/json' },
161
+ body: JSON.stringify({ issueId, allowReviewTier: allowReview }),
162
+ });
163
+ const data = await res.json();
164
+ if (!res.ok) {
165
+ setStatusMessage(`Repair blocked: ${data.error}`);
166
+ return;
167
+ }
168
+ if (Array.isArray(data.projects)) {
169
+ setProjects(data.projects);
170
+ }
171
+ setStatusMessage(
172
+ `Repaired "${data.repairRecord.issueTitle}" and verified fix via re-scan.`
173
+ );
174
+ } finally {
175
+ setIsBusy(false);
176
+ }
177
+ }
178
+
179
+ async function handleFixAllSafe() {
180
+ if (!activeReport) return;
181
+ setIsBusy(true);
182
+ setStatusMessage('Applying all SAFE repairs with automatic disk backups...');
183
+ try {
184
+ const res = await fetch(
185
+ `/api/devintel/projects/${activeReport.id}/repair-all-safe`,
186
+ { method: 'POST' }
187
+ );
188
+ const data = await res.json();
189
+ if (Array.isArray(data.projects)) {
190
+ setProjects(data.projects);
191
+ }
192
+ setStatusMessage(
193
+ `Applied ${data.appliedRepairs?.length || 0} SAFE repair(s) and verified on disk.`
194
+ );
195
+ } finally {
196
+ setIsBusy(false);
197
+ }
198
+ }
199
+
200
+ async function handleRollbackRepair(repairId: string) {
201
+ if (!activeReport) return;
202
+ setIsBusy(true);
203
+ setStatusMessage('Restoring original file from backup snapshot...');
204
+ try {
205
+ const res = await fetch(`/api/devintel/projects/${activeReport.id}/rollback`, {
206
+ method: 'POST',
207
+ headers: { 'Content-Type': 'application/json' },
208
+ body: JSON.stringify({ repairId }),
209
+ });
210
+ const data = await res.json();
211
+ if (Array.isArray(data.projects)) {
212
+ setProjects(data.projects);
213
+ }
214
+ setStatusMessage(
215
+ `Rolled back "${data.restoredRecord?.issueTitle}" and restored original file.`
216
+ );
217
+ } finally {
218
+ setIsBusy(false);
219
+ }
220
+ }
221
+
222
+ async function handleRegisterProject(name: string, rootPath: string) {
223
+ setIsBusy(true);
224
+ try {
225
+ const res = await fetch('/api/devintel/projects/register', {
226
+ method: 'POST',
227
+ headers: { 'Content-Type': 'application/json' },
228
+ body: JSON.stringify({ name, rootPath }),
229
+ });
230
+ const data = await res.json();
231
+ if (!res.ok) {
232
+ throw new Error(data.error || 'Failed to register project');
233
+ }
234
+ if (Array.isArray(data.projects)) {
235
+ setProjects(data.projects);
236
+ }
237
+ if (data.report?.id) {
238
+ setSelectedProjectId(data.report.id);
239
+ }
240
+ setStatusMessage(`Registered and scanned "${name}" (${data.report?.filesAnalyzed?.length || 0} files).`);
241
+ } finally {
242
+ setIsBusy(false);
243
+ }
244
+ }
245
+
246
+ async function handleUpdateQualityGate(cfg: Partial<QualityGateConfig>) {
247
+ if (!activeReport) return;
248
+ setIsBusy(true);
249
+ try {
250
+ const res = await fetch(
251
+ `/api/devintel/projects/${activeReport.id}/quality-gate`,
252
+ {
253
+ method: 'PUT',
254
+ headers: { 'Content-Type': 'application/json' },
255
+ body: JSON.stringify(cfg),
256
+ }
257
+ );
258
+ const data = await res.json();
259
+ if (Array.isArray(data.projects)) {
260
+ setProjects(data.projects);
261
+ }
262
+ setStatusMessage(
263
+ `Updated Quality Gate thresholds — Status: ${data.updatedReport?.qualityGateEvaluation?.passed ? 'PASSING' : 'FAILING'}`
264
+ );
265
+ } finally {
266
+ setIsBusy(false);
267
+ }
268
+ }
269
+
270
+ async function handleDiagnoseLog(rawLog: string): Promise<ErrorDiagnosisResult> {
271
+ const res = await fetch('/api/devintel/diagnose-log', {
272
+ method: 'POST',
273
+ headers: { 'Content-Type': 'application/json' },
274
+ body: JSON.stringify({ rawLog }),
275
+ });
276
+ return res.json();
277
+ }
278
+
279
+ async function handleRefreshEnvironment() {
280
+ setIsBusy(true);
281
+ try {
282
+ const res = await fetch('/api/devintel/environment/refresh', {
283
+ method: 'POST',
284
+ });
285
+ const data = await res.json();
286
+ if (Array.isArray(data.environmentChecks)) {
287
+ setEnvironmentChecks(data.environmentChecks);
288
+ }
289
+ setStatusMessage('Re-probed host operating system and developer binaries.');
290
+ } finally {
291
+ setIsBusy(false);
292
+ }
293
+ }
294
+
295
+ async function handleCallMcpTool(
296
+ toolName: string,
297
+ args: Record<string, string>
298
+ ): Promise<unknown> {
299
+ const res = await fetch('/api/devintel/mcp/call', {
300
+ method: 'POST',
301
+ headers: { 'Content-Type': 'application/json' },
302
+ body: JSON.stringify({ toolName, args }),
303
+ });
304
+ const data = await res.json();
305
+ if (Array.isArray(data.projects)) {
306
+ setProjects(data.projects);
307
+ }
308
+ return data.output;
309
+ }
310
+
311
+ if (loading || !activeReport) {
312
+ return (
313
+ <div className="min-h-screen bg-[#090D16] text-slate-100 flex items-center justify-center p-6">
314
+ <div className="text-center space-y-2">
315
+ <div className="text-sm font-semibold tracking-wide text-slate-200">
316
+ DEVINTELLIGENCE
317
+ </div>
318
+ <div className="text-xs text-slate-400 font-mono">
319
+ Initializing local engineering intelligence engines...
320
+ </div>
321
+ </div>
322
+ </div>
323
+ );
324
+ }
325
+
326
+ const groups = ['Core', 'Mobile & Native', 'Deep Analysis', 'Repair & Delivery'] as const;
327
+
328
+ return (
329
+ <div className="min-h-screen bg-[#090D16] text-slate-100 flex flex-col">
330
+ {/* Top Bar Contract: 3 Zones Separated by gap-8 */}
331
+ <header className="flex items-center justify-between gap-8 px-6 py-3.5 bg-[#090D16] border-b border-slate-800/90 sticky top-0 z-30">
332
+ {/* Zone 1: Single text element wordmark */}
333
+ <a
334
+ href="#top"
335
+ onClick={(e) => {
336
+ e.preventDefault();
337
+ setActiveModule('command-center');
338
+ }}
339
+ className="text-base font-bold tracking-tight text-slate-100 whitespace-nowrap shrink-0"
340
+ >
341
+ DEVINTELLIGENCE
342
+ </a>
343
+
344
+ {/* Zone 2: 5 concise single-line text navigation links */}
345
+ <nav
346
+ aria-label="Primary Shortcuts"
347
+ className="hidden md:flex items-center gap-6 text-xs font-medium text-slate-400"
348
+ >
349
+ <a
350
+ href="#command-center"
351
+ onClick={(e) => {
352
+ e.preventDefault();
353
+ setActiveModule('command-center');
354
+ }}
355
+ className={`hover:text-slate-100 hover:underline underline-offset-4 transition-colors whitespace-nowrap shrink-0 ${
356
+ activeModule === 'command-center' ? 'text-slate-100' : ''
357
+ }`}
358
+ >
359
+ Command Center
360
+ </a>
361
+ <a
362
+ href="#rn-doctor"
363
+ onClick={(e) => {
364
+ e.preventDefault();
365
+ setActiveModule('rn-doctor');
366
+ }}
367
+ className={`hover:text-slate-100 hover:underline underline-offset-4 transition-colors whitespace-nowrap shrink-0 ${
368
+ activeModule === 'rn-doctor' ? 'text-slate-100' : ''
369
+ }`}
370
+ >
371
+ React Native Doctor
372
+ </a>
373
+ <a
374
+ href="#auto-repair"
375
+ onClick={(e) => {
376
+ e.preventDefault();
377
+ setActiveModule('auto-repair');
378
+ }}
379
+ className={`hover:text-slate-100 hover:underline underline-offset-4 transition-colors whitespace-nowrap shrink-0 ${
380
+ activeModule === 'auto-repair' ? 'text-slate-100' : ''
381
+ }`}
382
+ >
383
+ Auto Repair Center
384
+ </a>
385
+ <a
386
+ href="#error-diagnostics"
387
+ onClick={(e) => {
388
+ e.preventDefault();
389
+ setActiveModule('error-diagnostics');
390
+ }}
391
+ className={`hover:text-slate-100 hover:underline underline-offset-4 transition-colors whitespace-nowrap shrink-0 ${
392
+ activeModule === 'error-diagnostics' ? 'text-slate-100' : ''
393
+ }`}
394
+ >
395
+ Error Diagnostics
396
+ </a>
397
+ <a
398
+ href="#quality-gate"
399
+ onClick={(e) => {
400
+ e.preventDefault();
401
+ setActiveModule('quality-gate');
402
+ }}
403
+ className={`hover:text-slate-100 hover:underline underline-offset-4 transition-colors whitespace-nowrap shrink-0 ${
404
+ activeModule === 'quality-gate' ? 'text-slate-100' : ''
405
+ }`}
406
+ >
407
+ Quality Gate
408
+ </a>
409
+ </nav>
410
+
411
+ {/* Zone 3: 1 Primary Action */}
412
+ <div className="flex items-center gap-3 shrink-0">
413
+ <button
414
+ type="button"
415
+ disabled={isBusy}
416
+ onClick={handleRunScan}
417
+ className="px-4 py-2 text-xs font-semibold text-white bg-blue-600 hover:bg-blue-500 disabled:opacity-50 rounded-lg transition-colors whitespace-nowrap shrink-0 cursor-pointer"
418
+ >
419
+ {isBusy ? 'Scanning...' : 'Run Deep Scan'}
420
+ </button>
421
+ </div>
422
+ </header>
423
+
424
+ {/* Main Workstation Layout: Sidebar + Content Viewport */}
425
+ <div className="flex-1 flex min-h-0">
426
+ {/* Sidebar Navigation (Desktop 256px) */}
427
+ <aside
428
+ aria-label="Workstation Modules"
429
+ className="hidden lg:flex lg:flex-col w-64 shrink-0 bg-[#0B101B] border-r border-slate-800/80 py-5 px-3 space-y-6 overflow-y-auto"
430
+ >
431
+ <div className="px-3 space-y-1.5">
432
+ <label
433
+ htmlFor="sidebar-project-select"
434
+ className="block text-[11px] font-medium text-slate-400"
435
+ >
436
+ Active Local Project
437
+ </label>
438
+ <select
439
+ id="sidebar-project-select"
440
+ value={activeReport.id}
441
+ onChange={(e) => setSelectedProjectId(e.target.value)}
442
+ className="w-full px-2.5 py-2 text-xs font-medium bg-slate-900 border border-slate-800 rounded-md text-slate-100 focus:outline-none focus:border-blue-500"
443
+ >
444
+ {projects.map((p) => (
445
+ <option key={p.id} value={p.id}>
446
+ {p.name}
447
+ </option>
448
+ ))}
449
+ </select>
450
+ <div className="text-[11px] text-slate-500 font-mono pt-0.5">
451
+ Understand. Diagnose. Repair. Optimize.
452
+ </div>
453
+ </div>
454
+
455
+ {groups.map((grp) => (
456
+ <div key={grp} className="space-y-1">
457
+ <div className="px-3 text-[11px] font-medium text-slate-500">
458
+ {grp}
459
+ </div>
460
+ {NAVIGATION_MODULES.filter((m) => m.group === grp).map((mod) => {
461
+ const Icon = mod.icon;
462
+ const isActive = activeModule === mod.id;
463
+ return (
464
+ <button
465
+ key={mod.id}
466
+ type="button"
467
+ onClick={() => setActiveModule(mod.id)}
468
+ className={`w-full flex items-center gap-2.5 px-3 py-2 rounded-md text-xs font-medium transition-colors cursor-pointer whitespace-nowrap ${
469
+ isActive
470
+ ? 'bg-blue-600/15 text-blue-400'
471
+ : 'text-slate-400 hover:text-slate-100 hover:bg-slate-800/50'
472
+ }`}
473
+ >
474
+ <Icon className="w-4 h-4 shrink-0" />
475
+ <span className="truncate">{mod.label}</span>
476
+ </button>
477
+ );
478
+ })}
479
+ </div>
480
+ ))}
481
+ </aside>
482
+
483
+ {/* Main Workspace Content Area */}
484
+ <div className="flex-1 flex flex-col min-w-0">
485
+ {/* Contextual Sub-Toolbar for Mobile Navigation & Active Project Context */}
486
+ <div className="px-6 py-3 bg-[#0B101B] border-b border-slate-800/80 flex flex-wrap items-center justify-between gap-3">
487
+ <div className="flex items-center gap-3">
488
+ <button
489
+ type="button"
490
+ aria-label="Toggle Modules Menu"
491
+ onClick={() => setMobileNavOpen(!mobileNavOpen)}
492
+ className="lg:hidden inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-slate-200 bg-slate-900 border border-slate-800 rounded"
493
+ >
494
+ {mobileNavOpen ? <X className="w-4 h-4" /> : <Menu className="w-4 h-4" />}
495
+ <span>Modules</span>
496
+ </button>
497
+
498
+ <div className="text-xs text-slate-400 font-mono">
499
+ <span className="text-slate-300 font-semibold">
500
+ {NAVIGATION_MODULES.find((m) => m.id === activeModule)?.label}
501
+ </span>
502
+ <span className="mx-2 text-slate-600">/</span>
503
+ <span>{activeReport.id}</span>
504
+ </div>
505
+ </div>
506
+
507
+ <div className="flex items-center gap-3 min-w-0">
508
+ {statusMessage && (
509
+ <div
510
+ role="status"
511
+ className="text-xs text-blue-300 font-mono truncate max-w-[240px] sm:max-w-md"
512
+ >
513
+ {statusMessage}
514
+ </div>
515
+ )}
516
+ <div className="lg:hidden">
517
+ <label htmlFor="mobile-project-select" className="sr-only">
518
+ Select Active Project
519
+ </label>
520
+ <select
521
+ id="mobile-project-select"
522
+ value={activeReport.id}
523
+ onChange={(e) => setSelectedProjectId(e.target.value)}
524
+ className="px-2.5 py-1.5 text-xs bg-slate-900 border border-slate-800 rounded text-slate-100"
525
+ >
526
+ {projects.map((p) => (
527
+ <option key={p.id} value={p.id}>
528
+ {p.name}
529
+ </option>
530
+ ))}
531
+ </select>
532
+ </div>
533
+ </div>
534
+ </div>
535
+
536
+ {/* Mobile Drawer Navigation */}
537
+ {mobileNavOpen && (
538
+ <div className="lg:hidden bg-slate-900 border-b border-slate-800 p-4 grid grid-cols-2 sm:grid-cols-3 gap-2">
539
+ {NAVIGATION_MODULES.map((mod) => {
540
+ const Icon = mod.icon;
541
+ return (
542
+ <button
543
+ key={mod.id}
544
+ type="button"
545
+ onClick={() => {
546
+ setActiveModule(mod.id);
547
+ setMobileNavOpen(false);
548
+ }}
549
+ className={`flex items-center gap-2 px-3 py-2 rounded text-xs font-medium text-left ${
550
+ activeModule === mod.id
551
+ ? 'bg-blue-600 text-white'
552
+ : 'text-slate-300 hover:bg-slate-800'
553
+ }`}
554
+ >
555
+ <Icon className="w-3.5 h-3.5 shrink-0" />
556
+ <span className="truncate">{mod.label}</span>
557
+ </button>
558
+ );
559
+ })}
560
+ </div>
561
+ )}
562
+
563
+ {/* Active Module Viewport */}
564
+ <main className="flex-1 p-6 lg:p-8 max-w-7xl w-full mx-auto">
565
+ {activeModule === 'command-center' && (
566
+ <CommandCenterView
567
+ report={activeReport}
568
+ allProjects={projects}
569
+ onSelectProject={setSelectedProjectId}
570
+ onNavigateModule={setActiveModule}
571
+ onRunScan={handleRunScan}
572
+ onFixAllSafe={handleFixAllSafe}
573
+ onFixIssue={handleFixIssue}
574
+ onRegisterProject={handleRegisterProject}
575
+ isBusy={isBusy}
576
+ />
577
+ )}
578
+
579
+ {activeModule === 'project-explorer' && (
580
+ <ProjectExplorerView
581
+ report={activeReport}
582
+ allProjects={projects}
583
+ onSelectProject={setSelectedProjectId}
584
+ onNavigateModule={setActiveModule}
585
+ onRunScan={handleRunScan}
586
+ onFixAllSafe={handleFixAllSafe}
587
+ onFixIssue={handleFixIssue}
588
+ onRegisterProject={handleRegisterProject}
589
+ isBusy={isBusy}
590
+ />
591
+ )}
592
+
593
+ {activeModule === 'project-health' && (
594
+ <ProjectHealthView
595
+ report={activeReport}
596
+ allProjects={projects}
597
+ onSelectProject={setSelectedProjectId}
598
+ onNavigateModule={setActiveModule}
599
+ onRunScan={handleRunScan}
600
+ onFixAllSafe={handleFixAllSafe}
601
+ onFixIssue={handleFixIssue}
602
+ onRegisterProject={handleRegisterProject}
603
+ isBusy={isBusy}
604
+ />
605
+ )}
606
+
607
+ {activeModule === 'rn-doctor' && (
608
+ <ReactNativeDoctorView
609
+ report={activeReport}
610
+ onFixIssue={handleFixIssue}
611
+ presetErrorLogs={presetErrorLogs}
612
+ onDiagnoseLog={handleDiagnoseLog}
613
+ isBusy={isBusy}
614
+ />
615
+ )}
616
+
617
+ {activeModule === 'android-diagnostics' && (
618
+ <AndroidDiagnosticsView
619
+ report={activeReport}
620
+ onFixIssue={handleFixIssue}
621
+ presetErrorLogs={presetErrorLogs}
622
+ onDiagnoseLog={handleDiagnoseLog}
623
+ isBusy={isBusy}
624
+ />
625
+ )}
626
+
627
+ {activeModule === 'ios-diagnostics' && (
628
+ <IosDiagnosticsView
629
+ report={activeReport}
630
+ onFixIssue={handleFixIssue}
631
+ presetErrorLogs={presetErrorLogs}
632
+ onDiagnoseLog={handleDiagnoseLog}
633
+ isBusy={isBusy}
634
+ />
635
+ )}
636
+
637
+ {activeModule === 'code-intelligence' && (
638
+ <CodeIntelligenceView
639
+ report={activeReport}
640
+ onFixIssue={handleFixIssue}
641
+ isBusy={isBusy}
642
+ />
643
+ )}
644
+
645
+ {activeModule === 'design-intelligence' && (
646
+ <DesignIntelligenceView
647
+ report={activeReport}
648
+ onFixIssue={handleFixIssue}
649
+ isBusy={isBusy}
650
+ />
651
+ )}
652
+
653
+ {activeModule === 'performance-lab' && (
654
+ <PerformanceLabView
655
+ report={activeReport}
656
+ onFixIssue={handleFixIssue}
657
+ isBusy={isBusy}
658
+ />
659
+ )}
660
+
661
+ {activeModule === 'security-audit' && (
662
+ <SecurityAuditView
663
+ report={activeReport}
664
+ onFixIssue={handleFixIssue}
665
+ isBusy={isBusy}
666
+ />
667
+ )}
668
+
669
+ {activeModule === 'dependency-intelligence' && (
670
+ <DependencyIntelligenceView
671
+ report={activeReport}
672
+ onFixIssue={handleFixIssue}
673
+ isBusy={isBusy}
674
+ />
675
+ )}
676
+
677
+ {activeModule === 'error-diagnostics' && (
678
+ <ErrorDiagnosticsView
679
+ report={activeReport}
680
+ onFixIssue={handleFixIssue}
681
+ presetErrorLogs={presetErrorLogs}
682
+ onDiagnoseLog={handleDiagnoseLog}
683
+ isBusy={isBusy}
684
+ />
685
+ )}
686
+
687
+ {activeModule === 'auto-repair' && (
688
+ <AutoRepairCenterView
689
+ report={activeReport}
690
+ environmentChecks={environmentChecks}
691
+ mcpTools={mcpTools}
692
+ onFixIssue={handleFixIssue}
693
+ onFixAllSafe={handleFixAllSafe}
694
+ onRollbackRepair={handleRollbackRepair}
695
+ onUpdateQualityGate={handleUpdateQualityGate}
696
+ onRefreshEnvironment={handleRefreshEnvironment}
697
+ onCallMcpTool={handleCallMcpTool}
698
+ isBusy={isBusy}
699
+ />
700
+ )}
701
+
702
+ {activeModule === 'visual-testing' && (
703
+ <VisualTestingView
704
+ report={activeReport}
705
+ environmentChecks={environmentChecks}
706
+ mcpTools={mcpTools}
707
+ onFixIssue={handleFixIssue}
708
+ onFixAllSafe={handleFixAllSafe}
709
+ onRollbackRepair={handleRollbackRepair}
710
+ onUpdateQualityGate={handleUpdateQualityGate}
711
+ onRefreshEnvironment={handleRefreshEnvironment}
712
+ onCallMcpTool={handleCallMcpTool}
713
+ isBusy={isBusy}
714
+ />
715
+ )}
716
+
717
+ {activeModule === 'quality-gate' && (
718
+ <QualityGateView
719
+ report={activeReport}
720
+ environmentChecks={environmentChecks}
721
+ mcpTools={mcpTools}
722
+ onFixIssue={handleFixIssue}
723
+ onFixAllSafe={handleFixAllSafe}
724
+ onRollbackRepair={handleRollbackRepair}
725
+ onUpdateQualityGate={handleUpdateQualityGate}
726
+ onRefreshEnvironment={handleRefreshEnvironment}
727
+ onCallMcpTool={handleCallMcpTool}
728
+ isBusy={isBusy}
729
+ />
730
+ )}
731
+
732
+ {activeModule === 'project-history' && (
733
+ <ProjectHistoryView
734
+ report={activeReport}
735
+ environmentChecks={environmentChecks}
736
+ mcpTools={mcpTools}
737
+ onFixIssue={handleFixIssue}
738
+ onFixAllSafe={handleFixAllSafe}
739
+ onRollbackRepair={handleRollbackRepair}
740
+ onUpdateQualityGate={handleUpdateQualityGate}
741
+ onRefreshEnvironment={handleRefreshEnvironment}
742
+ onCallMcpTool={handleCallMcpTool}
743
+ isBusy={isBusy}
744
+ />
745
+ )}
746
+
747
+ {activeModule === 'environment-manager' && (
748
+ <EnvironmentManagerView
749
+ report={activeReport}
750
+ environmentChecks={environmentChecks}
751
+ mcpTools={mcpTools}
752
+ onFixIssue={handleFixIssue}
753
+ onFixAllSafe={handleFixAllSafe}
754
+ onRollbackRepair={handleRollbackRepair}
755
+ onUpdateQualityGate={handleUpdateQualityGate}
756
+ onRefreshEnvironment={handleRefreshEnvironment}
757
+ onCallMcpTool={handleCallMcpTool}
758
+ isBusy={isBusy}
759
+ />
760
+ )}
761
+
762
+ {activeModule === 'settings' && (
763
+ <SettingsAndMcpView
764
+ report={activeReport}
765
+ environmentChecks={environmentChecks}
766
+ mcpTools={mcpTools}
767
+ onFixIssue={handleFixIssue}
768
+ onFixAllSafe={handleFixAllSafe}
769
+ onRollbackRepair={handleRollbackRepair}
770
+ onUpdateQualityGate={handleUpdateQualityGate}
771
+ onRefreshEnvironment={handleRefreshEnvironment}
772
+ onCallMcpTool={handleCallMcpTool}
773
+ isBusy={isBusy}
774
+ />
775
+ )}
776
+ </main>
777
+ </div>
778
+ </div>
779
+ </div>
780
+ );
781
+ }
782
+
783
+ export default App;