ctxo-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/masking/masking-pipeline.ts","../src/adapters/mcp/get-logic-slice.ts","../src/core/graph/symbol-graph.ts","../src/core/logic-slice/logic-slice-query.ts","../src/core/detail-levels/detail-formatter.ts","../src/adapters/mcp/get-why-context.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/core/blast-radius/blast-radius-calculator.ts","../src/adapters/mcp/get-architectural-overlay.ts","../src/core/overlay/architectural-overlay.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\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';\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 console.error(`[ctxo] Loaded ${patterns.length} custom masking pattern(s)`);\n return MaskingPipeline.fromConfig(patterns);\n } catch (err) {\n console.error(`[ctxo] Failed to load masking config: ${(err as Error).message}`);\n }\n }\n\n return new MaskingPipeline();\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (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 storage = new SqliteStorageAdapter(ctxoRoot);\n await storage.init();\n\n const masking = loadMaskingConfig(ctxoRoot);\n const git = new SimpleGitAdapter(process.cwd());\n\n // Create MCP server\n const server = new McpServer({ name: 'ctxo', version: '0.1.0' });\n\n // Staleness detection\n const { StalenessDetector } = await import('./core/staleness/staleness-detector.js');\n const staleness = new StalenessDetector(process.cwd(), ctxoRoot);\n\n // Register tools\n const logicSliceHandler = handleGetLogicSlice(storage, masking, staleness, ctxoRoot);\n const whyContextHandler = handleGetWhyContext(storage, git, masking, staleness, ctxoRoot);\n const changeIntelligenceHandler = handleGetChangeIntelligence(storage, git, masking, staleness, ctxoRoot);\n\n server.registerTool(\n 'get_logic_slice',\n {\n description: 'Retrieve a Logic-Slice for a named symbol — the symbol plus all transitive dependencies',\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 },\n },\n (args) => logicSliceHandler(args),\n );\n\n server.registerTool(\n 'get_why_context',\n {\n description: 'Retrieve git commit intent, anti-pattern warnings from revert history for a symbol',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n },\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',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n },\n },\n (args) => changeIntelligenceHandler(args),\n );\n\n const blastRadiusHandler = handleGetBlastRadius(storage, masking, staleness, ctxoRoot);\n\n server.registerTool(\n 'get_blast_radius',\n {\n description: 'Retrieve the blast radius for a symbol — symbols that would break if it changed',\n inputSchema: {\n symbolId: z.string().min(1).describe('The symbol ID (format: file::name::kind)'),\n },\n },\n (args) => blastRadiusHandler(args),\n );\n\n const overlayHandler = handleGetArchitecturalOverlay(storage, masking, staleness);\n\n server.registerTool(\n 'get_architectural_overlay',\n {\n description: 'Retrieve an architectural overlay — layer map identifying Domain, Infrastructure, and Adapter boundaries',\n inputSchema: {\n layer: z.string().optional().describe('Filter by specific layer name'),\n },\n },\n (args) => overlayHandler(args),\n );\n\n // Start MCP server\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err: unknown) => {\n console.error('[ctxo] Fatal:', (err as Error).message);\n process.exit(1);\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 + — excludes hex-only git hashes)\n { regex: /(?<![A-Za-z0-9/+])(?=[A-Za-z0-9/+=]{40}(?![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';\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}).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 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 const reader = new JsonIndexReader(ctxoRoot);\n const indices = reader.readAll();\n\n const graph = new SymbolGraph();\n // Phase 1: all nodes\n for (const fileIndex of indices) {\n for (const sym of fileIndex.symbols) {\n graph.addNode(sym);\n }\n }\n // Phase 2: all edges (fuzzy resolution active since nodes are loaded)\n for (const fileIndex of indices) {\n for (const edge of fileIndex.edges) {\n graph.addEdge(edge);\n }\n }\n return graph;\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 = () => {\n const jsonGraph = buildGraphFromJsonIndex(ctxoRoot);\n if (jsonGraph.nodeCount > 0) return jsonGraph;\n return buildGraphFromStorage(storage);\n };\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 } = parsed.data;\n const ids = symbolIds ?? (symbolId ? [symbolId] : []);\n\n const graph = 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 results.push(formatter.format(slice, level));\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(responseData));\n\n // Check staleness\n const content: Array<{ type: 'text'; text: string }> = [];\n if (staleness) {\n const warning = staleness.check(storage.listIndexedFiles());\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 } from '../types.js';\n\nexport class SymbolGraph {\n private readonly nodes = new Map<string, SymbolNode>();\n private readonly nodesByFileAndName = new Map<string, SymbolNode>();\n private readonly forwardEdges = new Map<string, GraphEdge[]>();\n private readonly reverseEdges = new Map<string, GraphEdge[]>();\n private edgeSet = new Set<string>();\n\n addNode(node: SymbolNode): void {\n this.nodes.set(node.symbolId, node);\n // Index by file::name (ignoring kind) for fuzzy edge resolution\n const parts = node.symbolId.split('::');\n if (parts.length >= 2) {\n this.nodesByFileAndName.set(`${parts[0]}::${parts[1]}`, node);\n }\n }\n\n addEdge(edge: GraphEdge): void {\n // Resolve edge endpoints: if exact ID not found, try fuzzy match by file::name\n const resolvedFrom = this.resolveNodeId(edge.from);\n const resolvedTo = this.resolveNodeId(edge.to);\n const resolvedEdge: GraphEdge = { from: resolvedFrom, to: resolvedTo, kind: edge.kind };\n\n const edgeKey = `${resolvedEdge.from}|${resolvedEdge.to}|${resolvedEdge.kind}`;\n if (this.edgeSet.has(edgeKey)) return;\n this.edgeSet.add(edgeKey);\n\n const forward = this.forwardEdges.get(resolvedEdge.from) ?? [];\n forward.push(resolvedEdge);\n this.forwardEdges.set(resolvedEdge.from, forward);\n\n const reverse = this.reverseEdges.get(resolvedEdge.to) ?? [];\n reverse.push(resolvedEdge);\n this.reverseEdges.set(resolvedEdge.to, reverse);\n }\n\n getNode(symbolId: string): SymbolNode | undefined {\n return this.nodes.get(symbolId) ?? this.resolveNodeFuzzy(symbolId);\n }\n\n private resolveNodeId(id: string): string {\n if (this.nodes.has(id)) return id;\n const parts = id.split('::');\n if (parts.length >= 2) {\n const fuzzyKey = `${parts[0]}::${parts[1]}`;\n const match = this.nodesByFileAndName.get(fuzzyKey);\n if (match) return match.symbolId;\n }\n return id;\n }\n\n private resolveNodeFuzzy(id: string): SymbolNode | undefined {\n const parts = id.split('::');\n if (parts.length >= 2) {\n return this.nodesByFileAndName.get(`${parts[0]}::${parts[1]}`);\n }\n return undefined;\n }\n\n getForwardEdges(symbolId: string): GraphEdge[] {\n return this.forwardEdges.get(symbolId) ?? [];\n }\n\n getReverseEdges(symbolId: string): GraphEdge[] {\n return this.reverseEdges.get(symbolId) ?? [];\n }\n\n hasNode(symbolId: string): boolean {\n return this.nodes.has(symbolId);\n }\n\n get nodeCount(): number {\n return this.nodes.size;\n }\n\n get edgeCount(): number {\n return this.edgeSet.size;\n }\n\n allNodes(): SymbolNode[] {\n return [...this.nodes.values()];\n }\n\n allEdges(): GraphEdge[] {\n const edges: GraphEdge[] = [];\n for (const list of this.forwardEdges.values()) {\n edges.push(...list);\n }\n return edges;\n }\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 // Estimate based on line count (average ~40 chars per line)\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","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 { JsonIndexReader } from '../storage/json-index-reader.js';\nimport type { CommitIntent, AntiPattern } from '../../core/types.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n});\n\nexport function handleGetWhyContext(\n storage: IStoragePort,\n git: IGitPort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const revertDetector = new RevertDetector();\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\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 // 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,\n antiPatternWarnings: antiPatterns,\n };\n\n if (antiPatterns.length > 0) {\n responsePayload.warningBadge = '⚠ Anti-pattern detected';\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 { 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 // 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 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 { 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 type { StalenessCheck } from './get-logic-slice.js';\nimport { buildGraphFromJsonIndex, buildGraphFromStorage } from './get-logic-slice.js';\n\nconst InputSchema = z.object({\n symbolId: z.string().min(1),\n});\n\nexport function handleGetBlastRadius(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n ctxoRoot = '.ctxo',\n) {\n const calculator = new BlastRadiusCalculator();\n const getGraph = () => {\n const jsonGraph = buildGraphFromJsonIndex(ctxoRoot);\n if (jsonGraph.nodeCount > 0) return jsonGraph;\n return buildGraphFromStorage(storage);\n };\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 } = parsed.data;\n const graph = 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 const payload = masking.mask(JSON.stringify({\n symbolId,\n impactScore: result.impactedSymbols.length,\n directDependentsCount: result.directDependentsCount,\n overallRiskScore: result.overallRiskScore,\n impactedSymbols: result.impactedSymbols,\n }));\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 { SymbolGraph } from '../graph/symbol-graph.js';\n\nexport interface BlastRadiusEntry {\n readonly symbolId: string;\n readonly depth: number;\n readonly dependentCount: number;\n readonly riskScore: number;\n}\n\nexport interface BlastRadiusResult {\n readonly impactedSymbols: BlastRadiusEntry[];\n readonly directDependentsCount: number;\n readonly overallRiskScore: number;\n}\n\nexport class BlastRadiusCalculator {\n calculate(graph: SymbolGraph, symbolId: string): BlastRadiusResult {\n if (!graph.hasNode(symbolId)) {\n return { impactedSymbols: [], directDependentsCount: 0, overallRiskScore: 0 };\n }\n\n const visited = new Set<string>([symbolId]);\n const entries: BlastRadiusEntry[] = [];\n\n // BFS via reverse edges (who depends on this symbol?)\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 const reverseEdges = graph.getReverseEdges(current.id);\n for (const edge of reverseEdges) {\n if (visited.has(edge.from)) continue;\n visited.add(edge.from);\n\n if (!graph.hasNode(edge.from)) continue;\n\n const depth = current.depth + 1;\n\n // Risk score: 1/depth^0.7 — closer dependents = higher risk\n const riskScore = 1 / Math.pow(depth, 0.7);\n\n entries.push({\n symbolId: edge.from,\n depth,\n dependentCount: graph.getReverseEdges(edge.from).length,\n riskScore: Math.round(riskScore * 1000) / 1000,\n });\n\n queue.push({ id: edge.from, depth });\n }\n }\n\n // Sort by depth ascending\n entries.sort((a, b) => a.depth - b.depth);\n\n const directDependentsCount = entries.filter((e) => e.depth === 1).length;\n\n // Overall risk: sum of all risk scores, normalized to 0.0–1.0\n const totalRisk = entries.reduce((sum, e) => sum + e.riskScore, 0);\n const maxPossibleRisk = entries.length > 0 ? entries.length : 1;\n const overallRiskScore = Math.round(Math.min(totalRisk / maxPossibleRisk, 1) * 1000) / 1000;\n\n return { impactedSymbols: entries, directDependentsCount, overallRiskScore };\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 { ArchitecturalOverlay } from '../../core/overlay/architectural-overlay.js';\nimport type { StalenessCheck } from './get-logic-slice.js';\n\nconst InputSchema = z.object({\n layer: z.string().optional(),\n});\n\nexport function handleGetArchitecturalOverlay(\n storage: IStoragePort,\n masking: IMaskingPort,\n staleness?: StalenessCheck,\n) {\n const overlay = new ArchitecturalOverlay();\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 files = storage.listIndexedFiles();\n const result = overlay.classify(files);\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 // Filter by layer if specified\n if (parsed.data.layer) {\n const filtered = result.layers[parsed.data.layer];\n const payload = masking.mask(JSON.stringify({ layer: parsed.data.layer, files: filtered ?? [] }));\n return { content: buildContent(payload) };\n }\n\n const payload = masking.mask(JSON.stringify(result));\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","export interface LayerRule {\n readonly pattern: RegExp;\n readonly layer: string;\n}\n\nexport interface OverlayResult {\n readonly layers: Record<string, string[]>;\n}\n\nconst DEFAULT_RULES: LayerRule[] = [\n // Test layer (matched first — __tests__, .test.ts, tests/, fixtures)\n { pattern: /__tests__/, layer: 'Test' },\n { pattern: /\\.test\\.ts$/, layer: 'Test' },\n { pattern: /\\btests\\b/, layer: 'Test' },\n { pattern: /\\bfixtures?\\b/, layer: 'Test' },\n // Composition root\n { pattern: /src\\/index\\.ts$/, layer: 'Composition' },\n // Domain\n { pattern: /\\bcore\\b/, layer: 'Domain' },\n { pattern: /\\bports?\\b/, layer: 'Domain' },\n // Adapter\n { pattern: /\\badapters?\\b/, layer: 'Adapter' },\n { pattern: /\\bcli\\b/, layer: 'Adapter' },\n // Infrastructure\n { pattern: /\\binfra\\b/, layer: 'Infrastructure' },\n { pattern: /\\bdb\\b/, layer: 'Infrastructure' },\n { pattern: /\\bqueue\\b/, layer: 'Infrastructure' },\n // Config files\n { pattern: /\\.(config|rc)\\.(ts|js|json)$/, layer: 'Configuration' },\n];\n\nexport class ArchitecturalOverlay {\n private readonly rules: LayerRule[];\n\n constructor(customRules?: LayerRule[]) {\n this.rules = (customRules ?? DEFAULT_RULES).map(({ pattern, layer }) => ({\n pattern: new RegExp(pattern.source, pattern.flags),\n layer,\n }));\n }\n\n classify(filePaths: readonly string[]): OverlayResult {\n const layers: Record<string, string[]> = {};\n\n for (const filePath of filePaths) {\n const layer = this.matchLayer(filePath);\n const list = layers[layer] ?? [];\n list.push(filePath);\n layers[layer] = list;\n }\n\n return { layers };\n }\n\n private matchLayer(filePath: string): string {\n for (const rule of this.rules) {\n if (rule.pattern.test(filePath)) {\n return rule.layer;\n }\n }\n return 'Unknown';\n }\n}\n"],"mappings":"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","names":["z","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","z","InputSchema","z","payload","z","args"]}
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ JsonIndexReader
4
+ } from "./chunk-54ETLIQX.js";
5
+ export {
6
+ JsonIndexReader
7
+ };
8
+ //# sourceMappingURL=json-index-reader-PNLPAS42.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/staleness/staleness-detector.ts
4
+ import { statSync, existsSync } from "fs";
5
+ import { join } from "path";
6
+ var StalenessDetector = class {
7
+ projectRoot;
8
+ indexDir;
9
+ constructor(projectRoot, ctxoRoot) {
10
+ this.projectRoot = projectRoot;
11
+ this.indexDir = join(ctxoRoot, "index");
12
+ }
13
+ check(indexedFiles) {
14
+ if (!existsSync(this.indexDir)) return void 0;
15
+ const staleFiles = [];
16
+ for (const relativePath of indexedFiles) {
17
+ const sourcePath = join(this.projectRoot, relativePath);
18
+ const indexPath = join(this.indexDir, `${relativePath}.json`);
19
+ if (!existsSync(sourcePath) || !existsSync(indexPath)) continue;
20
+ try {
21
+ const sourceMtime = Math.floor(statSync(sourcePath).mtimeMs / 1e3);
22
+ const indexMtime = Math.floor(statSync(indexPath).mtimeMs / 1e3);
23
+ if (sourceMtime > indexMtime) {
24
+ staleFiles.push(relativePath);
25
+ }
26
+ } catch {
27
+ }
28
+ }
29
+ if (staleFiles.length === 0) return void 0;
30
+ return {
31
+ staleFiles,
32
+ message: `Index may be stale for ${staleFiles.length} file(s). Run "ctxo index" to refresh.`
33
+ };
34
+ }
35
+ };
36
+ export {
37
+ StalenessDetector
38
+ };
39
+ //# sourceMappingURL=staleness-detector-5AN223FM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/staleness/staleness-detector.ts"],"sourcesContent":["import { statSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface StalenessWarning {\n readonly staleFiles: string[];\n readonly message: string;\n}\n\nexport class StalenessDetector {\n private readonly projectRoot: string;\n private readonly indexDir: string;\n\n constructor(projectRoot: string, ctxoRoot: string) {\n this.projectRoot = projectRoot;\n this.indexDir = join(ctxoRoot, 'index');\n }\n\n check(indexedFiles: readonly string[]): StalenessWarning | undefined {\n if (!existsSync(this.indexDir)) return undefined;\n\n const staleFiles: string[] = [];\n\n for (const relativePath of indexedFiles) {\n const sourcePath = join(this.projectRoot, relativePath);\n const indexPath = join(this.indexDir, `${relativePath}.json`);\n\n if (!existsSync(sourcePath) || !existsSync(indexPath)) continue;\n\n try {\n const sourceMtime = Math.floor(statSync(sourcePath).mtimeMs / 1000);\n const indexMtime = Math.floor(statSync(indexPath).mtimeMs / 1000);\n\n if (sourceMtime > indexMtime) {\n staleFiles.push(relativePath);\n }\n } catch {\n // Skip files we can't stat\n }\n }\n\n if (staleFiles.length === 0) return undefined;\n\n return {\n staleFiles,\n message: `Index may be stale for ${staleFiles.length} file(s). Run \"ctxo index\" to refresh.`,\n };\n }\n}\n"],"mappings":";;;AAAA,SAAS,UAAU,kBAAkB;AACrC,SAAS,YAAY;AAOd,IAAM,oBAAN,MAAwB;AAAA,EACZ;AAAA,EACA;AAAA,EAEjB,YAAY,aAAqB,UAAkB;AACjD,SAAK,cAAc;AACnB,SAAK,WAAW,KAAK,UAAU,OAAO;AAAA,EACxC;AAAA,EAEA,MAAM,cAA+D;AACnE,QAAI,CAAC,WAAW,KAAK,QAAQ,EAAG,QAAO;AAEvC,UAAM,aAAuB,CAAC;AAE9B,eAAW,gBAAgB,cAAc;AACvC,YAAM,aAAa,KAAK,KAAK,aAAa,YAAY;AACtD,YAAM,YAAY,KAAK,KAAK,UAAU,GAAG,YAAY,OAAO;AAE5D,UAAI,CAAC,WAAW,UAAU,KAAK,CAAC,WAAW,SAAS,EAAG;AAEvD,UAAI;AACF,cAAM,cAAc,KAAK,MAAM,SAAS,UAAU,EAAE,UAAU,GAAI;AAClE,cAAM,aAAa,KAAK,MAAM,SAAS,SAAS,EAAE,UAAU,GAAI;AAEhE,YAAI,cAAc,YAAY;AAC5B,qBAAW,KAAK,YAAY;AAAA,QAC9B;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,WAAO;AAAA,MACL;AAAA,MACA,SAAS,0BAA0B,WAAW,MAAM;AAAA,IACtD;AAAA,EACF;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "ctxo-mcp",
3
+ "version": "0.2.0",
4
+ "description": "MCP server delivering dependency-aware, history-enriched context for codebases",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "bin": {
10
+ "ctxo": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist/",
14
+ "README.md"
15
+ ],
16
+ "main": "dist/index.js",
17
+ "types": "dist/index.d.ts",
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsx src/index.ts",
21
+ "test": "vitest run",
22
+ "test:unit": "vitest run --config vitest.config.unit.ts",
23
+ "test:unit:coverage": "vitest run --config vitest.config.unit.ts --coverage",
24
+ "test:e2e": "vitest run --config vitest.config.e2e.ts",
25
+ "test:watch": "vitest",
26
+ "test:coverage": "vitest run --coverage",
27
+ "lint": "eslint src/",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "keywords": [
31
+ "mcp",
32
+ "context",
33
+ "ai",
34
+ "coding-assistant",
35
+ "dependency-graph"
36
+ ],
37
+ "author": "Alper Hankendi",
38
+ "license": "MIT",
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.12.1",
41
+ "chokidar": "^5.0.0",
42
+ "simple-git": "^3.27.0",
43
+ "sql.js": "^1.14.1",
44
+ "ts-morph": "^27.0.2",
45
+ "zod": "^4.3.6"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.15.3",
49
+ "@vitest/coverage-v8": "^3.1.3",
50
+ "eslint": "^9.26.0",
51
+ "tsup": "^8.4.0",
52
+ "tsx": "^4.19.4",
53
+ "typescript": "^5.8.3",
54
+ "typescript-eslint": "^8.57.2",
55
+ "vitest": "^3.1.3"
56
+ }
57
+ }