agentsbestfriend 0.2.0 → 0.4.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/dist/index.js CHANGED
@@ -11030,368 +11030,214 @@ var init_manager = __esm({
11030
11030
  }
11031
11031
  });
11032
11032
 
11033
- // ../core/dist/config/index.js
11034
- var config_exports = {};
11035
- __export(config_exports, {
11036
- configSchema: () => configSchema,
11037
- createDefaultConfig: () => createDefaultConfig,
11038
- getAbfHome: () => getAbfHome,
11039
- getConfigPath: () => getConfigPath,
11040
- loadConfig: () => loadConfig,
11041
- saveConfig: () => saveConfig,
11042
- updateConfig: () => updateConfig
11043
- });
11044
- var init_config = __esm({
11045
- "../core/dist/config/index.js"() {
11046
- "use strict";
11047
- init_schema();
11048
- init_manager();
11049
- }
11050
- });
11051
-
11052
- // ../core/dist/search/ripgrep.js
11053
- import { execFile } from "child_process";
11054
- import { promisify } from "util";
11055
- async function ripgrepSearch(opts) {
11056
- const config2 = loadConfig();
11057
- const rgPath = config2.search.ripgrepPath;
11058
- const maxResults = opts.maxResults ?? config2.search.defaultMaxResults;
11059
- const contextLines = opts.contextLines ?? 2;
11060
- const args = [
11061
- "--json",
11062
- "--max-count",
11063
- String(maxResults * 2),
11064
- // get extra so we have room after filtering
11065
- "--context",
11066
- String(contextLines)
11067
- ];
11068
- if (!opts.caseSensitive) {
11069
- args.push("--ignore-case");
11070
- }
11071
- if (!opts.regex) {
11072
- args.push("--fixed-strings");
11073
- }
11074
- if (opts.pathFilter) {
11075
- args.push("--glob", opts.pathFilter);
11076
- }
11077
- args.push("--hidden");
11078
- args.push("--glob", "!.git");
11079
- args.push("--glob", "!.abf");
11080
- args.push("--glob", "!node_modules");
11081
- args.push("--glob", "!.next");
11082
- args.push("--glob", "!dist");
11083
- args.push("--glob", "!build");
11084
- args.push("--glob", "!*.min.js");
11085
- args.push("--glob", "!*.min.css");
11086
- args.push("--glob", "!*.map");
11087
- args.push("--glob", "!package-lock.json");
11088
- args.push("--glob", "!yarn.lock");
11089
- args.push("--glob", "!pnpm-lock.yaml");
11090
- args.push("--", opts.query, ".");
11091
- try {
11092
- const { stdout } = await execFileAsync(rgPath, args, {
11093
- cwd: opts.cwd,
11094
- maxBuffer: 10 * 1024 * 1024
11095
- // 10MB
11096
- });
11097
- return parseRipgrepJson(stdout, maxResults);
11098
- } catch (error48) {
11099
- if (error48.code === 1 || error48.exitCode === 1) {
11100
- return { matches: [], totalMatches: 0, truncated: false };
11101
- }
11102
- if (error48.stderr) {
11103
- throw new Error(`ripgrep error: ${error48.stderr}`);
11104
- }
11105
- throw error48;
11033
+ // ../core/dist/utils/index.js
11034
+ import { createHash } from "crypto";
11035
+ import { readFileSync as readFileSync2, openSync, readSync, closeSync } from "fs";
11036
+ import { extname } from "path";
11037
+ function hashFileContent(content) {
11038
+ return createHash("sha256").update(content, "utf-8").digest("hex");
11039
+ }
11040
+ function detectLanguage(filePath) {
11041
+ const ext = extname(filePath).toLowerCase();
11042
+ const languageMap = {
11043
+ ".ts": "typescript",
11044
+ ".tsx": "typescript",
11045
+ ".js": "javascript",
11046
+ ".jsx": "javascript",
11047
+ ".mjs": "javascript",
11048
+ ".cjs": "javascript",
11049
+ ".py": "python",
11050
+ ".pyi": "python",
11051
+ ".go": "go",
11052
+ ".rs": "rust",
11053
+ ".java": "java",
11054
+ ".kt": "kotlin",
11055
+ ".kts": "kotlin",
11056
+ ".c": "c",
11057
+ ".h": "c",
11058
+ ".cpp": "cpp",
11059
+ ".cc": "cpp",
11060
+ ".cxx": "cpp",
11061
+ ".hpp": "cpp",
11062
+ ".cs": "csharp",
11063
+ ".swift": "swift",
11064
+ ".rb": "ruby",
11065
+ ".php": "php",
11066
+ ".lua": "lua",
11067
+ ".r": "r",
11068
+ ".R": "r",
11069
+ ".scala": "scala",
11070
+ ".dart": "dart",
11071
+ ".zig": "zig",
11072
+ ".ex": "elixir",
11073
+ ".exs": "elixir",
11074
+ ".erl": "erlang",
11075
+ ".hrl": "erlang",
11076
+ ".hs": "haskell",
11077
+ ".ml": "ocaml",
11078
+ ".mli": "ocaml",
11079
+ ".clj": "clojure",
11080
+ ".cljs": "clojure",
11081
+ ".vue": "vue",
11082
+ ".svelte": "svelte",
11083
+ ".astro": "astro",
11084
+ ".html": "html",
11085
+ ".htm": "html",
11086
+ ".css": "css",
11087
+ ".scss": "scss",
11088
+ ".sass": "sass",
11089
+ ".less": "less",
11090
+ ".json": "json",
11091
+ ".yaml": "yaml",
11092
+ ".yml": "yaml",
11093
+ ".toml": "toml",
11094
+ ".xml": "xml",
11095
+ ".md": "markdown",
11096
+ ".mdx": "markdown",
11097
+ ".sql": "sql",
11098
+ ".sh": "shell",
11099
+ ".bash": "shell",
11100
+ ".zsh": "shell",
11101
+ ".fish": "shell",
11102
+ ".ps1": "powershell",
11103
+ ".dockerfile": "dockerfile",
11104
+ ".tf": "terraform",
11105
+ ".hcl": "hcl",
11106
+ ".proto": "protobuf",
11107
+ ".graphql": "graphql",
11108
+ ".gql": "graphql"
11109
+ };
11110
+ return languageMap[ext] ?? null;
11111
+ }
11112
+ function countLines(content) {
11113
+ if (content.length === 0)
11114
+ return 0;
11115
+ let count = 1;
11116
+ for (let i = 0; i < content.length; i++) {
11117
+ if (content[i] === "\n")
11118
+ count++;
11106
11119
  }
11120
+ return count;
11107
11121
  }
11108
- async function isRipgrepAvailable() {
11109
- const config2 = loadConfig();
11122
+ function isBinaryFile(filePath) {
11110
11123
  try {
11111
- await execFileAsync(config2.search.ripgrepPath, ["--version"]);
11112
- return true;
11124
+ const buffer = Buffer.alloc(8192);
11125
+ const fd = openSync(filePath, "r");
11126
+ const bytesRead = readSync(fd, buffer, 0, 8192, 0);
11127
+ closeSync(fd);
11128
+ for (let i = 0; i < bytesRead; i++) {
11129
+ if (buffer[i] === 0)
11130
+ return true;
11131
+ }
11132
+ return false;
11113
11133
  } catch {
11114
11134
  return false;
11115
11135
  }
11116
11136
  }
11117
- function parseRipgrepJson(output, maxResults) {
11118
- const lines = output.trim().split("\n").filter(Boolean);
11119
- const matches = [];
11120
- let totalMatches = 0;
11121
- const contextMap = /* @__PURE__ */ new Map();
11122
- const messages = [];
11123
- for (const line of lines) {
11124
- try {
11125
- messages.push(JSON.parse(line));
11126
- } catch {
11127
- }
11128
- }
11129
- for (let i = 0; i < messages.length; i++) {
11130
- const msg = messages[i];
11131
- if (msg.type === "match" && msg.data) {
11132
- const filePath = msg.data.path?.text ?? "";
11133
- const lineNumber = msg.data.line_number ?? 0;
11134
- const lineText = msg.data.lines?.text?.trimEnd() ?? "";
11135
- const matchText = msg.data.submatches?.[0]?.match?.text ?? lineText;
11136
- const contextBefore = [];
11137
- const contextAfter = [];
11138
- for (let j = i - 1; j >= 0; j--) {
11139
- const ctx = messages[j];
11140
- if (ctx.type === "context" && ctx.data?.path?.text === filePath) {
11141
- contextBefore.unshift(ctx.data.lines?.text?.trimEnd() ?? "");
11142
- } else {
11143
- break;
11144
- }
11145
- }
11146
- for (let j = i + 1; j < messages.length; j++) {
11147
- const ctx = messages[j];
11148
- if (ctx.type === "context" && ctx.data?.path?.text === filePath) {
11149
- contextAfter.push(ctx.data.lines?.text?.trimEnd() ?? "");
11150
- } else {
11151
- break;
11152
- }
11153
- }
11154
- matches.push({
11155
- filePath,
11156
- lineNumber,
11157
- columnNumber: msg.data.submatches?.[0]?.start ?? 0,
11158
- matchText,
11159
- lineText,
11160
- contextBefore,
11161
- contextAfter
11162
- });
11163
- totalMatches++;
11164
- }
11165
- if (msg.type === "summary" && msg.data?.stats?.matches_found != null) {
11166
- totalMatches = msg.data.stats.matches_found;
11167
- }
11168
- }
11169
- if (totalMatches === 0)
11170
- totalMatches = matches.length;
11171
- const truncated = matches.length > maxResults;
11172
- return {
11173
- matches: matches.slice(0, maxResults),
11174
- totalMatches,
11175
- truncated
11176
- };
11177
- }
11178
- var execFileAsync;
11179
- var init_ripgrep = __esm({
11180
- "../core/dist/search/ripgrep.js"() {
11137
+ var init_utils = __esm({
11138
+ "../core/dist/utils/index.js"() {
11181
11139
  "use strict";
11182
- init_config();
11183
- execFileAsync = promisify(execFile);
11184
11140
  }
11185
11141
  });
11186
11142
 
11187
- // ../core/dist/search/keyword.js
11188
- import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
11143
+ // ../core/dist/indexer/discovery.js
11144
+ import { execFile } from "child_process";
11145
+ import { promisify } from "util";
11146
+ import { stat } from "fs/promises";
11189
11147
  import { join as join2, relative } from "path";
11190
- async function keywordSearch(opts) {
11148
+ async function discoverFiles(options) {
11191
11149
  const config2 = loadConfig();
11192
- const maxResults = opts.maxResults ?? config2.search.defaultMaxResults;
11193
- const keywords = tokenizeQuery(opts.query);
11194
- if (keywords.length === 0) {
11195
- return { matches: [], keywords: [], totalFilesScanned: 0 };
11196
- }
11197
- const files2 = opts.filePaths ?? await collectFiles(opts.cwd, opts.pathFilter);
11198
- const matches = [];
11199
- for (const filePath of files2) {
11200
- const absPath = join2(opts.cwd, filePath);
11201
- let content;
11202
- try {
11203
- const stat3 = statSync(absPath);
11204
- if (stat3.size > config2.indexing.maxFileSizeKb * 1024)
11205
- continue;
11206
- content = readFileSync2(absPath, "utf-8");
11207
- } catch {
11208
- continue;
11209
- }
11210
- const result = scoreFile(content, keywords, filePath);
11211
- if (result.totalHits > 0) {
11212
- matches.push(result);
11213
- }
11214
- }
11215
- matches.sort((a, b) => b.score - a.score);
11216
- return {
11217
- matches: matches.slice(0, maxResults),
11218
- keywords,
11219
- totalFilesScanned: files2.length
11220
- };
11221
- }
11222
- function tokenizeQuery(query) {
11223
- const stopWords = /* @__PURE__ */ new Set([
11224
- "the",
11225
- "a",
11226
- "an",
11227
- "is",
11228
- "are",
11229
- "was",
11230
- "were",
11231
- "be",
11232
- "been",
11233
- "being",
11234
- "have",
11235
- "has",
11236
- "had",
11237
- "do",
11238
- "does",
11239
- "did",
11240
- "will",
11241
- "would",
11242
- "could",
11243
- "should",
11244
- "may",
11245
- "might",
11246
- "can",
11247
- "shall",
11248
- "in",
11249
- "on",
11250
- "at",
11251
- "to",
11252
- "for",
11253
- "of",
11254
- "with",
11255
- "by",
11256
- "from",
11257
- "as",
11258
- "into",
11259
- "through",
11260
- "during",
11261
- "before",
11262
- "after",
11263
- "and",
11264
- "but",
11265
- "or",
11266
- "nor",
11267
- "not",
11268
- "so",
11269
- "if",
11270
- "then",
11271
- "else",
11272
- "this",
11273
- "that",
11274
- "these",
11275
- "those",
11276
- "it",
11277
- "its"
11278
- ]);
11279
- return query.toLowerCase().split(/[\s,;|+]+/).map((w) => w.replace(/[^a-z0-9_.-]/g, "")).filter((w) => w.length >= 2 && !stopWords.has(w));
11280
- }
11281
- function scoreFile(content, keywords, filePath) {
11282
- const lowerContent = content.toLowerCase();
11283
- const lines = content.split("\n");
11284
- const keywordHits = {};
11285
- let totalHits = 0;
11286
- for (const keyword of keywords) {
11287
- let count = 0;
11288
- let pos = 0;
11289
- while ((pos = lowerContent.indexOf(keyword, pos)) !== -1) {
11290
- count++;
11291
- pos += keyword.length;
11292
- }
11293
- if (count > 0) {
11294
- keywordHits[keyword] = count;
11295
- totalHits += count;
11296
- }
11150
+ const maxSizeBytes = (options.maxFileSizeKb ?? config2.indexing.maxFileSizeKb) * 1024;
11151
+ const allExclude = [
11152
+ ...config2.indexing.excludedPatterns ?? [],
11153
+ ...options.excludePatterns ?? []
11154
+ ];
11155
+ let filePaths;
11156
+ try {
11157
+ filePaths = await getGitTrackedFiles(options.projectRoot);
11158
+ } catch {
11159
+ filePaths = await walkDirectory(options.projectRoot, allExclude);
11297
11160
  }
11298
- const topLines = [];
11299
- if (totalHits > 0) {
11300
- const lineScores = [];
11301
- for (let i = 0; i < lines.length; i++) {
11302
- const lineLower = lines[i].toLowerCase();
11303
- const foundKeywords = [];
11304
- for (const keyword of keywords) {
11305
- if (lineLower.includes(keyword)) {
11306
- foundKeywords.push(keyword);
11307
- }
11308
- }
11309
- if (foundKeywords.length > 0) {
11310
- lineScores.push({
11311
- line: i + 1,
11312
- text: lines[i].trimEnd(),
11313
- keywords: foundKeywords,
11314
- score: foundKeywords.length
11315
- });
11161
+ const results = [];
11162
+ const BATCH = 100;
11163
+ for (let i = 0; i < filePaths.length; i += BATCH) {
11164
+ const batch = filePaths.slice(i, i + BATCH);
11165
+ const resolved = await Promise.all(batch.map(async (relPath) => {
11166
+ const absPath = join2(options.projectRoot, relPath);
11167
+ try {
11168
+ const st = await stat(absPath);
11169
+ if (!st.isFile())
11170
+ return null;
11171
+ if (st.size > maxSizeBytes)
11172
+ return null;
11173
+ if (st.size === 0)
11174
+ return null;
11175
+ if (isBinaryFile(absPath))
11176
+ return null;
11177
+ return {
11178
+ relativePath: relPath,
11179
+ absolutePath: absPath,
11180
+ sizeBytes: st.size,
11181
+ lastModifiedAt: st.mtimeMs
11182
+ };
11183
+ } catch {
11184
+ return null;
11316
11185
  }
11317
- }
11318
- lineScores.sort((a, b) => b.score - a.score);
11319
- for (const ls of lineScores.slice(0, 5)) {
11320
- topLines.push({ line: ls.line, text: ls.text, keywords: ls.keywords });
11186
+ }));
11187
+ for (const r of resolved) {
11188
+ if (r)
11189
+ results.push(r);
11321
11190
  }
11322
11191
  }
11323
- const uniqueKeywordsFound = Object.keys(keywordHits).length;
11324
- const pathLower = filePath.toLowerCase();
11325
- const pathBonus = keywords.filter((k) => pathLower.includes(k)).length * 2;
11326
- const score = uniqueKeywordsFound * 10 + Math.log2(1 + totalHits) + pathBonus;
11327
- return {
11328
- filePath,
11329
- score: Math.round(score * 100) / 100,
11330
- keywordHits,
11331
- totalHits,
11332
- lineCount: lines.length,
11333
- topLines
11334
- };
11192
+ return results;
11335
11193
  }
11336
- async function collectFiles(cwd, pathFilter) {
11337
- const { execFile: execFile8 } = await import("child_process");
11338
- const { promisify: promisify8 } = await import("util");
11339
- const execFileAsync8 = promisify8(execFile8);
11340
- try {
11341
- const { stdout } = await execFileAsync8("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
11342
- cwd,
11343
- maxBuffer: 10 * 1024 * 1024
11344
- });
11345
- let files2 = stdout.trim().split("\n").filter(Boolean);
11346
- if (pathFilter) {
11347
- const { minimatch } = await import("minimatch").catch(() => ({
11348
- minimatch: (f, p) => {
11349
- const regex = new RegExp("^" + p.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
11350
- return regex.test(f);
11351
- }
11352
- }));
11353
- files2 = files2.filter((f) => minimatch(f, pathFilter));
11354
- }
11355
- return files2;
11356
- } catch {
11357
- return walkDir(cwd, cwd);
11358
- }
11194
+ async function getGitTrackedFiles(cwd) {
11195
+ const { stdout } = await execFileAsync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd, maxBuffer: 10 * 1024 * 1024 });
11196
+ return stdout.trim().split("\n").filter(Boolean);
11359
11197
  }
11360
- function walkDir(root, dir) {
11198
+ async function walkDirectory(root, _excludePatterns) {
11199
+ const { readdir } = await import("fs/promises");
11361
11200
  const results = [];
11362
- const ignoreDirs = /* @__PURE__ */ new Set([
11363
- "node_modules",
11364
- ".git",
11365
- ".abf",
11366
- "dist",
11367
- "build",
11368
- ".next",
11369
- "__pycache__",
11370
- ".venv",
11371
- "venv"
11372
- ]);
11373
- try {
11374
- const entries = readdirSync(dir, { withFileTypes: true });
11201
+ async function walk(dir) {
11202
+ const entries = await readdir(dir, { withFileTypes: true });
11375
11203
  for (const entry of entries) {
11376
11204
  if (entry.name.startsWith(".") && entry.name !== ".")
11377
11205
  continue;
11378
- if (ignoreDirs.has(entry.name))
11206
+ if (ALWAYS_IGNORE.has(entry.name))
11379
11207
  continue;
11380
11208
  const fullPath = join2(dir, entry.name);
11381
11209
  if (entry.isDirectory()) {
11382
- results.push(...walkDir(root, fullPath));
11210
+ await walk(fullPath);
11383
11211
  } else if (entry.isFile()) {
11384
11212
  results.push(relative(root, fullPath));
11385
11213
  }
11386
11214
  }
11387
- } catch {
11388
11215
  }
11216
+ await walk(root);
11389
11217
  return results;
11390
11218
  }
11391
- var init_keyword = __esm({
11392
- "../core/dist/search/keyword.js"() {
11219
+ var execFileAsync, ALWAYS_IGNORE;
11220
+ var init_discovery = __esm({
11221
+ "../core/dist/indexer/discovery.js"() {
11393
11222
  "use strict";
11394
- init_config();
11223
+ init_manager();
11224
+ init_utils();
11225
+ execFileAsync = promisify(execFile);
11226
+ ALWAYS_IGNORE = /* @__PURE__ */ new Set([
11227
+ "node_modules",
11228
+ ".git",
11229
+ ".abf",
11230
+ "__pycache__",
11231
+ ".tox",
11232
+ ".mypy_cache",
11233
+ ".pytest_cache",
11234
+ "dist",
11235
+ "build",
11236
+ ".next",
11237
+ ".nuxt",
11238
+ "coverage",
11239
+ ".turbo"
11240
+ ]);
11395
11241
  }
11396
11242
  });
11397
11243
 
@@ -11427,127 +11273,10 @@ var init_entity = __esm({
11427
11273
  }
11428
11274
  });
11429
11275
 
11430
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/logger.js
11431
- var ConsoleLogWriter, DefaultLogger, NoopLogger;
11432
- var init_logger = __esm({
11433
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/logger.js"() {
11434
- "use strict";
11435
- init_entity();
11436
- ConsoleLogWriter = class {
11437
- static [entityKind] = "ConsoleLogWriter";
11438
- write(message) {
11439
- console.log(message);
11440
- }
11441
- };
11442
- DefaultLogger = class {
11443
- static [entityKind] = "DefaultLogger";
11444
- writer;
11445
- constructor(config2) {
11446
- this.writer = config2?.writer ?? new ConsoleLogWriter();
11447
- }
11448
- logQuery(query, params) {
11449
- const stringifiedParams = params.map((p) => {
11450
- try {
11451
- return JSON.stringify(p);
11452
- } catch {
11453
- return String(p);
11454
- }
11455
- });
11456
- const paramsStr = stringifiedParams.length ? ` -- params: [${stringifiedParams.join(", ")}]` : "";
11457
- this.writer.write(`Query: ${query}${paramsStr}`);
11458
- }
11459
- };
11460
- NoopLogger = class {
11461
- static [entityKind] = "NoopLogger";
11462
- logQuery() {
11463
- }
11464
- };
11465
- }
11466
- });
11467
-
11468
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.utils.js
11469
- var TableName;
11470
- var init_table_utils = __esm({
11471
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.utils.js"() {
11472
- "use strict";
11473
- TableName = /* @__PURE__ */ Symbol.for("drizzle:Name");
11474
- }
11475
- });
11476
-
11477
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.js
11478
- function getTableName(table) {
11479
- return table[TableName];
11480
- }
11481
- function getTableUniqueName(table) {
11482
- return `${table[Schema] ?? "public"}.${table[TableName]}`;
11483
- }
11484
- var Schema, Columns, ExtraConfigColumns, OriginalName, BaseName, IsAlias, ExtraConfigBuilder, IsDrizzleTable, Table;
11485
- var init_table = __esm({
11486
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.js"() {
11487
- "use strict";
11488
- init_entity();
11489
- init_table_utils();
11490
- Schema = /* @__PURE__ */ Symbol.for("drizzle:Schema");
11491
- Columns = /* @__PURE__ */ Symbol.for("drizzle:Columns");
11492
- ExtraConfigColumns = /* @__PURE__ */ Symbol.for("drizzle:ExtraConfigColumns");
11493
- OriginalName = /* @__PURE__ */ Symbol.for("drizzle:OriginalName");
11494
- BaseName = /* @__PURE__ */ Symbol.for("drizzle:BaseName");
11495
- IsAlias = /* @__PURE__ */ Symbol.for("drizzle:IsAlias");
11496
- ExtraConfigBuilder = /* @__PURE__ */ Symbol.for("drizzle:ExtraConfigBuilder");
11497
- IsDrizzleTable = /* @__PURE__ */ Symbol.for("drizzle:IsDrizzleTable");
11498
- Table = class {
11499
- static [entityKind] = "Table";
11500
- /** @internal */
11501
- static Symbol = {
11502
- Name: TableName,
11503
- Schema,
11504
- OriginalName,
11505
- Columns,
11506
- ExtraConfigColumns,
11507
- BaseName,
11508
- IsAlias,
11509
- ExtraConfigBuilder
11510
- };
11511
- /**
11512
- * @internal
11513
- * Can be changed if the table is aliased.
11514
- */
11515
- [TableName];
11516
- /**
11517
- * @internal
11518
- * Used to store the original name of the table, before any aliasing.
11519
- */
11520
- [OriginalName];
11521
- /** @internal */
11522
- [Schema];
11523
- /** @internal */
11524
- [Columns];
11525
- /** @internal */
11526
- [ExtraConfigColumns];
11527
- /**
11528
- * @internal
11529
- * Used to store the table name before the transformation via the `tableCreator` functions.
11530
- */
11531
- [BaseName];
11532
- /** @internal */
11533
- [IsAlias] = false;
11534
- /** @internal */
11535
- [IsDrizzleTable] = true;
11536
- /** @internal */
11537
- [ExtraConfigBuilder] = void 0;
11538
- constructor(name, schema, baseName) {
11539
- this[TableName] = this[OriginalName] = name;
11540
- this[Schema] = schema;
11541
- this[BaseName] = baseName;
11542
- }
11543
- };
11544
- }
11545
- });
11546
-
11547
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/column.js
11548
- var Column;
11549
- var init_column = __esm({
11550
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/column.js"() {
11276
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/column.js
11277
+ var Column;
11278
+ var init_column = __esm({
11279
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/column.js"() {
11551
11280
  "use strict";
11552
11281
  init_entity();
11553
11282
  Column = class {
@@ -11712,6 +11441,15 @@ var init_column_builder = __esm({
11712
11441
  }
11713
11442
  });
11714
11443
 
11444
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.utils.js
11445
+ var TableName;
11446
+ var init_table_utils = __esm({
11447
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.utils.js"() {
11448
+ "use strict";
11449
+ TableName = /* @__PURE__ */ Symbol.for("drizzle:Name");
11450
+ }
11451
+ });
11452
+
11715
11453
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/pg-core/foreign-keys.js
11716
11454
  var ForeignKeyBuilder, ForeignKey;
11717
11455
  var init_foreign_keys = __esm({
@@ -12276,6 +12014,76 @@ var init_view_common = __esm({
12276
12014
  }
12277
12015
  });
12278
12016
 
12017
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.js
12018
+ function getTableName(table) {
12019
+ return table[TableName];
12020
+ }
12021
+ function getTableUniqueName(table) {
12022
+ return `${table[Schema] ?? "public"}.${table[TableName]}`;
12023
+ }
12024
+ var Schema, Columns, ExtraConfigColumns, OriginalName, BaseName, IsAlias, ExtraConfigBuilder, IsDrizzleTable, Table;
12025
+ var init_table = __esm({
12026
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/table.js"() {
12027
+ "use strict";
12028
+ init_entity();
12029
+ init_table_utils();
12030
+ Schema = /* @__PURE__ */ Symbol.for("drizzle:Schema");
12031
+ Columns = /* @__PURE__ */ Symbol.for("drizzle:Columns");
12032
+ ExtraConfigColumns = /* @__PURE__ */ Symbol.for("drizzle:ExtraConfigColumns");
12033
+ OriginalName = /* @__PURE__ */ Symbol.for("drizzle:OriginalName");
12034
+ BaseName = /* @__PURE__ */ Symbol.for("drizzle:BaseName");
12035
+ IsAlias = /* @__PURE__ */ Symbol.for("drizzle:IsAlias");
12036
+ ExtraConfigBuilder = /* @__PURE__ */ Symbol.for("drizzle:ExtraConfigBuilder");
12037
+ IsDrizzleTable = /* @__PURE__ */ Symbol.for("drizzle:IsDrizzleTable");
12038
+ Table = class {
12039
+ static [entityKind] = "Table";
12040
+ /** @internal */
12041
+ static Symbol = {
12042
+ Name: TableName,
12043
+ Schema,
12044
+ OriginalName,
12045
+ Columns,
12046
+ ExtraConfigColumns,
12047
+ BaseName,
12048
+ IsAlias,
12049
+ ExtraConfigBuilder
12050
+ };
12051
+ /**
12052
+ * @internal
12053
+ * Can be changed if the table is aliased.
12054
+ */
12055
+ [TableName];
12056
+ /**
12057
+ * @internal
12058
+ * Used to store the original name of the table, before any aliasing.
12059
+ */
12060
+ [OriginalName];
12061
+ /** @internal */
12062
+ [Schema];
12063
+ /** @internal */
12064
+ [Columns];
12065
+ /** @internal */
12066
+ [ExtraConfigColumns];
12067
+ /**
12068
+ * @internal
12069
+ * Used to store the table name before the transformation via the `tableCreator` functions.
12070
+ */
12071
+ [BaseName];
12072
+ /** @internal */
12073
+ [IsAlias] = false;
12074
+ /** @internal */
12075
+ [IsDrizzleTable] = true;
12076
+ /** @internal */
12077
+ [ExtraConfigBuilder] = void 0;
12078
+ constructor(name, schema, baseName) {
12079
+ this[TableName] = this[OriginalName] = name;
12080
+ this[Schema] = schema;
12081
+ this[BaseName] = baseName;
12082
+ }
12083
+ };
12084
+ }
12085
+ });
12086
+
12279
12087
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/sql.js
12280
12088
  function isSQLWrapper(value) {
12281
12089
  return value !== null && value !== void 0 && typeof value.getSQL === "function";
@@ -12683,6 +12491,223 @@ var init_sql = __esm({
12683
12491
  }
12684
12492
  });
12685
12493
 
12494
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/alias.js
12495
+ function aliasedTable(table, tableAlias) {
12496
+ return new Proxy(table, new TableAliasProxyHandler(tableAlias, false));
12497
+ }
12498
+ function aliasedTableColumn(column, tableAlias) {
12499
+ return new Proxy(
12500
+ column,
12501
+ new ColumnAliasProxyHandler(new Proxy(column.table, new TableAliasProxyHandler(tableAlias, false)))
12502
+ );
12503
+ }
12504
+ function mapColumnsInAliasedSQLToAlias(query, alias) {
12505
+ return new SQL.Aliased(mapColumnsInSQLToAlias(query.sql, alias), query.fieldAlias);
12506
+ }
12507
+ function mapColumnsInSQLToAlias(query, alias) {
12508
+ return sql.join(query.queryChunks.map((c) => {
12509
+ if (is(c, Column)) {
12510
+ return aliasedTableColumn(c, alias);
12511
+ }
12512
+ if (is(c, SQL)) {
12513
+ return mapColumnsInSQLToAlias(c, alias);
12514
+ }
12515
+ if (is(c, SQL.Aliased)) {
12516
+ return mapColumnsInAliasedSQLToAlias(c, alias);
12517
+ }
12518
+ return c;
12519
+ }));
12520
+ }
12521
+ var ColumnAliasProxyHandler, TableAliasProxyHandler, RelationTableAliasProxyHandler;
12522
+ var init_alias = __esm({
12523
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/alias.js"() {
12524
+ "use strict";
12525
+ init_column();
12526
+ init_entity();
12527
+ init_sql();
12528
+ init_table();
12529
+ init_view_common();
12530
+ ColumnAliasProxyHandler = class {
12531
+ constructor(table) {
12532
+ this.table = table;
12533
+ }
12534
+ static [entityKind] = "ColumnAliasProxyHandler";
12535
+ get(columnObj, prop) {
12536
+ if (prop === "table") {
12537
+ return this.table;
12538
+ }
12539
+ return columnObj[prop];
12540
+ }
12541
+ };
12542
+ TableAliasProxyHandler = class {
12543
+ constructor(alias, replaceOriginalName) {
12544
+ this.alias = alias;
12545
+ this.replaceOriginalName = replaceOriginalName;
12546
+ }
12547
+ static [entityKind] = "TableAliasProxyHandler";
12548
+ get(target, prop) {
12549
+ if (prop === Table.Symbol.IsAlias) {
12550
+ return true;
12551
+ }
12552
+ if (prop === Table.Symbol.Name) {
12553
+ return this.alias;
12554
+ }
12555
+ if (this.replaceOriginalName && prop === Table.Symbol.OriginalName) {
12556
+ return this.alias;
12557
+ }
12558
+ if (prop === ViewBaseConfig) {
12559
+ return {
12560
+ ...target[ViewBaseConfig],
12561
+ name: this.alias,
12562
+ isAlias: true
12563
+ };
12564
+ }
12565
+ if (prop === Table.Symbol.Columns) {
12566
+ const columns = target[Table.Symbol.Columns];
12567
+ if (!columns) {
12568
+ return columns;
12569
+ }
12570
+ const proxiedColumns = {};
12571
+ Object.keys(columns).map((key) => {
12572
+ proxiedColumns[key] = new Proxy(
12573
+ columns[key],
12574
+ new ColumnAliasProxyHandler(new Proxy(target, this))
12575
+ );
12576
+ });
12577
+ return proxiedColumns;
12578
+ }
12579
+ const value = target[prop];
12580
+ if (is(value, Column)) {
12581
+ return new Proxy(value, new ColumnAliasProxyHandler(new Proxy(target, this)));
12582
+ }
12583
+ return value;
12584
+ }
12585
+ };
12586
+ RelationTableAliasProxyHandler = class {
12587
+ constructor(alias) {
12588
+ this.alias = alias;
12589
+ }
12590
+ static [entityKind] = "RelationTableAliasProxyHandler";
12591
+ get(target, prop) {
12592
+ if (prop === "sourceTable") {
12593
+ return aliasedTable(target.sourceTable, this.alias);
12594
+ }
12595
+ return target[prop];
12596
+ }
12597
+ };
12598
+ }
12599
+ });
12600
+
12601
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/errors.js
12602
+ var DrizzleError, DrizzleQueryError, TransactionRollbackError;
12603
+ var init_errors2 = __esm({
12604
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/errors.js"() {
12605
+ "use strict";
12606
+ init_entity();
12607
+ DrizzleError = class extends Error {
12608
+ static [entityKind] = "DrizzleError";
12609
+ constructor({ message, cause }) {
12610
+ super(message);
12611
+ this.name = "DrizzleError";
12612
+ this.cause = cause;
12613
+ }
12614
+ };
12615
+ DrizzleQueryError = class _DrizzleQueryError extends Error {
12616
+ constructor(query, params, cause) {
12617
+ super(`Failed query: ${query}
12618
+ params: ${params}`);
12619
+ this.query = query;
12620
+ this.params = params;
12621
+ this.cause = cause;
12622
+ Error.captureStackTrace(this, _DrizzleQueryError);
12623
+ if (cause) this.cause = cause;
12624
+ }
12625
+ };
12626
+ TransactionRollbackError = class extends DrizzleError {
12627
+ static [entityKind] = "TransactionRollbackError";
12628
+ constructor() {
12629
+ super({ message: "Rollback" });
12630
+ }
12631
+ };
12632
+ }
12633
+ });
12634
+
12635
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/logger.js
12636
+ var ConsoleLogWriter, DefaultLogger, NoopLogger;
12637
+ var init_logger = __esm({
12638
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/logger.js"() {
12639
+ "use strict";
12640
+ init_entity();
12641
+ ConsoleLogWriter = class {
12642
+ static [entityKind] = "ConsoleLogWriter";
12643
+ write(message) {
12644
+ console.log(message);
12645
+ }
12646
+ };
12647
+ DefaultLogger = class {
12648
+ static [entityKind] = "DefaultLogger";
12649
+ writer;
12650
+ constructor(config2) {
12651
+ this.writer = config2?.writer ?? new ConsoleLogWriter();
12652
+ }
12653
+ logQuery(query, params) {
12654
+ const stringifiedParams = params.map((p) => {
12655
+ try {
12656
+ return JSON.stringify(p);
12657
+ } catch {
12658
+ return String(p);
12659
+ }
12660
+ });
12661
+ const paramsStr = stringifiedParams.length ? ` -- params: [${stringifiedParams.join(", ")}]` : "";
12662
+ this.writer.write(`Query: ${query}${paramsStr}`);
12663
+ }
12664
+ };
12665
+ NoopLogger = class {
12666
+ static [entityKind] = "NoopLogger";
12667
+ logQuery() {
12668
+ }
12669
+ };
12670
+ }
12671
+ });
12672
+
12673
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/operations.js
12674
+ var init_operations = __esm({
12675
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/operations.js"() {
12676
+ "use strict";
12677
+ }
12678
+ });
12679
+
12680
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/query-promise.js
12681
+ var QueryPromise;
12682
+ var init_query_promise = __esm({
12683
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/query-promise.js"() {
12684
+ "use strict";
12685
+ init_entity();
12686
+ QueryPromise = class {
12687
+ static [entityKind] = "QueryPromise";
12688
+ [Symbol.toStringTag] = "QueryPromise";
12689
+ catch(onRejected) {
12690
+ return this.then(void 0, onRejected);
12691
+ }
12692
+ finally(onFinally) {
12693
+ return this.then(
12694
+ (value) => {
12695
+ onFinally?.();
12696
+ return value;
12697
+ },
12698
+ (reason) => {
12699
+ onFinally?.();
12700
+ throw reason;
12701
+ }
12702
+ );
12703
+ }
12704
+ then(onFulfilled, onRejected) {
12705
+ return this.execute().then(onFulfilled, onRejected);
12706
+ }
12707
+ };
12708
+ }
12709
+ });
12710
+
12686
12711
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/utils.js
12687
12712
  function mapResultRow(columns, row, joinsNotNullableMap) {
12688
12713
  const nullifyMap = {};
@@ -12833,7 +12858,7 @@ function isConfig(data) {
12833
12858
  return false;
12834
12859
  }
12835
12860
  var textDecoder;
12836
- var init_utils = __esm({
12861
+ var init_utils2 = __esm({
12837
12862
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/utils.js"() {
12838
12863
  "use strict";
12839
12864
  init_column();
@@ -13333,110 +13358,57 @@ var init_relations = __esm({
13333
13358
  }
13334
13359
  });
13335
13360
 
13336
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/alias.js
13337
- function aliasedTable(table, tableAlias) {
13338
- return new Proxy(table, new TableAliasProxyHandler(tableAlias, false));
13339
- }
13340
- function aliasedTableColumn(column, tableAlias) {
13341
- return new Proxy(
13342
- column,
13343
- new ColumnAliasProxyHandler(new Proxy(column.table, new TableAliasProxyHandler(tableAlias, false)))
13344
- );
13345
- }
13346
- function mapColumnsInAliasedSQLToAlias(query, alias) {
13347
- return new SQL.Aliased(mapColumnsInSQLToAlias(query.sql, alias), query.fieldAlias);
13348
- }
13349
- function mapColumnsInSQLToAlias(query, alias) {
13350
- return sql.join(query.queryChunks.map((c) => {
13351
- if (is(c, Column)) {
13352
- return aliasedTableColumn(c, alias);
13353
- }
13354
- if (is(c, SQL)) {
13355
- return mapColumnsInSQLToAlias(c, alias);
13356
- }
13357
- if (is(c, SQL.Aliased)) {
13358
- return mapColumnsInAliasedSQLToAlias(c, alias);
13359
- }
13360
- return c;
13361
- }));
13362
- }
13363
- var ColumnAliasProxyHandler, TableAliasProxyHandler, RelationTableAliasProxyHandler;
13364
- var init_alias = __esm({
13365
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/alias.js"() {
13361
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/aggregate.js
13362
+ var init_aggregate = __esm({
13363
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/aggregate.js"() {
13364
+ "use strict";
13365
+ }
13366
+ });
13367
+
13368
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/vector.js
13369
+ var init_vector = __esm({
13370
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/vector.js"() {
13371
+ "use strict";
13372
+ }
13373
+ });
13374
+
13375
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/index.js
13376
+ var init_functions = __esm({
13377
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/index.js"() {
13378
+ "use strict";
13379
+ init_aggregate();
13380
+ init_vector();
13381
+ }
13382
+ });
13383
+
13384
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/index.js
13385
+ var init_sql2 = __esm({
13386
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/index.js"() {
13387
+ "use strict";
13388
+ init_expressions();
13389
+ init_functions();
13390
+ init_sql();
13391
+ }
13392
+ });
13393
+
13394
+ // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/index.js
13395
+ var init_drizzle_orm = __esm({
13396
+ "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/index.js"() {
13366
13397
  "use strict";
13398
+ init_alias();
13399
+ init_column_builder();
13367
13400
  init_column();
13368
13401
  init_entity();
13369
- init_sql();
13402
+ init_errors2();
13403
+ init_logger();
13404
+ init_operations();
13405
+ init_query_promise();
13406
+ init_relations();
13407
+ init_sql2();
13408
+ init_subquery();
13370
13409
  init_table();
13410
+ init_utils2();
13371
13411
  init_view_common();
13372
- ColumnAliasProxyHandler = class {
13373
- constructor(table) {
13374
- this.table = table;
13375
- }
13376
- static [entityKind] = "ColumnAliasProxyHandler";
13377
- get(columnObj, prop) {
13378
- if (prop === "table") {
13379
- return this.table;
13380
- }
13381
- return columnObj[prop];
13382
- }
13383
- };
13384
- TableAliasProxyHandler = class {
13385
- constructor(alias, replaceOriginalName) {
13386
- this.alias = alias;
13387
- this.replaceOriginalName = replaceOriginalName;
13388
- }
13389
- static [entityKind] = "TableAliasProxyHandler";
13390
- get(target, prop) {
13391
- if (prop === Table.Symbol.IsAlias) {
13392
- return true;
13393
- }
13394
- if (prop === Table.Symbol.Name) {
13395
- return this.alias;
13396
- }
13397
- if (this.replaceOriginalName && prop === Table.Symbol.OriginalName) {
13398
- return this.alias;
13399
- }
13400
- if (prop === ViewBaseConfig) {
13401
- return {
13402
- ...target[ViewBaseConfig],
13403
- name: this.alias,
13404
- isAlias: true
13405
- };
13406
- }
13407
- if (prop === Table.Symbol.Columns) {
13408
- const columns = target[Table.Symbol.Columns];
13409
- if (!columns) {
13410
- return columns;
13411
- }
13412
- const proxiedColumns = {};
13413
- Object.keys(columns).map((key) => {
13414
- proxiedColumns[key] = new Proxy(
13415
- columns[key],
13416
- new ColumnAliasProxyHandler(new Proxy(target, this))
13417
- );
13418
- });
13419
- return proxiedColumns;
13420
- }
13421
- const value = target[prop];
13422
- if (is(value, Column)) {
13423
- return new Proxy(value, new ColumnAliasProxyHandler(new Proxy(target, this)));
13424
- }
13425
- return value;
13426
- }
13427
- };
13428
- RelationTableAliasProxyHandler = class {
13429
- constructor(alias) {
13430
- this.alias = alias;
13431
- }
13432
- static [entityKind] = "RelationTableAliasProxyHandler";
13433
- get(target, prop) {
13434
- if (prop === "sourceTable") {
13435
- return aliasedTable(target.sourceTable, this.alias);
13436
- }
13437
- return target[prop];
13438
- }
13439
- };
13440
13412
  }
13441
13413
  });
13442
13414
 
@@ -13520,37 +13492,6 @@ var init_selection_proxy = __esm({
13520
13492
  }
13521
13493
  });
13522
13494
 
13523
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/query-promise.js
13524
- var QueryPromise;
13525
- var init_query_promise = __esm({
13526
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/query-promise.js"() {
13527
- "use strict";
13528
- init_entity();
13529
- QueryPromise = class {
13530
- static [entityKind] = "QueryPromise";
13531
- [Symbol.toStringTag] = "QueryPromise";
13532
- catch(onRejected) {
13533
- return this.then(void 0, onRejected);
13534
- }
13535
- finally(onFinally) {
13536
- return this.then(
13537
- (value) => {
13538
- onFinally?.();
13539
- return value;
13540
- },
13541
- (reason) => {
13542
- onFinally?.();
13543
- throw reason;
13544
- }
13545
- );
13546
- }
13547
- then(onFulfilled, onRejected) {
13548
- return this.execute().then(onFulfilled, onRejected);
13549
- }
13550
- };
13551
- }
13552
- });
13553
-
13554
13495
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/foreign-keys.js
13555
13496
  var ForeignKeyBuilder2, ForeignKey2;
13556
13497
  var init_foreign_keys2 = __esm({
@@ -13744,7 +13685,7 @@ var init_blob = __esm({
13744
13685
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/columns/blob.js"() {
13745
13686
  "use strict";
13746
13687
  init_entity();
13747
- init_utils();
13688
+ init_utils2();
13748
13689
  init_common2();
13749
13690
  SQLiteBigIntBuilder = class extends SQLiteColumnBuilder {
13750
13691
  static [entityKind] = "SQLiteBigIntBuilder";
@@ -13842,7 +13783,7 @@ var init_custom = __esm({
13842
13783
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/columns/custom.js"() {
13843
13784
  "use strict";
13844
13785
  init_entity();
13845
- init_utils();
13786
+ init_utils2();
13846
13787
  init_common2();
13847
13788
  SQLiteCustomColumnBuilder = class extends SQLiteColumnBuilder {
13848
13789
  static [entityKind] = "SQLiteCustomColumnBuilder";
@@ -13900,7 +13841,7 @@ var init_integer = __esm({
13900
13841
  "use strict";
13901
13842
  init_entity();
13902
13843
  init_sql();
13903
- init_utils();
13844
+ init_utils2();
13904
13845
  init_common2();
13905
13846
  SQLiteBaseIntegerBuilder = class extends SQLiteColumnBuilder {
13906
13847
  static [entityKind] = "SQLiteBaseIntegerBuilder";
@@ -14013,7 +13954,7 @@ var init_numeric = __esm({
14013
13954
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/columns/numeric.js"() {
14014
13955
  "use strict";
14015
13956
  init_entity();
14016
- init_utils();
13957
+ init_utils2();
14017
13958
  init_common2();
14018
13959
  SQLiteNumericBuilder = class extends SQLiteColumnBuilder {
14019
13960
  static [entityKind] = "SQLiteNumericBuilder";
@@ -14128,7 +14069,7 @@ var init_text = __esm({
14128
14069
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/columns/text.js"() {
14129
14070
  "use strict";
14130
14071
  init_entity();
14131
- init_utils();
14072
+ init_utils2();
14132
14073
  init_common2();
14133
14074
  SQLiteTextBuilder = class extends SQLiteColumnBuilder {
14134
14075
  static [entityKind] = "SQLiteTextBuilder";
@@ -14392,7 +14333,7 @@ function extractUsedTable(table) {
14392
14333
  }
14393
14334
  return [];
14394
14335
  }
14395
- var init_utils2 = __esm({
14336
+ var init_utils3 = __esm({
14396
14337
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/utils.js"() {
14397
14338
  "use strict";
14398
14339
  init_entity();
@@ -14413,8 +14354,8 @@ var init_delete = __esm({
14413
14354
  init_selection_proxy();
14414
14355
  init_table3();
14415
14356
  init_table();
14416
- init_utils();
14417
14357
  init_utils2();
14358
+ init_utils3();
14418
14359
  SQLiteDeleteBase = class extends QueryPromise {
14419
14360
  constructor(table, session, dialect, withList) {
14420
14361
  super();
@@ -14590,73 +14531,6 @@ var init_casing = __esm({
14590
14531
  }
14591
14532
  });
14592
14533
 
14593
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/errors.js
14594
- var DrizzleError, DrizzleQueryError, TransactionRollbackError;
14595
- var init_errors2 = __esm({
14596
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/errors.js"() {
14597
- "use strict";
14598
- init_entity();
14599
- DrizzleError = class extends Error {
14600
- static [entityKind] = "DrizzleError";
14601
- constructor({ message, cause }) {
14602
- super(message);
14603
- this.name = "DrizzleError";
14604
- this.cause = cause;
14605
- }
14606
- };
14607
- DrizzleQueryError = class _DrizzleQueryError extends Error {
14608
- constructor(query, params, cause) {
14609
- super(`Failed query: ${query}
14610
- params: ${params}`);
14611
- this.query = query;
14612
- this.params = params;
14613
- this.cause = cause;
14614
- Error.captureStackTrace(this, _DrizzleQueryError);
14615
- if (cause) this.cause = cause;
14616
- }
14617
- };
14618
- TransactionRollbackError = class extends DrizzleError {
14619
- static [entityKind] = "TransactionRollbackError";
14620
- constructor() {
14621
- super({ message: "Rollback" });
14622
- }
14623
- };
14624
- }
14625
- });
14626
-
14627
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/aggregate.js
14628
- var init_aggregate = __esm({
14629
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/aggregate.js"() {
14630
- "use strict";
14631
- }
14632
- });
14633
-
14634
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/vector.js
14635
- var init_vector = __esm({
14636
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/vector.js"() {
14637
- "use strict";
14638
- }
14639
- });
14640
-
14641
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/index.js
14642
- var init_functions = __esm({
14643
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/functions/index.js"() {
14644
- "use strict";
14645
- init_aggregate();
14646
- init_vector();
14647
- }
14648
- });
14649
-
14650
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/index.js
14651
- var init_sql2 = __esm({
14652
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sql/index.js"() {
14653
- "use strict";
14654
- init_expressions();
14655
- init_functions();
14656
- init_sql();
14657
- }
14658
- });
14659
-
14660
14534
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/columns/index.js
14661
14535
  var init_columns = __esm({
14662
14536
  "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/sqlite-core/columns/index.js"() {
@@ -14701,7 +14575,7 @@ var init_dialect = __esm({
14701
14575
  init_table3();
14702
14576
  init_subquery();
14703
14577
  init_table();
14704
- init_utils();
14578
+ init_utils2();
14705
14579
  init_view_common();
14706
14580
  init_view_base();
14707
14581
  SQLiteDialect = class {
@@ -15440,9 +15314,9 @@ var init_select2 = __esm({
15440
15314
  init_sql();
15441
15315
  init_subquery();
15442
15316
  init_table();
15443
- init_utils();
15444
- init_view_common();
15445
15317
  init_utils2();
15318
+ init_view_common();
15319
+ init_utils3();
15446
15320
  init_view_base();
15447
15321
  SQLiteSelectBuilder = class {
15448
15322
  static [entityKind] = "SQLiteSelectBuilder";
@@ -16182,8 +16056,8 @@ var init_insert = __esm({
16182
16056
  init_sql();
16183
16057
  init_table3();
16184
16058
  init_table();
16185
- init_utils();
16186
16059
  init_utils2();
16060
+ init_utils3();
16187
16061
  init_query_builder2();
16188
16062
  SQLiteInsertBuilder = class {
16189
16063
  constructor(table, session, dialect, withList) {
@@ -16377,9 +16251,9 @@ var init_update = __esm({
16377
16251
  init_table3();
16378
16252
  init_subquery();
16379
16253
  init_table();
16380
- init_utils();
16381
- init_view_common();
16382
16254
  init_utils2();
16255
+ init_view_common();
16256
+ init_utils3();
16383
16257
  init_view_base();
16384
16258
  SQLiteUpdateBuilder = class {
16385
16259
  constructor(table, session, dialect, withList) {
@@ -17377,7 +17251,7 @@ var init_view = __esm({
17377
17251
  "use strict";
17378
17252
  init_entity();
17379
17253
  init_selection_proxy();
17380
- init_utils();
17254
+ init_utils2();
17381
17255
  init_query_builder2();
17382
17256
  init_table3();
17383
17257
  init_view_base();
@@ -17485,7 +17359,7 @@ var init_sqlite_core = __esm({
17485
17359
  init_subquery2();
17486
17360
  init_table3();
17487
17361
  init_unique_constraint2();
17488
- init_utils2();
17362
+ init_utils3();
17489
17363
  init_view();
17490
17364
  }
17491
17365
  });
@@ -17501,7 +17375,7 @@ var init_session2 = __esm({
17501
17375
  init_sql();
17502
17376
  init_sqlite_core();
17503
17377
  init_session();
17504
- init_utils();
17378
+ init_utils2();
17505
17379
  BetterSQLiteSession = class extends SQLiteSession {
17506
17380
  constructor(client, dialect, schema, options = {}) {
17507
17381
  super(dialect);
@@ -17661,7 +17535,7 @@ var init_driver = __esm({
17661
17535
  init_relations();
17662
17536
  init_db();
17663
17537
  init_dialect();
17664
- init_utils();
17538
+ init_utils2();
17665
17539
  init_session2();
17666
17540
  BetterSQLite3Database = class extends BaseSQLiteDatabase {
17667
17541
  static [entityKind] = "BetterSQLite3Database";
@@ -17907,421 +17781,6 @@ var init_connection = __esm({
17907
17781
  }
17908
17782
  });
17909
17783
 
17910
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/operations.js
17911
- var init_operations = __esm({
17912
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/operations.js"() {
17913
- "use strict";
17914
- }
17915
- });
17916
-
17917
- // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/index.js
17918
- var init_drizzle_orm = __esm({
17919
- "../../node_modules/.pnpm/drizzle-orm@0.45.2_@types+better-sqlite3@7.6.13_better-sqlite3@12.8.0_gel@2.2.0/node_modules/drizzle-orm/index.js"() {
17920
- "use strict";
17921
- init_alias();
17922
- init_column_builder();
17923
- init_column();
17924
- init_entity();
17925
- init_errors2();
17926
- init_logger();
17927
- init_operations();
17928
- init_query_promise();
17929
- init_relations();
17930
- init_sql2();
17931
- init_subquery();
17932
- init_table();
17933
- init_utils();
17934
- init_view_common();
17935
- }
17936
- });
17937
-
17938
- // ../core/dist/llm/provider.js
17939
- var LlmUnavailableError;
17940
- var init_provider = __esm({
17941
- "../core/dist/llm/provider.js"() {
17942
- "use strict";
17943
- LlmUnavailableError = class extends Error {
17944
- constructor(provider, reason) {
17945
- super(`LLM provider "${provider}" is not available${reason ? `: ${reason}` : ""}. Configure in ~/.abf/config.json or run \`abf doctor\`.`);
17946
- this.name = "LlmUnavailableError";
17947
- }
17948
- };
17949
- }
17950
- });
17951
-
17952
- // ../core/dist/llm/ollama.js
17953
- var OllamaProvider;
17954
- var init_ollama = __esm({
17955
- "../core/dist/llm/ollama.js"() {
17956
- "use strict";
17957
- init_provider();
17958
- OllamaProvider = class {
17959
- name = "ollama";
17960
- baseUrl;
17961
- summaryModel;
17962
- embeddingModel;
17963
- constructor(config2) {
17964
- this.baseUrl = config2.baseUrl.replace(/\/$/, "");
17965
- this.summaryModel = config2.summaryModel;
17966
- this.embeddingModel = config2.embeddingModel;
17967
- }
17968
- async isAvailable() {
17969
- try {
17970
- const res = await fetch(`${this.baseUrl}/api/tags`, {
17971
- signal: AbortSignal.timeout(3e3)
17972
- });
17973
- return res.ok;
17974
- } catch {
17975
- return false;
17976
- }
17977
- }
17978
- async generateSummary(content, filePath) {
17979
- const maxChars = 6e3;
17980
- const truncated = content.length > maxChars ? content.slice(0, maxChars) + "\n... (truncated)" : content;
17981
- const prompt = `Summarize this source file in 2-3 concise sentences. Focus on what the file does, its key exports, and its role in the codebase. Do NOT include code blocks.
17982
-
17983
- File: ${filePath}
17984
-
17985
- \`\`\`
17986
- ${truncated}
17987
- \`\`\`
17988
-
17989
- Summary:`;
17990
- const res = await fetch(`${this.baseUrl}/api/generate`, {
17991
- method: "POST",
17992
- headers: { "Content-Type": "application/json" },
17993
- body: JSON.stringify({
17994
- model: this.summaryModel,
17995
- prompt,
17996
- stream: false,
17997
- options: {
17998
- temperature: 0.1,
17999
- num_predict: 200
18000
- }
18001
- })
18002
- });
18003
- if (!res.ok) {
18004
- const text4 = await res.text();
18005
- throw new LlmUnavailableError("ollama", `${res.status}: ${text4}`);
18006
- }
18007
- const data = await res.json();
18008
- return data.response.trim();
18009
- }
18010
- async generateEmbedding(text4) {
18011
- const maxChars = 8e3;
18012
- const truncated = text4.length > maxChars ? text4.slice(0, maxChars) : text4;
18013
- const res = await fetch(`${this.baseUrl}/api/embed`, {
18014
- method: "POST",
18015
- headers: { "Content-Type": "application/json" },
18016
- body: JSON.stringify({
18017
- model: this.embeddingModel,
18018
- input: truncated
18019
- })
18020
- });
18021
- if (!res.ok) {
18022
- const body = await res.text();
18023
- throw new LlmUnavailableError("ollama", `embed ${res.status}: ${body}`);
18024
- }
18025
- const data = await res.json();
18026
- if (!data.embeddings?.[0]) {
18027
- throw new Error("Empty embedding response from Ollama");
18028
- }
18029
- return new Float32Array(data.embeddings[0]);
18030
- }
18031
- async listModels() {
18032
- const res = await fetch(`${this.baseUrl}/api/tags`);
18033
- if (!res.ok) {
18034
- throw new LlmUnavailableError("ollama", `${res.status}`);
18035
- }
18036
- const data = await res.json();
18037
- return (data.models ?? []).map((m) => ({
18038
- name: m.name,
18039
- size: m.size,
18040
- modifiedAt: m.modified_at
18041
- }));
18042
- }
18043
- async pullModel(modelName) {
18044
- const res = await fetch(`${this.baseUrl}/api/pull`, {
18045
- method: "POST",
18046
- headers: { "Content-Type": "application/json" },
18047
- body: JSON.stringify({ name: modelName, stream: false })
18048
- });
18049
- if (!res.ok) {
18050
- const text4 = await res.text();
18051
- throw new Error(`Failed to pull model ${modelName}: ${text4}`);
18052
- }
18053
- await res.json();
18054
- }
18055
- };
18056
- }
18057
- });
18058
-
18059
- // ../core/dist/llm/pipelines.js
18060
- import { readFile } from "fs/promises";
18061
- import { join as join4 } from "path";
18062
- async function generateSummaries(projectRoot, opts) {
18063
- const start = Date.now();
18064
- const provider = getLlmProvider();
18065
- if (!provider) {
18066
- throw new LlmUnavailableError("none", "LLM provider is set to 'none'");
18067
- }
18068
- if (!await provider.isAvailable()) {
18069
- throw new LlmUnavailableError(provider.name, "Cannot reach Ollama. Is it running?");
18070
- }
18071
- const db = createProjectDb(projectRoot);
18072
- const stats = {
18073
- generated: 0,
18074
- skipped: 0,
18075
- errors: 0,
18076
- durationMs: 0
18077
- };
18078
- try {
18079
- const rows = opts?.force ? db.select({ id: files.id, path: files.path }).from(files).all() : db.select({ id: files.id, path: files.path }).from(files).where(isNull(files.summary)).all();
18080
- const batchSize = opts?.batchSize ?? 5;
18081
- for (let i = 0; i < rows.length; i += batchSize) {
18082
- const batch = rows.slice(i, i + batchSize);
18083
- for (const row of batch) {
18084
- try {
18085
- const absPath = join4(projectRoot, row.path);
18086
- const content = await readFile(absPath, "utf-8");
18087
- const summary = await provider.generateSummary(content, row.path);
18088
- db.update(files).set({ summary }).where(eq(files.id, row.id)).run();
18089
- stats.generated++;
18090
- } catch (err) {
18091
- if (err instanceof LlmUnavailableError)
18092
- throw err;
18093
- stats.errors++;
18094
- }
18095
- }
18096
- }
18097
- stats.skipped = rows.length === 0 ? db.select({ id: files.id }).from(files).all().length : 0;
18098
- stats.durationMs = Date.now() - start;
18099
- return stats;
18100
- } finally {
18101
- closeDb(db);
18102
- }
18103
- }
18104
- var init_pipelines = __esm({
18105
- "../core/dist/llm/pipelines.js"() {
18106
- "use strict";
18107
- init_drizzle_orm();
18108
- init_connection();
18109
- init_schema2();
18110
- init_llm();
18111
- init_provider();
18112
- }
18113
- });
18114
-
18115
- // ../core/dist/llm/index.js
18116
- function getLlmProvider() {
18117
- if (_cachedProvider)
18118
- return _cachedProvider;
18119
- const config2 = loadConfig();
18120
- if (config2.llm.provider === "none")
18121
- return null;
18122
- _cachedProvider = new OllamaProvider(config2.llm.ollama);
18123
- return _cachedProvider;
18124
- }
18125
- var _cachedProvider;
18126
- var init_llm = __esm({
18127
- "../core/dist/llm/index.js"() {
18128
- "use strict";
18129
- init_provider();
18130
- init_ollama();
18131
- init_pipelines();
18132
- init_manager();
18133
- init_ollama();
18134
- _cachedProvider = null;
18135
- }
18136
- });
18137
-
18138
- // ../core/dist/search/semantic.js
18139
- async function semanticSearch(opts) {
18140
- const provider = getLlmProvider();
18141
- if (!provider) {
18142
- throw new LlmUnavailableError("none", "LLM provider is set to 'none'");
18143
- }
18144
- if (!await provider.isAvailable()) {
18145
- throw new LlmUnavailableError(provider.name, "Cannot reach Ollama for semantic search");
18146
- }
18147
- const maxResults = opts.maxResults ?? 10;
18148
- const embedStart = Date.now();
18149
- const queryVector = await provider.generateEmbedding(opts.query);
18150
- const queryEmbeddingMs = Date.now() - embedStart;
18151
- const searchStart = Date.now();
18152
- const db = createProjectDb(opts.cwd);
18153
- try {
18154
- const rows = db.select({
18155
- fileId: embeddings.fileId,
18156
- vector: embeddings.vector,
18157
- path: files.path,
18158
- summary: files.summary,
18159
- language: files.language
18160
- }).from(embeddings).innerJoin(files, eq(embeddings.fileId, files.id)).all();
18161
- const scored = [];
18162
- for (const row of rows) {
18163
- const fileVector = new Float32Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength / 4);
18164
- const score = cosineSimilarity(queryVector, fileVector);
18165
- scored.push({
18166
- filePath: row.path,
18167
- score,
18168
- summary: row.summary,
18169
- language: row.language
18170
- });
18171
- }
18172
- scored.sort((a, b) => b.score - a.score);
18173
- const matches = scored.slice(0, maxResults);
18174
- const searchMs = Date.now() - searchStart;
18175
- return { matches, queryEmbeddingMs, searchMs };
18176
- } finally {
18177
- closeDb(db);
18178
- }
18179
- }
18180
- function cosineSimilarity(a, b) {
18181
- if (a.length !== b.length)
18182
- return 0;
18183
- let dot = 0;
18184
- let normA = 0;
18185
- let normB = 0;
18186
- for (let i = 0; i < a.length; i++) {
18187
- dot += a[i] * b[i];
18188
- normA += a[i] * a[i];
18189
- normB += b[i] * b[i];
18190
- }
18191
- const denom = Math.sqrt(normA) * Math.sqrt(normB);
18192
- return denom === 0 ? 0 : dot / denom;
18193
- }
18194
- var init_semantic = __esm({
18195
- "../core/dist/search/semantic.js"() {
18196
- "use strict";
18197
- init_connection();
18198
- init_schema2();
18199
- init_drizzle_orm();
18200
- init_llm();
18201
- init_provider();
18202
- }
18203
- });
18204
-
18205
- // ../core/dist/search/index.js
18206
- var init_search = __esm({
18207
- "../core/dist/search/index.js"() {
18208
- "use strict";
18209
- init_ripgrep();
18210
- init_keyword();
18211
- init_semantic();
18212
- }
18213
- });
18214
-
18215
- // ../core/dist/utils/index.js
18216
- import { createHash } from "crypto";
18217
- import { readFileSync as readFileSync3, openSync, readSync, closeSync } from "fs";
18218
- import { extname } from "path";
18219
- function hashFileContent(content) {
18220
- return createHash("sha256").update(content, "utf-8").digest("hex");
18221
- }
18222
- function detectLanguage(filePath) {
18223
- const ext = extname(filePath).toLowerCase();
18224
- const languageMap = {
18225
- ".ts": "typescript",
18226
- ".tsx": "typescript",
18227
- ".js": "javascript",
18228
- ".jsx": "javascript",
18229
- ".mjs": "javascript",
18230
- ".cjs": "javascript",
18231
- ".py": "python",
18232
- ".pyi": "python",
18233
- ".go": "go",
18234
- ".rs": "rust",
18235
- ".java": "java",
18236
- ".kt": "kotlin",
18237
- ".kts": "kotlin",
18238
- ".c": "c",
18239
- ".h": "c",
18240
- ".cpp": "cpp",
18241
- ".cc": "cpp",
18242
- ".cxx": "cpp",
18243
- ".hpp": "cpp",
18244
- ".cs": "csharp",
18245
- ".swift": "swift",
18246
- ".rb": "ruby",
18247
- ".php": "php",
18248
- ".lua": "lua",
18249
- ".r": "r",
18250
- ".R": "r",
18251
- ".scala": "scala",
18252
- ".dart": "dart",
18253
- ".zig": "zig",
18254
- ".ex": "elixir",
18255
- ".exs": "elixir",
18256
- ".erl": "erlang",
18257
- ".hrl": "erlang",
18258
- ".hs": "haskell",
18259
- ".ml": "ocaml",
18260
- ".mli": "ocaml",
18261
- ".clj": "clojure",
18262
- ".cljs": "clojure",
18263
- ".vue": "vue",
18264
- ".svelte": "svelte",
18265
- ".astro": "astro",
18266
- ".html": "html",
18267
- ".htm": "html",
18268
- ".css": "css",
18269
- ".scss": "scss",
18270
- ".sass": "sass",
18271
- ".less": "less",
18272
- ".json": "json",
18273
- ".yaml": "yaml",
18274
- ".yml": "yaml",
18275
- ".toml": "toml",
18276
- ".xml": "xml",
18277
- ".md": "markdown",
18278
- ".mdx": "markdown",
18279
- ".sql": "sql",
18280
- ".sh": "shell",
18281
- ".bash": "shell",
18282
- ".zsh": "shell",
18283
- ".fish": "shell",
18284
- ".ps1": "powershell",
18285
- ".dockerfile": "dockerfile",
18286
- ".tf": "terraform",
18287
- ".hcl": "hcl",
18288
- ".proto": "protobuf",
18289
- ".graphql": "graphql",
18290
- ".gql": "graphql"
18291
- };
18292
- return languageMap[ext] ?? null;
18293
- }
18294
- function countLines(content) {
18295
- if (content.length === 0)
18296
- return 0;
18297
- let count = 1;
18298
- for (let i = 0; i < content.length; i++) {
18299
- if (content[i] === "\n")
18300
- count++;
18301
- }
18302
- return count;
18303
- }
18304
- function isBinaryFile(filePath) {
18305
- try {
18306
- const buffer = Buffer.alloc(8192);
18307
- const fd = openSync(filePath, "r");
18308
- const bytesRead = readSync(fd, buffer, 0, 8192, 0);
18309
- closeSync(fd);
18310
- for (let i = 0; i < bytesRead; i++) {
18311
- if (buffer[i] === 0)
18312
- return true;
18313
- }
18314
- return false;
18315
- } catch {
18316
- return false;
18317
- }
18318
- }
18319
- var init_utils3 = __esm({
18320
- "../core/dist/utils/index.js"() {
18321
- "use strict";
18322
- }
18323
- });
18324
-
18325
17784
  // ../core/dist/analysis/ts-parser.js
18326
17785
  import { Project, Node } from "ts-morph";
18327
17786
  function getProject() {
@@ -18773,7 +18232,7 @@ var init_parse = __esm({
18773
18232
  "use strict";
18774
18233
  init_ts_parser();
18775
18234
  init_regex_parser();
18776
- init_utils3();
18235
+ init_utils();
18777
18236
  parsers = [typescriptParser, pythonParser];
18778
18237
  parserMap = /* @__PURE__ */ new Map();
18779
18238
  for (const p of parsers) {
@@ -18784,291 +18243,990 @@ var init_parse = __esm({
18784
18243
  }
18785
18244
  });
18786
18245
 
18787
- // ../core/dist/indexer/discovery.js
18788
- import { execFile as execFile4 } from "child_process";
18789
- import { promisify as promisify4 } from "util";
18790
- import { stat } from "fs/promises";
18791
- import { join as join7, relative as relative4 } from "path";
18792
- async function discoverFiles(options) {
18246
+ // ../core/dist/indexer/pipeline.js
18247
+ import { readFile } from "fs/promises";
18248
+ async function runIndexPipeline(projectRoot) {
18249
+ const start = Date.now();
18250
+ const db = createProjectDb(projectRoot);
18251
+ try {
18252
+ const discovered = await discoverFiles({ projectRoot });
18253
+ const stats = {
18254
+ totalDiscovered: discovered.length,
18255
+ indexed: 0,
18256
+ updated: 0,
18257
+ removed: 0,
18258
+ skipped: 0,
18259
+ errors: 0,
18260
+ durationMs: 0
18261
+ };
18262
+ const existingFiles = db.select({ path: files.path, contentHash: files.contentHash }).from(files).all();
18263
+ const existingMap = new Map(existingFiles.map((f) => [f.path, f.contentHash]));
18264
+ const discoveredPaths = /* @__PURE__ */ new Set();
18265
+ const BATCH = 50;
18266
+ for (let i = 0; i < discovered.length; i += BATCH) {
18267
+ const batch = discovered.slice(i, i + BATCH);
18268
+ await Promise.all(batch.map((file2) => processFile(db, projectRoot, file2, existingMap, discoveredPaths, stats)));
18269
+ }
18270
+ for (const [existingPath] of existingMap) {
18271
+ if (!discoveredPaths.has(existingPath)) {
18272
+ db.delete(files).where(eq(files.path, existingPath)).run();
18273
+ stats.removed++;
18274
+ }
18275
+ }
18276
+ stats.durationMs = Date.now() - start;
18277
+ return stats;
18278
+ } finally {
18279
+ closeDb(db);
18280
+ }
18281
+ }
18282
+ async function processFile(db, projectRoot, file2, existingMap, discoveredPaths, stats) {
18283
+ discoveredPaths.add(file2.relativePath);
18284
+ try {
18285
+ const content = await readFile(file2.absolutePath, "utf-8");
18286
+ const hash2 = hashFileContent(content);
18287
+ const existingHash = existingMap.get(file2.relativePath);
18288
+ if (existingHash === hash2) {
18289
+ stats.skipped++;
18290
+ return;
18291
+ }
18292
+ const language = detectLanguage(file2.relativePath);
18293
+ const lineCount = countLines(content);
18294
+ const now = /* @__PURE__ */ new Date();
18295
+ if (existingHash) {
18296
+ db.update(files).set({
18297
+ contentHash: hash2,
18298
+ language,
18299
+ sizeBytes: file2.sizeBytes,
18300
+ lineCount,
18301
+ lastIndexedAt: now,
18302
+ lastModifiedAt: new Date(file2.lastModifiedAt)
18303
+ }).where(eq(files.path, file2.relativePath)).run();
18304
+ const fileRow = db.select({ id: files.id }).from(files).where(eq(files.path, file2.relativePath)).get();
18305
+ if (fileRow) {
18306
+ indexSymbolsAndImports(db, fileRow.id, file2.relativePath, content);
18307
+ }
18308
+ stats.updated++;
18309
+ } else {
18310
+ const result = db.insert(files).values({
18311
+ path: file2.relativePath,
18312
+ contentHash: hash2,
18313
+ language,
18314
+ sizeBytes: file2.sizeBytes,
18315
+ lineCount,
18316
+ lastIndexedAt: now,
18317
+ lastModifiedAt: new Date(file2.lastModifiedAt)
18318
+ }).run();
18319
+ const fileId = Number(result.lastInsertRowid);
18320
+ indexSymbolsAndImports(db, fileId, file2.relativePath, content);
18321
+ stats.indexed++;
18322
+ }
18323
+ } catch {
18324
+ stats.errors++;
18325
+ }
18326
+ }
18327
+ async function getIndexStatus(projectRoot) {
18328
+ const db = createProjectDb(projectRoot);
18329
+ try {
18330
+ const allFiles = db.select({
18331
+ path: files.path,
18332
+ lastIndexedAt: files.lastIndexedAt,
18333
+ lastModifiedAt: files.lastModifiedAt
18334
+ }).from(files).all();
18335
+ const discovered = await discoverFiles({ projectRoot });
18336
+ const indexedPaths = new Set(allFiles.map((f) => f.path));
18337
+ const discoveredPaths = new Set(discovered.map((f) => f.relativePath));
18338
+ let stale = 0;
18339
+ for (const path2 of indexedPaths) {
18340
+ if (!discoveredPaths.has(path2))
18341
+ stale++;
18342
+ }
18343
+ const lastUpdated = allFiles.length > 0 ? new Date(Math.max(...allFiles.map((f) => f.lastIndexedAt.getTime()))) : null;
18344
+ const { statSync: statSync3 } = await import("fs");
18345
+ const { join: join12 } = await import("path");
18346
+ let indexSizeBytes = 0;
18347
+ try {
18348
+ const dbPath = join12(projectRoot, ".abf", "index.db");
18349
+ indexSizeBytes = statSync3(dbPath).size;
18350
+ } catch {
18351
+ }
18352
+ return {
18353
+ indexedFiles: allFiles.length,
18354
+ totalTrackedFiles: discovered.length,
18355
+ lastUpdated,
18356
+ staleFiles: stale,
18357
+ indexSizeBytes
18358
+ };
18359
+ } finally {
18360
+ closeDb(db);
18361
+ }
18362
+ }
18363
+ function indexSymbolsAndImports(db, fileId, filePath, content) {
18364
+ db.delete(symbols).where(eq(symbols.fileId, fileId)).run();
18365
+ db.delete(imports).where(eq(imports.sourceFileId, fileId)).run();
18366
+ try {
18367
+ const parsed = parseFile(filePath, content);
18368
+ insertSymbols(db, fileId, parsed.symbols, null);
18369
+ for (const imp of parsed.imports) {
18370
+ db.insert(imports).values({
18371
+ sourceFileId: fileId,
18372
+ targetPath: imp.targetPath,
18373
+ importedSymbols: JSON.stringify(imp.importedSymbols)
18374
+ }).run();
18375
+ }
18376
+ } catch {
18377
+ }
18378
+ }
18379
+ function insertSymbols(db, fileId, syms, parentId) {
18380
+ for (const sym of syms) {
18381
+ const result = db.insert(symbols).values({
18382
+ fileId,
18383
+ name: sym.name,
18384
+ kind: sym.kind,
18385
+ startLine: sym.startLine,
18386
+ endLine: sym.endLine,
18387
+ parentId,
18388
+ exported: sym.exported,
18389
+ signature: sym.signature ?? null
18390
+ }).run();
18391
+ if (sym.children.length > 0) {
18392
+ const newId = Number(result.lastInsertRowid);
18393
+ insertSymbols(db, fileId, sym.children, newId);
18394
+ }
18395
+ }
18396
+ }
18397
+ var init_pipeline = __esm({
18398
+ "../core/dist/indexer/pipeline.js"() {
18399
+ "use strict";
18400
+ init_drizzle_orm();
18401
+ init_connection();
18402
+ init_schema2();
18403
+ init_discovery();
18404
+ init_utils();
18405
+ init_parse();
18406
+ }
18407
+ });
18408
+
18409
+ // ../core/dist/indexer/watcher.js
18410
+ import { watch } from "fs";
18411
+ import { join as join4 } from "path";
18412
+ import { readFile as readFile2, stat as stat2 } from "fs/promises";
18413
+ function watchProject(projectRoot) {
18793
18414
  const config2 = loadConfig();
18794
- const maxSizeBytes = (options.maxFileSizeKb ?? config2.indexing.maxFileSizeKb) * 1024;
18795
- const allExclude = [
18796
- ...config2.indexing.excludedPatterns ?? [],
18797
- ...options.excludePatterns ?? []
18415
+ const maxSizeBytes = config2.indexing.maxFileSizeKb * 1024;
18416
+ const debounceMs = 300;
18417
+ const pending = /* @__PURE__ */ new Map();
18418
+ const watcher = watch(projectRoot, { recursive: true }, (_event, filename) => {
18419
+ if (!filename)
18420
+ return;
18421
+ if (shouldIgnore(filename))
18422
+ return;
18423
+ const existing = pending.get(filename);
18424
+ if (existing)
18425
+ clearTimeout(existing);
18426
+ pending.set(filename, setTimeout(() => {
18427
+ pending.delete(filename);
18428
+ handleChange(projectRoot, filename, maxSizeBytes).catch(() => {
18429
+ });
18430
+ }, debounceMs));
18431
+ });
18432
+ return {
18433
+ close() {
18434
+ watcher.close();
18435
+ for (const timer of pending.values())
18436
+ clearTimeout(timer);
18437
+ pending.clear();
18438
+ }
18439
+ };
18440
+ }
18441
+ function shouldIgnore(filePath) {
18442
+ const parts = filePath.split("/");
18443
+ return parts.some((p) => IGNORE_SEGMENTS.has(p));
18444
+ }
18445
+ async function handleChange(projectRoot, relPath, maxSizeBytes) {
18446
+ const absPath = join4(projectRoot, relPath);
18447
+ const db = createProjectDb(projectRoot);
18448
+ try {
18449
+ let st;
18450
+ try {
18451
+ st = await stat2(absPath);
18452
+ } catch {
18453
+ db.delete(files).where(eq(files.path, relPath)).run();
18454
+ return;
18455
+ }
18456
+ if (!st.isFile())
18457
+ return;
18458
+ if (st.size > maxSizeBytes || st.size === 0)
18459
+ return;
18460
+ if (isBinaryFile(absPath))
18461
+ return;
18462
+ const content = await readFile2(absPath, "utf-8");
18463
+ const hash2 = hashFileContent(content);
18464
+ const language = detectLanguage(relPath);
18465
+ const lineCount = countLines(content);
18466
+ const now = /* @__PURE__ */ new Date();
18467
+ const existing = db.select({ contentHash: files.contentHash }).from(files).where(eq(files.path, relPath)).get();
18468
+ if (existing) {
18469
+ if (existing.contentHash === hash2)
18470
+ return;
18471
+ db.update(files).set({
18472
+ contentHash: hash2,
18473
+ language,
18474
+ sizeBytes: st.size,
18475
+ lineCount,
18476
+ lastIndexedAt: now,
18477
+ lastModifiedAt: new Date(st.mtimeMs)
18478
+ }).where(eq(files.path, relPath)).run();
18479
+ const fileRow = db.select({ id: files.id }).from(files).where(eq(files.path, relPath)).get();
18480
+ if (fileRow) {
18481
+ indexSymbolsAndImports(db, fileRow.id, relPath, content);
18482
+ }
18483
+ } else {
18484
+ const result = db.insert(files).values({
18485
+ path: relPath,
18486
+ contentHash: hash2,
18487
+ language,
18488
+ sizeBytes: st.size,
18489
+ lineCount,
18490
+ lastIndexedAt: now,
18491
+ lastModifiedAt: new Date(st.mtimeMs)
18492
+ }).run();
18493
+ const fileId = Number(result.lastInsertRowid);
18494
+ indexSymbolsAndImports(db, fileId, relPath, content);
18495
+ }
18496
+ } finally {
18497
+ closeDb(db);
18498
+ }
18499
+ }
18500
+ var IGNORE_SEGMENTS;
18501
+ var init_watcher = __esm({
18502
+ "../core/dist/indexer/watcher.js"() {
18503
+ "use strict";
18504
+ init_drizzle_orm();
18505
+ init_connection();
18506
+ init_schema2();
18507
+ init_utils();
18508
+ init_manager();
18509
+ init_pipeline();
18510
+ IGNORE_SEGMENTS = /* @__PURE__ */ new Set([
18511
+ "node_modules",
18512
+ ".git",
18513
+ ".abf",
18514
+ "__pycache__",
18515
+ ".tox",
18516
+ "dist",
18517
+ "build",
18518
+ ".next",
18519
+ ".nuxt",
18520
+ "coverage",
18521
+ ".turbo"
18522
+ ]);
18523
+ }
18524
+ });
18525
+
18526
+ // ../core/dist/indexer/index.js
18527
+ var init_indexer = __esm({
18528
+ "../core/dist/indexer/index.js"() {
18529
+ "use strict";
18530
+ init_discovery();
18531
+ init_pipeline();
18532
+ init_watcher();
18533
+ }
18534
+ });
18535
+
18536
+ // ../core/dist/config/index.js
18537
+ var config_exports = {};
18538
+ __export(config_exports, {
18539
+ configSchema: () => configSchema,
18540
+ createDefaultConfig: () => createDefaultConfig,
18541
+ getAbfHome: () => getAbfHome,
18542
+ getConfigPath: () => getConfigPath,
18543
+ loadConfig: () => loadConfig,
18544
+ saveConfig: () => saveConfig,
18545
+ updateConfig: () => updateConfig
18546
+ });
18547
+ var init_config = __esm({
18548
+ "../core/dist/config/index.js"() {
18549
+ "use strict";
18550
+ init_schema();
18551
+ init_manager();
18552
+ }
18553
+ });
18554
+
18555
+ // ../core/dist/search/ripgrep.js
18556
+ import { execFile as execFile2 } from "child_process";
18557
+ import { promisify as promisify2 } from "util";
18558
+ async function ripgrepSearch(opts) {
18559
+ const config2 = loadConfig();
18560
+ const rgPath = config2.search.ripgrepPath;
18561
+ const maxResults = opts.maxResults ?? config2.search.defaultMaxResults;
18562
+ const contextLines = opts.contextLines ?? 2;
18563
+ const args = [
18564
+ "--json",
18565
+ "--max-count",
18566
+ String(maxResults * 2),
18567
+ // get extra so we have room after filtering
18568
+ "--context",
18569
+ String(contextLines)
18798
18570
  ];
18799
- let filePaths;
18571
+ if (!opts.caseSensitive) {
18572
+ args.push("--ignore-case");
18573
+ }
18574
+ if (!opts.regex) {
18575
+ args.push("--fixed-strings");
18576
+ }
18577
+ if (opts.pathFilter) {
18578
+ args.push("--glob", opts.pathFilter);
18579
+ }
18580
+ args.push("--hidden");
18581
+ args.push("--glob", "!.git");
18582
+ args.push("--glob", "!.abf");
18583
+ args.push("--glob", "!node_modules");
18584
+ args.push("--glob", "!.next");
18585
+ args.push("--glob", "!dist");
18586
+ args.push("--glob", "!build");
18587
+ args.push("--glob", "!*.min.js");
18588
+ args.push("--glob", "!*.min.css");
18589
+ args.push("--glob", "!*.map");
18590
+ args.push("--glob", "!package-lock.json");
18591
+ args.push("--glob", "!yarn.lock");
18592
+ args.push("--glob", "!pnpm-lock.yaml");
18593
+ args.push("--", opts.query, ".");
18800
18594
  try {
18801
- filePaths = await getGitTrackedFiles(options.projectRoot);
18595
+ const { stdout } = await execFileAsync2(rgPath, args, {
18596
+ cwd: opts.cwd,
18597
+ maxBuffer: 10 * 1024 * 1024
18598
+ // 10MB
18599
+ });
18600
+ return parseRipgrepJson(stdout, maxResults);
18601
+ } catch (error48) {
18602
+ if (error48.code === 1 || error48.exitCode === 1) {
18603
+ return { matches: [], totalMatches: 0, truncated: false };
18604
+ }
18605
+ if (error48.stderr) {
18606
+ throw new Error(`ripgrep error: ${error48.stderr}`);
18607
+ }
18608
+ throw error48;
18609
+ }
18610
+ }
18611
+ async function isRipgrepAvailable() {
18612
+ const config2 = loadConfig();
18613
+ try {
18614
+ await execFileAsync2(config2.search.ripgrepPath, ["--version"]);
18615
+ return true;
18802
18616
  } catch {
18803
- filePaths = await walkDirectory(options.projectRoot, allExclude);
18617
+ return false;
18804
18618
  }
18805
- const results = [];
18806
- const BATCH = 100;
18807
- for (let i = 0; i < filePaths.length; i += BATCH) {
18808
- const batch = filePaths.slice(i, i + BATCH);
18809
- const resolved = await Promise.all(batch.map(async (relPath) => {
18810
- const absPath = join7(options.projectRoot, relPath);
18811
- try {
18812
- const st = await stat(absPath);
18813
- if (!st.isFile())
18814
- return null;
18815
- if (st.size > maxSizeBytes)
18816
- return null;
18817
- if (st.size === 0)
18818
- return null;
18819
- if (isBinaryFile(absPath))
18820
- return null;
18821
- return {
18822
- relativePath: relPath,
18823
- absolutePath: absPath,
18824
- sizeBytes: st.size,
18825
- lastModifiedAt: st.mtimeMs
18826
- };
18827
- } catch {
18828
- return null;
18619
+ }
18620
+ function parseRipgrepJson(output, maxResults) {
18621
+ const lines = output.trim().split("\n").filter(Boolean);
18622
+ const matches = [];
18623
+ let totalMatches = 0;
18624
+ const contextMap = /* @__PURE__ */ new Map();
18625
+ const messages = [];
18626
+ for (const line of lines) {
18627
+ try {
18628
+ messages.push(JSON.parse(line));
18629
+ } catch {
18630
+ }
18631
+ }
18632
+ for (let i = 0; i < messages.length; i++) {
18633
+ const msg = messages[i];
18634
+ if (msg.type === "match" && msg.data) {
18635
+ const filePath = msg.data.path?.text ?? "";
18636
+ const lineNumber = msg.data.line_number ?? 0;
18637
+ const lineText = msg.data.lines?.text?.trimEnd() ?? "";
18638
+ const matchText = msg.data.submatches?.[0]?.match?.text ?? lineText;
18639
+ const contextBefore = [];
18640
+ const contextAfter = [];
18641
+ for (let j = i - 1; j >= 0; j--) {
18642
+ const ctx = messages[j];
18643
+ if (ctx.type === "context" && ctx.data?.path?.text === filePath) {
18644
+ contextBefore.unshift(ctx.data.lines?.text?.trimEnd() ?? "");
18645
+ } else {
18646
+ break;
18647
+ }
18829
18648
  }
18830
- }));
18831
- for (const r of resolved) {
18832
- if (r)
18833
- results.push(r);
18649
+ for (let j = i + 1; j < messages.length; j++) {
18650
+ const ctx = messages[j];
18651
+ if (ctx.type === "context" && ctx.data?.path?.text === filePath) {
18652
+ contextAfter.push(ctx.data.lines?.text?.trimEnd() ?? "");
18653
+ } else {
18654
+ break;
18655
+ }
18656
+ }
18657
+ matches.push({
18658
+ filePath,
18659
+ lineNumber,
18660
+ columnNumber: msg.data.submatches?.[0]?.start ?? 0,
18661
+ matchText,
18662
+ lineText,
18663
+ contextBefore,
18664
+ contextAfter
18665
+ });
18666
+ totalMatches++;
18667
+ }
18668
+ if (msg.type === "summary" && msg.data?.stats?.matches_found != null) {
18669
+ totalMatches = msg.data.stats.matches_found;
18834
18670
  }
18835
18671
  }
18836
- return results;
18672
+ if (totalMatches === 0)
18673
+ totalMatches = matches.length;
18674
+ const truncated = matches.length > maxResults;
18675
+ return {
18676
+ matches: matches.slice(0, maxResults),
18677
+ totalMatches,
18678
+ truncated
18679
+ };
18837
18680
  }
18838
- async function getGitTrackedFiles(cwd) {
18839
- const { stdout } = await execFileAsync4("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd, maxBuffer: 10 * 1024 * 1024 });
18840
- return stdout.trim().split("\n").filter(Boolean);
18681
+ var execFileAsync2;
18682
+ var init_ripgrep = __esm({
18683
+ "../core/dist/search/ripgrep.js"() {
18684
+ "use strict";
18685
+ init_config();
18686
+ execFileAsync2 = promisify2(execFile2);
18687
+ }
18688
+ });
18689
+
18690
+ // ../core/dist/search/keyword.js
18691
+ import { readFileSync as readFileSync3, readdirSync, statSync } from "fs";
18692
+ import { join as join5, relative as relative2 } from "path";
18693
+ async function keywordSearch(opts) {
18694
+ const config2 = loadConfig();
18695
+ const maxResults = opts.maxResults ?? config2.search.defaultMaxResults;
18696
+ const keywords = tokenizeQuery(opts.query);
18697
+ if (keywords.length === 0) {
18698
+ return { matches: [], keywords: [], totalFilesScanned: 0 };
18699
+ }
18700
+ const files2 = opts.filePaths ?? await collectFiles(opts.cwd, opts.pathFilter);
18701
+ const matches = [];
18702
+ for (const filePath of files2) {
18703
+ const absPath = join5(opts.cwd, filePath);
18704
+ let content;
18705
+ try {
18706
+ const stat3 = statSync(absPath);
18707
+ if (stat3.size > config2.indexing.maxFileSizeKb * 1024)
18708
+ continue;
18709
+ content = readFileSync3(absPath, "utf-8");
18710
+ } catch {
18711
+ continue;
18712
+ }
18713
+ const result = scoreFile(content, keywords, filePath);
18714
+ if (result.totalHits > 0) {
18715
+ matches.push(result);
18716
+ }
18717
+ }
18718
+ matches.sort((a, b) => b.score - a.score);
18719
+ return {
18720
+ matches: matches.slice(0, maxResults),
18721
+ keywords,
18722
+ totalFilesScanned: files2.length
18723
+ };
18841
18724
  }
18842
- async function walkDirectory(root, _excludePatterns) {
18843
- const { readdir } = await import("fs/promises");
18725
+ function tokenizeQuery(query) {
18726
+ const stopWords = /* @__PURE__ */ new Set([
18727
+ "the",
18728
+ "a",
18729
+ "an",
18730
+ "is",
18731
+ "are",
18732
+ "was",
18733
+ "were",
18734
+ "be",
18735
+ "been",
18736
+ "being",
18737
+ "have",
18738
+ "has",
18739
+ "had",
18740
+ "do",
18741
+ "does",
18742
+ "did",
18743
+ "will",
18744
+ "would",
18745
+ "could",
18746
+ "should",
18747
+ "may",
18748
+ "might",
18749
+ "can",
18750
+ "shall",
18751
+ "in",
18752
+ "on",
18753
+ "at",
18754
+ "to",
18755
+ "for",
18756
+ "of",
18757
+ "with",
18758
+ "by",
18759
+ "from",
18760
+ "as",
18761
+ "into",
18762
+ "through",
18763
+ "during",
18764
+ "before",
18765
+ "after",
18766
+ "and",
18767
+ "but",
18768
+ "or",
18769
+ "nor",
18770
+ "not",
18771
+ "so",
18772
+ "if",
18773
+ "then",
18774
+ "else",
18775
+ "this",
18776
+ "that",
18777
+ "these",
18778
+ "those",
18779
+ "it",
18780
+ "its"
18781
+ ]);
18782
+ return query.toLowerCase().split(/[\s,;|+]+/).map((w) => w.replace(/[^a-z0-9_.-]/g, "")).filter((w) => w.length >= 2 && !stopWords.has(w));
18783
+ }
18784
+ function scoreFile(content, keywords, filePath) {
18785
+ const lowerContent = content.toLowerCase();
18786
+ const lines = content.split("\n");
18787
+ const keywordHits = {};
18788
+ let totalHits = 0;
18789
+ for (const keyword of keywords) {
18790
+ let count = 0;
18791
+ let pos = 0;
18792
+ while ((pos = lowerContent.indexOf(keyword, pos)) !== -1) {
18793
+ count++;
18794
+ pos += keyword.length;
18795
+ }
18796
+ if (count > 0) {
18797
+ keywordHits[keyword] = count;
18798
+ totalHits += count;
18799
+ }
18800
+ }
18801
+ const topLines = [];
18802
+ if (totalHits > 0) {
18803
+ const lineScores = [];
18804
+ for (let i = 0; i < lines.length; i++) {
18805
+ const lineLower = lines[i].toLowerCase();
18806
+ const foundKeywords = [];
18807
+ for (const keyword of keywords) {
18808
+ if (lineLower.includes(keyword)) {
18809
+ foundKeywords.push(keyword);
18810
+ }
18811
+ }
18812
+ if (foundKeywords.length > 0) {
18813
+ lineScores.push({
18814
+ line: i + 1,
18815
+ text: lines[i].trimEnd(),
18816
+ keywords: foundKeywords,
18817
+ score: foundKeywords.length
18818
+ });
18819
+ }
18820
+ }
18821
+ lineScores.sort((a, b) => b.score - a.score);
18822
+ for (const ls of lineScores.slice(0, 5)) {
18823
+ topLines.push({ line: ls.line, text: ls.text, keywords: ls.keywords });
18824
+ }
18825
+ }
18826
+ const uniqueKeywordsFound = Object.keys(keywordHits).length;
18827
+ const pathLower = filePath.toLowerCase();
18828
+ const pathBonus = keywords.filter((k) => pathLower.includes(k)).length * 2;
18829
+ const score = uniqueKeywordsFound * 10 + Math.log2(1 + totalHits) + pathBonus;
18830
+ return {
18831
+ filePath,
18832
+ score: Math.round(score * 100) / 100,
18833
+ keywordHits,
18834
+ totalHits,
18835
+ lineCount: lines.length,
18836
+ topLines
18837
+ };
18838
+ }
18839
+ async function collectFiles(cwd, pathFilter) {
18840
+ const { execFile: execFile8 } = await import("child_process");
18841
+ const { promisify: promisify8 } = await import("util");
18842
+ const execFileAsync8 = promisify8(execFile8);
18843
+ try {
18844
+ const { stdout } = await execFileAsync8("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
18845
+ cwd,
18846
+ maxBuffer: 10 * 1024 * 1024
18847
+ });
18848
+ let files2 = stdout.trim().split("\n").filter(Boolean);
18849
+ if (pathFilter) {
18850
+ const { minimatch } = await import("minimatch").catch(() => ({
18851
+ minimatch: (f, p) => {
18852
+ const regex = new RegExp("^" + p.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
18853
+ return regex.test(f);
18854
+ }
18855
+ }));
18856
+ files2 = files2.filter((f) => minimatch(f, pathFilter));
18857
+ }
18858
+ return files2;
18859
+ } catch {
18860
+ return walkDir(cwd, cwd);
18861
+ }
18862
+ }
18863
+ function walkDir(root, dir) {
18844
18864
  const results = [];
18845
- async function walk(dir) {
18846
- const entries = await readdir(dir, { withFileTypes: true });
18865
+ const ignoreDirs = /* @__PURE__ */ new Set([
18866
+ "node_modules",
18867
+ ".git",
18868
+ ".abf",
18869
+ "dist",
18870
+ "build",
18871
+ ".next",
18872
+ "__pycache__",
18873
+ ".venv",
18874
+ "venv"
18875
+ ]);
18876
+ try {
18877
+ const entries = readdirSync(dir, { withFileTypes: true });
18847
18878
  for (const entry of entries) {
18848
18879
  if (entry.name.startsWith(".") && entry.name !== ".")
18849
18880
  continue;
18850
- if (ALWAYS_IGNORE.has(entry.name))
18881
+ if (ignoreDirs.has(entry.name))
18851
18882
  continue;
18852
- const fullPath = join7(dir, entry.name);
18883
+ const fullPath = join5(dir, entry.name);
18853
18884
  if (entry.isDirectory()) {
18854
- await walk(fullPath);
18885
+ results.push(...walkDir(root, fullPath));
18855
18886
  } else if (entry.isFile()) {
18856
- results.push(relative4(root, fullPath));
18887
+ results.push(relative2(root, fullPath));
18857
18888
  }
18858
18889
  }
18890
+ } catch {
18859
18891
  }
18860
- await walk(root);
18861
18892
  return results;
18862
18893
  }
18863
- var execFileAsync4, ALWAYS_IGNORE;
18864
- var init_discovery = __esm({
18865
- "../core/dist/indexer/discovery.js"() {
18894
+ var init_keyword = __esm({
18895
+ "../core/dist/search/keyword.js"() {
18866
18896
  "use strict";
18867
- init_manager();
18868
- init_utils3();
18869
- execFileAsync4 = promisify4(execFile4);
18870
- ALWAYS_IGNORE = /* @__PURE__ */ new Set([
18871
- "node_modules",
18872
- ".git",
18873
- ".abf",
18874
- "__pycache__",
18875
- ".tox",
18876
- ".mypy_cache",
18877
- ".pytest_cache",
18878
- "dist",
18879
- "build",
18880
- ".next",
18881
- ".nuxt",
18882
- "coverage",
18883
- ".turbo"
18884
- ]);
18897
+ init_config();
18885
18898
  }
18886
18899
  });
18887
18900
 
18888
- // ../core/dist/indexer/pipeline.js
18889
- import { readFile as readFile2 } from "fs/promises";
18890
- async function runIndexPipeline(projectRoot) {
18901
+ // ../core/dist/llm/provider.js
18902
+ var LlmUnavailableError;
18903
+ var init_provider = __esm({
18904
+ "../core/dist/llm/provider.js"() {
18905
+ "use strict";
18906
+ LlmUnavailableError = class extends Error {
18907
+ constructor(provider, reason) {
18908
+ super(`LLM provider "${provider}" is not available${reason ? `: ${reason}` : ""}. Configure in ~/.abf/config.json or run \`abf doctor\`.`);
18909
+ this.name = "LlmUnavailableError";
18910
+ }
18911
+ };
18912
+ }
18913
+ });
18914
+
18915
+ // ../core/dist/llm/ollama.js
18916
+ var OllamaProvider;
18917
+ var init_ollama = __esm({
18918
+ "../core/dist/llm/ollama.js"() {
18919
+ "use strict";
18920
+ init_provider();
18921
+ OllamaProvider = class {
18922
+ name = "ollama";
18923
+ baseUrl;
18924
+ summaryModel;
18925
+ embeddingModel;
18926
+ constructor(config2) {
18927
+ this.baseUrl = config2.baseUrl.replace(/\/$/, "");
18928
+ this.summaryModel = config2.summaryModel;
18929
+ this.embeddingModel = config2.embeddingModel;
18930
+ }
18931
+ async isAvailable() {
18932
+ try {
18933
+ const res = await fetch(`${this.baseUrl}/api/tags`, {
18934
+ signal: AbortSignal.timeout(3e3)
18935
+ });
18936
+ return res.ok;
18937
+ } catch {
18938
+ return false;
18939
+ }
18940
+ }
18941
+ async generateSummary(content, filePath) {
18942
+ const maxChars = 6e3;
18943
+ const truncated = content.length > maxChars ? content.slice(0, maxChars) + "\n... (truncated)" : content;
18944
+ const prompt = `Summarize this source file in 2-3 concise sentences. Focus on what the file does, its key exports, and its role in the codebase. Do NOT include code blocks.
18945
+
18946
+ File: ${filePath}
18947
+
18948
+ \`\`\`
18949
+ ${truncated}
18950
+ \`\`\`
18951
+
18952
+ Summary:`;
18953
+ const res = await fetch(`${this.baseUrl}/api/generate`, {
18954
+ method: "POST",
18955
+ headers: { "Content-Type": "application/json" },
18956
+ body: JSON.stringify({
18957
+ model: this.summaryModel,
18958
+ prompt,
18959
+ stream: false,
18960
+ options: {
18961
+ temperature: 0.1,
18962
+ num_predict: 200
18963
+ }
18964
+ })
18965
+ });
18966
+ if (!res.ok) {
18967
+ const text4 = await res.text();
18968
+ throw new LlmUnavailableError("ollama", `${res.status}: ${text4}`);
18969
+ }
18970
+ const data = await res.json();
18971
+ return data.response.trim();
18972
+ }
18973
+ async generateEmbedding(text4) {
18974
+ const maxChars = 8e3;
18975
+ const truncated = text4.length > maxChars ? text4.slice(0, maxChars) : text4;
18976
+ const res = await fetch(`${this.baseUrl}/api/embed`, {
18977
+ method: "POST",
18978
+ headers: { "Content-Type": "application/json" },
18979
+ body: JSON.stringify({
18980
+ model: this.embeddingModel,
18981
+ input: truncated
18982
+ })
18983
+ });
18984
+ if (!res.ok) {
18985
+ const body = await res.text();
18986
+ throw new LlmUnavailableError("ollama", `embed ${res.status}: ${body}`);
18987
+ }
18988
+ const data = await res.json();
18989
+ if (!data.embeddings?.[0]) {
18990
+ throw new Error("Empty embedding response from Ollama");
18991
+ }
18992
+ return new Float32Array(data.embeddings[0]);
18993
+ }
18994
+ async listModels() {
18995
+ const res = await fetch(`${this.baseUrl}/api/tags`);
18996
+ if (!res.ok) {
18997
+ throw new LlmUnavailableError("ollama", `${res.status}`);
18998
+ }
18999
+ const data = await res.json();
19000
+ return (data.models ?? []).map((m) => ({
19001
+ name: m.name,
19002
+ size: m.size,
19003
+ modifiedAt: m.modified_at
19004
+ }));
19005
+ }
19006
+ async pullModel(modelName) {
19007
+ const res = await fetch(`${this.baseUrl}/api/pull`, {
19008
+ method: "POST",
19009
+ headers: { "Content-Type": "application/json" },
19010
+ body: JSON.stringify({ name: modelName, stream: false })
19011
+ });
19012
+ if (!res.ok) {
19013
+ const text4 = await res.text();
19014
+ throw new Error(`Failed to pull model ${modelName}: ${text4}`);
19015
+ }
19016
+ await res.json();
19017
+ }
19018
+ };
19019
+ }
19020
+ });
19021
+
19022
+ // ../core/dist/llm/pipelines.js
19023
+ import { readFile as readFile3 } from "fs/promises";
19024
+ import { join as join6 } from "path";
19025
+ async function generateSummaries(projectRoot, opts) {
18891
19026
  const start = Date.now();
19027
+ const provider = getLlmProvider();
19028
+ if (!provider) {
19029
+ throw new LlmUnavailableError("none", "LLM provider is set to 'none'");
19030
+ }
19031
+ if (!await provider.isAvailable()) {
19032
+ throw new LlmUnavailableError(provider.name, "Cannot reach Ollama. Is it running?");
19033
+ }
18892
19034
  const db = createProjectDb(projectRoot);
19035
+ const stats = {
19036
+ generated: 0,
19037
+ skipped: 0,
19038
+ errors: 0,
19039
+ durationMs: 0
19040
+ };
18893
19041
  try {
18894
- const discovered = await discoverFiles({ projectRoot });
18895
- const stats = {
18896
- totalDiscovered: discovered.length,
18897
- indexed: 0,
18898
- updated: 0,
18899
- removed: 0,
18900
- skipped: 0,
18901
- errors: 0,
18902
- durationMs: 0
18903
- };
18904
- const existingFiles = db.select({ path: files.path, contentHash: files.contentHash }).from(files).all();
18905
- const existingMap = new Map(existingFiles.map((f) => [f.path, f.contentHash]));
18906
- const discoveredPaths = /* @__PURE__ */ new Set();
18907
- const BATCH = 50;
18908
- for (let i = 0; i < discovered.length; i += BATCH) {
18909
- const batch = discovered.slice(i, i + BATCH);
18910
- await Promise.all(batch.map((file2) => processFile(db, projectRoot, file2, existingMap, discoveredPaths, stats)));
18911
- }
18912
- for (const [existingPath] of existingMap) {
18913
- if (!discoveredPaths.has(existingPath)) {
18914
- db.delete(files).where(eq(files.path, existingPath)).run();
18915
- stats.removed++;
19042
+ const rows = opts?.force ? db.select({ id: files.id, path: files.path }).from(files).all() : db.select({ id: files.id, path: files.path }).from(files).where(isNull(files.summary)).all();
19043
+ const batchSize = opts?.batchSize ?? 5;
19044
+ for (let i = 0; i < rows.length; i += batchSize) {
19045
+ const batch = rows.slice(i, i + batchSize);
19046
+ for (const row of batch) {
19047
+ try {
19048
+ const absPath = join6(projectRoot, row.path);
19049
+ const content = await readFile3(absPath, "utf-8");
19050
+ const summary = await provider.generateSummary(content, row.path);
19051
+ db.update(files).set({ summary }).where(eq(files.id, row.id)).run();
19052
+ stats.generated++;
19053
+ } catch (err) {
19054
+ if (err instanceof LlmUnavailableError)
19055
+ throw err;
19056
+ stats.errors++;
19057
+ }
18916
19058
  }
18917
19059
  }
19060
+ stats.skipped = rows.length === 0 ? db.select({ id: files.id }).from(files).all().length : 0;
18918
19061
  stats.durationMs = Date.now() - start;
18919
19062
  return stats;
18920
19063
  } finally {
18921
19064
  closeDb(db);
18922
19065
  }
18923
19066
  }
18924
- async function processFile(db, projectRoot, file2, existingMap, discoveredPaths, stats) {
18925
- discoveredPaths.add(file2.relativePath);
18926
- try {
18927
- const content = await readFile2(file2.absolutePath, "utf-8");
18928
- const hash2 = hashFileContent(content);
18929
- const existingHash = existingMap.get(file2.relativePath);
18930
- if (existingHash === hash2) {
18931
- stats.skipped++;
18932
- return;
18933
- }
18934
- const language = detectLanguage(file2.relativePath);
18935
- const lineCount = countLines(content);
18936
- const now = /* @__PURE__ */ new Date();
18937
- if (existingHash) {
18938
- db.update(files).set({
18939
- contentHash: hash2,
18940
- language,
18941
- sizeBytes: file2.sizeBytes,
18942
- lineCount,
18943
- lastIndexedAt: now,
18944
- lastModifiedAt: new Date(file2.lastModifiedAt)
18945
- }).where(eq(files.path, file2.relativePath)).run();
18946
- const fileRow = db.select({ id: files.id }).from(files).where(eq(files.path, file2.relativePath)).get();
18947
- if (fileRow) {
18948
- indexSymbolsAndImports(db, fileRow.id, file2.relativePath, content);
18949
- }
18950
- stats.updated++;
18951
- } else {
18952
- const result = db.insert(files).values({
18953
- path: file2.relativePath,
18954
- contentHash: hash2,
18955
- language,
18956
- sizeBytes: file2.sizeBytes,
18957
- lineCount,
18958
- lastIndexedAt: now,
18959
- lastModifiedAt: new Date(file2.lastModifiedAt)
18960
- }).run();
18961
- const fileId = Number(result.lastInsertRowid);
18962
- indexSymbolsAndImports(db, fileId, file2.relativePath, content);
18963
- stats.indexed++;
18964
- }
18965
- } catch {
18966
- stats.errors++;
19067
+ async function generateEmbeddings(projectRoot, opts) {
19068
+ const start = Date.now();
19069
+ const provider = getLlmProvider();
19070
+ if (!provider) {
19071
+ throw new LlmUnavailableError("none", "LLM provider is set to 'none'");
19072
+ }
19073
+ if (!await provider.isAvailable()) {
19074
+ throw new LlmUnavailableError(provider.name, "Cannot reach Ollama. Is it running?");
18967
19075
  }
18968
- }
18969
- async function getIndexStatus(projectRoot) {
18970
19076
  const db = createProjectDb(projectRoot);
19077
+ const stats = {
19078
+ generated: 0,
19079
+ skipped: 0,
19080
+ errors: 0,
19081
+ durationMs: 0
19082
+ };
18971
19083
  try {
18972
- const allFiles = db.select({
18973
- path: files.path,
18974
- lastIndexedAt: files.lastIndexedAt,
18975
- lastModifiedAt: files.lastModifiedAt
18976
- }).from(files).all();
18977
- const discovered = await discoverFiles({ projectRoot });
18978
- const indexedPaths = new Set(allFiles.map((f) => f.path));
18979
- const discoveredPaths = new Set(discovered.map((f) => f.relativePath));
18980
- let stale = 0;
18981
- for (const path2 of indexedPaths) {
18982
- if (!discoveredPaths.has(path2))
18983
- stale++;
18984
- }
18985
- const lastUpdated = allFiles.length > 0 ? new Date(Math.max(...allFiles.map((f) => f.lastIndexedAt.getTime()))) : null;
18986
- const { statSync: statSync3 } = await import("fs");
18987
- const { join: join12 } = await import("path");
18988
- let indexSizeBytes = 0;
18989
- try {
18990
- const dbPath = join12(projectRoot, ".abf", "index.db");
18991
- indexSizeBytes = statSync3(dbPath).size;
18992
- } catch {
19084
+ const allFiles = db.select({ id: files.id, path: files.path }).from(files).all();
19085
+ const existingEmbeddings = opts?.force ? /* @__PURE__ */ new Set() : new Set(db.select({ fileId: embeddings.fileId }).from(embeddings).all().map((e) => e.fileId));
19086
+ const needsEmbedding = allFiles.filter((f) => !existingEmbeddings.has(f.id));
19087
+ const batchSize = opts?.batchSize ?? 5;
19088
+ for (let i = 0; i < needsEmbedding.length; i += batchSize) {
19089
+ const batch = needsEmbedding.slice(i, i + batchSize);
19090
+ for (const row of batch) {
19091
+ try {
19092
+ const absPath = join6(projectRoot, row.path);
19093
+ const content = await readFile3(absPath, "utf-8");
19094
+ const vector = await provider.generateEmbedding(content);
19095
+ const buffer = Buffer.from(vector.buffer);
19096
+ const now = /* @__PURE__ */ new Date();
19097
+ if (opts?.force) {
19098
+ db.delete(embeddings).where(eq(embeddings.fileId, row.id)).run();
19099
+ }
19100
+ db.insert(embeddings).values({
19101
+ fileId: row.id,
19102
+ vector: buffer,
19103
+ modelName: "nomic-embed-text",
19104
+ // from config
19105
+ createdAt: now
19106
+ }).run();
19107
+ stats.generated++;
19108
+ } catch (err) {
19109
+ if (err instanceof LlmUnavailableError)
19110
+ throw err;
19111
+ stats.errors++;
19112
+ }
19113
+ }
18993
19114
  }
18994
- return {
18995
- indexedFiles: allFiles.length,
18996
- totalTrackedFiles: discovered.length,
18997
- lastUpdated,
18998
- staleFiles: stale,
18999
- indexSizeBytes
19000
- };
19115
+ stats.skipped = existingEmbeddings.size;
19116
+ stats.durationMs = Date.now() - start;
19117
+ return stats;
19001
19118
  } finally {
19002
19119
  closeDb(db);
19003
19120
  }
19004
19121
  }
19005
- function indexSymbolsAndImports(db, fileId, filePath, content) {
19006
- db.delete(symbols).where(eq(symbols.fileId, fileId)).run();
19007
- db.delete(imports).where(eq(imports.sourceFileId, fileId)).run();
19008
- try {
19009
- const parsed = parseFile(filePath, content);
19010
- insertSymbols(db, fileId, parsed.symbols, null);
19011
- for (const imp of parsed.imports) {
19012
- db.insert(imports).values({
19013
- sourceFileId: fileId,
19014
- targetPath: imp.targetPath,
19015
- importedSymbols: JSON.stringify(imp.importedSymbols)
19016
- }).run();
19017
- }
19018
- } catch {
19019
- }
19020
- }
19021
- function insertSymbols(db, fileId, syms, parentId) {
19022
- for (const sym of syms) {
19023
- const result = db.insert(symbols).values({
19024
- fileId,
19025
- name: sym.name,
19026
- kind: sym.kind,
19027
- startLine: sym.startLine,
19028
- endLine: sym.endLine,
19029
- parentId,
19030
- exported: sym.exported,
19031
- signature: sym.signature ?? null
19032
- }).run();
19033
- if (sym.children.length > 0) {
19034
- const newId = Number(result.lastInsertRowid);
19035
- insertSymbols(db, fileId, sym.children, newId);
19036
- }
19037
- }
19038
- }
19039
- var init_pipeline = __esm({
19040
- "../core/dist/indexer/pipeline.js"() {
19122
+ var init_pipelines = __esm({
19123
+ "../core/dist/llm/pipelines.js"() {
19041
19124
  "use strict";
19042
19125
  init_drizzle_orm();
19043
19126
  init_connection();
19044
19127
  init_schema2();
19045
- init_discovery();
19046
- init_utils3();
19047
- init_parse();
19128
+ init_llm();
19129
+ init_provider();
19048
19130
  }
19049
19131
  });
19050
19132
 
19051
- // ../core/dist/indexer/watcher.js
19052
- import { watch } from "fs";
19053
- import { join as join8 } from "path";
19054
- import { readFile as readFile3, stat as stat2 } from "fs/promises";
19055
- var init_watcher = __esm({
19056
- "../core/dist/indexer/watcher.js"() {
19133
+ // ../core/dist/llm/index.js
19134
+ function getLlmProvider() {
19135
+ if (_cachedProvider)
19136
+ return _cachedProvider;
19137
+ const config2 = loadConfig();
19138
+ if (config2.llm.provider === "none")
19139
+ return null;
19140
+ _cachedProvider = new OllamaProvider(config2.llm.ollama);
19141
+ return _cachedProvider;
19142
+ }
19143
+ var _cachedProvider;
19144
+ var init_llm = __esm({
19145
+ "../core/dist/llm/index.js"() {
19146
+ "use strict";
19147
+ init_provider();
19148
+ init_ollama();
19149
+ init_pipelines();
19150
+ init_manager();
19151
+ init_ollama();
19152
+ _cachedProvider = null;
19153
+ }
19154
+ });
19155
+
19156
+ // ../core/dist/search/semantic.js
19157
+ async function semanticSearch(opts) {
19158
+ const provider = getLlmProvider();
19159
+ if (!provider) {
19160
+ throw new LlmUnavailableError("none", "LLM provider is set to 'none'");
19161
+ }
19162
+ if (!await provider.isAvailable()) {
19163
+ throw new LlmUnavailableError(provider.name, "Cannot reach Ollama for semantic search");
19164
+ }
19165
+ const maxResults = opts.maxResults ?? 10;
19166
+ const embedStart = Date.now();
19167
+ const queryVector = await provider.generateEmbedding(opts.query);
19168
+ const queryEmbeddingMs = Date.now() - embedStart;
19169
+ const searchStart = Date.now();
19170
+ const db = createProjectDb(opts.cwd);
19171
+ try {
19172
+ const rows = db.select({
19173
+ fileId: embeddings.fileId,
19174
+ vector: embeddings.vector,
19175
+ path: files.path,
19176
+ summary: files.summary,
19177
+ language: files.language
19178
+ }).from(embeddings).innerJoin(files, eq(embeddings.fileId, files.id)).all();
19179
+ const scored = [];
19180
+ for (const row of rows) {
19181
+ const fileVector = new Float32Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength / 4);
19182
+ const score = cosineSimilarity(queryVector, fileVector);
19183
+ scored.push({
19184
+ filePath: row.path,
19185
+ score,
19186
+ summary: row.summary,
19187
+ language: row.language
19188
+ });
19189
+ }
19190
+ scored.sort((a, b) => b.score - a.score);
19191
+ const matches = scored.slice(0, maxResults);
19192
+ const searchMs = Date.now() - searchStart;
19193
+ return { matches, queryEmbeddingMs, searchMs };
19194
+ } finally {
19195
+ closeDb(db);
19196
+ }
19197
+ }
19198
+ function cosineSimilarity(a, b) {
19199
+ if (a.length !== b.length)
19200
+ return 0;
19201
+ let dot = 0;
19202
+ let normA = 0;
19203
+ let normB = 0;
19204
+ for (let i = 0; i < a.length; i++) {
19205
+ dot += a[i] * b[i];
19206
+ normA += a[i] * a[i];
19207
+ normB += b[i] * b[i];
19208
+ }
19209
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
19210
+ return denom === 0 ? 0 : dot / denom;
19211
+ }
19212
+ var init_semantic = __esm({
19213
+ "../core/dist/search/semantic.js"() {
19057
19214
  "use strict";
19058
19215
  init_connection();
19059
19216
  init_schema2();
19060
- init_utils3();
19061
- init_manager();
19217
+ init_drizzle_orm();
19218
+ init_llm();
19219
+ init_provider();
19062
19220
  }
19063
19221
  });
19064
19222
 
19065
- // ../core/dist/indexer/index.js
19066
- var init_indexer = __esm({
19067
- "../core/dist/indexer/index.js"() {
19223
+ // ../core/dist/search/index.js
19224
+ var init_search = __esm({
19225
+ "../core/dist/search/index.js"() {
19068
19226
  "use strict";
19069
- init_discovery();
19070
- init_pipeline();
19071
- init_watcher();
19227
+ init_ripgrep();
19228
+ init_keyword();
19229
+ init_semantic();
19072
19230
  }
19073
19231
  });
19074
19232
 
@@ -19120,6 +19278,33 @@ async function initCommand(projectPath) {
19120
19278
  `Duration: ${stats.durationMs}ms`
19121
19279
  ].filter(Boolean).join("\n ")
19122
19280
  );
19281
+ const provider = getLlmProvider();
19282
+ if (provider && await provider.isAvailable()) {
19283
+ const llmSpinner = clack.spinner();
19284
+ llmSpinner.start("Generating LLM summaries...");
19285
+ try {
19286
+ const sumStats = await generateSummaries(root);
19287
+ llmSpinner.stop(
19288
+ `Summaries: ${sumStats.generated} generated, ${sumStats.skipped} skipped (${sumStats.durationMs}ms)`
19289
+ );
19290
+ } catch {
19291
+ llmSpinner.stop("Summary generation failed (Ollama error)");
19292
+ }
19293
+ const embSpinner = clack.spinner();
19294
+ embSpinner.start("Generating embeddings...");
19295
+ try {
19296
+ const embStats = await generateEmbeddings(root);
19297
+ embSpinner.stop(
19298
+ `Embeddings: ${embStats.generated} generated, ${embStats.skipped} skipped (${embStats.durationMs}ms)`
19299
+ );
19300
+ } catch {
19301
+ embSpinner.stop("Embedding generation failed (Ollama error)");
19302
+ }
19303
+ } else {
19304
+ clack.log.info(
19305
+ "LLM enrichment skipped (Ollama not available). Run `abf index --summarize` later."
19306
+ );
19307
+ }
19123
19308
  const installMcp = await clack.confirm({
19124
19309
  message: "Install ABF as an MCP server for your coding agents?",
19125
19310
  initialValue: true
@@ -19195,6 +19380,7 @@ var init_init = __esm({
19195
19380
  "src/commands/init.ts"() {
19196
19381
  "use strict";
19197
19382
  init_indexer();
19383
+ init_llm();
19198
19384
  execFileAsync5 = promisify5(execFile5);
19199
19385
  AGENTS = [
19200
19386
  { value: "cursor", label: "Cursor" },
@@ -19399,6 +19585,31 @@ async function reindex() {
19399
19585
  `Time: ${stats.durationMs}ms`
19400
19586
  ].join("\n ")
19401
19587
  );
19588
+ const provider = getLlmProvider();
19589
+ if (provider && await provider.isAvailable()) {
19590
+ const llmSpinner = clack3.spinner();
19591
+ llmSpinner.start("Generating LLM summaries...");
19592
+ try {
19593
+ const sumStats = await generateSummaries(root);
19594
+ llmSpinner.stop(
19595
+ `Summaries: ${sumStats.generated} generated, ${sumStats.skipped} skipped (${sumStats.durationMs}ms)`
19596
+ );
19597
+ } catch {
19598
+ llmSpinner.stop("Summary generation failed");
19599
+ }
19600
+ const embSpinner = clack3.spinner();
19601
+ embSpinner.start("Generating embeddings...");
19602
+ try {
19603
+ const embStats = await generateEmbeddings(root);
19604
+ embSpinner.stop(
19605
+ `Embeddings: ${embStats.generated} generated, ${embStats.skipped} skipped (${embStats.durationMs}ms)`
19606
+ );
19607
+ } catch {
19608
+ embSpinner.stop("Embedding generation failed");
19609
+ }
19610
+ } else {
19611
+ clack3.log.info("LLM enrichment skipped (Ollama not available).");
19612
+ }
19402
19613
  } catch (error48) {
19403
19614
  s.stop("Error");
19404
19615
  clack3.log.error(error48.message);
@@ -43161,6 +43372,9 @@ var StdioServerTransport = class {
43161
43372
  }
43162
43373
  };
43163
43374
 
43375
+ // ../server/dist/server.js
43376
+ init_indexer();
43377
+
43164
43378
  // ../server/dist/tools/ping.js
43165
43379
  function registerPingTool(server) {
43166
43380
  server.tool("abf_ping", "Health check tool. Returns server status and project root. Use this to verify the ABF MCP server is running correctly.", {
@@ -43170,7 +43384,7 @@ function registerPingTool(server) {
43170
43384
  const status = {
43171
43385
  status: "ok",
43172
43386
  server: "agents-best-friend",
43173
- version: "0.1.0",
43387
+ version: "0.4.0",
43174
43388
  projectRoot,
43175
43389
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
43176
43390
  };
@@ -43402,12 +43616,12 @@ async function handleSemanticSearch(opts) {
43402
43616
  }
43403
43617
 
43404
43618
  // ../core/dist/analysis/project-overview.js
43405
- init_utils3();
43619
+ init_utils();
43406
43620
  import { readFileSync as readFileSync4, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
43407
- import { join as join5, basename, relative as relative2 } from "path";
43408
- import { execFile as execFile2 } from "child_process";
43409
- import { promisify as promisify2 } from "util";
43410
- var execFileAsync2 = promisify2(execFile2);
43621
+ import { join as join7, basename, relative as relative3 } from "path";
43622
+ import { execFile as execFile3 } from "child_process";
43623
+ import { promisify as promisify3 } from "util";
43624
+ var execFileAsync3 = promisify3(execFile3);
43411
43625
  async function analyzeProject(projectRoot, detailLevel = "compact") {
43412
43626
  const files2 = await getFileList(projectRoot);
43413
43627
  const name = basename(projectRoot);
@@ -43422,7 +43636,7 @@ async function analyzeProject(projectRoot, detailLevel = "compact") {
43422
43636
  const fileSample = files2.length > 500 ? files2.slice(0, 500) : files2;
43423
43637
  for (const file2 of fileSample) {
43424
43638
  try {
43425
- const content = readFileSync4(join5(projectRoot, file2), "utf-8");
43639
+ const content = readFileSync4(join7(projectRoot, file2), "utf-8");
43426
43640
  totalLines += content.split("\n").length;
43427
43641
  } catch {
43428
43642
  }
@@ -43458,7 +43672,7 @@ async function analyzeProject(projectRoot, detailLevel = "compact") {
43458
43672
  }
43459
43673
  async function getFileList(cwd) {
43460
43674
  try {
43461
- const { stdout } = await execFileAsync2("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd, maxBuffer: 10 * 1024 * 1024 });
43675
+ const { stdout } = await execFileAsync3("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd, maxBuffer: 10 * 1024 * 1024 });
43462
43676
  return stdout.trim().split("\n").filter(Boolean);
43463
43677
  } catch {
43464
43678
  return walkDirSimple(cwd, cwd, 3);
@@ -43485,11 +43699,11 @@ function walkDirSimple(root, dir, depth) {
43485
43699
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
43486
43700
  if (ignore.has(entry.name))
43487
43701
  continue;
43488
- const full = join5(dir, entry.name);
43702
+ const full = join7(dir, entry.name);
43489
43703
  if (entry.isDirectory()) {
43490
43704
  results.push(...walkDirSimple(root, full, depth - 1));
43491
43705
  } else if (entry.isFile()) {
43492
- results.push(relative2(root, full));
43706
+ results.push(relative3(root, full));
43493
43707
  }
43494
43708
  }
43495
43709
  } catch {
@@ -43506,7 +43720,7 @@ function detectTechStack(root, files2, languages) {
43506
43720
  const hasFile = (name) => fileSet.has(name);
43507
43721
  let pkgJson = null;
43508
43722
  try {
43509
- pkgJson = JSON.parse(readFileSync4(join5(root, "package.json"), "utf-8"));
43723
+ pkgJson = JSON.parse(readFileSync4(join7(root, "package.json"), "utf-8"));
43510
43724
  } catch {
43511
43725
  }
43512
43726
  const allDeps = pkgJson ? {
@@ -43582,7 +43796,7 @@ function detectTechStack(root, files2, languages) {
43582
43796
  linters.push("Flake8");
43583
43797
  if (hasFile("pyproject.toml")) {
43584
43798
  try {
43585
- const pyproject = readFileSync4(join5(root, "pyproject.toml"), "utf-8");
43799
+ const pyproject = readFileSync4(join7(root, "pyproject.toml"), "utf-8");
43586
43800
  if (pyproject.includes("[tool.ruff]"))
43587
43801
  linters.push("Ruff");
43588
43802
  if (pyproject.includes("[tool.black]"))
@@ -43612,7 +43826,7 @@ function detectTechStack(root, files2, languages) {
43612
43826
  frameworks.push("Django");
43613
43827
  if (allDeps["flask"] || files2.some((f) => f.endsWith("app.py"))) {
43614
43828
  try {
43615
- const content = readFileSync4(join5(root, files2.find((f) => f.endsWith("app.py")) ?? ""), "utf-8");
43829
+ const content = readFileSync4(join7(root, files2.find((f) => f.endsWith("app.py")) ?? ""), "utf-8");
43616
43830
  if (content.includes("Flask"))
43617
43831
  frameworks.push("Flask");
43618
43832
  if (content.includes("FastAPI"))
@@ -43625,7 +43839,7 @@ function detectTechStack(root, files2, languages) {
43625
43839
  function detectEntryPoints(root, files2) {
43626
43840
  const entries = [];
43627
43841
  try {
43628
- const pkg = JSON.parse(readFileSync4(join5(root, "package.json"), "utf-8"));
43842
+ const pkg = JSON.parse(readFileSync4(join7(root, "package.json"), "utf-8"));
43629
43843
  if (pkg.main)
43630
43844
  entries.push({ type: "main", path: pkg.main });
43631
43845
  if (pkg.module)
@@ -43810,11 +44024,11 @@ function detectPatterns(root, files2, dirs) {
43810
44024
  const hasApps = dirs.some((d) => d.path === "apps");
43811
44025
  if (hasPackages || hasApps)
43812
44026
  patterns.push("Monorepo");
43813
- if (existsSync3(join5(root, "turbo.json")))
44027
+ if (existsSync3(join7(root, "turbo.json")))
43814
44028
  patterns.push("Turborepo");
43815
- if (existsSync3(join5(root, "lerna.json")))
44029
+ if (existsSync3(join7(root, "lerna.json")))
43816
44030
  patterns.push("Lerna");
43817
- if (existsSync3(join5(root, "nx.json")))
44031
+ if (existsSync3(join7(root, "nx.json")))
43818
44032
  patterns.push("Nx");
43819
44033
  const dirNames = new Set(dirs.map((d) => d.path));
43820
44034
  if (dirNames.has("controllers") && dirNames.has("models"))
@@ -43842,19 +44056,19 @@ function detectPatterns(root, files2, dirs) {
43842
44056
  return patterns;
43843
44057
  }
43844
44058
  function detectPackageManager(root) {
43845
- if (existsSync3(join5(root, "pnpm-lock.yaml")))
44059
+ if (existsSync3(join7(root, "pnpm-lock.yaml")))
43846
44060
  return "pnpm";
43847
- if (existsSync3(join5(root, "yarn.lock")))
44061
+ if (existsSync3(join7(root, "yarn.lock")))
43848
44062
  return "yarn";
43849
- if (existsSync3(join5(root, "bun.lockb")) || existsSync3(join5(root, "bun.lock")))
44063
+ if (existsSync3(join7(root, "bun.lockb")) || existsSync3(join7(root, "bun.lock")))
43850
44064
  return "bun";
43851
- if (existsSync3(join5(root, "package-lock.json")))
44065
+ if (existsSync3(join7(root, "package-lock.json")))
43852
44066
  return "npm";
43853
- if (existsSync3(join5(root, "Pipfile.lock")))
44067
+ if (existsSync3(join7(root, "Pipfile.lock")))
43854
44068
  return "pipenv";
43855
- if (existsSync3(join5(root, "poetry.lock")))
44069
+ if (existsSync3(join7(root, "poetry.lock")))
43856
44070
  return "poetry";
43857
- if (existsSync3(join5(root, "uv.lock")))
44071
+ if (existsSync3(join7(root, "uv.lock")))
43858
44072
  return "uv";
43859
44073
  return null;
43860
44074
  }
@@ -44411,20 +44625,20 @@ function formatOverview(o) {
44411
44625
  }
44412
44626
 
44413
44627
  // ../core/dist/git/client.js
44414
- import { execFile as execFile3 } from "child_process";
44415
- import { promisify as promisify3 } from "util";
44416
- var execFileAsync3 = promisify3(execFile3);
44628
+ import { execFile as execFile4 } from "child_process";
44629
+ import { promisify as promisify4 } from "util";
44630
+ var execFileAsync4 = promisify4(execFile4);
44417
44631
  var GIT_MAX_BUFFER = 10 * 1024 * 1024;
44418
44632
  async function isGitRepo(cwd) {
44419
44633
  try {
44420
- await execFileAsync3("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
44634
+ await execFileAsync4("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
44421
44635
  return true;
44422
44636
  } catch {
44423
44637
  return false;
44424
44638
  }
44425
44639
  }
44426
44640
  async function getRecentCommits(cwd, count = 10) {
44427
- const { stdout } = await execFileAsync3("git", [
44641
+ const { stdout } = await execFileAsync4("git", [
44428
44642
  "log",
44429
44643
  `--max-count=${count}`,
44430
44644
  "--format=%H%x00%h%x00%an%x00%aI%x00%s",
@@ -44433,7 +44647,7 @@ async function getRecentCommits(cwd, count = 10) {
44433
44647
  return parseGitLog(stdout);
44434
44648
  }
44435
44649
  async function getFileHistory(cwd, filePath, count = 20) {
44436
- const { stdout } = await execFileAsync3("git", [
44650
+ const { stdout } = await execFileAsync4("git", [
44437
44651
  "log",
44438
44652
  `--max-count=${count}`,
44439
44653
  "--format=%H%x00%h%x00%an%x00%aI%x00%s",
@@ -44450,7 +44664,7 @@ async function getBlame(cwd, filePath, lineRange) {
44450
44664
  args.push(`-L${lineRange[0]},${lineRange[1]}`);
44451
44665
  }
44452
44666
  args.push("--", filePath);
44453
- const { stdout } = await execFileAsync3("git", args, {
44667
+ const { stdout } = await execFileAsync4("git", args, {
44454
44668
  cwd,
44455
44669
  maxBuffer: GIT_MAX_BUFFER
44456
44670
  });
@@ -44459,15 +44673,15 @@ async function getBlame(cwd, filePath, lineRange) {
44459
44673
  async function getDiff(cwd, filePath) {
44460
44674
  const fileArgs = filePath ? ["--", filePath] : [];
44461
44675
  const [staged, unstaged, statOutput] = await Promise.all([
44462
- execFileAsync3("git", ["diff", "--cached", ...fileArgs], {
44676
+ execFileAsync4("git", ["diff", "--cached", ...fileArgs], {
44463
44677
  cwd,
44464
44678
  maxBuffer: GIT_MAX_BUFFER
44465
44679
  }).then((r) => r.stdout),
44466
- execFileAsync3("git", ["diff", ...fileArgs], {
44680
+ execFileAsync4("git", ["diff", ...fileArgs], {
44467
44681
  cwd,
44468
44682
  maxBuffer: GIT_MAX_BUFFER
44469
44683
  }).then((r) => r.stdout),
44470
- execFileAsync3("git", ["diff", "--stat", "HEAD", ...fileArgs], {
44684
+ execFileAsync4("git", ["diff", "--stat", "HEAD", ...fileArgs], {
44471
44685
  cwd,
44472
44686
  maxBuffer: GIT_MAX_BUFFER
44473
44687
  }).then((r) => r.stdout).catch(() => "")
@@ -44555,11 +44769,11 @@ var GitActionSchema = external_exports3.enum(["log", "file_history", "blame", "d
44555
44769
  function registerGitTool(server) {
44556
44770
  server.tool("abf_git", "Query git history, blame, and diff for the project.", {
44557
44771
  action: GitActionSchema.describe("Git action: log (recent commits), file_history, blame, diff"),
44558
- file: external_exports3.string().optional().describe("File path (required for file_history, blame; optional for diff)"),
44772
+ file_path: external_exports3.string().optional().describe("File path (required for file_history, blame; optional for diff)"),
44559
44773
  count: external_exports3.number().int().min(1).max(100).default(10).describe("Number of commits (log, file_history)"),
44560
44774
  line_start: external_exports3.number().int().optional().describe("Start line for blame range"),
44561
44775
  line_end: external_exports3.number().int().optional().describe("End line for blame range")
44562
- }, async ({ action, file: file2, count, line_start, line_end }) => {
44776
+ }, async ({ action, file_path, count, line_start, line_end }) => {
44563
44777
  const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
44564
44778
  if (!await isGitRepo(cwd)) {
44565
44779
  return {
@@ -44580,25 +44794,25 @@ function registerGitTool(server) {
44580
44794
  break;
44581
44795
  }
44582
44796
  case "file_history": {
44583
- if (!file2) {
44584
- return errorResult("file_history requires a `file` parameter.");
44797
+ if (!file_path) {
44798
+ return errorResult("file_history requires a `file_path` parameter.");
44585
44799
  }
44586
- const history = await getFileHistory(cwd, file2, count);
44800
+ const history = await getFileHistory(cwd, file_path, count);
44587
44801
  text4 = `History for ${history.filePath}:
44588
44802
  ` + formatCommits(history.commits);
44589
44803
  break;
44590
44804
  }
44591
44805
  case "blame": {
44592
- if (!file2) {
44593
- return errorResult("blame requires a `file` parameter.");
44806
+ if (!file_path) {
44807
+ return errorResult("blame requires a `file_path` parameter.");
44594
44808
  }
44595
44809
  const range = line_start && line_end ? [line_start, line_end] : void 0;
44596
- const blameLines = await getBlame(cwd, file2, range);
44810
+ const blameLines = await getBlame(cwd, file_path, range);
44597
44811
  text4 = formatBlame(blameLines);
44598
44812
  break;
44599
44813
  }
44600
44814
  case "diff": {
44601
- const diff = await getDiff(cwd, file2);
44815
+ const diff = await getDiff(cwd, file_path);
44602
44816
  const { filesChanged, insertions, deletions } = diff.stats;
44603
44817
  const header = `${filesChanged} file(s) changed, +${insertions} -${deletions}`;
44604
44818
  text4 = diff.combined ? `${header}
@@ -44641,8 +44855,8 @@ init_indexer();
44641
44855
  init_llm();
44642
44856
  var IndexActionSchema = external_exports3.enum(["status", "rebuild", "update", "summarize"]);
44643
44857
  function registerIndexTool(server) {
44644
- server.tool("abf_index", "Manage the file index: check status, trigger rebuild, incremental update, or generate LLM summaries (requires Ollama).", {
44645
- action: IndexActionSchema.describe("status: show index info, rebuild: full re-index, update: incremental update, summarize: generate LLM file summaries (requires Ollama)")
44858
+ server.tool("abf_index", "Manage the file index: check status, trigger rebuild, incremental update, or generate LLM summaries. Rebuild/update will auto-generate summaries when Ollama is available.", {
44859
+ action: IndexActionSchema.describe("status: show index info, rebuild: full re-index, update: incremental update, summarize: (re)generate LLM file summaries (requires Ollama)")
44646
44860
  }, async ({ action }) => {
44647
44861
  const projectRoot = process.env.ABF_PROJECT_ROOT || process.cwd();
44648
44862
  try {
@@ -44662,7 +44876,7 @@ function registerIndexTool(server) {
44662
44876
  case "rebuild":
44663
44877
  case "update": {
44664
44878
  const stats = await runIndexPipeline(projectRoot);
44665
- const text4 = [
44879
+ const textParts = [
44666
44880
  `Index ${action} complete (${stats.durationMs}ms)`,
44667
44881
  `Discovered: ${stats.totalDiscovered}`,
44668
44882
  `New: ${stats.indexed}`,
@@ -44670,17 +44884,40 @@ function registerIndexTool(server) {
44670
44884
  `Removed: ${stats.removed}`,
44671
44885
  `Unchanged: ${stats.skipped}`,
44672
44886
  stats.errors > 0 ? `Errors: ${stats.errors}` : null
44673
- ].filter(Boolean).join("\n");
44674
- return { content: [{ type: "text", text: text4 }] };
44887
+ ].filter(Boolean);
44888
+ const provider = getLlmProvider();
44889
+ if (provider && await provider.isAvailable()) {
44890
+ try {
44891
+ const sumStats = await generateSummaries(projectRoot);
44892
+ textParts.push("", `LLM summaries: ${sumStats.generated} generated, ${sumStats.skipped} skipped${sumStats.errors > 0 ? `, ${sumStats.errors} errors` : ""} (${sumStats.durationMs}ms)`);
44893
+ } catch {
44894
+ textParts.push("", "LLM summaries: skipped (Ollama error)");
44895
+ }
44896
+ try {
44897
+ const embStats = await generateEmbeddings(projectRoot);
44898
+ textParts.push(`Embeddings: ${embStats.generated} generated, ${embStats.skipped} skipped${embStats.errors > 0 ? `, ${embStats.errors} errors` : ""} (${embStats.durationMs}ms)`);
44899
+ } catch {
44900
+ textParts.push("Embeddings: skipped (Ollama error)");
44901
+ }
44902
+ }
44903
+ return {
44904
+ content: [{ type: "text", text: textParts.join("\n") }]
44905
+ };
44675
44906
  }
44676
44907
  case "summarize": {
44677
- const stats = await generateSummaries(projectRoot);
44908
+ const sumStats = await generateSummaries(projectRoot);
44909
+ const embStats = await generateEmbeddings(projectRoot);
44678
44910
  const text4 = [
44679
- `Summary generation complete (${stats.durationMs}ms)`,
44680
- `Generated: ${stats.generated}`,
44681
- `Skipped (already have summary): ${stats.skipped}`,
44682
- stats.errors > 0 ? `Errors: ${stats.errors}` : null
44683
- ].filter(Boolean).join("\n");
44911
+ `Summary generation complete (${sumStats.durationMs}ms)`,
44912
+ ` Generated: ${sumStats.generated}`,
44913
+ ` Skipped: ${sumStats.skipped}`,
44914
+ sumStats.errors > 0 ? ` Errors: ${sumStats.errors}` : null,
44915
+ "",
44916
+ `Embedding generation complete (${embStats.durationMs}ms)`,
44917
+ ` Generated: ${embStats.generated}`,
44918
+ ` Skipped: ${embStats.skipped}`,
44919
+ embStats.errors > 0 ? ` Errors: ${embStats.errors}` : null
44920
+ ].filter((l) => l !== null).join("\n");
44684
44921
  return { content: [{ type: "text", text: text4 }] };
44685
44922
  }
44686
44923
  }
@@ -45029,13 +45266,13 @@ function importMatchesTarget(importPath, sourceFile, targetRelPath, targetName)
45029
45266
  init_search();
45030
45267
  function registerImpactTool(server) {
45031
45268
  server.tool("abf_impact", "Find all files and lines that reference a given symbol name. Useful for change impact analysis.", {
45032
- symbol_name: external_exports3.string().describe("The symbol (function, class, variable) name to find references for"),
45269
+ symbol: external_exports3.string().describe("The symbol (function, class, variable) name to find references for"),
45033
45270
  file_path: external_exports3.string().optional().describe("Optional: scope search to usages of this symbol from this file")
45034
- }, async ({ symbol_name, file_path }) => {
45271
+ }, async ({ symbol: symbol2, file_path }) => {
45035
45272
  const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
45036
45273
  try {
45037
45274
  const results = await ripgrepSearch({
45038
- query: `\\b${escapeRegex2(symbol_name)}\\b`,
45275
+ query: `\\b${escapeRegex2(symbol2)}\\b`,
45039
45276
  cwd,
45040
45277
  maxResults: 50,
45041
45278
  regex: true,
@@ -45046,7 +45283,7 @@ function registerImpactTool(server) {
45046
45283
  content: [
45047
45284
  {
45048
45285
  type: "text",
45049
- text: `No references found for "${symbol_name}".`
45286
+ text: `No references found for "${symbol2}".`
45050
45287
  }
45051
45288
  ]
45052
45289
  };
@@ -45057,12 +45294,12 @@ function registerImpactTool(server) {
45057
45294
  group.push({
45058
45295
  line: match.lineNumber,
45059
45296
  text: match.lineText.trim(),
45060
- usage: classifyUsage(match.lineText, symbol_name)
45297
+ usage: classifyUsage(match.lineText, symbol2)
45061
45298
  });
45062
45299
  byFile.set(match.filePath, group);
45063
45300
  }
45064
45301
  const lines = [
45065
- `${results.totalMatches} references to "${symbol_name}" in ${byFile.size} files:`,
45302
+ `${results.totalMatches} references to "${symbol2}" in ${byFile.size} files:`,
45066
45303
  ""
45067
45304
  ];
45068
45305
  for (const [filePath, refs] of byFile) {
@@ -45272,7 +45509,7 @@ Purely heuristic \u2014 no LLM required. Useful to understand a project's style
45272
45509
 
45273
45510
  // ../server/dist/server.js
45274
45511
  var SERVER_NAME = "agents-best-friend";
45275
- var SERVER_VERSION = "0.1.0";
45512
+ var SERVER_VERSION = "0.4.0";
45276
45513
  function createAbfServer() {
45277
45514
  const server = new McpServer({
45278
45515
  name: SERVER_NAME,
@@ -45301,6 +45538,16 @@ function createAbfServer() {
45301
45538
  async function startStdioServer() {
45302
45539
  const server = createAbfServer();
45303
45540
  const transport = new StdioServerTransport();
45541
+ const projectRoot = process.env.ABF_PROJECT_ROOT || process.cwd();
45542
+ const watcher = watchProject(projectRoot);
45543
+ process.on("SIGINT", () => {
45544
+ watcher.close();
45545
+ process.exit(0);
45546
+ });
45547
+ process.on("SIGTERM", () => {
45548
+ watcher.close();
45549
+ process.exit(0);
45550
+ });
45304
45551
  await server.connect(transport);
45305
45552
  }
45306
45553
 
@@ -45311,7 +45558,7 @@ async function startCommand() {
45311
45558
 
45312
45559
  // src/index.ts
45313
45560
  var program = new Command();
45314
- program.name("abf").description("AgentsBestFriend \u2014 AI-first code navigation and analysis tools").version("0.1.0");
45561
+ program.name("abf").description("AgentsBestFriend \u2014 AI-first code navigation and analysis tools").version("0.4.0");
45315
45562
  program.command("start").description("Start the MCP server in stdio mode (for AI agent connections)").action(async () => {
45316
45563
  await startCommand();
45317
45564
  });