@wrongstack/tools 0.296.3 → 0.296.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/read.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/codebase-index/languages.ts", "../src/codebase-index/ts-parser.ts", "../src/_win32-resolve.ts", "../src/codebase-index/spawn-gate.ts", "../src/codebase-index/go-parser.ts", "../src/codebase-index/generic-parser.ts", "../src/codebase-index/py-parser.ts", "../src/codebase-index/rs-parser.ts", "../src/codebase-index/json-parser.ts", "../src/codebase-index/yaml-parser.ts", "../src/read.ts", "../src/_util.ts", "../src/codebase-index/background-indexer.ts", "../src/codebase-index/circuit-breaker.ts", "../src/codebase-index/indexer.ts", "../src/codebase-index/gitignore.ts", "../src/codebase-index/parser-dispatch.ts", "../src/codebase-index/writer.ts", "../src/codebase-index/bm25.ts", "../src/codebase-index/lsp-kind.ts", "../src/codebase-index/schema.ts", "../src/codebase-index/sqlite-runtime.ts", "../src/codebase-index/writer-admin.ts", "../src/codebase-index/writer-bulk-insert.ts", "../src/codebase-index/writer-graph-helpers.ts", "../src/codebase-index/writer-ref-mapper.ts", "../src/codebase-index/writer-graph-reader.ts", "../src/codebase-index/writer-helpers.ts", "../src/codebase-index/writer-pragmas.ts", "../src/codebase-index/writer-schema.ts", "../src/codebase-index/writer-search-helpers.ts", "../src/codebase-index/writer-store-pool.ts", "../src/codebase-index/index-service.ts", "../src/codebase-index/project-server-client.ts", "../src/codebase-index/project-server-endpoint.ts", "../src/codebase-index/project-server-protocol.ts"],
4
- "sourcesContent": ["/**\n * Single source of truth for which files are indexable and which\n * {@link SymbolLang} they map to.\n *\n * Keep this list broad: missing a native AST parser must never mean \"skip\n * the file\". Unknown programming/config sources still go through the generic\n * regex extractor under their mapped lang (or `'other'`).\n */\n\nimport * as path from 'node:path';\nimport type { SymbolLang } from './schema.js';\n\n/**\n * Extension \u2192 language. Keys are lowercase including the leading dot.\n * Multi-dot extensions (`.d.ts`) are handled specially in {@link detectLang}.\n */\nexport const EXT_TO_LANG: Readonly<Record<string, SymbolLang>> = {\n // TypeScript / JavaScript (first-class TS compiler API)\n '.ts': 'ts',\n '.mts': 'ts',\n '.cts': 'ts',\n '.tsx': 'tsx',\n '.js': 'js',\n '.mjs': 'js',\n '.cjs': 'js',\n '.jsx': 'jsx',\n\n // First-class native/spawn parsers (+ regex fallback)\n '.go': 'go',\n '.py': 'py',\n '.pyi': 'py',\n '.pyw': 'py',\n '.rs': 'rs',\n '.json': 'json',\n '.jsonc': 'json',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n\n // C family\n '.c': 'c',\n '.h': 'c',\n '.cc': 'cpp',\n '.cpp': 'cpp',\n '.cxx': 'cpp',\n '.hh': 'cpp',\n '.hpp': 'cpp',\n '.hxx': 'cpp',\n\n // JVM / .NET\n '.java': 'java',\n '.cs': 'csharp',\n '.kt': 'kotlin',\n '.kts': 'kotlin',\n '.scala': 'scala',\n '.sc': 'scala',\n\n // Scripting\n '.php': 'php',\n '.rb': 'ruby',\n '.swift': 'swift',\n '.dart': 'dart',\n '.lua': 'lua',\n '.r': 'r',\n '.R': 'r',\n '.pl': 'other',\n '.pm': 'other',\n\n // Systems / functional\n '.zig': 'zig',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.hs': 'haskell',\n '.lhs': 'haskell',\n\n // Shell / data / docs / web\n '.sh': 'shell',\n '.bash': 'shell',\n '.zsh': 'shell',\n '.ps1': 'shell',\n '.sql': 'sql',\n '.md': 'md',\n '.mdx': 'md',\n '.toml': 'toml',\n '.html': 'html',\n '.htm': 'html',\n '.css': 'css',\n '.scss': 'css',\n '.less': 'css',\n '.vue': 'vue',\n '.svelte': 'svelte',\n '.proto': 'proto',\n '.graphql': 'graphql',\n '.gql': 'graphql',\n};\n\n/** Sorted unique extension list for discovery walks. */\nexport const INDEXABLE_EXTENSIONS: readonly string[] = Object.freeze(\n [...new Set(Object.keys(EXT_TO_LANG).map((e) => e.toLowerCase()))].sort(),\n);\n\n/** Filenames without (or with special) extensions that should still be indexed. */\nconst SPECIAL_FILENAMES: Readonly<Record<string, SymbolLang>> = {\n makefile: 'other',\n gnumakefile: 'other',\n dockerfile: 'other',\n 'docker-compose.yml': 'yaml',\n 'docker-compose.yaml': 'yaml',\n 'cmakelists.txt': 'other',\n gemfile: 'ruby',\n rakefile: 'ruby',\n procfile: 'other',\n justfile: 'other',\n};\n\n/**\n * Detect {@link SymbolLang} from a file path.\n * Returns `null` only for paths we intentionally refuse to index (binary\n * assets, lockfiles handled elsewhere, plain `.txt` without special name, \u2026).\n */\nexport function detectLang(file: string): SymbolLang | null {\n const base = path.basename(file);\n const lowerBase = base.toLowerCase();\n\n // declaration files: foo.d.ts \u2192 ts\n if (lowerBase.endsWith('.d.ts') || lowerBase.endsWith('.d.mts') || lowerBase.endsWith('.d.cts')) {\n return 'ts';\n }\n\n const special = SPECIAL_FILENAMES[lowerBase];\n if (special) return special;\n\n const ext = path.extname(base).toLowerCase();\n if (!ext) return null;\n return EXT_TO_LANG[ext] ?? null;\n}\n\n/** True when the path is eligible for the codebase index. */\nexport function isIndexablePath(file: string): boolean {\n return detectLang(file) !== null;\n}\n", "/**\n * TypeScript/JavaScript symbol extraction using the TypeScript Compiler API.\n *\n * We traverse the AST and collect:\n * - classes, interfaces, enums, type aliases \u2192 class|interface|enum|type\n * - functions and methods \u2192 function|method\n * - const/let/var declarations \u2192 const|let|var\n * - property/accessor declarations \u2192 property\n *\n * The `id` field on each Symbol is always 0 \u2014 the caller is responsible for\n * assigning unique ids during insertion.\n */\n\nimport type * as TS from '@typescript/typescript6';\nimport type { FileSymbols, Symbol as IndexSymbol, Ref, SymbolKind, SymbolLang } from './schema.js';\n\ntype TsModule = typeof import('@typescript/typescript6');\n\n/**\n * The TypeScript compiler is ~9MB of JavaScript and costs ~26MB heap / ~44MB\n * RSS to evaluate. A static `import` here put it in the module graph of every\n * bundle that can reach the indexer \u2014 including `wstack version`, the mailbox\n * bridge, and the codebase-index project server, none of which parse TS.\n *\n * It must stay a runtime `import()` of an EXTERNAL package: the build runs with\n * `splitting: false` (scripts/build-package.mjs), so esbuild inlines dynamic\n * imports of in-repo files. `parser-dispatch.ts` doing `await import('./ts-parser.js')`\n * therefore does NOT defer anything on its own \u2014 this boundary is the one that\n * survives bundling. Mirrors `_syntax-check.ts`.\n */\nlet ts!: TsModule;\nlet tsLoad: Promise<TsModule> | null = null;\n\nfunction loadTypescript(): Promise<TsModule> {\n tsLoad ??= import('@typescript/typescript6').then((m) => {\n ts = ((m as unknown as { default?: TsModule }).default ?? m) as TsModule;\n return ts;\n });\n return tsLoad;\n}\n\n// Map TypeScript SyntaxKind \u2192 our SymbolKind taxonomy. Built on first use\n// because the enum values only exist once the compiler module is loaded.\nlet kindMapCache: Partial<Record<TS.SyntaxKind, SymbolKind>> | null = null;\n\nfunction kindMap(): Partial<Record<TS.SyntaxKind, SymbolKind>> {\n kindMapCache ??= {\n [ts.SyntaxKind.ClassDeclaration]: 'class',\n [ts.SyntaxKind.InterfaceDeclaration]: 'interface',\n [ts.SyntaxKind.EnumDeclaration]: 'enum',\n [ts.SyntaxKind.TypeAliasDeclaration]: 'type',\n [ts.SyntaxKind.FunctionDeclaration]: 'function',\n [ts.SyntaxKind.MethodDeclaration]: 'method',\n [ts.SyntaxKind.GetAccessor]: 'property',\n [ts.SyntaxKind.SetAccessor]: 'property',\n [ts.SyntaxKind.PropertyDeclaration]: 'property',\n [ts.SyntaxKind.Parameter]: 'parameter',\n [ts.SyntaxKind.NamespaceExportDeclaration]: 'namespace',\n };\n return kindMapCache;\n}\n\nfunction kindOf(node: TS.Node): SymbolKind | null {\n // VariableDeclaration needs special handling \u2014 its parent tells us whether\n // it's `const`, `let`, or `var`.\n if (ts.isVariableDeclaration(node)) {\n const parent = node.parent;\n if (ts.isVariableDeclarationList(parent)) {\n const flags = parent.flags;\n if (flags & ts.NodeFlags.Let) return 'let';\n if (flags & ts.NodeFlags.Const) return 'const';\n return 'var';\n }\n }\n\n // Namespace (module) declaration\n if (ts.isModuleDeclaration(node)) return 'namespace';\n\n return kindMap()[node.kind] ?? null;\n}\n\n// Extension \u2192 language lives in languages.ts (single source of truth for\n// discovery + first-class + generic coverage).\n\nfunction getSignature(\n printer: TS.Printer,\n node: TS.Declaration,\n sourceFile: TS.SourceFile,\n): string {\n const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);\n return raw.replace(/\\s+/g, ' ').slice(0, 500);\n}\n\n/**\n * Extract the first line of a JSDoc comment preceding a node.\n * Uses `ts.getLeadingCommentRanges` which is the modern replacement for\n * the removed `ts.getJSDocComments`.\n */\nfunction getJsDoc(node: TS.Node, sourceFile: TS.SourceFile): string {\n const fullText = sourceFile.getFullText();\n // getLeadingCommentRanges wants the position where the node's leading trivia\n // begins (getFullStart), not the node's width \u2014 passing getFullWidth() looked\n // past the comment and silently returned no JSDoc for every symbol.\n const nodePos = node.getFullStart();\n const comments = ts.getLeadingCommentRanges(fullText, nodePos);\n if (!comments) return '';\n\n for (const range of comments) {\n const commentText = fullText.slice(range.pos, range.end);\n // Only process JSDoc comments (/** ... */)\n const trimmed = commentText.trim();\n if (trimmed.startsWith('/**') && trimmed.endsWith('*/')) {\n // Strip the /** and */ delimiters and leading * on each line\n const inner = trimmed\n .slice(3, -2) // remove /** and */\n .replace(/^[ \\t]*\\*[ ]?/gm, '') // remove leading \" * \" or \" *\" on each line\n .trim();\n return inner.split('\\n')[0]?.trim().slice(0, 200) ?? '';\n }\n }\n return '';\n}\n\n/** Push the current node's scope contribution onto `parts` (for the O(1) recursive scope tracker). */\nfunction pushScopeName(node: TS.Node, parts: string[]): void {\n if (\n ts.isClassDeclaration(node) ||\n ts.isInterfaceDeclaration(node) ||\n ts.isEnumDeclaration(node) ||\n ts.isTypeAliasDeclaration(node)\n ) {\n parts.push(node.name?.text ?? 'Anon');\n } else if (\n ts.isMethodDeclaration(node) ||\n ts.isGetAccessor(node) ||\n ts.isSetAccessor(node) ||\n ts.isPropertyDeclaration(node) ||\n ts.isFunctionDeclaration(node)\n ) {\n if (node.name && ts.isIdentifier(node.name)) {\n parts.push(node.name.text);\n }\n }\n}\n\nexport interface ParseOptions {\n file: string;\n content: string;\n lang: SymbolLang;\n}\n\n/**\n * Parse a TypeScript/JavaScript source file and extract all code symbols.\n *\n * The returned `Symbol.id` field is always `0` \u2014 the caller is responsible\n * for assigning unique numeric ids during bulk insertion.\n *\n * Returns an empty array for files that can't be parsed or contain no symbols.\n *\n * Async because the TypeScript compiler is loaded on first use \u2014 see\n * {@link loadTypescript}. The load is memoized, so only the first call to this\n * function in a process pays for it.\n */\nexport async function parseSymbols(opts: ParseOptions): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n await loadTypescript();\n\n let sourceFile: TS.SourceFile;\n try {\n sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);\n } catch {\n /* v8 ignore next -- createSourceFile tolerates malformed input and does not throw; defensive. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n\n const symbols: IndexSymbol[] = [];\n const refs: Ref[] = [];\n // Create the printer once per file instead of per-symbol. ts.createPrinter is\n // not free \u2014 it allocates internal emitter state \u2014 and we call getSignature\n // for every navigable declaration (often 100-300 per file).\n const printer = ts.createPrinter({});\n\n function visit(node: TS.Node, funcDepth: number, scopeParts: string[]): void {\n // \u2500\u2500 Symbol extraction \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const kind = kindOf(node);\n\n if (kind) {\n // Keep the index focused on navigable declarations. Function-local\n // variables and parameters account for most rows in large TypeScript\n // projects and otherwise swamp exact declaration searches.\n // funcDepth is a cheap O(1) counter threaded through the recursion\n // instead of walking up the parent chain per symbol.\n if (\n (kind === 'const' || kind === 'let' || kind === 'var' || kind === 'parameter') &&\n funcDepth > 0\n ) {\n // Fall through to ref extraction \u2014 function-local variables can still\n // appear in type references and calls.\n } else {\n const nameNode = (node as { name?: TS.Identifier | undefined }).name;\n if (!nameNode || !ts.isIdentifier(nameNode)) {\n // Anonymous declaration (e.g. `export default class { ... }`) \u2014 no\n // name identifier, so there's nothing to index. Skip children too\n // to avoid indexing members of anonymous containers. Ref extraction\n // for the node itself is also skipped, but anonymous declarations\n // never match ref checks anyway.\n return;\n }\n const name = nameNode.text;\n const pos = nameNode.getStart(sourceFile);\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);\n const scope = scopeParts.join('.');\n const signature = getSignature(printer, node as TS.Declaration, sourceFile);\n const docComment = getJsDoc(node, sourceFile);\n const text = [name, signature, docComment].filter(Boolean).join(' | ');\n\n symbols.push({\n id: 0,\n lang,\n kind,\n name,\n file,\n line: line + 1,\n col: character,\n signature,\n docComment,\n scope,\n text,\n });\n }\n }\n\n // \u2500\u2500 Reference extraction (inlined from extractRefs) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const pos = node.getStart(sourceFile);\n const { line } = sourceFile.getLineAndCharacterOfPosition(pos);\n const lineNum = line + 1;\n\n if (ts.isCallExpression(node)) {\n const expr = node.expression;\n if (ts.isIdentifier(expr)) {\n refs.push({ fromId: 0, toName: expr.text, callType: 'call', line: lineNum });\n }\n } else if (ts.isPropertyAccessExpression(node)) {\n if (ts.isIdentifier(node.expression)) {\n refs.push({ fromId: 0, toName: node.expression.text, callType: 'call', line: lineNum });\n }\n } else if (ts.isTypeReferenceNode(node)) {\n const name = getTypeName(node.typeName);\n if (name) refs.push({ fromId: 0, toName: name, callType: 'type_ref', line: lineNum });\n } else if (ts.isHeritageClause(node)) {\n for (const t of node.types) {\n const name = getTypeName(t.expression as TS.EntityName);\n if (name)\n refs.push({\n fromId: 0,\n toName: name,\n callType: node.token === ts.SyntaxKind.ExtendsKeyword ? 'inherit' : 'implement',\n line: lineNum,\n });\n }\n } else if (ts.isImportDeclaration(node)) {\n // Emit import refs for each imported symbol NAME rather than the module\n // path string. This lets the ref resolver match the import against the\n // target symbol's declaration name, so the dead-code BFS can traverse\n // module boundaries. Module-path refs (old behaviour) were never\n // resolvable because no symbol is ever named './foo.js'.\n emitImportSpecifierRefs(node, refs, lineNum);\n } else if (ts.isExportDeclaration(node) && node.moduleSpecifier) {\n // Re-exports: export { X } from './foo' \u2014 emit refs for the exported\n // names so they resolve to the source module's symbols.\n emitExportSpecifierRefs(node, refs, lineNum);\n }\n\n // Push scope name before recursing, pop after (O(1) instead of O(depth) parent walk)\n const scopeIdx = scopeParts.length;\n pushScopeName(node, scopeParts);\n const childFuncDepth = ts.isFunctionLike(node) ? funcDepth + 1 : funcDepth;\n ts.forEachChild(node, (child) => visit(child, childFuncDepth, scopeParts));\n scopeParts.length = scopeIdx;\n }\n\n visit(sourceFile, 0, []);\n\n return { file, lang, symbols, refs: deduplicateRefs(refs), mtimeMs: Date.now() };\n}\n\n// \u2500\u2500\u2500 Reference extraction helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Extract the name string from a type name node (simple or qualified). */\nfunction getTypeName(name: TS.EntityName): string {\n if (ts.isIdentifier(name)) return name.text;\n if (ts.isQualifiedName(name)) return `${getTypeName(name.left)}.${name.right.text}`;\n /* v8 ignore next -- an EntityName is always an Identifier or QualifiedName; defensive. */\n return '';\n}\n\n/** Remove duplicate refs (same toName, callType, line). fromId is always 0 at this stage. */\nfunction deduplicateRefs(refs: Ref[]): Ref[] {\n const seen = new Set<string>();\n return refs.filter((r) => {\n const key = `${r.toName}:${r.callType}:${r.line}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/** Extract the imported (original-export) name from an ImportSpecifier. */\nfunction getImportSpecifierName(spec: TS.ImportSpecifier): string {\n // import { X as Y } from './foo' \u2192 propertyName is 'X', name is 'Y'\n // import { X } from './foo' \u2192 propertyName is undefined, name is 'X'\n // We emit the ORIGINAL exported name so the ref resolves to the\n // declaration symbol in the source module.\n return spec.propertyName?.text ?? spec.name.text;\n}\n\n/**\n * Emit `import` refs for each named symbol brought into scope by an\n * `ImportDeclaration`. Uses the original exported name (not the local\n * alias and not the module path) so the ref resolver can match it against\n * the target symbol's declaration name.\n *\n * Handles:\n * import { X } from 'M' \u2192 ref toName: 'X'\n * import { X as Y } from 'M' \u2192 ref toName: 'X' (original name)\n * import X from 'M' \u2192 ref toName: 'X'\n * import * as X from 'M' \u2192 ref toName: 'X'\n * import { type X } from 'M' \u2192 ref toName: 'X' (type-only flagged)\n * import 'M' \u2192 no refs (side-effect only)\n */\nfunction emitImportSpecifierRefs(node: TS.ImportDeclaration, refs: Ref[], lineNum: number): void {\n const clause = node.importClause;\n if (!clause) return; // side-effect import: import 'foo'\n\n // Default import: import X from 'M' (may coexist with named bindings\n // e.g. import React, { useState } from 'react')\n if (clause.name) {\n refs.push({ fromId: 0, toName: clause.name.text, callType: 'import', line: lineNum });\n }\n\n // Named imports: import { X, Y } from 'M'\n const bindings = clause.namedBindings;\n if (!bindings) return;\n\n if (ts.isNamedImports(bindings)) {\n for (const element of bindings.elements) {\n refs.push({\n fromId: 0,\n toName: getImportSpecifierName(element),\n callType: 'import',\n line: lineNum,\n });\n }\n } else if (ts.isNamespaceImport(bindings)) {\n // import * as X from 'M'\n refs.push({ fromId: 0, toName: bindings.name.text, callType: 'import', line: lineNum });\n }\n}\n\n/**\n * Emit `import` refs for each symbol re-exported by an `ExportDeclaration`\n * with a `from` clause. These use the original source-side name so the ref\n * resolves to the declaration symbol in the source module.\n *\n * Handles:\n * export { X } from 'M' \u2192 ref toName: 'X'\n * export { X as Y } from 'M' \u2192 ref toName: 'X' (original name)\n * export * as X from 'M' \u2192 ref toName: 'X' (namespace)\n * export * from 'M' \u2192 NO ref (wildcard \u2014 handled via\n * file-level graph in dead-code-scan)\n */\nfunction emitExportSpecifierRefs(node: TS.ExportDeclaration, refs: Ref[], lineNum: number): void {\n const clause = node.exportClause;\n\n if (clause && ts.isNamespaceExport(clause)) {\n // export * as X from 'M' \u2014 NamespaceExport has a .name\n refs.push({ fromId: 0, toName: clause.name.text, callType: 'import', line: lineNum });\n return;\n }\n\n if (clause && ts.isNamedExports(clause)) {\n // export { X } from 'M' \u2014 NamedExports\n for (const element of clause.elements) {\n // export { X as Y } \u2192 propertyName is 'X' (original), name is 'Y' (exported)\n // export { X } \u2192 propertyName is undefined, name is 'X'\n const originalName = element.propertyName?.text ?? element.name.text;\n refs.push({ fromId: 0, toName: originalName, callType: 'import', line: lineNum });\n }\n return;\n }\n\n // export * from 'M' \u2014 no clause (wildcard). No per-symbol ref is\n // possible; the dead-code-scan handles this via file-level graph.\n}\n\n/** Detect SymbolLang from a file path \u2014 re-exported from the central map. */\nexport { detectLang } from './languages.js';\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * On Windows, Node.js `spawn()` without a shell does NOT resolve .cmd/.bat\n * extensions through PATHEXT \u2014 it only auto-resolves .exe. Most Node.js CLI\n * tools (npx, pnpm, biome, tsc, vitest, etc.) ship as .cmd wrappers on\n * Windows. This function resolves the command name to its full path so spawn\n * can find it without relying on shell-mode argument concatenation.\n *\n * On non-Windows, returns the command unchanged.\n */\nexport function resolveWin32Command(cmd: string): string {\n if (process.platform !== 'win32') return cmd;\n\n // Already has a path or extension \u2014 use as-is\n // Normalize forward slashes so path.extname correctly detects extensions\n // even when a Unix-style path is passed on Windows.\n if (cmd.includes('/') || cmd.includes('\\\\') || path.extname(cmd.replace(/\\//g, '\\\\'))) {\n return cmd;\n }\n\n const pathext = (process.env['PATHEXT'] ?? '.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC')\n .toLowerCase()\n .split(';');\n\n const pathDirs = (process.env['PATH'] ?? '').split(path.delimiter);\n\n for (const dir of pathDirs) {\n const base = path.join(dir, cmd);\n // Check extensions in PATHEXT order. .EXE should win first because\n // it's typically listed first, and .exe doesn't need shell: true.\n for (const ext of pathext) {\n const full = `${base}${ext}`;\n try {\n fs.accessSync(full, fs.constants.X_OK);\n return full;\n } catch {\n // Not found with this extension \u2014 try next\n }\n }\n }\n\n // Not found \u2014 return original; let spawn report ENOENT with the\n // expected error message so tools can surface it properly.\n return cmd;\n}\n\n/**\n * Resolve a PowerShell binary by name. `pickShell` in `_shell-pick.ts`\n * already decides whether the user wants `'pwsh'` (PowerShell 7+) or\n * `'powershell'` (Windows PowerShell 5.1). This helper turns that decision\n * into a real on-disk path.\n *\n * Order:\n * 1. If `cmd` is `pwsh` and a `pwsh.exe` exists on PATH \u2192 return that.\n * 2. If `cmd` is `pwsh` and only `powershell.exe` exists \u2192 fall back to\n * that (the alternative is a cryptic ENOENT for the user).\n * 3. Symmetric for `powershell`: prefer `powershell.exe`, fall back to\n * `pwsh.exe` if installed and the legacy binary is missing.\n * 4. Anything else \u2192 delegate to `resolveWin32Command` (handles `.cmd`\n * shims a sysadmin might drop in place, etc.).\n *\n * Returns the original command on ENOENT \u2014 `spawn()` will surface a clean\n * ENOENT and the user sees \"PowerShell not installed\", which is the right\n * diagnostic. We never throw from here.\n */\nexport function resolvePowerShell(cmd: string): string {\n if (process.platform !== 'win32') return cmd;\n const lower = cmd.toLowerCase();\n if (lower !== 'pwsh' && lower !== 'powershell' && lower !== 'pwsh.exe' && lower !== 'powershell.exe') {\n return resolveWin32Command(cmd);\n }\n // Prefer the requested edition, fall back to the other one.\n const primary = lower.startsWith('pwsh') ? 'pwsh.exe' : 'powershell.exe';\n const fallback = lower.startsWith('pwsh') ? 'powershell.exe' : 'pwsh.exe';\n const resolved = resolveWin32Command(primary);\n if (resolved !== primary) {\n // resolveWin32Command returns the original string when not found.\n const fb = resolveWin32Command(fallback);\n return fb === fallback ? cmd : fb;\n }\n return resolved;\n}\n\n/**\n * cmd.exe metacharacters that chain a new command or redirect I/O. When a\n * `.cmd`/`.bat` wrapper is launched through `cmd.exe`, any argument carrying\n * one of these can break out of the intended command line and run an\n * attacker-chosen command (the CVE-2024-27980 / \"BatBadBut\" argument-injection\n * class). We use a single vetted command line for cmd shims, so this guard is\n * mandatory before spawning.\n *\n * The set is limited to the unambiguous command-separator / redirection chars\n * plus newlines and NUL. Legitimate package-manager / test-runner flags and\n * Windows file paths (which use `:` `\\` `/` `.` `-` `_` space `(` `)`) never\n * contain these, so the guard is false-positive-free. Double quotes are also\n * rejected because cmd.exe quote toggling can break argument grouping.\n */\nconst WIN32_SHELL_META = /[&|<>\"\\r\\n\\0]/;\n\nexport interface Win32CmdShimInvocation {\n command: string;\n args: string[];\n windowsVerbatimArguments: true;\n}\n\n/**\n * Throw if any argument contains a cmd.exe command-injection metacharacter.\n * Call this ONLY on the Windows `.cmd`/`.bat` shim path. A no-op for safe args.\n */\nexport function assertSafeWin32ShellArgs(args: readonly unknown[]): void {\n for (const arg of args) {\n if (typeof arg === 'string' && WIN32_SHELL_META.test(arg)) {\n throw new Error(\n 'win32 cmd shim spawn: argument contains a shell metacharacter ' +\n '(one of & | < > \", or a newline) that could enable command injection ' +\n 'through the .cmd/.bat wrapper - refusing to run. Offending argument: ' +\n JSON.stringify(arg),\n );\n }\n }\n}\n\nexport function buildWin32CmdShimInvocation(\n command: string,\n args: readonly string[] = [],\n): Win32CmdShimInvocation {\n assertSafeWin32ShellArgs([command, ...args]);\n const line = ['call', quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(' ');\n return {\n command: process.env['COMSPEC'] ?? 'cmd.exe',\n args: ['/d', '/c', line],\n windowsVerbatimArguments: true,\n };\n}\n\nfunction quoteWin32CmdArg(arg: string): string {\n return `\"${arg}\"`;\n}\n", "/**\n * Global concurrency gate for external parser processes (Python / Go / cargo).\n *\n * Spawning many AST helpers in parallel burns CPU and thrash the OS process\n * table on Windows. Serializing native toolchains is correctness-preserving:\n * each file still gets the same parse result, just not all at once.\n */\n\nlet chain: Promise<unknown> = Promise.resolve();\n\n/**\n * Run `fn` exclusively with respect to other {@link withSpawnGate} callers\n * in this process. Errors propagate to the caller and do not break the queue.\n */\nexport function withSpawnGate<T>(fn: () => Promise<T>): Promise<T> {\n const run = chain.then(fn, fn);\n // Keep the chain alive even when `fn` rejects.\n chain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n}\n\n/** Test helper \u2014 reset queue between cases. */\nexport function resetSpawnGateForTests(): void {\n chain = Promise.resolve();\n}\n", "/**\n * Go source symbol extraction using `go/parser`.\n *\n * Spawns a `go run -` child process that parses the file with go/ast and\n * emits JSON. Falls back to empty results on any error.\n *\n * Extracts: package, func, type, const, var\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { resolveWin32Command } from '../_win32-resolve.js';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { withSpawnGate } from './spawn-gate.js';\n\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n try {\n // Serialize go child processes process-wide (same gate as Python).\n const parsed = await withSpawnGate(() => syncGoParse(file, content, lang));\n if (parsed.symbols.length > 0) {\n return parsed;\n }\n return fallbackParse(file, content, lang);\n } catch {\n /* v8 ignore next -- syncGoParse has its own catch; this outer guard is defensive. */\n return fallbackParse(file, content, lang);\n }\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Lightweight fallback parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction fallbackParse(filePath: string, content: string, lang: SymbolLang): FileSymbols {\n if (!/^\\s*package\\s+[A-Za-z_]\\w*/m.test(content) || hasUnbalancedDelimiters(content)) {\n return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n }\n\n const symbols: IndexSymbol[] = [];\n const packageName = content.match(/^\\s*package\\s+([A-Za-z_]\\w*)/m)?.[1] ?? '';\n const lines = content.split(/\\r?\\n/);\n for (const [idx, line] of lines.entries()) {\n const trimmed = line.trimStart();\n const col = line.length - trimmed.length + 1;\n const fn = /^func\\s+(?:\\([^)]*\\)\\s*)?([A-Za-z_]\\w*)\\s*\\(/.exec(trimmed);\n if (fn?.[1]) {\n addFallbackSymbol(symbols, { filePath, lang, kind: trimmed.startsWith('func (') ? 'method' : 'function', name: fn[1], line: idx + 1, col, signature: trimmed, scope: packageName ? `${packageName}.${fn[1]}` : fn[1] });\n continue;\n }\n\n const typeDecl = /^type\\s+([A-Za-z_]\\w*)\\b/.exec(trimmed);\n if (typeDecl?.[1]) {\n addFallbackSymbol(symbols, { filePath, lang, kind: 'type', name: typeDecl[1], line: idx + 1, col, signature: trimmed, scope: packageName });\n continue;\n }\n\n const valueDecl = /^(const|var)\\s+([A-Za-z_]\\w*)\\b/.exec(trimmed);\n if (valueDecl?.[1] && valueDecl[2]) {\n addFallbackSymbol(symbols, { filePath, lang, kind: valueDecl[1] as 'const' | 'var', name: valueDecl[2], line: idx + 1, col, signature: trimmed, scope: packageName });\n }\n }\n\n return { file: filePath, lang, symbols, mtimeMs: Date.now() };\n}\n\nfunction addFallbackSymbol(\n symbols: IndexSymbol[],\n opts: {\n filePath: string;\n lang: SymbolLang;\n kind: IndexSymbol['kind'];\n name: string;\n line: number;\n col: number;\n signature: string;\n scope: string;\n },\n): void {\n symbols.push({\n id: 0,\n lang: opts.lang,\n kind: opts.kind,\n name: opts.name,\n file: opts.filePath,\n line: opts.line,\n col: opts.col,\n signature: opts.signature,\n docComment: '',\n scope: opts.scope,\n text: `${opts.name} ${opts.signature}`.trim(),\n });\n}\n\nfunction hasUnbalancedDelimiters(content: string): boolean {\n const pairs: Record<string, string> = { '(': ')', '[': ']', '{': '}' };\n const closers = new Set(Object.values(pairs));\n const stack: string[] = [];\n for (const ch of content) {\n if (pairs[ch]) {\n stack.push(pairs[ch]);\n } else if (closers.has(ch) && stack.pop() !== ch) {\n return true;\n }\n }\n return stack.length > 0;\n}\n\n// \u2500\u2500\u2500 Inline Go parser script \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst GO_PARSE_SCRIPT = `\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\ntype Sym struct {\n\tName string \\`json:\"name\"\\`\n\tKind string \\`json:\"kind\"\\`\n\tLine int \\`json:\"line\"\\`\n\tCol int \\`json:\"col\"\\`\n\tSignature string \\`json:\"signature\"\\`\n\tScope string \\`json:\"scope\"\\`\n}\n\nfunc main() {\n\tsrc, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\tfmt.Print(\"[]\")\n\t\treturn\n\t}\n\tfset := token.NewFileSet()\n\tnode, err := parser.ParseFile(fset, \"src.go\", src, 0)\n\tif err != nil {\n\t\tfmt.Print(\"[]\")\n\t\treturn\n\t}\n\n\tvar syms []Sym\n\n\t// Package-level scope\n\tpkgScope := node.Name.Name\n\n\t// Collect all top-level declarations\n\tfor _, decl := range node.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tname := d.Name.Name\n\t\t\tkind := \"function\"\n\t\t\tscope := pkgScope\n\t\t\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\t\t\tscope = pkgScope + \".\" + recvTypeName(d.Recv.List[0].Type) + \".\" + name\n\t\t\t\tkind = \"method\"\n\t\t\t} else {\n\t\t\t\tscope = pkgScope + \".\" + name\n\t\t\t}\n\t\t\tpos := fset.Position(d.Pos())\n\t\t\tsig := formatFuncSig(d)\n\t\t\tsyms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: scope})\n\n\t\tcase *ast.GenDecl:\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tname := s.Name.Name\n\t\t\t\t\tpos := fset.Position(s.Pos())\n\t\t\t\t\tsig := \"type \" + name\n\t\t\t\t\tif s.TypeParams != nil {\n\t\t\t\t\t\tsig += formatTypeParams(s.TypeParams)\n\t\t\t\t\t}\n\t\t\t\t\tif st, ok := s.Type.(*ast.StructType); ok {\n\t\t\t\t\t\tsig += \" = struct { \" + formatFields(st.Fields.List) + \" }\"\n\t\t\t\t\t} else if it, ok := s.Type.(*ast.InterfaceType); ok {\n\t\t\t\t\t\tsig += \" = interface { \" + formatMethods(it.Methods.List) + \" }\"\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsig += \" = \" + formatType(s.Type)\n\t\t\t\t\t}\n\t\t\t\t\tsyms = append(syms, Sym{Name: name, Kind: \"type\", Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})\n\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tfor _, n := range s.Names {\n\t\t\t\t\t\tname := n.Name\n\t\t\t\t\t\tpos := fset.Position(n.Pos())\n\t\t\t\t\t\tkind := \"var\"\n\t\t\t\t\t\tif d.Tok == token.CONST {\n\t\t\t\t\t\t\tkind = \"const\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsig := kind + \" \" + name\n\t\t\t\t\t\tif s.Type != nil {\n\t\t\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdata, err := json.Marshal(syms)\n\tif err != nil {\n\t\tfmt.Print(\"[]\")\n\t\treturn\n\t}\n\tfmt.Print(string(data))\n}\n\nfunc recvTypeName(t ast.Expr) string {\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.StarExpr:\n\t\treturn recvTypeName(v.X)\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\nfunc formatFuncSig(d *ast.FuncDecl) string {\n\tscope := \"\"\n\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\tscope = \"(\" + formatFieldList(d.Recv.List) + \") \"\n\t}\n\tscope += formatFuncType(d.Type)\n\treturn \"func \" + scope\n}\n\nfunc formatFuncType(f *ast.FuncType) string {\n\tparams := formatFieldList(f.Params.List)\n\tresults := \"\"\n\tif f.Results != nil {\n\t\tresults = \" -> \" + formatFieldList(f.Results.List)\n\t}\n\treturn params + results\n}\n\nfunc formatFieldList(fields []*ast.Field) string {\n\tif len(fields) == 0 {\n\t\treturn \"()\"\n\t}\n\tnames := make([]string, 0, len(fields))\n\tfor _, f := range fields {\n\t\tname := \"\"\n\t\tif len(f.Names) > 0 {\n\t\t\tname = f.Names[0].Name\n\t\t}\n\t\tt := formatType(f.Type)\n\t\tif name != \"\" {\n\t\t\tnames = append(names, name+\" \"+t)\n\t\t} else {\n\t\t\tnames = append(names, t)\n\t\t}\n\t}\n\treturn \"(\" + strings.Join(names, \", \") + \")\"\n}\n\nfunc formatFields(fields []*ast.Field) string {\n\tlines := make([]string, 0)\n\tfor _, f := range fields {\n\t\tname := \"\"\n\t\tif len(f.Names) > 0 {\n\t\t\tname = f.Names[0].Name\n\t\t}\n\t\tt := formatType(f.Type)\n\t\tif name != \"\" {\n\t\t\tlines = append(lines, name+\" \"+t)\n\t\t} else {\n\t\t\tlines = append(lines, t)\n\t\t}\n\t}\n\treturn strings.Join(lines, \"; \")\n}\n\nfunc formatMethods(fields []*ast.Field) string {\n\treturn formatFields(fields)\n}\n\nfunc formatTypeParams(tp *ast.FieldList) string {\n\tif tp == nil || len(tp.List) == 0 {\n\t\treturn \"\"\n\t}\n\tparams := make([]string, len(tp.List))\n\tfor i, p := range tp.List {\n\t\tif len(p.Names) > 0 {\n\t\t\tparams[i] = p.Names[0].Name\n\t\t} else {\n\t\t\tparams[i] = \"T\"\n\t\t}\n\t}\n\treturn \"[\" + strings.Join(params, \", \") + \"]\"\n}\n\nfunc formatType(t ast.Expr) string {\n\tif t == nil {\n\t\treturn \"?\"\n\t}\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn formatType(v.X) + \".\" + v.Sel.Name\n\tcase *ast.StarExpr:\n\t\treturn \"*\" + formatType(v.X)\n\tcase *ast.ArrayType:\n\t\tif v.Len == nil {\n\t\t\treturn \"[]\" + formatType(v.Elt)\n\t\t}\n\t\treturn \"[...]\" + formatType(v.Elt)\n\tcase *ast.MapType:\n\t\treturn \"map[\" + formatType(v.Key) + \"]\" + formatType(v.Value)\n\tcase *ast.InterfaceType:\n\t\treturn \"interface{}\"\n\tcase *ast.StructType:\n\t\treturn \"struct{}\"\n\tcase *ast.FuncType:\n\t\treturn formatFuncType(v)\n\tcase *ast.ChanType:\n\t\treturn \"chan \" + formatType(v.Value)\n\tcase *ast.BasicLit:\n\t\treturn v.Value\n\tcase *ast.IndexExpr:\n\t\t// Generic instantiation with one type arg, e.g. Logger[int].\n\t\treturn formatType(v.X) + \"[\" + formatType(v.Index) + \"]\"\n\tcase *ast.IndexListExpr:\n\t\t// Generic instantiation with multiple type args, e.g. Map[K, V].\n\t\targs := make([]string, len(v.Indices))\n\t\tfor i, idx := range v.Indices {\n\t\t\targs[i] = formatType(idx)\n\t\t}\n\t\treturn formatType(v.X) + \"[\" + strings.Join(args, \", \") + \"]\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n`;\n\n// Cache the temp script path so we don't rewrite the parser script on every\n// file. The script is identical for every invocation \u2014 writing it once per\n// process (like py-parser does) eliminates mkdtemp + writeFile + rm per file.\nlet _cachedGoScriptPath: string | null = null;\n\nasync function syncGoParse(\n filePath: string,\n content: string,\n lang: SymbolLang,\n): Promise<FileSymbols> {\n // Feed the source over stdin \u2014 never pass the target .go file as a CLI arg.\n // `go run script.go target.go` makes the toolchain treat target.go as a\n // second package file (\"named files must all be in one directory\") and\n // refuses *_test.go outright. Reading from stdin sidesteps both, and lets\n // us parse the in-memory content without touching disk.\n try {\n // Local `let` so TypeScript's CFA narrows to `string` after the guard.\n // Module-scope `_cachedGoScriptPath` stays `string | null` because TS\n // can't prove no concurrent mutation between the check and the use.\n let scriptPath = _cachedGoScriptPath;\n if (!scriptPath) {\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ws-go-parse-'));\n scriptPath = path.join(tmpDir, 'parse.go');\n await fs.writeFile(scriptPath, GO_PARSE_SCRIPT, 'utf8');\n _cachedGoScriptPath = scriptPath;\n }\n\n // argv-array form (no shell): avoids any quoting/metachar issues in the\n // temp script path. The target source is fed via stdin, not as an arg.\n // Resolve the Go binary via PATHEXT on Windows so ENOENT is impossible.\n const goBinary = resolveWin32Command('go');\n\n const goResult = await new Promise<{ code: number | null; stdout: string }>(\n (resolve, reject) => {\n let settled = false;\n\n const proc: ChildProcess = spawn(goBinary, ['run', scriptPath], {\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n });\n\n proc.on('error', (err) => {\n if (settled) return;\n settled = true;\n reject(err);\n });\n\n let stdout = '';\n proc.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n // Drain stderr to avoid backpressure deadlocks from Go toolchain\n // diagnostics (e.g. \"found packages \u2026\").\n proc.stderr?.resume();\n\n // Write source via stdin so `go run` receives it without touching disk\n proc.stdin?.write(content);\n proc.stdin?.end();\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n proc.kill('SIGKILL');\n reject(new Error('timeout'));\n }, 15_000);\n timer.unref?.();\n\n proc.on('close', (code) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({ code, stdout });\n });\n },\n );\n\n const { code, stdout } = goResult;\n\n if (code !== 0 || !stdout.trim()) {\n return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n }\n\n const raw = JSON.parse(stdout.trim()) as Array<{\n name: string;\n kind: string;\n line: number;\n col: number;\n signature: string;\n scope: string;\n }>;\n const symbols: IndexSymbol[] = raw.map((s) => ({\n id: 0,\n lang,\n kind: s.kind as IndexSymbol['kind'],\n name: s.name,\n file: filePath,\n line: s.line,\n col: s.col,\n signature: s.signature ?? '',\n docComment: '',\n scope: s.scope ?? '',\n text: `${s.name} ${s.signature ?? ''}`.trim(),\n }));\n return { file: filePath, lang, symbols, mtimeMs: Date.now() };\n } catch {\n return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n", "/**\n * Universal regex symbol extractor.\n *\n * Used for:\n * - languages without a first-class AST parser (Java, C++, Ruby, \u2026)\n * - fallback when a native toolchain is missing (Python without `python`,\n * Go without `go`, Rust without cargo/syn)\n *\n * Patterns are intentionally recall-oriented: better a few false positives\n * in the index than silently dropping whole packages from search/code-map.\n */\n\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolKind, SymbolLang } from './schema.js';\n\ninterface ExtractPattern {\n /** Global regex; capture group 1 must be the symbol name. */\n re: RegExp;\n kind: SymbolKind;\n}\n\n/** Shared C-like declaration patterns (C/C++/Java/C#/\u2026 approximate). */\nconst C_LIKE: ExtractPattern[] = [\n { re: /\\b(?:class|struct|enum|interface|union)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n {\n re: /\\b(?:public|private|protected|static|final|async|override|virtual|inline|export)?\\s*(?:[\\w:<>[\\]\\s*&]+)\\s+([A-Za-z_]\\w*)\\s*\\([^;{]*\\)\\s*(?:const)?\\s*[{;]/g,\n kind: 'function',\n },\n { re: /\\b(?:namespace)\\s+([A-Za-z_]\\w*)/g, kind: 'namespace' },\n];\n\nconst LANG_PATTERNS: Partial<Record<SymbolLang, ExtractPattern[]>> = {\n py: [\n { re: /^(?:async\\s+)?def\\s+([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /^class\\s+([A-Za-z_]\\w*)/gm, kind: 'class' },\n { re: /^([A-Za-z_]\\w*)\\s*=/gm, kind: 'var' },\n ],\n go: [\n { re: /^func\\s+(?:\\([^)]*\\)\\s*)?([A-Za-z_]\\w*)\\s*\\(/gm, kind: 'function' },\n { re: /^type\\s+([A-Za-z_]\\w*)\\b/gm, kind: 'type' },\n { re: /^(?:const|var)\\s+([A-Za-z_]\\w*)\\b/gm, kind: 'const' },\n { re: /^package\\s+([A-Za-z_]\\w*)/gm, kind: 'namespace' },\n ],\n rs: [\n { re: /\\bfn\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\bstruct\\s+([A-Za-z_]\\w*)/g, kind: 'struct' },\n { re: /\\benum\\s+([A-Za-z_]\\w*)/g, kind: 'enum' },\n { re: /\\btrait\\s+([A-Za-z_]\\w*)/g, kind: 'trait' },\n { re: /\\bimpl(?:\\s*<[^>]+>)?\\s+([A-Za-z_]\\w*)/g, kind: 'impl' },\n { re: /\\b(?:const|static)\\s+([A-Za-z_]\\w*)/g, kind: 'const' },\n { re: /\\bmod\\s+([A-Za-z_]\\w*)/g, kind: 'mod' },\n ],\n c: C_LIKE,\n cpp: C_LIKE,\n java: [\n { re: /\\b(?:class|interface|enum|record)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n {\n re: /\\b(?:public|private|protected|static|final|abstract|synchronized|native|default|\\s)+\\s*[\\w.<>,[\\]\\s]+\\s+([A-Za-z_]\\w*)\\s*\\(/g,\n kind: 'method',\n },\n ],\n csharp: [\n { re: /\\b(?:class|interface|struct|enum|record)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n { re: /\\bnamespace\\s+([A-Za-z_.\\w]+)/g, kind: 'namespace' },\n {\n re: /\\b(?:public|private|protected|internal|static|async|override|virtual|\\s)+\\s*[\\w.<>,[\\]\\s]+\\s+([A-Za-z_]\\w*)\\s*\\(/g,\n kind: 'method',\n },\n ],\n php: [\n { re: /\\bfunction\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\bclass\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n { re: /\\binterface\\s+([A-Za-z_]\\w*)/g, kind: 'interface' },\n { re: /\\bnamespace\\s+([A-Za-z_\\\\]+)/g, kind: 'namespace' },\n ],\n ruby: [\n { re: /^\\s*def\\s+(?:self\\.)?([A-Za-z_]\\w*[!?]?)/gm, kind: 'function' },\n { re: /^\\s*class\\s+([A-Za-z_]\\w*)/gm, kind: 'class' },\n { re: /^\\s*module\\s+([A-Za-z_]\\w*)/gm, kind: 'namespace' },\n ],\n swift: [\n { re: /\\b(?:func)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:class|struct|enum|protocol|actor)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n kotlin: [\n { re: /\\b(?:fun)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:class|interface|object|enum\\s+class|data\\s+class)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n scala: [\n { re: /\\b(?:def)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:class|object|trait|enum)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n shell: [\n { re: /^(?:function\\s+)?([A-Za-z_]\\w*)\\s*\\(\\)\\s*\\{/gm, kind: 'function' },\n { re: /^([A-Za-z_][\\w]*)\\s*\\(\\)\\s*\\{/gm, kind: 'function' },\n ],\n sql: [\n { re: /\\bCREATE\\s+(?:OR\\s+REPLACE\\s+)?(?:TABLE|VIEW|INDEX|FUNCTION|PROCEDURE|TRIGGER)\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?([A-Za-z_\"][\\w.\"]*)/gi, kind: 'type' },\n ],\n md: [\n { re: /^(#{1,6})\\s+(.+)$/gm, kind: 'namespace' },\n ],\n toml: [\n { re: /^\\[([^\\]]+)\\]/gm, kind: 'namespace' },\n ],\n html: [\n { re: /\\bid\\s*=\\s*[\"']([^\"']+)[\"']/gi, kind: 'property' },\n { re: /<(?:script|template|style)\\b/gi, kind: 'namespace' },\n ],\n css: [\n { re: /^\\s*([.#]?[A-Za-z_][\\w-]*)\\s*\\{/gm, kind: 'type' },\n { re: /@(?:keyframes|media|supports)\\s+([^{\\s]+)/g, kind: 'namespace' },\n ],\n vue: [\n { re: /\\b(?:function|const|let|var|class|export\\s+(?:default\\s+)?(?:function|class|const))\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /<(?:script|template|style)\\b/gi, kind: 'namespace' },\n ],\n svelte: [\n { re: /\\b(?:function|const|let|var|class|export\\s+(?:default\\s+)?(?:function|class|const))\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n dart: [\n { re: /\\b(?:class|enum|mixin|extension)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n { re: /\\b([A-Za-z_]\\w*)\\s*\\([^;]*\\)\\s*(?:async\\s*)?\\{/g, kind: 'function' },\n ],\n lua: [\n { re: /\\bfunction\\s+([A-Za-z_.:]\\w*)/g, kind: 'function' },\n { re: /\\blocal\\s+function\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n r: [\n { re: /([A-Za-z.]\\w*)\\s*<-\\s*function\\s*\\(/g, kind: 'function' },\n { re: /([A-Za-z.]\\w*)\\s*=\\s*function\\s*\\(/g, kind: 'function' },\n ],\n proto: [\n { re: /\\b(?:message|service|enum)\\s+([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\brpc\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n graphql: [\n { re: /\\b(?:type|interface|enum|input|union|scalar)\\s+([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\b(?:query|mutation|subscription)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n zig: [\n { re: /\\b(?:fn|pub\\s+fn)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:const|var)\\s+([A-Za-z_]\\w*)/g, kind: 'const' },\n { re: /\\b(?:struct|enum|union)\\s*\\{/g, kind: 'type' },\n ],\n elixir: [\n { re: /\\bdef(?:p|macro|macrop)?\\s+([A-Za-z_]\\w*[!?]?)/g, kind: 'function' },\n { re: /\\bdefmodule\\s+([A-Za-z_.]\\w*)/g, kind: 'namespace' },\n ],\n haskell: [\n { re: /^([A-Za-z_]\\w*)\\s*::/gm, kind: 'function' },\n { re: /\\bdata\\s+([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\btype\\s+(?:family\\s+)?([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\bclass\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n other: [\n { re: /^(?:export\\s+)?(?:async\\s+)?function\\s+([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /^(?:export\\s+)?class\\s+([A-Za-z_]\\w*)/gm, kind: 'class' },\n { re: /^(?:export\\s+)?(?:const|let|var)\\s+([A-Za-z_]\\w*)/gm, kind: 'const' },\n { re: /^(?:async\\s+)?def\\s+([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /^func\\s+(?:\\([^)]*\\)\\s*)?([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /\\bfn\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /^(?:target|PHONY)\\s*:/gm, kind: 'namespace' },\n ],\n};\n\nconst KEYWORDS = new Set([\n 'if',\n 'else',\n 'for',\n 'while',\n 'switch',\n 'case',\n 'return',\n 'break',\n 'continue',\n 'new',\n 'delete',\n 'typeof',\n 'instanceof',\n 'void',\n 'null',\n 'true',\n 'false',\n 'this',\n 'super',\n 'import',\n 'export',\n 'from',\n 'as',\n 'default',\n 'public',\n 'private',\n 'protected',\n 'static',\n 'final',\n 'class',\n 'struct',\n 'enum',\n 'interface',\n 'function',\n 'def',\n 'fn',\n 'func',\n 'var',\n 'let',\n 'const',\n 'package',\n 'namespace',\n 'module',\n 'using',\n 'include',\n 'require',\n 'select',\n 'from',\n 'where',\n 'and',\n 'or',\n 'not',\n 'in',\n 'is',\n 'try',\n 'catch',\n 'finally',\n 'throw',\n 'async',\n 'await',\n 'yield',\n]);\n\nfunction patternsFor(lang: SymbolLang): ExtractPattern[] {\n return LANG_PATTERNS[lang] ?? LANG_PATTERNS.other ?? [];\n}\n\nfunction looksBinary(content: string): boolean {\n // NUL byte \u2192 almost certainly not source text\n if (content.includes('\\0')) return true;\n // High ratio of non-printable in the first 2KB\n const sample = content.slice(0, 2048);\n if (sample.length === 0) return false;\n let bad = 0;\n for (let i = 0; i < sample.length; i++) {\n const c = sample.charCodeAt(i);\n if (c === 9 || c === 10 || c === 13) continue;\n if (c < 32 || c === 127) bad++;\n }\n return bad / sample.length > 0.1;\n}\n\nfunction lineColAt(content: string, index: number): { line: number; col: number } {\n let line = 1;\n let lastNl = -1;\n for (let i = 0; i < index && i < content.length; i++) {\n if (content.charCodeAt(i) === 10) {\n line++;\n lastNl = i;\n }\n }\n return { line, col: index - lastNl };\n}\n\n/** Soft default: enough for normal sources without runaway regex on minified blobs. */\nexport const GENERIC_MAX_SYMBOLS_DEFAULT = 500;\n/** Only the leading window is scanned \u2014 huge generated files stay cheap. */\nexport const GENERIC_MAX_FILE_CHARS = 512 * 1024;\n\n/**\n * Extract symbols with language-tuned regexes. Never throws.\n * Caps results so pathological files cannot explode the index.\n */\nexport function parseGeneric(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n /** Soft cap per file (default {@link GENERIC_MAX_SYMBOLS_DEFAULT}). */\n maxSymbols?: number;\n}): FileSymbols {\n const { file, lang } = opts;\n const maxSymbols = opts.maxSymbols ?? GENERIC_MAX_SYMBOLS_DEFAULT;\n const mtimeMs = Date.now();\n\n if (!opts.content || looksBinary(opts.content)) {\n return { file, lang, symbols: [], mtimeMs };\n }\n // Bound CPU: never regex-scan multi-MB blobs in full.\n const content =\n opts.content.length > GENERIC_MAX_FILE_CHARS\n ? opts.content.slice(0, GENERIC_MAX_FILE_CHARS)\n : opts.content;\n\n const patterns = patternsFor(lang);\n const symbols: IndexSymbol[] = [];\n const seen = new Set<string>();\n\n for (const pattern of patterns) {\n // Clone flags so lastIndex never leaks across files.\n const re = new RegExp(pattern.re.source, pattern.re.flags.includes('g') ? pattern.re.flags : `${pattern.re.flags}g`);\n re.lastIndex = 0;\n for (const match of content.matchAll(re)) {\n if (symbols.length >= maxSymbols) break;\n\n let name = (match[1] ?? match[2] ?? '').trim();\n // Markdown headings put the title in group 2\n if (lang === 'md' && match[2]) name = match[2].trim();\n if (!name || name.length > 200) continue;\n // Strip markdown heading markers accidentally captured\n name = name.replace(/^#+\\s*/, '').replace(/[\"'`]/g, '');\n if (!name || KEYWORDS.has(name.toLowerCase())) continue;\n // Reject pure punctuation / numbers\n if (!/^[A-Za-z_#.@/\\w][\\w.\\-:/#!?]*$/.test(name) && lang !== 'md' && lang !== 'toml') {\n continue;\n }\n\n const { line, col } = lineColAt(content, match.index);\n const key = `${name}\\0${line}\\0${pattern.kind}`;\n if (seen.has(key)) continue;\n seen.add(key);\n\n const nl = content.indexOf('\\n', match.index);\n const lineText = content.slice(match.index, nl === -1 ? content.length : nl);\n const signature = (lineText || name).trim().slice(0, 500);\n\n symbols.push({\n id: 0,\n lang,\n kind: lang === 'md' ? 'namespace' : pattern.kind,\n name: name.slice(0, 200),\n file,\n line,\n col,\n signature,\n docComment: '',\n scope: '',\n text: `${name} ${signature}`.trim().slice(0, 1000),\n });\n }\n if (symbols.length >= maxSymbols) break;\n }\n\n return { file, lang, symbols, mtimeMs };\n}\n\n/** Async wrapper matching other parser entrypoints. */\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n return parseGeneric(opts);\n}\n", "/**\n * Python source symbol extraction using the `ast` module.\n *\n * Spawns a `python -c` child process that parses the file with Python's `ast`\n * module and emits JSON. Falls back to empty results on any error.\n *\n * Extracts: class, function, async function, const, var, import, import_from\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { resolveWin32Command } from '../_win32-resolve.js';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { parseGeneric } from './generic-parser.js';\nimport { withSpawnGate } from './spawn-gate.js';\n\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Prefer Python's `ast` when a runtime is available. When Python is missing\n * or the spawn fails, fall back to the generic regex extractor so `.py` files\n * still enter the index instead of being silently empty.\n *\n * Syntax errors from a working Python still return zero symbols (ast cannot\n * recover) \u2014 that is intentional correctness, not a gap in coverage.\n */\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n try {\n // Serialize python child processes process-wide (CPU/spawn cimrili\u011Fi).\n const native = await withSpawnGate(() => syncPyParse(file, content, lang));\n if (native !== null) return native;\n } catch {\n /* fall through to generic */\n }\n return parseGeneric({ file, content, lang: lang === 'py' ? 'py' : lang });\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Inline Python parser script \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst PY_PARSE_SCRIPT = `import ast, json, sys, os\n\ndef get_name(node):\n if isinstance(node, ast.Name):\n return node.id\n elif isinstance(node, ast.Attribute):\n return get_name(node.value) + \".\" + node.attr\n elif isinstance(node, ast.Subscript):\n return get_name(node.value)\n elif isinstance(node, ast.Call):\n return get_name(node.func)\n elif isinstance(node, ast.Constant):\n return str(node.value)\n return \"\"\n\ndef get_decorators(node):\n decs = []\n for dec in node.decorator_list:\n decs.append(get_name(dec))\n return decs\n\ndef get_bases(node):\n bases = []\n for base in node.bases:\n bases.append(get_name(base))\n return bases\n\ndef get_args(args):\n parts = []\n for arg in args.args:\n parts.append(arg.arg)\n return \", \".join(parts)\n\ndef get_returns(node):\n if node.returns is None:\n return \"\"\n return get_name(node.returns)\n\nclass Sym:\n def __init__(self, name, kind, line, col, signature, scope):\n self.name = name\n self.kind = kind\n self.line = line\n self.col = col\n self.signature = signature\n self.scope = scope\n def to_dict(self):\n return {\n \"name\": self.name,\n \"kind\": self.kind,\n \"line\": self.line,\n \"col\": self.col,\n \"signature\": self.signature,\n \"scope\": self.scope,\n }\n\ndef is_private(name):\n return name.startswith(\"__\") and not name.endswith(\"__\")\n\nsyms = []\nerrors = []\n\ntry:\n source = sys.stdin.read()\n tree = ast.parse(source, filename=sys.argv[1])\nexcept Exception as e:\n errors.append(str(e))\n print(\"[]\")\n sys.exit(0)\n\n# Module-level scope\nmodule_scope = os.path.basename(sys.argv[1])[:-3] # strip .py\n\nclass ModuleVisitor(ast.NodeVisitor):\n def __init__(self):\n self.scope_stack = [module_scope]\n\n def visit_ClassDef(self, node):\n bases = get_bases(node)\n decs = get_decorators(node)\n sig = \"class \" + node.name\n if bases:\n sig += \"(\" + \", \".join(bases) + \")\"\n sig += \": ...\"\n syms.append(Sym(\n name=node.name,\n kind=\"class\",\n line=node.lineno,\n col=node.col_offset,\n signature=sig,\n scope=\".\".join(self.scope_stack) + \".\" + node.name,\n ))\n self.scope_stack.append(node.name)\n self.generic_visit(node)\n self.scope_stack.pop()\n\n def visit_FunctionDef(self, node):\n decs = get_decorators(node)\n args = get_args(node.args)\n returns = get_returns(node)\n is_async = isinstance(node, ast.AsyncFunctionDef)\n\n kind = \"function\"\n prefix = \"def \"\n if decs:\n for d in decs:\n if d.endswith(\".staticmethod\"):\n kind = \"staticmethod\"\n elif d.endswith(\".classmethod\"):\n kind = \"classmethod\"\n elif d == \"property\":\n kind = \"property\"\n\n if is_async:\n kind = \"async_\" + kind\n\n sig = f\"{prefix}{node.name}({args})\"\n if returns:\n sig += f\" -> {returns}\"\n scope = \".\".join(self.scope_stack) + \".\" + node.name\n\n syms.append(Sym(\n name=node.name,\n kind=kind,\n line=node.lineno,\n col=node.col_offset,\n signature=sig,\n scope=scope,\n ))\n # Don't descend into function bodies to avoid local symbols\n # self.generic_visit(node)\n\n def visit_AsyncFunctionDef(self, node):\n # Treat as function\n self.visit_FunctionDef(node)\n\n def visit_Assign(self, node):\n for target in node.targets:\n if isinstance(target, ast.Name):\n name = target.id\n if is_private(name):\n continue\n # Infer constness from UPPER_CASE naming\n kind = \"const\" if name.isupper() else \"var\"\n col = target.col_offset if hasattr(target, 'col_offset') else 0\n syms.append(Sym(\n name=name,\n kind=kind,\n line=node.lineno,\n col=col,\n signature=f\"{name} = ...\",\n scope=\".\".join(self.scope_stack),\n ))\n\n def visit_AnnAssign(self, node):\n if isinstance(node.target, ast.Name):\n name = node.target.id\n if is_private(name):\n return\n kind = \"const\" if name.isupper() else \"var\"\n col = node.target.col_offset if hasattr(node.target, 'col_offset') else 0\n sig = f\"{name}: {get_name(node.annotation)}\"\n if node.value:\n sig += \" = ...\"\n syms.append(Sym(\n name=name,\n kind=kind,\n line=node.lineno,\n col=col,\n signature=sig,\n scope=\".\".join(self.scope_stack),\n ))\n\n def visit_Import(self, node):\n for alias in node.names:\n name = alias.asname or alias.name\n syms.append(Sym(\n name=name,\n kind=\"import\",\n line=node.lineno,\n col=node.col_offset,\n signature=f\"import {alias.name}\",\n scope=\".\".join(self.scope_stack),\n ))\n\n def visit_ImportFrom(self, node):\n module = node.module or \"\"\n for alias in node.names:\n name = alias.asname or alias.name\n syms.append(Sym(\n name=name,\n kind=\"import\",\n line=node.lineno,\n col=node.col_offset,\n signature=f\"from {module} import {alias.name}\",\n scope=\".\".join(self.scope_stack),\n ))\n\nvisitor = ModuleVisitor()\nvisitor.visit(tree)\n\nprint(json.dumps([s.to_dict() for s in syms]))\n`;\n\n// \u2500\u2500\u2500 Synchronous Python parse via child process \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Cross-platform Python binary resolver.\n *\n * Windows: walks PATHEXT via `resolveWin32Command` (handles .exe/.cmd/.bat).\n * A match means a real file on disk \u2014 return it immediately.\n * macOS / Linux: `resolveWin32Command` is a pass-through. We asynchronously\n * verify each candidate with `--version`, cached for the process lifetime.\n *\n * Candidates in priority order:\n * Windows: python3 \u2192 python \u2192 py (Python launcher)\n * Unix: python3 \u2192 python\n */\nasync function resolvePython(): Promise<string | null> {\n\tconst candidates = process.platform === 'win32'\n\t\t? ['python3', 'python', 'py']\n\t\t: ['python3', 'python'];\n\tfor (const name of candidates) {\n\t\tconst resolved = resolveWin32Command(name);\n\t\t// On Windows: verify even if resolveWin32Command found a\n\t\t// file \u2014 the WindowsApps redirector stub (python3.exe) passes the\n\t\t// accessSync check but exits with code 9009 (app not found).\n\t\tif (!(await commandIsAvailable(resolved))) continue;\n\t\treturn resolved;\n\t}\n\treturn null;\n}\n\nfunction commandIsAvailable(command: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tconst proc = spawn(command, ['--version'], {\n\t\t\tstdio: 'ignore',\n\t\t\twindowsHide: true,\n\t\t});\n\t\tconst finish = (available: boolean) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolve(available);\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tproc.kill('SIGKILL');\n\t\t\tfinish(false);\n\t\t}, 5_000);\n\t\ttimer.unref?.();\n\t\tproc.once('error', () => finish(false));\n\t\tproc.once('close', (code) => finish(code === 0));\n\t});\n}\n\n/**\n * Spawn the Python parser child process with proper error handling.\n *\n * Returns a promise that resolves to { code, stdout } or rejects with an\n * Error (ENOENT, timeout, spawn error) that the caller converts to empty\n * results. The 'error' event listener is critical: without it, a spawn\n * ENOENT on Windows crashes as an unhandled exception because\n * ChildProcess emits 'error' asynchronously and the Promise.race only\n * listens on 'close'.\n */\nfunction spawnPyParser(\n\tpyBinary: string,\n\tscriptPath: string,\n\tfilePath: string,\n\tcontent: string,\n): Promise<{ code: number | null; stdout: string }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\n\t\tconst proc: ChildProcess = spawn(pyBinary, [scriptPath, filePath], {\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\t// Mandatory: catch ENOENT / permission-denied / spawn failures.\n\t\tproc.on('error', (err) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\treject(err);\n\t\t});\n\n\t\t// Write source content via stdin so the child doesn't reopen the file.\n\t\tproc.stdin?.write(content);\n\t\tproc.stdin?.end();\n\n\t\tlet stdout = '';\n\t\tproc.stdout?.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });\n\n\t\t// Discard stderr to avoid backpressure deadlocks when Python emits\n\t\t// warnings (e.g. deprecation notices).\n\t\tproc.stderr?.resume();\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tproc.kill('SIGKILL');\n\t\t\treject(new Error('timeout'));\n\t\t}, 15_000);\n\t\ttimer.unref?.();\n\n\t\tproc.on('close', (code) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ code, stdout });\n\t\t});\n\t});\n}\n\n// Cache the temp script path + resolved Python binary so we don't rewrite\n// or re-resolve on every file.\nlet _cachedScriptPath: string | null = null;\nlet cachedPyBinary: Promise<string | null> | undefined;\n\n/**\n * Run the real Python AST parser.\n * - `null` \u2192 Python unavailable / spawn failed \u2192 caller should use generic fallback\n * - `FileSymbols` \u2192 Python ran (even if the file was invalid \u2192 empty symbols)\n */\nasync function syncPyParse(\n\tfilePath: string,\n\tcontent: string,\n\tlang: SymbolLang,\n): Promise<FileSymbols | null> {\n\ttry {\n\t\t// Write the parser script once per process \u2014 not per file.\n\t\t// Passing the whole 200-line program via `python -c \"...\"` breaks\n\t\t// under cmd.exe on Windows (embedded newlines truncate the command).\n\t\t// A real file sidesteps all quoting and can be reused across calls.\n\t\tif (!_cachedScriptPath) {\n\t\t\tconst tmpDir = path.join(os.tmpdir(), 'ws-py-parse');\n\t\t\tawait fs.mkdir(tmpDir, { recursive: true });\n\t\t\t_cachedScriptPath = path.join(tmpDir, 'parse.py');\n\t\t\tawait fs.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, 'utf8');\n\t\t}\n\n\t\t// Resolve Python binary once (expensive: walks PATH on Windows).\n\t\tcachedPyBinary ??= resolvePython();\n\t\tconst pyBinary = await cachedPyBinary;\n\t\tif (!pyBinary) return null;\n\n\t\t// argv-array form: no shell, so a hostile filename cannot inject commands.\n\t\t// Content is piped via stdin \u2014 avoids a second file read in the child.\n\t\tconst { code, stdout } = await spawnPyParser(\n\t\t\tpyBinary,\n\t\t\t_cachedScriptPath,\n\t\t\tfilePath,\n\t\t\tcontent,\n\t\t);\n\n\t\tif (code !== 0 || !stdout.trim()) {\n\t\t\t// Python ran but AST parse failed (syntax error) \u2014 empty, not fallback.\n\t\t\treturn { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n\t\t}\n\n\t\tconst raw = JSON.parse(stdout.trim()) as Array<{\n\t\t\tname: string;\n\t\t\tkind: string;\n\t\t\tline: number;\n\t\t\tcol: number;\n\t\t\tsignature: string;\n\t\t\tscope: string;\n\t\t}>;\n\t\tconst symbols: IndexSymbol[] = raw.map((s) => ({\n\t\t\tid: 0,\n\t\t\tlang,\n\t\t\tkind: s.kind as IndexSymbol['kind'],\n\t\t\tname: s.name,\n\t\t\tfile: filePath,\n\t\t\tline: s.line,\n\t\t\tcol: s.col,\n\t\t\tsignature: s.signature ?? '',\n\t\t\tdocComment: '',\n\t\t\tscope: s.scope ?? '',\n\t\t\ttext: `${s.name} ${s.signature ?? ''}`.trim(),\n\t\t}));\n\t\treturn { file: filePath, lang, symbols, mtimeMs: Date.now() };\n\t} catch {\n\t\t// Spawn/IO failure \u2192 generic fallback.\n\t\treturn null;\n\t}\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * Rust source symbol extraction.\n *\n * Tries to use the native `syn` crate via a cargo subproject (tools/syn-parser/).\n * Falls back to a robust regex-based extractor when cargo/syn is not available.\n *\n * The regex fallback extracts: fn, struct, enum, trait, impl, type, const, static, mod\n */\n\nimport { execFile, spawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveWin32Command } from '../_win32-resolve.js';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { withSpawnGate } from './spawn-gate.js';\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n // Try native parser first, fall back to regex\n const nativeAvailable = await checkNativeParser();\n if (nativeAvailable) {\n const result = await withSpawnGate(() => tryNativeParse(file, content));\n if (result) return result;\n }\n\n return regexParse({ file, content, lang });\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Native parser (syn) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Cache the native-parser availability check so we don't spawn `rustc` and\n// `cargo metadata` on every file. The result is constant for the process\n// lifetime \u2014 the toolchain either exists or it doesn't.\nlet nativeParserAvailability: Promise<boolean> | undefined;\n\nfunction probe(command: string, args: string[]): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(command, args, { timeout: 10_000, windowsHide: true }, (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n}\n\nfunction checkNativeParser(): Promise<boolean> {\n nativeParserAvailability ??= (async () => {\n try {\n await probe('rustc', ['--version']);\n // Check if our syn-parser crate is available. argv-array form (no shell)\n // so a cwd path containing spaces or shell metacharacters can't break out.\n const toolsDir = path.join(process.cwd(), 'tools');\n await probe(\n 'cargo',\n [\n 'metadata',\n '--no-deps',\n '--format-version',\n '1',\n '--manifest-path',\n path.join(toolsDir, 'Cargo.toml'),\n ],\n );\n return true;\n } catch {\n return false;\n }\n })();\n return nativeParserAvailability;\n}\n\nasync function tryNativeParse(file: string, content: string): Promise<FileSymbols | null> {\n try {\n const toolsDir = path.join(process.cwd(), 'tools');\n const crateDir = path.join(toolsDir, 'syn-parser');\n\n // Write source to temp file for cargo to read (async \u2014 non-blocking)\n const tmpFile = path.join(crateDir, 'src', 'input.rs');\n await fs.writeFile(tmpFile, content, 'utf8');\n\n // Use spawn for full async control with timeout via Promise.race + setTimeout kill\n // Resolve via PATHEXT on Windows so ENOENT is impossible (cargo is a .cmd wrapper).\n const cargoBinary = resolveWin32Command('cargo');\n\n const result = await new Promise<{ code: number | null; stdout: string }>(\n (resolve, reject) => {\n let settled = false;\n\n const proc: ChildProcessWithoutNullStreams = spawn(\n cargoBinary,\n ['run', '--manifest-path', path.join(toolsDir, 'Cargo.toml')],\n {\n cwd: process.cwd(),\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n },\n );\n\n proc.on('error', (err) => {\n if (settled) return;\n settled = true;\n reject(err);\n });\n\n let stdout = '';\n proc.stdout?.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });\n proc.stderr?.resume();\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n proc.kill('SIGKILL');\n reject(new Error('timeout'));\n }, 15_000);\n timer.unref?.();\n\n proc.on('close', (c: number | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({ code: c, stdout });\n });\n },\n );\n\n const { code, stdout } = result;\n\n if (code === 0 && stdout.trim()) {\n const symbols: IndexSymbol[] = JSON.parse(stdout.trim());\n return {\n file,\n lang: 'rs',\n symbols: symbols.map((s) => ({ ...s, id: 0, lang: 'rs' as SymbolLang })),\n mtimeMs: Date.now(),\n };\n }\n } catch {\n // Fall through to regex\n }\n return null;\n}\n\n// \u2500\u2500\u2500 Regex fallback parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface RustPattern {\n regex: RegExp;\n kind: IndexSymbol['kind'];\n}\n\nconst RS_PATTERNS: RustPattern[] = [\n { regex: /fn\\s+(\\w+)\\s*\\([^)]*\\)/g, kind: 'function' },\n { regex: /struct\\s+(\\w+)/g, kind: 'struct' },\n { regex: /enum\\s+(\\w+)/g, kind: 'enum' },\n { regex: /trait\\s+(\\w+)/g, kind: 'trait' },\n { regex: /impl\\s+(?:<[^>]+>)?(\\w+)/g, kind: 'impl' },\n { regex: /type\\s+(\\w+)\\s*=/g, kind: 'type' },\n { regex: /const\\s+(\\w+)/g, kind: 'const' },\n { regex: /static\\s+(\\w+)/g, kind: 'static' },\n { regex: /mod\\s+(\\w+)/g, kind: 'mod' },\n];\n\nfunction regexParse(opts: { file: string; content: string; lang: SymbolLang }): FileSymbols {\n const { file, content, lang } = opts;\n const symbols: IndexSymbol[] = [];\n const lines = content.split('\\n');\n\n // Build line offset map\n const lineOffsets: number[] = [0];\n for (let i = 0; i < lines.length; i++) {\n lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);\n }\n\n function lineFromOffset(offset: number): number {\n let lo = 0;\n let hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1; // 1-based\n }\n\n function extractDeclaration(lineIdx: number, _match: RegExpExecArray): string {\n const line = lines[lineIdx] ?? '';\n return line.trim().slice(0, 500);\n }\n\n for (const pattern of RS_PATTERNS) {\n pattern.regex.lastIndex = 0;\n for (\n let match = pattern.regex.exec(content);\n match !== null;\n match = pattern.regex.exec(content)\n ) {\n const name = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n const lineIdx = line - 1;\n const signature = extractDeclaration(lineIdx, match);\n\n symbols.push({\n id: 0,\n lang,\n kind: pattern.kind,\n name,\n file,\n line,\n col,\n signature,\n docComment: '',\n scope: '',\n text: `${name} ${signature}`.trim(),\n });\n }\n }\n\n // Deduplicate by name+line\n const seen = new Set<string>();\n const deduped = symbols.filter((s) => {\n const key = `${s.name}:${s.line}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n return { file, lang, symbols: deduped, mtimeMs: Date.now() };\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * JSON file symbol extraction.\n *\n * Extracts top-level keys as \"symbols\" with kind `property`.\n * Special handling for:\n * - package.json: scripts, dependencies, devDependencies \u2192 `const`\n * - tsconfig.json: compilerOptions keys \u2192 `property`\n * - JSON Schema / OpenAPI: $schema, $id, $ref \u2192 `schema`\n * - Root object itself \u2192 kind `object`\n *\n * Uses regex-based extraction for speed and zero dependencies.\n */\n\nimport * as path from 'node:path';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): FileSymbols {\n const { file, content, lang } = opts;\n\n try {\n return regexParse({ file, content, lang });\n } catch {\n /* v8 ignore next -- regexParse is pure regex/string work; the catch is a defensive fallback. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Regex parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\n/**\n * Extract key-value pairs from JSON content using regex.\n * Handles: \"key\": value, arrays with keyed objects, nested objects (depth \u2264 3).\n */\nfunction regexParse(opts: { file: string; content: string; lang: SymbolLang }): FileSymbols {\n const { file, content, lang } = opts;\n const symbols: IndexSymbol[] = [];\n const basename = path.basename(file).toLowerCase();\n\n const isPackageJson = basename === 'package.json';\n const isTsconfig = basename === 'tsconfig.json' || basename === 'tsconfig.build.json';\n const isJsonSchema =\n content.includes('$schema') || content.includes('$id') || content.includes('$ref');\n const isOpenApi = content.includes('openapi') || content.includes('swagger');\n\n const lines = content.split('\\n');\n\n // Build line offset map\n const lineOffsets: number[] = [0];\n for (let i = 0; i < lines.length; i++) {\n lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);\n }\n\n function lineFromOffset(offset: number): number {\n let lo = 0;\n let hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n }\n\n // Root object symbol\n const rootMatch = content.match(/^\\s*\\{/m);\n if (rootMatch) {\n const offset = expectDefined(rootMatch.index);\n const line = lineFromOffset(offset);\n symbols.push(\n makeSymbol({\n name: path.basename(file),\n kind: 'object',\n line,\n col: 0,\n signature: `\"${path.basename(file)}\" = { ... }`,\n file,\n lang,\n }),\n );\n }\n\n // Extract top-level keys\n const topLevelKeyRegex = /^\\s*\"([^\"]+)\"\\s*:/gm;\n for (\n let match = topLevelKeyRegex.exec(content);\n match !== null;\n match = topLevelKeyRegex.exec(content)\n ) {\n const key = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n\n let kind: IndexSymbol['kind'] = 'property';\n let signature = `\"${key}\": ...\"`;\n\n // Special casing for known file types\n if (isPackageJson) {\n if (\n key === 'scripts' ||\n key === 'dependencies' ||\n key === 'devDependencies' ||\n key === 'peerDependencies' ||\n key === 'optionalDependencies'\n ) {\n kind = 'const';\n signature = `\"${key}\": { ... }`;\n }\n } else if (isTsconfig) {\n if (key === 'compilerOptions') {\n kind = 'property';\n signature = `\"compilerOptions\": { ... }`;\n }\n }\n\n // JSON Schema / OpenAPI special keys\n if (isJsonSchema || isOpenApi) {\n if (key === '$schema' || key === '$id') {\n kind = 'schema';\n signature = `\"${key}\": \"...\"`;\n } else if (key === '$ref') {\n kind = 'schema';\n signature = `\"$ref\": \"...\"`;\n }\n }\n\n symbols.push(\n makeSymbol({\n name: key,\n kind,\n line,\n col,\n signature,\n file,\n lang,\n }),\n );\n\n // For package.json, also extract individual scripts as 'function'\n if (isPackageJson && key === 'scripts') {\n extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset);\n }\n\n // For tsconfig.json compilerOptions, extract nested keys\n if (isTsconfig && key === 'compilerOptions') {\n extractCompilerOptions(content, symbols, file, lang, lineOffsets, line, lineFromOffset);\n }\n }\n\n // Extract JSON Schema $defs or definitions\n const defsRegex = /\"\\$defs\"\\s*:|\"\\$defs\"\\s*:/g;\n const defsMatch = defsRegex.exec(content);\n if (defsMatch !== null) {\n const offset = expectDefined(defsMatch.index);\n const line = lineFromOffset(offset);\n symbols.push(\n makeSymbol({\n name: '$defs',\n kind: 'property',\n line,\n col: offset - (lineOffsets[line - 1] ?? 0),\n signature: '\"$defs\": { ... }',\n file,\n lang,\n }),\n );\n }\n\n // Extract definitions (OpenAPI components, JSON Schema definitions)\n const defsPatterns = [\n /\"\\$defs\"\\s*:/g,\n /\"definitions\"\\s*:/g,\n /\"components\"\\s*:/g,\n /\"schemas\"\\s*:/g,\n ];\n for (const pat of defsPatterns) {\n pat.lastIndex = 0;\n for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const key = match[0]?.match(/\"([^\"]+)\"/)?.[1] ?? expectDefined(match[0]);\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'property',\n line,\n col: offset - (lineOffsets[line - 1] ?? 0),\n signature: `\"${key}\": { ... }`,\n file,\n lang,\n }),\n );\n }\n }\n\n return { file, lang, symbols, mtimeMs: Date.now() };\n}\n\nfunction extractPackageScripts(\n content: string,\n symbols: IndexSymbol[],\n file: string,\n lang: SymbolLang,\n lineOffsets: number[],\n lineFromOffset: (offset: number) => number,\n): void {\n // Find the \"scripts\": { ... } block and extract each script key\n const scriptsBlockRegex = /\"scripts\"\\s*:\\s*\\{([^}]+)\\}/g;\n for (\n let match = scriptsBlockRegex.exec(content);\n match !== null;\n match = scriptsBlockRegex.exec(content)\n ) {\n const blockContent = expectDefined(match[0]);\n const blockOffset = (match.index ?? 0);\n\n // Extract each \"key\" inside the block (simple approach)\n const scriptKeyRegex = /\"(\\w[\\w-]*)\"\\s*:/g;\n for (\n let scriptMatch = scriptKeyRegex.exec(blockContent);\n scriptMatch !== null;\n scriptMatch = scriptKeyRegex.exec(blockContent)\n ) {\n const key = expectDefined(scriptMatch[1]);\n const keyOffset = blockOffset + expectDefined(scriptMatch.index);\n const line = lineFromOffset(keyOffset);\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'function',\n line,\n col: keyOffset - (lineOffsets[line - 1] ?? 0),\n signature: `\"${key}\": \"...\"`,\n file,\n lang,\n }),\n );\n }\n }\n}\n\nfunction extractCompilerOptions(\n content: string,\n symbols: IndexSymbol[],\n file: string,\n lang: SymbolLang,\n lineOffsets: number[],\n parentLine: number,\n lineFromOffset: (offset: number) => number,\n): void {\n // Find the \"compilerOptions\": { ... } block\n const optsBlockRegex = /\"compilerOptions\"\\s*:\\s*\\{([^}]+)\\}/g;\n for (\n let match = optsBlockRegex.exec(content);\n match !== null;\n match = optsBlockRegex.exec(content)\n ) {\n const blockContent = expectDefined(match[0]);\n const blockOffset = (match.index ?? 0);\n\n // Extract nested key inside compilerOptions (up to depth 1)\n const optKeyRegex = /\"(\\w[\\w]*)\"\\s*:/g;\n for (\n let optMatch = optKeyRegex.exec(blockContent);\n optMatch !== null;\n optMatch = optKeyRegex.exec(blockContent)\n ) {\n const key = expectDefined(optMatch[1]);\n const keyOffset = blockOffset + expectDefined(optMatch.index);\n const line = lineFromOffset(keyOffset);\n if (line <= parentLine) continue; // Skip top-level (already captured)\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'property',\n line,\n col: keyOffset - (lineOffsets[line - 1] ?? 0),\n signature: `\"${key}\": ...`,\n file,\n lang,\n }),\n );\n }\n }\n}\n\nfunction makeSymbol(opts: {\n name: string;\n kind: IndexSymbol['kind'];\n line: number;\n col: number;\n signature: string;\n file: string;\n lang: SymbolLang;\n}): IndexSymbol {\n return {\n id: 0,\n lang: opts.lang,\n kind: opts.kind,\n name: opts.name,\n file: opts.file,\n line: opts.line,\n col: opts.col,\n signature: opts.signature,\n docComment: '',\n scope: '',\n text: `${opts.name} ${opts.signature}`.trim(),\n };\n}\n", "import { expectDefined, truncate } from '@wrongstack/core/utils';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): FileSymbols {\n const { file, content, lang } = opts;\n\n try {\n return regexParse({ file, content, lang });\n } catch {\n /* v8 ignore next -- regexParse is pure regex/string work; the catch is a defensive fallback. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Regex parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction regexParse(opts: { file: string; content: string; lang: SymbolLang }): FileSymbols {\n const { file, content, lang } = opts;\n const symbols: IndexSymbol[] = [];\n\n const lines = content.split('\\n');\n\n // Build line offset map for accurate line/col\n const lineOffsets: number[] = [0];\n for (let i = 0; i < lines.length; i++) {\n lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);\n }\n\n function lineFromOffset(offset: number): number {\n let lo = 0;\n let hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n }\n\n // \u2500\u2500 1. Anchors and aliases \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // &anchor_name\n const anchorRegex = /&(\\w[\\w-]*)/g;\n for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {\n const name = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n symbols.push(\n makeSymbol({\n name,\n kind: 'const',\n line,\n col,\n signature: `&${name}`,\n file,\n lang,\n }),\n );\n }\n\n // *alias_name\n const aliasRegex = /\\*(\\w[\\w-]*)/g;\n for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {\n const name = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n symbols.push(\n makeSymbol({\n name,\n kind: 'const',\n line,\n col,\n signature: `*${name}`,\n file,\n lang,\n }),\n );\n }\n\n // \u2500\u2500 2. Top-level and nested key: value pairs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Matches `key: value` (but not block scalars or document markers)\n // Uses negative lookbehind and context to avoid false positives\n const kvRegex = /^(\\s*)([^:#\\s][^:#\\s]*)\\s*:/gm;\n for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {\n const indent = match[1]?.length ?? 0;\n const key = match[2];\n /* v8 ignore next -- the capture group always matches \u22651 char, so key is never empty; defensive. */\n if (!key) continue;\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n\n // Skip block scalar indicators (| or > at column 0 with key name before :)\n const lineContent = lines[line - 1] ?? '';\n if (/^[|&>]/.test(lineContent.trim())) continue;\n // Skip YAML document markers\n if (key === '---' || key === '...') continue;\n // Skip keys that are clearly part of a string value (unusual indent)\n if (indent > 12) continue;\n\n const value = extractValue(content, (match.index ?? 0));\n const kind: IndexSymbol['kind'] = isScalar(value) ? 'literal' : 'property';\n const signature = `${key}: ${truncate(value, 60)}`;\n\n symbols.push(makeSymbol({ name: key, kind, line, col, signature, file, lang }));\n }\n\n // \u2500\u2500 3. List item keys \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // `- key: value` (list item that is a keyed object)\n const listItemRegex = /^-(\\s+)([^:#\\s][^:#\\s]*)\\s*:/gm;\n for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {\n const key = expectDefined(match[2]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n const value = extractValue(content, offset + match[0]?.length);\n const kind: IndexSymbol['kind'] = isScalar(value) ? 'literal' : 'property';\n symbols.push(\n makeSymbol({\n name: key,\n kind,\n line,\n col,\n signature: `- ${key}: ${truncate(value, 60)}`,\n file,\n lang,\n }),\n );\n }\n\n // \u2500\u2500 4. Block scalar keys (key: | or key: >) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const blockScalarRegex = /^(\\s*)([^:#\\s][^:#\\s]*)\\s*:\\s*[|>](\\s|$)/gm;\n for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {\n const key = expectDefined(match[2]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'property',\n line,\n col,\n signature: `${key}: | ...`,\n file,\n lang,\n }),\n );\n }\n\n return { file, lang, symbols, mtimeMs: Date.now() };\n}\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractValue(content: string, afterColonOffset: number): string {\n // Get the rest of the line after the colon\n const lineEnd = content.indexOf('\\n', afterColonOffset);\n const rest = content.slice(afterColonOffset, lineEnd < 0 ? undefined : lineEnd);\n return rest.trim();\n}\n\nfunction isScalar(value: string): boolean {\n if (!value) return false;\n // Numbers, booleans, null, quoted strings\n if (/^-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?$/.test(value)) return true;\n if (/^(true|false|null|undefined)$/i.test(value)) return true;\n if (/^'[^']*'$/.test(value) || /^\"[^\"]*\"$/.test(value)) return true;\n return false;\n}\n\nfunction makeSymbol(opts: {\n name: string;\n kind: IndexSymbol['kind'];\n line: number;\n col: number;\n signature: string;\n file: string;\n lang: SymbolLang;\n}): IndexSymbol {\n return {\n id: 0,\n lang: opts.lang,\n kind: opts.kind,\n name: opts.name,\n file: opts.file,\n line: opts.line,\n col: opts.col,\n signature: opts.signature,\n docComment: '',\n scope: '',\n text: `${opts.name} ${opts.signature}`.trim(),\n };\n}\n", "import * as fs from 'node:fs/promises';\nimport { FsError, type Tool, ToolValidationError } from '@wrongstack/core/types';\nimport { toErrorMessage } from '@wrongstack/core/utils';\nimport { isBinaryBuffer, safeResolveReal, sha256hex } from './_util.js';\nimport { getIndexState, searchCodebaseIndex } from './codebase-index/background-indexer.js';\nimport type { SymbolKind } from './codebase-index/schema.js';\nimport { codebaseIndexDirOverride } from './codebase-index/writer-helpers.js';\n\n/**\n * Meta key for advanced mode. When `true` in `ctx.meta`, the read tool\n * automatically injects codebase-index symbols for the file being read\n * as a structured `symbols` field. Set via:\n * ctx.meta['tools.read.advancedMode'] = true\n * A per-call `includeSymbols` parameter overrides this flag.\n */\nconst ADVANCED_MODE_META_KEY = 'tools.read.advancedMode';\n\ninterface ReadInput {\n path: string;\n offset?: number | undefined;\n limit?: number | undefined;\n mode?: 'content' | 'summary' | undefined;\n /**\n * When true, include the codebase-index symbol list for this file\n * as a structured `symbols` field in the result. Overrides the\n * advanced-mode meta flag (`ctx.meta['tools.read.advancedMode']`)\n * per-call when explicitly set. When omitted, the meta flag governs.\n */\n includeSymbols?: boolean | undefined;\n}\n\n/**\n * A single indexed code symbol returned alongside file content when\n * advanced mode is on or `includeSymbols` is set. The LLM must treat\n * this as a symbol listing \u2014 not as file content.\n */\ninterface SymbolEntry {\n /** Symbol name (e.g. myFunction, MyClass). */\n name: string;\n /** Kind of symbol (function, class, interface, const, etc.). */\n kind: SymbolKind;\n /** 1-based declaration line in the file. */\n line: number;\n /** 0-based column offset. */\n col: number;\n /** Full signature or declaration text. */\n signature: string;\n}\n\ninterface ReadOutput {\n text: string;\n total_lines: number;\n encoding: string;\n truncated: boolean;\n cached?: boolean | undefined;\n note?: string | undefined;\n /**\n * Codebase-index symbols for this file, included when advanced mode\n * is active or `includeSymbols` is set. One entry per indexed symbol,\n * sorted by line number. This is a symbol listing \u2014 NOT file content.\n */\n symbols?: SymbolEntry[] | undefined;\n}\n\nconst MAX_BYTES = 5 * 1024 * 1024;\n\nexport const readTool: Tool<ReadInput, ReadOutput> = {\n name: 'read',\n category: 'Filesystem',\n description:\n 'Read the contents of a file with line numbers. This is the primary way to inspect source code, configuration, or any text file before making changes. ' +\n 'Lines are returned 1-indexed with a ` N| ` prefix for easy reference in edits. ' +\n 'When advanced mode is on or `includeSymbols` is set, the result also includes a `symbols` field ' +\n 'listing codebase-index symbol names, kinds, and line numbers for the file (not file content).',\n usageHint:\n 'FOUNDATIONAL TOOL \u2014 call this before almost any edit operation.\\n\\n' +\n 'Best practices:\\n' +\n '- Always read a file before using `edit`, `replace`, or `write` on it (the system often requires it for safety).\\n' +\n '- Use `offset` + `limit` for very large files instead of reading everything at once.\\n' +\n '- Default limit is generous (2000 lines) but can be increased.\\n' +\n '- The output format is designed to be directly usable as context for `edit` operations.\\n' +\n '- Set `includeSymbols: true` to also receive the codebase-index symbol listing for the file.\\n' +\n \"- Enable advanced mode (`ctx.meta['tools.read.advancedMode'] = true`) to auto-inject symbols on every read.\",\n selection: {\n doNotUseWhen: 'you need to search many files for matching content.',\n useInstead: ['grep'],\n },\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n icon: 'file',\n maxOutputBytes: 262_144,\n timeoutMs: 5_000,\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Path to the file (relative to project root or absolute within project).',\n },\n offset: {\n type: 'integer',\n description: '1-based starting line number. Use together with `limit` for large files.',\n },\n limit: {\n type: 'integer',\n description: 'Maximum number of lines to return (default is 2000).',\n },\n mode: {\n type: 'string',\n enum: ['content', 'summary'],\n description:\n 'Return full line-numbered content (default) or a compact file summary with imports/exports/symbols.',\n },\n includeSymbols: {\n type: 'boolean',\n description:\n 'When true, include the codebase-index symbol list for this file as a structured `symbols` field ' +\n 'in the result. Overrides the advanced-mode meta flag per-call.',\n },\n },\n required: ['path'],\n },\n async execute(input, ctx, execOpts) {\n if (!input?.path) {\n throw new ToolValidationError({\n message: 'read: path is required',\n field: 'path',\n });\n }\n const absPath = await safeResolveReal(input.path, ctx);\n\n // Determine whether to include symbols: per-call param overrides meta flag.\n const shouldIncludeSymbols =\n input.includeSymbols === true ||\n (input.includeSymbols !== false && ctx.meta[ADVANCED_MODE_META_KEY] === true);\n\n let stat: Awaited<ReturnType<typeof fs.stat>>;\n try {\n stat = await fs.stat(absPath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new FsError({\n message: `read: file not found \"${input.path}\"`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { errno: 'ENOENT' },\n });\n }\n throw new FsError({\n message: `read: failed to stat \"${input.path}\": ${toErrorMessage(err)}`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { errno: code },\n cause: err,\n });\n }\n if (!stat.isFile()) {\n throw new FsError({\n message: `read: \"${input.path}\" is not a regular file`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { reason: 'not-a-regular-file' },\n });\n }\n if (stat.size > MAX_BYTES) {\n throw new FsError({\n message: `read: file too large (${stat.size} bytes, limit ${MAX_BYTES})`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { size: stat.size, limit: MAX_BYTES, reason: 'too-large' },\n });\n }\n\n const offset = Math.max(1, input.offset ?? 1);\n const limit = Math.max(0, Math.min(input.limit ?? 2000, 5000));\n const prior = getReadRangeRecord(ctx, absPath);\n const requestedEnd = prior\n ? Math.min(offset + limit - 1, prior.totalLines)\n : offset + limit - 1;\n if (\n input.mode !== 'summary' &&\n limit > 0 &&\n prior &&\n coversRange(prior, stat.mtimeMs, offset, requestedEnd)\n ) {\n ctx.recordRead(absPath, stat.mtimeMs);\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text:\n `[unchanged since previous read: \"${input.path}\" mtime=${Math.round(stat.mtimeMs)}; ` +\n `requested lines ${offset}-${requestedEnd} were already shown. Use offset/limit for a new range if needed.]`,\n total_lines: prior.totalLines,\n encoding: 'utf8',\n truncated: requestedEnd < prior.totalLines,\n cached: true,\n note: mergeSymbolNote('Repeated read suppressed to save tokens.', symResult?.note),\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n };\n }\n\n const buf = await fs.readFile(absPath);\n if (isBinaryBuffer(buf)) {\n throw new Error(`read: \"${input.path}\" appears to be binary`);\n }\n\n const text = buf.toString('utf8');\n // Content hash recorded alongside the mtime: `edit` uses it as the\n // authoritative staleness check (mtime alone has a 2 s tolerance window\n // on Windows). The full file is read even for offset/limit slices, so\n // the hash always covers the whole content.\n const contentHash = sha256hex(text);\n const allLines = text.split(/\\r\\n|\\r|\\n/);\n const total = allLines.length;\n\n if (input.mode === 'summary') {\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, 1, Math.min(total, 200));\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: summarizeFile(input.path, stat.size, allLines),\n total_lines: total,\n encoding: 'utf8',\n truncated: total > 200,\n note: mergeSymbolNote(\n 'Summary mode returned compact structure instead of full file content.',\n symResult?.note,\n ),\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n };\n }\n if (limit === 0) {\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, 1, 0);\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: '',\n total_lines: total,\n encoding: 'utf8',\n truncated: total > 0,\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n ...(symResult?.note ? { note: symResult.note } : {}),\n };\n }\n // Offset past EOF: return an explicit message instead of an empty string.\n // Without this, models with weak instruction-following (e.g. k2p7) see an\n // empty result, assume the read failed transiently, and retry the exact\n // same offset indefinitely \u2014 a tight tool-use loop that burns iterations\n // and context without making progress.\n if (offset > total) {\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, total + 1, total + 1);\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: `[offset ${offset} is past end of file \"${input.path}\" \u2014 file has ${total} line(s). Do not retry this offset.]`,\n total_lines: total,\n encoding: 'utf8',\n truncated: false,\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n ...(symResult?.note ? { note: symResult.note } : {}),\n };\n }\n\n const slice = allLines.slice(offset - 1, offset - 1 + limit);\n const truncated = offset - 1 + slice.length < total;\n\n const width = String(offset + slice.length - 1).length;\n const numbered = slice\n .map((line, i) => `${String(offset + i).padStart(width, ' ')}\u2192${line}`)\n .join('\\n');\n\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, offset, offset + slice.length - 1);\n\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: numbered,\n total_lines: total,\n encoding: 'utf8',\n truncated,\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n ...(symResult?.note ? { note: symResult.note } : {}),\n };\n },\n};\n\n/**\n * Best-effort fetch of codebase-index symbols for a file. Returns\n * sorted symbol entries (or `undefined` when the index is unavailable\n * or has no symbols for this file) and an optional truncation note.\n * Never throws \u2014 symbol injection must never break the read operation.\n */\nasync function fetchSymbolsForFile(\n absPath: string,\n ctx: import('@wrongstack/core/agent').Context,\n signal?: AbortSignal,\n): Promise<{ symbols?: SymbolEntry[]; note?: string }> {\n try {\n const state = getIndexState();\n if (!state.ready) return {};\n\n const { results, total } = await searchCodebaseIndex(\n {\n projectRoot: ctx.projectRoot,\n indexDir: codebaseIndexDirOverride(ctx),\n query: '',\n file: absPath,\n limit: 500,\n },\n { signal },\n );\n\n if (results.length === 0) return {};\n\n const sorted = results\n .map((r) => ({\n name: r.name,\n kind: r.kind,\n line: r.line,\n col: r.col,\n signature: r.signature,\n }))\n .sort((a, b) => a.line - b.line || a.col - b.col);\n\n const result: { symbols: SymbolEntry[]; note?: string } = { symbols: sorted };\n if (total > results.length) {\n result.note = `Symbol listing truncated to ${results.length} of ${total} entries.`;\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/** Merge an optional symbol-truncation note into an existing or absent note field. */\nfunction mergeSymbolNote(\n note: string | undefined,\n symNote: string | undefined,\n): string | undefined {\n if (!symNote) return note;\n if (!note) return symNote;\n return `${note} ${symNote}`;\n}\n\ninterface ReadRangeRecord {\n mtimeMs: number;\n totalLines: number;\n ranges: Array<{ start: number; end: number }>;\n}\n\nconst READ_RANGES_META_KEY = 'tools.read.ranges.v1';\n\nfunction getReadRanges(\n ctx: import('@wrongstack/core/agent').Context,\n): Record<string, ReadRangeRecord> {\n const existing = ctx.meta[READ_RANGES_META_KEY];\n if (existing && typeof existing === 'object' && !Array.isArray(existing)) {\n return existing as Record<string, ReadRangeRecord>;\n }\n const next: Record<string, ReadRangeRecord> = {};\n ctx.meta[READ_RANGES_META_KEY] = next;\n return next;\n}\n\nfunction getReadRangeRecord(\n ctx: import('@wrongstack/core/agent').Context,\n absPath: string,\n): ReadRangeRecord | undefined {\n return getReadRanges(ctx)[absPath];\n}\n\nfunction rememberReadRange(\n ctx: import('@wrongstack/core/agent').Context,\n absPath: string,\n mtimeMs: number,\n totalLines: number,\n start: number,\n end: number,\n): void {\n if (end < start) return;\n const ranges = getReadRanges(ctx);\n const prior = ranges[absPath];\n const nextRanges = prior && Math.abs(prior.mtimeMs - mtimeMs) <= 1 ? prior.ranges.slice() : [];\n nextRanges.push({ start, end });\n ranges[absPath] = {\n mtimeMs,\n totalLines,\n ranges: mergeRanges(nextRanges),\n };\n}\n\nfunction coversRange(\n record: ReadRangeRecord,\n mtimeMs: number,\n start: number,\n end: number,\n): boolean {\n if (Math.abs(record.mtimeMs - mtimeMs) > 1) return false;\n return record.ranges.some((range) => range.start <= start && range.end >= end);\n}\n\nfunction mergeRanges(\n ranges: Array<{ start: number; end: number }>,\n): Array<{ start: number; end: number }> {\n const sorted = ranges.slice().sort((a, b) => a.start - b.start);\n const merged: Array<{ start: number; end: number }> = [];\n for (const range of sorted) {\n const last = merged[merged.length - 1];\n if (!last || range.start > last.end + 1) {\n merged.push({ ...range });\n continue;\n }\n last.end = Math.max(last.end, range.end);\n }\n return merged;\n}\n\nfunction summarizeFile(filePath: string, bytes: number, lines: string[]): string {\n const interesting = lines\n .map((line, index) => ({ line: line.trim(), number: index + 1 }))\n .filter(({ line }) =>\n /^(import\\s|export\\s|class\\s|interface\\s|type\\s|function\\s|const\\s+\\w+\\s*=|let\\s+\\w+\\s*=|var\\s+\\w+\\s*=|def\\s+|async\\s+function\\s)/.test(\n line,\n ),\n )\n .slice(0, 80)\n .map(({ line, number }) => `${number}: ${line}`);\n return [\n `summary: ${filePath}`,\n `bytes=${bytes}`,\n `total_lines=${lines.length}`,\n interesting.length > 0\n ? `symbols/imports:\\n${interesting.join('\\n')}`\n : 'symbols/imports: (none detected)',\n ].join('\\n');\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core/utils';\nimport type { Context } from '@wrongstack/core/agent';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n", "/**\n * Index host \u2014 the main-thread coordinator for all codebase-index operations.\n *\n * Production mode runs every operation (full scans, per-file reindexes,\n * searches, stats) in a dedicated worker thread (`worker.ts`), so the\n * synchronous `node:sqlite` calls and the TypeScript parser can never block\n * the main event loop \u2014 the failure mode that used to freeze terminals is\n * structurally impossible. When the built worker file is not present (tests\n * run from source, exotic runtimes) or `WRONGSTACK_INDEX_INLINE=1` is set,\n * operations fall back to running inline through the same service layer.\n *\n * Concerns owned here, in front of either execution mode:\n *\n * 1. **Serialization** \u2014 every write run (startup scan, per-edit incremental,\n * external file-watch, manual reindex) goes through one process-wide\n * promise-chain mutex so two runs never race the same `index.db` writer.\n * 2. **Debounce** \u2014 rapid successive edits to the same file coalesce, then\n * files that become ready in the same event-loop turn share one index run.\n * 3. **Watchdog** \u2014 every operation is raced against a timeout. In worker\n * mode a timeout hard-terminates the worker (it respawns lazily on the\n * next request); inline it aborts the run's signal. Either way the mutex\n * chain always advances and the promise always settles.\n * 4. **Circuit breaker** \u2014 repeated failures/timeouts pause indexing instead\n * of queuing more work behind a wedged pipeline. See circuit-breaker.ts.\n * 5. **State tracking** \u2014 ready/indexing/progress flags + change listeners\n * for the TUI status chip and the search/stats tools' gating.\n */\n\nimport * as fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { Worker } from 'node:worker_threads';\nimport {\n CircuitOpenError,\n type CircuitSnapshot,\n IndexTimeoutError,\n indexCircuitBreaker,\n LockError,\n} from './circuit-breaker.js';\nimport {\n fileGraphService as fileGraphServiceInline,\n indexService,\n packageGraphService as packageGraphServiceInline,\n searchService,\n statsService,\n symbolGraphService as symbolGraphServiceInline,\n} from './index-service.js';\nimport { isIndexablePath } from './languages.js';\nimport {\n callProjectIndexServer,\n checkProjectIndexServerHealth,\n closeProjectIndexServerClients,\n ensureProjectIndexServer,\n getProjectIndexServerConnectionState,\n isProjectIndexServerAvailable,\n resolveProjectIndexDaemonAvailability,\n onProjectIndexServerConnectionStateChange,\n type ProjectIndexServerClientHealth,\n type ProjectIndexServerConnectionState,\n type ProjectIndexServerShutdownResult,\n shutdownProjectIndexServer,\n} from './project-server-client.js';\nimport type { CodeMapGraph, IndexResult, IndexStats } from './schema.js';\nimport type {\n FileGraphOpArgs,\n HostToWorker,\n IndexOpArgs,\n OpName,\n OpShapes,\n SearchOpArgs,\n SearchOpResult,\n StatsOpArgs,\n SymbolGraphOpArgs,\n WorkerToHost,\n} from './worker-protocol.js';\nimport { indexStorePool } from './writer.js';\n\n// \u2500\u2500\u2500 Watchdog timeouts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Watchdog timeout for a full (startup / manual) index run.\n *\n * Bumped from 120s \u2192 240s in 0.284.x: large monorepos (WrongStack itself\n * has ~3k TS files) regularly exceed 120s on the first run on cold SSD +\n * Windows Defender real-time scanning. The watchdog only fires when the\n * worker is truly stuck \u2014 it does not slow down the happy path.\n */\nconst DEFAULT_FULL_INDEX_TIMEOUT_MS = 240_000;\n/** Watchdog timeout for one incremental reindex batch. */\nconst DEFAULT_INCREMENTAL_TIMEOUT_MS = 60_000;\n/**\n * Watchdog timeout for read operations (search / stats).\n *\n * Reads normally finish quickly, but a cold or contended SQLite index can take\n * longer than the former 8s budget, especially on large Windows workspaces.\n * Keep this below the outer tool timeout so callers receive the structured\n * IndexTimeoutError when the worker is genuinely wedged.\n */\nconst DEFAULT_QUERY_TIMEOUT_MS = 30_000;\n\n// \u2500\u2500\u2500 Indexing lifecycle state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Process-wide counters so codebase-search / codebase-stats can gate on\n// readiness and UIs can show an indexing indicator.\nlet _ready = false;\nlet _indexing = false;\nlet _currentFile = 0;\nlet _totalFiles = 0;\nlet _lastError: string | null = null;\n\n/** True once the first full-project index has completed (success or failure). */\nexport function isIndexReady(): boolean {\n return _ready;\n}\n\n/**\n * Mark the index as ready so downstream tools (codebase-search, codebase-stats)\n * don't gate on a startup index that never ran.\n */\nexport function setIndexReady(): void {\n _ready = true;\n}\n\n/** True while an index build is actively running. */\nexport function isIndexing(): boolean {\n return _indexing;\n}\n\n/** Current indexing progress: { currentFile, totalFiles, ready, indexing, circuit }. */\nexport function getIndexState(): {\n ready: boolean;\n indexing: boolean;\n currentFile: number;\n totalFiles: number;\n lastError: string | null;\n /** Detached per-project server connection owned by this client process. */\n server: ProjectIndexServerConnectionState;\n /** Circuit-breaker state \u2014 `open` means indexing is paused after repeated failures. */\n circuit: CircuitSnapshot;\n} {\n const server = getProjectIndexServerConnectionState();\n const remoteActivity = server.activity;\n const remoteIndexing = remoteActivity?.indexing ?? false;\n return {\n ready: _ready || (remoteActivity?.generation ?? 0) > 0,\n indexing: _indexing || remoteIndexing,\n currentFile: remoteIndexing ? (remoteActivity?.currentFile ?? 0) : _currentFile,\n totalFiles: remoteIndexing ? (remoteActivity?.totalFiles ?? 0) : _totalFiles,\n lastError: remoteActivity?.lastError ?? _lastError,\n server,\n circuit: indexCircuitBreaker.snapshot(),\n };\n}\n\n/**\n * Optional callback fired on every lifecycle transition (started, progress,\n * completed, failed). Plug into the event bus or a TUI dispatcher to surface\n * the indexing state in real time.\n */\ntype IndexStateListener = (state: ReturnType<typeof getIndexState>) => void;\nlet _listeners: IndexStateListener[] = [];\n\nexport function onIndexStateChange(listener: IndexStateListener): () => void {\n _listeners.push(listener);\n return () => {\n _listeners = _listeners.filter((l) => l !== listener);\n };\n}\n\nfunction emitState() {\n const state = getIndexState();\n for (const l of _listeners) l(state);\n}\n\n// Server lifecycle changes are part of index state even while no indexing job\n// is active, so the TUI can keep a truthful connected/offline/error indicator.\nonProjectIndexServerConnectionStateChange(() => emitState());\n\nfunction setIndexProgress(current: number, total: number) {\n _currentFile = current;\n _totalFiles = total;\n emitState();\n}\n\n// \u2500\u2500\u2500 Worker management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface PendingRpc {\n resolve: (value: unknown) => void;\n reject: (err: unknown) => void;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nlet worker: Worker | null = null;\nlet workerUnavailable = false;\nlet nextRpcId = 1;\nconst pending = new Map<number, PendingRpc>();\n\n/**\n * Locate the built worker file. The host is bundled into several entry points\n * (`dist/index.js`, `dist/builtin.js`, `dist/codebase-index/index.js`), so the\n * worker is probed at both relative locations. From source (vitest) neither\n * `.js` exists \u2192 inline mode, which keeps tests hermetic and mockable.\n */\nfunction resolveWorkerUrl(): URL | null {\n if (process.env['WRONGSTACK_INDEX_INLINE']) return null;\n for (const rel of ['./worker.js', './codebase-index/worker.js']) {\n try {\n const url = new URL(rel, import.meta.url);\n if (url.protocol === 'file:' && fs.existsSync(fileURLToPath(url))) return url;\n } catch {\n /* try the next candidate */\n }\n }\n return null;\n}\n\nfunction failAllPending(err: unknown): void {\n const entries = [...pending.values()];\n pending.clear();\n for (const p of entries) p.reject(err);\n}\n\nfunction ensureWorker(): Worker | null {\n if (worker) return worker;\n if (workerUnavailable) return null;\n const url = resolveWorkerUrl();\n if (!url) {\n workerUnavailable = true;\n return null;\n }\n try {\n const w = new Worker(url, { name: 'wstack-codebase-index' });\n // The worker must never keep the process alive on its own.\n w.unref();\n w.on('message', (msg: WorkerToHost) => {\n if (msg.type === 'progress') {\n pending.get(msg.id)?.onProgress?.(msg.current, msg.total);\n return;\n }\n const entry = pending.get(msg.id);\n if (!entry) return; // already timed out / cancelled\n pending.delete(msg.id);\n if (msg.ok) entry.resolve(msg.result);\n else {\n const error =\n msg.errorName === 'LockError' ? new LockError(msg.error) : new Error(msg.error);\n if (msg.errorName && msg.errorName !== 'Error') {\n (error as { name: string }).name = msg.errorName;\n }\n entry.reject(error);\n }\n });\n w.on('error', (err) => {\n // Ignore late events from a worker already terminated/replaced by the\n // watchdog; they must not reject RPCs owned by its successor.\n if (worker !== w) return;\n worker = null;\n failAllPending(err);\n });\n w.on('exit', () => {\n if (worker !== w) return;\n worker = null;\n failAllPending(new Error('codebase-index worker exited'));\n });\n worker = w;\n return w;\n } catch {\n // Spawn failed (no worker_threads, sandbox, \u2026) \u2014 fall back to inline for\n // the rest of the process lifetime.\n workerUnavailable = true;\n return null;\n }\n}\n\n/** Hard-kill a wedged worker. It respawns lazily on the next operation. */\nfunction terminateWorker(reason: unknown): void {\n const w = worker;\n worker = null;\n failAllPending(reason);\n if (w) void w.terminate().catch(() => {});\n}\n\n/**\n * Tear down the index host (worker + pending debounces). Call on process\n * shutdown; safe to call when nothing is running.\n */\nexport async function shutdownCodebaseIndexHost(): Promise<void> {\n cancelPendingReindexes();\n closeProjectIndexServerClients();\n indexStorePool.closeAll();\n const w = worker;\n worker = null;\n failAllPending(new Error('codebase-index host shut down'));\n workerUnavailable = false; // a future call may spawn a fresh worker\n if (w) {\n try {\n // On Windows SQLite files remain locked until the worker has actually\n // exited. Exposing the termination promise lets deterministic teardown\n // wait before removing an index directory.\n await w.terminate();\n } catch {\n // Shutdown is best-effort, matching watchdog termination semantics.\n }\n }\n}\n\ninterface CallOpts {\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\n/**\n * Run one operation, in the worker when available, inline otherwise. Both\n * paths share the watchdog: the returned promise ALWAYS settles within\n * `timeoutMs`, and a timeout in worker mode terminates the (possibly wedged\n * in synchronous code) worker \u2014 something an in-process watchdog can never do.\n */\nfunction callIndexOp<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n opts: CallOpts,\n): Promise<OpShapes[O]['result']> {\n // Production builds route every operation through one detached server per\n // project. The worker/inline path below still exists for source-tree tests\n // and exotic runtimes, but it has to be asked for: reaching it because the\n // build could not be located would give this process a private FTS5 database\n // and no indication that it had stopped sharing the project's index.\n const availability = resolveProjectIndexDaemonAvailability();\n if (availability.kind === 'available') {\n return callProjectIndexServer(op, args, opts);\n }\n if (availability.kind === 'missing-build') {\n return Promise.reject(\n new Error(\n 'Built codebase-index project server is unavailable. Build @wrongstack/tools, ' +\n 'or set WRONGSTACK_INDEX_INLINE=1 to explicitly accept a process-local index.',\n ),\n );\n }\n\n const w = ensureWorker();\n if (!w) return callInline(op, args, opts);\n\n if (opts.signal?.aborted) {\n return Promise.reject(\n opts.signal.reason instanceof Error ? opts.signal.reason : new Error('Indexing cancelled'),\n );\n }\n\n return new Promise<OpShapes[O]['result']>((resolve, reject) => {\n const id = nextRpcId++;\n\n const timer = setTimeout(() => {\n pending.delete(id);\n const err = new IndexTimeoutError(\n `Index ${op} exceeded its ${opts.timeoutMs}ms watchdog timeout`,\n );\n // A wedged worker (synchronous sqlite wait, pathological parse) cannot\n // be cooperatively cancelled \u2014 kill it; it respawns on the next call.\n terminateWorker(err);\n reject(err);\n }, opts.timeoutMs);\n timer.unref?.();\n\n const onAbort = () => {\n // Cooperative cancel; the worker aborts the op's signal and responds\n // with an error. The watchdog stays armed as the backstop.\n w.postMessage({ type: 'cancel', id } satisfies HostToWorker);\n };\n opts.signal?.addEventListener('abort', onAbort, { once: true });\n\n const cleanup = () => {\n clearTimeout(timer);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n pending.set(id, {\n resolve: (v) => {\n cleanup();\n resolve(v as OpShapes[O]['result']);\n },\n reject: (e) => {\n cleanup();\n reject(e);\n },\n onProgress: opts.onProgress,\n });\n\n w.postMessage({ type: 'request', id, op, args } satisfies HostToWorker);\n });\n}\n\n/** Inline fallback: same service code, raced against the same watchdog. */\nasync function callInline<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n opts: CallOpts,\n): Promise<OpShapes[O]['result']> {\n const ac = new AbortController();\n const onOuterAbort = () => ac.abort(opts.signal?.reason ?? new Error('Indexing cancelled'));\n if (opts.signal?.aborted) onOuterAbort();\n else opts.signal?.addEventListener('abort', onOuterAbort, { once: true });\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watchdog = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n const err = new IndexTimeoutError(\n `Index ${op} exceeded its ${opts.timeoutMs}ms watchdog timeout`,\n );\n ac.abort(err);\n reject(err);\n }, opts.timeoutMs);\n timer.unref?.();\n });\n\n const job = async (): Promise<OpShapes[O]['result']> => {\n switch (op) {\n case 'index':\n return (await indexService(args as IndexOpArgs, {\n signal: ac.signal,\n onProgress: opts.onProgress,\n })) as OpShapes[O]['result'];\n case 'search':\n return searchService(args as SearchOpArgs) as OpShapes[O]['result'];\n case 'stats':\n return statsService(args as StatsOpArgs) as OpShapes[O]['result'];\n case 'packageGraph':\n return packageGraphServiceInline(args as StatsOpArgs) as OpShapes[O]['result'];\n case 'fileGraph':\n return fileGraphServiceInline(args as FileGraphOpArgs) as OpShapes[O]['result'];\n case 'symbolGraph':\n return symbolGraphServiceInline(args as SymbolGraphOpArgs) as OpShapes[O]['result'];\n default:\n throw new Error(`unknown index op: ${String(op)}`);\n }\n };\n\n try {\n return await Promise.race([job(), watchdog]);\n } finally {\n if (timer) clearTimeout(timer);\n opts.signal?.removeEventListener('abort', onOuterAbort);\n }\n}\n\n// \u2500\u2500\u2500 Process-wide write mutex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// A single promise chain. Each enqueued job awaits the previous one's settle\n// (success OR failure) before running, so a thrown job never wedges the chain.\n// Only write runs (index) take the mutex; searches/stats are WAL reads.\nlet chain: Promise<unknown> = Promise.resolve();\n\nfunction withMutex<T>(job: () => Promise<T>): Promise<T> {\n const run = chain.then(job, job);\n // Keep the chain alive regardless of this job's outcome.\n chain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n}\n\n/** Build the fail-fast error thrown while the circuit is open. */\nfunction circuitOpenError(): CircuitOpenError {\n const c = indexCircuitBreaker.snapshot();\n return new CircuitOpenError(\n 'Codebase indexing is temporarily paused after repeated failures' +\n (c.lastFailure ? ` (last: ${c.lastFailure})` : '') +\n (c.cooldownRemainingMs > 0\n ? `; auto-retry in ${Math.ceil(c.cooldownRemainingMs / 1000)}s`\n : '') +\n '. Use /codebase-reindex to retry now.',\n );\n}\n\n// \u2500\u2500\u2500 Debounce \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst DEFAULT_DEBOUNCE_MS = 400;\nconst debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();\ninterface ReadyReindexBatch {\n projectRoot: string;\n indexDir?: string | undefined;\n files: Set<string>;\n timeoutMs: number;\n onErrors: Set<(err: unknown) => void>;\n flush?: ReturnType<typeof setImmediate> | undefined;\n}\nconst readyReindexBatches = new Map<string, ReadyReindexBatch>();\n\nfunction debounceKey(projectRoot: string, indexDir: string | undefined, file: string): string {\n return JSON.stringify([projectRoot, indexDir ?? '', file]);\n}\n\nfunction reindexBatchKey(projectRoot: string, indexDir: string | undefined): string {\n return JSON.stringify([projectRoot, indexDir ?? '']);\n}\n\nfunction flushReadyReindexBatch(key: string): void {\n const batch = readyReindexBatches.get(key);\n if (!batch) return;\n readyReindexBatches.delete(key);\n\n if (!indexCircuitBreaker.allowRequest()) {\n const error = circuitOpenError();\n for (const onError of batch.onErrors) onError(error);\n return;\n }\n\n void withMutex(() =>\n callIndexOp(\n 'index',\n {\n projectRoot: batch.projectRoot,\n files: [...batch.files].sort(),\n indexDir: batch.indexDir,\n },\n { timeoutMs: batch.timeoutMs },\n ),\n ).then(\n () => indexCircuitBreaker.recordSuccess(),\n (err) => {\n indexCircuitBreaker.recordFailure(err);\n for (const onError of batch.onErrors) onError(err);\n },\n );\n}\n\nfunction addReadyReindex(opts: {\n projectRoot: string;\n file: string;\n indexDir?: string | undefined;\n timeoutMs: number;\n onError?: ((err: unknown) => void) | undefined;\n}): void {\n const key = reindexBatchKey(opts.projectRoot, opts.indexDir);\n const existing = readyReindexBatches.get(key);\n if (existing) {\n existing.files.add(opts.file);\n existing.timeoutMs = Math.max(existing.timeoutMs, opts.timeoutMs);\n if (opts.onError) existing.onErrors.add(opts.onError);\n return;\n }\n\n const batch: ReadyReindexBatch = {\n projectRoot: opts.projectRoot,\n indexDir: opts.indexDir,\n files: new Set([opts.file]),\n timeoutMs: opts.timeoutMs,\n onErrors: new Set(opts.onError ? [opts.onError] : []),\n };\n batch.flush = setImmediate(() => flushReadyReindexBatch(key));\n batch.flush.unref?.();\n readyReindexBatches.set(key, batch);\n}\n\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** True when the file's extension maps to a language the indexer can parse. */\nexport function isIndexableFile(filePath: string): boolean {\n return isIndexablePath(filePath);\n}\n\n/**\n * Run a full-project scan and await it. Used at session start and by the manual\n * `/codebase-reindex` command. Incremental by default (unchanged files skipped\n * via mtime, so repeat runs are cheap); pass `force` to clear and rebuild.\n *\n * Sets the global `_ready` flag on completion so downstream tools know the\n * index is usable.\n */\n/** Pattern matching SQLite constraint failures caused by a stale/corrupt index DB. */\nfunction isRecoverableConstraintError(err: unknown): boolean {\n if (err instanceof Error) {\n const msg = err.message.toLowerCase();\n return msg.includes('unique constraint') || msg.includes('constraint failed');\n }\n return false;\n}\n\nexport async function runStartupIndex(opts: {\n projectRoot: string;\n indexDir?: string | undefined;\n force?: boolean | undefined;\n langs?: string[] | undefined;\n signal?: AbortSignal | undefined;\n /** Watchdog timeout for the whole run. Default: 120s. */\n timeoutMs?: number | undefined;\n}): Promise<IndexResult> {\n // Circuit breaker: after repeated failures/timeouts, fail fast instead of\n // queuing yet another run behind a possibly-wedged pipeline.\n if (!indexCircuitBreaker.allowRequest()) throw circuitOpenError();\n\n _indexing = true;\n emitState();\n\n try {\n const result = await withMutex(() => {\n // Reset counters inside the mutex \u2014 if runStartupIndex is called twice\n // concurrently, the second caller must not clobber a running index's\n // progress counters.\n _currentFile = 0;\n _totalFiles = 0;\n _lastError = null;\n return callIndexOp(\n 'index',\n {\n projectRoot: opts.projectRoot,\n indexDir: opts.indexDir,\n force: opts.force,\n langs: opts.langs,\n },\n {\n timeoutMs: opts.timeoutMs ?? DEFAULT_FULL_INDEX_TIMEOUT_MS,\n signal: opts.signal,\n onProgress: setIndexProgress,\n },\n );\n });\n _ready = true;\n indexCircuitBreaker.recordSuccess();\n return result;\n } catch (err) {\n _lastError = err instanceof Error ? err.message : String(err);\n\n // Auto-recovery: SQLite constraint failures indicate index DB corruption\n // from a previous interrupted write (e.g., process killed mid-insert).\n // Retry with force=true to wipe and rebuild from source.\n const originalError = _lastError;\n if (isRecoverableConstraintError(err) && !opts.force) {\n _lastError = null;\n const rebuildResult = await runStartupIndex({\n ...opts,\n force: true,\n });\n _ready = true;\n // Tag the result so callers can distinguish a normal run from a\n // corruption-triggered recovery.\n return {\n ...rebuildResult,\n autoRecovered: { failure: originalError, rebuiltWithForce: true },\n };\n }\n\n _ready = true; // index is \"ready\" in the sense that we won't try again; downstream tools will see lastError\n // Caller-initiated aborts (session teardown, Ctrl+C) are not indexer\n // failures \u2014 only genuine errors and watchdog timeouts trip the breaker.\n if (!opts.signal?.aborted) indexCircuitBreaker.recordFailure(err);\n throw err;\n } finally {\n _indexing = false;\n emitState();\n }\n}\n\n/**\n * Debounced, fire-and-forget incremental reindex of specific files. Used by the\n * per-edit toolCall middleware and the external file watcher. Non-indexable\n * paths are dropped. Errors are reported via the optional `onError` callback and\n * never thrown to the caller (background work must not crash a turn).\n */\nexport function enqueueReindex(opts: {\n projectRoot: string;\n files: string[];\n indexDir?: string | undefined;\n debounceMs?: number | undefined;\n /** Watchdog timeout per file. Default: 30s. */\n timeoutMs?: number | undefined;\n onError?: ((err: unknown) => void) | undefined;\n}): void {\n const files = opts.files.filter(isIndexableFile);\n if (files.length === 0) return;\n const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n\n for (const file of files) {\n const key = debounceKey(opts.projectRoot, opts.indexDir, file);\n const existing = debounceTimers.get(key);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n debounceTimers.delete(key);\n // All per-file debounce timers that expire in this event-loop turn are\n // folded into one worker/SQLite operation per project index. This keeps\n // same-file debounce semantics while avoiding N full index lifecycles\n // for watcher bursts such as branch switches or generated-file updates.\n addReadyReindex({\n projectRoot: opts.projectRoot,\n file,\n indexDir: opts.indexDir,\n timeoutMs: opts.timeoutMs ?? DEFAULT_INCREMENTAL_TIMEOUT_MS,\n onError: opts.onError,\n });\n }, ms);\n // Don't keep the event loop alive solely for a pending reindex.\n timer.unref?.();\n debounceTimers.set(key, timer);\n }\n}\n\n/** Cancel all pending debounced reindexes. For teardown / tests. */\nexport function cancelPendingReindexes(): void {\n for (const t of debounceTimers.values()) clearTimeout(t);\n debounceTimers.clear();\n for (const batch of readyReindexBatches.values()) {\n if (batch.flush) clearImmediate(batch.flush);\n }\n readyReindexBatches.clear();\n}\n\n/**\n * Ranked symbol search against the index. The query runs in the index worker\n * (or inline in fallback mode) \u2014 the main thread never opens SQLite. Reads\n * don't take the write mutex (WAL readers don't block the writer) and don't\n * feed the circuit breaker; a wedged read still trips the watchdog, which\n * recycles the worker.\n */\nexport async function searchCodebaseIndex(\n args: SearchOpArgs,\n opts: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<SearchOpResult> {\n return callIndexOp('search', args, {\n timeoutMs: opts.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n signal: opts.signal,\n });\n}\n\n/** Index health/statistics, fetched off the main thread like searches. */\nexport async function codebaseIndexStats(\n args: StatsOpArgs,\n opts: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<IndexStats> {\n return callIndexOp('stats', args, {\n timeoutMs: opts.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n signal: opts.signal,\n });\n}\n\n/** Package dependency graph, served by the same per-project index process. */\nexport async function packageGraphService(args: StatsOpArgs): Promise<CodeMapGraph> {\n return callIndexOp('packageGraph', args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });\n}\n\n/** File dependency graph, served by the same per-project index process. */\nexport async function fileGraphService(args: FileGraphOpArgs): Promise<CodeMapGraph> {\n return callIndexOp('fileGraph', args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });\n}\n\n/** Symbol dependency graph, served by the same per-project index process. */\nexport async function symbolGraphService(args: SymbolGraphOpArgs): Promise<CodeMapGraph> {\n return callIndexOp('symbolGraph', args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });\n}\n\n/**\n * Stop this project's detached index server. Unlike\n * shutdownCodebaseIndexHost(), this intentionally affects every connected\n * TUI/CLI/WebUI client for the project.\n */\nexport function shutdownCodebaseIndexServer(\n projectRoot: string,\n indexDir?: string,\n reason?: string,\n): Promise<ProjectIndexServerShutdownResult> {\n return shutdownProjectIndexServer(projectRoot, indexDir, reason);\n}\n\n/** Probe the connected project server without starting a missing server. */\nexport function checkCodebaseIndexServerHealth(\n projectRoot: string,\n indexDir?: string,\n options: { timeoutMs?: number | undefined } = {},\n): Promise<ProjectIndexServerClientHealth> {\n return checkProjectIndexServerHealth(projectRoot, indexDir, options);\n}\n\n/** Ensure the detached project server exists and owns external watching. */\nexport function ensureCodebaseIndexServer(options: {\n projectRoot: string;\n indexDir?: string | undefined;\n watchExternal?: boolean | undefined;\n debounceMs?: number | undefined;\n}): Promise<void> {\n if (!isProjectIndexServerAvailable()) return Promise.resolve();\n return ensureProjectIndexServer({\n projectRoot: options.projectRoot,\n indexDir: options.indexDir,\n watchExternal: options.watchExternal ?? false,\n debounceMs: options.debounceMs ?? DEFAULT_DEBOUNCE_MS,\n });\n}\n\n// \u2500\u2500\u2500 Test-only reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Reset all process-global indexing state for test isolation.\n *\n * Vitest runs test files in parallel within the same process, so module-level\n * state (`_indexing`, `_ready`, `chain`, `indexCircuitBreaker`) leaks between\n * tests. Call this in `beforeEach` to ensure each test starts with a clean\n * slate. Production code should NEVER call this.\n */\nexport function resetIndexStateForTesting(): void {\n _ready = false;\n _indexing = false;\n _currentFile = 0;\n _totalFiles = 0;\n _lastError = null;\n chain = Promise.resolve();\n indexCircuitBreaker.reset();\n cancelPendingReindexes();\n closeProjectIndexServerClients();\n // Don't terminate the worker \u2014 it's expensive to respawn and tests that\n // need it will call ensureWorker() lazily. Just clear the RPC state.\n for (const [, p] of pending) p.reject(new Error('test reset'));\n pending.clear();\n nextRpcId = 1;\n}\n", "/**\n * Circuit breaker for the codebase indexer.\n *\n * The indexer can wedge: a hung filesystem, a parser pathology, or another\n * wstack process holding the SQLite write lock (several surfaces \u2014 TUI, WebUI,\n * parallel terminals \u2014 share one per-project `index.db`). Without protection,\n * every queued reindex piles up behind the process-wide mutex, `isIndexing()`\n * stays true forever, and anything that awaits an index run (the startup scan,\n * `/codebase-reindex`) locks its terminal.\n *\n * Standard three-state breaker:\n *\n * closed \u2014 normal operation; consecutive failures are counted.\n * open \u2014 after `failureThreshold` consecutive failures, every request\n * is rejected fast ({@link CircuitOpenError}) for `cooldownMs`.\n * half-open \u2014 after the cooldown exactly one probe run is admitted;\n * success closes the circuit, failure re-opens it.\n *\n * Watchdog timeouts ({@link IndexTimeoutError}) count as failures;\n * caller-initiated aborts (session teardown) do not \u2014 the background indexer\n * makes that distinction before recording.\n *\n * Lock conflicts ({@link LockError}) do NOT count as failures \u2014 they are expected\n * transient conditions when multiple wstack surfaces share the same `index.db`.\n * The index store retries automatically; a LockError only reaches the circuit\n * breaker when all retries are exhausted.\n */\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\nexport interface CircuitSnapshot {\n state: CircuitState;\n consecutiveFailures: number;\n lastFailure: string | null;\n /** ms until an open circuit admits a half-open probe (0 unless open). */\n cooldownRemainingMs: number;\n}\n\n/** Thrown when a run is rejected because the circuit is open. */\nexport class CircuitOpenError extends Error {\n override readonly name = 'CircuitOpenError';\n}\n\n/** Thrown by the background indexer's watchdog when a run exceeds its timeout. */\nexport class IndexTimeoutError extends Error {\n override readonly name = 'IndexTimeoutError';\n}\n\n/**\n * Thrown when an SQLite operation fails with a lock conflict (SQLITE_BUSY or\n * SQLITE_LOCKED) even after all retry attempts are exhausted.\n *\n * The circuit breaker does **not** count `LockError` as a failure \u2014 a lock\n * conflict means another writer is active, not that this indexer is broken.\n * The caller should treat it as a transient failure and retry later.\n */\nexport class LockError extends Error {\n override readonly name = 'LockError';\n}\n\nexport interface CircuitBreakerOptions {\n /** Consecutive failures before the circuit opens. Default: 3. */\n failureThreshold?: number | undefined;\n /** How long an open circuit rejects requests before allowing a probe. Default: 60s. */\n cooldownMs?: number | undefined;\n /** Injectable clock for tests. Default: Date.now. */\n now?: (() => number) | undefined;\n}\n\nexport class IndexCircuitBreaker {\n private readonly failureThreshold: number;\n private readonly cooldownMs: number;\n private readonly now: () => number;\n\n private state: CircuitState = 'closed';\n private consecutiveFailures = 0;\n private openedAt = 0;\n private lastFailure: string | null = null;\n private probeInFlight = false;\n\n constructor(opts: CircuitBreakerOptions = {}) {\n this.failureThreshold = opts.failureThreshold ?? 3;\n this.cooldownMs = opts.cooldownMs ?? 60_000;\n this.now = opts.now ?? Date.now;\n }\n\n /**\n * True when a run may proceed. An open circuit transitions to half-open once\n * the cooldown has elapsed, admitting exactly one probe; further requests\n * are rejected until that probe settles via recordSuccess/recordFailure.\n */\n allowRequest(): boolean {\n if (this.state === 'closed') return true;\n if (this.state === 'open') {\n if (this.now() - this.openedAt < this.cooldownMs) return false;\n this.state = 'half-open';\n this.probeInFlight = true;\n return true;\n }\n // half-open: admit only one probe at a time.\n if (this.probeInFlight) return false;\n this.probeInFlight = true;\n return true;\n }\n\n recordSuccess(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.lastFailure = null;\n this.probeInFlight = false;\n }\n\n recordFailure(err: unknown): void {\n // LockError means \"another process is writing \u2014 try again later\", not a\n // broken indexer. Do not count it against the failure threshold.\n if (err instanceof LockError) {\n this.lastFailure = `[transient/lock] ${err.message}`;\n this.probeInFlight = false;\n return;\n }\n this.lastFailure = err instanceof Error ? err.message : String(err);\n this.probeInFlight = false;\n this.consecutiveFailures++;\n if (this.state === 'half-open' || this.consecutiveFailures >= this.failureThreshold) {\n this.state = 'open';\n this.openedAt = this.now();\n }\n }\n\n /** Force-close the circuit (manual recovery: `/codebase-reindex`). */\n reset(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.lastFailure = null;\n this.probeInFlight = false;\n this.openedAt = 0;\n }\n\n snapshot(): CircuitSnapshot {\n return {\n state: this.state,\n consecutiveFailures: this.consecutiveFailures,\n lastFailure: this.lastFailure,\n cooldownRemainingMs:\n this.state === 'open' ? Math.max(0, this.cooldownMs - (this.now() - this.openedAt)) : 0,\n };\n }\n}\n\n/**\n * Process-wide breaker shared by every index path (startup scan, per-edit\n * incremental, external watcher, the `codebase-index` tool). Module-level for\n * the same reason the mutex is: there is one `index.db` per project and one\n * indexing pipeline per process.\n */\nexport const indexCircuitBreaker = new IndexCircuitBreaker();\n\n/** Reset the shared breaker \u2014 used by `/codebase-reindex` and tests. */\nexport function resetIndexCircuitBreaker(): void {\n indexCircuitBreaker.reset();\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * Main indexing orchestrator.\n *\n * Given a project root and a list of files:\n * 1. Parse each file with the appropriate parser (TS, Go, Python, Rust, JSON, YAML)\n * 2. Delete old symbols for changed/deleted files\n * 3. Insert new symbols\n * 4. Update file metadata\n * 5. Return index statistics\n */\n\nimport { execFile } from 'node:child_process';\nimport type { Dirent, Stats } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport { availableParallelism } from 'node:os';\nimport * as path from 'node:path';\nimport type { Context } from '@wrongstack/core/agent';\nimport {\n DEFAULT_WALK_IGNORE_DIRS,\n indexParallelBatchSize,\n isFrugalPerf,\n} from '@wrongstack/core/utils';\nimport { type IgnoreMatcher, loadGitignoreMatcher } from './gitignore.js';\nimport { detectLang, INDEXABLE_EXTENSIONS } from './languages.js';\nimport { parseFileContent } from './parser-dispatch.js';\nimport type { FileMeta, IndexResult, Symbol as IndexSymbol, Ref, SymbolLang } from './schema.js';\nimport { IndexStore } from './writer.js';\n\n/** Yield the event loop every N files so the main thread stays responsive. */\nconst YIELD_EVERY_N = 50;\n\n/**\n * Parallel parse batch size \u2014 see {@link indexParallelBatchSize}.\n * Re-resolved at the start of each index run so env profile changes apply.\n */\nexport function resolveParallelBatch(): number {\n return indexParallelBatchSize(availableParallelism());\n}\n\nfunction yieldEventLoop(): Promise<void> {\n return new Promise((resolve) => setImmediate(resolve));\n}\n\n/**\n * Cooperatively abort if the signal is set. Throws with the signal's reason\n * (or a descriptive Error) so callers know *why* the operation was cancelled.\n * Called at yield points \u2014 never after a Promise resolve (that would be a\n * microtask that the signal check could miss).\n */\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (!signal?.aborted) return;\n if (signal.reason instanceof Error) throw signal.reason;\n throw new Error(typeof signal.reason === 'string' ? signal.reason : 'Indexing cancelled');\n}\n\n/**\n * Detect AbortError (DOMException with name 'AbortError') thrown by signal-aware\n * fs.promises calls (stat, readFile). We must re-throw these so the cancellation\n * propagates \u2014 catching them as ordinary errors would keep the loop running.\n */\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === 'AbortError';\n}\n\nconst DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;\nconst DEFAULT_IGNORE_FILES = new Set(['package-lock.json', 'pnpm-lock.yaml', 'pnpm-lock.yml']);\nconst INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);\nconst MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;\nconst MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;\n\nfunction isWithinProject(projectRoot: string, file: string): boolean {\n const rel = path.relative(projectRoot, file);\n return rel !== '' && !rel.startsWith(`..${path.sep}`) && rel !== '..' && !path.isAbsolute(rel);\n}\n\nfunction isMissingPathError(err: unknown): boolean {\n const code = (err as { code?: unknown } | null)?.code;\n return code === 'ENOENT' || code === 'ENOTDIR';\n}\n\nfunction normalizeComparablePath(value: string): string {\n const resolved = path.resolve(value);\n return process.platform === 'win32' ? resolved.toLowerCase() : resolved;\n}\n\nfunction gitOutput(projectRoot: string, args: string[]): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n execFile(\n 'git',\n ['-C', projectRoot, ...args],\n {\n encoding: 'buffer',\n maxBuffer: MAX_GIT_FILE_LIST_BYTES,\n windowsHide: true,\n },\n (error, stdout) => {\n if (error) reject(error);\n else resolve(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));\n },\n );\n });\n}\n\n/**\n * Git already maintains the canonical tracked/untracked directory index. On a\n * repository root this avoids hundreds of serial `readdir` calls; non-Git\n * projects and nested roots fall back to the filesystem walker below.\n */\nasync function findGitSourceFiles(\n projectRoot: string,\n ignore: string[],\n signal?: AbortSignal | undefined,\n): Promise<{ files: string[]; trustedUnchanged: Set<string> } | null> {\n try {\n throwIfAborted(signal);\n const topLevel = (await gitOutput(projectRoot, ['rev-parse', '--show-toplevel']))\n .toString('utf8')\n .trim();\n if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;\n\n throwIfAborted(signal);\n const ignoreSet = new Set([...DEFAULT_IGNORE, ...ignore]);\n const [output, statusOutput] = await Promise.all([\n gitOutput(projectRoot, ['ls-files', '--cached', '--others', '--exclude-standard', '-z']),\n gitOutput(projectRoot, [\n 'status',\n '--porcelain=v1',\n '-z',\n '--untracked-files=all',\n '--ignored=no',\n ]),\n ]);\n throwIfAborted(signal);\n const dirty = new Set<string>();\n const deleted = new Set<string>();\n const statusRecords = statusOutput.toString('utf8').split('\\0');\n for (let i = 0; i < statusRecords.length; i++) {\n const record = statusRecords[i];\n if (!record) continue;\n const status = record.slice(0, 2);\n const changedPath = path.resolve(projectRoot, record.slice(3));\n dirty.add(changedPath);\n if (status.includes('D')) deleted.add(changedPath);\n if (status.includes('R') || status.includes('C')) {\n const source = statusRecords[++i];\n if (source) dirty.add(path.resolve(projectRoot, source));\n }\n }\n const files: string[] = [];\n for (const relative of output.toString('utf8').split('\\0')) {\n if (!relative) continue;\n const portable = relative.replace(/\\\\/g, '/');\n if (\n portable.split('/').some((segment) => ignoreSet.has(segment)) ||\n DEFAULT_IGNORE_FILES.has(path.posix.basename(portable))\n ) {\n continue;\n }\n const full = path.resolve(projectRoot, relative);\n if (deleted.has(full)) continue;\n const ext = path.extname(relative).toLowerCase();\n if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);\n }\n return {\n files,\n trustedUnchanged: new Set(files.filter((file) => !dirty.has(file))),\n };\n } catch {\n return null;\n }\n}\n\ninterface IndexerOptions {\n projectRoot: string;\n files?: string[] | undefined;\n force?: boolean | undefined;\n langs?: string[] | undefined;\n ignore?: string[] | undefined;\n /** Override the index directory (default: the global per-project dir). */\n indexDir?: string | undefined;\n /**\n * Signal that cancels indexing cooperatively. Polled at yield points\n * (file walk, per-file loop) so a hung filesystem won't lock up the\n * process. When the tool executor's timeout fires, this signal aborts\n * and `runIndexer` throws, releasing the mutex and resetting flags.\n */\n signal?: AbortSignal | undefined;\n /**\n * Per-file progress callback. Injected by the caller instead of imported\n * from the host's module state so the indexer can run inside a worker\n * thread (worker posts progress messages; inline host updates its state).\n */\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nasync function findSourceFiles(\n projectRoot: string,\n ignore: string[],\n isGitIgnored: IgnoreMatcher,\n signal?: AbortSignal | undefined,\n): Promise<{\n files: string[];\n complete: boolean;\n errors: string[];\n trustedUnchanged?: Set<string>;\n}> {\n const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);\n if (gitFiles) {\n return {\n files: gitFiles.files,\n complete: true,\n errors: [],\n trustedUnchanged: gitFiles.trustedUnchanged,\n };\n }\n\n const results: string[] = [];\n const errors: string[] = [];\n let complete = true;\n const ignoreSet = new Set([...DEFAULT_IGNORE, ...ignore]);\n // Extension allow-list from languages.ts \u2014 every mapped language is discovered.\n // Special filenames (Makefile, Dockerfile, \u2026) are accepted via detectLang.\n const indexableExts = new Set(INDEXABLE_EXTENSIONS);\n\n let dirCount = 0;\n\n const walk = async (dir: string): Promise<void> => {\n // Yield + abort check before every readdir so a cancelled indexer\n // doesn't descend deeper into the tree.\n throwIfAborted(signal);\n // Periodically yield the event loop so the main thread stays responsive\n // during deep directory walks (Node 22's fs.promises.readdir doesn't\n // accept AbortSignal, so we rely on cooperative polling).\n if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {\n await yieldEventLoop();\n throwIfAborted(signal);\n }\n let entries: Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n complete = false;\n errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);\n return;\n }\n dirCount++;\n\n for (const e of entries) {\n if (ignoreSet.has(e.name)) continue;\n const full = path.join(dir, e.name);\n // Normalize to forward-slash relative path for pattern matching\n const rel = path.relative(projectRoot, full).replace(/\\\\/g, '/');\n if (e.isDirectory()) {\n // Prune .gitignore'd directories before descending (skips node_modules,\n // build output, and any project-specific ignored dirs).\n if (isGitIgnored(rel, true)) continue;\n await walk(full);\n } else if (e.isFile()) {\n if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;\n const ext = path.extname(e.name).toLowerCase();\n // Fast path: known extension. Slow path: special basenames (Makefile\u2026).\n if (indexableExts.has(ext) || detectLang(full) !== null) {\n results.push(full);\n }\n }\n }\n };\n\n await walk(projectRoot);\n return { files: results, complete, errors };\n}\n\nfunction assignRefsToSymbols(refs: Ref[], symbols: IndexSymbol[]): Ref[] {\n if (refs.length === 0 || symbols.length === 0) return [];\n const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);\n const seen = new Set<string>();\n const assigned: Ref[] = [];\n for (const ref of refs) {\n let owner: IndexSymbol | undefined;\n for (const symbol of ordered) {\n if (symbol.line > ref.line) break;\n owner = symbol;\n }\n // Imports usually appear before the first declaration. Attach them to the\n // first real symbol so file/package dependency graphs retain the module\n // edge without inventing an invalid owner id 0.\n if (!owner && ref.callType === 'import') owner = ordered[0];\n if (!owner || owner.id <= 0) continue;\n const key = `${owner.id}:${ref.toName}:${ref.callType}`;\n if (seen.has(key)) continue;\n seen.add(key);\n assigned.push({ ...ref, fromId: owner.id });\n }\n return assigned;\n}\n\n/** Run a full or incremental index and return statistics. */\nexport async function runIndexer(_ctx: Context, opts: IndexerOptions): Promise<IndexResult> {\n const store = new IndexStore(opts.projectRoot, { indexDir: opts.indexDir });\n try {\n return await runIndexerWithStore(store, opts);\n } finally {\n // Always release the synchronous SQLite connection \u2014 an abort mid-run\n // (executor timeout, session teardown) previously leaked it.\n try {\n store.close();\n } catch {\n /* already closed */\n }\n }\n}\n\nexport async function runIndexerWithStore(\n store: IndexStore,\n opts: IndexerOptions,\n): Promise<IndexResult> {\n const { projectRoot, langs, ignore = [], signal } = opts;\n // Graph semantics changed without a structural SQLite schema change. Keep a\n // separate data-version marker so older running processes do not downgrade\n // and wipe the same shared DB while a new WebUI is being rolled out.\n const relationGraphVersion = '2';\n const refResolutionVersion = '2';\n const force =\n (opts.force ?? false) || store.getMetadata('relation_graph_version') !== relationGraphVersion;\n const needsFullRefResolution =\n force || store.getMetadata('ref_resolution_version') !== refResolutionVersion;\n const startMs = Date.now();\n const errors: string[] = [];\n const langStats: Record<string, number> = {};\n let filesIndexed = 0;\n let symbolsIndexed = 0;\n\n // Honor the project-root .gitignore (skips node_modules, build output, and\n // any project-specific ignored paths) on top of the always-on DEFAULT_IGNORE.\n const isGitIgnored = await loadGitignoreMatcher(projectRoot);\n\n let files: string[];\n /** Set of all files discovered on disk (before language filtering).\n * Used for O(1) stale-file detection instead of stat-ing every\n * previously-indexed file. Null when an explicit file list was given. */\n let discoveredFiles: Set<string> | null = null;\n let discoveryComplete = true;\n let trustedUnchanged: Set<string> | undefined;\n if (opts.files && opts.files.length > 0) {\n // Explicit file list (per-edit / watcher path): keep paths inside the\n // project only and apply both always-on and .gitignore exclusions.\n files = opts.files\n .map((f) => path.resolve(projectRoot, f))\n .filter((f) => {\n if (!isWithinProject(projectRoot, f)) return false;\n const rel = path.relative(projectRoot, f).replace(/\\\\/g, '/');\n return (\n !rel.split('/').some((seg) => DEFAULT_IGNORE.includes(seg)) &&\n !DEFAULT_IGNORE_FILES.has(path.basename(f)) &&\n !isGitIgnored(rel, false)\n );\n });\n } else {\n const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);\n files = discovery.files;\n errors.push(...discovery.errors);\n discoveryComplete = discovery.complete;\n discoveredFiles = new Set(files);\n trustedUnchanged = discovery.trustedUnchanged;\n }\n\n if (langs && langs.length > 0) {\n const langSet = new Set(langs);\n files = files.filter((f) => {\n const lang = detectLang(f);\n return lang ? langSet.has(lang) : false;\n });\n }\n\n if (force) store.clearAll();\n\n // Collect existing file metadata for incremental check\n const existingMeta: Map<string, FileMeta> = new Map();\n if (!force) {\n for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);\n }\n\n // Git has already checked clean tracked files while producing status. Fold\n // their stored counts into the result once, before the async batch loop,\n // instead of creating thousands of promises and scheduler yields merely to\n // rediscover that their mtimes did not change.\n const totalFilesForProgress = files.length;\n let filesPreSkipped = 0;\n if (!force && trustedUnchanged) {\n files = files.filter((file) => {\n const meta = existingMeta.get(file);\n if (!meta || !trustedUnchanged.has(file)) return true;\n langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;\n symbolsIndexed += meta.symbolCount;\n filesIndexed++;\n filesPreSkipped++;\n return false;\n });\n if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);\n }\n\n // Process files in batches for parallel I/O and parsing.\n // SQLite writes remain sequential (they're synchronous and CPU-bound).\n // Batch width follows WRONGSTACK_PERF_PROFILE (frugal \u22644, balanced cores\u00D74).\n const parallelBatch = resolveParallelBatch();\n let filesSinceLastYield = 0;\n for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {\n const batchEnd = Math.min(batchStart + parallelBatch, files.length);\n const batchFiles = files.slice(batchStart, batchEnd);\n\n // Report progress to the caller so UIs can show indexing status.\n opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);\n\n // Yield the event loop periodically so the main thread stays responsive\n // (TUI rendering, input handling, etc.) during large index builds.\n // Uses a running counter instead of batchStart % YIELD_EVERY_N which\n // only works when the batch size divides YIELD_EVERY_N evenly \u2014 with\n // dynamic batch sizes that invariant no longer holds and the yield would\n // fire far less often than intended.\n // Also check for cancellation \u2014 the tool executor's timeout or a\n // session abort propagates through `signal`.\n filesSinceLastYield += batchFiles.length;\n if (filesSinceLastYield >= YIELD_EVERY_N) {\n filesSinceLastYield = 0;\n await yieldEventLoop();\n // Frugal: brief pause so sustained reindex doesn't pin a core.\n if (isFrugalPerf()) {\n await new Promise<void>((r) => setTimeout(r, 8));\n }\n throwIfAborted(signal);\n }\n\n // Phase 1: Parallel stat + incremental skip + read + parse\n const statOpts = signal ? { signal } : {};\n const statReadParse = await Promise.allSettled(\n batchFiles.map(\n async (\n file,\n ): Promise<{\n file: string;\n stat: Stats;\n lang: string;\n parsed: Awaited<ReturnType<typeof parseFileContent>> | null;\n content?: string;\n skippedMeta?: FileMeta;\n error?: string;\n missing?: boolean;\n }> => {\n let stat: Stats;\n try {\n stat = await (\n fs.stat as (path: string, opts: { signal?: AbortSignal }) => Promise<Stats>\n )(file, statOpts);\n } catch (e) {\n if (isAbortError(e)) throw e;\n return {\n file,\n stat: null as never as Stats,\n lang: '',\n parsed: null,\n error: `stat error: ${e instanceof Error ? e.message : String(e)}`,\n missing: isMissingPathError(e),\n };\n }\n if (!stat.isFile()) return { file, stat, lang: '', parsed: null };\n\n const lang = detectLang(file);\n if (!lang) return { file, stat, lang: '', parsed: null };\n if (stat.size > MAX_INDEX_FILE_BYTES) {\n return {\n file,\n stat,\n lang,\n parsed: null,\n error: `file too large (${stat.size} bytes; max ${MAX_INDEX_FILE_BYTES})`,\n };\n }\n\n const meta = existingMeta.get(file);\n if (!force && meta && meta.mtimeMs === Math.floor(stat.mtimeMs)) {\n return { file, stat, lang, parsed: null, skippedMeta: meta };\n }\n\n let content: string;\n try {\n content = await fs.readFile(file, { encoding: 'utf8', signal });\n } catch (e) {\n if (isAbortError(e)) throw e;\n return {\n file,\n stat,\n lang,\n parsed: null,\n error: `read error: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n\n let parsed: Awaited<ReturnType<typeof parseFileContent>>;\n try {\n parsed = await parseFileContent(file, content, lang as SymbolLang);\n } catch (e) {\n return {\n file,\n stat,\n lang,\n parsed: null,\n error: `parse error: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n return { file, stat, lang, parsed, content };\n },\n ),\n );\n\n // Phase 2: Sequential SQLite writes \u2014 amortized across the whole batch.\n //\n // Each file is still parsed in parallel (Phase 1), but the writes\n // happen in a single `commitBatch` transaction per outer batch\n // (PARALLEL_BATCH = 20 files). This drops the commit count from\n // ~5/file to 1/parallel-batch, which is the difference between\n // 100 fsync round-trips and 5 on a 20-file slice.\n const batchEntries: Array<{\n file: string;\n lang: SymbolLang;\n symbols: IndexSymbol[];\n refs: Ref[];\n mtimeMs: number;\n symbolCount: number;\n }> = [];\n const deleteForFiles: string[] = [];\n\n for (let fi = 0; fi < statReadParse.length; fi++) {\n const settled = statReadParse[fi]!;\n const file = expectDefined(batchFiles[fi]);\n\n if (settled.status === 'rejected') {\n const err = settled.reason;\n if (err instanceof Error && isAbortError(err)) throw err;\n errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);\n continue;\n }\n\n const result = settled.value;\n if (result.error) {\n // A missing path in a targeted watcher/edit run is authoritative: the\n // source was deleted or renamed, so remove its previous index rows.\n // Read/parse/permission failures are transient and retain the last good\n // snapshot instead of replacing it with an empty one.\n if (result.missing) store.deleteFile(file);\n errors.push(`${file}: ${result.error}`);\n continue;\n }\n\n const { stat, lang, parsed } = result;\n if (result.skippedMeta) {\n langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;\n symbolsIndexed += result.skippedMeta.symbolCount;\n filesIndexed++;\n continue;\n }\n\n if (!lang || !parsed) {\n if (lang) {\n store.upsertFile({\n file,\n lang: lang as SymbolLang,\n mtimeMs: Math.floor(stat.mtimeMs),\n symbolCount: 0,\n lastIndexed: Date.now(),\n });\n filesIndexed++;\n }\n continue;\n }\n\n // Empty symbol files still need their file row updated so future runs\n // know the mtime. Single transaction clears stale rows + upserts meta.\n if (parsed.symbols.length === 0) {\n store.replaceEmptyFile({\n file,\n lang: lang as SymbolLang,\n mtimeMs: Math.floor(stat.mtimeMs),\n symbolCount: 0,\n lastIndexed: Date.now(),\n });\n filesIndexed++;\n continue;\n }\n\n batchEntries.push({\n file,\n lang: lang as SymbolLang,\n symbols: parsed.symbols,\n refs: parsed.refs ?? [],\n mtimeMs: Math.floor(stat.mtimeMs),\n symbolCount: parsed.symbols.length,\n });\n deleteForFiles.push(file);\n }\n\n if (batchEntries.length > 0) {\n try {\n store.commitBatch(batchEntries, { deleteForFiles });\n for (const entry of batchEntries) {\n const count = entry.symbols.length;\n symbolsIndexed += count;\n langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;\n filesIndexed++;\n }\n } catch (err) {\n // If the batch commit fails, fall back to per-file writes so the\n // user still gets a partial index. Per-file writes are slower but\n // isolate failures.\n const message = err instanceof Error ? err.message : String(err);\n errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);\n for (const entry of batchEntries) {\n try {\n store.deleteRefsForFile(entry.file);\n store.deleteSymbolsForFile(entry.file);\n const symbolsWithIds = store.insertSymbols(entry.symbols);\n symbolsIndexed += symbolsWithIds.length;\n langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;\n filesIndexed++;\n if (entry.refs.length > 0 && symbolsWithIds.length > 0) {\n const fallbackBatch = assignRefsToSymbols(entry.refs, symbolsWithIds);\n if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);\n }\n store.resolveRefsForNames([\n ...entry.symbols.map((symbol) => symbol.name),\n ...entry.refs.map((ref) => ref.toName),\n ]);\n store.upsertFile({\n file: entry.file,\n lang: entry.lang,\n mtimeMs: entry.mtimeMs,\n symbolCount: entry.symbolCount,\n lastIndexed: Date.now(),\n });\n } catch (innerErr) {\n errors.push(\n `fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`,\n );\n }\n }\n }\n }\n }\n\n // Remove stale entries for files deleted since last run.\n // Instead of stat-ing every previously-indexed file (O(total indexed)),\n // derive stale files from the discovered set: any existingMeta entry not\n // in the scanned files is stale. Skip entirely for explicit file lists\n // (targeted reindex \u2014 can't derive stale from a subset).\n if (discoveredFiles && discoveryComplete) {\n for (const [file_] of existingMeta) {\n if (!discoveredFiles.has(file_)) {\n store.deleteFile(file_);\n }\n }\n }\n\n // Batch commits resolve only names touched by that batch. Existing databases\n // get one global repair pass when this contract version changes; subsequent\n // single-file watcher runs avoid rebuilding the full symbol-name map.\n if (needsFullRefResolution) store.resolveRefs();\n store.setMetadata('ref_resolution_version', refResolutionVersion);\n store.setMetadata('relation_graph_version', relationGraphVersion);\n // Planner refresh belongs to full/bulk runs, not the edit watcher hot path.\n if (!opts.files || filesIndexed >= 50) store.optimize();\n\n store.setLastIndexed(Date.now());\n if (!opts.files) store.compactIfNeeded();\n const durationMs = Date.now() - startMs;\n\n return {\n filesIndexed,\n symbolsIndexed,\n langStats,\n durationMs,\n errors,\n };\n}\n", "/**\n * Minimal but faithful `.gitignore` matcher for the indexer.\n *\n * Supports the parts of the gitignore spec that matter for skipping source\n * files: comments / blanks, `!` negation (last match wins), trailing-slash\n * directory-only rules, leading-slash / embedded-slash anchoring, and the\n * `*` / `**` / `?` / `[...]` globs (via core's {@link compileGlob}).\n *\n * Only the project-root `.gitignore` is read. Nested `.gitignore` files are not\n * walked \u2014 the common build/dependency dirs that would live deeper are already\n * covered by the indexer's always-on `DEFAULT_IGNORE`.\n *\n * Known limitation: a `!negated` file inside an ignored directory will not be\n * re-included, because the indexer prunes ignored directories before descending\n * (a large performance win). This matches most lightweight implementations.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { compileGlob } from '@wrongstack/core/utils';\n\nexport type IgnoreMatcher = (relPath: string, isDir: boolean) => boolean;\n\ninterface Rule {\n /** Matches the entry itself or anything under it (for dirs / plain names). */\n eqOrUnder: RegExp;\n /** Matches only entries strictly under it (for dir-only rules on files). */\n under: RegExp;\n negated: boolean;\n dirOnly: boolean;\n}\n\n/** Strip the `^`/`$` anchors compileGlob adds so we can re-anchor ourselves. */\nfunction globBody(glob: string): string {\n return compileGlob(glob).source.replace(/^\\^/, '').replace(/\\$$/, '');\n}\n\n/** Compile a list of raw `.gitignore` lines into a matcher. */\nexport function compileGitignore(lines: string[]): IgnoreMatcher {\n const rules: Rule[] = [];\n\n for (const raw of lines) {\n let line = raw.replace(/\\r$/, '');\n if (!line.trim() || line.trimStart().startsWith('#')) continue;\n line = line.trim();\n\n let negated = false;\n if (line.startsWith('!')) {\n negated = true;\n line = line.slice(1);\n }\n\n let dirOnly = false;\n if (line.endsWith('/')) {\n dirOnly = true;\n line = line.slice(0, -1);\n }\n if (!line) continue;\n\n // A slash anywhere (after the trailing slash is stripped) anchors the\n // pattern to the gitignore's directory (the project root here). A bare name\n // matches at any depth.\n const anchored = line.startsWith('/') || line.includes('/');\n if (line.startsWith('/')) line = line.slice(1);\n\n const body = globBody(line);\n const prefix = anchored ? '^' : '(?:^|.*/)';\n rules.push({\n eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),\n under: new RegExp(`${prefix}${body}/.*$`),\n negated,\n dirOnly,\n });\n }\n\n return (relPath: string, isDir: boolean): boolean => {\n const p = relPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n let ignored = false;\n for (const r of rules) {\n // A directory-only rule never matches a file by its own name; it only\n // matches files that live strictly beneath the named directory.\n const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;\n if (re.test(p)) ignored = !r.negated;\n }\n return ignored;\n };\n}\n\n/** Read `<projectRoot>/.gitignore` and compile it. Missing file \u2192 matches nothing. */\nexport async function loadGitignoreMatcher(projectRoot: string): Promise<IgnoreMatcher> {\n let lines: string[] = [];\n try {\n const raw = await fs.readFile(path.join(projectRoot, '.gitignore'), 'utf8');\n lines = raw.split('\\n');\n } catch {\n // No .gitignore \u2014 nothing extra to ignore beyond the indexer defaults.\n }\n return compileGitignore(lines);\n}\n", "import type { FileSymbols, SymbolLang } from './schema.js';\n\n/**\n * Load only the parser needed for this file. Keeping these imports lazy is\n * important for the project server: TypeScript's compiler API stays out of the\n * SQLite/IPC owner when TS/JS parsing is delegated to parser workers.\n */\nexport async function parseFileContent(\n file: string,\n content: string,\n lang: SymbolLang,\n): Promise<FileSymbols> {\n switch (lang) {\n case 'ts':\n case 'tsx':\n case 'js':\n case 'jsx': {\n const { parseSymbols } = await import('./ts-parser.js');\n return parseSymbols({ file, content, lang });\n }\n case 'go': {\n const { parseSymbols } = await import('./go-parser.js');\n return parseSymbols({ file, content, lang: 'go' });\n }\n case 'py': {\n const { parseSymbols } = await import('./py-parser.js');\n return parseSymbols({ file, content, lang: 'py' });\n }\n case 'rs': {\n const { parseSymbols } = await import('./rs-parser.js');\n return parseSymbols({ file, content, lang: 'rs' });\n }\n case 'json': {\n const { parseSymbols } = await import('./json-parser.js');\n return parseSymbols({ file, content, lang: 'json' });\n }\n case 'yaml': {\n const { parseSymbols } = await import('./yaml-parser.js');\n return parseSymbols({ file, content, lang: 'yaml' });\n }\n default: {\n const { parseSymbols } = await import('./generic-parser.js');\n return parseSymbols({ file, content, lang });\n }\n }\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * SQLite storage layer for the codebase index.\n *\n * Uses `node:sqlite` (synchronous API \u2014 DatabaseSync class).\n * Database file: ~/.wrongstack/projects/<hash>/codebase-index/index.db \u2014 kept\n * out of the repo so it never clutters the working tree or needs gitignoring.\n *\n * ### Multi-process safety\n *\n * Several wstack surfaces (TUI, WebUI, parallel terminals) share this per-project\n * database. WAL mode allows concurrent reads alongside a writer, and\n * `busy_timeout` bounds how long a write operation waits for the lock. When\n * the timeout expires and SQLite returns SQLITE_BUSY, the store retries with\n * exponential backoff (up to 3 attempts) before letting the error propagate.\n * If all retries are exhausted, a {@link LockError} is thrown \u2014 the circuit\n * breaker treats this as a transient condition and does NOT count it as a failure.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { type Bm25Index, buildBm25Index, buildIndexableText, tokenise } from './bm25.js';\nimport { lspKindToInternalKind } from './lsp-kind.js';\nimport type {\n CodeMapGraph,\n FileMeta,\n IndexStats,\n Symbol as IndexSymbol,\n Ref,\n SearchResult,\n SymbolKind,\n SymbolLang,\n} from './schema.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { loadDatabaseSync, runSqliteWithRetry } from './sqlite-runtime.js';\nimport {\n getAllFileMetasWithStatement,\n getAllIndexableWithStatement,\n getFileMetaWithStatement,\n getMaxSymbolIdWithStatement,\n getMetadataWithStatement,\n getStatsWithStatement,\n} from './writer-admin.js';\nimport {\n type BulkSymbolRow,\n bulkInsertFtsWithStatement,\n bulkInsertRefsWithStatement,\n bulkInsertSymbolsWithStatement,\n} from './writer-bulk-insert.js';\nimport {\n findRefsFromWithStatement,\n findRefsToWithStatement,\n getFileGraphWithStatement,\n getPackageGraphWithStatement,\n getSymbolGraphWithStatement,\n} from './writer-graph-reader.js';\nimport { assignRefsToSymbols, escapeLike, resolveIndexDir } from './writer-helpers.js';\nimport { applyIndexStorePragmas } from './writer-pragmas.js';\nimport {\n CORE_TABLES_SQL,\n METADATA_TABLE_SQL,\n REFS_INDEX_SQL,\n REFS_TABLE_SQL,\n SYMBOL_INDEX_SQL,\n SYMBOLS_FTS_SQL,\n} from './writer-schema.js';\nimport {\n buildWriterSearchWhere,\n mapWriterSearchRow,\n normalizeSearchLimit,\n type WriterSearchFilter,\n type WriterSearchRow,\n} from './writer-search-helpers.js';\nimport { StorePool } from './writer-store-pool.js';\n\nexport { codebaseIndexDirOverride, resolveIndexDir } from './writer-helpers.js';\nexport { StorePool } from './writer-store-pool.js';\n\nconst DB_FILE = 'index.db';\n\nexport class IndexStore {\n private db: DatabaseSync;\n /** Absolute path to this project's index directory. */\n private readonly indexDir: string;\n /**\n * True when the SQLite build provides FTS5 (Node's bundled SQLite does).\n * When false, ranked search falls back to the LIKE + in-process BM25 path.\n */\n private ftsAvailable = false;\n\n /**\n * Cache of prepared statements keyed by their SQL text. `DatabaseSync`\n * compiles SQL on every `.prepare()` call; for the fixed-SQL methods\n * (upsertFile, getFileMeta, deleteFile, insertRefs, \u2026) that runs thousands\n * of times during a full reindex. `StatementSync` objects are reusable\n * across calls on the same connection, so we compile each distinct SQL once\n * and reuse it. Cleared in {@link close} when the connection is torn down.\n */\n private readonly stmtCache = new Map<string, ReturnType<DatabaseSync['prepare']>>();\n /**\n * Cached full-corpus BM25 index for the FTS5-unavailable fallback path.\n * Built lazily on the first `searchRankedFallback` call and invalidated\n * (via `bm25Dirty`) whenever the `symbols` table is mutated. Computing\n * IDF over the full corpus is also more correct than the old per-query\n * candidate-subset IDF.\n *\n * Cache-lifecycle invariants (single source of truth lives at the\n * `invalidateBm25()` helper \u2014 see its docblock for the \"every mutation\n * MUST call this\" contract):\n * - declaration: this field + `bm25Dirty` (here)\n * - invalidation: `invalidateBm25()` flips the flag and nulls the cache\n * - build: `getOrBuildBm25()` rebuilds against current `symbols` rows\n * - teardown: `close()` resets the flag and nulls the cache\n */\n private bm25Cache: Bm25Index | null = null;\n // Dirty on open so the first getOrBuildBm25() rebuilds against current rows;\n // an empty or pre-existing corpus makes a stale IDF table meaningless.\n private bm25Dirty = true;\n\n /** Prepare-once helper: compile `sql` on first use, reuse thereafter. */\n private stmt(sql: string): ReturnType<DatabaseSync['prepare']> {\n let s = this.stmtCache.get(sql);\n if (s === undefined) {\n s = this.db.prepare(sql);\n this.stmtCache.set(sql, s);\n }\n return s;\n }\n\n constructor(projectRoot: string, opts: { indexDir?: string | undefined } = {}) {\n this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);\n fs.mkdirSync(this.indexDir, { recursive: true });\n const Database = loadDatabaseSync();\n this.db = new Database(path.join(this.indexDir, DB_FILE));\n applyIndexStorePragmas(this.db);\n this.initSchema();\n }\n\n runWithRetry<T>(fn: () => T): T {\n return runSqliteWithRetry(fn);\n }\n\n private initSchema(): void {\n this.db.exec(METADATA_TABLE_SQL);\n\n // Schema migration: the index is derived, rebuildable data \u2014 on any\n // version mismatch we drop everything and let the next index run repopulate\n // from source, instead of maintaining per-version migration scripts.\n const storedRows = this.stmt('SELECT value FROM metadata WHERE key = ?').all('version') as {\n value: string;\n }[];\n const storedVersion = storedRows.length ? Number(storedRows[0]?.value) : null;\n if (storedVersion !== null && storedVersion !== SCHEMA_VERSION) {\n this.db.exec(`\n DROP TABLE IF EXISTS symbols;\n DROP TABLE IF EXISTS files;\n DROP TABLE IF EXISTS refs;\n `);\n this.db.exec('DROP TABLE IF EXISTS symbols_fts');\n this.stmt('UPDATE metadata SET value = ? WHERE key = ?').run(\n String(SCHEMA_VERSION),\n 'version',\n );\n } else if (storedVersion === null) {\n this.stmt('INSERT INTO metadata(key, value) VALUES (?, ?)').run(\n 'version',\n String(SCHEMA_VERSION),\n );\n }\n\n this.db.exec(CORE_TABLES_SQL);\n for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);\n this.db.exec(REFS_TABLE_SQL);\n for (const sql of REFS_INDEX_SQL) this.db.exec(sql);\n\n // FTS5 full-text index over the camelCase-split symbol text; rowid is the\n // symbol id. Replaces the old `LIKE '%token%'` full-table scan + per-query\n // in-process BM25 build: MATCH uses the inverted index and bm25() ranks\n // natively. Kept in sync explicitly in insertSymbols/delete*/clearAll.\n try {\n this.db.exec(SYMBOLS_FTS_SQL);\n this.ftsAvailable = true;\n // A database may have been populated by a runtime without FTS5. Backfill\n // the derived table when FTS later becomes available instead of making\n // every historical symbol invisible until a forced rebuild.\n const symbolCount = Number(\n (this.stmt('SELECT COUNT(*) AS n FROM symbols').get() as { n?: number } | undefined)?.n ??\n 0,\n );\n const ftsCount = Number(\n (this.stmt('SELECT COUNT(*) AS n FROM symbols_fts').get() as { n?: number } | undefined)\n ?.n ?? 0,\n );\n if (symbolCount !== ftsCount) {\n this.db.exec('DELETE FROM symbols_fts');\n const rows = this.stmt(\n 'SELECT id, name, signature, doc_comment FROM symbols ORDER BY id',\n ).all() as Array<{ id: number; name: string; signature: string; doc_comment: string }>;\n bulkInsertFtsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n this.ftsAvailable,\n rows.map((row) => ({\n id: row.id,\n text: buildIndexableText(row.name, row.signature, row.doc_comment),\n })),\n );\n // The drift repair doesn't mutate `symbols`, but the drift may have\n // been caused by an external mutation that left the BM25 cache stale.\n // Invalidate so the next fallback search rebuilds from the repaired\n // FTS state rather than serving a frozen corpus.\n this.invalidateBm25();\n }\n } catch {\n // SQLite built without FTS5 \u2014 searchRanked falls back to LIKE + BM25.\n this.ftsAvailable = false;\n }\n\n // Seed the symbol-id sequence once. Subsequent allocations are O(1)\n // metadata updates instead of SELECT MAX(id) on every insert batch.\n this.ensureNextSymbolIdSeeded();\n }\n\n // \u2500\u2500\u2500 ID allocation & bulk helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private static readonly NEXT_SYMBOL_ID_KEY = 'next_symbol_id';\n /** Stay under typical SQLite SQLITE_MAX_VARIABLE_NUMBER (often 999). */\n private static readonly MAX_SQL_VARS = 900;\n\n /**\n * Ensure `metadata.next_symbol_id` exists. Safe to call outside a write\n * transaction on open; the first concurrent writer under BEGIN IMMEDIATE\n * re-reads and advances the counter atomically.\n */\n private ensureNextSymbolIdSeeded(): void {\n const existing = this.stmt('SELECT value FROM metadata WHERE key = ?').get(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n ) as { value?: string } | undefined;\n if (existing?.value !== undefined) return;\n const maxRows = this.stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n const next = (maxRows[0]?.m ?? 0) + 1;\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)').run(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n String(next),\n );\n }\n\n /**\n * Reserve `count` consecutive symbol ids. MUST run inside BEGIN IMMEDIATE\n * so concurrent indexers cannot hand out overlapping ranges.\n */\n private allocateSymbolIds(count: number): number {\n if (count <= 0) return this.getMaxSymbolId() + 1;\n this.ensureNextSymbolIdSeeded();\n const row = this.stmt('SELECT value FROM metadata WHERE key = ?').get(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n ) as { value?: string } | undefined;\n const start = Math.max(1, Number(row?.value ?? 1) || 1);\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)').run(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n String(start + count),\n );\n return start;\n }\n\n /**\n * Disconnect inbound refs before their target symbols are replaced and\n * return the affected names for scoped re-resolution.\n *\n * This also repairs a long-standing dangling-id edge case: `refs.to_id` has\n * no physical FK, so deleting a symbol previously left callers pointing at a\n * non-existent row.\n */\n private invalidateIncomingRefsForFiles(files: string[]): string[] {\n if (files.length === 0) return [];\n const placeholders = files.map(() => '?').join(',');\n const names = (\n this.stmt(`SELECT DISTINCT name FROM symbols WHERE file IN (${placeholders})`).all(\n ...files,\n ) as Array<{ name: string }>\n ).map((row) => row.name);\n this.stmt(\n `UPDATE refs SET to_id = NULL\n WHERE to_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n ).run(...files);\n return names;\n }\n\n /** Resolve only refs whose target names may have changed. */\n private resolveRefsForNamesUnsafe(names: Iterable<string>): number {\n const unique = [...new Set(names)].filter(Boolean);\n let changes = 0;\n for (let start = 0; start < unique.length; start += IndexStore.MAX_SQL_VARS) {\n const chunk = unique.slice(start, start + IndexStore.MAX_SQL_VARS);\n const placeholders = chunk.map(() => '?').join(',');\n const result = this.stmt(\n `UPDATE refs\n SET to_id = (SELECT MIN(id) FROM symbols WHERE name = refs.to_name)\n WHERE to_name IN (${placeholders})`,\n ).run(...chunk) as { changes?: number };\n changes += result.changes ?? 0;\n }\n return changes;\n }\n\n // \u2500\u2500\u2500 Symbol CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert symbols, assigning IDs atomically inside `BEGIN IMMEDIATE` /\n * `COMMIT`. Id ranges come from the `next_symbol_id` metadata counter\n * (O(1)); multi-row INSERT amortizes bind overhead for large files.\n *\n * @returns The symbols array with `id` fields populated so the caller can\n * use them for refs without re-reading from the DB.\n */\n insertSymbols(symbols: IndexSymbol[]): IndexSymbol[] {\n this.invalidateBm25();\n return this.runWithRetry(() => {\n // BEGIN IMMEDIATE serializes writers so allocateSymbolIds cannot overlap.\n this.db.exec('BEGIN IMMEDIATE');\n try {\n let nextId = this.allocateSymbolIds(symbols.length);\n const result: IndexSymbol[] = [];\n const bulk: BulkSymbolRow[] = [];\n const ftsRows: Array<{ id: number; text: string }> = [];\n\n for (const s of symbols) {\n const id = nextId++;\n bulk.push({\n id,\n lang: s.lang,\n kind: s.kind,\n name: s.name,\n file: s.file,\n line: s.line,\n col: s.col,\n signature: s.signature,\n docComment: s.docComment,\n scope: s.scope,\n text: s.text,\n });\n if (this.ftsAvailable) {\n ftsRows.push({ id, text: buildIndexableText(s.name, s.signature, s.docComment) });\n }\n result.push({ ...s, id });\n }\n bulkInsertSymbolsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, bulk);\n bulkInsertFtsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n this.ftsAvailable,\n ftsRows,\n );\n\n this.db.exec('COMMIT');\n return result;\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n deleteSymbolsForFile(file: string): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = this.invalidateIncomingRefsForFiles([file]);\n if (this.ftsAvailable) {\n this.stmt(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(file);\n }\n this.stmt('DELETE FROM symbols WHERE file_fk = ?').run(file);\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n } catch (error) {\n this.db.exec('ROLLBACK');\n throw error;\n }\n });\n }\n\n /**\n * Remove every trace of a file (refs, symbols, FTS rows, file meta). Used\n * when a source file disappears between index runs \u2014 previously this only\n * dropped the `files` row, leaving its symbols orphaned but still searchable.\n */\n deleteFile(file: string): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = this.invalidateIncomingRefsForFiles([file]);\n if (this.ftsAvailable) {\n this.stmt(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(file);\n }\n this.stmt(\n 'DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(file);\n this.stmt('DELETE FROM symbols WHERE file_fk = ?').run(file);\n this.stmt('DELETE FROM files WHERE file = ?').run(file);\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n // \u2500\u2500\u2500 File metadata \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n upsertFile(meta: FileMeta): void {\n this.runWithRetry(() => {\n this.stmt(\n `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(file) DO UPDATE SET\n lang = excluded.lang,\n mtime_ms = excluded.mtime_ms,\n symbol_count = excluded.symbol_count,\n last_indexed = excluded.last_indexed`,\n ).run(meta.file, meta.lang, meta.mtimeMs, meta.symbolCount, meta.lastIndexed);\n });\n }\n\n getFileMeta(file: string): FileMeta | null {\n return getFileMetaWithStatement((sql) => this.stmt(sql), file);\n }\n\n getAllFileMetas(): FileMeta[] {\n return getAllFileMetasWithStatement((sql) => this.stmt(sql));\n }\n\n // \u2500\u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n search(\n query: string,\n filter?: WriterSearchFilter,\n opts?: { limit?: number | undefined },\n ): SearchResult[] {\n const built = this.buildSearchWhere(query, filter);\n if (built === null) return [];\n\n const { where, values } = built;\n const limit = normalizeSearchLimit(opts?.limit);\n const limitSql = limit !== undefined ? ' LIMIT ?' : '';\n const sql = `SELECT id, lang, kind, name, file, line, col, signature, doc_comment, text FROM symbols ${where}${limitSql}`;\n\n const binds = limit !== undefined ? [...values, limit] : values;\n const rows = this.stmt(sql).all(\n ...(binds as (string | number)[]),\n ) as unknown as WriterSearchRow[];\n\n return rows.map((row) => mapWriterSearchRow(row, filter?.lspKind));\n }\n\n /** Shared WHERE builder for {@link search} / empty-query ranked totals. */\n private buildSearchWhere(query: string, filter?: WriterSearchFilter | undefined) {\n return buildWriterSearchWhere(query, filter);\n }\n\n private countSearch(query: string, filter?: WriterSearchFilter | undefined): number {\n const built = this.buildSearchWhere(query, filter);\n if (built === null) return 0;\n const row = this.stmt(`SELECT COUNT(*) AS n FROM symbols ${built.where}`).get(\n ...(built.values as (string | number)[]),\n ) as { n?: number } | undefined;\n return Number(row?.n ?? 0);\n }\n\n /**\n * Ranked search \u2014 the one-stop query the codebase-search tool and plug-lsp\n * use. With FTS5 this is a single indexed `MATCH` ranked by SQLite's native\n * `bm25()` with a built-in `snippet()`; without FTS5 it falls back to the\n * legacy LIKE scan + in-process BM25 (identical semantics, slower).\n *\n * Tokens are matched as prefixes (`\"tok\"*`), mirroring the old\n * `LIKE '%tok%'` recall for the common symbol-search shapes (\"user\" finds\n * \"users\", camelCase-split text makes \"complex\" find \"complexOperation\").\n */\n searchRanked(\n query: string,\n filter: WriterSearchFilter | undefined,\n limit: number,\n ): { results: SearchResult[]; total: number } {\n const rawLimit = Number.isFinite(limit) ? Math.trunc(limit) : 20;\n const safeLimit = Math.max(1, Math.min(rawLimit, 100));\n const tokens = tokenise(query);\n // No usable tokens \u2192 plain filtered listing (matches old `search('')`).\n if (tokens.length === 0 || !this.ftsAvailable) {\n return this.searchRankedFallback(query, filter, safeLimit);\n }\n\n let effectiveKind: SymbolKind | undefined = filter?.kind;\n if (filter?.lspKind !== undefined) {\n const mapped = lspKindToInternalKind(filter.lspKind);\n if (mapped === null) return { results: [], total: 0 };\n effectiveKind = mapped;\n }\n\n // Each token is quoted (neutralises FTS5 query syntax) and prefix-starred.\n const match = tokens.map((t) => `\"${t.replaceAll('\"', '')}\"*`).join(' OR ');\n\n const conditions: string[] = ['symbols_fts MATCH ?'];\n const values: (string | number)[] = [match];\n if (effectiveKind) {\n conditions.push('s.kind = ?');\n values.push(effectiveKind);\n }\n if (filter?.lang) {\n conditions.push('s.lang = ?');\n values.push(filter.lang);\n }\n if (filter?.file) {\n conditions.push(\"replace(s.file, '\\\\', '/') LIKE ? ESCAPE '\\\\'\");\n values.push(`%${escapeLike(filter.file.replace(/\\\\/g, '/'))}%`);\n }\n const where = conditions.join(' AND ');\n\n const countRows = this.stmt(\n `SELECT COUNT(*) AS n FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid WHERE ${where}`,\n ).all(...values) as { n: number }[];\n const total = countRows[0] ? Number(countRows[0].n) : 0;\n if (total === 0) return { results: [], total: 0 };\n\n const rows = this.stmt(\n `SELECT s.id, s.lang, s.kind, s.name, s.file, s.line, s.col, s.signature, s.doc_comment,\n -bm25(symbols_fts) AS score,\n snippet(symbols_fts, 0, '', '', '\u2026', 12) AS snippet\n FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid\n WHERE ${where}\n ORDER BY\n CASE WHEN lower(s.name) = lower(?) THEN 0\n WHEN lower(s.name) LIKE lower(?) ESCAPE '\\\\' THEN 1\n ELSE 2 END,\n bm25(symbols_fts), lower(s.name), s.file, s.line, s.col, s.id\n LIMIT ?`,\n ).all(...values, query.trim(), `${escapeLike(query.trim())}%`, safeLimit) as {\n id: number;\n lang: string;\n kind: string;\n name: string;\n file: string;\n line: number;\n col: number;\n signature: string;\n doc_comment: string;\n score: number;\n snippet: string;\n }[];\n\n return {\n results: rows.map((row) =>\n mapWriterSearchRow(row, filter?.lspKind, Math.max(0.0001, row.score), row.snippet),\n ),\n total,\n };\n }\n\n /**\n * Invalidate the cached BM25 index.\n *\n * **Contract: every method that mutates `symbols` MUST call this before\n * returning.** (`refs` mutations do not affect the BM25 fallback because\n * the corpus is built from `symbols.text` via `getAllIndexable()` and the\n * BM25 score is filtered by the LIKE-selected candidate set in\n * `searchRankedFallback`.) Today the call sites are `repairDrift`,\n * `insertSymbols`, `deleteSymbolsForFile`, `deleteFile`, `clearAll`, and\n * `commitBatch`. A future mutation that adds a new write path (e.g.\n * `renameFile`, `updateSignature`) MUST also call this \u2014 otherwise the\n * FTS5-unavailable fallback will serve stale search results. The\n * `close()` reset at L1820-1821 tears the cache down on store shutdown,\n * which is the only legitimate place that flips the flag outside this\n * helper.\n *\n * Called *before* `runWithRetry` on purpose: if the write fails all\n * retries the flag stays set, forcing a rebuild on the next search rather\n * than trusting a cache that may not reflect the intended mutation.\n * Do not move this inside the retry closure.\n */\n private invalidateBm25(): void {\n this.bm25Dirty = true;\n this.bm25Cache = null;\n }\n\n /**\n * Return the cached full-corpus BM25 index, rebuilding it only when the\n * symbols table has been mutated since the last build. The full-corpus IDF\n * is more correct than the old per-query candidate-subset IDF, and the\n * amortized build cost drops from O(symbols \u00D7 tokens) per search to once\n * per write batch.\n *\n * Note: the first call after a long idle (or on a freshly opened store)\n * pays the full corpus rebuild synchronously on the search path. For a\n * 5 500+ symbol corpus this is a visible one-time latency spike.\n */\n private getOrBuildBm25(): Bm25Index {\n if (this.bm25Cache && !this.bm25Dirty) return this.bm25Cache;\n const docs = this.getAllIndexable();\n this.bm25Cache = buildBm25Index(docs);\n this.bm25Dirty = false;\n return this.bm25Cache;\n }\n\n /** Legacy ranked path: LIKE candidates + in-process BM25 + JS snippets. */\n private searchRankedFallback(\n query: string,\n filter: WriterSearchFilter | undefined,\n limit: number,\n ): { results: SearchResult[]; total: number } {\n // Empty query = filtered listing: push LIMIT into SQL so a 10k-symbol\n // corpus never materializes fully just to take the first N rows.\n if (!query.trim()) {\n const total = this.countSearch(query, filter);\n if (total === 0) return { results: [], total: 0 };\n return { results: this.search(query, filter, { limit }), total };\n }\n\n const candidates = this.search(query, filter);\n if (candidates.length === 0) return { results: [], total: 0 };\n\n const candidateById = new Map(candidates.map((c) => [c.id, c]));\n // Use the cached full-corpus BM25 index instead of rebuilding from the\n // LIKE candidate subset on every query. The filter restricts scoring to\n // candidates; full-corpus IDF is more correct than subset IDF.\n const bm25 = this.getOrBuildBm25();\n const scored = bm25.score(query, (id) => candidateById.has(id));\n const q = query.trim().toLowerCase();\n const rank = (id: number): number => {\n const name = candidateById.get(id)?.name.toLowerCase() ?? '';\n if (name === q) return 0;\n if (name.startsWith(q)) return 1;\n return 2;\n };\n scored.sort((a, b) => {\n const rankDiff = rank(a.id) - rank(b.id);\n if (rankDiff !== 0) return rankDiff;\n const scoreDiff = b.score - a.score;\n if (scoreDiff !== 0) return scoreDiff;\n const left = expectDefined(candidateById.get(a.id));\n const right = expectDefined(candidateById.get(b.id));\n return (\n left.name.localeCompare(right.name) ||\n left.file.localeCompare(right.file) ||\n left.line - right.line ||\n left.col - right.col ||\n left.id - right.id\n );\n });\n const qTokens = tokenise(query);\n\n const results = scored.slice(0, limit).map(({ id, score }) => {\n const c = expectDefined(candidateById.get(id));\n return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };\n });\n return { results, total: candidates.length };\n }\n\n getAllIndexable(): Array<{ id: number; text: string }> {\n return getAllIndexableWithStatement((sql) => this.stmt(sql));\n }\n\n /**\n * Largest symbol id currently in the table (0 when empty). New ids must be\n * allocated from this, NOT from `COUNT(*)`: incremental reindexes delete a\n * changed file's rows, so the row count drops below the max id and a\n * count-based id would collide with a surviving row (UNIQUE constraint on\n * `symbols.id`). Ids may have gaps \u2014 that is fine.\n */\n getMaxSymbolId(): number {\n return getMaxSymbolIdWithStatement((sql) => this.stmt(sql));\n }\n\n // \u2500\u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): IndexStats {\n return getStatsWithStatement((sql) => this.stmt(sql), this.indexDir);\n }\n\n setLastIndexed(ts: number): void {\n this.runWithRetry(() => {\n this.stmt(\"INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)\").run(\n String(ts),\n );\n });\n }\n\n getMetadata(key: string): string | undefined {\n return getMetadataWithStatement((sql) => this.stmt(sql), key);\n }\n\n setMetadata(key: string, value: string): void {\n this.runWithRetry(() => {\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES(?, ?)').run(key, value);\n });\n }\n\n clearAll(): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n // DROP+CREATE is O(1) page-truncation vs DELETE's full-table scan.\n // Force-rebuilds (schema version change, manual /codebase-reindex)\n // were spending hundreds of ms on row-by-row deletion of thousands of\n // symbols and refs. DROP is instant regardless of table size.\n this.db.exec('DROP TABLE IF EXISTS refs');\n this.db.exec('DROP TABLE IF EXISTS symbols');\n this.db.exec('DROP TABLE IF EXISTS files');\n this.db.exec('DROP TABLE IF EXISTS metadata');\n if (this.ftsAvailable) this.db.exec('DROP TABLE IF EXISTS symbols_fts');\n this.db.exec('COMMIT');\n // Clear statement cache \u2014 prepared stmts reference the now-dropped tables.\n this.stmtCache.clear();\n this.initSchema();\n // Reset the symbol-id counter to 1 so repeated forced rebuilds\n // don't allocate from a monotonically growing id namespace.\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)').run(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n '1',\n );\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n // \u2500\u2500\u2500 Ref CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert cross-references for a given source symbol id.\n * Replaces any existing refs from the same source (idempotent on re-index).\n */\n insertRefs(fromId: number, refs: Ref[]): void {\n this.runWithRetry(() => {\n // Delete old refs from this symbol (handles re-index)\n this.stmt('DELETE FROM refs WHERE from_id = ?').run(fromId);\n if (refs.length === 0) return;\n bulkInsertRefsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n refs.map((ref) => ({ ...ref, fromId })),\n );\n });\n }\n\n /**\n * Bulk-insert refs for many source symbols in a single transaction.\n *\n * Unlike {@link insertRefs} this does NOT delete per source id \u2014 the caller\n * (the indexer) has already cleared stale refs for the file via\n * {@link deleteRefsForFile}, so the per-source DELETE would be redundant work\n * repeated once per symbol. One transaction for the whole file instead of one\n * per symbol turns an O(symbols) transaction count into O(1).\n *\n * Each ref's own {@link Ref.fromId} is used; pass an empty array to no-op.\n */\n insertRefsBatch(refs: Ref[]): void {\n if (refs.length === 0) return;\n this.runWithRetry(() => {\n bulkInsertRefsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, refs);\n });\n }\n\n /**\n * Commit a batch of file-level symbol/refs/upserts in a single transaction.\n *\n * Used by the indexer to amortize SQLite commit overhead across many files.\n * Before this, the indexer issued one transaction per file (BEGIN IMMEDIATE\n * for symbols, plus per-file deletes and an upsertFile call), so a 20-file\n * parallel batch cost ~5+ transactions \u00D7 20 files = 100+ commits. With\n * this entry point we do exactly one BEGIN/COMMIT per parallel batch.\n *\n * Each entry must already be a fully-parsed FileSymbols (symbols + refs).\n * The caller is responsible for the per-file prefix accounting\n * (refsByLine \u2192 flat list with `fromId` populated). `deleteForFiles` lets\n * the caller clear stale symbols/refs for any files being re-indexed before\n * the inserts run (required to keep refs \u2192 symbols FK invariants).\n *\n * Returns the symbols back with their assigned `id` (same shape as\n * {@link insertSymbols}) so callers can build final per-file results.\n */\n commitBatch(\n entries: Array<{\n file: string;\n lang: SymbolLang;\n symbols: IndexSymbol[];\n refs: Ref[];\n mtimeMs: number;\n symbolCount: number;\n }>,\n options: { deleteForFiles?: string[] | undefined } = {},\n ): IndexSymbol[] {\n if (entries.length === 0 && (options.deleteForFiles?.length ?? 0) === 0) {\n return [];\n }\n this.invalidateBm25();\n return this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = new Set<string>();\n for (const entry of entries) {\n for (const symbol of entry.symbols) affectedNames.add(symbol.name);\n for (const ref of entry.refs) affectedNames.add(ref.toName);\n }\n // 1) Clear stale refs+symbols for any files being re-indexed.\n if (options.deleteForFiles && options.deleteForFiles.length > 0) {\n const placeholders = options.deleteForFiles.map(() => '?').join(',');\n for (const name of this.invalidateIncomingRefsForFiles(options.deleteForFiles)) {\n affectedNames.add(name);\n }\n if (this.ftsAvailable) {\n this.stmt(\n `DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n ).run(...options.deleteForFiles);\n }\n // Refs first (FK direction: refs.from_id \u2192 symbols.id).\n this.stmt(\n `DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n ).run(...options.deleteForFiles);\n this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(\n ...options.deleteForFiles,\n );\n }\n\n // 2) Assign ids + multi-row insert symbols (+ FTS).\n const totalSymbols = entries.reduce((n, e) => n + e.symbols.length, 0);\n let nextId = this.allocateSymbolIds(totalSymbols);\n\n const allInserted: IndexSymbol[] = [];\n const refsToInsert: Ref[] = [];\n const bulkSyms: BulkSymbolRow[] = [];\n const ftsRows: Array<{ id: number; text: string }> = [];\n\n for (const entry of entries) {\n const insertedForEntry: IndexSymbol[] = [];\n for (const s of entry.symbols) {\n const id = nextId++;\n bulkSyms.push({\n id,\n lang: s.lang,\n kind: s.kind,\n name: s.name,\n file: s.file,\n line: s.line,\n col: s.col,\n signature: s.signature,\n docComment: s.docComment,\n scope: s.scope,\n text: s.text,\n });\n if (this.ftsAvailable) {\n ftsRows.push({\n id,\n text: buildIndexableText(s.name, s.signature, s.docComment),\n });\n }\n const inserted = { ...s, id };\n allInserted.push(inserted);\n insertedForEntry.push(inserted);\n }\n refsToInsert.push(...assignRefsToSymbols(entry.refs, insertedForEntry));\n }\n\n bulkInsertSymbolsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, bulkSyms);\n bulkInsertFtsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n this.ftsAvailable,\n ftsRows,\n );\n\n // 3) Multi-row insert all refs.\n bulkInsertRefsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, refsToInsert);\n\n // 4) Upsert file metadata for every entry (small N \u2014 single-row is fine).\n const upsertStmt = this.stmt(\n `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(file) DO UPDATE SET\n lang = excluded.lang,\n mtime_ms = excluded.mtime_ms,\n symbol_count = excluded.symbol_count,\n last_indexed = excluded.last_indexed`,\n );\n const now = Date.now();\n for (const entry of entries) {\n upsertStmt.run(entry.file, entry.lang, entry.mtimeMs, entry.symbolCount, now);\n }\n\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n return allInserted;\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n /**\n * Delete all refs whose source symbols are in a given file.\n * Used when re-indexing a file to clear stale refs.\n */\n deleteRefsForFile(file: string): void {\n this.runWithRetry(() => {\n this.stmt('DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)').run(\n file,\n );\n });\n }\n\n /**\n * Resolve `to_name` \u2192 `to_id` for all refs that have a name but no id.\n * Call this after all symbols have been inserted to fill in cross-references.\n *\n * Single statement: the `to_name IN (SELECT name FROM symbols)` guard restricts\n * the UPDATE to refs that will actually resolve, so `.changes` counts only refs\n * that found a target \u2014 matching the previous per-row loop's return value.\n */\n resolveRefs(): number {\n return this.runWithRetry(() => {\n // Prefer UPDATE-FROM with a pre-aggregated name\u2192id map (SQLite \u2265 3.33).\n // One hash join instead of a correlated subquery per unresolved row.\n // MIN(id) matches the previous LIMIT 1 / arbitrary-first semantics when\n // multiple symbols share a name.\n try {\n const result = this.stmt(\n `UPDATE refs\n SET to_id = s.id\n FROM (\n SELECT name, MIN(id) AS id FROM symbols GROUP BY name\n ) AS s\n WHERE refs.to_id IS NULL\n AND refs.to_name IS NOT NULL\n AND refs.to_name = s.name`,\n ).run() as { changes?: number };\n return result.changes ?? 0;\n } catch {\n const result = this.stmt(\n `UPDATE refs SET to_id = (\n SELECT id FROM symbols WHERE name = refs.to_name LIMIT 1\n ) WHERE to_id IS NULL AND to_name IS NOT NULL\n AND to_name IN (SELECT name FROM symbols)`,\n ).run() as { changes?: number };\n return result.changes ?? 0;\n }\n });\n }\n\n resolveRefsForNames(names: Iterable<string>): number {\n return this.runWithRetry(() => this.resolveRefsForNamesUnsafe(names));\n }\n\n /**\n * Clear symbols/refs for a file and mark it as indexed with zero symbols.\n * Used by the indexer for empty-parse results so three writes share one txn.\n */\n replaceEmptyFile(meta: FileMeta): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = this.invalidateIncomingRefsForFiles([meta.file]);\n if (this.ftsAvailable) {\n this.stmt(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(meta.file);\n }\n this.stmt(\n 'DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(meta.file);\n this.stmt('DELETE FROM symbols WHERE file_fk = ?').run(meta.file);\n this.stmt(\n `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(file) DO UPDATE SET\n lang = excluded.lang,\n mtime_ms = excluded.mtime_ms,\n symbol_count = excluded.symbol_count,\n last_indexed = excluded.last_indexed`,\n ).run(meta.file, meta.lang, meta.mtimeMs, meta.symbolCount, meta.lastIndexed);\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n /** Best-effort query planner refresh after a large reindex. */\n optimize(): void {\n try {\n this.db.exec('PRAGMA optimize');\n } catch {\n /* optional */\n }\n }\n\n /**\n * Reclaim page churn left by repeated force rebuilds.\n *\n * SQLite's DROP/CREATE path makes rebuilds fast but leaves pages on the\n * freelist. Compact only large, materially sparse databases and only when the\n * caller is already on a full-index maintenance path.\n */\n compactIfNeeded(options: { minBytes?: number; minFreeRatio?: number } = {}): boolean {\n const minBytes = options.minBytes ?? 256 * 1024 * 1024;\n const minFreeRatio = options.minFreeRatio ?? 0.35;\n try {\n const pageCount = Number(\n (this.stmt('PRAGMA page_count').get() as { page_count?: number } | undefined)?.page_count ??\n 0,\n );\n const pageSize = Number(\n (this.stmt('PRAGMA page_size').get() as { page_size?: number } | undefined)?.page_size ?? 0,\n );\n const freePages = Number(\n (this.stmt('PRAGMA freelist_count').get() as { freelist_count?: number } | undefined)\n ?.freelist_count ?? 0,\n );\n if (\n pageCount <= 0 ||\n pageSize <= 0 ||\n pageCount * pageSize < minBytes ||\n freePages / pageCount < minFreeRatio\n ) {\n return false;\n }\n this.runWithRetry(() => {\n this.db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n this.db.exec('VACUUM');\n this.db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n });\n return true;\n } catch {\n // Compaction is maintenance, never a reason to fail a valid index run.\n return false;\n }\n }\n\n /**\n * Find all references TO a given symbol (who calls / uses this symbol?).\n */\n findRefsTo(symbolId: number): Ref[] {\n return findRefsToWithStatement((sql) => this.stmt(sql), symbolId);\n }\n\n /**\n * Find all references FROM a given symbol (what does this symbol call/use?).\n */\n findRefsFrom(symbolId: number): Ref[] {\n return findRefsFromWithStatement((sql) => this.stmt(sql), symbolId);\n }\n\n // \u2500\u2500\u2500 CodeMap graph aggregation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Package-level graph: each workspace package is a node; edges are derived\n * from cross-package symbol references (a symbol in package A references a\n * symbol resolved in package B). Node metadata includes symbol/file counts.\n */\n getPackageGraph(): CodeMapGraph {\n return getPackageGraphWithStatement((sql) => this.stmt(sql));\n }\n\n /**\n * File-level graph for a single package: each file is a node; edges are\n * derived from cross-file symbol references within the package.\n */\n getFileGraph(packageFilter: string): CodeMapGraph {\n return getFileGraphWithStatement((sql) => this.stmt(sql), packageFilter);\n }\n\n /**\n * Symbol-level graph for a single file: each symbol is a node; edges are\n * derived from intra-file and cross-file symbol references (who calls whom).\n */\n getSymbolGraph(fileFilter: string): CodeMapGraph {\n return getSymbolGraphWithStatement((sql) => this.stmt(sql), fileFilter);\n }\n\n /**\n * Returns every symbol in the index. Used by dead-code analysis to\n * build the full symbol universe for the reachability scan.\n */\n getAllSymbols(): Array<{\n id: number;\n name: string;\n file: string;\n kind: SymbolKind;\n line: number;\n }> {\n return (\n this.stmt('SELECT id, name, file, kind, line FROM symbols ORDER BY id').all() as Array<{\n id: number;\n name: string;\n file: string;\n kind: string;\n line: number;\n }>\n ).map((r) => ({ ...r, kind: r.kind as SymbolKind }));\n }\n\n /**\n * Returns every resolved reference (to_id IS NOT NULL). Used by\n * dead-code analysis to build the consumer-ship graph. Refs whose\n * target symbol id is null (unresolved imports) are excluded.\n */\n getAllResolvedRefs(): Array<{\n fromId: number;\n toId: number;\n callType: string;\n }> {\n return this.stmt(\n 'SELECT from_id AS fromId, to_id AS toId, call_type AS callType FROM refs WHERE to_id IS NOT NULL',\n ).all() as Array<{ fromId: number; toId: number; callType: string }>;\n }\n\n /**\n * Returns ALL import refs (including unresolved) with their source-file\n * path and resolved target id. Used by the dead-code scan's file-level\n * graph traversal to handle barrel-only entry points where no symbol\n * carries the ref.\n *\n * Refs whose `from_id` doesn't match a known symbol (e.g. pure-barrel\n * files with no declarations) will have `sourceFile === null`.\n */\n getAllImportRefs(): Array<{\n /** Source-file path (null when the owning symbol can't be resolved). */\n sourceFile: string | null;\n toName: string;\n /** Resolved target symbol id (null when the name couldn't be matched). */\n toId: number | null;\n callType: string;\n line: number;\n }> {\n return this.stmt(\n `SELECT s.file AS sourceFile, r.to_name AS toName, r.to_id AS toId,\n r.call_type AS callType, r.line\n FROM refs r\n LEFT JOIN symbols s ON r.from_id = s.id\n WHERE r.call_type = 'import'\n ORDER BY r.line`,\n ).all() as Array<{\n sourceFile: string | null;\n toName: string;\n toId: number | null;\n callType: string;\n line: number;\n }>;\n }\n\n close(): void {\n // Drop cached StatementSync references before closing; db.close() finalizes\n // them, and keeping the map would retain handles to a dead connection.\n this.stmtCache.clear();\n // Release the BM25 cache and mark dirty so a hypothetical reopen starts\n // from a clean slate instead of serving a stale index.\n this.bm25Dirty = true;\n this.bm25Cache = null;\n try {\n this.db.close();\n } catch {\n /* already closed */\n }\n }\n}\n\n/** Process-wide singleton pool. */\nexport const indexStorePool = new StorePool(\n (projectRoot: string, opts?: { indexDir?: string | undefined }) =>\n new IndexStore(projectRoot, opts),\n);\n", "/**\n * BM25 ranking implementation \u2014 no external dependencies.\n *\n * Algorithm: Okapi BM25 with standard parameters (k1=1.5, b=0.75).\n */\n\nconst K1 = 1.5;\nconst B = 0.75;\n\ninterface Bm25Doc {\n id: number;\n tokens: string[];\n raw: string;\n len: number;\n}\n\n/** Tokenise a string into lowercase word tokens. */\nexport function tokenise(text: string): string[] {\n // Preserve all Unicode letters + digits + $ + '. Split on everything else.\n const sanitised = text.replace(/[^\\p{L}\\p{N}$'_]/gu, ' ').replace(/_/g, ' ');\n return sanitised.toLowerCase().split(' ').filter(Boolean);\n}\n\nexport interface IndexableDoc {\n id: number;\n text: string;\n}\n\n/**\n * Split a camelCase/SnakeCase identifier into its constituent words.\n * e.g. \"complexOperation\" \u2192 \"complex Operation\"\n * \"foo_bar_baz\" \u2192 \"foo bar baz\"\n * This allows a query for \"complex\" to match \"complexOperation\"\n * via the shared \"complex\" token.\n */\nfunction splitName(name: string): string {\n return name\n // Split an acronym from the word that follows it: HTTPServer \u2192 HTTP Server.\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n // Identifiers commonly encode versions/algorithms with digits.\n .replace(/([\\p{L}])(\\d)/gu, '$1 $2')\n .replace(/(\\d)([\\p{L}])/gu, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .trim();\n}\n\n/**\n * Build indexable text for BM25 from a symbol's fields.\n * The name is split into camelCase/SnakeCase words so that queries\n * like \"complex\" match \"complexOperation\". The verbatim name is\n * also included for exact-match queries.\n */\nexport function buildIndexableText(name: string, signature: string, docComment: string): string {\n return [splitName(name), name, signature, docComment].filter(Boolean).join(' ');\n}\n\nexport function buildBm25Index(docs: IndexableDoc[]): Bm25Index {\n const documents: Bm25Doc[] = docs.map((d) => {\n const tokens = tokenise(d.text);\n return { id: d.id, tokens, raw: d.text, len: tokens.length };\n });\n\n const N = documents.length;\n const totalLen = documents.reduce((sum, d) => sum + d.len, 0);\n const avgLen = N === 0 ? 0 : totalLen / N;\n\n return new Bm25Index(documents, N, avgLen);\n}\n\nexport class Bm25Index {\n private readonly safeAvgLen: number;\n /** Lazily-built id\u2192doc index so getDoc is O(1) instead of an O(D) linear find. */\n private _byId: Map<number, Bm25Doc> | undefined;\n\n constructor(\n private documents: Bm25Doc[],\n private N: number,\n avgLen: number,\n ) {\n this.safeAvgLen = avgLen === 0 ? 1 : avgLen;\n }\n\n score(query: string, filter?: (id: number) => boolean): Array<{ id: number; score: number }> {\n const qTokens = tokenise(query);\n if (qTokens.length === 0) return [];\n\n // Precompute document frequency per query term once, outside the\n // per-document loop. The SQLite FTS path uses token-prefix matching;\n // we mirror that contract here so fallback ranking has identical recall.\n // Previously this was recomputed for every (document, term) pair \u2014\n // O(D\u00B2QT) \u2014 making large indexes (5500+ symbols) hit the 30s search\n // watchdog on every query.\n const dfByTerm = new Map<string, number>();\n for (const qTerm of qTokens) {\n let dfVal = 0;\n for (const candidate of this.documents) {\n if (candidate.tokens.some((t) => t.startsWith(qTerm))) dfVal++;\n }\n dfByTerm.set(qTerm, dfVal);\n }\n\n const results: Array<{ id: number; score: number }> = [];\n\n for (const doc of this.documents) {\n if (filter && !filter(doc.id)) continue;\n\n let docScore = 0;\n for (const qTerm of qTokens) {\n let tf = 0;\n for (const t of doc.tokens) {\n if (t.startsWith(qTerm)) tf++;\n }\n if (tf === 0) continue;\n\n const dfVal = dfByTerm.get(qTerm) ?? 0;\n if (dfVal === 0) continue;\n\n const idf = Math.log((this.N - dfVal + 0.5) / (dfVal + 0.5) + 1);\n const lenRatio = B * (doc.len / this.safeAvgLen);\n const tfComponent = (tf * (K1 + 1)) / (tf + K1 * (1 - B + lenRatio));\n\n docScore += idf * tfComponent;\n }\n\n if (docScore > 0) results.push({ id: doc.id, score: docScore });\n }\n\n return results;\n }\n\n getDoc(id: number): Bm25Doc | undefined {\n if (!this._byId) {\n this._byId = new Map(this.documents.map((d) => [d.id, d]));\n }\n return this._byId.get(id);\n }\n\n extractSnippet(docId: number, queryTokens: string[], radius = 40): string {\n const doc = this.getDoc(docId);\n if (!doc) return '';\n\n for (const tok of queryTokens) {\n const idx = doc.raw.toLowerCase().indexOf(tok);\n if (idx !== -1) {\n const start = Math.max(0, idx - radius);\n const end = Math.min(doc.raw.length, idx + tok.length + radius);\n const excerpt = doc.raw.slice(start, end);\n const ellipsis = '\\u2026';\n return (start > 0 ? ellipsis : '') + excerpt + (end < doc.raw.length ? ellipsis : '');\n }\n }\n return doc.raw.slice(0, radius * 2) + (doc.raw.length > radius * 2 ? '\\u2026' : '');\n }\n}\n", "/**\n * LSP SymbolKind mapping utilities.\n *\n * LSP SymbolKind numbers are defined by vscode-languageserver-protocol.\n * This module maps between LSP kind numbers and the internal SymbolKind taxonomy.\n */\n\nimport type { SymbolKind } from './schema.js';\n\n/**\n * LSP SymbolKind values (1\u201326) as defined by vscode-languageserver-protocol.\n */\nexport enum LSPSymbolKind {\n File = 1,\n Module = 2,\n Namespace = 3,\n Package = 4,\n Class = 5,\n Method = 6,\n Property = 7,\n Field = 8,\n Constructor = 9,\n Enum = 10,\n Interface = 11,\n Function = 12,\n Variable = 13,\n Constant = 14,\n String = 15,\n Number = 16,\n Boolean = 17,\n Array = 18,\n Object = 19,\n Key = 20,\n Null = 21,\n EnumMember = 22,\n Struct = 23,\n Event = 24,\n Operator = 25,\n TypeParameter = 26,\n}\n\n/**\n * Maps an LSP kind number to the corresponding internal SymbolKind.\n * Returns null if the LSP kind has no equivalent in the internal taxonomy.\n */\nexport function lspKindToInternalKind(k: number): SymbolKind | null {\n switch (k) {\n case LSPSymbolKind.Class: return 'class';\n case LSPSymbolKind.Method: return 'method';\n case LSPSymbolKind.Property:\n case LSPSymbolKind.Field: return 'property';\n case LSPSymbolKind.Constructor: return 'class';\n case LSPSymbolKind.Enum: return 'enum';\n case LSPSymbolKind.Interface: return 'interface';\n case LSPSymbolKind.Function: return 'function';\n case LSPSymbolKind.Variable: return 'var';\n case LSPSymbolKind.Constant: return 'const';\n case LSPSymbolKind.EnumMember: return 'enum';\n case LSPSymbolKind.TypeParameter:return 'type';\n case LSPSymbolKind.Namespace: return 'namespace';\n default: return null;\n }\n}\n\n/**\n * Maps an internal SymbolKind to the corresponding LSP kind number.\n * Returns null if the internal kind has no equivalent LSP kind.\n */\nexport function internalKindToLspKind(k: SymbolKind): number | null {\n switch (k) {\n case 'class': return LSPSymbolKind.Class;\n case 'method': return LSPSymbolKind.Method;\n case 'property': return LSPSymbolKind.Property;\n case 'function': return LSPSymbolKind.Function;\n case 'var': return LSPSymbolKind.Variable;\n case 'const': return LSPSymbolKind.Constant;\n case 'let': return LSPSymbolKind.Variable;\n case 'enum': return LSPSymbolKind.Enum;\n case 'interface': return LSPSymbolKind.Interface;\n case 'namespace': return LSPSymbolKind.Namespace;\n case 'type': return LSPSymbolKind.TypeParameter;\n // parameter and other internal-only kinds have no LSP equivalent\n default: return null;\n }\n}\n\n/**\n * Returns true if `k` is a valid LSP SymbolKind number (1\u201326).\n */\nexport function isLspKind(k: number): boolean {\n return Number.isInteger(k) && k >= 1 && k <= 26;\n}\n", "// \u2500\u2500\u2500 Symbol kind taxonomy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Language a symbol belongs to.\n *\n * First-class parsers exist for TS/JS, Go, Python, Rust, JSON, YAML.\n * All other langs are still indexed via the generic regex extractor so\n * monorepos are never silently skipped just because a native toolchain\n * is missing. `'other'` covers unusual extensions / special filenames.\n */\nexport type SymbolLang =\n | 'ts'\n | 'js'\n | 'tsx'\n | 'jsx'\n | 'go'\n | 'py'\n | 'rs'\n | 'json'\n | 'yaml'\n | 'c'\n | 'cpp'\n | 'java'\n | 'csharp'\n | 'php'\n | 'ruby'\n | 'swift'\n | 'kotlin'\n | 'scala'\n | 'shell'\n | 'sql'\n | 'md'\n | 'toml'\n | 'html'\n | 'css'\n | 'vue'\n | 'svelte'\n | 'dart'\n | 'lua'\n | 'r'\n | 'proto'\n | 'graphql'\n | 'zig'\n | 'elixir'\n | 'haskell'\n | 'other';\n\n/** What kind of symbol this is. */\nexport type SymbolKind =\n | 'class'\n | 'interface'\n | 'enum'\n | 'type'\n | 'function'\n | 'method'\n | 'var'\n | 'const'\n | 'let'\n | 'property'\n | 'parameter'\n | 'namespace'\n | 'object' // JSON root object\n | 'literal' // scalar value in JSON/YAML\n | 'schema' // JSON Schema $ref/$schema entry\n // Rust-specific\n | 'struct'\n | 'trait'\n | 'impl'\n | 'static'\n | 'mod';\n\n/** A single indexed code symbol. */\nexport interface Symbol {\n id: number;\n lang: SymbolLang;\n kind: SymbolKind;\n name: string;\n file: string; // absolute path\n line: number; // 1-based\n col: number; // 0-based\n signature: string; // e.g. \"function foo(a: string): Promise<void>\"\n docComment: string; // JSDoc / docstring first line\n scope: string; // e.g. \"MyClass.method\" or module-level \"\"\n text: string; // concatenated searchable text: name + signature + docComment\n}\n\n/** Extracted symbols and cross-references for one file. */\nexport interface FileSymbols {\n file: string;\n lang: SymbolLang;\n symbols: Symbol[];\n refs?: Ref[] | undefined; // cross-references extracted from this file (optional for back-compat)\n mtimeMs: number;\n}\n\n/** Source file metadata tracked for incremental indexing. */\nexport interface FileMeta {\n file: string;\n lang: SymbolLang;\n mtimeMs: number;\n symbolCount: number;\n lastIndexed: number; // unix ms\n}\n\n/** Statistics about the index. */\nexport interface IndexStats {\n totalSymbols: number;\n totalFiles: number;\n byLang: Record<SymbolLang, number>;\n byKind: Record<SymbolKind, number>;\n indexPath: string;\n lastIndexed: number | null;\n sizeBytes: number;\n version: number;\n}\n\n/** Result of a search query. */\nexport interface SearchResult {\n id: number;\n name: string;\n kind: SymbolKind;\n lang: SymbolLang;\n file: string;\n line: number;\n col: number;\n signature: string;\n docComment: string;\n score: number;\n snippet: string;\n /** Original LSP SymbolKind number if the result was filtered by an LSP kind. */\n lspKind?: number | undefined;\n}\n\n/** Result of a full reindex. */\nexport interface IndexResult {\n filesIndexed: number;\n symbolsIndexed: number;\n langStats: Record<SymbolLang, number>;\n durationMs: number;\n errors: string[];\n /**\n * Present when `runStartupIndex` detected a corrupt/stale index (SQLite\n * constraint failure) and automatically recovered by wiping and rebuilding\n * with `force: true`. The original failure message is preserved here so\n * callers diagnosing intermittent crashes can distinguish a normal rebuild\n * from one triggered by corruption recovery.\n */\n autoRecovered?: { failure: string; rebuiltWithForce: true } | undefined;\n}\n\n// \u2500\u2500\u2500 Cross-reference types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** What kind of reference this is. */\nexport type CallType = 'call' | 'type_ref' | 'inherit' | 'implement' | 'import';\n\n/** A cross-reference between two symbols (who references whom). */\nexport interface Ref {\n id?: number | undefined;\n fromId: number; // symbol that makes the reference\n toName: string; // resolved name of the referenced symbol\n toId?: number | undefined; // resolved target symbol id (filled after index resolution)\n callType: CallType; // kind of reference\n line: number; // source line where the reference occurs\n}\n\n// \u2500\u2500\u2500 CodeMap graph types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A node in the code-map dependency graph. */\nexport interface GraphNode {\n id: string;\n label: string;\n kind: 'package' | 'file' | 'symbol';\n /** Package name (packages/) or undefined. */\n package?: string | undefined;\n /** File path (relative to project root) or undefined for package-level. */\n file?: string | undefined;\n /** Symbol id when kind === 'symbol'. */\n symbolId?: number | undefined;\n /** Symbol kind when kind === 'symbol'. */\n symbolKind?: SymbolKind | undefined;\n /** Number of symbols contained (for package/file nodes). */\n symbolCount?: number | undefined;\n /** Number of files contained (for package nodes). */\n fileCount?: number | undefined;\n /** Source language when the node represents a file or symbol. */\n lang?: SymbolLang | undefined;\n /** Declaration line when the node represents a symbol. */\n line?: number | undefined;\n /** Indexed declaration signature when the node represents a symbol. */\n signature?: string | undefined;\n /** Indexed declaration scope when the node represents a symbol. */\n scope?: string | undefined;\n /** True when this is a direct relation outside the current drill-down scope. */\n external?: boolean | undefined;\n}\n\n/** A directed edge: source references / depends-on target. */\nexport interface GraphEdge {\n source: string;\n target: string;\n /** Number of refs contributing to this edge (weight). */\n weight: number;\n /** Dominant ref type: 'call', 'import', 'type_ref', etc. */\n refType: CallType;\n}\n\n/** Complete graph response. */\nexport interface CodeMapGraph {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\n// \u2500\u2500\u2500 Schema version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// v2: added the symbols_fts FTS5 table (ranked search moved into SQLite).\n// v3: parser/search format update (navigable TS declarations, valid ref owners,\n// acronym/digit token splitting). Derived data must be rebuilt.\n// A version mismatch on open drops & rebuilds the index (it is derived data).\n// Non-structural CodeMap relation migrations use `relation_graph_version`\n// metadata so older running processes sharing the DB cannot downgrade it.\nexport const SCHEMA_VERSION = 3;\n", "import { createRequire } from 'node:module';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { toErrorMessage } from '@wrongstack/core/utils';\nimport { LockError } from './circuit-breaker.js';\n\nlet warningSilenced = false;\n\n/**\n * Swallow the one-time `ExperimentalWarning: SQLite ...` Node prints the first\n * time `node:sqlite` loads. Patched only once, and only filters that specific\n * warning \u2014 every other warning passes through untouched.\n */\nfunction silenceSqliteExperimentalWarning(): void {\n if (warningSilenced) return;\n warningSilenced = true;\n const original = process.emitWarning.bind(process);\n process.emitWarning = ((warning: unknown, ...rest: unknown[]): void => {\n const msg = typeof warning === 'string' ? warning : ((warning as Error)?.message ?? '');\n const name =\n typeof warning === 'string' ? String(rest[0] ?? '') : ((warning as Error)?.name ?? '');\n if (/sqlite/i.test(msg) && /experimental/i.test(`${name} ${msg}`)) return;\n (original as (w: unknown, ...r: unknown[]) => void)(warning, ...rest);\n }) as typeof process.emitWarning;\n}\n\nlet DatabaseSyncCtor: typeof DatabaseSync | undefined;\n\n/**\n * Load `node:sqlite`'s `DatabaseSync` lazily. Keeping this off writer.ts top\n * level lets codebase-index tools register at CLI boot without eagerly loading\n * SQLite. Runtimes without `node:sqlite` fail only when the index is used.\n */\nexport function loadDatabaseSync(): typeof DatabaseSync {\n if (DatabaseSyncCtor) return DatabaseSyncCtor;\n silenceSqliteExperimentalWarning();\n try {\n const req = createRequire(import.meta.url);\n DatabaseSyncCtor = (req('node:sqlite') as typeof import('node:sqlite')).DatabaseSync;\n } catch (err) {\n throw new Error(\n \"The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. \" +\n `This runtime doesn't provide it: ${toErrorMessage(err)}`,\n );\n }\n return DatabaseSyncCtor;\n}\n\n/** Maximum retry attempts for a lock-conflict error. */\nconst MAX_LOCK_RETRIES = 3;\n/** Base delay (ms) before the first retry after a lock error. */\nconst LOCK_RETRY_BASE_DELAY_MS = 50;\n/** Cap on the per-retry delay so we never sleep for more than this. */\nconst LOCK_RETRY_MAX_DELAY_MS = 500;\n\nfunction isLockError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const e = err as { code?: unknown; sqliteCode?: unknown };\n const code = e.code ?? e.sqliteCode;\n if (typeof code === 'string' && /SQLITE_(BUSY|LOCKED)/.test(code)) return true;\n if (typeof code === 'number' && (code === 5 || code === 6)) return true;\n return /SQLITE_(BUSY|LOCKED)/.test(err.message);\n}\n\nfunction sleepSync(ms: number): void {\n try {\n const sab = new SharedArrayBuffer(4);\n const view = new Int32Array(sab);\n Atomics.wait(view, 0, 0, ms);\n } catch {\n // busy_timeout already handled the bulk wait; retry immediately if\n // Atomics.wait is unavailable in this runtime.\n }\n}\n\nexport function runSqliteWithRetry<T>(fn: () => T): T {\n let lastError: unknown;\n for (let attempt = 0; attempt <= MAX_LOCK_RETRIES; attempt++) {\n try {\n return fn();\n } catch (err) {\n lastError = err;\n if (!isLockError(err)) throw err;\n if (attempt === MAX_LOCK_RETRIES) {\n const msg = lastError instanceof Error ? lastError.message : String(lastError);\n throw new LockError(`SQLite lock conflict after ${MAX_LOCK_RETRIES} retries: ${msg}`);\n }\n const delay = Math.min(LOCK_RETRY_BASE_DELAY_MS * 2 ** attempt, LOCK_RETRY_MAX_DELAY_MS);\n sleepSync(delay);\n }\n }\n throw lastError;\n}\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { FileMeta, IndexStats, SymbolKind, SymbolLang } from './schema.js';\nimport { SCHEMA_VERSION } from './schema.js';\n\nconst DB_FILE = 'index.db';\n\ntype Statement = ReturnType<DatabaseSync['prepare']>;\ntype PrepareStatement = (sql: string) => Statement;\n\nexport function getAllIndexableWithStatement(\n stmt: PrepareStatement,\n): Array<{ id: number; text: string }> {\n return (\n stmt('SELECT id, text FROM symbols').all() as { id: number; text: string }[]\n ).map(({ id, text }) => ({ id, text }));\n}\n\nexport function getMaxSymbolIdWithStatement(stmt: PrepareStatement): number {\n const rows = stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n return rows[0]?.m ?? 0;\n}\n\nexport function getStatsWithStatement(stmt: PrepareStatement, indexDir: string): IndexStats {\n const lastRows = stmt(\"SELECT value FROM metadata WHERE key = 'last_indexed'\").all() as {\n value: string;\n }[];\n const totalRows = stmt('SELECT COUNT(*) FROM symbols').all() as { 'COUNT(*)': number }[];\n const fileRows = stmt('SELECT COUNT(*) FROM files').all() as { 'COUNT(*)': number }[];\n const langRows = stmt('SELECT lang, COUNT(*) FROM symbols GROUP BY lang').all() as Array<{\n lang: string;\n 'COUNT(*)': number;\n }>;\n const kindRows = stmt('SELECT kind, COUNT(*) FROM symbols GROUP BY kind').all() as Array<{\n kind: string;\n 'COUNT(*)': number;\n }>;\n\n const byLang = {} as Record<SymbolLang, number>;\n for (const row of langRows) byLang[row.lang as SymbolLang] = Number(row['COUNT(*)']);\n\n const byKind = {} as Record<SymbolKind, number>;\n for (const row of kindRows) byKind[row.kind as SymbolKind] = Number(row['COUNT(*)']);\n\n return {\n totalSymbols: totalRows[0] ? Number(totalRows[0]['COUNT(*)']) : 0,\n totalFiles: fileRows[0] ? Number(fileRows[0]['COUNT(*)']) : 0,\n byLang,\n byKind,\n indexPath: indexDir,\n lastIndexed: lastRows.length ? Number(lastRows[0]?.value) : null,\n sizeBytes: getIndexDbSizeBytes(indexDir),\n version: SCHEMA_VERSION,\n };\n}\n\nexport function getMetadataWithStatement(\n stmt: PrepareStatement,\n key: string,\n): string | undefined {\n const rows = stmt('SELECT value FROM metadata WHERE key = ?').all(key) as {\n value: string;\n }[];\n return rows[0]?.value;\n}\n\nexport function getFileMetaWithStatement(\n stmt: PrepareStatement,\n file: string,\n): FileMeta | null {\n const rows = stmt(\n 'SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?',\n ).all(file) as Array<{\n file: string;\n lang: string;\n mtime_ms: number;\n symbol_count: number;\n last_indexed: number;\n }>;\n const r = rows[0];\n if (!r) return null;\n return {\n file: r.file,\n lang: r.lang as SymbolLang,\n mtimeMs: r.mtime_ms,\n symbolCount: r.symbol_count,\n lastIndexed: r.last_indexed,\n };\n}\n\nexport function getAllFileMetasWithStatement(stmt: PrepareStatement): FileMeta[] {\n return (\n stmt('SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files').all() as Array<{\n file: string;\n lang: string;\n mtime_ms: number;\n symbol_count: number;\n last_indexed: number;\n }>\n ).map((r) => ({\n file: r.file,\n lang: r.lang as SymbolLang,\n mtimeMs: r.mtime_ms,\n symbolCount: r.symbol_count,\n lastIndexed: r.last_indexed,\n }));\n}\n\nexport function getIndexDbSizeBytes(indexDir: string): number {\n try {\n return fs.statSync(path.join(indexDir, DB_FILE)).size;\n } catch {\n return 0;\n }\n}\n", "import type { DatabaseSync } from 'node:sqlite';\nimport type { Ref } from './schema.js';\n\ntype Statement = ReturnType<DatabaseSync['prepare']>;\ntype PrepareStatement = (sql: string) => Statement;\n\nexport interface BulkSymbolRow {\n id: number;\n lang: string;\n kind: string;\n name: string;\n file: string;\n line: number;\n col: number;\n signature: string;\n docComment: string;\n scope: string;\n text: string;\n}\n\nexport function bulkInsertSymbolsWithStatement(\n stmt: PrepareStatement,\n maxSqlVars: number,\n rows: BulkSymbolRow[],\n): void {\n if (rows.length === 0) return;\n const chunkSize = Math.max(1, Math.floor(maxSqlVars / 12));\n for (let i = 0; i < rows.length; i += chunkSize) {\n const chunk = rows.slice(i, i + chunkSize);\n const placeholders = chunk.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ');\n const insert = stmt(\n `INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)\n VALUES ${placeholders}`,\n );\n const binds: (string | number)[] = [];\n for (const r of chunk) {\n binds.push(\n r.id,\n r.lang,\n r.kind,\n r.name,\n r.file,\n r.line,\n r.col,\n r.signature,\n r.docComment,\n r.scope,\n r.text,\n r.file,\n );\n }\n insert.run(...binds);\n }\n}\n\nexport function bulkInsertFtsWithStatement(\n stmt: PrepareStatement,\n maxSqlVars: number,\n ftsAvailable: boolean,\n rows: Array<{ id: number; text: string }>,\n): void {\n if (!ftsAvailable || rows.length === 0) return;\n const chunkSize = Math.max(1, Math.floor(maxSqlVars / 2));\n for (let i = 0; i < rows.length; i += chunkSize) {\n const chunk = rows.slice(i, i + chunkSize);\n const placeholders = chunk.map(() => '(?, ?)').join(', ');\n const insert = stmt(`INSERT INTO symbols_fts(rowid, text) VALUES ${placeholders}`);\n const binds: (string | number)[] = [];\n for (const r of chunk) binds.push(r.id, r.text);\n insert.run(...binds);\n }\n}\n\nexport function bulkInsertRefsWithStatement(\n stmt: PrepareStatement,\n maxSqlVars: number,\n refs: Ref[],\n): void {\n if (refs.length === 0) return;\n const chunkSize = Math.max(1, Math.floor(maxSqlVars / 5));\n for (let i = 0; i < refs.length; i += chunkSize) {\n const chunk = refs.slice(i, i + chunkSize);\n const placeholders = chunk.map(() => '(?, ?, ?, ?, ?)').join(', ');\n const insert = stmt(\n `INSERT INTO refs(from_id, to_name, to_id, call_type, line) VALUES ${placeholders}`,\n );\n const binds: (string | number | null)[] = [];\n for (const ref of chunk) {\n binds.push(ref.fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);\n }\n insert.run(...binds);\n }\n}\n", "import * as path from 'node:path';\nimport type { CallType, GraphEdge, GraphNode, SymbolKind, SymbolLang } from './schema.js';\n\n/**\n * Derive a monorepo package name from an absolute file path.\n * Handles both `packages/<name>/...` and `apps/<name>/...` layouts.\n */\nexport function derivePackage(filePath: string): string | undefined {\n const f = filePath.replace(/\\\\/g, '/');\n const pkgsIdx = f.indexOf('/packages/');\n if (pkgsIdx !== -1) {\n const rest = f.slice(pkgsIdx + '/packages/'.length);\n const seg = rest.split('/')[0];\n return seg ? `@wrongstack/${seg}` : undefined;\n }\n const appsIdx = f.indexOf('/apps/');\n if (appsIdx !== -1) {\n const rest = f.slice(appsIdx + '/apps/'.length);\n const seg = rest.split('/')[0];\n return seg ? `app:${seg}` : undefined;\n }\n return undefined;\n}\n\nexport function packageFromImport(moduleName: string): string | undefined {\n if (!moduleName.startsWith('@wrongstack/')) return undefined;\n const parts = moduleName.split('/');\n return parts[1] ? `@wrongstack/${parts[1]}` : undefined;\n}\n\nexport function buildPackageGraphNodes(\n fileCounts: Array<{ file: string; n: number }>,\n files: Array<{ file: string }>,\n): { pkgNodes: Map<string, GraphNode>; fileToPkg: Map<string, string> } {\n const pkgNodes = new Map<string, GraphNode>();\n const fileToPkg = new Map<string, string>();\n\n for (const { file, n } of fileCounts) {\n const pkg = derivePackage(file) ?? '(root)';\n fileToPkg.set(file, pkg);\n const node = pkgNodes.get(pkg);\n if (node) {\n node.symbolCount = (node.symbolCount ?? 0) + n;\n } else {\n pkgNodes.set(pkg, {\n id: `pkg:${pkg}`,\n label: pkg,\n kind: 'package',\n package: pkg,\n symbolCount: n,\n fileCount: 0,\n });\n }\n }\n\n for (const { file } of files) {\n const pkg = derivePackage(file) ?? '(root)';\n fileToPkg.set(file, pkg);\n const node = pkgNodes.get(pkg);\n if (node) {\n node.fileCount = (node.fileCount ?? 0) + 1;\n } else {\n pkgNodes.set(pkg, {\n id: `pkg:${pkg}`,\n label: pkg,\n kind: 'package',\n package: pkg,\n symbolCount: 0,\n fileCount: 1,\n });\n }\n }\n\n return { pkgNodes, fileToPkg };\n}\n\nexport type WriterFileGraphSymbolRow = {\n file: string;\n id: number;\n name: string;\n kind: string;\n lang: string;\n line: number;\n};\n\nexport function buildFileGraphNodeState(\n pkgSyms: WriterFileGraphSymbolRow[],\n localFiles: Set<string>,\n): {\n fileNodes: Map<string, GraphNode>;\n symToFile: Map<number, string>;\n fileStats: Map<string, { count: number; lang: SymbolLang }>;\n ensureFileNode: (file: string) => void;\n} {\n const fileNodes = new Map<string, GraphNode>();\n const symToFile = new Map<number, string>();\n const fileStats = new Map<string, { count: number; lang: SymbolLang }>();\n for (const s of pkgSyms) {\n symToFile.set(s.id, s.file);\n const current = fileStats.get(s.file);\n fileStats.set(s.file, {\n count: (current?.count ?? 0) + 1,\n lang: (current?.lang ?? s.lang) as SymbolLang,\n });\n }\n\n const ensureFileNode = (file: string): void => {\n if (fileNodes.has(file)) return;\n const stats = fileStats.get(file);\n fileNodes.set(file, {\n id: `file:${file}`,\n label: file.replace(/\\\\/g, '/').split('/').pop() ?? file,\n kind: 'file',\n package: derivePackage(file) ?? '(root)',\n file,\n symbolCount: stats?.count ?? 0,\n lang: stats?.lang,\n external: !localFiles.has(file),\n });\n };\n for (const file of localFiles) {\n ensureFileNode(file);\n }\n return { fileNodes, symToFile, fileStats, ensureFileNode };\n}\n\nexport type WriterSymbolGraphRow = {\n id: number;\n name: string;\n kind: string;\n lang: string;\n file: string;\n line: number;\n signature: string;\n scope: string;\n};\n\nexport function buildSymbolGraphNodes(\n symById: Map<number, WriterSymbolGraphRow>,\n relatedIds: Set<number>,\n fileFilter: string,\n): GraphNode[] {\n return [...relatedIds]\n .map((id) => symById.get(id))\n .filter((symbol): symbol is WriterSymbolGraphRow => symbol !== undefined)\n .sort((a, b) => {\n const aExternal = a.file === fileFilter ? 0 : 1;\n const bExternal = b.file === fileFilter ? 0 : 1;\n return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;\n })\n .map((s) => ({\n id: `sym:${s.id}`,\n label: s.name,\n kind: 'symbol',\n symbolId: s.id,\n symbolKind: s.kind as SymbolKind,\n file: s.file,\n package: derivePackage(s.file) ?? '(root)',\n lang: s.lang as SymbolLang,\n line: s.line,\n signature: s.signature,\n scope: s.scope,\n external: s.file !== fileFilter,\n }));\n}\n\nexport function resolveRelativeImport(\n fromFile: string,\n moduleName: string,\n indexedFiles: Set<string>,\n): string | undefined {\n if (!moduleName.startsWith('.')) return undefined;\n const normalizedFrom = fromFile.replace(/\\\\/g, '/');\n const absolute = path.posix.normalize(\n path.posix.join(path.posix.dirname(normalizedFrom), moduleName),\n );\n const extension = path.posix.extname(absolute);\n const base = extension ? absolute.slice(0, -extension.length) : absolute;\n const candidates = [\n absolute,\n ...['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts'].map((ext) => `${base}${ext}`),\n ...['.ts', '.tsx', '.js', '.jsx'].map((ext) => path.posix.join(absolute, `index${ext}`)),\n ...['.ts', '.tsx', '.js', '.jsx'].map((ext) => path.posix.join(base, `index${ext}`)),\n ];\n const indexedByPortablePath = new Map(\n [...indexedFiles].map((file) => [file.replace(/\\\\/g, '/').toLocaleLowerCase(), file]),\n );\n for (const candidate of candidates) {\n const indexed = indexedByPortablePath.get(candidate.toLocaleLowerCase());\n if (indexed) return indexed;\n }\n return undefined;\n}\n\nexport type WeightedEdgeAccumulator = {\n weight: number;\n types: Map<string, number>;\n};\n\nexport function addWeightedEdge(\n edgeMap: Map<string, WeightedEdgeAccumulator>,\n source: string | number,\n target: string | number,\n callType: string,\n weight: number,\n): void {\n const key = `${source}\\u0000${target}`;\n let edge = edgeMap.get(key);\n if (!edge) {\n edge = { weight: 0, types: new Map() };\n edgeMap.set(key, edge);\n }\n edge.weight += weight;\n edge.types.set(callType, (edge.types.get(callType) ?? 0) + weight);\n}\n\nexport function materializeWeightedEdges(\n edgeMap: Map<string, WeightedEdgeAccumulator>,\n idPrefix: 'pkg' | 'file' | 'sym',\n): GraphEdge[] {\n const edges: GraphEdge[] = [];\n for (const [key, edge] of edgeMap) {\n const [source, target] = key.split('\\u0000');\n let bestType = 'call';\n let bestCount = 0;\n for (const [type, count] of edge.types) {\n if (count > bestCount) {\n bestType = type;\n bestCount = count;\n }\n }\n edges.push({\n source: `${idPrefix}:${source}`,\n target: `${idPrefix}:${target}`,\n weight: edge.weight,\n refType: bestType as CallType,\n });\n }\n return edges;\n}\n", "import type { Ref } from './schema.js';\n\nexport type WriterRefRow = {\n id: number;\n from_id: number;\n to_name: string;\n to_id: number | null;\n call_type: string;\n line: number;\n};\n\nexport function mapWriterRefRow(row: WriterRefRow): Ref {\n return {\n id: row.id,\n fromId: row.from_id,\n toName: row.to_name,\n toId: row.to_id ?? undefined,\n callType: row.call_type as Ref['callType'],\n line: row.line,\n };\n}\n", "import type { DatabaseSync } from 'node:sqlite';\nimport type { CodeMapGraph, Ref, SymbolLang } from './schema.js';\nimport {\n addWeightedEdge,\n buildFileGraphNodeState,\n buildPackageGraphNodes,\n buildSymbolGraphNodes,\n derivePackage,\n materializeWeightedEdges,\n packageFromImport,\n resolveRelativeImport,\n type WeightedEdgeAccumulator,\n type WriterFileGraphSymbolRow,\n type WriterSymbolGraphRow,\n} from './writer-graph-helpers.js';\nimport { mapWriterRefRow, type WriterRefRow } from './writer-ref-mapper.js';\n\ntype Statement = ReturnType<DatabaseSync['prepare']>;\ntype PrepareStatement = (sql: string) => Statement;\n\nexport function findRefsToWithStatement(stmt: PrepareStatement, symbolId: number): Ref[] {\n return (\n stmt(\n 'SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE to_id = ? OR to_name = (SELECT name FROM symbols WHERE id = ?)',\n ).all(symbolId, symbolId) as WriterRefRow[]\n ).map(mapWriterRefRow);\n}\n\nexport function findRefsFromWithStatement(stmt: PrepareStatement, symbolId: number): Ref[] {\n return (\n stmt('SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE from_id = ?').all(\n symbolId,\n ) as WriterRefRow[]\n ).map(mapWriterRefRow);\n}\n\nexport function getPackageGraphWithStatement(stmt: PrepareStatement): CodeMapGraph {\n const fileCounts = stmt('SELECT file, COUNT(*) AS n FROM symbols GROUP BY file').all() as Array<{\n file: string;\n n: number;\n }>;\n\n const files = stmt('SELECT DISTINCT file FROM files').all() as { file: string }[];\n const { pkgNodes, fileToPkg } = buildPackageGraphNodes(fileCounts, files);\n\n const refRows = stmt(\n `SELECT r.call_type, sf.file AS from_file, st.file AS to_file, COUNT(*) AS n\n FROM refs r\n JOIN symbols sf ON sf.id = r.from_id\n JOIN symbols st ON st.id = r.to_id\n WHERE r.to_id IS NOT NULL AND r.call_type != 'import'\n GROUP BY r.call_type, sf.file, st.file`,\n ).all() as Array<{ call_type: string; from_file: string; to_file: string; n: number }>;\n\n const edgeMap = new Map<string, WeightedEdgeAccumulator>();\n for (const r of refRows) {\n const fromPkg = fileToPkg.get(r.from_file) ?? derivePackage(r.from_file) ?? '(root)';\n const toPkg = fileToPkg.get(r.to_file) ?? derivePackage(r.to_file) ?? '(root)';\n if (fromPkg === toPkg) continue;\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromPkg, toPkg, r.call_type, n);\n }\n\n const importRows = stmt(\n `SELECT r.to_name, s.file AS from_file, COUNT(*) AS n\n FROM refs r\n JOIN symbols s ON s.id = r.from_id\n WHERE r.call_type = 'import'\n GROUP BY r.to_name, s.file`,\n ).all() as Array<{ to_name: string; from_file: string; n: number }>;\n for (const r of importRows) {\n const fromPkg = fileToPkg.get(r.from_file) ?? derivePackage(r.from_file) ?? '(root)';\n const toPkg = packageFromImport(r.to_name);\n if (!fromPkg || !toPkg || fromPkg === toPkg || !pkgNodes.has(toPkg)) continue;\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromPkg, toPkg, 'import', n);\n }\n\n const edges = materializeWeightedEdges(edgeMap, 'pkg');\n return { nodes: [...pkgNodes.values()], edges };\n}\n\nexport function getFileGraphWithStatement(\n stmt: PrepareStatement,\n packageFilter: string,\n): CodeMapGraph {\n const allFiles = stmt('SELECT DISTINCT file FROM symbols').all() as { file: string }[];\n const pkgFilePaths = allFiles\n .filter((f) => (derivePackage(f.file) ?? '(root)') === packageFilter)\n .map((f) => f.file);\n const localFiles = new Set(pkgFilePaths);\n if (localFiles.size === 0) return { nodes: [], edges: [] };\n\n const filePlaceholders = [...localFiles].map(() => '?').join(',');\n const pkgSyms = stmt(\n `SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`,\n ).all(...pkgFilePaths) as WriterFileGraphSymbolRow[];\n const { fileNodes, symToFile, fileStats, ensureFileNode } = buildFileGraphNodeState(\n pkgSyms,\n localFiles,\n );\n\n const indexedFiles = new Set(allFiles.map((f) => f.file));\n\n const refRows = stmt(\n `SELECT r.from_id, r.to_id, r.call_type, COUNT(*) AS n\n FROM refs r\n WHERE (r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))\n OR r.to_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders})))\n AND r.to_id IS NOT NULL\n GROUP BY r.from_id, r.to_id, r.call_type`,\n ).all(...pkgFilePaths, ...pkgFilePaths) as {\n from_id: number;\n to_id: number;\n call_type: string;\n n: number;\n }[];\n\n const knownSymIds = new Set(pkgSyms.map((s) => s.id));\n const crossRefIds = new Set<number>();\n for (const r of refRows) {\n if (!knownSymIds.has(r.from_id)) crossRefIds.add(r.from_id);\n if (!knownSymIds.has(r.to_id)) crossRefIds.add(r.to_id);\n }\n if (crossRefIds.size > 0) {\n const crossPlaceholders = [...crossRefIds].map(() => '?').join(',');\n const extras = stmt(`SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`).all(\n ...crossRefIds,\n ) as { id: number; file: string }[];\n for (const x of extras) {\n symToFile.set(x.id, x.file);\n if (!fileStats.has(x.file)) {\n fileStats.set(x.file, { count: 0, lang: 'ts' as SymbolLang });\n }\n }\n }\n\n const edgeMap = new Map<string, WeightedEdgeAccumulator>();\n for (const r of refRows) {\n if (r.call_type === 'import') continue;\n const fromFile = symToFile.get(r.from_id);\n const toFile = symToFile.get(r.to_id);\n if (!fromFile || !toFile || fromFile === toFile) continue;\n if (!localFiles.has(fromFile) && !localFiles.has(toFile)) continue;\n ensureFileNode(fromFile);\n ensureFileNode(toFile);\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromFile, toFile, r.call_type, n);\n }\n\n const importRows = stmt(\n `SELECT r.from_id, r.to_name, COUNT(*) AS n\n FROM refs r\n WHERE r.call_type = 'import'\n AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))\n GROUP BY r.from_id, r.to_name`,\n ).all(...pkgFilePaths) as { from_id: number; to_name: string; n: number }[];\n for (const r of importRows) {\n const fromFile = symToFile.get(r.from_id);\n if (!fromFile || !localFiles.has(fromFile)) continue;\n const toFile = resolveRelativeImport(fromFile, r.to_name, indexedFiles);\n if (!toFile || fromFile === toFile) continue;\n ensureFileNode(fromFile);\n ensureFileNode(toFile);\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromFile, toFile, 'import', n);\n }\n\n const edges = materializeWeightedEdges(edgeMap, 'file');\n return { nodes: [...fileNodes.values()], edges };\n}\n\nexport function getSymbolGraphWithStatement(\n stmt: PrepareStatement,\n fileFilter: string,\n): CodeMapGraph {\n const syms = stmt(\n 'SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id',\n ).all(fileFilter) as WriterSymbolGraphRow[];\n\n if (syms.length === 0) return { nodes: [], edges: [] };\n\n const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));\n const relatedIds = new Set(syms.map((symbol) => symbol.id));\n\n const refRows = stmt(\n `SELECT from_id, to_id, call_type, COUNT(*) AS n\n FROM (\n SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line\n FROM refs r\n JOIN symbols s ON s.id = r.from_id\n WHERE s.file = ?\n UNION\n SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line\n FROM refs r\n JOIN symbols s ON s.id = r.to_id\n WHERE s.file = ?\n )\n WHERE to_id IS NOT NULL\n GROUP BY from_id, to_id, call_type`,\n ).all(fileFilter, fileFilter) as {\n from_id: number;\n to_id: number;\n call_type: string;\n n: number;\n }[];\n\n const edgeMap = new Map<string, WeightedEdgeAccumulator>();\n for (const r of refRows) {\n if (r.to_id == null) continue;\n relatedIds.add(r.from_id);\n relatedIds.add(r.to_id);\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, r.from_id, r.to_id, r.call_type, n);\n }\n const edges = materializeWeightedEdges(edgeMap, 'sym');\n\n const loadedIds = new Set(syms.map((s) => s.id));\n const missingIds = [...relatedIds].filter((id) => !loadedIds.has(id));\n if (missingIds.length > 0) {\n const placeholders = missingIds.map(() => '?').join(',');\n const extras = stmt(\n `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`,\n ).all(...missingIds) as WriterSymbolGraphRow[];\n for (const s of extras) symById.set(s.id, s);\n }\n\n const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter);\n return { nodes, edges };\n}\n", "import { resolveWstackPaths } from '@wrongstack/core/utils';\nimport type { Ref, Symbol as IndexSymbol } from './schema.js';\n\nexport function escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nexport function assignRefsToSymbols(refs: Ref[], symbols: IndexSymbol[]): Ref[] {\n if (refs.length === 0 || symbols.length === 0) return [];\n const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);\n const seen = new Set<string>();\n const assigned: Ref[] = [];\n for (const ref of refs) {\n let owner: IndexSymbol | undefined;\n for (const symbol of ordered) {\n if (symbol.line > ref.line) break;\n owner = symbol;\n }\n if (!owner && ref.callType === 'import') owner = ordered[0];\n if (!owner || owner.id <= 0) continue;\n const key = `${owner.id}:${ref.toName}:${ref.callType}`;\n if (seen.has(key)) continue;\n seen.add(key);\n assigned.push({ ...ref, fromId: owner.id });\n }\n return assigned;\n}\n\n/**\n * Resolve the per-project index directory. By default it lives under the\n * global project dir (`~/.wrongstack/projects/<hash>/codebase-index`).\n */\nexport function resolveIndexDir(projectRoot: string, override?: string): string {\n return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;\n}\n\n/**\n * Optional index-directory override carried on the run context's `meta` bag.\n */\nexport function codebaseIndexDirOverride(ctx: {\n meta?: Record<string, unknown>;\n}): string | undefined {\n const v = ctx.meta?.['codebaseIndexDir'];\n return typeof v === 'string' ? v : undefined;\n}\n", "import type { DatabaseSync } from 'node:sqlite';\nimport { sqliteCachePragmas } from '@wrongstack/core/utils';\n\nexport function applyIndexStorePragmas(db: DatabaseSync): void {\n try {\n db.exec('PRAGMA journal_mode = WAL');\n db.exec('PRAGMA synchronous = NORMAL');\n db.exec('PRAGMA busy_timeout = 15000');\n db.exec('PRAGMA temp_store = MEMORY');\n const cache = sqliteCachePragmas();\n db.exec(`PRAGMA cache_size = -${cache.cacheSizeKiB}`);\n db.exec(`PRAGMA mmap_size = ${cache.mmapBytes}`);\n db.exec('PRAGMA foreign_keys = ON');\n db.exec('PRAGMA journal_size_limit = 67108864');\n db.exec('PRAGMA wal_autocheckpoint = 1000');\n } catch {\n /* pragmas are best-effort; old SQLite builds without WAL still work */\n }\n}\n", "export const METADATA_TABLE_SQL = `\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n`;\n\nexport const CORE_TABLES_SQL = `\n CREATE TABLE IF NOT EXISTS files (\n file TEXT PRIMARY KEY,\n lang TEXT NOT NULL,\n mtime_ms INTEGER NOT NULL,\n symbol_count INTEGER NOT NULL DEFAULT 0,\n last_indexed INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS symbols (\n id INTEGER PRIMARY KEY,\n lang TEXT NOT NULL,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file TEXT NOT NULL,\n line INTEGER NOT NULL,\n col INTEGER NOT NULL,\n signature TEXT NOT NULL DEFAULT '',\n doc_comment TEXT NOT NULL DEFAULT '',\n scope TEXT NOT NULL DEFAULT '',\n text TEXT NOT NULL DEFAULT '',\n file_fk TEXT NOT NULL\n );\n`;\n\nexport const SYMBOL_INDEX_SQL = [\n 'CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)',\n 'CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)',\n 'CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)',\n 'CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)',\n 'CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)',\n 'CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)',\n 'CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)',\n] as const;\n\nexport const REFS_TABLE_SQL = `\n CREATE TABLE IF NOT EXISTS refs (\n id INTEGER PRIMARY KEY,\n from_id INTEGER NOT NULL,\n to_name TEXT NOT NULL,\n to_id INTEGER,\n call_type TEXT NOT NULL,\n line INTEGER NOT NULL\n );\n`;\n\nexport const REFS_INDEX_SQL = [\n 'CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)',\n 'CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)',\n 'CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)',\n 'CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)',\n] as const;\n\nexport const SYMBOLS_FTS_SQL =\n \"CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')\";\n", "import type { SearchResult, SymbolKind, SymbolLang } from './schema.js';\nimport { lspKindToInternalKind } from './lsp-kind.js';\nimport { escapeLike } from './writer-helpers.js';\n\nexport interface WriterSearchFilter {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n}\n\nexport interface WriterSearchRow {\n id: number;\n lang: string;\n kind: string;\n name: string;\n file: string;\n line: number;\n col: number;\n signature: string;\n doc_comment: string;\n text?: string;\n score?: number;\n snippet?: string;\n}\n\nexport function normalizeSearchLimit(limit: number | undefined): number | undefined {\n return typeof limit === 'number' && Number.isFinite(limit)\n ? Math.max(0, Math.trunc(limit))\n : undefined;\n}\n\nexport function buildWriterSearchWhere(\n query: string,\n filter?: WriterSearchFilter | undefined,\n): { where: string; values: unknown[] } | null {\n const conditions: string[] = [];\n const values: unknown[] = [];\n\n let effectiveKind: SymbolKind | undefined = filter?.kind;\n if (filter?.lspKind !== undefined) {\n const mapped = lspKindToInternalKind(filter.lspKind);\n if (mapped !== null) {\n effectiveKind = mapped;\n } else {\n return null;\n }\n }\n\n if (effectiveKind) {\n conditions.push('kind = ?');\n values.push(effectiveKind);\n }\n if (filter?.lang) {\n conditions.push('lang = ?');\n values.push(filter.lang);\n }\n if (filter?.file) {\n conditions.push(\"replace(file, '\\\\', '/') LIKE ? ESCAPE '\\\\'\");\n values.push(`%${escapeLike(filter.file.replace(/\\\\/g, '/'))}%`);\n }\n if (query.trim()) {\n const tokens = query.toLowerCase().split(/\\s+/).filter(Boolean);\n conditions.push(`(${tokens.map(() => 'text LIKE ?').join(' OR ')})`);\n for (const token of tokens) values.push(`%${token}%`);\n }\n\n return { where: conditions.length ? `WHERE ${conditions.join(' AND ')}` : '', values };\n}\n\nexport function mapWriterSearchRow(\n row: WriterSearchRow,\n lspKind: number | undefined,\n score = 0,\n snippet = '',\n): SearchResult {\n return {\n id: row.id,\n lang: row.lang as SymbolLang,\n kind: row.kind as SymbolKind,\n name: row.name,\n file: row.file,\n line: row.line,\n col: row.col,\n signature: row.signature,\n docComment: row.doc_comment,\n score,\n snippet,\n lspKind,\n };\n}\n", "interface PooledIndexStore {\n close(): void;\n}\n\ntype StoreFactory<TStore extends PooledIndexStore> = (\n projectRoot: string,\n opts?: { indexDir?: string | undefined },\n) => TStore;\n\n/**\n * How many warm connections to keep.\n *\n * Each pooled store carries SQLite's page cache and mmap reservation (128MiB\n * and 512MiB respectively on the `balanced` profile \u2014 see\n * `core/src/utils/perf-profile.ts`). The pool used to be unbounded with a\n * no-op `release()`, so every project a long-lived host touched became\n * permanent resident memory. Two keeps the common \"one project, occasionally a\n * second\" case warm; beyond that, reopening is cheap relative to the residency.\n */\nconst DEFAULT_MAX_WARM_STORES = 2;\n\ninterface PoolEntry<TStore> {\n store: TStore;\n /** Outstanding acquire() calls not yet released. Never evict while > 0. */\n refs: number;\n /** Monotonic counter for LRU ordering. */\n lastUsed: number;\n}\n\n/**\n * Warm-connection pool for IndexStore instances.\n *\n * Each (projectRoot, indexDir) pair gets one persisted store. Every read\n * operation (search, stats, graph) acquires the store from the pool instead of\n * opening a fresh SQLite connection, re-parsing the schema, and re-preparing\n * statements. The connection stays warm between calls, subject to\n * {@link DEFAULT_MAX_WARM_STORES}.\n */\nexport class StorePool<TStore extends PooledIndexStore> {\n private readonly stores = new Map<string, PoolEntry<TStore>>();\n private readonly keyByStore = new WeakMap<object, string>();\n private clock = 0;\n\n constructor(\n private readonly createStore: StoreFactory<TStore>,\n private readonly maxWarmStores: number = DEFAULT_MAX_WARM_STORES,\n ) {}\n\n private key(projectRoot: string, indexDir?: string): string {\n return `${projectRoot}\\u0000${indexDir ?? ''}`;\n }\n\n /** Borrow a store. Creates it on first access for this key. */\n acquire(projectRoot: string, opts?: { indexDir?: string | undefined }): TStore {\n const k = this.key(projectRoot, opts?.indexDir);\n let entry = this.stores.get(k);\n if (!entry) {\n const store = this.createStore(projectRoot, { indexDir: opts?.indexDir });\n entry = { store, refs: 0, lastUsed: 0 };\n this.stores.set(k, entry);\n this.keyByStore.set(store as object, k);\n }\n entry.refs++;\n entry.lastUsed = ++this.clock;\n return entry.store;\n }\n\n /**\n * Return the store to the pool. The connection stays warm for reuse, but the\n * pool may now close the least-recently-used *idle* connection to stay within\n * {@link maxWarmStores}. A store with outstanding refs is never closed.\n */\n release(store: TStore): void {\n const k = this.keyByStore.get(store as object);\n const entry = k === undefined ? undefined : this.stores.get(k);\n if (entry && entry.refs > 0) entry.refs--;\n this.trim();\n }\n\n private trim(): void {\n if (this.stores.size <= this.maxWarmStores) return;\n const idle = [...this.stores.entries()]\n .filter(([, entry]) => entry.refs === 0)\n .sort((a, b) => a[1].lastUsed - b[1].lastUsed);\n let overflow = this.stores.size - this.maxWarmStores;\n for (const [k, entry] of idle) {\n if (overflow <= 0) break;\n try {\n entry.store.close();\n } catch {\n /* already closed */\n }\n this.stores.delete(k);\n overflow--;\n }\n }\n\n /** Close every pooled connection and drain the pool. Call on shutdown. */\n closeAll(): void {\n for (const entry of this.stores.values()) {\n try {\n entry.store.close();\n } catch {\n /* already closed */\n }\n }\n this.stores.clear();\n }\n\n /** Remove one store from the pool. Used by tests that need isolation. */\n evict(projectRoot: string, indexDir?: string): void {\n const k = this.key(projectRoot, indexDir);\n const entry = this.stores.get(k);\n if (entry) {\n try {\n entry.store.close();\n } catch {\n /* already closed */\n }\n this.stores.delete(k);\n }\n }\n\n /** True when the pool holds a connection for the given key. */\n has(projectRoot: string, indexDir?: string): boolean {\n return this.stores.has(this.key(projectRoot, indexDir));\n }\n\n /** Number of warm connections currently held. */\n get size(): number {\n return this.stores.size;\n }\n}\n", "/**\n * Execution-location-agnostic index operations.\n *\n * One implementation, two callers: the index worker thread (production \u2014\n * synchronous SQLite and the TypeScript parser can never block the main\n * thread / terminal UI there) and the inline fallback inside the host (tests,\n * `WRONGSTACK_INDEX_INLINE=1`, or runtimes where the worker file is missing).\n *\n * Operations share one warm IndexStore per resolved project/index directory.\n * The detached server owns write serialization, while worker/inline fallbacks\n * run on one event loop, so a pooled connection is both safe and substantially\n * cheaper than re-running schema/FTS drift checks for every edited file.\n */\n\nimport { runIndexerWithStore } from './indexer.js';\nimport type { CodeMapGraph, IndexResult, IndexStats, SymbolKind, SymbolLang } from './schema.js';\nimport type { IndexOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs } from './worker-protocol.js';\nimport { indexStorePool } from './writer.js';\n\nexport interface ServiceHooks {\n signal?: AbortSignal | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\n/** Full or per-file index run. */\nexport async function indexService(\n args: IndexOpArgs,\n hooks: ServiceHooks = {},\n): Promise<IndexResult> {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return await runIndexerWithStore(store, {\n projectRoot: args.projectRoot,\n indexDir: args.indexDir,\n files: args.files,\n force: args.force,\n langs: args.langs,\n ignore: args.ignore,\n signal: hooks.signal,\n onProgress: hooks.onProgress,\n });\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** Ranked symbol search (FTS5 inside SQLite; BM25 fallback without FTS5). */\nexport function searchService(args: SearchOpArgs): SearchOpResult {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.searchRanked(\n args.query,\n {\n kind: args.kind as SymbolKind | undefined,\n lang: args.lang as SymbolLang | undefined,\n file: args.file,\n lspKind: args.lspKind,\n },\n args.limit,\n );\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** Index health and statistics. */\nexport function statsService(args: StatsOpArgs): IndexStats {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getStats();\n } finally {\n indexStorePool.release(store);\n }\n}\n\n// \u2500\u2500\u2500 CodeMap graph services \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Package-level dependency graph. */\nexport function packageGraphService(args: StatsOpArgs): CodeMapGraph {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getPackageGraph();\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** File-level dependency graph for a single package. */\nexport function fileGraphService(args: StatsOpArgs & { packageFilter: string }): CodeMapGraph {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getFileGraph(args.packageFilter);\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** Symbol-level dependency graph for a single file. */\nexport function symbolGraphService(args: StatsOpArgs & { fileFilter: string }): CodeMapGraph {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getSymbolGraph(args.fileFilter);\n } finally {\n indexStorePool.release(store);\n }\n}\n", "import { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as net from 'node:net';\nimport { fileURLToPath } from 'node:url';\nimport { IndexTimeoutError, LockError } from './circuit-breaker.js';\nimport {\n PROJECT_INDEX_SERVER_PROTOCOL_VERSION,\n projectIndexServerBuildId,\n projectIndexServerEndpoint,\n projectIndexServerMetadataPath,\n} from './project-server-endpoint.js';\nimport {\n encodeProjectServerMessage,\n PROJECT_INDEX_SERVER_MAX_FRAME_CHARS,\n type ProjectIndexServerActivity,\n type ProjectIndexServerHealth,\n type ProjectIndexServerInfo,\n type ProjectServerMessage,\n} from './project-server-protocol.js';\nimport type { OpName, OpShapes } from './worker-protocol.js';\n\nconst CONNECT_ATTEMPT_TIMEOUT_MS = 750;\nconst SERVER_START_TIMEOUT_MS = 10_000;\nconst SERVER_CONTROL_TIMEOUT_MS = 5_000;\nconst SERVER_HEALTH_TIMEOUT_MS = 3_000;\nconst SERVER_HEARTBEAT_INTERVAL_MS = 10_000;\n\nclass StaleProjectIndexServerError extends Error {\n override readonly name = 'StaleProjectIndexServerError';\n\n constructor(\n message: string,\n readonly pid: number,\n ) {\n super(message);\n }\n}\n\ninterface PendingRequest {\n resolve(value: unknown): void;\n reject(error: unknown): void;\n timer: ReturnType<typeof setTimeout>;\n signal?: AbortSignal | undefined;\n onAbort?: (() => void) | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nexport interface ProjectServerCallOptions {\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nexport interface ProjectIndexServerShutdownResult {\n stopped: boolean;\n pid?: number | undefined;\n reason?: string | undefined;\n}\n\nexport type ProjectIndexServerConnectionStatus =\n | 'unavailable'\n | 'offline'\n | 'connecting'\n | 'connected'\n | 'degraded'\n | 'unresponsive'\n | 'error'\n | 'stopping';\n\nexport interface ProjectIndexServerClientHealth {\n status: 'healthy' | 'degraded' | 'unresponsive';\n checkedAt: number;\n lastHealthyAt: number | null;\n latencyMs: number | null;\n missedHeartbeats: number;\n server?: ProjectIndexServerHealth | undefined;\n}\n\nexport interface ProjectIndexServerConnectionState {\n status: ProjectIndexServerConnectionStatus;\n connected: boolean;\n projectRoot?: string | undefined;\n indexDir?: string | undefined;\n endpoint?: string | undefined;\n pid?: number | undefined;\n lastError?: string | undefined;\n activity?: ProjectIndexServerActivity | undefined;\n health?: ProjectIndexServerClientHealth | undefined;\n}\n\ntype ProjectIndexServerConnectionListener = (state: ProjectIndexServerConnectionState) => void;\n\nconst connectionStates = new Map<string, ProjectIndexServerConnectionState>();\nconst connectionStateListeners = new Set<ProjectIndexServerConnectionListener>();\nlet latestConnectionState: ProjectIndexServerConnectionState = {\n status: 'offline',\n connected: false,\n};\n\n/**\n * Why the index daemon is or is not usable.\n *\n * A bare `null` cannot distinguish \"the operator asked for in-process mode\"\n * from \"the built server is missing\", so callers that degrade on `null` treat a\n * broken build as a supported mode. For the index that means every process\n * quietly builds its own FTS5 database instead of sharing the project's one\n * owner \u2014 same data, N copies, none of them authoritative.\n */\nexport type ProjectIndexDaemonAvailability =\n | { readonly kind: 'available'; readonly url: URL }\n /** `WRONGSTACK_INDEX_INLINE` / `WRONGSTACK_INDEX_SERVER=0` was set. */\n | { readonly kind: 'inline-requested' }\n /** No server entry point exists in any known output layout. */\n | { readonly kind: 'missing-build' };\n\nexport function resolveProjectIndexDaemonAvailability(): ProjectIndexDaemonAvailability {\n if (process.env['WRONGSTACK_INDEX_INLINE'] || process.env['WRONGSTACK_INDEX_SERVER'] === '0') {\n return { kind: 'inline-requested' };\n }\n for (const rel of ['./project-server.js', './codebase-index/project-server.js']) {\n try {\n const url = new URL(rel, import.meta.url);\n if (url.protocol === 'file:' && fs.existsSync(fileURLToPath(url))) {\n return { kind: 'available', url };\n }\n } catch {\n /* try the next candidate */\n }\n }\n return { kind: 'missing-build' };\n}\n\nfunction resolveProjectServerUrl(): URL | null {\n const availability = resolveProjectIndexDaemonAvailability();\n return availability.kind === 'available' ? availability.url : null;\n}\n\nexport function projectIndexServerExpectedBuildId(): string | null {\n const override = process.env['WRONGSTACK_INDEX_SERVER_BUILD_ID']?.trim();\n if (override) return override;\n const url = resolveProjectServerUrl();\n return url ? projectIndexServerBuildId(url) : null;\n}\n\nexport function isProjectIndexServerAvailable(): boolean {\n return resolveProjectServerUrl() !== null;\n}\n\nfunction publishConnectionState(endpoint: string, state: ProjectIndexServerConnectionState): void {\n connectionStates.set(endpoint, state);\n latestConnectionState = state;\n for (const listener of connectionStateListeners) listener(state);\n}\n\nexport function getProjectIndexServerConnectionState(\n projectRoot?: string,\n indexDir?: string,\n): ProjectIndexServerConnectionState {\n if (projectRoot) {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n const existing = connectionStates.get(endpoint);\n if (existing) return existing;\n if (!isProjectIndexServerAvailable()) {\n return { status: 'unavailable', connected: false };\n }\n return {\n status: 'offline',\n connected: false,\n projectRoot,\n indexDir,\n endpoint,\n };\n }\n if (latestConnectionState.endpoint) return latestConnectionState;\n if (!isProjectIndexServerAvailable()) return { status: 'unavailable', connected: false };\n return latestConnectionState;\n}\n\nexport function onProjectIndexServerConnectionStateChange(\n listener: ProjectIndexServerConnectionListener,\n): () => void {\n connectionStateListeners.add(listener);\n return () => connectionStateListeners.delete(listener);\n}\n\nfunction remoteError(message: string, name?: string): Error {\n if (name === 'LockError') return new LockError(message);\n if (name === 'IndexTimeoutError') return new IndexTimeoutError(message);\n const error = new Error(message);\n if (name && name !== 'Error') error.name = name;\n return error;\n}\n\nfunction isProjectIndexServerHealth(value: unknown): value is ProjectIndexServerHealth {\n if (!value || typeof value !== 'object') return false;\n const health = value as Partial<ProjectIndexServerHealth>;\n const memory =\n health.memory && typeof health.memory === 'object'\n ? (health.memory as Partial<ProjectIndexServerHealth['memory']>)\n : undefined;\n const activity =\n health.activity && typeof health.activity === 'object'\n ? (health.activity as Partial<ProjectIndexServerActivity>)\n : undefined;\n return (\n typeof health.checkedAt === 'number' &&\n typeof health.uptimeMs === 'number' &&\n typeof memory?.rss === 'number' &&\n typeof memory.heapUsed === 'number' &&\n typeof memory.heapTotal === 'number' &&\n typeof memory.external === 'number' &&\n typeof health.clients === 'number' &&\n typeof health.activeRequests === 'number' &&\n typeof health.activeWrites === 'number' &&\n typeof health.queuedWrites === 'number' &&\n typeof health.pendingExternalFiles === 'number' &&\n typeof health.watchingExternal === 'boolean' &&\n typeof activity?.indexing === 'boolean' &&\n typeof activity.currentFile === 'number' &&\n typeof activity.totalFiles === 'number' &&\n typeof activity.generation === 'number'\n );\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction cancellationError(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new Error('Indexing cancelled');\n}\n\nclass ProjectServerConnection {\n private socket: net.Socket | null = null;\n private buffer = '';\n private info: ProjectIndexServerInfo | null = null;\n private activity: ProjectIndexServerActivity | null = null;\n private health: ProjectIndexServerClientHealth | null = null;\n private healthCheck: Promise<ProjectIndexServerClientHealth> | null = null;\n private connecting: Promise<void> | null = null;\n private connectResolve: (() => void) | null = null;\n private connectReject: ((error: unknown) => void) | null = null;\n private nextId = 1;\n private readonly pending = new Map<number, PendingRequest>();\n\n constructor(\n readonly projectRoot: string,\n readonly indexDir: string | undefined,\n readonly endpoint: string,\n ) {\n this.transition('offline');\n }\n\n private transition(\n status: ProjectIndexServerConnectionStatus,\n options: { pid?: number | undefined; error?: unknown } = {},\n ): void {\n const previous = connectionStates.get(this.endpoint);\n const pid = options.pid ?? (status === 'connected' ? this.info?.pid : undefined);\n const lastError =\n options.error === undefined\n ? status === 'error' || status === 'degraded' || status === 'unresponsive'\n ? previous?.lastError\n : undefined\n : options.error instanceof Error\n ? options.error.message\n : String(options.error);\n publishConnectionState(this.endpoint, {\n status,\n connected: status === 'connected' || status === 'degraded' || status === 'unresponsive',\n projectRoot: this.projectRoot,\n indexDir: this.indexDir,\n endpoint: this.endpoint,\n pid,\n lastError,\n ...(this.activity ? { activity: this.activity } : {}),\n ...(this.health ? { health: this.health } : {}),\n });\n }\n\n isConnected(): boolean {\n return this.socket !== null && !this.socket.destroyed && this.info !== null;\n }\n\n async checkHealth(\n spawnIfMissing = false,\n timeoutMs = SERVER_HEALTH_TIMEOUT_MS,\n ): Promise<ProjectIndexServerClientHealth> {\n await this.ensureConnected(spawnIfMissing);\n if (this.healthCheck) return this.healthCheck;\n const startedAt = Date.now();\n this.healthCheck = this.request<ProjectIndexServerHealth>({ type: 'ping' }, { timeoutMs })\n .then((server) => {\n const now = Date.now();\n this.health = {\n status: 'healthy',\n checkedAt: now,\n lastHealthyAt: now,\n latencyMs: Math.max(0, now - startedAt),\n missedHeartbeats: 0,\n ...(isProjectIndexServerHealth(server) ? { server } : {}),\n };\n this.transition('connected', { pid: this.info?.pid });\n return this.health;\n })\n .catch((error) => {\n if (!this.isConnected()) throw error;\n if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health!;\n const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;\n const status = missedHeartbeats >= 3 ? 'unresponsive' : 'degraded';\n this.health = {\n status,\n checkedAt: Date.now(),\n lastHealthyAt: this.health?.lastHealthyAt ?? null,\n latencyMs: null,\n missedHeartbeats,\n ...(this.health?.server ? { server: this.health.server } : {}),\n };\n this.transition(status, { pid: this.info?.pid, error });\n return this.health;\n })\n .finally(() => {\n this.healthCheck = null;\n });\n return this.healthCheck;\n }\n\n private markResponsive(): void {\n const now = Date.now();\n this.health = {\n status: 'healthy',\n checkedAt: now,\n lastHealthyAt: now,\n latencyMs: this.health?.latencyMs ?? null,\n missedHeartbeats: 0,\n ...(this.health?.server ? { server: this.health.server } : {}),\n };\n }\n\n async call<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n options: ProjectServerCallOptions,\n ): Promise<OpShapes[O]['result']> {\n if (options.signal?.aborted) throw cancellationError(options.signal);\n await this.ensureConnected(true);\n // Connection establishment can take up to ten seconds while electing and\n // spawning a server. Do not enqueue work after the caller cancelled during\n // that interval.\n if (options.signal?.aborted) throw cancellationError(options.signal);\n return this.request<OpShapes[O]['result']>({ type: 'request', op, args }, options);\n }\n\n async shutdownRemote(reason?: string): Promise<ProjectIndexServerShutdownResult> {\n try {\n await this.ensureConnected(false);\n } catch {\n return { stopped: false, reason: 'not-running' };\n }\n const pid = this.info?.pid;\n try {\n this.transition('stopping', { pid });\n await this.request<{ stopping: boolean }>(\n { type: 'shutdown', reason },\n { timeoutMs: SERVER_CONTROL_TIMEOUT_MS },\n );\n return { stopped: true, pid };\n } catch (error) {\n const forceKilled = this.forceKillKnownServer();\n return {\n stopped: forceKilled,\n pid,\n reason: forceKilled\n ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}`\n : error instanceof Error\n ? error.message\n : String(error),\n };\n } finally {\n this.close();\n }\n }\n\n async configure(watchExternal: boolean, debounceMs: number): Promise<void> {\n await this.ensureConnected(true);\n const startedAt = Date.now();\n const result = await this.request<{ watching: boolean; health?: unknown }>(\n { type: 'configure', watchExternal, debounceMs },\n { timeoutMs: SERVER_CONTROL_TIMEOUT_MS },\n );\n if (isProjectIndexServerHealth(result.health)) {\n const now = Date.now();\n this.health = {\n status: 'healthy',\n checkedAt: now,\n lastHealthyAt: now,\n latencyMs: Math.max(0, now - startedAt),\n missedHeartbeats: 0,\n server: result.health,\n };\n this.transition('connected', { pid: this.info?.pid });\n }\n }\n\n close(): void {\n const socket = this.socket;\n this.socket = null;\n this.info = null;\n this.activity = null;\n this.health = null;\n this.connectReject?.(new Error('codebase-index client disconnected'));\n this.connectResolve = null;\n this.connectReject = null;\n if (socket && !socket.destroyed) socket.destroy();\n this.rejectPending(new Error('codebase-index client disconnected'));\n this.transition('offline');\n maybeStopHeartbeatLoop();\n }\n\n private request<T>(\n message:\n | { type: 'request'; op: OpName; args: OpShapes[OpName]['args'] }\n | {\n type: 'shutdown';\n reason?: string | undefined;\n }\n | { type: 'configure'; watchExternal: boolean; debounceMs: number }\n | { type: 'ping' },\n options: ProjectServerCallOptions,\n ): Promise<T> {\n const socket = this.socket;\n if (!socket || socket.destroyed) {\n return Promise.reject(new Error('codebase-index server connection is not available'));\n }\n const id = this.nextId++;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n const entry = this.pending.get(id);\n if (!entry) return;\n this.pending.delete(id);\n this.write({ type: 'cancel', id });\n const error = new IndexTimeoutError(\n `Index ${message.type === 'request' ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`,\n );\n this.cleanupPending(entry);\n entry.reject(error);\n }, options.timeoutMs);\n timer.unref?.();\n\n const signal = options.signal;\n const onAbort = signal\n ? () => {\n const entry = this.pending.get(id);\n if (!entry) return;\n this.pending.delete(id);\n this.write({ type: 'cancel', id });\n this.cleanupPending(entry);\n entry.reject(cancellationError(signal));\n }\n : undefined;\n this.pending.set(id, {\n resolve,\n reject,\n timer,\n signal,\n onAbort,\n onProgress: options.onProgress,\n });\n if (signal && onAbort) {\n signal.addEventListener('abort', onAbort, { once: true });\n // AbortSignal does not replay an abort event to listeners attached\n // after it fired. Close the narrow setup race before writing.\n if (signal.aborted) {\n onAbort();\n return;\n }\n }\n this.write({ ...message, id });\n });\n }\n\n private async ensureConnected(spawnIfMissing: boolean): Promise<void> {\n if (this.socket && !this.socket.destroyed && this.info) return;\n if (this.connecting) return this.connecting;\n this.transition('connecting');\n this.connecting = this.connectWithElection(spawnIfMissing)\n .catch((error) => {\n this.transition('error', { error });\n throw error;\n })\n .finally(() => {\n this.connecting = null;\n });\n return this.connecting;\n }\n\n private async connectWithElection(spawnIfMissing: boolean): Promise<void> {\n const deadline =\n Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);\n let spawned = false;\n let staleAttempts = 0;\n let lastError: unknown = new Error('codebase-index server unavailable');\n while (Date.now() < deadline) {\n try {\n await this.connectOnce();\n return;\n } catch (error) {\n lastError = error;\n if (error instanceof StaleProjectIndexServerError) {\n staleAttempts++;\n if (!spawnIfMissing) break;\n if (staleAttempts >= 3) this.forceKillServer(error.pid);\n spawned = false;\n await delay(100);\n continue;\n }\n }\n if (!spawnIfMissing) break;\n if (!spawned) {\n this.spawnDetachedServer();\n spawned = true;\n }\n await delay(75);\n }\n throw lastError;\n }\n\n private connectOnce(): Promise<void> {\n this.socket?.destroy();\n this.socket = null;\n this.info = null;\n this.activity = null;\n this.health = null;\n this.buffer = '';\n\n return new Promise<void>((resolve, reject) => {\n const socket = net.createConnection(this.endpoint);\n this.socket = socket;\n socket.setEncoding('utf8');\n const timer = setTimeout(() => {\n reject(new Error('codebase-index server handshake timed out'));\n socket.destroy();\n }, CONNECT_ATTEMPT_TIMEOUT_MS);\n timer.unref?.();\n\n const finishResolve = () => {\n clearTimeout(timer);\n this.connectResolve = null;\n this.connectReject = null;\n resolve();\n };\n const finishReject = (error: unknown) => {\n clearTimeout(timer);\n this.connectResolve = null;\n this.connectReject = null;\n reject(error);\n };\n this.connectResolve = finishResolve;\n this.connectReject = finishReject;\n\n socket.on('data', (chunk: string) => this.onData(socket, chunk));\n socket.on('error', (error) => {\n if (!this.info) finishReject(error);\n });\n socket.on('close', () => this.onClose(socket));\n });\n }\n\n private onData(socket: net.Socket, chunk: string): void {\n if (socket !== this.socket) return;\n this.buffer += chunk;\n while (true) {\n const newline = this.buffer.indexOf('\\n');\n if (newline < 0) {\n if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {\n socket.destroy(new Error('codebase-index server response exceeds the IPC limit'));\n }\n return;\n }\n if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {\n socket.destroy(new Error('codebase-index server response exceeds the IPC limit'));\n return;\n }\n const line = this.buffer.slice(0, newline);\n this.buffer = this.buffer.slice(newline + 1);\n if (!line) continue;\n let message: ProjectServerMessage;\n try {\n message = JSON.parse(line) as ProjectServerMessage;\n } catch {\n socket.destroy(new Error('invalid codebase-index server response'));\n return;\n }\n this.onMessage(message);\n }\n }\n\n private onMessage(message: ProjectServerMessage): void {\n if (message.type === 'hello') {\n if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {\n this.rejectStaleServer(\n message,\n `codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`,\n );\n return;\n }\n const expectedBuildId = projectIndexServerExpectedBuildId();\n if (expectedBuildId && message.buildId !== expectedBuildId) {\n this.rejectStaleServer(\n message,\n `codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? 'legacy'}`,\n );\n return;\n }\n this.info = message;\n this.markResponsive();\n this.transition('connected', { pid: message.pid });\n ensureHeartbeatLoop();\n this.connectResolve?.();\n return;\n }\n if (message.type === 'index-state') {\n this.activity = message.state;\n this.markResponsive();\n this.transition('connected', { pid: this.info?.pid });\n return;\n }\n\n const entry = this.pending.get(message.id);\n if (!entry) return;\n this.markResponsive();\n const status = connectionStates.get(this.endpoint)?.status;\n if (status === 'degraded' || status === 'unresponsive') {\n this.transition('connected', { pid: this.info?.pid });\n }\n if (message.type === 'progress') {\n entry.onProgress?.(message.current, message.total);\n return;\n }\n this.pending.delete(message.id);\n this.cleanupPending(entry);\n if (message.ok) entry.resolve(message.result);\n else entry.reject(remoteError(message.error, message.errorName));\n }\n\n private onClose(socket: net.Socket): void {\n if (socket !== this.socket) return;\n const wasConnected = this.info !== null;\n this.socket = null;\n this.info = null;\n this.activity = null;\n this.health = null;\n const error = new Error('codebase-index server connection closed');\n this.connectReject?.(error);\n this.connectResolve = null;\n this.connectReject = null;\n this.rejectPending(error);\n if (wasConnected) this.transition('error', { error });\n maybeStopHeartbeatLoop();\n }\n\n private cleanupPending(entry: PendingRequest): void {\n clearTimeout(entry.timer);\n if (entry.signal && entry.onAbort) {\n entry.signal.removeEventListener('abort', entry.onAbort);\n }\n }\n\n private rejectPending(error: unknown): void {\n const entries = [...this.pending.values()];\n this.pending.clear();\n for (const entry of entries) {\n this.cleanupPending(entry);\n entry.reject(error);\n }\n }\n\n private write(message: object): void {\n const socket = this.socket;\n if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));\n }\n\n private rejectStaleServer(message: ProjectIndexServerInfo, reason: string): void {\n const socket = this.socket;\n if (socket && !socket.destroyed) {\n socket.write(\n encodeProjectServerMessage({\n type: 'shutdown',\n id: 0,\n reason: 'stale-build-replacement',\n }),\n );\n const timer = setTimeout(() => socket.destroy(), 25);\n timer.unref?.();\n }\n this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));\n }\n\n private spawnDetachedServer(): void {\n const url = resolveProjectServerUrl();\n if (!url) throw new Error('built codebase-index project server is unavailable');\n // Unix-domain socket files survive an unclean process death. We only reach\n // this branch after a direct connection attempt failed, so an existing\n // path is stale rather than a live server endpoint.\n if (process.platform !== 'win32') {\n try {\n fs.rmSync(this.endpoint, { force: true });\n } catch {\n /* bind/connect race will elect the winner */\n }\n }\n const args = [fileURLToPath(url), '--project-root', this.projectRoot];\n if (this.indexDir) args.push('--index-dir', this.indexDir);\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: 'ignore',\n windowsHide: true,\n env: process.env,\n });\n child.unref();\n }\n\n private forceKillKnownServer(): boolean {\n const pid = this.info?.pid;\n return pid ? this.forceKillServer(pid) : false;\n }\n\n private forceKillServer(pid: number): boolean {\n if (pid === process.pid) return false;\n try {\n process.kill(pid);\n const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);\n try {\n const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8')) as { pid?: number };\n if (metadata.pid === pid) fs.rmSync(metadataPath, { force: true });\n } catch {\n /* absent or already replaced */\n }\n return true;\n } catch {\n return false;\n }\n }\n}\n\nconst connections = new Map<string, ProjectServerConnection>();\nlet heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n\nfunction ensureHeartbeatLoop(): void {\n if (heartbeatTimer) return;\n heartbeatTimer = setInterval(() => {\n for (const connection of connections.values()) {\n if (connection.isConnected()) void connection.checkHealth(false).catch(() => {});\n }\n }, SERVER_HEARTBEAT_INTERVAL_MS);\n heartbeatTimer.unref?.();\n}\n\nfunction maybeStopHeartbeatLoop(): void {\n if (!heartbeatTimer) return;\n if ([...connections.values()].some((connection) => connection.isConnected())) return;\n clearInterval(heartbeatTimer);\n heartbeatTimer = undefined;\n}\n\nfunction connectionFor(projectRoot: string, indexDir?: string): ProjectServerConnection {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n let connection = connections.get(endpoint);\n if (!connection) {\n connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);\n connections.set(endpoint, connection);\n }\n return connection;\n}\n\nexport function callProjectIndexServer<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n options: ProjectServerCallOptions,\n): Promise<OpShapes[O]['result']> {\n return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);\n}\n\nexport function ensureProjectIndexServer(options: {\n projectRoot: string;\n indexDir?: string | undefined;\n watchExternal: boolean;\n debounceMs: number;\n}): Promise<void> {\n return connectionFor(options.projectRoot, options.indexDir).configure(\n options.watchExternal,\n options.debounceMs,\n );\n}\n\nexport function checkProjectIndexServerHealth(\n projectRoot: string,\n indexDir?: string,\n options: { timeoutMs?: number | undefined } = {},\n): Promise<ProjectIndexServerClientHealth> {\n return connectionFor(projectRoot, indexDir).checkHealth(\n false,\n options.timeoutMs ?? SERVER_HEALTH_TIMEOUT_MS,\n );\n}\n\nexport async function shutdownProjectIndexServer(\n projectRoot: string,\n indexDir?: string,\n reason?: string,\n): Promise<ProjectIndexServerShutdownResult> {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n const connection = connectionFor(projectRoot, indexDir);\n try {\n return await connection.shutdownRemote(reason);\n } finally {\n connection.close();\n connections.delete(endpoint);\n connectionStates.delete(endpoint);\n }\n}\n\n/** Disconnect this process from every project server without stopping them. */\nexport function closeProjectIndexServerClients(): void {\n for (const connection of connections.values()) connection.close();\n connections.clear();\n connectionStates.clear();\n latestConnectionState = {\n status: isProjectIndexServerAvailable() ? 'offline' : 'unavailable',\n connected: false,\n };\n if (heartbeatTimer) clearInterval(heartbeatTimer);\n heartbeatTimer = undefined;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveIndexDir } from './writer.js';\n\nexport const PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;\nexport const PROJECT_INDEX_SERVER_METADATA_FILE = 'server.json';\n\nlet buildIdCache:\n | {\n file: string;\n mtimeMs: number;\n size: number;\n buildId: string;\n }\n | undefined;\n\n/**\n * Content identity for the actual server artifact.\n *\n * Package versions do not change during a local rebuild, so the handshake\n * hashes the resolved `project-server.js` itself. The stat guard avoids\n * re-reading the bundle on every reconnect while still noticing a rebuild in a\n * long-lived client process.\n */\nexport function projectIndexServerBuildId(entrypoint: string | URL): string {\n const file =\n entrypoint instanceof URL || entrypoint.startsWith('file:')\n ? fileURLToPath(entrypoint)\n : path.resolve(entrypoint);\n try {\n const stat = fs.statSync(file);\n if (\n buildIdCache?.file === file &&\n buildIdCache.mtimeMs === stat.mtimeMs &&\n buildIdCache.size === stat.size\n ) {\n return buildIdCache.buildId;\n }\n const buildId = createHash('sha256').update(fs.readFileSync(file)).digest('hex').slice(0, 24);\n buildIdCache = { file, mtimeMs: stat.mtimeMs, size: stat.size, buildId };\n return buildId;\n } catch {\n // Both sides resolve the same artifact path. This fallback keeps exotic\n // read-only packagers usable, though normal builds always take the hash.\n return `unreadable:${path.basename(file)}`;\n }\n}\n\nfunction normalizeLocalPath(value: string): string {\n const resolved = path.resolve(value);\n return process.platform === 'win32' ? resolved.toLowerCase() : resolved;\n}\n\n/**\n * Local project identity used by the index transport.\n *\n * The index directory is authoritative rather than the cross-machine project\n * id: worktrees and local clones may share a project id while requiring\n * physically separate SQLite indexes.\n */\nexport function projectIndexServerKey(projectRoot: string, indexDir?: string): string {\n const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));\n return createHash('sha256').update(resolvedIndexDir).digest('hex').slice(0, 24);\n}\n\n/** Deterministic per-project local IPC endpoint. */\nexport function projectIndexServerEndpoint(projectRoot: string, indexDir?: string): string {\n const key = projectIndexServerKey(projectRoot, indexDir);\n if (process.platform === 'win32') {\n return `\\\\\\\\.\\\\pipe\\\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;\n }\n return path.join(\n os.tmpdir(),\n `wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`,\n `${key}.sock`,\n );\n}\n\nexport function projectIndexServerMetadataPath(projectRoot: string, indexDir?: string): string {\n return path.join(\n path.resolve(resolveIndexDir(projectRoot, indexDir)),\n PROJECT_INDEX_SERVER_METADATA_FILE,\n );\n}\n\nexport function ensureProjectIndexSocketDirectory(endpoint: string): void {\n if (process.platform !== 'win32') {\n fs.mkdirSync(path.dirname(endpoint), { recursive: true, mode: 0o700 });\n }\n}\n", "import type { OpName, OpShapes } from './worker-protocol.js';\n\n/** Hard ceiling for one newline-delimited IPC message (measured as JS characters). */\nexport const PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;\n\nexport interface ProjectIndexServerInfo {\n protocolVersion: number;\n buildId: string;\n pid: number;\n projectRoot: string;\n indexDir: string;\n endpoint: string;\n startedAt: string;\n}\n\nexport interface ProjectIndexServerActivity {\n indexing: boolean;\n currentFile: number;\n totalFiles: number;\n generation: number;\n updatedAt: number | null;\n lastError: string | null;\n}\n\nexport interface ProjectIndexServerHealth {\n checkedAt: number;\n uptimeMs: number;\n memory: {\n rss: number;\n heapUsed: number;\n heapTotal: number;\n external: number;\n };\n clients: number;\n activeRequests: number;\n activeWrites: number;\n queuedWrites: number;\n pendingExternalFiles: number;\n watchingExternal: boolean;\n /** Clients currently requesting ownership of the shared external watcher. */\n watchingClients?: number | undefined;\n /** Server-side heartbeat lease applied to connected clients. */\n clientLeaseTimeoutMs?: number | undefined;\n /** Time since the least recently responsive client sent a message. */\n oldestClientIdleMs?: number | undefined;\n activity: ProjectIndexServerActivity;\n}\n\nexport type ProjectServerClientMessage =\n | { type: 'request'; id: number; op: OpName; args: OpShapes[OpName]['args'] }\n | { type: 'cancel'; id: number }\n | { type: 'configure'; id: number; watchExternal: boolean; debounceMs: number }\n | { type: 'ping'; id: number }\n | { type: 'shutdown'; id: number; reason?: string | undefined };\n\nexport type ProjectServerMessage =\n | ({ type: 'hello' } & ProjectIndexServerInfo)\n | { type: 'index-state'; state: ProjectIndexServerActivity }\n | { type: 'response'; id: number; ok: true; result: unknown }\n | { type: 'response'; id: number; ok: false; error: string; errorName?: string | undefined }\n | { type: 'progress'; id: number; current: number; total: number };\n\nexport function encodeProjectServerMessage(message: object): string {\n return `${JSON.stringify(message)}\\n`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Single source of truth for which files are indexable and which\n * {@link SymbolLang} they map to.\n *\n * Keep this list broad: missing a native AST parser must never mean \"skip\n * the file\". Unknown programming/config sources still go through the generic\n * regex extractor under their mapped lang (or `'other'`).\n */\n\nimport * as path from 'node:path';\nimport type { SymbolLang } from './schema.js';\n\n/**\n * Extension \u2192 language. Keys are lowercase including the leading dot.\n * Multi-dot extensions (`.d.ts`) are handled specially in {@link detectLang}.\n */\nexport const EXT_TO_LANG: Readonly<Record<string, SymbolLang>> = {\n // TypeScript / JavaScript (first-class TS compiler API)\n '.ts': 'ts',\n '.mts': 'ts',\n '.cts': 'ts',\n '.tsx': 'tsx',\n '.js': 'js',\n '.mjs': 'js',\n '.cjs': 'js',\n '.jsx': 'jsx',\n\n // First-class native/spawn parsers (+ regex fallback)\n '.go': 'go',\n '.py': 'py',\n '.pyi': 'py',\n '.pyw': 'py',\n '.rs': 'rs',\n '.json': 'json',\n '.jsonc': 'json',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n\n // C family\n '.c': 'c',\n '.h': 'c',\n '.cc': 'cpp',\n '.cpp': 'cpp',\n '.cxx': 'cpp',\n '.hh': 'cpp',\n '.hpp': 'cpp',\n '.hxx': 'cpp',\n\n // JVM / .NET\n '.java': 'java',\n '.cs': 'csharp',\n '.kt': 'kotlin',\n '.kts': 'kotlin',\n '.scala': 'scala',\n '.sc': 'scala',\n\n // Scripting\n '.php': 'php',\n '.rb': 'ruby',\n '.swift': 'swift',\n '.dart': 'dart',\n '.lua': 'lua',\n '.r': 'r',\n '.R': 'r',\n '.pl': 'other',\n '.pm': 'other',\n\n // Systems / functional\n '.zig': 'zig',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.hs': 'haskell',\n '.lhs': 'haskell',\n\n // Shell / data / docs / web\n '.sh': 'shell',\n '.bash': 'shell',\n '.zsh': 'shell',\n '.ps1': 'shell',\n '.sql': 'sql',\n '.md': 'md',\n '.mdx': 'md',\n '.toml': 'toml',\n '.html': 'html',\n '.htm': 'html',\n '.css': 'css',\n '.scss': 'css',\n '.less': 'css',\n '.vue': 'vue',\n '.svelte': 'svelte',\n '.proto': 'proto',\n '.graphql': 'graphql',\n '.gql': 'graphql',\n};\n\n/** Sorted unique extension list for discovery walks. */\nexport const INDEXABLE_EXTENSIONS: readonly string[] = Object.freeze(\n [...new Set(Object.keys(EXT_TO_LANG).map((e) => e.toLowerCase()))].sort(),\n);\n\n/** Filenames without (or with special) extensions that should still be indexed. */\nconst SPECIAL_FILENAMES: Readonly<Record<string, SymbolLang>> = {\n makefile: 'other',\n gnumakefile: 'other',\n dockerfile: 'other',\n 'docker-compose.yml': 'yaml',\n 'docker-compose.yaml': 'yaml',\n 'cmakelists.txt': 'other',\n gemfile: 'ruby',\n rakefile: 'ruby',\n procfile: 'other',\n justfile: 'other',\n};\n\n/**\n * Detect {@link SymbolLang} from a file path.\n * Returns `null` only for paths we intentionally refuse to index (binary\n * assets, lockfiles handled elsewhere, plain `.txt` without special name, \u2026).\n */\nexport function detectLang(file: string): SymbolLang | null {\n const base = path.basename(file);\n const lowerBase = base.toLowerCase();\n\n // declaration files: foo.d.ts \u2192 ts\n if (lowerBase.endsWith('.d.ts') || lowerBase.endsWith('.d.mts') || lowerBase.endsWith('.d.cts')) {\n return 'ts';\n }\n\n const special = SPECIAL_FILENAMES[lowerBase];\n if (special) return special;\n\n const ext = path.extname(base).toLowerCase();\n if (!ext) return null;\n return EXT_TO_LANG[ext] ?? null;\n}\n\n/** True when the path is eligible for the codebase index. */\nexport function isIndexablePath(file: string): boolean {\n return detectLang(file) !== null;\n}\n", "/**\n * TypeScript/JavaScript symbol extraction using the TypeScript Compiler API.\n *\n * We traverse the AST and collect:\n * - classes, interfaces, enums, type aliases \u2192 class|interface|enum|type\n * - functions and methods \u2192 function|method\n * - const/let/var declarations \u2192 const|let|var\n * - property/accessor declarations \u2192 property\n *\n * The `id` field on each Symbol is always 0 \u2014 the caller is responsible for\n * assigning unique ids during insertion.\n */\n\nimport type * as TS from '@typescript/typescript6';\nimport type { FileSymbols, Symbol as IndexSymbol, Ref, SymbolKind, SymbolLang } from './schema.js';\n\ntype TsModule = typeof import('@typescript/typescript6');\n\n/**\n * The TypeScript compiler is ~9MB of JavaScript and costs ~26MB heap / ~44MB\n * RSS to evaluate. A static `import` here put it in the module graph of every\n * bundle that can reach the indexer \u2014 including `wstack version`, the mailbox\n * bridge, and the codebase-index project server, none of which parse TS.\n *\n * It must stay a runtime `import()` of an EXTERNAL package: the build runs with\n * `splitting: false` (scripts/build-package.mjs), so esbuild inlines dynamic\n * imports of in-repo files. `parser-dispatch.ts` doing `await import('./ts-parser.js')`\n * therefore does NOT defer anything on its own \u2014 this boundary is the one that\n * survives bundling. Mirrors `_syntax-check.ts`.\n */\nlet ts!: TsModule;\nlet tsLoad: Promise<TsModule> | null = null;\n\nfunction loadTypescript(): Promise<TsModule> {\n tsLoad ??= import('@typescript/typescript6').then((m) => {\n ts = ((m as unknown as { default?: TsModule }).default ?? m) as TsModule;\n return ts;\n });\n return tsLoad;\n}\n\n// Map TypeScript SyntaxKind \u2192 our SymbolKind taxonomy. Built on first use\n// because the enum values only exist once the compiler module is loaded.\nlet kindMapCache: Partial<Record<TS.SyntaxKind, SymbolKind>> | null = null;\n\nfunction kindMap(): Partial<Record<TS.SyntaxKind, SymbolKind>> {\n kindMapCache ??= {\n [ts.SyntaxKind.ClassDeclaration]: 'class',\n [ts.SyntaxKind.InterfaceDeclaration]: 'interface',\n [ts.SyntaxKind.EnumDeclaration]: 'enum',\n [ts.SyntaxKind.TypeAliasDeclaration]: 'type',\n [ts.SyntaxKind.FunctionDeclaration]: 'function',\n [ts.SyntaxKind.MethodDeclaration]: 'method',\n [ts.SyntaxKind.GetAccessor]: 'property',\n [ts.SyntaxKind.SetAccessor]: 'property',\n [ts.SyntaxKind.PropertyDeclaration]: 'property',\n [ts.SyntaxKind.Parameter]: 'parameter',\n [ts.SyntaxKind.NamespaceExportDeclaration]: 'namespace',\n };\n return kindMapCache;\n}\n\nfunction kindOf(node: TS.Node): SymbolKind | null {\n // VariableDeclaration needs special handling \u2014 its parent tells us whether\n // it's `const`, `let`, or `var`.\n if (ts.isVariableDeclaration(node)) {\n const parent = node.parent;\n if (ts.isVariableDeclarationList(parent)) {\n const flags = parent.flags;\n if (flags & ts.NodeFlags.Let) return 'let';\n if (flags & ts.NodeFlags.Const) return 'const';\n return 'var';\n }\n }\n\n // Namespace (module) declaration\n if (ts.isModuleDeclaration(node)) return 'namespace';\n\n return kindMap()[node.kind] ?? null;\n}\n\n// Extension \u2192 language lives in languages.ts (single source of truth for\n// discovery + first-class + generic coverage).\n\nfunction getSignature(\n printer: TS.Printer,\n node: TS.Declaration,\n sourceFile: TS.SourceFile,\n): string {\n const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);\n return raw.replace(/\\s+/g, ' ').slice(0, 500);\n}\n\n/**\n * Extract the first line of a JSDoc comment preceding a node.\n * Uses `ts.getLeadingCommentRanges` which is the modern replacement for\n * the removed `ts.getJSDocComments`.\n */\nfunction getJsDoc(node: TS.Node, sourceFile: TS.SourceFile): string {\n const fullText = sourceFile.getFullText();\n // getLeadingCommentRanges wants the position where the node's leading trivia\n // begins (getFullStart), not the node's width \u2014 passing getFullWidth() looked\n // past the comment and silently returned no JSDoc for every symbol.\n const nodePos = node.getFullStart();\n const comments = ts.getLeadingCommentRanges(fullText, nodePos);\n if (!comments) return '';\n\n for (const range of comments) {\n const commentText = fullText.slice(range.pos, range.end);\n // Only process JSDoc comments (/** ... */)\n const trimmed = commentText.trim();\n if (trimmed.startsWith('/**') && trimmed.endsWith('*/')) {\n // Strip the /** and */ delimiters and leading * on each line\n const inner = trimmed\n .slice(3, -2) // remove /** and */\n .replace(/^[ \\t]*\\*[ ]?/gm, '') // remove leading \" * \" or \" *\" on each line\n .trim();\n return inner.split('\\n')[0]?.trim().slice(0, 200) ?? '';\n }\n }\n return '';\n}\n\n/** Push the current node's scope contribution onto `parts` (for the O(1) recursive scope tracker). */\nfunction pushScopeName(node: TS.Node, parts: string[]): void {\n if (\n ts.isClassDeclaration(node) ||\n ts.isInterfaceDeclaration(node) ||\n ts.isEnumDeclaration(node) ||\n ts.isTypeAliasDeclaration(node)\n ) {\n parts.push(node.name?.text ?? 'Anon');\n } else if (\n ts.isMethodDeclaration(node) ||\n ts.isGetAccessor(node) ||\n ts.isSetAccessor(node) ||\n ts.isPropertyDeclaration(node) ||\n ts.isFunctionDeclaration(node)\n ) {\n if (node.name && ts.isIdentifier(node.name)) {\n parts.push(node.name.text);\n }\n }\n}\n\nexport interface ParseOptions {\n file: string;\n content: string;\n lang: SymbolLang;\n}\n\n/**\n * Parse a TypeScript/JavaScript source file and extract all code symbols.\n *\n * The returned `Symbol.id` field is always `0` \u2014 the caller is responsible\n * for assigning unique numeric ids during bulk insertion.\n *\n * Returns an empty array for files that can't be parsed or contain no symbols.\n *\n * Async because the TypeScript compiler is loaded on first use \u2014 see\n * {@link loadTypescript}. The load is memoized, so only the first call to this\n * function in a process pays for it.\n */\nexport async function parseSymbols(opts: ParseOptions): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n await loadTypescript();\n\n let sourceFile: TS.SourceFile;\n try {\n sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);\n } catch {\n /* v8 ignore next -- createSourceFile tolerates malformed input and does not throw; defensive. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n\n const symbols: IndexSymbol[] = [];\n const refs: Ref[] = [];\n // Create the printer once per file instead of per-symbol. ts.createPrinter is\n // not free \u2014 it allocates internal emitter state \u2014 and we call getSignature\n // for every navigable declaration (often 100-300 per file).\n const printer = ts.createPrinter({});\n\n function visit(node: TS.Node, funcDepth: number, scopeParts: string[]): void {\n // \u2500\u2500 Symbol extraction \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const kind = kindOf(node);\n\n if (kind) {\n // Keep the index focused on navigable declarations. Function-local\n // variables and parameters account for most rows in large TypeScript\n // projects and otherwise swamp exact declaration searches.\n // funcDepth is a cheap O(1) counter threaded through the recursion\n // instead of walking up the parent chain per symbol.\n if (\n (kind === 'const' || kind === 'let' || kind === 'var' || kind === 'parameter') &&\n funcDepth > 0\n ) {\n // Fall through to ref extraction \u2014 function-local variables can still\n // appear in type references and calls.\n } else {\n const nameNode = (node as { name?: TS.Identifier | undefined }).name;\n if (!nameNode || !ts.isIdentifier(nameNode)) {\n // Anonymous declaration (e.g. `export default class { ... }`) \u2014 no\n // name identifier, so there's nothing to index. Skip children too\n // to avoid indexing members of anonymous containers. Ref extraction\n // for the node itself is also skipped, but anonymous declarations\n // never match ref checks anyway.\n return;\n }\n const name = nameNode.text;\n const pos = nameNode.getStart(sourceFile);\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);\n const scope = scopeParts.join('.');\n const signature = getSignature(printer, node as TS.Declaration, sourceFile);\n const docComment = getJsDoc(node, sourceFile);\n const text = [name, signature, docComment].filter(Boolean).join(' | ');\n\n symbols.push({\n id: 0,\n lang,\n kind,\n name,\n file,\n line: line + 1,\n col: character,\n signature,\n docComment,\n scope,\n text,\n });\n }\n }\n\n // \u2500\u2500 Reference extraction (inlined from extractRefs) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const pos = node.getStart(sourceFile);\n const { line } = sourceFile.getLineAndCharacterOfPosition(pos);\n const lineNum = line + 1;\n\n if (ts.isCallExpression(node)) {\n const expr = node.expression;\n if (ts.isIdentifier(expr)) {\n refs.push({ fromId: 0, toName: expr.text, callType: 'call', line: lineNum });\n }\n } else if (ts.isPropertyAccessExpression(node)) {\n if (ts.isIdentifier(node.expression)) {\n refs.push({ fromId: 0, toName: node.expression.text, callType: 'call', line: lineNum });\n }\n } else if (ts.isTypeReferenceNode(node)) {\n const name = getTypeName(node.typeName);\n if (name) refs.push({ fromId: 0, toName: name, callType: 'type_ref', line: lineNum });\n } else if (ts.isHeritageClause(node)) {\n for (const t of node.types) {\n const name = getTypeName(t.expression as TS.EntityName);\n if (name)\n refs.push({\n fromId: 0,\n toName: name,\n callType: node.token === ts.SyntaxKind.ExtendsKeyword ? 'inherit' : 'implement',\n line: lineNum,\n });\n }\n } else if (ts.isImportDeclaration(node)) {\n // Emit import refs for each imported symbol NAME rather than the module\n // path string. This lets the ref resolver match the import against the\n // target symbol's declaration name, so the dead-code BFS can traverse\n // module boundaries. Module-path refs (old behaviour) were never\n // resolvable because no symbol is ever named './foo.js'.\n emitImportSpecifierRefs(node, refs, lineNum);\n } else if (ts.isExportDeclaration(node) && node.moduleSpecifier) {\n // Re-exports: export { X } from './foo' \u2014 emit refs for the exported\n // names so they resolve to the source module's symbols.\n emitExportSpecifierRefs(node, refs, lineNum);\n }\n\n // Push scope name before recursing, pop after (O(1) instead of O(depth) parent walk)\n const scopeIdx = scopeParts.length;\n pushScopeName(node, scopeParts);\n const childFuncDepth = ts.isFunctionLike(node) ? funcDepth + 1 : funcDepth;\n ts.forEachChild(node, (child) => visit(child, childFuncDepth, scopeParts));\n scopeParts.length = scopeIdx;\n }\n\n visit(sourceFile, 0, []);\n\n return { file, lang, symbols, refs: deduplicateRefs(refs), mtimeMs: Date.now() };\n}\n\n// \u2500\u2500\u2500 Reference extraction helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Extract the name string from a type name node (simple or qualified). */\nfunction getTypeName(name: TS.EntityName): string {\n if (ts.isIdentifier(name)) return name.text;\n if (ts.isQualifiedName(name)) return `${getTypeName(name.left)}.${name.right.text}`;\n /* v8 ignore next -- an EntityName is always an Identifier or QualifiedName; defensive. */\n return '';\n}\n\n/** Remove duplicate refs (same toName, callType, line). fromId is always 0 at this stage. */\nfunction deduplicateRefs(refs: Ref[]): Ref[] {\n const seen = new Set<string>();\n return refs.filter((r) => {\n const key = `${r.toName}:${r.callType}:${r.line}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/** Extract the imported (original-export) name from an ImportSpecifier. */\nfunction getImportSpecifierName(spec: TS.ImportSpecifier): string {\n // import { X as Y } from './foo' \u2192 propertyName is 'X', name is 'Y'\n // import { X } from './foo' \u2192 propertyName is undefined, name is 'X'\n // We emit the ORIGINAL exported name so the ref resolves to the\n // declaration symbol in the source module.\n return spec.propertyName?.text ?? spec.name.text;\n}\n\n/**\n * Emit `import` refs for each named symbol brought into scope by an\n * `ImportDeclaration`. Uses the original exported name (not the local\n * alias and not the module path) so the ref resolver can match it against\n * the target symbol's declaration name.\n *\n * Handles:\n * import { X } from 'M' \u2192 ref toName: 'X'\n * import { X as Y } from 'M' \u2192 ref toName: 'X' (original name)\n * import X from 'M' \u2192 ref toName: 'X'\n * import * as X from 'M' \u2192 ref toName: 'X'\n * import { type X } from 'M' \u2192 ref toName: 'X' (type-only flagged)\n * import 'M' \u2192 no refs (side-effect only)\n */\nfunction emitImportSpecifierRefs(node: TS.ImportDeclaration, refs: Ref[], lineNum: number): void {\n const clause = node.importClause;\n if (!clause) return; // side-effect import: import 'foo'\n\n // Default import: import X from 'M' (may coexist with named bindings\n // e.g. import React, { useState } from 'react')\n if (clause.name) {\n refs.push({ fromId: 0, toName: clause.name.text, callType: 'import', line: lineNum });\n }\n\n // Named imports: import { X, Y } from 'M'\n const bindings = clause.namedBindings;\n if (!bindings) return;\n\n if (ts.isNamedImports(bindings)) {\n for (const element of bindings.elements) {\n refs.push({\n fromId: 0,\n toName: getImportSpecifierName(element),\n callType: 'import',\n line: lineNum,\n });\n }\n } else if (ts.isNamespaceImport(bindings)) {\n // import * as X from 'M'\n refs.push({ fromId: 0, toName: bindings.name.text, callType: 'import', line: lineNum });\n }\n}\n\n/**\n * Emit `import` refs for each symbol re-exported by an `ExportDeclaration`\n * with a `from` clause. These use the original source-side name so the ref\n * resolves to the declaration symbol in the source module.\n *\n * Handles:\n * export { X } from 'M' \u2192 ref toName: 'X'\n * export { X as Y } from 'M' \u2192 ref toName: 'X' (original name)\n * export * as X from 'M' \u2192 ref toName: 'X' (namespace)\n * export * from 'M' \u2192 NO ref (wildcard \u2014 handled via\n * file-level graph in dead-code-scan)\n */\nfunction emitExportSpecifierRefs(node: TS.ExportDeclaration, refs: Ref[], lineNum: number): void {\n const clause = node.exportClause;\n\n if (clause && ts.isNamespaceExport(clause)) {\n // export * as X from 'M' \u2014 NamespaceExport has a .name\n refs.push({ fromId: 0, toName: clause.name.text, callType: 'import', line: lineNum });\n return;\n }\n\n if (clause && ts.isNamedExports(clause)) {\n // export { X } from 'M' \u2014 NamedExports\n for (const element of clause.elements) {\n // export { X as Y } \u2192 propertyName is 'X' (original), name is 'Y' (exported)\n // export { X } \u2192 propertyName is undefined, name is 'X'\n const originalName = element.propertyName?.text ?? element.name.text;\n refs.push({ fromId: 0, toName: originalName, callType: 'import', line: lineNum });\n }\n return;\n }\n\n // export * from 'M' \u2014 no clause (wildcard). No per-symbol ref is\n // possible; the dead-code-scan handles this via file-level graph.\n}\n\n/** Detect SymbolLang from a file path \u2014 re-exported from the central map. */\nexport { detectLang } from './languages.js';\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * On Windows, Node.js `spawn()` without a shell does NOT resolve .cmd/.bat\n * extensions through PATHEXT \u2014 it only auto-resolves .exe. Most Node.js CLI\n * tools (npx, pnpm, biome, tsc, vitest, etc.) ship as .cmd wrappers on\n * Windows. This function resolves the command name to its full path so spawn\n * can find it without relying on shell-mode argument concatenation.\n *\n * On non-Windows, returns the command unchanged.\n */\nexport function resolveWin32Command(cmd: string): string {\n if (process.platform !== 'win32') return cmd;\n\n // Already has a path or extension \u2014 use as-is\n // Normalize forward slashes so path.extname correctly detects extensions\n // even when a Unix-style path is passed on Windows.\n if (cmd.includes('/') || cmd.includes('\\\\') || path.extname(cmd.replace(/\\//g, '\\\\'))) {\n return cmd;\n }\n\n const pathext = (process.env['PATHEXT'] ?? '.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC')\n .toLowerCase()\n .split(';');\n\n const pathDirs = (process.env['PATH'] ?? '').split(path.delimiter);\n\n for (const dir of pathDirs) {\n const base = path.join(dir, cmd);\n // Check extensions in PATHEXT order. .EXE should win first because\n // it's typically listed first, and .exe doesn't need shell: true.\n for (const ext of pathext) {\n const full = `${base}${ext}`;\n try {\n fs.accessSync(full, fs.constants.X_OK);\n return full;\n } catch {\n // Not found with this extension \u2014 try next\n }\n }\n }\n\n // Not found \u2014 return original; let spawn report ENOENT with the\n // expected error message so tools can surface it properly.\n return cmd;\n}\n\n/**\n * Resolve a PowerShell binary by name. `pickShell` in `_shell-pick.ts`\n * already decides whether the user wants `'pwsh'` (PowerShell 7+) or\n * `'powershell'` (Windows PowerShell 5.1). This helper turns that decision\n * into a real on-disk path.\n *\n * Order:\n * 1. If `cmd` is `pwsh` and a `pwsh.exe` exists on PATH \u2192 return that.\n * 2. If `cmd` is `pwsh` and only `powershell.exe` exists \u2192 fall back to\n * that (the alternative is a cryptic ENOENT for the user).\n * 3. Symmetric for `powershell`: prefer `powershell.exe`, fall back to\n * `pwsh.exe` if installed and the legacy binary is missing.\n * 4. Anything else \u2192 delegate to `resolveWin32Command` (handles `.cmd`\n * shims a sysadmin might drop in place, etc.).\n *\n * Returns the original command on ENOENT \u2014 `spawn()` will surface a clean\n * ENOENT and the user sees \"PowerShell not installed\", which is the right\n * diagnostic. We never throw from here.\n */\nexport function resolvePowerShell(cmd: string): string {\n if (process.platform !== 'win32') return cmd;\n const lower = cmd.toLowerCase();\n if (lower !== 'pwsh' && lower !== 'powershell' && lower !== 'pwsh.exe' && lower !== 'powershell.exe') {\n return resolveWin32Command(cmd);\n }\n // Prefer the requested edition, fall back to the other one.\n const primary = lower.startsWith('pwsh') ? 'pwsh.exe' : 'powershell.exe';\n const fallback = lower.startsWith('pwsh') ? 'powershell.exe' : 'pwsh.exe';\n const resolved = resolveWin32Command(primary);\n if (resolved !== primary) {\n // resolveWin32Command returns the original string when not found.\n const fb = resolveWin32Command(fallback);\n return fb === fallback ? cmd : fb;\n }\n return resolved;\n}\n\n/**\n * cmd.exe metacharacters that chain a new command or redirect I/O. When a\n * `.cmd`/`.bat` wrapper is launched through `cmd.exe`, any argument carrying\n * one of these can break out of the intended command line and run an\n * attacker-chosen command (the CVE-2024-27980 / \"BatBadBut\" argument-injection\n * class). We use a single vetted command line for cmd shims, so this guard is\n * mandatory before spawning.\n *\n * The set is limited to the unambiguous command-separator / redirection chars\n * plus newlines and NUL. Legitimate package-manager / test-runner flags and\n * Windows file paths (which use `:` `\\` `/` `.` `-` `_` space `(` `)`) never\n * contain these, so the guard is false-positive-free. Double quotes are also\n * rejected because cmd.exe quote toggling can break argument grouping.\n */\nconst WIN32_SHELL_META = /[&|<>\"\\r\\n\\0]/;\n\nexport interface Win32CmdShimInvocation {\n command: string;\n args: string[];\n windowsVerbatimArguments: true;\n}\n\n/**\n * Throw if any argument contains a cmd.exe command-injection metacharacter.\n * Call this ONLY on the Windows `.cmd`/`.bat` shim path. A no-op for safe args.\n */\nexport function assertSafeWin32ShellArgs(args: readonly unknown[]): void {\n for (const arg of args) {\n if (typeof arg === 'string' && WIN32_SHELL_META.test(arg)) {\n throw new Error(\n 'win32 cmd shim spawn: argument contains a shell metacharacter ' +\n '(one of & | < > \", or a newline) that could enable command injection ' +\n 'through the .cmd/.bat wrapper - refusing to run. Offending argument: ' +\n JSON.stringify(arg),\n );\n }\n }\n}\n\nexport function buildWin32CmdShimInvocation(\n command: string,\n args: readonly string[] = [],\n): Win32CmdShimInvocation {\n assertSafeWin32ShellArgs([command, ...args]);\n const line = ['call', quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(' ');\n return {\n command: process.env['COMSPEC'] ?? 'cmd.exe',\n args: ['/d', '/c', line],\n windowsVerbatimArguments: true,\n };\n}\n\nfunction quoteWin32CmdArg(arg: string): string {\n return `\"${arg}\"`;\n}\n", "/**\n * Global concurrency gate for external parser processes (Python / Go / cargo).\n *\n * Spawning many AST helpers in parallel burns CPU and thrash the OS process\n * table on Windows. Serializing native toolchains is correctness-preserving:\n * each file still gets the same parse result, just not all at once.\n */\n\nlet chain: Promise<unknown> = Promise.resolve();\n\n/**\n * Run `fn` exclusively with respect to other {@link withSpawnGate} callers\n * in this process. Errors propagate to the caller and do not break the queue.\n */\nexport function withSpawnGate<T>(fn: () => Promise<T>): Promise<T> {\n const run = chain.then(fn, fn);\n // Keep the chain alive even when `fn` rejects.\n chain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n}\n\n/** Test helper \u2014 reset queue between cases. */\nexport function resetSpawnGateForTests(): void {\n chain = Promise.resolve();\n}\n", "/**\n * Go source symbol extraction using `go/parser`.\n *\n * Spawns a `go run -` child process that parses the file with go/ast and\n * emits JSON. Falls back to empty results on any error.\n *\n * Extracts: package, func, type, const, var\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { resolveWin32Command } from '../_win32-resolve.js';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { withSpawnGate } from './spawn-gate.js';\n\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n try {\n // Serialize go child processes process-wide (same gate as Python).\n const parsed = await withSpawnGate(() => syncGoParse(file, content, lang));\n if (parsed.symbols.length > 0) {\n return parsed;\n }\n return fallbackParse(file, content, lang);\n } catch {\n /* v8 ignore next -- syncGoParse has its own catch; this outer guard is defensive. */\n return fallbackParse(file, content, lang);\n }\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Lightweight fallback parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction fallbackParse(filePath: string, content: string, lang: SymbolLang): FileSymbols {\n if (!/^\\s*package\\s+[A-Za-z_]\\w*/m.test(content) || hasUnbalancedDelimiters(content)) {\n return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n }\n\n const symbols: IndexSymbol[] = [];\n const packageName = content.match(/^\\s*package\\s+([A-Za-z_]\\w*)/m)?.[1] ?? '';\n const lines = content.split(/\\r?\\n/);\n for (const [idx, line] of lines.entries()) {\n const trimmed = line.trimStart();\n const col = line.length - trimmed.length + 1;\n const fn = /^func\\s+(?:\\([^)]*\\)\\s*)?([A-Za-z_]\\w*)\\s*\\(/.exec(trimmed);\n if (fn?.[1]) {\n addFallbackSymbol(symbols, { filePath, lang, kind: trimmed.startsWith('func (') ? 'method' : 'function', name: fn[1], line: idx + 1, col, signature: trimmed, scope: packageName ? `${packageName}.${fn[1]}` : fn[1] });\n continue;\n }\n\n const typeDecl = /^type\\s+([A-Za-z_]\\w*)\\b/.exec(trimmed);\n if (typeDecl?.[1]) {\n addFallbackSymbol(symbols, { filePath, lang, kind: 'type', name: typeDecl[1], line: idx + 1, col, signature: trimmed, scope: packageName });\n continue;\n }\n\n const valueDecl = /^(const|var)\\s+([A-Za-z_]\\w*)\\b/.exec(trimmed);\n if (valueDecl?.[1] && valueDecl[2]) {\n addFallbackSymbol(symbols, { filePath, lang, kind: valueDecl[1] as 'const' | 'var', name: valueDecl[2], line: idx + 1, col, signature: trimmed, scope: packageName });\n }\n }\n\n return { file: filePath, lang, symbols, mtimeMs: Date.now() };\n}\n\nfunction addFallbackSymbol(\n symbols: IndexSymbol[],\n opts: {\n filePath: string;\n lang: SymbolLang;\n kind: IndexSymbol['kind'];\n name: string;\n line: number;\n col: number;\n signature: string;\n scope: string;\n },\n): void {\n symbols.push({\n id: 0,\n lang: opts.lang,\n kind: opts.kind,\n name: opts.name,\n file: opts.filePath,\n line: opts.line,\n col: opts.col,\n signature: opts.signature,\n docComment: '',\n scope: opts.scope,\n text: `${opts.name} ${opts.signature}`.trim(),\n });\n}\n\nfunction hasUnbalancedDelimiters(content: string): boolean {\n const pairs: Record<string, string> = { '(': ')', '[': ']', '{': '}' };\n const closers = new Set(Object.values(pairs));\n const stack: string[] = [];\n for (const ch of content) {\n if (pairs[ch]) {\n stack.push(pairs[ch]);\n } else if (closers.has(ch) && stack.pop() !== ch) {\n return true;\n }\n }\n return stack.length > 0;\n}\n\n// \u2500\u2500\u2500 Inline Go parser script \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst GO_PARSE_SCRIPT = `\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\ntype Sym struct {\n\tName string \\`json:\"name\"\\`\n\tKind string \\`json:\"kind\"\\`\n\tLine int \\`json:\"line\"\\`\n\tCol int \\`json:\"col\"\\`\n\tSignature string \\`json:\"signature\"\\`\n\tScope string \\`json:\"scope\"\\`\n}\n\nfunc main() {\n\tsrc, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\tfmt.Print(\"[]\")\n\t\treturn\n\t}\n\tfset := token.NewFileSet()\n\tnode, err := parser.ParseFile(fset, \"src.go\", src, 0)\n\tif err != nil {\n\t\tfmt.Print(\"[]\")\n\t\treturn\n\t}\n\n\tvar syms []Sym\n\n\t// Package-level scope\n\tpkgScope := node.Name.Name\n\n\t// Collect all top-level declarations\n\tfor _, decl := range node.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tname := d.Name.Name\n\t\t\tkind := \"function\"\n\t\t\tscope := pkgScope\n\t\t\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\t\t\tscope = pkgScope + \".\" + recvTypeName(d.Recv.List[0].Type) + \".\" + name\n\t\t\t\tkind = \"method\"\n\t\t\t} else {\n\t\t\t\tscope = pkgScope + \".\" + name\n\t\t\t}\n\t\t\tpos := fset.Position(d.Pos())\n\t\t\tsig := formatFuncSig(d)\n\t\t\tsyms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: scope})\n\n\t\tcase *ast.GenDecl:\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tname := s.Name.Name\n\t\t\t\t\tpos := fset.Position(s.Pos())\n\t\t\t\t\tsig := \"type \" + name\n\t\t\t\t\tif s.TypeParams != nil {\n\t\t\t\t\t\tsig += formatTypeParams(s.TypeParams)\n\t\t\t\t\t}\n\t\t\t\t\tif st, ok := s.Type.(*ast.StructType); ok {\n\t\t\t\t\t\tsig += \" = struct { \" + formatFields(st.Fields.List) + \" }\"\n\t\t\t\t\t} else if it, ok := s.Type.(*ast.InterfaceType); ok {\n\t\t\t\t\t\tsig += \" = interface { \" + formatMethods(it.Methods.List) + \" }\"\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsig += \" = \" + formatType(s.Type)\n\t\t\t\t\t}\n\t\t\t\t\tsyms = append(syms, Sym{Name: name, Kind: \"type\", Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})\n\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tfor _, n := range s.Names {\n\t\t\t\t\t\tname := n.Name\n\t\t\t\t\t\tpos := fset.Position(n.Pos())\n\t\t\t\t\t\tkind := \"var\"\n\t\t\t\t\t\tif d.Tok == token.CONST {\n\t\t\t\t\t\t\tkind = \"const\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsig := kind + \" \" + name\n\t\t\t\t\t\tif s.Type != nil {\n\t\t\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdata, err := json.Marshal(syms)\n\tif err != nil {\n\t\tfmt.Print(\"[]\")\n\t\treturn\n\t}\n\tfmt.Print(string(data))\n}\n\nfunc recvTypeName(t ast.Expr) string {\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.StarExpr:\n\t\treturn recvTypeName(v.X)\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\nfunc formatFuncSig(d *ast.FuncDecl) string {\n\tscope := \"\"\n\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\tscope = \"(\" + formatFieldList(d.Recv.List) + \") \"\n\t}\n\tscope += formatFuncType(d.Type)\n\treturn \"func \" + scope\n}\n\nfunc formatFuncType(f *ast.FuncType) string {\n\tparams := formatFieldList(f.Params.List)\n\tresults := \"\"\n\tif f.Results != nil {\n\t\tresults = \" -> \" + formatFieldList(f.Results.List)\n\t}\n\treturn params + results\n}\n\nfunc formatFieldList(fields []*ast.Field) string {\n\tif len(fields) == 0 {\n\t\treturn \"()\"\n\t}\n\tnames := make([]string, 0, len(fields))\n\tfor _, f := range fields {\n\t\tname := \"\"\n\t\tif len(f.Names) > 0 {\n\t\t\tname = f.Names[0].Name\n\t\t}\n\t\tt := formatType(f.Type)\n\t\tif name != \"\" {\n\t\t\tnames = append(names, name+\" \"+t)\n\t\t} else {\n\t\t\tnames = append(names, t)\n\t\t}\n\t}\n\treturn \"(\" + strings.Join(names, \", \") + \")\"\n}\n\nfunc formatFields(fields []*ast.Field) string {\n\tlines := make([]string, 0)\n\tfor _, f := range fields {\n\t\tname := \"\"\n\t\tif len(f.Names) > 0 {\n\t\t\tname = f.Names[0].Name\n\t\t}\n\t\tt := formatType(f.Type)\n\t\tif name != \"\" {\n\t\t\tlines = append(lines, name+\" \"+t)\n\t\t} else {\n\t\t\tlines = append(lines, t)\n\t\t}\n\t}\n\treturn strings.Join(lines, \"; \")\n}\n\nfunc formatMethods(fields []*ast.Field) string {\n\treturn formatFields(fields)\n}\n\nfunc formatTypeParams(tp *ast.FieldList) string {\n\tif tp == nil || len(tp.List) == 0 {\n\t\treturn \"\"\n\t}\n\tparams := make([]string, len(tp.List))\n\tfor i, p := range tp.List {\n\t\tif len(p.Names) > 0 {\n\t\t\tparams[i] = p.Names[0].Name\n\t\t} else {\n\t\t\tparams[i] = \"T\"\n\t\t}\n\t}\n\treturn \"[\" + strings.Join(params, \", \") + \"]\"\n}\n\nfunc formatType(t ast.Expr) string {\n\tif t == nil {\n\t\treturn \"?\"\n\t}\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn formatType(v.X) + \".\" + v.Sel.Name\n\tcase *ast.StarExpr:\n\t\treturn \"*\" + formatType(v.X)\n\tcase *ast.ArrayType:\n\t\tif v.Len == nil {\n\t\t\treturn \"[]\" + formatType(v.Elt)\n\t\t}\n\t\treturn \"[...]\" + formatType(v.Elt)\n\tcase *ast.MapType:\n\t\treturn \"map[\" + formatType(v.Key) + \"]\" + formatType(v.Value)\n\tcase *ast.InterfaceType:\n\t\treturn \"interface{}\"\n\tcase *ast.StructType:\n\t\treturn \"struct{}\"\n\tcase *ast.FuncType:\n\t\treturn formatFuncType(v)\n\tcase *ast.ChanType:\n\t\treturn \"chan \" + formatType(v.Value)\n\tcase *ast.BasicLit:\n\t\treturn v.Value\n\tcase *ast.IndexExpr:\n\t\t// Generic instantiation with one type arg, e.g. Logger[int].\n\t\treturn formatType(v.X) + \"[\" + formatType(v.Index) + \"]\"\n\tcase *ast.IndexListExpr:\n\t\t// Generic instantiation with multiple type args, e.g. Map[K, V].\n\t\targs := make([]string, len(v.Indices))\n\t\tfor i, idx := range v.Indices {\n\t\t\targs[i] = formatType(idx)\n\t\t}\n\t\treturn formatType(v.X) + \"[\" + strings.Join(args, \", \") + \"]\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n`;\n\n// Cache the temp script path so we don't rewrite the parser script on every\n// file. The script is identical for every invocation \u2014 writing it once per\n// process (like py-parser does) eliminates mkdtemp + writeFile + rm per file.\nlet _cachedGoScriptPath: string | null = null;\n\nasync function syncGoParse(\n filePath: string,\n content: string,\n lang: SymbolLang,\n): Promise<FileSymbols> {\n // Feed the source over stdin \u2014 never pass the target .go file as a CLI arg.\n // `go run script.go target.go` makes the toolchain treat target.go as a\n // second package file (\"named files must all be in one directory\") and\n // refuses *_test.go outright. Reading from stdin sidesteps both, and lets\n // us parse the in-memory content without touching disk.\n try {\n // Local `let` so TypeScript's CFA narrows to `string` after the guard.\n // Module-scope `_cachedGoScriptPath` stays `string | null` because TS\n // can't prove no concurrent mutation between the check and the use.\n let scriptPath = _cachedGoScriptPath;\n if (!scriptPath) {\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ws-go-parse-'));\n scriptPath = path.join(tmpDir, 'parse.go');\n await fs.writeFile(scriptPath, GO_PARSE_SCRIPT, 'utf8');\n _cachedGoScriptPath = scriptPath;\n }\n\n // argv-array form (no shell): avoids any quoting/metachar issues in the\n // temp script path. The target source is fed via stdin, not as an arg.\n // Resolve the Go binary via PATHEXT on Windows so ENOENT is impossible.\n const goBinary = resolveWin32Command('go');\n\n const goResult = await new Promise<{ code: number | null; stdout: string }>(\n (resolve, reject) => {\n let settled = false;\n\n const proc: ChildProcess = spawn(goBinary, ['run', scriptPath], {\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n });\n\n proc.on('error', (err) => {\n if (settled) return;\n settled = true;\n reject(err);\n });\n\n let stdout = '';\n proc.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n // Drain stderr to avoid backpressure deadlocks from Go toolchain\n // diagnostics (e.g. \"found packages \u2026\").\n proc.stderr?.resume();\n\n // Write source via stdin so `go run` receives it without touching disk\n proc.stdin?.write(content);\n proc.stdin?.end();\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n proc.kill('SIGKILL');\n reject(new Error('timeout'));\n }, 15_000);\n timer.unref?.();\n\n proc.on('close', (code) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({ code, stdout });\n });\n },\n );\n\n const { code, stdout } = goResult;\n\n if (code !== 0 || !stdout.trim()) {\n return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n }\n\n const raw = JSON.parse(stdout.trim()) as Array<{\n name: string;\n kind: string;\n line: number;\n col: number;\n signature: string;\n scope: string;\n }>;\n const symbols: IndexSymbol[] = raw.map((s) => ({\n id: 0,\n lang,\n kind: s.kind as IndexSymbol['kind'],\n name: s.name,\n file: filePath,\n line: s.line,\n col: s.col,\n signature: s.signature ?? '',\n docComment: '',\n scope: s.scope ?? '',\n text: `${s.name} ${s.signature ?? ''}`.trim(),\n }));\n return { file: filePath, lang, symbols, mtimeMs: Date.now() };\n } catch {\n return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n", "/**\n * Universal regex symbol extractor.\n *\n * Used for:\n * - languages without a first-class AST parser (Java, C++, Ruby, \u2026)\n * - fallback when a native toolchain is missing (Python without `python`,\n * Go without `go`, Rust without cargo/syn)\n *\n * Patterns are intentionally recall-oriented: better a few false positives\n * in the index than silently dropping whole packages from search/code-map.\n */\n\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolKind, SymbolLang } from './schema.js';\n\ninterface ExtractPattern {\n /** Global regex; capture group 1 must be the symbol name. */\n re: RegExp;\n kind: SymbolKind;\n}\n\n/** Shared C-like declaration patterns (C/C++/Java/C#/\u2026 approximate). */\nconst C_LIKE: ExtractPattern[] = [\n { re: /\\b(?:class|struct|enum|interface|union)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n {\n re: /\\b(?:public|private|protected|static|final|async|override|virtual|inline|export)?\\s*(?:[\\w:<>[\\]\\s*&]+)\\s+([A-Za-z_]\\w*)\\s*\\([^;{]*\\)\\s*(?:const)?\\s*[{;]/g,\n kind: 'function',\n },\n { re: /\\b(?:namespace)\\s+([A-Za-z_]\\w*)/g, kind: 'namespace' },\n];\n\nconst LANG_PATTERNS: Partial<Record<SymbolLang, ExtractPattern[]>> = {\n py: [\n { re: /^(?:async\\s+)?def\\s+([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /^class\\s+([A-Za-z_]\\w*)/gm, kind: 'class' },\n { re: /^([A-Za-z_]\\w*)\\s*=/gm, kind: 'var' },\n ],\n go: [\n { re: /^func\\s+(?:\\([^)]*\\)\\s*)?([A-Za-z_]\\w*)\\s*\\(/gm, kind: 'function' },\n { re: /^type\\s+([A-Za-z_]\\w*)\\b/gm, kind: 'type' },\n { re: /^(?:const|var)\\s+([A-Za-z_]\\w*)\\b/gm, kind: 'const' },\n { re: /^package\\s+([A-Za-z_]\\w*)/gm, kind: 'namespace' },\n ],\n rs: [\n { re: /\\bfn\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\bstruct\\s+([A-Za-z_]\\w*)/g, kind: 'struct' },\n { re: /\\benum\\s+([A-Za-z_]\\w*)/g, kind: 'enum' },\n { re: /\\btrait\\s+([A-Za-z_]\\w*)/g, kind: 'trait' },\n { re: /\\bimpl(?:\\s*<[^>]+>)?\\s+([A-Za-z_]\\w*)/g, kind: 'impl' },\n { re: /\\b(?:const|static)\\s+([A-Za-z_]\\w*)/g, kind: 'const' },\n { re: /\\bmod\\s+([A-Za-z_]\\w*)/g, kind: 'mod' },\n ],\n c: C_LIKE,\n cpp: C_LIKE,\n java: [\n { re: /\\b(?:class|interface|enum|record)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n {\n re: /\\b(?:public|private|protected|static|final|abstract|synchronized|native|default|\\s)+\\s*[\\w.<>,[\\]\\s]+\\s+([A-Za-z_]\\w*)\\s*\\(/g,\n kind: 'method',\n },\n ],\n csharp: [\n { re: /\\b(?:class|interface|struct|enum|record)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n { re: /\\bnamespace\\s+([A-Za-z_.\\w]+)/g, kind: 'namespace' },\n {\n re: /\\b(?:public|private|protected|internal|static|async|override|virtual|\\s)+\\s*[\\w.<>,[\\]\\s]+\\s+([A-Za-z_]\\w*)\\s*\\(/g,\n kind: 'method',\n },\n ],\n php: [\n { re: /\\bfunction\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\bclass\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n { re: /\\binterface\\s+([A-Za-z_]\\w*)/g, kind: 'interface' },\n { re: /\\bnamespace\\s+([A-Za-z_\\\\]+)/g, kind: 'namespace' },\n ],\n ruby: [\n { re: /^\\s*def\\s+(?:self\\.)?([A-Za-z_]\\w*[!?]?)/gm, kind: 'function' },\n { re: /^\\s*class\\s+([A-Za-z_]\\w*)/gm, kind: 'class' },\n { re: /^\\s*module\\s+([A-Za-z_]\\w*)/gm, kind: 'namespace' },\n ],\n swift: [\n { re: /\\b(?:func)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:class|struct|enum|protocol|actor)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n kotlin: [\n { re: /\\b(?:fun)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:class|interface|object|enum\\s+class|data\\s+class)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n scala: [\n { re: /\\b(?:def)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:class|object|trait|enum)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n shell: [\n { re: /^(?:function\\s+)?([A-Za-z_]\\w*)\\s*\\(\\)\\s*\\{/gm, kind: 'function' },\n { re: /^([A-Za-z_][\\w]*)\\s*\\(\\)\\s*\\{/gm, kind: 'function' },\n ],\n sql: [\n { re: /\\bCREATE\\s+(?:OR\\s+REPLACE\\s+)?(?:TABLE|VIEW|INDEX|FUNCTION|PROCEDURE|TRIGGER)\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?([A-Za-z_\"][\\w.\"]*)/gi, kind: 'type' },\n ],\n md: [\n { re: /^(#{1,6})\\s+(.+)$/gm, kind: 'namespace' },\n ],\n toml: [\n { re: /^\\[([^\\]]+)\\]/gm, kind: 'namespace' },\n ],\n html: [\n { re: /\\bid\\s*=\\s*[\"']([^\"']+)[\"']/gi, kind: 'property' },\n { re: /<(?:script|template|style)\\b/gi, kind: 'namespace' },\n ],\n css: [\n { re: /^\\s*([.#]?[A-Za-z_][\\w-]*)\\s*\\{/gm, kind: 'type' },\n { re: /@(?:keyframes|media|supports)\\s+([^{\\s]+)/g, kind: 'namespace' },\n ],\n vue: [\n { re: /\\b(?:function|const|let|var|class|export\\s+(?:default\\s+)?(?:function|class|const))\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /<(?:script|template|style)\\b/gi, kind: 'namespace' },\n ],\n svelte: [\n { re: /\\b(?:function|const|let|var|class|export\\s+(?:default\\s+)?(?:function|class|const))\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n dart: [\n { re: /\\b(?:class|enum|mixin|extension)\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n { re: /\\b([A-Za-z_]\\w*)\\s*\\([^;]*\\)\\s*(?:async\\s*)?\\{/g, kind: 'function' },\n ],\n lua: [\n { re: /\\bfunction\\s+([A-Za-z_.:]\\w*)/g, kind: 'function' },\n { re: /\\blocal\\s+function\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n r: [\n { re: /([A-Za-z.]\\w*)\\s*<-\\s*function\\s*\\(/g, kind: 'function' },\n { re: /([A-Za-z.]\\w*)\\s*=\\s*function\\s*\\(/g, kind: 'function' },\n ],\n proto: [\n { re: /\\b(?:message|service|enum)\\s+([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\brpc\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n graphql: [\n { re: /\\b(?:type|interface|enum|input|union|scalar)\\s+([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\b(?:query|mutation|subscription)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n ],\n zig: [\n { re: /\\b(?:fn|pub\\s+fn)\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /\\b(?:const|var)\\s+([A-Za-z_]\\w*)/g, kind: 'const' },\n { re: /\\b(?:struct|enum|union)\\s*\\{/g, kind: 'type' },\n ],\n elixir: [\n { re: /\\bdef(?:p|macro|macrop)?\\s+([A-Za-z_]\\w*[!?]?)/g, kind: 'function' },\n { re: /\\bdefmodule\\s+([A-Za-z_.]\\w*)/g, kind: 'namespace' },\n ],\n haskell: [\n { re: /^([A-Za-z_]\\w*)\\s*::/gm, kind: 'function' },\n { re: /\\bdata\\s+([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\btype\\s+(?:family\\s+)?([A-Za-z_]\\w*)/g, kind: 'type' },\n { re: /\\bclass\\s+([A-Za-z_]\\w*)/g, kind: 'class' },\n ],\n other: [\n { re: /^(?:export\\s+)?(?:async\\s+)?function\\s+([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /^(?:export\\s+)?class\\s+([A-Za-z_]\\w*)/gm, kind: 'class' },\n { re: /^(?:export\\s+)?(?:const|let|var)\\s+([A-Za-z_]\\w*)/gm, kind: 'const' },\n { re: /^(?:async\\s+)?def\\s+([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /^func\\s+(?:\\([^)]*\\)\\s*)?([A-Za-z_]\\w*)/gm, kind: 'function' },\n { re: /\\bfn\\s+([A-Za-z_]\\w*)/g, kind: 'function' },\n { re: /^(?:target|PHONY)\\s*:/gm, kind: 'namespace' },\n ],\n};\n\nconst KEYWORDS = new Set([\n 'if',\n 'else',\n 'for',\n 'while',\n 'switch',\n 'case',\n 'return',\n 'break',\n 'continue',\n 'new',\n 'delete',\n 'typeof',\n 'instanceof',\n 'void',\n 'null',\n 'true',\n 'false',\n 'this',\n 'super',\n 'import',\n 'export',\n 'from',\n 'as',\n 'default',\n 'public',\n 'private',\n 'protected',\n 'static',\n 'final',\n 'class',\n 'struct',\n 'enum',\n 'interface',\n 'function',\n 'def',\n 'fn',\n 'func',\n 'var',\n 'let',\n 'const',\n 'package',\n 'namespace',\n 'module',\n 'using',\n 'include',\n 'require',\n 'select',\n 'from',\n 'where',\n 'and',\n 'or',\n 'not',\n 'in',\n 'is',\n 'try',\n 'catch',\n 'finally',\n 'throw',\n 'async',\n 'await',\n 'yield',\n]);\n\nfunction patternsFor(lang: SymbolLang): ExtractPattern[] {\n return LANG_PATTERNS[lang] ?? LANG_PATTERNS.other ?? [];\n}\n\nfunction looksBinary(content: string): boolean {\n // NUL byte \u2192 almost certainly not source text\n if (content.includes('\\0')) return true;\n // High ratio of non-printable in the first 2KB\n const sample = content.slice(0, 2048);\n if (sample.length === 0) return false;\n let bad = 0;\n for (let i = 0; i < sample.length; i++) {\n const c = sample.charCodeAt(i);\n if (c === 9 || c === 10 || c === 13) continue;\n if (c < 32 || c === 127) bad++;\n }\n return bad / sample.length > 0.1;\n}\n\nfunction lineColAt(content: string, index: number): { line: number; col: number } {\n let line = 1;\n let lastNl = -1;\n for (let i = 0; i < index && i < content.length; i++) {\n if (content.charCodeAt(i) === 10) {\n line++;\n lastNl = i;\n }\n }\n return { line, col: index - lastNl };\n}\n\n/** Soft default: enough for normal sources without runaway regex on minified blobs. */\nexport const GENERIC_MAX_SYMBOLS_DEFAULT = 500;\n/** Only the leading window is scanned \u2014 huge generated files stay cheap. */\nexport const GENERIC_MAX_FILE_CHARS = 512 * 1024;\n\n/**\n * Extract symbols with language-tuned regexes. Never throws.\n * Caps results so pathological files cannot explode the index.\n */\nexport function parseGeneric(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n /** Soft cap per file (default {@link GENERIC_MAX_SYMBOLS_DEFAULT}). */\n maxSymbols?: number;\n}): FileSymbols {\n const { file, lang } = opts;\n const maxSymbols = opts.maxSymbols ?? GENERIC_MAX_SYMBOLS_DEFAULT;\n const mtimeMs = Date.now();\n\n if (!opts.content || looksBinary(opts.content)) {\n return { file, lang, symbols: [], mtimeMs };\n }\n // Bound CPU: never regex-scan multi-MB blobs in full.\n const content =\n opts.content.length > GENERIC_MAX_FILE_CHARS\n ? opts.content.slice(0, GENERIC_MAX_FILE_CHARS)\n : opts.content;\n\n const patterns = patternsFor(lang);\n const symbols: IndexSymbol[] = [];\n const seen = new Set<string>();\n\n for (const pattern of patterns) {\n // Clone flags so lastIndex never leaks across files.\n const re = new RegExp(pattern.re.source, pattern.re.flags.includes('g') ? pattern.re.flags : `${pattern.re.flags}g`);\n re.lastIndex = 0;\n for (const match of content.matchAll(re)) {\n if (symbols.length >= maxSymbols) break;\n\n let name = (match[1] ?? match[2] ?? '').trim();\n // Markdown headings put the title in group 2\n if (lang === 'md' && match[2]) name = match[2].trim();\n if (!name || name.length > 200) continue;\n // Strip markdown heading markers accidentally captured\n name = name.replace(/^#+\\s*/, '').replace(/[\"'`]/g, '');\n if (!name || KEYWORDS.has(name.toLowerCase())) continue;\n // Reject pure punctuation / numbers\n if (!/^[A-Za-z_#.@/\\w][\\w.\\-:/#!?]*$/.test(name) && lang !== 'md' && lang !== 'toml') {\n continue;\n }\n\n const { line, col } = lineColAt(content, match.index);\n const key = `${name}\\0${line}\\0${pattern.kind}`;\n if (seen.has(key)) continue;\n seen.add(key);\n\n const nl = content.indexOf('\\n', match.index);\n const lineText = content.slice(match.index, nl === -1 ? content.length : nl);\n const signature = (lineText || name).trim().slice(0, 500);\n\n symbols.push({\n id: 0,\n lang,\n kind: lang === 'md' ? 'namespace' : pattern.kind,\n name: name.slice(0, 200),\n file,\n line,\n col,\n signature,\n docComment: '',\n scope: '',\n text: `${name} ${signature}`.trim().slice(0, 1000),\n });\n }\n if (symbols.length >= maxSymbols) break;\n }\n\n return { file, lang, symbols, mtimeMs };\n}\n\n/** Async wrapper matching other parser entrypoints. */\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n return parseGeneric(opts);\n}\n", "/**\n * Python source symbol extraction using the `ast` module.\n *\n * Spawns a `python -c` child process that parses the file with Python's `ast`\n * module and emits JSON. Falls back to empty results on any error.\n *\n * Extracts: class, function, async function, const, var, import, import_from\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { resolveWin32Command } from '../_win32-resolve.js';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { parseGeneric } from './generic-parser.js';\nimport { withSpawnGate } from './spawn-gate.js';\n\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Prefer Python's `ast` when a runtime is available. When Python is missing\n * or the spawn fails, fall back to the generic regex extractor so `.py` files\n * still enter the index instead of being silently empty.\n *\n * Syntax errors from a working Python still return zero symbols (ast cannot\n * recover) \u2014 that is intentional correctness, not a gap in coverage.\n */\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n try {\n // Serialize python child processes process-wide (CPU/spawn cimrili\u011Fi).\n const native = await withSpawnGate(() => syncPyParse(file, content, lang));\n if (native !== null) return native;\n } catch {\n /* fall through to generic */\n }\n return parseGeneric({ file, content, lang: lang === 'py' ? 'py' : lang });\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Inline Python parser script \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst PY_PARSE_SCRIPT = `import ast, json, sys, os\n\ndef get_name(node):\n if isinstance(node, ast.Name):\n return node.id\n elif isinstance(node, ast.Attribute):\n return get_name(node.value) + \".\" + node.attr\n elif isinstance(node, ast.Subscript):\n return get_name(node.value)\n elif isinstance(node, ast.Call):\n return get_name(node.func)\n elif isinstance(node, ast.Constant):\n return str(node.value)\n return \"\"\n\ndef get_decorators(node):\n decs = []\n for dec in node.decorator_list:\n decs.append(get_name(dec))\n return decs\n\ndef get_bases(node):\n bases = []\n for base in node.bases:\n bases.append(get_name(base))\n return bases\n\ndef get_args(args):\n parts = []\n for arg in args.args:\n parts.append(arg.arg)\n return \", \".join(parts)\n\ndef get_returns(node):\n if node.returns is None:\n return \"\"\n return get_name(node.returns)\n\nclass Sym:\n def __init__(self, name, kind, line, col, signature, scope):\n self.name = name\n self.kind = kind\n self.line = line\n self.col = col\n self.signature = signature\n self.scope = scope\n def to_dict(self):\n return {\n \"name\": self.name,\n \"kind\": self.kind,\n \"line\": self.line,\n \"col\": self.col,\n \"signature\": self.signature,\n \"scope\": self.scope,\n }\n\ndef is_private(name):\n return name.startswith(\"__\") and not name.endswith(\"__\")\n\nsyms = []\nerrors = []\n\ntry:\n source = sys.stdin.read()\n tree = ast.parse(source, filename=sys.argv[1])\nexcept Exception as e:\n errors.append(str(e))\n print(\"[]\")\n sys.exit(0)\n\n# Module-level scope\nmodule_scope = os.path.basename(sys.argv[1])[:-3] # strip .py\n\nclass ModuleVisitor(ast.NodeVisitor):\n def __init__(self):\n self.scope_stack = [module_scope]\n\n def visit_ClassDef(self, node):\n bases = get_bases(node)\n decs = get_decorators(node)\n sig = \"class \" + node.name\n if bases:\n sig += \"(\" + \", \".join(bases) + \")\"\n sig += \": ...\"\n syms.append(Sym(\n name=node.name,\n kind=\"class\",\n line=node.lineno,\n col=node.col_offset,\n signature=sig,\n scope=\".\".join(self.scope_stack) + \".\" + node.name,\n ))\n self.scope_stack.append(node.name)\n self.generic_visit(node)\n self.scope_stack.pop()\n\n def visit_FunctionDef(self, node):\n decs = get_decorators(node)\n args = get_args(node.args)\n returns = get_returns(node)\n is_async = isinstance(node, ast.AsyncFunctionDef)\n\n kind = \"function\"\n prefix = \"def \"\n if decs:\n for d in decs:\n if d.endswith(\".staticmethod\"):\n kind = \"staticmethod\"\n elif d.endswith(\".classmethod\"):\n kind = \"classmethod\"\n elif d == \"property\":\n kind = \"property\"\n\n if is_async:\n kind = \"async_\" + kind\n\n sig = f\"{prefix}{node.name}({args})\"\n if returns:\n sig += f\" -> {returns}\"\n scope = \".\".join(self.scope_stack) + \".\" + node.name\n\n syms.append(Sym(\n name=node.name,\n kind=kind,\n line=node.lineno,\n col=node.col_offset,\n signature=sig,\n scope=scope,\n ))\n # Don't descend into function bodies to avoid local symbols\n # self.generic_visit(node)\n\n def visit_AsyncFunctionDef(self, node):\n # Treat as function\n self.visit_FunctionDef(node)\n\n def visit_Assign(self, node):\n for target in node.targets:\n if isinstance(target, ast.Name):\n name = target.id\n if is_private(name):\n continue\n # Infer constness from UPPER_CASE naming\n kind = \"const\" if name.isupper() else \"var\"\n col = target.col_offset if hasattr(target, 'col_offset') else 0\n syms.append(Sym(\n name=name,\n kind=kind,\n line=node.lineno,\n col=col,\n signature=f\"{name} = ...\",\n scope=\".\".join(self.scope_stack),\n ))\n\n def visit_AnnAssign(self, node):\n if isinstance(node.target, ast.Name):\n name = node.target.id\n if is_private(name):\n return\n kind = \"const\" if name.isupper() else \"var\"\n col = node.target.col_offset if hasattr(node.target, 'col_offset') else 0\n sig = f\"{name}: {get_name(node.annotation)}\"\n if node.value:\n sig += \" = ...\"\n syms.append(Sym(\n name=name,\n kind=kind,\n line=node.lineno,\n col=col,\n signature=sig,\n scope=\".\".join(self.scope_stack),\n ))\n\n def visit_Import(self, node):\n for alias in node.names:\n name = alias.asname or alias.name\n syms.append(Sym(\n name=name,\n kind=\"import\",\n line=node.lineno,\n col=node.col_offset,\n signature=f\"import {alias.name}\",\n scope=\".\".join(self.scope_stack),\n ))\n\n def visit_ImportFrom(self, node):\n module = node.module or \"\"\n for alias in node.names:\n name = alias.asname or alias.name\n syms.append(Sym(\n name=name,\n kind=\"import\",\n line=node.lineno,\n col=node.col_offset,\n signature=f\"from {module} import {alias.name}\",\n scope=\".\".join(self.scope_stack),\n ))\n\nvisitor = ModuleVisitor()\nvisitor.visit(tree)\n\nprint(json.dumps([s.to_dict() for s in syms]))\n`;\n\n// \u2500\u2500\u2500 Synchronous Python parse via child process \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Cross-platform Python binary resolver.\n *\n * Windows: walks PATHEXT via `resolveWin32Command` (handles .exe/.cmd/.bat).\n * A match means a real file on disk \u2014 return it immediately.\n * macOS / Linux: `resolveWin32Command` is a pass-through. We asynchronously\n * verify each candidate with `--version`, cached for the process lifetime.\n *\n * Candidates in priority order:\n * Windows: python3 \u2192 python \u2192 py (Python launcher)\n * Unix: python3 \u2192 python\n */\nasync function resolvePython(): Promise<string | null> {\n\tconst candidates = process.platform === 'win32'\n\t\t? ['python3', 'python', 'py']\n\t\t: ['python3', 'python'];\n\tfor (const name of candidates) {\n\t\tconst resolved = resolveWin32Command(name);\n\t\t// On Windows: verify even if resolveWin32Command found a\n\t\t// file \u2014 the WindowsApps redirector stub (python3.exe) passes the\n\t\t// accessSync check but exits with code 9009 (app not found).\n\t\tif (!(await commandIsAvailable(resolved))) continue;\n\t\treturn resolved;\n\t}\n\treturn null;\n}\n\nfunction commandIsAvailable(command: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tlet settled = false;\n\t\tconst proc = spawn(command, ['--version'], {\n\t\t\tstdio: 'ignore',\n\t\t\twindowsHide: true,\n\t\t});\n\t\tconst finish = (available: boolean) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolve(available);\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tproc.kill('SIGKILL');\n\t\t\tfinish(false);\n\t\t}, 5_000);\n\t\ttimer.unref?.();\n\t\tproc.once('error', () => finish(false));\n\t\tproc.once('close', (code) => finish(code === 0));\n\t});\n}\n\n/**\n * Spawn the Python parser child process with proper error handling.\n *\n * Returns a promise that resolves to { code, stdout } or rejects with an\n * Error (ENOENT, timeout, spawn error) that the caller converts to empty\n * results. The 'error' event listener is critical: without it, a spawn\n * ENOENT on Windows crashes as an unhandled exception because\n * ChildProcess emits 'error' asynchronously and the Promise.race only\n * listens on 'close'.\n */\nfunction spawnPyParser(\n\tpyBinary: string,\n\tscriptPath: string,\n\tfilePath: string,\n\tcontent: string,\n): Promise<{ code: number | null; stdout: string }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\n\t\tconst proc: ChildProcess = spawn(pyBinary, [scriptPath, filePath], {\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\t// Mandatory: catch ENOENT / permission-denied / spawn failures.\n\t\tproc.on('error', (err) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\treject(err);\n\t\t});\n\n\t\t// Write source content via stdin so the child doesn't reopen the file.\n\t\tproc.stdin?.write(content);\n\t\tproc.stdin?.end();\n\n\t\tlet stdout = '';\n\t\tproc.stdout?.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });\n\n\t\t// Discard stderr to avoid backpressure deadlocks when Python emits\n\t\t// warnings (e.g. deprecation notices).\n\t\tproc.stderr?.resume();\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tproc.kill('SIGKILL');\n\t\t\treject(new Error('timeout'));\n\t\t}, 15_000);\n\t\ttimer.unref?.();\n\n\t\tproc.on('close', (code) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ code, stdout });\n\t\t});\n\t});\n}\n\n// Cache the temp script path + resolved Python binary so we don't rewrite\n// or re-resolve on every file.\nlet _cachedScriptPath: string | null = null;\nlet cachedPyBinary: Promise<string | null> | undefined;\n\n/**\n * Run the real Python AST parser.\n * - `null` \u2192 Python unavailable / spawn failed \u2192 caller should use generic fallback\n * - `FileSymbols` \u2192 Python ran (even if the file was invalid \u2192 empty symbols)\n */\nasync function syncPyParse(\n\tfilePath: string,\n\tcontent: string,\n\tlang: SymbolLang,\n): Promise<FileSymbols | null> {\n\ttry {\n\t\t// Write the parser script once per process \u2014 not per file.\n\t\t// Passing the whole 200-line program via `python -c \"...\"` breaks\n\t\t// under cmd.exe on Windows (embedded newlines truncate the command).\n\t\t// A real file sidesteps all quoting and can be reused across calls.\n\t\tif (!_cachedScriptPath) {\n\t\t\tconst tmpDir = path.join(os.tmpdir(), 'ws-py-parse');\n\t\t\tawait fs.mkdir(tmpDir, { recursive: true });\n\t\t\t_cachedScriptPath = path.join(tmpDir, 'parse.py');\n\t\t\tawait fs.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, 'utf8');\n\t\t}\n\n\t\t// Resolve Python binary once (expensive: walks PATH on Windows).\n\t\tcachedPyBinary ??= resolvePython();\n\t\tconst pyBinary = await cachedPyBinary;\n\t\tif (!pyBinary) return null;\n\n\t\t// argv-array form: no shell, so a hostile filename cannot inject commands.\n\t\t// Content is piped via stdin \u2014 avoids a second file read in the child.\n\t\tconst { code, stdout } = await spawnPyParser(\n\t\t\tpyBinary,\n\t\t\t_cachedScriptPath,\n\t\t\tfilePath,\n\t\t\tcontent,\n\t\t);\n\n\t\tif (code !== 0 || !stdout.trim()) {\n\t\t\t// Python ran but AST parse failed (syntax error) \u2014 empty, not fallback.\n\t\t\treturn { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n\t\t}\n\n\t\tconst raw = JSON.parse(stdout.trim()) as Array<{\n\t\t\tname: string;\n\t\t\tkind: string;\n\t\t\tline: number;\n\t\t\tcol: number;\n\t\t\tsignature: string;\n\t\t\tscope: string;\n\t\t}>;\n\t\tconst symbols: IndexSymbol[] = raw.map((s) => ({\n\t\t\tid: 0,\n\t\t\tlang,\n\t\t\tkind: s.kind as IndexSymbol['kind'],\n\t\t\tname: s.name,\n\t\t\tfile: filePath,\n\t\t\tline: s.line,\n\t\t\tcol: s.col,\n\t\t\tsignature: s.signature ?? '',\n\t\t\tdocComment: '',\n\t\t\tscope: s.scope ?? '',\n\t\t\ttext: `${s.name} ${s.signature ?? ''}`.trim(),\n\t\t}));\n\t\treturn { file: filePath, lang, symbols, mtimeMs: Date.now() };\n\t} catch {\n\t\t// Spawn/IO failure \u2192 generic fallback.\n\t\treturn null;\n\t}\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * Rust source symbol extraction.\n *\n * Tries to use the native `syn` crate via a cargo subproject (tools/syn-parser/).\n * Falls back to a robust regex-based extractor when cargo/syn is not available.\n *\n * The regex fallback extracts: fn, struct, enum, trait, impl, type, const, static, mod\n */\n\nimport { execFile, spawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveWin32Command } from '../_win32-resolve.js';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { withSpawnGate } from './spawn-gate.js';\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n // Try native parser first, fall back to regex\n const nativeAvailable = await checkNativeParser();\n if (nativeAvailable) {\n const result = await withSpawnGate(() => tryNativeParse(file, content));\n if (result) return result;\n }\n\n return regexParse({ file, content, lang });\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Native parser (syn) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Cache the native-parser availability check so we don't spawn `rustc` and\n// `cargo metadata` on every file. The result is constant for the process\n// lifetime \u2014 the toolchain either exists or it doesn't.\nlet nativeParserAvailability: Promise<boolean> | undefined;\n\nfunction probe(command: string, args: string[]): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(command, args, { timeout: 10_000, windowsHide: true }, (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n}\n\nfunction checkNativeParser(): Promise<boolean> {\n nativeParserAvailability ??= (async () => {\n try {\n await probe('rustc', ['--version']);\n // Check if our syn-parser crate is available. argv-array form (no shell)\n // so a cwd path containing spaces or shell metacharacters can't break out.\n const toolsDir = path.join(process.cwd(), 'tools');\n await probe(\n 'cargo',\n [\n 'metadata',\n '--no-deps',\n '--format-version',\n '1',\n '--manifest-path',\n path.join(toolsDir, 'Cargo.toml'),\n ],\n );\n return true;\n } catch {\n return false;\n }\n })();\n return nativeParserAvailability;\n}\n\nasync function tryNativeParse(file: string, content: string): Promise<FileSymbols | null> {\n try {\n const toolsDir = path.join(process.cwd(), 'tools');\n const crateDir = path.join(toolsDir, 'syn-parser');\n\n // Write source to temp file for cargo to read (async \u2014 non-blocking)\n const tmpFile = path.join(crateDir, 'src', 'input.rs');\n await fs.writeFile(tmpFile, content, 'utf8');\n\n // Use spawn for full async control with timeout via Promise.race + setTimeout kill\n // Resolve via PATHEXT on Windows so ENOENT is impossible (cargo is a .cmd wrapper).\n const cargoBinary = resolveWin32Command('cargo');\n\n const result = await new Promise<{ code: number | null; stdout: string }>(\n (resolve, reject) => {\n let settled = false;\n\n const proc: ChildProcessWithoutNullStreams = spawn(\n cargoBinary,\n ['run', '--manifest-path', path.join(toolsDir, 'Cargo.toml')],\n {\n cwd: process.cwd(),\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n },\n );\n\n proc.on('error', (err) => {\n if (settled) return;\n settled = true;\n reject(err);\n });\n\n let stdout = '';\n proc.stdout?.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });\n proc.stderr?.resume();\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n proc.kill('SIGKILL');\n reject(new Error('timeout'));\n }, 15_000);\n timer.unref?.();\n\n proc.on('close', (c: number | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({ code: c, stdout });\n });\n },\n );\n\n const { code, stdout } = result;\n\n if (code === 0 && stdout.trim()) {\n const symbols: IndexSymbol[] = JSON.parse(stdout.trim());\n return {\n file,\n lang: 'rs',\n symbols: symbols.map((s) => ({ ...s, id: 0, lang: 'rs' as SymbolLang })),\n mtimeMs: Date.now(),\n };\n }\n } catch {\n // Fall through to regex\n }\n return null;\n}\n\n// \u2500\u2500\u2500 Regex fallback parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface RustPattern {\n regex: RegExp;\n kind: IndexSymbol['kind'];\n}\n\nconst RS_PATTERNS: RustPattern[] = [\n { regex: /fn\\s+(\\w+)\\s*\\([^)]*\\)/g, kind: 'function' },\n { regex: /struct\\s+(\\w+)/g, kind: 'struct' },\n { regex: /enum\\s+(\\w+)/g, kind: 'enum' },\n { regex: /trait\\s+(\\w+)/g, kind: 'trait' },\n { regex: /impl\\s+(?:<[^>]+>)?(\\w+)/g, kind: 'impl' },\n { regex: /type\\s+(\\w+)\\s*=/g, kind: 'type' },\n { regex: /const\\s+(\\w+)/g, kind: 'const' },\n { regex: /static\\s+(\\w+)/g, kind: 'static' },\n { regex: /mod\\s+(\\w+)/g, kind: 'mod' },\n];\n\nfunction regexParse(opts: { file: string; content: string; lang: SymbolLang }): FileSymbols {\n const { file, content, lang } = opts;\n const symbols: IndexSymbol[] = [];\n const lines = content.split('\\n');\n\n // Build line offset map\n const lineOffsets: number[] = [0];\n for (let i = 0; i < lines.length; i++) {\n lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);\n }\n\n function lineFromOffset(offset: number): number {\n let lo = 0;\n let hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1; // 1-based\n }\n\n function extractDeclaration(lineIdx: number, _match: RegExpExecArray): string {\n const line = lines[lineIdx] ?? '';\n return line.trim().slice(0, 500);\n }\n\n for (const pattern of RS_PATTERNS) {\n pattern.regex.lastIndex = 0;\n for (\n let match = pattern.regex.exec(content);\n match !== null;\n match = pattern.regex.exec(content)\n ) {\n const name = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n const lineIdx = line - 1;\n const signature = extractDeclaration(lineIdx, match);\n\n symbols.push({\n id: 0,\n lang,\n kind: pattern.kind,\n name,\n file,\n line,\n col,\n signature,\n docComment: '',\n scope: '',\n text: `${name} ${signature}`.trim(),\n });\n }\n }\n\n // Deduplicate by name+line\n const seen = new Set<string>();\n const deduped = symbols.filter((s) => {\n const key = `${s.name}:${s.line}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n return { file, lang, symbols: deduped, mtimeMs: Date.now() };\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * JSON file symbol extraction.\n *\n * Extracts top-level keys as \"symbols\" with kind `property`.\n * Special handling for:\n * - package.json: scripts, dependencies, devDependencies \u2192 `const`\n * - tsconfig.json: compilerOptions keys \u2192 `property`\n * - JSON Schema / OpenAPI: $schema, $id, $ref \u2192 `schema`\n * - Root object itself \u2192 kind `object`\n *\n * Uses regex-based extraction for speed and zero dependencies.\n */\n\nimport * as path from 'node:path';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): FileSymbols {\n const { file, content, lang } = opts;\n\n try {\n return regexParse({ file, content, lang });\n } catch {\n /* v8 ignore next -- regexParse is pure regex/string work; the catch is a defensive fallback. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Regex parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\n/**\n * Extract key-value pairs from JSON content using regex.\n * Handles: \"key\": value, arrays with keyed objects, nested objects (depth \u2264 3).\n */\nfunction regexParse(opts: { file: string; content: string; lang: SymbolLang }): FileSymbols {\n const { file, content, lang } = opts;\n const symbols: IndexSymbol[] = [];\n const basename = path.basename(file).toLowerCase();\n\n const isPackageJson = basename === 'package.json';\n const isTsconfig = basename === 'tsconfig.json' || basename === 'tsconfig.build.json';\n const isJsonSchema =\n content.includes('$schema') || content.includes('$id') || content.includes('$ref');\n const isOpenApi = content.includes('openapi') || content.includes('swagger');\n\n const lines = content.split('\\n');\n\n // Build line offset map\n const lineOffsets: number[] = [0];\n for (let i = 0; i < lines.length; i++) {\n lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);\n }\n\n function lineFromOffset(offset: number): number {\n let lo = 0;\n let hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n }\n\n // Root object symbol\n const rootMatch = content.match(/^\\s*\\{/m);\n if (rootMatch) {\n const offset = expectDefined(rootMatch.index);\n const line = lineFromOffset(offset);\n symbols.push(\n makeSymbol({\n name: path.basename(file),\n kind: 'object',\n line,\n col: 0,\n signature: `\"${path.basename(file)}\" = { ... }`,\n file,\n lang,\n }),\n );\n }\n\n // Extract top-level keys\n const topLevelKeyRegex = /^\\s*\"([^\"]+)\"\\s*:/gm;\n for (\n let match = topLevelKeyRegex.exec(content);\n match !== null;\n match = topLevelKeyRegex.exec(content)\n ) {\n const key = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n\n let kind: IndexSymbol['kind'] = 'property';\n let signature = `\"${key}\": ...\"`;\n\n // Special casing for known file types\n if (isPackageJson) {\n if (\n key === 'scripts' ||\n key === 'dependencies' ||\n key === 'devDependencies' ||\n key === 'peerDependencies' ||\n key === 'optionalDependencies'\n ) {\n kind = 'const';\n signature = `\"${key}\": { ... }`;\n }\n } else if (isTsconfig) {\n if (key === 'compilerOptions') {\n kind = 'property';\n signature = `\"compilerOptions\": { ... }`;\n }\n }\n\n // JSON Schema / OpenAPI special keys\n if (isJsonSchema || isOpenApi) {\n if (key === '$schema' || key === '$id') {\n kind = 'schema';\n signature = `\"${key}\": \"...\"`;\n } else if (key === '$ref') {\n kind = 'schema';\n signature = `\"$ref\": \"...\"`;\n }\n }\n\n symbols.push(\n makeSymbol({\n name: key,\n kind,\n line,\n col,\n signature,\n file,\n lang,\n }),\n );\n\n // For package.json, also extract individual scripts as 'function'\n if (isPackageJson && key === 'scripts') {\n extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset);\n }\n\n // For tsconfig.json compilerOptions, extract nested keys\n if (isTsconfig && key === 'compilerOptions') {\n extractCompilerOptions(content, symbols, file, lang, lineOffsets, line, lineFromOffset);\n }\n }\n\n // Extract JSON Schema $defs or definitions\n const defsRegex = /\"\\$defs\"\\s*:|\"\\$defs\"\\s*:/g;\n const defsMatch = defsRegex.exec(content);\n if (defsMatch !== null) {\n const offset = expectDefined(defsMatch.index);\n const line = lineFromOffset(offset);\n symbols.push(\n makeSymbol({\n name: '$defs',\n kind: 'property',\n line,\n col: offset - (lineOffsets[line - 1] ?? 0),\n signature: '\"$defs\": { ... }',\n file,\n lang,\n }),\n );\n }\n\n // Extract definitions (OpenAPI components, JSON Schema definitions)\n const defsPatterns = [\n /\"\\$defs\"\\s*:/g,\n /\"definitions\"\\s*:/g,\n /\"components\"\\s*:/g,\n /\"schemas\"\\s*:/g,\n ];\n for (const pat of defsPatterns) {\n pat.lastIndex = 0;\n for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const key = match[0]?.match(/\"([^\"]+)\"/)?.[1] ?? expectDefined(match[0]);\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'property',\n line,\n col: offset - (lineOffsets[line - 1] ?? 0),\n signature: `\"${key}\": { ... }`,\n file,\n lang,\n }),\n );\n }\n }\n\n return { file, lang, symbols, mtimeMs: Date.now() };\n}\n\nfunction extractPackageScripts(\n content: string,\n symbols: IndexSymbol[],\n file: string,\n lang: SymbolLang,\n lineOffsets: number[],\n lineFromOffset: (offset: number) => number,\n): void {\n // Find the \"scripts\": { ... } block and extract each script key\n const scriptsBlockRegex = /\"scripts\"\\s*:\\s*\\{([^}]+)\\}/g;\n for (\n let match = scriptsBlockRegex.exec(content);\n match !== null;\n match = scriptsBlockRegex.exec(content)\n ) {\n const blockContent = expectDefined(match[0]);\n const blockOffset = (match.index ?? 0);\n\n // Extract each \"key\" inside the block (simple approach)\n const scriptKeyRegex = /\"(\\w[\\w-]*)\"\\s*:/g;\n for (\n let scriptMatch = scriptKeyRegex.exec(blockContent);\n scriptMatch !== null;\n scriptMatch = scriptKeyRegex.exec(blockContent)\n ) {\n const key = expectDefined(scriptMatch[1]);\n const keyOffset = blockOffset + expectDefined(scriptMatch.index);\n const line = lineFromOffset(keyOffset);\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'function',\n line,\n col: keyOffset - (lineOffsets[line - 1] ?? 0),\n signature: `\"${key}\": \"...\"`,\n file,\n lang,\n }),\n );\n }\n }\n}\n\nfunction extractCompilerOptions(\n content: string,\n symbols: IndexSymbol[],\n file: string,\n lang: SymbolLang,\n lineOffsets: number[],\n parentLine: number,\n lineFromOffset: (offset: number) => number,\n): void {\n // Find the \"compilerOptions\": { ... } block\n const optsBlockRegex = /\"compilerOptions\"\\s*:\\s*\\{([^}]+)\\}/g;\n for (\n let match = optsBlockRegex.exec(content);\n match !== null;\n match = optsBlockRegex.exec(content)\n ) {\n const blockContent = expectDefined(match[0]);\n const blockOffset = (match.index ?? 0);\n\n // Extract nested key inside compilerOptions (up to depth 1)\n const optKeyRegex = /\"(\\w[\\w]*)\"\\s*:/g;\n for (\n let optMatch = optKeyRegex.exec(blockContent);\n optMatch !== null;\n optMatch = optKeyRegex.exec(blockContent)\n ) {\n const key = expectDefined(optMatch[1]);\n const keyOffset = blockOffset + expectDefined(optMatch.index);\n const line = lineFromOffset(keyOffset);\n if (line <= parentLine) continue; // Skip top-level (already captured)\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'property',\n line,\n col: keyOffset - (lineOffsets[line - 1] ?? 0),\n signature: `\"${key}\": ...`,\n file,\n lang,\n }),\n );\n }\n }\n}\n\nfunction makeSymbol(opts: {\n name: string;\n kind: IndexSymbol['kind'];\n line: number;\n col: number;\n signature: string;\n file: string;\n lang: SymbolLang;\n}): IndexSymbol {\n return {\n id: 0,\n lang: opts.lang,\n kind: opts.kind,\n name: opts.name,\n file: opts.file,\n line: opts.line,\n col: opts.col,\n signature: opts.signature,\n docComment: '',\n scope: '',\n text: `${opts.name} ${opts.signature}`.trim(),\n };\n}\n", "import { expectDefined, truncate } from '@wrongstack/core/utils';\nimport type { FileSymbols, Symbol as IndexSymbol, SymbolLang } from './schema.js';\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function parseSymbols(opts: {\n file: string;\n content: string;\n lang: SymbolLang;\n}): FileSymbols {\n const { file, content, lang } = opts;\n\n try {\n return regexParse({ file, content, lang });\n } catch {\n /* v8 ignore next -- regexParse is pure regex/string work; the catch is a defensive fallback. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n\nexport { detectLang } from './languages.js';\n\n// \u2500\u2500\u2500 Regex parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction regexParse(opts: { file: string; content: string; lang: SymbolLang }): FileSymbols {\n const { file, content, lang } = opts;\n const symbols: IndexSymbol[] = [];\n\n const lines = content.split('\\n');\n\n // Build line offset map for accurate line/col\n const lineOffsets: number[] = [0];\n for (let i = 0; i < lines.length; i++) {\n lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);\n }\n\n function lineFromOffset(offset: number): number {\n let lo = 0;\n let hi = lineOffsets.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n }\n\n // \u2500\u2500 1. Anchors and aliases \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // &anchor_name\n const anchorRegex = /&(\\w[\\w-]*)/g;\n for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {\n const name = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n symbols.push(\n makeSymbol({\n name,\n kind: 'const',\n line,\n col,\n signature: `&${name}`,\n file,\n lang,\n }),\n );\n }\n\n // *alias_name\n const aliasRegex = /\\*(\\w[\\w-]*)/g;\n for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {\n const name = expectDefined(match[1]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n symbols.push(\n makeSymbol({\n name,\n kind: 'const',\n line,\n col,\n signature: `*${name}`,\n file,\n lang,\n }),\n );\n }\n\n // \u2500\u2500 2. Top-level and nested key: value pairs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Matches `key: value` (but not block scalars or document markers)\n // Uses negative lookbehind and context to avoid false positives\n const kvRegex = /^(\\s*)([^:#\\s][^:#\\s]*)\\s*:/gm;\n for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {\n const indent = match[1]?.length ?? 0;\n const key = match[2];\n /* v8 ignore next -- the capture group always matches \u22651 char, so key is never empty; defensive. */\n if (!key) continue;\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n\n // Skip block scalar indicators (| or > at column 0 with key name before :)\n const lineContent = lines[line - 1] ?? '';\n if (/^[|&>]/.test(lineContent.trim())) continue;\n // Skip YAML document markers\n if (key === '---' || key === '...') continue;\n // Skip keys that are clearly part of a string value (unusual indent)\n if (indent > 12) continue;\n\n const value = extractValue(content, (match.index ?? 0));\n const kind: IndexSymbol['kind'] = isScalar(value) ? 'literal' : 'property';\n const signature = `${key}: ${truncate(value, 60)}`;\n\n symbols.push(makeSymbol({ name: key, kind, line, col, signature, file, lang }));\n }\n\n // \u2500\u2500 3. List item keys \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // `- key: value` (list item that is a keyed object)\n const listItemRegex = /^-(\\s+)([^:#\\s][^:#\\s]*)\\s*:/gm;\n for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {\n const key = expectDefined(match[2]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n const value = extractValue(content, offset + match[0]?.length);\n const kind: IndexSymbol['kind'] = isScalar(value) ? 'literal' : 'property';\n symbols.push(\n makeSymbol({\n name: key,\n kind,\n line,\n col,\n signature: `- ${key}: ${truncate(value, 60)}`,\n file,\n lang,\n }),\n );\n }\n\n // \u2500\u2500 4. Block scalar keys (key: | or key: >) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const blockScalarRegex = /^(\\s*)([^:#\\s][^:#\\s]*)\\s*:\\s*[|>](\\s|$)/gm;\n for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {\n const key = expectDefined(match[2]);\n const offset = (match.index ?? 0);\n const line = lineFromOffset(offset);\n const col = offset - (lineOffsets[line - 1] ?? 0);\n symbols.push(\n makeSymbol({\n name: key,\n kind: 'property',\n line,\n col,\n signature: `${key}: | ...`,\n file,\n lang,\n }),\n );\n }\n\n return { file, lang, symbols, mtimeMs: Date.now() };\n}\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractValue(content: string, afterColonOffset: number): string {\n // Get the rest of the line after the colon\n const lineEnd = content.indexOf('\\n', afterColonOffset);\n const rest = content.slice(afterColonOffset, lineEnd < 0 ? undefined : lineEnd);\n return rest.trim();\n}\n\nfunction isScalar(value: string): boolean {\n if (!value) return false;\n // Numbers, booleans, null, quoted strings\n if (/^-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?$/.test(value)) return true;\n if (/^(true|false|null|undefined)$/i.test(value)) return true;\n if (/^'[^']*'$/.test(value) || /^\"[^\"]*\"$/.test(value)) return true;\n return false;\n}\n\nfunction makeSymbol(opts: {\n name: string;\n kind: IndexSymbol['kind'];\n line: number;\n col: number;\n signature: string;\n file: string;\n lang: SymbolLang;\n}): IndexSymbol {\n return {\n id: 0,\n lang: opts.lang,\n kind: opts.kind,\n name: opts.name,\n file: opts.file,\n line: opts.line,\n col: opts.col,\n signature: opts.signature,\n docComment: '',\n scope: '',\n text: `${opts.name} ${opts.signature}`.trim(),\n };\n}\n", "import * as fs from 'node:fs/promises';\nimport { FsError, type Tool, ToolValidationError } from '@wrongstack/core/types';\nimport { toErrorMessage } from '@wrongstack/core/utils';\nimport { isBinaryBuffer, safeResolveReal, sha256hex } from './_util.js';\nimport { getIndexState, searchCodebaseIndex } from './codebase-index/background-indexer.js';\nimport type { SymbolKind } from './codebase-index/schema.js';\nimport { codebaseIndexDirOverride } from './codebase-index/writer-helpers.js';\n\n/**\n * Meta key for advanced mode. When `true` in `ctx.meta`, the read tool\n * automatically injects codebase-index symbols for the file being read\n * as a structured `symbols` field. Set via:\n * ctx.meta['tools.read.advancedMode'] = true\n * A per-call `includeSymbols` parameter overrides this flag.\n */\nconst ADVANCED_MODE_META_KEY = 'tools.read.advancedMode';\n\ninterface ReadInput {\n path: string;\n offset?: number | undefined;\n limit?: number | undefined;\n mode?: 'content' | 'summary' | undefined;\n /**\n * When true, include the codebase-index symbol list for this file\n * as a structured `symbols` field in the result. Overrides the\n * advanced-mode meta flag (`ctx.meta['tools.read.advancedMode']`)\n * per-call when explicitly set. When omitted, the meta flag governs.\n */\n includeSymbols?: boolean | undefined;\n}\n\n/**\n * A single indexed code symbol returned alongside file content when\n * advanced mode is on or `includeSymbols` is set. The LLM must treat\n * this as a symbol listing \u2014 not as file content.\n */\ninterface SymbolEntry {\n /** Symbol name (e.g. myFunction, MyClass). */\n name: string;\n /** Kind of symbol (function, class, interface, const, etc.). */\n kind: SymbolKind;\n /** 1-based declaration line in the file. */\n line: number;\n /** 0-based column offset. */\n col: number;\n /** Full signature or declaration text. */\n signature: string;\n}\n\ninterface ReadOutput {\n text: string;\n total_lines: number;\n encoding: string;\n truncated: boolean;\n cached?: boolean | undefined;\n note?: string | undefined;\n /**\n * Codebase-index symbols for this file, included when advanced mode\n * is active or `includeSymbols` is set. One entry per indexed symbol,\n * sorted by line number. This is a symbol listing \u2014 NOT file content.\n */\n symbols?: SymbolEntry[] | undefined;\n}\n\nconst MAX_BYTES = 5 * 1024 * 1024;\n\nexport const readTool: Tool<ReadInput, ReadOutput> = {\n name: 'read',\n category: 'Filesystem',\n description:\n 'Read the contents of a file with line numbers. This is the primary way to inspect source code, configuration, or any text file before making changes. ' +\n 'Lines are returned 1-indexed with a ` N| ` prefix for easy reference in edits. ' +\n 'When advanced mode is on or `includeSymbols` is set, the result also includes a `symbols` field ' +\n 'listing codebase-index symbol names, kinds, and line numbers for the file (not file content).',\n usageHint:\n 'FOUNDATIONAL TOOL \u2014 call this before almost any edit operation.\\n\\n' +\n 'Best practices:\\n' +\n '- Always read a file before using `edit`, `replace`, or `write` on it (the system often requires it for safety).\\n' +\n '- Use `offset` + `limit` for very large files instead of reading everything at once.\\n' +\n '- Default limit is generous (2000 lines) but can be increased.\\n' +\n '- The output format is designed to be directly usable as context for `edit` operations.\\n' +\n '- Set `includeSymbols: true` to also receive the codebase-index symbol listing for the file.\\n' +\n \"- Enable advanced mode (`ctx.meta['tools.read.advancedMode'] = true`) to auto-inject symbols on every read.\",\n selection: {\n doNotUseWhen: 'you need to search many files for matching content.',\n useInstead: ['grep'],\n },\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n icon: 'file',\n maxOutputBytes: 262_144,\n timeoutMs: 5_000,\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Path to the file (relative to project root or absolute within project).',\n },\n offset: {\n type: 'integer',\n description: '1-based starting line number. Use together with `limit` for large files.',\n },\n limit: {\n type: 'integer',\n description: 'Maximum number of lines to return (default is 2000).',\n },\n mode: {\n type: 'string',\n enum: ['content', 'summary'],\n description:\n 'Return full line-numbered content (default) or a compact file summary with imports/exports/symbols.',\n },\n includeSymbols: {\n type: 'boolean',\n description:\n 'When true, include the codebase-index symbol list for this file as a structured `symbols` field ' +\n 'in the result. Overrides the advanced-mode meta flag per-call.',\n },\n },\n required: ['path'],\n },\n async execute(input, ctx, execOpts) {\n if (!input?.path) {\n throw new ToolValidationError({\n message: 'read: path is required',\n field: 'path',\n });\n }\n const absPath = await safeResolveReal(input.path, ctx);\n\n // Determine whether to include symbols: per-call param overrides meta flag.\n const shouldIncludeSymbols =\n input.includeSymbols === true ||\n (input.includeSymbols !== false && ctx.meta[ADVANCED_MODE_META_KEY] === true);\n\n let stat: Awaited<ReturnType<typeof fs.stat>>;\n try {\n stat = await fs.stat(absPath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new FsError({\n message: `read: file not found \"${input.path}\"`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { errno: 'ENOENT' },\n });\n }\n throw new FsError({\n message: `read: failed to stat \"${input.path}\": ${toErrorMessage(err)}`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { errno: code },\n cause: err,\n });\n }\n if (!stat.isFile()) {\n throw new FsError({\n message: `read: \"${input.path}\" is not a regular file`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { reason: 'not-a-regular-file' },\n });\n }\n if (stat.size > MAX_BYTES) {\n throw new FsError({\n message: `read: file too large (${stat.size} bytes, limit ${MAX_BYTES})`,\n code: 'FS_READ_FAILED',\n path: absPath,\n context: { size: stat.size, limit: MAX_BYTES, reason: 'too-large' },\n });\n }\n\n const offset = Math.max(1, input.offset ?? 1);\n const limit = Math.max(0, Math.min(input.limit ?? 2000, 5000));\n const prior = getReadRangeRecord(ctx, absPath);\n const requestedEnd = prior\n ? Math.min(offset + limit - 1, prior.totalLines)\n : offset + limit - 1;\n if (\n input.mode !== 'summary' &&\n limit > 0 &&\n prior &&\n coversRange(prior, stat.mtimeMs, offset, requestedEnd)\n ) {\n ctx.recordRead(absPath, stat.mtimeMs);\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text:\n `[unchanged since previous read: \"${input.path}\" mtime=${Math.round(stat.mtimeMs)}; ` +\n `requested lines ${offset}-${requestedEnd} were already shown. Use offset/limit for a new range if needed.]`,\n total_lines: prior.totalLines,\n encoding: 'utf8',\n truncated: requestedEnd < prior.totalLines,\n cached: true,\n note: mergeSymbolNote('Repeated read suppressed to save tokens.', symResult?.note),\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n };\n }\n\n const buf = await fs.readFile(absPath);\n if (isBinaryBuffer(buf)) {\n throw new Error(`read: \"${input.path}\" appears to be binary`);\n }\n\n const text = buf.toString('utf8');\n // Content hash recorded alongside the mtime: `edit` uses it as the\n // authoritative staleness check (mtime alone has a 2 s tolerance window\n // on Windows). The full file is read even for offset/limit slices, so\n // the hash always covers the whole content.\n const contentHash = sha256hex(text);\n const allLines = text.split(/\\r\\n|\\r|\\n/);\n const total = allLines.length;\n\n if (input.mode === 'summary') {\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, 1, Math.min(total, 200));\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: summarizeFile(input.path, stat.size, allLines),\n total_lines: total,\n encoding: 'utf8',\n truncated: total > 200,\n note: mergeSymbolNote(\n 'Summary mode returned compact structure instead of full file content.',\n symResult?.note,\n ),\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n };\n }\n if (limit === 0) {\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, 1, 0);\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: '',\n total_lines: total,\n encoding: 'utf8',\n truncated: total > 0,\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n ...(symResult?.note ? { note: symResult.note } : {}),\n };\n }\n // Offset past EOF: return an explicit message instead of an empty string.\n // Without this, models with weak instruction-following (e.g. k2p7) see an\n // empty result, assume the read failed transiently, and retry the exact\n // same offset indefinitely \u2014 a tight tool-use loop that burns iterations\n // and context without making progress.\n if (offset > total) {\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, total + 1, total + 1);\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: `[offset ${offset} is past end of file \"${input.path}\" \u2014 file has ${total} line(s). Do not retry this offset.]`,\n total_lines: total,\n encoding: 'utf8',\n truncated: false,\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n ...(symResult?.note ? { note: symResult.note } : {}),\n };\n }\n\n const slice = allLines.slice(offset - 1, offset - 1 + limit);\n const truncated = offset - 1 + slice.length < total;\n\n const width = String(offset + slice.length - 1).length;\n const numbered = slice\n .map((line, i) => `${String(offset + i).padStart(width, ' ')}\u2192${line}`)\n .join('\\n');\n\n ctx.recordRead(absPath, stat.mtimeMs, 'user', contentHash);\n rememberReadRange(ctx, absPath, stat.mtimeMs, total, offset, offset + slice.length - 1);\n\n const symResult = shouldIncludeSymbols\n ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal)\n : undefined;\n return {\n text: numbered,\n total_lines: total,\n encoding: 'utf8',\n truncated,\n ...(symResult?.symbols ? { symbols: symResult.symbols } : {}),\n ...(symResult?.note ? { note: symResult.note } : {}),\n };\n },\n};\n\n/**\n * Best-effort fetch of codebase-index symbols for a file. Returns\n * sorted symbol entries (or `undefined` when the index is unavailable\n * or has no symbols for this file) and an optional truncation note.\n * Never throws \u2014 symbol injection must never break the read operation.\n */\nasync function fetchSymbolsForFile(\n absPath: string,\n ctx: import('@wrongstack/core/agent').Context,\n signal?: AbortSignal,\n): Promise<{ symbols?: SymbolEntry[]; note?: string }> {\n try {\n const state = getIndexState();\n if (!state.ready) return {};\n\n const { results, total } = await searchCodebaseIndex(\n {\n projectRoot: ctx.projectRoot,\n indexDir: codebaseIndexDirOverride(ctx),\n query: '',\n file: absPath,\n limit: 500,\n },\n { signal },\n );\n\n if (results.length === 0) return {};\n\n const sorted = results\n .map((r) => ({\n name: r.name,\n kind: r.kind,\n line: r.line,\n col: r.col,\n signature: r.signature,\n }))\n .sort((a, b) => a.line - b.line || a.col - b.col);\n\n const result: { symbols: SymbolEntry[]; note?: string } = { symbols: sorted };\n if (total > results.length) {\n result.note = `Symbol listing truncated to ${results.length} of ${total} entries.`;\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/** Merge an optional symbol-truncation note into an existing or absent note field. */\nfunction mergeSymbolNote(\n note: string | undefined,\n symNote: string | undefined,\n): string | undefined {\n if (!symNote) return note;\n if (!note) return symNote;\n return `${note} ${symNote}`;\n}\n\ninterface ReadRangeRecord {\n mtimeMs: number;\n totalLines: number;\n ranges: Array<{ start: number; end: number }>;\n}\n\nconst READ_RANGES_META_KEY = 'tools.read.ranges.v1';\n\nfunction getReadRanges(\n ctx: import('@wrongstack/core/agent').Context,\n): Record<string, ReadRangeRecord> {\n const existing = ctx.meta[READ_RANGES_META_KEY];\n if (existing && typeof existing === 'object' && !Array.isArray(existing)) {\n return existing as Record<string, ReadRangeRecord>;\n }\n const next: Record<string, ReadRangeRecord> = {};\n ctx.meta[READ_RANGES_META_KEY] = next;\n return next;\n}\n\nfunction getReadRangeRecord(\n ctx: import('@wrongstack/core/agent').Context,\n absPath: string,\n): ReadRangeRecord | undefined {\n return getReadRanges(ctx)[absPath];\n}\n\nfunction rememberReadRange(\n ctx: import('@wrongstack/core/agent').Context,\n absPath: string,\n mtimeMs: number,\n totalLines: number,\n start: number,\n end: number,\n): void {\n if (end < start) return;\n const ranges = getReadRanges(ctx);\n const prior = ranges[absPath];\n const nextRanges = prior && Math.abs(prior.mtimeMs - mtimeMs) <= 1 ? prior.ranges.slice() : [];\n nextRanges.push({ start, end });\n ranges[absPath] = {\n mtimeMs,\n totalLines,\n ranges: mergeRanges(nextRanges),\n };\n}\n\nfunction coversRange(\n record: ReadRangeRecord,\n mtimeMs: number,\n start: number,\n end: number,\n): boolean {\n if (Math.abs(record.mtimeMs - mtimeMs) > 1) return false;\n return record.ranges.some((range) => range.start <= start && range.end >= end);\n}\n\nfunction mergeRanges(\n ranges: Array<{ start: number; end: number }>,\n): Array<{ start: number; end: number }> {\n const sorted = ranges.slice().sort((a, b) => a.start - b.start);\n const merged: Array<{ start: number; end: number }> = [];\n for (const range of sorted) {\n const last = merged[merged.length - 1];\n if (!last || range.start > last.end + 1) {\n merged.push({ ...range });\n continue;\n }\n last.end = Math.max(last.end, range.end);\n }\n return merged;\n}\n\nfunction summarizeFile(filePath: string, bytes: number, lines: string[]): string {\n const interesting = lines\n .map((line, index) => ({ line: line.trim(), number: index + 1 }))\n .filter(({ line }) =>\n /^(import\\s|export\\s|class\\s|interface\\s|type\\s|function\\s|const\\s+\\w+\\s*=|let\\s+\\w+\\s*=|var\\s+\\w+\\s*=|def\\s+|async\\s+function\\s)/.test(\n line,\n ),\n )\n .slice(0, 80)\n .map(({ line, number }) => `${number}: ${line}`);\n return [\n `summary: ${filePath}`,\n `bytes=${bytes}`,\n `total_lines=${lines.length}`,\n interesting.length > 0\n ? `symbols/imports:\\n${interesting.join('\\n')}`\n : 'symbols/imports: (none detected)',\n ].join('\\n');\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core/utils';\nimport type { Context } from '@wrongstack/core/agent';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n", "/**\n * Index host \u2014 the main-thread coordinator for all codebase-index operations.\n *\n * Production mode runs every operation (full scans, per-file reindexes,\n * searches, stats) in a dedicated worker thread (`worker.ts`), so the\n * synchronous `node:sqlite` calls and the TypeScript parser can never block\n * the main event loop \u2014 the failure mode that used to freeze terminals is\n * structurally impossible. When the built worker file is not present (tests\n * run from source, exotic runtimes) or `WRONGSTACK_INDEX_INLINE=1` is set,\n * operations fall back to running inline through the same service layer.\n *\n * Concerns owned here, in front of either execution mode:\n *\n * 1. **Serialization** \u2014 every write run (startup scan, per-edit incremental,\n * external file-watch, manual reindex) goes through one process-wide\n * promise-chain mutex so two runs never race the same `index.db` writer.\n * 2. **Debounce** \u2014 rapid successive edits to the same file coalesce, then\n * files that become ready in the same event-loop turn share one index run.\n * 3. **Watchdog** \u2014 every operation is raced against a timeout. In worker\n * mode a timeout hard-terminates the worker (it respawns lazily on the\n * next request); inline it aborts the run's signal. Either way the mutex\n * chain always advances and the promise always settles.\n * 4. **Circuit breaker** \u2014 repeated failures/timeouts pause indexing instead\n * of queuing more work behind a wedged pipeline. See circuit-breaker.ts.\n * 5. **State tracking** \u2014 ready/indexing/progress flags + change listeners\n * for the TUI status chip and the search/stats tools' gating.\n */\n\nimport * as fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { Worker } from 'node:worker_threads';\nimport {\n CircuitOpenError,\n type CircuitSnapshot,\n IndexTimeoutError,\n indexCircuitBreaker,\n LockError,\n} from './circuit-breaker.js';\nimport {\n fileGraphService as fileGraphServiceInline,\n indexService,\n packageGraphService as packageGraphServiceInline,\n searchService,\n statsService,\n symbolGraphService as symbolGraphServiceInline,\n} from './index-service.js';\nimport { isIndexablePath } from './languages.js';\nimport {\n callProjectIndexServer,\n checkProjectIndexServerHealth,\n closeProjectIndexServerClients,\n ensureProjectIndexServer,\n getProjectIndexServerConnectionState,\n onProjectIndexServerConnectionStateChange,\n type ProjectIndexDaemonAvailability,\n type ProjectIndexServerClientHealth,\n type ProjectIndexServerConnectionState,\n type ProjectIndexServerShutdownResult,\n resolveProjectIndexDaemonAvailability,\n shutdownProjectIndexServer,\n} from './project-server-client.js';\nimport type { CodeMapGraph, IndexResult, IndexStats } from './schema.js';\nimport type {\n FileGraphOpArgs,\n HostToWorker,\n IndexOpArgs,\n OpName,\n OpShapes,\n SearchOpArgs,\n SearchOpResult,\n StatsOpArgs,\n SymbolGraphOpArgs,\n WorkerToHost,\n} from './worker-protocol.js';\nimport { indexStorePool } from './writer.js';\n\n// \u2500\u2500\u2500 Watchdog timeouts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Watchdog timeout for a full (startup / manual) index run.\n *\n * Bumped from 120s \u2192 240s in 0.284.x: large monorepos (WrongStack itself\n * has ~3k TS files) regularly exceed 120s on the first run on cold SSD +\n * Windows Defender real-time scanning. The watchdog only fires when the\n * worker is truly stuck \u2014 it does not slow down the happy path.\n */\nconst DEFAULT_FULL_INDEX_TIMEOUT_MS = 240_000;\n/** Watchdog timeout for one incremental reindex batch. */\nconst DEFAULT_INCREMENTAL_TIMEOUT_MS = 60_000;\n/**\n * Watchdog timeout for read operations (search / stats).\n *\n * Reads normally finish quickly, but a cold or contended SQLite index can take\n * longer than the former 8s budget, especially on large Windows workspaces.\n * Keep this below the outer tool timeout so callers receive the structured\n * IndexTimeoutError when the worker is genuinely wedged.\n */\nconst DEFAULT_QUERY_TIMEOUT_MS = 30_000;\n\n// \u2500\u2500\u2500 Indexing lifecycle state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Process-wide counters so codebase-search / codebase-stats can gate on\n// readiness and UIs can show an indexing indicator.\nlet _ready = false;\nlet _indexing = false;\nlet _currentFile = 0;\nlet _totalFiles = 0;\nlet _lastError: string | null = null;\n\n/** True once the first full-project index has completed (success or failure). */\nexport function isIndexReady(): boolean {\n return _ready;\n}\n\n/**\n * Mark the index as ready so downstream tools (codebase-search, codebase-stats)\n * don't gate on a startup index that never ran.\n */\nexport function setIndexReady(): void {\n _ready = true;\n}\n\n/** True while an index build is actively running. */\nexport function isIndexing(): boolean {\n return _indexing;\n}\n\n/** Current indexing progress: { currentFile, totalFiles, ready, indexing, circuit }. */\nexport function getIndexState(): {\n ready: boolean;\n indexing: boolean;\n currentFile: number;\n totalFiles: number;\n lastError: string | null;\n /** Detached per-project server connection owned by this client process. */\n server: ProjectIndexServerConnectionState;\n /** Circuit-breaker state \u2014 `open` means indexing is paused after repeated failures. */\n circuit: CircuitSnapshot;\n} {\n const server = getProjectIndexServerConnectionState();\n const remoteActivity = server.activity;\n const remoteIndexing = remoteActivity?.indexing ?? false;\n return {\n ready: _ready || (remoteActivity?.generation ?? 0) > 0,\n indexing: _indexing || remoteIndexing,\n currentFile: remoteIndexing ? (remoteActivity?.currentFile ?? 0) : _currentFile,\n totalFiles: remoteIndexing ? (remoteActivity?.totalFiles ?? 0) : _totalFiles,\n lastError: remoteActivity?.lastError ?? _lastError,\n server,\n circuit: indexCircuitBreaker.snapshot(),\n };\n}\n\n/**\n * Optional callback fired on every lifecycle transition (started, progress,\n * completed, failed). Plug into the event bus or a TUI dispatcher to surface\n * the indexing state in real time.\n */\ntype IndexStateListener = (state: ReturnType<typeof getIndexState>) => void;\nlet _listeners: IndexStateListener[] = [];\n\nexport function onIndexStateChange(listener: IndexStateListener): () => void {\n _listeners.push(listener);\n return () => {\n _listeners = _listeners.filter((l) => l !== listener);\n };\n}\n\nfunction emitState() {\n const state = getIndexState();\n for (const l of _listeners) l(state);\n}\n\n// Server lifecycle changes are part of index state even while no indexing job\n// is active, so the TUI can keep a truthful connected/offline/error indicator.\nonProjectIndexServerConnectionStateChange(() => emitState());\n\nfunction setIndexProgress(current: number, total: number) {\n _currentFile = current;\n _totalFiles = total;\n emitState();\n}\n\n// \u2500\u2500\u2500 Worker management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface PendingRpc {\n resolve: (value: unknown) => void;\n reject: (err: unknown) => void;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nlet worker: Worker | null = null;\nlet workerUnavailable = false;\nlet nextRpcId = 1;\nconst pending = new Map<number, PendingRpc>();\n\n/**\n * Locate the built worker file. The host is bundled into several entry points\n * (`dist/index.js`, `dist/builtin.js`, `dist/codebase-index/index.js`), so the\n * worker is probed at both relative locations. From source (vitest) neither\n * `.js` exists \u2192 inline mode, which keeps tests hermetic and mockable.\n */\nfunction resolveWorkerUrl(): URL | null {\n if (process.env['WRONGSTACK_INDEX_INLINE']) return null;\n for (const rel of ['./worker.js', './codebase-index/worker.js']) {\n try {\n const url = new URL(rel, import.meta.url);\n if (url.protocol === 'file:' && fs.existsSync(fileURLToPath(url))) return url;\n } catch {\n /* try the next candidate */\n }\n }\n return null;\n}\n\nfunction failAllPending(err: unknown): void {\n const entries = [...pending.values()];\n pending.clear();\n for (const p of entries) p.reject(err);\n}\n\nfunction ensureWorker(): Worker | null {\n if (worker) return worker;\n if (workerUnavailable) return null;\n const url = resolveWorkerUrl();\n if (!url) {\n workerUnavailable = true;\n return null;\n }\n try {\n const w = new Worker(url, { name: 'wstack-codebase-index' });\n // The worker must never keep the process alive on its own.\n w.unref();\n w.on('message', (msg: WorkerToHost) => {\n if (msg.type === 'progress') {\n pending.get(msg.id)?.onProgress?.(msg.current, msg.total);\n return;\n }\n const entry = pending.get(msg.id);\n if (!entry) return; // already timed out / cancelled\n pending.delete(msg.id);\n if (msg.ok) entry.resolve(msg.result);\n else {\n const error =\n msg.errorName === 'LockError' ? new LockError(msg.error) : new Error(msg.error);\n if (msg.errorName && msg.errorName !== 'Error') {\n (error as { name: string }).name = msg.errorName;\n }\n entry.reject(error);\n }\n });\n w.on('error', (err) => {\n // Ignore late events from a worker already terminated/replaced by the\n // watchdog; they must not reject RPCs owned by its successor.\n if (worker !== w) return;\n worker = null;\n failAllPending(err);\n });\n w.on('exit', () => {\n if (worker !== w) return;\n worker = null;\n failAllPending(new Error('codebase-index worker exited'));\n });\n worker = w;\n return w;\n } catch {\n // Spawn failed (no worker_threads, sandbox, \u2026) \u2014 fall back to inline for\n // the rest of the process lifetime.\n workerUnavailable = true;\n return null;\n }\n}\n\n/** Hard-kill a wedged worker. It respawns lazily on the next operation. */\nfunction terminateWorker(reason: unknown): void {\n const w = worker;\n worker = null;\n failAllPending(reason);\n if (w) void w.terminate().catch(() => {});\n}\n\n/**\n * Tear down the index host (worker + pending debounces). Call on process\n * shutdown; safe to call when nothing is running.\n */\nexport async function shutdownCodebaseIndexHost(): Promise<void> {\n cancelPendingReindexes();\n closeProjectIndexServerClients();\n indexStorePool.closeAll();\n const w = worker;\n worker = null;\n failAllPending(new Error('codebase-index host shut down'));\n workerUnavailable = false; // a future call may spawn a fresh worker\n if (w) {\n try {\n // On Windows SQLite files remain locked until the worker has actually\n // exited. Exposing the termination promise lets deterministic teardown\n // wait before removing an index directory.\n await w.terminate();\n } catch {\n // Shutdown is best-effort, matching watchdog termination semantics.\n }\n }\n}\n\ninterface CallOpts {\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\n/** Endpoints already reported as unbindable \u2014 warn once per endpoint, not per call. */\nconst warnedInvalidEndpoints = new Set<string>();\n\nfunction warnEndpointInvalidOnce(\n availability: Extract<ProjectIndexDaemonAvailability, { kind: 'endpoint-invalid' }>,\n): void {\n if (warnedInvalidEndpoints.has(availability.endpoint)) return;\n warnedInvalidEndpoints.add(availability.endpoint);\n process.stderr.write(\n `codebase-index: socket path is ${availability.byteLength} bytes, over this platform's ` +\n `${availability.maxBytes}-byte sun_path limit (${availability.endpoint}). ` +\n `Subsequent calls will reject until TMPDIR is shortened to restore the shared daemon.\\n`,\n );\n}\n\n/**\n * Run one operation, in the worker when available, inline otherwise. Both\n * paths share the watchdog: the returned promise ALWAYS settles within\n * `timeoutMs`, and a timeout in worker mode terminates the (possibly wedged\n * in synchronous code) worker \u2014 something an in-process watchdog can never do.\n */\nfunction callIndexOp<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n opts: CallOpts,\n): Promise<OpShapes[O]['result']> {\n // Production builds route every operation through one detached server per\n // project. The worker/inline path below still exists for source-tree tests\n // and exotic runtimes, but it has to be asked for: reaching it because the\n // build could not be located would give this process a private FTS5 database\n // and no indication that it had stopped sharing the project's index.\n const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);\n if (availability.kind === 'available') {\n return callProjectIndexServer(op, args, opts);\n }\n if (availability.kind === 'missing-build') {\n return Promise.reject(\n new Error(\n 'Built codebase-index project server is unavailable. Build @wrongstack/tools, ' +\n 'or set WRONGSTACK_INDEX_INLINE=1 to explicitly accept a process-local index.',\n ),\n );\n }\n if (availability.kind === 'endpoint-invalid') {\n // The daemon cannot bind this endpoint (socket path over the platform's\n // sun_path limit \u2014 macOS's long per-user TMPDIR is the known trigger).\n // Spawning would die silently (`stdio: 'ignore'`), so explicitly reject\n // here instead of falling through to the in-process index: silently\n // degrading would strip every connected client of the shared per-project\n // daemon and they would all open their own private FTS5 databases, with\n // no indication that they had stopped sharing one.\n warnEndpointInvalidOnce(availability);\n return Promise.reject(\n new Error(\n `codebase-index: socket path is ${availability.byteLength} bytes, over this platform's ` +\n `${availability.maxBytes}-byte sun_path limit (${availability.endpoint}). ` +\n `Set a shorter TMPDIR to relocate the shared daemon, ` +\n `or set WRONGSTACK_INDEX_INLINE=1 to explicitly opt into a process-local index.`,\n ),\n );\n }\n\n const w = ensureWorker();\n if (!w) return callInline(op, args, opts);\n\n if (opts.signal?.aborted) {\n return Promise.reject(\n opts.signal.reason instanceof Error ? opts.signal.reason : new Error('Indexing cancelled'),\n );\n }\n\n return new Promise<OpShapes[O]['result']>((resolve, reject) => {\n const id = nextRpcId++;\n\n const timer = setTimeout(() => {\n pending.delete(id);\n const err = new IndexTimeoutError(\n `Index ${op} exceeded its ${opts.timeoutMs}ms watchdog timeout`,\n );\n // A wedged worker (synchronous sqlite wait, pathological parse) cannot\n // be cooperatively cancelled \u2014 kill it; it respawns on the next call.\n terminateWorker(err);\n reject(err);\n }, opts.timeoutMs);\n timer.unref?.();\n\n const onAbort = () => {\n // Cooperative cancel; the worker aborts the op's signal and responds\n // with an error. The watchdog stays armed as the backstop.\n w.postMessage({ type: 'cancel', id } satisfies HostToWorker);\n };\n opts.signal?.addEventListener('abort', onAbort, { once: true });\n\n const cleanup = () => {\n clearTimeout(timer);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n pending.set(id, {\n resolve: (v) => {\n cleanup();\n resolve(v as OpShapes[O]['result']);\n },\n reject: (e) => {\n cleanup();\n reject(e);\n },\n onProgress: opts.onProgress,\n });\n\n w.postMessage({ type: 'request', id, op, args } satisfies HostToWorker);\n });\n}\n\n/** Inline fallback: same service code, raced against the same watchdog. */\nasync function callInline<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n opts: CallOpts,\n): Promise<OpShapes[O]['result']> {\n const ac = new AbortController();\n const onOuterAbort = () => ac.abort(opts.signal?.reason ?? new Error('Indexing cancelled'));\n if (opts.signal?.aborted) onOuterAbort();\n else opts.signal?.addEventListener('abort', onOuterAbort, { once: true });\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watchdog = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n const err = new IndexTimeoutError(\n `Index ${op} exceeded its ${opts.timeoutMs}ms watchdog timeout`,\n );\n ac.abort(err);\n reject(err);\n }, opts.timeoutMs);\n timer.unref?.();\n });\n\n const job = async (): Promise<OpShapes[O]['result']> => {\n switch (op) {\n case 'index':\n return (await indexService(args as IndexOpArgs, {\n signal: ac.signal,\n onProgress: opts.onProgress,\n })) as OpShapes[O]['result'];\n case 'search':\n return searchService(args as SearchOpArgs) as OpShapes[O]['result'];\n case 'stats':\n return statsService(args as StatsOpArgs) as OpShapes[O]['result'];\n case 'packageGraph':\n return packageGraphServiceInline(args as StatsOpArgs) as OpShapes[O]['result'];\n case 'fileGraph':\n return fileGraphServiceInline(args as FileGraphOpArgs) as OpShapes[O]['result'];\n case 'symbolGraph':\n return symbolGraphServiceInline(args as SymbolGraphOpArgs) as OpShapes[O]['result'];\n default:\n throw new Error(`unknown index op: ${String(op)}`);\n }\n };\n\n try {\n return await Promise.race([job(), watchdog]);\n } finally {\n if (timer) clearTimeout(timer);\n opts.signal?.removeEventListener('abort', onOuterAbort);\n }\n}\n\n// \u2500\u2500\u2500 Process-wide write mutex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// A single promise chain. Each enqueued job awaits the previous one's settle\n// (success OR failure) before running, so a thrown job never wedges the chain.\n// Only write runs (index) take the mutex; searches/stats are WAL reads.\nlet chain: Promise<unknown> = Promise.resolve();\n\nfunction withMutex<T>(job: () => Promise<T>): Promise<T> {\n const run = chain.then(job, job);\n // Keep the chain alive regardless of this job's outcome.\n chain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n}\n\n/** Build the fail-fast error thrown while the circuit is open. */\nfunction circuitOpenError(): CircuitOpenError {\n const c = indexCircuitBreaker.snapshot();\n return new CircuitOpenError(\n 'Codebase indexing is temporarily paused after repeated failures' +\n (c.lastFailure ? ` (last: ${c.lastFailure})` : '') +\n (c.cooldownRemainingMs > 0\n ? `; auto-retry in ${Math.ceil(c.cooldownRemainingMs / 1000)}s`\n : '') +\n '. Use /codebase-reindex to retry now.',\n );\n}\n\n// \u2500\u2500\u2500 Debounce \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst DEFAULT_DEBOUNCE_MS = 400;\nconst debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();\ninterface ReadyReindexBatch {\n projectRoot: string;\n indexDir?: string | undefined;\n files: Set<string>;\n timeoutMs: number;\n onErrors: Set<(err: unknown) => void>;\n flush?: ReturnType<typeof setImmediate> | undefined;\n}\nconst readyReindexBatches = new Map<string, ReadyReindexBatch>();\n\nfunction debounceKey(projectRoot: string, indexDir: string | undefined, file: string): string {\n return JSON.stringify([projectRoot, indexDir ?? '', file]);\n}\n\nfunction reindexBatchKey(projectRoot: string, indexDir: string | undefined): string {\n return JSON.stringify([projectRoot, indexDir ?? '']);\n}\n\nfunction flushReadyReindexBatch(key: string): void {\n const batch = readyReindexBatches.get(key);\n if (!batch) return;\n readyReindexBatches.delete(key);\n\n if (!indexCircuitBreaker.allowRequest()) {\n const error = circuitOpenError();\n for (const onError of batch.onErrors) onError(error);\n return;\n }\n\n void withMutex(() =>\n callIndexOp(\n 'index',\n {\n projectRoot: batch.projectRoot,\n files: [...batch.files].sort(),\n indexDir: batch.indexDir,\n },\n { timeoutMs: batch.timeoutMs },\n ),\n ).then(\n () => indexCircuitBreaker.recordSuccess(),\n (err) => {\n indexCircuitBreaker.recordFailure(err);\n for (const onError of batch.onErrors) onError(err);\n },\n );\n}\n\nfunction addReadyReindex(opts: {\n projectRoot: string;\n file: string;\n indexDir?: string | undefined;\n timeoutMs: number;\n onError?: ((err: unknown) => void) | undefined;\n}): void {\n const key = reindexBatchKey(opts.projectRoot, opts.indexDir);\n const existing = readyReindexBatches.get(key);\n if (existing) {\n existing.files.add(opts.file);\n existing.timeoutMs = Math.max(existing.timeoutMs, opts.timeoutMs);\n if (opts.onError) existing.onErrors.add(opts.onError);\n return;\n }\n\n const batch: ReadyReindexBatch = {\n projectRoot: opts.projectRoot,\n indexDir: opts.indexDir,\n files: new Set([opts.file]),\n timeoutMs: opts.timeoutMs,\n onErrors: new Set(opts.onError ? [opts.onError] : []),\n };\n batch.flush = setImmediate(() => flushReadyReindexBatch(key));\n batch.flush.unref?.();\n readyReindexBatches.set(key, batch);\n}\n\n// \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** True when the file's extension maps to a language the indexer can parse. */\nexport function isIndexableFile(filePath: string): boolean {\n return isIndexablePath(filePath);\n}\n\n/**\n * Run a full-project scan and await it. Used at session start and by the manual\n * `/codebase-reindex` command. Incremental by default (unchanged files skipped\n * via mtime, so repeat runs are cheap); pass `force` to clear and rebuild.\n *\n * Sets the global `_ready` flag on completion so downstream tools know the\n * index is usable.\n */\n/** Pattern matching SQLite constraint failures caused by a stale/corrupt index DB. */\nfunction isRecoverableConstraintError(err: unknown): boolean {\n if (err instanceof Error) {\n const msg = err.message.toLowerCase();\n return msg.includes('unique constraint') || msg.includes('constraint failed');\n }\n return false;\n}\n\nexport async function runStartupIndex(opts: {\n projectRoot: string;\n indexDir?: string | undefined;\n force?: boolean | undefined;\n langs?: string[] | undefined;\n signal?: AbortSignal | undefined;\n /** Watchdog timeout for the whole run. Default: 120s. */\n timeoutMs?: number | undefined;\n}): Promise<IndexResult> {\n // Circuit breaker: after repeated failures/timeouts, fail fast instead of\n // queuing yet another run behind a possibly-wedged pipeline.\n if (!indexCircuitBreaker.allowRequest()) throw circuitOpenError();\n\n _indexing = true;\n emitState();\n\n try {\n const result = await withMutex(() => {\n // Reset counters inside the mutex \u2014 if runStartupIndex is called twice\n // concurrently, the second caller must not clobber a running index's\n // progress counters.\n _currentFile = 0;\n _totalFiles = 0;\n _lastError = null;\n return callIndexOp(\n 'index',\n {\n projectRoot: opts.projectRoot,\n indexDir: opts.indexDir,\n force: opts.force,\n langs: opts.langs,\n },\n {\n timeoutMs: opts.timeoutMs ?? DEFAULT_FULL_INDEX_TIMEOUT_MS,\n signal: opts.signal,\n onProgress: setIndexProgress,\n },\n );\n });\n _ready = true;\n indexCircuitBreaker.recordSuccess();\n return result;\n } catch (err) {\n _lastError = err instanceof Error ? err.message : String(err);\n\n // Auto-recovery: SQLite constraint failures indicate index DB corruption\n // from a previous interrupted write (e.g., process killed mid-insert).\n // Retry with force=true to wipe and rebuild from source.\n const originalError = _lastError;\n if (isRecoverableConstraintError(err) && !opts.force) {\n _lastError = null;\n const rebuildResult = await runStartupIndex({\n ...opts,\n force: true,\n });\n _ready = true;\n // Tag the result so callers can distinguish a normal run from a\n // corruption-triggered recovery.\n return {\n ...rebuildResult,\n autoRecovered: { failure: originalError, rebuiltWithForce: true },\n };\n }\n\n _ready = true; // index is \"ready\" in the sense that we won't try again; downstream tools will see lastError\n // Caller-initiated aborts (session teardown, Ctrl+C) are not indexer\n // failures \u2014 only genuine errors and watchdog timeouts trip the breaker.\n if (!opts.signal?.aborted) indexCircuitBreaker.recordFailure(err);\n throw err;\n } finally {\n _indexing = false;\n emitState();\n }\n}\n\n/**\n * Debounced, fire-and-forget incremental reindex of specific files. Used by the\n * per-edit toolCall middleware and the external file watcher. Non-indexable\n * paths are dropped. Errors are reported via the optional `onError` callback and\n * never thrown to the caller (background work must not crash a turn).\n */\nexport function enqueueReindex(opts: {\n projectRoot: string;\n files: string[];\n indexDir?: string | undefined;\n debounceMs?: number | undefined;\n /** Watchdog timeout per file. Default: 30s. */\n timeoutMs?: number | undefined;\n onError?: ((err: unknown) => void) | undefined;\n}): void {\n const files = opts.files.filter(isIndexableFile);\n if (files.length === 0) return;\n const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n\n for (const file of files) {\n const key = debounceKey(opts.projectRoot, opts.indexDir, file);\n const existing = debounceTimers.get(key);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n debounceTimers.delete(key);\n // All per-file debounce timers that expire in this event-loop turn are\n // folded into one worker/SQLite operation per project index. This keeps\n // same-file debounce semantics while avoiding N full index lifecycles\n // for watcher bursts such as branch switches or generated-file updates.\n addReadyReindex({\n projectRoot: opts.projectRoot,\n file,\n indexDir: opts.indexDir,\n timeoutMs: opts.timeoutMs ?? DEFAULT_INCREMENTAL_TIMEOUT_MS,\n onError: opts.onError,\n });\n }, ms);\n // Don't keep the event loop alive solely for a pending reindex.\n timer.unref?.();\n debounceTimers.set(key, timer);\n }\n}\n\n/** Cancel all pending debounced reindexes. For teardown / tests. */\nexport function cancelPendingReindexes(): void {\n for (const t of debounceTimers.values()) clearTimeout(t);\n debounceTimers.clear();\n for (const batch of readyReindexBatches.values()) {\n if (batch.flush) clearImmediate(batch.flush);\n }\n readyReindexBatches.clear();\n}\n\n/**\n * Ranked symbol search against the index. The query runs in the index worker\n * (or inline in fallback mode) \u2014 the main thread never opens SQLite. Reads\n * don't take the write mutex (WAL readers don't block the writer) and don't\n * feed the circuit breaker; a wedged read still trips the watchdog, which\n * recycles the worker.\n */\nexport async function searchCodebaseIndex(\n args: SearchOpArgs,\n opts: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<SearchOpResult> {\n return callIndexOp('search', args, {\n timeoutMs: opts.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n signal: opts.signal,\n });\n}\n\n/** Index health/statistics, fetched off the main thread like searches. */\nexport async function codebaseIndexStats(\n args: StatsOpArgs,\n opts: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<IndexStats> {\n return callIndexOp('stats', args, {\n timeoutMs: opts.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n signal: opts.signal,\n });\n}\n\n/** Package dependency graph, served by the same per-project index process. */\nexport async function packageGraphService(args: StatsOpArgs): Promise<CodeMapGraph> {\n return callIndexOp('packageGraph', args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });\n}\n\n/** File dependency graph, served by the same per-project index process. */\nexport async function fileGraphService(args: FileGraphOpArgs): Promise<CodeMapGraph> {\n return callIndexOp('fileGraph', args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });\n}\n\n/** Symbol dependency graph, served by the same per-project index process. */\nexport async function symbolGraphService(args: SymbolGraphOpArgs): Promise<CodeMapGraph> {\n return callIndexOp('symbolGraph', args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });\n}\n\n/**\n * Stop this project's detached index server. Unlike\n * shutdownCodebaseIndexHost(), this intentionally affects every connected\n * TUI/CLI/WebUI client for the project.\n */\nexport function shutdownCodebaseIndexServer(\n projectRoot: string,\n indexDir?: string,\n reason?: string,\n): Promise<ProjectIndexServerShutdownResult> {\n return shutdownProjectIndexServer(projectRoot, indexDir, reason);\n}\n\n/** Probe the connected project server without starting a missing server. */\nexport function checkCodebaseIndexServerHealth(\n projectRoot: string,\n indexDir?: string,\n options: { timeoutMs?: number | undefined } = {},\n): Promise<ProjectIndexServerClientHealth> {\n return checkProjectIndexServerHealth(projectRoot, indexDir, options);\n}\n\n/** Ensure the detached project server exists and owns external watching. */\nexport function ensureCodebaseIndexServer(options: {\n projectRoot: string;\n indexDir?: string | undefined;\n watchExternal?: boolean | undefined;\n debounceMs?: number | undefined;\n}): Promise<void> {\n const availability = resolveProjectIndexDaemonAvailability(options.projectRoot, options.indexDir);\n if (availability.kind !== 'available') {\n if (availability.kind === 'endpoint-invalid') warnEndpointInvalidOnce(availability);\n return Promise.resolve();\n }\n return ensureProjectIndexServer({\n projectRoot: options.projectRoot,\n indexDir: options.indexDir,\n watchExternal: options.watchExternal ?? false,\n debounceMs: options.debounceMs ?? DEFAULT_DEBOUNCE_MS,\n });\n}\n\n// \u2500\u2500\u2500 Test-only reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Reset all process-global indexing state for test isolation.\n *\n * Vitest runs test files in parallel within the same process, so module-level\n * state (`_indexing`, `_ready`, `chain`, `indexCircuitBreaker`) leaks between\n * tests. Call this in `beforeEach` to ensure each test starts with a clean\n * slate. Production code should NEVER call this.\n */\nexport function resetIndexStateForTesting(): void {\n _ready = false;\n _indexing = false;\n _currentFile = 0;\n _totalFiles = 0;\n _lastError = null;\n chain = Promise.resolve();\n indexCircuitBreaker.reset();\n cancelPendingReindexes();\n closeProjectIndexServerClients();\n warnedInvalidEndpoints.clear();\n // Don't terminate the worker \u2014 it's expensive to respawn and tests that\n // need it will call ensureWorker() lazily. Just clear the RPC state.\n for (const [, p] of pending) p.reject(new Error('test reset'));\n pending.clear();\n nextRpcId = 1;\n}\n", "/**\n * Circuit breaker for the codebase indexer.\n *\n * The indexer can wedge: a hung filesystem, a parser pathology, or another\n * wstack process holding the SQLite write lock (several surfaces \u2014 TUI, WebUI,\n * parallel terminals \u2014 share one per-project `index.db`). Without protection,\n * every queued reindex piles up behind the process-wide mutex, `isIndexing()`\n * stays true forever, and anything that awaits an index run (the startup scan,\n * `/codebase-reindex`) locks its terminal.\n *\n * Standard three-state breaker:\n *\n * closed \u2014 normal operation; consecutive failures are counted.\n * open \u2014 after `failureThreshold` consecutive failures, every request\n * is rejected fast ({@link CircuitOpenError}) for `cooldownMs`.\n * half-open \u2014 after the cooldown exactly one probe run is admitted;\n * success closes the circuit, failure re-opens it.\n *\n * Watchdog timeouts ({@link IndexTimeoutError}) count as failures;\n * caller-initiated aborts (session teardown) do not \u2014 the background indexer\n * makes that distinction before recording.\n *\n * Lock conflicts ({@link LockError}) do NOT count as failures \u2014 they are expected\n * transient conditions when multiple wstack surfaces share the same `index.db`.\n * The index store retries automatically; a LockError only reaches the circuit\n * breaker when all retries are exhausted.\n */\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\nexport interface CircuitSnapshot {\n state: CircuitState;\n consecutiveFailures: number;\n lastFailure: string | null;\n /** ms until an open circuit admits a half-open probe (0 unless open). */\n cooldownRemainingMs: number;\n}\n\n/** Thrown when a run is rejected because the circuit is open. */\nexport class CircuitOpenError extends Error {\n override readonly name = 'CircuitOpenError';\n}\n\n/** Thrown by the background indexer's watchdog when a run exceeds its timeout. */\nexport class IndexTimeoutError extends Error {\n override readonly name = 'IndexTimeoutError';\n}\n\n/**\n * Thrown when an SQLite operation fails with a lock conflict (SQLITE_BUSY or\n * SQLITE_LOCKED) even after all retry attempts are exhausted.\n *\n * The circuit breaker does **not** count `LockError` as a failure \u2014 a lock\n * conflict means another writer is active, not that this indexer is broken.\n * The caller should treat it as a transient failure and retry later.\n */\nexport class LockError extends Error {\n override readonly name = 'LockError';\n}\n\nexport interface CircuitBreakerOptions {\n /** Consecutive failures before the circuit opens. Default: 3. */\n failureThreshold?: number | undefined;\n /** How long an open circuit rejects requests before allowing a probe. Default: 60s. */\n cooldownMs?: number | undefined;\n /** Injectable clock for tests. Default: Date.now. */\n now?: (() => number) | undefined;\n}\n\nexport class IndexCircuitBreaker {\n private readonly failureThreshold: number;\n private readonly cooldownMs: number;\n private readonly now: () => number;\n\n private state: CircuitState = 'closed';\n private consecutiveFailures = 0;\n private openedAt = 0;\n private lastFailure: string | null = null;\n private probeInFlight = false;\n\n constructor(opts: CircuitBreakerOptions = {}) {\n this.failureThreshold = opts.failureThreshold ?? 3;\n this.cooldownMs = opts.cooldownMs ?? 60_000;\n this.now = opts.now ?? Date.now;\n }\n\n /**\n * True when a run may proceed. An open circuit transitions to half-open once\n * the cooldown has elapsed, admitting exactly one probe; further requests\n * are rejected until that probe settles via recordSuccess/recordFailure.\n */\n allowRequest(): boolean {\n if (this.state === 'closed') return true;\n if (this.state === 'open') {\n if (this.now() - this.openedAt < this.cooldownMs) return false;\n this.state = 'half-open';\n this.probeInFlight = true;\n return true;\n }\n // half-open: admit only one probe at a time.\n if (this.probeInFlight) return false;\n this.probeInFlight = true;\n return true;\n }\n\n recordSuccess(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.lastFailure = null;\n this.probeInFlight = false;\n }\n\n recordFailure(err: unknown): void {\n // LockError means \"another process is writing \u2014 try again later\", not a\n // broken indexer. Do not count it against the failure threshold.\n if (err instanceof LockError) {\n this.lastFailure = `[transient/lock] ${err.message}`;\n this.probeInFlight = false;\n return;\n }\n this.lastFailure = err instanceof Error ? err.message : String(err);\n this.probeInFlight = false;\n this.consecutiveFailures++;\n if (this.state === 'half-open' || this.consecutiveFailures >= this.failureThreshold) {\n this.state = 'open';\n this.openedAt = this.now();\n }\n }\n\n /** Force-close the circuit (manual recovery: `/codebase-reindex`). */\n reset(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.lastFailure = null;\n this.probeInFlight = false;\n this.openedAt = 0;\n }\n\n snapshot(): CircuitSnapshot {\n return {\n state: this.state,\n consecutiveFailures: this.consecutiveFailures,\n lastFailure: this.lastFailure,\n cooldownRemainingMs:\n this.state === 'open' ? Math.max(0, this.cooldownMs - (this.now() - this.openedAt)) : 0,\n };\n }\n}\n\n/**\n * Process-wide breaker shared by every index path (startup scan, per-edit\n * incremental, external watcher, the `codebase-index` tool). Module-level for\n * the same reason the mutex is: there is one `index.db` per project and one\n * indexing pipeline per process.\n */\nexport const indexCircuitBreaker = new IndexCircuitBreaker();\n\n/** Reset the shared breaker \u2014 used by `/codebase-reindex` and tests. */\nexport function resetIndexCircuitBreaker(): void {\n indexCircuitBreaker.reset();\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * Main indexing orchestrator.\n *\n * Given a project root and a list of files:\n * 1. Parse each file with the appropriate parser (TS, Go, Python, Rust, JSON, YAML)\n * 2. Delete old symbols for changed/deleted files\n * 3. Insert new symbols\n * 4. Update file metadata\n * 5. Return index statistics\n */\n\nimport { execFile } from 'node:child_process';\nimport type { Dirent, Stats } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport { availableParallelism } from 'node:os';\nimport * as path from 'node:path';\nimport type { Context } from '@wrongstack/core/agent';\nimport {\n DEFAULT_WALK_IGNORE_DIRS,\n indexParallelBatchSize,\n isFrugalPerf,\n} from '@wrongstack/core/utils';\nimport { type IgnoreMatcher, loadGitignoreMatcher } from './gitignore.js';\nimport { detectLang, INDEXABLE_EXTENSIONS } from './languages.js';\nimport { parseFileContent } from './parser-dispatch.js';\nimport type { FileMeta, IndexResult, Symbol as IndexSymbol, Ref, SymbolLang } from './schema.js';\nimport { IndexStore } from './writer.js';\n\n/** Yield the event loop every N files so the main thread stays responsive. */\nconst YIELD_EVERY_N = 50;\n\n/**\n * Parallel parse batch size \u2014 see {@link indexParallelBatchSize}.\n * Re-resolved at the start of each index run so env profile changes apply.\n */\nexport function resolveParallelBatch(): number {\n return indexParallelBatchSize(availableParallelism());\n}\n\nfunction yieldEventLoop(): Promise<void> {\n return new Promise((resolve) => setImmediate(resolve));\n}\n\n/**\n * Cooperatively abort if the signal is set. Throws with the signal's reason\n * (or a descriptive Error) so callers know *why* the operation was cancelled.\n * Called at yield points \u2014 never after a Promise resolve (that would be a\n * microtask that the signal check could miss).\n */\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (!signal?.aborted) return;\n if (signal.reason instanceof Error) throw signal.reason;\n throw new Error(typeof signal.reason === 'string' ? signal.reason : 'Indexing cancelled');\n}\n\n/**\n * Detect AbortError (DOMException with name 'AbortError') thrown by signal-aware\n * fs.promises calls (stat, readFile). We must re-throw these so the cancellation\n * propagates \u2014 catching them as ordinary errors would keep the loop running.\n */\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === 'AbortError';\n}\n\nconst DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;\nconst DEFAULT_IGNORE_FILES = new Set(['package-lock.json', 'pnpm-lock.yaml', 'pnpm-lock.yml']);\nconst INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);\nconst MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;\nconst MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;\n\nfunction isWithinProject(projectRoot: string, file: string): boolean {\n const rel = path.relative(projectRoot, file);\n return rel !== '' && !rel.startsWith(`..${path.sep}`) && rel !== '..' && !path.isAbsolute(rel);\n}\n\nfunction isMissingPathError(err: unknown): boolean {\n const code = (err as { code?: unknown } | null)?.code;\n return code === 'ENOENT' || code === 'ENOTDIR';\n}\n\nfunction normalizeComparablePath(value: string): string {\n const resolved = path.resolve(value);\n return process.platform === 'win32' ? resolved.toLowerCase() : resolved;\n}\n\nfunction gitOutput(projectRoot: string, args: string[]): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n execFile(\n 'git',\n ['-C', projectRoot, ...args],\n {\n encoding: 'buffer',\n maxBuffer: MAX_GIT_FILE_LIST_BYTES,\n windowsHide: true,\n },\n (error, stdout) => {\n if (error) reject(error);\n else resolve(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));\n },\n );\n });\n}\n\n/**\n * Git already maintains the canonical tracked/untracked directory index. On a\n * repository root this avoids hundreds of serial `readdir` calls; non-Git\n * projects and nested roots fall back to the filesystem walker below.\n */\nasync function findGitSourceFiles(\n projectRoot: string,\n ignore: string[],\n signal?: AbortSignal | undefined,\n): Promise<{ files: string[]; trustedUnchanged: Set<string> } | null> {\n try {\n throwIfAborted(signal);\n const topLevel = (await gitOutput(projectRoot, ['rev-parse', '--show-toplevel']))\n .toString('utf8')\n .trim();\n if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;\n\n throwIfAborted(signal);\n const ignoreSet = new Set([...DEFAULT_IGNORE, ...ignore]);\n const [output, statusOutput] = await Promise.all([\n gitOutput(projectRoot, ['ls-files', '--cached', '--others', '--exclude-standard', '-z']),\n gitOutput(projectRoot, [\n 'status',\n '--porcelain=v1',\n '-z',\n '--untracked-files=all',\n '--ignored=no',\n ]),\n ]);\n throwIfAborted(signal);\n const dirty = new Set<string>();\n const deleted = new Set<string>();\n const statusRecords = statusOutput.toString('utf8').split('\\0');\n for (let i = 0; i < statusRecords.length; i++) {\n const record = statusRecords[i];\n if (!record) continue;\n const status = record.slice(0, 2);\n const changedPath = path.resolve(projectRoot, record.slice(3));\n dirty.add(changedPath);\n if (status.includes('D')) deleted.add(changedPath);\n if (status.includes('R') || status.includes('C')) {\n const source = statusRecords[++i];\n if (source) dirty.add(path.resolve(projectRoot, source));\n }\n }\n const files: string[] = [];\n for (const relative of output.toString('utf8').split('\\0')) {\n if (!relative) continue;\n const portable = relative.replace(/\\\\/g, '/');\n if (\n portable.split('/').some((segment) => ignoreSet.has(segment)) ||\n DEFAULT_IGNORE_FILES.has(path.posix.basename(portable))\n ) {\n continue;\n }\n const full = path.resolve(projectRoot, relative);\n if (deleted.has(full)) continue;\n const ext = path.extname(relative).toLowerCase();\n if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);\n }\n return {\n files,\n trustedUnchanged: new Set(files.filter((file) => !dirty.has(file))),\n };\n } catch {\n return null;\n }\n}\n\ninterface IndexerOptions {\n projectRoot: string;\n files?: string[] | undefined;\n force?: boolean | undefined;\n langs?: string[] | undefined;\n ignore?: string[] | undefined;\n /** Override the index directory (default: the global per-project dir). */\n indexDir?: string | undefined;\n /**\n * Signal that cancels indexing cooperatively. Polled at yield points\n * (file walk, per-file loop) so a hung filesystem won't lock up the\n * process. When the tool executor's timeout fires, this signal aborts\n * and `runIndexer` throws, releasing the mutex and resetting flags.\n */\n signal?: AbortSignal | undefined;\n /**\n * Per-file progress callback. Injected by the caller instead of imported\n * from the host's module state so the indexer can run inside a worker\n * thread (worker posts progress messages; inline host updates its state).\n */\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nasync function findSourceFiles(\n projectRoot: string,\n ignore: string[],\n isGitIgnored: IgnoreMatcher,\n signal?: AbortSignal | undefined,\n): Promise<{\n files: string[];\n complete: boolean;\n errors: string[];\n trustedUnchanged?: Set<string>;\n}> {\n const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);\n if (gitFiles) {\n return {\n files: gitFiles.files,\n complete: true,\n errors: [],\n trustedUnchanged: gitFiles.trustedUnchanged,\n };\n }\n\n const results: string[] = [];\n const errors: string[] = [];\n let complete = true;\n const ignoreSet = new Set([...DEFAULT_IGNORE, ...ignore]);\n // Extension allow-list from languages.ts \u2014 every mapped language is discovered.\n // Special filenames (Makefile, Dockerfile, \u2026) are accepted via detectLang.\n const indexableExts = new Set(INDEXABLE_EXTENSIONS);\n\n let dirCount = 0;\n\n const walk = async (dir: string): Promise<void> => {\n // Yield + abort check before every readdir so a cancelled indexer\n // doesn't descend deeper into the tree.\n throwIfAborted(signal);\n // Periodically yield the event loop so the main thread stays responsive\n // during deep directory walks (Node 22's fs.promises.readdir doesn't\n // accept AbortSignal, so we rely on cooperative polling).\n if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {\n await yieldEventLoop();\n throwIfAborted(signal);\n }\n let entries: Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n complete = false;\n errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);\n return;\n }\n dirCount++;\n\n for (const e of entries) {\n if (ignoreSet.has(e.name)) continue;\n const full = path.join(dir, e.name);\n // Normalize to forward-slash relative path for pattern matching\n const rel = path.relative(projectRoot, full).replace(/\\\\/g, '/');\n if (e.isDirectory()) {\n // Prune .gitignore'd directories before descending (skips node_modules,\n // build output, and any project-specific ignored dirs).\n if (isGitIgnored(rel, true)) continue;\n await walk(full);\n } else if (e.isFile()) {\n if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;\n const ext = path.extname(e.name).toLowerCase();\n // Fast path: known extension. Slow path: special basenames (Makefile\u2026).\n if (indexableExts.has(ext) || detectLang(full) !== null) {\n results.push(full);\n }\n }\n }\n };\n\n await walk(projectRoot);\n return { files: results, complete, errors };\n}\n\nfunction assignRefsToSymbols(refs: Ref[], symbols: IndexSymbol[]): Ref[] {\n if (refs.length === 0 || symbols.length === 0) return [];\n const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);\n const seen = new Set<string>();\n const assigned: Ref[] = [];\n for (const ref of refs) {\n let owner: IndexSymbol | undefined;\n for (const symbol of ordered) {\n if (symbol.line > ref.line) break;\n owner = symbol;\n }\n // Imports usually appear before the first declaration. Attach them to the\n // first real symbol so file/package dependency graphs retain the module\n // edge without inventing an invalid owner id 0.\n if (!owner && ref.callType === 'import') owner = ordered[0];\n if (!owner || owner.id <= 0) continue;\n const key = `${owner.id}:${ref.toName}:${ref.callType}`;\n if (seen.has(key)) continue;\n seen.add(key);\n assigned.push({ ...ref, fromId: owner.id });\n }\n return assigned;\n}\n\n/** Run a full or incremental index and return statistics. */\nexport async function runIndexer(_ctx: Context, opts: IndexerOptions): Promise<IndexResult> {\n const store = new IndexStore(opts.projectRoot, { indexDir: opts.indexDir });\n try {\n return await runIndexerWithStore(store, opts);\n } finally {\n // Always release the synchronous SQLite connection \u2014 an abort mid-run\n // (executor timeout, session teardown) previously leaked it.\n try {\n store.close();\n } catch {\n /* already closed */\n }\n }\n}\n\nexport async function runIndexerWithStore(\n store: IndexStore,\n opts: IndexerOptions,\n): Promise<IndexResult> {\n const { projectRoot, langs, ignore = [], signal } = opts;\n // Graph semantics changed without a structural SQLite schema change. Keep a\n // separate data-version marker so older running processes do not downgrade\n // and wipe the same shared DB while a new WebUI is being rolled out.\n const relationGraphVersion = '2';\n const refResolutionVersion = '2';\n const force =\n (opts.force ?? false) || store.getMetadata('relation_graph_version') !== relationGraphVersion;\n const needsFullRefResolution =\n force || store.getMetadata('ref_resolution_version') !== refResolutionVersion;\n const startMs = Date.now();\n const errors: string[] = [];\n const langStats: Record<string, number> = {};\n let filesIndexed = 0;\n let symbolsIndexed = 0;\n\n // Honor the project-root .gitignore (skips node_modules, build output, and\n // any project-specific ignored paths) on top of the always-on DEFAULT_IGNORE.\n const isGitIgnored = await loadGitignoreMatcher(projectRoot);\n\n let files: string[];\n /** Set of all files discovered on disk (before language filtering).\n * Used for O(1) stale-file detection instead of stat-ing every\n * previously-indexed file. Null when an explicit file list was given. */\n let discoveredFiles: Set<string> | null = null;\n let discoveryComplete = true;\n let trustedUnchanged: Set<string> | undefined;\n if (opts.files && opts.files.length > 0) {\n // Explicit file list (per-edit / watcher path): keep paths inside the\n // project only and apply both always-on and .gitignore exclusions.\n files = opts.files\n .map((f) => path.resolve(projectRoot, f))\n .filter((f) => {\n if (!isWithinProject(projectRoot, f)) return false;\n const rel = path.relative(projectRoot, f).replace(/\\\\/g, '/');\n return (\n !rel.split('/').some((seg) => DEFAULT_IGNORE.includes(seg)) &&\n !DEFAULT_IGNORE_FILES.has(path.basename(f)) &&\n !isGitIgnored(rel, false)\n );\n });\n } else {\n const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);\n files = discovery.files;\n errors.push(...discovery.errors);\n discoveryComplete = discovery.complete;\n discoveredFiles = new Set(files);\n trustedUnchanged = discovery.trustedUnchanged;\n }\n\n if (langs && langs.length > 0) {\n const langSet = new Set(langs);\n files = files.filter((f) => {\n const lang = detectLang(f);\n return lang ? langSet.has(lang) : false;\n });\n }\n\n if (force) store.clearAll();\n\n // Collect existing file metadata for incremental check\n const existingMeta: Map<string, FileMeta> = new Map();\n if (!force) {\n for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);\n }\n\n // Git has already checked clean tracked files while producing status. Fold\n // their stored counts into the result once, before the async batch loop,\n // instead of creating thousands of promises and scheduler yields merely to\n // rediscover that their mtimes did not change.\n const totalFilesForProgress = files.length;\n let filesPreSkipped = 0;\n if (!force && trustedUnchanged) {\n files = files.filter((file) => {\n const meta = existingMeta.get(file);\n if (!meta || !trustedUnchanged.has(file)) return true;\n langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;\n symbolsIndexed += meta.symbolCount;\n filesIndexed++;\n filesPreSkipped++;\n return false;\n });\n if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);\n }\n\n // Process files in batches for parallel I/O and parsing.\n // SQLite writes remain sequential (they're synchronous and CPU-bound).\n // Batch width follows WRONGSTACK_PERF_PROFILE (frugal \u22644, balanced cores\u00D74).\n const parallelBatch = resolveParallelBatch();\n let filesSinceLastYield = 0;\n for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {\n const batchEnd = Math.min(batchStart + parallelBatch, files.length);\n const batchFiles = files.slice(batchStart, batchEnd);\n\n // Report progress to the caller so UIs can show indexing status.\n opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);\n\n // Yield the event loop periodically so the main thread stays responsive\n // (TUI rendering, input handling, etc.) during large index builds.\n // Uses a running counter instead of batchStart % YIELD_EVERY_N which\n // only works when the batch size divides YIELD_EVERY_N evenly \u2014 with\n // dynamic batch sizes that invariant no longer holds and the yield would\n // fire far less often than intended.\n // Also check for cancellation \u2014 the tool executor's timeout or a\n // session abort propagates through `signal`.\n filesSinceLastYield += batchFiles.length;\n if (filesSinceLastYield >= YIELD_EVERY_N) {\n filesSinceLastYield = 0;\n await yieldEventLoop();\n // Frugal: brief pause so sustained reindex doesn't pin a core.\n if (isFrugalPerf()) {\n await new Promise<void>((r) => setTimeout(r, 8));\n }\n throwIfAborted(signal);\n }\n\n // Phase 1: Parallel stat + incremental skip + read + parse\n const statOpts = signal ? { signal } : {};\n const statReadParse = await Promise.allSettled(\n batchFiles.map(\n async (\n file,\n ): Promise<{\n file: string;\n stat: Stats;\n lang: string;\n parsed: Awaited<ReturnType<typeof parseFileContent>> | null;\n content?: string;\n skippedMeta?: FileMeta;\n error?: string;\n missing?: boolean;\n }> => {\n let stat: Stats;\n try {\n stat = await (\n fs.stat as (path: string, opts: { signal?: AbortSignal }) => Promise<Stats>\n )(file, statOpts);\n } catch (e) {\n if (isAbortError(e)) throw e;\n return {\n file,\n stat: null as never as Stats,\n lang: '',\n parsed: null,\n error: `stat error: ${e instanceof Error ? e.message : String(e)}`,\n missing: isMissingPathError(e),\n };\n }\n if (!stat.isFile()) return { file, stat, lang: '', parsed: null };\n\n const lang = detectLang(file);\n if (!lang) return { file, stat, lang: '', parsed: null };\n if (stat.size > MAX_INDEX_FILE_BYTES) {\n return {\n file,\n stat,\n lang,\n parsed: null,\n error: `file too large (${stat.size} bytes; max ${MAX_INDEX_FILE_BYTES})`,\n };\n }\n\n const meta = existingMeta.get(file);\n if (!force && meta && meta.mtimeMs === Math.floor(stat.mtimeMs)) {\n return { file, stat, lang, parsed: null, skippedMeta: meta };\n }\n\n let content: string;\n try {\n content = await fs.readFile(file, { encoding: 'utf8', signal });\n } catch (e) {\n if (isAbortError(e)) throw e;\n return {\n file,\n stat,\n lang,\n parsed: null,\n error: `read error: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n\n let parsed: Awaited<ReturnType<typeof parseFileContent>>;\n try {\n parsed = await parseFileContent(file, content, lang as SymbolLang);\n } catch (e) {\n return {\n file,\n stat,\n lang,\n parsed: null,\n error: `parse error: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n return { file, stat, lang, parsed, content };\n },\n ),\n );\n\n // Phase 2: Sequential SQLite writes \u2014 amortized across the whole batch.\n //\n // Each file is still parsed in parallel (Phase 1), but the writes\n // happen in a single `commitBatch` transaction per outer batch\n // (PARALLEL_BATCH = 20 files). This drops the commit count from\n // ~5/file to 1/parallel-batch, which is the difference between\n // 100 fsync round-trips and 5 on a 20-file slice.\n const batchEntries: Array<{\n file: string;\n lang: SymbolLang;\n symbols: IndexSymbol[];\n refs: Ref[];\n mtimeMs: number;\n symbolCount: number;\n }> = [];\n const deleteForFiles: string[] = [];\n\n for (let fi = 0; fi < statReadParse.length; fi++) {\n const settled = statReadParse[fi]!;\n const file = expectDefined(batchFiles[fi]);\n\n if (settled.status === 'rejected') {\n const err = settled.reason;\n if (err instanceof Error && isAbortError(err)) throw err;\n errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);\n continue;\n }\n\n const result = settled.value;\n if (result.error) {\n // A missing path in a targeted watcher/edit run is authoritative: the\n // source was deleted or renamed, so remove its previous index rows.\n // Read/parse/permission failures are transient and retain the last good\n // snapshot instead of replacing it with an empty one.\n if (result.missing) store.deleteFile(file);\n errors.push(`${file}: ${result.error}`);\n continue;\n }\n\n const { stat, lang, parsed } = result;\n if (result.skippedMeta) {\n langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;\n symbolsIndexed += result.skippedMeta.symbolCount;\n filesIndexed++;\n continue;\n }\n\n if (!lang || !parsed) {\n if (lang) {\n store.upsertFile({\n file,\n lang: lang as SymbolLang,\n mtimeMs: Math.floor(stat.mtimeMs),\n symbolCount: 0,\n lastIndexed: Date.now(),\n });\n filesIndexed++;\n }\n continue;\n }\n\n // Empty symbol files still need their file row updated so future runs\n // know the mtime. Single transaction clears stale rows + upserts meta.\n if (parsed.symbols.length === 0) {\n store.replaceEmptyFile({\n file,\n lang: lang as SymbolLang,\n mtimeMs: Math.floor(stat.mtimeMs),\n symbolCount: 0,\n lastIndexed: Date.now(),\n });\n filesIndexed++;\n continue;\n }\n\n batchEntries.push({\n file,\n lang: lang as SymbolLang,\n symbols: parsed.symbols,\n refs: parsed.refs ?? [],\n mtimeMs: Math.floor(stat.mtimeMs),\n symbolCount: parsed.symbols.length,\n });\n deleteForFiles.push(file);\n }\n\n if (batchEntries.length > 0) {\n try {\n store.commitBatch(batchEntries, { deleteForFiles });\n for (const entry of batchEntries) {\n const count = entry.symbols.length;\n symbolsIndexed += count;\n langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;\n filesIndexed++;\n }\n } catch (err) {\n // If the batch commit fails, fall back to per-file writes so the\n // user still gets a partial index. Per-file writes are slower but\n // isolate failures.\n const message = err instanceof Error ? err.message : String(err);\n errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);\n for (const entry of batchEntries) {\n try {\n store.deleteRefsForFile(entry.file);\n store.deleteSymbolsForFile(entry.file);\n const symbolsWithIds = store.insertSymbols(entry.symbols);\n symbolsIndexed += symbolsWithIds.length;\n langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;\n filesIndexed++;\n if (entry.refs.length > 0 && symbolsWithIds.length > 0) {\n const fallbackBatch = assignRefsToSymbols(entry.refs, symbolsWithIds);\n if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);\n }\n store.resolveRefsForNames([\n ...entry.symbols.map((symbol) => symbol.name),\n ...entry.refs.map((ref) => ref.toName),\n ]);\n store.upsertFile({\n file: entry.file,\n lang: entry.lang,\n mtimeMs: entry.mtimeMs,\n symbolCount: entry.symbolCount,\n lastIndexed: Date.now(),\n });\n } catch (innerErr) {\n errors.push(\n `fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`,\n );\n }\n }\n }\n }\n }\n\n // Remove stale entries for files deleted since last run.\n // Instead of stat-ing every previously-indexed file (O(total indexed)),\n // derive stale files from the discovered set: any existingMeta entry not\n // in the scanned files is stale. Skip entirely for explicit file lists\n // (targeted reindex \u2014 can't derive stale from a subset).\n if (discoveredFiles && discoveryComplete) {\n for (const [file_] of existingMeta) {\n if (!discoveredFiles.has(file_)) {\n store.deleteFile(file_);\n }\n }\n }\n\n // Batch commits resolve only names touched by that batch. Existing databases\n // get one global repair pass when this contract version changes; subsequent\n // single-file watcher runs avoid rebuilding the full symbol-name map.\n if (needsFullRefResolution) store.resolveRefs();\n store.setMetadata('ref_resolution_version', refResolutionVersion);\n store.setMetadata('relation_graph_version', relationGraphVersion);\n // Planner refresh belongs to full/bulk runs, not the edit watcher hot path.\n if (!opts.files || filesIndexed >= 50) store.optimize();\n\n store.setLastIndexed(Date.now());\n if (!opts.files) store.compactIfNeeded();\n const durationMs = Date.now() - startMs;\n\n return {\n filesIndexed,\n symbolsIndexed,\n langStats,\n durationMs,\n errors,\n };\n}\n", "/**\n * Minimal but faithful `.gitignore` matcher for the indexer.\n *\n * Supports the parts of the gitignore spec that matter for skipping source\n * files: comments / blanks, `!` negation (last match wins), trailing-slash\n * directory-only rules, leading-slash / embedded-slash anchoring, and the\n * `*` / `**` / `?` / `[...]` globs (via core's {@link compileGlob}).\n *\n * Only the project-root `.gitignore` is read. Nested `.gitignore` files are not\n * walked \u2014 the common build/dependency dirs that would live deeper are already\n * covered by the indexer's always-on `DEFAULT_IGNORE`.\n *\n * Known limitation: a `!negated` file inside an ignored directory will not be\n * re-included, because the indexer prunes ignored directories before descending\n * (a large performance win). This matches most lightweight implementations.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { compileGlob } from '@wrongstack/core/utils';\n\nexport type IgnoreMatcher = (relPath: string, isDir: boolean) => boolean;\n\ninterface Rule {\n /** Matches the entry itself or anything under it (for dirs / plain names). */\n eqOrUnder: RegExp;\n /** Matches only entries strictly under it (for dir-only rules on files). */\n under: RegExp;\n negated: boolean;\n dirOnly: boolean;\n}\n\n/** Strip the `^`/`$` anchors compileGlob adds so we can re-anchor ourselves. */\nfunction globBody(glob: string): string {\n return compileGlob(glob).source.replace(/^\\^/, '').replace(/\\$$/, '');\n}\n\n/** Compile a list of raw `.gitignore` lines into a matcher. */\nexport function compileGitignore(lines: string[]): IgnoreMatcher {\n const rules: Rule[] = [];\n\n for (const raw of lines) {\n let line = raw.replace(/\\r$/, '');\n if (!line.trim() || line.trimStart().startsWith('#')) continue;\n line = line.trim();\n\n let negated = false;\n if (line.startsWith('!')) {\n negated = true;\n line = line.slice(1);\n }\n\n let dirOnly = false;\n if (line.endsWith('/')) {\n dirOnly = true;\n line = line.slice(0, -1);\n }\n if (!line) continue;\n\n // A slash anywhere (after the trailing slash is stripped) anchors the\n // pattern to the gitignore's directory (the project root here). A bare name\n // matches at any depth.\n const anchored = line.startsWith('/') || line.includes('/');\n if (line.startsWith('/')) line = line.slice(1);\n\n const body = globBody(line);\n const prefix = anchored ? '^' : '(?:^|.*/)';\n rules.push({\n eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),\n under: new RegExp(`${prefix}${body}/.*$`),\n negated,\n dirOnly,\n });\n }\n\n return (relPath: string, isDir: boolean): boolean => {\n const p = relPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n let ignored = false;\n for (const r of rules) {\n // A directory-only rule never matches a file by its own name; it only\n // matches files that live strictly beneath the named directory.\n const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;\n if (re.test(p)) ignored = !r.negated;\n }\n return ignored;\n };\n}\n\n/** Read `<projectRoot>/.gitignore` and compile it. Missing file \u2192 matches nothing. */\nexport async function loadGitignoreMatcher(projectRoot: string): Promise<IgnoreMatcher> {\n let lines: string[] = [];\n try {\n const raw = await fs.readFile(path.join(projectRoot, '.gitignore'), 'utf8');\n lines = raw.split('\\n');\n } catch {\n // No .gitignore \u2014 nothing extra to ignore beyond the indexer defaults.\n }\n return compileGitignore(lines);\n}\n", "import type { FileSymbols, SymbolLang } from './schema.js';\n\n/**\n * Load only the parser needed for this file. Keeping these imports lazy is\n * important for the project server: TypeScript's compiler API stays out of the\n * SQLite/IPC owner when TS/JS parsing is delegated to parser workers.\n */\nexport async function parseFileContent(\n file: string,\n content: string,\n lang: SymbolLang,\n): Promise<FileSymbols> {\n switch (lang) {\n case 'ts':\n case 'tsx':\n case 'js':\n case 'jsx': {\n const { parseSymbols } = await import('./ts-parser.js');\n return parseSymbols({ file, content, lang });\n }\n case 'go': {\n const { parseSymbols } = await import('./go-parser.js');\n return parseSymbols({ file, content, lang: 'go' });\n }\n case 'py': {\n const { parseSymbols } = await import('./py-parser.js');\n return parseSymbols({ file, content, lang: 'py' });\n }\n case 'rs': {\n const { parseSymbols } = await import('./rs-parser.js');\n return parseSymbols({ file, content, lang: 'rs' });\n }\n case 'json': {\n const { parseSymbols } = await import('./json-parser.js');\n return parseSymbols({ file, content, lang: 'json' });\n }\n case 'yaml': {\n const { parseSymbols } = await import('./yaml-parser.js');\n return parseSymbols({ file, content, lang: 'yaml' });\n }\n default: {\n const { parseSymbols } = await import('./generic-parser.js');\n return parseSymbols({ file, content, lang });\n }\n }\n}\n", "import { expectDefined } from '@wrongstack/core/utils';\n/**\n * SQLite storage layer for the codebase index.\n *\n * Uses `node:sqlite` (synchronous API \u2014 DatabaseSync class).\n * Database file: ~/.wrongstack/projects/<hash>/codebase-index/index.db \u2014 kept\n * out of the repo so it never clutters the working tree or needs gitignoring.\n *\n * ### Multi-process safety\n *\n * Several wstack surfaces (TUI, WebUI, parallel terminals) share this per-project\n * database. WAL mode allows concurrent reads alongside a writer, and\n * `busy_timeout` bounds how long a write operation waits for the lock. When\n * the timeout expires and SQLite returns SQLITE_BUSY, the store retries with\n * exponential backoff (up to 3 attempts) before letting the error propagate.\n * If all retries are exhausted, a {@link LockError} is thrown \u2014 the circuit\n * breaker treats this as a transient condition and does NOT count it as a failure.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { type Bm25Index, buildBm25Index, buildIndexableText, tokenise } from './bm25.js';\nimport { lspKindToInternalKind } from './lsp-kind.js';\nimport type {\n CodeMapGraph,\n FileMeta,\n IndexStats,\n Symbol as IndexSymbol,\n Ref,\n SearchResult,\n SymbolKind,\n SymbolLang,\n} from './schema.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { loadDatabaseSync, runSqliteWithRetry } from './sqlite-runtime.js';\nimport {\n getAllFileMetasWithStatement,\n getAllIndexableWithStatement,\n getFileMetaWithStatement,\n getMaxSymbolIdWithStatement,\n getMetadataWithStatement,\n getStatsWithStatement,\n} from './writer-admin.js';\nimport {\n type BulkSymbolRow,\n bulkInsertFtsWithStatement,\n bulkInsertRefsWithStatement,\n bulkInsertSymbolsWithStatement,\n} from './writer-bulk-insert.js';\nimport {\n findRefsFromWithStatement,\n findRefsToWithStatement,\n getFileGraphWithStatement,\n getPackageGraphWithStatement,\n getSymbolGraphWithStatement,\n} from './writer-graph-reader.js';\nimport { assignRefsToSymbols, escapeLike, resolveIndexDir } from './writer-helpers.js';\nimport { applyIndexStorePragmas } from './writer-pragmas.js';\nimport {\n CORE_TABLES_SQL,\n METADATA_TABLE_SQL,\n REFS_INDEX_SQL,\n REFS_TABLE_SQL,\n SYMBOL_INDEX_SQL,\n SYMBOLS_FTS_SQL,\n} from './writer-schema.js';\nimport {\n buildWriterSearchWhere,\n mapWriterSearchRow,\n normalizeSearchLimit,\n type WriterSearchFilter,\n type WriterSearchRow,\n} from './writer-search-helpers.js';\nimport { StorePool } from './writer-store-pool.js';\n\nexport { codebaseIndexDirOverride, resolveIndexDir } from './writer-helpers.js';\nexport { StorePool } from './writer-store-pool.js';\n\nconst DB_FILE = 'index.db';\n\nexport class IndexStore {\n private db: DatabaseSync;\n /** Absolute path to this project's index directory. */\n private readonly indexDir: string;\n /**\n * True when the SQLite build provides FTS5 (Node's bundled SQLite does).\n * When false, ranked search falls back to the LIKE + in-process BM25 path.\n */\n private ftsAvailable = false;\n\n /**\n * Cache of prepared statements keyed by their SQL text. `DatabaseSync`\n * compiles SQL on every `.prepare()` call; for the fixed-SQL methods\n * (upsertFile, getFileMeta, deleteFile, insertRefs, \u2026) that runs thousands\n * of times during a full reindex. `StatementSync` objects are reusable\n * across calls on the same connection, so we compile each distinct SQL once\n * and reuse it. Cleared in {@link close} when the connection is torn down.\n */\n private readonly stmtCache = new Map<string, ReturnType<DatabaseSync['prepare']>>();\n /**\n * Cached full-corpus BM25 index for the FTS5-unavailable fallback path.\n * Built lazily on the first `searchRankedFallback` call and invalidated\n * (via `bm25Dirty`) whenever the `symbols` table is mutated. Computing\n * IDF over the full corpus is also more correct than the old per-query\n * candidate-subset IDF.\n *\n * Cache-lifecycle invariants (single source of truth lives at the\n * `invalidateBm25()` helper \u2014 see its docblock for the \"every mutation\n * MUST call this\" contract):\n * - declaration: this field + `bm25Dirty` (here)\n * - invalidation: `invalidateBm25()` flips the flag and nulls the cache\n * - build: `getOrBuildBm25()` rebuilds against current `symbols` rows\n * - teardown: `close()` resets the flag and nulls the cache\n */\n private bm25Cache: Bm25Index | null = null;\n // Dirty on open so the first getOrBuildBm25() rebuilds against current rows;\n // an empty or pre-existing corpus makes a stale IDF table meaningless.\n private bm25Dirty = true;\n\n /** Prepare-once helper: compile `sql` on first use, reuse thereafter. */\n private stmt(sql: string): ReturnType<DatabaseSync['prepare']> {\n let s = this.stmtCache.get(sql);\n if (s === undefined) {\n s = this.db.prepare(sql);\n this.stmtCache.set(sql, s);\n }\n return s;\n }\n\n constructor(projectRoot: string, opts: { indexDir?: string | undefined } = {}) {\n this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);\n fs.mkdirSync(this.indexDir, { recursive: true });\n const Database = loadDatabaseSync();\n this.db = new Database(path.join(this.indexDir, DB_FILE));\n applyIndexStorePragmas(this.db);\n this.initSchema();\n }\n\n runWithRetry<T>(fn: () => T): T {\n return runSqliteWithRetry(fn);\n }\n\n private initSchema(): void {\n this.db.exec(METADATA_TABLE_SQL);\n\n // Schema migration: the index is derived, rebuildable data \u2014 on any\n // version mismatch we drop everything and let the next index run repopulate\n // from source, instead of maintaining per-version migration scripts.\n const storedRows = this.stmt('SELECT value FROM metadata WHERE key = ?').all('version') as {\n value: string;\n }[];\n const storedVersion = storedRows.length ? Number(storedRows[0]?.value) : null;\n if (storedVersion !== null && storedVersion !== SCHEMA_VERSION) {\n this.db.exec(`\n DROP TABLE IF EXISTS symbols;\n DROP TABLE IF EXISTS files;\n DROP TABLE IF EXISTS refs;\n `);\n this.db.exec('DROP TABLE IF EXISTS symbols_fts');\n this.stmt('UPDATE metadata SET value = ? WHERE key = ?').run(\n String(SCHEMA_VERSION),\n 'version',\n );\n } else if (storedVersion === null) {\n this.stmt('INSERT INTO metadata(key, value) VALUES (?, ?)').run(\n 'version',\n String(SCHEMA_VERSION),\n );\n }\n\n this.db.exec(CORE_TABLES_SQL);\n for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);\n this.db.exec(REFS_TABLE_SQL);\n for (const sql of REFS_INDEX_SQL) this.db.exec(sql);\n\n // FTS5 full-text index over the camelCase-split symbol text; rowid is the\n // symbol id. Replaces the old `LIKE '%token%'` full-table scan + per-query\n // in-process BM25 build: MATCH uses the inverted index and bm25() ranks\n // natively. Kept in sync explicitly in insertSymbols/delete*/clearAll.\n try {\n this.db.exec(SYMBOLS_FTS_SQL);\n this.ftsAvailable = true;\n // A database may have been populated by a runtime without FTS5. Backfill\n // the derived table when FTS later becomes available instead of making\n // every historical symbol invisible until a forced rebuild.\n const symbolCount = Number(\n (this.stmt('SELECT COUNT(*) AS n FROM symbols').get() as { n?: number } | undefined)?.n ??\n 0,\n );\n const ftsCount = Number(\n (this.stmt('SELECT COUNT(*) AS n FROM symbols_fts').get() as { n?: number } | undefined)\n ?.n ?? 0,\n );\n if (symbolCount !== ftsCount) {\n this.db.exec('DELETE FROM symbols_fts');\n const rows = this.stmt(\n 'SELECT id, name, signature, doc_comment FROM symbols ORDER BY id',\n ).all() as Array<{ id: number; name: string; signature: string; doc_comment: string }>;\n bulkInsertFtsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n this.ftsAvailable,\n rows.map((row) => ({\n id: row.id,\n text: buildIndexableText(row.name, row.signature, row.doc_comment),\n })),\n );\n // The drift repair doesn't mutate `symbols`, but the drift may have\n // been caused by an external mutation that left the BM25 cache stale.\n // Invalidate so the next fallback search rebuilds from the repaired\n // FTS state rather than serving a frozen corpus.\n this.invalidateBm25();\n }\n } catch {\n // SQLite built without FTS5 \u2014 searchRanked falls back to LIKE + BM25.\n this.ftsAvailable = false;\n }\n\n // Seed the symbol-id sequence once. Subsequent allocations are O(1)\n // metadata updates instead of SELECT MAX(id) on every insert batch.\n this.ensureNextSymbolIdSeeded();\n }\n\n // \u2500\u2500\u2500 ID allocation & bulk helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private static readonly NEXT_SYMBOL_ID_KEY = 'next_symbol_id';\n /** Stay under typical SQLite SQLITE_MAX_VARIABLE_NUMBER (often 999). */\n private static readonly MAX_SQL_VARS = 900;\n\n /**\n * Ensure `metadata.next_symbol_id` exists. Safe to call outside a write\n * transaction on open; the first concurrent writer under BEGIN IMMEDIATE\n * re-reads and advances the counter atomically.\n */\n private ensureNextSymbolIdSeeded(): void {\n const existing = this.stmt('SELECT value FROM metadata WHERE key = ?').get(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n ) as { value?: string } | undefined;\n if (existing?.value !== undefined) return;\n const maxRows = this.stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n const next = (maxRows[0]?.m ?? 0) + 1;\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)').run(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n String(next),\n );\n }\n\n /**\n * Reserve `count` consecutive symbol ids. MUST run inside BEGIN IMMEDIATE\n * so concurrent indexers cannot hand out overlapping ranges.\n */\n private allocateSymbolIds(count: number): number {\n if (count <= 0) return this.getMaxSymbolId() + 1;\n this.ensureNextSymbolIdSeeded();\n const row = this.stmt('SELECT value FROM metadata WHERE key = ?').get(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n ) as { value?: string } | undefined;\n const start = Math.max(1, Number(row?.value ?? 1) || 1);\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)').run(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n String(start + count),\n );\n return start;\n }\n\n /**\n * Disconnect inbound refs before their target symbols are replaced and\n * return the affected names for scoped re-resolution.\n *\n * This also repairs a long-standing dangling-id edge case: `refs.to_id` has\n * no physical FK, so deleting a symbol previously left callers pointing at a\n * non-existent row.\n */\n private invalidateIncomingRefsForFiles(files: string[]): string[] {\n if (files.length === 0) return [];\n const placeholders = files.map(() => '?').join(',');\n const names = (\n this.stmt(`SELECT DISTINCT name FROM symbols WHERE file IN (${placeholders})`).all(\n ...files,\n ) as Array<{ name: string }>\n ).map((row) => row.name);\n this.stmt(\n `UPDATE refs SET to_id = NULL\n WHERE to_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n ).run(...files);\n return names;\n }\n\n /** Resolve only refs whose target names may have changed. */\n private resolveRefsForNamesUnsafe(names: Iterable<string>): number {\n const unique = [...new Set(names)].filter(Boolean);\n let changes = 0;\n for (let start = 0; start < unique.length; start += IndexStore.MAX_SQL_VARS) {\n const chunk = unique.slice(start, start + IndexStore.MAX_SQL_VARS);\n const placeholders = chunk.map(() => '?').join(',');\n const result = this.stmt(\n `UPDATE refs\n SET to_id = (SELECT MIN(id) FROM symbols WHERE name = refs.to_name)\n WHERE to_name IN (${placeholders})`,\n ).run(...chunk) as { changes?: number };\n changes += result.changes ?? 0;\n }\n return changes;\n }\n\n // \u2500\u2500\u2500 Symbol CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert symbols, assigning IDs atomically inside `BEGIN IMMEDIATE` /\n * `COMMIT`. Id ranges come from the `next_symbol_id` metadata counter\n * (O(1)); multi-row INSERT amortizes bind overhead for large files.\n *\n * @returns The symbols array with `id` fields populated so the caller can\n * use them for refs without re-reading from the DB.\n */\n insertSymbols(symbols: IndexSymbol[]): IndexSymbol[] {\n this.invalidateBm25();\n return this.runWithRetry(() => {\n // BEGIN IMMEDIATE serializes writers so allocateSymbolIds cannot overlap.\n this.db.exec('BEGIN IMMEDIATE');\n try {\n let nextId = this.allocateSymbolIds(symbols.length);\n const result: IndexSymbol[] = [];\n const bulk: BulkSymbolRow[] = [];\n const ftsRows: Array<{ id: number; text: string }> = [];\n\n for (const s of symbols) {\n const id = nextId++;\n bulk.push({\n id,\n lang: s.lang,\n kind: s.kind,\n name: s.name,\n file: s.file,\n line: s.line,\n col: s.col,\n signature: s.signature,\n docComment: s.docComment,\n scope: s.scope,\n text: s.text,\n });\n if (this.ftsAvailable) {\n ftsRows.push({ id, text: buildIndexableText(s.name, s.signature, s.docComment) });\n }\n result.push({ ...s, id });\n }\n bulkInsertSymbolsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, bulk);\n bulkInsertFtsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n this.ftsAvailable,\n ftsRows,\n );\n\n this.db.exec('COMMIT');\n return result;\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n deleteSymbolsForFile(file: string): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = this.invalidateIncomingRefsForFiles([file]);\n if (this.ftsAvailable) {\n this.stmt(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(file);\n }\n this.stmt('DELETE FROM symbols WHERE file_fk = ?').run(file);\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n } catch (error) {\n this.db.exec('ROLLBACK');\n throw error;\n }\n });\n }\n\n /**\n * Remove every trace of a file (refs, symbols, FTS rows, file meta). Used\n * when a source file disappears between index runs \u2014 previously this only\n * dropped the `files` row, leaving its symbols orphaned but still searchable.\n */\n deleteFile(file: string): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = this.invalidateIncomingRefsForFiles([file]);\n if (this.ftsAvailable) {\n this.stmt(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(file);\n }\n this.stmt(\n 'DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(file);\n this.stmt('DELETE FROM symbols WHERE file_fk = ?').run(file);\n this.stmt('DELETE FROM files WHERE file = ?').run(file);\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n // \u2500\u2500\u2500 File metadata \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n upsertFile(meta: FileMeta): void {\n this.runWithRetry(() => {\n this.stmt(\n `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(file) DO UPDATE SET\n lang = excluded.lang,\n mtime_ms = excluded.mtime_ms,\n symbol_count = excluded.symbol_count,\n last_indexed = excluded.last_indexed`,\n ).run(meta.file, meta.lang, meta.mtimeMs, meta.symbolCount, meta.lastIndexed);\n });\n }\n\n getFileMeta(file: string): FileMeta | null {\n return getFileMetaWithStatement((sql) => this.stmt(sql), file);\n }\n\n getAllFileMetas(): FileMeta[] {\n return getAllFileMetasWithStatement((sql) => this.stmt(sql));\n }\n\n // \u2500\u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n search(\n query: string,\n filter?: WriterSearchFilter,\n opts?: { limit?: number | undefined },\n ): SearchResult[] {\n const built = this.buildSearchWhere(query, filter);\n if (built === null) return [];\n\n const { where, values } = built;\n const limit = normalizeSearchLimit(opts?.limit);\n const limitSql = limit !== undefined ? ' LIMIT ?' : '';\n const sql = `SELECT id, lang, kind, name, file, line, col, signature, doc_comment, text FROM symbols ${where}${limitSql}`;\n\n const binds = limit !== undefined ? [...values, limit] : values;\n const rows = this.stmt(sql).all(\n ...(binds as (string | number)[]),\n ) as unknown as WriterSearchRow[];\n\n return rows.map((row) => mapWriterSearchRow(row, filter?.lspKind));\n }\n\n /** Shared WHERE builder for {@link search} / empty-query ranked totals. */\n private buildSearchWhere(query: string, filter?: WriterSearchFilter | undefined) {\n return buildWriterSearchWhere(query, filter);\n }\n\n private countSearch(query: string, filter?: WriterSearchFilter | undefined): number {\n const built = this.buildSearchWhere(query, filter);\n if (built === null) return 0;\n const row = this.stmt(`SELECT COUNT(*) AS n FROM symbols ${built.where}`).get(\n ...(built.values as (string | number)[]),\n ) as { n?: number } | undefined;\n return Number(row?.n ?? 0);\n }\n\n /**\n * Ranked search \u2014 the one-stop query the codebase-search tool and plug-lsp\n * use. With FTS5 this is a single indexed `MATCH` ranked by SQLite's native\n * `bm25()` with a built-in `snippet()`; without FTS5 it falls back to the\n * legacy LIKE scan + in-process BM25 (identical semantics, slower).\n *\n * Tokens are matched as prefixes (`\"tok\"*`), mirroring the old\n * `LIKE '%tok%'` recall for the common symbol-search shapes (\"user\" finds\n * \"users\", camelCase-split text makes \"complex\" find \"complexOperation\").\n */\n searchRanked(\n query: string,\n filter: WriterSearchFilter | undefined,\n limit: number,\n ): { results: SearchResult[]; total: number } {\n const rawLimit = Number.isFinite(limit) ? Math.trunc(limit) : 20;\n const safeLimit = Math.max(1, Math.min(rawLimit, 100));\n const tokens = tokenise(query);\n // No usable tokens \u2192 plain filtered listing (matches old `search('')`).\n if (tokens.length === 0 || !this.ftsAvailable) {\n return this.searchRankedFallback(query, filter, safeLimit);\n }\n\n let effectiveKind: SymbolKind | undefined = filter?.kind;\n if (filter?.lspKind !== undefined) {\n const mapped = lspKindToInternalKind(filter.lspKind);\n if (mapped === null) return { results: [], total: 0 };\n effectiveKind = mapped;\n }\n\n // Each token is quoted (neutralises FTS5 query syntax) and prefix-starred.\n const match = tokens.map((t) => `\"${t.replaceAll('\"', '')}\"*`).join(' OR ');\n\n const conditions: string[] = ['symbols_fts MATCH ?'];\n const values: (string | number)[] = [match];\n if (effectiveKind) {\n conditions.push('s.kind = ?');\n values.push(effectiveKind);\n }\n if (filter?.lang) {\n conditions.push('s.lang = ?');\n values.push(filter.lang);\n }\n if (filter?.file) {\n conditions.push(\"replace(s.file, '\\\\', '/') LIKE ? ESCAPE '\\\\'\");\n values.push(`%${escapeLike(filter.file.replace(/\\\\/g, '/'))}%`);\n }\n const where = conditions.join(' AND ');\n\n const countRows = this.stmt(\n `SELECT COUNT(*) AS n FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid WHERE ${where}`,\n ).all(...values) as { n: number }[];\n const total = countRows[0] ? Number(countRows[0].n) : 0;\n if (total === 0) return { results: [], total: 0 };\n\n const rows = this.stmt(\n `SELECT s.id, s.lang, s.kind, s.name, s.file, s.line, s.col, s.signature, s.doc_comment,\n -bm25(symbols_fts) AS score,\n snippet(symbols_fts, 0, '', '', '\u2026', 12) AS snippet\n FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid\n WHERE ${where}\n ORDER BY\n CASE WHEN lower(s.name) = lower(?) THEN 0\n WHEN lower(s.name) LIKE lower(?) ESCAPE '\\\\' THEN 1\n ELSE 2 END,\n bm25(symbols_fts), lower(s.name), s.file, s.line, s.col, s.id\n LIMIT ?`,\n ).all(...values, query.trim(), `${escapeLike(query.trim())}%`, safeLimit) as {\n id: number;\n lang: string;\n kind: string;\n name: string;\n file: string;\n line: number;\n col: number;\n signature: string;\n doc_comment: string;\n score: number;\n snippet: string;\n }[];\n\n return {\n results: rows.map((row) =>\n mapWriterSearchRow(row, filter?.lspKind, Math.max(0.0001, row.score), row.snippet),\n ),\n total,\n };\n }\n\n /**\n * Invalidate the cached BM25 index.\n *\n * **Contract: every method that mutates `symbols` MUST call this before\n * returning.** (`refs` mutations do not affect the BM25 fallback because\n * the corpus is built from `symbols.text` via `getAllIndexable()` and the\n * BM25 score is filtered by the LIKE-selected candidate set in\n * `searchRankedFallback`.) Today the call sites are `repairDrift`,\n * `insertSymbols`, `deleteSymbolsForFile`, `deleteFile`, `clearAll`, and\n * `commitBatch`. A future mutation that adds a new write path (e.g.\n * `renameFile`, `updateSignature`) MUST also call this \u2014 otherwise the\n * FTS5-unavailable fallback will serve stale search results. The\n * `close()` reset at L1820-1821 tears the cache down on store shutdown,\n * which is the only legitimate place that flips the flag outside this\n * helper.\n *\n * Called *before* `runWithRetry` on purpose: if the write fails all\n * retries the flag stays set, forcing a rebuild on the next search rather\n * than trusting a cache that may not reflect the intended mutation.\n * Do not move this inside the retry closure.\n */\n private invalidateBm25(): void {\n this.bm25Dirty = true;\n this.bm25Cache = null;\n }\n\n /**\n * Return the cached full-corpus BM25 index, rebuilding it only when the\n * symbols table has been mutated since the last build. The full-corpus IDF\n * is more correct than the old per-query candidate-subset IDF, and the\n * amortized build cost drops from O(symbols \u00D7 tokens) per search to once\n * per write batch.\n *\n * Note: the first call after a long idle (or on a freshly opened store)\n * pays the full corpus rebuild synchronously on the search path. For a\n * 5 500+ symbol corpus this is a visible one-time latency spike.\n */\n private getOrBuildBm25(): Bm25Index {\n if (this.bm25Cache && !this.bm25Dirty) return this.bm25Cache;\n const docs = this.getAllIndexable();\n this.bm25Cache = buildBm25Index(docs);\n this.bm25Dirty = false;\n return this.bm25Cache;\n }\n\n /** Legacy ranked path: LIKE candidates + in-process BM25 + JS snippets. */\n private searchRankedFallback(\n query: string,\n filter: WriterSearchFilter | undefined,\n limit: number,\n ): { results: SearchResult[]; total: number } {\n // Empty query = filtered listing: push LIMIT into SQL so a 10k-symbol\n // corpus never materializes fully just to take the first N rows.\n if (!query.trim()) {\n const total = this.countSearch(query, filter);\n if (total === 0) return { results: [], total: 0 };\n return { results: this.search(query, filter, { limit }), total };\n }\n\n const candidates = this.search(query, filter);\n if (candidates.length === 0) return { results: [], total: 0 };\n\n const candidateById = new Map(candidates.map((c) => [c.id, c]));\n // Use the cached full-corpus BM25 index instead of rebuilding from the\n // LIKE candidate subset on every query. The filter restricts scoring to\n // candidates; full-corpus IDF is more correct than subset IDF.\n const bm25 = this.getOrBuildBm25();\n const scored = bm25.score(query, (id) => candidateById.has(id));\n const q = query.trim().toLowerCase();\n const rank = (id: number): number => {\n const name = candidateById.get(id)?.name.toLowerCase() ?? '';\n if (name === q) return 0;\n if (name.startsWith(q)) return 1;\n return 2;\n };\n scored.sort((a, b) => {\n const rankDiff = rank(a.id) - rank(b.id);\n if (rankDiff !== 0) return rankDiff;\n const scoreDiff = b.score - a.score;\n if (scoreDiff !== 0) return scoreDiff;\n const left = expectDefined(candidateById.get(a.id));\n const right = expectDefined(candidateById.get(b.id));\n return (\n left.name.localeCompare(right.name) ||\n left.file.localeCompare(right.file) ||\n left.line - right.line ||\n left.col - right.col ||\n left.id - right.id\n );\n });\n const qTokens = tokenise(query);\n\n const results = scored.slice(0, limit).map(({ id, score }) => {\n const c = expectDefined(candidateById.get(id));\n return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };\n });\n return { results, total: candidates.length };\n }\n\n getAllIndexable(): Array<{ id: number; text: string }> {\n return getAllIndexableWithStatement((sql) => this.stmt(sql));\n }\n\n /**\n * Largest symbol id currently in the table (0 when empty). New ids must be\n * allocated from this, NOT from `COUNT(*)`: incremental reindexes delete a\n * changed file's rows, so the row count drops below the max id and a\n * count-based id would collide with a surviving row (UNIQUE constraint on\n * `symbols.id`). Ids may have gaps \u2014 that is fine.\n */\n getMaxSymbolId(): number {\n return getMaxSymbolIdWithStatement((sql) => this.stmt(sql));\n }\n\n // \u2500\u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): IndexStats {\n return getStatsWithStatement((sql) => this.stmt(sql), this.indexDir);\n }\n\n setLastIndexed(ts: number): void {\n this.runWithRetry(() => {\n this.stmt(\"INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)\").run(\n String(ts),\n );\n });\n }\n\n getMetadata(key: string): string | undefined {\n return getMetadataWithStatement((sql) => this.stmt(sql), key);\n }\n\n setMetadata(key: string, value: string): void {\n this.runWithRetry(() => {\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES(?, ?)').run(key, value);\n });\n }\n\n clearAll(): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n // DROP+CREATE is O(1) page-truncation vs DELETE's full-table scan.\n // Force-rebuilds (schema version change, manual /codebase-reindex)\n // were spending hundreds of ms on row-by-row deletion of thousands of\n // symbols and refs. DROP is instant regardless of table size.\n this.db.exec('DROP TABLE IF EXISTS refs');\n this.db.exec('DROP TABLE IF EXISTS symbols');\n this.db.exec('DROP TABLE IF EXISTS files');\n this.db.exec('DROP TABLE IF EXISTS metadata');\n if (this.ftsAvailable) this.db.exec('DROP TABLE IF EXISTS symbols_fts');\n this.db.exec('COMMIT');\n // Clear statement cache \u2014 prepared stmts reference the now-dropped tables.\n this.stmtCache.clear();\n this.initSchema();\n // Reset the symbol-id counter to 1 so repeated forced rebuilds\n // don't allocate from a monotonically growing id namespace.\n this.stmt('INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)').run(\n IndexStore.NEXT_SYMBOL_ID_KEY,\n '1',\n );\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n // \u2500\u2500\u2500 Ref CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Insert cross-references for a given source symbol id.\n * Replaces any existing refs from the same source (idempotent on re-index).\n */\n insertRefs(fromId: number, refs: Ref[]): void {\n this.runWithRetry(() => {\n // Delete old refs from this symbol (handles re-index)\n this.stmt('DELETE FROM refs WHERE from_id = ?').run(fromId);\n if (refs.length === 0) return;\n bulkInsertRefsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n refs.map((ref) => ({ ...ref, fromId })),\n );\n });\n }\n\n /**\n * Bulk-insert refs for many source symbols in a single transaction.\n *\n * Unlike {@link insertRefs} this does NOT delete per source id \u2014 the caller\n * (the indexer) has already cleared stale refs for the file via\n * {@link deleteRefsForFile}, so the per-source DELETE would be redundant work\n * repeated once per symbol. One transaction for the whole file instead of one\n * per symbol turns an O(symbols) transaction count into O(1).\n *\n * Each ref's own {@link Ref.fromId} is used; pass an empty array to no-op.\n */\n insertRefsBatch(refs: Ref[]): void {\n if (refs.length === 0) return;\n this.runWithRetry(() => {\n bulkInsertRefsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, refs);\n });\n }\n\n /**\n * Commit a batch of file-level symbol/refs/upserts in a single transaction.\n *\n * Used by the indexer to amortize SQLite commit overhead across many files.\n * Before this, the indexer issued one transaction per file (BEGIN IMMEDIATE\n * for symbols, plus per-file deletes and an upsertFile call), so a 20-file\n * parallel batch cost ~5+ transactions \u00D7 20 files = 100+ commits. With\n * this entry point we do exactly one BEGIN/COMMIT per parallel batch.\n *\n * Each entry must already be a fully-parsed FileSymbols (symbols + refs).\n * The caller is responsible for the per-file prefix accounting\n * (refsByLine \u2192 flat list with `fromId` populated). `deleteForFiles` lets\n * the caller clear stale symbols/refs for any files being re-indexed before\n * the inserts run (required to keep refs \u2192 symbols FK invariants).\n *\n * Returns the symbols back with their assigned `id` (same shape as\n * {@link insertSymbols}) so callers can build final per-file results.\n */\n commitBatch(\n entries: Array<{\n file: string;\n lang: SymbolLang;\n symbols: IndexSymbol[];\n refs: Ref[];\n mtimeMs: number;\n symbolCount: number;\n }>,\n options: { deleteForFiles?: string[] | undefined } = {},\n ): IndexSymbol[] {\n if (entries.length === 0 && (options.deleteForFiles?.length ?? 0) === 0) {\n return [];\n }\n this.invalidateBm25();\n return this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = new Set<string>();\n for (const entry of entries) {\n for (const symbol of entry.symbols) affectedNames.add(symbol.name);\n for (const ref of entry.refs) affectedNames.add(ref.toName);\n }\n // 1) Clear stale refs+symbols for any files being re-indexed.\n if (options.deleteForFiles && options.deleteForFiles.length > 0) {\n const placeholders = options.deleteForFiles.map(() => '?').join(',');\n for (const name of this.invalidateIncomingRefsForFiles(options.deleteForFiles)) {\n affectedNames.add(name);\n }\n if (this.ftsAvailable) {\n this.stmt(\n `DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n ).run(...options.deleteForFiles);\n }\n // Refs first (FK direction: refs.from_id \u2192 symbols.id).\n this.stmt(\n `DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n ).run(...options.deleteForFiles);\n this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(\n ...options.deleteForFiles,\n );\n }\n\n // 2) Assign ids + multi-row insert symbols (+ FTS).\n const totalSymbols = entries.reduce((n, e) => n + e.symbols.length, 0);\n let nextId = this.allocateSymbolIds(totalSymbols);\n\n const allInserted: IndexSymbol[] = [];\n const refsToInsert: Ref[] = [];\n const bulkSyms: BulkSymbolRow[] = [];\n const ftsRows: Array<{ id: number; text: string }> = [];\n\n for (const entry of entries) {\n const insertedForEntry: IndexSymbol[] = [];\n for (const s of entry.symbols) {\n const id = nextId++;\n bulkSyms.push({\n id,\n lang: s.lang,\n kind: s.kind,\n name: s.name,\n file: s.file,\n line: s.line,\n col: s.col,\n signature: s.signature,\n docComment: s.docComment,\n scope: s.scope,\n text: s.text,\n });\n if (this.ftsAvailable) {\n ftsRows.push({\n id,\n text: buildIndexableText(s.name, s.signature, s.docComment),\n });\n }\n const inserted = { ...s, id };\n allInserted.push(inserted);\n insertedForEntry.push(inserted);\n }\n refsToInsert.push(...assignRefsToSymbols(entry.refs, insertedForEntry));\n }\n\n bulkInsertSymbolsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, bulkSyms);\n bulkInsertFtsWithStatement(\n (sql) => this.stmt(sql),\n IndexStore.MAX_SQL_VARS,\n this.ftsAvailable,\n ftsRows,\n );\n\n // 3) Multi-row insert all refs.\n bulkInsertRefsWithStatement((sql) => this.stmt(sql), IndexStore.MAX_SQL_VARS, refsToInsert);\n\n // 4) Upsert file metadata for every entry (small N \u2014 single-row is fine).\n const upsertStmt = this.stmt(\n `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(file) DO UPDATE SET\n lang = excluded.lang,\n mtime_ms = excluded.mtime_ms,\n symbol_count = excluded.symbol_count,\n last_indexed = excluded.last_indexed`,\n );\n const now = Date.now();\n for (const entry of entries) {\n upsertStmt.run(entry.file, entry.lang, entry.mtimeMs, entry.symbolCount, now);\n }\n\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n return allInserted;\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n /**\n * Delete all refs whose source symbols are in a given file.\n * Used when re-indexing a file to clear stale refs.\n */\n deleteRefsForFile(file: string): void {\n this.runWithRetry(() => {\n this.stmt('DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)').run(\n file,\n );\n });\n }\n\n /**\n * Resolve `to_name` \u2192 `to_id` for all refs that have a name but no id.\n * Call this after all symbols have been inserted to fill in cross-references.\n *\n * Single statement: the `to_name IN (SELECT name FROM symbols)` guard restricts\n * the UPDATE to refs that will actually resolve, so `.changes` counts only refs\n * that found a target \u2014 matching the previous per-row loop's return value.\n */\n resolveRefs(): number {\n return this.runWithRetry(() => {\n // Prefer UPDATE-FROM with a pre-aggregated name\u2192id map (SQLite \u2265 3.33).\n // One hash join instead of a correlated subquery per unresolved row.\n // MIN(id) matches the previous LIMIT 1 / arbitrary-first semantics when\n // multiple symbols share a name.\n try {\n const result = this.stmt(\n `UPDATE refs\n SET to_id = s.id\n FROM (\n SELECT name, MIN(id) AS id FROM symbols GROUP BY name\n ) AS s\n WHERE refs.to_id IS NULL\n AND refs.to_name IS NOT NULL\n AND refs.to_name = s.name`,\n ).run() as { changes?: number };\n return result.changes ?? 0;\n } catch {\n const result = this.stmt(\n `UPDATE refs SET to_id = (\n SELECT id FROM symbols WHERE name = refs.to_name LIMIT 1\n ) WHERE to_id IS NULL AND to_name IS NOT NULL\n AND to_name IN (SELECT name FROM symbols)`,\n ).run() as { changes?: number };\n return result.changes ?? 0;\n }\n });\n }\n\n resolveRefsForNames(names: Iterable<string>): number {\n return this.runWithRetry(() => this.resolveRefsForNamesUnsafe(names));\n }\n\n /**\n * Clear symbols/refs for a file and mark it as indexed with zero symbols.\n * Used by the indexer for empty-parse results so three writes share one txn.\n */\n replaceEmptyFile(meta: FileMeta): void {\n this.invalidateBm25();\n this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const affectedNames = this.invalidateIncomingRefsForFiles([meta.file]);\n if (this.ftsAvailable) {\n this.stmt(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(meta.file);\n }\n this.stmt(\n 'DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file_fk = ?)',\n ).run(meta.file);\n this.stmt('DELETE FROM symbols WHERE file_fk = ?').run(meta.file);\n this.stmt(\n `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(file) DO UPDATE SET\n lang = excluded.lang,\n mtime_ms = excluded.mtime_ms,\n symbol_count = excluded.symbol_count,\n last_indexed = excluded.last_indexed`,\n ).run(meta.file, meta.lang, meta.mtimeMs, meta.symbolCount, meta.lastIndexed);\n this.resolveRefsForNamesUnsafe(affectedNames);\n this.db.exec('COMMIT');\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n });\n }\n\n /** Best-effort query planner refresh after a large reindex. */\n optimize(): void {\n try {\n this.db.exec('PRAGMA optimize');\n } catch {\n /* optional */\n }\n }\n\n /**\n * Reclaim page churn left by repeated force rebuilds.\n *\n * SQLite's DROP/CREATE path makes rebuilds fast but leaves pages on the\n * freelist. Compact only large, materially sparse databases and only when the\n * caller is already on a full-index maintenance path.\n */\n compactIfNeeded(options: { minBytes?: number; minFreeRatio?: number } = {}): boolean {\n const minBytes = options.minBytes ?? 256 * 1024 * 1024;\n const minFreeRatio = options.minFreeRatio ?? 0.35;\n try {\n const pageCount = Number(\n (this.stmt('PRAGMA page_count').get() as { page_count?: number } | undefined)?.page_count ??\n 0,\n );\n const pageSize = Number(\n (this.stmt('PRAGMA page_size').get() as { page_size?: number } | undefined)?.page_size ?? 0,\n );\n const freePages = Number(\n (this.stmt('PRAGMA freelist_count').get() as { freelist_count?: number } | undefined)\n ?.freelist_count ?? 0,\n );\n if (\n pageCount <= 0 ||\n pageSize <= 0 ||\n pageCount * pageSize < minBytes ||\n freePages / pageCount < minFreeRatio\n ) {\n return false;\n }\n this.runWithRetry(() => {\n this.db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n this.db.exec('VACUUM');\n this.db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n });\n return true;\n } catch {\n // Compaction is maintenance, never a reason to fail a valid index run.\n return false;\n }\n }\n\n /**\n * Find all references TO a given symbol (who calls / uses this symbol?).\n */\n findRefsTo(symbolId: number): Ref[] {\n return findRefsToWithStatement((sql) => this.stmt(sql), symbolId);\n }\n\n /**\n * Find all references FROM a given symbol (what does this symbol call/use?).\n */\n findRefsFrom(symbolId: number): Ref[] {\n return findRefsFromWithStatement((sql) => this.stmt(sql), symbolId);\n }\n\n // \u2500\u2500\u2500 CodeMap graph aggregation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Package-level graph: each workspace package is a node; edges are derived\n * from cross-package symbol references (a symbol in package A references a\n * symbol resolved in package B). Node metadata includes symbol/file counts.\n */\n getPackageGraph(): CodeMapGraph {\n return getPackageGraphWithStatement((sql) => this.stmt(sql));\n }\n\n /**\n * File-level graph for a single package: each file is a node; edges are\n * derived from cross-file symbol references within the package.\n */\n getFileGraph(packageFilter: string): CodeMapGraph {\n return getFileGraphWithStatement((sql) => this.stmt(sql), packageFilter);\n }\n\n /**\n * Symbol-level graph for a single file: each symbol is a node; edges are\n * derived from intra-file and cross-file symbol references (who calls whom).\n */\n getSymbolGraph(fileFilter: string): CodeMapGraph {\n return getSymbolGraphWithStatement((sql) => this.stmt(sql), fileFilter);\n }\n\n /**\n * Returns every symbol in the index. Used by dead-code analysis to\n * build the full symbol universe for the reachability scan.\n */\n getAllSymbols(): Array<{\n id: number;\n name: string;\n file: string;\n kind: SymbolKind;\n line: number;\n }> {\n return (\n this.stmt('SELECT id, name, file, kind, line FROM symbols ORDER BY id').all() as Array<{\n id: number;\n name: string;\n file: string;\n kind: string;\n line: number;\n }>\n ).map((r) => ({ ...r, kind: r.kind as SymbolKind }));\n }\n\n /**\n * Returns every resolved reference (to_id IS NOT NULL). Used by\n * dead-code analysis to build the consumer-ship graph. Refs whose\n * target symbol id is null (unresolved imports) are excluded.\n */\n getAllResolvedRefs(): Array<{\n fromId: number;\n toId: number;\n callType: string;\n }> {\n return this.stmt(\n 'SELECT from_id AS fromId, to_id AS toId, call_type AS callType FROM refs WHERE to_id IS NOT NULL',\n ).all() as Array<{ fromId: number; toId: number; callType: string }>;\n }\n\n /**\n * Returns ALL import refs (including unresolved) with their source-file\n * path and resolved target id. Used by the dead-code scan's file-level\n * graph traversal to handle barrel-only entry points where no symbol\n * carries the ref.\n *\n * Refs whose `from_id` doesn't match a known symbol (e.g. pure-barrel\n * files with no declarations) will have `sourceFile === null`.\n */\n getAllImportRefs(): Array<{\n /** Source-file path (null when the owning symbol can't be resolved). */\n sourceFile: string | null;\n toName: string;\n /** Resolved target symbol id (null when the name couldn't be matched). */\n toId: number | null;\n callType: string;\n line: number;\n }> {\n return this.stmt(\n `SELECT s.file AS sourceFile, r.to_name AS toName, r.to_id AS toId,\n r.call_type AS callType, r.line\n FROM refs r\n LEFT JOIN symbols s ON r.from_id = s.id\n WHERE r.call_type = 'import'\n ORDER BY r.line`,\n ).all() as Array<{\n sourceFile: string | null;\n toName: string;\n toId: number | null;\n callType: string;\n line: number;\n }>;\n }\n\n close(): void {\n // Drop cached StatementSync references before closing; db.close() finalizes\n // them, and keeping the map would retain handles to a dead connection.\n this.stmtCache.clear();\n // Release the BM25 cache and mark dirty so a hypothetical reopen starts\n // from a clean slate instead of serving a stale index.\n this.bm25Dirty = true;\n this.bm25Cache = null;\n try {\n this.db.close();\n } catch {\n /* already closed */\n }\n }\n}\n\n/** Process-wide singleton pool. */\nexport const indexStorePool = new StorePool(\n (projectRoot: string, opts?: { indexDir?: string | undefined }) =>\n new IndexStore(projectRoot, opts),\n);\n", "/**\n * BM25 ranking implementation \u2014 no external dependencies.\n *\n * Algorithm: Okapi BM25 with standard parameters (k1=1.5, b=0.75).\n */\n\nconst K1 = 1.5;\nconst B = 0.75;\n\ninterface Bm25Doc {\n id: number;\n tokens: string[];\n raw: string;\n len: number;\n}\n\n/** Tokenise a string into lowercase word tokens. */\nexport function tokenise(text: string): string[] {\n // Preserve all Unicode letters + digits + $ + '. Split on everything else.\n const sanitised = text.replace(/[^\\p{L}\\p{N}$'_]/gu, ' ').replace(/_/g, ' ');\n return sanitised.toLowerCase().split(' ').filter(Boolean);\n}\n\nexport interface IndexableDoc {\n id: number;\n text: string;\n}\n\n/**\n * Split a camelCase/SnakeCase identifier into its constituent words.\n * e.g. \"complexOperation\" \u2192 \"complex Operation\"\n * \"foo_bar_baz\" \u2192 \"foo bar baz\"\n * This allows a query for \"complex\" to match \"complexOperation\"\n * via the shared \"complex\" token.\n */\nfunction splitName(name: string): string {\n return name\n // Split an acronym from the word that follows it: HTTPServer \u2192 HTTP Server.\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n // Identifiers commonly encode versions/algorithms with digits.\n .replace(/([\\p{L}])(\\d)/gu, '$1 $2')\n .replace(/(\\d)([\\p{L}])/gu, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .trim();\n}\n\n/**\n * Build indexable text for BM25 from a symbol's fields.\n * The name is split into camelCase/SnakeCase words so that queries\n * like \"complex\" match \"complexOperation\". The verbatim name is\n * also included for exact-match queries.\n */\nexport function buildIndexableText(name: string, signature: string, docComment: string): string {\n return [splitName(name), name, signature, docComment].filter(Boolean).join(' ');\n}\n\nexport function buildBm25Index(docs: IndexableDoc[]): Bm25Index {\n const documents: Bm25Doc[] = docs.map((d) => {\n const tokens = tokenise(d.text);\n return { id: d.id, tokens, raw: d.text, len: tokens.length };\n });\n\n const N = documents.length;\n const totalLen = documents.reduce((sum, d) => sum + d.len, 0);\n const avgLen = N === 0 ? 0 : totalLen / N;\n\n return new Bm25Index(documents, N, avgLen);\n}\n\nexport class Bm25Index {\n private readonly safeAvgLen: number;\n /** Lazily-built id\u2192doc index so getDoc is O(1) instead of an O(D) linear find. */\n private _byId: Map<number, Bm25Doc> | undefined;\n\n constructor(\n private documents: Bm25Doc[],\n private N: number,\n avgLen: number,\n ) {\n this.safeAvgLen = avgLen === 0 ? 1 : avgLen;\n }\n\n score(query: string, filter?: (id: number) => boolean): Array<{ id: number; score: number }> {\n const qTokens = tokenise(query);\n if (qTokens.length === 0) return [];\n\n // Precompute document frequency per query term once, outside the\n // per-document loop. The SQLite FTS path uses token-prefix matching;\n // we mirror that contract here so fallback ranking has identical recall.\n // Previously this was recomputed for every (document, term) pair \u2014\n // O(D\u00B2QT) \u2014 making large indexes (5500+ symbols) hit the 30s search\n // watchdog on every query.\n const dfByTerm = new Map<string, number>();\n for (const qTerm of qTokens) {\n let dfVal = 0;\n for (const candidate of this.documents) {\n if (candidate.tokens.some((t) => t.startsWith(qTerm))) dfVal++;\n }\n dfByTerm.set(qTerm, dfVal);\n }\n\n const results: Array<{ id: number; score: number }> = [];\n\n for (const doc of this.documents) {\n if (filter && !filter(doc.id)) continue;\n\n let docScore = 0;\n for (const qTerm of qTokens) {\n let tf = 0;\n for (const t of doc.tokens) {\n if (t.startsWith(qTerm)) tf++;\n }\n if (tf === 0) continue;\n\n const dfVal = dfByTerm.get(qTerm) ?? 0;\n if (dfVal === 0) continue;\n\n const idf = Math.log((this.N - dfVal + 0.5) / (dfVal + 0.5) + 1);\n const lenRatio = B * (doc.len / this.safeAvgLen);\n const tfComponent = (tf * (K1 + 1)) / (tf + K1 * (1 - B + lenRatio));\n\n docScore += idf * tfComponent;\n }\n\n if (docScore > 0) results.push({ id: doc.id, score: docScore });\n }\n\n return results;\n }\n\n getDoc(id: number): Bm25Doc | undefined {\n if (!this._byId) {\n this._byId = new Map(this.documents.map((d) => [d.id, d]));\n }\n return this._byId.get(id);\n }\n\n extractSnippet(docId: number, queryTokens: string[], radius = 40): string {\n const doc = this.getDoc(docId);\n if (!doc) return '';\n\n for (const tok of queryTokens) {\n const idx = doc.raw.toLowerCase().indexOf(tok);\n if (idx !== -1) {\n const start = Math.max(0, idx - radius);\n const end = Math.min(doc.raw.length, idx + tok.length + radius);\n const excerpt = doc.raw.slice(start, end);\n const ellipsis = '\\u2026';\n return (start > 0 ? ellipsis : '') + excerpt + (end < doc.raw.length ? ellipsis : '');\n }\n }\n return doc.raw.slice(0, radius * 2) + (doc.raw.length > radius * 2 ? '\\u2026' : '');\n }\n}\n", "/**\n * LSP SymbolKind mapping utilities.\n *\n * LSP SymbolKind numbers are defined by vscode-languageserver-protocol.\n * This module maps between LSP kind numbers and the internal SymbolKind taxonomy.\n */\n\nimport type { SymbolKind } from './schema.js';\n\n/**\n * LSP SymbolKind values (1\u201326) as defined by vscode-languageserver-protocol.\n */\nexport enum LSPSymbolKind {\n File = 1,\n Module = 2,\n Namespace = 3,\n Package = 4,\n Class = 5,\n Method = 6,\n Property = 7,\n Field = 8,\n Constructor = 9,\n Enum = 10,\n Interface = 11,\n Function = 12,\n Variable = 13,\n Constant = 14,\n String = 15,\n Number = 16,\n Boolean = 17,\n Array = 18,\n Object = 19,\n Key = 20,\n Null = 21,\n EnumMember = 22,\n Struct = 23,\n Event = 24,\n Operator = 25,\n TypeParameter = 26,\n}\n\n/**\n * Maps an LSP kind number to the corresponding internal SymbolKind.\n * Returns null if the LSP kind has no equivalent in the internal taxonomy.\n */\nexport function lspKindToInternalKind(k: number): SymbolKind | null {\n switch (k) {\n case LSPSymbolKind.Class: return 'class';\n case LSPSymbolKind.Method: return 'method';\n case LSPSymbolKind.Property:\n case LSPSymbolKind.Field: return 'property';\n case LSPSymbolKind.Constructor: return 'class';\n case LSPSymbolKind.Enum: return 'enum';\n case LSPSymbolKind.Interface: return 'interface';\n case LSPSymbolKind.Function: return 'function';\n case LSPSymbolKind.Variable: return 'var';\n case LSPSymbolKind.Constant: return 'const';\n case LSPSymbolKind.EnumMember: return 'enum';\n case LSPSymbolKind.TypeParameter:return 'type';\n case LSPSymbolKind.Namespace: return 'namespace';\n default: return null;\n }\n}\n\n/**\n * Maps an internal SymbolKind to the corresponding LSP kind number.\n * Returns null if the internal kind has no equivalent LSP kind.\n */\nexport function internalKindToLspKind(k: SymbolKind): number | null {\n switch (k) {\n case 'class': return LSPSymbolKind.Class;\n case 'method': return LSPSymbolKind.Method;\n case 'property': return LSPSymbolKind.Property;\n case 'function': return LSPSymbolKind.Function;\n case 'var': return LSPSymbolKind.Variable;\n case 'const': return LSPSymbolKind.Constant;\n case 'let': return LSPSymbolKind.Variable;\n case 'enum': return LSPSymbolKind.Enum;\n case 'interface': return LSPSymbolKind.Interface;\n case 'namespace': return LSPSymbolKind.Namespace;\n case 'type': return LSPSymbolKind.TypeParameter;\n // parameter and other internal-only kinds have no LSP equivalent\n default: return null;\n }\n}\n\n/**\n * Returns true if `k` is a valid LSP SymbolKind number (1\u201326).\n */\nexport function isLspKind(k: number): boolean {\n return Number.isInteger(k) && k >= 1 && k <= 26;\n}\n", "// \u2500\u2500\u2500 Symbol kind taxonomy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Language a symbol belongs to.\n *\n * First-class parsers exist for TS/JS, Go, Python, Rust, JSON, YAML.\n * All other langs are still indexed via the generic regex extractor so\n * monorepos are never silently skipped just because a native toolchain\n * is missing. `'other'` covers unusual extensions / special filenames.\n */\nexport type SymbolLang =\n | 'ts'\n | 'js'\n | 'tsx'\n | 'jsx'\n | 'go'\n | 'py'\n | 'rs'\n | 'json'\n | 'yaml'\n | 'c'\n | 'cpp'\n | 'java'\n | 'csharp'\n | 'php'\n | 'ruby'\n | 'swift'\n | 'kotlin'\n | 'scala'\n | 'shell'\n | 'sql'\n | 'md'\n | 'toml'\n | 'html'\n | 'css'\n | 'vue'\n | 'svelte'\n | 'dart'\n | 'lua'\n | 'r'\n | 'proto'\n | 'graphql'\n | 'zig'\n | 'elixir'\n | 'haskell'\n | 'other';\n\n/** What kind of symbol this is. */\nexport type SymbolKind =\n | 'class'\n | 'interface'\n | 'enum'\n | 'type'\n | 'function'\n | 'method'\n | 'var'\n | 'const'\n | 'let'\n | 'property'\n | 'parameter'\n | 'namespace'\n | 'object' // JSON root object\n | 'literal' // scalar value in JSON/YAML\n | 'schema' // JSON Schema $ref/$schema entry\n // Rust-specific\n | 'struct'\n | 'trait'\n | 'impl'\n | 'static'\n | 'mod';\n\n/** A single indexed code symbol. */\nexport interface Symbol {\n id: number;\n lang: SymbolLang;\n kind: SymbolKind;\n name: string;\n file: string; // absolute path\n line: number; // 1-based\n col: number; // 0-based\n signature: string; // e.g. \"function foo(a: string): Promise<void>\"\n docComment: string; // JSDoc / docstring first line\n scope: string; // e.g. \"MyClass.method\" or module-level \"\"\n text: string; // concatenated searchable text: name + signature + docComment\n}\n\n/** Extracted symbols and cross-references for one file. */\nexport interface FileSymbols {\n file: string;\n lang: SymbolLang;\n symbols: Symbol[];\n refs?: Ref[] | undefined; // cross-references extracted from this file (optional for back-compat)\n mtimeMs: number;\n}\n\n/** Source file metadata tracked for incremental indexing. */\nexport interface FileMeta {\n file: string;\n lang: SymbolLang;\n mtimeMs: number;\n symbolCount: number;\n lastIndexed: number; // unix ms\n}\n\n/** Statistics about the index. */\nexport interface IndexStats {\n totalSymbols: number;\n totalFiles: number;\n byLang: Record<SymbolLang, number>;\n byKind: Record<SymbolKind, number>;\n indexPath: string;\n lastIndexed: number | null;\n sizeBytes: number;\n version: number;\n}\n\n/** Result of a search query. */\nexport interface SearchResult {\n id: number;\n name: string;\n kind: SymbolKind;\n lang: SymbolLang;\n file: string;\n line: number;\n col: number;\n signature: string;\n docComment: string;\n score: number;\n snippet: string;\n /** Original LSP SymbolKind number if the result was filtered by an LSP kind. */\n lspKind?: number | undefined;\n}\n\n/** Result of a full reindex. */\nexport interface IndexResult {\n filesIndexed: number;\n symbolsIndexed: number;\n langStats: Record<SymbolLang, number>;\n durationMs: number;\n errors: string[];\n /**\n * Present when `runStartupIndex` detected a corrupt/stale index (SQLite\n * constraint failure) and automatically recovered by wiping and rebuilding\n * with `force: true`. The original failure message is preserved here so\n * callers diagnosing intermittent crashes can distinguish a normal rebuild\n * from one triggered by corruption recovery.\n */\n autoRecovered?: { failure: string; rebuiltWithForce: true } | undefined;\n}\n\n// \u2500\u2500\u2500 Cross-reference types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** What kind of reference this is. */\nexport type CallType = 'call' | 'type_ref' | 'inherit' | 'implement' | 'import';\n\n/** A cross-reference between two symbols (who references whom). */\nexport interface Ref {\n id?: number | undefined;\n fromId: number; // symbol that makes the reference\n toName: string; // resolved name of the referenced symbol\n toId?: number | undefined; // resolved target symbol id (filled after index resolution)\n callType: CallType; // kind of reference\n line: number; // source line where the reference occurs\n}\n\n// \u2500\u2500\u2500 CodeMap graph types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A node in the code-map dependency graph. */\nexport interface GraphNode {\n id: string;\n label: string;\n kind: 'package' | 'file' | 'symbol';\n /** Package name (packages/) or undefined. */\n package?: string | undefined;\n /** File path (relative to project root) or undefined for package-level. */\n file?: string | undefined;\n /** Symbol id when kind === 'symbol'. */\n symbolId?: number | undefined;\n /** Symbol kind when kind === 'symbol'. */\n symbolKind?: SymbolKind | undefined;\n /** Number of symbols contained (for package/file nodes). */\n symbolCount?: number | undefined;\n /** Number of files contained (for package nodes). */\n fileCount?: number | undefined;\n /** Source language when the node represents a file or symbol. */\n lang?: SymbolLang | undefined;\n /** Declaration line when the node represents a symbol. */\n line?: number | undefined;\n /** Indexed declaration signature when the node represents a symbol. */\n signature?: string | undefined;\n /** Indexed declaration scope when the node represents a symbol. */\n scope?: string | undefined;\n /** True when this is a direct relation outside the current drill-down scope. */\n external?: boolean | undefined;\n}\n\n/** A directed edge: source references / depends-on target. */\nexport interface GraphEdge {\n source: string;\n target: string;\n /** Number of refs contributing to this edge (weight). */\n weight: number;\n /** Dominant ref type: 'call', 'import', 'type_ref', etc. */\n refType: CallType;\n}\n\n/** Complete graph response. */\nexport interface CodeMapGraph {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\n// \u2500\u2500\u2500 Schema version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// v2: added the symbols_fts FTS5 table (ranked search moved into SQLite).\n// v3: parser/search format update (navigable TS declarations, valid ref owners,\n// acronym/digit token splitting). Derived data must be rebuilt.\n// A version mismatch on open drops & rebuilds the index (it is derived data).\n// Non-structural CodeMap relation migrations use `relation_graph_version`\n// metadata so older running processes sharing the DB cannot downgrade it.\nexport const SCHEMA_VERSION = 3;\n", "import { createRequire } from 'node:module';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { toErrorMessage } from '@wrongstack/core/utils';\nimport { LockError } from './circuit-breaker.js';\n\nlet warningSilenced = false;\n\n/**\n * Swallow the one-time `ExperimentalWarning: SQLite ...` Node prints the first\n * time `node:sqlite` loads. Patched only once, and only filters that specific\n * warning \u2014 every other warning passes through untouched.\n */\nfunction silenceSqliteExperimentalWarning(): void {\n if (warningSilenced) return;\n warningSilenced = true;\n const original = process.emitWarning.bind(process);\n process.emitWarning = ((warning: unknown, ...rest: unknown[]): void => {\n const msg = typeof warning === 'string' ? warning : ((warning as Error)?.message ?? '');\n const name =\n typeof warning === 'string' ? String(rest[0] ?? '') : ((warning as Error)?.name ?? '');\n if (/sqlite/i.test(msg) && /experimental/i.test(`${name} ${msg}`)) return;\n (original as (w: unknown, ...r: unknown[]) => void)(warning, ...rest);\n }) as typeof process.emitWarning;\n}\n\nlet DatabaseSyncCtor: typeof DatabaseSync | undefined;\n\n/**\n * Load `node:sqlite`'s `DatabaseSync` lazily. Keeping this off writer.ts top\n * level lets codebase-index tools register at CLI boot without eagerly loading\n * SQLite. Runtimes without `node:sqlite` fail only when the index is used.\n */\nexport function loadDatabaseSync(): typeof DatabaseSync {\n if (DatabaseSyncCtor) return DatabaseSyncCtor;\n silenceSqliteExperimentalWarning();\n try {\n const req = createRequire(import.meta.url);\n DatabaseSyncCtor = (req('node:sqlite') as typeof import('node:sqlite')).DatabaseSync;\n } catch (err) {\n throw new Error(\n \"The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. \" +\n `This runtime doesn't provide it: ${toErrorMessage(err)}`,\n );\n }\n return DatabaseSyncCtor;\n}\n\n/** Maximum retry attempts for a lock-conflict error. */\nconst MAX_LOCK_RETRIES = 3;\n/** Base delay (ms) before the first retry after a lock error. */\nconst LOCK_RETRY_BASE_DELAY_MS = 50;\n/** Cap on the per-retry delay so we never sleep for more than this. */\nconst LOCK_RETRY_MAX_DELAY_MS = 500;\n\nfunction isLockError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const e = err as { code?: unknown; sqliteCode?: unknown };\n const code = e.code ?? e.sqliteCode;\n if (typeof code === 'string' && /SQLITE_(BUSY|LOCKED)/.test(code)) return true;\n if (typeof code === 'number' && (code === 5 || code === 6)) return true;\n return /SQLITE_(BUSY|LOCKED)/.test(err.message);\n}\n\nfunction sleepSync(ms: number): void {\n try {\n const sab = new SharedArrayBuffer(4);\n const view = new Int32Array(sab);\n Atomics.wait(view, 0, 0, ms);\n } catch {\n // busy_timeout already handled the bulk wait; retry immediately if\n // Atomics.wait is unavailable in this runtime.\n }\n}\n\nexport function runSqliteWithRetry<T>(fn: () => T): T {\n let lastError: unknown;\n for (let attempt = 0; attempt <= MAX_LOCK_RETRIES; attempt++) {\n try {\n return fn();\n } catch (err) {\n lastError = err;\n if (!isLockError(err)) throw err;\n if (attempt === MAX_LOCK_RETRIES) {\n const msg = lastError instanceof Error ? lastError.message : String(lastError);\n throw new LockError(`SQLite lock conflict after ${MAX_LOCK_RETRIES} retries: ${msg}`);\n }\n const delay = Math.min(LOCK_RETRY_BASE_DELAY_MS * 2 ** attempt, LOCK_RETRY_MAX_DELAY_MS);\n sleepSync(delay);\n }\n }\n throw lastError;\n}\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { FileMeta, IndexStats, SymbolKind, SymbolLang } from './schema.js';\nimport { SCHEMA_VERSION } from './schema.js';\n\nconst DB_FILE = 'index.db';\n\ntype Statement = ReturnType<DatabaseSync['prepare']>;\ntype PrepareStatement = (sql: string) => Statement;\n\nexport function getAllIndexableWithStatement(\n stmt: PrepareStatement,\n): Array<{ id: number; text: string }> {\n return (\n stmt('SELECT id, text FROM symbols').all() as { id: number; text: string }[]\n ).map(({ id, text }) => ({ id, text }));\n}\n\nexport function getMaxSymbolIdWithStatement(stmt: PrepareStatement): number {\n const rows = stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n return rows[0]?.m ?? 0;\n}\n\nexport function getStatsWithStatement(stmt: PrepareStatement, indexDir: string): IndexStats {\n const lastRows = stmt(\"SELECT value FROM metadata WHERE key = 'last_indexed'\").all() as {\n value: string;\n }[];\n const totalRows = stmt('SELECT COUNT(*) FROM symbols').all() as { 'COUNT(*)': number }[];\n const fileRows = stmt('SELECT COUNT(*) FROM files').all() as { 'COUNT(*)': number }[];\n const langRows = stmt('SELECT lang, COUNT(*) FROM symbols GROUP BY lang').all() as Array<{\n lang: string;\n 'COUNT(*)': number;\n }>;\n const kindRows = stmt('SELECT kind, COUNT(*) FROM symbols GROUP BY kind').all() as Array<{\n kind: string;\n 'COUNT(*)': number;\n }>;\n\n const byLang = {} as Record<SymbolLang, number>;\n for (const row of langRows) byLang[row.lang as SymbolLang] = Number(row['COUNT(*)']);\n\n const byKind = {} as Record<SymbolKind, number>;\n for (const row of kindRows) byKind[row.kind as SymbolKind] = Number(row['COUNT(*)']);\n\n return {\n totalSymbols: totalRows[0] ? Number(totalRows[0]['COUNT(*)']) : 0,\n totalFiles: fileRows[0] ? Number(fileRows[0]['COUNT(*)']) : 0,\n byLang,\n byKind,\n indexPath: indexDir,\n lastIndexed: lastRows.length ? Number(lastRows[0]?.value) : null,\n sizeBytes: getIndexDbSizeBytes(indexDir),\n version: SCHEMA_VERSION,\n };\n}\n\nexport function getMetadataWithStatement(\n stmt: PrepareStatement,\n key: string,\n): string | undefined {\n const rows = stmt('SELECT value FROM metadata WHERE key = ?').all(key) as {\n value: string;\n }[];\n return rows[0]?.value;\n}\n\nexport function getFileMetaWithStatement(\n stmt: PrepareStatement,\n file: string,\n): FileMeta | null {\n const rows = stmt(\n 'SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?',\n ).all(file) as Array<{\n file: string;\n lang: string;\n mtime_ms: number;\n symbol_count: number;\n last_indexed: number;\n }>;\n const r = rows[0];\n if (!r) return null;\n return {\n file: r.file,\n lang: r.lang as SymbolLang,\n mtimeMs: r.mtime_ms,\n symbolCount: r.symbol_count,\n lastIndexed: r.last_indexed,\n };\n}\n\nexport function getAllFileMetasWithStatement(stmt: PrepareStatement): FileMeta[] {\n return (\n stmt('SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files').all() as Array<{\n file: string;\n lang: string;\n mtime_ms: number;\n symbol_count: number;\n last_indexed: number;\n }>\n ).map((r) => ({\n file: r.file,\n lang: r.lang as SymbolLang,\n mtimeMs: r.mtime_ms,\n symbolCount: r.symbol_count,\n lastIndexed: r.last_indexed,\n }));\n}\n\nexport function getIndexDbSizeBytes(indexDir: string): number {\n try {\n return fs.statSync(path.join(indexDir, DB_FILE)).size;\n } catch {\n return 0;\n }\n}\n", "import type { DatabaseSync } from 'node:sqlite';\nimport type { Ref } from './schema.js';\n\ntype Statement = ReturnType<DatabaseSync['prepare']>;\ntype PrepareStatement = (sql: string) => Statement;\n\nexport interface BulkSymbolRow {\n id: number;\n lang: string;\n kind: string;\n name: string;\n file: string;\n line: number;\n col: number;\n signature: string;\n docComment: string;\n scope: string;\n text: string;\n}\n\nexport function bulkInsertSymbolsWithStatement(\n stmt: PrepareStatement,\n maxSqlVars: number,\n rows: BulkSymbolRow[],\n): void {\n if (rows.length === 0) return;\n const chunkSize = Math.max(1, Math.floor(maxSqlVars / 12));\n for (let i = 0; i < rows.length; i += chunkSize) {\n const chunk = rows.slice(i, i + chunkSize);\n const placeholders = chunk.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ');\n const insert = stmt(\n `INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)\n VALUES ${placeholders}`,\n );\n const binds: (string | number)[] = [];\n for (const r of chunk) {\n binds.push(\n r.id,\n r.lang,\n r.kind,\n r.name,\n r.file,\n r.line,\n r.col,\n r.signature,\n r.docComment,\n r.scope,\n r.text,\n r.file,\n );\n }\n insert.run(...binds);\n }\n}\n\nexport function bulkInsertFtsWithStatement(\n stmt: PrepareStatement,\n maxSqlVars: number,\n ftsAvailable: boolean,\n rows: Array<{ id: number; text: string }>,\n): void {\n if (!ftsAvailable || rows.length === 0) return;\n const chunkSize = Math.max(1, Math.floor(maxSqlVars / 2));\n for (let i = 0; i < rows.length; i += chunkSize) {\n const chunk = rows.slice(i, i + chunkSize);\n const placeholders = chunk.map(() => '(?, ?)').join(', ');\n const insert = stmt(`INSERT INTO symbols_fts(rowid, text) VALUES ${placeholders}`);\n const binds: (string | number)[] = [];\n for (const r of chunk) binds.push(r.id, r.text);\n insert.run(...binds);\n }\n}\n\nexport function bulkInsertRefsWithStatement(\n stmt: PrepareStatement,\n maxSqlVars: number,\n refs: Ref[],\n): void {\n if (refs.length === 0) return;\n const chunkSize = Math.max(1, Math.floor(maxSqlVars / 5));\n for (let i = 0; i < refs.length; i += chunkSize) {\n const chunk = refs.slice(i, i + chunkSize);\n const placeholders = chunk.map(() => '(?, ?, ?, ?, ?)').join(', ');\n const insert = stmt(\n `INSERT INTO refs(from_id, to_name, to_id, call_type, line) VALUES ${placeholders}`,\n );\n const binds: (string | number | null)[] = [];\n for (const ref of chunk) {\n binds.push(ref.fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);\n }\n insert.run(...binds);\n }\n}\n", "import * as path from 'node:path';\nimport type { CallType, GraphEdge, GraphNode, SymbolKind, SymbolLang } from './schema.js';\n\n/**\n * Derive a monorepo package name from an absolute file path.\n * Handles both `packages/<name>/...` and `apps/<name>/...` layouts.\n */\nexport function derivePackage(filePath: string): string | undefined {\n const f = filePath.replace(/\\\\/g, '/');\n const pkgsIdx = f.indexOf('/packages/');\n if (pkgsIdx !== -1) {\n const rest = f.slice(pkgsIdx + '/packages/'.length);\n const seg = rest.split('/')[0];\n return seg ? `@wrongstack/${seg}` : undefined;\n }\n const appsIdx = f.indexOf('/apps/');\n if (appsIdx !== -1) {\n const rest = f.slice(appsIdx + '/apps/'.length);\n const seg = rest.split('/')[0];\n return seg ? `app:${seg}` : undefined;\n }\n return undefined;\n}\n\nexport function packageFromImport(moduleName: string): string | undefined {\n if (!moduleName.startsWith('@wrongstack/')) return undefined;\n const parts = moduleName.split('/');\n return parts[1] ? `@wrongstack/${parts[1]}` : undefined;\n}\n\nexport function buildPackageGraphNodes(\n fileCounts: Array<{ file: string; n: number }>,\n files: Array<{ file: string }>,\n): { pkgNodes: Map<string, GraphNode>; fileToPkg: Map<string, string> } {\n const pkgNodes = new Map<string, GraphNode>();\n const fileToPkg = new Map<string, string>();\n\n for (const { file, n } of fileCounts) {\n const pkg = derivePackage(file) ?? '(root)';\n fileToPkg.set(file, pkg);\n const node = pkgNodes.get(pkg);\n if (node) {\n node.symbolCount = (node.symbolCount ?? 0) + n;\n } else {\n pkgNodes.set(pkg, {\n id: `pkg:${pkg}`,\n label: pkg,\n kind: 'package',\n package: pkg,\n symbolCount: n,\n fileCount: 0,\n });\n }\n }\n\n for (const { file } of files) {\n const pkg = derivePackage(file) ?? '(root)';\n fileToPkg.set(file, pkg);\n const node = pkgNodes.get(pkg);\n if (node) {\n node.fileCount = (node.fileCount ?? 0) + 1;\n } else {\n pkgNodes.set(pkg, {\n id: `pkg:${pkg}`,\n label: pkg,\n kind: 'package',\n package: pkg,\n symbolCount: 0,\n fileCount: 1,\n });\n }\n }\n\n return { pkgNodes, fileToPkg };\n}\n\nexport type WriterFileGraphSymbolRow = {\n file: string;\n id: number;\n name: string;\n kind: string;\n lang: string;\n line: number;\n};\n\nexport function buildFileGraphNodeState(\n pkgSyms: WriterFileGraphSymbolRow[],\n localFiles: Set<string>,\n): {\n fileNodes: Map<string, GraphNode>;\n symToFile: Map<number, string>;\n fileStats: Map<string, { count: number; lang: SymbolLang }>;\n ensureFileNode: (file: string) => void;\n} {\n const fileNodes = new Map<string, GraphNode>();\n const symToFile = new Map<number, string>();\n const fileStats = new Map<string, { count: number; lang: SymbolLang }>();\n for (const s of pkgSyms) {\n symToFile.set(s.id, s.file);\n const current = fileStats.get(s.file);\n fileStats.set(s.file, {\n count: (current?.count ?? 0) + 1,\n lang: (current?.lang ?? s.lang) as SymbolLang,\n });\n }\n\n const ensureFileNode = (file: string): void => {\n if (fileNodes.has(file)) return;\n const stats = fileStats.get(file);\n fileNodes.set(file, {\n id: `file:${file}`,\n label: file.replace(/\\\\/g, '/').split('/').pop() ?? file,\n kind: 'file',\n package: derivePackage(file) ?? '(root)',\n file,\n symbolCount: stats?.count ?? 0,\n lang: stats?.lang,\n external: !localFiles.has(file),\n });\n };\n for (const file of localFiles) {\n ensureFileNode(file);\n }\n return { fileNodes, symToFile, fileStats, ensureFileNode };\n}\n\nexport type WriterSymbolGraphRow = {\n id: number;\n name: string;\n kind: string;\n lang: string;\n file: string;\n line: number;\n signature: string;\n scope: string;\n};\n\nexport function buildSymbolGraphNodes(\n symById: Map<number, WriterSymbolGraphRow>,\n relatedIds: Set<number>,\n fileFilter: string,\n): GraphNode[] {\n return [...relatedIds]\n .map((id) => symById.get(id))\n .filter((symbol): symbol is WriterSymbolGraphRow => symbol !== undefined)\n .sort((a, b) => {\n const aExternal = a.file === fileFilter ? 0 : 1;\n const bExternal = b.file === fileFilter ? 0 : 1;\n return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;\n })\n .map((s) => ({\n id: `sym:${s.id}`,\n label: s.name,\n kind: 'symbol',\n symbolId: s.id,\n symbolKind: s.kind as SymbolKind,\n file: s.file,\n package: derivePackage(s.file) ?? '(root)',\n lang: s.lang as SymbolLang,\n line: s.line,\n signature: s.signature,\n scope: s.scope,\n external: s.file !== fileFilter,\n }));\n}\n\nexport function resolveRelativeImport(\n fromFile: string,\n moduleName: string,\n indexedFiles: Set<string>,\n): string | undefined {\n if (!moduleName.startsWith('.')) return undefined;\n const normalizedFrom = fromFile.replace(/\\\\/g, '/');\n const absolute = path.posix.normalize(\n path.posix.join(path.posix.dirname(normalizedFrom), moduleName),\n );\n const extension = path.posix.extname(absolute);\n const base = extension ? absolute.slice(0, -extension.length) : absolute;\n const candidates = [\n absolute,\n ...['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts'].map((ext) => `${base}${ext}`),\n ...['.ts', '.tsx', '.js', '.jsx'].map((ext) => path.posix.join(absolute, `index${ext}`)),\n ...['.ts', '.tsx', '.js', '.jsx'].map((ext) => path.posix.join(base, `index${ext}`)),\n ];\n const indexedByPortablePath = new Map(\n [...indexedFiles].map((file) => [file.replace(/\\\\/g, '/').toLocaleLowerCase(), file]),\n );\n for (const candidate of candidates) {\n const indexed = indexedByPortablePath.get(candidate.toLocaleLowerCase());\n if (indexed) return indexed;\n }\n return undefined;\n}\n\nexport type WeightedEdgeAccumulator = {\n weight: number;\n types: Map<string, number>;\n};\n\nexport function addWeightedEdge(\n edgeMap: Map<string, WeightedEdgeAccumulator>,\n source: string | number,\n target: string | number,\n callType: string,\n weight: number,\n): void {\n const key = `${source}\\u0000${target}`;\n let edge = edgeMap.get(key);\n if (!edge) {\n edge = { weight: 0, types: new Map() };\n edgeMap.set(key, edge);\n }\n edge.weight += weight;\n edge.types.set(callType, (edge.types.get(callType) ?? 0) + weight);\n}\n\nexport function materializeWeightedEdges(\n edgeMap: Map<string, WeightedEdgeAccumulator>,\n idPrefix: 'pkg' | 'file' | 'sym',\n): GraphEdge[] {\n const edges: GraphEdge[] = [];\n for (const [key, edge] of edgeMap) {\n const [source, target] = key.split('\\u0000');\n let bestType = 'call';\n let bestCount = 0;\n for (const [type, count] of edge.types) {\n if (count > bestCount) {\n bestType = type;\n bestCount = count;\n }\n }\n edges.push({\n source: `${idPrefix}:${source}`,\n target: `${idPrefix}:${target}`,\n weight: edge.weight,\n refType: bestType as CallType,\n });\n }\n return edges;\n}\n", "import type { Ref } from './schema.js';\n\nexport type WriterRefRow = {\n id: number;\n from_id: number;\n to_name: string;\n to_id: number | null;\n call_type: string;\n line: number;\n};\n\nexport function mapWriterRefRow(row: WriterRefRow): Ref {\n return {\n id: row.id,\n fromId: row.from_id,\n toName: row.to_name,\n toId: row.to_id ?? undefined,\n callType: row.call_type as Ref['callType'],\n line: row.line,\n };\n}\n", "import type { DatabaseSync } from 'node:sqlite';\nimport type { CodeMapGraph, Ref, SymbolLang } from './schema.js';\nimport {\n addWeightedEdge,\n buildFileGraphNodeState,\n buildPackageGraphNodes,\n buildSymbolGraphNodes,\n derivePackage,\n materializeWeightedEdges,\n packageFromImport,\n resolveRelativeImport,\n type WeightedEdgeAccumulator,\n type WriterFileGraphSymbolRow,\n type WriterSymbolGraphRow,\n} from './writer-graph-helpers.js';\nimport { mapWriterRefRow, type WriterRefRow } from './writer-ref-mapper.js';\n\ntype Statement = ReturnType<DatabaseSync['prepare']>;\ntype PrepareStatement = (sql: string) => Statement;\n\nexport function findRefsToWithStatement(stmt: PrepareStatement, symbolId: number): Ref[] {\n return (\n stmt(\n 'SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE to_id = ? OR to_name = (SELECT name FROM symbols WHERE id = ?)',\n ).all(symbolId, symbolId) as WriterRefRow[]\n ).map(mapWriterRefRow);\n}\n\nexport function findRefsFromWithStatement(stmt: PrepareStatement, symbolId: number): Ref[] {\n return (\n stmt('SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE from_id = ?').all(\n symbolId,\n ) as WriterRefRow[]\n ).map(mapWriterRefRow);\n}\n\nexport function getPackageGraphWithStatement(stmt: PrepareStatement): CodeMapGraph {\n const fileCounts = stmt('SELECT file, COUNT(*) AS n FROM symbols GROUP BY file').all() as Array<{\n file: string;\n n: number;\n }>;\n\n const files = stmt('SELECT DISTINCT file FROM files').all() as { file: string }[];\n const { pkgNodes, fileToPkg } = buildPackageGraphNodes(fileCounts, files);\n\n const refRows = stmt(\n `SELECT r.call_type, sf.file AS from_file, st.file AS to_file, COUNT(*) AS n\n FROM refs r\n JOIN symbols sf ON sf.id = r.from_id\n JOIN symbols st ON st.id = r.to_id\n WHERE r.to_id IS NOT NULL AND r.call_type != 'import'\n GROUP BY r.call_type, sf.file, st.file`,\n ).all() as Array<{ call_type: string; from_file: string; to_file: string; n: number }>;\n\n const edgeMap = new Map<string, WeightedEdgeAccumulator>();\n for (const r of refRows) {\n const fromPkg = fileToPkg.get(r.from_file) ?? derivePackage(r.from_file) ?? '(root)';\n const toPkg = fileToPkg.get(r.to_file) ?? derivePackage(r.to_file) ?? '(root)';\n if (fromPkg === toPkg) continue;\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromPkg, toPkg, r.call_type, n);\n }\n\n const importRows = stmt(\n `SELECT r.to_name, s.file AS from_file, COUNT(*) AS n\n FROM refs r\n JOIN symbols s ON s.id = r.from_id\n WHERE r.call_type = 'import'\n GROUP BY r.to_name, s.file`,\n ).all() as Array<{ to_name: string; from_file: string; n: number }>;\n for (const r of importRows) {\n const fromPkg = fileToPkg.get(r.from_file) ?? derivePackage(r.from_file) ?? '(root)';\n const toPkg = packageFromImport(r.to_name);\n if (!fromPkg || !toPkg || fromPkg === toPkg || !pkgNodes.has(toPkg)) continue;\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromPkg, toPkg, 'import', n);\n }\n\n const edges = materializeWeightedEdges(edgeMap, 'pkg');\n return { nodes: [...pkgNodes.values()], edges };\n}\n\nexport function getFileGraphWithStatement(\n stmt: PrepareStatement,\n packageFilter: string,\n): CodeMapGraph {\n const allFiles = stmt('SELECT DISTINCT file FROM symbols').all() as { file: string }[];\n const pkgFilePaths = allFiles\n .filter((f) => (derivePackage(f.file) ?? '(root)') === packageFilter)\n .map((f) => f.file);\n const localFiles = new Set(pkgFilePaths);\n if (localFiles.size === 0) return { nodes: [], edges: [] };\n\n const filePlaceholders = [...localFiles].map(() => '?').join(',');\n const pkgSyms = stmt(\n `SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`,\n ).all(...pkgFilePaths) as WriterFileGraphSymbolRow[];\n const { fileNodes, symToFile, fileStats, ensureFileNode } = buildFileGraphNodeState(\n pkgSyms,\n localFiles,\n );\n\n const indexedFiles = new Set(allFiles.map((f) => f.file));\n\n const refRows = stmt(\n `SELECT r.from_id, r.to_id, r.call_type, COUNT(*) AS n\n FROM refs r\n WHERE (r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))\n OR r.to_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders})))\n AND r.to_id IS NOT NULL\n GROUP BY r.from_id, r.to_id, r.call_type`,\n ).all(...pkgFilePaths, ...pkgFilePaths) as {\n from_id: number;\n to_id: number;\n call_type: string;\n n: number;\n }[];\n\n const knownSymIds = new Set(pkgSyms.map((s) => s.id));\n const crossRefIds = new Set<number>();\n for (const r of refRows) {\n if (!knownSymIds.has(r.from_id)) crossRefIds.add(r.from_id);\n if (!knownSymIds.has(r.to_id)) crossRefIds.add(r.to_id);\n }\n if (crossRefIds.size > 0) {\n const crossPlaceholders = [...crossRefIds].map(() => '?').join(',');\n const extras = stmt(`SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`).all(\n ...crossRefIds,\n ) as { id: number; file: string }[];\n for (const x of extras) {\n symToFile.set(x.id, x.file);\n if (!fileStats.has(x.file)) {\n fileStats.set(x.file, { count: 0, lang: 'ts' as SymbolLang });\n }\n }\n }\n\n const edgeMap = new Map<string, WeightedEdgeAccumulator>();\n for (const r of refRows) {\n if (r.call_type === 'import') continue;\n const fromFile = symToFile.get(r.from_id);\n const toFile = symToFile.get(r.to_id);\n if (!fromFile || !toFile || fromFile === toFile) continue;\n if (!localFiles.has(fromFile) && !localFiles.has(toFile)) continue;\n ensureFileNode(fromFile);\n ensureFileNode(toFile);\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromFile, toFile, r.call_type, n);\n }\n\n const importRows = stmt(\n `SELECT r.from_id, r.to_name, COUNT(*) AS n\n FROM refs r\n WHERE r.call_type = 'import'\n AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))\n GROUP BY r.from_id, r.to_name`,\n ).all(...pkgFilePaths) as { from_id: number; to_name: string; n: number }[];\n for (const r of importRows) {\n const fromFile = symToFile.get(r.from_id);\n if (!fromFile || !localFiles.has(fromFile)) continue;\n const toFile = resolveRelativeImport(fromFile, r.to_name, indexedFiles);\n if (!toFile || fromFile === toFile) continue;\n ensureFileNode(fromFile);\n ensureFileNode(toFile);\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, fromFile, toFile, 'import', n);\n }\n\n const edges = materializeWeightedEdges(edgeMap, 'file');\n return { nodes: [...fileNodes.values()], edges };\n}\n\nexport function getSymbolGraphWithStatement(\n stmt: PrepareStatement,\n fileFilter: string,\n): CodeMapGraph {\n const syms = stmt(\n 'SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id',\n ).all(fileFilter) as WriterSymbolGraphRow[];\n\n if (syms.length === 0) return { nodes: [], edges: [] };\n\n const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));\n const relatedIds = new Set(syms.map((symbol) => symbol.id));\n\n const refRows = stmt(\n `SELECT from_id, to_id, call_type, COUNT(*) AS n\n FROM (\n SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line\n FROM refs r\n JOIN symbols s ON s.id = r.from_id\n WHERE s.file = ?\n UNION\n SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line\n FROM refs r\n JOIN symbols s ON s.id = r.to_id\n WHERE s.file = ?\n )\n WHERE to_id IS NOT NULL\n GROUP BY from_id, to_id, call_type`,\n ).all(fileFilter, fileFilter) as {\n from_id: number;\n to_id: number;\n call_type: string;\n n: number;\n }[];\n\n const edgeMap = new Map<string, WeightedEdgeAccumulator>();\n for (const r of refRows) {\n if (r.to_id == null) continue;\n relatedIds.add(r.from_id);\n relatedIds.add(r.to_id);\n const n = Number(r.n) || 0;\n addWeightedEdge(edgeMap, r.from_id, r.to_id, r.call_type, n);\n }\n const edges = materializeWeightedEdges(edgeMap, 'sym');\n\n const loadedIds = new Set(syms.map((s) => s.id));\n const missingIds = [...relatedIds].filter((id) => !loadedIds.has(id));\n if (missingIds.length > 0) {\n const placeholders = missingIds.map(() => '?').join(',');\n const extras = stmt(\n `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`,\n ).all(...missingIds) as WriterSymbolGraphRow[];\n for (const s of extras) symById.set(s.id, s);\n }\n\n const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter);\n return { nodes, edges };\n}\n", "import { resolveWstackPaths } from '@wrongstack/core/utils';\nimport type { Ref, Symbol as IndexSymbol } from './schema.js';\n\nexport function escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nexport function assignRefsToSymbols(refs: Ref[], symbols: IndexSymbol[]): Ref[] {\n if (refs.length === 0 || symbols.length === 0) return [];\n const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);\n const seen = new Set<string>();\n const assigned: Ref[] = [];\n for (const ref of refs) {\n let owner: IndexSymbol | undefined;\n for (const symbol of ordered) {\n if (symbol.line > ref.line) break;\n owner = symbol;\n }\n if (!owner && ref.callType === 'import') owner = ordered[0];\n if (!owner || owner.id <= 0) continue;\n const key = `${owner.id}:${ref.toName}:${ref.callType}`;\n if (seen.has(key)) continue;\n seen.add(key);\n assigned.push({ ...ref, fromId: owner.id });\n }\n return assigned;\n}\n\n/**\n * Resolve the per-project index directory. By default it lives under the\n * global project dir (`~/.wrongstack/projects/<hash>/codebase-index`).\n */\nexport function resolveIndexDir(projectRoot: string, override?: string): string {\n return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;\n}\n\n/**\n * Optional index-directory override carried on the run context's `meta` bag.\n */\nexport function codebaseIndexDirOverride(ctx: {\n meta?: Record<string, unknown>;\n}): string | undefined {\n const v = ctx.meta?.['codebaseIndexDir'];\n return typeof v === 'string' ? v : undefined;\n}\n", "import type { DatabaseSync } from 'node:sqlite';\nimport { sqliteCachePragmas } from '@wrongstack/core/utils';\n\nexport function applyIndexStorePragmas(db: DatabaseSync): void {\n try {\n db.exec('PRAGMA journal_mode = WAL');\n db.exec('PRAGMA synchronous = NORMAL');\n db.exec('PRAGMA busy_timeout = 15000');\n db.exec('PRAGMA temp_store = MEMORY');\n const cache = sqliteCachePragmas();\n db.exec(`PRAGMA cache_size = -${cache.cacheSizeKiB}`);\n db.exec(`PRAGMA mmap_size = ${cache.mmapBytes}`);\n db.exec('PRAGMA foreign_keys = ON');\n db.exec('PRAGMA journal_size_limit = 67108864');\n db.exec('PRAGMA wal_autocheckpoint = 1000');\n } catch {\n /* pragmas are best-effort; old SQLite builds without WAL still work */\n }\n}\n", "export const METADATA_TABLE_SQL = `\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n`;\n\nexport const CORE_TABLES_SQL = `\n CREATE TABLE IF NOT EXISTS files (\n file TEXT PRIMARY KEY,\n lang TEXT NOT NULL,\n mtime_ms INTEGER NOT NULL,\n symbol_count INTEGER NOT NULL DEFAULT 0,\n last_indexed INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS symbols (\n id INTEGER PRIMARY KEY,\n lang TEXT NOT NULL,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file TEXT NOT NULL,\n line INTEGER NOT NULL,\n col INTEGER NOT NULL,\n signature TEXT NOT NULL DEFAULT '',\n doc_comment TEXT NOT NULL DEFAULT '',\n scope TEXT NOT NULL DEFAULT '',\n text TEXT NOT NULL DEFAULT '',\n file_fk TEXT NOT NULL\n );\n`;\n\nexport const SYMBOL_INDEX_SQL = [\n 'CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)',\n 'CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)',\n 'CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)',\n 'CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)',\n 'CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)',\n 'CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)',\n 'CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)',\n] as const;\n\nexport const REFS_TABLE_SQL = `\n CREATE TABLE IF NOT EXISTS refs (\n id INTEGER PRIMARY KEY,\n from_id INTEGER NOT NULL,\n to_name TEXT NOT NULL,\n to_id INTEGER,\n call_type TEXT NOT NULL,\n line INTEGER NOT NULL\n );\n`;\n\nexport const REFS_INDEX_SQL = [\n 'CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)',\n 'CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)',\n 'CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)',\n 'CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)',\n] as const;\n\nexport const SYMBOLS_FTS_SQL =\n \"CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')\";\n", "import type { SearchResult, SymbolKind, SymbolLang } from './schema.js';\nimport { lspKindToInternalKind } from './lsp-kind.js';\nimport { escapeLike } from './writer-helpers.js';\n\nexport interface WriterSearchFilter {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n}\n\nexport interface WriterSearchRow {\n id: number;\n lang: string;\n kind: string;\n name: string;\n file: string;\n line: number;\n col: number;\n signature: string;\n doc_comment: string;\n text?: string;\n score?: number;\n snippet?: string;\n}\n\nexport function normalizeSearchLimit(limit: number | undefined): number | undefined {\n return typeof limit === 'number' && Number.isFinite(limit)\n ? Math.max(0, Math.trunc(limit))\n : undefined;\n}\n\nexport function buildWriterSearchWhere(\n query: string,\n filter?: WriterSearchFilter | undefined,\n): { where: string; values: unknown[] } | null {\n const conditions: string[] = [];\n const values: unknown[] = [];\n\n let effectiveKind: SymbolKind | undefined = filter?.kind;\n if (filter?.lspKind !== undefined) {\n const mapped = lspKindToInternalKind(filter.lspKind);\n if (mapped !== null) {\n effectiveKind = mapped;\n } else {\n return null;\n }\n }\n\n if (effectiveKind) {\n conditions.push('kind = ?');\n values.push(effectiveKind);\n }\n if (filter?.lang) {\n conditions.push('lang = ?');\n values.push(filter.lang);\n }\n if (filter?.file) {\n conditions.push(\"replace(file, '\\\\', '/') LIKE ? ESCAPE '\\\\'\");\n values.push(`%${escapeLike(filter.file.replace(/\\\\/g, '/'))}%`);\n }\n if (query.trim()) {\n const tokens = query.toLowerCase().split(/\\s+/).filter(Boolean);\n conditions.push(`(${tokens.map(() => 'text LIKE ?').join(' OR ')})`);\n for (const token of tokens) values.push(`%${token}%`);\n }\n\n return { where: conditions.length ? `WHERE ${conditions.join(' AND ')}` : '', values };\n}\n\nexport function mapWriterSearchRow(\n row: WriterSearchRow,\n lspKind: number | undefined,\n score = 0,\n snippet = '',\n): SearchResult {\n return {\n id: row.id,\n lang: row.lang as SymbolLang,\n kind: row.kind as SymbolKind,\n name: row.name,\n file: row.file,\n line: row.line,\n col: row.col,\n signature: row.signature,\n docComment: row.doc_comment,\n score,\n snippet,\n lspKind,\n };\n}\n", "interface PooledIndexStore {\n close(): void;\n}\n\ntype StoreFactory<TStore extends PooledIndexStore> = (\n projectRoot: string,\n opts?: { indexDir?: string | undefined },\n) => TStore;\n\n/**\n * How many warm connections to keep.\n *\n * Each pooled store carries SQLite's page cache and mmap reservation (128MiB\n * and 512MiB respectively on the `balanced` profile \u2014 see\n * `core/src/utils/perf-profile.ts`). The pool used to be unbounded with a\n * no-op `release()`, so every project a long-lived host touched became\n * permanent resident memory. Two keeps the common \"one project, occasionally a\n * second\" case warm; beyond that, reopening is cheap relative to the residency.\n */\nconst DEFAULT_MAX_WARM_STORES = 2;\n\ninterface PoolEntry<TStore> {\n store: TStore;\n /** Outstanding acquire() calls not yet released. Never evict while > 0. */\n refs: number;\n /** Monotonic counter for LRU ordering. */\n lastUsed: number;\n}\n\n/**\n * Warm-connection pool for IndexStore instances.\n *\n * Each (projectRoot, indexDir) pair gets one persisted store. Every read\n * operation (search, stats, graph) acquires the store from the pool instead of\n * opening a fresh SQLite connection, re-parsing the schema, and re-preparing\n * statements. The connection stays warm between calls, subject to\n * {@link DEFAULT_MAX_WARM_STORES}.\n */\nexport class StorePool<TStore extends PooledIndexStore> {\n private readonly stores = new Map<string, PoolEntry<TStore>>();\n private readonly keyByStore = new WeakMap<object, string>();\n private clock = 0;\n\n constructor(\n private readonly createStore: StoreFactory<TStore>,\n private readonly maxWarmStores: number = DEFAULT_MAX_WARM_STORES,\n ) {}\n\n private key(projectRoot: string, indexDir?: string): string {\n return `${projectRoot}\\u0000${indexDir ?? ''}`;\n }\n\n /** Borrow a store. Creates it on first access for this key. */\n acquire(projectRoot: string, opts?: { indexDir?: string | undefined }): TStore {\n const k = this.key(projectRoot, opts?.indexDir);\n let entry = this.stores.get(k);\n if (!entry) {\n const store = this.createStore(projectRoot, { indexDir: opts?.indexDir });\n entry = { store, refs: 0, lastUsed: 0 };\n this.stores.set(k, entry);\n this.keyByStore.set(store as object, k);\n }\n entry.refs++;\n entry.lastUsed = ++this.clock;\n return entry.store;\n }\n\n /**\n * Return the store to the pool. The connection stays warm for reuse, but the\n * pool may now close the least-recently-used *idle* connection to stay within\n * {@link maxWarmStores}. A store with outstanding refs is never closed.\n */\n release(store: TStore): void {\n const k = this.keyByStore.get(store as object);\n const entry = k === undefined ? undefined : this.stores.get(k);\n if (entry && entry.refs > 0) entry.refs--;\n this.trim();\n }\n\n private trim(): void {\n if (this.stores.size <= this.maxWarmStores) return;\n const idle = [...this.stores.entries()]\n .filter(([, entry]) => entry.refs === 0)\n .sort((a, b) => a[1].lastUsed - b[1].lastUsed);\n let overflow = this.stores.size - this.maxWarmStores;\n for (const [k, entry] of idle) {\n if (overflow <= 0) break;\n try {\n entry.store.close();\n } catch {\n /* already closed */\n }\n this.stores.delete(k);\n overflow--;\n }\n }\n\n /** Close every pooled connection and drain the pool. Call on shutdown. */\n closeAll(): void {\n for (const entry of this.stores.values()) {\n try {\n entry.store.close();\n } catch {\n /* already closed */\n }\n }\n this.stores.clear();\n }\n\n /** Remove one store from the pool. Used by tests that need isolation. */\n evict(projectRoot: string, indexDir?: string): void {\n const k = this.key(projectRoot, indexDir);\n const entry = this.stores.get(k);\n if (entry) {\n try {\n entry.store.close();\n } catch {\n /* already closed */\n }\n this.stores.delete(k);\n }\n }\n\n /** True when the pool holds a connection for the given key. */\n has(projectRoot: string, indexDir?: string): boolean {\n return this.stores.has(this.key(projectRoot, indexDir));\n }\n\n /** Number of warm connections currently held. */\n get size(): number {\n return this.stores.size;\n }\n}\n", "/**\n * Execution-location-agnostic index operations.\n *\n * One implementation, two callers: the index worker thread (production \u2014\n * synchronous SQLite and the TypeScript parser can never block the main\n * thread / terminal UI there) and the inline fallback inside the host (tests,\n * `WRONGSTACK_INDEX_INLINE=1`, or runtimes where the worker file is missing).\n *\n * Operations share one warm IndexStore per resolved project/index directory.\n * The detached server owns write serialization, while worker/inline fallbacks\n * run on one event loop, so a pooled connection is both safe and substantially\n * cheaper than re-running schema/FTS drift checks for every edited file.\n */\n\nimport { runIndexerWithStore } from './indexer.js';\nimport type { CodeMapGraph, IndexResult, IndexStats, SymbolKind, SymbolLang } from './schema.js';\nimport type { IndexOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs } from './worker-protocol.js';\nimport { indexStorePool } from './writer.js';\n\nexport interface ServiceHooks {\n signal?: AbortSignal | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\n/** Full or per-file index run. */\nexport async function indexService(\n args: IndexOpArgs,\n hooks: ServiceHooks = {},\n): Promise<IndexResult> {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return await runIndexerWithStore(store, {\n projectRoot: args.projectRoot,\n indexDir: args.indexDir,\n files: args.files,\n force: args.force,\n langs: args.langs,\n ignore: args.ignore,\n signal: hooks.signal,\n onProgress: hooks.onProgress,\n });\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** Ranked symbol search (FTS5 inside SQLite; BM25 fallback without FTS5). */\nexport function searchService(args: SearchOpArgs): SearchOpResult {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.searchRanked(\n args.query,\n {\n kind: args.kind as SymbolKind | undefined,\n lang: args.lang as SymbolLang | undefined,\n file: args.file,\n lspKind: args.lspKind,\n },\n args.limit,\n );\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** Index health and statistics. */\nexport function statsService(args: StatsOpArgs): IndexStats {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getStats();\n } finally {\n indexStorePool.release(store);\n }\n}\n\n// \u2500\u2500\u2500 CodeMap graph services \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Package-level dependency graph. */\nexport function packageGraphService(args: StatsOpArgs): CodeMapGraph {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getPackageGraph();\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** File-level dependency graph for a single package. */\nexport function fileGraphService(args: StatsOpArgs & { packageFilter: string }): CodeMapGraph {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getFileGraph(args.packageFilter);\n } finally {\n indexStorePool.release(store);\n }\n}\n\n/** Symbol-level dependency graph for a single file. */\nexport function symbolGraphService(args: StatsOpArgs & { fileFilter: string }): CodeMapGraph {\n const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });\n try {\n return store.getSymbolGraph(args.fileFilter);\n } finally {\n indexStorePool.release(store);\n }\n}\n", "import { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as net from 'node:net';\nimport { fileURLToPath } from 'node:url';\nimport { checkUnixSocketPath } from '@wrongstack/core/utils';\nimport { IndexTimeoutError, LockError } from './circuit-breaker.js';\nimport {\n PROJECT_INDEX_SERVER_PROTOCOL_VERSION,\n projectIndexServerBuildId,\n projectIndexServerEndpoint,\n projectIndexServerMetadataPath,\n} from './project-server-endpoint.js';\nimport {\n encodeProjectServerMessage,\n PROJECT_INDEX_SERVER_MAX_FRAME_CHARS,\n type ProjectIndexServerActivity,\n type ProjectIndexServerHealth,\n type ProjectIndexServerInfo,\n type ProjectServerMessage,\n} from './project-server-protocol.js';\nimport type { OpName, OpShapes } from './worker-protocol.js';\n\nconst CONNECT_ATTEMPT_TIMEOUT_MS = 750;\nconst SERVER_START_TIMEOUT_MS = 10_000;\nconst SERVER_CONTROL_TIMEOUT_MS = 5_000;\nconst SERVER_HEALTH_TIMEOUT_MS = 3_000;\nconst SERVER_HEARTBEAT_INTERVAL_MS = 10_000;\n\nclass StaleProjectIndexServerError extends Error {\n override readonly name = 'StaleProjectIndexServerError';\n\n constructor(\n message: string,\n readonly pid: number,\n ) {\n super(message);\n }\n}\n\ninterface PendingRequest {\n resolve(value: unknown): void;\n reject(error: unknown): void;\n timer: ReturnType<typeof setTimeout>;\n signal?: AbortSignal | undefined;\n onAbort?: (() => void) | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nexport interface ProjectServerCallOptions {\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n onProgress?: ((current: number, total: number) => void) | undefined;\n}\n\nexport interface ProjectIndexServerShutdownResult {\n stopped: boolean;\n pid?: number | undefined;\n reason?: string | undefined;\n}\n\nexport type ProjectIndexServerConnectionStatus =\n | 'unavailable'\n | 'offline'\n | 'connecting'\n | 'connected'\n | 'degraded'\n | 'unresponsive'\n | 'error'\n | 'stopping';\n\nexport interface ProjectIndexServerClientHealth {\n status: 'healthy' | 'degraded' | 'unresponsive';\n checkedAt: number;\n lastHealthyAt: number | null;\n latencyMs: number | null;\n missedHeartbeats: number;\n server?: ProjectIndexServerHealth | undefined;\n}\n\nexport interface ProjectIndexServerConnectionState {\n status: ProjectIndexServerConnectionStatus;\n connected: boolean;\n projectRoot?: string | undefined;\n indexDir?: string | undefined;\n endpoint?: string | undefined;\n pid?: number | undefined;\n lastError?: string | undefined;\n activity?: ProjectIndexServerActivity | undefined;\n health?: ProjectIndexServerClientHealth | undefined;\n}\n\ntype ProjectIndexServerConnectionListener = (state: ProjectIndexServerConnectionState) => void;\n\nconst connectionStates = new Map<string, ProjectIndexServerConnectionState>();\nconst connectionStateListeners = new Set<ProjectIndexServerConnectionListener>();\nlet latestConnectionState: ProjectIndexServerConnectionState = {\n status: 'offline',\n connected: false,\n};\n\n/**\n * Why the index daemon is or is not usable.\n *\n * A bare `null` cannot distinguish \"the operator asked for in-process mode\"\n * from \"the built server is missing\", so callers that degrade on `null` treat a\n * broken build as a supported mode. For the index that means every process\n * quietly builds its own FTS5 database instead of sharing the project's one\n * owner \u2014 same data, N copies, none of them authoritative.\n */\nexport type ProjectIndexDaemonAvailability =\n | { readonly kind: 'available'; readonly url: URL }\n /** `WRONGSTACK_INDEX_INLINE` / `WRONGSTACK_INDEX_SERVER=0` was set. */\n | { readonly kind: 'inline-requested' }\n /** No server entry point exists in any known output layout. */\n | { readonly kind: 'missing-build' }\n /**\n * The derived socket path cannot be bound on this platform (over the\n * `sun_path` byte limit \u2014 seen on macOS whose per-user TMPDIR is ~48 bytes).\n * A daemon spawned onto this endpoint dies silently (`stdio: 'ignore'`), so\n * callers must degrade explicitly instead of entering the connect loop.\n */\n | {\n readonly kind: 'endpoint-invalid';\n readonly endpoint: string;\n readonly byteLength: number;\n readonly maxBytes: number;\n };\n\nexport function resolveProjectIndexDaemonAvailability(\n projectRoot?: string,\n indexDir?: string,\n): ProjectIndexDaemonAvailability {\n if (process.env['WRONGSTACK_INDEX_INLINE'] || process.env['WRONGSTACK_INDEX_SERVER'] === '0') {\n return { kind: 'inline-requested' };\n }\n let builtUrl: URL | null = null;\n for (const rel of ['./project-server.js', './codebase-index/project-server.js']) {\n try {\n const url = new URL(rel, import.meta.url);\n if (url.protocol === 'file:' && fs.existsSync(fileURLToPath(url))) {\n builtUrl = url;\n break;\n }\n } catch {\n /* try the next candidate */\n }\n }\n if (builtUrl === null) return { kind: 'missing-build' };\n // Validate the endpoint OUTSIDE the build-probing try/catch. A throw here\n // (today: never; tomorrow: `assertUnixSocketPathWithinLimit` or a future\n // platform guard) must surface as a real availability kind, not be silently\n // swallowed and misreported as `missing-build`. Callers rely on the\n // `endpoint-invalid` branch to reject loudly instead of falling through to\n // an in-process index that would silently strip every connected client of\n // the shared per-project daemon.\n if (projectRoot !== undefined) {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n const check = checkUnixSocketPath(endpoint);\n if (!check.ok) {\n return {\n kind: 'endpoint-invalid',\n endpoint,\n byteLength: check.byteLength,\n maxBytes: check.maxBytes,\n };\n }\n }\n return { kind: 'available', url: builtUrl };\n}\n\nfunction resolveProjectServerUrl(): URL | null {\n const availability = resolveProjectIndexDaemonAvailability();\n return availability.kind === 'available' ? availability.url : null;\n}\n\nexport function projectIndexServerExpectedBuildId(): string | null {\n const override = process.env['WRONGSTACK_INDEX_SERVER_BUILD_ID']?.trim();\n if (override) return override;\n const url = resolveProjectServerUrl();\n return url ? projectIndexServerBuildId(url) : null;\n}\n\nexport function isProjectIndexServerAvailable(): boolean {\n return resolveProjectServerUrl() !== null;\n}\n\nfunction publishConnectionState(endpoint: string, state: ProjectIndexServerConnectionState): void {\n connectionStates.set(endpoint, state);\n latestConnectionState = state;\n for (const listener of connectionStateListeners) listener(state);\n}\n\nexport function getProjectIndexServerConnectionState(\n projectRoot?: string,\n indexDir?: string,\n): ProjectIndexServerConnectionState {\n if (projectRoot) {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n const existing = connectionStates.get(endpoint);\n if (existing) return existing;\n if (!isProjectIndexServerAvailable()) {\n return { status: 'unavailable', connected: false };\n }\n return {\n status: 'offline',\n connected: false,\n projectRoot,\n indexDir,\n endpoint,\n };\n }\n if (latestConnectionState.endpoint) return latestConnectionState;\n if (!isProjectIndexServerAvailable()) return { status: 'unavailable', connected: false };\n return latestConnectionState;\n}\n\nexport function onProjectIndexServerConnectionStateChange(\n listener: ProjectIndexServerConnectionListener,\n): () => void {\n connectionStateListeners.add(listener);\n return () => connectionStateListeners.delete(listener);\n}\n\nfunction remoteError(message: string, name?: string): Error {\n if (name === 'LockError') return new LockError(message);\n if (name === 'IndexTimeoutError') return new IndexTimeoutError(message);\n const error = new Error(message);\n if (name && name !== 'Error') error.name = name;\n return error;\n}\n\nfunction isProjectIndexServerHealth(value: unknown): value is ProjectIndexServerHealth {\n if (!value || typeof value !== 'object') return false;\n const health = value as Partial<ProjectIndexServerHealth>;\n const memory =\n health.memory && typeof health.memory === 'object'\n ? (health.memory as Partial<ProjectIndexServerHealth['memory']>)\n : undefined;\n const activity =\n health.activity && typeof health.activity === 'object'\n ? (health.activity as Partial<ProjectIndexServerActivity>)\n : undefined;\n return (\n typeof health.checkedAt === 'number' &&\n typeof health.uptimeMs === 'number' &&\n typeof memory?.rss === 'number' &&\n typeof memory.heapUsed === 'number' &&\n typeof memory.heapTotal === 'number' &&\n typeof memory.external === 'number' &&\n typeof health.clients === 'number' &&\n typeof health.activeRequests === 'number' &&\n typeof health.activeWrites === 'number' &&\n typeof health.queuedWrites === 'number' &&\n typeof health.pendingExternalFiles === 'number' &&\n typeof health.watchingExternal === 'boolean' &&\n typeof activity?.indexing === 'boolean' &&\n typeof activity.currentFile === 'number' &&\n typeof activity.totalFiles === 'number' &&\n typeof activity.generation === 'number'\n );\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction cancellationError(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new Error('Indexing cancelled');\n}\n\nclass ProjectServerConnection {\n private socket: net.Socket | null = null;\n private buffer = '';\n private info: ProjectIndexServerInfo | null = null;\n private activity: ProjectIndexServerActivity | null = null;\n private health: ProjectIndexServerClientHealth | null = null;\n private healthCheck: Promise<ProjectIndexServerClientHealth> | null = null;\n private connecting: Promise<void> | null = null;\n private connectResolve: (() => void) | null = null;\n private connectReject: ((error: unknown) => void) | null = null;\n private nextId = 1;\n private readonly pending = new Map<number, PendingRequest>();\n\n constructor(\n readonly projectRoot: string,\n readonly indexDir: string | undefined,\n readonly endpoint: string,\n ) {\n this.transition('offline');\n }\n\n private transition(\n status: ProjectIndexServerConnectionStatus,\n options: { pid?: number | undefined; error?: unknown } = {},\n ): void {\n const previous = connectionStates.get(this.endpoint);\n const pid = options.pid ?? (status === 'connected' ? this.info?.pid : undefined);\n const lastError =\n options.error === undefined\n ? status === 'error' || status === 'degraded' || status === 'unresponsive'\n ? previous?.lastError\n : undefined\n : options.error instanceof Error\n ? options.error.message\n : String(options.error);\n publishConnectionState(this.endpoint, {\n status,\n connected: status === 'connected' || status === 'degraded' || status === 'unresponsive',\n projectRoot: this.projectRoot,\n indexDir: this.indexDir,\n endpoint: this.endpoint,\n pid,\n lastError,\n ...(this.activity ? { activity: this.activity } : {}),\n ...(this.health ? { health: this.health } : {}),\n });\n }\n\n isConnected(): boolean {\n return this.socket !== null && !this.socket.destroyed && this.info !== null;\n }\n\n async checkHealth(\n spawnIfMissing = false,\n timeoutMs = SERVER_HEALTH_TIMEOUT_MS,\n ): Promise<ProjectIndexServerClientHealth> {\n await this.ensureConnected(spawnIfMissing);\n if (this.healthCheck) return this.healthCheck;\n const startedAt = Date.now();\n this.healthCheck = this.request<ProjectIndexServerHealth>({ type: 'ping' }, { timeoutMs })\n .then((server) => {\n const now = Date.now();\n this.health = {\n status: 'healthy',\n checkedAt: now,\n lastHealthyAt: now,\n latencyMs: Math.max(0, now - startedAt),\n missedHeartbeats: 0,\n ...(isProjectIndexServerHealth(server) ? { server } : {}),\n };\n this.transition('connected', { pid: this.info?.pid });\n return this.health;\n })\n .catch((error) => {\n if (!this.isConnected()) throw error;\n if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health!;\n const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;\n const status = missedHeartbeats >= 3 ? 'unresponsive' : 'degraded';\n this.health = {\n status,\n checkedAt: Date.now(),\n lastHealthyAt: this.health?.lastHealthyAt ?? null,\n latencyMs: null,\n missedHeartbeats,\n ...(this.health?.server ? { server: this.health.server } : {}),\n };\n this.transition(status, { pid: this.info?.pid, error });\n return this.health;\n })\n .finally(() => {\n this.healthCheck = null;\n });\n return this.healthCheck;\n }\n\n private markResponsive(): void {\n const now = Date.now();\n this.health = {\n status: 'healthy',\n checkedAt: now,\n lastHealthyAt: now,\n latencyMs: this.health?.latencyMs ?? null,\n missedHeartbeats: 0,\n ...(this.health?.server ? { server: this.health.server } : {}),\n };\n }\n\n async call<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n options: ProjectServerCallOptions,\n ): Promise<OpShapes[O]['result']> {\n if (options.signal?.aborted) throw cancellationError(options.signal);\n await this.ensureConnected(true);\n // Connection establishment can take up to ten seconds while electing and\n // spawning a server. Do not enqueue work after the caller cancelled during\n // that interval.\n if (options.signal?.aborted) throw cancellationError(options.signal);\n return this.request<OpShapes[O]['result']>({ type: 'request', op, args }, options);\n }\n\n async shutdownRemote(reason?: string): Promise<ProjectIndexServerShutdownResult> {\n try {\n await this.ensureConnected(false);\n } catch {\n return { stopped: false, reason: 'not-running' };\n }\n const pid = this.info?.pid;\n try {\n this.transition('stopping', { pid });\n await this.request<{ stopping: boolean }>(\n { type: 'shutdown', reason },\n { timeoutMs: SERVER_CONTROL_TIMEOUT_MS },\n );\n return { stopped: true, pid };\n } catch (error) {\n const forceKilled = this.forceKillKnownServer();\n return {\n stopped: forceKilled,\n pid,\n reason: forceKilled\n ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}`\n : error instanceof Error\n ? error.message\n : String(error),\n };\n } finally {\n this.close();\n }\n }\n\n async configure(watchExternal: boolean, debounceMs: number): Promise<void> {\n await this.ensureConnected(true);\n const startedAt = Date.now();\n const result = await this.request<{ watching: boolean; health?: unknown }>(\n { type: 'configure', watchExternal, debounceMs },\n { timeoutMs: SERVER_CONTROL_TIMEOUT_MS },\n );\n if (isProjectIndexServerHealth(result.health)) {\n const now = Date.now();\n this.health = {\n status: 'healthy',\n checkedAt: now,\n lastHealthyAt: now,\n latencyMs: Math.max(0, now - startedAt),\n missedHeartbeats: 0,\n server: result.health,\n };\n this.transition('connected', { pid: this.info?.pid });\n }\n }\n\n close(): void {\n const socket = this.socket;\n this.socket = null;\n this.info = null;\n this.activity = null;\n this.health = null;\n this.connectReject?.(new Error('codebase-index client disconnected'));\n this.connectResolve = null;\n this.connectReject = null;\n if (socket && !socket.destroyed) socket.destroy();\n this.rejectPending(new Error('codebase-index client disconnected'));\n this.transition('offline');\n maybeStopHeartbeatLoop();\n }\n\n private request<T>(\n message:\n | { type: 'request'; op: OpName; args: OpShapes[OpName]['args'] }\n | {\n type: 'shutdown';\n reason?: string | undefined;\n }\n | { type: 'configure'; watchExternal: boolean; debounceMs: number }\n | { type: 'ping' },\n options: ProjectServerCallOptions,\n ): Promise<T> {\n const socket = this.socket;\n if (!socket || socket.destroyed) {\n return Promise.reject(new Error('codebase-index server connection is not available'));\n }\n const id = this.nextId++;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n const entry = this.pending.get(id);\n if (!entry) return;\n this.pending.delete(id);\n this.write({ type: 'cancel', id });\n const error = new IndexTimeoutError(\n `Index ${message.type === 'request' ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`,\n );\n this.cleanupPending(entry);\n entry.reject(error);\n }, options.timeoutMs);\n timer.unref?.();\n\n const signal = options.signal;\n const onAbort = signal\n ? () => {\n const entry = this.pending.get(id);\n if (!entry) return;\n this.pending.delete(id);\n this.write({ type: 'cancel', id });\n this.cleanupPending(entry);\n entry.reject(cancellationError(signal));\n }\n : undefined;\n this.pending.set(id, {\n resolve,\n reject,\n timer,\n signal,\n onAbort,\n onProgress: options.onProgress,\n });\n if (signal && onAbort) {\n signal.addEventListener('abort', onAbort, { once: true });\n // AbortSignal does not replay an abort event to listeners attached\n // after it fired. Close the narrow setup race before writing.\n if (signal.aborted) {\n onAbort();\n return;\n }\n }\n this.write({ ...message, id });\n });\n }\n\n private async ensureConnected(spawnIfMissing: boolean): Promise<void> {\n if (this.socket && !this.socket.destroyed && this.info) return;\n if (this.connecting) return this.connecting;\n this.transition('connecting');\n this.connecting = this.connectWithElection(spawnIfMissing)\n .catch((error) => {\n this.transition('error', { error });\n throw error;\n })\n .finally(() => {\n this.connecting = null;\n });\n return this.connecting;\n }\n\n private async connectWithElection(spawnIfMissing: boolean): Promise<void> {\n const deadline =\n Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);\n let spawned = false;\n let staleAttempts = 0;\n let lastError: unknown = new Error('codebase-index server unavailable');\n while (Date.now() < deadline) {\n try {\n await this.connectOnce();\n return;\n } catch (error) {\n lastError = error;\n if (error instanceof StaleProjectIndexServerError) {\n staleAttempts++;\n if (!spawnIfMissing) break;\n if (staleAttempts >= 3) this.forceKillServer(error.pid);\n spawned = false;\n await delay(100);\n continue;\n }\n }\n if (!spawnIfMissing) break;\n if (!spawned) {\n this.spawnDetachedServer();\n spawned = true;\n }\n await delay(75);\n }\n throw lastError;\n }\n\n private connectOnce(): Promise<void> {\n this.socket?.destroy();\n this.socket = null;\n this.info = null;\n this.activity = null;\n this.health = null;\n this.buffer = '';\n\n return new Promise<void>((resolve, reject) => {\n const socket = net.createConnection(this.endpoint);\n this.socket = socket;\n socket.setEncoding('utf8');\n const timer = setTimeout(() => {\n reject(new Error('codebase-index server handshake timed out'));\n socket.destroy();\n }, CONNECT_ATTEMPT_TIMEOUT_MS);\n timer.unref?.();\n\n const finishResolve = () => {\n clearTimeout(timer);\n this.connectResolve = null;\n this.connectReject = null;\n resolve();\n };\n const finishReject = (error: unknown) => {\n clearTimeout(timer);\n this.connectResolve = null;\n this.connectReject = null;\n reject(error);\n };\n this.connectResolve = finishResolve;\n this.connectReject = finishReject;\n\n socket.on('data', (chunk: string) => this.onData(socket, chunk));\n socket.on('error', (error) => {\n if (!this.info) finishReject(error);\n });\n socket.on('close', () => this.onClose(socket));\n });\n }\n\n private onData(socket: net.Socket, chunk: string): void {\n if (socket !== this.socket) return;\n this.buffer += chunk;\n while (true) {\n const newline = this.buffer.indexOf('\\n');\n if (newline < 0) {\n if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {\n socket.destroy(new Error('codebase-index server response exceeds the IPC limit'));\n }\n return;\n }\n if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {\n socket.destroy(new Error('codebase-index server response exceeds the IPC limit'));\n return;\n }\n const line = this.buffer.slice(0, newline);\n this.buffer = this.buffer.slice(newline + 1);\n if (!line) continue;\n let message: ProjectServerMessage;\n try {\n message = JSON.parse(line) as ProjectServerMessage;\n } catch {\n socket.destroy(new Error('invalid codebase-index server response'));\n return;\n }\n this.onMessage(message);\n }\n }\n\n private onMessage(message: ProjectServerMessage): void {\n if (message.type === 'hello') {\n if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {\n this.rejectStaleServer(\n message,\n `codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`,\n );\n return;\n }\n const expectedBuildId = projectIndexServerExpectedBuildId();\n if (expectedBuildId && message.buildId !== expectedBuildId) {\n this.rejectStaleServer(\n message,\n `codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? 'legacy'}`,\n );\n return;\n }\n this.info = message;\n this.markResponsive();\n this.transition('connected', { pid: message.pid });\n ensureHeartbeatLoop();\n this.connectResolve?.();\n return;\n }\n if (message.type === 'index-state') {\n this.activity = message.state;\n this.markResponsive();\n this.transition('connected', { pid: this.info?.pid });\n return;\n }\n\n const entry = this.pending.get(message.id);\n if (!entry) return;\n this.markResponsive();\n const status = connectionStates.get(this.endpoint)?.status;\n if (status === 'degraded' || status === 'unresponsive') {\n this.transition('connected', { pid: this.info?.pid });\n }\n if (message.type === 'progress') {\n entry.onProgress?.(message.current, message.total);\n return;\n }\n this.pending.delete(message.id);\n this.cleanupPending(entry);\n if (message.ok) entry.resolve(message.result);\n else entry.reject(remoteError(message.error, message.errorName));\n }\n\n private onClose(socket: net.Socket): void {\n if (socket !== this.socket) return;\n const wasConnected = this.info !== null;\n this.socket = null;\n this.info = null;\n this.activity = null;\n this.health = null;\n const error = new Error('codebase-index server connection closed');\n this.connectReject?.(error);\n this.connectResolve = null;\n this.connectReject = null;\n this.rejectPending(error);\n if (wasConnected) this.transition('error', { error });\n maybeStopHeartbeatLoop();\n }\n\n private cleanupPending(entry: PendingRequest): void {\n clearTimeout(entry.timer);\n if (entry.signal && entry.onAbort) {\n entry.signal.removeEventListener('abort', entry.onAbort);\n }\n }\n\n private rejectPending(error: unknown): void {\n const entries = [...this.pending.values()];\n this.pending.clear();\n for (const entry of entries) {\n this.cleanupPending(entry);\n entry.reject(error);\n }\n }\n\n private write(message: object): void {\n const socket = this.socket;\n if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));\n }\n\n private rejectStaleServer(message: ProjectIndexServerInfo, reason: string): void {\n const socket = this.socket;\n if (socket && !socket.destroyed) {\n socket.write(\n encodeProjectServerMessage({\n type: 'shutdown',\n id: 0,\n reason: 'stale-build-replacement',\n }),\n );\n const timer = setTimeout(() => socket.destroy(), 25);\n timer.unref?.();\n }\n this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));\n }\n\n private spawnDetachedServer(): void {\n const url = resolveProjectServerUrl();\n if (!url) throw new Error('built codebase-index project server is unavailable');\n // Unix-domain socket files survive an unclean process death. We only reach\n // this branch after a direct connection attempt failed, so an existing\n // path is stale rather than a live server endpoint.\n if (process.platform !== 'win32') {\n try {\n fs.rmSync(this.endpoint, { force: true });\n } catch {\n /* bind/connect race will elect the winner */\n }\n }\n const args = [fileURLToPath(url), '--project-root', this.projectRoot];\n if (this.indexDir) args.push('--index-dir', this.indexDir);\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: 'ignore',\n windowsHide: true,\n env: process.env,\n });\n child.unref();\n }\n\n private forceKillKnownServer(): boolean {\n const pid = this.info?.pid;\n return pid ? this.forceKillServer(pid) : false;\n }\n\n private forceKillServer(pid: number): boolean {\n if (pid === process.pid) return false;\n try {\n process.kill(pid);\n const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);\n try {\n const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8')) as { pid?: number };\n if (metadata.pid === pid) fs.rmSync(metadataPath, { force: true });\n } catch {\n /* absent or already replaced */\n }\n return true;\n } catch {\n return false;\n }\n }\n}\n\nconst connections = new Map<string, ProjectServerConnection>();\nlet heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n\nfunction ensureHeartbeatLoop(): void {\n if (heartbeatTimer) return;\n heartbeatTimer = setInterval(() => {\n for (const connection of connections.values()) {\n if (connection.isConnected()) void connection.checkHealth(false).catch(() => {});\n }\n }, SERVER_HEARTBEAT_INTERVAL_MS);\n heartbeatTimer.unref?.();\n}\n\nfunction maybeStopHeartbeatLoop(): void {\n if (!heartbeatTimer) return;\n if ([...connections.values()].some((connection) => connection.isConnected())) return;\n clearInterval(heartbeatTimer);\n heartbeatTimer = undefined;\n}\n\nfunction connectionFor(projectRoot: string, indexDir?: string): ProjectServerConnection {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n let connection = connections.get(endpoint);\n if (!connection) {\n connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);\n connections.set(endpoint, connection);\n }\n return connection;\n}\n\nexport function callProjectIndexServer<O extends OpName>(\n op: O,\n args: OpShapes[O]['args'],\n options: ProjectServerCallOptions,\n): Promise<OpShapes[O]['result']> {\n return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);\n}\n\nexport function ensureProjectIndexServer(options: {\n projectRoot: string;\n indexDir?: string | undefined;\n watchExternal: boolean;\n debounceMs: number;\n}): Promise<void> {\n return connectionFor(options.projectRoot, options.indexDir).configure(\n options.watchExternal,\n options.debounceMs,\n );\n}\n\nexport function checkProjectIndexServerHealth(\n projectRoot: string,\n indexDir?: string,\n options: { timeoutMs?: number | undefined } = {},\n): Promise<ProjectIndexServerClientHealth> {\n return connectionFor(projectRoot, indexDir).checkHealth(\n false,\n options.timeoutMs ?? SERVER_HEALTH_TIMEOUT_MS,\n );\n}\n\nexport async function shutdownProjectIndexServer(\n projectRoot: string,\n indexDir?: string,\n reason?: string,\n): Promise<ProjectIndexServerShutdownResult> {\n const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);\n const connection = connectionFor(projectRoot, indexDir);\n try {\n return await connection.shutdownRemote(reason);\n } finally {\n connection.close();\n connections.delete(endpoint);\n connectionStates.delete(endpoint);\n }\n}\n\n/** Disconnect this process from every project server without stopping them. */\nexport function closeProjectIndexServerClients(): void {\n for (const connection of connections.values()) connection.close();\n connections.clear();\n connectionStates.clear();\n latestConnectionState = {\n status: isProjectIndexServerAvailable() ? 'offline' : 'unavailable',\n connected: false,\n };\n if (heartbeatTimer) clearInterval(heartbeatTimer);\n heartbeatTimer = undefined;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { assertUnixSocketPathWithinLimit } from '@wrongstack/core/utils';\nimport { resolveIndexDir } from './writer.js';\n\nexport const PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;\nexport const PROJECT_INDEX_SERVER_METADATA_FILE = 'server.json';\n/**\n * Short directory name that owns the per-project Unix socket on Linux.\n *\n * The directory is created `0o700` by `ensureProjectIndexSocketDirectory`: on\n * multi-user Linux `/tmp` the kernel's sticky bit otherwise lets a local\n * attacker pre-bind a predictable socket name (project paths are guessable\n * via the public `buildId` handshake), hijacking clients whose `bind()` hits\n * EADDRINUSE. macOS is unaffected because each user already has a private\n * TMPDIR, so the subdirectory adds no extra ownership boundary there.\n *\n * The name is kept short to stay under the 103-byte `sun_path` cap on macOS\n * (48 bytes of `/var/folders/<xx>/<30 chars>/T` + 8 + 24 + 5 = 85 bytes).\n */\nexport const PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;\n\nlet buildIdCache:\n | {\n file: string;\n mtimeMs: number;\n size: number;\n buildId: string;\n }\n | undefined;\n\n/**\n * Content identity for the actual server artifact.\n *\n * Package versions do not change during a local rebuild, so the handshake\n * hashes the resolved `project-server.js` itself. The stat guard avoids\n * re-reading the bundle on every reconnect while still noticing a rebuild in a\n * long-lived client process.\n */\nexport function projectIndexServerBuildId(entrypoint: string | URL): string {\n const file =\n entrypoint instanceof URL || entrypoint.startsWith('file:')\n ? fileURLToPath(entrypoint)\n : path.resolve(entrypoint);\n try {\n const stat = fs.statSync(file);\n if (\n buildIdCache?.file === file &&\n buildIdCache.mtimeMs === stat.mtimeMs &&\n buildIdCache.size === stat.size\n ) {\n return buildIdCache.buildId;\n }\n const buildId = createHash('sha256').update(fs.readFileSync(file)).digest('hex').slice(0, 24);\n buildIdCache = { file, mtimeMs: stat.mtimeMs, size: stat.size, buildId };\n return buildId;\n } catch {\n // Both sides resolve the same artifact path. This fallback keeps exotic\n // read-only packagers usable, though normal builds always take the hash.\n return `unreadable:${path.basename(file)}`;\n }\n}\n\nfunction normalizeLocalPath(value: string): string {\n const resolved = path.resolve(value);\n return process.platform === 'win32' ? resolved.toLowerCase() : resolved;\n}\n\n/**\n * Local project identity used by the index transport.\n *\n * The index directory is authoritative rather than the cross-machine project\n * id: worktrees and local clones may share a project id while requiring\n * physically separate SQLite indexes.\n */\nexport function projectIndexServerKey(projectRoot: string, indexDir?: string): string {\n const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));\n return createHash('sha256').update(resolvedIndexDir).digest('hex').slice(0, 24);\n}\n\n/**\n * Deterministic per-project local IPC endpoint.\n *\n * The Unix layout places the socket inside a short `wsci-v1/` subdirectory of\n * the temp dir: macOS's per-user TMPDIR is ~48 bytes of\n * `/var/folders/<xx>/<30 chars>/T`, and `sun_path` caps the whole socket path\n * at 104 bytes including the NUL on macOS/BSD (108 on Linux). The previous\n * `wrongstack-codebase-index-v1/<key>.sock` subdirectory layout came to ~107\n * bytes there, so `bind()` failed with ENAMETOOLONG inside a detached child\n * whose stderr was discarded \u2014 clients saw only a 10s connect timeout. The\n * short `wsci-v1/` subdirectory stays well under both limits (85 bytes on the\n * worst-case macOS TMPDIR) and restores the `0o700` ownership boundary the\n * flat layout lost on multi-user Linux `/tmp`. The pipe name keeps the long\n * prefix because named pipes have no such limit.\n */\nexport function projectIndexServerEndpoint(projectRoot: string, indexDir?: string): string {\n const key = projectIndexServerKey(projectRoot, indexDir);\n if (process.platform === 'win32') {\n return `\\\\\\\\.\\\\pipe\\\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;\n }\n // Derivation stays pure: clients need the endpoint VALUE to report or\n // degrade on an unbindable path (`resolveProjectIndexDaemonAvailability`,\n // connection-state probes). The hard length assert lives at bind time in\n // `ensureProjectIndexSocketDirectory`, platform-aware via the shared helper.\n return path.join(os.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);\n}\n\nexport function projectIndexServerMetadataPath(projectRoot: string, indexDir?: string): string {\n return path.join(\n path.resolve(resolveIndexDir(projectRoot, indexDir)),\n PROJECT_INDEX_SERVER_METADATA_FILE,\n );\n}\n\nexport function ensureProjectIndexSocketDirectory(endpoint: string): void {\n if (process.platform !== 'win32') {\n // Fail fast with an actionable message: an over-long sun_path would\n // otherwise surface as ENAMETOOLONG/EINVAL inside a detached child whose\n // stderr is discarded, leaving clients a bare 10s connect timeout.\n assertUnixSocketPathWithinLimit(endpoint, 'codebase-index');\n fs.mkdirSync(path.dirname(endpoint), { recursive: true, mode: 0o700 });\n }\n}\n", "import type { OpName, OpShapes } from './worker-protocol.js';\n\n/** Hard ceiling for one newline-delimited IPC message (measured as JS characters). */\nexport const PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;\n\nexport interface ProjectIndexServerInfo {\n protocolVersion: number;\n buildId: string;\n pid: number;\n projectRoot: string;\n indexDir: string;\n endpoint: string;\n startedAt: string;\n}\n\nexport interface ProjectIndexServerActivity {\n indexing: boolean;\n currentFile: number;\n totalFiles: number;\n generation: number;\n updatedAt: number | null;\n lastError: string | null;\n}\n\nexport interface ProjectIndexServerHealth {\n checkedAt: number;\n uptimeMs: number;\n memory: {\n rss: number;\n heapUsed: number;\n heapTotal: number;\n external: number;\n };\n clients: number;\n activeRequests: number;\n activeWrites: number;\n queuedWrites: number;\n pendingExternalFiles: number;\n watchingExternal: boolean;\n /** Clients currently requesting ownership of the shared external watcher. */\n watchingClients?: number | undefined;\n /** Server-side heartbeat lease applied to connected clients. */\n clientLeaseTimeoutMs?: number | undefined;\n /** Time since the least recently responsive client sent a message. */\n oldestClientIdleMs?: number | undefined;\n activity: ProjectIndexServerActivity;\n}\n\nexport type ProjectServerClientMessage =\n | { type: 'request'; id: number; op: OpName; args: OpShapes[OpName]['args'] }\n | { type: 'cancel'; id: number }\n | { type: 'configure'; id: number; watchExternal: boolean; debounceMs: number }\n | { type: 'ping'; id: number }\n | { type: 'shutdown'; id: number; reason?: string | undefined };\n\nexport type ProjectServerMessage =\n | ({ type: 'hello' } & ProjectIndexServerInfo)\n | { type: 'index-state'; state: ProjectIndexServerActivity }\n | { type: 'response'; id: number; ok: true; result: unknown }\n | { type: 'response'; id: number; ok: false; error: string; errorName?: string | undefined }\n | { type: 'progress'; id: number; current: number; total: number };\n\nexport function encodeProjectServerMessage(message: object): string {\n return `${JSON.stringify(message)}\\n`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "pos", "line", "fs", "path", "parseSymbols", "path", "fs", "resolve", "stdout", "code", "parseSymbols", "parseSymbols", "spawn", "fs", "os", "path", "resolve", "parseSymbols", "spawn", "fs", "path", "resolve", "stdout", "parseSymbols", "expectDefined", "path", "regexParse", "basename", "parseSymbols", "expectDefined", "regexParse", "makeSymbol", "fs", "toErrorMessage", "probe", "fs", "fileURLToPath", "expectDefined", "execFile", "fs", "path", "path", "parseSymbols", "expectDefined", "fs", "path", "delay", "fs", "path", "path", "DB_FILE", "expectDefined", "ts", "resolve", "execFile", "relative", "assignRefsToSymbols", "stat", "expectDefined", "assignRefsToSymbols", "spawn", "fs", "fileURLToPath", "createHash", "fs", "os", "path", "stat", "createHash", "fileURLToPath", "resolve", "spawn", "fileURLToPath", "resolve", "chain", "stat", "toErrorMessage", "symResult"]
7
7
  }