brainbank 0.7.0 → 0.8.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 +76 -1398
- package/bin/brainbank +5 -1
- package/dist/{chunk-N2OJRXSB.js → chunk-3HVCONGF.js} +1 -1
- package/dist/{chunk-N2OJRXSB.js.map → chunk-3HVCONGF.js.map} +1 -1
- package/dist/{chunk-CCXVL56V.js → chunk-3JZIM5AU.js} +6 -3
- package/dist/chunk-3JZIM5AU.js.map +1 -0
- package/dist/{chunk-6XOXM7MI.js → chunk-5KU2PP34.js} +2 -2
- package/dist/{chunk-6XOXM7MI.js.map → chunk-5KU2PP34.js.map} +1 -1
- package/dist/chunk-7JDCHUJV.js +89 -0
- package/dist/chunk-7JDCHUJV.js.map +1 -0
- package/dist/{chunk-B77KABWH.js → chunk-7T2ZCZQA.js} +17 -15
- package/dist/chunk-7T2ZCZQA.js.map +1 -0
- package/dist/chunk-E3J37GDA.js +74 -0
- package/dist/chunk-E3J37GDA.js.map +1 -0
- package/dist/chunk-JEFWMS5Z.js +217 -0
- package/dist/chunk-JEFWMS5Z.js.map +1 -0
- package/dist/chunk-JRVYSTMP.js +3256 -0
- package/dist/chunk-JRVYSTMP.js.map +1 -0
- package/dist/chunk-OLDHLOMT.js +69 -0
- package/dist/chunk-OLDHLOMT.js.map +1 -0
- package/dist/{chunk-424UFCY7.js → chunk-QTZNB6AK.js} +6 -2
- package/dist/chunk-QTZNB6AK.js.map +1 -0
- package/dist/chunk-RFF7HMP6.js +109 -0
- package/dist/chunk-RFF7HMP6.js.map +1 -0
- package/dist/{chunk-ZNLN2VWV.js → chunk-TD3TEFI3.js} +1 -1
- package/dist/chunk-TD3TEFI3.js.map +1 -0
- package/dist/cli.js +1036 -341
- package/dist/cli.js.map +1 -1
- package/dist/haiku-expander-WOVJIVXD.js +8 -0
- package/dist/haiku-pruner-DB77ZQLJ.js +8 -0
- package/dist/http-server-GIRELCCL.js +9 -0
- package/dist/index.d.ts +1774 -611
- package/dist/index.js +282 -70
- package/dist/index.js.map +1 -1
- package/dist/{local-embedding-ZIMTK6PU.js → local-embedding-2RNCC5EU.js} +2 -2
- package/dist/{openai-embedding-VQZCZQYT.js → openai-embedding-Z5I4K4CN.js} +2 -2
- package/dist/perplexity-context-embedding-V5YUMXDR.js +9 -0
- package/dist/{perplexity-embedding-227WQY4R.js → perplexity-embedding-X2S72OAC.js} +2 -2
- package/dist/plugin-FF4Q34TI.js +32 -0
- package/dist/{qwen3-reranker-3MHEENT5.js → qwen3-reranker-HVIQOLKS.js} +2 -2
- package/dist/{resolve-CUJWY6HP.js → resolve-Q5D6HECY.js} +2 -2
- package/package.json +25 -52
- package/src/brainbank.ts +620 -0
- package/src/cli/commands/collection.ts +77 -0
- package/src/cli/commands/context.ts +171 -0
- package/src/cli/commands/daemon.ts +100 -0
- package/src/cli/commands/docs.ts +71 -0
- package/src/cli/commands/files.ts +69 -0
- package/src/cli/commands/help.ts +72 -0
- package/src/cli/commands/index.ts +282 -0
- package/src/cli/commands/kv.ts +140 -0
- package/src/cli/commands/mcp.ts +13 -0
- package/src/cli/commands/reembed.ts +30 -0
- package/src/cli/commands/scan.ts +365 -0
- package/src/cli/commands/search.ts +130 -0
- package/src/cli/commands/stats.ts +44 -0
- package/src/cli/commands/status.ts +47 -0
- package/src/cli/commands/watch.ts +43 -0
- package/src/cli/factory/brain-context.ts +43 -0
- package/src/cli/factory/builtin-registration.ts +123 -0
- package/src/cli/factory/config-loader.ts +72 -0
- package/src/cli/factory/index.ts +65 -0
- package/src/cli/factory/plugin-loader.ts +146 -0
- package/src/cli/index.ts +63 -0
- package/src/cli/server-client.ts +135 -0
- package/src/cli/utils.ts +121 -0
- package/src/config.ts +50 -0
- package/src/constants.ts +13 -0
- package/src/db/adapter.ts +112 -0
- package/src/db/metadata.ts +130 -0
- package/src/db/migrations.ts +66 -0
- package/src/db/sqlite-adapter.ts +208 -0
- package/src/db/tracker.ts +91 -0
- package/src/engine/index-api.ts +85 -0
- package/src/engine/reembed.ts +206 -0
- package/src/engine/search-api.ts +222 -0
- package/src/index.ts +159 -0
- package/src/lib/fts.ts +57 -0
- package/src/lib/languages.ts +180 -0
- package/src/lib/logger.ts +125 -0
- package/src/lib/math.ts +87 -0
- package/src/lib/provider-key.ts +20 -0
- package/src/lib/prune.ts +71 -0
- package/src/lib/rerank.ts +33 -0
- package/src/lib/rrf.ts +133 -0
- package/src/lib/write-lock.ts +108 -0
- package/src/plugin.ts +323 -0
- package/src/providers/embeddings/embedding-worker-thread.ts +95 -0
- package/src/providers/embeddings/embedding-worker.ts +141 -0
- package/src/providers/embeddings/local-embedding.ts +115 -0
- package/src/providers/embeddings/openai-embedding.ts +167 -0
- package/src/providers/embeddings/perplexity-context-embedding.ts +195 -0
- package/src/providers/embeddings/perplexity-embedding.ts +165 -0
- package/src/providers/embeddings/resolve.ts +34 -0
- package/src/providers/pruners/haiku-expander.ts +152 -0
- package/src/providers/pruners/haiku-pruner.ts +112 -0
- package/src/providers/rerankers/qwen3-reranker.ts +180 -0
- package/src/providers/vector/hnsw-index.ts +174 -0
- package/src/providers/vector/hnsw-loader.ts +129 -0
- package/src/search/bm25-boost.ts +61 -0
- package/src/search/context-builder.ts +298 -0
- package/src/search/keyword/composite-bm25-search.ts +62 -0
- package/src/search/types.ts +35 -0
- package/src/search/vector/composite-vector-search.ts +76 -0
- package/src/search/vector/mmr.ts +64 -0
- package/src/services/collection.ts +405 -0
- package/src/services/daemon.ts +87 -0
- package/src/services/http-server.ts +288 -0
- package/src/services/kv-service.ts +65 -0
- package/src/services/plugin-registry.ts +109 -0
- package/src/services/watch.ts +348 -0
- package/src/services/webhook-server.ts +100 -0
- package/src/types.ts +504 -0
- package/dist/base-3SNc_CeY.d.ts +0 -593
- package/dist/chunk-424UFCY7.js.map +0 -1
- package/dist/chunk-7EZR47JV.js +0 -232
- package/dist/chunk-7EZR47JV.js.map +0 -1
- package/dist/chunk-B77KABWH.js.map +0 -1
- package/dist/chunk-CCXVL56V.js.map +0 -1
- package/dist/chunk-DI3H6JVZ.js +0 -2432
- package/dist/chunk-DI3H6JVZ.js.map +0 -1
- package/dist/chunk-FGL32LUJ.js +0 -754
- package/dist/chunk-FGL32LUJ.js.map +0 -1
- package/dist/chunk-JRSKWF6K.js +0 -313
- package/dist/chunk-JRSKWF6K.js.map +0 -1
- package/dist/chunk-U2Q2XGPZ.js +0 -42
- package/dist/chunk-U2Q2XGPZ.js.map +0 -1
- package/dist/chunk-VQ27YUHH.js +0 -629
- package/dist/chunk-VQ27YUHH.js.map +0 -1
- package/dist/chunk-VVXYZIIB.js +0 -304
- package/dist/chunk-VVXYZIIB.js.map +0 -1
- package/dist/chunk-YOLKSYWK.js +0 -79
- package/dist/chunk-YOLKSYWK.js.map +0 -1
- package/dist/chunk-ZNLN2VWV.js.map +0 -1
- package/dist/code.d.ts +0 -33
- package/dist/code.js +0 -9
- package/dist/docs.d.ts +0 -21
- package/dist/docs.js +0 -9
- package/dist/git.d.ts +0 -33
- package/dist/git.js +0 -9
- package/dist/memory.d.ts +0 -17
- package/dist/memory.js +0 -9
- package/dist/notes.d.ts +0 -17
- package/dist/notes.js +0 -10
- package/dist/perplexity-context-embedding-KSVSZXMD.js +0 -9
- package/dist/resolve-CUJWY6HP.js.map +0 -1
- /package/dist/{code.js.map → haiku-expander-WOVJIVXD.js.map} +0 -0
- /package/dist/{docs.js.map → haiku-pruner-DB77ZQLJ.js.map} +0 -0
- /package/dist/{git.js.map → http-server-GIRELCCL.js.map} +0 -0
- /package/dist/{local-embedding-ZIMTK6PU.js.map → local-embedding-2RNCC5EU.js.map} +0 -0
- /package/dist/{memory.js.map → openai-embedding-Z5I4K4CN.js.map} +0 -0
- /package/dist/{notes.js.map → perplexity-context-embedding-V5YUMXDR.js.map} +0 -0
- /package/dist/{openai-embedding-VQZCZQYT.js.map → perplexity-embedding-X2S72OAC.js.map} +0 -0
- /package/dist/{perplexity-context-embedding-KSVSZXMD.js.map → plugin-FF4Q34TI.js.map} +0 -0
- /package/dist/{perplexity-embedding-227WQY4R.js.map → qwen3-reranker-HVIQOLKS.js.map} +0 -0
- /package/dist/{qwen3-reranker-3MHEENT5.js.map → resolve-Q5D6HECY.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,48 +1,1063 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { a as ProgressCallback, B as BrainBankConfig, P as Plugin, C as Collection, S as StageProgressCallback, I as IndexResult, D as DocumentCollection, b as SearchResult, c as ContextOptions, d as CoEditSuggestion, e as IndexStats, R as ResolvedConfig, E as EmbeddingProvider, f as Reranker, V as VectorIndex, g as SearchHit, h as CodeChunk, i as Database, H as HNSWIndex, L as LearningPattern } from './base-3SNc_CeY.js';
|
|
3
|
-
export { j as CodeResult, k as CodeResultMetadata, l as CollectionAddOptions, m as CollectionItem, n as CollectionPlugin, o as CollectionResult, p as CollectionSearchOptions, q as CommitResult, r as CommitResultMetadata, s as DistilledStrategy, t as DocChunk, u as DocumentResult, v as DocumentResultMetadata, G as GitCommitRecord, w as IndexablePlugin, x as PatternResult, y as PatternResultMetadata, z as PluginContext, A as SearchResultType, F as SearchablePlugin, W as WatchablePlugin, J as isCodeResult, K as isCollectionResult, M as isCommitResult, N as isDocumentResult, O as isPatternResult, Q as matchResult } from './base-3SNc_CeY.js';
|
|
4
|
-
export { code } from './code.js';
|
|
5
|
-
export { git } from './git.js';
|
|
6
|
-
export { docs } from './docs.js';
|
|
7
|
-
export { memory } from './memory.js';
|
|
8
|
-
export { notes } from './notes.js';
|
|
9
|
-
import 'better-sqlite3';
|
|
10
2
|
|
|
11
3
|
/**
|
|
12
|
-
* BrainBank —
|
|
4
|
+
* BrainBank — Database Adapter Interface
|
|
13
5
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
6
|
+
* Abstract contract for database operations. All consumers depend on
|
|
7
|
+
* this interface — never on a concrete driver. The `SQLiteAdapter`
|
|
8
|
+
* is the built-in implementation; future adapters (LibSQL, Turso,
|
|
9
|
+
* PostgreSQL) implement the same contract.
|
|
16
10
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
11
|
+
* Phase 1: sync-first API matching the existing `better-sqlite3` usage.
|
|
12
|
+
* Async variants will be added when needed by async-native adapters.
|
|
13
|
+
*/
|
|
14
|
+
/** Result from mutating queries (INSERT / UPDATE / DELETE). */
|
|
15
|
+
interface ExecuteResult {
|
|
16
|
+
/** Row ID of the last inserted row. */
|
|
17
|
+
lastInsertRowid: number | bigint;
|
|
18
|
+
/** Number of rows changed by the statement. */
|
|
19
|
+
changes: number;
|
|
20
|
+
}
|
|
21
|
+
/** A prepared statement with typed query methods. */
|
|
22
|
+
interface PreparedStatement<T = unknown> {
|
|
23
|
+
/** Execute a query and return the first matching row, or `undefined`. */
|
|
24
|
+
get(...params: unknown[]): T | undefined;
|
|
25
|
+
/** Execute a query and return all matching rows. */
|
|
26
|
+
all(...params: unknown[]): T[];
|
|
27
|
+
/** Execute a mutating statement and return the result. */
|
|
28
|
+
run(...params: unknown[]): ExecuteResult;
|
|
29
|
+
/** Iterate over matching rows without loading them all into memory. */
|
|
30
|
+
iterate(...params: unknown[]): IterableIterator<T>;
|
|
31
|
+
}
|
|
32
|
+
/** Adapter capability flags — describes what the underlying engine supports. */
|
|
33
|
+
interface AdapterCapabilities {
|
|
34
|
+
/** Full-text search engine. */
|
|
35
|
+
fts: 'fts5' | 'tsvector' | 'none';
|
|
36
|
+
/** Upsert syntax dialect. */
|
|
37
|
+
upsert: 'or-replace' | 'on-conflict';
|
|
38
|
+
/** Native JSON column support. */
|
|
39
|
+
json: boolean;
|
|
40
|
+
/** Native vector column support (e.g. pgvector). */
|
|
41
|
+
vectors: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Database adapter interface.
|
|
45
|
+
*
|
|
46
|
+
* All BrainBank components depend on this contract instead of a
|
|
47
|
+
* concrete database driver. Keeps the door open for LibSQL, Turso,
|
|
48
|
+
* PostgreSQL, etc. without touching consumer code.
|
|
49
|
+
*/
|
|
50
|
+
interface DatabaseAdapter {
|
|
51
|
+
/** Prepare a reusable statement. */
|
|
52
|
+
prepare<T = unknown>(sql: string): PreparedStatement<T>;
|
|
53
|
+
/** Execute raw DDL / multi-statement SQL (no results). */
|
|
54
|
+
exec(sql: string): void;
|
|
55
|
+
/** Run `fn` inside a transaction. Auto-commits on success, auto-rollbacks on error. */
|
|
56
|
+
transaction<T>(fn: () => T): T;
|
|
57
|
+
/** Run a prepared statement on multiple rows inside a single transaction. */
|
|
58
|
+
batch<T extends unknown[]>(sql: string, rows: T[]): void;
|
|
59
|
+
/** Close the database and release resources. */
|
|
60
|
+
close(): void;
|
|
61
|
+
/** Engine capabilities (FTS, upsert dialect, etc.). */
|
|
62
|
+
readonly capabilities: AdapterCapabilities;
|
|
63
|
+
/**
|
|
64
|
+
* Escape hatch: access the underlying raw driver.
|
|
65
|
+
* Returns `undefined` for adapters that don't support raw access.
|
|
66
|
+
*
|
|
67
|
+
* @deprecated Use `DatabaseAdapter` methods instead. This exists
|
|
68
|
+
* only for gradual migration of plugins that depend on driver internals.
|
|
69
|
+
*/
|
|
70
|
+
raw<T = unknown>(): T | undefined;
|
|
71
|
+
}
|
|
72
|
+
interface KvDataRow {
|
|
73
|
+
id: number;
|
|
74
|
+
collection: string;
|
|
75
|
+
content: string;
|
|
76
|
+
meta_json: string;
|
|
77
|
+
tags_json: string;
|
|
78
|
+
expires_at: number | null;
|
|
79
|
+
created_at: number;
|
|
80
|
+
}
|
|
81
|
+
interface KvVectorRow {
|
|
82
|
+
data_id: number;
|
|
83
|
+
embedding: Buffer;
|
|
84
|
+
}
|
|
85
|
+
interface EmbeddingMetaRow {
|
|
86
|
+
value: string;
|
|
87
|
+
}
|
|
88
|
+
interface VectorRow {
|
|
89
|
+
id: number;
|
|
90
|
+
embedding: Buffer;
|
|
91
|
+
}
|
|
92
|
+
interface CountRow {
|
|
93
|
+
c: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* BrainBank — Plugin Migration System
|
|
20
98
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
99
|
+
* Per-plugin versioned schema migrations.
|
|
100
|
+
* Each plugin declares a schemaVersion + ordered migrations array.
|
|
101
|
+
* Core stores applied versions in `plugin_versions` table.
|
|
24
102
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* watcher.close(); // stop watching
|
|
103
|
+
* Plugins call `runPluginMigrations()` at the top of their `initialize()`.
|
|
104
|
+
* Migrations use `IF NOT EXISTS` so first run on an existing DB is a no-op.
|
|
28
105
|
*/
|
|
29
106
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
107
|
+
/** A single migration step. */
|
|
108
|
+
interface Migration {
|
|
109
|
+
/** Version this migration brings the schema to. */
|
|
110
|
+
version: number;
|
|
111
|
+
/** Apply the migration. Must be idempotent (use IF NOT EXISTS). */
|
|
112
|
+
up(adapter: DatabaseAdapter): void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Run pending migrations for a plugin.
|
|
116
|
+
* Skips migrations whose version <= stored version.
|
|
117
|
+
* Each migration runs in its own transaction.
|
|
118
|
+
*/
|
|
119
|
+
declare function runPluginMigrations(adapter: DatabaseAdapter, pluginName: string, schemaVersion: number, migrations: Migration[]): void;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* BrainBank — Incremental Tracker
|
|
123
|
+
*
|
|
124
|
+
* Standardized helper for plugins to detect add/update/delete during indexing.
|
|
125
|
+
* Uses a shared `plugin_tracking` table with per-plugin namespacing.
|
|
126
|
+
*
|
|
127
|
+
* Usage in a plugin:
|
|
128
|
+
* const tracker = ctx.createTracker(); // uses plugin name
|
|
129
|
+
* for (const file of files) {
|
|
130
|
+
* const hash = sha256(content);
|
|
131
|
+
* if (tracker.isUnchanged(file, hash)) { skipped++; continue; }
|
|
132
|
+
* indexFile(file, content);
|
|
133
|
+
* tracker.markIndexed(file, hash);
|
|
134
|
+
* }
|
|
135
|
+
* const orphans = tracker.findOrphans(new Set(files));
|
|
136
|
+
* for (const key of orphans) { removeData(key); tracker.remove(key); }
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/** Incremental index tracker — detects add/update/delete for plugin files. */
|
|
140
|
+
interface IncrementalTracker {
|
|
141
|
+
/** Check if a key's content is unchanged. Returns true if the hash matches (skip indexing). */
|
|
142
|
+
isUnchanged(key: string, contentHash: string): boolean;
|
|
143
|
+
/** Mark a key as successfully indexed with the given hash. Call after indexing completes. */
|
|
144
|
+
markIndexed(key: string, contentHash: string): void;
|
|
145
|
+
/** Find tracked keys that are NOT in the current set. Returns keys to delete. */
|
|
146
|
+
findOrphans(currentKeys: Set<string>): string[];
|
|
147
|
+
/** Remove tracking for a key. Call after cleaning up the key's data. */
|
|
148
|
+
remove(key: string): void;
|
|
149
|
+
/** Remove all tracking entries for this plugin. */
|
|
150
|
+
clear(): void;
|
|
151
|
+
}
|
|
152
|
+
/** Create an IncrementalTracker scoped to a plugin name. */
|
|
153
|
+
declare function createTracker(db: DatabaseAdapter, pluginName: string): IncrementalTracker;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* BrainBank — Collection
|
|
157
|
+
*
|
|
158
|
+
* Universal key-value store with vector + BM25 hybrid search.
|
|
159
|
+
* The foundation primitive — store anything, search semantically.
|
|
160
|
+
*
|
|
161
|
+
* const errors = brain.collection('debug_errors');
|
|
162
|
+
* await errors.add('Fixed null check in api handler', { file: 'api.ts' });
|
|
163
|
+
* const hits = await errors.search('null pointer');
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
interface CollectionItem {
|
|
167
|
+
id: number;
|
|
168
|
+
collection: string;
|
|
169
|
+
content: string;
|
|
170
|
+
metadata: Record<string, unknown>;
|
|
171
|
+
tags: string[];
|
|
172
|
+
createdAt: number;
|
|
173
|
+
expiresAt?: number;
|
|
174
|
+
score?: number;
|
|
175
|
+
}
|
|
176
|
+
interface CollectionSearchOptions {
|
|
177
|
+
/** Max results. Default: 5 */
|
|
178
|
+
k?: number;
|
|
179
|
+
/** Search mode. Default: 'hybrid' */
|
|
180
|
+
mode?: 'hybrid' | 'vector' | 'keyword';
|
|
181
|
+
/** Minimum score threshold. Default: 0.15 */
|
|
182
|
+
minScore?: number;
|
|
183
|
+
/** Filter by tags (item must have ALL specified tags). */
|
|
184
|
+
tags?: string[];
|
|
185
|
+
}
|
|
186
|
+
interface CollectionAddOptions {
|
|
187
|
+
/** Metadata key-value pairs. */
|
|
188
|
+
metadata?: Record<string, unknown>;
|
|
189
|
+
/** Tags for filtering. */
|
|
190
|
+
tags?: string[];
|
|
191
|
+
/** Time-to-live duration string (e.g. '7d', '24h', '30m'). */
|
|
192
|
+
ttl?: string;
|
|
193
|
+
}
|
|
194
|
+
declare class Collection {
|
|
195
|
+
private _name;
|
|
196
|
+
private _db;
|
|
197
|
+
private _embedding;
|
|
198
|
+
private _hnsw;
|
|
199
|
+
private _vecs;
|
|
200
|
+
private _reranker?;
|
|
201
|
+
constructor(_name: string, _db: DatabaseAdapter, _embedding: EmbeddingProvider, _hnsw: HNSWIndex, _vecs: Map<number, Float32Array>, _reranker?: Reranker | undefined);
|
|
202
|
+
/** Collection name. */
|
|
203
|
+
get name(): string;
|
|
204
|
+
/** Add an item. Returns its ID. */
|
|
205
|
+
add(content: string, options?: CollectionAddOptions | Record<string, unknown>): Promise<number>;
|
|
206
|
+
/** Update an item's content (re-embeds). Returns the new ID. */
|
|
207
|
+
update(id: number, content: string, options?: CollectionAddOptions): Promise<number>;
|
|
208
|
+
/** Add multiple items. Returns their IDs. */
|
|
209
|
+
addMany(items: {
|
|
210
|
+
content: string;
|
|
211
|
+
metadata?: Record<string, unknown>;
|
|
212
|
+
tags?: string[];
|
|
213
|
+
ttl?: string;
|
|
214
|
+
}[]): Promise<number[]>;
|
|
215
|
+
/** Search this collection. */
|
|
216
|
+
search(query: string, options?: CollectionSearchOptions): Promise<CollectionItem[]>;
|
|
217
|
+
/** Search and return results as SearchResult[] for use in hybrid search pipelines. */
|
|
218
|
+
searchAsResults(query: string, k: number): Promise<SearchResult[]>;
|
|
219
|
+
/** List items (newest first). */
|
|
220
|
+
list(options?: {
|
|
221
|
+
limit?: number;
|
|
222
|
+
offset?: number;
|
|
223
|
+
tags?: string[];
|
|
224
|
+
}): CollectionItem[];
|
|
225
|
+
/** Count items in this collection. */
|
|
226
|
+
count(): number;
|
|
227
|
+
/** Keep only the N most recent items, remove the rest. */
|
|
228
|
+
trim(options: {
|
|
229
|
+
keep: number;
|
|
230
|
+
}): Promise<{
|
|
231
|
+
removed: number;
|
|
232
|
+
}>;
|
|
233
|
+
/** Remove items older than a duration string (e.g. '30d', '12h'). */
|
|
234
|
+
prune(options: {
|
|
235
|
+
olderThan: string;
|
|
236
|
+
}): Promise<{
|
|
237
|
+
removed: number;
|
|
238
|
+
}>;
|
|
239
|
+
/** Remove a specific item by ID. */
|
|
240
|
+
remove(id: number): void;
|
|
241
|
+
/** Clear all items in this collection. */
|
|
242
|
+
clear(): void;
|
|
243
|
+
private _removeById;
|
|
244
|
+
private _searchVector;
|
|
245
|
+
/** Compute adaptive over-fetch multiplier based on collection density in shared HNSW. */
|
|
246
|
+
private _adaptiveSearchK;
|
|
247
|
+
private _searchBM25;
|
|
248
|
+
private _rowToItem;
|
|
249
|
+
/** Filter results by tags (item must have ALL specified tags). */
|
|
250
|
+
private _filterByTags;
|
|
251
|
+
/** Remove expired items (TTL). Called automatically on search/list. */
|
|
252
|
+
private _pruneExpired;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* BrainBank — Type Definitions
|
|
257
|
+
*
|
|
258
|
+
* All interfaces and types for the semantic knowledge bank.
|
|
259
|
+
*/
|
|
260
|
+
|
|
261
|
+
/** Public contract for a KV collection. Plugins depend on this interface, not the concrete class. */
|
|
262
|
+
interface ICollection {
|
|
263
|
+
/** Collection name. */
|
|
264
|
+
readonly name: string;
|
|
265
|
+
/** Add an item. Returns its ID. */
|
|
266
|
+
add(content: string, options?: CollectionAddOptions | Record<string, unknown>): Promise<number>;
|
|
267
|
+
/** Update an item's content (re-embeds). Returns the new ID. */
|
|
268
|
+
update(id: number, content: string, options?: CollectionAddOptions): Promise<number>;
|
|
269
|
+
/** Add multiple items. Returns their IDs. */
|
|
270
|
+
addMany(items: {
|
|
271
|
+
content: string;
|
|
272
|
+
metadata?: Record<string, unknown>;
|
|
273
|
+
tags?: string[];
|
|
274
|
+
ttl?: string;
|
|
275
|
+
}[]): Promise<number[]>;
|
|
276
|
+
/** Search this collection. */
|
|
277
|
+
search(query: string, options?: CollectionSearchOptions): Promise<CollectionItem[]>;
|
|
278
|
+
/** List items (newest first). */
|
|
279
|
+
list(options?: {
|
|
280
|
+
limit?: number;
|
|
281
|
+
offset?: number;
|
|
282
|
+
tags?: string[];
|
|
283
|
+
}): CollectionItem[];
|
|
284
|
+
/** Count items in this collection. */
|
|
285
|
+
count(): number;
|
|
286
|
+
/** Keep only the N most recent items. */
|
|
287
|
+
trim(options: {
|
|
288
|
+
keep: number;
|
|
289
|
+
}): Promise<{
|
|
290
|
+
removed: number;
|
|
291
|
+
}>;
|
|
292
|
+
/** Remove items older than a duration string. */
|
|
293
|
+
prune(options: {
|
|
294
|
+
olderThan: string;
|
|
295
|
+
}): Promise<{
|
|
296
|
+
removed: number;
|
|
297
|
+
}>;
|
|
298
|
+
/** Remove a specific item by ID. */
|
|
299
|
+
remove(id: number): void;
|
|
300
|
+
/** Clear all items in this collection. */
|
|
301
|
+
clear(): void;
|
|
302
|
+
}
|
|
303
|
+
interface BrainBankConfig {
|
|
304
|
+
/** Root path of the repository to index. Default: '.' */
|
|
305
|
+
repoPath?: string;
|
|
306
|
+
/** SQLite database path. Default: '.brainbank/data/brainbank.db' */
|
|
307
|
+
dbPath?: string;
|
|
308
|
+
/** Max git commits to index. Default: 500 */
|
|
309
|
+
gitDepth?: number;
|
|
310
|
+
/** Max file size in bytes to index. Default: 512_000 (500KB) */
|
|
311
|
+
maxFileSize?: number;
|
|
312
|
+
/** Max diff bytes per commit. Default: 8192 */
|
|
313
|
+
maxDiffBytes?: number;
|
|
314
|
+
/** HNSW M parameter (connections per node). Default: 16 */
|
|
315
|
+
hnswM?: number;
|
|
316
|
+
/** HNSW efConstruction (build-time candidates). Default: 200 */
|
|
317
|
+
hnswEfConstruction?: number;
|
|
318
|
+
/** HNSW efSearch (query-time candidates). Default: 50 */
|
|
319
|
+
hnswEfSearch?: number;
|
|
320
|
+
/** Embedding dimensions. Default: 384 */
|
|
321
|
+
embeddingDims?: number;
|
|
322
|
+
/** Max HNSW elements. Default: 2_000_000 */
|
|
323
|
+
maxElements?: number;
|
|
324
|
+
/** Custom embedding provider (default: local WASM model) */
|
|
325
|
+
embeddingProvider?: EmbeddingProvider;
|
|
326
|
+
/** Optional reranker for improved search quality */
|
|
327
|
+
reranker?: Reranker;
|
|
328
|
+
/** Optional LLM noise filter — drops irrelevant results before formatting */
|
|
329
|
+
pruner?: Pruner;
|
|
330
|
+
/** Optional LLM context expander — discovers additional relevant chunks after pruning */
|
|
331
|
+
expander?: Expander;
|
|
332
|
+
/** Port for optional webhook server (enables push-based watch plugins). */
|
|
333
|
+
webhookPort?: number;
|
|
334
|
+
/** Context field defaults from config.json "context" section. */
|
|
335
|
+
contextFields?: Record<string, unknown>;
|
|
336
|
+
}
|
|
337
|
+
interface ResolvedConfig {
|
|
338
|
+
repoPath: string;
|
|
339
|
+
dbPath: string;
|
|
340
|
+
gitDepth: number;
|
|
341
|
+
maxFileSize: number;
|
|
342
|
+
maxDiffBytes: number;
|
|
343
|
+
hnswM: number;
|
|
344
|
+
hnswEfConstruction: number;
|
|
345
|
+
hnswEfSearch: number;
|
|
346
|
+
embeddingDims: number;
|
|
347
|
+
maxElements: number;
|
|
348
|
+
embeddingProvider?: EmbeddingProvider;
|
|
349
|
+
reranker?: Reranker;
|
|
350
|
+
pruner?: Pruner;
|
|
351
|
+
expander?: Expander;
|
|
352
|
+
webhookPort?: number;
|
|
353
|
+
/** Context field defaults from config.json "context" section. */
|
|
354
|
+
contextFields?: Record<string, unknown>;
|
|
355
|
+
}
|
|
356
|
+
interface EmbeddingProvider {
|
|
357
|
+
/** Vector dimensions produced by this provider. */
|
|
358
|
+
readonly dims: number;
|
|
359
|
+
/** Embed a single text string. */
|
|
360
|
+
embed(text: string): Promise<Float32Array>;
|
|
361
|
+
/** Embed multiple texts (batch). */
|
|
362
|
+
embedBatch(texts: string[]): Promise<Float32Array[]>;
|
|
363
|
+
/** Release resources. */
|
|
364
|
+
close(): Promise<void>;
|
|
365
|
+
}
|
|
366
|
+
interface Reranker {
|
|
367
|
+
/**
|
|
368
|
+
* Score each document's relevance to the query.
|
|
369
|
+
* @param query - The search query
|
|
370
|
+
* @param documents - Document contents to rank
|
|
371
|
+
* @returns Relevance scores (0.0 - 1.0) in same order as documents
|
|
372
|
+
*/
|
|
373
|
+
rank(query: string, documents: string[]): Promise<number[]>;
|
|
374
|
+
/** Release resources (e.g. unload model). */
|
|
375
|
+
close?(): Promise<void>;
|
|
376
|
+
}
|
|
377
|
+
/** Item passed to the pruner for noise classification. */
|
|
378
|
+
interface PrunerItem {
|
|
379
|
+
/** Positional index (used to map back to SearchResult[]) */
|
|
380
|
+
id: number;
|
|
381
|
+
/** File path — primary signal for relevance */
|
|
382
|
+
filePath: string;
|
|
383
|
+
/** Trimmed content preview */
|
|
384
|
+
preview: string;
|
|
385
|
+
/** Chunk metadata (type, name, language, lines, etc.) */
|
|
386
|
+
metadata: Record<string, unknown>;
|
|
387
|
+
}
|
|
388
|
+
interface Pruner {
|
|
389
|
+
/**
|
|
390
|
+
* Filter noise from search results and order by semantic relevance.
|
|
391
|
+
* @param query - The search query
|
|
392
|
+
* @param items - Items to evaluate (filePath + metadata + trimmed preview)
|
|
393
|
+
* @returns Array of item IDs to KEEP, ordered by relevance to the query
|
|
394
|
+
* (most relevant first). Everything else is dropped.
|
|
395
|
+
*/
|
|
396
|
+
prune(query: string, items: PrunerItem[]): Promise<number[]>;
|
|
397
|
+
/** Release resources. */
|
|
398
|
+
close?(): Promise<void>;
|
|
399
|
+
}
|
|
400
|
+
/** Lightweight chunk descriptor for the expander manifest. */
|
|
401
|
+
interface ExpanderManifestItem {
|
|
402
|
+
/** Chunk ID (code_chunks.id) */
|
|
403
|
+
id: number;
|
|
404
|
+
/** File path */
|
|
405
|
+
filePath: string;
|
|
406
|
+
/** Chunk name (e.g. function/class name) */
|
|
407
|
+
name: string;
|
|
408
|
+
/** Chunk type (function, class, method, etc.) */
|
|
409
|
+
chunkType: string;
|
|
410
|
+
/** Line range (e.g. "L45-L89") */
|
|
411
|
+
lines: string;
|
|
412
|
+
}
|
|
413
|
+
/** Result from the expander: chunk IDs to add + optional free-text note. */
|
|
414
|
+
interface ExpanderResult {
|
|
415
|
+
/** Additional chunk IDs to splice into results. */
|
|
416
|
+
ids: number[];
|
|
417
|
+
/** Brief observation for the agent (e.g. "file X not found", "module Y is deprecated"). */
|
|
418
|
+
note?: string;
|
|
419
|
+
}
|
|
420
|
+
interface Expander {
|
|
421
|
+
/**
|
|
422
|
+
* Given a task and a manifest of available chunks,
|
|
423
|
+
* return additional chunk IDs + an optional contextual note.
|
|
424
|
+
* Runs after pruning. Fail-open: errors return empty result.
|
|
425
|
+
*
|
|
426
|
+
* @param query - The search query / task description
|
|
427
|
+
* @param currentIds - IDs of chunks already in results (from search + prune)
|
|
428
|
+
* @param manifest - All available chunks (lightweight descriptors)
|
|
429
|
+
* @returns Chunk IDs to add + optional note
|
|
430
|
+
*/
|
|
431
|
+
expand(query: string, currentIds: number[], manifest: ExpanderManifestItem[]): Promise<ExpanderResult>;
|
|
432
|
+
/** Release resources. */
|
|
433
|
+
close?(): Promise<void>;
|
|
434
|
+
}
|
|
435
|
+
interface SearchHit {
|
|
436
|
+
id: number;
|
|
437
|
+
score: number;
|
|
438
|
+
}
|
|
439
|
+
interface VectorIndex {
|
|
440
|
+
/** Initialize the index. Must be called before add/search. */
|
|
441
|
+
init(): Promise<this>;
|
|
442
|
+
/** Add a vector with an integer ID. Idempotent: duplicate IDs are skipped. */
|
|
443
|
+
add(vector: Float32Array, id: number): void;
|
|
444
|
+
/** Mark a vector as deleted so it no longer appears in searches. */
|
|
445
|
+
remove(id: number): void;
|
|
446
|
+
/** Search for k nearest neighbors. */
|
|
447
|
+
search(query: Float32Array, k: number): SearchHit[];
|
|
448
|
+
/** Clear all vectors and reset to empty state. */
|
|
449
|
+
reinit(): void;
|
|
450
|
+
/** Number of vectors in the index. */
|
|
451
|
+
readonly size: number;
|
|
452
|
+
}
|
|
453
|
+
interface CodeChunk {
|
|
454
|
+
/** Auto-incremented DB id (set after insert) */
|
|
455
|
+
id?: number;
|
|
456
|
+
/** Relative file path from repo root */
|
|
457
|
+
filePath: string;
|
|
458
|
+
/** Chunk type: 'file' | 'function' | 'class' | 'block' */
|
|
459
|
+
chunkType: string;
|
|
460
|
+
/** Function/class name (if detected) */
|
|
461
|
+
name?: string;
|
|
462
|
+
/** Start line (1-indexed) */
|
|
463
|
+
startLine: number;
|
|
464
|
+
/** End line (1-indexed, inclusive) */
|
|
465
|
+
endLine: number;
|
|
466
|
+
/** Raw content of the chunk */
|
|
467
|
+
content: string;
|
|
468
|
+
/** Language identifier */
|
|
469
|
+
language: string;
|
|
470
|
+
}
|
|
471
|
+
interface GitCommitRecord {
|
|
472
|
+
id?: number;
|
|
473
|
+
hash: string;
|
|
474
|
+
shortHash: string;
|
|
475
|
+
message: string;
|
|
476
|
+
author: string;
|
|
477
|
+
date: string;
|
|
478
|
+
timestamp: number;
|
|
479
|
+
filesChanged: string[];
|
|
480
|
+
diff?: string;
|
|
481
|
+
additions: number;
|
|
482
|
+
deletions: number;
|
|
483
|
+
isMerge: boolean;
|
|
484
|
+
}
|
|
485
|
+
type SearchResultType = 'code' | 'commit' | 'document' | 'collection';
|
|
486
|
+
interface CodeResultMetadata {
|
|
487
|
+
/** Database chunk ID (used by call graph annotations). */
|
|
488
|
+
id?: number;
|
|
489
|
+
/** File path (may duplicate CodeResult.filePath for metadata-only access). */
|
|
490
|
+
filePath?: string;
|
|
491
|
+
/** Adjacent chunk IDs from the same file (used by context expansion). */
|
|
492
|
+
chunkIds?: number[];
|
|
493
|
+
chunkType: string;
|
|
494
|
+
name?: string;
|
|
495
|
+
startLine: number;
|
|
496
|
+
endLine: number;
|
|
497
|
+
language: string;
|
|
498
|
+
searchType?: string;
|
|
499
|
+
rrfScore?: number;
|
|
500
|
+
}
|
|
501
|
+
interface CommitResultMetadata {
|
|
502
|
+
hash: string;
|
|
503
|
+
shortHash: string;
|
|
504
|
+
author: string;
|
|
505
|
+
date: string;
|
|
506
|
+
files: string[];
|
|
507
|
+
additions?: number;
|
|
508
|
+
deletions?: number;
|
|
509
|
+
diff?: string;
|
|
510
|
+
searchType?: string;
|
|
511
|
+
rrfScore?: number;
|
|
512
|
+
}
|
|
513
|
+
interface DocumentResultMetadata {
|
|
514
|
+
collection?: string;
|
|
515
|
+
title?: string;
|
|
516
|
+
seq?: number;
|
|
517
|
+
path?: string;
|
|
518
|
+
searchType?: string;
|
|
519
|
+
/** Internal chunk ID used by hybrid search to map fused results. */
|
|
520
|
+
chunkId?: number;
|
|
521
|
+
rrfScore?: number;
|
|
522
|
+
}
|
|
523
|
+
interface CodeResult {
|
|
524
|
+
type: 'code';
|
|
525
|
+
score: number;
|
|
526
|
+
filePath: string;
|
|
527
|
+
content: string;
|
|
528
|
+
context?: string;
|
|
529
|
+
metadata: CodeResultMetadata;
|
|
530
|
+
}
|
|
531
|
+
interface CommitResult {
|
|
532
|
+
type: 'commit';
|
|
533
|
+
score: number;
|
|
534
|
+
filePath?: string;
|
|
535
|
+
content: string;
|
|
536
|
+
context?: string;
|
|
537
|
+
metadata: CommitResultMetadata;
|
|
538
|
+
}
|
|
539
|
+
interface DocumentResult {
|
|
540
|
+
type: 'document';
|
|
541
|
+
score: number;
|
|
542
|
+
filePath: string;
|
|
543
|
+
content: string;
|
|
544
|
+
context?: string;
|
|
545
|
+
metadata: DocumentResultMetadata;
|
|
546
|
+
}
|
|
547
|
+
interface CollectionResultMetadata {
|
|
548
|
+
id?: number;
|
|
549
|
+
collection?: string;
|
|
550
|
+
rrfScore?: number;
|
|
551
|
+
[key: string]: unknown;
|
|
552
|
+
}
|
|
553
|
+
interface CollectionResult {
|
|
554
|
+
type: 'collection';
|
|
555
|
+
score: number;
|
|
556
|
+
filePath?: string;
|
|
557
|
+
content: string;
|
|
558
|
+
context?: string;
|
|
559
|
+
metadata: CollectionResultMetadata;
|
|
560
|
+
}
|
|
561
|
+
type SearchResult = CodeResult | CommitResult | DocumentResult | CollectionResult;
|
|
562
|
+
/** Narrow a SearchResult to CodeResult. */
|
|
563
|
+
declare function isCodeResult(r: SearchResult): r is CodeResult;
|
|
564
|
+
/** Narrow a SearchResult to CommitResult. */
|
|
565
|
+
declare function isCommitResult(r: SearchResult): r is CommitResult;
|
|
566
|
+
/** Narrow a SearchResult to DocumentResult. */
|
|
567
|
+
declare function isDocumentResult(r: SearchResult): r is DocumentResult;
|
|
568
|
+
/** Narrow a SearchResult to CollectionResult. */
|
|
569
|
+
declare function isCollectionResult(r: SearchResult): r is CollectionResult;
|
|
570
|
+
type MatchHandlers<T> = {
|
|
571
|
+
code?: (r: CodeResult) => T;
|
|
572
|
+
commit?: (r: CommitResult) => T;
|
|
573
|
+
document?: (r: DocumentResult) => T;
|
|
574
|
+
collection?: (r: CollectionResult) => T;
|
|
575
|
+
_?: (r: SearchResult) => T;
|
|
576
|
+
};
|
|
577
|
+
/**
|
|
578
|
+
* Pattern-match on SearchResult type. Calls the matching handler
|
|
579
|
+
* or the `_` fallback. Returns undefined if no handler matches.
|
|
580
|
+
*/
|
|
581
|
+
declare function matchResult<T>(result: SearchResult, handlers: MatchHandlers<T>): T | undefined;
|
|
582
|
+
interface ContextOptions {
|
|
583
|
+
/** Per-source result limits. Built-in: 'code', 'git'. Default: { code: 6, git: 5 } */
|
|
584
|
+
sources?: Record<string, number>;
|
|
585
|
+
/** Files the agent is about to modify (improves co-edit suggestions) */
|
|
586
|
+
affectedFiles?: string[];
|
|
587
|
+
/** Minimum similarity score threshold. Default: 0.25 */
|
|
588
|
+
minScore?: number;
|
|
589
|
+
/** Use MMR for diversity. Default: true */
|
|
590
|
+
useMMR?: boolean;
|
|
591
|
+
/** MMR lambda (0 = diversity, 1 = relevance). Default: 0.7 */
|
|
592
|
+
mmrLambda?: number;
|
|
593
|
+
/** Filter results to files under this path prefix (e.g. 'src/services/'). */
|
|
594
|
+
pathPrefix?: string;
|
|
595
|
+
/** File paths to exclude from results (e.g. files already returned in a previous query). */
|
|
596
|
+
excludeFiles?: Set<string>;
|
|
597
|
+
/** Optional per-request pruner override (e.g. HaikuPruner for LLM noise filtering). */
|
|
598
|
+
pruner?: Pruner;
|
|
599
|
+
/** Caller origin for debug logging. */
|
|
600
|
+
source?: 'cli' | 'mcp' | 'daemon' | 'api';
|
|
601
|
+
/**
|
|
602
|
+
* Context field overrides. Merged on top of config.json "context" defaults.
|
|
603
|
+
* Plugin-defined fields like `lines`, `callTree`, `symbols`, `compact`, `imports`.
|
|
604
|
+
* Example: `{ lines: true, callTree: { depth: 3 }, symbols: true }`
|
|
605
|
+
*/
|
|
606
|
+
fields?: Record<string, unknown>;
|
|
607
|
+
}
|
|
608
|
+
interface DocumentCollection {
|
|
609
|
+
/** Collection name (e.g. 'notes', 'docs') */
|
|
610
|
+
name: string;
|
|
611
|
+
/** Directory path to index */
|
|
612
|
+
path: string;
|
|
613
|
+
/** Glob pattern for files (default: all markdown) */
|
|
614
|
+
pattern?: string;
|
|
615
|
+
/** Glob patterns to ignore */
|
|
616
|
+
ignore?: string[];
|
|
617
|
+
/** Context description for this collection */
|
|
618
|
+
context?: string;
|
|
619
|
+
}
|
|
620
|
+
interface DocChunk {
|
|
621
|
+
id?: number;
|
|
622
|
+
/** Collection name */
|
|
623
|
+
collection: string;
|
|
624
|
+
/** Relative file path within the collection */
|
|
625
|
+
filePath: string;
|
|
626
|
+
/** Document title (first heading or filename) */
|
|
627
|
+
title: string;
|
|
628
|
+
/** Chunk content */
|
|
629
|
+
content: string;
|
|
630
|
+
/** Chunk sequence within the document (0, 1, 2...) */
|
|
631
|
+
seq: number;
|
|
632
|
+
/** Character position in original document */
|
|
633
|
+
pos: number;
|
|
634
|
+
/** Content hash for incremental updates */
|
|
635
|
+
contentHash: string;
|
|
636
|
+
}
|
|
637
|
+
/** File-level progress (used by indexers). */
|
|
638
|
+
type ProgressCallback = (file: string, current: number, total: number) => void;
|
|
639
|
+
/** Stage-level progress (used by BrainBank.index() orchestrator). */
|
|
640
|
+
type StageProgressCallback = (stage: string, message: string) => void;
|
|
641
|
+
interface IndexResult {
|
|
642
|
+
indexed: number;
|
|
643
|
+
skipped: number;
|
|
644
|
+
chunks?: number;
|
|
645
|
+
removed?: number;
|
|
646
|
+
}
|
|
647
|
+
interface CoEditSuggestion {
|
|
648
|
+
file: string;
|
|
649
|
+
count: number;
|
|
650
|
+
}
|
|
651
|
+
/** Generalized watch event — works for files, APIs, webhooks. */
|
|
652
|
+
interface WatchEvent {
|
|
653
|
+
/** Event type. 'sync' is for batch/poll sources that don't distinguish CRUD. */
|
|
654
|
+
type: 'create' | 'update' | 'delete' | 'sync';
|
|
655
|
+
/** Unique ID of the changed item (file path, PR#123, PROJ-456, etc.). */
|
|
656
|
+
sourceId: string;
|
|
657
|
+
/** Source descriptor (e.g. 'file', 'github:pr', 'jira:card'). */
|
|
658
|
+
sourceName: string;
|
|
659
|
+
/** Optional raw payload to avoid re-fetching. */
|
|
660
|
+
payload?: unknown;
|
|
661
|
+
}
|
|
662
|
+
/** Callback that plugins invoke when they detect a change. */
|
|
663
|
+
type WatchEventHandler = (event: WatchEvent) => void;
|
|
664
|
+
/** Lifecycle handle returned by WatchablePlugin.watch(). */
|
|
665
|
+
interface WatchHandle {
|
|
666
|
+
/** Stop watching and release resources. */
|
|
667
|
+
stop(): Promise<void>;
|
|
668
|
+
/** Whether the watcher is still active. */
|
|
669
|
+
readonly active: boolean;
|
|
670
|
+
}
|
|
671
|
+
/** Optional hints from plugin to core — debounce, batching, priority. */
|
|
672
|
+
interface WatchConfig {
|
|
673
|
+
/** Debounce interval in ms. 0 = process immediately. Default: inherited from WatchOptions. */
|
|
34
674
|
debounceMs?: number;
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
675
|
+
/** Max events to batch before triggering re-index. Default: unlimited. */
|
|
676
|
+
batchSize?: number;
|
|
677
|
+
/** Processing priority. Default: 'realtime'. */
|
|
678
|
+
priority?: 'realtime' | 'background';
|
|
39
679
|
}
|
|
40
|
-
|
|
41
|
-
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* BrainBank — HNSW Vector Index
|
|
683
|
+
*
|
|
684
|
+
* Wraps hnswlib-node for O(log n) approximate nearest neighbor search.
|
|
685
|
+
* M=16 connections, ef=200 construction, ef=50 search by default.
|
|
686
|
+
* 150x faster than brute force at 1M vectors.
|
|
687
|
+
*
|
|
688
|
+
* Supports disk persistence: save(path) / tryLoad(path, count)
|
|
689
|
+
* to skip costly vector-by-vector rebuild on startup.
|
|
690
|
+
*/
|
|
691
|
+
|
|
692
|
+
declare class HNSWIndex implements VectorIndex {
|
|
693
|
+
private _dims;
|
|
694
|
+
private _maxElements;
|
|
695
|
+
private _M;
|
|
696
|
+
private _efConstruction;
|
|
697
|
+
private _efSearch;
|
|
698
|
+
private _index;
|
|
699
|
+
private _lib;
|
|
700
|
+
private _ids;
|
|
701
|
+
constructor(_dims: number, _maxElements?: number, _M?: number, _efConstruction?: number, _efSearch?: number);
|
|
702
|
+
/**
|
|
703
|
+
* Initialize the HNSW index.
|
|
704
|
+
* Must be called before add/search.
|
|
705
|
+
*/
|
|
706
|
+
init(): Promise<this>;
|
|
707
|
+
/**
|
|
708
|
+
* Reinitialize the index in-place, clearing all vectors.
|
|
709
|
+
* Required after reembed or full re-index to avoid duplicate IDs.
|
|
710
|
+
* init() must have been called first.
|
|
711
|
+
*/
|
|
712
|
+
reinit(): void;
|
|
713
|
+
private _createIndex;
|
|
714
|
+
/** Maximum capacity of this index. */
|
|
715
|
+
get maxElements(): number;
|
|
716
|
+
/**
|
|
717
|
+
* Add a vector with an integer ID.
|
|
718
|
+
* The vector should be pre-normalized for cosine distance.
|
|
719
|
+
*/
|
|
720
|
+
add(vector: Float32Array, id: number): void;
|
|
721
|
+
/**
|
|
722
|
+
* Mark a vector as deleted so it no longer appears in searches.
|
|
723
|
+
* Uses hnswlib-node markDelete under the hood.
|
|
724
|
+
* Safe to call with an ID that doesn't exist.
|
|
725
|
+
*/
|
|
726
|
+
remove(id: number): void;
|
|
727
|
+
/**
|
|
728
|
+
* Search for the k nearest neighbors.
|
|
729
|
+
* Returns results sorted by score (highest first).
|
|
730
|
+
* Score is 1 - cosine_distance (1.0 = identical).
|
|
731
|
+
*/
|
|
732
|
+
search(query: Float32Array, k: number): SearchHit[];
|
|
733
|
+
/** Number of vectors in the index. */
|
|
734
|
+
get size(): number;
|
|
735
|
+
/**
|
|
736
|
+
* Save the HNSW graph to disk.
|
|
737
|
+
* The file can be loaded later with tryLoad() to skip vector-by-vector insertion.
|
|
738
|
+
*/
|
|
739
|
+
save(path: string): void;
|
|
740
|
+
/**
|
|
741
|
+
* Try to load a previously saved HNSW index from disk.
|
|
742
|
+
* Returns true if loaded successfully, false if stale or missing.
|
|
743
|
+
* @param path File path to the saved index
|
|
744
|
+
* @param expectedCount Expected number of vectors (from SQLite) — used to detect staleness
|
|
745
|
+
*/
|
|
746
|
+
tryLoad(path: string, expectedCount: number): boolean;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* BrainBank — Search Types
|
|
751
|
+
*
|
|
752
|
+
* Shared interface for all search strategies.
|
|
753
|
+
* Implement SearchStrategy to add a new search backend.
|
|
754
|
+
*/
|
|
755
|
+
|
|
756
|
+
/** Any search implementation follows this shape. */
|
|
757
|
+
interface SearchStrategy {
|
|
758
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
759
|
+
/** Rebuild internal indices (e.g. FTS5). Optional. */
|
|
760
|
+
rebuild?(): void;
|
|
761
|
+
}
|
|
762
|
+
/** Pre-embedded vector search for a single domain (code, git, etc.). */
|
|
763
|
+
interface DomainVectorSearch {
|
|
764
|
+
/** Search using a pre-computed query vector. Optional queryText enables BM25 fusion. */
|
|
765
|
+
search(queryVec: Float32Array, k: number, minScore: number, useMMR?: boolean, mmrLambda?: number, queryText?: string): SearchResult[];
|
|
766
|
+
}
|
|
767
|
+
interface SearchOptions {
|
|
768
|
+
/** Per-source result limits. Built-in: 'code', 'git', 'memory'. Any other key = custom plugin or KV collection. */
|
|
769
|
+
sources?: Record<string, number>;
|
|
770
|
+
/** Minimum similarity score. Default: 0.25 */
|
|
771
|
+
minScore?: number;
|
|
772
|
+
/** Use MMR for diversity. Default: true */
|
|
773
|
+
useMMR?: boolean;
|
|
774
|
+
/** MMR lambda. Default: 0.7 */
|
|
775
|
+
mmrLambda?: number;
|
|
776
|
+
/** Caller origin for debug logging. */
|
|
777
|
+
source?: 'cli' | 'mcp' | 'daemon' | 'api';
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* BrainBank — Webhook Server
|
|
782
|
+
*
|
|
783
|
+
* Optional shared HTTP server for push-based watch plugins (e.g. Jira, GitHub).
|
|
784
|
+
* Opt-in: only created when `new BrainBank({ webhookPort: 4242 })` is configured.
|
|
785
|
+
*
|
|
786
|
+
* Plugins register routes during `watch()`:
|
|
787
|
+
* ctx.webhookServer?.register('jira', '/jira/webhook', handler);
|
|
788
|
+
*
|
|
789
|
+
* Each plugin gets its own path namespace. Unregistering cleans up the route.
|
|
790
|
+
*/
|
|
791
|
+
/** Handler for incoming webhook payloads. */
|
|
792
|
+
type WebhookHandler = (body: unknown) => void;
|
|
793
|
+
/** Shared HTTP server for push-based watch plugins. */
|
|
794
|
+
declare class WebhookServer {
|
|
795
|
+
private _server;
|
|
796
|
+
private _routes;
|
|
797
|
+
private _listening;
|
|
798
|
+
/** Start listening on the specified port. */
|
|
799
|
+
listen(port: number): void;
|
|
800
|
+
/** Register a webhook route for a plugin. */
|
|
801
|
+
register(pluginName: string, path: string, handler: WebhookHandler): void;
|
|
802
|
+
/** Remove all routes for a plugin. */
|
|
803
|
+
unregister(pluginName: string): void;
|
|
804
|
+
/** Stop the server and clear all routes. */
|
|
42
805
|
close(): void;
|
|
43
|
-
/** Whether the
|
|
44
|
-
|
|
806
|
+
/** Whether the server is currently listening. */
|
|
807
|
+
get active(): boolean;
|
|
808
|
+
/** Route incoming POST requests to the matching handler. */
|
|
809
|
+
private _handleRequest;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* BrainBank — Plugin System
|
|
814
|
+
*
|
|
815
|
+
* Plugins are pluggable strategies that scan external data sources
|
|
816
|
+
* and push content into BrainBank. Built-in plugins handle code,
|
|
817
|
+
* git, and docs. Third-party frameworks (LangChain, etc.)
|
|
818
|
+
* can implement custom plugins.
|
|
819
|
+
*
|
|
820
|
+
* import { BrainBank } from 'brainbank';
|
|
821
|
+
* import { code } from 'brainbank/indexers/code';
|
|
822
|
+
*
|
|
823
|
+
* const brain = new BrainBank()
|
|
824
|
+
* .use(code({ repoPath: '.' }));
|
|
825
|
+
*/
|
|
826
|
+
|
|
827
|
+
interface PluginContext {
|
|
828
|
+
/** Database adapter (shared across all plugins). */
|
|
829
|
+
db: DatabaseAdapter;
|
|
830
|
+
/** Embedding provider (shared). */
|
|
831
|
+
embedding: EmbeddingProvider;
|
|
832
|
+
/** Resolved BrainBank config. */
|
|
833
|
+
config: ResolvedConfig;
|
|
834
|
+
/**
|
|
835
|
+
* Create and initialize an HNSW index.
|
|
836
|
+
* Pass `name` to enable disk persistence (recommended).
|
|
837
|
+
*
|
|
838
|
+
* **Private vs shared:** Use `getOrCreateSharedHnsw()` for indexes that should be
|
|
839
|
+
* part of the composite search (code, git) and persisted across restarts.
|
|
840
|
+
* Use `createHnsw()` for plugin-local indexes that don't participate in the
|
|
841
|
+
* main search pipeline (e.g. internal similarity lookups).
|
|
842
|
+
*/
|
|
843
|
+
createHnsw(maxElements?: number, dims?: number, name?: string): Promise<HNSWIndex>;
|
|
844
|
+
/** Load existing vectors from a SQLite vectors table into an HNSW index + cache. */
|
|
845
|
+
loadVectors(table: string, idCol: string, hnsw: HNSWIndex, cache: Map<number, Float32Array>): void;
|
|
846
|
+
/**
|
|
847
|
+
* Get or create a shared HNSW index by key.
|
|
848
|
+
*
|
|
849
|
+
* **HNSW sharing strategies:**
|
|
850
|
+
* The `type` key determines sharing behavior. Two plugins that pass the
|
|
851
|
+
* same key share one HNSW index; different keys get separate indexes.
|
|
852
|
+
*
|
|
853
|
+
* | Plugin type | Key passed | Sharing behavior |
|
|
854
|
+
* |------------|------------------|------------------------------------------|
|
|
855
|
+
* | git | `'git'` | All `git:*` repos share one HNSW |
|
|
856
|
+
* | docs | `'docs'` | All docs share one HNSW |
|
|
857
|
+
* | code | `this.name` | Each `code:*` repo gets its own HNSW |
|
|
858
|
+
*
|
|
859
|
+
* **Rule of thumb:**
|
|
860
|
+
* - Same key = shared index (saves memory, single search covers all)
|
|
861
|
+
* - Plugin name as key = per-repo index (avoids cross-repo noise)
|
|
862
|
+
*
|
|
863
|
+
* The key is also used for hot-reload (`ensureFresh`) and disk persistence
|
|
864
|
+
* (`hnsw-<key>.index`), so it must match the key used in `bumpVersion()`.
|
|
865
|
+
*/
|
|
866
|
+
getOrCreateSharedHnsw(type: string, maxElements?: number, dims?: number): Promise<{
|
|
867
|
+
hnsw: HNSWIndex;
|
|
868
|
+
vecCache: Map<number, Float32Array>;
|
|
869
|
+
isNew: boolean;
|
|
870
|
+
}>;
|
|
871
|
+
/** Get or create a dynamic collection. */
|
|
872
|
+
collection(name: string): ICollection;
|
|
873
|
+
/**
|
|
874
|
+
* Create an incremental tracker scoped to this plugin.
|
|
875
|
+
* Provides `isUnchanged`, `markIndexed`, `findOrphans`, `remove`, `clear`
|
|
876
|
+
* for standardized add/update/delete detection during indexing.
|
|
877
|
+
*/
|
|
878
|
+
createTracker(): IncrementalTracker;
|
|
879
|
+
/** Optional webhook server for push-based watch plugins. undefined if not configured. */
|
|
880
|
+
webhookServer?: WebhookServer;
|
|
881
|
+
}
|
|
882
|
+
interface Plugin {
|
|
883
|
+
/** Unique plugin name (e.g. 'code', 'git', 'docs'). */
|
|
884
|
+
readonly name: string;
|
|
885
|
+
/** Initialize the plugin (create HNSW, load vectors, etc.). */
|
|
886
|
+
initialize(ctx: PluginContext): Promise<void>;
|
|
887
|
+
/** Return stats for this plugin. */
|
|
888
|
+
stats?(): Record<string, number | string>;
|
|
889
|
+
/** Clean up resources. */
|
|
890
|
+
close?(): void;
|
|
891
|
+
}
|
|
892
|
+
/** Options accepted by IndexablePlugin.index(). */
|
|
893
|
+
interface IndexOptions {
|
|
894
|
+
forceReindex?: boolean;
|
|
895
|
+
depth?: number;
|
|
896
|
+
onProgress?: ProgressCallback;
|
|
897
|
+
}
|
|
898
|
+
/** Plugins that can scan and index content (code, git). */
|
|
899
|
+
interface IndexablePlugin extends Plugin {
|
|
900
|
+
index(options?: IndexOptions): Promise<IndexResult>;
|
|
901
|
+
/** Incremental: re-index only specific items by ID. Falls back to index() if not implemented. */
|
|
902
|
+
indexItems?(ids: string[]): Promise<IndexResult>;
|
|
903
|
+
}
|
|
904
|
+
/** Plugins that can search indexed content (docs). */
|
|
905
|
+
interface SearchablePlugin extends Plugin {
|
|
906
|
+
search(query: string, options?: Record<string, unknown>): Promise<SearchResult[]>;
|
|
907
|
+
}
|
|
908
|
+
/** Plugins that can watch their own data source for changes. */
|
|
909
|
+
interface WatchablePlugin extends Plugin {
|
|
910
|
+
/** Start watching. Plugin controls how (fs.watch, polling, webhook, etc.). */
|
|
911
|
+
watch(onEvent: WatchEventHandler): WatchHandle;
|
|
912
|
+
/** Optional hints for the core (debounce, batching, priority). */
|
|
913
|
+
watchConfig?(): WatchConfig;
|
|
914
|
+
}
|
|
915
|
+
/** Check if a plugin can scan/index content. */
|
|
916
|
+
declare function isIndexable(i: Plugin): i is IndexablePlugin;
|
|
917
|
+
/** Check if a plugin can search content. */
|
|
918
|
+
declare function isSearchable(i: Plugin): i is SearchablePlugin;
|
|
919
|
+
/** Check if a plugin can watch its own data source. */
|
|
920
|
+
declare function isWatchable(i: Plugin): i is WatchablePlugin;
|
|
921
|
+
/** Path-specific context metadata for document collections. */
|
|
922
|
+
interface PathContext {
|
|
923
|
+
collection: string;
|
|
924
|
+
path: string;
|
|
925
|
+
context: string;
|
|
926
|
+
}
|
|
927
|
+
/** Plugins that manage document collections (docs). */
|
|
928
|
+
interface DocsPlugin extends SearchablePlugin {
|
|
929
|
+
addCollection(collection: DocumentCollection): void;
|
|
930
|
+
removeCollection(name: string): void;
|
|
931
|
+
listCollections(): DocumentCollection[];
|
|
932
|
+
indexDocs(options?: {
|
|
933
|
+
onProgress?: (collection: string, file: string, current: number, total: number) => void;
|
|
934
|
+
}): Promise<Record<string, {
|
|
935
|
+
indexed: number;
|
|
936
|
+
skipped: number;
|
|
937
|
+
removed: number;
|
|
938
|
+
chunks: number;
|
|
939
|
+
}>>;
|
|
940
|
+
addContext(collection: string, path: string, context: string): void;
|
|
941
|
+
listContexts(): PathContext[];
|
|
942
|
+
}
|
|
943
|
+
/** Check if a plugin manages document collections. */
|
|
944
|
+
declare function isDocsPlugin(i: Plugin): i is DocsPlugin;
|
|
945
|
+
/** Plugin that provides co-edit suggestions (e.g. git). */
|
|
946
|
+
interface CoEditPlugin extends Plugin {
|
|
947
|
+
coEdits: {
|
|
948
|
+
suggest(filePath: string, limit: number): {
|
|
949
|
+
file: string;
|
|
950
|
+
count: number;
|
|
951
|
+
}[];
|
|
952
|
+
};
|
|
45
953
|
}
|
|
954
|
+
/** Check if a plugin provides co-edit suggestions. */
|
|
955
|
+
declare function isCoEditPlugin(p: Plugin): p is CoEditPlugin;
|
|
956
|
+
/** Table descriptor for re-embedding — maps text rows to vector BLOBs. */
|
|
957
|
+
interface ReembedTable {
|
|
958
|
+
/** Human-readable name (for progress). */
|
|
959
|
+
name: string;
|
|
960
|
+
/** Table with text content. */
|
|
961
|
+
textTable: string;
|
|
962
|
+
/** Table with vector BLOBs. */
|
|
963
|
+
vectorTable: string;
|
|
964
|
+
/** PK column in text table. */
|
|
965
|
+
idColumn: string;
|
|
966
|
+
/** FK column in vector table. */
|
|
967
|
+
fkColumn: string;
|
|
968
|
+
/** Build the embedding text from a DB row. */
|
|
969
|
+
textBuilder: (row: Record<string, unknown>) => string;
|
|
970
|
+
}
|
|
971
|
+
/** Plugins that own vector tables and can rebuild embedding text from DB rows. */
|
|
972
|
+
interface ReembeddablePlugin extends Plugin {
|
|
973
|
+
/** Table descriptor for re-embedding. */
|
|
974
|
+
reembedConfig(): ReembedTable;
|
|
975
|
+
}
|
|
976
|
+
/** Check if a plugin supports re-embedding. */
|
|
977
|
+
declare function isReembeddable(p: Plugin): p is ReembeddablePlugin;
|
|
978
|
+
/** Plugin that provides a domain-specific vector search strategy. */
|
|
979
|
+
interface VectorSearchPlugin extends Plugin {
|
|
980
|
+
/** Create the domain vector search (called during SearchAPI wiring). */
|
|
981
|
+
createVectorSearch(): DomainVectorSearch | undefined;
|
|
982
|
+
}
|
|
983
|
+
/** Check if a plugin provides a domain vector search. */
|
|
984
|
+
declare function isVectorSearchPlugin(p: Plugin): p is VectorSearchPlugin;
|
|
985
|
+
/** Describes a configurable context field that a plugin supports. */
|
|
986
|
+
interface ContextFieldDef {
|
|
987
|
+
/** Field name (e.g. 'lines', 'callTree', 'symbols'). Must be unique per plugin. */
|
|
988
|
+
name: string;
|
|
989
|
+
/** Accepted value type. 'object' allows nested config like `{ depth: 3 }`. */
|
|
990
|
+
type: 'boolean' | 'number' | 'object';
|
|
991
|
+
/** Default value (used when not specified in config or query). */
|
|
992
|
+
default: unknown;
|
|
993
|
+
/** Human-readable description for CLI --help and MCP tool descriptions. */
|
|
994
|
+
description: string;
|
|
995
|
+
}
|
|
996
|
+
/** Plugin that declares configurable context fields. */
|
|
997
|
+
interface ContextFieldPlugin extends Plugin {
|
|
998
|
+
/** Declare available context fields. Called once during setup. */
|
|
999
|
+
contextFields(): ContextFieldDef[];
|
|
1000
|
+
}
|
|
1001
|
+
/** Check if a plugin declares context fields. */
|
|
1002
|
+
declare function isContextFieldPlugin(p: Plugin): p is ContextFieldPlugin;
|
|
1003
|
+
/** Plugin that contributes sections to the context builder output. */
|
|
1004
|
+
interface ContextFormatterPlugin extends Plugin {
|
|
1005
|
+
/**
|
|
1006
|
+
* Append formatted markdown sections to `parts`.
|
|
1007
|
+
* `fields` contains resolved context fields (plugin defaults ← config ← per-query).
|
|
1008
|
+
*/
|
|
1009
|
+
formatContext(results: SearchResult[], parts: string[], fields: Record<string, unknown>): void;
|
|
1010
|
+
}
|
|
1011
|
+
/** Check if a plugin provides context formatting. */
|
|
1012
|
+
declare function isContextFormatterPlugin(p: Plugin): p is ContextFormatterPlugin;
|
|
1013
|
+
/** Plugin that owns database tables and supports versioned migrations. */
|
|
1014
|
+
interface MigratablePlugin extends Plugin {
|
|
1015
|
+
/** Current schema version for this plugin. */
|
|
1016
|
+
readonly schemaVersion: number;
|
|
1017
|
+
/** Ordered list of migrations (version 1, 2, 3, …). */
|
|
1018
|
+
readonly migrations: Migration[];
|
|
1019
|
+
}
|
|
1020
|
+
/** Check if a plugin supports schema migrations. */
|
|
1021
|
+
declare function isMigratable(p: Plugin): p is MigratablePlugin;
|
|
1022
|
+
/** Plugin that can do FTS5 keyword search on its own tables. */
|
|
1023
|
+
interface BM25SearchPlugin extends Plugin {
|
|
1024
|
+
/** Run BM25 keyword search. Returns scored results. */
|
|
1025
|
+
searchBM25(query: string, k: number, minScore?: number): SearchResult[];
|
|
1026
|
+
/** Rebuild the FTS5 index from the content table. */
|
|
1027
|
+
rebuildFTS?(): void;
|
|
1028
|
+
}
|
|
1029
|
+
/** Check if a plugin provides BM25 keyword search. */
|
|
1030
|
+
declare function isBM25SearchPlugin(p: Plugin): p is BM25SearchPlugin;
|
|
1031
|
+
/** Plugin that supports context expansion (provides manifest + resolves chunk IDs). */
|
|
1032
|
+
interface ExpandablePlugin extends Plugin {
|
|
1033
|
+
/**
|
|
1034
|
+
* Build a manifest of candidate chunks for LLM expansion.
|
|
1035
|
+
* Returns chunks from files NOT already in search results.
|
|
1036
|
+
*
|
|
1037
|
+
* @param excludeFilePaths File paths already present in search results — excluded from manifest.
|
|
1038
|
+
* @param excludeIds Chunk IDs already in search results — excluded from manifest.
|
|
1039
|
+
*/
|
|
1040
|
+
buildManifest(excludeFilePaths: string[], excludeIds: number[]): ExpanderManifestItem[];
|
|
1041
|
+
/**
|
|
1042
|
+
* Resolve chunk IDs back into SearchResults.
|
|
1043
|
+
* Called after the expander selects additional IDs.
|
|
1044
|
+
*/
|
|
1045
|
+
resolveChunks(ids: number[]): SearchResult[];
|
|
1046
|
+
}
|
|
1047
|
+
/** Check if a plugin supports context expansion. */
|
|
1048
|
+
declare function isExpandablePlugin(p: Plugin): p is ExpandablePlugin;
|
|
1049
|
+
/** Plugin that can resolve file paths/patterns directly to SearchResults (no search). */
|
|
1050
|
+
interface FileResolvablePlugin extends Plugin {
|
|
1051
|
+
/**
|
|
1052
|
+
* Resolve file paths, directories, and glob patterns to SearchResults.
|
|
1053
|
+
* Each entry is resolved: exact → directory → glob → fuzzy basename fallback.
|
|
1054
|
+
*
|
|
1055
|
+
* @param patterns - File paths, directory prefixes (trailing `/`), or glob patterns (`*`).
|
|
1056
|
+
*/
|
|
1057
|
+
resolveFiles(patterns: string[]): SearchResult[];
|
|
1058
|
+
}
|
|
1059
|
+
/** Check if a plugin can resolve files directly. */
|
|
1060
|
+
declare function isFileResolvable(p: Plugin): p is FileResolvablePlugin;
|
|
46
1061
|
|
|
47
1062
|
/**
|
|
48
1063
|
* BrainBank — Re-embedding Engine
|
|
@@ -53,16 +1068,12 @@ interface Watcher {
|
|
|
53
1068
|
*
|
|
54
1069
|
* Usage:
|
|
55
1070
|
* const result = await brain.reembed({ onProgress });
|
|
56
|
-
* // → { code: 1200, git: 500, docs: 80, kv: 45,
|
|
1071
|
+
* // → { code: 1200, git: 500, docs: 80, kv: 45, total: 1837 }
|
|
57
1072
|
*/
|
|
58
1073
|
|
|
59
1074
|
interface ReembedResult {
|
|
60
|
-
code
|
|
61
|
-
|
|
62
|
-
memory: number;
|
|
63
|
-
notes: number;
|
|
64
|
-
docs: number;
|
|
65
|
-
kv: number;
|
|
1075
|
+
/** Per-table vector counts. Keys are table names (e.g. 'code', 'git', 'docs', 'kv'). */
|
|
1076
|
+
counts: Record<string, number>;
|
|
66
1077
|
total: number;
|
|
67
1078
|
}
|
|
68
1079
|
interface ReembedOptions {
|
|
@@ -72,17 +1083,79 @@ interface ReembedOptions {
|
|
|
72
1083
|
batchSize?: number;
|
|
73
1084
|
}
|
|
74
1085
|
|
|
1086
|
+
/**
|
|
1087
|
+
* BrainBank — Watcher
|
|
1088
|
+
*
|
|
1089
|
+
* Thin coordinator for plugin-driven watching. Each plugin CAN drive its own
|
|
1090
|
+
* watching via WatchablePlugin.watch(). For IndexablePlugins that don't
|
|
1091
|
+
* implement WatchablePlugin, the Watcher provides a single shared fs.watch
|
|
1092
|
+
* tree with fan-out routing so each plugin only receives relevant events.
|
|
1093
|
+
*
|
|
1094
|
+
* Responsibilities:
|
|
1095
|
+
* 1. Call `plugin.watch(onEvent)` for each WatchablePlugin
|
|
1096
|
+
* 2. For IndexablePlugins without watch(), share one recursive fs.watch tree
|
|
1097
|
+
* 3. Route events to the correct plugin based on sub-repo scope
|
|
1098
|
+
* 4. Dedup macOS double-fire events (change+rename per save)
|
|
1099
|
+
* 5. Apply per-plugin debounce from `plugin.watchConfig()`
|
|
1100
|
+
* 6. On event: call `plugin.indexItems([id])` or `plugin.index()` for re-indexing
|
|
1101
|
+
* 7. Call `handle.stop()` on `close()`
|
|
1102
|
+
*
|
|
1103
|
+
* const watcher = brain.watch({ debounceMs: 2000 });
|
|
1104
|
+
* watcher.close(); // stop watching
|
|
1105
|
+
*/
|
|
1106
|
+
|
|
1107
|
+
interface WatchOptions {
|
|
1108
|
+
/** Default debounce for plugins that don't specify watchConfig. Default: 2000 */
|
|
1109
|
+
debounceMs?: number;
|
|
1110
|
+
/** Glob patterns to ignore (from config.json code.ignore). */
|
|
1111
|
+
ignore?: string[];
|
|
1112
|
+
/** Called when a source triggers re-indexing. */
|
|
1113
|
+
onIndex?: (sourceId: string, pluginName: string) => void;
|
|
1114
|
+
/** Called on errors. */
|
|
1115
|
+
onError?: (error: Error) => void;
|
|
1116
|
+
}
|
|
1117
|
+
/** Plugin-driven watcher that coordinates re-indexing across all WatchablePlugins. */
|
|
1118
|
+
declare class Watcher {
|
|
1119
|
+
private _active;
|
|
1120
|
+
private _batches;
|
|
1121
|
+
private _reindexFn;
|
|
1122
|
+
private _options;
|
|
1123
|
+
private _keepalive;
|
|
1124
|
+
constructor(reindexFn: () => Promise<void>, plugins: Plugin[], options?: WatchOptions, repoPath?: string);
|
|
1125
|
+
/** Whether the watcher is active. */
|
|
1126
|
+
get active(): boolean;
|
|
1127
|
+
/** Stop all plugin watchers. */
|
|
1128
|
+
close(): Promise<void>;
|
|
1129
|
+
/** Start watching for each WatchablePlugin, with shared fs.watch fallback. */
|
|
1130
|
+
private _startWatching;
|
|
1131
|
+
/**
|
|
1132
|
+
* Single shared recursive fs.watch that fans out events to multiple plugins.
|
|
1133
|
+
* Each event is routed based on: sub-repo prefix (code:backend → servicehub-backend/),
|
|
1134
|
+
* file extension (docs → .md only), and code support (code → isSupported).
|
|
1135
|
+
*/
|
|
1136
|
+
private _startSharedFsWatch;
|
|
1137
|
+
/** Handle an incoming event from a plugin. */
|
|
1138
|
+
private _onEvent;
|
|
1139
|
+
/** Flush pending events for a plugin — trigger re-indexing. */
|
|
1140
|
+
private _flush;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
75
1143
|
/**
|
|
76
1144
|
* BrainBank — Main Orchestrator
|
|
77
1145
|
*
|
|
78
|
-
* Thin facade that composes
|
|
79
|
-
* PluginRegistry
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* IndexAPI — code / git / docs indexing orchestration
|
|
1146
|
+
* Thin facade that composes services:
|
|
1147
|
+
* - **PluginRegistry** — registration + lookup
|
|
1148
|
+
* - **SearchAPI** — all search + context logic
|
|
1149
|
+
* - **runIndex** — code / git / docs indexing orchestration
|
|
83
1150
|
*
|
|
1151
|
+
* Initialization is inline — no indirection layers.
|
|
84
1152
|
* All heavy logic lives in those modules; BrainBank owns state,
|
|
85
|
-
* guards (
|
|
1153
|
+
* guards (`_requireInit` / `initialize`), and public API shape.
|
|
1154
|
+
*
|
|
1155
|
+
* Multi-process coordination:
|
|
1156
|
+
* - `ensureFresh()` detects stale HNSW indices via `index_state` table
|
|
1157
|
+
* - Hot-reloads from disk when another process updated the index
|
|
1158
|
+
* - Called implicitly before every search operation
|
|
86
1159
|
*/
|
|
87
1160
|
|
|
88
1161
|
declare class BrainBank extends EventEmitter {
|
|
@@ -91,168 +1164,167 @@ declare class BrainBank extends EventEmitter {
|
|
|
91
1164
|
private _embedding;
|
|
92
1165
|
private _registry;
|
|
93
1166
|
private _searchAPI?;
|
|
94
|
-
private
|
|
1167
|
+
private _indexDeps?;
|
|
1168
|
+
private _kvService?;
|
|
95
1169
|
private _initialized;
|
|
96
1170
|
private _initPromise;
|
|
97
1171
|
private _watcher?;
|
|
98
|
-
private
|
|
99
|
-
private _kvHnsw?;
|
|
100
|
-
private _kvVecs;
|
|
1172
|
+
private _webhookServer?;
|
|
101
1173
|
private _sharedHnsw;
|
|
1174
|
+
private _repoDBs;
|
|
1175
|
+
private _loadedVersions;
|
|
102
1176
|
constructor(config?: BrainBankConfig);
|
|
1177
|
+
/** Whether the brainbank has been initialized. */
|
|
1178
|
+
get isInitialized(): boolean;
|
|
1179
|
+
/** The resolved configuration. */
|
|
1180
|
+
get config(): Readonly<ResolvedConfig>;
|
|
1181
|
+
/** All registered plugin names (insertion order). */
|
|
1182
|
+
get plugins(): string[];
|
|
103
1183
|
/**
|
|
104
1184
|
* Register a plugin. Chainable.
|
|
105
1185
|
*
|
|
106
|
-
*
|
|
1186
|
+
* @example
|
|
1187
|
+
* brain.use(code({ repoPath: '.' })).use(docs());
|
|
1188
|
+
*
|
|
1189
|
+
* @throws If called after `initialize()`.
|
|
107
1190
|
*/
|
|
108
1191
|
use(plugin: Plugin): this;
|
|
109
|
-
/**
|
|
110
|
-
|
|
111
|
-
|
|
1192
|
+
/**
|
|
1193
|
+
* Check if a plugin is loaded.
|
|
1194
|
+
* Also matches type prefix (e.g. `'code'` matches `'code:frontend'`).
|
|
1195
|
+
*/
|
|
112
1196
|
has(name: string): boolean;
|
|
113
|
-
/** Get a plugin instance.
|
|
114
|
-
plugin<T extends Plugin = Plugin>(
|
|
1197
|
+
/** Get a plugin instance by name. Returns `undefined` if not loaded. */
|
|
1198
|
+
plugin<T extends Plugin = Plugin>(name: string): T | undefined;
|
|
115
1199
|
/**
|
|
116
1200
|
* Initialize database, HNSW indices, and load existing vectors.
|
|
117
|
-
*
|
|
118
|
-
*
|
|
1201
|
+
* Automatically called by `index` / `search` methods if not yet initialized.
|
|
1202
|
+
* Concurrent calls are deduped via `_initPromise`.
|
|
1203
|
+
*
|
|
1204
|
+
* @param options.force - If `true`, skip vector load on dimension mismatch.
|
|
119
1205
|
*/
|
|
120
1206
|
initialize(options?: {
|
|
121
1207
|
force?: boolean;
|
|
122
1208
|
}): Promise<void>;
|
|
123
|
-
private _runInitialize;
|
|
124
1209
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
1210
|
+
* Estimated memory footprint of loaded HNSW indices (bytes).
|
|
1211
|
+
* Counts only vector data: `vectorCount × dims × 4`.
|
|
1212
|
+
* Returns 0 if not initialized.
|
|
1213
|
+
*/
|
|
1214
|
+
memoryHint(): number;
|
|
1215
|
+
/** Close database and release all resources. Synchronous. */
|
|
1216
|
+
close(): void;
|
|
1217
|
+
/**
|
|
1218
|
+
* Get or create a dynamic collection (universal KV primitive).
|
|
127
1219
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
1220
|
+
* @example
|
|
1221
|
+
* const errors = brain.collection('debug_errors');
|
|
1222
|
+
* await errors.add('Fixed null check', { file: 'api.ts' });
|
|
1223
|
+
* const hits = await errors.search('null pointer');
|
|
1224
|
+
*
|
|
1225
|
+
* @throws If not initialized.
|
|
131
1226
|
*/
|
|
132
|
-
collection(name: string):
|
|
1227
|
+
collection(name: string): ICollection;
|
|
133
1228
|
/** List all collection names that have data. */
|
|
134
1229
|
listCollectionNames(): string[];
|
|
135
1230
|
/** Delete a collection's data and evict from cache. */
|
|
136
1231
|
deleteCollection(name: string): void;
|
|
1232
|
+
/** Run indexing across selected modules. Auto-initializes. */
|
|
137
1233
|
index(options?: {
|
|
138
|
-
modules?:
|
|
139
|
-
gitDepth?: number;
|
|
1234
|
+
modules?: string[];
|
|
140
1235
|
forceReindex?: boolean;
|
|
141
1236
|
onProgress?: StageProgressCallback;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
git?: IndexResult;
|
|
145
|
-
docs?: Record<string, {
|
|
146
|
-
indexed: number;
|
|
147
|
-
skipped: number;
|
|
148
|
-
chunks: number;
|
|
149
|
-
}>;
|
|
150
|
-
}>;
|
|
151
|
-
/** Index only code files (all repos in multi-repo mode). */
|
|
152
|
-
indexCode(options?: {
|
|
153
|
-
forceReindex?: boolean;
|
|
154
|
-
onProgress?: ProgressCallback;
|
|
155
|
-
}): Promise<IndexResult>;
|
|
156
|
-
/** Index only git history (all repos in multi-repo mode). */
|
|
157
|
-
indexGit(options?: {
|
|
158
|
-
depth?: number;
|
|
159
|
-
onProgress?: ProgressCallback;
|
|
160
|
-
}): Promise<IndexResult>;
|
|
161
|
-
/** Register a document collection. */
|
|
162
|
-
addCollection(collection: DocumentCollection): Promise<void>;
|
|
163
|
-
/** Remove a collection and all its indexed data. */
|
|
164
|
-
removeCollection(name: string): Promise<void>;
|
|
165
|
-
/** List all registered collections. */
|
|
166
|
-
listCollections(): DocumentCollection[];
|
|
167
|
-
/** Index all (or specific) document collections. */
|
|
168
|
-
indexDocs(options?: {
|
|
169
|
-
collections?: string[];
|
|
170
|
-
onProgress?: (collection: string, file: string, current: number, total: number) => void;
|
|
171
|
-
}): Promise<Record<string, {
|
|
172
|
-
indexed: number;
|
|
173
|
-
skipped: number;
|
|
174
|
-
chunks: number;
|
|
175
|
-
}>>;
|
|
176
|
-
/** Search documents only. */
|
|
177
|
-
searchDocs(query: string, options?: {
|
|
178
|
-
collection?: string;
|
|
179
|
-
k?: number;
|
|
180
|
-
minScore?: number;
|
|
181
|
-
}): Promise<SearchResult[]>;
|
|
182
|
-
/** Add context description for a collection path. */
|
|
183
|
-
addContext(collection: string, path: string, context: string): void;
|
|
184
|
-
/** Remove context for a collection path. */
|
|
185
|
-
removeContext(collection: string, path: string): void;
|
|
186
|
-
/** List all context entries. */
|
|
187
|
-
listContexts(): {
|
|
188
|
-
collection: string;
|
|
189
|
-
path: string;
|
|
190
|
-
context: string;
|
|
191
|
-
}[];
|
|
1237
|
+
pluginOptions?: Record<string, unknown>;
|
|
1238
|
+
}): Promise<Record<string, unknown>>;
|
|
192
1239
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
1240
|
+
* Detect stale HNSW indices and hot-reload from disk.
|
|
1241
|
+
* Called implicitly before every search operation.
|
|
1242
|
+
* Cost: one SQLite SELECT (~5μs on WAL mode).
|
|
195
1243
|
*/
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
minScore?: number;
|
|
203
|
-
useMMR?: boolean;
|
|
204
|
-
}): Promise<SearchResult[]>;
|
|
205
|
-
/** Semantic search over code only. */
|
|
206
|
-
searchCode(query: string, k?: number): Promise<SearchResult[]>;
|
|
207
|
-
/** Semantic search over commits only. */
|
|
208
|
-
searchCommits(query: string, k?: number): Promise<SearchResult[]>;
|
|
1244
|
+
ensureFresh(): Promise<void>;
|
|
1245
|
+
/**
|
|
1246
|
+
* Semantic search across all loaded modules.
|
|
1247
|
+
* Scope via `sources: { code: 10, git: 0 }`.
|
|
1248
|
+
*/
|
|
1249
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
209
1250
|
/**
|
|
210
1251
|
* Hybrid search: vector + BM25 fused with Reciprocal Rank Fusion.
|
|
211
|
-
*
|
|
1252
|
+
* Scope via `sources: { code: 10, git: 5, docs: 3, myNotes: 5 }`.
|
|
212
1253
|
*/
|
|
213
|
-
hybridSearch(query: string, options?:
|
|
214
|
-
codeK?: number;
|
|
215
|
-
gitK?: number;
|
|
216
|
-
patternK?: number;
|
|
217
|
-
minScore?: number;
|
|
218
|
-
useMMR?: boolean;
|
|
219
|
-
collections?: Record<string, number>;
|
|
220
|
-
}): Promise<SearchResult[]>;
|
|
1254
|
+
hybridSearch(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
221
1255
|
/** BM25 keyword search only (no embeddings needed). */
|
|
222
|
-
searchBM25(query: string, options?:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
patternK?: number;
|
|
226
|
-
}): Promise<SearchResult[]>;
|
|
227
|
-
/** Rebuild FTS5 indices. */
|
|
228
|
-
rebuildFTS(): void;
|
|
229
|
-
/** Get git history for a specific file. */
|
|
230
|
-
fileHistory(filePath: string, limit?: number): Promise<Record<string, unknown>[]>;
|
|
231
|
-
/** Get co-edit suggestions for a file. */
|
|
232
|
-
coEdits(filePath: string, limit?: number): CoEditSuggestion[];
|
|
233
|
-
/** Get statistics for all loaded modules. */
|
|
234
|
-
stats(): IndexStats;
|
|
1256
|
+
searchBM25(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
1257
|
+
/** Build formatted context block for LLM system prompt injection. Auto-initializes. */
|
|
1258
|
+
getContext(task: string, options?: ContextOptions): Promise<string>;
|
|
235
1259
|
/**
|
|
236
|
-
*
|
|
237
|
-
*
|
|
1260
|
+
* Resolve file paths, directories, and glob patterns to full SearchResults.
|
|
1261
|
+
* Bypasses search entirely — reads directly from plugin indexes.
|
|
1262
|
+
*
|
|
1263
|
+
* @example
|
|
1264
|
+
* const files = brain.resolveFiles(['src/auth/login.ts', 'src/graph/']);
|
|
238
1265
|
*/
|
|
1266
|
+
resolveFiles(patterns: string[]): SearchResult[];
|
|
1267
|
+
/** Rebuild FTS5 indices. */
|
|
1268
|
+
rebuildFTS(): void;
|
|
1269
|
+
/** Get statistics for all loaded plugins. */
|
|
1270
|
+
stats(): Record<string, Record<string, number | string> | undefined>;
|
|
1271
|
+
/** Start watching for changes and auto-re-index. */
|
|
239
1272
|
watch(options?: WatchOptions): Watcher;
|
|
240
1273
|
/**
|
|
241
1274
|
* Re-embed all existing text with the current embedding provider.
|
|
242
|
-
* Use
|
|
1275
|
+
* Use after switching providers (e.g. Local → OpenAI).
|
|
243
1276
|
*/
|
|
244
1277
|
reembed(options?: ReembedOptions): Promise<ReembedResult>;
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
1278
|
+
/**
|
|
1279
|
+
* Linear 8-step initialization:
|
|
1280
|
+
* 1. Open database
|
|
1281
|
+
* 2. Resolve embedding provider
|
|
1282
|
+
* 3. Check dimension mismatch
|
|
1283
|
+
* 4. Create KV HNSW + KVService
|
|
1284
|
+
* 5. Load KV vectors
|
|
1285
|
+
* 6. Initialize plugins
|
|
1286
|
+
* 7. Persist HNSW indices
|
|
1287
|
+
* 8. Build SearchAPI + index deps
|
|
1288
|
+
*/
|
|
1289
|
+
private _runInitialize;
|
|
1290
|
+
/** Reset shared state after a failed `_runInitialize`. */
|
|
1291
|
+
private _cleanupAfterFailedInit;
|
|
1292
|
+
/** Resolve embedding: explicit config > stored DB key > local default. */
|
|
1293
|
+
private _resolveEmbedding;
|
|
1294
|
+
/**
|
|
1295
|
+
* Get or create a per-repo SQLiteAdapter for namespaced plugins.
|
|
1296
|
+
* Non-namespaced plugins use the root DB.
|
|
1297
|
+
* DB path: `.brainbank/<repoName>.db` (e.g., `servicehub-backend.db`).
|
|
1298
|
+
*/
|
|
1299
|
+
private _getOrCreatePluginDb;
|
|
1300
|
+
/** Build a per-plugin `PluginContext` with appropriate DB and HNSW scoping. */
|
|
1301
|
+
private _buildPluginContext;
|
|
1302
|
+
/**
|
|
1303
|
+
* Reload a single HNSW index by name.
|
|
1304
|
+
* Discovers the vector table via ReembeddablePlugin capability.
|
|
1305
|
+
* KV is handled directly since it's core-owned.
|
|
1306
|
+
*
|
|
1307
|
+
* The `name` comes from `index_state` and equals the plugin's `mod.name`
|
|
1308
|
+
* (e.g. `code:backend`, `git`, `docs`). This matches the key used in
|
|
1309
|
+
* `getOrCreateSharedHnsw()` during initialization.
|
|
1310
|
+
*/
|
|
1311
|
+
private _reloadIndex;
|
|
1312
|
+
/** Guard: throw descriptive error if not initialized. */
|
|
251
1313
|
private _requireInit;
|
|
252
|
-
/** Get the docs indexer as CollectionPlugin with init + type check. */
|
|
253
|
-
private _docsPlugin;
|
|
254
1314
|
}
|
|
255
1315
|
|
|
1316
|
+
/**
|
|
1317
|
+
* BrainBank — Constants
|
|
1318
|
+
*
|
|
1319
|
+
* Core-only constants. Plugin names are NOT defined here — they belong
|
|
1320
|
+
* to their respective packages. Only keys owned by the core live here.
|
|
1321
|
+
*/
|
|
1322
|
+
/** HNSW index key for KV collections (core-owned). */
|
|
1323
|
+
declare const HNSW: {
|
|
1324
|
+
readonly KV: "kv";
|
|
1325
|
+
};
|
|
1326
|
+
type HnswKey = typeof HNSW[keyof typeof HNSW];
|
|
1327
|
+
|
|
256
1328
|
/**
|
|
257
1329
|
* BrainBank — Local Embedding Provider
|
|
258
1330
|
*
|
|
@@ -425,22 +1497,116 @@ declare class PerplexityContextEmbedding implements EmbeddingProvider {
|
|
|
425
1497
|
}
|
|
426
1498
|
|
|
427
1499
|
/**
|
|
428
|
-
* BrainBank —
|
|
1500
|
+
* BrainBank — Embedding Worker Proxy
|
|
429
1501
|
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
1502
|
+
* Drop-in replacement for any EmbeddingProvider that offloads
|
|
1503
|
+
* embedding computation to a dedicated worker thread.
|
|
1504
|
+
* The main thread's event loop stays free for serving searches.
|
|
1505
|
+
*
|
|
1506
|
+
* Usage:
|
|
1507
|
+
* const proxy = new EmbeddingWorkerProxy('local', { model: '...' });
|
|
1508
|
+
* await proxy.ready();
|
|
1509
|
+
* const vec = await proxy.embed('hello');
|
|
432
1510
|
*/
|
|
1511
|
+
|
|
1512
|
+
declare class EmbeddingWorkerProxy implements EmbeddingProvider {
|
|
1513
|
+
private _worker;
|
|
1514
|
+
private _nextId;
|
|
1515
|
+
private _pending;
|
|
1516
|
+
private _ready;
|
|
1517
|
+
private _dims;
|
|
1518
|
+
/** Embedding dimensions (available after `ready()` resolves). */
|
|
1519
|
+
get dims(): number;
|
|
1520
|
+
constructor(providerType: string, providerOptions?: Record<string, unknown>);
|
|
1521
|
+
/** Wait for the worker to be ready (provider loaded). */
|
|
1522
|
+
ready(): Promise<void>;
|
|
1523
|
+
/** Send a request to the worker and wait for the response. */
|
|
1524
|
+
private _send;
|
|
1525
|
+
/** Embed a single text string. */
|
|
1526
|
+
embed(text: string): Promise<Float32Array>;
|
|
1527
|
+
/** Embed multiple texts in a batch. */
|
|
1528
|
+
embedBatch(texts: string[]): Promise<Float32Array[]>;
|
|
1529
|
+
/** Terminate the worker. */
|
|
1530
|
+
close(): Promise<void>;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
433
1533
|
/**
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
1534
|
+
* HttpServer — Lightweight JSON API for BrainBank.
|
|
1535
|
+
*
|
|
1536
|
+
* Exposes BrainBank operations over HTTP so CLI commands can delegate
|
|
1537
|
+
* to a running server instead of cold-loading models each time.
|
|
1538
|
+
*
|
|
1539
|
+
* Routes:
|
|
1540
|
+
* POST /context → brain.getContext()
|
|
1541
|
+
* POST /index → brain.index()
|
|
1542
|
+
* GET /health → { ok, pid, uptime, port }
|
|
1543
|
+
*
|
|
1544
|
+
* Multi-repo: a single server handles all repos via WorkspacePool.
|
|
1545
|
+
* Each request includes a `repo` field to select the workspace.
|
|
437
1546
|
*/
|
|
438
|
-
|
|
1547
|
+
|
|
1548
|
+
interface HttpServerOptions {
|
|
1549
|
+
port?: number;
|
|
1550
|
+
/** Factory to create a BrainBank instance for a given repo path. */
|
|
1551
|
+
factory: (repoPath: string) => Promise<BrainBank>;
|
|
1552
|
+
/** Default repo path when request doesn't specify one. */
|
|
1553
|
+
defaultRepo?: string;
|
|
1554
|
+
/** Called when an error occurs during pool operations. */
|
|
1555
|
+
onError?: (repo: string, err: unknown) => void;
|
|
1556
|
+
/** Called on server lifecycle events. */
|
|
1557
|
+
onLog?: (msg: string) => void;
|
|
1558
|
+
}
|
|
1559
|
+
declare class HttpServer {
|
|
1560
|
+
private _server;
|
|
1561
|
+
private _pool;
|
|
1562
|
+
private _port;
|
|
1563
|
+
private _defaultRepo;
|
|
1564
|
+
private _startTime;
|
|
1565
|
+
private _log;
|
|
1566
|
+
constructor(options: HttpServerOptions);
|
|
1567
|
+
/** Start listening. Writes PID file for daemon detection. */
|
|
1568
|
+
start(): Promise<void>;
|
|
1569
|
+
/** Stop the server and clean up. */
|
|
1570
|
+
close(): void;
|
|
1571
|
+
get port(): number;
|
|
1572
|
+
private _handleRequest;
|
|
1573
|
+
private _handleHealth;
|
|
1574
|
+
private _handleContext;
|
|
1575
|
+
private _handleIndex;
|
|
1576
|
+
private _readBody;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
439
1579
|
/**
|
|
440
|
-
*
|
|
441
|
-
*
|
|
1580
|
+
* Daemon — PID file management for the BrainBank HTTP server.
|
|
1581
|
+
*
|
|
1582
|
+
* PID file: ~/.cache/brainbank/server.pid
|
|
1583
|
+
* Format: JSON { pid: number, port: number }
|
|
1584
|
+
*
|
|
1585
|
+
* Used by:
|
|
1586
|
+
* - `brainbank daemon` to write PID on start
|
|
1587
|
+
* - `brainbank daemon stop` to find and kill the daemon
|
|
1588
|
+
* - `brainbank status` to report server state
|
|
1589
|
+
* - CLI commands to detect running server for delegation
|
|
442
1590
|
*/
|
|
443
|
-
declare
|
|
1591
|
+
declare const DEFAULT_PORT = 8181;
|
|
1592
|
+
interface PidInfo {
|
|
1593
|
+
pid: number;
|
|
1594
|
+
port: number;
|
|
1595
|
+
}
|
|
1596
|
+
/** Write the PID file after server starts. */
|
|
1597
|
+
declare function writePid(pid: number, port: number): void;
|
|
1598
|
+
/** Read PID info from file. Returns null if missing or malformed. */
|
|
1599
|
+
declare function readPid(): PidInfo | null;
|
|
1600
|
+
/** Remove the PID file. */
|
|
1601
|
+
declare function removePid(): void;
|
|
1602
|
+
/**
|
|
1603
|
+
* Check if the server process is still alive.
|
|
1604
|
+
* Uses `kill(pid, 0)` which doesn't actually send a signal —
|
|
1605
|
+
* it just checks whether the process exists.
|
|
1606
|
+
*/
|
|
1607
|
+
declare function isServerRunning(): PidInfo | null;
|
|
1608
|
+
/** Get the server URL if running, or null. */
|
|
1609
|
+
declare function getServerUrl(): string | null;
|
|
444
1610
|
|
|
445
1611
|
/**
|
|
446
1612
|
* BrainBank — Qwen3 Local Reranker
|
|
@@ -463,30 +1629,120 @@ interface Qwen3RerankerOptions {
|
|
|
463
1629
|
/** Context size for ranking. Default: 2048 */
|
|
464
1630
|
contextSize?: number;
|
|
465
1631
|
}
|
|
466
|
-
declare class Qwen3Reranker implements Reranker {
|
|
467
|
-
private readonly _modelUri;
|
|
468
|
-
private readonly _cacheDir;
|
|
469
|
-
private readonly _contextSize;
|
|
470
|
-
private _model;
|
|
471
|
-
private _context;
|
|
472
|
-
private _loadPromise;
|
|
473
|
-
constructor(options?: Qwen3RerankerOptions);
|
|
474
|
-
/**
|
|
475
|
-
* Lazy-load the reranker model and create a ranking context.
|
|
476
|
-
* Model is auto-downloaded from HuggingFace on first use.
|
|
477
|
-
*/
|
|
478
|
-
private _ensureLoaded;
|
|
479
|
-
/**
|
|
480
|
-
* Score each document's relevance to the query.
|
|
481
|
-
* Returns scores (0.0 - 1.0) in same order as input documents.
|
|
482
|
-
*
|
|
483
|
-
* Deduplicates identical documents to avoid redundant computation.
|
|
484
|
-
*/
|
|
485
|
-
rank(query: string, documents: string[]): Promise<number[]>;
|
|
486
|
-
/** Release model resources. */
|
|
1632
|
+
declare class Qwen3Reranker implements Reranker {
|
|
1633
|
+
private readonly _modelUri;
|
|
1634
|
+
private readonly _cacheDir;
|
|
1635
|
+
private readonly _contextSize;
|
|
1636
|
+
private _model;
|
|
1637
|
+
private _context;
|
|
1638
|
+
private _loadPromise;
|
|
1639
|
+
constructor(options?: Qwen3RerankerOptions);
|
|
1640
|
+
/**
|
|
1641
|
+
* Lazy-load the reranker model and create a ranking context.
|
|
1642
|
+
* Model is auto-downloaded from HuggingFace on first use.
|
|
1643
|
+
*/
|
|
1644
|
+
private _ensureLoaded;
|
|
1645
|
+
/**
|
|
1646
|
+
* Score each document's relevance to the query.
|
|
1647
|
+
* Returns scores (0.0 - 1.0) in same order as input documents.
|
|
1648
|
+
*
|
|
1649
|
+
* Deduplicates identical documents to avoid redundant computation.
|
|
1650
|
+
*/
|
|
1651
|
+
rank(query: string, documents: string[]): Promise<number[]>;
|
|
1652
|
+
/** Release model resources. */
|
|
1653
|
+
close(): Promise<void>;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* BrainBank — Haiku Pruner
|
|
1658
|
+
*
|
|
1659
|
+
* LLM-based noise filter using Anthropic's Haiku 4.5 model.
|
|
1660
|
+
* Binary classification: for each search result, Haiku decides
|
|
1661
|
+
* "relevant" or "noise" based on filePath, metadata, and full
|
|
1662
|
+
* file content (capped at ~8K chars per item by prune.ts).
|
|
1663
|
+
*
|
|
1664
|
+
* Latency: ~300-600ms.
|
|
1665
|
+
*/
|
|
1666
|
+
|
|
1667
|
+
interface HaikuPrunerOptions {
|
|
1668
|
+
/** Anthropic API key. Falls back to ANTHROPIC_API_KEY env var. */
|
|
1669
|
+
apiKey?: string;
|
|
1670
|
+
/** Model to use. Default: claude-haiku-4-5-20251001 */
|
|
1671
|
+
model?: string;
|
|
1672
|
+
}
|
|
1673
|
+
declare class HaikuPruner implements Pruner {
|
|
1674
|
+
private readonly _apiKey;
|
|
1675
|
+
private readonly _model;
|
|
1676
|
+
constructor(options?: HaikuPrunerOptions);
|
|
1677
|
+
prune(query: string, items: PrunerItem[]): Promise<number[]>;
|
|
1678
|
+
close(): Promise<void>;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* BrainBank — Prune Utility
|
|
1683
|
+
*
|
|
1684
|
+
* Bridges SearchResult[] → Pruner.prune() → filtered SearchResult[].
|
|
1685
|
+
* Converts results to lightweight PrunerItems with full content previews,
|
|
1686
|
+
* calls the pruner, and filters out dropped results.
|
|
1687
|
+
*
|
|
1688
|
+
* Content strategy: send the FULL chunk content to the pruner so it can
|
|
1689
|
+
* make informed decisions. A per-item character cap prevents cost blowups
|
|
1690
|
+
* on monster files — when truncated, the middle is kept (imports + core
|
|
1691
|
+
* logic) rather than just the top.
|
|
1692
|
+
*/
|
|
1693
|
+
|
|
1694
|
+
/** Run the pruner on search results. Returns results in the order the pruner chose. */
|
|
1695
|
+
declare function pruneResults(query: string, results: SearchResult[], pruner: Pruner): Promise<SearchResult[]>;
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* BrainBank — Haiku Expander
|
|
1699
|
+
*
|
|
1700
|
+
* LLM-powered context expansion using Anthropic's Haiku 4.5 model.
|
|
1701
|
+
* After search + pruning, reviews a manifest of available chunks
|
|
1702
|
+
* and requests additional IDs to include.
|
|
1703
|
+
*
|
|
1704
|
+
* Flow:
|
|
1705
|
+
* 1. Receives lightweight manifest (~20 chars per chunk)
|
|
1706
|
+
* 2. Haiku selects additional chunk IDs (just numbers, fast)
|
|
1707
|
+
* 3. Caller fetches those chunks from DB and splices into results
|
|
1708
|
+
*
|
|
1709
|
+
* Designed for minimal token usage:
|
|
1710
|
+
* - Input: ~2,000-3,000 tokens (manifest)
|
|
1711
|
+
* - Output: ~50-100 tokens (ID array)
|
|
1712
|
+
* - Cost: ~$0.001 per call
|
|
1713
|
+
* - Latency: ~300-600ms
|
|
1714
|
+
*
|
|
1715
|
+
* Fail-open: any error returns empty array (no expansion).
|
|
1716
|
+
*/
|
|
1717
|
+
|
|
1718
|
+
interface HaikuExpanderOptions {
|
|
1719
|
+
/** Anthropic API key. Falls back to ANTHROPIC_API_KEY env var. */
|
|
1720
|
+
apiKey?: string;
|
|
1721
|
+
/** Model to use. Default: claude-haiku-4-5-20251001 */
|
|
1722
|
+
model?: string;
|
|
1723
|
+
}
|
|
1724
|
+
declare class HaikuExpander implements Expander {
|
|
1725
|
+
private readonly _apiKey;
|
|
1726
|
+
private readonly _model;
|
|
1727
|
+
constructor(options?: HaikuExpanderOptions);
|
|
1728
|
+
expand(query: string, currentIds: number[], manifest: ExpanderManifestItem[]): Promise<ExpanderResult>;
|
|
1729
|
+
/** Parse Haiku response — handles both `{ ids, note }` and bare `[...]` formats. */
|
|
1730
|
+
private _parseResponse;
|
|
487
1731
|
close(): Promise<void>;
|
|
488
1732
|
}
|
|
489
1733
|
|
|
1734
|
+
/**
|
|
1735
|
+
* BrainBank — Provider Key
|
|
1736
|
+
*
|
|
1737
|
+
* Infers a stable key from an existing EmbeddingProvider instance.
|
|
1738
|
+
* Lives in lib/ (Layer 0) to avoid db/ → providers/ dependency.
|
|
1739
|
+
*/
|
|
1740
|
+
|
|
1741
|
+
/** Known embedding provider keys. */
|
|
1742
|
+
type EmbeddingKey = 'local' | 'openai' | 'perplexity' | 'perplexity-context';
|
|
1743
|
+
/** Infer a stable key from an existing provider instance. */
|
|
1744
|
+
declare function providerKey(p: EmbeddingProvider): EmbeddingKey;
|
|
1745
|
+
|
|
490
1746
|
/**
|
|
491
1747
|
* BrainBank — Embedding Provider Resolver
|
|
492
1748
|
*
|
|
@@ -494,12 +1750,8 @@ declare class Qwen3Reranker implements Reranker {
|
|
|
494
1750
|
* Used by the Initializer to auto-resolve from DB config.
|
|
495
1751
|
*/
|
|
496
1752
|
|
|
497
|
-
/** Known embedding provider keys. */
|
|
498
|
-
type EmbeddingKey = 'local' | 'openai' | 'perplexity' | 'perplexity-context';
|
|
499
1753
|
/** Resolve an EmbeddingProvider from a key string. Lazy-loads the provider module. */
|
|
500
1754
|
declare function resolveEmbedding(key: string): Promise<EmbeddingProvider>;
|
|
501
|
-
/** Infer a stable key from an existing provider instance. */
|
|
502
|
-
declare function providerKey(p: EmbeddingProvider): EmbeddingKey;
|
|
503
1755
|
|
|
504
1756
|
declare const DEFAULTS: ResolvedConfig;
|
|
505
1757
|
/**
|
|
@@ -526,196 +1778,6 @@ declare function resolveConfig(partial?: BrainBankConfig): ResolvedConfig;
|
|
|
526
1778
|
*/
|
|
527
1779
|
declare function searchMMR(index: VectorIndex, query: Float32Array, vectorCache: Map<number, Float32Array>, k: number, lambda?: number): SearchHit[];
|
|
528
1780
|
|
|
529
|
-
/**
|
|
530
|
-
* BrainBank — Tree-Sitter Code Chunker
|
|
531
|
-
*
|
|
532
|
-
* AST-aware code splitting using native tree-sitter bindings.
|
|
533
|
-
* Extracts semantic blocks (functions, classes, methods, interfaces)
|
|
534
|
-
* from the AST. Falls back to sliding window for unsupported languages.
|
|
535
|
-
*/
|
|
536
|
-
|
|
537
|
-
interface ChunkerConfig {
|
|
538
|
-
/** Max lines per chunk. Default: 80 */
|
|
539
|
-
maxLines?: number;
|
|
540
|
-
/** Min lines for a detected block to be a chunk. Default: 3 */
|
|
541
|
-
minLines?: number;
|
|
542
|
-
/** Overlap between adjacent generic chunks. Default: 5 */
|
|
543
|
-
overlap?: number;
|
|
544
|
-
}
|
|
545
|
-
declare class CodeChunker {
|
|
546
|
-
private MAX;
|
|
547
|
-
private MIN;
|
|
548
|
-
private OVERLAP;
|
|
549
|
-
private _parser;
|
|
550
|
-
private _langCache;
|
|
551
|
-
constructor(config?: ChunkerConfig);
|
|
552
|
-
/** Lazy-init tree-sitter parser. */
|
|
553
|
-
private _ensureParser;
|
|
554
|
-
/** Load a language grammar (cached). Throws if grammar package is not installed. */
|
|
555
|
-
private _loadGrammar;
|
|
556
|
-
/**
|
|
557
|
-
* Split file content into semantic chunks using tree-sitter AST.
|
|
558
|
-
* Throws if the language has a grammar but the package isn't installed.
|
|
559
|
-
* Falls back to sliding window only for truly unsupported languages.
|
|
560
|
-
*/
|
|
561
|
-
chunk(filePath: string, content: string, language: string): Promise<CodeChunk[]>;
|
|
562
|
-
/** Walk AST and extract top-level semantic blocks. */
|
|
563
|
-
private _extractChunks;
|
|
564
|
-
/** Classify and process a single AST node. */
|
|
565
|
-
private _processNode;
|
|
566
|
-
/** Check which category a node type belongs to. */
|
|
567
|
-
private _categorize;
|
|
568
|
-
/** Process a matched declaration: class → split by methods, else → chunk directly. */
|
|
569
|
-
private _processDeclaration;
|
|
570
|
-
/** Split a large class into individual method chunks. */
|
|
571
|
-
private _splitClassIntoMethods;
|
|
572
|
-
/** Find the class body node. */
|
|
573
|
-
private _findClassBody;
|
|
574
|
-
/** Extract name from an AST node. */
|
|
575
|
-
private _extractName;
|
|
576
|
-
/** Map category to chunk type. */
|
|
577
|
-
private _toChunkType;
|
|
578
|
-
/** Add a node as a chunk, avoiding duplicates. */
|
|
579
|
-
private _addChunk;
|
|
580
|
-
private _chunkGeneric;
|
|
581
|
-
/** Split a large block into overlapping sub-chunks. */
|
|
582
|
-
private _splitLargeBlock;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* BrainBank — Code Indexer
|
|
587
|
-
*
|
|
588
|
-
* Walks a repository, chunks source files semantically,
|
|
589
|
-
* embeds each chunk, and stores in SQLite + HNSW.
|
|
590
|
-
* Incremental: only re-indexes files that changed (by content hash).
|
|
591
|
-
*/
|
|
592
|
-
|
|
593
|
-
interface CodeWalkerDeps {
|
|
594
|
-
db: Database;
|
|
595
|
-
hnsw: HNSWIndex;
|
|
596
|
-
vectorCache: Map<number, Float32Array>;
|
|
597
|
-
embedding: EmbeddingProvider;
|
|
598
|
-
}
|
|
599
|
-
interface CodeIndexOptions {
|
|
600
|
-
forceReindex?: boolean;
|
|
601
|
-
onProgress?: ProgressCallback;
|
|
602
|
-
}
|
|
603
|
-
declare class CodeWalker {
|
|
604
|
-
private _chunker;
|
|
605
|
-
private _deps;
|
|
606
|
-
private _repoPath;
|
|
607
|
-
private _maxFileSize;
|
|
608
|
-
constructor(repoPath: string, deps: CodeWalkerDeps, maxFileSize?: number);
|
|
609
|
-
/** Index all supported files. Skips unchanged files (same content hash). */
|
|
610
|
-
index(options?: CodeIndexOptions): Promise<IndexResult>;
|
|
611
|
-
/** Remove old chunks and their HNSW vectors for a file. */
|
|
612
|
-
private _removeOldChunks;
|
|
613
|
-
/** Chunk, embed, and store a single file. Returns chunk count. */
|
|
614
|
-
private _indexFile;
|
|
615
|
-
private _walkRepo;
|
|
616
|
-
private _hash;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* BrainBank — Git Indexer
|
|
621
|
-
*
|
|
622
|
-
* Reads git history, embeds commit messages + diffs,
|
|
623
|
-
* and computes file co-edit relationships.
|
|
624
|
-
* Incremental: only processes new commits.
|
|
625
|
-
*/
|
|
626
|
-
|
|
627
|
-
interface GitIndexerDeps {
|
|
628
|
-
db: Database;
|
|
629
|
-
hnsw: HNSWIndex;
|
|
630
|
-
vectorCache: Map<number, Float32Array>;
|
|
631
|
-
embedding: EmbeddingProvider;
|
|
632
|
-
}
|
|
633
|
-
interface GitIndexOptions {
|
|
634
|
-
depth?: number;
|
|
635
|
-
onProgress?: ProgressCallback;
|
|
636
|
-
}
|
|
637
|
-
declare class GitIndexer {
|
|
638
|
-
private _deps;
|
|
639
|
-
private _repoPath;
|
|
640
|
-
private _maxDiffBytes;
|
|
641
|
-
constructor(repoPath: string, deps: GitIndexerDeps, maxDiffBytes?: number);
|
|
642
|
-
/**
|
|
643
|
-
* Index git history.
|
|
644
|
-
* Only processes commits not already in the database.
|
|
645
|
-
*/
|
|
646
|
-
index(options?: GitIndexOptions): Promise<IndexResult>;
|
|
647
|
-
/** Initialize simple-git. Returns null if git is unavailable. */
|
|
648
|
-
private _initGit;
|
|
649
|
-
/** Prepare all SQL statements (hoisted outside loops). */
|
|
650
|
-
private _prepareStatements;
|
|
651
|
-
/** Phase 1: Collect commit data from git (async git calls). */
|
|
652
|
-
private _collectCommits;
|
|
653
|
-
/** Extract diff, stat, and text from a single commit. */
|
|
654
|
-
private _parseCommit;
|
|
655
|
-
/** Phase 3: Insert commits + vectors in a single transaction. */
|
|
656
|
-
private _insertCommits;
|
|
657
|
-
/** Phase 4: Update HNSW index and compute co-edits. */
|
|
658
|
-
private _updateHnsw;
|
|
659
|
-
/** Compute which files tend to be edited together. */
|
|
660
|
-
private _computeCoEdits;
|
|
661
|
-
/** Query commit_files in chunks to stay under SQLite's 999-variable limit. */
|
|
662
|
-
private _queryCommitFiles;
|
|
663
|
-
/** Group file paths by commit ID. */
|
|
664
|
-
private _groupFilesByCommit;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* BrainBank — Document Indexer
|
|
669
|
-
*
|
|
670
|
-
* Indexes generic document collections (markdown, text, etc.)
|
|
671
|
-
* with heading-aware smart chunking, inspired by qmd.
|
|
672
|
-
*
|
|
673
|
-
* const indexer = new DocsIndexer(db, embedding, hnsw, vecCache);
|
|
674
|
-
* await indexer.indexCollection('notes', '/path/to/notes', '**\/*.md');
|
|
675
|
-
*/
|
|
676
|
-
|
|
677
|
-
declare class DocsIndexer {
|
|
678
|
-
private _db;
|
|
679
|
-
private _embedding;
|
|
680
|
-
private _hnsw;
|
|
681
|
-
private _vecCache;
|
|
682
|
-
constructor(_db: Database, _embedding: EmbeddingProvider, _hnsw: HNSWIndex, _vecCache: Map<number, Float32Array>);
|
|
683
|
-
/**
|
|
684
|
-
* Index all documents in a collection.
|
|
685
|
-
* Incremental — skips unchanged files (by content hash).
|
|
686
|
-
*/
|
|
687
|
-
indexCollection(collection: string, dirPath: string, pattern?: string, options?: {
|
|
688
|
-
ignore?: string[];
|
|
689
|
-
onProgress?: (file: string, current: number, total: number) => void;
|
|
690
|
-
}): Promise<{
|
|
691
|
-
indexed: number;
|
|
692
|
-
skipped: number;
|
|
693
|
-
chunks: number;
|
|
694
|
-
}>;
|
|
695
|
-
/** Walk directory tree and collect matching files. */
|
|
696
|
-
private _walkFiles;
|
|
697
|
-
/** Check if a file matches any ignore patterns. */
|
|
698
|
-
private _isIgnoredFile;
|
|
699
|
-
/** Check if all chunks for a file match the current hash and have vectors. */
|
|
700
|
-
private _isUnchanged;
|
|
701
|
-
/** Remove old chunks and their HNSW vectors for a file. */
|
|
702
|
-
private _removeOldChunks;
|
|
703
|
-
/** Index a single file: chunk, embed, store in DB + HNSW. */
|
|
704
|
-
private _indexFile;
|
|
705
|
-
/** Remove all indexed data for a collection. */
|
|
706
|
-
removeCollection(collection: string): void;
|
|
707
|
-
/** Split document into chunks at natural markdown boundaries. */
|
|
708
|
-
private _smartChunk;
|
|
709
|
-
/** Handle the last chunk: merge if too small, otherwise push. */
|
|
710
|
-
private _flushRemainder;
|
|
711
|
-
/** Find the best break position within the target window. */
|
|
712
|
-
private _findBestBreak;
|
|
713
|
-
/** Find all potential break points in the document with scores. */
|
|
714
|
-
private _findBreakPoints;
|
|
715
|
-
/** Extract document title from first heading or filename. */
|
|
716
|
-
private _extractTitle;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
1781
|
/**
|
|
720
1782
|
* BrainBank — Language Registry
|
|
721
1783
|
*
|
|
@@ -728,281 +1790,206 @@ declare const IGNORE_DIRS: Set<string>;
|
|
|
728
1790
|
declare function isSupported(filePath: string): boolean;
|
|
729
1791
|
/** Get the language name for a file. Returns undefined if not supported. */
|
|
730
1792
|
declare function getLanguage(filePath: string): string | undefined;
|
|
1793
|
+
/** Check if a directory name should be ignored. */
|
|
1794
|
+
declare function isIgnoredDir(dirName: string): boolean;
|
|
1795
|
+
/** Check if a filename should be ignored. */
|
|
1796
|
+
declare function isIgnoredFile(fileName: string): boolean;
|
|
731
1797
|
|
|
732
1798
|
/**
|
|
733
|
-
* BrainBank —
|
|
1799
|
+
* BrainBank — Math Utilities
|
|
734
1800
|
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
* Searchable by semantic similarity via HNSW.
|
|
1801
|
+
* Pure vector math functions for similarity calculations.
|
|
1802
|
+
* No dependencies — works on Float32Array directly.
|
|
738
1803
|
*/
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
* Filters by minimum success rate.
|
|
757
|
-
*/
|
|
758
|
-
search(query: string, k?: number, minSuccess?: number): Promise<(LearningPattern & {
|
|
759
|
-
score: number;
|
|
760
|
-
})[]>;
|
|
761
|
-
/**
|
|
762
|
-
* Get all patterns for a specific task type.
|
|
763
|
-
*/
|
|
764
|
-
getByTaskType(taskType: string, limit?: number): LearningPattern[];
|
|
765
|
-
/** Total number of stored patterns. */
|
|
766
|
-
get count(): number;
|
|
767
|
-
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Cosine similarity between two vectors.
|
|
1806
|
+
* Assumes vectors are already normalized (unit length).
|
|
1807
|
+
* Returns value between -1.0 and 1.0.
|
|
1808
|
+
*/
|
|
1809
|
+
declare function cosineSimilarity(a: Float32Array, b: Float32Array): number;
|
|
1810
|
+
/**
|
|
1811
|
+
* L2-normalize a vector to unit length.
|
|
1812
|
+
* Returns a new Float32Array.
|
|
1813
|
+
*/
|
|
1814
|
+
declare function normalize(vec: Float32Array): Float32Array;
|
|
1815
|
+
/**
|
|
1816
|
+
* Convert a Float32Array to a Buffer for SQLite storage.
|
|
1817
|
+
* Handles views with non-zero byteOffset (e.g. from batched embedding output).
|
|
1818
|
+
* Using Buffer.from(vec.buffer) directly is WRONG for views — it copies the entire parent buffer.
|
|
1819
|
+
*/
|
|
1820
|
+
declare function vecToBuffer(vec: Float32Array): Buffer;
|
|
768
1821
|
|
|
769
1822
|
/**
|
|
770
|
-
* BrainBank —
|
|
1823
|
+
* BrainBank — KV Service
|
|
771
1824
|
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
* - consolidate: run both
|
|
1825
|
+
* Owns the shared HNSW index and vector cache for KV collections.
|
|
1826
|
+
* Provides collection creation, listing, and deletion.
|
|
1827
|
+
* Extracted from BrainBank to separate infrastructure from facade.
|
|
776
1828
|
*/
|
|
777
1829
|
|
|
778
|
-
declare class
|
|
1830
|
+
declare class KVService {
|
|
779
1831
|
private _db;
|
|
780
|
-
private
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
deduped: number;
|
|
799
|
-
};
|
|
1832
|
+
private _embedding;
|
|
1833
|
+
private _hnsw;
|
|
1834
|
+
private _vecs;
|
|
1835
|
+
private _reranker?;
|
|
1836
|
+
private _collections;
|
|
1837
|
+
constructor(_db: DatabaseAdapter, _embedding: EmbeddingProvider, _hnsw: HNSWIndex, _vecs: Map<number, Float32Array>, _reranker?: Reranker | undefined);
|
|
1838
|
+
/** Get or create a named collection. */
|
|
1839
|
+
collection(name: string): Collection;
|
|
1840
|
+
/** List all collection names that have data. */
|
|
1841
|
+
listNames(): string[];
|
|
1842
|
+
/** Delete a collection's data and evict from cache. Removes vectors from HNSW to prevent ghost entries. */
|
|
1843
|
+
delete(name: string): void;
|
|
1844
|
+
/** Access the shared HNSW index (used by reembed). */
|
|
1845
|
+
get hnsw(): HNSWIndex;
|
|
1846
|
+
/** Access the shared vector cache. @internal */
|
|
1847
|
+
get vecs(): Map<number, Float32Array>;
|
|
1848
|
+
/** Clear all cached collections and vectors. */
|
|
1849
|
+
clear(): void;
|
|
800
1850
|
}
|
|
801
1851
|
|
|
802
1852
|
/**
|
|
803
|
-
* BrainBank —
|
|
1853
|
+
* BrainBank — Plugin Registry
|
|
804
1854
|
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
* Supports vector + BM25 hybrid retrieval via HNSW + FTS5.
|
|
1855
|
+
* Manages registration and lookup of plugins.
|
|
1856
|
+
* Extracted from BrainBank so the facade stays focused on orchestration.
|
|
808
1857
|
*
|
|
809
|
-
*
|
|
810
|
-
* -
|
|
811
|
-
* -
|
|
1858
|
+
* Responsibilities:
|
|
1859
|
+
* - Store plugins by name
|
|
1860
|
+
* - Type-prefix matching ('code' finds 'code:frontend', 'code:backend')
|
|
1861
|
+
* - Alias resolution (currently none; add here if needed)
|
|
1862
|
+
* - Consistent error messages on missing plugins
|
|
812
1863
|
*/
|
|
813
1864
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
filesChanged?: string[];
|
|
819
|
-
patterns?: string[];
|
|
820
|
-
openQuestions?: string[];
|
|
821
|
-
tags?: string[];
|
|
822
|
-
}
|
|
823
|
-
interface StoredNote extends NoteDigest {
|
|
824
|
-
id: number;
|
|
825
|
-
tier: 'short' | 'long';
|
|
826
|
-
createdAt: number;
|
|
827
|
-
score?: number;
|
|
828
|
-
}
|
|
829
|
-
interface RecallOptions {
|
|
830
|
-
/** Max results. Default: 5 */
|
|
831
|
-
k?: number;
|
|
832
|
-
/** Search mode. Default: 'hybrid' */
|
|
833
|
-
mode?: 'hybrid' | 'vector' | 'keyword';
|
|
834
|
-
/** Minimum score threshold. Default: 0.15 */
|
|
835
|
-
minScore?: number;
|
|
836
|
-
/** Filter by tier. Default: all */
|
|
837
|
-
tier?: 'short' | 'long';
|
|
838
|
-
}
|
|
839
|
-
declare class NoteStore {
|
|
840
|
-
private _db;
|
|
841
|
-
private _embedding;
|
|
842
|
-
private _hnsw;
|
|
843
|
-
private _vecs;
|
|
844
|
-
constructor(db: Database, embedding: EmbeddingProvider, hnsw: HNSWIndex, vecs: Map<number, Float32Array>);
|
|
845
|
-
/**
|
|
846
|
-
* Store a note digest.
|
|
847
|
-
* Embeds title + summary for vector search, auto-indexed in FTS5.
|
|
848
|
-
*/
|
|
849
|
-
remember(digest: NoteDigest): Promise<number>;
|
|
850
|
-
/**
|
|
851
|
-
* Recall relevant notes.
|
|
852
|
-
* Supports vector, keyword, or hybrid (default) retrieval.
|
|
853
|
-
*/
|
|
854
|
-
recall(query: string, options?: RecallOptions): Promise<StoredNote[]>;
|
|
1865
|
+
declare class PluginRegistry {
|
|
1866
|
+
private _map;
|
|
1867
|
+
/** Store a plugin. Duplicate names silently overwrite. */
|
|
1868
|
+
register(plugin: Plugin): void;
|
|
855
1869
|
/**
|
|
856
|
-
*
|
|
1870
|
+
* Check whether a plugin is registered.
|
|
1871
|
+
* Supports type-prefix matching: `has('code')` returns true if
|
|
1872
|
+
* 'code', 'code:frontend', or 'code:backend' is registered.
|
|
857
1873
|
*/
|
|
858
|
-
|
|
1874
|
+
has(name: string): boolean;
|
|
859
1875
|
/**
|
|
860
|
-
* Get
|
|
1876
|
+
* Get a plugin by name. Throws a descriptive error if not found.
|
|
1877
|
+
*
|
|
1878
|
+
* Resolution order:
|
|
1879
|
+
* 1. Alias map (currently empty)
|
|
1880
|
+
* 2. Exact match
|
|
1881
|
+
* 3. First type-prefix match ('code' → 'code:frontend')
|
|
861
1882
|
*/
|
|
862
|
-
|
|
863
|
-
total: number;
|
|
864
|
-
short: number;
|
|
865
|
-
long: number;
|
|
866
|
-
};
|
|
1883
|
+
get<T extends Plugin = Plugin>(name: string): T;
|
|
867
1884
|
/**
|
|
868
|
-
*
|
|
869
|
-
*
|
|
1885
|
+
* Return every plugin whose name equals `type` or starts with `type + ':'`.
|
|
1886
|
+
* Example: allByType('code') → [code, code:frontend, code:backend]
|
|
870
1887
|
*/
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
* BrainBank — Search Types
|
|
881
|
-
*
|
|
882
|
-
* Shared interface for all search strategies.
|
|
883
|
-
* Implement SearchStrategy to add a new search backend.
|
|
884
|
-
*/
|
|
885
|
-
|
|
886
|
-
/** Any search implementation follows this shape. */
|
|
887
|
-
interface SearchStrategy {
|
|
888
|
-
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
889
|
-
/** Rebuild internal indices (e.g. FTS5). Optional. */
|
|
890
|
-
rebuild?(): void;
|
|
891
|
-
}
|
|
892
|
-
interface SearchOptions {
|
|
893
|
-
/** Max code results. Default: 6 */
|
|
894
|
-
codeK?: number;
|
|
895
|
-
/** Max git results. Default: 5 */
|
|
896
|
-
gitK?: number;
|
|
897
|
-
/** Max pattern results. Default: 4 */
|
|
898
|
-
patternK?: number;
|
|
899
|
-
/** Minimum similarity score. Default: 0.25 */
|
|
900
|
-
minScore?: number;
|
|
901
|
-
/** Use MMR for diversity. Default: true */
|
|
902
|
-
useMMR?: boolean;
|
|
903
|
-
/** MMR lambda. Default: 0.7 */
|
|
904
|
-
mmrLambda?: number;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
/**
|
|
908
|
-
* BrainBank — Co-Edit Analyzer
|
|
909
|
-
*
|
|
910
|
-
* Suggests files that historically change together.
|
|
911
|
-
* Based on git commit co-occurrence analysis.
|
|
912
|
-
*/
|
|
913
|
-
|
|
914
|
-
declare class CoEditAnalyzer {
|
|
915
|
-
private _db;
|
|
916
|
-
constructor(_db: Database);
|
|
1888
|
+
allByType(type: string): Plugin[];
|
|
1889
|
+
/** Return the first plugin that matches the type prefix, or undefined. */
|
|
1890
|
+
firstByType(type: string): Plugin | undefined;
|
|
1891
|
+
/** All registered plugin names (insertion order). */
|
|
1892
|
+
get names(): string[];
|
|
1893
|
+
/** All registered plugin instances (insertion order). */
|
|
1894
|
+
get all(): Plugin[];
|
|
917
1895
|
/**
|
|
918
|
-
*
|
|
919
|
-
*
|
|
1896
|
+
* Underlying Map.
|
|
1897
|
+
* Prefer `all`, `allByType`, or `firstByType` everywhere else.
|
|
920
1898
|
*/
|
|
921
|
-
|
|
1899
|
+
get raw(): Map<string, Plugin>;
|
|
1900
|
+
/** Remove all registered plugins. Called by BrainBank.close(). */
|
|
1901
|
+
clear(): void;
|
|
922
1902
|
}
|
|
923
1903
|
|
|
924
1904
|
/**
|
|
925
1905
|
* BrainBank — Context Builder
|
|
926
1906
|
*
|
|
927
|
-
*
|
|
928
|
-
*
|
|
929
|
-
*
|
|
1907
|
+
* Orchestrates the context-building pipeline:
|
|
1908
|
+
* 1. Vector search (primary)
|
|
1909
|
+
* 2. Path scoping (filter)
|
|
1910
|
+
* 3. LLM noise pruning (optional)
|
|
1911
|
+
* 4. Session dedup (filter)
|
|
1912
|
+
* 5. LLM context expansion (optional — expander field)
|
|
1913
|
+
* 6. Plugin formatters (output)
|
|
1914
|
+
*
|
|
1915
|
+
* All search post-processing lives in `bm25-boost.ts`.
|
|
1916
|
+
* Plugin-agnostic — discovers formatters from ContextFormatterPlugin.
|
|
930
1917
|
*/
|
|
931
1918
|
|
|
932
1919
|
declare class ContextBuilder {
|
|
933
1920
|
private _search;
|
|
934
|
-
private
|
|
935
|
-
|
|
1921
|
+
private _registry;
|
|
1922
|
+
private _pruner?;
|
|
1923
|
+
private _embedding?;
|
|
1924
|
+
private _rerankerName?;
|
|
1925
|
+
private _configFields;
|
|
1926
|
+
private _expander?;
|
|
1927
|
+
constructor(_search: SearchStrategy | undefined, _registry: PluginRegistry, _pruner?: Pruner | undefined, _embedding?: EmbeddingProvider | undefined, _rerankerName?: string | undefined, _configFields?: Record<string, unknown>, _expander?: Expander | undefined);
|
|
1928
|
+
/** Set config-level context field defaults (from config.json "context" section). */
|
|
1929
|
+
set configFields(fields: Record<string, unknown>);
|
|
1930
|
+
/** Set the expander instance. */
|
|
1931
|
+
set expander(expander: Expander | undefined);
|
|
936
1932
|
/** Build a full context block for a task. Returns markdown for system prompt. */
|
|
937
1933
|
build(task: string, options?: ContextOptions): Promise<string>;
|
|
938
|
-
/**
|
|
939
|
-
private
|
|
940
|
-
/**
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1934
|
+
/** Invoke ContextFormatterPlugins. Multi-repo: each plugin formats its own results. */
|
|
1935
|
+
private _appendFormatterResults;
|
|
1936
|
+
/**
|
|
1937
|
+
* Resolve context fields: plugin defaults ← config.json ← per-query.
|
|
1938
|
+
* Returns a flat Record with the final value for each field.
|
|
1939
|
+
*/
|
|
1940
|
+
private _resolveFields;
|
|
1941
|
+
/**
|
|
1942
|
+
* Run LLM expansion: build manifest of candidate chunks from files
|
|
1943
|
+
* NOT already in search results, call expander, resolve selected IDs.
|
|
1944
|
+
*/
|
|
1945
|
+
private _expand;
|
|
1946
|
+
/** Detect the multi-repo prefix from existing results (e.g. 'servicehub-frontend'). */
|
|
1947
|
+
private _detectRepoPrefix;
|
|
1948
|
+
/** Collect results from SearchablePlugins that don't have their own formatter. */
|
|
1949
|
+
private _appendSearchableResults;
|
|
946
1950
|
}
|
|
947
1951
|
|
|
948
1952
|
/**
|
|
949
|
-
* BrainBank — Vector Search
|
|
1953
|
+
* BrainBank — Composite Vector Search
|
|
950
1954
|
*
|
|
951
|
-
*
|
|
952
|
-
*
|
|
1955
|
+
* Generic orchestrator for domain-specific vector searches.
|
|
1956
|
+
* Embeds the query once, delegates to registered DomainVectorSearch strategies.
|
|
1957
|
+
* Uses round-robin interleaving when multiple strategies exist to ensure
|
|
1958
|
+
* balanced representation across repos/domains.
|
|
1959
|
+
* Plugin-agnostic — strategies are discovered at wiring time.
|
|
953
1960
|
*/
|
|
954
1961
|
|
|
955
|
-
interface
|
|
956
|
-
|
|
957
|
-
codeHnsw?: HNSWIndex;
|
|
958
|
-
gitHnsw?: HNSWIndex;
|
|
959
|
-
patternHnsw?: HNSWIndex;
|
|
960
|
-
codeVecs: Map<number, Float32Array>;
|
|
961
|
-
gitVecs: Map<number, Float32Array>;
|
|
962
|
-
patternVecs: Map<number, Float32Array>;
|
|
1962
|
+
interface CompositeVectorConfig {
|
|
1963
|
+
strategies: Map<string, DomainVectorSearch>;
|
|
963
1964
|
embedding: EmbeddingProvider;
|
|
964
|
-
|
|
1965
|
+
/** Default K values per strategy name. Strategies not listed default to 0. */
|
|
1966
|
+
defaults?: Record<string, number>;
|
|
965
1967
|
}
|
|
966
|
-
declare class
|
|
967
|
-
private
|
|
968
|
-
|
|
969
|
-
|
|
1968
|
+
declare class CompositeVectorSearch implements SearchStrategy {
|
|
1969
|
+
private _c;
|
|
1970
|
+
/** Default K when no source override is provided. */
|
|
1971
|
+
private static readonly DEFAULT_K;
|
|
1972
|
+
constructor(_c: CompositeVectorConfig);
|
|
1973
|
+
/** Search across all registered domain strategies with score-based merge. */
|
|
970
1974
|
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
971
|
-
/** Vector search across code chunks. */
|
|
972
|
-
private _searchCode;
|
|
973
|
-
/** Vector search across git commits. */
|
|
974
|
-
private _searchGit;
|
|
975
|
-
/** Vector search across memory patterns. */
|
|
976
|
-
private _searchPatterns;
|
|
977
1975
|
}
|
|
978
1976
|
|
|
979
1977
|
/**
|
|
980
|
-
* BrainBank —
|
|
1978
|
+
* BrainBank — Composite BM25 Search Strategy
|
|
981
1979
|
*
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
* Uses Porter stemming + unicode61 tokenizer.
|
|
1980
|
+
* Generic BM25 coordinator that discovers BM25SearchPlugin instances
|
|
1981
|
+
* from the registry and delegates per-source keyword search.
|
|
985
1982
|
*/
|
|
986
1983
|
|
|
987
|
-
declare class
|
|
988
|
-
private
|
|
989
|
-
constructor(
|
|
1984
|
+
declare class CompositeBM25Search implements SearchStrategy {
|
|
1985
|
+
private _registry;
|
|
1986
|
+
constructor(_registry: PluginRegistry);
|
|
990
1987
|
/**
|
|
991
|
-
*
|
|
992
|
-
*
|
|
1988
|
+
* Run BM25 keyword search across all plugins that implement BM25SearchPlugin.
|
|
1989
|
+
* Each plugin searches its own FTS5 tables.
|
|
993
1990
|
*/
|
|
994
1991
|
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
995
|
-
/** FTS5
|
|
996
|
-
private _searchCode;
|
|
997
|
-
/** File-path fallback: match filenames via LIKE. */
|
|
998
|
-
private _searchCodeByPath;
|
|
999
|
-
/** FTS5 search across git commits. */
|
|
1000
|
-
private _searchGit;
|
|
1001
|
-
/** FTS5 search across memory patterns. */
|
|
1002
|
-
private _searchPatterns;
|
|
1003
|
-
/** Map a code_chunks row to a CodeResult. */
|
|
1004
|
-
private _toCodeResult;
|
|
1005
|
-
/** Rebuild the FTS index from scratch. */
|
|
1992
|
+
/** Rebuild FTS5 indices across all BM25 plugins. */
|
|
1006
1993
|
rebuild(): void;
|
|
1007
1994
|
}
|
|
1008
1995
|
|
|
@@ -1028,4 +2015,180 @@ declare class KeywordSearch implements SearchStrategy {
|
|
|
1028
2015
|
*/
|
|
1029
2016
|
declare function reciprocalRankFusion(resultSets: SearchResult[][], k?: number, maxResults?: number): SearchResult[];
|
|
1030
2017
|
|
|
1031
|
-
|
|
2018
|
+
/**
|
|
2019
|
+
* BrainBank — FTS Utilities
|
|
2020
|
+
*
|
|
2021
|
+
* Shared helpers for SQLite FTS5 query sanitization.
|
|
2022
|
+
*/
|
|
2023
|
+
/**
|
|
2024
|
+
* Sanitize a user query for FTS5 syntax.
|
|
2025
|
+
* Strips operators that would cause parse errors, splits compound words,
|
|
2026
|
+
* and converts words to implicit AND with exact-match quoting.
|
|
2027
|
+
*/
|
|
2028
|
+
declare function sanitizeFTS(query: string): string;
|
|
2029
|
+
/**
|
|
2030
|
+
* Normalize BM25 score from SQLite (negative, lower = better)
|
|
2031
|
+
* to 0.0–1.0 (higher = better) for consistency with vector search.
|
|
2032
|
+
*/
|
|
2033
|
+
declare function normalizeBM25(rawScore: number): number;
|
|
2034
|
+
/**
|
|
2035
|
+
* Escape SQL LIKE wildcard characters (`%`, `_`, `\`).
|
|
2036
|
+
* Use with `LIKE ? ESCAPE '\'` in queries.
|
|
2037
|
+
*/
|
|
2038
|
+
declare function escapeLike(s: string): string;
|
|
2039
|
+
|
|
2040
|
+
/**
|
|
2041
|
+
* BrainBank — Rerank
|
|
2042
|
+
*
|
|
2043
|
+
* Position-aware score blending between retrieval and reranker.
|
|
2044
|
+
* Pure function — no state.
|
|
2045
|
+
*
|
|
2046
|
+
* Top 1-3: 75% retrieval / 25% reranker (preserves exact matches)
|
|
2047
|
+
* Top 4-10: 60% retrieval / 40% reranker
|
|
2048
|
+
* Top 11+: 40% retrieval / 60% reranker (trust reranker more)
|
|
2049
|
+
*/
|
|
2050
|
+
|
|
2051
|
+
/** Re-rank results using position-aware blending. */
|
|
2052
|
+
declare function rerank(query: string, results: SearchResult[], reranker: Reranker): Promise<SearchResult[]>;
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* BrainBank — Database Metadata
|
|
2056
|
+
*
|
|
2057
|
+
* Helpers for reading/writing metadata stored in core SQLite tables:
|
|
2058
|
+
*
|
|
2059
|
+
* - **Index State** — cross-process HNSW version tracking.
|
|
2060
|
+
* Processes compare in-memory versions with DB to detect staleness
|
|
2061
|
+
* and trigger hot-reload via `ensureFresh()`.
|
|
2062
|
+
*
|
|
2063
|
+
* - **Embedding Meta** — tracks which embedding provider is stored in
|
|
2064
|
+
* the database. Detects dimension mismatches at startup and updates
|
|
2065
|
+
* metadata after `reembed()`.
|
|
2066
|
+
*/
|
|
2067
|
+
|
|
2068
|
+
/**
|
|
2069
|
+
* Increment the version for a given index name.
|
|
2070
|
+
* Sets writer_pid to current process PID.
|
|
2071
|
+
* Uses UPSERT so the row is created on first call.
|
|
2072
|
+
*/
|
|
2073
|
+
declare function bumpVersion(db: DatabaseAdapter, name: string): number;
|
|
2074
|
+
/**
|
|
2075
|
+
* Get all index versions as a Map.
|
|
2076
|
+
* Used by `ensureFresh()` to compare against in-memory versions.
|
|
2077
|
+
*/
|
|
2078
|
+
declare function getVersions(db: DatabaseAdapter): Map<string, number>;
|
|
2079
|
+
/** Get the version of a single index. Returns 0 if not found. */
|
|
2080
|
+
declare function getVersion(db: DatabaseAdapter, name: string): number;
|
|
2081
|
+
|
|
2082
|
+
/**
|
|
2083
|
+
* BrainBank — Write Lock
|
|
2084
|
+
*
|
|
2085
|
+
* Advisory file lock for cross-process HNSW write exclusion.
|
|
2086
|
+
* Uses `O_CREAT | O_EXCL` for atomic lock creation — works on all OS.
|
|
2087
|
+
* Stale locks (dead PID) are detected and stolen automatically.
|
|
2088
|
+
*/
|
|
2089
|
+
/**
|
|
2090
|
+
* Acquire an advisory lock. Blocks with exponential backoff if another
|
|
2091
|
+
* process holds the lock. Steals stale locks from dead processes.
|
|
2092
|
+
*
|
|
2093
|
+
* @throws After MAX_WAIT_MS if the lock cannot be acquired.
|
|
2094
|
+
*/
|
|
2095
|
+
declare function acquireLock(lockDir: string, name: string): Promise<void>;
|
|
2096
|
+
/** Release an advisory lock. Safe to call even if not held. */
|
|
2097
|
+
declare function releaseLock(lockDir: string, name: string): void;
|
|
2098
|
+
/**
|
|
2099
|
+
* Execute a function while holding an advisory lock.
|
|
2100
|
+
* Lock is always released, even on error.
|
|
2101
|
+
*/
|
|
2102
|
+
declare function withLock<T>(lockDir: string, name: string, fn: () => T | Promise<T>): Promise<T>;
|
|
2103
|
+
|
|
2104
|
+
/**
|
|
2105
|
+
* BrainBank — SQLite Adapter
|
|
2106
|
+
*
|
|
2107
|
+
* Implements `DatabaseAdapter` using `better-sqlite3`.
|
|
2108
|
+
* Drop-in replacement for the old `Database` class.
|
|
2109
|
+
* Handles WAL mode, directory creation, schema init, and transactions.
|
|
2110
|
+
*/
|
|
2111
|
+
|
|
2112
|
+
declare class SQLiteAdapter implements DatabaseAdapter {
|
|
2113
|
+
private _db;
|
|
2114
|
+
readonly capabilities: AdapterCapabilities;
|
|
2115
|
+
constructor(dbPath: string);
|
|
2116
|
+
/** Prepare a reusable statement. */
|
|
2117
|
+
prepare<T = unknown>(sql: string): PreparedStatement<T>;
|
|
2118
|
+
/** Execute raw SQL (no results). */
|
|
2119
|
+
exec(sql: string): void;
|
|
2120
|
+
/** Run a function inside a transaction. Auto-commits on success, auto-rollbacks on error. */
|
|
2121
|
+
transaction<T>(fn: () => T): T;
|
|
2122
|
+
/** Run a prepared statement on multiple rows. Wraps in a single transaction. */
|
|
2123
|
+
batch<T extends unknown[]>(sql: string, rows: T[]): void;
|
|
2124
|
+
/** Close the database. */
|
|
2125
|
+
close(): void;
|
|
2126
|
+
/**
|
|
2127
|
+
* Access the underlying `better-sqlite3` Database instance.
|
|
2128
|
+
*
|
|
2129
|
+
* @deprecated Use `DatabaseAdapter` methods instead. This exists
|
|
2130
|
+
* only for gradual migration of plugins that depend on driver internals.
|
|
2131
|
+
*/
|
|
2132
|
+
raw<T = unknown>(): T;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
/**
|
|
2136
|
+
* BrainBank — Brain Context
|
|
2137
|
+
*
|
|
2138
|
+
* Portable input for `createBrain()`. Decouples the factory from
|
|
2139
|
+
* `process.argv` / `process.env` so it can be called from the CLI,
|
|
2140
|
+
* MCP server, tests, or any programmatic consumer.
|
|
2141
|
+
*/
|
|
2142
|
+
/** Everything the factory needs to build a BrainBank instance. */
|
|
2143
|
+
interface BrainContext {
|
|
2144
|
+
/** Repository root path. */
|
|
2145
|
+
repoPath: string;
|
|
2146
|
+
/** Environment variable overrides. Falls back to `process.env`. */
|
|
2147
|
+
env?: Record<string, string | undefined>;
|
|
2148
|
+
/** CLI flag overrides (e.g. `{ ignore: 'dist,vendor' }`). */
|
|
2149
|
+
flags?: Record<string, string | undefined>;
|
|
2150
|
+
}
|
|
2151
|
+
/** Build a `BrainContext` from CLI argv + process.env. */
|
|
2152
|
+
declare function contextFromCLI(repoPath?: string): BrainContext;
|
|
2153
|
+
|
|
2154
|
+
/**
|
|
2155
|
+
* BrainBank CLI — Config Loader
|
|
2156
|
+
*
|
|
2157
|
+
* Loads .brainbank/config.json (or .ts/.js/.mjs fallback).
|
|
2158
|
+
* Config priority: CLI flags > config file > defaults.
|
|
2159
|
+
*/
|
|
2160
|
+
|
|
2161
|
+
/** Full .brainbank/config.json schema. */
|
|
2162
|
+
interface ProjectConfig {
|
|
2163
|
+
plugins?: string[];
|
|
2164
|
+
embedding?: string;
|
|
2165
|
+
reranker?: string;
|
|
2166
|
+
pruner?: string;
|
|
2167
|
+
maxFileSize?: number;
|
|
2168
|
+
indexers?: Plugin[];
|
|
2169
|
+
brainbank?: Partial<BrainBankConfig>;
|
|
2170
|
+
/** Context field defaults (e.g. { lines: true, callTree: true, symbols: false }). */
|
|
2171
|
+
context?: Record<string, unknown>;
|
|
2172
|
+
/** Per-plugin config sections (e.g. code, git, docs). */
|
|
2173
|
+
[pluginName: string]: unknown;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
/**
|
|
2177
|
+
* BrainBank CLI — Brain Factory
|
|
2178
|
+
*
|
|
2179
|
+
* Creates a configured BrainBank instance with dynamically loaded plugins,
|
|
2180
|
+
* auto-discovered indexers, and config file support.
|
|
2181
|
+
* Delegates to focused modules in factory/.
|
|
2182
|
+
*/
|
|
2183
|
+
|
|
2184
|
+
/** Reset factory caches. Useful for tests. */
|
|
2185
|
+
declare function resetFactoryCache(): void;
|
|
2186
|
+
/**
|
|
2187
|
+
* Create a BrainBank with built-in + discovered + config plugins.
|
|
2188
|
+
*
|
|
2189
|
+
* Accepts either a `BrainContext` (for programmatic use) or an optional
|
|
2190
|
+
* `repoPath` string (for CLI backward compat — builds context from argv).
|
|
2191
|
+
*/
|
|
2192
|
+
declare function createBrain(contextOrRepo?: BrainContext | string): Promise<BrainBank>;
|
|
2193
|
+
|
|
2194
|
+
export { type AdapterCapabilities, type BM25SearchPlugin, BrainBank, type BrainBankConfig, type BrainContext, type CoEditPlugin, type CoEditSuggestion, type CodeChunk, type CodeResult, type CodeResultMetadata, Collection, type CollectionAddOptions, type CollectionItem, type CollectionResult, type CollectionSearchOptions, type CommitResult, type CommitResultMetadata, CompositeBM25Search, CompositeVectorSearch, ContextBuilder, type ContextFieldDef, type ContextFieldPlugin, type ContextFormatterPlugin, type ContextOptions, type CountRow, DEFAULTS, DEFAULT_PORT, type DatabaseAdapter, type DocChunk, type DocsPlugin, type DocumentCollection, type DocumentResult, type DocumentResultMetadata, type DomainVectorSearch, type EmbeddingKey, type EmbeddingMetaRow, type EmbeddingProvider, EmbeddingWorkerProxy, type ExecuteResult, type ExpandablePlugin, type Expander, type ExpanderManifestItem, type ExpanderResult, type FileResolvablePlugin, type GitCommitRecord, HNSW, HNSWIndex, HaikuExpander, type HaikuExpanderOptions, HaikuPruner, type HaikuPrunerOptions, type HnswKey, HttpServer, type HttpServerOptions, IGNORE_DIRS, type IncrementalTracker, type IndexResult, type IndexablePlugin, KVService, type KvDataRow, type KvVectorRow, LocalEmbedding, type MigratablePlugin, type Migration, OpenAIEmbedding, type OpenAIEmbeddingOptions, PerplexityContextEmbedding, type PerplexityContextEmbeddingOptions, PerplexityEmbedding, type PerplexityEmbeddingOptions, type Plugin, type PluginContext, type PreparedStatement, type ProgressCallback, type ProjectConfig, type Pruner, type PrunerItem, Qwen3Reranker, type Qwen3RerankerOptions, type ReembedOptions, type ReembedResult, type ReembedTable, type ReembeddablePlugin, type Reranker, type ResolvedConfig, SQLiteAdapter, SUPPORTED_EXTENSIONS, type SearchHit, type SearchOptions, type SearchResult, type SearchResultType, type SearchStrategy, type SearchablePlugin, type StageProgressCallback, type VectorIndex, type VectorRow, type VectorSearchPlugin, type WatchConfig, type WatchEvent, type WatchEventHandler, type WatchHandle, type WatchOptions, type WatchablePlugin, Watcher, type WebhookHandler, WebhookServer, acquireLock, bumpVersion, contextFromCLI, cosineSimilarity, createBrain, createTracker, escapeLike, getLanguage, getServerUrl, getVersion, getVersions, isBM25SearchPlugin, isCoEditPlugin, isCodeResult, isCollectionResult, isCommitResult, isContextFieldPlugin, isContextFormatterPlugin, isDocsPlugin, isDocumentResult, isExpandablePlugin, isFileResolvable, isIgnoredDir, isIgnoredFile, isIndexable, isMigratable, isReembeddable, isSearchable, isServerRunning, isSupported, isVectorSearchPlugin, isWatchable, matchResult, normalize, normalizeBM25, providerKey, pruneResults, readPid, reciprocalRankFusion, releaseLock, removePid, rerank, resetFactoryCache, resolveConfig, resolveEmbedding, runPluginMigrations, sanitizeFTS, searchMMR, vecToBuffer, withLock, writePid };
|