okf 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.okf/capabilities/agent-skill.md +112 -0
  3. data/.okf/capabilities/bundles-manager.md +144 -0
  4. data/.okf/capabilities/graph-server.md +678 -0
  5. data/.okf/capabilities/index.md +26 -0
  6. data/.okf/capabilities/library-api.md +82 -0
  7. data/.okf/capabilities/linter.md +83 -0
  8. data/.okf/capabilities/read-views.md +228 -0
  9. data/.okf/capabilities/render.md +66 -0
  10. data/.okf/capabilities/search.md +297 -0
  11. data/.okf/capabilities/validator.md +60 -0
  12. data/.okf/cli.md +214 -0
  13. data/.okf/design/browser-tests.md +211 -0
  14. data/.okf/design/core-shell-split.md +73 -0
  15. data/.okf/design/index.md +17 -0
  16. data/.okf/design/integration-first.md +140 -0
  17. data/.okf/design/packaging.md +65 -0
  18. data/.okf/design/ruby-floor.md +53 -0
  19. data/.okf/design/runtime-dependencies.md +82 -0
  20. data/.okf/design/search-engines.md +154 -0
  21. data/.okf/design/server-trust-boundary.md +139 -0
  22. data/.okf/index.md +40 -0
  23. data/.okf/log.md +724 -0
  24. data/.okf/model/bundle.md +47 -0
  25. data/.okf/model/concept.md +75 -0
  26. data/.okf/model/graph.md +59 -0
  27. data/.okf/model/index.md +9 -0
  28. data/.okf/model/skeleton.md +76 -0
  29. data/.okf/overview.md +87 -0
  30. data/.okf/registry.md +432 -0
  31. data/.okf/structure/format-layer.md +59 -0
  32. data/.okf/structure/index.md +22 -0
  33. data/.okf/structure/search.md +53 -0
  34. data/.okf/structure/the-analysers.md +60 -0
  35. data/.okf/structure/the-cli.md +99 -0
  36. data/.okf/structure/the-disk-shell.md +76 -0
  37. data/.okf/structure/the-model.md +81 -0
  38. data/.okf/structure/the-server.md +74 -0
  39. data/.okf/structure/the-skill.md +52 -0
  40. data/.okf/testing/adding-a-verb.md +76 -0
  41. data/.okf/testing/index.md +12 -0
  42. data/.okf/testing/the-harness.md +45 -0
  43. data/CHANGELOG.md +161 -16
  44. data/README.md +226 -17
  45. data/lib/okf/cli/command.rb +8 -3
  46. data/lib/okf/cli/registry.rb +306 -47
  47. data/lib/okf/cli.rb +1 -1
  48. data/lib/okf/registry.rb +448 -22
  49. data/lib/okf/render/graph/template.html.erb +6 -2
  50. data/lib/okf/server/hub.rb +6 -2
  51. data/lib/okf/skill/reference/cli/registry.md +49 -6
  52. data/lib/okf/skill/reference/cli.md +1 -1
  53. data/lib/okf/version.rb +1 -1
  54. metadata +46 -5
data/CHANGELOG.md CHANGED
@@ -5,6 +5,149 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.2.0] - 2026-08-22
9
+
10
+ ### Added
11
+
12
+ - **`okf registry import <@slug…> [--from FILE]` — copy chosen bundles out of
13
+ another registry file into the one in force, where they become yours.** It is
14
+ the opposite trade from `link` and the pair is the point: a link holds a live
15
+ pointer to a whole file and the other side keeps owning what it lends, while an
16
+ import copies chosen references and hands over ownership, so what lands renames,
17
+ defaults and groups like anything you registered by hand. The case it exists for
18
+ is standing in a repo, running `okf registry list -g`, and wanting one of the
19
+ bundles it shows — before this the only way to move it was to read the path off
20
+ the screen and retype it into `registry set`.
21
+
22
+ The source is `--from`, defaulting to the global registry, because `-g` goes on
23
+ meaning the registry acted *on*, as it does on every sibling subcommand. A group
24
+ ask brings its members and any group nested inside it, recreated under the same
25
+ names. Slugs are preserved, so a collision **refuses** (`--as` renames a single
26
+ ask) rather than being minted around the way a linked name is — a linked name was
27
+ never chosen here, an imported one was typed. A bundle already registered here
28
+ under another name refuses first, naming that name. Every ask is validated before
29
+ anything is written, so an import lands whole or leaves the file byte-for-byte
30
+ alone.
31
+
32
+ - **`.okf.json` is the project-local registry's filename.** `okf registry init`
33
+ writes it, and it is what the docs now name. The older `.okf-registry.json` is
34
+ **still discovered**, so no committed registry breaks: both names are checked in
35
+ each directory on the way up — per directory, not one name swept to the root and
36
+ then the other, or a legacy file at a repo's root would beat a `.okf.json` two
37
+ levels down and "the nearest one wins" would mean something else. Within one
38
+ directory the short name wins, and a legacy file left beside it is named on
39
+ stderr rather than silently ignored.
40
+
41
+ The deprecation note is the `registry` umbrella's alone, and it is one stderr
42
+ line: that verb's subject *is* a registry file, and it is not the one people run
43
+ in loops or pipe into something else, which is exactly what `lint` and `search`
44
+ are. Renaming is `git mv .okf-registry.json .okf.json`, and the note says so. One
45
+ transitional cost worth naming: a checkout renamed to `.okf.json` is invisible to
46
+ an installed okf older than 2.2.0, which will fall back to the global registry
47
+ until that install is upgraded.
48
+
49
+ - **`okf registry link <name> <file>` — the global registry points at another
50
+ registry file, and that file's bundles resolve here.** Nothing is copied: the
51
+ target keeps owning its rows, so an edit there shows on the next read. The case
52
+ it exists for is a repository that already curates its own bundles in a
53
+ committed `.okf-registry.json` — a link composes that curation instead of
54
+ duplicating it, and the target resolves its own relative paths exactly as it
55
+ would from inside that checkout. A linked bundle answers to its own slug, or to
56
+ `<name>-<slug>` when the name is already taken here; `@<name>` resolves as a
57
+ group over the set; `okf registry unlink <name>` drops it.
58
+
59
+ Links are the **global** registry's alone. A project-local one parses them and
60
+ never resolves them, which is what makes depth one structural — a linked file's
61
+ own links are never followed, so no chain forms and there is no cycle to guard.
62
+ A link is read-only: `rename`, `del`, `default`, `set --as` and `group` refuse a
63
+ slug it owns, naming the file that does, from the browser's ⚙ Bundles panel as
64
+ from the terminal. A target that has gone or cannot be parsed is listed
65
+ `(missing)`/`(unreadable)` and resolves to nothing, rather than taking down the
66
+ registry holding it.
67
+
68
+ - `registry group` and `registry ungroup` refuse a group a link brought in —
69
+ the link's own set, or one the linked file curates. Naming one took the
70
+ update path, reported `grouped …`, and discarded the change on the next read,
71
+ because only the groups this registry owns are written. A write that reports
72
+ success and does not happen is worse than one that raises.
73
+
74
+ - **`-g`/`--global` on every `registry` subcommand but `init`.** It forces the
75
+ `$OKF_HOME` registry for one command, so `okf registry list -g` reads the global
76
+ one from inside a repo carrying its own, and `okf registry set <dir> -g`
77
+ registers there without leaving. `OKF_NO_DISCOVERY=1` already did this for a
78
+ whole session and still does; the flag exists because a lever reachable only
79
+ through an env var is one most people never find. It stays on the `registry`
80
+ umbrella — the one verb whose subject *is* a registry file — and nowhere else.
81
+
82
+ - The graph page's ⚙ Bundles panel marks a linked row `via @onm` and **offers no
83
+ actions menu** on it. Rename, Remove and Default are all refused for a linked
84
+ bundle, so the menu could only have produced three errors. The request is
85
+ refused whether or not the button exists — the guard is in `Registry`, not in
86
+ the page — which is the same split the read-only server already keeps: hiding
87
+ a control is a UI, refusing the request is the boundary.
88
+
89
+ - **`okf help` shows one `registry` row instead of ten, and `okf registry --help`
90
+ prints the subcommands.** Ten rows for one verb made a third of the map about
91
+ registry management, and the map is there to name the verbs rather than to be
92
+ every verb's manual. The row names the help that replaces it, since nothing
93
+ else tells a reader an umbrella has subcommands. A bare `okf registry` still
94
+ lists the registry, and `okf registry <command> --help` still answers for that
95
+ command.
96
+
97
+ ### Changed
98
+
99
+ - `okf registry list --json` groups now carry a `link` key — `null` for a group
100
+ this registry owns, the link's name for one that arrived through a link.
101
+ `Registry#groups_listing` returns both kinds in one list, own groups first,
102
+ because `#group?` resolves a linked group and a listing that named only the
103
+ local half would answer about a smaller set than the same object can resolve —
104
+ the drift every library consumer would inherit silently. A caller that wants
105
+ only the editable groups filters on `link`.
106
+
107
+ - `okf registry list --json` gains a `links` array, and each bundle row gains
108
+ `link` (the link it arrived through, `null` when the registry owns it) and
109
+ `origin` (its slug in that file, differing only where a collision moved the
110
+ name). The on-disk registry gains a `"links"` key, written empty when there are
111
+ none; an older file with no such key reads unchanged.
112
+
113
+ ## [2.1.1] - 2026-08-20
114
+
115
+ ### Fixed
116
+
117
+ - **The package metadata follows the `gems/` move and the repository rename.**
118
+ Two URLs on the package page were about to be wrong at once. `changelog_uri`
119
+ named `blob/main/okf/CHANGELOG.md`, a path that stopped existing when the
120
+ four gems moved under `gems/`; `homepage` named `serradura/okf-gem`, and the
121
+ repository is now **`serradura/okf`** — the name the command has had all
122
+ along. rubygems.org serves whatever the last release published, so neither
123
+ corrects itself: only a release republishes metadata, and this is that
124
+ release. Both land in one round rather than two, which is the whole reason
125
+ the rename waited for the paths to stop moving.
126
+
127
+ `homepage_uri`, `source_code_uri` and `changelog_uri` are all derived from
128
+ `homepage`, so one line moves four pieces of this gem's metadata. GitHub
129
+ redirects the old URLs permanently, so anything already published keeps
130
+ resolving.
131
+
132
+ ### Added
133
+
134
+ - **The gem ships a file-level map of itself, at `.okf/`.** `structure/` names
135
+ every one of the fifty files under `lib/`, grouped by the layer that owns it,
136
+ and `testing/adding-a-verb.md` is the ordered walk a new command owes. It is
137
+ the gem's own documentation rather than a sample, so an installed okf now
138
+ carries a real bundle for a reader to open with the tool they just installed:
139
+ `okf server "$(gem contents okf --show-install-dir)/.okf"`.
140
+
141
+ It deliberately copies nothing from the repository's own bundle, which still
142
+ holds okf's format, model, capabilities and design constraints — a second copy
143
+ of a catalogue is worse than none.
144
+
145
+ `test/unit/bundle_catalog_test.rb` pins both halves: a file under `lib/` named
146
+ by no concept, a concept naming a file that is gone, or the repository
147
+ bundle's `cli.md` group table disagreeing with `OKF::CLI.builtins` all fail
148
+ the suite. That group table was code-derived and unchecked until now.
149
+ `test/unit/packaging_test.rb` pins that the bundle actually ships.
150
+
8
151
  ## [2.1.0] - 2026-08-17
9
152
 
10
153
  ### Changed
@@ -1437,7 +1580,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1437
1580
  ### Added
1438
1581
 
1439
1582
  - Claude Code plugin. The repository now doubles as a plugin marketplace:
1440
- `/plugin marketplace add serradura/okf-gem`, then `/plugin install okf@okfgem`.
1583
+ `/plugin marketplace add serradura/okf`, then `/plugin install okf@okfgem`.
1441
1584
  The plugin carries the canonical skill (a generated copy; `rake plugin:sync`
1442
1585
  keeps it in lockstep with `lib/okf/skill`, and a test fails on drift), one
1443
1586
  front-door command (`/okf:gem`: no arguments orients on the CLI, the bundle,
@@ -1543,18 +1686,20 @@ Initial release.
1543
1686
 
1544
1687
  - Runs on Ruby >= 2.4 with two runtime dependencies: rack and webrick.
1545
1688
 
1546
- [2.0.0]: https://github.com/serradura/okf-gem/compare/v1.13.0...v2.0.0
1547
- [1.13.0]: https://github.com/serradura/okf-gem/compare/v1.12.0...v1.13.0
1548
- [1.12.0]: https://github.com/serradura/okf-gem/compare/v1.11.0...v1.12.0
1549
- [1.11.0]: https://github.com/serradura/okf-gem/compare/v1.10.0...v1.11.0
1550
- [1.10.0]: https://github.com/serradura/okf-gem/compare/v1.9.0...v1.10.0
1551
- [1.9.0]: https://github.com/serradura/okf-gem/compare/v1.8.0...v1.9.0
1552
- [1.8.0]: https://github.com/serradura/okf-gem/compare/v1.7.0...v1.8.0
1553
- [1.7.0]: https://github.com/serradura/okf-gem/compare/v1.6.0...v1.7.0
1554
- [1.6.0]: https://github.com/serradura/okf-gem/compare/v1.5.0...v1.6.0
1555
- [1.5.0]: https://github.com/serradura/okf-gem/compare/v1.4.0...v1.5.0
1556
- [1.4.0]: https://github.com/serradura/okf-gem/compare/v1.3.0...v1.4.0
1557
- [1.3.0]: https://github.com/serradura/okf-gem/compare/v1.2.0...v1.3.0
1558
- [1.2.0]: https://github.com/serradura/okf-gem/compare/v1.1.0...v1.2.0
1559
- [1.1.0]: https://github.com/serradura/okf-gem/compare/v1.0.0...v1.1.0
1560
- [1.0.0]: https://github.com/serradura/okf-gem/releases/tag/v1.0.0
1689
+ [2.1.1]: https://github.com/serradura/okf/compare/v2.1.0...v2.1.1
1690
+ [2.1.0]: https://github.com/serradura/okf/compare/v2.0.0...v2.1.0
1691
+ [2.0.0]: https://github.com/serradura/okf/compare/v1.13.0...v2.0.0
1692
+ [1.13.0]: https://github.com/serradura/okf/compare/v1.12.0...v1.13.0
1693
+ [1.12.0]: https://github.com/serradura/okf/compare/v1.11.0...v1.12.0
1694
+ [1.11.0]: https://github.com/serradura/okf/compare/v1.10.0...v1.11.0
1695
+ [1.10.0]: https://github.com/serradura/okf/compare/v1.9.0...v1.10.0
1696
+ [1.9.0]: https://github.com/serradura/okf/compare/v1.8.0...v1.9.0
1697
+ [1.8.0]: https://github.com/serradura/okf/compare/v1.7.0...v1.8.0
1698
+ [1.7.0]: https://github.com/serradura/okf/compare/v1.6.0...v1.7.0
1699
+ [1.6.0]: https://github.com/serradura/okf/compare/v1.5.0...v1.6.0
1700
+ [1.5.0]: https://github.com/serradura/okf/compare/v1.4.0...v1.5.0
1701
+ [1.4.0]: https://github.com/serradura/okf/compare/v1.3.0...v1.4.0
1702
+ [1.3.0]: https://github.com/serradura/okf/compare/v1.2.0...v1.3.0
1703
+ [1.2.0]: https://github.com/serradura/okf/compare/v1.1.0...v1.2.0
1704
+ [1.1.0]: https://github.com/serradura/okf/compare/v1.0.0...v1.1.0
1705
+ [1.0.0]: https://github.com/serradura/okf/releases/tag/v1.0.0
data/README.md CHANGED
@@ -5,7 +5,7 @@ ranked search, and a live knowledge graph. 100% local.**
5
5
 
6
6
  [Site](https://okfgem.com) · [Docs](https://okfgem.com/docs/) ·
7
7
  [Live demo](https://demo.okfgem.com) ·
8
- [Project README](https://github.com/serradura/okf-gem#readme)
8
+ [Project README](https://github.com/serradura/okf#readme)
9
9
 
10
10
  OKF (Open Knowledge Format) is portable project knowledge: Markdown files with
11
11
  YAML frontmatter that both humans and agents read from one source. This gem is
@@ -22,20 +22,41 @@ on every Ruby since **2.4** — the one your OS already ships.
22
22
 
23
23
  ## Install
24
24
 
25
+ > **In Claude Code**, the plugin is the fastest path: two commands install the whole
26
+ > toolchain (skill, `/okf:gem`, and the curation hook). See the
27
+ > [project README](https://github.com/serradura/okf#claude-code-plugin).
28
+ > Everywhere else, install the gem:
29
+
25
30
  ```bash
26
31
  gem install okf
27
32
  # or, in a project
28
33
  bundle add okf
29
34
  ```
30
35
 
31
- No Ruby? The official image carries the CLI:
36
+ Tested and supported on every Ruby from **2.4 through 4.0**. From a checkout,
37
+ `bundle exec rake install` builds and installs it locally.
38
+
39
+ ## No Ruby? Use Docker
40
+
41
+ The official image bundles the CLI, so every `okf` command runs against a bundle
42
+ you mount at `/data`:
32
43
 
33
44
  ```bash
34
45
  docker run --rm -v "$PWD:/data" ghcr.io/serradura/okf validate .
46
+ docker run --rm -v "$PWD:/data" -p 8808:8808 ghcr.io/serradura/okf server . --bind 0.0.0.0
47
+ ```
48
+
49
+ Tired of the long line? The Docker-backed [`okf` command](https://docker.okfgem.com)
50
+ drops the prefix so every verb reads exactly like the native CLI:
51
+
52
+ ```bash
53
+ curl -fsSL https://docker.okfgem.com/install.sh | sh # PowerShell: irm https://docker.okfgem.com/install.ps1 | iex
54
+ okf validate .
55
+ okf server .
35
56
  ```
36
57
 
37
- The Docker-backed [`okf` command](https://docker.okfgem.com) drops the prefix so
38
- every verb reads exactly like the native CLI.
58
+ Images are published for `linux/amd64` and `linux/arm64` on
59
+ [ghcr.io](https://github.com/serradura/okf/pkgs/container/okf).
39
60
 
40
61
  ## Four steps to your first bundle
41
62
 
@@ -55,6 +76,41 @@ okf server <folder> # 4. explore what you got, as a live graph
55
76
 
56
77
  Then `/okf maintain` keeps it in sync as the code changes.
57
78
 
79
+ What the gem does, and which verb does it. This table is the map; **[the
80
+ docs](https://okfgem.com/docs/)** are the manual.
81
+
82
+ | Capability | What it answers | Verb |
83
+ | ------------------------------------------------------------- | --------------------------------- | -------------------------- |
84
+ | [Companion agent skill](https://okfgem.com/docs/skill/) | Can an agent author it? | `skill` |
85
+ | [Conformance validator](https://okfgem.com/docs/cli/validate/) | Is this a legal OKF bundle? | `validate` |
86
+ | [Curation linter](https://okfgem.com/docs/cli/lint/) | Is it navigable, complete, fresh? | `lint` / `loose` |
87
+ | [Ranked text search](https://okfgem.com/docs/cli/search/) | Which concept covers X? | `search` |
88
+ | [Read views](https://okfgem.com/docs/cli/) | What is in here, and where? | `index` / `dirs` / `catalog` |
89
+ | [Interactive graph server](https://okfgem.com/docs/cli/server/) | Can I explore it visually? | `server` |
90
+ | [Static render](https://okfgem.com/docs/cli/render/) | Can I ship a serverless snapshot? | `render` |
91
+ | [Library API](https://okfgem.com/docs/library/) | Can my Ruby program use it? | in-process |
92
+
93
+ And because knowledge rarely lives in one bundle, a registry gives each one a
94
+ name — see [one registry, many bundles](#one-registry-many-bundles) below.
95
+
96
+ ## The graph
97
+
98
+ One page, from a phone to a desktop: the navigation rail becomes a drawer, the
99
+ toolbar folds into a `⚙` sheet, and a tap opens a preview card at the bottom edge
100
+ rather than a panel over the whole viewport, so the graph stays live while you
101
+ read. Drag the card up for the neighbourhood, tap a link and it walks there in
102
+ place.
103
+
104
+ It is keyboard-first: **`⌘/Ctrl-K`** opens a command palette that searches
105
+ concepts, jumps to a view, and — behind a [hub](#one-registry-many-bundles) —
106
+ switches bundles. **`/`** jumps to the current view's search, **`?`** answers with
107
+ every shortcut. Cluster mode boxes the graph by directory and nests as deep as
108
+ your tree does.
109
+
110
+ To skip the server entirely, **`okf render <dir>`** writes that same page as one
111
+ self-contained HTML file, the whole bundle baked in, so you can publish the graph
112
+ on GitHub Pages or any static host.
113
+
58
114
  ## The command line
59
115
 
60
116
  Written to be read by an **agent first and a person second** — that is what the
@@ -75,6 +131,8 @@ okf graph <dir|@slug> [--hubs] [--traffic] # the raw graph; --hubs ranks c
75
131
  okf server [DIR|@slug…] [-p PORT] [--bind ADDR] # the live graph: one bundle, or all of them
76
132
  okf render <dir|@slug> [-o FILE] # the same page as one static, self-contained file
77
133
  okf registry init | list | set | del | default | rename | group | ungroup # name & group your bundles
134
+ okf registry link | unlink <name> <file> # fold another registry file's bundles in
135
+ okf registry import <@slug…> [--from FILE] # copy bundles out of another registry into this one
78
136
  okf skill <dest> # install the companion agent skill
79
137
  okf --version
80
138
  ```
@@ -83,16 +141,157 @@ Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
83
141
  threshold crossed), `2` usage error. Every flag is in `okf <verb> --help` and in
84
142
  [the docs](https://okfgem.com/docs/).
85
143
 
86
- **A registry names your bundles.** `okf registry set ./docs --as handbook` once,
87
- then `@handbook` works anywhere a `<dir>` does, from any directory; `okf search
88
- @all rate limit` spans every one of them, and a bare `okf server` hosts them all
89
- behind one hub. `okf registry init` scopes one to a single project instead, and a
90
- committed `.okf-registry.json` travels with the repo.
144
+ ```bash
145
+ $ okf validate docs
146
+ OKF v0.2 conformance docs
147
+ concepts: 37 index.md: 10 log.md: 1
148
+ ! warn features/link-suggestions.md: cross-link target not found: `/graph-view.md` (tolerated under §6.1)
149
+
150
+ ✓ conformant (33 warning(s))
151
+
152
+ $ okf server docs
153
+ serving 37 concepts at http://127.0.0.1:8808 (Ctrl-C to stop)
154
+
155
+ $ okf render docs > public/index.html # the same page, static — host it anywhere
156
+ ```
157
+
158
+ ## One registry, many bundles
159
+
160
+ The registry is a per-user, ordered list of bundles in one plain JSON file (`$OKF_HOME/registry.json`, default `~/.okf`) — hand-editable,
161
+ greppable, no database. It stores references, never content: the bundles stay in
162
+ the repos that own them.
163
+
164
+ ```bash
165
+ okf registry set ./docs --as handbook # give the bundle a name
166
+ okf lint @handbook # @slug works wherever a <dir> does, from anywhere
167
+ okf search @all rate limit # ranked retrieval across every registered bundle
168
+ okf server # no args: the whole registry behind one hub
169
+ ```
170
+
171
+ Related bundles can share a name: `okf registry group backend @handbook @runbooks`
172
+ makes `@backend` stand for the set (members can be groups too, so they nest), and
173
+ `okf search @backend rate limit` or `okf server @backend` then targets all of them
174
+ at once — a durable subset for the two verbs that take several bundles.
175
+
176
+ The registry lives under `$OKF_HOME` (default `~/.okf`) — one per user. For one
177
+ scoped to a single project instead, `okf registry init` drops a
178
+ `.okf.json` in the current directory (the older `.okf-registry.json` is still
179
+ discovered, and `okf registry` says so once so you can `git mv` it); okf then
180
+ discovers it by walking up from wherever you run, and every registry op — and every `@slug` — resolves through
181
+ it in place of the global one. So a bare `okf server` inside that repo serves *its*
182
+ bundles with no `$OKF_HOME` setup. The nearest registry wins; `-g` on any
183
+ `registry` subcommand reaches the global one for a single command (`okf registry
184
+ list -g`, `okf registry set ./docs -g`), and `OKF_NO_DISCOVERY=1` does it for a
185
+ whole shell.
186
+
187
+ Commit that file and it travels with the repo: a bundle under the project root is
188
+ stored relative to the registry, so a checkout on another machine — or a container
189
+ that mounts the repo — resolves the same bundles unchanged. (Bundles outside the
190
+ tree keep absolute paths, which do not travel.)
191
+
192
+ A repository that already curates its own bundles can lend that list rather than
193
+ have it copied. `okf registry link onm ~/ONM/.okf.json` points the global
194
+ registry at another registry file: its bundles resolve under their own slugs,
195
+ `@onm` names the set, and nothing is duplicated — edit the other file and the
196
+ change shows on the next read. They are read-only from here, since the file that
197
+ owns them is elsewhere.
198
+
199
+ ```bash
200
+ okf registry link okf ~/code/okf/.okf.json # that repo's own curation, composed
201
+ okf search @all rate limit # now spans both files
202
+ ```
203
+
204
+ When you want one bundle rather than a whole file, `import` is the opposite
205
+ trade — it copies the reference, and what lands is yours to rename, default and
206
+ group. The source is `--from`, defaulting to the global registry, which inside a
207
+ repo is the only other one you have. A group ask brings its members with it,
208
+ slugs are preserved (a collision refuses rather than being renamed behind your
209
+ back), and nothing is written unless every ask checks out.
210
+
211
+ ```bash
212
+ okf registry list -g # what does the global registry hold?
213
+ okf registry import handbook specs # take those two, here, as mine
214
+ ```
215
+
216
+ Behind the hub each bundle mounts at `/b/<slug>/`, `/b/` lists them all, and the
217
+ `⌘/Ctrl-K` palette both switches bundles and **searches every one at once** — type
218
+ a few words and the matching concepts appear with their bundle and a snippet, from
219
+ wherever you are.
220
+
221
+ The ⚙ rail opens **Bundles**, the registry on the graph page itself: make
222
+ default, rename, remove, where you are already reading. Those controls are the one
223
+ thing that does not follow you onto a network — bind anywhere but loopback and
224
+ they are refused outright, since `--bind 0.0.0.0` is how a personal tool becomes a
225
+ public one.
226
+
227
+ ## Reading a big bundle a level at a time
228
+
229
+ A few hundred concepts is a map nobody reads whole, so `index` and `dirs` descend
230
+ instead of dumping. `--dir` takes a directory **and everything under it**,
231
+ `--depth N` bounds how far below that it goes, and the two compose the way you
232
+ actually walk a tree:
91
233
 
92
- **A big bundle is read a level at a time.** `okf index --depth 1 --except
93
- body,listing` is the map an agent orients on on a 414-concept bundle, 2.8 KB
94
- against the full 313 KB and `--dir` then opens one branch, bringing the
95
- ancestors that say what it is.
234
+ ```bash
235
+ okf dirs @handbook # the shape: every dir, what it holds directly and below
236
+ okf index @handbook --depth 1 --no-body # the top of the map, no prose
237
+ okf index @handbook --dir platform/api # now open one branch — with the chain that places it
238
+ ```
239
+
240
+ Naming a `--dir` brings its ancestors along, marked `↑`, so a branch is never
241
+ shown adrift of the context that says what it is — the root `index.md`'s prose
242
+ first among it.
243
+
244
+ For an agent the saving is the whole point. On a 400-concept bundle the full
245
+ `okf index --json` is 313 KB; the skeleton it orients on is 2.8 KB:
246
+
247
+ ```bash
248
+ okf index @handbook --json --depth 1 --except body,listing
249
+ ```
250
+
251
+ ## The agent skill
252
+
253
+ The gem carries the [companion OKF agent skill](https://okfgem.com/docs/skill/):
254
+ a `SKILL.md` plus reference and template files that teach a coding agent to
255
+ author, maintain, and consume OKF bundles and to drive
256
+ [the command line](#the-command-line).
257
+ Because the skill ships inside the gem, installing the gem already puts the skill
258
+ on your machine, and the skill's CLI reference can never drift from the
259
+ executable it was released with.
260
+
261
+ The skill routes a small set of verbs. In Claude Code they run as `/okf:gem
262
+ <verb>`; used standalone, the skill infers the verb from your request.
263
+
264
+ | Verb | What it does |
265
+ | ---------------- | ----------------------------------------------------------------------------------------------------------- |
266
+ | _(none)_ | Orient on the bundle and recommend the highest-value next move |
267
+ | `search` | Answer a question from the bundle, token-lean: the map, the finder, only the winning bodies |
268
+ | `produce` | Create or extend a bundle from code, docs, or knowledge in people's heads |
269
+ | `migrate` | Adopt existing Markdown docs in place: frontmatter and reserved files added, bodies kept verbatim |
270
+ | `maintain` | Sync the bundle's content with reality after the code or docs change |
271
+ | `refine` | Restructure it for retrieval: evidence-first, cohesion over balance — proposes, never applies |
272
+ | `consume` | Use the bundle as context for a task, writing back what you learn |
273
+ | `curate` | Structural upkeep as it stands: `validate` + `lint` + `loose` |
274
+ | `doctor` | Install and verify the CLI, then doctor the bundle |
275
+ | `<okf-cli-verb>` | Run any CLI verb (`validate`, `lint`, `search`, `index`, `server`, the read views) and interpret its output |
276
+
277
+ Three of those look alike and are not, which is the distinction worth learning
278
+ first: **`curate`** keeps the bundle *sound* (the structure as it stands),
279
+ **`maintain`** keeps it *true* (the code changed, so the content must catch up),
280
+ and **`refine`** changes *where knowledge lives* — the folder a concept sits in,
281
+ a fact re-explained in three overviews. Reach for `refine` when nothing is wrong
282
+ and everything is hard to find. It reads the evidence, then hands you a proposal
283
+ — it never rearranges your bundle on its own.
284
+
285
+ Point it at your agent's config directory and the tree settles in its own
286
+ `skills/okf/` folder, so a shared skills directory never gets the files loose:
287
+
288
+ ```bash
289
+ okf skill .claude # Claude Code -> .claude/skills/okf
290
+ okf skill .agents # agent-agnostic -> .agents/skills/okf
291
+ ```
292
+
293
+ The resolved directory must be empty unless you pass `--force`, so a customized
294
+ skill is never clobbered.
96
295
 
97
296
  ## The library
98
297
 
@@ -173,12 +372,22 @@ do not know.
173
372
 
174
373
  ## More
175
374
 
176
- The [project README](https://github.com/serradura/okf-gem#readme) carries the
375
+ The [project README](https://github.com/serradura/okf#readme) carries the
177
376
  diagrams, the comparison with `CLAUDE.md`, agent auto-memory and wikis, the
178
377
  Claude Code plugin, and the way to install the skill into any agent without this
179
- gem (`npx skills add serradura/okf-gem`). The [docs](https://okfgem.com/docs/) are the manual. And the
180
- repo documents *itself* in OKF — clone it and run `okf server .okf` to read this
181
- gem's own knowledge as a graph.
378
+ gem (`npx skills add serradura/okf`). The [docs](https://okfgem.com/docs/) are the manual.
379
+
380
+ And the gem documents *itself* in OKF. `.okf/` ships inside it — a map of what
381
+ every file under `lib/` does, and the walk a new verb owes — so from an
382
+ installed copy:
383
+
384
+ ```bash
385
+ okf server "$(gem contents okf --show-install-dir)/.okf"
386
+ ```
387
+
388
+ reads this gem's own knowledge as a graph, in this gem. That is the shortest
389
+ honest demonstration there is: the format is good enough that the tool's
390
+ maintainers use it on the tool.
182
391
 
183
392
  ## License
184
393
 
@@ -577,12 +577,17 @@ module OKF
577
577
 
578
578
  # The registry a verb resolves against — the single seam that opts the CLI
579
579
  # into discovery. `cwd: Dir.pwd` is what makes OKF::Registry.load walk up for
580
- # a project-local .okf-registry.json; a library caller passing no cwd stays
580
+ # a project-local .okf.json; a library caller passing no cwd stays
581
581
  # global-only. The registry subcommands and `server` open through here too,
582
582
  # so a bare `okf server` inside a repo serves that repo's bundles.
583
- def open_registry
583
+ # +global+ forces the $OKF_HOME registry by withholding the cwd discovery
584
+ # needs — the per-command form of OKF_NO_DISCOVERY, and the only thing
585
+ # `okf registry -g` does. It is an argument to the `registry` verb rather
586
+ # than a flag on all fourteen, because that verb's *subject* is a registry
587
+ # file; every other verb keeps inheriting the env var's one signal.
588
+ def open_registry(global: false)
584
589
  require "okf/registry"
585
- OKF::Registry.load(cwd: Dir.pwd)
590
+ OKF::Registry.load(cwd: global ? nil : Dir.pwd)
586
591
  end
587
592
 
588
593
  # Resolve one @ref through the active registry — a discovered project-local