@dikolab/kbdb 0.2.2 → 0.3.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/README.md +50 -15
- package/dist/cli.cjs +461 -72
- package/dist/cli.cjs.map +4 -4
- package/dist/cli.mjs +453 -64
- package/dist/cli.mjs.map +4 -4
- package/dist/kbdb-worker.cjs +395 -60
- package/dist/kbdb-worker.cjs.map +4 -4
- package/dist/mod.cjs +17 -0
- package/dist/mod.cjs.map +2 -2
- package/dist/mod.mjs +17 -0
- package/dist/mod.mjs.map +2 -2
- package/dist/src/shared/cli/functions/dispatch-command.function.d.ts +3 -1
- package/dist/src/shared/cli/functions/install-default-skills.function.d.ts +12 -0
- package/dist/src/shared/cli/functions/run-check.function.d.ts +47 -5
- package/dist/src/shared/cli/functions/run-export.function.d.ts +34 -0
- package/dist/src/shared/cli/functions/run-gc.function.d.ts +6 -0
- package/dist/src/shared/cli/functions/run-learn.function.d.ts +35 -6
- package/dist/src/shared/cli/functions/run-recall.function.d.ts +45 -0
- package/dist/src/shared/cli/functions/run-search.function.d.ts +52 -15
- package/dist/src/shared/cli/index.d.ts +4 -0
- package/dist/src/shared/cli/typings/cli-options.interface.d.ts +13 -0
- package/dist/src/shared/cli/typings/learn-result.model.d.ts +19 -0
- package/dist/src/shared/cli/typings/search-display.model.d.ts +18 -0
- package/dist/src/shared/mcp/classes/mcp-server.class.d.ts +8 -0
- package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +43 -0
- package/dist/src/shared/wasm-codec/functions/decode-add-section-result.function.d.ts +48 -0
- package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +58 -0
- package/dist/src/shared/wasm-codec/functions/decode-section-records.function.d.ts +33 -0
- package/dist/src/shared/wasm-codec/functions/encode-add-section-params.function.d.ts +52 -0
- package/dist/src/shared/wasm-codec/index.d.ts +2 -0
- package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +16 -0
- package/dist/src/shared/worker-client/index.d.ts +1 -1
- package/dist/src/shared/worker-client/typings/add-section-result.model.d.ts +29 -15
- package/dist/src/shared/worker-client/typings/export-result.model.d.ts +14 -0
- package/dist/src/shared/worker-client/typings/gc-result.model.d.ts +6 -0
- package/dist/src/shared/worker-client/typings/search-result.model.d.ts +12 -0
- package/dist/src/shared/worker-client/typings/section.model.d.ts +16 -0
- package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +35 -0
- package/dist/wasm/fs-database/kbdb_fs_database.d.ts +22 -1
- package/dist/wasm/fs-database/kbdb_fs_database.js +28 -1
- package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
- package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +1 -0
- package/dist/wasm/kb-worker/kbdb_worker.d.ts +48 -1
- package/dist/wasm/kb-worker/kbdb_worker.js +60 -1
- package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
- package/dist/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +2 -0
- package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
- package/dist/worker.mjs +396 -60
- package/dist/worker.mjs.map +4 -4
- package/docs/details/cli.md +102 -15
- package/docs/details/library-api.md +55 -6
- package/docs/details/mcp-server.md +8 -4
- package/docs/details/search-and-ranking.md +63 -5
- package/docs/details/storage.md +2 -0
- package/docs/goals/cli.md +255 -14
- package/docs/goals/database.md +244 -17
- package/docs/goals/document.md +1 -1
- package/docs/goals/mcp.md +83 -14
- package/docs/goals/query-parser.md +98 -4
- package/docs/goals/query-result.md +177 -10
- package/docs/goals/skills.md +72 -0
- package/docs/goals/worker-daemon.md +54 -7
- package/docs/modules/rust/fs-database/functions/integrity.md +11 -0
- package/docs/modules/rust/fs-database/functions/section.md +23 -6
- package/docs/modules/rust/fs-database/module.md +1 -0
- package/docs/modules/rust/fs-database/types/section.md +27 -1
- package/docs/modules/rust/overview.md +2 -1
- package/docs/modules/rust/query-parser/functions/module.md +2 -2
- package/docs/modules/rust/query-parser/functions/pipeline.md +44 -6
- package/docs/modules/rust/query-parser/module.md +5 -4
- package/docs/modules/rust/query-parser/types/keyphrase.md +11 -0
- package/docs/modules/rust/query-parser/types/module.md +3 -3
- package/docs/modules/typescript/shared/cli/functions.md +68 -3
- package/package.json +1 -1
- package/src/shared/cli/functions/dispatch-command.function.ts +53 -1
- package/src/shared/cli/functions/format-output.function.ts +36 -12
- package/src/shared/cli/functions/generate-help.function.ts +56 -1
- package/src/shared/cli/functions/install-default-skills.function.ts +296 -0
- package/src/shared/cli/functions/parse-args.function.ts +13 -0
- package/src/shared/cli/functions/run-check.function.ts +87 -6
- package/src/shared/cli/functions/run-export.function.ts +40 -0
- package/src/shared/cli/functions/run-gc.function.ts +6 -0
- package/src/shared/cli/functions/run-init.function.ts +2 -0
- package/src/shared/cli/functions/run-learn.function.ts +68 -16
- package/src/shared/cli/functions/run-recall.function.ts +66 -0
- package/src/shared/cli/functions/run-search.function.ts +58 -15
- package/src/shared/cli/index.ts +4 -0
- package/src/shared/cli/typings/cli-options.interface.ts +13 -0
- package/src/shared/cli/typings/learn-result.model.ts +16 -0
- package/src/shared/cli/typings/search-display.model.ts +18 -0
- package/src/shared/mcp/classes/mcp-server.class.ts +30 -1
- package/src/shared/mcp/functions/create-tool-definitions.function.ts +46 -5
- package/src/shared/mcp/functions/handle-tool-call.function.ts +70 -2
- package/src/shared/mcp/typings/mcp-client.interface.ts +45 -0
- package/src/shared/wasm-codec/functions/decode-add-section-result.function.ts +112 -0
- package/src/shared/wasm-codec/functions/decode-scored-results.function.ts +86 -0
- package/src/shared/wasm-codec/functions/decode-section-records.function.ts +87 -0
- package/src/shared/wasm-codec/functions/encode-add-section-params.function.ts +68 -0
- package/src/shared/wasm-codec/index.ts +5 -0
- package/src/shared/worker-client/classes/worker-client.class.ts +19 -0
- package/src/shared/worker-client/index.ts +4 -1
- package/src/shared/worker-client/typings/add-section-result.model.ts +30 -15
- package/src/shared/worker-client/typings/export-result.model.ts +14 -0
- package/src/shared/worker-client/typings/gc-result.model.ts +6 -0
- package/src/shared/worker-client/typings/search-result.model.ts +12 -0
- package/src/shared/worker-client/typings/section.model.ts +16 -0
- package/src/shared/worker-daemon/classes/worker-daemon.class.ts +315 -46
- package/src/shared/worker-daemon/functions/create-host-imports.function.ts +19 -4
- package/src/shared/worker-daemon/functions/create-ipc-server.function.ts +5 -1
- package/src/wasm/fs-database/kbdb_fs_database.d.ts +22 -1
- package/src/wasm/fs-database/kbdb_fs_database.js +28 -1
- package/src/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
- package/src/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +1 -0
- package/src/wasm/kb-worker/kbdb_worker.d.ts +48 -1
- package/src/wasm/kb-worker/kbdb_worker.js +60 -1
- package/src/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
- package/src/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +2 -0
- package/src/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -146,6 +146,19 @@ function parseArgs(argv) {
|
|
|
146
146
|
case "--skills":
|
|
147
147
|
options.skills = (argv[++i] ?? "").split(",");
|
|
148
148
|
break;
|
|
149
|
+
case "--tags":
|
|
150
|
+
options.tags = (argv[++i] ?? "").split(",");
|
|
151
|
+
break;
|
|
152
|
+
case "--replace":
|
|
153
|
+
options.replace = true;
|
|
154
|
+
break;
|
|
155
|
+
case "--depth":
|
|
156
|
+
case "-d":
|
|
157
|
+
options.depth = Math.min(
|
|
158
|
+
3,
|
|
159
|
+
Math.max(0, parseInt(argv[++i] ?? "", 10) || 0)
|
|
160
|
+
);
|
|
161
|
+
break;
|
|
149
162
|
case "--content":
|
|
150
163
|
case "-c":
|
|
151
164
|
options.content = true;
|
|
@@ -183,9 +196,11 @@ function formatText(paged) {
|
|
|
183
196
|
const num = paged.offset + i + 1;
|
|
184
197
|
const heading = r.heading ?? "(untitled)";
|
|
185
198
|
const terms = r.matched_terms.join(", ");
|
|
186
|
-
|
|
199
|
+
const fields = r.matched_fields.join(", ");
|
|
200
|
+
const confidencePct = (r.confidence * 100).toFixed(0);
|
|
201
|
+
return `${String(num)}. [${r.kbid}] ${heading} (score: ${r.score.toFixed(4)}, confidence: ${confidencePct}%)
|
|
187
202
|
${r.snippet || r.preview || "(no snippet)"}
|
|
188
|
-
type: ${r.type} terms: ${terms}
|
|
203
|
+
type: ${r.type} terms: ${terms}` + (fields ? ` fields: ${fields}` : "");
|
|
189
204
|
}).join("\n\n");
|
|
190
205
|
const footer = `
|
|
191
206
|
Showing ${String(start)}-${String(end)} of ${String(paged.total)} results`;
|
|
@@ -194,12 +209,32 @@ Use --offset ${String(end)} to see more` : "";
|
|
|
194
209
|
return lines + footer + hint;
|
|
195
210
|
}
|
|
196
211
|
function formatMcp(paged) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
212
|
+
const mcpItems = paged.items.map((r) => ({
|
|
213
|
+
kbid: r.kbid,
|
|
214
|
+
heading: r.heading,
|
|
215
|
+
type: r.type,
|
|
216
|
+
docids: r.docids,
|
|
217
|
+
snippet: r.snippet,
|
|
218
|
+
matched_terms: r.matched_terms,
|
|
219
|
+
matched_fields: r.matched_fields,
|
|
220
|
+
created_at: r.created_at
|
|
221
|
+
}));
|
|
222
|
+
return JSON.stringify(
|
|
223
|
+
{
|
|
224
|
+
type: "text",
|
|
225
|
+
text: paged.items.map(
|
|
226
|
+
(r) => `[${r.kbid}] ${r.heading ?? "(untitled)"} \u2014 ${r.snippet || r.preview}`
|
|
227
|
+
).join("\n"),
|
|
228
|
+
items: mcpItems,
|
|
229
|
+
total: paged.total,
|
|
230
|
+
offset: paged.offset,
|
|
231
|
+
limit: paged.limit,
|
|
232
|
+
has_more: paged.has_more,
|
|
233
|
+
relaxed: paged.relaxed
|
|
234
|
+
},
|
|
235
|
+
null,
|
|
236
|
+
2
|
|
237
|
+
);
|
|
203
238
|
}
|
|
204
239
|
|
|
205
240
|
// src/shared/cli/functions/run-search.function.ts
|
|
@@ -211,7 +246,8 @@ async function runSearch(client, options) {
|
|
|
211
246
|
total: 0,
|
|
212
247
|
offset: 0,
|
|
213
248
|
limit: options.limit,
|
|
214
|
-
has_more: false
|
|
249
|
+
has_more: false,
|
|
250
|
+
relaxed: false
|
|
215
251
|
};
|
|
216
252
|
}
|
|
217
253
|
const results = await client.search({
|
|
@@ -227,17 +263,43 @@ async function runSearch(client, options) {
|
|
|
227
263
|
type: r.type,
|
|
228
264
|
docids: r.docids,
|
|
229
265
|
score: r.score,
|
|
266
|
+
confidence: r.confidence ?? 0,
|
|
230
267
|
snippet: r.snippet,
|
|
231
268
|
matched_terms: r.matched_terms,
|
|
269
|
+
matched_fields: r.matched_fields ?? [],
|
|
270
|
+
created_at: r.created_at ?? "",
|
|
232
271
|
preview: ""
|
|
233
272
|
})),
|
|
234
273
|
total: results.total,
|
|
235
274
|
offset: results.offset,
|
|
236
275
|
limit: results.limit,
|
|
237
|
-
has_more: results.has_more
|
|
276
|
+
has_more: results.has_more,
|
|
277
|
+
relaxed: results.relaxed ?? false
|
|
238
278
|
};
|
|
239
279
|
}
|
|
240
280
|
|
|
281
|
+
// src/shared/cli/functions/run-recall.function.ts
|
|
282
|
+
var MAX_RECALL_DEPTH = 3;
|
|
283
|
+
async function runRecall(client, options) {
|
|
284
|
+
if (options.args.length === 0) {
|
|
285
|
+
return { error: "no kbid provided" };
|
|
286
|
+
}
|
|
287
|
+
const depth = Math.min(
|
|
288
|
+
MAX_RECALL_DEPTH,
|
|
289
|
+
Math.max(0, options.depth ?? 0)
|
|
290
|
+
);
|
|
291
|
+
if (options.args.length === 1) {
|
|
292
|
+
return client.recall({ kbid: options.args[0], depth });
|
|
293
|
+
}
|
|
294
|
+
return client.recall({ kbids: options.args, depth });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/shared/cli/functions/run-export.function.ts
|
|
298
|
+
async function runExport(client, options) {
|
|
299
|
+
const path = options.args[0];
|
|
300
|
+
return client.export({ path });
|
|
301
|
+
}
|
|
302
|
+
|
|
241
303
|
// src/shared/cli/functions/run-learn.function.ts
|
|
242
304
|
var import_node_fs = require("node:fs");
|
|
243
305
|
var import_node_path3 = require("node:path");
|
|
@@ -255,14 +317,11 @@ async function runLearn(client, options, io) {
|
|
|
255
317
|
content,
|
|
256
318
|
title: options.title,
|
|
257
319
|
description: options.description,
|
|
258
|
-
docid: options.docid
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
kbid: result.kbid,
|
|
262
|
-
path: "-",
|
|
263
|
-
size: content.length,
|
|
264
|
-
skipped: false
|
|
320
|
+
docid: options.docid,
|
|
321
|
+
tags: options.tags,
|
|
322
|
+
replace: options.replace
|
|
265
323
|
});
|
|
324
|
+
results.push(mapAddSectionResult(result, "-", content.length));
|
|
266
325
|
continue;
|
|
267
326
|
}
|
|
268
327
|
const files = collectFiles(target);
|
|
@@ -272,18 +331,30 @@ async function runLearn(client, options, io) {
|
|
|
272
331
|
sectionType: "text",
|
|
273
332
|
content,
|
|
274
333
|
title: options.title,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
size: content.length,
|
|
281
|
-
skipped: false
|
|
334
|
+
description: options.description,
|
|
335
|
+
docid: options.docid,
|
|
336
|
+
tags: options.tags,
|
|
337
|
+
replace: options.replace,
|
|
338
|
+
sourcePath: file
|
|
282
339
|
});
|
|
340
|
+
results.push(
|
|
341
|
+
mapAddSectionResult(result, file, content.length)
|
|
342
|
+
);
|
|
283
343
|
}
|
|
284
344
|
}
|
|
285
345
|
return results;
|
|
286
346
|
}
|
|
347
|
+
function mapAddSectionResult(result, path, size) {
|
|
348
|
+
return {
|
|
349
|
+
kbid: result.kbid,
|
|
350
|
+
path,
|
|
351
|
+
size,
|
|
352
|
+
skipped: false,
|
|
353
|
+
superseded: result.superseded ?? null,
|
|
354
|
+
warnings: result.warnings ?? [],
|
|
355
|
+
near_duplicates: result.near_duplicates ?? []
|
|
356
|
+
};
|
|
357
|
+
}
|
|
287
358
|
function readProcessStdin() {
|
|
288
359
|
return new Promise((resolve4) => {
|
|
289
360
|
const chunks = [];
|
|
@@ -348,8 +419,26 @@ async function runContent(client, options) {
|
|
|
348
419
|
}
|
|
349
420
|
|
|
350
421
|
// src/shared/cli/functions/run-check.function.ts
|
|
422
|
+
function parseDivergentContent(encoded) {
|
|
423
|
+
if (!encoded) return [];
|
|
424
|
+
return encoded.split(";").filter((segment) => segment.includes(":")).map((segment) => {
|
|
425
|
+
const colonIdx = segment.indexOf(":");
|
|
426
|
+
const sourceKey = segment.slice(0, colonIdx);
|
|
427
|
+
const kbids = segment.slice(colonIdx + 1).split(",").filter((k) => k.length > 0);
|
|
428
|
+
return { sourceKey, kbids };
|
|
429
|
+
}).filter((entry) => entry.kbids.length > 0);
|
|
430
|
+
}
|
|
351
431
|
async function runCheck(client, _options) {
|
|
352
|
-
|
|
432
|
+
const raw = await client.integrityCheck();
|
|
433
|
+
return {
|
|
434
|
+
ok: raw.ok,
|
|
435
|
+
errors: raw.errors,
|
|
436
|
+
sections_checked: raw.sections_checked,
|
|
437
|
+
documents_checked: raw.documents_checked,
|
|
438
|
+
references_checked: raw.references_checked,
|
|
439
|
+
elapsed_ms: raw.elapsed_ms,
|
|
440
|
+
divergent_content: parseDivergentContent(raw.divergent_content)
|
|
441
|
+
};
|
|
353
442
|
}
|
|
354
443
|
|
|
355
444
|
// src/shared/cli/functions/run-gc.function.ts
|
|
@@ -372,8 +461,109 @@ function findDb(dbPath) {
|
|
|
372
461
|
}
|
|
373
462
|
|
|
374
463
|
// src/shared/cli/functions/run-init.function.ts
|
|
464
|
+
var import_node_fs4 = require("node:fs");
|
|
465
|
+
var import_node_path6 = require("node:path");
|
|
466
|
+
|
|
467
|
+
// src/shared/cli/functions/install-default-skills.function.ts
|
|
468
|
+
var import_node_crypto = require("node:crypto");
|
|
375
469
|
var import_node_fs3 = require("node:fs");
|
|
376
470
|
var import_node_path5 = require("node:path");
|
|
471
|
+
var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
472
|
+
function base32Encode(bytes) {
|
|
473
|
+
let result = "";
|
|
474
|
+
let bits = 0;
|
|
475
|
+
let value = 0;
|
|
476
|
+
for (const byte of bytes) {
|
|
477
|
+
value = value << 8 | byte;
|
|
478
|
+
bits += 8;
|
|
479
|
+
while (bits >= 5) {
|
|
480
|
+
result += BASE32_ALPHABET[value >>> bits - 5 & 31];
|
|
481
|
+
bits -= 5;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (bits > 0) {
|
|
485
|
+
result += BASE32_ALPHABET[value << 5 - bits & 31];
|
|
486
|
+
}
|
|
487
|
+
return result.toLowerCase();
|
|
488
|
+
}
|
|
489
|
+
function computeSectionKbid(mimeType, content) {
|
|
490
|
+
const contentBytes = Buffer.from(content, "utf-8");
|
|
491
|
+
const header = `section ${mimeType} ${contentBytes.length.toString()}\0`;
|
|
492
|
+
const headerBytes = Buffer.from(header, "utf-8");
|
|
493
|
+
const input = Buffer.concat([headerBytes, contentBytes]);
|
|
494
|
+
const hash = (0, import_node_crypto.createHash)("sha256").update(input).digest();
|
|
495
|
+
const truncated = new Uint8Array(hash.buffer, 0, 16);
|
|
496
|
+
return base32Encode(truncated);
|
|
497
|
+
}
|
|
498
|
+
function escapeToml(s) {
|
|
499
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
500
|
+
}
|
|
501
|
+
function serializeSkillSection(kbid, title, description, content) {
|
|
502
|
+
const size = Buffer.byteLength(content, "utf-8");
|
|
503
|
+
const header = [
|
|
504
|
+
`kbid = "${escapeToml(kbid)}"`,
|
|
505
|
+
`section_type = "text/markdown"`,
|
|
506
|
+
`type_name = "skill"`,
|
|
507
|
+
`title = "${escapeToml(title)}"`,
|
|
508
|
+
`description = "${escapeToml(description)}"`,
|
|
509
|
+
`size = ${size.toString()}`,
|
|
510
|
+
`token_count = 0`,
|
|
511
|
+
`heading_token_count = 0`,
|
|
512
|
+
`body_token_count = 0`,
|
|
513
|
+
`code_token_count = 0`,
|
|
514
|
+
`created_at = 0`,
|
|
515
|
+
`checksum = "${escapeToml(kbid)}"`,
|
|
516
|
+
""
|
|
517
|
+
].join("\n");
|
|
518
|
+
return `${header}
|
|
519
|
+
---
|
|
520
|
+
${content}`;
|
|
521
|
+
}
|
|
522
|
+
function installSkill(sectionsDir, skill) {
|
|
523
|
+
const content = JSON.stringify({
|
|
524
|
+
name: skill.name,
|
|
525
|
+
description: skill.description,
|
|
526
|
+
arguments: [],
|
|
527
|
+
body: skill.body
|
|
528
|
+
});
|
|
529
|
+
const kbid = computeSectionKbid("text/markdown", content);
|
|
530
|
+
const prefix = kbid.slice(0, 2);
|
|
531
|
+
const shardDir = (0, import_node_path5.join)(sectionsDir, prefix);
|
|
532
|
+
const filePath = (0, import_node_path5.join)(shardDir, `${kbid}.section`);
|
|
533
|
+
if ((0, import_node_fs3.existsSync)(filePath)) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
(0, import_node_fs3.mkdirSync)(shardDir, { recursive: true });
|
|
537
|
+
const fileContent = serializeSkillSection(
|
|
538
|
+
kbid,
|
|
539
|
+
skill.name,
|
|
540
|
+
skill.description,
|
|
541
|
+
content
|
|
542
|
+
);
|
|
543
|
+
(0, import_node_fs3.writeFileSync)(filePath, fileContent, "utf-8");
|
|
544
|
+
}
|
|
545
|
+
var QUERY_GUIDANCE_BODY = 'This knowledge base uses lexical search (stemmed BM25F), not semantic/vector search. Follow these guidelines for effective retrieval:\n\n1. Issue MULTIPLE short keyword queries rather than one long natural-language question. Each query should target 2-3 specific terms.\n\n2. EXPAND SYNONYMS yourself. "login" will not match "sign-in" unless both terms appear in the content. Try alternative phrasings: search for "login", then "sign-in", then "authentication".\n\n3. USE BOOLEAN AND PHRASE SYNTAX:\n - Exact phrases: "error handling"\n - Boolean AND: auth AND token\n - Boolean OR: cache OR redis\n - Exclusion: auth -oauth\n\n4. LEAN ON HEADINGS. Headings carry 2x weight in ranking. Search for terms that are likely in section titles.\n\n5. STEMMING IS AUTOMATIC. "connections" matches "connect", "running" matches "run". You do not need to try inflected forms.\n\n6. USE RECALL AFTER SEARCH. After finding relevant sections with search, use recall with increasing depth (0-3) to progressively expand context: depth 0 = full content, depth 1 = parent documents + back-references, depth 2 = siblings + forward references, depth 3 = referenced sections.\n\n7. CODE IDENTIFIERS ARE SPLIT. camelCase and snake_case names are decomposed: getUserById becomes searchable as "get", "user", "by", "id".\n\n8. PAGINATION. Default limit is 20 results. Use offset for additional pages if total > limit.';
|
|
546
|
+
var MEMORY_PROTOCOL_BODY = "Follow these guidelines to keep the knowledge base accurate and useful:\n\n1. STORE DURABLE FACTS: decisions, architecture choices, configuration values, corrections to prior knowledge, API contracts, and resolved incidents. These are the facts that matter weeks later.\n\n2. DO NOT STORE: transient conversation context, debugging output, ephemeral task state, raw log dumps, or information that changes every session. The brain should contain knowledge, not activity logs.\n\n3. SEARCH BEFORE STORING. Always search the knowledge base before adding new content. If a section already covers the topic, use learn with --replace to update it rather than creating a duplicate.\n\n4. USE --replace FOR CORRECTIONS. When a fact changes (e.g., rate limit updated from 100 to 200 req/min), re-learn the corrected content with --replace so the old version is superseded. Without --replace, both the old and new versions coexist with equal ranking.\n\n5. ATTACH TAGS for scoping. Use tags like project name, domain, or topic (e.g., --tags auth,api,v2) so queries can be filtered by scope later.\n\n6. PREFER RECENT RESULTS. When search returns multiple sections covering the same topic, prefer the one with the most recent created_at timestamp -- it is more likely to reflect the current state.\n\n7. USE RECALL AFTER SEARCH. After finding relevant sections, use recall with increasing depth (0-3) to expand context progressively before making decisions.\n\n8. INCLUDE HEADINGS. Always provide a descriptive title when storing content. Headings carry 2x ranking weight -- omitting them significantly reduces searchability.";
|
|
547
|
+
var DEFAULT_SKILLS = [
|
|
548
|
+
{
|
|
549
|
+
name: "query-guidance",
|
|
550
|
+
description: "How to effectively search this knowledge base. Read this before issuing search queries.",
|
|
551
|
+
body: QUERY_GUIDANCE_BODY
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "memory-protocol",
|
|
555
|
+
description: "Guidelines for what to store in this knowledge base and when. Read this before using the learn tool.",
|
|
556
|
+
body: MEMORY_PROTOCOL_BODY
|
|
557
|
+
}
|
|
558
|
+
];
|
|
559
|
+
function installDefaultSkills(dbDir) {
|
|
560
|
+
const sectionsDir = (0, import_node_path5.join)(dbDir, "sections");
|
|
561
|
+
for (const skill of DEFAULT_SKILLS) {
|
|
562
|
+
installSkill(sectionsDir, skill);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/shared/cli/functions/run-init.function.ts
|
|
377
567
|
var DB_FORMAT_VERSION = 1;
|
|
378
568
|
var CATALOG_TOML = `version = ${DB_FORMAT_VERSION.toString()}
|
|
379
569
|
section_count = 0
|
|
@@ -391,21 +581,21 @@ var GITIGNORE = [
|
|
|
391
581
|
""
|
|
392
582
|
].join("\n");
|
|
393
583
|
function createScaffold(dbDir) {
|
|
394
|
-
(0,
|
|
395
|
-
(0,
|
|
396
|
-
(0,
|
|
397
|
-
(0,
|
|
398
|
-
(0,
|
|
399
|
-
(0,
|
|
400
|
-
(0,
|
|
401
|
-
(0,
|
|
402
|
-
(0,
|
|
584
|
+
(0, import_node_fs4.mkdirSync)(dbDir, { recursive: true });
|
|
585
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path6.join)(dbDir, "sections"));
|
|
586
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path6.join)(dbDir, "documents"));
|
|
587
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dbDir, "catalog.toml"), CATALOG_TOML);
|
|
588
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dbDir, "corpus.bin"), "");
|
|
589
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dbDir, "inverted.idx"), "");
|
|
590
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dbDir, "positional.idx"), "");
|
|
591
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dbDir, "worker.toml"), WORKER_TOML);
|
|
592
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dbDir, ".gitignore"), GITIGNORE);
|
|
403
593
|
}
|
|
404
594
|
function runInit(options) {
|
|
405
|
-
const basePath = options.dbPath ? (0,
|
|
406
|
-
const dbDir = (0,
|
|
595
|
+
const basePath = options.dbPath ? (0, import_node_path6.resolve)(options.dbPath) : process.cwd();
|
|
596
|
+
const dbDir = (0, import_node_path6.join)(basePath, ".kbdb");
|
|
407
597
|
try {
|
|
408
|
-
if ((0,
|
|
598
|
+
if ((0, import_node_fs4.existsSync)(dbDir)) {
|
|
409
599
|
return {
|
|
410
600
|
ok: false,
|
|
411
601
|
targetPath: dbDir,
|
|
@@ -413,6 +603,7 @@ function runInit(options) {
|
|
|
413
603
|
};
|
|
414
604
|
}
|
|
415
605
|
createScaffold(dbDir);
|
|
606
|
+
installDefaultSkills(dbDir);
|
|
416
607
|
return { ok: true, targetPath: dbDir };
|
|
417
608
|
} catch (err) {
|
|
418
609
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -770,6 +961,8 @@ async function dispatchCommand(client, options, io) {
|
|
|
770
961
|
return handleUnlearn(client, options);
|
|
771
962
|
case "search":
|
|
772
963
|
return handleSearch(client, options);
|
|
964
|
+
case "recall":
|
|
965
|
+
return handleRecall(client, options);
|
|
773
966
|
case "content":
|
|
774
967
|
return handleContent(client, options);
|
|
775
968
|
case "check":
|
|
@@ -780,6 +973,8 @@ async function dispatchCommand(client, options, io) {
|
|
|
780
973
|
return handleRebuild(client, options);
|
|
781
974
|
case "status":
|
|
782
975
|
return handleStatus(client, options);
|
|
976
|
+
case "export":
|
|
977
|
+
return handleExport(client, options);
|
|
783
978
|
case "db-init":
|
|
784
979
|
return handleDbInit(options);
|
|
785
980
|
case "db-migrate":
|
|
@@ -868,6 +1063,40 @@ async function handleSearch(client, options) {
|
|
|
868
1063
|
};
|
|
869
1064
|
}
|
|
870
1065
|
}
|
|
1066
|
+
async function handleRecall(client, options) {
|
|
1067
|
+
try {
|
|
1068
|
+
const result = await runRecall(client, options);
|
|
1069
|
+
if ("error" in result) {
|
|
1070
|
+
return {
|
|
1071
|
+
error: `error: ${result.error}`,
|
|
1072
|
+
exitCode: 1
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
return {
|
|
1076
|
+
output: JSON.stringify(result, null, 2),
|
|
1077
|
+
exitCode: 0
|
|
1078
|
+
};
|
|
1079
|
+
} catch (err) {
|
|
1080
|
+
return {
|
|
1081
|
+
error: `recall failed: ${errorToMessage(err)}`,
|
|
1082
|
+
exitCode: 1
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
async function handleExport(client, options) {
|
|
1087
|
+
try {
|
|
1088
|
+
const result = await runExport(client, options);
|
|
1089
|
+
return {
|
|
1090
|
+
output: JSON.stringify(result, null, 2),
|
|
1091
|
+
exitCode: 0
|
|
1092
|
+
};
|
|
1093
|
+
} catch (err) {
|
|
1094
|
+
return {
|
|
1095
|
+
error: `export failed: ${errorToMessage(err)}`,
|
|
1096
|
+
exitCode: 1
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
871
1100
|
async function handleContent(client, options) {
|
|
872
1101
|
try {
|
|
873
1102
|
const result = await runContent(client, options);
|
|
@@ -975,11 +1204,13 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
975
1204
|
"learn",
|
|
976
1205
|
"unlearn",
|
|
977
1206
|
"search",
|
|
1207
|
+
"recall",
|
|
978
1208
|
"content",
|
|
979
1209
|
"check",
|
|
980
1210
|
"gc",
|
|
981
1211
|
"rebuild",
|
|
982
1212
|
"status",
|
|
1213
|
+
"export",
|
|
983
1214
|
"mcp",
|
|
984
1215
|
"db",
|
|
985
1216
|
"skill",
|
|
@@ -1042,6 +1273,7 @@ Knowledge Base:
|
|
|
1042
1273
|
learn Import content from files/stdin
|
|
1043
1274
|
unlearn Remove sections by kbid
|
|
1044
1275
|
search Query the knowledge base
|
|
1276
|
+
recall Retrieve section context by kbid
|
|
1045
1277
|
content Compose rendered content from ids
|
|
1046
1278
|
|
|
1047
1279
|
Database:
|
|
@@ -1054,6 +1286,7 @@ Maintenance:
|
|
|
1054
1286
|
sections
|
|
1055
1287
|
rebuild Reconstruct all index files
|
|
1056
1288
|
status Show database status
|
|
1289
|
+
export Snapshot database for backup
|
|
1057
1290
|
|
|
1058
1291
|
Skills:
|
|
1059
1292
|
skill learn Create a new skill definition
|
|
@@ -1126,14 +1359,18 @@ Options:
|
|
|
1126
1359
|
--title <title> Section title
|
|
1127
1360
|
--description <d> Section description
|
|
1128
1361
|
--docid <docid> Document ID to group under
|
|
1362
|
+
--tags <t,...> Comma-separated tags
|
|
1363
|
+
--replace Supersede existing section by source key
|
|
1129
1364
|
--db <path> Path to .kbdb directory
|
|
1130
1365
|
|
|
1131
1366
|
Output:
|
|
1132
|
-
JSON array of { kbid, path, size, skipped
|
|
1367
|
+
JSON array of { kbid, path, size, skipped,
|
|
1368
|
+
superseded, warnings, near_duplicates }
|
|
1133
1369
|
|
|
1134
1370
|
Examples:
|
|
1135
1371
|
kbdb learn README.md
|
|
1136
1372
|
kbdb learn ./docs/
|
|
1373
|
+
kbdb learn --tags auth,api --replace doc.md
|
|
1137
1374
|
cat notes.txt | kbdb learn -
|
|
1138
1375
|
`,
|
|
1139
1376
|
unlearn: `kbdb unlearn - Remove sections by kbid
|
|
@@ -1174,6 +1411,25 @@ Examples:
|
|
|
1174
1411
|
kbdb search "machine learning"
|
|
1175
1412
|
kbdb search --limit 5 --format text query
|
|
1176
1413
|
kbdb search --mode documents --content term
|
|
1414
|
+
`,
|
|
1415
|
+
recall: `kbdb recall - Retrieve section context by kbid
|
|
1416
|
+
|
|
1417
|
+
Usage: kbdb recall [options] <kbid>...
|
|
1418
|
+
|
|
1419
|
+
Arguments:
|
|
1420
|
+
<kbid> 26-character knowledge base identifier
|
|
1421
|
+
|
|
1422
|
+
Options:
|
|
1423
|
+
--depth <n>, -d Expansion depth (0-3, default: 0)
|
|
1424
|
+
--tags <t,...> Filter by tags
|
|
1425
|
+
--db <path> Path to .kbdb directory
|
|
1426
|
+
|
|
1427
|
+
Output:
|
|
1428
|
+
JSON RecallResult or RecallResult[]
|
|
1429
|
+
|
|
1430
|
+
Examples:
|
|
1431
|
+
kbdb recall abcdef1234567890abcdef1234
|
|
1432
|
+
kbdb recall kbid1 --depth 2
|
|
1177
1433
|
`,
|
|
1178
1434
|
content: `kbdb content - Compose rendered content from ids
|
|
1179
1435
|
|
|
@@ -1247,6 +1503,25 @@ Output:
|
|
|
1247
1503
|
Examples:
|
|
1248
1504
|
kbdb status
|
|
1249
1505
|
kbdb status --db /path/to/project
|
|
1506
|
+
`,
|
|
1507
|
+
export: `kbdb export - Snapshot database for backup
|
|
1508
|
+
|
|
1509
|
+
Usage: kbdb export [options] [<path>]
|
|
1510
|
+
|
|
1511
|
+
Arguments:
|
|
1512
|
+
<path> Output directory path
|
|
1513
|
+
(default: ./kbdb-export/)
|
|
1514
|
+
|
|
1515
|
+
Options:
|
|
1516
|
+
--db <path> Path to .kbdb directory
|
|
1517
|
+
|
|
1518
|
+
Output:
|
|
1519
|
+
JSON { path, sections_exported, documents_exported }
|
|
1520
|
+
|
|
1521
|
+
Examples:
|
|
1522
|
+
kbdb export
|
|
1523
|
+
kbdb export ./my-backup
|
|
1524
|
+
kbdb export --db /path/to/project
|
|
1250
1525
|
`,
|
|
1251
1526
|
mcp: `kbdb mcp - Start MCP server mode (stdio)
|
|
1252
1527
|
|
|
@@ -1917,6 +2192,23 @@ var WorkerClient = class {
|
|
|
1917
2192
|
docType
|
|
1918
2193
|
});
|
|
1919
2194
|
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Exports the database to a target directory on disk.
|
|
2197
|
+
*
|
|
2198
|
+
* Copies section files, document manifests, and `catalog.toml`
|
|
2199
|
+
* to `params.path`. Index files are not exported because they
|
|
2200
|
+
* are derivable from the section and document data.
|
|
2201
|
+
*
|
|
2202
|
+
* @param params - optional export parameters
|
|
2203
|
+
* @param params.path - target directory path; defaults to
|
|
2204
|
+
* `./kbdb-export/` when not provided
|
|
2205
|
+
* @returns export report with the resolved path and file counts
|
|
2206
|
+
*/
|
|
2207
|
+
async export(params) {
|
|
2208
|
+
return this.call("export", {
|
|
2209
|
+
targetPath: params?.path || void 0
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
1920
2212
|
/**
|
|
1921
2213
|
* Sends a JSON-RPC 2.0 request and awaits the response.
|
|
1922
2214
|
*
|
|
@@ -1974,17 +2266,17 @@ var WorkerClient = class {
|
|
|
1974
2266
|
};
|
|
1975
2267
|
|
|
1976
2268
|
// src/shared/worker-client/functions/create-worker-client.function.ts
|
|
1977
|
-
var
|
|
1978
|
-
var
|
|
2269
|
+
var import_node_fs8 = require("node:fs");
|
|
2270
|
+
var import_node_path11 = require("node:path");
|
|
1979
2271
|
var import_node_os4 = require("node:os");
|
|
1980
2272
|
|
|
1981
2273
|
// src/shared/worker-client/functions/resolve-db-path.function.ts
|
|
1982
|
-
var
|
|
1983
|
-
var
|
|
2274
|
+
var import_node_fs5 = require("node:fs");
|
|
2275
|
+
var import_node_path7 = require("node:path");
|
|
1984
2276
|
function resolveDbPath(targetDir) {
|
|
1985
|
-
const base = targetDir ? (0,
|
|
1986
|
-
const dbPath = (0,
|
|
1987
|
-
if (!(0,
|
|
2277
|
+
const base = targetDir ? (0, import_node_path7.resolve)(targetDir) : process.cwd();
|
|
2278
|
+
const dbPath = (0, import_node_path7.join)(base, ".kbdb");
|
|
2279
|
+
if (!(0, import_node_fs5.existsSync)(dbPath)) {
|
|
1988
2280
|
throw new Error(`database not found: ${dbPath}`);
|
|
1989
2281
|
}
|
|
1990
2282
|
return dbPath;
|
|
@@ -2005,8 +2297,8 @@ async function computeContextId(absolutePath) {
|
|
|
2005
2297
|
}
|
|
2006
2298
|
|
|
2007
2299
|
// src/shared/worker-client/functions/discover-daemon.function.ts
|
|
2008
|
-
var
|
|
2009
|
-
var
|
|
2300
|
+
var import_node_fs6 = require("node:fs");
|
|
2301
|
+
var import_node_path8 = require("node:path");
|
|
2010
2302
|
var import_node_os = require("node:os");
|
|
2011
2303
|
|
|
2012
2304
|
// src/shared/platform/functions/is-process-alive.function.ts
|
|
@@ -2021,21 +2313,21 @@ function isProcessAlive(pid) {
|
|
|
2021
2313
|
|
|
2022
2314
|
// src/shared/worker-client/functions/discover-daemon.function.ts
|
|
2023
2315
|
function discoverDaemon(contextId) {
|
|
2024
|
-
const pidPath = (0,
|
|
2025
|
-
if (!(0,
|
|
2026
|
-
const pidStr = (0,
|
|
2316
|
+
const pidPath = (0, import_node_path8.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.pid`);
|
|
2317
|
+
if (!(0, import_node_fs6.existsSync)(pidPath)) return null;
|
|
2318
|
+
const pidStr = (0, import_node_fs6.readFileSync)(pidPath, "utf-8").trim();
|
|
2027
2319
|
const pid = parseInt(pidStr, 10);
|
|
2028
2320
|
if (isNaN(pid) || !isProcessAlive(pid)) {
|
|
2029
2321
|
try {
|
|
2030
|
-
(0,
|
|
2322
|
+
(0, import_node_fs6.unlinkSync)(pidPath);
|
|
2031
2323
|
} catch {
|
|
2032
2324
|
}
|
|
2033
2325
|
return null;
|
|
2034
2326
|
}
|
|
2035
|
-
const socketPath = (0,
|
|
2036
|
-
const crashPath = (0,
|
|
2327
|
+
const socketPath = (0, import_node_path8.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.sock`);
|
|
2328
|
+
const crashPath = (0, import_node_path8.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.crash`);
|
|
2037
2329
|
try {
|
|
2038
|
-
(0,
|
|
2330
|
+
(0, import_node_fs6.unlinkSync)(crashPath);
|
|
2039
2331
|
} catch {
|
|
2040
2332
|
}
|
|
2041
2333
|
return { pid, socketPath };
|
|
@@ -2059,23 +2351,23 @@ function spawnDaemon(contextPath, runtime2, workerScript2) {
|
|
|
2059
2351
|
}
|
|
2060
2352
|
|
|
2061
2353
|
// src/shared/worker-client/functions/wait-for-daemon.function.ts
|
|
2062
|
-
var
|
|
2063
|
-
var
|
|
2354
|
+
var import_node_fs7 = require("node:fs");
|
|
2355
|
+
var import_node_path9 = require("node:path");
|
|
2064
2356
|
var import_node_os2 = require("node:os");
|
|
2065
2357
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
2066
2358
|
var POLL_INTERVAL_MS = 100;
|
|
2067
2359
|
async function waitForDaemon(contextId, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
2068
|
-
const pidPath = (0,
|
|
2360
|
+
const pidPath = (0, import_node_path9.join)((0, import_node_os2.tmpdir)(), `kbdb-${contextId}.pid`);
|
|
2069
2361
|
const start = Date.now();
|
|
2070
2362
|
while (Date.now() - start < timeoutMs) {
|
|
2071
|
-
if ((0,
|
|
2363
|
+
if ((0, import_node_fs7.existsSync)(pidPath)) return true;
|
|
2072
2364
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
2073
2365
|
}
|
|
2074
2366
|
return false;
|
|
2075
2367
|
}
|
|
2076
2368
|
|
|
2077
2369
|
// src/shared/platform/functions/get-ipc-path.function.ts
|
|
2078
|
-
var
|
|
2370
|
+
var import_node_path10 = require("node:path");
|
|
2079
2371
|
|
|
2080
2372
|
// src/shared/platform/functions/get-tmpdir.function.ts
|
|
2081
2373
|
var import_node_os3 = require("node:os");
|
|
@@ -2088,7 +2380,7 @@ function getIpcPath(ctx) {
|
|
|
2088
2380
|
if (process.platform === "win32") {
|
|
2089
2381
|
return `\\\\.\\pipe\\kbdb-${ctx}`;
|
|
2090
2382
|
}
|
|
2091
|
-
return (0,
|
|
2383
|
+
return (0, import_node_path10.join)(getTmpdir(), `kbdb-${ctx}.sock`);
|
|
2092
2384
|
}
|
|
2093
2385
|
|
|
2094
2386
|
// src/shared/worker-client/functions/create-worker-client.function.ts
|
|
@@ -2113,14 +2405,14 @@ async function createWorkerClient(options) {
|
|
|
2113
2405
|
return client;
|
|
2114
2406
|
}
|
|
2115
2407
|
function readCrashReason(contextId) {
|
|
2116
|
-
const crashPath = (0,
|
|
2117
|
-
if (!(0,
|
|
2408
|
+
const crashPath = (0, import_node_path11.join)((0, import_node_os4.tmpdir)(), `kbdb-${contextId}.crash`);
|
|
2409
|
+
if (!(0, import_node_fs8.existsSync)(crashPath)) {
|
|
2118
2410
|
return "daemon failed to start (no details available)";
|
|
2119
2411
|
}
|
|
2120
2412
|
try {
|
|
2121
|
-
const raw = (0,
|
|
2413
|
+
const raw = (0, import_node_fs8.readFileSync)(crashPath, "utf-8");
|
|
2122
2414
|
const crash = JSON.parse(raw);
|
|
2123
|
-
(0,
|
|
2415
|
+
(0, import_node_fs8.unlinkSync)(crashPath);
|
|
2124
2416
|
return crash.error ?? "unknown error (crash log empty)";
|
|
2125
2417
|
} catch {
|
|
2126
2418
|
return "daemon failed to start (crash log unreadable)";
|
|
@@ -2135,7 +2427,7 @@ function createToolDefinitions() {
|
|
|
2135
2427
|
return [
|
|
2136
2428
|
{
|
|
2137
2429
|
name: "search",
|
|
2138
|
-
description: "
|
|
2430
|
+
description: "Query the knowledge base by keywords. Returns ranked results with heading, snippet, and matched terms. Use multiple short keyword queries for best coverage. Use recall to get full content.",
|
|
2139
2431
|
inputSchema: {
|
|
2140
2432
|
type: "object",
|
|
2141
2433
|
properties: {
|
|
@@ -2169,7 +2461,7 @@ function createToolDefinitions() {
|
|
|
2169
2461
|
},
|
|
2170
2462
|
{
|
|
2171
2463
|
name: "learn",
|
|
2172
|
-
description: "
|
|
2464
|
+
description: "Import content into the knowledge base. Use for durable facts, decisions, and corrections. Use replace when updating existing knowledge. Attach tags for scoping.",
|
|
2173
2465
|
inputSchema: {
|
|
2174
2466
|
type: "object",
|
|
2175
2467
|
properties: {
|
|
@@ -2192,6 +2484,15 @@ function createToolDefinitions() {
|
|
|
2192
2484
|
docid: {
|
|
2193
2485
|
type: "string",
|
|
2194
2486
|
description: "Document ID to group under"
|
|
2487
|
+
},
|
|
2488
|
+
tags: {
|
|
2489
|
+
type: "array",
|
|
2490
|
+
items: { type: "string" },
|
|
2491
|
+
description: "Tags for scoping and filtering"
|
|
2492
|
+
},
|
|
2493
|
+
replace: {
|
|
2494
|
+
type: "boolean",
|
|
2495
|
+
description: "Replace existing section with same source key (supersession)"
|
|
2195
2496
|
}
|
|
2196
2497
|
},
|
|
2197
2498
|
required: ["content"]
|
|
@@ -2360,6 +2661,26 @@ function createToolDefinitions() {
|
|
|
2360
2661
|
openWorldHint: false
|
|
2361
2662
|
}
|
|
2362
2663
|
},
|
|
2664
|
+
{
|
|
2665
|
+
name: "export",
|
|
2666
|
+
description: "Export the database as a self-contained directory snapshot (sections, documents, catalog -- no indexes).",
|
|
2667
|
+
inputSchema: {
|
|
2668
|
+
type: "object",
|
|
2669
|
+
properties: {
|
|
2670
|
+
path: {
|
|
2671
|
+
type: "string",
|
|
2672
|
+
description: "Export destination path (default: ./kbdb-export/)"
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
},
|
|
2676
|
+
annotations: {
|
|
2677
|
+
title: "Export Snapshot",
|
|
2678
|
+
readOnlyHint: false,
|
|
2679
|
+
destructiveHint: false,
|
|
2680
|
+
idempotentHint: true,
|
|
2681
|
+
openWorldHint: false
|
|
2682
|
+
}
|
|
2683
|
+
},
|
|
2363
2684
|
{
|
|
2364
2685
|
name: "skill-learn",
|
|
2365
2686
|
description: "Create or update a skill definition. Skills are reusable AI agent capabilities with template placeholders.",
|
|
@@ -2739,7 +3060,7 @@ async function handleAgentDelete(client, args) {
|
|
|
2739
3060
|
}
|
|
2740
3061
|
|
|
2741
3062
|
// src/shared/mcp/functions/handle-recall.function.ts
|
|
2742
|
-
async function
|
|
3063
|
+
async function handleRecall2(client, args) {
|
|
2743
3064
|
const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
|
|
2744
3065
|
const kbids = Array.isArray(args["kbids"]) && args["kbids"].every((item) => typeof item === "string") ? args["kbids"] : void 0;
|
|
2745
3066
|
if (!kbid && !kbids) {
|
|
@@ -2769,6 +3090,15 @@ async function handleRecall(client, args) {
|
|
|
2769
3090
|
function asString2(value, fallback) {
|
|
2770
3091
|
return typeof value === "string" ? value : fallback;
|
|
2771
3092
|
}
|
|
3093
|
+
function asBooleanOrUndefined(value) {
|
|
3094
|
+
return typeof value === "boolean" ? value : void 0;
|
|
3095
|
+
}
|
|
3096
|
+
function asStringArrayOrUndefined(value) {
|
|
3097
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
3098
|
+
return value;
|
|
3099
|
+
}
|
|
3100
|
+
return void 0;
|
|
3101
|
+
}
|
|
2772
3102
|
function asStringArray2(value) {
|
|
2773
3103
|
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
2774
3104
|
return value;
|
|
@@ -2787,7 +3117,7 @@ async function handleToolCall(client, name, args) {
|
|
|
2787
3117
|
case "retrieve":
|
|
2788
3118
|
return await handleRetrieve(client, args);
|
|
2789
3119
|
case "recall":
|
|
2790
|
-
return await
|
|
3120
|
+
return await handleRecall2(client, args);
|
|
2791
3121
|
case "status":
|
|
2792
3122
|
return await handleStatus2(client);
|
|
2793
3123
|
case "content":
|
|
@@ -2798,6 +3128,8 @@ async function handleToolCall(client, name, args) {
|
|
|
2798
3128
|
return await handleGc2(client);
|
|
2799
3129
|
case "rebuild":
|
|
2800
3130
|
return await handleRebuild2(client);
|
|
3131
|
+
case "export":
|
|
3132
|
+
return await handleExport2(client, args);
|
|
2801
3133
|
case "skill-learn":
|
|
2802
3134
|
return await handleSkillLearn(client, args);
|
|
2803
3135
|
case "skill-list":
|
|
@@ -2846,7 +3178,13 @@ async function handleSearch2(client, args) {
|
|
|
2846
3178
|
});
|
|
2847
3179
|
const stripped = {
|
|
2848
3180
|
...results,
|
|
2849
|
-
items: results.items.map(
|
|
3181
|
+
items: results.items.map(
|
|
3182
|
+
({
|
|
3183
|
+
score: _score,
|
|
3184
|
+
confidence: _confidence,
|
|
3185
|
+
...rest
|
|
3186
|
+
}) => rest
|
|
3187
|
+
)
|
|
2850
3188
|
};
|
|
2851
3189
|
return {
|
|
2852
3190
|
content: [
|
|
@@ -2862,6 +3200,8 @@ async function handleLearn2(client, args) {
|
|
|
2862
3200
|
const title = typeof args["title"] === "string" ? args["title"] : void 0;
|
|
2863
3201
|
const description = typeof args["description"] === "string" ? args["description"] : void 0;
|
|
2864
3202
|
const docid = typeof args["docid"] === "string" ? args["docid"] : void 0;
|
|
3203
|
+
const tags = asStringArrayOrUndefined(args["tags"]);
|
|
3204
|
+
const replace = asBooleanOrUndefined(args["replace"]);
|
|
2865
3205
|
if (sectionType.startsWith("image/") && !title) {
|
|
2866
3206
|
return {
|
|
2867
3207
|
content: [
|
|
@@ -2878,13 +3218,26 @@ async function handleLearn2(client, args) {
|
|
|
2878
3218
|
content: asString2(args["content"], ""),
|
|
2879
3219
|
title,
|
|
2880
3220
|
description,
|
|
2881
|
-
docid
|
|
3221
|
+
docid,
|
|
3222
|
+
tags,
|
|
3223
|
+
replace
|
|
2882
3224
|
});
|
|
3225
|
+
const normalized = {
|
|
3226
|
+
kbid: result.kbid,
|
|
3227
|
+
indexed_terms_count: result.indexed_terms_count ?? 0,
|
|
3228
|
+
elapsed_ms: result.elapsed_ms ?? 0,
|
|
3229
|
+
superseded: result.superseded ?? null,
|
|
3230
|
+
warnings: result.warnings ?? [],
|
|
3231
|
+
near_duplicates: (result.near_duplicates ?? []).map((d) => ({
|
|
3232
|
+
kbid: d.kbid,
|
|
3233
|
+
similarity: d.similarity
|
|
3234
|
+
}))
|
|
3235
|
+
};
|
|
2883
3236
|
return {
|
|
2884
3237
|
content: [
|
|
2885
3238
|
{
|
|
2886
3239
|
type: "text",
|
|
2887
|
-
text: JSON.stringify(
|
|
3240
|
+
text: JSON.stringify(normalized)
|
|
2888
3241
|
}
|
|
2889
3242
|
]
|
|
2890
3243
|
};
|
|
@@ -3125,6 +3478,20 @@ async function handleSkillDelete(client, args) {
|
|
|
3125
3478
|
]
|
|
3126
3479
|
};
|
|
3127
3480
|
}
|
|
3481
|
+
async function handleExport2(client, args) {
|
|
3482
|
+
const path = typeof args["path"] === "string" ? args["path"] : void 0;
|
|
3483
|
+
const result = await client.export(
|
|
3484
|
+
path !== void 0 ? { path } : {}
|
|
3485
|
+
);
|
|
3486
|
+
return {
|
|
3487
|
+
content: [
|
|
3488
|
+
{
|
|
3489
|
+
type: "text",
|
|
3490
|
+
text: JSON.stringify(result)
|
|
3491
|
+
}
|
|
3492
|
+
]
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3128
3495
|
|
|
3129
3496
|
// src/shared/mcp/functions/handle-resource-read.function.ts
|
|
3130
3497
|
async function handleResourceRead(client, uri) {
|
|
@@ -3496,8 +3863,9 @@ var McpServer = class {
|
|
|
3496
3863
|
const request = parsed;
|
|
3497
3864
|
if (request.id === void 0) {
|
|
3498
3865
|
if (request.method === "notifications/initialized" || request.method === "notifications/cancelled") {
|
|
3499
|
-
if (request.method === "notifications/initialized") {
|
|
3866
|
+
if (request.method === "notifications/initialized" && !this.initialized) {
|
|
3500
3867
|
this.initialized = true;
|
|
3868
|
+
void this.runStartupHealthCheck();
|
|
3501
3869
|
}
|
|
3502
3870
|
}
|
|
3503
3871
|
return null;
|
|
@@ -3690,6 +4058,27 @@ var McpServer = class {
|
|
|
3690
4058
|
const json = JSON.stringify(response);
|
|
3691
4059
|
process.stdout.write(json + "\n");
|
|
3692
4060
|
}
|
|
4061
|
+
/**
|
|
4062
|
+
* Runs an integrity check immediately after the MCP handshake
|
|
4063
|
+
* completes. Emits a `notifications/message` warning when the
|
|
4064
|
+
* check detects issues so the connected agent is aware before
|
|
4065
|
+
* making requests. The server continues operating regardless of
|
|
4066
|
+
* the result.
|
|
4067
|
+
*/
|
|
4068
|
+
async runStartupHealthCheck() {
|
|
4069
|
+
try {
|
|
4070
|
+
const report = await this.client.integrityCheck();
|
|
4071
|
+
if (!report.ok) {
|
|
4072
|
+
const summary = report.errors.map((e) => `${e.type}: ${e.entity} -- ${e.detail}`).join("; ");
|
|
4073
|
+
this.notify("notifications/message", {
|
|
4074
|
+
level: "warning",
|
|
4075
|
+
logger: "kbdb",
|
|
4076
|
+
data: `Database integrity issues detected: ${summary}`
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
} catch {
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
3693
4082
|
};
|
|
3694
4083
|
|
|
3695
4084
|
// src/shared/mcp/functions/create-mcp-server.function.ts
|