@gmickel/gno 1.25.1 → 1.26.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 (56) hide show
  1. package/README.md +10 -4
  2. package/assets/skill/SKILL.md +29 -17
  3. package/browser-extension/artifacts/{gno-browser-clipper-v1.25.1.zip → gno-browser-clipper-v1.26.0.zip} +0 -0
  4. package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip.sha256 +1 -0
  5. package/browser-extension/dist/manifest.json +1 -1
  6. package/package.json +1 -1
  7. package/spec/cli.md +146 -4
  8. package/spec/db/schema.sql +1 -1
  9. package/spec/output-schemas/project-profile-apply.schema.json +209 -0
  10. package/spec/output-schemas/project-profile-command.schema.json +160 -0
  11. package/spec/output-schemas/query-diagnose.schema.json +7 -1
  12. package/spec/output-schemas/setup-profile-result.schema.json +87 -0
  13. package/spec/project-profile.schema.json +301 -0
  14. package/src/cli/commands/collection/add.ts +39 -45
  15. package/src/cli/commands/collection/remove.ts +28 -28
  16. package/src/cli/commands/collection/rename.ts +55 -73
  17. package/src/cli/commands/context/add.ts +37 -26
  18. package/src/cli/commands/context/rm.ts +47 -20
  19. package/src/cli/commands/init.ts +55 -125
  20. package/src/cli/commands/models/use.ts +43 -38
  21. package/src/cli/commands/profile-apply.ts +334 -0
  22. package/src/cli/commands/profile.ts +409 -0
  23. package/src/cli/commands/setup-activation.ts +205 -54
  24. package/src/cli/commands/setup-profile.ts +223 -0
  25. package/src/cli/commands/setup.ts +3 -0
  26. package/src/cli/program.ts +110 -9
  27. package/src/config/index.ts +3 -0
  28. package/src/config/project-profile.ts +367 -0
  29. package/src/config/saver.ts +16 -7
  30. package/src/config/types.ts +42 -0
  31. package/src/core/config-mutation.ts +138 -76
  32. package/src/core/config-write-lock.ts +89 -0
  33. package/src/core/context-identity.ts +16 -0
  34. package/src/core/context-resolver.ts +2 -12
  35. package/src/core/folder-setup-planning.ts +6 -21
  36. package/src/core/folder-setup.ts +30 -2
  37. package/src/core/path-rules.ts +53 -0
  38. package/src/core/project-affinity-surface.ts +102 -7
  39. package/src/core/project-profile-apply-state.ts +268 -0
  40. package/src/core/project-profile-apply-validation.ts +95 -0
  41. package/src/core/project-profile-apply.ts +408 -0
  42. package/src/core/project-profile-canonical.ts +71 -0
  43. package/src/core/project-profile-diff.ts +302 -0
  44. package/src/core/project-profile-discovery.ts +519 -0
  45. package/src/core/project-profile-file.ts +37 -0
  46. package/src/core/project-profile-parser.ts +98 -0
  47. package/src/core/project-profile.ts +490 -0
  48. package/src/ingestion/walker.ts +85 -44
  49. package/src/llm/cache.ts +21 -0
  50. package/src/serve/config-sync.ts +2 -2
  51. package/src/serve/resident-runtime.ts +1 -0
  52. package/src/serve/routes/api.ts +1 -0
  53. package/src/store/migrations/021-multi-context-identity.ts +37 -0
  54. package/src/store/migrations/index.ts +2 -0
  55. package/src/store/sqlite/adapter.ts +83 -0
  56. package/browser-extension/artifacts/gno-browser-clipper-v1.25.1.zip.sha256 +0 -1
package/README.md CHANGED
@@ -18,9 +18,10 @@
18
18
  GNO is a local knowledge engine for notes, code, PDFs, Office docs, meeting transcripts, and reference material. It gives you fast keyword search, semantic retrieval, grounded answers with citations, wiki-style linking, and a real workspace UI, while keeping the whole stack local by default.
19
19
 
20
20
  CLI retrieval also uses the current repository/workspace as a transparent soft
21
- ranking signal. A trusted local cwd or repeatable `--project-root` can add at
22
- most `+0.03` to matching collection results; `--no-project-affinity` disables
23
- it, and explicit roots replace cwd inference. It never overrides collection,
21
+ ranking signal. A trusted explicit `--project-root`, nearest valid
22
+ `.gno/index.yml`, or local cwd can add at most `+0.03` to matching collection
23
+ results, in that precedence order; `--no-project-affinity` disables it. Profile
24
+ defaults stay project-local and never overwrite the user default. It never overrides collection,
24
25
  tag, date, exclude, or egress filters. SDK, REST, and MCP `projectHints` are
25
26
  opaque, untrusted, limited to 16, and intentionally have zero ranking effect:
26
27
  those surfaces never probe caller or server filesystem paths. Trusted local
@@ -53,6 +54,10 @@ bun install -g @gmickel/gno
53
54
  # Prove the first folder immediately; semantic work continues independently
54
55
  gno setup ~/notes --name notes
55
56
 
57
+ # In a repository with .gno/index.yml: preview, apply, and prove its collection
58
+ gno profile diff
59
+ gno setup . --apply-profile
60
+
56
61
  # Add more collections with the granular commands
57
62
  gno collection add ~/work/docs --name work-docs --pattern "**/*.{md,pdf,docx}"
58
63
  gno collection add ~/work/gno/src --name gno-code --pattern "**/*.{ts,tsx,js,jsx}"
@@ -107,7 +112,7 @@ gno daemon --detach # headless continuous indexing (background; --status / --st
107
112
 
108
113
  <!-- public-truth:current-version -->
109
114
 
110
- > Current release: **v1.25.1** — see [CHANGELOG.md](./CHANGELOG.md)
115
+ > Current release: **v1.26.0** — see [CHANGELOG.md](./CHANGELOG.md)
111
116
 
112
117
  <!-- /public-truth -->
113
118
 
@@ -194,6 +199,7 @@ Model guides:
194
199
  - [Code Embeddings](./docs/guides/code-embeddings.md)
195
200
  - [Per-Collection Models](./docs/guides/per-collection-models.md)
196
201
  - [Bring Your Own Models](./docs/guides/bring-your-own-models.md)
202
+ - [Project-Local Retrieval Profiles](./docs/guides/project-profiles.md)
197
203
 
198
204
  ### Fine-Tuned Model Quick Use
199
205
 
@@ -37,6 +37,11 @@ gno search "your query" # BM25 keyword search
37
37
  `gno setup` is the default activation path. It is idempotent, returns only
38
38
  after exact lexical proof, and runs directly without resident/Web/MCP
39
39
  attachment. Use `--no-semantic` to start no worker and record skipped state.
40
+ Inside a repository with `.gno/index.yml`, setup inspects the optional profile
41
+ before mutation. Run `gno profile diff`, then
42
+ `gno setup . --apply-profile` to apply its portable collection/context/content
43
+ rules before setup proves retrieval. Missing/invalid profiles keep ordinary
44
+ setup usable; no profile is applied implicitly.
40
45
  Use repeatable `--connector` with `claude-code-skill`,
41
46
  `claude-desktop-mcp`, `cursor-mcp`, `codex-skill`, `opencode-skill`,
42
47
  `openclaw-skill`, or `hermes-skill`. Connector skips/failures can return
@@ -70,22 +75,22 @@ Recipe rules:
70
75
 
71
76
  ## Command Overview
72
77
 
73
- | Category | Commands | Description |
74
- | ------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
75
- | **Search** | `search`, `vsearch`, `query`, `ask` | Find documents by keywords, meaning, or get AI answers |
76
- | **Links** | `links`, `backlinks`, `similar`, `graph`, `graph query` | Navigate document relationships and typed connections |
77
- | **Retrieve** | `get`, `multi-get`, `ls` | Fetch document content by URI or ID |
78
- | **Index** | `setup`, `init`, `collection add/list/remove`, `index`, `update`, `embed` | Prove first retrieval, then maintain the document index |
79
- | **Tags** | `tags`, `tags add`, `tags rm` | Organize and filter documents |
80
- | **Context** | `context add/list/rm/check/build/verify/watch/watches/reverify/unwatch` | Configure guidance or compile, verify, and watch saved evidence Capsules |
81
- | **Changes** | `changes`, `diff`, `impact` | Inspect bounded metadata history and dependency impact |
82
- | **Traces** | `trace list/show/label/export/replay/delete/purge` | Manage and replay private retrieval receipts |
83
- | **Models** | `models list/use/pull/clear/path` | Manage local AI models |
84
- | **Serve** | `serve`, `daemon` | One resident Web/headless gateway and watcher |
85
- | **Publish** | `publish export` | Export gno.sh publish artifacts |
86
- | **MCP** | `mcp`, `mcp install/uninstall/status` | AI assistant integration |
87
- | **Skill** | `skill install/uninstall/show/paths` | Install skill for AI agents |
88
- | **Admin** | `status`, `doctor`, `cleanup`, `reset`, `vec`, `completion` | Maintenance and diagnostics |
78
+ | Category | Commands | Description |
79
+ | ------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
80
+ | **Search** | `search`, `vsearch`, `query`, `ask` | Find documents by keywords, meaning, or get AI answers |
81
+ | **Links** | `links`, `backlinks`, `similar`, `graph`, `graph query` | Navigate document relationships and typed connections |
82
+ | **Retrieve** | `get`, `multi-get`, `ls` | Fetch document content by URI or ID |
83
+ | **Index** | `setup`, `profile check/show/diff/apply`, `init`, `collection add/list/remove`, `index`, `update`, `embed` | Reproduce profile intent, prove retrieval, then maintain the index |
84
+ | **Tags** | `tags`, `tags add`, `tags rm` | Organize and filter documents |
85
+ | **Context** | `context add/list/rm/check/build/verify/watch/watches/reverify/unwatch` | Configure guidance or compile, verify, and watch saved evidence Capsules |
86
+ | **Changes** | `changes`, `diff`, `impact` | Inspect bounded metadata history and dependency impact |
87
+ | **Traces** | `trace list/show/label/export/replay/delete/purge` | Manage and replay private retrieval receipts |
88
+ | **Models** | `models list/use/pull/clear/path` | Manage local AI models |
89
+ | **Serve** | `serve`, `daemon` | One resident Web/headless gateway and watcher |
90
+ | **Publish** | `publish export` | Export gno.sh publish artifacts |
91
+ | **MCP** | `mcp`, `mcp install/uninstall/status` | AI assistant integration |
92
+ | **Skill** | `skill install/uninstall/show/paths` | Install skill for AI agents |
93
+ | **Admin** | `status`, `doctor`, `cleanup`, `reset`, `vec`, `completion` | Maintenance and diagnostics |
89
94
 
90
95
  ## Search Modes
91
96
 
@@ -119,12 +124,19 @@ Recipe rules:
119
124
  --no-project-affinity Disable trusted local project-aware ranking
120
125
  ```
121
126
 
122
- CLI searches use the current repository/worktree as a soft signal by default.
127
+ CLI searches use explicit `--project-root`, the nearest valid compiled project
128
+ profile, then the current repository/worktree, in that precedence order.
123
129
  A matching collection can receive at most `+0.03`; roots never stack, all
124
130
  auxiliary signals share `±0.08`, and collection/tag/date/exclude/egress filters
125
131
  stay hard. Use `--project-root` for explicit trusted roots or
126
132
  `--no-project-affinity` to disable it.
127
133
 
134
+ Profile affinity defaults are request-local. `gno profile apply` never
135
+ overwrites the user's global `projectAffinity` default, so one repository
136
+ cannot change another repository's fallback. Explain/diagnose identify this
137
+ trusted source as `project_profile`; contexts, content types, source metadata,
138
+ and document fields never become project identity.
139
+
128
140
  Do not treat MCP/SDK/REST `projectHints` as paths. They are opaque, untrusted,
129
141
  limited to 16, never trigger filesystem probing, and currently produce zero
130
142
  affinity. Explain uses redacted aliases only. Diagnose preserves exact closed
@@ -0,0 +1 @@
1
+ a0cf108985e47f6e36026b20538fd95b9fac9d908cf1a13eb77aedcb38a49643 gno-browser-clipper-v1.26.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": "1.25.1"
24
+ "version": "1.26.0"
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "1.25.1",
3
+ "version": "1.26.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
@@ -65,6 +65,10 @@ equivalent files fail closed as ambiguous.
65
65
  | status | yes | no | no | yes | no | terminal |
66
66
  | init | no | no | no | no | no | terminal |
67
67
  | setup | yes | no | no | no | no | terminal |
68
+ | profile check | yes | no | no | no | no | terminal |
69
+ | profile show | yes | no | no | no | no | terminal |
70
+ | profile diff | yes | no | no | no | no | terminal |
71
+ | profile apply | yes | no | no | no | no | terminal |
68
72
  | collection add | no | no | no | no | no | terminal |
69
73
  | collection list | yes | no | no | yes | no | terminal |
70
74
  | collection remove | no | no | no | no | no | terminal |
@@ -125,6 +129,89 @@ equivalent files fail closed as ambiguous.
125
129
 
126
130
  ## Commands
127
131
 
132
+ ### gno profile
133
+
134
+ Inspect a repository-owned `.gno/index.yml` retrieval profile without changing
135
+ the user config, index database, model cache, or tracked files.
136
+
137
+ **Synopsis:**
138
+
139
+ ```bash
140
+ gno profile check [path] [--json]
141
+ gno profile show [path] [--json]
142
+ gno profile diff [path] [--json]
143
+ gno profile apply [path] [--json]
144
+ ```
145
+
146
+ With no path, discovery starts at the canonical current directory and walks
147
+ upward. Each directory is checked before its parent, so the nearest nested
148
+ profile wins and any ancestor profile is reported as shadowed; profiles are
149
+ never merged. Discovery stops after checking the first Git root (`.git` may be a
150
+ directory or a worktree file), before crossing a filesystem device boundary,
151
+ or at the filesystem root. This gives a nested repository precedence over its
152
+ parent repository and lets a monorepo subtree intentionally shadow the root
153
+ profile.
154
+
155
+ An explicit directory path is an exact profile-root override: only
156
+ `<path>/.gno/index.yml` is considered. An explicit
157
+ `<path>/.gno/index.yml` file selects that exact profile. Explicit overrides do
158
+ not fall back to ancestors. The profile file and its `.gno` path must resolve
159
+ inside the selected canonical profile root; symlink escapes fail closed.
160
+ Remote callers cannot enable discovery or cause filesystem probes.
161
+
162
+ `check` validates discovery, schema, referenced paths, local preset aliases,
163
+ and, with global `--offline`, exact cache availability without downloading.
164
+ `show` returns the same receipt plus normalized portable desired state. `diff`
165
+ compares that desired state with the selected user config. It reports stale
166
+ same-name/path mappings and explicit repair/removal choices but never applies
167
+ either choice. A missing user config is treated as empty desired-state input;
168
+ an unreadable or invalid config is an actionable validation diagnostic.
169
+
170
+ The three read-only JSON forms use
171
+ [`project-profile-command@1.0`](./output-schemas/project-profile-command.schema.json).
172
+ Receipts contain no absolute profile, config, database, cache, or model paths,
173
+ no timestamps, and no model URIs. Diagnostics and changes are canonically
174
+ ordered, so identical local state produces byte-identical JSON.
175
+
176
+ `apply` rebuilds the shared diff against config reloaded inside a cross-process
177
+ runtime lock, then creates or updates only resources declared by the profile
178
+ through a resumable operation. It can initialize a missing user config. Omitted collections,
179
+ collection-scoped contexts, and content-type rules remain untouched; stale
180
+ same-path collections are reported as skipped and retained. A stale same-name
181
+ collection root is repaired in place without deleting its collection or index
182
+ identity. Apply also updates a timestamp-free `projectProfileBindings` record
183
+ in the local user config with the canonical absolute profile path, profile
184
+ fingerprint, and projected collection. The public `profile_binding` resource
185
+ receipt identifies only the collection and never exposes that local path.
186
+ Model preset aliases resolve to collection-local model overrides.
187
+ Apply synchronizes the config projection but does not index documents; changed
188
+ collections appear in `pendingIndexing`.
189
+ `affinityDefaults` remains profile-scoped and is compiled at trusted local
190
+ retrieval time; apply does not overwrite the user config `projectAffinity`
191
+ default. Its apply resource is therefore `project_affinity/profile/skipped`,
192
+ while the nearest valid profile can still supply request-local affinity.
193
+
194
+ The deterministic JSON result uses
195
+ [`project-profile-apply@1.0`](./output-schemas/project-profile-apply.schema.json).
196
+ Its created/reused/updated/skipped resource receipt is also atomically saved as
197
+ `project-profiles/apply-receipt.json` under the user data directory. The config,
198
+ index, receipt, and lock paths must all remain outside the selected profile
199
+ root. The tracked `.gno/index.yml` is never written. Interrupted or concurrent
200
+ applies resume from fresh config state and converge idempotently.
201
+
202
+ **Exit Codes:**
203
+
204
+ - `0`: profile found and valid (`diff` may still report changes)
205
+ - `1`: profile missing, ambiguous discovery could not be resolved safely, or
206
+ profile/config validation failed, including runtime-path overlap
207
+ - `2`: unexpected local I/O, lock, receipt, or index-store failure
208
+
209
+ `gno setup` calls the same local `check` composition before its folder
210
+ transaction. Inspection is read-only and non-fatal. Plain setup prints bounded
211
+ preview/apply guidance but preserves the existing setup result contract.
212
+ Explicit `--apply-profile` runs the same lock-safe apply path before lexical
213
+ setup and emits `setup-profile-result@1.0`.
214
+
128
215
  ### gno status
129
216
 
130
217
  Display index status and health information.
@@ -365,7 +452,7 @@ to one standalone background worker and never delays lexical success.
365
452
  ```bash
366
453
  gno setup <folder> [-n|--name <name>] [--exclude <pattern>]...
367
454
  [--authorize-secret-risk] [--connector <id>]...
368
- [--no-semantic] [--json]
455
+ [--apply-profile] [--no-semantic] [--json]
369
456
  ```
370
457
 
371
458
  **Options:**
@@ -376,6 +463,7 @@ gno setup <folder> [-n|--name <name>] [--exclude <pattern>]...
376
463
  | `--exclude <pattern>` | repeatable | core defaults | One literal exclusion per occurrence; never CSV |
377
464
  | `--authorize-secret-risk` | boolean | false | Explicitly authorize likely credentials, private keys, or env files |
378
465
  | `--connector <id>` | repeatable | none | Install or reuse and verify one supported connector after lexical proof |
466
+ | `--apply-profile` | boolean | false | Apply a valid discovered project profile before lexical setup |
379
467
  | `--no-semantic` | boolean | false | Prove lexical retrieval but record semantic work as skipped |
380
468
  | `--json` | boolean | false | Emit one closed setup result object |
381
469
 
@@ -392,6 +480,53 @@ exact `activation.evidence.resultUri`. Terminal stage progress uses stderr;
392
480
  `--quiet` suppresses progress but not the final result. JSON writes exactly one
393
481
  canonical result to stdout on both success and domain failure, with no progress.
394
482
 
483
+ Before the folder transaction, setup performs the same local read-only
484
+ `gno profile check` composition from the supplied folder. Missing and invalid
485
+ profiles remain optional and never block ordinary setup. Terminal output shows
486
+ a valid profile fingerprint plus `profile diff`/`--apply-profile` guidance, or
487
+ an invalid-profile diagnostic, before setup mutates local state.
488
+
489
+ `--apply-profile` is explicit. When the check is valid, setup runs the existing
490
+ cross-process lock-safe, create/update-only `profile apply` path first, then
491
+ uses the applied profile collection's canonical root, name, and filters for
492
+ lexical setup. A nested folder invocation therefore indexes the declared
493
+ profile root rather than creating a duplicate subdirectory collection. The
494
+ profile never deletes omitted resources or overwrites the user-level
495
+ `projectAffinity` default.
496
+
497
+ Once a valid profile has been discovered for an explicit `--apply-profile`
498
+ request, apply is a fail-closed prerequisite. A validation failure returns exit
499
+ 1; an apply I/O/lock/store failure, thrown apply transport, missing result, or
500
+ malformed success receipt returns exit 2. In every case setup aborts before its
501
+ folder transaction or connector work. A late apply failure may have persisted
502
+ resumable create/update-only profile state, but cannot trigger ordinary setup
503
+ reconciliation. The outer result is `failed`, the nested lexical error code is
504
+ `profile_apply_failed`, and `profile.apply` retains a returned apply result or
505
+ is `null` when apply produced no result.
506
+
507
+ Missing or invalid profile inspection remains the optional fallback described
508
+ below. A thrown inspection transport/runtime failure for an explicit
509
+ `--apply-profile` request instead returns exit 2 with
510
+ `profile_inspection_failed` before apply or setup mutation. Because no trusted
511
+ profile check result exists, this failure uses the unchanged
512
+ `setup-command-result@1.0` shape rather than fabricating `profile.check`.
513
+
514
+ For a valid profile, `--apply-profile` is mutually exclusive with explicit
515
+ `--name` and `--exclude` values. A conflict returns exit 1 with
516
+ `profile_option_conflict` before config, store, or index mutation. Profile
517
+ include/exclude values use validated Bun-glob semantics; plain exclusion
518
+ components retain component matching. Brace alternatives are rejected; express
519
+ their branches as separate include/exclude entries. Discovery permission/I/O
520
+ failures return exit 2, while missing, disabled, invalid, and unsafe profiles
521
+ return exit 1.
522
+
523
+ Opt-in JSON uses
524
+ [`setup-profile-result@1.0`](./output-schemas/setup-profile-result.schema.json)
525
+ and includes the closed `profile.check`, nullable `profile.apply`, unchanged
526
+ setup result, and connectors. A missing/invalid profile keeps setup usable and
527
+ returns `completed_with_actions` with `profile.apply: null`. Without
528
+ `--apply-profile`, the existing setup/activation JSON bytes remain unchanged.
529
+
395
530
  After lexical proof, the command records one private atomic
396
531
  `setup-semantic@1.0` receipt per canonical index/folder and starts one detached,
397
532
  collection-scoped Bun worker. A matching live worker is reused; a dead worker is
@@ -430,6 +565,7 @@ second connector fingerprint or cache.
430
565
 
431
566
  - [`setup-command-result@1.0`](./output-schemas/setup-command-result.schema.json)
432
567
  - [`setup-activation-result@1.0`](./output-schemas/setup-activation-result.schema.json)
568
+ - [`setup-profile-result@1.0`](./output-schemas/setup-profile-result.schema.json)
433
569
  - [`setup-semantic@1.0`](./output-schemas/setup-semantic-receipt.schema.json)
434
570
  - [`FolderSetupReceipt@1.0`](./output-schemas/setup-receipt.schema.json)
435
571
 
@@ -1338,6 +1474,10 @@ retrieval results and is used as trusted guidance during grounded answer
1338
1474
  generation. Matching scopes compose once in this order: global, collection,
1339
1475
  then path prefixes from broadest to most specific. Context guides interpretation;
1340
1476
  it is not searched and does not change ranking.
1477
+ Multiple distinct normalized texts may share one scope. The persisted identity
1478
+ is `(scopeType, canonicalScopeKey, normalizedText)`. Text normalization removes
1479
+ a leading BOM, converts CRLF/CR to LF, applies NFC, and trims surrounding
1480
+ whitespace; an exact normalized duplicate is rejected.
1341
1481
 
1342
1482
  **Synopsis:**
1343
1483
 
@@ -1411,18 +1551,20 @@ gno context check [--json|--md]
1411
1551
 
1412
1552
  ### gno context rm
1413
1553
 
1414
- Remove a context.
1554
+ Remove a context. Scope-only removal succeeds only when exactly one record
1555
+ matches. If multiple texts share the scope, the caller MUST pass exact text;
1556
+ ambiguous removal fails without mutation.
1415
1557
 
1416
1558
  **Synopsis:**
1417
1559
 
1418
1560
  ```bash
1419
- gno context rm <scope>
1561
+ gno context rm <scope> [text]
1420
1562
  ```
1421
1563
 
1422
1564
  **Exit Codes:**
1423
1565
 
1424
1566
  - 0: Success
1425
- - 1: Scope not found
1567
+ - 1: Invalid/not-found scope, text not found, or ambiguous scope-only removal
1426
1568
 
1427
1569
  ---
1428
1570
 
@@ -61,7 +61,7 @@ CREATE TABLE IF NOT EXISTS contexts (
61
61
  scope_key TEXT NOT NULL,
62
62
  text TEXT NOT NULL,
63
63
  synced_at TEXT NOT NULL DEFAULT (datetime('now')),
64
- PRIMARY KEY (scope_type, scope_key)
64
+ PRIMARY KEY (scope_type, scope_key, text)
65
65
  );
66
66
 
67
67
  -- ─────────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,209 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gno://schemas/project-profile-apply@1.0",
4
+ "title": "GNO Project Profile Apply Result",
5
+ "description": "Deterministic, path-redacted receipt for create/update-only project profile application.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schemaVersion",
10
+ "command",
11
+ "status",
12
+ "applied",
13
+ "discovery",
14
+ "receipt",
15
+ "diagnostics"
16
+ ],
17
+ "properties": {
18
+ "schemaVersion": { "const": "1.0" },
19
+ "command": { "const": "apply" },
20
+ "status": {
21
+ "enum": [
22
+ "applied",
23
+ "unchanged",
24
+ "invalid",
25
+ "not_found",
26
+ "disabled",
27
+ "failed"
28
+ ]
29
+ },
30
+ "applied": { "type": "boolean" },
31
+ "discovery": { "$ref": "#/definitions/discovery" },
32
+ "receipt": {
33
+ "oneOf": [{ "$ref": "#/definitions/receipt" }, { "type": "null" }]
34
+ },
35
+ "diagnostics": {
36
+ "type": "array",
37
+ "items": { "$ref": "#/definitions/diagnostic" }
38
+ }
39
+ },
40
+ "definitions": {
41
+ "diagnostic": {
42
+ "type": "object",
43
+ "additionalProperties": false,
44
+ "required": ["code", "severity", "path", "message", "remediation"],
45
+ "properties": {
46
+ "code": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" },
47
+ "severity": { "enum": ["error", "warning"] },
48
+ "path": { "type": "string" },
49
+ "message": { "type": "string", "minLength": 1 },
50
+ "remediation": { "type": "string", "minLength": 1 }
51
+ }
52
+ },
53
+ "discovery": {
54
+ "type": "object",
55
+ "additionalProperties": false,
56
+ "required": [
57
+ "status",
58
+ "source",
59
+ "boundary",
60
+ "profile",
61
+ "ambiguous",
62
+ "shadowedProfiles"
63
+ ],
64
+ "properties": {
65
+ "status": { "enum": ["found", "not_found", "disabled", "error"] },
66
+ "source": { "enum": ["cwd", "override", "remote"] },
67
+ "boundary": {
68
+ "enum": ["repository", "filesystem", "explicit", "remote"]
69
+ },
70
+ "profile": {
71
+ "type": ["string", "null"],
72
+ "enum": [".gno/index.yml", null]
73
+ },
74
+ "ambiguous": { "type": "boolean" },
75
+ "shadowedProfiles": { "type": "integer", "minimum": 0 }
76
+ }
77
+ },
78
+ "change": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": ["action", "field", "destructive", "summary"],
82
+ "properties": {
83
+ "action": { "enum": ["add", "update", "repair", "review"] },
84
+ "field": { "type": "string", "minLength": 1 },
85
+ "destructive": { "const": false },
86
+ "summary": { "type": "string", "minLength": 1 }
87
+ }
88
+ },
89
+ "staleMapping": {
90
+ "type": "object",
91
+ "additionalProperties": false,
92
+ "required": ["collection", "reason", "choices"],
93
+ "properties": {
94
+ "collection": { "type": "string", "minLength": 1 },
95
+ "reason": { "enum": ["name_changed", "root_changed"] },
96
+ "choices": {
97
+ "type": "array",
98
+ "minItems": 2,
99
+ "maxItems": 2,
100
+ "items": { "enum": ["repair", "remove_explicitly"] }
101
+ }
102
+ }
103
+ },
104
+ "diff": {
105
+ "type": "object",
106
+ "additionalProperties": false,
107
+ "required": ["status", "changes", "staleMappings"],
108
+ "properties": {
109
+ "status": { "enum": ["in_sync", "changes_required"] },
110
+ "changes": {
111
+ "type": "array",
112
+ "items": { "$ref": "#/definitions/change" }
113
+ },
114
+ "staleMappings": {
115
+ "type": "array",
116
+ "items": { "$ref": "#/definitions/staleMapping" }
117
+ }
118
+ }
119
+ },
120
+ "resource": {
121
+ "type": "object",
122
+ "additionalProperties": false,
123
+ "required": ["kind", "id", "disposition", "pendingIndexing"],
124
+ "properties": {
125
+ "kind": {
126
+ "enum": [
127
+ "capability",
128
+ "collection",
129
+ "content_type",
130
+ "contexts",
131
+ "profile_binding",
132
+ "project_affinity",
133
+ "stale_mapping"
134
+ ]
135
+ },
136
+ "id": { "type": "string", "minLength": 1 },
137
+ "disposition": {
138
+ "enum": ["created", "reused", "updated", "skipped"]
139
+ },
140
+ "pendingIndexing": { "type": "boolean" }
141
+ }
142
+ },
143
+ "receipt": {
144
+ "type": "object",
145
+ "additionalProperties": false,
146
+ "required": [
147
+ "schemaVersion",
148
+ "command",
149
+ "status",
150
+ "profile",
151
+ "diff",
152
+ "resources",
153
+ "pendingIndexing",
154
+ "diagnostics"
155
+ ],
156
+ "properties": {
157
+ "schemaVersion": { "const": "1.0" },
158
+ "command": { "const": "apply" },
159
+ "status": { "enum": ["applied", "unchanged"] },
160
+ "profile": {
161
+ "type": "object",
162
+ "additionalProperties": false,
163
+ "required": ["fingerprint"],
164
+ "properties": {
165
+ "fingerprint": {
166
+ "type": "string",
167
+ "pattern": "^[a-f0-9]{64}$"
168
+ }
169
+ }
170
+ },
171
+ "diff": { "$ref": "#/definitions/diff" },
172
+ "resources": {
173
+ "type": "array",
174
+ "items": { "$ref": "#/definitions/resource" }
175
+ },
176
+ "pendingIndexing": {
177
+ "type": "array",
178
+ "uniqueItems": true,
179
+ "items": { "type": "string", "minLength": 1 }
180
+ },
181
+ "diagnostics": {
182
+ "type": "array",
183
+ "items": { "$ref": "#/definitions/diagnostic" }
184
+ }
185
+ }
186
+ }
187
+ },
188
+ "allOf": [
189
+ {
190
+ "if": {
191
+ "properties": {
192
+ "status": { "enum": ["applied", "unchanged"] }
193
+ },
194
+ "required": ["status"]
195
+ },
196
+ "then": {
197
+ "properties": {
198
+ "receipt": { "$ref": "#/definitions/receipt" }
199
+ }
200
+ },
201
+ "else": {
202
+ "properties": {
203
+ "receipt": { "type": "null" },
204
+ "applied": { "const": false }
205
+ }
206
+ }
207
+ }
208
+ ]
209
+ }