bluera-knowledge 0.17.2 → 0.18.1

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,16 +852,19 @@ 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) {
822
860
  console.log(`Location: ${result.data.path}`);
823
861
  }
824
862
  console.log("\nIndexing...");
863
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
864
+ await services.lance.initialize(result.data.id);
825
865
  const indexResult = await services.index.indexStore(result.data);
826
866
  if (indexResult.success) {
827
- console.log(`Indexed ${String(indexResult.data.documentsIndexed)} files`);
867
+ console.log(`Indexed ${String(indexResult.data.filesIndexed)} files`);
828
868
  } else {
829
869
  console.error(`Indexing failed: ${indexResult.error.message}`);
830
870
  }
@@ -849,16 +889,19 @@ async function handleAddFolder(args, options = {}) {
849
889
  });
850
890
  if (!result.success) {
851
891
  console.error(`Error: ${result.error.message}`);
852
- process.exit(1);
892
+ process.exitCode = 1;
893
+ return;
853
894
  }
854
895
  console.log(`Created store: ${storeName} (${result.data.id})`);
855
896
  if ("path" in result.data) {
856
897
  console.log(`Location: ${result.data.path}`);
857
898
  }
858
899
  console.log("\nIndexing...");
900
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
901
+ await services.lance.initialize(result.data.id);
859
902
  const indexResult = await services.index.indexStore(result.data);
860
903
  if (indexResult.success) {
861
- console.log(`Indexed ${String(indexResult.data.documentsIndexed)} files`);
904
+ console.log(`Indexed ${String(indexResult.data.filesIndexed)} files`);
862
905
  } else {
863
906
  console.error(`Indexing failed: ${indexResult.error.message}`);
864
907
  }
@@ -913,7 +956,8 @@ async function handleSuggest(options = {}) {
913
956
  spinner.stop();
914
957
  if (!result.success) {
915
958
  console.error(`Error: ${result.error.message}`);
916
- process.exit(1);
959
+ process.exitCode = 1;
960
+ return;
917
961
  }
918
962
  const { usages, totalFilesScanned, skippedFiles } = result.data;
919
963
  console.log(
@@ -1031,14 +1075,18 @@ function createSearchCommand(getOptions) {
1031
1075
  ).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
1076
  "--min-relevance <score>",
1033
1077
  "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(
1078
+ ).option(
1035
1079
  "--detail <level>",
1036
1080
  "Context detail: minimal, contextual, full (default: minimal)",
1037
1081
  "minimal"
1038
1082
  ).action(
1039
1083
  async (query, options) => {
1040
1084
  const globalOpts = getOptions();
1041
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1085
+ const services = await createServices(
1086
+ globalOpts.config,
1087
+ globalOpts.dataDir,
1088
+ globalOpts.projectRoot
1089
+ );
1042
1090
  let exitCode = 0;
1043
1091
  try {
1044
1092
  let storeIds = (await services.store.list()).map((s) => s.id);
@@ -1063,18 +1111,48 @@ function createSearchCommand(getOptions) {
1063
1111
  exitCode = 1;
1064
1112
  break searchLogic;
1065
1113
  }
1114
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1066
1115
  for (const storeId of storeIds) {
1067
1116
  await services.lance.initialize(storeId);
1068
1117
  }
1118
+ const limit = parseInt(options.limit, 10);
1119
+ if (Number.isNaN(limit)) {
1120
+ throw new Error(
1121
+ `Invalid value for --limit: "${options.limit}" is not a valid integer`
1122
+ );
1123
+ }
1124
+ let threshold;
1125
+ if (options.threshold !== void 0) {
1126
+ threshold = parseFloat(options.threshold);
1127
+ if (Number.isNaN(threshold)) {
1128
+ throw new Error(
1129
+ `Invalid value for --threshold: "${options.threshold}" is not a valid number`
1130
+ );
1131
+ }
1132
+ if (threshold < 0 || threshold > 1) {
1133
+ throw new Error(`Invalid value for --threshold: must be between 0 and 1`);
1134
+ }
1135
+ }
1136
+ let minRelevance;
1137
+ if (options.minRelevance !== void 0) {
1138
+ minRelevance = parseFloat(options.minRelevance);
1139
+ if (Number.isNaN(minRelevance)) {
1140
+ throw new Error(
1141
+ `Invalid value for --min-relevance: "${options.minRelevance}" is not a valid number`
1142
+ );
1143
+ }
1144
+ if (minRelevance < 0 || minRelevance > 1) {
1145
+ throw new Error(`Invalid value for --min-relevance: must be between 0 and 1`);
1146
+ }
1147
+ }
1069
1148
  const results = await services.search.search({
1070
1149
  query,
1071
1150
  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"
1151
+ mode: options.mode,
1152
+ limit,
1153
+ threshold,
1154
+ minRelevance,
1155
+ detail: options.detail
1078
1156
  });
1079
1157
  if (globalOpts.format === "json") {
1080
1158
  console.log(JSON.stringify(results, null, 2));
@@ -1086,7 +1164,7 @@ function createSearchCommand(getOptions) {
1086
1164
  } else {
1087
1165
  console.log(`
1088
1166
  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`;
1167
+ let statusLine = `Mode: ${results.mode} | Detail: ${options.detail} | Stores: ${String(results.stores.length)} | Results: ${String(results.totalResults)} | Time: ${String(results.timeMs)}ms`;
1090
1168
  if (results.confidence !== void 0) {
1091
1169
  statusLine += ` | Confidence: ${results.confidence}`;
1092
1170
  }
@@ -1168,6 +1246,8 @@ import { serve } from "@hono/node-server";
1168
1246
  import { Command as Command6 } from "commander";
1169
1247
 
1170
1248
  // src/server/app.ts
1249
+ import { rm } from "fs/promises";
1250
+ import { join as join2 } from "path";
1171
1251
  import { Hono } from "hono";
1172
1252
  import { cors } from "hono/cors";
1173
1253
  import { z } from "zod";
@@ -1229,6 +1309,13 @@ function createApp(services) {
1229
1309
  app.delete("/api/stores/:id", async (c) => {
1230
1310
  const store = await services.store.getByIdOrName(c.req.param("id"));
1231
1311
  if (!store) return c.json({ error: "Not found" }, 404);
1312
+ await services.lance.deleteStore(store.id);
1313
+ await services.codeGraph.deleteGraph(store.id);
1314
+ await services.manifest.delete(store.id);
1315
+ const resolvedDataDir = services.config.resolveDataDir();
1316
+ if (store.type === "repo" && "url" in store && store.url !== void 0 && store.path.startsWith(join2(resolvedDataDir, "repos"))) {
1317
+ await rm(store.path, { recursive: true, force: true });
1318
+ }
1232
1319
  const result = await services.store.delete(store.id);
1233
1320
  if (result.success) return c.json({ deleted: true });
1234
1321
  return c.json({ error: result.error.message }, 400);
@@ -1240,10 +1327,22 @@ function createApp(services) {
1240
1327
  return c.json({ error: parseResult.error.issues[0]?.message ?? "Invalid request body" }, 400);
1241
1328
  }
1242
1329
  const storeIds = (await services.store.list()).map((s) => s.id);
1330
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1243
1331
  for (const id of storeIds) {
1244
1332
  await services.lance.initialize(id);
1245
1333
  }
1246
- const requestedStores = parseResult.data.stores !== void 0 ? parseResult.data.stores.map((s) => createStoreId(s)) : storeIds;
1334
+ let requestedStores = storeIds;
1335
+ if (parseResult.data.stores !== void 0) {
1336
+ const resolvedStores = [];
1337
+ for (const requested of parseResult.data.stores) {
1338
+ const store = await services.store.getByIdOrName(requested);
1339
+ if (store === void 0) {
1340
+ return c.json({ error: `Store not found: ${requested}` }, 404);
1341
+ }
1342
+ resolvedStores.push(store.id);
1343
+ }
1344
+ requestedStores = resolvedStores;
1345
+ }
1247
1346
  const query = {
1248
1347
  query: parseResult.data.query,
1249
1348
  detail: parseResult.data.detail ?? "minimal",
@@ -1256,6 +1355,7 @@ function createApp(services) {
1256
1355
  app.post("/api/stores/:id/index", async (c) => {
1257
1356
  const store = await services.store.getByIdOrName(c.req.param("id"));
1258
1357
  if (!store) return c.json({ error: "Not found" }, 404);
1358
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1259
1359
  await services.lance.initialize(store.id);
1260
1360
  const result = await services.index.indexStore(store);
1261
1361
  if (result.success) return c.json(result.data);
@@ -1266,16 +1366,28 @@ function createApp(services) {
1266
1366
 
1267
1367
  // src/cli/commands/serve.ts
1268
1368
  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(
1369
+ 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
1370
  "--host <host>",
1271
- "Bind address (default: 127.0.0.1, use 0.0.0.0 for all interfaces)",
1272
- "127.0.0.1"
1371
+ "Bind address (reads from config if not specified, use 0.0.0.0 for all interfaces)"
1273
1372
  ).action(async (options) => {
1274
1373
  const globalOpts = getOptions();
1275
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1374
+ const services = await createServices(
1375
+ globalOpts.config,
1376
+ globalOpts.dataDir,
1377
+ globalOpts.projectRoot
1378
+ );
1379
+ const appConfig = await services.config.load();
1276
1380
  const app = createApp(services);
1277
- const port = parseInt(options.port ?? "3847", 10);
1278
- const host = options.host ?? "127.0.0.1";
1381
+ let port;
1382
+ if (options.port !== void 0) {
1383
+ port = parseInt(options.port, 10);
1384
+ if (Number.isNaN(port)) {
1385
+ throw new Error(`Invalid value for --port: "${options.port}" is not a valid integer`);
1386
+ }
1387
+ } else {
1388
+ port = appConfig.server.port;
1389
+ }
1390
+ const host = options.host ?? appConfig.server.host;
1279
1391
  console.log(`Starting server on http://${host}:${String(port)}`);
1280
1392
  const server = serve({
1281
1393
  fetch: app.fetch,
@@ -1300,7 +1412,7 @@ import { spawnSync } from "child_process";
1300
1412
  import { existsSync as existsSync2 } from "fs";
1301
1413
  import { mkdir } from "fs/promises";
1302
1414
  import { homedir } from "os";
1303
- import { join as join2 } from "path";
1415
+ import { join as join3 } from "path";
1304
1416
  import { Command as Command7 } from "commander";
1305
1417
  import ora4 from "ora";
1306
1418
 
@@ -1357,7 +1469,7 @@ var DEFAULT_REPOS = [
1357
1469
  ];
1358
1470
 
1359
1471
  // src/cli/commands/setup.ts
1360
- var DEFAULT_REPOS_DIR = join2(homedir(), ".bluera", "bluera-knowledge", "repos");
1472
+ var DEFAULT_REPOS_DIR = join3(homedir(), ".bluera", "bluera-knowledge", "repos");
1361
1473
  function createSetupCommand(getOptions) {
1362
1474
  const setup = new Command7("setup").description(
1363
1475
  "Quick-start with pre-configured Claude/Anthropic documentation repos"
@@ -1382,26 +1494,31 @@ function createSetupCommand(getOptions) {
1382
1494
  }
1383
1495
  return;
1384
1496
  }
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
- }
1497
+ let repos = DEFAULT_REPOS;
1498
+ if (options.only !== void 0 && options.only !== "") {
1499
+ const onlyNames = options.only.split(",").map((n) => n.trim().toLowerCase());
1500
+ repos = DEFAULT_REPOS.filter(
1501
+ (r) => onlyNames.some((n) => r.name.toLowerCase().includes(n))
1502
+ );
1503
+ if (repos.length === 0) {
1504
+ console.error(`No repos matched: ${options.only}`);
1505
+ console.log("Available repos:", DEFAULT_REPOS.map((r) => r.name).join(", "));
1506
+ process.exitCode = 1;
1507
+ return;
1398
1508
  }
1509
+ }
1510
+ const services = await createServices(
1511
+ globalOpts.config,
1512
+ globalOpts.dataDir,
1513
+ globalOpts.projectRoot
1514
+ );
1515
+ try {
1399
1516
  console.log(`
1400
1517
  Setting up ${String(repos.length)} repositories...
1401
1518
  `);
1402
1519
  await mkdir(options.reposDir, { recursive: true });
1403
1520
  for (const repo of repos) {
1404
- const repoPath = join2(options.reposDir, repo.name);
1521
+ const repoPath = join3(options.reposDir, repo.name);
1405
1522
  const spinner = ora4(`Processing ${repo.name}`).start();
1406
1523
  try {
1407
1524
  if (options.skipClone !== true) {
@@ -1450,6 +1567,7 @@ Setting up ${String(repos.length)} repositories...
1450
1567
  spinner.text = `${repo.name}: Indexing...`;
1451
1568
  const store = await services.store.getByIdOrName(storeId);
1452
1569
  if (store) {
1570
+ services.lance.setDimensions(await services.embeddings.ensureDimensions());
1453
1571
  await services.lance.initialize(store.id);
1454
1572
  const indexResult = await services.index.indexStore(store, (event) => {
1455
1573
  if (event.type === "progress") {
@@ -1458,7 +1576,7 @@ Setting up ${String(repos.length)} repositories...
1458
1576
  });
1459
1577
  if (indexResult.success) {
1460
1578
  spinner.succeed(
1461
- `${repo.name}: ${String(indexResult.data.documentsIndexed)} docs, ${String(indexResult.data.chunksCreated)} chunks`
1579
+ `${repo.name}: ${String(indexResult.data.filesIndexed)} docs, ${String(indexResult.data.chunksCreated)} chunks`
1462
1580
  );
1463
1581
  } else {
1464
1582
  throw new Error(
@@ -1485,8 +1603,8 @@ Setting up ${String(repos.length)} repositories...
1485
1603
  }
1486
1604
 
1487
1605
  // src/cli/commands/store.ts
1488
- import { rm } from "fs/promises";
1489
- import { join as join3 } from "path";
1606
+ import { rm as rm2 } from "fs/promises";
1607
+ import { join as join4 } from "path";
1490
1608
  import { Command as Command8 } from "commander";
1491
1609
  function createStoreCommand(getOptions) {
1492
1610
  const store = new Command8("store").description(
@@ -1494,7 +1612,11 @@ function createStoreCommand(getOptions) {
1494
1612
  );
1495
1613
  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
1614
  const globalOpts = getOptions();
1497
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1615
+ const services = await createServices(
1616
+ globalOpts.config,
1617
+ globalOpts.dataDir,
1618
+ globalOpts.projectRoot
1619
+ );
1498
1620
  try {
1499
1621
  const stores = await services.store.list(options.type);
1500
1622
  if (globalOpts.format === "json") {
@@ -1524,10 +1646,14 @@ function createStoreCommand(getOptions) {
1524
1646
  ).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
1647
  async (name, options) => {
1526
1648
  const globalOpts = getOptions();
1527
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1649
+ const services = await createServices(
1650
+ globalOpts.config,
1651
+ globalOpts.dataDir,
1652
+ globalOpts.projectRoot
1653
+ );
1528
1654
  let exitCode = 0;
1529
1655
  try {
1530
- const isUrl = options.source.startsWith("http://") || options.source.startsWith("https://");
1656
+ const isUrl = isGitUrl(options.source);
1531
1657
  const result = await services.store.create({
1532
1658
  name,
1533
1659
  type: options.type,
@@ -1559,7 +1685,11 @@ Created store: ${result.data.name} (${result.data.id})
1559
1685
  );
1560
1686
  store.command("info <store>").description("Show store details: ID, type, path/URL, timestamps").action(async (storeIdOrName) => {
1561
1687
  const globalOpts = getOptions();
1562
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1688
+ const services = await createServices(
1689
+ globalOpts.config,
1690
+ globalOpts.dataDir,
1691
+ globalOpts.projectRoot
1692
+ );
1563
1693
  let exitCode = 0;
1564
1694
  storeInfo: try {
1565
1695
  const s = await services.store.getByIdOrName(storeIdOrName);
@@ -1591,7 +1721,11 @@ Store: ${s.name}`);
1591
1721
  });
1592
1722
  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
1723
  const globalOpts = getOptions();
1594
- const services = await createServices(globalOpts.config, globalOpts.dataDir);
1724
+ const services = await createServices(
1725
+ globalOpts.config,
1726
+ globalOpts.dataDir,
1727
+ globalOpts.projectRoot
1728
+ );
1595
1729
  let exitCode = 0;
1596
1730
  storeDelete: try {
1597
1731
  const s = await services.store.getByIdOrName(storeIdOrName);
@@ -1625,10 +1759,11 @@ Store: ${s.name}`);
1625
1759
  }
1626
1760
  await services.lance.deleteStore(s.id);
1627
1761
  await services.codeGraph.deleteGraph(s.id);
1762
+ await services.manifest.delete(s.id);
1628
1763
  if (s.type === "repo" && "url" in s && s.url !== void 0) {
1629
1764
  const dataDir = services.config.resolveDataDir();
1630
- const repoPath = join3(dataDir, "repos", s.id);
1631
- await rm(repoPath, { recursive: true, force: true });
1765
+ const repoPath = join4(dataDir, "repos", s.id);
1766
+ await rm2(repoPath, { recursive: true, force: true });
1632
1767
  }
1633
1768
  const result = await services.store.delete(s.id);
1634
1769
  if (result.success) {
@@ -1648,6 +1783,8 @@ Store: ${s.name}`);
1648
1783
  }
1649
1784
 
1650
1785
  // src/cli/commands/sync.ts
1786
+ import { rm as rm3 } from "fs/promises";
1787
+ import { join as join5 } from "path";
1651
1788
  import { Command as Command9 } from "commander";
1652
1789
  async function createStoreFromDefinition(def, defService, storeService) {
1653
1790
  try {
@@ -1693,6 +1830,9 @@ async function createStoreFromDefinition(def, defService, storeService) {
1693
1830
  type: "web",
1694
1831
  url: def.url,
1695
1832
  depth: def.depth,
1833
+ maxPages: def.maxPages,
1834
+ crawlInstructions: def.crawlInstructions,
1835
+ extractInstructions: def.extractInstructions,
1696
1836
  description: def.description,
1697
1837
  tags: def.tags
1698
1838
  },
@@ -1717,7 +1857,7 @@ function createSyncCommand(getOptions) {
1717
1857
  );
1718
1858
  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
1859
  const globalOpts = getOptions();
1720
- const projectRoot = globalOpts.projectRoot ?? process.cwd();
1860
+ const projectRoot = globalOpts.projectRoot ?? ProjectRootService.resolve();
1721
1861
  const defService = new StoreDefinitionService(projectRoot);
1722
1862
  const services = await createServices(globalOpts.config, globalOpts.dataDir, projectRoot);
1723
1863
  try {
@@ -1765,6 +1905,14 @@ function createSyncCommand(getOptions) {
1765
1905
  for (const orphanName of result.orphans) {
1766
1906
  const store = await services.store.getByName(orphanName);
1767
1907
  if (store !== void 0) {
1908
+ await services.lance.deleteStore(store.id);
1909
+ await services.codeGraph.deleteGraph(store.id);
1910
+ await services.manifest.delete(store.id);
1911
+ if (store.type === "repo" && "url" in store && store.url !== void 0) {
1912
+ const dataDir = services.config.resolveDataDir();
1913
+ const repoPath = join5(dataDir, "repos", store.id);
1914
+ await rm3(repoPath, { recursive: true, force: true });
1915
+ }
1768
1916
  const deleteResult = await services.store.delete(store.id, {
1769
1917
  skipDefinitionSync: true
1770
1918
  });
@@ -1892,13 +2040,13 @@ function printHumanReadable(result, quiet) {
1892
2040
 
1893
2041
  // src/cli/program.ts
1894
2042
  import { readFileSync } from "fs";
1895
- import { dirname, join as join4 } from "path";
2043
+ import { dirname, join as join6 } from "path";
1896
2044
  import { fileURLToPath } from "url";
1897
2045
  import { Command as Command10 } from "commander";
1898
2046
  function getVersion() {
1899
2047
  const __filename2 = fileURLToPath(import.meta.url);
1900
2048
  const __dirname2 = dirname(__filename2);
1901
- const content = readFileSync(join4(__dirname2, "../package.json"), "utf-8");
2049
+ const content = readFileSync(join6(__dirname2, "../package.json"), "utf-8");
1902
2050
  const pkg = JSON.parse(content);
1903
2051
  return pkg.version;
1904
2052
  }
@@ -1906,7 +2054,7 @@ var version = getVersion();
1906
2054
  function createProgram() {
1907
2055
  const program2 = new Command10();
1908
2056
  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");
2057
+ 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
2058
  return program2;
1911
2059
  }
1912
2060
  function getGlobalOptions(program2) {
@@ -1916,17 +2064,16 @@ function getGlobalOptions(program2) {
1916
2064
  dataDir: opts.dataDir,
1917
2065
  projectRoot: opts.projectRoot,
1918
2066
  format: opts.format,
1919
- quiet: opts.quiet,
1920
- verbose: opts.verbose
2067
+ quiet: opts.quiet
1921
2068
  };
1922
2069
  }
1923
2070
 
1924
2071
  // src/index.ts
1925
2072
  var registry = AdapterRegistry.getInstance();
1926
2073
  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");
2074
+ var DEFAULT_DATA_DIR = ".bluera/bluera-knowledge/data";
2075
+ var DEFAULT_CONFIG = ".bluera/bluera-knowledge/config.json";
2076
+ var DEFAULT_REPOS_DIR2 = ".bluera/bluera-knowledge/data/repos/<store-id>/";
1930
2077
  function formatCommandHelp(cmd, indent = "") {
1931
2078
  const lines = [];
1932
2079
  const name = cmd.name();