okf-cli 0.1.1__tar.gz → 0.3.1__tar.gz
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.
- okf_cli-0.3.1/.github/workflows/test.yml +17 -0
- okf_cli-0.3.1/AGENTS.md +163 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/PKG-INFO +111 -35
- okf_cli-0.3.1/README.md +201 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/README.md +2 -2
- okf_cli-0.3.1/example/loose/deep/no-heading.md +4 -0
- okf_cli-0.3.1/example/loose/no-desc.md +6 -0
- okf_cli-0.3.1/example/loose/no-title.md +5 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/pyproject.toml +10 -4
- okf_cli-0.3.1/src/okf/cli.py +45 -0
- okf_cli-0.3.1/src/okf/commands/__init__.py +0 -0
- okf_cli-0.1.1/src/okf/cli.py → okf_cli-0.3.1/src/okf/commands/bundle.py +24 -91
- okf_cli-0.3.1/src/okf/commands/list.py +41 -0
- okf_cli-0.3.1/src/okf/commands/show.py +53 -0
- okf_cli-0.3.1/src/okf/commands/validate.py +47 -0
- okf_cli-0.3.1/src/okf/core.py +185 -0
- okf_cli-0.3.1/tests/test_cli.py +825 -0
- okf_cli-0.3.1/uv.lock +238 -0
- okf_cli-0.1.1/README.md +0 -126
- okf_cli-0.1.1/tests/test_cli.py +0 -304
- okf_cli-0.1.1/uv.lock +0 -150
- {okf_cli-0.1.1 → okf_cli-0.3.1}/.gitignore +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/LICENSE +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/OKF_SPEC.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/datasets/sales.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/domain.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/playbooks/incident-response.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/playbooks/oncall-guide.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/tables/customers.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/tables/orders.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/tables/partitions/2026/q1/jan/january.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/example/tables/partitions/daily.md +0 -0
- {okf_cli-0.1.1 → okf_cli-0.3.1}/src/okf/__init__.py +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
python-version: ["3.11"]
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v3
|
|
14
|
+
with:
|
|
15
|
+
python-version: ${{ matrix.python-version }}
|
|
16
|
+
- run: uv sync
|
|
17
|
+
- run: uv run pytest -q
|
okf_cli-0.3.1/AGENTS.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# AGENTS.md — okf-cli contributor context
|
|
2
|
+
|
|
3
|
+
This file is for AI agents (and humans) picking up work on `okf-cli`.
|
|
4
|
+
|
|
5
|
+
## Project overview
|
|
6
|
+
|
|
7
|
+
`okf-cli` is a Python CLI that converts plain markdown directories into
|
|
8
|
+
[Open Knowledge Format (OKF)](OKF_SPEC.md) bundles and provides tools to
|
|
9
|
+
inspect/validate them.
|
|
10
|
+
|
|
11
|
+
- **Input:** plain `.md` files starting with `# Title` and a `>` description block.
|
|
12
|
+
- **Output:** OKF-conformant markdown with YAML frontmatter, per-directory `index.md` files.
|
|
13
|
+
- **Operations:** `bundle`, `list`, `show`, `validate`.
|
|
14
|
+
|
|
15
|
+
## Tech stack
|
|
16
|
+
|
|
17
|
+
- Python 3.11+
|
|
18
|
+
- Package/venv manager: **uv**
|
|
19
|
+
- CLI framework: **typer**
|
|
20
|
+
- YAML parsing: **pyyaml**
|
|
21
|
+
- Tests: **pytest**
|
|
22
|
+
- Lint/format: **ruff** (via `uvx ruff check .` / `uvx ruff format .`)
|
|
23
|
+
- Build: hatchling (configured in `pyproject.toml`)
|
|
24
|
+
|
|
25
|
+
Always use `uv` for dependency sync and running commands:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv sync # install/sync deps
|
|
29
|
+
uv run pytest -q # run tests
|
|
30
|
+
uv run okf --help # run CLI
|
|
31
|
+
uv run okf bundle example bundled --default-type reference --force
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Project layout
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
okf-cli
|
|
38
|
+
├── .github/workflows/test.yml # CI (pytest on 3.11)
|
|
39
|
+
├── OKF_SPEC.md # OKF v0.1 specification
|
|
40
|
+
├── README.md # user-facing docs
|
|
41
|
+
├── AGENTS.md # this file
|
|
42
|
+
├── pyproject.toml # project metadata + deps
|
|
43
|
+
├── uv.lock # uv lockfile
|
|
44
|
+
├── src/okf/
|
|
45
|
+
│ ├── cli.py # Typer entrypoint, registers commands
|
|
46
|
+
│ ├── core.py # shared parsing/formatting/conformance
|
|
47
|
+
│ └── commands/
|
|
48
|
+
│ ├── bundle.py # plain markdown → OKF bundle
|
|
49
|
+
│ ├── list.py # list concept IDs
|
|
50
|
+
│ ├── show.py # print concept by ID
|
|
51
|
+
│ └── validate.py # conformance check
|
|
52
|
+
└── tests/test_cli.py # pytest suite
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Architecture
|
|
56
|
+
|
|
57
|
+
### `src/okf/cli.py`
|
|
58
|
+
|
|
59
|
+
- Creates the `typer.Typer` app named `okf`.
|
|
60
|
+
- Registers `bundle`, `list`, `show`, `validate`.
|
|
61
|
+
- No business logic here.
|
|
62
|
+
|
|
63
|
+
### `src/okf/core.py`
|
|
64
|
+
|
|
65
|
+
- `RESERVED` — filenames `bundle` skips: `index.md`, `log.md`, `README.md`.
|
|
66
|
+
- `SPEC_RESERVED` — filenames reserved by the OKF spec: `index.md`, `log.md`.
|
|
67
|
+
- `build_frontmatter(...)` — generates YAML frontmatter using JSON-escaped strings.
|
|
68
|
+
- `parse_md(...)` — extracts title/description/body from plain markdown. Tries strict format first (line 1 `# Title`, `>` block); falls back leniently (title from line 0 only, description from first 80 chars of body). Never raises.
|
|
69
|
+
- `parse_frontmatter(...)` — parses YAML frontmatter with `yaml.safe_load`.
|
|
70
|
+
- `check_conformance(...)` — validates a directory against OKF §9, returns `(errors, warnings)`. Now enforces:
|
|
71
|
+
- §9.1: Non-reserved `.md` must have parseable frontmatter.
|
|
72
|
+
- §9.2: Frontmatter must have non-empty `type`.
|
|
73
|
+
- §9.3: `index.md` must not contain frontmatter (§6), except root `index.md` may have only `okf_version` (§11). `log.md` must not contain frontmatter (§7).
|
|
74
|
+
- Non-UTF-8 files are flagged as errors.
|
|
75
|
+
|
|
76
|
+
### Commands
|
|
77
|
+
|
|
78
|
+
| Command | Input expected | Behavior |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `bundle` | non-conformant plain markdown dir | generates OKF bundle; skips `index.md`, `log.md`, `README.md` with warnings; root files need `--default-type`; requires `--force` to overwrite existing output |
|
|
81
|
+
| `list` | OKF-conformant bundle | prints concept IDs; exits 1 if dir is not conformant |
|
|
82
|
+
| `show` | OKF-conformant bundle | prints concept file by ID; exits 1 if dir is not conformant |
|
|
83
|
+
| `validate` | any directory | prints conformance errors and summary per §9 |
|
|
84
|
+
|
|
85
|
+
## Key conventions
|
|
86
|
+
|
|
87
|
+
### Reserved filenames
|
|
88
|
+
|
|
89
|
+
- `bundle` treats `index.md`, `log.md`, `README.md` as reserved because it
|
|
90
|
+
often runs against raw repo directories that may contain these files.
|
|
91
|
+
- `list`, `show`, `validate` operate on already-conformant OKF bundles, so
|
|
92
|
+
they only treat `index.md` and `log.md` as reserved per the spec.
|
|
93
|
+
- `README.md` can be a valid concept in an OKF bundle.
|
|
94
|
+
|
|
95
|
+
### OKF conformance (§9)
|
|
96
|
+
|
|
97
|
+
1. Every non-reserved `.md` must have parseable YAML frontmatter.
|
|
98
|
+
2. Every frontmatter must contain a non-empty `type` string.
|
|
99
|
+
3. Reserved filenames (`index.md`, `log.md`) follow structure per §6/§7 when present.
|
|
100
|
+
- `index.md` must not contain frontmatter (§6), except root `index.md` may contain only `okf_version` (§11).
|
|
101
|
+
- `log.md` must not contain frontmatter (§7).
|
|
102
|
+
4. All files must be valid UTF-8.
|
|
103
|
+
|
|
104
|
+
`check_conformance()` is the source of truth for this logic.
|
|
105
|
+
|
|
106
|
+
### Frontmatter
|
|
107
|
+
|
|
108
|
+
`build_frontmatter()` outputs JSON-escaped YAML values, e.g.:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
---
|
|
112
|
+
type: "tables"
|
|
113
|
+
title: "Customer Orders"
|
|
114
|
+
---
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`parse_frontmatter()` uses `yaml.safe_load()` and returns `None` for missing,
|
|
118
|
+
malformed, or non-dict frontmatter.
|
|
119
|
+
|
|
120
|
+
## Testing
|
|
121
|
+
|
|
122
|
+
Run the full suite before finishing:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
uv run pytest -q
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Add tests for new behavior in `tests/test_cli.py`. Existing patterns:
|
|
129
|
+
|
|
130
|
+
- Unit tests for `core.py` helpers.
|
|
131
|
+
- CLI integration tests using `typer.testing.CliRunner` and `tmp_path`.
|
|
132
|
+
- Conformance tests for `validate`, `list`, `show`.
|
|
133
|
+
|
|
134
|
+
## Common tasks
|
|
135
|
+
|
|
136
|
+
### Regenerate the example bundle
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv run okf bundle example bundled --default-type reference --force
|
|
140
|
+
uv run okf validate bundled
|
|
141
|
+
uv run okf list bundled
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`bundled/` is gitignored; it is only a local artifact. Use `--force` for re-runs.
|
|
145
|
+
|
|
146
|
+
### Add a new CLI command
|
|
147
|
+
|
|
148
|
+
1. Create `src/okf/commands/<name>.py` with a function taking `typer` arguments.
|
|
149
|
+
2. Import and register it in `src/okf/cli.py` with `app.command()(fn)` or
|
|
150
|
+
`app.command("alias")(fn)`.
|
|
151
|
+
3. Add tests in `tests/test_cli.py`.
|
|
152
|
+
|
|
153
|
+
### Modify conformance behavior
|
|
154
|
+
|
|
155
|
+
Update `check_conformance()` in `src/okf/core.py`, then ensure
|
|
156
|
+
`validate`, `list`, and `show` all behave consistently.
|
|
157
|
+
|
|
158
|
+
## Style guidelines
|
|
159
|
+
|
|
160
|
+
- Keep changes minimal. Prefer stdlib/installed dependencies over new ones.
|
|
161
|
+
- Do not add abstractions "for later".
|
|
162
|
+
- Use `Path` from `pathlib`, not string path manipulation.
|
|
163
|
+
- Use `uv` for all package/runtime commands.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: okf-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Open Knowledge Format tooling
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -24,13 +24,16 @@ License: MIT License
|
|
|
24
24
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
25
|
SOFTWARE.
|
|
26
26
|
License-File: LICENSE
|
|
27
|
-
Requires-Python: >=3.
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Requires-Dist: pyyaml>=6
|
|
28
29
|
Requires-Dist: typer>=0.15
|
|
29
30
|
Description-Content-Type: text/markdown
|
|
30
31
|
|
|
31
32
|
# okf-cli — Open Knowledge Format tooling
|
|
32
33
|
|
|
33
|
-
Converts plain markdown into [OKF](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)-conformant knowledge bundles. Domain experts write `# Title` then `> description` — `okf bundle` generates frontmatter, type, timestamps, and index files.
|
|
34
|
+
Converts plain markdown into [OKF](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)-conformant knowledge bundles. Domain experts write `# Title` then `> description` — `okf bundle` generates frontmatter, type, timestamps, and index files. Files without strict formatting are parsed leniently: title from line 0 if present, description from body text.
|
|
35
|
+
|
|
36
|
+
Also validates bundles and lists concept IDs for consumption by other tools.
|
|
34
37
|
|
|
35
38
|
## Install
|
|
36
39
|
|
|
@@ -38,25 +41,19 @@ Converts plain markdown into [OKF](https://github.com/GoogleCloudPlatform/knowle
|
|
|
38
41
|
uv tool install okf-cli
|
|
39
42
|
```
|
|
40
43
|
|
|
41
|
-
Then run:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
okf bundle example # output → bundled/
|
|
45
|
-
cat bundled/index.md
|
|
46
|
-
cat bundled/tables/orders.md
|
|
47
|
-
```
|
|
48
|
-
|
|
49
44
|
### Dev quickstart
|
|
50
45
|
|
|
51
46
|
```bash
|
|
52
47
|
uv sync
|
|
53
|
-
uv run okf
|
|
48
|
+
uv run okf --help
|
|
54
49
|
```
|
|
55
50
|
|
|
56
|
-
##
|
|
51
|
+
## Commands
|
|
52
|
+
|
|
53
|
+
### `okf bundle` — convert plain markdown to OKF bundle
|
|
57
54
|
|
|
58
55
|
```
|
|
59
|
-
okf <input-dir> [output-dir] [--default-type <name>]
|
|
56
|
+
okf bundle <input-dir> [output-dir] [--default-type <name>] [--force]
|
|
60
57
|
```
|
|
61
58
|
|
|
62
59
|
| Argument | Description |
|
|
@@ -64,10 +61,86 @@ okf <input-dir> [output-dir] [--default-type <name>]
|
|
|
64
61
|
| `input-dir` | Directory of plain `.md` files |
|
|
65
62
|
| `output-dir` | Target directory (default: `bundled`) |
|
|
66
63
|
| `--default-type` | Type for root-level files (skip root files if omitted) |
|
|
64
|
+
| `--force`, `-f` | Overwrite output directory if it exists |
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
okf bundle example --default-type reference # first run → bundled/
|
|
68
|
+
okf bundle example --default-type reference --force # re-run
|
|
69
|
+
cat bundled/tables/orders.md
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `okf list` — list concept IDs in a bundle
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
okf list <directory>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Prints concept IDs (bundle-relative path with `.md` stripped) for every
|
|
79
|
+
concept file in the bundle. Reserved filenames (`index.md`, `log.md`)
|
|
80
|
+
are excluded. `README.md` is not reserved by the OKF spec and is listed
|
|
81
|
+
as a concept if present.
|
|
82
|
+
|
|
83
|
+
Requires the directory to be a valid OKF bundle; fails if any
|
|
84
|
+
non-reserved `.md` is missing frontmatter or a non-empty `type`.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
okf list bundled/
|
|
88
|
+
# datasets/sales
|
|
89
|
+
# playbooks/incident-response
|
|
90
|
+
# tables/orders
|
|
91
|
+
# …
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Useful for piping OKF bundles into other tooling:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
okf list bundled/ | xargs -I{} okf show bundled/ {}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `okf show` — read a concept by ID
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
okf show <directory> <concept-id>
|
|
104
|
+
```
|
|
67
105
|
|
|
68
|
-
|
|
106
|
+
Prints the full contents (frontmatter + body) of a concept file.
|
|
107
|
+
Concept IDs are the bundle-relative path with `.md` stripped — exactly
|
|
108
|
+
as printed by `okf list`.
|
|
69
109
|
|
|
70
|
-
|
|
110
|
+
Requires the directory to be a valid OKF bundle.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
okf show bundled/ tables/orders
|
|
114
|
+
# ---
|
|
115
|
+
# type: "tables"
|
|
116
|
+
# title: "Customer Orders"
|
|
117
|
+
# ...
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Guards against path traversal and rejects reserved filenames.
|
|
121
|
+
|
|
122
|
+
### `okf validate` — check OKF conformance
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
okf validate <directory>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Checks OKF v0.1 §9 conformance:
|
|
129
|
+
|
|
130
|
+
- Every non-reserved `.md` has parseable YAML frontmatter with non-empty `type`.
|
|
131
|
+
- Reserved filenames (`index.md`, `log.md`) follow spec structure (§6/§7).
|
|
132
|
+
- Non-UTF-8 files are flagged.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
okf validate bundled/
|
|
136
|
+
# 16 files: 16 ok
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Writing input files (for `bundle`)
|
|
140
|
+
|
|
141
|
+
### Strict format (recommended)
|
|
142
|
+
|
|
143
|
+
Every `.md` file starts with:
|
|
71
144
|
|
|
72
145
|
```markdown
|
|
73
146
|
# Title
|
|
@@ -76,37 +149,36 @@ Every `.md` file must start with:
|
|
|
76
149
|
> Second optional description line.
|
|
77
150
|
```
|
|
78
151
|
|
|
79
|
-
Everything after the description block is
|
|
152
|
+
Everything after the description block is preserved unchanged.
|
|
153
|
+
|
|
154
|
+
### Lenient fallback
|
|
155
|
+
|
|
156
|
+
Files that don't follow the strict format are still bundled best-effort:
|
|
157
|
+
|
|
158
|
+
| Condition | Behavior |
|
|
159
|
+
|---|---|
|
|
160
|
+
| No `# Title` on line 1 | Title omitted from frontmatter |
|
|
161
|
+
| No `>` description block | Description derived from first 80 chars of body ("…" if truncated) |
|
|
162
|
+
| Root-level file without `--default-type` | Skip file, warn, continue |
|
|
80
163
|
|
|
81
164
|
**Rules:**
|
|
82
165
|
|
|
83
166
|
| Rule | Why |
|
|
84
167
|
|---|---|
|
|
85
|
-
| First line must be `# Title` | Tool reads title here |
|
|
86
|
-
| Followed by `> Description` | Tool reads description here |
|
|
87
168
|
| Folder name = concept type | `tables/orders.md` → `type: "tables"` |
|
|
88
169
|
| Only `.md` files processed | Non-`.md` files ignored |
|
|
89
|
-
|
|
|
90
|
-
|
|
91
|
-
**Violations:**
|
|
92
|
-
|
|
93
|
-
| Condition | Behavior |
|
|
94
|
-
|---|---|
|
|
95
|
-
| Line 1 not `# Title` | Error, stop |
|
|
96
|
-
| Empty title | Error, stop |
|
|
97
|
-
| No `> description` block | Error, stop |
|
|
98
|
-
| Root-level file without `--default-type` | Skip file, warn, continue |
|
|
170
|
+
| `index.md`, `log.md`, `README.md` skipped in `bundle` | Input may contain repo artifacts; these are not concepts. `bundle` warns when it skips them. Other commands (`list`, `show`, `validate`) operate on conformant OKF bundles where only `index.md` and `log.md` are reserved. |
|
|
99
171
|
|
|
100
172
|
Root files need `--default-type`. Otherwise put files in named folders.
|
|
101
173
|
|
|
102
174
|
See the [`example/`](example/) directory for a sample of how to structure files.
|
|
103
175
|
|
|
104
|
-
## How
|
|
176
|
+
## How `bundle` works
|
|
105
177
|
|
|
106
178
|
1. Walk `input-dir` for `.md` files (skip reserved names)
|
|
107
|
-
2. Extract `title` from
|
|
179
|
+
2. Extract `title` from `#` on line 1, `description` from `>` block. If strict format not met, falls back: title omitted if absent, description from first 80 chars of body
|
|
108
180
|
3. Set `type` from parent dir name, `timestamp` from file mtime
|
|
109
|
-
4. Write concept files with YAML frontmatter
|
|
181
|
+
4. Write concept files with YAML frontmatter (title field omitted if empty)
|
|
110
182
|
5. Generate `index.md` per directory — `# Contents` for files, `# Directories` for subdirs (recursive)
|
|
111
183
|
|
|
112
184
|
## Output
|
|
@@ -148,9 +220,13 @@ Generated bundles conform to [OKF v0.1](OKF_SPEC.md) (§9):
|
|
|
148
220
|
|
|
149
221
|
```
|
|
150
222
|
okf-cli
|
|
151
|
-
├──
|
|
152
|
-
├──
|
|
153
|
-
├──
|
|
223
|
+
├── .github/workflows/test.yml # CI
|
|
224
|
+
├── OKF_SPEC.md # OKF specification
|
|
225
|
+
├── pyproject.toml # uv-managed Python project
|
|
226
|
+
├── src/okf/
|
|
227
|
+
│ ├── cli.py # Typer entrypoint
|
|
228
|
+
│ ├── core.py # Shared parsing/formatting
|
|
229
|
+
│ └── commands/ # bundle, list, show, validate
|
|
154
230
|
├── tests/test_cli.py # Tests
|
|
155
231
|
└── example/ # Sample input markdown
|
|
156
232
|
```
|
okf_cli-0.3.1/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# okf-cli — Open Knowledge Format tooling
|
|
2
|
+
|
|
3
|
+
Converts plain markdown into [OKF](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)-conformant knowledge bundles. Domain experts write `# Title` then `> description` — `okf bundle` generates frontmatter, type, timestamps, and index files. Files without strict formatting are parsed leniently: title from line 0 if present, description from body text.
|
|
4
|
+
|
|
5
|
+
Also validates bundles and lists concept IDs for consumption by other tools.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv tool install okf-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Dev quickstart
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync
|
|
17
|
+
uv run okf --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
### `okf bundle` — convert plain markdown to OKF bundle
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
okf bundle <input-dir> [output-dir] [--default-type <name>] [--force]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Argument | Description |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `input-dir` | Directory of plain `.md` files |
|
|
31
|
+
| `output-dir` | Target directory (default: `bundled`) |
|
|
32
|
+
| `--default-type` | Type for root-level files (skip root files if omitted) |
|
|
33
|
+
| `--force`, `-f` | Overwrite output directory if it exists |
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
okf bundle example --default-type reference # first run → bundled/
|
|
37
|
+
okf bundle example --default-type reference --force # re-run
|
|
38
|
+
cat bundled/tables/orders.md
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `okf list` — list concept IDs in a bundle
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
okf list <directory>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Prints concept IDs (bundle-relative path with `.md` stripped) for every
|
|
48
|
+
concept file in the bundle. Reserved filenames (`index.md`, `log.md`)
|
|
49
|
+
are excluded. `README.md` is not reserved by the OKF spec and is listed
|
|
50
|
+
as a concept if present.
|
|
51
|
+
|
|
52
|
+
Requires the directory to be a valid OKF bundle; fails if any
|
|
53
|
+
non-reserved `.md` is missing frontmatter or a non-empty `type`.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
okf list bundled/
|
|
57
|
+
# datasets/sales
|
|
58
|
+
# playbooks/incident-response
|
|
59
|
+
# tables/orders
|
|
60
|
+
# …
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Useful for piping OKF bundles into other tooling:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
okf list bundled/ | xargs -I{} okf show bundled/ {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `okf show` — read a concept by ID
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
okf show <directory> <concept-id>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Prints the full contents (frontmatter + body) of a concept file.
|
|
76
|
+
Concept IDs are the bundle-relative path with `.md` stripped — exactly
|
|
77
|
+
as printed by `okf list`.
|
|
78
|
+
|
|
79
|
+
Requires the directory to be a valid OKF bundle.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
okf show bundled/ tables/orders
|
|
83
|
+
# ---
|
|
84
|
+
# type: "tables"
|
|
85
|
+
# title: "Customer Orders"
|
|
86
|
+
# ...
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Guards against path traversal and rejects reserved filenames.
|
|
90
|
+
|
|
91
|
+
### `okf validate` — check OKF conformance
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
okf validate <directory>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Checks OKF v0.1 §9 conformance:
|
|
98
|
+
|
|
99
|
+
- Every non-reserved `.md` has parseable YAML frontmatter with non-empty `type`.
|
|
100
|
+
- Reserved filenames (`index.md`, `log.md`) follow spec structure (§6/§7).
|
|
101
|
+
- Non-UTF-8 files are flagged.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
okf validate bundled/
|
|
105
|
+
# 16 files: 16 ok
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Writing input files (for `bundle`)
|
|
109
|
+
|
|
110
|
+
### Strict format (recommended)
|
|
111
|
+
|
|
112
|
+
Every `.md` file starts with:
|
|
113
|
+
|
|
114
|
+
```markdown
|
|
115
|
+
# Title
|
|
116
|
+
|
|
117
|
+
> Description
|
|
118
|
+
> Second optional description line.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Everything after the description block is preserved unchanged.
|
|
122
|
+
|
|
123
|
+
### Lenient fallback
|
|
124
|
+
|
|
125
|
+
Files that don't follow the strict format are still bundled best-effort:
|
|
126
|
+
|
|
127
|
+
| Condition | Behavior |
|
|
128
|
+
|---|---|
|
|
129
|
+
| No `# Title` on line 1 | Title omitted from frontmatter |
|
|
130
|
+
| No `>` description block | Description derived from first 80 chars of body ("…" if truncated) |
|
|
131
|
+
| Root-level file without `--default-type` | Skip file, warn, continue |
|
|
132
|
+
|
|
133
|
+
**Rules:**
|
|
134
|
+
|
|
135
|
+
| Rule | Why |
|
|
136
|
+
|---|---|
|
|
137
|
+
| Folder name = concept type | `tables/orders.md` → `type: "tables"` |
|
|
138
|
+
| Only `.md` files processed | Non-`.md` files ignored |
|
|
139
|
+
| `index.md`, `log.md`, `README.md` skipped in `bundle` | Input may contain repo artifacts; these are not concepts. `bundle` warns when it skips them. Other commands (`list`, `show`, `validate`) operate on conformant OKF bundles where only `index.md` and `log.md` are reserved. |
|
|
140
|
+
|
|
141
|
+
Root files need `--default-type`. Otherwise put files in named folders.
|
|
142
|
+
|
|
143
|
+
See the [`example/`](example/) directory for a sample of how to structure files.
|
|
144
|
+
|
|
145
|
+
## How `bundle` works
|
|
146
|
+
|
|
147
|
+
1. Walk `input-dir` for `.md` files (skip reserved names)
|
|
148
|
+
2. Extract `title` from `#` on line 1, `description` from `>` block. If strict format not met, falls back: title omitted if absent, description from first 80 chars of body
|
|
149
|
+
3. Set `type` from parent dir name, `timestamp` from file mtime
|
|
150
|
+
4. Write concept files with YAML frontmatter (title field omitted if empty)
|
|
151
|
+
5. Generate `index.md` per directory — `# Contents` for files, `# Directories` for subdirs (recursive)
|
|
152
|
+
|
|
153
|
+
## Output
|
|
154
|
+
|
|
155
|
+
Each concept becomes a markdown file with YAML frontmatter:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
---
|
|
159
|
+
type: "tables"
|
|
160
|
+
title: "Customer Orders"
|
|
161
|
+
description: "One row per completed customer order across all channels."
|
|
162
|
+
timestamp: "2026-07-04T15:06:51+00:00"
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
Original body preserved as-is.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Every directory gets its own `index.md`:
|
|
169
|
+
|
|
170
|
+
```markdown
|
|
171
|
+
# Contents
|
|
172
|
+
|
|
173
|
+
* [Customer Orders](orders.md) - One row per completed customer order across all channels.
|
|
174
|
+
|
|
175
|
+
# Directories
|
|
176
|
+
|
|
177
|
+
* [partitions](partitions/)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## OKF Conformance
|
|
181
|
+
|
|
182
|
+
Generated bundles conform to [OKF v0.1](OKF_SPEC.md) (§9):
|
|
183
|
+
|
|
184
|
+
- Every non-reserved `.md` has parseable YAML frontmatter with non-empty `type` ✓
|
|
185
|
+
- Reserved filenames follow spec structure ✓
|
|
186
|
+
- Consumers MUST tolerate missing optional fields, unknown types, broken links ✓
|
|
187
|
+
|
|
188
|
+
## Project layout
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
okf-cli
|
|
192
|
+
├── .github/workflows/test.yml # CI
|
|
193
|
+
├── OKF_SPEC.md # OKF specification
|
|
194
|
+
├── pyproject.toml # uv-managed Python project
|
|
195
|
+
├── src/okf/
|
|
196
|
+
│ ├── cli.py # Typer entrypoint
|
|
197
|
+
│ ├── core.py # Shared parsing/formatting
|
|
198
|
+
│ └── commands/ # bundle, list, show, validate
|
|
199
|
+
├── tests/test_cli.py # Tests
|
|
200
|
+
└── example/ # Sample input markdown
|
|
201
|
+
```
|
|
@@ -5,8 +5,8 @@ Run `okf bundle` to convert them into an OKF-conformant knowledge bundle.
|
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
7
|
# From repo root:
|
|
8
|
-
uv run okf example output-bundle
|
|
8
|
+
uv run okf bundle example output-bundle
|
|
9
9
|
|
|
10
10
|
# With a default type for root-level files (if any):
|
|
11
|
-
uv run okf example output-bundle --default-type reference
|
|
11
|
+
uv run okf bundle example output-bundle --default-type reference
|
|
12
12
|
```
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "okf-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "Open Knowledge Format tooling"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { file = "LICENSE" }
|
|
7
|
-
requires-python = ">=3.
|
|
8
|
-
dependencies = ["typer>=0.15"]
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
dependencies = ["typer>=0.15", "pyyaml>=6"]
|
|
9
9
|
|
|
10
10
|
[project.scripts]
|
|
11
11
|
okf = "okf.cli:app"
|
|
@@ -18,4 +18,10 @@ build-backend = "hatchling.build"
|
|
|
18
18
|
packages = ["src/okf"]
|
|
19
19
|
|
|
20
20
|
[dependency-groups]
|
|
21
|
-
dev = ["pytest>=8"]
|
|
21
|
+
dev = ["pytest>=8", "ruff>=0.14"]
|
|
22
|
+
|
|
23
|
+
[tool.ruff]
|
|
24
|
+
line-length = 88
|
|
25
|
+
|
|
26
|
+
[tool.ruff.lint]
|
|
27
|
+
select = ["E", "F", "I", "UP"]
|