@ctxo/cli 0.12.0 → 0.13.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.
- package/README.md +54 -8
- package/dist/{chunk-CDGQGCVN.js → chunk-DT3ZH4AU.js} +90 -25
- package/dist/chunk-DT3ZH4AU.js.map +1 -0
- package/dist/{chunk-HZ4UGA6J.js → chunk-WB7WZKX4.js} +1392 -333
- package/dist/chunk-WB7WZKX4.js.map +1 -0
- package/dist/{cli-router-LO2SWFC4.js → cli-router-DDHH4WQH.js} +3718 -946
- package/dist/cli-router-DDHH4WQH.js.map +1 -0
- package/dist/index.js +273 -274
- package/dist/index.js.map +1 -1
- package/dist/json-index-reader-OXTRTGE6.js +13 -0
- package/llms-full.txt +270 -28
- package/llms.txt +62 -10
- package/package.json +8 -6
- package/pages/visualize-template.html +8 -2
- package/skills/ctxo-review-pr/SKILL.md +2 -0
- package/skills/ctxo-safe-edit/SKILL.md +2 -0
- package/skills/ctxo-understand/SKILL.md +28 -4
- package/dist/chunk-CDGQGCVN.js.map +0 -1
- package/dist/chunk-HZ4UGA6J.js.map +0 -1
- package/dist/cli-router-LO2SWFC4.js.map +0 -1
- package/dist/json-index-reader-UYS74USB.js +0 -9
- /package/dist/{json-index-reader-UYS74USB.js.map → json-index-reader-OXTRTGE6.js.map} +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapters/transport/http-server-transport.ts","../src/core/masking/masking-pipeline.ts","../src/adapters/mcp/get-logic-slice.ts","../src/core/logic-slice/logic-slice-query.ts","../src/core/detail-levels/detail-formatter.ts","../src/core/response-envelope.ts","../src/core/intent-filter.ts","../src/adapters/mcp/get-why-context.ts","../src/core/overlay/snapshot-staleness.ts","../src/adapters/mcp/get-change-intelligence.ts","../src/core/change-intelligence/churn-analyzer.ts","../src/core/change-intelligence/health-scorer.ts","../src/adapters/mcp/get-blast-radius.ts","../src/adapters/mcp/get-architectural-overlay.ts","../src/adapters/mcp/get-dead-code.ts","../src/adapters/mcp/get-context-for-task.ts","../src/core/context-assembly/task-context-strategy.ts","../src/core/context-assembly/context-assembler.ts","../src/adapters/mcp/get-ranked-context.ts","../src/core/search/symbol-tokenizer.ts","../src/core/search/fuzzy-corrector.ts","../src/core/search/search-engine.ts","../src/adapters/mcp/search-symbols.ts","../src/adapters/mcp/get-changed-symbols.ts","../src/adapters/mcp/find-importers.ts","../src/adapters/mcp/get-class-hierarchy.ts","../src/adapters/mcp/get-symbol-importance.ts","../src/adapters/mcp/get-pr-impact.ts","../src/adapters/stats/with-recording.ts","../src/core/overlay/cluster-label-masker.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { startHttpTransport } from './adapters/transport/http-server-transport.js';\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createLogger } from './core/logger.js';\nimport { SqliteStorageAdapter } from './adapters/storage/sqlite-storage-adapter.js';\nimport { MaskingPipeline, type MaskingPatternConfig } from './core/masking/masking-pipeline.js';\nimport { handleGetLogicSlice } from './adapters/mcp/get-logic-slice.js';\nimport { handleGetWhyContext } from './adapters/mcp/get-why-context.js';\nimport { handleGetChangeIntelligence } from './adapters/mcp/get-change-intelligence.js';\nimport { SimpleGitAdapter } from './adapters/git/simple-git-adapter.js';\nimport { handleGetBlastRadius } from './adapters/mcp/get-blast-radius.js';\nimport { handleGetArchitecturalOverlay } from './adapters/mcp/get-architectural-overlay.js';\nimport { handleFindDeadCode } from './adapters/mcp/get-dead-code.js';\nimport { handleGetContextForTask } from './adapters/mcp/get-context-for-task.js';\nimport { handleGetRankedContext } from './adapters/mcp/get-ranked-context.js';\nimport { handleSearchSymbols } from './adapters/mcp/search-symbols.js';\nimport { handleGetChangedSymbols } from './adapters/mcp/get-changed-symbols.js';\nimport { handleFindImporters } from './adapters/mcp/find-importers.js';\nimport { handleGetClassHierarchy } from './adapters/mcp/get-class-hierarchy.js';\nimport { handleGetSymbolImportance } from './adapters/mcp/get-symbol-importance.js';\nimport { handleGetPrImpact } from './adapters/mcp/get-pr-impact.js';\nimport { SessionRecorderAdapter } from './adapters/stats/session-recorder-adapter.js';\nimport { withRecording } from './adapters/stats/with-recording.js';\nimport type { ISessionRecorderPort } from './ports/i-session-recorder-port.js';\nimport { detectWorkspace } from './adapters/workspace/single-package-workspace.js';\nimport { setWorkspaceMeta } from './core/response-envelope.js';\nimport { loadConfig, maskingClusterLabelPatterns, statsEnabled } from './core/config/load-config.js';\nimport { ClusterLabelMasker } from './core/overlay/cluster-label-masker.js';\n\nconst log = createLogger('ctxo:mcp');\n\nfunction loadMaskingConfig(ctxoRoot: string): MaskingPipeline {\n const jsonConfigPath = join(ctxoRoot, 'masking.json');\n\n // Try JSON masking config first\n if (existsSync(jsonConfigPath)) {\n try {\n const raw = readFileSync(jsonConfigPath, 'utf-8');\n const patterns: MaskingPatternConfig[] = JSON.parse(raw);\n log.info(`Loaded ${patterns.length} custom masking pattern(s)`);\n return MaskingPipeline.fromConfig(patterns);\n } catch (err) {\n log.error(`Failed to load masking config: ${(err as Error).message}`);\n }\n }\n\n return new MaskingPipeline();\n}\n\nfunction readStatsEnabled(ctxoRoot: string): boolean {\n return statsEnabled(loadConfig(ctxoRoot).config);\n}\n\nconst PKG_VERSION: string = (() => {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n})();\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n // HTTP mode — check before CLI routing since --http is a server flag, not a CLI command\n const httpPortStr = process.env.CTXO_HTTP_PORT\n || (args.includes('--http') ? (args[args.indexOf('--port') + 1] || '3001') : null);\n\n if (!httpPortStr && args.length > 0) {\n // CLI mode\n const { CliRouter } = await import('./cli/cli-router.js');\n const router = new CliRouter(process.cwd());\n await router.route(args);\n return;\n }\n\n // Initialize adapters\n const ctxoRoot = '.ctxo';\n const workspace = detectWorkspace(process.cwd());\n setWorkspaceMeta({ root: workspace.root });\n const storage = new SqliteStorageAdapter(ctxoRoot, { allowProductionPath: true });\n await storage.init();\n\n const masking = loadMaskingConfig(ctxoRoot);\n const clusterLabelMasker = new ClusterLabelMasker(\n maskingClusterLabelPatterns(loadConfig(ctxoRoot).config),\n );\n const git = new SimpleGitAdapter(process.cwd());\n\n // Session recording (opt-out via config.yaml stats.enabled: false)\n const recorder: ISessionRecorderPort | null = readStatsEnabled(ctxoRoot)\n ? new SessionRecorderAdapter(storage.getDb(), () => storage.persist())\n : null;\n\n // Create MCP server\n const server = new McpServer({ name: 'ctxo', version: PKG_VERSION });\n\n // Staleness detection\n const { StalenessDetector } = await import('./core/staleness/staleness-detector.js');\n const staleness = new StalenessDetector(process.cwd(), ctxoRoot);\n\n registerTools(server, storage, masking, git, staleness, recorder, ctxoRoot, clusterLabelMasker);\n\n // Start MCP server — HTTP mode or stdio mode\n if (httpPortStr) {\n await startHttpTransport(async () => {\n const s = new McpServer({ name: 'ctxo', version: PKG_VERSION });\n registerTools(s, storage, masking, git, staleness, recorder, ctxoRoot, clusterLabelMasker);\n return s;\n }, parseInt(httpPortStr, 10));\n } else {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n }\n}\n\nfunction registerTools(\n server: McpServer,\n storage: SqliteStorageAdapter,\n masking: MaskingPipeline,\n git: SimpleGitAdapter,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n staleness: any,\n recorder: ISessionRecorderPort | null,\n ctxoRoot = '.ctxo',\n clusterLabelMasker?: ClusterLabelMasker,\n) {\n // Tool annotations — all Ctxo tools are read-only index queries\n const toolAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n } as const;\n\n // Register tools — each handler wrapped with session recording\n const logicSliceHandler = withRecording('get_logic_slice', handleGetLogicSlice(storage, masking, staleness, ctxoRoot), recorder);\n const whyContextHandler = withRecording('get_why_context', handleGetWhyContext(storage, git, masking, staleness, ctxoRoot, clusterLabelMasker), recorder);\n const changeIntelligenceHandler = withRecording('get_change_intelligence', handleGetChangeIntelligence(storage, git, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_logic_slice',\n {\n description: 'Retrieve a symbol and all its transitive dependencies as a Logic-Slice. Use this when you need to UNDERSTAND what a symbol depends on (downstream view). L1=signature only, L2=direct deps, L3=full closure, L4=with token budget. Use `intent` to filter dependencies by keyword (e.g., \"core\", \"adapter\"). All responses include `_meta` with item counts and truncation info. For impact analysis (what BREAKS if this changes), use get_blast_radius instead. For task-specific context, use get_context_for_task.',\n inputSchema: {\n symbolId: z.string().optional().describe('Single symbol ID (format: file::name::kind)'),\n symbolIds: z.array(z.string()).optional().describe('Batch: array of symbol IDs'),\n level: z.number().min(1).max(4).optional().default(3).describe('Detail level (L1=signature, L2=direct deps, L3=full closure, L4=with token budget)'),\n intent: z.string().optional().describe('Filter dependencies by intent keywords (e.g., \"core\", \"adapter\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => logicSliceHandler(args),\n );\n\n server.registerTool(\n 'get_why_context',\n {\n description: 'Retrieve git commit history intent and anti-pattern warnings (reverts, rollbacks) for a symbol. Use this when you need to understand WHY code was written this way or whether it has a history of problems. Pair with get_change_intelligence for complexity/churn scores.',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n maxCommits: z.number().int().min(1).optional().describe('Limit commit history to N most recent commits'),\n },\n annotations: toolAnnotations,\n },\n (args) => whyContextHandler(args),\n );\n\n server.registerTool(\n 'get_change_intelligence',\n {\n description: 'Retrieve complexity x churn composite score for a symbol — identifies hotspots that are both complex and frequently changed. Use this to prioritize refactoring targets or assess risk before modifying code. For git history details, use get_why_context. For impact scope, use get_blast_radius.',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n },\n annotations: toolAnnotations,\n },\n (args) => changeIntelligenceHandler(args),\n );\n\n const blastRadiusHandler = withRecording('get_blast_radius', handleGetBlastRadius(storage, masking, staleness, ctxoRoot, git, clusterLabelMasker), recorder);\n\n server.registerTool(\n 'get_blast_radius',\n {\n description: 'BEFORE modifying any function or class, call this to understand impact. Returns all symbols that would break if the target changes, split into confirmed (direct importers), likely (co-changed), and potential (transitive) tiers with risk scores. Use `confidence` to filter by tier, `intent` to filter by keyword. Large results are auto-truncated with `_meta.hint` for drill-in. For what a symbol DEPENDS ON (downstream), use get_logic_slice instead. For full PR-level analysis, use get_pr_impact.',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional().describe('Filter by confidence tier'),\n intent: z.string().optional().describe('Filter impacted symbols by intent keywords (e.g., \"test\", \"adapter\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => blastRadiusHandler(args),\n );\n\n const overlayHandler = withRecording('get_architectural_overlay', handleGetArchitecturalOverlay(storage, masking, staleness, git, clusterLabelMasker), recorder);\n\n server.registerTool(\n 'get_architectural_overlay',\n {\n description: 'Get the project architectural layer map — identifies which symbols belong to Domain, Infrastructure, and Adapter layers. Use this when onboarding to a new codebase or validating that a change respects layer boundaries. For symbol-level analysis, use get_logic_slice or get_blast_radius.',\n inputSchema: {\n layer: z.string().optional().describe('Filter by specific layer name'),\n },\n annotations: toolAnnotations,\n },\n (args) => overlayHandler(args),\n );\n\n const deadCodeHandler = withRecording('find_dead_code', handleFindDeadCode(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'find_dead_code',\n {\n description: 'Find unreachable symbols and files that are never imported or called anywhere. Use this during cleanup, refactoring, or before deleting code to confirm it is truly unused. Use `intent` to filter results by keyword (e.g., \"adapter\", \"function\"). Large results are auto-truncated with `_meta`. For reverse dependency lookup of a specific symbol, use find_importers instead.',\n inputSchema: {\n includeTests: z.boolean().optional().default(false).describe('Include test files in analysis (default: exclude)'),\n intent: z.string().optional().describe('Filter dead code results by intent keywords (e.g., \"adapter\", \"function\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => deadCodeHandler(args),\n );\n\n const contextForTaskHandler = withRecording('get_context_for_task', handleGetContextForTask(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_context_for_task',\n {\n description: 'Get task-optimized context for a symbol based on what you are about to do. Specify taskType: \"fix\" (bug investigation — includes history + anti-patterns), \"extend\" (add feature — includes deps + blast radius), \"refactor\" (restructure — includes importers + complexity), or \"understand\" (learn — includes full slice + architecture). This is the BEST starting point when you know both the symbol and your intent.',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n taskType: z.enum(['fix', 'extend', 'refactor', 'understand']).describe('Task type determines which context is most relevant'),\n tokenBudget: z.number().optional().default(4000).describe('Max tokens for context (default 4000)'),\n },\n annotations: toolAnnotations,\n },\n (args) => contextForTaskHandler(args),\n );\n\n const rankedContextHandler = withRecording('get_ranked_context', handleGetRankedContext(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_ranked_context',\n {\n description: 'Search and rank symbols by relevance to a natural language query, packed within a token budget. Uses BM25 text matching + PageRank importance scoring. Results include `_meta` with truncation info. Use this when you have a question or topic but do not know which specific symbol to look at. For exact symbol name search, use search_symbols instead.',\n inputSchema: {\n query: z.string().min(1).describe('Search query (matches symbol names)'),\n tokenBudget: z.number().optional().default(4000).describe('Max tokens for results (default 4000)'),\n strategy: z.enum(['combined', 'dependency', 'importance']).optional().default('combined').describe('Ranking strategy'),\n },\n annotations: toolAnnotations,\n },\n (args) => rankedContextHandler(args),\n );\n\n const searchSymbolsHandler = withRecording('search_symbols', handleSearchSymbols(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'search_symbols',\n {\n description: 'Search symbols by exact name or regex pattern across the codebase index. Use this when you know (part of) the symbol name and need to find its ID for use with other tools. For semantic/relevance-based search, use get_ranked_context instead.',\n inputSchema: {\n pattern: z.string().min(1).describe('Search pattern (substring or regex)'),\n kind: z.enum(['function', 'class', 'interface', 'method', 'variable', 'type']).optional().describe('Filter by symbol kind'),\n filePattern: z.string().optional().describe('Filter by file path substring'),\n limit: z.number().int().min(1).max(100).optional().default(25).describe('Max results (default 25)'),\n },\n annotations: toolAnnotations,\n },\n (args) => searchSymbolsHandler(args),\n );\n\n const changedSymbolsHandler = withRecording('get_changed_symbols', handleGetChangedSymbols(storage, git, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_changed_symbols',\n {\n description: 'Get symbols in recently changed files based on git diff. Use this to see what was modified in recent commits. For full PR risk assessment (changed symbols + blast radius + co-changes), use get_pr_impact instead.',\n inputSchema: {\n since: z.string().optional().default('HEAD~1').describe('Git ref to diff against (default HEAD~1)'),\n maxFiles: z.number().int().min(1).optional().default(50).describe('Max changed files to process (default 50)'),\n },\n annotations: toolAnnotations,\n },\n (args) => changedSymbolsHandler(args),\n );\n\n const findImportersHandler = withRecording('find_importers', handleFindImporters(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'find_importers',\n {\n description: 'Find all symbols that import or depend on a given symbol (reverse dependency / \"who uses this?\"). Use this to check if a symbol is safe to modify or delete. Supports transitive traversal for deep impact chains. Use `intent` to filter by keyword (e.g., \"test\", \"core\"). For forward dependencies (what this symbol uses), use get_logic_slice. For aggregated impact with risk scores, use get_blast_radius.',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n edgeKinds: z.array(z.enum(['imports', 'calls', 'extends', 'implements', 'uses'])).optional().describe('Filter by edge kinds'),\n transitive: z.boolean().optional().default(false).describe('Follow transitive reverse edges (default false)'),\n maxDepth: z.number().int().min(1).max(10).optional().default(5).describe('Max BFS depth for transitive mode (default 5)'),\n intent: z.string().optional().describe('Filter importers by intent keywords (e.g., \"test\", \"core\", \"adapter\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => findImportersHandler(args),\n );\n\n const classHierarchyHandler = withRecording('get_class_hierarchy', handleGetClassHierarchy(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_class_hierarchy',\n {\n description: 'Get class inheritance hierarchy — ancestors (extends/implements chain) and descendants (subclasses). Use this when working with OOP code to understand type relationships before modifying a base class or interface. For dependency-based relationships (imports/calls), use get_logic_slice or find_importers.',\n inputSchema: {\n symbolId: z.string().min(1).optional().describe('Root symbol ID (omit for full project hierarchy)'),\n direction: z.enum(['ancestors', 'descendants', 'both']).optional().default('both').describe('Traversal direction (default both)'),\n },\n annotations: toolAnnotations,\n },\n (args) => classHierarchyHandler(args),\n );\n\n const symbolImportanceHandler = withRecording('get_symbol_importance', handleGetSymbolImportance(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_symbol_importance',\n {\n description: 'Rank symbols by structural importance using PageRank centrality on the dependency graph. Use this to identify the most critical symbols in the codebase — high-PageRank symbols are heavily depended upon and risky to change. For finding unused/unimportant code, use find_dead_code instead.',\n inputSchema: {\n limit: z.number().int().min(1).max(200).optional().default(25).describe('Max results (default 25)'),\n kind: z.enum(['function', 'class', 'interface', 'method', 'variable', 'type']).optional().describe('Filter by symbol kind'),\n filePattern: z.string().optional().describe('Filter by file path substring'),\n damping: z.number().min(0).max(1).optional().default(0.85).describe('PageRank damping factor (default 0.85)'),\n },\n annotations: toolAnnotations,\n },\n (args) => symbolImportanceHandler(args),\n );\n\n const prImpactHandler = withRecording('get_pr_impact', handleGetPrImpact(storage, git, masking, staleness, ctxoRoot, clusterLabelMasker), recorder);\n\n server.registerTool(\n 'get_pr_impact',\n {\n description: 'Analyze the full impact of a PR or recent changes in a SINGLE call — combines changed symbols + blast radius + co-change history into one risk assessment. Results include `_meta` with truncation info. Use this FIRST when reviewing a PR or evaluating recent commits. For single-symbol analysis, use get_blast_radius instead.',\n inputSchema: {\n since: z.string().optional().default('HEAD~1').describe('Git ref to diff against (default HEAD~1)'),\n maxFiles: z.number().int().min(1).optional().default(50).describe('Max changed files to analyze'),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional().describe('Filter impacted symbols by confidence tier'),\n },\n annotations: toolAnnotations,\n },\n (args) => prImpactHandler(args),\n );\n\n // Register empty resource/prompt capabilities to prevent -32601 errors\n // from MCP clients that unconditionally call listResources/listPrompts\n server.resource('ctxo-status', 'ctxo://status', async (uri) => ({\n contents: [{ uri: uri.href, text: 'Ctxo MCP server is running.' }],\n }));\n}\n\nmain().catch((err: unknown) => {\n log.error('Fatal: %s', (err as Error).message);\n process.exit(1);\n});\n","import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { randomUUID } from 'node:crypto';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:http');\n\n/**\n * Factory function that creates and registers all tools on a fresh McpServer.\n * Passed in from the composition root so this adapter doesn't import tool handlers.\n */\nexport type ServerFactory = () => Promise<McpServer>;\n\n/**\n * Starts ctxo MCP over HTTP. Each browser session gets its own McpServer + transport.\n * This avoids the \"already connected\" error when multiple clients connect.\n *\n * Usage:\n * CTXO_HTTP_PORT=3001 ctxo\n * ctxo --http\n */\nexport async function startHttpTransport(\n serverFactory: ServerFactory,\n port: number,\n): Promise<{ httpServer: Server }> {\n // Map of session ID -> { transport, server }\n const sessions = new Map<string, { transport: StreamableHTTPServerTransport; server: McpServer }>();\n\n const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // CORS\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Accept, Mcp-Session-Id');\n res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n // Existing session\n if (sessionId && sessions.has(sessionId)) {\n try {\n await sessions.get(sessionId)!.transport.handleRequest(req, res);\n } catch (err) {\n log.error('Request error: %s', (err as Error).message);\n if (!res.headersSent) { res.writeHead(500); res.end(); }\n }\n return;\n }\n\n // Invalid session\n if (sessionId && !sessions.has(sessionId)) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32001, message: 'Session not found' }, id: null }));\n return;\n }\n\n // New session — create fresh server + transport\n try {\n const server = await serverFactory();\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n });\n\n transport.onclose = () => {\n if (transport.sessionId) {\n sessions.delete(transport.sessionId);\n log.info('Session closed: %s', transport.sessionId);\n }\n };\n\n await server.connect(transport);\n await transport.handleRequest(req, res);\n\n if (transport.sessionId) {\n sessions.set(transport.sessionId, { transport, server });\n log.info('New session: %s (total: %d)', transport.sessionId, sessions.size);\n }\n } catch (err) {\n log.error('Session creation error: %s', (err as Error).message);\n if (!res.headersSent) { res.writeHead(500); res.end(); }\n }\n });\n\n return new Promise((resolve, reject) => {\n httpServer.on('error', reject);\n httpServer.listen(port, () => {\n log.info('MCP HTTP server listening on http://localhost:%d', port);\n process.stderr.write(`[ctxo] MCP HTTP server running at http://localhost:${port}\\n`);\n resolve({ httpServer });\n });\n });\n}\n","const DEFAULT_PATTERNS: Array<{ regex: RegExp; label: string }> = [\n // AWS Access Key IDs (AKIA...)\n { regex: /AKIA[0-9A-Z]{16}/g, label: 'AWS_KEY' },\n // AWS Secret Access Keys (40 char base64 containing / or +). The `[A-Za-z0-9=]*[/+]`\n // lookahead is scoped to the token itself (not `.*`, which would reach a later `/` in\n // the surrounding text, e.g. a file path in JSON, and wrongly redact hex-only git SHAs).\n { regex: /(?<![A-Za-z0-9/+])(?=[A-Za-z0-9/+=]{40}(?![A-Za-z0-9/+=]))(?=[A-Za-z0-9=]*[/+])[A-Za-z0-9/+=]{40}/g, label: 'AWS_SECRET' },\n // JWT tokens (eyJ...)\n { regex: /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, label: 'JWT' },\n // Private IPv4 (10.x.x.x, 172.16-31.x.x, 192.168.x.x)\n { regex: /\\b(?:10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|172\\.(?:1[6-9]|2\\d|3[01])\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3})\\b/g, label: 'PRIVATE_IP' },\n // Private IPv6 (fc00::/7)\n { regex: /\\b[fF][cCdD][0-9a-fA-F]{0,2}(?::[0-9a-fA-F]{1,4}){1,7}\\b/g, label: 'PRIVATE_IPV6' },\n // Environment variable assignments for secrets\n { regex: /(?:_SECRET|_KEY|_TOKEN|_PASSWORD)\\s*=\\s*['\"]?[^\\s'\"]{8,}['\"]?/g, label: 'ENV_SECRET' },\n // GCP service account keys\n { regex:/\"private_key\"\\s*:\\s*\"-----BEGIN[^\"]+\"/g, label: 'GCP_KEY' },\n // Azure connection strings\n { regex: /AccountKey=[A-Za-z0-9+/=]{20,}/g, label: 'AZURE_KEY' },\n];\n\nexport interface MaskingPatternConfig {\n readonly pattern: string;\n readonly flags?: string;\n readonly label: string;\n}\n\nexport class MaskingPipeline {\n private readonly patterns: Array<{ regex: RegExp; label: string }>;\n\n constructor(additionalPatterns: Array<{ regex: RegExp; label: string }> = []) {\n // Clone RegExp objects to avoid shared mutable lastIndex state\n this.patterns = [...DEFAULT_PATTERNS, ...additionalPatterns].map(({ regex, label }) => ({\n regex: new RegExp(regex.source, regex.flags),\n label,\n }));\n }\n\n static fromConfig(configPatterns: MaskingPatternConfig[]): MaskingPipeline {\n const additional: Array<{ regex: RegExp; label: string }> = [];\n\n for (const { pattern, flags, label } of configPatterns) {\n try {\n additional.push({ regex: new RegExp(pattern, flags ?? 'g'), label });\n } catch (err) {\n console.error(`[ctxo:masking] Invalid regex pattern \"${pattern}\": ${(err as Error).message}`);\n }\n }\n\n return new MaskingPipeline(additional);\n }\n\n mask(text: string): string {\n if (text.length === 0) return text;\n\n let result = text;\n for (const { regex, label } of this.patterns) {\n result = result.replace(regex, `[REDACTED:${label}]`);\n }\n return result;\n }\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { SymbolGraph } from '../../core/graph/symbol-graph.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport { LogicSliceQuery } from '../../core/logic-slice/logic-slice-query.js';\nimport { DetailFormatter } from '../../core/detail-levels/detail-formatter.js';\nimport { DetailLevelSchema } from '../../core/types.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1).optional(),\n symbolIds: z.array(z.string().min(1)).optional(),\n level: DetailLevelSchema.optional().default(3),\n intent: z.string().optional().describe('Filter dependencies by intent keywords (e.g., \"core\", \"adapter\", \"storage\")'),\n}).refine(\n (data) => data.symbolId || (data.symbolIds && data.symbolIds.length > 0),\n { message: 'Either symbolId or symbolIds must be provided' },\n);\n\nexport type GetLogicSliceInput = z.infer<typeof InputSchema>;\n\nexport function getLogicSliceInputSchema() {\n return {\n type: 'object' as const,\n properties: {\n symbolId: { type: 'string' as const, description: 'The symbol ID to retrieve (format: file::name::kind)' },\n level: { type: 'number' as const, enum: [1, 2, 3, 4], description: 'Detail level (1=minimal, 4=full)', default: 3 },\n },\n required: ['symbolId'] as const,\n };\n}\n\nexport function buildGraphFromStorage(storage: IStoragePort): SymbolGraph {\n const graph = new SymbolGraph();\n for (const sym of storage.getAllSymbols()) {\n graph.addNode(sym);\n }\n for (const edge of storage.getAllEdges()) {\n graph.addEdge(edge);\n }\n return graph;\n}\n\n/**\n * Build graph + return file list from a single JSON index read.\n * Returning both from one source prevents staleness warnings from diverging\n * from the graph data (see issue #40).\n */\nexport function buildGraphAndFilesFromJsonIndex(\n ctxoRoot: string,\n): { graph: SymbolGraph; indexedFiles: string[] } {\n const reader = new JsonIndexReader(ctxoRoot);\n const indices = reader.readAll();\n\n const graph = new SymbolGraph();\n const indexedFiles: string[] = [];\n\n for (const fileIndex of indices) {\n indexedFiles.push(fileIndex.file);\n for (const sym of fileIndex.symbols) {\n graph.addNode(sym);\n }\n }\n for (const fileIndex of indices) {\n for (const edge of fileIndex.edges) {\n graph.addEdge(edge);\n }\n }\n return { graph, indexedFiles };\n}\n\n/**\n * Build graph directly from JSON index files (bypasses SQLite cache).\n * Always reads fresh data from disk — works even if ctxo index ran in another process.\n */\nexport function buildGraphFromJsonIndex(ctxoRoot: string): SymbolGraph {\n return buildGraphAndFilesFromJsonIndex(ctxoRoot).graph;\n}\n\n/**\n * Returns graph + indexedFiles from JSON index, falling back to SQLite storage\n * when JSON is empty. Keeps graph data and staleness check file list aligned.\n */\nexport function getGraphAndFiles(\n ctxoRoot: string,\n storage: IStoragePort,\n): { graph: SymbolGraph; indexedFiles: string[] } {\n const json = buildGraphAndFilesFromJsonIndex(ctxoRoot);\n if (json.graph.nodeCount > 0) return json;\n return { graph: buildGraphFromStorage(storage), indexedFiles: storage.listIndexedFiles() };\n}\n\nexport interface StalenessCheck {\n check(indexedFiles: readonly string[]): { message: string } | undefined;\n}\n\nexport function handleGetLogicSlice(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const query = new LogicSliceQuery();\n const formatter = new DetailFormatter();\n\n // Build graph fresh on each call — try JSON index first (always up to date),\n // fall back to SQLite storage (for tests and when JSON not available).\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n const handler = (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, symbolIds, level, intent } = parsed.data;\n const ids = symbolIds ?? (symbolId ? [symbolId] : []);\n\n const { graph, indexedFiles } = getGraph();\n\n // Batch support: query multiple symbols\n const results = [];\n for (const id of ids) {\n const slice = query.getLogicSlice(graph, id);\n if (slice) {\n const formatted = formatter.format(slice, level) as unknown as Record<string, unknown>;\n // Apply intent filter to dependencies if present\n if (intent && Array.isArray(formatted['dependencies'])) {\n formatted['dependencies'] = filterByIntent(\n formatted['dependencies'] as Record<string, unknown>[],\n intent,\n );\n }\n results.push(formatted);\n } else {\n results.push({ found: false, symbolId: id, hint: 'Symbol not found. Run \"ctxo index\".' });\n }\n }\n\n // Single symbol: return directly. Batch: return array.\n const responseData = ids.length === 1 ? results[0] : { batch: true, results };\n\n // Apply masking\n const payload = masking.mask(JSON.stringify(wrapResponse(responseData as Record<string, unknown>)));\n\n // Check staleness\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) {\n content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n\n return handler;\n}\n","import type { SymbolNode, GraphEdge, LogicSliceResult } from '../types.js';\nimport type { SymbolGraph } from '../graph/symbol-graph.js';\n\nexport class LogicSliceQuery {\n getLogicSlice(\n graph: SymbolGraph,\n symbolId: string,\n maxDepth: number = Infinity,\n ): LogicSliceResult | undefined {\n const root = graph.getNode(symbolId);\n if (!root) return undefined;\n\n const visited = new Set<string>([symbolId]);\n const dependencies: SymbolNode[] = [];\n const collectedEdges: GraphEdge[] = [];\n\n // BFS transitive closure\n const queue: Array<{ id: string; depth: number }> = [{ id: symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (current.depth >= maxDepth) continue;\n\n const edges = graph.getForwardEdges(current.id);\n for (const edge of edges) {\n const depNode = graph.getNode(edge.to);\n // Only include edges whose target node exists in the graph\n if (!depNode) continue;\n\n collectedEdges.push(edge);\n\n if (visited.has(edge.to)) continue;\n visited.add(edge.to);\n\n dependencies.push(depNode);\n queue.push({ id: edge.to, depth: current.depth + 1 });\n }\n }\n\n return { root, dependencies, edges: collectedEdges };\n }\n}\n","import type {\n LogicSliceResult,\n FormattedSlice,\n DetailLevel,\n SymbolNode,\n GraphEdge,\n} from '../types.js';\n\nconst L1_MAX_LINES = 150;\nconst L4_TOKEN_BUDGET = 8000;\n\nexport class DetailFormatter {\n format(slice: LogicSliceResult, level: DetailLevel): FormattedSlice {\n switch (level) {\n case 1:\n return this.formatL1(slice);\n case 2:\n return this.formatL2(slice);\n case 3:\n return this.formatL3(slice);\n case 4:\n return this.formatL4(slice);\n }\n }\n\n private formatL1(slice: LogicSliceResult): FormattedSlice {\n const root = slice.root;\n const lineCount = root.endLine - root.startLine + 1;\n\n const clampedRoot: SymbolNode = lineCount > L1_MAX_LINES\n ? { ...root, endLine: root.startLine + L1_MAX_LINES - 1 }\n : root;\n\n return {\n root: clampedRoot,\n dependencies: [],\n edges: [],\n level: 1,\n levelDescription: 'L1: Root symbol signature only (no dependencies)',\n ...(lineCount > L1_MAX_LINES ? { truncation: { truncated: true as const, reason: 'token_budget_exceeded' as const } } : {}),\n };\n }\n\n private formatL2(slice: LogicSliceResult): FormattedSlice {\n // L2: root + depth-1 direct dependencies\n const directEdges = slice.edges.filter((e) => e.from === slice.root.symbolId);\n const directDepIds = new Set(directEdges.map((e) => e.to));\n const directDeps = slice.dependencies.filter((d) => directDepIds.has(d.symbolId));\n\n return {\n root: slice.root,\n dependencies: directDeps,\n edges: directEdges,\n level: 2,\n levelDescription: 'L2: Root + direct dependencies (depth 1)',\n };\n }\n\n private formatL3(slice: LogicSliceResult): FormattedSlice {\n return {\n root: slice.root,\n dependencies: slice.dependencies,\n edges: slice.edges,\n level: 3,\n levelDescription: 'L3: Full transitive dependency closure',\n };\n }\n\n private formatL4(slice: LogicSliceResult): FormattedSlice {\n // L4: full closure with token budget enforcement\n const estimatedTokens = this.estimateTokens(slice);\n\n if (estimatedTokens > L4_TOKEN_BUDGET) {\n // Truncate dependencies to fit budget\n const truncated = this.truncateToTokenBudget(slice, L4_TOKEN_BUDGET);\n return {\n ...truncated,\n level: 4,\n levelDescription: 'L4: Full closure with 8K token budget (truncated)',\n truncation: {\n truncated: true,\n reason: 'token_budget_exceeded',\n },\n };\n }\n\n return {\n root: slice.root,\n dependencies: slice.dependencies,\n edges: slice.edges,\n level: 4,\n levelDescription: 'L4: Full closure with 8K token budget',\n };\n }\n\n private estimateTokens(slice: LogicSliceResult): number {\n // Rough estimate: ~4 chars per token\n let chars = this.symbolCharEstimate(slice.root);\n for (const dep of slice.dependencies) {\n chars += this.symbolCharEstimate(dep);\n }\n for (const edge of slice.edges) {\n chars += (edge.from.length + edge.to.length + edge.kind.length + 10);\n }\n return Math.ceil(chars / 4);\n }\n\n private symbolCharEstimate(node: SymbolNode): number {\n // Use byte offsets for exact size when available, otherwise estimate from line count\n if (node.startOffset !== undefined && node.endOffset !== undefined) {\n return node.endOffset - node.startOffset;\n }\n return (node.endLine - node.startLine + 1) * 40;\n }\n\n private truncateToTokenBudget(\n slice: LogicSliceResult,\n budget: number,\n ): { root: SymbolNode; dependencies: SymbolNode[]; edges: GraphEdge[] } {\n let currentTokens = this.symbolCharEstimate(slice.root) / 4;\n const deps: SymbolNode[] = [];\n const includedIds = new Set<string>([slice.root.symbolId]);\n\n for (const dep of slice.dependencies) {\n const depTokens = this.symbolCharEstimate(dep) / 4;\n if (currentTokens + depTokens > budget) break;\n currentTokens += depTokens;\n deps.push(dep);\n includedIds.add(dep.symbolId);\n }\n\n const edges = slice.edges.filter(\n (e) => includedIds.has(e.from) && includedIds.has(e.to),\n );\n\n return { root: slice.root, dependencies: deps, edges };\n }\n}\n","/**\n * Response envelope — adds _meta field and truncates large payloads.\n *\n * Applied between payload construction and masking in every MCP handler.\n * Threshold default: 8192 bytes (~8KB). Configurable via CTXO_RESPONSE_LIMIT env.\n */\n\nconst DEFAULT_THRESHOLD = 8192;\n\nexport interface ResponseMeta {\n totalItems: number;\n returnedItems: number;\n truncated: boolean;\n totalBytes: number;\n hint?: string;\n /**\n * Workspace descriptor (forward-compat for Monorepo Workspace PRD).\n * v0.7: `root` is set at startup; `package` is always undefined.\n */\n workspace?: {\n root: string;\n package?: string;\n };\n /**\n * How far the community snapshot has drifted from current git HEAD.\n * Only populated on cluster-aware MCP tools (overlay, blast-radius,\n * why-context, pr-impact). Tells AI agents whether the cluster / drift /\n * boundary data they are about to act on is fresh or N commits stale.\n */\n snapshotStaleness?: {\n snapshotCommit: string;\n currentHeadCommit?: string;\n commitsBehind?: number;\n hint?: string;\n };\n}\n\nlet workspaceMeta: ResponseMeta['workspace'] | undefined;\n\n/**\n * Set the workspace descriptor attached to every subsequent `_meta` envelope.\n * Called once at composition root after `detectWorkspace()` resolves.\n */\nexport function setWorkspaceMeta(workspace: ResponseMeta['workspace']): void {\n workspaceMeta = workspace;\n}\n\nexport function getWorkspaceMeta(): ResponseMeta['workspace'] | undefined {\n return workspaceMeta;\n}\n\n/**\n * Known array field names in tool responses that can be truncated.\n * Maps field name → drill-in hint for the LLM.\n */\nconst TRUNCATABLE_FIELDS: Record<string, string> = {\n impactedSymbols: 'Use search_symbols to find specific impacted symbols, or get_blast_radius with a confidence filter.',\n importers: 'Use find_importers with edgeKinds filter or maxDepth to narrow results.',\n deadSymbols: 'Use search_symbols with kind filter to find specific dead symbols.',\n unusedExports: 'Use find_importers to check specific symbols.',\n results: 'Use search_symbols or get_ranked_context with a narrower query.',\n rankings: 'Use get_symbol_importance with kind or filePattern filter to narrow results.',\n context: 'Use get_context_for_task with a smaller tokenBudget.',\n hierarchies: 'Use get_class_hierarchy with a specific symbolId.',\n scaffolding: 'Review the scaffolding markers directly in the listed files.',\n deadFiles: 'Use find_dead_code with includeTests to adjust scope.',\n files: 'Use get_changed_symbols with a narrower since ref or smaller maxFiles.',\n clusters: 'Use get_architectural_overlay with mode=communities and layer filter for a focused view.',\n clustersAffected: 'Use get_blast_radius on individual changed symbols for per-cluster impact.',\n};\n\nfunction getThreshold(): number {\n const env = process.env['CTXO_RESPONSE_LIMIT'];\n if (env) {\n const n = parseInt(env, 10);\n if (!isNaN(n) && n > 0) return n;\n }\n return DEFAULT_THRESHOLD;\n}\n\n/**\n * Find the largest array field in a payload object that is in our truncatable list.\n */\nfunction findTruncatableArray(data: Record<string, unknown>): { key: string; arr: unknown[] } | null {\n let best: { key: string; arr: unknown[] } | null = null;\n for (const [key, value] of Object.entries(data)) {\n if (Array.isArray(value) && key in TRUNCATABLE_FIELDS) {\n if (!best || value.length > best.arr.length) {\n best = { key, arr: value };\n }\n }\n }\n return best;\n}\n\n/**\n * Wrap a payload object with _meta and truncate if needed.\n *\n * @param data - The raw response object (before JSON.stringify)\n * @returns The data object with _meta added (and arrays truncated if over threshold)\n */\nexport interface WrapResponseExtras {\n snapshotStaleness?: ResponseMeta['snapshotStaleness'];\n}\n\nfunction buildMeta(\n base: Omit<ResponseMeta, 'workspace' | 'snapshotStaleness'>,\n extras?: WrapResponseExtras,\n): ResponseMeta {\n const meta: ResponseMeta = workspaceMeta ? { ...base, workspace: workspaceMeta } : base;\n if (extras?.snapshotStaleness) {\n return { ...meta, snapshotStaleness: extras.snapshotStaleness };\n }\n return meta;\n}\n\nexport function wrapResponse(\n data: Record<string, unknown>,\n extras?: WrapResponseExtras,\n): Record<string, unknown> {\n const threshold = getThreshold();\n const fullJson = JSON.stringify(data);\n const totalBytes = Buffer.byteLength(fullJson, 'utf-8');\n\n const truncatable = findTruncatableArray(data);\n const totalItems = truncatable ? truncatable.arr.length : 0;\n\n // Under threshold — return with _meta, no truncation\n if (totalBytes <= threshold) {\n return {\n ...data,\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: totalItems,\n truncated: false,\n totalBytes,\n },\n extras,\n ),\n };\n }\n\n // Over threshold — truncate the largest array\n if (!truncatable || truncatable.arr.length <= 1) {\n return {\n ...data,\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: totalItems,\n truncated: false,\n totalBytes,\n },\n extras,\n ),\n };\n }\n\n // Binary search for the number of items that fit within threshold\n let lo = 1;\n let hi = truncatable.arr.length;\n\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n const trial = {\n ...data,\n [truncatable.key]: truncatable.arr.slice(0, mid),\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: mid,\n truncated: true,\n totalBytes,\n hint: TRUNCATABLE_FIELDS[truncatable.key],\n },\n extras,\n ),\n };\n const size = Buffer.byteLength(JSON.stringify(trial), 'utf-8');\n if (size <= threshold) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n\n const hint = TRUNCATABLE_FIELDS[truncatable.key];\n\n return {\n ...data,\n [truncatable.key]: truncatable.arr.slice(0, lo),\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: lo,\n truncated: true,\n totalBytes,\n hint,\n },\n extras,\n ),\n };\n}\n","/**\n * Intent-based result filtering for MCP tool responses.\n *\n * When an `intent` string is provided, filters array results to only include\n * entries whose text fields (symbolId, file, name, kind, edgeKind, reason)\n * match at least one keyword from the intent.\n *\n * Keywords are extracted by splitting the intent on whitespace and lowercasing.\n * A match is found when any keyword appears as a substring of any text field.\n */\n\n/**\n * Extract lowercase keywords from an intent string.\n * Filters out very short words (< 2 chars) to avoid noise.\n */\nfunction extractKeywords(intent: string): string[] {\n return intent\n .toLowerCase()\n .split(/\\s+/)\n .filter(w => w.length >= 2);\n}\n\n/**\n * Check if any keyword matches any of the given text values (case-insensitive substring).\n */\nfunction matchesAny(keywords: string[], values: string[]): boolean {\n const lower = values.map(v => v.toLowerCase());\n return keywords.some(kw => lower.some(v => v.includes(kw)));\n}\n\n/**\n * Collect all string values from a record for matching.\n * Extracts from known fields: symbolId, file, name, kind, edgeKind, edgeKinds, reason, confidence.\n */\nfunction collectMatchableStrings(entry: Record<string, unknown>): string[] {\n const strings: string[] = [];\n for (const key of ['symbolId', 'file', 'name', 'kind', 'edgeKind', 'reason', 'confidence']) {\n const v = entry[key];\n if (typeof v === 'string') strings.push(v);\n }\n const edgeKinds = entry['edgeKinds'];\n if (Array.isArray(edgeKinds)) {\n for (const ek of edgeKinds) {\n if (typeof ek === 'string') strings.push(ek);\n }\n }\n return strings;\n}\n\n/**\n * Filter an array of result entries by intent keywords.\n * Returns only entries where at least one keyword matches a text field.\n *\n * @param items - Array of result objects\n * @param intent - User intent string (e.g., \"test coverage\", \"adapter\", \"security\")\n * @returns Filtered array (empty if no matches)\n */\nexport function filterByIntent<T extends Record<string, unknown>>(\n items: T[],\n intent: string | undefined,\n): T[] {\n if (!intent || intent.trim().length === 0) return items;\n\n const keywords = extractKeywords(intent);\n if (keywords.length === 0) return items;\n\n return items.filter(item => {\n const strings = collectMatchableStrings(item);\n return matchesAny(keywords, strings);\n });\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { RevertDetector } from '../../core/why-context/revert-detector.js';\nimport { DriftDetector } from '../../core/overlay/drift-detector.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport type { AntiPattern, CommitIntent } from '../../core/types.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n maxCommits: z.number().int().min(1).optional(),\n});\n\nexport function handleGetWhyContext(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n clusterLabelMasker?: ClusterLabelMasker,\n) {\n const revertDetector = new RevertDetector();\n const indexReader = new JsonIndexReader(ctxoRoot);\n const driftDetector = new DriftDetector();\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, maxCommits } = parsed.data;\n\n const symbol = storage.getSymbolById(symbolId);\n if (!symbol) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n const filePath = symbolId.split('::')[0]!;\n\n // Try committed index first (FR16: anti-patterns persist in committed index)\n const indices = indexReader.readAll();\n const fileIndex = indices.find((i) => i.file === filePath);\n\n let commitHistory: CommitIntent[];\n let antiPatterns: AntiPattern[];\n\n if (fileIndex && fileIndex.intent.length > 0) {\n commitHistory = fileIndex.intent;\n antiPatterns = fileIndex.antiPatterns;\n } else {\n // Fallback: compute on-demand from git\n const commits = await git.getCommitHistory(filePath);\n commitHistory = commits.map((c) => ({\n hash: c.hash,\n message: c.message,\n date: c.date,\n kind: 'commit' as const,\n }));\n antiPatterns = revertDetector.detect(commits);\n }\n\n // Query-time limit: slice to maxCommits if provided\n const limitedHistory = maxCommits ? commitHistory.slice(0, maxCommits) : commitHistory;\n\n // Assemble result — separation of concerns: no changeIntelligence here\n // Use get_change_intelligence tool for complexity/churn scoring\n const responsePayload: Record<string, unknown> = {\n commitHistory: limitedHistory,\n antiPatternWarnings: antiPatterns,\n };\n\n if (antiPatterns.length > 0) {\n responsePayload.warningBadge = '⚠ Anti-pattern detected';\n }\n\n const rawSnapshot = storage.readCommunities();\n const currentSnapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n if (currentSnapshot) {\n const history = storage.listCommunityHistory();\n const drift = driftDetector.detect(currentSnapshot, history);\n const relevant = drift.events.filter((event) => event.symbolId === symbolId);\n if (relevant.length > 0 || drift.hint) {\n responsePayload.driftSignals = {\n events: relevant,\n confidence: drift.confidence,\n snapshotsAvailable: drift.snapshotsAvailable,\n stabilitySummary: drift.stability,\n ...(drift.hint ? { hint: drift.hint } : {}),\n };\n }\n }\n\n const stalenessMeta = await buildSnapshotStaleness(storage, git);\n if (stalenessMeta) {\n responsePayload._meta = { snapshotStaleness: stalenessMeta };\n }\n\n const payload = masking.mask(JSON.stringify(responsePayload));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(storage.listIndexedFiles());\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import type { CommunitySnapshot } from '../types.js';\n\nexport interface SnapshotStaleness {\n readonly snapshotCommit: string;\n readonly currentHeadCommit?: string;\n readonly commitsBehind?: number;\n readonly hint?: string;\n}\n\n/**\n * Narrow structural views of the capabilities this module needs from the\n * storage and git adapters. We declare them here (in core/) rather than\n * importing the full IStoragePort / IGitPort interfaces from ports/ so the\n * hexagonal boundary holds: core/ knows nothing about ports/. Callers pass\n * their existing port-typed adapters — they satisfy these shapes structurally.\n */\nexport interface SnapshotStalenessStorage {\n readCommunities(): CommunitySnapshot | undefined;\n}\n\nexport interface SnapshotStalenessGit {\n getHeadSha?(): Promise<string | undefined> | string | undefined;\n countCommitsBetween?(sha: string): Promise<number | undefined> | number | undefined;\n}\n\n/**\n * Summarise how far the persisted community snapshot has drifted from the\n * current git HEAD. Callers merge this into MCP `_meta` so AI agents know\n * whether cluster / drift / boundary data reflects the HEAD they are about\n * to edit or a stale history point.\n *\n * Returns undefined when no snapshot exists (nothing to report) or when git\n * is unavailable and no comparison is possible.\n */\nexport async function buildSnapshotStaleness(\n storage: SnapshotStalenessStorage,\n git: SnapshotStalenessGit,\n): Promise<SnapshotStaleness | undefined> {\n const snapshot = storage.readCommunities();\n if (!snapshot || !snapshot.commitSha || snapshot.commitSha === 'nocommit') return undefined;\n\n const getHead = git.getHeadSha?.bind(git);\n const countBetween = git.countCommitsBetween?.bind(git);\n const head = getHead ? await getHead() : undefined;\n\n if (!head) {\n // Git unavailable — still report the snapshot anchor so callers know what\n // commit the snapshot was computed against.\n return { snapshotCommit: snapshot.commitSha };\n }\n\n if (head === snapshot.commitSha) {\n return {\n snapshotCommit: snapshot.commitSha,\n currentHeadCommit: head,\n commitsBehind: 0,\n };\n }\n\n const behind = countBetween ? await countBetween(snapshot.commitSha) : undefined;\n\n const result: SnapshotStaleness = {\n snapshotCommit: snapshot.commitSha,\n currentHeadCommit: head,\n ...(typeof behind === 'number' ? { commitsBehind: behind } : {}),\n };\n\n if (typeof behind === 'number' && behind > 0) {\n return {\n ...result,\n hint: `Community snapshot is ${behind} commit${behind === 1 ? '' : 's'} behind HEAD. Run \\`ctxo index\\` to refresh cluster / drift / boundary data.`,\n };\n }\n if (behind === undefined) {\n // HEAD resolved but snapshot sha unknown to git (force-push, rebased, etc.).\n return {\n ...result,\n hint: 'Snapshot commit is not reachable from HEAD — it may have been rebased away. Run `ctxo index` to re-anchor.',\n };\n }\n return result;\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ChurnAnalyzer } from '../../core/change-intelligence/churn-analyzer.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { HealthScorer } from '../../core/change-intelligence/health-scorer.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n});\n\nexport function handleGetChangeIntelligence(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const churnAnalyzer = new ChurnAnalyzer();\n const healthScorer = new HealthScorer();\n const indexReader = new JsonIndexReader(ctxoRoot);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId } = parsed.data;\n\n const symbol = storage.getSymbolById(symbolId);\n if (!symbol) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n const filePath = symbolId.split('::')[0]!;\n const normalizePath = (p: string) => p.replace(/\\\\/g, '/');\n\n // Get churn data for all files in parallel\n const allFiles = storage.listIndexedFiles();\n const churnResults = await Promise.all(allFiles.map((f) => git.getFileChurn(f)));\n\n const maxChurn = Math.max(1, ...churnResults.map((c) => c.commitCount));\n const targetChurn = churnResults.find((c) => normalizePath(c.filePath) === normalizePath(filePath));\n const commitCount = targetChurn?.commitCount ?? 0;\n\n const normalizedChurn = churnAnalyzer.normalize(commitCount, maxChurn);\n\n // Read cyclomatic complexity from committed index\n const indices = indexReader.readAll();\n const fileIndex = indices.find((i) => i.file === filePath);\n const complexityEntries = fileIndex?.complexity ?? [];\n\n // Storage knows the symbol but its JSON index is missing on disk — SQLite and\n // `.ctxo/index/` have drifted. Surface it so the caller does not mistake the\n // fallback `complexity: 0` score for a real \"low risk\" verdict.\n const indexMissingWarning = !fileIndex\n ? `JSON index for ${filePath} not found on disk — complexity falls back to 0. Run \"ctxo index\" to resync.`\n : undefined;\n\n // Exact match first, then aggregate (max of methods for class-level queries)\n let cyclomatic = complexityEntries.find((c) => c.symbolId === symbolId)?.cyclomatic;\n if (cyclomatic === undefined) {\n // For class/file: use max complexity of all methods in this file\n const symbolName = symbolId.split('::')[1] ?? '';\n const relatedEntries = complexityEntries.filter((c) =>\n c.symbolId.startsWith(`${filePath}::${symbolName}.`),\n );\n cyclomatic = relatedEntries.length > 0\n ? Math.max(...relatedEntries.map((c) => c.cyclomatic))\n : 1;\n }\n // Normalize: cyclomatic 1=0, 10+=1.0\n const normalizedComplexity = Math.min((cyclomatic - 1) / 9, 1);\n\n const score = healthScorer.score(symbolId, normalizedComplexity, normalizedChurn);\n\n const payload = masking.mask(JSON.stringify(score));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(storage.listIndexedFiles());\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n if (indexMissingWarning) {\n content.push({ type: 'text', text: `⚠️ ${indexMissingWarning}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","export class ChurnAnalyzer {\n normalize(commitCount: number, maxCommitCount: number): number {\n if (maxCommitCount <= 0) return 0;\n if (commitCount < 0) {\n throw new Error(`Invalid commit count: ${commitCount}`);\n }\n return Math.min(commitCount / maxCommitCount, 1);\n }\n}\n","import type { ChangeIntelligenceScore, ScoreBand } from '../types.js';\n\nexport class HealthScorer {\n score(symbolId: string, complexity: number, churn: number): ChangeIntelligenceScore {\n const composite = complexity * churn;\n const band = this.toBand(composite);\n\n return { symbolId, complexity, churn, composite, band };\n }\n\n private toBand(composite: number): ScoreBand {\n if (composite < 0.3) return 'low';\n if (composite < 0.7) return 'medium';\n return 'high';\n }\n}\n","import { z } from 'zod';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { BlastRadiusCalculator } from '../../core/blast-radius/blast-radius-calculator.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\nimport type { CommunitySnapshot } from '../../core/types.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\n\ninterface ClusterBreakdown {\n readonly byCluster: Record<string, number>;\n readonly crossClusterCount: number;\n readonly multiClusterHint?: string;\n}\n\nfunction computeClusterBreakdown(\n snapshot: CommunitySnapshot,\n rootSymbolId: string,\n impactedSymbolIds: readonly string[],\n): ClusterBreakdown | undefined {\n if (impactedSymbolIds.length === 0) return undefined;\n const assignment = new Map<string, { id: number; label: string }>();\n for (const entry of snapshot.communities) {\n assignment.set(entry.symbolId, { id: entry.communityId, label: entry.communityLabel });\n }\n const rootCluster = assignment.get(rootSymbolId);\n const byCluster: Record<string, number> = {};\n let crossClusterCount = 0;\n for (const symbolId of impactedSymbolIds) {\n const cluster = assignment.get(symbolId);\n if (!cluster) continue;\n const label = cluster.label || `community-${cluster.id}`;\n byCluster[label] = (byCluster[label] ?? 0) + 1;\n if (rootCluster && cluster.id !== rootCluster.id) crossClusterCount++;\n }\n const clusterCount = Object.keys(byCluster).length;\n const multiClusterHint =\n clusterCount >= 3\n ? `Change impacts ${clusterCount} clusters — multi-team review recommended.`\n : undefined;\n return { byCluster, crossClusterCount, multiClusterHint };\n}\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional(),\n intent: z.string().optional().describe('Filter results by intent keywords (e.g., \"test\", \"adapter\", \"security\")'),\n});\n\nexport function handleGetBlastRadius(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n git?: IGitPort,\n clusterLabelMasker?: ClusterLabelMasker,\n) {\n const calculator = new BlastRadiusCalculator();\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n if (!graph.hasNode(symbolId)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n const result = calculator.calculate(graph, symbolId);\n\n // Apply confidence filter if requested\n let symbols = result.impactedSymbols;\n if (parsed.data.confidence) {\n symbols = symbols.filter(s => s.confidence === parsed.data.confidence);\n }\n\n // Apply intent filter if requested\n symbols = filterByIntent(symbols as unknown as Record<string, unknown>[], parsed.data.intent) as unknown as typeof symbols;\n\n const confirmedCount = symbols.filter(s => s.confidence === 'confirmed').length;\n const likelyCount = symbols.filter(s => s.confidence === 'likely').length;\n const potentialCount = symbols.filter(s => s.confidence === 'potential').length;\n\n const rawSnapshot = storage.readCommunities();\n const snapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n const clusterBreakdown = snapshot\n ? computeClusterBreakdown(snapshot, symbolId, symbols.map((s) => s.symbolId))\n : undefined;\n\n const body: Record<string, unknown> = {\n symbolId,\n impactScore: symbols.length,\n directDependentsCount: result.directDependentsCount,\n confirmedCount,\n likelyCount,\n potentialCount,\n overallRiskScore: result.overallRiskScore,\n impactedSymbols: symbols,\n };\n if (clusterBreakdown) {\n body.byCluster = clusterBreakdown.byCluster;\n body.crossClusterEdges = clusterBreakdown.crossClusterCount;\n if (clusterBreakdown.multiClusterHint) {\n body.multiClusterHint = clusterBreakdown.multiClusterHint;\n }\n }\n\n const stalenessMeta = git ? await buildSnapshotStaleness(storage, git) : undefined;\n const extras = stalenessMeta ? { snapshotStaleness: stalenessMeta } : undefined;\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ArchitecturalOverlay } from '../../core/overlay/architectural-overlay.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport type { CommunitySnapshot, GodNode } from '../../core/types.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nconst OVERLAY_MODES = ['regex', 'communities', 'both'] as const;\n\nconst InputSchema = z.object({\n layer: z.string().optional(),\n mode: z.enum(OVERLAY_MODES).optional(),\n});\n\nexport interface CommunityOverlayCluster {\n id: number;\n label: string;\n memberCount: number;\n members: string[];\n godNodes: string[];\n}\n\nexport interface CommunityOverlay {\n clusters: CommunityOverlayCluster[];\n modularity: number;\n edgeQuality: CommunitySnapshot['edgeQuality'];\n crossClusterEdges: number;\n computedAt: string;\n commitSha: string;\n}\n\nexport function buildCommunityOverlay(\n snapshot: CommunitySnapshot,\n options: { memberPreviewLimit?: number } = {},\n): CommunityOverlay {\n const previewLimit = options.memberPreviewLimit ?? 15;\n const byCluster = new Map<number, string[]>();\n for (const entry of snapshot.communities) {\n const list = byCluster.get(entry.communityId) ?? [];\n list.push(entry.symbolId);\n byCluster.set(entry.communityId, list);\n }\n const labelFor = new Map<number, string>();\n for (const entry of snapshot.communities) {\n if (!labelFor.has(entry.communityId)) {\n labelFor.set(entry.communityId, entry.communityLabel);\n }\n }\n const godNodesByCommunity = new Map<number, Set<string>>();\n for (const god of snapshot.godNodes as readonly GodNode[]) {\n for (const communityId of god.bridgedCommunities) {\n const set = godNodesByCommunity.get(communityId) ?? new Set<string>();\n set.add(god.symbolId);\n godNodesByCommunity.set(communityId, set);\n }\n }\n\n const clusters: CommunityOverlayCluster[] = [];\n for (const [id, members] of byCluster) {\n members.sort();\n clusters.push({\n id,\n label: labelFor.get(id) ?? `community-${id}`,\n memberCount: members.length,\n members: members.slice(0, previewLimit),\n godNodes: [...(godNodesByCommunity.get(id) ?? [])].sort(),\n });\n }\n clusters.sort((a, b) => b.memberCount - a.memberCount || a.id - b.id);\n\n return {\n clusters,\n modularity: snapshot.modularity,\n edgeQuality: snapshot.edgeQuality,\n crossClusterEdges: snapshot.crossClusterEdges,\n computedAt: snapshot.computedAt,\n commitSha: snapshot.commitSha,\n };\n}\n\nexport function handleGetArchitecturalOverlay(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n git?: IGitPort,\n clusterLabelMasker?: ClusterLabelMasker,\n) {\n const overlay = new ArchitecturalOverlay();\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const files = storage.listIndexedFiles();\n const result = overlay.classify(files);\n const mode = parsed.data.mode ?? 'both';\n\n const buildContent = (payloadStr: string) => {\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(files);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payloadStr });\n return content;\n };\n\n const stalenessMeta = git ? await buildSnapshotStaleness(storage, git) : undefined;\n const extras = stalenessMeta ? { snapshotStaleness: stalenessMeta } : undefined;\n\n // Filter by layer if specified (regex-mode semantics always apply here)\n if (parsed.data.layer) {\n const filtered = result.layers[parsed.data.layer];\n const payload = masking.mask(\n JSON.stringify(wrapResponse({ layer: parsed.data.layer, files: filtered ?? [] }, extras)),\n );\n return { content: buildContent(payload) };\n }\n\n const rawSnapshot = mode === 'regex' ? undefined : storage.readCommunities();\n const snapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n const community = snapshot ? buildCommunityOverlay(snapshot) : undefined;\n\n const body: Record<string, unknown> = {};\n if (mode !== 'communities') body.layers = result.layers;\n if (community) body.communities = community;\n if (mode === 'communities' && !community) {\n body.hint = 'No community snapshot available. Run `ctxo index` to generate one.';\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n return { content: buildContent(payload) };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { DeadCodeDetector } from '../../core/dead-code/dead-code-detector.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nconst InputSchema = z.object({\n includeTests: z.boolean().optional().default(false),\n intent: z.string().optional().describe('Filter dead code results by intent keywords (e.g., \"adapter\", \"core\", \"function\")'),\n});\n\nexport function handleFindDeadCode(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const detector = new DeadCodeDetector();\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { graph, indexedFiles } = getGraph();\n const result = detector.detect(graph, { includeTests: parsed.data.includeTests });\n\n // Apply intent filter to deadSymbols if requested\n const filtered = {\n ...result,\n deadSymbols: filterByIntent(result.deadSymbols as unknown as Record<string, unknown>[], parsed.data.intent) as unknown as typeof result.deadSymbols,\n };\n\n const payload = masking.mask(JSON.stringify(wrapResponse(filtered as unknown as Record<string, unknown>)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ContextAssembler } from '../../core/context-assembly/context-assembler.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\n\nconst TaskTypeSchema = z.enum(['fix', 'extend', 'refactor', 'understand']);\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n taskType: TaskTypeSchema,\n tokenBudget: z.number().min(100).optional().default(4000),\n});\n\nexport function handleGetContextForTask(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const assembler = new ContextAssembler();\n const indexReader = new JsonIndexReader(ctxoRoot);\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, taskType, tokenBudget } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n const indices = indexReader.readAll();\n\n const result = assembler.assembleForTask(graph, symbolId, taskType, indices, tokenBudget);\n\n if (!result) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\".' }) }],\n };\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse(result as unknown as Record<string, unknown>)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","export type TaskType = 'fix' | 'extend' | 'refactor' | 'understand';\n\nexport interface ScoringWeights {\n readonly directDependency: number;\n readonly interfaceTypeDef: number;\n readonly blastRadiusDependent: number;\n readonly highComplexity: number;\n readonly antiPatternHistory: number;\n}\n\nconst TASK_WEIGHTS: Record<TaskType, ScoringWeights> = {\n fix: {\n directDependency: 0.3,\n interfaceTypeDef: 0.1,\n blastRadiusDependent: 0.1,\n highComplexity: 0.2,\n antiPatternHistory: 0.3,\n },\n extend: {\n directDependency: 0.4,\n interfaceTypeDef: 0.5,\n blastRadiusDependent: 0.1,\n highComplexity: 0.0,\n antiPatternHistory: 0.0,\n },\n refactor: {\n directDependency: 0.2,\n interfaceTypeDef: 0.1,\n blastRadiusDependent: 0.5,\n highComplexity: 0.1,\n antiPatternHistory: 0.1,\n },\n understand: {\n directDependency: 0.5,\n interfaceTypeDef: 0.3,\n blastRadiusDependent: 0.1,\n highComplexity: 0.1,\n antiPatternHistory: 0.0,\n },\n};\n\nexport function getWeightsForTask(taskType: TaskType): ScoringWeights {\n return TASK_WEIGHTS[taskType];\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport type { SymbolNode, SymbolKind, FileIndex } from '../types.js';\nimport { LogicSliceQuery } from '../logic-slice/logic-slice-query.js';\nimport { BlastRadiusCalculator } from '../blast-radius/blast-radius-calculator.js';\nimport { type TaskType, type ScoringWeights, getWeightsForTask } from './task-context-strategy.js';\n\nexport interface ContextEntry {\n readonly symbolId: string;\n readonly name: string;\n readonly kind: SymbolKind;\n readonly file: string;\n readonly relevanceScore: number;\n readonly reason: string;\n readonly lines: number;\n readonly tokens: number;\n}\n\nexport interface TaskContextResult {\n readonly target: SymbolNode;\n readonly taskType: TaskType;\n readonly context: ContextEntry[];\n readonly totalTokens: number;\n readonly tokenBudget: number;\n readonly warnings: string[];\n}\n\nexport interface RankedContextResult {\n readonly query: string;\n readonly strategy: string;\n readonly results: Array<{\n readonly symbolId: string;\n readonly name: string;\n readonly kind: SymbolKind;\n readonly file: string;\n readonly relevanceScore: number;\n readonly importanceScore: number;\n readonly combinedScore: number;\n readonly tokens: number;\n }>;\n readonly totalTokens: number;\n readonly tokenBudget: number;\n}\n\nconst DEFAULT_TOKEN_BUDGET = 4000;\n\nexport class ContextAssembler {\n private readonly logicSlice = new LogicSliceQuery();\n private readonly blastRadius = new BlastRadiusCalculator();\n\n assembleForTask(\n graph: SymbolGraph,\n symbolId: string,\n taskType: TaskType,\n indices: readonly FileIndex[],\n tokenBudget = DEFAULT_TOKEN_BUDGET,\n ): TaskContextResult | undefined {\n const target = graph.getNode(symbolId);\n if (!target) return undefined;\n\n const weights = getWeightsForTask(taskType);\n\n // Gather forward deps + reverse deps\n const slice = this.logicSlice.getLogicSlice(graph, symbolId);\n const blastResult = this.blastRadius.calculate(graph, symbolId);\n\n // Build candidate pool (deduplicated)\n const candidateMap = new Map<string, { node: SymbolNode; signals: Signals }>();\n\n // Forward dependencies\n if (slice) {\n for (const dep of slice.dependencies) {\n candidateMap.set(dep.symbolId, {\n node: dep,\n signals: { isDirectDep: true, isInterfaceOrType: this.isInterfaceOrType(dep.kind), isDependent: false, complexity: 0, hasAntiPattern: false },\n });\n }\n }\n\n // Reverse dependents (blast radius) — fall back to a synthetic node when\n // the edge source has no matching symbols[] entry (e.g. test files that\n // only import). Without this, symbols whose importers are all such files\n // would produce an empty context even when reverse edges clearly exist.\n for (const entry of blastResult.impactedSymbols) {\n const node = graph.getNode(entry.symbolId) ?? synthesizeNodeFromId(entry.symbolId);\n if (!node) continue;\n\n const existing = candidateMap.get(entry.symbolId);\n if (existing) {\n existing.signals.isDependent = true;\n } else {\n candidateMap.set(entry.symbolId, {\n node,\n signals: { isDirectDep: false, isInterfaceOrType: this.isInterfaceOrType(node.kind), isDependent: true, complexity: 0, hasAntiPattern: false },\n });\n }\n }\n\n // Enrich with complexity + anti-patterns from index\n const fileIndexMap = new Map(indices.map((i) => [i.file, i]));\n for (const [sid, candidate] of candidateMap) {\n const file = sid.split('::')[0] ?? '';\n const fileIndex = fileIndexMap.get(file);\n\n if (fileIndex) {\n const complexity = fileIndex.complexity?.find((c) => c.symbolId === sid);\n candidate.signals.complexity = complexity?.cyclomatic ?? 0;\n candidate.signals.hasAntiPattern = fileIndex.antiPatterns.length > 0;\n }\n }\n\n // Score and rank\n const scored = [...candidateMap.entries()].map(([, { node, signals }]) => ({\n entry: this.buildContextEntry(node, signals, weights),\n score: this.computeScore(signals, weights),\n }));\n\n scored.sort((a, b) => b.score - a.score);\n\n // Greedy pack within token budget\n const context: ContextEntry[] = [];\n let totalTokens = 0;\n\n for (const { entry } of scored) {\n if (totalTokens + entry.tokens > tokenBudget) continue;\n context.push(entry);\n totalTokens += entry.tokens;\n }\n\n // Warnings\n const warnings: string[] = [];\n const targetFile = symbolId.split('::')[0] ?? '';\n const targetIndex = fileIndexMap.get(targetFile);\n if (targetIndex && targetIndex.antiPatterns.length > 0) {\n warnings.push(`⚠ Target symbol has ${targetIndex.antiPatterns.length} anti-pattern(s) in file history`);\n }\n\n return { target, taskType, context, totalTokens, tokenBudget, warnings };\n }\n\n assembleRanked(\n graph: SymbolGraph,\n query: string,\n strategy: 'combined' | 'dependency' | 'importance' = 'combined',\n tokenBudget = DEFAULT_TOKEN_BUDGET,\n ): RankedContextResult {\n const allNodes = graph.allNodes();\n const queryLower = query.toLowerCase();\n\n // Compute max reverse edges for importance normalization\n let maxReverseEdges = 1;\n for (const node of allNodes) {\n const count = graph.getReverseEdges(node.symbolId).length;\n if (count > maxReverseEdges) maxReverseEdges = count;\n }\n\n // Score each symbol\n const scored = allNodes.map((node) => {\n const relevanceScore = this.computeTextRelevance(node.name, queryLower);\n const importanceScore = graph.getReverseEdges(node.symbolId).length / maxReverseEdges;\n\n let combinedScore: number;\n switch (strategy) {\n case 'dependency':\n combinedScore = relevanceScore;\n break;\n case 'importance':\n combinedScore = importanceScore;\n break;\n default:\n combinedScore = relevanceScore * 0.6 + importanceScore * 0.4;\n }\n\n return {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n relevanceScore: Math.round(relevanceScore * 1000) / 1000,\n importanceScore: Math.round(importanceScore * 1000) / 1000,\n combinedScore: Math.round(combinedScore * 1000) / 1000,\n tokens: this.estimateTokens(node),\n };\n });\n\n // Filter zero-relevance for text-based strategies\n const filtered = strategy === 'importance'\n ? scored\n : scored.filter((s) => s.relevanceScore > 0 || s.importanceScore > 0.1);\n\n filtered.sort((a, b) => b.combinedScore - a.combinedScore);\n\n // Greedy pack\n const results: typeof filtered = [];\n let totalTokens = 0;\n\n for (const item of filtered) {\n if (totalTokens + item.tokens > tokenBudget) continue;\n results.push(item);\n totalTokens += item.tokens;\n }\n\n return { query, strategy, results, totalTokens, tokenBudget };\n }\n\n private computeTextRelevance(name: string, queryLower: string): number {\n const nameLower = name.toLowerCase();\n if (nameLower === queryLower) return 1.0;\n if (nameLower.includes(queryLower)) return 0.7;\n if (queryLower.split(/\\s+/).some((word) => nameLower.includes(word))) return 0.4;\n return 0;\n }\n\n private computeScore(signals: Signals, weights: ScoringWeights): number {\n let score = 0;\n if (signals.isDirectDep) score += weights.directDependency;\n if (signals.isInterfaceOrType) score += weights.interfaceTypeDef;\n if (signals.isDependent) score += weights.blastRadiusDependent;\n if (signals.complexity > 5) score += weights.highComplexity;\n if (signals.hasAntiPattern) score += weights.antiPatternHistory;\n return Math.round(score * 1000) / 1000;\n }\n\n private buildContextEntry(node: SymbolNode, signals: Signals, weights: ScoringWeights): ContextEntry {\n const reasons: string[] = [];\n if (signals.isDirectDep) reasons.push('direct dependency');\n if (signals.isInterfaceOrType) reasons.push('type/interface definition');\n if (signals.isDependent) reasons.push('blast radius dependent');\n if (signals.complexity > 5) reasons.push(`high complexity (CC=${signals.complexity})`);\n if (signals.hasAntiPattern) reasons.push('anti-pattern history');\n\n return {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n relevanceScore: this.computeScore(signals, weights),\n reason: reasons.join(', ') || 'related symbol',\n lines: node.endLine - node.startLine + 1,\n tokens: this.estimateTokens(node),\n };\n }\n\n private isInterfaceOrType(kind: SymbolKind): boolean {\n return kind === 'interface' || kind === 'type';\n }\n\n estimateTokensPublic(node: SymbolNode): number {\n return this.estimateTokens(node);\n }\n\n private estimateTokens(node: SymbolNode): number {\n // Use byte offsets for accurate estimation when available (~4 chars per token)\n if (node.startOffset !== undefined && node.endOffset !== undefined) {\n return Math.ceil((node.endOffset - node.startOffset) / 4);\n }\n return (node.endLine - node.startLine + 1) * 10;\n }\n}\n\ninterface Signals {\n isDirectDep: boolean;\n isInterfaceOrType: boolean;\n isDependent: boolean;\n complexity: number;\n hasAntiPattern: boolean;\n}\n\nconst VALID_SYMBOL_KINDS = new Set<SymbolKind>([\n 'function', 'class', 'interface', 'method', 'variable', 'type',\n]);\n\n/**\n * Build a minimal SymbolNode from a symbolId string. Used when a blast-radius\n * entry points at an orphan edge source (no matching symbols[] entry in the\n * index). startLine/endLine are 0 so token estimation falls back to a small\n * default without over-counting missing source ranges.\n */\nfunction synthesizeNodeFromId(symbolId: string): SymbolNode | undefined {\n const parts = symbolId.split('::');\n if (parts.length !== 3) return undefined;\n const [file, name, kindRaw] = parts;\n if (!file || !name || !kindRaw) return undefined;\n const kind = VALID_SYMBOL_KINDS.has(kindRaw as SymbolKind) ? (kindRaw as SymbolKind) : 'variable';\n return { symbolId, name, kind, startLine: 0, endLine: 0 };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ContextAssembler } from '../../core/context-assembly/context-assembler.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { SearchEngine } from '../../core/search/search-engine.js';\nimport type { ISearchPort } from '../../ports/i-search-port.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:search');\n\nconst InputSchema = z.object({\n query: z.string().min(1),\n tokenBudget: z.number().min(100).optional().default(4000),\n strategy: z.enum(['combined', 'dependency', 'importance']).optional().default('combined'),\n fuzzy: z.boolean().optional().default(true),\n searchMode: z.enum(['fts', 'legacy']).optional().default('fts'),\n});\n\nexport function handleGetRankedContext(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n searchEngine?: ISearchPort,\n) {\n const assembler = new ContextAssembler();\n const engine: ISearchPort = searchEngine ?? new SearchEngine();\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n let lastNodeCount = -1;\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { query, tokenBudget, strategy, searchMode } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n // Use new search engine when mode is 'fts'\n if (searchMode === 'fts' && strategy !== 'importance') {\n // Rebuild search index when graph changes (detected by node count change)\n const currentNodeCount = graph.nodeCount;\n if (currentNodeCount !== lastNodeCount) {\n const allNodes = graph.allNodes();\n // Compute PageRank scores (normalized reverse edge count)\n const pageRankScores = new Map<string, number>();\n let maxReverseEdges = 1;\n for (const node of allNodes) {\n const count = graph.getReverseEdges(node.symbolId).length;\n if (count > maxReverseEdges) maxReverseEdges = count;\n }\n for (const node of allNodes) {\n const count = graph.getReverseEdges(node.symbolId).length;\n pageRankScores.set(node.symbolId, count / maxReverseEdges);\n }\n\n engine.buildIndex(allNodes, pageRankScores);\n lastNodeCount = currentNodeCount;\n log.info(`Search engine: ${engine.getTier()} (${allNodes.length} symbols)`);\n }\n\n const searchResult = engine.search(query, 100);\n\n // Greedy pack within token budget\n const results: Array<{\n symbolId: string;\n name: string;\n kind: string;\n file: string;\n relevanceScore: number;\n importanceScore: number;\n combinedScore: number;\n tokens: number;\n }> = [];\n let totalTokens = 0;\n\n for (const sr of searchResult.results) {\n const node = graph.getNode(sr.symbolId);\n const tokens = node ? assembler.estimateTokensPublic(node) : 90;\n\n if (totalTokens + tokens > tokenBudget) continue;\n results.push({\n symbolId: sr.symbolId,\n name: sr.name,\n kind: sr.kind,\n file: sr.filePath,\n relevanceScore: Math.round(sr.relevanceScore * 1000) / 1000,\n importanceScore: Math.round(sr.importanceScore * 1000) / 1000,\n combinedScore: Math.round(sr.combinedScore * 1000) / 1000,\n tokens,\n });\n totalTokens += tokens;\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n query,\n strategy,\n results,\n totalTokens,\n tokenBudget,\n searchMetrics: searchResult.metrics,\n ...(searchResult.fuzzyCorrection ? { fuzzyCorrection: searchResult.fuzzyCorrection } : {}),\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n\n // Legacy mode: use existing substring matching\n const result = assembler.assembleRanked(graph, query, strategy, tokenBudget);\n\n const payload = masking.mask(JSON.stringify(wrapResponse(result as unknown as Record<string, unknown>)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","/**\n * Symbol Tokenizer — splits symbol names into searchable tokens.\n *\n * Handles camelCase, PascalCase, snake_case, kebab-case, and digit boundaries.\n * Used to populate FTS5 `tokenized_name` columns and to tokenize search queries.\n */\n\nconst STOP_WORDS = new Set([\n 'get', 'set', 'is', 'has', 'can', 'do', 'the', 'a', 'an', 'of', 'to', 'in',\n 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'not', 'no', 'or',\n]);\n\nexport interface TokenizerOptions {\n /** Include stop-word tokens in output (default: false) */\n includeStopWords?: boolean;\n /** Include the full original name as an additional token (default: true) */\n includeOriginal?: boolean;\n /** Include file path segments as tokens (default: false) */\n includeFilePath?: boolean;\n}\n\nconst DEFAULT_OPTIONS: Required<TokenizerOptions> = {\n includeStopWords: false,\n includeOriginal: true,\n includeFilePath: false,\n};\n\nexport class SymbolTokenizer {\n private readonly options: Required<TokenizerOptions>;\n\n constructor(options?: TokenizerOptions) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Tokenize a symbol name into searchable tokens.\n *\n * Examples:\n * \"getCoChangeMetrics\" → [\"get\", \"co\", \"change\", \"metrics\"] (or without stop words: [\"co\", \"change\", \"metrics\"])\n * \"SqliteStorageAdapter\" → [\"sqlite\", \"storage\", \"adapter\"]\n * \"i_storage_port\" → [\"i\", \"storage\", \"port\"]\n * \"BM25Scorer\" → [\"bm\", \"25\", \"scorer\"]\n * \"DB_PASSWORD\" → [\"db\", \"password\"]\n */\n tokenize(symbolName: string, filePath?: string): string[] {\n const splitTokens = SymbolTokenizer.splitName(symbolName);\n const lowered = splitTokens.map((t) => t.toLowerCase());\n\n const tokens: string[] = this.options.includeStopWords\n ? lowered\n : lowered.filter((t) => !STOP_WORDS.has(t));\n\n // Preserve original name as a token for exact matching\n if (this.options.includeOriginal) {\n const original = symbolName.toLowerCase();\n if (!tokens.includes(original)) {\n tokens.unshift(original);\n }\n }\n\n // Include file path segments\n if (this.options.includeFilePath && filePath) {\n const pathTokens = SymbolTokenizer.tokenizeFilePath(filePath);\n for (const pt of pathTokens) {\n if (!tokens.includes(pt)) {\n tokens.push(pt);\n }\n }\n }\n\n return tokens;\n }\n\n /**\n * Tokenize a search query. Queries use stop words and don't include originals.\n */\n tokenizeQuery(query: string): string[] {\n const words = query.trim().split(/\\s+/);\n const tokens: string[] = [];\n for (const word of words) {\n const split = SymbolTokenizer.splitName(word);\n for (const t of split) {\n const lowered = t.toLowerCase();\n if (lowered.length > 0 && !tokens.includes(lowered)) {\n tokens.push(lowered);\n }\n }\n }\n return tokens;\n }\n\n /**\n * Split a name into constituent words based on naming convention boundaries.\n *\n * Rules:\n * 1. Split on underscores and hyphens (snake_case, kebab-case)\n * 2. Split on camelCase/PascalCase boundaries (uppercase after lowercase)\n * 3. Split acronym boundaries (e.g., \"HTMLParser\" → [\"HTML\", \"Parser\"])\n * 4. Split on digit ↔ letter boundaries\n */\n static splitName(name: string): string[] {\n if (!name || name.length === 0) return [];\n\n // Step 1: Split on underscores, hyphens, dots, slashes\n const segments = name.split(/[_\\-./\\\\]+/).filter((s) => s.length > 0);\n\n const tokens: string[] = [];\n for (const segment of segments) {\n tokens.push(...SymbolTokenizer.splitCamelCase(segment));\n }\n\n return tokens.filter((t) => t.length > 0);\n }\n\n /**\n * Split a single segment on camelCase/PascalCase/digit boundaries.\n */\n private static splitCamelCase(segment: string): string[] {\n const tokens: string[] = [];\n let current = '';\n\n for (let i = 0; i < segment.length; i++) {\n const ch = segment[i]!;\n const prev = i > 0 ? segment[i - 1]! : '';\n\n // Boundary: letter → digit or digit → letter\n if (current.length > 0) {\n const prevIsDigit = isDigit(prev);\n const currIsDigit = isDigit(ch);\n if (prevIsDigit !== currIsDigit) {\n tokens.push(current);\n current = ch;\n continue;\n }\n }\n\n // Boundary: lowercase → uppercase (camelCase boundary)\n if (current.length > 0 && isLower(prev) && isUpper(ch)) {\n tokens.push(current);\n current = ch;\n continue;\n }\n\n // Acronym boundary: multiple uppercase followed by lowercase\n // \"HTMLParser\": H-T-M-L-P-a → when we hit 'a', current=\"HTMLP\"\n // We should split to \"HTML\" + \"Pa...\"\n if (current.length > 1 && isUpper(ch) === false && isUpper(prev) && !isDigit(ch)) {\n // prev is uppercase, current char is lowercase → split before prev\n const lastChar = current[current.length - 1]!;\n if (isUpper(lastChar)) {\n tokens.push(current.slice(0, -1));\n current = lastChar + ch;\n continue;\n }\n }\n\n current += ch;\n }\n\n if (current.length > 0) {\n tokens.push(current);\n }\n\n return tokens;\n }\n\n /**\n * Extract searchable tokens from a file path.\n * \"src/core/search/symbol-tokenizer.ts\" → [\"src\", \"core\", \"search\", \"symbol\", \"tokenizer\", \"ts\"]\n */\n static tokenizeFilePath(filePath: string): string[] {\n // Remove extension, split on separators\n const withoutExt = filePath.replace(/\\.[^.]+$/, '');\n const segments = withoutExt.split(/[/\\\\]+/);\n const tokens: string[] = [];\n for (const seg of segments) {\n const parts = seg.split(/[_\\-.]+/).filter((s) => s.length > 0);\n for (const p of parts) {\n const lowered = p.toLowerCase();\n if (lowered.length > 0 && !tokens.includes(lowered)) {\n tokens.push(lowered);\n }\n }\n }\n return tokens;\n }\n\n /** Check if a token is a stop word */\n static isStopWord(token: string): boolean {\n return STOP_WORDS.has(token.toLowerCase());\n }\n}\n\nfunction isUpper(ch: string): boolean {\n return ch >= 'A' && ch <= 'Z';\n}\n\nfunction isLower(ch: string): boolean {\n return ch >= 'a' && ch <= 'z';\n}\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9';\n}\n","/**\n * Fuzzy Corrector — Damerau-Levenshtein based typo correction.\n *\n * Uses adaptive thresholds: d≤1 for terms ≤5 chars, d≤2 for longer terms.\n * Vocabulary is built from tokenized symbol names during index build.\n */\n\nimport type { FuzzyCorrection } from '../types.js';\n\nexport class FuzzyCorrector {\n /** term → frequency (for tie-breaking) */\n private vocabulary: Map<string, number> = new Map();\n\n buildVocabulary(termFrequencies: Map<string, number>): void {\n this.vocabulary = new Map(termFrequencies);\n }\n\n /**\n * Attempt to correct query tokens.\n * Returns null if no correction found or no improvement possible.\n */\n correct(queryTokens: string[]): FuzzyCorrection | null {\n if (this.vocabulary.size === 0) return null;\n\n const corrections: FuzzyCorrection['corrections'] = [];\n const correctedTokens: string[] = [];\n let anyChanged = false;\n\n for (const token of queryTokens) {\n // Skip very short tokens (1-2 chars) — too many false matches\n if (token.length <= 2) {\n correctedTokens.push(token);\n continue;\n }\n\n // Check if token exists in vocabulary (exact match)\n if (this.vocabulary.has(token)) {\n correctedTokens.push(token);\n continue;\n }\n\n const maxDist = token.length <= 5 ? 1 : 2;\n const best = this.findClosest(token, maxDist);\n\n if (best) {\n corrections.push({\n original: token,\n corrected: best.term,\n distance: best.distance,\n });\n correctedTokens.push(best.term);\n anyChanged = true;\n } else {\n correctedTokens.push(token);\n }\n }\n\n if (!anyChanged) return null;\n\n return {\n originalQuery: queryTokens.join(' '),\n correctedQuery: correctedTokens.join(' '),\n corrections,\n };\n }\n\n /**\n * Find the closest vocabulary term within maxDistance.\n * Tie-breaks by frequency (higher = preferred).\n */\n private findClosest(\n token: string,\n maxDistance: number,\n ): { term: string; distance: number } | null {\n let bestTerm: string | null = null;\n let bestDist = maxDistance + 1;\n let bestFreq = -1;\n\n for (const [vocabTerm, freq] of this.vocabulary) {\n // Quick length filter: Damerau-Levenshtein ≥ |len difference|\n if (Math.abs(vocabTerm.length - token.length) > maxDistance) continue;\n\n const dist = damerauLevenshtein(token, vocabTerm);\n if (dist <= maxDistance) {\n if (dist < bestDist || (dist === bestDist && freq > bestFreq)) {\n bestTerm = vocabTerm;\n bestDist = dist;\n bestFreq = freq;\n }\n }\n }\n\n return bestTerm !== null ? { term: bestTerm, distance: bestDist } : null;\n }\n}\n\n/**\n * Damerau-Levenshtein distance.\n * Includes transpositions as a single edit operation.\n * \"tokne\" → \"token\" = distance 1 (transposition), not 2 (delete + insert).\n */\nexport function damerauLevenshtein(a: string, b: string): number {\n const lenA = a.length;\n const lenB = b.length;\n\n if (lenA === 0) return lenB;\n if (lenB === 0) return lenA;\n\n // Optimal string alignment distance (restricted edit distance)\n const d: number[][] = Array.from({ length: lenA + 1 }, () => new Array<number>(lenB + 1).fill(0));\n\n for (let i = 0; i <= lenA; i++) d[i]![0] = i;\n for (let j = 0; j <= lenB; j++) d[0]![j] = j;\n\n for (let i = 1; i <= lenA; i++) {\n for (let j = 1; j <= lenB; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n d[i]![j] = Math.min(\n d[i - 1]![j]! + 1, // deletion\n d[i]![j - 1]! + 1, // insertion\n d[i - 1]![j - 1]! + cost, // substitution\n );\n\n // Transposition\n if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {\n d[i]![j] = Math.min(d[i]![j]!, d[i - 2]![j - 2]! + cost);\n }\n }\n }\n\n return d[lenA]![lenB]!;\n}\n","/**\n * In-memory BM25 Search Engine (Tier 2)\n *\n * Implements two-phase cascade search:\n * Phase 1: Exact + tokenized matching with BM25 scoring\n * Phase 2: Trigram/partial matching (activated when Phase 1 returns < threshold results)\n * Phase 3: Fuzzy correction (activated when Phase 1+2 return < threshold results)\n *\n * Scoring: BM25 * (1 + pageRankWeight * pageRankScore) * bigramBoost\n */\n\nimport type { SymbolNode, SearchResponse, SearchResult, SearchMetrics } from '../types.js';\nimport { SymbolTokenizer } from './symbol-tokenizer.js';\nimport { FuzzyCorrector } from './fuzzy-corrector.js';\nimport { createLogger } from '../logger.js';\n\nconst log = createLogger('ctxo:search');\n\n/** BM25 parameters tuned for code search (short documents) */\ninterface BM25Config {\n k1: number; // term frequency saturation (default: 1.2)\n b: number; // length normalization (default: 0.25 — low for short symbol names)\n}\n\ninterface SearchEngineConfig {\n bm25: BM25Config;\n /** Weight of PageRank boost: finalScore = bm25 * (1 + pageRankWeight * pr) */\n pageRankWeight: number;\n /** Score penalty for Phase 2 (trigram) results */\n trigramPenalty: number;\n /** Threshold: if Phase 1 returns fewer results, activate Phase 2 */\n phase2Threshold: number;\n /** Threshold: if Phase 1+2 return fewer results, activate fuzzy correction */\n fuzzyThreshold: number;\n /** Maximum results per phase */\n maxPerPhase: number;\n}\n\nconst DEFAULT_CONFIG: SearchEngineConfig = {\n bm25: { k1: 1.2, b: 0.25 },\n pageRankWeight: 0.5,\n trigramPenalty: 0.8,\n phase2Threshold: 3,\n fuzzyThreshold: 3,\n maxPerPhase: 50,\n};\n\n/** A document in the inverted index (one per symbol) */\ninterface IndexedDocument {\n symbolId: string;\n name: string;\n kind: string;\n filePath: string;\n /** Tokenized name (from SymbolTokenizer) */\n tokens: string[];\n /** Token count (document length for BM25) */\n length: number;\n}\n\n/** Posting list entry */\ninterface Posting {\n docIndex: number;\n termFrequency: number;\n}\n\nexport class SearchEngine {\n private readonly config: SearchEngineConfig;\n private readonly tokenizer: SymbolTokenizer;\n private readonly fuzzy: FuzzyCorrector;\n\n // Primary index (exact + tokenized)\n private documents: IndexedDocument[] = [];\n private primaryIndex: Map<string, Posting[]> = new Map();\n private avgDocLength = 0;\n\n // Trigram index (character 3-grams)\n private trigramIndex: Map<string, Posting[]> = new Map();\n\n // PageRank scores\n private pageRankScores: Map<string, number> = new Map();\n\n constructor(config?: Partial<SearchEngineConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.tokenizer = new SymbolTokenizer({ includeOriginal: true, includeFilePath: true });\n this.fuzzy = new FuzzyCorrector();\n }\n\n getTier(): 'fts5' | 'in-memory' | 'legacy' {\n return 'in-memory';\n }\n\n buildIndex(symbols: SymbolNode[], pageRankScores?: Map<string, number>): void {\n const start = performance.now();\n this.documents = [];\n this.primaryIndex = new Map();\n this.trigramIndex = new Map();\n this.pageRankScores = pageRankScores ?? new Map();\n\n let totalLength = 0;\n const vocabFreq = new Map<string, number>();\n\n for (const sym of symbols) {\n const filePath = sym.symbolId.split('::')[0] ?? '';\n const tokens = this.tokenizer.tokenize(sym.name, filePath);\n const doc: IndexedDocument = {\n symbolId: sym.symbolId,\n name: sym.name,\n kind: sym.kind,\n filePath,\n tokens,\n length: tokens.length,\n };\n const docIndex = this.documents.length;\n this.documents.push(doc);\n totalLength += tokens.length;\n\n // Build primary inverted index\n const termFreqs = new Map<string, number>();\n for (const token of tokens) {\n termFreqs.set(token, (termFreqs.get(token) ?? 0) + 1);\n vocabFreq.set(token, (vocabFreq.get(token) ?? 0) + 1);\n }\n for (const [term, tf] of termFreqs) {\n let postings = this.primaryIndex.get(term);\n if (!postings) {\n postings = [];\n this.primaryIndex.set(term, postings);\n }\n postings.push({ docIndex, termFrequency: tf });\n }\n\n // Build trigram index from symbol name (lowercased)\n const nameLower = sym.name.toLowerCase();\n const trigrams = this.extractTrigrams(nameLower);\n const trigramFreqs = new Map<string, number>();\n for (const tri of trigrams) {\n trigramFreqs.set(tri, (trigramFreqs.get(tri) ?? 0) + 1);\n }\n for (const [tri, tf] of trigramFreqs) {\n let postings = this.trigramIndex.get(tri);\n if (!postings) {\n postings = [];\n this.trigramIndex.set(tri, postings);\n }\n postings.push({ docIndex, termFrequency: tf });\n }\n }\n\n this.avgDocLength = this.documents.length > 0 ? totalLength / this.documents.length : 1;\n\n // Build vocabulary for fuzzy corrector\n this.fuzzy.buildVocabulary(vocabFreq);\n\n const elapsed = performance.now() - start;\n log.info(`Index built: ${symbols.length} symbols, ${this.primaryIndex.size} terms, ${this.trigramIndex.size} trigrams (${elapsed.toFixed(0)}ms)`);\n }\n\n search(query: string, limit = 50): SearchResponse {\n const start = performance.now();\n const queryTokens = this.tokenizer.tokenizeQuery(query);\n\n if (queryTokens.length === 0) {\n return this.emptyResponse(query, start);\n }\n\n const metrics: SearchMetrics = {\n porterHits: 0,\n trigramHits: 0,\n phase2Activated: false,\n fuzzyApplied: false,\n latencyMs: 0,\n };\n\n // Phase 1: Primary index search (exact + tokenized)\n const phase1Scores = this.bm25Search(queryTokens, this.primaryIndex);\n metrics.porterHits = phase1Scores.size;\n\n let allScores = phase1Scores;\n\n // Phase 2: Trigram search (if Phase 1 insufficient)\n if (phase1Scores.size < this.config.phase2Threshold) {\n metrics.phase2Activated = true;\n const queryLower = query.toLowerCase();\n\n // Skip trigram for very short queries (< 3 chars produces no useful trigrams)\n if (queryLower.length >= 3) {\n const queryTrigrams = this.extractTrigrams(queryLower);\n const phase2Scores = this.bm25Search(queryTrigrams, this.trigramIndex);\n metrics.trigramHits = phase2Scores.size;\n\n // Merge: union by symbolId, Phase 2 with penalty\n allScores = this.mergeScores(phase1Scores, phase2Scores, this.config.trigramPenalty);\n }\n }\n\n // Phase 3: Fuzzy correction (if still insufficient)\n let fuzzyCorrection: SearchResponse['fuzzyCorrection'];\n if (allScores.size < this.config.fuzzyThreshold) {\n const correction = this.fuzzy.correct(queryTokens);\n if (correction) {\n metrics.fuzzyApplied = true;\n fuzzyCorrection = correction;\n const correctedTokens = this.tokenizer.tokenizeQuery(correction.correctedQuery);\n const fuzzyScores = this.bm25Search(correctedTokens, this.primaryIndex);\n\n // Merge fuzzy results (with slight penalty)\n allScores = this.mergeScores(allScores, fuzzyScores, 0.9);\n\n // Also try trigram on corrected query\n if (allScores.size < this.config.fuzzyThreshold && correction.correctedQuery.length >= 3) {\n const correctedTrigrams = this.extractTrigrams(correction.correctedQuery.toLowerCase());\n const fuzzyTrigramScores = this.bm25Search(correctedTrigrams, this.trigramIndex);\n allScores = this.mergeScores(allScores, fuzzyTrigramScores, this.config.trigramPenalty * 0.9);\n }\n }\n }\n\n // Snapshot raw BM25 relevance scores before boosts\n const rawRelevance = new Map(allScores);\n\n // Apply bigram boost for multi-word queries (applies to ALL paths including fuzzy)\n const effectiveQueryTerms = fuzzyCorrection\n ? this.tokenizer.tokenizeQuery(fuzzyCorrection.correctedQuery)\n : queryTokens;\n\n if (effectiveQueryTerms.length >= 2) {\n for (const [docIdx, score] of allScores) {\n const doc = this.documents[docIdx]!;\n const boost = this.bigramBoost(effectiveQueryTerms, doc.tokens);\n allScores.set(docIdx, score * boost);\n }\n }\n\n // Apply PageRank boost (applies to ALL paths including fuzzy)\n for (const [docIdx, score] of allScores) {\n const doc = this.documents[docIdx]!;\n const pr = this.pageRankScores.get(doc.symbolId) ?? 0;\n allScores.set(docIdx, score * (1 + this.config.pageRankWeight * pr));\n }\n\n metrics.latencyMs = performance.now() - start;\n const results = this.buildResults(allScores, rawRelevance, limit);\n\n const phaseInfo = metrics.phase2Activated ? 'phase2=yes' : 'phase2=no';\n if (fuzzyCorrection) {\n log.info(`search \"${query}\" → fuzzy \"${fuzzyCorrection.correctedQuery}\": ${results.length} results (${metrics.latencyMs.toFixed(0)}ms)`);\n } else {\n log.info(`search \"${query}\": ${results.length} results, porter=${metrics.porterHits} trigram=${metrics.trigramHits} ${phaseInfo} (${metrics.latencyMs.toFixed(0)}ms)`);\n }\n\n return { query, results, metrics, ...(fuzzyCorrection ? { fuzzyCorrection } : {}) };\n }\n\n updateFile(filePath: string, symbols: SymbolNode[]): void {\n // Remove old documents for this file\n const oldIndices = new Set<number>();\n for (let i = 0; i < this.documents.length; i++) {\n if (this.documents[i]!.filePath === filePath) {\n oldIndices.add(i);\n }\n }\n\n if (oldIndices.size === 0 && symbols.length === 0) return;\n\n // Full rebuild (simpler and correct for incremental updates)\n const allSymbols: SymbolNode[] = [];\n for (let i = 0; i < this.documents.length; i++) {\n if (!oldIndices.has(i)) {\n const doc = this.documents[i]!;\n allSymbols.push({\n symbolId: doc.symbolId,\n name: doc.name,\n kind: doc.kind as SymbolNode['kind'],\n startLine: 0,\n endLine: 0,\n });\n }\n }\n allSymbols.push(...symbols);\n this.buildIndex(allSymbols, this.pageRankScores);\n }\n\n /**\n * BM25 scoring over an inverted index.\n * Returns Map<docIndex, score>\n */\n private bm25Search(queryTerms: string[], index: Map<string, Posting[]>): Map<number, number> {\n const scores = new Map<number, number>();\n const N = this.documents.length;\n const { k1, b } = this.config.bm25;\n\n for (const term of queryTerms) {\n const postings = index.get(term);\n if (!postings) continue;\n\n const df = postings.length; // document frequency\n // IDF: log((N - df + 0.5) / (df + 0.5) + 1)\n const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);\n\n for (const { docIndex, termFrequency } of postings) {\n const doc = this.documents[docIndex]!;\n const tf = termFrequency;\n const docLen = doc.length;\n\n // BM25 term score\n const numerator = tf * (k1 + 1);\n const denominator = tf + k1 * (1 - b + b * (docLen / this.avgDocLength));\n const termScore = idf * (numerator / denominator);\n\n scores.set(docIndex, (scores.get(docIndex) ?? 0) + termScore);\n }\n }\n\n // Boost exact name matches (FR-1.9: 3-5x higher)\n const queryJoined = queryTerms.join('');\n for (const [docIdx, score] of scores) {\n const doc = this.documents[docIdx]!;\n const nameLower = doc.name.toLowerCase();\n if (nameLower === queryJoined || nameLower === queryTerms.join(' ')) {\n scores.set(docIdx, score * 5.0);\n }\n }\n\n return scores;\n }\n\n /**\n * Merge two score maps. Phase 2 scores are penalized.\n */\n private mergeScores(\n primary: Map<number, number>,\n secondary: Map<number, number>,\n penalty: number,\n ): Map<number, number> {\n const merged = new Map(primary);\n for (const [docIdx, score] of secondary) {\n const existing = merged.get(docIdx);\n const penalized = score * penalty;\n if (existing !== undefined) {\n merged.set(docIdx, Math.max(existing, penalized));\n } else {\n merged.set(docIdx, penalized);\n }\n }\n return merged;\n }\n\n /**\n * Bigram boost for multi-word queries.\n * Each adjacent query term pair found adjacent in symbol tokens → 2x boost.\n */\n private bigramBoost(queryTerms: string[], symbolTokens: string[]): number {\n if (queryTerms.length < 2) return 1.0;\n\n let adjacentPairs = 0;\n for (let i = 0; i < queryTerms.length - 1; i++) {\n const idxA = symbolTokens.findIndex((t) => t.includes(queryTerms[i]!));\n const idxB = symbolTokens.findIndex((t) => t.includes(queryTerms[i + 1]!));\n if (idxA >= 0 && idxB >= 0 && Math.abs(idxA - idxB) === 1) {\n adjacentPairs++;\n }\n }\n\n return 1 + adjacentPairs * 2.0;\n }\n\n /**\n * Extract character trigrams from a string.\n * \"sqlite\" → [\"sql\", \"qli\", \"lit\", \"ite\"]\n */\n private extractTrigrams(text: string): string[] {\n const trigrams: string[] = [];\n for (let i = 0; i <= text.length - 3; i++) {\n trigrams.push(text.substring(i, i + 3));\n }\n return trigrams;\n }\n\n private buildResults(\n combinedScores: Map<number, number>,\n rawRelevance: Map<number, number>,\n limit: number,\n ): SearchResult[] {\n const entries = [...combinedScores.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit);\n\n return entries.map(([docIdx, combined]) => {\n const doc = this.documents[docIdx]!;\n const pr = this.pageRankScores.get(doc.symbolId) ?? 0;\n return {\n symbolId: doc.symbolId,\n name: doc.name,\n kind: doc.kind,\n filePath: doc.filePath,\n relevanceScore: rawRelevance.get(docIdx) ?? 0,\n importanceScore: pr,\n combinedScore: combined,\n };\n });\n }\n\n private emptyResponse(query: string, start: number): SearchResponse {\n return {\n query,\n results: [],\n metrics: {\n porterHits: 0,\n trigramHits: 0,\n phase2Activated: false,\n fuzzyApplied: false,\n latencyMs: performance.now() - start,\n },\n };\n }\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { SearchEngine } from '../../core/search/search-engine.js';\nimport type { ISearchPort } from '../../ports/i-search-port.js';\n\nconst InputSchema = z.object({\n pattern: z.string().min(1),\n kind: z.enum(['function', 'class', 'interface', 'method', 'variable', 'type']).optional(),\n filePattern: z.string().optional(),\n limit: z.number().int().min(1).max(100).optional().default(25),\n mode: z.enum(['regex', 'fts']).optional().default('regex'),\n});\n\nexport function handleSearchSymbols(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n searchEngine?: ISearchPort,\n) {\n const engine: ISearchPort = searchEngine ?? new SearchEngine();\n let lastFtsNodeCount = -1;\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { pattern, kind, filePattern, limit, mode } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n const allNodes = graph.allNodes();\n\n // FTS mode: use search engine\n if (mode === 'fts') {\n if (allNodes.length !== lastFtsNodeCount) {\n engine.buildIndex(allNodes);\n lastFtsNodeCount = allNodes.length;\n }\n\n const searchResult = engine.search(pattern, limit);\n let ftsResults = searchResult.results;\n\n // Apply kind filter\n if (kind) {\n ftsResults = ftsResults.filter((r) => r.kind === kind);\n }\n // Apply file pattern filter\n if (filePattern) {\n const fp = filePattern.toLowerCase();\n ftsResults = ftsResults.filter((r) => r.filePath.toLowerCase().includes(fp));\n }\n\n const results = ftsResults.slice(0, limit).map((r) => {\n const node = graph.getNode(r.symbolId);\n return {\n symbolId: r.symbolId,\n name: r.name,\n kind: r.kind,\n file: r.filePath,\n startLine: node?.startLine ?? 0,\n endLine: node?.endLine ?? 0,\n relevanceScore: Math.round(r.relevanceScore * 1000) / 1000,\n };\n });\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n totalMatches: ftsResults.length,\n results,\n searchMetrics: searchResult.metrics,\n ...(searchResult.fuzzyCorrection ? { fuzzyCorrection: searchResult.fuzzyCorrection } : {}),\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n\n // Legacy regex mode (default)\n let matcher: (name: string) => boolean;\n try {\n const regex = new RegExp(pattern, 'i');\n matcher = (name: string) => regex.test(name);\n } catch {\n const lowerPattern = pattern.toLowerCase();\n matcher = (name: string) => name.toLowerCase().includes(lowerPattern);\n }\n\n const matches = allNodes.filter((node) => {\n if (!matcher(node.name)) return false;\n if (kind && node.kind !== kind) return false;\n if (filePattern) {\n const file = node.symbolId.split('::')[0] ?? '';\n if (!file.toLowerCase().includes(filePattern.toLowerCase())) return false;\n }\n return true;\n });\n\n const results = matches.slice(0, limit).map((node) => ({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0],\n startLine: node.startLine,\n endLine: node.endLine,\n }));\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n totalMatches: matches.length,\n results,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\n\nconst InputSchema = z.object({\n since: z.string().optional().default('HEAD~1'),\n maxFiles: z.number().int().min(1).optional().default(50),\n});\n\nexport function handleGetChangedSymbols(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { since, maxFiles } = parsed.data;\n const changedPaths = await git.getChangedFiles(since);\n const { graph, indexedFiles } = getGraph();\n\n // Index all nodes by file for fast lookup\n const nodesByFile = new Map<string, Array<{ symbolId: string; name: string; kind: string; startLine: number; endLine: number }>>();\n for (const node of graph.allNodes()) {\n const file = node.symbolId.split('::')[0] ?? '';\n const existing = nodesByFile.get(file) ?? [];\n existing.push({ symbolId: node.symbolId, name: node.name, kind: node.kind, startLine: node.startLine, endLine: node.endLine });\n nodesByFile.set(file, existing);\n }\n\n const files: Array<{ file: string; symbols: Array<{ symbolId: string; name: string; kind: string; startLine: number; endLine: number }> }> = [];\n let totalSymbols = 0;\n\n for (const filePath of changedPaths.slice(0, maxFiles)) {\n const symbols = nodesByFile.get(filePath);\n if (symbols && symbols.length > 0) {\n files.push({ file: filePath, symbols });\n totalSymbols += symbols.length;\n }\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n since,\n changedFiles: files.length,\n changedSymbols: totalSymbols,\n files,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n edgeKinds: z.array(z.enum(['imports', 'calls', 'extends', 'implements', 'uses'])).optional(),\n transitive: z.boolean().optional().default(false),\n maxDepth: z.number().int().min(1).max(10).optional().default(5),\n intent: z.string().optional().describe('Filter results by intent keywords (e.g., \"test\", \"adapter\", \"core\")'),\n});\n\nexport function handleFindImporters(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, edgeKinds, transitive, maxDepth } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n if (!graph.hasNode(symbolId)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n const importers: Array<{\n symbolId: string;\n name: string;\n kind: string;\n file: string;\n edgeKind: string;\n edgeKinds?: string[];\n depth: number;\n }> = [];\n\n if (!transitive) {\n // Direct importers only — deduplicate by symbolId, aggregate edgeKinds\n const reverseEdges = graph.getReverseEdges(symbolId);\n const seen = new Map<string, typeof importers[0]>();\n for (const edge of reverseEdges) {\n if (edgeKinds && !edgeKinds.includes(edge.kind as 'imports' | 'calls' | 'extends' | 'implements' | 'uses')) continue;\n const node = graph.getNode(edge.from);\n if (node) {\n const existing = seen.get(node.symbolId);\n if (existing) {\n if (!existing.edgeKinds!.includes(edge.kind)) {\n existing.edgeKinds!.push(edge.kind);\n }\n } else {\n const entry = {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n edgeKinds: [edge.kind],\n depth: 1,\n };\n seen.set(node.symbolId, entry);\n importers.push(entry);\n }\n }\n }\n } else {\n // BFS for transitive importers\n const visited = new Set<string>([symbolId]);\n const queue: Array<{ id: string; depth: number }> = [{ id: symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current.depth >= maxDepth) continue;\n\n const reverseEdges = graph.getReverseEdges(current.id);\n for (const edge of reverseEdges) {\n if (edgeKinds && !edgeKinds.includes(edge.kind as 'imports' | 'calls' | 'extends' | 'implements' | 'uses')) continue;\n if (visited.has(edge.from)) continue;\n visited.add(edge.from);\n\n const node = graph.getNode(edge.from);\n if (node) {\n const depth = current.depth + 1;\n importers.push({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n depth,\n });\n queue.push({ id: edge.from, depth });\n }\n }\n }\n }\n\n // Sort by depth ascending\n importers.sort((a, b) => a.depth - b.depth);\n\n // Apply intent filter if requested\n const filtered = filterByIntent(importers, parsed.data.intent);\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n symbolId,\n importerCount: filtered.length,\n importers: filtered,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport type { SymbolGraph } from '../../core/graph/symbol-graph.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1).optional(),\n direction: z.enum(['ancestors', 'descendants', 'both']).optional().default('both'),\n});\n\nexport function handleGetClassHierarchy(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, direction } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n if (symbolId) {\n return handleRooted(graph, symbolId, direction, masking, staleness, indexedFiles);\n }\n\n return handleFull(graph, masking, staleness, indexedFiles);\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n\n function handleRooted(\n graph: SymbolGraph,\n symbolId: string,\n direction: 'ancestors' | 'descendants' | 'both',\n maskingPort: IMaskingPort,\n stalenessCheck: StalenessCheck | undefined,\n indexedFiles: string[],\n ) {\n if (!graph.hasNode(symbolId)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n type HierarchyEntry = { symbolId: string; name: string; kind: string; file: string; edgeKind: string; depth: number };\n const ancestors: HierarchyEntry[] = [];\n const descendants: HierarchyEntry[] = [];\n\n const isHierarchyEdge = (kind: string) => kind === 'extends' || kind === 'implements';\n\n // Ancestors: follow forward edges (this symbol extends/implements X)\n if (direction === 'ancestors' || direction === 'both') {\n const visited = new Set<string>([symbolId]);\n const queue: Array<{ id: string; depth: number }> = [{ id: symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.getForwardEdges(current.id)) {\n if (!isHierarchyEdge(edge.kind)) continue;\n if (visited.has(edge.to)) continue;\n visited.add(edge.to);\n\n const node = graph.getNode(edge.to);\n if (node) {\n const depth = current.depth + 1;\n ancestors.push({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n depth,\n });\n queue.push({ id: edge.to, depth });\n }\n }\n }\n }\n\n // Descendants: follow reverse edges (X extends/implements this symbol)\n if (direction === 'descendants' || direction === 'both') {\n const visited = new Set<string>([symbolId]);\n const queue: Array<{ id: string; depth: number }> = [{ id: symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.getReverseEdges(current.id)) {\n if (!isHierarchyEdge(edge.kind)) continue;\n if (visited.has(edge.from)) continue;\n visited.add(edge.from);\n\n const node = graph.getNode(edge.from);\n if (node) {\n const depth = current.depth + 1;\n descendants.push({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n depth,\n });\n queue.push({ id: edge.from, depth });\n }\n }\n }\n }\n\n const result: Record<string, unknown> = { symbolId };\n if (direction === 'ancestors' || direction === 'both') result.ancestors = ancestors;\n if (direction === 'descendants' || direction === 'both') result.descendants = descendants;\n\n const payload = maskingPort.mask(JSON.stringify(wrapResponse(result)));\n const content: Array<{ type: 'text'; text: string }> = [];\n if (stalenessCheck) {\n const warning = stalenessCheck.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n\n function handleFull(\n graph: SymbolGraph,\n maskingPort: IMaskingPort,\n stalenessCheck: StalenessCheck | undefined,\n indexedFiles: string[],\n ) {\n const isHierarchyEdge = (kind: string) => kind === 'extends' || kind === 'implements';\n const hierarchyEdges = graph.allEdges().filter((e) => isHierarchyEdge(e.kind));\n\n // Collect all nodes involved in hierarchy edges\n const involved = new Set<string>();\n for (const edge of hierarchyEdges) {\n involved.add(edge.from);\n involved.add(edge.to);\n }\n\n // Find roots: nodes that are targets but never sources of extends/implements (true base classes/interfaces)\n const sources = new Set(hierarchyEdges.map((e) => e.from));\n const roots = [...involved].filter((id) => !sources.has(id));\n\n // If no clear roots, use all involved nodes as potential roots (handles cycles)\n const rootSet = roots.length > 0 ? roots : [...involved];\n\n type TreeNode = { symbolId: string; name: string; kind: string; file: string; edgeKind?: string; children: TreeNode[] };\n\n function buildTree(rootId: string, visited: Set<string>): TreeNode | undefined {\n const node = graph.getNode(rootId);\n if (!node) return undefined;\n visited.add(rootId);\n\n const children: TreeNode[] = [];\n for (const edge of graph.getReverseEdges(rootId)) {\n if (!isHierarchyEdge(edge.kind)) continue;\n if (visited.has(edge.from)) continue;\n const child = buildTree(edge.from, visited);\n if (child) {\n child.edgeKind = edge.kind;\n children.push(child);\n }\n }\n\n return {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n children,\n };\n }\n\n const visited = new Set<string>();\n const hierarchies: TreeNode[] = [];\n for (const rootId of rootSet) {\n if (visited.has(rootId)) continue;\n const tree = buildTree(rootId, visited);\n if (tree) hierarchies.push(tree);\n }\n\n const payload = maskingPort.mask(JSON.stringify(wrapResponse({\n hierarchies,\n totalClasses: involved.size,\n totalEdges: hierarchyEdges.length,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (stalenessCheck) {\n const warning = stalenessCheck.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { PageRankCalculator } from '../../core/importance/pagerank-calculator.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\n\nconst InputSchema = z.object({\n limit: z.number().int().min(1).max(200).optional().default(25),\n kind: z.enum(['function', 'class', 'interface', 'method', 'variable', 'type']).optional(),\n filePattern: z.string().optional(),\n damping: z.number().min(0).max(1).optional().default(0.85),\n});\n\nexport function handleGetSymbolImportance(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const calculator = new PageRankCalculator();\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { limit, kind, filePattern, damping } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n const result = calculator.calculate(graph, { damping, limit: graph.nodeCount, maxIterations: 100 });\n\n // Filter after full PageRank computation (filtering before would distort scores)\n let filtered = result.rankings;\n if (kind) {\n filtered = filtered.filter((e) => e.kind === kind);\n }\n if (filePattern) {\n const lowerPattern = filePattern.toLowerCase();\n filtered = filtered.filter((e) => e.file.toLowerCase().includes(lowerPattern));\n }\n filtered = filtered.slice(0, limit);\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n rankings: filtered,\n totalSymbols: result.totalSymbols,\n iterations: result.iterations,\n converged: result.converged,\n damping,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { BlastRadiusCalculator } from '../../core/blast-radius/blast-radius-calculator.js';\nimport { BoundaryViolationDetector } from '../../core/overlay/boundary-violation-detector.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { loadCoChangeMap } from '../../core/co-change/co-change-analyzer.js';\nimport type { CoChangeMatrix, CoChangeEntry, CommunitySnapshot } from '../../core/types.js';\n\nconst InputSchema = z.object({\n since: z.string().optional().default('HEAD~1'),\n maxFiles: z.number().int().min(1).optional().default(50),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional(),\n});\n\nfunction loadCoChanges(ctxoRoot: string): Map<string, CoChangeEntry[]> | undefined {\n const path = join(ctxoRoot, 'index', 'co-changes.json');\n if (!existsSync(path)) return undefined;\n try {\n const matrix: CoChangeMatrix = JSON.parse(readFileSync(path, 'utf-8'));\n return loadCoChangeMap(matrix);\n } catch {\n return undefined;\n }\n}\n\nfunction computeClustersAffected(\n snapshot: CommunitySnapshot,\n changedSymbolIds: ReadonlySet<string>,\n): Array<{ id: number; label: string; symbolCount: number }> {\n const totals = new Map<number, { label: string; count: number }>();\n for (const entry of snapshot.communities) {\n if (!changedSymbolIds.has(entry.symbolId)) continue;\n const existing = totals.get(entry.communityId);\n if (existing) {\n existing.count += 1;\n } else {\n totals.set(entry.communityId, { label: entry.communityLabel, count: 1 });\n }\n }\n return [...totals.entries()]\n .map(([id, { label, count }]) => ({ id, label, symbolCount: count }))\n .sort((a, b) => b.symbolCount - a.symbolCount || a.id - b.id);\n}\n\nexport function handleGetPrImpact(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n clusterLabelMasker?: ClusterLabelMasker,\n) {\n const calculator = new BlastRadiusCalculator();\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { since, maxFiles, confidence: confFilter } = parsed.data;\n\n // Step 1: Get changed files\n const changedPaths = await git.getChangedFiles(since);\n if (changedPaths.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ since, changedFiles: 0, changedSymbols: 0, totalImpact: 0, riskLevel: 'low', files: [], summary: { confirmedTotal: 0, likelyTotal: 0, potentialTotal: 0, highRiskSymbols: [] } }) }],\n };\n }\n\n // Step 2: Build graph + load co-changes\n const { graph, indexedFiles } = getGraph();\n const coChangeMap = loadCoChanges(ctxoRoot);\n\n // Step 3: Map files → symbols\n const allNodes = graph.allNodes();\n const nodesByFile = new Map<string, typeof allNodes>();\n for (const node of allNodes) {\n const file = node.symbolId.split('::')[0]!;\n let list = nodesByFile.get(file);\n if (!list) { list = []; nodesByFile.set(file, list); }\n list.push(node);\n }\n\n // Step 4: For each changed file, compute blast radius per symbol\n const limitedPaths = changedPaths.slice(0, maxFiles);\n let totalImpact = 0;\n let confirmedTotal = 0;\n let likelyTotal = 0;\n let potentialTotal = 0;\n const highRiskSymbols: string[] = [];\n\n const files: Array<{\n file: string;\n symbols: Array<{\n symbolId: string;\n name: string;\n kind: string;\n blast: {\n impactScore: number;\n confirmedCount: number;\n likelyCount: number;\n potentialCount: number;\n riskScore: number;\n topImpacted: Array<unknown>;\n };\n }>;\n coChangedWith?: string[];\n }> = [];\n\n for (const filePath of limitedPaths) {\n const symbols = nodesByFile.get(filePath);\n if (!symbols || symbols.length === 0) continue;\n\n const fileSymbols: typeof files[0]['symbols'] = [];\n\n for (const sym of symbols) {\n const result = calculator.calculate(graph, sym.symbolId, coChangeMap);\n\n let impacted = result.impactedSymbols;\n if (confFilter) {\n impacted = impacted.filter(s => s.confidence === confFilter);\n }\n\n const impactScore = impacted.length;\n const confirmed = impacted.filter(s => s.confidence === 'confirmed').length;\n const likely = impacted.filter(s => s.confidence === 'likely').length;\n const potential = impacted.filter(s => s.confidence === 'potential').length;\n\n totalImpact += impactScore;\n confirmedTotal += confirmed;\n likelyTotal += likely;\n potentialTotal += potential;\n\n if (result.overallRiskScore > 0.7) {\n highRiskSymbols.push(sym.symbolId);\n }\n\n const topImpacted = impacted\n .sort((a, b) => b.riskScore - a.riskScore)\n .slice(0, 10);\n\n fileSymbols.push({\n symbolId: sym.symbolId,\n name: sym.name,\n kind: sym.kind,\n blast: {\n impactScore,\n confirmedCount: confirmed,\n likelyCount: likely,\n potentialCount: potential,\n riskScore: result.overallRiskScore,\n topImpacted,\n },\n });\n }\n\n // Co-changed files for this file\n let coChangedWith: string[] | undefined;\n if (coChangeMap) {\n const entries = coChangeMap.get(filePath);\n if (entries && entries.length > 0) {\n coChangedWith = entries\n .sort((a, b) => b.frequency - a.frequency)\n .slice(0, 5)\n .map(e => e.file1 === filePath ? e.file2 : e.file1);\n }\n }\n\n files.push({\n file: filePath,\n symbols: fileSymbols,\n ...(coChangedWith ? { coChangedWith } : {}),\n });\n }\n\n // Step 5: Compute risk level\n const maxRisk = files.reduce((max, f) => {\n const fileMax = f.symbols.reduce((m, s) => Math.max(m, s.blast.riskScore), 0);\n return Math.max(max, fileMax);\n }, 0);\n const riskLevel = maxRisk > 0.7 ? 'high' : maxRisk > 0.3 ? 'medium' : 'low';\n\n const changedSymbols = files.reduce((sum, f) => sum + f.symbols.length, 0);\n\n const rawSnapshot = storage.readCommunities();\n const snapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n const history = snapshot ? storage.listCommunityHistory() : [];\n let boundarySection: Record<string, unknown> | undefined;\n let clustersAffected: Array<{ id: number; label: string; symbolCount: number }> | undefined;\n\n if (snapshot) {\n const detector = new BoundaryViolationDetector();\n const violationResult = detector.detect(graph, snapshot, history);\n const changedSymbolIds = new Set<string>();\n for (const file of files) {\n for (const sym of file.symbols) changedSymbolIds.add(sym.symbolId);\n }\n const relevant = violationResult.violations.filter(\n (v) => changedSymbolIds.has(v.from.symbolId) || changedSymbolIds.has(v.to.symbolId),\n );\n boundarySection = {\n violations: relevant,\n confidence: violationResult.confidence,\n snapshotsAvailable: violationResult.snapshotsAvailable,\n ...(violationResult.hint ? { hint: violationResult.hint } : {}),\n };\n clustersAffected = computeClustersAffected(snapshot, changedSymbolIds);\n }\n\n const body: Record<string, unknown> = {\n since,\n changedFiles: files.length,\n changedSymbols,\n totalImpact,\n riskLevel,\n files,\n summary: {\n confirmedTotal,\n likelyTotal,\n potentialTotal,\n highRiskSymbols: highRiskSymbols.slice(0, 10),\n },\n };\n if (boundarySection) body.boundaryViolations = boundarySection;\n if (clustersAffected && clustersAffected.length > 0) body.clustersAffected = clustersAffected;\n\n const stalenessMeta = await buildSnapshotStaleness(storage, git);\n const extras = stalenessMeta ? { snapshotStaleness: stalenessMeta } : undefined;\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import type { ISessionRecorderPort, SessionEvent } from '../../ports/i-session-recorder-port.js';\n\ntype McpToolResult = { content: Array<{ type: 'text'; text: string }> };\ntype McpHandler = (args: Record<string, unknown>) => McpToolResult | Promise<McpToolResult>;\n\n/**\n * Wraps an MCP handler with session recording.\n * Recording is fire-and-forget — errors are swallowed, never affect tool response.\n *\n * @param toolName - MCP tool name (e.g., 'get_logic_slice')\n * @param handler - Original handler function (sync or async)\n * @param recorder - Session recorder (null = recording disabled, zero overhead)\n */\nexport function withRecording(\n toolName: string,\n handler: McpHandler,\n recorder: ISessionRecorderPort | null,\n): McpHandler {\n if (!recorder) return handler;\n\n return (args: Record<string, unknown>) => {\n const start = performance.now();\n const resultOrPromise = handler(args);\n\n // Handle async handlers\n if (resultOrPromise instanceof Promise) {\n return resultOrPromise.then((result) => {\n recordEvent(toolName, args, result, performance.now() - start, recorder);\n return result;\n });\n }\n\n // Sync handler\n const latencyMs = performance.now() - start;\n recordEvent(toolName, args, resultOrPromise, latencyMs, recorder);\n return resultOrPromise;\n };\n}\n\nfunction recordEvent(\n toolName: string,\n args: Record<string, unknown>,\n result: McpToolResult,\n latencyMs: number,\n recorder: ISessionRecorderPort,\n): void {\n try {\n const responseText = result.content\n .filter((c) => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n let truncated = false;\n try {\n const parsed = JSON.parse(responseText);\n truncated = parsed?._meta?.truncated ?? false;\n } catch { /* non-JSON response, ignore */ }\n\n const responseBytes = Buffer.byteLength(responseText, 'utf-8');\n\n // Extract detail level: args.level (number) → 'L1'...'L4'\n const rawLevel = args['level'];\n let detailLevel: SessionEvent['detailLevel'] = null;\n if (typeof rawLevel === 'number' && rawLevel >= 1 && rawLevel <= 4) {\n detailLevel = `L${rawLevel}` as SessionEvent['detailLevel'];\n }\n\n const event: SessionEvent = {\n tool: toolName,\n symbolId: (typeof args['symbolId'] === 'string' ? args['symbolId'] : null),\n detailLevel,\n responseTokens: Math.ceil(responseBytes / 4),\n responseBytes,\n latencyMs,\n truncated,\n };\n\n recorder.record(event);\n } catch {\n // Recording failure must never affect tool response\n }\n}\n","import type { CommunityEntry, CommunitySnapshot, GodNode } from '../types.js';\nimport { makeGlobMatcher } from '../config/load-config.js';\n\n/**\n * Replace sensitive cluster labels with `[masked-cluster-N]`. Labels matching\n * any configured glob (e.g. `internal-*`, `*-vault`, `src/payment-*`) are\n * rewritten deterministically so MCP tool responses never leak org-sensitive\n * directory names to AI agents over the stdio channel.\n *\n * Determinism matters: drift-detector compares labels across snapshots, so\n * the same input label must always produce the same mask. We seed the mask\n * counter from the snapshot's sorted, unique label set.\n */\nexport class ClusterLabelMasker {\n private readonly matcher: (input: string) => boolean;\n private readonly enabled: boolean;\n private readonly labelCache = new Map<string, string>();\n\n constructor(patterns: readonly string[]) {\n this.enabled = patterns.length > 0;\n this.matcher = this.enabled ? makeGlobMatcher([...patterns]) : () => false;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n mask(label: string): string {\n if (!this.enabled) return label;\n if (!this.matcher(label)) return label;\n const cached = this.labelCache.get(label);\n if (cached) return cached;\n const masked = `[masked-cluster-${this.labelCache.size + 1}]`;\n this.labelCache.set(label, masked);\n return masked;\n }\n\n maskSnapshot(snapshot: CommunitySnapshot): CommunitySnapshot {\n if (!this.enabled) return snapshot;\n // Seed the cache in sorted order so output is stable across runs.\n const uniqueLabels = [...new Set(snapshot.communities.map((c) => c.communityLabel))].sort();\n for (const label of uniqueLabels) this.mask(label);\n\n const maskedCommunities: CommunityEntry[] = snapshot.communities.map((entry) => ({\n ...entry,\n communityLabel: this.mask(entry.communityLabel),\n }));\n const maskedGodNodes: GodNode[] = snapshot.godNodes.map((g) => ({ ...g }));\n return { ...snapshot, communities: maskedCommunities, godNodes: maskedGodNodes };\n }\n}\n"],"mappings":"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","names":["z","existsSync","readFileSync","join","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","payload","z","InputSchema","z","z","z","InputSchema","z","log","log","InputSchema","z","payload","content","z","InputSchema","z","results","payload","content","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","visited","z","InputSchema","z","z","InputSchema","z","log","join","existsSync","readFileSync","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapters/transport/http-server-transport.ts","../src/cli/http-port.ts","../src/core/masking/masking-pipeline.ts","../src/adapters/mcp/get-logic-slice.ts","../src/core/logic-slice/logic-slice-query.ts","../src/core/detail-levels/detail-formatter.ts","../src/core/response-envelope.ts","../src/core/intent-filter.ts","../src/adapters/mcp/get-why-context.ts","../src/core/overlay/snapshot-staleness.ts","../src/adapters/mcp/get-change-intelligence.ts","../src/core/change-intelligence/churn-analyzer.ts","../src/core/change-intelligence/health-scorer.ts","../src/adapters/mcp/get-blast-radius.ts","../src/adapters/mcp/get-architectural-overlay.ts","../src/adapters/mcp/get-dead-code.ts","../src/adapters/mcp/get-context-for-task.ts","../src/core/context-assembly/task-context-strategy.ts","../src/core/context-assembly/context-assembler.ts","../src/adapters/mcp/get-ranked-context.ts","../src/core/search/fuzzy-corrector.ts","../src/core/search/search-engine.ts","../src/adapters/mcp/search-symbols.ts","../src/adapters/mcp/get-changed-symbols.ts","../src/adapters/mcp/find-importers.ts","../src/adapters/mcp/get-class-hierarchy.ts","../src/adapters/mcp/get-symbol-importance.ts","../src/adapters/mcp/get-pr-impact.ts","../src/adapters/stats/with-recording.ts","../src/core/overlay/cluster-label-masker.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { startHttpTransport } from './adapters/transport/http-server-transport.js';\nimport { resolveHttpPort } from './cli/http-port.js';\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createLogger } from './core/logger.js';\nimport { SymbolKindSchema, EdgeKindSchema } from './core/types.js';\nimport { SqliteStorageAdapter } from './adapters/storage/sqlite-storage-adapter.js';\nimport { CoChangeReader } from './adapters/storage/co-change-reader.js';\nimport { MaskingPipeline, type MaskingPatternConfig } from './core/masking/masking-pipeline.js';\nimport { handleGetLogicSlice } from './adapters/mcp/get-logic-slice.js';\nimport { handleGetWhyContext } from './adapters/mcp/get-why-context.js';\nimport { handleGetChangeIntelligence } from './adapters/mcp/get-change-intelligence.js';\nimport { SimpleGitAdapter } from './adapters/git/simple-git-adapter.js';\nimport { handleGetBlastRadius } from './adapters/mcp/get-blast-radius.js';\nimport { handleGetArchitecturalOverlay, OVERLAY_MODES } from './adapters/mcp/get-architectural-overlay.js';\nimport { ContextsViewReader } from './adapters/storage/contexts-view-reader.js';\nimport { handleFindDeadCode } from './adapters/mcp/get-dead-code.js';\nimport { handleGetContextForTask } from './adapters/mcp/get-context-for-task.js';\nimport { handleGetRankedContext } from './adapters/mcp/get-ranked-context.js';\nimport { handleSearchSymbols } from './adapters/mcp/search-symbols.js';\nimport { handleGetChangedSymbols } from './adapters/mcp/get-changed-symbols.js';\nimport { handleFindImporters } from './adapters/mcp/find-importers.js';\nimport { handleGetClassHierarchy } from './adapters/mcp/get-class-hierarchy.js';\nimport { handleGetSymbolImportance } from './adapters/mcp/get-symbol-importance.js';\nimport { handleGetPrImpact } from './adapters/mcp/get-pr-impact.js';\nimport { SessionRecorderAdapter } from './adapters/stats/session-recorder-adapter.js';\nimport { withRecording } from './adapters/stats/with-recording.js';\nimport type { ISessionRecorderPort } from './ports/i-session-recorder-port.js';\nimport type { ICoChangePort } from './ports/i-co-change-port.js';\nimport { detectWorkspace } from './adapters/workspace/single-package-workspace.js';\nimport { setWorkspaceMeta } from './core/response-envelope.js';\nimport { loadConfig, maskingClusterLabelPatterns, statsEnabled } from './core/config/load-config.js';\nimport { ClusterLabelMasker } from './core/overlay/cluster-label-masker.js';\n\nconst log = createLogger('ctxo:mcp');\n\nfunction loadMaskingConfig(ctxoRoot: string): MaskingPipeline {\n const jsonConfigPath = join(ctxoRoot, 'masking.json');\n\n // Try JSON masking config first\n if (existsSync(jsonConfigPath)) {\n try {\n const raw = readFileSync(jsonConfigPath, 'utf-8');\n const patterns: MaskingPatternConfig[] = JSON.parse(raw);\n log.info(`Loaded ${patterns.length} custom masking pattern(s)`);\n return MaskingPipeline.fromConfig(patterns);\n } catch (err) {\n log.error(`Failed to load masking config: ${(err as Error).message}`);\n }\n }\n\n return new MaskingPipeline();\n}\n\nfunction readStatsEnabled(ctxoRoot: string): boolean {\n return statsEnabled(loadConfig(ctxoRoot).config);\n}\n\nconst PKG_VERSION: string = (() => {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n})();\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n // HTTP mode — check before CLI routing since --http is a server flag, not a CLI command\n // CTXO_HTTP_PORT only counts when there are no CLI arguments (see resolveHttpPort).\n const httpPortStr = resolveHttpPort(args, process.env);\n\n if (!httpPortStr && args.length > 0) {\n // CLI mode\n const { CliRouter } = await import('./cli/cli-router.js');\n const router = new CliRouter(process.cwd());\n await router.route(args);\n return;\n }\n\n // Initialize adapters\n const ctxoRoot = '.ctxo';\n const workspace = detectWorkspace(process.cwd());\n setWorkspaceMeta({ root: workspace.root });\n const storage = new SqliteStorageAdapter(ctxoRoot, { allowProductionPath: true });\n await storage.init();\n\n const masking = loadMaskingConfig(ctxoRoot);\n const clusterLabelMasker = new ClusterLabelMasker(\n maskingClusterLabelPatterns(loadConfig(ctxoRoot).config),\n );\n const git = new SimpleGitAdapter(process.cwd());\n // One reader for the whole server (every HTTP session too): it caches the co-change matrix by mtime.\n const coChange = new CoChangeReader(ctxoRoot);\n\n // Session recording (opt-out via config.yaml stats.enabled: false)\n const recorder: ISessionRecorderPort | null = readStatsEnabled(ctxoRoot)\n ? new SessionRecorderAdapter(storage.getDb(), () => storage.persist())\n : null;\n\n // Create MCP server\n const server = new McpServer({ name: 'ctxo', version: PKG_VERSION });\n\n // Staleness detection\n const { StalenessDetector } = await import('./core/staleness/staleness-detector.js');\n const staleness = new StalenessDetector(process.cwd(), ctxoRoot);\n\n registerTools(server, storage, masking, git, staleness, recorder, ctxoRoot, clusterLabelMasker, coChange);\n\n // Start MCP server — HTTP mode or stdio mode\n if (httpPortStr) {\n await startHttpTransport(async () => {\n const s = new McpServer({ name: 'ctxo', version: PKG_VERSION });\n registerTools(s, storage, masking, git, staleness, recorder, ctxoRoot, clusterLabelMasker, coChange);\n return s;\n }, parseInt(httpPortStr, 10));\n } else {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n }\n}\n\nfunction registerTools(\n server: McpServer,\n storage: SqliteStorageAdapter,\n masking: MaskingPipeline,\n git: SimpleGitAdapter,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n staleness: any,\n recorder: ISessionRecorderPort | null,\n ctxoRoot = '.ctxo',\n clusterLabelMasker?: ClusterLabelMasker,\n coChange?: ICoChangePort,\n) {\n // Tool annotations — all Ctxo tools are read-only index queries\n const toolAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n } as const;\n\n // The tools that resolve a symbol id through the graph also take the 2-part id of a SQL object.\n const SYMBOL_ID_FORMAT = 'format: file::name::kind; a SQL table, view or procedure also as name::kind, for example dbo.loans::table';\n\n // Register tools — each handler wrapped with session recording\n const logicSliceHandler = withRecording('get_logic_slice', handleGetLogicSlice(storage, masking, staleness, ctxoRoot), recorder);\n const whyContextHandler = withRecording('get_why_context', handleGetWhyContext(storage, git, masking, staleness, ctxoRoot, clusterLabelMasker), recorder);\n const changeIntelligenceHandler = withRecording('get_change_intelligence', handleGetChangeIntelligence(storage, git, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_logic_slice',\n {\n description: 'Retrieve a symbol and all its transitive dependencies as a Logic-Slice. Use this when you need to UNDERSTAND what a symbol depends on (downstream view). L1=signature only, L2=direct deps, L3=full closure, L4=with token budget and symbol annotations (attributes, columns; only L4 includes them). Use `intent` to filter dependencies by keyword (e.g., \"core\", \"adapter\"). All responses include `_meta` with item counts and truncation info. For impact analysis (what BREAKS if this changes), use get_blast_radius instead. For task-specific context, use get_context_for_task.',\n inputSchema: {\n symbolId: z.string().optional().describe(`Single symbol ID (${SYMBOL_ID_FORMAT})`),\n symbolIds: z.array(z.string()).optional().describe('Batch: array of symbol IDs'),\n level: z.number().min(1).max(4).optional().default(3).describe('Detail level (L1=signature, L2=direct deps, L3=full closure, L4=with token budget and annotations)'),\n intent: z.string().optional().describe('Filter dependencies by intent keywords (e.g., \"core\", \"adapter\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => logicSliceHandler(args),\n );\n\n server.registerTool(\n 'get_why_context',\n {\n description: 'Retrieve git commit history intent and anti-pattern warnings (reverts, rollbacks) for a symbol. Use this when you need to understand WHY code was written this way or whether it has a history of problems. Pair with get_change_intelligence for complexity/churn scores.',\n inputSchema: {\n symbolId: z.string().min(1).describe(`The symbol ID (${SYMBOL_ID_FORMAT})`),\n maxCommits: z.number().int().min(1).optional().describe('Limit commit history to N most recent commits'),\n },\n annotations: toolAnnotations,\n },\n (args) => whyContextHandler(args),\n );\n\n server.registerTool(\n 'get_change_intelligence',\n {\n description: 'Retrieve complexity x churn composite score for a symbol — identifies hotspots that are both complex and frequently changed. Use this to prioritize refactoring targets or assess risk before modifying code. For git history details, use get_why_context. For impact scope, use get_blast_radius.',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n },\n annotations: toolAnnotations,\n },\n (args) => changeIntelligenceHandler(args),\n );\n\n const blastRadiusHandler = withRecording('get_blast_radius', handleGetBlastRadius(storage, masking, staleness, ctxoRoot, git, clusterLabelMasker, coChange), recorder);\n\n server.registerTool(\n 'get_blast_radius',\n {\n description: 'BEFORE modifying any symbol (a function, class, method or type, or a SQL table, view or procedure), call this to understand impact. Returns every symbol that depends on the target, directly or transitively, with risk scores that fall with depth. Each dependent is in the tier of its strongest edge: confirmed (`calls`, `extends`, `implements` and the data edges `reads`, `writes`, `executes`), likely (co-changed or `uses`), and potential (`imports`). When co-change history exists, `coChangedFiles` lists up to 10 files that often change together with the target\\'s file but have no dependency path to it (informational: not counted in the tiers or risk scores). Use `confidence` to filter by tier, `intent` to filter by keyword. Large results are auto-truncated with `_meta.hint` for drill-in. For what a symbol DEPENDS ON (downstream), use get_logic_slice instead. For full PR-level analysis, use get_pr_impact.',\n inputSchema: {\n symbolId: z.string().min(1).describe(`The symbol ID (${SYMBOL_ID_FORMAT}). Every definition of a SQL object, and its name::kind id, has the same dependents`),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional().describe('Filter by confidence tier'),\n intent: z.string().optional().describe('Filter impacted symbols by intent keywords (e.g., \"test\", \"adapter\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => blastRadiusHandler(args),\n );\n\n const overlayHandler = withRecording('get_architectural_overlay', handleGetArchitecturalOverlay(storage, masking, staleness, git, clusterLabelMasker, new ContextsViewReader(ctxoRoot)), recorder);\n\n server.registerTool(\n 'get_architectural_overlay',\n {\n description: 'Get the project architecture map. mode \"both\" (default) returns the regex layer map (Domain, Infrastructure, Adapter, Test, Composition, Configuration) plus data-driven Louvain communities; \"regex\" or \"communities\" returns one of them. mode \"contexts\" returns candidate bounded contexts computed by `ctxo index`: file-level clusters of dependencies and co-change with hub files damped. The summary lists every candidate with label, size, cohesion and top terms, plus hubs and name collisions (type names whose shapes differ between candidates). Use this when onboarding to a new codebase, validating layer boundaries, or preparing a context map. For symbol-level analysis, use get_logic_slice or get_blast_radius. Pass candidate: <id> for one candidate in full: its files, cross-candidate edges, co-change pairs, hubs and collisions. The summary also gives each candidate\\'s sharedTableCount and the totals dataResourcesTotal, sharedResourcesTotal and externalResourcesTotal. With candidate: <id>, the detail adds sharedTables (SQL tables and views that files of this candidate and at least one other read or write, with access modes per candidate; up to the first 15 by name, each with up to 10 accesses) and the candidate\\'s own dataResources (up to the first 15 by name, each with its access modes, writer count, the owning candidate when only one writes it, and an external flag for resources that no .sql file defines). Both lists hold fewer entries when the response limit requires; sharedTableCount and dataResourcesTotal keep the full counts, and ctxo contexts --json has the full lists.',\n inputSchema: {\n layer: z.string().optional().describe('Filter by specific layer name'),\n mode: z.enum(OVERLAY_MODES).optional().describe('View to return: \"both\" (default) layers plus communities, \"regex\" layer map, \"communities\" Louvain clusters, \"contexts\" candidate contexts (file level)'),\n candidate: z.number().int().positive().optional().describe('With mode \"contexts\": the id of one candidate to return in full (files, terms, cross edges, co-change pairs, its hubs and collisions); omit for the summary of all candidates'),\n },\n annotations: toolAnnotations,\n },\n (args) => overlayHandler(args),\n );\n\n const deadCodeHandler = withRecording('find_dead_code', handleFindDeadCode(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'find_dead_code',\n {\n description: 'Find unreachable symbols and files that are never imported or called anywhere. Use this during cleanup, refactoring, or before deleting code to confirm it is truly unused. Symbols in .sql files are never reported. Use `intent` to filter results by keyword (e.g., \"adapter\", \"function\"). Large results are auto-truncated with `_meta`. For reverse dependency lookup of a specific symbol, use find_importers instead.',\n inputSchema: {\n includeTests: z.boolean().optional().default(false).describe('Include test files (default test globs and files with a test-framework annotation such as @Test) in analysis (default: exclude)'),\n intent: z.string().optional().describe('Filter dead code results by intent keywords (e.g., \"adapter\", \"function\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => deadCodeHandler(args),\n );\n\n const contextForTaskHandler = withRecording('get_context_for_task', handleGetContextForTask(storage, masking, staleness, ctxoRoot, coChange), recorder);\n\n server.registerTool(\n 'get_context_for_task',\n {\n description: 'Get task-optimized context for a symbol based on what you are about to do. Specify taskType: \"fix\" (bug investigation - includes history + anti-patterns), \"extend\" (add feature - includes deps + blast radius), \"refactor\" (restructure - includes importers + complexity), or \"understand\" (learn - includes full slice + architecture). Context entries that depend on the symbol (its blast radius) carry an optional `confidence` tier: confirmed, likely or potential, where co-change history can raise potential to likely. This is the BEST starting point when you know both the symbol and your intent.',\n inputSchema: {\n symbolId: z.string().min(1).describe(`The symbol ID (${SYMBOL_ID_FORMAT})`),\n taskType: z.enum(['fix', 'extend', 'refactor', 'understand']).describe('Task type determines which context is most relevant'),\n tokenBudget: z.number().optional().default(4000).describe('Max tokens for context (default 4000)'),\n },\n annotations: toolAnnotations,\n },\n (args) => contextForTaskHandler(args),\n );\n\n const rankedContextHandler = withRecording('get_ranked_context', handleGetRankedContext(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_ranked_context',\n {\n description: 'Search and rank symbols by relevance to a natural language query, packed within a token budget. Uses BM25 text matching + PageRank importance scoring. Results include `_meta` with truncation info. Use this when you have a question or topic but do not know which specific symbol to look at. For exact symbol name search, use search_symbols instead.',\n inputSchema: {\n query: z.string().min(1).describe('Search query (matches symbol names)'),\n tokenBudget: z.number().optional().default(4000).describe('Max tokens for results (default 4000)'),\n strategy: z.enum(['combined', 'dependency', 'importance']).optional().default('combined').describe('Ranking strategy'),\n searchMode: z.enum(['fts', 'legacy']).optional().default('fts').describe('\"fts\" (default): BM25 ranking with camelCase splitting, trigram fallback and typo correction; \"legacy\": substring matching; strategy \"importance\" always uses legacy matching'),\n },\n annotations: toolAnnotations,\n },\n (args) => rankedContextHandler(args),\n );\n\n const searchSymbolsHandler = withRecording('search_symbols', handleSearchSymbols(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'search_symbols',\n {\n description: 'Search symbols by exact name or regex pattern across the codebase index. Use this when you know (part of) the symbol name and need to find its ID for use with other tools. For semantic/relevance-based search, use get_ranked_context instead.',\n inputSchema: {\n pattern: z.string().min(1).describe('Search pattern (substring or regex)'),\n kind: SymbolKindSchema.optional().describe('Filter by symbol kind (table, view and procedure come from SQL plugins)'),\n filePattern: z.string().optional().describe('Filter by file path substring'),\n limit: z.number().int().min(1).max(100).optional().default(25).describe('Max results (default 25)'),\n mode: z.enum(['regex', 'fts']).optional().default('regex').describe('\"regex\" (default): case-insensitive regex or substring match on names; \"fts\": BM25 relevance search'),\n },\n annotations: toolAnnotations,\n },\n (args) => searchSymbolsHandler(args),\n );\n\n const changedSymbolsHandler = withRecording('get_changed_symbols', handleGetChangedSymbols(storage, git, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_changed_symbols',\n {\n description: 'Get symbols in recently changed files based on git diff. Use this to see what was modified in recent commits. For full PR risk assessment (changed symbols + blast radius + co-changes), use get_pr_impact instead.',\n inputSchema: {\n since: z.string().optional().default('HEAD~1').describe('Git ref to diff against (default HEAD~1)'),\n maxFiles: z.number().int().min(1).optional().default(50).describe('Max changed files to process (default 50)'),\n },\n annotations: toolAnnotations,\n },\n (args) => changedSymbolsHandler(args),\n );\n\n const findImportersHandler = withRecording('find_importers', handleFindImporters(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'find_importers',\n {\n description: 'Find all symbols that import or depend on a given symbol (reverse dependency / \"who uses this?\"). Use this to check if a symbol is safe to modify or delete. Supports transitive traversal for deep impact chains. Use `intent` to filter by keyword (e.g., \"test\", \"core\"). For forward dependencies (what this symbol uses), use get_logic_slice. For aggregated impact with risk scores, use get_blast_radius.',\n inputSchema: {\n symbolId: z.string().min(1).describe(`The symbol ID (${SYMBOL_ID_FORMAT}). Every definition of a SQL object, and its name::kind id, has the same importers`),\n edgeKinds: z.array(EdgeKindSchema).optional().describe('Filter by edge kinds (reads, writes and executes are data access)'),\n transitive: z.boolean().optional().default(false).describe('Follow transitive reverse edges (default false)'),\n maxDepth: z.number().int().min(1).max(10).optional().default(5).describe('Max BFS depth for transitive mode (default 5)'),\n intent: z.string().optional().describe('Filter importers by intent keywords (e.g., \"test\", \"core\", \"adapter\")'),\n },\n annotations: toolAnnotations,\n },\n (args) => findImportersHandler(args),\n );\n\n const classHierarchyHandler = withRecording('get_class_hierarchy', handleGetClassHierarchy(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_class_hierarchy',\n {\n description: 'Get class inheritance hierarchy — ancestors (extends/implements chain) and descendants (subclasses). Use this when working with OOP code to understand type relationships before modifying a base class or interface. For dependency-based relationships (imports/calls), use get_logic_slice or find_importers.',\n inputSchema: {\n symbolId: z.string().min(1).optional().describe('Root symbol ID (omit for full project hierarchy)'),\n direction: z.enum(['ancestors', 'descendants', 'both']).optional().default('both').describe('Traversal direction (default both)'),\n },\n annotations: toolAnnotations,\n },\n (args) => classHierarchyHandler(args),\n );\n\n const symbolImportanceHandler = withRecording('get_symbol_importance', handleGetSymbolImportance(storage, masking, staleness, ctxoRoot), recorder);\n\n server.registerTool(\n 'get_symbol_importance',\n {\n description: 'Rank symbols by structural importance using PageRank centrality on the dependency graph. Use this to identify the most critical symbols in the codebase — high-PageRank symbols are heavily depended upon and risky to change. For finding unused/unimportant code, use find_dead_code instead.',\n inputSchema: {\n limit: z.number().int().min(1).max(200).optional().default(25).describe('Max results (default 25)'),\n kind: SymbolKindSchema.optional().describe('Filter by symbol kind (table, view and procedure come from SQL plugins)'),\n filePattern: z.string().optional().describe('Filter by file path substring'),\n damping: z.number().min(0).max(1).optional().default(0.85).describe('PageRank damping factor (default 0.85)'),\n },\n annotations: toolAnnotations,\n },\n (args) => symbolImportanceHandler(args),\n );\n\n const prImpactHandler = withRecording('get_pr_impact', handleGetPrImpact(storage, git, masking, staleness, ctxoRoot, clusterLabelMasker, coChange), recorder);\n\n server.registerTool(\n 'get_pr_impact',\n {\n description: 'Analyze the full impact of a PR or recent changes in a SINGLE call — combines changed symbols + blast radius + co-change history into one risk assessment. Results include `_meta` with truncation info. Use this FIRST when reviewing a PR or evaluating recent commits. For single-symbol analysis, use get_blast_radius instead.',\n inputSchema: {\n since: z.string().optional().default('HEAD~1').describe('Git ref to diff against (default HEAD~1)'),\n maxFiles: z.number().int().min(1).optional().default(50).describe('Max changed files to analyze'),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional().describe('Filter impacted symbols by confidence tier'),\n },\n annotations: toolAnnotations,\n },\n (args) => prImpactHandler(args),\n );\n\n // Register empty resource/prompt capabilities to prevent -32601 errors\n // from MCP clients that unconditionally call listResources/listPrompts\n server.resource('ctxo-status', 'ctxo://status', async (uri) => ({\n contents: [{ uri: uri.href, text: 'Ctxo MCP server is running.' }],\n }));\n}\n\nmain().catch((err: unknown) => {\n log.error('Fatal: %s', (err as Error).message);\n process.exit(1);\n});\n","import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { randomUUID } from 'node:crypto';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:http');\n\n/**\n * Factory function that creates and registers all tools on a fresh McpServer.\n * Passed in from the composition root so this adapter doesn't import tool handlers.\n */\nexport type ServerFactory = () => Promise<McpServer>;\n\n/**\n * Starts ctxo MCP over HTTP. Each browser session gets its own McpServer + transport.\n * This avoids the \"already connected\" error when multiple clients connect.\n *\n * Usage:\n * CTXO_HTTP_PORT=3001 ctxo\n * ctxo --http\n */\nexport async function startHttpTransport(\n serverFactory: ServerFactory,\n port: number,\n): Promise<{ httpServer: Server }> {\n // Map of session ID -> { transport, server }\n const sessions = new Map<string, { transport: StreamableHTTPServerTransport; server: McpServer }>();\n\n const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // CORS\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Accept, Mcp-Session-Id');\n res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n // Existing session\n if (sessionId && sessions.has(sessionId)) {\n try {\n await sessions.get(sessionId)!.transport.handleRequest(req, res);\n } catch (err) {\n log.error('Request error: %s', (err as Error).message);\n if (!res.headersSent) { res.writeHead(500); res.end(); }\n }\n return;\n }\n\n // Invalid session\n if (sessionId && !sessions.has(sessionId)) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32001, message: 'Session not found' }, id: null }));\n return;\n }\n\n // New session — create fresh server + transport\n try {\n const server = await serverFactory();\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n });\n\n transport.onclose = () => {\n if (transport.sessionId) {\n sessions.delete(transport.sessionId);\n log.info('Session closed: %s', transport.sessionId);\n }\n };\n\n await server.connect(transport);\n await transport.handleRequest(req, res);\n\n if (transport.sessionId) {\n sessions.set(transport.sessionId, { transport, server });\n log.info('New session: %s (total: %d)', transport.sessionId, sessions.size);\n }\n } catch (err) {\n log.error('Session creation error: %s', (err as Error).message);\n if (!res.headersSent) { res.writeHead(500); res.end(); }\n }\n });\n\n return new Promise((resolve, reject) => {\n httpServer.on('error', reject);\n httpServer.listen(port, () => {\n log.info('MCP HTTP server listening on http://localhost:%d', port);\n process.stderr.write(`[ctxo] MCP HTTP server running at http://localhost:${port}\\n`);\n resolve({ httpServer });\n });\n });\n}\n","const DEFAULT_HTTP_PORT = '3001';\n\n/**\n * The HTTP port the MCP server should listen on, or null for stdio / CLI mode.\n *\n * `CTXO_HTTP_PORT` is honoured only when `ctxo` runs without arguments. With arguments the CLI router\n * runs: an exported variable must not turn `ctxo index --commit HEAD` (the git post-commit hook, which\n * would then never return) or `ctxo <command> --help` into a server. `--http [--port <n>]` selects\n * HTTP mode explicitly; `CTXO_HTTP_PORT` still wins over `--port` when both are given.\n */\nexport function resolveHttpPort(args: readonly string[], env: NodeJS.ProcessEnv): string | null {\n const fromEnv = env['CTXO_HTTP_PORT'] || null;\n if (args.length === 0) return fromEnv;\n if (!args.includes('--http')) return null;\n\n const portFlag = args.indexOf('--port');\n return fromEnv || (portFlag === -1 ? undefined : args[portFlag + 1]) || DEFAULT_HTTP_PORT;\n}\n","const DEFAULT_PATTERNS: Array<{ regex: RegExp; label: string }> = [\n // AWS Access Key IDs (AKIA...)\n { regex: /AKIA[0-9A-Z]{16}/g, label: 'AWS_KEY' },\n // AWS Secret Access Keys (40 char base64 containing / or +). The `[A-Za-z0-9=]*[/+]`\n // lookahead is scoped to the token itself (not `.*`, which would reach a later `/` in\n // the surrounding text, e.g. a file path in JSON, and wrongly redact hex-only git SHAs).\n { regex: /(?<![A-Za-z0-9/+])(?=[A-Za-z0-9/+=]{40}(?![A-Za-z0-9/+=]))(?=[A-Za-z0-9=]*[/+])[A-Za-z0-9/+=]{40}/g, label: 'AWS_SECRET' },\n // JWT tokens (eyJ...)\n { regex: /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, label: 'JWT' },\n // Private IPv4 (10.x.x.x, 172.16-31.x.x, 192.168.x.x)\n { regex: /\\b(?:10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|172\\.(?:1[6-9]|2\\d|3[01])\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3})\\b/g, label: 'PRIVATE_IP' },\n // Private IPv6 (fc00::/7)\n { regex: /\\b[fF][cCdD][0-9a-fA-F]{0,2}(?::[0-9a-fA-F]{1,4}){1,7}\\b/g, label: 'PRIVATE_IPV6' },\n // Environment variable assignments for secrets\n { regex: /(?:_SECRET|_KEY|_TOKEN|_PASSWORD)\\s*=\\s*['\"]?[^\\s'\"]{8,}['\"]?/g, label: 'ENV_SECRET' },\n // GCP service account keys\n { regex:/\"private_key\"\\s*:\\s*\"-----BEGIN[^\"]+\"/g, label: 'GCP_KEY' },\n // Azure connection strings\n { regex: /AccountKey=[A-Za-z0-9+/=]{20,}/g, label: 'AZURE_KEY' },\n];\n\nexport interface MaskingPatternConfig {\n readonly pattern: string;\n readonly flags?: string;\n readonly label: string;\n}\n\nexport class MaskingPipeline {\n private readonly patterns: Array<{ regex: RegExp; label: string }>;\n\n constructor(additionalPatterns: Array<{ regex: RegExp; label: string }> = []) {\n // Clone RegExp objects to avoid shared mutable lastIndex state\n this.patterns = [...DEFAULT_PATTERNS, ...additionalPatterns].map(({ regex, label }) => ({\n regex: new RegExp(regex.source, regex.flags),\n label,\n }));\n }\n\n static fromConfig(configPatterns: MaskingPatternConfig[]): MaskingPipeline {\n const additional: Array<{ regex: RegExp; label: string }> = [];\n\n for (const { pattern, flags, label } of configPatterns) {\n try {\n additional.push({ regex: new RegExp(pattern, flags ?? 'g'), label });\n } catch (err) {\n console.error(`[ctxo:masking] Invalid regex pattern \"${pattern}\": ${(err as Error).message}`);\n }\n }\n\n return new MaskingPipeline(additional);\n }\n\n mask(text: string): string {\n if (text.length === 0) return text;\n\n let result = text;\n for (const { regex, label } of this.patterns) {\n result = result.replace(regex, `[REDACTED:${label}]`);\n }\n return result;\n }\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { SymbolGraph } from '../../core/graph/symbol-graph.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport { LogicSliceQuery } from '../../core/logic-slice/logic-slice-query.js';\nimport { DetailFormatter } from '../../core/detail-levels/detail-formatter.js';\nimport { DetailLevelSchema, type FileIndex } from '../../core/types.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1).optional(),\n symbolIds: z.array(z.string().min(1)).optional(),\n level: DetailLevelSchema.optional().default(3),\n intent: z.string().optional().describe('Filter dependencies by intent keywords (e.g., \"core\", \"adapter\", \"storage\")'),\n}).refine(\n (data) => data.symbolId || (data.symbolIds && data.symbolIds.length > 0),\n { message: 'Either symbolId or symbolIds must be provided' },\n);\n\nexport type GetLogicSliceInput = z.infer<typeof InputSchema>;\n\nexport function getLogicSliceInputSchema() {\n return {\n type: 'object' as const,\n properties: {\n symbolId: { type: 'string' as const, description: 'The symbol ID to retrieve (format: file::name::kind)' },\n level: { type: 'number' as const, enum: [1, 2, 3, 4], description: 'Detail level (1=minimal, 4=full)', default: 3 },\n },\n required: ['symbolId'] as const,\n };\n}\n\nexport function buildGraphFromStorage(storage: IStoragePort): SymbolGraph {\n const graph = new SymbolGraph();\n for (const sym of storage.getAllSymbols()) {\n graph.addNode(sym);\n }\n for (const edge of storage.getAllEdges()) {\n graph.addEdge(edge);\n }\n return graph;\n}\n\n/**\n * Build graph + return file list from a single JSON index read.\n * Returning both from one source prevents staleness warnings from diverging\n * from the graph data (see issue #40).\n */\nexport function buildGraphAndFilesFromJsonIndex(\n ctxoRoot: string,\n): { graph: SymbolGraph; indexedFiles: string[] } {\n const reader = new JsonIndexReader(ctxoRoot);\n const indices = reader.readAll();\n return { graph: buildGraphFromIndices(indices), indexedFiles: indices.map((fileIndex) => fileIndex.file) };\n}\n\n/** Build a graph from file indices already read: every node first, then every edge. */\nexport function buildGraphFromIndices(indices: readonly FileIndex[]): SymbolGraph {\n const graph = new SymbolGraph();\n for (const fileIndex of indices) {\n for (const sym of fileIndex.symbols) {\n graph.addNode(sym);\n }\n }\n for (const fileIndex of indices) {\n for (const edge of fileIndex.edges) {\n graph.addEdge(edge);\n }\n }\n return graph;\n}\n\n/**\n * Build graph directly from JSON index files (bypasses SQLite cache).\n * Always reads fresh data from disk — works even if ctxo index ran in another process.\n */\nexport function buildGraphFromJsonIndex(ctxoRoot: string): SymbolGraph {\n return buildGraphAndFilesFromJsonIndex(ctxoRoot).graph;\n}\n\n/**\n * Returns graph + indexedFiles from JSON index, falling back to SQLite storage\n * when JSON is empty. Keeps graph data and staleness check file list aligned.\n */\nexport function getGraphAndFiles(\n ctxoRoot: string,\n storage: IStoragePort,\n): { graph: SymbolGraph; indexedFiles: string[] } {\n const json = buildGraphAndFilesFromJsonIndex(ctxoRoot);\n if (json.graph.nodeCount > 0) return json;\n return { graph: buildGraphFromStorage(storage), indexedFiles: storage.listIndexedFiles() };\n}\n\nexport interface StalenessCheck {\n check(indexedFiles: readonly string[]): { message: string } | undefined;\n}\n\nexport function handleGetLogicSlice(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const query = new LogicSliceQuery();\n const formatter = new DetailFormatter();\n\n // Build graph fresh on each call — try JSON index first (always up to date),\n // fall back to SQLite storage (for tests and when JSON not available).\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n const handler = (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, symbolIds, level, intent } = parsed.data;\n const ids = symbolIds ?? (symbolId ? [symbolId] : []);\n\n const { graph, indexedFiles } = getGraph();\n\n // Batch support: query multiple symbols\n const results = [];\n for (const id of ids) {\n const slice = query.getLogicSlice(graph, id);\n if (slice) {\n const formatted = formatter.format(slice, level) as unknown as Record<string, unknown>;\n // Apply intent filter to dependencies if present\n if (intent && Array.isArray(formatted['dependencies'])) {\n formatted['dependencies'] = filterByIntent(\n formatted['dependencies'] as Record<string, unknown>[],\n intent,\n );\n }\n results.push(formatted);\n } else {\n results.push({ found: false, symbolId: id, hint: 'Symbol not found. Run \"ctxo index\".' });\n }\n }\n\n // Single symbol: return directly. Batch: return array.\n const responseData = ids.length === 1 ? results[0] : { batch: true, results };\n\n // Apply masking\n const payload = masking.mask(JSON.stringify(wrapResponse(responseData as Record<string, unknown>)));\n\n // Check staleness\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) {\n content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n\n return handler;\n}\n","import type { SymbolNode, GraphEdge, LogicSliceResult } from '../types.js';\nimport type { SymbolGraph } from '../graph/symbol-graph.js';\n\nexport class LogicSliceQuery {\n getLogicSlice(\n graph: SymbolGraph,\n symbolId: string,\n maxDepth: number = Infinity,\n ): LogicSliceResult | undefined {\n // Resolve the id once and walk from the node it names: a 2-part data id walks its winning definition, and\n // a full id of another copy walks that copy's own edges.\n const root = graph.getNode(symbolId);\n if (!root) return undefined;\n\n const visited = new Set<string>([root.symbolId]);\n const dependencies: SymbolNode[] = [];\n const collectedEdges: GraphEdge[] = [];\n\n // BFS transitive closure\n const queue: Array<{ id: string; depth: number }> = [{ id: root.symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (current.depth >= maxDepth) continue;\n\n const edges = graph.getForwardEdges(current.id);\n for (const edge of edges) {\n const depNode = graph.getNode(edge.to);\n // Only include edges whose target node exists in the graph\n if (!depNode) continue;\n\n collectedEdges.push(edge);\n\n if (visited.has(edge.to)) continue;\n visited.add(edge.to);\n\n dependencies.push(depNode);\n queue.push({ id: edge.to, depth: current.depth + 1 });\n }\n }\n\n return { root, dependencies, edges: collectedEdges };\n }\n}\n","import type {\n LogicSliceResult,\n FormattedSlice,\n DetailLevel,\n SymbolNode,\n GraphEdge,\n} from '../types.js';\n\nconst L1_MAX_LINES = 150;\nconst L4_TOKEN_BUDGET = 8000;\n\n/**\n * `node` without its `annotations`. Node output leaves them out everywhere except logic slice L4, which\n * carries them unchanged: up to 20 attributes per C# symbol plus whole column lists would shrink every\n * other slice before its dependencies.\n *\n * Only a graph built from the JSON index has annotations. The SQLite fallback stores none, so even L4\n * carries none when the graph comes from there.\n */\nexport function withoutAnnotations(node: SymbolNode): SymbolNode {\n if (node.annotations === undefined) return node;\n const { annotations: _annotations, ...rest } = node;\n return rest;\n}\n\nexport class DetailFormatter {\n format(slice: LogicSliceResult, level: DetailLevel): FormattedSlice {\n switch (level) {\n case 1:\n return this.formatL1(slice);\n case 2:\n return this.formatL2(slice);\n case 3:\n return this.formatL3(slice);\n case 4:\n return this.formatL4(slice);\n }\n }\n\n private formatL1(slice: LogicSliceResult): FormattedSlice {\n const root = slice.root;\n const lineCount = root.endLine - root.startLine + 1;\n\n const clampedRoot: SymbolNode = lineCount > L1_MAX_LINES\n ? { ...root, endLine: root.startLine + L1_MAX_LINES - 1 }\n : root;\n\n return {\n root: withoutAnnotations(clampedRoot),\n dependencies: [],\n edges: [],\n level: 1,\n levelDescription: 'L1: Root symbol signature only (no dependencies)',\n ...(lineCount > L1_MAX_LINES ? { truncation: { truncated: true as const, reason: 'token_budget_exceeded' as const } } : {}),\n };\n }\n\n private formatL2(slice: LogicSliceResult): FormattedSlice {\n // L2: root + depth-1 direct dependencies\n const directEdges = slice.edges.filter((e) => e.from === slice.root.symbolId);\n const directDepIds = new Set(directEdges.map((e) => e.to));\n const directDeps = slice.dependencies.filter((d) => directDepIds.has(d.symbolId));\n\n return {\n root: withoutAnnotations(slice.root),\n dependencies: directDeps.map(withoutAnnotations),\n edges: directEdges,\n level: 2,\n levelDescription: 'L2: Root + direct dependencies (depth 1)',\n };\n }\n\n private formatL3(slice: LogicSliceResult): FormattedSlice {\n return {\n root: withoutAnnotations(slice.root),\n dependencies: slice.dependencies.map(withoutAnnotations),\n edges: slice.edges,\n level: 3,\n levelDescription: 'L3: Full transitive dependency closure',\n };\n }\n\n private formatL4(slice: LogicSliceResult): FormattedSlice {\n // L4: full closure with token budget enforcement. Nodes keep their annotations (see withoutAnnotations;\n // a graph from the SQLite fallback has none to keep).\n const estimatedTokens = this.estimateTokens(slice);\n\n if (estimatedTokens > L4_TOKEN_BUDGET) {\n // Truncate dependencies to fit budget\n const truncated = this.truncateToTokenBudget(slice, L4_TOKEN_BUDGET);\n return {\n ...truncated,\n level: 4,\n levelDescription: 'L4: Full closure with 8K token budget (truncated)',\n truncation: {\n truncated: true,\n reason: 'token_budget_exceeded',\n },\n };\n }\n\n return {\n root: slice.root,\n dependencies: slice.dependencies,\n edges: slice.edges,\n level: 4,\n levelDescription: 'L4: Full closure with 8K token budget',\n };\n }\n\n private estimateTokens(slice: LogicSliceResult): number {\n // Rough estimate: ~4 chars per token\n let chars = this.symbolCharEstimate(slice.root);\n for (const dep of slice.dependencies) {\n chars += this.symbolCharEstimate(dep);\n }\n for (const edge of slice.edges) {\n chars += (edge.from.length + edge.to.length + edge.kind.length + 10);\n }\n return Math.ceil(chars / 4);\n }\n\n private symbolCharEstimate(node: SymbolNode): number {\n // Use byte offsets for exact size when available, otherwise estimate from line count\n if (node.startOffset !== undefined && node.endOffset !== undefined) {\n return node.endOffset - node.startOffset;\n }\n return (node.endLine - node.startLine + 1) * 40;\n }\n\n private truncateToTokenBudget(\n slice: LogicSliceResult,\n budget: number,\n ): { root: SymbolNode; dependencies: SymbolNode[]; edges: GraphEdge[] } {\n let currentTokens = this.symbolCharEstimate(slice.root) / 4;\n const deps: SymbolNode[] = [];\n const includedIds = new Set<string>([slice.root.symbolId]);\n\n for (const dep of slice.dependencies) {\n const depTokens = this.symbolCharEstimate(dep) / 4;\n if (currentTokens + depTokens > budget) break;\n currentTokens += depTokens;\n deps.push(dep);\n includedIds.add(dep.symbolId);\n }\n\n const edges = slice.edges.filter(\n (e) => includedIds.has(e.from) && includedIds.has(e.to),\n );\n\n return { root: slice.root, dependencies: deps, edges };\n }\n}\n","/**\n * Response envelope — adds _meta field and truncates large payloads.\n *\n * Applied between payload construction and masking in every MCP handler.\n * Threshold default: 8192 bytes (~8KB). Configurable via CTXO_RESPONSE_LIMIT env.\n */\n\nconst DEFAULT_THRESHOLD = 8192;\n\nexport interface ResponseMeta {\n totalItems: number;\n returnedItems: number;\n truncated: boolean;\n totalBytes: number;\n hint?: string;\n /**\n * Workspace descriptor (forward-compat for Monorepo Workspace PRD).\n * v0.7: `root` is set at startup; `package` is always undefined.\n */\n workspace?: {\n root: string;\n package?: string;\n };\n /**\n * How far the community snapshot has drifted from current git HEAD.\n * Only populated on cluster-aware MCP tools (overlay, blast-radius,\n * why-context, pr-impact). Tells AI agents whether the cluster / drift /\n * boundary data they are about to act on is fresh or N commits stale.\n */\n snapshotStaleness?: {\n snapshotCommit: string;\n currentHeadCommit?: string;\n commitsBehind?: number;\n hint?: string;\n };\n}\n\nlet workspaceMeta: ResponseMeta['workspace'] | undefined;\n\n/**\n * Set the workspace descriptor attached to every subsequent `_meta` envelope.\n * Called once at composition root after `detectWorkspace()` resolves.\n */\nexport function setWorkspaceMeta(workspace: ResponseMeta['workspace']): void {\n workspaceMeta = workspace;\n}\n\nexport function getWorkspaceMeta(): ResponseMeta['workspace'] | undefined {\n return workspaceMeta;\n}\n\n/**\n * Known array field names in tool responses that can be truncated.\n * Maps field name → drill-in hint for the LLM.\n */\nconst TRUNCATABLE_FIELDS: Record<string, string> = {\n impactedSymbols: 'Use search_symbols to find specific impacted symbols, or get_blast_radius with a confidence filter.',\n importers: 'Use find_importers with edgeKinds filter or maxDepth to narrow results.',\n deadSymbols: 'Use search_symbols with kind filter to find specific dead symbols.',\n unusedExports: 'Use find_importers to check specific symbols.',\n results: 'Use search_symbols or get_ranked_context with a narrower query.',\n rankings: 'Use get_symbol_importance with kind or filePattern filter to narrow results.',\n context: 'Use get_context_for_task with a smaller tokenBudget.',\n hierarchies: 'Use get_class_hierarchy with a specific symbolId.',\n scaffolding: 'Review the scaffolding markers directly in the listed files.',\n deadFiles: 'Use find_dead_code with includeTests to adjust scope.',\n files: 'Use get_changed_symbols with a narrower since ref or smaller maxFiles.',\n clusters: 'Use get_architectural_overlay with mode=communities and layer filter for a focused view.',\n candidates:\n 'Use get_architectural_overlay with mode=contexts and candidate: <id> for one candidate in full, or ctxo contexts --json for the full contexts view.',\n clustersAffected: 'Use get_blast_radius on individual changed symbols for per-cluster impact.',\n};\n\n/** The response limit in bytes: `CTXO_RESPONSE_LIMIT` when it is a positive integer, else 8192. */\nexport function getThreshold(): number {\n const env = process.env['CTXO_RESPONSE_LIMIT'];\n if (env) {\n const n = parseInt(env, 10);\n if (!isNaN(n) && n > 0) return n;\n }\n return DEFAULT_THRESHOLD;\n}\n\n/**\n * Find the largest array field in a payload object that is in our truncatable list.\n */\nfunction findTruncatableArray(data: Record<string, unknown>): { key: string; arr: unknown[] } | null {\n let best: { key: string; arr: unknown[] } | null = null;\n for (const [key, value] of Object.entries(data)) {\n if (Array.isArray(value) && key in TRUNCATABLE_FIELDS) {\n if (!best || value.length > best.arr.length) {\n best = { key, arr: value };\n }\n }\n }\n return best;\n}\n\n/**\n * Wrap a payload object with _meta and truncate if needed.\n *\n * @param data - The raw response object (before JSON.stringify)\n * @returns The data object with _meta added (and arrays truncated if over threshold)\n */\nexport interface WrapResponseExtras {\n snapshotStaleness?: ResponseMeta['snapshotStaleness'];\n}\n\nfunction buildMeta(\n base: Omit<ResponseMeta, 'workspace' | 'snapshotStaleness'>,\n extras?: WrapResponseExtras,\n): ResponseMeta {\n const meta: ResponseMeta = workspaceMeta ? { ...base, workspace: workspaceMeta } : base;\n if (extras?.snapshotStaleness) {\n return { ...meta, snapshotStaleness: extras.snapshotStaleness };\n }\n return meta;\n}\n\nexport function wrapResponse(\n data: Record<string, unknown>,\n extras?: WrapResponseExtras,\n): Record<string, unknown> {\n const threshold = getThreshold();\n const fullJson = JSON.stringify(data);\n const totalBytes = Buffer.byteLength(fullJson, 'utf-8');\n\n const truncatable = findTruncatableArray(data);\n const totalItems = truncatable ? truncatable.arr.length : 0;\n\n // Under threshold — return with _meta, no truncation\n if (totalBytes <= threshold) {\n return {\n ...data,\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: totalItems,\n truncated: false,\n totalBytes,\n },\n extras,\n ),\n };\n }\n\n // Over threshold — truncate the largest array\n if (!truncatable || truncatable.arr.length <= 1) {\n return {\n ...data,\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: totalItems,\n truncated: false,\n totalBytes,\n },\n extras,\n ),\n };\n }\n\n // Binary search for the number of items that fit within threshold\n let lo = 1;\n let hi = truncatable.arr.length;\n\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n const trial = {\n ...data,\n [truncatable.key]: truncatable.arr.slice(0, mid),\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: mid,\n truncated: true,\n totalBytes,\n hint: TRUNCATABLE_FIELDS[truncatable.key],\n },\n extras,\n ),\n };\n const size = Buffer.byteLength(JSON.stringify(trial), 'utf-8');\n if (size <= threshold) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n\n const hint = TRUNCATABLE_FIELDS[truncatable.key];\n\n return {\n ...data,\n [truncatable.key]: truncatable.arr.slice(0, lo),\n _meta: buildMeta(\n {\n totalItems,\n returnedItems: lo,\n truncated: true,\n totalBytes,\n hint,\n },\n extras,\n ),\n };\n}\n","/**\n * Intent-based result filtering for MCP tool responses.\n *\n * When an `intent` string is provided, filters array results to only include\n * entries whose text fields (symbolId, file, name, kind, edgeKind, reason)\n * match at least one keyword from the intent.\n *\n * Keywords are extracted by splitting the intent on whitespace and lowercasing.\n * A match is found when any keyword appears as a substring of any text field.\n */\n\n/**\n * Extract lowercase keywords from an intent string.\n * Filters out very short words (< 2 chars) to avoid noise.\n */\nfunction extractKeywords(intent: string): string[] {\n return intent\n .toLowerCase()\n .split(/\\s+/)\n .filter(w => w.length >= 2);\n}\n\n/**\n * Check if any keyword matches any of the given text values (case-insensitive substring).\n */\nfunction matchesAny(keywords: string[], values: string[]): boolean {\n const lower = values.map(v => v.toLowerCase());\n return keywords.some(kw => lower.some(v => v.includes(kw)));\n}\n\n/**\n * Collect all string values from a record for matching.\n * Extracts from known fields: symbolId, file, name, kind, edgeKind, edgeKinds, reason, confidence.\n */\nfunction collectMatchableStrings(entry: Record<string, unknown>): string[] {\n const strings: string[] = [];\n for (const key of ['symbolId', 'file', 'name', 'kind', 'edgeKind', 'reason', 'confidence']) {\n const v = entry[key];\n if (typeof v === 'string') strings.push(v);\n }\n const edgeKinds = entry['edgeKinds'];\n if (Array.isArray(edgeKinds)) {\n for (const ek of edgeKinds) {\n if (typeof ek === 'string') strings.push(ek);\n }\n }\n return strings;\n}\n\n/**\n * Filter an array of result entries by intent keywords.\n * Returns only entries where at least one keyword matches a text field.\n *\n * @param items - Array of result objects\n * @param intent - User intent string (e.g., \"test coverage\", \"adapter\", \"security\")\n * @returns Filtered array (empty if no matches)\n */\nexport function filterByIntent<T extends Record<string, unknown>>(\n items: T[],\n intent: string | undefined,\n): T[] {\n if (!intent || intent.trim().length === 0) return items;\n\n const keywords = extractKeywords(intent);\n if (keywords.length === 0) return items;\n\n return items.filter(item => {\n const strings = collectMatchableStrings(item);\n return matchesAny(keywords, strings);\n });\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { RevertDetector } from '../../core/why-context/revert-detector.js';\nimport { DriftDetector } from '../../core/overlay/drift-detector.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport type { AntiPattern, CommitIntent, FileIndex } from '../../core/types.js';\nimport type { SymbolGraph } from '../../core/graph/symbol-graph.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { buildGraphFromIndices, buildGraphFromStorage } from './get-logic-slice.js';\n\n/** The graph of the committed index, or of the SQLite storage when that has no symbols (as `getGraphAndFiles`). */\nfunction graphOf(indices: readonly FileIndex[], storage: IStoragePort): SymbolGraph {\n const graph = buildGraphFromIndices(indices);\n return graph.nodeCount > 0 ? graph : buildGraphFromStorage(storage);\n}\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1),\n maxCommits: z.number().int().min(1).optional(),\n});\n\nexport function handleGetWhyContext(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n clusterLabelMasker?: ClusterLabelMasker,\n) {\n const revertDetector = new RevertDetector();\n const indexReader = new JsonIndexReader(ctxoRoot);\n const driftDetector = new DriftDetector();\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId: inputId, maxCommits } = parsed.data;\n\n // Committed index first (FR16: anti-patterns persist in committed index)\n const indices = indexReader.readAll();\n\n // Resolve the id once. An exact id is looked up directly; anything else (a 2-part data id, a fuzzy id)\n // through the graph, so a 2-part id reads the history of its winning definition's file.\n const symbolId = storage.getSymbolById(inputId)\n ? inputId\n : graphOf(indices, storage).getNode(inputId)?.symbolId;\n if (!symbolId) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n const filePath = symbolId.split('::')[0]!;\n const fileIndex = indices.find((i) => i.file === filePath);\n\n let commitHistory: CommitIntent[];\n let antiPatterns: AntiPattern[];\n\n if (fileIndex && fileIndex.intent.length > 0) {\n commitHistory = fileIndex.intent;\n antiPatterns = fileIndex.antiPatterns;\n } else {\n // Fallback: compute on-demand from git\n const commits = await git.getCommitHistory(filePath);\n commitHistory = commits.map((c) => ({\n hash: c.hash,\n message: c.message,\n date: c.date,\n kind: 'commit' as const,\n }));\n antiPatterns = revertDetector.detect(commits);\n }\n\n // Query-time limit: slice to maxCommits if provided\n const limitedHistory = maxCommits ? commitHistory.slice(0, maxCommits) : commitHistory;\n\n // Assemble result — separation of concerns: no changeIntelligence here\n // Use get_change_intelligence tool for complexity/churn scoring\n const responsePayload: Record<string, unknown> = {\n commitHistory: limitedHistory,\n antiPatternWarnings: antiPatterns,\n };\n\n if (antiPatterns.length > 0) {\n responsePayload.warningBadge = '⚠ Anti-pattern detected';\n }\n\n const rawSnapshot = storage.readCommunities();\n const currentSnapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n if (currentSnapshot) {\n const history = storage.listCommunityHistory();\n const drift = driftDetector.detect(currentSnapshot, history);\n const relevant = drift.events.filter((event) => event.symbolId === symbolId);\n if (relevant.length > 0 || drift.hint) {\n responsePayload.driftSignals = {\n events: relevant,\n confidence: drift.confidence,\n snapshotsAvailable: drift.snapshotsAvailable,\n distinctCommits: drift.distinctCommits,\n stabilitySummary: drift.stability,\n ...(drift.hint ? { hint: drift.hint } : {}),\n };\n }\n }\n\n const stalenessMeta = await buildSnapshotStaleness(storage, git);\n if (stalenessMeta) {\n responsePayload._meta = { snapshotStaleness: stalenessMeta };\n }\n\n const payload = masking.mask(JSON.stringify(responsePayload));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(storage.listIndexedFiles());\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import type { CommunitySnapshot } from '../types.js';\n\nexport interface SnapshotStaleness {\n readonly snapshotCommit: string;\n readonly currentHeadCommit?: string;\n readonly commitsBehind?: number;\n readonly hint?: string;\n}\n\n/**\n * Narrow structural views of the capabilities this module needs from the\n * storage and git adapters. We declare them here (in core/) rather than\n * importing the full IStoragePort / IGitPort interfaces from ports/ so the\n * hexagonal boundary holds: core/ knows nothing about ports/. Callers pass\n * their existing port-typed adapters — they satisfy these shapes structurally.\n */\nexport interface SnapshotStalenessStorage {\n readCommunities(): CommunitySnapshot | undefined;\n}\n\nexport interface SnapshotStalenessGit {\n getHeadSha?(): Promise<string | undefined> | string | undefined;\n countCommitsBetween?(sha: string): Promise<number | undefined> | number | undefined;\n}\n\n/**\n * Summarise how far the persisted community snapshot has drifted from the\n * current git HEAD. Callers merge this into MCP `_meta` so AI agents know\n * whether cluster / drift / boundary data reflects the HEAD they are about\n * to edit or a stale history point.\n *\n * Returns undefined when no snapshot exists (nothing to report) or when git\n * is unavailable and no comparison is possible.\n */\nexport async function buildSnapshotStaleness(\n storage: SnapshotStalenessStorage,\n git: SnapshotStalenessGit,\n): Promise<SnapshotStaleness | undefined> {\n const snapshot = storage.readCommunities();\n if (!snapshot || !snapshot.commitSha || snapshot.commitSha === 'nocommit') return undefined;\n\n const getHead = git.getHeadSha?.bind(git);\n const countBetween = git.countCommitsBetween?.bind(git);\n const head = getHead ? await getHead() : undefined;\n\n if (!head) {\n // Git unavailable — still report the snapshot anchor so callers know what\n // commit the snapshot was computed against.\n return { snapshotCommit: snapshot.commitSha };\n }\n\n if (head === snapshot.commitSha) {\n return {\n snapshotCommit: snapshot.commitSha,\n currentHeadCommit: head,\n commitsBehind: 0,\n };\n }\n\n const behind = countBetween ? await countBetween(snapshot.commitSha) : undefined;\n\n const result: SnapshotStaleness = {\n snapshotCommit: snapshot.commitSha,\n currentHeadCommit: head,\n ...(typeof behind === 'number' ? { commitsBehind: behind } : {}),\n };\n\n if (typeof behind === 'number' && behind > 0) {\n return {\n ...result,\n hint: `Community snapshot is ${behind} commit${behind === 1 ? '' : 's'} behind HEAD. Run \\`ctxo index\\` to refresh cluster / drift / boundary data.`,\n };\n }\n if (behind === undefined) {\n // HEAD resolved but snapshot sha unknown to git (force-push, rebased, etc.).\n return {\n ...result,\n hint: 'Snapshot commit is not reachable from HEAD — it may have been rebased away. Run `ctxo index` to re-anchor.',\n };\n }\n return result;\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ChurnAnalyzer } from '../../core/change-intelligence/churn-analyzer.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { HealthScorer } from '../../core/change-intelligence/health-scorer.js';\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1),\n});\n\nexport function handleGetChangeIntelligence(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const churnAnalyzer = new ChurnAnalyzer();\n const healthScorer = new HealthScorer();\n const indexReader = new JsonIndexReader(ctxoRoot);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId } = parsed.data;\n\n const symbol = storage.getSymbolById(symbolId);\n if (!symbol) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n const filePath = symbolId.split('::')[0]!;\n const normalizePath = (p: string) => p.replace(/\\\\/g, '/');\n\n // Get churn data for all files in parallel\n const allFiles = storage.listIndexedFiles();\n const churnResults = await Promise.all(allFiles.map((f) => git.getFileChurn(f)));\n\n const maxChurn = Math.max(1, ...churnResults.map((c) => c.commitCount));\n const targetChurn = churnResults.find((c) => normalizePath(c.filePath) === normalizePath(filePath));\n const commitCount = targetChurn?.commitCount ?? 0;\n\n const normalizedChurn = churnAnalyzer.normalize(commitCount, maxChurn);\n\n // Read cyclomatic complexity from committed index\n const indices = indexReader.readAll();\n const fileIndex = indices.find((i) => i.file === filePath);\n const complexityEntries = fileIndex?.complexity ?? [];\n\n // Storage knows the symbol but its JSON index is missing on disk — SQLite and\n // `.ctxo/index/` have drifted. Surface it so the caller does not mistake the\n // fallback `complexity: 0` score for a real \"low risk\" verdict.\n const indexMissingWarning = !fileIndex\n ? `JSON index for ${filePath} not found on disk — complexity falls back to 0. Run \"ctxo index\" to resync.`\n : undefined;\n\n // Exact match first, then aggregate (max of methods for class-level queries)\n let cyclomatic = complexityEntries.find((c) => c.symbolId === symbolId)?.cyclomatic;\n if (cyclomatic === undefined) {\n // For class/file: use max complexity of all methods in this file\n const symbolName = symbolId.split('::')[1] ?? '';\n const relatedEntries = complexityEntries.filter((c) =>\n c.symbolId.startsWith(`${filePath}::${symbolName}.`),\n );\n cyclomatic = relatedEntries.length > 0\n ? Math.max(...relatedEntries.map((c) => c.cyclomatic))\n : 1;\n }\n // Normalize: cyclomatic 1=0, 10+=1.0\n const normalizedComplexity = Math.min((cyclomatic - 1) / 9, 1);\n\n const score = healthScorer.score(symbolId, normalizedComplexity, normalizedChurn);\n\n const payload = masking.mask(JSON.stringify(score));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(storage.listIndexedFiles());\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n if (indexMissingWarning) {\n content.push({ type: 'text', text: `⚠️ ${indexMissingWarning}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","export class ChurnAnalyzer {\n normalize(commitCount: number, maxCommitCount: number): number {\n if (maxCommitCount <= 0) return 0;\n if (commitCount < 0) {\n throw new Error(`Invalid commit count: ${commitCount}`);\n }\n return Math.min(commitCount / maxCommitCount, 1);\n }\n}\n","import type { ChangeIntelligenceScore, ScoreBand } from '../types.js';\n\nexport class HealthScorer {\n score(symbolId: string, complexity: number, churn: number): ChangeIntelligenceScore {\n const composite = complexity * churn;\n const band = this.toBand(composite);\n\n return { symbolId, complexity, churn, composite, band };\n }\n\n private toBand(composite: number): ScoreBand {\n if (composite < 0.3) return 'low';\n if (composite < 0.7) return 'medium';\n return 'high';\n }\n}\n","import { z } from 'zod';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { ICoChangePort } from '../../ports/i-co-change-port.js';\nimport { BlastRadiusCalculator } from '../../core/blast-radius/blast-radius-calculator.js';\nimport { findCoChangedFilesForBlastRadius } from '../../core/blast-radius/co-changed-files.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\nimport type { CommunitySnapshot } from '../../core/types.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\n\ninterface ClusterBreakdown {\n readonly byCluster: Record<string, number>;\n readonly crossClusterCount: number;\n readonly multiClusterHint?: string;\n}\n\nfunction computeClusterBreakdown(\n snapshot: CommunitySnapshot,\n rootSymbolId: string,\n impactedSymbolIds: readonly string[],\n): ClusterBreakdown | undefined {\n if (impactedSymbolIds.length === 0) return undefined;\n const assignment = new Map<string, { id: number; label: string }>();\n for (const entry of snapshot.communities) {\n assignment.set(entry.symbolId, { id: entry.communityId, label: entry.communityLabel });\n }\n const rootCluster = assignment.get(rootSymbolId);\n const byCluster: Record<string, number> = {};\n let crossClusterCount = 0;\n for (const symbolId of impactedSymbolIds) {\n const cluster = assignment.get(symbolId);\n if (!cluster) continue;\n const label = cluster.label || `community-${cluster.id}`;\n byCluster[label] = (byCluster[label] ?? 0) + 1;\n if (rootCluster && cluster.id !== rootCluster.id) crossClusterCount++;\n }\n const clusterCount = Object.keys(byCluster).length;\n const multiClusterHint =\n clusterCount >= 3\n ? `Change impacts ${clusterCount} clusters — multi-team review recommended.`\n : undefined;\n return { byCluster, crossClusterCount, multiClusterHint };\n}\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional(),\n intent: z.string().optional().describe('Filter results by intent keywords (e.g., \"test\", \"adapter\", \"security\")'),\n});\n\nexport function handleGetBlastRadius(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n git?: IGitPort,\n clusterLabelMasker?: ClusterLabelMasker,\n coChange?: ICoChangePort,\n) {\n const calculator = new BlastRadiusCalculator();\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n // Resolve the id once: a 2-part data id names its winning definition. The response echoes the id as given.\n const root = graph.getNode(symbolId);\n if (!root) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n const rootId = root.symbolId;\n\n // Read per call: the port caches by mtime, so a matrix rewritten while the server runs is picked up.\n const coChangeMap = coChange?.readCoChangeMap();\n const result = calculator.calculate(graph, rootId, coChangeMap);\n\n // Apply confidence filter if requested\n let symbols = result.impactedSymbols;\n if (parsed.data.confidence) {\n symbols = symbols.filter(s => s.confidence === parsed.data.confidence);\n }\n\n // Apply intent filter if requested\n symbols = filterByIntent(symbols as unknown as Record<string, unknown>[], parsed.data.intent) as unknown as typeof symbols;\n\n const confirmedCount = symbols.filter(s => s.confidence === 'confirmed').length;\n const likelyCount = symbols.filter(s => s.confidence === 'likely').length;\n const potentialCount = symbols.filter(s => s.confidence === 'potential').length;\n\n const rawSnapshot = storage.readCommunities();\n const snapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n const clusterBreakdown = snapshot\n ? computeClusterBreakdown(snapshot, rootId, symbols.map((s) => s.symbolId))\n : undefined;\n\n const body: Record<string, unknown> = {\n symbolId,\n impactScore: symbols.length,\n directDependentsCount: result.directDependentsCount,\n confirmedCount,\n likelyCount,\n potentialCount,\n overallRiskScore: result.overallRiskScore,\n impactedSymbols: symbols,\n };\n if (clusterBreakdown) {\n body.byCluster = clusterBreakdown.byCluster;\n body.crossClusterEdges = clusterBreakdown.crossClusterCount;\n if (clusterBreakdown.multiClusterHint) {\n body.multiClusterHint = clusterBreakdown.multiClusterHint;\n }\n }\n\n // Files that change together with the root's file but have no dependency path to it. From the\n // unfiltered result: the confidence and intent filters above shape the impactedSymbols list only.\n const coChangedFiles = findCoChangedFilesForBlastRadius(coChangeMap, rootId, result.impactedSymbols);\n if (coChangedFiles.length > 0) body.coChangedFiles = coChangedFiles;\n\n const stalenessMeta = git ? await buildSnapshotStaleness(storage, git) : undefined;\n const extras = stalenessMeta ? { snapshotStaleness: stalenessMeta } : undefined;\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ArchitecturalOverlay } from '../../core/overlay/architectural-overlay.js';\nimport { compareCodepoint } from '../../core/compare-codepoint.js';\nimport { getThreshold, wrapResponse, type WrapResponseExtras } from '../../core/response-envelope.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport type { CommunitySnapshot, GodNode } from '../../core/types.js';\nimport type { ContextsView } from '../../core/contexts-view/contexts-view-builder.js';\nimport type { TermCollision } from '../../core/contexts-view/context-terms.js';\nimport type { CandidateSharedTable } from '../../core/contexts-view/data-coupling.js';\nimport type { IContextsViewPort } from '../../ports/i-contexts-view-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nexport const OVERLAY_MODES = ['regex', 'communities', 'both', 'contexts'] as const;\n\nexport const InputSchema = z.object({\n layer: z.string().optional(),\n mode: z.enum(OVERLAY_MODES).optional(),\n candidate: z.number().int().positive().optional(),\n});\n\nexport interface CommunityOverlayCluster {\n id: number;\n label: string;\n memberCount: number;\n members: string[];\n godNodes: string[];\n}\n\nexport interface CommunityOverlay {\n clusters: CommunityOverlayCluster[];\n modularity: number;\n edgeQuality: CommunitySnapshot['edgeQuality'];\n crossClusterEdges: number;\n computedAt: string;\n commitSha: string;\n}\n\nexport function buildCommunityOverlay(\n snapshot: CommunitySnapshot,\n options: { memberPreviewLimit?: number } = {},\n): CommunityOverlay {\n const previewLimit = options.memberPreviewLimit ?? 15;\n const byCluster = new Map<number, string[]>();\n for (const entry of snapshot.communities) {\n const list = byCluster.get(entry.communityId) ?? [];\n list.push(entry.symbolId);\n byCluster.set(entry.communityId, list);\n }\n const labelFor = new Map<number, string>();\n for (const entry of snapshot.communities) {\n if (!labelFor.has(entry.communityId)) {\n labelFor.set(entry.communityId, entry.communityLabel);\n }\n }\n const godNodesByCommunity = new Map<number, Set<string>>();\n for (const god of snapshot.godNodes as readonly GodNode[]) {\n for (const communityId of god.bridgedCommunities) {\n const set = godNodesByCommunity.get(communityId) ?? new Set<string>();\n set.add(god.symbolId);\n godNodesByCommunity.set(communityId, set);\n }\n }\n\n const clusters: CommunityOverlayCluster[] = [];\n for (const [id, members] of byCluster) {\n members.sort();\n clusters.push({\n id,\n label: labelFor.get(id) ?? `community-${id}`,\n memberCount: members.length,\n members: members.slice(0, previewLimit),\n godNodes: [...(godNodesByCommunity.get(id) ?? [])].sort(),\n });\n }\n clusters.sort((a, b) => b.memberCount - a.memberCount || a.id - b.id);\n\n return {\n clusters,\n modularity: snapshot.modularity,\n edgeQuality: snapshot.edgeQuality,\n crossClusterEdges: snapshot.crossClusterEdges,\n computedAt: snapshot.computedAt,\n commitSha: snapshot.commitSha,\n };\n}\n\n/** Hubs and collisions listed in the summary, and hubs in the detail (R24); the totals say how many there are. */\nconst HUB_COLLISION_LIMIT = 50;\n/** Top terms per candidate in the summary. */\nconst SUMMARY_TERM_LIMIT = 5;\n/** Files of the candidate in the detail. */\nconst DETAIL_FILE_LIMIT = 15;\n/** Collisions in the detail. */\nconst DETAIL_COLLISION_LIMIT = 10;\n/** The candidate's own occurrences per collision, and members per occurrence, in the detail. */\nconst DETAIL_OCCURRENCE_LIMIT = 5;\n/** Shared tables of the candidate, and the candidate's own data resources, in the detail. */\nconst DETAIL_DATA_LIMIT = 15;\n/** Accesses per shared table in the detail. */\nconst DETAIL_ACCESS_LIMIT = 10;\n\nconst NO_VIEW_HINT = 'No contexts view available. Run ctxo index to generate one.';\nconst SUMMARY_HINT = 'Pass candidate: <id> for one candidate in full; run ctxo contexts --json for the whole view.';\nconst DETAIL_HINT = 'Run ctxo contexts --json for full member sets.';\n\n/** Masks candidate labels, after seeding the masker in sorted order so the `[masked-cluster-N]` numbering is stable. */\nfunction labelMaskerFor(view: ContextsView, labelMasker: ClusterLabelMasker | undefined): (label: string) => string {\n const masker = labelMasker?.isEnabled() ? labelMasker : undefined;\n if (!masker) return (label) => label;\n for (const label of [...new Set(view.candidates.map((c) => c.label))].sort(compareCodepoint)) masker.mask(label);\n return (label) => masker.mask(label);\n}\n\n/** The summary fields of a collision: its name, minimum Jaccard, the ids of the candidates it spans and its size. */\nfunction collisionSummary(collision: TermCollision) {\n return {\n name: collision.name,\n minJaccard: collision.minJaccard,\n candidates: [...new Set(collision.occurrences.map((occurrence) => occurrence.candidate))].sort((a, b) => a - b),\n occurrenceCount: collision.occurrences.length,\n };\n}\n\n/**\n * Every candidate in brief, plus the capped hubs and collisions: small enough to list the whole view. Data\n * coupling appears as counts only (`sharedTableCount` on a candidate with any, and the three resource\n * totals); the lists are in the detail and in `ctxo contexts --json`.\n */\nfunction contextsSummary(view: ContextsView, maskLabel: (label: string) => string): Record<string, unknown> {\n return {\n commitSha: view.commitSha,\n coverage: view.coverage,\n candidates: view.candidates.map((candidate) => ({\n id: candidate.id,\n label: maskLabel(candidate.label),\n fingerprint: candidate.fingerprint,\n fileCount: candidate.fileCount,\n cohesion: candidate.cohesion,\n topTerms: candidate.topTerms.slice(0, SUMMARY_TERM_LIMIT),\n ...(candidate.sharedTables.length > 0 ? { sharedTableCount: candidate.sharedTables.length } : {}),\n })),\n hubs: view.hubs.slice(0, HUB_COLLISION_LIMIT).map((hub) => ({\n file: hub.file,\n reasons: hub.reasons,\n ...(hub.candidate !== undefined ? { candidate: hub.candidate } : {}),\n })),\n hubsTotal: view.hubs.length,\n collisions: view.collisions.slice(0, HUB_COLLISION_LIMIT).map(collisionSummary),\n collisionsTotal: view.collisions.length,\n unclustered: { count: view.unclustered.count },\n dataResourcesTotal: view.dataResources.length,\n sharedResourcesTotal: view.dataResources.filter((resource) => resource.accesses.length >= 2).length,\n externalResourcesTotal: view.dataResources.filter((resource) => resource.external).length,\n hint: SUMMARY_HINT,\n };\n}\n\n/**\n * A shared table in the detail: its accesses capped at `DETAIL_ACCESS_LIMIT` by candidate id, the requesting\n * candidate's own access always kept, with `accessCount`.\n */\nfunction sharedTableDetail(table: CandidateSharedTable, id: number) {\n const own = table.accesses.filter((access) => access.candidate === id);\n const others = table.accesses.filter((access) => access.candidate !== id).slice(0, DETAIL_ACCESS_LIMIT - own.length);\n return {\n ...table,\n accesses: [...own, ...others].sort((a, b) => a.candidate - b.candidate),\n accessCount: table.accesses.length,\n };\n}\n\n/** The resources the candidate reads or writes, compact: its own modes and the writer count. */\nfunction ownDataResources(view: ContextsView, id: number) {\n const own: Array<Record<string, unknown>> = [];\n for (const resource of view.dataResources) {\n const access = resource.accesses.find((entry) => entry.candidate === id);\n if (!access) continue;\n own.push({\n name: resource.name,\n ...(resource.kind !== undefined ? { kind: resource.kind } : {}),\n external: resource.external,\n modes: access.modes,\n ...(resource.owner !== undefined ? { owner: resource.owner } : {}),\n writerCount: resource.writers.length,\n });\n }\n return own;\n}\n\n/**\n * One candidate in full (files capped), the hubs assigned to it (at most `HUB_COLLISION_LIMIT`) and the\n * collisions it takes part in (at most `DETAIL_COLLISION_LIMIT`), each list with its total. A collision carries\n * its summary fields plus this candidate's own occurrences only, at most `DETAIL_OCCURRENCE_LIMIT`, each as\n * `{ symbolId, members, memberCount }` with the first members in view order: the file is part of the symbol\n * ID, and the other candidates are named in `candidates`. The candidate's `sharedTables` keep at most the first\n * `DETAIL_DATA_LIMIT` by name (with `sharedTableCount`), each with at most `DETAIL_ACCESS_LIMIT` accesses (with\n * `accessCount`); `dataResources` lists at most the first `DETAIL_DATA_LIMIT` resources it reads or writes, by\n * name, with `dataResourcesTotal`. No array here is truncatable, so these caps are what keeps the response\n * within the limit, and `fitDataToLimit` then drops trailing data entries while it is still over.\n */\nfunction contextsDetail(\n view: ContextsView,\n id: number,\n maskLabel: (label: string) => string,\n extras: WrapResponseExtras | undefined,\n): Record<string, unknown> {\n const candidate = view.candidates.find((c) => c.id === id);\n if (!candidate) {\n return { found: false, hint: `No candidate ${id} in the contexts view. Call without candidate for the list.` };\n }\n const hubs = view.hubs.filter((hub) => hub.candidate === id);\n const collisions = view.collisions.filter((collision) =>\n collision.occurrences.some((occurrence) => occurrence.candidate === id),\n );\n const dataResources = ownDataResources(view, id);\n const shownTables = candidate.sharedTables.slice(0, DETAIL_DATA_LIMIT).map((table) => sharedTableDetail(table, id));\n const shownResources = dataResources.slice(0, DETAIL_DATA_LIMIT);\n const body = {\n commitSha: view.commitSha,\n candidate: {\n ...candidate,\n label: maskLabel(candidate.label),\n files: candidate.files.slice(0, DETAIL_FILE_LIMIT),\n sharedTables: shownTables,\n sharedTableCount: candidate.sharedTables.length,\n },\n hubs: hubs\n .slice(0, HUB_COLLISION_LIMIT)\n .map((hub) => ({ file: hub.file, reasons: hub.reasons, dependents: hub.dependents, damping: hub.damping })),\n hubsTotal: hubs.length,\n collisions: collisions.slice(0, DETAIL_COLLISION_LIMIT).map((collision) => ({\n ...collisionSummary(collision),\n occurrences: collision.occurrences\n .filter((occurrence) => occurrence.candidate === id)\n .slice(0, DETAIL_OCCURRENCE_LIMIT)\n .map((occurrence) => ({\n symbolId: occurrence.symbolId,\n members: occurrence.members.slice(0, DETAIL_OCCURRENCE_LIMIT),\n memberCount: occurrence.members.length,\n })),\n })),\n collisionsTotal: collisions.length,\n dataResources: shownResources,\n dataResourcesTotal: dataResources.length,\n hint: DETAIL_HINT,\n };\n fitDataToLimit(body, shownTables, shownResources, extras);\n return body;\n}\n\n/**\n * K33: the detail must fit the response limit (`CTXO_RESPONSE_LIMIT`, default 8192), and only its data lists\n * may shrink. While the body, serialised as the envelope sends it, is over the limit, the last entry (by name)\n * of the longer of `sharedTables` and `dataResources` is dropped, `dataResources` on a tie. `sharedTableCount`\n * and `dataResourcesTotal` keep the full totals. The lists are the body's own arrays, shortened in place; every\n * other field stays whole. Deterministic: the same input drops the same entries.\n */\nfunction fitDataToLimit(\n body: Record<string, unknown>,\n tables: unknown[],\n resources: unknown[],\n extras: WrapResponseExtras | undefined,\n): void {\n const limit = getThreshold();\n const size = () => Buffer.byteLength(JSON.stringify(wrapResponse(body, extras)), 'utf-8');\n while ((tables.length > 0 || resources.length > 0) && size() > limit) {\n if (resources.length >= tables.length) resources.pop();\n else tables.pop();\n }\n}\n\n/**\n * Builds the `mode: \"contexts\"` body from the (read-only, shared, deep-frozen) view: the summary of every\n * candidate, or with `candidate` that one candidate in full. Only copies are built (`map`, `slice`, spread):\n * the view must never be sorted or spliced in place. The full view stays in the artifact and in\n * `ctxo contexts --json`; `settings` is left out here. `extras` are the envelope extras the response is sent\n * with, so the detail is measured as it is sent.\n */\nexport function buildContextsOverlay(\n view: ContextsView | undefined,\n options: { candidate?: number; labelMasker?: ClusterLabelMasker; extras?: WrapResponseExtras } = {},\n): Record<string, unknown> {\n if (!view) return { found: false, hint: NO_VIEW_HINT };\n const maskLabel = labelMaskerFor(view, options.labelMasker);\n return options.candidate === undefined\n ? contextsSummary(view, maskLabel)\n : contextsDetail(view, options.candidate, maskLabel, options.extras);\n}\n\nexport function handleGetArchitecturalOverlay(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n git?: IGitPort,\n clusterLabelMasker?: ClusterLabelMasker,\n contextsView?: IContextsViewPort,\n) {\n const overlay = new ArchitecturalOverlay();\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const files = storage.listIndexedFiles();\n const result = overlay.classify(files);\n const mode = parsed.data.mode ?? 'both';\n\n const buildContent = (payloadStr: string) => {\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(files);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payloadStr });\n return content;\n };\n\n const stalenessMeta = git ? await buildSnapshotStaleness(storage, git) : undefined;\n const extras = stalenessMeta ? { snapshotStaleness: stalenessMeta } : undefined;\n\n // Filter by layer if specified (regex-mode semantics always apply here)\n if (parsed.data.layer) {\n const filtered = result.layers[parsed.data.layer];\n const payload = masking.mask(\n JSON.stringify(wrapResponse({ layer: parsed.data.layer, files: filtered ?? [] }, extras)),\n );\n return { content: buildContent(payload) };\n }\n\n if (mode === 'contexts') {\n const body = buildContextsOverlay(contextsView?.readContextsView(), {\n candidate: parsed.data.candidate,\n labelMasker: clusterLabelMasker,\n extras,\n });\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n return { content: buildContent(payload) };\n }\n\n const rawSnapshot = mode === 'regex' ? undefined : storage.readCommunities();\n const snapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n const community = snapshot ? buildCommunityOverlay(snapshot) : undefined;\n\n const body: Record<string, unknown> = {};\n if (mode !== 'communities') body.layers = result.layers;\n if (community) body.communities = community;\n if (mode === 'communities' && !community) {\n body.hint = 'No community snapshot available. Run `ctxo index` to generate one.';\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n return { content: buildContent(payload) };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { DeadCodeDetector } from '../../core/dead-code/dead-code-detector.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nexport const InputSchema = z.object({\n includeTests: z.boolean().optional().default(false),\n intent: z.string().optional().describe('Filter dead code results by intent keywords (e.g., \"adapter\", \"core\", \"function\")'),\n});\n\nexport function handleFindDeadCode(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const detector = new DeadCodeDetector();\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { graph, indexedFiles } = getGraph();\n const result = detector.detect(graph, { includeTests: parsed.data.includeTests });\n\n // Apply intent filter to deadSymbols if requested\n const filtered = {\n ...result,\n deadSymbols: filterByIntent(result.deadSymbols as unknown as Record<string, unknown>[], parsed.data.intent) as unknown as typeof result.deadSymbols,\n };\n\n const payload = masking.mask(JSON.stringify(wrapResponse(filtered as unknown as Record<string, unknown>)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { ICoChangePort } from '../../ports/i-co-change-port.js';\nimport { ContextAssembler } from '../../core/context-assembly/context-assembler.js';\nimport { JsonIndexReader } from '../storage/json-index-reader.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\n\nconst TaskTypeSchema = z.enum(['fix', 'extend', 'refactor', 'understand']);\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1),\n taskType: TaskTypeSchema,\n tokenBudget: z.number().min(100).optional().default(4000),\n});\n\nexport function handleGetContextForTask(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n coChange?: ICoChangePort,\n) {\n const assembler = new ContextAssembler();\n const indexReader = new JsonIndexReader(ctxoRoot);\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, taskType, tokenBudget } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n const indices = indexReader.readAll();\n\n const result = assembler.assembleForTask(graph, symbolId, taskType, indices, tokenBudget, coChange?.readCoChangeMap());\n\n if (!result) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\".' }) }],\n };\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse(result as unknown as Record<string, unknown>)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","export type TaskType = 'fix' | 'extend' | 'refactor' | 'understand';\n\nexport interface ScoringWeights {\n readonly directDependency: number;\n readonly interfaceTypeDef: number;\n readonly blastRadiusDependent: number;\n readonly highComplexity: number;\n readonly antiPatternHistory: number;\n}\n\nconst TASK_WEIGHTS: Record<TaskType, ScoringWeights> = {\n fix: {\n directDependency: 0.3,\n interfaceTypeDef: 0.1,\n blastRadiusDependent: 0.1,\n highComplexity: 0.2,\n antiPatternHistory: 0.3,\n },\n extend: {\n directDependency: 0.4,\n interfaceTypeDef: 0.5,\n blastRadiusDependent: 0.1,\n highComplexity: 0.0,\n antiPatternHistory: 0.0,\n },\n refactor: {\n directDependency: 0.2,\n interfaceTypeDef: 0.1,\n blastRadiusDependent: 0.5,\n highComplexity: 0.1,\n antiPatternHistory: 0.1,\n },\n understand: {\n directDependency: 0.5,\n interfaceTypeDef: 0.3,\n blastRadiusDependent: 0.1,\n highComplexity: 0.1,\n antiPatternHistory: 0.0,\n },\n};\n\nexport function getWeightsForTask(taskType: TaskType): ScoringWeights {\n return TASK_WEIGHTS[taskType];\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport { SYMBOL_KINDS, type SymbolNode, type SymbolKind, type FileIndex, type CoChangeEntry } from '../types.js';\nimport { LogicSliceQuery } from '../logic-slice/logic-slice-query.js';\nimport { BlastRadiusCalculator, type ImpactConfidence } from '../blast-radius/blast-radius-calculator.js';\nimport { withoutAnnotations } from '../detail-levels/detail-formatter.js';\nimport { type TaskType, type ScoringWeights, getWeightsForTask } from './task-context-strategy.js';\n\nexport interface ContextEntry {\n readonly symbolId: string;\n readonly name: string;\n readonly kind: SymbolKind;\n readonly file: string;\n readonly relevanceScore: number;\n readonly reason: string;\n readonly lines: number;\n readonly tokens: number;\n /**\n * The blast radius tier of a dependent of the target (what breaks if the target changes). Absent on\n * entries that are not dependents, such as the target's own dependencies.\n */\n readonly confidence?: ImpactConfidence;\n}\n\nexport interface TaskContextResult {\n readonly target: SymbolNode;\n readonly taskType: TaskType;\n readonly context: ContextEntry[];\n readonly totalTokens: number;\n readonly tokenBudget: number;\n readonly warnings: string[];\n}\n\nexport interface RankedContextResult {\n readonly query: string;\n readonly strategy: string;\n readonly results: Array<{\n readonly symbolId: string;\n readonly name: string;\n readonly kind: SymbolKind;\n readonly file: string;\n readonly relevanceScore: number;\n readonly importanceScore: number;\n readonly combinedScore: number;\n readonly tokens: number;\n }>;\n readonly totalTokens: number;\n readonly tokenBudget: number;\n}\n\nconst DEFAULT_TOKEN_BUDGET = 4000;\n\nexport class ContextAssembler {\n private readonly logicSlice = new LogicSliceQuery();\n private readonly blastRadius = new BlastRadiusCalculator();\n\n /**\n * `coChangeMap` is the same lookup `get_blast_radius` uses: with it, a potential dependent whose file\n * co-changes with the target's becomes likely. It changes the tier shown on an entry, not which entries\n * make the context or how they rank.\n */\n assembleForTask(\n graph: SymbolGraph,\n symbolId: string,\n taskType: TaskType,\n indices: readonly FileIndex[],\n tokenBudget = DEFAULT_TOKEN_BUDGET,\n coChangeMap?: Map<string, CoChangeEntry[]>,\n ): TaskContextResult | undefined {\n // Resolve the id once: a 2-part data id names its winning definition.\n const target = graph.getNode(symbolId);\n if (!target) return undefined;\n const targetId = target.symbolId;\n\n const weights = getWeightsForTask(taskType);\n\n // Gather forward deps + reverse deps\n const slice = this.logicSlice.getLogicSlice(graph, targetId);\n const blastResult = this.blastRadius.calculate(graph, targetId, coChangeMap);\n\n // Build candidate pool (deduplicated)\n const candidateMap = new Map<string, { node: SymbolNode; signals: Signals }>();\n\n // Forward dependencies\n if (slice) {\n for (const dep of slice.dependencies) {\n candidateMap.set(dep.symbolId, {\n node: dep,\n signals: { isDirectDep: true, isInterfaceOrType: this.isInterfaceOrType(dep.kind), isDependent: false, complexity: 0, hasAntiPattern: false },\n });\n }\n }\n\n // Reverse dependents (blast radius) — fall back to a synthetic node when\n // the edge source has no matching symbols[] entry (e.g. test files that\n // only import). Without this, symbols whose importers are all such files\n // would produce an empty context even when reverse edges clearly exist.\n for (const entry of blastResult.impactedSymbols) {\n const node = graph.getNode(entry.symbolId) ?? synthesizeNodeFromId(entry.symbolId);\n if (!node) continue;\n\n const existing = candidateMap.get(entry.symbolId);\n if (existing) {\n existing.signals.isDependent = true;\n existing.signals.dependentConfidence = entry.confidence;\n } else {\n candidateMap.set(entry.symbolId, {\n node,\n signals: { isDirectDep: false, isInterfaceOrType: this.isInterfaceOrType(node.kind), isDependent: true, dependentConfidence: entry.confidence, complexity: 0, hasAntiPattern: false },\n });\n }\n }\n\n // Enrich with complexity + anti-patterns from index\n const fileIndexMap = new Map(indices.map((i) => [i.file, i]));\n for (const [sid, candidate] of candidateMap) {\n const file = sid.split('::')[0] ?? '';\n const fileIndex = fileIndexMap.get(file);\n\n if (fileIndex) {\n const complexity = fileIndex.complexity?.find((c) => c.symbolId === sid);\n candidate.signals.complexity = complexity?.cyclomatic ?? 0;\n candidate.signals.hasAntiPattern = fileIndex.antiPatterns.length > 0;\n }\n }\n\n // Score and rank\n const scored = [...candidateMap.entries()].map(([, { node, signals }]) => ({\n entry: this.buildContextEntry(node, signals, weights),\n score: this.computeScore(signals, weights),\n }));\n\n scored.sort((a, b) => b.score - a.score);\n\n // Greedy pack within token budget\n const context: ContextEntry[] = [];\n let totalTokens = 0;\n\n for (const { entry } of scored) {\n if (totalTokens + entry.tokens > tokenBudget) continue;\n context.push(entry);\n totalTokens += entry.tokens;\n }\n\n // Warnings\n const warnings: string[] = [];\n const targetFile = targetId.split('::')[0] ?? '';\n const targetIndex = fileIndexMap.get(targetFile);\n if (targetIndex && targetIndex.antiPatterns.length > 0) {\n warnings.push(`⚠ Target symbol has ${targetIndex.antiPatterns.length} anti-pattern(s) in file history`);\n }\n\n // The target leaves its annotations out, as logic slices below L4 do (see withoutAnnotations).\n return { target: withoutAnnotations(target), taskType, context, totalTokens, tokenBudget, warnings };\n }\n\n assembleRanked(\n graph: SymbolGraph,\n query: string,\n strategy: 'combined' | 'dependency' | 'importance' = 'combined',\n tokenBudget = DEFAULT_TOKEN_BUDGET,\n ): RankedContextResult {\n const allNodes = graph.allNodes();\n const queryLower = query.toLowerCase();\n\n // Compute max reverse edges for importance normalization\n let maxReverseEdges = 1;\n for (const node of allNodes) {\n const count = graph.getReverseEdges(node.symbolId).length;\n if (count > maxReverseEdges) maxReverseEdges = count;\n }\n\n // Score each symbol\n const scored = allNodes.map((node) => {\n const relevanceScore = this.computeTextRelevance(node.name, queryLower);\n const importanceScore = graph.getReverseEdges(node.symbolId).length / maxReverseEdges;\n\n let combinedScore: number;\n switch (strategy) {\n case 'dependency':\n combinedScore = relevanceScore;\n break;\n case 'importance':\n combinedScore = importanceScore;\n break;\n default:\n combinedScore = relevanceScore * 0.6 + importanceScore * 0.4;\n }\n\n return {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n relevanceScore: Math.round(relevanceScore * 1000) / 1000,\n importanceScore: Math.round(importanceScore * 1000) / 1000,\n combinedScore: Math.round(combinedScore * 1000) / 1000,\n tokens: this.estimateTokens(node),\n };\n });\n\n // Filter zero-relevance for text-based strategies\n const filtered = strategy === 'importance'\n ? scored\n : scored.filter((s) => s.relevanceScore > 0 || s.importanceScore > 0.1);\n\n filtered.sort((a, b) => b.combinedScore - a.combinedScore);\n\n // Greedy pack\n const results: typeof filtered = [];\n let totalTokens = 0;\n\n for (const item of filtered) {\n if (totalTokens + item.tokens > tokenBudget) continue;\n results.push(item);\n totalTokens += item.tokens;\n }\n\n return { query, strategy, results, totalTokens, tokenBudget };\n }\n\n private computeTextRelevance(name: string, queryLower: string): number {\n const nameLower = name.toLowerCase();\n if (nameLower === queryLower) return 1.0;\n if (nameLower.includes(queryLower)) return 0.7;\n if (queryLower.split(/\\s+/).some((word) => nameLower.includes(word))) return 0.4;\n return 0;\n }\n\n private computeScore(signals: Signals, weights: ScoringWeights): number {\n let score = 0;\n if (signals.isDirectDep) score += weights.directDependency;\n if (signals.isInterfaceOrType) score += weights.interfaceTypeDef;\n if (signals.isDependent) score += weights.blastRadiusDependent;\n if (signals.complexity > 5) score += weights.highComplexity;\n if (signals.hasAntiPattern) score += weights.antiPatternHistory;\n return Math.round(score * 1000) / 1000;\n }\n\n private buildContextEntry(node: SymbolNode, signals: Signals, weights: ScoringWeights): ContextEntry {\n const reasons: string[] = [];\n if (signals.isDirectDep) reasons.push('direct dependency');\n if (signals.isInterfaceOrType) reasons.push('type/interface definition');\n if (signals.isDependent) reasons.push('blast radius dependent');\n if (signals.complexity > 5) reasons.push(`high complexity (CC=${signals.complexity})`);\n if (signals.hasAntiPattern) reasons.push('anti-pattern history');\n\n return {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n relevanceScore: this.computeScore(signals, weights),\n reason: reasons.join(', ') || 'related symbol',\n lines: node.endLine - node.startLine + 1,\n tokens: this.estimateTokens(node),\n ...(signals.dependentConfidence ? { confidence: signals.dependentConfidence } : {}),\n };\n }\n\n private isInterfaceOrType(kind: SymbolKind): boolean {\n return kind === 'interface' || kind === 'type';\n }\n\n estimateTokensPublic(node: SymbolNode): number {\n return this.estimateTokens(node);\n }\n\n private estimateTokens(node: SymbolNode): number {\n // Use byte offsets for accurate estimation when available (~4 chars per token)\n if (node.startOffset !== undefined && node.endOffset !== undefined) {\n return Math.ceil((node.endOffset - node.startOffset) / 4);\n }\n return (node.endLine - node.startLine + 1) * 10;\n }\n}\n\ninterface Signals {\n isDirectDep: boolean;\n isInterfaceOrType: boolean;\n isDependent: boolean;\n /** The blast radius tier of the dependency; set exactly when `isDependent` is. */\n dependentConfidence?: ImpactConfidence;\n complexity: number;\n hasAntiPattern: boolean;\n}\n\nconst VALID_SYMBOL_KINDS: ReadonlySet<string> = new Set<string>(SYMBOL_KINDS);\n\n/**\n * Build a minimal SymbolNode from a symbolId string. Used when a blast-radius\n * entry points at an orphan edge source (no matching symbols[] entry in the\n * index). startLine/endLine are 0 so token estimation falls back to a small\n * default without over-counting missing source ranges.\n */\nfunction synthesizeNodeFromId(symbolId: string): SymbolNode | undefined {\n const parts = symbolId.split('::');\n if (parts.length !== 3) return undefined;\n const [file, name, kindRaw] = parts;\n if (!file || !name || !kindRaw) return undefined;\n const kind = VALID_SYMBOL_KINDS.has(kindRaw) ? (kindRaw as SymbolKind) : 'variable';\n return { symbolId, name, kind, startLine: 0, endLine: 0 };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { ContextAssembler } from '../../core/context-assembly/context-assembler.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { SearchEngine } from '../../core/search/search-engine.js';\nimport type { ISearchPort } from '../../ports/i-search-port.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:search');\n\nexport const InputSchema = z.object({\n query: z.string().min(1),\n tokenBudget: z.number().min(100).optional().default(4000),\n strategy: z.enum(['combined', 'dependency', 'importance']).optional().default('combined'),\n searchMode: z.enum(['fts', 'legacy']).optional().default('fts'),\n});\n\nexport function handleGetRankedContext(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n searchEngine?: ISearchPort,\n) {\n const assembler = new ContextAssembler();\n const engine: ISearchPort = searchEngine ?? new SearchEngine();\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n let lastNodeCount = -1;\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { query, tokenBudget, strategy, searchMode } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n // Use new search engine when mode is 'fts'\n if (searchMode === 'fts' && strategy !== 'importance') {\n // Rebuild search index when graph changes (detected by node count change)\n const currentNodeCount = graph.nodeCount;\n if (currentNodeCount !== lastNodeCount) {\n const allNodes = graph.allNodes();\n // Compute PageRank scores (normalized reverse edge count)\n const pageRankScores = new Map<string, number>();\n let maxReverseEdges = 1;\n for (const node of allNodes) {\n const count = graph.getReverseEdges(node.symbolId).length;\n if (count > maxReverseEdges) maxReverseEdges = count;\n }\n for (const node of allNodes) {\n const count = graph.getReverseEdges(node.symbolId).length;\n pageRankScores.set(node.symbolId, count / maxReverseEdges);\n }\n\n engine.buildIndex(allNodes, pageRankScores);\n lastNodeCount = currentNodeCount;\n log.info(`Search engine: ${engine.getTier()} (${allNodes.length} symbols)`);\n }\n\n const searchResult = engine.search(query, 100);\n\n // Greedy pack within token budget\n const results: Array<{\n symbolId: string;\n name: string;\n kind: string;\n file: string;\n relevanceScore: number;\n importanceScore: number;\n combinedScore: number;\n tokens: number;\n }> = [];\n let totalTokens = 0;\n\n for (const sr of searchResult.results) {\n const node = graph.getNode(sr.symbolId);\n const tokens = node ? assembler.estimateTokensPublic(node) : 90;\n\n if (totalTokens + tokens > tokenBudget) continue;\n results.push({\n symbolId: sr.symbolId,\n name: sr.name,\n kind: sr.kind,\n file: sr.filePath,\n relevanceScore: Math.round(sr.relevanceScore * 1000) / 1000,\n importanceScore: Math.round(sr.importanceScore * 1000) / 1000,\n combinedScore: Math.round(sr.combinedScore * 1000) / 1000,\n tokens,\n });\n totalTokens += tokens;\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n query,\n strategy,\n results,\n totalTokens,\n tokenBudget,\n searchMetrics: searchResult.metrics,\n ...(searchResult.fuzzyCorrection ? { fuzzyCorrection: searchResult.fuzzyCorrection } : {}),\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n\n // Legacy mode: use existing substring matching\n const result = assembler.assembleRanked(graph, query, strategy, tokenBudget);\n\n const payload = masking.mask(JSON.stringify(wrapResponse(result as unknown as Record<string, unknown>)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","/**\n * Fuzzy Corrector — Damerau-Levenshtein based typo correction.\n *\n * Uses adaptive thresholds: d≤1 for terms ≤5 chars, d≤2 for longer terms.\n * Vocabulary is built from tokenized symbol names during index build.\n */\n\nimport type { FuzzyCorrection } from '../types.js';\n\nexport class FuzzyCorrector {\n /** term → frequency (for tie-breaking) */\n private vocabulary: Map<string, number> = new Map();\n\n buildVocabulary(termFrequencies: Map<string, number>): void {\n this.vocabulary = new Map(termFrequencies);\n }\n\n /**\n * Attempt to correct query tokens.\n * Returns null if no correction found or no improvement possible.\n */\n correct(queryTokens: string[]): FuzzyCorrection | null {\n if (this.vocabulary.size === 0) return null;\n\n const corrections: FuzzyCorrection['corrections'] = [];\n const correctedTokens: string[] = [];\n let anyChanged = false;\n\n for (const token of queryTokens) {\n // Skip very short tokens (1-2 chars) — too many false matches\n if (token.length <= 2) {\n correctedTokens.push(token);\n continue;\n }\n\n // Check if token exists in vocabulary (exact match)\n if (this.vocabulary.has(token)) {\n correctedTokens.push(token);\n continue;\n }\n\n const maxDist = token.length <= 5 ? 1 : 2;\n const best = this.findClosest(token, maxDist);\n\n if (best) {\n corrections.push({\n original: token,\n corrected: best.term,\n distance: best.distance,\n });\n correctedTokens.push(best.term);\n anyChanged = true;\n } else {\n correctedTokens.push(token);\n }\n }\n\n if (!anyChanged) return null;\n\n return {\n originalQuery: queryTokens.join(' '),\n correctedQuery: correctedTokens.join(' '),\n corrections,\n };\n }\n\n /**\n * Find the closest vocabulary term within maxDistance.\n * Tie-breaks by frequency (higher = preferred).\n */\n private findClosest(\n token: string,\n maxDistance: number,\n ): { term: string; distance: number } | null {\n let bestTerm: string | null = null;\n let bestDist = maxDistance + 1;\n let bestFreq = -1;\n\n for (const [vocabTerm, freq] of this.vocabulary) {\n // Quick length filter: Damerau-Levenshtein ≥ |len difference|\n if (Math.abs(vocabTerm.length - token.length) > maxDistance) continue;\n\n const dist = damerauLevenshtein(token, vocabTerm);\n if (dist <= maxDistance) {\n if (dist < bestDist || (dist === bestDist && freq > bestFreq)) {\n bestTerm = vocabTerm;\n bestDist = dist;\n bestFreq = freq;\n }\n }\n }\n\n return bestTerm !== null ? { term: bestTerm, distance: bestDist } : null;\n }\n}\n\n/**\n * Damerau-Levenshtein distance.\n * Includes transpositions as a single edit operation.\n * \"tokne\" → \"token\" = distance 1 (transposition), not 2 (delete + insert).\n */\nexport function damerauLevenshtein(a: string, b: string): number {\n const lenA = a.length;\n const lenB = b.length;\n\n if (lenA === 0) return lenB;\n if (lenB === 0) return lenA;\n\n // Optimal string alignment distance (restricted edit distance)\n const d: number[][] = Array.from({ length: lenA + 1 }, () => new Array<number>(lenB + 1).fill(0));\n\n for (let i = 0; i <= lenA; i++) d[i]![0] = i;\n for (let j = 0; j <= lenB; j++) d[0]![j] = j;\n\n for (let i = 1; i <= lenA; i++) {\n for (let j = 1; j <= lenB; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n d[i]![j] = Math.min(\n d[i - 1]![j]! + 1, // deletion\n d[i]![j - 1]! + 1, // insertion\n d[i - 1]![j - 1]! + cost, // substitution\n );\n\n // Transposition\n if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {\n d[i]![j] = Math.min(d[i]![j]!, d[i - 2]![j - 2]! + cost);\n }\n }\n }\n\n return d[lenA]![lenB]!;\n}\n","/**\n * In-memory BM25 Search Engine (Tier 2)\n *\n * Implements two-phase cascade search:\n * Phase 1: Exact + tokenized matching with BM25 scoring\n * Phase 2: Trigram/partial matching (activated when Phase 1 returns < threshold results)\n * Phase 3: Fuzzy correction (activated when Phase 1+2 return < threshold results)\n *\n * Scoring: BM25 * (1 + pageRankWeight * pageRankScore) * bigramBoost\n */\n\nimport type { SymbolNode, SearchResponse, SearchResult, SearchMetrics } from '../types.js';\nimport { SymbolTokenizer } from './symbol-tokenizer.js';\nimport { FuzzyCorrector } from './fuzzy-corrector.js';\nimport { createLogger } from '../logger.js';\n\nconst log = createLogger('ctxo:search');\n\n/** BM25 parameters tuned for code search (short documents) */\ninterface BM25Config {\n k1: number; // term frequency saturation (default: 1.2)\n b: number; // length normalization (default: 0.25 — low for short symbol names)\n}\n\ninterface SearchEngineConfig {\n bm25: BM25Config;\n /** Weight of PageRank boost: finalScore = bm25 * (1 + pageRankWeight * pr) */\n pageRankWeight: number;\n /** Score penalty for Phase 2 (trigram) results */\n trigramPenalty: number;\n /** Threshold: if Phase 1 returns fewer results, activate Phase 2 */\n phase2Threshold: number;\n /** Threshold: if Phase 1+2 return fewer results, activate fuzzy correction */\n fuzzyThreshold: number;\n /** Maximum results per phase */\n maxPerPhase: number;\n}\n\nconst DEFAULT_CONFIG: SearchEngineConfig = {\n bm25: { k1: 1.2, b: 0.25 },\n pageRankWeight: 0.5,\n trigramPenalty: 0.8,\n phase2Threshold: 3,\n fuzzyThreshold: 3,\n maxPerPhase: 50,\n};\n\n/** A document in the inverted index (one per symbol) */\ninterface IndexedDocument {\n symbolId: string;\n name: string;\n kind: string;\n filePath: string;\n /** Tokenized name (from SymbolTokenizer) */\n tokens: string[];\n /** Token count (document length for BM25) */\n length: number;\n}\n\n/** Posting list entry */\ninterface Posting {\n docIndex: number;\n termFrequency: number;\n}\n\nexport class SearchEngine {\n private readonly config: SearchEngineConfig;\n private readonly tokenizer: SymbolTokenizer;\n private readonly fuzzy: FuzzyCorrector;\n\n // Primary index (exact + tokenized)\n private documents: IndexedDocument[] = [];\n private primaryIndex: Map<string, Posting[]> = new Map();\n private avgDocLength = 0;\n\n // Trigram index (character 3-grams)\n private trigramIndex: Map<string, Posting[]> = new Map();\n\n // PageRank scores\n private pageRankScores: Map<string, number> = new Map();\n\n constructor(config?: Partial<SearchEngineConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.tokenizer = new SymbolTokenizer({ includeOriginal: true, includeFilePath: true });\n this.fuzzy = new FuzzyCorrector();\n }\n\n getTier(): 'fts5' | 'in-memory' | 'legacy' {\n return 'in-memory';\n }\n\n buildIndex(symbols: SymbolNode[], pageRankScores?: Map<string, number>): void {\n const start = performance.now();\n this.documents = [];\n this.primaryIndex = new Map();\n this.trigramIndex = new Map();\n this.pageRankScores = pageRankScores ?? new Map();\n\n let totalLength = 0;\n const vocabFreq = new Map<string, number>();\n\n for (const sym of symbols) {\n const filePath = sym.symbolId.split('::')[0] ?? '';\n const tokens = this.tokenizer.tokenize(sym.name, filePath);\n const doc: IndexedDocument = {\n symbolId: sym.symbolId,\n name: sym.name,\n kind: sym.kind,\n filePath,\n tokens,\n length: tokens.length,\n };\n const docIndex = this.documents.length;\n this.documents.push(doc);\n totalLength += tokens.length;\n\n // Build primary inverted index\n const termFreqs = new Map<string, number>();\n for (const token of tokens) {\n termFreqs.set(token, (termFreqs.get(token) ?? 0) + 1);\n vocabFreq.set(token, (vocabFreq.get(token) ?? 0) + 1);\n }\n for (const [term, tf] of termFreqs) {\n let postings = this.primaryIndex.get(term);\n if (!postings) {\n postings = [];\n this.primaryIndex.set(term, postings);\n }\n postings.push({ docIndex, termFrequency: tf });\n }\n\n // Build trigram index from symbol name (lowercased)\n const nameLower = sym.name.toLowerCase();\n const trigrams = this.extractTrigrams(nameLower);\n const trigramFreqs = new Map<string, number>();\n for (const tri of trigrams) {\n trigramFreqs.set(tri, (trigramFreqs.get(tri) ?? 0) + 1);\n }\n for (const [tri, tf] of trigramFreqs) {\n let postings = this.trigramIndex.get(tri);\n if (!postings) {\n postings = [];\n this.trigramIndex.set(tri, postings);\n }\n postings.push({ docIndex, termFrequency: tf });\n }\n }\n\n this.avgDocLength = this.documents.length > 0 ? totalLength / this.documents.length : 1;\n\n // Build vocabulary for fuzzy corrector\n this.fuzzy.buildVocabulary(vocabFreq);\n\n const elapsed = performance.now() - start;\n log.info(`Index built: ${symbols.length} symbols, ${this.primaryIndex.size} terms, ${this.trigramIndex.size} trigrams (${elapsed.toFixed(0)}ms)`);\n }\n\n search(query: string, limit = 50): SearchResponse {\n const start = performance.now();\n const queryTokens = this.tokenizer.tokenizeQuery(query);\n\n if (queryTokens.length === 0) {\n return this.emptyResponse(query, start);\n }\n\n const metrics: SearchMetrics = {\n porterHits: 0,\n trigramHits: 0,\n phase2Activated: false,\n fuzzyApplied: false,\n latencyMs: 0,\n };\n\n // Phase 1: Primary index search (exact + tokenized)\n const phase1Scores = this.bm25Search(queryTokens, this.primaryIndex);\n metrics.porterHits = phase1Scores.size;\n\n let allScores = phase1Scores;\n\n // Phase 2: Trigram search (if Phase 1 insufficient)\n if (phase1Scores.size < this.config.phase2Threshold) {\n metrics.phase2Activated = true;\n const queryLower = query.toLowerCase();\n\n // Skip trigram for very short queries (< 3 chars produces no useful trigrams)\n if (queryLower.length >= 3) {\n const queryTrigrams = this.extractTrigrams(queryLower);\n const phase2Scores = this.bm25Search(queryTrigrams, this.trigramIndex);\n metrics.trigramHits = phase2Scores.size;\n\n // Merge: union by symbolId, Phase 2 with penalty\n allScores = this.mergeScores(phase1Scores, phase2Scores, this.config.trigramPenalty);\n }\n }\n\n // Phase 3: Fuzzy correction (if still insufficient)\n let fuzzyCorrection: SearchResponse['fuzzyCorrection'];\n if (allScores.size < this.config.fuzzyThreshold) {\n const correction = this.fuzzy.correct(queryTokens);\n if (correction) {\n metrics.fuzzyApplied = true;\n fuzzyCorrection = correction;\n const correctedTokens = this.tokenizer.tokenizeQuery(correction.correctedQuery);\n const fuzzyScores = this.bm25Search(correctedTokens, this.primaryIndex);\n\n // Merge fuzzy results (with slight penalty)\n allScores = this.mergeScores(allScores, fuzzyScores, 0.9);\n\n // Also try trigram on corrected query\n if (allScores.size < this.config.fuzzyThreshold && correction.correctedQuery.length >= 3) {\n const correctedTrigrams = this.extractTrigrams(correction.correctedQuery.toLowerCase());\n const fuzzyTrigramScores = this.bm25Search(correctedTrigrams, this.trigramIndex);\n allScores = this.mergeScores(allScores, fuzzyTrigramScores, this.config.trigramPenalty * 0.9);\n }\n }\n }\n\n // Snapshot raw BM25 relevance scores before boosts\n const rawRelevance = new Map(allScores);\n\n // Apply bigram boost for multi-word queries (applies to ALL paths including fuzzy)\n const effectiveQueryTerms = fuzzyCorrection\n ? this.tokenizer.tokenizeQuery(fuzzyCorrection.correctedQuery)\n : queryTokens;\n\n if (effectiveQueryTerms.length >= 2) {\n for (const [docIdx, score] of allScores) {\n const doc = this.documents[docIdx]!;\n const boost = this.bigramBoost(effectiveQueryTerms, doc.tokens);\n allScores.set(docIdx, score * boost);\n }\n }\n\n // Apply PageRank boost (applies to ALL paths including fuzzy)\n for (const [docIdx, score] of allScores) {\n const doc = this.documents[docIdx]!;\n const pr = this.pageRankScores.get(doc.symbolId) ?? 0;\n allScores.set(docIdx, score * (1 + this.config.pageRankWeight * pr));\n }\n\n metrics.latencyMs = performance.now() - start;\n const results = this.buildResults(allScores, rawRelevance, limit);\n\n const phaseInfo = metrics.phase2Activated ? 'phase2=yes' : 'phase2=no';\n if (fuzzyCorrection) {\n log.info(`search \"${query}\" → fuzzy \"${fuzzyCorrection.correctedQuery}\": ${results.length} results (${metrics.latencyMs.toFixed(0)}ms)`);\n } else {\n log.info(`search \"${query}\": ${results.length} results, porter=${metrics.porterHits} trigram=${metrics.trigramHits} ${phaseInfo} (${metrics.latencyMs.toFixed(0)}ms)`);\n }\n\n return { query, results, metrics, ...(fuzzyCorrection ? { fuzzyCorrection } : {}) };\n }\n\n updateFile(filePath: string, symbols: SymbolNode[]): void {\n // Remove old documents for this file\n const oldIndices = new Set<number>();\n for (let i = 0; i < this.documents.length; i++) {\n if (this.documents[i]!.filePath === filePath) {\n oldIndices.add(i);\n }\n }\n\n if (oldIndices.size === 0 && symbols.length === 0) return;\n\n // Full rebuild (simpler and correct for incremental updates)\n const allSymbols: SymbolNode[] = [];\n for (let i = 0; i < this.documents.length; i++) {\n if (!oldIndices.has(i)) {\n const doc = this.documents[i]!;\n allSymbols.push({\n symbolId: doc.symbolId,\n name: doc.name,\n kind: doc.kind as SymbolNode['kind'],\n startLine: 0,\n endLine: 0,\n });\n }\n }\n allSymbols.push(...symbols);\n this.buildIndex(allSymbols, this.pageRankScores);\n }\n\n /**\n * BM25 scoring over an inverted index.\n * Returns Map<docIndex, score>\n */\n private bm25Search(queryTerms: string[], index: Map<string, Posting[]>): Map<number, number> {\n const scores = new Map<number, number>();\n const N = this.documents.length;\n const { k1, b } = this.config.bm25;\n\n for (const term of queryTerms) {\n const postings = index.get(term);\n if (!postings) continue;\n\n const df = postings.length; // document frequency\n // IDF: log((N - df + 0.5) / (df + 0.5) + 1)\n const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);\n\n for (const { docIndex, termFrequency } of postings) {\n const doc = this.documents[docIndex]!;\n const tf = termFrequency;\n const docLen = doc.length;\n\n // BM25 term score\n const numerator = tf * (k1 + 1);\n const denominator = tf + k1 * (1 - b + b * (docLen / this.avgDocLength));\n const termScore = idf * (numerator / denominator);\n\n scores.set(docIndex, (scores.get(docIndex) ?? 0) + termScore);\n }\n }\n\n // Boost exact name matches (FR-1.9: 3-5x higher)\n const queryJoined = queryTerms.join('');\n for (const [docIdx, score] of scores) {\n const doc = this.documents[docIdx]!;\n const nameLower = doc.name.toLowerCase();\n if (nameLower === queryJoined || nameLower === queryTerms.join(' ')) {\n scores.set(docIdx, score * 5.0);\n }\n }\n\n return scores;\n }\n\n /**\n * Merge two score maps. Phase 2 scores are penalized.\n */\n private mergeScores(\n primary: Map<number, number>,\n secondary: Map<number, number>,\n penalty: number,\n ): Map<number, number> {\n const merged = new Map(primary);\n for (const [docIdx, score] of secondary) {\n const existing = merged.get(docIdx);\n const penalized = score * penalty;\n if (existing !== undefined) {\n merged.set(docIdx, Math.max(existing, penalized));\n } else {\n merged.set(docIdx, penalized);\n }\n }\n return merged;\n }\n\n /**\n * Bigram boost for multi-word queries.\n * Each adjacent query term pair found adjacent in symbol tokens → 2x boost.\n */\n private bigramBoost(queryTerms: string[], symbolTokens: string[]): number {\n if (queryTerms.length < 2) return 1.0;\n\n let adjacentPairs = 0;\n for (let i = 0; i < queryTerms.length - 1; i++) {\n const idxA = symbolTokens.findIndex((t) => t.includes(queryTerms[i]!));\n const idxB = symbolTokens.findIndex((t) => t.includes(queryTerms[i + 1]!));\n if (idxA >= 0 && idxB >= 0 && Math.abs(idxA - idxB) === 1) {\n adjacentPairs++;\n }\n }\n\n return 1 + adjacentPairs * 2.0;\n }\n\n /**\n * Extract character trigrams from a string.\n * \"sqlite\" → [\"sql\", \"qli\", \"lit\", \"ite\"]\n */\n private extractTrigrams(text: string): string[] {\n const trigrams: string[] = [];\n for (let i = 0; i <= text.length - 3; i++) {\n trigrams.push(text.substring(i, i + 3));\n }\n return trigrams;\n }\n\n private buildResults(\n combinedScores: Map<number, number>,\n rawRelevance: Map<number, number>,\n limit: number,\n ): SearchResult[] {\n const entries = [...combinedScores.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit);\n\n return entries.map(([docIdx, combined]) => {\n const doc = this.documents[docIdx]!;\n const pr = this.pageRankScores.get(doc.symbolId) ?? 0;\n return {\n symbolId: doc.symbolId,\n name: doc.name,\n kind: doc.kind,\n filePath: doc.filePath,\n relevanceScore: rawRelevance.get(docIdx) ?? 0,\n importanceScore: pr,\n combinedScore: combined,\n };\n });\n }\n\n private emptyResponse(query: string, start: number): SearchResponse {\n return {\n query,\n results: [],\n metrics: {\n porterHits: 0,\n trigramHits: 0,\n phase2Activated: false,\n fuzzyApplied: false,\n latencyMs: performance.now() - start,\n },\n };\n }\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { SearchEngine } from '../../core/search/search-engine.js';\nimport { SymbolKindSchema } from '../../core/types.js';\nimport type { ISearchPort } from '../../ports/i-search-port.js';\n\nexport const InputSchema = z.object({\n pattern: z.string().min(1),\n kind: SymbolKindSchema.optional(),\n filePattern: z.string().optional(),\n limit: z.number().int().min(1).max(100).optional().default(25),\n mode: z.enum(['regex', 'fts']).optional().default('regex'),\n});\n\nexport function handleSearchSymbols(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n searchEngine?: ISearchPort,\n) {\n const engine: ISearchPort = searchEngine ?? new SearchEngine();\n let lastFtsNodeCount = -1;\n\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { pattern, kind, filePattern, limit, mode } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n const allNodes = graph.allNodes();\n\n // FTS mode: use search engine\n if (mode === 'fts') {\n if (allNodes.length !== lastFtsNodeCount) {\n engine.buildIndex(allNodes);\n lastFtsNodeCount = allNodes.length;\n }\n\n const searchResult = engine.search(pattern, limit);\n let ftsResults = searchResult.results;\n\n // Apply kind filter\n if (kind) {\n ftsResults = ftsResults.filter((r) => r.kind === kind);\n }\n // Apply file pattern filter\n if (filePattern) {\n const fp = filePattern.toLowerCase();\n ftsResults = ftsResults.filter((r) => r.filePath.toLowerCase().includes(fp));\n }\n\n const results = ftsResults.slice(0, limit).map((r) => {\n const node = graph.getNode(r.symbolId);\n return {\n symbolId: r.symbolId,\n name: r.name,\n kind: r.kind,\n file: r.filePath,\n startLine: node?.startLine ?? 0,\n endLine: node?.endLine ?? 0,\n relevanceScore: Math.round(r.relevanceScore * 1000) / 1000,\n };\n });\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n totalMatches: ftsResults.length,\n results,\n searchMetrics: searchResult.metrics,\n ...(searchResult.fuzzyCorrection ? { fuzzyCorrection: searchResult.fuzzyCorrection } : {}),\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n\n // Legacy regex mode (default)\n let matcher: (name: string) => boolean;\n try {\n const regex = new RegExp(pattern, 'i');\n matcher = (name: string) => regex.test(name);\n } catch {\n const lowerPattern = pattern.toLowerCase();\n matcher = (name: string) => name.toLowerCase().includes(lowerPattern);\n }\n\n const matches = allNodes.filter((node) => {\n if (!matcher(node.name)) return false;\n if (kind && node.kind !== kind) return false;\n if (filePattern) {\n const file = node.symbolId.split('::')[0] ?? '';\n if (!file.toLowerCase().includes(filePattern.toLowerCase())) return false;\n }\n return true;\n });\n\n const results = matches.slice(0, limit).map((node) => ({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0],\n startLine: node.startLine,\n endLine: node.endLine,\n }));\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n totalMatches: matches.length,\n results,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\n\nexport const InputSchema = z.object({\n since: z.string().optional().default('HEAD~1'),\n maxFiles: z.number().int().min(1).optional().default(50),\n});\n\nexport function handleGetChangedSymbols(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { since, maxFiles } = parsed.data;\n const changedPaths = await git.getChangedFiles(since);\n const { graph, indexedFiles } = getGraph();\n\n // Index all nodes by file for fast lookup\n const nodesByFile = new Map<string, Array<{ symbolId: string; name: string; kind: string; startLine: number; endLine: number }>>();\n for (const node of graph.allNodes()) {\n const file = node.symbolId.split('::')[0] ?? '';\n const existing = nodesByFile.get(file) ?? [];\n existing.push({ symbolId: node.symbolId, name: node.name, kind: node.kind, startLine: node.startLine, endLine: node.endLine });\n nodesByFile.set(file, existing);\n }\n\n const files: Array<{ file: string; symbols: Array<{ symbolId: string; name: string; kind: string; startLine: number; endLine: number }> }> = [];\n let totalSymbols = 0;\n\n for (const filePath of changedPaths.slice(0, maxFiles)) {\n const symbols = nodesByFile.get(filePath);\n if (symbols && symbols.length > 0) {\n files.push({ file: filePath, symbols });\n totalSymbols += symbols.length;\n }\n }\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n since,\n changedFiles: files.length,\n changedSymbols: totalSymbols,\n files,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { filterByIntent } from '../../core/intent-filter.js';\nimport { EdgeKindSchema, type EdgeKind } from '../../core/types.js';\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1),\n edgeKinds: z.array(EdgeKindSchema).optional(),\n transitive: z.boolean().optional().default(false),\n maxDepth: z.number().int().min(1).max(10).optional().default(5),\n intent: z.string().optional().describe('Filter results by intent keywords (e.g., \"test\", \"adapter\", \"core\")'),\n});\n\nexport function handleFindImporters(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, edgeKinds, transitive, maxDepth } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n // Resolve the id once: a 2-part data id names its winning definition. getDependentEdges gives every\n // copy of a table, view or procedure the importers of the resource.\n const root = graph.getNode(symbolId);\n if (!root) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n const rootId = root.symbolId;\n\n const importers: Array<{\n symbolId: string;\n name: string;\n kind: string;\n file: string;\n edgeKind: string;\n edgeKinds?: string[];\n depth: number;\n }> = [];\n\n if (!transitive) {\n // Direct importers only — deduplicate by symbolId, aggregate edgeKinds\n const reverseEdges = graph.getDependentEdges(rootId);\n const seen = new Map<string, typeof importers[0]>();\n for (const edge of reverseEdges) {\n if (edgeKinds && !edgeKinds.includes(edge.kind as EdgeKind)) continue;\n const node = graph.getNode(edge.from);\n if (node) {\n const existing = seen.get(node.symbolId);\n if (existing) {\n if (!existing.edgeKinds!.includes(edge.kind)) {\n existing.edgeKinds!.push(edge.kind);\n }\n } else {\n const entry = {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n edgeKinds: [edge.kind],\n depth: 1,\n };\n seen.set(node.symbolId, entry);\n importers.push(entry);\n }\n }\n }\n } else {\n // BFS for transitive importers. No definition of the same resource counts as an importer of another.\n const definitions = graph.dataResourceIdentity(rootId)?.definitions.map((definition) => definition.symbolId) ?? [];\n const visited = new Set<string>([rootId, ...definitions]);\n const queue: Array<{ id: string; depth: number }> = [{ id: rootId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current.depth >= maxDepth) continue;\n\n const reverseEdges = graph.getDependentEdges(current.id);\n for (const edge of reverseEdges) {\n if (edgeKinds && !edgeKinds.includes(edge.kind as EdgeKind)) continue;\n if (visited.has(edge.from)) continue;\n visited.add(edge.from);\n\n const node = graph.getNode(edge.from);\n if (node) {\n const depth = current.depth + 1;\n importers.push({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n depth,\n });\n queue.push({ id: edge.from, depth });\n }\n }\n }\n }\n\n // Sort by depth ascending\n importers.sort((a, b) => a.depth - b.depth);\n\n // Apply intent filter if requested\n const filtered = filterByIntent(importers, parsed.data.intent);\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n symbolId,\n importerCount: filtered.length,\n importers: filtered,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport type { SymbolGraph } from '../../core/graph/symbol-graph.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport type { EdgeKind } from '../../core/types.js';\n\n/** The edge kinds that make up a class hierarchy. Data access edges (reads, writes, executes) never do. */\nexport const HIERARCHY_EDGE_KINDS: ReadonlySet<EdgeKind> = new Set<EdgeKind>(['extends', 'implements']);\n\nexport const InputSchema = z.object({\n symbolId: z.string().min(1).optional(),\n direction: z.enum(['ancestors', 'descendants', 'both']).optional().default('both'),\n});\n\nexport function handleGetClassHierarchy(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { symbolId, direction } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n if (symbolId) {\n return handleRooted(graph, symbolId, direction, masking, staleness, indexedFiles);\n }\n\n return handleFull(graph, masking, staleness, indexedFiles);\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n\n function handleRooted(\n graph: SymbolGraph,\n symbolId: string,\n direction: 'ancestors' | 'descendants' | 'both',\n maskingPort: IMaskingPort,\n stalenessCheck: StalenessCheck | undefined,\n indexedFiles: string[],\n ) {\n if (!graph.hasNode(symbolId)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ found: false, hint: 'Symbol not found. Run \"ctxo index\" to build the codebase index.' }) }],\n };\n }\n\n type HierarchyEntry = { symbolId: string; name: string; kind: string; file: string; edgeKind: string; depth: number };\n const ancestors: HierarchyEntry[] = [];\n const descendants: HierarchyEntry[] = [];\n\n const isHierarchyEdge = (kind: string) => HIERARCHY_EDGE_KINDS.has(kind as EdgeKind);\n\n // Ancestors: follow forward edges (this symbol extends/implements X)\n if (direction === 'ancestors' || direction === 'both') {\n const visited = new Set<string>([symbolId]);\n const queue: Array<{ id: string; depth: number }> = [{ id: symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.getForwardEdges(current.id)) {\n if (!isHierarchyEdge(edge.kind)) continue;\n if (visited.has(edge.to)) continue;\n visited.add(edge.to);\n\n const node = graph.getNode(edge.to);\n if (node) {\n const depth = current.depth + 1;\n ancestors.push({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n depth,\n });\n queue.push({ id: edge.to, depth });\n }\n }\n }\n }\n\n // Descendants: follow reverse edges (X extends/implements this symbol)\n if (direction === 'descendants' || direction === 'both') {\n const visited = new Set<string>([symbolId]);\n const queue: Array<{ id: string; depth: number }> = [{ id: symbolId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.getReverseEdges(current.id)) {\n if (!isHierarchyEdge(edge.kind)) continue;\n if (visited.has(edge.from)) continue;\n visited.add(edge.from);\n\n const node = graph.getNode(edge.from);\n if (node) {\n const depth = current.depth + 1;\n descendants.push({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n edgeKind: edge.kind,\n depth,\n });\n queue.push({ id: edge.from, depth });\n }\n }\n }\n }\n\n const result: Record<string, unknown> = { symbolId };\n if (direction === 'ancestors' || direction === 'both') result.ancestors = ancestors;\n if (direction === 'descendants' || direction === 'both') result.descendants = descendants;\n\n const payload = maskingPort.mask(JSON.stringify(wrapResponse(result)));\n const content: Array<{ type: 'text'; text: string }> = [];\n if (stalenessCheck) {\n const warning = stalenessCheck.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n\n function handleFull(\n graph: SymbolGraph,\n maskingPort: IMaskingPort,\n stalenessCheck: StalenessCheck | undefined,\n indexedFiles: string[],\n ) {\n const isHierarchyEdge = (kind: string) => HIERARCHY_EDGE_KINDS.has(kind as EdgeKind);\n const hierarchyEdges = graph.allEdges().filter((e) => isHierarchyEdge(e.kind));\n\n // Collect all nodes involved in hierarchy edges\n const involved = new Set<string>();\n for (const edge of hierarchyEdges) {\n involved.add(edge.from);\n involved.add(edge.to);\n }\n\n // Find roots: nodes that are targets but never sources of extends/implements (true base classes/interfaces)\n const sources = new Set(hierarchyEdges.map((e) => e.from));\n const roots = [...involved].filter((id) => !sources.has(id));\n\n // If no clear roots, use all involved nodes as potential roots (handles cycles)\n const rootSet = roots.length > 0 ? roots : [...involved];\n\n type TreeNode = { symbolId: string; name: string; kind: string; file: string; edgeKind?: string; children: TreeNode[] };\n\n function buildTree(rootId: string, visited: Set<string>): TreeNode | undefined {\n const node = graph.getNode(rootId);\n if (!node) return undefined;\n visited.add(rootId);\n\n const children: TreeNode[] = [];\n for (const edge of graph.getReverseEdges(rootId)) {\n if (!isHierarchyEdge(edge.kind)) continue;\n if (visited.has(edge.from)) continue;\n const child = buildTree(edge.from, visited);\n if (child) {\n child.edgeKind = edge.kind;\n children.push(child);\n }\n }\n\n return {\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n children,\n };\n }\n\n const visited = new Set<string>();\n const hierarchies: TreeNode[] = [];\n for (const rootId of rootSet) {\n if (visited.has(rootId)) continue;\n const tree = buildTree(rootId, visited);\n if (tree) hierarchies.push(tree);\n }\n\n const payload = maskingPort.mask(JSON.stringify(wrapResponse({\n hierarchies,\n totalClasses: involved.size,\n totalEdges: hierarchyEdges.length,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (stalenessCheck) {\n const warning = stalenessCheck.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n }\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport { PageRankCalculator } from '../../core/importance/pagerank-calculator.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport { SymbolKindSchema } from '../../core/types.js';\n\nexport const InputSchema = z.object({\n limit: z.number().int().min(1).max(200).optional().default(25),\n kind: SymbolKindSchema.optional(),\n filePattern: z.string().optional(),\n damping: z.number().min(0).max(1).optional().default(0.85),\n});\n\nexport function handleGetSymbolImportance(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const calculator = new PageRankCalculator();\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { limit, kind, filePattern, damping } = parsed.data;\n const { graph, indexedFiles } = getGraph();\n\n const result = calculator.calculate(graph, { damping, limit: graph.nodeCount, maxIterations: 100 });\n\n // Filter after full PageRank computation (filtering before would distort scores)\n let filtered = result.rankings;\n if (kind) {\n filtered = filtered.filter((e) => e.kind === kind);\n }\n if (filePattern) {\n const lowerPattern = filePattern.toLowerCase();\n filtered = filtered.filter((e) => e.file.toLowerCase().includes(lowerPattern));\n }\n filtered = filtered.slice(0, limit);\n\n const payload = masking.mask(JSON.stringify(wrapResponse({\n rankings: filtered,\n totalSymbols: result.totalSymbols,\n iterations: result.iterations,\n converged: result.converged,\n damping,\n })));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import { z } from 'zod';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { IMaskingPort } from '../../ports/i-masking-port.js';\nimport type { ICoChangePort } from '../../ports/i-co-change-port.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\nimport { getGraphAndFiles } from './get-logic-slice.js';\nimport { BlastRadiusCalculator } from '../../core/blast-radius/blast-radius-calculator.js';\nimport { BoundaryViolationDetector } from '../../core/overlay/boundary-violation-detector.js';\nimport { buildSnapshotStaleness } from '../../core/overlay/snapshot-staleness.js';\nimport type { ClusterLabelMasker } from '../../core/overlay/cluster-label-masker.js';\nimport { wrapResponse } from '../../core/response-envelope.js';\nimport type { CommunitySnapshot } from '../../core/types.js';\n\nexport const InputSchema = z.object({\n since: z.string().optional().default('HEAD~1'),\n maxFiles: z.number().int().min(1).optional().default(50),\n confidence: z.enum(['confirmed', 'likely', 'potential']).optional(),\n});\n\nfunction computeClustersAffected(\n snapshot: CommunitySnapshot,\n changedSymbolIds: ReadonlySet<string>,\n): Array<{ id: number; label: string; symbolCount: number }> {\n const totals = new Map<number, { label: string; count: number }>();\n for (const entry of snapshot.communities) {\n if (!changedSymbolIds.has(entry.symbolId)) continue;\n const existing = totals.get(entry.communityId);\n if (existing) {\n existing.count += 1;\n } else {\n totals.set(entry.communityId, { label: entry.communityLabel, count: 1 });\n }\n }\n return [...totals.entries()]\n .map(([id, { label, count }]) => ({ id, label, symbolCount: count }))\n .sort((a, b) => b.symbolCount - a.symbolCount || a.id - b.id);\n}\n\nexport function handleGetPrImpact(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n clusterLabelMasker?: ClusterLabelMasker,\n coChange?: ICoChangePort,\n) {\n const calculator = new BlastRadiusCalculator();\n const getGraph = () => getGraphAndFiles(ctxoRoot, storage);\n\n return async (args: Record<string, unknown>) => {\n try {\n const parsed = InputSchema.safeParse(args);\n if (!parsed.success) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: parsed.error.message }) }],\n };\n }\n\n const { since, maxFiles, confidence: confFilter } = parsed.data;\n\n // Step 1: Get changed files\n const changedPaths = await git.getChangedFiles(since);\n if (changedPaths.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ since, changedFiles: 0, changedSymbols: 0, totalImpact: 0, riskLevel: 'low', files: [], summary: { confirmedTotal: 0, likelyTotal: 0, potentialTotal: 0, highRiskSymbols: [] } }) }],\n };\n }\n\n // Step 2: Build graph + load co-changes\n const { graph, indexedFiles } = getGraph();\n const coChangeMap = coChange?.readCoChangeMap();\n\n // Step 3: Map files → symbols\n const allNodes = graph.allNodes();\n const nodesByFile = new Map<string, typeof allNodes>();\n for (const node of allNodes) {\n const file = node.symbolId.split('::')[0]!;\n let list = nodesByFile.get(file);\n if (!list) { list = []; nodesByFile.set(file, list); }\n list.push(node);\n }\n\n // Step 4: For each changed file, compute blast radius per symbol\n const limitedPaths = changedPaths.slice(0, maxFiles);\n let totalImpact = 0;\n let confirmedTotal = 0;\n let likelyTotal = 0;\n let potentialTotal = 0;\n const highRiskSymbols: string[] = [];\n\n const files: Array<{\n file: string;\n symbols: Array<{\n symbolId: string;\n name: string;\n kind: string;\n blast: {\n impactScore: number;\n confirmedCount: number;\n likelyCount: number;\n potentialCount: number;\n riskScore: number;\n topImpacted: Array<unknown>;\n };\n }>;\n coChangedWith?: string[];\n }> = [];\n\n for (const filePath of limitedPaths) {\n const symbols = nodesByFile.get(filePath);\n if (!symbols || symbols.length === 0) continue;\n\n const fileSymbols: typeof files[0]['symbols'] = [];\n\n for (const sym of symbols) {\n const result = calculator.calculate(graph, sym.symbolId, coChangeMap);\n\n let impacted = result.impactedSymbols;\n if (confFilter) {\n impacted = impacted.filter(s => s.confidence === confFilter);\n }\n\n const impactScore = impacted.length;\n const confirmed = impacted.filter(s => s.confidence === 'confirmed').length;\n const likely = impacted.filter(s => s.confidence === 'likely').length;\n const potential = impacted.filter(s => s.confidence === 'potential').length;\n\n totalImpact += impactScore;\n confirmedTotal += confirmed;\n likelyTotal += likely;\n potentialTotal += potential;\n\n if (result.overallRiskScore > 0.7) {\n highRiskSymbols.push(sym.symbolId);\n }\n\n const topImpacted = impacted\n .sort((a, b) => b.riskScore - a.riskScore)\n .slice(0, 10);\n\n fileSymbols.push({\n symbolId: sym.symbolId,\n name: sym.name,\n kind: sym.kind,\n blast: {\n impactScore,\n confirmedCount: confirmed,\n likelyCount: likely,\n potentialCount: potential,\n riskScore: result.overallRiskScore,\n topImpacted,\n },\n });\n }\n\n // Co-changed files for this file\n let coChangedWith: string[] | undefined;\n if (coChangeMap) {\n const entries = coChangeMap.get(filePath);\n if (entries && entries.length > 0) {\n // The map is the port's cached copy, shared with every other call: sort a copy of the list.\n coChangedWith = [...entries]\n .sort((a, b) => b.frequency - a.frequency)\n .slice(0, 5)\n .map(e => e.file1 === filePath ? e.file2 : e.file1);\n }\n }\n\n files.push({\n file: filePath,\n symbols: fileSymbols,\n ...(coChangedWith ? { coChangedWith } : {}),\n });\n }\n\n // Step 5: Compute risk level\n const maxRisk = files.reduce((max, f) => {\n const fileMax = f.symbols.reduce((m, s) => Math.max(m, s.blast.riskScore), 0);\n return Math.max(max, fileMax);\n }, 0);\n const riskLevel = maxRisk > 0.7 ? 'high' : maxRisk > 0.3 ? 'medium' : 'low';\n\n const changedSymbols = files.reduce((sum, f) => sum + f.symbols.length, 0);\n\n const rawSnapshot = storage.readCommunities();\n const snapshot =\n rawSnapshot && clusterLabelMasker?.isEnabled()\n ? clusterLabelMasker.maskSnapshot(rawSnapshot)\n : rawSnapshot;\n const history = snapshot ? storage.listCommunityHistory() : [];\n let boundarySection: Record<string, unknown> | undefined;\n let clustersAffected: Array<{ id: number; label: string; symbolCount: number }> | undefined;\n\n if (snapshot) {\n const detector = new BoundaryViolationDetector();\n const violationResult = detector.detect(graph, snapshot, history);\n const changedSymbolIds = new Set<string>();\n for (const file of files) {\n for (const sym of file.symbols) changedSymbolIds.add(sym.symbolId);\n }\n const relevant = violationResult.violations.filter(\n (v) => changedSymbolIds.has(v.from.symbolId) || changedSymbolIds.has(v.to.symbolId),\n );\n boundarySection = {\n violations: relevant,\n confidence: violationResult.confidence,\n snapshotsAvailable: violationResult.snapshotsAvailable,\n distinctCommits: violationResult.distinctCommits,\n ...(violationResult.hint ? { hint: violationResult.hint } : {}),\n };\n clustersAffected = computeClustersAffected(snapshot, changedSymbolIds);\n }\n\n const body: Record<string, unknown> = {\n since,\n changedFiles: files.length,\n changedSymbols,\n totalImpact,\n riskLevel,\n files,\n summary: {\n confirmedTotal,\n likelyTotal,\n potentialTotal,\n highRiskSymbols: highRiskSymbols.slice(0, 10),\n },\n };\n if (boundarySection) body.boundaryViolations = boundarySection;\n if (clustersAffected && clustersAffected.length > 0) body.clustersAffected = clustersAffected;\n\n const stalenessMeta = await buildSnapshotStaleness(storage, git);\n const extras = stalenessMeta ? { snapshotStaleness: stalenessMeta } : undefined;\n const payload = masking.mask(JSON.stringify(wrapResponse(body, extras)));\n\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(indexedFiles);\n if (warning) content.push({ type: 'text', text: `⚠️ ${warning.message}` });\n }\n content.push({ type: 'text', text: payload });\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: true, message: (err as Error).message }) }],\n };\n }\n };\n}\n","import type { ISessionRecorderPort, SessionEvent } from '../../ports/i-session-recorder-port.js';\n\ntype McpToolResult = { content: Array<{ type: 'text'; text: string }> };\ntype McpHandler = (args: Record<string, unknown>) => McpToolResult | Promise<McpToolResult>;\n\n/**\n * Wraps an MCP handler with session recording.\n * Recording is fire-and-forget — errors are swallowed, never affect tool response.\n *\n * @param toolName - MCP tool name (e.g., 'get_logic_slice')\n * @param handler - Original handler function (sync or async)\n * @param recorder - Session recorder (null = recording disabled, zero overhead)\n */\nexport function withRecording(\n toolName: string,\n handler: McpHandler,\n recorder: ISessionRecorderPort | null,\n): McpHandler {\n if (!recorder) return handler;\n\n return (args: Record<string, unknown>) => {\n const start = performance.now();\n const resultOrPromise = handler(args);\n\n // Handle async handlers\n if (resultOrPromise instanceof Promise) {\n return resultOrPromise.then((result) => {\n recordEvent(toolName, args, result, performance.now() - start, recorder);\n return result;\n });\n }\n\n // Sync handler\n const latencyMs = performance.now() - start;\n recordEvent(toolName, args, resultOrPromise, latencyMs, recorder);\n return resultOrPromise;\n };\n}\n\nfunction recordEvent(\n toolName: string,\n args: Record<string, unknown>,\n result: McpToolResult,\n latencyMs: number,\n recorder: ISessionRecorderPort,\n): void {\n try {\n const responseText = result.content\n .filter((c) => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n let truncated = false;\n try {\n const parsed = JSON.parse(responseText);\n truncated = parsed?._meta?.truncated ?? false;\n } catch { /* non-JSON response, ignore */ }\n\n const responseBytes = Buffer.byteLength(responseText, 'utf-8');\n\n // Extract detail level: args.level (number) → 'L1'...'L4'\n const rawLevel = args['level'];\n let detailLevel: SessionEvent['detailLevel'] = null;\n if (typeof rawLevel === 'number' && rawLevel >= 1 && rawLevel <= 4) {\n detailLevel = `L${rawLevel}` as SessionEvent['detailLevel'];\n }\n\n const event: SessionEvent = {\n tool: toolName,\n symbolId: (typeof args['symbolId'] === 'string' ? args['symbolId'] : null),\n detailLevel,\n responseTokens: Math.ceil(responseBytes / 4),\n responseBytes,\n latencyMs,\n truncated,\n };\n\n recorder.record(event);\n } catch {\n // Recording failure must never affect tool response\n }\n}\n","import type { CommunityEntry, CommunitySnapshot, GodNode } from '../types.js';\nimport { makeGlobMatcher } from '../config/load-config.js';\n\n/**\n * Replace sensitive cluster labels with `[masked-cluster-N]`. Labels matching\n * any configured glob (e.g. `internal-*`, `*-vault`, `src/payment-*`) are\n * rewritten deterministically so MCP tool responses never leak org-sensitive\n * directory names to AI agents over the stdio channel.\n *\n * Determinism matters: drift-detector compares labels across snapshots, so\n * the same input label must always produce the same mask. We seed the mask\n * counter from the snapshot's sorted, unique label set.\n */\nexport class ClusterLabelMasker {\n private readonly matcher: (input: string) => boolean;\n private readonly enabled: boolean;\n private readonly labelCache = new Map<string, string>();\n\n constructor(patterns: readonly string[]) {\n this.enabled = patterns.length > 0;\n this.matcher = this.enabled ? makeGlobMatcher([...patterns]) : () => false;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n mask(label: string): string {\n if (!this.enabled) return label;\n if (!this.matcher(label)) return label;\n const cached = this.labelCache.get(label);\n if (cached) return cached;\n const masked = `[masked-cluster-${this.labelCache.size + 1}]`;\n this.labelCache.set(label, masked);\n return masked;\n }\n\n maskSnapshot(snapshot: CommunitySnapshot): CommunitySnapshot {\n if (!this.enabled) return snapshot;\n // Seed the cache in sorted order so output is stable across runs.\n const uniqueLabels = [...new Set(snapshot.communities.map((c) => c.communityLabel))].sort();\n for (const label of uniqueLabels) this.mask(label);\n\n const maskedCommunities: CommunityEntry[] = snapshot.communities.map((entry) => ({\n ...entry,\n communityLabel: this.mask(entry.communityLabel),\n }));\n const maskedGodNodes: GodNode[] = snapshot.godNodes.map((g) => ({ ...g }));\n return { ...snapshot, communities: maskedCommunities, godNodes: maskedGodNodes };\n }\n}\n"],"mappings":"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","names":["z","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","payload","body","z","InputSchema","z","z","z","InputSchema","z","log","log","InputSchema","z","payload","content","z","InputSchema","z","results","payload","content","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","visited","z","InputSchema","z","z","InputSchema","z","log","z"]}
|