@gmickel/gno 2.12.2 → 2.14.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 (75) hide show
  1. package/README.md +1 -1
  2. package/assets/skill/SKILL.md +36 -5
  3. package/assets/skill/cli-reference.md +62 -11
  4. package/assets/skill/examples.md +30 -4
  5. package/assets/skill/mcp-reference.md +16 -0
  6. package/assets/spa-production.json.gz +0 -0
  7. package/browser-extension/artifacts/{gno-browser-clipper-v2.12.2.zip → gno-browser-clipper-v2.14.0.zip} +0 -0
  8. package/browser-extension/artifacts/gno-browser-clipper-v2.14.0.zip.sha256 +1 -0
  9. package/browser-extension/dist/manifest.json +1 -1
  10. package/package.json +1 -1
  11. package/spec/cli.md +139 -16
  12. package/spec/mcp.md +111 -3
  13. package/spec/output-schemas/collection-list.schema.json +28 -0
  14. package/spec/output-schemas/convert.schema.json +97 -0
  15. package/spec/output-schemas/ls.schema.json +65 -0
  16. package/spec/output-schemas/mcp-job-status.schema.json +17 -1
  17. package/spec/output-schemas/mcp-sync-result.schema.json +9 -2
  18. package/spec/output-schemas/search-results.schema.json +7 -0
  19. package/src/cli/commands/index-cmd.ts +50 -12
  20. package/src/cli/commands/ls.ts +20 -3
  21. package/src/cli/commands/profile-apply.ts +2 -1
  22. package/src/cli/commands/query.ts +21 -8
  23. package/src/cli/commands/search.ts +11 -0
  24. package/src/cli/commands/vsearch.ts +17 -4
  25. package/src/cli/options.ts +19 -0
  26. package/src/cli/program.ts +90 -10
  27. package/src/config/loader.ts +11 -1
  28. package/src/config/types.ts +90 -1
  29. package/src/core/audit-collection-rules.ts +331 -0
  30. package/src/core/audit-links.ts +3 -1
  31. package/src/core/audit-workspace.ts +69 -5
  32. package/src/core/collection-egress-policy-service.ts +4 -1
  33. package/src/core/collection-scope.ts +105 -0
  34. package/src/core/egress-enforcement.ts +20 -4
  35. package/src/core/egress-policy.ts +4 -0
  36. package/src/core/egress-provenance.ts +33 -11
  37. package/src/core/errors.ts +5 -0
  38. package/src/core/job-manager.ts +238 -19
  39. package/src/core/project-profile-apply-state.ts +6 -15
  40. package/src/core/project-profile-apply.ts +32 -1
  41. package/src/core/reindex-paths.ts +167 -0
  42. package/src/core/retrieval-trace-request.ts +1 -0
  43. package/src/ingestion/convert-file.ts +181 -0
  44. package/src/llm/registry.ts +20 -0
  45. package/src/mcp/http-egress.ts +21 -2
  46. package/src/mcp/tools/collection-scope.ts +25 -0
  47. package/src/mcp/tools/index-cmd.ts +66 -48
  48. package/src/mcp/tools/index.ts +74 -10
  49. package/src/mcp/tools/job-status.ts +7 -0
  50. package/src/mcp/tools/query.ts +28 -7
  51. package/src/mcp/tools/search.ts +4 -0
  52. package/src/mcp/tools/sync.ts +68 -60
  53. package/src/mcp/tools/vsearch.ts +9 -3
  54. package/src/pipeline/filters.ts +48 -5
  55. package/src/pipeline/graph-retrieval.ts +1 -0
  56. package/src/pipeline/hybrid.ts +15 -2
  57. package/src/pipeline/hydration.ts +7 -2
  58. package/src/pipeline/owner-fusion.ts +1 -0
  59. package/src/pipeline/search.ts +2 -0
  60. package/src/pipeline/types.ts +7 -0
  61. package/src/pipeline/vsearch.ts +10 -5
  62. package/src/sdk/client.ts +45 -12
  63. package/src/sdk/convert.ts +27 -0
  64. package/src/sdk/documents.ts +15 -2
  65. package/src/sdk/index.ts +2 -0
  66. package/src/sdk/types.ts +4 -1
  67. package/src/serve/jobs.ts +4 -1
  68. package/src/serve/public/components/IndexingProgress.tsx +25 -3
  69. package/src/serve/routes/api.ts +164 -24
  70. package/src/store/sqlite/adapter.ts +97 -27
  71. package/src/store/sqlite/eligibility.ts +4 -0
  72. package/src/store/types.ts +7 -0
  73. package/src/store/vector/sqlite-vec.ts +67 -15
  74. package/src/store/vector/variant-search.ts +41 -4
  75. package/browser-extension/artifacts/gno-browser-clipper-v2.12.2.zip.sha256 +0 -1
package/README.md CHANGED
@@ -139,7 +139,7 @@ See the [guide](docs/COMPILED-CONTEXT.md).
139
139
 
140
140
  <!-- public-truth:current-version -->
141
141
 
142
- > Current source version: **v2.12.2**. See [CHANGELOG.md](./CHANGELOG.md).
142
+ > Current source version: **v2.14.0**. See [CHANGELOG.md](./CHANGELOG.md).
143
143
 
144
144
  <!-- /public-truth -->
145
145
 
@@ -84,7 +84,7 @@ Recipe rules:
84
84
  | ------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
85
85
  | **Search** | `search`, `vsearch`, `query`, `ask` | Find documents by keywords, meaning, or get AI answers |
86
86
  | **Links** | `links`, `backlinks`, `similar`, `graph`, `graph query` | Navigate document relationships and typed connections |
87
- | **Retrieve** | `get`, `multi-get`, `ls` | Fetch document content by URI or ID |
87
+ | **Retrieve** | `get`, `multi-get`, `ls`, `ls --filter` | Fetch document content by URI or ID; list documents by typed metadata |
88
88
  | **Index** | `setup`, `profile check/show/diff/apply`, `init`, `collection add/list/remove`, `index`, `update`, `embed` | Reproduce profile intent, prove retrieval, then maintain the index |
89
89
  | **Tags** | `tags`, `tags add`, `tags rm` | Organize and filter documents |
90
90
  | **Context** | `context add/list/rm/check/build/verify/watch/watches/reverify/unwatch` | Configure guidance or compile, verify, and watch saved evidence Capsules |
@@ -154,13 +154,15 @@ machines are slower.
154
154
  exact identifier/quote/error you already have verbatim → `gno search` (a
155
155
  question, or a name or topic taken from it, is not exact text). No good results: run balanced
156
156
  `gno query`, then rephrase, then `--thorough`. An expected document still
157
- missing: `gno query diagnose` before falling back to grep.
157
+ missing: `gno query diagnose` before falling back to grep. When the user
158
+ explicitly asks for a semantic, vector or meaning-only search (their documents
159
+ use different wording), use `gno vsearch` instead.
158
160
 
159
161
  ## Common Flags (search/vsearch/query/ask)
160
162
 
161
163
  ```
162
164
  -n <num> Max results (default: 5)
163
- -c, --collection Filter to collection
165
+ -c, --collection Filter to collection (repeat for several; search/vsearch/query)
164
166
  --tags-any <t1,t2> Has ANY of these tags
165
167
  --tags-all <t1,t2> Has ALL of these tags
166
168
  --since <date> Modified after date (ISO: 2026-03-01)
@@ -345,7 +347,10 @@ confirmation and invalidates active sessions, streams, and queued work.
345
347
 
346
348
  Bearer authentication, MCP write enablement, and collection policy are
347
349
  independent gates. `EGRESS_DENIED` is not permission to retry through another
348
- surface. Audit output is local and content-free via
350
+ surface. The one exception is reason `COLLECTION_LIMIT_EXCEEDED`: the call
351
+ covered more than 64 collections (128 for lineage), so retry it scoped to the
352
+ collections the task needs (`collection`/`collections`, `-c`). It is not an
353
+ outage; never restart, raise a limit or relax policy for it. Audit output is local and content-free via
349
354
  `gno egress-audit list|show|status`; deletion/purge must be explicit. A local
350
355
  policy change cannot retract data already uploaded—tell the user to remove it
351
356
  at the remote service. For gno.sh, supported private links can be revoked or
@@ -371,6 +376,29 @@ never from code spans, code blocks, or non-Markdown files.
371
376
  Report `truncation.snapshotTruncated` as "totals cover the bounded snapshot,
372
377
  not the whole index".
373
378
 
379
+ A collection can opt into three report-only link rules with an `audit` block in
380
+ its config: `linkCollections` (other collections it may link to; its own is
381
+ always allowed), `externalUrls: report`, and `paths` (globs every document path
382
+ must match). Their findings are `warning`s: `links.collection-policy`,
383
+ `links.external-url` and `links.path-layout`. Report them as structure and
384
+ publishing hygiene, not access control: `links.external-url` only reports a
385
+ URL written in a note; it sends and blocks nothing, so the remedy is editing
386
+ the note or setting `externalUrls: ignore`, never a collection policy change.
387
+ Show which URLs were found with
388
+ `gno audit links --json | jq -r '.findings[] | select(.ruleId == "links.external-url") | "\(.subject) \(.location) \(.evidence[0].summary)"'`.
389
+ Never add an `audit` block on your own initiative. When the user asks for a
390
+ rule, there is no CLI flag: always write out the exact `audit` block for that
391
+ collection's entry in the config file, then run the check. For "published may
392
+ link only to reference":
393
+
394
+ ```bash
395
+ gno doctor # prints "Config loaded: <path>"
396
+ # In <path>, under the `published` collection entry, add:
397
+ # audit:
398
+ # linkCollections: [reference]
399
+ gno audit links -c published --json
400
+ ```
401
+
374
402
  Treat exit `4` as a complete report with findings. Exit `5` or report status
375
403
  `partial`/`changed_during_audit` means evidence is unavailable, inconclusive,
376
404
  cancelled, truncated, or repeatedly changed—never healthy. Preserve stable
@@ -753,7 +781,10 @@ fields must be indexed from nested YAML `gno.metadata`; arbitrary frontmatter
753
781
  keys are not automatically custom fields. Run `gno metadata --collection <c>`
754
782
  (MCP `gno_list_metadata`) first to see which keys, types and values exist.
755
783
  CLI retrieval accepts `--filter` JSON; MCP/SDK/REST accept the same `filter`
756
- object:
784
+ object. To enumerate documents by metadata with no search term (every
785
+ `status: draft` note), use `gno ls [<collection>] --filter '<json>'`, not a
786
+ search with a placeholder query; `--json` returns `{documents, meta}`, so read
787
+ `.documents[]` and `meta.total`:
757
788
 
758
789
  ```json
759
790
  {
@@ -151,6 +151,19 @@ gno index [options]
151
151
  | `--models-pull` | Download models if missing |
152
152
  | `--git-pull` | Git pull before indexing |
153
153
 
154
+ ### gno convert
155
+
156
+ Print the Markdown GNO would index for one file. Needs no config or index and
157
+ writes nothing.
158
+
159
+ ```bash
160
+ gno convert <file> [--json]
161
+ ```
162
+
163
+ `--json` adds `title`, `converter`, `mirrorHash` and `warnings`. Mail archives,
164
+ calendars and other multi-record formats are refused (`MULTI_DOCUMENT`); index
165
+ them in a collection instead.
166
+
154
167
  ### gno embed
155
168
 
156
169
  Generate embeddings only.
@@ -393,16 +406,16 @@ BM25 keyword search.
393
406
  gno search <query> [options]
394
407
  ```
395
408
 
396
- | Option | Default | Description |
397
- | ------------------ | ------- | -------------------------------- |
398
- | `-n` | 5 | Max results |
399
- | `--min-score` | 0 | Minimum score (0-1) |
400
- | `-c, --collection` | all | Filter to collection |
401
- | `--tags-any` | - | Filter: has ANY tag (comma-sep) |
402
- | `--tags-all` | - | Filter: has ALL tags (comma-sep) |
403
- | `--full` | false | Full content (not snippets) |
404
- | `--line-numbers` | false | Include line numbers |
405
- | `--lang` | auto | Language filter |
409
+ | Option | Default | Description |
410
+ | ------------------ | ------- | ------------------------------------------------------ |
411
+ | `-n` | 5 | Max results |
412
+ | `--min-score` | 0 | Minimum score (0-1) |
413
+ | `-c, --collection` | all | Filter to collection; repeat (`-c a -c b`) for several |
414
+ | `--tags-any` | - | Filter: has ANY tag (comma-sep) |
415
+ | `--tags-all` | - | Filter: has ALL tags (comma-sep) |
416
+ | `--full` | false | Full content (not snippets) |
417
+ | `--line-numbers` | false | Include line numbers |
418
+ | `--lang` | auto | Language filter |
406
419
 
407
420
  Output formats: `--json`, `--files`, `--csv`, `--md`, `--xml`
408
421
 
@@ -497,9 +510,14 @@ gno multi-get <pattern> [--max-bytes <n>] [--line-numbers]
497
510
  List documents.
498
511
 
499
512
  ```bash
500
- gno ls [<scope>] [--json|--files|--md]
513
+ gno ls [<scope>] [--filter <json>] [-n <num>] [--offset <num>] [--json|--files|--md]
501
514
  ```
502
515
 
516
+ - `<scope>`: collection name or `gno://collection/prefix`
517
+ - `--filter <json>`: list only documents whose typed metadata (`gno.metadata`) matches; same predicate syntax and semantics as `gno search --filter`. Applies before pagination
518
+ - `--json` returns `{documents: [{docid, uri, title, source}], meta: {total, returned, offset}}`; `meta.total` counts all matches
519
+ - With `--filter`, `meta.warnings` (`METADATA_COVERAGE_INCOMPLETE`) appears when documents with invalid or pending metadata were skipped; other formats print it on stderr
520
+
503
521
  ## Context Management
504
522
 
505
523
  ### gno context add
@@ -640,6 +658,9 @@ gno egress-audit purge
640
658
  Denied egress is terminal for that route; do not retry through another network
641
659
  surface. Relaxing policy requires the current revision; tightening to
642
660
  `local_only` does not. Audit receipts are local and content-free.
661
+ `COLLECTION_LIMIT_EXCEEDED` is the one denial to retry: the request covered more
662
+ than 64 collections (128 for lineage), so name only the collections it needs
663
+ (`-c <name>`).
643
664
  Deletion/purge must remain explicit.
644
665
 
645
666
  ## Note Linking
@@ -836,6 +857,36 @@ gno models path [--json]
836
857
 
837
858
  ## Maintenance
838
859
 
860
+ ### gno audit
861
+
862
+ Read-only workspace integrity report. Exit 4 = complete with findings, 5 =
863
+ partial/inconclusive.
864
+
865
+ ```bash
866
+ gno audit [links|provenance|freshness|all] [-c <collection>] [--json]
867
+ gno audit links -c published --json
868
+ ```
869
+
870
+ Opt-in, report-only collection rules come from an `audit` block on a
871
+ collection in the config file (`gno doctor` shows its path); there are no CLI
872
+ flags for them:
873
+
874
+ ```yaml
875
+ collections:
876
+ - name: published
877
+ path: ~/notes/published
878
+ audit:
879
+ linkCollections: [reference] # other collections it may link to
880
+ externalUrls: report # report | ignore (default ignore)
881
+ paths: ["20??/**/*.md"] # globs every document path must match
882
+ ```
883
+
884
+ | Rule id | Reports |
885
+ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
886
+ | `links.collection-policy` | A link into a collection `linkCollections` does not list |
887
+ | `links.external-url` | A URL written in a document (outside frontmatter, code, comments), or `unchecked` when the text is unavailable. Not egress |
888
+ | `links.path-layout` | A document whose path matches none of the `paths` globs |
889
+
839
890
  ### gno peek
840
891
 
841
892
  Cheap counts, backlog, recent files, and serve liveness. Same `peek@1.0`
@@ -183,6 +183,17 @@ gno ls work
183
183
  gno ls --json
184
184
  ```
185
185
 
186
+ ### List by metadata
187
+
188
+ ```bash
189
+ # Every draft note in work (no search term needed)
190
+ gno ls work --filter '{"op":"eq","key":"status","value":"draft"}'
191
+
192
+ # URIs of every note whose project list contains atlas
193
+ gno ls work --filter '{"op":"in","key":"project","values":["atlas"]}' -n 1000 --json \
194
+ | jq -r '.documents[].uri'
195
+ ```
196
+
186
197
  ## Search & Retrieve Full Content
187
198
 
188
199
  ### Pipeline: Search then get
@@ -204,10 +215,10 @@ gno search "error handling" --json | jq -r '.results[].uri' | xargs gno multi-ge
204
215
 
205
216
  ```bash
206
217
  # Get URIs of matches
207
- gno search "api endpoint" --json | jq '.[] | .uri'
218
+ gno search "api endpoint" --json | jq '.results[] | .uri'
208
219
 
209
220
  # Get snippets
210
- gno search "config" --json | jq '.[] | {uri, snippet}'
221
+ gno search "config" --json | jq '.results[] | {uri, snippet}'
211
222
  ```
212
223
 
213
224
  ### Check index health
@@ -224,10 +235,10 @@ gno doctor --json
224
235
 
225
236
  ```bash
226
237
  # Export all document IDs
227
- gno ls --json | jq -r '.[].docid' > doc-ids.txt
238
+ gno ls --json -n 10000 | jq -r '.documents[].docid' > doc-ids.txt
228
239
 
229
240
  # Search and process results
230
- gno search "error" --json | jq -r '.[] | .uri' | while read uri; do
241
+ gno search "error" --json | jq -r '.results[] | .uri' | while read uri; do
231
242
  echo "Processing: $uri"
232
243
  gno get "$uri" > "output/$(basename $uri)"
233
244
  done
@@ -235,6 +246,21 @@ done
235
246
 
236
247
  ## Maintenance
237
248
 
249
+ ### Check a published collection's links
250
+
251
+ Let `published` link only to itself and `reference`: add this to the
252
+ `published` collection in the config file (`gno doctor` shows its path).
253
+
254
+ ```yaml
255
+ audit:
256
+ linkCollections: [reference]
257
+ ```
258
+
259
+ ```bash
260
+ gno audit links -c published --json \
261
+ | jq -r '.findings[] | select(.ruleId == "links.collection-policy") | "\(.subject) \(.location): \(.message)"'
262
+ ```
263
+
238
264
  ### Update index after changes
239
265
 
240
266
  ```bash
@@ -93,6 +93,14 @@ gno serve --status --json
93
93
  gno daemon --status --json
94
94
  ```
95
95
 
96
+ Each HTTP request is checked against the egress policy of the collections it
97
+ covers: the ones it names, or every configured collection when it names none.
98
+ One check covers at most 64 collections (128 for a policy lineage; 64 names for
99
+ `gno_egress_check`). A 403 whose reason is `COLLECTION_LIMIT_EXCEEDED` means the
100
+ call covered too many collections: retry with `collection` or `collections` set
101
+ to just the ones the task needs. It is not an outage, so do not restart the
102
+ server, raise a limit, change policy, or retry through another surface.
103
+
96
104
  ## Retrieval Order
97
105
 
98
106
  `gno mcp --tool-profile core` (or `gateway.toolProfile: core` for the resident
@@ -114,6 +122,8 @@ abstains unless every substantive claim is supported. This is a support
114
122
  classification against the retained Capsule, not a guarantee that the corpus
115
123
  is complete or its sources are true. Bounded graph expansion is on by default;
116
124
  set `graph: false` or `noGraph: true` only for an explicit BM25/vector-only path.
125
+ To search several collections, pass `collections: ["a", "b"]` to `gno_query`,
126
+ `gno_search` or `gno_vsearch` instead of `collection` (not both).
117
127
  Check `gno_peek` first for counts, backlog, serve liveness, or recent files.
118
128
  Use `gno_status` for activation, onboarding, or heavy health (missing vectors,
119
129
  stale embeddings). Use `gno_query_diagnose` when a known target document
@@ -251,6 +261,12 @@ before calling.
251
261
  `not_found`, so do not blindly resend an uncertain old write as new.
252
262
  - Errors arrive as tool text `CODE: message` with `structuredContent.error`.
253
263
 
264
+ ## Audits
265
+
266
+ `gno_audit` returns the same report as `gno audit`. Collection `audit` config
267
+ adds `links.collection-policy`, `links.external-url` and `links.path-layout`
268
+ findings with no new tool inputs; they report structure, not egress.
269
+
254
270
  ## Uninstall
255
271
 
256
272
  ```bash
Binary file
@@ -0,0 +1 @@
1
+ aca4d00878cb85d4f57181425caf0bb2175ecec0a5bc4ea437e994b8fd42c9cc gno-browser-clipper-v2.14.0.zip
@@ -21,5 +21,5 @@
21
21
  "content_security_policy": {
22
22
  "extension_pages": "script-src 'self'; object-src 'none'; connect-src http://127.0.0.1:*"
23
23
  },
24
- "version": "2.12.2"
24
+ "version": "2.14.0"
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "2.12.2",
3
+ "version": "2.14.0",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
package/spec/cli.md CHANGED
@@ -91,6 +91,7 @@ equivalent files fail closed as ambiguous.
91
91
  | sessions | yes | no | no | no | no | terminal |
92
92
  | get | yes | no | no | yes | no | terminal |
93
93
  | multi-get | yes | yes | no | yes | no | terminal |
94
+ | convert | yes | no | no | no | no | Markdown |
94
95
  | ls | yes | yes | no | yes | no | terminal |
95
96
  | daemon | yes¹ | no | no | no | no | terminal |
96
97
  | context add | no | no | no | no | no | terminal |
@@ -190,7 +191,11 @@ through a resumable operation. It can initialize a missing user config. Omitted
190
191
  collection-scoped contexts, and content-type rules remain untouched; stale
191
192
  same-path collections are reported as skipped and retained. A stale same-name
192
193
  collection root is repaired in place without deleting its collection or index
193
- identity. Apply also updates a timestamp-free `projectProfileBindings` record
194
+ identity. A re-applied collection keeps every setting the profile does not
195
+ own (for example `egressPolicy`, `egressPolicyRevision`, `sourceAvailability`,
196
+ `workspaceRoot`, `memoryManaged`, `audit` and `updateCmd`). When a repaired root
197
+ falls outside an explicit `workspaceRoot`, apply fails with
198
+ `WORKSPACE_ROOT_CONFLICT` (exit 1) before any config write. Apply also updates a timestamp-free `projectProfileBindings` record
194
199
  in the local user config with the canonical absolute profile path, profile
195
200
  fingerprint, and projected collection. The public `profile_binding` resource
196
201
  receipt identifies only the collection and never exposes that local path.
@@ -892,6 +897,12 @@ List all configured collections.
892
897
  gno collection list [--json|--md]
893
898
  ```
894
899
 
900
+ Each JSON item is the configured collection plus `effectiveWorkspaceRoot` and
901
+ `workspaceSource`. A collection with an `audit` block lists it unchanged
902
+ (`linkCollections`, `externalUrls`, `paths`); see
903
+ [gno audit](#gno-audit). Schema:
904
+ [`collection-list@1.0`](./output-schemas/collection-list.schema.json).
905
+
895
906
  ---
896
907
 
897
908
  ### gno collection clear-embeddings
@@ -1045,6 +1056,32 @@ target missing from the listing stays unresolved; an unreadable folder or the
1045
1056
  file bound makes the listing incomplete, which the `links.outside-index` rule
1046
1057
  message states once.
1047
1058
 
1059
+ **Opt-in collection rules.** A collection's optional `audit` config object
1060
+ adds three report-only `links` rules. Each is emitted only when an audited
1061
+ collection (the `--collection` filter, else every collection) configures it,
1062
+ so without `audit` the rule list, findings, fingerprints and exit codes are
1063
+ unchanged. The config fingerprint includes a collection's `audit` object only
1064
+ when present on an audited collection.
1065
+
1066
+ | Rule id | Enabled by | Reports |
1067
+ | ------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------- |
1068
+ | `links.collection-policy` | `audit.linkCollections` | A link that resolves into a collection the list does not name |
1069
+ | `links.external-url` | `audit.externalUrls: report` | An external URL written in a document, or a document whose indexed text is unavailable (`unchecked`) |
1070
+ | `links.path-layout` | `audit.paths` | A document whose collection-relative path matches none of the globs |
1071
+
1072
+ Findings are `warning`s with location evidence: `collection-policy` (detail
1073
+ `sourceCollection`, `targetCollection`, `target`, `linkType`, `allowed`) at the
1074
+ link's line and column, `external-url` at the URL's line and column (or
1075
+ `external-url-unchecked` with no location), and `path-layout` (detail
1076
+ `paths`). Only links that resolve to exactly one document are policy findings.
1077
+ External URLs are `scheme://` URLs in the document's indexed text outside
1078
+ frontmatter, code and HTML comments; `gno://` and `file://` are local. Path
1079
+ globs use `pattern` syntax against `recordSourcePath ?? relPath`. A truncated
1080
+ snapshot makes all three `inconclusive`. Config validation: `linkCollections`
1081
+ members must name configured collections, `externalUrls` is `report` or
1082
+ `ignore`, and `paths` is 1-64 relative globs without `..` and with balanced
1083
+ `{}` / `[]`.
1084
+
1048
1085
  The wiki link parser reads Obsidian's table-escaped alias `[[Note\|Alias]]`
1049
1086
  as target `Note` with alias `Alias`. Markdown link text may contain balanced
1050
1087
  square brackets (`[see [1]](note.md)`); a destination containing a square
@@ -1245,13 +1282,14 @@ Build or update the index end-to-end (update + embed).
1245
1282
  **Synopsis:**
1246
1283
 
1247
1284
  ```bash
1248
- gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes] [--lock-wait <duration>] [--no-wait]
1285
+ gno index [collection] [--path <path...>] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes] [--lock-wait <duration>] [--no-wait]
1249
1286
  ```
1250
1287
 
1251
1288
  **Options:**
1252
1289
  | Option | Type | Description |
1253
1290
  |--------|------|-------------|
1254
1291
  | `--collection` | string | Scope to single collection |
1292
+ | `--path <path...>` | string[] | Reindex only these files of the collection (relative to its root, or absolute inside it); at most 256, not directories; requires a collection, not combinable with `--git-pull` |
1255
1293
  | `--no-embed` | boolean | Run ingestion only, skip embedding |
1256
1294
  | `--models-pull` | boolean | Download models if missing (prompts unless `--yes`) |
1257
1295
  | `--git-pull` | boolean | Run `git pull` in git repositories |
@@ -1264,6 +1302,7 @@ gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes
1264
1302
 
1265
1303
  - Runs two separable stages: `lexical` (`update`) then `embed`
1266
1304
  - With `--no-embed`, runs the `lexical` stage only (embed stage `skipped`)
1305
+ - With `--path`, the `lexical` stage re-reads only the listed files and marks a listed file that no longer exists inactive; the collection's update command does not run. A path outside the collection root (including through a symlink), `--path` without a collection, or `--path` with `--git-pull` exits 1 (`VALIDATION`) before anything is written
1267
1306
  - Waits by default for the shared write lease; `--no-wait` fails immediately with exit 4
1268
1307
 
1269
1308
  **Staged, resumable contract:** each stage persists its own progress inside
@@ -1472,7 +1511,7 @@ BM25 keyword search over indexed documents.
1472
1511
  **Synopsis:**
1473
1512
 
1474
1513
  ```bash
1475
- gno search [query] [--query-file <path>] [-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]
1514
+ gno search [query] [--query-file <path>] [-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]
1476
1515
  ```
1477
1516
 
1478
1517
  **Arguments:**
@@ -1488,7 +1527,7 @@ gno search [query] [--query-file <path>] [-n <num>] [--min-score <num>] [-c <col
1488
1527
  | `--query-file` | string | none | Read the query from a file; `-` reads stdin so the query never appears on argv. Do not also pass a positional query. |
1489
1528
  | `-n` | integer | 5 (20 for --json/--files) | Max results |
1490
1529
  | `--min-score` | number | 0 | Minimum score threshold |
1491
- | `-c, --collection` | string | all | Filter to collection |
1530
+ | `-c, --collection` | string[] | all | Filter to collection; repeat to search several collections (at most 256) |
1492
1531
  | `--since` | string | none | Modified-at lower bound (ISO date/time or relative token) |
1493
1532
  | `--until` | string | none | Modified-at upper bound (ISO date/time or relative token) |
1494
1533
  | `--category` | string | none | Filter to docs with matching category/content type (comma-separated) |
@@ -1621,6 +1660,19 @@ gno search "deploy staging" -n 10 --collection work
1621
1660
  gno search "contract" --json | jq '.[] | .uri'
1622
1661
  ```
1623
1662
 
1663
+ **Collection scope (`search`, `vsearch`, `query`):** one `-c` filters to that
1664
+ collection. Repeating `-c` (`-c work -c notes`) searches only those
1665
+ collections; the filter applies before every candidate limit, so other
1666
+ collections never take result slots, graph neighbours or rerank budget. Names
1667
+ are trimmed, lowercased, de-duplicated and sorted, and `--json` echoes them as
1668
+ `meta.collections` (`meta.collection` stays the single-collection field). More
1669
+ than 256 names after normalization exits 1 (`VALIDATION`); names that are not
1670
+ configured exit 2 with `Collection not found: <name>` or
1671
+ `Collections not found: <a>, <b>`, the same as a single unknown `-c`. When the
1672
+ listed collections resolve different embed, expand or rerank models, that role
1673
+ uses the unscoped default; when they agree, their shared model is used.
1674
+ `gno query diagnose` accepts one `-c` and exits 1 when given several.
1675
+
1624
1676
  ---
1625
1677
 
1626
1678
  Keyword retrieval applies collection/path scope, caller allowlists, tags, modified-date bounds, category and author filters, managed-memory visibility, and whole-document exclusions before its ranked candidate limit. Higher-ranked ineligible documents cannot consume that window; fewer eligible matches still produce a short result. Existing BM25 weights, query syntax, recency/project-affinity reranking and minimum-score behavior are unchanged.
@@ -1632,7 +1684,7 @@ Vector semantic search over indexed documents.
1632
1684
  **Synopsis:**
1633
1685
 
1634
1686
  ```bash
1635
- 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]
1687
+ 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]
1636
1688
  ```
1637
1689
 
1638
1690
  **Options:** Same as `gno search` (including temporal/category/author, tag, and
@@ -1677,7 +1729,7 @@ Hybrid search combining BM25 and vector retrieval with optional expansion and re
1677
1729
  **Synopsis:**
1678
1730
 
1679
1731
  ```bash
1680
- gno query [query...] [--query-file <path>] [-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]
1732
+ gno query [query...] [--query-file <path>] [-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]
1681
1733
  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]
1682
1734
  ```
1683
1735
 
@@ -2496,6 +2548,51 @@ See [Output Schemas](./output-schemas/multi-get.schema.json)
2496
2548
 
2497
2549
  ---
2498
2550
 
2551
+ ### gno convert
2552
+
2553
+ Print the Markdown GNO would index for one file. The file goes through the
2554
+ same conversion path as indexing (MIME detection, converter, canonical
2555
+ Markdown, mirror hash) with the default indexing limits, so the output equals
2556
+ the mirror `gno update` stores for the same bytes. No config, index, store or
2557
+ cache is read or written, and the command works with no GNO config present.
2558
+
2559
+ **Synopsis:**
2560
+
2561
+ ```bash
2562
+ gno convert <file> [--json]
2563
+ ```
2564
+
2565
+ **Arguments:**
2566
+ | Arg | Type | Description |
2567
+ |-----|------|-------------|
2568
+ | `<file>` | path | One regular file (a symlink to a regular file is followed) |
2569
+
2570
+ **Output:**
2571
+
2572
+ - Terminal: exactly the Markdown on stdout; conversion warnings go to stderr.
2573
+ - `--json`: [convert@1.0](./output-schemas/convert.schema.json) with `source`
2574
+ (`path`, `mime`, `ext`, `sizeBytes`), `converter` (`id`, `version`),
2575
+ `title`, `languageHint`, `markdown`, `mirrorHash` (SHA-256 hex, as stored by
2576
+ indexing) and `warnings`.
2577
+
2578
+ Single-document formats are supported. Formats whose adapters split one file
2579
+ into several records (mail archives, calendars and the other record adapters)
2580
+ are refused, because their record identity comes from the collection and path.
2581
+
2582
+ **Errors:** the standard error envelope, with `details.reason`:
2583
+
2584
+ | Case | `details.reason` | Exit |
2585
+ | ------------------------------------------------------------ | -------------------------------------------------------------- | ---- |
2586
+ | Path missing, a directory or another non-regular file | `NOT_FOUND` | 1 |
2587
+ | Larger than the indexing size limit (checked before reading) | `TOO_LARGE` | 1 |
2588
+ | No converter for the type | `UNSUPPORTED` | 1 |
2589
+ | Multi-record format (`details.format` names it) | `MULTI_DOCUMENT` | 1 |
2590
+ | Converter failure, timeout, memory limit or corrupt input | the converter's code (`CORRUPT`, `PERMISSION`, `TIMEOUT`, ...) | 2 |
2591
+
2592
+ The SDK exposes the same operation as `convertFile(path)`.
2593
+
2594
+ ---
2595
+
2499
2596
  ### gno ls
2500
2597
 
2501
2598
  List documents in a collection or prefix.
@@ -2503,7 +2600,7 @@ List documents in a collection or prefix.
2503
2600
  **Synopsis:**
2504
2601
 
2505
2602
  ```bash
2506
- gno ls [<scope>] [--json|--files|--md]
2603
+ gno ls [<scope>] [--filter <json>] [-n <num>] [--offset <num>] [--json|--files|--md]
2507
2604
  ```
2508
2605
 
2509
2606
  **Arguments:**
@@ -2511,23 +2608,49 @@ gno ls [<scope>] [--json|--files|--md]
2511
2608
  |-----|------|-------------|
2512
2609
  | `<scope>` | string | Collection name or `gno://collection/prefix` (default: all) |
2513
2610
 
2514
- **Output (JSON):**
2611
+ **Options:**
2612
+ | Flag | Type | Description |
2613
+ |------|------|-------------|
2614
+ | `--filter` | JSON | Typed metadata predicate; same schema and semantics as `gno search --filter` |
2615
+ | `-n, --limit` | integer | Max documents (default 20) |
2616
+ | `--offset` | integer | Skip the first N documents |
2617
+
2618
+ Active documents are listed in URI order. With `--filter`, only documents whose
2619
+ typed metadata satisfies the predicate are listed; documents with invalid or
2620
+ not-yet-extracted metadata never match, including under negated predicates.
2621
+ The filter and the scope both apply, before pagination: `limit`, `offset` and
2622
+ `meta.total` count matching documents only. Without `--filter`, output is
2623
+ unchanged.
2624
+
2625
+ **Output (JSON):** [`ls@1.0`](output-schemas/ls.schema.json)
2515
2626
 
2516
2627
  ```json
2517
- [
2518
- {
2519
- "docid": "#a1b2c3d4",
2520
- "uri": "gno://work/doc.md",
2521
- "title": "Document Title",
2522
- "source": { "relPath": "doc.md", "mime": "text/markdown", "ext": ".md" }
2628
+ {
2629
+ "documents": [
2630
+ {
2631
+ "docid": "#a1b2c3d4",
2632
+ "uri": "gno://work/doc.md",
2633
+ "title": "Document Title",
2634
+ "source": { "relPath": "doc.md", "mime": "text/markdown", "ext": ".md" }
2635
+ }
2636
+ ],
2637
+ "meta": {
2638
+ "total": 12,
2639
+ "returned": 1,
2640
+ "offset": 0,
2641
+ "warnings": [{ "code": "METADATA_COVERAGE_INCOMPLETE", "message": "..." }]
2523
2642
  }
2524
- ]
2643
+ }
2525
2644
  ```
2526
2645
 
2646
+ `meta.warnings` appears only with `--filter` when documents in scope were
2647
+ skipped because their metadata is invalid or pending re-ingestion. Terminal,
2648
+ `--files` and `--md` output print the warning on stderr.
2649
+
2527
2650
  **Exit Codes:**
2528
2651
 
2529
2652
  - 0: Success
2530
- - 1: Invalid scope
2653
+ - 1: Invalid scope or invalid `--filter` (`VALIDATION`, same message as `gno search --filter`)
2531
2654
  - 2: DB failure
2532
2655
 
2533
2656
  ---