@gmickel/gno 1.18.0 → 1.20.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 (153) hide show
  1. package/README.md +14 -7
  2. package/assets/skill/SKILL.md +54 -12
  3. package/assets/skill/mcp-reference.md +7 -2
  4. package/assets/skill/recipes/citation-and-provenance.md +32 -9
  5. package/package.json +2 -1
  6. package/spec/AGENTS.md +83 -0
  7. package/spec/CLAUDE.md +83 -0
  8. package/spec/bench-fixture.schema.json +137 -0
  9. package/spec/cli.md +2919 -0
  10. package/spec/db/schema.sql +442 -0
  11. package/spec/evals-agentic.md +592 -0
  12. package/spec/evals.md +1106 -0
  13. package/spec/mcp.md +2279 -0
  14. package/spec/output-schemas/activation-verification.schema.json +515 -0
  15. package/spec/output-schemas/ask.schema.json +564 -0
  16. package/spec/output-schemas/backlinks.schema.json +131 -0
  17. package/spec/output-schemas/bench-result.schema.json +120 -0
  18. package/spec/output-schemas/capture-receipt.schema.json +143 -0
  19. package/spec/output-schemas/claim-verification.schema.json +291 -0
  20. package/spec/output-schemas/collection-list.schema.json +45 -0
  21. package/spec/output-schemas/context-capsule-v1.schema.json +726 -0
  22. package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
  23. package/spec/output-schemas/context-list.schema.json +21 -0
  24. package/spec/output-schemas/doctor.schema.json +313 -0
  25. package/spec/output-schemas/error.schema.json +30 -0
  26. package/spec/output-schemas/expansion.schema.json +37 -0
  27. package/spec/output-schemas/get.schema.json +140 -0
  28. package/spec/output-schemas/graph-query.schema.json +99 -0
  29. package/spec/output-schemas/graph.schema.json +371 -0
  30. package/spec/output-schemas/links-list.schema.json +186 -0
  31. package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
  32. package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
  33. package/spec/output-schemas/mcp-http-error.schema.json +30 -0
  34. package/spec/output-schemas/mcp-job-list.schema.json +58 -0
  35. package/spec/output-schemas/mcp-job-status.schema.json +224 -0
  36. package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
  37. package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
  38. package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
  39. package/spec/output-schemas/models-list.schema.json +93 -0
  40. package/spec/output-schemas/multi-get.schema.json +103 -0
  41. package/spec/output-schemas/process-status.schema.json +119 -0
  42. package/spec/output-schemas/query-diagnose.schema.json +123 -0
  43. package/spec/output-schemas/resident-status.schema.json +154 -0
  44. package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
  45. package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
  46. package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
  47. package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
  48. package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
  49. package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
  50. package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
  51. package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
  52. package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
  53. package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
  54. package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
  55. package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
  56. package/spec/output-schemas/search-result.schema.json +154 -0
  57. package/spec/output-schemas/search-results.schema.json +338 -0
  58. package/spec/output-schemas/similar.schema.json +84 -0
  59. package/spec/output-schemas/status.schema.json +676 -0
  60. package/spec/output-schemas/tags-list.schema.json +48 -0
  61. package/src/app/context-runtime-contract.ts +10 -5
  62. package/src/app/context-runtime-input.ts +29 -1
  63. package/src/app/context-runtime-types.ts +7 -0
  64. package/src/app/context-runtime.ts +20 -2
  65. package/src/app/context-surface.ts +4 -0
  66. package/src/app/verified-ask.ts +291 -0
  67. package/src/cli/commands/ask-format.ts +255 -0
  68. package/src/cli/commands/ask.ts +144 -183
  69. package/src/cli/commands/context-build.ts +56 -9
  70. package/src/cli/commands/get.ts +64 -3
  71. package/src/cli/commands/query.ts +62 -23
  72. package/src/cli/commands/replay.ts +140 -0
  73. package/src/cli/commands/search.ts +48 -3
  74. package/src/cli/commands/shared.ts +3 -1
  75. package/src/cli/commands/trace.ts +200 -0
  76. package/src/cli/commands/vsearch.ts +75 -53
  77. package/src/cli/program.ts +287 -1
  78. package/src/config/index.ts +9 -0
  79. package/src/config/retrieval-traces.ts +56 -0
  80. package/src/config/types.ts +4 -0
  81. package/src/core/context-budget.ts +6 -0
  82. package/src/core/context-capsule-retrieval-schema.ts +4 -0
  83. package/src/core/context-capsule-schema.ts +17 -0
  84. package/src/core/context-capsule-validation.ts +3 -2
  85. package/src/core/context-capsule.ts +18 -0
  86. package/src/core/context-compiler.ts +44 -25
  87. package/src/core/context-evidence.ts +6 -0
  88. package/src/core/retrieval-qrels.ts +405 -0
  89. package/src/core/retrieval-replay-candidate.ts +368 -0
  90. package/src/core/retrieval-replay-types.ts +109 -0
  91. package/src/core/retrieval-replay-validation.ts +89 -0
  92. package/src/core/retrieval-replay.ts +441 -0
  93. package/src/core/retrieval-trace-evidence-origin.ts +178 -0
  94. package/src/core/retrieval-trace-export.ts +113 -0
  95. package/src/core/retrieval-trace-filter-normalization.ts +27 -0
  96. package/src/core/retrieval-trace-filters.ts +19 -0
  97. package/src/core/retrieval-trace-management-helpers.ts +247 -0
  98. package/src/core/retrieval-trace-management-types.ts +132 -0
  99. package/src/core/retrieval-trace-management.ts +422 -0
  100. package/src/core/retrieval-trace-request.ts +141 -0
  101. package/src/core/retrieval-trace-session.ts +507 -0
  102. package/src/core/retrieval-trace.ts +472 -0
  103. package/src/llm/errors.ts +10 -1
  104. package/src/llm/httpGeneration.ts +11 -1
  105. package/src/llm/nodeLlamaCpp/generation.ts +54 -10
  106. package/src/llm/types.ts +6 -0
  107. package/src/mcp/tools/ask.ts +228 -0
  108. package/src/mcp/tools/context.ts +87 -15
  109. package/src/mcp/tools/get.ts +35 -1
  110. package/src/mcp/tools/index.ts +83 -0
  111. package/src/mcp/tools/query.ts +95 -64
  112. package/src/mcp/tools/search.ts +36 -13
  113. package/src/mcp/tools/trace.ts +143 -0
  114. package/src/mcp/tools/vsearch.ts +71 -38
  115. package/src/pipeline/answer.ts +167 -26
  116. package/src/pipeline/claim-verification-schema.ts +235 -0
  117. package/src/pipeline/claim-verification.ts +487 -0
  118. package/src/pipeline/claim-verifier.ts +474 -0
  119. package/src/pipeline/graph-retrieval.ts +15 -1
  120. package/src/pipeline/hybrid.ts +151 -43
  121. package/src/pipeline/search.ts +36 -3
  122. package/src/pipeline/trace-metadata.ts +47 -0
  123. package/src/pipeline/types.ts +68 -0
  124. package/src/pipeline/vsearch.ts +101 -38
  125. package/src/sdk/client.ts +415 -73
  126. package/src/sdk/documents.ts +48 -1
  127. package/src/sdk/index.ts +17 -0
  128. package/src/sdk/types.ts +28 -0
  129. package/src/serve/context-capsule.ts +67 -8
  130. package/src/serve/public/app.tsx +12 -1
  131. package/src/serve/public/components/AskVerificationPanel.tsx +189 -0
  132. package/src/serve/public/globals.built.css +1 -1
  133. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  134. package/src/serve/public/pages/Ask.tsx +42 -4
  135. package/src/serve/public/pages/Dashboard.tsx +10 -0
  136. package/src/serve/public/pages/TraceHistory.tsx +478 -0
  137. package/src/serve/public/pages/trace-history-detail.tsx +224 -0
  138. package/src/serve/retrieval-trace.ts +28 -0
  139. package/src/serve/routes/api.ts +508 -68
  140. package/src/serve/routes/traces.ts +156 -0
  141. package/src/serve/server.ts +87 -2
  142. package/src/store/index.ts +31 -0
  143. package/src/store/migrations/014-retrieval-traces.ts +303 -0
  144. package/src/store/migrations/index.ts +2 -0
  145. package/src/store/retrieval-trace-codec.ts +384 -0
  146. package/src/store/sqlite/adapter.ts +153 -1
  147. package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
  148. package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
  149. package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
  150. package/src/store/sqlite/retrieval-trace-store.ts +515 -0
  151. package/src/store/types.ts +297 -0
  152. package/src/store/vector/sqlite-vec.ts +76 -1
  153. package/src/store/vector/types.ts +1 -1
package/spec/cli.md ADDED
@@ -0,0 +1,2919 @@
1
+ # GNO CLI Specification
2
+
3
+ **Version:** 0.1.0
4
+ **Last Updated:** 2025-12-30
5
+
6
+ This document specifies the command-line interface for GNO, a local knowledge indexing and retrieval system.
7
+
8
+ ## Global Conventions
9
+
10
+ ### Exit Codes
11
+
12
+ | Code | Name | Description |
13
+ | ---- | ----------- | ------------------------------------------------------------- |
14
+ | 0 | SUCCESS | Command completed successfully |
15
+ | 1 | VALIDATION | Validation or usage error (bad args, missing required params) |
16
+ | 2 | RUNTIME | Runtime failure (IO, DB, conversion, model, network) |
17
+ | 3 | NOT_RUNNING | `--status`/`--stop` found no live matching process |
18
+
19
+ ### Global Flags
20
+
21
+ All commands accept these flags:
22
+
23
+ | Flag | Type | Description |
24
+ | ----------------- | ------- | -------------------------------------------------------- |
25
+ | `--index <name>` | string | Use alternate index DB name (default: "default") |
26
+ | `--config <path>` | string | Override config file path |
27
+ | `--no-color` | boolean | Disable colored output |
28
+ | `--verbose` | boolean | Enable verbose logging to stderr |
29
+ | `--yes` | boolean | Non-interactive mode: accept safe defaults, never prompt |
30
+ | `--quiet` | boolean | Suppress non-essential output |
31
+ | `--offline` | boolean | Offline mode: use cached models only |
32
+ | `--no-pager` | boolean | Disable automatic paging of long output |
33
+ | `--skill` | boolean | Output SKILL.md for agent discovery and exit |
34
+
35
+ ### Output Format Flags
36
+
37
+ Commands that produce structured output support these format flags:
38
+
39
+ | Flag | Description |
40
+ | --------- | -------------------------------------------------------------------------------------------------------------- |
41
+ | `--json` | JSON output (array or object depending on command) |
42
+ | `--files` | Line protocol: `#docid,<score>,gno://collection/path` (`?index=<name>` may be present for non-default indexes) |
43
+ | `--csv` | Comma-separated values with header row |
44
+ | `--md` | Markdown formatted output |
45
+ | `--xml` | XML formatted output |
46
+
47
+ Default output is human-readable terminal format.
48
+
49
+ Index names are filesystem identifiers: 1–64 UTF-16 code units drawn from
50
+ Unicode letters, marks, numbers, internal ASCII spaces, `.`, `_`, or `-`. The
51
+ first character must be a letter or number; the last cannot be a space or `.`;
52
+ `..` is forbidden. Absolute paths, path separators, controls, and
53
+ platform-invalid punctuation are validation errors (exit 1). NFC/case-folded
54
+ equivalents have one logical identity and database selection. The canonical
55
+ identity is limited to 242 UTF-8 bytes so `index-<identity>.sqlite` stays within
56
+ the portable 255-byte filename-component limit. The same contract applies to
57
+ indexed `gno://` references. New indexes use the canonical filename. One
58
+ pre-existing legacy filename for that identity remains addressable; multiple
59
+ equivalent files fail closed as ambiguous.
60
+
61
+ ### Output Format Support Matrix
62
+
63
+ | Command | --json | --files | --csv | --md | --xml | Default |
64
+ | ------------------ | ------ | ------- | ----- | ---- | ----- | -------- |
65
+ | status | yes | no | no | yes | no | terminal |
66
+ | init | no | no | no | no | no | terminal |
67
+ | collection add | no | no | no | no | no | terminal |
68
+ | collection list | yes | no | no | yes | no | terminal |
69
+ | collection remove | no | no | no | no | no | terminal |
70
+ | collection rename | no | no | no | no | no | terminal |
71
+ | update | no | no | no | no | no | terminal |
72
+ | index | no | no | no | no | no | terminal |
73
+ | embed | no | no | no | no | no | terminal |
74
+ | search | yes | yes | yes | yes | yes | terminal |
75
+ | vsearch | yes | yes | yes | yes | yes | terminal |
76
+ | query | yes | yes | yes | yes | yes | terminal |
77
+ | bench | yes | no | no | no | no | terminal |
78
+ | ask | yes | no | no | yes | no | terminal |
79
+ | capture | yes | no | no | no | no | terminal |
80
+ | get | yes | no | no | yes | no | terminal |
81
+ | multi-get | yes | yes | no | yes | no | terminal |
82
+ | ls | yes | yes | no | yes | no | terminal |
83
+ | daemon | yes¹ | no | no | no | no | terminal |
84
+ | context add | no | no | no | no | no | terminal |
85
+ | context list | yes | no | no | yes | no | terminal |
86
+ | context check | yes | no | no | yes | no | terminal |
87
+ | context build | yes | no | no | yes | no | Markdown |
88
+ | context verify | yes | no | no | yes | no | Markdown |
89
+ | context rm | no | no | no | no | no | terminal |
90
+ | models list | yes | no | no | yes | no | terminal |
91
+ | models pull | no | no | no | no | no | terminal |
92
+ | models clear | no | no | no | no | no | terminal |
93
+ | models path | yes | no | no | no | no | terminal |
94
+ | cleanup | no | no | no | no | no | terminal |
95
+ | doctor | yes | no | no | yes | no | terminal |
96
+ | mcp | no | no | no | no | no | stdio |
97
+ | mcp install | yes | no | no | no | no | terminal |
98
+ | mcp uninstall | yes | no | no | no | no | terminal |
99
+ | mcp status | yes | no | no | no | no | terminal |
100
+ | skill install | yes | no | no | no | no | terminal |
101
+ | skill uninstall | yes | no | no | no | no | terminal |
102
+ | skill show | no | no | no | no | no | terminal |
103
+ | skill paths | yes | no | no | no | no | terminal |
104
+ | tags list | yes | no | no | yes | no | terminal |
105
+ | tags add | yes | no | no | no | no | terminal |
106
+ | tags rm | yes | no | no | no | no | terminal |
107
+ | links list | yes | no | no | yes | no | terminal |
108
+ | backlinks | yes | no | no | yes | no | terminal |
109
+ | similar | yes | no | no | yes | no | terminal |
110
+ | graph | yes | no | no | no | no | terminal |
111
+ | graph query | yes | no | no | no | no | terminal |
112
+ | serve | yes¹ | no | no | no | no | terminal |
113
+ | completion | no | no | no | no | no | terminal |
114
+ | completion install | yes | no | no | no | no | terminal |
115
+
116
+ ¹ `--json` applies only to `--status` on `gno serve` and `gno daemon` (see [process-status schema](./output-schemas/process-status.schema.json)).
117
+
118
+ ---
119
+
120
+ ## Commands
121
+
122
+ ### gno status
123
+
124
+ Display index status and health information.
125
+
126
+ **Synopsis:**
127
+
128
+ ```bash
129
+ gno status [--json|--md]
130
+ ```
131
+
132
+ **Output (JSON):**
133
+
134
+ ```json
135
+ {
136
+ "indexName": "default",
137
+ "configPath": "/path/to/config",
138
+ "dbPath": "/path/to/index.sqlite",
139
+ "collections": [
140
+ {
141
+ "name": "work",
142
+ "path": "/path",
143
+ "documentCount": 100,
144
+ "chunkCount": 500,
145
+ "embeddedCount": 500
146
+ }
147
+ ],
148
+ "totalDocuments": 100,
149
+ "totalChunks": 500,
150
+ "embeddingBacklog": 0,
151
+ "lastUpdated": "2025-12-23T10:00:00Z",
152
+ "healthy": true,
153
+ "activation": {
154
+ "schemaVersion": "1.0",
155
+ "usable": true,
156
+ "healthy": true,
157
+ "collections": [
158
+ {
159
+ "collection": "work",
160
+ "ready": true,
161
+ "generatedAt": "2025-12-23T10:00:00Z",
162
+ "stages": {
163
+ "index": {
164
+ "status": "passed",
165
+ "startedAt": "2025-12-23T10:00:00Z",
166
+ "completedAt": "2025-12-23T10:00:00Z",
167
+ "latencyMs": 3
168
+ },
169
+ "lexical": {
170
+ "status": "passed",
171
+ "startedAt": "2025-12-23T10:00:00Z",
172
+ "completedAt": "2025-12-23T10:00:00Z",
173
+ "latencyMs": 2
174
+ },
175
+ "semantic": {
176
+ "status": "pending",
177
+ "startedAt": null,
178
+ "completedAt": null,
179
+ "latencyMs": null,
180
+ "code": "semantic_not_checked"
181
+ },
182
+ "connector": {
183
+ "status": "skipped",
184
+ "startedAt": null,
185
+ "completedAt": null,
186
+ "latencyMs": null,
187
+ "code": "connector_not_requested"
188
+ }
189
+ },
190
+ "semanticAvailability": {
191
+ "status": "pending",
192
+ "code": "semantic_not_checked",
193
+ "command": "gno status"
194
+ },
195
+ "remediation": null
196
+ }
197
+ ],
198
+ "connectors": [],
199
+ "connectorProjection": {
200
+ "total": 0,
201
+ "projected": 0,
202
+ "truncated": false
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ `activation.usable` means at least one configured collection passed its local
209
+ lexical proof. `activation.healthy` means every configured collection passed.
210
+ Semantic and connector stages remain independent; passive status never starts a
211
+ model runtime or connector process. `gno status` still exits 0 when activation
212
+ is unhealthy so scripts can inspect the structured state.
213
+
214
+ JSON output also includes `resident` using
215
+ `gno://schemas/resident-status@1.0`. Direct `gno status` is intentionally
216
+ truthful about its lifecycle: `mode:"direct-cli"`, `resident:false`, no
217
+ listener, and zero resident counters. It does not imply attachment to a live
218
+ `serve` or `daemon`.
219
+
220
+ Local activation fingerprints use active-document identifiers and source/mirror
221
+ hashes plus schema, tokenizer, and owned FTS synchronization metadata. Passive
222
+ status never selects or compares stored markdown or FTS bodies. On a receipt
223
+ miss, lexical proof reads at most 64 document prefixes of 32,768 characters and
224
+ tries at most 64 corpus-derived terms. `index_out_of_sync` fails before probing
225
+ when any active document lacks a current owned FTS row. Migration 013 compares
226
+ legacy FTS bodies once before backfilling that marker; after migration, direct
227
+ out-of-band FTS body mutations remain outside the owned-writer contract.
228
+
229
+ Passive callers report `semantic_not_checked` when vector runtime availability
230
+ is unknown. `vector_unavailable` is reserved for a resident runtime that has
231
+ positively reported vector search unavailable.
232
+
233
+ `connectorProjection.total` counts every configured collection and connector
234
+ target pair before projection bounds. `projected` equals `connectors.length`,
235
+ and `truncated` is true exactly when `total > projected`. No result is claimed
236
+ for omitted pairs, and human-readable health output must not report connector
237
+ proof as healthy while the projection is truncated.
238
+
239
+ **Exit Codes:**
240
+
241
+ - 0: Success
242
+ - 2: DB not initialized or inaccessible
243
+
244
+ ---
245
+
246
+ ### gno init
247
+
248
+ Initialize GNO configuration and index database. Safe to run repeatedly (idempotent).
249
+
250
+ **Synopsis:**
251
+
252
+ ```bash
253
+ gno init [<path>] [--name <name>] [--pattern <glob>] [--include <csv-ext>] [--exclude <csv>] [--update <cmd>] [--tokenizer <type>] [--language <code>] [--yes]
254
+ ```
255
+
256
+ **Arguments:**
257
+ | Arg | Type | Description |
258
+ |-----|------|-------------|
259
+ | `<path>` | string | Optional root directory to add as a collection |
260
+
261
+ **Options:**
262
+
263
+ | Option | Type | Default | Description |
264
+ | ------------- | ------- | ------------------- | --------------------------------------------------------- |
265
+ | `--name` | string | dirname | Collection name (required if path given) |
266
+ | `--pattern` | glob | `**/*` | File matching pattern |
267
+ | `--include` | csv | - | Extension allowlist (e.g., `.md,.pdf`) |
268
+ | `--exclude` | csv | `.git,node_modules` | Exclude patterns |
269
+ | `--update` | string | - | Shell command to run before indexing |
270
+ | `--tokenizer` | string | unicode61 | FTS tokenizer: unicode61, porter, trigram |
271
+ | `--language` | string | - | BCP-47 language hint for collection (e.g., en, de, zh-CN) |
272
+ | `--yes` | boolean | false | Skip prompts, accept defaults |
273
+
274
+ **Behavior:**
275
+
276
+ 1. Creates config directory and `index.yml` if missing
277
+ 2. Creates data directory and `index-<name>.sqlite` if missing
278
+ 3. Runs migrations on DB
279
+ 4. If `<path>` provided, adds collection (like `collection add`)
280
+ 5. Prints resolved paths and next steps
281
+
282
+ **Exit Codes:**
283
+
284
+ - 0: Success (or already initialized)
285
+ - 1: Invalid arguments
286
+ - 2: Cannot create directories or DB
287
+
288
+ **Examples:**
289
+
290
+ ```bash
291
+ # Initialize with defaults
292
+ gno init
293
+
294
+ # Initialize with a collection
295
+ gno init ~/notes --name notes --pattern "**/*.md"
296
+
297
+ # Non-interactive initialization
298
+ gno init ~/work/docs --name work --yes
299
+
300
+ # Initialize with porter stemmer (English-optimized)
301
+ gno init --tokenizer porter
302
+
303
+ # Initialize with language hint for German docs
304
+ gno init ~/docs/german --name german --language de
305
+ ```
306
+
307
+ **Config Shape (`index.yml`):**
308
+
309
+ ```yaml
310
+ version: "1.0"
311
+ ftsTokenizer: snowball english
312
+ editorUriTemplate: "vscode://file/{path}:{line}:{col}"
313
+ collections:
314
+ - name: notes
315
+ path: /Users/you/notes
316
+ pattern: "**/*"
317
+ include: []
318
+ exclude: [.git, node_modules]
319
+ updateCmd: git pull
320
+ languageHint: en
321
+ models:
322
+ embed: file:/models/embed.gguf
323
+ contexts:
324
+ - scopeType: global
325
+ scopeKey: /
326
+ text: Shared retrieval context
327
+ models:
328
+ activePreset: slim-tuned
329
+ contentTypes:
330
+ - id: person
331
+ prefixes: [people/, contacts/]
332
+ preset: person
333
+ graphHints: [mentions, works_at]
334
+ searchBoost: 1.15
335
+ - id: meeting
336
+ prefixes: [meetings/]
337
+ preset: meeting
338
+ temporal: true
339
+ ```
340
+
341
+ `contentTypes` is optional and defaults to `[]`. It is schema-lite and opt-in:
342
+ Zod validates `id`, `prefixes`, `preset`, `graphHints`, `searchBoost`, and
343
+ `temporal`, while `preset` remains a permissive string. Post-parse normalization
344
+ warns and drops unknown preset references, dedupes exact duplicate prefixes,
345
+ retains overlapping prefixes, and sorts rules longest-prefix-first. `searchBoost`
346
+ is accepted but currently no-op. `graphHints` is active: ordered hints type
347
+ projected wiki/markdown edges and surface in graph traversal/diagnose metadata.
348
+
349
+ ---
350
+
351
+ ### gno collection add
352
+
353
+ Add a new collection to the index.
354
+
355
+ **Synopsis:**
356
+
357
+ ```bash
358
+ gno collection add <path> --name <name> [--pattern <glob>] [--include <csv-ext>] [--exclude <csv>] [--update <cmd>] [--embed-model <uri>] [--language <code>]
359
+ ```
360
+
361
+ **Arguments:**
362
+ | Arg | Type | Description |
363
+ |-----|------|-------------|
364
+ | `<path>` | string | Absolute path to collection root directory |
365
+
366
+ **Options:**
367
+
368
+ | Option | Type | Default | Description |
369
+ | --------------- | ------ | ------------------------------------------ | ---------------------------------------------------- |
370
+ | `--name` | string | required | Unique collection identifier |
371
+ | `--pattern` | glob | `**/*` | File matching glob pattern |
372
+ | `--include` | csv | - | Extension allowlist |
373
+ | `--exclude` | csv | `.git,node_modules,.venv,.idea,dist,build` | Exclude patterns |
374
+ | `--update` | string | - | Shell command to run before indexing |
375
+ | `--embed-model` | string | - | Initial collection-specific embedding model override |
376
+ | `--language` | string | - | BCP-47 language hint (e.g., en, de, zh-CN) |
377
+
378
+ **Exit Codes:**
379
+
380
+ - 0: Success
381
+ - 1: Missing required args, invalid path, duplicate name, or invalid language hint
382
+ - 2: Config write failure
383
+
384
+ **Examples:**
385
+
386
+ ```bash
387
+ gno collection add ~/notes --name notes --pattern "**/*.md"
388
+ gno collection add ~/work/docs --name work --pattern "**/*.{md,pdf,docx}"
389
+ ```
390
+
391
+ ---
392
+
393
+ ### gno collection list
394
+
395
+ List all configured collections.
396
+
397
+ **Synopsis:**
398
+
399
+ ```bash
400
+ gno collection list [--json|--md]
401
+ ```
402
+
403
+ ---
404
+
405
+ ### gno collection clear-embeddings
406
+
407
+ Clear embeddings for one collection.
408
+
409
+ **Synopsis:**
410
+
411
+ ```bash
412
+ gno collection clear-embeddings <name> [--all] [--json]
413
+ ```
414
+
415
+ **Arguments:**
416
+ | Arg | Type | Description |
417
+ |-----|------|-------------|
418
+ | `<name>` | string | Collection name |
419
+
420
+ **Options:**
421
+
422
+ | Option | Type | Default | Description |
423
+ | -------- | ------- | ------- | ---------------------------------------------------------------------------- |
424
+ | `--all` | boolean | false | Remove all embeddings for the collection (default only removes stale models) |
425
+ | `--json` | boolean | false | JSON output |
426
+
427
+ **Behavior:**
428
+
429
+ - default mode is `stale`
430
+ - `stale` removes embeddings for models that are not the current embed model for that collection
431
+ - `all` removes every embedding for that collection and requires a new `gno embed --collection <name>` run
432
+ - embeddings shared by active documents in other collections are retained
433
+
434
+ ---
435
+
436
+ ### gno embed
437
+
438
+ Generate embeddings for indexed chunks.
439
+
440
+ **Synopsis:**
441
+
442
+ ```bash
443
+ gno embed [collection] [--collection <name>] [--force] [--model <uri>] [--batch-size <n>] [--dry-run] [--yes] [--json]
444
+ ```
445
+
446
+ **Arguments:**
447
+ | Arg | Type | Description |
448
+ |-----|------|-------------|
449
+ | `[collection]` | string | Optional collection name shortcut |
450
+
451
+ **Behavior note:**
452
+
453
+ - `[collection]` and `--collection <name>` are aliases
454
+ - if provided, embedding work is scoped to that collection only
455
+
456
+ ````
457
+
458
+ **Output (JSON):**
459
+
460
+ ```json
461
+ [
462
+ {
463
+ "name": "notes",
464
+ "path": "/home/user/notes",
465
+ "pattern": "**/*.md",
466
+ "include": null,
467
+ "exclude": [".git", "node_modules"],
468
+ "updateCmd": null
469
+ }
470
+ ]
471
+ ````
472
+
473
+ **Exit Codes:**
474
+
475
+ - 0: Success
476
+
477
+ ---
478
+
479
+ ### gno collection remove
480
+
481
+ Remove a collection from the index.
482
+
483
+ **Synopsis:**
484
+
485
+ ```bash
486
+ gno collection remove <name>
487
+ ```
488
+
489
+ **Arguments:**
490
+ | Arg | Type | Description |
491
+ |-----|------|-------------|
492
+ | `<name>` | string | Collection name to remove |
493
+
494
+ **Behavior:**
495
+
496
+ - Removes collection from config
497
+ - Marks documents as inactive (does not delete DB rows until `cleanup`)
498
+
499
+ **Exit Codes:**
500
+
501
+ - 0: Success
502
+ - 1: Collection not found
503
+
504
+ ---
505
+
506
+ ### gno collection rename
507
+
508
+ Rename a collection.
509
+
510
+ **Synopsis:**
511
+
512
+ ```bash
513
+ gno collection rename <old> <new>
514
+ ```
515
+
516
+ **Arguments:**
517
+ | Arg | Type | Description |
518
+ |-----|------|-------------|
519
+ | `<old>` | string | Current collection name |
520
+ | `<new>` | string | New collection name |
521
+
522
+ **Exit Codes:**
523
+
524
+ - 0: Success
525
+ - 1: Old name not found or new name already exists
526
+
527
+ ---
528
+
529
+ ### gno update
530
+
531
+ Sync files from disk into the index (ingestion without embedding).
532
+
533
+ **Synopsis:**
534
+
535
+ ```bash
536
+ gno update [--git-pull]
537
+ ```
538
+
539
+ **Options:**
540
+ | Option | Type | Description |
541
+ |--------|------|-------------|
542
+ | `--git-pull` | boolean | Run `git pull` in git repositories before scanning |
543
+
544
+ **Behavior:**
545
+
546
+ 1. For each collection, enumerate files matching patterns
547
+ 2. Hash files, detect MIME types
548
+ 3. Convert to Markdown mirror
549
+ 4. Chunk content for indexing
550
+ 5. Update FTS index
551
+ 6. Mark missing files as inactive
552
+
553
+ **Exit Codes:**
554
+
555
+ - 0: Success (conversion warnings do not affect exit code)
556
+ - 2: DB failure or critical IO error
557
+
558
+ ---
559
+
560
+ ### gno index
561
+
562
+ Build or update the index end-to-end (update + embed).
563
+
564
+ **Synopsis:**
565
+
566
+ ```bash
567
+ gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes]
568
+ ```
569
+
570
+ **Options:**
571
+ | Option | Type | Description |
572
+ |--------|------|-------------|
573
+ | `--collection` | string | Scope to single collection |
574
+ | `--no-embed` | boolean | Run ingestion only, skip embedding |
575
+ | `--models-pull` | boolean | Download models if missing (prompts unless `--yes`) |
576
+ | `--git-pull` | boolean | Run `git pull` in git repositories |
577
+ | `--yes` | boolean | Accept defaults, no prompts |
578
+
579
+ **Behavior:**
580
+
581
+ - Runs `update` then `embed` by default
582
+ - With `--no-embed`, runs `update` only
583
+
584
+ **Exit Codes:**
585
+
586
+ - 0: Success
587
+ - 1: Invalid collection name
588
+ - 2: DB or model failure
589
+
590
+ **Examples:**
591
+
592
+ ```bash
593
+ # Full index build
594
+ gno index
595
+
596
+ # Update single collection without embedding
597
+ gno index --collection notes --no-embed
598
+
599
+ # CI/scripted usage
600
+ gno index --models-pull --yes
601
+
602
+ # Debug embedding errors
603
+ gno index --verbose
604
+ ```
605
+
606
+ **Verbose Mode:**
607
+
608
+ With `--verbose`, embedding errors during the embed phase are logged to stderr (see `gno embed`).
609
+
610
+ ---
611
+
612
+ ### gno embed
613
+
614
+ Generate embeddings for chunks without vectors.
615
+
616
+ On CPU-only machines, implementations may use multiple embedding contexts
617
+ internally to improve throughput, and may fall back to fewer contexts if
618
+ memory pressure prevents creating the full pool.
619
+
620
+ **Synopsis:**
621
+
622
+ ```bash
623
+ gno embed [--force] [--model <uri>] [--batch-size <n>] [--dry-run] [--yes] [--json]
624
+ ```
625
+
626
+ **Options:**
627
+
628
+ | Option | Type | Default | Description |
629
+ | -------------- | ------- | ------- | --------------------------------------------- |
630
+ | `--force` | boolean | false | Re-embed all chunks (ignore existing vectors) |
631
+ | `--model` | string | config | Override embedding model URI |
632
+ | `--batch-size` | integer | 32 | Chunks per batch |
633
+ | `--dry-run` | boolean | false | Show what would be embedded without doing it |
634
+ | `--yes`, `-y` | boolean | false | Skip confirmation prompts |
635
+ | `--json` | boolean | false | Output result as JSON |
636
+
637
+ **Exit Codes:**
638
+
639
+ - 0: Success
640
+ - 1: User cancelled
641
+ - 2: Model not available or embedding failure
642
+
643
+ **JSON Output:**
644
+
645
+ ```json
646
+ {
647
+ "embedded": 1234,
648
+ "errors": 0,
649
+ "duration": 45.2,
650
+ "model": "hf:BAAI/bge-m3-gguf/bge-m3-q8_0.gguf",
651
+ "searchAvailable": true
652
+ }
653
+ ```
654
+
655
+ **Verbose Mode:**
656
+
657
+ With `--verbose`, embedding errors are logged to stderr:
658
+
659
+ ```
660
+ [embed] Batch failed: <error message>
661
+ [embed] Count mismatch: got X, expected Y
662
+ [embed] Store failed: <error message>
663
+ ```
664
+
665
+ ---
666
+
667
+ ### Retrieval trace receipts
668
+
669
+ When local retrieval tracing is enabled, successful `search`, `vsearch`,
670
+ `query`, `ask`, `get`, and `context build` commands write one
671
+ `Trace: <traceId>` receipt line to stderr after the normal result. Stdout and
672
+ all JSON/Markdown/file payload schemas remain byte-for-byte unchanged.
673
+ Retrieval-only commands leave the trace open for explicit evidence follow-up.
674
+ Pass that receipt back to `gno get --trace-id <traceId>` to record the exact
675
+ opened line range against the same query. Disabled tracing performs no trace
676
+ ID, fingerprint, or receipt work and writes no receipt line.
677
+
678
+ Trace management remains available for already-stored receipts after recording
679
+ is disabled:
680
+
681
+ ```text
682
+ gno trace list [-n <limit>] [--cursor <cursor>] [--json|--md]
683
+ gno trace show <trace-id> [--detail-limit <limit>] [--json|--md]
684
+ gno trace label <trace-id> --label <relevant|irrelevant|missing-expected> --target <ref>
685
+ [--target-kind <document|chunk|span>] [--from-line <line> --to-line <line>]
686
+ [--source-hash <sha256>] [--docid <docid>] [--idempotency-key <key>] [--json|--md]
687
+ gno trace export <trace-id...> [--format <agentic-receipt|qrels>] [--output <path>] [--json]
688
+ gno trace replay <qrels-export-id> --candidate <bm25|vector|hybrid>
689
+ [-n <limit>] [--candidate-limit <limit>] [--no-expand] [--no-rerank] [--json|--md]
690
+ gno trace delete <trace-id> [--json|--md]
691
+ gno --yes trace purge [--json|--md]
692
+ ```
693
+
694
+ `list` is newest-first, cursor-paginated, and never returns raw replay queries
695
+ or goals. `show` returns one bounded detail receipt with exact per-section
696
+ totals and truncation flags. A relevant or irrelevant label must resolve to
697
+ recorded evidence; `missing-expected` accepts only a content-free `gno://` URI,
698
+ docid, or immutable source hash. Labels are append-only and retry-safe.
699
+
700
+ `export` accepts one or more immutable terminal traces and sorts and
701
+ deduplicates their IDs. The default deterministic `agentic-receipt` artifact
702
+ preserves the complete stored receipt. `--format qrels` requires replay-mode
703
+ receipts with an exact query, strict filters, ranked evidence, and at least one
704
+ explicit relevant or missing-expected judgment. It exports only hashes,
705
+ coordinates, ranks, capabilities, fallbacks, and explicit outcomes—never
706
+ source or mirror text. Both formats reject open or missing traces.
707
+ `completed`, `partial`, `failed`, and `cancelled` remain distinct; no terminal
708
+ state implies negative relevance.
709
+
710
+ `replay` verifies the saved qrels aggregate manifest and reruns only the named
711
+ candidate against the current local index. It compares final and planner ranks,
712
+ coverage, explicit open/cite/pin outcomes, capability fallbacks, fingerprints,
713
+ and unchanged/stale/missing source state. The result is
714
+ `improved|unchanged|regressed|unreplayable` with a human promotion
715
+ recommendation and `applied: false`; replay never changes configuration,
716
+ boosts, prompts, models, traces, or user files. Missing or cascaded manifest
717
+ links and changed source hashes fail closed instead of becoming an empty
718
+ successful run.
719
+ Without `--output`, JSON is the complete `retrieval-trace-export` receipt.
720
+ `--output` writes only the canonical artifact atomically and intentionally
721
+ emits no stdout. Full purge requires the global `--yes` flag and reports
722
+ whether SQLite/WAL physical cleanup completed, remained busy, or failed.
723
+
724
+ Structured outputs validate against
725
+ `retrieval-trace-{list,show,judgment,export,qrels,replay,delete,purge}.schema.json`;
726
+ file-only `trace export --output` is the documented exception.
727
+
728
+ ---
729
+
730
+ ### gno search
731
+
732
+ BM25 keyword search over indexed documents.
733
+
734
+ **Synopsis:**
735
+
736
+ ```bash
737
+ gno search <query> [-n <num>] [--min-score <num>] [-c <collection>] [--since <date>] [--until <date>] [--category <values>] [--author <text>] [--intent <text>] [--exclude <values>] [--tags-all <tags>] [--tags-any <tags>] [--full] [--line-numbers] [--lang <bcp47>] [--json|--files|--csv|--md|--xml]
738
+ ```
739
+
740
+ **Arguments:**
741
+ | Arg | Type | Description |
742
+ |-----|------|-------------|
743
+ | `<query>` | string | Search query |
744
+
745
+ **Options:**
746
+
747
+ | Option | Type | Default | Description |
748
+ | ------------------ | ------- | ------------------------- | --------------------------------------------------------------------------------------------- |
749
+ | `-n` | integer | 5 (20 for --json/--files) | Max results |
750
+ | `--min-score` | number | 0 | Minimum score threshold |
751
+ | `-c, --collection` | string | all | Filter to collection |
752
+ | `--since` | string | none | Modified-at lower bound (ISO date/time or relative token) |
753
+ | `--until` | string | none | Modified-at upper bound (ISO date/time or relative token) |
754
+ | `--category` | string | none | Filter to docs with matching category/content type (comma-separated) |
755
+ | `--author` | string | none | Filter to docs where author contains value (case-insensitive) |
756
+ | `--intent` | string | none | Disambiguating context for ambiguous queries; steers snippets without being searched directly |
757
+ | `--exclude` | string | none | Hard-prune docs containing any comma-separated term in title/path/body |
758
+ | `--tags-all` | string | none | Filter to docs with ALL tags (comma-separated) |
759
+ | `--tags-any` | string | none | Filter to docs with ANY tag (comma-separated) |
760
+ | `--full` | boolean | false | Include full mirror content instead of snippet |
761
+ | `--line-numbers` | boolean | false | Include line numbers in output |
762
+ | `--lang` | string | auto | Language filter/hint (BCP-47) |
763
+
764
+ **Scoring:**
765
+
766
+ Scores are normalized per query to a 0-1 range using min-max scaling:
767
+
768
+ - `1.0` = best match among returned results
769
+ - `0.0` = worst match among returned results
770
+
771
+ Important notes:
772
+
773
+ - Scores are **relative within a single query's result set**, not comparable across different queries
774
+ - `--min-score` filters based on this normalized score (e.g., `--min-score 0.5` keeps top half)
775
+ - Raw SQLite FTS5 BM25 scores vary with corpus size; normalization ensures consistent UX
776
+ - When all results have equal raw scores, they all receive `1.0`
777
+ - Queries with explicit recency intent (`latest`, `newest`, `recent`) are ordered newest-first using canonical frontmatter date when present, falling back to source modified time.
778
+
779
+ **Lexical query semantics:**
780
+
781
+ - plain terms use prefix matching
782
+ - quoted phrases are supported
783
+ - negation is supported only when at least one positive term exists
784
+ - hyphenated compounds such as `real-time`, `gpt-4`, and `DEC-0054` are handled intentionally
785
+ - malformed lexical syntax returns exit code `1`
786
+
787
+ **Output (JSON):**
788
+ See [Output Schemas](./output-schemas/search-result.schema.json)
789
+
790
+ Every structured search result may include `context`, the matching
791
+ user-configured guidance joined in deterministic global, collection, then
792
+ broad-to-specific path-prefix order. The field is absent when no scope matches;
793
+ `uri` and `docid` remain the exact source identity. The same contract applies to
794
+ `vsearch`, `query`, and the `results` array returned by `ask`.
795
+
796
+ **Exit Codes:**
797
+
798
+ - 0: Success (including zero results)
799
+ - 1: Invalid query or options
800
+ - 2: DB failure
801
+
802
+ **Examples:**
803
+
804
+ ```bash
805
+ gno search "termination clause"
806
+ gno search "deploy staging" -n 10 --collection work
807
+ gno search "contract" --json | jq '.[] | .uri'
808
+ ```
809
+
810
+ ---
811
+
812
+ ### gno vsearch
813
+
814
+ Vector semantic search over indexed documents.
815
+
816
+ **Synopsis:**
817
+
818
+ ```bash
819
+ gno vsearch <query> [-n <num>] [--min-score <num>] [-c <collection>] [--since <date>] [--until <date>] [--category <values>] [--author <text>] [--intent <text>] [--exclude <values>] [--tags-all <tags>] [--tags-any <tags>] [--full] [--line-numbers] [--lang <bcp47>] [--json|--files|--csv|--md|--xml]
820
+ ```
821
+
822
+ **Options:** Same as `gno search` (including temporal/category/author and tag filters)
823
+
824
+ **Scoring:**
825
+
826
+ Vector similarity scores are normalized to a 0-1 range:
827
+
828
+ - `1.0` = identical/most similar
829
+ - `0.0` = least similar (within result set)
830
+
831
+ Cosine distance (0=identical, 2=opposite) is converted: `score = 1 - (distance / 2)`
832
+
833
+ **Exit Codes:**
834
+
835
+ - 0: Success
836
+ - 1: Invalid options
837
+ - 2: Vectors not available (suggests `gno index` or `gno embed`)
838
+
839
+ ---
840
+
841
+ ### gno query
842
+
843
+ Hybrid search combining BM25 and vector retrieval with optional expansion and reranking.
844
+
845
+ **Synopsis:**
846
+
847
+ ```bash
848
+ gno query <query> [-n <num>] [--min-score <num>] [-c <collection>] [--since <date>] [--until <date>] [--category <values>] [--author <text>] [--intent <text>] [--exclude <values>] [-C <num>] [--tags-all <tags>] [--tags-any <tags>] [--full] [--line-numbers] [--lang <bcp47>] [--no-expand] [--no-rerank] [--graph] [--no-graph] [--query-mode <mode:text>]... [--explain] [--json|--files|--csv|--md|--xml]
849
+ gno query diagnose <query> --target <doc> [-n <num>] [--min-score <num>] [-c <collection>] [--since <date>] [--until <date>] [--category <values>] [--author <text>] [--intent <text>] [--exclude <values>] [-C <num>] [--tags-all <tags>] [--tags-any <tags>] [--lang <bcp47>] [--no-expand] [--no-rerank] [--graph] [--no-graph] [--json]
850
+ ```
851
+
852
+ **Options:** Same as `gno search`, plus:
853
+
854
+ **Additional Options:**
855
+ | Option | Type | Description |
856
+ |--------|------|-------------|
857
+ | `--no-expand` | boolean | Disable query expansion |
858
+ | `--no-rerank` | boolean | Disable cross-encoder reranking |
859
+ | `--graph` | boolean | Enable bounded one-hop graph neighbor expansion |
860
+ | `--no-graph` | boolean | Compatibility no-op; graph expansion is off unless `--graph` is passed |
861
+ | `--intent` | string | Disambiguating context for ambiguous queries; steers expansion, rerank chunk/snippet choice, and disables strong-signal bypass without being searched directly |
862
+ | `--exclude` | string | Hard-prune docs containing any comma-separated term in title/path/body |
863
+ | `-C, --candidate-limit` | integer | Max candidates passed to reranking (default 20) |
864
+ | `--query-mode` | string[] | Structured mode entry (`term:<text>`, `intent:<text>`, `hyde:<text>`). Repeatable. |
865
+ | `--explain` | boolean | Print retrieval explanation to stderr |
866
+ | `--target` | ref | Required for `query diagnose`; target document to diagnose |
867
+
868
+ **Compatibility / Migration:**
869
+
870
+ - Legacy query invocations remain valid (`gno query "<text>"`, `--fast`, `--thorough`, `--no-expand`, `--no-rerank`).
871
+ - `--fast` skips query expansion and reranking. Graph expansion is already off unless `--graph` is passed.
872
+ - `--intent` is orthogonal to `--query-mode`: intent steers scoring/prompting, while query modes inject caller-provided retrieval expansions.
873
+ - `--query-mode` is optional and additive to the command surface.
874
+ - If one or more `--query-mode` entries are provided, generated expansion is bypassed and provided entries are used as retrieval intents.
875
+ - By default, `gno query` does not expand through the document graph. Use `--graph` to add a capped one-hop graph-neighbor candidate set after BM25/vector retrieval. Explicit links are weighted above inferred, ambiguous, or similarity edges.
876
+
877
+ **Diagnose Output:**
878
+
879
+ `gno query diagnose` wraps the shared `diagnoseQueryTarget()` core and emits
880
+ `query-diagnose.schema.json` for `--json`. The payload requires
881
+ `schemaVersion: "1.0"`, resolves the target first, reports `target.status`
882
+ (`not_found|inactive|no_indexed_content|filtered_out|diagnosed`), and only runs
883
+ stage tracing for `diagnosed` targets. Stages report
884
+ `present`, `rank`, `score`, `survived`, `dropReason`, `status`, and
885
+ `sourceCount` across BM25, vector, fusion, graph, and rerank. BM25-only mode
886
+ marks vector/rerank skipped when unavailable or disabled, but fusion remains
887
+ active with `sourceCount: 1`.
888
+
889
+ **Explain Output (stderr):**
890
+
891
+ ```
892
+ [explain] expansion: enabled (3 lexical, 2 semantic variants)
893
+ [explain] bm25: 45 candidates
894
+ [explain] vector: 38 candidates
895
+ [explain] graph: seeds=5, candidates=4/20, explicit=3, inferred=1, ambiguous=0, similarity=0
896
+ [explain] fusion: RRF k=60, 52 unique candidates
897
+ [explain] rerank: top 20 reranked
898
+ [explain] result 1: score=0.92 (bm25=0.85, vec=0.78, rerank=0.95)
899
+ ```
900
+
901
+ **Exit Codes:**
902
+
903
+ - 0: Success (degrades gracefully if vectors unavailable)
904
+ - 1: Invalid options
905
+ - 2: DB or model failure
906
+
907
+ ---
908
+
909
+ ### gno bench
910
+
911
+ Run retrieval quality benchmarks against an already indexed GNO corpus.
912
+
913
+ **Synopsis:**
914
+
915
+ ```bash
916
+ gno bench <fixture.json> [-c <collection>] [-k <num>] [--mode <name>]... [-C <num>] [--json]
917
+ ```
918
+
919
+ **Fixture schema:** [`spec/bench-fixture.schema.json`](./bench-fixture.schema.json)
920
+
921
+ **JSON output schema:** [`spec/output-schemas/bench-result.schema.json`](./output-schemas/bench-result.schema.json)
922
+
923
+ **Options:**
924
+
925
+ | Option | Type | Description |
926
+ | ----------------------- | -------- | ----------------------------------------------------------------------------------------------- |
927
+ | `-c, --collection` | string | Override fixture/query collection |
928
+ | `-k, --top-k` | integer | Override top-k cutoff used for Precision@K, Recall@K, F1@K, MRR, and nDCG@K |
929
+ | `--mode` | string[] | Override fixture modes. Repeatable: `bm25`, `vector`, `hybrid`, `fast`, `no-rerank`, `thorough` |
930
+ | `-C, --candidate-limit` | integer | Override candidate limit for hybrid/rerank modes |
931
+ | `--json` | boolean | Emit structured benchmark result |
932
+
933
+ Fixtures support:
934
+
935
+ - `version: 1`
936
+ - optional `metadata`, `collection`, `topK`, `candidateLimit`
937
+ - `modes` as aliases or objects with `type`, `noExpand`, `noRerank`, `candidateLimit`, `limit`, and `queryModes`
938
+ - `queries[]` with `id`, `query`, expected documents/URIs, optional `collection`, optional `topK`, optional `queryModes`, and optional graded `judgments`
939
+
940
+ Metrics reported per mode and per query:
941
+
942
+ - `precisionAtK`
943
+ - `recallAtK`
944
+ - `f1AtK`
945
+ - `mrr`
946
+ - `ndcgAtK`
947
+ - latency summaries (`p50Ms`, `p95Ms`, `meanMs`)
948
+
949
+ **Exit Codes:**
950
+
951
+ - 0: Fixture loaded and benchmark ran
952
+ - 1: Invalid fixture, mode, or options
953
+ - 2: Runtime failure
954
+
955
+ ---
956
+
957
+ ### gno ask
958
+
959
+ Human-friendly query with citations-first output and optional grounded answer.
960
+
961
+ **Synopsis:**
962
+
963
+ ```bash
964
+ gno ask <query> [-n <num>] [-c <collection>] [--lang <bcp47>] [--since <date>] [--until <date>] [--category <values>] [--author <text>] [--intent <text>] [--exclude <values>] [--query-mode <mode:text>]... [-C <num>] [--answer|--verify] [--no-answer] [--max-answer-tokens <n>] [--context-budget-tokens <n>] [--context-budget-bytes <n>] [--min-score <score>] [--graph] [--no-expand] [--no-rerank] [--show-sources] [--json|--md]
965
+ ```
966
+
967
+ **Options:**
968
+
969
+ | Option | Type | Default | Description |
970
+ | ------------------------- | -------- | ------- | ---------------------------------------------------------------------------------- |
971
+ | `--answer` | boolean | false | Generate short grounded answer |
972
+ | `--verify` | boolean | false | Generate from a closed Context Capsule; verify every claim or abstain |
973
+ | `--no-answer` | boolean | false | Force retrieval-only output |
974
+ | `--max-answer-tokens` | integer | config | Cap answer generation tokens |
975
+ | `--context-budget-tokens` | integer | 12000 | Global token budget for verified Context evidence |
976
+ | `--context-budget-bytes` | integer | none | Optional global byte budget for verified Context evidence |
977
+ | `--min-score` | number | none | Minimum retrieval score from 0 through 1 |
978
+ | `--graph` | boolean | false | Include bounded graph expansion in verified Context retrieval |
979
+ | `--since` | string | none | Modified-at lower bound (ISO date/time or relative token) |
980
+ | `--until` | string | none | Modified-at upper bound (ISO date/time or relative token) |
981
+ | `--category` | string | none | Filter to docs with matching category/content type (comma-separated) |
982
+ | `--author` | string | none | Filter to docs where author contains value (case-insensitive) |
983
+ | `--intent` | string | none | Disambiguating context for ambiguous questions without searching on that text |
984
+ | `--exclude` | string | none | Hard-prune docs containing any comma-separated term in title/path/body |
985
+ | `--query-mode` | string[] | none | Structured mode entry (`term:<text>`, `intent:<text>`, `hyde:<text>`). Repeatable. |
986
+ | `-C, --candidate-limit` | integer | 20 | Max candidates passed to reranking |
987
+ | `--no-expand` | boolean | false | Disable query expansion |
988
+ | `--no-rerank` | boolean | false | Disable cross-encoder reranking |
989
+ | `--show-sources` | boolean | false | Show all retrieved sources (not just cited) |
990
+
991
+ **Output (JSON):**
992
+ See [Output Schemas](./output-schemas/ask.schema.json)
993
+
994
+ Notes:
995
+
996
+ - `meta.answerContext` is optional explain payload for answer source selection.
997
+ - `--verify` implies answer generation and cannot be combined with
998
+ `--no-answer`. The JSON result adds the closed Capsule, freshness receipt,
999
+ four-state per-claim verdicts (`supported`, `contradicted`, `insufficient`,
1000
+ `uncertain`), exact evidence IDs and line spans, coverage, gaps, semantic
1001
+ verifier state, and explicit abstention. Support below 100% never returns the
1002
+ draft answer.
1003
+ - Terminal and Markdown verified output preserve the same verdicts, exact
1004
+ support/conflict spans, coverage, gaps, abstention, and capability
1005
+ degradation. With `--show-sources`, both formats list every retained Capsule
1006
+ evidence span with its exact URI and line range. JSON remains the canonical
1007
+ machine contract.
1008
+ - Verification classifies support only against the closed Capsule and its
1009
+ freshness receipt. It does not guarantee corpus completeness or source truth.
1010
+ An unavailable, incapable, failed, or malformed semantic verifier cannot mark
1011
+ claims supported; unresolved substantive claims remain uncertain and force
1012
+ abstention.
1013
+ - Verified retrieval records the normalized request and requested/attempted
1014
+ capability states in its Capsule. The active `--index` value is host-owned
1015
+ and used for both compilation and freshness verification.
1016
+ - Strategy: adaptive coverage (relevance + query/facet coverage), not fixed top-N.
1017
+ - Each result preserves optional configured `context`. Answer generation places
1018
+ that trusted configuration in a separate prompt role from untrusted retrieved
1019
+ document content.
1020
+
1021
+ **Exit Codes:**
1022
+
1023
+ - 0: Success
1024
+ - 1: Invalid options
1025
+ - 2: DB or model failure
1026
+
1027
+ **Examples:**
1028
+
1029
+ ```bash
1030
+ gno ask "how do we deploy to staging"
1031
+ gno ask "termination clause" --collection work --answer
1032
+ gno ask "who owns launch?" --verify --show-sources
1033
+ ```
1034
+
1035
+ ---
1036
+
1037
+ ### gno capture
1038
+
1039
+ Capture a note into an editable collection with structured provenance.
1040
+
1041
+ **Synopsis:**
1042
+
1043
+ ```bash
1044
+ gno capture [content...] [--stdin|--file <path>] [--collection <name>] [--title <title>] [--path <relPath>] [--folder <relPath>] [--preset <id>] [--tags <tags>] [--collision-policy <policy>] [--source-kind <kind>] [--source-url <url>] [--source-title <title>] [--source-author <author>] [--source-date <date>] [--source-id <id>] [--json]
1045
+ ```
1046
+
1047
+ **Content Sources:**
1048
+
1049
+ - Inline argument, `--stdin`, and `--file` are mutually exclusive.
1050
+ - Content is required unless `--preset` can scaffold a non-empty note.
1051
+ - `--preset` accepts: `blank`, `project-note`, `research-note`,
1052
+ `decision-note`, `prompt-pattern`, `source-summary`, `idea-original`,
1053
+ `person`, `company-project`, `meeting`.
1054
+ - `--json` wins over global `--quiet`; quiet prints only the created/opened URI.
1055
+
1056
+ **Provenance:**
1057
+
1058
+ Capture writes structured `source:` frontmatter and returns the shared
1059
+ [`capture-receipt`](./output-schemas/capture-receipt.schema.json). `--source-date`
1060
+ maps to `source.observedAt`; `--source-id` maps to `source.externalId`.
1061
+
1062
+ **Path and Collision Rules:**
1063
+
1064
+ - Explicit `--path` wins.
1065
+ - Without `--path`, `--folder`/`--title` produce a safe markdown filename.
1066
+ - Without path, folder, or title, GNO writes to
1067
+ `inbox/YYYY-MM-DD/capture-<body-hash>.md` using UTC capture time.
1068
+ - Default collision policy is `open_existing` for generated hash paths and
1069
+ `error` for explicit/title/folder paths.
1070
+ - Collision checks include indexed documents and disk-only files.
1071
+ - Content must be text; NUL or binary-like control bytes are rejected.
1072
+ - Capture writes use exclusive create semantics so a file that appears after
1073
+ planning is not replaced.
1074
+
1075
+ **Examples:**
1076
+
1077
+ ```bash
1078
+ gno capture "thought to remember"
1079
+ gno capture --stdin --collection notes --preset source-summary --tags inbox,gno
1080
+ gno capture --file ./clip.md --source-url https://example.com --source-kind web --json
1081
+ gno capture "meeting note" --quiet
1082
+ ```
1083
+
1084
+ ---
1085
+
1086
+ ### gno get
1087
+
1088
+ Retrieve a single document by reference.
1089
+
1090
+ **Synopsis:**
1091
+
1092
+ ```bash
1093
+ gno get <ref> [--from <line>] [-l <lines>] [--line-numbers] [--trace-id <id>] [--source] [--json|--md]
1094
+ ```
1095
+
1096
+ **Arguments:**
1097
+ | Arg | Type | Description |
1098
+ |-----|------|-------------|
1099
+ | `<ref>` | string | Document reference: `gno://...`, `collection/path`, `#docid`, or `:line` suffix |
1100
+
1101
+ **Options:**
1102
+ | Option | Type | Description |
1103
+ |--------|------|-------------|
1104
+ | `--from` | integer | Start at line number |
1105
+ | `-l` | integer | Limit to N lines |
1106
+ | `--line-numbers` | boolean | Prefix lines with numbers |
1107
+ | `--trace-id` | string | Continue an open retrieval trace and record the exact returned span |
1108
+ | `--source` | boolean | Include source metadata in output |
1109
+
1110
+ **Ref Formats:**
1111
+
1112
+ - `gno://work/contracts/nda.docx` - Full URI
1113
+ - `work/contracts/nda.docx` - Collection-relative path
1114
+ - `#a1b2c3d4` - Document ID
1115
+ - `gno://work/doc.md:120` - URI with line number suffix
1116
+
1117
+ **Output (JSON):**
1118
+ See [Output Schemas](./output-schemas/get.schema.json)
1119
+
1120
+ **Exit Codes:**
1121
+
1122
+ - 0: Success
1123
+ - 1: Invalid ref format
1124
+ - 2: Document not found
1125
+
1126
+ **Examples:**
1127
+
1128
+ ```bash
1129
+ gno get gno://work/contracts/nda.docx
1130
+ gno get "#a1b2c3d4" --line-numbers
1131
+ gno get work/doc.md:120 -l 50
1132
+ gno get gno://work/doc.md --from 120 -l 50 --trace-id <traceId>
1133
+ ```
1134
+
1135
+ ---
1136
+
1137
+ ### gno multi-get
1138
+
1139
+ Retrieve multiple documents by pattern or list.
1140
+
1141
+ **Synopsis:**
1142
+
1143
+ ```bash
1144
+ gno multi-get <pattern-or-list> [--max-bytes <n>] [--line-numbers] [--json|--files|--md]
1145
+ ```
1146
+
1147
+ **Arguments:**
1148
+ | Arg | Type | Description |
1149
+ |-----|------|-------------|
1150
+ | `<pattern-or-list>` | string | Glob pattern, comma-separated refs, or docid list |
1151
+
1152
+ **Options:**
1153
+
1154
+ | Option | Type | Default | Description |
1155
+ | ---------------- | ------- | ------- | ---------------------------------------------- |
1156
+ | `--max-bytes` | integer | 10240 | Max bytes per document (truncate with warning) |
1157
+ | `--line-numbers` | boolean | false | Include line numbers |
1158
+
1159
+ **Output (JSON):**
1160
+ See [Output Schemas](./output-schemas/multi-get.schema.json)
1161
+
1162
+ **Exit Codes:**
1163
+
1164
+ - 0: Success (partial results if some docs missing)
1165
+ - 1: Invalid pattern
1166
+ - 2: DB failure
1167
+
1168
+ ---
1169
+
1170
+ ### gno ls
1171
+
1172
+ List documents in a collection or prefix.
1173
+
1174
+ **Synopsis:**
1175
+
1176
+ ```bash
1177
+ gno ls [<scope>] [--json|--files|--md]
1178
+ ```
1179
+
1180
+ **Arguments:**
1181
+ | Arg | Type | Description |
1182
+ |-----|------|-------------|
1183
+ | `<scope>` | string | Collection name or `gno://collection/prefix` (default: all) |
1184
+
1185
+ **Output (JSON):**
1186
+
1187
+ ```json
1188
+ [
1189
+ {
1190
+ "docid": "#a1b2c3d4",
1191
+ "uri": "gno://work/doc.md",
1192
+ "title": "Document Title",
1193
+ "source": { "relPath": "doc.md", "mime": "text/markdown", "ext": ".md" }
1194
+ }
1195
+ ]
1196
+ ```
1197
+
1198
+ **Exit Codes:**
1199
+
1200
+ - 0: Success
1201
+ - 1: Invalid scope
1202
+ - 2: DB failure
1203
+
1204
+ ---
1205
+
1206
+ ### gno context add
1207
+
1208
+ Add context metadata for a scope.
1209
+
1210
+ Configured text is returned as optional `context` on matching structured
1211
+ retrieval results and is used as trusted guidance during grounded answer
1212
+ generation. Matching scopes compose once in this order: global, collection,
1213
+ then path prefixes from broadest to most specific. Context guides interpretation;
1214
+ it is not searched and does not change ranking.
1215
+
1216
+ **Synopsis:**
1217
+
1218
+ ```bash
1219
+ gno context add <scope> "<text>"
1220
+ ```
1221
+
1222
+ **Arguments:**
1223
+ | Arg | Type | Description |
1224
+ |-----|------|-------------|
1225
+ | `<scope>` | string | `/` (global), `collection:` prefix, or `gno://collection/prefix` |
1226
+ | `<text>` | string | Context description text |
1227
+
1228
+ **Exit Codes:**
1229
+
1230
+ - 0: Success
1231
+ - 1: Invalid scope format
1232
+
1233
+ **Examples:**
1234
+
1235
+ ```bash
1236
+ gno context add / "Corporate knowledge base"
1237
+ gno context add work: "Work documents and contracts"
1238
+ gno context add gno://work/contracts "Legal contracts and NDAs"
1239
+ ```
1240
+
1241
+ ---
1242
+
1243
+ ### gno context list
1244
+
1245
+ List all configured contexts.
1246
+
1247
+ **Synopsis:**
1248
+
1249
+ ```bash
1250
+ gno context list [--json|--md]
1251
+ ```
1252
+
1253
+ **Output (JSON):**
1254
+
1255
+ ```json
1256
+ [
1257
+ { "scope": "/", "text": "Corporate knowledge base" },
1258
+ { "scope": "work:", "text": "Work documents" }
1259
+ ]
1260
+ ```
1261
+
1262
+ ---
1263
+
1264
+ ### gno context check
1265
+
1266
+ Validate context configuration.
1267
+
1268
+ **Synopsis:**
1269
+
1270
+ ```bash
1271
+ gno context check [--json|--md]
1272
+ ```
1273
+
1274
+ **Output (JSON):**
1275
+
1276
+ ```json
1277
+ {
1278
+ "valid": true,
1279
+ "warnings": [],
1280
+ "errors": []
1281
+ }
1282
+ ```
1283
+
1284
+ ---
1285
+
1286
+ ### gno context rm
1287
+
1288
+ Remove a context.
1289
+
1290
+ **Synopsis:**
1291
+
1292
+ ```bash
1293
+ gno context rm <scope>
1294
+ ```
1295
+
1296
+ **Exit Codes:**
1297
+
1298
+ - 0: Success
1299
+ - 1: Scope not found
1300
+
1301
+ ---
1302
+
1303
+ ### gno context build
1304
+
1305
+ Compile exact indexed evidence into a deterministic Context Capsule. The
1306
+ Capsule is returned only on stdout or at an explicitly requested output path;
1307
+ the command never persists Capsules implicitly. Model and download progress is
1308
+ written to stderr.
1309
+
1310
+ **Synopsis:**
1311
+
1312
+ ```bash
1313
+ gno context build "<goal>" --budget <tokens> [--collection <name>] [--fast|--thorough] [--json|--md] [--output <file>]
1314
+ ```
1315
+
1316
+ `--budget` is the global token ceiling. `--bytes` optionally sets a separate
1317
+ byte ceiling; otherwise it is four times the token request. Without an active
1318
+ token counter, `usedTokens` uses the conservative UTF-8 byte count and the
1319
+ Capsule records `tokenizer_unavailable`. `--query`, `--uri-prefix`, tag,
1320
+ category, author, language, date, and repeatable `--query-mode` filters use the
1321
+ same canonical retrieval semantics as `gno query`. `--collection` is
1322
+ repeatable. Tag filters are NFC-normalized, lowercased, deduplicated, and
1323
+ validated before retrieval. Result and candidate limits are global across
1324
+ repeated collections: the merged result pool is capped once, while candidate
1325
+ work is distributed deterministically in canonical collection order.
1326
+
1327
+ JSON is the canonical V1 payload. Markdown is a readable projection of that
1328
+ same payload and hard-delimits each untrusted evidence passage. Passage,
1329
+ metadata, manifest, and verification-receipt blocks use deterministic
1330
+ collision-resistant Markdown fences: the fence character and width are derived
1331
+ from the complete block, so indexed text cannot forge a closing boundary.
1332
+ Indexed title, heading, and configured-context metadata remains JSON-escaped;
1333
+ exact passage bytes remain unchanged inside the fence. Budgets, normalized
1334
+ retrieval requests, capability attempts/outcomes, fingerprints, snapshots,
1335
+ fallbacks, omissions, and truncation remain auditable.
1336
+ An enabled retrieval trace links the request to `capsuleId` in local trace
1337
+ storage and returns its random identity only on stderr. The trace identity is
1338
+ never added to the canonical Capsule, its budget, or its deterministic ID.
1339
+ Invalid goals,
1340
+ budgets, filters, URI/index combinations, or output paths exit 1. Snapshot,
1341
+ retrieval, provenance, and store failures exit 2 with no partial Capsule.
1342
+ Requested collections must exist in the active configuration before retrieval.
1343
+
1344
+ ### gno context verify
1345
+
1346
+ Verify a saved canonical JSON Capsule without rebuilding or mutating it.
1347
+
1348
+ **Synopsis:**
1349
+
1350
+ ```bash
1351
+ gno context verify <file|-> [--json|--md] [--output <file>]
1352
+ ```
1353
+
1354
+ `-` reads stdin. Verification re-resolves exact source, mirror, chunk, passage,
1355
+ and index state. Without a live rank resolver, ranking is reported as
1356
+ `ranking_unavailable`; stale or missing evidence is never reported as ranked.
1357
+ JSON uses the canonical verification schema. Markdown projects the same receipt,
1358
+ including fingerprint drift and every available current hash. Non-canonical
1359
+ metadata and invalid identity/budget data fail before the store is read.
1360
+ When global `--index` is omitted, the Capsule scope selects the index. An
1361
+ explicit global `--index` must match the Capsule scope; mismatch fails before a
1362
+ store is opened. Active-tokenizer Capsules require the matching tokenizer
1363
+ fingerprint and deterministic recount callback before any store read; CLI
1364
+ runtimes without that tokenizer fail with `tokenizer_unavailable` rather than
1365
+ trusting saved `usedTokens`.
1366
+
1367
+ ---
1368
+
1369
+ ### gno models list
1370
+
1371
+ List configured and available models.
1372
+
1373
+ **Synopsis:**
1374
+
1375
+ ```bash
1376
+ gno models list [--json|--md]
1377
+ ```
1378
+
1379
+ **Output (JSON):**
1380
+
1381
+ ```json
1382
+ {
1383
+ "activePreset": "slim",
1384
+ "presets": [
1385
+ { "id": "slim", "name": "Slim (Default, ~1GB)", "active": true },
1386
+ { "id": "balanced", "name": "Balanced (~2GB)", "active": false },
1387
+ {
1388
+ "id": "quality",
1389
+ "name": "Quality (Best Answers, ~2.5GB)",
1390
+ "active": false
1391
+ }
1392
+ ],
1393
+ "embed": {
1394
+ "uri": "hf:gpustack/bge-m3-GGUF/bge-m3-Q4_K_M.gguf",
1395
+ "cached": true
1396
+ },
1397
+ "rerank": {
1398
+ "uri": "hf:gpustack/bge-reranker-v2-m3-GGUF/bge-reranker-v2-m3-Q4_K_M.gguf",
1399
+ "cached": false
1400
+ },
1401
+ "gen": {
1402
+ "uri": "hf:unsloth/Qwen3-1.7B-GGUF/Qwen3-1.7B-Q4_K_M.gguf",
1403
+ "cached": true
1404
+ }
1405
+ }
1406
+ ```
1407
+
1408
+ ---
1409
+
1410
+ ### gno models use
1411
+
1412
+ Switch active model preset.
1413
+
1414
+ **Synopsis:**
1415
+
1416
+ ```bash
1417
+ gno models use <preset>
1418
+ ```
1419
+
1420
+ **Arguments:**
1421
+ | Arg | Type | Description |
1422
+ |-----|------|-------------|
1423
+ | `<preset>` | string | Preset ID: `slim`, `balanced`, or `quality` |
1424
+
1425
+ **Presets:**
1426
+ | ID | Gen Model | RAM | Use Case |
1427
+ |----|-----------|-----|----------|
1428
+ | `slim` | Qwen3-1.7B | ~1GB | Default, fast queries |
1429
+ | `balanced` | Qwen2.5-3B-Instruct | ~2GB | Slightly larger model |
1430
+ | `quality` | Qwen3-4B-Instruct | ~2.5GB | Best grounded answers |
1431
+
1432
+ **Exit Codes:**
1433
+
1434
+ - 0: Success
1435
+ - 1: Unknown preset
1436
+
1437
+ **Behavior note:**
1438
+
1439
+ - if the preset switch changes the active embedding model, terminal output should
1440
+ tell the user to run `gno embed`
1441
+
1442
+ ---
1443
+
1444
+ ### gno models pull
1445
+
1446
+ Download models to local cache.
1447
+
1448
+ **Synopsis:**
1449
+
1450
+ ```bash
1451
+ gno models pull [--all|--embed|--rerank|--gen] [--force]
1452
+ ```
1453
+
1454
+ **Options:**
1455
+ | Option | Description |
1456
+ |--------|-------------|
1457
+ | `--all` | Pull all configured models |
1458
+ | `--embed` | Pull embedding model only |
1459
+ | `--rerank` | Pull reranker model only |
1460
+ | `--gen` | Pull generation model only |
1461
+ | `--force` | Re-download even if already cached |
1462
+
1463
+ **Behavior:**
1464
+
1465
+ - Skips models that are already cached (checksum match) unless `--force` is used
1466
+ - Default (no flags): pulls all models
1467
+
1468
+ **Exit Codes:**
1469
+
1470
+ - 0: Success
1471
+ - 2: Download failure
1472
+
1473
+ ---
1474
+
1475
+ ### gno models clear
1476
+
1477
+ Remove cached models.
1478
+
1479
+ **Synopsis:**
1480
+
1481
+ ```bash
1482
+ gno models clear [--all|--embed|--rerank|--gen]
1483
+ ```
1484
+
1485
+ ---
1486
+
1487
+ ### gno models path
1488
+
1489
+ Print model cache directory.
1490
+
1491
+ **Synopsis:**
1492
+
1493
+ ```bash
1494
+ gno models path [--json]
1495
+ ```
1496
+
1497
+ **Output:**
1498
+
1499
+ ```
1500
+ /Users/user/Library/Caches/gno/models
1501
+ ```
1502
+
1503
+ ---
1504
+
1505
+ ### gno cleanup
1506
+
1507
+ Remove orphaned content, chunks, and vectors not referenced by active documents.
1508
+
1509
+ **Synopsis:**
1510
+
1511
+ ```bash
1512
+ gno cleanup
1513
+ ```
1514
+
1515
+ **Exit Codes:**
1516
+
1517
+ - 0: Success
1518
+ - 2: DB failure
1519
+
1520
+ ---
1521
+
1522
+ ### gno doctor
1523
+
1524
+ Diagnose configuration and dependencies.
1525
+
1526
+ **Synopsis:**
1527
+
1528
+ ```bash
1529
+ gno doctor [--json|--md]
1530
+ ```
1531
+
1532
+ **Output (JSON):**
1533
+
1534
+ ```json
1535
+ {
1536
+ "healthy": true,
1537
+ "checks": [
1538
+ {
1539
+ "name": "config",
1540
+ "status": "ok",
1541
+ "message": "Config loaded: ~/.config/gno/config.yaml"
1542
+ },
1543
+ {
1544
+ "name": "database",
1545
+ "status": "ok",
1546
+ "message": "Database found: ~/.local/share/gno/index.db"
1547
+ },
1548
+ { "name": "embed-model", "status": "ok", "message": "embed model cached" },
1549
+ {
1550
+ "name": "rerank-model",
1551
+ "status": "warn",
1552
+ "message": "rerank model not cached. Run: gno models pull --rerank"
1553
+ },
1554
+ { "name": "gen-model", "status": "ok", "message": "gen model cached" },
1555
+ {
1556
+ "name": "node-llama-cpp",
1557
+ "status": "ok",
1558
+ "message": "node-llama-cpp loaded successfully"
1559
+ },
1560
+ {
1561
+ "name": "embedding-fingerprint",
1562
+ "status": "warn",
1563
+ "message": "current abc123def456, 12 pending/stale, 3 legacy, 2 groups",
1564
+ "details": [
1565
+ "Run: gno embed",
1566
+ "If vectors still look stale, run: gno embed --force"
1567
+ ],
1568
+ "embeddingFingerprint": {
1569
+ "model": "hf:Qwen/Qwen3-Embedding-0.6B-GGUF:Qwen3-Embedding-0.6B-Q8_0.gguf",
1570
+ "currentFingerprint": "abc123def4567890",
1571
+ "pendingChunks": 12,
1572
+ "legacyChunks": 3,
1573
+ "mixedGroups": 2,
1574
+ "groups": [
1575
+ {
1576
+ "model": "hf:Qwen/Qwen3-Embedding-0.6B-GGUF:Qwen3-Embedding-0.6B-Q8_0.gguf",
1577
+ "fingerprint": "abc123def4567890",
1578
+ "count": 42,
1579
+ "current": true,
1580
+ "legacy": false
1581
+ }
1582
+ ]
1583
+ }
1584
+ }
1585
+ ]
1586
+ }
1587
+ ```
1588
+
1589
+ The `embedding-fingerprint` check is additive doctor-only diagnostics. It uses
1590
+ the active embed model and stored vector dimensions to report the current
1591
+ freshness fingerprint, pending/stale chunks, legacy empty-fingerprint vectors,
1592
+ and stored fingerprint groups. Stale, legacy, and mixed groups are warnings;
1593
+ recover with `gno embed`, or `gno embed --force` if vectors still look stale.
1594
+
1595
+ The additive `activation` object uses the same contract as `gno status` and
1596
+ `GET /api/status`. Doctor performs only the local lexical proof. It never starts
1597
+ connector children or initializes/downloads models. A failed lexical proof adds
1598
+ the `retrieval-activation` error check and exits 2 after writing the complete
1599
+ result; no duplicate error is written to stderr. Connector failure or projection
1600
+ truncation adds a warning and makes the structured doctor result non-healthy,
1601
+ but preserves exit 0 when lexical proof and all other required checks pass. An
1602
+ omitted target/collection pair has no inferred result.
1603
+
1604
+ **Exit Codes:**
1605
+
1606
+ - 0: All checks pass or only warnings
1607
+ - 2: Critical checks failed
1608
+
1609
+ ---
1610
+
1611
+ ### gno mcp
1612
+
1613
+ Start MCP server over stdio.
1614
+
1615
+ **Synopsis:**
1616
+
1617
+ ```bash
1618
+ gno mcp
1619
+ ```
1620
+
1621
+ **Behavior:**
1622
+
1623
+ - Starts JSON-RPC 2.0 MCP server on stdin/stdout
1624
+ - Keeps DB open for server lifetime
1625
+ - See [MCP Specification](./mcp.md) for protocol details
1626
+
1627
+ **Exit Codes:**
1628
+
1629
+ - 0: Clean shutdown
1630
+ - 2: Initialization failure
1631
+
1632
+ ---
1633
+
1634
+ ### gno mcp install
1635
+
1636
+ Install gno as an MCP server in client configurations.
1637
+
1638
+ **Synopsis:**
1639
+
1640
+ ```bash
1641
+ gno mcp install [--target <target>] [--scope <scope>] [--force] [--dry-run] [--json]
1642
+ ```
1643
+
1644
+ **Options:**
1645
+
1646
+ | Option | Type | Default | Description |
1647
+ | ----------- | ------- | -------------- | --------------------------------------------------------- |
1648
+ | `--target` | string | claude-desktop | Target client (see table below) |
1649
+ | `--scope` | string | target default | Scope: `user` or `project`; LibreChat defaults to project |
1650
+ | `--force` | boolean | false | Overwrite existing gno configuration |
1651
+ | `--dry-run` | boolean | false | Show what would be done without changes |
1652
+
1653
+ **Targets:**
1654
+
1655
+ | Value | Description | Project Scope |
1656
+ | ---------------- | ---------------------------- | ------------------ |
1657
+ | `claude-desktop` | Claude Desktop app (default) | No |
1658
+ | `claude-code` | Claude Code CLI | Yes |
1659
+ | `codex` | OpenAI Codex CLI | Yes |
1660
+ | `cursor` | Cursor editor | Yes |
1661
+ | `zed` | Zed editor | No |
1662
+ | `windsurf` | Windsurf IDE | No |
1663
+ | `opencode` | OpenCode CLI | Yes |
1664
+ | `amp` | Amp (Sourcegraph) | No |
1665
+ | `lmstudio` | LM Studio | No |
1666
+ | `librechat` | LibreChat | Yes (project only) |
1667
+
1668
+ **Config Locations:**
1669
+
1670
+ | Target | Scope | macOS | Windows | Linux |
1671
+ | -------------- | ------- | ----------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------- |
1672
+ | claude-desktop | user | `~/Library/Application Support/Claude/claude_desktop_config.json` | `%APPDATA%\Claude\claude_desktop_config.json` | `~/.config/Claude/claude_desktop_config.json` |
1673
+ | claude-code | user | `~/.claude.json` | `~/.claude.json` | `~/.claude.json` |
1674
+ | claude-code | project | `./.mcp.json` | `./.mcp.json` | `./.mcp.json` |
1675
+ | codex | user | `~/.codex/config.toml` | `~/.codex/config.toml` | `~/.codex/config.toml` |
1676
+ | codex | project | `./.codex/config.toml` | `./.codex/config.toml` | `./.codex/config.toml` |
1677
+ | cursor | user | `~/.cursor/mcp.json` | `~/.cursor/mcp.json` | `~/.cursor/mcp.json` |
1678
+ | cursor | project | `./.cursor/mcp.json` | `./.cursor/mcp.json` | `./.cursor/mcp.json` |
1679
+ | zed | user | `~/.config/zed/settings.json` | `%APPDATA%\Zed\settings.json` | `~/.config/zed/settings.json` |
1680
+ | windsurf | user | `~/.codeium/windsurf/mcp_config.json` | `~/.codeium/windsurf/mcp_config.json` | `~/.codeium/windsurf/mcp_config.json` |
1681
+ | opencode | user | `~/.config/opencode/opencode.json` | `~/.config/opencode/opencode.json` | `~/.config/opencode/opencode.json` |
1682
+ | opencode | project | `./opencode.json` | `./opencode.json` | `./opencode.json` |
1683
+ | amp | user | `~/.config/amp/settings.json` | `~/.config/amp/settings.json` | `~/.config/amp/settings.json` |
1684
+ | lmstudio | user | `~/.lmstudio/mcp.json` | `~/.lmstudio/mcp.json` | `~/.lmstudio/mcp.json` |
1685
+ | librechat | project | `./librechat.yaml` | `./librechat.yaml` | `./librechat.yaml` |
1686
+
1687
+ **Config Formats:**
1688
+
1689
+ - JSONC-compatible (`mcpServers` key): Claude Desktop, Claude Code, Cursor, Windsurf, LM Studio
1690
+ - Standard YAML (`mcpServers` key): LibreChat
1691
+ - Codex TOML: `[mcp_servers.gno]` plus `[mcp_servers.gno.env]`
1692
+ - Zed: `context_servers` key
1693
+ - OpenCode: `mcp` key with array command format
1694
+ - Amp: `amp.mcpServers` key
1695
+
1696
+ JSON/JSONC edits preserve comments, trailing commas, and unrelated layout.
1697
+ OpenCode and Amp discover an existing `.jsonc` alternate instead of creating a
1698
+ duplicate canonical `.json` file.
1699
+
1700
+ `--dry-run --json` reports the normalized command, arguments, and workspace
1701
+ environment, not the target's persisted wrapper shape. Previewing replacement
1702
+ of an existing `gno` entry requires `--force --dry-run --json`; no file is
1703
+ written in dry-run mode.
1704
+
1705
+ **Behavior:**
1706
+
1707
+ 1. Resolves the active index and validates it with the shared index-name contract
1708
+ 2. Resolves the active explicit, environment-selected, or default config to an absolute path
1709
+ 3. Builds an absolute command using the current Bun executable, `run`, and the current package's `src/index.ts`
1710
+ 4. Appends `--index <active> --config <absolute> mcp` (`--enable-write` follows `mcp` when requested)
1711
+ 5. Resolves absolute `GNO_DATA_DIR` and `GNO_CACHE_DIR` values for the active workspace
1712
+ 6. Reads existing config (creates if missing)
1713
+ 7. Adds the format-specific `gno` server entry, using `env` for standard/Codex/YAML entries and `environment` for OpenCode
1714
+ 8. Creates a backup before modifying
1715
+ 9. Writes atomically via temp file + rename
1716
+
1717
+ The persisted index, config, data directory, and cache directory are workspace
1718
+ identity, not display metadata. They make the installed GUI client deterministic
1719
+ even when it has a different `PATH` or does not inherit `GNO_*` variables. Only
1720
+ the two audited absolute-path environment keys are persisted; status and
1721
+ activation reject other environment keys or invalid values.
1722
+
1723
+ **Output (JSON):**
1724
+
1725
+ ```json
1726
+ {
1727
+ "installed": {
1728
+ "target": "claude-desktop",
1729
+ "scope": "user",
1730
+ "configPath": "~/Library/Application Support/Claude/claude_desktop_config.json",
1731
+ "action": "created",
1732
+ "serverEntry": {
1733
+ "command": "/path/to/bun",
1734
+ "args": [
1735
+ "run",
1736
+ "/path/to/@gmickel/gno/src/index.ts",
1737
+ "--index",
1738
+ "default",
1739
+ "--config",
1740
+ "/absolute/path/to/index.yml",
1741
+ "mcp"
1742
+ ],
1743
+ "env": {
1744
+ "GNO_DATA_DIR": "/absolute/path/to/data",
1745
+ "GNO_CACHE_DIR": "/absolute/path/to/cache"
1746
+ }
1747
+ }
1748
+ }
1749
+ }
1750
+ ```
1751
+
1752
+ **Exit Codes:**
1753
+
1754
+ - 0: Success
1755
+ - 1: Already configured (without --force), invalid scope for target, invalid index name
1756
+ - 2: Bun not found, gno not found, IO failure
1757
+
1758
+ **Examples:**
1759
+
1760
+ ```bash
1761
+ # Install for Claude Desktop (default)
1762
+ gno mcp install
1763
+
1764
+ # Install for Cursor
1765
+ gno mcp install --target cursor
1766
+
1767
+ # Install for Zed
1768
+ gno mcp install --target zed
1769
+
1770
+ # Install for Claude Code (project scope)
1771
+ gno mcp install --target claude-code --scope project
1772
+
1773
+ # Force overwrite
1774
+ gno mcp install --force
1775
+
1776
+ # Preview changes
1777
+ gno mcp install --dry-run
1778
+ ```
1779
+
1780
+ ---
1781
+
1782
+ ### gno mcp uninstall
1783
+
1784
+ Remove gno MCP server from client configurations.
1785
+
1786
+ **Synopsis:**
1787
+
1788
+ ```bash
1789
+ gno mcp uninstall [--target <target>] [--scope <scope>] [--json]
1790
+ ```
1791
+
1792
+ **Options:**
1793
+
1794
+ | Option | Type | Default | Description |
1795
+ | ---------- | ------ | -------------- | ------------------------------------ |
1796
+ | `--target` | string | claude-desktop | Target client |
1797
+ | `--scope` | string | target default | Scope; LibreChat defaults to project |
1798
+
1799
+ **Behavior:**
1800
+
1801
+ 1. Reads existing config
1802
+ 2. Removes the format-specific GNO entry if present (`mcpServers.gno`,
1803
+ `context_servers.gno`, `mcp.gno`, `amp.mcpServers.gno`, or Codex's
1804
+ `[mcp_servers.gno]` plus `[mcp_servers.gno.env]` tables)
1805
+ 3. Creates backup before modifying
1806
+ 4. Removes an empty format-specific server object; Codex preserves unrelated
1807
+ TOML and comments byte-for-byte apart from necessary surrounding whitespace
1808
+ 5. Preserves other entries
1809
+
1810
+ **Output (JSON):**
1811
+
1812
+ ```json
1813
+ {
1814
+ "uninstalled": {
1815
+ "target": "claude-desktop",
1816
+ "scope": "user",
1817
+ "configPath": "~/Library/Application Support/Claude/claude_desktop_config.json",
1818
+ "action": "removed"
1819
+ }
1820
+ }
1821
+ ```
1822
+
1823
+ **Exit Codes:**
1824
+
1825
+ - 0: Success (including if not configured)
1826
+ - 1: Invalid scope for target
1827
+ - 2: IO failure
1828
+
1829
+ ---
1830
+
1831
+ ### gno mcp status
1832
+
1833
+ Show MCP server installation status across all targets.
1834
+
1835
+ **Synopsis:**
1836
+
1837
+ ```bash
1838
+ gno mcp status [--target <target>] [--scope <scope>] [--json]
1839
+ ```
1840
+
1841
+ **Options:**
1842
+
1843
+ | Option | Type | Default | Description |
1844
+ | ---------- | ------ | ------- | --------------------------- |
1845
+ | `--target` | string | all | Filter by target (or `all`) |
1846
+ | `--scope` | string | all | Filter by scope (or `all`) |
1847
+
1848
+ **Output (Terminal, abbreviated; unfiltered status enumerates 14 target/scope
1849
+ pairs):**
1850
+
1851
+ ```text
1852
+ MCP Server Status
1853
+ ──────────────────────────────────────────────────
1854
+
1855
+ ✓ Claude Desktop: configured
1856
+ Command: /path/to/bun
1857
+ Args: run /path/to/@gmickel/gno/src/index.ts --index default --config /absolute/path/to/index.yml mcp
1858
+ Config: ~/Library/Application Support/Claude/claude_desktop_config.json
1859
+
1860
+ ✗ Claude Code: not configured
1861
+ Config: ~/.claude.json
1862
+
1863
+ ✗ Claude Code (project): not configured
1864
+ Config: ./.mcp.json
1865
+
1866
+ 1/14 targets configured
1867
+ ```
1868
+
1869
+ **Output (JSON):**
1870
+
1871
+ ```json
1872
+ {
1873
+ "targets": [
1874
+ {
1875
+ "target": "claude-desktop",
1876
+ "scope": "user",
1877
+ "configPath": "~/Library/Application Support/Claude/claude_desktop_config.json",
1878
+ "configured": true,
1879
+ "serverEntry": {
1880
+ "command": "/path/to/bun",
1881
+ "args": [
1882
+ "run",
1883
+ "/path/to/@gmickel/gno/src/index.ts",
1884
+ "--index",
1885
+ "default",
1886
+ "--config",
1887
+ "/absolute/path/to/index.yml",
1888
+ "mcp"
1889
+ ],
1890
+ "env": {
1891
+ "GNO_DATA_DIR": "/absolute/path/to/data",
1892
+ "GNO_CACHE_DIR": "/absolute/path/to/cache"
1893
+ }
1894
+ }
1895
+ },
1896
+ {
1897
+ "target": "claude-code",
1898
+ "scope": "user",
1899
+ "configPath": "~/.claude.json",
1900
+ "configured": false
1901
+ }
1902
+ ],
1903
+ "summary": { "configured": 1, "total": 14 }
1904
+ }
1905
+ ```
1906
+
1907
+ **Exit Codes:**
1908
+
1909
+ - 0: Success
1910
+ - 1: Invalid target or scope
1911
+ - 2: IO failure
1912
+
1913
+ ---
1914
+
1915
+ ### gno skill install
1916
+
1917
+ Install GNO agent skill for Claude Code, Codex, OpenCode, OpenClaw, or Hermes.
1918
+
1919
+ **Synopsis:**
1920
+
1921
+ ```bash
1922
+ gno skill install [--scope <project|user>] [--target <claude|codex|opencode|openclaw|hermes|all>] [--force] [--json]
1923
+ ```
1924
+
1925
+ **Options:**
1926
+
1927
+ | Option | Type | Default | Description |
1928
+ | ---------- | ------- | ------- | ------------------------------------------------------------- |
1929
+ | `--scope` | string | project | `project` (.claude/skills/) or `user` (~/.claude/skills/) |
1930
+ | `--target` | string | claude | `claude`, `codex`, `opencode`, `openclaw`, `hermes`, or `all` |
1931
+ | `--force` | boolean | false | Overwrite existing skill without prompting |
1932
+
1933
+ **Behavior:**
1934
+
1935
+ 1. Resolves target path based on scope and target
1936
+ 2. If skill exists and not `--force`/`--yes`: error
1937
+ 3. Atomically installs skill directory (temp + rename)
1938
+ 4. Copies SKILL.md, reference files, and nested recipe files
1939
+
1940
+ **Output (JSON):**
1941
+
1942
+ ```json
1943
+ {
1944
+ "installed": [
1945
+ { "target": "claude", "scope": "project", "path": ".claude/skills/gno" }
1946
+ ]
1947
+ }
1948
+ ```
1949
+
1950
+ **Exit Codes:**
1951
+
1952
+ - 0: Success
1953
+ - 1: Skill already exists (without --force)
1954
+ - 2: IO failure
1955
+
1956
+ **Examples:**
1957
+
1958
+ ```bash
1959
+ # Install to current project for Claude Code
1960
+ gno skill install
1961
+
1962
+ # Install globally for all agents
1963
+ gno skill install --scope user --target all
1964
+
1965
+ # Force reinstall
1966
+ gno skill install --force
1967
+ ```
1968
+
1969
+ ---
1970
+
1971
+ ### gno skill uninstall
1972
+
1973
+ Remove GNO agent skill.
1974
+
1975
+ **Synopsis:**
1976
+
1977
+ ```bash
1978
+ gno skill uninstall [--scope <project|user>] [--target <claude|codex|opencode|openclaw|hermes|all>] [--json]
1979
+ ```
1980
+
1981
+ **Options:** Same as `skill install` (except `--force`)
1982
+
1983
+ **Safety Checks:**
1984
+
1985
+ - Validates path ends with `/skills/gno` before removal
1986
+ - Rejects paths that don't match expected structure
1987
+ - Uses atomic removal with retry for Windows compatibility
1988
+
1989
+ **Output (JSON):**
1990
+
1991
+ ```json
1992
+ {
1993
+ "uninstalled": [
1994
+ { "target": "claude", "scope": "project", "path": ".claude/skills/gno" }
1995
+ ]
1996
+ }
1997
+ ```
1998
+
1999
+ **Exit Codes:**
2000
+
2001
+ - 0: Success
2002
+ - 1: Skill not found
2003
+ - 2: IO failure or safety check failed
2004
+
2005
+ ---
2006
+
2007
+ ### gno skill show
2008
+
2009
+ Preview skill files without installing.
2010
+
2011
+ **Synopsis:**
2012
+
2013
+ ```bash
2014
+ gno skill show [--file <relative-md-path>] [--all]
2015
+ ```
2016
+
2017
+ **Options:**
2018
+
2019
+ | Option | Type | Default | Description |
2020
+ | -------- | ------- | -------- | ------------------------------------------------------------------------------------------------- |
2021
+ | `--file` | string | SKILL.md | Relative POSIX markdown path to show, including nested paths like `recipes/brain-first-lookup.md` |
2022
+ | `--all` | boolean | false | Show all skill markdown files with separators |
2023
+
2024
+ **Behavior:**
2025
+
2026
+ - Outputs file content to stdout
2027
+ - Lists available files at end
2028
+ - Recursively lists bundled markdown files under the skill asset directory
2029
+ - Rejects absolute paths, `..`, backslashes, and non-markdown file paths
2030
+
2031
+ **Exit Codes:**
2032
+
2033
+ - 0: Success
2034
+ - 1: Invalid file name
2035
+
2036
+ **Examples:**
2037
+
2038
+ ```bash
2039
+ gno skill show
2040
+ gno skill show --file cli-reference.md
2041
+ gno skill show --file recipes/brain-first-lookup.md
2042
+ gno skill show --all
2043
+ ```
2044
+
2045
+ ---
2046
+
2047
+ ### gno skill paths
2048
+
2049
+ Show resolved skill installation paths.
2050
+
2051
+ **Synopsis:**
2052
+
2053
+ ```bash
2054
+ gno skill paths [--scope <project|user>] [--target <claude|codex|opencode|openclaw|hermes|all>] [--json]
2055
+ ```
2056
+
2057
+ **Options:** Same as `skill install`
2058
+
2059
+ **Output (JSON):**
2060
+
2061
+ ```json
2062
+ {
2063
+ "paths": [
2064
+ {
2065
+ "target": "claude",
2066
+ "scope": "project",
2067
+ "path": "/path/to/.claude/skills/gno",
2068
+ "exists": false
2069
+ },
2070
+ {
2071
+ "target": "claude",
2072
+ "scope": "user",
2073
+ "path": "/home/user/.claude/skills/gno",
2074
+ "exists": true
2075
+ }
2076
+ ]
2077
+ }
2078
+ ```
2079
+
2080
+ **Exit Codes:**
2081
+
2082
+ - 0: Success
2083
+
2084
+ ---
2085
+
2086
+ ### gno tags list
2087
+
2088
+ List all tags with document counts.
2089
+
2090
+ **Synopsis:**
2091
+
2092
+ ```bash
2093
+ gno tags [list] [-c, --collection <name>] [--prefix <prefix>] [--json] [--md]
2094
+ ```
2095
+
2096
+ **Options:**
2097
+
2098
+ | Option | Type | Description |
2099
+ | ------------------ | ------ | ------------------------- |
2100
+ | `-c, --collection` | string | Filter by collection name |
2101
+ | `--prefix` | string | Filter by tag prefix |
2102
+ | `--json` | flag | JSON output |
2103
+ | `--md` | flag | Markdown output |
2104
+
2105
+ **Output (JSON):**
2106
+
2107
+ ```json
2108
+ {
2109
+ "tags": [
2110
+ { "tag": "javascript", "count": 15 },
2111
+ { "tag": "python", "count": 8 }
2112
+ ],
2113
+ "meta": {
2114
+ "total": 25,
2115
+ "collection": "notes",
2116
+ "prefix": "java"
2117
+ }
2118
+ }
2119
+ ```
2120
+
2121
+ **Exit Codes:**
2122
+
2123
+ - 0: Success
2124
+
2125
+ ---
2126
+
2127
+ ### gno tags add
2128
+
2129
+ Add a tag to a document.
2130
+
2131
+ **Synopsis:**
2132
+
2133
+ ```bash
2134
+ gno tags add <doc> <tag> [--json]
2135
+ ```
2136
+
2137
+ **Arguments:**
2138
+
2139
+ - `<doc>` - Document reference (docid or URI)
2140
+ - `<tag>` - Tag to add (normalized to lowercase)
2141
+
2142
+ **Options:**
2143
+
2144
+ | Option | Type | Description |
2145
+ | -------- | ---- | ----------- |
2146
+ | `--json` | flag | JSON output |
2147
+
2148
+ **Behavior:**
2149
+
2150
+ - Validates tag format (lowercase alphanumeric with hyphens/dots/slashes)
2151
+ - Adds tag to document in database with source='user'
2152
+ - For markdown files, also updates frontmatter tags
2153
+ - Idempotent: succeeds if tag already exists
2154
+
2155
+ **Output (JSON):**
2156
+
2157
+ ```json
2158
+ {
2159
+ "docid": "abc123",
2160
+ "tag": "javascript",
2161
+ "wroteToFile": true
2162
+ }
2163
+ ```
2164
+
2165
+ **Exit Codes:**
2166
+
2167
+ - 0: Success
2168
+ - 1: Invalid tag format or document not found
2169
+
2170
+ ---
2171
+
2172
+ ### gno tags rm
2173
+
2174
+ Remove a tag from a document.
2175
+
2176
+ **Synopsis:**
2177
+
2178
+ ```bash
2179
+ gno tags rm <doc> <tag> [--json]
2180
+ ```
2181
+
2182
+ **Arguments:**
2183
+
2184
+ - `<doc>` - Document reference (docid or URI)
2185
+ - `<tag>` - Tag to remove
2186
+
2187
+ **Options:**
2188
+
2189
+ | Option | Type | Description |
2190
+ | -------- | ---- | ----------- |
2191
+ | `--json` | flag | JSON output |
2192
+
2193
+ **Behavior:**
2194
+
2195
+ - Removes tag from document in database
2196
+ - For markdown files with frontmatter tags, also updates the file
2197
+
2198
+ **Output (JSON):**
2199
+
2200
+ ```json
2201
+ {
2202
+ "docid": "abc123",
2203
+ "tag": "javascript",
2204
+ "removedFromFile": true
2205
+ }
2206
+ ```
2207
+
2208
+ **Exit Codes:**
2209
+
2210
+ - 0: Success
2211
+ - 1: Tag not found on document or document not found
2212
+
2213
+ ---
2214
+
2215
+ ### gno links list
2216
+
2217
+ List outgoing links from a document.
2218
+
2219
+ **Synopsis:**
2220
+
2221
+ ```bash
2222
+ gno links [list] <doc> [--type <wiki|markdown>] [--edge-type <type>] [--relation <type>] [--json] [--md]
2223
+ ```
2224
+
2225
+ **Arguments:**
2226
+
2227
+ | Argument | Description |
2228
+ | -------- | ---------------------------------------- |
2229
+ | `<doc>` | Document reference (docid, URI, or path) |
2230
+
2231
+ **Options:**
2232
+
2233
+ | Flag | Type | Description |
2234
+ | ------------- | ------ | --------------------------------- |
2235
+ | `--type` | string | Filter positional links by syntax |
2236
+ | `--edge-type` | string | Filter semantic edges by type |
2237
+ | `--relation` | string | Alias for `--edge-type` |
2238
+ | `--json` | flag | JSON output |
2239
+ | `--md` | flag | Markdown output |
2240
+
2241
+ **Behavior:**
2242
+
2243
+ - Lists all outgoing links from the document
2244
+ - Shows link type (wiki or markdown), target, display text, location
2245
+ - Indicates whether each link resolves to an indexed document
2246
+ - Default subcommand is `list` (can be omitted)
2247
+ - `--edge-type`/`--relation` switches to semantic `doc_edges` output (`edgeType`, `relationType`, `confidence`, `edgeSource`)
2248
+ - `--edge-type` and `--relation` are aliases for the same semantic edge type filter; if both are supplied they must match
2249
+ - `--type` cannot be combined with `--edge-type` or `--relation`
2250
+
2251
+ **Output (JSON):**
2252
+
2253
+ Schema: `links-list.schema.json`
2254
+
2255
+ ```json
2256
+ {
2257
+ "links": [
2258
+ {
2259
+ "targetRef": "Other Note",
2260
+ "linkType": "wiki",
2261
+ "linkText": "display text",
2262
+ "startLine": 10,
2263
+ "startCol": 5,
2264
+ "resolved": true,
2265
+ "resolvedDocid": "#abc123"
2266
+ }
2267
+ ],
2268
+ "meta": {
2269
+ "docid": "#def456",
2270
+ "uri": "gno://notes/source.md",
2271
+ "totalLinks": 1,
2272
+ "resolvedCount": 1
2273
+ }
2274
+ }
2275
+ ```
2276
+
2277
+ **Exit Codes:**
2278
+
2279
+ - 0: Success
2280
+ - 1: Document not found or invalid options
2281
+
2282
+ **Examples:**
2283
+
2284
+ ```bash
2285
+ # List all links from a document
2286
+ gno links gno://notes/source.md
2287
+
2288
+ # Filter to wiki links only
2289
+ gno links list #abc123 --type wiki
2290
+
2291
+ # Filter semantic relationship edges
2292
+ gno links gno://notes/source.md --edge-type mentions --json
2293
+ gno links gno://notes/source.md --relation mentions --json
2294
+
2295
+ # JSON output
2296
+ gno links gno://notes/note.md --json
2297
+ ```
2298
+
2299
+ ---
2300
+
2301
+ ### gno backlinks
2302
+
2303
+ List documents that link to a target document.
2304
+
2305
+ **Synopsis:**
2306
+
2307
+ ```bash
2308
+ gno backlinks <doc> [-c, --collection <name>] [--edge-type <type>] [--relation <type>] [--json] [--md]
2309
+ ```
2310
+
2311
+ **Arguments:**
2312
+
2313
+ | Argument | Description |
2314
+ | -------- | ---------------------------------------- |
2315
+ | `<doc>` | Document reference (docid, URI, or path) |
2316
+
2317
+ **Options:**
2318
+
2319
+ | Flag | Type | Description |
2320
+ | ------------------ | ------ | --------------------------------- |
2321
+ | `-c, --collection` | string | Filter by collection |
2322
+ | `--edge-type` | string | Filter semantic backlinks by type |
2323
+ | `--relation` | string | Alias for `--edge-type` |
2324
+ | `--json` | flag | JSON output |
2325
+ | `--md` | flag | Markdown output |
2326
+
2327
+ **Behavior:**
2328
+
2329
+ - Lists all documents that link TO the specified document
2330
+ - Shows source document info, link location, and link text
2331
+ - Supports both wiki and markdown link resolution
2332
+ - `--edge-type`/`--relation` switches to semantic `doc_edges` backlinks (`edgeType`, `relationType`, `confidence`, `edgeSource`) while preserving `--collection`
2333
+
2334
+ **Output (JSON):**
2335
+
2336
+ Schema: `backlinks.schema.json`
2337
+
2338
+ ```json
2339
+ {
2340
+ "backlinks": [
2341
+ {
2342
+ "sourceDocid": "#abc123",
2343
+ "sourceUri": "gno://notes/source.md",
2344
+ "sourceTitle": "Source Note",
2345
+ "linkText": "link to target",
2346
+ "startLine": 15,
2347
+ "startCol": 3
2348
+ }
2349
+ ],
2350
+ "meta": {
2351
+ "docid": "#def456",
2352
+ "uri": "gno://notes/target.md",
2353
+ "totalBacklinks": 1
2354
+ }
2355
+ }
2356
+ ```
2357
+
2358
+ **Exit Codes:**
2359
+
2360
+ - 0: Success
2361
+ - 1: Document not found
2362
+
2363
+ **Examples:**
2364
+
2365
+ ```bash
2366
+ # List backlinks to a document
2367
+ gno backlinks gno://notes/target.md
2368
+
2369
+ # Filter by collection
2370
+ gno backlinks #abc123 --collection notes
2371
+
2372
+ # Filter semantic backlinks
2373
+ gno backlinks gno://notes/target.md --relation related_to --json
2374
+
2375
+ # JSON output
2376
+ gno backlinks gno://docs/api.md --json
2377
+ ```
2378
+
2379
+ ---
2380
+
2381
+ ### gno similar
2382
+
2383
+ Find semantically similar documents using vector embeddings.
2384
+
2385
+ **Synopsis:**
2386
+
2387
+ ```bash
2388
+ gno similar <doc> [-n, --limit <num>] [--threshold <num>] [--cross-collection] [--json] [--md]
2389
+ ```
2390
+
2391
+ **Arguments:**
2392
+
2393
+ | Argument | Description |
2394
+ | -------- | ---------------------------------------- |
2395
+ | `<doc>` | Document reference (docid, URI, or path) |
2396
+
2397
+ **Options:**
2398
+
2399
+ | Flag | Type | Default | Description |
2400
+ | -------------------- | ------ | ------- | ------------------------------ |
2401
+ | `-n, --limit` | number | 5 | Maximum results |
2402
+ | `--threshold` | number | 0.7 | Minimum similarity score (0-1) |
2403
+ | `--cross-collection` | flag | false | Search across all collections |
2404
+ | `--json` | flag | | JSON output |
2405
+ | `--md` | flag | | Markdown output |
2406
+
2407
+ **Behavior:**
2408
+
2409
+ - Finds documents semantically similar to the source document
2410
+ - Requires embeddings to be generated (`gno embed`)
2411
+ - Uses average document embedding for comparison
2412
+ - By default, limits results to same collection
2413
+
2414
+ **Output (JSON):**
2415
+
2416
+ Schema: `similar.schema.json`
2417
+
2418
+ ```json
2419
+ {
2420
+ "similar": [
2421
+ {
2422
+ "docid": "#abc123",
2423
+ "uri": "gno://notes/related.md",
2424
+ "title": "Related Note",
2425
+ "score": 0.85,
2426
+ "collection": "notes",
2427
+ "relPath": "related.md"
2428
+ }
2429
+ ],
2430
+ "meta": {
2431
+ "docid": "#def456",
2432
+ "totalResults": 1,
2433
+ "limit": 5,
2434
+ "threshold": 0.7,
2435
+ "crossCollection": false
2436
+ }
2437
+ }
2438
+ ```
2439
+
2440
+ **Exit Codes:**
2441
+
2442
+ - 0: Success
2443
+ - 1: Document not found or no embeddings
2444
+ - 2: Vector search unavailable
2445
+
2446
+ **Examples:**
2447
+
2448
+ ```bash
2449
+ # Find similar documents
2450
+ gno similar gno://notes/note.md
2451
+
2452
+ # Increase limit and lower threshold
2453
+ gno similar #abc123 --limit 10 --threshold 0.5
2454
+
2455
+ # Search across all collections
2456
+ gno similar gno://docs/api.md --cross-collection --json
2457
+ ```
2458
+
2459
+ ---
2460
+
2461
+ ### gno graph
2462
+
2463
+ Generate knowledge graph of document links.
2464
+
2465
+ **Synopsis:**
2466
+
2467
+ ```bash
2468
+ gno graph [-c, --collection <name>] [--limit <num>] [--edge-limit <num>] [--include-similar] [--threshold <num>] [--include-isolated] [--similar-top-k <num>] [--json]
2469
+ ```
2470
+
2471
+ **Options:**
2472
+
2473
+ | Flag | Type | Default | Description |
2474
+ | -------------------- | ------ | ------- | ------------------------------ |
2475
+ | `-c, --collection` | string | all | Filter to single collection |
2476
+ | `--limit` | number | 2000 | Maximum nodes to return |
2477
+ | `--edge-limit` | number | 10000 | Maximum edges to return |
2478
+ | `--include-similar` | flag | false | Include similarity edges |
2479
+ | `--threshold` | number | 0.7 | Similarity threshold (0-1) |
2480
+ | `--include-isolated` | flag | false | Include isolated nodes |
2481
+ | `--similar-top-k` | number | 5 | Similar docs per node (max 20) |
2482
+ | `--json` | flag | | JSON output |
2483
+
2484
+ **Behavior:**
2485
+
2486
+ - Returns nodes (documents) and links (edges) as graph data
2487
+ - Includes a graph report with hubs, bridge candidates, isolated documents, unresolved links, and edge-type counts
2488
+ - Edges include wiki links, markdown links, and optionally similarity edges
2489
+ - Node degree reflects total unique connections (in + out)
2490
+ - When collection is filtered, degree may reflect links outside the filter
2491
+ - Truncates results if node/edge limits are exceeded
2492
+
2493
+ **Output (JSON):**
2494
+
2495
+ Schema: `graph.schema.json`
2496
+
2497
+ ```json
2498
+ {
2499
+ "nodes": [
2500
+ {
2501
+ "id": "#abc123",
2502
+ "uri": "gno://notes/note.md",
2503
+ "title": "My Note",
2504
+ "collection": "notes",
2505
+ "relPath": "note.md",
2506
+ "degree": 5
2507
+ }
2508
+ ],
2509
+ "links": [
2510
+ {
2511
+ "source": "#abc123",
2512
+ "target": "#def456",
2513
+ "type": "wiki",
2514
+ "weight": 1,
2515
+ "confidence": "explicit",
2516
+ "audit": { "resolution": "exact-title", "matchCount": 1 }
2517
+ }
2518
+ ],
2519
+ "report": {
2520
+ "hubs": [
2521
+ {
2522
+ "id": "#abc123",
2523
+ "uri": "gno://notes/note.md",
2524
+ "title": "My Note",
2525
+ "collection": "notes",
2526
+ "relPath": "note.md",
2527
+ "degree": 5
2528
+ }
2529
+ ],
2530
+ "bridgeCandidates": [],
2531
+ "isolated": { "total": 2, "examples": [] },
2532
+ "unresolvedLinks": {
2533
+ "total": 5,
2534
+ "byType": { "wiki": 4, "markdown": 1 }
2535
+ },
2536
+ "edgeTypes": { "wiki": 280, "markdown": 40, "similar": 0 },
2537
+ "edgeConfidence": {
2538
+ "explicit": 300,
2539
+ "inferred": 18,
2540
+ "ambiguous": 2,
2541
+ "similarity": 0
2542
+ },
2543
+ "audit": { "inferredEdges": 18, "ambiguousEdges": 2, "similarityEdges": 0 }
2544
+ },
2545
+ "meta": {
2546
+ "collection": null,
2547
+ "nodeLimit": 2000,
2548
+ "edgeLimit": 10000,
2549
+ "totalNodes": 150,
2550
+ "totalEdges": 320,
2551
+ "totalEdgesUnresolved": 5,
2552
+ "returnedNodes": 150,
2553
+ "returnedEdges": 320,
2554
+ "truncated": false,
2555
+ "linkedOnly": true,
2556
+ "includedSimilar": false,
2557
+ "similarAvailable": true,
2558
+ "similarTopK": 5,
2559
+ "similarTruncatedByComputeBudget": false,
2560
+ "warnings": []
2561
+ }
2562
+ }
2563
+ ```
2564
+
2565
+ ### gno graph query
2566
+
2567
+ Run a bounded typed-edge traversal from one document. This command uses the
2568
+ typed `doc_edges` projection (`relations:`, graph-hinted links, and backfilled
2569
+ wiki/markdown links) and is scoped to a resolved root rather than the global
2570
+ graph export.
2571
+
2572
+ **Synopsis:**
2573
+
2574
+ ```bash
2575
+ gno graph query <doc> [--direction <both|out|in>] [--edge-type <type>] [--max-depth <n>] [--max-nodes <n>] [--frontier-limit <n>] [--visited-limit <n>] [--json]
2576
+ ```
2577
+
2578
+ **Options:**
2579
+
2580
+ | Flag | Type | Default | Description |
2581
+ | ------------------ | ------ | ------- | ------------------------------------ |
2582
+ | `--direction` | enum | both | Traverse outgoing, incoming, or both |
2583
+ | `--edge-type` | string | all | Filter to one typed edge/relation |
2584
+ | `--max-depth` | number | 2 | Maximum traversal depth |
2585
+ | `--max-nodes` | number | 100 | Maximum returned nodes |
2586
+ | `--frontier-limit` | number | 100 | Max frontier width per depth |
2587
+ | `--visited-limit` | number | 500 | Max visited rows during traversal |
2588
+ | `--json` | flag | | JSON output |
2589
+
2590
+ **Behavior:**
2591
+
2592
+ - Resolves `<doc>` using the shared core ref parser (`#docid`, `gno://...`, or `collection/path`)
2593
+ - Traverses typed edges with cycle safety and deterministic ordering
2594
+ - Enforces hard depth, frontier, and visited-row caps; sets `meta.truncated` with warnings when caps trip
2595
+ - Includes per-node `graphHints` from the node's configured content type
2596
+
2597
+ Schema: `graph-query.schema.json`
2598
+
2599
+ **Global Graph Edge Types:**
2600
+
2601
+ - `wiki`: Wiki link (`[[Target]]`)
2602
+ - `markdown`: Markdown link (`[text](path.md)`)
2603
+ - `similar`: Semantic similarity (requires `--include-similar` flag)
2604
+
2605
+ `gno graph query --edge-type` filters the typed `doc_edges.edge_type` values
2606
+ derived from frontmatter relations, content-type graph hints, and backfilled
2607
+ wiki/markdown projections (for example `mentions`, `references`, or
2608
+ `related`), not the global graph export edge-type enum above.
2609
+
2610
+ **Exit Codes:**
2611
+
2612
+ - 0: Success
2613
+ - 1: No documents indexed
2614
+
2615
+ **Examples:**
2616
+
2617
+ ```bash
2618
+ # Full graph
2619
+ gno graph
2620
+
2621
+ # Filter by collection
2622
+ gno graph --collection notes
2623
+
2624
+ # Include similarity edges
2625
+ gno graph --include-similar --threshold 0.6
2626
+
2627
+ # JSON output with limits
2628
+ gno graph --limit 500 --edge-limit 2000 --json
2629
+ ```
2630
+
2631
+ ---
2632
+
2633
+ ### gno serve
2634
+
2635
+ Start web UI server for visual search and browse.
2636
+
2637
+ Both resident commands read the optional root `gateway` config. CLI gateway
2638
+ flags override the corresponding scalar/list values for that invocation:
2639
+
2640
+ ```yaml
2641
+ gateway:
2642
+ host: 127.0.0.1
2643
+ tokenFile: ~/.config/gno/mcp-token
2644
+ allowedHosts: [127.0.0.1:3000, localhost:3000]
2645
+ allowedOrigins: [http://127.0.0.1:3000, http://localhost:3000]
2646
+ enableWrite: false
2647
+ limits:
2648
+ maxBodyBytes: 1048576
2649
+ maxRequestsPerMinute: 120
2650
+ maxConcurrentRequests: 64
2651
+ maxQueuedRequests: 16
2652
+ maxSessions: 32
2653
+ sessionIdleTimeoutMs: 300000
2654
+ ```
2655
+
2656
+ The token file is generated only when a path is explicitly configured. A
2657
+ wildcard/non-loopback `host` requires a token file plus non-empty exact Host and
2658
+ Origin allowlists; startup otherwise exits 2 without opening a listener.
2659
+ `gno serve` additionally rejects non-loopback hosts because its Web UI and REST
2660
+ API share the listener; use `gno daemon` for authenticated non-loopback MCP.
2661
+
2662
+ **Synopsis:**
2663
+
2664
+ ```bash
2665
+ gno serve [--port <num>] [gateway-options] [--detach] [--pid-file <path>] [--log-file <path>]
2666
+ gno serve --status [--json]
2667
+ gno serve --stop
2668
+ ```
2669
+
2670
+ **Options:**
2671
+
2672
+ | Option | Type | Default | Description |
2673
+ | ---------------------- | ------- | ------------------------ | ---------------------------------------------------------------- |
2674
+ | `-p, --port` | number | 3000 | Port to listen on |
2675
+ | `--detach` | boolean | false | Self-spawn a detached child; parent prints `{pid,url}` and exits |
2676
+ | `--pid-file <path>` | string | `{data}/serve.pid` | Override pid-file location (JSON metadata, absolute path) |
2677
+ | `--log-file <path>` | string | `{data}/serve.log` | Override log-file location (append mode) |
2678
+ | `--status` | boolean | false | Read pid-file, check liveness, print status (JSON with `--json`) |
2679
+ | `--stop` | boolean | false | Graceful SIGTERM with 10s timeout → SIGKILL fallback |
2680
+ | `--host <address>` | string | `127.0.0.1` | Loopback listen address (Web/REST remains local-only) |
2681
+ | `--mcp-token-file` | string | config | Restrictive bearer-token file |
2682
+ | `--mcp-allowed-host` | string | config/loopback defaults | Exact Host value; repeatable |
2683
+ | `--mcp-allowed-origin` | string | config/loopback defaults | Exact Origin; repeatable |
2684
+ | `--mcp-enable-write` | boolean | false | Separately authorize HTTP MCP mutation tools |
2685
+
2686
+ `--detach`, `--status`, and `--stop` are mutually exclusive. Passing more than one produces a `VALIDATION` error (exit 1).
2687
+
2688
+ Default paths live under `resolveDirs().data` (honours `GNO_DATA_DIR`). Only one
2689
+ resident owner (`serve` or `daemon`) may use a `GNO_DATA_DIR`; any second start
2690
+ is blocked.
2691
+
2692
+ **Behavior:**
2693
+
2694
+ - Opens database once at startup (not per-request)
2695
+ - Closes the HTTP server, background runtime, and database on SIGINT/SIGTERM
2696
+ before the CLI exits; the CLI bootstrap does not race the command's handler
2697
+ - Sets CSP header: `default-src 'self'; script-src 'self'`
2698
+ - Health check at `/api/health` returns `{ok:true}`
2699
+ - Safe lifecycle status at `/api/resident/status` and the `resident` member of
2700
+ `/api/status` derive from the same `resident-status@1.0` snapshot
2701
+ - Mounts stateful Streamable HTTP MCP at `/mcp` only after the fail-closed
2702
+ actual-peer, Host, Origin, bearer, body, rate, request, queue, and session
2703
+ boundary initializes
2704
+ - On `--detach`: forks a detached child with stdio redirected to `--log-file`, writes pid-file JSON (`{pid, port, cmd:"serve", version, started_at}`), prints `{pid, url}` on stdout, exits 0
2705
+ - On `--status`: output matches the [process-status schema](./output-schemas/process-status.schema.json). Liveness via `process.kill(pid, 0)`; stale pid-files (ESRCH) are reported as `running:false`. Live status best-effort reads the same redacted `resident-status@1.0` snapshot from the recorded listener.
2706
+ - On `--stop`: sends SIGTERM, polls every 100ms for up to 10s, falls back to SIGKILL, polls 2s more, unlinks pid-file if the process cleaned up after itself
2707
+ - **Windows**: `--detach` is unsupported and returns a `VALIDATION` error pointing to WSL. `--status` / `--stop` / `--pid-file` / `--log-file` remain parseable but have nothing to manage.
2708
+
2709
+ **Exit Codes:**
2710
+
2711
+ - 0: Server stopped gracefully, `--detach` succeeded, `--stop` completed, or `--status` found a live process
2712
+ - 1: Validation error (mutex violation, bad flag combination, Windows `--detach`)
2713
+ - 2: Server failed to start (DB error, port in use, spawn failure)
2714
+ - 3: `--status` or `--stop` found no live matching process (`NOT_RUNNING`)
2715
+
2716
+ **Examples:**
2717
+
2718
+ ```bash
2719
+ gno serve
2720
+ gno serve --port 8080
2721
+
2722
+ # Backgrounding
2723
+ gno serve --detach
2724
+ gno serve --status
2725
+ gno serve --status --json
2726
+ gno serve --stop
2727
+
2728
+ # Custom paths
2729
+ gno serve --detach --pid-file /tmp/gno-serve.pid --log-file /tmp/gno-serve.log
2730
+
2731
+ # Mutually exclusive — errors with VALIDATION
2732
+ gno serve --detach --stop
2733
+ ```
2734
+
2735
+ ---
2736
+
2737
+ ### gno daemon
2738
+
2739
+ Start a headless long-running watcher process for continuous indexing.
2740
+
2741
+ **Synopsis:**
2742
+
2743
+ ```bash
2744
+ gno daemon [--port <num>] [--no-sync-on-start] [gateway-options] [--detach] [--pid-file <path>] [--log-file <path>]
2745
+ gno daemon --status [--json]
2746
+ gno daemon --stop
2747
+ ```
2748
+
2749
+ **Options:**
2750
+
2751
+ | Option | Type | Default | Description |
2752
+ | ---------------------- | ------- | ------------------------ | ---------------------------------------------------------------- |
2753
+ | `--no-sync-on-start` | boolean | false | Skip initial sync; only watch future file changes |
2754
+ | `-p, --port <num>` | number | 3000 | Headless HTTP MCP gateway port |
2755
+ | `--detach` | boolean | false | Self-spawn a detached child; parent prints `{pid}` and exits |
2756
+ | `--pid-file <path>` | string | `{data}/daemon.pid` | Override pid-file location (JSON metadata, absolute path) |
2757
+ | `--log-file <path>` | string | `{data}/daemon.log` | Override log-file location (append mode) |
2758
+ | `--status` | boolean | false | Read pid-file, check liveness, print status (JSON with `--json`) |
2759
+ | `--stop` | boolean | false | Graceful SIGTERM with 10s timeout → SIGKILL fallback |
2760
+ | `--host <address>` | string | `127.0.0.1` | HTTP listen address |
2761
+ | `--mcp-token-file` | string | config | Restrictive bearer-token file |
2762
+ | `--mcp-allowed-host` | string | config/loopback defaults | Exact Host value; repeatable |
2763
+ | `--mcp-allowed-origin` | string | config/loopback defaults | Exact Origin; repeatable |
2764
+ | `--mcp-enable-write` | boolean | false | Separately authorize HTTP MCP mutation tools |
2765
+
2766
+ `--detach`, `--status`, and `--stop` are mutually exclusive. Passing more than one produces a `VALIDATION` error (exit 1).
2767
+
2768
+ Default paths live under `resolveDirs().data` (honours `GNO_DATA_DIR`). Only one
2769
+ resident owner (`serve` or `daemon`) may use a `GNO_DATA_DIR`; any second start
2770
+ is blocked.
2771
+
2772
+ **Behavior:**
2773
+
2774
+ - Opens DB once at startup
2775
+ - Loads config and requires at least one configured collection
2776
+ - Starts the same watcher + embed scheduler used by `gno serve`
2777
+ - Runs an initial sync by default
2778
+ - Triggers embedding after initial sync completes
2779
+ - Runs in the foreground until `SIGINT` / `SIGTERM`
2780
+ - Starts a headless `/mcp` Streamable HTTP listener; it does not serve the Web UI
2781
+ - Exposes the same safe REST lifecycle snapshot at `/api/resident/status`;
2782
+ resident-aware app status at `/api/status` is loopback-only because it
2783
+ includes local index and configuration details
2784
+ - On `--detach`: forks a detached child with stdio redirected to `--log-file`, writes pid-file JSON including the MCP gateway `port`, prints `{pid}` on stdout, exits 0
2785
+ - On `--status`: output matches the [process-status schema](./output-schemas/process-status.schema.json), including the MCP gateway port and a best-effort copy of the live redacted resident snapshot
2786
+ - On `--stop`: SIGTERM → 10s poll → SIGKILL → 2s poll; the daemon's own signal handler unlinks the pid-file, `--stop` unlinks as fallback
2787
+ - **Windows**: `--detach` is unsupported and returns a `VALIDATION` error pointing to WSL.
2788
+
2789
+ **Packaged conformance:** `bun run test:package` installs the generated npm
2790
+ tarball and exercises the shipped binary. It covers concurrent HTTP MCP clients,
2791
+ stdio parity, resident reuse, redacted lifecycle schemas, boundary rejection,
2792
+ bearer rotation and session binding, daemon-only authenticated non-loopback
2793
+ binding, and detached restart/shutdown. Windows artifact jobs provide the final
2794
+ platform-specific detach and interrupt-exit sweep.
2795
+
2796
+ **Exit Codes:**
2797
+
2798
+ - 0: Daemon stopped gracefully, `--detach` succeeded, `--stop` completed, or `--status` found a live process
2799
+ - 1: Validation error (mutex violation, bad flag combination, Windows `--detach`)
2800
+ - 2: Startup/runtime failure
2801
+ - 3: `--status` or `--stop` found no live matching process (`NOT_RUNNING`)
2802
+
2803
+ **Examples:**
2804
+
2805
+ ```bash
2806
+ gno daemon
2807
+ gno daemon --no-sync-on-start
2808
+
2809
+ # Backgrounding
2810
+ gno daemon --detach
2811
+ gno daemon --status
2812
+ gno daemon --status --json
2813
+ gno daemon --stop
2814
+
2815
+ # Custom paths
2816
+ gno daemon --detach --log-file /tmp/gno-daemon.log
2817
+
2818
+ # Mutually exclusive — errors with VALIDATION
2819
+ gno daemon --status --stop
2820
+ ```
2821
+
2822
+ ---
2823
+
2824
+ ### gno completion
2825
+
2826
+ Output or install shell completion scripts.
2827
+
2828
+ **Synopsis:**
2829
+
2830
+ ```bash
2831
+ gno completion <shell>
2832
+ gno completion install [--shell <shell>] [--json]
2833
+ ```
2834
+
2835
+ **Subcommands:**
2836
+
2837
+ | Subcommand | Description |
2838
+ | ---------- | ------------------------------------------ |
2839
+ | `<shell>` | Output completion script (bash, zsh, fish) |
2840
+ | `install` | Auto-install completion to shell config |
2841
+
2842
+ **Options (install):**
2843
+
2844
+ | Flag | Type | Description |
2845
+ | ------------- | ------ | ----------------------------------------------- |
2846
+ | `-s, --shell` | string | Shell to install for (auto-detected if omitted) |
2847
+ | `--json` | flag | JSON output |
2848
+
2849
+ **Supported Shells:**
2850
+
2851
+ - `bash` - Appends to ~/.bashrc or ~/.bash_profile (macOS)
2852
+ - `zsh` - Appends to ~/.zshrc
2853
+ - `fish` - Creates ~/.config/fish/completions/gno.fish
2854
+
2855
+ **Completion Features:**
2856
+
2857
+ - Static: Commands, subcommands, flags (always available)
2858
+ - Dynamic: Collection names for `--collection` flag (when DB available)
2859
+
2860
+ **Examples:**
2861
+
2862
+ ```bash
2863
+ # Output bash completion script
2864
+ gno completion bash >> ~/.bashrc
2865
+
2866
+ # Auto-install for detected shell
2867
+ gno completion install
2868
+
2869
+ # Install for specific shell
2870
+ gno completion install --shell zsh
2871
+ ```
2872
+
2873
+ **Exit Codes:**
2874
+
2875
+ - 0: Success
2876
+ - 1: Unsupported shell
2877
+
2878
+ ---
2879
+
2880
+ ## Error Output
2881
+
2882
+ Errors are written to stderr. With `--json` flag, errors are also returned as:
2883
+
2884
+ ```json
2885
+ {
2886
+ "error": {
2887
+ "code": "VALIDATION",
2888
+ "message": "Missing required argument: query",
2889
+ "details": {}
2890
+ }
2891
+ }
2892
+ ```
2893
+
2894
+ Error codes match exit codes: `VALIDATION` (exit 1), `RUNTIME` (exit 2), `NOT_RUNNING` (exit 3).
2895
+
2896
+ **`NOT_RUNNING` is not an error envelope.** `gno serve|daemon --status --json` returns a `process-status`-shaped payload on stdout with exit 3 when no live matching process is found (it reports observable state, not failure). `--stop` exits 3 silently when there is nothing to stop and does not accept `--json`. The error envelope above is reserved for `VALIDATION` and `RUNTIME` failures where the command could not produce its structured output at all.
2897
+
2898
+ ---
2899
+
2900
+ ## Environment Variables
2901
+
2902
+ | Variable | Description |
2903
+ | -------------------------- | ------------------------------------------------ |
2904
+ | `GNO_CONFIG_DIR` | Override config directory |
2905
+ | `GNO_DATA_DIR` | Override data directory (DB location) |
2906
+ | `GNO_CACHE_DIR` | Override cache directory (models) |
2907
+ | `NO_COLOR` | Disable colored output (standard) |
2908
+ | `PAGER` | Pager for long output (default: less -R, more) |
2909
+ | `GNO_SKILLS_HOME_OVERRIDE` | Override home dir for skill user scope (testing) |
2910
+ | `CLAUDE_SKILLS_DIR` | Override Claude skills directory |
2911
+ | `CODEX_SKILLS_DIR` | Override Codex skills directory |
2912
+
2913
+ ---
2914
+
2915
+ ## See Also
2916
+
2917
+ - [MCP Specification](./mcp.md)
2918
+ - [Output Schemas](./output-schemas/)
2919
+ - [PRD](../docs/prd.md)