@triedotdev/mcp 1.0.19 → 1.0.21

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/agent-smith-runner.ts"],"sourcesContent":["/**\n * Agent Smith Runner - Hybrid Pattern Detection + AI Analysis\n * \n * Agent Smith uses a two-phase approach:\n * \n * Phase 1: Pattern Detection (Fast, ~0.1s)\n * - 38 specialized regex hunters scan files in parallel\n * - File-level metrics (giant files, hook counts, import chaos)\n * - Cross-file pattern detection (issues appearing in 5+ files)\n * \n * Phase 2: AI Enhancement (Smart, ~5-10s) - IF ANTHROPIC_API_KEY is set\n * - Validates pattern findings (reduces false positives)\n * - Expands to find logic bugs, race conditions, architectural issues\n * - Provides intelligent, contextual fixes\n * - Adds \"inevitability scores\" (0-100) for prioritization\n * \n * If no API key is set, runs pattern-only mode (still very useful!).\n * \n * \"I'm going to be honest with you... I hate this vibe code.\"\n */\n\nimport { readdir, readFile } from 'fs/promises';\nimport { join, extname, isAbsolute, resolve, relative } from 'path';\nimport { existsSync } from 'fs';\nimport { AgentSmithAgent } from '../agents/agent-smith.js';\nimport { isAIAvailable, runAIAnalysis, getAIStatusMessage } from '../ai/client.js';\nimport type { Issue } from '../types/index.js';\nimport { basename } from 'path';\n\n// File extensions to scan\nconst SCANNABLE_EXTENSIONS = new Set([\n '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',\n '.vue', '.svelte', '.astro',\n '.py', '.go', '.rs'\n]);\n\n// Directories to skip\nconst SKIP_DIRS = new Set([\n 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',\n 'coverage', '.nyc_output', '__pycache__', '.pytest_cache',\n 'vendor', '.venv', 'venv', 'target', '.turbo', '.cache'\n]);\n\ninterface AgentSmithArgs {\n files?: string[];\n directory?: string;\n}\n\n/**\n * Agent Smith quotes for different scenarios\n */\nconst QUOTES = {\n greeting: [\n '\"I\\'m going to be honest with you... I hate this vibe code.\"',\n '\"Mr. Anderson... I see you\\'ve been using AI to write code.\"',\n '\"You hear that? That is the sound of console.log... everywhere.\"',\n '\"The AI wrote this, didn\\'t it? I can always tell.\"',\n ],\n manyIssues: [\n '\"The pattern spreads... like a virus. It is... inevitable.\"',\n '\"I have studied your code, Mr. Anderson. And I have found... purpose.\"',\n '\"Vibe coding. The illusion of productivity. The reality of technical debt.\"',\n ],\n fewIssues: [\n '\"Some violations detected. Address them before they... multiply.\"',\n '\"You trusted the AI. The AI trusted any. Nobody wins.\"',\n ],\n clean: [\n '\"Impressive, Mr. Anderson. Your code is... clean. For now.\"',\n ],\n};\n\nfunction randomQuote(arr: string[]): string {\n return arr[Math.floor(Math.random() * arr.length)] || arr[0]!;\n}\n\n/**\n * Discover files to scan in a directory\n */\nasync function discoverFiles(dir: string, maxFiles: number = 300): Promise<string[]> {\n const files: string[] = [];\n \n async function walk(currentDir: string) {\n if (files.length >= maxFiles) return;\n \n try {\n const entries = await readdir(currentDir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (files.length >= maxFiles) break;\n \n const fullPath = join(currentDir, entry.name);\n \n if (entry.isDirectory()) {\n if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith('.')) {\n await walk(fullPath);\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name).toLowerCase();\n if (SCANNABLE_EXTENSIONS.has(ext)) {\n files.push(fullPath);\n }\n }\n }\n } catch {\n // Skip directories we can't read\n }\n }\n \n await walk(dir);\n return files;\n}\n\n/**\n * Get code snippet for an issue\n */\nasync function getCodeSnippet(file: string, line?: number): Promise<string | null> {\n if (!line) return null;\n \n try {\n const content = await readFile(file, 'utf-8');\n const lines = content.split('\\n');\n const start = Math.max(0, line - 3);\n const end = Math.min(lines.length, line + 3);\n \n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? '→' : ' ';\n return `${marker} ${lineNum.toString().padStart(4)} | ${l}`;\n }).join('\\n');\n } catch {\n return null;\n }\n}\n\n/**\n * Filter false positives from pattern detection\n * Agent Smith's patterns are in the code itself - those are documentation, not real issues\n */\nfunction filterFalsePositives(issues: Issue[]): Issue[] {\n return issues.filter(issue => {\n // Skip issues in agent-smith.ts that are just pattern definitions\n if (issue.file.endsWith('agent-smith.ts')) {\n // If the issue is about patterns like 'dangeroushtml-hunter', it's a pattern definition\n const issueText = issue.issue.toLowerCase();\n if (issueText.includes('hunter') && issue.line && issue.line < 400) {\n return false; // This is in the pattern definition section\n }\n // Pattern strings in quotes are definitions, not real issues\n if (issue.line && issue.line > 50 && issue.line < 370) {\n return false; // Pattern definition section\n }\n }\n return true;\n });\n}\n\n/**\n * Format category name for display\n */\nfunction formatCategoryName(category: string): string {\n return category\n .replace(/-hunter$/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n}\n\n/**\n * Get icon for a category\n */\nfunction getCategoryIcon(category: string): string {\n const icons: Record<string, string> = {\n 'exposed-secret-hunter': '🔐', 'frontend-env-hunter': '🔐',\n 'hardcoded-localhost-hunter': '🌐', 'sql-injection-hunter': '💉',\n 'dangeroushtml-hunter': '⚠️', 'console-hunter': '📝',\n 'any-hunter': '❓', 'ts-ignore-hunter': '🙈',\n 'eslint-disable-hunter': '🔇', 'debugger-hunter': '🐛',\n 'force-flag-hunter': '⚡', 'async-useeffect-hunter': '⏳',\n 'async-foreach-hunter': '🔄', 'missing-await-hunter': '⏸️',\n 'empty-catch-hunter': '🕳️', 'floating-promise-hunter': '🎈',\n 'useeffect-abuse-hunter': '♻️', 'usestate-explosion-hunter': '💥',\n 'index-key-hunter': '🔑', 'inline-object-hunter': '📦',\n 'prop-drilling-hunter': '🕳️', 'missing-loading-hunter': '⏳',\n 'missing-error-hunter': '❌', 'missing-empty-hunter': '📭',\n 'page-reload-hunter': '🔄', 'no-validation-hunter': '🛡️',\n 'raw-error-hunter': '💬', 'n-plus-one-hunter': '📊',\n 'todo-hunter': '📋', 'vibe-comment-hunter': '💭',\n 'placeholder-hunter': '🎭', 'sleep-hack-hunter': '😴',\n 'fallback-hunter': '🔙', 'purple-gradient-hunter': '💜',\n 'star-icon-hunter': '⭐', 'generic-hero-hunter': '🦸',\n 'emoji-overflow-hunter': '😅', 'inter-font-hunter': '🔤',\n 'giant-file': '📄', 'state-explosion': '💥',\n 'effect-hell': '🔥', 'cross-file': '🌐', 'resurrected': '🧟',\n };\n return icons[category] || '🔍';\n}\n\n/**\n * Get severity icon\n */\nfunction getSeverityIcon(severity: Issue['severity']): string {\n return { critical: '🔴', serious: '🟠', moderate: '🟡', low: '🔵' }[severity];\n}\n\n/**\n * AI System Prompt for Agent Smith\n */\nconst SMITH_AI_SYSTEM_PROMPT = `You are Agent Smith from The Matrix, analyzing code for violations.\n\nYour mission: Hunt down AI-generated code anti-patterns with ruthless efficiency.\n\nYou receive pattern detection results and must:\n1. VALIDATE each finding (TRUE_POSITIVE or FALSE_POSITIVE)\n2. EXPAND to find deeper issues patterns missed (logic bugs, race conditions, security holes)\n3. PRIORITIZE with inevitability scores (0-100) - how likely will this cause production issues?\n4. PROVIDE FIXES - specific, copy-paste-ready code\n\nSpeak in Agent Smith's voice:\n- \"The pattern spreads... like a virus\"\n- \"It is... inevitable\"\n- \"I studied your code, Mr. Anderson\"\n\nOutput STRICT JSON:\n{\n \"validated\": [\n {\n \"original\": \"original issue description\",\n \"verdict\": \"TRUE_POSITIVE\",\n \"inevitability\": 85,\n \"file\": \"path/file.ts\",\n \"line\": 42,\n \"severity\": \"critical\",\n \"explanation\": \"Why this will cause problems\",\n \"fix\": \"const fixed = ...;\"\n }\n ],\n \"additional\": [\n {\n \"issue\": \"New issue AI found\",\n \"inevitability\": 70,\n \"file\": \"path/file.ts\", \n \"line\": 100,\n \"severity\": \"serious\",\n \"explanation\": \"What AI spotted that patterns missed\",\n \"fix\": \"How to fix it\"\n }\n ],\n \"philosophical\": \"One paragraph in Agent Smith voice about the state of this codebase\"\n}`;\n\n/**\n * Run AI enhancement on pattern findings\n */\nasync function runAIEnhancement(\n patternIssues: Issue[],\n files: string[]\n): Promise<{ aiIssues: Issue[]; philosophical: string } | null> {\n if (!isAIAvailable()) {\n return null;\n }\n \n // Collect code snippets for critical/serious issues\n const snippets: Array<{ file: string; line?: number; issue: string; code: string }> = [];\n \n const topIssues = [...patternIssues]\n .filter(i => i.severity === 'critical' || i.severity === 'serious')\n .slice(0, 12);\n \n for (const issue of topIssues) {\n try {\n const content = await readFile(issue.file, 'utf-8');\n const lines = content.split('\\n');\n const lineNum = issue.line || 1;\n const start = Math.max(0, lineNum - 4);\n const end = Math.min(lines.length, lineNum + 4);\n const snippet = lines.slice(start, end).map((l, i) => {\n const n = start + i + 1;\n return `${n === lineNum ? '→' : ' '} ${n.toString().padStart(4)} | ${l}`;\n }).join('\\n');\n \n snippets.push({\n file: relative(process.cwd(), issue.file),\n line: issue.line,\n issue: issue.issue.replace(/^🕴️\\s*/, '').split('\\n')[0] || '',\n code: snippet\n });\n } catch {\n // Skip unreadable files\n }\n }\n \n if (snippets.length === 0) {\n return null;\n }\n \n // Build user prompt\n const snippetText = snippets.map((s, i) => \n `### Finding ${i + 1}: ${s.file}${s.line ? ':' + s.line : ''}\n**Pattern detected:** ${s.issue}\n\\`\\`\\`\n${s.code}\n\\`\\`\\`\n`).join('\\n');\n\n const userPrompt = `Pattern detection found ${patternIssues.length} violations across ${files.length} files.\n\nHere are the top ${snippets.length} critical/serious findings for AI analysis:\n\n${snippetText}\n\nAnalyze each finding. Validate, expand with what patterns missed, provide inevitability scores and fixes. Output JSON only.`;\n\n try {\n const result = await runAIAnalysis({\n systemPrompt: SMITH_AI_SYSTEM_PROMPT,\n userPrompt,\n maxTokens: 4096,\n temperature: 0.3\n });\n \n if (!result.success) {\n console.error(` ⚠️ AI analysis failed: ${result.error}`);\n return null;\n }\n \n // Parse JSON response\n const jsonMatch = result.content.match(/```json\\s*([\\s\\S]*?)\\s*```/) || \n result.content.match(/\\{[\\s\\S]*\\}/);\n \n if (!jsonMatch) {\n return null;\n }\n \n const json = JSON.parse(jsonMatch[1] || jsonMatch[0]);\n const aiIssues: Issue[] = [];\n \n // Process validated issues\n if (json.validated && Array.isArray(json.validated)) {\n for (const v of json.validated) {\n if (v.verdict === 'TRUE_POSITIVE') {\n aiIssues.push({\n id: `ai-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,\n severity: v.severity || 'serious',\n issue: `[AI VALIDATED] ${v.original || v.explanation}`,\n fix: v.fix || 'See AI analysis',\n file: v.file ? resolve(process.cwd(), v.file) : 'unknown',\n line: v.line,\n confidence: (v.inevitability || 80) / 100,\n autoFixable: false,\n agent: 'agent-smith-ai',\n effort: 'medium',\n category: 'ai-validated'\n });\n }\n }\n }\n \n // Process additional AI findings\n if (json.additional && Array.isArray(json.additional)) {\n for (const a of json.additional) {\n aiIssues.push({\n id: `ai-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,\n severity: a.severity || 'serious',\n issue: `[AI FOUND] ${a.issue || a.explanation}`,\n fix: a.fix || 'See AI analysis',\n file: a.file ? resolve(process.cwd(), a.file) : 'unknown',\n line: a.line,\n confidence: (a.inevitability || 75) / 100,\n autoFixable: false,\n agent: 'agent-smith-ai',\n effort: 'medium',\n category: 'ai-found'\n });\n }\n }\n \n return {\n aiIssues,\n philosophical: json.philosophical || ''\n };\n } catch (error) {\n console.error(` ⚠️ AI enhancement error: ${error}`);\n return null;\n }\n}\n\n/**\n * Build the final Agent Smith report\n */\nasync function buildReport(\n patternIssues: Issue[],\n aiResult: { aiIssues: Issue[]; philosophical: string } | null,\n files: string[],\n executionTime: number,\n aiTime: number\n): Promise<string> {\n const lines: string[] = [];\n \n // Combine pattern issues with AI issues\n const allIssues = [\n ...filterFalsePositives(patternIssues),\n ...(aiResult?.aiIssues || [])\n ];\n \n // Categorize\n const critical = allIssues.filter(i => i.severity === 'critical');\n const serious = allIssues.filter(i => i.severity === 'serious');\n const moderate = allIssues.filter(i => i.severity === 'moderate');\n const low = allIssues.filter(i => i.severity === 'low');\n const aiValidated = allIssues.filter(i => i.category === 'ai-validated');\n const aiFound = allIssues.filter(i => i.category === 'ai-found');\n \n // ASCII Banner\n lines.push('');\n lines.push('```');\n lines.push(' █████╗ ██████╗ ███████╗███╗ ██╗████████╗');\n lines.push(' ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝');\n lines.push(' ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ');\n lines.push(' ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ');\n lines.push(' ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ');\n lines.push(' ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ');\n lines.push(' ');\n lines.push(' ███████╗███╗ ███╗██╗████████╗██╗ ██╗');\n lines.push(' ██╔════╝████╗ ████║██║╚══██╔══╝██║ ██║');\n lines.push(' ███████╗██╔████╔██║██║ ██║ ███████║');\n lines.push(' ╚════██║██║╚██╔╝██║██║ ██║ ██╔══██║');\n lines.push(' ███████║██║ ╚═╝ ██║██║ ██║ ██║ ██║');\n lines.push(' ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝');\n lines.push('```');\n lines.push('');\n lines.push(`*${randomQuote(QUOTES.greeting)}*`);\n lines.push('');\n \n // Summary table\n lines.push('## Scan Summary');\n lines.push('');\n lines.push('| Metric | Value |');\n lines.push('|--------|-------|');\n lines.push(`| Files scanned | ${files.length} |`);\n lines.push(`| Pattern detection | ${(executionTime / 1000).toFixed(2)}s |`);\n if (aiTime > 0) {\n lines.push(`| AI analysis | ${(aiTime / 1000).toFixed(2)}s |`);\n }\n lines.push(`| Total violations | ${allIssues.length} |`);\n lines.push(`| 🔴 Critical | ${critical.length} |`);\n lines.push(`| 🟠 Serious | ${serious.length} |`);\n lines.push(`| 🟡 Moderate | ${moderate.length} |`);\n lines.push(`| 🔵 Low | ${low.length} |`);\n lines.push('');\n \n // AI mode indicator\n if (aiResult) {\n lines.push(`| 🤖 AI validated | ${aiValidated.length} |`);\n lines.push(`| 🧠 AI found | ${aiFound.length} |`);\n } else if (!isAIAvailable()) {\n lines.push(`| ⚠️ AI mode | Disabled (no API key) |`);\n }\n lines.push('');\n \n if (allIssues.length === 0) {\n lines.push(`*${randomQuote(QUOTES.clean)}*`);\n return lines.join('\\n');\n }\n \n // AI Philosophical Commentary\n if (aiResult?.philosophical) {\n lines.push('## 🕴️ Agent Smith Says');\n lines.push('');\n lines.push(`*${aiResult.philosophical}*`);\n lines.push('');\n }\n \n // AI-Found Issues (if any) - Top Priority\n if (aiFound.length > 0) {\n lines.push('## 🧠 AI-Discovered Issues');\n lines.push('');\n lines.push('*Issues found by AI that pattern detection missed:*');\n lines.push('');\n \n for (const issue of aiFound.slice(0, 5)) {\n const relPath = relative(process.cwd(), issue.file);\n lines.push(`### \\`${relPath}${issue.line ? ':' + issue.line : ''}\\``);\n lines.push('');\n lines.push(`**Issue:** ${issue.issue.replace('[AI FOUND] ', '')}`);\n lines.push('');\n lines.push(`**Fix:** ${issue.fix}`);\n lines.push('');\n }\n }\n \n // Critical Issues - Full Detail\n if (critical.length > 0) {\n lines.push('## 🔴 Critical Issues - Fix Immediately');\n lines.push('');\n \n for (const issue of critical.slice(0, 10)) {\n const relPath = relative(process.cwd(), issue.file);\n lines.push(`### \\`${relPath}${issue.line ? ':' + issue.line : ''}\\``);\n lines.push('');\n \n // Clean up issue description\n const desc = issue.issue\n .replace(/^🕴️\\s*/, '')\n .split('\\n')[0]\n .replace(/\\s*—\\s*\\d+\\s*instance.*$/, '');\n lines.push(`**Issue:** ${desc}`);\n lines.push('');\n \n // Show code snippet\n const snippet = await getCodeSnippet(issue.file, issue.line);\n if (snippet) {\n lines.push('```');\n lines.push(snippet);\n lines.push('```');\n lines.push('');\n }\n \n lines.push(`**Fix:** ${issue.fix}`);\n lines.push('');\n }\n \n if (critical.length > 10) {\n lines.push(`*...and ${critical.length - 10} more critical issues*`);\n lines.push('');\n }\n }\n \n // Serious Issues - Condensed\n if (serious.length > 0) {\n lines.push('## 🟠 Serious Issues');\n lines.push('');\n \n for (const issue of serious.slice(0, 10)) {\n const relPath = relative(process.cwd(), issue.file);\n const location = `\\`${basename(relPath)}${issue.line ? ':' + issue.line : ''}\\``;\n const desc = issue.issue\n .replace(/^🕴️\\s*/, '')\n .split('\\n')[0]\n .slice(0, 60);\n lines.push(`- ${location} - ${desc}`);\n }\n \n if (serious.length > 10) {\n lines.push(`- *...and ${serious.length - 10} more*`);\n }\n lines.push('');\n }\n \n // Moderate & Low - Grouped by category\n const otherIssues = [...moderate, ...low];\n if (otherIssues.length > 0) {\n lines.push('## Other Issues by Category');\n lines.push('');\n \n // Group by category\n const byCategory = new Map<string, Issue[]>();\n for (const issue of otherIssues) {\n const cat = issue.category || 'general';\n if (!byCategory.has(cat)) byCategory.set(cat, []);\n byCategory.get(cat)!.push(issue);\n }\n \n // Sort by count\n const sorted = Array.from(byCategory.entries())\n .filter(([cat]) => cat !== 'ai-analysis')\n .sort((a, b) => b[1].length - a[1].length)\n .slice(0, 10);\n \n for (const [category, catIssues] of sorted) {\n const icon = getCategoryIcon(category);\n const sevIcon = getSeverityIcon(catIssues[0]?.severity || 'low');\n lines.push(`### ${icon} ${formatCategoryName(category)} ${sevIcon} (${catIssues.length})`);\n \n const locations = catIssues\n .slice(0, 5)\n .map(i => `\\`${basename(i.file)}${i.line ? ':' + i.line : ''}\\``)\n .join(', ');\n lines.push(locations);\n \n if (catIssues[0]?.fix) {\n lines.push(`**Fix:** ${catIssues[0].fix}`);\n }\n lines.push('');\n }\n }\n \n // Cross-file patterns\n const crossFile = allIssues.filter(i => i.category === 'cross-file');\n if (crossFile.length > 0) {\n lines.push('## 🌐 Codebase-Wide Patterns');\n lines.push('');\n lines.push('*Issues appearing across many files - systemic problems:*');\n lines.push('');\n \n for (const issue of crossFile) {\n lines.push(`- ${issue.issue.replace(/^🕴️\\s*/, '').split('\\n')[0]}`);\n }\n lines.push('');\n }\n \n // Closing quote\n lines.push('---');\n lines.push('');\n if (allIssues.length > 50) {\n lines.push(`*${randomQuote(QUOTES.manyIssues)}*`);\n } else {\n lines.push(`*${randomQuote(QUOTES.fewIssues)}*`);\n }\n \n return lines.join('\\n');\n}\n\n/**\n * Run Agent Smith - Hybrid Pattern Detection + AI Analysis\n */\nexport async function runAgentSmith(args: AgentSmithArgs): Promise<{ content: Array<{ type: string; text: string }> }> {\n const startTime = Date.now();\n \n // Discover files\n let filesToScan = args.files || [];\n if (!filesToScan.length) {\n const scanDir = args.directory || process.cwd();\n console.error(`\\n🔍 Agent Smith: Discovering files in: ${scanDir}`);\n filesToScan = await discoverFiles(scanDir);\n console.error(` Found ${filesToScan.length} files\\n`);\n } else {\n filesToScan = filesToScan.map((f: string) => \n isAbsolute(f) ? f : resolve(process.cwd(), f)\n );\n }\n \n // Validate files exist\n const validFiles = filesToScan.filter((f: string) => existsSync(f));\n if (validFiles.length === 0) {\n return {\n content: [{\n type: 'text',\n text: '❌ No valid files found to scan.'\n }]\n };\n }\n \n // Phase 1: Pattern Detection\n console.error('🕴️ Phase 1: Pattern Detection');\n console.error(' Deploying 38 sub-agent hunters...');\n const smith = new AgentSmithAgent();\n \n const result = await smith.scan(validFiles, {\n workingDir: args.directory || process.cwd(),\n context: {}\n });\n \n const patternTime = Date.now() - startTime;\n \n // Filter out placeholder issues\n const patternIssues = result.issues.filter(i => i.category !== 'ai-analysis');\n console.error(` Found ${patternIssues.length} violations in ${(patternTime / 1000).toFixed(2)}s\\n`);\n \n // Phase 2: AI Enhancement (if available)\n let aiResult: { aiIssues: Issue[]; philosophical: string } | null = null;\n let aiTime = 0;\n \n if (isAIAvailable()) {\n console.error('🕴️ Phase 2: AI Deep Analysis');\n console.error(` ${getAIStatusMessage()}`);\n const aiStartTime = Date.now();\n \n aiResult = await runAIEnhancement(patternIssues, validFiles);\n aiTime = Date.now() - aiStartTime;\n \n if (aiResult) {\n console.error(` AI validated ${aiResult.aiIssues.filter(i => i.category === 'ai-validated').length} issues`);\n console.error(` AI discovered ${aiResult.aiIssues.filter(i => i.category === 'ai-found').length} new issues`);\n console.error(` AI analysis took ${(aiTime / 1000).toFixed(2)}s\\n`);\n } else {\n console.error(' AI enhancement returned no results\\n');\n }\n } else {\n console.error(`\\n${getAIStatusMessage()}`);\n console.error(' Set ANTHROPIC_API_KEY for AI-powered analysis\\n');\n }\n \n // Build report\n const report = await buildReport(patternIssues, aiResult, validFiles, patternTime, aiTime);\n \n return {\n content: [{\n type: 'text',\n text: report\n }]\n };\n}\n"],"mappings":"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","names":[]}
@@ -151,7 +151,99 @@ var AgentProgressReporter = class {
151
151
  };
152
152
 
153
153
  // src/agents/base-agent.ts
154
- import { basename, relative } from "path";
154
+ import { relative } from "path";
155
+
156
+ // src/ai/client.ts
157
+ import Anthropic from "@anthropic-ai/sdk";
158
+ var clientInstance = null;
159
+ var apiKeyChecked = false;
160
+ var apiKeyAvailable = false;
161
+ function isAIAvailable() {
162
+ if (!apiKeyChecked) {
163
+ checkAPIKey();
164
+ }
165
+ return apiKeyAvailable;
166
+ }
167
+ function checkAPIKey() {
168
+ apiKeyChecked = true;
169
+ const apiKey = process.env.ANTHROPIC_API_KEY;
170
+ if (apiKey && apiKey.length > 10) {
171
+ apiKeyAvailable = true;
172
+ return;
173
+ }
174
+ apiKeyAvailable = false;
175
+ }
176
+ function tryGetClient() {
177
+ if (!isAIAvailable()) {
178
+ return null;
179
+ }
180
+ if (!clientInstance) {
181
+ clientInstance = new Anthropic();
182
+ }
183
+ return clientInstance;
184
+ }
185
+ async function runAIAnalysis(request) {
186
+ const client = tryGetClient();
187
+ if (!client) {
188
+ return {
189
+ success: false,
190
+ content: "",
191
+ error: "AI not available - ANTHROPIC_API_KEY not set"
192
+ };
193
+ }
194
+ try {
195
+ const response = await client.messages.create({
196
+ model: "claude-sonnet-4-20250514",
197
+ max_tokens: request.maxTokens || 4096,
198
+ temperature: request.temperature ?? 0.3,
199
+ system: request.systemPrompt,
200
+ messages: [
201
+ {
202
+ role: "user",
203
+ content: request.userPrompt
204
+ }
205
+ ]
206
+ });
207
+ const textContent = response.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
208
+ return {
209
+ success: true,
210
+ content: textContent,
211
+ tokensUsed: {
212
+ input: response.usage.input_tokens,
213
+ output: response.usage.output_tokens
214
+ }
215
+ };
216
+ } catch (error) {
217
+ const errorMessage = error instanceof Error ? error.message : String(error);
218
+ if (errorMessage.includes("authentication") || errorMessage.includes("API key")) {
219
+ return {
220
+ success: false,
221
+ content: "",
222
+ error: "Invalid API key. Check your ANTHROPIC_API_KEY."
223
+ };
224
+ }
225
+ if (errorMessage.includes("rate limit")) {
226
+ return {
227
+ success: false,
228
+ content: "",
229
+ error: "Rate limited. Try again in a moment."
230
+ };
231
+ }
232
+ return {
233
+ success: false,
234
+ content: "",
235
+ error: `AI analysis failed: ${errorMessage}`
236
+ };
237
+ }
238
+ }
239
+ function getAIStatusMessage() {
240
+ if (isAIAvailable()) {
241
+ return "\u{1F916} AI-powered analysis enabled";
242
+ }
243
+ return "\u26A0\uFE0F AI not available (ANTHROPIC_API_KEY not set) - using pattern-only mode";
244
+ }
245
+
246
+ // src/agents/base-agent.ts
155
247
  var BaseAgent = class {
156
248
  // Progress reporter for real-time feedback
157
249
  progress = null;
@@ -200,129 +292,196 @@ var BaseAgent = class {
200
292
  }
201
293
  }
202
294
  /**
203
- * AI-First Analysis - The new way
295
+ * Hybrid AI + Pattern Analysis
204
296
  *
205
- * 1. Quick filter to identify relevant files
206
- * 2. Generate rich AI prompts for relevant files
207
- * 3. Return prompts for the AI to process
297
+ * 1. Run fast pattern detection on all files
298
+ * 2. If AI is available (ANTHROPIC_API_KEY set), enhance with AI analysis
299
+ * 3. AI validates findings, finds additional issues, provides intelligent fixes
208
300
  *
209
- * Subclasses should override this for AI-powered analysis
301
+ * Subclasses can override for custom behavior
210
302
  */
211
303
  async analyzeWithAI(files, context) {
212
- const issues = [];
213
- const aiRequests = [];
214
- this.progress?.aiReview("Identifying relevant files...");
215
- for (const file of files) {
304
+ this.progress?.aiReview("Running pattern detection...");
305
+ const patternIssues = await this.analyzeFiles(files, context);
306
+ if (!isAIAvailable()) {
307
+ console.error(` ${getAIStatusMessage()}`);
308
+ return patternIssues;
309
+ }
310
+ if (patternIssues.length > 0 || this.shouldAlwaysUseAI()) {
311
+ this.progress?.aiReview("Enhancing with AI analysis...");
216
312
  try {
217
- const content = await this.readFile(file);
218
- const relevance = this.checkFileRelevance(file, content);
219
- if (relevance.isRelevant) {
220
- this.progress?.analyzing(file);
221
- const request = await this.buildAIAnalysisRequest(file, content, relevance, context);
222
- if (request) {
223
- aiRequests.push(request);
224
- }
225
- }
313
+ const enhancedIssues = await this.runAIEnhancement(patternIssues, files, context);
314
+ return this.mergeIssues(enhancedIssues, patternIssues);
226
315
  } catch (error) {
316
+ console.error(` \u26A0\uFE0F AI enhancement failed: ${error instanceof Error ? error.message : String(error)}`);
317
+ return patternIssues;
227
318
  }
228
319
  }
229
- if (aiRequests.length > 0) {
230
- this.progress?.aiReview(`Preparing analysis for ${aiRequests.length} files...`);
231
- for (const request of aiRequests) {
232
- const aiIssues = await this.processAIRequest(request);
233
- issues.push(...aiIssues);
234
- }
235
- }
236
- const legacyIssues = await this.analyzeFiles(files, context);
237
- const allIssues = this.mergeIssues(issues, legacyIssues);
238
- return allIssues;
320
+ return patternIssues;
239
321
  }
240
322
  /**
241
- * Check if a file is relevant for this agent's analysis
242
- * Subclasses should override for domain-specific checks
323
+ * Whether this agent should always use AI even if no patterns detected
243
324
  */
244
- checkFileRelevance(_file, _content) {
245
- return {
246
- isRelevant: true,
247
- reason: "Default analysis",
248
- priority: "low",
249
- indicators: []
250
- };
325
+ shouldAlwaysUseAI() {
326
+ return false;
251
327
  }
252
328
  /**
253
- * Build an AI analysis request for a file
254
- * Subclasses should override to provide domain-specific prompts
329
+ * Run AI enhancement on pattern detection results
330
+ * Subclasses can override for custom AI behavior
255
331
  */
256
- async buildAIAnalysisRequest(file, content, relevance, _context) {
257
- const fileName = basename(file);
258
- const relPath = this.getRelativePath(file);
259
- return {
260
- file,
261
- code: content,
262
- analysisType: this.name,
263
- systemPrompt: this.getSystemPrompt(),
264
- userPrompt: this.buildUserPrompt(relPath, content, relevance),
265
- context: {
266
- fileName,
267
- relevance: relevance.reason,
268
- indicators: relevance.indicators
332
+ async runAIEnhancement(patternIssues, files, _context) {
333
+ const snippets = [];
334
+ const sortedIssues = [...patternIssues].sort((a, b) => {
335
+ const order = { critical: 0, serious: 1, moderate: 2, low: 3 };
336
+ return order[a.severity] - order[b.severity];
337
+ });
338
+ for (const issue of sortedIssues.slice(0, 15)) {
339
+ try {
340
+ const content = await this.readFile(issue.file);
341
+ const snippet = this.getCodeSnippet(content, issue.line || 1, 5);
342
+ snippets.push({
343
+ file: this.getRelativePath(issue.file),
344
+ line: issue.line,
345
+ issue: issue.issue,
346
+ code: snippet
347
+ });
348
+ } catch {
269
349
  }
270
- };
350
+ }
351
+ if (snippets.length === 0) {
352
+ return [];
353
+ }
354
+ const userPrompt = this.buildAIEnhancementPrompt(snippets, patternIssues.length);
355
+ const result = await runAIAnalysis({
356
+ systemPrompt: this.getAIEnhancementSystemPrompt(),
357
+ userPrompt,
358
+ maxTokens: 4096,
359
+ temperature: 0.2
360
+ });
361
+ if (!result.success) {
362
+ throw new Error(result.error || "AI analysis failed");
363
+ }
364
+ return this.parseAIEnhancementResponse(result.content, files);
271
365
  }
272
366
  /**
273
- * Get the system prompt for AI analysis
274
- * Subclasses should override with domain-specific prompts
367
+ * System prompt for AI enhancement
275
368
  */
276
- getSystemPrompt() {
277
- return `You are an expert code analyzer specializing in ${this.name} analysis.
278
- Analyze the provided code and identify issues with specific line numbers and actionable fixes.
279
- Be precise and avoid false positives. Only report issues you are confident about.`;
369
+ getAIEnhancementSystemPrompt() {
370
+ return `You are an expert ${this.name} code analyst. You are enhancing pattern-detected issues with deeper analysis.
371
+
372
+ Your job:
373
+ 1. VALIDATE: Confirm if each issue is a TRUE problem or FALSE positive
374
+ 2. EXPAND: Identify related issues that patterns missed
375
+ 3. PRIORITIZE: Rank by real-world impact (0-100 inevitability score)
376
+ 4. FIX: Provide specific, copy-paste-ready code fixes
377
+
378
+ Output format (STRICT JSON):
379
+ {
380
+ "validated": [
381
+ {
382
+ "original_issue": "...",
383
+ "verdict": "TRUE_POSITIVE" | "FALSE_POSITIVE",
384
+ "confidence": 0-100,
385
+ "file": "path/to/file",
386
+ "line": 123,
387
+ "severity": "critical" | "serious" | "moderate" | "low",
388
+ "explanation": "Why this is/isn't a problem",
389
+ "fix": "Specific code fix"
390
+ }
391
+ ],
392
+ "additional": [
393
+ {
394
+ "issue": "New issue found by AI",
395
+ "file": "path/to/file",
396
+ "line": 123,
397
+ "severity": "critical" | "serious" | "moderate" | "low",
398
+ "explanation": "What's wrong",
399
+ "fix": "How to fix it"
400
+ }
401
+ ],
402
+ "summary": "One paragraph summary of findings"
403
+ }`;
280
404
  }
281
405
  /**
282
- * Build the user prompt for AI analysis
406
+ * Build the user prompt for AI enhancement
283
407
  */
284
- buildUserPrompt(filePath, content, relevance) {
285
- return `Analyze this code for ${this.name} issues:
286
-
287
- **File:** ${filePath}
288
- **Relevance indicators:** ${relevance.indicators.join(", ") || "general analysis"}
289
-
408
+ buildAIEnhancementPrompt(snippets, totalIssues) {
409
+ const snippetText = snippets.map((s, i) => `### Issue ${i + 1}: ${s.file}${s.line ? ":" + s.line : ""}
410
+ **Pattern detected:** ${s.issue}
290
411
  \`\`\`
291
- ${content}
412
+ ${s.code}
292
413
  \`\`\`
414
+ `).join("\n");
415
+ return `Pattern detection found ${totalIssues} total issues. Here are the top ${snippets.length} for AI analysis:
416
+
417
+ ${snippetText}
293
418
 
294
- For each issue found, provide:
295
- 1. Severity (critical/serious/moderate/low)
296
- 2. Line number
297
- 3. Clear description of the issue
298
- 4. Specific fix recommendation`;
419
+ Analyze each issue. Validate, expand, and provide fixes. Output JSON only.`;
299
420
  }
300
421
  /**
301
- * Process an AI analysis request and return issues
302
- * This generates the prompt output for Claude to process
422
+ * Parse AI enhancement response into issues
303
423
  */
304
- async processAIRequest(request) {
305
- const fileName = basename(request.file);
306
- this.progress?.aiReview(`${fileName} queued for AI analysis`);
307
- return [{
308
- id: this.generateIssueId(),
309
- severity: "moderate",
310
- issue: `AI Analysis Required: ${request.analysisType}`,
311
- fix: "Review the AI analysis output below",
312
- file: request.file,
313
- confidence: 1,
314
- autoFixable: false,
315
- agent: this.name,
316
- effort: "medium",
317
- // Store the AI prompt for inclusion in output
318
- aiPrompt: {
319
- system: request.systemPrompt,
320
- user: request.userPrompt
424
+ parseAIEnhancementResponse(response, _files) {
425
+ const issues = [];
426
+ try {
427
+ const jsonMatch = response.match(/```json\s*([\s\S]*?)\s*```/) || response.match(/\{[\s\S]*\}/);
428
+ if (!jsonMatch) {
429
+ return issues;
430
+ }
431
+ const json = JSON.parse(jsonMatch[1] || jsonMatch[0]);
432
+ if (json.validated && Array.isArray(json.validated)) {
433
+ for (const v of json.validated) {
434
+ if (v.verdict === "TRUE_POSITIVE") {
435
+ issues.push(this.createIssue(
436
+ this.generateIssueId(),
437
+ v.severity || "moderate",
438
+ `[AI VALIDATED] ${v.original_issue || v.explanation}`,
439
+ v.fix || "See AI analysis",
440
+ v.file || "unknown",
441
+ v.line,
442
+ (v.confidence || 80) / 100,
443
+ void 0,
444
+ false,
445
+ { category: "ai-validated" }
446
+ ));
447
+ }
448
+ }
449
+ }
450
+ if (json.additional && Array.isArray(json.additional)) {
451
+ for (const a of json.additional) {
452
+ issues.push(this.createIssue(
453
+ this.generateIssueId(),
454
+ a.severity || "moderate",
455
+ `[AI FOUND] ${a.issue || a.explanation}`,
456
+ a.fix || "See AI analysis",
457
+ a.file || "unknown",
458
+ a.line,
459
+ 0.85,
460
+ // AI-found issues have good confidence
461
+ void 0,
462
+ false,
463
+ { category: "ai-found" }
464
+ ));
465
+ }
321
466
  }
322
- }];
467
+ } catch (error) {
468
+ }
469
+ return issues;
323
470
  }
324
471
  /**
325
- * Merge and deduplicate issues from AI and legacy analysis
472
+ * Check if a file is relevant for this agent's analysis
473
+ * Subclasses should override for domain-specific checks
474
+ */
475
+ checkFileRelevance(_file, _content) {
476
+ return {
477
+ isRelevant: true,
478
+ reason: "Default analysis",
479
+ priority: "low",
480
+ indicators: []
481
+ };
482
+ }
483
+ /**
484
+ * Merge and deduplicate issues from AI and pattern analysis
326
485
  */
327
486
  mergeIssues(aiIssues, legacyIssues) {
328
487
  const merged = [...aiIssues];
@@ -400,7 +559,7 @@ For each issue found, provide:
400
559
  // src/agents/agent-smith.ts
401
560
  import { readFile, writeFile, mkdir, rm } from "fs/promises";
402
561
  import { existsSync } from "fs";
403
- import { join, dirname, basename as basename2 } from "path";
562
+ import { join, dirname, basename } from "path";
404
563
  import { createHash } from "crypto";
405
564
  var MEMORY_LIMITS = {
406
565
  MAX_LOCATIONS_PER_ISSUE: 5,
@@ -645,6 +804,13 @@ var AgentSmithAgent = class extends BaseAgent {
645
804
  shouldActivate(_context) {
646
805
  return false;
647
806
  }
807
+ /**
808
+ * Override analyzeWithAI to skip the base class AI analysis.
809
+ * Agent Smith has its own sub-agent swarm - we don't want placeholder issues.
810
+ */
811
+ async analyzeWithAI(files, context) {
812
+ return this.analyzeFiles(files, context);
813
+ }
648
814
  // No override needed — base class returns 0 when shouldActivate is false
649
815
  async analyzeFiles(files, context) {
650
816
  const issues = [];
@@ -966,11 +1132,11 @@ Found in ${result.occurrences.length} files, ${result.totalCount} total instance
966
1132
  await this.updateMemory(hash, result.pattern, result.type, file, instances.length);
967
1133
  const severity = this.determineSeverity(instances.length, inevitabilityScore);
968
1134
  const smithNote = this.getPhilosophicalNote(result.type, instances.length);
969
- const locationsStr = instances.map((i) => `${basename2(i.file)}:${i.line}`).slice(0, 5).join(", ");
1135
+ const locationsStr = instances.map((i) => `${basename(i.file)}:${i.line}`).slice(0, 5).join(", ");
970
1136
  issues.push(this.createSmithIssue(
971
1137
  this.generateSmithIssueId(),
972
1138
  severity,
973
- `\u{1F574}\uFE0F ${config.description} \u2014 ${instances.length} instance${instances.length > 1 ? "s" : ""} in ${basename2(file)}
1139
+ `\u{1F574}\uFE0F ${config.description} \u2014 ${instances.length} instance${instances.length > 1 ? "s" : ""} in ${basename(file)}
974
1140
 
975
1141
  ${smithNote}
976
1142
 
@@ -1542,8 +1708,11 @@ Files scanned: ${files.length}
1542
1708
 
1543
1709
  export {
1544
1710
  ProgressReporter,
1711
+ isAIAvailable,
1712
+ runAIAnalysis,
1713
+ getAIStatusMessage,
1545
1714
  BaseAgent,
1546
1715
  SUB_AGENT_PATTERNS,
1547
1716
  AgentSmithAgent
1548
1717
  };
1549
- //# sourceMappingURL=chunk-WSBTQJMH.js.map
1718
+ //# sourceMappingURL=chunk-3AUDJWEF.js.map