@dikolab/kbdb 0.6.1 → 0.6.3

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 +33 -6
  2. package/README.md.bak +33 -6
  3. package/dist/README.md +33 -6
  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 +1 -1
package/dist/cli.cjs CHANGED
@@ -180,6 +180,10 @@ function parseArgs(argv) {
180
180
  case "-c":
181
181
  options.content = true;
182
182
  break;
183
+ case "--search":
184
+ case "-s":
185
+ options.search = true;
186
+ break;
183
187
  case "--relaxed":
184
188
  options.relaxed = true;
185
189
  break;
@@ -216,18 +220,19 @@ function parseArgs(argv) {
216
220
  }
217
221
 
218
222
  // src/shared/cli/functions/format-text-output.function.ts
219
- function formatTextOutput(paged) {
223
+ function formatTextOutput(paged, content) {
220
224
  if (paged.items.length === 0) return "No results found.";
221
225
  const start = paged.offset + 1;
222
226
  const end = paged.offset + paged.items.length;
223
227
  const lines = paged.items.map((r, i) => {
224
228
  const num = paged.offset + i + 1;
225
- const heading = r.heading ?? "(untitled)";
229
+ const heading = r.heading || "(untitled)";
226
230
  const terms = r.matched_terms.join(", ");
227
231
  const fields = r.matched_fields.join(", ");
228
232
  const confidencePct = (r.confidence * 100).toFixed(0);
233
+ const body = content ? r.preview || "(no content)" : r.snippet || r.preview || "(no snippet)";
229
234
  return `${String(num)}. [${r.kbid}] ${heading} (score: ${r.score.toFixed(4)}, confidence: ${confidencePct}%)
230
- ${r.snippet || r.preview || "(no snippet)"}
235
+ ${body}
231
236
  type: ${r.type} terms: ${terms}` + (fields ? ` fields: ${fields}` : "");
232
237
  }).join("\n\n");
233
238
  const footer = `
@@ -238,29 +243,21 @@ Use --offset ${String(end)} to see more` : "";
238
243
  }
239
244
 
240
245
  // src/shared/cli/functions/format-mcp-output.function.ts
241
- function formatMcpOutput(paged) {
242
- const mcpItems = paged.items.map((r) => ({
243
- kbid: r.kbid,
244
- heading: r.heading,
245
- type: r.type,
246
- docids: r.docids,
247
- snippet: r.snippet,
248
- matched_terms: r.matched_terms,
249
- matched_fields: r.matched_fields,
250
- created_at: r.created_at
251
- }));
246
+ function formatMcpOutput(paged, content) {
247
+ const text = paged.items.length === 0 ? "No results found." : paged.items.map((r) => {
248
+ const body = content ? r.preview || "(no content)" : r.snippet || r.preview;
249
+ return `[${r.kbid}] ${r.heading || "(untitled)"} \u2014 ${body}`;
250
+ }).join("\n");
252
251
  return JSON.stringify(
253
252
  {
254
- type: "text",
255
- text: paged.items.map(
256
- (r) => `[${r.kbid}] ${r.heading ?? "(untitled)"} \u2014 ${r.snippet || r.preview}`
257
- ).join("\n"),
258
- items: mcpItems,
259
- total: paged.total,
260
- offset: paged.offset,
261
- limit: paged.limit,
262
- has_more: paged.has_more,
263
- relaxed: paged.relaxed
253
+ jsonrpc: "2.0",
254
+ result: {
255
+ content: [{ type: "text", text }],
256
+ total: paged.total,
257
+ offset: paged.offset,
258
+ limit: paged.limit,
259
+ has_more: paged.has_more
260
+ }
264
261
  },
265
262
  null,
266
263
  2
@@ -330,15 +327,24 @@ var SEARCH_FIELDS = [
330
327
  ["snippet", "snippet"],
331
328
  ["level", "level"]
332
329
  ];
333
- var LEARN_FIELDS = [
330
+ var SEARCH_CONTENT_FIELDS = [
334
331
  ["kbid", "kbid"],
335
- ["path", "path"],
336
- ["size", "size"],
337
- ["skipped", "skipped"],
338
- ["superseded", "superseded"],
339
- ["warnings", "warnings"],
332
+ ["heading", "heading"],
333
+ ["score", "score"],
334
+ ["confidence", "confidence"],
335
+ ["type", "type"],
336
+ ["terms", "matched_terms"],
337
+ ["fields", "matched_fields"],
338
+ ["content", "preview"],
340
339
  ["level", "level"]
341
340
  ];
341
+ var LEARN_FIELDS = [
342
+ ["items", "items"],
343
+ ["total", "total"],
344
+ ["offset", "offset"],
345
+ ["superseded", "superseded"],
346
+ ["warnings", "warnings"]
347
+ ];
342
348
  var UNLEARN_FIELDS = [
343
349
  ["removed", "removed"],
344
350
  ["elapsed_ms", "elapsed_ms"]
@@ -366,7 +372,9 @@ var IMPORT_FIELDS = [
366
372
  ];
367
373
  var STATUS_FIELDS = [
368
374
  ["initialized", "initialized"],
369
- ["path", "contextPath"],
375
+ ["path", "context_path"],
376
+ ["documents", "document_count"],
377
+ ["sections", "section_count"],
370
378
  ["content_cached", "cacheStats.contentCount"],
371
379
  ["index_cached", "cacheStats.indexCount"]
372
380
  ];
@@ -389,16 +397,19 @@ var REBUILD_FIELDS = [
389
397
  ];
390
398
 
391
399
  // src/shared/cli/functions/format-output.function.ts
392
- function formatOutput(paged, format) {
400
+ function formatOutput(paged, format, content) {
393
401
  switch (format) {
394
402
  case "json":
395
403
  return JSON.stringify(paged, null, 2);
396
404
  case "text":
397
- return formatTextOutput(paged);
405
+ return formatTextOutput(paged, content);
398
406
  case "rec":
399
- return formatRecfileOutput(paged.items, SEARCH_FIELDS);
407
+ return formatRecfileOutput(
408
+ paged.items,
409
+ content ? SEARCH_CONTENT_FIELDS : SEARCH_FIELDS
410
+ );
400
411
  case "mcp":
401
- return formatMcpOutput(paged);
412
+ return formatMcpOutput(paged, content);
402
413
  }
403
414
  }
404
415
 
@@ -415,6 +426,93 @@ var RECFILE_COMMAND_FIELD_MAP = {
415
426
  rebuild: REBUILD_FIELDS
416
427
  };
417
428
 
429
+ // src/shared/cli/functions/format-command-text.function.ts
430
+ function str(v) {
431
+ if (typeof v === "string") return v;
432
+ if (typeof v === "number") return String(v);
433
+ if (typeof v === "boolean") return String(v);
434
+ if (v == null) return "";
435
+ return JSON.stringify(v);
436
+ }
437
+ function formatCommandText(data, command) {
438
+ const obj = data;
439
+ switch (command) {
440
+ case "status":
441
+ return formatStatus(obj);
442
+ case "check":
443
+ return formatCheck(obj);
444
+ case "gc":
445
+ return formatGc(obj);
446
+ case "rebuild":
447
+ return formatRebuild(obj);
448
+ case "unlearn":
449
+ return formatUnlearn(obj);
450
+ case "learn":
451
+ return formatLearn(data);
452
+ default:
453
+ return JSON.stringify(data, null, 2);
454
+ }
455
+ }
456
+ function formatStatus(o) {
457
+ const cs = o["cacheStats"];
458
+ const path = o["context_path"] ?? o["contextPath"] ?? "(unknown)";
459
+ const lines = [
460
+ `Initialized: ${str(o["initialized"] ?? false)}`,
461
+ `Path: ${str(path)}`
462
+ ];
463
+ if (o["document_count"] != null)
464
+ lines.push(`Documents: ${str(o["document_count"])}`);
465
+ if (o["section_count"] != null)
466
+ lines.push(`Sections: ${str(o["section_count"])}`);
467
+ if (cs) {
468
+ lines.push(
469
+ `Content cached: ${str(cs["contentCount"] ?? 0)}`,
470
+ `Index cached: ${str(cs["indexCount"] ?? 0)}`
471
+ );
472
+ }
473
+ return lines.join("\n");
474
+ }
475
+ function formatCheck(o) {
476
+ const ok = o["ok"] ? "OK" : "FAILED";
477
+ 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)`;
478
+ }
479
+ function formatGc(o) {
480
+ return `GC: removed ${str(o["removed_sections"] ?? 0)} sections, reclaimed ${str(o["removed_bytes"] ?? 0)} bytes (${str(o["elapsed_ms"] ?? 0)}ms)`;
481
+ }
482
+ function formatRebuild(o) {
483
+ return `Rebuild: ${str(o["sections_reindexed"] ?? 0)} sections reindexed, ${str(o["terms_indexed"] ?? 0)} terms (${str(o["elapsed_ms"] ?? 0)}ms)`;
484
+ }
485
+ function formatUnlearn(o) {
486
+ const removed = o["removed"];
487
+ if (Array.isArray(removed)) {
488
+ return `Removed: ${removed.length.toString()} section(s)`;
489
+ }
490
+ return `Removed: ${str(removed ?? false)} (${str(o["elapsed_ms"] ?? 0)}ms)`;
491
+ }
492
+ function formatLearn(data) {
493
+ const obj = data;
494
+ const items = obj["items"];
495
+ if (Array.isArray(items)) {
496
+ return items.map((id) => `Learned: ${str(id)}`).join("\n");
497
+ }
498
+ return JSON.stringify(data, null, 2);
499
+ }
500
+
501
+ // src/shared/cli/functions/wrap-mcp-envelope.function.ts
502
+ function wrapMcpEnvelope(data, command) {
503
+ const text = formatCommandText(data, command);
504
+ return JSON.stringify(
505
+ {
506
+ jsonrpc: "2.0",
507
+ result: {
508
+ content: [{ type: "text", text }]
509
+ }
510
+ },
511
+ null,
512
+ 2
513
+ );
514
+ }
515
+
418
516
  // src/shared/cli/functions/format-command-output.function.ts
419
517
  function formatCommandOutput(data, format, command) {
420
518
  switch (format) {
@@ -423,9 +521,11 @@ function formatCommandOutput(data, format, command) {
423
521
  return fields ? formatRecfileOutput(data, fields) : JSON.stringify(data, null, 2);
424
522
  }
425
523
  case "json":
524
+ return JSON.stringify(data, null, 2);
426
525
  case "text":
526
+ return formatCommandText(data, command);
427
527
  case "mcp":
428
- return JSON.stringify(data, null, 2);
528
+ return wrapMcpEnvelope(data, command);
429
529
  }
430
530
  }
431
531
 
@@ -516,7 +616,7 @@ async function runSearch(client, options) {
516
616
  return {
517
617
  items: results.items.map((r) => ({
518
618
  kbid: r.kbid,
519
- heading: r.heading,
619
+ heading: r.heading || "(untitled)",
520
620
  type: r.type,
521
621
  docids: r.docids,
522
622
  score: r.score,
@@ -542,6 +642,11 @@ async function runSearch(client, options) {
542
642
  var SECTION_TYPES = ["text", "code", "image"];
543
643
  async function resolveKbidPrefix(prefix, client) {
544
644
  if (prefix.length >= 26) return prefix;
645
+ if (prefix.length < 2) {
646
+ const msg = `prefix "${prefix}" is too short (minimum 2 characters)`;
647
+ log(msg, 3 /* ERROR */);
648
+ throw new Error(msg);
649
+ }
545
650
  const seen = /* @__PURE__ */ new Set();
546
651
  for (const type of SECTION_TYPES) {
547
652
  const records = await client.listByType(type);
@@ -550,7 +655,7 @@ async function resolveKbidPrefix(prefix, client) {
550
655
  }
551
656
  }
552
657
  if (seen.size === 0) {
553
- const msg = `no section matches prefix "${prefix}"`;
658
+ const msg = `no section matches prefix "${prefix}". Try "kbdb content --search <query>" to find by keyword`;
554
659
  log(msg, 3 /* ERROR */);
555
660
  throw new Error(msg);
556
661
  }
@@ -664,7 +769,9 @@ async function runLearn(client, options, io) {
664
769
  replace: options.replace,
665
770
  level: options.level
666
771
  });
667
- results.push(mapAddSectionResult(result, "-", content.length));
772
+ results.push(
773
+ mapAddSectionResult(result, "-", content.length, "text")
774
+ );
668
775
  continue;
669
776
  }
670
777
  const files = collectFiles(target);
@@ -682,7 +789,7 @@ async function runLearn(client, options, io) {
682
789
  level: options.level ?? computeDirectoryLevel(file, target)
683
790
  });
684
791
  results.push(
685
- mapAddSectionResult(result, file, content.length)
792
+ mapAddSectionResult(result, file, content.length, "text")
686
793
  );
687
794
  }
688
795
  }
@@ -692,9 +799,11 @@ async function runLearn(client, options, io) {
692
799
  );
693
800
  return results;
694
801
  }
695
- function mapAddSectionResult(result, path, size) {
802
+ function mapAddSectionResult(result, path, size, sectionType) {
696
803
  return {
697
- kbid: result.kbid,
804
+ kbid: result.items[0] ?? "",
805
+ indexed_terms_count: 0,
806
+ type: sectionType,
698
807
  path,
699
808
  size,
700
809
  skipped: false,
@@ -731,11 +840,26 @@ async function runUnlearn(client, options) {
731
840
  // src/shared/cli/functions/run-content.function.ts
732
841
  async function runContent(client, options) {
733
842
  log("running content", 0 /* DEBUG */);
843
+ if (options.search) {
844
+ return runContentSearch(client, options);
845
+ }
734
846
  const resolved = await Promise.all(
735
847
  options.args.map((a) => resolveKbidPrefix(a, client))
736
848
  );
737
849
  return client.content(resolved);
738
850
  }
851
+ async function runContentSearch(client, options) {
852
+ const query = options.args.join(" ");
853
+ const result = await client.search({
854
+ query,
855
+ limit: options.limit
856
+ });
857
+ const kbids = result.items.map((hit) => hit.kbid);
858
+ if (kbids.length === 0) {
859
+ return { type: "text/markdown", data: "", total: 0 };
860
+ }
861
+ return client.content(kbids);
862
+ }
739
863
 
740
864
  // src/shared/cli/functions/run-check.function.ts
741
865
  function parseDivergentContent(encoded) {
@@ -750,14 +874,21 @@ function parseDivergentContent(encoded) {
750
874
  async function runCheck(client, _options) {
751
875
  log("running check", 0 /* DEBUG */);
752
876
  const raw = await client.integrityCheck();
877
+ const divergent = parseDivergentContent(raw.divergent_content);
878
+ const divergentErrors = divergent.map((d) => ({
879
+ type: "divergent_content",
880
+ entity: d.sourceKey,
881
+ detail: `${d.kbids.length.toString()} divergent sections: ${d.kbids.join(", ")}`
882
+ }));
883
+ const errors = [...raw.errors, ...divergentErrors];
753
884
  return {
754
- ok: raw.ok,
755
- errors: raw.errors,
885
+ ok: raw.ok && errors.length === 0,
886
+ errors,
756
887
  sections_checked: raw.sections_checked,
757
888
  documents_checked: raw.documents_checked,
758
889
  references_checked: raw.references_checked,
759
890
  elapsed_ms: raw.elapsed_ms,
760
- divergent_content: parseDivergentContent(raw.divergent_content)
891
+ divergent_content: divergent
761
892
  };
762
893
  }
763
894
 
@@ -1055,7 +1186,15 @@ async function runSkillLearn(client, options) {
1055
1186
  description: options.description
1056
1187
  });
1057
1188
  return {
1058
- output: JSON.stringify(result, null, 2),
1189
+ output: JSON.stringify(
1190
+ {
1191
+ kbid: result.items[0],
1192
+ name,
1193
+ arguments: args.length > 0 ? args : []
1194
+ },
1195
+ null,
1196
+ 2
1197
+ ),
1059
1198
  exitCode: 0
1060
1199
  };
1061
1200
  }
@@ -1116,10 +1255,12 @@ async function runSkillGet(client, options) {
1116
1255
  };
1117
1256
  }
1118
1257
  let content;
1119
- if (/^[0-9a-f]{16,}$/i.test(identifier)) {
1258
+ let kbid;
1259
+ if (/^[a-z2-7]{26}$/.test(identifier)) {
1120
1260
  const sections = await client.readSections([identifier]);
1121
1261
  if (sections.length > 0) {
1122
1262
  content = sections[0]?.content;
1263
+ kbid = identifier;
1123
1264
  }
1124
1265
  }
1125
1266
  if (content === void 0) {
@@ -1127,9 +1268,10 @@ async function runSkillGet(client, options) {
1127
1268
  const match = records.filter((r) => r.title === identifier).pop();
1128
1269
  if (match) {
1129
1270
  content = match.content;
1271
+ kbid = match.kbid;
1130
1272
  }
1131
1273
  }
1132
- if (content === void 0) {
1274
+ if (content === void 0 || kbid === void 0) {
1133
1275
  return {
1134
1276
  error: `skill not found: ${identifier}`,
1135
1277
  exitCode: 1
@@ -1137,7 +1279,7 @@ async function runSkillGet(client, options) {
1137
1279
  }
1138
1280
  const skill = parseSkillContent(content);
1139
1281
  return {
1140
- output: JSON.stringify(skill, null, 2),
1282
+ output: JSON.stringify({ kbid, ...skill }, null, 2),
1141
1283
  exitCode: 0
1142
1284
  };
1143
1285
  }
@@ -1152,7 +1294,7 @@ async function runSkillDelete(client, options) {
1152
1294
  const result = await client.removeSection(kbid);
1153
1295
  return {
1154
1296
  output: JSON.stringify(
1155
- { kbid, removed: result.removed },
1297
+ { removed: result.removed ? [kbid] : [] },
1156
1298
  null,
1157
1299
  2
1158
1300
  ),
@@ -1194,7 +1336,7 @@ async function runAgentCreate(client, options) {
1194
1336
  title: name,
1195
1337
  description: options.description
1196
1338
  });
1197
- const allKbids = [metaResult.kbid, ...skills];
1339
+ const allKbids = [metaResult.items[0], ...skills];
1198
1340
  const docid = await client.groupSections(name, allKbids, "agent");
1199
1341
  return {
1200
1342
  output: JSON.stringify(
@@ -1294,11 +1436,33 @@ async function runAgentDelete(client, options) {
1294
1436
  }
1295
1437
  await client.removeGrouping(docid);
1296
1438
  return {
1297
- output: JSON.stringify({ docid, removed: true }, null, 2),
1439
+ output: JSON.stringify({ removed: [docid] }, null, 2),
1298
1440
  exitCode: 0
1299
1441
  };
1300
1442
  }
1301
1443
 
1444
+ // src/shared/cli/functions/run-skill-search.function.ts
1445
+ async function runSkillSearch(client, options) {
1446
+ const paged = await runSearch(client, options);
1447
+ const filtered = paged.items.filter((item) => item.type === "skill");
1448
+ return {
1449
+ ...paged,
1450
+ items: filtered,
1451
+ total: filtered.length
1452
+ };
1453
+ }
1454
+
1455
+ // src/shared/cli/functions/run-agent-search.function.ts
1456
+ async function runAgentSearch(client, options) {
1457
+ const paged = await runSearch(client, options);
1458
+ const filtered = paged.items.filter((item) => item.type === "agent");
1459
+ return {
1460
+ ...paged,
1461
+ items: filtered,
1462
+ total: filtered.length
1463
+ };
1464
+ }
1465
+
1302
1466
  // src/shared/cli/functions/run-import.function.ts
1303
1467
  var import_node_path6 = require("node:path");
1304
1468
  async function runImport(client, options) {
@@ -1389,6 +1553,10 @@ async function dispatchCommand(client, options, io) {
1389
1553
  const result = await runAgentDelete(client, options);
1390
1554
  return result;
1391
1555
  }
1556
+ case "skill-search":
1557
+ return handleTypedSearch(client, options, "skill");
1558
+ case "agent-search":
1559
+ return handleTypedSearch(client, options, "agent");
1392
1560
  default:
1393
1561
  log("unknown command: " + options.command, 2 /* WARNING */);
1394
1562
  return {
@@ -1400,8 +1568,33 @@ async function dispatchCommand(client, options, io) {
1400
1568
  async function handleLearn(client, options, io) {
1401
1569
  try {
1402
1570
  const results = await runLearn(client, options, io);
1571
+ const items = results.map((r) => r.kbid);
1572
+ const base = {
1573
+ items,
1574
+ total: results.length,
1575
+ offset: 0
1576
+ };
1577
+ if (results.length === 0) {
1578
+ const empty = {
1579
+ ...base,
1580
+ superseded: null,
1581
+ warnings: [],
1582
+ near_duplicates: []
1583
+ };
1584
+ return {
1585
+ output: formatCommandOutput(empty, options.format, "learn"),
1586
+ exitCode: 0
1587
+ };
1588
+ }
1589
+ const last = results[results.length - 1];
1590
+ const data = {
1591
+ ...base,
1592
+ superseded: last.superseded,
1593
+ warnings: last.warnings,
1594
+ near_duplicates: last.near_duplicates
1595
+ };
1403
1596
  return {
1404
- output: formatCommandOutput(results, options.format, "learn"),
1597
+ output: formatCommandOutput(data, options.format, "learn"),
1405
1598
  exitCode: 0
1406
1599
  };
1407
1600
  } catch (err) {
@@ -1414,9 +1607,10 @@ async function handleLearn(client, options, io) {
1414
1607
  async function handleUnlearn(client, options) {
1415
1608
  try {
1416
1609
  const results = await runUnlearn(client, options);
1610
+ const removed = results.filter((r) => r.removed).map((r) => r.kbid);
1417
1611
  return {
1418
1612
  output: formatCommandOutput(
1419
- results,
1613
+ { removed },
1420
1614
  options.format,
1421
1615
  "unlearn"
1422
1616
  ),
@@ -1436,7 +1630,7 @@ async function handleSearch(client, options) {
1436
1630
  await hydrateSearchPreviews(client, results);
1437
1631
  }
1438
1632
  return {
1439
- output: formatOutput(results, options.format),
1633
+ output: formatOutput(results, options.format, options.content),
1440
1634
  exitCode: 0
1441
1635
  };
1442
1636
  } catch (err) {
@@ -1497,11 +1691,38 @@ async function handleImport(client, options) {
1497
1691
  async function handleContent(client, options) {
1498
1692
  try {
1499
1693
  const result = await runContent(client, options);
1500
- if (options.format === "rec") {
1694
+ if (options.format === "text") {
1501
1695
  return { output: result.data, exitCode: 0 };
1502
1696
  }
1697
+ if (options.format === "rec") {
1698
+ const header = `type: ${result.type}
1699
+ total: ${String(result.total)}
1700
+
1701
+ `;
1702
+ return {
1703
+ output: header + result.data,
1704
+ exitCode: 0
1705
+ };
1706
+ }
1707
+ if (options.format === "mcp") {
1708
+ const text = result.data || "(empty)";
1709
+ return {
1710
+ output: JSON.stringify(
1711
+ {
1712
+ jsonrpc: "2.0",
1713
+ result: {
1714
+ content: [{ type: "text", text }]
1715
+ }
1716
+ },
1717
+ null,
1718
+ 2
1719
+ ),
1720
+ exitCode: 0
1721
+ };
1722
+ }
1723
+ const envelope = !options.search && options.args.length === 1 ? { kbid: options.args[0], ...result } : result;
1503
1724
  return {
1504
- output: JSON.stringify(result, null, 2),
1725
+ output: JSON.stringify(envelope, null, 2),
1505
1726
  exitCode: 0
1506
1727
  };
1507
1728
  } catch (err) {
@@ -1588,9 +1809,24 @@ async function handleDbMigrate(client, options) {
1588
1809
  };
1589
1810
  }
1590
1811
  }
1812
+ async function handleTypedSearch(client, options, type) {
1813
+ try {
1814
+ const runner = type === "skill" ? runSkillSearch : runAgentSearch;
1815
+ const results = await runner(client, options);
1816
+ return {
1817
+ output: formatOutput(results, options.format),
1818
+ exitCode: 0
1819
+ };
1820
+ } catch (err) {
1821
+ return {
1822
+ error: `${type} search failed: ${errorToMessage(err)}`,
1823
+ exitCode: 1
1824
+ };
1825
+ }
1826
+ }
1591
1827
 
1592
1828
  // src/shared/version/constants/version.constant.ts
1593
- var VERSION = "0.6.1";
1829
+ var VERSION = "0.6.3";
1594
1830
 
1595
1831
  // src/shared/cli/functions/generate-help.function.ts
1596
1832
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
@@ -3215,8 +3451,15 @@ function handleInit(options, deps2) {
3215
3451
  exitCode: 0
3216
3452
  };
3217
3453
  }
3454
+ const msg = result.errorMessage ?? "init failed";
3455
+ if (/already exists/i.test(msg)) {
3456
+ return {
3457
+ output: `${result.targetPath} (already initialized)`,
3458
+ exitCode: 0
3459
+ };
3460
+ }
3218
3461
  return {
3219
- error: `error: ${result.errorMessage ?? "init failed"}`,
3462
+ error: `error: ${msg}`,
3220
3463
  exitCode: 1
3221
3464
  };
3222
3465
  }
@@ -3476,7 +3719,7 @@ function createToolDefinitions() {
3476
3719
  properties: {
3477
3720
  content: {
3478
3721
  type: "string",
3479
- description: "Text content to add"
3722
+ description: "Raw text or Markdown content to index. Do not wrap in JSON."
3480
3723
  },
3481
3724
  type: {
3482
3725
  type: "string",
@@ -3706,7 +3949,7 @@ function createToolDefinitions() {
3706
3949
  },
3707
3950
  body: {
3708
3951
  type: "string",
3709
- description: "Skill body template. Use {{argName}} for placeholders."
3952
+ description: "Skill body template as raw text. Use {{argName}} for placeholders. Do not wrap in JSON."
3710
3953
  }
3711
3954
  },
3712
3955
  required: ["name", "body"]
@@ -3795,7 +4038,7 @@ function createToolDefinitions() {
3795
4038
  },
3796
4039
  persona: {
3797
4040
  type: "string",
3798
- description: "Agent persona prompt"
4041
+ description: "Agent persona as raw text. Do not wrap in JSON."
3799
4042
  },
3800
4043
  skills: {
3801
4044
  type: "array",
@@ -3870,6 +4113,56 @@ function createToolDefinitions() {
3870
4113
  openWorldHint: false
3871
4114
  }
3872
4115
  },
4116
+ {
4117
+ name: "skill-search",
4118
+ description: "Search for skills by keyword. Returns ranked skill definitions matching the query.",
4119
+ inputSchema: {
4120
+ type: "object",
4121
+ properties: {
4122
+ query: {
4123
+ type: "string",
4124
+ description: "Search query text"
4125
+ },
4126
+ limit: {
4127
+ type: "number",
4128
+ description: "Maximum results (default: 20)"
4129
+ }
4130
+ },
4131
+ required: ["query"]
4132
+ },
4133
+ annotations: {
4134
+ title: "Search Skills",
4135
+ readOnlyHint: true,
4136
+ destructiveHint: false,
4137
+ idempotentHint: true,
4138
+ openWorldHint: false
4139
+ }
4140
+ },
4141
+ {
4142
+ name: "agent-search",
4143
+ description: "Search for agents by keyword. Returns ranked agent profiles matching the query.",
4144
+ inputSchema: {
4145
+ type: "object",
4146
+ properties: {
4147
+ query: {
4148
+ type: "string",
4149
+ description: "Search query text"
4150
+ },
4151
+ limit: {
4152
+ type: "number",
4153
+ description: "Maximum results (default: 20)"
4154
+ }
4155
+ },
4156
+ required: ["query"]
4157
+ },
4158
+ annotations: {
4159
+ title: "Search Agents",
4160
+ readOnlyHint: true,
4161
+ destructiveHint: false,
4162
+ idempotentHint: true,
4163
+ openWorldHint: false
4164
+ }
4165
+ },
3873
4166
  {
3874
4167
  name: "auto-capture-review",
3875
4168
  description: "Review and approve pending auto-captured knowledge before storage.",
@@ -3912,6 +4205,33 @@ function createResourceTemplates() {
3912
4205
  ];
3913
4206
  }
3914
4207
 
4208
+ // src/shared/mcp/functions/format-search-results.function.ts
4209
+ function formatSearchResults(query, results) {
4210
+ if (results.items.length === 0) {
4211
+ return `No results found for "${query}"`;
4212
+ }
4213
+ const lines = results.items.map(
4214
+ (item, index) => formatItem(item, results.offset + index + 1)
4215
+ );
4216
+ const start = results.offset + 1;
4217
+ const end = results.offset + results.items.length;
4218
+ const total = String(results.total);
4219
+ return [
4220
+ `Found ${total} results for "${query}"`,
4221
+ "",
4222
+ ...lines,
4223
+ "",
4224
+ `Showing ${String(start)}-${String(end)} of ${total} results`
4225
+ ].join("\n");
4226
+ }
4227
+ function formatItem(item, position) {
4228
+ const heading = item.heading ?? "(untitled)";
4229
+ const terms = item.matched_terms.join(", ");
4230
+ return `${String(position)}. [${item.kbid}] ${heading}
4231
+ ${item.snippet}
4232
+ type: ${item.type} terms: ${terms}`;
4233
+ }
4234
+
3915
4235
  // src/shared/mcp/functions/handle-agent-create.function.ts
3916
4236
  function asString(value, fallback) {
3917
4237
  return typeof value === "string" ? value : fallback;
@@ -3962,35 +4282,52 @@ async function handleAgentCreate(client, args) {
3962
4282
  title: name,
3963
4283
  description
3964
4284
  });
3965
- const allKbids = [metaResult.kbid, ...skills];
4285
+ const allKbids = [metaResult.items[0], ...skills];
3966
4286
  const docid = await client.groupSections(name, allKbids, "agent");
3967
4287
  return {
3968
4288
  content: [
3969
4289
  {
3970
4290
  type: "text",
3971
- text: JSON.stringify({
3972
- docid,
3973
- name,
3974
- skillCount: skills.length
3975
- })
4291
+ text: `Created agent "${name}" (docid: ${docid})
4292
+ Skills: ${String(skills.length)} attached`
3976
4293
  }
3977
4294
  ]
3978
4295
  };
3979
4296
  }
3980
4297
 
3981
4298
  // src/shared/mcp/functions/handle-agent-list.function.ts
4299
+ async function readAgentDescription(client, sections) {
4300
+ const metaKbid = sections[0]?.kbid;
4301
+ if (!metaKbid) return "";
4302
+ const metaSections = await client.readSections([metaKbid]);
4303
+ const first = metaSections.at(0);
4304
+ if (!first) return "";
4305
+ try {
4306
+ const meta = JSON.parse(first.content);
4307
+ return typeof meta["description"] === "string" ? meta["description"] : "";
4308
+ } catch {
4309
+ return "";
4310
+ }
4311
+ }
3982
4312
  async function handleAgentList(client) {
3983
4313
  const docs = await client.listDocumentsByType("agent");
3984
- const items = docs.map((d) => ({
3985
- docid: d.docid,
3986
- name: d.title,
3987
- skillCount: Math.max(0, d.sections.length - 1)
3988
- }));
4314
+ const items = await Promise.all(
4315
+ docs.map(async (d) => ({
4316
+ name: d.title,
4317
+ description: await readAgentDescription(client, d.sections)
4318
+ }))
4319
+ );
4320
+ const lines = items.map(
4321
+ (item, index) => `${String(index + 1)}. ${item.name}` + (item.description ? ` -- ${item.description}` : "")
4322
+ );
4323
+ const text = [`${String(items.length)} agents:`, ...lines].join(
4324
+ "\n"
4325
+ );
3989
4326
  return {
3990
4327
  content: [
3991
4328
  {
3992
4329
  type: "text",
3993
- text: JSON.stringify(items, null, 2)
4330
+ text
3994
4331
  }
3995
4332
  ]
3996
4333
  };
@@ -4039,6 +4376,19 @@ async function resolveSkillRefs(client, skillRefs) {
4039
4376
  }
4040
4377
  });
4041
4378
  }
4379
+ function formatAgent(agent) {
4380
+ const skillLines = agent.skills.length > 0 ? agent.skills.map(
4381
+ (s, index) => `${String(index + 1)}. ${s.name ?? `(missing: ${s.kbid})`}`
4382
+ ).join("\n") : "none";
4383
+ return [
4384
+ `Agent: ${agent.name} (docid: ${agent.docid})`,
4385
+ `Description: ${agent.description}`,
4386
+ `Persona: ${agent.persona}`,
4387
+ "",
4388
+ "Skills:",
4389
+ skillLines
4390
+ ].join("\n");
4391
+ }
4042
4392
  async function handleAgentGet(client, args) {
4043
4393
  const docid = typeof args["docid"] === "string" ? args["docid"] : "";
4044
4394
  if (!docid) {
@@ -4063,7 +4413,7 @@ async function handleAgentGet(client, args) {
4063
4413
  content: [
4064
4414
  {
4065
4415
  type: "text",
4066
- text: JSON.stringify(agent, null, 2)
4416
+ text: formatAgent(agent)
4067
4417
  }
4068
4418
  ]
4069
4419
  };
@@ -4083,17 +4433,74 @@ async function handleAgentDelete(client, args) {
4083
4433
  content: [
4084
4434
  {
4085
4435
  type: "text",
4086
- text: JSON.stringify({ docid, removed: true })
4436
+ text: `Deleted agent (docid: ${docid})`
4087
4437
  }
4088
4438
  ]
4089
4439
  };
4090
4440
  }
4091
4441
 
4092
4442
  // src/shared/mcp/functions/handle-recall.function.ts
4443
+ function asRecord(value) {
4444
+ return typeof value === "object" && value !== null ? value : void 0;
4445
+ }
4446
+ function strField(record, key, fallback) {
4447
+ const value = record[key];
4448
+ return typeof value === "string" ? value : fallback;
4449
+ }
4450
+ function formatList(label, items, line) {
4451
+ if (items === void 0 || items === null) {
4452
+ return "";
4453
+ }
4454
+ const list = Array.isArray(items) ? items : [];
4455
+ const rows = list.map((raw) => asRecord(raw)).filter((r) => r !== void 0).map((r) => ` - ${line(r)}`);
4456
+ if (rows.length > 0) {
4457
+ return `
4458
+
4459
+ ${label}:
4460
+ ${rows.join("\n")}`;
4461
+ }
4462
+ return `
4463
+
4464
+ ${label}: (none)`;
4465
+ }
4466
+ function formatRecallItem(raw) {
4467
+ const item = asRecord(raw) ?? {};
4468
+ const kbid = strField(item, "kbid", "?");
4469
+ const heading = strField(item, "heading", "(untitled)");
4470
+ const type = strField(item, "type", "unknown");
4471
+ const content = strField(item, "content", "");
4472
+ const refLine = (r) => `${strField(r, "heading", strField(r, "kbid", "?"))} (${strField(r, "kbid", "?")})`;
4473
+ const text = `${heading} [${type}] (${kbid})
4474
+
4475
+ ${content}` + formatList(
4476
+ "Documents",
4477
+ item["documents"],
4478
+ (d) => `${strField(d, "title", "?")} (${strField(d, "docid", "?")})`
4479
+ ) + formatList("Back references", item["back_references"], refLine) + formatList("Siblings", item["siblings"], refLine) + formatList("References", item["references"], refLine);
4480
+ return text;
4481
+ }
4482
+ function buildNotFoundResult(id, depth) {
4483
+ const base = {
4484
+ kbid: id,
4485
+ heading: "(not found)",
4486
+ type: "unknown",
4487
+ content: "",
4488
+ docids: []
4489
+ };
4490
+ if (depth >= 1) {
4491
+ base["documents"] = [];
4492
+ base["back_references"] = [];
4493
+ }
4494
+ if (depth >= 2) {
4495
+ base["siblings"] = [];
4496
+ base["references"] = [];
4497
+ }
4498
+ return base;
4499
+ }
4093
4500
  async function handleRecall2(client, args) {
4094
4501
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
4095
4502
  const kbids = Array.isArray(args["kbids"]) && args["kbids"].every((item) => typeof item === "string") ? args["kbids"] : void 0;
4096
- if (!kbid && !kbids) {
4503
+ if (!kbid && (!kbids || kbids.length === 0)) {
4097
4504
  return {
4098
4505
  content: [
4099
4506
  {
@@ -4105,17 +4512,63 @@ async function handleRecall2(client, args) {
4105
4512
  };
4106
4513
  }
4107
4514
  const depth = typeof args["depth"] === "number" ? args["depth"] : 0;
4108
- const result = await client.recall({ kbid, kbids, depth });
4515
+ let result;
4516
+ try {
4517
+ result = await client.recall({ kbid, kbids, depth });
4518
+ } catch {
4519
+ const ids = kbid ? [kbid] : kbids ?? [];
4520
+ const items = ids.map((id) => buildNotFoundResult(id, depth));
4521
+ result = items.length === 1 ? items[0] : items;
4522
+ }
4523
+ const text = Array.isArray(result) ? result.map(formatRecallItem).join("\n\n---\n\n") : formatRecallItem(result);
4109
4524
  return {
4110
4525
  content: [
4111
4526
  {
4112
4527
  type: "text",
4113
- text: JSON.stringify(result, null, 2)
4528
+ text
4114
4529
  }
4115
4530
  ]
4116
4531
  };
4117
4532
  }
4118
4533
 
4534
+ // src/shared/mcp/functions/handle-skill-search.function.ts
4535
+ async function handleSkillSearch(client, args) {
4536
+ const query = typeof args["query"] === "string" ? args["query"] : "";
4537
+ const results = await client.search({
4538
+ query,
4539
+ limit: args["limit"]
4540
+ });
4541
+ const filtered = {
4542
+ ...results,
4543
+ items: results.items.filter(
4544
+ (item) => item.type === "skill"
4545
+ ),
4546
+ total: 0
4547
+ };
4548
+ filtered.total = filtered.items.length;
4549
+ const text = formatSearchResults(query, filtered);
4550
+ return { content: [{ type: "text", text }] };
4551
+ }
4552
+
4553
+ // src/shared/mcp/functions/handle-agent-search.function.ts
4554
+ async function handleAgentSearch(client, args) {
4555
+ const query = typeof args["query"] === "string" ? args["query"] : "";
4556
+ const results = await client.search({
4557
+ query,
4558
+ limit: args["limit"]
4559
+ });
4560
+ const filtered = {
4561
+ ...results,
4562
+ items: results.items.filter(
4563
+ (item) => item.type === "agent"
4564
+ ),
4565
+ total: 0
4566
+ };
4567
+ filtered.total = filtered.items.length;
4568
+ const text = formatSearchResults(query, filtered);
4569
+ return { content: [{ type: "text", text }] };
4570
+ }
4571
+
4119
4572
  // src/shared/mcp/functions/handle-tool-call.function.ts
4120
4573
  function asString2(value, fallback) {
4121
4574
  return typeof value === "string" ? value : fallback;
@@ -4135,6 +4588,18 @@ function asStringArray2(value) {
4135
4588
  }
4136
4589
  return [];
4137
4590
  }
4591
+ function unwrapJsonContent(raw) {
4592
+ const trimmed = raw.trim();
4593
+ if (!trimmed.startsWith("{")) return raw;
4594
+ try {
4595
+ const parsed = JSON.parse(trimmed);
4596
+ if (typeof parsed["content"] === "string")
4597
+ return parsed["content"];
4598
+ if (typeof parsed["text"] === "string") return parsed["text"];
4599
+ } catch {
4600
+ }
4601
+ return raw;
4602
+ }
4138
4603
  async function handleToolCall(client, name, args) {
4139
4604
  log("tool call: " + name, 0 /* DEBUG */);
4140
4605
  try {
@@ -4177,6 +4642,10 @@ async function handleToolCall(client, name, args) {
4177
4642
  return await handleAgentGet(client, args);
4178
4643
  case "agent-delete":
4179
4644
  return await handleAgentDelete(client, args);
4645
+ case "skill-search":
4646
+ return await handleSkillSearch(client, args);
4647
+ case "agent-search":
4648
+ return await handleAgentSearch(client, args);
4180
4649
  case "auto-capture-review":
4181
4650
  return handleAutoCaptureReview();
4182
4651
  default:
@@ -4208,30 +4677,18 @@ async function handleToolCall(client, name, args) {
4208
4677
  }
4209
4678
  }
4210
4679
  async function handleSearch2(client, args) {
4680
+ const query = asString2(args["query"], "");
4211
4681
  const results = await client.search({
4212
- query: asString2(args["query"], ""),
4682
+ query,
4213
4683
  mode: args["mode"],
4214
4684
  limit: args["limit"],
4215
4685
  offset: args["offset"],
4216
4686
  algo: args["algo"],
4217
4687
  relaxed: args["relaxed"]
4218
4688
  });
4219
- const stripped = {
4220
- ...results,
4221
- items: results.items.map(
4222
- ({
4223
- score: _score,
4224
- confidence: _confidence,
4225
- ...rest
4226
- }) => rest
4227
- )
4228
- };
4229
4689
  return {
4230
4690
  content: [
4231
- {
4232
- type: "text",
4233
- text: JSON.stringify(stripped, null, 2)
4234
- }
4691
+ { type: "text", text: formatSearchResults(query, results) }
4235
4692
  ]
4236
4693
  };
4237
4694
  }
@@ -4253,70 +4710,47 @@ async function handleLearn2(client, args) {
4253
4710
  isError: true
4254
4711
  };
4255
4712
  }
4713
+ const rawContent = asString2(args["content"], "");
4714
+ const content = unwrapJsonContent(rawContent);
4256
4715
  const result = await client.addSection({
4257
4716
  sectionType,
4258
- content: asString2(args["content"], ""),
4717
+ content,
4259
4718
  title,
4260
4719
  description,
4261
4720
  docid,
4262
4721
  tags,
4263
4722
  replace
4264
4723
  });
4265
- const normalized = {
4266
- kbid: result.kbid,
4267
- indexed_terms_count: result.indexed_terms_count ?? 0,
4268
- elapsed_ms: result.elapsed_ms ?? 0,
4724
+ const payload = {
4725
+ items: result.items,
4726
+ total: result.total,
4727
+ offset: result.offset,
4269
4728
  superseded: result.superseded ?? null,
4270
4729
  warnings: result.warnings ?? [],
4271
- near_duplicates: (result.near_duplicates ?? []).map((d) => ({
4272
- kbid: d.kbid,
4273
- similarity: d.similarity,
4274
- ...d.method !== void 0 ? { method: d.method } : {}
4275
- }))
4730
+ near_duplicates: result.near_duplicates ?? []
4276
4731
  };
4277
4732
  return {
4278
- content: [
4279
- {
4280
- type: "text",
4281
- text: JSON.stringify(normalized)
4282
- }
4283
- ]
4733
+ content: [{ type: "text", text: JSON.stringify(payload) }]
4284
4734
  };
4285
4735
  }
4286
4736
  async function handleUnlearn2(client, args) {
4287
- const result = await client.removeSection(
4288
- asString2(args["kbid"], "")
4289
- );
4737
+ const kbid = asString2(args["kbid"], "");
4738
+ await client.removeSection(kbid);
4290
4739
  return {
4291
- content: [
4292
- {
4293
- type: "text",
4294
- text: JSON.stringify(result)
4295
- }
4296
- ]
4740
+ content: [{ type: "text", text: `Removed section ${kbid}` }]
4297
4741
  };
4298
4742
  }
4299
4743
  async function handleRetrieve(client, args) {
4300
4744
  if (typeof args["kbid"] === "string") {
4301
4745
  const sections = await client.readSections([args["kbid"]]);
4302
4746
  return {
4303
- content: [
4304
- {
4305
- type: "text",
4306
- text: JSON.stringify(sections, null, 2)
4307
- }
4308
- ]
4747
+ content: [{ type: "text", text: formatSections(sections) }]
4309
4748
  };
4310
4749
  }
4311
4750
  if (typeof args["docid"] === "string") {
4312
4751
  const doc = await client.retrieveDocument(args["docid"]);
4313
4752
  return {
4314
- content: [
4315
- {
4316
- type: "text",
4317
- text: JSON.stringify(doc, null, 2)
4318
- }
4319
- ]
4753
+ content: [{ type: "text", text: formatDocument(doc) }]
4320
4754
  };
4321
4755
  }
4322
4756
  return {
@@ -4329,61 +4763,81 @@ async function handleRetrieve(client, args) {
4329
4763
  isError: true
4330
4764
  };
4331
4765
  }
4766
+ function formatSections(sections) {
4767
+ if (sections.length === 0) {
4768
+ return "No sections found";
4769
+ }
4770
+ return sections.map((s) => `${s.kbid} (${s.sectionType})
4771
+ ${s.content}`).join("\n\n");
4772
+ }
4773
+ function formatDocument(doc) {
4774
+ if (typeof doc !== "object" || doc === null) {
4775
+ return String(doc);
4776
+ }
4777
+ return Object.entries(doc).map(([key, value]) => {
4778
+ if (Array.isArray(value)) {
4779
+ const items = value.map((item) => formatDocValue(item)).map((s) => ` - ${s}`);
4780
+ return items.length > 0 ? `${key}:
4781
+ ${items.join("\n")}` : `${key}: (none)`;
4782
+ }
4783
+ return `${key}: ${formatDocValue(value)}`;
4784
+ }).join("\n");
4785
+ }
4786
+ function formatDocValue(value) {
4787
+ if (typeof value !== "object" || value === null) {
4788
+ return String(value);
4789
+ }
4790
+ const rec = value;
4791
+ return Object.entries(rec).map(([k, v]) => `${k}=${String(v)}`).join(", ");
4792
+ }
4332
4793
  async function handleStatus2(client) {
4333
4794
  const status = await client.dbStatus();
4334
- return {
4335
- content: [
4336
- {
4337
- type: "text",
4338
- text: JSON.stringify(status, null, 2)
4339
- }
4340
- ]
4341
- };
4795
+ const cs = status.cacheStats;
4796
+ const lines = [
4797
+ `Database: ${status.contextPath}`,
4798
+ `Initialized: ${status.initialized ? "yes" : "no"}`
4799
+ ];
4800
+ if (cs) {
4801
+ lines.push(
4802
+ `Cached content entries: ${String(cs.contentCount)}`,
4803
+ `Cached index entries: ${String(cs.indexCount)}`
4804
+ );
4805
+ }
4806
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4342
4807
  }
4343
4808
  async function handleContent2(client, args) {
4344
4809
  const ids = asStringArray2(args["ids"]);
4345
4810
  const result = await client.content(ids);
4811
+ const text = result.data || "(no content found)";
4346
4812
  return {
4347
- content: [
4348
- {
4349
- type: "text",
4350
- text: JSON.stringify(result)
4351
- }
4352
- ]
4813
+ content: [{ type: "text", text }]
4353
4814
  };
4354
4815
  }
4355
4816
  async function handleCheck2(client) {
4356
4817
  const report = await client.integrityCheck();
4357
- return {
4358
- content: [
4359
- {
4360
- type: "text",
4361
- text: JSON.stringify(report, null, 2)
4362
- }
4363
- ]
4364
- };
4818
+ const status = report.ok ? "OK" : "FAIL";
4819
+ const lines = [
4820
+ `Integrity check: ${status}. ${String(report.sections_checked)} sections checked.`
4821
+ ];
4822
+ for (const error of report.errors) {
4823
+ lines.push(`- ${error.type}: ${error.entity} (${error.detail})`);
4824
+ }
4825
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4365
4826
  }
4366
4827
  async function handleGc2(client) {
4367
4828
  const report = await client.gc();
4368
- return {
4369
- content: [
4370
- {
4371
- type: "text",
4372
- text: JSON.stringify(report, null, 2)
4373
- }
4374
- ]
4375
- };
4829
+ const lines = [
4830
+ `GC complete: removed ${String(report.removed_sections)} sections, freed ${String(report.removed_bytes)} bytes.`
4831
+ ];
4832
+ if (report.stale_sources !== void 0) {
4833
+ lines.push(`stale sources: ${String(report.stale_sources)}`);
4834
+ }
4835
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4376
4836
  }
4377
4837
  async function handleRebuild2(client) {
4378
4838
  const report = await client.rebuildIndexes();
4379
- return {
4380
- content: [
4381
- {
4382
- type: "text",
4383
- text: JSON.stringify(report, null, 2)
4384
- }
4385
- ]
4386
- };
4839
+ const text = `Rebuild complete: ${String(report.sections_reindexed)} sections reindexed, ${String(report.terms_indexed)} terms indexed.`;
4840
+ return { content: [{ type: "text", text }] };
4387
4841
  }
4388
4842
  async function handleSkillLearn(client, args) {
4389
4843
  const name = asString2(args["name"], "");
@@ -4419,37 +4873,22 @@ async function handleSkillLearn(client, args) {
4419
4873
  content: [
4420
4874
  {
4421
4875
  type: "text",
4422
- text: JSON.stringify({ kbid: result.kbid, name })
4876
+ text: `Created skill "${name}" (kbid: ${result.items[0]})`
4423
4877
  }
4424
4878
  ]
4425
4879
  };
4426
4880
  }
4427
4881
  async function handleSkillList(client) {
4428
4882
  const records = await client.listByType("skill");
4429
- const items = records.map((r) => {
4430
- let argumentCount = 0;
4431
- try {
4432
- const parsed = JSON.parse(r.content);
4433
- if (Array.isArray(parsed["arguments"])) {
4434
- argumentCount = parsed["arguments"].length;
4435
- }
4436
- } catch {
4437
- }
4438
- return {
4439
- kbid: r.kbid,
4440
- name: r.title,
4441
- description: r.description,
4442
- argumentCount
4443
- };
4883
+ const lines = records.map((r, index) => {
4884
+ const desc = r.description ? ` -- ${r.description}` : "";
4885
+ const label = r.title ?? "(untitled)";
4886
+ return `${String(index + 1)}. ${label} (${r.kbid})${desc}`;
4444
4887
  });
4445
- return {
4446
- content: [
4447
- {
4448
- type: "text",
4449
- text: JSON.stringify(items, null, 2)
4450
- }
4451
- ]
4452
- };
4888
+ const text = [`${String(records.length)} skills:`, ...lines].join(
4889
+ "\n"
4890
+ );
4891
+ return { content: [{ type: "text", text }] };
4453
4892
  }
4454
4893
  async function handleSkillGet(client, args) {
4455
4894
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
@@ -4493,14 +4932,29 @@ async function handleSkillGet(client, args) {
4493
4932
  }
4494
4933
  const skill = parseSkillContent(content);
4495
4934
  return {
4496
- content: [
4497
- {
4498
- type: "text",
4499
- text: JSON.stringify(skill, null, 2)
4500
- }
4501
- ]
4935
+ content: [{ type: "text", text: formatSkill(skill) }]
4502
4936
  };
4503
4937
  }
4938
+ function formatSkill(skill) {
4939
+ const lines = [`name: ${skill.name}`];
4940
+ if (skill.description) {
4941
+ lines.push(`description: ${skill.description}`);
4942
+ }
4943
+ if (skill.arguments && skill.arguments.length > 0) {
4944
+ lines.push("arguments:");
4945
+ for (const arg of skill.arguments) {
4946
+ const details = [
4947
+ ...arg.type ? [arg.type] : [],
4948
+ ...arg.required ? ["required"] : []
4949
+ ];
4950
+ lines.push(
4951
+ details.length > 0 ? `- ${arg.name}: ${details.join(", ")}` : `- ${arg.name}`
4952
+ );
4953
+ }
4954
+ }
4955
+ lines.push("body:", skill.body);
4956
+ return lines.join("\n");
4957
+ }
4504
4958
  async function handleSkillDelete(client, args) {
4505
4959
  const kbid = asString2(args["kbid"], "");
4506
4960
  if (!kbid) {
@@ -4509,14 +4963,9 @@ async function handleSkillDelete(client, args) {
4509
4963
  isError: true
4510
4964
  };
4511
4965
  }
4512
- const result = await client.removeSection(kbid);
4966
+ await client.removeSection(kbid);
4513
4967
  return {
4514
- content: [
4515
- {
4516
- type: "text",
4517
- text: JSON.stringify({ kbid, removed: result.removed })
4518
- }
4519
- ]
4968
+ content: [{ type: "text", text: `Deleted skill ${kbid}` }]
4520
4969
  };
4521
4970
  }
4522
4971
  function handleAutoCaptureReview() {
@@ -4524,10 +4973,7 @@ function handleAutoCaptureReview() {
4524
4973
  content: [
4525
4974
  {
4526
4975
  type: "text",
4527
- text: JSON.stringify({
4528
- pending: [],
4529
- message: "No pending auto-captured knowledge entries."
4530
- })
4976
+ text: "No pending auto-captured knowledge entries."
4531
4977
  }
4532
4978
  ]
4533
4979
  };
@@ -4537,14 +4983,13 @@ async function handleExport2(client, args) {
4537
4983
  const result = await client.export(
4538
4984
  path !== void 0 ? { path } : {}
4539
4985
  );
4540
- return {
4541
- content: [
4542
- {
4543
- type: "text",
4544
- text: JSON.stringify(result)
4545
- }
4546
- ]
4547
- };
4986
+ const lines = [
4987
+ `Exported to ${result.path}`,
4988
+ `sections: ${String(result.sections_exported)}`,
4989
+ `documents: ${String(result.documents_exported)}`,
4990
+ `elapsed: ${String(result.elapsed_ms)}ms`
4991
+ ];
4992
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4548
4993
  }
4549
4994
 
4550
4995
  // src/shared/mcp/functions/handle-resource-read.function.ts