@ctxo/cli 0.10.0 → 0.11.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,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ JsonIndexReader
4
+ } from "./chunk-CDGQGCVN.js";
5
+ import "./chunk-5DSKZJNN.js";
6
+ export {
7
+ JsonIndexReader
8
+ };
9
+ //# sourceMappingURL=json-index-reader-UYS74USB.js.map
package/llms-full.txt CHANGED
@@ -22,13 +22,14 @@ MCP client configuration:
22
22
 
23
23
  ## Monorepo & Plugin Architecture (v0.7+)
24
24
 
25
- Ctxo is a pnpm workspace monorepo with five packages:
25
+ Ctxo is a pnpm workspace monorepo with six packages:
26
26
 
27
27
  - `@ctxo/cli` (packages/cli) — CLI entry, composition root, MCP server, tool handlers
28
28
  - `@ctxo/plugin-api` (packages/plugin-api) — `CtxoLanguagePlugin` v1 protocol interface
29
29
  - `@ctxo/lang-typescript` — ts-morph (full tier) plugin for TS/JS
30
30
  - `@ctxo/lang-go` — tree-sitter / gopls plugin for Go
31
31
  - `@ctxo/lang-csharp` — Roslyn plugin for C#
32
+ - `@ctxo/lang-java` - tree-sitter Java plugin (syntax tier); full tier via `@ctxo/lang-java-analyzer` companion package (JRE 17+ required)
32
33
 
33
34
  Plugin protocol v1 (`CtxoLanguagePlugin`):
34
35
  - `apiVersion: 1`
package/llms.txt CHANGED
@@ -20,6 +20,7 @@ ctxo # Start MCP server (stdio)
20
20
  - `@ctxo/lang-typescript` — ts-morph full-tier plugin
21
21
  - `@ctxo/lang-go` — tree-sitter / gopls plugin
22
22
  - `@ctxo/lang-csharp` — Roslyn plugin
23
+ - `@ctxo/lang-java` - tree-sitter Java plugin (syntax tier built-in; full tier via `@ctxo/lang-java-analyzer` companion, requires JRE 17+)
23
24
 
24
25
  Language adapters ship as separate npm packages and are discovered by scanning the project `package.json` for `@ctxo/lang-*` or `ctxo-lang-*` dependencies.
25
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctxo/cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "MCP server delivering dependency-aware, history-enriched context for codebases",
5
5
  "type": "module",
6
6
  "engines": {
@@ -40,7 +40,7 @@
40
40
  "sql.js": "^1.14.1",
41
41
  "yaml": "^2.6.1",
42
42
  "zod": "^4.3.6",
43
- "@ctxo/plugin-api": "0.7.1"
43
+ "@ctxo/plugin-api": "0.7.2"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^22.15.3",
@@ -52,9 +52,9 @@
52
52
  "typescript": "^5.8.3",
53
53
  "typescript-eslint": "^8.57.2",
54
54
  "vitest": "^3.1.3",
55
- "@ctxo/lang-typescript": "0.7.1",
55
+ "@ctxo/lang-csharp": "0.7.3",
56
56
  "@ctxo/lang-go": "0.8.0",
57
- "@ctxo/lang-csharp": "0.7.2"
57
+ "@ctxo/lang-typescript": "0.7.1"
58
58
  },
59
59
  "repository": {
60
60
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/storage/json-index-reader.ts","../src/core/types.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync, realpathSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport { FileIndexSchema, type FileIndex } from '../../core/types.js';\n\nexport class JsonIndexReader {\n private readonly indexDir: string;\n\n constructor(ctxoRoot: string) {\n this.indexDir = join(ctxoRoot, 'index');\n }\n\n readAll(): FileIndex[] {\n if (!existsSync(this.indexDir)) {\n return [];\n }\n\n const jsonFiles = this.collectJsonFiles(this.indexDir);\n const results: FileIndex[] = [];\n\n for (const filePath of jsonFiles) {\n const parsed = this.readSingle(filePath);\n if (parsed) {\n results.push(parsed);\n }\n }\n\n return results;\n }\n\n readSingle(absolutePath: string): FileIndex | undefined {\n try {\n const raw = readFileSync(absolutePath, 'utf-8');\n const data: unknown = JSON.parse(raw);\n const result = FileIndexSchema.safeParse(data);\n\n if (!result.success) {\n const rel = relative(this.indexDir, absolutePath);\n console.error(\n `[ctxo:json-reader] Invalid schema in ${rel}: ${result.error.message}`,\n );\n return undefined;\n }\n\n return result.data;\n } catch (err) {\n const rel = relative(this.indexDir, absolutePath);\n console.error(\n `[ctxo:json-reader] Failed to read ${rel}: ${(err as Error).message}`,\n );\n return undefined;\n }\n }\n\n private collectJsonFiles(dir: string, visited: Set<string> = new Set()): string[] {\n const realDir = realpathSync(dir);\n if (visited.has(realDir)) return []; // Guard against symlink loops\n visited.add(realDir);\n\n const files: string[] = [];\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isSymbolicLink()) continue; // Skip symlinks entirely\n\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n // Skip non-FileIndex directories under .ctxo/index/\n if (entry.name === 'communities.history') continue;\n files.push(...this.collectJsonFiles(fullPath, visited));\n } else if (\n entry.name.endsWith('.json') &&\n entry.name !== 'co-changes.json' &&\n entry.name !== 'communities.json' &&\n entry.name !== 'drift-events.json' &&\n entry.name !== 'boundary-violations.json'\n ) {\n files.push(fullPath);\n }\n }\n\n return files;\n }\n}\n","import { z } from 'zod';\n\n// ── Symbol Kinds ────────────────────────────────────────────────\n\nexport const SYMBOL_KINDS = [\n 'function',\n 'class',\n 'interface',\n 'method',\n 'variable',\n 'type',\n] as const;\n\nexport const SymbolKindSchema = z.enum(SYMBOL_KINDS);\nexport type SymbolKind = z.infer<typeof SymbolKindSchema>;\n\n// ── Edge Kinds ──────────────────────────────────────────────────\n\nexport const EDGE_KINDS = [\n 'imports',\n 'calls',\n 'extends',\n 'implements',\n 'uses',\n] as const;\n\nexport const EdgeKindSchema = z.enum(EDGE_KINDS);\nexport type EdgeKind = z.infer<typeof EdgeKindSchema>;\n\n// ── Detail Levels ───────────────────────────────────────────────\n\nexport const DETAIL_LEVELS = [1, 2, 3, 4] as const;\n\nexport const DetailLevelSchema = z.union([\n z.literal(1),\n z.literal(2),\n z.literal(3),\n z.literal(4),\n]);\nexport type DetailLevel = z.infer<typeof DetailLevelSchema>;\n\n// ── Symbol ID ───────────────────────────────────────────────────\n\n/**\n * Format: \"<relativeFile>::<name>::<kind>\"\n * Example: \"src/foo.ts::myFn::function\"\n */\nexport const SymbolIdSchema = z\n .string()\n .min(1)\n .refine(\n (value) => {\n const parts = value.split('::');\n if (parts.length !== 3) return false;\n const [file, name, kind] = parts;\n if (!file || !name || !kind) return false;\n return SymbolKindSchema.safeParse(kind).success;\n },\n { message: 'Symbol ID must match format \"<file>::<name>::<kind>\"' },\n );\nexport type SymbolId = z.infer<typeof SymbolIdSchema>;\n\n// ── Symbol Node ─────────────────────────────────────────────────\n\nexport const SymbolNodeSchema = z\n .object({\n symbolId: SymbolIdSchema,\n name: z.string().min(1),\n kind: SymbolKindSchema,\n startLine: z.number().int().nonnegative(),\n endLine: z.number().int().nonnegative(),\n startOffset: z.number().int().nonnegative().optional(),\n endOffset: z.number().int().nonnegative().optional(),\n })\n .refine((node) => node.endLine >= node.startLine, {\n message: 'endLine must be >= startLine',\n });\nexport type SymbolNode = z.infer<typeof SymbolNodeSchema>;\n\n// ── Graph Edge ──────────────────────────────────────────────────\n\nexport const GraphEdgeSchema = z.object({\n from: SymbolIdSchema,\n to: SymbolIdSchema,\n kind: EdgeKindSchema,\n typeOnly: z.boolean().optional(),\n});\nexport type GraphEdge = z.infer<typeof GraphEdgeSchema>;\n\n// ── Commit Intent ───────────────────────────────────────────────\n\nexport const CommitIntentSchema = z.object({\n hash: z.string().min(1),\n message: z.string(),\n date: z.string().min(1),\n kind: z.literal('commit'),\n});\nexport type CommitIntent = z.infer<typeof CommitIntentSchema>;\n\n// ── Anti-Pattern ────────────────────────────────────────────────\n\nexport const AntiPatternSchema = z.object({\n hash: z.string().min(1),\n message: z.string(),\n date: z.string().min(1),\n});\nexport type AntiPattern = z.infer<typeof AntiPatternSchema>;\n\n// ── File Index ──────────────────────────────────────────────────\n\nexport const ComplexityMetricsSchema = z.object({\n symbolId: z.string().min(1),\n cyclomatic: z.number().nonnegative(),\n});\n\nexport const FileIndexSchema = z.object({\n file: z.string().min(1),\n lastModified: z.number().nonnegative(),\n contentHash: z.string().optional(),\n symbols: z.array(SymbolNodeSchema),\n edges: z.array(GraphEdgeSchema),\n complexity: z.array(ComplexityMetricsSchema).optional(),\n intent: z.array(CommitIntentSchema),\n antiPatterns: z.array(AntiPatternSchema),\n});\nexport type FileIndex = z.infer<typeof FileIndexSchema>;\n\n// ── Logic-Slice Result ──────────────────────────────────────────\n\nexport interface LogicSliceResult {\n readonly root: SymbolNode;\n readonly dependencies: readonly SymbolNode[];\n readonly edges: readonly GraphEdge[];\n}\n\n// ── Formatted Slice ─────────────────────────────────────────────\n\nexport interface TruncationInfo {\n readonly truncated: true;\n readonly reason: 'token_budget_exceeded';\n}\n\nexport interface FormattedSlice {\n readonly root: SymbolNode;\n readonly dependencies: readonly SymbolNode[];\n readonly edges: readonly GraphEdge[];\n readonly level: DetailLevel;\n readonly levelDescription?: string;\n readonly truncation?: TruncationInfo;\n}\n\n// ── Complexity & Churn ──────────────────────────────────────────\n\nexport interface ComplexityMetrics {\n readonly symbolId: string;\n readonly cyclomatic: number;\n}\n\nexport interface ChurnData {\n readonly filePath: string;\n readonly commitCount: number;\n}\n\nexport const SCORE_BANDS = ['low', 'medium', 'high'] as const;\nexport type ScoreBand = (typeof SCORE_BANDS)[number];\n\nexport interface ChangeIntelligenceScore {\n readonly symbolId: string;\n readonly complexity: number;\n readonly churn: number;\n readonly composite: number;\n readonly band: ScoreBand;\n}\n\n// ── Why-Context Result ──────────────────────────────────────────\n\nexport interface WhyContextResult {\n readonly commitHistory: readonly CommitIntent[];\n readonly antiPatternWarnings: readonly AntiPattern[];\n readonly changeIntelligence?: ChangeIntelligenceScore;\n}\n\n// ── Co-Change Analysis ─────────────────────────────────────────\n\nexport interface CoChangeEntry {\n readonly file1: string;\n readonly file2: string;\n readonly sharedCommits: number;\n readonly frequency: number;\n}\n\nexport interface CoChangeMatrix {\n readonly version: 1;\n readonly timestamp: number;\n readonly entries: readonly CoChangeEntry[];\n}\n\n// ── Commit Record (from git adapter) ────────────────────────────\n\nexport interface CommitRecord {\n readonly hash: string;\n readonly message: string;\n readonly date: string;\n readonly author: string;\n}\n\n// ── Blame Line (from git adapter) ───────────────────────────────\n\nexport interface BlameLine {\n readonly hash: string;\n readonly lineNumber: number;\n readonly author: string;\n readonly date: string;\n}\n\n// ── Search Types ───────────────────────────────────────────────\n\nexport interface SearchResult {\n symbolId: string;\n name: string;\n kind: string;\n filePath: string;\n /** BM25 relevance score (higher = more relevant) */\n relevanceScore: number;\n /** PageRank importance score (0-1) */\n importanceScore: number;\n /** Final combined score after all boosts */\n combinedScore: number;\n}\n\nexport interface SearchMetrics {\n porterHits: number;\n trigramHits: number;\n phase2Activated: boolean;\n fuzzyApplied: boolean;\n latencyMs: number;\n}\n\nexport interface FuzzyCorrection {\n originalQuery: string;\n correctedQuery: string;\n corrections: Array<{\n original: string;\n corrected: string;\n distance: number;\n }>;\n}\n\nexport interface SearchResponse {\n query: string;\n results: SearchResult[];\n metrics: SearchMetrics;\n fuzzyCorrection?: FuzzyCorrection;\n}\n\n// ── Community Detection ────────────────────────────────────────\n\nexport interface CommunityEntry {\n readonly symbolId: string;\n readonly communityId: number;\n readonly communityLabel: string;\n}\n\nexport interface GodNode {\n readonly symbolId: string;\n readonly bridgedCommunities: readonly number[];\n readonly centralityScore: number;\n}\n\nexport const EDGE_QUALITY = ['full', 'syntax-only', 'mixed'] as const;\nexport type EdgeQuality = (typeof EDGE_QUALITY)[number];\n\nexport interface CommunitySnapshot {\n readonly version: 1;\n readonly computedAt: string;\n readonly commitSha: string;\n readonly modularity: number;\n readonly communities: readonly CommunityEntry[];\n readonly godNodes: readonly GodNode[];\n readonly edgeQuality: EdgeQuality;\n readonly crossClusterEdges: number;\n}\n\nexport interface DriftEvent {\n readonly symbolId: string;\n readonly movedFrom: { readonly id: number; readonly label: string };\n readonly movedTo: { readonly id: number; readonly label: string };\n readonly firstSeenInNewCluster: string;\n}\n\nexport interface BoundaryViolation {\n readonly from: {\n readonly symbolId: string;\n readonly communityId: number;\n readonly label: string;\n };\n readonly to: {\n readonly symbolId: string;\n readonly communityId: number;\n readonly label: string;\n };\n readonly edgeKind: EdgeKind;\n readonly historicalEdgesBetweenClusters: number;\n readonly severity: 'high' | 'medium';\n}\n\n// ── Persisted architectural artifacts ──────────────────────────\n// Written by `ctxo index` next to communities.json so the static\n// HTML visualizer can render Architecture views without re-running\n// detectors in the browser.\n\nexport type ConfidenceLevel = 'high' | 'medium' | 'low';\n\nexport interface DriftEventsArtifact {\n readonly version: 1;\n readonly computedAt: string;\n readonly commitSha: string;\n readonly confidence: ConfidenceLevel;\n readonly snapshotsAvailable: number;\n readonly hint?: string;\n readonly events: readonly DriftEvent[];\n}\n\nexport interface BoundaryViolationsArtifact {\n readonly version: 1;\n readonly computedAt: string;\n readonly commitSha: string;\n readonly confidence: ConfidenceLevel;\n readonly snapshotsAvailable: number;\n readonly hint?: string;\n readonly violations: readonly BoundaryViolation[];\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/storage/atomic-write.ts","../src/core/logger.ts"],"sourcesContent":["import { readdirSync, renameSync, statSync, unlinkSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:storage');\n\nconst TMP_SUFFIX_RE = /\\.\\d+\\.tmp$/;\nconst TMP_STALE_THRESHOLD_MS = 60_000; // anything older than 60 s is from a dead process\n\nexport function atomicWrite(targetPath: string, content: string): void {\n const tmpPath = `${targetPath}.${process.pid}.tmp`;\n writeFileSync(tmpPath, content, 'utf-8');\n renameSync(tmpPath, targetPath);\n}\n\n/**\n * Remove orphaned .<pid>.tmp files left behind when a previous write crashed\n * (SIGKILL / power loss / ENOSPC). Only files older than a threshold are\n * removed so we never race a concurrent writer.\n *\n * Safe to call on every startup — walks the supplied directory non-recursively.\n */\nexport function sweepStaleTmpFiles(dir: string, now: number = Date.now()): number {\n if (!existsSync(dir)) return 0;\n let cleaned = 0;\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isSymbolicLink()) continue;\n if (entry.isDirectory()) {\n cleaned += sweepStaleTmpFiles(join(dir, entry.name), now);\n continue;\n }\n if (!TMP_SUFFIX_RE.test(entry.name)) continue;\n const full = join(dir, entry.name);\n try {\n const age = now - statSync(full).mtimeMs;\n if (age < TMP_STALE_THRESHOLD_MS) continue;\n unlinkSync(full);\n cleaned++;\n } catch (err) {\n log.error(`failed to sweep ${entry.name}: ${(err as Error).message}`);\n }\n }\n } catch (err) {\n log.error(`failed to scan ${dir}: ${(err as Error).message}`);\n }\n return cleaned;\n}\n","/**\n * Structured logger for Ctxo — all output goes to stderr (stdout reserved for MCP JSON-RPC).\n *\n * Supports debug mode via DEBUG environment variable:\n * DEBUG=ctxo:* — all debug output\n * DEBUG=ctxo:git — only git adapter debug\n * DEBUG=ctxo:index — only indexing debug\n * DEBUG=ctxo:mcp — only MCP handler debug\n * DEBUG=ctxo:storage — only storage adapter debug\n * DEBUG=ctxo:masking — only masking pipeline debug\n *\n * Always outputs: info, warn, error (regardless of DEBUG).\n * Only outputs debug when matching namespace is enabled.\n */\n\nfunction isDebugEnabled(namespace: string): boolean {\n const debugEnv = process.env['DEBUG'] ?? '';\n if (!debugEnv) return false;\n const patterns = debugEnv.split(',').map(p => p.trim());\n return patterns.some(p => {\n if (p === '*' || p === 'ctxo:*') return true;\n if (p === namespace) return true;\n if (p.endsWith(':*') && namespace.startsWith(p.slice(0, -1))) return true;\n return false;\n });\n}\n\nexport interface Logger {\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nexport function createLogger(namespace: string): Logger {\n const prefix = `[${namespace}]`;\n\n return {\n debug(message: string, ...args: unknown[]) {\n if (isDebugEnabled(namespace)) {\n console.error(`${prefix} DEBUG ${message}`, ...args);\n }\n },\n info(message: string, ...args: unknown[]) {\n console.error(`${prefix} ${message}`, ...args);\n },\n warn(message: string, ...args: unknown[]) {\n console.error(`${prefix} WARN ${message}`, ...args);\n },\n error(message: string, ...args: unknown[]) {\n console.error(`${prefix} ERROR ${message}`, ...args);\n },\n };\n}\n"],"mappings":";;;AAAA,SAAS,aAAa,YAAY,UAAU,YAAY,eAAe,kBAAkB;AACzF,SAAS,YAAY;;;ACcrB,SAAS,eAAe,WAA4B;AAClD,QAAM,WAAW,QAAQ,IAAI,OAAO,KAAK;AACzC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AACtD,SAAO,SAAS,KAAK,OAAK;AACxB,QAAI,MAAM,OAAO,MAAM,SAAU,QAAO;AACxC,QAAI,MAAM,UAAW,QAAO;AAC5B,QAAI,EAAE,SAAS,IAAI,KAAK,UAAU,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC,EAAG,QAAO;AACrE,WAAO;AAAA,EACT,CAAC;AACH;AASO,SAAS,aAAa,WAA2B;AACtD,QAAM,SAAS,IAAI,SAAS;AAE5B,SAAO;AAAA,IACL,MAAM,YAAoB,MAAiB;AACzC,UAAI,eAAe,SAAS,GAAG;AAC7B,gBAAQ,MAAM,GAAG,MAAM,UAAU,OAAO,IAAI,GAAG,IAAI;AAAA,MACrD;AAAA,IACF;AAAA,IACA,KAAK,YAAoB,MAAiB;AACxC,cAAQ,MAAM,GAAG,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI;AAAA,IAC/C;AAAA,IACA,KAAK,YAAoB,MAAiB;AACxC,cAAQ,MAAM,GAAG,MAAM,SAAS,OAAO,IAAI,GAAG,IAAI;AAAA,IACpD;AAAA,IACA,MAAM,YAAoB,MAAiB;AACzC,cAAQ,MAAM,GAAG,MAAM,UAAU,OAAO,IAAI,GAAG,IAAI;AAAA,IACrD;AAAA,EACF;AACF;;;ADhDA,IAAM,MAAM,aAAa,cAAc;AAEvC,IAAM,gBAAgB;AACtB,IAAM,yBAAyB;AAExB,SAAS,YAAY,YAAoB,SAAuB;AACrE,QAAM,UAAU,GAAG,UAAU,IAAI,QAAQ,GAAG;AAC5C,gBAAc,SAAS,SAAS,OAAO;AACvC,aAAW,SAAS,UAAU;AAChC;AASO,SAAS,mBAAmB,KAAa,MAAc,KAAK,IAAI,GAAW;AAChF,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO;AAC7B,MAAI,UAAU;AACd,MAAI;AACF,eAAW,SAAS,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAC7D,UAAI,MAAM,eAAe,EAAG;AAC5B,UAAI,MAAM,YAAY,GAAG;AACvB,mBAAW,mBAAmB,KAAK,KAAK,MAAM,IAAI,GAAG,GAAG;AACxD;AAAA,MACF;AACA,UAAI,CAAC,cAAc,KAAK,MAAM,IAAI,EAAG;AACrC,YAAM,OAAO,KAAK,KAAK,MAAM,IAAI;AACjC,UAAI;AACF,cAAM,MAAM,MAAM,SAAS,IAAI,EAAE;AACjC,YAAI,MAAM,uBAAwB;AAClC,mBAAW,IAAI;AACf;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,MAAM,mBAAmB,MAAM,IAAI,KAAM,IAAc,OAAO,EAAE;AAAA,MACtE;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,MAAM,kBAAkB,GAAG,KAAM,IAAc,OAAO,EAAE;AAAA,EAC9D;AACA,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/storage/sqlite-storage-adapter.ts","../src/adapters/storage/community-snapshot-writer.ts","../src/adapters/git/simple-git-adapter.ts","../src/adapters/stats/session-recorder-adapter.ts","../src/adapters/workspace/single-package-workspace.ts","../src/core/config/load-config.ts","../src/core/config/config-schema.ts","../src/core/graph/symbol-graph.ts","../src/core/why-context/revert-detector.ts","../src/core/overlay/drift-detector.ts","../src/core/blast-radius/blast-radius-calculator.ts","../src/core/overlay/architectural-overlay.ts","../src/core/dead-code/dead-code-detector.ts","../src/core/importance/pagerank-calculator.ts","../src/core/overlay/boundary-violation-detector.ts","../src/core/co-change/co-change-analyzer.ts"],"sourcesContent":["import initSqlJs, { type Database } from 'sql.js';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type {\n CommunitySnapshot,\n FileIndex,\n GraphEdge,\n SymbolNode,\n} from '../../core/types.js';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport { CommunitySnapshotWriter } from './community-snapshot-writer.js';\nimport { JsonIndexReader } from './json-index-reader.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:storage');\n\nexport interface SqliteStorageAdapterOptions {\n /**\n * Acknowledge writes to a production (non-tmpdir) `.ctxo/` root. Required\n * when the CLI talks to the user's real project. Tests using mkdtempSync\n * paths can omit this; they are auto-recognised as safe.\n */\n readonly allowProductionPath?: boolean;\n}\n\nexport class SqliteStorageAdapter implements IStoragePort {\n private db: Database | undefined;\n private readonly dbPath: string;\n private readonly ctxoRoot: string;\n private readonly communityWriter: CommunitySnapshotWriter;\n\n constructor(ctxoRoot: string, options: SqliteStorageAdapterOptions = {}) {\n this.ctxoRoot = ctxoRoot;\n this.dbPath = join(ctxoRoot, '.cache', 'symbols.db');\n this.communityWriter = new CommunitySnapshotWriter(ctxoRoot, {\n allowProductionPath: options.allowProductionPath,\n });\n }\n\n async init(): Promise<void> {\n const SQL = await initSqlJs();\n\n if (existsSync(this.dbPath)) {\n try {\n const buffer = readFileSync(this.dbPath);\n this.db = new SQL.Database(buffer);\n this.verifyIntegrity();\n } catch {\n log.warn('Corrupt DB detected, rebuilding from JSON index');\n this.db = new SQL.Database();\n this.createTables();\n }\n } else {\n this.db = new SQL.Database();\n this.createTables();\n }\n\n // Always rebuild from JSON — it is the source of truth\n this.rebuildFromJson();\n }\n\n async initEmpty(): Promise<void> {\n const SQL = await initSqlJs();\n if (existsSync(this.dbPath)) {\n try {\n const buffer = readFileSync(this.dbPath);\n this.db = new SQL.Database(buffer);\n } catch {\n this.db = new SQL.Database();\n }\n } else {\n this.db = new SQL.Database();\n }\n this.createTables();\n this.clearIndexTables();\n }\n\n /** Returns the underlying sql.js Database instance. Throws if not initialized. */\n getDb(): Database {\n if (!this.db) {\n throw new Error('SqliteStorageAdapter not initialized. Call init() first.');\n }\n return this.db;\n }\n\n private database(): Database {\n return this.getDb();\n }\n\n private verifyIntegrity(): void {\n const db = this.database();\n const result = db.exec('PRAGMA integrity_check');\n const firstRow = result[0]?.values[0];\n if (!firstRow || firstRow[0] !== 'ok') {\n throw new Error('SQLite integrity check failed');\n }\n\n const tables = db.exec(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name IN ('symbols', 'edges', 'files')\",\n );\n if (!tables[0] || tables[0].values.length < 3) {\n throw new Error('Missing required tables');\n }\n }\n\n private createTables(): void {\n const db = this.database();\n db.run(`\n CREATE TABLE IF NOT EXISTS files (\n file_path TEXT PRIMARY KEY,\n last_modified INTEGER NOT NULL\n )\n `);\n db.run(`\n CREATE TABLE IF NOT EXISTS symbols (\n symbol_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n kind TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL\n )\n `);\n db.run(`\n CREATE TABLE IF NOT EXISTS edges (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_symbol TEXT NOT NULL,\n to_symbol TEXT NOT NULL,\n kind TEXT NOT NULL\n )\n `);\n db.run('CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_path)');\n db.run('CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(from_symbol)');\n db.run('CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(to_symbol)');\n }\n\n private clearIndexTables(): void {\n const db = this.database();\n db.run('DELETE FROM edges');\n db.run('DELETE FROM symbols');\n db.run('DELETE FROM files');\n }\n\n private rebuildFromJson(): void {\n const reader = new JsonIndexReader(this.ctxoRoot);\n const indices = reader.readAll();\n\n if (indices.length > 0) {\n this.bulkWrite(indices);\n }\n }\n\n writeSymbolFile(fileIndex: FileIndex): void {\n const db = this.database();\n\n db.run('BEGIN TRANSACTION');\n try {\n this.deleteFileData(db, fileIndex.file);\n\n db.run(\n 'INSERT INTO files (file_path, last_modified) VALUES (?, ?)',\n [fileIndex.file, fileIndex.lastModified],\n );\n\n for (const sym of fileIndex.symbols) {\n db.run(\n 'INSERT OR REPLACE INTO symbols (symbol_id, name, kind, file_path, start_line, end_line) VALUES (?, ?, ?, ?, ?, ?)',\n [sym.symbolId, sym.name, sym.kind, fileIndex.file, sym.startLine, sym.endLine],\n );\n }\n\n for (const edge of fileIndex.edges) {\n db.run(\n 'INSERT INTO edges (from_symbol, to_symbol, kind) VALUES (?, ?, ?)',\n [edge.from, edge.to, edge.kind],\n );\n }\n\n db.run('COMMIT');\n this.persistIfNeeded();\n } catch (err) {\n db.run('ROLLBACK');\n throw err;\n }\n }\n\n /**\n * Read symbol file from SQLite cache.\n * NOTE: SQLite only caches symbols + edges (graph topology).\n * intent, antiPatterns, and complexity live in committed JSON index only.\n * Use JsonIndexReader for full FileIndex data.\n */\n readSymbolFile(relativePath: string): FileIndex | undefined {\n const db = this.database();\n\n const fileResult = db.exec(\n 'SELECT file_path, last_modified FROM files WHERE file_path = ?',\n [relativePath],\n );\n if (!fileResult[0] || fileResult[0].values.length === 0) {\n return undefined;\n }\n\n const [filePath, lastModified] = fileResult[0].values[0] as [string, number];\n\n const symbols = this.getSymbolsForFile(db, filePath);\n const edges = this.getEdgesForFile(db, filePath);\n\n return {\n file: filePath,\n lastModified,\n symbols,\n edges,\n // Not stored in SQLite — use JsonIndexReader for these fields\n intent: [],\n antiPatterns: [],\n };\n }\n\n listIndexedFiles(): string[] {\n const db = this.database();\n const result = db.exec('SELECT file_path FROM files ORDER BY file_path');\n if (!result[0]) return [];\n return result[0].values.map((row) => row[0] as string);\n }\n\n deleteSymbolFile(relativePath: string): void {\n const db = this.database();\n this.deleteFileData(db, relativePath);\n }\n\n getSymbolById(symbolId: string): SymbolNode | undefined {\n const db = this.database();\n const result = db.exec(\n 'SELECT symbol_id, name, kind, start_line, end_line FROM symbols WHERE symbol_id = ?',\n [symbolId],\n );\n if (!result[0] || result[0].values.length === 0) {\n return undefined;\n }\n\n const [sid, name, kind, startLine, endLine] = result[0].values[0] as [string, string, string, number, number];\n return { symbolId: sid, name, kind: kind as SymbolNode['kind'], startLine, endLine };\n }\n\n getEdgesFrom(symbolId: string): GraphEdge[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT from_symbol, to_symbol, kind FROM edges WHERE from_symbol = ?',\n [symbolId],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n\n getEdgesTo(symbolId: string): GraphEdge[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT from_symbol, to_symbol, kind FROM edges WHERE to_symbol = ?',\n [symbolId],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n\n getAllSymbols(): SymbolNode[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT symbol_id, name, kind, start_line, end_line FROM symbols',\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n symbolId: row[0] as string,\n name: row[1] as string,\n kind: row[2] as SymbolNode['kind'],\n startLine: row[3] as number,\n endLine: row[4] as number,\n }));\n }\n\n getAllEdges(): GraphEdge[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT from_symbol, to_symbol, kind FROM edges',\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n\n bulkWrite(indices: FileIndex[]): void {\n const db = this.database();\n\n db.run('BEGIN TRANSACTION');\n try {\n for (const fileIndex of indices) {\n this.deleteFileData(db, fileIndex.file);\n\n db.run(\n 'INSERT INTO files (file_path, last_modified) VALUES (?, ?)',\n [fileIndex.file, fileIndex.lastModified],\n );\n\n for (const sym of fileIndex.symbols) {\n db.run(\n 'INSERT OR REPLACE INTO symbols (symbol_id, name, kind, file_path, start_line, end_line) VALUES (?, ?, ?, ?, ?, ?)',\n [sym.symbolId, sym.name, sym.kind, fileIndex.file, sym.startLine, sym.endLine],\n );\n }\n\n for (const edge of fileIndex.edges) {\n db.run(\n 'INSERT INTO edges (from_symbol, to_symbol, kind) VALUES (?, ?, ?)',\n [edge.from, edge.to, edge.kind],\n );\n }\n }\n\n db.run('COMMIT');\n this.persistIfNeeded();\n } catch (err) {\n db.run('ROLLBACK');\n throw err;\n }\n }\n\n persist(): void {\n const db = this.database();\n const data = db.export();\n const buffer = Buffer.from(data);\n mkdirSync(dirname(this.dbPath), { recursive: true });\n writeFileSync(this.dbPath, buffer);\n }\n\n readCommunities(): CommunitySnapshot | undefined {\n return this.communityWriter.readCurrent();\n }\n\n writeCommunities(snapshot: CommunitySnapshot): void {\n this.communityWriter.writeSnapshot(snapshot);\n }\n\n listCommunityHistory(limit?: number): CommunitySnapshot[] {\n return this.communityWriter.listHistory(limit);\n }\n\n close(): void {\n if (this.db) {\n this.persist();\n this.db.close();\n this.db = undefined;\n }\n }\n\n private persistIfNeeded(): void {\n try {\n this.persist();\n } catch (err) {\n log.error(`Failed to persist DB: ${(err as Error).message}`);\n }\n }\n\n private deleteFileData(db: Database, filePath: string): void {\n // Delete edges originating from this file's symbols\n const symResult = db.exec(\n 'SELECT symbol_id FROM symbols WHERE file_path = ?',\n [filePath],\n );\n if (symResult[0]) {\n for (const row of symResult[0].values) {\n db.run('DELETE FROM edges WHERE from_symbol = ?', [row[0]]);\n }\n }\n db.run('DELETE FROM symbols WHERE file_path = ?', [filePath]);\n db.run('DELETE FROM files WHERE file_path = ?', [filePath]);\n }\n\n private getSymbolsForFile(db: Database, filePath: string): SymbolNode[] {\n const result = db.exec(\n 'SELECT symbol_id, name, kind, start_line, end_line FROM symbols WHERE file_path = ? ORDER BY start_line',\n [filePath],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n symbolId: row[0] as string,\n name: row[1] as string,\n kind: row[2] as SymbolNode['kind'],\n startLine: row[3] as number,\n endLine: row[4] as number,\n }));\n }\n\n private getEdgesForFile(db: Database, filePath: string): GraphEdge[] {\n const result = db.exec(\n `SELECT e.from_symbol, e.to_symbol, e.kind FROM edges e\n INNER JOIN symbols s ON e.from_symbol = s.symbol_id\n WHERE s.file_path = ?`,\n [filePath],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n unlinkSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, join, resolve } from 'node:path';\n\nimport { createLogger } from '../../core/logger.js';\nimport type { CommunitySnapshot } from '../../core/types.js';\nimport { atomicWrite } from './atomic-write.js';\n\nconst log = createLogger('ctxo:community-snapshot');\n\nconst DEFAULT_HISTORY_LIMIT = 10;\nconst HISTORY_DIR_NAME = 'communities.history';\nconst CURRENT_FILE_NAME = 'communities.json';\nconst NOCOMMIT_SHA = 'nocommit';\n\nexport interface CommunitySnapshotWriterOptions {\n readonly historyLimit?: number;\n /**\n * Acknowledge that this writer targets a production `.ctxo/` root (user's\n * real project). Required when the path is outside the system tmpdir; this\n * prevents test runs from accidentally writing a tiny/invalid snapshot into\n * the developer's real project via an unisolated fixture.\n */\n readonly allowProductionPath?: boolean;\n}\n\nexport class CommunitySnapshotWriter {\n private readonly indexDir: string;\n private readonly historyDir: string;\n private readonly historyLimit: number;\n\n constructor(\n ctxoRoot: string,\n historyLimitOrOptions: number | CommunitySnapshotWriterOptions = DEFAULT_HISTORY_LIMIT,\n ) {\n const options: CommunitySnapshotWriterOptions =\n typeof historyLimitOrOptions === 'number'\n ? { historyLimit: historyLimitOrOptions }\n : historyLimitOrOptions;\n\n this.indexDir = join(ctxoRoot, 'index');\n this.historyDir = join(this.indexDir, HISTORY_DIR_NAME);\n this.historyLimit = options.historyLimit ?? DEFAULT_HISTORY_LIMIT;\n\n assertSafeWritePath(ctxoRoot, options.allowProductionPath ?? false);\n }\n\n writeSnapshot(snapshot: CommunitySnapshot): void {\n mkdirSync(this.indexDir, { recursive: true });\n mkdirSync(this.historyDir, { recursive: true });\n\n // Archive the previous current snapshot (if any) into history BEFORE\n // overwriting. This keeps history as \"snapshots older than current\" —\n // drift and boundary-violation detectors compare current vs history[0]\n // and must not see current in both positions.\n //\n // Skip archival for snapshots whose commitSha is 'nocommit' (git\n // unavailable at write time). They have no stable anchor for drift\n // comparison and would pollute history with indistinguishable entries.\n const currentPath = join(this.indexDir, CURRENT_FILE_NAME);\n if (existsSync(currentPath)) {\n try {\n const existingRaw = readFileSync(currentPath, 'utf-8');\n const existing = JSON.parse(existingRaw) as CommunitySnapshot;\n if (existing.commitSha && existing.commitSha !== NOCOMMIT_SHA) {\n const historyFile = this.historyFilename(existing);\n this.atomicWrite(join(this.historyDir, historyFile), existingRaw);\n }\n } catch (err) {\n log.error(`failed to archive existing snapshot: ${(err as Error).message}`);\n }\n }\n\n const json = JSON.stringify(snapshot, null, 2);\n this.atomicWrite(currentPath, json);\n\n this.rotateHistory();\n }\n\n readCurrent(): CommunitySnapshot | undefined {\n const path = join(this.indexDir, CURRENT_FILE_NAME);\n if (!existsSync(path)) return undefined;\n return this.safeParse(path);\n }\n\n listHistory(limit?: number): CommunitySnapshot[] {\n if (!existsSync(this.historyDir)) return [];\n const entries = readdirSync(this.historyDir)\n .filter((name) => name.endsWith('.json'))\n .sort()\n .reverse();\n const snapshots: CommunitySnapshot[] = [];\n for (const name of entries) {\n const snap = this.safeParse(join(this.historyDir, name));\n if (!snap) continue;\n // Defense in depth: filter any legacy `nocommit` entries that slipped\n // into history before the writer skip-archival guard landed.\n if (!snap.commitSha || snap.commitSha === NOCOMMIT_SHA) continue;\n snapshots.push(snap);\n if (typeof limit === 'number' && snapshots.length >= limit) break;\n }\n return snapshots;\n }\n\n private atomicWrite(targetPath: string, content: string): void {\n atomicWrite(targetPath, content);\n }\n\n private historyFilename(snapshot: CommunitySnapshot): string {\n const ts = snapshot.computedAt.replace(/[:.]/g, '-');\n const sha = snapshot.commitSha || 'nocommit';\n const base = join(this.historyDir, `${ts}-${sha}`);\n // Collision guard: if the same-timestamp file already exists (rare but\n // possible with sub-millisecond writes), append a monotonic suffix.\n let candidate = `${base}.json`;\n let n = 1;\n while (existsSync(candidate)) {\n candidate = `${base}.${n++}.json`;\n if (n > 1_000) break; // safety\n }\n return candidate.slice(this.historyDir.length + 1);\n }\n\n private rotateHistory(): void {\n if (!existsSync(this.historyDir)) return;\n const entries = readdirSync(this.historyDir)\n .filter((name) => name.endsWith('.json'))\n .sort();\n const excess = entries.length - this.historyLimit;\n if (excess <= 0) return;\n for (let i = 0; i < excess; i++) {\n const target = join(this.historyDir, entries[i]!);\n try {\n unlinkSync(target);\n } catch (err) {\n log.error(`failed to rotate ${basename(target)}: ${(err as Error).message}`);\n }\n }\n }\n\n private safeParse(path: string): CommunitySnapshot | undefined {\n try {\n const raw = readFileSync(path, 'utf-8');\n return JSON.parse(raw) as CommunitySnapshot;\n } catch (err) {\n log.error(`failed to parse ${basename(path)}: ${(err as Error).message}`);\n return undefined;\n }\n }\n}\n\n/**\n * Production `.ctxo/` roots must be opted into explicitly. This catches\n * the rogue case where a test forgets to pass a tmpdir and would otherwise\n * clobber the developer's real snapshot (as happened during v0.8 runtime\n * analysis — a 39-node modularity-0 snapshot appeared in the repo root from\n * an unisolated fixture).\n */\nfunction assertSafeWritePath(ctxoRoot: string, allowProduction: boolean): void {\n const resolved = resolve(ctxoRoot);\n const tmpRoot = resolve(tmpdir());\n const inTmp = resolved.startsWith(tmpRoot);\n if (inTmp || allowProduction) return;\n throw new Error(\n `CommunitySnapshotWriter: refusing to write to non-tmp path ${resolved}. ` +\n `Pass { allowProductionPath: true } for production CLI writes, or target a tmpdir-based path in tests.`,\n );\n}\n","import { simpleGit, type SimpleGit } from 'simple-git';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { CommitRecord, ChurnData } from '../../core/types.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:git');\n\nexport class SimpleGitAdapter implements IGitPort {\n private readonly git: SimpleGit;\n\n constructor(projectRoot: string) {\n this.git = simpleGit(projectRoot);\n log.debug('Initialized with root: %s', projectRoot);\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n await this.git.version();\n return true;\n } catch {\n return false;\n }\n }\n\n async getCommitHistory(filePath: string, maxCount?: number): Promise<CommitRecord[]> {\n try {\n const log = await this.git.log({\n file: filePath,\n '--follow': null,\n ...(maxCount ? { maxCount } : {}),\n });\n\n return log.all.map((entry) => ({\n hash: entry.hash,\n message: entry.message,\n date: entry.date,\n author: entry.author_name,\n }));\n } catch (err) {\n log.error(` Failed to get history for ${filePath}: ${(err as Error).message}`);\n return [];\n }\n }\n\n async getBatchHistory(maxCount = 20): Promise<Map<string, CommitRecord[]>> {\n try {\n const log = await this.git.log({ maxCount: maxCount * 50, '--name-only': null });\n const result = new Map<string, CommitRecord[]>();\n\n for (const entry of log.all) {\n const record: CommitRecord = {\n hash: entry.hash,\n message: entry.message,\n date: entry.date,\n author: entry.author_name,\n };\n\n const diff = entry.diff;\n const files: string[] = diff?.files?.map((f: { file: string }) => f.file.replace(/\\\\/g, '/')) ?? [];\n\n for (const file of files) {\n let list = result.get(file);\n if (!list) { list = []; result.set(file, list); }\n if (list.length < maxCount) {\n list.push(record);\n }\n }\n }\n\n return result;\n } catch (err) {\n log.error(` Batch history failed: ${(err as Error).message}`);\n return new Map();\n }\n }\n\n async getChangedFiles(since: string): Promise<string[]> {\n try {\n const diff = await this.git.diffSummary([since]);\n return diff.files.map((f) => f.file.replace(/\\\\/g, '/'));\n } catch (err) {\n log.error(` Failed to get changed files since ${since}: ${(err as Error).message}`);\n return [];\n }\n }\n\n async getFileChurn(filePath: string): Promise<ChurnData> {\n try {\n const log = await this.git.log({ file: filePath, '--follow': null });\n\n return {\n filePath,\n commitCount: log.total,\n };\n } catch (err) {\n log.error(` Failed to get churn for ${filePath}: ${(err as Error).message}`);\n return { filePath, commitCount: 0 };\n }\n }\n\n async getHeadSha(): Promise<string | undefined> {\n try {\n const raw = await this.git.revparse(['--short', 'HEAD']);\n const sha = raw.trim();\n return sha.length > 0 ? sha : undefined;\n } catch (err) {\n log.error(`Failed to resolve HEAD: ${(err as Error).message}`);\n return undefined;\n }\n }\n\n async countCommitsBetween(base: string): Promise<number | undefined> {\n try {\n const raw = await this.git.raw(['rev-list', '--count', `${base}..HEAD`]);\n const n = parseInt(raw.trim(), 10);\n return Number.isFinite(n) && n >= 0 ? n : undefined;\n } catch (err) {\n log.error(`Failed to count commits since ${base}: ${(err as Error).message}`);\n return undefined;\n }\n }\n}\n","import type { Database } from 'sql.js';\nimport type {\n ISessionRecorderPort,\n SessionEvent,\n AggregatedStats,\n ToolStats,\n SymbolStats,\n DetailLevelStats,\n} from '../../ports/i-session-recorder-port.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:stats');\n\nexport class SessionRecorderAdapter implements ISessionRecorderPort {\n private readonly db: Database;\n private readonly onWrite: (() => void) | null;\n private tableCreated = false;\n\n constructor(db: Database, onWrite?: () => void) {\n this.db = db;\n this.onWrite = onWrite ?? null;\n }\n\n private ensureTable(): void {\n if (this.tableCreated) return;\n this.db.run(`\n CREATE TABLE IF NOT EXISTS session_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp TEXT DEFAULT (datetime('now')),\n tool TEXT NOT NULL,\n symbol_id TEXT,\n detail_level TEXT,\n response_tokens INTEGER,\n response_bytes INTEGER,\n latency_ms REAL,\n truncated BOOLEAN DEFAULT 0\n )\n `);\n this.db.run('CREATE INDEX IF NOT EXISTS idx_session_timestamp ON session_events(timestamp)');\n this.db.run('CREATE INDEX IF NOT EXISTS idx_session_tool ON session_events(tool)');\n this.tableCreated = true;\n }\n\n record(event: SessionEvent): void {\n try {\n this.ensureTable();\n this.db.run(\n `INSERT INTO session_events (tool, symbol_id, detail_level, response_tokens, response_bytes, latency_ms, truncated)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [\n event.tool,\n event.symbolId,\n event.detailLevel,\n event.responseTokens,\n event.responseBytes,\n event.latencyMs,\n event.truncated ? 1 : 0,\n ],\n );\n this.flushToDisk();\n } catch (err) {\n log.error(`Failed to record session event: ${(err as Error).message}`);\n }\n }\n\n queryStats(days?: number): AggregatedStats {\n this.ensureTable();\n\n const whereClause = days != null && days > 0\n ? `WHERE timestamp >= datetime('now', '-${Math.floor(days)} days')`\n : '';\n\n // Total calls and tokens\n const summaryResult = this.db.exec(\n `SELECT COUNT(*) as total_calls, COALESCE(SUM(response_tokens), 0) as total_tokens\n FROM session_events ${whereClause}`,\n );\n const summaryRow = summaryResult[0]?.values[0];\n const totalCalls = Number(summaryRow?.[0] ?? 0);\n const totalTokensServed = Number(summaryRow?.[1] ?? 0);\n\n // Top tools\n const toolsResult = this.db.exec(\n `SELECT tool, COUNT(*) as calls, CAST(AVG(response_tokens) AS INTEGER) as avg_tokens\n FROM session_events ${whereClause}\n GROUP BY tool ORDER BY calls DESC LIMIT 5`,\n );\n const topTools: ToolStats[] = (toolsResult[0]?.values ?? []).map((row) => ({\n tool: String(row[0]),\n calls: Number(row[1]),\n avgTokens: Number(row[2]),\n }));\n\n // Top symbols (exclude nulls)\n const symbolWherePrefix = whereClause ? whereClause + ' AND' : 'WHERE';\n const symbolsResult = this.db.exec(\n `SELECT symbol_id, COUNT(*) as queries\n FROM session_events ${symbolWherePrefix} symbol_id IS NOT NULL\n GROUP BY symbol_id ORDER BY queries DESC LIMIT 5`,\n );\n const topSymbols: SymbolStats[] = (symbolsResult[0]?.values ?? []).map((row) => {\n const symbolId = String(row[0]);\n const parts = symbolId.split('::');\n const name = parts.length >= 2 ? parts[1]! : symbolId;\n return {\n symbolId,\n name,\n queries: Number(row[1]),\n };\n });\n\n // Detail level distribution\n const levelResult = this.db.exec(\n `SELECT detail_level, COUNT(*) as count\n FROM session_events ${symbolWherePrefix} detail_level IS NOT NULL\n GROUP BY detail_level ORDER BY detail_level`,\n );\n const levelRows = levelResult[0]?.values ?? [];\n const levelTotal = levelRows.reduce((sum, row) => sum + Number(row[1]), 0);\n const detailLevelDistribution: DetailLevelStats[] = levelRows.map((row) => ({\n level: String(row[0]),\n count: Number(row[1]),\n percentage: levelTotal > 0 ? Math.round((Number(row[1]) / levelTotal) * 100) : 0,\n }));\n\n return {\n totalCalls,\n totalTokensServed,\n topTools,\n topSymbols,\n detailLevelDistribution,\n };\n }\n\n clear(): void {\n this.ensureTable();\n this.db.run('DELETE FROM session_events');\n this.flushToDisk();\n }\n\n private flushToDisk(): void {\n try {\n this.onWrite?.();\n } catch (err) {\n log.error(`Failed to flush stats to disk: ${(err as Error).message}`);\n }\n }\n}\n","import { existsSync, readdirSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport type { IWorkspace, IPackage } from '@ctxo/plugin-api';\n\nconst MANIFEST_CANDIDATES = [\n 'package.json',\n 'pyproject.toml',\n 'setup.py',\n 'pom.xml',\n 'build.gradle',\n 'build.gradle.kts',\n 'go.mod',\n 'Cargo.toml',\n];\n\nfunction findManifest(dir: string): string {\n for (const name of MANIFEST_CANDIDATES) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) return candidate;\n }\n const csproj = findFirstByExt(dir, '.csproj') ?? findFirstByExt(dir, '.sln');\n if (csproj) return csproj;\n return join(dir, 'package.json');\n}\n\nfunction findFirstByExt(dir: string, ext: string): string | null {\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isFile() && entry.name.endsWith(ext)) return join(dir, entry.name);\n }\n } catch {\n // ignore\n }\n return null;\n}\n\n/**\n * v0.7 implementation: always returns a single-package workspace rooted at `cwd`.\n * Future Nx/Turborepo/pnpm workspace detectors implement the same interface.\n */\nexport function detectWorkspace(cwd: string): IWorkspace {\n const root = resolve(cwd);\n const pkg: IPackage = {\n root,\n manifest: findManifest(root),\n };\n return {\n root,\n packages: [pkg],\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport picomatch from 'picomatch';\nimport { createLogger } from '../logger.js';\nimport {\n CtxoConfigSchema,\n DEFAULT_CONFIG,\n DEFAULT_WATCH_SNAPSHOT_MIN_FILE_CHANGES,\n type CtxoConfig,\n} from './config-schema.js';\n\nconst log = createLogger('ctxo:config');\n\nexport interface LoadedConfig {\n readonly config: CtxoConfig;\n readonly errors: readonly string[];\n readonly invalidGlobs: readonly string[];\n readonly path: string;\n readonly exists: boolean;\n}\n\nfunction isValidGlob(pattern: string): boolean {\n try {\n picomatch(pattern);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction validateGlobs(patterns: string[] | undefined, field: string, invalid: string[]): void {\n if (!patterns) return;\n for (const p of patterns) {\n if (!isValidGlob(p)) invalid.push(`${field}: ${p}`);\n }\n}\n\nexport function loadConfig(ctxoRoot: string): LoadedConfig {\n const path = join(ctxoRoot, 'config.yaml');\n const errors: string[] = [];\n const invalidGlobs: string[] = [];\n\n if (!existsSync(path)) {\n return { config: DEFAULT_CONFIG, errors, invalidGlobs, path, exists: false };\n }\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (err) {\n errors.push(`Cannot read ${path}: ${(err as Error).message}`);\n return { config: DEFAULT_CONFIG, errors, invalidGlobs, path, exists: true };\n }\n\n let parsed: unknown;\n try {\n parsed = parseYaml(raw);\n } catch (err) {\n errors.push(`Invalid YAML in ${path}: ${(err as Error).message}`);\n log.warn(`Invalid YAML in config.yaml, falling back to defaults: ${(err as Error).message}`);\n return { config: DEFAULT_CONFIG, errors, invalidGlobs, path, exists: true };\n }\n\n if (parsed === null || parsed === undefined) {\n return { config: DEFAULT_CONFIG, errors, invalidGlobs, path, exists: true };\n }\n\n const result = CtxoConfigSchema.safeParse(parsed);\n if (!result.success) {\n for (const issue of result.error.issues) {\n errors.push(`${issue.path.join('.') || '<root>'}: ${issue.message}`);\n }\n log.warn(`Invalid config.yaml (${errors.length} issue(s)), falling back to defaults`);\n return { config: DEFAULT_CONFIG, errors, invalidGlobs, path, exists: true };\n }\n\n validateGlobs(result.data.index?.ignore, 'index.ignore', invalidGlobs);\n validateGlobs(result.data.index?.ignoreProjects, 'index.ignoreProjects', invalidGlobs);\n for (const pattern of invalidGlobs) {\n log.warn(`Invalid glob pattern in config.yaml — ${pattern}`);\n }\n\n return { config: result.data, errors, invalidGlobs, path, exists: true };\n}\n\nexport function statsEnabled(config: CtxoConfig): boolean {\n return config.stats?.enabled !== false;\n}\n\nexport function indexIgnorePatterns(config: CtxoConfig): string[] {\n return (config.index?.ignore ?? []).filter(isValidGlob);\n}\n\nexport function indexIgnoreProjectPatterns(config: CtxoConfig): string[] {\n return (config.index?.ignoreProjects ?? []).filter(isValidGlob);\n}\n\nexport function maskingClusterLabelPatterns(config: CtxoConfig): string[] {\n return (config.masking?.clusterLabels ?? []).filter(isValidGlob);\n}\n\nexport function watchSnapshotMinFileChanges(config: CtxoConfig): number {\n const value = config.watch?.snapshotMinFileChanges;\n if (typeof value === 'number' && Number.isFinite(value) && value >= 1) return value;\n return DEFAULT_WATCH_SNAPSHOT_MIN_FILE_CHANGES;\n}\n\n/**\n * Build a single predicate that returns true when the given path matches any\n * of the supplied glob patterns. Paths are normalised to forward slashes.\n */\nexport function makeGlobMatcher(patterns: string[]): (path: string) => boolean {\n if (patterns.length === 0) return () => false;\n const matchers = patterns.map((p) => picomatch(p, { dot: true }));\n return (input: string) => {\n const normalised = input.replace(/\\\\/g, '/');\n return matchers.some((m) => m(normalised));\n };\n}\n","import { z } from 'zod';\n\n/**\n * Zod schema for `.ctxo/config.yaml`.\n *\n * Unknown fields on the root are ignored (forward compatibility) but unknown\n * fields under recognised sections (`stats`, `index`) are rejected so typos\n * surface immediately via `ctxo doctor`.\n */\nexport const StatsConfigSchema = z\n .object({\n enabled: z.boolean().optional(),\n })\n .strict();\n\nexport const IndexConfigSchema = z\n .object({\n ignore: z.array(z.string().min(1)).optional(),\n ignoreProjects: z.array(z.string().min(1)).optional(),\n })\n .strict();\n\nexport const MaskingConfigSchema = z\n .object({\n /**\n * Glob patterns matched against community cluster labels. Matching labels\n * are replaced with `[masked-cluster-N]` in MCP tool responses so\n * org-sensitive folder names (e.g. `src/payment-vault`, `internal-*`)\n * never surface to AI agents over the stdio channel.\n */\n clusterLabels: z.array(z.string().min(1)).optional(),\n })\n .strict();\n\nexport const WatchConfigSchema = z\n .object({\n /**\n * Minimum number of files that must be re-indexed since the last\n * community snapshot before `ctxo watch` recomputes one. Default: 5.\n * On large monorepos Louvain + PageRank cost ~1 s of blocking CPU;\n * bumping this (e.g. 25) avoids recomputing on every save.\n */\n snapshotMinFileChanges: z.number().int().min(1).optional(),\n })\n .strict();\n\nexport const GateConfigSchema = z\n .object({\n enabled: z.boolean().optional(),\n sensitivity: z.enum(['strict', 'balanced', 'lenient']).optional(),\n /** Absolute floor: minimum confirmed+likely dependents before the gate can fire. */\n minDependents: z.number().int().min(0).optional(),\n /** Repo-relative significance: symbol must be in the top `percentile`% by PageRank. */\n percentile: z.number().int().min(1).max(100).optional(),\n })\n .strict();\n\nexport const CtxoConfigSchema = z\n .object({\n version: z.union([z.string(), z.number()]).optional(),\n stats: StatsConfigSchema.optional(),\n index: IndexConfigSchema.optional(),\n masking: MaskingConfigSchema.optional(),\n watch: WatchConfigSchema.optional(),\n gate: GateConfigSchema.optional(),\n })\n .passthrough();\n\nexport type StatsConfig = z.infer<typeof StatsConfigSchema>;\nexport type IndexConfig = z.infer<typeof IndexConfigSchema>;\nexport type MaskingConfig = z.infer<typeof MaskingConfigSchema>;\nexport type WatchConfig = z.infer<typeof WatchConfigSchema>;\nexport type GateConfig = z.infer<typeof GateConfigSchema>;\nexport type CtxoConfig = z.infer<typeof CtxoConfigSchema>;\n\nexport const DEFAULT_WATCH_SNAPSHOT_MIN_FILE_CHANGES = 5;\n\nexport const DEFAULT_CONFIG: CtxoConfig = {\n version: '1.0',\n stats: { enabled: true },\n index: { ignore: [], ignoreProjects: [] },\n masking: { clusterLabels: [] },\n watch: { snapshotMinFileChanges: DEFAULT_WATCH_SNAPSHOT_MIN_FILE_CHANGES },\n gate: { enabled: true, sensitivity: 'balanced' },\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 [p0, p1] = node.symbolId.split('::');\n if (p0 !== undefined && p1 !== undefined) {\n this.nodesByFileAndName.set(`${p0}::${p1}`, 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 [p0, p1] = id.split('::');\n if (p0 !== undefined && p1 !== undefined) {\n const fuzzyKey = `${p0}::${p1}`;\n const match = this.nodesByFileAndName.get(fuzzyKey);\n if (match) return match.symbolId;\n\n // Try .js → .ts extension swap (handles unresolved module specifiers)\n const jsToTs = p0.replace(/\\.js$/, '.ts');\n if (jsToTs !== p0) {\n const altKey = `${jsToTs}::${p1}`;\n const altMatch = this.nodesByFileAndName.get(altKey);\n if (altMatch) return altMatch.symbolId;\n }\n }\n return id;\n }\n\n private resolveNodeFuzzy(id: string): SymbolNode | undefined {\n const [p0, p1] = id.split('::');\n if (p0 !== undefined && p1 !== undefined) {\n const match = this.nodesByFileAndName.get(`${p0}::${p1}`);\n if (match) return match;\n\n // Try .js → .ts extension swap (consistent with resolveNodeId)\n const jsToTs = p0.replace(/\\.js$/, '.ts');\n if (jsToTs !== p0) {\n return this.nodesByFileAndName.get(`${jsToTs}::${p1}`);\n }\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) || this.resolveNodeFuzzy(symbolId) !== undefined;\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 { CommitRecord, AntiPattern } from '../types.js';\n\n// Explicit revert patterns\nconst REVERT_QUOTED_PATTERN = /^Revert \"(.+)\"$/;\nconst REVERT_PREFIX_PATTERN = /^revert:\\s*(.+)$/i;\n\n// Undo/rollback patterns\nconst UNDO_PATTERN = /^undo[:\\s]/i;\nconst ROLLBACK_PATTERN = /^rollback[:\\s]/i;\n\n// Indirect revert indicators (keywords in commit message body)\nconst INDIRECT_KEYWORDS = [\n /\\brevert(?:s|ed|ing)?\\b/i,\n /\\broll(?:s|ed|ing)?\\s*back\\b/i,\n /\\bundo(?:es|ne|ing)?\\b/i,\n /\\bbacked?\\s*out\\b/i,\n /\\bremov(?:e|es|ed|ing)\\s+(?:broken|buggy|faulty)\\b/i,\n];\n\nexport class RevertDetector {\n detect(commits: readonly CommitRecord[]): AntiPattern[] {\n const antiPatterns: AntiPattern[] = [];\n\n for (const commit of commits) {\n if (!commit.message) continue;\n\n if (this.isRevert(commit.message)) {\n antiPatterns.push({\n hash: commit.hash,\n message: commit.message,\n date: commit.date,\n });\n }\n }\n\n return antiPatterns;\n }\n\n private isRevert(message: string): boolean {\n // Explicit patterns (high confidence)\n if (REVERT_QUOTED_PATTERN.test(message)) return true;\n if (REVERT_PREFIX_PATTERN.test(message)) return true;\n if (UNDO_PATTERN.test(message)) return true;\n if (ROLLBACK_PATTERN.test(message)) return true;\n\n // Indirect indicators (keyword search in full message)\n for (const pattern of INDIRECT_KEYWORDS) {\n if (pattern.test(message)) return true;\n }\n\n return false;\n }\n}\n","import type { CommunityEntry, CommunitySnapshot, DriftEvent } from '../types.js';\n\nexport type DriftStability = 'stable' | 'transient' | 'unverified';\n\nexport interface DriftEventWithStability extends DriftEvent {\n readonly stability: DriftStability;\n}\n\nexport interface DriftStabilitySummary {\n /** Events confirmed across ≥2 snapshots (real drift). */\n readonly stable: number;\n /** Events that bounced back to a prior cluster (likely Louvain jitter). */\n readonly transient: number;\n /** Events seen only once because history is too shallow to classify. */\n readonly unverified: number;\n}\n\nexport interface DriftDetectionResult {\n readonly events: DriftEventWithStability[];\n readonly confidence: 'high' | 'medium' | 'low';\n readonly snapshotsAvailable: number;\n readonly stability: DriftStabilitySummary;\n readonly hint?: string;\n}\n\n// Issue #56: lowered from 0.5 → 0.3 to be more forgiving when Louvain\n// renumbers clusters between runs. Jaccard 0.3 still requires meaningful\n// membership overlap but tolerates routine re-clustering.\nconst DEFAULT_JACCARD_THRESHOLD = 0.3;\n\nexport interface DriftDetectorOptions {\n readonly jaccardThreshold?: number;\n /**\n * Filter out transient drift — i.e., events where the symbol bounced back\n * to a previously-seen cluster within the retained snapshot window. Enabled\n * by default so callers don't have to wade through Louvain jitter.\n */\n readonly suppressTransient?: boolean;\n}\n\nexport class DriftDetector {\n private readonly jaccardThreshold: number;\n private readonly suppressTransient: boolean;\n\n constructor(options: DriftDetectorOptions = {}) {\n this.jaccardThreshold = options.jaccardThreshold ?? DEFAULT_JACCARD_THRESHOLD;\n this.suppressTransient = options.suppressTransient ?? true;\n }\n\n detect(\n current: CommunitySnapshot,\n history: readonly CommunitySnapshot[],\n ): DriftDetectionResult {\n const snapshotsAvailable = history.length;\n const previous = history[0];\n const anchor = history[1];\n\n if (!previous) {\n return {\n events: [],\n confidence: 'low',\n snapshotsAvailable,\n stability: { stable: 0, transient: 0, unverified: 0 },\n hint: 'No prior snapshot available — install the post-commit hook (`ctxo init`), run `ctxo watch`, or add `ctxo index --check` to CI so the drift history accumulates.',\n };\n }\n\n const currentByCommunityId = indexByCommunityId(current.communities);\n const previousByCommunityId = indexByCommunityId(previous.communities);\n const mapping = this.mapCommunities(currentByCommunityId, previousByCommunityId);\n\n const currentAssignments = assignmentMap(current.communities);\n const previousAssignments = assignmentMap(previous.communities);\n const anchorAssignments = anchor ? assignmentMap(anchor.communities) : undefined;\n const previousLabels = labelMap(previous.communities);\n const currentLabels = labelMap(current.communities);\n\n // Compute anchor→previous mapping so we can translate the symbol's anchor\n // cluster id into the \"previous snapshot\" id space for comparison.\n const anchorToPrev = anchor\n ? this.mapCommunities(previousByCommunityId, indexByCommunityId(anchor.communities))\n : new Map<number, number>();\n\n const rawEvents: DriftEventWithStability[] = [];\n const stability = { stable: 0, transient: 0, unverified: 0 };\n\n for (const [symbolId, currentId] of currentAssignments) {\n const prevId = previousAssignments.get(symbolId);\n if (prevId === undefined) continue;\n const mappedPrevId = mapping.get(prevId);\n if (mappedPrevId === currentId) continue;\n\n // Stability classification:\n // stable — anchor's cluster maps to `prevId` (the symbol was settled in the\n // prior cluster for ≥2 snapshots, then moved; real drift).\n // transient — anchor's cluster maps to `currentId` (the symbol bounced between\n // clusters; likely Louvain jitter — worth suppressing by default).\n // unverified — no anchor snapshot, or anchor has no entry for this symbol.\n let classification: DriftStability;\n if (!anchorAssignments) {\n classification = 'unverified';\n } else {\n const anchorId = anchorAssignments.get(symbolId);\n if (anchorId === undefined) {\n classification = 'unverified';\n } else {\n const anchorInPrevSpace = anchorToPrev.get(anchorId);\n if (anchorInPrevSpace === prevId) {\n classification = 'stable';\n } else if (anchorInPrevSpace !== undefined) {\n // Anchor maps to some previous cluster that is not the movedFrom cluster.\n // If that translates forward to the current cluster, this is a bounce.\n const anchorInCurrentSpace = mapping.get(anchorInPrevSpace);\n classification =\n anchorInCurrentSpace === currentId ? 'transient' : 'unverified';\n } else {\n classification = 'unverified';\n }\n }\n }\n\n stability[classification]++;\n rawEvents.push({\n symbolId,\n movedFrom: {\n id: prevId,\n label: previousLabels.get(prevId) ?? `community-${prevId}`,\n },\n movedTo: {\n id: currentId,\n label: currentLabels.get(currentId) ?? `community-${currentId}`,\n },\n firstSeenInNewCluster: current.computedAt,\n stability: classification,\n });\n }\n\n const events = (this.suppressTransient\n ? rawEvents.filter((e) => e.stability !== 'transient')\n : rawEvents\n ).sort((a, b) => a.symbolId.localeCompare(b.symbolId));\n\n return {\n events,\n confidence: confidenceFor(snapshotsAvailable),\n snapshotsAvailable,\n stability,\n hint: hintFor(snapshotsAvailable),\n };\n }\n\n private mapCommunities(\n current: ReadonlyMap<number, Set<string>>,\n previous: ReadonlyMap<number, Set<string>>,\n ): Map<number, number> {\n // For each previous-community, pick the current-community with highest Jaccard.\n const mapping = new Map<number, number>();\n for (const [prevId, prevMembers] of previous) {\n let bestCurrentId: number | undefined;\n let bestOverlap = 0;\n for (const [currentId, currentMembers] of current) {\n const overlap = jaccard(prevMembers, currentMembers);\n if (overlap > bestOverlap) {\n bestOverlap = overlap;\n bestCurrentId = currentId;\n }\n }\n if (bestCurrentId !== undefined && bestOverlap >= this.jaccardThreshold) {\n mapping.set(prevId, bestCurrentId);\n }\n }\n return mapping;\n }\n}\n\nfunction indexByCommunityId(\n entries: readonly CommunityEntry[],\n): Map<number, Set<string>> {\n const index = new Map<number, Set<string>>();\n for (const entry of entries) {\n const bucket = index.get(entry.communityId) ?? new Set<string>();\n bucket.add(entry.symbolId);\n index.set(entry.communityId, bucket);\n }\n return index;\n}\n\nfunction assignmentMap(entries: readonly CommunityEntry[]): Map<string, number> {\n const map = new Map<string, number>();\n for (const entry of entries) {\n map.set(entry.symbolId, entry.communityId);\n }\n return map;\n}\n\nfunction labelMap(entries: readonly CommunityEntry[]): Map<number, string> {\n const labels = new Map<number, string>();\n for (const entry of entries) {\n if (!labels.has(entry.communityId)) {\n labels.set(entry.communityId, entry.communityLabel);\n }\n }\n return labels;\n}\n\nfunction jaccard(a: ReadonlySet<string>, b: ReadonlySet<string>): number {\n if (a.size === 0 && b.size === 0) return 1;\n let intersection = 0;\n for (const x of a) if (b.has(x)) intersection++;\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction confidenceFor(snapshots: number): 'high' | 'medium' | 'low' {\n if (snapshots >= 7) return 'high';\n if (snapshots >= 3) return 'medium';\n return 'low';\n}\n\nfunction hintFor(snapshots: number): string | undefined {\n if (snapshots >= 3) return undefined;\n return 'Install post-commit hook or run `ctxo index` more often to build a richer drift history.';\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport type { CoChangeEntry } from '../types.js';\n\nexport type ImpactConfidence = 'confirmed' | 'likely' | 'potential';\n\nexport interface BlastRadiusEntry {\n readonly symbolId: string;\n readonly depth: number;\n readonly dependentCount: number;\n readonly riskScore: number;\n readonly confidence: ImpactConfidence;\n readonly edgeKinds: string[];\n readonly coChangeFrequency?: number;\n}\n\nexport interface BlastRadiusResult {\n readonly impactedSymbols: BlastRadiusEntry[];\n readonly directDependentsCount: number;\n readonly confirmedCount: number;\n readonly likelyCount: number;\n readonly potentialCount: number;\n readonly overallRiskScore: number;\n}\n\nconst CONFIRMED_KINDS = new Set(['calls', 'extends', 'implements']);\nconst LIKELY_KINDS = new Set(['uses']);\n\nconst CONFIDENCE_RANK: Record<ImpactConfidence, number> = {\n confirmed: 2,\n likely: 1,\n potential: 0,\n};\n\nfunction edgeKindToConfidence(kind: string): ImpactConfidence {\n if (CONFIRMED_KINDS.has(kind)) return 'confirmed';\n if (LIKELY_KINDS.has(kind)) return 'likely';\n return 'potential';\n}\n\nexport class BlastRadiusCalculator {\n calculate(graph: SymbolGraph, symbolId: string, coChangeMap?: Map<string, CoChangeEntry[]>): BlastRadiusResult {\n if (!graph.hasNode(symbolId)) {\n return { impactedSymbols: [], directDependentsCount: 0, confirmedCount: 0, likelyCount: 0, potentialCount: 0, overallRiskScore: 0 };\n }\n\n const sourceFile = symbolId.split('::')[0]!;\n\n const visited = new Set<string>([symbolId]);\n const rawEntries: Array<{ symbolId: string; depth: number; riskScore: number; confidence: ImpactConfidence; edgeKinds: string[]; coChangeFrequency?: number }> = [];\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\n // Group edges by source node, pick strongest confidence per node\n // confirmed > likely > potential — collect all edge kinds\n const bestByNode = new Map<string, { confidence: ImpactConfidence; kinds: Set<string> }>();\n for (const edge of reverseEdges) {\n if (visited.has(edge.from)) continue;\n const conf = edgeKindToConfidence(edge.kind);\n const existing = bestByNode.get(edge.from);\n if (!existing) {\n bestByNode.set(edge.from, { confidence: conf, kinds: new Set([edge.kind]) });\n } else {\n existing.kinds.add(edge.kind);\n if (CONFIDENCE_RANK[conf] > CONFIDENCE_RANK[existing.confidence]) {\n existing.confidence = conf;\n }\n }\n }\n\n for (const [nodeId, info] of bestByNode) {\n visited.add(nodeId);\n // Keep orphan edge sources (common for test/config files that only\n // import — ts-morph writes their synthetic \"file-module\" symbolId to\n // the edge's `from` but never to the file's symbols[] array). BFS\n // into an orphan terminates naturally since getReverseEdges returns [].\n\n const depth = current.depth + 1;\n const riskScore = 1 / Math.pow(depth, 0.7);\n\n // Co-change boost: if files frequently change together, upgrade potential → likely\n let confidence = info.confidence;\n let coChangeFrequency: number | undefined;\n if (coChangeMap) {\n const nodeFile = nodeId.split('::')[0]!;\n const coEntries = coChangeMap.get(sourceFile);\n if (coEntries) {\n const match = coEntries.find(e => e.file1 === nodeFile || e.file2 === nodeFile);\n if (match) {\n coChangeFrequency = match.frequency;\n if (confidence === 'potential' && match.frequency > 0.5) {\n confidence = 'likely';\n }\n }\n }\n }\n\n rawEntries.push({\n symbolId: nodeId,\n depth,\n riskScore: Math.round(riskScore * 1000) / 1000,\n confidence,\n edgeKinds: [...info.kinds],\n coChangeFrequency,\n });\n\n queue.push({ id: nodeId, depth });\n }\n }\n\n // GAP-30 FIX: compute dependentCount as blast-scope in-degree (not global)\n const blastSet = new Set(rawEntries.map((e) => e.symbolId));\n blastSet.add(symbolId);\n\n const entries: BlastRadiusEntry[] = rawEntries.map((e) => ({\n ...e,\n dependentCount: graph.getReverseEdges(e.symbolId).filter((re) => blastSet.has(re.from)).length,\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 // GAP-29 FIX: depth-weighted risk — direct dependents dominate\n const totalRisk = entries.reduce((sum, e) => sum + e.riskScore, 0);\n const overallRiskScore = Math.round(Math.min(totalRisk / Math.max(directDependentsCount, 1), 1) * 1000) / 1000;\n\n const confirmedCount = entries.filter((e) => e.confidence === 'confirmed').length;\n const likelyCount = entries.filter((e) => e.confidence === 'likely').length;\n const potentialCount = entries.filter((e) => e.confidence === 'potential').length;\n\n return { impactedSymbols: entries, directDependentsCount, confirmedCount, likelyCount, potentialCount, overallRiskScore };\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","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport type { SymbolKind } from '../types.js';\n\nexport type DeadCodeConfidence = 1.0 | 0.9 | 0.7;\n\nexport interface DeadSymbolEntry {\n readonly symbolId: string;\n readonly file: string;\n readonly name: string;\n readonly kind: SymbolKind;\n readonly confidence: DeadCodeConfidence;\n readonly reason: string;\n readonly cascadeDepth?: number;\n}\n\nexport interface UnusedExportEntry {\n readonly symbolId: string;\n readonly file: string;\n readonly name: string;\n readonly kind: SymbolKind;\n}\n\nexport interface ScaffoldingEntry {\n readonly file: string;\n readonly line: number;\n readonly pattern: string;\n readonly text: string;\n}\n\nexport interface DeadCodeResult {\n readonly totalSymbols: number;\n readonly reachableSymbols: number;\n readonly deadSymbols: DeadSymbolEntry[];\n readonly unusedExports: UnusedExportEntry[];\n readonly deadFiles: string[];\n readonly scaffolding: ScaffoldingEntry[];\n readonly deadCodePercentage: number;\n}\n\n// Patterns for files to exclude from dead code analysis\nconst TEST_PATTERNS = [/__tests__/, /\\.test\\.ts$/, /\\.spec\\.ts$/, /\\btests\\//, /\\bfixtures?\\//];\nconst CONFIG_PATTERNS = [/\\.(config|rc)\\.(ts|js|json)$/, /tsconfig/, /eslint/, /\\.d\\.ts$/];\n\n// Framework lifecycle symbols — should never be flagged as dead\nconst FRAMEWORK_PATTERNS = [\n // Vitest\n /^(describe|it|expect|beforeAll|beforeEach|afterAll|afterEach|vi)$/,\n // MCP SDK\n /^(registerTool|registerPrompt|connect|close)$/,\n // Zod schemas (conventionally exported for validation)\n /Schema$/,\n // Node.js lifecycle\n /^main$/,\n];\n\n// Scaffolding / AI artifact patterns\nconst SCAFFOLDING_PATTERNS = [\n { regex: /\\bTODO\\b/i, pattern: 'TODO' },\n { regex: /\\bFIXME\\b/i, pattern: 'FIXME' },\n { regex: /\\bHACK\\b/i, pattern: 'HACK' },\n { regex: /\\bPLACEHOLDER\\b/i, pattern: 'PLACEHOLDER' },\n { regex: /\\bXXX\\b/, pattern: 'XXX' },\n { regex: /Phase\\s+\\d|Step\\s+\\d/i, pattern: 'PHASE/STEP' },\n { regex: /not\\s+(?:yet\\s+)?implement/i, pattern: 'NOT_IMPLEMENTED' },\n { regex: /temporary|temp\\s+fix/i, pattern: 'TEMPORARY' },\n];\n\nexport class DeadCodeDetector {\n detect(\n graph: SymbolGraph,\n options: { includeTests?: boolean; sourceContents?: Map<string, string> } = {},\n ): DeadCodeResult {\n const allNodes = graph.allNodes();\n\n // Filter candidates — exclude tests/config by default\n const candidates = options.includeTests\n ? allNodes\n : allNodes.filter((n) => {\n const file = n.symbolId.split('::')[0] ?? '';\n return !this.matchesAny(file, TEST_PATTERNS) && !this.matchesAny(file, CONFIG_PATTERNS);\n });\n\n if (candidates.length === 0) {\n return { totalSymbols: 0, reachableSymbols: 0, deadSymbols: [], unusedExports: [], deadFiles: [], scaffolding: this.detectScaffolding(options.sourceContents), deadCodePercentage: 0 };\n }\n\n // Dynamic entry point detection: symbols with zero reverse edges (no one imports them)\n const candidateIds = new Set(candidates.map((n) => n.symbolId));\n const entryPoints = new Set<string>();\n\n for (const node of candidates) {\n // Framework lifecycle symbols are always entry points (never dead)\n if (this.isFrameworkSymbol(node.name)) {\n entryPoints.add(node.symbolId);\n continue;\n }\n\n const reverseEdges = graph.getReverseEdges(node.symbolId);\n const forwardEdges = graph.getForwardEdges(node.symbolId);\n\n // Filter: only count reverse edges from OTHER candidates\n const incomingFromCandidates = reverseEdges.filter((e) => candidateIds.has(e.from) && e.from !== node.symbolId);\n\n if (incomingFromCandidates.length === 0) {\n const hasOutgoing = forwardEdges.some((e) => candidateIds.has(e.to));\n if (hasOutgoing || reverseEdges.length > 0) {\n entryPoints.add(node.symbolId);\n }\n }\n }\n\n // Forward BFS from all entry points — mark reachable symbols\n const reachable = new Set<string>();\n const queue = [...entryPoints];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (reachable.has(current)) continue;\n reachable.add(current);\n\n for (const edge of graph.getForwardEdges(current)) {\n if (!reachable.has(edge.to) && candidateIds.has(edge.to)) {\n queue.push(edge.to);\n }\n }\n }\n\n // Dead = candidates not reachable from any entry point\n // These are \"island\" symbols — circular deps where the whole cluster is unreachable\n const deadIds = new Set<string>();\n for (const node of candidates) {\n if (!reachable.has(node.symbolId)) {\n deadIds.add(node.symbolId);\n }\n }\n\n // Compute cascade depth: how deep in the dead chain is this symbol?\n const cascadeDepthMap = this.computeCascadeDepths(graph, deadIds, candidateIds);\n\n // Compute confidence per dead symbol\n const deadSymbols: DeadSymbolEntry[] = [];\n for (const node of candidates) {\n if (!deadIds.has(node.symbolId)) continue;\n\n const file = node.symbolId.split('::')[0] ?? '';\n const confidence = this.computeConfidence(graph, node.symbolId, deadIds);\n const cascadeDepth = cascadeDepthMap.get(node.symbolId);\n\n deadSymbols.push({\n symbolId: node.symbolId,\n file,\n name: node.name,\n kind: node.kind,\n confidence,\n reason: this.describeReason(confidence),\n ...(cascadeDepth !== undefined && cascadeDepth > 0 ? { cascadeDepth } : {}),\n });\n }\n\n // Dead files = files where ALL candidate symbols are dead\n const fileStats = new Map<string, { total: number; dead: number }>();\n for (const node of candidates) {\n const file = node.symbolId.split('::')[0] ?? '';\n const stats = fileStats.get(file) ?? { total: 0, dead: 0 };\n stats.total++;\n if (deadIds.has(node.symbolId)) stats.dead++;\n fileStats.set(file, stats);\n }\n\n const deadFiles = [...fileStats.entries()]\n .filter(([, s]) => s.total > 0 && s.dead === s.total)\n .map(([file]) => file);\n\n // Unused exports: symbols with zero reverse edges from other candidates\n // Different from dead code: an unused export may be an entry point (reachable)\n // but still never imported by anyone\n const unusedExports: UnusedExportEntry[] = [];\n for (const node of candidates) {\n const reverseEdges = graph.getReverseEdges(node.symbolId);\n const externalImporters = reverseEdges.filter((e) =>\n candidateIds.has(e.from) && e.from !== node.symbolId,\n );\n\n if (externalImporters.length === 0 && !deadIds.has(node.symbolId)) {\n // Exported, reachable (entry point), but nobody imports it\n unusedExports.push({\n symbolId: node.symbolId,\n file: node.symbolId.split('::')[0] ?? '',\n name: node.name,\n kind: node.kind,\n });\n }\n }\n\n // Scaffolding detection: scan source contents for TODO/FIXME/HACK patterns\n const scaffolding = this.detectScaffolding(options.sourceContents);\n\n const deadCodePercentage = Math.round((deadSymbols.length / candidates.length) * 100 * 10) / 10;\n\n return {\n totalSymbols: candidates.length,\n reachableSymbols: reachable.size,\n deadSymbols,\n unusedExports,\n deadFiles,\n scaffolding,\n deadCodePercentage,\n };\n }\n\n private computeConfidence(\n graph: SymbolGraph,\n symbolId: string,\n deadIds: Set<string>,\n ): DeadCodeConfidence {\n const reverseEdges = graph.getReverseEdges(symbolId);\n\n // 1.0: Zero importers at all — definitely dead\n if (reverseEdges.length === 0) {\n return 1.0;\n }\n\n // 0.9: Has importers but they're all from test/config files\n const allImportersExcluded = reverseEdges.every((e) => {\n const importerFile = e.from.split('::')[0] ?? '';\n return this.matchesAny(importerFile, TEST_PATTERNS) || this.matchesAny(importerFile, CONFIG_PATTERNS);\n });\n if (allImportersExcluded) {\n return 0.9;\n }\n\n // 0.7: All importers are themselves dead (cascading dead code)\n const allImportersDead = reverseEdges.every((e) => deadIds.has(e.from));\n if (allImportersDead) {\n return 0.7;\n }\n\n // Shouldn't reach here if symbol is truly dead, but default to 0.7\n return 0.7;\n }\n\n private describeReason(confidence: DeadCodeConfidence): string {\n switch (confidence) {\n case 1.0:\n return 'Zero importers — no code references this symbol';\n case 0.9:\n return 'Only referenced from test/config files';\n case 0.7:\n return 'All importers are themselves dead (cascading)';\n }\n }\n\n private isFrameworkSymbol(name: string): boolean {\n return FRAMEWORK_PATTERNS.some((p) => p.test(name));\n }\n\n private computeCascadeDepths(\n graph: SymbolGraph,\n deadIds: Set<string>,\n candidateIds: Set<string>,\n ): Map<string, number> {\n const depths = new Map<string, number>();\n\n // Find root dead symbols (zero reverse edges from other dead symbols)\n const rootDead = new Set<string>();\n for (const id of deadIds) {\n const reverseEdges = graph.getReverseEdges(id);\n const deadImporters = reverseEdges.filter((e) => deadIds.has(e.from) && e.from !== id);\n if (deadImporters.length === 0) {\n rootDead.add(id);\n depths.set(id, 0);\n }\n }\n\n // BFS from root dead symbols to compute cascade depth\n const queue = [...rootDead].map((id) => ({ id, depth: 0 }));\n const visited = new Set(rootDead);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.getForwardEdges(current.id)) {\n if (deadIds.has(edge.to) && !visited.has(edge.to) && candidateIds.has(edge.to)) {\n const newDepth = current.depth + 1;\n depths.set(edge.to, newDepth);\n visited.add(edge.to);\n queue.push({ id: edge.to, depth: newDepth });\n }\n }\n }\n\n return depths;\n }\n\n private detectScaffolding(sourceContents?: Map<string, string>): ScaffoldingEntry[] {\n if (!sourceContents || sourceContents.size === 0) return [];\n\n const results: ScaffoldingEntry[] = [];\n\n for (const [file, content] of sourceContents) {\n // Skip test/config files\n if (this.matchesAny(file, TEST_PATTERNS) || this.matchesAny(file, CONFIG_PATTERNS)) continue;\n\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n for (const { regex, pattern } of SCAFFOLDING_PATTERNS) {\n if (regex.test(line)) {\n results.push({\n file,\n line: i + 1,\n pattern,\n text: line.trim().slice(0, 120),\n });\n break; // One match per line\n }\n }\n }\n }\n\n return results;\n }\n\n private matchesAny(value: string, patterns: RegExp[]): boolean {\n return patterns.some((p) => p.test(value));\n }\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\n\nexport interface PageRankEntry {\n readonly symbolId: string;\n readonly name: string;\n readonly kind: string;\n readonly file: string;\n readonly score: number;\n readonly inDegree: number;\n readonly outDegree: number;\n}\n\nexport interface PageRankResult {\n readonly rankings: PageRankEntry[];\n readonly totalSymbols: number;\n readonly iterations: number;\n readonly converged: boolean;\n}\n\nexport interface PageRankOptions {\n readonly damping?: number;\n readonly maxIterations?: number;\n readonly tolerance?: number;\n readonly limit?: number;\n}\n\nconst DEFAULT_DAMPING = 0.85;\nconst DEFAULT_MAX_ITERATIONS = 100;\nconst DEFAULT_TOLERANCE = 1e-6;\nconst DEFAULT_LIMIT = 25;\n\nexport class PageRankCalculator {\n calculate(graph: SymbolGraph, options: PageRankOptions = {}): PageRankResult {\n const damping = options.damping ?? DEFAULT_DAMPING;\n const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n const tolerance = options.tolerance ?? DEFAULT_TOLERANCE;\n const limit = options.limit ?? DEFAULT_LIMIT;\n\n const nodes = graph.allNodes();\n const n = nodes.length;\n\n if (n === 0) {\n return { rankings: [], totalSymbols: 0, iterations: 0, converged: true };\n }\n\n // Initialize scores uniformly\n const scores = new Map<string, number>();\n const initialScore = 1 / n;\n for (const node of nodes) {\n scores.set(node.symbolId, initialScore);\n }\n\n // Precompute out-degree for each node\n const outDegree = new Map<string, number>();\n for (const node of nodes) {\n outDegree.set(node.symbolId, graph.getForwardEdges(node.symbolId).length);\n }\n\n // Iterative PageRank\n // Edge direction: A->B means \"A depends on B\" (A imports/calls/uses B)\n // PageRank: B is important if many important nodes link to B\n // Score distribution: each node distributes its score via forward edges to dependencies\n // So we accumulate via reverse edges: for each node, sum score from nodes that point to it\n let iterations = 0;\n let converged = false;\n\n while (iterations < maxIterations) {\n const newScores = new Map<string, number>();\n const base = (1 - damping) / n;\n\n // Handle dangling nodes (no outgoing edges) — distribute their score evenly\n let danglingSum = 0;\n for (const node of nodes) {\n if ((outDegree.get(node.symbolId) ?? 0) === 0) {\n danglingSum += scores.get(node.symbolId) ?? 0;\n }\n }\n const danglingContrib = damping * danglingSum / n;\n\n for (const node of nodes) {\n let incomingScore = 0;\n\n // Sum contributions from all nodes that have a forward edge TO this node\n // (i.e., reverse edges of this node)\n const reverseEdges = graph.getReverseEdges(node.symbolId);\n const contributors = new Set<string>();\n for (const edge of reverseEdges) {\n if (contributors.has(edge.from)) continue; // deduplicate multi-edges\n contributors.add(edge.from);\n const fromScore = scores.get(edge.from) ?? 0;\n const fromOut = outDegree.get(edge.from) ?? 1;\n incomingScore += fromScore / fromOut;\n }\n\n newScores.set(node.symbolId, base + damping * incomingScore + danglingContrib);\n }\n\n // Check convergence\n let maxDelta = 0;\n for (const node of nodes) {\n const delta = Math.abs((newScores.get(node.symbolId) ?? 0) - (scores.get(node.symbolId) ?? 0));\n if (delta > maxDelta) maxDelta = delta;\n }\n\n // Update scores\n for (const [id, score] of newScores) {\n scores.set(id, score);\n }\n\n iterations++;\n\n if (maxDelta < tolerance) {\n converged = true;\n break;\n }\n }\n\n // Build ranked results\n const entries: PageRankEntry[] = nodes.map((node) => ({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n score: Math.round((scores.get(node.symbolId) ?? 0) * 1_000_000) / 1_000_000,\n inDegree: graph.getReverseEdges(node.symbolId).length,\n outDegree: outDegree.get(node.symbolId) ?? 0,\n }));\n\n // Sort by score descending\n entries.sort((a, b) => b.score - a.score);\n\n return {\n rankings: entries.slice(0, limit),\n totalSymbols: n,\n iterations,\n converged,\n };\n }\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport type {\n BoundaryViolation,\n CommunityEntry,\n CommunitySnapshot,\n} from '../types.js';\n\nexport interface BoundaryViolationDetectionResult {\n readonly violations: BoundaryViolation[];\n readonly confidence: 'high' | 'medium' | 'low';\n readonly snapshotsAvailable: number;\n readonly hint?: string;\n}\n\nconst HIGH_SEVERITY_THRESHOLD = 0;\nconst MEDIUM_SEVERITY_THRESHOLD = 2;\nconst MIN_SNAPSHOTS_FOR_CONFIDENCE = 2;\n\nexport class BoundaryViolationDetector {\n detect(\n graph: SymbolGraph,\n current: CommunitySnapshot,\n history: readonly CommunitySnapshot[],\n ): BoundaryViolationDetectionResult {\n const snapshotsAvailable = history.length;\n const currentAssignments = assignmentMap(current.communities);\n const currentLabels = labelMap(current.communities);\n const historicalAssignments = history.map((snapshot) => ({\n snapshot,\n assignments: assignmentMap(snapshot.communities),\n labels: labelMap(snapshot.communities),\n }));\n\n const violations: BoundaryViolation[] = [];\n\n for (const edge of graph.allEdges()) {\n if (edge.from === edge.to) continue;\n const fromCommunity = currentAssignments.get(edge.from);\n const toCommunity = currentAssignments.get(edge.to);\n if (fromCommunity === undefined || toCommunity === undefined) continue;\n if (fromCommunity === toCommunity) continue;\n\n const fromLabel = currentLabels.get(fromCommunity) ?? `community-${fromCommunity}`;\n const toLabel = currentLabels.get(toCommunity) ?? `community-${toCommunity}`;\n\n const historicalCount =\n snapshotsAvailable < MIN_SNAPSHOTS_FOR_CONFIDENCE\n ? -1\n : countHistoricalCrossCluster(historicalAssignments, edge.from, edge.to);\n\n if (historicalCount === -1 || historicalCount <= MEDIUM_SEVERITY_THRESHOLD) {\n violations.push({\n from: {\n symbolId: edge.from,\n communityId: fromCommunity,\n label: fromLabel,\n },\n to: {\n symbolId: edge.to,\n communityId: toCommunity,\n label: toLabel,\n },\n edgeKind: edge.kind,\n historicalEdgesBetweenClusters: historicalCount,\n severity:\n historicalCount === HIGH_SEVERITY_THRESHOLD ? 'high' : 'medium',\n });\n }\n }\n\n violations.sort((a, b) => {\n if (a.severity !== b.severity) return a.severity === 'high' ? -1 : 1;\n const cmp = a.from.symbolId.localeCompare(b.from.symbolId);\n return cmp !== 0 ? cmp : a.to.symbolId.localeCompare(b.to.symbolId);\n });\n\n return {\n violations,\n confidence: confidenceFor(snapshotsAvailable),\n snapshotsAvailable,\n hint: hintFor(snapshotsAvailable),\n };\n }\n}\n\nfunction countHistoricalCrossCluster(\n history: ReadonlyArray<{\n readonly snapshot: CommunitySnapshot;\n readonly assignments: ReadonlyMap<string, number>;\n readonly labels: ReadonlyMap<number, string>;\n }>,\n fromSymbol: string,\n toSymbol: string,\n): number {\n let count = 0;\n for (const snap of history) {\n const fromId = snap.assignments.get(fromSymbol);\n const toId = snap.assignments.get(toSymbol);\n if (fromId === undefined || toId === undefined) continue;\n if (fromId !== toId) count++;\n }\n return count;\n}\n\nfunction assignmentMap(entries: readonly CommunityEntry[]): Map<string, number> {\n const map = new Map<string, number>();\n for (const entry of entries) {\n map.set(entry.symbolId, entry.communityId);\n }\n return map;\n}\n\nfunction labelMap(entries: readonly CommunityEntry[]): Map<number, string> {\n const labels = new Map<number, string>();\n for (const entry of entries) {\n if (!labels.has(entry.communityId)) {\n labels.set(entry.communityId, entry.communityLabel);\n }\n }\n return labels;\n}\n\nfunction confidenceFor(snapshots: number): 'high' | 'medium' | 'low' {\n if (snapshots >= 7) return 'high';\n if (snapshots >= 3) return 'medium';\n return 'low';\n}\n\nfunction hintFor(snapshots: number): string | undefined {\n if (snapshots >= MIN_SNAPSHOTS_FOR_CONFIDENCE) return undefined;\n return 'Insufficient snapshot history — enable `ctxo init` hook or add `ctxo index --check` to CI for boundary-violation signal.';\n}\n","import type { CoChangeEntry, CoChangeMatrix, FileIndex } from '../types.js';\n\nconst MIN_SHARED_COMMITS = 2;\nconst MIN_FREQUENCY = 0.1;\n\nexport function aggregateCoChanges(indices: readonly FileIndex[]): CoChangeMatrix {\n // Step 1: Build commit → files map\n const commitToFiles = new Map<string, Set<string>>();\n const fileCommitCounts = new Map<string, number>();\n\n for (const idx of indices) {\n const file = idx.file;\n let count = 0;\n for (const intent of idx.intent) {\n count++;\n let files = commitToFiles.get(intent.hash);\n if (!files) {\n files = new Set();\n commitToFiles.set(intent.hash, files);\n }\n files.add(file);\n }\n if (count > 0) {\n fileCommitCounts.set(file, count);\n }\n }\n\n // Step 2: Count shared commits per file pair\n const pairCounts = new Map<string, number>();\n for (const files of commitToFiles.values()) {\n if (files.size < 2) continue;\n const sorted = [...files].sort();\n for (let i = 0; i < sorted.length; i++) {\n for (let j = i + 1; j < sorted.length; j++) {\n const key = `${sorted[i]}|${sorted[j]}`;\n pairCounts.set(key, (pairCounts.get(key) ?? 0) + 1);\n }\n }\n }\n\n // Step 3: Calculate frequency and filter\n const entries: CoChangeEntry[] = [];\n for (const [key, sharedCommits] of pairCounts) {\n if (sharedCommits < MIN_SHARED_COMMITS) continue;\n\n const [file1, file2] = key.split('|') as [string, string];\n const count1 = fileCommitCounts.get(file1) ?? 0;\n const count2 = fileCommitCounts.get(file2) ?? 0;\n const minCount = Math.min(count1, count2);\n if (minCount === 0) continue;\n\n const frequency = Math.round((sharedCommits / minCount) * 1000) / 1000;\n if (frequency < MIN_FREQUENCY) continue;\n\n entries.push({ file1, file2, sharedCommits, frequency });\n }\n\n entries.sort((a, b) => b.frequency - a.frequency || b.sharedCommits - a.sharedCommits);\n\n return {\n version: 1,\n timestamp: Math.floor(Date.now() / 1000),\n entries,\n };\n}\n\nexport function loadCoChangeMap(matrix: CoChangeMatrix): Map<string, CoChangeEntry[]> {\n const map = new Map<string, CoChangeEntry[]>();\n for (const entry of matrix.entries) {\n let list1 = map.get(entry.file1);\n if (!list1) { list1 = []; map.set(entry.file1, list1); }\n list1.push(entry);\n\n let list2 = map.get(entry.file2);\n if (!list2) { list2 = []; map.set(entry.file2, list2); }\n list2.push(entry);\n }\n return map;\n}\n"],"mappings":"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","names":["readFileSync","existsSync","mkdirSync","join","log","join","existsSync","readFileSync","mkdirSync","log","log","existsSync","readdirSync","join","resolve","existsSync","readFileSync","join","log","join","existsSync","readFileSync","assignmentMap","labelMap","confidenceFor","hintFor"]}