opencode-rag-plugin 1.15.1 → 1.17.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 (232) hide show
  1. package/ReadMe.md +4 -6
  2. package/dist/api.js +44 -24
  3. package/dist/chunker/base.d.ts +34 -3
  4. package/dist/chunker/base.js +70 -21
  5. package/dist/chunker/factory.d.ts +4 -1
  6. package/dist/chunker/factory.js +12 -1
  7. package/dist/chunker/grammar.js +3 -0
  8. package/dist/chunker/image.js +8 -8
  9. package/dist/chunker/pdf.js +11 -13
  10. package/dist/chunker/xml.d.ts +2 -0
  11. package/dist/chunker/xml.js +2 -0
  12. package/dist/cli/commands/index-command.js +0 -4
  13. package/dist/cli/commands/index.d.ts +1 -1
  14. package/dist/cli/commands/index.js +1 -1
  15. package/dist/cli/commands/init-helpers.d.ts +12 -0
  16. package/dist/cli/commands/init-helpers.js +84 -2
  17. package/dist/cli/commands/init.js +20 -2
  18. package/dist/cli/commands/query.js +1 -0
  19. package/dist/cli/commands/setup.d.ts +2 -0
  20. package/dist/cli/commands/setup.js +113 -0
  21. package/dist/cli/commands/status.js +64 -2
  22. package/dist/cli/index.js +2 -2
  23. package/dist/content/image.js +24 -3
  24. package/dist/content/reader.d.ts +6 -1
  25. package/dist/content/reader.js +49 -4
  26. package/dist/core/config.d.ts +19 -0
  27. package/dist/core/config.js +34 -4
  28. package/dist/core/desc-cache.d.ts +31 -0
  29. package/dist/core/desc-cache.js +124 -0
  30. package/dist/core/interfaces.d.ts +58 -3
  31. package/dist/core/manifest.d.ts +24 -1
  32. package/dist/core/manifest.js +34 -3
  33. package/dist/core/resolve-api-key.js +4 -2
  34. package/dist/core/runtime-overrides.js +9 -7
  35. package/dist/core/setup-runtime.d.ts +23 -0
  36. package/dist/core/setup-runtime.js +183 -0
  37. package/dist/core/version-check.d.ts +9 -0
  38. package/dist/core/version-check.js +49 -0
  39. package/dist/describer/anthropic.d.ts +2 -2
  40. package/dist/describer/anthropic.js +5 -7
  41. package/dist/describer/describer.d.ts +2 -2
  42. package/dist/describer/describer.js +6 -8
  43. package/dist/describer/gemini.d.ts +2 -2
  44. package/dist/describer/gemini.js +5 -7
  45. package/dist/embedder/factory.d.ts +3 -1
  46. package/dist/embedder/factory.js +7 -1
  47. package/dist/embedder/health.js +4 -0
  48. package/dist/embedder/http.d.ts +1 -1
  49. package/dist/embedder/http.js +74 -43
  50. package/dist/eval/compare-merge.d.ts +10 -0
  51. package/dist/eval/compare-merge.js +537 -0
  52. package/dist/eval/compare-rankings.d.ts +10 -0
  53. package/dist/eval/compare-rankings.js +245 -0
  54. package/dist/eval/dump-descriptions.d.ts +7 -0
  55. package/dist/eval/dump-descriptions.js +58 -0
  56. package/dist/eval/fast-index.d.ts +8 -0
  57. package/dist/eval/fast-index.js +283 -0
  58. package/dist/eval/run-branch-compare.d.ts +7 -0
  59. package/dist/eval/run-branch-compare.js +220 -0
  60. package/dist/eval/run-token-test.js +1 -0
  61. package/dist/eval/test-kw.d.ts +1 -0
  62. package/dist/eval/test-kw.js +22 -0
  63. package/dist/eval/update-descriptions.d.ts +7 -0
  64. package/dist/eval/update-descriptions.js +84 -0
  65. package/dist/index.d.ts +1 -0
  66. package/dist/index.js +1 -0
  67. package/dist/indexer/embed-stage.js +2 -1
  68. package/dist/indexer/git-diff.js +21 -9
  69. package/dist/indexer/pipeline.d.ts +6 -0
  70. package/dist/indexer/pipeline.js +290 -37
  71. package/dist/indexer/watch.js +1 -3
  72. package/dist/indexer/worker.d.ts +15 -2
  73. package/dist/indexer/worker.js +25 -12
  74. package/dist/mcp/handlers.d.ts +9 -0
  75. package/dist/mcp/handlers.js +23 -6
  76. package/dist/mcp/server.js +2 -0
  77. package/dist/opencode/create-read-tool.d.ts +2 -0
  78. package/dist/opencode/create-read-tool.js +8 -2
  79. package/dist/opencode/read-fallback.d.ts +1 -5
  80. package/dist/opencode/read-fallback.js +1 -18
  81. package/dist/opencode/read-format.js +5 -3
  82. package/dist/opencode/tools.js +5 -7
  83. package/dist/plugin.js +141 -57
  84. package/dist/retriever/keyword-index.d.ts +3 -2
  85. package/dist/retriever/keyword-index.js +25 -1
  86. package/dist/retriever/retriever.d.ts +4 -1
  87. package/dist/retriever/retriever.js +34 -56
  88. package/dist/vectorstore/lancedb.d.ts +35 -4
  89. package/dist/vectorstore/lancedb.js +146 -23
  90. package/dist/vectorstore/memory.d.ts +6 -1
  91. package/dist/vectorstore/memory.js +58 -0
  92. package/dist/watcher.js +3 -0
  93. package/dist/web/api.js +10 -2
  94. package/dist/web/server.js +18 -3
  95. package/package.json +8 -9
  96. package/scripts/postinstall-setup.js +82 -0
  97. package/dist/api.js.map +0 -1
  98. package/dist/chunker/base.js.map +0 -1
  99. package/dist/chunker/bash.js.map +0 -1
  100. package/dist/chunker/c.js.map +0 -1
  101. package/dist/chunker/cpp.js.map +0 -1
  102. package/dist/chunker/csharp.js.map +0 -1
  103. package/dist/chunker/css.js.map +0 -1
  104. package/dist/chunker/doc.js.map +0 -1
  105. package/dist/chunker/dockerfile.js.map +0 -1
  106. package/dist/chunker/docx.js.map +0 -1
  107. package/dist/chunker/excel.js.map +0 -1
  108. package/dist/chunker/factory.js.map +0 -1
  109. package/dist/chunker/fallback.js.map +0 -1
  110. package/dist/chunker/go.js.map +0 -1
  111. package/dist/chunker/grammar.js.map +0 -1
  112. package/dist/chunker/html.js.map +0 -1
  113. package/dist/chunker/image.js.map +0 -1
  114. package/dist/chunker/ini.js.map +0 -1
  115. package/dist/chunker/java.js.map +0 -1
  116. package/dist/chunker/javascript.js.map +0 -1
  117. package/dist/chunker/json.js.map +0 -1
  118. package/dist/chunker/kotlin.js.map +0 -1
  119. package/dist/chunker/loader.js.map +0 -1
  120. package/dist/chunker/markdown.js.map +0 -1
  121. package/dist/chunker/pdf.js.map +0 -1
  122. package/dist/chunker/php.js.map +0 -1
  123. package/dist/chunker/powershell.js.map +0 -1
  124. package/dist/chunker/python.js.map +0 -1
  125. package/dist/chunker/razor.js.map +0 -1
  126. package/dist/chunker/ruby.js.map +0 -1
  127. package/dist/chunker/rust.js.map +0 -1
  128. package/dist/chunker/sln.js.map +0 -1
  129. package/dist/chunker/sql.js.map +0 -1
  130. package/dist/chunker/ssl.js.map +0 -1
  131. package/dist/chunker/swift.js.map +0 -1
  132. package/dist/chunker/tex.js.map +0 -1
  133. package/dist/chunker/toml.js.map +0 -1
  134. package/dist/chunker/typescript.js.map +0 -1
  135. package/dist/chunker/uuid.js.map +0 -1
  136. package/dist/chunker/xml.js.map +0 -1
  137. package/dist/chunker/yaml.js.map +0 -1
  138. package/dist/cli/commands/clear.js.map +0 -1
  139. package/dist/cli/commands/describe-image.js.map +0 -1
  140. package/dist/cli/commands/dump.js.map +0 -1
  141. package/dist/cli/commands/eval.js.map +0 -1
  142. package/dist/cli/commands/index-command.js.map +0 -1
  143. package/dist/cli/commands/index.js.map +0 -1
  144. package/dist/cli/commands/init-helpers.js.map +0 -1
  145. package/dist/cli/commands/init.js.map +0 -1
  146. package/dist/cli/commands/list.js.map +0 -1
  147. package/dist/cli/commands/mcp.js.map +0 -1
  148. package/dist/cli/commands/query.js.map +0 -1
  149. package/dist/cli/commands/show.js.map +0 -1
  150. package/dist/cli/commands/status.js.map +0 -1
  151. package/dist/cli/commands/ui.js.map +0 -1
  152. package/dist/cli/commands/update.d.ts +0 -17
  153. package/dist/cli/commands/update.js +0 -79
  154. package/dist/cli/commands/update.js.map +0 -1
  155. package/dist/cli/format.js.map +0 -1
  156. package/dist/cli/helpers.js.map +0 -1
  157. package/dist/cli/index.js.map +0 -1
  158. package/dist/cli/progress.d.ts +0 -42
  159. package/dist/cli/progress.js +0 -137
  160. package/dist/cli/progress.js.map +0 -1
  161. package/dist/cli/types.js.map +0 -1
  162. package/dist/cli.js.map +0 -1
  163. package/dist/content/doc.js.map +0 -1
  164. package/dist/content/docx.js.map +0 -1
  165. package/dist/content/excel.js.map +0 -1
  166. package/dist/content/image.js.map +0 -1
  167. package/dist/content/pdf.js.map +0 -1
  168. package/dist/content/reader.js.map +0 -1
  169. package/dist/content/types.js.map +0 -1
  170. package/dist/core/bootstrap.js.map +0 -1
  171. package/dist/core/config.js.map +0 -1
  172. package/dist/core/doc-progress.js.map +0 -1
  173. package/dist/core/fileLogger.js.map +0 -1
  174. package/dist/core/interfaces.js.map +0 -1
  175. package/dist/core/manifest.js.map +0 -1
  176. package/dist/core/provider-defaults.js.map +0 -1
  177. package/dist/core/rag-injection-flag.js.map +0 -1
  178. package/dist/core/resolve-api-key.js.map +0 -1
  179. package/dist/core/runtime-overrides.js.map +0 -1
  180. package/dist/describer/anthropic.js.map +0 -1
  181. package/dist/describer/describer.js.map +0 -1
  182. package/dist/describer/factory.js.map +0 -1
  183. package/dist/describer/gemini.js.map +0 -1
  184. package/dist/describer/shared.js.map +0 -1
  185. package/dist/embedder/cohere.js.map +0 -1
  186. package/dist/embedder/factory.js.map +0 -1
  187. package/dist/embedder/health.js.map +0 -1
  188. package/dist/embedder/http.js.map +0 -1
  189. package/dist/embedder/ollama.js.map +0 -1
  190. package/dist/embedder/openai.js.map +0 -1
  191. package/dist/eval/index.js.map +0 -1
  192. package/dist/eval/run-token-test.js.map +0 -1
  193. package/dist/eval/session-logger.js.map +0 -1
  194. package/dist/eval/storage.js.map +0 -1
  195. package/dist/eval/token-analysis.js.map +0 -1
  196. package/dist/eval/token-counter.js.map +0 -1
  197. package/dist/eval/types.js.map +0 -1
  198. package/dist/index.js.map +0 -1
  199. package/dist/indexer/description-stage.js.map +0 -1
  200. package/dist/indexer/embed-stage.js.map +0 -1
  201. package/dist/indexer/git-diff.js.map +0 -1
  202. package/dist/indexer/metadata.js.map +0 -1
  203. package/dist/indexer/pipeline.js.map +0 -1
  204. package/dist/indexer/stats.js.map +0 -1
  205. package/dist/indexer/watch.js.map +0 -1
  206. package/dist/indexer/worker.js.map +0 -1
  207. package/dist/indexer.js.map +0 -1
  208. package/dist/mcp/cli.js.map +0 -1
  209. package/dist/mcp/handlers.js.map +0 -1
  210. package/dist/mcp/server.js.map +0 -1
  211. package/dist/opencode/create-read-tool.js.map +0 -1
  212. package/dist/opencode/read-fallback.js.map +0 -1
  213. package/dist/opencode/read-format.js.map +0 -1
  214. package/dist/opencode/read-query.js.map +0 -1
  215. package/dist/opencode/tool-args.js.map +0 -1
  216. package/dist/opencode/tools.js.map +0 -1
  217. package/dist/plugin-entry.js.map +0 -1
  218. package/dist/plugin.js.map +0 -1
  219. package/dist/retriever/context-optimizer.js.map +0 -1
  220. package/dist/retriever/keyword-index.js.map +0 -1
  221. package/dist/retriever/retriever.js.map +0 -1
  222. package/dist/tui.js.map +0 -1
  223. package/dist/updater.d.ts +0 -45
  224. package/dist/updater.js +0 -175
  225. package/dist/updater.js.map +0 -1
  226. package/dist/vectorstore/factory.js.map +0 -1
  227. package/dist/vectorstore/lancedb.js.map +0 -1
  228. package/dist/vectorstore/memory.js.map +0 -1
  229. package/dist/watcher.js.map +0 -1
  230. package/dist/web/api.js.map +0 -1
  231. package/dist/web/server.js.map +0 -1
  232. package/dist/web/static.js.map +0 -1
@@ -0,0 +1,220 @@
1
+ /**
2
+ * @fileoverview Branch comparison benchmark — runs N queries against the indexed codebase
3
+ * and outputs per-query results as JSON for later comparison.
4
+ *
5
+ * Usage: node --import tsx src/eval/run-branch-compare.ts --output .opencode/rag_db/eval-results/branch.json
6
+ */
7
+ import { writeFileSync } from "node:fs";
8
+ import path from "node:path";
9
+ import { loadConfig, DEFAULT_CONFIG } from "../core/config.js";
10
+ import { createEmbedder } from "../embedder/factory.js";
11
+ import { createVectorStore } from "../vectorstore/factory.js";
12
+ import { retrieve } from "../retriever/retriever.js";
13
+ import { KeywordIndex } from "../retriever/keyword-index.js";
14
+ import { loadRuntimeOverrides, applyRuntimeOverrides, } from "../core/runtime-overrides.js";
15
+ import { resolveApiKey } from "../core/resolve-api-key.js";
16
+ import { execSync } from "node:child_process";
17
+ const WORKTREE = process.cwd();
18
+ const STORE_PATH = path.join(WORKTREE, ".opencode", "rag_db");
19
+ const QUERIES = [
20
+ "How does the retrieval pipeline work end-to-end?",
21
+ "How does the plugin interact with chat messages?",
22
+ "How does the keyword index combine with vector search?",
23
+ "Where is the embedder factory defined?",
24
+ "Where is the LanceDB store implementation?",
25
+ "Find all usages of the retrieve function",
26
+ "Find all usages of SearchResult type",
27
+ "How does the chunker factory register new languages?",
28
+ "What is the default minScore configuration?",
29
+ "How does the session logger capture token usage?",
30
+ "How does L2 normalization affect vector search scores?",
31
+ "What is the MetadataFilter interface used for?",
32
+ "How does the background indexer handle file changes?",
33
+ "Where is the config validation logic?",
34
+ "How are PDF documents chunked and indexed?",
35
+ "What embedding providers are supported?",
36
+ "How does the CLI parse and dispatch commands?",
37
+ "How does the session logger persist events?",
38
+ "What is the manifest schema version used for?",
39
+ "How does the OpenCode plugin register tools?",
40
+ "Where is the globMatch function defined?",
41
+ "How does the proxy-aware HTTP client work?",
42
+ "What is the FETCH_OVERFETCH_FACTOR constant?",
43
+ "How does the TUI settings menu work?",
44
+ "How are image descriptions generated?",
45
+ // Set B — Code-identifier queries (test hybrid search with keyword contributions)
46
+ "retrieve function",
47
+ "KeywordIndex class",
48
+ "embedder factory",
49
+ "vector store LanceDB",
50
+ "find usages SearchResult",
51
+ "chunkFile method",
52
+ "embedBatch function",
53
+ "retriever retrieve vector search",
54
+ "session logger token",
55
+ "config validation",
56
+ ];
57
+ const THRESHOLDS = [0.85, 0.75, 0.65, 0.50, 0.35];
58
+ function getGitInfo() {
59
+ try {
60
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
61
+ encoding: "utf-8",
62
+ }).trim();
63
+ const commit = execSync("git rev-parse --short HEAD", {
64
+ encoding: "utf-8",
65
+ }).trim();
66
+ return { branch, commit };
67
+ }
68
+ catch {
69
+ return { branch: "unknown", commit: "unknown" };
70
+ }
71
+ }
72
+ function getConfig() {
73
+ const configPath = path.join(WORKTREE, "opencode-rag.json");
74
+ let cfg;
75
+ try {
76
+ cfg = loadConfig(configPath);
77
+ }
78
+ catch {
79
+ cfg = DEFAULT_CONFIG;
80
+ }
81
+ const overrides = loadRuntimeOverrides(STORE_PATH);
82
+ cfg = applyRuntimeOverrides(cfg, overrides);
83
+ resolveApiKey(cfg, WORKTREE);
84
+ return cfg;
85
+ }
86
+ async function probeDimension(embedder) {
87
+ try {
88
+ const probe = await embedder.embed(["dimension-probe"], "query");
89
+ if (probe.length > 0 && probe[0].length > 0) {
90
+ return probe[0].length;
91
+ }
92
+ }
93
+ catch {
94
+ // fall through to default
95
+ }
96
+ return 384;
97
+ }
98
+ function topResults(results, worktree, max = 5) {
99
+ return results.slice(0, max).map((r, i) => {
100
+ const exp = r.explanation;
101
+ return {
102
+ rank: i,
103
+ score: r.score,
104
+ filePath: path.relative(worktree, r.chunk.metadata.filePath).replace(/\\/g, "/"),
105
+ startLine: r.chunk.metadata.startLine,
106
+ endLine: r.chunk.metadata.endLine,
107
+ language: r.chunk.metadata.language,
108
+ explanation: exp
109
+ ? {
110
+ vectorScore: exp.scoreBreakdown.vectorScore,
111
+ keywordScore: exp.scoreBreakdown.keywordScore,
112
+ rawVectorScore: exp.scoreBreakdown.rawVectorScore,
113
+ rawKeywordScore: exp.scoreBreakdown.rawKeywordScore,
114
+ keywordWeight: exp.scoreBreakdown.keywordWeight,
115
+ vectorRank: exp.scoreBreakdown.vectorRank,
116
+ keywordRank: exp.scoreBreakdown.keywordRank,
117
+ matchedTerms: exp.matchedTerms,
118
+ }
119
+ : undefined,
120
+ };
121
+ });
122
+ }
123
+ function thresholdAnalysis(results) {
124
+ return THRESHOLDS.map((t) => ({
125
+ threshold: t,
126
+ passedCount: results.filter((r) => r.score >= t).length,
127
+ wouldInject: results.some((r) => r.score >= t),
128
+ }));
129
+ }
130
+ function parseArgs() {
131
+ const args = process.argv.slice(2);
132
+ let output = path.join(WORKTREE, ".opencode", "rag_db", "eval-results", "branch.json");
133
+ for (let i = 0; i < args.length; i++) {
134
+ if (args[i] === "--output" && args[i + 1]) {
135
+ output = path.resolve(WORKTREE, args[i + 1]);
136
+ break;
137
+ }
138
+ }
139
+ return { output };
140
+ }
141
+ async function main() {
142
+ const { output } = parseArgs();
143
+ const { branch, commit } = getGitInfo();
144
+ console.log(`\n OpenCodeRAG Branch Benchmark`);
145
+ console.log(` Branch: ${branch} @ ${commit}`);
146
+ console.log(` Output: ${output}\n`);
147
+ const cfg = getConfig();
148
+ const embedder = createEmbedder(cfg);
149
+ const dimension = await probeDimension(embedder);
150
+ const store = createVectorStore(cfg, STORE_PATH, dimension);
151
+ let keywordIndex;
152
+ try {
153
+ keywordIndex = await KeywordIndex.load(STORE_PATH);
154
+ }
155
+ catch {
156
+ // optional
157
+ }
158
+ const indexedCount = await store.count();
159
+ console.log(` Indexed chunks: ${indexedCount}`);
160
+ console.log(` Embedding: ${cfg.embedding.provider}/${cfg.embedding.model} (${dimension}d)`);
161
+ console.log(` Retrieval: topK=${cfg.retrieval.topK}, minScore=${cfg.retrieval.minScore}, hybrid=${cfg.retrieval.hybridSearch?.enabled}\n`);
162
+ const queryResults = [];
163
+ for (let i = 0; i < QUERIES.length; i++) {
164
+ const query = QUERIES[i];
165
+ process.stdout.write(` [${i + 1}/${QUERIES.length}] ${query.substring(0, 55)}...`);
166
+ const start = performance.now();
167
+ const searchResults = await retrieve(query, embedder, store, {
168
+ topK: cfg.retrieval.topK,
169
+ minScore: 0, // no filter — we want all scores for threshold analysis
170
+ keywordIndex,
171
+ keywordWeight: cfg.retrieval.hybridSearch?.keywordWeight,
172
+ hybridEnabled: cfg.retrieval.hybridSearch?.enabled,
173
+ queryPrefix: cfg.embedding.queryPrefix,
174
+ explain: true,
175
+ });
176
+ const elapsed = performance.now() - start;
177
+ queryResults.push({
178
+ query,
179
+ queryIndex: i,
180
+ resultCount: searchResults.length,
181
+ latencyMs: Math.round(elapsed * 10) / 10,
182
+ topResults: topResults(searchResults, WORKTREE, 5),
183
+ thresholdAnalysis: thresholdAnalysis(searchResults),
184
+ });
185
+ const topScore = searchResults.length > 0
186
+ ? searchResults[0].score.toFixed(3)
187
+ : "N/A";
188
+ console.log(` ${searchResults.length} results, top=${topScore}, ${Math.round(elapsed)}ms`);
189
+ }
190
+ const outputPayload = {
191
+ branch,
192
+ commit,
193
+ timestamp: new Date().toISOString(),
194
+ config: {
195
+ embeddingProvider: cfg.embedding.provider,
196
+ embeddingModel: cfg.embedding.model,
197
+ topK: cfg.retrieval.topK,
198
+ minScore: cfg.retrieval.minScore,
199
+ hybridEnabled: cfg.retrieval.hybridSearch?.enabled ?? false,
200
+ keywordWeight: cfg.retrieval.hybridSearch?.keywordWeight ?? 0.4,
201
+ },
202
+ indexChunkCount: indexedCount,
203
+ queries: queryResults,
204
+ };
205
+ const dir = path.dirname(output);
206
+ try {
207
+ const { mkdirSync } = await import("node:fs");
208
+ mkdirSync(dir, { recursive: true });
209
+ }
210
+ catch {
211
+ // dir already exists
212
+ }
213
+ writeFileSync(output, JSON.stringify(outputPayload, null, 2), "utf-8");
214
+ console.log(`\n Results written to: ${output}\n`);
215
+ }
216
+ main().catch((err) => {
217
+ console.error("Benchmark failed:", err);
218
+ process.exit(1);
219
+ });
220
+ //# sourceMappingURL=run-branch-compare.js.map
@@ -95,6 +95,7 @@ async function main() {
95
95
  minScore: cfg.retrieval.minScore,
96
96
  keywordIndex,
97
97
  keywordWeight: cfg.retrieval.hybridSearch?.keywordWeight,
98
+ hybridEnabled: cfg.retrieval.hybridSearch?.enabled,
98
99
  queryPrefix: cfg.embedding.queryPrefix,
99
100
  });
100
101
  const inputTokens = countTokens(query);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { KeywordIndex } from "../retriever/keyword-index.js";
2
+ import path from "node:path";
3
+ const storePath = path.join(process.cwd(), ".opencode", "rag_db");
4
+ const ki = await KeywordIndex.load(storePath);
5
+ console.log("Keyword index entries:", ki.count());
6
+ const queries = [
7
+ "retrieve function",
8
+ "KeywordIndex class",
9
+ "embedder factory",
10
+ "vector store LanceDB",
11
+ "find usages SearchResult",
12
+ ];
13
+ for (const q of queries) {
14
+ const res = ki.search(q, 5);
15
+ console.log(`\n"${q}": ${res.length} results`);
16
+ for (const r of res) {
17
+ const fp = r.chunk.metadata.filePath.substring(r.chunk.metadata.filePath.lastIndexOf("/src/") + 1);
18
+ console.log(` score=${r.score.toFixed(2)} ${fp}:${r.chunk.metadata.startLine}-${r.chunk.metadata.endLine}`);
19
+ }
20
+ }
21
+ ki.close();
22
+ //# sourceMappingURL=test-kw.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Update a LanceDB index's chunk descriptions from a JSON dump file.
3
+ * Run AFTER normal indexing to replace descriptions with ones from another branch.
4
+ *
5
+ * Usage: node --import tsx src/eval/update-descriptions.ts --input .opencode/rag_db/chunk-descriptions.json
6
+ */
7
+ export {};
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @fileoverview Update a LanceDB index's chunk descriptions from a JSON dump file.
3
+ * Run AFTER normal indexing to replace descriptions with ones from another branch.
4
+ *
5
+ * Usage: node --import tsx src/eval/update-descriptions.ts --input .opencode/rag_db/chunk-descriptions.json
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import path from "node:path";
9
+ async function main() {
10
+ const args = process.argv.slice(2);
11
+ const storePath = path.join(process.cwd(), ".opencode", "rag_db");
12
+ let inputPath = "";
13
+ for (let i = 0; i < args.length; i++) {
14
+ if (args[i] === "--input" && args[i + 1]) {
15
+ inputPath = path.resolve(args[i + 1]);
16
+ break;
17
+ }
18
+ }
19
+ if (!inputPath) {
20
+ console.error("Usage: node --import tsx src/eval/update-descriptions.ts --input <descriptions.json>");
21
+ process.exit(1);
22
+ }
23
+ console.log(` Loading descriptions from: ${inputPath}`);
24
+ const sourceDescs = JSON.parse(readFileSync(inputPath, "utf-8"));
25
+ console.log(` Loaded ${Object.keys(sourceDescs).length} descriptions`);
26
+ console.log(` Connecting to store: ${storePath}`);
27
+ const lancedb = await import("@lancedb/lancedb");
28
+ const db = await lancedb.connect(storePath);
29
+ const tableNames = await db.tableNames();
30
+ if (!tableNames.includes("chunks")) {
31
+ console.error(" No chunks table found.");
32
+ await db.close();
33
+ process.exit(1);
34
+ }
35
+ const table = await db.openTable("chunks");
36
+ const count = await table.countRows();
37
+ console.log(` Total rows: ${count}`);
38
+ // Read all rows
39
+ const rows = await table.query().select(["id", "description"]).limit(count).toArray();
40
+ let updated = 0;
41
+ let notFound = 0;
42
+ let batch = [];
43
+ for (const row of rows) {
44
+ const id = row.id;
45
+ const sourceDesc = sourceDescs[id];
46
+ if (sourceDesc === undefined) {
47
+ notFound++;
48
+ continue;
49
+ }
50
+ batch.push({ id, description: sourceDesc });
51
+ updated++;
52
+ // Update in batches of 100
53
+ if (batch.length >= 100) {
54
+ console.log(` Updating batch of ${batch.length}...`);
55
+ for (const b of batch) {
56
+ await table.update({
57
+ where: `id = '${b.id.replace(/'/g, "''")}'`,
58
+ values: { description: b.description },
59
+ });
60
+ }
61
+ batch = [];
62
+ }
63
+ }
64
+ // Final batch
65
+ if (batch.length > 0) {
66
+ console.log(` Updating final batch of ${batch.length}...`);
67
+ for (const b of batch) {
68
+ await table.update({
69
+ where: `id = '${b.id.replace(/'/g, "''")}'`,
70
+ values: { description: b.description },
71
+ });
72
+ }
73
+ }
74
+ console.log(`\n Done:`);
75
+ console.log(` Updated: ${updated}`);
76
+ console.log(` Not found in dump: ${notFound}`);
77
+ console.log(` Source dump had ${Object.keys(sourceDescs).length} entries`);
78
+ await db.close();
79
+ }
80
+ main().catch((err) => {
81
+ console.error("Update failed:", err);
82
+ process.exit(1);
83
+ });
84
+ //# sourceMappingURL=update-descriptions.js.map
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export { loadConfig, DEFAULT_CONFIG } from "./core/config.js";
15
15
  export { createBackgroundIndexer } from "./watcher.js";
16
16
  export { createWatchIgnore } from "./indexer.js";
17
17
  export { ImageChunker, createImageVisionProvider, getMimeType, SUPPORTED_IMAGE_EXTENSIONS } from "./chunker/image.js";
18
+ export { DescriptionCache } from "./core/desc-cache.js";
18
19
  export type { RagConfig, DescriptionConfig, ImageDescriptionConfig } from "./core/config.js";
19
20
  export type { Chunk, SearchResult, OptimizedSearchResult, Chunker, DescriptionProvider, EmbeddingProvider, VectorStore } from "./core/interfaces.js";
20
21
  export type { ContextOptimizationConfig, ContextOptimizationOptions } from "./retriever/context-optimizer.js";
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export { loadConfig, DEFAULT_CONFIG } from "./core/config.js";
15
15
  export { createBackgroundIndexer } from "./watcher.js";
16
16
  export { createWatchIgnore } from "./indexer.js";
17
17
  export { ImageChunker, createImageVisionProvider, getMimeType, SUPPORTED_IMAGE_EXTENSIONS } from "./chunker/image.js";
18
+ export { DescriptionCache } from "./core/desc-cache.js";
18
19
  /**
19
20
  * High-level convenience API — search, index, and retrieve context in a single function call.
20
21
  * @module
@@ -18,7 +18,8 @@ export async function embedChunks({ chunks, embedder, batchSize, concurrency, })
18
18
  const chunk = chunks[i];
19
19
  if (chunk.embedding && chunk.embedding.length > 0)
20
20
  continue;
21
- textsToEmbed.push(chunk.content);
21
+ const text = "Description: " + "\n" + (chunk.description || "") + "\n" + "Content:" + "\n" + chunk.content;
22
+ textsToEmbed.push(text);
22
23
  chunkIndices.push(i);
23
24
  }
24
25
  if (textsToEmbed.length > 0) {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Uses git to detect changed, deleted, and untracked files for incremental indexing.
3
3
  */
4
- import { execSync } from "node:child_process";
4
+ import { execFileSync } from "node:child_process";
5
5
  /**
6
6
  * Resolve the root directory of the git repository containing `cwd`.
7
7
  *
@@ -10,7 +10,7 @@ import { execSync } from "node:child_process";
10
10
  */
11
11
  export function getRepoRoot(cwd) {
12
12
  try {
13
- return execSync("git rev-parse --show-toplevel", {
13
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
14
14
  cwd,
15
15
  encoding: "utf-8",
16
16
  timeout: 5000,
@@ -29,7 +29,7 @@ export function getRepoRoot(cwd) {
29
29
  */
30
30
  export function getCurrentCommit(cwd) {
31
31
  try {
32
- return execSync("git rev-parse HEAD", {
32
+ return execFileSync("git", ["rev-parse", "HEAD"], {
33
33
  cwd,
34
34
  encoding: "utf-8",
35
35
  timeout: 5000,
@@ -49,8 +49,12 @@ export function getCurrentCommit(cwd) {
49
49
  */
50
50
  export function getChangedFilesSince(cwd, fromCommit) {
51
51
  try {
52
- const changedRaw = execSync(`git diff --name-only --diff-filter=ACMRT "${fromCommit}" HEAD`, { cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"] }).trim();
53
- const deletedRaw = execSync(`git diff --name-only --diff-filter=D "${fromCommit}" HEAD`, { cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"] }).trim();
52
+ const changedRaw = execFileSync("git", ["diff", "--name-only", "--diff-filter=ACMRT", fromCommit, "HEAD"], {
53
+ cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"],
54
+ }).trim();
55
+ const deletedRaw = execFileSync("git", ["diff", "--name-only", "--diff-filter=D", fromCommit, "HEAD"], {
56
+ cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"],
57
+ }).trim();
54
58
  const changedFiles = changedRaw.length > 0 ? changedRaw.split("\n") : [];
55
59
  const deletedFiles = deletedRaw.length > 0 ? deletedRaw.split("\n") : [];
56
60
  const currentCommit = getCurrentCommit(cwd);
@@ -70,7 +74,9 @@ export function getChangedFilesSince(cwd, fromCommit) {
70
74
  */
71
75
  export function getUntrackedFiles(cwd) {
72
76
  try {
73
- const raw = execSync("git ls-files --others --exclude-standard", { cwd, encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).trim();
77
+ const raw = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
78
+ cwd, encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"],
79
+ }).trim();
74
80
  return raw.length > 0 ? raw.split("\n") : [];
75
81
  }
76
82
  catch {
@@ -87,9 +93,15 @@ export function getUntrackedFiles(cwd) {
87
93
  */
88
94
  export function getWorkingTreeChanges(cwd) {
89
95
  try {
90
- const modifiedRaw = execSync("git diff-index --name-only -M HEAD", { cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"] }).trim();
91
- const deletedRaw = execSync("git diff --name-only --diff-filter=D HEAD", { cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"] }).trim();
92
- const untrackedRaw = execSync("git ls-files --others --exclude-standard", { cwd, encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).trim();
96
+ const modifiedRaw = execFileSync("git", ["diff-index", "--name-only", "-M", "HEAD"], {
97
+ cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"],
98
+ }).trim();
99
+ const deletedRaw = execFileSync("git", ["diff", "--name-only", "--diff-filter=D", "HEAD"], {
100
+ cwd, encoding: "utf-8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"],
101
+ }).trim();
102
+ const untrackedRaw = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
103
+ cwd, encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"],
104
+ }).trim();
93
105
  const changedFiles = modifiedRaw.length > 0 ? modifiedRaw.split("\n") : [];
94
106
  const deletedFiles = deletedRaw.length > 0 ? deletedRaw.split("\n") : [];
95
107
  const untrackedFiles = untrackedRaw.length > 0 ? untrackedRaw.split("\n") : [];
@@ -1,5 +1,6 @@
1
1
  import type { RagConfig } from "../core/config.js";
2
2
  import type { DescriptionProvider, EmbeddingProvider, IndexProgress, KeywordIndex, VectorStore } from "../core/interfaces.js";
3
+ import type { ImageVisionProvider } from "../chunker/image.js";
3
4
  import { type IndexRunStats, type IndexStatusSummary } from "./stats.js";
4
5
  export type { WatchPassScheduler } from "./watch.js";
5
6
  export { createWatchPassScheduler, createWatchIgnore } from "./watch.js";
@@ -39,6 +40,11 @@ export interface RunIndexPassOptions {
39
40
  * entries are removed from the index without comparing against the scanned set.
40
41
  */
41
42
  deletedPaths?: string[];
43
+ /**
44
+ * Optional injected image vision provider for testing. When omitted, the
45
+ * provider is created from the imageDescription config as usual.
46
+ */
47
+ imageVisionProvider?: ImageVisionProvider;
42
48
  }
43
49
  /** Minimal logger interface for indexing pipeline diagnostics. */
44
50
  export interface Logger {