@orquentra/codebuddy 2.9.0 → 2.14.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/CLAUDE_CODEX.md +33 -0
- package/README.md +27 -3
- package/dist/{adapters-S4HHTWJW.js → adapters-OJRSVFV3.js} +2 -2
- package/dist/{chunk-GKNYMNFM.js → chunk-4DKYKDHI.js} +6 -1
- package/dist/chunk-4DKYKDHI.js.map +1 -0
- package/dist/{chunk-SRA4MDVG.js → chunk-7QMHIJBA.js} +30 -3
- package/dist/chunk-7QMHIJBA.js.map +1 -0
- package/dist/{chunk-6NZEZJYD.js → chunk-ESC4Y3L7.js} +3 -5
- package/dist/{chunk-6NZEZJYD.js.map → chunk-ESC4Y3L7.js.map} +1 -1
- package/dist/chunk-GQKWUHMA.js +20 -0
- package/dist/chunk-GQKWUHMA.js.map +1 -0
- package/dist/{chunk-J4T6HHI4.js → chunk-L6BRYZ6G.js} +537 -316
- package/dist/chunk-L6BRYZ6G.js.map +1 -0
- package/dist/{chunk-UK6XTS2Y.js → chunk-LRQNPYCK.js} +13 -19
- package/dist/chunk-LRQNPYCK.js.map +1 -0
- package/dist/{chunk-GXOML4UZ.js → chunk-LW75SKZR.js} +2 -2
- package/dist/{chunk-ENKDFLOO.js → chunk-UOTKHSQ5.js} +25 -8
- package/dist/chunk-UOTKHSQ5.js.map +1 -0
- package/dist/{chunk-W7GAXL6C.js → chunk-V7PT6ER5.js} +6 -5
- package/dist/chunk-V7PT6ER5.js.map +1 -0
- package/dist/{chunk-F4BJ3DXW.js → chunk-XSNW6Y3X.js} +1253 -315
- package/dist/chunk-XSNW6Y3X.js.map +1 -0
- package/dist/{chunk-2WH2Q4TA.js → chunk-YJPCS2GP.js} +256 -68
- package/dist/chunk-YJPCS2GP.js.map +1 -0
- package/dist/{chunk-TLWGZVPC.js → chunk-YNSTIMCF.js} +746 -66
- package/dist/chunk-YNSTIMCF.js.map +1 -0
- package/dist/{chunk-QN76UJP7.js → chunk-YWDH6DQ6.js} +70 -4
- package/dist/chunk-YWDH6DQ6.js.map +1 -0
- package/dist/cli/index.js +23 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/{codebuddy-DO7ZhoWn.d.ts → codebuddy-CHqMptZB.d.ts} +35 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +8 -8
- package/dist/install-EU35ERK5.js +30 -0
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/lean-43ADYTBW.js +28 -0
- package/dist/{main-YJKSYFPT.js → main-SFC5LV4E.js} +704 -242
- package/dist/main-SFC5LV4E.js.map +1 -0
- package/dist/{operations-OG4ISWIM.js → operations-6Z6ZDVBP.js} +3 -3
- package/dist/{pretool-command-PRFOIHMW.js → pretool-command-B5GY5T6E.js} +2 -2
- package/dist/prompt-gate-TDTFFQW2.js +8 -0
- package/dist/{runtime-GCSEIAVD.js → runtime-W5AW32SD.js} +14 -6
- package/dist/{summaries-ML53R7EL.js → summaries-32PHOB6L.js} +3 -4
- package/dist/{zvec-XDZNZ2OL.js → zvec-24HLAFGN.js} +3 -4
- package/dist/zvec-24HLAFGN.js.map +1 -0
- package/package.json +11 -2
- package/dist/chunk-2WH2Q4TA.js.map +0 -1
- package/dist/chunk-ENKDFLOO.js.map +0 -1
- package/dist/chunk-F4BJ3DXW.js.map +0 -1
- package/dist/chunk-GKNYMNFM.js.map +0 -1
- package/dist/chunk-ITQO632W.js +0 -673
- package/dist/chunk-ITQO632W.js.map +0 -1
- package/dist/chunk-J4T6HHI4.js.map +0 -1
- package/dist/chunk-QN76UJP7.js.map +0 -1
- package/dist/chunk-SRA4MDVG.js.map +0 -1
- package/dist/chunk-TLWGZVPC.js.map +0 -1
- package/dist/chunk-UK6XTS2Y.js.map +0 -1
- package/dist/chunk-W7GAXL6C.js.map +0 -1
- package/dist/install-BXM56QCD.js +0 -19
- package/dist/lean-command-7DFW32MA.js +0 -19
- package/dist/main-YJKSYFPT.js.map +0 -1
- /package/dist/{adapters-S4HHTWJW.js.map → adapters-OJRSVFV3.js.map} +0 -0
- /package/dist/{chunk-GXOML4UZ.js.map → chunk-LW75SKZR.js.map} +0 -0
- /package/dist/{install-BXM56QCD.js.map → install-EU35ERK5.js.map} +0 -0
- /package/dist/{lean-command-7DFW32MA.js.map → lean-43ADYTBW.js.map} +0 -0
- /package/dist/{operations-OG4ISWIM.js.map → operations-6Z6ZDVBP.js.map} +0 -0
- /package/dist/{pretool-command-PRFOIHMW.js.map → pretool-command-B5GY5T6E.js.map} +0 -0
- /package/dist/{runtime-GCSEIAVD.js.map → prompt-gate-TDTFFQW2.js.map} +0 -0
- /package/dist/{summaries-ML53R7EL.js.map → runtime-W5AW32SD.js.map} +0 -0
- /package/dist/{zvec-XDZNZ2OL.js.map → summaries-32PHOB6L.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/version.ts","../src/core/memory-file-store.ts","../src/core/memory-lifecycle.ts","../src/db/schema.ts","../src/core/memory-search.ts"],"sourcesContent":["{\n \"name\": \"@orquentra/codebuddy\",\n \"version\": \"2.9.0\",\n \"description\": \"Local-first project context and memory for AI coding agents.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"author\": \"Orquentra Inc\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Orquentra-Inc/CodeBuddy\"\n },\n \"engines\": {\n \"node\": \">=20\"\n },\n \"bin\": {\n \"codebuddy\": \"./dist/cli/index.js\"\n },\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./langgraph\": {\n \"types\": \"./dist/langgraph/index.d.ts\",\n \"import\": \"./dist/langgraph/index.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"src/db/migrations\",\n \"README.md\",\n \"CLAUDE_CODEX.md\",\n \"ARCHITECTURE.md\",\n \"WORKFLOW.md\",\n \"LICENSE\",\n \"docker-compose.yml\",\n \"examples\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"db:generate\": \"drizzle-kit generate\",\n \"db:migrate\": \"drizzle-kit migrate\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --passWithNoTests\",\n \"test:pg\": \"bash scripts/test-pg-recovery.sh\",\n \"eval:agent-ab\": \"tsx scripts/eval-agent-ab.mts\",\n \"eval:memory-ab\": \"tsx scripts/eval-memory-ab.mts\",\n \"eval:retrieval\": \"tsx scripts/eval-retrieval.mts\",\n \"eval:savings\": \"tsx scripts/eval-task-savings.mts\",\n \"branches:prune\": \"bash scripts/prune-merged-branches.sh\",\n \"lint\": \"biome check .\",\n \"format\": \"biome format --write .\",\n \"prepublishOnly\": \"node scripts/check-release.mjs && npm run typecheck && npm test && npm run build\",\n \"eval:semantic\": \"tsx scripts/eval-semantic.mts\"\n },\n \"keywords\": [\n \"mcp\",\n \"memory\",\n \"agent\",\n \"multi-agent\",\n \"huggingface\",\n \"pgvector\",\n \"postgres\",\n \"langgraph\",\n \"claude\"\n ],\n \"homepage\": \"https://github.com/Orquentra-Inc/CodeBuddy\",\n \"bugs\": {\n \"url\": \"https://github.com/Orquentra-Inc/CodeBuddy/issues\"\n },\n \"dependencies\": {\n \"@clack/prompts\": \"^1.8.1\",\n \"@huggingface/inference\": \"^4.13.19\",\n \"@modelcontextprotocol/sdk\": \"^1.17.5\",\n \"commander\": \"^14.0.0\",\n \"drizzle-orm\": \"^0.45.2\",\n \"js-tiktoken\": \"^1.0.21\",\n \"p-queue\": \"^10.0.0\",\n \"p-retry\": \"^8.0.1\",\n \"p-timeout\": \"^8.1.0\",\n \"picocolors\": \"^1.1.1\",\n \"pino\": \"^10.4.0\",\n \"postgres\": \"^3.4.7\",\n \"yaml\": \"^2.9.0\",\n \"zod\": \"^4.6.7\"\n },\n \"peerDependencies\": {\n \"@langchain/langgraph\": \"^0.2.0 || ^0.3.0\"\n },\n \"peerDependenciesMeta\": {\n \"@langchain/langgraph\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.5.0\",\n \"@types/node\": \"^24.3.0\",\n \"drizzle-kit\": \"^0.31.10\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.5\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^4.1.12\"\n },\n \"optionalDependencies\": {\n \"@huggingface/transformers\": \"^4.3.2\"\n }\n}\n","import pkg from \"../package.json\" with { type: \"json\" };\n\n/**\n * The single source of truth for the product version. `package.json` is the\n * canonical value (npm reads it for the published package); the CLI `--version`\n * flag and the MCP server handshake both import it from here so a version bump\n * only ever touches `package.json`. The bundler inlines this at build time, so\n * there is no runtime file read.\n */\nexport const VERSION: string = pkg.version;\n","import { mkdir, readdir, readFile, rm, stat } from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport { redactPersisted } from \"../core/sensitive.js\";\nimport { VERSION } from \"../version.js\";\nimport {\n assertNoSymlinkBetween,\n composeMarkdown,\n parseFrontMatter,\n writeAtomic,\n} from \"./markdown-store-fs.js\";\nimport { isEligibleMemory } from \"./memory-lifecycle.js\";\n\nexport const FACT_CATEGORIES = [\"general\", \"incident\"] as const;\nexport type FactCategory = (typeof FACT_CATEGORIES)[number];\nexport const INCIDENT_SEVERITIES = [\"low\", \"medium\", \"high\", \"critical\"] as const;\nexport type IncidentSeverity = (typeof INCIDENT_SEVERITIES)[number];\n\nconst factFrontMatterSchema = z.object({\n schemaVersion: z.literal(1),\n id: z.string().regex(/^fact_[a-z0-9]+$/),\n namespace: z.string().min(1),\n type: z.literal(\"fact\"),\n subject: z.string(),\n predicate: z.string(),\n object: z.string(),\n /** Explicit single-valued claim identity; facts sharing it are rival assertions. */\n propositionKey: z.string().min(1).nullable().default(null),\n /** The caller's `remember` idempotency key, so a retry after reindex dedupes. */\n idempotencyKey: z.string().min(1).max(200).nullable().default(null),\n confidence: z.number().min(0).max(1),\n createdAt: z.preprocess(\n (value) => (value instanceof Date ? value.toISOString() : value),\n z.string().datetime(),\n ),\n createdByAgent: z.string().nullable(),\n sourceInteractionId: z.string().nullable(),\n sourceDeleted: z.boolean(),\n category: z.enum(FACT_CATEGORIES).default(\"general\"),\n paths: z.array(z.string().min(1)).default([]),\n severity: z.enum(INCIDENT_SEVERITIES).nullable().default(null),\n introducedBy: z.string().nullable().default(null),\n resolvedBy: z.string().nullable().default(null),\n fingerprint: z\n .string()\n .regex(/^[a-f0-9]{64}$/)\n .nullable()\n .default(null),\n sourcePlanId: z.string().nullable().default(null),\n sourceSessionId: z.string().nullable().default(null),\n verification: z.array(z.string()).default([]),\n supersededBy: z.string().nullable().default(null),\n /** Namespaces granted a live reference to this fact (durable form of reference shares). */\n sharedWith: z.array(z.string().min(1)).default([]),\n});\n\nconst summaryFrontMatterSchema = z.object({\n schemaVersion: z.literal(1),\n id: z.string().regex(/^sum_[a-z0-9]+$/),\n namespace: z.string().min(1),\n type: z.literal(\"summary\"),\n sessionId: z.string().min(1),\n version: z.number().int().positive(),\n tokenCount: z.number().int().nonnegative(),\n createdAt: z.preprocess(\n (value) => (value instanceof Date ? value.toISOString() : value),\n z.string().datetime(),\n ),\n createdByAgent: z.string().nullable(),\n});\n\nexport type FactFile = z.infer<typeof factFrontMatterSchema> & {\n content: string;\n path: string;\n};\n\nexport type FactFileWrite = Omit<\n FactFile,\n | \"path\"\n | \"schemaVersion\"\n | \"type\"\n | \"category\"\n | \"paths\"\n | \"severity\"\n | \"introducedBy\"\n | \"resolvedBy\"\n | \"fingerprint\"\n | \"sourcePlanId\"\n | \"sourceSessionId\"\n | \"verification\"\n | \"supersededBy\"\n | \"sharedWith\"\n | \"propositionKey\"\n | \"idempotencyKey\"\n> &\n Partial<\n Pick<\n FactFile,\n | \"category\"\n | \"paths\"\n | \"severity\"\n | \"introducedBy\"\n | \"resolvedBy\"\n | \"fingerprint\"\n | \"sourcePlanId\"\n | \"sourceSessionId\"\n | \"verification\"\n | \"supersededBy\"\n | \"sharedWith\"\n | \"propositionKey\"\n | \"idempotencyKey\"\n >\n >;\nexport type IncidentFactFile = FactFile & {\n category: \"incident\";\n severity: IncidentSeverity;\n};\nexport type SummaryFile = z.infer<typeof summaryFrontMatterSchema> & {\n content: string;\n path: string;\n};\nexport type SummaryFileWrite = Omit<SummaryFile, \"path\" | \"schemaVersion\" | \"type\">;\n/** A managed memory file that exists on disk but could not be parsed. */\nexport type InvalidMemoryFile = { id: string; path: string; error: string };\n\n/** Files read concurrently while scanning. */\nconst SCAN_BATCH = 64;\n\n/** Parsed memory files by absolute path, valid while mtime and size match. */\nconst parsedFiles = new Map<string, { mtimeMs: number; size: number; item: { id: string } }>();\nconst loadedCaches = new Set<string>();\n\n/**\n * The parse cache also lives on disk (`.codebuddy/cache/parsed-<dir>.json`,\n * gitignored and rebuildable like the index manifest), so each short-lived\n * hook process re-parses only files that changed since any earlier process.\n * Entries are still checked against each file's mtime and size, the cache is\n * discarded when the CodeBuddy version changes (the parsed shape may differ),\n * and a missing or corrupt cache only costs a full parse.\n */\nfunction parseCachePath(root: string, directory: string): string {\n return join(\n root,\n \".codebuddy\",\n \"cache\",\n `parsed-${directory.split(sep).at(-1) ?? \"memory\"}.json`,\n );\n}\n\nasync function loadParseCache(root: string, directory: string): Promise<void> {\n if (loadedCaches.has(directory)) return;\n loadedCaches.add(directory);\n try {\n const raw = JSON.parse(await readFile(parseCachePath(root, directory), \"utf8\")) as {\n version: string;\n files: Record<string, { mtimeMs: number; size: number; item: { id: string } }>;\n };\n if (raw.version !== VERSION) return;\n for (const [name, entry] of Object.entries(raw.files)) {\n const path = join(directory, name);\n if (!parsedFiles.has(path)) parsedFiles.set(path, entry);\n }\n } catch {\n // No cache yet, or unreadable: parse from the files.\n }\n}\n\nasync function saveParseCache(root: string, directory: string): Promise<void> {\n const files: Record<string, unknown> = {};\n for (const [path, entry] of parsedFiles) {\n if (dirname(path) === directory) files[relative(directory, path)] = entry;\n }\n try {\n const path = parseCachePath(root, directory);\n await mkdir(dirname(path), { recursive: true });\n await writeAtomic(path, JSON.stringify({ version: VERSION, files }));\n } catch {\n // A cache that cannot be written only costs a re-parse next time.\n }\n}\n\nexport class MemoryFileStore {\n readonly repositoryRoot: string;\n readonly factsDirectory: string;\n readonly summariesDirectory: string;\n\n constructor(repositoryRoot = process.cwd()) {\n this.repositoryRoot = resolve(repositoryRoot);\n this.factsDirectory = join(this.repositoryRoot, \".codebuddy\", \"memory\", \"facts\");\n this.summariesDirectory = join(this.repositoryRoot, \".codebuddy\", \"memory\", \"summaries\");\n }\n\n async writeFact(raw: FactFileWrite): Promise<string> {\n // Persistence boundary: every fact writer (capture, remember, outcomes,\n // migration, resolve/supersede rewrites) is redacted here.\n const input = redactPersisted(raw);\n const path = this.factPath(input.id);\n await this.prepareDirectory(this.factsDirectory);\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(path));\n\n const metadata = {\n schemaVersion: 1,\n id: input.id,\n namespace: input.namespace,\n type: \"fact\",\n subject: input.subject,\n predicate: input.predicate,\n object: input.object,\n propositionKey: input.propositionKey,\n idempotencyKey: input.idempotencyKey,\n confidence: input.confidence,\n createdAt: input.createdAt,\n createdByAgent: input.createdByAgent,\n sourceInteractionId: input.sourceInteractionId,\n sourceDeleted: input.sourceDeleted,\n category: input.category,\n paths: input.paths,\n severity: input.severity,\n introducedBy: input.introducedBy,\n resolvedBy: input.resolvedBy,\n fingerprint: input.fingerprint,\n sourcePlanId: input.sourcePlanId,\n sourceSessionId: input.sourceSessionId,\n verification: input.verification,\n supersededBy: input.supersededBy,\n sharedWith: input.sharedWith,\n };\n const frontMatter = stringifyYaml(stripUndefinedAndDefaultIncidentFields(metadata));\n\n await writeAtomic(path, composeMarkdown(frontMatter, input.content));\n return path;\n }\n\n async writeSummary(input: SummaryFileWrite): Promise<string> {\n const path = this.summaryPath(input.id);\n await this.prepareDirectory(this.summariesDirectory);\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(path));\n const metadata = stringifyYaml({\n schemaVersion: 1,\n id: input.id,\n namespace: input.namespace,\n type: \"summary\",\n sessionId: input.sessionId,\n version: input.version,\n tokenCount: input.tokenCount,\n createdAt: input.createdAt,\n createdByAgent: input.createdByAgent,\n });\n await writeAtomic(path, composeMarkdown(metadata, input.content));\n return path;\n }\n\n async readFact(pathOrId: string): Promise<FactFile> {\n return this.readFactFile(pathOrId, false);\n }\n\n /** `trusted`: the caller (scan) already proved the path is a regular file in a checked directory. */\n private async readFactFile(pathOrId: string, trusted: boolean): Promise<FactFile> {\n const path = pathOrId.endsWith(\".md\") ? this.safePath(pathOrId) : this.factPath(pathOrId);\n if (!trusted) await assertNoSymlinkBetween(this.repositoryRoot, path);\n const parsed = parseFrontMatter(await readFile(path, \"utf8\"));\n const metadata = factFrontMatterSchema.parse(parseYaml(parsed.frontMatter));\n return {\n ...metadata,\n content: parsed.content.trim(),\n path,\n };\n }\n\n async listFacts(): Promise<FactFile[]> {\n return (await this.scanFacts()).facts;\n }\n\n /**\n * Parse every managed fact file. Hand-written markdown is ignored, and a\n * schema-invalid/corrupt fact is reported in `invalid` (not thrown) so one\n * bad file can't break recall, capture or reindexing for the whole project.\n * Invalid files stay on disk for manual repair.\n */\n async scanFacts(): Promise<{ facts: FactFile[]; invalid: InvalidMemoryFile[] }> {\n const scan = await this.scan(this.factsDirectory, /^fact_[a-z0-9]+\\.md$/, (path, trusted) =>\n this.readFactFile(path, trusted),\n );\n return { facts: scan.items, invalid: scan.invalid };\n }\n\n /**\n * List incident facts for the namespace, ranked by severity then recency.\n * Unlike {@link findIncidentFactsForPaths} this does not filter by path, so\n * callers (e.g. the context engine's project bootstrap) can surface the\n * repository's incident hotspots before any specific edit target is known.\n */\n async listIncidentFacts(\n input: { namespace?: string; includeResolved?: boolean } = {},\n ): Promise<IncidentFactFile[]> {\n const incidents = (await this.listFacts()).filter(\n (fact): fact is IncidentFactFile =>\n fact.category === \"incident\" && Boolean(fact.severity) && isEligibleMemory(fact, input),\n );\n\n return incidents.sort((left, right) => {\n const severity = severityRank(right.severity) - severityRank(left.severity);\n if (severity !== 0) return severity;\n return right.createdAt.localeCompare(left.createdAt);\n });\n }\n\n async findIncidentFactsForPaths(input: {\n namespace?: string;\n paths: string[];\n includeResolved?: boolean;\n }): Promise<IncidentFactFile[]> {\n const wanted = new Set(input.paths.map(normalizeMemoryPath));\n if (wanted.size === 0) return [];\n\n return (\n await this.listIncidentFacts({\n ...(input.namespace !== undefined ? { namespace: input.namespace } : {}),\n ...(input.includeResolved !== undefined ? { includeResolved: input.includeResolved } : {}),\n })\n ).filter((fact) => fact.paths.map(normalizeMemoryPath).some((path) => wanted.has(path)));\n }\n\n async readSummary(pathOrId: string): Promise<SummaryFile> {\n const path = pathOrId.endsWith(\".md\") ? this.safePath(pathOrId) : this.summaryPath(pathOrId);\n await assertNoSymlinkBetween(this.repositoryRoot, path);\n const parsed = parseFrontMatter(await readFile(path, \"utf8\"));\n return {\n ...summaryFrontMatterSchema.parse(parseYaml(parsed.frontMatter)),\n content: parsed.content.trim(),\n path,\n };\n }\n\n async listSummaries(): Promise<SummaryFile[]> {\n return (await this.scanSummaries()).summaries;\n }\n\n async scanSummaries(): Promise<{ summaries: SummaryFile[]; invalid: InvalidMemoryFile[] }> {\n const scan = await this.scan(this.summariesDirectory, /^sum_[a-z0-9]+\\.md$/, (path) =>\n this.readSummary(path),\n );\n return { summaries: scan.items, invalid: scan.invalid };\n }\n\n /**\n * Parse every managed file in `directory`. Hot path: the prompt hook and\n * capture scan facts several times per process, and the MCP server for its\n * lifetime, so each parsed file is cached by path, mtime and size (a changed\n * file is re-read, from any process) and files are read in parallel batches.\n * The directory chain is checked for symlinks once; a `withFileTypes` entry\n * that is a symlink is never `isFile()`, so no file escapes through one.\n */\n private async scan<T extends { id: string }>(\n directory: string,\n managedName: RegExp,\n read: (path: string, trusted: boolean) => Promise<T>,\n ): Promise<{ items: T[]; invalid: InvalidMemoryFile[] }> {\n try {\n await this.prepareDirectory(directory);\n await assertNoSymlinkBetween(this.repositoryRoot, directory);\n await loadParseCache(this.repositoryRoot, directory);\n let reparsed = false;\n const entries = (await readdir(directory, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && managedName.test(entry.name))\n .sort((left, right) => left.name.localeCompare(right.name));\n // Forget files that were deleted, so the cache never outgrows the directory.\n const live = new Set(entries.map((entry) => join(directory, entry.name)));\n for (const path of [...parsedFiles.keys()]) {\n if (dirname(path) === directory && !live.has(path)) {\n parsedFiles.delete(path);\n reparsed = true;\n }\n }\n const outcomes: Array<{ item: T } | { invalid: InvalidMemoryFile }> = [];\n for (let start = 0; start < entries.length; start += SCAN_BATCH) {\n const batch = entries.slice(start, start + SCAN_BATCH);\n outcomes.push(\n ...(await Promise.all(\n batch.map(async (entry) => {\n const path = join(directory, entry.name);\n const id = entry.name.slice(0, -\".md\".length);\n try {\n const info = await stat(path);\n const cached = parsedFiles.get(path);\n const fresh =\n cached !== undefined &&\n cached.mtimeMs === info.mtimeMs &&\n cached.size === info.size;\n const item = fresh ? (cached.item as T) : await read(path, true);\n if (!fresh) reparsed = true;\n if (item.id !== id) {\n throw new Error(`file ID ${id} differs from front matter ID ${item.id}`);\n }\n parsedFiles.set(path, { mtimeMs: info.mtimeMs, size: info.size, item });\n return { item: { ...item } };\n } catch (error) {\n parsedFiles.delete(path);\n return {\n invalid: {\n id,\n path,\n error: (error as Error).message.split(\"\\n\")[0]?.slice(0, 300) ?? \"unreadable\",\n },\n };\n }\n }),\n )),\n );\n }\n if (reparsed) await saveParseCache(this.repositoryRoot, directory);\n const result: { items: T[]; invalid: InvalidMemoryFile[] } = { items: [], invalid: [] };\n for (const outcome of outcomes) {\n if (\"item\" in outcome) result.items.push(outcome.item);\n else result.invalid.push(outcome.invalid);\n }\n return result;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { items: [], invalid: [] };\n throw error;\n }\n }\n\n async deleteFact(id: string): Promise<void> {\n await rm(this.factPath(id), { force: true });\n }\n\n async markIncidentResolved(id: string, resolvedBy: string): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { resolvedBy }));\n }\n\n async markFactSuperseded(id: string, supersededBy: string): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { supersededBy }));\n }\n\n async setFactSharedWith(id: string, sharedWith: string[]): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { sharedWith: [...new Set(sharedWith)].sort() }));\n }\n\n /** Persist interaction deletion in Markdown before the derived DB is updated. */\n async markSourceInteractionDeleted(\n namespace: string,\n sourceInteractionId: string,\n ): Promise<void> {\n const { facts } = await this.scanFacts();\n for (const fact of facts) {\n if (fact.namespace !== namespace || fact.sourceInteractionId !== sourceInteractionId)\n continue;\n if (fact.sourceDeleted) continue;\n await this.writeFact(copyFact(fact, { sourceDeleted: true }));\n }\n }\n\n /** Persist that a fact's source interaction was forgotten, keeping its provenance id. */\n async markFactSourceDeleted(id: string): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { sourceDeleted: true }));\n }\n\n private factPath(id: string): string {\n if (!/^fact_[a-z0-9]+$/.test(id)) {\n throw new Error(`Invalid fact id: ${id}`);\n }\n return this.safePath(join(this.factsDirectory, `${id}.md`));\n }\n\n private summaryPath(id: string): string {\n if (!/^sum_[a-z0-9]+$/.test(id)) throw new Error(`Invalid summary id: ${id}`);\n return this.safePath(join(this.summariesDirectory, `${id}.md`));\n }\n\n private safePath(path: string): string {\n const absolute = resolve(path);\n const relativePath = relative(this.repositoryRoot, absolute);\n if (relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || relativePath === \"\") {\n throw new Error(`Memory path escapes repository root: ${path}`);\n }\n return absolute;\n }\n\n private async prepareDirectory(directory: string): Promise<void> {\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(directory));\n await mkdir(directory, { recursive: true, mode: 0o700 });\n }\n}\n\nfunction copyFact(\n fact: FactFile,\n patch: Partial<Pick<FactFile, \"resolvedBy\" | \"supersededBy\" | \"sourceDeleted\" | \"sharedWith\">>,\n): FactFileWrite {\n const { path: _path, schemaVersion: _schemaVersion, type: _type, ...write } = fact;\n return { ...write, ...patch };\n}\n\nfunction stripUndefinedAndDefaultIncidentFields(\n metadata: Record<string, unknown>,\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(metadata).filter(([key, value]) => {\n if (value === undefined) return false;\n if (key === \"category\" && value === \"general\") return false;\n if ((key === \"paths\" || key === \"sharedWith\") && Array.isArray(value) && value.length === 0)\n return false;\n if (\n [\"severity\", \"introducedBy\", \"resolvedBy\", \"propositionKey\", \"idempotencyKey\"].includes(\n key,\n ) &&\n (value === null || value === undefined)\n ) {\n return false;\n }\n return true;\n }),\n );\n}\n\nfunction normalizeMemoryPath(path: string): string {\n return path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction severityRank(severity: IncidentSeverity): number {\n switch (severity) {\n case \"critical\":\n return 4;\n case \"high\":\n return 3;\n case \"medium\":\n return 2;\n case \"low\":\n return 1;\n }\n}\n","/** Lifecycle fields shared by Markdown facts and indexed (database) fact rows. */\nexport type MemoryLifecycle = {\n namespace?: string;\n sourceDeleted: boolean;\n supersededBy?: string | null;\n category?: string;\n resolvedBy?: string | null;\n};\n\n/**\n * The single eligibility rule for durable memory evidence. A fact is eligible\n * when it belongs to the requested namespace, its source was not deleted, and\n * it was not superseded. A resolved incident is eligible only for historical\n * inspection (`includeResolved`). File context, risk signals, and database\n * recall apply this before ranking or deduplication, so ineligible evidence\n * can never displace or suppress eligible evidence.\n */\nexport function isEligibleMemory(\n fact: MemoryLifecycle,\n options: { namespace?: string; includeResolved?: boolean } = {},\n): boolean {\n if (options.namespace && fact.namespace !== options.namespace) return false;\n if (fact.sourceDeleted || fact.supersededBy) return false;\n if (fact.category === \"incident\" && fact.resolvedBy && !options.includeResolved) return false;\n return true;\n}\n","import { sql } from \"drizzle-orm\";\nimport {\n boolean,\n customType,\n index,\n integer,\n jsonb,\n numeric,\n pgEnum,\n pgTable,\n text,\n timestamp,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\n\nexport const VECTOR_DIMENSIONS = 384;\nexport const HNSW_M = 16;\nexport const HNSW_EF_CONSTRUCTION = 64;\n\nconst vector = customType<{ data: string; driverData: string }>({\n dataType() {\n return `vector(${VECTOR_DIMENSIONS})`;\n },\n});\n\nexport const memoryTypeEnum = pgEnum(\"memory_type\", [\"interaction\", \"fact\", \"summary\"]);\nexport const interactionRoleEnum = pgEnum(\"interaction_role\", [\"user\", \"assistant\", \"system\"]);\nexport const embeddingOwnerTypeEnum = pgEnum(\"embedding_owner_type\", [\n \"interaction\",\n \"fact\",\n \"summary\",\n]);\nexport const embeddingStatusEnum = pgEnum(\"embedding_status\", [\n \"pending\",\n \"processing\",\n \"ready\",\n \"failed\",\n]);\nexport const shareModeEnum = pgEnum(\"share_mode\", [\"reference\", \"snapshot\"]);\nexport const modelCallTypeEnum = pgEnum(\"model_call_type\", [\"embedding\", \"llm\"]);\nexport const modelCallStatusEnum = pgEnum(\"model_call_status\", [\n \"success\",\n \"failed\",\n \"timeout\",\n \"rate_limited\",\n \"gated\",\n \"fallback\",\n]);\nexport const auditActionEnum = pgEnum(\"audit_action\", [\n \"remember\",\n \"remember_batch\",\n \"share\",\n \"forget\",\n \"config\",\n]);\n\nconst createdAt = timestamp(\"created_at\", { withTimezone: true }).notNull().defaultNow();\nconst updatedAt = timestamp(\"updated_at\", { withTimezone: true }).notNull().defaultNow();\n\nexport const namespaces = pgTable(\n \"namespaces\",\n {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull(),\n createdAt,\n updatedAt,\n },\n (table) => ({\n nameUnique: uniqueIndex(\"namespaces_name_unique\").on(table.name),\n }),\n);\n\nexport const interactions = pgTable(\n \"interactions\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n sessionId: text(\"session_id\").notNull(),\n role: interactionRoleEnum(\"role\").notNull().default(\"user\"),\n content: text(\"content\").notNull(),\n contentHash: text(\"content_hash\").notNull(),\n tokenCount: integer(\"token_count\").notNull().default(0),\n createdByAgent: text(\"created_by_agent\"),\n createdAt,\n },\n (table) => ({\n namespaceContentHashUnique: uniqueIndex(\"interactions_namespace_content_hash_unique\").on(\n table.namespaceId,\n table.contentHash,\n ),\n namespaceSessionCreatedIdx: index(\"interactions_namespace_session_created_idx\").on(\n table.namespaceId,\n table.sessionId,\n table.createdAt,\n ),\n }),\n);\n\nexport const sessionSummaries = pgTable(\n \"session_summaries\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n sessionId: text(\"session_id\").notNull(),\n content: text(\"content\").notNull(),\n version: integer(\"version\").notNull().default(1),\n tokenCount: integer(\"token_count\").notNull().default(0),\n createdByAgent: text(\"created_by_agent\"),\n /** See facts.fileBacked. */\n fileBacked: boolean(\"file_backed\").notNull().default(false),\n createdAt,\n },\n (table) => ({\n namespaceSessionVersionIdx: index(\"session_summaries_namespace_session_version_idx\").on(\n table.namespaceId,\n table.sessionId,\n table.version,\n ),\n }),\n);\n\nexport const facts = pgTable(\n \"facts\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n subject: text(\"subject\").notNull(),\n predicate: text(\"predicate\").notNull(),\n object: text(\"object\").notNull(),\n propositionKey: text(\"proposition_key\"),\n /** Kept apart from content_hash so a reindex (which recomputes the hash) keeps it. */\n idempotencyKey: text(\"idempotency_key\"),\n content: text(\"content\").notNull(),\n contentHash: text(\"content_hash\").notNull(),\n confidence: numeric(\"confidence\", { precision: 5, scale: 4 }).notNull().default(\"1\"),\n /**\n * Historical provenance only — deliberately not a foreign key. Interactions\n * are ephemeral DB rows; Markdown facts must re-import after a rebuild that\n * lost them, and the ID must survive so `sourceDeleted` can be tracked.\n */\n sourceInteractionId: text(\"source_interaction_id\"),\n sourceDeleted: boolean(\"source_deleted\").notNull().default(false),\n supersededBy: text(\"superseded_by\"),\n /**\n * True once a reindex imported this row from its Markdown file. Only such\n * rows are deleted when the file disappears; DB-only legacy rows are kept.\n */\n fileBacked: boolean(\"file_backed\").notNull().default(false),\n embeddingModel: text(\"embedding_model\"),\n createdByAgent: text(\"created_by_agent\"),\n createdAt,\n updatedAt,\n },\n (table) => ({\n namespaceContentHashUnique: uniqueIndex(\"facts_namespace_content_hash_unique\").on(\n table.namespaceId,\n table.contentHash,\n ),\n namespaceSubjectIdx: index(\"facts_namespace_subject_idx\").on(table.namespaceId, table.subject),\n namespaceIdempotencyIdx: index(\"facts_namespace_idempotency_idx\").on(\n table.namespaceId,\n table.idempotencyKey,\n ),\n sourceInteractionIdx: index(\"facts_source_interaction_idx\").on(table.sourceInteractionId),\n }),\n);\n\nexport const embeddings = pgTable(\n \"embeddings\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n ownerType: embeddingOwnerTypeEnum(\"owner_type\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n vector: vector(\"vector\").notNull(),\n embeddingModel: text(\"embedding_model\").notNull(),\n embeddingSpace: text(\"embedding_space\"),\n status: embeddingStatusEnum(\"status\").notNull().default(\"pending\"),\n attempts: integer(\"attempts\").notNull().default(0),\n lastError: text(\"last_error\"),\n /** Lease deadline while status='processing'; null otherwise. */\n leaseUntil: timestamp(\"lease_until\", { withTimezone: true }),\n leaseToken: text(\"lease_token\"),\n createdAt,\n updatedAt,\n },\n (table) => ({\n ownerUnique: uniqueIndex(\"embeddings_owner_unique\").on(table.ownerType, table.ownerId),\n namespaceModelStatusIdx: index(\"embeddings_namespace_model_status_idx\").on(\n table.namespaceId,\n table.embeddingModel,\n table.status,\n ),\n namespaceSpaceStatusIdx: index(\"embeddings_namespace_space_status_idx\").on(\n table.namespaceId,\n table.embeddingSpace,\n table.status,\n ),\n vectorHnswIdx: index(\"embeddings_vector_hnsw_idx\").using(\n \"hnsw\",\n sql`${table.vector} vector_cosine_ops`,\n ),\n }),\n);\n\nexport const shares = pgTable(\n \"shares\",\n {\n id: text(\"id\").primaryKey(),\n fromNamespaceId: text(\"from_namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n toNamespaceId: text(\"to_namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n sourceFactId: text(\"source_fact_id\")\n .notNull()\n .references(() => facts.id, { onDelete: \"cascade\" }),\n snapshotFactId: text(\"snapshot_fact_id\").references(() => facts.id, { onDelete: \"set null\" }),\n mode: shareModeEnum(\"mode\").notNull().default(\"reference\"),\n tombstoned: boolean(\"tombstoned\").notNull().default(false),\n createdByAgent: text(\"created_by_agent\"),\n createdAt,\n },\n (table) => ({\n shareUnique: uniqueIndex(\"shares_target_source_unique\").on(\n table.toNamespaceId,\n table.sourceFactId,\n table.mode,\n ),\n fromNamespaceIdx: index(\"shares_from_namespace_idx\").on(table.fromNamespaceId),\n }),\n);\n\nexport const checkpoints = pgTable(\n \"checkpoints\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n threadId: text(\"thread_id\").notNull(),\n checkpointId: text(\"checkpoint_id\").notNull(),\n parentId: text(\"parent_id\"),\n channelValues: jsonb(\"channel_values\").notNull().default(sql`'{}'::jsonb`),\n metadata: jsonb(\"metadata\").notNull().default(sql`'{}'::jsonb`),\n createdAt,\n },\n (table) => ({\n checkpointUnique: uniqueIndex(\"checkpoints_thread_checkpoint_unique\").on(\n table.namespaceId,\n table.threadId,\n table.checkpointId,\n ),\n }),\n);\n\nexport const modelCalls = pgTable(\n \"model_calls\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\").references(() => namespaces.id, { onDelete: \"set null\" }),\n model: text(\"model\").notNull(),\n type: modelCallTypeEnum(\"type\").notNull(),\n latencyMs: integer(\"latency_ms\"),\n tokens: integer(\"tokens\"),\n status: modelCallStatusEnum(\"status\").notNull(),\n error: text(\"error\"),\n metadata: jsonb(\"metadata\").notNull().default(sql`'{}'::jsonb`),\n createdAt,\n },\n (table) => ({\n modelCreatedIdx: index(\"model_calls_model_created_idx\").on(table.model, table.createdAt),\n namespaceCreatedIdx: index(\"model_calls_namespace_created_idx\").on(\n table.namespaceId,\n table.createdAt,\n ),\n }),\n);\n\nexport const auditLog = pgTable(\n \"audit_log\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\").references(() => namespaces.id, { onDelete: \"set null\" }),\n action: auditActionEnum(\"action\").notNull(),\n entityType: text(\"entity_type\").notNull(),\n entityId: text(\"entity_id\"),\n createdByAgent: text(\"created_by_agent\"),\n metadata: jsonb(\"metadata\").notNull().default(sql`'{}'::jsonb`),\n createdAt,\n },\n (table) => ({\n namespaceActionCreatedIdx: index(\"audit_log_namespace_action_created_idx\").on(\n table.namespaceId,\n table.action,\n table.createdAt,\n ),\n }),\n);\n","import { VECTOR_DIMENSIONS } from \"../db/schema.js\";\n\n/** Bound both DB hydration and the lexical query work independently of caller input. */\nexport function candidateLimit(limit: number): number {\n if (!Number.isFinite(limit)) return 256;\n return Math.max(1, Math.min(1024, Math.trunc(limit)));\n}\n\n/** pgvector stores float4, so values must stay finite after float4 rounding. */\nexport function validSearchVector(vector: number[]): boolean {\n return (\n vector.length === VECTOR_DIMENSIONS &&\n vector.every((value) => Number.isFinite(Math.fround(value))) &&\n vector.some((value) => value !== 0)\n );\n}\n\n/** Common prompt scaffolding should not make unrelated facts look relevant. */\nconst QUERY_STOPWORDS = new Set([\n \"a\",\n \"about\",\n \"after\",\n \"again\",\n \"all\",\n \"also\",\n \"always\",\n \"an\",\n \"and\",\n \"any\",\n \"are\",\n \"as\",\n \"at\",\n \"back\",\n \"be\",\n \"been\",\n \"before\",\n \"being\",\n \"but\",\n \"by\",\n \"can\",\n \"could\",\n \"did\",\n \"do\",\n \"does\",\n \"done\",\n \"each\",\n \"even\",\n \"ever\",\n \"for\",\n \"from\",\n \"get\",\n \"gets\",\n \"got\",\n \"had\",\n \"has\",\n \"have\",\n \"here\",\n \"how\",\n \"i\",\n \"if\",\n \"in\",\n \"into\",\n \"is\",\n \"it\",\n \"its\",\n \"just\",\n \"like\",\n \"make\",\n \"me\",\n \"more\",\n \"most\",\n \"my\",\n \"never\",\n \"no\",\n \"not\",\n \"now\",\n \"of\",\n \"on\",\n \"only\",\n \"or\",\n \"other\",\n \"our\",\n \"out\",\n \"over\",\n \"please\",\n \"really\",\n \"same\",\n \"should\",\n \"so\",\n \"some\",\n \"sometimes\",\n \"still\",\n \"such\",\n \"than\",\n \"that\",\n \"the\",\n \"their\",\n \"them\",\n \"then\",\n \"there\",\n \"these\",\n \"they\",\n \"this\",\n \"those\",\n \"through\",\n \"to\",\n \"too\",\n \"under\",\n \"up\",\n \"us\",\n \"very\",\n \"was\",\n \"we\",\n \"were\",\n \"what\",\n \"when\",\n \"where\",\n \"which\",\n \"while\",\n \"who\",\n \"why\",\n \"will\",\n \"with\",\n \"would\",\n \"yet\",\n \"you\",\n \"your\",\n]);\n\n/** Literal, case-insensitive terms; no SQL wildcard or query-language syntax. */\nexport function lexicalTerms(query: string): string[] {\n return [\n ...new Set(\n query\n .slice(0, 4096)\n .toLowerCase()\n .match(/[\\p{L}\\p{N}_]+/gu) ?? [],\n ),\n ]\n .filter((term) => !QUERY_STOPWORDS.has(term))\n .slice(0, 32)\n .map((term) => term.slice(0, 64));\n}\n\n/**\n * Everyday coding words. Shared alone they do not say which fact a prompt is\n * about; in a small fact corpus they would otherwise look rare.\n */\nconst GENERIC_TERMS = new Set(\n (\n \"add added adding bug build call change changed changes check code config configure configuration \" +\n \"create data default delete error errors feature file files fix fixed handle issue line load logic \" +\n \"new option options problem project read remove result return run running save set setting settings \" +\n \"show step support test tests thing time update updated use used value values version want way work \" +\n \"works write\"\n ).split(\" \"),\n);\n/** A lone shared term admits a fact only if at most this share of the facts hold it… */\nconst RARE_TERM_SHARE = 0.005;\n/** …or at most this many (so small corpora are not held to a fraction of one fact). */\nconst RARE_TERM_MIN_DF = 2;\n\n/**\n * Lexical admission for per-prompt fact recall (#9): at least two distinct\n * non-generic shared terms, or one that is rare across the fact corpus\n * (document frequency ≤ max(2, 0.5% of facts)) and not generic. One common\n * word in common (\"session\" in an unrelated deploy prompt) is a coincidence.\n */\nexport function admitsLexically(\n shared: string[],\n documentFrequency: Map<string, number>,\n corpusSize: number,\n): boolean {\n const specific = shared.filter((term) => !GENERIC_TERMS.has(term));\n if (specific.length >= 2) return true;\n const rare = Math.max(RARE_TERM_MIN_DF, Math.floor(RARE_TERM_SHARE * corpusSize));\n return specific.some((term) => (documentFrequency.get(term) ?? 0) <= rare);\n}\n\n/** Words every captured decision shares (\"we decided to use …\"); they carry no claim. */\nconst CLAIM_BOILERPLATE = new Set([\n \"decided\",\n \"decide\",\n \"decision\",\n \"made\",\n \"use\",\n \"uses\",\n \"used\",\n \"using\",\n \"now\",\n \"going\",\n \"will\",\n]);\n\n/**\n * Jaccard overlap of two texts' content words (stopwords dropped), 0..1.\n * Used to spot near-duplicate facts: at capture (so a reworded decision is not\n * saved twice) and at recall (so two copies never take two of the few slots).\n */\nexport function termOverlap(a: string, b: string): number {\n const terms = (text: string) =>\n new Set(\n (text.toLowerCase().match(/[\\p{L}\\p{N}_]+/gu) ?? []).filter(\n (term) => !QUERY_STOPWORDS.has(term) && !CLAIM_BOILERPLATE.has(term),\n ),\n );\n const left = terms(a);\n const right = terms(b);\n if (left.size === 0 || right.size === 0) return 0;\n let shared = 0;\n for (const term of left) if (right.has(term)) shared += 1;\n return shared / (left.size + right.size - shared);\n}\n\n/** Score complete-word evidence, favoring explicit fact fields over free text. */\nexport function lexicalFactScore(\n fact: { subject: string; object: string; content: string },\n terms: string[],\n): number {\n if (terms.length === 0) return 0;\n const structured = new Set(\n `${fact.subject} ${fact.object}`.toLowerCase().match(/[\\p{L}\\p{N}_]+/gu) ?? [],\n );\n const prose = new Set(fact.content.toLowerCase().match(/[\\p{L}\\p{N}_]+/gu) ?? []);\n const points = terms.reduce(\n (total, term) => total + (structured.has(term) ? 2 : 0) + (prose.has(term) ? 1 : 0),\n 0,\n );\n return points / (3 * terms.length);\n}\n\nexport function cosine(a: number[], b: number[]): number {\n if (a.length !== b.length) return 0;\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < a.length; i++) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dot += av * bv;\n na += av * av;\n nb += bv * bv;\n }\n const denom = Math.sqrt(na) * Math.sqrt(nb);\n return denom === 0 ? 0 : dot / denom;\n}\n"],"mappings":"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","names":["facts","vector","text"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/integrations/graphify.ts","../src/map/graph-freshness.ts","../src/context/file-vectors.ts","../src/context/discovery.ts","../src/map/graphify.ts","../src/context/anchors.ts","../src/context/content-index.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { mkdir, mkdtemp, readFile, rm, stat, utimes } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { loadFreshIndex } from \"../indexer/engine.js\";\nimport { writeGraphSourceSnapshot } from \"../map/graph-freshness.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type GraphifyCommandRunner = (\n command: string,\n args: string[],\n cwd: string,\n) => Promise<{ stdout: string; stderr: string }>;\n\nexport type GraphifySetupOptions = {\n repositoryRoot: string;\n run?: GraphifyCommandRunner;\n};\n\nexport type GraphifySetupResult = {\n available: boolean;\n installed: boolean;\n generated: boolean;\n graphPath: string;\n message: string;\n};\n\nexport type GenerateGraphifyOptions = {\n repositoryRoot: string;\n graphPath?: string;\n run?: GraphifyCommandRunner;\n};\n\nexport type GenerateGraphifyResult = {\n graphPath: string;\n generated: boolean;\n nodes: number;\n edges: number;\n};\n\nconst DEFAULT_GRAPH_PATH = \"graphify-out/graph.json\";\n\n/** Detect, install, and register Graphify without making it a CodeBuddy dependency. */\nexport async function setupGraphify(options: GraphifySetupOptions): Promise<GraphifySetupResult> {\n const run = options.run ?? runCommand;\n let available = await commandWorks(run, \"graphify\", [\"--version\"], options.repositoryRoot);\n let installed = false;\n\n if (!available) {\n const installers = [\n [\"uv\", [\"tool\", \"install\", \"graphifyy\"]],\n [\"pipx\", [\"install\", \"graphifyy\"]],\n [\"python\", [\"-m\", \"pip\", \"install\", \"--user\", \"graphifyy\"]],\n ] as const;\n for (const [command, args] of installers) {\n try {\n await run(command, [...args], options.repositoryRoot);\n available = await commandWorks(run, \"graphify\", [\"--version\"], options.repositoryRoot);\n if (available) {\n installed = true;\n break;\n }\n } catch {\n // Try the next supported local installer.\n }\n }\n }\n\n if (!available) {\n return {\n available: false,\n installed,\n generated: false,\n graphPath: DEFAULT_GRAPH_PATH,\n message:\n \"Graphify was selected but is not available. Install graphifyy with uv/pipx, then run `codebuddy graphify index`.\",\n };\n }\n\n try {\n await run(\"graphify\", [\"install\"], options.repositoryRoot).catch(() => undefined);\n const graph = await generateGraphifyGraph({ repositoryRoot: options.repositoryRoot, run });\n return {\n available: true,\n installed,\n generated: true,\n graphPath: graph.graphPath,\n message: `Graphify graph generated (${graph.nodes} nodes, ${graph.edges} edges).`,\n };\n } catch (error) {\n return {\n available: true,\n installed,\n generated: false,\n graphPath: DEFAULT_GRAPH_PATH,\n message: `Graphify is installed but its graph could not be generated: ${(error as Error).message}`,\n };\n }\n}\n\nasync function commandWorks(\n run: GraphifyCommandRunner,\n command: string,\n args: string[],\n cwd: string,\n): Promise<boolean> {\n try {\n await run(command, args, cwd);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function runCommand(\n command: string,\n args: string[],\n cwd: string,\n): Promise<{ stdout: string; stderr: string }> {\n return execFileAsync(command, args, { cwd });\n}\n\nexport async function graphifyGraphExists(\n repositoryRoot: string,\n graphPath = DEFAULT_GRAPH_PATH,\n): Promise<boolean> {\n try {\n const parsed = await readGraphFile(resolveGraphPath(repositoryRoot, graphPath));\n return parsed.nodes > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Build a graph with Graphify itself. Staging is used for first builds and\n * invalid graphs so a failed extractor can never replace a usable graph.\n */\nexport async function generateGraphifyGraph(\n options: GenerateGraphifyOptions,\n): Promise<GenerateGraphifyResult> {\n const run = options.run ?? runCommand;\n const repositoryRoot = resolve(options.repositoryRoot);\n const graphPath = options.graphPath ?? DEFAULT_GRAPH_PATH;\n const destination = resolveGraphPath(repositoryRoot, graphPath);\n await mkdir(dirname(destination), { recursive: true });\n await ignoreGeneratedGraphDirectory(dirname(destination));\n\n const { manifest: sourceSnapshot } = await loadFreshIndex(repositoryRoot);\n const current = await readGraphFileIfPresent(destination);\n const isDefaultPath = graphPath === DEFAULT_GRAPH_PATH;\n if (current && isDefaultPath) {\n const backup = await readFile(destination, \"utf8\");\n const backupStat = await stat(destination);\n try {\n await run(\"graphify\", [\"update\", repositoryRoot, \"--force\", \"--no-cluster\"], repositoryRoot);\n const updated = await readGraphFile(destination);\n await writeGraphSourceSnapshot(destination, sourceSnapshot);\n return { graphPath, generated: true, ...updated };\n } catch (error) {\n await writeAtomic(destination, backup);\n await utimes(destination, backupStat.atime, backupStat.mtime);\n throw new Error(\n `Graphify update failed; the previous graph was preserved: ${(error as Error).message}`,\n );\n }\n }\n\n const stagingRoot = await mkdtemp(join(tmpdir(), \"codebuddy-graphify-\"));\n try {\n await run(\n \"graphify\",\n [\"extract\", repositoryRoot, \"--out\", stagingRoot, \"--code-only\", \"--no-cluster\"],\n repositoryRoot,\n );\n const stagedPath = join(stagingRoot, DEFAULT_GRAPH_PATH);\n const staged = await readGraphFile(stagedPath);\n await writeAtomic(destination, await readFile(stagedPath, \"utf8\"));\n await writeGraphSourceSnapshot(destination, sourceSnapshot);\n return { graphPath, generated: true, ...staged };\n } finally {\n await rm(stagingRoot, { recursive: true, force: true });\n }\n}\n\n/**\n * The graph directory is derived output CodeBuddy writes itself, so it is\n * self-ignoring like `.codebuddy/`. An existing .gitignore is never rewritten:\n * a project that deliberately commits its graph keeps doing so.\n */\nasync function ignoreGeneratedGraphDirectory(directory: string): Promise<void> {\n const path = join(directory, \".gitignore\");\n try {\n await stat(path);\n } catch {\n await writeAtomic(path, \"# CodeBuddy-generated Graphify output (derived, rebuildable)\\n*\\n\");\n }\n}\n\nfunction resolveGraphPath(repositoryRoot: string, graphPath: string): string {\n const absolute = resolve(repositoryRoot, graphPath);\n const relativePath = relative(repositoryRoot, absolute);\n if (relativePath === \"\" || relativePath === \"..\" || relativePath.startsWith(`..${sep}`)) {\n throw new Error(`Graph path must be inside the project root: ${graphPath}`);\n }\n return absolute;\n}\n\nasync function readGraphFileIfPresent(\n path: string,\n): Promise<{ nodes: number; edges: number } | null> {\n try {\n return await readGraphFile(path);\n } catch {\n return null;\n }\n}\n\nasync function readGraphFile(path: string): Promise<{ nodes: number; edges: number }> {\n const info = await stat(path);\n if (!info.isFile() || info.size === 0)\n throw new Error(`Graphify graph is missing or empty: ${path}`);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n const nodes = arrayLength(raw.nodes ?? raw.entities);\n const edges = arrayLength(raw.edges ?? raw.links ?? raw.relationships);\n if (nodes === 0) throw new Error(`Graphify graph contains no nodes: ${path}`);\n return { nodes, edges };\n}\n\nfunction arrayLength(value: unknown): number {\n return Array.isArray(value) ? value.length : 0;\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport type { IndexManifest } from \"../indexer/types.js\";\n\nconst digest = (value: string) => createHash(\"sha256\").update(value).digest(\"hex\");\n\n/** Record the source snapshot from BEFORE extraction, so edits during extraction fail closed. */\nexport async function writeGraphSourceSnapshot(\n path: string,\n manifest: IndexManifest,\n): Promise<void> {\n await writeAtomic(\n `${path}.sources.json`,\n JSON.stringify({\n version: 1,\n graphHash: digest(await readFile(path, \"utf8\")),\n sources: Object.fromEntries(\n Object.values(manifest.entries).map((entry) => [entry.path, entry.hash]),\n ),\n }),\n );\n}\n\n/** Missing = legacy graph. Invalid/mismatched = trust no endpoints until rebuilt. */\nexport async function readGraphSourceSnapshot(\n path: string,\n): Promise<Record<string, string> | null> {\n let raw: string;\n try {\n raw = await readFile(`${path}.sources.json`, \"utf8\");\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"ENOENT\" ? null : {};\n }\n try {\n const snapshot = JSON.parse(raw);\n if (\n snapshot.version !== 1 ||\n snapshot.graphHash !== digest(await readFile(path, \"utf8\")) ||\n !snapshot.sources ||\n typeof snapshot.sources !== \"object\" ||\n Array.isArray(snapshot.sources)\n )\n return {};\n return Object.fromEntries(\n Object.entries(snapshot.sources).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n ),\n );\n } catch {\n return {};\n }\n}\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { loadFreshIndex } from \"../indexer/engine.js\";\nimport type { IndexManifest } from \"../indexer/types.js\";\nimport { LOCAL_EMBEDDING_MODEL } from \"../providers/models.js\";\nimport { NON_SOURCE_PATH, readBoundedSource, TEST_PATH } from \"./discovery.js\";\n\n/**\n * Dense file vectors for task → file ordering: the local embedder (granite)\n * over 40-line chunks of each source file, cached by content hash in\n * `.codebuddy/cache/file-vectors.json` (gitignored, rebuildable).\n *\n * Built only in the background (`hooks sync`, `codebuddy index`), never while\n * a prompt waits; a prompt only embeds its own text. Keyword evidence decides\n * whether a task concerns this repository at all, vectors then pick the\n * files: on their own they cannot tell an unrelated task (best chunk ≤ 0.81\n * on the labelled sets) from a reworded related one (as low as 0.79), but for\n * admitted tasks they find the right files more often than keywords. Measured with granite: on dsicord-bot's 40\n * labelled tasks top-1 went from 45% to 55% and top-3 from 60% to 71%; on\n * evals/task-savings.json retrieval recall from 0.741 to 0.815, with every\n * unrelated task still silent. A prompt pays ~170 ms to load the model when\n * nothing else in the hook loaded it (a project with no stored facts).\n */\n\ntype FileVectors = { hash: string; vectors: string[] };\ntype Store = { version: 1; model: string; files: Record<string, FileVectors> };\n\nconst CHUNK_LINES = 40;\nconst CHUNK_STEP = 30;\nconst CHUNK_CHARS = 2_000;\nconst BATCH = 16;\nconst MODEL = `${LOCAL_EMBEDDING_MODEL.id}@${LOCAL_EMBEDDING_MODEL.revision}`;\n\nexport type FileVectorRefresh = { embedded: number; files: number; pending: number };\n/** Texts in, L2-normalized vectors out (tests pass a stub; default: the local model). */\nexport type Embed = (texts: string[]) => Promise<number[][]>;\n\nasync function localEmbed(texts: string[]): Promise<number[][]> {\n const { LocalModelProvider } = await import(\"../providers/local.js\");\n return (await new LocalModelProvider().embed({ input: texts })).vectors;\n}\n\n/**\n * Embed the chunks of every source file whose hash changed since the last\n * run, until `deadlineAt`; what is left waits for the next run. Saves after\n * each batch group so an interrupted run keeps its work.\n */\nexport async function refreshFileVectors(\n repositoryRoot: string,\n manifest: IndexManifest,\n deadlineAt: number,\n embed: Embed = localEmbed,\n): Promise<FileVectorRefresh> {\n const store = await loadStore(repositoryRoot);\n const eligible = eligiblePaths(manifest);\n const files: Record<string, FileVectors> = {};\n const todo: string[] = [];\n for (const path of eligible) {\n const cached = store.files[path];\n if (cached && cached.hash === manifest.entries[path]?.hash) files[path] = cached;\n else todo.push(path);\n }\n let embedded = 0;\n let lastSave = Date.now();\n for (const path of todo) {\n if (Date.now() >= deadlineAt) break;\n const source = await readBoundedSource(repositoryRoot, path);\n if (source === null) continue;\n const vectors: string[] = [];\n const chunks = chunk(path, source);\n for (let start = 0; start < chunks.length; start += BATCH) {\n for (const vector of await embed(chunks.slice(start, start + BATCH))) {\n vectors.push(encode(vector));\n }\n }\n files[path] = { hash: manifest.entries[path]?.hash ?? \"\", vectors };\n embedded += 1;\n if (Date.now() - lastSave > 30_000) {\n await saveStore(repositoryRoot, { version: 1, model: MODEL, files });\n lastSave = Date.now();\n }\n }\n await saveStore(repositoryRoot, { version: 1, model: MODEL, files });\n return {\n embedded,\n files: Object.keys(files).length,\n pending: eligible.length - Object.keys(files).length,\n };\n}\n\n/** Refresh the index, then the vectors of what changed. Null when the embedder cannot load. */\nexport async function syncFileVectors(\n repositoryRoot: string,\n budgetMs: number,\n): Promise<FileVectorRefresh | null> {\n const started = Date.now();\n try {\n const { manifest } = await loadFreshIndex(repositoryRoot);\n return await refreshFileVectors(repositoryRoot, manifest, started + budgetMs);\n } catch {\n return null;\n }\n}\n\n/**\n * Indexed source files by their best chunk's cosine to the task, best first.\n * Null when there are no vectors (never built, another model, semantic off)\n * or the embedder cannot load: callers keep the keyword order.\n */\nexport async function rankFilesByVector(\n repositoryRoot: string,\n manifest: IndexManifest,\n task: string,\n embed: Embed = localEmbed,\n): Promise<Array<{ path: string; score: number }> | null> {\n if (process.env.CODEBUDDY_SEMANTIC === \"0\") return null;\n const store = await loadStore(repositoryRoot);\n const current = eligiblePaths(manifest).filter(\n (path) => store.files[path]?.hash === manifest.entries[path]?.hash,\n );\n if (current.length === 0) return null;\n let query: number[];\n try {\n const [vector] = await embed([task]);\n if (!vector) return null;\n query = vector;\n } catch {\n return null;\n }\n return current\n .map((path) => ({\n path,\n score: Math.max(...(store.files[path]?.vectors ?? []).map((v) => dot(decode(v), query))),\n }))\n .sort((left, right) => right.score - left.score || left.path.localeCompare(right.path));\n}\n\nfunction eligiblePaths(manifest: IndexManifest): string[] {\n return Object.keys(manifest.entries).filter(\n (path) => !NON_SOURCE_PATH.test(path) && !TEST_PATH.test(path),\n );\n}\n\n/** The path heads every chunk, so a file's name counts toward each of its passages. */\nfunction chunk(path: string, source: string): string[] {\n const lines = source.split(\"\\n\");\n const chunks: string[] = [];\n for (let start = 0; start === 0 || start + 10 < lines.length; start += CHUNK_STEP) {\n // Capped: one minified or data line would otherwise make the whole batch's\n // attention tensor too large for ONNX Runtime.\n chunks.push(\n `${path}\\n${lines.slice(start, start + CHUNK_LINES).join(\"\\n\")}`.slice(0, CHUNK_CHARS),\n );\n }\n return chunks;\n}\n\nfunction encode(vector: number[]): string {\n return Buffer.from(Float32Array.from(vector).buffer).toString(\"base64\");\n}\n\nfunction decode(text: string): Float32Array {\n const bytes = Buffer.from(text, \"base64\");\n return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);\n}\n\nfunction dot(left: Float32Array, right: number[]): number {\n let sum = 0;\n for (let index = 0; index < right.length; index++)\n sum += (left[index] ?? 0) * (right[index] ?? 0);\n return sum;\n}\n\nfunction storePath(repositoryRoot: string): string {\n return join(repositoryRoot, \".codebuddy\", \"cache\", \"file-vectors.json\");\n}\n\nasync function loadStore(repositoryRoot: string): Promise<Store> {\n try {\n const parsed = JSON.parse(await readFile(storePath(repositoryRoot), \"utf8\")) as Store;\n if (parsed.version === 1 && parsed.model === MODEL && parsed.files) return parsed;\n } catch {\n // Missing or corrupt: nothing to rank by until the next refresh.\n }\n return { version: 1, model: MODEL, files: {} };\n}\n\nasync function saveStore(repositoryRoot: string, store: Store): Promise<void> {\n const path = storePath(repositoryRoot);\n await mkdir(dirname(path), { recursive: true });\n await writeAtomic(path, JSON.stringify(store));\n}\n","import { readFile, realpath, stat } from \"node:fs/promises\";\nimport { isAbsolute, join, relative } from \"node:path\";\nimport { readConfigFile } from \"../core/config-file.js\";\nimport { isCredentialPath } from \"../core/sensitive.js\";\nimport type { IndexManifest } from \"../indexer/types.js\";\nimport { graphifyGraphExists } from \"../integrations/graphify.js\";\nimport { loadArchitectureMap } from \"../map/cache.js\";\nimport { readGraphSourceSnapshot } from \"../map/graph-freshness.js\";\nimport { loadGraphifyMap } from \"../map/graphify.js\";\nimport type { ArchitectureMap } from \"../map/types.js\";\nimport type { RetrievalHit, ZvecCandidates } from \"../retrieval/zvec.js\";\nimport { extractTaskAnchors } from \"./anchors.js\";\nimport { idfCoverage, rankContent } from \"./content-index.js\";\nimport { rankFilesByVector } from \"./file-vectors.js\";\n\n/**\n * Task-path discovery: rank indexed files against a natural-language task,\n * with calibrated abstention. Kept apart from the context engine so the lean\n * hook can use it without loading plan, risk, worklog or memory code.\n */\n\n/** Most files the engine describes (symbols, excerpts) for one request. */\nexport const TASK_PATH_LIMIT = 10;\n/** Task-discovered targets: a wrong file costs the agent a read, a missing one costs nothing. */\nconst TASK_FILES = 3;\n/** Multi-file expansion: neighbours of the top `seeds` targets, at most `files`. */\nconst EXPANSION = { seeds: 2, files: 1 } as const;\n\nconst MAX_EVIDENCE_BYTES = 512_000;\n\n/** Current file text, or null when it is missing, oversized, or binary. */\nexport async function readBoundedSource(\n repositoryRoot: string,\n path: string,\n): Promise<string | null> {\n // Credential files are never read as evidence: not ranked, indexed or shown.\n if (isCredentialPath(path)) return null;\n try {\n const absolute = join(repositoryRoot, path);\n if ((await stat(absolute)).size > MAX_EVIDENCE_BYTES) return null;\n const source = await readFile(absolute, \"utf8\");\n return source.includes(\"\\0\") ? null : source;\n } catch {\n return null;\n }\n}\n\n/**\n * Rank cached file summaries for a task when the agent has not named files.\n * Explicit anchors in the task (paths, `path:line`, stack frames, quoted\n * error strings) come first; ranking fills the remaining slots.\n */\nexport async function discoverTaskPaths(\n repositoryRoot: string,\n task: string,\n manifest: IndexManifest,\n mapSource: Promise<ArchitectureMap>,\n options: DiscoveryOptions = {},\n): Promise<DiscoveredPaths> {\n const anchors = await extractTaskAnchors(\n repositoryRoot,\n task,\n new Set(Object.keys(manifest.entries)),\n );\n if (anchors.length === 0)\n return rankTaskFiles(repositoryRoot, task, manifest, mapSource, options);\n const anchored = anchors.map((anchor) => anchor.path);\n const hits: RetrievalHit[] = anchors.flatMap((anchor) =>\n anchor.line === null\n ? []\n : [\n {\n path: anchor.path,\n startLine: anchor.line,\n endLine: anchor.line,\n rank: 0,\n source: \"anchor\" as const,\n freshness: \"unverified\" as const,\n },\n ],\n );\n const explain = `retrieval: anchors (${anchors\n .map(\n (anchor) => `${anchor.kind} ${anchor.path}${anchor.line === null ? \"\" : `:${anchor.line}`}`,\n )\n .join(\", \")})`;\n if (anchored.length >= TASK_FILES) return { paths: anchored, hits, explain: [explain] };\n const ranked = await rankTaskFiles(repositoryRoot, task, manifest, mapSource, options);\n return {\n ...(ranked.symbols ? { symbols: ranked.symbols } : {}),\n paths: [...anchored, ...ranked.paths.filter((path) => !anchored.includes(path))].slice(\n 0,\n TASK_FILES,\n ),\n hits: [...hits, ...ranked.hits.filter((hit) => !anchored.includes(hit.path))],\n explain: [explain, ...ranked.explain],\n };\n}\n\n/**\n * Index entries with their BYOK model summaries attached as `llmText` (when\n * the layer and its summaries feature are on). Loaded on demand so a repo\n * without the layer never imports it.\n */\nasync function withLlmSummaries(\n repositoryRoot: string,\n manifest: IndexManifest,\n): Promise<Array<IndexManifest[\"entries\"][string] & { llmText?: string }>> {\n const entries = Object.values(manifest.entries);\n try {\n const { resolveLlmConfig } = await import(\"../llm/config.js\");\n const config = await resolveLlmConfig(repositoryRoot);\n if (!config.enabled || !config.features.summaries) return entries;\n const { loadLlmSummaries } = await import(\"../llm/summaries.js\");\n const summaries = await loadLlmSummaries(repositoryRoot, manifest);\n if (summaries.size === 0) return entries;\n return entries.map((entry) => {\n const summary = summaries.get(entry.path);\n return summary\n ? { ...entry, llmText: `${summary.summary} ${Object.values(summary.symbols).join(\" \")}` }\n : entry;\n });\n } catch {\n return entries;\n }\n}\n\nexport type DiscoveryOptions = {\n semantic?: boolean;\n /**\n * Epoch ms by which the BYOK rerank must have answered (the caller's hook\n * budget). The call gets min(configured timeout, time left) measured right\n * before it is made; under 200 ms left it is skipped. Default: no deadline\n * beyond the configured timeout.\n */\n llmDeadlineAt?: number;\n /**\n * With `semantic: false`, still ask zvec-grep (when enabled) but only while a\n * `zg server` is running, every zg call killed at this epoch ms. Lets the lean\n * hook use zvec without the local embedding model or an unbounded probe.\n */\n zvecDeadlineAt?: number;\n};\n\nexport type DiscoveredPaths = {\n paths: string[];\n hits: RetrievalHit[];\n explain: string[];\n /** Symbols the BYOK rerank named per file, validated against the symbol table. */\n symbols?: Record<string, string[]>;\n};\n\nasync function rankTaskFiles(\n repositoryRoot: string,\n task: string,\n manifest: IndexManifest,\n mapSource: Promise<ArchitectureMap>,\n options: DiscoveryOptions,\n): Promise<DiscoveredPaths> {\n // Changelogs restate every feature's wording; they are never a task's target\n // unless the task is about the changelog itself.\n const wantsChangelog = asksForChangelog(task);\n const entries = (await withLlmSummaries(repositoryRoot, manifest)).filter(\n (entry) => wantsChangelog || !CHANGELOG_PATH.test(entry.path),\n );\n const wantsTests = /\\b(tests?|spec)\\b/i.test(task);\n // An exact code identifier names its file better than any embedding: route\n // it lexically to the declaring files and skip vector noise.\n const identifiers = identifierTokens(task).filter((token) => CODE_IDENTIFIER.test(token));\n if (identifiers.length > 0) {\n // Files declaring more of the named identifiers first.\n const exact = entries\n .filter((entry) => wantsTests || !TEST_PATH.test(entry.path))\n .map((entry) => ({\n path: entry.path,\n named: new Set(\n (entry.symbolTable ?? []).flatMap((symbol) =>\n // A doc heading naming an identifier is not its declaration.\n symbol.kind === \"heading\"\n ? []\n : symbol.name.split(\".\").filter((segment) => identifiers.includes(segment)),\n ),\n ).size,\n }))\n .filter((entry) => entry.named > 0)\n .sort((left, right) => right.named - left.named || left.path.localeCompare(right.path))\n .map((entry) => entry.path);\n if (exact.length > 0) {\n const evidence = new Map(\n scoreTaskPaths(entries, task)\n .filter((entry) => entry.score > 0)\n .map((entry) => [entry.path, entry.score]),\n );\n const expansion = expandAlongImports(exact, await mapSource, evidence, task, {\n allowGraphOnly: true,\n });\n return {\n paths: expansion.paths.slice(0, TASK_FILES),\n hits: [],\n explain: [\n `retrieval: exact-symbol routing (${identifiers.slice(0, 5).join(\", \")})`,\n ...(expansion.explain ? [expansion.explain] : []),\n ],\n };\n }\n }\n // A named CLI command (`<bin> <command>`) routes to its command module,\n // ahead of keyword ranking; the logic it calls still comes from keywords.\n const commands = await commandFiles(repositoryRoot, task, entries);\n const scored = scoreTaskPaths(entries, task);\n const summaryKeyword = scored\n .filter((entry) => entry.score >= EVIDENCE_FLOOR.keywordScore)\n .map((entry) => entry.path);\n // Full-text evidence (words inside function bodies): already gated, so it\n // admits files the summary floor misses and votes in the order of both.\n const { matches: content, idf } = await rankContent(\n repositoryRoot,\n manifest,\n taskTerms(task),\n wantsTests,\n DOCS_TASK.test(task),\n wantsChangelog,\n );\n const keyword =\n content.length > 0\n ? fuseRankings([summaryKeyword, content.map((match) => match.path)])\n : summaryKeyword;\n const weakKeyword = scored.length - keyword.length;\n // Loaded on demand so callers that opt out (the lean hook) never import it.\n const semantic: ZvecCandidates =\n options.semantic === false && options.zvecDeadlineAt === undefined\n ? {\n status: \"disabled\",\n paths: [],\n hits: [],\n reason: \"semantic retrieval off\",\n stale: 0,\n changed: 0,\n }\n : await import(\"../retrieval/zvec.js\")\n .then(({ zvecCandidates }) =>\n zvecCandidates(\n repositoryRoot,\n task,\n options.semantic === false && options.zvecDeadlineAt !== undefined\n ? { deadlineAt: options.zvecDeadlineAt, requireServer: true }\n : {},\n ),\n )\n .catch(\n (): ZvecCandidates => ({\n status: \"failed\",\n paths: [],\n hits: [],\n reason: \"zvec-grep failed\",\n stale: 0,\n changed: 0,\n }),\n );\n // Lexical evidence for expansion: any task term, even below the floor.\n const evidence = new Map(\n scored.filter((entry) => entry.score > 0).map((entry) => [entry.path, entry.score]),\n );\n let ranked = keyword;\n let hits: RetrievalHit[] = [];\n const explain: string[] = [];\n if (semantic.status !== \"disabled\" && semantic.paths.length === 0) {\n explain.push(`retrieval: keyword ranking only (${semantic.reason})`);\n } else if (semantic.status !== \"disabled\") {\n // Keep zvec hits consistent with keyword rules: no tests unless asked for.\n // zg 0.2.x reports a rank, not a calibrated score, so a vector hit below the\n // keyword floor must share enough task terms with its current source.\n const terms = taskTerms(task);\n const vector: string[] = [];\n const partial: string[] = [];\n let uncorroborated = 0;\n let belowFloor = 0;\n // zg's own score is rank-derived (RRF), not a similarity, so the floor is on\n // the hit's current source: the content gate's task IDF coverage.\n const minScore = semantic.minScore ?? 0;\n for (const path of semantic.paths) {\n if (!wantsTests && TEST_PATH.test(path)) continue;\n if (!wantsChangelog && CHANGELOG_PATH.test(path)) continue;\n if (keyword.includes(path)) vector.push(path);\n else if (\n await hasMatchedPassageEvidence(\n repositoryRoot,\n path,\n semantic.hits,\n terms,\n manifest.entries[path]?.symbolTable ?? [],\n )\n ) {\n const source = (await readBoundedSource(repositoryRoot, path)) ?? \"\";\n // The path counts too, as it does in the BM25 counts the IDF table comes from.\n if (idfCoverage(`${path} ${source}`, terms, idf) < minScore) {\n belowFloor++;\n continue;\n }\n if (countTerms(source, terms) >= EVIDENCE_FLOOR.vectorTerms) vector.push(path);\n else partial.push(path);\n if (!evidence.has(path)) evidence.set(path, 0);\n } else uncorroborated++;\n }\n // The floor admits files; it does not reorder them. A weak keyword match\n // still votes in fusion when its vector hit was corroborated.\n ranked = rankEvidenceCandidates(\n scored.map((entry) => entry.path),\n keyword,\n vector,\n partial,\n );\n hits = semantic.hits.filter((hit) => vector.includes(hit.path) || partial.includes(hit.path));\n explain.push(\n `retrieval: keyword ranking fused with ${semantic.reason}` +\n (uncorroborated > 0 ? `; ${uncorroborated} uncorroborated vector file(s) dropped` : \"\"),\n );\n if (belowFloor > 0) {\n explain.push(\n `abstention: ${belowFloor} vector-only file(s) below zvec.minScore ${minScore} (task IDF coverage of current source) dropped`,\n );\n }\n }\n\n let symbols: Record<string, string[]> | undefined;\n const reranked = await llmRerank({\n repositoryRoot,\n task,\n manifest,\n entries,\n scored,\n semantic: semantic.paths,\n ranked,\n wantsTests,\n deadlineAt: options.llmDeadlineAt,\n });\n if (reranked) {\n explain.push(reranked.explain);\n ranked = reranked.ranked;\n symbols = reranked.symbols;\n }\n\n // Calibrated abstention (#45): return nothing rather than code when no\n // *source* file reaches the evidence floor; docs and config alone (an eval\n // dataset quoting the prompt, say) never justify sending code.\n const wantsDocs = DOCS_TASK.test(task);\n if (!ranked.some((path) => countsAsSource(path, task))) {\n explain.push(\n \"abstention: no source file reached the evidence floor (\" +\n (ranked.length > 0\n ? \"only docs/config did\"\n : `best keyword score ${scored[0]?.score ?? 0} < ${EVIDENCE_FLOOR.keywordScore}`) +\n (commands.length > 0\n ? \"); only the named CLI command returned\"\n : \"); no task targets returned\"),\n );\n return { paths: commands, hits: [], explain };\n }\n if (weakKeyword > 0) {\n explain.push(\n `abstention: ${weakKeyword} weak keyword candidate(s) below the evidence floor (score < ${EVIDENCE_FLOOR.keywordScore}) dropped`,\n );\n }\n // Keyword evidence decided the task is about this repository; built file\n // vectors pick which files (see file-vectors.ts for the measurements).\n const vectors =\n options.semantic === false || reranked\n ? null\n : await rankFilesByVector(repositoryRoot, manifest, task);\n if (vectors && vectors.length > 0) {\n // Vectors cover source only; a docs task keeps its best-ranked doc first.\n const doc = wantsDocs ? ranked.filter((path) => DOC_PATH.test(path)).slice(0, 1) : [];\n ranked = [...doc, ...vectors.map((match) => match.path)].slice(0, TASK_FILES);\n explain.push(\n `retrieval: file vectors ordered the files (best ${vectors[0]?.score.toFixed(2)})`,\n );\n }\n const expansion = expandAlongImports(ranked, await mapSource, evidence, task);\n if (expansion.explain) explain.push(expansion.explain);\n if (commands.length > 0) {\n explain.push(`retrieval: CLI command routing (${commands.join(\", \")})`);\n }\n const paths = [...new Set([...commands, ...expansion.paths])];\n if (paths.length > TASK_FILES) {\n explain.push(\n `abstention: ${paths.length - TASK_FILES} lower-ranked candidate(s) beyond the top ${TASK_FILES} dropped`,\n );\n }\n return {\n paths: paths.slice(0, TASK_FILES),\n hits,\n explain,\n ...(symbols && Object.keys(symbols).length > 0 ? { symbols } : {}),\n };\n}\n\n/**\n * BYOK rerank of the keyword/fused path. Returns null when the layer is off.\n * The model may reorder admitted files, abstain, or promote a below-floor\n * candidate that has at least two task terms in its model summary or current\n * source (the vector corroboration rule); anything else it names is dropped.\n */\n/** A rerank call shorter than this cannot answer in time; skip it. */\nconst MIN_RERANK_MS = 200;\n\nasync function llmRerank(input: {\n repositoryRoot: string;\n task: string;\n manifest: IndexManifest;\n entries: Array<{ path: string; summary: string; llmText?: string }>;\n scored: Array<{ path: string; score: number; order: number }>;\n semantic: string[];\n ranked: string[];\n wantsTests: boolean;\n deadlineAt: number | undefined;\n}): Promise<{ ranked: string[]; explain: string; symbols: Record<string, string[]> } | null> {\n let config: Awaited<ReturnType<typeof import(\"../llm/config.js\").resolveLlmConfig>>;\n try {\n config = await (await import(\"../llm/config.js\")).resolveLlmConfig(input.repositoryRoot);\n } catch {\n return null;\n }\n if (!config.enabled || !config.features.rerank) return null;\n const skipped = (reason: string) => ({\n ranked: input.ranked,\n explain: `retrieval: llm rerank skipped (${reason})`,\n symbols: {},\n });\n const usable = (path: string) => input.wantsTests || !TEST_PATH.test(path);\n const candidatePaths = [\n ...new Set([\n ...input.ranked,\n ...input.scored\n .map((entry) => entry.path)\n .filter(usable)\n .slice(0, 12),\n ...input.semantic.filter(usable).slice(0, 8),\n ]),\n ].slice(0, 15);\n if (candidatePaths.length === 0) return null;\n const byPath = new Map(input.entries.map((entry) => [entry.path, entry]));\n const deadlineMs = Math.min(\n config.timeoutMs,\n input.deadlineAt === undefined ? config.timeoutMs : input.deadlineAt - Date.now(),\n );\n if (deadlineMs < MIN_RERANK_MS) return skipped(\"no time left in the hook budget\");\n const { rerankCandidates } = await import(\"../llm/rerank.js\");\n const result = await rerankCandidates(\n input.repositoryRoot,\n config,\n input.task,\n candidatePaths.map((path) => ({\n path,\n summary: byPath.get(path)?.llmText || byPath.get(path)?.summary || \"\",\n signatures: (input.manifest.entries[path]?.symbolTable ?? []).map((s) => s.signature),\n })),\n deadlineMs,\n ).catch(() => ({ ok: false as const, reason: \"invalid\" as const }));\n if (!result.ok) return skipped(result.reason);\n const label = `${config.provider} ${config.model}, ${result.cached ? \"cached\" : `${result.latencyMs} ms`}`;\n if (result.value.abstain) {\n return {\n ranked: [],\n explain: `retrieval: llm rerank (${label}, model abstained over ${candidatePaths.length} candidates)`,\n symbols: {},\n };\n }\n const terms = taskTerms(input.task);\n const picks: string[] = [];\n const symbols: Record<string, string[]> = {};\n let promoted = 0;\n for (const file of result.value.files) {\n if (!input.ranked.includes(file.path)) {\n const source = (await readBoundedSource(input.repositoryRoot, file.path)) ?? \"\";\n const llmText = byPath.get(file.path)?.llmText ?? \"\";\n if (\n Math.max(countTerms(llmText, terms), countTerms(source, terms)) < EVIDENCE_FLOOR.vectorTerms\n )\n continue;\n promoted++;\n }\n picks.push(file.path);\n const known = new Set(\n (input.manifest.entries[file.path]?.symbolTable ?? []).map((s) => s.name),\n );\n const named = file.symbols.filter((name) => known.has(name));\n if (named.length > 0) symbols[file.path] = named;\n }\n return {\n ranked: [...picks, ...input.ranked.filter((path) => !picks.includes(path))],\n explain:\n `retrieval: llm rerank (${label}, ${picks.length} of ${candidatePaths.length} candidates` +\n (promoted > 0 ? `, ${promoted} promoted` : \"\") +\n \")\",\n symbols,\n };\n}\n\n/**\n * Source files for CLI commands the task names as `<bin> <command> [sub]`,\n * where `<bin>` is a `bin` entry of the repository's package.json: a file in a\n * `cli`/`cmd`/`commands` directory whose stem is `command` or `command-sub`.\n */\nasync function commandFiles(\n repositoryRoot: string,\n task: string,\n entries: Array<{ path: string }>,\n): Promise<string[]> {\n const bins = await packageBins(repositoryRoot);\n if (bins.length === 0) return [];\n const stems = new Set<string>();\n const pattern = new RegExp(\n `\\\\b(?:${bins.map(escapeRegExp).join(\"|\")})\\\\s+([a-z][\\\\w-]*)(?:\\\\s+([a-z][\\\\w-]*))?`,\n \"gi\",\n );\n for (const [, command, sub] of task.matchAll(pattern)) {\n if (!command) continue;\n stems.add(command.toLowerCase());\n if (sub) stems.add(`${command}-${sub}`.toLowerCase());\n }\n if (stems.size === 0) return [];\n const matches = entries\n .map((entry) => entry.path)\n .filter((path) => {\n if (TEST_PATH.test(path) || NON_SOURCE_PATH.test(path) || !COMMAND_DIR.test(path)) {\n return false;\n }\n const name = path.slice(path.lastIndexOf(\"/\") + 1);\n return stems.has(name.slice(0, name.lastIndexOf(\".\")).toLowerCase());\n });\n // `command-sub` names the command more precisely than `command`.\n return matches\n .sort((left, right) => right.length - left.length || left.localeCompare(right))\n .slice(0, TASK_FILES);\n}\n\nconst COMMAND_DIR = /(^|\\/)(cli|cmd|commands?)\\//;\n\nasync function packageBins(repositoryRoot: string): Promise<string[]> {\n try {\n const manifest = JSON.parse(await readFile(join(repositoryRoot, \"package.json\"), \"utf8\")) as {\n name?: unknown;\n bin?: unknown;\n };\n if (typeof manifest.bin === \"string\" && typeof manifest.name === \"string\") {\n return [manifest.name.slice(manifest.name.lastIndexOf(\"/\") + 1)];\n }\n if (manifest.bin && typeof manifest.bin === \"object\") return Object.keys(manifest.bin);\n } catch {\n // No (readable) package.json: no CLI routing.\n }\n return [];\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Task wording that asks for a path through several files. */\nconst MULTI_FILE_TASK =\n /\\b(trace|follow|flows?|through|across|between|end[- ]to[- ]end|pipeline)\\b|\\bfrom\\b.+\\bto\\b/i;\n\n/**\n * Bounded multi-file expansion (#45). Only when the task asks for a path\n * through several files: import neighbours of the top targets that carry\n * lexical evidence (at least one task term, even below the floor) get the\n * import edge credited as one more term, up to `expansionFiles` of them, most\n * linked first. Each is placed before the first lower-scoring non-seed target,\n * so it never displaces a file with stronger evidence.\n */\nfunction expandAlongImports(\n ranked: string[],\n map: ArchitectureMap,\n evidence: Map<string, number>,\n task: string,\n options: { allowGraphOnly?: boolean } = {},\n): { paths: string[]; explain: string | null } {\n if (!MULTI_FILE_TASK.test(task)) return { paths: ranked, explain: null };\n const seeds = ranked.slice(0, EXPANSION.seeds);\n const links = new Map<string, Set<string>>();\n const graphOnly = new Map<string, number>();\n for (const edge of map.edges) {\n for (const [seed, other] of [\n [edge.from, edge.to],\n [edge.to, edge.from],\n ] as const) {\n if (!seeds.includes(seed) || seeds.includes(other)) continue;\n if (!evidence.has(other)) {\n if (\n !options.allowGraphOnly ||\n edge.from !== seed ||\n TEST_PATH.test(other) ||\n NON_SOURCE_PATH.test(other)\n )\n continue;\n graphOnly.set(\n other,\n Math.max(\n graphOnly.get(other) ?? 0,\n edge.symbols?.length ? 2 : edge.kind === \"import\" ? 1 : 0,\n ),\n );\n }\n links.set(other, (links.get(other) ?? new Set()).add(seed));\n }\n }\n const scoreOf = (path: string) => evidence.get(path) ?? 0;\n const added = [...links]\n .sort(\n ([left, a], [right, b]) =>\n b.size - a.size ||\n scoreOf(right) - scoreOf(left) ||\n (graphOnly.get(right) ?? 0) - (graphOnly.get(left) ?? 0) ||\n left.localeCompare(right),\n )\n .slice(0, EXPANSION.files)\n .map(([path]) => path);\n if (added.length === 0) return { paths: ranked, explain: null };\n const paths = ranked.filter((path) => !added.includes(path));\n for (const path of added) {\n const credited = scoreOf(path) + 1;\n const at = paths.findIndex(\n (other, index) => index >= seeds.length && scoreOf(other) < credited,\n );\n paths.splice(at < 0 ? paths.length : at, 0, path);\n }\n return {\n paths,\n explain:\n `expansion: ${added.length} import neighbour(s) of the top ${seeds.length} target(s) ranked with the import edge credited as a task term (${added.join(\", \")}; multi-file task wording)` +\n (added.some((path) => graphOnly.has(path)) ? \"; graph-only import bridge\" : \"\"),\n };\n}\n\n/** Partial semantic matches fill available slots after established evidence. */\nexport function rankEvidenceCandidates(\n lexical: string[],\n admittedKeywords: string[],\n vector: string[],\n partial: string[],\n): string[] {\n const admitted = new Set([...admittedKeywords, ...vector]);\n const established = fuseRankings([lexical, vector]).filter((path) => admitted.has(path));\n return [...new Set([...established, ...partial])];\n}\n\n/** Reciprocal rank fusion: robust to the two lists using unrelated score scales. */\nfunction fuseRankings(lists: string[][], k = 60): string[] {\n const scores = new Map<string, number>();\n for (const list of lists) {\n list.forEach((path, index) => {\n scores.set(path, (scores.get(path) ?? 0) + 1 / (k + index + 1));\n });\n }\n return [...scores]\n .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))\n .map(([path]) => path);\n}\n\nconst TASK_STOPWORDS = new Set(\n \"the and for are was were how does did which where when what why who with without from into onto that this these those there their them then than have has had not but can could should would will its it's our your you all any each some such only also just like make makes made use uses used get gets set sets new old way\".split(\n \" \",\n ),\n);\n/**\n * Minimum evidence for a task-discovered file (#45). `keywordScore` is the\n * `scoreTaskPaths` score: two distinct task terms, or one in the file name. A\n * vector hit below it needs `vectorTerms` distinct task terms in current source.\n * Calibrated on the `tune` split of evals/task-savings.json only: every expected\n * file retrieved there scored ≥ 2 (one 0.5 step of margin kept; no tune file\n * scoring 1 was expected) and expected vector-only files matched 2 and 3 terms.\n */\nexport const EVIDENCE_FLOOR = { keywordScore: 1.5, vectorTerms: 2 } as const;\n/** A zg chunk is evidence for its matched passage, not unrelated terms elsewhere in the file. */\nasync function hasMatchedPassageEvidence(\n repositoryRoot: string,\n path: string,\n hits: RetrievalHit[],\n terms: string[],\n symbols: Array<{ startLine: number; endLine: number }>,\n): Promise<boolean> {\n const source = await readBoundedSource(repositoryRoot, path);\n if (source === null) return false;\n // Keep the established file-evidence route: passage-only filtering lost\n // required files on the fused evaluation. Passages additionally admit narrow paraphrases.\n if (countTerms(source, terms) >= EVIDENCE_FLOOR.vectorTerms) return true;\n const lines = source.split(/\\r?\\n/);\n return hits.some((hit) => {\n if (hit.path !== path || hit.endLine > lines.length) return false;\n const passage = lines.slice(hit.startLine - 1, hit.endLine);\n const matched = countTerms(passage.join(\" \"), terms);\n return (\n matched >= EVIDENCE_FLOOR.vectorTerms ||\n (matched >= 1 &&\n terms.length === 2 &&\n hit.rank <= 3 &&\n symbols.some(\n (symbol) => hit.startLine <= symbol.endLine && hit.endLine >= symbol.startLine,\n ))\n );\n });\n}\nexport const TEST_PATH =\n /(^|\\/)(__tests__|__fixtures__|fixtures?|test|tests)\\/|\\.(test|spec)\\.[a-z]+$|(^|\\/)(test_[^/]*|[^/]*_test|conftest)\\.py$/;\n/**\n * Changelogs and release notes: upper-case names (NEWS, CHANGES.rst, HISTORY.md)\n * with any extension, \"changelog\"/\"release notes\" in any case, and lower-case\n * changes/history/news/releases only as prose files (so `src/history.ts` is code).\n */\nexport const CHANGELOG_PATH = new RegExp(\n [\n \"(?:CHANGES|CHANGELOG|HISTORY|NEWS|RELEASES?|RELEASE[-_]?NOTES)(?:[._-][^/]*)?\",\n \"[Cc]hange[-_]?[Ll]og[^/]*\",\n \"[Rr]elease[-_]?[Nn]otes[^/]*\",\n \"(?:[Cc]hanges|[Hh]istory|[Nn]ews|[Rr]eleases?)\\\\.(?:md|mdx|rst|txt|adoc|markdown)\",\n ]\n .map((name) => `(?:^|/)${name}$`)\n .join(\"|\"),\n);\n/**\n * The task is about the changelog: \"changelog\", \"release notes\", \"version\n * history\", or a changelog file named in capitals or with its extension. The\n * plain words \"changes\" and \"history\" are too common in coding tasks to count.\n */\nexport function asksForChangelog(task: string): boolean {\n return (\n /\\b(?:change[-_ ]?logs?|release[-_ ]?notes?|(?:release|version) history)\\b/i.test(task) ||\n /\\b(?:CHANGES|HISTORY|NEWS|RELEASES?)\\b/.test(task) ||\n /\\b(?:changes|history|news|releases?)\\.(?:md|mdx|rst|txt|adoc)\\b/i.test(task)\n );\n}\nconst NON_SOURCE_BASE =\n /(^|\\/)(docs?|\\.github)\\/|\\.(md|mdx|rst|adoc|txt|ya?ml|json|lock)$|(^|\\/)CHANGELOG/i;\n/** Docs, config and changelogs: never enough on their own to send code. */\nexport const NON_SOURCE_PATH = {\n test: (path: string): boolean => NON_SOURCE_BASE.test(path) || CHANGELOG_PATH.test(path),\n};\nconst PROSE_PATH = /(^|\\/)docs?\\/|\\.(md|mdx|rst|txt)$/i;\n/**\n * Prose documentation (a subset of NON_SOURCE_PATH; config files are not\n * docs). Changelogs are not docs: a docs task never makes them targets.\n */\nexport const DOC_PATH = {\n test: (path: string): boolean => PROSE_PATH.test(path) && !CHANGELOG_PATH.test(path),\n};\n/** A task about documentation lets docs compete with (and stand in for) source. */\nconst DOCS_TASK = /\\b(docs?|documentation|documented|guides?|tutorials?|readme|manual)\\b/i;\n\n/**\n * Source, or a non-source file the task is about: a doc for a docs task, a\n * changelog for a changelog task. Only these can stop abstention.\n */\nexport function countsAsSource(path: string, task: string): boolean {\n return (\n !NON_SOURCE_PATH.test(path) ||\n (DOCS_TASK.test(task) && DOC_PATH.test(path)) ||\n (asksForChangelog(task) && CHANGELOG_PATH.test(path))\n );\n}\n\n/**\n * Rank indexed files against a natural-language task. Terms match whole words\n * (camelCase and snake_case split, simple plurals folded) in the path, summary\n * and symbols; a term in the file name counts extra. Tests and fixtures are\n * skipped unless the task is about tests, and docs/config files are\n * down-weighted so source files win ties.\n */\nexport function rankTaskPaths(entries: TaskPathEntry[], task: string): string[] {\n return scoreTaskPaths(entries, task).map((entry) => entry.path);\n}\n\ntype TaskPathEntry = {\n path: string;\n summary: string;\n symbols: string[];\n symbolTable?: Array<{ name: string }>;\n /** Model-written description (BYOK summaries): affects `order`, never `score`. */\n llmText?: string;\n};\n\n/** `rankTaskPaths` with each file's score, best first. */\nexport function scoreTaskPaths(\n entries: TaskPathEntry[],\n task: string,\n): Array<{ path: string; score: number; order: number }> {\n const terms = taskTerms(task);\n if (terms.length === 0) return [];\n const wantsTests = terms.some((term) => term === \"test\" || term === \"spec\");\n const candidates = entries.flatMap((entry) => {\n if (TEST_PATH.test(entry.path) && !wantsTests) return [];\n const words = new Set(\n splitWords(\n `${entry.path} ${entry.summary} ${entry.symbols.join(\" \")} ${entry.symbolTable?.map((symbol) => symbol.name).join(\" \") ?? \"\"}`,\n ),\n );\n const llmWords = new Set(entry.llmText ? splitWords(entry.llmText) : []);\n const fileWords = new Set(splitWords(entry.path.slice(entry.path.lastIndexOf(\"/\") + 1)));\n return [{ path: entry.path, words, llmWords, fileWords }];\n });\n // Rare task terms identify a file better than common ones, so ordering\n // weighs each matched term by inverse document frequency. The returned\n // `score` stays unweighted and ignores model-written text: the evidence\n // floor is calibrated on it.\n const idf = new Map(\n terms.map((term) => {\n const df = candidates.filter(\n (candidate) => candidate.words.has(term) || candidate.llmWords.has(term),\n ).length;\n return [term, Math.log((candidates.length + 1) / (df + 1)) + 1];\n }),\n );\n return candidates\n .flatMap(({ path, words, llmWords, fileWords }) => {\n let score = 0;\n let weighted = 0;\n for (const term of terms) {\n if (words.has(term)) {\n const points = fileWords.has(term) ? 1.5 : 1;\n score += points;\n weighted += points * (idf.get(term) ?? 1);\n } else if (llmWords.has(term)) {\n weighted += 0.5 * (idf.get(term) ?? 1);\n }\n }\n if (weighted === 0) return [];\n if (!countsAsSource(path, task)) {\n score *= 0.5;\n weighted *= 0.5;\n }\n return [{ path, score, order: weighted }];\n })\n .sort(\n (left, right) =>\n right.order - left.order || right.score - left.score || left.path.localeCompare(right.path),\n );\n}\n\n/** Lowercase words with camelCase/snake_case split and a trailing plural \"s\" folded. */\nexport function splitWords(text: string): string[] {\n return rawWords(text).map(foldPlural);\n}\n\nfunction rawWords(text: string): string[] {\n return text\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((word) => word.length > 2);\n}\n\n/** `crashes` → `crash`, `users` → `user`, `cookies` → `cookie`; `class`, `status`, `this` stay. */\nfunction foldPlural(word: string): string {\n if (word.length <= 3 || !word.endsWith(\"s\") || /(ss|us|is)$/.test(word)) return word;\n if (/(ss|sh|ch|x|z)es$/.test(word)) return word.slice(0, -2);\n return word.slice(0, -1);\n}\n\n/** Task words that carry meaning (stopwords removed), as used for path ranking. */\nexport function taskTerms(task: string): string[] {\n return [\n ...new Set(\n rawWords(task)\n .filter((word) => !TASK_STOPWORDS.has(word))\n .map(foldPlural)\n .filter((term) => !TASK_STOPWORDS.has(term)),\n ),\n ];\n}\n\n/** How many distinct task terms appear in `text`. */\nexport function countTerms(text: string, terms: string[]): number {\n if (terms.length === 0) return 0;\n const words = new Set(splitWords(text));\n return terms.filter((term) => words.has(term)).length;\n}\n\n/** Looks like code, not prose: camelCase humps, snake_case, `$`, or dotted `Class.method`. */\nconst CODE_IDENTIFIER = /[a-z0-9][A-Z]|[_$.]/;\n\n/** Identifier-shaped task tokens; dotted ones also split (`Vault.lock` → `Vault`, `lock`). */\nexport function identifierTokens(task: string): string[] {\n const tokens = task.match(/[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*/g) ?? [];\n return [...new Set(tokens.flatMap((token) => [token, ...token.split(\".\")]))];\n}\n\n/** How many segments of a (`Class.method`) symbol name the tokens name. */\nexport function namedSegments(name: string, tokens: string[]): number {\n return name.split(\".\").filter((segment) => tokens.includes(segment)).length;\n}\n\nexport async function loadProjectArchitectureMap(\n repositoryRoot: string,\n manifestSource: IndexManifest | Promise<IndexManifest>,\n): Promise<ArchitectureMap> {\n const [config, manifest] = await Promise.all([readConfigFile(repositoryRoot), manifestSource]);\n const builtIn = (await loadArchitectureMap(repositoryRoot, manifest)).map;\n if (\n config.graphify?.enabled &&\n (await graphifyGraphExists(repositoryRoot, config.graphify.graphPath))\n ) {\n try {\n const graphPath = join(\n repositoryRoot,\n config.graphify.graphPath ?? \"graphify-out/graph.json\",\n );\n const [graph, graphFile, sourceHashes] = await Promise.all([\n loadGraphifyMap(graphPath, repositoryRoot),\n stat(graphPath),\n readGraphSourceSnapshot(graphPath),\n ]);\n // A graph can be valid JSON yet lag the current source, or cover only a\n // subset of files. Keep fresh Graphify-only relations and symbol labels,\n // while retaining the current import scan for the rest of the project.\n const livePaths = new Set(\n (\n await Promise.all(\n graph.modules.map(async (module) => {\n try {\n const absolute = await realpath(join(repositoryRoot, module.path));\n const inside = relative(await realpath(repositoryRoot), absolute);\n if (inside.startsWith(\"..\") || isAbsolute(inside)) return null;\n const source = await stat(absolute);\n const fresh =\n sourceHashes === null\n ? source.mtimeMs <= graphFile.mtimeMs\n : sourceHashes[module.path] !== undefined &&\n sourceHashes[module.path] === manifest.entries[module.path]?.hash;\n return source.isFile() && fresh ? module.path : null;\n } catch {\n // Deleted files must not contribute graph relationships.\n return null;\n }\n }),\n )\n ).filter((path): path is string => path !== null),\n );\n const modules = new Map(builtIn.modules.map((module) => [module.path, module]));\n for (const module of graph.modules) {\n if (livePaths.has(module.path) && !modules.has(module.path))\n modules.set(module.path, module);\n }\n const edges = new Map(builtIn.edges.map((edge) => [`${edge.from}\\0${edge.to}`, edge]));\n let freshGraphEdges = 0;\n for (const edge of graph.edges) {\n if (!livePaths.has(edge.from) || !livePaths.has(edge.to)) continue;\n freshGraphEdges++;\n const key = `${edge.from}\\0${edge.to}`;\n const scanned = edges.get(key);\n edges.set(\n key,\n scanned ? { ...scanned, ...(edge.symbols ? { symbols: edge.symbols } : {}) } : edge,\n );\n }\n const mergedEdges = [...edges.values()].sort((left, right) =>\n `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`),\n );\n const importsByPath = new Map<string, string[]>();\n for (const edge of mergedEdges) {\n const imports = importsByPath.get(edge.from) ?? [];\n imports.push(edge.to);\n importsByPath.set(edge.from, imports);\n }\n return {\n modules: [...modules.values()]\n .map((module) => ({ ...module, imports: importsByPath.get(module.path) ?? [] }))\n .sort((left, right) => left.path.localeCompare(right.path)),\n edges: mergedEdges,\n source: freshGraphEdges > 0 ? \"graphify+imports\" : \"imports\",\n };\n } catch {\n // An unusable or malformed graph degrades to the built-in import map\n // instead of failing the whole context request.\n }\n }\n return { ...builtIn, source: \"imports\" };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { ArchitectureMap, ModuleEdge, ModuleNode } from \"./types.js\";\n\ntype GraphifyRecord = Record<string, unknown>;\n\n/**\n * Graphify emits containment and documentation relations alongside real\n * dependencies. Keeping them would make a README or an MCP config look like an\n * importer of the code it mentions, so they are dropped. Edges with no relation\n * field (older graph.json exports) are still treated as imports.\n */\nconst NON_DEPENDENCY_RELATIONS = new Set([\n \"contains\",\n \"part_of\",\n \"defines\",\n \"declares\",\n \"references\",\n \"requires_env\",\n \"rationale_for\",\n]);\n\n/**\n * Graphify's graph is undirected, so a relation phrased from the dependency's\n * point of view (\"b is imported by a\") arrives with its endpoints the wrong way\n * round for a dependency edge. These are swapped rather than dropped.\n */\nconst INVERSE_RELATIONS = new Set([\n \"imported_by\",\n \"called_by\",\n \"used_by\",\n \"referenced_by\",\n \"extended_by\",\n \"implemented_by\",\n \"dependency_of\",\n \"depended_on_by\",\n]);\n\n// Only known code dependencies may become file-level import neighbours. A new\n// Graphify relation must be reviewed before it can influence retrieval.\nconst DIRECT_DEPENDENCY_RELATIONS = new Set([\n \"import\",\n \"imports\",\n \"imports_from\",\n \"dynamic_import\",\n \"dynamic_imports\",\n \"calls\",\n \"uses\",\n \"extends\",\n \"implements\",\n \"depends_on\",\n]);\n\n/** How many symbol names an edge carries, so a large fan-in stays bounded. */\nconst MAX_EDGE_SYMBOLS = 8;\n\n/** Import the common graph.json node/edge shape without depending on Graphify. */\nexport async function loadGraphifyMap(\n graphPath: string,\n repositoryRoot = process.cwd(),\n): Promise<ArchitectureMap> {\n const raw = JSON.parse(await readFile(graphPath, \"utf8\")) as GraphifyRecord;\n const nodes = arrayRecord(raw.nodes ?? raw.entities);\n const edges = arrayRecord(raw.edges ?? raw.links ?? raw.relationships);\n const nodePaths = new Map<string, string>();\n const nodeLabels = new Map<string, string>();\n const modules: ModuleNode[] = [];\n\n for (const node of nodes) {\n const id = stringValue(node.id ?? node.key ?? node.name);\n const path = graphifyPath(node, repositoryRoot);\n if (!id || !path) continue;\n nodePaths.set(id, path);\n const label = stringValue(node.label ?? node.name);\n // The node standing for the file itself carries no useful symbol name.\n if (label && label !== path && !path.endsWith(`/${label}`)) nodeLabels.set(id, label);\n modules.push({ path, language: languageFor(path), imports: [] });\n }\n\n const modulePaths = new Set(modules.map((module) => module.path));\n const importedEdges: ModuleEdge[] = [];\n for (const edge of edges) {\n const relation = stringValue(edge.relation ?? edge.type ?? edge.kind)?.toLowerCase() ?? null;\n if (relation && NON_DEPENDENCY_RELATIONS.has(relation)) continue;\n if (relation && !DIRECT_DEPENDENCY_RELATIONS.has(relation) && !INVERSE_RELATIONS.has(relation))\n continue;\n const inverse = relation !== null && INVERSE_RELATIONS.has(relation);\n const sourceEndpoint = edge.from ?? edge.source;\n const targetEndpoint = edge.to ?? edge.target;\n const [tail, head] = inverse\n ? [targetEndpoint, sourceEndpoint]\n : [sourceEndpoint, targetEndpoint];\n const from = endpointPath(tail, nodePaths, repositoryRoot);\n const to = endpointPath(head, nodePaths, repositoryRoot);\n if (!from || !to || !modulePaths.has(from) || !modulePaths.has(to) || from === to) continue;\n // The endpoint inside the dependency names the symbol the dependant uses.\n const symbol = endpointLabel(head, nodeLabels);\n importedEdges.push({\n from,\n to,\n kind: relation?.includes(\"dynamic\") ? \"dynamic_import\" : \"import\",\n ...(symbol ? { symbols: [symbol] } : {}),\n });\n }\n\n const uniqueModules = [...new Map(modules.map((module) => [module.path, module])).values()].sort(\n (left, right) => left.path.localeCompare(right.path),\n );\n const byEndpoints = new Map<string, ModuleEdge>();\n for (const edge of importedEdges) {\n const key = `${edge.from}:${edge.to}`;\n const existing = byEndpoints.get(key);\n if (!existing) {\n byEndpoints.set(key, edge);\n continue;\n }\n // Several symbol-level links collapse into one file edge: keep every symbol\n // name, and let a static import win over a dynamic one.\n const symbols = mergeSymbols(existing.symbols, edge.symbols);\n byEndpoints.set(key, {\n ...(existing.kind === \"dynamic_import\" && edge.kind === \"import\" ? edge : existing),\n ...(symbols ? { symbols } : {}),\n });\n }\n const uniqueEdges = [...byEndpoints.values()].sort((left, right) =>\n `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`),\n );\n if (uniqueModules.length === 0 || uniqueEdges.length === 0) {\n throw new Error(`Graphify graph contains no usable file relationships: ${graphPath}`);\n }\n for (const module of uniqueModules) {\n module.imports = uniqueEdges\n .filter((edge) => edge.from === module.path)\n .map((edge) => edge.to)\n .sort();\n }\n return { modules: uniqueModules, edges: uniqueEdges };\n}\n\nfunction arrayRecord(value: unknown): GraphifyRecord[] {\n return Array.isArray(value)\n ? value.filter((item): item is GraphifyRecord => Boolean(item && typeof item === \"object\"))\n : [];\n}\n\nfunction endpointPath(\n endpoint: unknown,\n nodePaths: Map<string, string>,\n repositoryRoot: string,\n): string | null {\n if (typeof endpoint === \"string\")\n return nodePaths.get(endpoint) ?? normalizeGraphPath(endpoint, repositoryRoot);\n if (!endpoint || typeof endpoint !== \"object\") return null;\n const record = endpoint as GraphifyRecord;\n const id = stringValue(record.id ?? record.key ?? record.name);\n return nodePaths.get(id ?? \"\") ?? graphifyPath(record, repositoryRoot);\n}\n\nfunction endpointLabel(endpoint: unknown, nodeLabels: Map<string, string>): string | null {\n if (typeof endpoint === \"string\") return nodeLabels.get(endpoint) ?? null;\n if (!endpoint || typeof endpoint !== \"object\") return null;\n const id = stringValue((endpoint as GraphifyRecord).id);\n return id ? (nodeLabels.get(id) ?? null) : null;\n}\n\nfunction mergeSymbols(left?: string[], right?: string[]): string[] | undefined {\n if (!left && !right) return undefined;\n return [...new Set([...(left ?? []), ...(right ?? [])])].sort().slice(0, MAX_EDGE_SYMBOLS);\n}\n\nfunction graphifyPath(node: GraphifyRecord, repositoryRoot: string): string | null {\n const source = node.source;\n const location = node.location;\n const candidate =\n stringValue(node.path ?? node.file ?? node.file_path ?? node.source_file) ??\n (source && typeof source === \"object\" ? stringValue((source as GraphifyRecord).path) : null) ??\n (location && typeof location === \"object\"\n ? stringValue((location as GraphifyRecord).file ?? (location as GraphifyRecord).path)\n : null);\n return candidate ? normalizeGraphPath(candidate, repositoryRoot) : null;\n}\n\nfunction normalizeGraphPath(path: string, repositoryRoot: string): string | null {\n const absolute = isAbsolute(path) ? resolve(path) : resolve(repositoryRoot, path);\n const relativePath = relative(resolve(repositoryRoot), absolute).replace(/\\\\/g, \"/\");\n if (relativePath === \"\" || relativePath === \"..\" || relativePath.startsWith(\"../\")) return null;\n return relativePath.replace(/^\\.\\//, \"\");\n}\n\nfunction stringValue(value: unknown): string | null {\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction languageFor(path: string): ModuleNode[\"language\"] {\n if (/\\.py$/.test(path)) return \"py\";\n return /\\.tsx?$/.test(path) ? \"ts\" : \"js\";\n}\n","import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { NON_SOURCE_PATH, readBoundedSource, TEST_PATH } from \"./discovery.js\";\n\n/**\n * Explicit anchors in a task: repo paths, `path:line`, stack frames and quoted\n * error strings found verbatim in current source. They name the code better\n * than any ranking, so discovery puts them first. Only files in the index count;\n * a typo or a deleted file yields no anchor.\n */\nexport type TaskAnchor = {\n path: string;\n line: number | null;\n kind: \"path\" | \"line\" | \"stack\" | \"error\";\n};\n\nconst MAX_ANCHORS = 3;\n/** Source files searched for a quoted error string (bounded for the prompt hook). */\nconst MAX_ERROR_SEARCH_FILES = 2000;\n\nconst JS_FRAME = /\\bat\\s+(?:[^\\s()]+\\s+\\()?([^\\s()]+?):(\\d+):\\d+\\)?/g;\nconst PY_FRAME = /\\bFile\\s+\"([^\"]+)\",\\s+line\\s+(\\d+)/g;\nconst PATH_TOKEN =\n /(?:^|[\\s(`'\"[])((?:\\.{0,2}\\/)?[\\w@.-]+(?:\\/[\\w@.-]+)*\\.[A-Za-z0-9]+)(?::(\\d+))?/g;\nconst QUOTED = /\"([^\"\\n]{8,200})\"|'([^'\\n]{8,200})'|`([^`\\n]{8,200})`/g;\n\nexport async function extractTaskAnchors(\n repositoryRoot: string,\n task: string,\n paths: ReadonlySet<string>,\n): Promise<TaskAnchor[]> {\n const root = resolve(repositoryRoot);\n const found: Array<TaskAnchor & { at: number }> = [];\n const add = (raw: string, line: string | undefined, kind: TaskAnchor[\"kind\"], at: number) => {\n const path = toIndexedPath(root, raw, paths);\n if (path) found.push({ path, line: line ? Number(line) : null, kind, at });\n };\n for (const match of task.matchAll(JS_FRAME)) add(match[1] ?? \"\", match[2], \"stack\", match.index);\n for (const match of task.matchAll(PY_FRAME)) add(match[1] ?? \"\", match[2], \"stack\", match.index);\n for (const match of task.matchAll(PATH_TOKEN)) {\n add(match[1] ?? \"\", match[2], match[2] ? \"line\" : \"path\", match.index);\n }\n\n const anchors: TaskAnchor[] = [];\n const seen = new Set<string>();\n // A stack frame and a plain path token can match the same text; the first\n // (stack frames are collected first) wins for that path.\n for (const { at: _at, ...anchor } of found.sort((left, right) => left.at - right.at)) {\n if (seen.has(anchor.path)) continue;\n seen.add(anchor.path);\n anchors.push(anchor);\n }\n if (anchors.length < MAX_ANCHORS) {\n for (const error of await errorAnchors(root, task, paths)) {\n if (seen.has(error.path)) continue;\n seen.add(error.path);\n anchors.push(error);\n }\n }\n return anchors.slice(0, MAX_ANCHORS);\n}\n\nfunction toIndexedPath(root: string, raw: string, paths: ReadonlySet<string>): string | null {\n const absolute = isAbsolute(raw) ? raw : resolve(root, raw);\n const rel = relative(root, absolute);\n if (rel === \"\" || rel.startsWith(`..${sep}`) || rel === \"..\" || isAbsolute(rel)) return null;\n const normalized = rel.replace(/\\\\/g, \"/\");\n return paths.has(normalized) ? normalized : null;\n}\n\n/**\n * Quoted strings (not paths) found verbatim in exactly one source file. A\n * string in several files (\"use client\", a shared label) names none of them.\n */\nasync function errorAnchors(\n root: string,\n task: string,\n paths: ReadonlySet<string>,\n): Promise<TaskAnchor[]> {\n const quotes = [...task.matchAll(QUOTED)]\n .map((match) => match[1] ?? match[2] ?? match[3] ?? \"\")\n .filter((text) => text.trim().length >= 8 && !toIndexedPath(root, text, paths));\n if (quotes.length === 0) return [];\n const sources = [...paths]\n .filter((path) => !TEST_PATH.test(path) && !NON_SOURCE_PATH.test(path))\n .sort()\n .slice(0, MAX_ERROR_SEARCH_FILES);\n // ponytail: one pass per quote over bounded sources; an index of string literals if repos outgrow it.\n const matches = quotes.map(() => [] as TaskAnchor[]);\n for (const path of sources) {\n if (matches.every((found) => found.length > 1)) break;\n const source = await readBoundedSource(root, path);\n if (!source) continue;\n quotes.forEach((quote, i) => {\n const found = matches[i] as TaskAnchor[];\n if (found.length > 1) return;\n const index = source.indexOf(quote);\n if (index >= 0) {\n found.push({ path, line: source.slice(0, index).split(\"\\n\").length, kind: \"error\" });\n }\n });\n }\n return matches.flatMap((found) => (found.length === 1 ? found : []));\n}\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport type { IndexManifest } from \"../indexer/types.js\";\nimport {\n CHANGELOG_PATH,\n DOC_PATH,\n NON_SOURCE_PATH,\n readBoundedSource,\n splitWords,\n TEST_PATH,\n} from \"./discovery.js\";\n\n/**\n * Full-text relevance for task → file discovery: BM25 over each indexed\n * source file's words, not just its path, summary and symbol names (which\n * miss every word inside a function body). On the dsicord-bot labelled set it\n * took exact-wording tasks from 46.7% to 66.7% top-1 and reworded ones from\n * 0% to 30%.\n *\n * Word counts are cached per file by content hash in\n * `.codebuddy/cache/content-terms.json` (gitignored, rebuildable), so a prompt\n * re-reads only files that changed since the last one.\n */\n\n/** `passages`: the distinct words of each 40-line window (stepping 20 lines). */\ntype FileTerms = {\n hash: string;\n length: number;\n counts: Record<string, number>;\n passages: string[][];\n};\ntype Cache = { version: 2; files: Record<string, FileTerms> };\n\nconst PASSAGE_LINES = 40;\n\nconst K1 = 1.2;\nconst B = 0.75;\n/**\n * A match must hold at least this many distinct task terms in one passage\n * (fewer if the task has fewer). Terms scattered across a large file are how\n * unrelated tasks matched: one task word in a git helper, another in an\n * unrelated function further down.\n */\nconst MIN_TERMS = 2;\n/** …and score at least this share of the best file, so a weak tail never rides along. */\nconst MIN_SHARE = 0.5;\n/**\n * …and cover at least this share of the task's rarity (IDF) mass. A task whose\n * most specific words appear nowhere in the repository (an email feature asked\n * of a repo with no mail code) is about something else, however well two\n * common words match. 0.3 is the lowest value that kept every unrelated task in\n * evals/task-savings.json silent (0.2 let two through); higher values cost recall.\n */\nconst MIN_IDF_COVERAGE = 0.3;\n\nexport type ContentMatch = { path: string; score: number; terms: number };\n\n/**\n * Source files ranked by BM25 for the task, already gated: each one contains\n * at least two task terms and scores at least half of the best file. Empty\n * when nothing clears that bar, so callers can stay silent instead of\n * guessing.\n */\nexport async function rankByContent(\n repositoryRoot: string,\n manifest: IndexManifest,\n terms: string[],\n wantsTests: boolean,\n /** A docs task also ranks prose docs (never config). */\n wantsDocs = false,\n wantsChangelog = false,\n): Promise<ContentMatch[]> {\n return (await rankContent(repositoryRoot, manifest, terms, wantsTests, wantsDocs, wantsChangelog))\n .matches;\n}\n\n/** `rankByContent` plus each task term's IDF over the indexed source files. */\nexport async function rankContent(\n repositoryRoot: string,\n manifest: IndexManifest,\n terms: string[],\n wantsTests: boolean,\n wantsDocs = false,\n wantsChangelog = false,\n): Promise<{ matches: ContentMatch[]; idf: Map<string, number> }> {\n const none = { matches: [], idf: new Map<string, number>() };\n if (terms.length === 0) return none;\n const paths = Object.keys(manifest.entries).filter(\n (path) =>\n (!NON_SOURCE_PATH.test(path) ||\n (wantsDocs && DOC_PATH.test(path)) ||\n (wantsChangelog && CHANGELOG_PATH.test(path))) &&\n (wantsTests || !TEST_PATH.test(path)),\n );\n const cache = await loadCache(repositoryRoot);\n let changed = false;\n const files: Record<string, FileTerms> = {};\n await Promise.all(\n paths.map(async (path) => {\n const hash = manifest.entries[path]?.hash ?? \"\";\n const cached = cache.files[path];\n if (cached && cached.hash === hash) {\n files[path] = cached;\n return;\n }\n const source = await readBoundedSource(repositoryRoot, path);\n if (source === null) return;\n const counts: Record<string, number> = {};\n const words = splitWords(`${path} ${source}`);\n for (const word of words) counts[word] = (counts[word] ?? 0) + 1;\n const lines = source.split(\"\\n\");\n const passages: string[][] = [];\n for (\n let start = 0;\n start === 0 || start + PASSAGE_LINES / 2 < lines.length;\n start += PASSAGE_LINES / 2\n ) {\n passages.push([\n ...new Set(splitWords(lines.slice(start, start + PASSAGE_LINES).join(\"\\n\"))),\n ]);\n }\n files[path] = { hash, length: words.length, counts, passages };\n changed = true;\n }),\n );\n // Entries this task did not rank (docs on a code task) stay while current.\n const retained = Object.fromEntries(\n Object.entries(cache.files).filter(\n ([path, file]) => !(path in files) && manifest.entries[path]?.hash === file.hash,\n ),\n );\n const kept = Object.keys(retained).length + Object.keys(files).length;\n if (changed || kept !== Object.keys(cache.files).length) {\n await saveCache(repositoryRoot, { version: 2, files: { ...retained, ...files } });\n }\n\n const documents = Object.entries(files);\n if (documents.length === 0) return none;\n const averageLength =\n documents.reduce((sum, [, file]) => sum + file.length, 0) / documents.length;\n const idf = new Map(\n terms.map((term) => {\n const df = documents.filter(([, file]) => (file.counts[term] ?? 0) > 0).length;\n return [term, Math.log(1 + (documents.length - df + 0.5) / (df + 0.5))];\n }),\n );\n const totalIdf = [...idf.values()].reduce((sum, value) => sum + value, 0);\n const scored = documents\n .map(([path, file]) => {\n const fileName = new Set(splitWords(path.slice(path.lastIndexOf(\"/\") + 1)));\n let score = 0;\n let covered = 0;\n for (const term of terms) {\n const tf = file.counts[term] ?? 0;\n if (tf === 0) continue;\n covered += idf.get(term) ?? 0;\n const saturation =\n (tf * (K1 + 1)) / (tf + K1 * (1 - B + (B * file.length) / averageLength));\n score += (idf.get(term) ?? 0) * saturation * (fileName.has(term) ? 1.5 : 1);\n }\n // The file name counts as part of every passage.\n const named = terms.filter((term) => fileName.has(term));\n const matched = Math.max(\n named.length,\n ...file.passages.map(\n (words) => new Set([...named, ...terms.filter((term) => words.includes(term))]).size,\n ),\n );\n return { path, score, terms: matched, coverage: totalIdf > 0 ? covered / totalIdf : 0 };\n })\n .filter((match) => match.score > 0)\n .sort((left, right) => right.score - left.score || left.path.localeCompare(right.path));\n const best = scored[0]?.score ?? 0;\n const needed = Math.min(MIN_TERMS, terms.length);\n const matches = scored\n .filter(\n (match) =>\n match.terms >= needed &&\n match.score >= MIN_SHARE * best &&\n match.coverage >= MIN_IDF_COVERAGE,\n )\n .map(({ path, score, terms: matched }) => ({ path, score, terms: matched }));\n return { matches, idf };\n}\n\n/**\n * Share of the task's IDF mass whose terms occur in `text` (0..1): the\n * `MIN_IDF_COVERAGE` measure, applied to any text. Terms absent from every\n * indexed file carry the most weight, so a task about something the repository\n * does not contain covers little however many common words a file shares.\n */\nexport function idfCoverage(text: string, terms: string[], idf: Map<string, number>): number {\n const total = terms.reduce((sum, term) => sum + (idf.get(term) ?? 0), 0);\n if (total === 0) return 0;\n const words = new Set(splitWords(text));\n return terms.reduce((sum, term) => sum + (words.has(term) ? (idf.get(term) ?? 0) : 0), 0) / total;\n}\n\nfunction cachePath(repositoryRoot: string): string {\n return join(repositoryRoot, \".codebuddy\", \"cache\", \"content-terms.json\");\n}\n\nasync function loadCache(repositoryRoot: string): Promise<Cache> {\n try {\n const parsed = JSON.parse(await readFile(cachePath(repositoryRoot), \"utf8\")) as Cache;\n if (parsed.version === 2 && parsed.files && typeof parsed.files === \"object\") return parsed;\n } catch {\n // Missing or corrupt: rebuild from the files.\n }\n return { version: 2, files: {} };\n}\n\nasync function saveCache(repositoryRoot: string, cache: Cache): Promise<void> {\n try {\n const path = cachePath(repositoryRoot);\n await mkdir(dirname(path), { recursive: true });\n await writeAtomic(path, JSON.stringify(cache));\n } catch {\n // A cache that cannot be written only costs a re-read next prompt.\n }\n}\n"],"mappings":"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","names":["readFile","readFile","mkdir","readFile","dirname","join","readFile","stat","isAbsolute","join","relative","readFile","relative","resolve","isAbsolute","relative","resolve","sep","resolve","isAbsolute","relative","sep","mkdir","readFile","dirname","join","join","readFile","mkdir","dirname","join","stat","readFile","evidence","expansion","join","stat","relative","isAbsolute","join","readFile","mkdir","dirname"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/lean.ts","../src/hooks/lean-command.ts"],"sourcesContent":["/**\n * Lean mode: the prompt hook's only job is to cut the agent's token spend. It\n * injects ranked file pointers (and code spans for read-only questions) and\n * nothing else — no MCP tools, rules block, or memory capture.\n */\nimport { createHash } from \"node:crypto\";\nimport { appendFile, mkdir } from \"node:fs/promises\";\nimport { extname, join } from \"node:path\";\nimport {\n discoverTaskPaths,\n loadProjectArchitectureMap,\n NON_SOURCE_PATH,\n readBoundedSource,\n taskTerms,\n} from \"../context/discovery.js\";\nimport { type ContextCapsule, SessionCapsuleLedger } from \"../context/session-ledger.js\";\nimport { detectTestCommand, findCoveringTest } from \"../context/tests.js\";\nimport { loadKnownSecretValues, redactSourceText } from \"../core/sensitive.js\";\nimport { loadFreshIndex } from \"../indexer/engine.js\";\nimport { estimateTokens } from \"../savings/tokens.js\";\nimport { hookRoot } from \"./root.js\";\nimport type { HookInput } from \"./runtime.js\";\n\nexport const LEAN_POINTER_PREFIX = \"CodeBuddy likely files: \";\nexport const LEAN_LIMITS = {\n paths: 6,\n symbolsPerFile: 3,\n wholeFileLines: 80,\n spanTokens: 1200,\n} as const;\n\nexport type LeanPointer = {\n path: string;\n symbols: Array<{ name: string; startLine: number; endLine: number }>;\n /** The test file that covers this path, when one follows a known convention. */\n test?: string;\n};\n\ntype LoadedPointer = LeanPointer & { lines: string[] };\n\n/** A lean block finished later than this is dropped: the host may already have moved on. */\nexport const LEAN_HOOK_BUDGET_MS = 1500;\n/** Rendering after the BYOK rerank needs this much of the budget. */\nconst RENDER_RESERVE_MS = 300;\n\n/**\n * UserPromptSubmit in lean mode → ranked file pointers, or nothing. Never\n * throws: an error is appended to `.codebuddy/cache/hooks/errors.log` and the\n * turn proceeds as if CodeBuddy were not installed.\n */\nexport async function runLeanHook(\n input: HookInput,\n emit?: (block: string) => Promise<void>,\n budgetMs = LEAN_HOOK_BUDGET_MS,\n): Promise<string> {\n const started = Date.now();\n const repositoryRoot = hookRoot(input.cwd);\n const prompt = input.prompt?.trim() ?? \"\";\n if (prompt === \"\" || prompt.startsWith(\"/\")) return \"\";\n const sessionId = input.session_id ?? \"default\";\n try {\n const { manifest } = await loadFreshIndex(repositoryRoot);\n const discovery = await discoverTaskPaths(\n repositoryRoot,\n prompt,\n manifest,\n loadProjectArchitectureMap(repositoryRoot, manifest),\n // The rerank, and zvec-grep (opt-in, only with a running `zg server`),\n // must answer while rendering still fits in the hook budget.\n {\n semantic: false,\n llmDeadlineAt: started + budgetMs - RENDER_RESERVE_MS,\n zvecDeadlineAt: started + budgetMs - RENDER_RESERVE_MS,\n },\n );\n const terms = taskTerms(prompt);\n const indexed = new Set(Object.keys(manifest.entries));\n const pointers: LeanPointer[] = discovery.paths\n .filter((path) => !NON_SOURCE_PATH.test(path))\n .map((path) => {\n const test = findCoveringTest(path, indexed);\n const table = manifest.entries[path]?.symbolTable ?? [];\n // Symbols the BYOK rerank named come first, then term matches.\n const picked = discovery.symbols?.[path] ?? [];\n const ordered = [\n ...picked.flatMap((name) => table.filter((symbol) => symbol.name === name)),\n ...table.filter(\n (symbol) =>\n !picked.includes(symbol.name) &&\n terms.some((term) => symbol.name.toLowerCase().includes(term)),\n ),\n ];\n return {\n path,\n symbols: ordered.map(({ name, startLine, endLine }) => ({ name, startLine, endLine })),\n ...(test ? { test } : {}),\n };\n });\n const ledger = new SessionCapsuleLedger(repositoryRoot);\n // The host keeps the block for this turn only, so nothing counts as already sent.\n if (input.ephemeral_context) await ledger.reset(sessionId);\n const { text, emitted } = await renderLeanBlock({\n repositoryRoot,\n prompt,\n pointers,\n ledger,\n sessionId,\n testCommand: pointers.length > 0 ? await detectTestCommand(repositoryRoot) : null,\n });\n if (Date.now() - started >= budgetMs) return \"\";\n if (text) await emit?.(text);\n await ledger.markSent(sessionId, emitted);\n return text;\n } catch (error) {\n await logHookError(repositoryRoot, sessionId, error).catch(() => {});\n return \"\";\n }\n}\n\nasync function logHookError(root: string, sessionId: string, error: unknown): Promise<void> {\n const directory = join(root, \".codebuddy\", \"cache\", \"hooks\");\n await mkdir(directory, { recursive: true });\n const message = (error as Error)?.message ?? String(error);\n await appendFile(\n join(directory, \"errors.log\"),\n `${new Date().toISOString()} ${message.replace(/\\s+/g, \" \")} session=${sessionId}\\n`,\n );\n}\n\n/**\n * The lean block for one prompt: a single pointer line, plus current-source\n * spans when the prompt is read-only. Capsules already delivered this session\n * are left out; the caller marks `emitted` as sent once the block is written.\n */\nexport async function renderLeanBlock(input: {\n repositoryRoot: string;\n prompt: string;\n pointers: LeanPointer[];\n ledger: SessionCapsuleLedger;\n sessionId: string;\n /** Sent once per session, after the pointers. */\n testCommand?: string | null;\n}): Promise<{ text: string; emitted: ContextCapsule[] }> {\n const loaded: LoadedPointer[] = [];\n for (const pointer of input.pointers) {\n if (loaded.length === LEAN_LIMITS.paths) break;\n const source = await readBoundedSource(input.repositoryRoot, pointer.path);\n if (source === null) continue; // deleted, oversized or binary since indexing\n loaded.push({\n path: pointer.path,\n symbols: pointer.symbols.slice(0, LEAN_LIMITS.symbolsPerFile),\n ...(pointer.test ? { test: pointer.test } : {}),\n lines: source.split(/\\r?\\n/),\n });\n }\n if (loaded.length === 0) return { text: \"\", emitted: [] };\n const knownSecrets = await loadKnownSecretValues(input.repositoryRoot);\n\n const capsules: ContextCapsule[] = [\n {\n key: \"lean:pointers\",\n text: LEAN_POINTER_PREFIX + loaded.map(describePointer).join(\", \"),\n },\n ...(input.testCommand\n ? [{ key: \"lean:test-command\", text: `CodeBuddy test command: ${input.testCommand}` }]\n : []),\n ];\n if (isReadOnlyPrompt(input.prompt)) {\n let tokens = 0;\n for (const span of loaded.flatMap((pointer) => spansOf(pointer, knownSecrets))) {\n const cost = estimateTokens(span.text);\n if (tokens + cost > LEAN_LIMITS.spanTokens) break;\n tokens += cost;\n capsules.push(span);\n }\n }\n const seen = await input.ledger.seen(input.sessionId, capsules);\n const emitted = capsules.filter((capsule) => !seen.has(capsule.key));\n return { text: emitted.map((capsule) => capsule.text).join(\"\\n\"), emitted };\n}\n\nfunction describePointer(pointer: LoadedPointer): string {\n const ranges = pointer.symbols.map((s) => `${s.name} L${s.startLine}-${s.endLine}`).join(\", \");\n const test = pointer.test ? `test: ${pointer.test}` : \"\";\n const detail = [ranges, test].filter(Boolean).join(\"; \");\n return detail ? `${pointer.path} (${detail})` : pointer.path;\n}\n\n/** Whole small files; otherwise the matched symbol spans. */\nfunction spansOf(pointer: LoadedPointer, knownSecrets: readonly string[]): ContextCapsule[] {\n const ranges =\n pointer.lines.length <= LEAN_LIMITS.wholeFileLines\n ? [{ start: 1, end: pointer.lines.length }]\n : pointer.symbols.map((s) => ({ start: s.startLine, end: s.endLine }));\n const language = extname(pointer.path).slice(1);\n return ranges.map(({ start, end }) => {\n const body = redactSourceText(pointer.lines.slice(start - 1, end).join(\"\\n\"), knownSecrets);\n const hash = createHash(\"sha1\").update(body).digest(\"hex\").slice(0, 12);\n return {\n key: `lean:span:${pointer.path}:${start}-${end}:${hash}`,\n text: `${pointer.path}:L${start}-${end}\\n\\`\\`\\`${language}\\n${body}\\n\\`\\`\\``,\n };\n });\n}\n\nconst FORCED_READ_ONLY =\n /\\b(without editing|without changing|don't change|do not change|don't edit|do not edit)\\b/i;\nconst QUESTION_START = /^(how|what|why|where|which|who|explain|describe|summarize)\\b/i;\nconst EDIT_VERB =\n /\\b(add|change|fix|implement|update|remove|delete|rename|refactor|write|create|make|edit|replace|move|migrate|support|build)\\b/i;\n\n/**\n * Whether a prompt asks a question without asking for edits. Conservative: a\n * false positive costs up to the span budget, a false negative costs nothing.\n */\nexport function isReadOnlyPrompt(prompt: string): boolean {\n const text = prompt.trim();\n if (text === \"\" || text.startsWith(\"/\")) return false;\n if (FORCED_READ_ONLY.test(text)) return true;\n const asks = QUESTION_START.test(text) || text.endsWith(\"?\");\n return asks && !EDIT_VERB.test(text);\n}\n","import { runLeanHook } from \"./lean.js\";\nimport { failSoft, readHookInput, writeStdout } from \"./stdio.js\";\n\n/** `codebuddy hooks lean`: read the hook payload, print the lean block, never fail. */\nexport async function runLeanHookCommand(): Promise<void> {\n await failSoft(async () => {\n await runLeanHook(await readHookInput(), writeStdout);\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/clients/adapters.ts"],"sourcesContent":["/**\n * Client adapters: the one place that says, per coding agent, where its rules\n * file lives, how its MCP config is written, and which lifecycle events\n * CodeBuddy can wire automatically. Everything else (rules install, hooks,\n * `codebuddy clients`) reads this table instead of branching on client names.\n *\n * Capabilities are what CodeBuddy actually implements today, not what the host\n * might support: a host with plugin events but no CodeBuddy bridge reports its\n * lifecycle as missing, with the reason in `gap`.\n */\n\nexport const AUTOMATION_MODES = [\"agent\", \"hooks\", \"mcp-only\", \"lean\"] as const;\nexport type AutomationMode = (typeof AUTOMATION_MODES)[number];\n\nexport const MODE_LABELS: Record<AutomationMode, string> = {\n agent: \"agent-driven\",\n hooks: \"hook-driven\",\n \"mcp-only\": \"MCP-only\",\n lean: \"Lean (token reduction)\",\n};\n\nexport const LIFECYCLE_EVENTS = [\"prompt\", \"edit\", \"turn-end\", \"compaction\"] as const;\nexport type LifecycleEvent = (typeof LIFECYCLE_EVENTS)[number];\n\n/**\n * Project-local JSON MCP config CodeBuddy writes, or a global config file it\n * edits only with `clients install --global` (otherwise `manual` is shown).\n */\nexport type McpTarget =\n | { file: string; format: \"mcpServers\" | \"opencode\" }\n | { global: \"codex\" | \"hermes\"; manual: string };\n\n/** Where a client's CodeBuddy lifecycle hooks are installed (see `hooks/install.ts`). */\nexport type HookSurface = \"claude-settings\" | \"opencode-plugin\" | \"hermes-plugin\";\n\nexport type ClientAdapter = {\n name: string;\n /** Host binary and the version (or source commit) this adapter was verified against. */\n host: { bin: string; verified: string };\n rulesFile: \"CLAUDE.md\" | \"AGENTS.md\";\n mcp: McpTarget;\n /** Lifecycle events CodeBuddy hooks handle automatically for this client. */\n hookEvents: readonly LifecycleEvent[];\n /** Set exactly when `hookEvents` is non-empty. */\n hooks?: HookSurface;\n /** Why lifecycle automation is missing (verified host surface, not certification). */\n gap?: string;\n /** Host-side caveat that is not a missing lifecycle event. */\n note?: string;\n /** How the host frames CodeBuddy's per-turn costs (source- or live-verified, not measured). */\n framing: string;\n /** The host drops an injected context block after the turn, so each prompt gets it in full. */\n ephemeralContext?: true;\n /** The host lists MCP tools by name and loads a schema only when a tool is used. */\n defersMcpSchemas?: true;\n};\n\nexport const CLIENT_IDS = [\"claude\", \"codex\", \"opencode\", \"freebuff\", \"hermes\"] as const;\nexport type ClientId = (typeof CLIENT_IDS)[number];\n\nexport const CLIENT_ADAPTERS: Record<ClientId, ClientAdapter> = {\n claude: {\n name: \"Claude Code\",\n host: { bin: \"claude\", verified: \"2.1.287\" },\n rulesFile: \"CLAUDE.md\",\n mcp: { file: \".mcp.json\", format: \"mcpServers\" },\n hookEvents: LIFECYCLE_EVENTS,\n hooks: \"claude-settings\",\n framing:\n \"UserPromptSubmit stdout is added to the prompt as hook context (wrapper is host-defined); unchanged lines are not resent; MCP schemas may be deferred by tool search\",\n defersMcpSchemas: true,\n },\n codex: {\n name: \"Codex\",\n host: { bin: \"codex\", verified: \"0.163.0\" },\n rulesFile: \"AGENTS.md\",\n mcp: { global: \"codex\", manual: \"codebuddy codex install (global ~/.codex/config.toml)\" },\n hookEvents: [],\n gap: \"no CodeBuddy hook adapter for Codex yet\",\n framing: \"agent mode only; AGENTS.md is part of the instructions every request\",\n },\n opencode: {\n name: \"OpenCode\",\n host: { bin: \"opencode\", verified: \"1.18.32\" },\n rulesFile: \"AGENTS.md\",\n // opencode.jsonc is used instead when it is the only one present.\n mcp: { file: \"opencode.json\", format: \"opencode\" },\n // Plugin bridge: chat.message (prompt), tool.execute.after (edit),\n // session.idle (turn-end), session.compacted.\n hookEvents: LIFECYCLE_EVENTS,\n hooks: \"opencode-plugin\",\n framing:\n \"block appended verbatim as a synthetic text part that stays in history; unchanged lines are not resent\",\n },\n freebuff: {\n name: \"Freebuff\",\n // Not installed; checked against source CodebuffAI/freebuff@f9ae64dd.\n host: { bin: \"freebuff\", verified: \"source f9ae64dd\" },\n rulesFile: \"AGENTS.md\",\n mcp: { file: \".agents/mcp.json\", format: \"mcpServers\" },\n hookEvents: [],\n gap: \"no lifecycle surface at CodebuffAI/freebuff@f9ae64dd: no user hook config, and the SDK run_file_change_hooks tool is a no-op (sdk/src/tools/run-file-change-hooks.ts)\",\n note: \"reads AGENTS.md, and CLAUDE.md only in a directory without one (common/src/constants/knowledge.ts)\",\n framing:\n \"agent mode only; AGENTS.md is sent as a fenced block under a fixed project-instructions header\",\n },\n hermes: {\n name: \"Hermes Agent\",\n host: { bin: \"hermes\", verified: \"0.17.0\" },\n rulesFile: \"AGENTS.md\",\n mcp: {\n global: \"hermes\",\n manual: \"add a codebuddy entry under mcp_servers in ~/.hermes/config.yaml\",\n },\n // Project Python plugin: pre_llm_call (prompt), post_tool_call (edit),\n // on_session_end (turn-end). Compaction has no plugin hook; the context is\n // resent in full every turn and the session ledger reset with it.\n hookEvents: LIFECYCLE_EVENTS,\n hooks: \"hermes-plugin\",\n ephemeralContext: true,\n framing:\n \"block appended to this turn's user message only and never persisted, so it is resent in full every turn\",\n note: \"hooks load only with HERMES_ENABLE_PROJECT_PLUGINS=1 and codebuddy in plugins.enabled of ~/.hermes/config.yaml; .hermes.md takes precedence over AGENTS.md\",\n },\n};\n\nexport function parseClient(value: string | undefined): ClientId {\n const client = value ?? \"claude\";\n if (!(CLIENT_IDS as readonly string[]).includes(client)) {\n throw new Error(`Unknown client \"${client}\". Use one of: ${CLIENT_IDS.join(\", \")}.`);\n }\n return client as ClientId;\n}\n\nexport function parseMode(value: string): AutomationMode {\n if (!(AUTOMATION_MODES as readonly string[]).includes(value)) {\n throw new Error(`Unknown mode \"${value}\". Use one of: ${AUTOMATION_MODES.join(\", \")}.`);\n }\n return value as AutomationMode;\n}\n\n/** Hook-driven mode needs every lifecycle event wired; partial coverage would drop captures. */\nexport function supportedModes(adapter: ClientAdapter): AutomationMode[] {\n return adapter.hookEvents.length === LIFECYCLE_EVENTS.length\n ? [...AUTOMATION_MODES]\n : [\"agent\", \"mcp-only\"];\n}\n\n/** Other clients that read the same rules file (a managed block there serves all of them). */\nexport function sharedRulesReaders(client: ClientId): ClientId[] {\n const file = CLIENT_ADAPTERS[client].rulesFile;\n return CLIENT_IDS.filter((id) => id !== client && CLIENT_ADAPTERS[id].rulesFile === file);\n}\n"],"mappings":";AAWO,IAAM,mBAAmB,CAAC,SAAS,SAAS,YAAY,MAAM;AAG9D,IAAM,cAA8C;AAAA,EACzD,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,MAAM;AACR;AAEO,IAAM,mBAAmB,CAAC,UAAU,QAAQ,YAAY,YAAY;AAoCpE,IAAM,aAAa,CAAC,UAAU,SAAS,YAAY,YAAY,QAAQ;AAGvE,IAAM,kBAAmD;AAAA,EAC9D,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,MAAM,EAAE,KAAK,UAAU,UAAU,UAAU;AAAA,IAC3C,WAAW;AAAA,IACX,KAAK,EAAE,MAAM,aAAa,QAAQ,aAAa;AAAA,IAC/C,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,SACE;AAAA,IACF,kBAAkB;AAAA,EACpB;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,EAAE,KAAK,SAAS,UAAU,UAAU;AAAA,IAC1C,WAAW;AAAA,IACX,KAAK,EAAE,QAAQ,SAAS,QAAQ,wDAAwD;AAAA,IACxF,YAAY,CAAC;AAAA,IACb,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM,EAAE,KAAK,YAAY,UAAU,UAAU;AAAA,IAC7C,WAAW;AAAA;AAAA,IAEX,KAAK,EAAE,MAAM,iBAAiB,QAAQ,WAAW;AAAA;AAAA;AAAA,IAGjD,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,SACE;AAAA,EACJ;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA;AAAA,IAEN,MAAM,EAAE,KAAK,YAAY,UAAU,kBAAkB;AAAA,IACrD,WAAW;AAAA,IACX,KAAK,EAAE,MAAM,oBAAoB,QAAQ,aAAa;AAAA,IACtD,YAAY,CAAC;AAAA,IACb,KAAK;AAAA,IACL,MAAM;AAAA,IACN,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,MAAM,EAAE,KAAK,UAAU,UAAU,SAAS;AAAA,IAC1C,WAAW;AAAA,IACX,KAAK;AAAA,MACH,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA;AAAA;AAAA;AAAA,IAIA,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,kBAAkB;AAAA,IAClB,SACE;AAAA,IACF,MAAM;AAAA,EACR;AACF;AAEO,SAAS,YAAY,OAAqC;AAC/D,QAAM,SAAS,SAAS;AACxB,MAAI,CAAE,WAAiC,SAAS,MAAM,GAAG;AACvD,UAAM,IAAI,MAAM,mBAAmB,MAAM,kBAAkB,WAAW,KAAK,IAAI,CAAC,GAAG;AAAA,EACrF;AACA,SAAO;AACT;AAEO,SAAS,UAAU,OAA+B;AACvD,MAAI,CAAE,iBAAuC,SAAS,KAAK,GAAG;AAC5D,UAAM,IAAI,MAAM,iBAAiB,KAAK,kBAAkB,iBAAiB,KAAK,IAAI,CAAC,GAAG;AAAA,EACxF;AACA,SAAO;AACT;AAGO,SAAS,eAAe,SAA0C;AACvE,SAAO,QAAQ,WAAW,WAAW,iBAAiB,SAClD,CAAC,GAAG,gBAAgB,IACpB,CAAC,SAAS,UAAU;AAC1B;AAGO,SAAS,mBAAmB,QAA8B;AAC/D,QAAM,OAAO,gBAAgB,MAAM,EAAE;AACrC,SAAO,WAAW,OAAO,CAAC,OAAO,OAAO,UAAU,gBAAgB,EAAE,EAAE,cAAc,IAAI;AAC1F;","names":[]}
|
package/dist/install-BXM56QCD.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
detectClient,
|
|
3
|
-
installClient,
|
|
4
|
-
setHooks,
|
|
5
|
-
uninstallClient
|
|
6
|
-
} from "./chunk-J4T6HHI4.js";
|
|
7
|
-
import "./chunk-W7GAXL6C.js";
|
|
8
|
-
import "./chunk-ITQO632W.js";
|
|
9
|
-
import "./chunk-Y7D7PUBE.js";
|
|
10
|
-
import "./chunk-KNR2P2UY.js";
|
|
11
|
-
import "./chunk-CG5B3LYG.js";
|
|
12
|
-
import "./chunk-PZ5AY32C.js";
|
|
13
|
-
export {
|
|
14
|
-
detectClient,
|
|
15
|
-
installClient,
|
|
16
|
-
setHooks,
|
|
17
|
-
uninstallClient
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=install-BXM56QCD.js.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runLeanHookCommand
|
|
3
|
-
} from "./chunk-UK6XTS2Y.js";
|
|
4
|
-
import "./chunk-FJOTP632.js";
|
|
5
|
-
import "./chunk-GKNYMNFM.js";
|
|
6
|
-
import "./chunk-TGFTKWOR.js";
|
|
7
|
-
import "./chunk-CAEPHLHC.js";
|
|
8
|
-
import "./chunk-TLWGZVPC.js";
|
|
9
|
-
import "./chunk-ITQO632W.js";
|
|
10
|
-
import "./chunk-Y7D7PUBE.js";
|
|
11
|
-
import "./chunk-AL6VBTNA.js";
|
|
12
|
-
import "./chunk-HPOR6YXA.js";
|
|
13
|
-
import "./chunk-KNR2P2UY.js";
|
|
14
|
-
import "./chunk-CG5B3LYG.js";
|
|
15
|
-
import "./chunk-PZ5AY32C.js";
|
|
16
|
-
export {
|
|
17
|
-
runLeanHookCommand
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=lean-command-7DFW32MA.js.map
|