@dikolab/kbdb 0.6.3 → 0.6.4

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 (26) hide show
  1. package/dist/cli.cjs +268 -144
  2. package/dist/cli.cjs.map +4 -4
  3. package/dist/cli.mjs +225 -101
  4. package/dist/cli.mjs.map +4 -4
  5. package/dist/mod.cjs +1 -1
  6. package/dist/mod.cjs.map +1 -1
  7. package/dist/mod.mjs +1 -1
  8. package/dist/mod.mjs.map +1 -1
  9. package/dist/src/shared/cli/functions/learn-markdown-document.function.d.ts +22 -0
  10. package/dist/src/shared/cli/functions/learn-target-content.function.d.ts +24 -0
  11. package/dist/src/shared/cli/functions/map-add-section-result.function.d.ts +12 -0
  12. package/dist/src/shared/cli/functions/run-agent-list.function.d.ts +13 -9
  13. package/dist/src/shared/cli/functions/run-learn.function.d.ts +10 -59
  14. package/dist/src/shared/cli/functions/run-skill-list.function.d.ts +11 -7
  15. package/dist/src/shared/cli/functions/split-markdown-sections.function.d.ts +12 -0
  16. package/dist/src/shared/cli/typings/learn-client.interface.d.ts +14 -28
  17. package/dist/src/shared/cli/typings/markdown-section.interface.d.ts +23 -0
  18. package/dist/src/shared/mcp/functions/handle-agent-list.function.d.ts +11 -21
  19. package/dist/src/shared/mcp/functions/handle-tool-call.function.d.ts +1 -15
  20. package/dist/src/shared/mcp/functions/handle-tool-skill-list.function.d.ts +13 -3
  21. package/dist/src/shared/query-filter/functions/matches-query.function.d.ts +17 -0
  22. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  23. package/dist/wasm/default-embedding/kbdb_default_embedding_bg.wasm +0 -0
  24. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  25. package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
  26. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  // src/cli.ts
5
5
  var import_node_fs10 = require("node:fs");
6
- var import_node_path14 = require("node:path");
6
+ var import_node_path15 = require("node:path");
7
7
 
8
8
  // src/shared/runtime/functions/detect-runtime.ts
9
9
  function detectRuntime() {
@@ -745,6 +745,120 @@ function computeDirectoryLevel(filePath, basePath) {
745
745
  return Math.min(depth + 1, MAX_LEVEL);
746
746
  }
747
747
 
748
+ // src/shared/cli/functions/learn-markdown-document.function.ts
749
+ var import_node_path3 = require("node:path");
750
+
751
+ // src/shared/cli/functions/split-markdown-sections.function.ts
752
+ var HEADING_PATTERN = /^(#{1,6})\s+(.+)$/;
753
+ function splitMarkdownSections(content) {
754
+ const sections = [];
755
+ let draft = { lines: [] };
756
+ let inCodeBlock = false;
757
+ for (const line of content.split("\n")) {
758
+ if (line.startsWith("```") || line.startsWith("~~~")) {
759
+ inCodeBlock = !inCodeBlock;
760
+ draft.lines.push(line);
761
+ continue;
762
+ }
763
+ const heading = inCodeBlock ? null : HEADING_PATTERN.exec(line);
764
+ if (heading) {
765
+ flushSection(draft, sections);
766
+ draft = {
767
+ title: heading[2].trim(),
768
+ level: heading[1].length,
769
+ lines: []
770
+ };
771
+ continue;
772
+ }
773
+ draft.lines.push(line);
774
+ }
775
+ flushSection(draft, sections);
776
+ return sections.length > 0 ? sections : [{ content: content.trimEnd() }];
777
+ }
778
+ function flushSection(draft, sections) {
779
+ const body = draft.lines.join("\n").trimEnd();
780
+ if (draft.title === void 0 && body.length === 0) return;
781
+ sections.push(
782
+ draft.title === void 0 ? { content: body } : { title: draft.title, level: draft.level, content: body }
783
+ );
784
+ }
785
+
786
+ // src/shared/cli/functions/map-add-section-result.function.ts
787
+ function mapAddSectionResult(result, path, size, sectionType) {
788
+ return {
789
+ kbid: result.items[0] ?? "",
790
+ indexed_terms_count: 0,
791
+ type: sectionType,
792
+ path,
793
+ size,
794
+ skipped: false,
795
+ superseded: result.superseded ?? null,
796
+ warnings: result.warnings ?? [],
797
+ near_duplicates: result.near_duplicates ?? []
798
+ };
799
+ }
800
+
801
+ // src/shared/cli/functions/learn-markdown-document.function.ts
802
+ async function learnMarkdownDocument(client, options, path, content, sourcePath) {
803
+ const sections = splitMarkdownSections(content);
804
+ const titled = sections.filter((s) => s.title !== void 0);
805
+ if (sections.length < 2 || titled.length === 0) return void 0;
806
+ const docTitle = titled[0]?.title ?? (0, import_node_path3.basename)(path);
807
+ const kbids = [];
808
+ const results = [];
809
+ for (const section of sections) {
810
+ const added = await client.addSection({
811
+ sectionType: "text",
812
+ content: section.content,
813
+ title: section.title,
814
+ description: options.description,
815
+ docid: path,
816
+ tags: options.tags,
817
+ replace: options.replace,
818
+ sourcePath,
819
+ level: section.level
820
+ });
821
+ kbids.push(added.items[0] ?? "");
822
+ results.push(
823
+ mapAddSectionResult(
824
+ added,
825
+ path,
826
+ section.content.length,
827
+ "text"
828
+ )
829
+ );
830
+ }
831
+ await client.groupSections(docTitle, kbids);
832
+ return results;
833
+ }
834
+
835
+ // src/shared/cli/functions/learn-target-content.function.ts
836
+ async function learnTargetContent(client, options, path, content, sourcePath, level) {
837
+ const markdownEligible = (path === "-" || path.endsWith(".md")) && !options.title;
838
+ if (markdownEligible) {
839
+ const split = await learnMarkdownDocument(
840
+ client,
841
+ options,
842
+ path,
843
+ content,
844
+ sourcePath
845
+ );
846
+ if (split) return split;
847
+ }
848
+ const result = await client.addSection({
849
+ sectionType: "text",
850
+ content,
851
+ title: options.title,
852
+ description: options.description,
853
+ docid: options.docid,
854
+ tags: options.tags,
855
+ replace: options.replace,
856
+ sourcePath,
857
+ level
858
+ });
859
+ return [mapAddSectionResult(result, path, content.length, "text")];
860
+ }
861
+
748
862
  // src/shared/cli/functions/run-learn.function.ts
749
863
  async function runLearn(client, options, io) {
750
864
  log(
@@ -759,37 +873,30 @@ async function runLearn(client, options, io) {
759
873
  if (stdinConsumed) continue;
760
874
  stdinConsumed = true;
761
875
  const content = await readStdin();
762
- const result = await client.addSection({
763
- sectionType: "text",
764
- content,
765
- title: options.title,
766
- description: options.description,
767
- docid: options.docid,
768
- tags: options.tags,
769
- replace: options.replace,
770
- level: options.level
771
- });
772
876
  results.push(
773
- mapAddSectionResult(result, "-", content.length, "text")
877
+ ...await learnTargetContent(
878
+ client,
879
+ options,
880
+ "-",
881
+ content,
882
+ void 0,
883
+ options.level
884
+ )
774
885
  );
775
886
  continue;
776
887
  }
777
888
  const files = collectFiles(target);
778
889
  for (const file of files) {
779
890
  const content = (0, import_node_fs2.readFileSync)(file, "utf-8");
780
- const result = await client.addSection({
781
- sectionType: "text",
782
- content,
783
- title: options.title,
784
- description: options.description,
785
- docid: options.docid,
786
- tags: options.tags,
787
- replace: options.replace,
788
- sourcePath: file,
789
- level: options.level ?? computeDirectoryLevel(file, target)
790
- });
791
891
  results.push(
792
- mapAddSectionResult(result, file, content.length, "text")
892
+ ...await learnTargetContent(
893
+ client,
894
+ options,
895
+ file,
896
+ content,
897
+ file,
898
+ options.level ?? computeDirectoryLevel(file, target)
899
+ )
793
900
  );
794
901
  }
795
902
  }
@@ -799,19 +906,6 @@ async function runLearn(client, options, io) {
799
906
  );
800
907
  return results;
801
908
  }
802
- function mapAddSectionResult(result, path, size, sectionType) {
803
- return {
804
- kbid: result.items[0] ?? "",
805
- indexed_terms_count: 0,
806
- type: sectionType,
807
- path,
808
- size,
809
- skipped: false,
810
- superseded: result.superseded ?? null,
811
- warnings: result.warnings ?? [],
812
- near_duplicates: result.near_duplicates ?? []
813
- };
814
- }
815
909
  function readProcessStdin() {
816
910
  return new Promise((resolve7) => {
817
911
  const chunks = [];
@@ -906,32 +1000,32 @@ async function runRebuild(client, _options) {
906
1000
 
907
1001
  // src/shared/cli/functions/find-db.function.ts
908
1002
  var import_node_fs3 = require("node:fs");
909
- var import_node_path3 = require("node:path");
1003
+ var import_node_path4 = require("node:path");
910
1004
  function findDb(dbPath) {
911
1005
  if (dbPath) {
912
- const target = (0, import_node_path3.join)((0, import_node_path3.resolve)(dbPath), ".kbdb");
1006
+ const target = (0, import_node_path4.join)((0, import_node_path4.resolve)(dbPath), ".kbdb");
913
1007
  return (0, import_node_fs3.existsSync)(target) ? target : null;
914
1008
  }
915
1009
  let current = process.cwd();
916
- let parent = (0, import_node_path3.dirname)(current);
1010
+ let parent = (0, import_node_path4.dirname)(current);
917
1011
  while (parent !== current) {
918
- const candidate2 = (0, import_node_path3.join)(current, ".kbdb");
1012
+ const candidate2 = (0, import_node_path4.join)(current, ".kbdb");
919
1013
  if ((0, import_node_fs3.existsSync)(candidate2)) return candidate2;
920
1014
  current = parent;
921
- parent = (0, import_node_path3.dirname)(current);
1015
+ parent = (0, import_node_path4.dirname)(current);
922
1016
  }
923
- const candidate = (0, import_node_path3.join)(current, ".kbdb");
1017
+ const candidate = (0, import_node_path4.join)(current, ".kbdb");
924
1018
  return (0, import_node_fs3.existsSync)(candidate) ? candidate : null;
925
1019
  }
926
1020
 
927
1021
  // src/shared/cli/functions/run-init.function.ts
928
1022
  var import_node_fs5 = require("node:fs");
929
- var import_node_path5 = require("node:path");
1023
+ var import_node_path6 = require("node:path");
930
1024
 
931
1025
  // src/shared/cli/functions/install-default-skills.function.ts
932
1026
  var import_node_crypto = require("node:crypto");
933
1027
  var import_node_fs4 = require("node:fs");
934
- var import_node_path4 = require("node:path");
1028
+ var import_node_path5 = require("node:path");
935
1029
  var BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
936
1030
  function base32Encode(bytes) {
937
1031
  let result = "";
@@ -992,8 +1086,8 @@ function installSkill(sectionsDir, skill) {
992
1086
  });
993
1087
  const kbid = computeSectionKbid("text/markdown", content);
994
1088
  const prefix = kbid.slice(0, 2);
995
- const shardDir = (0, import_node_path4.join)(sectionsDir, prefix);
996
- const filePath = (0, import_node_path4.join)(shardDir, `${kbid}.section`);
1089
+ const shardDir = (0, import_node_path5.join)(sectionsDir, prefix);
1090
+ const filePath = (0, import_node_path5.join)(shardDir, `${kbid}.section`);
997
1091
  if ((0, import_node_fs4.existsSync)(filePath)) {
998
1092
  return;
999
1093
  }
@@ -1021,7 +1115,7 @@ var DEFAULT_SKILLS = [
1021
1115
  }
1022
1116
  ];
1023
1117
  function installDefaultSkills(dbDir) {
1024
- const sectionsDir = (0, import_node_path4.join)(dbDir, "sections");
1118
+ const sectionsDir = (0, import_node_path5.join)(dbDir, "sections");
1025
1119
  for (const skill of DEFAULT_SKILLS) {
1026
1120
  installSkill(sectionsDir, skill);
1027
1121
  }
@@ -1046,19 +1140,19 @@ var GITIGNORE = [
1046
1140
  ].join("\n");
1047
1141
  function createScaffold(dbDir) {
1048
1142
  (0, import_node_fs5.mkdirSync)(dbDir, { recursive: true });
1049
- (0, import_node_fs5.mkdirSync)((0, import_node_path5.join)(dbDir, "sections"));
1050
- (0, import_node_fs5.mkdirSync)((0, import_node_path5.join)(dbDir, "documents"));
1051
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(dbDir, "catalog.toml"), CATALOG_TOML);
1052
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(dbDir, "corpus.bin"), "");
1053
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(dbDir, "inverted.idx"), "");
1054
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(dbDir, "positional.idx"), "");
1055
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(dbDir, "worker.toml"), WORKER_TOML);
1056
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(dbDir, ".gitignore"), GITIGNORE);
1143
+ (0, import_node_fs5.mkdirSync)((0, import_node_path6.join)(dbDir, "sections"));
1144
+ (0, import_node_fs5.mkdirSync)((0, import_node_path6.join)(dbDir, "documents"));
1145
+ (0, import_node_fs5.writeFileSync)((0, import_node_path6.join)(dbDir, "catalog.toml"), CATALOG_TOML);
1146
+ (0, import_node_fs5.writeFileSync)((0, import_node_path6.join)(dbDir, "corpus.bin"), "");
1147
+ (0, import_node_fs5.writeFileSync)((0, import_node_path6.join)(dbDir, "inverted.idx"), "");
1148
+ (0, import_node_fs5.writeFileSync)((0, import_node_path6.join)(dbDir, "positional.idx"), "");
1149
+ (0, import_node_fs5.writeFileSync)((0, import_node_path6.join)(dbDir, "worker.toml"), WORKER_TOML);
1150
+ (0, import_node_fs5.writeFileSync)((0, import_node_path6.join)(dbDir, ".gitignore"), GITIGNORE);
1057
1151
  }
1058
1152
  function runInit(options) {
1059
1153
  log("running init", 0 /* DEBUG */);
1060
- const basePath = options.dbPath ? (0, import_node_path5.resolve)(options.dbPath) : process.cwd();
1061
- const dbDir = (0, import_node_path5.join)(basePath, ".kbdb");
1154
+ const basePath = options.dbPath ? (0, import_node_path6.resolve)(options.dbPath) : process.cwd();
1155
+ const dbDir = (0, import_node_path6.join)(basePath, ".kbdb");
1062
1156
  try {
1063
1157
  if ((0, import_node_fs5.existsSync)(dbDir)) {
1064
1158
  return {
@@ -1199,28 +1293,27 @@ async function runSkillLearn(client, options) {
1199
1293
  };
1200
1294
  }
1201
1295
 
1296
+ // src/shared/query-filter/functions/matches-query.function.ts
1297
+ function matchesQuery(query, ...fields) {
1298
+ if (!query) return true;
1299
+ const needle = query.toLowerCase();
1300
+ return fields.some(
1301
+ (field) => typeof field === "string" && field.toLowerCase().includes(needle)
1302
+ );
1303
+ }
1304
+
1202
1305
  // src/shared/cli/functions/run-skill-list.function.ts
1203
- async function runSkillList(client, _options) {
1306
+ async function runSkillList(client, options) {
1204
1307
  log("running skill-list", 0 /* DEBUG */);
1205
1308
  const records = await client.listByType("skill");
1206
- const items = records.map((r) => {
1207
- let argumentCount = 0;
1208
- try {
1209
- const parsed = JSON.parse(r.content);
1210
- if (Array.isArray(parsed["arguments"])) {
1211
- argumentCount = parsed["arguments"].length;
1212
- }
1213
- } catch {
1214
- }
1215
- return {
1216
- kbid: r.kbid,
1217
- name: r.title,
1218
- description: r.description,
1219
- argumentCount
1220
- };
1221
- });
1309
+ const query = options.args.join(" ").toLowerCase();
1310
+ const filtered = records.filter(
1311
+ (r) => matchesQuery(query, r.title, r.description)
1312
+ );
1313
+ const names = filtered.map((r) => r.title);
1314
+ const data = { items: names, total: names.length, offset: 0 };
1222
1315
  return {
1223
- output: JSON.stringify(items, null, 2),
1316
+ output: JSON.stringify(data, null, 2),
1224
1317
  exitCode: 0
1225
1318
  };
1226
1319
  }
@@ -1349,16 +1442,15 @@ async function runAgentCreate(client, options) {
1349
1442
  }
1350
1443
 
1351
1444
  // src/shared/cli/functions/run-agent-list.function.ts
1352
- async function runAgentList(client, _options) {
1445
+ async function runAgentList(client, options) {
1353
1446
  log("running agent-list", 0 /* DEBUG */);
1354
1447
  const docs = await client.listDocumentsByType("agent");
1355
- const items = docs.map((d) => ({
1356
- docid: d.docid,
1357
- name: d.title,
1358
- skillCount: Math.max(0, d.sections.length - 1)
1359
- }));
1448
+ const query = options.args.join(" ").toLowerCase();
1449
+ const filtered = docs.filter((d) => matchesQuery(query, d.title));
1450
+ const names = filtered.map((d) => d.title);
1451
+ const data = { items: names, total: names.length, offset: 0 };
1360
1452
  return {
1361
- output: JSON.stringify(items, null, 2),
1453
+ output: JSON.stringify(data, null, 2),
1362
1454
  exitCode: 0
1363
1455
  };
1364
1456
  }
@@ -1464,7 +1556,7 @@ async function runAgentSearch(client, options) {
1464
1556
  }
1465
1557
 
1466
1558
  // src/shared/cli/functions/run-import.function.ts
1467
- var import_node_path6 = require("node:path");
1559
+ var import_node_path7 = require("node:path");
1468
1560
  async function runImport(client, options) {
1469
1561
  const from = options.from;
1470
1562
  if (!from) {
@@ -1473,7 +1565,7 @@ async function runImport(client, options) {
1473
1565
  throw new Error(msg);
1474
1566
  }
1475
1567
  return client.import({
1476
- from: (0, import_node_path6.resolve)(process.cwd(), from),
1568
+ from: (0, import_node_path7.resolve)(process.cwd(), from),
1477
1569
  dryRun: options.dryRun === true
1478
1570
  });
1479
1571
  }
@@ -1826,7 +1918,7 @@ async function handleTypedSearch(client, options, type) {
1826
1918
  }
1827
1919
 
1828
1920
  // src/shared/version/constants/version.constant.ts
1829
- var VERSION = "0.6.3";
1921
+ var VERSION = "0.6.4";
1830
1922
 
1831
1923
  // src/shared/cli/functions/generate-help.function.ts
1832
1924
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
@@ -1854,13 +1946,13 @@ var GROUP_SUBCOMMANDS = {
1854
1946
  ],
1855
1947
  skill: [
1856
1948
  ["learn", "Create a new skill definition"],
1857
- ["list", "List all skills"],
1949
+ ["list", "List skills (filter by keyword)"],
1858
1950
  ["get", "Retrieve a skill by name or kbid"],
1859
1951
  ["delete", "Remove a skill"]
1860
1952
  ],
1861
1953
  agent: [
1862
1954
  ["create", "Create an agent with skill refs"],
1863
- ["list", "List all agents"],
1955
+ ["list", "List agents (filter by keyword)"],
1864
1956
  ["get", "Retrieve an agent by docid"],
1865
1957
  ["delete", "Remove an agent"]
1866
1958
  ],
@@ -1905,9 +1997,9 @@ Usage: kbdb [options]
1905
1997
  kbdb <command> <subcommand> [options] ...[params]
1906
1998
 
1907
1999
  Knowledge Base:
1908
- learn Import content from files/stdin
2000
+ learn Import content (splits .md on headings)
1909
2001
  unlearn Remove sections by kbid
1910
- search Query the knowledge base
2002
+ search Query the knowledge base (sibling boost)
1911
2003
  recall Retrieve section context by kbid
1912
2004
  content Compose rendered content from ids
1913
2005
 
@@ -1926,13 +2018,13 @@ Maintenance:
1926
2018
 
1927
2019
  Skills:
1928
2020
  skill learn Create a new skill definition
1929
- skill list List all skills
2021
+ skill list List skills (filter by keyword)
1930
2022
  skill get Retrieve a skill by name or kbid
1931
2023
  skill delete Remove a skill
1932
2024
 
1933
2025
  Agents:
1934
2026
  agent create Create an agent with skill refs
1935
- agent list List all agents
2027
+ agent list List agents (filter by keyword)
1936
2028
  agent get Retrieve an agent by docid
1937
2029
  agent delete Remove an agent
1938
2030
 
@@ -2004,9 +2096,17 @@ Options:
2004
2096
  --replace Supersede existing section by source key
2005
2097
  --db <path> Path to .kbdb directory
2006
2098
 
2099
+ Markdown splitting:
2100
+ .md files are automatically split on ATX
2101
+ headings (# through ######) into independent
2102
+ sections. Each section uses its heading as
2103
+ the title and shares a docid for document
2104
+ grouping. Providing --title imports the file
2105
+ as a single section (skips splitting).
2106
+ Non-.md files are never split.
2107
+
2007
2108
  Output:
2008
- JSON array of { kbid, path, size, skipped,
2009
- superseded, warnings, near_duplicates }
2109
+ { items, total, offset } envelope
2010
2110
 
2011
2111
  Examples:
2012
2112
  kbdb learn README.md
@@ -2045,8 +2145,14 @@ Options:
2045
2145
  --content, -c Hydrate results with section content
2046
2146
  --db <path> Path to .kbdb directory
2047
2147
 
2148
+ Scoring:
2149
+ Results are ranked by BM25F. Sections from
2150
+ the same document (shared docid) receive a
2151
+ sibling score boost when multiple sections
2152
+ match the query.
2153
+
2048
2154
  Output:
2049
- JSON array of { kbid, score, preview, matches }
2155
+ { items, total, offset } envelope
2050
2156
 
2051
2157
  Examples:
2052
2158
  kbdb search "machine learning"
@@ -2255,16 +2361,20 @@ Examples:
2255
2361
  `,
2256
2362
  "skill-list": `kbdb skill list - List all skills
2257
2363
 
2258
- Usage: kbdb skill list [options]
2364
+ Usage: kbdb skill list [options] [keywords...]
2365
+
2366
+ Arguments:
2367
+ [keywords...] Case-insensitive substring filter on name or description
2259
2368
 
2260
2369
  Options:
2261
2370
  --db <path> Path to .kbdb directory
2262
2371
 
2263
2372
  Output:
2264
- JSON array of { kbid, name, description, argumentCount }
2373
+ JSON { items, total, offset }
2265
2374
 
2266
2375
  Examples:
2267
2376
  kbdb skill list
2377
+ kbdb skill list summarize
2268
2378
  kbdb skill list --db /path/to/project
2269
2379
  `,
2270
2380
  "skill-get": `kbdb skill get - Retrieve a skill by name or kbid
@@ -2320,16 +2430,20 @@ Examples:
2320
2430
  `,
2321
2431
  "agent-list": `kbdb agent list - List all agents
2322
2432
 
2323
- Usage: kbdb agent list [options]
2433
+ Usage: kbdb agent list [options] [keywords...]
2434
+
2435
+ Arguments:
2436
+ [keywords...] Case-insensitive substring filter on agent name
2324
2437
 
2325
2438
  Options:
2326
2439
  --db <path> Path to .kbdb directory
2327
2440
 
2328
2441
  Output:
2329
- JSON array of { docid, name, description, skillCount }
2442
+ JSON { items, total, offset }
2330
2443
 
2331
2444
  Examples:
2332
2445
  kbdb agent list
2446
+ kbdb agent list reviewer
2333
2447
  kbdb agent list --db /path/to/project
2334
2448
  `,
2335
2449
  "agent-get": `kbdb agent get - Retrieve an agent by docid
@@ -2468,7 +2582,7 @@ var import_node_net = require("node:net");
2468
2582
 
2469
2583
  // src/shared/worker-client/functions/discover-daemon.function.ts
2470
2584
  var import_node_fs6 = require("node:fs");
2471
- var import_node_path7 = require("node:path");
2585
+ var import_node_path8 = require("node:path");
2472
2586
  var import_node_os = require("node:os");
2473
2587
 
2474
2588
  // src/shared/platform/functions/is-process-alive.function.ts
@@ -2484,7 +2598,7 @@ function isProcessAlive(pid) {
2484
2598
  // src/shared/worker-client/functions/discover-daemon.function.ts
2485
2599
  function discoverDaemon(contextId) {
2486
2600
  log("discovering daemon for context: " + contextId, 0 /* DEBUG */);
2487
- const pidPath = (0, import_node_path7.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.pid`);
2601
+ const pidPath = (0, import_node_path8.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.pid`);
2488
2602
  if (!(0, import_node_fs6.existsSync)(pidPath)) {
2489
2603
  log(
2490
2604
  "no daemon PID file found for context: " + contextId,
@@ -2506,8 +2620,8 @@ function discoverDaemon(contextId) {
2506
2620
  return null;
2507
2621
  }
2508
2622
  log("daemon found: pid=" + pid.toString(), 0 /* DEBUG */);
2509
- const socketPath = (0, import_node_path7.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.sock`);
2510
- const crashPath = (0, import_node_path7.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.crash`);
2623
+ const socketPath = (0, import_node_path8.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.sock`);
2624
+ const crashPath = (0, import_node_path8.join)((0, import_node_os.tmpdir)(), `kbdb-${contextId}.crash`);
2511
2625
  try {
2512
2626
  (0, import_node_fs6.unlinkSync)(crashPath);
2513
2627
  } catch {
@@ -3028,10 +3142,10 @@ var WorkerClient = class {
3028
3142
 
3029
3143
  // src/shared/worker-client/functions/resolve-db-path.function.ts
3030
3144
  var import_node_fs7 = require("node:fs");
3031
- var import_node_path8 = require("node:path");
3145
+ var import_node_path9 = require("node:path");
3032
3146
  function resolveDbPath(targetDir) {
3033
- const base = targetDir ? (0, import_node_path8.resolve)(targetDir) : process.cwd();
3034
- const dbPath = (0, import_node_path8.join)(base, ".kbdb");
3147
+ const base = targetDir ? (0, import_node_path9.resolve)(targetDir) : process.cwd();
3148
+ const dbPath = (0, import_node_path9.join)(base, ".kbdb");
3035
3149
  if (!(0, import_node_fs7.existsSync)(dbPath)) {
3036
3150
  const msg = `database not found: ${dbPath}`;
3037
3151
  log(msg, 3 /* ERROR */);
@@ -3098,13 +3212,13 @@ function spawnDaemon(contextPath, runtime, workerScript) {
3098
3212
 
3099
3213
  // src/shared/worker-client/functions/wait-for-daemon.function.ts
3100
3214
  var import_node_fs8 = require("node:fs");
3101
- var import_node_path9 = require("node:path");
3215
+ var import_node_path10 = require("node:path");
3102
3216
  var import_node_os2 = require("node:os");
3103
3217
  var DEFAULT_TIMEOUT_MS = 1e4;
3104
3218
  var POLL_INTERVAL_MS = 100;
3105
3219
  async function waitForDaemon(contextId, timeoutMs = DEFAULT_TIMEOUT_MS) {
3106
3220
  log("waiting for daemon: contextId=" + contextId, 0 /* DEBUG */);
3107
- const pidPath = (0, import_node_path9.join)((0, import_node_os2.tmpdir)(), `kbdb-${contextId}.pid`);
3221
+ const pidPath = (0, import_node_path10.join)((0, import_node_os2.tmpdir)(), `kbdb-${contextId}.pid`);
3108
3222
  const start = Date.now();
3109
3223
  let iteration = 0;
3110
3224
  while (Date.now() - start < timeoutMs) {
@@ -3132,10 +3246,10 @@ async function waitForDaemon(contextId, timeoutMs = DEFAULT_TIMEOUT_MS) {
3132
3246
 
3133
3247
  // src/shared/worker-client/functions/read-crash-reason.function.ts
3134
3248
  var import_node_fs9 = require("node:fs");
3135
- var import_node_path10 = require("node:path");
3249
+ var import_node_path11 = require("node:path");
3136
3250
  var import_node_os3 = require("node:os");
3137
3251
  function readCrashReason(contextId) {
3138
- const crashPath = (0, import_node_path10.join)((0, import_node_os3.tmpdir)(), `kbdb-${contextId}.crash`);
3252
+ const crashPath = (0, import_node_path11.join)((0, import_node_os3.tmpdir)(), `kbdb-${contextId}.crash`);
3139
3253
  if (!(0, import_node_fs9.existsSync)(crashPath)) {
3140
3254
  return "daemon failed to start (no details available)";
3141
3255
  }
@@ -3150,11 +3264,11 @@ function readCrashReason(contextId) {
3150
3264
  }
3151
3265
 
3152
3266
  // src/shared/worker-client/functions/resolve-spawn-script.function.ts
3153
- var import_node_path12 = require("node:path");
3267
+ var import_node_path13 = require("node:path");
3154
3268
  var import_node_url = require("node:url");
3155
3269
 
3156
3270
  // src/shared/dir-resolver/functions/join-path.function.ts
3157
- var import_node_path11 = require("node:path");
3271
+ var import_node_path12 = require("node:path");
3158
3272
  function isRemoteUrl(path) {
3159
3273
  return path.startsWith("http://") || path.startsWith("https://");
3160
3274
  }
@@ -3163,7 +3277,7 @@ function joinPath(base, ...segments) {
3163
3277
  const trailing = base.endsWith("/") ? "" : "/";
3164
3278
  return base + trailing + segments.join("/");
3165
3279
  }
3166
- return (0, import_node_path11.join)(base, ...segments);
3280
+ return (0, import_node_path12.join)(base, ...segments);
3167
3281
  }
3168
3282
 
3169
3283
  // src/shared/dir-resolver/functions/resolve-worker-script.function.ts
@@ -3181,15 +3295,15 @@ function resolveSpawnScript(runtime) {
3181
3295
  }
3182
3296
  function resolveScriptDir2(runtime) {
3183
3297
  if (typeof __dirname !== "undefined") return __dirname;
3184
- const thisDir = (0, import_node_path12.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
3298
+ const thisDir = (0, import_node_path13.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
3185
3299
  if (runtime === "deno") {
3186
- return (0, import_node_path12.resolve)(thisDir, "..", "..", "..");
3300
+ return (0, import_node_path13.resolve)(thisDir, "..", "..", "..");
3187
3301
  }
3188
3302
  return thisDir;
3189
3303
  }
3190
3304
 
3191
3305
  // src/shared/platform/functions/get-ipc-path.function.ts
3192
- var import_node_path13 = require("node:path");
3306
+ var import_node_path14 = require("node:path");
3193
3307
 
3194
3308
  // src/shared/platform/functions/get-tmpdir.function.ts
3195
3309
  var import_node_os4 = require("node:os");
@@ -3202,7 +3316,7 @@ function getIpcPath(ctx) {
3202
3316
  if (process.platform === "win32") {
3203
3317
  return `\\\\.\\pipe\\kbdb-${ctx}`;
3204
3318
  }
3205
- return (0, import_node_path13.join)(getTmpdir(), `kbdb-${ctx}.sock`);
3319
+ return (0, import_node_path14.join)(getTmpdir(), `kbdb-${ctx}.sock`);
3206
3320
  }
3207
3321
 
3208
3322
  // src/shared/worker-client/functions/create-worker-client.function.ts
@@ -3670,7 +3784,7 @@ function createToolDefinitions() {
3670
3784
  return [
3671
3785
  {
3672
3786
  name: "search",
3673
- 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.",
3787
+ description: "Query the knowledge base by keywords. Returns ranked results with heading, snippet, and matched terms. 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.",
3674
3788
  inputSchema: {
3675
3789
  type: "object",
3676
3790
  properties: {
@@ -3713,7 +3827,7 @@ function createToolDefinitions() {
3713
3827
  },
3714
3828
  {
3715
3829
  name: "learn",
3716
- description: "Import content into the knowledge base. Use for durable facts, decisions, and corrections. Use replace when updating existing knowledge. Attach tags for scoping.",
3830
+ description: "Import content into the knowledge base. Markdown content is automatically split on ATX headings into independent sections that share a docid. Providing title imports as a single section (skips splitting). Use replace when updating existing knowledge. Attach tags for scoping.",
3717
3831
  inputSchema: {
3718
3832
  type: "object",
3719
3833
  properties: {
@@ -3727,7 +3841,7 @@ function createToolDefinitions() {
3727
3841
  },
3728
3842
  title: {
3729
3843
  type: "string",
3730
- description: "Section title. Required for image/* types."
3844
+ description: "Section title. Skips markdown splitting when set. Required for image/* types."
3731
3845
  },
3732
3846
  description: {
3733
3847
  type: "string",
@@ -3735,7 +3849,7 @@ function createToolDefinitions() {
3735
3849
  },
3736
3850
  docid: {
3737
3851
  type: "string",
3738
- description: "Document ID to group under"
3852
+ description: "Document ID to group sections under. Set automatically when markdown splitting is active."
3739
3853
  },
3740
3854
  tags: {
3741
3855
  type: "array",
@@ -3964,10 +4078,15 @@ function createToolDefinitions() {
3964
4078
  },
3965
4079
  {
3966
4080
  name: "skill-list",
3967
- description: "List all skills in the knowledge base.",
4081
+ description: "List skill definitions stored in the knowledge base, optionally filtered by a substring query.",
3968
4082
  inputSchema: {
3969
4083
  type: "object",
3970
- properties: {}
4084
+ properties: {
4085
+ query: {
4086
+ type: "string",
4087
+ description: "Case-insensitive substring filter on skill name or description."
4088
+ }
4089
+ }
3971
4090
  },
3972
4091
  annotations: {
3973
4092
  title: "List Skills",
@@ -4058,10 +4177,15 @@ function createToolDefinitions() {
4058
4177
  },
4059
4178
  {
4060
4179
  name: "agent-list",
4061
- description: "List all agents in the knowledge base.",
4180
+ description: "List agent documents in the knowledge base, optionally filtered by a substring query.",
4062
4181
  inputSchema: {
4063
4182
  type: "object",
4064
- properties: {}
4183
+ properties: {
4184
+ query: {
4185
+ type: "string",
4186
+ description: "Case-insensitive substring filter on agent name or description."
4187
+ }
4188
+ }
4065
4189
  },
4066
4190
  annotations: {
4067
4191
  title: "List Agents",
@@ -4309,7 +4433,7 @@ async function readAgentDescription(client, sections) {
4309
4433
  return "";
4310
4434
  }
4311
4435
  }
4312
- async function handleAgentList(client) {
4436
+ async function handleAgentList(client, args = {}) {
4313
4437
  const docs = await client.listDocumentsByType("agent");
4314
4438
  const items = await Promise.all(
4315
4439
  docs.map(async (d) => ({
@@ -4317,17 +4441,17 @@ async function handleAgentList(client) {
4317
4441
  description: await readAgentDescription(client, d.sections)
4318
4442
  }))
4319
4443
  );
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"
4444
+ const query = typeof args["query"] === "string" ? args["query"].toLowerCase() : "";
4445
+ const filtered = items.filter(
4446
+ (item) => matchesQuery(query, item.name, item.description)
4325
4447
  );
4448
+ const names = filtered.map((item) => item.name);
4449
+ const data = { items: names, total: names.length, offset: 0 };
4326
4450
  return {
4327
4451
  content: [
4328
4452
  {
4329
4453
  type: "text",
4330
- text
4454
+ text: JSON.stringify(data, null, 2)
4331
4455
  }
4332
4456
  ]
4333
4457
  };
@@ -4629,7 +4753,7 @@ async function handleToolCall(client, name, args) {
4629
4753
  case "skill-learn":
4630
4754
  return await handleSkillLearn(client, args);
4631
4755
  case "skill-list":
4632
- return await handleSkillList(client);
4756
+ return await handleSkillList(client, args);
4633
4757
  case "skill-get":
4634
4758
  return await handleSkillGet(client, args);
4635
4759
  case "skill-delete":
@@ -4637,7 +4761,7 @@ async function handleToolCall(client, name, args) {
4637
4761
  case "agent-create":
4638
4762
  return await handleAgentCreate(client, args);
4639
4763
  case "agent-list":
4640
- return await handleAgentList(client);
4764
+ return await handleAgentList(client, args);
4641
4765
  case "agent-get":
4642
4766
  return await handleAgentGet(client, args);
4643
4767
  case "agent-delete":
@@ -4878,17 +5002,17 @@ async function handleSkillLearn(client, args) {
4878
5002
  ]
4879
5003
  };
4880
5004
  }
4881
- async function handleSkillList(client) {
5005
+ async function handleSkillList(client, args) {
4882
5006
  const records = await client.listByType("skill");
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}`;
4887
- });
4888
- const text = [`${String(records.length)} skills:`, ...lines].join(
4889
- "\n"
5007
+ const query = typeof args["query"] === "string" ? args["query"].toLowerCase() : "";
5008
+ const filtered = records.filter(
5009
+ (r) => matchesQuery(query, r.title, r.description)
4890
5010
  );
4891
- return { content: [{ type: "text", text }] };
5011
+ const names = filtered.map((r) => r.title ?? "");
5012
+ const data = { items: names, total: names.length, offset: 0 };
5013
+ return {
5014
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
5015
+ };
4892
5016
  }
4893
5017
  async function handleSkillGet(client, args) {
4894
5018
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;
@@ -5924,7 +6048,7 @@ var deps = {
5924
6048
  log("starting MCP server", 1 /* INFO */);
5925
6049
  const client = await createWorkerClient({ contextPath });
5926
6050
  try {
5927
- const tomlPath = (0, import_node_path14.join)(contextPath, "worker.toml");
6051
+ const tomlPath = (0, import_node_path15.join)(contextPath, "worker.toml");
5928
6052
  let autoCaptureConfig;
5929
6053
  try {
5930
6054
  const tomlText = (0, import_node_fs10.readFileSync)(tomlPath, "utf-8");