@orquentra/codebuddy 2.6.0 → 2.7.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.
Files changed (117) hide show
  1. package/CLAUDE_CODEX.md +6 -4
  2. package/WORKFLOW.md +5 -5
  3. package/dist/{chunk-U4HAJLIB.js → chunk-3QGW53GD.js} +16 -3
  4. package/dist/chunk-3QGW53GD.js.map +1 -0
  5. package/dist/{chunk-L5AYZC3F.js → chunk-5EDGAZQN.js} +438 -68
  6. package/dist/chunk-5EDGAZQN.js.map +1 -0
  7. package/dist/chunk-5UH3BEQR.js +32 -0
  8. package/dist/chunk-5UH3BEQR.js.map +1 -0
  9. package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
  10. package/dist/{chunk-UN57FKQX.js → chunk-AK2UAE32.js} +152 -862
  11. package/dist/chunk-AK2UAE32.js.map +1 -0
  12. package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
  13. package/dist/chunk-AL6VBTNA.js.map +1 -0
  14. package/dist/{chunk-WHZSVY2L.js → chunk-AQHPEJR6.js} +94 -23
  15. package/dist/chunk-AQHPEJR6.js.map +1 -0
  16. package/dist/{chunk-PCE4STAY.js → chunk-BQM7FJ7D.js} +6 -6
  17. package/dist/chunk-BQM7FJ7D.js.map +1 -0
  18. package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
  19. package/dist/chunk-CAEPHLHC.js.map +1 -0
  20. package/dist/chunk-CG5B3LYG.js +66 -0
  21. package/dist/chunk-CG5B3LYG.js.map +1 -0
  22. package/dist/chunk-CSRD5OQA.js +79 -0
  23. package/dist/chunk-CSRD5OQA.js.map +1 -0
  24. package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
  25. package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
  26. package/dist/chunk-EEUFU7KV.js +1634 -0
  27. package/dist/chunk-EEUFU7KV.js.map +1 -0
  28. package/dist/{chunk-UFZD3JAA.js → chunk-ESZXCZIN.js} +3 -3
  29. package/dist/{chunk-PYLH4MH5.js → chunk-FJOTP632.js} +1 -1
  30. package/dist/{chunk-SYWJG5NA.js → chunk-GKNYMNFM.js} +17 -7
  31. package/dist/{chunk-SYWJG5NA.js.map → chunk-GKNYMNFM.js.map} +1 -1
  32. package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
  33. package/dist/chunk-HPOR6YXA.js.map +1 -0
  34. package/dist/chunk-JUAFCLRB.js +1014 -0
  35. package/dist/chunk-JUAFCLRB.js.map +1 -0
  36. package/dist/{chunk-776EQXDV.js → chunk-KNR2P2UY.js} +110 -83
  37. package/dist/chunk-KNR2P2UY.js.map +1 -0
  38. package/dist/{chunk-N34XHWBA.js → chunk-LCSBL5TT.js} +24 -11
  39. package/dist/chunk-LCSBL5TT.js.map +1 -0
  40. package/dist/{chunk-QDBDYCM2.js → chunk-MSUAVR3R.js} +76 -11
  41. package/dist/chunk-MSUAVR3R.js.map +1 -0
  42. package/dist/{chunk-6MKEIDX5.js → chunk-SFMIMLXR.js} +2 -2
  43. package/dist/chunk-TGFTKWOR.js +16 -0
  44. package/dist/chunk-TGFTKWOR.js.map +1 -0
  45. package/dist/chunk-TUR6YKEE.js +55 -0
  46. package/dist/chunk-TUR6YKEE.js.map +1 -0
  47. package/dist/chunk-Y7D7PUBE.js +321 -0
  48. package/dist/chunk-Y7D7PUBE.js.map +1 -0
  49. package/dist/{chunk-VAKBJV26.js → chunk-YJIA2MFL.js} +918 -2238
  50. package/dist/chunk-YJIA2MFL.js.map +1 -0
  51. package/dist/cli/index.js +14 -3
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
  54. package/dist/{codebuddy-DM-i0Fu5.d.ts → codebuddy-DO7ZhoWn.d.ts} +37 -3
  55. package/dist/config-GEZFLXK6.js +12 -0
  56. package/dist/docker-autostart-KDME7TVC.js +12 -0
  57. package/dist/index.d.ts +48 -3
  58. package/dist/index.js +32 -25
  59. package/dist/langgraph/index.d.ts +1 -1
  60. package/dist/lean-command-CWJAVQ5G.js +18 -0
  61. package/dist/local-ST4WMRCG.js +17 -0
  62. package/dist/{main-HFHLGID4.js → main-AHNGHC4L.js} +327 -329
  63. package/dist/main-AHNGHC4L.js.map +1 -0
  64. package/dist/{memory-4RGVLPKG.js → memory-TGHVTBME.js} +9 -8
  65. package/dist/memory-TGHVTBME.js.map +1 -0
  66. package/dist/operations-7BTAAUHF.js +26 -0
  67. package/dist/postgres-docker-QQZQ2ABK.js +16 -0
  68. package/dist/pretool-command-3SXQSMAO.js +13 -0
  69. package/dist/{rerank-3P3C2UHN.js → rerank-IQAEWLQR.js} +6 -5
  70. package/dist/rerank-IQAEWLQR.js.map +1 -0
  71. package/dist/runtime-E7JQZ2BZ.js +46 -0
  72. package/dist/runtime-E7JQZ2BZ.js.map +1 -0
  73. package/dist/stdio-HOV4P3DQ.js +12 -0
  74. package/dist/stdio-HOV4P3DQ.js.map +1 -0
  75. package/dist/summaries-XV3JGGCC.js +20 -0
  76. package/dist/summaries-XV3JGGCC.js.map +1 -0
  77. package/dist/{zvec-MAPSJQSR.js → zvec-KP4KNB7Y.js} +10 -3
  78. package/dist/zvec-KP4KNB7Y.js.map +1 -0
  79. package/package.json +7 -10
  80. package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
  81. package/src/db/migrations/meta/0006_snapshot.json +1258 -0
  82. package/src/db/migrations/meta/_journal.json +7 -0
  83. package/dist/chunk-776EQXDV.js.map +0 -1
  84. package/dist/chunk-GDJH7XJC.js.map +0 -1
  85. package/dist/chunk-JPHJBCZB.js.map +0 -1
  86. package/dist/chunk-L5AYZC3F.js.map +0 -1
  87. package/dist/chunk-N34XHWBA.js.map +0 -1
  88. package/dist/chunk-PCE4STAY.js.map +0 -1
  89. package/dist/chunk-PQK3GKZH.js +0 -98
  90. package/dist/chunk-PQK3GKZH.js.map +0 -1
  91. package/dist/chunk-QDBDYCM2.js.map +0 -1
  92. package/dist/chunk-U4HAJLIB.js.map +0 -1
  93. package/dist/chunk-UN57FKQX.js.map +0 -1
  94. package/dist/chunk-VAKBJV26.js.map +0 -1
  95. package/dist/chunk-WHZSVY2L.js.map +0 -1
  96. package/dist/chunk-XBJQKCLU.js.map +0 -1
  97. package/dist/config-KFEHLV6W.js +0 -11
  98. package/dist/lean-command-C5YPFKHP.js +0 -15
  99. package/dist/local-OTOY56B4.js +0 -13
  100. package/dist/main-HFHLGID4.js.map +0 -1
  101. package/dist/memory-4RGVLPKG.js.map +0 -1
  102. package/dist/operations-LOBAQRLO.js +0 -24
  103. package/dist/pretool-command-MQQ2UNQM.js +0 -10
  104. package/dist/rerank-3P3C2UHN.js.map +0 -1
  105. package/dist/summaries-ZBV5IN7Y.js +0 -18
  106. /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
  107. /package/dist/{chunk-UFZD3JAA.js.map → chunk-ESZXCZIN.js.map} +0 -0
  108. /package/dist/{chunk-PYLH4MH5.js.map → chunk-FJOTP632.js.map} +0 -0
  109. /package/dist/{chunk-6MKEIDX5.js.map → chunk-SFMIMLXR.js.map} +0 -0
  110. /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
  111. /package/dist/{config-KFEHLV6W.js.map → config-GEZFLXK6.js.map} +0 -0
  112. /package/dist/{lean-command-C5YPFKHP.js.map → docker-autostart-KDME7TVC.js.map} +0 -0
  113. /package/dist/{local-OTOY56B4.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
  114. /package/dist/{operations-LOBAQRLO.js.map → local-ST4WMRCG.js.map} +0 -0
  115. /package/dist/{pretool-command-MQQ2UNQM.js.map → operations-7BTAAUHF.js.map} +0 -0
  116. /package/dist/{summaries-ZBV5IN7Y.js.map → postgres-docker-QQZQ2ABK.js.map} +0 -0
  117. /package/dist/{zvec-MAPSJQSR.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
@@ -1,98 +0,0 @@
1
- // src/indexer/symbols.ts
2
- import { extname } from "path";
3
- var SYMBOL_CODE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
4
- var SIGNATURE_MAX = 120;
5
- var SYMBOL_LIMIT = 100;
6
- function supportsSymbols(path) {
7
- return SYMBOL_CODE_EXTENSIONS.has(extname(path).toLowerCase());
8
- }
9
- var DECLARATION = /^(?<export>export\s+)?(?<default>default\s+)?(?<mods>(?:declare\s+|abstract\s+|async\s+)*)(?<kind>function\*?|class|interface|type|enum|const|let|var)\s+(?<name>[A-Za-z_$][\w$]*)/;
10
- function extractSymbolTable(content) {
11
- const lines = content.split(/\r?\n/);
12
- const symbols = [];
13
- for (let index = 0; index < lines.length; index++) {
14
- const line = lines[index] ?? "";
15
- if (/^\s/.test(line)) continue;
16
- const match = DECLARATION.exec(line);
17
- if (!match?.groups) continue;
18
- const rawKind = match.groups.kind ?? "";
19
- const kind = normalizeKind(rawKind);
20
- const name = match.groups.name ?? "";
21
- if (!name) continue;
22
- const startLine = index + 1;
23
- const endLine = findEndLine(lines, index) + 1;
24
- const exported = Boolean(match.groups.export);
25
- symbols.push({ name, kind, startLine, endLine, signature: buildSignature(line), exported });
26
- if (kind === "class") symbols.push(...classMethods(lines, index, endLine - 1, name, exported));
27
- if (symbols.length >= SYMBOL_LIMIT) break;
28
- }
29
- return symbols.slice(0, SYMBOL_LIMIT);
30
- }
31
- var METHOD = /^\s+(?:(?:public|private|protected|static|abstract|override|async|get|set|readonly)\s+)*(#?[A-Za-z_$][\w$]*)\??(?:<[^>]*>)?\s*\(/;
32
- function classMethods(lines, classIndex, classEnd, className, exported) {
33
- const methods = [];
34
- let depth = 0;
35
- let parens = 0;
36
- let open = null;
37
- for (let index = classIndex; index <= classEnd; index++) {
38
- const line = lines[index] ?? "";
39
- const method = depth === 1 && !open ? METHOD.exec(line) : null;
40
- if (method?.[1]) {
41
- open = {
42
- name: `${className}.${method[1]}`,
43
- startLine: index + 1,
44
- signature: `${className}: ${buildSignature(line.trim())}`
45
- };
46
- parens = 0;
47
- }
48
- const code = codeOnly(line);
49
- depth += braceDelta(code);
50
- parens += delta(code, "(", ")");
51
- if (open && depth === 1 && parens <= 0 && (code.includes("}") || code.trimEnd().endsWith(";"))) {
52
- methods.push({ ...open, kind: "method", endLine: index + 1, exported });
53
- open = null;
54
- }
55
- }
56
- return methods;
57
- }
58
- function normalizeKind(raw) {
59
- if (raw.startsWith("function")) return "function";
60
- if (raw === "let" || raw === "var" || raw === "const") return "const";
61
- return raw;
62
- }
63
- function buildSignature(line) {
64
- const head = line.split("{")[0]?.split("=>")[0]?.trimEnd() ?? line.trim();
65
- const cleaned = head.replace(/[=;{]\s*$/, "").trim();
66
- return cleaned.length <= SIGNATURE_MAX ? cleaned : `${cleaned.slice(0, SIGNATURE_MAX - 1)}\u2026`;
67
- }
68
- function findEndLine(lines, startIndex) {
69
- let braces = 0;
70
- let parens = 0;
71
- for (let index = startIndex; index < lines.length; index++) {
72
- const code = codeOnly(lines[index] ?? "");
73
- braces += braceDelta(code);
74
- parens += delta(code, "(", ")");
75
- if (braces <= 0 && parens <= 0) return index;
76
- }
77
- return lines.length - 1;
78
- }
79
- function codeOnly(line) {
80
- return line.replace(/(["'`])(?:\\.|(?!\1).)*\1/g, '""').replace(/\/\/.*$/, "");
81
- }
82
- function braceDelta(code) {
83
- return delta(code, "{", "}");
84
- }
85
- function delta(code, open, close) {
86
- let total = 0;
87
- for (const char of code) {
88
- if (char === open) total++;
89
- else if (char === close) total--;
90
- }
91
- return total;
92
- }
93
-
94
- export {
95
- supportsSymbols,
96
- extractSymbolTable
97
- };
98
- //# sourceMappingURL=chunk-PQK3GKZH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/indexer/symbols.ts"],"sourcesContent":["import { extname } from \"node:path\";\n\n/**\n * Lightweight symbol extraction (Roadmap N.3). Records the top-level symbols of\n * a TS/JS file, plus class methods as `Class.method` — name, kind, line range,\n * and a one-line signature — using regex\n * + brace matching, deliberately *not* a full parser (consistent with the\n * indexer's \"no fragile parser platform\" stance). This lets context represent a\n * file by its public API (a handful of signatures) instead of its whole body,\n * so an agent can see what a file offers without reading it.\n *\n * It is a heuristic: it targets the common top-level declaration forms and skips\n * anything it can't classify. Deterministic — the same file always yields the\n * same table.\n */\n\nexport type SymbolKind = \"function\" | \"method\" | \"class\" | \"interface\" | \"type\" | \"enum\" | \"const\";\n\nexport type SymbolSpan = {\n name: string;\n kind: SymbolKind;\n /** 1-based inclusive line range. */\n startLine: number;\n endLine: number;\n /** One-line signature (declaration head), truncated. */\n signature: string;\n exported: boolean;\n};\n\nconst SYMBOL_CODE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"]);\nconst SIGNATURE_MAX = 120;\nconst SYMBOL_LIMIT = 100;\n\n/** True when a symbol table can be extracted for this path's language. */\nexport function supportsSymbols(path: string): boolean {\n return SYMBOL_CODE_EXTENSIONS.has(extname(path).toLowerCase());\n}\n\nconst DECLARATION =\n /^(?<export>export\\s+)?(?<default>default\\s+)?(?<mods>(?:declare\\s+|abstract\\s+|async\\s+)*)(?<kind>function\\*?|class|interface|type|enum|const|let|var)\\s+(?<name>[A-Za-z_$][\\w$]*)/;\n\nexport function extractSymbolTable(content: string): SymbolSpan[] {\n const lines = content.split(/\\r?\\n/);\n const symbols: SymbolSpan[] = [];\n\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? \"\";\n // Top-level only: a declaration that starts at column 0 (optionally exported).\n if (/^\\s/.test(line)) continue;\n const match = DECLARATION.exec(line);\n if (!match?.groups) continue;\n\n const rawKind = match.groups.kind ?? \"\";\n const kind = normalizeKind(rawKind);\n const name = match.groups.name ?? \"\";\n if (!name) continue;\n\n const startLine = index + 1;\n const endLine = findEndLine(lines, index) + 1;\n const exported = Boolean(match.groups.export);\n symbols.push({ name, kind, startLine, endLine, signature: buildSignature(line), exported });\n if (kind === \"class\") symbols.push(...classMethods(lines, index, endLine - 1, name, exported));\n if (symbols.length >= SYMBOL_LIMIT) break;\n }\n\n return symbols.slice(0, SYMBOL_LIMIT);\n}\n\nconst METHOD =\n /^\\s+(?:(?:public|private|protected|static|abstract|override|async|get|set|readonly)\\s+)*(#?[A-Za-z_$][\\w$]*)\\??(?:<[^>]*>)?\\s*\\(/;\n\n/**\n * Methods directly in a class body (brace depth 1), named `Class.method`, so a\n * task can name a method and get its own span instead of the whole class.\n */\nfunction classMethods(\n lines: string[],\n classIndex: number,\n classEnd: number,\n className: string,\n exported: boolean,\n): SymbolSpan[] {\n const methods: SymbolSpan[] = [];\n let depth = 0;\n let parens = 0;\n let open: { name: string; startLine: number; signature: string } | null = null;\n for (let index = classIndex; index <= classEnd; index++) {\n const line = lines[index] ?? \"\";\n const method: RegExpExecArray | null = depth === 1 && !open ? METHOD.exec(line) : null;\n if (method?.[1]) {\n open = {\n name: `${className}.${method[1]}`,\n startLine: index + 1,\n signature: `${className}: ${buildSignature(line.trim())}`,\n };\n parens = 0;\n }\n const code = codeOnly(line);\n depth += braceDelta(code);\n parens += delta(code, \"(\", \")\");\n // Back at class-body depth after a body, or a bodiless (abstract/overload)\n // member; an object type in a multi-line parameter list is not the body.\n if (\n open &&\n depth === 1 &&\n parens <= 0 &&\n (code.includes(\"}\") || code.trimEnd().endsWith(\";\"))\n ) {\n methods.push({ ...open, kind: \"method\", endLine: index + 1, exported });\n open = null;\n }\n }\n return methods;\n}\n\n/**\n * Render a file's symbols as compact signature lines (the \"symbol capsule\").\n * The signature already preserves a leading `export`, so it is used verbatim.\n */\nexport function symbolSignatures(symbols: SymbolSpan[], limit = 12): string[] {\n return symbols.slice(0, limit).map((symbol) => symbol.signature);\n}\n\nfunction normalizeKind(raw: string): SymbolKind {\n if (raw.startsWith(\"function\")) return \"function\";\n if (raw === \"let\" || raw === \"var\" || raw === \"const\") return \"const\";\n return raw as SymbolKind;\n}\n\nfunction buildSignature(line: string): string {\n const head = line.split(\"{\")[0]?.split(\"=>\")[0]?.trimEnd() ?? line.trim();\n const cleaned = head.replace(/[=;{]\\s*$/, \"\").trim();\n return cleaned.length <= SIGNATURE_MAX ? cleaned : `${cleaned.slice(0, SIGNATURE_MAX - 1)}…`;\n}\n\n/**\n * Find the last line of a declaration: the first line where both braces and\n * parentheses are balanced, so a multi-line parameter list carries on to its\n * body. A one-line const/type is a single line. Braces in single-line strings\n * and `//` comments are ignored; multi-line template literals, block comments\n * and regex literals can still skew it.\n */\nfunction findEndLine(lines: string[], startIndex: number): number {\n let braces = 0;\n let parens = 0;\n for (let index = startIndex; index < lines.length; index++) {\n const code = codeOnly(lines[index] ?? \"\");\n braces += braceDelta(code);\n parens += delta(code, \"(\", \")\");\n if (braces <= 0 && parens <= 0) return index;\n }\n return lines.length - 1;\n}\n\n/** The line without single-line string literals and a trailing `//` comment. */\nfunction codeOnly(line: string): string {\n return line.replace(/([\"'`])(?:\\\\.|(?!\\1).)*\\1/g, '\"\"').replace(/\\/\\/.*$/, \"\");\n}\n\nfunction braceDelta(code: string): number {\n return delta(code, \"{\", \"}\");\n}\n\nfunction delta(code: string, open: string, close: string): number {\n let total = 0;\n for (const char of code) {\n if (char === open) total++;\n else if (char === close) total--;\n }\n return total;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/retrieval/zvec.ts"],"sourcesContent":["import { execFile, spawn } from \"node:child_process\";\nimport { mkdir, readFile, rm, stat } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { readConfigFile } from \"../core/config-file.js\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\n\n/**\n * Optional semantic candidate source backed by zvec-grep (`zg`), a local\n * code-search CLI. It only *suggests* files: hits are normalized to\n * repository-relative paths, checked against the working tree, and fused with\n * the keyword ranking by the caller. Risk, policy and incident evidence are\n * never derived from it.\n *\n * Off unless `.codebuddy/config.json` sets `zvec.enabled` (or\n * `CODEBUDDY_ZVEC=1`). It never builds an index implicitly: an unready index,\n * a missing or unsupported binary, a timeout, or unparseable output all return\n * no candidates plus a reason, and callers fall back to keyword ranking.\n *\n * Queries exclude non-source paths (docs, Markdown, evals, scripts, tests,\n * CodeBuddy's own state) unless the task asks about them: otherwise prose that\n * quotes a task, such as audits or eval prompts, outranks the code it describes.\n *\n * Benchmarked: vector-only queries beat zg's default hybrid route on code\n * tasks, so only `--vector` is used. Results are parsed from zg's agent\n * markdown (`#N matchedBy=… path:start-end`), as 0.2.x has no JSON output.\n *\n * Queries use `--mode auto`: a running `zg server` answers in ~200ms, otherwise\n * zg searches directly (~400ms). Auto mode never starts the daemon. The\n * version/readiness check (~400ms) is cached on disk for a few minutes so the\n * per-prompt hook process doesn't repeat it. A failed check (not installed,\n * index not ready, timeout) is cached too, but only for 30 s and only until the\n * workspace's `.zvec-grep/` changes, so an unready index costs one probe per\n * window instead of one per call while a fresh `zg index` is still picked up.\n *\n * Freshness: queries keep `--refresh off` so the prompt path never waits on\n * embedding. Instead `refreshZvecIndex` (run by project sync after index\n * passes, off the request path) and `codebuddy zvec index` record when the zg\n * index was last brought up to date, and hits whose file changed after that are\n * dropped rather than presented with their old meaning.\n */\n\n/** zvec-grep versions whose CLI and output format this adapter was tested against. */\nconst SUPPORTED = /^0\\.2\\.\\d+$/;\nconst DEFAULT_TIMEOUT_MS = 1_500;\nconst READY_CACHE_MS = 10 * 60_000;\n/** A failed readiness check is reused this long, so a fresh `zg index` is noticed quickly. */\nconst NOT_READY_CACHE_MS = 30_000;\n/** An incremental `zg index` runs in the background; only a runaway is killed. */\nconst REFRESH_TIMEOUT_MS = 10 * 60_000;\n/** Small, fast local model; indexes this repo in seconds. */\nexport const DEFAULT_ZVEC_MODEL = \"local/potion-code-16m-v2\";\nconst DOC_GLOBS = [\"!**/*.md\", \"!**/*.mdx\", \"!docs/**\"];\nconst TEST_GLOBS = [\n \"!**/*.test.*\",\n \"!**/*.spec.*\",\n \"!**/__tests__/**\",\n \"!**/test/**\",\n \"!**/tests/**\",\n];\nconst ALWAYS_EXCLUDED_GLOBS = [\"!evals/**\", \"!scripts/**\", \"!.codebuddy/**\", \"!**/fixtures/**\"];\n\n/** zg `-g` exclusions for a task: docs and tests stay searchable when the task names them. */\nexport function zvecExcludeGlobs(task: string): string[] {\n return [\n ...(/\\b(docs?|readme|documentation|markdown)\\b/i.test(task) ? [] : DOC_GLOBS),\n ...(/\\b(tests?|spec)\\b/i.test(task) ? [] : TEST_GLOBS),\n ...ALWAYS_EXCLUDED_GLOBS,\n ];\n}\n\nconst EXCLUDED_PREFIXES = [\".codebuddy/cache/\", \".zvec-grep/\", \".git/\", \"node_modules/\"];\n\nexport type ZvecStatus =\n | \"ok\"\n | \"disabled\"\n | \"not-installed\"\n | \"unsupported-version\"\n | \"index-not-ready\"\n | \"timeout\"\n | \"failed\";\n\n/** zg provides no content hash: a valid range alone cannot prove index freshness. */\nexport type RetrievalHit = {\n path: string;\n startLine: number;\n endLine: number;\n rank: number;\n /** `anchor`: a line the task itself names (path:line, stack frame, error string). */\n source: \"zvec\" | \"anchor\";\n freshness: \"unverified\";\n};\n\nexport type ZvecCandidates = {\n status: ZvecStatus;\n /** Repo-relative paths of existing files, best first. */\n paths: string[];\n /** Every chunk hit inside `paths`, in rank order (several per file). */\n hits: RetrievalHit[];\n reason: string;\n /** Hits dropped because the file no longer exists (stale index). */\n stale: number;\n /** Hits dropped because the file changed after the zg index was refreshed. */\n changed: number;\n};\n\nexport async function zvecCandidates(\n repositoryRoot: string,\n task: string,\n options: { limit?: number } = {},\n): Promise<ZvecCandidates> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n if (!config.enabled) return none(\"disabled\", \"zvec-grep not enabled\");\n const run = (args: string[]) => runZg(config.binary, args, root, config.timeoutMs);\n\n const cached = await readCachedReadiness(root, config.binary);\n if (cached !== \"ready\") {\n if (cached) return cached;\n const readiness = await checkReadiness(run);\n await markReadiness(root, config.binary, readiness);\n if (readiness) return readiness;\n }\n\n // zg ranks chunks, and one file can own many of them: oversample chunks so\n // `limit` distinct files survive de-duplication.\n const limit = Math.min(20, Math.max(1, Math.floor(options.limit ?? 10)));\n const chunkLimit = limit * 8;\n const globs = zvecExcludeGlobs(task);\n const result = await run([\n \"query\",\n \"--vector\",\n task,\n \"--mode\",\n \"auto\",\n \"--refresh\",\n \"off\",\n \"--limit\",\n String(chunkLimit),\n ...globs.flatMap((glob) => [\"-g\", glob]),\n ]);\n if (result.error) {\n await resetZvecCache(root);\n return fromError(result.error, \"zvec-grep query\");\n }\n\n const hits = parseHits(result.stdout).slice(0, chunkLimit);\n // With no index, zg answers with a \"run zg index\" hint instead of hits.\n if (hits.length === 0 && /\\bzg index\\b/.test(result.stdout)) {\n const notReady = none(\"index-not-ready\", NOT_READY);\n await markReadiness(root, config.binary, notReady);\n return notReady;\n }\n const indexedAt = await readIndexedAt(root);\n const paths: string[] = [];\n const candidates: RetrievalHit[] = [];\n const seen = new Set<string>();\n let stale = 0;\n let changed = 0;\n for (const hit of hits) {\n const path = toRepoRelative(root, hit.path);\n if (!path) continue;\n // Later chunks of an accepted file keep their spans; a rejected file stays rejected.\n if (seen.has(path)) {\n if (paths.includes(path)) candidates.push({ ...hit, path });\n continue;\n }\n if (paths.length >= limit) continue;\n seen.add(path);\n if (EXCLUDED_PREFIXES.some((prefix) => path.startsWith(prefix))) continue;\n try {\n const info = await stat(resolve(root, path));\n if (!info.isFile()) continue;\n // ctime too: a rename over the path keeps the old mtime.\n if (indexedAt !== null && Math.max(info.mtimeMs, info.ctimeMs) > indexedAt) {\n changed++;\n continue;\n }\n } catch {\n stale++;\n continue;\n }\n paths.push(path);\n candidates.push({ ...hit, path });\n }\n return {\n status: \"ok\",\n paths,\n hits: candidates,\n stale,\n changed,\n reason:\n `zvec-grep vector search: ${paths.length} file(s)` +\n (globs.length > 0 ? \"; non-source paths excluded\" : \"\") +\n (stale > 0 ? `, ${stale} stale hit(s) dropped` : \"\") +\n (changed > 0 ? `, ${changed} hit(s) changed since the zg index dropped` : \"\") +\n (indexedAt === null\n ? \"; index age unknown, hits unverified (run `codebuddy zvec index`)\"\n : \"\"),\n };\n}\n\nexport type ZvecRefresh = { status: ZvecStatus; reason: string };\n\n/**\n * Incrementally refresh an existing zg index (no `--rebuild`, no embedding\n * flag) and record when it started, which is the freshness bound applied to\n * later hits. Never builds a missing index and never starts a zg server; every\n * failure is returned as a status for the caller to log.\n */\nexport async function refreshZvecIndex(\n repositoryRoot: string,\n options: { signal?: AbortSignal } = {},\n): Promise<ZvecRefresh> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n if (!config.enabled) return { status: \"disabled\", reason: \"zvec-grep not enabled\" };\n const readiness = await checkReadiness((args) =>\n runZg(config.binary, args, root, config.timeoutMs),\n );\n if (readiness) return { status: readiness.status, reason: readiness.reason };\n const startedAt = Date.now();\n const result = await runZg(\n config.binary,\n [\"index\", root, \"--mode\", \"auto\"],\n root,\n REFRESH_TIMEOUT_MS,\n options.signal,\n );\n if (result.error) {\n const failure = fromError(result.error, \"zvec-grep refresh\");\n return { status: failure.status, reason: failure.reason };\n }\n await markIndexed(root, startedAt);\n return { status: \"ok\", reason: \"zvec-grep index refreshed\" };\n}\n\n/** Extract bounded, validated line ranges in retrieval rank order. */\nexport function parseHits(output: string): RetrievalHit[] {\n return [...output.matchAll(/^#(\\d+) matchedBy=\\S+ (.+?):(\\d+)(?:-(\\d+))?\\s*$/gm)]\n .flatMap((match) => {\n const rank = Number(match[1]);\n const startLine = Number(match[3]);\n const endLine = Number(match[4] ?? match[3]);\n if (startLine < 1 || endLine < startLine) return [];\n return [\n {\n path: match[2] as string,\n rank,\n startLine,\n endLine,\n source: \"zvec\" as const,\n freshness: \"unverified\" as const,\n },\n ];\n })\n .sort((left, right) => left.rank - right.rank);\n}\n\n/** Forget the cached readiness check, e.g. after `zg index` or a failed query. */\nexport async function resetZvecCache(repositoryRoot: string): Promise<void> {\n await rm(readyPath(resolve(repositoryRoot)), { force: true });\n}\n\nexport type ZvecReport = {\n enabled: boolean;\n binary: string;\n version: string | null;\n supported: boolean;\n indexReady: boolean;\n server: \"running\" | \"stopped\" | \"unknown\";\n reason: string;\n};\n\n/** Everything `codebuddy zvec status` shows. Read-only; never builds an index. */\nexport async function zvecStatus(repositoryRoot: string): Promise<ZvecReport> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n const run = (args: string[]) =>\n runZg(config.binary, args, root, Math.max(config.timeoutMs, 5_000));\n const version = await run([\"version\"]);\n const found = version.error ? null : (version.stdout.trim().split(/\\s+/).pop() ?? null);\n const supported = found !== null && SUPPORTED.test(found);\n const readiness = supported ? await checkReadiness(run) : null;\n const server = supported ? await run([\"server\", \"status\"]) : null;\n return {\n enabled: config.enabled,\n binary: config.binary,\n version: found,\n supported,\n indexReady: supported && readiness === null,\n server:\n server === null || server.error\n ? \"unknown\"\n : /running|ready/i.test(server.stdout)\n ? \"running\"\n : \"stopped\",\n reason: version.error\n ? fromError(version.error, \"zvec-grep version check\").reason\n : !supported\n ? `zvec-grep ${found ?? \"unknown\"} is not 0.2.x`\n : (readiness?.reason ?? \"ready\"),\n };\n}\n\n/**\n * Build or incrementally refresh the zg index for this repository, streaming\n * zg's progress. Explicit by design: CodeBuddy never indexes implicitly.\n */\nexport async function buildZvecIndex(\n repositoryRoot: string,\n options: {\n model?: string;\n rebuild?: boolean /** Discard zg's progress output. */;\n quiet?: boolean;\n } = {},\n): Promise<number> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n const args = [\n \"index\",\n root,\n \"--embedding\",\n options.model ?? DEFAULT_ZVEC_MODEL,\n \"--mode\",\n \"auto\",\n ];\n if (options.rebuild) args.push(\"--rebuild\");\n const startedAt = Date.now();\n const code = await new Promise<number>((resolveRun, reject) => {\n const child = spawn(config.binary, args, {\n cwd: root,\n stdio: options.quiet ? \"ignore\" : \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (exit) => resolveRun(exit ?? 1));\n });\n await resetZvecCache(root);\n if (code === 0) await markIndexed(root, startedAt);\n return code;\n}\n\nconst NOT_READY = \"zvec-grep index missing or not ready; run `codebuddy zvec index`\";\n\n/** Version + index readiness. Returns null when ready, else the failure. */\nasync function checkReadiness(\n run: (args: string[]) => Promise<RunResult>,\n): Promise<ZvecCandidates | null> {\n const version = await run([\"version\"]);\n if (version.error) return fromError(version.error, \"zvec-grep version check\");\n const found = version.stdout.trim().split(/\\s+/).pop() ?? \"\";\n if (!SUPPORTED.test(found)) {\n return none(\"unsupported-version\", `zvec-grep ${found || \"unknown\"} is not 0.2.x`);\n }\n const ready = await run([\"status\", \"--check-ready\", \"--mode\", \"auto\"]);\n if (!ready.error) return null;\n return ready.error.code === \"ETIMEDOUT\"\n ? none(\"timeout\", \"zvec-grep status timed out\")\n : none(\"index-not-ready\", NOT_READY);\n}\n\nfunction readyPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"zvec-ready.json\");\n}\n\ntype CachedReadiness = {\n binary?: string;\n checkedAt?: string;\n /** Absent in caches written before failures were cached: those were always ready. */\n failure?: { status: ZvecStatus; reason: string };\n};\n\n/**\n * \"ready\", a cached failure (reason marked as cached), or null when the check\n * must run. A failure is dropped once `.zvec-grep/` changed after it was\n * recorded, i.e. someone (re)built the index outside CodeBuddy.\n */\nasync function readCachedReadiness(\n root: string,\n binary: string,\n): Promise<\"ready\" | ZvecCandidates | null> {\n let cached: CachedReadiness;\n try {\n cached = JSON.parse(await readFile(readyPath(root), \"utf8\")) as CachedReadiness;\n } catch {\n return null;\n }\n const checkedAt = Date.parse(cached.checkedAt ?? \"\");\n const age = Date.now() - checkedAt;\n if (cached.binary !== binary || !(age >= 0)) return null;\n if (!cached.failure) return age <= READY_CACHE_MS ? \"ready\" : null;\n if (age > NOT_READY_CACHE_MS) return null;\n const workspace = await stat(join(root, \".zvec-grep\")).catch(() => null);\n if (workspace && workspace.mtimeMs > checkedAt) return null;\n const { status, reason } = cached.failure;\n return none(status, `${reason}; cached check from ${Math.round(age / 1000)}s ago`);\n}\n\n/** Cache a readiness result: null = ready. */\nasync function markReadiness(\n root: string,\n binary: string,\n failure: ZvecCandidates | null,\n): Promise<void> {\n try {\n const path = readyPath(root);\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n const record: CachedReadiness = {\n binary,\n checkedAt: new Date().toISOString(),\n ...(failure ? { failure: { status: failure.status, reason: failure.reason } } : {}),\n };\n await writeAtomic(path, `${JSON.stringify(record)}\\n`);\n } catch {\n // A read-only cache only costs a repeated readiness check.\n }\n}\n\nfunction indexedPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"zvec-index.json\");\n}\n\n/** When the zg index was last refreshed by CodeBuddy, or null if unknown. */\nasync function readIndexedAt(root: string): Promise<number | null> {\n try {\n const parsed = JSON.parse(await readFile(indexedPath(root), \"utf8\")) as { indexedAt?: string };\n const at = Date.parse(parsed.indexedAt ?? \"\");\n return Number.isFinite(at) ? at : null;\n } catch {\n return null;\n }\n}\n\nasync function markIndexed(root: string, at: number): Promise<void> {\n try {\n const path = indexedPath(root);\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeAtomic(path, `${JSON.stringify({ indexedAt: new Date(at).toISOString() })}\\n`);\n } catch {\n // Without the record, later hits are reported as unverified, not trusted.\n }\n}\n\nasync function zvecConfig(\n root: string,\n): Promise<{ enabled: boolean; binary: string; timeoutMs: number }> {\n let file: Awaited<ReturnType<typeof readConfigFile>> = {};\n try {\n file = await readConfigFile(root);\n } catch {\n // A broken config must not break context assembly; treat zvec as disabled.\n }\n const env = process.env.CODEBUDDY_ZVEC;\n return {\n enabled: env !== undefined ? env === \"1\" || env === \"true\" : (file.zvec?.enabled ?? false),\n binary: file.zvec?.binary ?? \"zg\",\n timeoutMs: file.zvec?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n}\n\ntype RunResult = {\n stdout: string;\n error: (Error & { code?: string | number | null | undefined }) | null;\n};\n\nfunction runZg(\n binary: string,\n args: string[],\n cwd: string,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<RunResult> {\n return new Promise((resolveRun) => {\n execFile(\n binary,\n args,\n {\n cwd,\n timeout: timeoutMs,\n maxBuffer: 1024 * 1024,\n killSignal: \"SIGKILL\",\n ...(signal ? { signal } : {}),\n },\n (error, stdout) => {\n if (error && (error as { killed?: boolean }).killed) {\n resolveRun({ stdout: \"\", error: Object.assign(error, { code: \"ETIMEDOUT\" }) });\n return;\n }\n resolveRun({ stdout: String(stdout), error });\n },\n );\n });\n}\n\nfunction fromError(error: NonNullable<RunResult[\"error\"]>, what: string): ZvecCandidates {\n if (error.code === \"ENOENT\") return none(\"not-installed\", \"zvec-grep (`zg`) not found on PATH\");\n if (error.code === \"ETIMEDOUT\") return none(\"timeout\", `${what} timed out`);\n return none(\"failed\", `${what} failed`);\n}\n\nfunction none(status: ZvecStatus, reason: string): ZvecCandidates {\n return { status, paths: [], hits: [], reason, stale: 0, changed: 0 };\n}\n\nfunction toRepoRelative(root: string, path: string): string | null {\n const absolute = isAbsolute(path) ? path : resolve(root, path);\n const rel = relative(root, absolute);\n if (rel === \"\" || rel === \"..\" || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return null;\n return rel.split(sep).join(\"/\");\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/providers/local.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type {\n EmbeddingRequest,\n EmbeddingResponse,\n ModelProvider,\n TextGenerationResponse,\n} from \"./adapter.js\";\nimport type { EmbeddingSpace } from \"./embedding-space.js\";\nimport { LOCAL_EMBEDDING_MODEL } from \"./models.js\";\n\n/**\n * Local Hugging Face models through transformers.js (ONNX Runtime, CPU). The\n * weights download once into `~/.cache/codebuddy/models` (override with\n * `CODEBUDDY_MODEL_CACHE`); afterwards nothing leaves the machine and no token\n * is needed. `@huggingface/transformers` is an optional dependency, loaded on\n * first use, so a missing install only disables semantic ranking.\n */\n\ntype Transformers = typeof import(\"@huggingface/transformers\");\n/** The slice of transformers.js' FeatureExtractionPipeline used here. */\ntype Extractor = (\n texts: string[],\n options: { pooling: \"cls\"; normalize: boolean },\n) => Promise<{ tolist(): number[][] }>;\n\n/** Hard cap on a single input; ModernBERT reads 8k tokens, memories are far shorter. */\nconst MAX_INPUT_CHARS = 8_000;\n\nlet transformers: Promise<Transformers> | null = null;\nlet extractor: Promise<Extractor> | null = null;\n\nexport function modelCacheDir(): string {\n return process.env.CODEBUDDY_MODEL_CACHE ?? join(homedir(), \".cache\", \"codebuddy\", \"models\");\n}\n\nfunction loadTransformers(): Promise<Transformers> {\n transformers ??= import(\"@huggingface/transformers\").then((module) => {\n module.env.cacheDir = modelCacheDir();\n // Info/debug logs go to console.log, i.e. stdout: the MCP JSON-RPC channel\n // under `codebuddy serve`. Errors only (they use stderr), here and in ONNX Runtime.\n module.env.logLevel = module.LogLevel.ERROR;\n return module;\n });\n // A failed import (optional dependency missing) is retried on the next call.\n transformers.catch(() => {\n transformers = null;\n });\n return transformers;\n}\n\nexport class LocalModelProvider implements ModelProvider {\n readonly space: EmbeddingSpace = {\n model: LOCAL_EMBEDDING_MODEL.id,\n revision: LOCAL_EMBEDDING_MODEL.revision,\n schema: LOCAL_EMBEDDING_MODEL.schema,\n dimensions: LOCAL_EMBEDDING_MODEL.dimensions,\n };\n\n /** `request.model` is ignored: one provider is one embedding space. */\n async embed(request: EmbeddingRequest): Promise<EmbeddingResponse> {\n const inputs = (typeof request.input === \"string\" ? [request.input] : request.input).map(\n (text) => text.slice(0, MAX_INPUT_CHARS),\n );\n extractor ??= loadTransformers().then(\n async (module) =>\n (await module.pipeline(\"feature-extraction\", this.space.model, {\n revision: this.space.revision,\n dtype: \"q8\",\n })) as unknown as Extractor,\n );\n // A failed load (no network on first use) is retried on the next call.\n extractor.catch(() => {\n extractor = null;\n });\n const extract = await extractor;\n // Granite r2 is trained with CLS pooling; L2-normalized so cosine = dot.\n const output = await extract(inputs, { pooling: \"cls\", normalize: true });\n return { ...this.space, vectors: output.tolist() };\n }\n\n async generateText(): Promise<TextGenerationResponse> {\n throw new Error(\"The local provider only embeds; text generation uses the BYOK LLM layer.\");\n }\n}\n\ntype Reranker = {\n tokenizer: (\n texts: string[],\n options: { text_pair: string[]; padding: boolean; truncation: boolean },\n ) => unknown;\n model: (inputs: unknown) => Promise<{ logits: { tolist(): number[][] } }>;\n};\nconst rerankers = new Map<string, Promise<Reranker>>();\n\n/**\n * Local cross-encoder logits for (query, document) pairs, in input order.\n * Higher is more relevant; values order documents for one query and are not\n * calibrated across queries, so callers order by them and never threshold.\n */\nexport async function rerankLocally(\n model: { id: string; revision?: string },\n query: string,\n documents: string[],\n): Promise<number[]> {\n if (documents.length === 0) return [];\n let pending = rerankers.get(model.id);\n if (!pending) {\n pending = loadTransformers().then(async (module) => {\n const options = model.revision ? { revision: model.revision } : {};\n const [tokenizer, classifier] = await Promise.all([\n module.AutoTokenizer.from_pretrained(model.id, options),\n module.AutoModelForSequenceClassification.from_pretrained(model.id, {\n ...options,\n dtype: \"q8\",\n }),\n ]);\n return { tokenizer, model: classifier } as unknown as Reranker;\n });\n rerankers.set(model.id, pending);\n pending.catch(() => rerankers.delete(model.id));\n }\n const { tokenizer, model: classifier } = await pending;\n const inputs = tokenizer(\n documents.map(() => query.slice(0, MAX_INPUT_CHARS)),\n {\n text_pair: documents.map((document) => document.slice(0, MAX_INPUT_CHARS)),\n padding: true,\n truncation: true,\n },\n );\n const { logits } = await classifier(inputs);\n return logits.tolist().map((row) => row[0] ?? Number.NEGATIVE_INFINITY);\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AA0BrB,IAAM,kBAAkB;AAExB,IAAI,eAA6C;AACjD,IAAI,YAAuC;AAEpC,SAAS,gBAAwB;AACtC,SAAO,QAAQ,IAAI,yBAAyB,KAAK,QAAQ,GAAG,UAAU,aAAa,QAAQ;AAC7F;AAEA,SAAS,mBAA0C;AACjD,mBAAiB,OAAO,2BAA2B,EAAE,KAAK,CAAC,WAAW;AACpE,WAAO,IAAI,WAAW,cAAc;AAGpC,WAAO,IAAI,WAAW,OAAO,SAAS;AACtC,WAAO;AAAA,EACT,CAAC;AAED,eAAa,MAAM,MAAM;AACvB,mBAAe;AAAA,EACjB,CAAC;AACD,SAAO;AACT;AAEO,IAAM,qBAAN,MAAkD;AAAA,EAC9C,QAAwB;AAAA,IAC/B,OAAO,sBAAsB;AAAA,IAC7B,UAAU,sBAAsB;AAAA,IAChC,QAAQ,sBAAsB;AAAA,IAC9B,YAAY,sBAAsB;AAAA,EACpC;AAAA;AAAA,EAGA,MAAM,MAAM,SAAuD;AACjE,UAAM,UAAU,OAAO,QAAQ,UAAU,WAAW,CAAC,QAAQ,KAAK,IAAI,QAAQ,OAAO;AAAA,MACnF,CAAC,SAAS,KAAK,MAAM,GAAG,eAAe;AAAA,IACzC;AACA,kBAAc,iBAAiB,EAAE;AAAA,MAC/B,OAAO,WACJ,MAAM,OAAO,SAAS,sBAAsB,KAAK,MAAM,OAAO;AAAA,QAC7D,UAAU,KAAK,MAAM;AAAA,QACrB,OAAO;AAAA,MACT,CAAC;AAAA,IACL;AAEA,cAAU,MAAM,MAAM;AACpB,kBAAY;AAAA,IACd,CAAC;AACD,UAAM,UAAU,MAAM;AAEtB,UAAM,SAAS,MAAM,QAAQ,QAAQ,EAAE,SAAS,OAAO,WAAW,KAAK,CAAC;AACxE,WAAO,EAAE,GAAG,KAAK,OAAO,SAAS,OAAO,OAAO,EAAE;AAAA,EACnD;AAAA,EAEA,MAAM,eAAgD;AACpD,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACF;AASA,IAAM,YAAY,oBAAI,IAA+B;AAOrD,eAAsB,cACpB,OACA,OACA,WACmB;AACnB,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AACpC,MAAI,UAAU,UAAU,IAAI,MAAM,EAAE;AACpC,MAAI,CAAC,SAAS;AACZ,cAAU,iBAAiB,EAAE,KAAK,OAAO,WAAW;AAClD,YAAM,UAAU,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AACjE,YAAM,CAACA,YAAWC,WAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,QAChD,OAAO,cAAc,gBAAgB,MAAM,IAAI,OAAO;AAAA,QACtD,OAAO,mCAAmC,gBAAgB,MAAM,IAAI;AAAA,UAClE,GAAG;AAAA,UACH,OAAO;AAAA,QACT,CAAC;AAAA,MACH,CAAC;AACD,aAAO,EAAE,WAAAD,YAAW,OAAOC,YAAW;AAAA,IACxC,CAAC;AACD,cAAU,IAAI,MAAM,IAAI,OAAO;AAC/B,YAAQ,MAAM,MAAM,UAAU,OAAO,MAAM,EAAE,CAAC;AAAA,EAChD;AACA,QAAM,EAAE,WAAW,OAAO,WAAW,IAAI,MAAM;AAC/C,QAAM,SAAS;AAAA,IACb,UAAU,IAAI,MAAM,MAAM,MAAM,GAAG,eAAe,CAAC;AAAA,IACnD;AAAA,MACE,WAAW,UAAU,IAAI,CAAC,aAAa,SAAS,MAAM,GAAG,eAAe,CAAC;AAAA,MACzE,SAAS;AAAA,MACT,YAAY;AAAA,IACd;AAAA,EACF;AACA,QAAM,EAAE,OAAO,IAAI,MAAM,WAAW,MAAM;AAC1C,SAAO,OAAO,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,OAAO,iBAAiB;AACxE;","names":["tokenizer","classifier"]}