@dikolab/kbdb 0.6.2 → 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 (27) 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/kbdb-worker.cjs +0 -0
  6. package/dist/mod.cjs +1 -1
  7. package/dist/mod.cjs.map +1 -1
  8. package/dist/mod.mjs +1 -1
  9. package/dist/mod.mjs.map +1 -1
  10. package/dist/src/shared/cli/functions/learn-markdown-document.function.d.ts +22 -0
  11. package/dist/src/shared/cli/functions/learn-target-content.function.d.ts +24 -0
  12. package/dist/src/shared/cli/functions/map-add-section-result.function.d.ts +12 -0
  13. package/dist/src/shared/cli/functions/run-agent-list.function.d.ts +13 -9
  14. package/dist/src/shared/cli/functions/run-learn.function.d.ts +10 -59
  15. package/dist/src/shared/cli/functions/run-skill-list.function.d.ts +11 -7
  16. package/dist/src/shared/cli/functions/split-markdown-sections.function.d.ts +12 -0
  17. package/dist/src/shared/cli/typings/learn-client.interface.d.ts +14 -28
  18. package/dist/src/shared/cli/typings/markdown-section.interface.d.ts +23 -0
  19. package/dist/src/shared/mcp/functions/handle-agent-list.function.d.ts +11 -21
  20. package/dist/src/shared/mcp/functions/handle-tool-call.function.d.ts +1 -15
  21. package/dist/src/shared/mcp/functions/handle-tool-skill-list.function.d.ts +13 -3
  22. package/dist/src/shared/query-filter/functions/matches-query.function.d.ts +17 -0
  23. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  24. package/dist/wasm/default-embedding/kbdb_default_embedding_bg.wasm +0 -0
  25. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  26. package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
  27. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -659,6 +659,120 @@ function computeDirectoryLevel(filePath, basePath) {
659
659
  return Math.min(depth + 1, MAX_LEVEL);
660
660
  }
661
661
 
662
+ // src/shared/cli/functions/learn-markdown-document.function.ts
663
+ import { basename as basename2 } from "node:path";
664
+
665
+ // src/shared/cli/functions/split-markdown-sections.function.ts
666
+ var HEADING_PATTERN = /^(#{1,6})\s+(.+)$/;
667
+ function splitMarkdownSections(content) {
668
+ const sections = [];
669
+ let draft = { lines: [] };
670
+ let inCodeBlock = false;
671
+ for (const line of content.split("\n")) {
672
+ if (line.startsWith("```") || line.startsWith("~~~")) {
673
+ inCodeBlock = !inCodeBlock;
674
+ draft.lines.push(line);
675
+ continue;
676
+ }
677
+ const heading = inCodeBlock ? null : HEADING_PATTERN.exec(line);
678
+ if (heading) {
679
+ flushSection(draft, sections);
680
+ draft = {
681
+ title: heading[2].trim(),
682
+ level: heading[1].length,
683
+ lines: []
684
+ };
685
+ continue;
686
+ }
687
+ draft.lines.push(line);
688
+ }
689
+ flushSection(draft, sections);
690
+ return sections.length > 0 ? sections : [{ content: content.trimEnd() }];
691
+ }
692
+ function flushSection(draft, sections) {
693
+ const body = draft.lines.join("\n").trimEnd();
694
+ if (draft.title === void 0 && body.length === 0) return;
695
+ sections.push(
696
+ draft.title === void 0 ? { content: body } : { title: draft.title, level: draft.level, content: body }
697
+ );
698
+ }
699
+
700
+ // src/shared/cli/functions/map-add-section-result.function.ts
701
+ function mapAddSectionResult(result, path, size, sectionType) {
702
+ return {
703
+ kbid: result.items[0] ?? "",
704
+ indexed_terms_count: 0,
705
+ type: sectionType,
706
+ path,
707
+ size,
708
+ skipped: false,
709
+ superseded: result.superseded ?? null,
710
+ warnings: result.warnings ?? [],
711
+ near_duplicates: result.near_duplicates ?? []
712
+ };
713
+ }
714
+
715
+ // src/shared/cli/functions/learn-markdown-document.function.ts
716
+ async function learnMarkdownDocument(client, options, path, content, sourcePath) {
717
+ const sections = splitMarkdownSections(content);
718
+ const titled = sections.filter((s) => s.title !== void 0);
719
+ if (sections.length < 2 || titled.length === 0) return void 0;
720
+ const docTitle = titled[0]?.title ?? basename2(path);
721
+ const kbids = [];
722
+ const results = [];
723
+ for (const section of sections) {
724
+ const added = await client.addSection({
725
+ sectionType: "text",
726
+ content: section.content,
727
+ title: section.title,
728
+ description: options.description,
729
+ docid: path,
730
+ tags: options.tags,
731
+ replace: options.replace,
732
+ sourcePath,
733
+ level: section.level
734
+ });
735
+ kbids.push(added.items[0] ?? "");
736
+ results.push(
737
+ mapAddSectionResult(
738
+ added,
739
+ path,
740
+ section.content.length,
741
+ "text"
742
+ )
743
+ );
744
+ }
745
+ await client.groupSections(docTitle, kbids);
746
+ return results;
747
+ }
748
+
749
+ // src/shared/cli/functions/learn-target-content.function.ts
750
+ async function learnTargetContent(client, options, path, content, sourcePath, level) {
751
+ const markdownEligible = (path === "-" || path.endsWith(".md")) && !options.title;
752
+ if (markdownEligible) {
753
+ const split = await learnMarkdownDocument(
754
+ client,
755
+ options,
756
+ path,
757
+ content,
758
+ sourcePath
759
+ );
760
+ if (split) return split;
761
+ }
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
+ sourcePath,
771
+ level
772
+ });
773
+ return [mapAddSectionResult(result, path, content.length, "text")];
774
+ }
775
+
662
776
  // src/shared/cli/functions/run-learn.function.ts
663
777
  async function runLearn(client, options, io) {
664
778
  log(
@@ -673,37 +787,30 @@ async function runLearn(client, options, io) {
673
787
  if (stdinConsumed) continue;
674
788
  stdinConsumed = true;
675
789
  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
790
  results.push(
687
- mapAddSectionResult(result, "-", content.length, "text")
791
+ ...await learnTargetContent(
792
+ client,
793
+ options,
794
+ "-",
795
+ content,
796
+ void 0,
797
+ options.level
798
+ )
688
799
  );
689
800
  continue;
690
801
  }
691
802
  const files = collectFiles(target);
692
803
  for (const file of files) {
693
804
  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
805
  results.push(
706
- mapAddSectionResult(result, file, content.length, "text")
806
+ ...await learnTargetContent(
807
+ client,
808
+ options,
809
+ file,
810
+ content,
811
+ file,
812
+ options.level ?? computeDirectoryLevel(file, target)
813
+ )
707
814
  );
708
815
  }
709
816
  }
@@ -713,19 +820,6 @@ async function runLearn(client, options, io) {
713
820
  );
714
821
  return results;
715
822
  }
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
823
  function readProcessStdin() {
730
824
  return new Promise((resolve7) => {
731
825
  const chunks = [];
@@ -1113,28 +1207,27 @@ async function runSkillLearn(client, options) {
1113
1207
  };
1114
1208
  }
1115
1209
 
1210
+ // src/shared/query-filter/functions/matches-query.function.ts
1211
+ function matchesQuery(query, ...fields) {
1212
+ if (!query) return true;
1213
+ const needle = query.toLowerCase();
1214
+ return fields.some(
1215
+ (field) => typeof field === "string" && field.toLowerCase().includes(needle)
1216
+ );
1217
+ }
1218
+
1116
1219
  // src/shared/cli/functions/run-skill-list.function.ts
1117
- async function runSkillList(client, _options) {
1220
+ async function runSkillList(client, options) {
1118
1221
  log("running skill-list", 0 /* DEBUG */);
1119
1222
  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
- });
1223
+ const query = options.args.join(" ").toLowerCase();
1224
+ const filtered = records.filter(
1225
+ (r) => matchesQuery(query, r.title, r.description)
1226
+ );
1227
+ const names = filtered.map((r) => r.title);
1228
+ const data = { items: names, total: names.length, offset: 0 };
1136
1229
  return {
1137
- output: JSON.stringify(items, null, 2),
1230
+ output: JSON.stringify(data, null, 2),
1138
1231
  exitCode: 0
1139
1232
  };
1140
1233
  }
@@ -1263,16 +1356,15 @@ async function runAgentCreate(client, options) {
1263
1356
  }
1264
1357
 
1265
1358
  // src/shared/cli/functions/run-agent-list.function.ts
1266
- async function runAgentList(client, _options) {
1359
+ async function runAgentList(client, options) {
1267
1360
  log("running agent-list", 0 /* DEBUG */);
1268
1361
  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
- }));
1362
+ const query = options.args.join(" ").toLowerCase();
1363
+ const filtered = docs.filter((d) => matchesQuery(query, d.title));
1364
+ const names = filtered.map((d) => d.title);
1365
+ const data = { items: names, total: names.length, offset: 0 };
1274
1366
  return {
1275
- output: JSON.stringify(items, null, 2),
1367
+ output: JSON.stringify(data, null, 2),
1276
1368
  exitCode: 0
1277
1369
  };
1278
1370
  }
@@ -1740,7 +1832,7 @@ async function handleTypedSearch(client, options, type) {
1740
1832
  }
1741
1833
 
1742
1834
  // src/shared/version/constants/version.constant.ts
1743
- var VERSION = "0.6.1";
1835
+ var VERSION = "0.6.4";
1744
1836
 
1745
1837
  // src/shared/cli/functions/generate-help.function.ts
1746
1838
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
@@ -1768,13 +1860,13 @@ var GROUP_SUBCOMMANDS = {
1768
1860
  ],
1769
1861
  skill: [
1770
1862
  ["learn", "Create a new skill definition"],
1771
- ["list", "List all skills"],
1863
+ ["list", "List skills (filter by keyword)"],
1772
1864
  ["get", "Retrieve a skill by name or kbid"],
1773
1865
  ["delete", "Remove a skill"]
1774
1866
  ],
1775
1867
  agent: [
1776
1868
  ["create", "Create an agent with skill refs"],
1777
- ["list", "List all agents"],
1869
+ ["list", "List agents (filter by keyword)"],
1778
1870
  ["get", "Retrieve an agent by docid"],
1779
1871
  ["delete", "Remove an agent"]
1780
1872
  ],
@@ -1819,9 +1911,9 @@ Usage: kbdb [options]
1819
1911
  kbdb <command> <subcommand> [options] ...[params]
1820
1912
 
1821
1913
  Knowledge Base:
1822
- learn Import content from files/stdin
1914
+ learn Import content (splits .md on headings)
1823
1915
  unlearn Remove sections by kbid
1824
- search Query the knowledge base
1916
+ search Query the knowledge base (sibling boost)
1825
1917
  recall Retrieve section context by kbid
1826
1918
  content Compose rendered content from ids
1827
1919
 
@@ -1840,13 +1932,13 @@ Maintenance:
1840
1932
 
1841
1933
  Skills:
1842
1934
  skill learn Create a new skill definition
1843
- skill list List all skills
1935
+ skill list List skills (filter by keyword)
1844
1936
  skill get Retrieve a skill by name or kbid
1845
1937
  skill delete Remove a skill
1846
1938
 
1847
1939
  Agents:
1848
1940
  agent create Create an agent with skill refs
1849
- agent list List all agents
1941
+ agent list List agents (filter by keyword)
1850
1942
  agent get Retrieve an agent by docid
1851
1943
  agent delete Remove an agent
1852
1944
 
@@ -1918,9 +2010,17 @@ Options:
1918
2010
  --replace Supersede existing section by source key
1919
2011
  --db <path> Path to .kbdb directory
1920
2012
 
2013
+ Markdown splitting:
2014
+ .md files are automatically split on ATX
2015
+ headings (# through ######) into independent
2016
+ sections. Each section uses its heading as
2017
+ the title and shares a docid for document
2018
+ grouping. Providing --title imports the file
2019
+ as a single section (skips splitting).
2020
+ Non-.md files are never split.
2021
+
1921
2022
  Output:
1922
- JSON array of { kbid, path, size, skipped,
1923
- superseded, warnings, near_duplicates }
2023
+ { items, total, offset } envelope
1924
2024
 
1925
2025
  Examples:
1926
2026
  kbdb learn README.md
@@ -1959,8 +2059,14 @@ Options:
1959
2059
  --content, -c Hydrate results with section content
1960
2060
  --db <path> Path to .kbdb directory
1961
2061
 
2062
+ Scoring:
2063
+ Results are ranked by BM25F. Sections from
2064
+ the same document (shared docid) receive a
2065
+ sibling score boost when multiple sections
2066
+ match the query.
2067
+
1962
2068
  Output:
1963
- JSON array of { kbid, score, preview, matches }
2069
+ { items, total, offset } envelope
1964
2070
 
1965
2071
  Examples:
1966
2072
  kbdb search "machine learning"
@@ -2169,16 +2275,20 @@ Examples:
2169
2275
  `,
2170
2276
  "skill-list": `kbdb skill list - List all skills
2171
2277
 
2172
- Usage: kbdb skill list [options]
2278
+ Usage: kbdb skill list [options] [keywords...]
2279
+
2280
+ Arguments:
2281
+ [keywords...] Case-insensitive substring filter on name or description
2173
2282
 
2174
2283
  Options:
2175
2284
  --db <path> Path to .kbdb directory
2176
2285
 
2177
2286
  Output:
2178
- JSON array of { kbid, name, description, argumentCount }
2287
+ JSON { items, total, offset }
2179
2288
 
2180
2289
  Examples:
2181
2290
  kbdb skill list
2291
+ kbdb skill list summarize
2182
2292
  kbdb skill list --db /path/to/project
2183
2293
  `,
2184
2294
  "skill-get": `kbdb skill get - Retrieve a skill by name or kbid
@@ -2234,16 +2344,20 @@ Examples:
2234
2344
  `,
2235
2345
  "agent-list": `kbdb agent list - List all agents
2236
2346
 
2237
- Usage: kbdb agent list [options]
2347
+ Usage: kbdb agent list [options] [keywords...]
2348
+
2349
+ Arguments:
2350
+ [keywords...] Case-insensitive substring filter on agent name
2238
2351
 
2239
2352
  Options:
2240
2353
  --db <path> Path to .kbdb directory
2241
2354
 
2242
2355
  Output:
2243
- JSON array of { docid, name, description, skillCount }
2356
+ JSON { items, total, offset }
2244
2357
 
2245
2358
  Examples:
2246
2359
  kbdb agent list
2360
+ kbdb agent list reviewer
2247
2361
  kbdb agent list --db /path/to/project
2248
2362
  `,
2249
2363
  "agent-get": `kbdb agent get - Retrieve an agent by docid
@@ -3503,7 +3617,7 @@ function createToolDefinitions() {
3503
3617
  return [
3504
3618
  {
3505
3619
  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.",
3620
+ 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.",
3507
3621
  inputSchema: {
3508
3622
  type: "object",
3509
3623
  properties: {
@@ -3546,7 +3660,7 @@ function createToolDefinitions() {
3546
3660
  },
3547
3661
  {
3548
3662
  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.",
3663
+ 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.",
3550
3664
  inputSchema: {
3551
3665
  type: "object",
3552
3666
  properties: {
@@ -3560,7 +3674,7 @@ function createToolDefinitions() {
3560
3674
  },
3561
3675
  title: {
3562
3676
  type: "string",
3563
- description: "Section title. Required for image/* types."
3677
+ description: "Section title. Skips markdown splitting when set. Required for image/* types."
3564
3678
  },
3565
3679
  description: {
3566
3680
  type: "string",
@@ -3568,7 +3682,7 @@ function createToolDefinitions() {
3568
3682
  },
3569
3683
  docid: {
3570
3684
  type: "string",
3571
- description: "Document ID to group under"
3685
+ description: "Document ID to group sections under. Set automatically when markdown splitting is active."
3572
3686
  },
3573
3687
  tags: {
3574
3688
  type: "array",
@@ -3797,10 +3911,15 @@ function createToolDefinitions() {
3797
3911
  },
3798
3912
  {
3799
3913
  name: "skill-list",
3800
- description: "List all skills in the knowledge base.",
3914
+ description: "List skill definitions stored in the knowledge base, optionally filtered by a substring query.",
3801
3915
  inputSchema: {
3802
3916
  type: "object",
3803
- properties: {}
3917
+ properties: {
3918
+ query: {
3919
+ type: "string",
3920
+ description: "Case-insensitive substring filter on skill name or description."
3921
+ }
3922
+ }
3804
3923
  },
3805
3924
  annotations: {
3806
3925
  title: "List Skills",
@@ -3891,10 +4010,15 @@ function createToolDefinitions() {
3891
4010
  },
3892
4011
  {
3893
4012
  name: "agent-list",
3894
- description: "List all agents in the knowledge base.",
4013
+ description: "List agent documents in the knowledge base, optionally filtered by a substring query.",
3895
4014
  inputSchema: {
3896
4015
  type: "object",
3897
- properties: {}
4016
+ properties: {
4017
+ query: {
4018
+ type: "string",
4019
+ description: "Case-insensitive substring filter on agent name or description."
4020
+ }
4021
+ }
3898
4022
  },
3899
4023
  annotations: {
3900
4024
  title: "List Agents",
@@ -4142,7 +4266,7 @@ async function readAgentDescription(client, sections) {
4142
4266
  return "";
4143
4267
  }
4144
4268
  }
4145
- async function handleAgentList(client) {
4269
+ async function handleAgentList(client, args = {}) {
4146
4270
  const docs = await client.listDocumentsByType("agent");
4147
4271
  const items = await Promise.all(
4148
4272
  docs.map(async (d) => ({
@@ -4150,17 +4274,17 @@ async function handleAgentList(client) {
4150
4274
  description: await readAgentDescription(client, d.sections)
4151
4275
  }))
4152
4276
  );
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"
4277
+ const query = typeof args["query"] === "string" ? args["query"].toLowerCase() : "";
4278
+ const filtered = items.filter(
4279
+ (item) => matchesQuery(query, item.name, item.description)
4158
4280
  );
4281
+ const names = filtered.map((item) => item.name);
4282
+ const data = { items: names, total: names.length, offset: 0 };
4159
4283
  return {
4160
4284
  content: [
4161
4285
  {
4162
4286
  type: "text",
4163
- text
4287
+ text: JSON.stringify(data, null, 2)
4164
4288
  }
4165
4289
  ]
4166
4290
  };
@@ -4462,7 +4586,7 @@ async function handleToolCall(client, name, args) {
4462
4586
  case "skill-learn":
4463
4587
  return await handleSkillLearn(client, args);
4464
4588
  case "skill-list":
4465
- return await handleSkillList(client);
4589
+ return await handleSkillList(client, args);
4466
4590
  case "skill-get":
4467
4591
  return await handleSkillGet(client, args);
4468
4592
  case "skill-delete":
@@ -4470,7 +4594,7 @@ async function handleToolCall(client, name, args) {
4470
4594
  case "agent-create":
4471
4595
  return await handleAgentCreate(client, args);
4472
4596
  case "agent-list":
4473
- return await handleAgentList(client);
4597
+ return await handleAgentList(client, args);
4474
4598
  case "agent-get":
4475
4599
  return await handleAgentGet(client, args);
4476
4600
  case "agent-delete":
@@ -4711,17 +4835,17 @@ async function handleSkillLearn(client, args) {
4711
4835
  ]
4712
4836
  };
4713
4837
  }
4714
- async function handleSkillList(client) {
4838
+ async function handleSkillList(client, args) {
4715
4839
  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"
4840
+ const query = typeof args["query"] === "string" ? args["query"].toLowerCase() : "";
4841
+ const filtered = records.filter(
4842
+ (r) => matchesQuery(query, r.title, r.description)
4723
4843
  );
4724
- return { content: [{ type: "text", text }] };
4844
+ const names = filtered.map((r) => r.title ?? "");
4845
+ const data = { items: names, total: names.length, offset: 0 };
4846
+ return {
4847
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
4848
+ };
4725
4849
  }
4726
4850
  async function handleSkillGet(client, args) {
4727
4851
  const kbid = typeof args["kbid"] === "string" ? args["kbid"] : void 0;