@dikolab/kbdb 0.6.0 → 0.6.2

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.
Files changed (49) hide show
  1. package/README.md +36 -7
  2. package/README.md.bak +36 -7
  3. package/dist/README.md +36 -7
  4. package/dist/cli.cjs +663 -218
  5. package/dist/cli.cjs.map +3 -3
  6. package/dist/cli.mjs +663 -218
  7. package/dist/cli.mjs.map +3 -3
  8. package/dist/kbdb-worker.cjs +71 -12
  9. package/dist/kbdb-worker.cjs.map +3 -3
  10. package/dist/mod.cjs +1 -1
  11. package/dist/mod.cjs.map +1 -1
  12. package/dist/mod.mjs +1 -1
  13. package/dist/mod.mjs.map +1 -1
  14. package/dist/src/shared/cli/constants/recfile-field-maps.constant.d.ts +3 -1
  15. package/dist/src/shared/cli/functions/format-command-output.function.d.ts +0 -4
  16. package/dist/src/shared/cli/functions/format-command-text.function.d.ts +1 -0
  17. package/dist/src/shared/cli/functions/format-mcp-output.function.d.ts +5 -4
  18. package/dist/src/shared/cli/functions/format-output.function.d.ts +3 -7
  19. package/dist/src/shared/cli/functions/format-text-output.function.d.ts +3 -2
  20. package/dist/src/shared/cli/functions/run-agent-search.function.d.ts +13 -0
  21. package/dist/src/shared/cli/functions/run-check.function.d.ts +1 -1
  22. package/dist/src/shared/cli/functions/run-content.function.d.ts +18 -14
  23. package/dist/src/shared/cli/functions/run-learn.function.d.ts +6 -11
  24. package/dist/src/shared/cli/functions/run-skill-search.function.d.ts +13 -0
  25. package/dist/src/shared/cli/functions/wrap-mcp-envelope.function.d.ts +8 -0
  26. package/dist/src/shared/cli/typings/cli-options.interface.d.ts +6 -0
  27. package/dist/src/shared/cli/typings/learn-client.interface.d.ts +6 -2
  28. package/dist/src/shared/cli/typings/learn-result.model.d.ts +4 -0
  29. package/dist/src/shared/cli/typings/search-display.model.d.ts +2 -2
  30. package/dist/src/shared/mcp/constants/tool-agent-search.constant.d.ts +3 -0
  31. package/dist/src/shared/mcp/constants/tool-skill-search.constant.d.ts +3 -0
  32. package/dist/src/shared/mcp/functions/format-search-results.function.d.ts +25 -0
  33. package/dist/src/shared/mcp/functions/handle-agent-get.function.d.ts +4 -4
  34. package/dist/src/shared/mcp/functions/handle-agent-list.function.d.ts +3 -7
  35. package/dist/src/shared/mcp/functions/handle-agent-search.function.d.ts +11 -0
  36. package/dist/src/shared/mcp/functions/handle-recall.function.d.ts +1 -1
  37. package/dist/src/shared/mcp/functions/handle-skill-search.function.d.ts +11 -0
  38. package/dist/src/shared/mcp/functions/handle-tool-search.function.d.ts +3 -3
  39. package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +2 -2
  40. package/dist/src/shared/mcp/typings/mcp-worker-client.interface.d.ts +2 -2
  41. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  42. package/dist/src/shared/worker-client/typings/add-section-result.model.d.ts +6 -12
  43. package/dist/src/shared/worker-client/typings/status-result.model.d.ts +2 -2
  44. package/dist/src/shared/worker-daemon/functions/strip-recall-by-depth.function.d.ts +22 -0
  45. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  46. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  47. package/dist/worker.mjs +71 -12
  48. package/dist/worker.mjs.map +3 -3
  49. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -152,6 +152,10 @@ function parseArgs(argv) {
152
152
  case "-c":
153
153
  options.content = true;
154
154
  break;
155
+ case "--search":
156
+ case "-s":
157
+ options.search = true;
158
+ break;
155
159
  case "--relaxed":
156
160
  options.relaxed = true;
157
161
  break;
@@ -188,18 +192,19 @@ function parseArgs(argv) {
188
192
  }
189
193
 
190
194
  // src/shared/cli/functions/format-text-output.function.ts
191
- function formatTextOutput(paged) {
195
+ function formatTextOutput(paged, content) {
192
196
  if (paged.items.length === 0) return "No results found.";
193
197
  const start = paged.offset + 1;
194
198
  const end = paged.offset + paged.items.length;
195
199
  const lines = paged.items.map((r, i) => {
196
200
  const num = paged.offset + i + 1;
197
- const heading = r.heading ?? "(untitled)";
201
+ const heading = r.heading || "(untitled)";
198
202
  const terms = r.matched_terms.join(", ");
199
203
  const fields = r.matched_fields.join(", ");
200
204
  const confidencePct = (r.confidence * 100).toFixed(0);
205
+ const body = content ? r.preview || "(no content)" : r.snippet || r.preview || "(no snippet)";
201
206
  return `${String(num)}. [${r.kbid}] ${heading} (score: ${r.score.toFixed(4)}, confidence: ${confidencePct}%)
202
- ${r.snippet || r.preview || "(no snippet)"}
207
+ ${body}
203
208
  type: ${r.type} terms: ${terms}` + (fields ? ` fields: ${fields}` : "");
204
209
  }).join("\n\n");
205
210
  const footer = `
@@ -210,29 +215,21 @@ Use --offset ${String(end)} to see more` : "";
210
215
  }
211
216
 
212
217
  // src/shared/cli/functions/format-mcp-output.function.ts
213
- function formatMcpOutput(paged) {
214
- const mcpItems = paged.items.map((r) => ({
215
- kbid: r.kbid,
216
- heading: r.heading,
217
- type: r.type,
218
- docids: r.docids,
219
- snippet: r.snippet,
220
- matched_terms: r.matched_terms,
221
- matched_fields: r.matched_fields,
222
- created_at: r.created_at
223
- }));
218
+ function formatMcpOutput(paged, content) {
219
+ const text = paged.items.length === 0 ? "No results found." : paged.items.map((r) => {
220
+ const body = content ? r.preview || "(no content)" : r.snippet || r.preview;
221
+ return `[${r.kbid}] ${r.heading || "(untitled)"} \u2014 ${body}`;
222
+ }).join("\n");
224
223
  return JSON.stringify(
225
224
  {
226
- type: "text",
227
- text: paged.items.map(
228
- (r) => `[${r.kbid}] ${r.heading ?? "(untitled)"} \u2014 ${r.snippet || r.preview}`
229
- ).join("\n"),
230
- items: mcpItems,
231
- total: paged.total,
232
- offset: paged.offset,
233
- limit: paged.limit,
234
- has_more: paged.has_more,
235
- relaxed: paged.relaxed
225
+ jsonrpc: "2.0",
226
+ result: {
227
+ content: [{ type: "text", text }],
228
+ total: paged.total,
229
+ offset: paged.offset,
230
+ limit: paged.limit,
231
+ has_more: paged.has_more
232
+ }
236
233
  },
237
234
  null,
238
235
  2
@@ -302,15 +299,24 @@ var SEARCH_FIELDS = [
302
299
  ["snippet", "snippet"],
303
300
  ["level", "level"]
304
301
  ];
305
- var LEARN_FIELDS = [
302
+ var SEARCH_CONTENT_FIELDS = [
306
303
  ["kbid", "kbid"],
307
- ["path", "path"],
308
- ["size", "size"],
309
- ["skipped", "skipped"],
310
- ["superseded", "superseded"],
311
- ["warnings", "warnings"],
304
+ ["heading", "heading"],
305
+ ["score", "score"],
306
+ ["confidence", "confidence"],
307
+ ["type", "type"],
308
+ ["terms", "matched_terms"],
309
+ ["fields", "matched_fields"],
310
+ ["content", "preview"],
312
311
  ["level", "level"]
313
312
  ];
313
+ var LEARN_FIELDS = [
314
+ ["items", "items"],
315
+ ["total", "total"],
316
+ ["offset", "offset"],
317
+ ["superseded", "superseded"],
318
+ ["warnings", "warnings"]
319
+ ];
314
320
  var UNLEARN_FIELDS = [
315
321
  ["removed", "removed"],
316
322
  ["elapsed_ms", "elapsed_ms"]
@@ -338,7 +344,9 @@ var IMPORT_FIELDS = [
338
344
  ];
339
345
  var STATUS_FIELDS = [
340
346
  ["initialized", "initialized"],
341
- ["path", "contextPath"],
347
+ ["path", "context_path"],
348
+ ["documents", "document_count"],
349
+ ["sections", "section_count"],
342
350
  ["content_cached", "cacheStats.contentCount"],
343
351
  ["index_cached", "cacheStats.indexCount"]
344
352
  ];
@@ -361,16 +369,19 @@ var REBUILD_FIELDS = [
361
369
  ];
362
370
 
363
371
  // src/shared/cli/functions/format-output.function.ts
364
- function formatOutput(paged, format) {
372
+ function formatOutput(paged, format, content) {
365
373
  switch (format) {
366
374
  case "json":
367
375
  return JSON.stringify(paged, null, 2);
368
376
  case "text":
369
- return formatTextOutput(paged);
377
+ return formatTextOutput(paged, content);
370
378
  case "rec":
371
- return formatRecfileOutput(paged.items, SEARCH_FIELDS);
379
+ return formatRecfileOutput(
380
+ paged.items,
381
+ content ? SEARCH_CONTENT_FIELDS : SEARCH_FIELDS
382
+ );
372
383
  case "mcp":
373
- return formatMcpOutput(paged);
384
+ return formatMcpOutput(paged, content);
374
385
  }
375
386
  }
376
387
 
@@ -387,6 +398,93 @@ var RECFILE_COMMAND_FIELD_MAP = {
387
398
  rebuild: REBUILD_FIELDS
388
399
  };
389
400
 
401
+ // src/shared/cli/functions/format-command-text.function.ts
402
+ function str(v) {
403
+ if (typeof v === "string") return v;
404
+ if (typeof v === "number") return String(v);
405
+ if (typeof v === "boolean") return String(v);
406
+ if (v == null) return "";
407
+ return JSON.stringify(v);
408
+ }
409
+ function formatCommandText(data, command) {
410
+ const obj = data;
411
+ switch (command) {
412
+ case "status":
413
+ return formatStatus(obj);
414
+ case "check":
415
+ return formatCheck(obj);
416
+ case "gc":
417
+ return formatGc(obj);
418
+ case "rebuild":
419
+ return formatRebuild(obj);
420
+ case "unlearn":
421
+ return formatUnlearn(obj);
422
+ case "learn":
423
+ return formatLearn(data);
424
+ default:
425
+ return JSON.stringify(data, null, 2);
426
+ }
427
+ }
428
+ function formatStatus(o) {
429
+ const cs = o["cacheStats"];
430
+ const path = o["context_path"] ?? o["contextPath"] ?? "(unknown)";
431
+ const lines = [
432
+ `Initialized: ${str(o["initialized"] ?? false)}`,
433
+ `Path: ${str(path)}`
434
+ ];
435
+ if (o["document_count"] != null)
436
+ lines.push(`Documents: ${str(o["document_count"])}`);
437
+ if (o["section_count"] != null)
438
+ lines.push(`Sections: ${str(o["section_count"])}`);
439
+ if (cs) {
440
+ lines.push(
441
+ `Content cached: ${str(cs["contentCount"] ?? 0)}`,
442
+ `Index cached: ${str(cs["indexCount"] ?? 0)}`
443
+ );
444
+ }
445
+ return lines.join("\n");
446
+ }
447
+ function formatCheck(o) {
448
+ const ok = o["ok"] ? "OK" : "FAILED";
449
+ return `Integrity check: ${ok} (${str(o["sections_checked"] ?? 0)} sections, ${str(o["documents_checked"] ?? 0)} documents, ${str(o["references_checked"] ?? 0)} refs, ${str(o["elapsed_ms"] ?? 0)}ms)`;
450
+ }
451
+ function formatGc(o) {
452
+ return `GC: removed ${str(o["removed_sections"] ?? 0)} sections, reclaimed ${str(o["removed_bytes"] ?? 0)} bytes (${str(o["elapsed_ms"] ?? 0)}ms)`;
453
+ }
454
+ function formatRebuild(o) {
455
+ return `Rebuild: ${str(o["sections_reindexed"] ?? 0)} sections reindexed, ${str(o["terms_indexed"] ?? 0)} terms (${str(o["elapsed_ms"] ?? 0)}ms)`;
456
+ }
457
+ function formatUnlearn(o) {
458
+ const removed = o["removed"];
459
+ if (Array.isArray(removed)) {
460
+ return `Removed: ${removed.length.toString()} section(s)`;
461
+ }
462
+ return `Removed: ${str(removed ?? false)} (${str(o["elapsed_ms"] ?? 0)}ms)`;
463
+ }
464
+ function formatLearn(data) {
465
+ const obj = data;
466
+ const items = obj["items"];
467
+ if (Array.isArray(items)) {
468
+ return items.map((id) => `Learned: ${str(id)}`).join("\n");
469
+ }
470
+ return JSON.stringify(data, null, 2);
471
+ }
472
+
473
+ // src/shared/cli/functions/wrap-mcp-envelope.function.ts
474
+ function wrapMcpEnvelope(data, command) {
475
+ const text = formatCommandText(data, command);
476
+ return JSON.stringify(
477
+ {
478
+ jsonrpc: "2.0",
479
+ result: {
480
+ content: [{ type: "text", text }]
481
+ }
482
+ },
483
+ null,
484
+ 2
485
+ );
486
+ }
487
+
390
488
  // src/shared/cli/functions/format-command-output.function.ts
391
489
  function formatCommandOutput(data, format, command) {
392
490
  switch (format) {
@@ -395,9 +493,11 @@ function formatCommandOutput(data, format, command) {
395
493
  return fields ? formatRecfileOutput(data, fields) : JSON.stringify(data, null, 2);
396
494
  }
397
495
  case "json":
496
+ return JSON.stringify(data, null, 2);
398
497
  case "text":
498
+ return formatCommandText(data, command);
399
499
  case "mcp":
400
- return JSON.stringify(data, null, 2);
500
+ return wrapMcpEnvelope(data, command);
401
501
  }
402
502
  }
403
503
 
@@ -430,7 +530,7 @@ async function runSearch(client, options) {
430
530
  return {
431
531
  items: results.items.map((r) => ({
432
532
  kbid: r.kbid,
433
- heading: r.heading,
533
+ heading: r.heading || "(untitled)",
434
534
  type: r.type,
435
535
  docids: r.docids,
436
536
  score: r.score,
@@ -456,6 +556,11 @@ async function runSearch(client, options) {
456
556
  var SECTION_TYPES = ["text", "code", "image"];
457
557
  async function resolveKbidPrefix(prefix, client) {
458
558
  if (prefix.length >= 26) return prefix;
559
+ if (prefix.length < 2) {
560
+ const msg = `prefix "${prefix}" is too short (minimum 2 characters)`;
561
+ log(msg, 3 /* ERROR */);
562
+ throw new Error(msg);
563
+ }
459
564
  const seen = /* @__PURE__ */ new Set();
460
565
  for (const type of SECTION_TYPES) {
461
566
  const records = await client.listByType(type);
@@ -464,7 +569,7 @@ async function resolveKbidPrefix(prefix, client) {
464
569
  }
465
570
  }
466
571
  if (seen.size === 0) {
467
- const msg = `no section matches prefix "${prefix}"`;
572
+ const msg = `no section matches prefix "${prefix}". Try "kbdb content --search <query>" to find by keyword`;
468
573
  log(msg, 3 /* ERROR */);
469
574
  throw new Error(msg);
470
575
  }
@@ -578,7 +683,9 @@ async function runLearn(client, options, io) {
578
683
  replace: options.replace,
579
684
  level: options.level
580
685
  });
581
- results.push(mapAddSectionResult(result, "-", content.length));
686
+ results.push(
687
+ mapAddSectionResult(result, "-", content.length, "text")
688
+ );
582
689
  continue;
583
690
  }
584
691
  const files = collectFiles(target);
@@ -596,7 +703,7 @@ async function runLearn(client, options, io) {
596
703
  level: options.level ?? computeDirectoryLevel(file, target)
597
704
  });
598
705
  results.push(
599
- mapAddSectionResult(result, file, content.length)
706
+ mapAddSectionResult(result, file, content.length, "text")
600
707
  );
601
708
  }
602
709
  }
@@ -606,9 +713,11 @@ async function runLearn(client, options, io) {
606
713
  );
607
714
  return results;
608
715
  }
609
- function mapAddSectionResult(result, path, size) {
716
+ function mapAddSectionResult(result, path, size, sectionType) {
610
717
  return {
611
- kbid: result.kbid,
718
+ kbid: result.items[0] ?? "",
719
+ indexed_terms_count: 0,
720
+ type: sectionType,
612
721
  path,
613
722
  size,
614
723
  skipped: false,
@@ -645,11 +754,26 @@ async function runUnlearn(client, options) {
645
754
  // src/shared/cli/functions/run-content.function.ts
646
755
  async function runContent(client, options) {
647
756
  log("running content", 0 /* DEBUG */);
757
+ if (options.search) {
758
+ return runContentSearch(client, options);
759
+ }
648
760
  const resolved = await Promise.all(
649
761
  options.args.map((a) => resolveKbidPrefix(a, client))
650
762
  );
651
763
  return client.content(resolved);
652
764
  }
765
+ async function runContentSearch(client, options) {
766
+ const query = options.args.join(" ");
767
+ const result = await client.search({
768
+ query,
769
+ limit: options.limit
770
+ });
771
+ const kbids = result.items.map((hit) => hit.kbid);
772
+ if (kbids.length === 0) {
773
+ return { type: "text/markdown", data: "", total: 0 };
774
+ }
775
+ return client.content(kbids);
776
+ }
653
777
 
654
778
  // src/shared/cli/functions/run-check.function.ts
655
779
  function parseDivergentContent(encoded) {
@@ -664,14 +788,21 @@ function parseDivergentContent(encoded) {
664
788
  async function runCheck(client, _options) {
665
789
  log("running check", 0 /* DEBUG */);
666
790
  const raw = await client.integrityCheck();
791
+ const divergent = parseDivergentContent(raw.divergent_content);
792
+ const divergentErrors = divergent.map((d) => ({
793
+ type: "divergent_content",
794
+ entity: d.sourceKey,
795
+ detail: `${d.kbids.length.toString()} divergent sections: ${d.kbids.join(", ")}`
796
+ }));
797
+ const errors = [...raw.errors, ...divergentErrors];
667
798
  return {
668
- ok: raw.ok,
669
- errors: raw.errors,
799
+ ok: raw.ok && errors.length === 0,
800
+ errors,
670
801
  sections_checked: raw.sections_checked,
671
802
  documents_checked: raw.documents_checked,
672
803
  references_checked: raw.references_checked,
673
804
  elapsed_ms: raw.elapsed_ms,
674
- divergent_content: parseDivergentContent(raw.divergent_content)
805
+ divergent_content: divergent
675
806
  };
676
807
  }
677
808
 
@@ -969,7 +1100,15 @@ async function runSkillLearn(client, options) {
969
1100
  description: options.description
970
1101
  });
971
1102
  return {
972
- output: JSON.stringify(result, null, 2),
1103
+ output: JSON.stringify(
1104
+ {
1105
+ kbid: result.items[0],
1106
+ name,
1107
+ arguments: args.length > 0 ? args : []
1108
+ },
1109
+ null,
1110
+ 2
1111
+ ),
973
1112
  exitCode: 0
974
1113
  };
975
1114
  }
@@ -1030,10 +1169,12 @@ async function runSkillGet(client, options) {
1030
1169
  };
1031
1170
  }
1032
1171
  let content;
1033
- if (/^[0-9a-f]{16,}$/i.test(identifier)) {
1172
+ let kbid;
1173
+ if (/^[a-z2-7]{26}$/.test(identifier)) {
1034
1174
  const sections = await client.readSections([identifier]);
1035
1175
  if (sections.length > 0) {
1036
1176
  content = sections[0]?.content;
1177
+ kbid = identifier;
1037
1178
  }
1038
1179
  }
1039
1180
  if (content === void 0) {
@@ -1041,9 +1182,10 @@ async function runSkillGet(client, options) {
1041
1182
  const match = records.filter((r) => r.title === identifier).pop();
1042
1183
  if (match) {
1043
1184
  content = match.content;
1185
+ kbid = match.kbid;
1044
1186
  }
1045
1187
  }
1046
- if (content === void 0) {
1188
+ if (content === void 0 || kbid === void 0) {
1047
1189
  return {
1048
1190
  error: `skill not found: ${identifier}`,
1049
1191
  exitCode: 1
@@ -1051,7 +1193,7 @@ async function runSkillGet(client, options) {
1051
1193
  }
1052
1194
  const skill = parseSkillContent(content);
1053
1195
  return {
1054
- output: JSON.stringify(skill, null, 2),
1196
+ output: JSON.stringify({ kbid, ...skill }, null, 2),
1055
1197
  exitCode: 0
1056
1198
  };
1057
1199
  }
@@ -1066,7 +1208,7 @@ async function runSkillDelete(client, options) {
1066
1208
  const result = await client.removeSection(kbid);
1067
1209
  return {
1068
1210
  output: JSON.stringify(
1069
- { kbid, removed: result.removed },
1211
+ { removed: result.removed ? [kbid] : [] },
1070
1212
  null,
1071
1213
  2
1072
1214
  ),
@@ -1108,7 +1250,7 @@ async function runAgentCreate(client, options) {
1108
1250
  title: name,
1109
1251
  description: options.description
1110
1252
  });
1111
- const allKbids = [metaResult.kbid, ...skills];
1253
+ const allKbids = [metaResult.items[0], ...skills];
1112
1254
  const docid = await client.groupSections(name, allKbids, "agent");
1113
1255
  return {
1114
1256
  output: JSON.stringify(
@@ -1208,11 +1350,33 @@ async function runAgentDelete(client, options) {
1208
1350
  }
1209
1351
  await client.removeGrouping(docid);
1210
1352
  return {
1211
- output: JSON.stringify({ docid, removed: true }, null, 2),
1353
+ output: JSON.stringify({ removed: [docid] }, null, 2),
1212
1354
  exitCode: 0
1213
1355
  };
1214
1356
  }
1215
1357
 
1358
+ // src/shared/cli/functions/run-skill-search.function.ts
1359
+ async function runSkillSearch(client, options) {
1360
+ const paged = await runSearch(client, options);
1361
+ const filtered = paged.items.filter((item) => item.type === "skill");
1362
+ return {
1363
+ ...paged,
1364
+ items: filtered,
1365
+ total: filtered.length
1366
+ };
1367
+ }
1368
+
1369
+ // src/shared/cli/functions/run-agent-search.function.ts
1370
+ async function runAgentSearch(client, options) {
1371
+ const paged = await runSearch(client, options);
1372
+ const filtered = paged.items.filter((item) => item.type === "agent");
1373
+ return {
1374
+ ...paged,
1375
+ items: filtered,
1376
+ total: filtered.length
1377
+ };
1378
+ }
1379
+
1216
1380
  // src/shared/cli/functions/run-import.function.ts
1217
1381
  import { resolve as resolve4 } from "node:path";
1218
1382
  async function runImport(client, options) {
@@ -1303,6 +1467,10 @@ async function dispatchCommand(client, options, io) {
1303
1467
  const result = await runAgentDelete(client, options);
1304
1468
  return result;
1305
1469
  }
1470
+ case "skill-search":
1471
+ return handleTypedSearch(client, options, "skill");
1472
+ case "agent-search":
1473
+ return handleTypedSearch(client, options, "agent");
1306
1474
  default:
1307
1475
  log("unknown command: " + options.command, 2 /* WARNING */);
1308
1476
  return {
@@ -1314,8 +1482,33 @@ async function dispatchCommand(client, options, io) {
1314
1482
  async function handleLearn(client, options, io) {
1315
1483
  try {
1316
1484
  const results = await runLearn(client, options, io);
1485
+ const items = results.map((r) => r.kbid);
1486
+ const base = {
1487
+ items,
1488
+ total: results.length,
1489
+ offset: 0
1490
+ };
1491
+ if (results.length === 0) {
1492
+ const empty = {
1493
+ ...base,
1494
+ superseded: null,
1495
+ warnings: [],
1496
+ near_duplicates: []
1497
+ };
1498
+ return {
1499
+ output: formatCommandOutput(empty, options.format, "learn"),
1500
+ exitCode: 0
1501
+ };
1502
+ }
1503
+ const last = results[results.length - 1];
1504
+ const data = {
1505
+ ...base,
1506
+ superseded: last.superseded,
1507
+ warnings: last.warnings,
1508
+ near_duplicates: last.near_duplicates
1509
+ };
1317
1510
  return {
1318
- output: formatCommandOutput(results, options.format, "learn"),
1511
+ output: formatCommandOutput(data, options.format, "learn"),
1319
1512
  exitCode: 0
1320
1513
  };
1321
1514
  } catch (err) {
@@ -1328,9 +1521,10 @@ async function handleLearn(client, options, io) {
1328
1521
  async function handleUnlearn(client, options) {
1329
1522
  try {
1330
1523
  const results = await runUnlearn(client, options);
1524
+ const removed = results.filter((r) => r.removed).map((r) => r.kbid);
1331
1525
  return {
1332
1526
  output: formatCommandOutput(
1333
- results,
1527
+ { removed },
1334
1528
  options.format,
1335
1529
  "unlearn"
1336
1530
  ),
@@ -1350,7 +1544,7 @@ async function handleSearch(client, options) {
1350
1544
  await hydrateSearchPreviews(client, results);
1351
1545
  }
1352
1546
  return {
1353
- output: formatOutput(results, options.format),
1547
+ output: formatOutput(results, options.format, options.content),
1354
1548
  exitCode: 0
1355
1549
  };
1356
1550
  } catch (err) {
@@ -1411,11 +1605,38 @@ async function handleImport(client, options) {
1411
1605
  async function handleContent(client, options) {
1412
1606
  try {
1413
1607
  const result = await runContent(client, options);
1414
- if (options.format === "rec") {
1608
+ if (options.format === "text") {
1415
1609
  return { output: result.data, exitCode: 0 };
1416
1610
  }
1611
+ if (options.format === "rec") {
1612
+ const header = `type: ${result.type}
1613
+ total: ${String(result.total)}
1614
+
1615
+ `;
1616
+ return {
1617
+ output: header + result.data,
1618
+ exitCode: 0
1619
+ };
1620
+ }
1621
+ if (options.format === "mcp") {
1622
+ const text = result.data || "(empty)";
1623
+ return {
1624
+ output: JSON.stringify(
1625
+ {
1626
+ jsonrpc: "2.0",
1627
+ result: {
1628
+ content: [{ type: "text", text }]
1629
+ }
1630
+ },
1631
+ null,
1632
+ 2
1633
+ ),
1634
+ exitCode: 0
1635
+ };
1636
+ }
1637
+ const envelope = !options.search && options.args.length === 1 ? { kbid: options.args[0], ...result } : result;
1417
1638
  return {
1418
- output: JSON.stringify(result, null, 2),
1639
+ output: JSON.stringify(envelope, null, 2),
1419
1640
  exitCode: 0
1420
1641
  };
1421
1642
  } catch (err) {
@@ -1502,9 +1723,24 @@ async function handleDbMigrate(client, options) {
1502
1723
  };
1503
1724
  }
1504
1725
  }
1726
+ async function handleTypedSearch(client, options, type) {
1727
+ try {
1728
+ const runner = type === "skill" ? runSkillSearch : runAgentSearch;
1729
+ const results = await runner(client, options);
1730
+ return {
1731
+ output: formatOutput(results, options.format),
1732
+ exitCode: 0
1733
+ };
1734
+ } catch (err) {
1735
+ return {
1736
+ error: `${type} search failed: ${errorToMessage(err)}`,
1737
+ exitCode: 1
1738
+ };
1739
+ }
1740
+ }
1505
1741
 
1506
1742
  // src/shared/version/constants/version.constant.ts
1507
- var VERSION = "0.6.0";
1743
+ var VERSION = "0.6.1";
1508
1744
 
1509
1745
  // src/shared/cli/functions/generate-help.function.ts
1510
1746
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
@@ -3048,8 +3284,15 @@ function handleInit(options, deps2) {
3048
3284
  exitCode: 0
3049
3285
  };
3050
3286
  }
3287
+ const msg = result.errorMessage ?? "init failed";
3288
+ if (/already exists/i.test(msg)) {
3289
+ return {
3290
+ output: `${result.targetPath} (already initialized)`,
3291
+ exitCode: 0
3292
+ };
3293
+ }
3051
3294
  return {
3052
- error: `error: ${result.errorMessage ?? "init failed"}`,
3295
+ error: `error: ${msg}`,
3053
3296
  exitCode: 1
3054
3297
  };
3055
3298
  }
@@ -3309,7 +3552,7 @@ function createToolDefinitions() {
3309
3552
  properties: {
3310
3553
  content: {
3311
3554
  type: "string",
3312
- description: "Text content to add"
3555
+ description: "Raw text or Markdown content to index. Do not wrap in JSON."
3313
3556
  },
3314
3557
  type: {
3315
3558
  type: "string",
@@ -3539,7 +3782,7 @@ function createToolDefinitions() {
3539
3782
  },
3540
3783
  body: {
3541
3784
  type: "string",
3542
- description: "Skill body template. Use {{argName}} for placeholders."
3785
+ description: "Skill body template as raw text. Use {{argName}} for placeholders. Do not wrap in JSON."
3543
3786
  }
3544
3787
  },
3545
3788
  required: ["name", "body"]
@@ -3628,7 +3871,7 @@ function createToolDefinitions() {
3628
3871
  },
3629
3872
  persona: {
3630
3873
  type: "string",
3631
- description: "Agent persona prompt"
3874
+ description: "Agent persona as raw text. Do not wrap in JSON."
3632
3875
  },
3633
3876
  skills: {
3634
3877
  type: "array",
@@ -3703,6 +3946,56 @@ function createToolDefinitions() {
3703
3946
  openWorldHint: false
3704
3947
  }
3705
3948
  },
3949
+ {
3950
+ name: "skill-search",
3951
+ description: "Search for skills by keyword. Returns ranked skill definitions matching the query.",
3952
+ inputSchema: {
3953
+ type: "object",
3954
+ properties: {
3955
+ query: {
3956
+ type: "string",
3957
+ description: "Search query text"
3958
+ },
3959
+ limit: {
3960
+ type: "number",
3961
+ description: "Maximum results (default: 20)"
3962
+ }
3963
+ },
3964
+ required: ["query"]
3965
+ },
3966
+ annotations: {
3967
+ title: "Search Skills",
3968
+ readOnlyHint: true,
3969
+ destructiveHint: false,
3970
+ idempotentHint: true,
3971
+ openWorldHint: false
3972
+ }
3973
+ },
3974
+ {
3975
+ name: "agent-search",
3976
+ description: "Search for agents by keyword. Returns ranked agent profiles matching the query.",
3977
+ inputSchema: {
3978
+ type: "object",
3979
+ properties: {
3980
+ query: {
3981
+ type: "string",
3982
+ description: "Search query text"
3983
+ },
3984
+ limit: {
3985
+ type: "number",
3986
+ description: "Maximum results (default: 20)"
3987
+ }
3988
+ },
3989
+ required: ["query"]
3990
+ },
3991
+ annotations: {
3992
+ title: "Search Agents",
3993
+ readOnlyHint: true,
3994
+ destructiveHint: false,
3995
+ idempotentHint: true,
3996
+ openWorldHint: false
3997
+ }
3998
+ },
3706
3999
  {
3707
4000
  name: "auto-capture-review",
3708
4001
  description: "Review and approve pending auto-captured knowledge before storage.",
@@ -3745,6 +4038,33 @@ function createResourceTemplates() {
3745
4038
  ];
3746
4039
  }
3747
4040
 
4041
+ // src/shared/mcp/functions/format-search-results.function.ts
4042
+ function formatSearchResults(query, results) {
4043
+ if (results.items.length === 0) {
4044
+ return `No results found for "${query}"`;
4045
+ }
4046
+ const lines = results.items.map(
4047
+ (item, index) => formatItem(item, results.offset + index + 1)
4048
+ );
4049
+ const start = results.offset + 1;
4050
+ const end = results.offset + results.items.length;
4051
+ const total = String(results.total);
4052
+ return [
4053
+ `Found ${total} results for "${query}"`,
4054
+ "",
4055
+ ...lines,
4056
+ "",
4057
+ `Showing ${String(start)}-${String(end)} of ${total} results`
4058
+ ].join("\n");
4059
+ }
4060
+ function formatItem(item, position) {
4061
+ const heading = item.heading ?? "(untitled)";
4062
+ const terms = item.matched_terms.join(", ");
4063
+ return `${String(position)}. [${item.kbid}] ${heading}
4064
+ ${item.snippet}
4065
+ type: ${item.type} terms: ${terms}`;
4066
+ }
4067
+
3748
4068
  // src/shared/mcp/functions/handle-agent-create.function.ts
3749
4069
  function asString(value, fallback) {
3750
4070
  return typeof value === "string" ? value : fallback;
@@ -3795,35 +4115,52 @@ async function handleAgentCreate(client, args) {
3795
4115
  title: name,
3796
4116
  description
3797
4117
  });
3798
- const allKbids = [metaResult.kbid, ...skills];
4118
+ const allKbids = [metaResult.items[0], ...skills];
3799
4119
  const docid = await client.groupSections(name, allKbids, "agent");
3800
4120
  return {
3801
4121
  content: [
3802
4122
  {
3803
4123
  type: "text",
3804
- text: JSON.stringify({
3805
- docid,
3806
- name,
3807
- skillCount: skills.length
3808
- })
4124
+ text: `Created agent "${name}" (docid: ${docid})
4125
+ Skills: ${String(skills.length)} attached`
3809
4126
  }
3810
4127
  ]
3811
4128
  };
3812
4129
  }
3813
4130
 
3814
4131
  // src/shared/mcp/functions/handle-agent-list.function.ts
4132
+ async function readAgentDescription(client, sections) {
4133
+ const metaKbid = sections[0]?.kbid;
4134
+ if (!metaKbid) return "";
4135
+ const metaSections = await client.readSections([metaKbid]);
4136
+ const first = metaSections.at(0);
4137
+ if (!first) return "";
4138
+ try {
4139
+ const meta = JSON.parse(first.content);
4140
+ return typeof meta["description"] === "string" ? meta["description"] : "";
4141
+ } catch {
4142
+ return "";
4143
+ }
4144
+ }
3815
4145
  async function handleAgentList(client) {
3816
4146
  const docs = await client.listDocumentsByType("agent");
3817
- const items = docs.map((d) => ({
3818
- docid: d.docid,
3819
- name: d.title,
3820
- skillCount: Math.max(0, d.sections.length - 1)
3821
- }));
4147
+ const items = await Promise.all(
4148
+ docs.map(async (d) => ({
4149
+ name: d.title,
4150
+ description: await readAgentDescription(client, d.sections)
4151
+ }))
4152
+ );
4153
+ const lines = items.map(
4154
+ (item, index) => `${String(index + 1)}. ${item.name}` + (item.description ? ` -- ${item.description}` : "")
4155
+ );
4156
+ const text = [`${String(items.length)} agents:`, ...lines].join(
4157
+ "\n"
4158
+ );
3822
4159
  return {
3823
4160
  content: [
3824
4161
  {
3825
4162
  type: "text",
3826
- text: JSON.stringify(items, null, 2)
4163
+ text
3827
4164
  }
3828
4165
  ]
3829
4166
  };
@@ -3872,6 +4209,19 @@ async function resolveSkillRefs(client, skillRefs) {
3872
4209
  }
3873
4210
  });
3874
4211
  }
4212
+ function formatAgent(agent) {
4213
+ const skillLines = agent.skills.length > 0 ? agent.skills.map(
4214
+ (s, index) => `${String(index + 1)}. ${s.name ?? `(missing: ${s.kbid})`}`
4215
+ ).join("\n") : "none";
4216
+ return [
4217
+ `Agent: ${agent.name} (docid: ${agent.docid})`,
4218
+ `Description: ${agent.description}`,
4219
+ `Persona: ${agent.persona}`,
4220
+ "",
4221
+ "Skills:",
4222
+ skillLines
4223
+ ].join("\n");
4224
+ }
3875
4225
  async function handleAgentGet(client, args) {
3876
4226
  const docid = typeof args["docid"] === "string" ? args["docid"] : "";
3877
4227
  if (!docid) {
@@ -3896,7 +4246,7 @@ async function handleAgentGet(client, args) {
3896
4246
  content: [
3897
4247
  {
3898
4248
  type: "text",
3899
- text: JSON.stringify(agent, null, 2)
4249
+ text: formatAgent(agent)
3900
4250
  }
3901
4251
  ]
3902
4252
  };
@@ -3916,17 +4266,74 @@ async function handleAgentDelete(client, args) {
3916
4266
  content: [
3917
4267
  {
3918
4268
  type: "text",
3919
- text: JSON.stringify({ docid, removed: true })
4269
+ text: `Deleted agent (docid: ${docid})`
3920
4270
  }
3921
4271
  ]
3922
4272
  };
3923
4273
  }
3924
4274
 
3925
4275
  // src/shared/mcp/functions/handle-recall.function.ts
4276
+ function asRecord(value) {
4277
+ return typeof value === "object" && value !== null ? value : void 0;
4278
+ }
4279
+ function strField(record, key, fallback) {
4280
+ const value = record[key];
4281
+ return typeof value === "string" ? value : fallback;
4282
+ }
4283
+ function formatList(label, items, line) {
4284
+ if (items === void 0 || items === null) {
4285
+ return "";
4286
+ }
4287
+ const list = Array.isArray(items) ? items : [];
4288
+ const rows = list.map((raw) => asRecord(raw)).filter((r) => r !== void 0).map((r) => ` - ${line(r)}`);
4289
+ if (rows.length > 0) {
4290
+ return `
4291
+
4292
+ ${label}:
4293
+ ${rows.join("\n")}`;
4294
+ }
4295
+ return `
4296
+
4297
+ ${label}: (none)`;
4298
+ }
4299
+ function formatRecallItem(raw) {
4300
+ const item = asRecord(raw) ?? {};
4301
+ const kbid = strField(item, "kbid", "?");
4302
+ const heading = strField(item, "heading", "(untitled)");
4303
+ const type = strField(item, "type", "unknown");
4304
+ const content = strField(item, "content", "");
4305
+ const refLine = (r) => `${strField(r, "heading", strField(r, "kbid", "?"))} (${strField(r, "kbid", "?")})`;
4306
+ const text = `${heading} [${type}] (${kbid})
4307
+
4308
+ ${content}` + formatList(
4309
+ "Documents",
4310
+ item["documents"],
4311
+ (d) => `${strField(d, "title", "?")} (${strField(d, "docid", "?")})`
4312
+ ) + formatList("Back references", item["back_references"], refLine) + formatList("Siblings", item["siblings"], refLine) + formatList("References", item["references"], refLine);
4313
+ return text;
4314
+ }
4315
+ function buildNotFoundResult(id, depth) {
4316
+ const base = {
4317
+ kbid: id,
4318
+ heading: "(not found)",
4319
+ type: "unknown",
4320
+ content: "",
4321
+ docids: []
4322
+ };
4323
+ if (depth >= 1) {
4324
+ base["documents"] = [];
4325
+ base["back_references"] = [];
4326
+ }
4327
+ if (depth >= 2) {
4328
+ base["siblings"] = [];
4329
+ base["references"] = [];
4330
+ }
4331
+ return base;
4332
+ }
3926
4333
  async function handleRecall2(client, args) {
3927
4334
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
3928
4335
  const kbids = Array.isArray(args["kbids"]) && args["kbids"].every((item) => typeof item === "string") ? args["kbids"] : void 0;
3929
- if (!kbid && !kbids) {
4336
+ if (!kbid && (!kbids || kbids.length === 0)) {
3930
4337
  return {
3931
4338
  content: [
3932
4339
  {
@@ -3938,17 +4345,63 @@ async function handleRecall2(client, args) {
3938
4345
  };
3939
4346
  }
3940
4347
  const depth = typeof args["depth"] === "number" ? args["depth"] : 0;
3941
- const result = await client.recall({ kbid, kbids, depth });
4348
+ let result;
4349
+ try {
4350
+ result = await client.recall({ kbid, kbids, depth });
4351
+ } catch {
4352
+ const ids = kbid ? [kbid] : kbids ?? [];
4353
+ const items = ids.map((id) => buildNotFoundResult(id, depth));
4354
+ result = items.length === 1 ? items[0] : items;
4355
+ }
4356
+ const text = Array.isArray(result) ? result.map(formatRecallItem).join("\n\n---\n\n") : formatRecallItem(result);
3942
4357
  return {
3943
4358
  content: [
3944
4359
  {
3945
4360
  type: "text",
3946
- text: JSON.stringify(result, null, 2)
4361
+ text
3947
4362
  }
3948
4363
  ]
3949
4364
  };
3950
4365
  }
3951
4366
 
4367
+ // src/shared/mcp/functions/handle-skill-search.function.ts
4368
+ async function handleSkillSearch(client, args) {
4369
+ const query = typeof args["query"] === "string" ? args["query"] : "";
4370
+ const results = await client.search({
4371
+ query,
4372
+ limit: args["limit"]
4373
+ });
4374
+ const filtered = {
4375
+ ...results,
4376
+ items: results.items.filter(
4377
+ (item) => item.type === "skill"
4378
+ ),
4379
+ total: 0
4380
+ };
4381
+ filtered.total = filtered.items.length;
4382
+ const text = formatSearchResults(query, filtered);
4383
+ return { content: [{ type: "text", text }] };
4384
+ }
4385
+
4386
+ // src/shared/mcp/functions/handle-agent-search.function.ts
4387
+ async function handleAgentSearch(client, args) {
4388
+ const query = typeof args["query"] === "string" ? args["query"] : "";
4389
+ const results = await client.search({
4390
+ query,
4391
+ limit: args["limit"]
4392
+ });
4393
+ const filtered = {
4394
+ ...results,
4395
+ items: results.items.filter(
4396
+ (item) => item.type === "agent"
4397
+ ),
4398
+ total: 0
4399
+ };
4400
+ filtered.total = filtered.items.length;
4401
+ const text = formatSearchResults(query, filtered);
4402
+ return { content: [{ type: "text", text }] };
4403
+ }
4404
+
3952
4405
  // src/shared/mcp/functions/handle-tool-call.function.ts
3953
4406
  function asString2(value, fallback) {
3954
4407
  return typeof value === "string" ? value : fallback;
@@ -3968,6 +4421,18 @@ function asStringArray2(value) {
3968
4421
  }
3969
4422
  return [];
3970
4423
  }
4424
+ function unwrapJsonContent(raw) {
4425
+ const trimmed = raw.trim();
4426
+ if (!trimmed.startsWith("{")) return raw;
4427
+ try {
4428
+ const parsed = JSON.parse(trimmed);
4429
+ if (typeof parsed["content"] === "string")
4430
+ return parsed["content"];
4431
+ if (typeof parsed["text"] === "string") return parsed["text"];
4432
+ } catch {
4433
+ }
4434
+ return raw;
4435
+ }
3971
4436
  async function handleToolCall(client, name, args) {
3972
4437
  log("tool call: " + name, 0 /* DEBUG */);
3973
4438
  try {
@@ -4010,6 +4475,10 @@ async function handleToolCall(client, name, args) {
4010
4475
  return await handleAgentGet(client, args);
4011
4476
  case "agent-delete":
4012
4477
  return await handleAgentDelete(client, args);
4478
+ case "skill-search":
4479
+ return await handleSkillSearch(client, args);
4480
+ case "agent-search":
4481
+ return await handleAgentSearch(client, args);
4013
4482
  case "auto-capture-review":
4014
4483
  return handleAutoCaptureReview();
4015
4484
  default:
@@ -4041,30 +4510,18 @@ async function handleToolCall(client, name, args) {
4041
4510
  }
4042
4511
  }
4043
4512
  async function handleSearch2(client, args) {
4513
+ const query = asString2(args["query"], "");
4044
4514
  const results = await client.search({
4045
- query: asString2(args["query"], ""),
4515
+ query,
4046
4516
  mode: args["mode"],
4047
4517
  limit: args["limit"],
4048
4518
  offset: args["offset"],
4049
4519
  algo: args["algo"],
4050
4520
  relaxed: args["relaxed"]
4051
4521
  });
4052
- const stripped = {
4053
- ...results,
4054
- items: results.items.map(
4055
- ({
4056
- score: _score,
4057
- confidence: _confidence,
4058
- ...rest
4059
- }) => rest
4060
- )
4061
- };
4062
4522
  return {
4063
4523
  content: [
4064
- {
4065
- type: "text",
4066
- text: JSON.stringify(stripped, null, 2)
4067
- }
4524
+ { type: "text", text: formatSearchResults(query, results) }
4068
4525
  ]
4069
4526
  };
4070
4527
  }
@@ -4086,70 +4543,47 @@ async function handleLearn2(client, args) {
4086
4543
  isError: true
4087
4544
  };
4088
4545
  }
4546
+ const rawContent = asString2(args["content"], "");
4547
+ const content = unwrapJsonContent(rawContent);
4089
4548
  const result = await client.addSection({
4090
4549
  sectionType,
4091
- content: asString2(args["content"], ""),
4550
+ content,
4092
4551
  title,
4093
4552
  description,
4094
4553
  docid,
4095
4554
  tags,
4096
4555
  replace
4097
4556
  });
4098
- const normalized = {
4099
- kbid: result.kbid,
4100
- indexed_terms_count: result.indexed_terms_count ?? 0,
4101
- elapsed_ms: result.elapsed_ms ?? 0,
4557
+ const payload = {
4558
+ items: result.items,
4559
+ total: result.total,
4560
+ offset: result.offset,
4102
4561
  superseded: result.superseded ?? null,
4103
4562
  warnings: result.warnings ?? [],
4104
- near_duplicates: (result.near_duplicates ?? []).map((d) => ({
4105
- kbid: d.kbid,
4106
- similarity: d.similarity,
4107
- ...d.method !== void 0 ? { method: d.method } : {}
4108
- }))
4563
+ near_duplicates: result.near_duplicates ?? []
4109
4564
  };
4110
4565
  return {
4111
- content: [
4112
- {
4113
- type: "text",
4114
- text: JSON.stringify(normalized)
4115
- }
4116
- ]
4566
+ content: [{ type: "text", text: JSON.stringify(payload) }]
4117
4567
  };
4118
4568
  }
4119
4569
  async function handleUnlearn2(client, args) {
4120
- const result = await client.removeSection(
4121
- asString2(args["kbid"], "")
4122
- );
4570
+ const kbid = asString2(args["kbid"], "");
4571
+ await client.removeSection(kbid);
4123
4572
  return {
4124
- content: [
4125
- {
4126
- type: "text",
4127
- text: JSON.stringify(result)
4128
- }
4129
- ]
4573
+ content: [{ type: "text", text: `Removed section ${kbid}` }]
4130
4574
  };
4131
4575
  }
4132
4576
  async function handleRetrieve(client, args) {
4133
4577
  if (typeof args["kbid"] === "string") {
4134
4578
  const sections = await client.readSections([args["kbid"]]);
4135
4579
  return {
4136
- content: [
4137
- {
4138
- type: "text",
4139
- text: JSON.stringify(sections, null, 2)
4140
- }
4141
- ]
4580
+ content: [{ type: "text", text: formatSections(sections) }]
4142
4581
  };
4143
4582
  }
4144
4583
  if (typeof args["docid"] === "string") {
4145
4584
  const doc = await client.retrieveDocument(args["docid"]);
4146
4585
  return {
4147
- content: [
4148
- {
4149
- type: "text",
4150
- text: JSON.stringify(doc, null, 2)
4151
- }
4152
- ]
4586
+ content: [{ type: "text", text: formatDocument(doc) }]
4153
4587
  };
4154
4588
  }
4155
4589
  return {
@@ -4162,61 +4596,81 @@ async function handleRetrieve(client, args) {
4162
4596
  isError: true
4163
4597
  };
4164
4598
  }
4599
+ function formatSections(sections) {
4600
+ if (sections.length === 0) {
4601
+ return "No sections found";
4602
+ }
4603
+ return sections.map((s) => `${s.kbid} (${s.sectionType})
4604
+ ${s.content}`).join("\n\n");
4605
+ }
4606
+ function formatDocument(doc) {
4607
+ if (typeof doc !== "object" || doc === null) {
4608
+ return String(doc);
4609
+ }
4610
+ return Object.entries(doc).map(([key, value]) => {
4611
+ if (Array.isArray(value)) {
4612
+ const items = value.map((item) => formatDocValue(item)).map((s) => ` - ${s}`);
4613
+ return items.length > 0 ? `${key}:
4614
+ ${items.join("\n")}` : `${key}: (none)`;
4615
+ }
4616
+ return `${key}: ${formatDocValue(value)}`;
4617
+ }).join("\n");
4618
+ }
4619
+ function formatDocValue(value) {
4620
+ if (typeof value !== "object" || value === null) {
4621
+ return String(value);
4622
+ }
4623
+ const rec = value;
4624
+ return Object.entries(rec).map(([k, v]) => `${k}=${String(v)}`).join(", ");
4625
+ }
4165
4626
  async function handleStatus2(client) {
4166
4627
  const status = await client.dbStatus();
4167
- return {
4168
- content: [
4169
- {
4170
- type: "text",
4171
- text: JSON.stringify(status, null, 2)
4172
- }
4173
- ]
4174
- };
4628
+ const cs = status.cacheStats;
4629
+ const lines = [
4630
+ `Database: ${status.contextPath}`,
4631
+ `Initialized: ${status.initialized ? "yes" : "no"}`
4632
+ ];
4633
+ if (cs) {
4634
+ lines.push(
4635
+ `Cached content entries: ${String(cs.contentCount)}`,
4636
+ `Cached index entries: ${String(cs.indexCount)}`
4637
+ );
4638
+ }
4639
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4175
4640
  }
4176
4641
  async function handleContent2(client, args) {
4177
4642
  const ids = asStringArray2(args["ids"]);
4178
4643
  const result = await client.content(ids);
4644
+ const text = result.data || "(no content found)";
4179
4645
  return {
4180
- content: [
4181
- {
4182
- type: "text",
4183
- text: JSON.stringify(result)
4184
- }
4185
- ]
4646
+ content: [{ type: "text", text }]
4186
4647
  };
4187
4648
  }
4188
4649
  async function handleCheck2(client) {
4189
4650
  const report = await client.integrityCheck();
4190
- return {
4191
- content: [
4192
- {
4193
- type: "text",
4194
- text: JSON.stringify(report, null, 2)
4195
- }
4196
- ]
4197
- };
4651
+ const status = report.ok ? "OK" : "FAIL";
4652
+ const lines = [
4653
+ `Integrity check: ${status}. ${String(report.sections_checked)} sections checked.`
4654
+ ];
4655
+ for (const error of report.errors) {
4656
+ lines.push(`- ${error.type}: ${error.entity} (${error.detail})`);
4657
+ }
4658
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4198
4659
  }
4199
4660
  async function handleGc2(client) {
4200
4661
  const report = await client.gc();
4201
- return {
4202
- content: [
4203
- {
4204
- type: "text",
4205
- text: JSON.stringify(report, null, 2)
4206
- }
4207
- ]
4208
- };
4662
+ const lines = [
4663
+ `GC complete: removed ${String(report.removed_sections)} sections, freed ${String(report.removed_bytes)} bytes.`
4664
+ ];
4665
+ if (report.stale_sources !== void 0) {
4666
+ lines.push(`stale sources: ${String(report.stale_sources)}`);
4667
+ }
4668
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4209
4669
  }
4210
4670
  async function handleRebuild2(client) {
4211
4671
  const report = await client.rebuildIndexes();
4212
- return {
4213
- content: [
4214
- {
4215
- type: "text",
4216
- text: JSON.stringify(report, null, 2)
4217
- }
4218
- ]
4219
- };
4672
+ const text = `Rebuild complete: ${String(report.sections_reindexed)} sections reindexed, ${String(report.terms_indexed)} terms indexed.`;
4673
+ return { content: [{ type: "text", text }] };
4220
4674
  }
4221
4675
  async function handleSkillLearn(client, args) {
4222
4676
  const name = asString2(args["name"], "");
@@ -4252,37 +4706,22 @@ async function handleSkillLearn(client, args) {
4252
4706
  content: [
4253
4707
  {
4254
4708
  type: "text",
4255
- text: JSON.stringify({ kbid: result.kbid, name })
4709
+ text: `Created skill "${name}" (kbid: ${result.items[0]})`
4256
4710
  }
4257
4711
  ]
4258
4712
  };
4259
4713
  }
4260
4714
  async function handleSkillList(client) {
4261
4715
  const records = await client.listByType("skill");
4262
- const items = records.map((r) => {
4263
- let argumentCount = 0;
4264
- try {
4265
- const parsed = JSON.parse(r.content);
4266
- if (Array.isArray(parsed["arguments"])) {
4267
- argumentCount = parsed["arguments"].length;
4268
- }
4269
- } catch {
4270
- }
4271
- return {
4272
- kbid: r.kbid,
4273
- name: r.title,
4274
- description: r.description,
4275
- argumentCount
4276
- };
4716
+ const lines = records.map((r, index) => {
4717
+ const desc = r.description ? ` -- ${r.description}` : "";
4718
+ const label = r.title ?? "(untitled)";
4719
+ return `${String(index + 1)}. ${label} (${r.kbid})${desc}`;
4277
4720
  });
4278
- return {
4279
- content: [
4280
- {
4281
- type: "text",
4282
- text: JSON.stringify(items, null, 2)
4283
- }
4284
- ]
4285
- };
4721
+ const text = [`${String(records.length)} skills:`, ...lines].join(
4722
+ "\n"
4723
+ );
4724
+ return { content: [{ type: "text", text }] };
4286
4725
  }
4287
4726
  async function handleSkillGet(client, args) {
4288
4727
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
@@ -4326,14 +4765,29 @@ async function handleSkillGet(client, args) {
4326
4765
  }
4327
4766
  const skill = parseSkillContent(content);
4328
4767
  return {
4329
- content: [
4330
- {
4331
- type: "text",
4332
- text: JSON.stringify(skill, null, 2)
4333
- }
4334
- ]
4768
+ content: [{ type: "text", text: formatSkill(skill) }]
4335
4769
  };
4336
4770
  }
4771
+ function formatSkill(skill) {
4772
+ const lines = [`name: ${skill.name}`];
4773
+ if (skill.description) {
4774
+ lines.push(`description: ${skill.description}`);
4775
+ }
4776
+ if (skill.arguments && skill.arguments.length > 0) {
4777
+ lines.push("arguments:");
4778
+ for (const arg of skill.arguments) {
4779
+ const details = [
4780
+ ...arg.type ? [arg.type] : [],
4781
+ ...arg.required ? ["required"] : []
4782
+ ];
4783
+ lines.push(
4784
+ details.length > 0 ? `- ${arg.name}: ${details.join(", ")}` : `- ${arg.name}`
4785
+ );
4786
+ }
4787
+ }
4788
+ lines.push("body:", skill.body);
4789
+ return lines.join("\n");
4790
+ }
4337
4791
  async function handleSkillDelete(client, args) {
4338
4792
  const kbid = asString2(args["kbid"], "");
4339
4793
  if (!kbid) {
@@ -4342,14 +4796,9 @@ async function handleSkillDelete(client, args) {
4342
4796
  isError: true
4343
4797
  };
4344
4798
  }
4345
- const result = await client.removeSection(kbid);
4799
+ await client.removeSection(kbid);
4346
4800
  return {
4347
- content: [
4348
- {
4349
- type: "text",
4350
- text: JSON.stringify({ kbid, removed: result.removed })
4351
- }
4352
- ]
4801
+ content: [{ type: "text", text: `Deleted skill ${kbid}` }]
4353
4802
  };
4354
4803
  }
4355
4804
  function handleAutoCaptureReview() {
@@ -4357,10 +4806,7 @@ function handleAutoCaptureReview() {
4357
4806
  content: [
4358
4807
  {
4359
4808
  type: "text",
4360
- text: JSON.stringify({
4361
- pending: [],
4362
- message: "No pending auto-captured knowledge entries."
4363
- })
4809
+ text: "No pending auto-captured knowledge entries."
4364
4810
  }
4365
4811
  ]
4366
4812
  };
@@ -4370,14 +4816,13 @@ async function handleExport2(client, args) {
4370
4816
  const result = await client.export(
4371
4817
  path !== void 0 ? { path } : {}
4372
4818
  );
4373
- return {
4374
- content: [
4375
- {
4376
- type: "text",
4377
- text: JSON.stringify(result)
4378
- }
4379
- ]
4380
- };
4819
+ const lines = [
4820
+ `Exported to ${result.path}`,
4821
+ `sections: ${String(result.sections_exported)}`,
4822
+ `documents: ${String(result.documents_exported)}`,
4823
+ `elapsed: ${String(result.elapsed_ms)}ms`
4824
+ ];
4825
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4381
4826
  }
4382
4827
 
4383
4828
  // src/shared/mcp/functions/handle-resource-read.function.ts