@vibecheckai/cli 3.1.8 → 3.2.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 (36) hide show
  1. package/bin/registry.js +106 -116
  2. package/bin/runners/context/generators/mcp.js +18 -0
  3. package/bin/runners/context/index.js +72 -4
  4. package/bin/runners/context/proof-context.js +293 -1
  5. package/bin/runners/context/security-scanner.js +311 -73
  6. package/bin/runners/lib/analyzers.js +607 -20
  7. package/bin/runners/lib/detectors-v2.js +172 -15
  8. package/bin/runners/lib/entitlements-v2.js +48 -1
  9. package/bin/runners/lib/evidence-pack.js +678 -0
  10. package/bin/runners/lib/html-proof-report.js +913 -0
  11. package/bin/runners/lib/missions/plan.js +231 -41
  12. package/bin/runners/lib/missions/templates.js +125 -0
  13. package/bin/runners/lib/scan-output.js +492 -253
  14. package/bin/runners/lib/ship-output.js +901 -641
  15. package/bin/runners/runCheckpoint.js +44 -3
  16. package/bin/runners/runContext.d.ts +4 -0
  17. package/bin/runners/runDoctor.js +10 -2
  18. package/bin/runners/runFix.js +51 -341
  19. package/bin/runners/runInit.js +11 -0
  20. package/bin/runners/runPolish.d.ts +4 -0
  21. package/bin/runners/runPolish.js +608 -29
  22. package/bin/runners/runProve.js +210 -25
  23. package/bin/runners/runReality.js +846 -101
  24. package/bin/runners/runScan.js +238 -4
  25. package/bin/runners/runShip.js +19 -3
  26. package/bin/runners/runWatch.js +14 -1
  27. package/bin/vibecheck.js +32 -2
  28. package/mcp-server/consolidated-tools.js +408 -42
  29. package/mcp-server/index.js +152 -15
  30. package/mcp-server/proof-tools.js +571 -0
  31. package/mcp-server/tier-auth.js +22 -19
  32. package/mcp-server/tools-v3.js +744 -0
  33. package/mcp-server/truth-firewall-tools.js +190 -4
  34. package/package.json +3 -1
  35. package/bin/runners/runInstall.js +0 -281
  36. package/bin/runners/runLabs.js +0 -341
@@ -16,6 +16,10 @@
16
16
  * - Configuration: Environment variables, feature flags, logging
17
17
  * - Documentation: README, API docs, CHANGELOG, contributing guide
18
18
  * - Infrastructure: Docker, CI/CD, monitoring, backups
19
+ * - Observability: OpenTelemetry, structured logging, metrics, tracing
20
+ * - Resilience: Circuit breakers, retry logic, timeouts, graceful shutdown
21
+ * - Internationalization: i18n setup, RTL support, locale detection
22
+ * - Privacy: GDPR compliance, cookie consent, data export, account deletion
19
23
  */
20
24
 
21
25
  const fs = require("fs");
@@ -148,6 +152,185 @@ async function findAllFiles(dir, pattern, maxDepth = 5, currentDepth = 0) {
148
152
  return results;
149
153
  }
150
154
 
155
+ // ═══════════════════════════════════════════════════════════════════════════════
156
+ // LIBRARY DETECTION - Reduces false positives by detecting library alternatives
157
+ // ═══════════════════════════════════════════════════════════════════════════════
158
+
159
+ /**
160
+ * Common libraries that provide specific functionality
161
+ * Used to skip checks when a library already handles it
162
+ */
163
+ const LIBRARY_ALTERNATIVES = {
164
+ // Error handling libraries
165
+ errorBoundary: [
166
+ 'react-error-boundary',
167
+ '@sentry/react',
168
+ 'bugsnag-react',
169
+ ],
170
+
171
+ // Toast/notification libraries
172
+ toast: [
173
+ 'react-hot-toast',
174
+ 'react-toastify',
175
+ 'sonner',
176
+ '@radix-ui/react-toast',
177
+ 'notistack',
178
+ 'react-notifications',
179
+ ],
180
+
181
+ // Loading/spinner libraries
182
+ spinner: [
183
+ 'react-spinners',
184
+ 'react-loader-spinner',
185
+ 'react-loading',
186
+ '@chakra-ui/react', // Has Spinner
187
+ '@mantine/core', // Has Loader
188
+ ],
189
+
190
+ // Skeleton libraries
191
+ skeleton: [
192
+ 'react-loading-skeleton',
193
+ 'react-content-loader',
194
+ '@chakra-ui/react', // Has Skeleton
195
+ '@mantine/core', // Has Skeleton
196
+ '@radix-ui/themes',
197
+ ],
198
+
199
+ // Form validation libraries
200
+ formValidation: [
201
+ 'react-hook-form',
202
+ 'formik',
203
+ '@tanstack/react-form',
204
+ 'react-final-form',
205
+ 'zod', // Often paired with RHF
206
+ 'yup',
207
+ ],
208
+
209
+ // UI component libraries (provide many components)
210
+ uiLibrary: [
211
+ '@chakra-ui/react',
212
+ '@mantine/core',
213
+ '@radix-ui/themes',
214
+ '@mui/material',
215
+ 'antd',
216
+ '@nextui-org/react',
217
+ 'shadcn',
218
+ '@headlessui/react',
219
+ ],
220
+
221
+ // State management (affects how data flows)
222
+ stateManagement: [
223
+ 'zustand',
224
+ '@reduxjs/toolkit',
225
+ 'jotai',
226
+ 'recoil',
227
+ 'mobx',
228
+ '@tanstack/react-query',
229
+ ],
230
+
231
+ // Internationalization
232
+ i18n: [
233
+ 'next-intl',
234
+ 'react-i18next',
235
+ 'next-translate',
236
+ '@formatjs/intl',
237
+ 'lingui',
238
+ ],
239
+
240
+ // Analytics/monitoring
241
+ analytics: [
242
+ '@sentry/nextjs',
243
+ '@sentry/react',
244
+ '@vercel/analytics',
245
+ 'posthog-js',
246
+ 'mixpanel-browser',
247
+ '@segment/analytics-next',
248
+ ],
249
+ };
250
+
251
+ /**
252
+ * Check if package.json contains any of the specified libraries
253
+ * @param {string} packageJsonContent - Raw package.json content
254
+ * @param {string[]} libraries - List of library names to check
255
+ * @returns {string|null} - Name of found library or null
256
+ */
257
+ function hasLibrary(packageJsonContent, libraries) {
258
+ if (!packageJsonContent) return null;
259
+
260
+ for (const lib of libraries) {
261
+ // Check both dependencies and devDependencies
262
+ // Use word boundary to avoid partial matches
263
+ const pattern = new RegExp(`"${lib.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}"\\s*:`, 'i');
264
+ if (pattern.test(packageJsonContent)) {
265
+ return lib;
266
+ }
267
+ }
268
+
269
+ return null;
270
+ }
271
+
272
+ /**
273
+ * Detect project type for context-aware checking
274
+ * @param {string} projectPath - Path to project root
275
+ * @param {string} packageJsonContent - Raw package.json content
276
+ * @returns {Object} Project type info
277
+ */
278
+ async function detectProjectType(projectPath, packageJsonContent) {
279
+ const hasApp = await pathExists(path.join(projectPath, 'app'));
280
+ const hasPages = await pathExists(path.join(projectPath, 'pages'));
281
+ const hasSrc = await pathExists(path.join(projectPath, 'src'));
282
+
283
+ const isNextJs = packageJsonContent && /["']next["']/.test(packageJsonContent);
284
+ const isRemix = packageJsonContent && /["']@remix-run\//.test(packageJsonContent);
285
+ const isVite = packageJsonContent && /["']vite["']/.test(packageJsonContent);
286
+ const isAstro = packageJsonContent && /["']astro["']/.test(packageJsonContent);
287
+
288
+ // Check for specific project patterns
289
+ const isLibrary = packageJsonContent && /"main"|"module"|"exports"/.test(packageJsonContent);
290
+ const isCli = packageJsonContent && /"bin"/.test(packageJsonContent);
291
+ const isApi = !hasApp && !hasPages && packageJsonContent && /["']express["']|["']fastify["']|["']hono["']/.test(packageJsonContent);
292
+
293
+ // Check if using a comprehensive UI library
294
+ const uiLib = hasLibrary(packageJsonContent, LIBRARY_ALTERNATIVES.uiLibrary);
295
+
296
+ return {
297
+ isNextJs,
298
+ isRemix,
299
+ isVite,
300
+ isAstro,
301
+ isLibrary,
302
+ isCli,
303
+ isApi,
304
+ hasAppRouter: hasApp && isNextJs,
305
+ hasPagesRouter: hasPages && isNextJs,
306
+ hasSrc,
307
+ uiLibrary: uiLib,
308
+ // Skip frontend polish checks for non-frontend projects
309
+ skipFrontend: isLibrary || isCli || isApi,
310
+ };
311
+ }
312
+
313
+ /**
314
+ * Calculate issue severity based on project context
315
+ * Some issues are less important for certain project types
316
+ */
317
+ function adjustSeverity(baseSeverity, projectType, issueId) {
318
+ // For libraries/CLIs, frontend issues are less critical
319
+ if (projectType.skipFrontend && issueId.startsWith('missing-')) {
320
+ return 'low';
321
+ }
322
+
323
+ // If using a comprehensive UI library, component issues are less critical
324
+ if (projectType.uiLibrary) {
325
+ const componentIssues = ['missing-spinner', 'missing-skeleton', 'missing-toast', 'missing-empty-states'];
326
+ if (componentIssues.includes(issueId)) {
327
+ return 'low';
328
+ }
329
+ }
330
+
331
+ return baseSeverity;
332
+ }
333
+
151
334
  // ═══════════════════════════════════════════════════════════════════════════════
152
335
  // POLISH CHECKERS
153
336
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -155,6 +338,7 @@ async function findAllFiles(dir, pattern, maxDepth = 5, currentDepth = 0) {
155
338
  const checkers = {
156
339
  // ─────────────────────────────────────────────────────────────────────────────
157
340
  // FRONTEND CHECKER - Comprehensive UX/UI Polish Analysis
341
+ // Enhanced with library detection and false positive reduction
158
342
  // ─────────────────────────────────────────────────────────────────────────────
159
343
  async frontend(projectPath) {
160
344
  const issues = [];
@@ -172,20 +356,37 @@ const checkers = {
172
356
  const searchPath = hasSrc ? srcPath : (hasApp ? appPath : pagesPath);
173
357
  const packageJson = await readFileSafe(path.join(projectPath, 'package.json'));
174
358
 
359
+ // Detect project type and installed libraries for context-aware checking
360
+ const projectType = await detectProjectType(projectPath, packageJson);
361
+
362
+ // Skip frontend checks for non-frontend projects
363
+ if (projectType.skipFrontend) {
364
+ return issues;
365
+ }
366
+
367
+ // Check for library alternatives to reduce false positives
368
+ const errorBoundaryLib = hasLibrary(packageJson, LIBRARY_ALTERNATIVES.errorBoundary);
369
+ const toastLib = hasLibrary(packageJson, LIBRARY_ALTERNATIVES.toast);
370
+ const spinnerLib = hasLibrary(packageJson, LIBRARY_ALTERNATIVES.spinner);
371
+ const skeletonLib = hasLibrary(packageJson, LIBRARY_ALTERNATIVES.skeleton);
372
+ const formLib = hasLibrary(packageJson, LIBRARY_ALTERNATIVES.formValidation);
373
+
175
374
  // ═══════════════════════════════════════════════════════════════════════════
176
375
  // ERROR HANDLING & BOUNDARIES
177
376
  // ═══════════════════════════════════════════════════════════════════════════
178
377
 
179
378
  const hasErrorBoundary = await findFile(searchPath, /ErrorBoundary|error\.(tsx?|jsx?)$/i);
180
- if (!hasErrorBoundary) {
379
+ if (!hasErrorBoundary && !errorBoundaryLib && !projectType.uiLibrary) {
380
+ const severity = adjustSeverity('critical', projectType, 'missing-error-boundary');
181
381
  issues.push({
182
382
  id: 'missing-error-boundary',
183
383
  category: 'Frontend',
184
- severity: 'critical',
384
+ severity,
185
385
  title: 'Missing Error Boundary',
186
386
  description: 'No error boundary component found. React errors will crash the entire app.',
187
387
  suggestion: 'Add ErrorBoundary component to catch and handle React errors gracefully.',
188
388
  autoFixable: true,
389
+ confidence: 0.9, // High confidence - error boundaries are critical
189
390
  aiPrompt: `Create a reusable ErrorBoundary component for React/Next.js that:
190
391
  1. Catches JavaScript errors anywhere in the child component tree
191
392
  2. Logs errors to console and optionally to an error tracking service
@@ -200,18 +401,24 @@ const checkers = {
200
401
 
201
402
  Place it in src/components/ErrorBoundary.tsx`,
202
403
  });
404
+ } else if (errorBoundaryLib) {
405
+ // Library provides this - no issue needed
203
406
  }
204
407
 
205
408
  const has404 = await findFile(searchPath, /not-found|NotFound|404/i);
206
- if (!has404) {
409
+ // For Next.js app router, check the specific convention
410
+ const hasNextAppRouter404 = hasApp && await pathExists(path.join(appPath, 'not-found.tsx'));
411
+ if (!has404 && !hasNextAppRouter404) {
412
+ const severity = adjustSeverity('medium', projectType, 'missing-404');
207
413
  issues.push({
208
414
  id: 'missing-404',
209
415
  category: 'Frontend',
210
- severity: 'medium',
416
+ severity,
211
417
  title: 'Missing 404 Page',
212
418
  description: 'No custom 404/NotFound page found. Users will see default browser error.',
213
419
  suggestion: 'Add a custom 404 page with navigation options.',
214
420
  autoFixable: true,
421
+ confidence: 0.85, // High confidence but not critical
215
422
  aiPrompt: `Create a beautiful, user-friendly 404 Not Found page that:
216
423
  1. Has a visually appealing design with subtle animations
217
424
  2. Shows a clear "Page Not Found" message
@@ -234,16 +441,21 @@ For Pages Router: Create pages/404.tsx`,
234
441
  // LOADING STATES & SKELETONS
235
442
  // ═══════════════════════════════════════════════════════════════════════════
236
443
 
237
- const hasLoadingSpinner = await findFile(searchPath, /Spinner|LoadingSpinner/i);
238
- if (!hasLoadingSpinner) {
444
+ const hasLoadingSpinner = await findFile(searchPath, /Spinner|LoadingSpinner|Loading\.(tsx?|jsx?)$/i);
445
+ // Skip if a spinner library or UI library is installed
446
+ if (!hasLoadingSpinner && !spinnerLib && !projectType.uiLibrary) {
447
+ const severity = adjustSeverity('high', projectType, 'missing-spinner');
239
448
  issues.push({
240
449
  id: 'missing-spinner',
241
450
  category: 'Frontend',
242
- severity: 'high',
451
+ severity,
243
452
  title: 'Missing Loading Spinner',
244
453
  description: 'No spinner component found. Users need visual feedback during loading.',
245
- suggestion: 'Add a reusable Spinner component with multiple sizes and variants.',
454
+ suggestion: spinnerLib
455
+ ? `Consider using the ${spinnerLib} library you have installed.`
456
+ : 'Add a reusable Spinner component with multiple sizes and variants.',
246
457
  autoFixable: true,
458
+ confidence: 0.7, // Medium confidence - could be using inline styles or library
247
459
  aiPrompt: `Create a modern, reusable Spinner/Loading component that:
248
460
  1. Has multiple size variants: sm, md, lg, xl
249
461
  2. Supports custom colors via props or CSS variables
@@ -259,16 +471,21 @@ Create: src/components/ui/Spinner.tsx and src/components/ui/Spinner.css`,
259
471
  });
260
472
  }
261
473
 
262
- const hasSkeleton = await findFile(searchPath, /Skeleton|Shimmer|Placeholder/i);
263
- if (!hasSkeleton) {
474
+ const hasSkeleton = await findFile(searchPath, /Skeleton|Shimmer|ContentLoader/i);
475
+ // Skip if a skeleton library or UI library is installed
476
+ if (!hasSkeleton && !skeletonLib && !projectType.uiLibrary) {
477
+ const severity = adjustSeverity('high', projectType, 'missing-skeleton');
264
478
  issues.push({
265
479
  id: 'missing-skeleton',
266
480
  category: 'Frontend',
267
- severity: 'high',
481
+ severity,
268
482
  title: 'Missing Skeleton Loaders',
269
483
  description: 'No skeleton/shimmer components found. Skeleton loaders provide better perceived performance than spinners.',
270
- suggestion: 'Add skeleton components for content placeholders.',
484
+ suggestion: skeletonLib
485
+ ? `Consider using the ${skeletonLib} library you have installed.`
486
+ : 'Add skeleton components for content placeholders.',
271
487
  autoFixable: true,
488
+ confidence: 0.65, // Medium-low confidence - many apps don't need skeletons
272
489
  aiPrompt: `Create a comprehensive Skeleton loading system with:
273
490
 
274
491
  1. Base Skeleton component with:
@@ -358,16 +575,21 @@ Create: src/components/ui/EmptyState.tsx with all variants`,
358
575
  });
359
576
  }
360
577
 
361
- const hasToast = await findFile(searchPath, /Toast|Notification|Snackbar/i);
362
- if (!hasToast) {
578
+ const hasToast = await findFile(searchPath, /Toast|Notification|Snackbar|Toaster/i);
579
+ // Skip if a toast library or UI library is installed
580
+ if (!hasToast && !toastLib && !projectType.uiLibrary) {
581
+ const severity = adjustSeverity('high', projectType, 'missing-toast');
363
582
  issues.push({
364
583
  id: 'missing-toast',
365
584
  category: 'Frontend',
366
- severity: 'high',
585
+ severity,
367
586
  title: 'Missing Toast/Notification System',
368
587
  description: 'No toast or notification component found for user feedback.',
369
- suggestion: 'Add a toast/notification system for user feedback on actions.',
588
+ suggestion: toastLib
589
+ ? `Consider using the ${toastLib} library you have installed.`
590
+ : 'Add a toast/notification system for user feedback on actions.',
370
591
  autoFixable: true,
592
+ confidence: 0.75, // Medium-high confidence
371
593
  aiPrompt: `Create a complete Toast notification system with:
372
594
 
373
595
  1. Toast component with:
@@ -401,16 +623,20 @@ Create: src/components/ui/Toast.tsx and src/hooks/useToast.ts`,
401
623
  // ═══════════════════════════════════════════════════════════════════════════
402
624
 
403
625
  const hasFormValidation = await findFile(searchPath, /FormError|FieldError|ValidationMessage/i);
404
- const hasReactHookForm = packageJson && /react-hook-form/i.test(packageJson);
405
- if (!hasFormValidation && !hasReactHookForm) {
626
+ // Skip if a form library is installed
627
+ if (!hasFormValidation && !formLib && !projectType.uiLibrary) {
628
+ const severity = adjustSeverity('high', projectType, 'missing-form-validation-ui');
406
629
  issues.push({
407
630
  id: 'missing-form-validation-ui',
408
631
  category: 'Frontend',
409
- severity: 'high',
632
+ severity,
410
633
  title: 'Missing Form Validation UI',
411
634
  description: 'No form validation components found. Users need clear feedback on form errors.',
412
- suggestion: 'Add form validation components with clear error states.',
635
+ suggestion: formLib
636
+ ? `The ${formLib} library you have installed provides form validation. Make sure to display errors.`
637
+ : 'Add form validation components with clear error states.',
413
638
  autoFixable: true,
639
+ confidence: 0.65, // Medium confidence - many libraries provide this
414
640
  aiPrompt: `Create a comprehensive form validation UI system with:
415
641
 
416
642
  1. FormField wrapper component with:
@@ -2099,6 +2325,350 @@ export const UploadProgress = ({ progress, fileName }) => (
2099
2325
 
2100
2326
  return issues;
2101
2327
  },
2328
+
2329
+ // ─────────────────────────────────────────────────────────────────────────────
2330
+ // OBSERVABILITY CHECKER - Logging, Metrics, Tracing
2331
+ // ─────────────────────────────────────────────────────────────────────────────
2332
+ async observability(projectPath) {
2333
+ const issues = [];
2334
+ const packageJson = await readFileSafe(path.join(projectPath, 'package.json'));
2335
+ const srcPath = path.join(projectPath, 'src');
2336
+ const hasSrc = await pathExists(srcPath);
2337
+ const searchPath = hasSrc ? srcPath : projectPath;
2338
+
2339
+ // Check for OpenTelemetry
2340
+ const hasOpenTelemetry = packageJson && /@opentelemetry|otel/i.test(packageJson);
2341
+ if (!hasOpenTelemetry) {
2342
+ issues.push({
2343
+ id: 'missing-opentelemetry',
2344
+ category: 'Observability',
2345
+ severity: 'medium',
2346
+ title: 'Missing OpenTelemetry',
2347
+ description: 'No OpenTelemetry setup found. Distributed tracing helps debug production issues.',
2348
+ suggestion: 'Add @opentelemetry/sdk-node and configure tracing for your application.',
2349
+ autoFixable: false,
2350
+ aiPrompt: 'Set up OpenTelemetry for distributed tracing in my Node.js/Next.js application. Include automatic instrumentation for HTTP requests, database queries, and external API calls. Configure exporters for Jaeger or similar backend.',
2351
+ });
2352
+ }
2353
+
2354
+ // Check for structured logging
2355
+ const hasStructuredLogging = packageJson && /pino|winston|bunyan|@elastic\/ecs-pino-format/i.test(packageJson);
2356
+ if (!hasStructuredLogging) {
2357
+ issues.push({
2358
+ id: 'missing-structured-logging',
2359
+ category: 'Observability',
2360
+ severity: 'high',
2361
+ title: 'Missing Structured Logging',
2362
+ description: 'No structured logging library found. console.log is hard to parse in production.',
2363
+ suggestion: 'Add pino or winston for structured JSON logging.',
2364
+ autoFixable: false,
2365
+ aiPrompt: 'Set up structured logging using pino in my application. Configure it to output JSON logs in production with request ID correlation, log levels, and proper error serialization. Include a development mode with pretty-printing.',
2366
+ });
2367
+ }
2368
+
2369
+ // Check for metrics
2370
+ const hasMetrics = packageJson && /prom-client|@opentelemetry\/sdk-metrics|datadog-metrics/i.test(packageJson);
2371
+ if (!hasMetrics) {
2372
+ issues.push({
2373
+ id: 'missing-metrics',
2374
+ category: 'Observability',
2375
+ severity: 'medium',
2376
+ title: 'Missing Metrics Collection',
2377
+ description: 'No metrics library found. You won\'t have visibility into application performance.',
2378
+ suggestion: 'Add prom-client or OpenTelemetry metrics for application monitoring.',
2379
+ autoFixable: false,
2380
+ aiPrompt: 'Set up Prometheus metrics collection in my application using prom-client. Include default metrics (CPU, memory, event loop lag) and custom business metrics for key operations.',
2381
+ });
2382
+ }
2383
+
2384
+ // Check for correlation IDs
2385
+ const hasCorrelationId = await findFile(searchPath, /correlation|request.*id|trace.*id|x-request-id/i);
2386
+ if (!hasCorrelationId && hasSrc) {
2387
+ issues.push({
2388
+ id: 'missing-correlation-ids',
2389
+ category: 'Observability',
2390
+ severity: 'medium',
2391
+ title: 'Missing Request Correlation IDs',
2392
+ description: 'No correlation ID handling found. Debugging distributed requests is difficult.',
2393
+ suggestion: 'Add correlation ID middleware to trace requests across services.',
2394
+ autoFixable: true,
2395
+ aiPrompt: 'Add request correlation ID middleware to my Express/Next.js application. Generate unique IDs for each request, propagate them in headers, and include them in all log output.',
2396
+ });
2397
+ }
2398
+
2399
+ return issues;
2400
+ },
2401
+
2402
+ // ─────────────────────────────────────────────────────────────────────────────
2403
+ // RESILIENCE CHECKER - Circuit Breakers, Retry, Graceful Degradation
2404
+ // ─────────────────────────────────────────────────────────────────────────────
2405
+ async resilience(projectPath) {
2406
+ const issues = [];
2407
+ const packageJson = await readFileSafe(path.join(projectPath, 'package.json'));
2408
+ const srcPath = path.join(projectPath, 'src');
2409
+ const hasSrc = await pathExists(srcPath);
2410
+ const searchPath = hasSrc ? srcPath : projectPath;
2411
+
2412
+ // Check for circuit breaker
2413
+ const hasCircuitBreaker = packageJson && /opossum|cockatiel|resilience4j|circuit.*breaker/i.test(packageJson);
2414
+ if (!hasCircuitBreaker) {
2415
+ issues.push({
2416
+ id: 'missing-circuit-breaker',
2417
+ category: 'Resilience',
2418
+ severity: 'medium',
2419
+ title: 'Missing Circuit Breaker',
2420
+ description: 'No circuit breaker library found. External service failures can cascade.',
2421
+ suggestion: 'Add opossum or cockatiel for circuit breaker patterns on external calls.',
2422
+ autoFixable: false,
2423
+ aiPrompt: 'Implement circuit breaker pattern using opossum for external API calls in my application. Configure appropriate thresholds, fallback responses, and health checks for third-party services.',
2424
+ });
2425
+ }
2426
+
2427
+ // Check for retry logic
2428
+ const hasRetry = packageJson && /axios-retry|got|ky|p-retry|async-retry/i.test(packageJson);
2429
+ const hasRetryInCode = await findFile(searchPath, /retry|exponential.*backoff|maxRetries/i);
2430
+ if (!hasRetry && !hasRetryInCode) {
2431
+ issues.push({
2432
+ id: 'missing-retry-logic',
2433
+ category: 'Resilience',
2434
+ severity: 'high',
2435
+ title: 'Missing Retry Logic',
2436
+ description: 'No retry mechanism found. Transient failures will cause immediate errors.',
2437
+ suggestion: 'Add retry logic with exponential backoff for external API calls.',
2438
+ autoFixable: false,
2439
+ aiPrompt: 'Add retry logic with exponential backoff to external API calls in my application. Use axios-retry or p-retry with configurable max retries, backoff multiplier, and jitter.',
2440
+ });
2441
+ }
2442
+
2443
+ // Check for timeout configuration
2444
+ const hasTimeoutConfig = await findFile(searchPath, /timeout|AbortController|AbortSignal/i);
2445
+ if (!hasTimeoutConfig && hasSrc) {
2446
+ issues.push({
2447
+ id: 'missing-timeouts',
2448
+ category: 'Resilience',
2449
+ severity: 'high',
2450
+ title: 'Missing Request Timeouts',
2451
+ description: 'No timeout configuration found. Slow external services can hang your app.',
2452
+ suggestion: 'Add timeouts to all external HTTP calls and database queries.',
2453
+ autoFixable: false,
2454
+ aiPrompt: 'Add proper timeout handling to all external API calls and database queries in my application. Use AbortController for fetch requests and configure connection/query timeouts for database operations.',
2455
+ });
2456
+ }
2457
+
2458
+ // Check for graceful shutdown
2459
+ const hasGracefulShutdown = await findFile(searchPath, /graceful.*shutdown|SIGTERM|SIGINT|beforeExit/i);
2460
+ if (!hasGracefulShutdown) {
2461
+ issues.push({
2462
+ id: 'missing-graceful-shutdown',
2463
+ category: 'Resilience',
2464
+ severity: 'high',
2465
+ title: 'Missing Graceful Shutdown',
2466
+ description: 'No graceful shutdown handling found. In-flight requests may be dropped.',
2467
+ suggestion: 'Add SIGTERM/SIGINT handlers to drain connections before shutdown.',
2468
+ autoFixable: true,
2469
+ aiPrompt: 'Implement graceful shutdown in my Node.js application. Handle SIGTERM and SIGINT signals, stop accepting new connections, drain existing connections, close database pools, and exit cleanly.',
2470
+ });
2471
+ }
2472
+
2473
+ // Check for bulkhead pattern
2474
+ const hasBulkhead = packageJson && /bottleneck|p-limit|p-queue|semaphore/i.test(packageJson);
2475
+ if (!hasBulkhead) {
2476
+ issues.push({
2477
+ id: 'missing-bulkhead',
2478
+ category: 'Resilience',
2479
+ severity: 'low',
2480
+ title: 'Missing Bulkhead/Rate Limiting for Internal Operations',
2481
+ description: 'No internal concurrency limiting found. Resource exhaustion possible.',
2482
+ suggestion: 'Add bottleneck or p-limit to limit concurrent operations.',
2483
+ autoFixable: false,
2484
+ aiPrompt: 'Add concurrency limiting using bottleneck or p-limit for resource-intensive operations (file uploads, heavy computations, external API calls) to prevent resource exhaustion.',
2485
+ });
2486
+ }
2487
+
2488
+ return issues;
2489
+ },
2490
+
2491
+ // ─────────────────────────────────────────────────────────────────────────────
2492
+ // INTERNATIONALIZATION CHECKER - i18n Setup
2493
+ // ─────────────────────────────────────────────────────────────────────────────
2494
+ async internationalization(projectPath) {
2495
+ const issues = [];
2496
+ const packageJson = await readFileSafe(path.join(projectPath, 'package.json'));
2497
+ const srcPath = path.join(projectPath, 'src');
2498
+ const hasSrc = await pathExists(srcPath);
2499
+ const searchPath = hasSrc ? srcPath : projectPath;
2500
+
2501
+ // Check for i18n library
2502
+ const hasI18n = packageJson && /next-intl|react-i18next|i18next|lingui|formatjs|react-intl/i.test(packageJson);
2503
+ if (!hasI18n) {
2504
+ issues.push({
2505
+ id: 'missing-i18n',
2506
+ category: 'Internationalization',
2507
+ severity: 'low',
2508
+ title: 'Missing Internationalization (i18n)',
2509
+ description: 'No i18n library found. Adding translations later is costly.',
2510
+ suggestion: 'Add next-intl or react-i18next for internationalization support.',
2511
+ autoFixable: false,
2512
+ aiPrompt: 'Set up internationalization in my Next.js/React application using next-intl or react-i18next. Include locale detection, language switching, and a translation extraction workflow.',
2513
+ });
2514
+ }
2515
+
2516
+ // Check for locale detection
2517
+ const hasLocaleDetection = await findFile(searchPath, /locale|navigator\.language|accept-language|getLocale/i);
2518
+ if (hasI18n && !hasLocaleDetection) {
2519
+ issues.push({
2520
+ id: 'missing-locale-detection',
2521
+ category: 'Internationalization',
2522
+ severity: 'medium',
2523
+ title: 'Missing Locale Detection',
2524
+ description: 'i18n setup found but no automatic locale detection.',
2525
+ suggestion: 'Add automatic locale detection from browser settings or Accept-Language header.',
2526
+ autoFixable: false,
2527
+ aiPrompt: 'Add automatic locale detection to my internationalized application. Detect from Accept-Language header on server, navigator.language on client, and allow user override with persistence.',
2528
+ });
2529
+ }
2530
+
2531
+ // Check for RTL support
2532
+ const hasRTL = await findFile(searchPath, /rtl|dir=.*rtl|direction.*rtl|ltr.*rtl/i);
2533
+ if (hasI18n && !hasRTL) {
2534
+ issues.push({
2535
+ id: 'missing-rtl-support',
2536
+ category: 'Internationalization',
2537
+ severity: 'low',
2538
+ title: 'Missing RTL Support',
2539
+ description: 'No RTL (right-to-left) support found. Arabic/Hebrew users affected.',
2540
+ suggestion: 'Add RTL layout support using CSS logical properties or rtlcss.',
2541
+ autoFixable: false,
2542
+ aiPrompt: 'Add RTL (right-to-left) support to my application for Arabic and Hebrew locales. Use CSS logical properties (start/end instead of left/right), directional icons, and proper text alignment.',
2543
+ });
2544
+ }
2545
+
2546
+ // Check for translation files
2547
+ const hasTranslationFiles = await pathExists(path.join(projectPath, 'locales')) ||
2548
+ await pathExists(path.join(projectPath, 'translations')) ||
2549
+ await pathExists(path.join(projectPath, 'messages')) ||
2550
+ await pathExists(path.join(srcPath, 'locales'));
2551
+ if (hasI18n && !hasTranslationFiles) {
2552
+ issues.push({
2553
+ id: 'missing-translation-files',
2554
+ category: 'Internationalization',
2555
+ severity: 'medium',
2556
+ title: 'Missing Translation Files',
2557
+ description: 'i18n library found but no translation files directory.',
2558
+ suggestion: 'Create locales/ directory with JSON translation files for each language.',
2559
+ autoFixable: true,
2560
+ aiPrompt: 'Create a translation file structure for my i18n setup. Include English as the base language with organized namespaces, and add a script to extract translation keys from code.',
2561
+ });
2562
+ }
2563
+
2564
+ return issues;
2565
+ },
2566
+
2567
+ // ─────────────────────────────────────────────────────────────────────────────
2568
+ // PRIVACY CHECKER - GDPR, Cookie Consent, Data Handling
2569
+ // ─────────────────────────────────────────────────────────────────────────────
2570
+ async privacy(projectPath) {
2571
+ const issues = [];
2572
+ const packageJson = await readFileSafe(path.join(projectPath, 'package.json'));
2573
+ const srcPath = path.join(projectPath, 'src');
2574
+ const hasSrc = await pathExists(srcPath);
2575
+ const searchPath = hasSrc ? srcPath : projectPath;
2576
+
2577
+ // Check for cookie consent
2578
+ const hasCookieConsent = packageJson && /cookie-consent|cookieconsent|react-cookie-consent|gdpr|onetrust|cookiebot/i.test(packageJson);
2579
+ const hasCookieConsentInCode = await findFile(searchPath, /cookie.*consent|gdpr.*consent|consent.*banner/i);
2580
+ if (!hasCookieConsent && !hasCookieConsentInCode) {
2581
+ issues.push({
2582
+ id: 'missing-cookie-consent',
2583
+ category: 'Privacy',
2584
+ severity: 'high',
2585
+ title: 'Missing Cookie Consent',
2586
+ description: 'No cookie consent mechanism found. GDPR/CCPA compliance required.',
2587
+ suggestion: 'Add a cookie consent banner for EU/California visitors.',
2588
+ autoFixable: false,
2589
+ aiPrompt: 'Implement a GDPR-compliant cookie consent banner for my application. Include categories (necessary, analytics, marketing), remember user choice, and only load tracking scripts after consent.',
2590
+ });
2591
+ }
2592
+
2593
+ // Check for privacy policy
2594
+ const hasPrivacyPolicy = await pathExists(path.join(projectPath, 'public', 'privacy.html')) ||
2595
+ await pathExists(path.join(projectPath, 'public', 'privacy-policy.html')) ||
2596
+ await findFile(searchPath, /privacy.*policy|PrivacyPolicy/i);
2597
+ if (!hasPrivacyPolicy) {
2598
+ issues.push({
2599
+ id: 'missing-privacy-policy',
2600
+ category: 'Privacy',
2601
+ severity: 'high',
2602
+ title: 'Missing Privacy Policy',
2603
+ description: 'No privacy policy page found. Required by law in many jurisdictions.',
2604
+ suggestion: 'Add a privacy policy page explaining data collection and usage.',
2605
+ autoFixable: false,
2606
+ aiPrompt: 'Create a privacy policy page for my application that covers: what data is collected, how it is used, third-party services, cookies, user rights (GDPR), and contact information.',
2607
+ });
2608
+ }
2609
+
2610
+ // Check for data export/deletion capability
2611
+ const hasDataExport = await findFile(searchPath, /export.*data|download.*data|gdpr.*export|data.*portability/i);
2612
+ if (!hasDataExport) {
2613
+ issues.push({
2614
+ id: 'missing-data-export',
2615
+ category: 'Privacy',
2616
+ severity: 'medium',
2617
+ title: 'Missing Data Export Feature',
2618
+ description: 'No data export capability found. GDPR requires data portability.',
2619
+ suggestion: 'Add ability for users to export their data in a standard format.',
2620
+ autoFixable: false,
2621
+ aiPrompt: 'Implement a data export feature for GDPR compliance. Allow users to download all their personal data in JSON or CSV format, including profile, activity history, and any stored preferences.',
2622
+ });
2623
+ }
2624
+
2625
+ // Check for account deletion
2626
+ const hasAccountDeletion = await findFile(searchPath, /delete.*account|account.*deletion|remove.*account|gdpr.*delete/i);
2627
+ if (!hasAccountDeletion) {
2628
+ issues.push({
2629
+ id: 'missing-account-deletion',
2630
+ category: 'Privacy',
2631
+ severity: 'high',
2632
+ title: 'Missing Account Deletion',
2633
+ description: 'No account deletion feature found. GDPR requires right to erasure.',
2634
+ suggestion: 'Add ability for users to delete their account and all associated data.',
2635
+ autoFixable: false,
2636
+ aiPrompt: 'Implement account deletion functionality with GDPR right to erasure. Include confirmation flow, grace period option, cascade deletion of related data, and anonymization of non-deletable records.',
2637
+ });
2638
+ }
2639
+
2640
+ // Check for data retention policy
2641
+ const hasDataRetention = await findFile(searchPath, /retention|data.*cleanup|purge.*old|ttl.*expire/i);
2642
+ if (!hasDataRetention) {
2643
+ issues.push({
2644
+ id: 'missing-data-retention',
2645
+ category: 'Privacy',
2646
+ severity: 'medium',
2647
+ title: 'Missing Data Retention Policy',
2648
+ description: 'No data retention/cleanup logic found. Old data should be purged.',
2649
+ suggestion: 'Implement data retention policies to automatically purge old data.',
2650
+ autoFixable: false,
2651
+ aiPrompt: 'Implement a data retention policy for my application. Add scheduled jobs to purge old sessions, logs, and inactive accounts. Document retention periods for each data type.',
2652
+ });
2653
+ }
2654
+
2655
+ // Check for PII encryption
2656
+ const hasEncryption = await findFile(searchPath, /encrypt|crypto.*cipher|aes|bcrypt|argon2/i);
2657
+ if (!hasEncryption) {
2658
+ issues.push({
2659
+ id: 'missing-pii-encryption',
2660
+ category: 'Privacy',
2661
+ severity: 'high',
2662
+ title: 'Missing Data Encryption',
2663
+ description: 'No encryption utilities found. Sensitive data should be encrypted.',
2664
+ suggestion: 'Add encryption for PII at rest and ensure passwords use bcrypt/argon2.',
2665
+ autoFixable: false,
2666
+ aiPrompt: 'Set up encryption for sensitive data in my application. Use bcrypt or argon2 for passwords, field-level encryption for PII, and ensure encryption keys are properly managed.',
2667
+ });
2668
+ }
2669
+
2670
+ return issues;
2671
+ },
2102
2672
  };
2103
2673
 
2104
2674
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -2217,15 +2787,19 @@ ${c.bold}OPTIONS${c.reset}
2217
2787
  --json Output as JSON (includes AI prompts)
2218
2788
 
2219
2789
  ${c.bold}CATEGORIES${c.reset}
2220
- frontend Error boundaries, skeletons, ARIA, haptics, animations, caching
2221
- backend Health endpoints, validation, rate limiting, error handling
2222
- security .env files, security headers, CORS, secrets management
2223
- performance Image optimization, caching, bundle analysis
2224
- accessibility A11y testing, skip links, focus management
2225
- seo Meta tags, sitemap, robots.txt, Open Graph
2226
- configuration TypeScript, ESLint, Prettier, EditorConfig
2227
- documentation README, CHANGELOG, CONTRIBUTING, LICENSE
2228
- infrastructure Docker, CI/CD, deployment config, env validation
2790
+ frontend Error boundaries, skeletons, ARIA, haptics, animations, caching
2791
+ backend Health endpoints, validation, rate limiting, error handling
2792
+ security .env files, security headers, CORS, secrets management
2793
+ performance Image optimization, caching, bundle analysis
2794
+ accessibility A11y testing, skip links, focus management
2795
+ seo Meta tags, sitemap, robots.txt, Open Graph
2796
+ configuration TypeScript, ESLint, Prettier, EditorConfig
2797
+ documentation README, CHANGELOG, CONTRIBUTING, LICENSE
2798
+ infrastructure Docker, CI/CD, deployment config, env validation
2799
+ observability OpenTelemetry, structured logging, metrics, tracing
2800
+ resilience Circuit breakers, retry logic, timeouts, graceful shutdown
2801
+ internationalization i18n setup, RTL support, locale detection, translations
2802
+ privacy GDPR, cookie consent, data export, account deletion
2229
2803
 
2230
2804
  ${c.bold}EXAMPLES${c.reset}
2231
2805
  vibecheck polish # Analyze current project
@@ -2271,6 +2845,11 @@ function getCategoryIcon(category) {
2271
2845
  Configuration: icons.gear,
2272
2846
  Documentation: icons.book,
2273
2847
  Infrastructure: icons.rocket,
2848
+ // New world-class categories
2849
+ Observability: '📊',
2850
+ Resilience: '🛡️',
2851
+ Internationalization: '🌍',
2852
+ Privacy: '🔐',
2274
2853
  };
2275
2854
  return categoryIcons[category] || icons.star;
2276
2855
  }