bluera-knowledge 0.17.2 → 0.18.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
@@ -3,34 +3,33 @@ import {
3
3
  ZilAdapter,
4
4
  runMCPServer,
5
5
  spawnBackgroundWorker
6
- } from "./chunk-YMDXPECI.js";
6
+ } from "./chunk-EZXJ3W5X.js";
7
7
  import {
8
8
  IntelligentCrawler,
9
9
  getCrawlStrategy
10
- } from "./chunk-PFHK5Q22.js";
10
+ } from "./chunk-VUGQ7HAR.js";
11
11
  import {
12
12
  ASTParser,
13
13
  AdapterRegistry,
14
14
  ChunkingService,
15
15
  JobService,
16
+ ProjectRootService,
16
17
  StoreDefinitionService,
17
18
  classifyWebContentType,
18
- createDocumentId,
19
19
  createServices,
20
- createStoreId,
21
20
  destroyServices,
22
21
  err,
23
22
  extractRepoName,
24
23
  isFileStoreDefinition,
24
+ isGitUrl,
25
25
  isRepoStoreDefinition,
26
26
  isWebStoreDefinition,
27
27
  ok
28
- } from "./chunk-WMALVLFW.js";
29
- import "./chunk-HRQD3MPH.js";
30
-
31
- // src/index.ts
32
- import { homedir as homedir2 } from "os";
33
- import { join as join5 } from "path";
28
+ } from "./chunk-RDDGZIDL.js";
29
+ import {
30
+ createDocumentId
31
+ } from "./chunk-CLIMKLTW.js";
32
+ import "./chunk-HXBIIMYL.js";
34
33
 
35
34
  // src/cli/commands/crawl.ts
36
35
  import { createHash } from "crypto";
@@ -61,7 +60,11 @@ function createCrawlCommand(getOptions) {
61
60
  );
62
61
  }
63
62
  }
64
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
63
+ const services = await createServices(
64
+ globalOpts.config,
65
+ globalOpts.dataDir,
66
+ globalOpts.projectRoot
67
+ );
65
68
  let store;
66
69
  let storeCreated = false;
67
70
  const existingStore = await services.store.getByIdOrName(storeIdOrName);
@@ -92,7 +95,12 @@ function createCrawlCommand(getOptions) {
92
95
  } else {
93
96
  store = existingStore;
94
97
  }
95
- const maxPages = cmdOptions.maxPages !== void 0 ? parseInt(cmdOptions.maxPages, 10) : 50;
98
+ const maxPages = parseInt(cmdOptions.maxPages, 10);
99
+ if (Number.isNaN(maxPages)) {
100
+ throw new Error(
101
+ `Invalid value for --max-pages: "${cmdOptions.maxPages}" is not a valid integer`
102
+ );
103
+ }
96
104
  const isInteractive = process.stdout.isTTY && globalOpts.quiet !== true && globalOpts.format !== "json";
97
105
  let spinner;
98
106
  if (isInteractive) {
@@ -101,7 +109,8 @@ function createCrawlCommand(getOptions) {
101
109
  } else if (globalOpts.quiet !== true && globalOpts.format !== "json") {
102
110
  console.log(`Crawling ${url}`);
103
111
  }
104
- const crawler = new IntelligentCrawler();
112
+ const appConfig = await services.config.load();
113
+ const crawler = new IntelligentCrawler(appConfig.crawl);
105
114
  const webChunker = ChunkingService.forContentType("web");
106
115
  let pagesIndexed = 0;
107
116
  let chunksCreated = 0;
@@ -122,6 +131,7 @@ function createCrawlCommand(getOptions) {
122
131
  }
123
132
  });
124
133
  try {
134
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
125
135
  await services.lance.initialize(store.id);
126
136
  const docs = [];
127
137
  for await (const result of crawler.crawl(url, {
@@ -150,7 +160,7 @@ function createCrawlCommand(getOptions) {
150
160
  title: result.title,
151
161
  extracted: result.extracted !== void 0,
152
162
  depth: result.depth,
153
- indexedAt: /* @__PURE__ */ new Date(),
163
+ indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
154
164
  fileType,
155
165
  chunkIndex: chunk.chunkIndex,
156
166
  totalChunks: chunk.totalChunks,
@@ -165,6 +175,7 @@ function createCrawlCommand(getOptions) {
165
175
  if (spinner) {
166
176
  spinner.text = "Indexing documents...";
167
177
  }
178
+ await services.lance.clearAllDocuments(store.id);
168
179
  await services.lance.addDocuments(store.id, docs);
169
180
  await services.lance.createFtsIndex(store.id);
170
181
  }
@@ -203,7 +214,7 @@ function createCrawlCommand(getOptions) {
203
214
  await destroyServices(services);
204
215
  }
205
216
  if (exitCode !== 0) {
206
- process.exit(exitCode);
217
+ process.exitCode = exitCode;
207
218
  }
208
219
  }
209
220
  );
@@ -213,9 +224,13 @@ function createCrawlCommand(getOptions) {
213
224
  import { Command as Command2 } from "commander";
214
225
  import ora2 from "ora";
215
226
  function createIndexCommand(getOptions) {
216
- const index = new Command2("index").description("Scan store files, chunk text, generate embeddings, save to LanceDB").argument("<store>", "Store ID or name").option("--force", "Re-index all files even if unchanged").action(async (storeIdOrName, _options) => {
227
+ const index = new Command2("index").description("Scan store files, chunk text, generate embeddings, save to LanceDB").argument("<store>", "Store ID or name").option("--force", "Re-index all files even if unchanged").action(async (storeIdOrName, options) => {
217
228
  const globalOpts = getOptions();
218
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
229
+ const services = await createServices(
230
+ globalOpts.config,
231
+ globalOpts.dataDir,
232
+ globalOpts.projectRoot
233
+ );
219
234
  let exitCode = 0;
220
235
  try {
221
236
  indexLogic: {
@@ -232,19 +247,21 @@ function createIndexCommand(getOptions) {
232
247
  } else if (globalOpts.quiet !== true && globalOpts.format !== "json") {
233
248
  console.log(`Indexing store: ${store.name}`);
234
249
  }
250
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
235
251
  await services.lance.initialize(store.id);
236
- const result = await services.index.indexStore(store, (event) => {
252
+ const progressCallback = (event) => {
237
253
  if (event.type === "progress") {
238
254
  if (spinner) {
239
255
  spinner.text = `Indexing: ${String(event.current)}/${String(event.total)} files - ${event.message}`;
240
256
  }
241
257
  }
242
- });
258
+ };
259
+ const result = options.force === true ? await services.index.indexStore(store, progressCallback) : await services.index.indexStoreIncremental(store, progressCallback);
243
260
  if (result.success) {
244
261
  if (globalOpts.format === "json") {
245
262
  console.log(JSON.stringify(result.data, null, 2));
246
263
  } else {
247
- const message = `Indexed ${String(result.data.documentsIndexed)} documents, ${String(result.data.chunksCreated)} chunks in ${String(result.data.timeMs)}ms`;
264
+ const message = `Indexed ${String(result.data.filesIndexed)} documents, ${String(result.data.chunksCreated)} chunks in ${String(result.data.timeMs)}ms`;
248
265
  if (spinner !== void 0) {
249
266
  spinner.succeed(message);
250
267
  } else if (globalOpts.quiet !== true) {
@@ -266,7 +283,7 @@ function createIndexCommand(getOptions) {
266
283
  await destroyServices(services);
267
284
  }
268
285
  if (exitCode !== 0) {
269
- process.exit(exitCode);
286
+ process.exitCode = exitCode;
270
287
  }
271
288
  });
272
289
  index.command("watch <store>").description("Watch store directory; re-index when files change").option(
@@ -275,20 +292,35 @@ function createIndexCommand(getOptions) {
275
292
  "1000"
276
293
  ).action(async (storeIdOrName, options) => {
277
294
  const globalOpts = getOptions();
278
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
295
+ const services = await createServices(
296
+ globalOpts.config,
297
+ globalOpts.dataDir,
298
+ globalOpts.projectRoot
299
+ );
279
300
  const store = await services.store.getByIdOrName(storeIdOrName);
280
301
  if (store === void 0 || store.type !== "file" && store.type !== "repo") {
281
302
  console.error(`Error: File/repo store not found: ${storeIdOrName}`);
282
- process.exit(3);
303
+ await destroyServices(services);
304
+ process.exitCode = 3;
305
+ return;
306
+ }
307
+ const appConfig = await services.config.load();
308
+ const { WatchService } = await import("./watch.service-VDSUQ72Z.js");
309
+ const watchService = new WatchService(services.index, services.lance, services.embeddings, {
310
+ ignorePatterns: appConfig.indexing.ignorePatterns
311
+ });
312
+ const debounceMs = parseInt(options.debounce, 10);
313
+ if (Number.isNaN(debounceMs)) {
314
+ throw new Error(
315
+ `Invalid value for --debounce: "${options.debounce}" is not a valid integer`
316
+ );
283
317
  }
284
- const { WatchService } = await import("./watch.service-OPLKIDFQ.js");
285
- const watchService = new WatchService(services.index, services.lance);
286
318
  if (globalOpts.quiet !== true) {
287
319
  console.log(`Watching ${store.name} for changes...`);
288
320
  }
289
321
  await watchService.watch(
290
322
  store,
291
- parseInt(options.debounce ?? "1000", 10),
323
+ debounceMs,
292
324
  () => {
293
325
  if (globalOpts.quiet !== true) {
294
326
  console.log(`Re-indexed ${store.name}`);
@@ -301,8 +333,11 @@ function createIndexCommand(getOptions) {
301
333
  process.on("SIGINT", () => {
302
334
  void (async () => {
303
335
  await watchService.unwatchAll();
336
+ await destroyServices(services);
304
337
  process.exit(0);
305
- })().catch(() => {
338
+ })().catch((err2) => {
339
+ console.error("Shutdown error:", err2);
340
+ process.exit(1);
306
341
  });
307
342
  });
308
343
  });
@@ -316,7 +351,8 @@ function createMCPCommand(getOptions) {
316
351
  const opts = getOptions();
317
352
  await runMCPServer({
318
353
  dataDir: opts.dataDir,
319
- config: opts.config
354
+ config: opts.config,
355
+ projectRoot: opts.projectRoot
320
356
  });
321
357
  });
322
358
  return mcp;
@@ -509,6 +545,7 @@ var DependencyUsageAnalyzer = class {
509
545
  if (![
510
546
  "node_modules",
511
547
  ".git",
548
+ ".bluera",
512
549
  "dist",
513
550
  "build",
514
551
  "coverage",
@@ -815,7 +852,8 @@ async function handleAddRepo(args, options = {}) {
815
852
  });
816
853
  if (!result.success) {
817
854
  console.error(`Error: ${result.error.message}`);
818
- process.exit(1);
855
+ process.exitCode = 1;
856
+ return;
819
857
  }
820
858
  console.log(`Created store: ${storeName} (${result.data.id})`);
821
859
  if ("path" in result.data) {
@@ -824,7 +862,7 @@ async function handleAddRepo(args, options = {}) {
824
862
  console.log("\nIndexing...");
825
863
  const indexResult = await services.index.indexStore(result.data);
826
864
  if (indexResult.success) {
827
- console.log(`Indexed ${String(indexResult.data.documentsIndexed)} files`);
865
+ console.log(`Indexed ${String(indexResult.data.filesIndexed)} files`);
828
866
  } else {
829
867
  console.error(`Indexing failed: ${indexResult.error.message}`);
830
868
  }
@@ -849,7 +887,8 @@ async function handleAddFolder(args, options = {}) {
849
887
  });
850
888
  if (!result.success) {
851
889
  console.error(`Error: ${result.error.message}`);
852
- process.exit(1);
890
+ process.exitCode = 1;
891
+ return;
853
892
  }
854
893
  console.log(`Created store: ${storeName} (${result.data.id})`);
855
894
  if ("path" in result.data) {
@@ -858,7 +897,7 @@ async function handleAddFolder(args, options = {}) {
858
897
  console.log("\nIndexing...");
859
898
  const indexResult = await services.index.indexStore(result.data);
860
899
  if (indexResult.success) {
861
- console.log(`Indexed ${String(indexResult.data.documentsIndexed)} files`);
900
+ console.log(`Indexed ${String(indexResult.data.filesIndexed)} files`);
862
901
  } else {
863
902
  console.error(`Indexing failed: ${indexResult.error.message}`);
864
903
  }
@@ -913,7 +952,8 @@ async function handleSuggest(options = {}) {
913
952
  spinner.stop();
914
953
  if (!result.success) {
915
954
  console.error(`Error: ${result.error.message}`);
916
- process.exit(1);
955
+ process.exitCode = 1;
956
+ return;
917
957
  }
918
958
  const { usages, totalFilesScanned, skippedFiles } = result.data;
919
959
  console.log(
@@ -1031,14 +1071,18 @@ function createSearchCommand(getOptions) {
1031
1071
  ).option("-n, --limit <count>", "Maximum results to return (default: 10)", "10").option("-t, --threshold <score>", "Minimum score 0-1; omit low-relevance results").option(
1032
1072
  "--min-relevance <score>",
1033
1073
  "Minimum raw cosine similarity 0-1; returns empty if no results meet threshold"
1034
- ).option("--include-content", "Show full document content, not just preview snippet").option(
1074
+ ).option(
1035
1075
  "--detail <level>",
1036
1076
  "Context detail: minimal, contextual, full (default: minimal)",
1037
1077
  "minimal"
1038
1078
  ).action(
1039
1079
  async (query, options) => {
1040
1080
  const globalOpts = getOptions();
1041
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1081
+ const services = await createServices(
1082
+ globalOpts.config,
1083
+ globalOpts.dataDir,
1084
+ globalOpts.projectRoot
1085
+ );
1042
1086
  let exitCode = 0;
1043
1087
  try {
1044
1088
  let storeIds = (await services.store.list()).map((s) => s.id);
@@ -1063,18 +1107,48 @@ function createSearchCommand(getOptions) {
1063
1107
  exitCode = 1;
1064
1108
  break searchLogic;
1065
1109
  }
1110
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1066
1111
  for (const storeId of storeIds) {
1067
1112
  await services.lance.initialize(storeId);
1068
1113
  }
1114
+ const limit = parseInt(options.limit, 10);
1115
+ if (Number.isNaN(limit)) {
1116
+ throw new Error(
1117
+ `Invalid value for --limit: "${options.limit}" is not a valid integer`
1118
+ );
1119
+ }
1120
+ let threshold;
1121
+ if (options.threshold !== void 0) {
1122
+ threshold = parseFloat(options.threshold);
1123
+ if (Number.isNaN(threshold)) {
1124
+ throw new Error(
1125
+ `Invalid value for --threshold: "${options.threshold}" is not a valid number`
1126
+ );
1127
+ }
1128
+ if (threshold < 0 || threshold > 1) {
1129
+ throw new Error(`Invalid value for --threshold: must be between 0 and 1`);
1130
+ }
1131
+ }
1132
+ let minRelevance;
1133
+ if (options.minRelevance !== void 0) {
1134
+ minRelevance = parseFloat(options.minRelevance);
1135
+ if (Number.isNaN(minRelevance)) {
1136
+ throw new Error(
1137
+ `Invalid value for --min-relevance: "${options.minRelevance}" is not a valid number`
1138
+ );
1139
+ }
1140
+ if (minRelevance < 0 || minRelevance > 1) {
1141
+ throw new Error(`Invalid value for --min-relevance: must be between 0 and 1`);
1142
+ }
1143
+ }
1069
1144
  const results = await services.search.search({
1070
1145
  query,
1071
1146
  stores: storeIds,
1072
- mode: options.mode ?? "hybrid",
1073
- limit: parseInt(options.limit ?? "10", 10),
1074
- threshold: options.threshold !== void 0 ? parseFloat(options.threshold) : void 0,
1075
- minRelevance: options.minRelevance !== void 0 ? parseFloat(options.minRelevance) : void 0,
1076
- includeContent: options.includeContent,
1077
- detail: options.detail ?? "minimal"
1147
+ mode: options.mode,
1148
+ limit,
1149
+ threshold,
1150
+ minRelevance,
1151
+ detail: options.detail
1078
1152
  });
1079
1153
  if (globalOpts.format === "json") {
1080
1154
  console.log(JSON.stringify(results, null, 2));
@@ -1086,7 +1160,7 @@ function createSearchCommand(getOptions) {
1086
1160
  } else {
1087
1161
  console.log(`
1088
1162
  Search: "${query}"`);
1089
- let statusLine = `Mode: ${results.mode} | Detail: ${String(options.detail)} | Stores: ${String(results.stores.length)} | Results: ${String(results.totalResults)} | Time: ${String(results.timeMs)}ms`;
1163
+ let statusLine = `Mode: ${results.mode} | Detail: ${options.detail} | Stores: ${String(results.stores.length)} | Results: ${String(results.totalResults)} | Time: ${String(results.timeMs)}ms`;
1090
1164
  if (results.confidence !== void 0) {
1091
1165
  statusLine += ` | Confidence: ${results.confidence}`;
1092
1166
  }
@@ -1168,6 +1242,8 @@ import { serve } from "@hono/node-server";
1168
1242
  import { Command as Command6 } from "commander";
1169
1243
 
1170
1244
  // src/server/app.ts
1245
+ import { rm } from "fs/promises";
1246
+ import { join as join2 } from "path";
1171
1247
  import { Hono } from "hono";
1172
1248
  import { cors } from "hono/cors";
1173
1249
  import { z } from "zod";
@@ -1229,6 +1305,13 @@ function createApp(services) {
1229
1305
  app.delete("/api/stores/:id", async (c) => {
1230
1306
  const store = await services.store.getByIdOrName(c.req.param("id"));
1231
1307
  if (!store) return c.json({ error: "Not found" }, 404);
1308
+ await services.lance.deleteStore(store.id);
1309
+ await services.codeGraph.deleteGraph(store.id);
1310
+ await services.manifest.delete(store.id);
1311
+ const resolvedDataDir = services.config.resolveDataDir();
1312
+ if (store.type === "repo" && "url" in store && store.url !== void 0 && store.path.startsWith(join2(resolvedDataDir, "repos"))) {
1313
+ await rm(store.path, { recursive: true, force: true });
1314
+ }
1232
1315
  const result = await services.store.delete(store.id);
1233
1316
  if (result.success) return c.json({ deleted: true });
1234
1317
  return c.json({ error: result.error.message }, 400);
@@ -1240,10 +1323,22 @@ function createApp(services) {
1240
1323
  return c.json({ error: parseResult.error.issues[0]?.message ?? "Invalid request body" }, 400);
1241
1324
  }
1242
1325
  const storeIds = (await services.store.list()).map((s) => s.id);
1326
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1243
1327
  for (const id of storeIds) {
1244
1328
  await services.lance.initialize(id);
1245
1329
  }
1246
- const requestedStores = parseResult.data.stores !== void 0 ? parseResult.data.stores.map((s) => createStoreId(s)) : storeIds;
1330
+ let requestedStores = storeIds;
1331
+ if (parseResult.data.stores !== void 0) {
1332
+ const resolvedStores = [];
1333
+ for (const requested of parseResult.data.stores) {
1334
+ const store = await services.store.getByIdOrName(requested);
1335
+ if (store === void 0) {
1336
+ return c.json({ error: `Store not found: ${requested}` }, 404);
1337
+ }
1338
+ resolvedStores.push(store.id);
1339
+ }
1340
+ requestedStores = resolvedStores;
1341
+ }
1247
1342
  const query = {
1248
1343
  query: parseResult.data.query,
1249
1344
  detail: parseResult.data.detail ?? "minimal",
@@ -1256,6 +1351,7 @@ function createApp(services) {
1256
1351
  app.post("/api/stores/:id/index", async (c) => {
1257
1352
  const store = await services.store.getByIdOrName(c.req.param("id"));
1258
1353
  if (!store) return c.json({ error: "Not found" }, 404);
1354
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1259
1355
  await services.lance.initialize(store.id);
1260
1356
  const result = await services.index.indexStore(store);
1261
1357
  if (result.success) return c.json(result.data);
@@ -1266,16 +1362,28 @@ function createApp(services) {
1266
1362
 
1267
1363
  // src/cli/commands/serve.ts
1268
1364
  function createServeCommand(getOptions) {
1269
- return new Command6("serve").description("Start HTTP API server for programmatic search access").option("-p, --port <port>", "Port to listen on (default: 3847)", "3847").option(
1365
+ return new Command6("serve").description("Start HTTP API server for programmatic search access").option("-p, --port <port>", "Port to listen on (reads from config if not specified)").option(
1270
1366
  "--host <host>",
1271
- "Bind address (default: 127.0.0.1, use 0.0.0.0 for all interfaces)",
1272
- "127.0.0.1"
1367
+ "Bind address (reads from config if not specified, use 0.0.0.0 for all interfaces)"
1273
1368
  ).action(async (options) => {
1274
1369
  const globalOpts = getOptions();
1275
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1370
+ const services = await createServices(
1371
+ globalOpts.config,
1372
+ globalOpts.dataDir,
1373
+ globalOpts.projectRoot
1374
+ );
1375
+ const appConfig = await services.config.load();
1276
1376
  const app = createApp(services);
1277
- const port = parseInt(options.port ?? "3847", 10);
1278
- const host = options.host ?? "127.0.0.1";
1377
+ let port;
1378
+ if (options.port !== void 0) {
1379
+ port = parseInt(options.port, 10);
1380
+ if (Number.isNaN(port)) {
1381
+ throw new Error(`Invalid value for --port: "${options.port}" is not a valid integer`);
1382
+ }
1383
+ } else {
1384
+ port = appConfig.server.port;
1385
+ }
1386
+ const host = options.host ?? appConfig.server.host;
1279
1387
  console.log(`Starting server on http://${host}:${String(port)}`);
1280
1388
  const server = serve({
1281
1389
  fetch: app.fetch,
@@ -1300,7 +1408,7 @@ import { spawnSync } from "child_process";
1300
1408
  import { existsSync as existsSync2 } from "fs";
1301
1409
  import { mkdir } from "fs/promises";
1302
1410
  import { homedir } from "os";
1303
- import { join as join2 } from "path";
1411
+ import { join as join3 } from "path";
1304
1412
  import { Command as Command7 } from "commander";
1305
1413
  import ora4 from "ora";
1306
1414
 
@@ -1357,7 +1465,7 @@ var DEFAULT_REPOS = [
1357
1465
  ];
1358
1466
 
1359
1467
  // src/cli/commands/setup.ts
1360
- var DEFAULT_REPOS_DIR = join2(homedir(), ".bluera", "bluera-knowledge", "repos");
1468
+ var DEFAULT_REPOS_DIR = join3(homedir(), ".bluera", "bluera-knowledge", "repos");
1361
1469
  function createSetupCommand(getOptions) {
1362
1470
  const setup = new Command7("setup").description(
1363
1471
  "Quick-start with pre-configured Claude/Anthropic documentation repos"
@@ -1382,26 +1490,31 @@ function createSetupCommand(getOptions) {
1382
1490
  }
1383
1491
  return;
1384
1492
  }
1385
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1386
- try {
1387
- let repos = DEFAULT_REPOS;
1388
- if (options.only !== void 0 && options.only !== "") {
1389
- const onlyNames = options.only.split(",").map((n) => n.trim().toLowerCase());
1390
- repos = DEFAULT_REPOS.filter(
1391
- (r) => onlyNames.some((n) => r.name.toLowerCase().includes(n))
1392
- );
1393
- if (repos.length === 0) {
1394
- console.error(`No repos matched: ${options.only}`);
1395
- console.log("Available repos:", DEFAULT_REPOS.map((r) => r.name).join(", "));
1396
- process.exit(1);
1397
- }
1493
+ let repos = DEFAULT_REPOS;
1494
+ if (options.only !== void 0 && options.only !== "") {
1495
+ const onlyNames = options.only.split(",").map((n) => n.trim().toLowerCase());
1496
+ repos = DEFAULT_REPOS.filter(
1497
+ (r) => onlyNames.some((n) => r.name.toLowerCase().includes(n))
1498
+ );
1499
+ if (repos.length === 0) {
1500
+ console.error(`No repos matched: ${options.only}`);
1501
+ console.log("Available repos:", DEFAULT_REPOS.map((r) => r.name).join(", "));
1502
+ process.exitCode = 1;
1503
+ return;
1398
1504
  }
1505
+ }
1506
+ const services = await createServices(
1507
+ globalOpts.config,
1508
+ globalOpts.dataDir,
1509
+ globalOpts.projectRoot
1510
+ );
1511
+ try {
1399
1512
  console.log(`
1400
1513
  Setting up ${String(repos.length)} repositories...
1401
1514
  `);
1402
1515
  await mkdir(options.reposDir, { recursive: true });
1403
1516
  for (const repo of repos) {
1404
- const repoPath = join2(options.reposDir, repo.name);
1517
+ const repoPath = join3(options.reposDir, repo.name);
1405
1518
  const spinner = ora4(`Processing ${repo.name}`).start();
1406
1519
  try {
1407
1520
  if (options.skipClone !== true) {
@@ -1450,6 +1563,7 @@ Setting up ${String(repos.length)} repositories...
1450
1563
  spinner.text = `${repo.name}: Indexing...`;
1451
1564
  const store = await services.store.getByIdOrName(storeId);
1452
1565
  if (store) {
1566
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1453
1567
  await services.lance.initialize(store.id);
1454
1568
  const indexResult = await services.index.indexStore(store, (event) => {
1455
1569
  if (event.type === "progress") {
@@ -1458,7 +1572,7 @@ Setting up ${String(repos.length)} repositories...
1458
1572
  });
1459
1573
  if (indexResult.success) {
1460
1574
  spinner.succeed(
1461
- `${repo.name}: ${String(indexResult.data.documentsIndexed)} docs, ${String(indexResult.data.chunksCreated)} chunks`
1575
+ `${repo.name}: ${String(indexResult.data.filesIndexed)} docs, ${String(indexResult.data.chunksCreated)} chunks`
1462
1576
  );
1463
1577
  } else {
1464
1578
  throw new Error(
@@ -1485,8 +1599,8 @@ Setting up ${String(repos.length)} repositories...
1485
1599
  }
1486
1600
 
1487
1601
  // src/cli/commands/store.ts
1488
- import { rm } from "fs/promises";
1489
- import { join as join3 } from "path";
1602
+ import { rm as rm2 } from "fs/promises";
1603
+ import { join as join4 } from "path";
1490
1604
  import { Command as Command8 } from "commander";
1491
1605
  function createStoreCommand(getOptions) {
1492
1606
  const store = new Command8("store").description(
@@ -1494,7 +1608,11 @@ function createStoreCommand(getOptions) {
1494
1608
  );
1495
1609
  store.command("list").description("Show all stores with their type (file/repo/web) and ID").option("-t, --type <type>", "Filter by type: file, repo, or web").action(async (options) => {
1496
1610
  const globalOpts = getOptions();
1497
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1611
+ const services = await createServices(
1612
+ globalOpts.config,
1613
+ globalOpts.dataDir,
1614
+ globalOpts.projectRoot
1615
+ );
1498
1616
  try {
1499
1617
  const stores = await services.store.list(options.type);
1500
1618
  if (globalOpts.format === "json") {
@@ -1524,10 +1642,14 @@ function createStoreCommand(getOptions) {
1524
1642
  ).requiredOption("-s, --source <path>", "Local path for file/repo stores, URL for web stores").option("-b, --branch <branch>", "Git branch to clone (repo stores only)").option("-d, --description <desc>", "Optional description for the store").option("--tags <tags>", "Comma-separated tags for filtering").action(
1525
1643
  async (name, options) => {
1526
1644
  const globalOpts = getOptions();
1527
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1645
+ const services = await createServices(
1646
+ globalOpts.config,
1647
+ globalOpts.dataDir,
1648
+ globalOpts.projectRoot
1649
+ );
1528
1650
  let exitCode = 0;
1529
1651
  try {
1530
- const isUrl = options.source.startsWith("http://") || options.source.startsWith("https://");
1652
+ const isUrl = isGitUrl(options.source);
1531
1653
  const result = await services.store.create({
1532
1654
  name,
1533
1655
  type: options.type,
@@ -1559,7 +1681,11 @@ Created store: ${result.data.name} (${result.data.id})
1559
1681
  );
1560
1682
  store.command("info <store>").description("Show store details: ID, type, path/URL, timestamps").action(async (storeIdOrName) => {
1561
1683
  const globalOpts = getOptions();
1562
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1684
+ const services = await createServices(
1685
+ globalOpts.config,
1686
+ globalOpts.dataDir,
1687
+ globalOpts.projectRoot
1688
+ );
1563
1689
  let exitCode = 0;
1564
1690
  storeInfo: try {
1565
1691
  const s = await services.store.getByIdOrName(storeIdOrName);
@@ -1591,7 +1717,11 @@ Store: ${s.name}`);
1591
1717
  });
1592
1718
  store.command("delete <store>").description("Remove store and its indexed documents from LanceDB").option("-f, --force", "Delete without confirmation prompt").option("-y, --yes", "Alias for --force").action(async (storeIdOrName, options) => {
1593
1719
  const globalOpts = getOptions();
1594
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1720
+ const services = await createServices(
1721
+ globalOpts.config,
1722
+ globalOpts.dataDir,
1723
+ globalOpts.projectRoot
1724
+ );
1595
1725
  let exitCode = 0;
1596
1726
  storeDelete: try {
1597
1727
  const s = await services.store.getByIdOrName(storeIdOrName);
@@ -1625,10 +1755,11 @@ Store: ${s.name}`);
1625
1755
  }
1626
1756
  await services.lance.deleteStore(s.id);
1627
1757
  await services.codeGraph.deleteGraph(s.id);
1758
+ await services.manifest.delete(s.id);
1628
1759
  if (s.type === "repo" && "url" in s && s.url !== void 0) {
1629
1760
  const dataDir = services.config.resolveDataDir();
1630
- const repoPath = join3(dataDir, "repos", s.id);
1631
- await rm(repoPath, { recursive: true, force: true });
1761
+ const repoPath = join4(dataDir, "repos", s.id);
1762
+ await rm2(repoPath, { recursive: true, force: true });
1632
1763
  }
1633
1764
  const result = await services.store.delete(s.id);
1634
1765
  if (result.success) {
@@ -1648,6 +1779,8 @@ Store: ${s.name}`);
1648
1779
  }
1649
1780
 
1650
1781
  // src/cli/commands/sync.ts
1782
+ import { rm as rm3 } from "fs/promises";
1783
+ import { join as join5 } from "path";
1651
1784
  import { Command as Command9 } from "commander";
1652
1785
  async function createStoreFromDefinition(def, defService, storeService) {
1653
1786
  try {
@@ -1693,6 +1826,9 @@ async function createStoreFromDefinition(def, defService, storeService) {
1693
1826
  type: "web",
1694
1827
  url: def.url,
1695
1828
  depth: def.depth,
1829
+ maxPages: def.maxPages,
1830
+ crawlInstructions: def.crawlInstructions,
1831
+ extractInstructions: def.extractInstructions,
1696
1832
  description: def.description,
1697
1833
  tags: def.tags
1698
1834
  },
@@ -1717,7 +1853,7 @@ function createSyncCommand(getOptions) {
1717
1853
  );
1718
1854
  sync.option("--dry-run", "Show what would happen without making changes").option("--prune", "Remove stores not in definitions").option("--reindex", "Re-index existing stores after sync").action(async (options) => {
1719
1855
  const globalOpts = getOptions();
1720
- const projectRoot = globalOpts.projectRoot ?? process.cwd();
1856
+ const projectRoot = globalOpts.projectRoot ?? ProjectRootService.resolve();
1721
1857
  const defService = new StoreDefinitionService(projectRoot);
1722
1858
  const services = await createServices(globalOpts.config, globalOpts.dataDir, projectRoot);
1723
1859
  try {
@@ -1765,6 +1901,14 @@ function createSyncCommand(getOptions) {
1765
1901
  for (const orphanName of result.orphans) {
1766
1902
  const store = await services.store.getByName(orphanName);
1767
1903
  if (store !== void 0) {
1904
+ await services.lance.deleteStore(store.id);
1905
+ await services.codeGraph.deleteGraph(store.id);
1906
+ await services.manifest.delete(store.id);
1907
+ if (store.type === "repo" && "url" in store && store.url !== void 0) {
1908
+ const dataDir = services.config.resolveDataDir();
1909
+ const repoPath = join5(dataDir, "repos", store.id);
1910
+ await rm3(repoPath, { recursive: true, force: true });
1911
+ }
1768
1912
  const deleteResult = await services.store.delete(store.id, {
1769
1913
  skipDefinitionSync: true
1770
1914
  });
@@ -1892,13 +2036,13 @@ function printHumanReadable(result, quiet) {
1892
2036
 
1893
2037
  // src/cli/program.ts
1894
2038
  import { readFileSync } from "fs";
1895
- import { dirname, join as join4 } from "path";
2039
+ import { dirname, join as join6 } from "path";
1896
2040
  import { fileURLToPath } from "url";
1897
2041
  import { Command as Command10 } from "commander";
1898
2042
  function getVersion() {
1899
2043
  const __filename2 = fileURLToPath(import.meta.url);
1900
2044
  const __dirname2 = dirname(__filename2);
1901
- const content = readFileSync(join4(__dirname2, "../package.json"), "utf-8");
2045
+ const content = readFileSync(join6(__dirname2, "../package.json"), "utf-8");
1902
2046
  const pkg = JSON.parse(content);
1903
2047
  return pkg.version;
1904
2048
  }
@@ -1906,7 +2050,7 @@ var version = getVersion();
1906
2050
  function createProgram() {
1907
2051
  const program2 = new Command10();
1908
2052
  program2.name("bluera-knowledge").description("CLI tool for managing knowledge stores with semantic search").version(version);
1909
- program2.option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", "Data directory").option("-p, --project-root <path>", "Project root directory (for resolving relative paths)").option("-f, --format <format>", "Output format: json | table | plain", "table").option("-q, --quiet", "Suppress non-essential output").option("-v, --verbose", "Enable verbose logging");
2053
+ program2.option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", "Data directory").option("-p, --project-root <path>", "Project root directory (for resolving relative paths)").option("-f, --format <format>", "Output format: json | table | plain", "table").option("-q, --quiet", "Suppress non-essential output");
1910
2054
  return program2;
1911
2055
  }
1912
2056
  function getGlobalOptions(program2) {
@@ -1916,17 +2060,16 @@ function getGlobalOptions(program2) {
1916
2060
  dataDir: opts.dataDir,
1917
2061
  projectRoot: opts.projectRoot,
1918
2062
  format: opts.format,
1919
- quiet: opts.quiet,
1920
- verbose: opts.verbose
2063
+ quiet: opts.quiet
1921
2064
  };
1922
2065
  }
1923
2066
 
1924
2067
  // src/index.ts
1925
2068
  var registry = AdapterRegistry.getInstance();
1926
2069
  registry.register(new ZilAdapter());
1927
- var DEFAULT_DATA_DIR = join5(homedir2(), ".bluera", "bluera-knowledge", "data");
1928
- var DEFAULT_CONFIG = join5(homedir2(), ".bluera", "bluera-knowledge", "config.json");
1929
- var DEFAULT_REPOS_DIR2 = join5(homedir2(), ".bluera", "bluera-knowledge", "repos");
2070
+ var DEFAULT_DATA_DIR = ".bluera/bluera-knowledge/data";
2071
+ var DEFAULT_CONFIG = ".bluera/bluera-knowledge/config.json";
2072
+ var DEFAULT_REPOS_DIR2 = ".bluera/bluera-knowledge/data/repos/<store-id>/";
1930
2073
  function formatCommandHelp(cmd, indent = "") {
1931
2074
  const lines = [];
1932
2075
  const name = cmd.name();