@dikolab/kbdb 0.1.5 → 0.2.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 (193) hide show
  1. package/README.md +30 -9
  2. package/dist/cli.cjs +181 -50
  3. package/dist/cli.cjs.map +3 -3
  4. package/dist/cli.mjs +181 -50
  5. package/dist/cli.mjs.map +3 -3
  6. package/dist/kbdb-worker.cjs +283 -29
  7. package/dist/kbdb-worker.cjs.map +4 -4
  8. package/dist/mod.cjs +13 -2
  9. package/dist/mod.cjs.map +2 -2
  10. package/dist/mod.mjs +13 -2
  11. package/dist/mod.mjs.map +2 -2
  12. package/dist/src/shared/cli/functions/format-output.function.d.ts +7 -7
  13. package/dist/src/shared/cli/functions/run-search.function.d.ts +25 -16
  14. package/dist/src/shared/cli/index.d.ts +1 -1
  15. package/dist/src/shared/cli/typings/cli-options.interface.d.ts +2 -0
  16. package/dist/src/shared/cli/typings/search-display.model.d.ts +25 -7
  17. package/dist/src/shared/mcp/functions/handle-recall.function.d.ts +29 -0
  18. package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +30 -11
  19. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  20. package/dist/src/shared/wasm-codec/functions/decode-recall-result.function.d.ts +28 -0
  21. package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +14 -7
  22. package/dist/src/shared/wasm-codec/functions/encode-recall-params.function.d.ts +21 -0
  23. package/dist/src/shared/wasm-codec/functions/encode-search-params.function.d.ts +1 -1
  24. package/dist/src/shared/wasm-codec/index.d.ts +5 -2
  25. package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +13 -3
  26. package/dist/src/shared/worker-client/index.d.ts +2 -1
  27. package/dist/src/shared/worker-client/typings/recall-result.model.d.ts +94 -0
  28. package/dist/src/shared/worker-client/typings/search-params.model.d.ts +2 -0
  29. package/dist/src/shared/worker-client/typings/search-result.model.d.ts +23 -7
  30. package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +1 -0
  31. package/dist/wasm/fs-database/kbdb_fs_database.d.ts +18 -0
  32. package/dist/wasm/fs-database/kbdb_fs_database.js +30 -0
  33. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  34. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +2 -0
  35. package/dist/wasm/kb-worker/kbdb_worker.d.ts +38 -0
  36. package/dist/wasm/kb-worker/kbdb_worker.js +56 -0
  37. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  38. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +3 -0
  39. package/dist/worker.mjs +283 -29
  40. package/dist/worker.mjs.map +4 -4
  41. package/docs/details/README.md +26 -0
  42. package/docs/details/cli.md +608 -0
  43. package/docs/details/library-api.md +406 -0
  44. package/docs/details/mcp-server.md +430 -0
  45. package/docs/details/search-and-ranking.md +457 -0
  46. package/docs/details/storage.md +529 -0
  47. package/docs/goals/agents.md +751 -0
  48. package/docs/goals/architecture.svg +198 -0
  49. package/docs/goals/cli.md +2308 -0
  50. package/docs/goals/content-composer.md +609 -0
  51. package/docs/goals/content-parser.md +279 -0
  52. package/docs/goals/database.md +1452 -0
  53. package/docs/goals/document.md +368 -0
  54. package/docs/goals/mcp.md +1467 -0
  55. package/docs/goals/overview.md +124 -0
  56. package/docs/goals/query-parser.md +271 -0
  57. package/docs/goals/query-result.md +693 -0
  58. package/docs/goals/skills.md +738 -0
  59. package/docs/goals/worker-client.md +944 -0
  60. package/docs/goals/worker-daemon.md +1447 -0
  61. package/docs/modules/overview.md +298 -0
  62. package/docs/modules/rust/fs-database/functions/document.md +48 -0
  63. package/docs/modules/rust/fs-database/functions/integrity.md +68 -0
  64. package/docs/modules/rust/fs-database/functions/io.md +87 -0
  65. package/docs/modules/rust/fs-database/functions/module.md +22 -0
  66. package/docs/modules/rust/fs-database/functions/query-exec.md +44 -0
  67. package/docs/modules/rust/fs-database/functions/section.md +59 -0
  68. package/docs/modules/rust/fs-database/indexes/btree.md +35 -0
  69. package/docs/modules/rust/fs-database/indexes/corpus.md +61 -0
  70. package/docs/modules/rust/fs-database/indexes/inverted.md +60 -0
  71. package/docs/modules/rust/fs-database/indexes/module.md +64 -0
  72. package/docs/modules/rust/fs-database/indexes/positional.md +39 -0
  73. package/docs/modules/rust/fs-database/module.md +149 -0
  74. package/docs/modules/rust/fs-database/types/catalog.md +53 -0
  75. package/docs/modules/rust/fs-database/types/module.md +12 -0
  76. package/docs/modules/rust/fs-database/types/section.md +101 -0
  77. package/docs/modules/rust/kb-worker/functions/compose.md +57 -0
  78. package/docs/modules/rust/kb-worker/functions/module.md +20 -0
  79. package/docs/modules/rust/kb-worker/functions/retrieve.md +194 -0
  80. package/docs/modules/rust/kb-worker/functions/search.md +173 -0
  81. package/docs/modules/rust/kb-worker/functions/write.md +172 -0
  82. package/docs/modules/rust/kb-worker/module.md +171 -0
  83. package/docs/modules/rust/kb-worker/types/cache.md +90 -0
  84. package/docs/modules/rust/kb-worker/types/context.md +26 -0
  85. package/docs/modules/rust/kb-worker/types/module.md +16 -0
  86. package/docs/modules/rust/kb-worker/types/status.md +29 -0
  87. package/docs/modules/rust/kb-worker/types/token.md +50 -0
  88. package/docs/modules/rust/overview.md +495 -0
  89. package/docs/modules/rust/query-parser/functions/extract.md +56 -0
  90. package/docs/modules/rust/query-parser/functions/module.md +16 -0
  91. package/docs/modules/rust/query-parser/functions/parse.md +33 -0
  92. package/docs/modules/rust/query-parser/functions/pipeline.md +103 -0
  93. package/docs/modules/rust/query-parser/functions/rank.md +85 -0
  94. package/docs/modules/rust/query-parser/module.md +136 -0
  95. package/docs/modules/rust/query-parser/types/cache.md +39 -0
  96. package/docs/modules/rust/query-parser/types/keyphrase.md +60 -0
  97. package/docs/modules/rust/query-parser/types/module.md +17 -0
  98. package/docs/modules/rust/query-parser/types/token.md +42 -0
  99. package/docs/modules/rust/shared/content-composer/functions.md +114 -0
  100. package/docs/modules/rust/shared/content-composer/module.md +147 -0
  101. package/docs/modules/rust/shared/content-composer/types.md +93 -0
  102. package/docs/modules/rust/shared/content-parser/functions.md +72 -0
  103. package/docs/modules/rust/shared/content-parser/module.md +99 -0
  104. package/docs/modules/rust/shared/content-parser/types.md +71 -0
  105. package/docs/modules/rust/shared/corpus/module.md +24 -0
  106. package/docs/modules/rust/shared/corpus/types.md +141 -0
  107. package/docs/modules/rust/shared/document/module.md +25 -0
  108. package/docs/modules/rust/shared/document/types.md +154 -0
  109. package/docs/modules/rust/shared/encode/module.md +22 -0
  110. package/docs/modules/rust/shared/encode/traits.md +304 -0
  111. package/docs/modules/rust/shared/error/module.md +28 -0
  112. package/docs/modules/rust/shared/error/types.md +302 -0
  113. package/docs/modules/rust/shared/kbid/module.md +24 -0
  114. package/docs/modules/rust/shared/kbid/types.md +147 -0
  115. package/docs/modules/rust/shared/memory/functions.md +209 -0
  116. package/docs/modules/rust/shared/memory/module.md +24 -0
  117. package/docs/modules/rust/shared/module.md +168 -0
  118. package/docs/modules/rust/shared/query/module.md +27 -0
  119. package/docs/modules/rust/shared/query/types.md +236 -0
  120. package/docs/modules/rust/shared/section/module.md +25 -0
  121. package/docs/modules/rust/shared/section/types.md +294 -0
  122. package/docs/modules/rust/shared/term/module.md +25 -0
  123. package/docs/modules/rust/shared/term/types.md +139 -0
  124. package/docs/modules/typescript/cli.md +129 -0
  125. package/docs/modules/typescript/kbdb-worker.md +135 -0
  126. package/docs/modules/typescript/overview.md +378 -0
  127. package/docs/modules/typescript/shared/cli/constants.md +23 -0
  128. package/docs/modules/typescript/shared/cli/functions.md +630 -0
  129. package/docs/modules/typescript/shared/cli/module.md +125 -0
  130. package/docs/modules/typescript/shared/cli/typings.md +172 -0
  131. package/docs/modules/typescript/shared/hash/functions.md +20 -0
  132. package/docs/modules/typescript/shared/hash/module.md +21 -0
  133. package/docs/modules/typescript/shared/mcp/classes.md +179 -0
  134. package/docs/modules/typescript/shared/mcp/functions.md +386 -0
  135. package/docs/modules/typescript/shared/mcp/module.md +160 -0
  136. package/docs/modules/typescript/shared/mcp/typings.md +522 -0
  137. package/docs/modules/typescript/shared/module.md +94 -0
  138. package/docs/modules/typescript/shared/platform/functions.md +42 -0
  139. package/docs/modules/typescript/shared/platform/module.md +25 -0
  140. package/docs/modules/typescript/shared/runtime/functions.md +26 -0
  141. package/docs/modules/typescript/shared/runtime/module.md +27 -0
  142. package/docs/modules/typescript/shared/runtime/typings.md +13 -0
  143. package/docs/modules/typescript/shared/version/module.md +27 -0
  144. package/docs/modules/typescript/shared/wasm-codec/functions.md +391 -0
  145. package/docs/modules/typescript/shared/wasm-codec/module.md +98 -0
  146. package/docs/modules/typescript/shared/worker-client/classes.md +264 -0
  147. package/docs/modules/typescript/shared/worker-client/functions.md +175 -0
  148. package/docs/modules/typescript/shared/worker-client/module.md +84 -0
  149. package/docs/modules/typescript/shared/worker-client/typings.md +508 -0
  150. package/docs/modules/typescript/shared/worker-daemon/classes.md +239 -0
  151. package/docs/modules/typescript/shared/worker-daemon/constants.md +37 -0
  152. package/docs/modules/typescript/shared/worker-daemon/functions.md +221 -0
  153. package/docs/modules/typescript/shared/worker-daemon/module.md +112 -0
  154. package/docs/modules/typescript/shared/worker-daemon/typings.md +126 -0
  155. package/docs/overview.md +188 -0
  156. package/docs/release-notes/0.1.0.md +189 -0
  157. package/docs/release-notes/0.1.1.md +46 -0
  158. package/docs/release-notes/0.1.2.md +38 -0
  159. package/docs/release-notes/0.1.3.md +42 -0
  160. package/docs/release-notes/0.2.0.md +147 -0
  161. package/docs/release-notes/README.md +9 -0
  162. package/package.json +8 -2
  163. package/src/shared/cli/functions/dispatch-command.function.ts +3 -3
  164. package/src/shared/cli/functions/format-output.function.ts +41 -34
  165. package/src/shared/cli/functions/parse-args.function.ts +7 -0
  166. package/src/shared/cli/functions/run-search.function.ts +53 -27
  167. package/src/shared/cli/index.ts +4 -1
  168. package/src/shared/cli/typings/cli-options.interface.ts +2 -0
  169. package/src/shared/cli/typings/search-display.model.ts +26 -7
  170. package/src/shared/mcp/functions/create-tool-definitions.function.ts +64 -4
  171. package/src/shared/mcp/functions/handle-recall.function.ts +66 -0
  172. package/src/shared/mcp/functions/handle-tool-call.function.ts +11 -1
  173. package/src/shared/mcp/typings/mcp-client.interface.ts +33 -15
  174. package/src/shared/version/constants/version.constant.ts +1 -1
  175. package/src/shared/wasm-codec/functions/decode-recall-result.function.ts +267 -0
  176. package/src/shared/wasm-codec/functions/decode-scored-results.function.ts +81 -27
  177. package/src/shared/wasm-codec/functions/encode-recall-params.function.ts +68 -0
  178. package/src/shared/wasm-codec/functions/encode-search-params.function.ts +4 -1
  179. package/src/shared/wasm-codec/index.ts +11 -3
  180. package/src/shared/worker-client/classes/worker-client.class.ts +24 -4
  181. package/src/shared/worker-client/index.ts +11 -1
  182. package/src/shared/worker-client/typings/recall-result.model.ts +98 -0
  183. package/src/shared/worker-client/typings/search-params.model.ts +2 -0
  184. package/src/shared/worker-client/typings/search-result.model.ts +24 -7
  185. package/src/shared/worker-daemon/classes/worker-daemon.class.ts +25 -4
  186. package/src/wasm/fs-database/kbdb_fs_database.d.ts +18 -0
  187. package/src/wasm/fs-database/kbdb_fs_database.js +30 -0
  188. package/src/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  189. package/src/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +2 -0
  190. package/src/wasm/kb-worker/kbdb_worker.d.ts +38 -0
  191. package/src/wasm/kb-worker/kbdb_worker.js +56 -0
  192. package/src/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  193. package/src/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +3 -0
@@ -0,0 +1,2308 @@
1
+ # CLI -- The kbdb Command
2
+
3
+ *A globally installed command that manages knowledge
4
+ base entries in CLI mode and serves as an MCP server
5
+ for AI agents in MCP mode.*
6
+
7
+ ## Goal
8
+
9
+ The `kbdb` executable is a Deno or Node.js command
10
+ that operates in two run modes:
11
+
12
+ 1. **CLI mode** (default) -- Short-lived commands for
13
+ content management and search.
14
+ 2. **MCP mode** (`kbdb mcp`) -- Long-lived MCP server
15
+ for AI agent integration.
16
+
17
+ The run mode is determined by the first argument. If
18
+ the first argument is `mcp`, the executable starts in
19
+ MCP mode (see [MCP](mcp.md)). Otherwise, it runs in
20
+ CLI mode.
21
+
22
+ In CLI mode, the executable is a thin pass-through.
23
+ It parses command-line arguments, creates a worker
24
+ client (see [Worker Client](worker-client.md)),
25
+ delegates all computation to the daemon, prints
26
+ results to stdout, and prints errors to stderr. The
27
+ CLI does not perform keyword extraction, indexing,
28
+ ranking, or database I/O itself -- the daemon and its
29
+ WASM modules handle all of it.
30
+
31
+ ## Installation
32
+
33
+ - **Node.js**: `npm install -g @dikolab/kbdb`
34
+ - **Deno**: `deno install` or `deno add @dikolab/kbdb`
35
+ (via [jsr.io](https://jsr.io))
36
+
37
+ After installation, the `kbdb` command is available
38
+ globally in the user's shell.
39
+
40
+ ## Synopsis
41
+
42
+ ```
43
+ kbdb [options]
44
+ kbdb <command> [options] ...[parameters]
45
+ kbdb <command> <subcommand> [options] ...[parameters]
46
+ ```
47
+
48
+ All commands and their canonical forms:
49
+
50
+ ```
51
+ kbdb --help | -h
52
+ kbdb --version | -v
53
+ kbdb learn [options] <path | ->...
54
+ kbdb unlearn [options] <kbid>...
55
+ kbdb search [options] <query>...
56
+ kbdb content [options] <id>...
57
+ kbdb mcp [options]
58
+ kbdb check [options]
59
+ kbdb gc [options]
60
+ kbdb rebuild [options]
61
+ kbdb status [options]
62
+ kbdb db init [options]
63
+ kbdb db migrate [options]
64
+ kbdb skill learn [options] --name <n> --body <file|->
65
+ kbdb skill list [options]
66
+ kbdb skill get [options] <name-or-kbid>
67
+ kbdb skill delete [options] <kbid>
68
+ kbdb agent create [options] --name <n>
69
+ --persona <file|-> --skills <kbid,...>
70
+ kbdb agent list [options]
71
+ kbdb agent get [options] <docid>
72
+ kbdb agent delete [options] <docid>
73
+ ```
74
+
75
+ The help manual shows `[options]` immediately after
76
+ the command or subcommand to emphasise the canonical
77
+ form. The parser accepts options at any position in
78
+ the command line.
79
+
80
+ ## Run Modes
81
+
82
+ The `kbdb` executable inspects its first argument to
83
+ determine the run mode:
84
+
85
+ ```
86
+ kbdb mcp [--db <path>] -> MCP server mode
87
+ kbdb <command> [options] -> CLI mode (default)
88
+ ```
89
+
90
+ ### CLI Mode
91
+
92
+ All commands except `mcp` run in CLI mode. The
93
+ executable parses arguments, delegates to the worker
94
+ daemon via the worker client module, prints results,
95
+ and exits. Each invocation is a short-lived process.
96
+
97
+ ### MCP Mode
98
+
99
+ When the first argument is `mcp`, the executable
100
+ starts as a long-lived MCP server. It communicates
101
+ with the AI agent host over stdio using JSON-RPC 2.0,
102
+ and delegates all operations to the worker daemon via
103
+ the same worker client module.
104
+
105
+ MCP mode requires `--db <path>` or the `KBDB_DIR`
106
+ environment variable. It does not fall back to the
107
+ current working directory because the server's cwd is
108
+ determined by the host application, not the user.
109
+
110
+ See [MCP](mcp.md) for the full MCP mode
111
+ specification.
112
+
113
+ ## Global Options
114
+
115
+ These options apply to all commands.
116
+
117
+ | Option | Short | Description |
118
+ |---------------|-------|--------------------------|
119
+ | `--help` | `-h` | Print help text and exit |
120
+ | `--version` | `-v` | Print version and exit |
121
+ | `--db <path>` | | Database directory path |
122
+
123
+ ### `--help`, `-h`
124
+
125
+ Print the help text and exit immediately. No database
126
+ connection is made.
127
+
128
+ ```
129
+ kbdb --help
130
+ kbdb -h
131
+ ```
132
+
133
+ When no command is given (`kbdb` with no arguments),
134
+ the help text is printed as the default behaviour.
135
+
136
+ ### `--version`, `-v`
137
+
138
+ Print the package version string and exit. No database
139
+ connection is made.
140
+
141
+ ```
142
+ kbdb --version
143
+ kbdb -v
144
+ ```
145
+
146
+ ### `--db <path>`
147
+
148
+ Set the path to the directory that contains (or will
149
+ contain) the `.kbdb` database folder. The database
150
+ directory is always `${path}/.kbdb`.
151
+
152
+ ```
153
+ kbdb search "query" --db /path/to/project
154
+ ```
155
+
156
+ When omitted:
157
+ - **CLI mode**: defaults to the current working
158
+ directory (`process.cwd()`).
159
+ - **MCP mode**: falls back to the `KBDB_DIR`
160
+ environment variable. If neither `--db` nor
161
+ `KBDB_DIR` is set, the MCP server prints an error
162
+ and exits non-zero.
163
+
164
+ ## Commands
165
+
166
+ ### `learn` -- import documents
167
+
168
+ ```
169
+ kbdb learn [options] <path | ->...
170
+ ```
171
+
172
+ Import and index documents into the knowledge base.
173
+
174
+ **Arguments:**
175
+ - `<path | ->...` -- one or more file paths, directory
176
+ paths, or `-` for stdin. When a path is a directory,
177
+ it is scanned recursively for supported **text**
178
+ files only (`.md`, `.txt`, `.rst`, `.adoc`). Dotfiles
179
+ and dot-directories are skipped at every level.
180
+ Unsupported extensions are silently skipped. Multiple
181
+ paths can be specified. When a path is `-`, content
182
+ is read from stdin until EOF and ingested as a text
183
+ section. The `-` argument can appear alongside
184
+ file/directory paths. Stdin is consumed once -- if
185
+ `-` appears multiple times, only the first reads
186
+ stdin. Subsequent `-` arguments are silently ignored.
187
+
188
+ **Options:**
189
+ - `--title <title>` -- Section title. Optional for all
190
+ section types. Applies to all sections ingested in
191
+ this invocation.
192
+ - `--description <desc>` -- Section description.
193
+ Optional for all section types. Applies to all
194
+ sections ingested in this invocation.
195
+ - `--docid <docid>` -- Attach imported section(s)
196
+ to this document ID. If the document does not
197
+ exist, it is created. Applies to all sections
198
+ ingested in this invocation.
199
+
200
+ **Supported file extensions:**
201
+
202
+ | Extension | Directory Scan |
203
+ |-----------|----------------|
204
+ | `.md` | yes |
205
+ | `.txt` | yes |
206
+ | `.rst` | yes |
207
+ | `.adoc` | yes |
208
+
209
+ Extensions are defined in
210
+ `src/shared/cli/constants/defaults.constant.ts` as
211
+ `SUPPORTED_EXTENSIONS`. Unsupported extensions are
212
+ silently skipped. Image files are not supported.
213
+
214
+ **Behaviour:**
215
+
216
+ The `learn` command collects files from each argument:
217
+
218
+ 1. **Directory path** -- Recursive scan picks up
219
+ supported text files (`.md`, `.txt`, `.rst`,
220
+ `.adoc`). Dotfiles and dot-directories are skipped
221
+ at every level.
222
+ 2. **Text file path** -- Reads the file as UTF-8. If
223
+ the extension is not in `SUPPORTED_EXTENSIONS`, the
224
+ file is silently skipped.
225
+ 3. **Stdin (`-`)** -- Reads text content from stdin
226
+ until EOF. Stdin is consumed only once regardless of
227
+ how many `-` arguments appear.
228
+
229
+ Each collected file is ingested by calling
230
+ `client.addSection()`. The `docid`, `title`, and
231
+ `description` options from the CLI apply to all
232
+ sections in the same invocation.
233
+
234
+ **Output:**
235
+ - **stdout**: JSON array of `LearnResult` objects:
236
+ ```json
237
+ [
238
+ {
239
+ "kbid": "abcdef1234567890abcdef1234",
240
+ "path": "/absolute/path/to/file.md",
241
+ "size": 1234,
242
+ "skipped": false
243
+ }
244
+ ]
245
+ ```
246
+ For stdin input, `path` is `"-"`. The `size` field
247
+ is the UTF-8 character count of the content. The
248
+ `skipped` field is always `false` for results that
249
+ appear in the array -- files that are skipped produce
250
+ no entry.
251
+ - **stderr**: errors only.
252
+
253
+ **Exit code:** `0` on success, `1` on error.
254
+
255
+ **Delegation chain:**
256
+ ```
257
+ src/cli.ts parseArgs()
258
+ -> findDb() / promptUser() / runDbInit()
259
+ -> createWorkerClient({ contextPath })
260
+ -> versionGate(client, dbDir)
261
+ -> dispatchCommand(client, options)
262
+ -> runLearn(client, options, io?)
263
+ -> client.addSection({
264
+ sectionType, content,
265
+ title?, description?, docid? })
266
+ -> IPC -> worker daemon
267
+ -> kb-worker.wasm
268
+ -> query-parser.wasm (tokenize)
269
+ -> fs-database.wasm (store + index)
270
+ ```
271
+
272
+ ### `unlearn` -- remove sections
273
+
274
+ ```
275
+ kbdb unlearn [options] <kbid>...
276
+ ```
277
+
278
+ Remove knowledge base sections by their kb-id.
279
+
280
+ **Arguments:**
281
+ - `<kbid>...` -- one or more 26-character kb-id
282
+ strings. Each is processed independently.
283
+
284
+ **Behaviour:**
285
+ - The engine removes the referenced content and
286
+ cascades the deletion to associated index entries
287
+ and ranking data.
288
+ - If a kb-id does not exist, the result shows
289
+ `"removed": false` rather than throwing an error.
290
+
291
+ **Output:**
292
+ - **stdout**: JSON array of `UnlearnResult` objects:
293
+ ```json
294
+ [
295
+ {
296
+ "kbid": "abcdef1234567890abcdef1234",
297
+ "removed": true
298
+ }
299
+ ]
300
+ ```
301
+ - **stderr**: errors only.
302
+
303
+ **Exit code:** `0` on success, `1` on error.
304
+
305
+ **Delegation chain:**
306
+ ```
307
+ src/cli.ts parseArgs()
308
+ -> findDb() / promptUser() / runDbInit()
309
+ -> createWorkerClient({ contextPath })
310
+ -> versionGate(client, dbDir)
311
+ -> dispatchCommand(client, options)
312
+ -> runUnlearn(client, options)
313
+ -> client.removeSection(kbid)
314
+ -> IPC -> worker daemon
315
+ -> kb-worker.wasm
316
+ -> fs-database.wasm
317
+ (delete + re-index)
318
+ ```
319
+
320
+ ### `search` -- query the knowledge base
321
+
322
+ ```
323
+ kbdb search [options] <query>...
324
+ ```
325
+
326
+ Search the knowledge base by keywords or phrases.
327
+
328
+ **Arguments:**
329
+ - `<query>...` -- one or more query terms. All terms
330
+ are joined with spaces into a single query string.
331
+ Returns an empty result set if no terms are given.
332
+
333
+ **Search options:**
334
+
335
+ | Option | Short | Default | Description |
336
+ |------------------|-------|------------|--------------------|
337
+ | `--mode <mode>` | `-m` | `sections` | Result granularity |
338
+ | `--limit <n>` | `-l` | `20` | Max results |
339
+ | `--offset <n>` | `-o` | `0` | Result offset |
340
+ | `--format <fmt>` | `-f` | `json` | Output format |
341
+ | `--content` | `-c` | | Hydrate previews |
342
+ | `--db <path>` | | cwd | Database directory |
343
+
344
+ #### `--mode`, `-m`
345
+
346
+ Controls the granularity of search results.
347
+
348
+ | Mode | Description |
349
+ |-------------|--------------------------------------|
350
+ | `sections` | Individual matching sections (default). |
351
+ | `documents` | Complete documents containing matches. |
352
+ | `stats` | Index statistics and result counts. |
353
+
354
+ ```
355
+ kbdb search "machine learning" --mode documents
356
+ kbdb search "API endpoint" -m stats
357
+ ```
358
+
359
+ #### `--limit`, `-l`
360
+
361
+ Maximum number of results to return. Must be a
362
+ positive integer. Non-numeric values fall back to the
363
+ default (20).
364
+
365
+ ```
366
+ kbdb search "query" --limit 50
367
+ kbdb search "query" -l 5
368
+ ```
369
+
370
+ #### `--offset`, `-o`
371
+
372
+ Zero-based offset into the ranked result set. Used
373
+ for pagination alongside `--limit`. Must be a
374
+ non-negative integer. Non-numeric values fall back
375
+ to the default (0).
376
+
377
+ ```
378
+ kbdb search "query" --offset 20
379
+ kbdb search "query" -o 40 -l 20
380
+ ```
381
+
382
+ #### `--format`, `-f`
383
+
384
+ Controls the output format printed to stdout.
385
+
386
+ | Format | Description |
387
+ |--------|-----------------------------------------|
388
+ | `json` | Pretty-printed JSON array (default). |
389
+ | `text` | Numbered human-readable list with scores and matched terms. |
390
+ | `mcp` | JSON envelope for MCP protocol consumers. |
391
+
392
+ ```
393
+ kbdb search "query" --format text
394
+ kbdb search "query" -f mcp
395
+ ```
396
+
397
+ #### `--content`, `-c`
398
+
399
+ When specified, hydrates each result's `preview` field
400
+ with the actual section content fetched from the
401
+ worker daemon via `client.content()`. Without this
402
+ flag, `preview` is an empty string.
403
+
404
+ ```
405
+ kbdb search "query" --content
406
+ kbdb search "query" -c
407
+ ```
408
+
409
+ Failures to fetch content for individual results are
410
+ suppressed -- the result is still returned with an
411
+ empty `preview`.
412
+
413
+ **Output shape:**
414
+
415
+ All search output is formatted from a `SearchDisplay[]`
416
+ array (see [SearchDisplay type](#searchdisplay-type)).
417
+
418
+ `--format json` (default):
419
+ ```json
420
+ {
421
+ "items": [
422
+ {
423
+ "kbid": "abcdef1234567890abcdef1234",
424
+ "heading": "Machine Learning Basics",
425
+ "type": "text/markdown",
426
+ "docids": ["doc-intro-ml"],
427
+ "score": 8.7432,
428
+ "snippet": "Machine learning is a subfield...",
429
+ "matched_terms": ["machine", "learning"],
430
+ "preview": ""
431
+ }
432
+ ],
433
+ "total": 42,
434
+ "offset": 0,
435
+ "limit": 20,
436
+ "has_more": true
437
+ }
438
+ ```
439
+
440
+ When `--content` is used, `preview` contains the
441
+ section text:
442
+ ```json
443
+ {
444
+ "items": [
445
+ {
446
+ "kbid": "abcdef1234567890abcdef1234",
447
+ "heading": "Machine Learning Basics",
448
+ "type": "text/markdown",
449
+ "docids": ["doc-intro-ml"],
450
+ "score": 8.7432,
451
+ "snippet": "Machine learning is a subfield...",
452
+ "matched_terms": ["machine", "learning"],
453
+ "preview": "Machine learning is a subfield..."
454
+ }
455
+ ],
456
+ "total": 42,
457
+ "offset": 0,
458
+ "limit": 20,
459
+ "has_more": true
460
+ }
461
+ ```
462
+
463
+ `--format text`:
464
+ ```
465
+ 1. [abcdef1234567890abcdef1234] (score: 8.7432)
466
+ Machine Learning Basics [text/markdown]
467
+ Machine learning is a subfield...
468
+ matches: machine, learning
469
+
470
+ 2. [fedcba9876543210fedcba9876] (score: 6.1200)
471
+ Neural Networks [text/markdown]
472
+ Neural networks are computing systems...
473
+ matches: machine
474
+
475
+ Showing 1-20 of 42 results
476
+ ```
477
+
478
+ When `--content` is also used, each entry includes
479
+ the preview text between the kbid line and the
480
+ matches line.
481
+
482
+ `--format mcp`:
483
+ ```json
484
+ {
485
+ "type": "text",
486
+ "text": "[abcdef...] score=8.7432 — ..."
487
+ }
488
+ ```
489
+
490
+ **Exit code:** `0` on success, `1` on error.
491
+
492
+ **Delegation chain:**
493
+ ```
494
+ src/cli.ts parseArgs()
495
+ -> findDb() / promptUser() / runDbInit()
496
+ -> createWorkerClient({ contextPath })
497
+ -> versionGate(client, dbDir)
498
+ -> dispatchCommand(client, options)
499
+ -> runSearch(client, options)
500
+ -> client.search({
501
+ query, mode, limit, offset })
502
+ -> IPC -> worker daemon
503
+ -> kb-worker.wasm
504
+ -> query-parser.wasm
505
+ (parse query)
506
+ -> fs-database.wasm
507
+ (index lookup)
508
+ -> query-parser.wasm
509
+ (rank results)
510
+ -> hydrateSearchPreviews(client, results)
511
+ (only when options.content === true)
512
+ -> formatOutput(results, options.format)
513
+ -> stdout
514
+ ```
515
+
516
+ ### `content` -- compose rendered document
517
+
518
+ ```
519
+ kbdb content [options] <id>...
520
+ ```
521
+
522
+ Compose a rendered Markdown document from one or
523
+ more knowledge base identifiers and output it to
524
+ stdout.
525
+
526
+ **Arguments:**
527
+ - `<id>...` -- one or more kbid or docid identifiers.
528
+
529
+ **Output:**
530
+ - **stdout**: JSON envelope containing the composed
531
+ Markdown text:
532
+ ```json
533
+ {
534
+ "type": "text/markdown",
535
+ "data": "# Rendered Markdown\n\nContent here...",
536
+ "total": 42
537
+ }
538
+ ```
539
+ - **stderr**: errors only.
540
+
541
+ **Exit code:** `0` on success, `1` on error.
542
+
543
+ **Delegation chain:**
544
+ ```
545
+ src/cli.ts parseArgs()
546
+ -> findDb() / promptUser() / runDbInit()
547
+ -> createWorkerClient({ contextPath })
548
+ -> versionGate(client, dbDir)
549
+ -> dispatchCommand(client, options)
550
+ -> runContent(client, options)
551
+ -> client.content(ids)
552
+ -> IPC -> worker daemon
553
+ -> kb-worker.wasm
554
+ -> fs-database.wasm (fetch)
555
+ ```
556
+
557
+ See [Content Composer](content-composer.md) for the
558
+ full composition specification.
559
+
560
+ ### `mcp` -- start MCP server
561
+
562
+ ```
563
+ kbdb mcp [options]
564
+ ```
565
+
566
+ Start a long-lived MCP server on stdio.
567
+
568
+ **Behaviour:**
569
+ - Communicates with the AI agent host over stdio
570
+ using JSON-RPC 2.0.
571
+ - Requires `--db <path>` or the `KBDB_DIR` environment
572
+ variable. Does NOT fall back to the current working
573
+ directory.
574
+ - Runs a version gate before starting: exits with an
575
+ error if the database needs migration or is too new.
576
+ - The MCP server does not prompt, does not print
577
+ banners, and does not write to stdout except for
578
+ JSON-RPC responses.
579
+ - Runs until the host closes the stdio stream or sends
580
+ a shutdown signal.
581
+
582
+ **Environment variables:**
583
+ - `KBDB_DIR` -- fallback database directory path when
584
+ `--db` is not specified.
585
+
586
+ **Exit code:** `0` on clean shutdown, `1` on error.
587
+
588
+ See [MCP](mcp.md) for the full specification of
589
+ tools, resources, and protocol details.
590
+
591
+ **Delegation chain:**
592
+ ```
593
+ src/cli.ts parseArgs()
594
+ -> findDb(rawDbPath)
595
+ -> createWorkerClient({ contextPath: dbDir })
596
+ -> versionGate(client, dbDir)
597
+ -> deps.startMcpServer(dbDir)
598
+ (stdio JSON-RPC 2.0 loop)
599
+ ```
600
+
601
+ ### CLI vs MCP tool coverage
602
+
603
+ The MCP server exposes `retrieve` (section/document
604
+ lookup by ID) and `status` (database metadata) tools
605
+ that have no CLI equivalents. These are programmatic
606
+ operations suited to MCP clients (AI agents). CLI
607
+ users use `kbdb search` to find content and
608
+ `kbdb content` to compose documents by ID.
609
+
610
+ ### `check` -- integrity check
611
+
612
+ ```
613
+ kbdb check [options]
614
+ ```
615
+
616
+ Verify the integrity of the database. Checks section
617
+ file checksums, document manifest references, and
618
+ type consistency.
619
+
620
+ **Output:**
621
+ - **stdout**: JSON integrity report:
622
+ ```json
623
+ {
624
+ "ok": false,
625
+ "errors": [
626
+ {
627
+ "type": "orphan_section",
628
+ "entity": "x9y8z7w6v5u4t3s2r1q0p9o8n7",
629
+ "detail": "Section not referenced by any document"
630
+ }
631
+ ]
632
+ }
633
+ ```
634
+ When all checks pass, `ok` is `true` and `errors`
635
+ is empty.
636
+ - **stderr**: errors only.
637
+
638
+ **Exit code:** `0` when `ok` is `true`, `1` when
639
+ errors are found or the command itself fails.
640
+
641
+ **Delegation chain:**
642
+ ```
643
+ src/cli.ts parseArgs()
644
+ -> findDb() / promptUser() / runDbInit()
645
+ -> createWorkerClient({ contextPath })
646
+ -> versionGate(client, dbDir)
647
+ -> dispatchCommand(client, options)
648
+ -> runCheck(client, options)
649
+ -> client.integrityCheck()
650
+ -> IPC -> worker daemon
651
+ -> kb-worker.wasm
652
+ -> fs-database.wasm
653
+ -> stdout
654
+ ```
655
+
656
+ See [Database -- Integrity and
657
+ Maintenance](database.md) for the full list of
658
+ checks and error types.
659
+
660
+ ### `gc` -- garbage collection
661
+
662
+ ```
663
+ kbdb gc [options]
664
+ ```
665
+
666
+ Remove unreferenced sections from the database.
667
+ A section is eligible for removal when no document
668
+ manifest references it (empty `docids` array).
669
+
670
+ **Output:**
671
+ - **stdout**: JSON report:
672
+ ```json
673
+ {
674
+ "removed_sections": 3,
675
+ "removed_bytes": 15360
676
+ }
677
+ ```
678
+ When no sections are eligible, `removed_sections`
679
+ is `0` and `removed_bytes` is `0`.
680
+ - **stderr**: errors only.
681
+
682
+ **Exit code:** `0` on success, `1` on error.
683
+
684
+ **Delegation chain:**
685
+ ```
686
+ src/cli.ts parseArgs()
687
+ -> findDb() / promptUser() / runDbInit()
688
+ -> createWorkerClient({ contextPath })
689
+ -> versionGate(client, dbDir)
690
+ -> dispatchCommand(client, options)
691
+ -> runGc(client, options)
692
+ -> client.gc()
693
+ -> IPC -> worker daemon
694
+ -> kb-worker.wasm
695
+ -> fs-database.wasm
696
+ -> stdout
697
+ ```
698
+
699
+ ### `rebuild` -- rebuild indexes
700
+
701
+ ```
702
+ kbdb rebuild [options]
703
+ ```
704
+
705
+ Reconstruct all index files from section files and
706
+ document manifests on disk.
707
+
708
+ **Output:**
709
+ - **stdout**: JSON report:
710
+ ```json
711
+ {
712
+ "sections_reindexed": 39,
713
+ "terms_indexed": 1500
714
+ }
715
+ ```
716
+ - **stderr**: errors only.
717
+
718
+ **Exit code:** `0` on success, `1` on error.
719
+
720
+ **Delegation chain:**
721
+ ```
722
+ src/cli.ts parseArgs()
723
+ -> findDb() / promptUser() / runDbInit()
724
+ -> createWorkerClient({ contextPath })
725
+ -> versionGate(client, dbDir)
726
+ -> dispatchCommand(client, options)
727
+ -> runRebuild(client, options)
728
+ -> client.rebuildIndexes()
729
+ -> IPC -> worker daemon
730
+ -> kb-worker.wasm
731
+ -> query-parser.wasm (tokenize)
732
+ -> fs-database.wasm
733
+ (store indexes)
734
+ -> stdout
735
+ ```
736
+
737
+ ### `kbdb db` -- database management
738
+
739
+ The `db` command group manages the `.kbdb` database
740
+ directory lifecycle. It replaces the deprecated
741
+ `--init` and `--migrate` flags with a consistent
742
+ `kbdb db <subcommand>` pattern.
743
+
744
+ #### `db init` -- initialise database
745
+
746
+ ```
747
+ kbdb db init [options]
748
+ ```
749
+
750
+ Create a blank `.kbdb` database directory at the
751
+ target path. This is an explicit initialisation
752
+ command -- it does not silently create directories.
753
+
754
+ `db init` sets `options.command = 'db-init'` and is
755
+ handled before the data-command path (no daemon
756
+ required).
757
+
758
+ **Options:**
759
+
760
+ | Option | Description |
761
+ |---------------|------------------------------------------|
762
+ | `--db <path>` | Path to create `.kbdb` in. Defaults to cwd. |
763
+
764
+ **Behaviour:**
765
+ - Resolves target path from `--db` or `process.cwd()`.
766
+ - Creates the `.kbdb` directory scaffold directly on
767
+ disk (no daemon required): `catalog.toml`,
768
+ `corpus.bin`, `inverted.idx`, `positional.idx`,
769
+ `sections/`, `documents/`, `worker.toml`,
770
+ `.gitignore`.
771
+ - The database is stamped with `DB_FORMAT_VERSION = 1`.
772
+ - If `.kbdb` already exists: prints error to stderr,
773
+ exits non-zero.
774
+ - Implemented by `runDbInit()` in
775
+ `src/shared/cli/functions/run-db-init.function.ts`.
776
+
777
+ **Output:**
778
+ - **stdout**: JSON object on success:
779
+ ```json
780
+ { "path": "/absolute/path/.kbdb", "created": true }
781
+ ```
782
+ - **stderr**: error message on failure.
783
+
784
+ **Exit code:** `0` on success, `1` on error.
785
+
786
+ **Delegation chain:**
787
+ ```
788
+ src/cli.ts parseArgs()
789
+ -> dispatchCommand(options)
790
+ -> runDbInit(options)
791
+ -> createScaffold(dbDir)
792
+ (writes files directly to disk)
793
+ ```
794
+
795
+ **Examples:**
796
+ ```sh
797
+ # Initialise in the current directory
798
+ kbdb db init
799
+
800
+ # Initialise in a specific directory
801
+ kbdb db init --db /path/to/project
802
+ ```
803
+
804
+ #### `db migrate` -- migrate database
805
+
806
+ ```
807
+ kbdb db migrate [options]
808
+ ```
809
+
810
+ Run sequential migration scripts to upgrade a `.kbdb`
811
+ database directory from an older format version to the
812
+ current version.
813
+
814
+ `db migrate` sets `options.command = 'db-migrate'`.
815
+
816
+ **Options:**
817
+
818
+ | Option | Description |
819
+ |---------------|------------------------------------------|
820
+ | `--db <path>` | Path containing `.kbdb`. Defaults to cwd. |
821
+
822
+ **Behaviour:**
823
+ - Resolves target path from `--db` or `process.cwd()`.
824
+ - Requires an existing `.kbdb` directory. Returns an
825
+ error if none is found.
826
+ - Calls `client.checkVersion(dbDir)` to read the
827
+ catalog version:
828
+ - `needs_migration` (DB version < WASM version):
829
+ calls `client.migrateDatabase(dbDir)`, prints the
830
+ JSON `MigrateResult`, exits 0.
831
+ - `ok` (DB version == WASM version): prints
832
+ "already up to date", exits 0.
833
+ - `too_new` (DB version > WASM version): prints
834
+ error to stderr, exits 1.
835
+
836
+ **Output:**
837
+ - **stdout**: `MigrateResult` JSON on success, or
838
+ "already up to date" when already current.
839
+ - **stderr**: error message on failure.
840
+
841
+ **Exit code:** `0` on success, `1` on error.
842
+
843
+ **Delegation chain:**
844
+ ```
845
+ src/cli.ts parseArgs()
846
+ -> findDb() / promptUser() / runDbInit()
847
+ -> createWorkerClient({ contextPath })
848
+ -> dispatchCommand(client, options)
849
+ -> runDbMigrate(client, options)
850
+ -> client.checkVersion(dbDir)
851
+ -> client.migrateDatabase(dbDir)
852
+ -> IPC -> worker daemon
853
+ -> fs-migration.wasm
854
+ (db_migrate -> migration chain)
855
+ ```
856
+
857
+ **Examples:**
858
+ ```sh
859
+ # Migrate the database in the current directory
860
+ kbdb db migrate
861
+
862
+ # Migrate a database at a specific path
863
+ kbdb db migrate --db /path/to/project
864
+ ```
865
+
866
+ ### `--init` -- initialise database (deprecated)
867
+
868
+ > **Deprecated.** Use `kbdb db init` instead.
869
+ > `kbdb --init` still works but emits a deprecation
870
+ > warning to stderr:
871
+ > `warning: --init is deprecated, use "kbdb db init"`
872
+
873
+ ```
874
+ kbdb --init [--db <path>]
875
+ ```
876
+
877
+ Create a blank `.kbdb` database directory at the
878
+ target path. This is an explicit initialisation
879
+ command -- it does not silently create directories.
880
+
881
+ `--init` sets `options.command = 'init'` and is
882
+ handled before the data-command path.
883
+
884
+ **Behaviour:**
885
+ - Resolves target path from `--db` or `process.cwd()`.
886
+ - Creates the `.kbdb` directory scaffold directly on
887
+ disk (no daemon required): `catalog.toml`,
888
+ `corpus.bin`, `inverted.idx`, `positional.idx`,
889
+ `sections/`, `documents/`, `worker.toml`,
890
+ `.gitignore`.
891
+ - The database is stamped with `DB_FORMAT_VERSION = 1`.
892
+ - If `.kbdb` already exists: prints error to stderr,
893
+ exits non-zero.
894
+ - Implemented by `runDbInit()` in
895
+ `src/shared/cli/functions/run-db-init.function.ts`.
896
+
897
+ **Output:**
898
+ - **stdout**: the absolute path to the created `.kbdb`
899
+ directory on success.
900
+ - **stderr**: error message on failure.
901
+
902
+ **Exit code:** `0` on success, `1` on error.
903
+
904
+ **Delegation chain:**
905
+ ```
906
+ src/cli.ts parseArgs()
907
+ -> handleInit(options, deps)
908
+ -> deps.runDbInit(options)
909
+ -> createScaffold(dbDir)
910
+ (writes files directly to disk)
911
+ ```
912
+
913
+ ### `--migrate` -- migrate database (deprecated)
914
+
915
+ > **Deprecated.** Use `kbdb db migrate` instead.
916
+ > `kbdb --migrate` still works but emits a
917
+ > deprecation warning to stderr:
918
+ > `warning: --migrate is deprecated, use "kbdb db migrate"`
919
+
920
+ ```
921
+ kbdb --migrate [--db <path>]
922
+ ```
923
+
924
+ Run sequential migration scripts to upgrade a `.kbdb`
925
+ database directory from an older format version to the
926
+ current version.
927
+
928
+ `--migrate` sets `options.command = 'migrate'` and is
929
+ handled before the data-command path.
930
+
931
+ **Behaviour:**
932
+ - Resolves target path from `--db` or
933
+ `process.cwd()`.
934
+ - Requires an existing `.kbdb` directory. Returns an
935
+ error if none is found.
936
+ - Calls `client.checkVersion(dbDir)` to read the
937
+ catalog version:
938
+ - `needs_migration` (DB version < WASM version):
939
+ calls `client.migrateDatabase(dbDir)`, prints the
940
+ JSON result, exits 0.
941
+ - `ok` (DB version == WASM version): prints
942
+ "already up to date", exits 0.
943
+ - `too_new` (DB version > WASM version): prints
944
+ error to stderr, exits 1.
945
+
946
+ **Output:**
947
+ - **stdout**: migration result JSON on success, or
948
+ "already up to date" string when already current.
949
+ - **stderr**: error message on failure.
950
+
951
+ **Exit code:** `0` on success, `1` on error.
952
+
953
+ **Delegation chain:**
954
+ ```
955
+ src/cli.ts parseArgs()
956
+ -> handleMigrate(options, deps)
957
+ -> deps.findDb(options.dbPath)
958
+ -> deps.createWorkerClient({ contextPath })
959
+ -> client.checkVersion(dbDir)
960
+ -> client.migrateDatabase(dbDir)
961
+ -> IPC -> worker daemon
962
+ -> fs-migration.wasm
963
+ (db_migrate -> migration chain)
964
+ ```
965
+
966
+ ### `kbdb skill` -- skill management
967
+
968
+ The `skill` command group manages reusable AI agent
969
+ skill definitions stored as sections with
970
+ `sectionType: 'skill'`. See [Skills](skills.md) for
971
+ the full skill specification.
972
+
973
+ #### `skill learn` -- create a skill
974
+
975
+ ```
976
+ kbdb skill learn [options] --name <n> --body <file|->
977
+ ```
978
+
979
+ Read a skill body from a file or stdin, extract
980
+ `{{argName}}` placeholders, and store the skill as
981
+ an indexed section.
982
+
983
+ `skill learn` sets `options.command = 'skill-learn'`.
984
+
985
+ **Options:**
986
+
987
+ | Option | Required | Description |
988
+ |---------------------|----------|-----------------------------------|
989
+ | `--name <n>` | yes | Skill name (kebab-case) |
990
+ | `--description <d>` | no | Human-readable skill description |
991
+ | `--body <file\|->` | yes | Template body file path or stdin |
992
+ | `--db <path>` | no | Path to `.kbdb` directory |
993
+
994
+ **Behaviour:**
995
+ - Reads the body text from the file path given to
996
+ `--body`, or from stdin when `--body` is `-`.
997
+ - Scans the body for `{{argName}}` placeholders and
998
+ auto-detects the argument list.
999
+ - Constructs the skill JSON and calls
1000
+ `client.addSection()` with `sectionType: 'skill'`.
1001
+ - The skill `name` is used as the section `title`
1002
+ (2.0x index weight), `description` as the section
1003
+ description.
1004
+
1005
+ **Output:**
1006
+ - **stdout**: JSON object on success:
1007
+ ```json
1008
+ {
1009
+ "kbid": "abcdef1234567890abcdef1234",
1010
+ "name": "summarize-document",
1011
+ "arguments": ["docid", "max_points"]
1012
+ }
1013
+ ```
1014
+ - **stderr**: errors only.
1015
+
1016
+ **Exit code:** `0` on success, `1` on error.
1017
+
1018
+ **Delegation chain:**
1019
+ ```
1020
+ src/cli.ts parseArgs()
1021
+ -> findDb() / promptUser() / runDbInit()
1022
+ -> createWorkerClient({ contextPath })
1023
+ -> versionGate(client, dbDir)
1024
+ -> dispatchCommand(client, options)
1025
+ -> runSkillLearn(client, options)
1026
+ -> client.addSection({
1027
+ sectionType: 'skill',
1028
+ content: skillJson,
1029
+ title: name,
1030
+ description })
1031
+ -> IPC -> worker daemon
1032
+ -> kb-worker.wasm
1033
+ -> fs-database.wasm (store)
1034
+ ```
1035
+
1036
+ **Examples:**
1037
+ ```sh
1038
+ # Learn a skill from a file
1039
+ kbdb skill learn --name summarize-document \
1040
+ --description "Summarize a document" \
1041
+ --body ./prompts/summarize.txt
1042
+
1043
+ # Learn a skill from stdin
1044
+ cat summarize.txt | kbdb skill learn \
1045
+ --name summarize-document --body -
1046
+
1047
+ # Learn a skill in a specific database
1048
+ kbdb skill learn --name my-skill \
1049
+ --body ./skill.txt --db /path/to/project
1050
+ ```
1051
+
1052
+ #### `skill list` -- list all skills
1053
+
1054
+ ```
1055
+ kbdb skill list [options]
1056
+ ```
1057
+
1058
+ List all skill definitions stored in the database.
1059
+
1060
+ `skill list` sets `options.command = 'skill-list'`.
1061
+
1062
+ **Output:**
1063
+ - **stdout**: JSON array of skill summaries:
1064
+ ```json
1065
+ [
1066
+ {
1067
+ "kbid": "abcdef1234567890abcdef1234",
1068
+ "name": "summarize-document",
1069
+ "description": "Summarize a document...",
1070
+ "argumentCount": 2
1071
+ }
1072
+ ]
1073
+ ```
1074
+ Empty array when no skills exist.
1075
+ - **stderr**: errors only.
1076
+
1077
+ **Exit code:** `0` on success, `1` on error.
1078
+
1079
+ **Delegation chain:**
1080
+ ```
1081
+ src/cli.ts parseArgs()
1082
+ -> findDb() / promptUser() / runDbInit()
1083
+ -> createWorkerClient({ contextPath })
1084
+ -> versionGate(client, dbDir)
1085
+ -> dispatchCommand(client, options)
1086
+ -> runSkillList(client, options)
1087
+ -> client.listByType('skill')
1088
+ -> IPC -> worker daemon
1089
+ -> kb-worker.wasm
1090
+ -> fs-database.wasm (scan)
1091
+ ```
1092
+
1093
+ #### `skill get` -- retrieve a skill
1094
+
1095
+ ```
1096
+ kbdb skill get [options] <name-or-kbid>
1097
+ ```
1098
+
1099
+ Retrieve a single skill definition by name or kbid.
1100
+
1101
+ `skill get` sets `options.command = 'skill-get'`.
1102
+
1103
+ **Arguments:**
1104
+ - `<name-or-kbid>` -- (required) the skill name
1105
+ (e.g. `summarize-document`) or a 26-character kbid.
1106
+ When looked up by name, returns the most recently
1107
+ created skill with that name.
1108
+
1109
+ **Output:**
1110
+ - **stdout**: JSON `SkillDefinition` object:
1111
+ ```json
1112
+ {
1113
+ "kbid": "abcdef1234567890abcdef1234",
1114
+ "name": "summarize-document",
1115
+ "description": "Summarize a document...",
1116
+ "arguments": [
1117
+ {
1118
+ "name": "docid",
1119
+ "type": "string",
1120
+ "description": "Document identifier",
1121
+ "required": true
1122
+ }
1123
+ ],
1124
+ "body": "Summarize the document {{docid}}..."
1125
+ }
1126
+ ```
1127
+ - **stderr**: errors only.
1128
+
1129
+ **Exit code:** `0` on success, `1` when not found or
1130
+ on error.
1131
+
1132
+ **Delegation chain:**
1133
+ ```
1134
+ src/cli.ts parseArgs()
1135
+ -> findDb() / promptUser() / runDbInit()
1136
+ -> createWorkerClient({ contextPath })
1137
+ -> versionGate(client, dbDir)
1138
+ -> dispatchCommand(client, options)
1139
+ -> runSkillGet(client, options)
1140
+ -> client.listByType('skill')
1141
+ (filter by name or kbid)
1142
+ -> client.content([kbid])
1143
+ -> IPC -> worker daemon
1144
+ -> kb-worker.wasm
1145
+ -> fs-database.wasm (fetch)
1146
+ ```
1147
+
1148
+ #### `skill delete` -- remove a skill
1149
+
1150
+ ```
1151
+ kbdb skill delete [options] <kbid>
1152
+ ```
1153
+
1154
+ Remove a skill section by its kbid. The skill body
1155
+ and index entries are permanently deleted.
1156
+
1157
+ `skill delete` sets `options.command = 'skill-delete'`.
1158
+
1159
+ **Arguments:**
1160
+ - `<kbid>` -- (required) the 26-character kbid of the
1161
+ skill to remove.
1162
+
1163
+ **Output:**
1164
+ - **stdout**: JSON result:
1165
+ ```json
1166
+ { "kbid": "abcdef1234567890abcdef1234", "removed": true }
1167
+ ```
1168
+ `"removed"` is `false` when the kbid does not exist.
1169
+ - **stderr**: errors only.
1170
+
1171
+ **Exit code:** `0` on success, `1` on error.
1172
+
1173
+ **Delegation chain:**
1174
+ ```
1175
+ src/cli.ts parseArgs()
1176
+ -> findDb() / promptUser() / runDbInit()
1177
+ -> createWorkerClient({ contextPath })
1178
+ -> versionGate(client, dbDir)
1179
+ -> dispatchCommand(client, options)
1180
+ -> runSkillDelete(client, options)
1181
+ -> client.removeSection(kbid)
1182
+ -> IPC -> worker daemon
1183
+ -> kb-worker.wasm
1184
+ -> fs-database.wasm (delete)
1185
+ ```
1186
+
1187
+ ### `kbdb agent` -- agent management
1188
+
1189
+ The `agent` command group manages AI agent persona
1190
+ definitions stored as document manifests with
1191
+ `type: 'agent'`. See [Agents](agents.md) for the
1192
+ full agent specification.
1193
+
1194
+ #### `agent create` -- create an agent
1195
+
1196
+ ```
1197
+ kbdb agent create [options] --name <n>
1198
+ --persona <file|-> --skills <kbid,...>
1199
+ ```
1200
+
1201
+ Read a persona from a file or stdin, validate the
1202
+ referenced skill kbids, and create an agent document
1203
+ manifest.
1204
+
1205
+ `agent create` sets `options.command = 'agent-create'`.
1206
+
1207
+ **Options:**
1208
+
1209
+ | Option | Required | Description |
1210
+ |---------------------|----------|-----------------------------------|
1211
+ | `--name <n>` | yes | Agent name (kebab-case) |
1212
+ | `--description <d>` | no | Human-readable agent description |
1213
+ | `--persona <file\|->` | yes | Persona file path or stdin |
1214
+ | `--skills <kbid,...>` | yes | Comma-separated ordered skill kbids |
1215
+ | `--db <path>` | no | Path to `.kbdb` directory |
1216
+
1217
+ **Behaviour:**
1218
+ - Reads the persona text from the file path given to
1219
+ `--persona`, or from stdin when `--persona` is `-`.
1220
+ - Splits `--skills` on commas to produce an ordered
1221
+ list of skill kbids. Each kbid is validated to
1222
+ exist in the database before the agent is created.
1223
+ - Creates a document manifest with `type = "agent"`,
1224
+ the persona text, and ordered `[[sections]]`
1225
+ entries referencing each skill kbid.
1226
+
1227
+ **Output:**
1228
+ - **stdout**: JSON object on success:
1229
+ ```json
1230
+ {
1231
+ "docid": "agent-code-reviewer",
1232
+ "name": "code-reviewer",
1233
+ "skills": [
1234
+ "abcdef1234567890abcdef1234",
1235
+ "fedcba9876543210fedcba9876"
1236
+ ]
1237
+ }
1238
+ ```
1239
+ - **stderr**: errors only.
1240
+
1241
+ **Exit code:** `0` on success, `1` on error.
1242
+
1243
+ **Delegation chain:**
1244
+ ```
1245
+ src/cli.ts parseArgs()
1246
+ -> findDb() / promptUser() / runDbInit()
1247
+ -> createWorkerClient({ contextPath })
1248
+ -> versionGate(client, dbDir)
1249
+ -> dispatchCommand(client, options)
1250
+ -> runAgentCreate(client, options)
1251
+ -> client.listByType('skill')
1252
+ (validate skill kbids)
1253
+ -> client.createDocument({
1254
+ type: 'agent',
1255
+ title: name,
1256
+ description,
1257
+ persona,
1258
+ skills: kbids })
1259
+ -> IPC -> worker daemon
1260
+ -> kb-worker.wasm
1261
+ -> fs-database.wasm (write)
1262
+ ```
1263
+
1264
+ **Examples:**
1265
+ ```sh
1266
+ # Create an agent from a persona file
1267
+ kbdb agent create --name code-reviewer \
1268
+ --description "Reviews code for best practices" \
1269
+ --persona ./personas/reviewer.txt \
1270
+ --skills abc123,def456
1271
+
1272
+ # Create an agent with persona from stdin
1273
+ cat persona.txt | kbdb agent create \
1274
+ --name my-agent --persona - \
1275
+ --skills abc123
1276
+
1277
+ # Create an agent in a specific database
1278
+ kbdb agent create --name my-agent \
1279
+ --persona ./persona.txt --skills abc123 \
1280
+ --db /path/to/project
1281
+ ```
1282
+
1283
+ #### `agent list` -- list all agents
1284
+
1285
+ ```
1286
+ kbdb agent list [options]
1287
+ ```
1288
+
1289
+ List all agent definitions stored in the database.
1290
+
1291
+ `agent list` sets `options.command = 'agent-list'`.
1292
+
1293
+ **Output:**
1294
+ - **stdout**: JSON array of agent summaries:
1295
+ ```json
1296
+ [
1297
+ {
1298
+ "docid": "agent-code-reviewer",
1299
+ "name": "code-reviewer",
1300
+ "description": "Reviews code for best practices",
1301
+ "skillCount": 2
1302
+ }
1303
+ ]
1304
+ ```
1305
+ Empty array when no agents exist.
1306
+ - **stderr**: errors only.
1307
+
1308
+ **Exit code:** `0` on success, `1` on error.
1309
+
1310
+ **Delegation chain:**
1311
+ ```
1312
+ src/cli.ts parseArgs()
1313
+ -> findDb() / promptUser() / runDbInit()
1314
+ -> createWorkerClient({ contextPath })
1315
+ -> versionGate(client, dbDir)
1316
+ -> dispatchCommand(client, options)
1317
+ -> runAgentList(client, options)
1318
+ -> client.listDocumentsByType('agent')
1319
+ -> IPC -> worker daemon
1320
+ -> kb-worker.wasm
1321
+ -> fs-database.wasm (scan)
1322
+ ```
1323
+
1324
+ #### `agent get` -- retrieve an agent
1325
+
1326
+ ```
1327
+ kbdb agent get [options] <docid>
1328
+ ```
1329
+
1330
+ Retrieve a single agent definition by its document
1331
+ ID, with resolved skill definitions.
1332
+
1333
+ `agent get` sets `options.command = 'agent-get'`.
1334
+
1335
+ **Arguments:**
1336
+ - `<docid>` -- (required) the agent document ID
1337
+ (e.g. `agent-code-reviewer`).
1338
+
1339
+ **Output:**
1340
+ - **stdout**: JSON `AgentDefinition` object with
1341
+ resolved skills:
1342
+ ```json
1343
+ {
1344
+ "docid": "agent-code-reviewer",
1345
+ "name": "code-reviewer",
1346
+ "description": "Reviews code for best practices",
1347
+ "persona": "You are an expert code reviewer...",
1348
+ "skills": [
1349
+ {
1350
+ "kbid": "abcdef1234567890abcdef1234",
1351
+ "name": "review-style",
1352
+ "position": 0
1353
+ },
1354
+ {
1355
+ "kbid": "fedcba9876543210fedcba9876",
1356
+ "name": null,
1357
+ "position": 1
1358
+ }
1359
+ ]
1360
+ }
1361
+ ```
1362
+ A `null` skill name indicates a dangling reference
1363
+ (the referenced skill was deleted). This is a
1364
+ warning, not an error.
1365
+ - **stderr**: errors only.
1366
+
1367
+ **Exit code:** `0` on success, `1` when not found or
1368
+ on error.
1369
+
1370
+ **Delegation chain:**
1371
+ ```
1372
+ src/cli.ts parseArgs()
1373
+ -> findDb() / promptUser() / runDbInit()
1374
+ -> createWorkerClient({ contextPath })
1375
+ -> versionGate(client, dbDir)
1376
+ -> dispatchCommand(client, options)
1377
+ -> runAgentGet(client, options)
1378
+ -> client.listDocumentsByType('agent')
1379
+ (find by docid)
1380
+ -> client.listByType('skill')
1381
+ (resolve skill kbids)
1382
+ -> IPC -> worker daemon
1383
+ -> kb-worker.wasm
1384
+ -> fs-database.wasm (fetch)
1385
+ ```
1386
+
1387
+ #### `agent delete` -- remove an agent
1388
+
1389
+ ```
1390
+ kbdb agent delete [options] <docid>
1391
+ ```
1392
+
1393
+ Remove an agent document manifest by its document ID.
1394
+ Only the agent manifest is removed -- the referenced
1395
+ skill sections are not deleted.
1396
+
1397
+ `agent delete` sets `options.command = 'agent-delete'`.
1398
+
1399
+ **Arguments:**
1400
+ - `<docid>` -- (required) the agent document ID to
1401
+ remove.
1402
+
1403
+ **Output:**
1404
+ - **stdout**: JSON result:
1405
+ ```json
1406
+ { "docid": "agent-code-reviewer", "removed": true }
1407
+ ```
1408
+ `"removed"` is `false` when the docid does not exist.
1409
+ - **stderr**: errors only.
1410
+
1411
+ **Exit code:** `0` on success, `1` on error.
1412
+
1413
+ **Delegation chain:**
1414
+ ```
1415
+ src/cli.ts parseArgs()
1416
+ -> findDb() / promptUser() / runDbInit()
1417
+ -> createWorkerClient({ contextPath })
1418
+ -> versionGate(client, dbDir)
1419
+ -> dispatchCommand(client, options)
1420
+ -> runAgentDelete(client, options)
1421
+ -> client.removeDocument(docid)
1422
+ -> IPC -> worker daemon
1423
+ -> kb-worker.wasm
1424
+ -> fs-database.wasm (delete)
1425
+ ```
1426
+
1427
+ ## Database Initialisation
1428
+
1429
+ Before executing any data command (`learn`, `unlearn`,
1430
+ `search`, etc.) the CLI checks for the `.kbdb`
1431
+ storage directory via `findDb()`.
1432
+
1433
+ ### Current behaviour
1434
+
1435
+ 1. **Explicit init** -- `kbdb db init` creates the
1436
+ database directory with the full scaffold. This is
1437
+ the only way to create a new database without being
1438
+ prompted.
1439
+
1440
+ 2. **Interactive prompt** -- when a data command finds
1441
+ no `.kbdb` directory:
1442
+ - If stdin is a TTY (`isStdinTty()` returns `true`):
1443
+ prompt "No .kbdb database found. Create one in
1444
+ \<path>? [y/N]"
1445
+ - On "y": call `runDbInit()`, continue with the
1446
+ command.
1447
+ - Otherwise: print "aborted: no database created"
1448
+ to stderr, exit 1.
1449
+ - If stdin is not a TTY (piped / CI): print error to
1450
+ stderr, exit 1. No prompt.
1451
+
1452
+ 3. **Version check gate** -- when `.kbdb` exists, the
1453
+ CLI calls `client.checkVersion(dbDir)` before
1454
+ dispatching any data command:
1455
+ - `needs_migration`: print error ("database needs
1456
+ migration -- run `kbdb db migrate`"), exit 1.
1457
+ - `too_new`: print error ("database version X is
1458
+ newer than kbdb version Y -- upgrade kbdb"),
1459
+ exit 1.
1460
+ - `ok`: proceed.
1461
+
1462
+ 4. **MCP mode** -- does NOT prompt. Requires an
1463
+ existing `.kbdb` directory and passes the same
1464
+ version gate.
1465
+
1466
+ ## CLI Mode Execution Model
1467
+
1468
+ The CLI follows a strict pass-through architecture:
1469
+
1470
+ 1. Parse command-line arguments (`parseArgs()`).
1471
+ 2. Handle `help` or empty command: return `HELP_TEXT`,
1472
+ exit 0.
1473
+ 3. Handle `version`: return `VERSION` string, exit 0.
1474
+ 4. Handle `db-init`: call `runDbInit()` directly (no
1475
+ daemon needed).
1476
+ 5. Handle `mcp`: find DB, create client, version gate,
1477
+ start MCP server.
1478
+ 6. Handle `db-migrate`: find DB, create client, call
1479
+ `checkVersion` and `migrateDatabase`.
1480
+ 7. For data commands: find DB (prompt if missing),
1481
+ create client, version gate, call
1482
+ `dispatchCommand()`.
1483
+ 8. `dispatchCommand()` routes to the appropriate
1484
+ `run*` function and returns a `CommandResult`.
1485
+ 9. The entry point (`src/cli.ts`) writes
1486
+ `result.output` to stdout and `result.error` to
1487
+ stderr, then exits with `result.exitCode`.
1488
+
1489
+ The CLI prints nothing else. No banners, no progress
1490
+ bars, no decorative output unless explicitly requested
1491
+ via flag. Stdout contains only the result. Stderr
1492
+ contains only errors. This makes the CLI composable
1493
+ with pipes, scripts, and programmatic callers.
1494
+
1495
+ ## Argument Parsing
1496
+
1497
+ The `parseArgs(argv)` function in
1498
+ `src/shared/cli/functions/parse-args.function.ts`
1499
+ processes the raw argument vector
1500
+ (`process.argv.slice(2)` on Node.js,
1501
+ `Deno.args` on Deno) into a `CliOptions` object.
1502
+
1503
+ ### Parsing rules
1504
+
1505
+ 1. `--help` / `-h` and `--version` / `-v` are
1506
+ **early-exit flags** -- they set the command and
1507
+ return immediately, ignoring all other arguments.
1508
+ 2. `--init` sets `command = 'init'`, emits a
1509
+ deprecation warning to stderr, and continues
1510
+ parsing (does not return early).
1511
+ 3. `--migrate` sets `command = 'migrate'`, emits a
1512
+ deprecation warning to stderr, and continues
1513
+ parsing.
1514
+ 4. When the first positional token is `db`, `skill`,
1515
+ or `agent`, the parser consumes the next positional
1516
+ token as the subcommand and combines them into a
1517
+ hyphenated command string: `db init` →
1518
+ `'db-init'`, `skill learn` → `'skill-learn'`,
1519
+ `agent create` → `'agent-create'`, etc.
1520
+ 5. All other flags (`--db`, `--format`, `--mode`,
1521
+ `--limit`, `--offset`, `--title`, `--description`,
1522
+ `--docid`, `--name`, `--persona`, `--body`,
1523
+ `--skills`) consume the next token as their value.
1524
+ 6. `--content` / `-c` is a boolean flag; it sets
1525
+ `options.content = true` without consuming the next
1526
+ token.
1527
+ 7. The first non-flag, non-group token becomes the
1528
+ **command**.
1529
+ 8. Subsequent non-flag tokens become **positional
1530
+ arguments** (`args`).
1531
+ 9. Unknown flags are treated as positional arguments.
1532
+
1533
+ ### `CliOptions` type
1534
+
1535
+ Defined in
1536
+ `src/shared/cli/typings/cli-options.interface.ts`:
1537
+
1538
+ ```typescript
1539
+ /** Supported output formats for CLI results. */
1540
+ type OutputFormat = 'json' | 'text' | 'mcp';
1541
+
1542
+ /**
1543
+ * Controls which entity type is returned by a search.
1544
+ */
1545
+ type SearchMode = 'sections' | 'documents' | 'stats';
1546
+
1547
+ /** Parsed representation of all CLI arguments. */
1548
+ interface CliOptions {
1549
+ /** Sub-command to execute. Simple commands use the
1550
+ * command name directly (e.g. 'search', 'learn').
1551
+ * Compound commands use a hyphenated form
1552
+ * (e.g. 'db-init', 'skill-learn', 'agent-create').
1553
+ * Legacy flag commands use 'init' and 'migrate'.
1554
+ * Empty string when no command is given. */
1555
+ command: string;
1556
+ /** Path to the knowledge-base root directory. */
1557
+ dbPath?: string;
1558
+ /** Output format for results. Defaults to 'json'. */
1559
+ format: OutputFormat;
1560
+ /** Search mode. Defaults to 'sections'. */
1561
+ mode: SearchMode;
1562
+ /** Maximum number of results. Defaults to 20. */
1563
+ limit: number;
1564
+ /** Zero-based result offset for pagination.
1565
+ * Defaults to 0. */
1566
+ offset: number;
1567
+ /** Section title for learn command. */
1568
+ title?: string;
1569
+ /** Section description for learn command. */
1570
+ description?: string;
1571
+ /** Document ID to attach sections to (learn). */
1572
+ docid?: string;
1573
+ /** When true, hydrates search result previews
1574
+ * with actual section content. */
1575
+ content?: boolean;
1576
+ /** Skill or agent name (skill learn, agent create). */
1577
+ name?: string;
1578
+ /** Persona file path or '-' for stdin
1579
+ * (agent create). */
1580
+ persona?: string;
1581
+ /** Skill body file path or '-' for stdin
1582
+ * (skill learn). */
1583
+ body?: string;
1584
+ /** Comma-separated skill kbids
1585
+ * (agent create). Parsed to string[]. */
1586
+ skills?: string[];
1587
+ /** Positional arguments following the sub-command. */
1588
+ args: string[];
1589
+ }
1590
+ ```
1591
+
1592
+ `command` is a plain `string`, not a union type. The
1593
+ value is whatever the first non-flag token is (or the
1594
+ compound form for `db`/`skill`/`agent` groups), or the
1595
+ special values `'help'`, `'version'`, `'init'`, and
1596
+ `'migrate'` set by flags.
1597
+
1598
+ `OutputFormat` and `SearchMode` are string literal
1599
+ union type aliases, not enums.
1600
+
1601
+ ### Defaults
1602
+
1603
+ | Field | Default | Constant |
1604
+ |----------|--------------|-------------------|
1605
+ | `format` | `'json'` | `DEFAULT_FORMAT` |
1606
+ | `mode` | `'sections'` | `DEFAULT_MODE` |
1607
+ | `limit` | `20` | `DEFAULT_LIMIT` |
1608
+ | `offset` | `0` | `DEFAULT_OFFSET` |
1609
+
1610
+ Constants are defined in
1611
+ `src/shared/cli/constants/defaults.constant.ts`.
1612
+ The same file exports `SUPPORTED_EXTENSIONS` listing
1613
+ the file extensions accepted by the learn command.
1614
+
1615
+ ## Help Text
1616
+
1617
+ The CLI implements a three-level contextual help
1618
+ system. The level of detail depends on how many
1619
+ command tokens precede `--help`.
1620
+
1621
+ ### Level 1: `kbdb --help` (global)
1622
+
1623
+ Shows all commands grouped by category, global
1624
+ options, and a footer pointing to per-command help.
1625
+
1626
+ ```
1627
+ kbdb - File-based knowledge base database for AI agents
1628
+
1629
+ Usage: kbdb [options]
1630
+ kbdb <command> [options] ...[parameters]
1631
+ kbdb <command> <subcommand> [options] ...[params]
1632
+
1633
+ Options:
1634
+ -h, --help Show this help message
1635
+ -v, --version Show version information
1636
+
1637
+ Knowledge Base:
1638
+ learn Import content from files/stdin
1639
+ unlearn Remove sections by kbid
1640
+ search Query the knowledge base
1641
+ content Compose rendered content from ids
1642
+
1643
+ Database:
1644
+ db init Initialise a new .kbdb directory
1645
+ db migrate Migrate database to current format
1646
+
1647
+ Maintenance:
1648
+ check Verify knowledge base integrity
1649
+ gc Garbage collect unreferenced
1650
+ sections
1651
+ rebuild Reconstruct all index files
1652
+ status Show database status
1653
+
1654
+ Skills:
1655
+ skill learn Create a new skill definition
1656
+ skill list List all skills
1657
+ skill get Retrieve a skill by name or kbid
1658
+ skill delete Remove a skill
1659
+
1660
+ Agents:
1661
+ agent create Create an agent with skill refs
1662
+ agent list List all agents
1663
+ agent get Retrieve an agent by docid
1664
+ agent delete Remove an agent
1665
+
1666
+ Server:
1667
+ mcp Start MCP server mode (stdio)
1668
+
1669
+ Global Options:
1670
+ --db <path> Path to .kbdb directory
1671
+ --format <fmt> Output format (json|text|mcp)
1672
+
1673
+ Run "kbdb <command> --help" for detailed information.
1674
+ ```
1675
+
1676
+ ### Level 2: `kbdb <command> --help` (command)
1677
+
1678
+ **For commands with subcommands** (`db`, `skill`,
1679
+ `agent`): shows subcommand listing with one-line
1680
+ descriptions, global options, and a footer:
1681
+ `Run "kbdb <cmd> <subcmd> --help" for detailed
1682
+ information.`
1683
+
1684
+ **For commands without subcommands** (`learn`,
1685
+ `search`, `check`, etc.): shows full detail including:
1686
+ - Description paragraph
1687
+ - Arguments section with semantics
1688
+ - Options section with defaults and value descriptions
1689
+ - Output section showing both text and json format
1690
+ examples
1691
+ - Two to three usage examples
1692
+
1693
+ ### Level 3: `kbdb <cmd> <subcmd> --help` (subcommand)
1694
+
1695
+ Same detail level as Level 2 command help but scoped
1696
+ to a single subcommand. Includes: usage line,
1697
+ description paragraph, options with defaults,
1698
+ arguments, output format examples, and usage examples.
1699
+
1700
+ ### Help text principles
1701
+
1702
+ - Every help level shows `[options]` immediately after
1703
+ the command or subcommand in usage lines.
1704
+ - Options show short and long forms, value placeholder,
1705
+ and default when applicable.
1706
+ - The output section shows both `text` and `json`
1707
+ formats where relevant.
1708
+ - Examples progress from simple to complex.
1709
+ - Global options (`--db`, `--format`, `--help`) appear
1710
+ in every help level for discoverability.
1711
+
1712
+ ## Output Types
1713
+
1714
+ ### `SearchDisplay` type
1715
+
1716
+ Defined in
1717
+ `src/shared/cli/typings/search-display.model.ts`.
1718
+ This is the type that `formatOutput` and all search
1719
+ output handlers operate on. It is produced by
1720
+ `runSearch()` and optionally enriched by
1721
+ `hydrateSearchPreviews()`.
1722
+
1723
+ ```typescript
1724
+ interface SearchDisplay {
1725
+ /** Knowledge-base identifier of the matched section. */
1726
+ kbid: string;
1727
+ /** Section title text, or null when the section
1728
+ * has no title. */
1729
+ heading: string | null;
1730
+ /** Section MIME type (e.g. "text/markdown"). */
1731
+ type: string;
1732
+ /** Document identifiers this section belongs to.
1733
+ * May be empty for standalone sections. */
1734
+ docids: string[];
1735
+ /** Relevance score from the ranking engine. */
1736
+ score: number;
1737
+ /** KWIC excerpt (~150 chars) centered on the
1738
+ * highest-scoring matched term. Empty string
1739
+ * for image sections. */
1740
+ snippet: string;
1741
+ /** Query terms that matched, as a flat string[]. */
1742
+ matched_terms: string[];
1743
+ /** Full section text. Empty string when --content
1744
+ * is not used. */
1745
+ preview: string;
1746
+ }
1747
+ ```
1748
+
1749
+ `heading`, `type`, `docids`, `snippet`, and
1750
+ `matched_terms` are always populated from the search
1751
+ result. `preview` is always an empty string unless
1752
+ `--content` / `-c` is supplied, in which case
1753
+ `hydrateSearchPreviews()` fills it with the actual
1754
+ section text fetched from `client.content()`.
1755
+ `heading` is `null` when the section has no title.
1756
+
1757
+ ### `LearnResult` type
1758
+
1759
+ Defined in
1760
+ `src/shared/cli/typings/learn-result.model.ts`.
1761
+ Produced by `runLearn()` for each file ingested.
1762
+
1763
+ ```typescript
1764
+ interface LearnResult {
1765
+ /** Knowledge-base identifier assigned to the section. */
1766
+ kbid: string;
1767
+ /** Absolute path to the source file (or "-" for stdin). */
1768
+ path: string;
1769
+ /** UTF-8 character count of the ingested content. */
1770
+ size: number;
1771
+ /** true when the file was skipped without being ingested. */
1772
+ skipped: boolean;
1773
+ }
1774
+ ```
1775
+
1776
+ ### `CommandResult` type
1777
+
1778
+ Defined in
1779
+ `src/shared/cli/typings/command-result.model.ts`.
1780
+ Returned by `runCli()` and `dispatchCommand()` so the
1781
+ entry point can write output and exit without those
1782
+ functions touching process globals.
1783
+
1784
+ ```typescript
1785
+ interface CommandResult {
1786
+ /** Text to write to stdout (if any). */
1787
+ output?: string;
1788
+ /** Error message to write to stderr (if any). */
1789
+ error?: string;
1790
+ /** Process exit code. 0 means success. */
1791
+ exitCode: number;
1792
+ }
1793
+ ```
1794
+
1795
+ ## Key Functions
1796
+
1797
+ ### `runCli`
1798
+
1799
+ Source:
1800
+ `src/shared/cli/functions/run-cli.function.ts`
1801
+
1802
+ Full CLI orchestrator. Takes the raw argument vector
1803
+ and an injectable `CliDeps` object. Returns a
1804
+ `CommandResult`. Never touches process globals.
1805
+
1806
+ ```typescript
1807
+ async function runCli(
1808
+ argv: string[],
1809
+ deps: CliDeps,
1810
+ ): Promise<CommandResult>
1811
+ ```
1812
+
1813
+ `CliDeps` contains:
1814
+
1815
+ | Field | Description |
1816
+ |---------------------|--------------------------------------|
1817
+ | `isStdinTty` | Returns `true` when stdin is a TTY |
1818
+ | `promptUser` | Prompts the user and returns input |
1819
+ | `createWorkerClient`| Creates a connected worker client |
1820
+ | `findDb` | Looks up the `.kbdb` directory |
1821
+ | `runDbInit` | Creates the `.kbdb` scaffold on disk |
1822
+ | `startMcpServer` | (Optional) Starts the MCP server |
1823
+
1824
+ Routing within `runCli`:
1825
+ - `command === 'help'` or empty: return `HELP_TEXT`
1826
+ - `command === 'version'`: return `VERSION`
1827
+ - `command === 'db-init'`: `handleDbInit` (no client)
1828
+ - `command === 'init'`: `handleDbInit` (no client,
1829
+ deprecated -- emits warning)
1830
+ - `command === 'mcp'`: `handleMcp` (requires client)
1831
+ - `command === 'db-migrate'`: `handleDbMigrate`
1832
+ (requires client)
1833
+ - `command === 'migrate'`: `handleDbMigrate` (requires
1834
+ client, deprecated -- emits warning)
1835
+ - anything else: `handleDataCommand` (requires client,
1836
+ runs version gate, then `dispatchCommand`)
1837
+
1838
+ ### `dispatchCommand`
1839
+
1840
+ Source:
1841
+ `src/shared/cli/functions/dispatch-command.function.ts`
1842
+
1843
+ Routes a parsed command to the appropriate `run*`
1844
+ handler. Returns `CommandResult` without throwing.
1845
+ Wraps each handler call in a try/catch so errors are
1846
+ captured as `error` fields with a non-zero exit code.
1847
+
1848
+ ```typescript
1849
+ async function dispatchCommand(
1850
+ client: DispatchClient,
1851
+ options: CliOptions,
1852
+ io?: LearnIO,
1853
+ ): Promise<CommandResult>
1854
+ ```
1855
+
1856
+ Routing table:
1857
+
1858
+ | command | handler | requires client |
1859
+ |------------------|--------------------|-----------------|
1860
+ | `learn` | `runLearn` | yes |
1861
+ | `unlearn` | `runUnlearn` | yes |
1862
+ | `search` | `runSearch` | yes |
1863
+ | `content` | `runContent` | yes |
1864
+ | `check` | `runCheck` | yes |
1865
+ | `gc` | `runGc` | yes |
1866
+ | `rebuild` | `runRebuild` | yes |
1867
+ | `db-init` | `runDbInit` | no |
1868
+ | `db-migrate` | `runDbMigrate` | yes |
1869
+ | `skill-learn` | `runSkillLearn` | yes |
1870
+ | `skill-list` | `runSkillList` | yes |
1871
+ | `skill-get` | `runSkillGet` | yes |
1872
+ | `skill-delete` | `runSkillDelete` | yes |
1873
+ | `agent-create` | `runAgentCreate` | yes |
1874
+ | `agent-list` | `runAgentList` | yes |
1875
+ | `agent-get` | `runAgentGet` | yes |
1876
+ | `agent-delete` | `runAgentDelete` | yes |
1877
+
1878
+ Unknown commands return exit code 1 with an "unknown
1879
+ command" error.
1880
+
1881
+ For `search`, `dispatchCommand` also:
1882
+ 1. Calls `runSearch()` to get `SearchDisplay[]`.
1883
+ 2. When `options.content === true`, calls
1884
+ `hydrateSearchPreviews(client, results)` to fill
1885
+ each `preview` field.
1886
+ 3. Calls `formatOutput(results, options.format)` to
1887
+ produce the final output string.
1888
+
1889
+ ### `formatOutput`
1890
+
1891
+ Source:
1892
+ `src/shared/cli/functions/format-output.function.ts`
1893
+
1894
+ Serialises a `SearchDisplay[]` to a string in the
1895
+ requested format.
1896
+
1897
+ ```typescript
1898
+ function formatOutput(
1899
+ results: SearchDisplay[],
1900
+ format: OutputFormat,
1901
+ ): string
1902
+ ```
1903
+
1904
+ - `'json'` -- `JSON.stringify(results, null, 2)`
1905
+ - `'text'` -- numbered list with kbid, score, preview,
1906
+ and matched terms
1907
+ - `'mcp'` -- `{ type: 'text', text: '...' }` envelope
1908
+ with one line per result
1909
+
1910
+ ### `runDbInit`
1911
+
1912
+ Source:
1913
+ `src/shared/cli/functions/run-db-init.function.ts`
1914
+
1915
+ Creates the `.kbdb` directory scaffold directly on
1916
+ disk without a daemon. Replaces the former `runInit`.
1917
+
1918
+ ```typescript
1919
+ async function runDbInit(
1920
+ options: CliOptions,
1921
+ ): Promise<CommandResult>
1922
+ ```
1923
+
1924
+ Writes `catalog.toml`, `corpus.bin`, `inverted.idx`,
1925
+ `positional.idx`, `sections/`, `documents/`,
1926
+ `worker.toml`, and `.gitignore` under the resolved
1927
+ database directory. Returns a JSON result with `path`
1928
+ and `created: true` on success.
1929
+
1930
+ ### `runDbMigrate`
1931
+
1932
+ Source:
1933
+ `src/shared/cli/functions/run-db-migrate.function.ts`
1934
+
1935
+ Runs the database migration path via the worker
1936
+ client. Replaces the former `runMigrate`.
1937
+
1938
+ ```typescript
1939
+ async function runDbMigrate(
1940
+ client: DispatchClient,
1941
+ options: CliOptions,
1942
+ ): Promise<CommandResult>
1943
+ ```
1944
+
1945
+ Calls `client.checkVersion(dbDir)`, then
1946
+ `client.migrateDatabase(dbDir)` when migration is
1947
+ needed. Returns the `MigrateResult` JSON.
1948
+
1949
+ ### `runSkillLearn`
1950
+
1951
+ Source:
1952
+ `src/shared/cli/functions/run-skill-learn.function.ts`
1953
+
1954
+ Reads the skill body, extracts `{{argName}}`
1955
+ placeholders, and stores the skill as a section.
1956
+
1957
+ ```typescript
1958
+ async function runSkillLearn(
1959
+ client: DispatchClient,
1960
+ options: CliOptions,
1961
+ ): Promise<CommandResult>
1962
+ ```
1963
+
1964
+ Reads body from `options.body` (file path or `-` for
1965
+ stdin). Calls `client.addSection()` with
1966
+ `sectionType: 'skill'`. Returns JSON with `kbid`,
1967
+ `name`, and `arguments`.
1968
+
1969
+ ### `runSkillList`
1970
+
1971
+ Source:
1972
+ `src/shared/cli/functions/run-skill-list.function.ts`
1973
+
1974
+ Lists all skill sections in the database.
1975
+
1976
+ ```typescript
1977
+ async function runSkillList(
1978
+ client: DispatchClient,
1979
+ options: CliOptions,
1980
+ ): Promise<CommandResult>
1981
+ ```
1982
+
1983
+ Calls `client.listByType('skill')` and returns a JSON
1984
+ array of skill summaries.
1985
+
1986
+ ### `runSkillGet`
1987
+
1988
+ Source:
1989
+ `src/shared/cli/functions/run-skill-get.function.ts`
1990
+
1991
+ Retrieves a single skill by name or kbid.
1992
+
1993
+ ```typescript
1994
+ async function runSkillGet(
1995
+ client: DispatchClient,
1996
+ options: CliOptions,
1997
+ ): Promise<CommandResult>
1998
+ ```
1999
+
2000
+ Uses `options.args[0]` as the name-or-kbid. Calls
2001
+ `client.listByType('skill')` to find the section,
2002
+ then `client.content([kbid])` to fetch the body.
2003
+ Returns a full `SkillDefinition` JSON.
2004
+
2005
+ ### `runSkillDelete`
2006
+
2007
+ Source:
2008
+ `src/shared/cli/functions/run-skill-delete.function.ts`
2009
+
2010
+ Removes a skill section by kbid.
2011
+
2012
+ ```typescript
2013
+ async function runSkillDelete(
2014
+ client: DispatchClient,
2015
+ options: CliOptions,
2016
+ ): Promise<CommandResult>
2017
+ ```
2018
+
2019
+ Uses `options.args[0]` as the kbid. Calls
2020
+ `client.removeSection(kbid)`. Returns JSON with
2021
+ `kbid` and `removed`.
2022
+
2023
+ ### `runAgentCreate`
2024
+
2025
+ Source:
2026
+ `src/shared/cli/functions/run-agent-create.function.ts`
2027
+
2028
+ Reads the persona, validates skill kbids, and creates
2029
+ an agent document manifest.
2030
+
2031
+ ```typescript
2032
+ async function runAgentCreate(
2033
+ client: DispatchClient,
2034
+ options: CliOptions,
2035
+ ): Promise<CommandResult>
2036
+ ```
2037
+
2038
+ Reads persona from `options.persona` (file path or
2039
+ `-` for stdin). Splits `options.skills` on commas.
2040
+ Validates each skill kbid exists via
2041
+ `client.listByType('skill')`. Calls
2042
+ `client.createDocument()` with `type: 'agent'`.
2043
+ Returns JSON with `docid`, `name`, and `skills`.
2044
+
2045
+ ### `runAgentList`
2046
+
2047
+ Source:
2048
+ `src/shared/cli/functions/run-agent-list.function.ts`
2049
+
2050
+ Lists all agent document manifests in the database.
2051
+
2052
+ ```typescript
2053
+ async function runAgentList(
2054
+ client: DispatchClient,
2055
+ options: CliOptions,
2056
+ ): Promise<CommandResult>
2057
+ ```
2058
+
2059
+ Calls `client.listDocumentsByType('agent')` and
2060
+ returns a JSON array of agent summaries.
2061
+
2062
+ ### `runAgentGet`
2063
+
2064
+ Source:
2065
+ `src/shared/cli/functions/run-agent-get.function.ts`
2066
+
2067
+ Retrieves a single agent by docid with resolved
2068
+ skill definitions.
2069
+
2070
+ ```typescript
2071
+ async function runAgentGet(
2072
+ client: DispatchClient,
2073
+ options: CliOptions,
2074
+ ): Promise<CommandResult>
2075
+ ```
2076
+
2077
+ Uses `options.args[0]` as the docid. Fetches the
2078
+ agent manifest via `client.listDocumentsByType()`,
2079
+ then resolves each skill kbid via
2080
+ `client.listByType('skill')`. Returns a full
2081
+ `AgentDefinition` JSON. Dangling skill kbids appear
2082
+ with `name: null`.
2083
+
2084
+ ### `runAgentDelete`
2085
+
2086
+ Source:
2087
+ `src/shared/cli/functions/run-agent-delete.function.ts`
2088
+
2089
+ Removes an agent document manifest by docid. Does
2090
+ not delete the referenced skill sections.
2091
+
2092
+ ```typescript
2093
+ async function runAgentDelete(
2094
+ client: DispatchClient,
2095
+ options: CliOptions,
2096
+ ): Promise<CommandResult>
2097
+ ```
2098
+
2099
+ Uses `options.args[0]` as the docid. Calls
2100
+ `client.removeDocument(docid)`. Returns JSON with
2101
+ `docid` and `removed`.
2102
+
2103
+ ## Architecture -- How CLI Connects to WASM
2104
+
2105
+ The CLI never loads WASM modules or accesses the
2106
+ `.kbdb` directory directly. The architecture is a
2107
+ four-layer delegation chain:
2108
+
2109
+ ```
2110
+ User
2111
+ |
2112
+ v
2113
+ src/cli.ts (TypeScript) Layer 1: CLI
2114
+ | parseArgs(), runCli(),
2115
+ | dispatchCommand(), formatOutput()
2116
+ |
2117
+ v
2118
+ worker-client/ (TypeScript) Layer 2: IPC Client
2119
+ | createWorkerClient()
2120
+ | JSON-RPC 2.0 over IPC
2121
+ |
2122
+ v
2123
+ src/worker.ts (TypeScript) Layer 3: Daemon
2124
+ | loads WASM modules,
2125
+ | manages cache,
2126
+ | serves IPC requests
2127
+ |
2128
+ v
2129
+ WASM modules (Rust) Layer 4: Engine
2130
+ kb-worker.wasm (orchestration)
2131
+ -> query-parser.wasm (tokenize, parse, rank)
2132
+ -> fs-database.wasm (storage, indexes)
2133
+ -> fs-migration.wasm (version migration)
2134
+ ```
2135
+
2136
+ ### Layer 1: CLI (`src/cli.ts`)
2137
+
2138
+ The entry point. Responsibilities:
2139
+ - Parse command-line arguments
2140
+ - Dispatch to the correct command handler via `runCli`
2141
+ - Format output for stdout via `dispatchCommand` /
2142
+ `formatOutput`
2143
+ - Handle errors to stderr
2144
+ - Exit with appropriate code
2145
+
2146
+ Source: `src/cli.ts`
2147
+ Module: `src/shared/cli/`
2148
+
2149
+ ### Layer 2: Worker Client
2150
+
2151
+ The IPC bridge. Responsibilities:
2152
+ - Resolve database directory path
2153
+ - Compute context ID (SHA-256 of path)
2154
+ - Discover running daemon or spawn a new one
2155
+ - Connect to daemon over IPC socket
2156
+ - Expose typed async methods that send JSON-RPC 2.0
2157
+ requests
2158
+
2159
+ Source: `src/shared/worker-client/`
2160
+ See: [Worker Client](worker-client.md)
2161
+
2162
+ ### Layer 3: Worker Daemon
2163
+
2164
+ The background process. Responsibilities:
2165
+ - Compile and load WASM modules once at startup
2166
+ - Maintain in-memory LRU cache
2167
+ - Process IPC requests through WASM modules
2168
+ - Manage daemon lifecycle (idle timeout, shutdown)
2169
+
2170
+ Source: `src/worker.ts`
2171
+ Module: `src/shared/worker-daemon/`
2172
+ See: [Worker Daemon](worker-daemon.md)
2173
+
2174
+ ### Layer 4: WASM Engine
2175
+
2176
+ The Rust engine compiled to WebAssembly.
2177
+ Responsibilities vary by module:
2178
+
2179
+ | Module | Responsibility |
2180
+ |---------------------|-----------------------------------|
2181
+ | `kb-worker.wasm` | Orchestrates the other modules, manages cache and async result tokens |
2182
+ | `query-parser.wasm` | Tokenization pipeline, query parsing, keyword extraction, ranking (BM25F, TF-IDF, RAKE) |
2183
+ | `fs-database.wasm` | `.kbdb` directory I/O: section CRUD, document manifests, B+ tree indexes, integrity checks |
2184
+ | `fs-migration.wasm` | Database format versioning, sequential migration scripts |
2185
+
2186
+ See: [Database](database.md),
2187
+ [Query Parser](query-parser.md)
2188
+
2189
+ ### Which WASM module handles what
2190
+
2191
+ | CLI command | WASM modules involved |
2192
+ |-------------------|-------------------------------------------|
2193
+ | `learn` | `query-parser` (tokenize) + `fs-database` (store + index) via `kb-worker` |
2194
+ | `unlearn` | `fs-database` (delete + re-index) via `kb-worker` |
2195
+ | `search` | `query-parser` (parse + rank) + `fs-database` (index lookup) via `kb-worker` |
2196
+ | `content` | `fs-database` (fetch sections/docs) via `kb-worker` |
2197
+ | `check` | `fs-database` (verify checksums, scan manifests) via `kb-worker` |
2198
+ | `gc` | `fs-database` (scan + remove unreferenced) via `kb-worker` |
2199
+ | `rebuild` | `query-parser` (retokenize) + `fs-database` (rebuild indexes) via `kb-worker` |
2200
+ | `db init` | None -- `runDbInit()` writes files directly |
2201
+ | `db migrate` | `fs-migration` (`db_migrate`) via daemon |
2202
+ | `skill learn` | `fs-database` (store section) via `kb-worker` |
2203
+ | `skill list` | `fs-database` (scan by type) via `kb-worker` |
2204
+ | `skill get` | `fs-database` (fetch section) via `kb-worker` |
2205
+ | `skill delete` | `fs-database` (delete section) via `kb-worker` |
2206
+ | `agent create` | `fs-database` (write manifest) via `kb-worker` |
2207
+ | `agent list` | `fs-database` (scan documents by type) via `kb-worker` |
2208
+ | `agent get` | `fs-database` (fetch manifest + sections) via `kb-worker` |
2209
+ | `agent delete` | `fs-database` (remove manifest) via `kb-worker` |
2210
+
2211
+ ### Data flow: search
2212
+
2213
+ ```
2214
+ kbdb search "machine learning" --limit 5
2215
+
2216
+ 1. parseArgs() -> { command: 'search',
2217
+ args: ['machine', 'learning'],
2218
+ mode: 'sections', limit: 5, offset: 0,
2219
+ format: 'json', content: undefined }
2220
+
2221
+ 2. handleDataCommand():
2222
+ findDb() -> '/home/user/project/.kbdb'
2223
+
2224
+ 3. createWorkerClient({ contextPath })
2225
+ -> discovers daemon, connects via IPC
2226
+
2227
+ 4. versionGate(client, dbDir)
2228
+ -> client.checkVersion(dbDir)
2229
+ -> status 'ok', proceed
2230
+
2231
+ 5. dispatchCommand(client, options)
2232
+ -> runSearch(client, options)
2233
+ -> client.search({
2234
+ query: 'machine learning',
2235
+ mode: 'sections', limit: 5,
2236
+ offset: 0 })
2237
+ -> JSON-RPC: { method: 'search', params: {...} }
2238
+
2239
+ 6. Worker daemon receives request:
2240
+ -> kb-worker.wasm orchestrates:
2241
+ a. query-parser.wasm: parse query into
2242
+ retrieval plan
2243
+ b. fs-database.wasm: execute plan against
2244
+ indexes, return raw matches
2245
+ c. query-parser.wasm: rank results (BM25F,
2246
+ proximity, zone, freshness scoring)
2247
+ -> returns ranked results via IPC
2248
+
2249
+ 7. runSearch() maps to SearchDisplay[]:
2250
+ [{ kbid, heading, type, docids, score,
2251
+ snippet, matched_terms, preview: '' }]
2252
+
2253
+ 8. options.content is falsy -> skip hydration
2254
+
2255
+ 9. formatOutput(results, 'json')
2256
+ -> JSON.stringify(results, null, 2)
2257
+
2258
+ 10. CommandResult: { output, exitCode: 0 }
2259
+
2260
+ 11. src/cli.ts writes output to stdout
2261
+ client.disconnect()
2262
+ ```
2263
+
2264
+ ## Environment Variables
2265
+
2266
+ | Variable | Used by | Description |
2267
+ |------------|----------|----------------------------|
2268
+ | `KBDB_DIR` | MCP mode | Fallback database directory when `--db` is not specified. Not used in CLI mode. |
2269
+
2270
+ ## Exit Codes
2271
+
2272
+ | Code | Meaning |
2273
+ |------|----------------------------------------|
2274
+ | `0` | Success |
2275
+ | `1` | Error (invalid args, missing DB, WASM error, version mismatch, unknown command) |
2276
+
2277
+ All new commands (`db init`, `db migrate`, `skill`
2278
+ subcommands, `agent` subcommands) follow the same two
2279
+ exit code convention. No additional codes are needed.
2280
+
2281
+ ## Design Constraints
2282
+
2283
+ - The CLI is a **thin pass-through**. It does not
2284
+ perform keyword extraction, indexing, ranking, or
2285
+ database I/O itself.
2286
+ - **stdout** contains only the command result. No
2287
+ banners, progress bars, or decorative output.
2288
+ - **stderr** contains only errors (and deprecation
2289
+ warnings for `--init` / `--migrate`).
2290
+ - The CLI does not read or write `.kbdb` files
2291
+ directly (except `db init`, which creates the
2292
+ scaffold without a daemon). All other database
2293
+ operations go through the worker client -> daemon ->
2294
+ WASM chain.
2295
+ - The CLI works on both **Deno and Node.js**. Runtime
2296
+ differences (argument access, process spawning) are
2297
+ abstracted in `src/shared/runtime/` and
2298
+ `src/shared/platform/`.
2299
+ - Output format is controlled by `--format`, not by
2300
+ detecting whether stdout is a TTY.
2301
+ - The `--init` and `--migrate` flags are kept for
2302
+ backward compatibility but are deprecated. They emit
2303
+ a warning to stderr and delegate to the same
2304
+ `runDbInit` / `runDbMigrate` handlers as the new
2305
+ `kbdb db init` / `kbdb db migrate` commands.
2306
+ - `kbdb db init` is the canonical way to create a
2307
+ new database. `kbdb db migrate` is the canonical
2308
+ way to migrate.