cto-ai-cli 3.1.0 → 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.
- package/DOCS.md +151 -0
- package/README.md +124 -15
- package/dist/action/index.js +23 -1
- package/dist/api/dashboard.js +23 -1
- package/dist/api/dashboard.js.map +1 -1
- package/dist/api/server.js +23 -1
- package/dist/api/server.js.map +1 -1
- package/dist/cli/score.js +341 -2
- package/dist/cli/v2/index.js +23 -1
- package/dist/cli/v2/index.js.map +1 -1
- package/dist/engine/index.js +23 -1
- package/dist/engine/index.js.map +1 -1
- package/dist/govern/index.d.ts +25 -2
- package/dist/govern/index.js +155 -1
- package/dist/govern/index.js.map +1 -1
- package/dist/interact/index.js +23 -1
- package/dist/interact/index.js.map +1 -1
- package/dist/mcp/v2.js +23 -1
- package/dist/mcp/v2.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/interact/orchestrator.ts","../../src/engine/selector.ts","../../src/govern/secrets.ts","../../src/engine/pruner.ts","../../src/engine/tokenizer.ts","../../src/engine/graph-utils.ts","../../src/engine/coverage.ts","../../src/engine/budget.ts","../../src/interact/router.ts","../../src/interact/estimator.ts","../../src/interact/prompt.ts","../../src/govern/audit.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { InteractionPlan, PlanDecision, TaskType } from '../types/interact.js';\nimport type { ProjectAnalysis } from '../types/engine.js';\nimport type { PolicySet } from '../types/govern.js';\nimport { selectContext } from '../engine/selector.js';\nimport { classifyTask, routeModel } from './router.js';\nimport { estimateCost } from './estimator.js';\nimport { buildPrompt } from './prompt.js';\nimport { logAudit } from '../govern/audit.js';\n\n// ===== INTERACTION ORCHESTRATOR =====\n//\n// The brain of CTO v2. Given a task description:\n// 1. Classify task type\n// 2. Select optimal context (deterministic)\n// 3. Choose best model\n// 4. Build structured prompt\n// 5. Estimate cost\n// 6. Explain all decisions\n\nexport interface OrchestratorInput {\n task: string;\n analysis: ProjectAnalysis;\n budget?: number;\n model?: string;\n policies?: PolicySet;\n depth?: number;\n enableCoT?: boolean;\n enableConstraints?: boolean;\n enableAntiHallucination?: boolean;\n}\n\nexport async function planInteraction(input: OrchestratorInput): Promise<InteractionPlan> {\n const {\n task,\n analysis,\n budget = 50_000,\n model: preferredModel,\n policies,\n depth = 2,\n enableCoT = true,\n enableConstraints = true,\n enableAntiHallucination = true,\n } = input;\n\n const decisions: PlanDecision[] = [];\n\n // 1. CLASSIFY task\n const taskType: TaskType = classifyTask(task);\n decisions.push({\n step: 'classify',\n decision: taskType,\n reason: `Task classified as \"${taskType}\" based on keyword analysis`,\n data: { task, taskType },\n });\n\n // 2. SELECT context (deterministic)\n const context = await selectContext({\n task,\n analysis,\n budget,\n policies,\n depth,\n });\n\n decisions.push({\n step: 'select-context',\n decision: `${context.files.length} files selected (${context.totalTokens} tokens)`,\n reason: `Coverage: ${context.coverage.score}%, Risk: ${context.riskScore}/100`,\n data: {\n filesIncluded: context.files.length,\n tokensUsed: context.totalTokens,\n budget,\n coverage: context.coverage.score,\n risk: context.riskScore,\n },\n });\n\n // 3. CHOOSE model\n const modelChoice = routeModel(taskType, analysis, preferredModel);\n decisions.push({\n step: 'choose-model',\n decision: modelChoice.model,\n reason: modelChoice.reason,\n data: {\n confidence: modelChoice.confidence,\n alternatives: modelChoice.alternatives.length,\n },\n });\n\n // 4. BUILD prompt\n const prompt = buildPrompt({\n task,\n taskType,\n analysis,\n selection: context,\n enableCoT,\n enableConstraints,\n enableAntiHallucination,\n });\n\n decisions.push({\n step: 'build-prompt',\n decision: `${prompt.sections.length} sections, ${prompt.totalTokens} tokens`,\n reason: `Sections: ${prompt.sections.map((s) => s.id).join(', ')}`,\n });\n\n // 5. ESTIMATE cost\n const cost = estimateCost(\n modelChoice.model,\n context.totalTokens + prompt.totalTokens,\n analysis.totalTokens,\n );\n\n decisions.push({\n step: 'estimate-cost',\n decision: cost.formatted,\n reason: cost.savings.formatted,\n data: {\n inputTokens: cost.inputTokens,\n totalCost: cost.totalCost,\n savings: cost.savings.percent,\n },\n });\n\n const plan: InteractionPlan = {\n id: randomUUID().substring(0, 8),\n task,\n taskType,\n timestamp: new Date(),\n context,\n model: modelChoice,\n prompt,\n cost,\n decisions,\n };\n\n // 6. AUDIT LOG — record this interaction for governance\n try {\n await logAudit('interact', analysis.projectPath, {\n interactionId: plan.id,\n task,\n taskType,\n contextHash: context.hash,\n filesIncluded: context.files.length,\n filesExcluded: analysis.totalFiles - context.files.length,\n tokensUsed: context.totalTokens,\n coverageScore: context.coverage.score,\n riskScore: context.riskScore,\n model: modelChoice.model,\n estimatedCost: cost.totalCost,\n });\n } catch {\n // Audit failure should never block the interaction pipeline\n }\n\n return plan;\n}\n","import { createHash } from 'node:crypto';\nimport type {\n AnalyzedFile,\n ProjectAnalysis,\n ContextSelection,\n SelectedFile,\n SelectionDecision,\n PruneLevel,\n CoverageResult,\n} from '../types/engine.js';\nimport type { PolicySet, PolicyRule } from '../types/govern.js';\nimport { scanFileForSecrets } from '../govern/secrets.js';\nimport { pruneFile } from './pruner.js';\nimport { calculateCoverage } from './coverage.js';\nimport { getPruneLevelForRisk } from './budget.js';\nimport { buildAdjacencyList, bfsBidirectional, matchGlob } from './graph-utils.js';\n\n// ===== DETERMINISTIC CONTEXT SELECTION =====\n//\n// Given a task, analysis, budget, and policies → always returns the same file set.\n// No randomness, no heuristics that change over time.\n//\n// Algorithm:\n// 1. Identify target files from task description\n// 2. Expand via dependency graph (BFS, depth=2)\n// 3. Apply policies (include-always, exclude-always)\n// 4. Score & rank by riskScore\n// 5. Greedy allocation with cascading prune levels\n// 6. Validate policies\n// 7. Calculate coverage score\n// 8. Hash for reproducibility\n\nexport interface SelectionInput {\n task: string;\n analysis: ProjectAnalysis;\n budget: number;\n policies?: PolicySet;\n depth?: number;\n}\n\nexport async function selectContext(input: SelectionInput): Promise<ContextSelection> {\n const { task, analysis, budget, policies, depth = 2 } = input;\n const decisions: SelectionDecision[] = [];\n\n // 1. Identify target files from task description\n const targetPaths = identifyTargetFiles(task, analysis.files);\n if (targetPaths.length > 0) {\n decisions.push({\n file: targetPaths.join(', '),\n action: 'include-full',\n reason: `Target file(s) identified from task description`,\n });\n }\n\n // 2. Expand via dependency graph (O(V+E) using adjacency list)\n const adj = buildAdjacencyList(analysis.graph.edges);\n const expandedPaths = targetPaths.length > 0\n ? Array.from(bfsBidirectional(targetPaths, adj, depth))\n : [];\n const expansionCount = expandedPaths.length - targetPaths.length;\n if (expansionCount > 0) {\n decisions.push({\n file: `${expansionCount} dependencies`,\n action: 'include-full',\n reason: `Expanded ${targetPaths.length} target(s) to ${expandedPaths.length} files via dependency graph (depth ${depth})`,\n });\n }\n\n // 2b. Add type providers of expanded files as candidates.\n // This aligns with coverage.ts which counts type providers as \"relevant\".\n // Without this, coverage would flag missing critical type files that the\n // selector never considered as candidates — a consistency bug.\n const allFileMap = new Map(analysis.files.map((f) => [f.relativePath, f]));\n if (targetPaths.length > 0) {\n for (const path of expandedPaths) {\n const file = allFileMap.get(path);\n if (!file) continue;\n for (const imp of file.imports) {\n const impFile = allFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n expandedPaths.push(imp);\n }\n }\n }\n }\n\n // 3. Apply policies\n const { mustInclude, mustExclude } = applyPolicies(analysis.files, policies);\n\n // Merge: targets + expanded + must-include → candidate set\n const candidateSet = new Set([...expandedPaths, ...mustInclude]);\n\n // If no targets identified, use all files (filtered by exclude)\n if (targetPaths.length === 0) {\n for (const f of analysis.files) {\n candidateSet.add(f.relativePath);\n }\n }\n\n // Remove must-exclude\n for (const ex of mustExclude) {\n candidateSet.delete(ex);\n decisions.push({\n file: ex,\n action: 'exclude',\n reason: 'Excluded by policy',\n });\n }\n\n // 3b. secret-block: scan candidate files for secrets and exclude them\n const hasSecretBlock = policies?.rules.some(\n (r) => r.type === 'secret-block' && r.enabled,\n );\n if (hasSecretBlock) {\n for (const path of Array.from(candidateSet)) {\n const file = allFileMap.get(path);\n if (!file) continue;\n const findings = await scanFileForSecrets(\n file.path,\n analysis.projectPath,\n );\n if (findings.length > 0) {\n candidateSet.delete(path);\n decisions.push({\n file: path,\n action: 'exclude',\n reason: `Blocked: ${findings.length} secret(s) detected (${findings.map((f) => f.type).join(', ')})`,\n });\n }\n }\n }\n\n // 4. Sort candidates by riskScore descending\n const candidates = Array.from(candidateSet)\n .map((p) => allFileMap.get(p))\n .filter((f): f is AnalyzedFile => f !== undefined)\n .sort((a, b) => {\n // Targets always first\n const aIsTarget = targetPaths.includes(a.relativePath) ? 0 : 1;\n const bIsTarget = targetPaths.includes(b.relativePath) ? 0 : 1;\n if (aIsTarget !== bIsTarget) return aIsTarget - bIsTarget;\n\n // Then must-include\n const aIsMust = mustInclude.has(a.relativePath) ? 0 : 1;\n const bIsMust = mustInclude.has(b.relativePath) ? 0 : 1;\n if (aIsMust !== bIsMust) return aIsMust - bIsMust;\n\n // Then by riskScore\n return b.riskScore - a.riskScore;\n });\n\n // 5. Greedy allocation with cascading prune levels\n const selectedFiles: SelectedFile[] = [];\n let usedTokens = 0;\n\n for (const file of candidates) {\n const isTarget = targetPaths.includes(file.relativePath);\n const isMustInclude = mustInclude.has(file.relativePath);\n const defaultLevel = isTarget ? 'full' : getPruneLevelForRisk(file.riskScore);\n const levels = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levels) {\n if (level === 'excluded') break;\n\n let tokens: number;\n if (level === 'full') {\n tokens = file.tokens;\n } else {\n const pruned = await pruneFile(file, level);\n tokens = pruned.prunedTokens;\n }\n\n if (usedTokens + tokens <= budget) {\n usedTokens += tokens;\n selectedFiles.push({\n relativePath: file.relativePath,\n tokens,\n originalTokens: file.tokens,\n pruneLevel: level,\n riskScore: file.riskScore,\n reason: buildReason(file, level, isTarget, isMustInclude),\n });\n\n if (level !== defaultLevel) {\n decisions.push({\n file: file.relativePath,\n action: `include-${level}` as SelectionDecision['action'],\n reason: `Downgraded from ${defaultLevel} to ${level} due to budget constraint`,\n alternatives: `Would need ${file.tokens - tokens} more tokens for ${defaultLevel}`,\n });\n }\n\n included = true;\n break;\n }\n }\n\n if (!included) {\n decisions.push({\n file: file.relativePath,\n action: 'exclude',\n reason: `Budget exhausted (risk: ${file.riskScore}, needs ${file.tokens} tokens)`,\n });\n }\n }\n\n // 6. Calculate coverage\n const includedPaths = selectedFiles.map((f) => f.relativePath);\n const coverage = calculateCoverage(\n targetPaths,\n includedPaths,\n analysis.files,\n analysis.graph,\n depth,\n );\n\n // 7. Calculate overall risk of the selection (lower = better)\n const includedSet = new Set(includedPaths);\n const excludedFiles = analysis.files.filter(\n (f) => !includedSet.has(f.relativePath),\n );\n const excludedRisk = excludedFiles.length > 0\n ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length)\n : 0;\n\n // 8. Hash for determinism\n const hashInput = selectedFiles\n .map((f) => `${f.relativePath}:${f.pruneLevel}`)\n .sort()\n .join('|') + `|budget:${budget}`;\n const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 16);\n\n return {\n files: selectedFiles,\n totalTokens: usedTokens,\n budget,\n usedPercent: budget > 0 ? Math.round((usedTokens / budget) * 100 * 10) / 10 : 0,\n coverage,\n riskScore: excludedRisk,\n deterministic: true,\n hash,\n decisions,\n };\n}\n\n// ===== TARGET IDENTIFICATION =====\n\nfunction identifyTargetFiles(task: string, files: AnalyzedFile[]): string[] {\n const targets: string[] = [];\n\n // Extract file paths mentioned in the task\n // Matches patterns like: src/foo/bar.ts, ./utils.js, components/Header.tsx\n const pathPattern = /(?:^|\\s|[\"'`])([.\\w/-]+\\.[a-zA-Z]{1,4})(?:\\s|$|[\"'`]|,|:)/g;\n let match: RegExpExecArray | null;\n\n while ((match = pathPattern.exec(task)) !== null) {\n const candidate = match[1];\n // Find matching file\n const found = files.find(\n (f) => f.relativePath === candidate || f.relativePath.endsWith(candidate),\n );\n if (found && !targets.includes(found.relativePath)) {\n targets.push(found.relativePath);\n }\n }\n\n return targets;\n}\n\n// ===== POLICY APPLICATION =====\n\nfunction applyPolicies(\n files: AnalyzedFile[],\n policies?: PolicySet,\n): { mustInclude: Set<string>; mustExclude: Set<string> } {\n const mustInclude = new Set<string>();\n const mustExclude = new Set<string>();\n\n if (!policies) return { mustInclude, mustExclude };\n\n for (const rule of policies.rules) {\n if (!rule.enabled) continue;\n\n if (rule.type === 'include-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustInclude.add(file.relativePath);\n }\n }\n }\n\n if (rule.type === 'exclude-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustExclude.add(file.relativePath);\n }\n }\n }\n\n // secret-block is handled separately in applyPoliciesAsync\n // because it requires file I/O (scanning file contents)\n }\n\n return { mustInclude, mustExclude };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n\nfunction buildReason(\n file: AnalyzedFile,\n level: PruneLevel,\n isTarget: boolean,\n isMustInclude: boolean,\n): string {\n if (isTarget) return 'Target file';\n if (isMustInclude) return 'Required by policy';\n\n const impact = file.exclusionImpact;\n const levelStr = level === 'full' ? 'full content' : level;\n\n if (impact === 'critical') return `Critical dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'high') return `High-risk dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'medium') return `Medium relevance (risk ${file.riskScore}) — ${levelStr}`;\n return `Low relevance (risk ${file.riskScore}) — ${levelStr}`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, relative } from 'node:path';\nimport type { SecretFinding, SecretType } from '../types/govern.js';\n\n// ===== SECRET DETECTION ENGINE =====\n\ninterface SecretPattern {\n type: SecretType;\n pattern: RegExp;\n severity: SecretFinding['severity'];\n description: string;\n}\n\nconst BUILTIN_PATTERNS: { type: SecretType; source: string; flags: string; severity: SecretFinding['severity']; description: string }[] = [\n // API Keys\n { type: 'api-key', source: '(?:api[_-]?key|apikey)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{20,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'API Key' },\n { type: 'api-key', source: 'sk-[a-zA-Z0-9]{20,}', flags: 'g', severity: 'critical', description: 'OpenAI/Anthropic API Key' },\n { type: 'api-key', source: 'sk-ant-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'Anthropic API Key' },\n\n // AWS\n { type: 'aws-key', source: 'AKIA[0-9A-Z]{16}', flags: 'g', severity: 'critical', description: 'AWS Access Key ID' },\n { type: 'aws-key', source: '(?:aws_secret_access_key|aws_secret)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9/+=]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'AWS Secret Key' },\n\n // Private Keys\n { type: 'private-key', source: '-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'Private Key' },\n { type: 'private-key', source: '-----BEGIN OPENSSH PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'SSH Private Key' },\n\n // Passwords\n { type: 'password', source: '(?:password|passwd|pwd)\\\\s*[:=]\\\\s*[\\'\"]([^\\'\"]{8,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Hardcoded Password' },\n { type: 'password', source: '(?:DB_PASSWORD|DATABASE_PASSWORD|MYSQL_PASSWORD|POSTGRES_PASSWORD)\\\\s*[:=]\\\\s*[\\'\"]?([^\\'\"{}\\\\s]{4,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database Password' },\n\n // Tokens\n { type: 'token', source: '(?:bearer|token|auth_token|access_token|refresh_token)\\\\s*[:=]\\\\s*[\\'\"]([a-zA-Z0-9_\\\\-.]{20,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Auth Token' },\n { type: 'token', source: 'ghp_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub Personal Access Token' },\n { type: 'token', source: 'gho_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub OAuth Token' },\n { type: 'token', source: 'glpat-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'GitLab Personal Access Token' },\n { type: 'token', source: 'npm_[a-zA-Z0-9]{36}', flags: 'g', severity: 'high', description: 'npm Token' },\n\n // Connection strings\n { type: 'connection-string', source: '(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\\\/\\\\/[^\\\\s\\'\"]+:[^\\\\s\\'\"]+@[^\\\\s\\'\"]+', flags: 'gi', severity: 'critical', description: 'Database Connection String' },\n { type: 'connection-string', source: '(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{10,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database URL' },\n\n // Environment variables with secrets\n { type: 'env-variable', source: '(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{8,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Secret Environment Variable' },\n];\n\nfunction buildPatterns(customPatterns: string[] = []): SecretPattern[] {\n const patterns: SecretPattern[] = BUILTIN_PATTERNS.map((def) => ({\n type: def.type,\n pattern: new RegExp(def.source, def.flags),\n severity: def.severity,\n description: def.description,\n }));\n\n for (const custom of customPatterns) {\n try {\n patterns.push({\n type: 'custom',\n pattern: new RegExp(custom, 'gi'),\n severity: 'medium',\n description: `Custom pattern: ${custom}`,\n });\n } catch { /* skip invalid regex */ }\n }\n\n return patterns;\n}\n\nexport function scanContentForSecrets(\n content: string,\n filePath: string,\n customPatterns: string[] = [],\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n secretPattern.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = secretPattern.pattern.exec(line)) !== null) {\n const matchText = match[0];\n if (isTemplateOrPlaceholder(matchText)) continue;\n\n findings.push({\n type: secretPattern.type,\n file: filePath,\n line: i + 1,\n match: matchText,\n redacted: redactSecret(matchText),\n severity: secretPattern.severity,\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\nexport async function scanFileForSecrets(\n filePath: string,\n projectPath: string,\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(filePath));\n return scanContentForSecrets(content, relPath, customPatterns);\n } catch {\n return [];\n }\n}\n\nexport async function scanProjectForSecrets(\n projectPath: string,\n filePaths: string[],\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n const allFindings: SecretFinding[] = [];\n\n for (const fp of filePaths) {\n const findings = await scanFileForSecrets(fp, projectPath, customPatterns);\n allFindings.push(...findings);\n }\n\n return allFindings.sort((a, b) => {\n const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n}\n\nexport function sanitizeContent(content: string, customPatterns: string[] = []): string {\n let sanitized = content;\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n sanitized = sanitized.replace(secretPattern.pattern, (match) => {\n if (isTemplateOrPlaceholder(match)) return match;\n return redactSecret(match);\n });\n }\n\n return sanitized;\n}\n\nfunction redactSecret(value: string): string {\n if (value.length <= 8) return '***REDACTED***';\n const prefix = value.substring(0, 4);\n const suffix = value.substring(value.length - 2);\n return `${prefix}${'*'.repeat(Math.min(value.length - 6, 20))}${suffix}`;\n}\n\nfunction isTemplateOrPlaceholder(value: string): boolean {\n const placeholders = [\n /\\$\\{.*\\}/, /\\{\\{.*\\}\\}/, /%[sd]/, /<[A-Z_]+>/, /YOUR_.*_HERE/i,\n /\\bCHANGE_ME\\b/i, /\\bPLACEHOLDER\\b/i, /\\bexample\\b/i, /\\bTODO\\b/i, /xxx+/i,\n /\\breplace.?me\\b/i, /\\bdummy\\b/i, /\\btest_?key\\b/i, /\\bsample\\b/i,\n ];\n return placeholders.some((p) => p.test(value));\n}\n\nfunction deduplicateFindings(findings: SecretFinding[]): SecretFinding[] {\n const seen = new Set<string>();\n return findings.filter((f) => {\n const key = `${f.file}:${f.line}:${f.type}:${f.match}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","import { Project, SyntaxKind, type SourceFile } from 'ts-morph';\nimport { readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { AnalyzedFile, PruneLevel, PrunedContent } from '../types/engine.js';\nimport { countTokensChars4 } from './tokenizer.js';\n\nconst TS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mts', 'mjs']);\n\n// ===== PUBLIC API =====\n\nexport async function pruneFile(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n if (level === 'excluded') {\n return emptyResult(file, 'excluded');\n }\n\n if (level === 'full') {\n return fullContent(file);\n }\n\n const ext = file.extension.toLowerCase();\n const isTS = TS_EXTENSIONS.has(ext);\n\n if (isTS) {\n return pruneTypeScript(file, level);\n }\n\n return pruneGeneric(file, level);\n}\n\nexport async function pruneFiles(\n files: AnalyzedFile[],\n levelFn: (file: AnalyzedFile) => PruneLevel,\n): Promise<PrunedContent[]> {\n const results: PrunedContent[] = [];\n\n for (const file of files) {\n const level = levelFn(file);\n const pruned = await pruneFile(file, level);\n results.push(pruned);\n }\n\n return results;\n}\n\n// ===== TYPESCRIPT AST-BASED PRUNING =====\n\nasync function pruneTypeScript(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n let project: Project;\n try {\n const tsConfigPath = findTsConfig(file.path);\n project = new Project({\n tsConfigFilePath: tsConfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: tsConfigPath\n ? undefined\n : { allowJs: true, esModuleInterop: true },\n });\n project.createSourceFile(file.path, content, { overwrite: true });\n } catch {\n // Fallback to generic pruning if AST fails\n return pruneGenericFromContent(file, content, level);\n }\n\n const sourceFile = project.getSourceFiles()[0];\n if (!sourceFile) {\n return pruneGenericFromContent(file, content, level);\n }\n\n const prunedContent = level === 'signatures'\n ? extractSignaturesAST(sourceFile)\n : extractSkeletonAST(sourceFile);\n\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== SIGNATURES: Keep imports, type defs, function signatures, class outlines =====\n\nfunction extractSignaturesAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases\n for (const ta of sf.getTypeAliases()) {\n addJSDoc(ta, parts);\n parts.push(ta.getText());\n }\n\n // Interfaces\n for (const iface of sf.getInterfaces()) {\n addJSDoc(iface, parts);\n parts.push(iface.getText());\n }\n\n // Enums\n for (const en of sf.getEnums()) {\n addJSDoc(en, parts);\n parts.push(en.getText());\n }\n\n // Function declarations — signature only\n for (const fn of sf.getFunctions()) {\n addJSDoc(fn, parts);\n const isExported = fn.isExported();\n const isAsync = fn.isAsync();\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n const returnType = fn.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n\n const prefix = isExported ? 'export ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(`${prefix}${asyncStr}function ${name}(${params})${returnStr} { /* ... */ }`);\n }\n\n // Variable declarations (const/let with arrow functions)\n for (const stmt of sf.getVariableStatements()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init && (init.getKind() === SyntaxKind.ArrowFunction || init.getKind() === SyntaxKind.FunctionExpression)) {\n addJSDoc(stmt, parts);\n const isExported = stmt.isExported();\n const prefix = isExported ? 'export ' : '';\n const kind = stmt.getDeclarationKind();\n const name = decl.getName();\n const typeNode = decl.getTypeNode()?.getText();\n const typeStr = typeNode ? `: ${typeNode}` : '';\n parts.push(`${prefix}${kind} ${name}${typeStr} = /* ... */;`);\n } else {\n // Non-function variables: keep full declaration\n addJSDoc(stmt, parts);\n parts.push(stmt.getText());\n }\n }\n }\n\n // Classes — outline with method signatures\n for (const cls of sf.getClasses()) {\n addJSDoc(cls, parts);\n const isExported = cls.isExported();\n const prefix = isExported ? 'export ' : '';\n const name = cls.getName() ?? '<anonymous>';\n const ext = cls.getExtends()?.getText();\n const impl = cls.getImplements().map((i) => i.getText()).join(', ');\n let header = `${prefix}class ${name}`;\n if (ext) header += ` extends ${ext}`;\n if (impl) header += ` implements ${impl}`;\n header += ' {';\n parts.push(header);\n\n // Properties\n for (const prop of cls.getProperties()) {\n parts.push(` ${prop.getText()}`);\n }\n\n // Constructor\n const ctor = cls.getConstructors()[0];\n if (ctor) {\n const ctorParams = ctor.getParameters().map((p) => p.getText()).join(', ');\n parts.push(` constructor(${ctorParams}) { /* ... */ }`);\n }\n\n // Methods — signature only\n for (const method of cls.getMethods()) {\n const isStatic = method.isStatic();\n const isAsync = method.isAsync();\n const methodName = method.getName();\n const methodParams = method.getParameters().map((p) => p.getText()).join(', ');\n const returnType = method.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n const staticStr = isStatic ? 'static ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(` ${staticStr}${asyncStr}${methodName}(${methodParams})${returnStr} { /* ... */ }`);\n }\n\n parts.push('}');\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n // Export assignments\n for (const exp of sf.getExportAssignments()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== SKELETON: Keep only declarations, no bodies at all =====\n\nfunction extractSkeletonAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases — full\n for (const ta of sf.getTypeAliases()) {\n if (ta.isExported()) parts.push(ta.getText());\n }\n\n // Interfaces — name + extends only\n for (const iface of sf.getInterfaces()) {\n if (!iface.isExported()) continue;\n const ext = iface.getExtends().map((e) => e.getText());\n const extStr = ext.length > 0 ? ` extends ${ext.join(', ')}` : '';\n parts.push(`export interface ${iface.getName()}${extStr} { /* ${iface.getProperties().length} props */ }`);\n }\n\n // Enums — name only\n for (const en of sf.getEnums()) {\n if (!en.isExported()) continue;\n const members = en.getMembers().map((m) => m.getName());\n parts.push(`export enum ${en.getName()} { ${members.join(', ')} }`);\n }\n\n // Functions — name + params only\n for (const fn of sf.getFunctions()) {\n if (!fn.isExported()) continue;\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n parts.push(`export function ${name}(${params});`);\n }\n\n // Classes — name + method names only\n for (const cls of sf.getClasses()) {\n if (!cls.isExported()) continue;\n const methods = cls.getMethods().map((m) => m.getName());\n parts.push(`export class ${cls.getName()} { /* methods: ${methods.join(', ')} */ }`);\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== GENERIC PRUNING (non-TS files) =====\n\nasync function pruneGeneric(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n return pruneGenericFromContent(file, content, level);\n}\n\nfunction pruneGenericFromContent(\n file: AnalyzedFile,\n content: string,\n level: PruneLevel,\n): PrunedContent {\n const lines = content.split('\\n');\n let result: string[];\n\n if (level === 'signatures') {\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t === '' ||\n t.startsWith('#') ||\n t.startsWith('//') ||\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('async def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n t.startsWith('const ') ||\n t.startsWith('let ') ||\n t.startsWith('var ') ||\n /^(pub |fn |struct |enum |impl |mod |use )/.test(t)\n );\n });\n } else {\n // skeleton: even more aggressive — only declarations\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n /^(pub |fn |struct |enum |mod |use )/.test(t)\n );\n });\n }\n\n const prunedContent = result.join('\\n');\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== HELPERS =====\n\nasync function fullContent(file: AnalyzedFile): Promise<PrunedContent> {\n let content = '';\n try {\n content = await readFile(file.path, 'utf-8');\n } catch { /* empty */ }\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: file.tokens,\n pruneLevel: 'full',\n content,\n savingsPercent: 0,\n };\n}\n\nfunction emptyResult(file: AnalyzedFile, level: PruneLevel): PrunedContent {\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: 0,\n pruneLevel: level,\n content: '',\n savingsPercent: 100,\n };\n}\n\nfunction addJSDoc(node: { getJsDocs?: () => { getText(): string }[] }, parts: string[]): void {\n if (!node.getJsDocs) return;\n const docs = node.getJsDocs();\n if (docs.length > 0) {\n parts.push(docs[0].getText());\n }\n}\n\nfunction findTsConfig(filePath: string): string | undefined {\n let dir = filePath;\n for (let i = 0; i < 10; i++) {\n dir = join(dir, '..');\n const candidate = join(dir, 'tsconfig.json');\n if (existsSync(candidate)) return candidate;\n }\n return undefined;\n}\n","import { encodingForModel } from 'js-tiktoken';\nimport { readFile, stat } from 'node:fs/promises';\n\nconst CHARS_PER_TOKEN = 4;\n\nlet encoder: ReturnType<typeof encodingForModel> | null = null;\n\nfunction getEncoder() {\n if (!encoder) {\n encoder = encodingForModel('claude-3-5-sonnet-20241022' as Parameters<typeof encodingForModel>[0]);\n }\n return encoder;\n}\n\nexport function countTokensTiktoken(text: string): number {\n try {\n const enc = getEncoder();\n const tokens = enc.encode(text);\n return tokens.length;\n } catch {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n }\n}\n\nexport function countTokensChars4(sizeInBytes: number): number {\n return Math.ceil(sizeInBytes / CHARS_PER_TOKEN);\n}\n\nexport function estimateTokens(\n content: string,\n sizeInBytes: number,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): number {\n if (method === 'tiktoken') {\n return countTokensTiktoken(content);\n }\n return countTokensChars4(sizeInBytes);\n}\n\nexport async function estimateFileTokens(\n filePath: string,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): Promise<number> {\n if (method === 'chars4') {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n\n try {\n const content = await readFile(filePath, 'utf-8');\n return countTokensTiktoken(content);\n } catch {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n}\n\nexport function freeEncoder(): void {\n encoder = null;\n}\n","import type { GraphEdge } from '../types/engine.js';\n\n// ===== SHARED GRAPH UTILITIES =====\n\nexport interface AdjacencyList {\n forward: Map<string, string[]>; // file → files it imports\n reverse: Map<string, string[]>; // file → files that import it\n}\n\nexport function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList {\n const forward = new Map<string, string[]>();\n const reverse = new Map<string, string[]>();\n\n for (const edge of edges) {\n if (!forward.has(edge.from)) forward.set(edge.from, []);\n forward.get(edge.from)!.push(edge.to);\n\n if (!reverse.has(edge.to)) reverse.set(edge.to, []);\n reverse.get(edge.to)!.push(edge.from);\n }\n\n return { forward, reverse };\n}\n\nexport function bfsBidirectional(\n seeds: string[],\n adj: AdjacencyList,\n depth: number,\n): Set<string> {\n const result = new Set(seeds);\n let frontier = [...seeds];\n const visited = new Set<string>();\n\n for (let d = 0; d < depth; d++) {\n const nextFrontier: string[] = [];\n\n for (const node of frontier) {\n if (visited.has(node)) continue;\n visited.add(node);\n\n // Forward neighbors (imports)\n const fwd = adj.forward.get(node);\n if (fwd) {\n for (const neighbor of fwd) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n\n // Reverse neighbors (imported by)\n const rev = adj.reverse.get(node);\n if (rev) {\n for (const neighbor of rev) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n }\n\n frontier = nextFrontier;\n }\n\n return result;\n}\n\nexport function matchGlob(path: string, pattern: string): boolean {\n const regexStr = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '§§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§§/g, '.*')\n .replace(/\\?/g, '.');\n\n try {\n return new RegExp(`^${regexStr}$`).test(path);\n } catch {\n return false;\n }\n}\n","import type {\n AnalyzedFile,\n ProjectGraph,\n CoverageResult,\n} from '../types/engine.js';\nimport { buildAdjacencyList, bfsBidirectional } from './graph-utils.js';\n\n// ===== CONTEXT COVERAGE SCORING =====\n//\n// Given a set of included files and a task's target files, calculate\n// how much of the \"relevant universe\" is covered.\n//\n// coverageScore = |includedRelevant| / |relevantFiles| × 100\n//\n// \"Relevant\" = target files + their dependencies (up to `depth` levels)\n// + type providers used by included files.\n\nexport function calculateCoverage(\n targetPaths: string[],\n includedPaths: string[],\n allFiles: AnalyzedFile[],\n graph: ProjectGraph,\n depth: number = 2,\n): CoverageResult {\n // 1. Find all relevant files: targets + their dependency cone\n // Uses adjacency list for O(V+E) BFS instead of scanning all edges per node.\n const adj = buildAdjacencyList(graph.edges);\n const relevantSet = targetPaths.length > 0\n ? bfsBidirectional(targetPaths, adj, depth)\n : new Set<string>();\n const includedSet = new Set(includedPaths);\n\n // Also add type providers used by included files\n const tempFileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n for (const path of includedPaths) {\n const file = tempFileMap.get(path);\n if (!file) continue;\n\n for (const imp of file.imports) {\n const impFile = tempFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n relevantSet.add(imp);\n }\n }\n }\n\n // 2. Calculate coverage\n const relevantFiles = Array.from(relevantSet);\n const includedRelevant = relevantFiles.filter((f) => includedSet.has(f));\n const missingRelevant = relevantFiles.filter((f) => !includedSet.has(f));\n\n // Missing critical = missing files with critical/high exclusion impact\n const missingCritical = missingRelevant.filter((f) => {\n const file = tempFileMap.get(f);\n return file && (file.exclusionImpact === 'critical' || file.exclusionImpact === 'high');\n });\n\n // Risk-weighted coverage: missing a high-risk file hurts more than missing a low-risk one.\n // score = Σ(riskScore of included relevant) / Σ(riskScore of all relevant) × 100\n // Falls back to count-based if no risk data is available.\n const fileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n let totalRelevantRisk = 0;\n let includedRelevantRisk = 0;\n\n for (const f of relevantFiles) {\n const risk = fileMap.get(f)?.riskScore ?? 1;\n totalRelevantRisk += risk;\n if (includedSet.has(f)) {\n includedRelevantRisk += risk;\n }\n }\n\n const score = totalRelevantRisk > 0\n ? Math.round((includedRelevantRisk / totalRelevantRisk) * 100)\n : relevantFiles.length > 0\n ? Math.round((includedRelevant.length / relevantFiles.length) * 100)\n : 100;\n\n // Build explanation\n let explanation: string;\n if (score >= 90) {\n explanation = `Excellent coverage (${score}%): AI has nearly all relevant context.`;\n } else if (score >= 70) {\n explanation = `Good coverage (${score}%): Most relevant files included.`;\n if (missingCritical.length > 0) {\n explanation += ` Warning: ${missingCritical.length} critical file(s) missing.`;\n }\n } else if (score >= 50) {\n explanation = `Partial coverage (${score}%): Significant context is missing.`;\n if (missingCritical.length > 0) {\n explanation += ` ${missingCritical.length} critical file(s) not included — AI quality will degrade.`;\n }\n } else {\n explanation = `Low coverage (${score}%): Most relevant files are excluded. AI response quality will be poor.`;\n }\n\n return {\n score,\n relevantFiles,\n includedRelevant,\n missingRelevant,\n missingCritical,\n explanation,\n };\n}\n","import type { AnalyzedFile, BudgetPlan, BudgetEntry, PruneLevel } from '../types/engine.js';\nimport { pruneFile } from './pruner.js';\n\n// ===== BUDGET OPTIMIZER =====\n//\n// Greedy knapsack with cascading prune levels.\n// Files are sorted by riskScore (descending) — highest risk first.\n// Each file is tried at progressively smaller prune levels until it fits.\n\nexport function getPruneLevelForRisk(riskScore: number): PruneLevel {\n if (riskScore >= 80) return 'full'; // critical — include everything, no cascading\n if (riskScore >= 60) return 'full'; // high — try full first, can cascade\n if (riskScore >= 30) return 'signatures'; // medium — signatures by default\n return 'skeleton'; // low — skeleton\n}\n\nexport function isCriticalRisk(riskScore: number): boolean {\n return riskScore >= 80;\n}\n\nexport async function optimizeBudget(\n files: AnalyzedFile[],\n budget: number,\n): Promise<BudgetPlan> {\n const entries: BudgetEntry[] = [];\n let used = 0;\n\n // Sort by riskScore descending — most important files first\n const sorted = [...files].sort((a, b) => b.riskScore - a.riskScore);\n\n for (const file of sorted) {\n const defaultLevel = getPruneLevelForRisk(file.riskScore);\n\n // Try cascading prune levels: full → signatures → skeleton → excluded\n const levelsToTry = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levelsToTry) {\n if (level === 'excluded') break;\n\n if (level === 'full') {\n if (used + file.tokens <= budget) {\n used += file.tokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: file.tokens,\n pruneLevel: 'full',\n included: true,\n reason: `Risk ${file.riskScore} — included in full`,\n });\n included = true;\n break;\n }\n } else {\n const pruned = await pruneFile(file, level);\n if (pruned.prunedTokens > 0 && used + pruned.prunedTokens <= budget) {\n used += pruned.prunedTokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: pruned.prunedTokens,\n pruneLevel: level,\n included: true,\n reason: `Risk ${file.riskScore} — pruned to ${level} (budget constraint)`,\n });\n included = true;\n break;\n }\n }\n }\n\n if (!included) {\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: 0,\n pruneLevel: 'excluded',\n included: false,\n reason: `Risk ${file.riskScore} — excluded (budget exhausted, ${file.tokens} tokens)`,\n });\n }\n }\n\n return {\n budget,\n used,\n remaining: budget - used,\n fillPercent: budget > 0 ? Math.round((used / budget) * 100 * 10) / 10 : 0,\n files: entries,\n };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n","import type { TaskType, ModelChoice, ModelSpec, ModelId } from '../types/interact.js';\nimport type { ProjectAnalysis } from '../types/engine.js';\n\n// ===== MODEL REGISTRY =====\n\nexport const MODEL_REGISTRY: ModelSpec[] = [\n {\n id: 'claude-haiku-3.5',\n name: 'Claude 3.5 Haiku',\n tier: 'fast',\n pricing: { inputPerMillion: 0.80, outputPerMillion: 4.00, cacheReadPerMillion: 0.08 },\n contextWindow: 200_000,\n strengths: ['speed', 'simple-tasks', 'low-cost'],\n },\n {\n id: 'claude-sonnet-4',\n name: 'Claude Sonnet 4',\n tier: 'balanced',\n pricing: { inputPerMillion: 3.00, outputPerMillion: 15.00, cacheReadPerMillion: 0.30 },\n contextWindow: 200_000,\n strengths: ['code-generation', 'refactoring', 'balanced-reasoning'],\n },\n {\n id: 'claude-opus-4',\n name: 'Claude Opus 4',\n tier: 'reasoning',\n pricing: { inputPerMillion: 15.00, outputPerMillion: 75.00, cacheReadPerMillion: 1.50 },\n contextWindow: 200_000,\n strengths: ['deep-reasoning', 'architecture', 'complex-debugging'],\n },\n];\n\n// ===== ROUTING RULES =====\n\ninterface RoutingRule {\n task: TaskType;\n defaultModel: ModelId;\n upgradeIf: (analysis: ProjectAnalysis) => boolean;\n upgradeTo: ModelId;\n reason: string;\n upgradeReason: string;\n}\n\nconst ROUTING_RULES: RoutingRule[] = [\n {\n task: 'simple-edit',\n defaultModel: 'claude-haiku-3.5',\n upgradeIf: () => false,\n upgradeTo: 'claude-haiku-3.5',\n reason: 'Simple edits are best handled by fast models',\n upgradeReason: '',\n },\n {\n task: 'docs',\n defaultModel: 'claude-haiku-3.5',\n upgradeIf: (a) => a.totalTokens > 100_000,\n upgradeTo: 'claude-sonnet-4',\n reason: 'Documentation tasks are straightforward',\n upgradeReason: 'Large codebase — Sonnet provides better understanding',\n },\n {\n task: 'test',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.riskProfile.overallComplexity > 15,\n upgradeTo: 'claude-opus-4',\n reason: 'Test generation requires good code understanding',\n upgradeReason: 'High complexity codebase — Opus for better test coverage',\n },\n {\n task: 'debug',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.riskProfile.distribution.critical > 5,\n upgradeTo: 'claude-opus-4',\n reason: 'Debugging requires solid reasoning about code flow',\n upgradeReason: 'Many critical files involved — Opus for deeper analysis',\n },\n {\n task: 'refactor',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.totalFiles > 50 && a.riskProfile.overallComplexity > 10,\n upgradeTo: 'claude-opus-4',\n reason: 'Refactoring needs good structural understanding',\n upgradeReason: 'Large + complex project — Opus for safer refactoring',\n },\n {\n task: 'review',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.riskProfile.distribution.critical > 3,\n upgradeTo: 'claude-opus-4',\n reason: 'Code review benefits from balanced reasoning',\n upgradeReason: 'Critical code under review — Opus for thorough analysis',\n },\n {\n task: 'feature',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.totalFiles > 100,\n upgradeTo: 'claude-opus-4',\n reason: 'Feature development needs code generation + understanding',\n upgradeReason: 'Large codebase — Opus for better integration',\n },\n {\n task: 'architecture',\n defaultModel: 'claude-opus-4',\n upgradeIf: () => false,\n upgradeTo: 'claude-opus-4',\n reason: 'Architecture decisions require deep reasoning',\n upgradeReason: '',\n },\n];\n\n// ===== TASK CLASSIFICATION =====\n\nconst TASK_KEYWORDS: Record<TaskType, string[]> = {\n debug: ['debug', 'fix', 'bug', 'error', 'issue', 'broken', 'crash', 'failing', 'wrong'],\n review: ['review', 'check', 'assess', 'evaluate', 'audit', 'inspect', 'critique'],\n refactor: ['refactor', 'restructure', 'reorganize', 'clean up', 'simplify', 'extract', 'move'],\n test: ['test', 'spec', 'coverage', 'unit test', 'integration test', 'e2e'],\n docs: ['document', 'docs', 'readme', 'jsdoc', 'comment', 'explain'],\n feature: ['add', 'implement', 'create', 'build', 'new', 'feature', 'endpoint'],\n architecture: ['architecture', 'design', 'system', 'structure', 'migrate', 'pattern'],\n 'simple-edit': ['rename', 'typo', 'update', 'change', 'modify', 'tweak', 'adjust'],\n};\n\nexport function classifyTask(taskDescription: string): TaskType {\n const lower = taskDescription.toLowerCase();\n\n // Score each task type by keyword matches\n let bestType: TaskType = 'simple-edit';\n let bestScore = 0;\n\n for (const [type, keywords] of Object.entries(TASK_KEYWORDS) as [TaskType, string[]][]) {\n let score = 0;\n for (const kw of keywords) {\n if (lower.includes(kw)) score++;\n }\n if (score > bestScore) {\n bestScore = score;\n bestType = type;\n }\n }\n\n return bestType;\n}\n\n// ===== MODEL ROUTING =====\n\nexport function routeModel(\n taskType: TaskType,\n analysis: ProjectAnalysis,\n preferredModel?: ModelId,\n): ModelChoice {\n // If user explicitly chose a model, respect it\n if (preferredModel) {\n const spec = MODEL_REGISTRY.find((m) => m.id === preferredModel);\n if (spec) {\n return {\n model: preferredModel,\n reason: 'User-specified model',\n confidence: 1.0,\n alternatives: buildAlternatives(preferredModel, taskType),\n };\n }\n }\n\n // Find routing rule for this task\n const rule = ROUTING_RULES.find((r) => r.task === taskType);\n if (!rule) {\n return {\n model: 'claude-sonnet-4',\n reason: 'Default model for unrecognized task type',\n confidence: 0.5,\n alternatives: buildAlternatives('claude-sonnet-4', taskType),\n };\n }\n\n // Check if upgrade is warranted\n const shouldUpgrade = rule.upgradeIf(analysis);\n const model = shouldUpgrade ? rule.upgradeTo : rule.defaultModel;\n const reason = shouldUpgrade ? rule.upgradeReason : rule.reason;\n\n return {\n model,\n reason,\n confidence: shouldUpgrade ? 0.8 : 0.9,\n alternatives: buildAlternatives(model, taskType),\n };\n}\n\n// ===== HELPERS =====\n\nfunction buildAlternatives(chosenModel: ModelId, taskType: TaskType) {\n return MODEL_REGISTRY\n .filter((m) => m.id !== chosenModel)\n .map((m) => {\n const chosen = MODEL_REGISTRY.find((r) => r.id === chosenModel)!;\n const costDelta = m.pricing.inputPerMillion - chosen.pricing.inputPerMillion;\n const tradeoff = costDelta > 0\n ? `More capable but ${(costDelta / chosen.pricing.inputPerMillion * 100).toFixed(0)}% more expensive`\n : `${Math.abs(costDelta / chosen.pricing.inputPerMillion * 100).toFixed(0)}% cheaper but less capable`;\n\n return { model: m.id, costDelta, tradeoff };\n });\n}\n\nexport function getModelSpec(modelId: ModelId): ModelSpec | undefined {\n return MODEL_REGISTRY.find((m) => m.id === modelId);\n}\n","import type { CostEstimate, ModelId } from '../types/interact.js';\nimport { getModelSpec, MODEL_REGISTRY } from './router.js';\n\n// ===== COST ESTIMATION =====\n\nexport function estimateCost(\n modelId: ModelId,\n inputTokens: number,\n totalProjectTokens: number,\n estimatedOutputRatio: number = 0.3,\n): CostEstimate {\n const spec = getModelSpec(modelId) ?? MODEL_REGISTRY[1]; // fallback to Sonnet\n const estimatedOutputTokens = Math.round(inputTokens * estimatedOutputRatio);\n\n const inputCost = (inputTokens / 1_000_000) * spec.pricing.inputPerMillion;\n const outputCost = (estimatedOutputTokens / 1_000_000) * spec.pricing.outputPerMillion;\n const totalCost = inputCost + outputCost;\n\n // Without optimization = sending all project tokens\n const woInputCost = (totalProjectTokens / 1_000_000) * spec.pricing.inputPerMillion;\n const woOutputCost = (Math.round(totalProjectTokens * estimatedOutputRatio) / 1_000_000) * spec.pricing.outputPerMillion;\n const woTotalCost = woInputCost + woOutputCost;\n\n const tokensSaved = totalProjectTokens - inputTokens;\n const costSaved = woTotalCost - totalCost;\n const savingsPercent = woTotalCost > 0 ? (costSaved / woTotalCost) * 100 : 0;\n\n return {\n model: modelId,\n inputTokens,\n estimatedOutputTokens,\n inputCost: round(inputCost),\n outputCost: round(outputCost),\n totalCost: round(totalCost),\n formatted: formatCost(totalCost),\n\n withoutOptimization: {\n inputTokens: totalProjectTokens,\n totalCost: round(woTotalCost),\n formatted: formatCost(woTotalCost),\n },\n\n savings: {\n tokensSaved: Math.max(0, tokensSaved),\n costSaved: round(Math.max(0, costSaved)),\n percent: Math.max(0, Math.round(savingsPercent)),\n formatted: costSaved > 0\n ? `saved ${formatCost(costSaved)} (${Math.round(savingsPercent)}%)`\n : 'no savings',\n },\n };\n}\n\n// ===== HELPERS =====\n\nfunction round(n: number): number {\n return Math.round(n * 1_000_000) / 1_000_000;\n}\n\nfunction formatCost(cost: number): string {\n if (cost < 0.001) return '<$0.001';\n if (cost < 0.01) return `$${cost.toFixed(4)}`;\n if (cost < 1) return `$${cost.toFixed(3)}`;\n return `$${cost.toFixed(2)}`;\n}\n","import type { StructuredPrompt, PromptSection, TaskType } from '../types/interact.js';\nimport type { ContextSelection, ProjectAnalysis } from '../types/engine.js';\nimport { countTokensChars4 } from '../engine/tokenizer.js';\n\n// ===== STRUCTURED PROMPT BUILDER =====\n//\n// Not templates. Not libraries.\n// Task-aware prompt construction with sections that can be measured and optimized.\n\nexport interface PromptOptions {\n task: string;\n taskType: TaskType;\n analysis: ProjectAnalysis;\n selection: ContextSelection;\n enableCoT?: boolean;\n enableConstraints?: boolean;\n enableAntiHallucination?: boolean;\n}\n\nexport function buildPrompt(options: PromptOptions): StructuredPrompt {\n const {\n task,\n taskType,\n analysis,\n selection,\n enableCoT = true,\n enableConstraints = true,\n enableAntiHallucination = true,\n } = options;\n\n const sections: PromptSection[] = [];\n\n // 1. System section — role priming\n sections.push(buildSystemSection(analysis.stack, taskType));\n\n // 2. Context section — project info + selected files\n sections.push(buildContextSection(analysis, selection));\n\n // 3. Task section — what the user wants\n sections.push(buildTaskSection(task, taskType));\n\n // 4. Constraints section — safety rails\n if (enableConstraints) {\n sections.push(buildConstraintsSection(analysis.stack, taskType));\n }\n\n // 5. Chain-of-thought section\n if (enableCoT) {\n sections.push(buildCoTSection(taskType));\n }\n\n // 6. Anti-hallucination section\n if (enableAntiHallucination) {\n sections.push(buildAntiHallucinationSection());\n }\n\n // 7. Output format section\n sections.push(buildFormatSection(taskType));\n\n const rendered = sections.map((s) => s.content).join('\\n\\n---\\n\\n');\n const totalTokens = sections.reduce((s, sec) => s + sec.tokens, 0);\n\n return { sections, totalTokens, rendered };\n}\n\n// ===== SECTION BUILDERS =====\n\nfunction buildSystemSection(stack: string[], taskType: TaskType): PromptSection {\n const stackStr = stack.length > 0 ? stack.join(', ') : 'software';\n const taskRole = TASK_ROLES[taskType] ?? 'engineer';\n\n const content = [\n `You are a senior ${stackStr} ${taskRole} with deep expertise in clean architecture, testing, and production-quality code.`,\n 'You prioritize correctness, readability, and maintainability.',\n 'You never make assumptions without evidence from the code.',\n ].join(' ');\n\n return makeSection('system', 'system', content);\n}\n\nfunction buildContextSection(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n): PromptSection {\n const lines: string[] = [];\n\n lines.push(`## Project: ${analysis.projectName}`);\n lines.push(`Stack: ${analysis.stack.join(', ') || 'Unknown'}`);\n lines.push(`Files analyzed: ${analysis.totalFiles} | Tokens: ~${Math.round(analysis.totalTokens / 1000)}K`);\n lines.push(`Context coverage: ${selection.coverage.score}% | Risk score: ${selection.riskScore}/100`);\n lines.push('');\n\n // List included files with their prune levels\n lines.push('### Included Files');\n lines.push('');\n\n const fullFiles = selection.files.filter((f) => f.pruneLevel === 'full');\n const sigFiles = selection.files.filter((f) => f.pruneLevel === 'signatures');\n const skelFiles = selection.files.filter((f) => f.pruneLevel === 'skeleton');\n\n if (fullFiles.length > 0) {\n lines.push('**Full content (read these first):**');\n for (const f of fullFiles) {\n lines.push(`- \\`${f.relativePath}\\` (~${Math.round(f.tokens / 1000)}K tokens) — ${f.reason}`);\n }\n lines.push('');\n }\n\n if (sigFiles.length > 0) {\n lines.push('**Signatures only (reference as needed):**');\n for (const f of sigFiles) {\n lines.push(`- \\`${f.relativePath}\\` (~${Math.round(f.tokens / 1000)}K tokens)`);\n }\n lines.push('');\n }\n\n if (skelFiles.length > 0) {\n lines.push('**Skeleton (structure overview):**');\n for (const f of skelFiles) {\n lines.push(`- \\`${f.relativePath}\\``);\n }\n lines.push('');\n }\n\n if (selection.coverage.missingCritical.length > 0) {\n lines.push('⚠️ **Missing critical files** (not included due to budget):');\n for (const f of selection.coverage.missingCritical) {\n lines.push(`- \\`${f}\\``);\n }\n lines.push('');\n }\n\n const content = lines.join('\\n');\n return makeSection('context', 'context', content);\n}\n\nfunction buildTaskSection(task: string, taskType: TaskType): PromptSection {\n const content = [\n '## Task',\n '',\n task,\n '',\n `Task type: **${taskType}**`,\n ].join('\\n');\n\n return makeSection('task', 'task', content);\n}\n\nfunction buildConstraintsSection(stack: string[], taskType: TaskType): PromptSection {\n const lines: string[] = ['## Constraints', ''];\n\n lines.push('- **Do NOT** delete or modify existing tests unless explicitly asked');\n lines.push('- **Do NOT** change function signatures that are part of the public API');\n lines.push('- **Do NOT** introduce new dependencies without mentioning it');\n lines.push('- **Always** handle errors explicitly (no silent catches)');\n lines.push('- **Always** preserve existing code style and conventions');\n lines.push('- **Prefer** minimal changes — smallest diff that solves the problem');\n\n if (stack.includes('TypeScript')) {\n lines.push('- **Always** use strict TypeScript types (no `any` unless unavoidable)');\n lines.push('- **Always** add explicit return types to exported functions');\n }\n\n if (taskType === 'refactor') {\n lines.push('- **Do NOT** change behavior — refactoring must be behavior-preserving');\n lines.push('- **Verify** all existing tests still pass after changes');\n }\n\n if (taskType === 'test') {\n lines.push('- Use AAA pattern: Arrange, Act, Assert');\n lines.push('- Test boundaries, null/undefined, async errors, type edges');\n lines.push('- Use descriptive test names: \"should [expected] when [condition]\"');\n }\n\n return makeSection('constraints', 'constraints', lines.join('\\n'));\n}\n\nfunction buildCoTSection(taskType: TaskType): PromptSection {\n const steps = COT_STEPS[taskType] ?? COT_STEPS['simple-edit'];\n\n const lines: string[] = ['## Thinking Process', '', 'Before writing any code:'];\n steps.forEach((step, i) => {\n lines.push(`${i + 1}. ${step}`);\n });\n\n return makeSection('cot', 'constraints', lines.join('\\n'));\n}\n\nfunction buildAntiHallucinationSection(): PromptSection {\n const content = [\n '## Important',\n '',\n '- Only reference files, functions, and APIs that exist in the provided context',\n '- If you are unsure about something, say so explicitly',\n '- Do NOT invent function signatures, types, or module paths',\n '- If the context is insufficient to complete the task, explain what is missing',\n ].join('\\n');\n\n return makeSection('anti-hallucination', 'constraints', content);\n}\n\nfunction buildFormatSection(taskType: TaskType): PromptSection {\n const lines: string[] = ['## Output Format', ''];\n\n if (taskType === 'review') {\n lines.push('Provide findings in priority order: Critical > Major > Minor > Nitpick');\n lines.push('For each finding: file, line, issue, and concrete suggestion with code');\n } else if (taskType === 'architecture') {\n lines.push('Present 2-3 options with trade-offs, then recommend one with justification');\n } else if (taskType === 'debug') {\n lines.push('1. Root cause analysis');\n lines.push('2. Minimal fix');\n lines.push('3. Explanation of why the fix works');\n lines.push('4. Edge cases to consider');\n } else {\n lines.push('Provide clean, production-ready code with brief explanations of key decisions');\n }\n\n return makeSection('format', 'format', lines.join('\\n'));\n}\n\n// ===== CONSTANTS =====\n\nconst TASK_ROLES: Record<TaskType, string> = {\n debug: 'debugger',\n review: 'code reviewer',\n refactor: 'architect',\n test: 'test engineer',\n docs: 'technical writer',\n feature: 'engineer',\n architecture: 'systems architect',\n 'simple-edit': 'engineer',\n};\n\nconst COT_STEPS: Record<TaskType, string[]> = {\n debug: [\n '**Reproduce** — Understand the exact symptom and when it occurs',\n '**Hypothesize** — List the most likely root causes (max 3)',\n '**Verify** — Check each hypothesis against the code',\n '**Fix** — Apply the minimal fix that addresses the root cause',\n '**Validate** — Explain why the fix works and what edge cases it covers',\n ],\n review: [\n '**Understand** — Read the code and understand its purpose',\n '**Assess** — Evaluate correctness, readability, performance, security',\n '**Prioritize** — Rank issues by severity (critical > major > minor)',\n '**Suggest** — Provide concrete, actionable improvements with code',\n ],\n refactor: [\n '**Analyze** — Identify code smells and structural issues',\n '**Plan** — Define the target structure before changing anything',\n '**Preserve** — Ensure behavior doesn\\'t change',\n '**Refactor** — Apply changes incrementally',\n '**Verify** — Confirm all existing tests still pass',\n ],\n test: [\n '**Identify** — What needs testing? (happy path, edge cases, errors)',\n '**Structure** — Use AAA pattern: Arrange, Act, Assert',\n '**Cover** — Test boundaries, null/undefined, async errors',\n '**Isolate** — Mock external dependencies, test units independently',\n ],\n docs: [\n '**Read** — Understand the code before documenting',\n '**Structure** — Organize by audience (API users, contributors, operators)',\n '**Write** — Clear, concise, with examples',\n ],\n feature: [\n '**Clarify** — Restate the requirement in your own words',\n '**Design** — Plan the approach (types, interfaces, flow)',\n '**Implement** — Build incrementally, starting with types',\n '**Test** — Write tests alongside implementation',\n '**Integrate** — Ensure no regressions',\n ],\n architecture: [\n '**Context** — Understand current architecture and constraints',\n '**Options** — Present 2-3 viable approaches with trade-offs',\n '**Recommend** — Choose the best and explain why',\n '**Plan** — Define migration steps',\n '**Risks** — Identify risks and mitigation strategies',\n ],\n 'simple-edit': [\n '**Understand** — Read the relevant code',\n '**Plan** — Think before writing',\n '**Implement** — Write clean, well-typed code',\n '**Verify** — Check for edge cases',\n ],\n};\n\n// ===== HELPERS =====\n\nfunction makeSection(\n id: string,\n role: PromptSection['role'],\n content: string,\n): PromptSection {\n const tokens = countTokensChars4(Buffer.byteLength(content, 'utf-8'));\n return { id, role, content, tokens };\n}\n","import { randomUUID, createHash } from 'node:crypto';\nimport { readdir, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { userInfo } from 'node:os';\nimport { homedir } from 'node:os';\nimport type { AuditEntry, AuditAction } from '../types/govern.js';\n\n// ===== AUDIT TRAIL =====\n//\n// Immutable, tamper-evident audit log for all CTO interactions.\n// Each entry is SHA-256 hashed for integrity verification.\n\nconst CTO_DIR = '.cto-ai';\nconst AUDIT_DIR = 'audit';\nconst MAX_ENTRIES_PER_FILE = 500;\n\nfunction getAuditDir(): string {\n return join(homedir(), CTO_DIR, AUDIT_DIR);\n}\n\nfunction getCurrentAuditFile(): string {\n const date = new Date().toISOString().split('T')[0].replace(/-/g, '');\n return join(getAuditDir(), `audit_${date}.json`);\n}\n\nfunction computeIntegrityHash(entry: Omit<AuditEntry, 'integrityHash'>): string {\n const payload = JSON.stringify({\n id: entry.id,\n timestamp: entry.timestamp,\n action: entry.action,\n user: entry.user,\n projectPath: entry.projectPath,\n details: entry.details,\n });\n return createHash('sha256').update(payload).digest('hex');\n}\n\n// ===== HELPERS (inline to avoid cross-layer deps) =====\n\nasync function ensureDir(dirPath: string): Promise<void> {\n const { mkdir } = await import('node:fs/promises');\n await mkdir(dirPath, { recursive: true });\n}\n\nasync function readJSON<T>(filePath: string): Promise<T | null> {\n const { readFile } = await import('node:fs/promises');\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nasync function writeJSON(filePath: string, data: unknown): Promise<void> {\n const { writeFile } = await import('node:fs/promises');\n await ensureDir(join(filePath, '..'));\n await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// ===== PUBLIC API =====\n\nexport async function logAudit(\n action: AuditAction,\n projectPath: string,\n details: Record<string, unknown> = {},\n): Promise<AuditEntry> {\n const auditDir = getAuditDir();\n await ensureDir(auditDir);\n\n let currentUser: string;\n try {\n currentUser = userInfo().username;\n } catch {\n currentUser = process.env.USER ?? process.env.USERNAME ?? 'unknown';\n }\n\n const partialEntry = {\n id: randomUUID().substring(0, 12),\n timestamp: new Date(),\n action,\n user: currentUser,\n projectPath,\n details,\n };\n\n const entry: AuditEntry = {\n ...partialEntry,\n integrityHash: computeIntegrityHash(partialEntry),\n };\n\n const auditFile = getCurrentAuditFile();\n let entries = await readJSON<AuditEntry[]>(auditFile) ?? [];\n entries.push(entry);\n\n if (entries.length > MAX_ENTRIES_PER_FILE) {\n entries = entries.slice(-MAX_ENTRIES_PER_FILE);\n }\n\n await writeJSON(auditFile, entries);\n try { await chmod(auditFile, 0o600); } catch { /* ignore on Windows */ }\n\n return entry;\n}\n\nexport async function getAuditEntries(\n options: {\n projectPath?: string;\n action?: AuditAction;\n since?: Date;\n limit?: number;\n } = {},\n): Promise<AuditEntry[]> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return [];\n }\n\n const auditFiles = files\n .filter((f) => f.startsWith('audit_') && f.endsWith('.json'))\n .sort()\n .reverse();\n\n const allEntries: AuditEntry[] = [];\n const limit = options.limit ?? 100;\n\n for (const file of auditFiles) {\n if (allEntries.length >= limit) break;\n\n const entries = await readJSON<AuditEntry[]>(join(auditDir, file));\n if (!entries) continue;\n\n for (const entry of entries.reverse()) {\n if (allEntries.length >= limit) break;\n if (options.projectPath && entry.projectPath !== options.projectPath) continue;\n if (options.action && entry.action !== options.action) continue;\n if (options.since && new Date(entry.timestamp) < options.since) continue;\n allEntries.push(entry);\n }\n }\n\n return allEntries;\n}\n\nexport function verifyAuditEntry(entry: AuditEntry): boolean {\n const { integrityHash, ...rest } = entry;\n const expected = computeIntegrityHash(rest as Omit<AuditEntry, 'integrityHash'>);\n return expected === integrityHash;\n}\n\nexport async function verifyAuditIntegrity(): Promise<{\n totalEntries: number;\n validEntries: number;\n invalidEntries: AuditEntry[];\n}> {\n const entries = await getAuditEntries({ limit: 10000 });\n const invalidEntries: AuditEntry[] = [];\n\n for (const entry of entries) {\n if (!verifyAuditEntry(entry)) {\n invalidEntries.push(entry);\n }\n }\n\n return {\n totalEntries: entries.length,\n validEntries: entries.length - invalidEntries.length,\n invalidEntries,\n };\n}\n\nexport async function purgeOldAuditEntries(retentionDays: number): Promise<number> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return 0;\n }\n\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - retentionDays);\n const cutoffStr = cutoff.toISOString().split('T')[0].replace(/-/g, '');\n\n let purged = 0;\n const { unlink } = await import('node:fs/promises');\n\n for (const file of files) {\n if (!file.startsWith('audit_') || !file.endsWith('.json')) continue;\n const dateStr = file.replace('audit_', '').replace('.json', '');\n if (dateStr < cutoffStr) {\n try {\n await unlink(join(auditDir, file));\n purged++;\n } catch { /* ignore */ }\n }\n }\n\n return purged;\n}\n"],"mappings":"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","names":["randomUUID","readFile","readFile","readFile","createHash","join","readFile","randomUUID"]}
|
|
1
|
+
{"version":3,"sources":["../../src/interact/orchestrator.ts","../../src/engine/selector.ts","../../src/govern/secrets.ts","../../src/engine/pruner.ts","../../src/engine/tokenizer.ts","../../src/engine/graph-utils.ts","../../src/engine/coverage.ts","../../src/engine/budget.ts","../../src/interact/router.ts","../../src/interact/estimator.ts","../../src/interact/prompt.ts","../../src/govern/audit.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { InteractionPlan, PlanDecision, TaskType } from '../types/interact.js';\nimport type { ProjectAnalysis } from '../types/engine.js';\nimport type { PolicySet } from '../types/govern.js';\nimport { selectContext } from '../engine/selector.js';\nimport { classifyTask, routeModel } from './router.js';\nimport { estimateCost } from './estimator.js';\nimport { buildPrompt } from './prompt.js';\nimport { logAudit } from '../govern/audit.js';\n\n// ===== INTERACTION ORCHESTRATOR =====\n//\n// The brain of CTO v2. Given a task description:\n// 1. Classify task type\n// 2. Select optimal context (deterministic)\n// 3. Choose best model\n// 4. Build structured prompt\n// 5. Estimate cost\n// 6. Explain all decisions\n\nexport interface OrchestratorInput {\n task: string;\n analysis: ProjectAnalysis;\n budget?: number;\n model?: string;\n policies?: PolicySet;\n depth?: number;\n enableCoT?: boolean;\n enableConstraints?: boolean;\n enableAntiHallucination?: boolean;\n}\n\nexport async function planInteraction(input: OrchestratorInput): Promise<InteractionPlan> {\n const {\n task,\n analysis,\n budget = 50_000,\n model: preferredModel,\n policies,\n depth = 2,\n enableCoT = true,\n enableConstraints = true,\n enableAntiHallucination = true,\n } = input;\n\n const decisions: PlanDecision[] = [];\n\n // 1. CLASSIFY task\n const taskType: TaskType = classifyTask(task);\n decisions.push({\n step: 'classify',\n decision: taskType,\n reason: `Task classified as \"${taskType}\" based on keyword analysis`,\n data: { task, taskType },\n });\n\n // 2. SELECT context (deterministic)\n const context = await selectContext({\n task,\n analysis,\n budget,\n policies,\n depth,\n });\n\n decisions.push({\n step: 'select-context',\n decision: `${context.files.length} files selected (${context.totalTokens} tokens)`,\n reason: `Coverage: ${context.coverage.score}%, Risk: ${context.riskScore}/100`,\n data: {\n filesIncluded: context.files.length,\n tokensUsed: context.totalTokens,\n budget,\n coverage: context.coverage.score,\n risk: context.riskScore,\n },\n });\n\n // 3. CHOOSE model\n const modelChoice = routeModel(taskType, analysis, preferredModel);\n decisions.push({\n step: 'choose-model',\n decision: modelChoice.model,\n reason: modelChoice.reason,\n data: {\n confidence: modelChoice.confidence,\n alternatives: modelChoice.alternatives.length,\n },\n });\n\n // 4. BUILD prompt\n const prompt = buildPrompt({\n task,\n taskType,\n analysis,\n selection: context,\n enableCoT,\n enableConstraints,\n enableAntiHallucination,\n });\n\n decisions.push({\n step: 'build-prompt',\n decision: `${prompt.sections.length} sections, ${prompt.totalTokens} tokens`,\n reason: `Sections: ${prompt.sections.map((s) => s.id).join(', ')}`,\n });\n\n // 5. ESTIMATE cost\n const cost = estimateCost(\n modelChoice.model,\n context.totalTokens + prompt.totalTokens,\n analysis.totalTokens,\n );\n\n decisions.push({\n step: 'estimate-cost',\n decision: cost.formatted,\n reason: cost.savings.formatted,\n data: {\n inputTokens: cost.inputTokens,\n totalCost: cost.totalCost,\n savings: cost.savings.percent,\n },\n });\n\n const plan: InteractionPlan = {\n id: randomUUID().substring(0, 8),\n task,\n taskType,\n timestamp: new Date(),\n context,\n model: modelChoice,\n prompt,\n cost,\n decisions,\n };\n\n // 6. AUDIT LOG — record this interaction for governance\n try {\n await logAudit('interact', analysis.projectPath, {\n interactionId: plan.id,\n task,\n taskType,\n contextHash: context.hash,\n filesIncluded: context.files.length,\n filesExcluded: analysis.totalFiles - context.files.length,\n tokensUsed: context.totalTokens,\n coverageScore: context.coverage.score,\n riskScore: context.riskScore,\n model: modelChoice.model,\n estimatedCost: cost.totalCost,\n });\n } catch {\n // Audit failure should never block the interaction pipeline\n }\n\n return plan;\n}\n","import { createHash } from 'node:crypto';\nimport type {\n AnalyzedFile,\n ProjectAnalysis,\n ContextSelection,\n SelectedFile,\n SelectionDecision,\n PruneLevel,\n CoverageResult,\n} from '../types/engine.js';\nimport type { PolicySet, PolicyRule } from '../types/govern.js';\nimport { scanFileForSecrets } from '../govern/secrets.js';\nimport { pruneFile } from './pruner.js';\nimport { calculateCoverage } from './coverage.js';\nimport { getPruneLevelForRisk } from './budget.js';\nimport { buildAdjacencyList, bfsBidirectional, matchGlob } from './graph-utils.js';\n\n// ===== DETERMINISTIC CONTEXT SELECTION =====\n//\n// Given a task, analysis, budget, and policies → always returns the same file set.\n// No randomness, no heuristics that change over time.\n//\n// Algorithm:\n// 1. Identify target files from task description\n// 2. Expand via dependency graph (BFS, depth=2)\n// 3. Apply policies (include-always, exclude-always)\n// 4. Score & rank by riskScore\n// 5. Greedy allocation with cascading prune levels\n// 6. Validate policies\n// 7. Calculate coverage score\n// 8. Hash for reproducibility\n\nexport interface SelectionInput {\n task: string;\n analysis: ProjectAnalysis;\n budget: number;\n policies?: PolicySet;\n depth?: number;\n}\n\nexport async function selectContext(input: SelectionInput): Promise<ContextSelection> {\n const { task, analysis, budget, policies, depth = 2 } = input;\n const decisions: SelectionDecision[] = [];\n\n // 1. Identify target files from task description\n const targetPaths = identifyTargetFiles(task, analysis.files);\n if (targetPaths.length > 0) {\n decisions.push({\n file: targetPaths.join(', '),\n action: 'include-full',\n reason: `Target file(s) identified from task description`,\n });\n }\n\n // 2. Expand via dependency graph (O(V+E) using adjacency list)\n const adj = buildAdjacencyList(analysis.graph.edges);\n const expandedPaths = targetPaths.length > 0\n ? Array.from(bfsBidirectional(targetPaths, adj, depth))\n : [];\n const expansionCount = expandedPaths.length - targetPaths.length;\n if (expansionCount > 0) {\n decisions.push({\n file: `${expansionCount} dependencies`,\n action: 'include-full',\n reason: `Expanded ${targetPaths.length} target(s) to ${expandedPaths.length} files via dependency graph (depth ${depth})`,\n });\n }\n\n // 2b. Add type providers of expanded files as candidates.\n // This aligns with coverage.ts which counts type providers as \"relevant\".\n // Without this, coverage would flag missing critical type files that the\n // selector never considered as candidates — a consistency bug.\n const allFileMap = new Map(analysis.files.map((f) => [f.relativePath, f]));\n if (targetPaths.length > 0) {\n for (const path of expandedPaths) {\n const file = allFileMap.get(path);\n if (!file) continue;\n for (const imp of file.imports) {\n const impFile = allFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n expandedPaths.push(imp);\n }\n }\n }\n }\n\n // 3. Apply policies\n const { mustInclude, mustExclude } = applyPolicies(analysis.files, policies);\n\n // Merge: targets + expanded + must-include → candidate set\n const candidateSet = new Set([...expandedPaths, ...mustInclude]);\n\n // If no targets identified, use all files (filtered by exclude)\n if (targetPaths.length === 0) {\n for (const f of analysis.files) {\n candidateSet.add(f.relativePath);\n }\n }\n\n // Remove must-exclude\n for (const ex of mustExclude) {\n candidateSet.delete(ex);\n decisions.push({\n file: ex,\n action: 'exclude',\n reason: 'Excluded by policy',\n });\n }\n\n // 3b. secret-block: scan candidate files for secrets and exclude them\n const hasSecretBlock = policies?.rules.some(\n (r) => r.type === 'secret-block' && r.enabled,\n );\n if (hasSecretBlock) {\n for (const path of Array.from(candidateSet)) {\n const file = allFileMap.get(path);\n if (!file) continue;\n const findings = await scanFileForSecrets(\n file.path,\n analysis.projectPath,\n );\n if (findings.length > 0) {\n candidateSet.delete(path);\n decisions.push({\n file: path,\n action: 'exclude',\n reason: `Blocked: ${findings.length} secret(s) detected (${findings.map((f) => f.type).join(', ')})`,\n });\n }\n }\n }\n\n // 4. Sort candidates by riskScore descending\n const candidates = Array.from(candidateSet)\n .map((p) => allFileMap.get(p))\n .filter((f): f is AnalyzedFile => f !== undefined)\n .sort((a, b) => {\n // Targets always first\n const aIsTarget = targetPaths.includes(a.relativePath) ? 0 : 1;\n const bIsTarget = targetPaths.includes(b.relativePath) ? 0 : 1;\n if (aIsTarget !== bIsTarget) return aIsTarget - bIsTarget;\n\n // Then must-include\n const aIsMust = mustInclude.has(a.relativePath) ? 0 : 1;\n const bIsMust = mustInclude.has(b.relativePath) ? 0 : 1;\n if (aIsMust !== bIsMust) return aIsMust - bIsMust;\n\n // Then by riskScore\n return b.riskScore - a.riskScore;\n });\n\n // 5. Greedy allocation with cascading prune levels\n const selectedFiles: SelectedFile[] = [];\n let usedTokens = 0;\n\n for (const file of candidates) {\n const isTarget = targetPaths.includes(file.relativePath);\n const isMustInclude = mustInclude.has(file.relativePath);\n const defaultLevel = isTarget ? 'full' : getPruneLevelForRisk(file.riskScore);\n const levels = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levels) {\n if (level === 'excluded') break;\n\n let tokens: number;\n if (level === 'full') {\n tokens = file.tokens;\n } else {\n const pruned = await pruneFile(file, level);\n tokens = pruned.prunedTokens;\n }\n\n if (usedTokens + tokens <= budget) {\n usedTokens += tokens;\n selectedFiles.push({\n relativePath: file.relativePath,\n tokens,\n originalTokens: file.tokens,\n pruneLevel: level,\n riskScore: file.riskScore,\n reason: buildReason(file, level, isTarget, isMustInclude),\n });\n\n if (level !== defaultLevel) {\n decisions.push({\n file: file.relativePath,\n action: `include-${level}` as SelectionDecision['action'],\n reason: `Downgraded from ${defaultLevel} to ${level} due to budget constraint`,\n alternatives: `Would need ${file.tokens - tokens} more tokens for ${defaultLevel}`,\n });\n }\n\n included = true;\n break;\n }\n }\n\n if (!included) {\n decisions.push({\n file: file.relativePath,\n action: 'exclude',\n reason: `Budget exhausted (risk: ${file.riskScore}, needs ${file.tokens} tokens)`,\n });\n }\n }\n\n // 6. Calculate coverage\n const includedPaths = selectedFiles.map((f) => f.relativePath);\n const coverage = calculateCoverage(\n targetPaths,\n includedPaths,\n analysis.files,\n analysis.graph,\n depth,\n );\n\n // 7. Calculate overall risk of the selection (lower = better)\n const includedSet = new Set(includedPaths);\n const excludedFiles = analysis.files.filter(\n (f) => !includedSet.has(f.relativePath),\n );\n const excludedRisk = excludedFiles.length > 0\n ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length)\n : 0;\n\n // 8. Hash for determinism\n const hashInput = selectedFiles\n .map((f) => `${f.relativePath}:${f.pruneLevel}`)\n .sort()\n .join('|') + `|budget:${budget}`;\n const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 16);\n\n return {\n files: selectedFiles,\n totalTokens: usedTokens,\n budget,\n usedPercent: budget > 0 ? Math.round((usedTokens / budget) * 100 * 10) / 10 : 0,\n coverage,\n riskScore: excludedRisk,\n deterministic: true,\n hash,\n decisions,\n };\n}\n\n// ===== TARGET IDENTIFICATION =====\n\nfunction identifyTargetFiles(task: string, files: AnalyzedFile[]): string[] {\n const targets: string[] = [];\n\n // Extract file paths mentioned in the task\n // Matches patterns like: src/foo/bar.ts, ./utils.js, components/Header.tsx\n const pathPattern = /(?:^|\\s|[\"'`])([.\\w/-]+\\.[a-zA-Z]{1,4})(?:\\s|$|[\"'`]|,|:)/g;\n let match: RegExpExecArray | null;\n\n while ((match = pathPattern.exec(task)) !== null) {\n const candidate = match[1];\n // Find matching file\n const found = files.find(\n (f) => f.relativePath === candidate || f.relativePath.endsWith(candidate),\n );\n if (found && !targets.includes(found.relativePath)) {\n targets.push(found.relativePath);\n }\n }\n\n return targets;\n}\n\n// ===== POLICY APPLICATION =====\n\nfunction applyPolicies(\n files: AnalyzedFile[],\n policies?: PolicySet,\n): { mustInclude: Set<string>; mustExclude: Set<string> } {\n const mustInclude = new Set<string>();\n const mustExclude = new Set<string>();\n\n if (!policies) return { mustInclude, mustExclude };\n\n for (const rule of policies.rules) {\n if (!rule.enabled) continue;\n\n if (rule.type === 'include-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustInclude.add(file.relativePath);\n }\n }\n }\n\n if (rule.type === 'exclude-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustExclude.add(file.relativePath);\n }\n }\n }\n\n // secret-block is handled separately in applyPoliciesAsync\n // because it requires file I/O (scanning file contents)\n }\n\n return { mustInclude, mustExclude };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n\nfunction buildReason(\n file: AnalyzedFile,\n level: PruneLevel,\n isTarget: boolean,\n isMustInclude: boolean,\n): string {\n if (isTarget) return 'Target file';\n if (isMustInclude) return 'Required by policy';\n\n const impact = file.exclusionImpact;\n const levelStr = level === 'full' ? 'full content' : level;\n\n if (impact === 'critical') return `Critical dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'high') return `High-risk dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'medium') return `Medium relevance (risk ${file.riskScore}) — ${levelStr}`;\n return `Low relevance (risk ${file.riskScore}) — ${levelStr}`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, relative } from 'node:path';\nimport type { SecretFinding, SecretType } from '../types/govern.js';\n\n// ===== SECRET DETECTION ENGINE =====\n\ninterface SecretPattern {\n type: SecretType;\n pattern: RegExp;\n severity: SecretFinding['severity'];\n description: string;\n}\n\nconst BUILTIN_PATTERNS: { type: SecretType; source: string; flags: string; severity: SecretFinding['severity']; description: string }[] = [\n // API Keys\n { type: 'api-key', source: '(?:api[_-]?key|apikey)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{20,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'API Key' },\n { type: 'api-key', source: 'sk-[a-zA-Z0-9]{20,}', flags: 'g', severity: 'critical', description: 'OpenAI/Anthropic API Key' },\n { type: 'api-key', source: 'sk-ant-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'Anthropic API Key' },\n\n // AWS\n { type: 'aws-key', source: 'AKIA[0-9A-Z]{16}', flags: 'g', severity: 'critical', description: 'AWS Access Key ID' },\n { type: 'aws-key', source: '(?:aws_secret_access_key|aws_secret)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9/+=]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'AWS Secret Key' },\n\n // Private Keys\n { type: 'private-key', source: '-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'Private Key' },\n { type: 'private-key', source: '-----BEGIN OPENSSH PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'SSH Private Key' },\n\n // Passwords\n { type: 'password', source: '(?:password|passwd|pwd)\\\\s*[:=]\\\\s*[\\'\"]([^\\'\"]{8,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Hardcoded Password' },\n { type: 'password', source: '(?:DB_PASSWORD|DATABASE_PASSWORD|MYSQL_PASSWORD|POSTGRES_PASSWORD)\\\\s*[:=]\\\\s*[\\'\"]?([^\\'\"{}\\\\s]{4,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database Password' },\n\n // Tokens\n { type: 'token', source: '(?:bearer|token|auth_token|access_token|refresh_token)\\\\s*[:=]\\\\s*[\\'\"]([a-zA-Z0-9_\\\\-.]{20,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Auth Token' },\n { type: 'token', source: 'ghp_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub Personal Access Token' },\n { type: 'token', source: 'gho_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub OAuth Token' },\n { type: 'token', source: 'glpat-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'GitLab Personal Access Token' },\n { type: 'token', source: 'npm_[a-zA-Z0-9]{36}', flags: 'g', severity: 'high', description: 'npm Token' },\n\n // Connection strings\n { type: 'connection-string', source: '(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\\\/\\\\/[^\\\\s\\'\"]+:[^\\\\s\\'\"]+@[^\\\\s\\'\"]+', flags: 'gi', severity: 'critical', description: 'Database Connection String' },\n { type: 'connection-string', source: '(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{10,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database URL' },\n\n // Environment variables with secrets\n { type: 'env-variable', source: '(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{8,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Secret Environment Variable' },\n\n // Stripe\n { type: 'api-key', source: 'sk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Live Secret Key' },\n { type: 'api-key', source: 'pk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'high', description: 'Stripe Live Publishable Key' },\n { type: 'api-key', source: 'rk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Restricted Key' },\n\n // Slack\n { type: 'token', source: 'xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack Bot Token' },\n { type: 'token', source: 'xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack User Token' },\n { type: 'api-key', source: 'https://hooks\\\\.slack\\\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+', flags: 'g', severity: 'high', description: 'Slack Webhook URL' },\n\n // Google\n { type: 'api-key', source: 'AIza[0-9A-Za-z_-]{35}', flags: 'g', severity: 'high', description: 'Google API Key' },\n { type: 'token', source: 'ya29\\\\.[0-9A-Za-z_-]+', flags: 'g', severity: 'high', description: 'Google OAuth Token' },\n\n // Azure\n { type: 'api-key', source: '(?:AccountKey|SharedAccessKey)\\\\s*=\\\\s*[a-zA-Z0-9+/=]{40,}', flags: 'g', severity: 'critical', description: 'Azure Storage Key' },\n\n // Twilio\n { type: 'api-key', source: 'AC[a-f0-9]{32}', flags: 'g', severity: 'high', description: 'Twilio Account SID' },\n\n // SendGrid\n { type: 'api-key', source: 'SG\\\\.[a-zA-Z0-9_-]{22}\\\\.[a-zA-Z0-9_-]{43}', flags: 'g', severity: 'critical', description: 'SendGrid API Key' },\n\n // JWT\n { type: 'token', source: 'eyJ[a-zA-Z0-9_-]{10,}\\\\.eyJ[a-zA-Z0-9_-]{10,}\\\\.[a-zA-Z0-9_-]{10,}', flags: 'g', severity: 'high', description: 'JSON Web Token' },\n\n // PII\n { type: 'pii', source: '\\\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Z|a-z]{2,}\\\\b', flags: 'g', severity: 'medium', description: 'Email Address (PII)' },\n { type: 'pii', source: '\\\\b\\\\d{3}[-.]?\\\\d{2}[-.]?\\\\d{4}\\\\b', flags: 'g', severity: 'high', description: 'Possible SSN (PII)' },\n];\n\nfunction buildPatterns(customPatterns: string[] = []): SecretPattern[] {\n const patterns: SecretPattern[] = BUILTIN_PATTERNS.map((def) => ({\n type: def.type,\n pattern: new RegExp(def.source, def.flags),\n severity: def.severity,\n description: def.description,\n }));\n\n for (const custom of customPatterns) {\n try {\n patterns.push({\n type: 'custom',\n pattern: new RegExp(custom, 'gi'),\n severity: 'medium',\n description: `Custom pattern: ${custom}`,\n });\n } catch { /* skip invalid regex */ }\n }\n\n return patterns;\n}\n\nexport function scanContentForSecrets(\n content: string,\n filePath: string,\n customPatterns: string[] = [],\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n secretPattern.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = secretPattern.pattern.exec(line)) !== null) {\n const matchText = match[0];\n if (isTemplateOrPlaceholder(matchText)) continue;\n\n findings.push({\n type: secretPattern.type,\n file: filePath,\n line: i + 1,\n match: matchText,\n redacted: redactSecret(matchText),\n severity: secretPattern.severity,\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\nexport async function scanFileForSecrets(\n filePath: string,\n projectPath: string,\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(filePath));\n return scanContentForSecrets(content, relPath, customPatterns);\n } catch {\n return [];\n }\n}\n\nexport async function scanProjectForSecrets(\n projectPath: string,\n filePaths: string[],\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n const allFindings: SecretFinding[] = [];\n\n for (const fp of filePaths) {\n const findings = await scanFileForSecrets(fp, projectPath, customPatterns);\n allFindings.push(...findings);\n }\n\n return allFindings.sort((a, b) => {\n const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n}\n\nexport function sanitizeContent(content: string, customPatterns: string[] = []): string {\n let sanitized = content;\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n sanitized = sanitized.replace(secretPattern.pattern, (match) => {\n if (isTemplateOrPlaceholder(match)) return match;\n return redactSecret(match);\n });\n }\n\n return sanitized;\n}\n\nfunction redactSecret(value: string): string {\n if (value.length <= 8) return '***REDACTED***';\n const prefix = value.substring(0, 4);\n const suffix = value.substring(value.length - 2);\n return `${prefix}${'*'.repeat(Math.min(value.length - 6, 20))}${suffix}`;\n}\n\nfunction isTemplateOrPlaceholder(value: string): boolean {\n const placeholders = [\n /\\$\\{.*\\}/, /\\{\\{.*\\}\\}/, /%[sd]/, /<[A-Z_]+>/, /YOUR_.*_HERE/i,\n /\\bCHANGE_ME\\b/i, /\\bPLACEHOLDER\\b/i, /\\bexample\\b/i, /\\bTODO\\b/i, /xxx+/i,\n /\\breplace.?me\\b/i, /\\bdummy\\b/i, /\\btest_?key\\b/i, /\\bsample\\b/i,\n ];\n return placeholders.some((p) => p.test(value));\n}\n\nfunction deduplicateFindings(findings: SecretFinding[]): SecretFinding[] {\n const seen = new Set<string>();\n return findings.filter((f) => {\n const key = `${f.file}:${f.line}:${f.type}:${f.match}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n// ===== ENTROPY ANALYSIS =====\n\nfunction shannonEntropy(str: string): number {\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) || 0) + 1);\n }\n let entropy = 0;\n for (const count of freq.values()) {\n const p = count / str.length;\n if (p > 0) entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\nconst HIGH_ENTROPY_RE = /['\"]([a-zA-Z0-9+/=_\\-]{30,})['\"]|=\\s*['\"]?([a-zA-Z0-9+/=_\\-]{30,})['\"]?/g;\n\nconst ENTROPY_SKIP = [\n /^[a-f0-9]{32,}$/i, // hex hashes\n /^[A-Z_]{30,}$/, // all-caps constants\n /^[a-z_]{30,}$/, // all-lowercase identifiers\n /^[a-zA-Z0-9+/]+=+$/, // base64 padding\n /^[a-z]+[A-Z][a-zA-Z]+$/, // camelCase identifiers\n /sha\\d+-/i, // integrity hashes (sha256-, sha512-)\n];\n\nexport function scanContentForHighEntropy(\n content: string,\n filePath: string,\n threshold: number = 5.0,\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.trim().startsWith('//') || line.trim().startsWith('#') || line.trim().startsWith('*')) continue;\n\n HIGH_ENTROPY_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = HIGH_ENTROPY_RE.exec(line)) !== null) {\n const value = match[1] || match[2];\n if (!value || value.length < 40) continue;\n if (isTemplateOrPlaceholder(value)) continue;\n if (ENTROPY_SKIP.some((p) => p.test(value))) continue;\n\n const entropy = shannonEntropy(value);\n if (entropy >= threshold) {\n findings.push({\n type: 'high-entropy',\n file: filePath,\n line: i + 1,\n match: value,\n redacted: redactSecret(value),\n severity: entropy >= 5.0 ? 'high' : 'medium',\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\n// ===== FULL PROJECT AUDIT =====\n\nexport interface AuditResult {\n findings: SecretFinding[];\n summary: {\n totalFiles: number;\n filesScanned: number;\n filesWithSecrets: number;\n totalFindings: number;\n bySeverity: { critical: number; high: number; medium: number; low: number };\n byType: Record<string, number>;\n };\n recommendations: string[];\n}\n\nexport async function auditProject(\n projectPath: string,\n filePaths: string[],\n options: { customPatterns?: string[]; entropyThreshold?: number; includePII?: boolean } = {},\n): Promise<AuditResult> {\n const { customPatterns = [], entropyThreshold = 4.5, includePII = true } = options;\n const allFindings: SecretFinding[] = [];\n const filesWithSecrets = new Set<string>();\n\n for (const fp of filePaths) {\n try {\n const content = await readFile(fp, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(fp));\n\n // Skip test files and declaration files for entropy (too many false positives)\n const isTestFile = /\\.(test|spec|mock)\\.[jt]sx?$/.test(relPath) || relPath.includes('__tests__');\n const isDtsFile = relPath.endsWith('.d.ts');\n\n // Pattern-based detection\n let findings = scanContentForSecrets(content, relPath, customPatterns);\n\n // Filter PII if not wanted\n if (!includePII) {\n findings = findings.filter((f) => f.type !== 'pii');\n }\n\n // Entropy-based detection (skip test/declaration files — too noisy)\n const entropyFindings = (isTestFile || isDtsFile)\n ? []\n : scanContentForHighEntropy(content, relPath, entropyThreshold);\n\n const combined = [...findings, ...entropyFindings];\n if (combined.length > 0) {\n filesWithSecrets.add(relPath);\n allFindings.push(...combined);\n }\n } catch {\n // skip unreadable files\n }\n }\n\n // Sort by severity\n allFindings.sort((a, b) => {\n const order = { critical: 0, high: 1, medium: 2, low: 3 };\n return order[a.severity] - order[b.severity];\n });\n\n // Build summary\n const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };\n const byType: Record<string, number> = {};\n for (const f of allFindings) {\n bySeverity[f.severity]++;\n byType[f.type] = (byType[f.type] || 0) + 1;\n }\n\n // Generate recommendations\n const recommendations: string[] = [];\n if (bySeverity.critical > 0) {\n recommendations.push('CRITICAL: Rotate all detected credentials immediately. They may already be compromised.');\n }\n if (byType['password'] > 0) {\n recommendations.push('Move passwords to environment variables or a secrets manager (AWS Secrets Manager, Vault, etc.).');\n }\n if (byType['api-key'] > 0 || byType['aws-key'] > 0) {\n recommendations.push('Use environment variables for API keys. Never commit them to source control.');\n }\n if (byType['connection-string'] > 0) {\n recommendations.push('Database connection strings should use environment variables, not hardcoded values.');\n }\n if (byType['private-key'] > 0) {\n recommendations.push('Private keys should NEVER be in source code. Use a key management service.');\n }\n if (byType['pii'] > 0) {\n recommendations.push('PII detected. Review for GDPR/CCPA compliance. Consider data anonymization.');\n }\n if (byType['high-entropy'] > 0) {\n recommendations.push('High-entropy strings detected that may be secrets. Review manually.');\n }\n if (allFindings.length > 0) {\n recommendations.push('Add a .gitignore entry for .env files if not already present.');\n recommendations.push('Run `npx cto-ai-cli --audit` regularly or add to CI pipeline.');\n }\n if (allFindings.length === 0) {\n recommendations.push('No secrets detected. Great job keeping your codebase clean!');\n }\n\n return {\n findings: allFindings,\n summary: {\n totalFiles: filePaths.length,\n filesScanned: filePaths.length,\n filesWithSecrets: filesWithSecrets.size,\n totalFindings: allFindings.length,\n bySeverity,\n byType,\n },\n recommendations,\n };\n}\n","import { Project, SyntaxKind, type SourceFile } from 'ts-morph';\nimport { readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { AnalyzedFile, PruneLevel, PrunedContent } from '../types/engine.js';\nimport { countTokensChars4 } from './tokenizer.js';\n\nconst TS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mts', 'mjs']);\n\n// ===== PUBLIC API =====\n\nexport async function pruneFile(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n if (level === 'excluded') {\n return emptyResult(file, 'excluded');\n }\n\n if (level === 'full') {\n return fullContent(file);\n }\n\n const ext = file.extension.toLowerCase();\n const isTS = TS_EXTENSIONS.has(ext);\n\n if (isTS) {\n return pruneTypeScript(file, level);\n }\n\n return pruneGeneric(file, level);\n}\n\nexport async function pruneFiles(\n files: AnalyzedFile[],\n levelFn: (file: AnalyzedFile) => PruneLevel,\n): Promise<PrunedContent[]> {\n const results: PrunedContent[] = [];\n\n for (const file of files) {\n const level = levelFn(file);\n const pruned = await pruneFile(file, level);\n results.push(pruned);\n }\n\n return results;\n}\n\n// ===== TYPESCRIPT AST-BASED PRUNING =====\n\nasync function pruneTypeScript(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n let project: Project;\n try {\n const tsConfigPath = findTsConfig(file.path);\n project = new Project({\n tsConfigFilePath: tsConfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: tsConfigPath\n ? undefined\n : { allowJs: true, esModuleInterop: true },\n });\n project.createSourceFile(file.path, content, { overwrite: true });\n } catch {\n // Fallback to generic pruning if AST fails\n return pruneGenericFromContent(file, content, level);\n }\n\n const sourceFile = project.getSourceFiles()[0];\n if (!sourceFile) {\n return pruneGenericFromContent(file, content, level);\n }\n\n const prunedContent = level === 'signatures'\n ? extractSignaturesAST(sourceFile)\n : extractSkeletonAST(sourceFile);\n\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== SIGNATURES: Keep imports, type defs, function signatures, class outlines =====\n\nfunction extractSignaturesAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases\n for (const ta of sf.getTypeAliases()) {\n addJSDoc(ta, parts);\n parts.push(ta.getText());\n }\n\n // Interfaces\n for (const iface of sf.getInterfaces()) {\n addJSDoc(iface, parts);\n parts.push(iface.getText());\n }\n\n // Enums\n for (const en of sf.getEnums()) {\n addJSDoc(en, parts);\n parts.push(en.getText());\n }\n\n // Function declarations — signature only\n for (const fn of sf.getFunctions()) {\n addJSDoc(fn, parts);\n const isExported = fn.isExported();\n const isAsync = fn.isAsync();\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n const returnType = fn.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n\n const prefix = isExported ? 'export ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(`${prefix}${asyncStr}function ${name}(${params})${returnStr} { /* ... */ }`);\n }\n\n // Variable declarations (const/let with arrow functions)\n for (const stmt of sf.getVariableStatements()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init && (init.getKind() === SyntaxKind.ArrowFunction || init.getKind() === SyntaxKind.FunctionExpression)) {\n addJSDoc(stmt, parts);\n const isExported = stmt.isExported();\n const prefix = isExported ? 'export ' : '';\n const kind = stmt.getDeclarationKind();\n const name = decl.getName();\n const typeNode = decl.getTypeNode()?.getText();\n const typeStr = typeNode ? `: ${typeNode}` : '';\n parts.push(`${prefix}${kind} ${name}${typeStr} = /* ... */;`);\n } else {\n // Non-function variables: keep full declaration\n addJSDoc(stmt, parts);\n parts.push(stmt.getText());\n }\n }\n }\n\n // Classes — outline with method signatures\n for (const cls of sf.getClasses()) {\n addJSDoc(cls, parts);\n const isExported = cls.isExported();\n const prefix = isExported ? 'export ' : '';\n const name = cls.getName() ?? '<anonymous>';\n const ext = cls.getExtends()?.getText();\n const impl = cls.getImplements().map((i) => i.getText()).join(', ');\n let header = `${prefix}class ${name}`;\n if (ext) header += ` extends ${ext}`;\n if (impl) header += ` implements ${impl}`;\n header += ' {';\n parts.push(header);\n\n // Properties\n for (const prop of cls.getProperties()) {\n parts.push(` ${prop.getText()}`);\n }\n\n // Constructor\n const ctor = cls.getConstructors()[0];\n if (ctor) {\n const ctorParams = ctor.getParameters().map((p) => p.getText()).join(', ');\n parts.push(` constructor(${ctorParams}) { /* ... */ }`);\n }\n\n // Methods — signature only\n for (const method of cls.getMethods()) {\n const isStatic = method.isStatic();\n const isAsync = method.isAsync();\n const methodName = method.getName();\n const methodParams = method.getParameters().map((p) => p.getText()).join(', ');\n const returnType = method.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n const staticStr = isStatic ? 'static ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(` ${staticStr}${asyncStr}${methodName}(${methodParams})${returnStr} { /* ... */ }`);\n }\n\n parts.push('}');\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n // Export assignments\n for (const exp of sf.getExportAssignments()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== SKELETON: Keep only declarations, no bodies at all =====\n\nfunction extractSkeletonAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases — full\n for (const ta of sf.getTypeAliases()) {\n if (ta.isExported()) parts.push(ta.getText());\n }\n\n // Interfaces — name + extends only\n for (const iface of sf.getInterfaces()) {\n if (!iface.isExported()) continue;\n const ext = iface.getExtends().map((e) => e.getText());\n const extStr = ext.length > 0 ? ` extends ${ext.join(', ')}` : '';\n parts.push(`export interface ${iface.getName()}${extStr} { /* ${iface.getProperties().length} props */ }`);\n }\n\n // Enums — name only\n for (const en of sf.getEnums()) {\n if (!en.isExported()) continue;\n const members = en.getMembers().map((m) => m.getName());\n parts.push(`export enum ${en.getName()} { ${members.join(', ')} }`);\n }\n\n // Functions — name + params only\n for (const fn of sf.getFunctions()) {\n if (!fn.isExported()) continue;\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n parts.push(`export function ${name}(${params});`);\n }\n\n // Classes — name + method names only\n for (const cls of sf.getClasses()) {\n if (!cls.isExported()) continue;\n const methods = cls.getMethods().map((m) => m.getName());\n parts.push(`export class ${cls.getName()} { /* methods: ${methods.join(', ')} */ }`);\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== GENERIC PRUNING (non-TS files) =====\n\nasync function pruneGeneric(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n return pruneGenericFromContent(file, content, level);\n}\n\nfunction pruneGenericFromContent(\n file: AnalyzedFile,\n content: string,\n level: PruneLevel,\n): PrunedContent {\n const lines = content.split('\\n');\n let result: string[];\n\n if (level === 'signatures') {\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t === '' ||\n t.startsWith('#') ||\n t.startsWith('//') ||\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('async def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n t.startsWith('const ') ||\n t.startsWith('let ') ||\n t.startsWith('var ') ||\n /^(pub |fn |struct |enum |impl |mod |use )/.test(t)\n );\n });\n } else {\n // skeleton: even more aggressive — only declarations\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n /^(pub |fn |struct |enum |mod |use )/.test(t)\n );\n });\n }\n\n const prunedContent = result.join('\\n');\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== HELPERS =====\n\nasync function fullContent(file: AnalyzedFile): Promise<PrunedContent> {\n let content = '';\n try {\n content = await readFile(file.path, 'utf-8');\n } catch { /* empty */ }\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: file.tokens,\n pruneLevel: 'full',\n content,\n savingsPercent: 0,\n };\n}\n\nfunction emptyResult(file: AnalyzedFile, level: PruneLevel): PrunedContent {\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: 0,\n pruneLevel: level,\n content: '',\n savingsPercent: 100,\n };\n}\n\nfunction addJSDoc(node: { getJsDocs?: () => { getText(): string }[] }, parts: string[]): void {\n if (!node.getJsDocs) return;\n const docs = node.getJsDocs();\n if (docs.length > 0) {\n parts.push(docs[0].getText());\n }\n}\n\nfunction findTsConfig(filePath: string): string | undefined {\n let dir = filePath;\n for (let i = 0; i < 10; i++) {\n dir = join(dir, '..');\n const candidate = join(dir, 'tsconfig.json');\n if (existsSync(candidate)) return candidate;\n }\n return undefined;\n}\n","import { encodingForModel } from 'js-tiktoken';\nimport { readFile, stat } from 'node:fs/promises';\n\nconst CHARS_PER_TOKEN = 4;\n\nlet encoder: ReturnType<typeof encodingForModel> | null = null;\n\nfunction getEncoder() {\n if (!encoder) {\n encoder = encodingForModel('claude-3-5-sonnet-20241022' as Parameters<typeof encodingForModel>[0]);\n }\n return encoder;\n}\n\nexport function countTokensTiktoken(text: string): number {\n try {\n const enc = getEncoder();\n const tokens = enc.encode(text);\n return tokens.length;\n } catch {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n }\n}\n\nexport function countTokensChars4(sizeInBytes: number): number {\n return Math.ceil(sizeInBytes / CHARS_PER_TOKEN);\n}\n\nexport function estimateTokens(\n content: string,\n sizeInBytes: number,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): number {\n if (method === 'tiktoken') {\n return countTokensTiktoken(content);\n }\n return countTokensChars4(sizeInBytes);\n}\n\nexport async function estimateFileTokens(\n filePath: string,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): Promise<number> {\n if (method === 'chars4') {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n\n try {\n const content = await readFile(filePath, 'utf-8');\n return countTokensTiktoken(content);\n } catch {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n}\n\nexport function freeEncoder(): void {\n encoder = null;\n}\n","import type { GraphEdge } from '../types/engine.js';\n\n// ===== SHARED GRAPH UTILITIES =====\n\nexport interface AdjacencyList {\n forward: Map<string, string[]>; // file → files it imports\n reverse: Map<string, string[]>; // file → files that import it\n}\n\nexport function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList {\n const forward = new Map<string, string[]>();\n const reverse = new Map<string, string[]>();\n\n for (const edge of edges) {\n if (!forward.has(edge.from)) forward.set(edge.from, []);\n forward.get(edge.from)!.push(edge.to);\n\n if (!reverse.has(edge.to)) reverse.set(edge.to, []);\n reverse.get(edge.to)!.push(edge.from);\n }\n\n return { forward, reverse };\n}\n\nexport function bfsBidirectional(\n seeds: string[],\n adj: AdjacencyList,\n depth: number,\n): Set<string> {\n const result = new Set(seeds);\n let frontier = [...seeds];\n const visited = new Set<string>();\n\n for (let d = 0; d < depth; d++) {\n const nextFrontier: string[] = [];\n\n for (const node of frontier) {\n if (visited.has(node)) continue;\n visited.add(node);\n\n // Forward neighbors (imports)\n const fwd = adj.forward.get(node);\n if (fwd) {\n for (const neighbor of fwd) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n\n // Reverse neighbors (imported by)\n const rev = adj.reverse.get(node);\n if (rev) {\n for (const neighbor of rev) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n }\n\n frontier = nextFrontier;\n }\n\n return result;\n}\n\nexport function matchGlob(path: string, pattern: string): boolean {\n const regexStr = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '§§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§§/g, '.*')\n .replace(/\\?/g, '.');\n\n try {\n return new RegExp(`^${regexStr}$`).test(path);\n } catch {\n return false;\n }\n}\n","import type {\n AnalyzedFile,\n ProjectGraph,\n CoverageResult,\n} from '../types/engine.js';\nimport { buildAdjacencyList, bfsBidirectional } from './graph-utils.js';\n\n// ===== CONTEXT COVERAGE SCORING =====\n//\n// Given a set of included files and a task's target files, calculate\n// how much of the \"relevant universe\" is covered.\n//\n// coverageScore = |includedRelevant| / |relevantFiles| × 100\n//\n// \"Relevant\" = target files + their dependencies (up to `depth` levels)\n// + type providers used by included files.\n\nexport function calculateCoverage(\n targetPaths: string[],\n includedPaths: string[],\n allFiles: AnalyzedFile[],\n graph: ProjectGraph,\n depth: number = 2,\n): CoverageResult {\n // 1. Find all relevant files: targets + their dependency cone\n // Uses adjacency list for O(V+E) BFS instead of scanning all edges per node.\n const adj = buildAdjacencyList(graph.edges);\n const relevantSet = targetPaths.length > 0\n ? bfsBidirectional(targetPaths, adj, depth)\n : new Set<string>();\n const includedSet = new Set(includedPaths);\n\n // Also add type providers used by included files\n const tempFileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n for (const path of includedPaths) {\n const file = tempFileMap.get(path);\n if (!file) continue;\n\n for (const imp of file.imports) {\n const impFile = tempFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n relevantSet.add(imp);\n }\n }\n }\n\n // 2. Calculate coverage\n const relevantFiles = Array.from(relevantSet);\n const includedRelevant = relevantFiles.filter((f) => includedSet.has(f));\n const missingRelevant = relevantFiles.filter((f) => !includedSet.has(f));\n\n // Missing critical = missing files with critical/high exclusion impact\n const missingCritical = missingRelevant.filter((f) => {\n const file = tempFileMap.get(f);\n return file && (file.exclusionImpact === 'critical' || file.exclusionImpact === 'high');\n });\n\n // Risk-weighted coverage: missing a high-risk file hurts more than missing a low-risk one.\n // score = Σ(riskScore of included relevant) / Σ(riskScore of all relevant) × 100\n // Falls back to count-based if no risk data is available.\n const fileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n let totalRelevantRisk = 0;\n let includedRelevantRisk = 0;\n\n for (const f of relevantFiles) {\n const risk = fileMap.get(f)?.riskScore ?? 1;\n totalRelevantRisk += risk;\n if (includedSet.has(f)) {\n includedRelevantRisk += risk;\n }\n }\n\n const score = totalRelevantRisk > 0\n ? Math.round((includedRelevantRisk / totalRelevantRisk) * 100)\n : relevantFiles.length > 0\n ? Math.round((includedRelevant.length / relevantFiles.length) * 100)\n : 100;\n\n // Build explanation\n let explanation: string;\n if (score >= 90) {\n explanation = `Excellent coverage (${score}%): AI has nearly all relevant context.`;\n } else if (score >= 70) {\n explanation = `Good coverage (${score}%): Most relevant files included.`;\n if (missingCritical.length > 0) {\n explanation += ` Warning: ${missingCritical.length} critical file(s) missing.`;\n }\n } else if (score >= 50) {\n explanation = `Partial coverage (${score}%): Significant context is missing.`;\n if (missingCritical.length > 0) {\n explanation += ` ${missingCritical.length} critical file(s) not included — AI quality will degrade.`;\n }\n } else {\n explanation = `Low coverage (${score}%): Most relevant files are excluded. AI response quality will be poor.`;\n }\n\n return {\n score,\n relevantFiles,\n includedRelevant,\n missingRelevant,\n missingCritical,\n explanation,\n };\n}\n","import type { AnalyzedFile, BudgetPlan, BudgetEntry, PruneLevel } from '../types/engine.js';\nimport { pruneFile } from './pruner.js';\n\n// ===== BUDGET OPTIMIZER =====\n//\n// Greedy knapsack with cascading prune levels.\n// Files are sorted by riskScore (descending) — highest risk first.\n// Each file is tried at progressively smaller prune levels until it fits.\n\nexport function getPruneLevelForRisk(riskScore: number): PruneLevel {\n if (riskScore >= 80) return 'full'; // critical — include everything, no cascading\n if (riskScore >= 60) return 'full'; // high — try full first, can cascade\n if (riskScore >= 30) return 'signatures'; // medium — signatures by default\n return 'skeleton'; // low — skeleton\n}\n\nexport function isCriticalRisk(riskScore: number): boolean {\n return riskScore >= 80;\n}\n\nexport async function optimizeBudget(\n files: AnalyzedFile[],\n budget: number,\n): Promise<BudgetPlan> {\n const entries: BudgetEntry[] = [];\n let used = 0;\n\n // Sort by riskScore descending — most important files first\n const sorted = [...files].sort((a, b) => b.riskScore - a.riskScore);\n\n for (const file of sorted) {\n const defaultLevel = getPruneLevelForRisk(file.riskScore);\n\n // Try cascading prune levels: full → signatures → skeleton → excluded\n const levelsToTry = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levelsToTry) {\n if (level === 'excluded') break;\n\n if (level === 'full') {\n if (used + file.tokens <= budget) {\n used += file.tokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: file.tokens,\n pruneLevel: 'full',\n included: true,\n reason: `Risk ${file.riskScore} — included in full`,\n });\n included = true;\n break;\n }\n } else {\n const pruned = await pruneFile(file, level);\n if (pruned.prunedTokens > 0 && used + pruned.prunedTokens <= budget) {\n used += pruned.prunedTokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: pruned.prunedTokens,\n pruneLevel: level,\n included: true,\n reason: `Risk ${file.riskScore} — pruned to ${level} (budget constraint)`,\n });\n included = true;\n break;\n }\n }\n }\n\n if (!included) {\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: 0,\n pruneLevel: 'excluded',\n included: false,\n reason: `Risk ${file.riskScore} — excluded (budget exhausted, ${file.tokens} tokens)`,\n });\n }\n }\n\n return {\n budget,\n used,\n remaining: budget - used,\n fillPercent: budget > 0 ? Math.round((used / budget) * 100 * 10) / 10 : 0,\n files: entries,\n };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n","import type { TaskType, ModelChoice, ModelSpec, ModelId } from '../types/interact.js';\nimport type { ProjectAnalysis } from '../types/engine.js';\n\n// ===== MODEL REGISTRY =====\n\nexport const MODEL_REGISTRY: ModelSpec[] = [\n {\n id: 'claude-haiku-3.5',\n name: 'Claude 3.5 Haiku',\n tier: 'fast',\n pricing: { inputPerMillion: 0.80, outputPerMillion: 4.00, cacheReadPerMillion: 0.08 },\n contextWindow: 200_000,\n strengths: ['speed', 'simple-tasks', 'low-cost'],\n },\n {\n id: 'claude-sonnet-4',\n name: 'Claude Sonnet 4',\n tier: 'balanced',\n pricing: { inputPerMillion: 3.00, outputPerMillion: 15.00, cacheReadPerMillion: 0.30 },\n contextWindow: 200_000,\n strengths: ['code-generation', 'refactoring', 'balanced-reasoning'],\n },\n {\n id: 'claude-opus-4',\n name: 'Claude Opus 4',\n tier: 'reasoning',\n pricing: { inputPerMillion: 15.00, outputPerMillion: 75.00, cacheReadPerMillion: 1.50 },\n contextWindow: 200_000,\n strengths: ['deep-reasoning', 'architecture', 'complex-debugging'],\n },\n];\n\n// ===== ROUTING RULES =====\n\ninterface RoutingRule {\n task: TaskType;\n defaultModel: ModelId;\n upgradeIf: (analysis: ProjectAnalysis) => boolean;\n upgradeTo: ModelId;\n reason: string;\n upgradeReason: string;\n}\n\nconst ROUTING_RULES: RoutingRule[] = [\n {\n task: 'simple-edit',\n defaultModel: 'claude-haiku-3.5',\n upgradeIf: () => false,\n upgradeTo: 'claude-haiku-3.5',\n reason: 'Simple edits are best handled by fast models',\n upgradeReason: '',\n },\n {\n task: 'docs',\n defaultModel: 'claude-haiku-3.5',\n upgradeIf: (a) => a.totalTokens > 100_000,\n upgradeTo: 'claude-sonnet-4',\n reason: 'Documentation tasks are straightforward',\n upgradeReason: 'Large codebase — Sonnet provides better understanding',\n },\n {\n task: 'test',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.riskProfile.overallComplexity > 15,\n upgradeTo: 'claude-opus-4',\n reason: 'Test generation requires good code understanding',\n upgradeReason: 'High complexity codebase — Opus for better test coverage',\n },\n {\n task: 'debug',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.riskProfile.distribution.critical > 5,\n upgradeTo: 'claude-opus-4',\n reason: 'Debugging requires solid reasoning about code flow',\n upgradeReason: 'Many critical files involved — Opus for deeper analysis',\n },\n {\n task: 'refactor',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.totalFiles > 50 && a.riskProfile.overallComplexity > 10,\n upgradeTo: 'claude-opus-4',\n reason: 'Refactoring needs good structural understanding',\n upgradeReason: 'Large + complex project — Opus for safer refactoring',\n },\n {\n task: 'review',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.riskProfile.distribution.critical > 3,\n upgradeTo: 'claude-opus-4',\n reason: 'Code review benefits from balanced reasoning',\n upgradeReason: 'Critical code under review — Opus for thorough analysis',\n },\n {\n task: 'feature',\n defaultModel: 'claude-sonnet-4',\n upgradeIf: (a) => a.totalFiles > 100,\n upgradeTo: 'claude-opus-4',\n reason: 'Feature development needs code generation + understanding',\n upgradeReason: 'Large codebase — Opus for better integration',\n },\n {\n task: 'architecture',\n defaultModel: 'claude-opus-4',\n upgradeIf: () => false,\n upgradeTo: 'claude-opus-4',\n reason: 'Architecture decisions require deep reasoning',\n upgradeReason: '',\n },\n];\n\n// ===== TASK CLASSIFICATION =====\n\nconst TASK_KEYWORDS: Record<TaskType, string[]> = {\n debug: ['debug', 'fix', 'bug', 'error', 'issue', 'broken', 'crash', 'failing', 'wrong'],\n review: ['review', 'check', 'assess', 'evaluate', 'audit', 'inspect', 'critique'],\n refactor: ['refactor', 'restructure', 'reorganize', 'clean up', 'simplify', 'extract', 'move'],\n test: ['test', 'spec', 'coverage', 'unit test', 'integration test', 'e2e'],\n docs: ['document', 'docs', 'readme', 'jsdoc', 'comment', 'explain'],\n feature: ['add', 'implement', 'create', 'build', 'new', 'feature', 'endpoint'],\n architecture: ['architecture', 'design', 'system', 'structure', 'migrate', 'pattern'],\n 'simple-edit': ['rename', 'typo', 'update', 'change', 'modify', 'tweak', 'adjust'],\n};\n\nexport function classifyTask(taskDescription: string): TaskType {\n const lower = taskDescription.toLowerCase();\n\n // Score each task type by keyword matches\n let bestType: TaskType = 'simple-edit';\n let bestScore = 0;\n\n for (const [type, keywords] of Object.entries(TASK_KEYWORDS) as [TaskType, string[]][]) {\n let score = 0;\n for (const kw of keywords) {\n if (lower.includes(kw)) score++;\n }\n if (score > bestScore) {\n bestScore = score;\n bestType = type;\n }\n }\n\n return bestType;\n}\n\n// ===== MODEL ROUTING =====\n\nexport function routeModel(\n taskType: TaskType,\n analysis: ProjectAnalysis,\n preferredModel?: ModelId,\n): ModelChoice {\n // If user explicitly chose a model, respect it\n if (preferredModel) {\n const spec = MODEL_REGISTRY.find((m) => m.id === preferredModel);\n if (spec) {\n return {\n model: preferredModel,\n reason: 'User-specified model',\n confidence: 1.0,\n alternatives: buildAlternatives(preferredModel, taskType),\n };\n }\n }\n\n // Find routing rule for this task\n const rule = ROUTING_RULES.find((r) => r.task === taskType);\n if (!rule) {\n return {\n model: 'claude-sonnet-4',\n reason: 'Default model for unrecognized task type',\n confidence: 0.5,\n alternatives: buildAlternatives('claude-sonnet-4', taskType),\n };\n }\n\n // Check if upgrade is warranted\n const shouldUpgrade = rule.upgradeIf(analysis);\n const model = shouldUpgrade ? rule.upgradeTo : rule.defaultModel;\n const reason = shouldUpgrade ? rule.upgradeReason : rule.reason;\n\n return {\n model,\n reason,\n confidence: shouldUpgrade ? 0.8 : 0.9,\n alternatives: buildAlternatives(model, taskType),\n };\n}\n\n// ===== HELPERS =====\n\nfunction buildAlternatives(chosenModel: ModelId, taskType: TaskType) {\n return MODEL_REGISTRY\n .filter((m) => m.id !== chosenModel)\n .map((m) => {\n const chosen = MODEL_REGISTRY.find((r) => r.id === chosenModel)!;\n const costDelta = m.pricing.inputPerMillion - chosen.pricing.inputPerMillion;\n const tradeoff = costDelta > 0\n ? `More capable but ${(costDelta / chosen.pricing.inputPerMillion * 100).toFixed(0)}% more expensive`\n : `${Math.abs(costDelta / chosen.pricing.inputPerMillion * 100).toFixed(0)}% cheaper but less capable`;\n\n return { model: m.id, costDelta, tradeoff };\n });\n}\n\nexport function getModelSpec(modelId: ModelId): ModelSpec | undefined {\n return MODEL_REGISTRY.find((m) => m.id === modelId);\n}\n","import type { CostEstimate, ModelId } from '../types/interact.js';\nimport { getModelSpec, MODEL_REGISTRY } from './router.js';\n\n// ===== COST ESTIMATION =====\n\nexport function estimateCost(\n modelId: ModelId,\n inputTokens: number,\n totalProjectTokens: number,\n estimatedOutputRatio: number = 0.3,\n): CostEstimate {\n const spec = getModelSpec(modelId) ?? MODEL_REGISTRY[1]; // fallback to Sonnet\n const estimatedOutputTokens = Math.round(inputTokens * estimatedOutputRatio);\n\n const inputCost = (inputTokens / 1_000_000) * spec.pricing.inputPerMillion;\n const outputCost = (estimatedOutputTokens / 1_000_000) * spec.pricing.outputPerMillion;\n const totalCost = inputCost + outputCost;\n\n // Without optimization = sending all project tokens\n const woInputCost = (totalProjectTokens / 1_000_000) * spec.pricing.inputPerMillion;\n const woOutputCost = (Math.round(totalProjectTokens * estimatedOutputRatio) / 1_000_000) * spec.pricing.outputPerMillion;\n const woTotalCost = woInputCost + woOutputCost;\n\n const tokensSaved = totalProjectTokens - inputTokens;\n const costSaved = woTotalCost - totalCost;\n const savingsPercent = woTotalCost > 0 ? (costSaved / woTotalCost) * 100 : 0;\n\n return {\n model: modelId,\n inputTokens,\n estimatedOutputTokens,\n inputCost: round(inputCost),\n outputCost: round(outputCost),\n totalCost: round(totalCost),\n formatted: formatCost(totalCost),\n\n withoutOptimization: {\n inputTokens: totalProjectTokens,\n totalCost: round(woTotalCost),\n formatted: formatCost(woTotalCost),\n },\n\n savings: {\n tokensSaved: Math.max(0, tokensSaved),\n costSaved: round(Math.max(0, costSaved)),\n percent: Math.max(0, Math.round(savingsPercent)),\n formatted: costSaved > 0\n ? `saved ${formatCost(costSaved)} (${Math.round(savingsPercent)}%)`\n : 'no savings',\n },\n };\n}\n\n// ===== HELPERS =====\n\nfunction round(n: number): number {\n return Math.round(n * 1_000_000) / 1_000_000;\n}\n\nfunction formatCost(cost: number): string {\n if (cost < 0.001) return '<$0.001';\n if (cost < 0.01) return `$${cost.toFixed(4)}`;\n if (cost < 1) return `$${cost.toFixed(3)}`;\n return `$${cost.toFixed(2)}`;\n}\n","import type { StructuredPrompt, PromptSection, TaskType } from '../types/interact.js';\nimport type { ContextSelection, ProjectAnalysis } from '../types/engine.js';\nimport { countTokensChars4 } from '../engine/tokenizer.js';\n\n// ===== STRUCTURED PROMPT BUILDER =====\n//\n// Not templates. Not libraries.\n// Task-aware prompt construction with sections that can be measured and optimized.\n\nexport interface PromptOptions {\n task: string;\n taskType: TaskType;\n analysis: ProjectAnalysis;\n selection: ContextSelection;\n enableCoT?: boolean;\n enableConstraints?: boolean;\n enableAntiHallucination?: boolean;\n}\n\nexport function buildPrompt(options: PromptOptions): StructuredPrompt {\n const {\n task,\n taskType,\n analysis,\n selection,\n enableCoT = true,\n enableConstraints = true,\n enableAntiHallucination = true,\n } = options;\n\n const sections: PromptSection[] = [];\n\n // 1. System section — role priming\n sections.push(buildSystemSection(analysis.stack, taskType));\n\n // 2. Context section — project info + selected files\n sections.push(buildContextSection(analysis, selection));\n\n // 3. Task section — what the user wants\n sections.push(buildTaskSection(task, taskType));\n\n // 4. Constraints section — safety rails\n if (enableConstraints) {\n sections.push(buildConstraintsSection(analysis.stack, taskType));\n }\n\n // 5. Chain-of-thought section\n if (enableCoT) {\n sections.push(buildCoTSection(taskType));\n }\n\n // 6. Anti-hallucination section\n if (enableAntiHallucination) {\n sections.push(buildAntiHallucinationSection());\n }\n\n // 7. Output format section\n sections.push(buildFormatSection(taskType));\n\n const rendered = sections.map((s) => s.content).join('\\n\\n---\\n\\n');\n const totalTokens = sections.reduce((s, sec) => s + sec.tokens, 0);\n\n return { sections, totalTokens, rendered };\n}\n\n// ===== SECTION BUILDERS =====\n\nfunction buildSystemSection(stack: string[], taskType: TaskType): PromptSection {\n const stackStr = stack.length > 0 ? stack.join(', ') : 'software';\n const taskRole = TASK_ROLES[taskType] ?? 'engineer';\n\n const content = [\n `You are a senior ${stackStr} ${taskRole} with deep expertise in clean architecture, testing, and production-quality code.`,\n 'You prioritize correctness, readability, and maintainability.',\n 'You never make assumptions without evidence from the code.',\n ].join(' ');\n\n return makeSection('system', 'system', content);\n}\n\nfunction buildContextSection(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n): PromptSection {\n const lines: string[] = [];\n\n lines.push(`## Project: ${analysis.projectName}`);\n lines.push(`Stack: ${analysis.stack.join(', ') || 'Unknown'}`);\n lines.push(`Files analyzed: ${analysis.totalFiles} | Tokens: ~${Math.round(analysis.totalTokens / 1000)}K`);\n lines.push(`Context coverage: ${selection.coverage.score}% | Risk score: ${selection.riskScore}/100`);\n lines.push('');\n\n // List included files with their prune levels\n lines.push('### Included Files');\n lines.push('');\n\n const fullFiles = selection.files.filter((f) => f.pruneLevel === 'full');\n const sigFiles = selection.files.filter((f) => f.pruneLevel === 'signatures');\n const skelFiles = selection.files.filter((f) => f.pruneLevel === 'skeleton');\n\n if (fullFiles.length > 0) {\n lines.push('**Full content (read these first):**');\n for (const f of fullFiles) {\n lines.push(`- \\`${f.relativePath}\\` (~${Math.round(f.tokens / 1000)}K tokens) — ${f.reason}`);\n }\n lines.push('');\n }\n\n if (sigFiles.length > 0) {\n lines.push('**Signatures only (reference as needed):**');\n for (const f of sigFiles) {\n lines.push(`- \\`${f.relativePath}\\` (~${Math.round(f.tokens / 1000)}K tokens)`);\n }\n lines.push('');\n }\n\n if (skelFiles.length > 0) {\n lines.push('**Skeleton (structure overview):**');\n for (const f of skelFiles) {\n lines.push(`- \\`${f.relativePath}\\``);\n }\n lines.push('');\n }\n\n if (selection.coverage.missingCritical.length > 0) {\n lines.push('⚠️ **Missing critical files** (not included due to budget):');\n for (const f of selection.coverage.missingCritical) {\n lines.push(`- \\`${f}\\``);\n }\n lines.push('');\n }\n\n const content = lines.join('\\n');\n return makeSection('context', 'context', content);\n}\n\nfunction buildTaskSection(task: string, taskType: TaskType): PromptSection {\n const content = [\n '## Task',\n '',\n task,\n '',\n `Task type: **${taskType}**`,\n ].join('\\n');\n\n return makeSection('task', 'task', content);\n}\n\nfunction buildConstraintsSection(stack: string[], taskType: TaskType): PromptSection {\n const lines: string[] = ['## Constraints', ''];\n\n lines.push('- **Do NOT** delete or modify existing tests unless explicitly asked');\n lines.push('- **Do NOT** change function signatures that are part of the public API');\n lines.push('- **Do NOT** introduce new dependencies without mentioning it');\n lines.push('- **Always** handle errors explicitly (no silent catches)');\n lines.push('- **Always** preserve existing code style and conventions');\n lines.push('- **Prefer** minimal changes — smallest diff that solves the problem');\n\n if (stack.includes('TypeScript')) {\n lines.push('- **Always** use strict TypeScript types (no `any` unless unavoidable)');\n lines.push('- **Always** add explicit return types to exported functions');\n }\n\n if (taskType === 'refactor') {\n lines.push('- **Do NOT** change behavior — refactoring must be behavior-preserving');\n lines.push('- **Verify** all existing tests still pass after changes');\n }\n\n if (taskType === 'test') {\n lines.push('- Use AAA pattern: Arrange, Act, Assert');\n lines.push('- Test boundaries, null/undefined, async errors, type edges');\n lines.push('- Use descriptive test names: \"should [expected] when [condition]\"');\n }\n\n return makeSection('constraints', 'constraints', lines.join('\\n'));\n}\n\nfunction buildCoTSection(taskType: TaskType): PromptSection {\n const steps = COT_STEPS[taskType] ?? COT_STEPS['simple-edit'];\n\n const lines: string[] = ['## Thinking Process', '', 'Before writing any code:'];\n steps.forEach((step, i) => {\n lines.push(`${i + 1}. ${step}`);\n });\n\n return makeSection('cot', 'constraints', lines.join('\\n'));\n}\n\nfunction buildAntiHallucinationSection(): PromptSection {\n const content = [\n '## Important',\n '',\n '- Only reference files, functions, and APIs that exist in the provided context',\n '- If you are unsure about something, say so explicitly',\n '- Do NOT invent function signatures, types, or module paths',\n '- If the context is insufficient to complete the task, explain what is missing',\n ].join('\\n');\n\n return makeSection('anti-hallucination', 'constraints', content);\n}\n\nfunction buildFormatSection(taskType: TaskType): PromptSection {\n const lines: string[] = ['## Output Format', ''];\n\n if (taskType === 'review') {\n lines.push('Provide findings in priority order: Critical > Major > Minor > Nitpick');\n lines.push('For each finding: file, line, issue, and concrete suggestion with code');\n } else if (taskType === 'architecture') {\n lines.push('Present 2-3 options with trade-offs, then recommend one with justification');\n } else if (taskType === 'debug') {\n lines.push('1. Root cause analysis');\n lines.push('2. Minimal fix');\n lines.push('3. Explanation of why the fix works');\n lines.push('4. Edge cases to consider');\n } else {\n lines.push('Provide clean, production-ready code with brief explanations of key decisions');\n }\n\n return makeSection('format', 'format', lines.join('\\n'));\n}\n\n// ===== CONSTANTS =====\n\nconst TASK_ROLES: Record<TaskType, string> = {\n debug: 'debugger',\n review: 'code reviewer',\n refactor: 'architect',\n test: 'test engineer',\n docs: 'technical writer',\n feature: 'engineer',\n architecture: 'systems architect',\n 'simple-edit': 'engineer',\n};\n\nconst COT_STEPS: Record<TaskType, string[]> = {\n debug: [\n '**Reproduce** — Understand the exact symptom and when it occurs',\n '**Hypothesize** — List the most likely root causes (max 3)',\n '**Verify** — Check each hypothesis against the code',\n '**Fix** — Apply the minimal fix that addresses the root cause',\n '**Validate** — Explain why the fix works and what edge cases it covers',\n ],\n review: [\n '**Understand** — Read the code and understand its purpose',\n '**Assess** — Evaluate correctness, readability, performance, security',\n '**Prioritize** — Rank issues by severity (critical > major > minor)',\n '**Suggest** — Provide concrete, actionable improvements with code',\n ],\n refactor: [\n '**Analyze** — Identify code smells and structural issues',\n '**Plan** — Define the target structure before changing anything',\n '**Preserve** — Ensure behavior doesn\\'t change',\n '**Refactor** — Apply changes incrementally',\n '**Verify** — Confirm all existing tests still pass',\n ],\n test: [\n '**Identify** — What needs testing? (happy path, edge cases, errors)',\n '**Structure** — Use AAA pattern: Arrange, Act, Assert',\n '**Cover** — Test boundaries, null/undefined, async errors',\n '**Isolate** — Mock external dependencies, test units independently',\n ],\n docs: [\n '**Read** — Understand the code before documenting',\n '**Structure** — Organize by audience (API users, contributors, operators)',\n '**Write** — Clear, concise, with examples',\n ],\n feature: [\n '**Clarify** — Restate the requirement in your own words',\n '**Design** — Plan the approach (types, interfaces, flow)',\n '**Implement** — Build incrementally, starting with types',\n '**Test** — Write tests alongside implementation',\n '**Integrate** — Ensure no regressions',\n ],\n architecture: [\n '**Context** — Understand current architecture and constraints',\n '**Options** — Present 2-3 viable approaches with trade-offs',\n '**Recommend** — Choose the best and explain why',\n '**Plan** — Define migration steps',\n '**Risks** — Identify risks and mitigation strategies',\n ],\n 'simple-edit': [\n '**Understand** — Read the relevant code',\n '**Plan** — Think before writing',\n '**Implement** — Write clean, well-typed code',\n '**Verify** — Check for edge cases',\n ],\n};\n\n// ===== HELPERS =====\n\nfunction makeSection(\n id: string,\n role: PromptSection['role'],\n content: string,\n): PromptSection {\n const tokens = countTokensChars4(Buffer.byteLength(content, 'utf-8'));\n return { id, role, content, tokens };\n}\n","import { randomUUID, createHash } from 'node:crypto';\nimport { readdir, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { userInfo } from 'node:os';\nimport { homedir } from 'node:os';\nimport type { AuditEntry, AuditAction } from '../types/govern.js';\n\n// ===== AUDIT TRAIL =====\n//\n// Immutable, tamper-evident audit log for all CTO interactions.\n// Each entry is SHA-256 hashed for integrity verification.\n\nconst CTO_DIR = '.cto-ai';\nconst AUDIT_DIR = 'audit';\nconst MAX_ENTRIES_PER_FILE = 500;\n\nfunction getAuditDir(): string {\n return join(homedir(), CTO_DIR, AUDIT_DIR);\n}\n\nfunction getCurrentAuditFile(): string {\n const date = new Date().toISOString().split('T')[0].replace(/-/g, '');\n return join(getAuditDir(), `audit_${date}.json`);\n}\n\nfunction computeIntegrityHash(entry: Omit<AuditEntry, 'integrityHash'>): string {\n const payload = JSON.stringify({\n id: entry.id,\n timestamp: entry.timestamp,\n action: entry.action,\n user: entry.user,\n projectPath: entry.projectPath,\n details: entry.details,\n });\n return createHash('sha256').update(payload).digest('hex');\n}\n\n// ===== HELPERS (inline to avoid cross-layer deps) =====\n\nasync function ensureDir(dirPath: string): Promise<void> {\n const { mkdir } = await import('node:fs/promises');\n await mkdir(dirPath, { recursive: true });\n}\n\nasync function readJSON<T>(filePath: string): Promise<T | null> {\n const { readFile } = await import('node:fs/promises');\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nasync function writeJSON(filePath: string, data: unknown): Promise<void> {\n const { writeFile } = await import('node:fs/promises');\n await ensureDir(join(filePath, '..'));\n await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// ===== PUBLIC API =====\n\nexport async function logAudit(\n action: AuditAction,\n projectPath: string,\n details: Record<string, unknown> = {},\n): Promise<AuditEntry> {\n const auditDir = getAuditDir();\n await ensureDir(auditDir);\n\n let currentUser: string;\n try {\n currentUser = userInfo().username;\n } catch {\n currentUser = process.env.USER ?? process.env.USERNAME ?? 'unknown';\n }\n\n const partialEntry = {\n id: randomUUID().substring(0, 12),\n timestamp: new Date(),\n action,\n user: currentUser,\n projectPath,\n details,\n };\n\n const entry: AuditEntry = {\n ...partialEntry,\n integrityHash: computeIntegrityHash(partialEntry),\n };\n\n const auditFile = getCurrentAuditFile();\n let entries = await readJSON<AuditEntry[]>(auditFile) ?? [];\n entries.push(entry);\n\n if (entries.length > MAX_ENTRIES_PER_FILE) {\n entries = entries.slice(-MAX_ENTRIES_PER_FILE);\n }\n\n await writeJSON(auditFile, entries);\n try { await chmod(auditFile, 0o600); } catch { /* ignore on Windows */ }\n\n return entry;\n}\n\nexport async function getAuditEntries(\n options: {\n projectPath?: string;\n action?: AuditAction;\n since?: Date;\n limit?: number;\n } = {},\n): Promise<AuditEntry[]> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return [];\n }\n\n const auditFiles = files\n .filter((f) => f.startsWith('audit_') && f.endsWith('.json'))\n .sort()\n .reverse();\n\n const allEntries: AuditEntry[] = [];\n const limit = options.limit ?? 100;\n\n for (const file of auditFiles) {\n if (allEntries.length >= limit) break;\n\n const entries = await readJSON<AuditEntry[]>(join(auditDir, file));\n if (!entries) continue;\n\n for (const entry of entries.reverse()) {\n if (allEntries.length >= limit) break;\n if (options.projectPath && entry.projectPath !== options.projectPath) continue;\n if (options.action && entry.action !== options.action) continue;\n if (options.since && new Date(entry.timestamp) < options.since) continue;\n allEntries.push(entry);\n }\n }\n\n return allEntries;\n}\n\nexport function verifyAuditEntry(entry: AuditEntry): boolean {\n const { integrityHash, ...rest } = entry;\n const expected = computeIntegrityHash(rest as Omit<AuditEntry, 'integrityHash'>);\n return expected === integrityHash;\n}\n\nexport async function verifyAuditIntegrity(): Promise<{\n totalEntries: number;\n validEntries: number;\n invalidEntries: AuditEntry[];\n}> {\n const entries = await getAuditEntries({ limit: 10000 });\n const invalidEntries: AuditEntry[] = [];\n\n for (const entry of entries) {\n if (!verifyAuditEntry(entry)) {\n invalidEntries.push(entry);\n }\n }\n\n return {\n totalEntries: entries.length,\n validEntries: entries.length - invalidEntries.length,\n invalidEntries,\n };\n}\n\nexport async function purgeOldAuditEntries(retentionDays: number): Promise<number> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return 0;\n }\n\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - retentionDays);\n const cutoffStr = cutoff.toISOString().split('T')[0].replace(/-/g, '');\n\n let purged = 0;\n const { unlink } = await import('node:fs/promises');\n\n for (const file of files) {\n if (!file.startsWith('audit_') || !file.endsWith('.json')) continue;\n const dateStr = file.replace('audit_', '').replace('.json', '');\n if (dateStr < cutoffStr) {\n try {\n await unlink(join(auditDir, file));\n purged++;\n } catch { /* ignore */ }\n }\n }\n\n return purged;\n}\n"],"mappings":"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","names":["randomUUID","readFile","readFile","readFile","createHash","join","readFile","randomUUID"]}
|
package/dist/mcp/v2.js
CHANGED
|
@@ -14745,7 +14745,29 @@ var BUILTIN_PATTERNS = [
|
|
|
14745
14745
|
{ type: "connection-string", source: `(?:mongodb(?:\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\/\\/[^\\s'"]+:[^\\s'"]+@[^\\s'"]+`, flags: "gi", severity: "critical", description: "Database Connection String" },
|
|
14746
14746
|
{ type: "connection-string", source: `(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\s*[:=]\\s*['"]?([^\\s'"]{10,})['"]?`, flags: "gi", severity: "high", description: "Database URL" },
|
|
14747
14747
|
// Environment variables with secrets
|
|
14748
|
-
{ type: "env-variable", source: `(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\s*[:=]\\s*['"]?([^\\s'"]{8,})['"]?`, flags: "gi", severity: "high", description: "Secret Environment Variable" }
|
|
14748
|
+
{ type: "env-variable", source: `(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\s*[:=]\\s*['"]?([^\\s'"]{8,})['"]?`, flags: "gi", severity: "high", description: "Secret Environment Variable" },
|
|
14749
|
+
// Stripe
|
|
14750
|
+
{ type: "api-key", source: "sk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Stripe Live Secret Key" },
|
|
14751
|
+
{ type: "api-key", source: "pk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "high", description: "Stripe Live Publishable Key" },
|
|
14752
|
+
{ type: "api-key", source: "rk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Stripe Restricted Key" },
|
|
14753
|
+
// Slack
|
|
14754
|
+
{ type: "token", source: "xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Slack Bot Token" },
|
|
14755
|
+
{ type: "token", source: "xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Slack User Token" },
|
|
14756
|
+
{ type: "api-key", source: "https://hooks\\.slack\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+", flags: "g", severity: "high", description: "Slack Webhook URL" },
|
|
14757
|
+
// Google
|
|
14758
|
+
{ type: "api-key", source: "AIza[0-9A-Za-z_-]{35}", flags: "g", severity: "high", description: "Google API Key" },
|
|
14759
|
+
{ type: "token", source: "ya29\\.[0-9A-Za-z_-]+", flags: "g", severity: "high", description: "Google OAuth Token" },
|
|
14760
|
+
// Azure
|
|
14761
|
+
{ type: "api-key", source: "(?:AccountKey|SharedAccessKey)\\s*=\\s*[a-zA-Z0-9+/=]{40,}", flags: "g", severity: "critical", description: "Azure Storage Key" },
|
|
14762
|
+
// Twilio
|
|
14763
|
+
{ type: "api-key", source: "AC[a-f0-9]{32}", flags: "g", severity: "high", description: "Twilio Account SID" },
|
|
14764
|
+
// SendGrid
|
|
14765
|
+
{ type: "api-key", source: "SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}", flags: "g", severity: "critical", description: "SendGrid API Key" },
|
|
14766
|
+
// JWT
|
|
14767
|
+
{ type: "token", source: "eyJ[a-zA-Z0-9_-]{10,}\\.eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}", flags: "g", severity: "high", description: "JSON Web Token" },
|
|
14768
|
+
// PII
|
|
14769
|
+
{ type: "pii", source: "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b", flags: "g", severity: "medium", description: "Email Address (PII)" },
|
|
14770
|
+
{ type: "pii", source: "\\b\\d{3}[-.]?\\d{2}[-.]?\\d{4}\\b", flags: "g", severity: "high", description: "Possible SSN (PII)" }
|
|
14749
14771
|
];
|
|
14750
14772
|
function buildPatterns(customPatterns = []) {
|
|
14751
14773
|
const patterns = BUILTIN_PATTERNS.map((def) => ({
|