@tai-io/codesearch 2026.313.1614
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/dist/build-info.d.ts +3 -0
- package/dist/build-info.js +4 -0
- package/dist/config.d.ts +62 -0
- package/dist/config.js +52 -0
- package/dist/core/cleanup.d.ts +8 -0
- package/dist/core/cleanup.js +41 -0
- package/dist/core/doc-indexer.d.ts +13 -0
- package/dist/core/doc-indexer.js +76 -0
- package/dist/core/doc-searcher.d.ts +13 -0
- package/dist/core/doc-searcher.js +65 -0
- package/dist/core/file-category.d.ts +7 -0
- package/dist/core/file-category.js +75 -0
- package/dist/core/indexer.d.ts +18 -0
- package/dist/core/indexer.js +177 -0
- package/dist/core/preview.d.ts +13 -0
- package/dist/core/preview.js +58 -0
- package/dist/core/repo-map.d.ts +33 -0
- package/dist/core/repo-map.js +144 -0
- package/dist/core/searcher.d.ts +12 -0
- package/dist/core/searcher.js +97 -0
- package/dist/core/sync.d.ts +15 -0
- package/dist/core/sync.js +212 -0
- package/dist/core/targeted-indexer.d.ts +19 -0
- package/dist/core/targeted-indexer.js +127 -0
- package/dist/embedding/factory.d.ts +4 -0
- package/dist/embedding/factory.js +24 -0
- package/dist/embedding/openai.d.ts +33 -0
- package/dist/embedding/openai.js +234 -0
- package/dist/embedding/truncate.d.ts +6 -0
- package/dist/embedding/truncate.js +14 -0
- package/dist/embedding/types.d.ts +18 -0
- package/dist/embedding/types.js +2 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +21 -0
- package/dist/format.d.ts +18 -0
- package/dist/format.js +151 -0
- package/dist/hooks/cli-router.d.ts +7 -0
- package/dist/hooks/cli-router.js +47 -0
- package/dist/hooks/hook-output.d.ts +56 -0
- package/dist/hooks/hook-output.js +21 -0
- package/dist/hooks/post-tool-use.d.ts +13 -0
- package/dist/hooks/post-tool-use.js +123 -0
- package/dist/hooks/stop-hook.d.ts +11 -0
- package/dist/hooks/stop-hook.js +137 -0
- package/dist/hooks/targeted-runner.d.ts +11 -0
- package/dist/hooks/targeted-runner.js +58 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +138 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +54 -0
- package/dist/setup-message.d.ts +4 -0
- package/dist/setup-message.js +48 -0
- package/dist/splitter/ast.d.ts +13 -0
- package/dist/splitter/ast.js +231 -0
- package/dist/splitter/line.d.ts +10 -0
- package/dist/splitter/line.js +103 -0
- package/dist/splitter/symbol-extract.d.ts +16 -0
- package/dist/splitter/symbol-extract.js +61 -0
- package/dist/splitter/types.d.ts +16 -0
- package/dist/splitter/types.js +2 -0
- package/dist/state/doc-metadata.d.ts +18 -0
- package/dist/state/doc-metadata.js +59 -0
- package/dist/state/registry.d.ts +7 -0
- package/dist/state/registry.js +46 -0
- package/dist/state/snapshot.d.ts +26 -0
- package/dist/state/snapshot.js +100 -0
- package/dist/tool-schemas.d.ts +215 -0
- package/dist/tool-schemas.js +269 -0
- package/dist/tools.d.ts +58 -0
- package/dist/tools.js +245 -0
- package/dist/vectordb/rrf.d.ts +32 -0
- package/dist/vectordb/rrf.js +88 -0
- package/dist/vectordb/sqlite.d.ts +34 -0
- package/dist/vectordb/sqlite.js +624 -0
- package/dist/vectordb/types.d.ts +63 -0
- package/dist/vectordb/types.js +2 -0
- package/messages.yaml +69 -0
- package/package.json +79 -0
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StateManager } from './state/snapshot.js';
|
|
2
|
+
import type { Embedding } from './embedding/types.js';
|
|
3
|
+
import type { VectorDB } from './vectordb/types.js';
|
|
4
|
+
export declare class ToolHandlers {
|
|
5
|
+
private embedding;
|
|
6
|
+
private vectordb;
|
|
7
|
+
private state;
|
|
8
|
+
constructor(embedding: Embedding, vectordb: VectorDB, state: StateManager);
|
|
9
|
+
handleIndex(args: Record<string, unknown>): Promise<{
|
|
10
|
+
content: {
|
|
11
|
+
type: string;
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
}>;
|
|
15
|
+
handleSearch(args: Record<string, unknown>): Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: string;
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
}>;
|
|
21
|
+
handleClear(args: Record<string, unknown>): Promise<{
|
|
22
|
+
content: {
|
|
23
|
+
type: string;
|
|
24
|
+
text: string;
|
|
25
|
+
}[];
|
|
26
|
+
}>;
|
|
27
|
+
handleList(): Promise<{
|
|
28
|
+
content: {
|
|
29
|
+
type: string;
|
|
30
|
+
text: string;
|
|
31
|
+
}[];
|
|
32
|
+
}>;
|
|
33
|
+
handleCleanup(args: Record<string, unknown>): Promise<{
|
|
34
|
+
content: {
|
|
35
|
+
type: string;
|
|
36
|
+
text: string;
|
|
37
|
+
}[];
|
|
38
|
+
}>;
|
|
39
|
+
handleIngest(args: Record<string, unknown>): Promise<{
|
|
40
|
+
content: {
|
|
41
|
+
type: string;
|
|
42
|
+
text: string;
|
|
43
|
+
}[];
|
|
44
|
+
}>;
|
|
45
|
+
handleLookup(args: Record<string, unknown>): Promise<{
|
|
46
|
+
content: {
|
|
47
|
+
type: string;
|
|
48
|
+
text: string;
|
|
49
|
+
}[];
|
|
50
|
+
}>;
|
|
51
|
+
handleBrowse(args: Record<string, unknown>): Promise<{
|
|
52
|
+
content: {
|
|
53
|
+
type: string;
|
|
54
|
+
text: string;
|
|
55
|
+
}[];
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { normalizePath, pathToCollectionName } from './paths.js';
|
|
2
|
+
import { indexCodebase, previewCodebase, deleteSnapshot } from './core/indexer.js';
|
|
3
|
+
import { cleanupVectors } from './core/cleanup.js';
|
|
4
|
+
import { scanFiles, buildSnapshot, diffSnapshots } from './core/sync.js';
|
|
5
|
+
import { loadSnapshot } from './vectordb/sqlite.js';
|
|
6
|
+
import { getConfig } from './config.js';
|
|
7
|
+
import { searchCode, formatSearchResults, formatCompactResults } from './core/searcher.js';
|
|
8
|
+
import { indexDocument } from './core/doc-indexer.js';
|
|
9
|
+
import { searchDocuments } from './core/doc-searcher.js';
|
|
10
|
+
import { generateRepoMap, VectorDBSymbolSource } from './core/repo-map.js';
|
|
11
|
+
import { registerProject, resolveProject, listProjects } from './state/registry.js';
|
|
12
|
+
import { textResult, formatCleanupResult, formatPreview, formatIndexResult, formatListIndexed, formatDocIndexResult, formatDocSearchResults, } from './format.js';
|
|
13
|
+
function getErrorMessage(err) {
|
|
14
|
+
return err instanceof Error ? err.message : String(err);
|
|
15
|
+
}
|
|
16
|
+
function resolvePath(args) {
|
|
17
|
+
const pathArg = args.path;
|
|
18
|
+
if (pathArg)
|
|
19
|
+
return normalizePath(pathArg);
|
|
20
|
+
const projectArg = args.project;
|
|
21
|
+
if (projectArg)
|
|
22
|
+
return resolveProject(projectArg);
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
function noPathError() {
|
|
26
|
+
const projects = listProjects();
|
|
27
|
+
const names = Object.keys(projects);
|
|
28
|
+
if (names.length > 0) {
|
|
29
|
+
const list = names.map((n) => ` - ${n} → ${projects[n]}`).join('\n');
|
|
30
|
+
return textResult(`Error: provide \`path\` or \`project\`. Registered projects:\n${list}`);
|
|
31
|
+
}
|
|
32
|
+
return textResult('Error: provide `path` (absolute) or `project` (name). No projects registered yet — index a codebase first.');
|
|
33
|
+
}
|
|
34
|
+
const locks = new Map();
|
|
35
|
+
async function withMutex(key, fn) {
|
|
36
|
+
const prev = locks.get(key) ?? Promise.resolve();
|
|
37
|
+
let resolve;
|
|
38
|
+
const current = new Promise((r) => {
|
|
39
|
+
resolve = r;
|
|
40
|
+
});
|
|
41
|
+
locks.set(key, current);
|
|
42
|
+
await prev;
|
|
43
|
+
try {
|
|
44
|
+
return await fn();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
resolve();
|
|
48
|
+
if (locks.get(key) === current) {
|
|
49
|
+
locks.delete(key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class ToolHandlers {
|
|
54
|
+
embedding;
|
|
55
|
+
vectordb;
|
|
56
|
+
state;
|
|
57
|
+
constructor(embedding, vectordb, state) {
|
|
58
|
+
this.embedding = embedding;
|
|
59
|
+
this.vectordb = vectordb;
|
|
60
|
+
this.state = state;
|
|
61
|
+
}
|
|
62
|
+
async handleIndex(args) {
|
|
63
|
+
const normalizedPath = resolvePath(args);
|
|
64
|
+
if (!normalizedPath)
|
|
65
|
+
return noPathError();
|
|
66
|
+
const force = args.force ?? false;
|
|
67
|
+
const dryRun = args.dryRun ?? false;
|
|
68
|
+
const config = getConfig();
|
|
69
|
+
const customExt = args.customExtensions ?? config.customExtensions;
|
|
70
|
+
const customIgnore = args.customIgnorePatterns ?? config.customIgnorePatterns;
|
|
71
|
+
const collectionName = pathToCollectionName(normalizedPath);
|
|
72
|
+
if (dryRun) {
|
|
73
|
+
try {
|
|
74
|
+
const preview = await previewCodebase(normalizedPath, customExt, customIgnore);
|
|
75
|
+
return textResult(formatPreview(preview, normalizedPath));
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const message = getErrorMessage(err);
|
|
79
|
+
return textResult(`Error previewing ${normalizedPath}: ${message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return withMutex(normalizedPath, async () => {
|
|
83
|
+
this.state.setIndexing(normalizedPath, collectionName);
|
|
84
|
+
try {
|
|
85
|
+
const result = await indexCodebase(normalizedPath, this.embedding, this.vectordb, force, (pct, msg) => {
|
|
86
|
+
this.state.updateProgress(normalizedPath, pct, msg);
|
|
87
|
+
}, customExt, customIgnore);
|
|
88
|
+
this.state.setIndexed(normalizedPath, result.totalFiles, result.totalChunks);
|
|
89
|
+
registerProject(normalizedPath);
|
|
90
|
+
return textResult(formatIndexResult(result, normalizedPath));
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const message = getErrorMessage(err);
|
|
94
|
+
this.state.setError(normalizedPath, message);
|
|
95
|
+
return textResult(`Error indexing ${normalizedPath}: ${message}`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async handleSearch(args) {
|
|
100
|
+
const query = args.query;
|
|
101
|
+
if (!query)
|
|
102
|
+
return textResult('Error: "query" is required. Provide a natural language search query.');
|
|
103
|
+
const normalizedPath = resolvePath(args);
|
|
104
|
+
if (!normalizedPath)
|
|
105
|
+
return noPathError();
|
|
106
|
+
const rawLimit = args.limit;
|
|
107
|
+
const limit = rawLimit !== undefined && Number.isFinite(rawLimit) && rawLimit >= 1 ? rawLimit : undefined;
|
|
108
|
+
const extensionFilter = args.extensionFilter;
|
|
109
|
+
const compact = args.compact !== false;
|
|
110
|
+
try {
|
|
111
|
+
const results = await searchCode(normalizedPath, query, this.embedding, this.vectordb, {
|
|
112
|
+
limit,
|
|
113
|
+
extensionFilter,
|
|
114
|
+
});
|
|
115
|
+
const formatted = compact
|
|
116
|
+
? formatCompactResults(results, query, normalizedPath)
|
|
117
|
+
: formatSearchResults(results, query, normalizedPath);
|
|
118
|
+
return textResult(formatted);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const message = getErrorMessage(err);
|
|
122
|
+
return textResult(`Error: ${message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async handleClear(args) {
|
|
126
|
+
const normalizedPath = resolvePath(args);
|
|
127
|
+
if (!normalizedPath)
|
|
128
|
+
return noPathError();
|
|
129
|
+
const collectionName = pathToCollectionName(normalizedPath);
|
|
130
|
+
return withMutex(normalizedPath, async () => {
|
|
131
|
+
try {
|
|
132
|
+
await this.vectordb.dropCollection(collectionName);
|
|
133
|
+
deleteSnapshot(normalizedPath);
|
|
134
|
+
this.state.remove(normalizedPath);
|
|
135
|
+
return textResult(`Index cleared for ${normalizedPath}.`);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const message = getErrorMessage(err);
|
|
139
|
+
return textResult(`Error clearing index: ${message}`);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
144
|
+
async handleList() {
|
|
145
|
+
const states = this.state.getAllStates();
|
|
146
|
+
if (states.length === 0) {
|
|
147
|
+
return textResult('No codebases are currently indexed in this session.\n\nUse `index` to index a codebase first.');
|
|
148
|
+
}
|
|
149
|
+
return textResult(formatListIndexed(states));
|
|
150
|
+
}
|
|
151
|
+
async handleCleanup(args) {
|
|
152
|
+
const normalizedPath = resolvePath(args);
|
|
153
|
+
if (!normalizedPath)
|
|
154
|
+
return noPathError();
|
|
155
|
+
const dryRun = args.dryRun ?? false;
|
|
156
|
+
const config = getConfig();
|
|
157
|
+
const customExt = args.customExtensions ?? config.customExtensions;
|
|
158
|
+
const customIgnore = args.customIgnorePatterns ?? config.customIgnorePatterns;
|
|
159
|
+
return withMutex(normalizedPath, async () => {
|
|
160
|
+
try {
|
|
161
|
+
if (dryRun) {
|
|
162
|
+
const previousSnapshot = loadSnapshot(normalizedPath);
|
|
163
|
+
if (!previousSnapshot) {
|
|
164
|
+
return textResult(`Error: No snapshot found for ${normalizedPath}. Index the codebase first before running cleanup.`);
|
|
165
|
+
}
|
|
166
|
+
const filePaths = await scanFiles(normalizedPath, customExt, customIgnore);
|
|
167
|
+
const currentSnapshot = buildSnapshot(normalizedPath, filePaths);
|
|
168
|
+
const { removed } = diffSnapshots(previousSnapshot, currentSnapshot);
|
|
169
|
+
const dryResult = { removedFiles: removed, totalRemoved: removed.length, durationMs: 0 };
|
|
170
|
+
return textResult(formatCleanupResult(dryResult, normalizedPath, true));
|
|
171
|
+
}
|
|
172
|
+
const result = await cleanupVectors(normalizedPath, this.vectordb, undefined, customExt, customIgnore);
|
|
173
|
+
return textResult(formatCleanupResult(result, normalizedPath, false));
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const message = getErrorMessage(err);
|
|
177
|
+
return textResult(`Error during cleanup: ${message}`);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async handleIngest(args) {
|
|
182
|
+
const content = args.content;
|
|
183
|
+
if (!content)
|
|
184
|
+
return textResult('Error: "content" is required. Provide the documentation text to cache.');
|
|
185
|
+
const source = args.source;
|
|
186
|
+
if (!source)
|
|
187
|
+
return textResult('Error: "source" is required. Provide the source URL or identifier.');
|
|
188
|
+
const library = args.library;
|
|
189
|
+
if (!library)
|
|
190
|
+
return textResult('Error: "library" is required. Provide the library name (e.g., "react", "langfuse").');
|
|
191
|
+
const topic = args.topic;
|
|
192
|
+
if (!topic)
|
|
193
|
+
return textResult('Error: "topic" is required. Provide the topic within the library (e.g., "hooks").');
|
|
194
|
+
const ttlDays = args.ttlDays ?? 7;
|
|
195
|
+
try {
|
|
196
|
+
const result = await indexDocument(content, source, library, topic, this.embedding, this.vectordb, ttlDays);
|
|
197
|
+
return textResult(formatDocIndexResult(result));
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
const message = getErrorMessage(err);
|
|
201
|
+
return textResult(`Error caching documentation: ${message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async handleLookup(args) {
|
|
205
|
+
const query = args.query;
|
|
206
|
+
if (!query)
|
|
207
|
+
return textResult('Error: "query" is required. Provide a natural language search query.');
|
|
208
|
+
const library = args.library;
|
|
209
|
+
const rawLimit = args.limit;
|
|
210
|
+
const limit = rawLimit !== undefined && Number.isFinite(rawLimit) && rawLimit >= 1 ? rawLimit : undefined;
|
|
211
|
+
try {
|
|
212
|
+
const results = await searchDocuments(query, this.embedding, this.vectordb, {
|
|
213
|
+
library,
|
|
214
|
+
limit,
|
|
215
|
+
});
|
|
216
|
+
return textResult(formatDocSearchResults(results, query));
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
const message = getErrorMessage(err);
|
|
220
|
+
return textResult(`Error: ${message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async handleBrowse(args) {
|
|
224
|
+
const normalizedPath = resolvePath(args);
|
|
225
|
+
if (!normalizedPath)
|
|
226
|
+
return noPathError();
|
|
227
|
+
const pathFilter = args.pathFilter;
|
|
228
|
+
const kindFilter = args.kind;
|
|
229
|
+
const maxTokens = args.maxTokens;
|
|
230
|
+
try {
|
|
231
|
+
const source = new VectorDBSymbolSource(this.vectordb);
|
|
232
|
+
const map = await generateRepoMap(normalizedPath, source, {
|
|
233
|
+
pathFilter,
|
|
234
|
+
kindFilter,
|
|
235
|
+
maxTokens,
|
|
236
|
+
});
|
|
237
|
+
return textResult(map);
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
const message = getErrorMessage(err);
|
|
241
|
+
return textResult(`Error: ${message}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SearchResult } from './types.js';
|
|
2
|
+
export declare const RRF_K = 5;
|
|
3
|
+
export declare const RRF_ALPHA = 0.7;
|
|
4
|
+
export interface RankedPoint {
|
|
5
|
+
id: string | number;
|
|
6
|
+
payload?: Record<string, unknown> | null;
|
|
7
|
+
rawScore: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ScoredPayload {
|
|
10
|
+
id: string | number;
|
|
11
|
+
content: string;
|
|
12
|
+
relativePath: string;
|
|
13
|
+
startLine: number;
|
|
14
|
+
endLine: number;
|
|
15
|
+
fileExtension: string;
|
|
16
|
+
language: string;
|
|
17
|
+
fileCategory: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function rankByTermFrequency(points: {
|
|
20
|
+
id: string | number;
|
|
21
|
+
payload?: Record<string, unknown> | null;
|
|
22
|
+
}[], queryText: string): RankedPoint[];
|
|
23
|
+
export declare function extractPayload(point: {
|
|
24
|
+
id: string | number;
|
|
25
|
+
payload?: Record<string, unknown> | null;
|
|
26
|
+
}): ScoredPayload;
|
|
27
|
+
export declare function reciprocalRankFusion(denseResults: {
|
|
28
|
+
id: string | number;
|
|
29
|
+
score?: number;
|
|
30
|
+
payload?: Record<string, unknown> | null;
|
|
31
|
+
}[], textResults: RankedPoint[], limit: number): SearchResult[];
|
|
32
|
+
//# sourceMappingURL=rrf.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const RRF_K = 5; // Reciprocal Rank Fusion constant (low k for code search — stronger rank separation)
|
|
2
|
+
export const RRF_ALPHA = 0.7; // Blend weight: 70% rank-based (fusion stability), 30% raw similarity (query-specific signal)
|
|
3
|
+
// Rank text-match results by normalized term frequency so RRF receives a meaningful ordering.
|
|
4
|
+
export function rankByTermFrequency(points, queryText) {
|
|
5
|
+
if (points.length === 0)
|
|
6
|
+
return [];
|
|
7
|
+
const terms = [
|
|
8
|
+
...new Set(queryText
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.split(/\s+/)
|
|
11
|
+
.filter((t) => t.length > 0)),
|
|
12
|
+
];
|
|
13
|
+
if (terms.length === 0)
|
|
14
|
+
return points.map((p) => ({ ...p, rawScore: 0 }));
|
|
15
|
+
const termPatterns = terms.map((t) => new RegExp(t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'));
|
|
16
|
+
const scored = points.map((point) => {
|
|
17
|
+
const content = point.payload?.content ?? '';
|
|
18
|
+
const wordCount = Math.max(1, content.split(/\s+/).length);
|
|
19
|
+
let hits = 0;
|
|
20
|
+
for (const pattern of termPatterns) {
|
|
21
|
+
pattern.lastIndex = 0;
|
|
22
|
+
const matches = content.match(pattern);
|
|
23
|
+
hits += matches ? matches.length : 0;
|
|
24
|
+
}
|
|
25
|
+
const tf = hits / wordCount;
|
|
26
|
+
return { point, tf };
|
|
27
|
+
});
|
|
28
|
+
scored.sort((a, b) => b.tf - a.tf);
|
|
29
|
+
const maxTf = scored[0].tf;
|
|
30
|
+
return scored.map((s) => ({
|
|
31
|
+
...s.point,
|
|
32
|
+
rawScore: maxTf > 0 ? s.tf / maxTf : 0,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
export function extractPayload(point) {
|
|
36
|
+
const p = point.payload ?? {};
|
|
37
|
+
return {
|
|
38
|
+
id: point.id,
|
|
39
|
+
content: String(p.content ?? ''),
|
|
40
|
+
relativePath: String(p.relativePath ?? ''),
|
|
41
|
+
startLine: Number(p.startLine ?? 0),
|
|
42
|
+
endLine: Number(p.endLine ?? 0),
|
|
43
|
+
fileExtension: String(p.fileExtension ?? ''),
|
|
44
|
+
language: String(p.language ?? ''),
|
|
45
|
+
fileCategory: String(p.fileCategory ?? ''),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function reciprocalRankFusion(denseResults, textResults, limit) {
|
|
49
|
+
const scoreMap = new Map();
|
|
50
|
+
const blendedScore = (rank, rawSimilarity) => RRF_ALPHA * (1 / (RRF_K + rank + 1)) + (1 - RRF_ALPHA) * rawSimilarity;
|
|
51
|
+
for (let rank = 0; rank < denseResults.length; rank++) {
|
|
52
|
+
const point = denseResults[rank];
|
|
53
|
+
const rawSim = point.score ?? 0;
|
|
54
|
+
const score = blendedScore(rank, rawSim);
|
|
55
|
+
const existing = scoreMap.get(point.id);
|
|
56
|
+
const payload = extractPayload(point);
|
|
57
|
+
if (existing) {
|
|
58
|
+
existing.score += score;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
scoreMap.set(point.id, { score, payload });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (let rank = 0; rank < textResults.length; rank++) {
|
|
65
|
+
const point = textResults[rank];
|
|
66
|
+
const score = blendedScore(rank, point.rawScore);
|
|
67
|
+
const existing = scoreMap.get(point.id);
|
|
68
|
+
const payload = extractPayload(point);
|
|
69
|
+
if (existing) {
|
|
70
|
+
existing.score += score;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
scoreMap.set(point.id, { score, payload });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const sorted = [...scoreMap.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
77
|
+
return sorted.map(({ score, payload }) => ({
|
|
78
|
+
content: payload.content,
|
|
79
|
+
relativePath: payload.relativePath,
|
|
80
|
+
startLine: payload.startLine,
|
|
81
|
+
endLine: payload.endLine,
|
|
82
|
+
fileExtension: payload.fileExtension,
|
|
83
|
+
language: payload.language,
|
|
84
|
+
score,
|
|
85
|
+
fileCategory: payload.fileCategory,
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=rrf.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { VectorDB, CodeDocument, HybridSearchParams, SearchResult, SymbolEntry } from './types.js';
|
|
2
|
+
import type { FileSnapshot } from '../core/sync.js';
|
|
3
|
+
export declare class SqliteVectorDB implements VectorDB {
|
|
4
|
+
private dbPath;
|
|
5
|
+
constructor(dbPath: string);
|
|
6
|
+
private get db();
|
|
7
|
+
createCollection(name: string, dimension: number): Promise<void>;
|
|
8
|
+
hasCollection(name: string): Promise<boolean>;
|
|
9
|
+
dropCollection(name: string): Promise<void>;
|
|
10
|
+
insert(name: string, documents: CodeDocument[]): Promise<void>;
|
|
11
|
+
search(name: string, params: HybridSearchParams): Promise<SearchResult[]>;
|
|
12
|
+
getById(name: string, id: string): Promise<{
|
|
13
|
+
payload: Record<string, unknown>;
|
|
14
|
+
vector: number[];
|
|
15
|
+
} | null>;
|
|
16
|
+
updatePoint(name: string, id: string, vector: number[], payload: Record<string, unknown>): Promise<void>;
|
|
17
|
+
deleteByPath(name: string, relativePath: string): Promise<void>;
|
|
18
|
+
deleteByFilter(name: string, filter: Record<string, unknown>): Promise<void>;
|
|
19
|
+
listSymbols(name: string): Promise<SymbolEntry[]>;
|
|
20
|
+
scrollAll(name: string): Promise<{
|
|
21
|
+
id: string | number;
|
|
22
|
+
vector: number[];
|
|
23
|
+
payload: Record<string, unknown>;
|
|
24
|
+
}[]>;
|
|
25
|
+
}
|
|
26
|
+
export declare function loadSnapshot(rootPath: string, dbPath?: string): FileSnapshot | null;
|
|
27
|
+
export declare function saveSnapshot(rootPath: string, snapshot: FileSnapshot, dbPath?: string): void;
|
|
28
|
+
export declare function deleteSnapshot(rootPath: string, dbPath?: string): void;
|
|
29
|
+
export declare function snapshotExists(rootPath: string, dbPath?: string): boolean;
|
|
30
|
+
export declare function listSnapshotCollections(dbPath?: string): string[];
|
|
31
|
+
export declare function deleteSnapshotByCollection(collectionName: string, dbPath?: string): void;
|
|
32
|
+
/** Reset the module-level DB connection (for testing). */
|
|
33
|
+
export declare function resetForTesting(): void;
|
|
34
|
+
//# sourceMappingURL=sqlite.d.ts.map
|