brainbank 0.1.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/LICENSE +21 -0
- package/README.md +1059 -0
- package/assets/architecture.png +0 -0
- package/bin/brainbank +11 -0
- package/dist/chunk-2P3EGY6S.js +37 -0
- package/dist/chunk-2P3EGY6S.js.map +1 -0
- package/dist/chunk-3GAIDXRW.js +105 -0
- package/dist/chunk-3GAIDXRW.js.map +1 -0
- package/dist/chunk-4ZKBQ33J.js +56 -0
- package/dist/chunk-4ZKBQ33J.js.map +1 -0
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/chunk-7QVYU63E.js.map +1 -0
- package/dist/chunk-EDKSKLX4.js +490 -0
- package/dist/chunk-EDKSKLX4.js.map +1 -0
- package/dist/chunk-GOUBW7UA.js +373 -0
- package/dist/chunk-GOUBW7UA.js.map +1 -0
- package/dist/chunk-MJ3Y24H6.js +185 -0
- package/dist/chunk-MJ3Y24H6.js.map +1 -0
- package/dist/chunk-N6ZMBFDE.js +224 -0
- package/dist/chunk-N6ZMBFDE.js.map +1 -0
- package/dist/chunk-YGSEUWLV.js +2053 -0
- package/dist/chunk-YGSEUWLV.js.map +1 -0
- package/dist/chunk-Z5SU54HP.js +171 -0
- package/dist/chunk-Z5SU54HP.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +731 -0
- package/dist/cli.js.map +1 -0
- package/dist/code.d.ts +31 -0
- package/dist/code.js +8 -0
- package/dist/code.js.map +1 -0
- package/dist/docs.d.ts +19 -0
- package/dist/docs.js +8 -0
- package/dist/docs.js.map +1 -0
- package/dist/git.d.ts +31 -0
- package/dist/git.js +8 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +845 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/memory.d.ts +19 -0
- package/dist/memory.js +146 -0
- package/dist/memory.js.map +1 -0
- package/dist/notes.d.ts +19 -0
- package/dist/notes.js +57 -0
- package/dist/notes.js.map +1 -0
- package/dist/openai-PCTYLOWI.js +8 -0
- package/dist/openai-PCTYLOWI.js.map +1 -0
- package/dist/types-Da_zLLOl.d.ts +474 -0
- package/package.json +91 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name
|
|
3
|
+
} from "./chunk-7QVYU63E.js";
|
|
4
|
+
|
|
5
|
+
// src/indexers/code-indexer.ts
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path2 from "path";
|
|
8
|
+
|
|
9
|
+
// src/indexers/chunker.ts
|
|
10
|
+
var CodeChunker = class {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "CodeChunker");
|
|
13
|
+
}
|
|
14
|
+
MAX;
|
|
15
|
+
MIN;
|
|
16
|
+
OVERLAP;
|
|
17
|
+
constructor(config = {}) {
|
|
18
|
+
this.MAX = config.maxLines ?? 80;
|
|
19
|
+
this.MIN = config.minLines ?? 3;
|
|
20
|
+
this.OVERLAP = config.overlap ?? 5;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Split file content into semantic chunks.
|
|
24
|
+
* Small files (< maxLines) become a single chunk.
|
|
25
|
+
* For JS/TS/Python: detects functions and classes.
|
|
26
|
+
* For other languages: sliding window with overlap.
|
|
27
|
+
*/
|
|
28
|
+
chunk(filePath, content, language) {
|
|
29
|
+
const lines = content.split("\n");
|
|
30
|
+
if (lines.length <= this.MAX) {
|
|
31
|
+
return [{
|
|
32
|
+
filePath,
|
|
33
|
+
chunkType: "file",
|
|
34
|
+
startLine: 1,
|
|
35
|
+
endLine: lines.length,
|
|
36
|
+
content: content.trim(),
|
|
37
|
+
language
|
|
38
|
+
}];
|
|
39
|
+
}
|
|
40
|
+
switch (language) {
|
|
41
|
+
case "typescript":
|
|
42
|
+
case "javascript":
|
|
43
|
+
return this._chunkJS(filePath, lines, language);
|
|
44
|
+
case "python":
|
|
45
|
+
return this._chunkPython(filePath, lines, language);
|
|
46
|
+
default:
|
|
47
|
+
return this._chunkGeneric(filePath, lines, language);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ── JS / TS Strategy ────────────────────────────
|
|
51
|
+
_chunkJS(filePath, lines, language) {
|
|
52
|
+
const chunks = [];
|
|
53
|
+
const funcRe = /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/;
|
|
54
|
+
const constFuncRe = /^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\(/;
|
|
55
|
+
const classRe = /^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/;
|
|
56
|
+
const arrowRe = /^(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*\S+)?\s*=>/;
|
|
57
|
+
const interfaceRe = /^(?:export\s+)?(?:interface|type)\s+(\w+)/;
|
|
58
|
+
let i = 0;
|
|
59
|
+
while (i < lines.length) {
|
|
60
|
+
const line = lines[i].trim();
|
|
61
|
+
const fm = line.match(funcRe) || line.match(constFuncRe) || line.match(arrowRe);
|
|
62
|
+
const cm = line.match(classRe);
|
|
63
|
+
const im = line.match(interfaceRe);
|
|
64
|
+
if (fm || cm || im) {
|
|
65
|
+
const name = fm?.[1] || cm?.[1] || im?.[1] || "default";
|
|
66
|
+
const type = cm ? "class" : im ? "interface" : "function";
|
|
67
|
+
const start = i;
|
|
68
|
+
const end = this._findBlockEnd(lines, i);
|
|
69
|
+
if (end - start >= this.MIN) {
|
|
70
|
+
if (end - start > this.MAX) {
|
|
71
|
+
chunks.push(...this._splitLarge(filePath, lines, start, end, name, type, language));
|
|
72
|
+
} else {
|
|
73
|
+
const content = lines.slice(start, end + 1).join("\n").trim();
|
|
74
|
+
chunks.push({
|
|
75
|
+
filePath,
|
|
76
|
+
chunkType: type,
|
|
77
|
+
name,
|
|
78
|
+
startLine: start + 1,
|
|
79
|
+
endLine: end + 1,
|
|
80
|
+
content,
|
|
81
|
+
language
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
i = end + 1;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
i++;
|
|
89
|
+
}
|
|
90
|
+
if (chunks.length > 0) {
|
|
91
|
+
return chunks.filter((c) => c.content.length > 20);
|
|
92
|
+
}
|
|
93
|
+
return this._chunkGeneric(filePath, lines, language);
|
|
94
|
+
}
|
|
95
|
+
// ── Python Strategy ─────────────────────────────
|
|
96
|
+
_chunkPython(filePath, lines, language) {
|
|
97
|
+
const chunks = [];
|
|
98
|
+
let i = 0;
|
|
99
|
+
while (i < lines.length) {
|
|
100
|
+
const funcMatch = lines[i].match(/^(?:async\s+)?def\s+(\w+)/);
|
|
101
|
+
const classMatch = lines[i].match(/^class\s+(\w+)/);
|
|
102
|
+
if (funcMatch || classMatch) {
|
|
103
|
+
const name = funcMatch?.[1] || classMatch?.[1];
|
|
104
|
+
const type = classMatch ? "class" : "function";
|
|
105
|
+
const start = i;
|
|
106
|
+
const baseIndent = (lines[i].match(/^(\s*)/) ?? ["", ""])[1].length;
|
|
107
|
+
let end = i + 1;
|
|
108
|
+
while (end < lines.length) {
|
|
109
|
+
const line = lines[end];
|
|
110
|
+
if (line.trim() !== "") {
|
|
111
|
+
const indent = (line.match(/^(\s*)/) ?? ["", ""])[1].length;
|
|
112
|
+
if (indent <= baseIndent) break;
|
|
113
|
+
}
|
|
114
|
+
end++;
|
|
115
|
+
}
|
|
116
|
+
end = Math.min(end - 1, lines.length - 1);
|
|
117
|
+
if (end - start >= this.MIN) {
|
|
118
|
+
const content = lines.slice(start, end + 1).join("\n").trim();
|
|
119
|
+
chunks.push({
|
|
120
|
+
filePath,
|
|
121
|
+
chunkType: type,
|
|
122
|
+
name,
|
|
123
|
+
startLine: start + 1,
|
|
124
|
+
endLine: end + 1,
|
|
125
|
+
content,
|
|
126
|
+
language
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
i = end + 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
i++;
|
|
133
|
+
}
|
|
134
|
+
return chunks.length > 0 ? chunks : this._chunkGeneric(filePath, lines, language);
|
|
135
|
+
}
|
|
136
|
+
// ── Generic Strategy (sliding window) ───────────
|
|
137
|
+
_chunkGeneric(filePath, lines, language) {
|
|
138
|
+
const chunks = [];
|
|
139
|
+
const step = this.MAX - this.OVERLAP;
|
|
140
|
+
for (let s = 0; s < lines.length; s += step) {
|
|
141
|
+
const e = Math.min(s + this.MAX, lines.length);
|
|
142
|
+
const content = lines.slice(s, e).join("\n").trim();
|
|
143
|
+
if (content.length > 20) {
|
|
144
|
+
chunks.push({
|
|
145
|
+
filePath,
|
|
146
|
+
chunkType: "block",
|
|
147
|
+
startLine: s + 1,
|
|
148
|
+
endLine: e,
|
|
149
|
+
content,
|
|
150
|
+
language
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (e >= lines.length) break;
|
|
154
|
+
}
|
|
155
|
+
return chunks;
|
|
156
|
+
}
|
|
157
|
+
// ── Block End Detection (brace balance) ─────────
|
|
158
|
+
_findBlockEnd(lines, start) {
|
|
159
|
+
let depth = 0;
|
|
160
|
+
let foundOpen = false;
|
|
161
|
+
for (let i = start; i < lines.length; i++) {
|
|
162
|
+
for (const c of lines[i]) {
|
|
163
|
+
if (c === "{") {
|
|
164
|
+
depth++;
|
|
165
|
+
foundOpen = true;
|
|
166
|
+
}
|
|
167
|
+
if (c === "}") depth--;
|
|
168
|
+
}
|
|
169
|
+
if (foundOpen && depth === 0) return i;
|
|
170
|
+
}
|
|
171
|
+
return Math.min(start + this.MAX, lines.length - 1);
|
|
172
|
+
}
|
|
173
|
+
// ── Split Large Blocks ──────────────────────────
|
|
174
|
+
_splitLarge(filePath, lines, start, end, name, type, language) {
|
|
175
|
+
const chunks = [];
|
|
176
|
+
const step = this.MAX - this.OVERLAP;
|
|
177
|
+
let part = 1;
|
|
178
|
+
for (let s = start; s <= end; s += step) {
|
|
179
|
+
const e = Math.min(s + this.MAX, end + 1);
|
|
180
|
+
const content = lines.slice(s, e).join("\n").trim();
|
|
181
|
+
chunks.push({
|
|
182
|
+
filePath,
|
|
183
|
+
chunkType: type,
|
|
184
|
+
name: `${name} (part ${part++})`,
|
|
185
|
+
startLine: s + 1,
|
|
186
|
+
endLine: e,
|
|
187
|
+
content,
|
|
188
|
+
language
|
|
189
|
+
});
|
|
190
|
+
if (e > end) break;
|
|
191
|
+
}
|
|
192
|
+
return chunks;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// src/indexers/languages.ts
|
|
197
|
+
import path from "path";
|
|
198
|
+
var SUPPORTED_EXTENSIONS = {
|
|
199
|
+
// TypeScript / JavaScript
|
|
200
|
+
".ts": "typescript",
|
|
201
|
+
".tsx": "typescript",
|
|
202
|
+
".js": "javascript",
|
|
203
|
+
".jsx": "javascript",
|
|
204
|
+
".mjs": "javascript",
|
|
205
|
+
".cjs": "javascript",
|
|
206
|
+
// Systems
|
|
207
|
+
".go": "go",
|
|
208
|
+
".rs": "rust",
|
|
209
|
+
".cpp": "cpp",
|
|
210
|
+
".cc": "cpp",
|
|
211
|
+
".c": "c",
|
|
212
|
+
".h": "c",
|
|
213
|
+
".hpp": "cpp",
|
|
214
|
+
// JVM
|
|
215
|
+
".java": "java",
|
|
216
|
+
".kt": "kotlin",
|
|
217
|
+
".scala": "scala",
|
|
218
|
+
// Scripting
|
|
219
|
+
".py": "python",
|
|
220
|
+
".rb": "ruby",
|
|
221
|
+
".php": "php",
|
|
222
|
+
".lua": "lua",
|
|
223
|
+
".sh": "bash",
|
|
224
|
+
".bash": "bash",
|
|
225
|
+
".zsh": "bash",
|
|
226
|
+
// Web
|
|
227
|
+
".html": "html",
|
|
228
|
+
".css": "css",
|
|
229
|
+
".scss": "scss",
|
|
230
|
+
".less": "less",
|
|
231
|
+
".svelte": "svelte",
|
|
232
|
+
".vue": "vue",
|
|
233
|
+
// Data / Config
|
|
234
|
+
".json": "json",
|
|
235
|
+
".yaml": "yaml",
|
|
236
|
+
".yml": "yaml",
|
|
237
|
+
".toml": "toml",
|
|
238
|
+
".xml": "xml",
|
|
239
|
+
".graphql": "graphql",
|
|
240
|
+
".gql": "graphql",
|
|
241
|
+
// Docs
|
|
242
|
+
".md": "markdown",
|
|
243
|
+
".mdx": "markdown",
|
|
244
|
+
// Database
|
|
245
|
+
".sql": "sql",
|
|
246
|
+
".prisma": "prisma",
|
|
247
|
+
// Other
|
|
248
|
+
".swift": "swift",
|
|
249
|
+
".dart": "dart",
|
|
250
|
+
".r": "r",
|
|
251
|
+
".ex": "elixir",
|
|
252
|
+
".exs": "elixir",
|
|
253
|
+
".erl": "erlang",
|
|
254
|
+
".zig": "zig"
|
|
255
|
+
};
|
|
256
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
257
|
+
// Package managers
|
|
258
|
+
"node_modules",
|
|
259
|
+
"bower_components",
|
|
260
|
+
".pnpm",
|
|
261
|
+
// Build output
|
|
262
|
+
"dist",
|
|
263
|
+
"build",
|
|
264
|
+
"out",
|
|
265
|
+
".next",
|
|
266
|
+
".nuxt",
|
|
267
|
+
".output",
|
|
268
|
+
".svelte-kit",
|
|
269
|
+
// Version control
|
|
270
|
+
".git",
|
|
271
|
+
".hg",
|
|
272
|
+
".svn",
|
|
273
|
+
// IDE / Editor
|
|
274
|
+
".idea",
|
|
275
|
+
".vscode",
|
|
276
|
+
// Runtime / Cache
|
|
277
|
+
"__pycache__",
|
|
278
|
+
".pytest_cache",
|
|
279
|
+
"venv",
|
|
280
|
+
".venv",
|
|
281
|
+
".env",
|
|
282
|
+
".tox",
|
|
283
|
+
// Coverage / Test artifacts
|
|
284
|
+
"coverage",
|
|
285
|
+
".nyc_output",
|
|
286
|
+
"htmlcov",
|
|
287
|
+
// Compiled
|
|
288
|
+
"target",
|
|
289
|
+
// Rust, Java
|
|
290
|
+
".cargo",
|
|
291
|
+
"vendor",
|
|
292
|
+
// Go, PHP
|
|
293
|
+
// AI / Model cache
|
|
294
|
+
".model-cache",
|
|
295
|
+
".brainbank",
|
|
296
|
+
// OS
|
|
297
|
+
".DS_Store"
|
|
298
|
+
]);
|
|
299
|
+
var IGNORE_FILES = /* @__PURE__ */ new Set([
|
|
300
|
+
"package-lock.json",
|
|
301
|
+
"yarn.lock",
|
|
302
|
+
"pnpm-lock.yaml",
|
|
303
|
+
"bun.lockb",
|
|
304
|
+
"Cargo.lock",
|
|
305
|
+
"Gemfile.lock",
|
|
306
|
+
"poetry.lock",
|
|
307
|
+
"composer.lock",
|
|
308
|
+
"go.sum"
|
|
309
|
+
]);
|
|
310
|
+
function isSupported(filePath) {
|
|
311
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
312
|
+
return ext in SUPPORTED_EXTENSIONS;
|
|
313
|
+
}
|
|
314
|
+
__name(isSupported, "isSupported");
|
|
315
|
+
function getLanguage(filePath) {
|
|
316
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
317
|
+
return SUPPORTED_EXTENSIONS[ext];
|
|
318
|
+
}
|
|
319
|
+
__name(getLanguage, "getLanguage");
|
|
320
|
+
function isIgnoredDir(dirName) {
|
|
321
|
+
return IGNORE_DIRS.has(dirName) || dirName.startsWith(".");
|
|
322
|
+
}
|
|
323
|
+
__name(isIgnoredDir, "isIgnoredDir");
|
|
324
|
+
function isIgnoredFile(fileName) {
|
|
325
|
+
return IGNORE_FILES.has(fileName);
|
|
326
|
+
}
|
|
327
|
+
__name(isIgnoredFile, "isIgnoredFile");
|
|
328
|
+
|
|
329
|
+
// src/indexers/code-indexer.ts
|
|
330
|
+
var CodeIndexer = class {
|
|
331
|
+
static {
|
|
332
|
+
__name(this, "CodeIndexer");
|
|
333
|
+
}
|
|
334
|
+
_chunker = new CodeChunker();
|
|
335
|
+
_deps;
|
|
336
|
+
_repoPath;
|
|
337
|
+
_maxFileSize;
|
|
338
|
+
constructor(repoPath, deps, maxFileSize = 512e3) {
|
|
339
|
+
this._deps = deps;
|
|
340
|
+
this._repoPath = repoPath;
|
|
341
|
+
this._maxFileSize = maxFileSize;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Index all supported files in the repository.
|
|
345
|
+
* Skips unchanged files (same content hash).
|
|
346
|
+
*/
|
|
347
|
+
async index(options = {}) {
|
|
348
|
+
const { forceReindex = false, onProgress } = options;
|
|
349
|
+
const files = this._walkRepo(this._repoPath);
|
|
350
|
+
let indexed = 0, skipped = 0, totalChunks = 0;
|
|
351
|
+
for (let i = 0; i < files.length; i++) {
|
|
352
|
+
const filePath = files[i];
|
|
353
|
+
const rel = path2.relative(this._repoPath, filePath);
|
|
354
|
+
onProgress?.(rel, i + 1, files.length);
|
|
355
|
+
let content;
|
|
356
|
+
try {
|
|
357
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
358
|
+
} catch {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const hash = this._hash(content);
|
|
362
|
+
const existing = this._deps.db.prepare(
|
|
363
|
+
"SELECT file_hash FROM indexed_files WHERE file_path = ?"
|
|
364
|
+
).get(rel);
|
|
365
|
+
if (!forceReindex && existing?.file_hash === hash) {
|
|
366
|
+
skipped++;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (existing) {
|
|
370
|
+
this._deps.db.prepare("DELETE FROM code_chunks WHERE file_path = ?").run(rel);
|
|
371
|
+
}
|
|
372
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
373
|
+
const language = SUPPORTED_EXTENSIONS[ext] ?? "text";
|
|
374
|
+
const chunks = this._chunker.chunk(rel, content, language);
|
|
375
|
+
for (const chunk of chunks) {
|
|
376
|
+
const text = [
|
|
377
|
+
`File: ${rel}`,
|
|
378
|
+
chunk.name ? `${chunk.chunkType}: ${chunk.name}` : chunk.chunkType,
|
|
379
|
+
chunk.content
|
|
380
|
+
].join("\n");
|
|
381
|
+
const vec = await this._deps.embedding.embed(text);
|
|
382
|
+
const result = this._deps.db.prepare(
|
|
383
|
+
`INSERT INTO code_chunks (file_path, chunk_type, name, start_line, end_line, content, language, file_hash)
|
|
384
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
385
|
+
).run(rel, chunk.chunkType, chunk.name ?? null, chunk.startLine, chunk.endLine, chunk.content, language, hash);
|
|
386
|
+
const id = Number(result.lastInsertRowid);
|
|
387
|
+
this._deps.db.prepare(
|
|
388
|
+
"INSERT INTO code_vectors (chunk_id, embedding) VALUES (?, ?)"
|
|
389
|
+
).run(id, Buffer.from(vec.buffer));
|
|
390
|
+
this._deps.hnsw.add(vec, id);
|
|
391
|
+
this._deps.vectorCache.set(id, vec);
|
|
392
|
+
totalChunks++;
|
|
393
|
+
}
|
|
394
|
+
this._deps.db.prepare(
|
|
395
|
+
"INSERT OR REPLACE INTO indexed_files (file_path, file_hash) VALUES (?, ?)"
|
|
396
|
+
).run(rel, hash);
|
|
397
|
+
indexed++;
|
|
398
|
+
}
|
|
399
|
+
return { indexed, skipped, chunks: totalChunks };
|
|
400
|
+
}
|
|
401
|
+
// ── File Walker ─────────────────────────────────
|
|
402
|
+
_walkRepo(dir, files = []) {
|
|
403
|
+
let entries;
|
|
404
|
+
try {
|
|
405
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
406
|
+
} catch {
|
|
407
|
+
return files;
|
|
408
|
+
}
|
|
409
|
+
for (const entry of entries) {
|
|
410
|
+
if (entry.isDirectory()) {
|
|
411
|
+
if (isIgnoredDir(entry.name)) continue;
|
|
412
|
+
this._walkRepo(path2.join(dir, entry.name), files);
|
|
413
|
+
} else if (entry.isFile()) {
|
|
414
|
+
if (isIgnoredFile(entry.name)) continue;
|
|
415
|
+
const ext = path2.extname(entry.name).toLowerCase();
|
|
416
|
+
if (!(ext in SUPPORTED_EXTENSIONS)) continue;
|
|
417
|
+
const full = path2.join(dir, entry.name);
|
|
418
|
+
try {
|
|
419
|
+
if (fs.statSync(full).size <= this._maxFileSize) {
|
|
420
|
+
files.push(full);
|
|
421
|
+
}
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return files;
|
|
427
|
+
}
|
|
428
|
+
// ── FNV-1a Hash ─────────────────────────────────
|
|
429
|
+
_hash(content) {
|
|
430
|
+
let h = 2166136261;
|
|
431
|
+
for (let i = 0; i < content.length; i++) {
|
|
432
|
+
h ^= content.charCodeAt(i);
|
|
433
|
+
h = h * 16777619 >>> 0;
|
|
434
|
+
}
|
|
435
|
+
return h.toString(16);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// src/plugins/code.ts
|
|
440
|
+
var CodeModuleImpl = class {
|
|
441
|
+
constructor(opts = {}) {
|
|
442
|
+
this.opts = opts;
|
|
443
|
+
this.name = opts.name ?? "code";
|
|
444
|
+
}
|
|
445
|
+
static {
|
|
446
|
+
__name(this, "CodeModuleImpl");
|
|
447
|
+
}
|
|
448
|
+
name;
|
|
449
|
+
hnsw;
|
|
450
|
+
indexer;
|
|
451
|
+
vecCache = /* @__PURE__ */ new Map();
|
|
452
|
+
async initialize(ctx) {
|
|
453
|
+
const shared = await ctx.getOrCreateSharedHnsw("code");
|
|
454
|
+
this.hnsw = shared.hnsw;
|
|
455
|
+
this.vecCache = shared.vecCache;
|
|
456
|
+
if (shared.isNew) {
|
|
457
|
+
ctx.loadVectors("code_vectors", "chunk_id", this.hnsw, this.vecCache);
|
|
458
|
+
}
|
|
459
|
+
const repoPath = this.opts.repoPath ?? ctx.config.repoPath;
|
|
460
|
+
this.indexer = new CodeIndexer(repoPath, {
|
|
461
|
+
db: ctx.db,
|
|
462
|
+
hnsw: this.hnsw,
|
|
463
|
+
vectorCache: this.vecCache,
|
|
464
|
+
embedding: ctx.embedding
|
|
465
|
+
}, this.opts.maxFileSize ?? ctx.config.maxFileSize);
|
|
466
|
+
}
|
|
467
|
+
async index(options = {}) {
|
|
468
|
+
return this.indexer.index(options);
|
|
469
|
+
}
|
|
470
|
+
stats() {
|
|
471
|
+
return { hnswSize: this.hnsw.size };
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
function code(opts) {
|
|
475
|
+
return new CodeModuleImpl(opts);
|
|
476
|
+
}
|
|
477
|
+
__name(code, "code");
|
|
478
|
+
|
|
479
|
+
export {
|
|
480
|
+
SUPPORTED_EXTENSIONS,
|
|
481
|
+
IGNORE_DIRS,
|
|
482
|
+
isSupported,
|
|
483
|
+
getLanguage,
|
|
484
|
+
isIgnoredDir,
|
|
485
|
+
isIgnoredFile,
|
|
486
|
+
CodeChunker,
|
|
487
|
+
CodeIndexer,
|
|
488
|
+
code
|
|
489
|
+
};
|
|
490
|
+
//# sourceMappingURL=chunk-EDKSKLX4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/indexers/code-indexer.ts","../src/indexers/chunker.ts","../src/indexers/languages.ts","../src/plugins/code.ts"],"sourcesContent":["/**\n * BrainBank — Code Indexer\n * \n * Walks a repository, chunks source files semantically,\n * embeds each chunk, and stores in SQLite + HNSW.\n * Incremental: only re-indexes files that changed (by content hash).\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { CodeChunker } from './chunker.ts';\nimport { SUPPORTED_EXTENSIONS, IGNORE_DIRS, isIgnoredDir, isIgnoredFile } from './languages.ts';\nimport type { Database } from '../storage/database.ts';\nimport type { EmbeddingProvider, ProgressCallback, IndexResult } from '../types.ts';\nimport type { HNSWIndex } from '../vector/hnsw.ts';\n\nexport interface CodeIndexerDeps {\n db: Database;\n hnsw: HNSWIndex;\n vectorCache: Map<number, Float32Array>;\n embedding: EmbeddingProvider;\n}\n\nexport interface CodeIndexOptions {\n forceReindex?: boolean;\n onProgress?: ProgressCallback;\n}\n\nexport class CodeIndexer {\n private _chunker = new CodeChunker();\n private _deps: CodeIndexerDeps;\n private _repoPath: string;\n private _maxFileSize: number;\n\n constructor(repoPath: string, deps: CodeIndexerDeps, maxFileSize: number = 512_000) {\n this._deps = deps;\n this._repoPath = repoPath;\n this._maxFileSize = maxFileSize;\n }\n\n /**\n * Index all supported files in the repository.\n * Skips unchanged files (same content hash).\n */\n async index(options: CodeIndexOptions = {}): Promise<IndexResult> {\n const { forceReindex = false, onProgress } = options;\n const files = this._walkRepo(this._repoPath);\n let indexed = 0, skipped = 0, totalChunks = 0;\n\n for (let i = 0; i < files.length; i++) {\n const filePath = files[i];\n const rel = path.relative(this._repoPath, filePath);\n onProgress?.(rel, i + 1, files.length);\n\n let content: string;\n try { content = fs.readFileSync(filePath, 'utf-8'); }\n catch { continue; }\n\n const hash = this._hash(content);\n const existing = this._deps.db.prepare(\n 'SELECT file_hash FROM indexed_files WHERE file_path = ?'\n ).get(rel) as any;\n\n if (!forceReindex && existing?.file_hash === hash) {\n skipped++;\n continue;\n }\n\n // Delete old chunks if re-indexing\n if (existing) {\n this._deps.db.prepare('DELETE FROM code_chunks WHERE file_path = ?').run(rel);\n }\n\n const ext = path.extname(filePath).toLowerCase();\n const language = SUPPORTED_EXTENSIONS[ext] ?? 'text';\n const chunks = this._chunker.chunk(rel, content, language);\n\n for (const chunk of chunks) {\n // Build embedding text with file context\n const text = [\n `File: ${rel}`,\n chunk.name ? `${chunk.chunkType}: ${chunk.name}` : chunk.chunkType,\n chunk.content,\n ].join('\\n');\n\n const vec = await this._deps.embedding.embed(text);\n\n const result = this._deps.db.prepare(\n `INSERT INTO code_chunks (file_path, chunk_type, name, start_line, end_line, content, language, file_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n ).run(rel, chunk.chunkType, chunk.name ?? null, chunk.startLine, chunk.endLine, chunk.content, language, hash);\n\n const id = Number(result.lastInsertRowid);\n this._deps.db.prepare(\n 'INSERT INTO code_vectors (chunk_id, embedding) VALUES (?, ?)'\n ).run(id, Buffer.from(vec.buffer));\n\n this._deps.hnsw.add(vec, id);\n this._deps.vectorCache.set(id, vec);\n totalChunks++;\n }\n\n // Mark file as indexed\n this._deps.db.prepare(\n 'INSERT OR REPLACE INTO indexed_files (file_path, file_hash) VALUES (?, ?)'\n ).run(rel, hash);\n indexed++;\n }\n\n return { indexed, skipped, chunks: totalChunks };\n }\n\n // ── File Walker ─────────────────────────────────\n\n private _walkRepo(dir: string, files: string[] = []): string[] {\n let entries: fs.Dirent[];\n try { entries = fs.readdirSync(dir, { withFileTypes: true }); }\n catch { return files; }\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (isIgnoredDir(entry.name)) continue;\n this._walkRepo(path.join(dir, entry.name), files);\n } else if (entry.isFile()) {\n if (isIgnoredFile(entry.name)) continue;\n const ext = path.extname(entry.name).toLowerCase();\n if (!(ext in SUPPORTED_EXTENSIONS)) continue;\n\n const full = path.join(dir, entry.name);\n try {\n if (fs.statSync(full).size <= this._maxFileSize) {\n files.push(full);\n }\n } catch {}\n }\n }\n return files;\n }\n\n // ── FNV-1a Hash ─────────────────────────────────\n\n private _hash(content: string): string {\n let h = 2166136261;\n for (let i = 0; i < content.length; i++) {\n h ^= content.charCodeAt(i);\n h = (h * 16777619) >>> 0;\n }\n return h.toString(16);\n }\n}\n","/**\n * BrainBank — Code Chunker\n * \n * Language-aware code splitting into semantic blocks.\n * Detects functions, classes, and methods using regex + brace balancing.\n * Falls back to sliding window for unsupported languages.\n */\n\nimport type { CodeChunk } from '../types.ts';\n\n// ── Configuration ───────────────────────────────────\n\nexport interface ChunkerConfig {\n /** Max lines per chunk. Default: 80 */\n maxLines?: number;\n /** Min lines for a detected block to be a chunk. Default: 3 */\n minLines?: number;\n /** Overlap between adjacent generic chunks. Default: 5 */\n overlap?: number;\n}\n\n// ── CodeChunker ─────────────────────────────────────\n\nexport class CodeChunker {\n private MAX: number;\n private MIN: number;\n private OVERLAP: number;\n\n constructor(config: ChunkerConfig = {}) {\n this.MAX = config.maxLines ?? 80;\n this.MIN = config.minLines ?? 3;\n this.OVERLAP = config.overlap ?? 5;\n }\n\n /**\n * Split file content into semantic chunks.\n * Small files (< maxLines) become a single chunk.\n * For JS/TS/Python: detects functions and classes.\n * For other languages: sliding window with overlap.\n */\n chunk(filePath: string, content: string, language: string): CodeChunk[] {\n const lines = content.split('\\n');\n\n // Small file → single chunk\n if (lines.length <= this.MAX) {\n return [{\n filePath,\n chunkType: 'file',\n startLine: 1,\n endLine: lines.length,\n content: content.trim(),\n language,\n }];\n }\n\n switch (language) {\n case 'typescript':\n case 'javascript':\n return this._chunkJS(filePath, lines, language);\n case 'python':\n return this._chunkPython(filePath, lines, language);\n default:\n return this._chunkGeneric(filePath, lines, language);\n }\n }\n\n // ── JS / TS Strategy ────────────────────────────\n\n private _chunkJS(filePath: string, lines: string[], language: string): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const funcRe = /^(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)/;\n const constFuncRe = /^(?:export\\s+)?(?:const|let|var)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?\\(/;\n const classRe = /^(?:export\\s+)?(?:abstract\\s+)?class\\s+(\\w+)/;\n const arrowRe = /^(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?\\([^)]*\\)\\s*(?::\\s*\\S+)?\\s*=>/;\n const interfaceRe = /^(?:export\\s+)?(?:interface|type)\\s+(\\w+)/;\n\n let i = 0;\n while (i < lines.length) {\n const line = lines[i].trim();\n const fm = line.match(funcRe) || line.match(constFuncRe) || line.match(arrowRe);\n const cm = line.match(classRe);\n const im = line.match(interfaceRe);\n\n if (fm || cm || im) {\n const name = fm?.[1] || cm?.[1] || im?.[1] || 'default';\n const type = cm ? 'class' : im ? 'interface' : 'function';\n const start = i;\n const end = this._findBlockEnd(lines, i);\n\n if (end - start >= this.MIN) {\n if (end - start > this.MAX) {\n chunks.push(...this._splitLarge(filePath, lines, start, end, name, type, language));\n } else {\n const content = lines.slice(start, end + 1).join('\\n').trim();\n chunks.push({\n filePath,\n chunkType: type,\n name,\n startLine: start + 1,\n endLine: end + 1,\n content,\n language,\n });\n }\n i = end + 1;\n continue;\n }\n }\n i++;\n }\n\n // If we found semantic chunks, filter noise and return\n if (chunks.length > 0) {\n return chunks.filter(c => c.content.length > 20);\n }\n\n // Fallback to generic\n return this._chunkGeneric(filePath, lines, language);\n }\n\n // ── Python Strategy ─────────────────────────────\n\n private _chunkPython(filePath: string, lines: string[], language: string): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n let i = 0;\n\n while (i < lines.length) {\n const funcMatch = lines[i].match(/^(?:async\\s+)?def\\s+(\\w+)/);\n const classMatch = lines[i].match(/^class\\s+(\\w+)/);\n\n if (funcMatch || classMatch) {\n const name = funcMatch?.[1] || classMatch?.[1]!;\n const type = classMatch ? 'class' : 'function';\n const start = i;\n const baseIndent = (lines[i].match(/^(\\s*)/) ?? ['', ''])[1].length;\n\n let end = i + 1;\n while (end < lines.length) {\n const line = lines[end];\n if (line.trim() !== '') {\n const indent = (line.match(/^(\\s*)/) ?? ['', ''])[1].length;\n if (indent <= baseIndent) break;\n }\n end++;\n }\n end = Math.min(end - 1, lines.length - 1);\n\n if (end - start >= this.MIN) {\n const content = lines.slice(start, end + 1).join('\\n').trim();\n chunks.push({\n filePath,\n chunkType: type,\n name,\n startLine: start + 1,\n endLine: end + 1,\n content,\n language,\n });\n }\n i = end + 1;\n continue;\n }\n i++;\n }\n\n return chunks.length > 0 ? chunks : this._chunkGeneric(filePath, lines, language);\n }\n\n // ── Generic Strategy (sliding window) ───────────\n\n private _chunkGeneric(filePath: string, lines: string[], language: string): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const step = this.MAX - this.OVERLAP;\n\n for (let s = 0; s < lines.length; s += step) {\n const e = Math.min(s + this.MAX, lines.length);\n const content = lines.slice(s, e).join('\\n').trim();\n if (content.length > 20) {\n chunks.push({\n filePath,\n chunkType: 'block',\n startLine: s + 1,\n endLine: e,\n content,\n language,\n });\n }\n if (e >= lines.length) break;\n }\n\n return chunks;\n }\n\n // ── Block End Detection (brace balance) ─────────\n\n private _findBlockEnd(lines: string[], start: number): number {\n let depth = 0;\n let foundOpen = false;\n\n for (let i = start; i < lines.length; i++) {\n for (const c of lines[i]) {\n if (c === '{') { depth++; foundOpen = true; }\n if (c === '}') depth--;\n }\n if (foundOpen && depth === 0) return i;\n }\n\n // No brace-balanced end found — take up to MAX lines\n return Math.min(start + this.MAX, lines.length - 1);\n }\n\n // ── Split Large Blocks ──────────────────────────\n\n private _splitLarge(\n filePath: string,\n lines: string[],\n start: number,\n end: number,\n name: string,\n type: string,\n language: string,\n ): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const step = this.MAX - this.OVERLAP;\n let part = 1;\n\n for (let s = start; s <= end; s += step) {\n const e = Math.min(s + this.MAX, end + 1);\n const content = lines.slice(s, e).join('\\n').trim();\n chunks.push({\n filePath,\n chunkType: type,\n name: `${name} (part ${part++})`,\n startLine: s + 1,\n endLine: e,\n content,\n language,\n });\n if (e > end) break;\n }\n\n return chunks;\n }\n}\n","/**\n * BrainBank — Language Registry\n * \n * Supported file extensions, language mappings, and ignore lists.\n * Controls which files get indexed and how they're chunked.\n */\n\n// ── Supported Extensions ────────────────────────────\n\nexport const SUPPORTED_EXTENSIONS: Record<string, string> = {\n // TypeScript / JavaScript\n '.ts': 'typescript',\n '.tsx': 'typescript',\n '.js': 'javascript',\n '.jsx': 'javascript',\n '.mjs': 'javascript',\n '.cjs': 'javascript',\n\n // Systems\n '.go': 'go',\n '.rs': 'rust',\n '.cpp': 'cpp',\n '.cc': 'cpp',\n '.c': 'c',\n '.h': 'c',\n '.hpp': 'cpp',\n\n // JVM\n '.java': 'java',\n '.kt': 'kotlin',\n '.scala': 'scala',\n\n // Scripting\n '.py': 'python',\n '.rb': 'ruby',\n '.php': 'php',\n '.lua': 'lua',\n '.sh': 'bash',\n '.bash': 'bash',\n '.zsh': 'bash',\n\n // Web\n '.html': 'html',\n '.css': 'css',\n '.scss': 'scss',\n '.less': 'less',\n '.svelte': 'svelte',\n '.vue': 'vue',\n\n // Data / Config\n '.json': 'json',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n '.toml': 'toml',\n '.xml': 'xml',\n '.graphql': 'graphql',\n '.gql': 'graphql',\n\n // Docs\n '.md': 'markdown',\n '.mdx': 'markdown',\n\n // Database\n '.sql': 'sql',\n '.prisma': 'prisma',\n\n // Other\n '.swift': 'swift',\n '.dart': 'dart',\n '.r': 'r',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.erl': 'erlang',\n '.zig': 'zig',\n};\n\n// ── Ignore Directories ──────────────────────────────\n\nexport const IGNORE_DIRS = new Set([\n // Package managers\n 'node_modules',\n 'bower_components',\n '.pnpm',\n\n // Build output\n 'dist',\n 'build',\n 'out',\n '.next',\n '.nuxt',\n '.output',\n '.svelte-kit',\n\n // Version control\n '.git',\n '.hg',\n '.svn',\n\n // IDE / Editor\n '.idea',\n '.vscode',\n\n // Runtime / Cache\n '__pycache__',\n '.pytest_cache',\n 'venv',\n '.venv',\n '.env',\n '.tox',\n\n // Coverage / Test artifacts\n 'coverage',\n '.nyc_output',\n 'htmlcov',\n\n // Compiled\n 'target', // Rust, Java\n '.cargo',\n 'vendor', // Go, PHP\n\n // AI / Model cache\n '.model-cache',\n '.brainbank',\n\n // OS\n '.DS_Store',\n]);\n\n// ── Ignore Files ────────────────────────────────────\n\nexport const IGNORE_FILES = new Set([\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n 'bun.lockb',\n 'Cargo.lock',\n 'Gemfile.lock',\n 'poetry.lock',\n 'composer.lock',\n 'go.sum',\n]);\n\n// ── Helpers ─────────────────────────────────────────\n\nimport path from 'node:path';\n\n/** Check if a file extension is supported for indexing. */\nexport function isSupported(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return ext in SUPPORTED_EXTENSIONS;\n}\n\n/** Get the language name for a file. Returns undefined if not supported. */\nexport function getLanguage(filePath: string): string | undefined {\n const ext = path.extname(filePath).toLowerCase();\n return SUPPORTED_EXTENSIONS[ext];\n}\n\n/** Check if a directory name should be ignored. */\nexport function isIgnoredDir(dirName: string): boolean {\n return IGNORE_DIRS.has(dirName) || dirName.startsWith('.');\n}\n\n/** Check if a filename should be ignored. */\nexport function isIgnoredFile(fileName: string): boolean {\n return IGNORE_FILES.has(fileName);\n}\n","/**\n * BrainBank — Code Module\n * \n * Language-aware code indexing for 30+ languages.\n * \n * import { BrainBank } from 'brainbank';\n * import { code } from 'brainbank/code';\n * \n * const brain = new BrainBank().use(code({ repoPath: '.' }));\n * \n * // Multi-repo: namespace to avoid key collisions\n * brain\n * .use(code({ repoPath: './frontend', name: 'code:frontend' }))\n * .use(code({ repoPath: './backend', name: 'code:backend' }));\n */\n\nimport type { BrainBankModule, ModuleContext } from './types.ts';\nimport type { HNSWIndex } from '../vector/hnsw.ts';\nimport { CodeIndexer } from '../indexers/code-indexer.ts';\nimport type { IndexResult, ProgressCallback } from '../types.ts';\n\nexport interface CodeModuleOptions {\n /** Repository path to index. Default: '.' */\n repoPath?: string;\n /** Maximum file size in bytes. Default: from config */\n maxFileSize?: number;\n /** Custom indexer name for multi-repo (e.g. 'code:frontend'). Default: 'code' */\n name?: string;\n}\n\nclass CodeModuleImpl implements BrainBankModule {\n readonly name: string;\n hnsw!: HNSWIndex;\n indexer!: CodeIndexer;\n vecCache = new Map<number, Float32Array>();\n\n constructor(private opts: CodeModuleOptions = {}) {\n this.name = opts.name ?? 'code';\n }\n\n async initialize(ctx: ModuleContext): Promise<void> {\n // Use shared HNSW so all code indexers (code, code:frontend, etc.) share one index\n const shared = await ctx.getOrCreateSharedHnsw('code');\n this.hnsw = shared.hnsw;\n this.vecCache = shared.vecCache;\n\n // Only load vectors once (first code indexer to initialize)\n if (shared.isNew) {\n ctx.loadVectors('code_vectors', 'chunk_id', this.hnsw, this.vecCache);\n }\n\n const repoPath = this.opts.repoPath ?? ctx.config.repoPath;\n this.indexer = new CodeIndexer(repoPath, {\n db: ctx.db,\n hnsw: this.hnsw,\n vectorCache: this.vecCache,\n embedding: ctx.embedding,\n }, this.opts.maxFileSize ?? ctx.config.maxFileSize);\n }\n\n async index(options: {\n forceReindex?: boolean;\n onProgress?: ProgressCallback;\n } = {}): Promise<IndexResult> {\n return this.indexer.index(options);\n }\n\n stats(): Record<string, any> {\n return { hnswSize: this.hnsw.size };\n }\n}\n\n/** Create a code indexing module. */\nexport function code(opts?: CodeModuleOptions): BrainBankModule {\n return new CodeModuleImpl(opts);\n}\n"],"mappings":"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","names":["path","path"]}
|