@wrongstack/tools 0.292.1 → 0.295.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_env.d.ts +1 -1
- package/dist/_env.d.ts.map +1 -1
- package/dist/_fetch-guard.d.ts.map +1 -1
- package/dist/_spawn-stream.d.ts +1 -1
- package/dist/_spawn-stream.d.ts.map +1 -1
- package/dist/_util.d.ts +1 -1
- package/dist/_util.d.ts.map +1 -1
- package/dist/audit.d.ts +1 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +81 -16
- package/dist/audit.js.map +2 -2
- package/dist/bash.d.ts +1 -1
- package/dist/bash.d.ts.map +1 -1
- package/dist/bash.js +66 -52
- package/dist/bash.js.map +3 -3
- package/dist/batch-tool-use.d.ts +1 -1
- package/dist/batch-tool-use.d.ts.map +1 -1
- package/dist/batch-tool-use.js +1 -1
- package/dist/batch-tool-use.js.map +1 -1
- package/dist/browser/index.js +10 -1
- package/dist/browser/index.js.map +2 -2
- package/dist/browser/manager.d.ts +2 -0
- package/dist/browser/manager.d.ts.map +1 -1
- package/dist/browser/tools.d.ts +1 -1
- package/dist/browser/tools.d.ts.map +1 -1
- package/dist/builtin.d.ts +1 -1
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +1394 -553
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts +2 -2
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/bm25.d.ts +3 -1
- package/dist/codebase-index/bm25.d.ts.map +1 -1
- package/dist/codebase-index/codebase-index-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-index-tool.d.ts.map +1 -1
- package/dist/codebase-index/codebase-search-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-search-tool.d.ts.map +1 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts.map +1 -1
- package/dist/codebase-index/generic-parser.d.ts +34 -0
- package/dist/codebase-index/generic-parser.d.ts.map +1 -0
- package/dist/codebase-index/go-parser.d.ts +1 -1
- package/dist/codebase-index/go-parser.d.ts.map +1 -1
- package/dist/codebase-index/index.d.ts +4 -2
- package/dist/codebase-index/index.d.ts.map +1 -1
- package/dist/codebase-index/index.js +1036 -341
- package/dist/codebase-index/index.js.map +4 -4
- package/dist/codebase-index/indexer.d.ts +6 -1
- package/dist/codebase-index/indexer.d.ts.map +1 -1
- package/dist/codebase-index/json-parser.d.ts +1 -1
- package/dist/codebase-index/languages.d.ts +25 -0
- package/dist/codebase-index/languages.d.ts.map +1 -0
- package/dist/codebase-index/py-parser.d.ts +9 -1
- package/dist/codebase-index/py-parser.d.ts.map +1 -1
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/codebase-index/rs-parser.d.ts +1 -1
- package/dist/codebase-index/rs-parser.d.ts.map +1 -1
- package/dist/codebase-index/schema.d.ts +9 -2
- package/dist/codebase-index/schema.d.ts.map +1 -1
- package/dist/codebase-index/spawn-gate.d.ts +15 -0
- package/dist/codebase-index/spawn-gate.d.ts.map +1 -0
- package/dist/codebase-index/ts-parser.d.ts +2 -2
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +961 -317
- package/dist/codebase-index/worker.js.map +4 -4
- package/dist/codebase-index/writer.d.ts +83 -4
- package/dist/codebase-index/writer.d.ts.map +1 -1
- package/dist/codebase-index/yaml-parser.d.ts +1 -1
- package/dist/design.d.ts +5 -2
- package/dist/design.d.ts.map +1 -1
- package/dist/design.js +36 -3
- package/dist/design.js.map +2 -2
- package/dist/diff.d.ts +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +2 -2
- package/dist/diff.js.map +1 -1
- package/dist/document.d.ts +1 -1
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +1 -1
- package/dist/document.js.map +1 -1
- package/dist/e2e.d.ts +1 -1
- package/dist/e2e.d.ts.map +1 -1
- package/dist/e2e.js +1 -1
- package/dist/e2e.js.map +1 -1
- package/dist/edit.d.ts +1 -1
- package/dist/edit.d.ts.map +1 -1
- package/dist/edit.js +3 -3
- package/dist/edit.js.map +2 -2
- package/dist/exec.d.ts +1 -1
- package/dist/exec.d.ts.map +1 -1
- package/dist/exec.js +60 -21
- package/dist/exec.js.map +3 -3
- package/dist/fetch.d.ts +1 -1
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +4 -5
- package/dist/fetch.js.map +2 -2
- package/dist/format.d.ts +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +81 -16
- package/dist/format.js.map +2 -2
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +3 -2
- package/dist/git.js.map +2 -2
- package/dist/glob.d.ts +1 -1
- package/dist/glob.d.ts.map +1 -1
- package/dist/glob.js +2 -2
- package/dist/glob.js.map +1 -1
- package/dist/grep.d.ts +1 -1
- package/dist/grep.d.ts.map +1 -1
- package/dist/grep.js +4 -3
- package/dist/grep.js.map +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1657 -705
- package/dist/index.js.map +4 -4
- package/dist/install.d.ts +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +85 -17
- package/dist/install.js.map +2 -2
- package/dist/json.d.ts +1 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +2 -2
- package/dist/json.js.map +2 -2
- package/dist/kanban.d.ts +4 -3
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +94 -27
- package/dist/kanban.js.map +2 -2
- package/dist/languages/execute-tool.d.ts +1 -1
- package/dist/languages/execute-tool.d.ts.map +1 -1
- package/dist/languages/execute.d.ts +1 -1
- package/dist/languages/execute.d.ts.map +1 -1
- package/dist/languages/index.d.ts +1 -1
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +99 -17
- package/dist/languages/index.js.map +2 -2
- package/dist/languages/package-tool.d.ts +3 -4
- package/dist/languages/package-tool.d.ts.map +1 -1
- package/dist/languages/plan.d.ts.map +1 -1
- package/dist/languages/profiles/additional.d.ts.map +1 -1
- package/dist/languages/profiles/primary.d.ts.map +1 -1
- package/dist/languages/tool.d.ts +1 -1
- package/dist/languages/tool.d.ts.map +1 -1
- package/dist/lint.d.ts +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +81 -16
- package/dist/lint.js.map +2 -2
- package/dist/logs.d.ts +1 -1
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +2 -2
- package/dist/logs.js.map +1 -1
- package/dist/memory.d.ts +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +1 -1
- package/dist/memory.js.map +1 -1
- package/dist/mode.d.ts +1 -1
- package/dist/mode.d.ts.map +1 -1
- package/dist/mode.js.map +1 -1
- package/dist/outdated.d.ts +1 -1
- package/dist/outdated.d.ts.map +1 -1
- package/dist/outdated.js +101 -19
- package/dist/outdated.js.map +2 -2
- package/dist/pack.d.ts +1 -1
- package/dist/pack.js +1394 -553
- package/dist/pack.js.map +4 -4
- package/dist/patch.d.ts +1 -1
- package/dist/patch.d.ts.map +1 -1
- package/dist/patch.js +2 -2
- package/dist/patch.js.map +1 -1
- package/dist/plan.d.ts +1 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +7 -7
- package/dist/plan.js.map +2 -2
- package/dist/process-guardian.d.ts +7 -0
- package/dist/process-guardian.d.ts.map +1 -1
- package/dist/process-registry-persistent.d.ts +5 -0
- package/dist/process-registry-persistent.d.ts.map +1 -1
- package/dist/process-registry.d.ts +9 -1
- package/dist/process-registry.d.ts.map +1 -1
- package/dist/process-registry.js +18 -3
- package/dist/process-registry.js.map +2 -2
- package/dist/ps-slash.js +54 -15
- package/dist/ps-slash.js.map +2 -2
- package/dist/read.d.ts +1 -1
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +3 -2
- package/dist/read.js.map +2 -2
- package/dist/replace.d.ts +1 -1
- package/dist/replace.d.ts.map +1 -1
- package/dist/replace.js +4 -4
- package/dist/replace.js.map +2 -2
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +2 -2
- package/dist/scaffold.js.map +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +4 -4
- package/dist/search.js.map +2 -2
- package/dist/session-kanban.d.ts +4 -1
- package/dist/session-kanban.d.ts.map +1 -1
- package/dist/session-kanban.js +23 -15
- package/dist/session-kanban.js.map +2 -2
- package/dist/set-working-dir.d.ts +1 -1
- package/dist/set-working-dir.d.ts.map +1 -1
- package/dist/skill.d.ts +1 -1
- package/dist/skill.d.ts.map +1 -1
- package/dist/skill.js +2 -5
- package/dist/skill.js.map +2 -2
- package/dist/spawn-background.d.ts.map +1 -1
- package/dist/task.d.ts +3 -2
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +6 -8
- package/dist/task.js.map +2 -2
- package/dist/test.d.ts +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +81 -16
- package/dist/test.js.map +2 -2
- package/dist/todo.d.ts +2 -1
- package/dist/todo.d.ts.map +1 -1
- package/dist/todo.js +4 -4
- package/dist/todo.js.map +2 -2
- package/dist/tool-help.d.ts +1 -1
- package/dist/tool-help.d.ts.map +1 -1
- package/dist/tool-help.js.map +1 -1
- package/dist/tool-search.d.ts +1 -1
- package/dist/tool-search.d.ts.map +1 -1
- package/dist/tool-search.js.map +1 -1
- package/dist/tool-tier.d.ts +16 -0
- package/dist/tool-tier.d.ts.map +1 -0
- package/dist/tool-tier.js +22233 -0
- package/dist/tool-tier.js.map +7 -0
- package/dist/tool-use.d.ts +1 -1
- package/dist/tool-use.d.ts.map +1 -1
- package/dist/tool-use.js.map +1 -1
- package/dist/tree.d.ts +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +2 -2
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.d.ts +1 -1
- package/dist/typecheck.d.ts.map +1 -1
- package/dist/typecheck.js +81 -16
- package/dist/typecheck.js.map +2 -2
- package/dist/write.d.ts +1 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +3 -2
- package/dist/write.js.map +2 -2
- package/package.json +7 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/codebase-index/writer.ts", "../../src/codebase-index/circuit-breaker.ts", "../../src/codebase-index/schema.ts", "../../src/codebase-index/lsp-kind.ts", "../../src/codebase-index/bm25.ts", "../../src/codebase-index/background-indexer.ts", "../../src/codebase-index/indexer.ts", "../../src/codebase-index/ts-parser.ts", "../../src/codebase-index/go-parser.ts", "../../src/_win32-resolve.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/codebase-index/gitignore.ts", "../../src/codebase-index/index-service.ts", "../../src/codebase-index/codebase-index-tool.ts", "../../src/codebase-index/codebase-search-tool.ts", "../../src/codebase-index/codebase-stats-tool.ts"],
|
|
4
|
-
"sourcesContent": ["import { expectDefined } from '@wrongstack/core';\nimport { LockError } from './circuit-breaker.js';\nimport { toErrorMessage } 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 { createRequire } from 'node:module';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { resolveWstackPaths } from '@wrongstack/core';\nimport type {\n FileMeta,\n IndexStats,\n Ref,\n SearchResult,\n Symbol as IndexSymbol,\n SymbolKind,\n SymbolLang,\n CodeMapGraph,\n GraphNode,\n GraphEdge,\n CallType,\n} from './schema.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { lspKindToInternalKind } from './lsp-kind.js';\nimport { buildBm25Index, buildIndexableText, tokenise } from './bm25.js';\nconst DB_FILE = 'index.db';\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\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 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 * matching every other piece of per-project state. Callers may pass an\n * explicit `override` (used by tests and any wiring that already resolved the\n * path) to avoid touching the real home directory.\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 * Production leaves it unset (the index resolves to the global per-project\n * dir); tests and bespoke wiring set `meta.codebaseIndexDir` to redirect it.\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\n/**\n * Warm-connection pool for IndexStore instances.\n *\n * Each (projectRoot, indexDir) pair gets one persisted IndexStore. 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 \u2014 only warm-up cost is\n * paid once per project per process lifetime.\n *\n * In WAL mode, readers coexist with the writer connection, so a single pooled\n * connection handles both reads and writes safely. The caller is responsible\n * for serializing writes (the host's promise-chain mutex does this); the pool\n * itself has no lock because the worker processes one message at a time.\n */\nexport class StorePool {\n private readonly stores = new Map<string, IndexStore>();\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 }): IndexStore {\n const k = this.key(projectRoot, opts?.indexDir);\n let store = this.stores.get(k);\n if (!store) {\n store = new IndexStore(projectRoot, { indexDir: opts?.indexDir });\n this.stores.set(k, store);\n }\n return store;\n }\n\n /** Return the store to the pool. The connection stays warm for subsequent\n * operations on the same (projectRoot, indexDir). */\n release(_store: IndexStore): void {\n // No-op \u2014 the store remains open with its prepared-statement cache intact,\n // ready for the next acquire() on the same key. The connection is closed\n // explicitly via closeAll() (shutdown) or evict() (test isolation).\n }\n\n /** Close every pooled connection and drain the pool. Call on shutdown. */\n closeAll(): void {\n for (const store of this.stores.values()) {\n try {\n 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 store = this.stores.get(k);\n if (store) {\n try {\n 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\n/** Process-wide singleton pool. */\nexport const indexStorePool = new StorePool();\n\nlet warningSilenced = false;\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 * Load `node:sqlite`'s `DatabaseSync` lazily. Keeping this off the module's\n * top-level import means the codebase-index tools can be registered at CLI boot\n * without eagerly loading SQLite \u2014 so a runtime that lacks `node:sqlite` (it is\n * experimental, available since Node 22.5) only fails if the index is actually\n * used, with a clear message instead of a crash on import.\n */\nfunction 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// \u2500\u2500\u2500 SQLite lock-error retry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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/** Maximum retry attempts for a lock-conflict error. */\nconst MAX_LOCK_RETRIES = 3;\n/**\n * Base delay (ms) before the first retry after a lock error. Each subsequent\n * retry doubles this (exponential backoff). Combined with the 5-second\n * busy_timeout pragma, this means: 5s (pragma) + 50ms + 100ms + 200ms per\n * attempt \u2014 enough to wait out most cross-process writer conflicts.\n */\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\n/**\n * Returns true when `err` represents a SQLite lock conflict (SQLITE_BUSY or\n * SQLITE_LOCKED). These are transient \u2014 another process holds the write lock\n * and will release it shortly. Retry instead of failing.\n *\n * node:sqlite surfaces these as plain Error instances with `code` set to\n * 'SQLITE_BUSY' or 'SQLITE_LOCKED', or with a message that contains the\n * error name. Defensive: anything we can't classify as safe is NOT treated\n * as a lock error so real failures are not retried indefinitely.\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; // SQLITE_BUSY=5, SQLITE_LOCKED=6\n // node:sqlite sometimes surfaces the numeric code as a string in the message\n if (/SQLITE_(BUSY|LOCKED)/.test(err.message)) return true;\n return false;\n}\n\n/**\n * Synchronous sleep via Atomics.wait on a zero-length SharedArrayBuffer.\n * This is the only way to synchronously block in a Worker thread without\n * busy-waiting. The main thread (where DatabaseSync is never used) is\n * unaffected.\n *\n * The call is wrapped in try/catch because Atomics.wait throws in browsers\n * and other environments where SharedArrayBuffer is not available.\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 // Atomics.wait not available (browser, unknown runtime) \u2014 fall through.\n // The retry still happens but without sleeping, which is acceptable because\n // busy_timeout already handled the bulk of the wait.\n }\n}\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 /** 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 /**\n * Execute a SQLite write operation with automatic retry on lock conflicts.\n *\n * When another wstack process is holding the write lock the statement first\n * waits up to `busy_timeout` ms, then throws SQLITE_BUSY. This wrapper catches\n * that error and retries (up to MAX_LOCK_RETRIES) with exponential backoff,\n * giving the competing writer time to finish and release the lock.\n *\n * @param fn The write operation to execute. Can return a value which is\n * returned to the caller on success.\n * @throws {@link LockError} when all retries are exhausted on a lock conflict\n * (non-lock errors always propagate on the first attempt).\n */\n runWithRetry<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 // All retries exhausted \u2014 wrap in LockError so the circuit breaker\n // knows this is a transient lock conflict, not a real failure.\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 // Exponential backoff: 50ms \u2192 100ms \u2192 200ms, capped at 500ms.\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; // unreachable \u2014 satisfies TypeScript\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 // Multi-process safety: several wstack surfaces (TUI, WebUI, parallel\n // terminals) share this per-project db. WAL lets readers coexist with the\n // writer, and busy_timeout gives SQLite a head start waiting for the lock.\n // When the timeout expires the statement throws SQLITE_BUSY; the\n // runWithRetry() wrapper then retries with exponential backoff so most\n // lock-conflict errors are resolved without a circuit-breaker failure.\n try {\n this.db.exec('PRAGMA journal_mode = WAL');\n this.db.exec('PRAGMA busy_timeout = 5000');\n } catch {\n /* pragmas are best-effort \u2014 an old SQLite build without WAL still works */\n }\n this.initSchema();\n }\n\n private initSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\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.db\n .prepare('SELECT value FROM metadata WHERE key = ?')\n .all('version') as { value: string }[];\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.db\n .prepare('UPDATE metadata SET value = ? WHERE key = ?')\n .run(String(SCHEMA_VERSION), 'version');\n } else if (storedVersion === null) {\n this.db\n .prepare('INSERT INTO metadata(key, value) VALUES (?, ?)')\n .run('version', String(SCHEMA_VERSION));\n }\n\n this.db.exec(`\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\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)');\n // Compound index for the common lang+kind filter in ranked search.\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)');\n // Index on file_fk: deleteSymbolsForFile and FTS delete paths query by\n // file_fk \u2014 without this index every reindex/delete scans the symbols table.\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)');\n\n this.db.exec(`\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\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)');\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(\n \"CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')\",\n );\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.db.prepare('SELECT COUNT(*) AS n FROM symbols').get() as { n?: number } | undefined)\n ?.n ?? 0,\n );\n const ftsCount = Number(\n (\n this.db.prepare('SELECT COUNT(*) AS n FROM symbols_fts').get() as\n | { n?: number }\n | undefined\n )?.n ?? 0,\n );\n if (symbolCount !== ftsCount) {\n this.db.exec('DELETE FROM symbols_fts');\n const insert = this.db.prepare('INSERT INTO symbols_fts(rowid, text) VALUES (?, ?)');\n const rows = this.db\n .prepare('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 for (const row of rows) {\n insert.run(row.id, buildIndexableText(row.name, row.signature, row.doc_comment));\n }\n }\n } catch {\n // SQLite built without FTS5 \u2014 searchRanked falls back to LIKE + BM25.\n this.ftsAvailable = false;\n }\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`. The ID allocation (`SELECT MAX(id)`) and all `INSERT`s share\n * the same transaction, preventing UNIQUE constraint violations when two\n * processes index concurrently (each would see a different `MAX(id)` and\n * neither can insert with the other's IDs).\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 return this.runWithRetry(() => {\n // BEGIN IMMEDIATE takes a write lock immediately (does not wait for the\n // first INSERT). This serializes writers: the next process blocks here\n // until this transaction commits, so its `SELECT MAX(id)` sees all IDs\n // we assign \u2014 no collisions.\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const maxRows = this.stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n let nextId = (maxRows[0]?.m ?? 0) + 1;\n\n const stmt = this.db.prepare(\n `INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n const ftsStmt = this.ftsAvailable\n ? this.db.prepare('INSERT INTO symbols_fts(rowid, text) VALUES (?, ?)')\n : null;\n\n const result: IndexSymbol[] = [];\n for (const s of symbols) {\n const id = nextId++;\n stmt.run(\n id,\n s.lang,\n s.kind,\n s.name,\n s.file,\n s.line,\n s.col,\n s.signature,\n s.docComment,\n s.scope,\n s.text,\n s.file,\n );\n ftsStmt?.run(id, buildIndexableText(s.name, s.signature, s.docComment));\n result.push({ ...s, id });\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.runWithRetry(() => {\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 });\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.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\n if (this.ftsAvailable) {\n this.db\n .prepare(\n 'DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)',\n )\n .run(file);\n }\n this.db\n .prepare('DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file_fk = ?)')\n .run(file);\n this.db.prepare('DELETE FROM symbols WHERE file_fk = ?').run(file);\n this.db.prepare('DELETE FROM files WHERE file = ?').run(file);\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 const rows = this.stmt(\n 'SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?',\n ).all(file) as {\n file: string;\n lang: string;\n mtime_ms: number;\n symbol_count: number;\n last_indexed: number;\n }[];\n if (!rows.length) return null;\n const r = expectDefined(rows[0]);\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\n getAllFileMetas(): FileMeta[] {\n return (\n this.db\n .prepare('SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files')\n .all() as {\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\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?: {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n },\n ): SearchResult[] {\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 // LSP kind was explicitly provided but has no internal mapping \u2192 no results\n return [];\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 const tokenConds = tokens.map(() => 'text LIKE ?');\n conditions.push(`(${tokenConds.join(' OR ')})`);\n for (const t of tokens) values.push(`%${t}%`);\n }\n\n const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';\n const sql = `SELECT id, lang, kind, name, file, line, col, signature, doc_comment, text FROM symbols ${where}`;\n\n const stmt = this.db.prepare(sql);\n const rows = stmt.all(...(values as (string | number)[])) 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 text: string;\n }[];\n\n return rows.map((r) => ({\n id: r.id,\n lang: r.lang as SymbolLang,\n kind: r.kind as SymbolKind,\n name: r.name,\n file: r.file,\n line: r.line,\n col: r.col,\n signature: r.signature,\n docComment: r.doc_comment,\n score: 0,\n snippet: '',\n lspKind: filter?.lspKind,\n }));\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:\n | {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n }\n | 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.db\n .prepare(\n `SELECT COUNT(*) AS n FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid WHERE ${where}`,\n )\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.db\n .prepare(\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 )\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((r) => ({\n id: r.id,\n lang: r.lang as SymbolLang,\n kind: r.kind as SymbolKind,\n name: r.name,\n file: r.file,\n line: r.line,\n col: r.col,\n signature: r.signature,\n docComment: r.doc_comment,\n // bm25() is negative-is-better; negate so callers keep \"higher is\n // better\" and clamp so a match never reports a zero score.\n score: Math.max(0.0001, r.score),\n snippet: r.snippet,\n lspKind: filter?.lspKind,\n })),\n total,\n };\n }\n\n /** Legacy ranked path: LIKE candidates + in-process BM25 + JS snippets. */\n private searchRankedFallback(\n query: string,\n filter:\n | {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n }\n | undefined,\n limit: number,\n ): { results: SearchResult[]; total: number } {\n const candidates = this.search(query, filter);\n if (candidates.length === 0) return { results: [], total: 0 };\n\n if (!query.trim()) {\n return { results: candidates.slice(0, limit), total: candidates.length };\n }\n\n const candidateById = new Map(candidates.map((c) => [c.id, c]));\n const bm25 = buildBm25Index(\n candidates.map((c) => ({\n id: c.id,\n text: buildIndexableText(c.name, c.signature, c.docComment),\n })),\n );\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 (\n this.stmt('SELECT id, text FROM symbols').all() as { id: number; text: string }[]\n ).map(({ id, text }) => ({ id, text }));\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 const rows = this.stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n return rows[0]?.m ?? 0;\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 const sizeBytes = this.sizeBytes();\n\n const lastRows = this.db\n .prepare(\"SELECT value FROM metadata WHERE key = 'last_indexed'\")\n .all() as { value: string }[];\n const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;\n\n const totalRows = this.stmt('SELECT COUNT(*) FROM symbols').all() as {\n 'COUNT(*)': number;\n }[];\n const totalSymbols = totalRows[0] ? Number(totalRows[0]['COUNT(*)']) : 0;\n\n const fileRows = this.stmt('SELECT COUNT(*) FROM files').all() as {\n 'COUNT(*)': number;\n }[];\n const totalFiles = fileRows[0] ? Number(fileRows[0]['COUNT(*)']) : 0;\n\n const langRows = this.stmt('SELECT lang, COUNT(*) FROM symbols GROUP BY lang').all() as {\n lang: string;\n 'COUNT(*)': number;\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 kindRows = this.stmt('SELECT kind, COUNT(*) FROM symbols GROUP BY kind').all() as {\n kind: string;\n 'COUNT(*)': number;\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,\n totalFiles,\n byLang,\n byKind,\n indexPath: this.indexDir,\n lastIndexed,\n sizeBytes,\n version: SCHEMA_VERSION,\n };\n }\n\n setLastIndexed(ts: number): void {\n this.runWithRetry(() => {\n this.db\n .prepare(\"INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)\")\n .run(String(ts));\n });\n }\n\n getMetadata(key: string): string | undefined {\n const rows = this.db.prepare('SELECT value FROM metadata WHERE key = ?').all(key) as {\n value: string;\n }[];\n return rows[0]?.value;\n }\n\n setMetadata(key: string, value: string): void {\n this.runWithRetry(() => {\n this.db.prepare('INSERT OR REPLACE INTO metadata(key, value) VALUES(?, ?)').run(key, value);\n });\n }\n\n clearAll(): void {\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 } 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\n const stmt = this.db.prepare(\n `INSERT INTO refs(from_id, to_name, to_id, call_type, line)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const ref of refs) {\n stmt.run(fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);\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 const stmt = this.db.prepare(\n `INSERT INTO refs(from_id, to_name, to_id, call_type, line)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const ref of refs) {\n stmt.run(ref.fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);\n }\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 return this.runWithRetry(() => {\n this.db.exec('BEGIN IMMEDIATE');\n try {\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 if (this.ftsAvailable) {\n this.db\n .prepare(\n `DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n )\n .run(...options.deleteForFiles);\n }\n // Refs first (FK direction: refs.from_id \u2192 symbols.id).\n this.db\n .prepare(\n `DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n )\n .run(...options.deleteForFiles);\n this.db\n .prepare(`DELETE FROM symbols WHERE file IN (${placeholders})`)\n .run(...options.deleteForFiles);\n }\n\n // 2) Assign ids + insert symbols.\n const maxRows = this.stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n let nextId = (maxRows[0]?.m ?? 0) + 1;\n\n const symStmt = this.db.prepare(\n `INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n const ftsStmt = this.ftsAvailable\n ? this.db.prepare('INSERT INTO symbols_fts(rowid, text) VALUES (?, ?)')\n : null;\n\n const allInserted: IndexSymbol[] = [];\n const refsToInsert: Ref[] = [];\n\n for (const entry of entries) {\n const insertedForEntry: IndexSymbol[] = [];\n for (const s of entry.symbols) {\n const id = nextId++;\n symStmt.run(\n id,\n s.lang,\n s.kind,\n s.name,\n s.file,\n s.line,\n s.col,\n s.signature,\n s.docComment,\n s.scope,\n s.text,\n s.file,\n );\n ftsStmt?.run(id, buildIndexableText(s.name, s.signature, s.docComment));\n const inserted = { ...s, id };\n allInserted.push(inserted);\n insertedForEntry.push(inserted);\n }\n refsToInsert.push(...assignRefsToSymbols(entry.refs, insertedForEntry));\n }\n\n // 3) Insert all refs in one go.\n if (refsToInsert.length > 0) {\n const refStmt = this.db.prepare(\n `INSERT INTO refs(from_id, to_name, to_id, call_type, line)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const ref of refsToInsert) {\n refStmt.run(ref.fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);\n }\n }\n\n // 4) Upsert file metadata for every entry.\n const upsertStmt = this.db.prepare(\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.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.db\n .prepare('DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)')\n .run(file);\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 const result = this.db\n .prepare(\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 )\n .run() as { changes?: number };\n return result.changes ?? 0;\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 (\n this.db\n .prepare(\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 )\n .all(symbolId, symbolId) as {\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 ).map((r) => ({\n id: r.id,\n fromId: r.from_id,\n toName: r.to_name,\n toId: r.to_id ?? undefined,\n callType: r.call_type as Ref['callType'],\n line: r.line,\n }));\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 (\n this.db\n .prepare('SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE from_id = ?')\n .all(symbolId) as {\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 ).map((r) => ({\n id: r.id,\n fromId: r.from_id,\n toName: r.to_name,\n toId: r.to_id ?? undefined,\n callType: r.call_type as Ref['callType'],\n line: r.line,\n }));\n }\n\n private sizeBytes(): number {\n const dbPath = path.join(this.indexDir, DB_FILE);\n try {\n return fs.statSync(dbPath).size;\n } catch {\n return 0;\n }\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 * Derive a monorepo package name from an absolute file path.\n * Handles both `packages/<name>/...` (workspace) and `src/<name>/...` layouts.\n * Returns `undefined` for files outside any recognisable package structure.\n */\n private static derivePackage(filePath: string): string | undefined {\n // Normalise backslashes (Windows) to forward slashes.\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\n private static 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\n private static 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\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 type Row = { file: string; id: number };\n // Only file+id are needed \u2014 name, kind, lang, line are not used for\n // package-level aggregation. Narrow columns reduce data transfer from\n // SQLite to JS by ~60% for this query.\n const symbols = this.db\n .prepare('SELECT file, id FROM symbols ORDER BY id')\n .all() as Row[];\n\n // Build per-package aggregates\n const pkgNodes = new Map<string, GraphNode>();\n const fileToPkg = new Map<string, string>();\n const symbolToPkg = new Map<number, string>();\n\n for (const s of symbols) {\n const pkg = IndexStore.derivePackage(s.file) ?? '(root)';\n fileToPkg.set(s.file, pkg);\n symbolToPkg.set(s.id, pkg);\n const node = pkgNodes.get(pkg);\n if (node) {\n node.symbolCount = (node.symbolCount ?? 0) + 1;\n } else {\n pkgNodes.set(pkg, {\n id: `pkg:${pkg}`,\n label: pkg,\n kind: 'package' as const,\n package: pkg,\n symbolCount: 1,\n fileCount: 0,\n });\n }\n }\n\n // File counts per package\n const files = this.db.prepare('SELECT DISTINCT file FROM files').all() as { file: string }[];\n for (const { file } of files) {\n const pkg = IndexStore.derivePackage(file) ?? '(root)';\n const node = pkgNodes.get(pkg);\n if (node) node.fileCount = (node.fileCount ?? 0) + 1;\n }\n\n // Cross-package edges from resolved refs\n const refRows = this.db\n .prepare(\n `SELECT r.from_id, r.to_id, r.call_type\n FROM refs r\n WHERE r.to_id IS NOT NULL`,\n )\n .all() as { from_id: number; to_id: number; call_type: string }[];\n\n const edgeMap = new Map<string, { weight: number; types: Map<string, number> }>();\n for (const r of refRows) {\n if (r.call_type === 'import') continue;\n const fromPkg = symbolToPkg.get(r.from_id);\n const toPkg = symbolToPkg.get(r.to_id);\n if (!fromPkg || !toPkg || fromPkg === toPkg) continue;\n const key = `${fromPkg}\\u0000${toPkg}`;\n let e = edgeMap.get(key);\n if (!e) {\n e = { weight: 0, types: new Map() };\n edgeMap.set(key, e);\n }\n e.weight++;\n e.types.set(r.call_type, (e.types.get(r.call_type) ?? 0) + 1);\n }\n\n // Module imports are path/package references, not symbol names. They must\n // bypass symbol resolution or aliases/barrels leave the architecture with\n // isolated boxes even though source imports are explicit.\n const importRows = this.db\n .prepare(\n `SELECT r.from_id, r.to_name\n FROM refs r\n WHERE r.call_type = 'import'`,\n )\n .all() as { from_id: number; to_name: string }[];\n for (const r of importRows) {\n const fromPkg = symbolToPkg.get(r.from_id);\n const toPkg = IndexStore.packageFromImport(r.to_name);\n if (!fromPkg || !toPkg || fromPkg === toPkg || !pkgNodes.has(toPkg)) continue;\n const key = `${fromPkg}\\u0000${toPkg}`;\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++;\n edge.types.set('import', (edge.types.get('import') ?? 0) + 1);\n }\n\n const edges: GraphEdge[] = [];\n for (const [key, e] of edgeMap) {\n const [source, target] = key.split('\\u0000');\n // Dominant ref type = most frequent\n let bestType = 'call';\n let bestCount = 0;\n for (const [t, c] of e.types) {\n if (c > bestCount) {\n bestType = t;\n bestCount = c;\n }\n }\n edges.push({\n source: `pkg:${source}`,\n target: `pkg:${target}`,\n weight: e.weight,\n refType: bestType as CallType,\n });\n }\n\n return { nodes: [...pkgNodes.values()], edges };\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 type SymRow = {\n file: string;\n id: number;\n name: string;\n kind: string;\n lang: string;\n line: number;\n };\n\n // Phase 1: Discover which files belong to the target package. Instead of\n // loading every symbol row (5500+) and filtering in JS, scan just the\n // distinct file paths \u2014 a much smaller result set.\n const allFiles = this.db\n .prepare('SELECT DISTINCT file FROM symbols')\n .all() as { file: string }[];\n const pkgFilePaths = allFiles\n .filter((f) => (IndexStore.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 // Phase 2: Query symbols only for files in this package.\n const filePlaceholders = [...localFiles].map(() => '?').join(',');\n const pkgSyms = this.db\n .prepare(\n `SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`,\n )\n .all(...pkgFilePaths) as SymRow[];\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' as const,\n package: IndexStore.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\n // Build indexed-files set for import resolution \u2014 cheap DISTINCT query\n // on file paths only (no full symbol rows).\n const indexedFiles = new Set(allFiles.map((f) => f.file));\n\n // Phase 3: Restrict refs to those involving the target package's symbols\n // instead of scanning every ref row in the project.\n const refRows = this.db\n .prepare(\n `SELECT r.from_id, r.to_id, r.call_type\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 )\n .all(...pkgFilePaths, ...pkgFilePaths) as {\n from_id: number;\n to_id: number;\n call_type: string;\n }[];\n\n // Collect symbol ids referenced across packages for lazy fetch\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 = this.db\n .prepare(\n `SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`,\n )\n .all(...crossRefIds) 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, { weight: number; types: Map<string, number> }>();\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 key = `${fromFile}\\u0000${toFile}`;\n let e = edgeMap.get(key);\n if (!e) {\n e = { weight: 0, types: new Map() };\n edgeMap.set(key, e);\n }\n e.weight++;\n e.types.set(r.call_type, (e.types.get(r.call_type) ?? 0) + 1);\n }\n\n const importRows = this.db\n .prepare(\n `SELECT r.from_id, r.to_name\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 )\n .all(...pkgFilePaths) as { from_id: number; to_name: string }[];\n for (const r of importRows) {\n const fromFile = symToFile.get(r.from_id);\n if (!fromFile || !localFiles.has(fromFile)) continue;\n const toFile = IndexStore.resolveRelativeImport(fromFile, r.to_name, indexedFiles);\n if (!toFile || fromFile === toFile) continue;\n ensureFileNode(fromFile);\n ensureFileNode(toFile);\n const key = `${fromFile}\\u0000${toFile}`;\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++;\n edge.types.set('import', (edge.types.get('import') ?? 0) + 1);\n }\n\n const edges: GraphEdge[] = [];\n for (const [key, e] of edgeMap) {\n const [source, target] = key.split('\\u0000');\n let bestType = 'call';\n let bestCount = 0;\n for (const [t, c] of e.types) {\n if (c > bestCount) {\n bestType = t;\n bestCount = c;\n }\n }\n edges.push({\n source: `file:${source}`,\n target: `file:${target}`,\n weight: e.weight,\n refType: bestType as CallType,\n });\n }\n\n return { nodes: [...fileNodes.values()], edges };\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 type SymRow = {\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 // Query ONLY symbols from the target file \u2014 avoids loading the entire\n // project's symbol table (5500+ rows) just to render one file's graph.\n // Cross-file symbols referenced via refs are fetched lazily below.\n const syms = this.db\n .prepare(\n 'SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id',\n )\n .all(fileFilter) as SymRow[];\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 toGraphNode = (s: SymRow): GraphNode => ({\n id: `sym:${s.id}`,\n label: s.name,\n kind: 'symbol' as const,\n symbolId: s.id,\n symbolKind: s.kind as SymbolKind,\n file: s.file,\n package: IndexStore.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 // Collect refs FROM symbols in this file (outgoing) and TO symbols in this\n // file (incoming), so the graph shows both callers and callees.\n const symIds = new Set(syms.map((s) => s.id));\n const refRows = this.db\n .prepare(\n `SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line\n FROM refs r\n WHERE r.from_id IN (SELECT id FROM symbols WHERE file = ?)\n OR r.to_id IN (SELECT id FROM symbols WHERE file = ?)`,\n )\n .all(fileFilter, fileFilter) as {\n from_id: number;\n to_id: number | null;\n to_name: string;\n call_type: string;\n line: number;\n }[];\n\n const edgeMap = new Map<string, { weight: number; types: Map<string, number> }>();\n for (const r of refRows) {\n if (r.to_id === null) continue;\n // Only include edges where at least one endpoint is in our file\n if (!symIds.has(r.from_id) && !symIds.has(r.to_id)) continue;\n relatedIds.add(r.from_id);\n relatedIds.add(r.to_id);\n const key = `${r.from_id}\\u0000${r.to_id}`;\n let e = edgeMap.get(key);\n if (!e) {\n e = { weight: 0, types: new Map() };\n edgeMap.set(key, e);\n }\n e.weight++;\n e.types.set(r.call_type, (e.types.get(r.call_type) ?? 0) + 1);\n }\n\n const edges: GraphEdge[] = [];\n for (const [key, e] of edgeMap) {\n const [fromId, toId] = key.split('\\u0000').map(Number);\n let bestType = 'call';\n let bestCount = 0;\n for (const [t, c] of e.types) {\n if (c > bestCount) {\n bestType = t;\n bestCount = c;\n }\n }\n edges.push({\n source: `sym:${fromId}`,\n target: `sym:${toId}`,\n weight: e.weight,\n refType: bestType as CallType,\n });\n }\n\n // Lazily fetch referenced symbols from other files that aren't already\n // loaded (discovered via refs above) instead of loading the entire project\n // symbol table. For a typical file, this saves loading 5000+ irrelevant rows.\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 = this.db\n .prepare(\n `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`,\n )\n .all(...missingIds) as SymRow[];\n for (const s of extras) symById.set(s.id, s);\n }\n\n // Include direct callers/callees from other files. Previously their edges\n // pointed at missing React Flow nodes, which made cross-file relations\n // invisible exactly where users expected to inspect them.\n const nodes = [...relatedIds]\n .map((id) => symById.get(id))\n .filter((symbol): symbol is SymRow => symbol !== undefined)\n .sort((a, b) => {\n const aExternal = a.file === fileFilter ? 0 : 1;\n const bExternal = b.file === fileFilter ? 0 : 1;\n return (\n aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id\n );\n })\n .map(toGraphNode);\n\n return { nodes, edges };\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 try {\n this.db.close();\n } catch {\n /* already closed */\n }\n }\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", "// \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/** Language a symbol belongs to. */\nexport type SymbolLang = 'ts' | 'js' | 'tsx' | 'jsx' | 'go' | 'py' | 'rs' | 'json' | 'yaml';\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\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", "/**\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", "/**\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 df: Record<string, number> = {};\n for (const doc of documents) {\n const seen = new Set<string>();\n for (const t of doc.tokens) {\n if (!seen.has(t)) {\n df[t] = (df[t] ?? 0) + 1;\n seen.add(t);\n }\n }\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, df, N, avgLen);\n}\n\nexport class Bm25Index {\n private readonly safeAvgLen: number;\n\n constructor(\n private documents: Bm25Doc[],\n _df: Record<string, number>,\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 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 // The SQLite FTS path uses token-prefix matching. Compute document\n // frequency with the same contract so fallback ranking has identical\n // recall instead of selecting LIKE candidates and then dropping them.\n let dfVal = 0;\n for (const candidate of this.documents) {\n if (candidate.tokens.some((t) => t.startsWith(qTerm))) dfVal++;\n }\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 return this.documents.find((d) => d.id === 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 * 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 into a\n * single reindex, keyed per `(indexDir, file)`.\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 LockError,\n indexCircuitBreaker,\n} from './circuit-breaker.js';\nimport { indexService, searchService, statsService } from './index-service.js';\nimport type { IndexResult, IndexStats } from './schema.js';\nimport { detectLang } from './ts-parser.js';\nimport { indexStorePool } from './writer.js';\nimport type {\n HostToWorker,\n IndexOpArgs,\n OpName,\n OpShapes,\n SearchOpArgs,\n SearchOpResult,\n StatsOpArgs,\n WorkerToHost,\n} from './worker-protocol.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 a single-file incremental reindex. */\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 /** Circuit-breaker state \u2014 `open` means indexing is paused after repeated failures. */\n circuit: CircuitSnapshot;\n} {\n return {\n ready: _ready,\n indexing: _indexing,\n currentFile: _currentFile,\n totalFiles: _totalFiles,\n lastError: _lastError,\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\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 = 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 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 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 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>>();\n\nfunction debounceKey(indexDir: string | undefined, file: string): string {\n return `${indexDir ?? ''}|${file}`;\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 detectLang(filePath) !== null;\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 if (isRecoverableConstraintError(err) && !opts.force) {\n _lastError = null;\n const rebuildResult = await runStartupIndex({\n ...opts,\n force: true,\n });\n _ready = true;\n return rebuildResult;\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.indexDir, file);\n const existing = debounceTimers.get(key);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n debounceTimers.delete(key);\n // Checked at fire time (not enqueue time) so an edit made while the\n // circuit is open is dropped instead of queuing behind a wedged mutex.\n if (!indexCircuitBreaker.allowRequest()) {\n opts.onError?.(circuitOpenError());\n return;\n }\n void withMutex(() =>\n callIndexOp(\n 'index',\n { projectRoot: opts.projectRoot, files: [file], indexDir: opts.indexDir },\n { timeoutMs: opts.timeoutMs ?? DEFAULT_INCREMENTAL_TIMEOUT_MS },\n ),\n ).then(\n () => indexCircuitBreaker.recordSuccess(),\n (err) => {\n indexCircuitBreaker.recordFailure(err);\n opts.onError?.(err);\n },\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}\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// \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 // 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", "import { expectDefined } from '@wrongstack/core';\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 * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { availableParallelism } from 'node:os';\nimport type { Dirent, Stats } from 'node:fs';\nimport type { Context } from '@wrongstack/core';\nimport { compileGlob } from '@wrongstack/core';\nimport type { FileMeta, IndexResult, Ref, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { IndexStore } from './writer.js';\nimport { parseSymbols as parseTs, detectLang } from './ts-parser.js';\nimport { parseSymbols as parseGo } from './go-parser.js';\nimport { parseSymbols as parsePy } from './py-parser.js';\nimport { parseSymbols as parseRs } from './rs-parser.js';\nimport { parseSymbols as parseJson } from './json-parser.js';\nimport { parseSymbols as parseYaml } from './yaml-parser.js';\nimport { loadGitignoreMatcher, type IgnoreMatcher } from './gitignore.js';\n/** Yield the event loop every N files so the main thread stays responsive. */\nconst YIELD_EVERY_N = 50;\n/**\n * Number of files to process in parallel during the stat+read+parse phase.\n * Auto-tuned to available CPU cores \u2014 each parse is CPU-bound (TypeScript\n * compiler, AST walking), so more cores benefit from a wider batch. The * 4\n * multiplier accounts for the I/O wait between stat/read/parse for each file;\n * on NVMe + many cores the full batch may complete before the event loop\n * yields. Capped at 40 to avoid overwhelming the GC on high-core-count\n * machines (128+ thread EPYC/Ryzen).\n */\nconst PARALLEL_BATCH = Math.min(availableParallelism() * 4, 40);\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 = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n 'coverage',\n '.turbo',\n '__snapshots__',\n '.nyc_output',\n];\nconst DEFAULT_IGNORE_FILES = new Set(['package-lock.json', 'pnpm-lock.yaml', 'pnpm-lock.yml']);\nconst MAX_INDEX_FILE_BYTES = 5 * 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\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<{ files: string[]; complete: boolean; errors: string[] }> {\n const results: string[] = [];\n const errors: string[] = [];\n let complete = true;\n const ignoreSet = new Set([...DEFAULT_IGNORE, ...ignore]);\n // compileGlob does not support brace expansion \u2014 use one pattern per extension\n const globs = [\n { ext: '.ts', pat: compileGlob('**/*.ts') },\n { ext: '.tsx', pat: compileGlob('**/*.tsx') },\n { ext: '.js', pat: compileGlob('**/*.js') },\n { ext: '.jsx', pat: compileGlob('**/*.jsx') },\n { ext: '.go', pat: compileGlob('**/*.go') },\n { ext: '.py', pat: compileGlob('**/*.py') },\n { ext: '.rs', pat: compileGlob('**/*.rs') },\n { ext: '.json', pat: compileGlob('**/*.json') },\n { ext: '.yaml', pat: compileGlob('**/*.yaml') },\n { ext: '.yml', pat: compileGlob('**/*.yml') },\n ];\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 for (const { ext: extName, pat } of globs) {\n if (ext === extName && (pat.test(rel) || pat.test(e.name))) {\n results.push(full);\n break;\n }\n }\n }\n }\n };\n\n await walk(projectRoot);\n return { files: results, complete, errors };\n}\n\n/** Dispatch to the correct parser based on language. */\nasync function parseFile(\n file: string,\n content: string,\n lang: string,\n): Promise<ReturnType<typeof parseTs>> {\n switch (lang) {\n case 'ts':\n case 'tsx':\n case 'js':\n case 'jsx':\n return parseTs({ file, content, lang: lang as 'ts' | 'tsx' | 'js' | 'jsx' });\n case 'go':\n return parseGo({ file, content, lang: 'go' });\n case 'py':\n return parsePy({ file, content, lang: 'py' });\n case 'rs':\n return parseRs({ file, content, lang: 'rs' });\n case 'json':\n return parseJson({ file, content, lang: 'json' });\n case 'yaml':\n return parseYaml({ file, content, lang: 'yaml' });\n default:\n return { file, lang: lang as 'ts' | 'tsx' | 'js' | 'jsx', symbols: [], mtimeMs: Date.now() };\n }\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\nasync function runIndexerWithStore(store: IndexStore, opts: IndexerOptions): 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 force =\n (opts.force ?? false) || store.getMetadata('relation_graph_version') !== relationGraphVersion;\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 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 }\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 // Process files in batches for parallel I/O and parsing.\n // SQLite writes remain sequential (they're synchronous and CPU-bound).\n let filesSinceLastYield = 0;\n for (let batchStart = 0; batchStart < files.length; batchStart += PARALLEL_BATCH) {\n const batchEnd = Math.min(batchStart + PARALLEL_BATCH, 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?.(batchEnd, files.length);\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 (os.availableParallelism * 4) that invariant no\n // longer holds and the yield would 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 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 parseFile>> | 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 parseFile>>;\n try {\n parsed = await parseFile(file, content, lang);\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. They don't contribute any symbols or refs, so we\n // upsert directly without scheduling them for the batch.\n if (parsed.symbols.length === 0) {\n store.deleteRefsForFile(file);\n store.deleteSymbolsForFile(file);\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 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.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 // Resolution must run even when every file was an incremental-cache hit:\n // older indexes can contain unresolved refs from a previous interrupted run.\n store.resolveRefs();\n store.setMetadata('relation_graph_version', relationGraphVersion);\n\n const durationMs = Date.now() - startMs;\n store.setLastIndexed(Date.now());\n\n return {\n filesIndexed,\n symbolsIndexed,\n langStats,\n durationMs,\n errors,\n };\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 * as ts from '@typescript/typescript6';\nimport type { FileSymbols, Ref, Symbol as IndexSymbol, SymbolKind, SymbolLang } from './schema.js';\n\n// Map TypeScript SyntaxKind \u2192 our SymbolKind taxonomy\nconst KIND_MAP: Partial<Record<ts.SyntaxKind, SymbolKind>> = {\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\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 KIND_MAP[node.kind] ?? null;\n}\n\nfunction extToLang(ext: string): SymbolLang | null {\n switch (ext) {\n case '.ts': return 'ts';\n case '.tsx': return 'tsx';\n case '.js': return 'js';\n case '.jsx': return 'jsx';\n case '.go': return 'go';\n case '.py': return 'py';\n case '.rs': return 'rs';\n case '.json': return 'json';\n case '.yaml': return 'yaml';\n case '.yml': return 'yaml';\n default: return null;\n }\n}\n\nfunction getSignature(printer: ts.Printer, node: ts.Declaration, sourceFile: ts.SourceFile): 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 */\nexport function parseSymbols(opts: ParseOptions): FileSymbols {\n const { file, content, lang } = opts;\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) refs.push({ fromId: 0, toName: name, callType: node.token === ts.SyntaxKind.ExtendsKeyword ? 'inherit' : 'implement', line: lineNum });\n }\n } else if (ts.isImportDeclaration(node)) {\n const moduleName = getModuleName(node);\n if (moduleName) refs.push({ fromId: 0, toName: moduleName, callType: 'import', line: 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/** Get the module path string from an import declaration. */\nfunction getModuleName(node: ts.ImportDeclaration): string {\n const moduleSpecifier = node.moduleSpecifier;\n if (ts.isStringLiteral(moduleSpecifier)) return moduleSpecifier.text;\n /* v8 ignore next -- an import declaration's module specifier is always a string literal; 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/** Detect SymbolLang from a file path extension. */\nexport function detectLang(file: string): SymbolLang | null {\n const idx = file.lastIndexOf('.');\n if (idx < 0) return null;\n return extToLang(file.slice(idx));\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';\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 const parsed = await 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 './ts-parser.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\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 const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ws-go-parse-'));\n try {\n const scriptPath = path.join(tmpDir, 'parse.go');\n await fs.writeFile(scriptPath, GO_PARSE_SCRIPT, 'utf8');\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 } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n}\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 * 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, spawnSync, 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';\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: { file: string; content: string; lang: SymbolLang }): Promise<FileSymbols> {\n const { file, content, lang } = opts;\n\n try {\n return await syncPyParse(file, content, lang);\n } catch {\n /* v8 ignore next -- syncPyParse has its own catch; this outer guard is defensive. */\n return { file, lang, symbols: [], mtimeMs: Date.now() };\n }\n}\n\nexport { detectLang } from './ts-parser.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 verify each\n * candidate with `spawnSync --version` \u2014 the only reliable way to\n * check that a binary actually exists on PATH. Cost is a single\n * short-lived process per lookup, 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 */\nfunction resolvePython(): 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 with spawnSync 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\tconst result = spawnSync(resolved, ['--version'], {\n\t\t\tstdio: 'pipe',\n\t\t\ttimeout: 5_000,\n\t\t});\n\t\tif (result.error) continue; // ENOENT or similar \u2014 try next\n\t\tif (result.status !== 0) continue; // binary exists but broken (e.g. Windows Store stub)\n\t\treturn resolved;\n\t}\n\treturn null;\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: string | null = null;\n\nasync function syncPyParse(filePath: string, content: string, lang: SymbolLang): Promise<FileSymbols> {\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\tif (!_cachedPyBinary) {\n\t\t\t_cachedPyBinary = resolvePython();\n\t\t\tif (!_cachedPyBinary) return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n\t\t}\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\t_cachedPyBinary,\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\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\treturn { file: filePath, lang, symbols: [], mtimeMs: Date.now() };\n\t}\n}", "import { expectDefined } from '@wrongstack/core';\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 { execFileSync, 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';\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 = checkNativeParser();\n if (nativeAvailable) {\n const result = await tryNativeParse(file, content);\n if (result) return result;\n }\n\n return regexParse({ file, content, lang });\n}\n\nexport { detectLang } from './ts-parser.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\nfunction checkNativeParser(): boolean {\n try {\n execFileSync('rustc', ['--version'], { stdio: 'pipe', windowsHide: true });\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 try {\n execFileSync(\n 'cargo',\n [\n 'metadata',\n '--no-deps',\n '--format-version',\n '1',\n '--manifest-path',\n path.join(toolsDir, 'Cargo.toml'),\n ],\n { stdio: 'pipe', windowsHide: true },\n );\n return true;\n } catch {\n return false;\n }\n } catch {\n return false;\n }\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';\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 './ts-parser.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';\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 './ts-parser.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", "/**\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';\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", "/**\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 * Every operation opens its own short-lived IndexStore, exactly like the old\n * per-call code paths did \u2014 there is no connection state to share, which keeps\n * multi-project usage trivially correct and crash recovery simple.\n */\n\nimport type { Context } from '@wrongstack/core';\nimport { runIndexer } 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\n/** A run with no live agent Context \u2014 `runIndexer` only reads `opts`. */\nfunction stubCtx(projectRoot: string): Context {\n return {\n projectRoot,\n cwd: projectRoot,\n messages: [],\n todos: [],\n readFiles: new Set<string>(),\n fileMtimes: new Map<string, number>(),\n } as never as Context;\n}\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 return runIndexer(stubCtx(args.projectRoot), {\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}\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", "\nimport type { Tool } from '@wrongstack/core';\nimport { codebaseIndexDirOverride } from './writer.js';\nimport { isIndexing, runStartupIndex } from './background-indexer.js';\nimport { indexCircuitBreaker } from './circuit-breaker.js';\n\nexport const codebaseIndexTool: Tool<CodebaseIndexInput, CodebaseIndexOutput> = {\n name: 'codebase-index',\n category: 'Project',\n icon: 'index',\n description:\n 'Build or incrementally update the project-wide symbol index. This powers fast codebase search and understanding. ' +\n 'By default it only processes files that have changed since the last indexing run.',\n usageHint:\n 'CREATE OR REFRESH THE SEARCH INDEX:\\n\\n' +\n '- When `codebase-stats` or `codebase-search` reports no persisted index, call this without arguments, then retry the search.\\n' +\n '- Normal and first-time usage: call without arguments for an incremental build.\\n' +\n '- Use `force: true` only for a corrupt/stale index or an explicitly requested clean rebuild.\\n' +\n '- Use `langs` to restrict to specific languages if you only care about certain parts of the project.\\n' +\n 'This tool is relatively expensive \u2014 do not call it on every turn. Use it when the index is stale or before heavy codebase-search sessions.',\n permission: 'confirm',\n mutating: true,\n capabilities: ['fs.write.outside-project'],\n timeoutMs: 120_000,\n inputSchema: {\n type: 'object',\n properties: {\n force: {\n type: 'boolean',\n description: 'Force a full reindex \u2014 clears the index first and reindexes all files.',\n },\n langs: {\n type: 'array',\n items: { type: 'string' },\n description: 'Limit reindex to specific languages: ts, tsx, js, jsx, go, py, rs',\n },\n },\n },\n async execute(input, ctx, execOpts) {\n // If the startup index is still running, tell the agent to wait instead of\n // firing a second reindex that would just queue behind the mutex.\n if (isIndexing()) {\n return {\n filesIndexed: 0,\n symbolsIndexed: 0,\n langStats: {},\n durationMs: 0,\n errors: [],\n note: 'A full index is already in progress. Retry codebase-index after it completes (check codebase-stats).',\n };\n }\n\n // Circuit breaker: after repeated failures/timeouts indexing is paused.\n // Report instead of erroring so the agent can carry on without the index.\n const circuit = indexCircuitBreaker.snapshot();\n if (circuit.state === 'open' && circuit.cooldownRemainingMs > 0) {\n return {\n filesIndexed: 0,\n symbolsIndexed: 0,\n langStats: {},\n durationMs: 0,\n errors: [],\n note:\n `Codebase indexing is paused after repeated failures (last: ${circuit.lastFailure ?? 'unknown'}). ` +\n `Auto-retry possible in ${Math.ceil(circuit.cooldownRemainingMs / 1000)}s; the user can run /codebase-reindex to retry immediately.`,\n };\n }\n\n // Route through the background coordinator so the run shares the\n // process-wide mutex, the watchdog timeout, and breaker accounting with\n // the startup scan and live reindexes (a direct runIndexer call here used\n // to race them on the same SQLite file).\n return await runStartupIndex({\n projectRoot: ctx.projectRoot,\n force: input.force ?? false,\n langs: input.langs,\n indexDir: codebaseIndexDirOverride(ctx),\n signal: execOpts?.signal,\n });\n },\n};\n\n// \u2500\u2500\u2500 Types for tool I/O \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 CodebaseIndexInput {\n force?: boolean | undefined;\n langs?: string[] | undefined;\n}\n\ninterface CodebaseIndexOutput {\n filesIndexed: number;\n symbolsIndexed: number;\n langStats: Record<string, number>;\n durationMs: number;\n errors: string[];\n /** Advisory note when the indexer was skipped (e.g. another index in progress). */\n note?: string | undefined;\n}\n", "/**\n * `codebase-search` tool \u2014 search the symbol index with BM25 ranking.\n *\n * Usage: codebase-search({\n * query: string, // search terms\n * kind?: string, // class|function|interface|method|const|...\n * lang?: string, // ts|tsx|js|jsx|go|py|rs\n * file?: string, // filter to a specific file path (substring match)\n * limit?: number, // max results (default 20, max 100)\n * })\n *\n * Returns: [{ name, kind, lang, file, line, signature, snippet, score }, ...]\n *\n * The query executes in the index worker via FTS5 (`MATCH` + native `bm25()`)\n * \u2014 the main thread never opens SQLite, so a contended or wedged index can\n * slow this tool down but can never freeze the terminal.\n */\n\nimport type { Tool } from '@wrongstack/core';\nimport {\n codebaseIndexStats,\n getIndexState,\n searchCodebaseIndex,\n} from './background-indexer.js';\nimport type { SearchResult } from './schema.js';\nimport { codebaseIndexDirOverride } from './writer.js';\nexport const codebaseSearchTool: Tool<CodebaseSearchInput, CodebaseSearchOutput> = {\n name: 'codebase-search',\n category: 'Project',\n icon: 'index',\n description:\n 'Search code symbols using a fast SQLite+BM25 index, with optional LSP fallback. ' +\n 'Prefer this before broad `tree`, `glob`, or `grep` exploration when finding code by name or concept. ' +\n 'Set `preferLsp: true` for live precision when the LSP plugin is active (supersedes codebase-lsp-search).',\n usageHint:\n 'FIRST CHOICE FOR INDEXABLE CODE UNDERSTANDING:\\n\\n' +\n '- Call before broad `tree`, `glob`, or `grep` exploration when locating symbols, concepts, definitions, or candidate modules.\\n' +\n '- `kind` filter is very useful (e.g. only functions or only interfaces).\\n' +\n '- Combine with `file` filter to scope to a specific directory or module.\\n' +\n '- If `indexStatus` reports no persisted data, run `codebase-index` and retry.\\n' +\n 'Use `grep` afterwards for exact text, regexes, unsupported content, or concrete usage sites.',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n // The index host has its own 30s read watchdog. Leave enough headroom for\n // worker teardown and structured timeout reporting.\n timeoutMs: 35_000,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query \u2014 searches symbol names, signatures, and doc comments',\n },\n kind: {\n type: 'string',\n enum: [\n 'class', 'interface', 'enum', 'type', 'function', 'method', 'var', 'const', 'let',\n 'property', 'parameter', 'namespace', 'object', 'literal', 'schema', 'struct', 'trait',\n 'impl', 'static', 'mod',\n ],\n description: 'Filter by indexed symbol kind',\n },\n lang: {\n type: 'string',\n enum: ['ts', 'tsx', 'js', 'jsx', 'go', 'py', 'rs', 'json', 'yaml'],\n description: 'Filter by indexed language',\n },\n lspKind: {\n type: 'integer',\n description:\n 'Filter by LSP SymbolKind number (e.g. 5=Class, 12=Function, 11=Interface, 10=Enum)',\n },\n file: {\n type: 'string',\n description: 'Filter to files matching this path substring',\n },\n limit: {\n type: 'integer',\n description: 'Maximum results to return (default 20, max 100)',\n minimum: 1,\n maximum: 100,\n },\n preferLsp: {\n type: 'boolean',\n description:\n 'Prefer live LSP results over the index. Index-only when the LSP plugin is not active. ' +\n 'When the LSP plugin is active and this is true, results come from live workspaceSymbol queries.',\n },\n },\n required: ['query'],\n },\n async execute(input, ctx, execOpts) {\n const state = getIndexState();\n if (state.indexing && !state.ready) {\n return {\n results: [],\n total: 0,\n query: input.query,\n indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`,\n };\n }\n // WAL provides a consistent previous snapshot while a refresh writes, so\n // keep search available instead of returning an empty, misleading result.\n if (state.lastError) {\n const circuit = state.circuit;\n const retryHint =\n circuit.state === 'open'\n ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1000)}s); the user can run /codebase-reindex to retry now.`\n : 'Try /codebase-reindex.';\n return {\n results: [],\n total: 0,\n query: input.query,\n indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`,\n };\n }\n\n const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 20), 100));\n const { results, total } = await searchCodebaseIndex(\n {\n projectRoot: ctx.projectRoot,\n indexDir: codebaseIndexDirOverride(ctx),\n query: input.query,\n kind: input.kind?.toLowerCase(),\n lang: input.lang?.toLowerCase(),\n file: input.file,\n lspKind: input.lspKind,\n limit,\n },\n { signal: execOpts?.signal },\n );\n // Process-local readiness resets on launch while the SQLite index persists.\n // A zero-hit query on a cold process therefore cannot by itself prove the\n // index is missing; consult persisted metadata before emitting that hint.\n let hasPersistedIndex = state.ready || total > 0;\n if (!hasPersistedIndex) {\n try {\n const stats = await codebaseIndexStats(\n { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },\n { signal: execOpts?.signal },\n );\n hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;\n } catch {\n // Search already completed successfully. Keep the conservative missing\n // hint rather than failing the whole tool because the stats probe failed.\n }\n }\n return {\n results,\n total,\n query: input.query,\n ...(hasPersistedIndex\n ? {}\n : { indexStatus: 'No persisted index data found. Run codebase-index to build it.' }),\n };\n },\n};\n\n// \u2500\u2500\u2500 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface CodebaseSearchInput {\n query: string;\n kind?: string | undefined;\n lang?: string | undefined;\n file?: string | undefined;\n limit?: number | undefined;\n lspKind?: number | undefined;\n /**\n * When true, hints that LSP-based search should be preferred over the index.\n * The built-in tool ignores this (index-only), but when the LSP plugin is\n * active it intercepts the tool and provides live LSP workspaceSymbol results.\n */\n preferLsp?: boolean | undefined;\n}\n\ninterface CodebaseSearchOutput {\n results: SearchResult[];\n total: number; // total candidates before limit\n query: string;\n /** Non-empty when the index blocked the search (not ready, indexing, failed). */\n indexStatus?: string | undefined;\n}\n", "/**\n * `codebase-stats` tool \u2014 report index health and statistics.\n *\n * Usage: codebase-stats({})\n *\n * Returns: { totalSymbols, totalFiles, byLang, byKind, lastIndexed, sizeBytes, version }\n */\n\nimport type { Tool } from '@wrongstack/core';\nimport { codebaseIndexStats, getIndexState } from './background-indexer.js';\nimport { IndexTimeoutError } from './circuit-breaker.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { codebaseIndexDirOverride, resolveIndexDir } from './writer.js';\n\nexport const codebaseStatsTool: Tool<Record<string, never>, CodebaseStatsOutput> = {\n name: 'codebase-stats',\n category: 'Project',\n icon: 'index',\n description:\n 'Check whether a persisted codebase index exists and report its health and statistics (symbols, files, language/kind breakdown, size, last update).',\n usageHint:\n 'CHECK ONCE BEFORE BROAD CODE EXPLORATION:\\n\\n' +\n '- Call before broad `tree`, `glob`, or `grep` exploration to see whether index-backed search is available.\\n' +\n '- If it reports no persisted data, run `codebase-index`, then use `codebase-search`.\\n' +\n '- No arguments required.\\n' +\n 'Lightweight and safe to call frequently.',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n // The index host has its own 30s read watchdog. Keep the outer tool budget\n // longer so the host can return a structured timeout instead of being\n // pre-empted by the generic tool executor.\n timeoutMs: 35_000,\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n async execute(_input, ctx, execOpts) {\n const idxState = getIndexState();\n const indexPath = resolveIndexDir(ctx.projectRoot, codebaseIndexDirOverride(ctx));\n\n // The index worker can be busy parsing or writing during a startup build.\n // Do not queue a stats read merely to report progress: on large projects\n // that made this lightweight tool hit its outer timeout. Return the\n // process-local lifecycle state immediately and let callers retry later.\n if (idxState.indexing) {\n return {\n totalSymbols: 0,\n totalFiles: 0,\n byLang: {},\n byKind: {},\n lastIndexed: null,\n sizeBytes: 0,\n indexPath,\n version: SCHEMA_VERSION,\n statsAvailable: false,\n indexing: {\n currentFile: idxState.currentFile,\n totalFiles: idxState.totalFiles,\n },\n indexStatus: idxState.ready\n ? `Index refresh in progress (${idxState.currentFile}/${idxState.totalFiles} files). The persisted snapshot remains available to codebase-search; retry stats after refresh.`\n : `Startup indexing in progress (${idxState.currentFile}/${idxState.totalFiles} files). Do not start another index; retry stats or codebase-search after completion.`,\n };\n }\n\n // Always inspect persisted state. Readiness is process-local and resets on\n // every CLI launch, while the SQLite index intentionally survives launches.\n // Fetched via the index host (worker thread when available) \u2014 the main\n // thread never opens SQLite here.\n let stats;\n try {\n stats = await codebaseIndexStats(\n { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },\n { signal: execOpts?.signal },\n );\n } catch (err) {\n if (!(err instanceof IndexTimeoutError) && (err as Error)?.name !== 'IndexTimeoutError') {\n throw err;\n }\n return {\n totalSymbols: 0,\n totalFiles: 0,\n byLang: {},\n byKind: {},\n lastIndexed: null,\n sizeBytes: 0,\n indexPath,\n version: SCHEMA_VERSION,\n statsAvailable: false,\n indexStatus:\n 'Index statistics timed out. Do not repeatedly retry stats; try codebase-search directly or use the appropriate grep/glob/tree fallback.',\n };\n }\n\n const circuit = idxState.circuit;\n const hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;\n let indexStatus: string | undefined;\n if (circuit.state === 'open') {\n indexStatus =\n `${hasPersistedIndex ? 'Indexing' : 'No persisted index data found, and indexing'} is paused after repeated failures ` +\n `(last: ${circuit.lastFailure ?? 'unknown'}); auto-retry in ` +\n `${Math.ceil(circuit.cooldownRemainingMs / 1000)}s, or run /codebase-reindex. ` +\n (hasPersistedIndex\n ? 'Stats reflect the last successful build.'\n : 'Build the index after recovery.');\n } else if (!hasPersistedIndex) {\n indexStatus =\n 'No persisted index data found. Run codebase-index to build it before broad code exploration.';\n }\n return {\n totalSymbols: stats.totalSymbols,\n totalFiles: stats.totalFiles,\n byLang: stats.byLang,\n byKind: stats.byKind,\n lastIndexed: stats.lastIndexed,\n sizeBytes: stats.sizeBytes,\n indexPath: stats.indexPath,\n version: stats.version,\n statsAvailable: true,\n ...(indexStatus ? { indexStatus } : {}),\n };\n },\n};\n\ninterface CodebaseStatsOutput {\n totalSymbols: number;\n totalFiles: number;\n byLang: Record<string, number>;\n byKind: Record<string, number>;\n lastIndexed: number | null;\n sizeBytes: number;\n indexPath: string;\n version: number;\n /** False when lifecycle state was returned without opening the SQLite index. */\n statsAvailable: boolean;\n /** Present while an index build or refresh is active. */\n indexing?:\n | {\n currentFile: number;\n totalFiles: number;\n }\n | undefined;\n /** Non-empty when the index is not ready or is still building. */\n indexStatus?: string | undefined;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["ts", "fs", "expectDefined", "fs", "path", "
|
|
3
|
+
"sources": ["../../src/codebase-index/writer.ts", "../../src/codebase-index/circuit-breaker.ts", "../../src/codebase-index/schema.ts", "../../src/codebase-index/lsp-kind.ts", "../../src/codebase-index/bm25.ts", "../../src/codebase-index/background-indexer.ts", "../../src/codebase-index/indexer.ts", "../../src/codebase-index/ts-parser.ts", "../../src/codebase-index/languages.ts", "../../src/codebase-index/go-parser.ts", "../../src/_win32-resolve.ts", "../../src/codebase-index/spawn-gate.ts", "../../src/codebase-index/py-parser.ts", "../../src/codebase-index/generic-parser.ts", "../../src/codebase-index/rs-parser.ts", "../../src/codebase-index/json-parser.ts", "../../src/codebase-index/yaml-parser.ts", "../../src/codebase-index/gitignore.ts", "../../src/codebase-index/index-service.ts", "../../src/codebase-index/codebase-index-tool.ts", "../../src/codebase-index/codebase-search-tool.ts", "../../src/codebase-index/codebase-stats-tool.ts"],
|
|
4
|
+
"sourcesContent": ["import { expectDefined } from '@wrongstack/core/utils';\nimport { LockError } from './circuit-breaker.js';\nimport { toErrorMessage } 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 { createRequire } from 'node:module';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { resolveWstackPaths, sqliteCachePragmas } from '@wrongstack/core/utils';\nimport type {\n FileMeta,\n IndexStats,\n Ref,\n SearchResult,\n Symbol as IndexSymbol,\n SymbolKind,\n SymbolLang,\n CodeMapGraph,\n GraphNode,\n GraphEdge,\n CallType,\n} from './schema.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { lspKindToInternalKind } from './lsp-kind.js';\nimport { Bm25Index, buildBm25Index, buildIndexableText, tokenise } from './bm25.js';\nconst DB_FILE = 'index.db';\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\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 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 * matching every other piece of per-project state. Callers may pass an\n * explicit `override` (used by tests and any wiring that already resolved the\n * path) to avoid touching the real home directory.\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 * Production leaves it unset (the index resolves to the global per-project\n * dir); tests and bespoke wiring set `meta.codebaseIndexDir` to redirect it.\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\n/**\n * Warm-connection pool for IndexStore instances.\n *\n * Each (projectRoot, indexDir) pair gets one persisted IndexStore. 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 \u2014 only warm-up cost is\n * paid once per project per process lifetime.\n *\n * In WAL mode, readers coexist with the writer connection, so a single pooled\n * connection handles both reads and writes safely. The caller is responsible\n * for serializing writes (the host's promise-chain mutex does this); the pool\n * itself has no lock because the worker processes one message at a time.\n */\nexport class StorePool {\n private readonly stores = new Map<string, IndexStore>();\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 }): IndexStore {\n const k = this.key(projectRoot, opts?.indexDir);\n let store = this.stores.get(k);\n if (!store) {\n store = new IndexStore(projectRoot, { indexDir: opts?.indexDir });\n this.stores.set(k, store);\n }\n return store;\n }\n\n /** Return the store to the pool. The connection stays warm for subsequent\n * operations on the same (projectRoot, indexDir). */\n release(_store: IndexStore): void {\n // No-op \u2014 the store remains open with its prepared-statement cache intact,\n // ready for the next acquire() on the same key. The connection is closed\n // explicitly via closeAll() (shutdown) or evict() (test isolation).\n }\n\n /** Close every pooled connection and drain the pool. Call on shutdown. */\n closeAll(): void {\n for (const store of this.stores.values()) {\n try {\n 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 store = this.stores.get(k);\n if (store) {\n try {\n 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\n/** Process-wide singleton pool. */\nexport const indexStorePool = new StorePool();\n\nlet warningSilenced = false;\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 * Load `node:sqlite`'s `DatabaseSync` lazily. Keeping this off the module's\n * top-level import means the codebase-index tools can be registered at CLI boot\n * without eagerly loading SQLite \u2014 so a runtime that lacks `node:sqlite` (it is\n * experimental, available since Node 22.5) only fails if the index is actually\n * used, with a clear message instead of a crash on import.\n */\nfunction 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// \u2500\u2500\u2500 SQLite lock-error retry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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/** Maximum retry attempts for a lock-conflict error. */\nconst MAX_LOCK_RETRIES = 3;\n/**\n * Base delay (ms) before the first retry after a lock error. Each subsequent\n * retry doubles this (exponential backoff). Combined with the 5-second\n * busy_timeout pragma, this means: 5s (pragma) + 50ms + 100ms + 200ms per\n * attempt \u2014 enough to wait out most cross-process writer conflicts.\n */\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\n/**\n * Returns true when `err` represents a SQLite lock conflict (SQLITE_BUSY or\n * SQLITE_LOCKED). These are transient \u2014 another process holds the write lock\n * and will release it shortly. Retry instead of failing.\n *\n * node:sqlite surfaces these as plain Error instances with `code` set to\n * 'SQLITE_BUSY' or 'SQLITE_LOCKED', or with a message that contains the\n * error name. Defensive: anything we can't classify as safe is NOT treated\n * as a lock error so real failures are not retried indefinitely.\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; // SQLITE_BUSY=5, SQLITE_LOCKED=6\n // node:sqlite sometimes surfaces the numeric code as a string in the message\n if (/SQLITE_(BUSY|LOCKED)/.test(err.message)) return true;\n return false;\n}\n\n/**\n * Synchronous sleep via Atomics.wait on a zero-length SharedArrayBuffer.\n * This is the only way to synchronously block in a Worker thread without\n * busy-waiting. The main thread (where DatabaseSync is never used) is\n * unaffected.\n *\n * The call is wrapped in try/catch because Atomics.wait throws in browsers\n * and other environments where SharedArrayBuffer is not available.\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 // Atomics.wait not available (browser, unknown runtime) \u2014 fall through.\n // The retry still happens but without sleeping, which is acceptable because\n // busy_timeout already handled the bulk of the wait.\n }\n}\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 /**\n * Execute a SQLite write operation with automatic retry on lock conflicts.\n *\n * When another wstack process is holding the write lock the statement first\n * waits up to `busy_timeout` ms, then throws SQLITE_BUSY. This wrapper catches\n * that error and retries (up to MAX_LOCK_RETRIES) with exponential backoff,\n * giving the competing writer time to finish and release the lock.\n *\n * @param fn The write operation to execute. Can return a value which is\n * returned to the caller on success.\n * @throws {@link LockError} when all retries are exhausted on a lock conflict\n * (non-lock errors always propagate on the first attempt).\n */\n runWithRetry<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 // All retries exhausted \u2014 wrap in LockError so the circuit breaker\n // knows this is a transient lock conflict, not a real failure.\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 // Exponential backoff: 50ms \u2192 100ms \u2192 200ms, capped at 500ms.\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; // unreachable \u2014 satisfies TypeScript\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 // Multi-process safety: several wstack surfaces (TUI, WebUI, parallel\n // terminals) share this per-project db. WAL lets readers coexist with the\n // writer, and busy_timeout gives SQLite a head start waiting for the lock.\n // When the timeout expires the statement throws SQLITE_BUSY; the\n // runWithRetry() wrapper then retries with exponential backoff so most\n // lock-conflict errors are resolved without a circuit-breaker failure.\n try {\n this.db.exec('PRAGMA journal_mode = WAL');\n this.db.exec('PRAGMA synchronous = NORMAL');\n // Large reindex batches hold the write lock longer; give concurrent\n // readers/writers more headroom before SQLITE_BUSY \u2192 retry.\n this.db.exec('PRAGMA busy_timeout = 15000');\n this.db.exec('PRAGMA temp_store = MEMORY');\n // Cache/mmap follow WRONGSTACK_PERF_PROFILE (frugal = leaner RSS).\n const cache = sqliteCachePragmas();\n this.db.exec(`PRAGMA cache_size = -${cache.cacheSizeKiB}`);\n this.db.exec(`PRAGMA mmap_size = ${cache.mmapBytes}`);\n this.db.exec('PRAGMA foreign_keys = ON');\n // Cap WAL growth so long-lived index processes don't leave multi-GB -wal files.\n this.db.exec('PRAGMA journal_size_limit = 67108864'); // 64 MiB\n this.db.exec('PRAGMA wal_autocheckpoint = 1000');\n } catch {\n /* pragmas are best-effort \u2014 an old SQLite build without WAL still works */\n }\n this.initSchema();\n }\n\n private initSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\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 = ?')\n .all('version') as { value: string }[];\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 = ?')\n .run(String(SCHEMA_VERSION), 'version');\n } else if (storedVersion === null) {\n this.stmt('INSERT INTO metadata(key, value) VALUES (?, ?)')\n .run('version', String(SCHEMA_VERSION));\n }\n\n this.db.exec(`\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\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)');\n // Compound index for the common lang+kind filter in ranked search.\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)');\n // Index on file_fk: deleteSymbolsForFile and FTS delete paths query by\n // file_fk \u2014 without this index every reindex/delete scans the symbols table.\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)');\n // resolveRefs joins refs.to_name \u2192 symbols.name; covering (name, id) avoids\n // a second lookup for the id column on each unresolved ref.\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)');\n\n this.db.exec(`\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\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)');\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(\n \"CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')\",\n );\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 (\n this.stmt('SELECT COUNT(*) AS n FROM symbols_fts').get() as\n | { n?: number }\n | undefined\n )?.n ?? 0,\n );\n if (symbolCount !== ftsCount) {\n this.db.exec('DELETE FROM symbols_fts');\n const rows = this.stmt('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 this.bulkInsertFts(\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 /** Multi-row INSERT for symbols \u2014 amortizes prepare/bind overhead in large batches. */\n private bulkInsertSymbols(\n rows: Array<{\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 ): void {\n if (rows.length === 0) return;\n const cols = 12;\n const chunkSize = Math.max(1, Math.floor(IndexStore.MAX_SQL_VARS / cols));\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 stmt = this.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 stmt.run(...binds);\n }\n }\n\n private bulkInsertFts(rows: Array<{ id: number; text: string }>): void {\n if (!this.ftsAvailable || rows.length === 0) return;\n const cols = 2;\n const chunkSize = Math.max(1, Math.floor(IndexStore.MAX_SQL_VARS / cols));\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 stmt = this.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 stmt.run(...binds);\n }\n }\n\n private bulkInsertRefs(refs: Ref[]): void {\n if (refs.length === 0) return;\n const cols = 5;\n const chunkSize = Math.max(1, Math.floor(IndexStore.MAX_SQL_VARS / cols));\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 stmt = this.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 stmt.run(...binds);\n }\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: Array<{\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 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 this.bulkInsertSymbols(bulk);\n this.bulkInsertFts(ftsRows);\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 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 });\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 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.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 const rows = this.stmt(\n 'SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?',\n ).all(file) as {\n file: string;\n lang: string;\n mtime_ms: number;\n symbol_count: number;\n last_indexed: number;\n }[];\n if (!rows.length) return null;\n const r = expectDefined(rows[0]);\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\n getAllFileMetas(): FileMeta[] {\n return (\n this.stmt('SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files')\n .all() as {\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\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?: {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n },\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 =\n typeof opts?.limit === 'number' && Number.isFinite(opts.limit)\n ? Math.max(0, Math.trunc(opts.limit))\n : undefined;\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(...(binds as (string | number)[])) 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 text: string;\n }[];\n\n return rows.map((r) => ({\n id: r.id,\n lang: r.lang as SymbolLang,\n kind: r.kind as SymbolKind,\n name: r.name,\n file: r.file,\n line: r.line,\n col: r.col,\n signature: r.signature,\n docComment: r.doc_comment,\n score: 0,\n snippet: '',\n lspKind: filter?.lspKind,\n }));\n }\n\n /** Shared WHERE builder for {@link search} / empty-query ranked totals. */\n private buildSearchWhere(\n query: string,\n filter?: {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n },\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 // LSP kind was explicitly provided but has no internal mapping \u2192 no results\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 const tokenConds = tokens.map(() => 'text LIKE ?');\n conditions.push(`(${tokenConds.join(' OR ')})`);\n for (const t of tokens) values.push(`%${t}%`);\n }\n\n const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';\n return { where, values };\n }\n\n private countSearch(\n query: string,\n filter?: {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n },\n ): 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:\n | {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n }\n | 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 )\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 )\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((r) => ({\n id: r.id,\n lang: r.lang as SymbolLang,\n kind: r.kind as SymbolKind,\n name: r.name,\n file: r.file,\n line: r.line,\n col: r.col,\n signature: r.signature,\n docComment: r.doc_comment,\n // bm25() is negative-is-better; negate so callers keep \"higher is\n // better\" and clamp so a match never reports a zero score.\n score: Math.max(0.0001, r.score),\n snippet: r.snippet,\n lspKind: filter?.lspKind,\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:\n | {\n kind?: SymbolKind | undefined;\n lang?: SymbolLang | undefined;\n file?: string | undefined;\n lspKind?: number | undefined;\n }\n | 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 (\n this.stmt('SELECT id, text FROM symbols').all() as { id: number; text: string }[]\n ).map(({ id, text }) => ({ id, text }));\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 const rows = this.stmt('SELECT MAX(id) AS m FROM symbols').all() as {\n m: number | null;\n }[];\n return rows[0]?.m ?? 0;\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 const sizeBytes = this.sizeBytes();\n\n const lastRows = this.stmt(\"SELECT value FROM metadata WHERE key = 'last_indexed'\")\n .all() as { value: string }[];\n const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;\n\n const totalRows = this.stmt('SELECT COUNT(*) FROM symbols').all() as {\n 'COUNT(*)': number;\n }[];\n const totalSymbols = totalRows[0] ? Number(totalRows[0]['COUNT(*)']) : 0;\n\n const fileRows = this.stmt('SELECT COUNT(*) FROM files').all() as {\n 'COUNT(*)': number;\n }[];\n const totalFiles = fileRows[0] ? Number(fileRows[0]['COUNT(*)']) : 0;\n\n const langRows = this.stmt('SELECT lang, COUNT(*) FROM symbols GROUP BY lang').all() as {\n lang: string;\n 'COUNT(*)': number;\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 kindRows = this.stmt('SELECT kind, COUNT(*) FROM symbols GROUP BY kind').all() as {\n kind: string;\n 'COUNT(*)': number;\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,\n totalFiles,\n byLang,\n byKind,\n indexPath: this.indexDir,\n lastIndexed,\n sizeBytes,\n version: SCHEMA_VERSION,\n };\n }\n\n setLastIndexed(ts: number): void {\n this.runWithRetry(() => {\n this.stmt(\"INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)\")\n .run(String(ts));\n });\n }\n\n getMetadata(key: string): string | undefined {\n const rows = this.stmt('SELECT value FROM metadata WHERE key = ?').all(key) as {\n value: string;\n }[];\n return rows[0]?.value;\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 } 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 this.bulkInsertRefs(refs.map((ref) => ({ ...ref, fromId })));\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 this.bulkInsertRefs(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 // 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 if (this.ftsAvailable) {\n this.stmt(\n `DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`,\n )\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 )\n .run(...options.deleteForFiles);\n this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`)\n .run(...options.deleteForFiles);\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: Array<{\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 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 this.bulkInsertSymbols(bulkSyms);\n this.bulkInsertFts(ftsRows);\n\n // 3) Multi-row insert all refs.\n this.bulkInsertRefs(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.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 = ?)')\n .run(file);\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 /**\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 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.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 * Find all references TO a given symbol (who calls / uses this symbol?).\n */\n findRefsTo(symbolId: number): Ref[] {\n return (\n this.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 )\n .all(symbolId, symbolId) as {\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 ).map((r) => ({\n id: r.id,\n fromId: r.from_id,\n toName: r.to_name,\n toId: r.to_id ?? undefined,\n callType: r.call_type as Ref['callType'],\n line: r.line,\n }));\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 (\n this.stmt('SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE from_id = ?')\n .all(symbolId) as {\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 ).map((r) => ({\n id: r.id,\n fromId: r.from_id,\n toName: r.to_name,\n toId: r.to_id ?? undefined,\n callType: r.call_type as Ref['callType'],\n line: r.line,\n }));\n }\n\n private sizeBytes(): number {\n const dbPath = path.join(this.indexDir, DB_FILE);\n try {\n return fs.statSync(dbPath).size;\n } catch {\n return 0;\n }\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 * Derive a monorepo package name from an absolute file path.\n * Handles both `packages/<name>/...` (workspace) and `src/<name>/...` layouts.\n * Returns `undefined` for files outside any recognisable package structure.\n */\n private static derivePackage(filePath: string): string | undefined {\n // Normalise backslashes (Windows) to forward slashes.\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\n private static 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\n private static 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\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 // Aggregate symbol counts per file in SQL \u2014 avoids shipping every symbol id\n // into JS just to count them (5k\u201350k rows on large monorepos).\n const fileCounts = this.stmt(\n 'SELECT file, COUNT(*) AS n FROM symbols GROUP BY file',\n ).all() as Array<{ file: string; n: number }>;\n\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 = IndexStore.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' as const,\n package: pkg,\n symbolCount: n,\n fileCount: 0,\n });\n }\n }\n\n // File counts per package\n const files = this.stmt('SELECT DISTINCT file FROM files').all() as { file: string }[];\n for (const { file } of files) {\n const pkg = IndexStore.derivePackage(file) ?? '(root)';\n fileToPkg.set(file, pkg);\n const node = pkgNodes.get(pkg);\n if (node) node.fileCount = (node.fileCount ?? 0) + 1;\n else {\n pkgNodes.set(pkg, {\n id: `pkg:${pkg}`,\n label: pkg,\n kind: 'package' as const,\n package: pkg,\n symbolCount: 0,\n fileCount: 1,\n });\n }\n }\n\n // Cross-package edges: resolve packages via JOIN instead of a full\n // symbol-id \u2192 package map in JS.\n const refRows = this.stmt(\n `SELECT r.call_type, sf.file AS from_file, st.file AS to_file\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 ).all() as Array<{ call_type: string; from_file: string; to_file: string }>;\n\n const edgeMap = new Map<string, { weight: number; types: Map<string, number> }>();\n for (const r of refRows) {\n const fromPkg = fileToPkg.get(r.from_file) ?? IndexStore.derivePackage(r.from_file) ?? '(root)';\n const toPkg = fileToPkg.get(r.to_file) ?? IndexStore.derivePackage(r.to_file) ?? '(root)';\n if (fromPkg === toPkg) continue;\n const key = `${fromPkg}\\u0000${toPkg}`;\n let e = edgeMap.get(key);\n if (!e) {\n e = { weight: 0, types: new Map() };\n edgeMap.set(key, e);\n }\n e.weight++;\n e.types.set(r.call_type, (e.types.get(r.call_type) ?? 0) + 1);\n }\n\n // Module imports are path/package references, not symbol names. They must\n // bypass symbol resolution or aliases/barrels leave the architecture with\n // isolated boxes even though source imports are explicit.\n const importRows = this.stmt(\n `SELECT r.to_name, s.file AS from_file\n FROM refs r\n JOIN symbols s ON s.id = r.from_id\n WHERE r.call_type = 'import'`,\n ).all() as Array<{ to_name: string; from_file: string }>;\n for (const r of importRows) {\n const fromPkg = fileToPkg.get(r.from_file) ?? IndexStore.derivePackage(r.from_file) ?? '(root)';\n const toPkg = IndexStore.packageFromImport(r.to_name);\n if (!fromPkg || !toPkg || fromPkg === toPkg || !pkgNodes.has(toPkg)) continue;\n const key = `${fromPkg}\\u0000${toPkg}`;\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++;\n edge.types.set('import', (edge.types.get('import') ?? 0) + 1);\n }\n\n const edges: GraphEdge[] = [];\n for (const [key, e] of edgeMap) {\n const [source, target] = key.split('\\u0000');\n // Dominant ref type = most frequent\n let bestType = 'call';\n let bestCount = 0;\n for (const [t, c] of e.types) {\n if (c > bestCount) {\n bestType = t;\n bestCount = c;\n }\n }\n edges.push({\n source: `pkg:${source}`,\n target: `pkg:${target}`,\n weight: e.weight,\n refType: bestType as CallType,\n });\n }\n\n return { nodes: [...pkgNodes.values()], edges };\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 type SymRow = {\n file: string;\n id: number;\n name: string;\n kind: string;\n lang: string;\n line: number;\n };\n\n // Phase 1: Discover which files belong to the target package. Instead of\n // loading every symbol row (5500+) and filtering in JS, scan just the\n // distinct file paths \u2014 a much smaller result set.\n const allFiles = this.stmt('SELECT DISTINCT file FROM symbols')\n .all() as { file: string }[];\n const pkgFilePaths = allFiles\n .filter((f) => (IndexStore.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 // Phase 2: Query symbols only for files in this package.\n const filePlaceholders = [...localFiles].map(() => '?').join(',');\n const pkgSyms = this.stmt(\n `SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`,\n )\n .all(...pkgFilePaths) as SymRow[];\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' as const,\n package: IndexStore.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\n // Build indexed-files set for import resolution \u2014 cheap DISTINCT query\n // on file paths only (no full symbol rows).\n const indexedFiles = new Set(allFiles.map((f) => f.file));\n\n // Phase 3: Restrict refs to those involving the target package's symbols\n // instead of scanning every ref row in the project.\n const refRows = this.stmt(\n `SELECT r.from_id, r.to_id, r.call_type\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 )\n .all(...pkgFilePaths, ...pkgFilePaths) as {\n from_id: number;\n to_id: number;\n call_type: string;\n }[];\n\n // Collect symbol ids referenced across packages for lazy fetch\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 = this.stmt(\n `SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`,\n )\n .all(...crossRefIds) 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, { weight: number; types: Map<string, number> }>();\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 key = `${fromFile}\\u0000${toFile}`;\n let e = edgeMap.get(key);\n if (!e) {\n e = { weight: 0, types: new Map() };\n edgeMap.set(key, e);\n }\n e.weight++;\n e.types.set(r.call_type, (e.types.get(r.call_type) ?? 0) + 1);\n }\n\n const importRows = this.stmt(\n `SELECT r.from_id, r.to_name\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 )\n .all(...pkgFilePaths) as { from_id: number; to_name: string }[];\n for (const r of importRows) {\n const fromFile = symToFile.get(r.from_id);\n if (!fromFile || !localFiles.has(fromFile)) continue;\n const toFile = IndexStore.resolveRelativeImport(fromFile, r.to_name, indexedFiles);\n if (!toFile || fromFile === toFile) continue;\n ensureFileNode(fromFile);\n ensureFileNode(toFile);\n const key = `${fromFile}\\u0000${toFile}`;\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++;\n edge.types.set('import', (edge.types.get('import') ?? 0) + 1);\n }\n\n const edges: GraphEdge[] = [];\n for (const [key, e] of edgeMap) {\n const [source, target] = key.split('\\u0000');\n let bestType = 'call';\n let bestCount = 0;\n for (const [t, c] of e.types) {\n if (c > bestCount) {\n bestType = t;\n bestCount = c;\n }\n }\n edges.push({\n source: `file:${source}`,\n target: `file:${target}`,\n weight: e.weight,\n refType: bestType as CallType,\n });\n }\n\n return { nodes: [...fileNodes.values()], edges };\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 type SymRow = {\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 // Query ONLY symbols from the target file \u2014 avoids loading the entire\n // project's symbol table (5500+ rows) just to render one file's graph.\n // Cross-file symbols referenced via refs are fetched lazily below.\n const syms = this.stmt(\n 'SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id',\n )\n .all(fileFilter) as SymRow[];\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 toGraphNode = (s: SymRow): GraphNode => ({\n id: `sym:${s.id}`,\n label: s.name,\n kind: 'symbol' as const,\n symbolId: s.id,\n symbolKind: s.kind as SymbolKind,\n file: s.file,\n package: IndexStore.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 // Collect refs FROM symbols in this file (outgoing) and TO symbols in this\n // file (incoming), so the graph shows both callers and callees.\n const symIds = new Set(syms.map((s) => s.id));\n // Two index-friendly JOINs (outgoing + incoming) UNIONed together.\n // The previous `from_id IN (SELECT \u2026 WHERE file = ?) OR to_id IN (SELECT\n // \u2026 WHERE file = ?)` form could not be flattened by the SQLite planner and\n // degenerated to a sequential scan over `refs` for every graph render.\n // Each JOIN drives off `idx_s_file` (symbols.file) and probes `refs` via\n // `idx_r_from` / `idx_r_to_id`. UNION (not UNION ALL) deduplicates on the\n // projection tuple `(from_id, to_id, to_name, call_type, line)`. A row\n // whose both endpoints are in the file would in principle appear once in\n // each branch and collapse to one; distinct physical rows have distinct\n // `(from_id, to_id, to_name, call_type, line)` tuples under the `refs`\n // schema, so dedup is a no-op in practice and the row set matches the\n // original OR query exactly. `r.id` is intentionally NOT projected: it\n // would be dead (never read by the consumer) and would slightly widen\n // the dedup tuple without changing correctness. If a non-PK column is\n // ever added to the projection, revisit this comment \u2014 it can re-introduce\n // real duplicates that UNION would silently drop.\n const refRows = this.stmt(\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 .all(fileFilter, fileFilter) as {\n from_id: number;\n to_id: number | null;\n to_name: string;\n call_type: string;\n line: number;\n }[];\n\n const edgeMap = new Map<string, { weight: number; types: Map<string, number> }>();\n for (const r of refRows) {\n if (r.to_id === null) continue;\n // Only include edges where at least one endpoint is in our file\n if (!symIds.has(r.from_id) && !symIds.has(r.to_id)) continue;\n relatedIds.add(r.from_id);\n relatedIds.add(r.to_id);\n const key = `${r.from_id}\\u0000${r.to_id}`;\n let e = edgeMap.get(key);\n if (!e) {\n e = { weight: 0, types: new Map() };\n edgeMap.set(key, e);\n }\n e.weight++;\n e.types.set(r.call_type, (e.types.get(r.call_type) ?? 0) + 1);\n }\n\n const edges: GraphEdge[] = [];\n for (const [key, e] of edgeMap) {\n const [fromId, toId] = key.split('\\u0000').map(Number);\n let bestType = 'call';\n let bestCount = 0;\n for (const [t, c] of e.types) {\n if (c > bestCount) {\n bestType = t;\n bestCount = c;\n }\n }\n edges.push({\n source: `sym:${fromId}`,\n target: `sym:${toId}`,\n weight: e.weight,\n refType: bestType as CallType,\n });\n }\n\n // Lazily fetch referenced symbols from other files that aren't already\n // loaded (discovered via refs above) instead of loading the entire project\n // symbol table. For a typical file, this saves loading 5000+ irrelevant rows.\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 = this.stmt(\n `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`,\n )\n .all(...missingIds) as SymRow[];\n for (const s of extras) symById.set(s.id, s);\n }\n\n // Include direct callers/callees from other files. Previously their edges\n // pointed at missing React Flow nodes, which made cross-file relations\n // invisible exactly where users expected to inspect them.\n const nodes = [...relatedIds]\n .map((id) => symById.get(id))\n .filter((symbol): symbol is SymRow => symbol !== undefined)\n .sort((a, b) => {\n const aExternal = a.file === fileFilter ? 0 : 1;\n const bExternal = b.file === fileFilter ? 0 : 1;\n return (\n aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id\n );\n })\n .map(toGraphNode);\n\n return { nodes, edges };\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 * 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", "// \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\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", "/**\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", "/**\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 * 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 LockError,\n indexCircuitBreaker,\n} from './circuit-breaker.js';\nimport { indexService, searchService, statsService } from './index-service.js';\nimport type { IndexResult, IndexStats } from './schema.js';\nimport { isIndexablePath } from './languages.js';\nimport { indexStorePool } from './writer.js';\nimport type {\n HostToWorker,\n IndexOpArgs,\n OpName,\n OpShapes,\n SearchOpArgs,\n SearchOpResult,\n StatsOpArgs,\n WorkerToHost,\n} from './worker-protocol.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 /** Circuit-breaker state \u2014 `open` means indexing is paused after repeated failures. */\n circuit: CircuitSnapshot;\n} {\n return {\n ready: _ready,\n indexing: _indexing,\n currentFile: _currentFile,\n totalFiles: _totalFiles,\n lastError: _lastError,\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\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 = 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 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 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 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 if (isRecoverableConstraintError(err) && !opts.force) {\n _lastError = null;\n const rebuildResult = await runStartupIndex({\n ...opts,\n force: true,\n });\n _ready = true;\n return rebuildResult;\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// \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 // 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", "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 * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { availableParallelism } from 'node:os';\nimport type { Dirent, Stats } from 'node:fs';\nimport type { Context } from '@wrongstack/core/agent';\nimport { indexParallelBatchSize, isFrugalPerf } from '@wrongstack/core/utils';\nimport type { FileMeta, IndexResult, Ref, Symbol as IndexSymbol, SymbolLang } from './schema.js';\nimport { IndexStore } from './writer.js';\nimport { parseSymbols as parseTs } from './ts-parser.js';\nimport { detectLang, INDEXABLE_EXTENSIONS } from './languages.js';\nimport { parseSymbols as parseGo } from './go-parser.js';\nimport { parseSymbols as parsePy } from './py-parser.js';\nimport { parseSymbols as parseRs } from './rs-parser.js';\nimport { parseSymbols as parseJson } from './json-parser.js';\nimport { parseSymbols as parseYaml } from './yaml-parser.js';\nimport { parseSymbols as parseGeneric } from './generic-parser.js';\nimport { loadGitignoreMatcher, type IgnoreMatcher } from './gitignore.js';\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 = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n 'coverage',\n '.turbo',\n '__snapshots__',\n '.nyc_output',\n];\nconst DEFAULT_IGNORE_FILES = new Set(['package-lock.json', 'pnpm-lock.yaml', 'pnpm-lock.yml']);\nconst MAX_INDEX_FILE_BYTES = 5 * 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\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<{ files: string[]; complete: boolean; errors: string[] }> {\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\n/**\n * Dispatch to the best available parser for `lang`.\n *\n * First-class AST/toolchain parsers run when available; every other language\n * (and native failures) land in the generic regex extractor so files always\n * contribute searchable symbols when they contain structure.\n */\nasync function parseFile(\n file: string,\n content: string,\n lang: string,\n): Promise<ReturnType<typeof parseTs>> {\n const symbolLang = lang as SymbolLang;\n switch (symbolLang) {\n case 'ts':\n case 'tsx':\n case 'js':\n case 'jsx':\n return parseTs({ file, content, lang: symbolLang });\n case 'go':\n return parseGo({ file, content, lang: 'go' });\n case 'py':\n return parsePy({ file, content, lang: 'py' });\n case 'rs':\n return parseRs({ file, content, lang: 'rs' });\n case 'json':\n return parseJson({ file, content, lang: 'json' });\n case 'yaml':\n return parseYaml({ file, content, lang: 'yaml' });\n default:\n // c, cpp, java, csharp, ruby, shell, md, \u2026 and 'other'\n return parseGeneric({ file, content, lang: symbolLang });\n }\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\nasync function runIndexerWithStore(store: IndexStore, opts: IndexerOptions): 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 force =\n (opts.force ?? false) || store.getMetadata('relation_graph_version') !== relationGraphVersion;\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 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 }\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 // 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?.(batchEnd, files.length);\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 parseFile>> | 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 parseFile>>;\n try {\n parsed = await parseFile(file, content, lang);\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.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 // Resolution must run even when every file was an incremental-cache hit:\n // older indexes can contain unresolved refs from a previous interrupted run.\n store.resolveRefs();\n store.setMetadata('relation_graph_version', relationGraphVersion);\n // Refresh query planner stats after bulk writes (best-effort, free on small DBs).\n store.optimize();\n\n const durationMs = Date.now() - startMs;\n store.setLastIndexed(Date.now());\n\n return {\n filesIndexed,\n symbolsIndexed,\n langStats,\n durationMs,\n errors,\n };\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 * as ts from '@typescript/typescript6';\nimport type { FileSymbols, Ref, Symbol as IndexSymbol, SymbolKind, SymbolLang } from './schema.js';\n\n// Map TypeScript SyntaxKind \u2192 our SymbolKind taxonomy\nconst KIND_MAP: Partial<Record<ts.SyntaxKind, SymbolKind>> = {\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\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 KIND_MAP[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(printer: ts.Printer, node: ts.Declaration, sourceFile: ts.SourceFile): 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 */\nexport function parseSymbols(opts: ParseOptions): FileSymbols {\n const { file, content, lang } = opts;\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) refs.push({ fromId: 0, toName: name, callType: node.token === ts.SyntaxKind.ExtendsKeyword ? 'inherit' : 'implement', line: lineNum });\n }\n } else if (ts.isImportDeclaration(node)) {\n const moduleName = getModuleName(node);\n if (moduleName) refs.push({ fromId: 0, toName: moduleName, callType: 'import', line: 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/** Get the module path string from an import declaration. */\nfunction getModuleName(node: ts.ImportDeclaration): string {\n const moduleSpecifier = node.moduleSpecifier;\n if (ts.isStringLiteral(moduleSpecifier)) return moduleSpecifier.text;\n /* v8 ignore next -- an import declaration's module specifier is always a string literal; 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/** Detect SymbolLang from a file path \u2014 re-exported from the central map. */\nexport { detectLang } from './languages.js';\n", "/**\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 * 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", "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 * 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", "/**\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 let match: RegExpExecArray | null;\n while ((match = re.exec(content)) !== null) {\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", "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", "/**\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", "/**\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 * Every operation opens its own short-lived IndexStore, exactly like the old\n * per-call code paths did \u2014 there is no connection state to share, which keeps\n * multi-project usage trivially correct and crash recovery simple.\n */\n\nimport type { Context } from '@wrongstack/core/agent';\nimport { runIndexer } 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\n/** A run with no live agent Context \u2014 `runIndexer` only reads `opts`. */\nfunction stubCtx(projectRoot: string): Context {\n return {\n projectRoot,\n cwd: projectRoot,\n messages: [],\n todos: [],\n readFiles: new Set<string>(),\n fileMtimes: new Map<string, number>(),\n } as never as Context;\n}\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 return runIndexer(stubCtx(args.projectRoot), {\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}\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", "\nimport type { Tool } from '@wrongstack/core/types';\nimport { codebaseIndexDirOverride } from './writer.js';\nimport { isIndexing, runStartupIndex } from './background-indexer.js';\nimport { indexCircuitBreaker } from './circuit-breaker.js';\n\nexport const codebaseIndexTool: Tool<CodebaseIndexInput, CodebaseIndexOutput> = {\n name: 'codebase-index',\n category: 'Project',\n icon: 'index',\n description:\n 'Build or incrementally update the project-wide symbol index. This powers fast codebase search and understanding. ' +\n 'By default it only processes files that have changed since the last indexing run.',\n usageHint:\n 'CREATE OR REFRESH THE SEARCH INDEX:\\n\\n' +\n '- When `codebase-stats` or `codebase-search` reports no persisted index, call this without arguments, then retry the search.\\n' +\n '- Normal and first-time usage: call without arguments for an incremental build.\\n' +\n '- Use `force: true` only for a corrupt/stale index or an explicitly requested clean rebuild.\\n' +\n '- Use `langs` to restrict to specific languages if you only care about certain parts of the project.\\n' +\n 'This tool is relatively expensive \u2014 do not call it on every turn. Use it when the index is stale or before heavy codebase-search sessions.',\n permission: 'confirm',\n mutating: true,\n capabilities: ['fs.write.outside-project'],\n timeoutMs: 120_000,\n inputSchema: {\n type: 'object',\n properties: {\n force: {\n type: 'boolean',\n description: 'Force a full reindex \u2014 clears the index first and reindexes all files.',\n },\n langs: {\n type: 'array',\n items: { type: 'string' },\n description: 'Limit reindex to specific languages: ts, tsx, js, jsx, go, py, rs',\n },\n },\n },\n async execute(input, ctx, execOpts) {\n // If the startup index is still running, tell the agent to wait instead of\n // firing a second reindex that would just queue behind the mutex.\n if (isIndexing()) {\n return {\n filesIndexed: 0,\n symbolsIndexed: 0,\n langStats: {},\n durationMs: 0,\n errors: [],\n note: 'A full index is already in progress. Retry codebase-index after it completes (check codebase-stats).',\n };\n }\n\n // Circuit breaker: after repeated failures/timeouts indexing is paused.\n // Report instead of erroring so the agent can carry on without the index.\n const circuit = indexCircuitBreaker.snapshot();\n if (circuit.state === 'open' && circuit.cooldownRemainingMs > 0) {\n return {\n filesIndexed: 0,\n symbolsIndexed: 0,\n langStats: {},\n durationMs: 0,\n errors: [],\n note:\n `Codebase indexing is paused after repeated failures (last: ${circuit.lastFailure ?? 'unknown'}). ` +\n `Auto-retry possible in ${Math.ceil(circuit.cooldownRemainingMs / 1000)}s; the user can run /codebase-reindex to retry immediately.`,\n };\n }\n\n // Route through the background coordinator so the run shares the\n // process-wide mutex, the watchdog timeout, and breaker accounting with\n // the startup scan and live reindexes (a direct runIndexer call here used\n // to race them on the same SQLite file).\n return await runStartupIndex({\n projectRoot: ctx.projectRoot,\n force: input.force ?? false,\n langs: input.langs,\n indexDir: codebaseIndexDirOverride(ctx),\n signal: execOpts?.signal,\n });\n },\n};\n\n// \u2500\u2500\u2500 Types for tool I/O \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 CodebaseIndexInput {\n force?: boolean | undefined;\n langs?: string[] | undefined;\n}\n\ninterface CodebaseIndexOutput {\n filesIndexed: number;\n symbolsIndexed: number;\n langStats: Record<string, number>;\n durationMs: number;\n errors: string[];\n /** Advisory note when the indexer was skipped (e.g. another index in progress). */\n note?: string | undefined;\n}\n", "/**\n * `codebase-search` tool \u2014 search the symbol index with BM25 ranking.\n *\n * Usage: codebase-search({\n * query: string, // search terms\n * kind?: string, // class|function|interface|method|const|...\n * lang?: string, // ts|tsx|js|jsx|go|py|rs\n * file?: string, // filter to a specific file path (substring match)\n * limit?: number, // max results (default 20, max 100)\n * })\n *\n * Returns: [{ name, kind, lang, file, line, signature, snippet, score }, ...]\n *\n * The query executes in the index worker via FTS5 (`MATCH` + native `bm25()`)\n * \u2014 the main thread never opens SQLite, so a contended or wedged index can\n * slow this tool down but can never freeze the terminal.\n */\n\nimport type { Tool } from '@wrongstack/core/types';\nimport {\n codebaseIndexStats,\n getIndexState,\n searchCodebaseIndex,\n} from './background-indexer.js';\nimport type { SearchResult } from './schema.js';\nimport { codebaseIndexDirOverride } from './writer.js';\nexport const codebaseSearchTool: Tool<CodebaseSearchInput, CodebaseSearchOutput> = {\n name: 'codebase-search',\n category: 'Project',\n icon: 'index',\n description:\n 'Search code symbols using a fast SQLite+BM25 index, with optional LSP fallback. ' +\n 'Prefer this before broad `tree`, `glob`, or `grep` exploration when finding code by name or concept. ' +\n 'Set `preferLsp: true` for live precision when the LSP plugin is active (supersedes codebase-lsp-search).',\n usageHint:\n 'FIRST CHOICE FOR INDEXABLE CODE UNDERSTANDING:\\n\\n' +\n '- Call before broad `tree`, `glob`, or `grep` exploration when locating symbols, concepts, definitions, or candidate modules.\\n' +\n '- `kind` filter is very useful (e.g. only functions or only interfaces).\\n' +\n '- Combine with `file` filter to scope to a specific directory or module.\\n' +\n '- If `indexStatus` reports no persisted data, run `codebase-index` and retry.\\n' +\n 'Use `grep` afterwards for exact text, regexes, unsupported content, or concrete usage sites.',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n // The index host has its own 30s read watchdog. Leave enough headroom for\n // worker teardown and structured timeout reporting.\n timeoutMs: 35_000,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query \u2014 searches symbol names, signatures, and doc comments',\n },\n kind: {\n type: 'string',\n enum: [\n 'class', 'interface', 'enum', 'type', 'function', 'method', 'var', 'const', 'let',\n 'property', 'parameter', 'namespace', 'object', 'literal', 'schema', 'struct', 'trait',\n 'impl', 'static', 'mod',\n ],\n description: 'Filter by indexed symbol kind',\n },\n lang: {\n type: 'string',\n enum: ['ts', 'tsx', 'js', 'jsx', 'go', 'py', 'rs', 'json', 'yaml'],\n description: 'Filter by indexed language',\n },\n lspKind: {\n type: 'integer',\n description:\n 'Filter by LSP SymbolKind number (e.g. 5=Class, 12=Function, 11=Interface, 10=Enum)',\n },\n file: {\n type: 'string',\n description: 'Filter to files matching this path substring',\n },\n limit: {\n type: 'integer',\n description: 'Maximum results to return (default 20, max 100)',\n minimum: 1,\n maximum: 100,\n },\n preferLsp: {\n type: 'boolean',\n description:\n 'Prefer live LSP results over the index. Index-only when the LSP plugin is not active. ' +\n 'When the LSP plugin is active and this is true, results come from live workspaceSymbol queries.',\n },\n },\n required: ['query'],\n },\n async execute(input, ctx, execOpts) {\n const state = getIndexState();\n if (state.indexing && !state.ready) {\n return {\n results: [],\n total: 0,\n query: input.query,\n indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`,\n };\n }\n // WAL provides a consistent previous snapshot while a refresh writes, so\n // keep search available instead of returning an empty, misleading result.\n if (state.lastError) {\n const circuit = state.circuit;\n const retryHint =\n circuit.state === 'open'\n ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1000)}s); the user can run /codebase-reindex to retry now.`\n : 'Try /codebase-reindex.';\n return {\n results: [],\n total: 0,\n query: input.query,\n indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`,\n };\n }\n\n const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 20), 100));\n const { results, total } = await searchCodebaseIndex(\n {\n projectRoot: ctx.projectRoot,\n indexDir: codebaseIndexDirOverride(ctx),\n query: input.query,\n kind: input.kind?.toLowerCase(),\n lang: input.lang?.toLowerCase(),\n file: input.file,\n lspKind: input.lspKind,\n limit,\n },\n { signal: execOpts?.signal },\n );\n // Process-local readiness resets on launch while the SQLite index persists.\n // A zero-hit query on a cold process therefore cannot by itself prove the\n // index is missing; consult persisted metadata before emitting that hint.\n let hasPersistedIndex = state.ready || total > 0;\n if (!hasPersistedIndex) {\n try {\n const stats = await codebaseIndexStats(\n { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },\n { signal: execOpts?.signal },\n );\n hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;\n } catch {\n // Search already completed successfully. Keep the conservative missing\n // hint rather than failing the whole tool because the stats probe failed.\n }\n }\n return {\n results,\n total,\n query: input.query,\n ...(hasPersistedIndex\n ? {}\n : { indexStatus: 'No persisted index data found. Run codebase-index to build it.' }),\n };\n },\n};\n\n// \u2500\u2500\u2500 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface CodebaseSearchInput {\n query: string;\n kind?: string | undefined;\n lang?: string | undefined;\n file?: string | undefined;\n limit?: number | undefined;\n lspKind?: number | undefined;\n /**\n * When true, hints that LSP-based search should be preferred over the index.\n * The built-in tool ignores this (index-only), but when the LSP plugin is\n * active it intercepts the tool and provides live LSP workspaceSymbol results.\n */\n preferLsp?: boolean | undefined;\n}\n\ninterface CodebaseSearchOutput {\n results: SearchResult[];\n total: number; // total candidates before limit\n query: string;\n /** Non-empty when the index blocked the search (not ready, indexing, failed). */\n indexStatus?: string | undefined;\n}\n", "/**\n * `codebase-stats` tool \u2014 report index health and statistics.\n *\n * Usage: codebase-stats({})\n *\n * Returns: { totalSymbols, totalFiles, byLang, byKind, lastIndexed, sizeBytes, version }\n */\n\nimport type { Tool } from '@wrongstack/core/types';\nimport { codebaseIndexStats, getIndexState } from './background-indexer.js';\nimport { IndexTimeoutError } from './circuit-breaker.js';\nimport { SCHEMA_VERSION } from './schema.js';\nimport { codebaseIndexDirOverride, resolveIndexDir } from './writer.js';\n\nexport const codebaseStatsTool: Tool<Record<string, never>, CodebaseStatsOutput> = {\n name: 'codebase-stats',\n category: 'Project',\n icon: 'index',\n description:\n 'Check whether a persisted codebase index exists and report its health and statistics (symbols, files, language/kind breakdown, size, last update).',\n usageHint:\n 'CHECK ONCE BEFORE BROAD CODE EXPLORATION:\\n\\n' +\n '- Call before broad `tree`, `glob`, or `grep` exploration to see whether index-backed search is available.\\n' +\n '- If it reports no persisted data, run `codebase-index`, then use `codebase-search`.\\n' +\n '- No arguments required.\\n' +\n 'Lightweight and safe to call frequently.',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n // The index host has its own 30s read watchdog. Keep the outer tool budget\n // longer so the host can return a structured timeout instead of being\n // pre-empted by the generic tool executor.\n timeoutMs: 35_000,\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n async execute(_input, ctx, execOpts) {\n const idxState = getIndexState();\n const indexPath = resolveIndexDir(ctx.projectRoot, codebaseIndexDirOverride(ctx));\n\n // The index worker can be busy parsing or writing during a startup build.\n // Do not queue a stats read merely to report progress: on large projects\n // that made this lightweight tool hit its outer timeout. Return the\n // process-local lifecycle state immediately and let callers retry later.\n if (idxState.indexing) {\n return {\n totalSymbols: 0,\n totalFiles: 0,\n byLang: {},\n byKind: {},\n lastIndexed: null,\n sizeBytes: 0,\n indexPath,\n version: SCHEMA_VERSION,\n statsAvailable: false,\n indexing: {\n currentFile: idxState.currentFile,\n totalFiles: idxState.totalFiles,\n },\n indexStatus: idxState.ready\n ? `Index refresh in progress (${idxState.currentFile}/${idxState.totalFiles} files). The persisted snapshot remains available to codebase-search; retry stats after refresh.`\n : `Startup indexing in progress (${idxState.currentFile}/${idxState.totalFiles} files). Do not start another index; retry stats or codebase-search after completion.`,\n };\n }\n\n // Always inspect persisted state. Readiness is process-local and resets on\n // every CLI launch, while the SQLite index intentionally survives launches.\n // Fetched via the index host (worker thread when available) \u2014 the main\n // thread never opens SQLite here.\n let stats;\n try {\n stats = await codebaseIndexStats(\n { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },\n { signal: execOpts?.signal },\n );\n } catch (err) {\n if (!(err instanceof IndexTimeoutError) && (err as Error)?.name !== 'IndexTimeoutError') {\n throw err;\n }\n return {\n totalSymbols: 0,\n totalFiles: 0,\n byLang: {},\n byKind: {},\n lastIndexed: null,\n sizeBytes: 0,\n indexPath,\n version: SCHEMA_VERSION,\n statsAvailable: false,\n indexStatus:\n 'Index statistics timed out. Do not repeatedly retry stats; try codebase-search directly or use the appropriate grep/glob/tree fallback.',\n };\n }\n\n const circuit = idxState.circuit;\n const hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;\n let indexStatus: string | undefined;\n if (circuit.state === 'open') {\n indexStatus =\n `${hasPersistedIndex ? 'Indexing' : 'No persisted index data found, and indexing'} is paused after repeated failures ` +\n `(last: ${circuit.lastFailure ?? 'unknown'}); auto-retry in ` +\n `${Math.ceil(circuit.cooldownRemainingMs / 1000)}s, or run /codebase-reindex. ` +\n (hasPersistedIndex\n ? 'Stats reflect the last successful build.'\n : 'Build the index after recovery.');\n } else if (!hasPersistedIndex) {\n indexStatus =\n 'No persisted index data found. Run codebase-index to build it before broad code exploration.';\n }\n return {\n totalSymbols: stats.totalSymbols,\n totalFiles: stats.totalFiles,\n byLang: stats.byLang,\n byKind: stats.byKind,\n lastIndexed: stats.lastIndexed,\n sizeBytes: stats.sizeBytes,\n indexPath: stats.indexPath,\n version: stats.version,\n statsAvailable: true,\n ...(indexStatus ? { indexStatus } : {}),\n };\n },\n};\n\ninterface CodebaseStatsOutput {\n totalSymbols: number;\n totalFiles: number;\n byLang: Record<string, number>;\n byKind: Record<string, number>;\n lastIndexed: number | null;\n sizeBytes: number;\n indexPath: string;\n version: number;\n /** False when lifecycle state was returned without opening the SQLite index. */\n statsAvailable: boolean;\n /** Present while an index build or refresh is active. */\n indexing?:\n | {\n currentFile: number;\n totalFiles: number;\n }\n | undefined;\n /** Non-empty when the index is not ready or is still building. */\n indexStatus?: string | undefined;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ts", "fs", "expectDefined", "fs", "path", "path", "pos", "line", "path", "fs", "fs", "path", "parseSymbols", "resolve", "stdout", "code", "spawn", "fs", "os", "path", "parseSymbols", "parseSymbols", "resolve", "spawn", "expectDefined", "spawn", "fs", "path", "parseSymbols", "resolve", "spawn", "stdout", "expectDefined", "expectDefined", "path", "parseSymbols", "regexParse", "basename", "expectDefined", "expectDefined", "parseSymbols", "regexParse", "expectDefined", "makeSymbol", "fs", "path", "resolve", "parseSymbols", "assignRefsToSymbols", "stat", "expectDefined", "resolve", "chain"]
|
|
7
7
|
}
|