dep-oracle 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/server.ts","../src/mcp/tools.ts","../src/utils/graph.ts","../src/reporters/json.ts"],"sourcesContent":["/**\n * MCP stdio server entry point for dep-oracle.\n *\n * Exposes the full dep-oracle analysis engine as MCP tools so that\n * Claude (and other MCP clients) can scan projects, evaluate trust\n * scores, detect zombies, and more -- all through the standard\n * Model Context Protocol.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { registerTools } from './tools.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst pkgPath = join(__dirname, '..', '..', 'package.json');\nconst pkgVersion = (() => {\n try {\n return JSON.parse(readFileSync(pkgPath, 'utf-8')).version as string;\n } catch {\n return '1.2.1';\n }\n})();\n\nconst server = new Server(\n { name: 'dep-oracle', version: pkgVersion },\n { capabilities: { tools: {} } },\n);\n\nregisterTools(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","/**\n * MCP tool definitions for dep-oracle.\n *\n * Registers 8 tools that expose the full analysis pipeline:\n * 1. dep_oracle_scan -- full project scan\n * 2. dep_oracle_trust_score -- single package trust score\n * 3. dep_oracle_blast_radius -- import impact analysis\n * 4. dep_oracle_zombies -- list zombie dependencies\n * 5. dep_oracle_suggest_migration -- migration suggestions\n * 6. dep_oracle_typosquat_check -- typosquat risk check\n * 7. dep_oracle_compare -- side-by-side package comparison\n * 8. dep_oracle_report -- generate HTML report\n *\n * All tools share the same core engine (parsers, collectors, analyzers)\n * as the CLI.\n */\n\nimport { resolve } from 'node:path';\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n ListToolsRequestSchema,\n CallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { CacheManager } from '../cache/store.js';\nimport { NpmParser } from '../parsers/npm.js';\nimport { PythonParser } from '../parsers/python.js';\nimport type { DependencyTree, TrustReport, ScanResult } from '../parsers/schema.js';\nimport { CollectorOrchestrator } from '../collectors/orchestrator.js';\nimport { TrustScoreEngine } from '../analyzers/trust-score.js';\nimport { ZombieDetector } from '../analyzers/zombie-detector.js';\nimport { BlastRadiusCalculator } from '../analyzers/blast-radius.js';\nimport { MigrationAdvisor } from '../analyzers/migration-advisor.js';\nimport { TyposquatDetector } from '../analyzers/typosquat.js';\nimport { buildImportGraph, getBlastRadius } from '../utils/graph.js';\nimport { JsonReporter } from '../reporters/json.js';\n\n// ---------------------------------------------------------------------------\n// Security: path validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9._-~]*\\/)?[a-z0-9-~][a-z0-9._-~]*$/i;\n\nfunction validateDir(input: string | undefined): string {\n const dir = resolve(String(input ?? process.cwd()));\n // Block obvious path traversal patterns\n const normalized = dir.replace(/\\\\/g, '/');\n if (normalized.includes('/../') || normalized.endsWith('/..')) {\n throw new Error('Path traversal not allowed');\n }\n return dir;\n}\n\nfunction validateOutputPath(output: string, baseDir: string): string {\n const resolved = resolve(output);\n const base = resolve(baseDir);\n if (!resolved.startsWith(base)) {\n throw new Error('Output path must be within the project directory');\n }\n return resolved;\n}\n\nfunction validatePackageName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) throw new Error('Package name is required');\n if (trimmed.length > 214) throw new Error('Package name too long');\n if (!VALID_PACKAGE_NAME.test(trimmed)) {\n throw new Error(`Invalid package name: \"${trimmed}\"`);\n }\n return trimmed;\n}\n\n// ---------------------------------------------------------------------------\n// Shared instances (created once per MCP server lifetime)\n// ---------------------------------------------------------------------------\n\nconst cache = new CacheManager();\nconst orchestrator = new CollectorOrchestrator(cache, {\n githubToken: process.env.GITHUB_TOKEN,\n});\nconst trustEngine = new TrustScoreEngine();\nconst zombieDetector = new ZombieDetector();\nconst blastRadiusCalc = new BlastRadiusCalculator();\nconst migrationAdvisor = new MigrationAdvisor();\nconst typosquatDetector = new TyposquatDetector();\nconst jsonReporter = new JsonReporter();\nconst parsers = [new NpmParser(), new PythonParser()];\n\n// ---------------------------------------------------------------------------\n// Tool definitions\n// ---------------------------------------------------------------------------\n\nconst TOOLS = [\n {\n name: 'dep_oracle_scan',\n description:\n 'Perform a full dependency security scan on a project directory. ' +\n 'Parses the manifest (package.json, requirements.txt, etc.), collects data ' +\n 'from registries and GitHub, computes trust scores for every dependency, ' +\n 'detects zombies, typosquats, and calculates blast radius. Returns a complete ' +\n 'ScanResult JSON with per-package trust reports and an overall project score.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory to scan. ' +\n 'Defaults to the current working directory if omitted.',\n },\n },\n required: [] as string[],\n },\n },\n {\n name: 'dep_oracle_trust_score',\n description:\n 'Calculate the trust score for a single npm package. Queries the npm registry, ' +\n 'GitHub, OSV vulnerability database, and other sources to produce a weighted ' +\n 'score (0-100) across 6 dimensions: security, maintainer, activity, popularity, ' +\n 'funding, and license. Returns a TrustReport JSON with the overall score, ' +\n 'per-dimension metrics, zombie status, and alternative suggestions.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'npm package name (e.g. \"express\", \"@scope/pkg\").',\n },\n version: {\n type: 'string',\n description:\n 'Specific version to analyze (e.g. \"4.18.2\"). If omitted, the latest version is used.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_blast_radius',\n description:\n 'Analyze the blast radius (import impact) of a package within a project. ' +\n 'Scans all JS/TS source files to find how many files import the given package. ' +\n 'Returns the count of affected files, their paths, and the percentage of the ' +\n 'codebase impacted. Useful for understanding the risk if a dependency is ' +\n 'compromised or needs replacement.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to check import usage for.',\n },\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_zombies',\n description:\n 'Detect zombie (abandoned/unmaintained) dependencies in a project. ' +\n 'Parses the manifest, queries registry and GitHub for each dependency, and ' +\n 'flags packages that show signs of abandonment: deprecated, no commits in 12+ months, ' +\n 'no active maintainers, etc. Returns an array of zombie packages with severity ' +\n 'levels and reasons.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n },\n required: [] as string[],\n },\n },\n {\n name: 'dep_oracle_suggest_migration',\n description:\n 'Get migration suggestions for a given package. Looks up the package in a ' +\n 'curated knowledge base of common replacements and returns alternatives with ' +\n 'descriptions and difficulty ratings. Useful for replacing deprecated, abandoned, ' +\n 'or low-trust packages (e.g. moment -> dayjs, request -> got, lodash -> es-toolkit).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to find migration alternatives for.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_typosquat_check',\n description:\n 'Check whether a package name is a potential typosquat of a popular package. ' +\n 'Uses Levenshtein distance, homoglyph detection, and pattern analysis to identify ' +\n 'suspicious package names that closely resemble well-known packages. Returns a ' +\n 'risk assessment with similar package names and the edit distance.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to check for typosquatting risk.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_compare',\n description:\n 'Compare two packages side-by-side by computing trust scores for both. ' +\n 'Returns the full trust report for each package including scores, metrics, ' +\n 'zombie status, and trend direction. Useful for evaluating alternatives ' +\n 'or choosing between competing libraries.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n packageA: {\n type: 'string',\n description: 'First package name to compare.',\n },\n packageB: {\n type: 'string',\n description: 'Second package name to compare.',\n },\n },\n required: ['packageA', 'packageB'],\n },\n },\n {\n name: 'dep_oracle_report',\n description:\n 'Generate a JSON report for a project. Runs a full scan and outputs the results ' +\n 'as formatted JSON. Optionally writes the report to a file. Returns the JSON ' +\n 'content or the path to the generated file.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n output: {\n type: 'string',\n description:\n 'Absolute path to write the report file. If omitted, the report content is returned directly.',\n },\n },\n required: [] as string[],\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Tool registration\n// ---------------------------------------------------------------------------\n\nexport function registerTools(server: Server): void {\n // List all available tools\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOLS,\n }));\n\n // Handle tool invocations\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n switch (name) {\n case 'dep_oracle_scan':\n return await handleScan(args);\n case 'dep_oracle_trust_score':\n return await handleTrustScore(args);\n case 'dep_oracle_blast_radius':\n return await handleBlastRadius(args);\n case 'dep_oracle_zombies':\n return await handleZombies(args);\n case 'dep_oracle_suggest_migration':\n return await handleSuggestMigration(args);\n case 'dep_oracle_typosquat_check':\n return await handleTyposquatCheck(args);\n case 'dep_oracle_compare':\n return await handleCompare(args);\n case 'dep_oracle_report':\n return await handleReport(args);\n default:\n return errorResponse(`Unknown tool: ${name}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return errorResponse(`Tool \"${name}\" failed: ${message}`);\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build a successful text content response\n// ---------------------------------------------------------------------------\n\nfunction successResponse(data: unknown) {\n const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);\n return {\n content: [{ type: 'text' as const, text }],\n };\n}\n\nfunction errorResponse(message: string) {\n return {\n content: [{ type: 'text' as const, text: message }],\n isError: true,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared: parse project dependencies\n// ---------------------------------------------------------------------------\n\nasync function parseProject(dir: string): Promise<DependencyTree | null> {\n for (const parser of parsers) {\n if (await parser.detect(dir)) {\n return parser.parse(dir);\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Shared: build a full trust report for a single package\n// ---------------------------------------------------------------------------\n\nasync function buildTrustReport(\n packageName: string,\n version: string,\n blastRadius: number = 0,\n): Promise<TrustReport> {\n const results = await orchestrator.collectAll(packageName, version);\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(\n results.registry.data,\n results.github.data,\n );\n const typosquat = typosquatDetector.check(packageName);\n const alternatives = migrationAdvisor.suggest(\n packageName,\n zombie.isZombie ? 'zombie dependency' : 'low trust score',\n );\n\n // Determine trend from popularity data\n const trend = results.popularity.data?.trend ?? 'stable';\n\n return {\n package: packageName,\n version,\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius,\n alternatives: alternatives.map((a) => a.alternative),\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tool handlers\n// ---------------------------------------------------------------------------\n\nasync function handleScan(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\". ` +\n 'Supported: package.json, requirements.txt, pyproject.toml, Pipfile.',\n );\n }\n\n // Build import graph for blast radius\n const importGraph = await buildImportGraph(dir);\n\n // Collect trust reports for all direct dependencies\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const report = await buildTrustReport(node.name, node.version, blastRadius);\n reports.push(report);\n }\n\n // Overall score: weighted average\n const overallScore =\n reports.length > 0\n ? Math.round(\n reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length,\n )\n : 0;\n\n // Summary\n const zombieCount = reports.filter((r) => r.isZombie).length;\n const criticalCount = reports.filter((r) => r.trustScore < 50).length;\n const summary =\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${zombieCount} zombie(s) detected. ` +\n `${criticalCount} package(s) below trust threshold.`;\n\n const scanResult: ScanResult = {\n tree,\n reports,\n overallScore,\n summary,\n };\n\n // Serialize (handles Map objects)\n const serialized = jsonReporter.report(scanResult);\n return successResponse(serialized);\n}\n\nasync function handleTrustScore(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const version = String(args?.version ?? 'latest');\n const report = await buildTrustReport(packageName, version);\n return successResponse(report);\n}\n\nasync function handleBlastRadius(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n const dir = validateDir(args?.dir as string | undefined);\n const result = await blastRadiusCalc.calculate(packageName, dir);\n return successResponse(result);\n}\n\nasync function handleZombies(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\".`,\n );\n }\n\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const zombies: Array<{\n package: string;\n version: string;\n severity: string;\n reason: string;\n lastActivity: string | null;\n }> = [];\n\n for (const node of directNodes) {\n const results = await orchestrator.collectAll(node.name, node.version);\n const zombie = zombieDetector.detect(\n results.registry.data,\n results.github.data,\n );\n\n if (zombie.isZombie) {\n zombies.push({\n package: node.name,\n version: node.version,\n severity: zombie.severity,\n reason: zombie.reason,\n lastActivity: zombie.lastActivity?.toISOString() ?? null,\n });\n }\n }\n\n if (zombies.length === 0) {\n return successResponse({\n message: 'No zombie dependencies detected.',\n zombies: [],\n });\n }\n\n return successResponse({\n message: `Found ${zombies.length} zombie dependency(ies).`,\n zombies,\n });\n}\n\nasync function handleSuggestMigration(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const suggestions = migrationAdvisor.suggest(packageName, 'manual query');\n\n if (suggestions.length === 0) {\n return successResponse({\n package: packageName,\n message: `No migration suggestions found for \"${packageName}\". ` +\n 'This package may not have known alternatives in our database.',\n suggestions: [],\n });\n }\n\n return successResponse({\n package: packageName,\n suggestions,\n });\n}\n\nasync function handleTyposquatCheck(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const result = typosquatDetector.check(packageName);\n return successResponse({\n package: packageName,\n ...result,\n });\n}\n\nasync function handleCompare(args: Record<string, unknown> | undefined) {\n const packageA = validatePackageName(String(args?.packageA ?? ''));\n const packageB = validatePackageName(String(args?.packageB ?? ''));\n\n const [reportA, reportB] = await Promise.all([\n buildTrustReport(packageA, 'latest'),\n buildTrustReport(packageB, 'latest'),\n ]);\n\n return successResponse({\n comparison: {\n packageA: reportA,\n packageB: reportB,\n winner:\n reportA.trustScore > reportB.trustScore\n ? packageA\n : reportA.trustScore < reportB.trustScore\n ? packageB\n : 'tie',\n scoreDifference: Math.abs(reportA.trustScore - reportB.trustScore),\n },\n });\n}\n\nasync function handleReport(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n const output = args?.output ? validateOutputPath(String(args.output), dir) : null;\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\".`,\n );\n }\n\n // Build import graph for blast radius\n const importGraph = await buildImportGraph(dir);\n\n // Collect trust reports for direct dependencies\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const report = await buildTrustReport(node.name, node.version, blastRadius);\n reports.push(report);\n }\n\n const overallScore =\n reports.length > 0\n ? Math.round(\n reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length,\n )\n : 0;\n\n const zombieCount = reports.filter((r) => r.isZombie).length;\n const criticalCount = reports.filter((r) => r.trustScore < 50).length;\n const summary =\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${zombieCount} zombie(s) detected. ` +\n `${criticalCount} package(s) below trust threshold.`;\n\n const scanResult: ScanResult = {\n tree,\n reports,\n overallScore,\n summary,\n };\n\n const jsonContent = jsonReporter.report(scanResult);\n\n if (output) {\n const { writeFile } = await import('node:fs/promises');\n await writeFile(output, jsonContent, 'utf-8');\n return successResponse({\n message: `Report written to ${output}`,\n path: output,\n });\n }\n\n return successResponse(jsonContent);\n}\n","import { readFile, readdir, stat } from \"node:fs/promises\";\nimport { join, extname } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** File extensions to scan for import statements. */\nconst SCANNABLE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\", \".mts\", \".cts\"]);\n\n/** Directories to skip during recursive traversal. */\nconst IGNORED_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \"out\",\n \".next\",\n \".nuxt\",\n \"coverage\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n]);\n\n// ---------------------------------------------------------------------------\n// Import extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Regular expressions that capture the package name from various import forms.\n *\n * ESM static imports:\n * import ... from \"package\"\n * import ... from 'package'\n * export ... from \"package\"\n *\n * CommonJS:\n * require(\"package\")\n * require('package')\n *\n * Dynamic imports:\n * import(\"package\")\n * import('package')\n *\n * The captured group is the full module specifier; we extract the package name\n * from it (handling scoped packages like @scope/name).\n */\nconst IMPORT_PATTERNS: RegExp[] = [\n // ESM: import ... from \"pkg\" / export ... from \"pkg\"\n /(?:import|export)\\s+.*?\\s+from\\s+[\"']([^\"']+)[\"']/g,\n // ESM: import \"pkg\" (side-effect import)\n /import\\s+[\"']([^\"']+)[\"']/g,\n // CJS: require(\"pkg\")\n /require\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g,\n // Dynamic: import(\"pkg\")\n /import\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g,\n];\n\n/**\n * Extract the npm package name from a module specifier.\n *\n * - Relative paths (\"./foo\", \"../bar\") are ignored (returns null).\n * - Node built-ins (\"node:fs\") are ignored.\n * - Scoped packages: \"@scope/name/sub\" -> \"@scope/name\"\n * - Regular packages: \"express/lib/router\" -> \"express\"\n */\nfunction extractPackageName(specifier: string): string | null {\n // Skip relative imports\n if (specifier.startsWith(\".\") || specifier.startsWith(\"/\")) return null;\n\n // Skip Node built-in modules\n if (specifier.startsWith(\"node:\")) return null;\n\n // Scoped package: @scope/name or @scope/name/sub/path\n if (specifier.startsWith(\"@\")) {\n const parts = specifier.split(\"/\");\n if (parts.length < 2) return null;\n return `${parts[0]}/${parts[1]}`;\n }\n\n // Regular package: name or name/sub/path\n return specifier.split(\"/\")[0];\n}\n\n/**\n * Scan a single file's content and return the set of external package names\n * it imports.\n */\nfunction extractImports(content: string): Set<string> {\n const packages = new Set<string>();\n\n for (const pattern of IMPORT_PATTERNS) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(content)) !== null) {\n const specifier = match[1];\n const pkg = extractPackageName(specifier);\n if (pkg) {\n packages.add(pkg);\n }\n }\n }\n\n return packages;\n}\n\n// ---------------------------------------------------------------------------\n// Recursive file traversal\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively collect all scannable source files under `dir`.\n */\nasync function collectSourceFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return files;\n }\n\n const tasks: Promise<void>[] = [];\n\n for (const entry of entries) {\n if (IGNORED_DIRS.has(entry)) continue;\n\n const fullPath = join(dir, entry);\n\n tasks.push(\n (async () => {\n try {\n const info = await stat(fullPath);\n if (info.isDirectory()) {\n const nested = await collectSourceFiles(fullPath);\n files.push(...nested);\n } else if (info.isFile() && SCANNABLE_EXTENSIONS.has(extname(entry))) {\n files.push(fullPath);\n }\n } catch {\n // Permission errors, broken symlinks, etc. — skip silently\n }\n })(),\n );\n }\n\n await Promise.all(tasks);\n return files;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Import graph mapping: `packageName -> Set<filePaths that import it>`.\n */\nexport type ImportGraph = Map<string, Set<string>>;\n\n/**\n * Scan all JS/TS source files under `dir` and build a mapping from each\n * external package name to the set of files that import it.\n *\n * This is used to compute the **blast radius** — how many files in the\n * project would be affected if a particular dependency were removed or\n * compromised.\n *\n * @param dir Root directory to scan (usually the project root).\n * @returns Map from package name to set of absolute file paths.\n */\nexport async function buildImportGraph(dir: string): Promise<ImportGraph> {\n const graph: ImportGraph = new Map();\n const sourceFiles = await collectSourceFiles(dir);\n\n // Read all files in parallel (bounded by OS limits)\n const results = await Promise.all(\n sourceFiles.map(async (filePath) => {\n try {\n const content = await readFile(filePath, \"utf-8\");\n const packages = extractImports(content);\n return { filePath, packages };\n } catch {\n return { filePath, packages: new Set<string>() };\n }\n }),\n );\n\n for (const { filePath, packages } of results) {\n for (const pkg of packages) {\n let fileSet = graph.get(pkg);\n if (!fileSet) {\n fileSet = new Set();\n graph.set(pkg, fileSet);\n }\n fileSet.add(filePath);\n }\n }\n\n return graph;\n}\n\n/**\n * Compute the blast radius of a package — the number of source files in\n * the project that directly import it.\n *\n * @param packageName npm/pypi package name.\n * @param graph Import graph built by `buildImportGraph`.\n * @returns Number of files that import this package.\n */\nexport function getBlastRadius(packageName: string, graph: ImportGraph): number {\n const files = graph.get(packageName);\n return files ? files.size : 0;\n}\n\n/**\n * Return the list of files that import a given package.\n *\n * @param packageName npm/pypi package name.\n * @param graph Import graph built by `buildImportGraph`.\n * @returns Array of absolute file paths.\n */\nexport function getImportingFiles(packageName: string, graph: ImportGraph): string[] {\n const files = graph.get(packageName);\n return files ? Array.from(files) : [];\n}\n","import type { ScanResult } from \"../parsers/schema.js\";\n\n// ---------------------------------------------------------------------------\n// JsonReporter\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a ScanResult as formatted JSON.\n *\n * The output is a self-contained JSON string suitable for piping into jq,\n * saving to a file, or passing to downstream tooling.\n */\nexport class JsonReporter {\n /**\n * Convert the scan result into a pretty-printed JSON string.\n *\n * Map objects (e.g. DependencyTree.nodes) are converted to plain\n * objects so that they survive JSON serialization.\n */\n report(result: ScanResult): string {\n return JSON.stringify(this.toSerializable(result), null, 2);\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n /**\n * Walk the value recursively and convert Map instances into plain objects\n * so JSON.stringify can handle them.\n */\n private toSerializable(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (value instanceof Map) {\n const obj: Record<string, unknown> = {};\n for (const [k, v] of value.entries()) {\n obj[String(k)] = this.toSerializable(v);\n }\n return obj;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.toSerializable(item));\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = this.toSerializable(v);\n }\n return result;\n }\n\n return value;\n }\n}\n"],"mappings":"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","names":["join","server","__filename","__dirname","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dep-oracle",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "mcpName": "io.github.ertugrulakben/dep-oracle",
5
5
  "description": "Predictive dependency security engine. Trust scores, zombie detection, blast radius analysis for your supply chain.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.ertugrulakben/dep-oracle",
4
- "version": "1.2.0",
4
+ "version": "1.2.1",
5
5
  "description": "Predictive dependency security engine. Trust scores, zombie detection, blast radius analysis.",
6
6
  "repository": {
7
7
  "url": "https://github.com/ertugrulakben/dep-oracle",
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "dep-oracle",
14
- "version": "1.2.0",
14
+ "version": "1.2.1",
15
15
  "runtimeHint": "node",
16
16
  "transport": {
17
17
  "type": "stdio"