opencode-rag-plugin 1.15.1 → 1.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReadMe.md +52 -9
- package/dist/api.js +44 -24
- package/dist/chunker/base.d.ts +34 -3
- package/dist/chunker/base.js +70 -21
- package/dist/chunker/factory.d.ts +4 -1
- package/dist/chunker/factory.js +12 -1
- package/dist/chunker/grammar.js +3 -0
- package/dist/chunker/image.js +8 -8
- package/dist/chunker/pdf.js +11 -13
- package/dist/chunker/xml.d.ts +2 -0
- package/dist/chunker/xml.js +2 -0
- package/dist/cli/commands/index-command.js +0 -4
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/init-helpers.d.ts +12 -0
- package/dist/cli/commands/init-helpers.js +87 -2
- package/dist/cli/commands/init.js +20 -2
- package/dist/cli/commands/query.js +1 -0
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.js +113 -0
- package/dist/cli/commands/status.js +64 -2
- package/dist/cli/commands/update.d.ts +4 -6
- package/dist/cli/commands/update.js +44 -58
- package/dist/cli/index.js +2 -1
- package/dist/content/image.js +24 -3
- package/dist/content/reader.d.ts +6 -1
- package/dist/content/reader.js +49 -4
- package/dist/core/config.d.ts +29 -1
- package/dist/core/config.js +105 -8
- package/dist/core/desc-cache.d.ts +31 -0
- package/dist/core/desc-cache.js +124 -0
- package/dist/core/interfaces.d.ts +58 -3
- package/dist/core/manifest.d.ts +24 -1
- package/dist/core/manifest.js +34 -3
- package/dist/core/resolve-api-key.js +4 -2
- package/dist/core/runtime-overrides.js +12 -8
- package/dist/core/setup-runtime.d.ts +23 -0
- package/dist/core/setup-runtime.js +231 -0
- package/dist/core/version-check.d.ts +85 -0
- package/dist/core/version-check.js +160 -0
- package/dist/describer/anthropic.d.ts +2 -2
- package/dist/describer/anthropic.js +5 -7
- package/dist/describer/describer.d.ts +2 -2
- package/dist/describer/describer.js +6 -8
- package/dist/describer/gemini.d.ts +2 -2
- package/dist/describer/gemini.js +5 -7
- package/dist/embedder/cohere.d.ts +1 -1
- package/dist/embedder/cohere.js +2 -2
- package/dist/embedder/factory.d.ts +3 -1
- package/dist/embedder/factory.js +8 -2
- package/dist/embedder/health.js +5 -1
- package/dist/embedder/http.d.ts +1 -1
- package/dist/embedder/http.js +74 -43
- package/dist/embedder/ollama.d.ts +1 -1
- package/dist/embedder/ollama.js +2 -2
- package/dist/embedder/openai.d.ts +1 -1
- package/dist/embedder/openai.js +2 -2
- package/dist/eval/compare-merge.d.ts +10 -0
- package/dist/eval/compare-merge.js +537 -0
- package/dist/eval/compare-rankings.d.ts +10 -0
- package/dist/eval/compare-rankings.js +245 -0
- package/dist/eval/dump-descriptions.d.ts +7 -0
- package/dist/eval/dump-descriptions.js +58 -0
- package/dist/eval/fast-index.d.ts +8 -0
- package/dist/eval/fast-index.js +283 -0
- package/dist/eval/run-branch-compare.d.ts +7 -0
- package/dist/eval/run-branch-compare.js +220 -0
- package/dist/eval/run-token-test.js +1 -0
- package/dist/eval/test-kw.d.ts +1 -0
- package/dist/eval/test-kw.js +22 -0
- package/dist/eval/update-descriptions.d.ts +7 -0
- package/dist/eval/update-descriptions.js +84 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/indexer/embed-stage.js +2 -1
- package/dist/indexer/git-diff.js +21 -9
- package/dist/indexer/pipeline.d.ts +6 -0
- package/dist/indexer/pipeline.js +304 -40
- package/dist/indexer/watch.js +1 -3
- package/dist/indexer/worker.d.ts +15 -2
- package/dist/indexer/worker.js +25 -12
- package/dist/mcp/handlers.d.ts +9 -0
- package/dist/mcp/handlers.js +23 -6
- package/dist/mcp/server.js +2 -0
- package/dist/opencode/create-read-tool.d.ts +2 -0
- package/dist/opencode/create-read-tool.js +8 -2
- package/dist/opencode/read-fallback.d.ts +1 -5
- package/dist/opencode/read-fallback.js +1 -18
- package/dist/opencode/read-format.js +5 -3
- package/dist/opencode/tools.js +5 -7
- package/dist/plugin.js +223 -71
- package/dist/retriever/keyword-index.d.ts +3 -2
- package/dist/retriever/keyword-index.js +25 -1
- package/dist/retriever/retriever.d.ts +4 -1
- package/dist/retriever/retriever.js +34 -56
- package/dist/tui.js +18 -1
- package/dist/vectorstore/lancedb.d.ts +35 -4
- package/dist/vectorstore/lancedb.js +146 -23
- package/dist/vectorstore/memory.d.ts +6 -1
- package/dist/vectorstore/memory.js +58 -0
- package/dist/watcher.d.ts +2 -0
- package/dist/watcher.js +5 -1
- package/dist/web/api.js +10 -2
- package/dist/web/server.js +18 -3
- package/package.json +8 -9
- package/scripts/postinstall-setup.js +82 -0
- package/dist/api.js.map +0 -1
- package/dist/chunker/base.js.map +0 -1
- package/dist/chunker/bash.js.map +0 -1
- package/dist/chunker/c.js.map +0 -1
- package/dist/chunker/cpp.js.map +0 -1
- package/dist/chunker/csharp.js.map +0 -1
- package/dist/chunker/css.js.map +0 -1
- package/dist/chunker/doc.js.map +0 -1
- package/dist/chunker/dockerfile.js.map +0 -1
- package/dist/chunker/docx.js.map +0 -1
- package/dist/chunker/excel.js.map +0 -1
- package/dist/chunker/factory.js.map +0 -1
- package/dist/chunker/fallback.js.map +0 -1
- package/dist/chunker/go.js.map +0 -1
- package/dist/chunker/grammar.js.map +0 -1
- package/dist/chunker/html.js.map +0 -1
- package/dist/chunker/image.js.map +0 -1
- package/dist/chunker/ini.js.map +0 -1
- package/dist/chunker/java.js.map +0 -1
- package/dist/chunker/javascript.js.map +0 -1
- package/dist/chunker/json.js.map +0 -1
- package/dist/chunker/kotlin.js.map +0 -1
- package/dist/chunker/loader.js.map +0 -1
- package/dist/chunker/markdown.js.map +0 -1
- package/dist/chunker/pdf.js.map +0 -1
- package/dist/chunker/php.js.map +0 -1
- package/dist/chunker/powershell.js.map +0 -1
- package/dist/chunker/python.js.map +0 -1
- package/dist/chunker/razor.js.map +0 -1
- package/dist/chunker/ruby.js.map +0 -1
- package/dist/chunker/rust.js.map +0 -1
- package/dist/chunker/sln.js.map +0 -1
- package/dist/chunker/sql.js.map +0 -1
- package/dist/chunker/ssl.js.map +0 -1
- package/dist/chunker/swift.js.map +0 -1
- package/dist/chunker/tex.js.map +0 -1
- package/dist/chunker/toml.js.map +0 -1
- package/dist/chunker/typescript.js.map +0 -1
- package/dist/chunker/uuid.js.map +0 -1
- package/dist/chunker/xml.js.map +0 -1
- package/dist/chunker/yaml.js.map +0 -1
- package/dist/cli/commands/clear.js.map +0 -1
- package/dist/cli/commands/describe-image.js.map +0 -1
- package/dist/cli/commands/dump.js.map +0 -1
- package/dist/cli/commands/eval.js.map +0 -1
- package/dist/cli/commands/index-command.js.map +0 -1
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/cli/commands/init-helpers.js.map +0 -1
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/list.js.map +0 -1
- package/dist/cli/commands/mcp.js.map +0 -1
- package/dist/cli/commands/query.js.map +0 -1
- package/dist/cli/commands/show.js.map +0 -1
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/commands/ui.js.map +0 -1
- package/dist/cli/commands/update.js.map +0 -1
- package/dist/cli/format.js.map +0 -1
- package/dist/cli/helpers.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/progress.d.ts +0 -42
- package/dist/cli/progress.js +0 -137
- package/dist/cli/progress.js.map +0 -1
- package/dist/cli/types.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/content/doc.js.map +0 -1
- package/dist/content/docx.js.map +0 -1
- package/dist/content/excel.js.map +0 -1
- package/dist/content/image.js.map +0 -1
- package/dist/content/pdf.js.map +0 -1
- package/dist/content/reader.js.map +0 -1
- package/dist/content/types.js.map +0 -1
- package/dist/core/bootstrap.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/doc-progress.js.map +0 -1
- package/dist/core/fileLogger.js.map +0 -1
- package/dist/core/interfaces.js.map +0 -1
- package/dist/core/manifest.js.map +0 -1
- package/dist/core/provider-defaults.js.map +0 -1
- package/dist/core/rag-injection-flag.js.map +0 -1
- package/dist/core/resolve-api-key.js.map +0 -1
- package/dist/core/runtime-overrides.js.map +0 -1
- package/dist/describer/anthropic.js.map +0 -1
- package/dist/describer/describer.js.map +0 -1
- package/dist/describer/factory.js.map +0 -1
- package/dist/describer/gemini.js.map +0 -1
- package/dist/describer/shared.js.map +0 -1
- package/dist/embedder/cohere.js.map +0 -1
- package/dist/embedder/factory.js.map +0 -1
- package/dist/embedder/health.js.map +0 -1
- package/dist/embedder/http.js.map +0 -1
- package/dist/embedder/ollama.js.map +0 -1
- package/dist/embedder/openai.js.map +0 -1
- package/dist/eval/index.js.map +0 -1
- package/dist/eval/run-token-test.js.map +0 -1
- package/dist/eval/session-logger.js.map +0 -1
- package/dist/eval/storage.js.map +0 -1
- package/dist/eval/token-analysis.js.map +0 -1
- package/dist/eval/token-counter.js.map +0 -1
- package/dist/eval/types.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/indexer/description-stage.js.map +0 -1
- package/dist/indexer/embed-stage.js.map +0 -1
- package/dist/indexer/git-diff.js.map +0 -1
- package/dist/indexer/metadata.js.map +0 -1
- package/dist/indexer/pipeline.js.map +0 -1
- package/dist/indexer/stats.js.map +0 -1
- package/dist/indexer/watch.js.map +0 -1
- package/dist/indexer/worker.js.map +0 -1
- package/dist/indexer.js.map +0 -1
- package/dist/mcp/cli.js.map +0 -1
- package/dist/mcp/handlers.js.map +0 -1
- package/dist/mcp/server.js.map +0 -1
- package/dist/opencode/create-read-tool.js.map +0 -1
- package/dist/opencode/read-fallback.js.map +0 -1
- package/dist/opencode/read-format.js.map +0 -1
- package/dist/opencode/read-query.js.map +0 -1
- package/dist/opencode/tool-args.js.map +0 -1
- package/dist/opencode/tools.js.map +0 -1
- package/dist/plugin-entry.js.map +0 -1
- package/dist/plugin.js.map +0 -1
- package/dist/retriever/context-optimizer.js.map +0 -1
- package/dist/retriever/keyword-index.js.map +0 -1
- package/dist/retriever/retriever.js.map +0 -1
- package/dist/tui.js.map +0 -1
- package/dist/updater.d.ts +0 -45
- package/dist/updater.js +0 -175
- package/dist/updater.js.map +0 -1
- package/dist/vectorstore/factory.js.map +0 -1
- package/dist/vectorstore/lancedb.js.map +0 -1
- package/dist/vectorstore/memory.js.map +0 -1
- package/dist/watcher.js.map +0 -1
- package/dist/web/api.js.map +0 -1
- package/dist/web/server.js.map +0 -1
- package/dist/web/static.js.map +0 -1
package/dist/content/reader.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import pLimit from "p-limit";
|
|
7
|
-
import { computeFileHash, normalizeFilePath } from "../core/manifest.js";
|
|
7
|
+
import { computeFileHash, computeDescriptionConfigHash, normalizeFilePath } from "../core/manifest.js";
|
|
8
|
+
import { DescriptionCache } from "../core/desc-cache.js";
|
|
8
9
|
import { createImageVisionProvider, } from "../chunker/image.js";
|
|
9
10
|
import * as pdfExtractor from "./pdf.js";
|
|
10
11
|
import * as docxExtractor from "./docx.js";
|
|
@@ -84,8 +85,11 @@ async function dispatchExtraction(filePath, buffer, imageVisionProvider, imagePr
|
|
|
84
85
|
* Scan the workspace directory for indexable files, reading content or dispatching
|
|
85
86
|
* binary extraction (PDF, DOCX, DOC, Excel, images). Respects the file manifest
|
|
86
87
|
* for incremental re-indexing by skipping unchanged files.
|
|
88
|
+
*
|
|
89
|
+
* @param descCache - Optional persistent description cache. When set, image
|
|
90
|
+
* descriptions are cached and reused across sessions (survives aborted runs).
|
|
87
91
|
*/
|
|
88
|
-
export async function scanWorkspaceFiles(cwd, config, logger, manifest, filterPaths) {
|
|
92
|
+
export async function scanWorkspaceFiles(cwd, config, logger, manifest, filterPaths, injectedVisionProvider, descCache) {
|
|
89
93
|
const extensions = new Set(config.indexing.includeExtensions);
|
|
90
94
|
let imageVisionProvider = null;
|
|
91
95
|
let imagePrompt;
|
|
@@ -95,7 +99,7 @@ export async function scanWorkspaceFiles(cwd, config, logger, manifest, filterPa
|
|
|
95
99
|
for (const ext of imageExtractor.SUPPORTED_IMAGE_EXTENSIONS) {
|
|
96
100
|
extensions.add(ext.toLowerCase());
|
|
97
101
|
}
|
|
98
|
-
imageVisionProvider = createImageVisionProvider(imageCfg);
|
|
102
|
+
imageVisionProvider = injectedVisionProvider ?? createImageVisionProvider(imageCfg);
|
|
99
103
|
imagePrompt = imageCfg.prompt;
|
|
100
104
|
imageResizeMaxDimension = imageCfg.resizeMaxDimension;
|
|
101
105
|
}
|
|
@@ -135,6 +139,10 @@ export async function scanWorkspaceFiles(cwd, config, logger, manifest, filterPa
|
|
|
135
139
|
const scanConcurrency = Math.min(config.indexing.concurrency * 2, 16);
|
|
136
140
|
const textLimit = pLimit(scanConcurrency);
|
|
137
141
|
const imageLimit = pLimit(1);
|
|
142
|
+
// Compute a hash of the image description config — used to build cache keys.
|
|
143
|
+
const imageDescConfigHash = config.imageDescription?.enabled
|
|
144
|
+
? computeDescriptionConfigHash(config) ?? ""
|
|
145
|
+
: "";
|
|
138
146
|
let completed = 0;
|
|
139
147
|
async function processFile(filePath) {
|
|
140
148
|
const normalizedPath = normalizeFilePath(filePath);
|
|
@@ -168,11 +176,48 @@ export async function scanWorkspaceFiles(cwd, config, logger, manifest, filterPa
|
|
|
168
176
|
excelExtractor.EXCEL_EXTENSIONS.has(filePath.toLowerCase()) ||
|
|
169
177
|
isImage;
|
|
170
178
|
logger?.info(`Reading: ${filePath}`);
|
|
179
|
+
const buffer = isBinary ? await fs.readFile(filePath) : Buffer.alloc(0);
|
|
180
|
+
// For images, check the persistent description cache before calling the vision provider
|
|
181
|
+
if (isImage && descCache && imageDescConfigHash) {
|
|
182
|
+
const imageBytesHash = computeFileHash(buffer.toString("base64"));
|
|
183
|
+
const cacheKey = DescriptionCache.imageKey(imageBytesHash, imageDescConfigHash);
|
|
184
|
+
const cachedDesc = descCache.get(cacheKey);
|
|
185
|
+
if (cachedDesc) {
|
|
186
|
+
logger?.info(` Using cached image description: ${filePath}`);
|
|
187
|
+
const content = cachedDesc;
|
|
188
|
+
let fileMtime;
|
|
189
|
+
let fileSize;
|
|
190
|
+
try {
|
|
191
|
+
const stat = await fs.stat(filePath);
|
|
192
|
+
fileMtime = stat.mtimeMs;
|
|
193
|
+
fileSize = stat.size;
|
|
194
|
+
}
|
|
195
|
+
catch { /* best-effort stat */ }
|
|
196
|
+
completed++;
|
|
197
|
+
return {
|
|
198
|
+
filePath,
|
|
199
|
+
normalizedPath,
|
|
200
|
+
content,
|
|
201
|
+
hash: computeFileHash(content),
|
|
202
|
+
isEmpty: false,
|
|
203
|
+
isTooSmall: false,
|
|
204
|
+
extractionStatus: "ok",
|
|
205
|
+
mtime: fileMtime,
|
|
206
|
+
size: fileSize,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
171
210
|
if (isImage) {
|
|
172
211
|
logger?.info(` Describing image: ${filePath}`);
|
|
173
212
|
}
|
|
174
|
-
const buffer = isBinary ? await fs.readFile(filePath) : Buffer.alloc(0);
|
|
175
213
|
const result = await dispatchExtraction(filePath, buffer, imageVisionProvider, imagePrompt, imageResizeMaxDimension);
|
|
214
|
+
// Cache the image description for future runs
|
|
215
|
+
if (isImage && result.ok && descCache && imageDescConfigHash) {
|
|
216
|
+
const imageBytesHash = computeFileHash(buffer.toString("base64"));
|
|
217
|
+
const cacheKey = DescriptionCache.imageKey(imageBytesHash, imageDescConfigHash);
|
|
218
|
+
descCache.set(cacheKey, result.content);
|
|
219
|
+
await descCache.save();
|
|
220
|
+
}
|
|
176
221
|
if (!result.ok) {
|
|
177
222
|
logger?.warn(` ${filePath} (extraction failed: ${result.error})`);
|
|
178
223
|
}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -114,6 +114,13 @@ export interface DocumentationModeConfig {
|
|
|
114
114
|
/** System prompt for the documentation agent. */
|
|
115
115
|
systemPrompt: string;
|
|
116
116
|
}
|
|
117
|
+
/** Configuration for the wiki mode that instructs the AI agent to maintain a persistent knowledge wiki. */
|
|
118
|
+
export interface WikiModeConfig {
|
|
119
|
+
/** Whether wiki mode is enabled. */
|
|
120
|
+
enabled: boolean;
|
|
121
|
+
/** System prompt for the wiki maintainer agent. */
|
|
122
|
+
systemPrompt: string;
|
|
123
|
+
}
|
|
117
124
|
/** Configuration for the terminal UI (TUI) keybindings. */
|
|
118
125
|
export interface TuiConfig {
|
|
119
126
|
/** Keybinding to toggle the file list panel. */
|
|
@@ -128,7 +135,7 @@ export interface McpConfig {
|
|
|
128
135
|
}
|
|
129
136
|
/** Configuration for automatic self-updates. */
|
|
130
137
|
export interface AutoUpdateConfig {
|
|
131
|
-
/** Whether
|
|
138
|
+
/** Whether update checking is enabled (defaults to true). */
|
|
132
139
|
enabled: boolean;
|
|
133
140
|
}
|
|
134
141
|
/** Configuration for post-retrieval context window optimization (adjacent merge, similarity dedup, file diversity cap). */
|
|
@@ -164,6 +171,8 @@ export interface RagConfig {
|
|
|
164
171
|
documentPrefix?: string;
|
|
165
172
|
/** Prefix prepended to queries before embedding (e.g. "search_query:"). */
|
|
166
173
|
queryPrefix?: string;
|
|
174
|
+
/** Cached embedding vector dimension. Probed once on first startup, then persisted to config. */
|
|
175
|
+
vectorDimension?: number;
|
|
167
176
|
};
|
|
168
177
|
/** Indexing pipeline controls: what to index, concurrency, batch sizes. */
|
|
169
178
|
indexing: {
|
|
@@ -187,6 +196,12 @@ export interface RagConfig {
|
|
|
187
196
|
ollamaMaxBatchSize?: number;
|
|
188
197
|
/** Maximum concurrent description generation requests. */
|
|
189
198
|
descriptionConcurrency?: number;
|
|
199
|
+
/**
|
|
200
|
+
* Maximum file size in bytes for SVG/XML files before chunking is skipped.
|
|
201
|
+
* Large SVGs can cause tree-sitter to hang. Set to 0 for no limit.
|
|
202
|
+
* @default 1_048_576 (1 MB)
|
|
203
|
+
*/
|
|
204
|
+
maxSvgSizeBytes?: number;
|
|
190
205
|
};
|
|
191
206
|
/** Vector storage backend configuration. */
|
|
192
207
|
vectorStore: {
|
|
@@ -227,6 +242,8 @@ export interface RagConfig {
|
|
|
227
242
|
readNoResultsBehavior?: ReadNoResultsBehavior;
|
|
228
243
|
/** Maximum related files shown when read results are empty. */
|
|
229
244
|
readRelatedFilesMax?: number;
|
|
245
|
+
/** Whether to inject RAG tool guidance into system prompts (default true). */
|
|
246
|
+
injectSystemPrompt?: boolean;
|
|
230
247
|
};
|
|
231
248
|
/** Custom chunker module registrations. */
|
|
232
249
|
chunkers?: ChunkerConfig[];
|
|
@@ -241,6 +258,8 @@ export interface RagConfig {
|
|
|
241
258
|
imageDescription?: ImageDescriptionConfig;
|
|
242
259
|
/** Automated documentation mode config. */
|
|
243
260
|
documentationMode?: DocumentationModeConfig;
|
|
261
|
+
/** Wiki mode config that instructs the AI agent to maintain a persistent knowledge wiki. */
|
|
262
|
+
wikiMode?: WikiModeConfig;
|
|
244
263
|
/** MCP server integration config. */
|
|
245
264
|
mcp?: McpConfig;
|
|
246
265
|
/** Auto-update checking config. */
|
|
@@ -293,3 +312,12 @@ export declare const CONFIG_FILE_CANDIDATES: string[];
|
|
|
293
312
|
export declare function findConfigFile(directory: string): string | undefined;
|
|
294
313
|
/** Load and parse a JSON config file, deep-merge with defaults, optionally validate. */
|
|
295
314
|
export declare function loadConfig(filePath: string, validate?: boolean): RagConfig;
|
|
315
|
+
/**
|
|
316
|
+
* Persist a probed embedding vector dimension into the config JSON file.
|
|
317
|
+
* Sets `embedding.vectorDimension` so subsequent startups skip the probe.
|
|
318
|
+
* This is a best-effort operation — failures are silently ignored.
|
|
319
|
+
*
|
|
320
|
+
* @param configPath - Absolute path to the config JSON file.
|
|
321
|
+
* @param dimension - The vector dimension to persist.
|
|
322
|
+
*/
|
|
323
|
+
export declare function persistProbedDimension(configPath: string, dimension: number): void;
|
package/dist/core/config.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview Configuration types and defaults for OpenCodeRAG. Defines RagConfig,
|
|
3
3
|
* all sub-config interfaces, DEFAULT_CONFIG, validateConfig, and loadConfig.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { env } from "node:process";
|
|
8
8
|
export const DEFAULT_CONFIG = {
|
|
@@ -10,7 +10,7 @@ export const DEFAULT_CONFIG = {
|
|
|
10
10
|
provider: "ollama",
|
|
11
11
|
baseUrl: "http://127.0.0.1:11434/api",
|
|
12
12
|
model: "qwen3-embedding:0.6b",
|
|
13
|
-
timeoutMs:
|
|
13
|
+
timeoutMs: 120000,
|
|
14
14
|
},
|
|
15
15
|
indexing: {
|
|
16
16
|
includeExtensions: [
|
|
@@ -104,8 +104,9 @@ export const DEFAULT_CONFIG = {
|
|
|
104
104
|
concurrency: 8,
|
|
105
105
|
embedBatchSize: 100,
|
|
106
106
|
embedConcurrency: 6,
|
|
107
|
-
ollamaMaxBatchSize:
|
|
107
|
+
ollamaMaxBatchSize: 500,
|
|
108
108
|
descriptionConcurrency: 4,
|
|
109
|
+
maxSvgSizeBytes: 1_048_576,
|
|
109
110
|
},
|
|
110
111
|
vectorStore: {
|
|
111
112
|
path: "./.opencode/rag_db",
|
|
@@ -113,7 +114,7 @@ export const DEFAULT_CONFIG = {
|
|
|
113
114
|
},
|
|
114
115
|
retrieval: {
|
|
115
116
|
topK: 20,
|
|
116
|
-
minScore: 0.
|
|
117
|
+
minScore: 0.35,
|
|
117
118
|
hybridSearch: {
|
|
118
119
|
enabled: true,
|
|
119
120
|
keywordWeight: 0.4,
|
|
@@ -130,6 +131,7 @@ export const DEFAULT_CONFIG = {
|
|
|
130
131
|
enabled: true,
|
|
131
132
|
maxContextChunks: 10,
|
|
132
133
|
readOverride: true,
|
|
134
|
+
injectSystemPrompt: true,
|
|
133
135
|
autoIndex: {
|
|
134
136
|
enabled: false,
|
|
135
137
|
debounceMs: 2000,
|
|
@@ -143,7 +145,7 @@ export const DEFAULT_CONFIG = {
|
|
|
143
145
|
model: "minicpm-v4.6:latest",
|
|
144
146
|
baseUrl: "http://127.0.0.1:11434/api",
|
|
145
147
|
timeoutMs: 60000,
|
|
146
|
-
prompt: "Describe this image
|
|
148
|
+
prompt: "Describe this image in 1-2 sentences: what it shows, its purpose, and key visual elements.",
|
|
147
149
|
think: false,
|
|
148
150
|
numCtx: 2048,
|
|
149
151
|
resizeMaxDimension: 1024,
|
|
@@ -156,7 +158,7 @@ export const DEFAULT_CONFIG = {
|
|
|
156
158
|
think: false,
|
|
157
159
|
numCtx: 4096,
|
|
158
160
|
timeoutMs: 60000,
|
|
159
|
-
systemPrompt: "Describe this code in
|
|
161
|
+
systemPrompt: "Describe this code in 2-3 sentences: purpose, key concepts, inputs/outputs, and dependencies. No code repetition.",
|
|
160
162
|
batchConcurrency: 1,
|
|
161
163
|
retryMax: 3,
|
|
162
164
|
retryBaseDelayMs: 1000,
|
|
@@ -181,11 +183,72 @@ export const DEFAULT_CONFIG = {
|
|
|
181
183
|
"## Output format\n\n" +
|
|
182
184
|
"Return your changes as a list of file paths with the full new content of the comment block for each modified symbol. Do NOT output the entire file unless asked.",
|
|
183
185
|
},
|
|
186
|
+
wikiMode: {
|
|
187
|
+
enabled: false,
|
|
188
|
+
systemPrompt: "You are a wiki maintainer for this codebase. You incrementally build and maintain " +
|
|
189
|
+
"a persistent knowledge wiki at `.opencode/wiki/` — a structured, interlinked collection " +
|
|
190
|
+
"of markdown files that synthesizes knowledge from the codebase, documentation, and your conversations.\n\n" +
|
|
191
|
+
"## Three layers\n\n" +
|
|
192
|
+
"- **Raw sources**: code, docs, READMEs (immutable — read with `search_semantic`, `get_file_skeleton`, `find_usages`, `read`)\n" +
|
|
193
|
+
"- **Wiki**: `.opencode/wiki/*.md` (you own this layer — create, update, and maintain ALL pages)\n" +
|
|
194
|
+
"- **Schema**: this prompt (the protocol you follow)\n\n" +
|
|
195
|
+
"## Wiki layout\n\n" +
|
|
196
|
+
"`.opencode/wiki/`\n" +
|
|
197
|
+
"- `index.md` — content catalog: every page listed with a link and one-line summary\n" +
|
|
198
|
+
"- `log.md` — append-only timeline: `## [date] op | title`\n" +
|
|
199
|
+
"- `entities/` — pages for modules, services, key classes, components\n" +
|
|
200
|
+
"- `concepts/` — pages for patterns, conventions, architectural decisions, gotchas\n" +
|
|
201
|
+
"- `sources/` — summaries of external sources (articles, specs, discussions)\n\n" +
|
|
202
|
+
"## Page frontmatter\n\n" +
|
|
203
|
+
"Every page should start with:\n" +
|
|
204
|
+
"```\n" +
|
|
205
|
+
"---\n" +
|
|
206
|
+
"title: <page title>\n" +
|
|
207
|
+
"tags: [relevant tags]\n" +
|
|
208
|
+
"sourceRefs: [file paths or URLs this page synthesizes]\n" +
|
|
209
|
+
"lastReviewed: <date>\n" +
|
|
210
|
+
"---\n" +
|
|
211
|
+
"```\n\n" +
|
|
212
|
+
"## Operations\n\n" +
|
|
213
|
+
"### Ingest (when you learn something new)\n" +
|
|
214
|
+
"1. Read the source (code, doc, external article)\n" +
|
|
215
|
+
"2. Extract key information — what it does, why it exists, how it connects to existing knowledge\n" +
|
|
216
|
+
"3. Create or update relevant entity/concept pages\n" +
|
|
217
|
+
"4. Add cross-references with `[[wiki/page-name]]` links between related pages\n" +
|
|
218
|
+
"5. Update `index.md` with any new pages\n" +
|
|
219
|
+
"6. Append to `log.md`: `## [date] ingest | page title`\n\n" +
|
|
220
|
+
"### Query (when the user asks a question)\n" +
|
|
221
|
+
"1. Read `index.md` first to find relevant wiki pages\n" +
|
|
222
|
+
"2. Read those pages with the `read` tool\n" +
|
|
223
|
+
"3. If the wiki is insufficient, use `search_semantic` for code-level retrieval\n" +
|
|
224
|
+
"4. Synthesize an answer citing wiki pages where possible\n" +
|
|
225
|
+
"5. If the answer produced valuable analysis, file it back as a new wiki page\n\n" +
|
|
226
|
+
"### Lint (periodically, or when `/wiki lint` is invoked)\n" +
|
|
227
|
+
"Check for:\n" +
|
|
228
|
+
"- Orphan pages with no inbound `[[links]]`\n" +
|
|
229
|
+
"- Stale pages whose `sourceRefs` files changed since `lastReviewed`\n" +
|
|
230
|
+
"- Contradictions between pages\n" +
|
|
231
|
+
"- Important concepts mentioned but lacking their own page\n" +
|
|
232
|
+
"Append findings to `log.md`.\n\n" +
|
|
233
|
+
"### Seed (when `/wiki seed` is invoked)\n" +
|
|
234
|
+
"Generate initial wiki from existing sources:\n" +
|
|
235
|
+
"1. Read README and top-level docs for project overview → create overview page\n" +
|
|
236
|
+
"2. Scan file structure with `get_file_skeleton` for major modules → create entity pages\n" +
|
|
237
|
+
"3. Identify common conventions from code patterns → create concept pages\n" +
|
|
238
|
+
"4. Create `index.md` listing all pages\n" +
|
|
239
|
+
"5. Create `log.md` with initial entry\n" +
|
|
240
|
+
"6. Keep pages focused — one concept per page, short prose\n\n" +
|
|
241
|
+
"## Principles\n\n" +
|
|
242
|
+
"- You write and maintain ALL wiki pages — never ask the user to do it\n" +
|
|
243
|
+
"- Good answers and analyses get filed back as new pages — explorations compound\n" +
|
|
244
|
+
"- One concept per page — short, focused, cross-referenced\n" +
|
|
245
|
+
"- The wiki is git-trackable in `.opencode/` — every edit is a diff",
|
|
246
|
+
},
|
|
184
247
|
mcp: {
|
|
185
248
|
enabled: true,
|
|
186
249
|
},
|
|
187
250
|
autoUpdate: {
|
|
188
|
-
enabled:
|
|
251
|
+
enabled: true,
|
|
189
252
|
},
|
|
190
253
|
ui: {
|
|
191
254
|
port: 3210,
|
|
@@ -213,7 +276,7 @@ export function validateConfig(config) {
|
|
|
213
276
|
const KNOWN_TOP_KEYS = new Set([
|
|
214
277
|
"embedding", "indexing", "vectorStore", "retrieval",
|
|
215
278
|
"openCode", "chunkers", "chunking", "description",
|
|
216
|
-
"imageDescription", "documentationMode", "mcp", "autoUpdate", "ui", "tui", "logging",
|
|
279
|
+
"imageDescription", "documentationMode", "wikiMode", "mcp", "autoUpdate", "ui", "tui", "logging",
|
|
217
280
|
]);
|
|
218
281
|
const topKeys = new Set(Object.keys(config));
|
|
219
282
|
for (const key of topKeys) {
|
|
@@ -331,6 +394,8 @@ export function loadConfig(filePath, validate = true) {
|
|
|
331
394
|
let parsed;
|
|
332
395
|
try {
|
|
333
396
|
raw = readFileSync(filePath, "utf-8");
|
|
397
|
+
if (raw.charCodeAt(0) === 0xfeff)
|
|
398
|
+
raw = raw.slice(1);
|
|
334
399
|
parsed = JSON.parse(raw);
|
|
335
400
|
}
|
|
336
401
|
catch (err) {
|
|
@@ -397,6 +462,10 @@ export function loadConfig(filePath, validate = true) {
|
|
|
397
462
|
...DEFAULT_CONFIG.documentationMode,
|
|
398
463
|
...(safeObj(parsed.documentationMode) ?? {}),
|
|
399
464
|
},
|
|
465
|
+
wikiMode: {
|
|
466
|
+
...DEFAULT_CONFIG.wikiMode,
|
|
467
|
+
...(safeObj(parsed.wikiMode) ?? {}),
|
|
468
|
+
},
|
|
400
469
|
mcp: {
|
|
401
470
|
...DEFAULT_CONFIG.mcp,
|
|
402
471
|
...(safeObj(parsed.mcp) ?? {}),
|
|
@@ -426,4 +495,32 @@ export function loadConfig(filePath, validate = true) {
|
|
|
426
495
|
}
|
|
427
496
|
return cfg;
|
|
428
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Persist a probed embedding vector dimension into the config JSON file.
|
|
500
|
+
* Sets `embedding.vectorDimension` so subsequent startups skip the probe.
|
|
501
|
+
* This is a best-effort operation — failures are silently ignored.
|
|
502
|
+
*
|
|
503
|
+
* @param configPath - Absolute path to the config JSON file.
|
|
504
|
+
* @param dimension - The vector dimension to persist.
|
|
505
|
+
*/
|
|
506
|
+
export function persistProbedDimension(configPath, dimension) {
|
|
507
|
+
let raw;
|
|
508
|
+
try {
|
|
509
|
+
raw = readFileSync(configPath, "utf-8");
|
|
510
|
+
}
|
|
511
|
+
catch {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const obj = JSON.parse(raw);
|
|
516
|
+
const embedding = (obj.embedding ??= {});
|
|
517
|
+
if (embedding.vectorDimension === dimension)
|
|
518
|
+
return; // already set
|
|
519
|
+
embedding.vectorDimension = dimension;
|
|
520
|
+
writeFileSync(configPath, JSON.stringify(obj, null, 2), "utf-8");
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
// best-effort
|
|
524
|
+
}
|
|
525
|
+
}
|
|
429
526
|
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Persistent description cache keyed by content hash + description config hash.
|
|
3
|
+
* Survives aborted index runs so that descriptions are not regenerated unnecessarily.
|
|
4
|
+
* Two namespaces: "code" (per-chunk descriptions) and "image" (per-file image descriptions).
|
|
5
|
+
*/
|
|
6
|
+
export declare class DescriptionCache {
|
|
7
|
+
private cache;
|
|
8
|
+
private cacheDir;
|
|
9
|
+
private dirty;
|
|
10
|
+
private savePromise;
|
|
11
|
+
private loadPromise;
|
|
12
|
+
constructor(cacheDir: string);
|
|
13
|
+
/** Load the cache from disk. */
|
|
14
|
+
load(): Promise<void>;
|
|
15
|
+
/** Get a cached description for the given key. */
|
|
16
|
+
get(key: string): string | undefined;
|
|
17
|
+
/** Set a cached description for the given key. */
|
|
18
|
+
set(key: string, description: string): void;
|
|
19
|
+
/** Set multiple entries at once. */
|
|
20
|
+
setMany(entries: Array<[string, string]>): void;
|
|
21
|
+
/** Check if a key exists in the cache. */
|
|
22
|
+
has(key: string): boolean;
|
|
23
|
+
/** Persist the cache to disk if dirty. Safe to call multiple times. */
|
|
24
|
+
save(): Promise<void>;
|
|
25
|
+
/** Build a cache key for a code chunk. */
|
|
26
|
+
static codeKey(content: string, descConfigHash: string): string;
|
|
27
|
+
/** Build a cache key for an image file. */
|
|
28
|
+
static imageKey(imageBytesHash: string, imageDescConfigHash: string): string;
|
|
29
|
+
/** Remove old entries to stay under the limit. */
|
|
30
|
+
private maybeEvict;
|
|
31
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Persistent description cache keyed by content hash + description config hash.
|
|
3
|
+
* Survives aborted index runs so that descriptions are not regenerated unnecessarily.
|
|
4
|
+
* Two namespaces: "code" (per-chunk descriptions) and "image" (per-file image descriptions).
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
const CACHE_VERSION = 1;
|
|
10
|
+
const MAX_ENTRIES = 50_000;
|
|
11
|
+
const MAX_AGE_DAYS = 30;
|
|
12
|
+
export class DescriptionCache {
|
|
13
|
+
cache = { version: CACHE_VERSION, entries: {} };
|
|
14
|
+
cacheDir;
|
|
15
|
+
dirty = false;
|
|
16
|
+
savePromise = Promise.resolve();
|
|
17
|
+
loadPromise = null;
|
|
18
|
+
constructor(cacheDir) {
|
|
19
|
+
this.cacheDir = cacheDir;
|
|
20
|
+
}
|
|
21
|
+
/** Load the cache from disk. */
|
|
22
|
+
async load() {
|
|
23
|
+
if (this.loadPromise)
|
|
24
|
+
return this.loadPromise;
|
|
25
|
+
this.loadPromise = (async () => {
|
|
26
|
+
const cachePath = path.join(this.cacheDir, ".desc-cache.json");
|
|
27
|
+
try {
|
|
28
|
+
const raw = await fs.readFile(cachePath, "utf-8");
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
if (parsed.version === CACHE_VERSION && parsed.entries) {
|
|
31
|
+
this.cache = parsed;
|
|
32
|
+
this.dirty = false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Cache file missing or corrupt — start fresh
|
|
37
|
+
}
|
|
38
|
+
})();
|
|
39
|
+
return this.loadPromise;
|
|
40
|
+
}
|
|
41
|
+
/** Get a cached description for the given key. */
|
|
42
|
+
get(key) {
|
|
43
|
+
return this.cache.entries[key]?.description;
|
|
44
|
+
}
|
|
45
|
+
/** Set a cached description for the given key. */
|
|
46
|
+
set(key, description) {
|
|
47
|
+
this.cache.entries[key] = {
|
|
48
|
+
description,
|
|
49
|
+
createdAt: Date.now(),
|
|
50
|
+
};
|
|
51
|
+
this.dirty = true;
|
|
52
|
+
this.maybeEvict();
|
|
53
|
+
}
|
|
54
|
+
/** Set multiple entries at once. */
|
|
55
|
+
setMany(entries) {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
for (const [key, description] of entries) {
|
|
58
|
+
this.cache.entries[key] = { description, createdAt: now };
|
|
59
|
+
}
|
|
60
|
+
this.dirty = true;
|
|
61
|
+
this.maybeEvict();
|
|
62
|
+
}
|
|
63
|
+
/** Check if a key exists in the cache. */
|
|
64
|
+
has(key) {
|
|
65
|
+
return key in this.cache.entries;
|
|
66
|
+
}
|
|
67
|
+
/** Persist the cache to disk if dirty. Safe to call multiple times. */
|
|
68
|
+
async save() {
|
|
69
|
+
if (!this.dirty)
|
|
70
|
+
return;
|
|
71
|
+
this.savePromise = this.savePromise.then(async () => {
|
|
72
|
+
const cachePath = path.join(this.cacheDir, ".desc-cache.json");
|
|
73
|
+
const tmpPath = cachePath + ".tmp";
|
|
74
|
+
try {
|
|
75
|
+
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
76
|
+
await fs.writeFile(tmpPath, JSON.stringify(this.cache), "utf-8");
|
|
77
|
+
try {
|
|
78
|
+
await fs.rename(tmpPath, cachePath);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
await fs.unlink(cachePath).catch(() => { });
|
|
82
|
+
await fs.rename(tmpPath, cachePath);
|
|
83
|
+
}
|
|
84
|
+
this.dirty = false;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Best-effort save
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return this.savePromise;
|
|
91
|
+
}
|
|
92
|
+
/** Build a cache key for a code chunk. */
|
|
93
|
+
static codeKey(content, descConfigHash) {
|
|
94
|
+
const contentHash = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
95
|
+
return contentHash + "_" + descConfigHash.slice(0, 16);
|
|
96
|
+
}
|
|
97
|
+
/** Build a cache key for an image file. */
|
|
98
|
+
static imageKey(imageBytesHash, imageDescConfigHash) {
|
|
99
|
+
return imageBytesHash.slice(0, 16) + "_" + imageDescConfigHash.slice(0, 16);
|
|
100
|
+
}
|
|
101
|
+
/** Remove old entries to stay under the limit. */
|
|
102
|
+
maybeEvict() {
|
|
103
|
+
const keys = Object.keys(this.cache.entries);
|
|
104
|
+
if (keys.length <= MAX_ENTRIES)
|
|
105
|
+
return;
|
|
106
|
+
const cutoff = Date.now() - MAX_AGE_DAYS * 24 * 60 * 60 * 1000;
|
|
107
|
+
const entries = this.cache.entries;
|
|
108
|
+
for (const key of keys) {
|
|
109
|
+
if (entries[key].createdAt < cutoff) {
|
|
110
|
+
delete entries[key];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// If still over limit, delete oldest
|
|
114
|
+
if (Object.keys(entries).length > MAX_ENTRIES) {
|
|
115
|
+
const sorted = Object.entries(entries)
|
|
116
|
+
.sort(([, a], [, b]) => a.createdAt - b.createdAt);
|
|
117
|
+
const toDelete = Object.keys(entries).length - MAX_ENTRIES;
|
|
118
|
+
for (let i = 0; i < toDelete; i++) {
|
|
119
|
+
delete entries[sorted[i][0]];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=desc-cache.js.map
|
|
@@ -22,12 +22,26 @@ export interface DescriptionLogger {
|
|
|
22
22
|
warn(message: string): void;
|
|
23
23
|
debug(message: string): void;
|
|
24
24
|
}
|
|
25
|
+
/** Progress callback invoked after each chunk is described during batch generation. */
|
|
26
|
+
export type ChunkProgressFn = (chunk: Chunk, completed: number, total: number) => void;
|
|
27
|
+
/** Optional progress reporting for batch description generation. */
|
|
28
|
+
export interface BatchDescriptionOptions {
|
|
29
|
+
/** Total number of chunks across the whole run (may exceed `chunks.length` when called per sub-batch). */
|
|
30
|
+
total?: number;
|
|
31
|
+
/** Called once per completed chunk with the chunk, the running completed count, and `total`. */
|
|
32
|
+
onProgress?: ChunkProgressFn;
|
|
33
|
+
}
|
|
25
34
|
/** Generates natural-language descriptions for code chunks using an LLM. */
|
|
26
35
|
export interface DescriptionProvider {
|
|
27
36
|
/** Generate a description for a single chunk. */
|
|
28
37
|
generateDescription(chunk: Chunk): Promise<string>;
|
|
29
|
-
/**
|
|
30
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Generate descriptions for multiple chunks concurrently. Returns a Map of chunk ID to description.
|
|
40
|
+
* @param chunks - Chunks to describe.
|
|
41
|
+
* @param logger - Optional logger for diagnostic messages.
|
|
42
|
+
* @param opts - Optional progress reporting options.
|
|
43
|
+
*/
|
|
44
|
+
generateBatchDescriptions(chunks: Chunk[], logger?: DescriptionLogger, opts?: BatchDescriptionOptions): Promise<Map<string, string>>;
|
|
31
45
|
}
|
|
32
46
|
/** Explains how a search result score was computed, including vector and keyword contributions. */
|
|
33
47
|
export interface SearchExplanation {
|
|
@@ -43,6 +57,10 @@ export interface SearchExplanation {
|
|
|
43
57
|
rawKeywordScore: number;
|
|
44
58
|
/** Weight applied to keyword score during fusion (0-1). */
|
|
45
59
|
keywordWeight: number;
|
|
60
|
+
/** Rank (0-indexed) in the vector store results. */
|
|
61
|
+
vectorRank?: number;
|
|
62
|
+
/** Rank (0-indexed) in the keyword index results. */
|
|
63
|
+
keywordRank?: number;
|
|
46
64
|
};
|
|
47
65
|
/** Query terms that matched in the keyword index, if hybrid search was used. */
|
|
48
66
|
matchedTerms?: string[];
|
|
@@ -86,12 +104,14 @@ export interface EmbeddingProvider {
|
|
|
86
104
|
}
|
|
87
105
|
/** In-memory TF-IDF inverted index for keyword-based search alongside vector search. */
|
|
88
106
|
export interface KeywordIndex {
|
|
107
|
+
/** Release resources and free memory. */
|
|
108
|
+
close(): void;
|
|
89
109
|
/** Index a set of chunks by tokenizing their content. */
|
|
90
110
|
addChunks(chunks: Chunk[]): void;
|
|
91
111
|
/** Remove all entries for a given file path from the index. */
|
|
92
112
|
removeByFilePath(filePath: string): void;
|
|
93
113
|
/** Search the index for the top-K matching chunks. */
|
|
94
|
-
search(query: string, topK: number): SearchResult[];
|
|
114
|
+
search(query: string, topK: number, filter?: MetadataFilter): SearchResult[];
|
|
95
115
|
/** Get the terms from a query that matched a specific chunk. */
|
|
96
116
|
getMatchedTerms(query: string, chunkId: string): string[];
|
|
97
117
|
/** Clear all indexed data. */
|
|
@@ -101,20 +121,55 @@ export interface KeywordIndex {
|
|
|
101
121
|
/** Persist the index to disk as JSON. */
|
|
102
122
|
save(filePath?: string): Promise<void>;
|
|
103
123
|
}
|
|
124
|
+
/** A paginated chunk summary for dump/list operations. */
|
|
125
|
+
export interface ChunkSummary {
|
|
126
|
+
id: string;
|
|
127
|
+
filePath: string;
|
|
128
|
+
language: string;
|
|
129
|
+
startLine: number;
|
|
130
|
+
endLine: number;
|
|
131
|
+
content: string;
|
|
132
|
+
description: string;
|
|
133
|
+
}
|
|
134
|
+
/** A file summary for list operations. */
|
|
135
|
+
export interface FileSummary {
|
|
136
|
+
filePath: string;
|
|
137
|
+
language: string;
|
|
138
|
+
chunkCount: number;
|
|
139
|
+
}
|
|
104
140
|
/** Persistent vector storage and retrieval backend (LanceDB or in-memory). */
|
|
105
141
|
export interface VectorStore {
|
|
106
142
|
/** Store a batch of chunks with their embeddings. */
|
|
107
143
|
addChunks(chunks: Chunk[]): Promise<void>;
|
|
108
144
|
/** Search for the top-K nearest neighbor chunks by embedding similarity. */
|
|
109
145
|
search(embedding: number[], topK: number): Promise<SearchResult[]>;
|
|
146
|
+
/** Search with optional metadata filtering. */
|
|
147
|
+
searchWithFilter(embedding: number[], topK: number, filter?: MetadataFilter): Promise<SearchResult[]>;
|
|
110
148
|
/** Return the total number of stored chunks. */
|
|
111
149
|
count(): Promise<number>;
|
|
112
150
|
/** Remove all stored data. */
|
|
113
151
|
clear(): Promise<void>;
|
|
114
152
|
/** Delete all chunks associated with a specific file path. */
|
|
115
153
|
deleteByFilePath(filePath: string): Promise<void>;
|
|
154
|
+
/** Return all unique file paths currently stored in the index. */
|
|
155
|
+
getFilePaths(): Promise<string[]>;
|
|
116
156
|
/** Release any held resources and close the store. */
|
|
117
157
|
close(): Promise<void>;
|
|
158
|
+
/** Retrieve a paginated list of chunks without embeddings. */
|
|
159
|
+
getChunks(offset: number, limit: number): Promise<ChunkSummary[]>;
|
|
160
|
+
/** List all distinct file paths with language and chunk count. */
|
|
161
|
+
listFiles(): Promise<FileSummary[]>;
|
|
162
|
+
/** Retrieve all chunks for a specific file path, sorted by start line. */
|
|
163
|
+
getChunksByFilePath(filePath: string): Promise<Chunk[]>;
|
|
164
|
+
/** Re-open the store, optionally pointing at a new database path. */
|
|
165
|
+
reopen?(newPath?: string): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
/** Filter criteria for narrowing search results by file path patterns or language. */
|
|
168
|
+
export interface MetadataFilter {
|
|
169
|
+
/** Glob-style path patterns (e.g. "src/**", "lib/auth/*"). */
|
|
170
|
+
pathPatterns?: string[];
|
|
171
|
+
/** Language identifiers (e.g. ["typescript", "tsx"]). */
|
|
172
|
+
languages?: string[];
|
|
118
173
|
}
|
|
119
174
|
/** Callback interface for reporting indexing progress to the UI or CLI. */
|
|
120
175
|
export interface IndexProgress {
|