@dikolab/kbdb 0.2.1 → 0.3.0

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 (120) hide show
  1. package/README.md +59 -24
  2. package/dist/cli.cjs +462 -73
  3. package/dist/cli.cjs.map +4 -4
  4. package/dist/cli.mjs +454 -65
  5. package/dist/cli.mjs.map +4 -4
  6. package/dist/kbdb-worker.cjs +395 -60
  7. package/dist/kbdb-worker.cjs.map +4 -4
  8. package/dist/mod.cjs +18 -1
  9. package/dist/mod.cjs.map +2 -2
  10. package/dist/mod.mjs +18 -1
  11. package/dist/mod.mjs.map +2 -2
  12. package/dist/src/shared/cli/functions/dispatch-command.function.d.ts +3 -1
  13. package/dist/src/shared/cli/functions/install-default-skills.function.d.ts +12 -0
  14. package/dist/src/shared/cli/functions/run-check.function.d.ts +47 -5
  15. package/dist/src/shared/cli/functions/run-export.function.d.ts +34 -0
  16. package/dist/src/shared/cli/functions/run-gc.function.d.ts +6 -0
  17. package/dist/src/shared/cli/functions/run-learn.function.d.ts +35 -6
  18. package/dist/src/shared/cli/functions/run-recall.function.d.ts +45 -0
  19. package/dist/src/shared/cli/functions/run-search.function.d.ts +52 -15
  20. package/dist/src/shared/cli/index.d.ts +4 -0
  21. package/dist/src/shared/cli/typings/cli-options.interface.d.ts +13 -0
  22. package/dist/src/shared/cli/typings/learn-result.model.d.ts +19 -0
  23. package/dist/src/shared/cli/typings/search-display.model.d.ts +18 -0
  24. package/dist/src/shared/mcp/classes/mcp-server.class.d.ts +8 -0
  25. package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +43 -0
  26. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  27. package/dist/src/shared/wasm-codec/functions/decode-add-section-result.function.d.ts +48 -0
  28. package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +58 -0
  29. package/dist/src/shared/wasm-codec/functions/decode-section-records.function.d.ts +33 -0
  30. package/dist/src/shared/wasm-codec/functions/encode-add-section-params.function.d.ts +52 -0
  31. package/dist/src/shared/wasm-codec/index.d.ts +2 -0
  32. package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +16 -0
  33. package/dist/src/shared/worker-client/index.d.ts +1 -1
  34. package/dist/src/shared/worker-client/typings/add-section-result.model.d.ts +29 -15
  35. package/dist/src/shared/worker-client/typings/export-result.model.d.ts +14 -0
  36. package/dist/src/shared/worker-client/typings/gc-result.model.d.ts +6 -0
  37. package/dist/src/shared/worker-client/typings/search-result.model.d.ts +12 -0
  38. package/dist/src/shared/worker-client/typings/section.model.d.ts +16 -0
  39. package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +35 -0
  40. package/dist/wasm/fs-database/kbdb_fs_database.d.ts +22 -1
  41. package/dist/wasm/fs-database/kbdb_fs_database.js +28 -1
  42. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  43. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +1 -0
  44. package/dist/wasm/kb-worker/kbdb_worker.d.ts +48 -1
  45. package/dist/wasm/kb-worker/kbdb_worker.js +60 -1
  46. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  47. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +2 -0
  48. package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
  49. package/dist/worker.mjs +396 -60
  50. package/dist/worker.mjs.map +4 -4
  51. package/docs/details/cli.md +102 -15
  52. package/docs/details/library-api.md +55 -6
  53. package/docs/details/mcp-server.md +8 -4
  54. package/docs/details/search-and-ranking.md +63 -5
  55. package/docs/details/storage.md +2 -0
  56. package/docs/goals/cli.md +255 -14
  57. package/docs/goals/database.md +244 -17
  58. package/docs/goals/document.md +1 -1
  59. package/docs/goals/mcp.md +83 -14
  60. package/docs/goals/query-parser.md +98 -4
  61. package/docs/goals/query-result.md +177 -10
  62. package/docs/goals/skills.md +72 -0
  63. package/docs/goals/worker-daemon.md +54 -7
  64. package/docs/modules/rust/fs-database/functions/integrity.md +11 -0
  65. package/docs/modules/rust/fs-database/functions/section.md +23 -6
  66. package/docs/modules/rust/fs-database/module.md +1 -0
  67. package/docs/modules/rust/fs-database/types/section.md +27 -1
  68. package/docs/modules/rust/overview.md +2 -1
  69. package/docs/modules/rust/query-parser/functions/module.md +2 -2
  70. package/docs/modules/rust/query-parser/functions/pipeline.md +44 -6
  71. package/docs/modules/rust/query-parser/module.md +5 -4
  72. package/docs/modules/rust/query-parser/types/keyphrase.md +11 -0
  73. package/docs/modules/rust/query-parser/types/module.md +3 -3
  74. package/docs/modules/typescript/shared/cli/functions.md +68 -3
  75. package/package.json +1 -1
  76. package/src/shared/cli/functions/dispatch-command.function.ts +53 -1
  77. package/src/shared/cli/functions/format-output.function.ts +36 -12
  78. package/src/shared/cli/functions/generate-help.function.ts +56 -1
  79. package/src/shared/cli/functions/install-default-skills.function.ts +296 -0
  80. package/src/shared/cli/functions/parse-args.function.ts +13 -0
  81. package/src/shared/cli/functions/run-check.function.ts +87 -6
  82. package/src/shared/cli/functions/run-export.function.ts +40 -0
  83. package/src/shared/cli/functions/run-gc.function.ts +6 -0
  84. package/src/shared/cli/functions/run-init.function.ts +2 -0
  85. package/src/shared/cli/functions/run-learn.function.ts +68 -16
  86. package/src/shared/cli/functions/run-recall.function.ts +66 -0
  87. package/src/shared/cli/functions/run-search.function.ts +58 -15
  88. package/src/shared/cli/index.ts +4 -0
  89. package/src/shared/cli/typings/cli-options.interface.ts +13 -0
  90. package/src/shared/cli/typings/learn-result.model.ts +16 -0
  91. package/src/shared/cli/typings/search-display.model.ts +18 -0
  92. package/src/shared/mcp/classes/mcp-server.class.ts +30 -1
  93. package/src/shared/mcp/functions/create-tool-definitions.function.ts +46 -5
  94. package/src/shared/mcp/functions/handle-tool-call.function.ts +70 -2
  95. package/src/shared/mcp/typings/mcp-client.interface.ts +45 -0
  96. package/src/shared/version/constants/version.constant.ts +1 -1
  97. package/src/shared/wasm-codec/functions/decode-add-section-result.function.ts +112 -0
  98. package/src/shared/wasm-codec/functions/decode-scored-results.function.ts +86 -0
  99. package/src/shared/wasm-codec/functions/decode-section-records.function.ts +87 -0
  100. package/src/shared/wasm-codec/functions/encode-add-section-params.function.ts +68 -0
  101. package/src/shared/wasm-codec/index.ts +5 -0
  102. package/src/shared/worker-client/classes/worker-client.class.ts +19 -0
  103. package/src/shared/worker-client/index.ts +4 -1
  104. package/src/shared/worker-client/typings/add-section-result.model.ts +30 -15
  105. package/src/shared/worker-client/typings/export-result.model.ts +14 -0
  106. package/src/shared/worker-client/typings/gc-result.model.ts +6 -0
  107. package/src/shared/worker-client/typings/search-result.model.ts +12 -0
  108. package/src/shared/worker-client/typings/section.model.ts +16 -0
  109. package/src/shared/worker-daemon/classes/worker-daemon.class.ts +315 -46
  110. package/src/shared/worker-daemon/functions/create-host-imports.function.ts +19 -4
  111. package/src/shared/worker-daemon/functions/create-ipc-server.function.ts +5 -1
  112. package/src/wasm/fs-database/kbdb_fs_database.d.ts +22 -1
  113. package/src/wasm/fs-database/kbdb_fs_database.js +28 -1
  114. package/src/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  115. package/src/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +1 -0
  116. package/src/wasm/kb-worker/kbdb_worker.d.ts +48 -1
  117. package/src/wasm/kb-worker/kbdb_worker.js +60 -1
  118. package/src/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  119. package/src/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +2 -0
  120. package/src/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
@@ -0,0 +1,66 @@
1
+ import type { CliOptions } from '../typings/cli-options.interface.ts';
2
+ import type { RecallResult } from '../../worker-client/typings/recall-result.model.ts';
3
+
4
+ /** Maximum allowed expansion depth for recall operations. */
5
+ const MAX_RECALL_DEPTH = 3;
6
+
7
+ /**
8
+ * Minimal client interface required by `runRecall`.
9
+ *
10
+ * Decouples the CLI function from the concrete `WorkerClient`
11
+ * implementation, making it straightforward to test with mocks.
12
+ */
13
+ export interface RecallClient {
14
+ /**
15
+ * Retrieves full content and context for one or more sections
16
+ * by their knowledge-base identifiers.
17
+ *
18
+ * @param params - section identifier(s) and optional expansion
19
+ * depth (0--3)
20
+ * @returns a single `RecallResult` when `kbid` is supplied, or
21
+ * a `RecallResult[]` when `kbids` is supplied
22
+ */
23
+ recall(params: {
24
+ kbid?: string;
25
+ kbids?: string[];
26
+ depth?: number;
27
+ }): Promise<RecallResult | RecallResult[]>;
28
+ }
29
+
30
+ /**
31
+ * Retrieves full content and progressive context for one or more
32
+ * sections by kbid via the provided client.
33
+ *
34
+ * Returns an error result when no kbids are provided in
35
+ * `options.args`. For a single kbid, calls `client.recall` with the
36
+ * `kbid` parameter and returns the single `RecallResult`. For
37
+ * multiple kbids, calls `client.recall` with the `kbids` array.
38
+ *
39
+ * The `depth` option is read from `options.depth`, defaulting to 0
40
+ * and clamped to the range 0--3.
41
+ *
42
+ * @param client - recall client to delegate to
43
+ * @param options - parsed CLI options supplying kbids in `args` and
44
+ * expansion depth in `depth`
45
+ * @returns the `RecallResult` or `RecallResult[]` from the client,
46
+ * or an error object when no kbids are provided
47
+ */
48
+ export async function runRecall(
49
+ client: RecallClient,
50
+ options: CliOptions,
51
+ ): Promise<RecallResult | RecallResult[] | { error: string }> {
52
+ if (options.args.length === 0) {
53
+ return { error: 'no kbid provided' };
54
+ }
55
+
56
+ const depth = Math.min(
57
+ MAX_RECALL_DEPTH,
58
+ Math.max(0, options.depth ?? 0),
59
+ );
60
+
61
+ if (options.args.length === 1) {
62
+ return client.recall({ kbid: options.args[0], depth });
63
+ }
64
+
65
+ return client.recall({ kbids: options.args, depth });
66
+ }
@@ -1,6 +1,58 @@
1
1
  import type { CliOptions } from '../typings/cli-options.interface.ts';
2
2
  import type { PagedSearchDisplay } from '../typings/search-display.model.ts';
3
3
 
4
+ /** A single raw search result item returned by the daemon. */
5
+ interface RawSearchItem {
6
+ /** Knowledge-base identifier of the matched section. */
7
+ readonly kbid: string;
8
+ /** Section heading, or null when untitled. */
9
+ readonly heading: string | null;
10
+ /** MIME type of the section content. */
11
+ readonly type: string;
12
+ /** Document IDs this section belongs to. */
13
+ readonly docids: readonly string[];
14
+ /** Relevance score from the ranking engine. */
15
+ readonly score: number;
16
+ /** KWIC excerpt centered on the highest-scoring matched term. */
17
+ readonly snippet: string;
18
+ /** Query terms that matched within this section. */
19
+ readonly matched_terms: readonly string[];
20
+ /**
21
+ * Fields that contained at least one match.
22
+ * May be absent from older daemon versions.
23
+ */
24
+ readonly matched_fields?: readonly string[];
25
+ /**
26
+ * Normalised confidence score (0.0-1.0) relative to the top result.
27
+ * May be absent from older daemon versions.
28
+ */
29
+ readonly confidence?: number;
30
+ /**
31
+ * ISO 8601 creation timestamp of the section.
32
+ * May be absent from older daemon versions.
33
+ */
34
+ readonly created_at?: string;
35
+ }
36
+
37
+ /** Raw paginated search response from the daemon. */
38
+ interface RawSearchResponse {
39
+ /** Result items for the current page. */
40
+ readonly items: readonly RawSearchItem[];
41
+ /** Total number of matches across all pages. */
42
+ readonly total: number;
43
+ /** Number of results skipped. */
44
+ readonly offset: number;
45
+ /** Maximum results per page. */
46
+ readonly limit: number;
47
+ /** Whether more results exist beyond this page. */
48
+ readonly has_more: boolean;
49
+ /**
50
+ * When `true`, the engine relaxed the query to produce results.
51
+ * May be absent from older daemon versions.
52
+ */
53
+ readonly relaxed?: boolean;
54
+ }
55
+
4
56
  /**
5
57
  * Minimal client interface required by `runSearch`.
6
58
  *
@@ -18,21 +70,7 @@ export interface SearchClient {
18
70
  mode?: string;
19
71
  limit?: number;
20
72
  offset?: number;
21
- }): Promise<{
22
- readonly items: readonly {
23
- readonly kbid: string;
24
- readonly heading: string | null;
25
- readonly type: string;
26
- readonly docids: readonly string[];
27
- readonly score: number;
28
- readonly snippet: string;
29
- readonly matched_terms: readonly string[];
30
- }[];
31
- readonly total: number;
32
- readonly offset: number;
33
- readonly limit: number;
34
- readonly has_more: boolean;
35
- }>;
73
+ }): Promise<RawSearchResponse>;
36
74
  }
37
75
 
38
76
  /**
@@ -59,6 +97,7 @@ export async function runSearch(
59
97
  offset: 0,
60
98
  limit: options.limit,
61
99
  has_more: false,
100
+ relaxed: false,
62
101
  };
63
102
  }
64
103
 
@@ -76,13 +115,17 @@ export async function runSearch(
76
115
  type: r.type,
77
116
  docids: r.docids,
78
117
  score: r.score,
118
+ confidence: r.confidence ?? 0,
79
119
  snippet: r.snippet,
80
120
  matched_terms: r.matched_terms,
121
+ matched_fields: r.matched_fields ?? [],
122
+ created_at: r.created_at ?? '',
81
123
  preview: '',
82
124
  })),
83
125
  total: results.total,
84
126
  offset: results.offset,
85
127
  limit: results.limit,
86
128
  has_more: results.has_more,
129
+ relaxed: results.relaxed ?? false,
87
130
  };
88
131
  }
@@ -1,6 +1,8 @@
1
1
  export { parseArgs } from './functions/parse-args.function.ts';
2
2
  export { formatOutput } from './functions/format-output.function.ts';
3
3
  export { runSearch } from './functions/run-search.function.ts';
4
+ export { runRecall } from './functions/run-recall.function.ts';
5
+ export { runExport } from './functions/run-export.function.ts';
4
6
  export { runLearn } from './functions/run-learn.function.ts';
5
7
  export { runUnlearn } from './functions/run-unlearn.function.ts';
6
8
  export { runContent } from './functions/run-content.function.ts';
@@ -28,6 +30,8 @@ export type {
28
30
  PagedSearchDisplay,
29
31
  } from './typings/search-display.model.ts';
30
32
  export type { SearchClient } from './functions/run-search.function.ts';
33
+ export type { RecallClient } from './functions/run-recall.function.ts';
34
+ export type { ExportClient } from './functions/run-export.function.ts';
31
35
  export type { LearnClient } from './functions/run-learn.function.ts';
32
36
  export type {
33
37
  UnlearnClient,
@@ -52,6 +52,19 @@ export interface CliOptions {
52
52
  body?: string;
53
53
  /** Comma-separated skill kbids for agent create. */
54
54
  skills?: string[];
55
+ /**
56
+ * Recall expansion depth (0-3). Clamped to 3.
57
+ * Defaults to 0.
58
+ */
59
+ depth?: number;
60
+ /** Comma-separated tags for scoping (learn, recall). */
61
+ tags?: string[];
62
+ /**
63
+ * When `true`, enables supersession by source key (learn).
64
+ * Re-learning edited content replaces the original section
65
+ * instead of creating a sibling.
66
+ */
67
+ replace?: boolean;
55
68
  /** Positional arguments following the sub-command. */
56
69
  args: string[];
57
70
  }
@@ -8,4 +8,20 @@ export interface LearnResult {
8
8
  size: number;
9
9
  /** `true` when the file was skipped without being ingested. */
10
10
  skipped: boolean;
11
+ /**
12
+ * Kbid of the section that was superseded via `--replace`,
13
+ * or `null` when no supersession occurred.
14
+ */
15
+ superseded: string | null;
16
+ /**
17
+ * Advisory warning strings returned by the engine
18
+ * (e.g. `"heading_missing"` when no title is provided).
19
+ */
20
+ warnings: string[];
21
+ /**
22
+ * Sections with content similarity >= 0.85 to this section.
23
+ * Each entry identifies a near-duplicate section and its
24
+ * similarity score.
25
+ */
26
+ near_duplicates: { kbid: string; similarity: number }[];
11
27
  }
@@ -10,10 +10,22 @@ export interface SearchDisplay {
10
10
  docids: readonly string[];
11
11
  /** Relevance score assigned by the ranking engine. */
12
12
  score: number;
13
+ /**
14
+ * Normalised confidence score (0.0-1.0) relative to the
15
+ * top result in this result set.
16
+ */
17
+ confidence: number;
13
18
  /** KWIC snippet from the WASM engine (~150 chars). */
14
19
  snippet: string;
15
20
  /** Query terms that matched within this section. */
16
21
  matched_terms: readonly string[];
22
+ /**
23
+ * Fields that contained at least one match:
24
+ * subset of `["heading", "body", "code"]`.
25
+ */
26
+ matched_fields: readonly string[];
27
+ /** ISO 8601 creation timestamp of the section. */
28
+ created_at: string;
17
29
  /**
18
30
  * CLI-side truncated content preview for terminal display.
19
31
  * Populated when `--content` / `-c` is used.
@@ -33,4 +45,10 @@ export interface PagedSearchDisplay {
33
45
  limit: number;
34
46
  /** Whether more results exist beyond this page. */
35
47
  has_more: boolean;
48
+ /**
49
+ * When `true`, the engine relaxed the query (e.g. dropped
50
+ * rare terms) to return results that would otherwise be
51
+ * empty.
52
+ */
53
+ relaxed: boolean;
36
54
  }
@@ -207,8 +207,12 @@ export class McpServer {
207
207
  request.method === 'notifications/initialized' ||
208
208
  request.method === 'notifications/cancelled'
209
209
  ) {
210
- if (request.method === 'notifications/initialized') {
210
+ if (
211
+ request.method === 'notifications/initialized' &&
212
+ !this.initialized
213
+ ) {
211
214
  this.initialized = true;
215
+ void this.runStartupHealthCheck();
212
216
  }
213
217
  }
214
218
  return null;
@@ -445,4 +449,29 @@ export class McpServer {
445
449
  const json = JSON.stringify(response);
446
450
  process.stdout.write(json + '\n');
447
451
  }
452
+
453
+ /**
454
+ * Runs an integrity check immediately after the MCP handshake
455
+ * completes. Emits a `notifications/message` warning when the
456
+ * check detects issues so the connected agent is aware before
457
+ * making requests. The server continues operating regardless of
458
+ * the result.
459
+ */
460
+ private async runStartupHealthCheck(): Promise<void> {
461
+ try {
462
+ const report = await this.client.integrityCheck();
463
+ if (!report.ok) {
464
+ const summary = report.errors
465
+ .map((e) => `${e.type}: ${e.entity} -- ${e.detail}`)
466
+ .join('; ');
467
+ this.notify('notifications/message', {
468
+ level: 'warning',
469
+ logger: 'kbdb',
470
+ data: `Database integrity issues detected: ${summary}`,
471
+ });
472
+ }
473
+ } catch {
474
+ // Health check failure is non-fatal -- server continues
475
+ }
476
+ }
448
477
  }
@@ -10,10 +10,11 @@ export function createToolDefinitions(): McpToolDefinition[] {
10
10
  {
11
11
  name: 'search',
12
12
  description:
13
- 'Search the knowledge base. Returns ranked' +
14
- ' results with headings and snippets for' +
15
- ' relevance evaluation. Use `recall` to' +
16
- ' get full content of specific results.',
13
+ 'Query the knowledge base by keywords.' +
14
+ ' Returns ranked results with heading,' +
15
+ ' snippet, and matched terms. Use multiple' +
16
+ ' short keyword queries for best coverage.' +
17
+ ' Use recall to get full content.',
17
18
  inputSchema: {
18
19
  type: 'object',
19
20
  properties: {
@@ -49,7 +50,11 @@ export function createToolDefinitions(): McpToolDefinition[] {
49
50
  },
50
51
  {
51
52
  name: 'learn',
52
- description: 'Add a text section to the knowledge base.',
53
+ description:
54
+ 'Import content into the knowledge base.' +
55
+ ' Use for durable facts, decisions, and' +
56
+ ' corrections. Use replace when updating' +
57
+ ' existing knowledge. Attach tags for scoping.',
53
58
  inputSchema: {
54
59
  type: 'object',
55
60
  properties: {
@@ -76,6 +81,17 @@ export function createToolDefinitions(): McpToolDefinition[] {
76
81
  type: 'string',
77
82
  description: 'Document ID to group under',
78
83
  },
84
+ tags: {
85
+ type: 'array',
86
+ items: { type: 'string' },
87
+ description: 'Tags for scoping and filtering',
88
+ },
89
+ replace: {
90
+ type: 'boolean',
91
+ description:
92
+ 'Replace existing section with same' +
93
+ ' source key (supersession)',
94
+ },
79
95
  },
80
96
  required: ['content'],
81
97
  },
@@ -268,6 +284,31 @@ export function createToolDefinitions(): McpToolDefinition[] {
268
284
  openWorldHint: false,
269
285
  },
270
286
  },
287
+ {
288
+ name: 'export',
289
+ description:
290
+ 'Export the database as a self-contained' +
291
+ ' directory snapshot (sections, documents,' +
292
+ ' catalog -- no indexes).',
293
+ inputSchema: {
294
+ type: 'object',
295
+ properties: {
296
+ path: {
297
+ type: 'string',
298
+ description:
299
+ 'Export destination path' +
300
+ ' (default: ./kbdb-export/)',
301
+ },
302
+ },
303
+ },
304
+ annotations: {
305
+ title: 'Export Snapshot',
306
+ readOnlyHint: false,
307
+ destructiveHint: false,
308
+ idempotentHint: true,
309
+ openWorldHint: false,
310
+ },
311
+ },
271
312
  {
272
313
  name: 'skill-learn',
273
314
  description:
@@ -32,6 +32,30 @@ function asString(value: unknown, fallback: string): string {
32
32
  return typeof value === 'string' ? value : fallback;
33
33
  }
34
34
 
35
+ /**
36
+ * Narrows an `unknown` value to a boolean, returning `undefined`
37
+ * when the value is not a boolean.
38
+ */
39
+ function asBooleanOrUndefined(value: unknown): boolean | undefined {
40
+ return typeof value === 'boolean' ? value : undefined;
41
+ }
42
+
43
+ /**
44
+ * Narrows an `unknown` value to a `string[]`, returning `undefined`
45
+ * when the value is not an array of strings.
46
+ */
47
+ function asStringArrayOrUndefined(
48
+ value: unknown,
49
+ ): string[] | undefined {
50
+ if (
51
+ Array.isArray(value) &&
52
+ value.every((item) => typeof item === 'string')
53
+ ) {
54
+ return value;
55
+ }
56
+ return undefined;
57
+ }
58
+
35
59
  /**
36
60
  * Narrows an `unknown` value to a `string[]`, returning an empty
37
61
  * array when the value is not an array of strings.
@@ -82,6 +106,8 @@ export async function handleToolCall(
82
106
  return await handleGc(client);
83
107
  case 'rebuild':
84
108
  return await handleRebuild(client);
109
+ case 'export':
110
+ return await handleExport(client, args);
85
111
  case 'skill-learn':
86
112
  return await handleSkillLearn(client, args);
87
113
  case 'skill-list':
@@ -135,7 +161,15 @@ async function handleSearch(
135
161
 
136
162
  const stripped = {
137
163
  ...results,
138
- items: results.items.map(({ score: _score, ...rest }) => rest),
164
+ items: results.items.map(
165
+ ({
166
+ score: _score,
167
+ confidence: _confidence,
168
+ ...rest
169
+ }: (typeof results.items)[number] & {
170
+ confidence?: number;
171
+ }) => rest,
172
+ ),
139
173
  };
140
174
 
141
175
  return {
@@ -161,6 +195,8 @@ async function handleLearn(
161
195
  : undefined;
162
196
  const docid =
163
197
  typeof args['docid'] === 'string' ? args['docid'] : undefined;
198
+ const tags = asStringArrayOrUndefined(args['tags']);
199
+ const replace = asBooleanOrUndefined(args['replace']);
164
200
  if (sectionType.startsWith('image/') && !title) {
165
201
  return {
166
202
  content: [
@@ -178,12 +214,25 @@ async function handleLearn(
178
214
  title,
179
215
  description,
180
216
  docid,
217
+ tags,
218
+ replace,
181
219
  });
220
+ const normalized = {
221
+ kbid: result.kbid,
222
+ indexed_terms_count: result.indexed_terms_count ?? 0,
223
+ elapsed_ms: result.elapsed_ms ?? 0,
224
+ superseded: result.superseded ?? null,
225
+ warnings: result.warnings ?? [],
226
+ near_duplicates: (result.near_duplicates ?? []).map((d) => ({
227
+ kbid: d.kbid,
228
+ similarity: d.similarity,
229
+ })),
230
+ };
182
231
  return {
183
232
  content: [
184
233
  {
185
234
  type: 'text',
186
- text: JSON.stringify(result),
235
+ text: JSON.stringify(normalized),
187
236
  },
188
237
  ],
189
238
  };
@@ -472,3 +521,22 @@ async function handleSkillDelete(
472
521
  ],
473
522
  };
474
523
  }
524
+
525
+ async function handleExport(
526
+ client: McpWorkerClient,
527
+ args: Record<string, unknown>,
528
+ ): Promise<ToolCallResult> {
529
+ const path =
530
+ typeof args['path'] === 'string' ? args['path'] : undefined;
531
+ const result = await client.export(
532
+ path !== undefined ? { path } : {},
533
+ );
534
+ return {
535
+ content: [
536
+ {
537
+ type: 'text',
538
+ text: JSON.stringify(result),
539
+ },
540
+ ],
541
+ };
542
+ }
@@ -45,6 +45,18 @@ export interface McpIntegrityCheckResult {
45
45
  readonly elapsed_ms: number;
46
46
  }
47
47
 
48
+ /** Export report returned by the daemon. */
49
+ export interface McpExportResult {
50
+ /** Absolute path to the directory where files were written. */
51
+ readonly path: string;
52
+ /** Number of section files copied to the export directory. */
53
+ readonly sections_exported: number;
54
+ /** Number of document manifest files copied. */
55
+ readonly documents_exported: number;
56
+ /** Time taken to complete the export, in milliseconds. */
57
+ readonly elapsed_ms: number;
58
+ }
59
+
48
60
  /** Garbage collection report returned by the daemon. */
49
61
  export interface McpGcResult {
50
62
  /** Number of unreferenced sections removed. */
@@ -53,6 +65,12 @@ export interface McpGcResult {
53
65
  readonly removed_bytes: number;
54
66
  /** Time taken to complete garbage collection, in milliseconds. */
55
67
  readonly elapsed_ms: number;
68
+ /**
69
+ * Number of sections whose source_path file no longer contains
70
+ * their heading text. Advisory; not auto-removed by gc.
71
+ * Absent on older daemon versions.
72
+ */
73
+ readonly stale_sources?: number;
56
74
  }
57
75
 
58
76
  /** Index rebuild report returned by the daemon. */
@@ -96,11 +114,20 @@ export interface McpWorkerClient {
96
114
  readonly score: number;
97
115
  readonly snippet: string;
98
116
  readonly matched_terms: readonly string[];
117
+ /** Fields that contained matches (heading, body, code). */
118
+ readonly matched_fields?: readonly string[];
119
+ /** ISO 8601 creation timestamp of the section. */
120
+ readonly created_at?: string;
99
121
  }[];
100
122
  readonly total: number;
101
123
  readonly offset: number;
102
124
  readonly limit: number;
103
125
  readonly has_more: boolean;
126
+ /**
127
+ * `true` when results came from an automatic AND-to-OR
128
+ * fallback query relaxation.
129
+ */
130
+ readonly relaxed?: boolean;
104
131
  }>;
105
132
 
106
133
  /**
@@ -120,6 +147,13 @@ export interface McpWorkerClient {
120
147
  readonly description?: string;
121
148
  /** Optional document ID to group this section under. */
122
149
  readonly docid?: string;
150
+ /** Optional tags for scoping and filtering. */
151
+ readonly tags?: string[];
152
+ /**
153
+ * When `true`, replaces an existing section that has the same
154
+ * source key (supersession).
155
+ */
156
+ readonly replace?: boolean;
123
157
  }): Promise<AddSectionResult>;
124
158
 
125
159
  /**
@@ -263,6 +297,17 @@ export interface McpWorkerClient {
263
297
  readonly depth?: number;
264
298
  }): Promise<unknown>;
265
299
 
300
+ /**
301
+ * Exports the database to a target directory as a self-contained
302
+ * snapshot (sections, documents, catalog -- no indexes).
303
+ *
304
+ * @param params - optional export parameters
305
+ * @returns export report with the resolved path and file counts
306
+ */
307
+ export(params?: {
308
+ readonly path?: string;
309
+ }): Promise<McpExportResult>;
310
+
266
311
  /** Destroys the IPC socket and clears the connection state. */
267
312
  disconnect(): void;
268
313
  }
@@ -1,2 +1,2 @@
1
1
  /** Current package version, kept in sync with package.json. */
2
- export const VERSION = '0.2.1';
2
+ export const VERSION = '0.2.2';