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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +193 -1
- data/README.md +77 -428
- data/lib/okf/bundle/folder.rb +25 -2
- data/lib/okf/bundle/reader.rb +39 -16
- data/lib/okf/bundle/search.rb +5 -5
- data/lib/okf/bundle/skeleton.rb +241 -0
- data/lib/okf/bundle.rb +37 -17
- data/lib/okf/cli/catalog.rb +6 -6
- data/lib/okf/cli/command.rb +139 -19
- data/lib/okf/cli/dirs.rb +1 -1
- data/lib/okf/cli/files.rb +1 -1
- data/lib/okf/cli/graph.rb +115 -9
- data/lib/okf/cli/index.rb +2 -2
- data/lib/okf/cli/registry.rb +152 -15
- data/lib/okf/cli/render.rb +3 -2
- data/lib/okf/cli/search.rb +53 -8
- data/lib/okf/cli/server.rb +9 -5
- data/lib/okf/cli/stats.rb +8 -8
- data/lib/okf/cli/tags.rb +2 -2
- data/lib/okf/cli.rb +3 -3
- data/lib/okf/concept/file.rb +17 -2
- data/lib/okf/path.rb +17 -3
- data/lib/okf/registry.rb +351 -20
- data/lib/okf/render/graph/template.html.erb +211 -7
- data/lib/okf/render/graph.rb +27 -3
- data/lib/okf/safe_read.rb +50 -0
- data/lib/okf/server/app.rb +13 -3
- data/lib/okf/server/hub.rb +5 -3
- data/lib/okf/skill/SKILL.md +5 -1
- data/lib/okf/skill/playbooks/menu.md +2 -1
- data/lib/okf/skill/playbooks/refine.md +26 -0
- data/lib/okf/skill/reference/cli.md +81 -21
- data/lib/okf/version.rb +1 -1
- data/lib/okf.rb +2 -0
- metadata +4 -3
- data/CODE_OF_CONDUCT.md +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84ee2cefa8e04f2358c9125ca00ccdf9de8a12ab0a78a96315ec846bab8d3e87
|
|
4
|
+
data.tar.gz: 7a3186c5aa2924b4425a0c0a86c9f0812948427348d4fd3d0cd46852bd6dc575
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0c9f1002b408702c74f73be001eab10b6691419700d33b41e81bd001d21858f1d79289696edbb2ad6e7c6f4a76ccd4c7458cf211e82fded5224f5fcfa6328b4
|
|
7
|
+
data.tar.gz: 0ed68e0095ab1aa912c5df3b6fd7b9f82e0e07f2cad0c182437cb70021beed8d5ecb054106185204b18d0f138303372bc62202c286aec75ce6c4a43434f2e546
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,196 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.13.0] - 2026-08-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`Bundle#directories`** — the bundle's own answer to "does this bundle have
|
|
13
|
+
directory X?", public because every surface now asks it (the CLI's concept
|
|
14
|
+
views, `okf-mcp`'s `dir` refusal) and the API this version's minor bump
|
|
15
|
+
exists for. Introduced by, and argued in, the `root` fix under Fixed below.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **The repository became a monorepo, and the gem moved into `okf/`.** Nothing
|
|
20
|
+
about the published gem changes in kind — same name, same library, same `v*` tag
|
|
21
|
+
series, same image, byte-identical in size to a pre-move build — but a checkout is laid out differently: one
|
|
22
|
+
directory per gem, named for the gem it ships, so `okf-mcp/` and the rest can
|
|
23
|
+
land beside the baseline without reshaping anything. `plugin/`,
|
|
24
|
+
`.claude-plugin/`, `.okf/` and the `Dockerfile` stay at the root; the last of
|
|
25
|
+
those because its build context must be the repo root, where the `.git` the
|
|
26
|
+
gemspec's `git ls-files` needs lives. From a checkout it is `cd okf` for
|
|
27
|
+
everything about the gem, and plain `rake` at the root to run every gem's suite
|
|
28
|
+
or to validate this repo's own bundle.
|
|
29
|
+
- **`CODE_OF_CONDUCT.md` no longer ships in the gem** (70 files, from 71). It is
|
|
30
|
+
a repository document and was packaged only because nothing rejected it. The
|
|
31
|
+
gem now carries **its own README** instead of the project's — which also fixes
|
|
32
|
+
a long-standing wart, since the project README opens with images under
|
|
33
|
+
`.github/` that the gemspec has never packaged, so the page on rubygems.org has
|
|
34
|
+
been rendering a broken hero.
|
|
35
|
+
- `changelog_uri` points at `okf/CHANGELOG.md`, where this file now lives.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **A directory really named `root` is nameable again.** `--dir` and `--area`
|
|
40
|
+
accept `root` as a spelling of the bundle root, which is worth a shell quote
|
|
41
|
+
right up until a bundle actually has a `root/` directory — and then the alias
|
|
42
|
+
made it unaddressable, answering for the bundle root instead: the wrong
|
|
43
|
+
concepts, exit 0, nothing said. The alias now yields to a real directory,
|
|
44
|
+
since a convenience is worth less than a directory being reachable at all;
|
|
45
|
+
where no such directory exists nothing changes, which is every bundle that
|
|
46
|
+
has ever relied on the spelling. `okf dirs` carried the same fold into its
|
|
47
|
+
`subtree` column, where it computed the `root` row's subtree against the
|
|
48
|
+
bundle root — so the number on the row disagreed with what `--dir` on that
|
|
49
|
+
row returns, the one thing it promises never to do.
|
|
50
|
+
|
|
51
|
+
"Does this bundle have a `root` directory?" is now asked of the bundle
|
|
52
|
+
(`Bundle#directories`, under Added above) rather than of whichever list a view
|
|
53
|
+
had to hand. The concept views were reading it off the *catalog*, which knows
|
|
54
|
+
only directories holding concepts, so a `root/` carrying nothing but an
|
|
55
|
+
`index.md` stayed folded in `catalog`/`files`/`tags`/`types`/`search` while
|
|
56
|
+
`dirs` and `index` named it correctly — two answers to one question about one
|
|
57
|
+
bundle. And a multi-bundle `okf search @a @b --dir root` resolved per bundle
|
|
58
|
+
inside the loop, so one flag meant the `root/` subtree in the bundle that has
|
|
59
|
+
one and the bundle root in the bundle that does not, merged into a single
|
|
60
|
+
ranking with nothing in the output saying so; the served set resolves it once.
|
|
61
|
+
|
|
62
|
+
`Bundle#directories` counts every file kind that makes a directory real —
|
|
63
|
+
concepts, an `index.md`, and now a scoped `log.md`, which the first cut left
|
|
64
|
+
out: a `root/` holding only its history was invisible to the set, so the
|
|
65
|
+
alias won again for exactly the shape the fix was named after, one file kind
|
|
66
|
+
over. The same seed feeds `directory_index`, so `dirs`, `index` and `stats`
|
|
67
|
+
count a log-only directory too, and every surface answers "does this bundle
|
|
68
|
+
have directory X?" from one list.
|
|
69
|
+
- **`filter_entries` regained its 1.12.0 arity.** The dirs-source fix above
|
|
70
|
+
added a required third parameter to a helper on `CLI::Command` — the base
|
|
71
|
+
every verb inherits, plugin gems' verbs included — so an out-of-tree verb
|
|
72
|
+
calling the released two-argument shape raised `ArgumentError` at runtime.
|
|
73
|
+
The parameter defaults now, and the default is 1.12.0's resolution — the
|
|
74
|
+
helper is handed rows, not the folder, so it has nothing to resolve a real
|
|
75
|
+
`root/` against, and the `root` alias folds exactly as it did; the plugin
|
|
76
|
+
suite drives a 1.12.0-era verb through the two-argument shape to pin it.
|
|
77
|
+
Its sibling `filter_ids` makes the opposite choice on purpose: it *is*
|
|
78
|
+
handed the folder, and its two-argument shape resolves through `dir_scope`,
|
|
79
|
+
because the unconditional fold there was the bug this entry fixes, not a
|
|
80
|
+
contract — an old caller gets the corrected answer rather than the familiar
|
|
81
|
+
wrong one. In-tree callers pass
|
|
82
|
+
the set through `dir_scope`, which also derives it only when a `--dir` or
|
|
83
|
+
`--area` flag will actually consult it — a plain listing no longer pays a
|
|
84
|
+
per-path ancestor walk for a set nothing reads (and `Bundle#directories` is
|
|
85
|
+
memoized, so the verbs that ask twice pay once).
|
|
86
|
+
- **Coverage stopped measuring the plugin's curation hook** for one commit, and
|
|
87
|
+
said so by going *up*: SimpleCov's root defaults to the working directory, so
|
|
88
|
+
moving the gem down a level dropped ~100 tested lines out of the report and
|
|
89
|
+
line coverage read 98.63% against 98.47%. Its root is the repository now, with
|
|
90
|
+
the report still written inside the gem.
|
|
91
|
+
|
|
92
|
+
### Security
|
|
93
|
+
|
|
94
|
+
- **A symlinked file in a bundle can no longer read past the bundle root.** The
|
|
95
|
+
traversal guard `Path.join_under!` was lexical — it expands the path string,
|
|
96
|
+
and `File.expand_path` does not resolve a symlink — so a link whose name sat
|
|
97
|
+
inside the root but whose target did not passed the check, and the read
|
|
98
|
+
followed it. Every read now realpath-resolves and re-checks against the real
|
|
99
|
+
root: `Path.under?` is the shared pure predicate, `Bundle::Reader` quarantines
|
|
100
|
+
an escaping file into the unparseable bucket (a planted symlink is one bad
|
|
101
|
+
file, not grounds to fail the whole bundle read, which would hand any writer of
|
|
102
|
+
a served directory a denial of service), and `Concept::File` guards its own
|
|
103
|
+
read. A symlink that stays inside the bundle still resolves as before. The gap
|
|
104
|
+
reached every reader; serving a bundle's contents to an agent — as `okf-mcp`
|
|
105
|
+
does — is what surfaced it.
|
|
106
|
+
|
|
107
|
+
## [1.12.0] - 2026-07-24
|
|
108
|
+
|
|
109
|
+
### Added
|
|
110
|
+
|
|
111
|
+
- **`okf graph --traffic`** — the link graph read one grain coarser. `--hubs`
|
|
112
|
+
measures concepts, but the refine playbook's directory judgements ("does this
|
|
113
|
+
directory prune? a concern, or a container?") had nothing measured at their
|
|
114
|
+
grain. `--traffic` collapses each concept into its directory and the links
|
|
115
|
+
between two directories into one weighted arc, so every row carries its
|
|
116
|
+
internal / out / in traffic and a **cohesion** — its internal share of that
|
|
117
|
+
total. That is cohesion versus coupling on a knowledge tree: near-zero cohesion
|
|
118
|
+
under heavy inbound is a shared vocabulary doing its job, heavy outbound with
|
|
119
|
+
nothing back is a projection wearing a directory. The rows sort by cohesion
|
|
120
|
+
ascending, so the directories with a case to answer come first, and a directory
|
|
121
|
+
with no traffic at all prints `—` rather than a `0%` it did not earn. The arc
|
|
122
|
+
**cut** is fitted to the bundle, not fixed — at weight 3, ten bundles ranged
|
|
123
|
+
from 2 arcs to 136 — and `--cut N` overrides it; cohesion is computed over
|
|
124
|
+
*every* arc regardless, so narrowing the drawn picture never moves the
|
|
125
|
+
evidence. JSON: `{ bundle, cut, fitted, dirs, arcs, total_arcs }`. The pure
|
|
126
|
+
model underneath is `Bundle::Skeleton` — concepts folded to directories, links
|
|
127
|
+
to weighted arcs, every edge tagged with the cut it survives (a local-degree
|
|
128
|
+
sparsifier, union rule, so no linked concept is ever stranded) — and it does no
|
|
129
|
+
I/O and draws nothing: it names a cut rather than taking one, so both the graph
|
|
130
|
+
page and the CLI narrow the same emission their own way.
|
|
131
|
+
- **The graph page draws links in three amounts, and opens dense on its spine.**
|
|
132
|
+
227 links over 47 concepts at degree 9.7 is unreadable because of its arrows,
|
|
133
|
+
not its dots — so links become a layer: **every** link, the **spine** (each
|
|
134
|
+
concept's strongest edge — the Skeleton's `keep_at===0` set, chosen so it
|
|
135
|
+
touches every linked concept), or **none**, with a selected concept's own links
|
|
136
|
+
always shown in full. A dense bundle now opens on its spine rather than greeting
|
|
137
|
+
the reader with the thicket; the trigger is undirected degree above a floor set
|
|
138
|
+
between a tree's ~2 and the 9.7 that drove it, and `--map` overrides to none
|
|
139
|
+
with the directories boxed. Above 800 edges the first layout runs on the spine
|
|
140
|
+
alone and the rest arrive a frame later with no re-layout — **6.25 s → 3.29 s**
|
|
141
|
+
on 414 concepts, nothing on screen moving. Proven in both render modes on a
|
|
142
|
+
committed 110-concept / 880-link fixture, the only one over both floors.
|
|
143
|
+
- **Registry groups — a named, recursive set of bundles.** A group is a registry
|
|
144
|
+
slug that names a list of members (bundle *or* group slugs, so they nest) and
|
|
145
|
+
resolves recursively, path-deduped, to its bundle leaves. `okf registry group
|
|
146
|
+
<slug> <@member…>` creates one or adds to it, `ungroup` removes members (and
|
|
147
|
+
emptying a group deletes it), and `del`/`rename` now span a group slug too —
|
|
148
|
+
one name cascades across every member list, one `del` cascade-drops the slug and
|
|
149
|
+
deletes any group it empties. Groups live in their own list in the registry
|
|
150
|
+
JSON, so the first-is-default rule and the `File.directory?` guards never meet a
|
|
151
|
+
pathless entry. `okf search @backend` merges the members into one ranking and
|
|
152
|
+
`okf server @backend` mounts each (the first at `/`), both skipping a vanished
|
|
153
|
+
member with a note, exactly as `@all` does. Every single-bundle verb **refuses**
|
|
154
|
+
a `@group` (exit 2) — the same rule that refuses a second bundle, through the
|
|
155
|
+
same `resolve_registered` seam.
|
|
156
|
+
- **`okf registry init`** — create a project-local `.okf-registry.json` in the
|
|
157
|
+
current directory. Once it exists, okf discovers it by walking up from the
|
|
158
|
+
working directory, and every registry operation — and every `@slug` — resolves
|
|
159
|
+
through it in place of the global `$OKF_HOME` registry, so a bare `okf server`
|
|
160
|
+
inside a repo serves that repo's bundles with no global setup. The nearest one
|
|
161
|
+
on the path wins (nested registries resolve nearest-first), `okf registry list`
|
|
162
|
+
names the local file it found, and `OKF_NO_DISCOVERY=1` forces the global one —
|
|
163
|
+
the escape hatch for a fixed-cwd caller (CI, a tool).
|
|
164
|
+
- **A local registry stores portable, relative paths.** A bundle inside the
|
|
165
|
+
registry's own tree is written relative to the `.okf-registry.json`, so the file
|
|
166
|
+
can be committed and travels with the repo — a checkout on another machine, or a
|
|
167
|
+
container that mounts it, resolves the same bundles unchanged. A bundle outside
|
|
168
|
+
the tree keeps an absolute path (it cannot travel). Paths still read back
|
|
169
|
+
absolute everywhere the CLI reports them; the relative form lives only on disk,
|
|
170
|
+
and an existing absolute local entry migrates to relative on its next write. The
|
|
171
|
+
global `$OKF_HOME` registry is unchanged — it stores absolute paths as before.
|
|
172
|
+
|
|
173
|
+
### Changed
|
|
174
|
+
|
|
175
|
+
- **The derived `area` field is renamed `top_dir`** — the first-path-segment
|
|
176
|
+
rollup the catalog, search, `stats`, and `graph --hubs` carry. `area` was never
|
|
177
|
+
the OKF spec's word (the spec speaks only of `dir`), so the rollup now names
|
|
178
|
+
itself in the spec's vocabulary: it is the `dir` at the top level. The `--json`
|
|
179
|
+
keys move with it — catalog/search rows carry `top_dir` in place of `area`,
|
|
180
|
+
`stats` emits `top_dirs`/`by_top_dir`, and `graph --hubs` emits
|
|
181
|
+
`top_dir`/`by_top_dir`. The **deprecated `--area`/`--by area` input flags are
|
|
182
|
+
unchanged** — they still warn and map to `--dir`/`--by dir`, and now source the
|
|
183
|
+
renamed field internally. No behavior changes; the well-homed-hub numbers are
|
|
184
|
+
identical.
|
|
185
|
+
|
|
186
|
+
### Fixed
|
|
187
|
+
|
|
188
|
+
- **A local-registry server preserves its relative-path anchor across a
|
|
189
|
+
re-open.** The hub re-read its boot registry with `OKF::Registry.new(path)`,
|
|
190
|
+
which drops the `relative_base` a discovered `.okf-registry.json` carries — so
|
|
191
|
+
on a project-local server the Bundles panel matched each mounted bundle's
|
|
192
|
+
absolute root against the re-read entry's *relative* path, missed, and drew
|
|
193
|
+
every in-tree bundle as "folder is gone", while a browser add flattened the new
|
|
194
|
+
bundle to an absolute path, undoing the portability relative storage exists for.
|
|
195
|
+
`Registry#reopen` re-reads the same file anchored the same way, and both hub
|
|
196
|
+
re-open sites use it.
|
|
197
|
+
|
|
8
198
|
## [1.11.0] - 2026-07-22
|
|
9
199
|
|
|
10
200
|
### Added
|
|
@@ -205,7 +395,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
205
395
|
rendered `(root)` for humans — and "cluster" stays prose for what a dir
|
|
206
396
|
groups. Both deprecated spellings keep their **old behavior exactly** and warn
|
|
207
397
|
once per run on stderr (`--json` on stdout is unaffected); they go in a later
|
|
208
|
-
release
|
|
398
|
+
release.
|
|
209
399
|
|
|
210
400
|
## [1.10.0] - 2026-07-21
|
|
211
401
|
|
|
@@ -1132,6 +1322,8 @@ Initial release.
|
|
|
1132
1322
|
|
|
1133
1323
|
- Runs on Ruby >= 2.4 with two runtime dependencies: rack and webrick.
|
|
1134
1324
|
|
|
1325
|
+
[1.13.0]: https://github.com/serradura/okf-gem/compare/v1.12.0...v1.13.0
|
|
1326
|
+
[1.12.0]: https://github.com/serradura/okf-gem/compare/v1.11.0...v1.12.0
|
|
1135
1327
|
[1.11.0]: https://github.com/serradura/okf-gem/compare/v1.10.0...v1.11.0
|
|
1136
1328
|
[1.10.0]: https://github.com/serradura/okf-gem/compare/v1.9.0...v1.10.0
|
|
1137
1329
|
[1.9.0]: https://github.com/serradura/okf-gem/compare/v1.8.0...v1.9.0
|