@dikolab/kbdb 0.6.3 → 0.6.5

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 (79) hide show
  1. package/dist/cli.cjs +681 -249
  2. package/dist/cli.cjs.map +4 -4
  3. package/dist/cli.mjs +638 -206
  4. package/dist/cli.mjs.map +4 -4
  5. package/dist/kbdb-worker.cjs +49 -7
  6. package/dist/kbdb-worker.cjs.map +4 -4
  7. package/dist/mod.cjs +1 -1
  8. package/dist/mod.cjs.map +1 -1
  9. package/dist/mod.mjs +1 -1
  10. package/dist/mod.mjs.map +1 -1
  11. package/dist/src/shared/cli/constants/defaults.constant.d.ts +1 -1
  12. package/dist/src/shared/cli/functions/learn-markdown-document.function.d.ts +22 -0
  13. package/dist/src/shared/cli/functions/learn-structured-document.function.d.ts +10 -0
  14. package/dist/src/shared/cli/functions/learn-target-content.function.d.ts +12 -0
  15. package/dist/src/shared/cli/functions/map-add-section-result.function.d.ts +12 -0
  16. package/dist/src/shared/cli/functions/run-agent-list.function.d.ts +13 -9
  17. package/dist/src/shared/cli/functions/run-learn.function.d.ts +10 -59
  18. package/dist/src/shared/cli/functions/run-skill-list.function.d.ts +11 -7
  19. package/dist/src/shared/cli/functions/split-markdown-sections.function.d.ts +16 -0
  20. package/dist/src/shared/cli/typings/learn-client.interface.d.ts +16 -28
  21. package/dist/src/shared/cli/typings/learn-result.model.d.ts +2 -0
  22. package/dist/src/shared/cli/typings/markdown-section.interface.d.ts +23 -0
  23. package/dist/src/shared/content-splitter/classes/heading-parent-tracker.class.d.ts +20 -0
  24. package/dist/src/shared/content-splitter/constants/content-type-maps.constant.d.ts +5 -0
  25. package/dist/src/shared/content-splitter/constants/split-strategy-map.constant.d.ts +4 -0
  26. package/dist/src/shared/content-splitter/functions/detect-content-type.function.d.ts +13 -0
  27. package/dist/src/shared/content-splitter/functions/flush-markdown-draft.function.d.ts +14 -0
  28. package/dist/src/shared/content-splitter/functions/flush-toml-draft.function.d.ts +15 -0
  29. package/dist/src/shared/content-splitter/functions/push-content-section.function.d.ts +16 -0
  30. package/dist/src/shared/content-splitter/functions/resolve-parent-ids.function.d.ts +14 -0
  31. package/dist/src/shared/content-splitter/functions/split-content.function.d.ts +11 -0
  32. package/dist/src/shared/content-splitter/functions/split-html.function.d.ts +12 -0
  33. package/dist/src/shared/content-splitter/functions/split-markdown.function.d.ts +13 -0
  34. package/dist/src/shared/content-splitter/functions/split-plain-text.function.d.ts +8 -0
  35. package/dist/src/shared/content-splitter/functions/split-toml.function.d.ts +12 -0
  36. package/dist/src/shared/content-splitter/index.d.ts +11 -0
  37. package/dist/src/shared/content-splitter/typings/content-format.type.d.ts +2 -0
  38. package/dist/src/shared/content-splitter/typings/content-section.interface.d.ts +17 -0
  39. package/dist/src/shared/content-splitter/typings/markdown-draft.interface.d.ts +12 -0
  40. package/dist/src/shared/content-splitter/typings/split-result.interface.d.ts +8 -0
  41. package/dist/src/shared/content-splitter/typings/split-strategy.interface.d.ts +3 -0
  42. package/dist/src/shared/content-splitter/typings/toml-draft.interface.d.ts +15 -0
  43. package/dist/src/shared/mcp/functions/build-learn-payload.function.d.ts +24 -0
  44. package/dist/src/shared/mcp/functions/determine-learn-split.function.d.ts +18 -0
  45. package/dist/src/shared/mcp/functions/handle-agent-list.function.d.ts +11 -21
  46. package/dist/src/shared/mcp/functions/handle-tool-call.function.d.ts +1 -15
  47. package/dist/src/shared/mcp/functions/handle-tool-learn.function.d.ts +3 -1
  48. package/dist/src/shared/mcp/functions/handle-tool-skill-list.function.d.ts +13 -3
  49. package/dist/src/shared/mcp/functions/learn-with-splitting.function.d.ts +22 -0
  50. package/dist/src/shared/mcp/functions/parse-learn-args.function.d.ts +24 -0
  51. package/dist/src/shared/mcp/functions/store-split-learn-sections.function.d.ts +16 -0
  52. package/dist/src/shared/mcp/functions/unwrap-json-content.function.d.ts +9 -0
  53. package/dist/src/shared/mcp/typings/learn-section-params.interface.d.ts +20 -0
  54. package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +4 -0
  55. package/dist/src/shared/mcp/typings/mcp-worker-client.interface.d.ts +4 -0
  56. package/dist/src/shared/query-filter/functions/matches-query.function.d.ts +17 -0
  57. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  58. package/dist/src/shared/wasm-codec/functions/decode-one-recall-result.function.d.ts +2 -1
  59. package/dist/src/shared/wasm-codec/functions/decode-recall-document.function.d.ts +2 -1
  60. package/dist/src/shared/wasm-codec/functions/decode-recall-result.function.d.ts +2 -0
  61. package/dist/src/shared/wasm-codec/functions/decode-recall-sibling.function.d.ts +2 -1
  62. package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +6 -0
  63. package/dist/src/shared/wasm-codec/functions/decode-section-match.function.d.ts +2 -1
  64. package/dist/src/shared/wasm-codec/functions/decode-section-records.function.d.ts +4 -0
  65. package/dist/src/shared/wasm-codec/functions/encode-add-section-params.function.d.ts +1 -0
  66. package/dist/src/shared/wasm-codec/typings/add-section-input.model.d.ts +2 -0
  67. package/dist/src/shared/wasm-codec/typings/section-match.model.d.ts +4 -0
  68. package/dist/src/shared/wasm-codec/typings/section-record-trailing.model.d.ts +2 -0
  69. package/dist/src/shared/wasm-codec/typings/section-record.model.d.ts +2 -0
  70. package/dist/src/shared/worker-client/typings/recall-result.model.d.ts +12 -0
  71. package/dist/src/shared/worker-client/typings/search-result.model.d.ts +4 -0
  72. package/dist/src/shared/worker-client/typings/section.model.d.ts +2 -0
  73. package/dist/wasm/default-embedding/kbdb_default_embedding_bg.wasm +0 -0
  74. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  75. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  76. package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
  77. package/dist/worker.mjs +49 -7
  78. package/dist/worker.mjs.map +4 -4
  79. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -23,7 +23,10 @@ var SUPPORTED_EXTENSIONS = [
23
23
  ".md",
24
24
  ".txt",
25
25
  ".rst",
26
- ".adoc"
26
+ ".adoc",
27
+ ".html",
28
+ ".htm",
29
+ ".toml"
27
30
  ];
28
31
 
29
32
  // src/shared/cli/functions/parse-args.function.ts
@@ -659,6 +662,319 @@ function computeDirectoryLevel(filePath, basePath) {
659
662
  return Math.min(depth + 1, MAX_LEVEL);
660
663
  }
661
664
 
665
+ // src/shared/content-splitter/constants/content-type-maps.constant.ts
666
+ var HINT_FORMAT_MAP = {
667
+ markdown: "markdown",
668
+ "text/markdown": "markdown",
669
+ md: "markdown",
670
+ html: "html",
671
+ "text/html": "html",
672
+ toml: "toml",
673
+ "application/toml": "toml",
674
+ "text/toml": "toml",
675
+ "text/x-toml": "toml"
676
+ };
677
+ var EXTENSION_FORMAT_MAP = {
678
+ md: "markdown",
679
+ markdown: "markdown",
680
+ html: "html",
681
+ htm: "html",
682
+ toml: "toml"
683
+ };
684
+
685
+ // src/shared/content-splitter/functions/detect-content-type.function.ts
686
+ function detectContentType(content, typeHint, filePath) {
687
+ const hinted = typeHint && HINT_FORMAT_MAP[typeHint.toLowerCase()];
688
+ if (hinted) return hinted;
689
+ const extension = filePath?.split(".").pop()?.toLowerCase();
690
+ const extMatch = extension && EXTENSION_FORMAT_MAP[extension];
691
+ if (extMatch) return extMatch;
692
+ return sniffContentType(content);
693
+ }
694
+ function sniffContentType(content) {
695
+ const trimmed = content.trimStart();
696
+ if (/^<!DOCTYPE/i.test(trimmed) || /^<html/i.test(trimmed)) {
697
+ return "html";
698
+ }
699
+ if (trimmed.startsWith("[") && trimmed.includes("=")) {
700
+ return "toml";
701
+ }
702
+ return "plain-text";
703
+ }
704
+
705
+ // src/shared/content-splitter/classes/heading-parent-tracker.class.ts
706
+ var HeadingParentTracker = class {
707
+ stack = [];
708
+ /**
709
+ * Resolves the parent index for a heading at `level`
710
+ * appearing at output position `index`, then records the
711
+ * heading so subsequent, deeper headings can nest under it.
712
+ *
713
+ * @param level - heading depth, shallower is smaller
714
+ * @param index - position of this heading in the output
715
+ * sections array
716
+ * @returns the parent's output index, or `null` when the
717
+ * heading is at the root
718
+ */
719
+ resolve(level, index) {
720
+ while (this.stack.length > 0 && this.stack[this.stack.length - 1].level >= level) {
721
+ this.stack.pop();
722
+ }
723
+ const parentIndex = this.stack.length > 0 ? this.stack[this.stack.length - 1].index : null;
724
+ this.stack.push({ level, index });
725
+ return parentIndex;
726
+ }
727
+ };
728
+
729
+ // src/shared/content-splitter/functions/push-content-section.function.ts
730
+ function pushContentSection(sections, tracker, content, title, level) {
731
+ if (level === void 0) {
732
+ sections.push({ content, parentIndex: null });
733
+ return;
734
+ }
735
+ sections.push({
736
+ title,
737
+ level,
738
+ content,
739
+ parentIndex: tracker.resolve(level, sections.length)
740
+ });
741
+ }
742
+
743
+ // src/shared/content-splitter/functions/flush-markdown-draft.function.ts
744
+ function flushMarkdownDraft(draft, sections, tracker) {
745
+ const body = draft.lines.join("\n").trimEnd();
746
+ if (draft.title === void 0 && body.length === 0) return;
747
+ pushContentSection(
748
+ sections,
749
+ tracker,
750
+ body,
751
+ draft.title,
752
+ draft.level
753
+ );
754
+ }
755
+
756
+ // src/shared/content-splitter/functions/split-markdown.function.ts
757
+ var HEADING_PATTERN = /^(#{1,6})\s+(.+)$/;
758
+ function splitMarkdown(content) {
759
+ const sections = [];
760
+ const tracker = new HeadingParentTracker();
761
+ let draft = { lines: [] };
762
+ let inCodeBlock = false;
763
+ for (const line of content.split("\n")) {
764
+ if (line.startsWith("```") || line.startsWith("~~~")) {
765
+ inCodeBlock = !inCodeBlock;
766
+ draft.lines.push(line);
767
+ continue;
768
+ }
769
+ const heading = inCodeBlock ? null : HEADING_PATTERN.exec(line);
770
+ if (heading) {
771
+ flushMarkdownDraft(draft, sections, tracker);
772
+ draft = {
773
+ title: heading[2].trim(),
774
+ level: heading[1].length,
775
+ lines: []
776
+ };
777
+ continue;
778
+ }
779
+ draft.lines.push(line);
780
+ }
781
+ flushMarkdownDraft(draft, sections, tracker);
782
+ return sections.length > 0 ? sections : [{ content: content.trimEnd(), parentIndex: null }];
783
+ }
784
+
785
+ // src/shared/content-splitter/functions/split-html.function.ts
786
+ var HEADING_PATTERN2 = /<h([1-6])[^>]*>(.*?)<\/h\1>/gi;
787
+ function splitHtml(content) {
788
+ const matches = [...content.matchAll(HEADING_PATTERN2)];
789
+ if (matches.length === 0) {
790
+ return [{ content: content.trimEnd(), parentIndex: null }];
791
+ }
792
+ const sections = [];
793
+ const tracker = new HeadingParentTracker();
794
+ const firstIndex = matches[0].index;
795
+ const preamble = content.slice(0, firstIndex).trimEnd();
796
+ if (preamble.length > 0) {
797
+ pushContentSection(sections, tracker, preamble);
798
+ }
799
+ matches.forEach((match, i) => {
800
+ const start = match.index + match[0].length;
801
+ const end = matches[i + 1]?.index ?? content.length;
802
+ pushContentSection(
803
+ sections,
804
+ tracker,
805
+ content.slice(start, end).trimEnd(),
806
+ match[2].trim(),
807
+ Number(match[1])
808
+ );
809
+ });
810
+ return sections;
811
+ }
812
+
813
+ // src/shared/content-splitter/functions/flush-toml-draft.function.ts
814
+ function flushTomlDraft(draft, sections, pathIndex) {
815
+ const body = draft.lines.join("\n").trimEnd();
816
+ if (draft.title === void 0 && body.length === 0) return;
817
+ if (draft.title === void 0) {
818
+ sections.push({ content: body, parentIndex: null });
819
+ return;
820
+ }
821
+ const path = draft.path ?? draft.title;
822
+ sections.push({
823
+ title: draft.title,
824
+ content: body,
825
+ parentIndex: resolveParentIndex(path, pathIndex)
826
+ });
827
+ pathIndex.set(path, sections.length - 1);
828
+ }
829
+ function resolveParentIndex(path, pathIndex) {
830
+ if (/^["']/.test(path)) return null;
831
+ const segments = path.split(".");
832
+ segments.pop();
833
+ if (segments.length === 0) return null;
834
+ return pathIndex.get(segments.join(".")) ?? null;
835
+ }
836
+
837
+ // src/shared/content-splitter/functions/split-toml.function.ts
838
+ var HEADER_PATTERN = /^\[([^\]]+)\]\s*$/;
839
+ var QUOTED_PATTERN = /^(["'])(.*)\1$/;
840
+ function splitToml(content) {
841
+ const sections = [];
842
+ const pathIndex = /* @__PURE__ */ new Map();
843
+ let draft = { lines: [] };
844
+ for (const line of content.split("\n")) {
845
+ const header = HEADER_PATTERN.exec(line);
846
+ if (header) {
847
+ flushTomlDraft(draft, sections, pathIndex);
848
+ draft = { ...parseHeader(header[1]), lines: [] };
849
+ continue;
850
+ }
851
+ draft.lines.push(line);
852
+ }
853
+ flushTomlDraft(draft, sections, pathIndex);
854
+ return sections.length > 0 ? sections : [{ content: content.trimEnd(), parentIndex: null }];
855
+ }
856
+ function parseHeader(inner) {
857
+ const trimmed = inner.trim();
858
+ const quoted = QUOTED_PATTERN.exec(trimmed);
859
+ return quoted ? { title: quoted[2], path: trimmed } : { title: trimmed, path: trimmed };
860
+ }
861
+
862
+ // src/shared/content-splitter/functions/split-plain-text.function.ts
863
+ function splitPlainText(content) {
864
+ return [{ content, parentIndex: null }];
865
+ }
866
+
867
+ // src/shared/content-splitter/constants/split-strategy-map.constant.ts
868
+ var SPLIT_STRATEGY_MAP = {
869
+ markdown: splitMarkdown,
870
+ html: splitHtml,
871
+ toml: splitToml,
872
+ "plain-text": splitPlainText
873
+ };
874
+
875
+ // src/shared/content-splitter/functions/split-content.function.ts
876
+ function splitContent(content, typeHint, filePath) {
877
+ const format = detectContentType(content, typeHint, filePath);
878
+ return {
879
+ format,
880
+ sections: SPLIT_STRATEGY_MAP[format](content)
881
+ };
882
+ }
883
+
884
+ // src/shared/content-splitter/functions/resolve-parent-ids.function.ts
885
+ function resolveParentIds(sections, kbids) {
886
+ return sections.map(
887
+ (section) => section.parentIndex === null ? null : kbids[section.parentIndex]
888
+ );
889
+ }
890
+
891
+ // src/shared/cli/functions/map-add-section-result.function.ts
892
+ function mapAddSectionResult(result, path, size, sectionType) {
893
+ return {
894
+ kbid: result.items[0] ?? "",
895
+ indexed_terms_count: 0,
896
+ type: sectionType,
897
+ path,
898
+ size,
899
+ skipped: false,
900
+ superseded: result.superseded ?? null,
901
+ warnings: result.warnings ?? [],
902
+ near_duplicates: result.near_duplicates ?? []
903
+ };
904
+ }
905
+
906
+ // src/shared/cli/functions/learn-structured-document.function.ts
907
+ import { basename as basename2 } from "node:path";
908
+ async function learnStructuredDocument(client, options, path, splitResult, sourcePath) {
909
+ const { sections } = splitResult;
910
+ const firstTitled = sections.find((s) => s.title !== void 0);
911
+ const docTitle = firstTitled?.title ?? basename2(path);
912
+ const kbids = [];
913
+ const results = [];
914
+ for (const section of sections) {
915
+ const [parent] = resolveParentIds([section], kbids);
916
+ const added = await client.addSection({
917
+ sectionType: "text",
918
+ content: section.content,
919
+ title: section.title,
920
+ description: options.description,
921
+ docid: path,
922
+ tags: options.tags,
923
+ replace: options.replace,
924
+ sourcePath,
925
+ level: section.level,
926
+ parent: parent ?? void 0
927
+ });
928
+ kbids.push(added.items[0] ?? "");
929
+ results.push(
930
+ mapAddSectionResult(
931
+ added,
932
+ path,
933
+ section.content.length,
934
+ "text"
935
+ )
936
+ );
937
+ }
938
+ await client.groupSections(docTitle, kbids);
939
+ return results;
940
+ }
941
+
942
+ // src/shared/cli/functions/learn-target-content.function.ts
943
+ async function learnTargetContent(client, options, path, content, sourcePath, level) {
944
+ if (!options.title) {
945
+ const isStdin = path === "-";
946
+ const split = splitContent(
947
+ content,
948
+ isStdin ? "markdown" : void 0,
949
+ isStdin ? void 0 : path
950
+ );
951
+ const hasTitled = split.sections.some(
952
+ (s) => s.title !== void 0
953
+ );
954
+ if (split.sections.length >= 2 && hasTitled) {
955
+ return learnStructuredDocument(
956
+ client,
957
+ options,
958
+ path,
959
+ split,
960
+ sourcePath
961
+ );
962
+ }
963
+ }
964
+ const result = await client.addSection({
965
+ sectionType: "text",
966
+ content,
967
+ title: options.title,
968
+ description: options.description,
969
+ docid: options.docid,
970
+ tags: options.tags,
971
+ replace: options.replace,
972
+ sourcePath,
973
+ level
974
+ });
975
+ return [mapAddSectionResult(result, path, content.length, "text")];
976
+ }
977
+
662
978
  // src/shared/cli/functions/run-learn.function.ts
663
979
  async function runLearn(client, options, io) {
664
980
  log(
@@ -673,37 +989,30 @@ async function runLearn(client, options, io) {
673
989
  if (stdinConsumed) continue;
674
990
  stdinConsumed = true;
675
991
  const content = await readStdin();
676
- const result = await client.addSection({
677
- sectionType: "text",
678
- content,
679
- title: options.title,
680
- description: options.description,
681
- docid: options.docid,
682
- tags: options.tags,
683
- replace: options.replace,
684
- level: options.level
685
- });
686
992
  results.push(
687
- mapAddSectionResult(result, "-", content.length, "text")
993
+ ...await learnTargetContent(
994
+ client,
995
+ options,
996
+ "-",
997
+ content,
998
+ void 0,
999
+ options.level
1000
+ )
688
1001
  );
689
1002
  continue;
690
1003
  }
691
1004
  const files = collectFiles(target);
692
1005
  for (const file of files) {
693
1006
  const content = readFileSync(file, "utf-8");
694
- const result = await client.addSection({
695
- sectionType: "text",
696
- content,
697
- title: options.title,
698
- description: options.description,
699
- docid: options.docid,
700
- tags: options.tags,
701
- replace: options.replace,
702
- sourcePath: file,
703
- level: options.level ?? computeDirectoryLevel(file, target)
704
- });
705
1007
  results.push(
706
- mapAddSectionResult(result, file, content.length, "text")
1008
+ ...await learnTargetContent(
1009
+ client,
1010
+ options,
1011
+ file,
1012
+ content,
1013
+ file,
1014
+ options.level ?? computeDirectoryLevel(file, target)
1015
+ )
707
1016
  );
708
1017
  }
709
1018
  }
@@ -713,19 +1022,6 @@ async function runLearn(client, options, io) {
713
1022
  );
714
1023
  return results;
715
1024
  }
716
- function mapAddSectionResult(result, path, size, sectionType) {
717
- return {
718
- kbid: result.items[0] ?? "",
719
- indexed_terms_count: 0,
720
- type: sectionType,
721
- path,
722
- size,
723
- skipped: false,
724
- superseded: result.superseded ?? null,
725
- warnings: result.warnings ?? [],
726
- near_duplicates: result.near_duplicates ?? []
727
- };
728
- }
729
1025
  function readProcessStdin() {
730
1026
  return new Promise((resolve7) => {
731
1027
  const chunks = [];
@@ -1113,28 +1409,27 @@ async function runSkillLearn(client, options) {
1113
1409
  };
1114
1410
  }
1115
1411
 
1412
+ // src/shared/query-filter/functions/matches-query.function.ts
1413
+ function matchesQuery(query, ...fields) {
1414
+ if (!query) return true;
1415
+ const needle = query.toLowerCase();
1416
+ return fields.some(
1417
+ (field) => typeof field === "string" && field.toLowerCase().includes(needle)
1418
+ );
1419
+ }
1420
+
1116
1421
  // src/shared/cli/functions/run-skill-list.function.ts
1117
- async function runSkillList(client, _options) {
1422
+ async function runSkillList(client, options) {
1118
1423
  log("running skill-list", 0 /* DEBUG */);
1119
1424
  const records = await client.listByType("skill");
1120
- const items = records.map((r) => {
1121
- let argumentCount = 0;
1122
- try {
1123
- const parsed = JSON.parse(r.content);
1124
- if (Array.isArray(parsed["arguments"])) {
1125
- argumentCount = parsed["arguments"].length;
1126
- }
1127
- } catch {
1128
- }
1129
- return {
1130
- kbid: r.kbid,
1131
- name: r.title,
1132
- description: r.description,
1133
- argumentCount
1134
- };
1135
- });
1425
+ const query = options.args.join(" ").toLowerCase();
1426
+ const filtered = records.filter(
1427
+ (r) => matchesQuery(query, r.title, r.description)
1428
+ );
1429
+ const names = filtered.map((r) => r.title);
1430
+ const data = { items: names, total: names.length, offset: 0 };
1136
1431
  return {
1137
- output: JSON.stringify(items, null, 2),
1432
+ output: JSON.stringify(data, null, 2),
1138
1433
  exitCode: 0
1139
1434
  };
1140
1435
  }
@@ -1263,16 +1558,15 @@ async function runAgentCreate(client, options) {
1263
1558
  }
1264
1559
 
1265
1560
  // src/shared/cli/functions/run-agent-list.function.ts
1266
- async function runAgentList(client, _options) {
1561
+ async function runAgentList(client, options) {
1267
1562
  log("running agent-list", 0 /* DEBUG */);
1268
1563
  const docs = await client.listDocumentsByType("agent");
1269
- const items = docs.map((d) => ({
1270
- docid: d.docid,
1271
- name: d.title,
1272
- skillCount: Math.max(0, d.sections.length - 1)
1273
- }));
1564
+ const query = options.args.join(" ").toLowerCase();
1565
+ const filtered = docs.filter((d) => matchesQuery(query, d.title));
1566
+ const names = filtered.map((d) => d.title);
1567
+ const data = { items: names, total: names.length, offset: 0 };
1274
1568
  return {
1275
- output: JSON.stringify(items, null, 2),
1569
+ output: JSON.stringify(data, null, 2),
1276
1570
  exitCode: 0
1277
1571
  };
1278
1572
  }
@@ -1740,7 +2034,7 @@ async function handleTypedSearch(client, options, type) {
1740
2034
  }
1741
2035
 
1742
2036
  // src/shared/version/constants/version.constant.ts
1743
- var VERSION = "0.6.3";
2037
+ var VERSION = "0.6.5";
1744
2038
 
1745
2039
  // src/shared/cli/functions/generate-help.function.ts
1746
2040
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
@@ -1768,13 +2062,13 @@ var GROUP_SUBCOMMANDS = {
1768
2062
  ],
1769
2063
  skill: [
1770
2064
  ["learn", "Create a new skill definition"],
1771
- ["list", "List all skills"],
2065
+ ["list", "List skills (filter by keyword)"],
1772
2066
  ["get", "Retrieve a skill by name or kbid"],
1773
2067
  ["delete", "Remove a skill"]
1774
2068
  ],
1775
2069
  agent: [
1776
2070
  ["create", "Create an agent with skill refs"],
1777
- ["list", "List all agents"],
2071
+ ["list", "List agents (filter by keyword)"],
1778
2072
  ["get", "Retrieve an agent by docid"],
1779
2073
  ["delete", "Remove an agent"]
1780
2074
  ],
@@ -1819,9 +2113,9 @@ Usage: kbdb [options]
1819
2113
  kbdb <command> <subcommand> [options] ...[params]
1820
2114
 
1821
2115
  Knowledge Base:
1822
- learn Import content from files/stdin
2116
+ learn Import content (auto-splits structured formats)
1823
2117
  unlearn Remove sections by kbid
1824
- search Query the knowledge base
2118
+ search Query the knowledge base (sibling boost)
1825
2119
  recall Retrieve section context by kbid
1826
2120
  content Compose rendered content from ids
1827
2121
 
@@ -1840,28 +2134,34 @@ Maintenance:
1840
2134
 
1841
2135
  Skills:
1842
2136
  skill learn Create a new skill definition
1843
- skill list List all skills
2137
+ skill list List skills (filter by keyword)
1844
2138
  skill get Retrieve a skill by name or kbid
1845
2139
  skill delete Remove a skill
2140
+ skill search Search skills by keyword
1846
2141
 
1847
2142
  Agents:
1848
2143
  agent create Create an agent with skill refs
1849
- agent list List all agents
2144
+ agent list List agents (filter by keyword)
1850
2145
  agent get Retrieve an agent by docid
1851
2146
  agent delete Remove an agent
1852
-
1853
- Worker:
1854
- worker stop Stop the worker daemon
1855
- worker restart Restart the worker daemon
2147
+ agent search Search agents by keyword
1856
2148
 
1857
2149
  Server:
1858
2150
  mcp Start MCP server mode (stdio)
1859
2151
 
1860
2152
  Global Options:
1861
2153
  -h, --help Show this help message
1862
- -v, --version Show version information
2154
+ --version Show version information
2155
+ -v, --verbose Log warnings (-vv info, -vvv debug)
1863
2156
  --db <path> Path to .kbdb directory
1864
- --format <fmt> Output format (json|text|mcp)
2157
+ --format <fmt> Output format (rec|json|text|mcp)
2158
+ --timeout-ms <ms> Request timeout (default 30000)
2159
+ --retry <n> Retries on timeout (default 1)
2160
+ --non-interactive Suppress prompts
2161
+
2162
+ Worker:
2163
+ worker stop Stop background daemon
2164
+ worker restart Restart background daemon
1865
2165
 
1866
2166
  Run "kbdb <command> --help" for detailed information.
1867
2167
  `;
@@ -1918,9 +2218,22 @@ Options:
1918
2218
  --replace Supersede existing section by source key
1919
2219
  --db <path> Path to .kbdb directory
1920
2220
 
2221
+ Content splitting:
2222
+ Structured formats are automatically split
2223
+ into independent sections that share a docid.
2224
+ Each section carries a level (heading depth)
2225
+ and parent (kbid of enclosing section).
2226
+ Providing --title imports as a single section
2227
+ (skips splitting).
2228
+
2229
+ Supported formats:
2230
+ Markdown .md, text/markdown ATX headings
2231
+ HTML .html, text/html <h1>-<h6> tags
2232
+ TOML .toml, application/toml [section]
2233
+ Plain .txt, text/plain no splitting
2234
+
1921
2235
  Output:
1922
- JSON array of { kbid, path, size, skipped,
1923
- superseded, warnings, near_duplicates }
2236
+ { items, total, offset } envelope
1924
2237
 
1925
2238
  Examples:
1926
2239
  kbdb learn README.md
@@ -1955,12 +2268,23 @@ Arguments:
1955
2268
  Options:
1956
2269
  --mode <mode> Result granularity (sections|documents|stats)
1957
2270
  --limit <n> Max results (default: 20)
1958
- --format <fmt> Output format (json|text|mcp)
2271
+ --format <fmt> Output format (rec|json|text|mcp)
1959
2272
  --content, -c Hydrate results with section content
1960
2273
  --db <path> Path to .kbdb directory
1961
2274
 
2275
+ Scoring:
2276
+ Results are ranked by BM25F. Sections from
2277
+ the same document (shared docid) receive a
2278
+ sibling score boost when multiple sections
2279
+ match the query.
2280
+
2281
+ Fields:
2282
+ Each result includes level (heading depth,
2283
+ 1-6 or null) and parent (kbid of enclosing
2284
+ section or null).
2285
+
1962
2286
  Output:
1963
- JSON array of { kbid, score, preview, matches }
2287
+ { items, total, offset } envelope
1964
2288
 
1965
2289
  Examples:
1966
2290
  kbdb search "machine learning"
@@ -1980,7 +2304,8 @@ Options:
1980
2304
  --db <path> Path to .kbdb directory
1981
2305
 
1982
2306
  Output:
1983
- JSON RecallResult or RecallResult[]
2307
+ JSON RecallResult or RecallResult[] with
2308
+ level, parent, siblings, and references
1984
2309
 
1985
2310
  Examples:
1986
2311
  kbdb recall abcdef1234567890abcdef1234
@@ -1989,11 +2314,14 @@ Examples:
1989
2314
  content: `kbdb content - Compose rendered content from ids
1990
2315
 
1991
2316
  Usage: kbdb content [options] <id>...
2317
+ kbdb content --search <query>...
1992
2318
 
1993
2319
  Arguments:
1994
2320
  <id> kbid or docid identifier
2321
+ <query> Search terms (with --search)
1995
2322
 
1996
2323
  Options:
2324
+ --search, -s Search for ids by keyword
1997
2325
  --db <path> Path to .kbdb directory
1998
2326
 
1999
2327
  Output:
@@ -2002,6 +2330,7 @@ Output:
2002
2330
  Examples:
2003
2331
  kbdb content abcdef1234567890abcdef1234
2004
2332
  kbdb content id1 id2
2333
+ kbdb content --search machine learning
2005
2334
  `,
2006
2335
  check: `kbdb check - Verify knowledge base integrity
2007
2336
 
@@ -2169,16 +2498,20 @@ Examples:
2169
2498
  `,
2170
2499
  "skill-list": `kbdb skill list - List all skills
2171
2500
 
2172
- Usage: kbdb skill list [options]
2501
+ Usage: kbdb skill list [options] [keywords...]
2502
+
2503
+ Arguments:
2504
+ [keywords...] Case-insensitive substring filter on name or description
2173
2505
 
2174
2506
  Options:
2175
2507
  --db <path> Path to .kbdb directory
2176
2508
 
2177
2509
  Output:
2178
- JSON array of { kbid, name, description, argumentCount }
2510
+ JSON { items, total, offset }
2179
2511
 
2180
2512
  Examples:
2181
2513
  kbdb skill list
2514
+ kbdb skill list summarize
2182
2515
  kbdb skill list --db /path/to/project
2183
2516
  `,
2184
2517
  "skill-get": `kbdb skill get - Retrieve a skill by name or kbid
@@ -2234,16 +2567,20 @@ Examples:
2234
2567
  `,
2235
2568
  "agent-list": `kbdb agent list - List all agents
2236
2569
 
2237
- Usage: kbdb agent list [options]
2570
+ Usage: kbdb agent list [options] [keywords...]
2571
+
2572
+ Arguments:
2573
+ [keywords...] Case-insensitive substring filter on agent name
2238
2574
 
2239
2575
  Options:
2240
2576
  --db <path> Path to .kbdb directory
2241
2577
 
2242
2578
  Output:
2243
- JSON array of { docid, name, description, skillCount }
2579
+ JSON { items, total, offset }
2244
2580
 
2245
2581
  Examples:
2246
2582
  kbdb agent list
2583
+ kbdb agent list reviewer
2247
2584
  kbdb agent list --db /path/to/project
2248
2585
  `,
2249
2586
  "agent-get": `kbdb agent get - Retrieve an agent by docid
@@ -3503,7 +3840,7 @@ function createToolDefinitions() {
3503
3840
  return [
3504
3841
  {
3505
3842
  name: "search",
3506
- description: "Query the knowledge base by keywords. Returns ranked results with heading, snippet, and matched terms. Use multiple short keyword queries for best coverage. Use recall to get full content.",
3843
+ description: "Query the knowledge base by keywords. Returns ranked results with heading, snippet, matched terms, level, and parent. Sections from the same document receive a sibling score boost when multiple sections match. Use multiple short keyword queries for best coverage. Use recall for full content.",
3507
3844
  inputSchema: {
3508
3845
  type: "object",
3509
3846
  properties: {
@@ -3546,7 +3883,7 @@ function createToolDefinitions() {
3546
3883
  },
3547
3884
  {
3548
3885
  name: "learn",
3549
- description: "Import content into the knowledge base. Use for durable facts, decisions, and corrections. Use replace when updating existing knowledge. Attach tags for scoping.",
3886
+ description: "Import content into the knowledge base. Structured content (Markdown, HTML, TOML) is automatically split into sections that share a docid. Each section carries a level (heading depth) and parent reference. Providing title imports as a single section (skips splitting). Use replace when updating existing knowledge. Attach tags for scoping.",
3550
3887
  inputSchema: {
3551
3888
  type: "object",
3552
3889
  properties: {
@@ -3556,11 +3893,11 @@ function createToolDefinitions() {
3556
3893
  },
3557
3894
  type: {
3558
3895
  type: "string",
3559
- description: "MIME type (default: text/markdown). Use image/* for images."
3896
+ description: "MIME type or format hint. Supported: text/markdown, text/html, application/toml (also text/toml, text/x-toml), text/plain, image/*. Default: text/markdown."
3560
3897
  },
3561
3898
  title: {
3562
3899
  type: "string",
3563
- description: "Section title. Required for image/* types."
3900
+ description: "Section title. Skips content splitting when set. Required for image/* types."
3564
3901
  },
3565
3902
  description: {
3566
3903
  type: "string",
@@ -3568,7 +3905,7 @@ function createToolDefinitions() {
3568
3905
  },
3569
3906
  docid: {
3570
3907
  type: "string",
3571
- description: "Document ID to group under"
3908
+ description: "Document ID to group sections under. Set automatically when content splitting is active."
3572
3909
  },
3573
3910
  tags: {
3574
3911
  type: "array",
@@ -3637,7 +3974,7 @@ function createToolDefinitions() {
3637
3974
  },
3638
3975
  {
3639
3976
  name: "recall",
3640
- description: "Retrieve full content and context for sections by identifier. Use after search to read relevant results. Returns section text, metadata, references, and back-references.",
3977
+ description: "Retrieve full content and context for sections by identifier. Use after search to read relevant results. Returns section text, level, parent, metadata, references, and back-references.",
3641
3978
  inputSchema: {
3642
3979
  type: "object",
3643
3980
  properties: {
@@ -3768,7 +4105,7 @@ function createToolDefinitions() {
3768
4105
  },
3769
4106
  {
3770
4107
  name: "skill-learn",
3771
- description: "Create or update a skill definition. Skills are reusable AI agent capabilities with template placeholders.",
4108
+ description: "Create or update a skill definition. Skills are reusable AI agent capabilities with template placeholders. Stored as atomic JSON (not split into sections).",
3772
4109
  inputSchema: {
3773
4110
  type: "object",
3774
4111
  properties: {
@@ -3797,10 +4134,15 @@ function createToolDefinitions() {
3797
4134
  },
3798
4135
  {
3799
4136
  name: "skill-list",
3800
- description: "List all skills in the knowledge base.",
4137
+ description: "List skill definitions stored in the knowledge base, optionally filtered by a substring query.",
3801
4138
  inputSchema: {
3802
4139
  type: "object",
3803
- properties: {}
4140
+ properties: {
4141
+ query: {
4142
+ type: "string",
4143
+ description: "Case-insensitive substring filter on skill name or description."
4144
+ }
4145
+ }
3804
4146
  },
3805
4147
  annotations: {
3806
4148
  title: "List Skills",
@@ -3857,7 +4199,7 @@ function createToolDefinitions() {
3857
4199
  },
3858
4200
  {
3859
4201
  name: "agent-create",
3860
- description: "Create an AI agent with a persona and optional skill references.",
4202
+ description: "Create an AI agent with a persona and optional skill references. Stored as atomic JSON (not split into sections).",
3861
4203
  inputSchema: {
3862
4204
  type: "object",
3863
4205
  properties: {
@@ -3891,10 +4233,15 @@ function createToolDefinitions() {
3891
4233
  },
3892
4234
  {
3893
4235
  name: "agent-list",
3894
- description: "List all agents in the knowledge base.",
4236
+ description: "List agent documents in the knowledge base, optionally filtered by a substring query.",
3895
4237
  inputSchema: {
3896
4238
  type: "object",
3897
- properties: {}
4239
+ properties: {
4240
+ query: {
4241
+ type: "string",
4242
+ description: "Case-insensitive substring filter on agent name or description."
4243
+ }
4244
+ }
3898
4245
  },
3899
4246
  annotations: {
3900
4247
  title: "List Agents",
@@ -4013,7 +4360,7 @@ function createToolDefinitions() {
4013
4360
  title: "Auto-Capture Review",
4014
4361
  readOnlyHint: false,
4015
4362
  destructiveHint: false,
4016
- idempotentHint: true,
4363
+ idempotentHint: false,
4017
4364
  openWorldHint: false
4018
4365
  }
4019
4366
  }
@@ -4142,7 +4489,7 @@ async function readAgentDescription(client, sections) {
4142
4489
  return "";
4143
4490
  }
4144
4491
  }
4145
- async function handleAgentList(client) {
4492
+ async function handleAgentList(client, args = {}) {
4146
4493
  const docs = await client.listDocumentsByType("agent");
4147
4494
  const items = await Promise.all(
4148
4495
  docs.map(async (d) => ({
@@ -4150,17 +4497,17 @@ async function handleAgentList(client) {
4150
4497
  description: await readAgentDescription(client, d.sections)
4151
4498
  }))
4152
4499
  );
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"
4500
+ const query = typeof args["query"] === "string" ? args["query"].toLowerCase() : "";
4501
+ const filtered = items.filter(
4502
+ (item) => matchesQuery(query, item.name, item.description)
4158
4503
  );
4504
+ const names = filtered.map((item) => item.name);
4505
+ const data = { items: names, total: names.length, offset: 0 };
4159
4506
  return {
4160
4507
  content: [
4161
4508
  {
4162
4509
  type: "text",
4163
- text
4510
+ text: JSON.stringify(data, null, 2)
4164
4511
  }
4165
4512
  ]
4166
4513
  };
@@ -4402,25 +4749,38 @@ async function handleAgentSearch(client, args) {
4402
4749
  return { content: [{ type: "text", text }] };
4403
4750
  }
4404
4751
 
4405
- // src/shared/mcp/functions/handle-tool-call.function.ts
4752
+ // src/shared/mcp/functions/as-string.function.ts
4406
4753
  function asString2(value, fallback) {
4407
4754
  return typeof value === "string" ? value : fallback;
4408
4755
  }
4409
- function asBooleanOrUndefined(value) {
4410
- return typeof value === "boolean" ? value : void 0;
4411
- }
4756
+
4757
+ // src/shared/mcp/functions/as-string-array-or-undefined.function.ts
4412
4758
  function asStringArrayOrUndefined(value) {
4413
4759
  if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
4414
4760
  return value;
4415
4761
  }
4416
4762
  return void 0;
4417
4763
  }
4418
- function asStringArray2(value) {
4419
- if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
4420
- return value;
4421
- }
4422
- return [];
4764
+
4765
+ // src/shared/mcp/functions/as-boolean-or-undefined.function.ts
4766
+ function asBooleanOrUndefined(value) {
4767
+ return typeof value === "boolean" ? value : void 0;
4768
+ }
4769
+
4770
+ // src/shared/mcp/functions/parse-learn-args.function.ts
4771
+ function parseLearnArgs(args) {
4772
+ return {
4773
+ sectionType: asString2(args["type"], "text/markdown"),
4774
+ title: typeof args["title"] === "string" ? args["title"] : void 0,
4775
+ description: typeof args["description"] === "string" ? args["description"] : void 0,
4776
+ docid: typeof args["docid"] === "string" ? args["docid"] : void 0,
4777
+ tags: asStringArrayOrUndefined(args["tags"]),
4778
+ replace: asBooleanOrUndefined(args["replace"]),
4779
+ level: typeof args["level"] === "number" ? args["level"] : void 0
4780
+ };
4423
4781
  }
4782
+
4783
+ // src/shared/mcp/functions/unwrap-json-content.function.ts
4424
4784
  function unwrapJsonContent(raw) {
4425
4785
  const trimmed = raw.trim();
4426
4786
  if (!trimmed.startsWith("{")) return raw;
@@ -4433,6 +4793,158 @@ function unwrapJsonContent(raw) {
4433
4793
  }
4434
4794
  return raw;
4435
4795
  }
4796
+
4797
+ // src/shared/mcp/functions/determine-learn-split.function.ts
4798
+ function determineLearnSplit(content, sectionType, title) {
4799
+ const splittable = sectionType.startsWith("text/") || sectionType === "markdown" || sectionType === "application/toml";
4800
+ if (title !== void 0 || !splittable) return void 0;
4801
+ const result = splitContent(content, sectionType);
4802
+ const hasTitledSection = result.sections.some(
4803
+ (section) => section.title !== void 0
4804
+ );
4805
+ return result.sections.length >= 2 && hasTitledSection ? result : void 0;
4806
+ }
4807
+
4808
+ // src/shared/mcp/functions/learn-with-splitting.function.ts
4809
+ async function learnWithSplitting(client, splitResult, params) {
4810
+ const { sections } = splitResult;
4811
+ const firstTitled = sections.find((s) => s.title !== void 0);
4812
+ const docTitle = firstTitled?.title ?? params.docid ?? "Untitled";
4813
+ const docid = params.docid ?? firstTitled?.title;
4814
+ const kbids = [];
4815
+ for (const section of sections) {
4816
+ const [parent] = resolveParentIds([section], kbids);
4817
+ const result = await client.addSection({
4818
+ sectionType: params.sectionType,
4819
+ content: section.content,
4820
+ title: section.title,
4821
+ description: params.description,
4822
+ docid,
4823
+ tags: params.tags,
4824
+ replace: params.replace,
4825
+ level: section.level,
4826
+ parent: parent ?? void 0
4827
+ });
4828
+ kbids.push(result.items[0] ?? "");
4829
+ }
4830
+ return { kbids, docTitle };
4831
+ }
4832
+
4833
+ // src/shared/mcp/functions/store-split-learn-sections.function.ts
4834
+ async function storeSplitLearnSections(client, splitResult, params) {
4835
+ const { kbids, docTitle } = await learnWithSplitting(
4836
+ client,
4837
+ splitResult,
4838
+ params
4839
+ );
4840
+ await client.groupSections(docTitle, kbids);
4841
+ return {
4842
+ items: kbids,
4843
+ total: kbids.length,
4844
+ offset: 0,
4845
+ superseded: null,
4846
+ warnings: [],
4847
+ near_duplicates: []
4848
+ };
4849
+ }
4850
+
4851
+ // src/shared/mcp/functions/build-learn-payload.function.ts
4852
+ function buildLearnPayload(result) {
4853
+ return {
4854
+ items: result.items,
4855
+ total: result.total,
4856
+ offset: result.offset,
4857
+ superseded: result.superseded ?? null,
4858
+ warnings: result.warnings ?? [],
4859
+ near_duplicates: result.near_duplicates ?? []
4860
+ };
4861
+ }
4862
+
4863
+ // src/shared/mcp/functions/handle-tool-learn.function.ts
4864
+ async function handleToolLearn(client, args) {
4865
+ const learnArgs = parseLearnArgs(args);
4866
+ const { sectionType, title, level, ...shared } = learnArgs;
4867
+ if (sectionType.startsWith("image/") && !title) {
4868
+ return {
4869
+ content: [
4870
+ {
4871
+ type: "text",
4872
+ text: "title is required for image/* types"
4873
+ }
4874
+ ],
4875
+ isError: true
4876
+ };
4877
+ }
4878
+ const content = unwrapJsonContent(asString2(args["content"], ""));
4879
+ const splitResult = determineLearnSplit(content, sectionType, title);
4880
+ const result = splitResult ? await storeSplitLearnSections(client, splitResult, {
4881
+ sectionType,
4882
+ ...shared
4883
+ }) : await client.addSection({
4884
+ sectionType,
4885
+ content,
4886
+ title,
4887
+ level,
4888
+ ...shared
4889
+ });
4890
+ return {
4891
+ content: [
4892
+ {
4893
+ type: "text",
4894
+ text: JSON.stringify(buildLearnPayload(result))
4895
+ }
4896
+ ]
4897
+ };
4898
+ }
4899
+
4900
+ // src/shared/mcp/functions/handle-tool-skill-learn.function.ts
4901
+ async function handleToolSkillLearn(client, args) {
4902
+ const name = asString2(args["name"], "");
4903
+ if (!name) {
4904
+ return {
4905
+ content: [{ type: "text", text: "name is required" }],
4906
+ isError: true
4907
+ };
4908
+ }
4909
+ const body = asString2(args["body"], "");
4910
+ if (!body) {
4911
+ return {
4912
+ content: [{ type: "text", text: "body is required" }],
4913
+ isError: true
4914
+ };
4915
+ }
4916
+ const description = typeof args["description"] === "string" ? args["description"] : void 0;
4917
+ const skillArgs = extractSkillArguments(body);
4918
+ const skill = {
4919
+ name,
4920
+ description,
4921
+ arguments: skillArgs.length > 0 ? skillArgs : void 0,
4922
+ body
4923
+ };
4924
+ const json = serializeSkillContent(skill);
4925
+ const result = await client.addSection({
4926
+ sectionType: "skill",
4927
+ content: json,
4928
+ title: name,
4929
+ description
4930
+ });
4931
+ return {
4932
+ content: [
4933
+ {
4934
+ type: "text",
4935
+ text: `Created skill "${name}" (kbid: ${result.items[0]})`
4936
+ }
4937
+ ]
4938
+ };
4939
+ }
4940
+
4941
+ // src/shared/mcp/functions/handle-tool-call.function.ts
4942
+ function asStringArray2(value) {
4943
+ if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
4944
+ return value;
4945
+ }
4946
+ return [];
4947
+ }
4436
4948
  async function handleToolCall(client, name, args) {
4437
4949
  log("tool call: " + name, 0 /* DEBUG */);
4438
4950
  try {
@@ -4440,7 +4952,7 @@ async function handleToolCall(client, name, args) {
4440
4952
  case "search":
4441
4953
  return await handleSearch2(client, args);
4442
4954
  case "learn":
4443
- return await handleLearn2(client, args);
4955
+ return await handleToolLearn(client, args);
4444
4956
  case "unlearn":
4445
4957
  return await handleUnlearn2(client, args);
4446
4958
  case "retrieve":
@@ -4460,9 +4972,9 @@ async function handleToolCall(client, name, args) {
4460
4972
  case "export":
4461
4973
  return await handleExport2(client, args);
4462
4974
  case "skill-learn":
4463
- return await handleSkillLearn(client, args);
4975
+ return await handleToolSkillLearn(client, args);
4464
4976
  case "skill-list":
4465
- return await handleSkillList(client);
4977
+ return await handleSkillList(client, args);
4466
4978
  case "skill-get":
4467
4979
  return await handleSkillGet(client, args);
4468
4980
  case "skill-delete":
@@ -4470,7 +4982,7 @@ async function handleToolCall(client, name, args) {
4470
4982
  case "agent-create":
4471
4983
  return await handleAgentCreate(client, args);
4472
4984
  case "agent-list":
4473
- return await handleAgentList(client);
4985
+ return await handleAgentList(client, args);
4474
4986
  case "agent-get":
4475
4987
  return await handleAgentGet(client, args);
4476
4988
  case "agent-delete":
@@ -4525,47 +5037,6 @@ async function handleSearch2(client, args) {
4525
5037
  ]
4526
5038
  };
4527
5039
  }
4528
- async function handleLearn2(client, args) {
4529
- const sectionType = asString2(args["type"], "text/markdown");
4530
- const title = typeof args["title"] === "string" ? args["title"] : void 0;
4531
- const description = typeof args["description"] === "string" ? args["description"] : void 0;
4532
- const docid = typeof args["docid"] === "string" ? args["docid"] : void 0;
4533
- const tags = asStringArrayOrUndefined(args["tags"]);
4534
- const replace = asBooleanOrUndefined(args["replace"]);
4535
- if (sectionType.startsWith("image/") && !title) {
4536
- return {
4537
- content: [
4538
- {
4539
- type: "text",
4540
- text: "title is required for image/* types"
4541
- }
4542
- ],
4543
- isError: true
4544
- };
4545
- }
4546
- const rawContent = asString2(args["content"], "");
4547
- const content = unwrapJsonContent(rawContent);
4548
- const result = await client.addSection({
4549
- sectionType,
4550
- content,
4551
- title,
4552
- description,
4553
- docid,
4554
- tags,
4555
- replace
4556
- });
4557
- const payload = {
4558
- items: result.items,
4559
- total: result.total,
4560
- offset: result.offset,
4561
- superseded: result.superseded ?? null,
4562
- warnings: result.warnings ?? [],
4563
- near_duplicates: result.near_duplicates ?? []
4564
- };
4565
- return {
4566
- content: [{ type: "text", text: JSON.stringify(payload) }]
4567
- };
4568
- }
4569
5040
  async function handleUnlearn2(client, args) {
4570
5041
  const kbid = asString2(args["kbid"], "");
4571
5042
  await client.removeSection(kbid);
@@ -4672,56 +5143,17 @@ async function handleRebuild2(client) {
4672
5143
  const text = `Rebuild complete: ${String(report.sections_reindexed)} sections reindexed, ${String(report.terms_indexed)} terms indexed.`;
4673
5144
  return { content: [{ type: "text", text }] };
4674
5145
  }
4675
- async function handleSkillLearn(client, args) {
4676
- const name = asString2(args["name"], "");
4677
- if (!name) {
4678
- return {
4679
- content: [{ type: "text", text: "name is required" }],
4680
- isError: true
4681
- };
4682
- }
4683
- const body = asString2(args["body"], "");
4684
- if (!body) {
4685
- return {
4686
- content: [{ type: "text", text: "body is required" }],
4687
- isError: true
4688
- };
4689
- }
4690
- const description = typeof args["description"] === "string" ? args["description"] : void 0;
4691
- const skillArgs = extractSkillArguments(body);
4692
- const skill = {
4693
- name,
4694
- description,
4695
- arguments: skillArgs.length > 0 ? skillArgs : void 0,
4696
- body
4697
- };
4698
- const json = serializeSkillContent(skill);
4699
- const result = await client.addSection({
4700
- sectionType: "skill",
4701
- content: json,
4702
- title: name,
4703
- description
4704
- });
4705
- return {
4706
- content: [
4707
- {
4708
- type: "text",
4709
- text: `Created skill "${name}" (kbid: ${result.items[0]})`
4710
- }
4711
- ]
4712
- };
4713
- }
4714
- async function handleSkillList(client) {
5146
+ async function handleSkillList(client, args) {
4715
5147
  const records = await client.listByType("skill");
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}`;
4720
- });
4721
- const text = [`${String(records.length)} skills:`, ...lines].join(
4722
- "\n"
5148
+ const query = typeof args["query"] === "string" ? args["query"].toLowerCase() : "";
5149
+ const filtered = records.filter(
5150
+ (r) => matchesQuery(query, r.title, r.description)
4723
5151
  );
4724
- return { content: [{ type: "text", text }] };
5152
+ const names = filtered.map((r) => r.title ?? "");
5153
+ const data = { items: names, total: names.length, offset: 0 };
5154
+ return {
5155
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
5156
+ };
4725
5157
  }
4726
5158
  async function handleSkillGet(client, args) {
4727
5159
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;