okf 1.11.0 → 1.13.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.
data/README.md CHANGED
@@ -1,240 +1,66 @@
1
- <p align="center">
2
- <a href="https://okfgem.com">
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset=".github/hero-dark.png">
5
- <img src=".github/hero-light.png" width="100%" alt="okf-gem, the Open Knowledge Format toolkit. Everything OKF, in one gem: author, curate, and consume your project's knowledge, with your agent. 100% local. Start at https://okfgem.com/#try. Three pieces in one install: the Agent Skill (the brain) authors, curates and consumes; the CLI/Lib (the muscle) validates, lints and does full-text search; the Graph (the dashboard) searches and explores it, live or static. Available from RubyGems, as a Docker image, and as a Claude Code plugin.">
6
- </picture>
7
- </a>
8
- </p>
9
-
10
- <p align="center">
11
- <a href="https://rubygems.org/gems/okf"><img src="https://img.shields.io/gem/v/okf" alt="Gem version"></a>
12
- <a href="https://rubygems.org/gems/okf"><img src="https://img.shields.io/gem/dt/okf" alt="Downloads"></a>
13
- <a href="https://github.com/serradura/okf-gem/pkgs/container/okf"><img src="https://img.shields.io/badge/ghcr.io-okf-2496ED?logo=docker&logoColor=white" alt="Docker image"></a>
14
- <a href="https://github.com/serradura/okf-gem/actions/workflows/main.yml"><img src="https://github.com/serradura/okf-gem/actions/workflows/main.yml/badge.svg" alt="CI"></a>
15
- <a href="https://github.com/serradura/okf-gem"><img src="https://img.shields.io/badge/ruby-%3E%3D%202.4-black" alt="Ruby >= 2.4"></a>
16
- <a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License: Apache-2.0"></a>
17
- <a href="lib/okf/skill/reference/SPEC.md"><img src="https://img.shields.io/badge/OKF-v0.1-6E56CF" alt="OKF v0.1"></a>
18
- <a href="#claude-code-plugin"><img src="https://img.shields.io/badge/Claude%20Code-plugin-D97757" alt="Claude Code plugin"></a>
19
- </p>
20
-
21
- <p align="center">
22
- <b><a href="https://okfgem.com">Site</a></b> &nbsp;·&nbsp;
23
- <b><a href="https://okfgem.com/docs/">Docs</a></b> &nbsp;·&nbsp;
24
- <b><a href="https://demo.okfgem.com">Live demo</a></b> &nbsp;·&nbsp;
25
- <b><a href="https://claude.okfgem.com">Claude plugin</a></b> &nbsp;·&nbsp;
26
- <b><a href="https://docker.okfgem.com">Docker image</a></b>
27
- </p>
28
-
29
- **okf-gem** (`okf` on RubyGems or Docker) gives your project's knowledge one
30
- durable home in your repo, in Markdown your team and your agents both read: the
31
- decisions and the reasoning an agent cannot re-derive from the code, versioned
32
- beside the code they explain.
33
-
34
- One install carries the whole workflow, and that is the point of a single gem:
35
-
36
- - an **Agent Skill**, so your agent writes and curates the knowledge instead of you;
37
- - a **CLI and Ruby library**, so it stays correct: validated, linted, and searchable in milliseconds;
38
- - a **Graph**, so anyone can see the shape of what the team knows, live or as one static file you can host anywhere.
39
-
40
- The package is **Agent Skill + CLI/Lib + Graph**. It runs 100% local, adds no
41
- service to your stack, and does not define a new place to keep knowledge: it
42
- gives you leverage over the Markdown you already have.
43
-
44
- ## Why OKF
45
-
46
- Project knowledge (why a service exists, what a metric really measures, the
47
- reasoning a schema encodes) lives scattered across wikis, code comments, and
48
- whoever happened to be in the room, and an agent re-derives it every session. OKF
49
- gives it one durable, diffable home, versioned next to the code it describes and
50
- read from the same file by people and agents alike. [OKF][okf] is an open,
51
- vendor-neutral format (Google Cloud, 2026); this gem is the Ruby-native way to
52
- work with it.
53
-
54
- [okf]: https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
55
-
56
- Knowledge already has several homes near an agent, and each holds a different
57
- thing. None of the others is built for curated, durable team knowledge:
58
-
59
- | | OKF bundle (this) | `CLAUDE.md` / `AGENTS.md` | Agent auto-memory | Wiki / Notion |
60
- | ------------------------------ | ----------------------------------------------------- | -------------------------- | ------------------------ | ---------------- |
61
- | Holds | curated team knowledge | standing instructions | what one agent picked up | human docs |
62
- | Versioned with the code | ✅ | ✅ | ❌ | ❌ |
63
- | Portable across agents | ✅ plain Markdown + YAML | ⚠️ per-harness conventions | ❌ per-agent store | ⚠️ export needed |
64
- | Typed and queryable | ✅ frontmatter + graph | ❌ prose | ❌ | ⚠️ partially |
65
- | Reviewed in PRs | ✅ | ✅ | ❌ implicit | ⚠️ rarely |
66
- | Scales past one context window | ✅ progressive disclosure<br>(`okf index` + `search`) | ❌ loaded whole | ⚠️ partially | n/a |
67
- | Checked by tooling | ✅ exit codes for CI<br>(`okf validate` + `lint`) | ❌ | ❌ | ❌ |
68
-
69
- The last two rows are this gem's job. Scaling past one context window is
70
- progressive disclosure — `okf index` reads the map, `okf search` pulls only the
71
- concepts a task needs, so the bundle is never loaded whole. And drift never
72
- hides here: the other homes have no detector, but `okf validate` and `lint` turn
73
- a bundle's drift into findings you can gate on in CI.
74
-
75
- ## What a bundle looks like
76
-
77
- A bundle is just a directory; each concept is one Markdown file whose path is its
78
- id. This repo documents _itself_ in OKF, so the tree below is real:
1
+ # okf
79
2
 
80
- ```
81
- .okf/
82
- ├── index.md # progressive-disclosure map (root carries okf_version)
83
- ├── log.md # ISO-dated change history, newest first
84
- ├── overview.md
85
- ├── format/frontmatter.md
86
- ├── model/graph.md
87
- └── capabilities/graph-server.md # one concept = one file
88
- ```
89
-
90
- The only hard requirement is YAML frontmatter with a non-empty `type`; everything
91
- else is optional and tolerated when missing. A concept (here the real
92
- `capabilities/graph-server.md`, body trimmed) reads:
93
-
94
- ```markdown
95
- ---
96
- type: Capability
97
- title: Interactive graph server (server)
98
- description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
99
- resource: lib/okf/server/app.rb
100
- tags: [server, graph, rack, diagram]
101
- timestamp: 2026-07-11T12:00:00Z
102
- ---
103
-
104
- # Overview
3
+ **The complete Open Knowledge Format toolkit: an agent skill, a CLI and library,
4
+ ranked search, and a live knowledge graph. 100% local.**
105
5
 
106
- `okf server` boots an interactive view of the [graph](../model/graph.md)
107
- ```
108
-
109
- That bundle is this gem's own documentation. Clone the repo and run
110
- `okf server .okf` to browse it as an interactive graph.
111
-
112
- ## Try it in four steps
6
+ [Site](https://okfgem.com) · [Docs](https://okfgem.com/docs/) ·
7
+ [Live demo](https://demo.okfgem.com) ·
8
+ [Project README](https://github.com/serradura/okf-gem#readme)
113
9
 
114
- From zero to your first bundle.
10
+ OKF (Open Knowledge Format) is portable project knowledge: Markdown files with
11
+ YAML frontmatter that both humans and agents read from one source. This gem is
12
+ the Ruby-native way to work with it — the decisions and the reasoning an agent
13
+ cannot re-derive from the code, versioned beside the code they explain.
115
14
 
116
- **1. Get the `okf` command.** Two ways in; either one puts `okf` on your `PATH`.
15
+ One install carries the whole workflow: an **Agent Skill** so your agent writes
16
+ and curates the knowledge, a **CLI and Ruby library** so it stays correct, and a
17
+ **Graph** so anyone can see the shape of what the team knows.
117
18
 
118
- ```bash
119
- gem install okf # with Ruby
120
- curl -fsSL https://docker.okfgem.com/install.sh | sh # no Ruby? Docker
121
- ```
19
+ It adds no service to your stack. `rack`, `webrick` and `minifts` are the only
20
+ runtime dependencies, there is no native extension and no build step, and it runs
21
+ on every Ruby since **2.4** the one your OS already ships.
122
22
 
123
- **2. Install the skill.** Teach your agent the format — Claude Code, or any
124
- other agent.
23
+ ## Install
125
24
 
126
25
  ```bash
127
- okf skill .claude # or: okf skill .agents
26
+ gem install okf
27
+ # or, in a project
28
+ bundle add okf
128
29
  ```
129
30
 
130
- **3. Start an agent session** where your project lives.
31
+ No Ruby? The official image carries the CLI:
131
32
 
132
33
  ```bash
133
- claude
34
+ docker run --rm -v "$PWD:/data" ghcr.io/serradura/okf validate .
134
35
  ```
135
36
 
136
- **4. Make your first bundle.** Two ways in, by what you already have: docs keep
137
- every word, code gets written up for you.
37
+ The Docker-backed [`okf` command](https://docker.okfgem.com) drops the prefix so
38
+ every verb reads exactly like the native CLI.
138
39
 
139
- ```
140
- /okf migrate <path-to-your-docs> # have docs? adopted in place, bodies verbatim
141
- /okf produce based on <path-to-your-code> # only code? the skill authors the concepts
142
- ```
143
-
144
- > [!TIP]
145
- > **Once you have a bundle**, run `/okf maintain` in the agent session to keep it
146
- > in sync as the code changes, and `okf server <folder>` to explore it as a graph.
147
- > In Claude Code, the [plugin](#claude-code-plugin) adds a post-edit curation hook
148
- > that runs `validate` + `lint` for you.
149
-
150
- The package, end to end:
151
-
152
- <p align="center">
153
- <picture>
154
- <source media="(prefers-color-scheme: dark)" srcset=".github/overview-dark.png">
155
- <img src=".github/overview-light.png" width="760" alt="The package: the Agent Skill (your coding agent authors and curates, you stay the editor) writes and maintains the bundle, a folder of Markdown + YAML in your repo where one concept is one file and links between files are the knowledge graph. The bundle is read by the CLI/Lib (validate: legal OKF per section 9; lint: well-curated and fresh; search: ranked retrieval; require okf for Ruby objects) and by the Graph, in four modes: okf server (a live local server), okf render (the same page exported as one static, self-contained HTML file you can host anywhere), okf registry (every registered bundle behind one hub), and OKF::Server::App (the Rack app mounted in a Rails route). One gem, 100% local, Ruby 2.4 or newer, only rack, webrick and minifts as dependencies.">
156
- </picture>
157
- </p>
158
-
159
- What the gem does, and which verb does it. This table is the map; **[the
160
- docs](https://okfgem.com/docs/)** are the manual.
161
-
162
- | Capability | What it answers | Verb |
163
- | ------------------------------------------------------------- | --------------------------------- | -------------------------- |
164
- | [Companion agent skill](https://okfgem.com/docs/skill/) | Can an agent author it? | `skill` |
165
- | [Conformance validator](https://okfgem.com/docs/cli/validate/) | Is this a legal OKF bundle? | `validate` |
166
- | [Curation linter](https://okfgem.com/docs/cli/lint/) | Is it navigable, complete, fresh? | `lint` / `loose` |
167
- | [Ranked text search](https://okfgem.com/docs/cli/search/) | Which concept covers X? | `search` |
168
- | [Read views](https://okfgem.com/docs/cli/) | What is in here, and where? | `index` / `dirs` / `catalog` |
169
- | [Interactive graph server](https://okfgem.com/docs/cli/server/) | Can I explore it visually? | `server` |
170
- | [Static render](https://okfgem.com/docs/cli/render/) | Can I ship a serverless snapshot? | `render` |
171
- | [Library API](https://okfgem.com/docs/library/) | Can my Ruby program use it? | in-process |
172
-
173
- And because knowledge rarely lives in one bundle, a per-user
174
- [registry](.okf/registry.md) gives each bundle a name: `okf registry set ./docs`
175
- once, then `@docs` works anywhere a `<dir>` does — from any directory — and a
176
- bare `okf server` hosts every registered bundle behind one hub.
177
-
178
- > [!TIP]
179
- > **Browse the gem as knowledge, not just docs.** This README is the front door;
180
- > the depth lives in the [`.okf/`](.okf) bundle this repo ships. Start at the
181
- > [overview](.okf/overview.md), then follow the graph into the
182
- > [capabilities](.okf/capabilities/) (what it does), the
183
- > [design constraints](.okf/design/) (why it stays this light), and the
184
- > [format itself](.okf/format/) (what it operates on). Run `okf server .okf` to
185
- > walk the same bundle as an interactive graph.
186
-
187
- **It installs on the Ruby your OS already ships** — every Ruby since 2.4, three
188
- small dependencies, no native extension and no build step — so there is nothing
189
- to provision and nothing to keep up to date. The
190
- [design constraints](.okf/design/) that hold that line are enforced by tests on
191
- every supported Ruby.
192
-
193
- ## Installation
194
-
195
- > **In Claude Code**, the plugin is the fastest path: two commands install the whole
196
- > toolchain (skill, `/okf:gem`, and the curation hook). See
197
- > [Claude Code plugin](#claude-code-plugin). Everywhere else, install the gem:
40
+ ## Four steps to your first bundle
198
41
 
199
42
  ```bash
200
- gem install okf
201
- # or, in a project
202
- bundle add okf
43
+ okf skill .claude # 1. teach your agent the format (or: okf skill .agents)
44
+ claude # 2. start an agent session where your project lives
203
45
  ```
204
46
 
205
- Tested and supported on every Ruby from **2.4 through 4.0**. From a checkout,
206
- `bundle exec rake install` builds and installs it locally.
207
-
208
- ### No Ruby? Use Docker
209
-
210
- The official image bundles the CLI, so every `okf` command runs against a bundle
211
- you mount at `/data`:
212
-
213
- ```bash
214
- docker run --rm -v "$PWD:/data" ghcr.io/serradura/okf validate .
215
- docker run --rm -v "$PWD:/data" -p 8808:8808 ghcr.io/serradura/okf server . --bind 0.0.0.0
216
47
  ```
217
-
218
- Tired of the long line? The Docker-backed [`okf` command](https://docker.okfgem.com)
219
- drops the prefix so every verb reads exactly like the native CLI:
48
+ /okf migrate <path-to-your-docs> # 3a. have docs? adopted in place, bodies verbatim
49
+ /okf produce based on <path-to-your-code> # 3b. only code? the skill authors the concepts
50
+ ```
220
51
 
221
52
  ```bash
222
- curl -fsSL https://docker.okfgem.com/install.sh | sh # PowerShell: irm https://docker.okfgem.com/install.ps1 | iex
223
- okf validate .
224
- okf server .
53
+ okf server <folder> # 4. explore what you got, as a live graph
225
54
  ```
226
55
 
227
- Images are published for `linux/amd64` and `linux/arm64` on
228
- [ghcr.io](https://github.com/serradura/okf-gem/pkgs/container/okf).
56
+ Then `/okf maintain` keeps it in sync as the code changes.
229
57
 
230
- ## Command line
58
+ ## The command line
231
59
 
232
- These verbs are written to be read by an **agent first and a person second** —
233
- that is what the skill drives, with no wrapper in between. Every read verb takes
234
- `--json`, the list views project down to the fields you ask for
235
- (`--fields`/`--except`), so nothing pays for output it will not read, and the
236
- exit codes are stable enough to branch on in CI. The same commands render as
237
- scannable plain text when a human is the one looking.
60
+ Written to be read by an **agent first and a person second** — that is what the
61
+ skill drives, with no wrapper in between. Every read verb takes `--json`, the
62
+ list views project down to the fields you ask for (`--fields`/`--except`), and
63
+ the exit codes are stable enough to branch on in CI.
238
64
 
239
65
  ```bash
240
66
  okf validate <dir|@slug> # is this legal OKF?
@@ -244,10 +70,10 @@ okf search <dir|@slug…|@all> <term…> # ranked retrieval; @all sp
244
70
  okf index <dir|@slug> [--dir D] [--depth N] # the §6 map: index bodies, rollups, listings
245
71
  okf dirs <dir|@slug> [--dir D] [--depth N] # the shape: every directory and what it holds
246
72
  okf catalog | files | tags | types | stats <dir|@slug> # the browser views, on the CLI
247
- okf graph <dir|@slug> [--hubs] # the raw graph; --hubs ranks by inbound links
73
+ okf graph <dir|@slug> [--hubs] [--traffic] # the raw graph; --hubs ranks concepts, --traffic dirs
248
74
  okf server [DIR|@slug…] [-p PORT] [--bind ADDR] # the live graph: one bundle, or all of them
249
75
  okf render <dir|@slug> [-o FILE] # the same page as one static, self-contained file
250
- okf registry list | set | del | default | rename # name your bundles (see below)
76
+ okf registry init | list | set | del | default | rename | group | ungroup # name & group your bundles
251
77
  okf skill <dest> # install the companion agent skill
252
78
  okf --version
253
79
  ```
@@ -256,146 +82,23 @@ Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
256
82
  threshold crossed), `2` usage error. Every flag is in `okf <verb> --help` and in
257
83
  [the docs](https://okfgem.com/docs/).
258
84
 
259
- ```bash
260
- $ okf validate docs
261
- OKF v0.1 conformance docs
262
- concepts: 37 index.md: 10 log.md: 1
263
- ! warn features/link-suggestions.md: cross-link target not found: `/graph-view.md` (tolerated under §5.3)
264
-
265
- ✓ conformant (33 warning(s))
266
-
267
- $ okf server docs
268
- serving 37 concepts at http://127.0.0.1:8808 (Ctrl-C to stop)
269
-
270
- $ okf render docs > public/index.html # the same page, static — host it anywhere
271
- ```
272
-
273
- ### Reading a big bundle a level at a time
274
-
275
- A few hundred concepts is a map nobody reads whole, so `index` and `dirs` descend
276
- instead of dumping. `--dir` takes a directory **and everything under it**,
277
- `--depth N` bounds how far below that it goes, and the two compose the way you
278
- actually walk a tree:
279
-
280
- ```bash
281
- okf dirs @handbook # the shape: every dir, what it holds directly and below
282
- okf index @handbook --depth 1 --no-body # the top of the map, no prose
283
- okf index @handbook --dir platform/api # now open one branch — with the chain that places it
284
- ```
285
-
286
- Naming a `--dir` brings its ancestors along, marked `↑`, so a branch is never
287
- shown adrift of the context that says what it is — the root `index.md`'s prose
288
- first among it.
85
+ **A registry names your bundles.** `okf registry set ./docs --as handbook` once,
86
+ then `@handbook` works anywhere a `<dir>` does, from any directory; `okf search
87
+ @all rate limit` spans every one of them, and a bare `okf server` hosts them all
88
+ behind one hub. `okf registry init` scopes one to a single project instead, and a
89
+ committed `.okf-registry.json` travels with the repo.
289
90
 
290
- For an agent the saving is the whole point. On a 400-concept bundle the full
291
- `okf index --json` is 313 KB; the skeleton it orients on is 2.8 KB:
91
+ **A big bundle is read a level at a time.** `okf index --depth 1 --except
92
+ body,listing` is the map an agent orients on on a 400-concept bundle, 2.8 KB
93
+ against the full 313 KB — and `--dir` then opens one branch, bringing the
94
+ ancestors that say what it is.
292
95
 
293
- ```bash
294
- okf index @handbook --json --depth 1 --except body,listing
295
- ```
296
-
297
- ## The graph
298
-
299
- <picture>
300
- <source media="(prefers-color-scheme: dark)" srcset=".github/server-dark.png">
301
- <img src=".github/server-light.png" alt="The okf graph server: a force-directed knowledge graph with a concept selected, its neighbors highlighted and the rest of the bundle dimmed, and the inspector panel showing the concept's type, description, tags, and every concept it links to and from, each labelled with its type.">
302
- </picture>
303
-
304
- _The graph server on this repo's own [`.okf`](.okf) bundle, with the `overview`
305
- concept selected. Try it live at
306
- **[demo.okfgem.com](https://demo.okfgem.com)**._
307
-
308
- One page, from a phone to a desktop: the navigation rail becomes a drawer, the
309
- toolbar folds into a `⚙` sheet, and a tap opens a preview card at the bottom edge
310
- rather than a panel over the whole viewport, so the graph stays live while you
311
- read. Drag the card up for the neighbourhood, tap a link and it walks there in
312
- place.
313
-
314
- It is keyboard-first: **`⌘/Ctrl-K`** opens a command palette that searches
315
- concepts, jumps to a view, and — behind a [hub](#one-registry-many-bundles) —
316
- switches bundles. **`/`** jumps to the current view's search, **`?`** answers with
317
- every shortcut. Cluster mode boxes the graph by directory and nests as deep as
318
- your tree does.
319
-
320
- To skip the server entirely, **`okf render <dir>`** writes that same page as one
321
- self-contained HTML file, the whole bundle baked in, so you can publish the graph
322
- on GitHub Pages or any static host.
323
-
324
- ### One registry, many bundles
325
-
326
- The [registry](.okf/registry.md) is a per-user, ordered list of bundles in one
327
- plain JSON file (`$OKF_HOME/registry.json`, default `~/.okf`) — hand-editable,
328
- greppable, no database. It stores references, never content: the bundles stay in
329
- the repos that own them.
330
-
331
- ```bash
332
- okf registry set ./docs --as handbook # give the bundle a name
333
- okf lint @handbook # @slug works wherever a <dir> does, from anywhere
334
- okf search @all rate limit # ranked retrieval across every registered bundle
335
- okf server # no args: the whole registry behind one hub
336
- ```
337
-
338
- Behind the hub each bundle mounts at `/b/<slug>/`, `/b/` lists them all, and the
339
- `⌘/Ctrl-K` palette both switches bundles and **searches every one at once** — type
340
- a few words and the matching concepts appear with their bundle and a snippet, from
341
- wherever you are.
342
-
343
- The ⚙ rail opens **Bundles**, the registry on the graph page itself: make
344
- default, rename, remove, where you are already reading. Those controls are the one
345
- thing that does not follow you onto a network — bind anywhere but loopback and
346
- they are refused outright, since `--bind 0.0.0.0` is how a personal tool becomes a
347
- public one.
348
-
349
- ## Agent skill
350
-
351
- The gem carries the [companion OKF agent skill](.okf/capabilities/agent-skill.md):
352
- a `SKILL.md` plus reference and template files that teach a coding agent to
353
- author, maintain, and consume OKF bundles and to drive the commands above.
354
- Because the skill ships inside the gem, installing the gem already puts the skill
355
- on your machine, and the skill's CLI reference can never drift from the
356
- executable it was released with.
357
-
358
- The skill routes a small set of verbs. In Claude Code they run as `/okf:gem
359
- <verb>`; used standalone, the skill infers the verb from your request.
360
-
361
- | Verb | What it does |
362
- | ---------------- | ----------------------------------------------------------------------------------------------------------- |
363
- | _(none)_ | Orient on the bundle and recommend the highest-value next move |
364
- | `search` | Answer a question from the bundle, token-lean: the map, the finder, only the winning bodies |
365
- | `produce` | Create or extend a bundle from code, docs, or knowledge in people's heads |
366
- | `migrate` | Adopt existing Markdown docs in place: frontmatter and reserved files added, bodies kept verbatim |
367
- | `maintain` | Sync the bundle's content with reality after the code or docs change |
368
- | `refine` | Restructure it for retrieval: evidence-first, cohesion over balance — proposes, never applies |
369
- | `consume` | Use the bundle as context for a task, writing back what you learn |
370
- | `curate` | Structural upkeep as it stands: `validate` + `lint` + `loose` |
371
- | `doctor` | Install and verify the CLI, then doctor the bundle |
372
- | `<okf-cli-verb>` | Run any CLI verb (`validate`, `lint`, `search`, `index`, `server`, the read views) and interpret its output |
373
-
374
- Three of those look alike and are not, which is the distinction worth learning
375
- first: **`curate`** keeps the bundle *sound* (the structure as it stands),
376
- **`maintain`** keeps it *true* (the code changed, so the content must catch up),
377
- and **`refine`** changes *where knowledge lives* — the folder a concept sits in,
378
- a fact re-explained in three overviews. Reach for `refine` when nothing is wrong
379
- and everything is hard to find. It reads the evidence, then hands you a proposal
380
- — it never rearranges your bundle on its own.
381
-
382
- Point it at your agent's config directory and the tree settles in its own
383
- `skills/okf/` folder, so a shared skills directory never gets the files loose:
384
-
385
- ```bash
386
- okf skill .claude # Claude Code -> .claude/skills/okf
387
- okf skill .agents # agent-agnostic -> .agents/skills/okf
388
- ```
389
-
390
- The resolved directory must be empty unless you pass `--force`, so a customized
391
- skill is never clobbered.
392
-
393
- ## Library
96
+ ## The library
394
97
 
395
98
  `require "okf"` gives you the whole thing as Ruby objects — two layers: pure
396
99
  in-memory data (`OKF::Concept`, `OKF::Bundle`) you build and analyze with no disk
397
100
  involved, and on-disk handles (`OKF::Concept::File`, `OKF::Bundle::Folder`) that
398
- add load/save/reload/delete, an "ActiveRecord for the filesystem".
101
+ add load/save/reload/delete.
399
102
 
400
103
  ```ruby
401
104
  require "okf"
@@ -411,106 +114,52 @@ OKF::Server::App.new(folder) # => a Rack app: the interactive graph, mountab
411
114
  ```
412
115
 
413
116
  That last line is the point of the Rack app: the graph mounts inside an app you
414
- already have, auth included. The [Rails guide](https://okfgem.com/docs/guides/rails/)
415
- walks it, and the [library API](.okf/capabilities/library-api.md) concept covers
416
- the pure layer, the writer, and the lower-level pieces.
117
+ already have, auth included. The
118
+ [Rails guide](https://okfgem.com/docs/guides/rails/) walks it, and the
119
+ [library API](https://okfgem.com/docs/library/) covers the pure layer, the
120
+ writer, and the lower-level pieces.
417
121
 
418
- ### validate and lint are two different questions
122
+ ## validate and lint are two different questions
419
123
 
420
- `validate` (the [conformance validator](.okf/capabilities/validator.md)) asks
421
- _"is this legal OKF?"_ and implements the spec's
422
- [§9](lib/okf/skill/reference/SPEC.md#9-conformance) exactly which means it is
423
- *forbidden* to reject a bundle for a broken link or a missing optional field.
124
+ `validate` asks *"is this legal OKF?"* and implements the spec's
125
+ [§9](lib/okf/skill/reference/SPEC.md#9-conformance) exactly which means it is *forbidden* to
126
+ reject a bundle for a broken link or a missing optional field.
424
127
 
425
- `lint` (the [curation linter](.okf/capabilities/linter.md)) asks the
426
- complementary question, _"is this well-curated, navigable, trustworthy?"_, over
427
- exactly those tolerated things: reachability, backlog, completeness, freshness,
428
- provenance, hygiene. It is advisory and exits `0` even with findings unless you
429
- pass `--fail-on warn`.
128
+ `lint` asks the complementary question, *"is this well-curated, navigable,
129
+ trustworthy?"*, over exactly those tolerated things: reachability, backlog,
130
+ completeness, freshness, provenance, hygiene. It is advisory and exits `0` even
131
+ with findings unless you pass `--fail-on warn`.
430
132
 
431
133
  Keeping them apart is what lets you gate CI on conformance without gating it on
432
134
  taste. `lint --json` is also the structured input an agent reads to reason about
433
135
  the two things no checker can compute — contradictions, and *semantic* staleness.
434
136
 
435
- ## Extending okf, and running it safely
137
+ ## Extending it
436
138
 
437
139
  Publish a gem named `okf-*` carrying an `okf/plugin.rb` and installing it is the
438
140
  whole installation: your verb answers to `okf` and behaves like a built-in.
439
141
  Nothing an addon registers can displace one, and a broken addon is skipped rather
440
- than taking the CLI down. Contract and threat model:
441
- [extension points](.okf/design/extension-points.md).
142
+ than taking the CLI down. [`okf-mcp`](https://rubygems.org/gems/okf-mcp) is the
143
+ first one — install it and `okf mcp` serves your bundles over the Model Context
144
+ Protocol, with nothing in this gem naming it.
442
145
 
443
- The graph page treats a bundle as untrusted content: inlined data is escaped, and
146
+ The graph page treats a bundle as untrusted content: inlined data is escaped and
444
147
  every concept body is sanitized before it reaches the DOM, so a script hidden in
445
148
  Markdown is stripped rather than run. It still loads libraries from a CDN, so
446
149
  treat an unfamiliar bundle the way you would treat any document from a source you
447
- do not know. Full write-up:
448
- [server trust boundary](.okf/design/server-trust-boundary.md).
150
+ do not know.
449
151
 
450
- ## Claude Code plugin
152
+ ## More
451
153
 
452
- This repository doubles as a Claude Code plugin marketplace, so the whole
453
- toolchain installs with two commands inside Claude Code:
454
-
455
- ```
456
- /plugin marketplace add serradura/okf-gem
457
- /plugin install okf@okfgem
458
- ```
459
-
460
- The plugin carries three pieces: the [`okf` skill](#agent-skill) above;
461
- **`/okf:gem`**, a front door that hands its arguments to the skill unchanged (no
462
- arguments: it orients on your bundle and recommends the next move, never
463
- auto-runs); and a **curation hook** that runs `okf validate` + `okf lint` after
464
- every edit inside a bundle and returns the findings as context. The checks are
465
- the CLI's own, so the feedback is deterministic.
466
-
467
- The hook stays silent outside bundles, and it is config-free to switch off:
468
- `OKF_CURATE_DISABLED=1` turns it off, `OKF_CURATE_QUIET=1` keeps the findings
469
- without the install suggestion, and an `<!-- okf-disable -->` comment skips one
470
- file.
471
-
472
- Prefer no plugin? `gem install okf && okf skill .claude` installs the skill
473
- alone, and the skill itself instructs the agent to run the same checks after
474
- editing a bundle.
475
-
476
- ## Development
477
-
478
- ```bash
479
- bin/setup # install dependencies
480
- bundle exec rake # tests + RuboCop (what CI runs)
481
- bundle exec rake test # just the test suite
482
- ruby -Ilib exe/okf validate <dir> # run the CLI from a checkout
483
- ```
484
-
485
- The suite runs on every supported Ruby; to check the 2.4 floor locally:
486
-
487
- ```bash
488
- docker run --rm -v "$PWD":/src:ro ruby:2.4 bash -c \
489
- "cp -a /src /build && cd /build && rm -f Gemfile.lock && bundle install --quiet && bundle exec rake test"
490
- ```
491
-
492
- The graph page has its own suite in a real browser (`bundle exec rake
493
- browser:setup`, then `rake test:browser`). See [AGENTS.md](AGENTS.md) for the
494
- maintainer guide.
495
-
496
- ## Contributing
497
-
498
- Bug reports and pull requests are welcome on GitHub at
499
- <https://github.com/serradura/okf-gem>. This project is intended to be a safe,
500
- welcoming space for collaboration, and contributors are expected to adhere to
501
- the [code of conduct](CODE_OF_CONDUCT.md).
154
+ The [project README](https://github.com/serradura/okf-gem#readme) carries the
155
+ diagrams, the comparison with `CLAUDE.md`, agent auto-memory and wikis, and the
156
+ Claude Code plugin. The [docs](https://okfgem.com/docs/) are the manual. And the
157
+ repo documents *itself* in OKF — clone it and run `okf server .okf` to read this
158
+ gem's own knowledge as a graph.
502
159
 
503
160
  ## License
504
161
 
505
- The gem is available as open source under the terms of the
506
- [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) (see
507
- `LICENSE.txt`). The Open Knowledge Format specification bundled with the skill
508
- is authored by Google Cloud Platform and included under its own Apache-2.0
509
- license, Copyright (c) Google LLC. See `NOTICE` and
162
+ Apache-2.0; see `LICENSE.txt`. The Open Knowledge Format specification bundled
163
+ with the skill is authored by Google Cloud Platform and included under its own
164
+ Apache-2.0 license, Copyright (c) Google LLC. See `NOTICE` and
510
165
  `lib/okf/skill/reference/APACHE-2.0.txt`.
511
-
512
- [okf-skills](https://github.com/scaccogatto/okf-skills) by Marco Boffo, a Python
513
- OKF toolkit for Claude Code with a feature-rich interactive graph view, was an
514
- early inspiration for this gem's Claude Code plugin and for the knowledge-as-code
515
- comparison in [Why OKF](#why-okf). okf-gem takes a different shape: a Ruby-native
516
- gem built around the `okf` CLI and an embeddable library.
@@ -42,6 +42,10 @@ module OKF
42
42
  @bundle.graph(minimal: minimal, body: body)
43
43
  end
44
44
 
45
+ def skeleton
46
+ @bundle.skeleton
47
+ end
48
+
45
49
  def catalog
46
50
  @bundle.catalog
47
51
  end
@@ -50,6 +54,10 @@ module OKF
50
54
  @bundle.hubs
51
55
  end
52
56
 
57
+ def directories
58
+ @bundle.directories
59
+ end
60
+
53
61
  def directory_index
54
62
  @bundle.directory_index
55
63
  end
@@ -100,6 +108,17 @@ module OKF
100
108
  Concept::File.read(root: @root, path: path)
101
109
  end
102
110
 
111
+ # The raw markdown bytes for one concept id — read once through the same
112
+ # containment guard as #concept, but without the parse #concept pays for,
113
+ # so a caller that wants the file verbatim (never a re-serialized copy)
114
+ # does one read, not a read plus a discarded frontmatter parse. nil when no
115
+ # concept has that id; raises Path::Error if the file has become a symlink
116
+ # escaping the root, and the reader's own SystemCallError if it has gone.
117
+ def concept_source(id)
118
+ path = @bundle.paths_by_id[id] or return nil
119
+ Concept::File.new(root: @root, path: path).read
120
+ end
121
+
103
122
  # Materialize the in-memory bundle to disk (Writer validates §9 before
104
123
  # publishing, so a malformed bundle is never written).
105
124
  def save(overwrite: false)
@@ -127,8 +146,12 @@ module OKF
127
146
  end
128
147
 
129
148
  def log_content(path)
130
- File.read(File.join(@root, path), encoding: "UTF-8")
131
- rescue SystemCallError
149
+ # Live, but through the same containment as every other read: a log.md
150
+ # that was a real file at boot and is a symlink out of the root now falls
151
+ # back to the boot snapshot rather than serving the target — the same
152
+ # answer a vanished file gets, since an escape is a file we must not read.
153
+ SafeRead.read!(@root, File.join(@root, path))
154
+ rescue SystemCallError, Path::Error
132
155
  @bundle.reserved_content(path)
133
156
  end
134
157
  end