okf 1.0.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1f08bf7a4a1a49ef0bea5953b380df85bdbe79e1b30998e0d168d122d4a20c2
4
- data.tar.gz: 97aff0312275def9b514c0e50bcc3872a73e3a304efa7ac8d85046a846711a7f
3
+ metadata.gz: 95f1db166e67b4a3c4b4fbd679bce25ef19e3de3f50f010e13e6b1630ddacfcf
4
+ data.tar.gz: cef59494dbd959585ba85f631b025eb5ba88ab94e38c6d440ac8aed1b2fbd61a
5
5
  SHA512:
6
- metadata.gz: '09c1784bee88f5a74087345fa3ee26a13ba2875ff9447559852c23cf294f6f05ffa457eabdd67cbbac4bc5e255b3cc91f128863fab92d9b5a88f3ae41062e871'
7
- data.tar.gz: d26885ca8683d2de6ad036d7a158089ba4b7e12437131b617687bad51bd15157f64dd148bcd7f8f502a95ae48d644f9c5674464e82ff3f97bc34faf61a24dc24
6
+ metadata.gz: ff17b4c61c5e9fb63e63235fb19fb51b75ce7626acafdfaa88a367f5a6c40a2e9c2eabe7af0283fac5cf6edfd1349b22aa900fa23c321522bd29b19e76befd95
7
+ data.tar.gz: 8846d3604a36b3818024f21d88d04cc62bc91d374dac1a7fd34e17d6eb8269fcf72f7675191a1b3243144359d847c734cf049d4025cf962fb5bf7369f7cc8fac
@@ -4,14 +4,15 @@ title: Interactive graph server (server)
4
4
  description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
5
5
  resource: lib/okf/server/app.rb
6
6
  tags: [server, graph, rack, diagram]
7
- timestamp: 2026-07-11T12:00:00Z
7
+ timestamp: 2026-07-12T12:00:00Z
8
8
  ---
9
9
 
10
10
  # Overview
11
11
 
12
12
  `okf server` boots an interactive view of the [graph](../model/graph.md):
13
13
  `OKF::Server::App` is a Rack app that serves one self-contained HTML page which
14
- draws the bundle with Cytoscape and renders concept bodies with marked. Because
14
+ draws the bundle with Cytoscape and renders concept bodies with marked, sanitized
15
+ by DOMPurify. Because
15
16
  it is a plain Rack app, it also mounts inside a host application (e.g. a Rails
16
17
  route) — the built-in WEBrick runner is just the default, injected so tests drive
17
18
  it without opening a socket.
@@ -19,8 +20,9 @@ it without opening a socket.
19
20
  # The page stays self-contained
20
21
 
21
22
  One ERB template, inline CSS and JS, no build step and no bundler. The only
22
- external assets are Cytoscape and marked from a CDN — plus Mermaid, lazy-loaded
23
- only when a concept body actually contains a diagram; everything else is inlined.
23
+ external assets are Cytoscape, marked, and DOMPurify from a CDN — plus Mermaid,
24
+ lazy-loaded only when a concept body actually contains a diagram; everything else
25
+ is inlined.
24
26
  The graph draws from a **minimal** node payload and pulls each concept's body
25
27
  **on demand** via `fetch()`, which is why even a large bundle loads fast.
26
28
 
@@ -35,7 +37,7 @@ sequenceDiagram
35
37
  Note over A,B: angle brackets escaped (json_for_script) — safe
36
38
  B->>A: GET /node?id=… (on demand)
37
39
  A-->>B: concept Markdown body
38
- Note over A,B: marked renders it UNSANITIZEDtrust boundary
40
+ Note over A,B: marked renders it, DOMPurify sanitizes it safe
39
41
  ```
40
42
 
41
43
  # Endpoints
@@ -49,11 +51,12 @@ sequenceDiagram
49
51
 
50
52
  # Trust boundary
51
53
 
52
- Fetched Markdown bodies are rendered **without sanitization**, so only serve
53
- bundles you trust. Data inlined into the page is safe it goes through
54
- `json_for_script`, which escapes `<` so it cannot break out of its `<script>`
55
- but the on-demand body is not. See the
56
- [server trust boundary](../design/server-trust-boundary.md) for the full picture.
54
+ Both paths into the page are guarded. Inlined data goes through `json_for_script`,
55
+ which escapes `<` so it cannot break out of its `<script>`; each fetched body is
56
+ run through `DOMPurify.sanitize(marked.parse(...))`, which strips any script or
57
+ handler before it reaches the DOM. See the
58
+ [server trust boundary](../design/server-trust-boundary.md) for what that does and
59
+ does not cover.
57
60
 
58
61
  # Citations
59
62
 
@@ -4,7 +4,7 @@ title: Library API
4
4
  description: The Ruby surface — a pure in-memory model plus on-disk handles, an "ActiveRecord for the filesystem".
5
5
  resource: lib/okf.rb
6
6
  tags: [library, api, ruby, diagram]
7
- timestamp: 2026-07-11T12:00:00Z
7
+ timestamp: 2026-07-12T12:00:00Z
8
8
  ---
9
9
 
10
10
  # Overview
@@ -18,6 +18,11 @@ gives you two layers, split cleanly by the [core/shell rule](../design/core-shel
18
18
  - **on-disk handles** — `OKF::Concept::File` and `OKF::Bundle::Folder`, which add
19
19
  `load` / `save` / `reload` / `delete` on top of the pure model.
20
20
 
21
+ `require "okf"` stops at those two layers: the [CLI](../cli.md) and the skill
22
+ installer load only when asked for (from `exe/okf`, or an explicit
23
+ `require "okf/cli"` / `require "okf/skill"`), so an app embedding the library
24
+ never drags in the command-line machinery.
25
+
21
26
  ```mermaid
22
27
  classDiagram
23
28
  class Concept {
data/.okf/design/index.md CHANGED
@@ -7,4 +7,4 @@ the code looks the way it does.
7
7
  * [Core/shell split](core-shell-split.md) - pure logic must never touch disk, stdio, or the shell layer; a test enforces it.
8
8
  * [Ruby 2.4 floor](ruby-floor.md) - runs on the Ruby an OS already ships; newer APIs are banned.
9
9
  * [Runtime dependencies](runtime-dependencies.md) - exactly `rack` and `webrick`, no ActiveSupport.
10
- * [Server trust boundary](server-trust-boundary.md) - the served page renders concept bodies unsanitized.
10
+ * [Server trust boundary](server-trust-boundary.md) - the served page sanitizes concept bodies and escapes inlined data; both XSS paths are closed.
@@ -1,35 +1,40 @@
1
1
  ---
2
2
  type: Constraint
3
3
  title: The server trust boundary
4
- description: The served page renders concept bodies without sanitization, so only serve bundles you trust.
4
+ description: The served page sanitizes each concept body before rendering and escapes inlined data, so both XSS paths into the page are closed.
5
5
  resource: lib/okf/server/templates/graph.html.erb
6
6
  tags: [security, server, xss]
7
- timestamp: 2026-07-11T12:00:00Z
7
+ timestamp: 2026-07-12T12:00:00Z
8
8
  ---
9
9
 
10
10
  # Overview
11
11
 
12
- The [graph server](../capabilities/graph-server.md) is designed for trusted
13
- bundles. Its page renders each concept's Markdown body **without sanitization**
14
- (marked, no sanitizer), so a hostile bundle could carry active content. The rule
15
- is simple: **only serve bundles you trust.**
12
+ The [graph server](../capabilities/graph-server.md) renders whatever bundle you
13
+ point it at, and a bundle is just files, so the page has to assume a body might
14
+ carry active content. Two defenses handle that one for each path into the page.
16
15
 
17
16
  # Where the boundary sits
18
17
 
19
- There are two different data paths into the page, and only one is escaped:
18
+ There are two data paths into the page, and each carries its own guard:
20
19
 
21
20
  | Path | Handling | Safe? |
22
21
  |------|----------|-------|
23
22
  | Graph data **inlined** into the page | through `json_for_script`, which escapes `<` | yes — it cannot break out of its `<script>` |
24
- | Concept bodies **fetched** on demand (`/node?id=`) | rendered as Markdown, unsanitized | nothis is the trust boundary |
23
+ | Concept bodies **fetched** on demand (`/node?id=`) | `marked` renders the Markdown, then `DOMPurify.sanitize` scrubs it before it reaches the DOM | yesscripts, handlers, and `javascript:` URLs are stripped |
25
24
 
26
- So the XSS boundary is not the inlined data (that is handled); it is the
27
- on-demand [body](../format/cross-links.md) render. The
28
- [self-contained page](../capabilities/graph-server.md) keeps external assets down
29
- to Cytoscape and marked from a CDN, but that does not make the rendered body
30
- safe.
25
+ The [description](../format/cross-links.md) shown in the inspector takes a third
26
+ path and never needs the client's help: the server escapes it
27
+ (`OKF::Server::App#description_fragment`) before sending it, so it arrives inert.
28
+
29
+ # What sanitizing does not cover
30
+
31
+ DOMPurify removes the code, not the content. The page still fetches and shows the
32
+ links, images, and Mermaid diagrams a body names (Mermaid runs in its `strict`
33
+ mode), and it pulls Cytoscape, marked, and DOMPurify from a CDN. So the rule is no
34
+ longer *only serve bundles you trust* — it is the ordinary care you would give any
35
+ document from a source you do not know.
31
36
 
32
37
  # Citations
33
38
 
34
- [1] [README.md — Server trust boundary](https://github.com/serradura/okf-gem/blob/main/README.md) — the unsanitized-render warning.
35
- [2] [lib/okf/server/templates/graph.html.erb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/templates/graph.html.erb) — `json_for_script` and the body render.
39
+ [1] [README.md — Server trust boundary](https://github.com/serradura/okf-gem/blob/main/README.md) — the two-defense summary.
40
+ [2] [lib/okf/server/templates/graph.html.erb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/templates/graph.html.erb) — `json_for_script` and the `DOMPurify.sanitize(marked.parse(...))` render.
data/.okf/log.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Update Log
2
2
 
3
3
  ## 2026-07-12
4
+ * **Sync**: caught the bundle up with the gem at 1.1.0 — the [graph server](capabilities/graph-server.md) now sanitizes each fetched body with DOMPurify before rendering, so the [server trust boundary](design/server-trust-boundary.md) closes the on-demand render path (its [design listing](design/) reworded to match), and the [library API](capabilities/library-api.md) notes that `require "okf"` loads the library alone now that the CLI and skill load on demand.
4
5
  * **Sync**: caught the bundle up with the CLI at 1.0.0 — documented the new `index` command (the §6 progressive-disclosure map, the read view that sees the reserved `index.md` layer), compact-by-default JSON with `--pretty`, and `--fields`/`--except` projection on the list views, in [read views](capabilities/read-views.md) plus the `index`-verb enumerations in the [CLI](cli.md), the [overview](overview.md), and the [capabilities](capabilities/) index listing.
5
6
 
6
7
  ## 2026-07-11
data/.okf/overview.md CHANGED
@@ -15,8 +15,9 @@ gives you leverage over knowledge that already lives as Markdown.
15
15
 
16
16
  ```mermaid
17
17
  flowchart LR
18
- skill["companion<br/>agent skill"] -. authors .-> bundle[("OKF v0.1 bundle<br/>Markdown + YAML")]
18
+ skill["companion<br/>agent skill"] -. authors/curate .-> bundle[("OKF v0.1 bundle<br/>Markdown + YAML")]
19
19
  bundle --> model["pure model<br/>Concept · Bundle · Graph"]
20
+ skill -. execute .-> cli
20
21
  subgraph cli ["okf CLI"]
21
22
  validate["validate — legal? §9"]
22
23
  lint["lint — well-curated?"]
@@ -29,13 +30,13 @@ flowchart LR
29
30
  Over such a bundle the gem gives you five capabilities behind one
30
31
  [command-line tool](cli.md):
31
32
 
32
- | Capability | What it answers | Verb |
33
- |------------|-----------------|------|
34
- | [Conformance validator](capabilities/validator.md) | Is this a legal OKF bundle? (§9) | `validate` |
35
- | [Curation linter](capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
36
- | [Interactive graph server](capabilities/graph-server.md) | Can I explore it visually? | `server` |
37
- | [Library API](capabilities/library-api.md) | Can my Ruby program use it? | (in-process) |
38
- | [Companion agent skill](capabilities/agent-skill.md) | Can an agent author it? | `skill` |
33
+ | Capability | What it answers | Verb |
34
+ | -------------------------------------------------------- | --------------------------------- | ---------------- |
35
+ | [Companion agent skill](capabilities/agent-skill.md) | Can an agent author it? | `skill` |
36
+ | [Conformance validator](capabilities/validator.md) | Is this a legal OKF bundle? (§9) | `validate` |
37
+ | [Curation linter](capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
38
+ | [Interactive graph server](capabilities/graph-server.md) | Can I explore it visually? | `server` |
39
+ | [Library API](capabilities/library-api.md) | Can my Ruby program use it? | (in-process) |
39
40
 
40
41
  Alongside those, a family of [read views](capabilities/read-views.md) —
41
42
  `index`, `catalog`, `files`, `tags`, `stats`, `graph` — print the bundle at a
@@ -43,7 +44,7 @@ glance so an agent reads it without a browser.
43
44
 
44
45
  # The two ideas it inherits from the format
45
46
 
46
- - **Dual audience.** Every file serves a human skimming it *and* an agent
47
+ - **Dual audience.** Every file serves a human skimming it _and_ an agent
47
48
  extracting from it, so bodies are structural Markdown and
48
49
  [links](format/cross-links.md) are plain Markdown links — both readers already
49
50
  understand them.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0] - 2026-07-12
4
+
5
+ - Claude Code plugin. The repository now doubles as a plugin marketplace:
6
+ `/plugin marketplace add serradura/okf-gem`, then `/plugin install okf@okfgem`.
7
+ The plugin carries the canonical skill (a generated copy; `rake plugin:sync`
8
+ keeps it in lockstep with `lib/okf/skill`, and a test fails on drift), one
9
+ front-door command (`/okf:gem`: no arguments orients on the CLI, the bundle,
10
+ and what `validate`/`lint` report and recommends the highest-value next move
11
+ without running one, `doctor` installs the gem and doctors the repo's bundle,
12
+ `curate` runs the full validate + lint + loose cycle, anything else hands the
13
+ task to the skill), and a PostToolUse hook that runs `okf validate` +
14
+ `okf lint` after every edit inside a bundle and hands the relevant findings
15
+ back as context: every conformance error, plus the warnings and lint findings
16
+ that concern the edited file. The checks are the CLI's own, so the feedback is
17
+ deterministic. The hook stays silent outside bundles, and when the CLI is
18
+ missing it suggests `/okf:gem` once per session instead of erroring on each
19
+ edit. It is config-free to silence: `OKF_CURATE_DISABLED=1` turns it off,
20
+ `OKF_CURATE_QUIET=1` keeps the findings but drops that suggestion, and an
21
+ `<!-- okf-disable -->` comment in a file skips curation for that one. The skill
22
+ routes through per-verb playbooks (`playbooks/`), and its signature guidance
23
+ lines carry stable `<!-- check:… -->` / `<!-- rule:okf-… -->` markers.
24
+ Nothing under `plugin/` ships in the gem.
25
+
26
+ ## [1.1.0] - 2026-07-12
27
+
28
+ - The graph server now sanitizes every concept body before rendering it. The
29
+ page runs marked's HTML output through [DOMPurify](https://github.com/cure53/DOMPurify)
30
+ (loaded from the same CDN as Cytoscape and marked) on the way to the DOM, so a
31
+ bundle carrying active content in a Markdown body can no longer script the
32
+ viewer. Inlined graph data was already escaped through `json_for_script`; this
33
+ closes the other path.
34
+ - `require "okf"` now loads the pure library only. The two argv-facing shells —
35
+ `OKF::CLI` and the `OKF::Skill` installer — load on demand, from `exe/okf` or
36
+ an explicit `require "okf/cli"` / `require "okf/skill"`. `optparse` moves with
37
+ the CLI, so an embedding app (e.g. a Rails store) that only reaches for the
38
+ in-memory model and on-disk handles no longer pulls in the command-line
39
+ machinery. The CLI itself is unchanged.
40
+
3
41
  ## [1.0.0] - 2026-07-12
4
42
 
5
43
  Initial release.
data/README.md CHANGED
@@ -5,17 +5,59 @@
5
5
  </h1>
6
6
  </p>
7
7
 
8
- > A rough project, cut and polished into a jewel. And like any jewel, what it is
9
- > worth comes down to what it does with knowledge: reading it, validating it,
10
- > curating it, and putting it on display.
11
-
12
- **okf-gem** reads, validates, lints, and serves **Open Knowledge Format (OKF)**
13
- v0.1 bundles. OKF is portable knowledge: a directory of Markdown files with YAML
14
- frontmatter that both humans and agents read. Each file is a _concept_; a
15
- directory of them is a _bundle_. Over such a bundle the gem gives you five
16
- things: a library API, a conformance validator, a curation linter, an
17
- interactive graph server, and a companion agent skill. All but the library API
18
- are reachable through one `okf` command-line tool.
8
+ <p align="center">
9
+ <i>A lightweight Ruby gem for OKF: author, curate, and serve bundles as an interactive graph.</i>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://rubygems.org/gems/okf"><img src="https://img.shields.io/gem/v/okf" alt="Gem version"></a>
14
+ <a href="https://rubygems.org/gems/okf"><img src="https://img.shields.io/gem/dt/okf" alt="Downloads"></a>
15
+ <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>
16
+ <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>
17
+ <a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License: Apache-2.0"></a>
18
+ <a href="lib/okf/skill/reference/SPEC.md"><img src="https://img.shields.io/badge/OKF-v0.1-6E56CF" alt="OKF v0.1"></a>
19
+ <a href="#claude-code-plugin"><img src="https://img.shields.io/badge/Claude%20Code-plugin-D97757" alt="Claude Code plugin"></a>
20
+ </p>
21
+
22
+ **okf-gem** — `okf` on RubyGems — reads, validates, lints, and serves
23
+ **Open Knowledge Format (OKF)** v0.1 bundles: directories of Markdown files with YAML frontmatter that humans and agents read from one source. It does not define a new place to keep knowledge; it gives you leverage over knowledge that already lives as Markdown. Each file is a _concept_; a directory of them is a _bundle_.
24
+
25
+ > **Quick start.** One skill, one command, a curation hook, and a CLI that
26
+ > validates, lints, indexes, and serves your Markdown as a graph. In Claude Code,
27
+ > add the plugin and let it set everything up: `/plugin marketplace add
28
+ serradura/okf-gem`, then `/plugin install okf@okfgem`, then `/okf:gem`. On the
29
+ > command line: `gem install okf`, then `okf validate <dir>`.
30
+
31
+ Here is what it is able to do:
32
+
33
+ <p align="center">
34
+ <picture>
35
+ <source media="(prefers-color-scheme: dark)" srcset=".github/overview-dark.png">
36
+ <img src=".github/overview.png" width="100%" alt="The companion agent skill authors and curates an OKF v0.1 bundle (Markdown + YAML). A pure model (Concept, Bundle, Graph) reads that bundle and feeds both the okf CLI (validate — legal per §9; lint — well-curated; server — explore) and a library API you embed in Ruby.">
37
+ </picture>
38
+ </p>
39
+
40
+ Over a bundle the gem gives you the `okf`
41
+ command-line tool (the library API is also usable in-process). Each capability
42
+ below links to the concept that documents it: this gem's own knowledge is an OKF
43
+ bundle, so you can read its design in the format it defends.
44
+
45
+ | Capability | What it answers | Verb |
46
+ | ------------------------------------------------------------- | --------------------------------- | ---------------- |
47
+ | [Companion agent skill](.okf/capabilities/agent-skill.md) | Can an agent author it? | `skill` |
48
+ | [Conformance validator](.okf/capabilities/validator.md) | Is this a legal OKF bundle? (§9) | `validate` |
49
+ | [Curation linter](.okf/capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
50
+ | [Interactive graph server](.okf/capabilities/graph-server.md) | Can I explore it visually? | `server` |
51
+ | [Library API](.okf/capabilities/library-api.md) | Can my Ruby program use it? | in-process |
52
+
53
+ > [!TIP]
54
+ > **Browse the gem as knowledge, not just docs.** This README is the front door;
55
+ > the depth lives in the [`.okf/`](.okf) bundle this repo ships. Start at the
56
+ > [overview](.okf/overview.md), then follow the graph into the
57
+ > [capabilities](.okf/capabilities/) (what it does), the
58
+ > [design constraints](.okf/design/) (why it stays this light), and the
59
+ > [format itself](.okf/format/) (what it operates on). Run `okf server .okf` to
60
+ > walk the same bundle as an interactive graph.
19
61
 
20
62
  It is deliberately light so it runs on the Ruby your OS already ships:
21
63
 
@@ -23,10 +65,87 @@ It is deliberately light so it runs on the Ruby your OS already ships:
23
65
  its core dependency;
24
66
  - only two runtime dependencies: `rack` (the server is a mountable Rack app)
25
67
  and `webrick` (unbundled from Ruby in 3.0);
26
- - no ActiveSupport, no build step, no JavaScript toolchain.
68
+ - no ActiveSupport, no build step, no JavaScript toolchain — the
69
+ [design constraints](.okf/design/) that hold this line are enforced by tests.
70
+
71
+ That range is not aspirational: CI runs the full test suite and RuboCop on every
72
+ one of these on each push.
73
+
74
+ ## Why OKF
75
+
76
+ Project knowledge (why a service exists, what a metric really measures, the
77
+ reasoning a schema encodes) lives scattered across wikis, code comments, and
78
+ whoever happened to be in the room, and an agent re-derives it every session. OKF
79
+ gives it one durable, diffable home, versioned next to the code it describes and
80
+ read from the same file by people and agents alike. [OKF][okf] is an open,
81
+ vendor-neutral format (Google Cloud, 2026); this gem is the Ruby-native way to
82
+ work with it.
83
+
84
+ [okf]: https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
85
+
86
+ Knowledge already has several homes near an agent, and each holds a different
87
+ thing. None of the others is built for curated, durable team knowledge:
88
+
89
+ | | OKF bundle (this) | `CLAUDE.md` / `AGENTS.md` | Agent auto-memory | Wiki / Notion |
90
+ | ------------------------------ | ----------------------------------------------- | -------------------------- | ------------------------ | ---------------- |
91
+ | Holds | curated team knowledge | standing instructions | what one agent picked up | human docs |
92
+ | Versioned with the code | ✅ | ✅ | ❌ | ❌ |
93
+ | Portable across agents | ✅ plain Markdown + YAML | ⚠️ per-harness conventions | ❌ per-agent store | ⚠️ export needed |
94
+ | Typed and queryable | ✅ frontmatter + graph | ❌ prose | ❌ | ⚠️ partially |
95
+ | Reviewed in PRs | ✅ | ✅ | ❌ implicit | ⚠️ rarely |
96
+ | Scales past one context window | ✅ progressive disclosure (`okf index`) | ❌ loaded whole | ⚠️ partially | n/a |
97
+ | Checked by tooling | ✅ (`okf validate` + `lint`), exit codes for CI | ❌ | ❌ | ❌ |
98
+
99
+ The last row is this gem's job. The other homes have no detector, so their
100
+ drift stays invisible; a bundle's drift shows up as findings you can gate on.
101
+
102
+ ## What a bundle looks like
103
+
104
+ A bundle is just a directory; each concept is one Markdown file whose path is its
105
+ id. This repo documents _itself_ in OKF, so the tree below is real:
106
+
107
+ ```
108
+ .okf/
109
+ ├── index.md # progressive-disclosure map (root carries okf_version)
110
+ ├── log.md # ISO-dated change history, newest first
111
+ ├── overview.md
112
+ ├── format/frontmatter.md
113
+ ├── model/graph.md
114
+ └── capabilities/graph-server.md # one concept = one file
115
+ ```
116
+
117
+ The only hard requirement is YAML frontmatter with a non-empty `type`; everything
118
+ else is optional and tolerated when missing. A concept (here the real
119
+ `capabilities/graph-server.md`, body trimmed) reads:
120
+
121
+ ```markdown
122
+ ---
123
+ type: Capability
124
+ title: Interactive graph server (server)
125
+ description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
126
+ resource: lib/okf/server/app.rb
127
+ tags: [server, graph, rack, diagram]
128
+ timestamp: 2026-07-11T12:00:00Z
129
+ ---
130
+
131
+ # Overview
132
+
133
+ `okf server` boots an interactive view of the [graph](../model/graph.md) …
134
+ ```
135
+
136
+ That bundle is this gem's own documentation. Clone the repo and run
137
+ `okf server .okf` to browse it as the graph diagrammed at the top of this file.
27
138
 
28
139
  ## Installation
29
140
 
141
+ > **In Claude Code**, the plugin is the fastest path: two commands install the whole
142
+ > toolchain (skill, `/okf:gem`, and the curation hook). See
143
+ > [Claude Code plugin](#claude-code-plugin). Everywhere else, install the gem:
144
+
145
+ | Ruby version | 2.4 | 2.5 | 2.6 | 2.7 | 3.0 | 3.1 | 3.2 | 3.3 | 3.4 | 4.0 |
146
+ | ---------------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
147
+ | Tested/Supported | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
148
+
30
149
  ```bash
31
150
  gem install okf
32
151
  # or, in a project
@@ -46,9 +165,10 @@ bundle exec rake install
46
165
  okf validate <dir> [--json] # check OKF v0.1 conformance (§9)
47
166
  okf lint <dir> [--json] [--fail-on warn] [...] # report curation-quality issues
48
167
  okf loose <dir> [--json] # list files with no graph links, by folder
168
+ okf index <dir> [--json] [--area A] [--no-body] # progressive-disclosure map (§6): bodies, rollups, listings
49
169
  okf server <dir> [-p PORT] [--bind ADDR] [...] # serve the interactive graph over HTTP
50
170
  okf graph <dir> [--json] [--minimal] [--no-body] # print the knowledge graph
51
- okf catalog | files | tags | stats <dir> [--json] # the browser views, on the CLI
171
+ okf catalog | files | tags | types | stats <dir> [--json] # the browser views, on the CLI
52
172
  okf skill <dest> [--here] [--force] # install the companion agent skill
53
173
  okf --version
54
174
  ```
@@ -56,10 +176,6 @@ okf --version
56
176
  Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
57
177
  threshold crossed), `2` usage error.
58
178
 
59
- > [!NOTE]
60
- > Section numbers like §5, §8, and §9 refer to the OKF v0.1 spec, bundled with
61
- > the skill at [`lib/okf/skill/reference/SPEC.md`](lib/okf/skill/reference/SPEC.md).
62
-
63
179
  ```bash
64
180
  $ okf validate docs
65
181
  OKF v0.1 conformance — docs
@@ -82,11 +198,13 @@ _The graph server on this repo's own [`.okf`](.okf) bundle, with the
82
198
 
83
199
  `graph` and `server` are best-effort (§9): a file with invalid frontmatter is
84
200
  skipped (and noted on stderr), not fatal, so one bad file never breaks the rest.
85
- Before serving a bundle you did not author, read the
86
- [server trust boundary](#server-trust-boundary).
201
+ The [graph server](.okf/capabilities/graph-server.md) concept walks the request
202
+ flow and endpoints; the [server trust boundary](#server-trust-boundary) below
203
+ explains how the page handles a bundle you did not author.
87
204
 
88
- `lint` reports curation quality (reachability, backlog, completeness, freshness,
89
- provenance, and hygiene) separately from `validate`. It is advisory: it exits
205
+ `lint` (the [curation linter](.okf/capabilities/linter.md)) reports curation
206
+ quality (reachability, backlog, completeness, freshness, provenance, and hygiene)
207
+ separately from `validate`. It is advisory: it exits
90
208
  `0` even with findings unless you opt into gating with `--fail-on warn`.
91
209
 
92
210
  ```bash
@@ -116,11 +234,27 @@ always exits `0`.
116
234
 
117
235
  ## Agent skill
118
236
 
119
- The gem carries the companion OKF agent skill: a `SKILL.md` plus reference
237
+ The gem carries the [companion OKF agent skill](.okf/capabilities/agent-skill.md):
238
+ a `SKILL.md` plus reference
120
239
  and template files that teach a coding agent to author, maintain, and consume OKF
121
240
  bundles and to drive the commands above. Because the skill ships inside the gem,
122
241
  installing the gem already puts the skill on your machine, and the skill's
123
242
  CLI reference can never drift from the executable it was released with.
243
+ Using Claude Code? The [plugin](#claude-code-plugin) below installs this same
244
+ skill plus a post-edit curation hook.
245
+
246
+ The skill routes a small set of verbs. In Claude Code they run as `/okf:gem
247
+ <verb>`; used standalone, the skill infers the verb from your request.
248
+
249
+ | Verb | What it does |
250
+ | ---------------- | ------------------------------------------------------------------------------------------------- |
251
+ | _(none)_ | Orient on the bundle and recommend the highest-value next move |
252
+ | `produce` | Create or extend a bundle from code, docs, or knowledge in people's heads |
253
+ | `maintain` | Sync the bundle's content with reality after the code or docs change |
254
+ | `consume` | Use the bundle as context for a task, writing back what you learn |
255
+ | `curate` | Structural upkeep as it stands: `validate` + `lint` + `loose` |
256
+ | `doctor` | Install and verify the CLI, then doctor the bundle |
257
+ | `<okf-cli-verb>` | Run any CLI verb (`validate`, `lint`, `index`, `server`, the read views) and interpret its output |
124
258
 
125
259
  Point it at your agent's config directory (or its skills directory) and the tree
126
260
  settles in its own `skills/okf/` folder, so a shared skills directory never gets
@@ -139,7 +273,8 @@ is never clobbered.
139
273
 
140
274
  ## Library
141
275
 
142
- The gem is two layers: pure in-memory data (`OKF::Concept`, `OKF::Bundle`)
276
+ The gem (the [library API](.okf/capabilities/library-api.md)) is two layers: pure
277
+ in-memory data (`OKF::Concept`, `OKF::Bundle`)
143
278
  you build, interrogate, and analyze with no disk involved, and on-disk
144
279
  handles (`OKF::Concept::File`, `OKF::Bundle::Folder`) that add
145
280
  load/save/reload/delete, an "ActiveRecord for the filesystem".
@@ -199,7 +334,8 @@ The lower-level pieces are usable on their own too: `OKF::Bundle::Validator.call
199
334
 
200
335
  ### Conformance model
201
336
 
202
- `validate` implements the spec's [§9 conformance definition](lib/okf/skill/reference/SPEC.md#9-conformance)
337
+ `validate` (the [conformance validator](.okf/capabilities/validator.md)) implements
338
+ the spec's [§9 conformance definition](lib/okf/skill/reference/SPEC.md#9-conformance)
203
339
  exactly. There are three hard conditions, all errors:
204
340
 
205
341
  - **§9.1** every non-reserved file has a parseable YAML frontmatter block;
@@ -236,12 +372,51 @@ is the structured input an agent consumes to reason about those.
236
372
 
237
373
  ## Server trust boundary
238
374
 
239
- > [!WARNING]
240
- > The served page loads its JavaScript (Cytoscape, marked, mermaid, and layout
241
- > plugins) from a CDN and renders each concept's Markdown body **without
242
- > sanitization**, so only serve bundles you trust. Inlined graph data still
243
- > cannot break out of its `<script>` (`<` is escaped), but the fetched Markdown
244
- > is rendered unsanitized.
375
+ > [!NOTE]
376
+ > The graph page defends against a bundle that carries active content in two
377
+ > places. It escapes any graph data inlined into the page (`<` becomes `&lt;`),
378
+ > which keeps that data from breaking out of its `<script>`. And it fetches each
379
+ > concept body on demand, then runs marked's HTML output through
380
+ > [DOMPurify](https://github.com/cure53/DOMPurify) before it reaches the DOM, so
381
+ > a script or event handler hidden in a Markdown body is stripped rather than
382
+ > executed. Descriptions are escaped on the server.
383
+ >
384
+ > That covers the paths that could run code. The page still loads Cytoscape,
385
+ > marked, and DOMPurify from a CDN and renders whatever links and diagrams a body
386
+ > contains, so treat an unfamiliar bundle the way you would treat any document
387
+ > from a source you do not know.
388
+
389
+ The [server trust boundary](.okf/design/server-trust-boundary.md) concept has the
390
+ full write-up: the two data paths, what sanitizing does, and what it leaves to
391
+ your judgment.
392
+
393
+ ## Claude Code plugin
394
+
395
+ This repository doubles as a Claude Code plugin marketplace, so the whole
396
+ toolchain installs with two commands inside Claude Code:
397
+
398
+ ```
399
+ /plugin marketplace add serradura/okf-gem
400
+ /plugin install okf@okfgem
401
+ ```
402
+
403
+ The plugin carries three pieces:
404
+
405
+ | Piece | What it does |
406
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
407
+ | `okf` skill | The [companion skill](#agent-skill) above, bundled with the plugin (a generated copy that `rake plugin:sync` keeps identical to `lib/okf/skill`). |
408
+ | `/okf:gem` | The front door, routed by argument. No arguments: orients on the CLI, the bundle, and what `validate`/`lint` report, then recommends the highest-value next move (never auto-runs). `doctor`: installs and verifies the `okf` CLI, then doctors the repo's bundle. `curate`: the full curation cycle (`validate` + `lint` + `loose`). Anything else (`produce`, `maintain`, `consume`, a CLI verb): handed to the skill. |
409
+ | Curation hook | After every Write or Edit inside a bundle, runs `okf validate` + `okf lint` and returns the findings as context. The checks are the CLI's own, so the feedback is deterministic. |
410
+
411
+ The hook stays silent outside bundles, and when the CLI is missing it suggests
412
+ `/okf:gem` once per session instead of failing on each edit. It is config-free to
413
+ switch off: set `OKF_CURATE_DISABLED=1` to turn it off, `OKF_CURATE_QUIET=1` to
414
+ keep the findings but drop that suggestion, or drop an `<!-- okf-disable -->`
415
+ comment into a file to skip curation for that one.
416
+
417
+ Prefer no plugin? `gem install okf && okf skill .claude` installs the skill
418
+ alone, and the skill itself instructs the agent to run the same checks after
419
+ editing a bundle.
245
420
 
246
421
  ## Development
247
422
 
@@ -274,3 +449,9 @@ The gem is available as open source under the terms of the
274
449
  is authored by Google Cloud Platform and included under its own Apache-2.0
275
450
  license, Copyright (c) Google LLC. See `NOTICE` and
276
451
  `lib/okf/skill/reference/APACHE-2.0.txt`.
452
+
453
+ [okf-skills](https://github.com/scaccogatto/okf-skills) by Marco Boffo, a Python
454
+ OKF toolkit for Claude Code with a feature-rich interactive graph view, was an
455
+ early inspiration for this gem's Claude Code plugin and for the knowledge-as-code
456
+ comparison in [Why OKF](#why-okf). okf-gem takes a different shape: a Ruby-native
457
+ gem built around the `okf` CLI and an embeddable library.
data/exe/okf CHANGED
@@ -2,5 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "okf"
5
+ require "okf/skill"
6
+ require "okf/cli"
5
7
 
6
8
  exit OKF::CLI.start(ARGV)
data/lib/okf/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "optparse"
4
+
3
5
  module OKF
4
6
  # Command-line front end: `okf graph|validate|lint|loose|index|catalog|files|tags|types|stats|server <dir>`.
5
7
  # This is the
@@ -11,6 +11,7 @@
11
11
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
12
12
  <script src="https://cdn.jsdelivr.net/npm/cytoscape@3.30.2/dist/cytoscape.min.js"></script>
13
13
  <script src="https://cdn.jsdelivr.net/npm/marked@14/marked.min.js"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
14
15
  <style>
15
16
  :root{
16
17
  --bg:#ffffff; --panel:#ffffff; --panel-2:#f6f7f8; --canvas:#f4f5f7; --ink:#1f2328; --ink-soft:#333333;
@@ -466,7 +467,7 @@ function rethemeMermaid(){if(!window.mermaid)return;const nodes=[...document.que
466
467
  window.mermaid.initialize({startOnLoad:false,securityLevel:'strict',theme:isDark()?'dark':'default'});
467
468
  nodes.forEach(d=>{if(d.dataset.src){d.removeAttribute('data-processed');d.innerHTML='';d.textContent=d.dataset.src;}});
468
469
  window.mermaid.run({nodes}).catch(()=>{});}
469
- function renderMarkdown(el,text){el.innerHTML=text?marked.parse(text):'<span class="empty">empty body</span>';processMermaid(el);}
470
+ function renderMarkdown(el,text){el.innerHTML=text?DOMPurify.sanitize(marked.parse(text)):'<span class="empty">empty body</span>';processMermaid(el);}
470
471
 
471
472
  /* ── inspector ── */
472
473
  const gbody=document.querySelector('.graph-body'), sideBody=document.getElementById('side-body');
@@ -88,7 +88,7 @@ flags. The division of labour is the whole game:
88
88
 
89
89
  The one trap worth carrying in your head: **freshness is off by default** — a plain
90
90
  `okf lint` never reports stale concepts; pass `--stale-after <90d|12w|ISO-date>`
91
- when the bundle carries timestamps.
91
+ when the bundle carries timestamps. <!-- check:stale -->
92
92
 
93
93
  Read [cli.md](reference/cli.md) before *interpreting* a verb's output in depth:
94
94
  what `validate` may and may not reject, lint's categories and check ids, the JSON
@@ -101,17 +101,19 @@ index <dir>` (the §6 map: every directory's index body, rollups, and listings)
101
101
  read `log.md` (the §7 baseline of what changed last) **before** greping or opening
102
102
  leaves. It is the cheapest high-signal context, and the only reliable way to catch
103
103
  enumeration drift: **grep cannot find an index entry that is missing** — you can't
104
- search for the word that should be there but isn't. Per-verb steps are in
105
- [authoring.md](reference/authoring.md) (no `okf` installed? read the root
104
+ search for the word that should be there but isn't. <!-- rule:okf-orient-index -->
105
+ Per-verb steps are in the
106
+ playbooks (the Commands table below; no `okf` installed? read the root
106
107
  `index.md` plus each area's `index.md`).
107
108
 
108
109
  ## The authoring verbs — the craft
109
110
 
110
111
  `produce` (create or extend a bundle), `maintain` (sync it with reality),
111
112
  `consume` (use it as context) carry the judgment the executable can't — this is
112
- where the skill earns its keep. Read [authoring.md](reference/authoring.md)
113
- before doing them, and the verbatim spec [SPEC.md](reference/SPEC.md) when you
114
- need chapter and verse.
113
+ where the skill earns its keep. Each has a playbook (the Commands table below);
114
+ read the modelling craft in [authoring.md](reference/authoring.md) before
115
+ producing or maintaining, and the verbatim spec [SPEC.md](reference/SPEC.md)
116
+ when you need chapter and verse.
115
117
 
116
118
  **No subcommand?** Infer intent: "document this / capture X" → `produce`; "the
117
119
  code changed, update the docs" → `maintain`; a repo already carrying a bundle
@@ -123,11 +125,35 @@ ask.
123
125
  root, but first detect whether the project already keeps its bundle elsewhere
124
126
  (e.g. `docs/`) and prefer that. Commit the bundle alongside the code it describes.
125
127
 
128
+ ## Commands
129
+
130
+ The first word of the arguments picks a row. **No arguments at all** — someone
131
+ asking "what should I do?" — is its own row: read `playbooks/menu.md`, orient on
132
+ the signals, and recommend the highest-value move without running one. When there
133
+ is wording but no matching first word, infer intent as in "No subcommand?" above.
134
+ Read the referenced playbook before executing — it *is* the procedure.
135
+
136
+ | Verb | Category | What it does | Reference |
137
+ |------|----------|--------------|-----------|
138
+ | *(none)* | Orient | recommend the highest-value next move; never auto-run | [playbooks/menu.md](playbooks/menu.md) |
139
+ | `produce` | Author | create or extend a bundle | [playbooks/produce.md](playbooks/produce.md) |
140
+ | `maintain` | Author | sync the bundle's content with reality after a change | [playbooks/maintain.md](playbooks/maintain.md) |
141
+ | `consume` | Use | use the bundle as context for a task | [playbooks/consume.md](playbooks/consume.md) |
142
+ | `curate` | Curate | structural upkeep as it stands: validate + lint + loose | [playbooks/curate.md](playbooks/curate.md) |
143
+ | `doctor` | Setup | install and verify the CLI, then doctor the bundle | [playbooks/doctor.md](playbooks/doctor.md) |
144
+ | `<okf-cli-verb>` | Read | validate, lint, loose, index, catalog, files, tags, types, stats, graph, server, skill | `okf <verb> --help` + [reference/cli.md](reference/cli.md) |
145
+
146
+ Two boundaries worth keeping sharp: `curate` is structural upkeep only — when
147
+ the *content* no longer matches reality, that is `maintain` — and `doctor` is
148
+ the one playbook that does not assume the CLI is installed. In Claude Code with
149
+ the okf plugin, `/okf:gem` routes these same verbs.
150
+
126
151
  ## The lifecycle is a flywheel, not phases
127
152
 
128
153
  produce seeds a bundle; consume reads it; **maintain** runs whenever reality drifts
129
154
  *or* whenever consuming teaches you something durable — that write-back reflex is
130
155
  what keeps a bundle alive instead of rotting into folklore. When you learn
131
- something while consuming, switch to maintain and record it. Full playbooks and the
132
- modelling craft (granularity, choosing `type`, tag vocabulary, topology,
133
- `resource`, links, citations) are in [reference/authoring.md](reference/authoring.md).
156
+ something while consuming, switch to maintain and record it. The playbooks live
157
+ one per verb in `playbooks/` (the Commands table above); the modelling craft
158
+ (granularity, choosing `type`, tag vocabulary, topology, `resource`, links,
159
+ citations) is in [reference/authoring.md](reference/authoring.md).
@@ -0,0 +1,12 @@
1
+ # Playbook: consume — use a bundle as context
2
+
3
+ 1. **Orient first** (the [SKILL.md](../SKILL.md) reflex): `okf index <dir>` maps the
4
+ whole bundle in one pass — every directory's index body, rollups, and listings —
5
+ and `log.md` gives recent history. Then follow links only into the concepts the
6
+ task needs. For a large bundle, `okf graph --json` gives the whole link structure
7
+ at once so you can plan a traversal without opening every file.
8
+ 2. Treat broken links as not-yet-written knowledge, not errors.
9
+ 3. **Write-back reflex:** if you learn something durable while working — a fact the
10
+ bundle lacks, a link it is missing, a concept that no longer matches reality —
11
+ switch to [maintain](maintain.md) and record it. That reflex is what keeps the
12
+ bundle alive.
@@ -0,0 +1,33 @@
1
+ # Playbook: curate an OKF bundle
2
+
3
+ Run the complete curation cycle over the whole bundle and act on the findings
4
+ with the okf skill's judgment. If the `okf` CLI is missing, stop and follow
5
+ [doctor.md](doctor.md) first (in Claude Code: `/okf:gem doctor`).
6
+
7
+ Curation is structural upkeep of the bundle *as it stands*: conformance,
8
+ reachability, backlog, completeness, hygiene. It is not `maintain`, the
9
+ skill's workflow for when the project changed and the bundle's *content*
10
+ must catch up with reality; reach for that one when what is written stopped
11
+ being true. Curating can surface semantic staleness, and when it does,
12
+ switch to `maintain` for those concepts.
13
+
14
+ 1. Locate the bundle: the directory you were given, if any; otherwise a
15
+ `.okf/` directory or a root `index.md` whose frontmatter carries
16
+ `okf_version`.
17
+ 2. Measure: `okf validate <root> --json`, `okf lint <root> --json`,
18
+ `okf loose <root> --json`.
19
+ 3. Interpret through the three lenses the okf skill teaches, and keep them
20
+ separate:
21
+ - conformance errors (§9) are the only hard failures; fix them first,
22
+ always;
23
+ - lint findings are curation debt across reachability, backlog,
24
+ completeness, freshness, provenance, and hygiene. They are advisory;
25
+ rank them by how much each hurts a reader navigating the graph;
26
+ - loose files can be legitimate terminal leaves, so judge each one before
27
+ linking it anywhere.
28
+ 4. Propose, then apply: list the fixes worth making (must-fix errors first,
29
+ then the debt worth settling, then the judgment calls), apply the ones the
30
+ user confirms, or all the obvious ones when the user asked you to just
31
+ clean up.
32
+ 5. Re-measure: run validate + lint again and report the before and after in
33
+ two lines.
@@ -0,0 +1,55 @@
1
+ # Playbook: set up the okf CLI and doctor the bundle
2
+
3
+ You are the installer and doctor for the Open Knowledge Format toolchain. Work
4
+ through the steps in order and report what you find at each one. Ask before any
5
+ install that touches the user's system; everything else, just do.
6
+
7
+ ## 1. Is the CLI already here?
8
+
9
+ Run `okf --version`. If it prints a version, skip to step 3.
10
+
11
+ ## 2. Install the gem
12
+
13
+ Find a Ruby first. The gem runs on any Ruby >= 2.4, so whatever the OS or a
14
+ version manager already ships will do:
15
+
16
+ - `ruby --version`; if absent, check the managers: `rbenv versions`,
17
+ `asdf list ruby`, `mise ls ruby`.
18
+ - Repo has a Gemfile and okf belongs to the project? `bundle add okf`, then use
19
+ `bundle exec okf` everywhere below.
20
+ - Otherwise: `gem install okf`.
21
+
22
+ Troubleshooting, in order of likelihood:
23
+
24
+ - `okf: command not found` right after a successful install: the gem bindir is
25
+ not on PATH. `ruby -e 'puts Gem.bindir'` shows where the executable landed;
26
+ add that to PATH, or refresh the manager's shims (`rbenv rehash`,
27
+ `asdf reshim ruby`).
28
+ - Permission error on the system Ruby: do not reach for sudo. Use
29
+ `gem install --user-install okf` and add `$(ruby -e 'puts Gem.user_dir')/bin`
30
+ to PATH.
31
+ - Windows: a RubyInstaller Ruby works. Run the install from the same shell that
32
+ has `ruby` on PATH, and note the executable is `okf.bat` under Gem.bindir.
33
+
34
+ Verify with `okf --version` before moving on.
35
+
36
+ ## 3. Doctor the bundle
37
+
38
+ 1. Locate a bundle: the directory you were given, if any; otherwise a `.okf/`
39
+ directory, or a root `index.md` whose frontmatter carries `okf_version`.
40
+ 2. Found one? Run `okf validate <root>` and `okf lint <root>`, then summarize
41
+ in a few lines: conformant or not (and the errors if not), the warning
42
+ count, and the top curation findings by category.
43
+ 3. No bundle? Offer to bootstrap one. The okf skill knows how (its "produce"
44
+ workflow); do not scaffold anything without the user's yes.
45
+
46
+ ## 4. Say what changes now
47
+
48
+ Close with a short orientation. In Claude Code with the okf plugin active:
49
+ every Write or Edit that touches an OKF bundle runs `okf validate` and
50
+ `okf lint` automatically, and the findings come back as context to act on.
51
+ The checks are the CLI's own, so the feedback is deterministic. `/okf:gem
52
+ curate` runs the same cycle over the whole bundle on demand, and `/okf:gem`
53
+ with any other arguments (produce, maintain, consume, or a CLI verb) hands
54
+ the task straight to the skill. Without the plugin, the skill itself
55
+ instructs the agent to run the same checks after editing a bundle.
@@ -0,0 +1,67 @@
1
+ # Playbook: maintain — keep a bundle in sync with reality
2
+
3
+ Reach for this when the project changed and the bundle's *content* must catch
4
+ up. The modelling craft behind steps 3 and 7 lives in
5
+ [authoring.md](../reference/authoring.md).
6
+
7
+ 1. **Orient before hunting.** Run `okf index <dir>` (the §6 map — every directory's
8
+ index body, rollups, and listings), read `log.md` (the §7 baseline: what changed
9
+ last), and `okf stats <dir>` (size and shape) *before* you grep. It is the
10
+ cheapest context and it primes the hunt — and it is the only reliable way to
11
+ catch enumeration drift, because **grep cannot find an index entry that is
12
+ missing.** (This is the always-on reflex in [SKILL.md](../SKILL.md).)
13
+ 2. **Find *every* affected concept** — the failure mode is fixing only the obvious
14
+ one. Don't rely on reading the whole bundle; that only scales on tiny ones. Grep
15
+ the changed asset's `resource` URI across the bundle, grep its path/name, and use
16
+ `okf graph --json` to pull the concepts that link *to* the ones you're touching.
17
+ Let grep and the graph find them so nothing drifts silently.
18
+ 3. Update bodies and `timestamp`; fix or add cross-links; create new concepts for
19
+ new assets; mark retired assets with a `**Deprecation**` note rather than
20
+ silently deleting the context that explains them.
21
+ 4. **Update every enumeration that names what you changed — including `index.md`
22
+ bodies**, not just the concept files: a new, renamed, or removed concept changes
23
+ its directory's index listing too. Append a dated `log.md` entry. Step 1's map
24
+ is how you verify this — re-run `okf index` and confirm each listing matches
25
+ reality.
26
+ 5. Run `validate`, then `lint` to catch the curation drift the change introduced —
27
+ new orphans, broken citations, dangling index entries. Add `--stale-after`
28
+ (e.g. `90d`) if concepts carry timestamps: freshness is off by default, so a
29
+ plain `lint` will not tell you what the change left stale.
30
+ 6. **Review loose files** <!-- check:unlinked --> — run `okf loose <dir>` (the
31
+ folder-grouped view of `lint`'s `unlinked` check): the concepts with **no
32
+ cross-links in or out**, which
33
+ float in the graph. This is a semantic pass the tool cannot do for you — for each
34
+ floater, judge intent:
35
+ - **should it link out?** the concept relates to others but says so nowhere —
36
+ write the sentence that explains the relationship and put the link in it;
37
+ - **should something link to it?** it is knowledge others should reach by
38
+ following links, not just via an index — add the inbound link from where it
39
+ belongs;
40
+ - **legitimately terminal?** a backlog item, a spec reference, a leaf reachable
41
+ by design only through its index — leave it. **Terminal-by-design is not a
42
+ defect.** Loose ≠ orphan: an index listing makes a file *reachable* (not an
43
+ orphan) but is not a graph edge, so an indexed file can still float here.
44
+ 7. **Curate the tag vocabulary** <!-- rule:okf-tag-vocabulary --> when the pass
45
+ touched tags, or when `okf tags <dir>` shows a long tail of singletons. Run `okf tags <dir> --by area` and
46
+ `--by type` — the grouped view is the analysis; read each group top-down:
47
+ - **twins** — two tags riding the exact same concepts (equal counts sort them
48
+ adjacent). Merge into one unless each genuinely names a different theme.
49
+ - **group-name echoes** — a tag matching its own group's name (a `format` tag
50
+ inside `format/`, an `overview` tag on an Overview). It restates an axis the
51
+ concept already carries; drop it from those concepts.
52
+ - **singletons** — for each, ask: would an existing tag serve? is it an
53
+ anticipated cluster that concepts landing soon will join? is it a deliberate
54
+ marker (`security`, `deprecated`)? Merge, keep, or drop accordingly — a
55
+ count of 1 is a question, never a verdict.
56
+ - **connective tags** — recurring across groups: these are the vocabulary's
57
+ spine. Protect them; prefer merging others *into* them over renaming them,
58
+ because consumers learn these keys and stability is part of their value.
59
+ The trap in this pass is optimizing the numbers instead of the vocabulary:
60
+ you can reach zero singletons by deleting every tag, and perfect cohesion by
61
+ tagging everything alike. The goal is a small set of tags where each one
62
+ either connects or marks — judged, not counted.
63
+
64
+ Before calling the pass done, walk the
65
+ [Closeout gate](../reference/authoring.md#closeout--the-finishing-gate) once:
66
+ steps 4–7 above cover most of it, and the gate is the check that nothing was
67
+ skipped.
@@ -0,0 +1,34 @@
1
+ # Playbook: menu — "what should I do?" the no-argument front door
2
+
3
+ Reached when `/okf:gem` (or the skill) runs with no verb and no task. Do not
4
+ guess a workflow and do not run one on your own: **orient on the signals, then
5
+ recommend the two or three highest-value moves and let the user pick.** The full
6
+ Commands table in [SKILL.md](../SKILL.md) is the fallback menu; the recommendation
7
+ is the lede.
8
+
9
+ 1. **CLI present?** `okf --version`. If it is missing, the only useful move is
10
+ setup: follow [doctor.md](doctor.md) (install and verify the CLI) and stop
11
+ here. Everything below needs the CLI.
12
+ 2. **Bundle present?** Locate one: the directory you were given, else a `.okf/`
13
+ directory or a root `index.md` whose frontmatter carries `okf_version`.
14
+ - **No bundle** → lead with **`produce`** (create the first bundle from the
15
+ code, docs, or what lives only in people's heads). Nothing else applies yet.
16
+ 3. **Read the bundle's state** from the CLI, not by eyeballing:
17
+ `okf validate <root> --json`, `okf lint <root> --json`, `okf loose <root>
18
+ --json`. Then recommend by what they report, most-blocking first:
19
+ - **`validate` has errors** → lead with **`curate`**: §9 conformance errors are
20
+ the only hard failures, and curate fixes them before anything else.
21
+ - **clean `validate`, but `lint`/`loose` findings** → lead with **`curate`** to
22
+ settle the curation debt (reachability, backlog, completeness, hygiene),
23
+ naming the top one or two categories from the report.
24
+ - **clean across the board** → the bundle is healthy, so lead with **`consume`**
25
+ (put it to work on the task at hand) and offer **`maintain`** as the move for
26
+ when the code or docs have since changed. If the working tree has uncommitted
27
+ changes to the code the bundle describes (`git status`), prefer **`maintain`**:
28
+ that is exactly the drift it exists to close.
29
+ 4. **Freshness is off by default.** If the bundle carries timestamps, note that a
30
+ plain `lint` said nothing about staleness and `okf lint <root> --stale-after
31
+ 90d` is the check that would.
32
+
33
+ Keep it to two or three pointed picks, each with the exact `/okf:gem <verb>` to
34
+ run and a one-line reason from the signals. Never auto-run a workflow from here.
@@ -0,0 +1,23 @@
1
+ # Playbook: produce — create or extend a bundle
2
+
3
+ The craft that makes these steps land well — granularity, choosing `type`, tag
4
+ vocabulary, topology, links, citations — lives in
5
+ [authoring.md](../reference/authoring.md). Read it before a non-trivial produce.
6
+
7
+ 1. Read [SPEC.md](../reference/SPEC.md) if you are unsure of any rule.
8
+ 2. Pick the source(s): **code** (derive concepts from source, READMEs, docstrings,
9
+ config), **docs/wiki** (distill pages into concepts; cite the originals under
10
+ `# Citations`), **manual** (decisions, playbooks, metrics that live only in
11
+ people's heads).
12
+ 3. Choose a domain-based directory layout. One concept per file.
13
+ 4. Write each concept from [templates/concept.md](../templates/concept.md): a
14
+ descriptive `type` from the bundle's vocabulary, recommended fields filled,
15
+ cross-links to related concepts written into prose.
16
+ 5. Add or refresh `index.md` per directory from
17
+ [templates/index.md](../templates/index.md); for the bundle root use
18
+ [templates/root-index.md](../templates/root-index.md) so it carries
19
+ `okf_version: "0.1"`. Append a dated entry to `log.md`.
20
+ 6. **Close out** — walk the
21
+ [Closeout gate](../reference/authoring.md#closeout--the-finishing-gate)
22
+ (`validate` + `lint` are part of it, see [cli.md](../reference/cli.md))
23
+ before finishing.
@@ -27,7 +27,7 @@ Consult the right section on demand instead of re-reading all of [SPEC.md](SPEC.
27
27
  These are the decisions that make or break a bundle. None are enforced by the
28
28
  tools — they are yours to get right.
29
29
 
30
- ### One concept = one file — but what is a concept?
30
+ ### One concept = one file — but what is a concept? <!-- rule:okf-atomic-concept -->
31
31
  A concept is the smallest unit of knowledge someone would want to **link to or
32
32
  cite on its own**. If two things are always referenced together, they are one
33
33
  concept; if either is referenced alone, split them. Err atomic — it is cheap to
@@ -36,7 +36,7 @@ file should split: two `type`s fighting for the frontmatter, two audiences, or a
36
36
  heading that others would plausibly link to directly. The file path (minus `.md`)
37
37
  is the concept's stable ID, so name it for what it *is*, not where it sits today.
38
38
 
39
- ### `type` is the graph's vocabulary
39
+ ### `type` is the graph's vocabulary <!-- rule:okf-type-vocabulary -->
40
40
  `type` is the only required field, and it is the dimension every consumer groups
41
41
  and colours by (the graph server colours nodes by it; graph analysis clusters by
42
42
  it). It is freeform — the spec does not enumerate types — and that freedom is a
@@ -46,7 +46,7 @@ types across files is what makes the graph legible; inventing a new type per fil
46
46
  makes `type` meaningless. Before adding a new type, check what the bundle already
47
47
  uses.
48
48
 
49
- ### Tags are the connective axis — curate them like a vocabulary
49
+ ### Tags are the connective axis — curate them like a vocabulary <!-- rule:okf-tag-vocabulary -->
50
50
  `type` says what a concept *is*; the directory says where it *lives*; `tags` are
51
51
  the only axis that cuts across both. A tag earns its place one of two ways: by
52
52
  **connecting** concepts that type and area don't already group (a `billing` tag
@@ -57,14 +57,14 @@ a tag's clothes. Reuse before minting: run `okf tags <dir>` and pick from the
57
57
  existing vocabulary first; 2–4 tags per concept is plenty. Scattered singletons
58
58
  are how a vocabulary rots into one label per file.
59
59
 
60
- ### Topology: organize by domain, not by type
60
+ ### Topology: organize by domain, not by type <!-- rule:okf-domain-topology -->
61
61
  Lay out directories by what the knowledge is *about* (`services/`, `datasets/`,
62
62
  `decisions/`), not by concept type. The directory tree is itself knowledge — it
63
63
  shows a reader how the system decomposes, and it usually mirrors the shape of the
64
64
  codebase or the org. A `types/`-first layout scatters related concepts and buries
65
65
  the domain.
66
66
 
67
- ### `resource` is the bridge to reality
67
+ ### `resource` is the bridge to reality <!-- rule:okf-resource-bridge -->
68
68
  Set `resource` (a canonical URI) **only** when a concept *is* a real, addressable
69
69
  asset — a table (`bigquery://…`), a service repo, a dashboard, an endpoint. Its
70
70
  presence is what lets `maintain` find every concept affected by a changed asset by
@@ -72,7 +72,7 @@ grepping for that URI. Abstract concepts — a decision, a principle, a metric
72
72
  definition — have no resource, and **omitting it is meaningful**, not laziness. Do
73
73
  not invent placeholder URIs.
74
74
 
75
- ### Links are untyped on purpose
75
+ ### Links are untyped on purpose <!-- rule:okf-untyped-links -->
76
76
  A markdown link asserts only "these two relate." The *kind* of relationship —
77
77
  depends-on, supersedes, derived-from, owns — lives in the **prose around the
78
78
  link**, never in a made-up typed-edge syntax. Write the sentence that explains the
@@ -81,14 +81,14 @@ relationship and put the link inside it. Prefer absolute bundle-relative targets
81
81
  does not exist yet is fine — it is not-yet-written knowledge (§5.3), and `lint`'s
82
82
  backlog will surface it as demand.
83
83
 
84
- ### Provenance is what makes knowledge trustworthy (§8)
84
+ ### Provenance is what makes knowledge trustworthy (§8) <!-- check:uncited_external -->
85
85
  Any external or empirical claim — a latency number, an approval, a quota, a
86
86
  "because X team decided Y" — should carry a citation to its source under a
87
87
  `# Citations` heading. Uncited claims are exactly how a bundle decays into folklore
88
88
  nobody trusts. `lint`'s provenance category exists to catch missing and broken
89
89
  citations; write them as you go so you never have to reconstruct them.
90
90
 
91
- ### Capture the non-obvious — not what code already says
91
+ ### Capture the non-obvious — not what code already says <!-- rule:okf-non-obvious -->
92
92
  A bundle that restates function signatures or config keys goes stale the moment
93
93
  the code changes and adds no knowledge. Capture what you **cannot** derive by
94
94
  reading one source file: the *why* behind a design, cross-cutting relationships,
@@ -96,14 +96,14 @@ decisions and their tradeoffs, operational tribal knowledge, the metric that
96
96
  actually matters. If the code or git history already records it faithfully, link
97
97
  to it rather than duplicating it.
98
98
 
99
- ### Write for both readers at once
99
+ ### Write for both readers at once <!-- rule:okf-dual-audience -->
100
100
  Use structural markdown so an agent can extract deterministically and a human can
101
101
  skim: headings, tables, fenced code, lists. Conventional headings a reader expects
102
102
  are `# Schema` (field/column tables), `# Examples`, and `# Citations`. Fill
103
103
  recommended frontmatter — `title`, `description`, `tags`, `timestamp` (ISO 8601) —
104
104
  whenever it aids consumption.
105
105
 
106
- ### Reserved files
106
+ ### Reserved files <!-- rule:okf-reserved-files -->
107
107
  `index.md` is a directory listing and carries **no frontmatter** — with one
108
108
  exception: the **bundle-root** `index.md` is the only index that may carry
109
109
  frontmatter, and it may carry *only* `okf_version: "0.1"` (§11; `validate` §9.3
@@ -114,90 +114,9 @@ bundle-root [root-index](../templates/root-index.md), [log](../templates/log.md)
114
114
 
115
115
  ## Playbooks
116
116
 
117
- ### produce create or extend a bundle
118
- 1. Read [SPEC.md](SPEC.md) if you are unsure of any rule.
119
- 2. Pick the source(s): **code** (derive concepts from source, READMEs, docstrings,
120
- config), **docs/wiki** (distill pages into concepts; cite the originals under
121
- `# Citations`), **manual** (decisions, playbooks, metrics that live only in
122
- people's heads).
123
- 3. Choose a domain-based directory layout. One concept per file.
124
- 4. Write each concept from [templates/concept.md](../templates/concept.md): a
125
- descriptive `type` from the bundle's vocabulary, recommended fields filled,
126
- cross-links to related concepts written into prose.
127
- 5. Add or refresh `index.md` per directory from
128
- [templates/index.md](../templates/index.md); for the bundle root use
129
- [templates/root-index.md](../templates/root-index.md) so it carries
130
- `okf_version: "0.1"`. Append a dated entry to `log.md`.
131
- 6. **Close out** — walk the Closeout gate below (`validate` + `lint` are part of it,
132
- see [cli.md](cli.md)) before finishing.
133
-
134
- ### maintain — keep a bundle in sync with reality
135
- 1. **Orient before hunting.** Run `okf index <dir>` (the §6 map — every directory's
136
- index body, rollups, and listings), read `log.md` (the §7 baseline: what changed
137
- last), and `okf stats <dir>` (size and shape) *before* you grep. It is the
138
- cheapest context and it primes the hunt — and it is the only reliable way to
139
- catch enumeration drift, because **grep cannot find an index entry that is
140
- missing.** (This is the always-on reflex in [SKILL.md](../SKILL.md).)
141
- 2. **Find *every* affected concept** — the failure mode is fixing only the obvious
142
- one. Don't rely on reading the whole bundle; that only scales on tiny ones. Grep
143
- the changed asset's `resource` URI across the bundle, grep its path/name, and use
144
- `okf graph --json` to pull the concepts that link *to* the ones you're touching.
145
- Let grep and the graph find them so nothing drifts silently.
146
- 3. Update bodies and `timestamp`; fix or add cross-links; create new concepts for
147
- new assets; mark retired assets with a `**Deprecation**` note rather than
148
- silently deleting the context that explains them.
149
- 4. **Update every enumeration that names what you changed — including `index.md`
150
- bodies**, not just the concept files: a new, renamed, or removed concept changes
151
- its directory's index listing too. Append a dated `log.md` entry. Step 1's map
152
- is how you verify this — re-run `okf index` and confirm each listing matches
153
- reality.
154
- 5. Run `validate`, then `lint` to catch the curation drift the change introduced —
155
- new orphans, broken citations, dangling index entries. Add `--stale-after`
156
- (e.g. `90d`) if concepts carry timestamps: freshness is off by default, so a
157
- plain `lint` will not tell you what the change left stale.
158
- 6. **Review loose files** — run `okf loose <dir>` (the folder-grouped view of
159
- `lint`'s `unlinked` check): the concepts with **no cross-links in or out**, which
160
- float in the graph. This is a semantic pass the tool cannot do for you — for each
161
- floater, judge intent:
162
- - **should it link out?** the concept relates to others but says so nowhere —
163
- write the sentence that explains the relationship and put the link in it;
164
- - **should something link to it?** it is knowledge others should reach by
165
- following links, not just via an index — add the inbound link from where it
166
- belongs;
167
- - **legitimately terminal?** a backlog item, a spec reference, a leaf reachable
168
- by design only through its index — leave it. **Terminal-by-design is not a
169
- defect.** Loose ≠ orphan: an index listing makes a file *reachable* (not an
170
- orphan) but is not a graph edge, so an indexed file can still float here.
171
- 7. **Curate the tag vocabulary** when the pass touched tags, or when `okf tags
172
- <dir>` shows a long tail of singletons. Run `okf tags <dir> --by area` and
173
- `--by type` — the grouped view is the analysis; read each group top-down:
174
- - **twins** — two tags riding the exact same concepts (equal counts sort them
175
- adjacent). Merge into one unless each genuinely names a different theme.
176
- - **group-name echoes** — a tag matching its own group's name (a `format` tag
177
- inside `format/`, an `overview` tag on an Overview). It restates an axis the
178
- concept already carries; drop it from those concepts.
179
- - **singletons** — for each, ask: would an existing tag serve? is it an
180
- anticipated cluster that concepts landing soon will join? is it a deliberate
181
- marker (`security`, `deprecated`)? Merge, keep, or drop accordingly — a
182
- count of 1 is a question, never a verdict.
183
- - **connective tags** — recurring across groups: these are the vocabulary's
184
- spine. Protect them; prefer merging others *into* them over renaming them,
185
- because consumers learn these keys and stability is part of their value.
186
- The trap in this pass is optimizing the numbers instead of the vocabulary:
187
- you can reach zero singletons by deleting every tag, and perfect cohesion by
188
- tagging everything alike. The goal is a small set of tags where each one
189
- either connects or marks — judged, not counted.
190
-
191
- ### consume — use a bundle as context
192
- 1. **Orient first** (the [SKILL.md](../SKILL.md) reflex): `okf index <dir>` maps the
193
- whole bundle in one pass — every directory's index body, rollups, and listings —
194
- and `log.md` gives recent history. Then follow links only into the concepts the
195
- task needs. For a large bundle, `okf graph --json` gives the whole link structure
196
- at once so you can plan a traversal without opening every file.
197
- 2. Treat broken links as not-yet-written knowledge, not errors.
198
- 3. **Write-back reflex:** if you learn something durable while working — a fact the
199
- bundle lacks, a link it is missing, a concept that no longer matches reality —
200
- switch to `maintain` and record it. That reflex is what keeps the bundle alive.
117
+ The step-by-step playbooks live in [../playbooks/](../playbooks/), one file per
118
+ verb (produce, maintain, consume, curate, doctor), routed by the Commands table
119
+ in [SKILL.md](../SKILL.md). The Closeout below is their shared finishing gate.
201
120
 
202
121
  ## Closeout — the finishing gate
203
122
 
@@ -105,7 +105,8 @@ But an index listing is **not a graph edge**: a file can be listed in an index y
105
105
  have no cross-links, so it floats in the graph while `lint` reports it as reachable.
106
106
  `loose`/`unlinked` catch exactly that gap. A loose file is not automatically a
107
107
  defect — a terminal leaf (a backlog item, a spec reference) can be loose by design;
108
- `loose` surfaces the set so you can judge intent (see `maintain` in authoring.md).
108
+ `loose` surfaces the set so you can judge intent (see the
109
+ [maintain playbook](../playbooks/maintain.md)).
109
110
 
110
111
  ## index — the progressive-disclosure map (§6)
111
112
 
@@ -148,8 +149,8 @@ in/out link degree). Add `--json` to any for a machine substrate.
148
149
  descending. The "what themes dominate" view. JSON: `{ bundle, count, tags: [{ tag,
149
150
  count, concepts: [id, …] }] }`. `--by type|area` regroups the list per concept
150
151
  dimension with **within-group** counts (a tag spanning groups appears in each) —
151
- the substrate for tag curation; the judgment recipe lives in
152
- [authoring.md](authoring.md)'s maintain playbook. JSON: `{ bundle, count, by,
152
+ the substrate for tag curation; the judgment recipe lives in the
153
+ [maintain playbook](../playbooks/maintain.md). JSON: `{ bundle, count, by,
153
154
  groups: [{ <dim>, count, tags: […] }] }`.
154
155
  - **`types`** — every type with the concepts that carry it, ordered by count
155
156
  descending. The "what kinds of knowledge" view. JSON: `{ bundle, count, types:
data/lib/okf/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OKF
4
- VERSION = "1.0.0"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/okf.rb CHANGED
@@ -4,7 +4,6 @@ require "erb"
4
4
  require "fileutils"
5
5
  require "date"
6
6
  require "json"
7
- require "optparse"
8
7
  require "pathname"
9
8
  require "securerandom"
10
9
  require "set"
@@ -50,6 +49,4 @@ module OKF
50
49
  require "okf/bundle/reader"
51
50
  require "okf/bundle/writer"
52
51
  require "okf/bundle/folder"
53
- require "okf/skill"
54
- require "okf/cli"
55
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: okf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
@@ -38,11 +38,13 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.4'
40
40
  description: |
41
- OKF is portable knowledge Markdown files with YAML frontmatter that both
42
- humans and agents read. This gem reads OKF bundles, checks them for v0.1 (§9)
43
- conformance, and serves them as an interactive graph (a mountable Rack app).
44
- It ships a library API (OKF::Bundle and friends) plus an `okf` command-line
45
- tool (validate / lint / loose / server / graph / skill).
41
+ OKF (Open Knowledge Format) is portable knowledge: Markdown files with YAML
42
+ frontmatter that both humans and agents read from one source. This gem is the
43
+ Ruby-native way to work with it. Its companion agent skill authors and curates
44
+ a bundle; the `okf` command-line tool validates the result for v0.1 (§9)
45
+ conformance, lints its curation quality, and serves it as an interactive graph
46
+ (a mountable Rack app). The same validate, lint, and graph run in-process
47
+ through a library API (OKF::Bundle and friends).
46
48
  email:
47
49
  - rodrigo.serradura@gmail.com
48
50
  executables:
@@ -104,6 +106,12 @@ files:
104
106
  - lib/okf/server/templates/graph.html.erb
105
107
  - lib/okf/skill.rb
106
108
  - lib/okf/skill/SKILL.md
109
+ - lib/okf/skill/playbooks/consume.md
110
+ - lib/okf/skill/playbooks/curate.md
111
+ - lib/okf/skill/playbooks/doctor.md
112
+ - lib/okf/skill/playbooks/maintain.md
113
+ - lib/okf/skill/playbooks/menu.md
114
+ - lib/okf/skill/playbooks/produce.md
107
115
  - lib/okf/skill/reference/APACHE-2.0.txt
108
116
  - lib/okf/skill/reference/SPEC.md
109
117
  - lib/okf/skill/reference/authoring.md
@@ -138,5 +146,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
146
  requirements: []
139
147
  rubygems_version: 4.0.16
140
148
  specification_version: 4
141
- summary: Read, validate, and serve Open Knowledge Format (OKF) v0.1 bundles.
149
+ summary: 'A lightweight Ruby gem for OKF: author, curate, and serve bundles as an
150
+ interactive graph.'
142
151
  test_files: []