okf 1.0.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 +7 -0
- data/.okf/capabilities/agent-skill.md +46 -0
- data/.okf/capabilities/graph-server.md +60 -0
- data/.okf/capabilities/index.md +20 -0
- data/.okf/capabilities/library-api.md +67 -0
- data/.okf/capabilities/linter.md +49 -0
- data/.okf/capabilities/read-views.md +84 -0
- data/.okf/capabilities/validator.md +40 -0
- data/.okf/cli.md +52 -0
- data/.okf/design/core-shell-split.md +58 -0
- data/.okf/design/index.md +10 -0
- data/.okf/design/ruby-floor.md +45 -0
- data/.okf/design/runtime-dependencies.md +44 -0
- data/.okf/design/server-trust-boundary.md +35 -0
- data/.okf/format/citations.md +33 -0
- data/.okf/format/cross-links.md +52 -0
- data/.okf/format/frontmatter.md +38 -0
- data/.okf/format/index.md +9 -0
- data/.okf/format/okf-format.md +43 -0
- data/.okf/index.md +18 -0
- data/.okf/log.md +9 -0
- data/.okf/model/bundle.md +38 -0
- data/.okf/model/concept.md +44 -0
- data/.okf/model/graph.md +44 -0
- data/.okf/model/index.md +8 -0
- data/.okf/overview.md +66 -0
- data/CHANGELOG.md +54 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +201 -0
- data/NOTICE +10 -0
- data/README.md +276 -0
- data/exe/okf +6 -0
- data/lib/okf/bundle/folder.rb +94 -0
- data/lib/okf/bundle/graph.rb +118 -0
- data/lib/okf/bundle/linter/report.rb +56 -0
- data/lib/okf/bundle/linter.rb +416 -0
- data/lib/okf/bundle/reader.rb +60 -0
- data/lib/okf/bundle/validator/result.rb +35 -0
- data/lib/okf/bundle/validator.rb +131 -0
- data/lib/okf/bundle/writer.rb +137 -0
- data/lib/okf/bundle.rb +216 -0
- data/lib/okf/cli.rb +910 -0
- data/lib/okf/concept/file.rb +63 -0
- data/lib/okf/concept.rb +101 -0
- data/lib/okf/markdown/citations.rb +49 -0
- data/lib/okf/markdown/frontmatter.rb +55 -0
- data/lib/okf/markdown/links.rb +98 -0
- data/lib/okf/path.rb +34 -0
- data/lib/okf/server/app.rb +120 -0
- data/lib/okf/server/graph.rb +112 -0
- data/lib/okf/server/runner.rb +78 -0
- data/lib/okf/server/templates/graph.html.erb +803 -0
- data/lib/okf/skill/SKILL.md +133 -0
- data/lib/okf/skill/reference/APACHE-2.0.txt +202 -0
- data/lib/okf/skill/reference/SPEC.md +460 -0
- data/lib/okf/skill/reference/authoring.md +218 -0
- data/lib/okf/skill/reference/cli.md +196 -0
- data/lib/okf/skill/templates/concept.md +24 -0
- data/lib/okf/skill/templates/index.md +8 -0
- data/lib/okf/skill/templates/log.md +6 -0
- data/lib/okf/skill/templates/root-index.md +12 -0
- data/lib/okf/skill.rb +82 -0
- data/lib/okf/version.rb +5 -0
- data/lib/okf.rb +55 -0
- metadata +142 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# OKF tool verbs — the `okf` CLI
|
|
2
|
+
|
|
3
|
+
`validate`, `lint`, `loose`, `index`, `catalog`, `files`, `tags`, `types`, `stats`,
|
|
4
|
+
`server`, and `graph` are **not** eyeball passes and are not
|
|
5
|
+
reimplemented in this skill. They run the deterministic `okf` executable shipped by
|
|
6
|
+
the companion gem — the single source of truth for OKF mechanics. Your job is to
|
|
7
|
+
invoke it correctly and interpret the result, not to reason out conformance by hand.
|
|
8
|
+
|
|
9
|
+
## Presence guard
|
|
10
|
+
|
|
11
|
+
Check the tool exists before relying on it. If it is missing, the gem is not
|
|
12
|
+
installed — say so and stop; never fabricate a result:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
command -v okf >/dev/null || echo "okf CLI not found — install it: 'gem install okf' (or from a checkout: 'cd gem && bundle exec rake install')"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Invocation
|
|
19
|
+
|
|
20
|
+
The surface is self-describing — `okf --help` maps every verb, `okf <verb> --help`
|
|
21
|
+
its flags. Ask the tool for what exists; this file carries only what `--help`
|
|
22
|
+
cannot: each verb's semantics, its traps, and its JSON shape.
|
|
23
|
+
|
|
24
|
+
**`--json` is compact by design.** Every emitting verb prints single-line JSON —
|
|
25
|
+
the token-efficient substrate you consume; `--pretty` (which implies `--json`)
|
|
26
|
+
indents it for a human. The bytes differ, the JSON is identical, so parse either.
|
|
27
|
+
When you only need to *scan* a bundle, the plain text views are lighter still than
|
|
28
|
+
JSON (they print each key once, not per row) — reach for `--json` when you need to
|
|
29
|
+
extract structure, not merely read it.
|
|
30
|
+
|
|
31
|
+
**Project the JSON to what you'll read.** On `index`, `catalog`, and `files`,
|
|
32
|
+
`--fields a,b` keeps only those properties and `--except a,b` drops them
|
|
33
|
+
(mutually exclusive; both imply `--json`; an unknown name is a usage error that
|
|
34
|
+
lists the valid ones). Projection happens before emission, so you pay no tokens
|
|
35
|
+
for a field you dropped — e.g. `okf index <dir> --except body,listing` is the lean
|
|
36
|
+
directory *skeleton* (structure + rollups), and on a large bundle that is the
|
|
37
|
+
difference between a few hundred bytes and hundreds of KB, since the per-item rows
|
|
38
|
+
(`listing`) dominate at scale. `okf index --no-body` is shorthand for dropping just
|
|
39
|
+
`body`.
|
|
40
|
+
|
|
41
|
+
**Exit codes:** `0` success · `1` non-conformant bundle (or a `lint --fail-on`
|
|
42
|
+
threshold crossed) · `2` usage error. `graph` and `server` are best-effort
|
|
43
|
+
(§9): a file with invalid frontmatter is skipped and noted on stderr, never fatal.
|
|
44
|
+
|
|
45
|
+
## validate — the hard gate (§9)
|
|
46
|
+
|
|
47
|
+
Implements the spec's §9 conformance definition exactly:
|
|
48
|
+
|
|
49
|
+
- **§9.1** every non-reserved file has a parseable YAML frontmatter block;
|
|
50
|
+
- **§9.2** every such block has a non-empty `type`;
|
|
51
|
+
- **§9.3** any `index.md`/`log.md` present follows §6/§7 (a nested `index.md` has
|
|
52
|
+
no frontmatter, a root `index.md` carries only `okf_version`, `log.md` date
|
|
53
|
+
headings are ISO `YYYY-MM-DD`).
|
|
54
|
+
|
|
55
|
+
`ERROR`s are the three conditions above; the bundle is non-conformant until every
|
|
56
|
+
one is fixed. `warn`s are soft — missing recommended fields, non-list tags, an
|
|
57
|
+
unparseable timestamp, and **broken cross-links, which §5.3 explicitly tolerates**.
|
|
58
|
+
Fix warnings when cheap; never block on them. Use `--json` in CI.
|
|
59
|
+
|
|
60
|
+
## lint — curation quality (advisory)
|
|
61
|
+
|
|
62
|
+
Asks the complementary question to `validate`: not "is this legal OKF?" but "is
|
|
63
|
+
this well-curated, navigable, trustworthy?" — precisely over the things §9 forbids
|
|
64
|
+
`validate` from rejecting. It has its own report, never emits conformance errors,
|
|
65
|
+
and **exits `0` even with findings** unless you pass `--fail-on warn`.
|
|
66
|
+
|
|
67
|
+
Six conceptual categories, each backed by individual checks (names in parens):
|
|
68
|
+
|
|
69
|
+
- **reachability** — orphans, concepts not in any index, disconnected islands,
|
|
70
|
+
and unlinked (degree-0) files
|
|
71
|
+
(`orphan`, `not_in_index`, `disconnected_component`, `unlinked`)
|
|
72
|
+
- **backlog** — demand-ranked missing concepts (linked-to but absent), broken index entries
|
|
73
|
+
(`missing_concept`, `broken_index_entry`)
|
|
74
|
+
- **completeness** — stubs, missing `title` / `description` / `timestamp`
|
|
75
|
+
(`stub`, `missing_title`, `missing_description`, `missing_timestamp`)
|
|
76
|
+
- **freshness** — concepts older than a cutoff (`stale`) — **only computed when you
|
|
77
|
+
pass `--stale-after`; a plain `okf lint` never reports staleness at all**
|
|
78
|
+
- **provenance** — uncited external claims, broken citations, spec §8
|
|
79
|
+
(`uncited_external`, `broken_citation`)
|
|
80
|
+
- **hygiene** — duplicate titles, unused/undefined reference links, self-links
|
|
81
|
+
(`duplicate_title`, `unused_reference_def`, `undefined_reference`, `self_link`)
|
|
82
|
+
|
|
83
|
+
`--only` / `--except` filter by the **individual check names above**, not the
|
|
84
|
+
category labels — `okf lint <dir> --only orphan,stub` works; `--only reachability`
|
|
85
|
+
is an error. Two knobs tune specific checks: `--min-body N` sets the `stub` body
|
|
86
|
+
threshold in characters (default 50), and `--stale-after DUR` sets the `stale`
|
|
87
|
+
cutoff — a duration like `90d` or `12w`, or an ISO date like `2026-01-01` (a bare
|
|
88
|
+
number is rejected).
|
|
89
|
+
|
|
90
|
+
`lint --json` is the structured substrate you consume to reason about the two
|
|
91
|
+
things lint deliberately does **not** compute — contradictions and *semantic*
|
|
92
|
+
staleness — which need understanding of meaning.
|
|
93
|
+
|
|
94
|
+
## loose — files with no graph connections (by folder)
|
|
95
|
+
|
|
96
|
+
Lists the **loose** files — concepts with graph **degree 0**: no cross-links in
|
|
97
|
+
*or* out — grouped by folder. It is a focused, folder-organized view over `lint`'s
|
|
98
|
+
`unlinked` check (`okf loose <dir>` ≈ `okf lint <dir> --only unlinked`, regrouped),
|
|
99
|
+
for the "which files float in the graph?" question. Advisory: **exits `0`**; `--json`
|
|
100
|
+
emits `{ bundle, count, loose: [{ id, title, dir }] }`.
|
|
101
|
+
|
|
102
|
+
**Loose ≠ orphan** — the trap. `lint`'s `orphan` is about *reachability*, and an
|
|
103
|
+
`index.md` listing makes a file reachable, so an indexed file is never an orphan.
|
|
104
|
+
But an index listing is **not a graph edge**: a file can be listed in an index yet
|
|
105
|
+
have no cross-links, so it floats in the graph while `lint` reports it as reachable.
|
|
106
|
+
`loose`/`unlinked` catch exactly that gap. A loose file is not automatically a
|
|
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).
|
|
109
|
+
|
|
110
|
+
## index — the progressive-disclosure map (§6)
|
|
111
|
+
|
|
112
|
+
The "orient before you read" view, and the one read verb that sees the layer the
|
|
113
|
+
others can't: `index.md` files are reserved/structural, so `catalog`/`files`/… (all
|
|
114
|
+
concept views) never show them. `okf index <dir>` prints one entry per directory
|
|
115
|
+
that holds concepts or carries an `index.md`, root first — the authored index body
|
|
116
|
+
(frontmatter stripped), a `type`/`tag` rollup over the concepts that live directly
|
|
117
|
+
there, its child directories, and the concept listing. Run it first when picking up
|
|
118
|
+
an existing bundle: it is the cheapest high-signal orientation, and it surfaces
|
|
119
|
+
enumeration drift a grep can't (you can't grep for a listing entry that is *missing*).
|
|
120
|
+
|
|
121
|
+
`--area A` narrows to a directory and is **repeatable** — `--area model --area
|
|
122
|
+
format` shows both; `root` names the bundle root. `--no-body` drops the prose to a
|
|
123
|
+
skeleton (headers, rollups, child pointers). For a directory that has concepts but
|
|
124
|
+
**no `index.md`**, the listing is **synthesized** from the concepts' descriptions
|
|
125
|
+
and tagged `(no index.md)` — §6 explicitly permits synthesizing a map on the fly.
|
|
126
|
+
|
|
127
|
+
It is a **read view**: advisory, always exit 0. A synthesized directory is a
|
|
128
|
+
*signal* (a map worth writing), never a defect — `index` emits no lint findings and
|
|
129
|
+
never fails a bundle. JSON: `{ bundle, count, directories: [{ dir, index_path,
|
|
130
|
+
present, synthesized, count, types, tags, subdirs, body, listing: [{ id, title,
|
|
131
|
+
description, type, tags }] }] }`.
|
|
132
|
+
|
|
133
|
+
## catalog / files / tags / types / stats — the server views, as text
|
|
134
|
+
|
|
135
|
+
The browser server (below) has Catalog, Files, Tags and Stats panels; these
|
|
136
|
+
verbs reproduce them on the CLI so an agent can read a bundle without a browser.
|
|
137
|
+
All are advisory reads (exit 0) sharing one data source (per-concept metadata plus
|
|
138
|
+
in/out link degree). Add `--json` to any for a machine substrate.
|
|
139
|
+
|
|
140
|
+
- **`catalog`** — every concept with its metadata (type, status, tags, timestamp,
|
|
141
|
+
in/out link degree, description), grouped by top-level area. The "what's here, in
|
|
142
|
+
detail" view. JSON: `{ bundle, count, concepts: [{ id, title, type, description,
|
|
143
|
+
tags, timestamp, status, backlog_ref, dir, area, links_out, links_in }] }`.
|
|
144
|
+
- **`files`** — the folder tree: each concept's filename + title, grouped by
|
|
145
|
+
directory. The "how it's organised" view. JSON: `{ bundle, count, files: [{ path,
|
|
146
|
+
id, dir, type, title, description }] }`.
|
|
147
|
+
- **`tags`** — every tag with the concepts that carry it, ordered by count
|
|
148
|
+
descending. The "what themes dominate" view. JSON: `{ bundle, count, tags: [{ tag,
|
|
149
|
+
count, concepts: [id, …] }] }`. `--by type|area` regroups the list per concept
|
|
150
|
+
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,
|
|
153
|
+
groups: [{ <dim>, count, tags: […] }] }`.
|
|
154
|
+
- **`types`** — every type with the concepts that carry it, ordered by count
|
|
155
|
+
descending. The "what kinds of knowledge" view. JSON: `{ bundle, count, types:
|
|
156
|
+
[{ type, count, concepts: [id, …] }] }`.
|
|
157
|
+
- **`stats`** — bundle rollups: concept / area / type / cross-link / distinct-tag
|
|
158
|
+
totals plus per-type and per-area breakdowns. The "shape at a glance" view. JSON:
|
|
159
|
+
`{ bundle, concepts, areas, concept_types, cross_links, distinct_tags, by_type, by_area }`.
|
|
160
|
+
|
|
161
|
+
The four list views narrow with the same filters the browser panels offer —
|
|
162
|
+
`--type TYPE`, `--area AREA`, `--tag TAG`; each takes the ones orthogonal to
|
|
163
|
+
itself (`tags` can't filter by tag). Matching is case-insensitive and exact; a
|
|
164
|
+
concept at the bundle root lives in the `(root)` area, which `--area` also accepts
|
|
165
|
+
as plain `root` (no shell quoting). A filter that matches nothing is an empty view,
|
|
166
|
+
not an error: `okf tags <dir> --area billing --json` answers "which tags does the
|
|
167
|
+
billing area use?", `okf catalog <dir> --tag auth` answers "what carries the auth
|
|
168
|
+
tag?".
|
|
169
|
+
|
|
170
|
+
Reach for `stats` first to size a bundle, `catalog`/`files` to enumerate it, `tags`
|
|
171
|
+
to find thematic clusters — all without standing up the server.
|
|
172
|
+
|
|
173
|
+
## server — interactive graph server
|
|
174
|
+
|
|
175
|
+
Starts a local HTTP server (`okf server <dir>`; `-p`/`--port`, default 8808, and
|
|
176
|
+
`--bind`) and prints its URL — stop it with Ctrl-C. The page boots from a lean
|
|
177
|
+
payload (nodes carry only `id` and `title`, plus compact type/tag indexes) and
|
|
178
|
+
fetches each concept's markdown body **live from disk** as you click it, so the
|
|
179
|
+
initial load stays small and edits show without a restart. Concepts render as nodes
|
|
180
|
+
coloured by `type` and sized by degree, links as edges, with a detail panel
|
|
181
|
+
(rendered markdown, "Links to" / "Linked from" backlinks), layout switching,
|
|
182
|
+
type/area/tag filters on every view, and search. It is a Rack app, so the same
|
|
183
|
+
server can be mounted in a host app (e.g. Rails).
|
|
184
|
+
|
|
185
|
+
**Trust boundary:** the page loads Cytoscape and marked from a CDN and
|
|
186
|
+
renders each concept's markdown body **without sanitization**, so only serve
|
|
187
|
+
bundles you trust. Inlined graph data cannot break out of its `<script>` (every
|
|
188
|
+
`<` is escaped), but the fetched markdown is rendered unsanitized.
|
|
189
|
+
|
|
190
|
+
## graph — the raw structure
|
|
191
|
+
|
|
192
|
+
Prints the node/edge graph. `--json` emits a machine-readable dump (`nodes` with
|
|
193
|
+
`id`/`type`/`title`/`description`/`tags`, and `edges`) you can pipe into other
|
|
194
|
+
analysis or use to plan a traversal before consuming a large bundle. `--no-body`
|
|
195
|
+
drops each node's body; `--minimal` ships only `id`/`title` plus the type/tag
|
|
196
|
+
indexes — the lean shape the `server` page boots from.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: <Concept type, e.g. Service, BigQuery Table, Metric, Playbook, Decision>
|
|
3
|
+
title: <Human-readable display name>
|
|
4
|
+
description: <Single sentence summarizing the concept.>
|
|
5
|
+
resource: <Canonical URI of the underlying asset — omit for abstract concepts>
|
|
6
|
+
tags: [<tag>, <tag>]
|
|
7
|
+
timestamp: <ISO 8601, e.g. 2026-06-14T10:00:00Z>
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
<What this concept is and why it matters.>
|
|
13
|
+
|
|
14
|
+
# Schema
|
|
15
|
+
|
|
16
|
+
<Use for assets with fields/columns; otherwise replace with relevant sections.>
|
|
17
|
+
|
|
18
|
+
| Field | Type | Description |
|
|
19
|
+
|-------|------|-------------|
|
|
20
|
+
| | | |
|
|
21
|
+
|
|
22
|
+
# Citations
|
|
23
|
+
|
|
24
|
+
[1] [<source title>](<url>)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
okf_version: "0.1"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# <Directory / Group Heading>
|
|
6
|
+
|
|
7
|
+
* [<Title>](<relative-url>) - <short description from the concept's frontmatter>
|
|
8
|
+
* [<Title>](<relative-url>) - <short description>
|
|
9
|
+
|
|
10
|
+
# <Another Group>
|
|
11
|
+
|
|
12
|
+
* [<Subdirectory>](<subdir>/) - <short description of the subdirectory>
|
data/lib/okf/skill.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OKF
|
|
4
|
+
# Installs this gem's companion agent skill — the SKILL.md + reference/ +
|
|
5
|
+
# templates/ that teach an agent to author, maintain, and consume OKF bundles
|
|
6
|
+
# and to drive the `okf` executable — into a destination directory.
|
|
7
|
+
#
|
|
8
|
+
# The skill ships *inside* the gem (under lib/okf/skill), so `gem install okf`
|
|
9
|
+
# provides both the CLI and the skill that uses it, and the two can never drift
|
|
10
|
+
# apart in version: the skill's own reference/cli.md always matches the CLI it
|
|
11
|
+
# was released with.
|
|
12
|
+
class Skill
|
|
13
|
+
class Error < OKF::Error
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The canonical skill tree, bundled in the sibling skill/ directory.
|
|
17
|
+
ASSETS = File.expand_path("skill", __dir__)
|
|
18
|
+
|
|
19
|
+
# An agent discovers a skill as <skills-dir>/<name>/SKILL.md, so by default
|
|
20
|
+
# (nest: true) the tree lands in a skills/okf/ folder under the destination:
|
|
21
|
+
#
|
|
22
|
+
# okf skill .claude -> .claude/skills/okf (adds skills/ then okf/)
|
|
23
|
+
# okf skill .agents/skills -> .agents/skills/okf (already a skills dir)
|
|
24
|
+
# okf skill .../skills/okf -> .../skills/okf (already the skill dir)
|
|
25
|
+
#
|
|
26
|
+
# Point it at a project or skills directory and the skill settles in its own
|
|
27
|
+
# folder instead of splattering its files loose among the others. Pass
|
|
28
|
+
# nest: false (`--here`) to install straight into the destination, wherever it
|
|
29
|
+
# is.
|
|
30
|
+
NAME = "okf"
|
|
31
|
+
SKILLS_DIR = "skills"
|
|
32
|
+
|
|
33
|
+
def self.install(dest, force: false, nest: true)
|
|
34
|
+
new(dest, force: force, nest: nest).install
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :dest
|
|
38
|
+
|
|
39
|
+
def initialize(dest, force: false, nest: true)
|
|
40
|
+
@dest = nest ? resolve(dest.to_s) : dest.to_s
|
|
41
|
+
@path = File.expand_path(@dest)
|
|
42
|
+
@force = force
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Copy the skill tree into dest, creating it if needed. Refuses to write over
|
|
46
|
+
# a non-empty directory unless forced, so an existing (possibly customized)
|
|
47
|
+
# skill is never silently clobbered. Returns the relative paths written.
|
|
48
|
+
def install
|
|
49
|
+
if File.exist?(@path) && !File.directory?(@path)
|
|
50
|
+
raise Error, "destination #{@dest} exists and is not a directory"
|
|
51
|
+
end
|
|
52
|
+
if File.directory?(@path) && !Dir.empty?(@path) && !@force
|
|
53
|
+
raise Error, "destination #{@dest} is not empty (pass --force to overwrite)"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
FileUtils.mkdir_p(@path)
|
|
57
|
+
FileUtils.cp_r(File.join(ASSETS, "."), @path)
|
|
58
|
+
files
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Relative paths of every file in the bundled skill, sorted for stable output.
|
|
62
|
+
def files
|
|
63
|
+
Dir.glob(File.join(ASSETS, "**", "*"))
|
|
64
|
+
.select { |path| File.file?(path) }
|
|
65
|
+
.map { |path| path[(ASSETS.length + 1)..-1] }
|
|
66
|
+
.sort
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Resolve the destination to a skills/okf leaf, so the skill always sits in its
|
|
72
|
+
# own folder under whatever the user pointed at. A destination already named
|
|
73
|
+
# okf is the skill dir itself (idempotent); one named skills only needs okf/.
|
|
74
|
+
def resolve(dest)
|
|
75
|
+
case File.basename(dest)
|
|
76
|
+
when NAME then dest
|
|
77
|
+
when SKILLS_DIR then File.join(dest, NAME)
|
|
78
|
+
else File.join(dest, SKILLS_DIR, NAME)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/okf/version.rb
ADDED
data/lib/okf.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "date"
|
|
6
|
+
require "json"
|
|
7
|
+
require "optparse"
|
|
8
|
+
require "pathname"
|
|
9
|
+
require "securerandom"
|
|
10
|
+
require "set"
|
|
11
|
+
require "time"
|
|
12
|
+
require "yaml"
|
|
13
|
+
|
|
14
|
+
module OKF
|
|
15
|
+
class Error < StandardError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Blank in the frontmatter sense: nil, false, an empty/whitespace-only string,
|
|
19
|
+
# or an empty collection. Numbers and other scalars are never blank. The one
|
|
20
|
+
# domain-wide predicate behind "non-empty type" (§9.2) and the recommended-field
|
|
21
|
+
# warnings, kept here so the gem needs no ActiveSupport.
|
|
22
|
+
def self.blank?(value)
|
|
23
|
+
return true if value.nil? || value == false
|
|
24
|
+
return value.strip.empty? if value.is_a?(String)
|
|
25
|
+
|
|
26
|
+
value.respond_to?(:empty?) ? value.empty? : false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
require "okf/version"
|
|
30
|
+
|
|
31
|
+
# ── kernel: cross-cutting primitives ──
|
|
32
|
+
require "okf/path"
|
|
33
|
+
|
|
34
|
+
# ── Markdown: parse structure out of a markdown document (§4/§5/§8) ──
|
|
35
|
+
require "okf/markdown/frontmatter"
|
|
36
|
+
require "okf/markdown/links"
|
|
37
|
+
require "okf/markdown/citations"
|
|
38
|
+
|
|
39
|
+
# ── domain: pure representations + analyzers (no disk, no CLI) ──
|
|
40
|
+
require "okf/concept"
|
|
41
|
+
require "okf/bundle"
|
|
42
|
+
require "okf/bundle/graph"
|
|
43
|
+
require "okf/bundle/validator"
|
|
44
|
+
require "okf/bundle/validator/result"
|
|
45
|
+
require "okf/bundle/linter"
|
|
46
|
+
require "okf/bundle/linter/report"
|
|
47
|
+
|
|
48
|
+
# ── shell: everything that touches the outside world ──
|
|
49
|
+
require "okf/concept/file"
|
|
50
|
+
require "okf/bundle/reader"
|
|
51
|
+
require "okf/bundle/writer"
|
|
52
|
+
require "okf/bundle/folder"
|
|
53
|
+
require "okf/skill"
|
|
54
|
+
require "okf/cli"
|
|
55
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: okf
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rodrigo Serradura
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rack
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: webrick
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.4'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.4'
|
|
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).
|
|
46
|
+
email:
|
|
47
|
+
- rodrigo.serradura@gmail.com
|
|
48
|
+
executables:
|
|
49
|
+
- okf
|
|
50
|
+
extensions: []
|
|
51
|
+
extra_rdoc_files: []
|
|
52
|
+
files:
|
|
53
|
+
- ".okf/capabilities/agent-skill.md"
|
|
54
|
+
- ".okf/capabilities/graph-server.md"
|
|
55
|
+
- ".okf/capabilities/index.md"
|
|
56
|
+
- ".okf/capabilities/library-api.md"
|
|
57
|
+
- ".okf/capabilities/linter.md"
|
|
58
|
+
- ".okf/capabilities/read-views.md"
|
|
59
|
+
- ".okf/capabilities/validator.md"
|
|
60
|
+
- ".okf/cli.md"
|
|
61
|
+
- ".okf/design/core-shell-split.md"
|
|
62
|
+
- ".okf/design/index.md"
|
|
63
|
+
- ".okf/design/ruby-floor.md"
|
|
64
|
+
- ".okf/design/runtime-dependencies.md"
|
|
65
|
+
- ".okf/design/server-trust-boundary.md"
|
|
66
|
+
- ".okf/format/citations.md"
|
|
67
|
+
- ".okf/format/cross-links.md"
|
|
68
|
+
- ".okf/format/frontmatter.md"
|
|
69
|
+
- ".okf/format/index.md"
|
|
70
|
+
- ".okf/format/okf-format.md"
|
|
71
|
+
- ".okf/index.md"
|
|
72
|
+
- ".okf/log.md"
|
|
73
|
+
- ".okf/model/bundle.md"
|
|
74
|
+
- ".okf/model/concept.md"
|
|
75
|
+
- ".okf/model/graph.md"
|
|
76
|
+
- ".okf/model/index.md"
|
|
77
|
+
- ".okf/overview.md"
|
|
78
|
+
- CHANGELOG.md
|
|
79
|
+
- CODE_OF_CONDUCT.md
|
|
80
|
+
- LICENSE.txt
|
|
81
|
+
- NOTICE
|
|
82
|
+
- README.md
|
|
83
|
+
- exe/okf
|
|
84
|
+
- lib/okf.rb
|
|
85
|
+
- lib/okf/bundle.rb
|
|
86
|
+
- lib/okf/bundle/folder.rb
|
|
87
|
+
- lib/okf/bundle/graph.rb
|
|
88
|
+
- lib/okf/bundle/linter.rb
|
|
89
|
+
- lib/okf/bundle/linter/report.rb
|
|
90
|
+
- lib/okf/bundle/reader.rb
|
|
91
|
+
- lib/okf/bundle/validator.rb
|
|
92
|
+
- lib/okf/bundle/validator/result.rb
|
|
93
|
+
- lib/okf/bundle/writer.rb
|
|
94
|
+
- lib/okf/cli.rb
|
|
95
|
+
- lib/okf/concept.rb
|
|
96
|
+
- lib/okf/concept/file.rb
|
|
97
|
+
- lib/okf/markdown/citations.rb
|
|
98
|
+
- lib/okf/markdown/frontmatter.rb
|
|
99
|
+
- lib/okf/markdown/links.rb
|
|
100
|
+
- lib/okf/path.rb
|
|
101
|
+
- lib/okf/server/app.rb
|
|
102
|
+
- lib/okf/server/graph.rb
|
|
103
|
+
- lib/okf/server/runner.rb
|
|
104
|
+
- lib/okf/server/templates/graph.html.erb
|
|
105
|
+
- lib/okf/skill.rb
|
|
106
|
+
- lib/okf/skill/SKILL.md
|
|
107
|
+
- lib/okf/skill/reference/APACHE-2.0.txt
|
|
108
|
+
- lib/okf/skill/reference/SPEC.md
|
|
109
|
+
- lib/okf/skill/reference/authoring.md
|
|
110
|
+
- lib/okf/skill/reference/cli.md
|
|
111
|
+
- lib/okf/skill/templates/concept.md
|
|
112
|
+
- lib/okf/skill/templates/index.md
|
|
113
|
+
- lib/okf/skill/templates/log.md
|
|
114
|
+
- lib/okf/skill/templates/root-index.md
|
|
115
|
+
- lib/okf/version.rb
|
|
116
|
+
homepage: https://github.com/serradura/okf-gem
|
|
117
|
+
licenses:
|
|
118
|
+
- Apache-2.0
|
|
119
|
+
metadata:
|
|
120
|
+
allowed_push_host: https://rubygems.org
|
|
121
|
+
homepage_uri: https://github.com/serradura/okf-gem
|
|
122
|
+
source_code_uri: https://github.com/serradura/okf-gem
|
|
123
|
+
changelog_uri: https://github.com/serradura/okf-gem/blob/main/CHANGELOG.md
|
|
124
|
+
rubygems_mfa_required: 'true'
|
|
125
|
+
rdoc_options: []
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: 2.4.0
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
requirements: []
|
|
139
|
+
rubygems_version: 4.0.16
|
|
140
|
+
specification_version: 4
|
|
141
|
+
summary: Read, validate, and serve Open Knowledge Format (OKF) v0.1 bundles.
|
|
142
|
+
test_files: []
|