From aa1d5ab6bb861ab0b9fd23e95609f7df7e836847 Mon Sep 17 00:00:00 2001 From: Duyet Le Date: Sun, 14 Jun 2026 13:18:55 +0700 Subject: [PATCH] feat(okf): add Open Knowledge Format plugin with author and refactor skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the `okf` plugin for the Open Knowledge Format (OKF) v0.1 — a vendor-neutral format representing knowledge as markdown files with YAML frontmatter. - skills/okf: author, init, and validate OKF bundles; bundles the OKF v0.1 SPEC.md as an offline reference and a stdlib-only validate_okf.py. - skills/okf-refactor: convert existing notes/docs/catalog exports into a conformant, cross-linked OKF bundle. - Register in Claude and Codex marketplace manifests. Co-Authored-By: duyetbot --- .agents/plugins/marketplace.json | 12 ++ .claude-plugin/marketplace.json | 5 + marketplace.json | 8 + okf/.claude-plugin/plugin.json | 12 ++ okf/.codex-plugin/plugin.json | 19 +++ okf/README.md | 61 +++++++ okf/skills/okf-refactor/SKILL.md | 104 ++++++++++++ okf/skills/okf/SKILL.md | 155 ++++++++++++++++++ okf/skills/okf/reference/SPEC.md | 218 +++++++++++++++++++++++++ okf/skills/okf/scripts/validate_okf.py | 104 ++++++++++++ 10 files changed, 698 insertions(+) create mode 100644 okf/.claude-plugin/plugin.json create mode 100644 okf/.codex-plugin/plugin.json create mode 100644 okf/README.md create mode 100644 okf/skills/okf-refactor/SKILL.md create mode 100644 okf/skills/okf/SKILL.md create mode 100644 okf/skills/okf/reference/SPEC.md create mode 100644 okf/skills/okf/scripts/validate_okf.py diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index e47e687..689d720 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -267,6 +267,18 @@ "authentication": "ON_INSTALL" }, "category": "Productivity" + }, + { + "name": "okf", + "source": { + "source": "local", + "path": "./okf" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" } ] } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ef285ac..7209ae2 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -108,6 +108,11 @@ "name": "kb", "source": "./kb", "description": "Knowledge base maintenance skills. Consolidate, dedupe, prune, refresh, and forget notes in a markdown KB." + }, + { + "name": "okf", + "source": "./okf", + "description": "Open Knowledge Format (OKF) tooling. Author, initialize, validate, and refactor knowledge into vendor-neutral OKF v0.1 bundles: markdown files with YAML frontmatter." } ] } diff --git a/marketplace.json b/marketplace.json index fd75bb5..7f01520 100644 --- a/marketplace.json +++ b/marketplace.json @@ -186,6 +186,14 @@ "version": "1.0.0", "type": "skill", "category": "productivity" + }, + { + "name": "okf", + "id": "okf@duyet-claude-plugins", + "description": "Open Knowledge Format (OKF) tooling. Author, initialize, validate, and refactor knowledge into vendor-neutral OKF v0.1 bundles: markdown files with YAML frontmatter.", + "version": "1.0.0", + "type": "skill", + "category": "productivity" } ], "metadata": { diff --git a/okf/.claude-plugin/plugin.json b/okf/.claude-plugin/plugin.json new file mode 100644 index 0000000..11d2395 --- /dev/null +++ b/okf/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "okf", + "version": "1.0.0", + "description": "Open Knowledge Format (OKF) tooling. Author, initialize, validate, and refactor knowledge into vendor-neutral OKF v0.1 bundles: markdown files with YAML frontmatter.", + "author": { + "name": "duyet", + "url": "https://github.com/duyet" + }, + "homepage": "https://github.com/duyet/codex-claude-plugins", + "repository": "https://github.com/duyet/codex-claude-plugins", + "license": "MIT" +} diff --git a/okf/.codex-plugin/plugin.json b/okf/.codex-plugin/plugin.json new file mode 100644 index 0000000..4445e38 --- /dev/null +++ b/okf/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "okf", + "version": "1.0.0", + "description": "Open Knowledge Format (OKF) tooling. Author, initialize, validate, and refactor knowledge into vendor-neutral OKF v0.1 bundles: markdown files with YAML frontmatter.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Open Knowledge Format", + "shortDescription": "Author, validate, and refactor knowledge into OKF v0.1 bundles (markdown + YAML frontmatter).", + "developerName": "duyet", + "category": "productivity", + "capabilities": ["Skill"], + "links": { + "homepage": "https://github.com/duyet/codex-claude-plugins" + } + } +} diff --git a/okf/README.md b/okf/README.md new file mode 100644 index 0000000..06202d8 --- /dev/null +++ b/okf/README.md @@ -0,0 +1,61 @@ +# okf + +Tooling for the **Open Knowledge Format (OKF)** — a vendor-neutral way to represent +knowledge as plain markdown files with YAML frontmatter, organized in a directory +hierarchy. A bundle is just a directory: version-controllable, portable, lock-in +free, and readable by humans, LLMs, and any tool that speaks markdown (Obsidian, +Notion, MkDocs, a static file server, a search index, a graph viewer). + +Spec: [OKF v0.1](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) +— bundled verbatim at `skills/okf/reference/SPEC.md` so the agent is grounded +offline. + +## Skills + +- **okf** — author, initialize, and validate OKF bundles. Scaffolds the correct + layout, writes concept docs with proper frontmatter, generates `index.md` / + `log.md`, and ships `validate_okf.py` for OKF v0.1 conformance checks. +- **okf-refactor** — convert existing knowledge (loose notes, wikis, README/docs, + a catalog/metadata export, a database schema) into a conformant OKF bundle: + one concept per file, cross-linked into a knowledge graph, with generated indexes. + +## Install + +```bash +/plugin marketplace add duyet/codex-claude-plugins +/plugin install okf@duyet-claude-plugins +``` + +Alternative ([skills.sh](https://skills.sh)): + +```bash +npx skills add duyet/codex-claude-plugins +``` + +## Usage + +```text +# Initialize a fresh bundle +okf init an OKF bundle at ./bundles/my_catalog + +# Author a concept +okf add a BigQuery Table concept for the orders table + +# Validate conformance +python3 skills/okf/scripts/validate_okf.py ./bundles/my_catalog + +# Refactor existing knowledge into OKF +okf-refactor convert ./notes into an OKF bundle at ./bundles/notes +``` + +## What conformance means (OKF v0.1) + +1. Every non-reserved `.md` file has parseable YAML frontmatter. +2. Every frontmatter block has a non-empty `type`. +3. Reserved files (`index.md`, `log.md`) follow their structure and carry no + frontmatter — except the bundle-root `index.md`, which may declare + `okf_version: "0.1"`. + +Consumers are required to tolerate unknown types, unknown keys, broken links, and +missing indexes — so producing OKF is forgiving, and these skills aim to produce +the disciplined, well-linked end of that spectrum. diff --git a/okf/skills/okf-refactor/SKILL.md b/okf/skills/okf-refactor/SKILL.md new file mode 100644 index 0000000..bf812ab --- /dev/null +++ b/okf/skills/okf-refactor/SKILL.md @@ -0,0 +1,104 @@ +--- +name: okf-refactor +description: Refactor existing knowledge into an Open Knowledge Format (OKF) bundle — convert loose notes, docs, READMEs, wikis, or a catalog/metadata export into markdown concept files with YAML frontmatter, index.md, and a knowledge graph. Use when migrating existing knowledge to OKF, normalizing a notes folder, or exporting a catalog to OKF. +--- + +# okf-refactor — convert existing knowledge into an OKF bundle + +## Goal + +Take knowledge that already exists in some other shape — a folder of loose +markdown notes, a wiki, README/docs, a data-catalog export, a database schema, a +JSON/CSV metadata dump — and emit a conformant **OKF v0.1** bundle: a directory of +one-concept-per-file markdown docs with YAML frontmatter, navigable `index.md` +files, and a cross-linked knowledge graph. + +This skill depends on the **okf** skill for the format itself. Read +`../okf/reference/SPEC.md` (the bundled OKF v0.1 spec) before refactoring — it is +the source of truth. Use `../okf/scripts/validate_okf.py` to check the result. + +## Inputs and outputs + +- **Input** (``): a directory, file set, or export to read FROM. Treat it as + immutable ground truth — never edit the source in place. +- **Output** (``): the bundle directory to write TO. New, separate from ``. + +Confirm both paths before writing. If `` is omitted, default to +`./bundles/` and state the choice. + +## The refactor pass (run in order; plan before writing) + +1. **Survey the source.** Inventory every item of knowledge and its natural + boundaries. Identify what each "thing" is (a table, an endpoint, a concept, a + runbook) — that becomes a concept's `type`. Note relationships between things + (foreign keys, "see also", parent/child) — those become cross-links. + +2. **Design the taxonomy = the directory layout.** Group concepts into top-level + directories by `type` or domain (`tables/`, `datasets/`, `references/`, + `concepts/`, `playbooks/`…). Start flat; nest only when a level earns it. + Directories are the coarse taxonomy; frontmatter `tags` carry cross-cutting + facets. + +3. **Map each source item to one concept doc.** Atomic — one concept per file, + `/.md`, kebab-case stable slug. Never pack two concepts into one + file; split multi-topic source notes. + +4. **Write frontmatter for each concept.** Always set a non-empty `type`. Derive + `title` and `description` (one line — this is what indexes show). Set + `resource` to the canonical URI when the item has one. Map source metadata to + `tags` and `timestamp`. Preserve any source-specific metadata as extra + frontmatter keys (consumers tolerate unknown keys) rather than dropping it. + +5. **Write the body.** Move structured fields under `# Schema` (prefer a table), + runnable usage under `# Examples`, and sources under `# Citations`. Keep prose + tight; favor structural markdown. Do not invent facts not present in the source. + +6. **Wire the graph.** Convert every relationship into an inline markdown link + using **absolute bundle-relative** paths (`[orders](/tables/orders.md)`). + Ensure no concept is fully orphaned — each should link, or be linked by, at + least one other where a real relationship exists. + +7. **Mint reference docs.** External authoritative sources cited by many concepts + become standalone `references/.md` concept docs (with their own `type`, + e.g. `Reference`), linked from `# Citations`. + +8. **Generate indexes.** Write an `index.md` per navigable directory (no + frontmatter; group under `#` sections; copy each bullet's description from the + linked concept). Write the bundle-root `index.md` with `okf_version: "0.1"` + frontmatter linking the top-level groups. + +9. **Seed `log.md`.** At the root, record an `## ` entry noting the + `* **Initialization**: Refactored from .` Use the real current date. + +10. **Validate.** Run `validate_okf.py `. Fix every error. Broken + cross-links are warnings (the spec tolerates them) — resolve the ones you can, + report the rest. + +## Fidelity rules + +- **Lossless of meaning.** Restructuring may drop redundant words, never facts. + If the source has metadata with no obvious home, keep it as a custom frontmatter + key rather than discarding it. +- **No invention.** Don't fabricate descriptions, schemas, or links the source + doesn't support. Mark genuine gaps plainly instead of guessing. +- **Source stays immutable.** All writes land in ``; `` is read-only. +- **Idempotent-ish.** Re-running against the same source should converge on the + same bundle, not accumulate duplicates. + +## Report + +After the pass, summarize: number of concepts emitted (by `type`/group), index and +reference docs created, cross-links wired, and any unresolved gaps or broken links +the source left dangling. Then show the bundle tree. + +## Checklist + +- [ ] Confirmed `` (read-only) and `` (new) paths. +- [ ] Read ../okf/reference/SPEC.md before refactoring. +- [ ] One concept per file; multi-topic notes split; stable kebab-case slugs. +- [ ] Every concept has a non-empty `type`, plus `title` + `description`. +- [ ] Relationships wired as absolute bundle-relative links; no true orphans. +- [ ] Root index.md has `okf_version: "0.1"`; per-directory indexes generated. +- [ ] log.md seeded with the initialization entry (real date). +- [ ] validate_okf.py run; errors fixed; remaining broken links reported. +- [ ] No facts invented; source-specific metadata preserved as custom keys. diff --git a/okf/skills/okf/SKILL.md b/okf/skills/okf/SKILL.md new file mode 100644 index 0000000..1f9bc51 --- /dev/null +++ b/okf/skills/okf/SKILL.md @@ -0,0 +1,155 @@ +--- +name: okf +description: Author, initialize, and validate Open Knowledge Format (OKF) bundles — vendor-neutral knowledge as markdown files with YAML frontmatter. Use when creating an OKF bundle, scaffolding a knowledge catalog, writing OKF concept docs, building index.md/log.md files, or checking a bundle for OKF v0.1 conformance. +--- + +# okf — author and validate Open Knowledge Format bundles + +## What OKF is + +Open Knowledge Format (OKF) is a vendor-neutral format for representing knowledge +as plain markdown files with YAML frontmatter, organized in a directory hierarchy. +It is not tied to any agent, framework, model provider, or serving system. A +bundle is just a directory: version-controllable, portable, lock-in free, readable +by humans and LLMs alike, and consumable by anything that reads markdown (Obsidian, +Notion, MkDocs, a static file server, a search index, a graph viewer). + +**Read [reference/SPEC.md](reference/SPEC.md) first** — it is the full OKF v0.1 +spec, bundled so you can ground every decision in it without network access. When +the spec and this skill disagree, the spec wins. + +## When to use + +- Initialize a new, empty OKF bundle with the correct layout. +- Author or edit OKF concept documents (one concept per `.md` file). +- Generate or refresh `index.md` (progressive disclosure) and `log.md` (history). +- Validate an existing bundle for OKF v0.1 conformance. + +To convert an existing pile of notes / a catalog export into an OKF bundle, use +the companion **okf-refactor** skill instead. + +## The non-negotiable rules (from the spec) + +1. Every non-reserved `.md` file MUST have parseable YAML frontmatter. +2. Every frontmatter block MUST include a non-empty `type`. +3. Reserved filenames — `index.md`, `log.md` — follow their special structure and + carry NO frontmatter (the sole exception: the bundle-root `index.md` may carry + `okf_version`). +4. Cross-links are normal markdown links; prefer **absolute bundle-relative** + paths (`/tables/orders.md`) over relative ones so links survive file moves. +5. Be a disciplined producer: emit `title`, `description`, and (for assets) + `resource` even though only `type` is strictly required — consumers index on them. + +## Initialize a new bundle + +When asked to init/scaffold an OKF bundle at ``: + +1. Confirm `` (ask if not given). Create the root directory. +2. Decide the top-level grouping from the domain — directories ARE the coarse + taxonomy (e.g. `datasets/`, `tables/`, `references/`, or `concepts/`, + `playbooks/`, …). Don't over-structure; start flat, nest only when a level + earns it. +3. Write a bundle-root `index.md` that declares the version and links the groups: + + ```markdown + --- + okf_version: "0.1" + --- + + # + + * [Datasets](datasets/) - + * [Tables](tables/) - + ``` + +4. Add a `references/` directory only if you expect standalone citation docs. +5. Optionally seed a root `log.md` with an `## ` / `* **Initialization**` + entry. Use the real current date. + +## Author a concept document + +One concept per file, `/.md`. Slug is kebab-case, stable, matches the +asset name where possible. + +```markdown +--- +type: # REQUIRED — e.g. "BigQuery Table", "API Endpoint", "Playbook" +title: # strongly recommended +description: # strongly recommended — used in indexes/previews +resource: # for assets that have one +tags: [, ] # optional, cross-cutting +timestamp: # optional, last meaningful change +--- + +# Schema (when the concept has structured fields — prefer a table) + +| Column | Type | Description | +|--------|------|-------------| +| ... | ... | FK to [other](/group/other.md) | + +# Examples (concrete usage in code blocks) + +# Citations (external sources) + +[1] [Title](https://example.com) +``` + +Guidance: +- Prefer structural markdown (tables, lists, code blocks) over prose paragraphs. +- Express relationships by **linking** related concepts inline in the prose; the + link is an undirected edge in the knowledge graph. +- Add producer-specific frontmatter keys freely — consumers preserve unknowns. + +## Generate / refresh `index.md` + +For any directory worth navigating, write an `index.md` with NO frontmatter +(except the root, which keeps `okf_version`). Group concepts under `#` sections; +pull each bullet's description from the linked concept's `description`: + +```markdown +# Tables + +* [Orders](orders.md) - one row per completed customer order +* [Customers](customers.md) - one row per customer +``` + +Use relative links inside an index (they sit beside their targets). Regenerate the +index whenever concepts are added, removed, or re-described. + +## Maintain `log.md` + +Append directory-level history, newest first, ISO dates: + +```markdown +# Directory Update Log + +## 2026-06-14 +* **Creation**: Added orders and customers tables. +``` + +## Validate a bundle for OKF v0.1 conformance + +Run `scripts/validate_okf.py ` (bundled with this skill) for a mechanical +check, then eyeball the report. It verifies: + +- Every non-reserved `.md` has parseable frontmatter with a non-empty `type`. +- `index.md` / `log.md` carry no frontmatter (root `index.md` may carry only + `okf_version`). +- Reports broken bundle-relative cross-links as warnings (broken links are + *tolerated* by the spec — surface them, don't fail on them). + +```bash +python3 ${CLAUDE_PLUGIN_ROOT}/skills/okf/scripts/validate_okf.py ./bundles/my_bundle +``` + +(Outside a plugin runtime, the script lives next to this SKILL.md under `scripts/`.) + +## Checklist + +- [ ] Read reference/SPEC.md before authoring. +- [ ] Every concept file has frontmatter with a non-empty `type`. +- [ ] `title` + `description` present on concepts; `resource` on assets. +- [ ] Cross-links use absolute bundle-relative paths in concept bodies. +- [ ] Root `index.md` declares `okf_version: "0.1"`; other index/log files have no frontmatter. +- [ ] Indexes regenerated to match current concepts; descriptions copied from concepts. +- [ ] Ran validate_okf.py; resolved errors (broken links are warnings, not errors). diff --git a/okf/skills/okf/reference/SPEC.md b/okf/skills/okf/reference/SPEC.md new file mode 100644 index 0000000..264a8f7 --- /dev/null +++ b/okf/skills/okf/reference/SPEC.md @@ -0,0 +1,218 @@ +# Open Knowledge Format (OKF) v0.1 — Specification + +> Vendor-neutral format for representing knowledge as plain markdown files with +> YAML frontmatter. Not tied to any agent, framework, model provider, or serving +> system. Anyone can produce it (humans, agents, export pipelines, scripts); +> anyone can consume it (file servers, Obsidian/Notion/MkDocs, LLMs, search +> indexes, graph viewers). +> +> Source: https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md + +## Core principles + +- **Human- and agent-readable** — no SDK or query language between reader and content. +- **Version-controllable** — bundles live in git; diffs, blame, review just work. +- **Portable, lock-in free** — a bundle is a directory; tarball it, host it anywhere. +- **Structured + unstructured deliberately** — frontmatter for the few fields you + query/filter/index on; markdown body for prose, schemas, examples. +- **Minimally opinionated, freely extensible** — a small set of required keys for + interoperability; arbitrary extra frontmatter keys and body sections are allowed. +- **Progressive disclosure** — `index.md` files let a reader navigate one level at + a time instead of loading the whole bundle. +- **Graph-shaped** — concepts link to each other via normal markdown links, + expressing relationships richer than the directory tree. + +## Bundle structure + +A knowledge bundle is a hierarchical directory of markdown files: + +``` +bundle_root/ +├── index.md (optional; only place frontmatter is allowed in an index) +├── log.md (optional) +├── concept.md +└── subdirectory/ + ├── index.md + ├── concept.md + └── nested_subdirectory/ +``` + +**Reserved filenames:** +- `index.md` — progressive-disclosure directory listings. +- `log.md` — chronological update history. + +Every other `.md` file represents one concept. + +## Concept document + +Two sections: required YAML frontmatter, then a markdown body. + +### Frontmatter + +```yaml +--- +type: # REQUIRED — non-empty string +title: # Recommended +description: # Recommended +resource: # Recommended for assets +tags: [, ] # Optional +timestamp: # Optional +# Producers may add arbitrary custom keys. +--- +``` + +- **`type` (REQUIRED)** — descriptive string identifying the concept category + (e.g. `BigQuery Table`, `API Endpoint`, `Playbook`). Types are not centrally + registered; consumers must tolerate unknown types. +- **`title`** — display name; consumers may derive from filename if omitted. +- **`description`** — single-sentence summary for previews and indexes. +- **`resource`** — URI uniquely identifying the underlying asset. +- **`tags`** — YAML list for cross-cutting categorization. +- **`timestamp`** — last meaningful change, ISO 8601. + +Producers may add custom key/value pairs; consumers must preserve unknown fields +on round-trips. + +### Body + +Standard markdown. Conventional section headings (prefer structural markdown — +tables, lists, code blocks — over prose): + +| Section | Purpose | +|---------------|-----------------------------------------------| +| `# Schema` | Structured description of columns/fields. | +| `# Examples` | Concrete usage examples in code blocks. | +| `# Citations` | External sources supporting claims. | + +## Cross-linking + +- **Absolute (bundle-relative) — recommended:** `[customers](/tables/customers.md)` + — stable when documents move within subdirectories. +- **Relative — standard markdown:** `[neighbor](./other.md)`. + +Links assert relationships conveyed by the surrounding prose, not by link syntax. +Consumers treat all links as undirected edges; broken links are tolerated as +incomplete knowledge. + +## Index files (`index.md`) + +Optional, support progressive disclosure. Sections group concepts. No frontmatter +(except the bundle-root `index.md`, which may carry `okf_version`): + +```markdown +# Section Heading + +* [Title](relative-url) - description from the linked concept +* [Subdirectory](subdir/) - group description + +# Another Section + +* [Title](url) - description +``` + +Producers may generate them automatically; consumers may synthesize dynamically. + +## Log files (`log.md`) + +Optional, record directory-level history as date-grouped entries, newest first. +Date headings must use ISO 8601 `YYYY-MM-DD`. Leading bold words are conventional: + +```markdown +# Directory Update Log + +## 2026-05-22 +* **Update**: Description of change +* **Creation**: Description of new content + +## 2026-05-15 +* **Initialization**: Foundational changes +``` + +## Citations + +External sources supporting concept claims go under `# Citations`. Links may be +absolute URLs, bundle-relative paths, or paths into a `references/` subdirectory: + +```markdown +# Citations + +[1] [Title](https://absolute-url) +[2] [Title](/bundle-relative-path) +[3] [Title](references/local-reference.md) +``` + +## Conformance + +A bundle conforms to OKF v0.1 if: + +1. Every non-reserved `.md` file contains parseable YAML frontmatter. +2. Every frontmatter block includes a non-empty `type` field. +3. Reserved filenames (`index.md`, `log.md`) follow the structure above when present. + +Consumers must gracefully handle: missing optional fields, unknown `type` values, +unknown frontmatter keys, broken cross-links, and absent index files. This +permissive model supports growth, refactoring, and partial agent generation. + +## Versioning + +A bundle may declare its target version with `okf_version: "0.1"` in the +**bundle-root `index.md` frontmatter** — the only place frontmatter appears in an +index file. + +- **Minor bumps:** backward-compatible additions (new optional fields, headings). +- **Major bumps:** breaking changes (field renames, reserved-filename changes). + +Best-effort consumption is preferred over rejection. + +## Minimal example + +``` +my_bundle/ +├── index.md +├── datasets/ +│ ├── index.md +│ └── sales.md +└── tables/ + ├── index.md + ├── orders.md + └── customers.md +``` + +`datasets/sales.md`: + +```markdown +--- +type: BigQuery Dataset +title: Sales +description: All sales-related tables for the retail business. +resource: https://console.cloud.google.com/bigquery?p=acme&d=sales +tags: [sales] +timestamp: 2026-05-28T00:00:00Z +--- + +The sales dataset contains transactional tables, including +[orders](/tables/orders.md) and [customers](/tables/customers.md). +``` + +`tables/orders.md`: + +```markdown +--- +type: BigQuery Table +title: Orders +description: One row per completed customer order. +resource: https://console.cloud.google.com/bigquery?p=acme&d=sales&t=orders +tags: [sales, orders] +timestamp: 2026-05-28T00:00:00Z +--- + +# Schema + +| Column | Type | Description | +|---------------|---------|------------------------------------------| +| `order_id` | STRING | Unique order identifier. | +| `customer_id` | STRING | FK to [customers](/tables/customers.md). | +| `total_usd` | NUMERIC | Order total in USD. | + +Part of the [sales dataset](/datasets/sales.md). +``` diff --git a/okf/skills/okf/scripts/validate_okf.py b/okf/skills/okf/scripts/validate_okf.py new file mode 100644 index 0000000..8552705 --- /dev/null +++ b/okf/skills/okf/scripts/validate_okf.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Validate an OKF v0.1 bundle. Stdlib only — no PyYAML required. + +Usage: python3 validate_okf.py + +Exit code 0 if conformant (broken cross-links are warnings, not failures), +1 if any conformance error is found. +""" +import os +import re +import sys + +RESERVED = {"index.md", "log.md"} + + +def split_frontmatter(text): + """Return (frontmatter_str_or_None, has_delimited_block).""" + if not text.startswith("---"): + return None, False + m = re.match(r"^---\s*\n(.*?)\n---\s*(?:\n|$)", text, re.DOTALL) + if not m: + return None, False + return m.group(1), True + + +def frontmatter_keys(fm): + """Crude top-level key scan — enough for conformance checks.""" + keys = {} + for line in fm.splitlines(): + m = re.match(r"^([A-Za-z_][\w-]*):\s*(.*)$", line) + if m: + keys[m.group(1)] = m.group(2).strip() + return keys + + +def main(): + if len(sys.argv) != 2: + print("usage: validate_okf.py ", file=sys.stderr) + return 2 + root = os.path.abspath(sys.argv[1]) + if not os.path.isdir(root): + print(f"error: not a directory: {root}", file=sys.stderr) + return 2 + + errors, warnings = [], [] + concept_count = 0 + + for dirpath, _dirs, files in os.walk(root): + for name in files: + if not name.endswith(".md"): + continue + path = os.path.join(dirpath, name) + rel = os.path.relpath(path, root) + with open(path, encoding="utf-8") as f: + text = f.read() + fm, delimited = split_frontmatter(text) + is_root_index = (rel == "index.md") + + if name in RESERVED: + # Reserved files carry no frontmatter, except root index.md may + # carry only okf_version. + if delimited: + keys = frontmatter_keys(fm) + if is_root_index and set(keys) <= {"okf_version"}: + pass + else: + errors.append( + f"{rel}: reserved file must not carry frontmatter" + + (" (root index.md may carry only okf_version)" + if is_root_index else "") + ) + continue + + # Non-reserved concept file. + concept_count += 1 + if not delimited: + errors.append(f"{rel}: missing parseable YAML frontmatter block") + continue + keys = frontmatter_keys(fm) + if not keys.get("type"): + errors.append(f"{rel}: frontmatter missing non-empty 'type'") + + # Warn on broken absolute (bundle-relative) cross-links. + for target in re.findall(r"\]\((/[^)\s]+\.md)\)", text): + dest = os.path.join(root, target.lstrip("/")) + if not os.path.isfile(dest): + warnings.append(f"{rel}: broken link -> {target}") + + for w in warnings: + print(f"warning: {w}") + for e in errors: + print(f"error: {e}") + + print(f"\nchecked {concept_count} concept doc(s): " + f"{len(errors)} error(s), {len(warnings)} warning(s)") + if errors: + print("NOT conformant with OKF v0.1") + return 1 + print("conformant with OKF v0.1") + return 0 + + +if __name__ == "__main__": + sys.exit(main())