@orquentra/codebuddy 2.4.1 → 2.6.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/README.md +25 -2
- package/dist/{chunk-ITGRANYW.js → chunk-6MKEIDX5.js} +2 -2
- package/dist/{chunk-K63EOA4B.js → chunk-776EQXDV.js} +25 -6
- package/dist/chunk-776EQXDV.js.map +1 -0
- package/dist/chunk-JFDRBPYF.js +99 -0
- package/dist/chunk-JFDRBPYF.js.map +1 -0
- package/dist/chunk-JPHJBCZB.js +99 -0
- package/dist/chunk-JPHJBCZB.js.map +1 -0
- package/dist/{chunk-IBMJYIEP.js → chunk-L5AYZC3F.js} +7 -7
- package/dist/{chunk-DSN5CEHO.js → chunk-N34XHWBA.js} +3 -3
- package/dist/{chunk-3ZELDCR3.js → chunk-PCE4STAY.js} +7 -7
- package/dist/{chunk-TI5TQD6D.js → chunk-QDBDYCM2.js} +2 -2
- package/dist/{chunk-5OWZVZSO.js → chunk-SYWJG5NA.js} +8 -165
- package/dist/chunk-SYWJG5NA.js.map +1 -0
- package/dist/chunk-U4HAJLIB.js +91 -0
- package/dist/chunk-U4HAJLIB.js.map +1 -0
- package/dist/{chunk-IE2OUO54.js → chunk-UFZD3JAA.js} +3 -3
- package/dist/chunk-UN57FKQX.js +4033 -0
- package/dist/chunk-UN57FKQX.js.map +1 -0
- package/dist/chunk-VAKBJV26.js +5112 -0
- package/dist/chunk-VAKBJV26.js.map +1 -0
- package/dist/chunk-XBJQKCLU.js +103 -0
- package/dist/chunk-XBJQKCLU.js.map +1 -0
- package/dist/cli/index.js +2 -2
- package/dist/cloud-7WM3YLI4.js +17 -0
- package/dist/{codebuddy-D2xJ6hFk.d.ts → codebuddy-DM-i0Fu5.d.ts} +49 -18
- package/dist/{config-S2TULOMK.js → config-KFEHLV6W.js} +3 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +34 -25
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/lean-command-C5YPFKHP.js +15 -0
- package/dist/local-OTOY56B4.js +13 -0
- package/dist/{main-BFDVQHKO.js → main-HFHLGID4.js} +1410 -245
- package/dist/main-HFHLGID4.js.map +1 -0
- package/dist/{memory-NIBFAMY4.js → memory-4RGVLPKG.js} +6 -6
- package/dist/operations-LOBAQRLO.js +24 -0
- package/dist/operations-LOBAQRLO.js.map +1 -0
- package/dist/{rerank-Q7MIFNKA.js → rerank-3P3C2UHN.js} +4 -4
- package/dist/{summaries-TSE44KUQ.js → summaries-ZBV5IN7Y.js} +6 -6
- package/dist/summaries-ZBV5IN7Y.js.map +1 -0
- package/dist/{zvec-F27AR357.js → zvec-MAPSJQSR.js} +3 -3
- package/dist/zvec-MAPSJQSR.js.map +1 -0
- package/package.json +6 -2
- package/dist/chunk-5OWZVZSO.js.map +0 -1
- package/dist/chunk-K63EOA4B.js.map +0 -1
- package/dist/chunk-OLFCOR4U.js +0 -8580
- package/dist/chunk-OLFCOR4U.js.map +0 -1
- package/dist/lean-command-D6TZJEFX.js +0 -13
- package/dist/main-BFDVQHKO.js.map +0 -1
- /package/dist/{chunk-ITGRANYW.js.map → chunk-6MKEIDX5.js.map} +0 -0
- /package/dist/{chunk-IBMJYIEP.js.map → chunk-L5AYZC3F.js.map} +0 -0
- /package/dist/{chunk-DSN5CEHO.js.map → chunk-N34XHWBA.js.map} +0 -0
- /package/dist/{chunk-3ZELDCR3.js.map → chunk-PCE4STAY.js.map} +0 -0
- /package/dist/{chunk-TI5TQD6D.js.map → chunk-QDBDYCM2.js.map} +0 -0
- /package/dist/{chunk-IE2OUO54.js.map → chunk-UFZD3JAA.js.map} +0 -0
- /package/dist/{config-S2TULOMK.js.map → cloud-7WM3YLI4.js.map} +0 -0
- /package/dist/{lean-command-D6TZJEFX.js.map → config-KFEHLV6W.js.map} +0 -0
- /package/dist/{summaries-TSE44KUQ.js.map → lean-command-C5YPFKHP.js.map} +0 -0
- /package/dist/{zvec-F27AR357.js.map → local-OTOY56B4.js.map} +0 -0
- /package/dist/{memory-NIBFAMY4.js.map → memory-4RGVLPKG.js.map} +0 -0
- /package/dist/{rerank-Q7MIFNKA.js.map → rerank-3P3C2UHN.js.map} +0 -0
package/README.md
CHANGED
|
@@ -18,17 +18,40 @@ codebuddy
|
|
|
18
18
|
|
|
19
19
|
The setup wizard configures the project and agent integration. Project context
|
|
20
20
|
works without a database; semantic memory recall optionally uses PostgreSQL
|
|
21
|
-
with pgvector and a
|
|
21
|
+
with pgvector and a local Hugging Face embedding model (no token needed).
|
|
22
|
+
|
|
23
|
+
Run setup inside the project you want CodeBuddy to read. Claude Desktop
|
|
24
|
+
registration saves that folder's absolute path, so restarting the desktop app
|
|
25
|
+
does not change which project is indexed. `serve` requires an initialized
|
|
26
|
+
project (`.codebuddy/config.json`) and refuses filesystem roots and home folders.
|
|
27
|
+
Older desktop registrations without a project path can be repaired by running
|
|
28
|
+
`codebuddy claude install` from the initialized project folder, then restarting
|
|
29
|
+
Claude Desktop.
|
|
22
30
|
|
|
23
31
|
## What it does
|
|
24
32
|
|
|
25
33
|
- **Task context:** relevant files, symbols, code excerpts, nearby dependencies,
|
|
26
34
|
and covering tests within a token budget.
|
|
27
|
-
- **Project memory:** decisions, rules, and handoffs stored as readable Markdown
|
|
35
|
+
- **Project memory:** decisions, rules, and handoffs stored as readable Markdown,
|
|
36
|
+
recalled on every prompt by hybrid lexical + vector search
|
|
37
|
+
([how it works](docs/semantic-memory.md)).
|
|
28
38
|
- **Change guidance:** plans, policies, incident history, and risk signals.
|
|
29
39
|
- **Optional retrieval:** zvec semantic search, Graphify architecture maps, and
|
|
30
40
|
a bring-your-own LLM layer.
|
|
31
41
|
|
|
42
|
+
## See what it does
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
codebuddy dashboard
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This opens a live local page showing:
|
|
49
|
+
- every prompt and the memories recalled for it;
|
|
50
|
+
- MCP tool calls with their latency;
|
|
51
|
+
- the tokens delivered to your agent.
|
|
52
|
+
|
|
53
|
+
It updates as you work, and the traces never leave your machine.
|
|
54
|
+
|
|
32
55
|
## Try it without a database
|
|
33
56
|
|
|
34
57
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readConfigFile
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-776EQXDV.js";
|
|
4
4
|
|
|
5
5
|
// src/llm/config.ts
|
|
6
6
|
var BASE_URL = {
|
|
@@ -60,4 +60,4 @@ export {
|
|
|
60
60
|
resolveLlmConfig,
|
|
61
61
|
redactLlmKeys
|
|
62
62
|
};
|
|
63
|
-
//# sourceMappingURL=chunk-
|
|
63
|
+
//# sourceMappingURL=chunk-6MKEIDX5.js.map
|
|
@@ -95,8 +95,15 @@ var fileSchema = z.object({
|
|
|
95
95
|
namespace: z.string().min(1).optional(),
|
|
96
96
|
tokenBudget: z.number().int().positive().optional(),
|
|
97
97
|
provider: z.object({
|
|
98
|
-
type: z.
|
|
99
|
-
apiKey: z.string().min(1).optional()
|
|
98
|
+
type: z.enum(["local", "huggingface", "openrouter"]).default("local"),
|
|
99
|
+
apiKey: z.string().min(1).optional(),
|
|
100
|
+
model: z.string().min(1).optional(),
|
|
101
|
+
relevanceFloor: z.number().min(-1).max(1).optional()
|
|
102
|
+
}).optional(),
|
|
103
|
+
/** Optional cross-encoder that orders recalled facts (off by default). */
|
|
104
|
+
rerank: z.object({
|
|
105
|
+
provider: z.enum(["off", "local", "huggingface", "openrouter"]).default("off"),
|
|
106
|
+
model: z.string().min(1).optional()
|
|
100
107
|
}).optional(),
|
|
101
108
|
plan: z.object({
|
|
102
109
|
policy: z.enum(PLAN_POLICIES).default("suggest")
|
|
@@ -175,7 +182,7 @@ async function initConfigFile(cwd = process.cwd()) {
|
|
|
175
182
|
// Default to the folder name (sanitized) so a fresh scaffold agrees with
|
|
176
183
|
// doctor's expected default instead of warning immediately after init.
|
|
177
184
|
namespace: defaultNamespaceForCwd(cwd),
|
|
178
|
-
provider: { type: "
|
|
185
|
+
provider: { type: "local" },
|
|
179
186
|
tokenBudget: 4e3
|
|
180
187
|
};
|
|
181
188
|
await writeFile(path, `${JSON.stringify(template, null, 2)}
|
|
@@ -227,8 +234,10 @@ async function loadRuntimeConfig(cwd = process.cwd(), options = {}) {
|
|
|
227
234
|
namespace: process.env.CODEBUDDY_NAMESPACE ?? file.namespace ?? defaultNamespaceForCwd(projectRoot),
|
|
228
235
|
projectRoot,
|
|
229
236
|
provider: {
|
|
230
|
-
type: "
|
|
231
|
-
...hfToken ? { apiKey: hfToken } : {}
|
|
237
|
+
type: file.provider?.type ?? "local",
|
|
238
|
+
...hfToken ? { apiKey: hfToken } : {},
|
|
239
|
+
...file.provider?.model ? { model: file.provider.model } : {},
|
|
240
|
+
...file.provider?.relevanceFloor !== void 0 ? { relevanceFloor: file.provider.relevanceFloor } : {}
|
|
232
241
|
},
|
|
233
242
|
...file.tokenBudget ? { tokenBudget: file.tokenBudget } : {},
|
|
234
243
|
...file.graphify ? { graphify: file.graphify } : {}
|
|
@@ -355,6 +364,15 @@ async function setLlmEnabled(enabled, options = {}, cwd = process.cwd()) {
|
|
|
355
364
|
await writeAtomic(path, `${JSON.stringify(raw, null, 2)}
|
|
356
365
|
`, { mode: 384 });
|
|
357
366
|
}
|
|
367
|
+
async function setConfigBlock(key, value, cwd = process.cwd()) {
|
|
368
|
+
await initConfigFile(cwd);
|
|
369
|
+
const path = configPath(cwd);
|
|
370
|
+
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
371
|
+
raw[key] = value;
|
|
372
|
+
fileSchema.parse(raw);
|
|
373
|
+
await writeAtomic(path, `${JSON.stringify(raw, null, 2)}
|
|
374
|
+
`, { mode: 384 });
|
|
375
|
+
}
|
|
358
376
|
function redactSecrets(value) {
|
|
359
377
|
if (typeof value === "string") {
|
|
360
378
|
let redacted = value;
|
|
@@ -411,6 +429,7 @@ export {
|
|
|
411
429
|
setPlanPolicy,
|
|
412
430
|
setZvecEnabled,
|
|
413
431
|
setLlmEnabled,
|
|
432
|
+
setConfigBlock,
|
|
414
433
|
redactSecrets
|
|
415
434
|
};
|
|
416
|
-
//# sourceMappingURL=chunk-
|
|
435
|
+
//# sourceMappingURL=chunk-776EQXDV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/markdown-store-fs.ts","../src/core/config-file.ts"],"sourcesContent":["import { lstat, open, rename } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\n\n/**\n * Filesystem helpers shared by the Markdown-backed stores\n * (`MemoryFileStore`, `PlanFileStore`). These enforce the security\n * invariants that keep `.codebuddy/` writes inside the repository root:\n * no path traversal, no symlinked path components, and crash-safe atomic\n * writes. Keep this module small and audited; both stores depend on it.\n */\n\n/**\n * Resolve `path` and assert it stays inside `root`. Returns the absolute\n * path. Throws if the resolved path escapes the root.\n */\nexport function resolveWithinRoot(root: string, path: string): string {\n const absoluteRoot = resolve(root);\n const absolute = resolve(path);\n const relativePath = relative(absoluteRoot, absolute);\n if (relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || relativePath === \"\") {\n throw new Error(`Path escapes repository root: ${path}`);\n }\n return absolute;\n}\n\n/**\n * Walk each path component between `root` and `target` and refuse if any\n * component is a symbolic link. Prevents a planted symlink from\n * redirecting a write outside the repository. Missing components are\n * fine — the caller creates them.\n */\nexport async function assertNoSymlinkBetween(root: string, target: string): Promise<void> {\n const absoluteRoot = resolve(root);\n const absoluteTarget = resolve(target);\n const relativeTarget = relative(absoluteRoot, absoluteTarget);\n if (relativeTarget === \"..\" || relativeTarget.startsWith(`..${sep}`)) {\n throw new Error(`Path escapes repository root: ${target}`);\n }\n\n let current = absoluteRoot;\n const parts = relativeTarget.split(sep).filter(Boolean);\n for (const part of parts) {\n current = join(current, part);\n try {\n const info = await lstat(current);\n if (info.isSymbolicLink()) {\n throw new Error(`Refusing to use symlinked path: ${current}`);\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw error;\n }\n }\n}\n\n/**\n * Write `document` to `path` atomically: write to a unique temp file with\n * an exclusive (`wx`) create, fsync, then rename into place. A crash mid-\n * write leaves either the old file or nothing — never a partial file.\n *\n * `mode` sets permissions on NEWLY created files (an existing file keeps its\n * mode, matching rename semantics). Secrets-bearing configs should use 0o600.\n */\nexport async function writeAtomic(\n path: string,\n document: string,\n options?: { mode?: number },\n): Promise<void> {\n const temporaryPath = `${path}.${process.pid}.${Date.now()}.tmp`;\n const handle = await open(temporaryPath, \"wx\", options?.mode ?? 0o600);\n try {\n await handle.writeFile(document, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(temporaryPath, path);\n}\n\n/**\n * Split a Markdown document with leading YAML front matter into its\n * front-matter text and body. Throws on malformed or unterminated\n * front matter so hand-edited files fail loudly rather than silently\n * losing fields.\n */\nexport function parseFrontMatter(document: string): { frontMatter: string; content: string } {\n if (!document.startsWith(\"---\\n\")) {\n throw new Error(\"Markdown file must start with YAML front matter.\");\n }\n const closingIndex = document.indexOf(\"\\n---\\n\", 4);\n if (closingIndex === -1) {\n throw new Error(\"Markdown file has unterminated YAML front matter.\");\n }\n return {\n frontMatter: document.slice(4, closingIndex),\n content: document.slice(closingIndex + 5),\n };\n}\n\n/**\n * Compose a Markdown document from serialized front-matter YAML and a\n * body. Ensures exactly one trailing newline on the body so files are\n * stable across rewrites (minimal git diffs).\n */\nexport function composeMarkdown(frontMatterYaml: string, body: string): string {\n const trimmedFrontMatter = frontMatterYaml.trimEnd();\n const content = body.endsWith(\"\\n\") ? body : `${body}\\n`;\n return `---\\n${trimmedFrontMatter}\\n---\\n${content}`;\n}\n","import { constants } from \"node:fs\";\nimport { access, chmod, mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { z } from \"zod\";\nimport { writeAtomic } from \"./markdown-store-fs.js\";\nimport type { CodeBuddyConfig } from \"./types.js\";\n\nexport const CONFIG_DIR = \".codebuddy\";\nexport const CONFIG_FILE = \"config.json\";\nexport const POLICY_FILE = \"policies.yaml\";\n\nconst PROJECT_GITIGNORE = [\n \"# CodeBuddy local-only files\",\n \"/config.json\",\n \"/cache/\",\n \"/memory/review/\",\n \"/memory/review-archive/\",\n \"/plans/.locks/\",\n \"/memory/.locks/\",\n \"\",\n \"# Keep these reviewable project artifacts shareable by default:\",\n \"# /memory/facts/*.md\",\n \"# /memory/summaries/*.md\",\n \"# /plans/*.md\",\n \"# If your project memory is private, ignore /memory/ in the repo root .gitignore.\",\n \"\",\n].join(\"\\n\");\n\nconst DEFAULT_POLICY_FILE = [\n \"rules:\",\n \" - id: auth-sensitive\",\n ' pattern: \"src/auth/**\"',\n ' message: \"Auth changes need careful review.\"',\n \" weight: 70\",\n \"\",\n].join(\"\\n\");\n\n/**\n * Plan policy controls how strongly CodeBuddy nudges agents toward writing\n * a plan before substantive work:\n * - off: plans are never suggested or required.\n * - suggest: agents are reminded to plan, but small tasks stay lightweight (default).\n * - required: agents should refuse substantive changes without an active plan.\n */\nexport const PLAN_POLICIES = [\"off\", \"suggest\", \"required\"] as const;\nexport type PlanPolicy = (typeof PLAN_POLICIES)[number];\n\nconst fileSchema = z.object({\n postgresUrl: z.string().min(1).optional(),\n namespace: z.string().min(1).optional(),\n tokenBudget: z.number().int().positive().optional(),\n provider: z\n .object({\n type: z.enum([\"local\", \"huggingface\", \"openrouter\"]).default(\"local\"),\n apiKey: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n relevanceFloor: z.number().min(-1).max(1).optional(),\n })\n .optional(),\n /** Optional cross-encoder that orders recalled facts (off by default). */\n rerank: z\n .object({\n provider: z.enum([\"off\", \"local\", \"huggingface\", \"openrouter\"]).default(\"off\"),\n model: z.string().min(1).optional(),\n })\n .optional(),\n plan: z\n .object({\n policy: z.enum(PLAN_POLICIES).default(\"suggest\"),\n })\n .optional(),\n graphify: z\n .object({\n enabled: z.boolean(),\n graphPath: z.string().min(1).optional(),\n })\n .optional(),\n /**\n * Optional hosted small-model layer (bring your own key). Keys are read from\n * the environment only, never from this file (`src/llm/config.ts`).\n */\n llm: z\n .object({\n enabled: z.boolean(),\n provider: z.enum([\"groq\", \"openrouter\"]).optional(),\n model: z.string().min(1).optional(),\n fallback: z\n .object({ provider: z.enum([\"groq\", \"openrouter\"]), model: z.string().min(1) })\n .optional(),\n timeoutMs: z.number().int().min(100).max(5_000).optional(),\n dailyTokenBudget: z.number().int().positive().optional(),\n features: z\n .object({\n summaries: z.boolean().optional(),\n rerank: z.boolean().optional(),\n memory: z.boolean().optional(),\n })\n .optional(),\n })\n .optional(),\n /** Optional local semantic retrieval via zvec-grep (`zg`). Off unless enabled. */\n zvec: z\n .object({\n enabled: z.boolean(),\n binary: z.string().min(1).optional(),\n timeoutMs: z.number().int().positive().max(10_000).optional(),\n })\n .optional(),\n});\n\nexport type CodeBuddyFileConfig = z.infer<typeof fileSchema>;\n\nexport type ConfigPermissionReport = {\n path: string;\n exists: boolean;\n mode: string | null;\n ok: boolean;\n warning?: string;\n};\n\nexport type ProjectScaffoldReport = {\n codebuddyDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n memoryFactsDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n memorySummariesDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n plansDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n gitignore: { path: string; exists: boolean; ok: boolean; warning?: string };\n policies: { path: string; exists: boolean; ok: boolean; warning?: string };\n};\n\nexport function configPath(cwd = process.cwd()): string {\n return join(cwd, CONFIG_DIR, CONFIG_FILE);\n}\n\nexport async function initProjectScaffold(cwd = process.cwd()): Promise<{\n codebuddyDir: string;\n gitignorePath: string;\n policyPath: string;\n}> {\n const codebuddyDir = join(cwd, CONFIG_DIR);\n await mkdir(join(codebuddyDir, \"memory\", \"facts\"), { recursive: true, mode: 0o700 });\n await mkdir(join(codebuddyDir, \"memory\", \"summaries\"), { recursive: true, mode: 0o700 });\n await mkdir(join(codebuddyDir, \"plans\"), { recursive: true, mode: 0o700 });\n\n const gitignorePath = join(codebuddyDir, \".gitignore\");\n try {\n await access(gitignorePath, constants.F_OK);\n let gitignore = await readFile(gitignorePath, \"utf8\");\n const lines = new Set(gitignore.split(/\\r?\\n/));\n const missing = [\"/memory/review/\", \"/memory/review-archive/\"].filter(\n (line) => !lines.has(line),\n );\n if (missing.length > 0) {\n gitignore = `${gitignore.trimEnd()}\\n${missing.join(\"\\n\")}\\n`;\n await writeFile(gitignorePath, gitignore, { mode: 0o644 });\n }\n } catch {\n await writeFile(gitignorePath, PROJECT_GITIGNORE, { mode: 0o644 });\n }\n\n const policyPath = join(codebuddyDir, POLICY_FILE);\n try {\n await access(policyPath, constants.F_OK);\n } catch {\n await writeFile(policyPath, DEFAULT_POLICY_FILE, { mode: 0o644 });\n }\n\n return { codebuddyDir, gitignorePath, policyPath };\n}\n\nexport async function initConfigFile(\n cwd = process.cwd(),\n): Promise<{ path: string; created: boolean }> {\n const path = configPath(cwd);\n await initProjectScaffold(cwd);\n try {\n await access(path, constants.F_OK);\n await chmod(path, 0o600);\n return { path, created: false };\n } catch {\n const template: CodeBuddyFileConfig = {\n postgresUrl: process.env.DATABASE_URL ?? \"postgres://localhost:5432/codebuddy\",\n // Default to the folder name (sanitized) so a fresh scaffold agrees with\n // doctor's expected default instead of warning immediately after init.\n namespace: defaultNamespaceForCwd(cwd),\n provider: { type: \"local\" },\n tokenBudget: 4000,\n };\n await writeFile(path, `${JSON.stringify(template, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600);\n return { path, created: true };\n }\n}\n\n/** Persist a complete project config without changing its private permissions. */\nexport async function writeConfigFile(\n config: CodeBuddyFileConfig,\n cwd = process.cwd(),\n): Promise<{ path: string }> {\n const path = configPath(cwd);\n await initProjectScaffold(cwd);\n await writeFile(path, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600);\n return { path };\n}\n\nexport async function readConfigFile(cwd = process.cwd()): Promise<CodeBuddyFileConfig> {\n const path = configPath(cwd);\n try {\n const raw = await readFile(path, \"utf8\");\n return fileSchema.parse(JSON.parse(raw));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n}\n\n/**\n * The ONE namespace-resolution rule, shared by the DB/MCP runtime, every\n * file-based CLI command, and doctor. Precedence:\n * 1. CODEBUDDY_NAMESPACE env\n * 2. `.codebuddy/config.json` `namespace`\n * 3. the current folder name (sanitized) — matching what doctor reports as\n * the expected default.\n */\nexport function sanitizeNamespace(value: string): string {\n return value.replace(/[^a-zA-Z0-9_.:-]/g, \"-\").slice(0, 128);\n}\n\n/** Folder-name fallback used when neither env nor config specify a namespace. */\nexport function defaultNamespaceForCwd(cwd = process.cwd()): string {\n return sanitizeNamespace(basename(cwd)) || \"default\";\n}\n\nexport async function resolveNamespaceFrom(cwd = process.cwd()): Promise<string> {\n if (process.env.CODEBUDDY_NAMESPACE) return process.env.CODEBUDDY_NAMESPACE;\n const file = await readConfigFile(cwd);\n return file.namespace ?? defaultNamespaceForCwd(cwd);\n}\n\nexport const MISSING_DATABASE_URL =\n \"DATABASE_URL or .codebuddy/config.json postgresUrl is required.\";\n\n/**\n * `requireDatabase: false` (the MCP server) returns an empty `postgresUrl`\n * instead of throwing, so file-based tools still run without PostgreSQL;\n * `createRuntime` rejects the empty URL with the same message.\n */\nexport async function loadRuntimeConfig(\n cwd = process.cwd(),\n options: { requireDatabase?: boolean } = {},\n): Promise<CodeBuddyConfig> {\n const projectRoot = process.env.CODEBUDDY_PROJECT_ROOT ?? cwd;\n const file = await readConfigFile(projectRoot);\n const postgresUrl = process.env.DATABASE_URL ?? file.postgresUrl ?? \"\";\n if (!postgresUrl && options.requireDatabase !== false) {\n throw new Error(MISSING_DATABASE_URL);\n }\n const hfToken = process.env.HF_TOKEN ?? file.provider?.apiKey;\n return {\n postgresUrl,\n namespace:\n process.env.CODEBUDDY_NAMESPACE ?? file.namespace ?? defaultNamespaceForCwd(projectRoot),\n projectRoot,\n provider: {\n type: file.provider?.type ?? \"local\",\n ...(hfToken ? { apiKey: hfToken } : {}),\n ...(file.provider?.model ? { model: file.provider.model } : {}),\n ...(file.provider?.relevanceFloor !== undefined\n ? { relevanceFloor: file.provider.relevanceFloor }\n : {}),\n },\n ...(file.tokenBudget ? { tokenBudget: file.tokenBudget } : {}),\n ...(file.graphify ? { graphify: file.graphify } : {}),\n };\n}\n\nexport async function checkConfigPermissions(cwd = process.cwd()): Promise<ConfigPermissionReport> {\n const path = configPath(cwd);\n try {\n const info = await stat(path);\n const modeNumber = info.mode & 0o777;\n const mode = modeNumber.toString(8).padStart(4, \"0\");\n const ok = modeNumber === 0o600;\n return {\n path,\n exists: true,\n mode,\n ok,\n ...(ok ? {} : { warning: \".codebuddy/config.json should be chmod 0600.\" }),\n };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { path, exists: false, mode: null, ok: true };\n }\n throw error;\n }\n}\n\nexport async function checkProjectScaffold(cwd = process.cwd()): Promise<ProjectScaffoldReport> {\n const codebuddyDir = join(cwd, CONFIG_DIR);\n const memoryFactsDir = join(codebuddyDir, \"memory\", \"facts\");\n const memorySummariesDir = join(codebuddyDir, \"memory\", \"summaries\");\n const plansDir = join(codebuddyDir, \"plans\");\n const gitignorePath = join(codebuddyDir, \".gitignore\");\n const policyPath = join(codebuddyDir, POLICY_FILE);\n\n const [\n codebuddyDirExists,\n memoryFactsDirExists,\n memorySummariesDirExists,\n plansDirExists,\n gitignoreExists,\n policiesExist,\n ] = await Promise.all([\n pathExists(codebuddyDir),\n pathExists(memoryFactsDir),\n pathExists(memorySummariesDir),\n pathExists(plansDir),\n pathExists(gitignorePath),\n pathExists(policyPath),\n ]);\n\n return {\n codebuddyDir: {\n path: codebuddyDir,\n exists: codebuddyDirExists,\n ok: codebuddyDirExists,\n ...(codebuddyDirExists\n ? {}\n : { warning: \"Run `codebuddy use` or `codebuddy init` to create the project scaffold.\" }),\n },\n memoryFactsDir: {\n path: memoryFactsDir,\n exists: memoryFactsDirExists,\n ok: memoryFactsDirExists,\n ...(memoryFactsDirExists ? {} : { warning: \"Missing .codebuddy/memory/facts directory.\" }),\n },\n memorySummariesDir: {\n path: memorySummariesDir,\n exists: memorySummariesDirExists,\n ok: memorySummariesDirExists,\n ...(memorySummariesDirExists\n ? {}\n : { warning: \"Missing .codebuddy/memory/summaries directory.\" }),\n },\n plansDir: {\n path: plansDir,\n exists: plansDirExists,\n ok: plansDirExists,\n ...(plansDirExists ? {} : { warning: \"Missing .codebuddy/plans directory.\" }),\n },\n gitignore: {\n path: gitignorePath,\n exists: gitignoreExists,\n ok: gitignoreExists,\n ...(gitignoreExists ? {} : { warning: \"Missing .codebuddy/.gitignore.\" }),\n },\n policies: {\n path: policyPath,\n exists: policiesExist,\n ok: policiesExist,\n ...(policiesExist\n ? {}\n : {\n warning:\n \"Missing .codebuddy/policies.yaml. Run `codebuddy use` or `codebuddy init` to scaffold it.\",\n }),\n },\n };\n}\n\n/** Read the plan policy, honoring CODEBUDDY_PLAN_POLICY then the config file. */\nexport async function loadPlanPolicy(cwd = process.cwd()): Promise<PlanPolicy> {\n const fromEnv = process.env.CODEBUDDY_PLAN_POLICY;\n if (fromEnv && (PLAN_POLICIES as readonly string[]).includes(fromEnv)) {\n return fromEnv as PlanPolicy;\n }\n const file = await readConfigFile(cwd);\n return file.plan?.policy ?? \"suggest\";\n}\n\n/** Persist the plan policy into `.codebuddy/config.json`, creating it if needed. */\nexport async function setPlanPolicy(policy: PlanPolicy, cwd = process.cwd()): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw.plan = { ...(raw.plan as Record<string, unknown> | undefined), policy };\n // Atomic rewrite: this file holds the postgresUrl and provider apiKey.\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/** Turn optional zvec-grep retrieval on or off in `.codebuddy/config.json`. */\nexport async function setZvecEnabled(enabled: boolean, cwd = process.cwd()): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw.zvec = { ...(raw.zvec as Record<string, unknown> | undefined), enabled };\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/** Turn the BYOK LLM layer on or off; never writes a key (keys live in the environment). */\nexport async function setLlmEnabled(\n enabled: boolean,\n options: { provider?: \"groq\" | \"openrouter\"; model?: string } = {},\n cwd = process.cwd(),\n): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw.llm = {\n ...(raw.llm as Record<string, unknown> | undefined),\n enabled,\n ...(options.provider ? { provider: options.provider } : {}),\n ...(options.model ? { model: options.model } : {}),\n };\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/**\n * Replace one top-level block of `.codebuddy/config.json` (`provider`,\n * `rerank`), keeping every other key and the private file mode. The result\n * must still parse, so a bad value never reaches disk.\n */\nexport async function setConfigBlock(\n key: \"provider\" | \"rerank\",\n value: Record<string, unknown>,\n cwd = process.cwd(),\n): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw[key] = value;\n fileSchema.parse(raw);\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport function redactSecrets(value: unknown): unknown {\n if (typeof value === \"string\") {\n let redacted = value;\n // Provider keys (HF, BYOK LLM) literally anywhere in a string.\n for (const key of [\n process.env.HF_TOKEN,\n process.env.GROQ_API_KEY,\n process.env.OPENROUTER_API_KEY,\n process.env.CODEBUDDY_LLM_API_KEY,\n ]) {\n if (key) redacted = redacted.split(key).join(\"[REDACTED]\");\n }\n // Credentials embedded in connection URLs (postgres://user:pass@host…).\n // The password — not the username — is the secret.\n redacted = redacted.replace(\n /(\\b[a-z][a-z0-9+.-]*:\\/\\/[^:/\\s@]+:)[^/\\s@]+(@)/gi,\n \"$1[REDACTED]$2\",\n );\n return redacted;\n }\n if (Array.isArray(value)) return value.map(redactSecrets);\n if (!value || typeof value !== \"object\") return value;\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, nested]) => [\n key,\n /token|apiKey|authorization|secret/i.test(key) ? \"[REDACTED]\" : redactSecrets(nested),\n ]),\n );\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["join","join"]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EMBEDDING_DIMENSIONS
|
|
3
|
+
} from "./chunk-JPHJBCZB.js";
|
|
4
|
+
|
|
5
|
+
// src/providers/cloud.ts
|
|
6
|
+
var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
7
|
+
var HF_ROUTER_BASE_URL = "https://router.huggingface.co/hf-inference/models";
|
|
8
|
+
async function postJson(url, apiKey, body, timeoutMs) {
|
|
9
|
+
const response = await fetch(url, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
|
|
12
|
+
body: JSON.stringify(body),
|
|
13
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
14
|
+
});
|
|
15
|
+
const text = await response.text();
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
let message = text.slice(0, 200);
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(text);
|
|
20
|
+
message = typeof parsed.error === "string" ? parsed.error : parsed.error?.message ?? message;
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
throw new Error(
|
|
24
|
+
`${new URL(url).host} returned ${response.status}: ${message.split(apiKey).join("[REDACTED]")}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return JSON.parse(text);
|
|
28
|
+
}
|
|
29
|
+
var OpenRouterEmbeddingProvider = class {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.space = {
|
|
33
|
+
model: options.model,
|
|
34
|
+
revision: "hosted",
|
|
35
|
+
schema: `openrouter-embeddings-d${EMBEDDING_DIMENSIONS}`,
|
|
36
|
+
dimensions: EMBEDDING_DIMENSIONS
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
options;
|
|
40
|
+
space;
|
|
41
|
+
async embed(request) {
|
|
42
|
+
const input = typeof request.input === "string" ? [request.input] : request.input;
|
|
43
|
+
const json = await postJson(
|
|
44
|
+
`${this.options.baseUrl ?? OPENROUTER_BASE_URL}/embeddings`,
|
|
45
|
+
this.options.apiKey,
|
|
46
|
+
{ model: this.space.model, input, dimensions: EMBEDDING_DIMENSIONS },
|
|
47
|
+
this.options.timeoutMs ?? 1e4
|
|
48
|
+
);
|
|
49
|
+
const rows = [...json.data ?? []].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
50
|
+
const vectors = rows.map((row) => row.embedding ?? []);
|
|
51
|
+
const width = vectors[0]?.length ?? 0;
|
|
52
|
+
if (vectors.length !== input.length || width !== EMBEDDING_DIMENSIONS) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`OpenRouter model ${this.space.model} returned ${vectors.length} vector(s) of ${width} dimensions; CodeBuddy stores ${EMBEDDING_DIMENSIONS}. Pick a model that supports \`dimensions: ${EMBEDDING_DIMENSIONS}\` (for example openai/text-embedding-3-small).`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return { ...this.space, vectors };
|
|
58
|
+
}
|
|
59
|
+
async generateText() {
|
|
60
|
+
throw new Error("The OpenRouter embedding provider only embeds.");
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
async function rerankOpenRouter(options, query, documents) {
|
|
64
|
+
const json = await postJson(
|
|
65
|
+
`${options.baseUrl ?? OPENROUTER_BASE_URL}/rerank`,
|
|
66
|
+
options.apiKey,
|
|
67
|
+
{ model: options.model, query, documents, top_n: documents.length },
|
|
68
|
+
options.timeoutMs ?? 2e3
|
|
69
|
+
);
|
|
70
|
+
const scores = documents.map(() => Number.NEGATIVE_INFINITY);
|
|
71
|
+
for (const result of json.results ?? []) {
|
|
72
|
+
if (result.index >= 0 && result.index < scores.length)
|
|
73
|
+
scores[result.index] = result.relevance_score;
|
|
74
|
+
}
|
|
75
|
+
return scores;
|
|
76
|
+
}
|
|
77
|
+
async function rerankHuggingFace(options, query, documents) {
|
|
78
|
+
const json = await postJson(
|
|
79
|
+
`${options.baseUrl ?? HF_ROUTER_BASE_URL}/${options.model}`,
|
|
80
|
+
options.apiKey,
|
|
81
|
+
{ inputs: documents.map((document) => ({ text: query, text_pair: document })) },
|
|
82
|
+
options.timeoutMs ?? 2e3
|
|
83
|
+
);
|
|
84
|
+
if (!Array.isArray(json) || json.length !== documents.length) {
|
|
85
|
+
throw new Error(`Hugging Face reranker ${options.model} returned an unexpected shape.`);
|
|
86
|
+
}
|
|
87
|
+
return json.map(
|
|
88
|
+
(item) => (Array.isArray(item) ? item[0]?.score : item.score) ?? Number.NEGATIVE_INFINITY
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
OPENROUTER_BASE_URL,
|
|
94
|
+
HF_ROUTER_BASE_URL,
|
|
95
|
+
OpenRouterEmbeddingProvider,
|
|
96
|
+
rerankOpenRouter,
|
|
97
|
+
rerankHuggingFace
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-JFDRBPYF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/cloud.ts"],"sourcesContent":["import type {\n EmbeddingRequest,\n EmbeddingResponse,\n ModelProvider,\n TextGenerationResponse,\n} from \"./adapter.js\";\nimport type { EmbeddingSpace } from \"./embedding-space.js\";\nimport { EMBEDDING_DIMENSIONS } from \"./models.js\";\n\n/**\n * Hosted alternatives to the local models. Keys come from the environment\n * (OPENROUTER_API_KEY, HF_TOKEN) and never appear in errors.\n */\n\nexport const OPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\";\nexport const HF_ROUTER_BASE_URL = \"https://router.huggingface.co/hf-inference/models\";\n\n/** Fetch JSON with a timeout; an error names the status and the provider's message, never the key. */\nasync function postJson(\n url: string,\n apiKey: string,\n body: unknown,\n timeoutMs: number,\n): Promise<unknown> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { authorization: `Bearer ${apiKey}`, \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeoutMs),\n });\n const text = await response.text();\n if (!response.ok) {\n let message = text.slice(0, 200);\n try {\n const parsed = JSON.parse(text) as { error?: string | { message?: string } };\n message =\n typeof parsed.error === \"string\" ? parsed.error : (parsed.error?.message ?? message);\n } catch {\n // Not JSON: keep the raw excerpt.\n }\n throw new Error(\n `${new URL(url).host} returned ${response.status}: ${message.split(apiKey).join(\"[REDACTED]\")}`,\n );\n }\n return JSON.parse(text);\n}\n\n/**\n * OpenRouter's OpenAI-compatible embeddings endpoint, asking for 384\n * dimensions (Matryoshka models such as openai/text-embedding-3-small honour\n * it). A model that cannot return 384-d vectors fails with a clear message.\n */\nexport class OpenRouterEmbeddingProvider implements ModelProvider {\n readonly space: EmbeddingSpace;\n\n constructor(\n private readonly options: {\n apiKey: string;\n model: string;\n baseUrl?: string;\n timeoutMs?: number;\n },\n ) {\n this.space = {\n model: options.model,\n revision: \"hosted\",\n schema: `openrouter-embeddings-d${EMBEDDING_DIMENSIONS}`,\n dimensions: EMBEDDING_DIMENSIONS,\n };\n }\n\n async embed(request: EmbeddingRequest): Promise<EmbeddingResponse> {\n const input = typeof request.input === \"string\" ? [request.input] : request.input;\n const json = (await postJson(\n `${this.options.baseUrl ?? OPENROUTER_BASE_URL}/embeddings`,\n this.options.apiKey,\n { model: this.space.model, input, dimensions: EMBEDDING_DIMENSIONS },\n this.options.timeoutMs ?? 10_000,\n )) as { data?: Array<{ index?: number; embedding?: number[] }> };\n const rows = [...(json.data ?? [])].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));\n const vectors = rows.map((row) => row.embedding ?? []);\n const width = vectors[0]?.length ?? 0;\n if (vectors.length !== input.length || width !== EMBEDDING_DIMENSIONS) {\n throw new Error(\n `OpenRouter model ${this.space.model} returned ${vectors.length} vector(s) of ${width} dimensions; ` +\n `CodeBuddy stores ${EMBEDDING_DIMENSIONS}. Pick a model that supports \\`dimensions: ${EMBEDDING_DIMENSIONS}\\` (for example openai/text-embedding-3-small).`,\n );\n }\n return { ...this.space, vectors };\n }\n\n async generateText(): Promise<TextGenerationResponse> {\n throw new Error(\"The OpenRouter embedding provider only embeds.\");\n }\n}\n\n/** OpenRouter `/rerank` (Cohere format): relevance score per document, in input order. */\nexport async function rerankOpenRouter(\n options: { apiKey: string; model: string; baseUrl?: string; timeoutMs?: number },\n query: string,\n documents: string[],\n): Promise<number[]> {\n const json = (await postJson(\n `${options.baseUrl ?? OPENROUTER_BASE_URL}/rerank`,\n options.apiKey,\n { model: options.model, query, documents, top_n: documents.length },\n options.timeoutMs ?? 2_000,\n )) as { results?: Array<{ index: number; relevance_score: number }> };\n const scores = documents.map(() => Number.NEGATIVE_INFINITY);\n for (const result of json.results ?? []) {\n if (result.index >= 0 && result.index < scores.length)\n scores[result.index] = result.relevance_score;\n }\n return scores;\n}\n\n/**\n * Hugging Face Inference (text-classification over query/document pairs) for\n * cross-encoders such as BAAI/bge-reranker-v2-m3. Accepts either one\n * `{score}` per pair or a `[{label, score}]` list per pair.\n */\nexport async function rerankHuggingFace(\n options: { apiKey: string; model: string; baseUrl?: string; timeoutMs?: number },\n query: string,\n documents: string[],\n): Promise<number[]> {\n const json = (await postJson(\n `${options.baseUrl ?? HF_ROUTER_BASE_URL}/${options.model}`,\n options.apiKey,\n { inputs: documents.map((document) => ({ text: query, text_pair: document })) },\n options.timeoutMs ?? 2_000,\n )) as Array<{ score?: number } | Array<{ score?: number }>>;\n if (!Array.isArray(json) || json.length !== documents.length) {\n throw new Error(`Hugging Face reranker ${options.model} returned an unexpected shape.`);\n }\n return json.map(\n (item) => (Array.isArray(item) ? item[0]?.score : item.score) ?? Number.NEGATIVE_INFINITY,\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// src/providers/models.ts
|
|
2
|
+
var EMBEDDING_DIMENSIONS = 384;
|
|
3
|
+
var LOCAL_EMBEDDING_MODEL = {
|
|
4
|
+
id: "onnx-community/granite-embedding-small-english-r2-ONNX",
|
|
5
|
+
role: "primary",
|
|
6
|
+
kind: "embedding",
|
|
7
|
+
dimensions: EMBEDDING_DIMENSIONS,
|
|
8
|
+
revision: "1dc7835ba0cb9c76a3618d0bf0c427c97671b3c8",
|
|
9
|
+
schema: "transformers.js@4-q8-cls-l2",
|
|
10
|
+
supportsBatch: true,
|
|
11
|
+
relevanceFloor: 0.84
|
|
12
|
+
};
|
|
13
|
+
var DEFAULT_RERANK_MODELS = {
|
|
14
|
+
local: {
|
|
15
|
+
id: "mixedbread-ai/mxbai-rerank-xsmall-v1",
|
|
16
|
+
revision: "b5c6e9da73abc3711f593f705371cdbe9e0fe422"
|
|
17
|
+
},
|
|
18
|
+
huggingface: { id: "BAAI/bge-reranker-v2-m3" },
|
|
19
|
+
openrouter: { id: "cohere/rerank-v3.5" }
|
|
20
|
+
};
|
|
21
|
+
var DEFAULT_CLOUD_EMBEDDING_MODELS = {
|
|
22
|
+
huggingface: "sentence-transformers/all-MiniLM-L6-v2",
|
|
23
|
+
openrouter: "openai/text-embedding-3-small"
|
|
24
|
+
};
|
|
25
|
+
var DEFAULT_EMBEDDING_MODELS = [
|
|
26
|
+
{
|
|
27
|
+
id: "sentence-transformers/all-MiniLM-L6-v2",
|
|
28
|
+
role: "primary",
|
|
29
|
+
kind: "embedding",
|
|
30
|
+
dimensions: EMBEDDING_DIMENSIONS,
|
|
31
|
+
dailyCallBudget: 1e3,
|
|
32
|
+
supportsBatch: true,
|
|
33
|
+
relevanceFloor: 0.42
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "BAAI/bge-small-en-v1.5",
|
|
37
|
+
role: "fallback",
|
|
38
|
+
kind: "embedding",
|
|
39
|
+
dimensions: EMBEDDING_DIMENSIONS,
|
|
40
|
+
dailyCallBudget: 1e3,
|
|
41
|
+
supportsBatch: true,
|
|
42
|
+
relevanceFloor: 0.69
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
var DEFAULT_LLM_MODELS = [
|
|
46
|
+
{
|
|
47
|
+
id: "meta-llama/Llama-3.2-3B-Instruct",
|
|
48
|
+
role: "primary",
|
|
49
|
+
kind: "llm",
|
|
50
|
+
contextWindow: 131072,
|
|
51
|
+
dailyCallBudget: 300
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "Qwen/Qwen2.5-7B-Instruct",
|
|
55
|
+
role: "fallback",
|
|
56
|
+
kind: "llm",
|
|
57
|
+
contextWindow: 32768,
|
|
58
|
+
dailyCallBudget: 300
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "mistralai/Mistral-7B-Instruct-v0.3",
|
|
62
|
+
role: "last_resort",
|
|
63
|
+
kind: "llm",
|
|
64
|
+
contextWindow: 32768,
|
|
65
|
+
dailyCallBudget: 300
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
var MODEL_REGISTRY = [
|
|
69
|
+
LOCAL_EMBEDDING_MODEL,
|
|
70
|
+
...DEFAULT_EMBEDDING_MODELS,
|
|
71
|
+
...DEFAULT_LLM_MODELS
|
|
72
|
+
];
|
|
73
|
+
function getModelDefinition(modelId) {
|
|
74
|
+
return MODEL_REGISTRY.find((model) => model.id === modelId);
|
|
75
|
+
}
|
|
76
|
+
function getModelContextWindow(modelId) {
|
|
77
|
+
return getModelDefinition(modelId)?.contextWindow;
|
|
78
|
+
}
|
|
79
|
+
function assertCompatibleEmbeddingModels(models) {
|
|
80
|
+
const mismatchedModel = models.find((model) => model.dimensions !== EMBEDDING_DIMENSIONS);
|
|
81
|
+
if (mismatchedModel) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Embedding model ${mismatchedModel.id} has ${mismatchedModel.dimensions ?? "unknown"} dimensions; expected ${EMBEDDING_DIMENSIONS}.`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
EMBEDDING_DIMENSIONS,
|
|
90
|
+
LOCAL_EMBEDDING_MODEL,
|
|
91
|
+
DEFAULT_RERANK_MODELS,
|
|
92
|
+
DEFAULT_CLOUD_EMBEDDING_MODELS,
|
|
93
|
+
DEFAULT_EMBEDDING_MODELS,
|
|
94
|
+
DEFAULT_LLM_MODELS,
|
|
95
|
+
getModelDefinition,
|
|
96
|
+
getModelContextWindow,
|
|
97
|
+
assertCompatibleEmbeddingModels
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-JPHJBCZB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/models.ts"],"sourcesContent":["export type ModelRole = \"primary\" | \"fallback\" | \"last_resort\";\nexport type ModelKind = \"embedding\" | \"llm\";\n\nexport type ModelDefinition = {\n id: string;\n role: ModelRole;\n kind: ModelKind;\n dimensions?: number;\n /** Actual deployment revision metadata; this does not pin Hugging Face requests. */\n revision?: string;\n schema?: string;\n contextWindow?: number;\n dailyCallBudget?: number;\n supportsBatch?: boolean;\n /**\n * Cosine at or above which a memory counts as semantically relevant to a\n * task on its own (without a shared word or path). Calibrated per model by\n * `npm run eval:semantic`; cosines are not comparable across models.\n */\n relevanceFloor?: number;\n};\n\nexport const EMBEDDING_DIMENSIONS = 384;\n\n/**\n * Default local embedder (ONNX via transformers.js, no network after the first\n * download): ModernBERT-based, 384-d, 8k context, trained on code retrieval.\n * The revision is a pinned commit so the embedding space never drifts.\n */\nexport const LOCAL_EMBEDDING_MODEL = {\n id: \"onnx-community/granite-embedding-small-english-r2-ONNX\",\n role: \"primary\",\n kind: \"embedding\",\n dimensions: EMBEDDING_DIMENSIONS,\n revision: \"1dc7835ba0cb9c76a3618d0bf0c427c97671b3c8\",\n schema: \"transformers.js@4-q8-cls-l2\",\n supportsBatch: true,\n relevanceFloor: 0.84,\n} satisfies ModelDefinition;\n\n/**\n * Cross-encoder rerankers per `rerank.provider` (off by default: on\n * `npm run eval:semantic` none beat granite's dense order). The local default\n * was the best small one measured (13/20 first results, ~20 ms per prompt).\n */\nexport const DEFAULT_RERANK_MODELS = {\n local: {\n id: \"mixedbread-ai/mxbai-rerank-xsmall-v1\",\n revision: \"b5c6e9da73abc3711f593f705371cdbe9e0fe422\",\n },\n huggingface: { id: \"BAAI/bge-reranker-v2-m3\" },\n openrouter: { id: \"cohere/rerank-v3.5\" },\n} as const;\n\n/** Default hosted embedders; each must return 384-d vectors. */\nexport const DEFAULT_CLOUD_EMBEDDING_MODELS = {\n huggingface: \"sentence-transformers/all-MiniLM-L6-v2\",\n openrouter: \"openai/text-embedding-3-small\",\n} as const;\n\nexport const DEFAULT_EMBEDDING_MODELS = [\n {\n id: \"sentence-transformers/all-MiniLM-L6-v2\",\n role: \"primary\",\n kind: \"embedding\",\n dimensions: EMBEDDING_DIMENSIONS,\n dailyCallBudget: 1_000,\n supportsBatch: true,\n relevanceFloor: 0.42,\n },\n {\n id: \"BAAI/bge-small-en-v1.5\",\n role: \"fallback\",\n kind: \"embedding\",\n dimensions: EMBEDDING_DIMENSIONS,\n dailyCallBudget: 1_000,\n supportsBatch: true,\n relevanceFloor: 0.69,\n },\n] satisfies ModelDefinition[];\n\nexport const DEFAULT_LLM_MODELS = [\n {\n id: \"meta-llama/Llama-3.2-3B-Instruct\",\n role: \"primary\",\n kind: \"llm\",\n contextWindow: 131_072,\n dailyCallBudget: 300,\n },\n {\n id: \"Qwen/Qwen2.5-7B-Instruct\",\n role: \"fallback\",\n kind: \"llm\",\n contextWindow: 32_768,\n dailyCallBudget: 300,\n },\n {\n id: \"mistralai/Mistral-7B-Instruct-v0.3\",\n role: \"last_resort\",\n kind: \"llm\",\n contextWindow: 32_768,\n dailyCallBudget: 300,\n },\n] satisfies ModelDefinition[];\n\nexport const MODEL_REGISTRY: ModelDefinition[] = [\n LOCAL_EMBEDDING_MODEL,\n ...DEFAULT_EMBEDDING_MODELS,\n ...DEFAULT_LLM_MODELS,\n];\n\nexport function getModelDefinition(modelId: string): ModelDefinition | undefined {\n return MODEL_REGISTRY.find((model) => model.id === modelId);\n}\n\nexport function getModelContextWindow(modelId: string): number | undefined {\n return getModelDefinition(modelId)?.contextWindow;\n}\n\nexport function assertCompatibleEmbeddingModels(models: ModelDefinition[]): void {\n const mismatchedModel = models.find((model) => model.dimensions !== EMBEDDING_DIMENSIONS);\n\n if (mismatchedModel) {\n throw new Error(\n `Embedding model ${mismatchedModel.id} has ${mismatchedModel.dimensions ?? \"unknown\"} dimensions; expected ${EMBEDDING_DIMENSIONS}.`,\n );\n }\n}\n"],"mappings":";AAsBO,IAAM,uBAAuB;AAO7B,IAAM,wBAAwB;AAAA,EACnC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,gBAAgB;AAClB;AAOO,IAAM,wBAAwB;AAAA,EACnC,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU;AAAA,EACZ;AAAA,EACA,aAAa,EAAE,IAAI,0BAA0B;AAAA,EAC7C,YAAY,EAAE,IAAI,qBAAqB;AACzC;AAGO,IAAM,iCAAiC;AAAA,EAC5C,aAAa;AAAA,EACb,YAAY;AACd;AAEO,IAAM,2BAA2B;AAAA,EACtC;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,gBAAgB;AAAA,EAClB;AACF;AAEO,IAAM,qBAAqB;AAAA,EAChC;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,eAAe;AAAA,IACf,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,eAAe;AAAA,IACf,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,eAAe;AAAA,IACf,iBAAiB;AAAA,EACnB;AACF;AAEO,IAAM,iBAAoC;AAAA,EAC/C;AAAA,EACA,GAAG;AAAA,EACH,GAAG;AACL;AAEO,SAAS,mBAAmB,SAA8C;AAC/E,SAAO,eAAe,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO;AAC5D;AAEO,SAAS,sBAAsB,SAAqC;AACzE,SAAO,mBAAmB,OAAO,GAAG;AACtC;AAEO,SAAS,gCAAgC,QAAiC;AAC/E,QAAM,kBAAkB,OAAO,KAAK,CAAC,UAAU,MAAM,eAAe,oBAAoB;AAExF,MAAI,iBAAiB;AACnB,UAAM,IAAI;AAAA,MACR,mBAAmB,gBAAgB,EAAE,QAAQ,gBAAgB,cAAc,SAAS,yBAAyB,oBAAoB;AAAA,IACnI;AAAA,EACF;AACF;","names":[]}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
assertNoSymlinkBetween,
|
|
7
7
|
readConfigFile,
|
|
8
8
|
writeAtomic
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-776EQXDV.js";
|
|
10
10
|
|
|
11
11
|
// src/map/indexer.ts
|
|
12
12
|
import { readdir, readFile, stat } from "fs/promises";
|
|
@@ -1077,10 +1077,10 @@ async function discoverTaskPaths(repositoryRoot, task, manifest, mapSource, opti
|
|
|
1077
1077
|
async function withLlmSummaries(repositoryRoot, manifest) {
|
|
1078
1078
|
const entries = Object.values(manifest.entries);
|
|
1079
1079
|
try {
|
|
1080
|
-
const { resolveLlmConfig } = await import("./config-
|
|
1080
|
+
const { resolveLlmConfig } = await import("./config-KFEHLV6W.js");
|
|
1081
1081
|
const config = await resolveLlmConfig(repositoryRoot);
|
|
1082
1082
|
if (!config.enabled || !config.features.summaries) return entries;
|
|
1083
|
-
const { loadLlmSummaries } = await import("./summaries-
|
|
1083
|
+
const { loadLlmSummaries } = await import("./summaries-ZBV5IN7Y.js");
|
|
1084
1084
|
const summaries = await loadLlmSummaries(repositoryRoot, manifest);
|
|
1085
1085
|
if (summaries.size === 0) return entries;
|
|
1086
1086
|
return entries.map((entry) => {
|
|
@@ -1132,7 +1132,7 @@ async function rankTaskFiles(repositoryRoot, task, manifest, mapSource, options)
|
|
|
1132
1132
|
reason: "semantic retrieval off",
|
|
1133
1133
|
stale: 0,
|
|
1134
1134
|
changed: 0
|
|
1135
|
-
} : await import("./zvec-
|
|
1135
|
+
} : await import("./zvec-MAPSJQSR.js").then(({ zvecCandidates }) => zvecCandidates(repositoryRoot, task)).catch(
|
|
1136
1136
|
() => ({
|
|
1137
1137
|
status: "failed",
|
|
1138
1138
|
paths: [],
|
|
@@ -1232,7 +1232,7 @@ var MIN_RERANK_MS = 200;
|
|
|
1232
1232
|
async function llmRerank(input) {
|
|
1233
1233
|
let config;
|
|
1234
1234
|
try {
|
|
1235
|
-
config = await (await import("./config-
|
|
1235
|
+
config = await (await import("./config-KFEHLV6W.js")).resolveLlmConfig(input.repositoryRoot);
|
|
1236
1236
|
} catch {
|
|
1237
1237
|
return null;
|
|
1238
1238
|
}
|
|
@@ -1257,7 +1257,7 @@ async function llmRerank(input) {
|
|
|
1257
1257
|
input.deadlineAt === void 0 ? config.timeoutMs : input.deadlineAt - Date.now()
|
|
1258
1258
|
);
|
|
1259
1259
|
if (deadlineMs < MIN_RERANK_MS) return skipped("no time left in the hook budget");
|
|
1260
|
-
const { rerankCandidates } = await import("./rerank-
|
|
1260
|
+
const { rerankCandidates } = await import("./rerank-3P3C2UHN.js");
|
|
1261
1261
|
const result = await rerankCandidates(
|
|
1262
1262
|
input.repositoryRoot,
|
|
1263
1263
|
config,
|
|
@@ -1575,4 +1575,4 @@ export {
|
|
|
1575
1575
|
namedSegments,
|
|
1576
1576
|
loadProjectArchitectureMap
|
|
1577
1577
|
};
|
|
1578
|
-
//# sourceMappingURL=chunk-
|
|
1578
|
+
//# sourceMappingURL=chunk-L5AYZC3F.js.map
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
detectTestCommand,
|
|
9
9
|
estimateTokens,
|
|
10
10
|
findCoveringTest
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-SYWJG5NA.js";
|
|
12
12
|
import {
|
|
13
13
|
NON_SOURCE_PATH,
|
|
14
14
|
discoverTaskPaths,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
loadProjectArchitectureMap,
|
|
17
17
|
readBoundedSource,
|
|
18
18
|
taskTerms
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-L5AYZC3F.js";
|
|
20
20
|
|
|
21
21
|
// src/hooks/lean.ts
|
|
22
22
|
import { createHash } from "crypto";
|
|
@@ -172,4 +172,4 @@ export {
|
|
|
172
172
|
renderLeanBlock,
|
|
173
173
|
runLeanHookCommand
|
|
174
174
|
};
|
|
175
|
-
//# sourceMappingURL=chunk-
|
|
175
|
+
//# sourceMappingURL=chunk-N34XHWBA.js.map
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chatJson
|
|
3
|
+
} from "./chunk-UFZD3JAA.js";
|
|
1
4
|
import {
|
|
2
5
|
NON_SOURCE_PATH,
|
|
3
6
|
TEST_PATH,
|
|
4
7
|
loadFreshIndex,
|
|
5
8
|
loadProjectArchitectureMap,
|
|
6
9
|
readBoundedSource
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import {
|
|
9
|
-
chatJson
|
|
10
|
-
} from "./chunk-IE2OUO54.js";
|
|
10
|
+
} from "./chunk-L5AYZC3F.js";
|
|
11
11
|
import {
|
|
12
12
|
redactSensitive,
|
|
13
13
|
scanSensitive
|
|
14
14
|
} from "./chunk-GDJH7XJC.js";
|
|
15
15
|
import {
|
|
16
16
|
resolveLlmConfig
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-6MKEIDX5.js";
|
|
18
18
|
import {
|
|
19
19
|
writeAtomic
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-776EQXDV.js";
|
|
21
21
|
|
|
22
22
|
// src/llm/summaries.ts
|
|
23
23
|
import { mkdir, readFile } from "fs/promises";
|
|
@@ -201,4 +201,4 @@ export {
|
|
|
201
201
|
loadLlmSummaries,
|
|
202
202
|
summarizeRepository
|
|
203
203
|
};
|
|
204
|
-
//# sourceMappingURL=chunk-
|
|
204
|
+
//# sourceMappingURL=chunk-PCE4STAY.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readConfigFile,
|
|
3
3
|
writeAtomic
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-776EQXDV.js";
|
|
5
5
|
|
|
6
6
|
// src/retrieval/zvec.ts
|
|
7
7
|
import { execFile, spawn } from "child_process";
|
|
@@ -321,4 +321,4 @@ export {
|
|
|
321
321
|
zvecStatus,
|
|
322
322
|
buildZvecIndex
|
|
323
323
|
};
|
|
324
|
-
//# sourceMappingURL=chunk-
|
|
324
|
+
//# sourceMappingURL=chunk-QDBDYCM2.js.map
|