opencode-rag-plugin 1.15.0 → 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.
- package/ReadMe.md +6 -7
- 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 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/init-helpers.d.ts +12 -0
- package/dist/cli/commands/init-helpers.js +84 -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/index.js +2 -2
- 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 +19 -0
- package/dist/core/config.js +34 -4
- 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 +9 -7
- package/dist/core/setup-runtime.d.ts +23 -0
- package/dist/core/setup-runtime.js +183 -0
- package/dist/core/version-check.d.ts +9 -0
- package/dist/core/version-check.js +49 -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/factory.d.ts +3 -1
- package/dist/embedder/factory.js +7 -1
- package/dist/embedder/health.js +4 -0
- package/dist/embedder/http.d.ts +1 -1
- package/dist/embedder/http.js +74 -43
- 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 +1 -0
- package/dist/index.js +1 -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 +290 -37
- 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 +196 -81
- 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/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.js +3 -0
- 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.d.ts +0 -17
- package/dist/cli/commands/update.js +0 -79
- 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/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 = {
|
|
@@ -106,6 +106,7 @@ export const DEFAULT_CONFIG = {
|
|
|
106
106
|
embedConcurrency: 6,
|
|
107
107
|
ollamaMaxBatchSize: 4000,
|
|
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,
|
|
@@ -426,4 +428,32 @@ export function loadConfig(filePath, validate = true) {
|
|
|
426
428
|
}
|
|
427
429
|
return cfg;
|
|
428
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Persist a probed embedding vector dimension into the config JSON file.
|
|
433
|
+
* Sets `embedding.vectorDimension` so subsequent startups skip the probe.
|
|
434
|
+
* This is a best-effort operation — failures are silently ignored.
|
|
435
|
+
*
|
|
436
|
+
* @param configPath - Absolute path to the config JSON file.
|
|
437
|
+
* @param dimension - The vector dimension to persist.
|
|
438
|
+
*/
|
|
439
|
+
export function persistProbedDimension(configPath, dimension) {
|
|
440
|
+
let raw;
|
|
441
|
+
try {
|
|
442
|
+
raw = readFileSync(configPath, "utf-8");
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const obj = JSON.parse(raw);
|
|
449
|
+
const embedding = (obj.embedding ??= {});
|
|
450
|
+
if (embedding.vectorDimension === dimension)
|
|
451
|
+
return; // already set
|
|
452
|
+
embedding.vectorDimension = dimension;
|
|
453
|
+
writeFileSync(configPath, JSON.stringify(obj, null, 2), "utf-8");
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// best-effort
|
|
457
|
+
}
|
|
458
|
+
}
|
|
429
459
|
//# 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 {
|
package/dist/core/manifest.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @fileoverview File hash manifest for tracking indexing state across sessions.
|
|
3
3
|
* Provides load/save with atomic writes, schema versioning, and corruption detection.
|
|
4
4
|
*/
|
|
5
|
+
import type { RagConfig } from "./config.js";
|
|
5
6
|
/** Metadata entry for a single indexed file. */
|
|
6
7
|
export interface ManifestEntry {
|
|
7
8
|
/** SHA-256 hash of the file content. */
|
|
@@ -16,9 +17,22 @@ export interface ManifestEntry {
|
|
|
16
17
|
size?: number;
|
|
17
18
|
/** Whether the description generation step failed for this file. */
|
|
18
19
|
descriptionFailed?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Hash of the description config (provider, model, baseUrl, prompts) that
|
|
22
|
+
* was active when descriptions were generated for this file. Skipped when
|
|
23
|
+
* no description provider is configured. Used to avoid re-describing
|
|
24
|
+
* files whose source content and description config are both unchanged.
|
|
25
|
+
*/
|
|
26
|
+
descHash?: string;
|
|
19
27
|
}
|
|
20
28
|
/** Current schema version for manifest files. */
|
|
21
|
-
export declare const SCHEMA_VERSION =
|
|
29
|
+
export declare const SCHEMA_VERSION = 3;
|
|
30
|
+
/**
|
|
31
|
+
* Oldest schema version still accepted as valid.
|
|
32
|
+
* Bumping this intentionally marks older manifests corrupt and forces a
|
|
33
|
+
* rebuild, so only raise it when a migration is not practical.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MIN_SUPPORTED_SCHEMA_VERSION = 3;
|
|
22
36
|
/** Persistent manifest tracking indexing state across sessions. */
|
|
23
37
|
export interface FileManifest {
|
|
24
38
|
/** Unix timestamp of the last full index pass. */
|
|
@@ -49,6 +63,15 @@ export declare function manifestPathFor(dbPath: string): string;
|
|
|
49
63
|
export declare function normalizeFilePath(filePath: string): string;
|
|
50
64
|
/** Compute the SHA-256 hex hash of a string. */
|
|
51
65
|
export declare function computeFileHash(content: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Compute a hash of the description configuration (provider, model, baseUrl,
|
|
68
|
+
* systemPrompt, image prompt, etc.) so that files are only re-described when
|
|
69
|
+
* the config actually changes.
|
|
70
|
+
*
|
|
71
|
+
* Returns `undefined` when neither `description` nor `imageDescription` is
|
|
72
|
+
* configured — callers treat this as "no descriptions needed".
|
|
73
|
+
*/
|
|
74
|
+
export declare function computeDescriptionConfigHash(config: RagConfig): string | undefined;
|
|
52
75
|
/** Load a manifest from disk, handling missing or corrupt files gracefully. */
|
|
53
76
|
export declare function loadManifest(dbPath: string): Promise<LoadedManifest>;
|
|
54
77
|
/** Persist a manifest to disk with atomic write (temp file + rename, with Windows fallback). */
|
package/dist/core/manifest.js
CHANGED
|
@@ -6,7 +6,13 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
/** Current schema version for manifest files. */
|
|
9
|
-
export const SCHEMA_VERSION =
|
|
9
|
+
export const SCHEMA_VERSION = 3;
|
|
10
|
+
/**
|
|
11
|
+
* Oldest schema version still accepted as valid.
|
|
12
|
+
* Bumping this intentionally marks older manifests corrupt and forces a
|
|
13
|
+
* rebuild, so only raise it when a migration is not practical.
|
|
14
|
+
*/
|
|
15
|
+
export const MIN_SUPPORTED_SCHEMA_VERSION = 3;
|
|
10
16
|
/** Create a new empty manifest with the current schema version. */
|
|
11
17
|
export function createEmptyManifest() {
|
|
12
18
|
return { files: {}, schemaVersion: SCHEMA_VERSION };
|
|
@@ -23,6 +29,30 @@ export function normalizeFilePath(filePath) {
|
|
|
23
29
|
export function computeFileHash(content) {
|
|
24
30
|
return createHash("sha256").update(content).digest("hex");
|
|
25
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Compute a hash of the description configuration (provider, model, baseUrl,
|
|
34
|
+
* systemPrompt, image prompt, etc.) so that files are only re-described when
|
|
35
|
+
* the config actually changes.
|
|
36
|
+
*
|
|
37
|
+
* Returns `undefined` when neither `description` nor `imageDescription` is
|
|
38
|
+
* configured — callers treat this as "no descriptions needed".
|
|
39
|
+
*/
|
|
40
|
+
export function computeDescriptionConfigHash(config) {
|
|
41
|
+
const desc = config.description;
|
|
42
|
+
const img = config.imageDescription;
|
|
43
|
+
if (!desc && !img)
|
|
44
|
+
return undefined;
|
|
45
|
+
const parts = [];
|
|
46
|
+
if (desc) {
|
|
47
|
+
parts.push(`desc:${desc.provider}|${desc.model}|${desc.baseUrl}|${desc.systemPrompt}`);
|
|
48
|
+
}
|
|
49
|
+
if (img) {
|
|
50
|
+
parts.push(`img:${img.provider}|${img.model}|${img.baseUrl}|${img.prompt}`);
|
|
51
|
+
}
|
|
52
|
+
if (parts.length === 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
return createHash("sha256").update(parts.join("||")).digest("hex").slice(0, 16);
|
|
55
|
+
}
|
|
26
56
|
/** Load a manifest from disk, handling missing or corrupt files gracefully. */
|
|
27
57
|
export async function loadManifest(dbPath) {
|
|
28
58
|
const filePath = manifestPathFor(dbPath);
|
|
@@ -32,14 +62,15 @@ export async function loadManifest(dbPath) {
|
|
|
32
62
|
if (!parsed || typeof parsed !== "object" || !parsed.files || typeof parsed.files !== "object") {
|
|
33
63
|
return { manifest: createEmptyManifest(), path: filePath, status: "corrupt" };
|
|
34
64
|
}
|
|
65
|
+
const version = parsed.schemaVersion ?? 0;
|
|
35
66
|
return {
|
|
36
67
|
manifest: {
|
|
37
68
|
lastIndexedAt: typeof parsed.lastIndexedAt === "number" ? parsed.lastIndexedAt : undefined,
|
|
38
|
-
schemaVersion:
|
|
69
|
+
schemaVersion: version,
|
|
39
70
|
files: parsed.files,
|
|
40
71
|
},
|
|
41
72
|
path: filePath,
|
|
42
|
-
status:
|
|
73
|
+
status: version >= MIN_SUPPORTED_SCHEMA_VERSION ? "ok" : "corrupt",
|
|
43
74
|
};
|
|
44
75
|
}
|
|
45
76
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ function resolveForSection(provider, section, worktree) {
|
|
|
41
41
|
// so createEmbedder can throw a clear error about the missing key
|
|
42
42
|
}
|
|
43
43
|
function stripJsoncComments(text) {
|
|
44
|
-
return text.replace(
|
|
44
|
+
return text.replace(/("[^"\\]*(?:\\.[^"\\]*)*")|(\/\/[^\n]*|\/\*[\s\S]*?\*\/)/g, (_, string) => string ?? "");
|
|
45
45
|
}
|
|
46
46
|
function readOpenCodeProviderKey(worktree, providerId) {
|
|
47
47
|
const locations = [
|
|
@@ -57,7 +57,9 @@ function readOpenCodeProviderKey(worktree, providerId) {
|
|
|
57
57
|
if (!existsSync(loc))
|
|
58
58
|
continue;
|
|
59
59
|
const raw = readFileSync(loc, "utf-8");
|
|
60
|
-
|
|
60
|
+
// Only strip JSONC comments from .jsonc files; plain JSON may contain
|
|
61
|
+
// "https://" URLs that look like line-comment markers after "//".
|
|
62
|
+
const cleaned = loc.endsWith(".jsonc") ? stripJsoncComments(raw) : raw;
|
|
61
63
|
const config = JSON.parse(cleaned);
|
|
62
64
|
const providerSection = config.provider;
|
|
63
65
|
if (!providerSection)
|
|
@@ -80,41 +80,43 @@ export function applyRuntimeOverrides(cfg, overrides) {
|
|
|
80
80
|
merged.embedding.baseUrl = overrides.embedding.baseUrl;
|
|
81
81
|
}
|
|
82
82
|
if (overrides.description) {
|
|
83
|
+
const defaultDesc = DEFAULT_CONFIG.description;
|
|
83
84
|
if (overrides.description.enabled !== undefined) {
|
|
84
85
|
if (!merged.description)
|
|
85
|
-
merged.description = {
|
|
86
|
+
merged.description = { ...defaultDesc };
|
|
86
87
|
merged.description.enabled = overrides.description.enabled;
|
|
87
88
|
}
|
|
88
89
|
if (overrides.description.provider !== undefined) {
|
|
89
90
|
if (!merged.description)
|
|
90
|
-
merged.description = {
|
|
91
|
+
merged.description = { ...defaultDesc };
|
|
91
92
|
merged.description.provider = overrides.description.provider;
|
|
92
93
|
}
|
|
93
94
|
if (overrides.description.model !== undefined) {
|
|
94
95
|
if (!merged.description)
|
|
95
|
-
merged.description = {
|
|
96
|
+
merged.description = { ...defaultDesc };
|
|
96
97
|
merged.description.model = overrides.description.model;
|
|
97
98
|
}
|
|
98
99
|
if (overrides.description.baseUrl !== undefined) {
|
|
99
100
|
if (!merged.description)
|
|
100
|
-
merged.description = {
|
|
101
|
+
merged.description = { ...defaultDesc };
|
|
101
102
|
merged.description.baseUrl = overrides.description.baseUrl;
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
if (overrides.imageDescription) {
|
|
106
|
+
const defaultImg = DEFAULT_CONFIG.imageDescription;
|
|
105
107
|
if (overrides.imageDescription.enabled !== undefined) {
|
|
106
108
|
if (!merged.imageDescription)
|
|
107
|
-
merged.imageDescription = {
|
|
109
|
+
merged.imageDescription = { ...defaultImg };
|
|
108
110
|
merged.imageDescription.enabled = overrides.imageDescription.enabled;
|
|
109
111
|
}
|
|
110
112
|
if (overrides.imageDescription.provider !== undefined) {
|
|
111
113
|
if (!merged.imageDescription)
|
|
112
|
-
merged.imageDescription = {
|
|
114
|
+
merged.imageDescription = { ...defaultImg };
|
|
113
115
|
merged.imageDescription.provider = overrides.imageDescription.provider;
|
|
114
116
|
}
|
|
115
117
|
if (overrides.imageDescription.model !== undefined) {
|
|
116
118
|
if (!merged.imageDescription)
|
|
117
|
-
merged.imageDescription = {
|
|
119
|
+
merged.imageDescription = { ...defaultImg };
|
|
118
120
|
merged.imageDescription.model = overrides.imageDescription.model;
|
|
119
121
|
}
|
|
120
122
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SetupResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
errors: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function getRuntimeDir(): string;
|
|
6
|
+
export declare function getNpmGlobalRoot(): string;
|
|
7
|
+
export declare function getVersionFile(runtimeDir: string): string;
|
|
8
|
+
export declare function readVersionFile(versionFile: string): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* npm-generated .cmd / .ps1 wrappers on Windows invoke `.js` files directly
|
|
11
|
+
* via file association. If the `.js` association points to a text editor
|
|
12
|
+
* (e.g. Notepad++) instead of Node.js, the CLI opens the editor instead of
|
|
13
|
+
* running. This function patches the wrappers to call `node` explicitly.
|
|
14
|
+
*
|
|
15
|
+
* Runs only on Windows. Safe to call on every setup — detects already-patched
|
|
16
|
+
* wrappers by checking for the `node` prefix.
|
|
17
|
+
*/
|
|
18
|
+
export declare function patchWindowsWrappers(npmGlobalRoot: string): void;
|
|
19
|
+
export declare function setupRuntime(options?: {
|
|
20
|
+
force?: boolean;
|
|
21
|
+
silent?: boolean;
|
|
22
|
+
version?: string;
|
|
23
|
+
}): Promise<SetupResult>;
|