@orquentra/codebuddy 2.9.0 → 2.15.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/ARCHITECTURE.md +98 -0
- package/CLAUDE_CODEX.md +33 -0
- package/README.md +114 -9
- package/dist/{adapters-S4HHTWJW.js → adapters-OJRSVFV3.js} +2 -2
- package/dist/{chunk-GXOML4UZ.js → chunk-4BSO3HST.js} +126 -18
- package/dist/chunk-4BSO3HST.js.map +1 -0
- package/dist/{chunk-CAEPHLHC.js → chunk-57642NGU.js} +2 -44
- package/dist/chunk-57642NGU.js.map +1 -0
- package/dist/{chunk-ESZXCZIN.js → chunk-6YI2D5F5.js} +33 -5
- package/dist/chunk-6YI2D5F5.js.map +1 -0
- package/dist/{chunk-GKNYMNFM.js → chunk-7IPSDDO5.js} +10 -3
- package/dist/chunk-7IPSDDO5.js.map +1 -0
- package/dist/chunk-BTTQWSDS.js +372 -0
- package/dist/chunk-BTTQWSDS.js.map +1 -0
- package/dist/{chunk-2WH2Q4TA.js → chunk-D5E7SGI5.js} +304 -77
- package/dist/chunk-D5E7SGI5.js.map +1 -0
- package/dist/chunk-EMWGLKPR.js +45 -0
- package/dist/chunk-EMWGLKPR.js.map +1 -0
- package/dist/chunk-GQKWUHMA.js +20 -0
- package/dist/chunk-GQKWUHMA.js.map +1 -0
- package/dist/chunk-ITXE27SZ.js +358 -0
- package/dist/chunk-ITXE27SZ.js.map +1 -0
- package/dist/{chunk-UK6XTS2Y.js → chunk-JJQDVGF7.js} +23 -23
- package/dist/chunk-JJQDVGF7.js.map +1 -0
- package/dist/{chunk-F4BJ3DXW.js → chunk-KQCP3ZYH.js} +1273 -331
- package/dist/chunk-KQCP3ZYH.js.map +1 -0
- package/dist/{chunk-QN76UJP7.js → chunk-KYRM3HPP.js} +89 -21
- package/dist/chunk-KYRM3HPP.js.map +1 -0
- package/dist/{chunk-KNR2P2UY.js → chunk-M75SN4D4.js} +19 -4
- package/dist/chunk-M75SN4D4.js.map +1 -0
- package/dist/{chunk-J4T6HHI4.js → chunk-RZ266NHE.js} +538 -317
- package/dist/chunk-RZ266NHE.js.map +1 -0
- package/dist/chunk-U7KDM62P.js +43 -0
- package/dist/chunk-U7KDM62P.js.map +1 -0
- package/dist/chunk-UATJCD2T.js +368 -0
- package/dist/chunk-UATJCD2T.js.map +1 -0
- package/dist/{chunk-ENKDFLOO.js → chunk-UOTKHSQ5.js} +25 -8
- package/dist/chunk-UOTKHSQ5.js.map +1 -0
- package/dist/{chunk-TLWGZVPC.js → chunk-UR3DF74I.js} +1007 -132
- package/dist/chunk-UR3DF74I.js.map +1 -0
- package/dist/chunk-UTPC66MV.js +128 -0
- package/dist/chunk-UTPC66MV.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-WHKXUXHE.js +156 -0
- package/dist/chunk-WHKXUXHE.js.map +1 -0
- package/dist/{chunk-SRA4MDVG.js → chunk-YMXFSHXT.js} +978 -950
- package/dist/chunk-YMXFSHXT.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/config-TXQJCCVI.js +15 -0
- package/dist/expand-KWYGNCKZ.js +98 -0
- package/dist/expand-KWYGNCKZ.js.map +1 -0
- package/dist/file-vectors-MKXTSGGH.js +19 -0
- package/dist/index-facts-4MTEHKMN.js +38 -0
- package/dist/index-pass-TBDUEZCB.js +14 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +25 -23
- package/dist/install-E3JWINTN.js +31 -0
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/lean-MLTAFS5L.js +29 -0
- package/dist/local-75DJECVR.js +48 -0
- package/dist/local-llm-MECVJTVU.js +19 -0
- package/dist/{main-YJKSYFPT.js → main-RLMKQZW4.js} +999 -328
- package/dist/main-RLMKQZW4.js.map +1 -0
- package/dist/{memory-TGHVTBME.js → memory-PFFVMTCH.js} +9 -6
- package/dist/{memory-TGHVTBME.js.map → memory-PFFVMTCH.js.map} +1 -1
- package/dist/{operations-OG4ISWIM.js → operations-STR7S5UO.js} +8 -7
- package/dist/operations-STR7S5UO.js.map +1 -0
- package/dist/{pretool-command-PRFOIHMW.js → pretool-command-B5GY5T6E.js} +2 -2
- package/dist/pretool-command-B5GY5T6E.js.map +1 -0
- package/dist/prompt-gate-TDTFFQW2.js +8 -0
- package/dist/prompt-gate-TDTFFQW2.js.map +1 -0
- package/dist/{rerank-IQAEWLQR.js → rerank-XXKWDI5Q.js} +7 -4
- package/dist/{rerank-IQAEWLQR.js.map → rerank-XXKWDI5Q.js.map} +1 -1
- package/dist/{runtime-GCSEIAVD.js → runtime-SWFWGTQ4.js} +18 -9
- package/dist/runtime-SWFWGTQ4.js.map +1 -0
- package/dist/summaries-QXWFFDUZ.js +31 -0
- package/dist/summaries-QXWFFDUZ.js.map +1 -0
- package/dist/{zvec-XDZNZ2OL.js → zvec-F73IBWO5.js} +13 -6
- package/dist/zvec-F73IBWO5.js.map +1 -0
- package/package.json +13 -3
- package/dist/chunk-2WH2Q4TA.js.map +0 -1
- package/dist/chunk-6NZEZJYD.js +0 -206
- package/dist/chunk-6NZEZJYD.js.map +0 -1
- package/dist/chunk-CAEPHLHC.js.map +0 -1
- package/dist/chunk-ENKDFLOO.js.map +0 -1
- package/dist/chunk-ESZXCZIN.js.map +0 -1
- package/dist/chunk-F4BJ3DXW.js.map +0 -1
- package/dist/chunk-GKNYMNFM.js.map +0 -1
- package/dist/chunk-GXOML4UZ.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-KNR2P2UY.js.map +0 -1
- package/dist/chunk-QN76UJP7.js.map +0 -1
- package/dist/chunk-SFMIMLXR.js +0 -63
- package/dist/chunk-SFMIMLXR.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/config-GEZFLXK6.js +0 -12
- 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/summaries-ML53R7EL.js +0 -21
- /package/dist/{adapters-S4HHTWJW.js.map → adapters-OJRSVFV3.js.map} +0 -0
- /package/dist/{config-GEZFLXK6.js.map → config-TXQJCCVI.js.map} +0 -0
- /package/dist/{install-BXM56QCD.js.map → file-vectors-MKXTSGGH.js.map} +0 -0
- /package/dist/{lean-command-7DFW32MA.js.map → index-facts-4MTEHKMN.js.map} +0 -0
- /package/dist/{operations-OG4ISWIM.js.map → index-pass-TBDUEZCB.js.map} +0 -0
- /package/dist/{pretool-command-PRFOIHMW.js.map → install-E3JWINTN.js.map} +0 -0
- /package/dist/{runtime-GCSEIAVD.js.map → lean-MLTAFS5L.js.map} +0 -0
- /package/dist/{summaries-ML53R7EL.js.map → local-75DJECVR.js.map} +0 -0
- /package/dist/{zvec-XDZNZ2OL.js.map → local-llm-MECVJTVU.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/local-llm.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { confirm, isCancel, spinner } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { readConfigFile, setConfigBlock } from \"../../core/config-file.js\";\nimport {\n checkLocalModelResources,\n downloadLocalModel,\n formatBytes,\n freeMemory,\n installedLocalModel,\n LLAMA_RUNTIME,\n LOCAL_MODELS,\n type LocalModelSize,\n localModelPath,\n localRuntimeAvailable,\n removeLocalModels,\n} from \"../../llm/local.js\";\n\nconst ok = (text: string) => `${pc.green(\"✓\")} ${text}`;\nconst warn = (text: string) => `${pc.yellow(\"!\")} ${text}`;\nconst row = (label: string, value: string) => ` ${label.padEnd(10)} ${value}`;\n\nexport const LOCAL_LLM_SIZES = Object.keys(LOCAL_MODELS) as LocalModelSize[];\n\n/**\n * `codebuddy models add local-llm`: the explicit opt-in for the local small\n * model. Shows the size, checks disk and RAM, installs the node-llama-cpp\n * runtime when missing (after confirmation), downloads the pinned GGUF with a\n * checksum check, and records the size in `.codebuddy/config.json`.\n */\nexport async function addLocalLlm(\n options: { size?: LocalModelSize; yes?: boolean; interactive?: boolean } = {},\n): Promise<boolean> {\n const spec = LOCAL_MODELS[options.size ?? \"default\"];\n const interactive = options.interactive ?? process.stdin.isTTY === true;\n console.log(\n `${pc.bold(\"Local LLM\")} ${spec.id} ${pc.dim(`(${formatBytes(spec.bytes)} download, ~${formatBytes(spec.ramBytes)} RAM while carding)`)}`,\n );\n const resources = await checkLocalModelResources(spec);\n if (!resources.ok) {\n console.log(warn(resources.reason));\n return false;\n }\n if (freeMemory() < spec.ramBytes) {\n console.log(warn(`only ${formatBytes(freeMemory())} RAM free right now; carding may be slow`));\n }\n if (!(await localRuntimeAvailable())) {\n const install = options.yes\n ? true\n : interactive\n ? await confirm({\n message: `Install the local runtime (${LLAMA_RUNTIME}, ~20–35 MB prebuilt) with npm -g?`,\n initialValue: true,\n })\n : false;\n if (isCancel(install) || !install) {\n console.log(warn(`Runtime missing. Install it with: npm install -g ${LLAMA_RUNTIME}`));\n return false;\n }\n const code = await new Promise<number>((resolve) => {\n const child = spawn(\"npm\", [\"install\", \"-g\", LLAMA_RUNTIME], { stdio: \"inherit\" });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (exit) => resolve(exit ?? 1));\n });\n if (code !== 0 || !(await localRuntimeAvailable())) {\n console.log(warn(`Runtime install failed. Run: npm install -g ${LLAMA_RUNTIME}`));\n return false;\n }\n }\n if ((await installedLocalModel(spec.size))?.id !== spec.id) {\n const progress = spinner();\n progress.start(`Downloading ${spec.file}`);\n let shown = 0;\n try {\n await downloadLocalModel(spec, (received, total) => {\n const percent = Math.floor((received / total) * 100);\n if (percent !== shown) {\n shown = percent;\n progress.message(`Downloading ${spec.file} ${percent}%`);\n }\n });\n progress.stop(ok(`${spec.id} verified ${pc.dim(localModelPath(spec))}`));\n } catch (error) {\n progress.stop(warn((error as Error).message));\n return false;\n }\n } else {\n console.log(ok(`${spec.id} already installed`));\n }\n const llm = (await readConfigFile()).llm;\n // A BYOK block stays as it is (the local model is then unused until it is switched).\n if (llm === undefined || llm.provider === \"local\" || llm.enabled === false) {\n await setConfigBlock(\"llm\", { ...llm, enabled: true, provider: \"local\", size: spec.size });\n } else {\n console.log(\n pc.dim(\n ` .codebuddy/config.json uses ${llm.provider}; set llm.provider to \"local\" to use it.`,\n ),\n );\n }\n console.log(\n pc.dim(\n \" Cards are written in the background by `codebuddy serve` or `codebuddy index --cards`.\",\n ),\n );\n return true;\n}\n\nexport async function removeLocalLlm(): Promise<void> {\n const removed = await removeLocalModels();\n const llm = (await readConfigFile()).llm;\n if (llm?.provider === \"local\") await setConfigBlock(\"llm\", { ...llm, enabled: false });\n console.log(\n removed.length > 0\n ? ok(`removed ${removed.join(\", \")}; CodeBuddy falls back to 2.14 behaviour`)\n : pc.dim(\"no local LLM installed\"),\n );\n}\n\nexport async function printLocalLlmStatus(\n cards: { carded: number; eligible: number } | null,\n): Promise<void> {\n const llm = (await readConfigFile()).llm;\n const installed = await installedLocalModel(llm?.size);\n console.log(pc.bold(\"Local LLM\"));\n if (!installed) {\n console.log(row(\"model\", pc.dim(\"not installed (optional: codebuddy models add local-llm)\")));\n return;\n }\n const runtime = await localRuntimeAvailable();\n console.log(row(\"model\", ok(`${installed.id} ${pc.dim(formatBytes(installed.bytes))}`)));\n console.log(\n row(\"runtime\", runtime ? ok(LLAMA_RUNTIME) : warn(`missing: npm install -g ${LLAMA_RUNTIME}`)),\n );\n if (llm?.enabled === false)\n console.log(row(\"status\", warn(\"disabled in .codebuddy/config.json\")));\n if (cards) console.log(row(\"cards\", `${cards.carded} / ${cards.eligible} source files`));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -21,12 +21,13 @@ var CLIENT_ADAPTERS = {
|
|
|
21
21
|
},
|
|
22
22
|
codex: {
|
|
23
23
|
name: "Codex",
|
|
24
|
-
host: { bin: "codex", verified: "0.
|
|
24
|
+
host: { bin: "codex", verified: "0.169.0" },
|
|
25
25
|
rulesFile: "AGENTS.md",
|
|
26
26
|
mcp: { global: "codex", manual: "codebuddy codex install (global ~/.codex/config.toml)" },
|
|
27
|
-
hookEvents:
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
hookEvents: LIFECYCLE_EVENTS,
|
|
28
|
+
hooks: "codex-hooks",
|
|
29
|
+
note: "Requires Codex hook support (verified 0.169.0), a trusted project and review of the installed definitions in /hooks. CodeBuddy never bypasses hook trust. Rollout import remains available for older sessions.",
|
|
30
|
+
framing: "native hook additionalContext is developer context; project instructions remain in AGENTS.md"
|
|
30
31
|
},
|
|
31
32
|
opencode: {
|
|
32
33
|
name: "OpenCode",
|
|
@@ -101,4 +102,4 @@ export {
|
|
|
101
102
|
supportedModes,
|
|
102
103
|
sharedRulesReaders
|
|
103
104
|
};
|
|
104
|
-
//# sourceMappingURL=chunk-
|
|
105
|
+
//# sourceMappingURL=chunk-V7PT6ER5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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\" | \"codex-hooks\" | \"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.169.0\" },\n rulesFile: \"AGENTS.md\",\n mcp: { global: \"codex\", manual: \"codebuddy codex install (global ~/.codex/config.toml)\" },\n hookEvents: LIFECYCLE_EVENTS,\n hooks: \"codex-hooks\",\n note: \"Requires Codex hook support (verified 0.169.0), a trusted project and review of the installed definitions in /hooks. CodeBuddy never bypasses hook trust. Rollout import remains available for older sessions.\",\n framing:\n \"native hook additionalContext is developer context; project instructions remain in AGENTS.md\",\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;AAAA,IACZ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SACE;AAAA,EACJ;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":[]}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readConfigFile
|
|
3
|
+
} from "./chunk-M75SN4D4.js";
|
|
4
|
+
import {
|
|
5
|
+
installedLocalModel
|
|
6
|
+
} from "./chunk-BTTQWSDS.js";
|
|
7
|
+
|
|
8
|
+
// src/core/global-config.ts
|
|
9
|
+
import { chmod, mkdir, readFile, writeFile } from "fs/promises";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
var GLOBAL_DIR = ".codebuddy";
|
|
14
|
+
var GLOBAL_FILE = "global.json";
|
|
15
|
+
var globalConfigSchema = z.object({
|
|
16
|
+
hfToken: z.string().min(1).optional(),
|
|
17
|
+
/** Cloud LLM keys asked for by `codebuddy use` on machines too small for the local model. */
|
|
18
|
+
groqApiKey: z.string().min(1).optional(),
|
|
19
|
+
openrouterApiKey: z.string().min(1).optional(),
|
|
20
|
+
databaseUrl: z.string().min(1).optional(),
|
|
21
|
+
// Where the codebuddy install lives on this machine. Cached so future
|
|
22
|
+
// `codebuddy use` calls in unrelated projects can find the CLI entry
|
|
23
|
+
// without re-resolving via process.argv every time.
|
|
24
|
+
installPath: z.string().optional()
|
|
25
|
+
});
|
|
26
|
+
function globalConfigPath(home = homedir()) {
|
|
27
|
+
return join(home, GLOBAL_DIR, GLOBAL_FILE);
|
|
28
|
+
}
|
|
29
|
+
async function readGlobalConfig(home = homedir()) {
|
|
30
|
+
const path = globalConfigPath(home);
|
|
31
|
+
try {
|
|
32
|
+
const raw = await readFile(path, "utf8");
|
|
33
|
+
return globalConfigSchema.parse(JSON.parse(raw));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error.code === "ENOENT") return {};
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function writeGlobalConfig(next, home = homedir()) {
|
|
40
|
+
const path = globalConfigPath(home);
|
|
41
|
+
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
42
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}
|
|
43
|
+
`, { mode: 384 });
|
|
44
|
+
await chmod(path, 384);
|
|
45
|
+
return { path };
|
|
46
|
+
}
|
|
47
|
+
async function mergeGlobalConfig(patch, home = homedir()) {
|
|
48
|
+
const current = await readGlobalConfig(home);
|
|
49
|
+
const merged = { ...current, ...patch };
|
|
50
|
+
const { path } = await writeGlobalConfig(merged, home);
|
|
51
|
+
return { path, merged };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/llm/config.ts
|
|
55
|
+
var BASE_URL = {
|
|
56
|
+
groq: "https://api.groq.com/openai/v1",
|
|
57
|
+
openrouter: "https://openrouter.ai/api/v1"
|
|
58
|
+
};
|
|
59
|
+
var KEY_ENV = {
|
|
60
|
+
groq: "GROQ_API_KEY",
|
|
61
|
+
openrouter: "OPENROUTER_API_KEY"
|
|
62
|
+
};
|
|
63
|
+
var DEFAULT_MODEL = {
|
|
64
|
+
// Qwen everywhere: the cloud path for machines too small for the local
|
|
65
|
+
// Qwen3. Groq shut llama-3.1-8b-instant down on 2026-08-16 and
|
|
66
|
+
// qwen3.6-27b on 2026-09-14; qwen3.8-27b is its replacement (reasoning off
|
|
67
|
+
// by default). OpenRouter lists no free Qwen model (2026-11), so it is paid there.
|
|
68
|
+
groq: "qwen/qwen3.8-27b",
|
|
69
|
+
openrouter: "qwen/qwen3.8-27b"
|
|
70
|
+
};
|
|
71
|
+
var DEFAULTS = { timeoutMs: 700, dailyTokenBudget: 4e5 };
|
|
72
|
+
async function resolveLlmConfig(repositoryRoot, env = process.env) {
|
|
73
|
+
if (env.CODEBUDDY_LLM === "0") return { enabled: false, reason: "CODEBUDDY_LLM=0" };
|
|
74
|
+
const configured = (await readConfigFile(repositoryRoot)).llm;
|
|
75
|
+
if (configured === void 0 || configured.provider === "local") {
|
|
76
|
+
if (configured?.enabled === false) return { enabled: false, reason: "llm not enabled" };
|
|
77
|
+
const local = await installedLocalModel(configured?.size);
|
|
78
|
+
if (local) return localConfig(local, configured);
|
|
79
|
+
if (configured?.provider === "local") {
|
|
80
|
+
return {
|
|
81
|
+
enabled: false,
|
|
82
|
+
reason: "local model not installed (codebuddy models add local-llm)"
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const block = configured ?? (env.CODEBUDDY_LLM === "1" ? { enabled: true } : void 0);
|
|
87
|
+
if (!block?.enabled) return { enabled: false, reason: "llm not enabled" };
|
|
88
|
+
const provider = block.provider === "local" || block.provider === void 0 ? "groq" : block.provider;
|
|
89
|
+
const apiKey = env.CODEBUDDY_LLM_API_KEY || env[KEY_ENV[provider]] || await savedKey(provider, env);
|
|
90
|
+
if (!apiKey) return { enabled: false, reason: `no API key in ${KEY_ENV[provider]}` };
|
|
91
|
+
const fallbackKey = block.fallback && (env[KEY_ENV[block.fallback.provider]] || await savedKey(block.fallback.provider, env));
|
|
92
|
+
return {
|
|
93
|
+
enabled: true,
|
|
94
|
+
provider,
|
|
95
|
+
model: block.model ?? DEFAULT_MODEL[provider],
|
|
96
|
+
// Any OpenAI-compatible endpoint (a proxy, a test stub).
|
|
97
|
+
baseUrl: env.CODEBUDDY_LLM_BASE_URL?.replace(/\/+$/, "") || BASE_URL[provider],
|
|
98
|
+
apiKey,
|
|
99
|
+
timeoutMs: block.timeoutMs ?? DEFAULTS.timeoutMs,
|
|
100
|
+
dailyTokenBudget: block.dailyTokenBudget ?? DEFAULTS.dailyTokenBudget,
|
|
101
|
+
features: {
|
|
102
|
+
summaries: block.features?.summaries ?? true,
|
|
103
|
+
rerank: block.features?.rerank ?? true,
|
|
104
|
+
memory: block.features?.memory ?? false
|
|
105
|
+
},
|
|
106
|
+
fallback: block.fallback && fallbackKey ? {
|
|
107
|
+
provider: block.fallback.provider,
|
|
108
|
+
model: block.fallback.model,
|
|
109
|
+
baseUrl: BASE_URL[block.fallback.provider],
|
|
110
|
+
apiKey: fallbackKey
|
|
111
|
+
} : null
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function savedKey(provider, env) {
|
|
115
|
+
if (env.CODEBUDDY_GLOBAL_CONFIG === "0") return void 0;
|
|
116
|
+
const global = await readGlobalConfig().catch(
|
|
117
|
+
() => ({})
|
|
118
|
+
);
|
|
119
|
+
return provider === "groq" ? global.groqApiKey : global.openrouterApiKey;
|
|
120
|
+
}
|
|
121
|
+
function localConfig(spec, block) {
|
|
122
|
+
return {
|
|
123
|
+
enabled: true,
|
|
124
|
+
provider: "local",
|
|
125
|
+
model: spec.id,
|
|
126
|
+
baseUrl: "",
|
|
127
|
+
apiKey: "",
|
|
128
|
+
timeoutMs: block?.timeoutMs ?? DEFAULTS.timeoutMs,
|
|
129
|
+
dailyTokenBudget: Number.POSITIVE_INFINITY,
|
|
130
|
+
features: {
|
|
131
|
+
summaries: block?.features?.summaries ?? true,
|
|
132
|
+
rerank: block?.features?.rerank ?? false,
|
|
133
|
+
memory: block?.features?.memory ?? false,
|
|
134
|
+
facts: block?.features?.facts ?? true,
|
|
135
|
+
expansion: block?.features?.expansion ?? true
|
|
136
|
+
},
|
|
137
|
+
fallback: null,
|
|
138
|
+
local: spec
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function redactLlmKeys(text, env = process.env) {
|
|
142
|
+
let redacted = text;
|
|
143
|
+
for (const name of ["CODEBUDDY_LLM_API_KEY", ...Object.values(KEY_ENV)]) {
|
|
144
|
+
const key = env[name];
|
|
145
|
+
if (key) redacted = redacted.split(key).join("[REDACTED]");
|
|
146
|
+
}
|
|
147
|
+
return redacted;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
readGlobalConfig,
|
|
152
|
+
mergeGlobalConfig,
|
|
153
|
+
resolveLlmConfig,
|
|
154
|
+
redactLlmKeys
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=chunk-WHKXUXHE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/global-config.ts","../src/llm/config.ts"],"sourcesContent":["import { chmod, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { z } from \"zod\";\n\n/**\n * User-level configuration shared across all projects on this machine.\n * Stored at ~/.codebuddy/global.json with 0600 permissions so the HF token\n * is never world-readable. Per-project namespace is NOT stored here — that\n * is implicit in the directory the user runs `codebuddy use` from.\n */\n\nexport const GLOBAL_DIR = \".codebuddy\";\nexport const GLOBAL_FILE = \"global.json\";\n\nconst globalConfigSchema = z.object({\n hfToken: z.string().min(1).optional(),\n /** Cloud LLM keys asked for by `codebuddy use` on machines too small for the local model. */\n groqApiKey: z.string().min(1).optional(),\n openrouterApiKey: z.string().min(1).optional(),\n databaseUrl: z.string().min(1).optional(),\n // Where the codebuddy install lives on this machine. Cached so future\n // `codebuddy use` calls in unrelated projects can find the CLI entry\n // without re-resolving via process.argv every time.\n installPath: z.string().optional(),\n});\n\nexport type GlobalConfig = z.infer<typeof globalConfigSchema>;\n\nexport function globalConfigPath(home = homedir()): string {\n return join(home, GLOBAL_DIR, GLOBAL_FILE);\n}\n\nexport async function readGlobalConfig(home = homedir()): Promise<GlobalConfig> {\n const path = globalConfigPath(home);\n try {\n const raw = await readFile(path, \"utf8\");\n return globalConfigSchema.parse(JSON.parse(raw));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n}\n\nexport async function writeGlobalConfig(\n next: GlobalConfig,\n home = homedir(),\n): Promise<{ path: string }> {\n const path = globalConfigPath(home);\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeFile(path, `${JSON.stringify(next, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600);\n return { path };\n}\n\nexport async function mergeGlobalConfig(\n patch: GlobalConfig,\n home = homedir(),\n): Promise<{ path: string; merged: GlobalConfig }> {\n const current = await readGlobalConfig(home);\n const merged: GlobalConfig = { ...current, ...patch };\n const { path } = await writeGlobalConfig(merged, home);\n return { path, merged };\n}\n","import { readConfigFile } from \"../core/config-file.js\";\nimport { readGlobalConfig } from \"../core/global-config.js\";\nimport { installedLocalModel, type LocalModelSize, type LocalModelSpec } from \"./local.js\";\n\n/**\n * Small-model layer: bring-your-own-key (Groq/OpenRouter) or local\n * (`provider: \"local\"`, a GGUF model from `codebuddy models add local-llm`).\n * BYOK is off unless `.codebuddy/config.json` has `llm.enabled` and the\n * environment holds a key; keys are read from the environment only, never\n * stored in config, and redacted from any error text. The local backend is on\n * whenever its model is installed and no `llm` block says otherwise.\n */\nexport type LlmProvider = \"groq\" | \"openrouter\" | \"local\";\nexport type LlmFeatures = {\n summaries: boolean;\n rerank: boolean;\n memory: boolean;\n /** Project facts extracted at index time (`llm/index-facts.ts`). */\n facts?: boolean;\n /** Prompt-time code-term expansion for weak tasks (local backend only). */\n expansion?: boolean;\n};\ntype Endpoint = { provider: LlmProvider; model: string; baseUrl: string; apiKey: string };\nexport type EnabledLlmConfig = Endpoint & {\n enabled: true;\n timeoutMs: number;\n dailyTokenBudget: number;\n features: LlmFeatures;\n /** Background work only; never on the prompt path. */\n fallback: Endpoint | null;\n /** Set for `provider: \"local\"`: the installed GGUF model. */\n local?: LocalModelSpec;\n};\nexport type LlmConfig = EnabledLlmConfig | { enabled: false; reason: string };\n\ntype HostedProvider = Exclude<LlmProvider, \"local\">;\nconst BASE_URL: Record<HostedProvider, string> = {\n groq: \"https://api.groq.com/openai/v1\",\n openrouter: \"https://openrouter.ai/api/v1\",\n};\nconst KEY_ENV: Record<HostedProvider, string> = {\n groq: \"GROQ_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n};\nconst DEFAULT_MODEL: Record<HostedProvider, string> = {\n // Qwen everywhere: the cloud path for machines too small for the local\n // Qwen3. Groq shut llama-3.1-8b-instant down on 2026-08-16 and\n // qwen3.6-27b on 2026-09-14; qwen3.8-27b is its replacement (reasoning off\n // by default). OpenRouter lists no free Qwen model (2026-11), so it is paid there.\n groq: \"qwen/qwen3.8-27b\",\n openrouter: \"qwen/qwen3.8-27b\",\n};\nconst DEFAULTS = { timeoutMs: 700, dailyTokenBudget: 400_000 } as const;\n\nexport async function resolveLlmConfig(\n repositoryRoot: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<LlmConfig> {\n if (env.CODEBUDDY_LLM === \"0\") return { enabled: false, reason: \"CODEBUDDY_LLM=0\" };\n // CODEBUDDY_LLM=1 turns it on with defaults where no config says otherwise\n // (eval copies, one-off runs); an explicit `enabled: false` still wins.\n const configured = (await readConfigFile(repositoryRoot)).llm;\n if (configured === undefined || configured.provider === \"local\") {\n if (configured?.enabled === false) return { enabled: false, reason: \"llm not enabled\" };\n const local = await installedLocalModel(configured?.size);\n if (local) return localConfig(local, configured);\n if (configured?.provider === \"local\") {\n return {\n enabled: false,\n reason: \"local model not installed (codebuddy models add local-llm)\",\n };\n }\n }\n const block = configured ?? (env.CODEBUDDY_LLM === \"1\" ? { enabled: true } : undefined);\n if (!block?.enabled) return { enabled: false, reason: \"llm not enabled\" };\n const provider: HostedProvider =\n block.provider === \"local\" || block.provider === undefined ? \"groq\" : block.provider;\n const apiKey =\n env.CODEBUDDY_LLM_API_KEY || env[KEY_ENV[provider]] || (await savedKey(provider, env));\n if (!apiKey) return { enabled: false, reason: `no API key in ${KEY_ENV[provider]}` };\n const fallbackKey =\n block.fallback &&\n (env[KEY_ENV[block.fallback.provider]] || (await savedKey(block.fallback.provider, env)));\n return {\n enabled: true,\n provider,\n model: block.model ?? DEFAULT_MODEL[provider],\n // Any OpenAI-compatible endpoint (a proxy, a test stub).\n baseUrl: env.CODEBUDDY_LLM_BASE_URL?.replace(/\\/+$/, \"\") || BASE_URL[provider],\n apiKey,\n timeoutMs: block.timeoutMs ?? DEFAULTS.timeoutMs,\n dailyTokenBudget: block.dailyTokenBudget ?? DEFAULTS.dailyTokenBudget,\n features: {\n summaries: block.features?.summaries ?? true,\n rerank: block.features?.rerank ?? true,\n memory: block.features?.memory ?? false,\n },\n fallback:\n block.fallback && fallbackKey\n ? {\n provider: block.fallback.provider,\n model: block.fallback.model,\n baseUrl: BASE_URL[block.fallback.provider],\n apiKey: fallbackKey,\n }\n : null,\n };\n}\n\n/**\n * A key `codebuddy use` asked for and saved in ~/.codebuddy/global.json (0600),\n * the same place as the HF token; the environment always wins. Off with\n * `CODEBUDDY_GLOBAL_CONFIG=0` (tests), so a developer's saved key never leaks in.\n */\nasync function savedKey(\n provider: HostedProvider,\n env: NodeJS.ProcessEnv,\n): Promise<string | undefined> {\n if (env.CODEBUDDY_GLOBAL_CONFIG === \"0\") return undefined;\n const global = await readGlobalConfig().catch(\n () => ({}) as Awaited<ReturnType<typeof readGlobalConfig>>,\n );\n return provider === \"groq\" ? global.groqApiKey : global.openrouterApiKey;\n}\n\ntype LlmBlock = NonNullable<Awaited<ReturnType<typeof readConfigFile>>[\"llm\"]>;\n\n/**\n * The local backend: no key, no daily budget (no cost), no hosted fallback.\n * Cards, facts and expansion default on; rerank stays off until the harness\n * shows it beats dense order (the standing rule for rerankers).\n */\nfunction localConfig(spec: LocalModelSpec, block: LlmBlock | undefined): EnabledLlmConfig {\n return {\n enabled: true,\n provider: \"local\",\n model: spec.id,\n baseUrl: \"\",\n apiKey: \"\",\n timeoutMs: block?.timeoutMs ?? DEFAULTS.timeoutMs,\n dailyTokenBudget: Number.POSITIVE_INFINITY,\n features: {\n summaries: block?.features?.summaries ?? true,\n rerank: block?.features?.rerank ?? false,\n memory: block?.features?.memory ?? false,\n facts: block?.features?.facts ?? true,\n expansion: block?.features?.expansion ?? true,\n },\n fallback: null,\n local: spec,\n };\n}\n\nexport type { LocalModelSize };\n\n/** Replace every LLM key present in the environment with `[REDACTED]`. */\nexport function redactLlmKeys(text: string, env: NodeJS.ProcessEnv = process.env): string {\n let redacted = text;\n // (A saved key is redacted by the client, which always strips `config.apiKey`.)\n for (const name of [\"CODEBUDDY_LLM_API_KEY\", ...Object.values(KEY_ENV)]) {\n const key = env[name];\n if (key) redacted = redacted.split(key).join(\"[REDACTED]\");\n }\n return redacted;\n}\n"],"mappings":"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","names":[]}
|