bluera-knowledge 0.14.8 → 0.15.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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "bluera-knowledge",
3
- "version": "0.14.8",
3
+ "version": "0.15.0",
4
4
  "description": "Clone repos, crawl docs, search locally. Fast, authoritative answers for AI coding agents."
5
5
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [0.15.0](https://github.com/blueraai/bluera-knowledge/compare/v0.14.8...v0.15.0) (2026-01-16)
6
+
7
+
8
+ ### Features
9
+
10
+ * **services:** wire up StoreDefinitionService and GitignoreService ([4d52052](https://github.com/blueraai/bluera-knowledge/commit/4d520523f52b1424f3bf523ad3596227add8fdb9))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **gitignore:** correct patterns to actually track stores.config.json ([7e0dd34](https://github.com/blueraai/bluera-knowledge/commit/7e0dd342610b45f59237ede36c14b5ed124943cd))
16
+ * **mcp:** use default value syntax for dual-mode MCP support ([4f62f5c](https://github.com/blueraai/bluera-knowledge/commit/4f62f5cfe67b24758ba2b198730a674f4b3055d7))
17
+
5
18
  ## [0.14.8](https://github.com/blueraai/bluera-knowledge/compare/v0.14.7...v0.14.8) (2026-01-16)
6
19
 
7
20
 
@@ -1,13 +1,16 @@
1
1
  import {
2
2
  AdapterRegistry,
3
3
  JobService,
4
- ProjectRootService,
4
+ StoreDefinitionService,
5
+ createLazyServices,
5
6
  createLogger,
6
- createServices,
7
7
  createStoreId,
8
8
  destroyServices,
9
+ isFileStoreDefinition,
10
+ isRepoStoreDefinition,
11
+ isWebStoreDefinition,
9
12
  summarizePayload
10
- } from "./chunk-5VW5DNW4.js";
13
+ } from "./chunk-WYZQUKUD.js";
11
14
 
12
15
  // src/mcp/server.ts
13
16
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -1239,213 +1242,7 @@ var storeCommands = [
1239
1242
  ];
1240
1243
 
1241
1244
  // src/mcp/commands/sync.commands.ts
1242
- import { z as z7 } from "zod";
1243
-
1244
- // src/services/store-definition.service.ts
1245
- import { readFile, writeFile, mkdir, access } from "fs/promises";
1246
- import { dirname, resolve, isAbsolute, join as join2 } from "path";
1247
-
1248
- // src/types/store-definition.ts
1249
1245
  import { z as z6 } from "zod";
1250
- var BaseStoreDefinitionSchema = z6.object({
1251
- name: z6.string().min(1, "Store name is required"),
1252
- description: z6.string().optional(),
1253
- tags: z6.array(z6.string()).optional()
1254
- });
1255
- var FileStoreDefinitionSchema = BaseStoreDefinitionSchema.extend({
1256
- type: z6.literal("file"),
1257
- path: z6.string().min(1, "Path is required for file stores")
1258
- });
1259
- var RepoStoreDefinitionSchema = BaseStoreDefinitionSchema.extend({
1260
- type: z6.literal("repo"),
1261
- url: z6.url("Valid URL is required for repo stores"),
1262
- branch: z6.string().optional(),
1263
- depth: z6.number().int().positive("Depth must be a positive integer").optional()
1264
- });
1265
- var WebStoreDefinitionSchema = BaseStoreDefinitionSchema.extend({
1266
- type: z6.literal("web"),
1267
- url: z6.url("Valid URL is required for web stores"),
1268
- depth: z6.number().int().min(0, "Depth must be non-negative").default(1),
1269
- maxPages: z6.number().int().positive("maxPages must be a positive integer").optional(),
1270
- crawlInstructions: z6.string().optional(),
1271
- extractInstructions: z6.string().optional()
1272
- });
1273
- var StoreDefinitionSchema = z6.discriminatedUnion("type", [
1274
- FileStoreDefinitionSchema,
1275
- RepoStoreDefinitionSchema,
1276
- WebStoreDefinitionSchema
1277
- ]);
1278
- var StoreDefinitionsConfigSchema = z6.object({
1279
- version: z6.literal(1),
1280
- stores: z6.array(StoreDefinitionSchema)
1281
- });
1282
- function isFileStoreDefinition(def) {
1283
- return def.type === "file";
1284
- }
1285
- function isRepoStoreDefinition(def) {
1286
- return def.type === "repo";
1287
- }
1288
- function isWebStoreDefinition(def) {
1289
- return def.type === "web";
1290
- }
1291
- var DEFAULT_STORE_DEFINITIONS_CONFIG = {
1292
- version: 1,
1293
- stores: []
1294
- };
1295
-
1296
- // src/services/store-definition.service.ts
1297
- async function fileExists(path2) {
1298
- try {
1299
- await access(path2);
1300
- return true;
1301
- } catch {
1302
- return false;
1303
- }
1304
- }
1305
- var StoreDefinitionService = class {
1306
- configPath;
1307
- projectRoot;
1308
- config = null;
1309
- constructor(projectRoot) {
1310
- this.projectRoot = projectRoot ?? ProjectRootService.resolve();
1311
- this.configPath = join2(this.projectRoot, ".bluera/bluera-knowledge/stores.config.json");
1312
- }
1313
- /**
1314
- * Load store definitions from config file.
1315
- * Returns empty config if file doesn't exist.
1316
- * Throws on parse/validation errors (fail fast per CLAUDE.md).
1317
- */
1318
- async load() {
1319
- if (this.config !== null) {
1320
- return this.config;
1321
- }
1322
- const exists = await fileExists(this.configPath);
1323
- if (!exists) {
1324
- this.config = {
1325
- ...DEFAULT_STORE_DEFINITIONS_CONFIG,
1326
- stores: [...DEFAULT_STORE_DEFINITIONS_CONFIG.stores]
1327
- };
1328
- return this.config;
1329
- }
1330
- const content = await readFile(this.configPath, "utf-8");
1331
- let parsed;
1332
- try {
1333
- parsed = JSON.parse(content);
1334
- } catch (error) {
1335
- throw new Error(
1336
- `Failed to parse store definitions at ${this.configPath}: ${error instanceof Error ? error.message : String(error)}`
1337
- );
1338
- }
1339
- const result = StoreDefinitionsConfigSchema.safeParse(parsed);
1340
- if (!result.success) {
1341
- throw new Error(`Invalid store definitions at ${this.configPath}: ${result.error.message}`);
1342
- }
1343
- this.config = result.data;
1344
- return this.config;
1345
- }
1346
- /**
1347
- * Save store definitions to config file.
1348
- */
1349
- async save(config) {
1350
- await mkdir(dirname(this.configPath), { recursive: true });
1351
- await writeFile(this.configPath, JSON.stringify(config, null, 2));
1352
- this.config = config;
1353
- }
1354
- /**
1355
- * Add a store definition.
1356
- * Throws if a definition with the same name already exists.
1357
- */
1358
- async addDefinition(definition) {
1359
- const config = await this.load();
1360
- const existing = config.stores.find((s) => s.name === definition.name);
1361
- if (existing !== void 0) {
1362
- throw new Error(`Store definition "${definition.name}" already exists`);
1363
- }
1364
- config.stores.push(definition);
1365
- await this.save(config);
1366
- }
1367
- /**
1368
- * Remove a store definition by name.
1369
- * Returns true if removed, false if not found.
1370
- */
1371
- async removeDefinition(name) {
1372
- const config = await this.load();
1373
- const index = config.stores.findIndex((s) => s.name === name);
1374
- if (index === -1) {
1375
- return false;
1376
- }
1377
- config.stores.splice(index, 1);
1378
- await this.save(config);
1379
- return true;
1380
- }
1381
- /**
1382
- * Update an existing store definition.
1383
- * Only updates the provided fields, preserving others.
1384
- * Throws if definition not found.
1385
- */
1386
- async updateDefinition(name, updates) {
1387
- const config = await this.load();
1388
- const index = config.stores.findIndex((s) => s.name === name);
1389
- if (index === -1) {
1390
- throw new Error(`Store definition "${name}" not found`);
1391
- }
1392
- const existing = config.stores[index];
1393
- if (existing === void 0) {
1394
- throw new Error(`Store definition "${name}" not found at index ${String(index)}`);
1395
- }
1396
- if (updates.description !== void 0) {
1397
- existing.description = updates.description;
1398
- }
1399
- if (updates.tags !== void 0) {
1400
- existing.tags = updates.tags;
1401
- }
1402
- await this.save(config);
1403
- }
1404
- /**
1405
- * Get a store definition by name.
1406
- * Returns undefined if not found.
1407
- */
1408
- async getByName(name) {
1409
- const config = await this.load();
1410
- return config.stores.find((s) => s.name === name);
1411
- }
1412
- /**
1413
- * Check if any definitions exist.
1414
- */
1415
- async hasDefinitions() {
1416
- const config = await this.load();
1417
- return config.stores.length > 0;
1418
- }
1419
- /**
1420
- * Resolve a file store path relative to project root.
1421
- */
1422
- resolvePath(path2) {
1423
- if (isAbsolute(path2)) {
1424
- return path2;
1425
- }
1426
- return resolve(this.projectRoot, path2);
1427
- }
1428
- /**
1429
- * Get the config file path.
1430
- */
1431
- getConfigPath() {
1432
- return this.configPath;
1433
- }
1434
- /**
1435
- * Get the project root.
1436
- */
1437
- getProjectRoot() {
1438
- return this.projectRoot;
1439
- }
1440
- /**
1441
- * Clear the cached config (useful for testing).
1442
- */
1443
- clearCache() {
1444
- this.config = null;
1445
- }
1446
- };
1447
-
1448
- // src/mcp/commands/sync.commands.ts
1449
1246
  async function handleStoresSync(args, context) {
1450
1247
  const { services, options } = context;
1451
1248
  const projectRoot = options.projectRoot;
@@ -1605,10 +1402,10 @@ var syncCommands = [
1605
1402
  {
1606
1403
  name: "stores:sync",
1607
1404
  description: "Sync stores from definitions config (bootstrap on fresh clone)",
1608
- argsSchema: z7.object({
1609
- prune: z7.boolean().optional().describe("Remove stores not in definitions"),
1610
- dryRun: z7.boolean().optional().describe("Show what would happen without making changes"),
1611
- reindex: z7.boolean().optional().describe("Re-index existing stores after sync")
1405
+ argsSchema: z6.object({
1406
+ prune: z6.boolean().optional().describe("Remove stores not in definitions"),
1407
+ dryRun: z6.boolean().optional().describe("Show what would happen without making changes"),
1408
+ reindex: z6.boolean().optional().describe("Re-index existing stores after sync")
1612
1409
  }),
1613
1410
  handler: (args, context) => {
1614
1411
  const syncArgs = {};
@@ -1627,13 +1424,13 @@ var syncCommands = [
1627
1424
  ];
1628
1425
 
1629
1426
  // src/mcp/commands/uninstall.commands.ts
1630
- import { z as z8 } from "zod";
1427
+ import { z as z7 } from "zod";
1631
1428
 
1632
1429
  // src/mcp/handlers/uninstall.handler.ts
1633
1430
  import { existsSync } from "fs";
1634
1431
  import { readdir, rm as rm2 } from "fs/promises";
1635
1432
  import { homedir } from "os";
1636
- import { join as join3 } from "path";
1433
+ import { join as join2 } from "path";
1637
1434
  var logger2 = createLogger("uninstall-handler");
1638
1435
  var handleUninstall = async (args, context) => {
1639
1436
  const { global: includeGlobal = false, keepDefinitions = true } = args;
@@ -1641,14 +1438,14 @@ var handleUninstall = async (args, context) => {
1641
1438
  const kept = [];
1642
1439
  const errors = [];
1643
1440
  const projectRoot = context.options.projectRoot ?? process.cwd();
1644
- const projectDataDir = join3(projectRoot, ".bluera", "bluera-knowledge");
1441
+ const projectDataDir = join2(projectRoot, ".bluera", "bluera-knowledge");
1645
1442
  logger2.info({ projectDataDir, includeGlobal, keepDefinitions }, "Starting uninstall");
1646
1443
  if (existsSync(projectDataDir)) {
1647
1444
  if (keepDefinitions) {
1648
1445
  try {
1649
1446
  const entries = await readdir(projectDataDir, { withFileTypes: true });
1650
1447
  for (const entry of entries) {
1651
- const entryPath = join3(projectDataDir, entry.name);
1448
+ const entryPath = join2(projectDataDir, entry.name);
1652
1449
  if (entry.name === "stores.config.json") {
1653
1450
  kept.push(entryPath);
1654
1451
  continue;
@@ -1679,7 +1476,7 @@ var handleUninstall = async (args, context) => {
1679
1476
  }
1680
1477
  }
1681
1478
  if (includeGlobal) {
1682
- const globalDir = join3(homedir(), ".local", "share", "bluera-knowledge");
1479
+ const globalDir = join2(homedir(), ".local", "share", "bluera-knowledge");
1683
1480
  if (existsSync(globalDir)) {
1684
1481
  try {
1685
1482
  await rm2(globalDir, { recursive: true, force: true });
@@ -1736,9 +1533,9 @@ var uninstallCommands = [
1736
1533
  {
1737
1534
  name: "uninstall",
1738
1535
  description: "Remove Bluera Knowledge data from project (and optionally global data)",
1739
- argsSchema: z8.object({
1740
- global: z8.boolean().optional().describe("Also remove global data (~/.local/share/bluera-knowledge)"),
1741
- keepDefinitions: z8.boolean().optional().describe("Keep stores.config.json for team sharing (default: true)")
1536
+ argsSchema: z7.object({
1537
+ global: z7.boolean().optional().describe("Also remove global data (~/.local/share/bluera-knowledge)"),
1538
+ keepDefinitions: z7.boolean().optional().describe("Keep stores.config.json for team sharing (default: true)")
1742
1539
  }),
1743
1540
  handler: (args, context) => handleUninstall(args, context)
1744
1541
  }
@@ -2080,7 +1877,7 @@ var registry = AdapterRegistry.getInstance();
2080
1877
  if (!registry.hasExtension(".zil")) {
2081
1878
  registry.register(new ZilAdapter());
2082
1879
  }
2083
- function createMCPServer(options) {
1880
+ function createMCPServer(options, services) {
2084
1881
  const server = new Server(
2085
1882
  {
2086
1883
  name: "bluera-knowledge",
@@ -2192,7 +1989,6 @@ function createMCPServer(options) {
2192
1989
  const { name, arguments: args } = request.params;
2193
1990
  const startTime = Date.now();
2194
1991
  logger4.info({ tool: name, args: JSON.stringify(args) }, "Tool invoked");
2195
- const services = await createServices(options.config, options.dataDir, options.projectRoot);
2196
1992
  const context = { services, options };
2197
1993
  try {
2198
1994
  let result;
@@ -2221,8 +2017,6 @@ function createMCPServer(options) {
2221
2017
  "Tool execution failed"
2222
2018
  );
2223
2019
  throw error;
2224
- } finally {
2225
- await destroyServices(services);
2226
2020
  }
2227
2021
  });
2228
2022
  return server;
@@ -2235,8 +2029,23 @@ async function runMCPServer(options) {
2235
2029
  },
2236
2030
  "MCP server starting"
2237
2031
  );
2238
- const server = createMCPServer(options);
2032
+ const services = await createLazyServices(options.config, options.dataDir, options.projectRoot);
2033
+ const server = createMCPServer(options, services);
2239
2034
  const transport = new StdioServerTransport();
2035
+ const shutdown = async (signal) => {
2036
+ logger4.info({ signal }, "Shutdown signal received");
2037
+ try {
2038
+ await destroyServices(services);
2039
+ logger4.info("Services destroyed, exiting");
2040
+ } catch (error) {
2041
+ logger4.error(
2042
+ { error: error instanceof Error ? error.message : String(error) },
2043
+ "Error during shutdown"
2044
+ );
2045
+ }
2046
+ };
2047
+ process.on("SIGINT", () => void shutdown("SIGINT"));
2048
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
2240
2049
  await server.connect(transport);
2241
2050
  logger4.info("MCP server connected to stdio transport");
2242
2051
  }
@@ -2263,11 +2072,7 @@ if (isMCPServerEntry) {
2263
2072
  export {
2264
2073
  ZilAdapter,
2265
2074
  spawnBackgroundWorker,
2266
- isFileStoreDefinition,
2267
- isRepoStoreDefinition,
2268
- isWebStoreDefinition,
2269
- StoreDefinitionService,
2270
2075
  createMCPServer,
2271
2076
  runMCPServer
2272
2077
  };
2273
- //# sourceMappingURL=chunk-SWHYQLSJ.js.map
2078
+ //# sourceMappingURL=chunk-DX5I6U5X.js.map