@ctxo/cli 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/storage/sqlite-storage-adapter.ts","../src/adapters/storage/community-snapshot-writer.ts","../src/core/compare-codepoint.ts","../src/adapters/storage/co-change-reader.ts","../src/core/co-change/co-change-analyzer.ts","../src/core/co-change/co-change-window.ts","../src/core/config/load-config.ts","../src/core/config/config-schema.ts","../src/adapters/git/simple-git-adapter.ts","../src/adapters/storage/contexts-view-reader.ts","../src/core/contexts-view/contexts-view-schema.ts","../src/adapters/stats/session-recorder-adapter.ts","../src/adapters/workspace/single-package-workspace.ts","../src/core/data-coupling/data-kinds.ts","../src/core/test-files/test-file-classifier.ts","../src/core/graph/symbol-graph.ts","../src/core/why-context/revert-detector.ts","../src/core/overlay/snapshot-history.ts","../src/core/overlay/drift-detector.ts","../src/core/blast-radius/blast-radius-calculator.ts","../src/core/blast-radius/co-changed-files.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/search/symbol-tokenizer.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 { compareCodepoint } from '../../core/compare-codepoint.js';\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/** The counter `historyFilename` puts before the extension when two archives would get the same name. */\nconst COLLISION_COUNTER_RE = /^(.*)\\.(\\d+)\\.json$/;\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 against the\n // newest history entry from a different commit (history[0] on a clean\n // history) and must not see current in both positions.\n //\n // Two cases are not archived:\n // - commitSha 'nocommit' (git unavailable at write time). Such snapshots\n // have no stable anchor for drift comparison and would pollute history\n // with indistinguishable entries.\n // - the same commit as the new snapshot. History is a chain of commits,\n // not of runs: the v1 post-commit hook ran `ctxo index --file` once per\n // changed file (the v2 hook runs `ctxo index --commit HEAD` once) and\n // `ctxo watch` re-snapshots after every burst of edits, all at the same\n // HEAD. The new snapshot simply replaces current, so a\n // commit enters history once, when the next commit supersedes it.\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 const existingCommit = archivedCommit(existing);\n if (existingCommit !== undefined && existingCommit !== snapshot.commitSha) {\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 /**\n * Archived snapshots, newest first, at most one per commit. Where several\n * files hold the same commitSha (older versions archived once per\n * `ctxo index --file` run) the newest file wins. `limit` caps the number of\n * commits returned. Read-only: the duplicates stay on disk until the next\n * write rotates them out.\n */\n listHistory(limit?: number): CommunitySnapshot[] {\n if (!existsSync(this.historyDir)) return [];\n const snapshots: CommunitySnapshot[] = [];\n const seen = new Set<string>();\n for (const name of this.historyNames().reverse()) {\n const snap = this.safeParse(join(this.historyDir, name));\n // Defense in depth: filter any legacy `nocommit` entries that slipped\n // into history before the writer skip-archival guard landed.\n const commit = archivedCommit(snap);\n if (!snap || commit === undefined) continue;\n if (seen.has(commit)) continue;\n seen.add(commit);\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 /** The `.json` files in the history folder, oldest first. */\n private historyNames(): string[] {\n return readdirSync(this.historyDir)\n .filter((name) => name.endsWith('.json'))\n .sort(compareHistoryNames);\n }\n\n /**\n * Rotation in two steps. First the duplicates go: every file that is not\n * the newest readable file of its commit (the copies older versions left\n * behind, one per `ctxo index --file` run). Then, by file count, the oldest\n * files beyond `historyLimit`. Copies therefore never use up slots, history\n * keeps each commit at most once, and a commit's newest file is removed\n * only together with its commit, when that is among the oldest beyond the\n * limit. Files that stand for no commit (unreadable, 'nocommit') are not\n * duplicates of anything and age out by count like the rest.\n */\n private rotateHistory(): void {\n if (!existsSync(this.historyDir)) return;\n const seen = new Set<string>();\n const unique: string[] = []; // one file per commit, plus files that stand for none; newest first\n const doomed: string[] = [];\n for (const name of this.historyNames().reverse()) {\n const commit = archivedCommit(this.safeParse(join(this.historyDir, name)));\n if (commit !== undefined) {\n if (seen.has(commit)) {\n doomed.push(name);\n continue;\n }\n seen.add(commit);\n }\n unique.push(name);\n }\n doomed.push(...unique.slice(this.historyLimit));\n\n for (const name of doomed) {\n const target = join(this.historyDir, name);\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 * The commit a snapshot stands for in history, or undefined when it stands for\n * none: unreadable, no commitSha, or 'nocommit'. Those are never archived and\n * never listed.\n */\nfunction archivedCommit(snapshot: CommunitySnapshot | undefined): string | undefined {\n const sha = snapshot?.commitSha;\n return sha && sha !== NOCOMMIT_SHA ? sha : undefined;\n}\n\n/**\n * Orders history file names oldest first. A name starts with the snapshot's\n * timestamp, so comparing names is comparing time, with one exception: when\n * two archives share a timestamp and a commit, `historyFilename` numbers the\n * later one (\"<ts>-<sha>.1.json\") and \"x.1.json\" sorts before \"x.json\". The\n * name without the counter decides first, the counter breaks the tie.\n */\nfunction compareHistoryNames(a: string, b: string): number {\n const [baseA, counterA] = splitCollisionCounter(a);\n const [baseB, counterB] = splitCollisionCounter(b);\n return compareCodepoint(baseA, baseB) || counterA - counterB;\n}\n\nfunction splitCollisionCounter(name: string): [base: string, counter: number] {\n const match = COLLISION_COUNTER_RE.exec(name);\n return match ? [`${match[1]}.json`, Number(match[2])] : [name, 0];\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","/**\n * Compares strings with `<` and `>` (UTF-16 code unit order, which Ctxo calls code point order),\n * never by locale.\n *\n * `localeCompare` depends on the machine's locale (Turkish treats dotted and dotless i specially, for\n * one), so output sorted with it can differ between teammates and CI runners. Every list that ends up\n * in a committed artifact is sorted with this comparator instead.\n */\nexport function compareCodepoint(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n","import { readFileSync, statSync, type Stats } from 'node:fs';\nimport { join } from 'node:path';\nimport { loadCoChangeMap } from '../../core/co-change/co-change-analyzer.js';\nimport { createLogger } from '../../core/logger.js';\nimport { CoChangeMatrixSchema, type CoChangeEntry } from '../../core/types.js';\nimport type { ICoChangePort } from '../../ports/i-co-change-port.js';\n\nconst log = createLogger('ctxo:co-change');\n\ntype CoChangeMap = Map<string, CoChangeEntry[]>;\n\n/**\n * What one version of the file, told apart by mtime and size, held: a usable map, or `undefined` for\n * content that was read but is unusable. It never stands for an I/O failure.\n */\ninterface CachedContent {\n readonly version: string;\n readonly map: CoChangeMap | undefined;\n}\n\n/** The problem is in the content, so nothing changes until the file does. */\nconst UNTIL_REWRITTEN = 'co-change is ignored until ctxo index rewrites it';\n/** The problem may be passing: nothing is remembered about it, and the next call tries again. */\nconst UNTIL_RETRY = 'co-change is ignored for now and the next call tries again';\n\n/**\n * Reads `<ctxoRoot>/index/co-changes.json` for every caller that tiers a blast radius: the MCP handlers,\n * `ctxo blast-radius`, the safe-edit gate and `ctxo gate --preview`.\n *\n * The file is validated with `CoChangeMatrixSchema` and turned into a lookup by file. The result is cached\n * until the file's mtime or size changes, so a long-running MCP server picks up a matrix that `ctxo index`\n * or the post-commit hook rewrites, without parsing it again on every call. Size joins the mtime in the\n * key because a coarse file system clock can give two different writes the same mtime.\n *\n * It never throws, and every caller works from the dependency graph alone when it returns `undefined`:\n * - A missing file (ENOENT) is the normal state of a project without history: silent.\n * - Content that is not JSON or not a matrix warns once and is cached like good content, since it stays\n * the same answer until the file changes.\n * - Any other trouble (a stat error that is not ENOENT, a read that fails) says nothing about the content,\n * so it is never cached: the next call tries again. It warns once for as long as the same problem lasts.\n *\n * The warning is deduplicated by its own key (`lastWarning`), not by the data cache, because the cache\n * deliberately forgets failures that are worth retrying.\n *\n * The returned map is shared between calls and frozen (every list and every entry): treat it as read-only.\n */\nexport class CoChangeReader implements ICoChangePort {\n private readonly filePath: string;\n private cached: CachedContent | undefined;\n /** What the last warning was about, so one problem is reported once. */\n private lastWarning: string | undefined;\n\n constructor(ctxoRoot: string) {\n this.filePath = join(ctxoRoot, 'index', 'co-changes.json');\n }\n\n readCoChangeMap(): CoChangeMap | undefined {\n let stats: Stats;\n try {\n stats = statSync(this.filePath);\n } catch (err) {\n // Whatever was cached may be out of date once the file cannot be seen, so it goes in every case.\n this.cached = undefined;\n const { code, message } = err as NodeJS.ErrnoException;\n if (code === 'ENOENT') {\n this.lastWarning = undefined; // a clean slate: a bad file that comes back is worth a new warning\n } else {\n this.warnOnce(`stat ${code ?? message}`, `cannot be inspected (${message})`, UNTIL_RETRY);\n }\n return undefined;\n }\n\n // The stat is taken before the read: a file replaced in between is cached under the older key, so the\n // next call sees a different stat and reads again. The other order could keep stale content under a new key.\n const version = `${stats.mtimeMs}:${stats.size}`;\n if (this.cached?.version === version) return this.cached.map;\n return this.load(version);\n }\n\n private load(version: string): CoChangeMap | undefined {\n let raw: string;\n try {\n raw = readFileSync(this.filePath, 'utf-8');\n } catch (err) {\n this.cached = undefined;\n this.warnOnce(`read ${version}`, `cannot be read (${(err as Error).message})`, UNTIL_RETRY);\n return undefined;\n }\n\n const map = this.parse(raw, version);\n this.cached = { version, map };\n if (map) this.lastWarning = undefined;\n return map;\n }\n\n private parse(raw: string, version: string): CoChangeMap | undefined {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (err) {\n return this.unusable(version, `is not valid JSON (${(err as Error).message})`);\n }\n\n const parsed = CoChangeMatrixSchema.safeParse(data);\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const where = issue && issue.path.length > 0 ? `${issue.path.map(String).join('.')}: ` : '';\n return this.unusable(version, `is not a valid co-change matrix (${where}${issue?.message ?? 'unknown problem'})`);\n }\n return freezeEntries(loadCoChangeMap(parsed.data));\n }\n\n private unusable(version: string, problem: string): undefined {\n this.warnOnce(`content ${version}`, problem, UNTIL_REWRITTEN);\n return undefined;\n }\n\n private warnOnce(key: string, problem: string, consequence: string): void {\n if (this.lastWarning === key) return;\n this.lastWarning = key;\n log.warn(`${this.filePath} ${problem}; ${consequence}`);\n }\n}\n\n/**\n * Freezes every list and every entry of the map. It is shared by all callers, so an in-place edit (a sort,\n * say) would silently change what every other caller sees; frozen, the first attempt throws instead. The\n * map itself stays a plain Map.\n */\nfunction freezeEntries(map: CoChangeMap): CoChangeMap {\n for (const list of map.values()) {\n for (const entry of list) Object.freeze(entry);\n Object.freeze(list);\n }\n return map;\n}\n","import { compareCodepoint } from '../compare-codepoint.js';\nimport type { CanonicalCommit } from '../history/change-log.js';\nimport type { CoChangeEntry, CoChangeMatrix, CoChangeWindowMeta } from '../types.js';\n\nconst MIN_FREQUENCY = 0.1;\n\nexport interface CoChangeBuildOptions {\n /**\n * True for the files that take part: the currently indexed files and the tracked files matched by\n * `cochange.include`, minus those matched by `cochange.exclude`. Deleted files are not included, so\n * they drop out of the matrix.\n */\n readonly isIncluded: (path: string) => boolean;\n /** Recorded in the window metadata only; `selectWindow` has already applied it. */\n readonly maxCommits: number | 'all';\n /** Recorded in the window metadata only; the caller turned it into a cutoff for `selectWindow`. */\n readonly since?: string;\n /** Short SHA of HEAD, recorded in the window metadata. */\n readonly head: string;\n readonly maxFilesPerCommit: number;\n readonly minShared: number;\n /** Stored as is: the builder reads no clock. */\n readonly timestamp: number;\n}\n\n/**\n * Builds the co-change matrix of one window of commits: how often pairs of files changed in the same\n * commit.\n *\n * `windowed` is the output of `selectWindow`, so `window.commitsScanned` is its length. Per commit:\n * the files are filtered with `isIncluded` first; a commit left with no file is ignored; a commit\n * left with more than `maxFilesPerCommit` files is skipped and counted in `massSkipped` (the excluded\n * files never count toward that limit); every other commit counts as used and adds one to the history\n * of each of its files and one to the shared count of each pair of them. A pair becomes an entry when\n * it was shared at least `minShared` times and `frequency` (shared over the smaller of the two file\n * histories, rounded to three decimals) is at least 0.1.\n *\n * Entries are sorted by frequency, then shared count (both descending), then file names in code point\n * order. The result depends only on the inputs: the same window gives byte-identical JSON.\n */\nexport function buildCoChangeMatrix(\n windowed: readonly CanonicalCommit[],\n options: CoChangeBuildOptions,\n): CoChangeMatrix {\n const commitsPerFile = new Map<string, number>();\n const sharedPerPair = new Map<string, Map<string, number>>();\n let commitsUsed = 0;\n let massSkipped = 0;\n\n for (const commit of windowed) {\n const files = includedFiles(commit, options.isIncluded);\n if (files.length === 0) continue;\n if (files.length > options.maxFilesPerCommit) {\n massSkipped++;\n continue;\n }\n\n commitsUsed++;\n for (const file of files) {\n commitsPerFile.set(file, (commitsPerFile.get(file) ?? 0) + 1);\n }\n // `files` is sorted, so every pair is visited once with the smaller name first.\n for (let i = 0; i < files.length - 1; i++) {\n const file1 = files[i]!;\n let partners = sharedPerPair.get(file1);\n if (!partners) {\n partners = new Map();\n sharedPerPair.set(file1, partners);\n }\n for (let j = i + 1; j < files.length; j++) {\n const file2 = files[j]!;\n partners.set(file2, (partners.get(file2) ?? 0) + 1);\n }\n }\n }\n\n const entries: CoChangeEntry[] = [];\n for (const [file1, partners] of sharedPerPair) {\n for (const [file2, sharedCommits] of partners) {\n if (sharedCommits < options.minShared) continue;\n\n // Both files were counted in every commit that counted toward the pair.\n const smallerHistory = Math.min(commitsPerFile.get(file1)!, commitsPerFile.get(file2)!);\n const frequency = Math.round((sharedCommits / smallerHistory) * 1000) / 1000;\n if (frequency < MIN_FREQUENCY) continue;\n\n entries.push({ file1, file2, sharedCommits, frequency });\n }\n }\n entries.sort(compareEntries);\n\n const window: CoChangeWindowMeta = {\n maxCommits: options.maxCommits,\n ...(options.since !== undefined ? { since: options.since } : {}),\n head: options.head,\n commitsScanned: windowed.length,\n commitsUsed,\n massSkipped,\n maxFilesPerCommit: options.maxFilesPerCommit,\n minShared: options.minShared,\n };\n\n return { version: 1, timestamp: options.timestamp, window, entries };\n}\n\n/**\n * The included files of a commit, de-duplicated and in code point order. A canonical commit already\n * arrives that way; normalising again keeps the pair order independent of the caller.\n */\nfunction includedFiles(commit: CanonicalCommit, isIncluded: (path: string) => boolean): string[] {\n const included = new Set<string>();\n for (const file of commit.files) {\n if (isIncluded(file)) included.add(file);\n }\n return [...included].sort(compareCodepoint);\n}\n\nfunction compareEntries(a: CoChangeEntry, b: CoChangeEntry): number {\n return (\n b.frequency - a.frequency ||\n b.sharedCommits - a.sharedCommits ||\n compareCodepoint(a.file1, b.file1) ||\n compareCodepoint(a.file2, b.file2)\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","import type { CanonicalCommit } from '../history/change-log.js';\n\n/** A duration such as `12m`: the unit is days, weeks, calendar months or calendar years. */\nexport interface CoChangeSince {\n readonly amount: number;\n readonly unit: 'd' | 'w' | 'm' | 'y';\n}\n\nconst SINCE_PATTERN = /^(\\d+)([dwmy])$/;\nconst SECONDS_PER_DAY = 86_400;\n\n/**\n * Parses a `cochange.since` duration: digits followed by `d`, `w`, `m` or `y` (for example `30d`,\n * `12m`). Returns `undefined` for anything else, and for an amount too large to be an exact integer.\n * A zero amount is valid; it keeps only the commits made at or after HEAD's committer date.\n */\nexport function parseCoChangeSince(value: string): CoChangeSince | undefined {\n const match = SINCE_PATTERN.exec(value);\n if (!match) return undefined;\n const amount = Number.parseInt(match[1]!, 10);\n if (!Number.isSafeInteger(amount)) return undefined;\n return { amount, unit: match[2] as CoChangeSince['unit'] };\n}\n\n/**\n * The unix-second instant `since` before HEAD's committer date. Months and years are calendar\n * spans in UTC: the day of the month is kept when the target month has it and otherwise clamps to\n * the last day (31 March minus one month is 28 February, or 29 in a leap year), and the time of day\n * is kept. Days and weeks are fixed 24 hour spans. The wall clock and the local time zone play no\n * part, so one HEAD always gives one cutoff.\n *\n * Returns `-Infinity` (no restriction) when the span reaches beyond every date a JavaScript Date\n * can hold, or when `headCommittedAt` is not a finite number.\n */\nexport function sinceCutoff(headCommittedAt: number, since: CoChangeSince): number {\n const cutoff = subtractSince(headCommittedAt, since);\n return Number.isFinite(cutoff) ? cutoff : Number.NEGATIVE_INFINITY;\n}\n\nfunction subtractSince(headCommittedAt: number, since: CoChangeSince): number {\n switch (since.unit) {\n case 'd':\n return headCommittedAt - since.amount * SECONDS_PER_DAY;\n case 'w':\n return headCommittedAt - since.amount * 7 * SECONDS_PER_DAY;\n case 'm':\n return subtractUtcMonths(headCommittedAt, since.amount);\n case 'y':\n return subtractUtcMonths(headCommittedAt, since.amount * 12);\n }\n}\n\nfunction subtractUtcMonths(epochSeconds: number, months: number): number {\n const date = new Date(epochSeconds * 1000);\n const dayOfMonth = date.getUTCDate();\n // Park on the 1st so that moving the month cannot overflow into the one after it.\n date.setUTCDate(1);\n date.setUTCMonth(date.getUTCMonth() - months);\n date.setUTCDate(Math.min(dayOfMonth, daysInUtcMonth(date)));\n return date.getTime() / 1000;\n}\n\nfunction daysInUtcMonth(date: Date): number {\n const lastDay = new Date(date.getTime());\n // Day 0 of the next month is the last day of this one.\n lastDay.setUTCMonth(lastDay.getUTCMonth() + 1, 0);\n return lastDay.getUTCDate();\n}\n\n/**\n * Picks the commits a co-change matrix is built from. `commits` must be newest first, as the change\n * log returns them.\n *\n * `maxCommits` applies first and takes the newest N in log order ('all' takes every commit). `cutoff`\n * (a unix second from `sinceCutoff`) then keeps the commits whose committer date is at or after it.\n * The cutoff is a plain filter: a commit with a skewed older date is dropped without hiding the\n * newer commits behind it.\n *\n * A `maxCommits` below 1 or NaN selects nothing and a fractional value rounds down, so a bad value\n * can only narrow the window, never widen it.\n */\nexport function selectWindow(\n commits: readonly CanonicalCommit[],\n maxCommits: number | 'all',\n cutoff?: number,\n): CanonicalCommit[] {\n const newest = maxCommits === 'all' ? [...commits] : commits.slice(0, wholeCommitCount(maxCommits));\n return cutoff === undefined ? newest : newest.filter((commit) => commit.committedAt >= cutoff);\n}\n\nfunction wholeCommitCount(maxCommits: number): number {\n return maxCommits >= 1 ? Math.floor(maxCommits) : 0;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport picomatch from 'picomatch';\nimport { parseCoChangeSince } from '../co-change/co-change-window.js';\nimport { createLogger } from '../logger.js';\nimport type { CommunityDetectorOptions } from '../overlay/community-detector.js';\nimport {\n CtxoConfigSchema,\n DEFAULT_COCHANGE_MAX_COMMITS,\n DEFAULT_COCHANGE_MAX_FILES_PER_COMMIT,\n DEFAULT_COCHANGE_MIN_SHARED,\n DEFAULT_CONFIG,\n DEFAULT_TECHNICAL_TERMS,\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 validateGlobs(result.data.index?.tests, 'index.tests', invalidGlobs);\n validateGlobs(result.data.cochange?.include, 'cochange.include', invalidGlobs);\n validateGlobs(result.data.cochange?.exclude, 'cochange.exclude', invalidGlobs);\n validateGlobs(result.data.contextsView?.exclude, 'contextsView.exclude', invalidGlobs);\n validateGlobs(result.data.contextsView?.hubs?.paths, 'contextsView.hubs.paths', 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\n/** True when facts extraction is requested (`index.facts: true`). */\nexport function factsRequested(config: CtxoConfig): boolean {\n return config.index?.facts === true;\n}\n\nexport function indexTestPatterns(config: CtxoConfig): string[] {\n return (config.index?.tests ?? []).filter(isValidGlob);\n}\n\n/**\n * The `communities` section as detector options. Only usable values are passed on, so the detector\n * keeps its own defaults (1.0, 3, 25) for anything absent or invalid.\n */\nexport function communityDetectorOptions(config: CtxoConfig): CommunityDetectorOptions {\n const section = config.communities;\n const resolution = section?.resolution;\n const minBridged = section?.godNodeMinBridged;\n const maxResults = section?.godNodeMaxResults;\n return {\n ...(typeof resolution === 'number' && Number.isFinite(resolution) && resolution > 0 ? { resolution } : {}),\n ...(typeof minBridged === 'number' && Number.isInteger(minBridged) && minBridged >= 1\n ? { godNodeMinBridged: minBridged }\n : {}),\n ...(typeof maxResults === 'number' && Number.isInteger(maxResults) && maxResults >= 0\n ? { godNodeMaxResults: maxResults }\n : {}),\n };\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/** The co-change settings with every default applied; what `buildCoChangeMatrix` and its callers use. */\nexport interface ResolvedCoChangeConfig {\n readonly maxCommits: number | 'all';\n readonly since?: string;\n readonly maxFilesPerCommit: number;\n readonly minShared: number;\n readonly include: readonly string[];\n readonly exclude: readonly string[];\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 1;\n}\n\nfunction positiveIntegerOr(value: unknown, fallback: number): number {\n return isPositiveInteger(value) ? value : fallback;\n}\n\nfunction validMaxCommits(value: unknown): number | 'all' | undefined {\n if (value === 'all') return 'all';\n return isPositiveInteger(value) ? value : undefined;\n}\n\nfunction validSince(value: unknown): string | undefined {\n return typeof value === 'string' && parseCoChangeSince(value) !== undefined ? value : undefined;\n}\n\n/**\n * Resolves the `cochange` section. Each setting comes from the first source that has a usable value:\n * the CLI override (`--cochange-commits`, `--cochange-since`), then `.ctxo/config.yaml`, then the\n * built-in default (2000 commits, 30 files per commit, 2 shared commits, no `since`).\n *\n * A value the schema would reject is skipped rather than trusted, and so is an invalid glob. That\n * matters for a value that did not come through `loadConfig`, and above all for `maxCommits`, where\n * a zero or negative number would otherwise turn into an unbounded window.\n */\nexport function resolveCoChangeConfig(\n config: CtxoConfig,\n overrides: { maxCommits?: number | 'all'; since?: string } = {},\n): ResolvedCoChangeConfig {\n const section = config.cochange;\n const since = validSince(overrides.since) ?? validSince(section?.since);\n return {\n maxCommits:\n validMaxCommits(overrides.maxCommits) ??\n validMaxCommits(section?.maxCommits) ??\n DEFAULT_COCHANGE_MAX_COMMITS,\n ...(since !== undefined ? { since } : {}),\n maxFilesPerCommit: positiveIntegerOr(section?.maxFilesPerCommit, DEFAULT_COCHANGE_MAX_FILES_PER_COMMIT),\n minShared: positiveIntegerOr(section?.minShared, DEFAULT_COCHANGE_MIN_SHARED),\n include: (section?.include ?? []).filter(isValidGlob),\n exclude: (section?.exclude ?? []).filter(isValidGlob),\n };\n}\n\n/** The `contextsView` section with every default applied; what the file graph and clustering use. */\nexport interface ResolvedContextsViewConfig {\n readonly weights: { readonly dependency: number; readonly coChange: number; readonly data: number };\n readonly exclude: readonly string[];\n readonly hubs: {\n readonly auto: boolean;\n readonly topPercent: number;\n readonly minDependents: number;\n readonly paths: readonly string[];\n };\n readonly resolution: number;\n readonly minCandidateFiles: number;\n readonly technicalTerms: readonly string[];\n}\n\nfunction nonNegativeOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\nfunction positiveOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\n/**\n * Resolves the `contextsView` section (defaults: weights 1, 1 and data 0, hubs auto with top 1% and at least\n * 10 dependents, resolution 1, minCandidateFiles 3). Unusable values and invalid globs are skipped.\n */\nexport function resolveContextsViewConfig(config: CtxoConfig): ResolvedContextsViewConfig {\n const section = config.contextsView;\n const hubs = section?.hubs;\n const topPercent = hubs?.topPercent;\n return {\n weights: {\n dependency: nonNegativeOr(section?.weights?.dependency, 1),\n coChange: nonNegativeOr(section?.weights?.coChange, 1),\n data: nonNegativeOr(section?.weights?.data, 0),\n },\n exclude: (section?.exclude ?? []).filter(isValidGlob),\n hubs: {\n auto: hubs?.auto !== false,\n topPercent: typeof topPercent === 'number' && topPercent > 0 && topPercent <= 100 ? topPercent : 1,\n minDependents: positiveIntegerOr(hubs?.minDependents, 10),\n paths: (hubs?.paths ?? []).filter(isValidGlob),\n },\n resolution: positiveOr(section?.resolution, 1),\n minCandidateFiles: positiveIntegerOr(section?.minCandidateFiles, 3),\n technicalTerms: section?.technicalTerms ? [...section.technicalTerms] : [...DEFAULT_TECHNICAL_TERMS],\n };\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';\nimport { parseCoChangeSince } from '../co-change/co-change-window.js';\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 facts: z.boolean().optional(),\n tests: 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 CoChangeConfigSchema = z\n .object({\n /**\n * Size of the co-change window: the last N non-merge commits from HEAD, or \"all\".\n * Default: 2000. Independent of `ctxo index --max-history`, which only sets how much\n * per-file commit history is kept.\n */\n maxCommits: z\n .union([z.number().int().min(1), z.literal('all')], { error: 'must be a positive integer or \"all\"' })\n .optional(),\n /**\n * Narrows the window to a duration counted back from HEAD's committer date (never the wall\n * clock): `30d`, `2w`, `12m` or `1y`. Applies together with `maxCommits`.\n */\n since: z\n .string()\n .refine((value) => parseCoChangeSince(value) !== undefined, {\n error: 'must be a duration such as 30d, 2w, 12m or 1y',\n })\n .optional(),\n /** Commits touching more analysed files than this are skipped as mass changes. Default: 30. */\n maxFilesPerCommit: z.number().int().min(1).optional(),\n /** A file pair becomes an entry only after this many shared commits. Default: 2. */\n minShared: z.number().int().min(1).optional(),\n /** Extra tracked files to analyse that no language plugin indexes, such as SQL scripts (globs). */\n include: z.array(z.string().min(1)).optional(),\n /** Indexed files to leave out of the analysis (globs). */\n exclude: z.array(z.string().min(1)).optional(),\n })\n .strict();\n\nexport const CommunitiesConfigSchema = z\n .object({\n /** Louvain resolution: higher gives more, smaller communities. Default: 1.0. */\n resolution: z.number().positive().optional(),\n /** Minimum number of communities a symbol must bridge to count as a god node. Default: 3. */\n godNodeMinBridged: z.number().int().min(1).optional(),\n /** Maximum number of god nodes reported. Default: 25. */\n godNodeMaxResults: z.number().int().min(0).optional(),\n })\n .strict();\n\nexport const ContextsViewConfigSchema = z\n .object({\n /** Blend of the file-edge signals. Defaults: dependency 1.0, coChange 1.0, data 0 (shared tables are a finding, not a clustering weight). */\n weights: z\n .object({\n dependency: z.number().min(0).optional(),\n coChange: z.number().min(0).optional(),\n data: z.number().min(0).optional(),\n })\n .strict()\n .optional(),\n /** Files to leave out of the file graph (globs). */\n exclude: z.array(z.string().min(1)).optional(),\n hubs: z\n .object({\n /** Detect hubs automatically (fan-in and god nodes). Default: true. */\n auto: z.boolean().optional(),\n /** A file is a fan-in hub when it is in the top `topPercent`% by dependents. Default: 1. */\n topPercent: z.number().positive().max(100).optional(),\n /** Minimum distinct dependent files for a fan-in hub. Default: 10. */\n minDependents: z.number().int().min(1).optional(),\n /** Files that lose all their edges (globs). */\n paths: z.array(z.string().min(1)).optional(),\n })\n .strict()\n .optional(),\n /** Louvain resolution for the file graph. Default: 1.0. */\n resolution: z.number().positive().optional(),\n /** Candidates with fewer files fold into a neighbour. Default: 3. */\n minCandidateFiles: z.number().int().min(1).optional(),\n /** Words dropped from candidate terms; replaces the default list. */\n technicalTerms: z.array(z.string().min(1)).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 cochange: CoChangeConfigSchema.optional(),\n communities: CommunitiesConfigSchema.optional(),\n contextsView: ContextsViewConfigSchema.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 CoChangeConfig = z.infer<typeof CoChangeConfigSchema>;\nexport type CommunitiesConfig = z.infer<typeof CommunitiesConfigSchema>;\nexport type ContextsViewConfig = z.infer<typeof ContextsViewConfigSchema>;\nexport type CtxoConfig = z.infer<typeof CtxoConfigSchema>;\n\nexport const DEFAULT_WATCH_SNAPSHOT_MIN_FILE_CHANGES = 5;\nexport const DEFAULT_COCHANGE_MAX_COMMITS = 2000;\nexport const DEFAULT_COCHANGE_MAX_FILES_PER_COMMIT = 30;\nexport const DEFAULT_COCHANGE_MIN_SHARED = 2;\nexport const DEFAULT_TECHNICAL_TERMS: readonly string[] = [\n 'Service',\n 'Repository',\n 'Controller',\n 'Handler',\n 'Manager',\n 'Dto',\n 'Impl',\n 'Async',\n];\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 cochange: {\n maxCommits: DEFAULT_COCHANGE_MAX_COMMITS,\n maxFilesPerCommit: DEFAULT_COCHANGE_MAX_FILES_PER_COMMIT,\n minShared: DEFAULT_COCHANGE_MIN_SHARED,\n include: [],\n exclude: [],\n },\n};\n","import { spawnSync } from 'node:child_process';\nimport { simpleGit, type SimpleGit } from 'simple-git';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type {\n CommitRecord,\n ChurnData,\n ChangeLog,\n CommitChangeSet,\n FileChange,\n FileChangeStatus,\n} from '../../core/types.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:git');\n\nconst NUL = '\\x00';\nconst RECORD_SEPARATOR = '\\x1e';\nconst FIELD_SEPARATOR = '\\x1f';\n\n/**\n * One record per commit: a record separator, then hash, author date (ISO), committer unix\n * time, author name and subject, joined by field separators.\n */\nconst CHANGE_LOG_FORMAT = '--format=%x1e%H%x1f%aI%x1f%ct%x1f%an%x1f%s';\n\n/**\n * Config overrides that keep git's output parseable whatever the user has configured.\n * - core.quotepath=off: paths come out verbatim instead of quoted and octal-escaped.\n * - log.showSignature=false: when on, gpg verification text (CRLF on Windows) is written\n * into the log stream ahead of each commit.\n */\nconst STABLE_OUTPUT_CONFIG: readonly string[] = [\n '-c', 'core.quotepath=off',\n '-c', 'log.showSignature=false',\n];\n\nfunction toForwardSlashes(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\nfunction isFileChangeStatus(value: string): value is FileChangeStatus {\n return value === 'A' || value === 'M' || value === 'D' || value === 'R' || value === 'C' || value === 'T';\n}\n\n/**\n * Parses the NUL-separated tokens of `--name-status -z`: `<status> <path>`, or for renames and\n * copies `<status><score> <old path> <new path>` (for example `R100`). Only status tokens are\n * trimmed, because git puts a newline between a commit header and its first status; paths are\n * never altered beyond separator normalisation. Statuses outside FileChangeStatus (for example\n * U) are skipped, and a truncated trailing entry is dropped.\n */\nfunction parseNameStatus(tokens: readonly string[]): FileChange[] {\n const changes: FileChange[] = [];\n let i = 0;\n while (i < tokens.length) {\n const kind = (tokens[i] ?? '').trim().charAt(0);\n if (kind === '') {\n i += 1; // terminator after the last path, or a bare newline\n } else if (kind === 'R' || kind === 'C') {\n const oldPath = tokens[i + 1];\n const path = tokens[i + 2];\n i += 3;\n if (oldPath !== undefined && path !== undefined) {\n changes.push({ status: kind, path: toForwardSlashes(path), oldPath: toForwardSlashes(oldPath) });\n }\n } else {\n const path = tokens[i + 1];\n i += 2;\n if (path !== undefined && isFileChangeStatus(kind)) {\n changes.push({ status: kind, path: toForwardSlashes(path) });\n }\n }\n }\n return changes;\n}\n\n/**\n * Parses `git ls-files -t -z`: records of `<tag> <path>`, where the tag is H for an ordinary tracked file\n * and S for a skip-worktree one (others mark unmerged, removed, modified or untracked paths). Returns the\n * paths tagged S.\n */\nfunction parseSkipWorktree(raw: string): Set<string> {\n const paths = new Set<string>();\n for (const record of raw.split(NUL)) {\n if (record.length > 2 && record.charAt(0) === 'S' && record.charAt(1) === ' ') {\n paths.add(toForwardSlashes(record.slice(2)));\n }\n }\n return paths;\n}\n\n/** Parses `git log` output produced with CHANGE_LOG_FORMAT, `--name-status` and `-z`. */\nfunction parseChangeLog(raw: string): CommitChangeSet[] {\n const commits: CommitChangeSet[] = [];\n for (const record of raw.split(RECORD_SEPARATOR)) {\n // \"<header>\\0\\n<status>\\0<path>\\0...\": the header ends at the first NUL. Text before the\n // first separator, or a record without a complete header, is skipped.\n const [header = '', ...tokens] = record.split(NUL);\n const [hash, date, time, author, ...subject] = header.split(FIELD_SEPARATOR);\n const committedAt = Number.parseInt(time ?? '', 10);\n if (!hash || date === undefined || author === undefined || Number.isNaN(committedAt)) continue;\n\n commits.push({\n hash,\n message: subject.join(FIELD_SEPARATOR).trim(),\n date,\n author,\n committedAt,\n changes: parseNameStatus(tokens),\n });\n }\n return commits;\n}\n\nexport class SimpleGitAdapter implements IGitPort {\n private readonly git: SimpleGit;\n private readonly projectRoot: string;\n\n constructor(projectRoot: string) {\n this.projectRoot = projectRoot;\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 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 async getChangeLog(maxCommits: number | 'all'): Promise<ChangeLog | undefined> {\n try {\n // HEAD gets its own call because the log below skips merge commits and HEAD may be one.\n // It is read first on purpose: if a commit lands between the two calls, `head` can only\n // end up older than the log (it looks stale), never newer (it would look current while\n // missing a commit).\n const headRaw = await this.git.raw([...STABLE_OUTPUT_CONFIG, 'log', '-1', '--format=%h%x1f%ct', 'HEAD']);\n const [head, headTime] = headRaw.trim().split(FIELD_SEPARATOR);\n const headCommittedAt = Number.parseInt(headTime ?? '', 10);\n if (!head || Number.isNaN(headCommittedAt)) {\n throw new Error(`unexpected HEAD description ${JSON.stringify(headRaw)}`);\n }\n\n const args = [...STABLE_OUTPUT_CONFIG, 'log', '--no-merges', '-M', '--name-status', '-z', CHANGE_LOG_FORMAT];\n if (maxCommits !== 'all') args.push(`--max-count=${maxCommits}`);\n args.push('HEAD');\n\n return { head, headCommittedAt, commits: parseChangeLog(await this.git.raw(args)) };\n } catch (err) {\n log.error(`Failed to read change log: ${(err as Error).message}`);\n return undefined;\n }\n }\n\n async getSkipWorktreePaths(): Promise<ReadonlySet<string> | undefined> {\n try {\n // One listing of the index with status tags; -z keeps unusual names verbatim.\n return parseSkipWorktree(await this.git.raw([...STABLE_OUTPUT_CONFIG, 'ls-files', '-t', '-z']));\n } catch (err) {\n log.error(`Failed to list skip-worktree paths: ${(err as Error).message}`);\n return undefined;\n }\n }\n\n async getIgnoredPaths(paths: readonly string[]): Promise<ReadonlySet<string> | undefined> {\n if (paths.length === 0) return new Set();\n\n // The paths go in on stdin, NUL separated, so a long list cannot overflow the command line and unusual\n // names arrive verbatim. Exit code 0: some of them are ignored; 1: none is; anything else is a failure.\n const result = spawnSync('git', [...STABLE_OUTPUT_CONFIG, 'check-ignore', '-z', '--stdin'], {\n cwd: this.projectRoot,\n input: paths.map((path) => `${path}${NUL}`).join(''),\n encoding: 'utf-8',\n maxBuffer: 64 * 1024 * 1024,\n });\n if (result.error === undefined && result.status === 1) return new Set();\n if (result.error !== undefined || result.status !== 0) {\n const reason = (result.error?.message ?? result.stderr ?? '').replace(/\\s+/g, ' ').trim();\n log.error(`Failed to check ignored paths: ${reason}`);\n return undefined;\n }\n return new Set(\n result.stdout\n .split(NUL)\n .filter((path) => path.length > 0)\n .map(toForwardSlashes),\n );\n }\n\n async getCommitFileChanges(rev: string): Promise<FileChange[] | undefined> {\n // A revision is never an option; refuse anything that git would parse as one.\n if (rev.startsWith('-')) {\n log.error(`Refusing to read file changes for option-like revision ${rev}`);\n return undefined;\n }\n\n try {\n const raw = await this.git.raw([\n ...STABLE_OUTPUT_CONFIG,\n 'diff-tree', '--root', '--no-commit-id', '-r', '-M', '--name-status', '-z', rev,\n ]);\n return parseNameStatus(raw.split(NUL));\n } catch (err) {\n log.error(`Failed to read file changes of ${rev}: ${(err as Error).message}`);\n return undefined;\n }\n }\n}\n","import { readFileSync, statSync, type Stats } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ContextsView } from '../../core/contexts-view/contexts-view-builder.js';\nimport { ContextsViewSchema } from '../../core/contexts-view/contexts-view-schema.js';\nimport { createLogger } from '../../core/logger.js';\nimport type { IContextsViewPort } from '../../ports/i-contexts-view-port.js';\n\nconst log = createLogger('ctxo:contexts-view');\n\n/** What one version of the file, told apart by mtime and size, held: a view, or `undefined` for unusable content. */\ninterface CachedContent {\n readonly version: string;\n readonly view: ContextsView | undefined;\n}\n\nconst UNTIL_REWRITTEN = 'the contexts view is ignored until ctxo index rewrites it';\nconst UNTIL_RETRY = 'the contexts view is ignored for now and the next call tries again';\n\n/**\n * Freezes the view and everything in it. It is shared by all callers, so an in-place edit (a sort, say)\n * would silently change what every other caller sees; frozen, the first attempt throws instead.\n */\nfunction deepFreeze<T>(value: T): T {\n if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const child of Object.values(value)) deepFreeze(child);\n }\n return value;\n}\n\n/**\n * Reads `<ctxoRoot>/index/contexts-view.json`, validates it with `ContextsViewSchema` and caches the result\n * until the file's mtime or size changes. It never throws; `undefined` means there is no usable view.\n *\n * - A missing file is the normal state of a project that was never indexed: silent.\n * - Content that is not JSON (a merge conflict, a hand edit) or not a view warns once per file version and\n * is cached like good content, since the answer stays the same until the file changes.\n * - Any other trouble (a stat error that is not ENOENT, a failed read) is never cached; it warns once for\n * as long as the same problem lasts.\n */\nexport class ContextsViewReader implements IContextsViewPort {\n private readonly filePath: string;\n private cached: CachedContent | undefined;\n private lastWarning: string | undefined;\n\n constructor(ctxoRoot: string) {\n this.filePath = join(ctxoRoot, 'index', 'contexts-view.json');\n }\n\n readContextsView(): ContextsView | undefined {\n let stats: Stats;\n try {\n stats = statSync(this.filePath);\n } catch (err) {\n this.cached = undefined;\n const { code, message } = err as NodeJS.ErrnoException;\n if (code === 'ENOENT') {\n this.lastWarning = undefined;\n } else {\n this.warnOnce(`stat ${code ?? message}`, `cannot be inspected (${message})`, UNTIL_RETRY);\n }\n return undefined;\n }\n\n // The stat is taken before the read, so a file replaced in between is cached under the older key and\n // the next call reads again.\n // The inode joins mtime and size because atomicWrite renames in a fresh file, so a same-size rewrite in\n // one mtime tick still changes the key.\n const version = `${stats.mtimeMs}:${stats.size}:${stats.ino}`;\n if (this.cached?.version === version) return this.cached.view;\n return this.load(version);\n }\n\n private load(version: string): ContextsView | undefined {\n let raw: string;\n try {\n raw = readFileSync(this.filePath, 'utf-8');\n } catch (err) {\n this.cached = undefined;\n this.warnOnce(`read ${version}`, `cannot be read (${(err as Error).message})`, UNTIL_RETRY);\n return undefined;\n }\n\n const view = this.parse(raw, version);\n this.cached = { version, view };\n if (view) this.lastWarning = undefined;\n return view;\n }\n\n private parse(raw: string, version: string): ContextsView | undefined {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (err) {\n this.warnOnce(`content ${version}`, `is not valid JSON (${(err as Error).message})`, UNTIL_REWRITTEN);\n return undefined;\n }\n const parsed = ContextsViewSchema.safeParse(data);\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const where = issue && issue.path.length > 0 ? `${issue.path.map(String).join('.')}: ` : '';\n this.warnOnce(\n `content ${version}`,\n `is not a valid contexts view (${where}${issue?.message ?? 'unknown problem'})`,\n UNTIL_REWRITTEN,\n );\n return undefined;\n }\n return deepFreeze(parsed.data);\n }\n\n private warnOnce(key: string, problem: string, consequence: string): void {\n if (this.lastWarning === key) return;\n this.lastWarning = key;\n log.warn(`${this.filePath} ${problem}; ${consequence}`);\n }\n}\n","import { DATA_RESOURCE_KINDS } from '@ctxo/plugin-api';\nimport { z } from 'zod';\n\nimport { EdgeKindSchema } from '../types.js';\nimport type { ContextsView } from './contexts-view-builder.js';\n\nconst count = z.number().int().nonnegative();\n\nconst SettingsSchema = z.object({\n // A PR 1A artifact has no data weight; it was 0 then.\n weights: z.object({ dependency: z.number(), coChange: z.number(), data: z.number().default(0) }),\n exclude: z.array(z.string()),\n hubs: z.object({\n auto: z.boolean(),\n topPercent: z.number(),\n minDependents: z.number(),\n paths: z.array(z.string()),\n }),\n resolution: z.number(),\n minCandidateFiles: z.number(),\n technicalTerms: z.array(z.string()),\n});\n\nconst DataAccessSchema = z.object({\n candidate: z.number().int(),\n modes: z.array(z.enum(['reads', 'writes'])),\n});\n\nconst SharedTableSchema = z.object({\n name: z.string(),\n kind: z.enum(DATA_RESOURCE_KINDS).optional(),\n external: z.boolean(),\n accesses: z.array(DataAccessSchema),\n owner: z.number().int().optional(),\n});\n\nconst DataResourceSchema = z.object({\n name: z.string(),\n kind: z.enum(DATA_RESOURCE_KINDS).optional(),\n external: z.boolean(),\n accesses: z.array(DataAccessSchema),\n writers: z.array(z.number().int()),\n owner: z.number().int().optional(),\n});\n\nconst CandidateSchema = z.object({\n id: z.number().int(),\n label: z.string(),\n fingerprint: z.string(),\n fileCount: count,\n files: z.array(z.string()),\n cohesion: z.number(),\n topTerms: z.array(z.object({ term: z.string(), files: count })),\n crossEdges: z.array(\n z.object({\n candidate: z.number().int(),\n total: count,\n byKind: z.partialRecord(EdgeKindSchema, count),\n }),\n ),\n coChangePairs: z.array(\n z.object({\n file: z.string(),\n otherFile: z.string(),\n otherCandidate: z.number().int(),\n frequency: z.number(),\n sharedCommits: count,\n }),\n ),\n // Absent in a PR 1A artifact.\n sharedTables: z.array(SharedTableSchema).default([]),\n});\n\nconst HubSchema = z.object({\n file: z.string(),\n reasons: z.array(z.enum(['configured', 'fan-in', 'god-node'])),\n dependents: count,\n damping: z.number(),\n candidate: z.number().int().optional(),\n});\n\nconst CollisionSchema = z.object({\n name: z.string(),\n minJaccard: z.number(),\n occurrences: z.array(\n z.object({\n candidate: z.number().int(),\n file: z.string(),\n symbolId: z.string(),\n members: z.array(z.string()),\n }),\n ),\n});\n\n/** Shape of `.ctxo/index/contexts-view.json`; what the reader accepts. */\nexport const ContextsViewSchema: z.ZodType<ContextsView> = z.object({\n version: z.literal(1),\n commitSha: z.string(),\n settings: SettingsSchema,\n coverage: z.object({ files: count, covered: count, ratio: z.number() }),\n candidates: z.array(CandidateSchema),\n hubs: z.array(HubSchema),\n unclustered: z.object({ count, files: z.array(z.string()) }),\n collisions: z.array(CollisionSchema),\n // Absent in a PR 1A artifact.\n dataResources: z.array(DataResourceSchema).default([]),\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 { DATA_RESOURCE_KINDS, type DataResourceKind } from '@ctxo/plugin-api';\nimport type { EdgeKind } from '../types.js';\n\n/** Edge kinds that couple code to data resources (protocol v2). */\nexport const DATA_EDGE_KINDS: ReadonlySet<EdgeKind> = new Set<EdgeKind>(['reads', 'writes', 'executes']);\n\nconst PROCEDURE_KINDS: readonly string[] = ['procedure'];\n\nexport function isDataResourceKind(kind: string): kind is DataResourceKind {\n return (DATA_RESOURCE_KINDS as readonly string[]).includes(kind);\n}\n\n/**\n * The definition kinds a data reference of `kind` matches, or undefined when `kind` names no data object. A\n * table or view reference matches both, because a 2-part `::table` reference may name a view; a procedure\n * reference matches procedures only.\n */\nexport function dataReferenceKinds(kind: string): readonly string[] | undefined {\n if (isDataResourceKind(kind)) return DATA_RESOURCE_KINDS;\n if (kind === 'procedure') return PROCEDURE_KINDS;\n return undefined;\n}\n","import { makeGlobMatcher } from '../config/load-config.js';\nimport type { FileIndex } from '../types.js';\n\n/** Globs that mark a repo-relative path as a test file, in spec 7.1 order. */\nexport const DEFAULT_TEST_GLOBS: readonly string[] = [\n '**/__tests__/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/test/**',\n '**/tests/**',\n '**/*_test.go',\n '**/src/test/**',\n '**/*.Tests/**',\n '**/*.Test/**',\n '**/*Tests.cs',\n '**/*Test.cs',\n '**/test_*.py',\n '**/*_test.py',\n];\n\n/**\n * The default globs that decide by file name alone. With annotation evidence (see `annotationAware`) a path\n * matched only by one of these is a test file only when a test-framework annotation marks it.\n */\nexport const NAME_ONLY_TEST_GLOBS: readonly string[] = ['**/*Tests.cs', '**/*Test.cs'];\n\n/**\n * Short names of the attributes that mark a test, as the C# plugin reports them (`Fact`, not\n * `Xunit.FactAttribute`): xUnit, NUnit and MSTest. Matching is exact and case-sensitive.\n */\nexport const TEST_FRAMEWORK_ANNOTATIONS: readonly string[] = [\n 'Fact',\n 'Theory',\n 'SkippableFact',\n 'SkippableTheory',\n 'Test',\n 'TestCase',\n 'TestCaseSource',\n 'TestFixture',\n 'TestMethod',\n 'DataTestMethod',\n 'TestClass',\n];\n\n/** Takes a repo-relative path with either separator. */\nexport type TestFileClassifier = (path: string) => boolean;\n\nexport interface TestFileClassifierOptions {\n /**\n * True when annotation evidence exists (the plugin that reports annotations ran). A path matched only by\n * a name-only glob is then a test file only if `annotatedFiles` holds it. Default false: name globs decide.\n */\n readonly annotationAware?: boolean;\n}\n\nfunction normalisePath(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/** The files (forward-slash paths) that hold a symbol carrying a test-framework annotation. */\nexport function filesWithTestAnnotations(indices: readonly FileIndex[]): Set<string> {\n const names = new Set(TEST_FRAMEWORK_ANNOTATIONS);\n const files = new Set<string>();\n for (const index of indices) {\n const marked = index.symbols.some((symbol) => symbol.annotations?.some((annotation) => names.has(annotation.name)));\n if (marked) files.add(normalisePath(index.file));\n }\n return files;\n}\n\n/**\n * Classifies test files by the default globs plus any extra (`index.tests`) globs. A path in `annotatedFiles`\n * is always a test file. With `options.annotationAware`, the name-only globs no longer decide on their own.\n */\nexport function createTestFileClassifier(\n extraGlobs: readonly string[] = [],\n annotatedFiles?: ReadonlySet<string>,\n options: TestFileClassifierOptions = {},\n): TestFileClassifier {\n const nameOnly = new Set(NAME_ONLY_TEST_GLOBS);\n const defaults = options.annotationAware === true\n ? DEFAULT_TEST_GLOBS.filter((glob) => !nameOnly.has(glob))\n : DEFAULT_TEST_GLOBS;\n const matchesGlob = makeGlobMatcher([...defaults, ...extraGlobs]);\n return (path) => annotatedFiles?.has(normalisePath(path)) === true || matchesGlob(path);\n}\n","import { compareCodepoint } from '../compare-codepoint.js';\nimport { dataReferenceKinds } from '../data-coupling/data-kinds.js';\nimport { createTestFileClassifier } from '../test-files/test-file-classifier.js';\nimport type { SymbolNode, GraphEdge } from '../types.js';\n\n/**\n * Test files by `DEFAULT_TEST_GLOBS` only. The graph has no config, so `index.tests` extras do not count\n * here: a copy in a test folder that only those extras name ranks as production.\n */\nconst isDefaultTestFile = createTestFileClassifier();\n\nconst DOCUMENTATION_FOLDERS: ReadonlySet<string> = new Set(['docs', 'doc', 'documentation']);\n\n/** ASCII A-Z lowercased, with no locale or Unicode folding. */\nfunction asciiLower(text: string): string {\n return text.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));\n}\n\n/** True when a folder of the path is `docs`, `doc` or `documentation`, compared after ASCII lowercasing. */\nfunction isUnderDocumentationFolder(file: string): boolean {\n const folders = file.replace(/\\\\/g, '/').split('/').slice(0, -1);\n return folders.some((folder) => DOCUMENTATION_FOLDERS.has(asciiLower(folder)));\n}\n\n/**\n * The rank of a data definition among several of the same name, smaller first: not in a test file comes\n * first, then not under a documentation folder. A definition is never dropped; the rank only orders them.\n */\nfunction definitionRank(symbolId: string): number {\n const at = symbolId.indexOf('::');\n const file = at < 0 ? symbolId : symbolId.slice(0, at);\n return (isDefaultTestFile(file) ? 2 : 0) + (isUnderDocumentationFolder(file) ? 1 : 0);\n}\n\n/** A table, view or procedure as one resource across every definition of its canonical name. */\nexport interface DataResourceIdentity {\n /** `<name>::<kind>` with the winner's kind, for example `dbo.loans::table`. */\n readonly canonicalId: string;\n /** The definition every data reference to the name resolves to (the `definitionRank` tie-break). */\n readonly winner: SymbolNode;\n /** Every definition of the name, once each, in tie-break order: the winner comes first. */\n readonly definitions: readonly SymbolNode[];\n /**\n * The definitions in neither a test file nor a documentation folder (the best `definitionRank`), in\n * tie-break order. Empty when every definition is a docs or test copy.\n */\n readonly production: readonly SymbolNode[];\n}\n\n/**\n * The symbol graph of an index.\n *\n * Add all nodes before any edge: `addEdge` resolves each endpoint against the nodes present at that moment,\n * and an endpoint it cannot resolve keeps its raw id for good.\n *\n * A table, view or procedure is one resource per canonical name, whatever the number of files that define\n * it (`dataResourceIdentity`). Every data reference resolves to the same definition, the winner, so only the\n * winner has incoming edges. The impact tools read dependents through `getDependentEdges`, which gives every\n * definition those edges, and forward edges stay on each definition: a procedure body in a later migration\n * keeps its own accesses.\n */\nexport class SymbolGraph {\n private readonly nodes = new Map<string, SymbolNode>();\n private readonly nodesByFileAndName = new Map<string, SymbolNode>();\n private readonly nodesByNameAndKind = new Map<string, SymbolNode[]>();\n private readonly forwardEdges = new Map<string, GraphEdge[]>();\n private readonly reverseEdges = new Map<string, GraphEdge[]>();\n private readonly definitionRanks = new Map<string, number>();\n private edgeSet = new Set<string>();\n\n addNode(node: SymbolNode): void {\n this.nodes.set(node.symbolId, node);\n // Index by file::name (ignoring kind) for fuzzy edge resolution\n const parts = node.symbolId.split('::');\n const [p0, p1] = parts;\n if (p0 !== undefined && p1 !== undefined) {\n this.nodesByFileAndName.set(`${p0}::${p1}`, node);\n }\n // Index by name::kind for name-reference targets (e.g. Java \"Bar::class\",\n // \"fixture.Bar::helper::method\") that carry name+kind but not the file path.\n if (parts.length === 3) {\n const nk = `${parts[1]}::${parts[2]}`;\n const list = this.nodesByNameAndKind.get(nk);\n if (list) list.push(node); else this.nodesByNameAndKind.set(nk, [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 segs = id.split('::');\n const [p0, p1] = segs;\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\n // Final fallback: name-reference targets (e.g. Java \"Bar::class\", \"pkg.Bar::helper::method\" or SQL\n // \"dbo.loans::table\") carry name+kind but not the file path. A data reference resolves even when\n // several definitions share the name, by the tie-break in resolveDataReference; any other reference\n // resolves by name+kind ONLY when unambiguous (exactly one node matches).\n const dataMatch = this.resolveDataReference(segs);\n if (dataMatch) return dataMatch.symbolId;\n const nameKind = this.extractNameKind(segs);\n if (nameKind) {\n const candidates = this.nodesByNameAndKind.get(nameKind);\n if (candidates && candidates.length === 1) return candidates[0]!.symbolId;\n }\n\n return id;\n }\n\n private resolveNodeFuzzy(id: string): SymbolNode | undefined {\n const segs = id.split('::');\n const [p0, p1] = segs;\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 const tsMatch = this.nodesByFileAndName.get(`${jsToTs}::${p1}`);\n if (tsMatch) return tsMatch;\n }\n }\n\n // Final fallback, as in resolveNodeId: a data reference by its tie-break, anything else by name+kind\n // only when unambiguous.\n const dataMatch = this.resolveDataReference(segs);\n if (dataMatch) return dataMatch;\n const nameKind = this.extractNameKind(segs);\n if (nameKind) {\n const candidates = this.nodesByNameAndKind.get(nameKind);\n if (candidates && candidates.length === 1) return candidates[0];\n }\n\n return undefined;\n }\n\n /**\n * Resolves a 2-part data reference (`dbo.loans::table`). A table or view reference is kind-agnostic\n * across data resource kinds, a procedure reference matches procedures only. Several definitions\n * resolve by `definitionRank` (a production copy before a documentation copy before a test copy), then\n * to the codepoint-smallest symbolId, which is the first file in sorted path order. The result does\n * not depend on insertion order.\n */\n private resolveDataReference(segs: string[]): SymbolNode | undefined {\n if (segs.length !== 2) return undefined;\n const [name, kind] = segs as [string, string];\n const kinds = dataReferenceKinds(kind);\n if (!kinds) return undefined;\n let best: SymbolNode | undefined;\n for (const k of kinds) {\n for (const node of this.nodesByNameAndKind.get(`${name}::${k}`) ?? []) {\n if (!best || this.compareDefinitions(node, best) < 0) best = node;\n }\n }\n return best;\n }\n\n /** The tie-break order of same-name definitions: `definitionRank`, then the codepoint-smaller symbolId. */\n private readonly compareDefinitions = (a: SymbolNode, b: SymbolNode): number =>\n this.definitionRankOf(a.symbolId) - this.definitionRankOf(b.symbolId) || compareCodepoint(a.symbolId, b.symbolId);\n\n /**\n * The identity of the table, view or procedure that `id` names: a full id of any of its definitions, or a\n * 2-part reference such as `dbo.loans::table`. Undefined when `id` names no node, or a node of another\n * kind. A table or view identity spans both kinds, as a 2-part `::table` reference does.\n */\n dataResourceIdentity(id: string): DataResourceIdentity | undefined {\n const node = this.getNode(id);\n return node ? this.identityOf(node) : undefined;\n }\n\n private identityOf(node: SymbolNode): DataResourceIdentity | undefined {\n const segs = node.symbolId.split('::');\n if (segs.length !== 3) return undefined;\n const [, name, kind] = segs as [string, string, string];\n const kinds = dataReferenceKinds(kind);\n if (!kinds) return undefined;\n const byId = new Map<string, SymbolNode>();\n for (const k of kinds) {\n for (const definition of this.nodesByNameAndKind.get(`${name}::${k}`) ?? []) {\n byId.set(definition.symbolId, this.nodes.get(definition.symbolId) ?? definition);\n }\n }\n const definitions = [...byId.values()].sort(this.compareDefinitions);\n const winner = definitions[0] ?? node;\n const production = definitions.filter((definition) => this.definitionRankOf(definition.symbolId) === 0);\n return { canonicalId: `${name}::${winner.kind}`, winner, definitions, production };\n }\n\n /** `definitionRank`, cached per symbolId (the glob match runs once per definition). */\n private definitionRankOf(symbolId: string): number {\n let rank = this.definitionRanks.get(symbolId);\n if (rank === undefined) {\n rank = definitionRank(symbolId);\n this.definitionRanks.set(symbolId, rank);\n }\n return rank;\n }\n\n /**\n * Extracts a `name::kind` key from the segments of a reference ID, supporting:\n * - 2-part: `Bar::class` → `Bar::class`\n * - 3-part: `pkg.Bar::helper::method` → `helper::method`\n * Returns undefined for 1-part IDs (no kind information available).\n */\n private extractNameKind(segs: string[]): string | undefined {\n if (segs.length === 3) return `${segs[1]}::${segs[2]}`; // pkg.Type::member::kind → member::kind\n if (segs.length === 2) return `${segs[0]}::${segs[1]}`; // Type::kind\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 /**\n * The dependents of a resolved symbolId, as the impact tools read them. For a definition of a table, view\n * or procedure these are the incoming edges of every same-name definition, which in practice are the\n * winner's: any copy then reports the dependents of the resource. For every other id they are its own\n * incoming edges, as `getReverseEdges` gives them.\n */\n getDependentEdges(symbolId: string): GraphEdge[] {\n const node = this.nodes.get(symbolId);\n const definitions = node ? this.identityOf(node)?.definitions : undefined;\n if (!definitions || definitions.length < 2) return this.getReverseEdges(symbolId);\n return definitions.flatMap((definition) => this.getReverseEdges(definition.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 { CommunitySnapshot } from '../types.js';\n\n/**\n * The snapshots of the commits before `currentSha`: newest first, one per commit.\n *\n * History is a chain of commits, not of runs, so two kinds of entry in a history list are not earlier\n * commits. One is a snapshot of the current commit itself: indexes written by older versions archived\n * the previous run's snapshot even at the same HEAD. The other is a further snapshot of a commit the list\n * already has: the post-commit hook used to archive one per changed file. The first (newest) snapshot of\n * a commit stands for that commit.\n */\nexport function priorCommitSnapshots(\n history: readonly CommunitySnapshot[],\n currentSha?: string,\n): CommunitySnapshot[] {\n const seen = new Set<string>();\n const prior: CommunitySnapshot[] = [];\n for (const snapshot of history) {\n if (snapshot.commitSha === currentSha || seen.has(snapshot.commitSha)) continue;\n seen.add(snapshot.commitSha);\n prior.push(snapshot);\n }\n return prior;\n}\n\n/**\n * How many commits other than `currentSha` the history reaches back over. Drift and boundary confidence\n * is built on this number, not on the number of snapshot files.\n */\nexport function distinctCommitCount(\n history: readonly CommunitySnapshot[],\n currentSha?: string,\n): number {\n return priorCommitSnapshots(history, currentSha).length;\n}\n","import { compareCodepoint } from '../compare-codepoint.js';\nimport type { CommunityEntry, CommunitySnapshot, DriftEvent } from '../types.js';\nimport { distinctCommitCount, priorCommitSnapshots } from './snapshot-history.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 /** The number of history entries passed in (kept for compatibility; confidence is built on `distinctCommits`). */\n readonly snapshotsAvailable: number;\n /** Commits other than the current one that the history covers; `confidence` is built on this. */\n readonly distinctCommits: 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 distinctCommits = distinctCommitCount(history, current.commitSha);\n const confidence = confidenceFor(distinctCommits);\n const hint = hintFor(distinctCommits);\n // `previous` is the newest history entry from a different commit than `current`; `anchor` is the\n // newest one from a third, older commit. A snapshot of the current commit, or a second snapshot of\n // one commit, is not another point in time to compare with.\n const [previous, anchor] = priorCommitSnapshots(history, current.commitSha);\n\n if (!previous) {\n return {\n events: [],\n confidence,\n snapshotsAvailable,\n distinctCommits,\n stability: { stable: 0, transient: 0, unverified: 0 },\n hint,\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) => compareCodepoint(a.symbolId, b.symbolId));\n\n return {\n events,\n confidence,\n snapshotsAvailable,\n distinctCommits,\n stability,\n hint,\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(distinctCommits: number): 'high' | 'medium' | 'low' {\n if (distinctCommits >= 7) return 'high';\n if (distinctCommits >= 3) return 'medium';\n return 'low';\n}\n\nfunction hintFor(distinctCommits: number): string | undefined {\n if (distinctCommits >= 3) return undefined;\n return 'Install the post-commit hook (`ctxo init`) or run `ctxo index` after commits to build history; `ctxo index --check` does not record snapshots.';\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport { EDGE_KINDS, type CoChangeEntry, type EdgeKind } 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\n/** Confidence of a dependent by edge kind. The data edges are exact references, so they are confirmed. */\nexport const EDGE_KIND_CONFIDENCE: Readonly<Record<EdgeKind, ImpactConfidence>> = {\n calls: 'confirmed',\n extends: 'confirmed',\n implements: 'confirmed',\n reads: 'confirmed',\n writes: 'confirmed',\n executes: 'confirmed',\n uses: 'likely',\n imports: 'potential',\n};\n\nconst CONFIDENCE_RANK: Record<ImpactConfidence, number> = {\n confirmed: 2,\n likely: 1,\n potential: 0,\n};\n\nfunction edgeKindToConfidence(kind: string): ImpactConfidence {\n return (EDGE_KINDS as readonly string[]).includes(kind) ? EDGE_KIND_CONFIDENCE[kind as EdgeKind] : 'potential';\n}\n\nexport class BlastRadiusCalculator {\n /**\n * `symbolId` may be any id `SymbolGraph.getNode` resolves, a 2-part data id included. The root is the\n * node it names, and the root's own file is the one co-change is matched against. Dependents come from\n * `getDependentEdges`, so every copy of a table, view or procedure, and its 2-part id, has the dependents\n * of the resource, and no definition of the resource counts as a dependent of another.\n */\n calculate(graph: SymbolGraph, symbolId: string, coChangeMap?: Map<string, CoChangeEntry[]>): BlastRadiusResult {\n const root = graph.getNode(symbolId);\n if (!root) {\n return { impactedSymbols: [], directDependentsCount: 0, confirmedCount: 0, likelyCount: 0, potentialCount: 0, overallRiskScore: 0 };\n }\n\n const rootId = root.symbolId;\n const sourceFile = rootId.split('::')[0]!;\n const definitions = graph.dataResourceIdentity(rootId)?.definitions.map((definition) => definition.symbolId) ?? [];\n\n const visited = new Set<string>([rootId, ...definitions]);\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: rootId, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n const reverseEdges = graph.getDependentEdges(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 // Each entry pairs the root's file with one partner, so match the dependent's file against that\n // partner. Matching either side would also hit a dependent inside the root's own file, with\n // whatever file the first entry happens to pair the root with.\n const match = coEntries.find(e => (e.file1 === sourceFile ? e.file2 : e.file1) === 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([rootId, ...definitions, ...rawEntries.map((e) => e.symbolId)]);\n\n const entries: BlastRadiusEntry[] = rawEntries.map((e) => ({\n ...e,\n dependentCount: graph.getDependentEdges(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","import { compareCodepoint } from '../compare-codepoint.js';\nimport type { CoChangeEntry } from '../types.js';\n\n/** A file that changes together with a symbol's file but has no dependency path to the symbol. */\nexport interface CoChangedFile {\n readonly file: string;\n readonly frequency: number;\n readonly sharedCommits: number;\n}\n\nconst DEFAULT_LIMIT = 10;\nconst DEFAULT_MIN_FREQUENCY = 0.5;\n\n/**\n * The files that co-change with `rootFile` at `frequency >= minFrequency` (default 0.5) and are not in\n * `impactedFiles`, strongest first (frequency, then shared commits, both descending, then file name in\n * code point order), at most `limit` of them (default 10).\n *\n * `impactedFiles` holds the files that already have a graph path to the root: the root's own file and the\n * file of every impacted symbol in any confidence tier. What is left is the evidence the graph cannot\n * give, which is why it is a separate, informational list: it never feeds the tiers, the risk scores or\n * the gate.\n *\n * Pure: it neither changes its inputs nor reads anything else, so it is safe to call on the cached map the\n * `ICoChangePort` hands to every caller.\n */\nexport function findCoChangedFiles(\n coChangeMap: ReadonlyMap<string, readonly CoChangeEntry[]>,\n rootFile: string,\n impactedFiles: ReadonlySet<string>,\n options: { limit?: number; minFrequency?: number } = {},\n): CoChangedFile[] {\n const limit = options.limit ?? DEFAULT_LIMIT;\n const minFrequency = options.minFrequency ?? DEFAULT_MIN_FREQUENCY;\n\n // One row per partner file. A well-formed matrix lists a pair once, but a hand-edited one may not, and a\n // file must not be listed twice: keep the strongest entry.\n const strongestByFile = new Map<string, CoChangedFile>();\n for (const entry of coChangeMap.get(rootFile) ?? []) {\n const file = entry.file1 === rootFile ? entry.file2 : entry.file1;\n if (file === rootFile || impactedFiles.has(file) || entry.frequency < minFrequency) continue;\n\n const candidate: CoChangedFile = { file, frequency: entry.frequency, sharedCommits: entry.sharedCommits };\n const known = strongestByFile.get(file);\n if (!known || compareStrongestFirst(candidate, known) < 0) strongestByFile.set(file, candidate);\n }\n\n return [...strongestByFile.values()].sort(compareStrongestFirst).slice(0, Math.max(0, limit));\n}\n\nfunction compareStrongestFirst(a: CoChangedFile, b: CoChangedFile): number {\n return (\n b.frequency - a.frequency ||\n b.sharedCommits - a.sharedCommits ||\n compareCodepoint(a.file, b.file)\n );\n}\n\n/**\n * `findCoChangedFiles` for the root symbol of a blast radius: the one place that says which files already\n * have a graph path to the root. They are the root's own file and the file of every symbol in\n * `impactedSymbols`, whatever its confidence tier.\n *\n * Pass the whole, unfiltered list of impacted symbols. A `confidence` or `intent` filter decides which\n * symbols a caller shows; it does not change that their files have a path to the root, and a file must\n * never turn up as \"co-changed without a graph path\" because a filter hid its dependent.\n *\n * Returns an empty list without a matrix (`undefined`), so a caller needs no check of its own.\n */\nexport function findCoChangedFilesForBlastRadius(\n coChangeMap: ReadonlyMap<string, readonly CoChangeEntry[]> | undefined,\n rootSymbolId: string,\n impactedSymbols: Iterable<{ readonly symbolId: string }>,\n): CoChangedFile[] {\n if (!coChangeMap) return [];\n\n const rootFile = fileOf(rootSymbolId);\n const impactedFiles = new Set<string>([rootFile]);\n for (const { symbolId } of impactedSymbols) impactedFiles.add(fileOf(symbolId));\n return findCoChangedFiles(coChangeMap, rootFile, impactedFiles);\n}\n\n/** The file part of a symbol id (`<file>::<name>::<kind>`). */\nfunction fileOf(symbolId: string): string {\n return symbolId.split('::')[0]!;\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 { DATA_EDGE_KINDS } from '../data-coupling/data-kinds.js';\nimport 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// SQL objects are reached by data edges and by runtime, never reported as dead\nconst SQL_PATTERN = /\\.sql$/i;\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 = allNodes.filter((n) => {\n const file = n.symbolId.split('::')[0] ?? '';\n if (SQL_PATTERN.test(file)) return false;\n if (options.includeTests) return true;\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 // Data edges count too: their targets (SQL objects) are never candidates, but a method that reads,\n // writes or executes one is as much in use as a method that calls another.\n const hasOutgoing = forwardEdges.some((e) => candidateIds.has(e.to) || DATA_EDGE_KINDS.has(e.kind));\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 { compareCodepoint } from '../compare-codepoint.js';\nimport { DATA_EDGE_KINDS } from '../data-coupling/data-kinds.js';\nimport type { SymbolGraph } from '../graph/symbol-graph.js';\nimport type {\n BoundaryViolation,\n CommunityEntry,\n CommunitySnapshot,\n} from '../types.js';\nimport { distinctCommitCount, priorCommitSnapshots } from './snapshot-history.js';\n\nexport interface BoundaryViolationDetectionResult {\n readonly violations: BoundaryViolation[];\n readonly confidence: 'high' | 'medium' | 'low';\n /** The number of history entries passed in (kept for compatibility; confidence is built on `distinctCommits`). */\n readonly snapshotsAvailable: number;\n /** Commits other than the current one that the history covers; `confidence` is built on this. */\n readonly distinctCommits: number;\n readonly hint?: string;\n}\n\nconst HIGH_SEVERITY_THRESHOLD = 0;\nconst MEDIUM_SEVERITY_THRESHOLD = 2;\nconst MIN_DISTINCT_COMMITS_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 distinctCommits = distinctCommitCount(history, current.commitSha);\n const currentAssignments = assignmentMap(current.communities);\n const currentLabels = labelMap(current.communities);\n // The evidence is one snapshot per earlier commit: a snapshot of the current commit is not history,\n // and several snapshots of one commit are one data point, not several.\n const earlierCommits = priorCommitSnapshots(history, current.commitSha);\n const historicalAssignments = earlierCommits.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 if (DATA_EDGE_KINDS.has(edge.kind)) 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 distinctCommits < MIN_DISTINCT_COMMITS_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 = compareCodepoint(a.from.symbolId, b.from.symbolId);\n return cmp !== 0 ? cmp : compareCodepoint(a.to.symbolId, b.to.symbolId);\n });\n\n return {\n violations,\n confidence: confidenceFor(distinctCommits),\n snapshotsAvailable,\n distinctCommits,\n hint: hintFor(distinctCommits),\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(distinctCommits: number): 'high' | 'medium' | 'low' {\n if (distinctCommits >= 7) return 'high';\n if (distinctCommits >= 3) return 'medium';\n return 'low';\n}\n\nfunction hintFor(distinctCommits: number): string | undefined {\n if (distinctCommits >= MIN_DISTINCT_COMMITS_FOR_CONFIDENCE) return undefined;\n return 'Install the post-commit hook (`ctxo init`) or run `ctxo index` after commits to build history; `ctxo index --check` does not record snapshots.';\n}\n","/**\n * Symbol Tokenizer — splits symbol names into searchable tokens.\n *\n * Handles camelCase, PascalCase, snake_case, kebab-case, and digit boundaries.\n * Used to populate FTS5 `tokenized_name` columns and to tokenize search queries.\n */\n\nconst STOP_WORDS = new Set([\n 'get', 'set', 'is', 'has', 'can', 'do', 'the', 'a', 'an', 'of', 'to', 'in',\n 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'not', 'no', 'or',\n]);\n\nexport interface TokenizerOptions {\n /** Include stop-word tokens in output (default: false) */\n includeStopWords?: boolean;\n /** Include the full original name as an additional token (default: true) */\n includeOriginal?: boolean;\n /** Include file path segments as tokens (default: false) */\n includeFilePath?: boolean;\n}\n\nconst DEFAULT_OPTIONS: Required<TokenizerOptions> = {\n includeStopWords: false,\n includeOriginal: true,\n includeFilePath: false,\n};\n\nexport class SymbolTokenizer {\n private readonly options: Required<TokenizerOptions>;\n\n constructor(options?: TokenizerOptions) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Tokenize a symbol name into searchable tokens.\n *\n * Examples:\n * \"getCoChangeMetrics\" → [\"get\", \"co\", \"change\", \"metrics\"] (or without stop words: [\"co\", \"change\", \"metrics\"])\n * \"SqliteStorageAdapter\" → [\"sqlite\", \"storage\", \"adapter\"]\n * \"i_storage_port\" → [\"i\", \"storage\", \"port\"]\n * \"BM25Scorer\" → [\"bm\", \"25\", \"scorer\"]\n * \"DB_PASSWORD\" → [\"db\", \"password\"]\n */\n tokenize(symbolName: string, filePath?: string): string[] {\n const splitTokens = SymbolTokenizer.splitName(symbolName);\n const lowered = splitTokens.map((t) => t.toLowerCase());\n\n const tokens: string[] = this.options.includeStopWords\n ? lowered\n : lowered.filter((t) => !STOP_WORDS.has(t));\n\n // Preserve original name as a token for exact matching\n if (this.options.includeOriginal) {\n const original = symbolName.toLowerCase();\n if (!tokens.includes(original)) {\n tokens.unshift(original);\n }\n }\n\n // Include file path segments\n if (this.options.includeFilePath && filePath) {\n const pathTokens = SymbolTokenizer.tokenizeFilePath(filePath);\n for (const pt of pathTokens) {\n if (!tokens.includes(pt)) {\n tokens.push(pt);\n }\n }\n }\n\n return tokens;\n }\n\n /**\n * Tokenize a search query. Queries use stop words and don't include originals.\n */\n tokenizeQuery(query: string): string[] {\n const words = query.trim().split(/\\s+/);\n const tokens: string[] = [];\n for (const word of words) {\n const split = SymbolTokenizer.splitName(word);\n for (const t of split) {\n const lowered = t.toLowerCase();\n if (lowered.length > 0 && !tokens.includes(lowered)) {\n tokens.push(lowered);\n }\n }\n }\n return tokens;\n }\n\n /**\n * Split a name into constituent words based on naming convention boundaries.\n *\n * Rules:\n * 1. Split on underscores and hyphens (snake_case, kebab-case)\n * 2. Split on camelCase/PascalCase boundaries (uppercase after lowercase)\n * 3. Split acronym boundaries (e.g., \"HTMLParser\" → [\"HTML\", \"Parser\"])\n * 4. Split on digit ↔ letter boundaries\n */\n static splitName(name: string): string[] {\n if (!name || name.length === 0) return [];\n\n // Step 1: Split on underscores, hyphens, dots, slashes\n const segments = name.split(/[_\\-./\\\\]+/).filter((s) => s.length > 0);\n\n const tokens: string[] = [];\n for (const segment of segments) {\n tokens.push(...SymbolTokenizer.splitCamelCase(segment));\n }\n\n return tokens.filter((t) => t.length > 0);\n }\n\n /**\n * Split a single segment on camelCase/PascalCase/digit boundaries.\n */\n private static splitCamelCase(segment: string): string[] {\n const tokens: string[] = [];\n let current = '';\n\n for (let i = 0; i < segment.length; i++) {\n const ch = segment[i]!;\n const prev = i > 0 ? segment[i - 1]! : '';\n\n // Boundary: letter → digit or digit → letter\n if (current.length > 0) {\n const prevIsDigit = isDigit(prev);\n const currIsDigit = isDigit(ch);\n if (prevIsDigit !== currIsDigit) {\n tokens.push(current);\n current = ch;\n continue;\n }\n }\n\n // Boundary: lowercase → uppercase (camelCase boundary)\n if (current.length > 0 && isLower(prev) && isUpper(ch)) {\n tokens.push(current);\n current = ch;\n continue;\n }\n\n // Acronym boundary: multiple uppercase followed by lowercase\n // \"HTMLParser\": H-T-M-L-P-a → when we hit 'a', current=\"HTMLP\"\n // We should split to \"HTML\" + \"Pa...\"\n if (current.length > 1 && isUpper(ch) === false && isUpper(prev) && !isDigit(ch)) {\n // prev is uppercase, current char is lowercase → split before prev\n const lastChar = current[current.length - 1]!;\n if (isUpper(lastChar)) {\n tokens.push(current.slice(0, -1));\n current = lastChar + ch;\n continue;\n }\n }\n\n current += ch;\n }\n\n if (current.length > 0) {\n tokens.push(current);\n }\n\n return tokens;\n }\n\n /**\n * Extract searchable tokens from a file path.\n * \"src/core/search/symbol-tokenizer.ts\" → [\"src\", \"core\", \"search\", \"symbol\", \"tokenizer\", \"ts\"]\n */\n static tokenizeFilePath(filePath: string): string[] {\n // Remove extension, split on separators\n const withoutExt = filePath.replace(/\\.[^.]+$/, '');\n const segments = withoutExt.split(/[/\\\\]+/);\n const tokens: string[] = [];\n for (const seg of segments) {\n const parts = seg.split(/[_\\-.]+/).filter((s) => s.length > 0);\n for (const p of parts) {\n const lowered = p.toLowerCase();\n if (lowered.length > 0 && !tokens.includes(lowered)) {\n tokens.push(lowered);\n }\n }\n }\n return tokens;\n }\n\n /** Check if a token is a stop word */\n static isStopWord(token: string): boolean {\n return STOP_WORDS.has(token.toLowerCase());\n }\n}\n\nfunction isUpper(ch: string): boolean {\n return ch >= 'A' && ch <= 'Z';\n}\n\nfunction isLower(ch: string): boolean {\n return ch >= 'a' && ch <= 'z';\n}\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9';\n}\n"],"mappings":"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","names":["readFileSync","existsSync","mkdirSync","join","log","join","existsSync","readFileSync","mkdirSync","readFileSync","join","log","join","readFileSync","existsSync","readFileSync","join","log","join","existsSync","readFileSync","log","readFileSync","statSync","join","z","z","log","UNTIL_REWRITTEN","UNTIL_RETRY","join","statSync","readFileSync","log","existsSync","readdirSync","join","resolve","DATA_RESOURCE_KINDS","DEFAULT_LIMIT","assignmentMap","labelMap","confidenceFor","hintFor","count"]}