@dikolab/kbdb 0.2.1 → 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 +59 -24
- package/dist/cli.cjs +462 -73
- package/dist/cli.cjs.map +4 -4
- package/dist/cli.mjs +454 -65
- 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 +18 -1
- package/dist/mod.cjs.map +2 -2
- package/dist/mod.mjs +18 -1
- 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/version/constants/version.constant.d.ts +1 -1
- 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/version/constants/version.constant.ts +1 -1
- 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.mjs
CHANGED
|
@@ -99,6 +99,19 @@ function parseArgs(argv) {
|
|
|
99
99
|
case "--skills":
|
|
100
100
|
options.skills = (argv[++i] ?? "").split(",");
|
|
101
101
|
break;
|
|
102
|
+
case "--tags":
|
|
103
|
+
options.tags = (argv[++i] ?? "").split(",");
|
|
104
|
+
break;
|
|
105
|
+
case "--replace":
|
|
106
|
+
options.replace = true;
|
|
107
|
+
break;
|
|
108
|
+
case "--depth":
|
|
109
|
+
case "-d":
|
|
110
|
+
options.depth = Math.min(
|
|
111
|
+
3,
|
|
112
|
+
Math.max(0, parseInt(argv[++i] ?? "", 10) || 0)
|
|
113
|
+
);
|
|
114
|
+
break;
|
|
102
115
|
case "--content":
|
|
103
116
|
case "-c":
|
|
104
117
|
options.content = true;
|
|
@@ -136,9 +149,11 @@ function formatText(paged) {
|
|
|
136
149
|
const num = paged.offset + i + 1;
|
|
137
150
|
const heading = r.heading ?? "(untitled)";
|
|
138
151
|
const terms = r.matched_terms.join(", ");
|
|
139
|
-
|
|
152
|
+
const fields = r.matched_fields.join(", ");
|
|
153
|
+
const confidencePct = (r.confidence * 100).toFixed(0);
|
|
154
|
+
return `${String(num)}. [${r.kbid}] ${heading} (score: ${r.score.toFixed(4)}, confidence: ${confidencePct}%)
|
|
140
155
|
${r.snippet || r.preview || "(no snippet)"}
|
|
141
|
-
type: ${r.type} terms: ${terms}
|
|
156
|
+
type: ${r.type} terms: ${terms}` + (fields ? ` fields: ${fields}` : "");
|
|
142
157
|
}).join("\n\n");
|
|
143
158
|
const footer = `
|
|
144
159
|
Showing ${String(start)}-${String(end)} of ${String(paged.total)} results`;
|
|
@@ -147,12 +162,32 @@ Use --offset ${String(end)} to see more` : "";
|
|
|
147
162
|
return lines + footer + hint;
|
|
148
163
|
}
|
|
149
164
|
function formatMcp(paged) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
const mcpItems = paged.items.map((r) => ({
|
|
166
|
+
kbid: r.kbid,
|
|
167
|
+
heading: r.heading,
|
|
168
|
+
type: r.type,
|
|
169
|
+
docids: r.docids,
|
|
170
|
+
snippet: r.snippet,
|
|
171
|
+
matched_terms: r.matched_terms,
|
|
172
|
+
matched_fields: r.matched_fields,
|
|
173
|
+
created_at: r.created_at
|
|
174
|
+
}));
|
|
175
|
+
return JSON.stringify(
|
|
176
|
+
{
|
|
177
|
+
type: "text",
|
|
178
|
+
text: paged.items.map(
|
|
179
|
+
(r) => `[${r.kbid}] ${r.heading ?? "(untitled)"} \u2014 ${r.snippet || r.preview}`
|
|
180
|
+
).join("\n"),
|
|
181
|
+
items: mcpItems,
|
|
182
|
+
total: paged.total,
|
|
183
|
+
offset: paged.offset,
|
|
184
|
+
limit: paged.limit,
|
|
185
|
+
has_more: paged.has_more,
|
|
186
|
+
relaxed: paged.relaxed
|
|
187
|
+
},
|
|
188
|
+
null,
|
|
189
|
+
2
|
|
190
|
+
);
|
|
156
191
|
}
|
|
157
192
|
|
|
158
193
|
// src/shared/cli/functions/run-search.function.ts
|
|
@@ -164,7 +199,8 @@ async function runSearch(client, options) {
|
|
|
164
199
|
total: 0,
|
|
165
200
|
offset: 0,
|
|
166
201
|
limit: options.limit,
|
|
167
|
-
has_more: false
|
|
202
|
+
has_more: false,
|
|
203
|
+
relaxed: false
|
|
168
204
|
};
|
|
169
205
|
}
|
|
170
206
|
const results = await client.search({
|
|
@@ -180,17 +216,43 @@ async function runSearch(client, options) {
|
|
|
180
216
|
type: r.type,
|
|
181
217
|
docids: r.docids,
|
|
182
218
|
score: r.score,
|
|
219
|
+
confidence: r.confidence ?? 0,
|
|
183
220
|
snippet: r.snippet,
|
|
184
221
|
matched_terms: r.matched_terms,
|
|
222
|
+
matched_fields: r.matched_fields ?? [],
|
|
223
|
+
created_at: r.created_at ?? "",
|
|
185
224
|
preview: ""
|
|
186
225
|
})),
|
|
187
226
|
total: results.total,
|
|
188
227
|
offset: results.offset,
|
|
189
228
|
limit: results.limit,
|
|
190
|
-
has_more: results.has_more
|
|
229
|
+
has_more: results.has_more,
|
|
230
|
+
relaxed: results.relaxed ?? false
|
|
191
231
|
};
|
|
192
232
|
}
|
|
193
233
|
|
|
234
|
+
// src/shared/cli/functions/run-recall.function.ts
|
|
235
|
+
var MAX_RECALL_DEPTH = 3;
|
|
236
|
+
async function runRecall(client, options) {
|
|
237
|
+
if (options.args.length === 0) {
|
|
238
|
+
return { error: "no kbid provided" };
|
|
239
|
+
}
|
|
240
|
+
const depth = Math.min(
|
|
241
|
+
MAX_RECALL_DEPTH,
|
|
242
|
+
Math.max(0, options.depth ?? 0)
|
|
243
|
+
);
|
|
244
|
+
if (options.args.length === 1) {
|
|
245
|
+
return client.recall({ kbid: options.args[0], depth });
|
|
246
|
+
}
|
|
247
|
+
return client.recall({ kbids: options.args, depth });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/shared/cli/functions/run-export.function.ts
|
|
251
|
+
async function runExport(client, options) {
|
|
252
|
+
const path = options.args[0];
|
|
253
|
+
return client.export({ path });
|
|
254
|
+
}
|
|
255
|
+
|
|
194
256
|
// src/shared/cli/functions/run-learn.function.ts
|
|
195
257
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
196
258
|
import { basename, extname, join } from "node:path";
|
|
@@ -208,14 +270,11 @@ async function runLearn(client, options, io) {
|
|
|
208
270
|
content,
|
|
209
271
|
title: options.title,
|
|
210
272
|
description: options.description,
|
|
211
|
-
docid: options.docid
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
kbid: result.kbid,
|
|
215
|
-
path: "-",
|
|
216
|
-
size: content.length,
|
|
217
|
-
skipped: false
|
|
273
|
+
docid: options.docid,
|
|
274
|
+
tags: options.tags,
|
|
275
|
+
replace: options.replace
|
|
218
276
|
});
|
|
277
|
+
results.push(mapAddSectionResult(result, "-", content.length));
|
|
219
278
|
continue;
|
|
220
279
|
}
|
|
221
280
|
const files = collectFiles(target);
|
|
@@ -225,18 +284,30 @@ async function runLearn(client, options, io) {
|
|
|
225
284
|
sectionType: "text",
|
|
226
285
|
content,
|
|
227
286
|
title: options.title,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
size: content.length,
|
|
234
|
-
skipped: false
|
|
287
|
+
description: options.description,
|
|
288
|
+
docid: options.docid,
|
|
289
|
+
tags: options.tags,
|
|
290
|
+
replace: options.replace,
|
|
291
|
+
sourcePath: file
|
|
235
292
|
});
|
|
293
|
+
results.push(
|
|
294
|
+
mapAddSectionResult(result, file, content.length)
|
|
295
|
+
);
|
|
236
296
|
}
|
|
237
297
|
}
|
|
238
298
|
return results;
|
|
239
299
|
}
|
|
300
|
+
function mapAddSectionResult(result, path, size) {
|
|
301
|
+
return {
|
|
302
|
+
kbid: result.kbid,
|
|
303
|
+
path,
|
|
304
|
+
size,
|
|
305
|
+
skipped: false,
|
|
306
|
+
superseded: result.superseded ?? null,
|
|
307
|
+
warnings: result.warnings ?? [],
|
|
308
|
+
near_duplicates: result.near_duplicates ?? []
|
|
309
|
+
};
|
|
310
|
+
}
|
|
240
311
|
function readProcessStdin() {
|
|
241
312
|
return new Promise((resolve4) => {
|
|
242
313
|
const chunks = [];
|
|
@@ -301,8 +372,26 @@ async function runContent(client, options) {
|
|
|
301
372
|
}
|
|
302
373
|
|
|
303
374
|
// src/shared/cli/functions/run-check.function.ts
|
|
375
|
+
function parseDivergentContent(encoded) {
|
|
376
|
+
if (!encoded) return [];
|
|
377
|
+
return encoded.split(";").filter((segment) => segment.includes(":")).map((segment) => {
|
|
378
|
+
const colonIdx = segment.indexOf(":");
|
|
379
|
+
const sourceKey = segment.slice(0, colonIdx);
|
|
380
|
+
const kbids = segment.slice(colonIdx + 1).split(",").filter((k) => k.length > 0);
|
|
381
|
+
return { sourceKey, kbids };
|
|
382
|
+
}).filter((entry) => entry.kbids.length > 0);
|
|
383
|
+
}
|
|
304
384
|
async function runCheck(client, _options) {
|
|
305
|
-
|
|
385
|
+
const raw = await client.integrityCheck();
|
|
386
|
+
return {
|
|
387
|
+
ok: raw.ok,
|
|
388
|
+
errors: raw.errors,
|
|
389
|
+
sections_checked: raw.sections_checked,
|
|
390
|
+
documents_checked: raw.documents_checked,
|
|
391
|
+
references_checked: raw.references_checked,
|
|
392
|
+
elapsed_ms: raw.elapsed_ms,
|
|
393
|
+
divergent_content: parseDivergentContent(raw.divergent_content)
|
|
394
|
+
};
|
|
306
395
|
}
|
|
307
396
|
|
|
308
397
|
// src/shared/cli/functions/run-gc.function.ts
|
|
@@ -325,8 +414,109 @@ function findDb(dbPath) {
|
|
|
325
414
|
}
|
|
326
415
|
|
|
327
416
|
// src/shared/cli/functions/run-init.function.ts
|
|
417
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
418
|
+
import { join as join4, resolve as resolve2 } from "node:path";
|
|
419
|
+
|
|
420
|
+
// src/shared/cli/functions/install-default-skills.function.ts
|
|
421
|
+
import { createHash } from "node:crypto";
|
|
328
422
|
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "node:fs";
|
|
329
|
-
import { join as join3
|
|
423
|
+
import { join as join3 } from "node:path";
|
|
424
|
+
var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
425
|
+
function base32Encode(bytes) {
|
|
426
|
+
let result = "";
|
|
427
|
+
let bits = 0;
|
|
428
|
+
let value = 0;
|
|
429
|
+
for (const byte of bytes) {
|
|
430
|
+
value = value << 8 | byte;
|
|
431
|
+
bits += 8;
|
|
432
|
+
while (bits >= 5) {
|
|
433
|
+
result += BASE32_ALPHABET[value >>> bits - 5 & 31];
|
|
434
|
+
bits -= 5;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (bits > 0) {
|
|
438
|
+
result += BASE32_ALPHABET[value << 5 - bits & 31];
|
|
439
|
+
}
|
|
440
|
+
return result.toLowerCase();
|
|
441
|
+
}
|
|
442
|
+
function computeSectionKbid(mimeType, content) {
|
|
443
|
+
const contentBytes = Buffer.from(content, "utf-8");
|
|
444
|
+
const header = `section ${mimeType} ${contentBytes.length.toString()}\0`;
|
|
445
|
+
const headerBytes = Buffer.from(header, "utf-8");
|
|
446
|
+
const input = Buffer.concat([headerBytes, contentBytes]);
|
|
447
|
+
const hash = createHash("sha256").update(input).digest();
|
|
448
|
+
const truncated = new Uint8Array(hash.buffer, 0, 16);
|
|
449
|
+
return base32Encode(truncated);
|
|
450
|
+
}
|
|
451
|
+
function escapeToml(s) {
|
|
452
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
453
|
+
}
|
|
454
|
+
function serializeSkillSection(kbid, title, description, content) {
|
|
455
|
+
const size = Buffer.byteLength(content, "utf-8");
|
|
456
|
+
const header = [
|
|
457
|
+
`kbid = "${escapeToml(kbid)}"`,
|
|
458
|
+
`section_type = "text/markdown"`,
|
|
459
|
+
`type_name = "skill"`,
|
|
460
|
+
`title = "${escapeToml(title)}"`,
|
|
461
|
+
`description = "${escapeToml(description)}"`,
|
|
462
|
+
`size = ${size.toString()}`,
|
|
463
|
+
`token_count = 0`,
|
|
464
|
+
`heading_token_count = 0`,
|
|
465
|
+
`body_token_count = 0`,
|
|
466
|
+
`code_token_count = 0`,
|
|
467
|
+
`created_at = 0`,
|
|
468
|
+
`checksum = "${escapeToml(kbid)}"`,
|
|
469
|
+
""
|
|
470
|
+
].join("\n");
|
|
471
|
+
return `${header}
|
|
472
|
+
---
|
|
473
|
+
${content}`;
|
|
474
|
+
}
|
|
475
|
+
function installSkill(sectionsDir, skill) {
|
|
476
|
+
const content = JSON.stringify({
|
|
477
|
+
name: skill.name,
|
|
478
|
+
description: skill.description,
|
|
479
|
+
arguments: [],
|
|
480
|
+
body: skill.body
|
|
481
|
+
});
|
|
482
|
+
const kbid = computeSectionKbid("text/markdown", content);
|
|
483
|
+
const prefix = kbid.slice(0, 2);
|
|
484
|
+
const shardDir = join3(sectionsDir, prefix);
|
|
485
|
+
const filePath = join3(shardDir, `${kbid}.section`);
|
|
486
|
+
if (existsSync2(filePath)) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
mkdirSync(shardDir, { recursive: true });
|
|
490
|
+
const fileContent = serializeSkillSection(
|
|
491
|
+
kbid,
|
|
492
|
+
skill.name,
|
|
493
|
+
skill.description,
|
|
494
|
+
content
|
|
495
|
+
);
|
|
496
|
+
writeFileSync(filePath, fileContent, "utf-8");
|
|
497
|
+
}
|
|
498
|
+
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.';
|
|
499
|
+
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.";
|
|
500
|
+
var DEFAULT_SKILLS = [
|
|
501
|
+
{
|
|
502
|
+
name: "query-guidance",
|
|
503
|
+
description: "How to effectively search this knowledge base. Read this before issuing search queries.",
|
|
504
|
+
body: QUERY_GUIDANCE_BODY
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: "memory-protocol",
|
|
508
|
+
description: "Guidelines for what to store in this knowledge base and when. Read this before using the learn tool.",
|
|
509
|
+
body: MEMORY_PROTOCOL_BODY
|
|
510
|
+
}
|
|
511
|
+
];
|
|
512
|
+
function installDefaultSkills(dbDir) {
|
|
513
|
+
const sectionsDir = join3(dbDir, "sections");
|
|
514
|
+
for (const skill of DEFAULT_SKILLS) {
|
|
515
|
+
installSkill(sectionsDir, skill);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/shared/cli/functions/run-init.function.ts
|
|
330
520
|
var DB_FORMAT_VERSION = 1;
|
|
331
521
|
var CATALOG_TOML = `version = ${DB_FORMAT_VERSION.toString()}
|
|
332
522
|
section_count = 0
|
|
@@ -344,21 +534,21 @@ var GITIGNORE = [
|
|
|
344
534
|
""
|
|
345
535
|
].join("\n");
|
|
346
536
|
function createScaffold(dbDir) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
537
|
+
mkdirSync2(dbDir, { recursive: true });
|
|
538
|
+
mkdirSync2(join4(dbDir, "sections"));
|
|
539
|
+
mkdirSync2(join4(dbDir, "documents"));
|
|
540
|
+
writeFileSync2(join4(dbDir, "catalog.toml"), CATALOG_TOML);
|
|
541
|
+
writeFileSync2(join4(dbDir, "corpus.bin"), "");
|
|
542
|
+
writeFileSync2(join4(dbDir, "inverted.idx"), "");
|
|
543
|
+
writeFileSync2(join4(dbDir, "positional.idx"), "");
|
|
544
|
+
writeFileSync2(join4(dbDir, "worker.toml"), WORKER_TOML);
|
|
545
|
+
writeFileSync2(join4(dbDir, ".gitignore"), GITIGNORE);
|
|
356
546
|
}
|
|
357
547
|
function runInit(options) {
|
|
358
548
|
const basePath = options.dbPath ? resolve2(options.dbPath) : process.cwd();
|
|
359
|
-
const dbDir =
|
|
549
|
+
const dbDir = join4(basePath, ".kbdb");
|
|
360
550
|
try {
|
|
361
|
-
if (
|
|
551
|
+
if (existsSync3(dbDir)) {
|
|
362
552
|
return {
|
|
363
553
|
ok: false,
|
|
364
554
|
targetPath: dbDir,
|
|
@@ -366,6 +556,7 @@ function runInit(options) {
|
|
|
366
556
|
};
|
|
367
557
|
}
|
|
368
558
|
createScaffold(dbDir);
|
|
559
|
+
installDefaultSkills(dbDir);
|
|
369
560
|
return { ok: true, targetPath: dbDir };
|
|
370
561
|
} catch (err) {
|
|
371
562
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -723,6 +914,8 @@ async function dispatchCommand(client, options, io) {
|
|
|
723
914
|
return handleUnlearn(client, options);
|
|
724
915
|
case "search":
|
|
725
916
|
return handleSearch(client, options);
|
|
917
|
+
case "recall":
|
|
918
|
+
return handleRecall(client, options);
|
|
726
919
|
case "content":
|
|
727
920
|
return handleContent(client, options);
|
|
728
921
|
case "check":
|
|
@@ -733,6 +926,8 @@ async function dispatchCommand(client, options, io) {
|
|
|
733
926
|
return handleRebuild(client, options);
|
|
734
927
|
case "status":
|
|
735
928
|
return handleStatus(client, options);
|
|
929
|
+
case "export":
|
|
930
|
+
return handleExport(client, options);
|
|
736
931
|
case "db-init":
|
|
737
932
|
return handleDbInit(options);
|
|
738
933
|
case "db-migrate":
|
|
@@ -821,6 +1016,40 @@ async function handleSearch(client, options) {
|
|
|
821
1016
|
};
|
|
822
1017
|
}
|
|
823
1018
|
}
|
|
1019
|
+
async function handleRecall(client, options) {
|
|
1020
|
+
try {
|
|
1021
|
+
const result = await runRecall(client, options);
|
|
1022
|
+
if ("error" in result) {
|
|
1023
|
+
return {
|
|
1024
|
+
error: `error: ${result.error}`,
|
|
1025
|
+
exitCode: 1
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
output: JSON.stringify(result, null, 2),
|
|
1030
|
+
exitCode: 0
|
|
1031
|
+
};
|
|
1032
|
+
} catch (err) {
|
|
1033
|
+
return {
|
|
1034
|
+
error: `recall failed: ${errorToMessage(err)}`,
|
|
1035
|
+
exitCode: 1
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
async function handleExport(client, options) {
|
|
1040
|
+
try {
|
|
1041
|
+
const result = await runExport(client, options);
|
|
1042
|
+
return {
|
|
1043
|
+
output: JSON.stringify(result, null, 2),
|
|
1044
|
+
exitCode: 0
|
|
1045
|
+
};
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
return {
|
|
1048
|
+
error: `export failed: ${errorToMessage(err)}`,
|
|
1049
|
+
exitCode: 1
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
824
1053
|
async function handleContent(client, options) {
|
|
825
1054
|
try {
|
|
826
1055
|
const result = await runContent(client, options);
|
|
@@ -921,18 +1150,20 @@ async function hydrateSearchPreviews(client, paged) {
|
|
|
921
1150
|
}
|
|
922
1151
|
|
|
923
1152
|
// src/shared/version/constants/version.constant.ts
|
|
924
|
-
var VERSION = "0.2.
|
|
1153
|
+
var VERSION = "0.2.2";
|
|
925
1154
|
|
|
926
1155
|
// src/shared/cli/functions/generate-help.function.ts
|
|
927
1156
|
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
928
1157
|
"learn",
|
|
929
1158
|
"unlearn",
|
|
930
1159
|
"search",
|
|
1160
|
+
"recall",
|
|
931
1161
|
"content",
|
|
932
1162
|
"check",
|
|
933
1163
|
"gc",
|
|
934
1164
|
"rebuild",
|
|
935
1165
|
"status",
|
|
1166
|
+
"export",
|
|
936
1167
|
"mcp",
|
|
937
1168
|
"db",
|
|
938
1169
|
"skill",
|
|
@@ -995,6 +1226,7 @@ Knowledge Base:
|
|
|
995
1226
|
learn Import content from files/stdin
|
|
996
1227
|
unlearn Remove sections by kbid
|
|
997
1228
|
search Query the knowledge base
|
|
1229
|
+
recall Retrieve section context by kbid
|
|
998
1230
|
content Compose rendered content from ids
|
|
999
1231
|
|
|
1000
1232
|
Database:
|
|
@@ -1007,6 +1239,7 @@ Maintenance:
|
|
|
1007
1239
|
sections
|
|
1008
1240
|
rebuild Reconstruct all index files
|
|
1009
1241
|
status Show database status
|
|
1242
|
+
export Snapshot database for backup
|
|
1010
1243
|
|
|
1011
1244
|
Skills:
|
|
1012
1245
|
skill learn Create a new skill definition
|
|
@@ -1079,14 +1312,18 @@ Options:
|
|
|
1079
1312
|
--title <title> Section title
|
|
1080
1313
|
--description <d> Section description
|
|
1081
1314
|
--docid <docid> Document ID to group under
|
|
1315
|
+
--tags <t,...> Comma-separated tags
|
|
1316
|
+
--replace Supersede existing section by source key
|
|
1082
1317
|
--db <path> Path to .kbdb directory
|
|
1083
1318
|
|
|
1084
1319
|
Output:
|
|
1085
|
-
JSON array of { kbid, path, size, skipped
|
|
1320
|
+
JSON array of { kbid, path, size, skipped,
|
|
1321
|
+
superseded, warnings, near_duplicates }
|
|
1086
1322
|
|
|
1087
1323
|
Examples:
|
|
1088
1324
|
kbdb learn README.md
|
|
1089
1325
|
kbdb learn ./docs/
|
|
1326
|
+
kbdb learn --tags auth,api --replace doc.md
|
|
1090
1327
|
cat notes.txt | kbdb learn -
|
|
1091
1328
|
`,
|
|
1092
1329
|
unlearn: `kbdb unlearn - Remove sections by kbid
|
|
@@ -1127,6 +1364,25 @@ Examples:
|
|
|
1127
1364
|
kbdb search "machine learning"
|
|
1128
1365
|
kbdb search --limit 5 --format text query
|
|
1129
1366
|
kbdb search --mode documents --content term
|
|
1367
|
+
`,
|
|
1368
|
+
recall: `kbdb recall - Retrieve section context by kbid
|
|
1369
|
+
|
|
1370
|
+
Usage: kbdb recall [options] <kbid>...
|
|
1371
|
+
|
|
1372
|
+
Arguments:
|
|
1373
|
+
<kbid> 26-character knowledge base identifier
|
|
1374
|
+
|
|
1375
|
+
Options:
|
|
1376
|
+
--depth <n>, -d Expansion depth (0-3, default: 0)
|
|
1377
|
+
--tags <t,...> Filter by tags
|
|
1378
|
+
--db <path> Path to .kbdb directory
|
|
1379
|
+
|
|
1380
|
+
Output:
|
|
1381
|
+
JSON RecallResult or RecallResult[]
|
|
1382
|
+
|
|
1383
|
+
Examples:
|
|
1384
|
+
kbdb recall abcdef1234567890abcdef1234
|
|
1385
|
+
kbdb recall kbid1 --depth 2
|
|
1130
1386
|
`,
|
|
1131
1387
|
content: `kbdb content - Compose rendered content from ids
|
|
1132
1388
|
|
|
@@ -1200,6 +1456,25 @@ Output:
|
|
|
1200
1456
|
Examples:
|
|
1201
1457
|
kbdb status
|
|
1202
1458
|
kbdb status --db /path/to/project
|
|
1459
|
+
`,
|
|
1460
|
+
export: `kbdb export - Snapshot database for backup
|
|
1461
|
+
|
|
1462
|
+
Usage: kbdb export [options] [<path>]
|
|
1463
|
+
|
|
1464
|
+
Arguments:
|
|
1465
|
+
<path> Output directory path
|
|
1466
|
+
(default: ./kbdb-export/)
|
|
1467
|
+
|
|
1468
|
+
Options:
|
|
1469
|
+
--db <path> Path to .kbdb directory
|
|
1470
|
+
|
|
1471
|
+
Output:
|
|
1472
|
+
JSON { path, sections_exported, documents_exported }
|
|
1473
|
+
|
|
1474
|
+
Examples:
|
|
1475
|
+
kbdb export
|
|
1476
|
+
kbdb export ./my-backup
|
|
1477
|
+
kbdb export --db /path/to/project
|
|
1203
1478
|
`,
|
|
1204
1479
|
mcp: `kbdb mcp - Start MCP server mode (stdio)
|
|
1205
1480
|
|
|
@@ -1870,6 +2145,23 @@ var WorkerClient = class {
|
|
|
1870
2145
|
docType
|
|
1871
2146
|
});
|
|
1872
2147
|
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Exports the database to a target directory on disk.
|
|
2150
|
+
*
|
|
2151
|
+
* Copies section files, document manifests, and `catalog.toml`
|
|
2152
|
+
* to `params.path`. Index files are not exported because they
|
|
2153
|
+
* are derivable from the section and document data.
|
|
2154
|
+
*
|
|
2155
|
+
* @param params - optional export parameters
|
|
2156
|
+
* @param params.path - target directory path; defaults to
|
|
2157
|
+
* `./kbdb-export/` when not provided
|
|
2158
|
+
* @returns export report with the resolved path and file counts
|
|
2159
|
+
*/
|
|
2160
|
+
async export(params) {
|
|
2161
|
+
return this.call("export", {
|
|
2162
|
+
targetPath: params?.path || void 0
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
1873
2165
|
/**
|
|
1874
2166
|
* Sends a JSON-RPC 2.0 request and awaits the response.
|
|
1875
2167
|
*
|
|
@@ -1927,17 +2219,17 @@ var WorkerClient = class {
|
|
|
1927
2219
|
};
|
|
1928
2220
|
|
|
1929
2221
|
// src/shared/worker-client/functions/create-worker-client.function.ts
|
|
1930
|
-
import { existsSync as
|
|
1931
|
-
import { join as
|
|
2222
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
2223
|
+
import { join as join8 } from "node:path";
|
|
1932
2224
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
1933
2225
|
|
|
1934
2226
|
// src/shared/worker-client/functions/resolve-db-path.function.ts
|
|
1935
|
-
import { existsSync as
|
|
1936
|
-
import { join as
|
|
2227
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
2228
|
+
import { join as join5, resolve as resolve3 } from "node:path";
|
|
1937
2229
|
function resolveDbPath(targetDir) {
|
|
1938
2230
|
const base = targetDir ? resolve3(targetDir) : process.cwd();
|
|
1939
|
-
const dbPath =
|
|
1940
|
-
if (!
|
|
2231
|
+
const dbPath = join5(base, ".kbdb");
|
|
2232
|
+
if (!existsSync4(dbPath)) {
|
|
1941
2233
|
throw new Error(`database not found: ${dbPath}`);
|
|
1942
2234
|
}
|
|
1943
2235
|
return dbPath;
|
|
@@ -1950,8 +2242,8 @@ async function computeContextId(absolutePath) {
|
|
|
1950
2242
|
}
|
|
1951
2243
|
|
|
1952
2244
|
// src/shared/worker-client/functions/discover-daemon.function.ts
|
|
1953
|
-
import { existsSync as
|
|
1954
|
-
import { join as
|
|
2245
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, unlinkSync } from "node:fs";
|
|
2246
|
+
import { join as join6 } from "node:path";
|
|
1955
2247
|
import { tmpdir } from "node:os";
|
|
1956
2248
|
|
|
1957
2249
|
// src/shared/platform/functions/is-process-alive.function.ts
|
|
@@ -1966,8 +2258,8 @@ function isProcessAlive(pid) {
|
|
|
1966
2258
|
|
|
1967
2259
|
// src/shared/worker-client/functions/discover-daemon.function.ts
|
|
1968
2260
|
function discoverDaemon(contextId) {
|
|
1969
|
-
const pidPath =
|
|
1970
|
-
if (!
|
|
2261
|
+
const pidPath = join6(tmpdir(), `kbdb-${contextId}.pid`);
|
|
2262
|
+
if (!existsSync5(pidPath)) return null;
|
|
1971
2263
|
const pidStr = readFileSync2(pidPath, "utf-8").trim();
|
|
1972
2264
|
const pid = parseInt(pidStr, 10);
|
|
1973
2265
|
if (isNaN(pid) || !isProcessAlive(pid)) {
|
|
@@ -1977,8 +2269,8 @@ function discoverDaemon(contextId) {
|
|
|
1977
2269
|
}
|
|
1978
2270
|
return null;
|
|
1979
2271
|
}
|
|
1980
|
-
const socketPath =
|
|
1981
|
-
const crashPath =
|
|
2272
|
+
const socketPath = join6(tmpdir(), `kbdb-${contextId}.sock`);
|
|
2273
|
+
const crashPath = join6(tmpdir(), `kbdb-${contextId}.crash`);
|
|
1982
2274
|
try {
|
|
1983
2275
|
unlinkSync(crashPath);
|
|
1984
2276
|
} catch {
|
|
@@ -2004,16 +2296,16 @@ function spawnDaemon(contextPath, runtime2, workerScript2) {
|
|
|
2004
2296
|
}
|
|
2005
2297
|
|
|
2006
2298
|
// src/shared/worker-client/functions/wait-for-daemon.function.ts
|
|
2007
|
-
import { existsSync as
|
|
2008
|
-
import { join as
|
|
2299
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
2300
|
+
import { join as join7 } from "node:path";
|
|
2009
2301
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
2010
2302
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
2011
2303
|
var POLL_INTERVAL_MS = 100;
|
|
2012
2304
|
async function waitForDaemon(contextId, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
2013
|
-
const pidPath =
|
|
2305
|
+
const pidPath = join7(tmpdir2(), `kbdb-${contextId}.pid`);
|
|
2014
2306
|
const start = Date.now();
|
|
2015
2307
|
while (Date.now() - start < timeoutMs) {
|
|
2016
|
-
if (
|
|
2308
|
+
if (existsSync6(pidPath)) return true;
|
|
2017
2309
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
2018
2310
|
}
|
|
2019
2311
|
return false;
|
|
@@ -2041,8 +2333,8 @@ async function createWorkerClient(options) {
|
|
|
2041
2333
|
return client;
|
|
2042
2334
|
}
|
|
2043
2335
|
function readCrashReason(contextId) {
|
|
2044
|
-
const crashPath =
|
|
2045
|
-
if (!
|
|
2336
|
+
const crashPath = join8(tmpdir3(), `kbdb-${contextId}.crash`);
|
|
2337
|
+
if (!existsSync7(crashPath)) {
|
|
2046
2338
|
return "daemon failed to start (no details available)";
|
|
2047
2339
|
}
|
|
2048
2340
|
try {
|
|
@@ -2063,7 +2355,7 @@ function createToolDefinitions() {
|
|
|
2063
2355
|
return [
|
|
2064
2356
|
{
|
|
2065
2357
|
name: "search",
|
|
2066
|
-
description: "
|
|
2358
|
+
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.",
|
|
2067
2359
|
inputSchema: {
|
|
2068
2360
|
type: "object",
|
|
2069
2361
|
properties: {
|
|
@@ -2097,7 +2389,7 @@ function createToolDefinitions() {
|
|
|
2097
2389
|
},
|
|
2098
2390
|
{
|
|
2099
2391
|
name: "learn",
|
|
2100
|
-
description: "
|
|
2392
|
+
description: "Import content into the knowledge base. Use for durable facts, decisions, and corrections. Use replace when updating existing knowledge. Attach tags for scoping.",
|
|
2101
2393
|
inputSchema: {
|
|
2102
2394
|
type: "object",
|
|
2103
2395
|
properties: {
|
|
@@ -2120,6 +2412,15 @@ function createToolDefinitions() {
|
|
|
2120
2412
|
docid: {
|
|
2121
2413
|
type: "string",
|
|
2122
2414
|
description: "Document ID to group under"
|
|
2415
|
+
},
|
|
2416
|
+
tags: {
|
|
2417
|
+
type: "array",
|
|
2418
|
+
items: { type: "string" },
|
|
2419
|
+
description: "Tags for scoping and filtering"
|
|
2420
|
+
},
|
|
2421
|
+
replace: {
|
|
2422
|
+
type: "boolean",
|
|
2423
|
+
description: "Replace existing section with same source key (supersession)"
|
|
2123
2424
|
}
|
|
2124
2425
|
},
|
|
2125
2426
|
required: ["content"]
|
|
@@ -2288,6 +2589,26 @@ function createToolDefinitions() {
|
|
|
2288
2589
|
openWorldHint: false
|
|
2289
2590
|
}
|
|
2290
2591
|
},
|
|
2592
|
+
{
|
|
2593
|
+
name: "export",
|
|
2594
|
+
description: "Export the database as a self-contained directory snapshot (sections, documents, catalog -- no indexes).",
|
|
2595
|
+
inputSchema: {
|
|
2596
|
+
type: "object",
|
|
2597
|
+
properties: {
|
|
2598
|
+
path: {
|
|
2599
|
+
type: "string",
|
|
2600
|
+
description: "Export destination path (default: ./kbdb-export/)"
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
},
|
|
2604
|
+
annotations: {
|
|
2605
|
+
title: "Export Snapshot",
|
|
2606
|
+
readOnlyHint: false,
|
|
2607
|
+
destructiveHint: false,
|
|
2608
|
+
idempotentHint: true,
|
|
2609
|
+
openWorldHint: false
|
|
2610
|
+
}
|
|
2611
|
+
},
|
|
2291
2612
|
{
|
|
2292
2613
|
name: "skill-learn",
|
|
2293
2614
|
description: "Create or update a skill definition. Skills are reusable AI agent capabilities with template placeholders.",
|
|
@@ -2667,7 +2988,7 @@ async function handleAgentDelete(client, args) {
|
|
|
2667
2988
|
}
|
|
2668
2989
|
|
|
2669
2990
|
// src/shared/mcp/functions/handle-recall.function.ts
|
|
2670
|
-
async function
|
|
2991
|
+
async function handleRecall2(client, args) {
|
|
2671
2992
|
const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
|
|
2672
2993
|
const kbids = Array.isArray(args["kbids"]) && args["kbids"].every((item) => typeof item === "string") ? args["kbids"] : void 0;
|
|
2673
2994
|
if (!kbid && !kbids) {
|
|
@@ -2697,6 +3018,15 @@ async function handleRecall(client, args) {
|
|
|
2697
3018
|
function asString2(value, fallback) {
|
|
2698
3019
|
return typeof value === "string" ? value : fallback;
|
|
2699
3020
|
}
|
|
3021
|
+
function asBooleanOrUndefined(value) {
|
|
3022
|
+
return typeof value === "boolean" ? value : void 0;
|
|
3023
|
+
}
|
|
3024
|
+
function asStringArrayOrUndefined(value) {
|
|
3025
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
3026
|
+
return value;
|
|
3027
|
+
}
|
|
3028
|
+
return void 0;
|
|
3029
|
+
}
|
|
2700
3030
|
function asStringArray2(value) {
|
|
2701
3031
|
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
2702
3032
|
return value;
|
|
@@ -2715,7 +3045,7 @@ async function handleToolCall(client, name, args) {
|
|
|
2715
3045
|
case "retrieve":
|
|
2716
3046
|
return await handleRetrieve(client, args);
|
|
2717
3047
|
case "recall":
|
|
2718
|
-
return await
|
|
3048
|
+
return await handleRecall2(client, args);
|
|
2719
3049
|
case "status":
|
|
2720
3050
|
return await handleStatus2(client);
|
|
2721
3051
|
case "content":
|
|
@@ -2726,6 +3056,8 @@ async function handleToolCall(client, name, args) {
|
|
|
2726
3056
|
return await handleGc2(client);
|
|
2727
3057
|
case "rebuild":
|
|
2728
3058
|
return await handleRebuild2(client);
|
|
3059
|
+
case "export":
|
|
3060
|
+
return await handleExport2(client, args);
|
|
2729
3061
|
case "skill-learn":
|
|
2730
3062
|
return await handleSkillLearn(client, args);
|
|
2731
3063
|
case "skill-list":
|
|
@@ -2774,7 +3106,13 @@ async function handleSearch2(client, args) {
|
|
|
2774
3106
|
});
|
|
2775
3107
|
const stripped = {
|
|
2776
3108
|
...results,
|
|
2777
|
-
items: results.items.map(
|
|
3109
|
+
items: results.items.map(
|
|
3110
|
+
({
|
|
3111
|
+
score: _score,
|
|
3112
|
+
confidence: _confidence,
|
|
3113
|
+
...rest
|
|
3114
|
+
}) => rest
|
|
3115
|
+
)
|
|
2778
3116
|
};
|
|
2779
3117
|
return {
|
|
2780
3118
|
content: [
|
|
@@ -2790,6 +3128,8 @@ async function handleLearn2(client, args) {
|
|
|
2790
3128
|
const title = typeof args["title"] === "string" ? args["title"] : void 0;
|
|
2791
3129
|
const description = typeof args["description"] === "string" ? args["description"] : void 0;
|
|
2792
3130
|
const docid = typeof args["docid"] === "string" ? args["docid"] : void 0;
|
|
3131
|
+
const tags = asStringArrayOrUndefined(args["tags"]);
|
|
3132
|
+
const replace = asBooleanOrUndefined(args["replace"]);
|
|
2793
3133
|
if (sectionType.startsWith("image/") && !title) {
|
|
2794
3134
|
return {
|
|
2795
3135
|
content: [
|
|
@@ -2806,13 +3146,26 @@ async function handleLearn2(client, args) {
|
|
|
2806
3146
|
content: asString2(args["content"], ""),
|
|
2807
3147
|
title,
|
|
2808
3148
|
description,
|
|
2809
|
-
docid
|
|
3149
|
+
docid,
|
|
3150
|
+
tags,
|
|
3151
|
+
replace
|
|
2810
3152
|
});
|
|
3153
|
+
const normalized = {
|
|
3154
|
+
kbid: result.kbid,
|
|
3155
|
+
indexed_terms_count: result.indexed_terms_count ?? 0,
|
|
3156
|
+
elapsed_ms: result.elapsed_ms ?? 0,
|
|
3157
|
+
superseded: result.superseded ?? null,
|
|
3158
|
+
warnings: result.warnings ?? [],
|
|
3159
|
+
near_duplicates: (result.near_duplicates ?? []).map((d) => ({
|
|
3160
|
+
kbid: d.kbid,
|
|
3161
|
+
similarity: d.similarity
|
|
3162
|
+
}))
|
|
3163
|
+
};
|
|
2811
3164
|
return {
|
|
2812
3165
|
content: [
|
|
2813
3166
|
{
|
|
2814
3167
|
type: "text",
|
|
2815
|
-
text: JSON.stringify(
|
|
3168
|
+
text: JSON.stringify(normalized)
|
|
2816
3169
|
}
|
|
2817
3170
|
]
|
|
2818
3171
|
};
|
|
@@ -3053,6 +3406,20 @@ async function handleSkillDelete(client, args) {
|
|
|
3053
3406
|
]
|
|
3054
3407
|
};
|
|
3055
3408
|
}
|
|
3409
|
+
async function handleExport2(client, args) {
|
|
3410
|
+
const path = typeof args["path"] === "string" ? args["path"] : void 0;
|
|
3411
|
+
const result = await client.export(
|
|
3412
|
+
path !== void 0 ? { path } : {}
|
|
3413
|
+
);
|
|
3414
|
+
return {
|
|
3415
|
+
content: [
|
|
3416
|
+
{
|
|
3417
|
+
type: "text",
|
|
3418
|
+
text: JSON.stringify(result)
|
|
3419
|
+
}
|
|
3420
|
+
]
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3056
3423
|
|
|
3057
3424
|
// src/shared/mcp/functions/handle-resource-read.function.ts
|
|
3058
3425
|
async function handleResourceRead(client, uri) {
|
|
@@ -3424,8 +3791,9 @@ var McpServer = class {
|
|
|
3424
3791
|
const request = parsed;
|
|
3425
3792
|
if (request.id === void 0) {
|
|
3426
3793
|
if (request.method === "notifications/initialized" || request.method === "notifications/cancelled") {
|
|
3427
|
-
if (request.method === "notifications/initialized") {
|
|
3794
|
+
if (request.method === "notifications/initialized" && !this.initialized) {
|
|
3428
3795
|
this.initialized = true;
|
|
3796
|
+
void this.runStartupHealthCheck();
|
|
3429
3797
|
}
|
|
3430
3798
|
}
|
|
3431
3799
|
return null;
|
|
@@ -3618,6 +3986,27 @@ var McpServer = class {
|
|
|
3618
3986
|
const json = JSON.stringify(response);
|
|
3619
3987
|
process.stdout.write(json + "\n");
|
|
3620
3988
|
}
|
|
3989
|
+
/**
|
|
3990
|
+
* Runs an integrity check immediately after the MCP handshake
|
|
3991
|
+
* completes. Emits a `notifications/message` warning when the
|
|
3992
|
+
* check detects issues so the connected agent is aware before
|
|
3993
|
+
* making requests. The server continues operating regardless of
|
|
3994
|
+
* the result.
|
|
3995
|
+
*/
|
|
3996
|
+
async runStartupHealthCheck() {
|
|
3997
|
+
try {
|
|
3998
|
+
const report = await this.client.integrityCheck();
|
|
3999
|
+
if (!report.ok) {
|
|
4000
|
+
const summary = report.errors.map((e) => `${e.type}: ${e.entity} -- ${e.detail}`).join("; ");
|
|
4001
|
+
this.notify("notifications/message", {
|
|
4002
|
+
level: "warning",
|
|
4003
|
+
logger: "kbdb",
|
|
4004
|
+
data: `Database integrity issues detected: ${summary}`
|
|
4005
|
+
});
|
|
4006
|
+
}
|
|
4007
|
+
} catch {
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
3621
4010
|
};
|
|
3622
4011
|
|
|
3623
4012
|
// src/shared/mcp/functions/create-mcp-server.function.ts
|