okf-cli 0.1.0__tar.gz → 0.1.2__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.1.0 → okf_cli-0.1.2}/PKG-INFO +17 -5
- {okf_cli-0.1.0 → okf_cli-0.1.2}/README.md +16 -4
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/README.md +3 -3
- {okf_cli-0.1.0 → okf_cli-0.1.2}/pyproject.toml +1 -1
- {okf_cli-0.1.0 → okf_cli-0.1.2}/src/okf/cli.py +9 -2
- {okf_cli-0.1.0 → okf_cli-0.1.2}/tests/test_cli.py +18 -18
- {okf_cli-0.1.0 → okf_cli-0.1.2}/uv.lock +1 -1
- {okf_cli-0.1.0 → okf_cli-0.1.2}/.gitignore +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/LICENSE +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/OKF_SPEC.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/datasets/sales.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/domain.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/playbooks/incident-response.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/playbooks/oncall-guide.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/tables/customers.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/tables/orders.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/tables/partitions/2026/q1/jan/january.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/example/tables/partitions/daily.md +0 -0
- {okf_cli-0.1.0 → okf_cli-0.1.2}/src/okf/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: okf-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Open Knowledge Format tooling
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -30,17 +30,29 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
|
|
31
31
|
# okf-cli — Open Knowledge Format tooling
|
|
32
32
|
|
|
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
|
|
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
34
|
|
|
35
|
-
##
|
|
35
|
+
## Install
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
uv
|
|
39
|
-
|
|
38
|
+
uv tool install okf-cli
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then run:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
okf bundle example # output → bundled/
|
|
40
45
|
cat bundled/index.md
|
|
41
46
|
cat bundled/tables/orders.md
|
|
42
47
|
```
|
|
43
48
|
|
|
49
|
+
### Dev quickstart
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv sync
|
|
53
|
+
uv run okf example
|
|
54
|
+
```
|
|
55
|
+
|
|
44
56
|
## Usage
|
|
45
57
|
|
|
46
58
|
```
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
# okf-cli — Open Knowledge Format tooling
|
|
2
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
|
|
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.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
uv
|
|
9
|
-
|
|
8
|
+
uv tool install okf-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Then run:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
okf bundle example # output → bundled/
|
|
10
15
|
cat bundled/index.md
|
|
11
16
|
cat bundled/tables/orders.md
|
|
12
17
|
```
|
|
13
18
|
|
|
19
|
+
### Dev quickstart
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv sync
|
|
23
|
+
uv run okf example
|
|
24
|
+
```
|
|
25
|
+
|
|
14
26
|
## Usage
|
|
15
27
|
|
|
16
28
|
```
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# OKF Example
|
|
2
2
|
|
|
3
3
|
This directory contains plain markdown files written by domain experts.
|
|
4
|
-
Run `okf
|
|
4
|
+
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,4 +1,4 @@
|
|
|
1
|
-
"""okf
|
|
1
|
+
"""okf bundle — Plain markdown to OKF bundle converter."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
@@ -14,6 +14,13 @@ app = typer.Typer(
|
|
|
14
14
|
no_args_is_help=True,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
+
|
|
18
|
+
@app.callback()
|
|
19
|
+
def main() -> None:
|
|
20
|
+
"""Convert plain markdown into OKF-conformant knowledge bundles."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
17
24
|
RESERVED = frozenset({"index.md", "log.md", "readme.md"})
|
|
18
25
|
|
|
19
26
|
|
|
@@ -78,7 +85,7 @@ def _parse_md(text: str) -> tuple[str, str, str]:
|
|
|
78
85
|
|
|
79
86
|
|
|
80
87
|
@app.command()
|
|
81
|
-
def
|
|
88
|
+
def bundle(
|
|
82
89
|
input_dir: str = typer.Argument(
|
|
83
90
|
..., help="Source directory of plain markdown files"
|
|
84
91
|
),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Tests for okf
|
|
1
|
+
"""Tests for okf bundle CLI."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from pathlib import Path
|
|
@@ -113,7 +113,7 @@ def _write_fixture(dir: Path, files: dict[str, str]):
|
|
|
113
113
|
p.write_text(content)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def
|
|
116
|
+
def test_bundle_basic(tmp_path: Path):
|
|
117
117
|
src = tmp_path / "notes"
|
|
118
118
|
dst = tmp_path / "bundle"
|
|
119
119
|
src.mkdir()
|
|
@@ -125,7 +125,7 @@ def test_enrich_basic(tmp_path: Path):
|
|
|
125
125
|
},
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
128
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
129
129
|
assert result.exit_code == 0, result.output
|
|
130
130
|
|
|
131
131
|
# Check output structure
|
|
@@ -150,7 +150,7 @@ def test_enrich_basic(tmp_path: Path):
|
|
|
150
150
|
assert "[tables](tables/)" in root_idx
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
def
|
|
153
|
+
def test_bundle_root_file_with_default_type(tmp_path: Path):
|
|
154
154
|
src = tmp_path / "notes"
|
|
155
155
|
dst = tmp_path / "bundle"
|
|
156
156
|
src.mkdir()
|
|
@@ -162,7 +162,7 @@ def test_enrich_root_file_with_default_type(tmp_path: Path):
|
|
|
162
162
|
},
|
|
163
163
|
)
|
|
164
164
|
|
|
165
|
-
result = runner.invoke(app, [str(src), str(dst), "--default-type", "reference"])
|
|
165
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst), "--default-type", "reference"])
|
|
166
166
|
assert result.exit_code == 0, result.output
|
|
167
167
|
|
|
168
168
|
stand = (dst / "standalone.md").read_text()
|
|
@@ -177,7 +177,7 @@ def test_enrich_root_file_with_default_type(tmp_path: Path):
|
|
|
177
177
|
assert "[Standalone](standalone.md)" in root_idx
|
|
178
178
|
|
|
179
179
|
|
|
180
|
-
def
|
|
180
|
+
def test_bundle_skip_root_file_without_default(tmp_path: Path, capsys):
|
|
181
181
|
src = tmp_path / "notes"
|
|
182
182
|
dst = tmp_path / "bundle"
|
|
183
183
|
src.mkdir()
|
|
@@ -189,7 +189,7 @@ def test_enrich_skip_root_file_without_default(tmp_path: Path, capsys):
|
|
|
189
189
|
},
|
|
190
190
|
)
|
|
191
191
|
|
|
192
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
192
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
193
193
|
assert result.exit_code == 0, result.output
|
|
194
194
|
|
|
195
195
|
assert not (dst / "standalone.md").exists()
|
|
@@ -202,7 +202,7 @@ def test_enrich_skip_root_file_without_default(tmp_path: Path, capsys):
|
|
|
202
202
|
assert "[Standalone" not in root_idx
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
def
|
|
205
|
+
def test_bundle_validation_error(tmp_path: Path):
|
|
206
206
|
src = tmp_path / "notes"
|
|
207
207
|
dst = tmp_path / "bundle"
|
|
208
208
|
src.mkdir()
|
|
@@ -213,12 +213,12 @@ def test_enrich_validation_error(tmp_path: Path):
|
|
|
213
213
|
},
|
|
214
214
|
)
|
|
215
215
|
|
|
216
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
216
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
217
217
|
assert result.exit_code == 1
|
|
218
218
|
assert "Line 1 must be" in result.output
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
def
|
|
221
|
+
def test_bundle_replaces_existing_output(tmp_path: Path):
|
|
222
222
|
src = tmp_path / "notes"
|
|
223
223
|
dst = tmp_path / "bundle"
|
|
224
224
|
src.mkdir()
|
|
@@ -226,26 +226,26 @@ def test_enrich_replaces_existing_output(tmp_path: Path):
|
|
|
226
226
|
(dst / "leftover.txt").write_text("should be gone")
|
|
227
227
|
_write_fixture(src, {"tables/a.md": "# A\n\n> Desc.\n"})
|
|
228
228
|
|
|
229
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
229
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
230
230
|
assert result.exit_code == 0, result.output
|
|
231
231
|
assert "Removed existing" in result.output
|
|
232
232
|
assert (dst / "tables" / "a.md").exists()
|
|
233
233
|
assert not (dst / "leftover.txt").exists()
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
def
|
|
236
|
+
def test_bundle_no_md_files(tmp_path: Path):
|
|
237
237
|
src = tmp_path / "notes"
|
|
238
238
|
dst = tmp_path / "bundle"
|
|
239
239
|
src.mkdir()
|
|
240
240
|
# Create a non-md file
|
|
241
241
|
(src / "readme.txt").write_text("hello")
|
|
242
242
|
|
|
243
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
243
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
244
244
|
assert result.exit_code == 1
|
|
245
245
|
assert "No markdown files" in result.output
|
|
246
246
|
|
|
247
247
|
|
|
248
|
-
def
|
|
248
|
+
def test_bundle_logmd_warning(tmp_path: Path):
|
|
249
249
|
src = tmp_path / "notes"
|
|
250
250
|
dst = tmp_path / "bundle"
|
|
251
251
|
src.mkdir()
|
|
@@ -253,7 +253,7 @@ def test_enrich_logmd_warning(tmp_path: Path):
|
|
|
253
253
|
(src / "tables").mkdir()
|
|
254
254
|
(src / "tables/orders.md").write_text("# Orders\n\n> One row.\n\nBody.")
|
|
255
255
|
|
|
256
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
256
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
257
257
|
assert result.exit_code == 0, result.output
|
|
258
258
|
assert "reserved filename" in result.output
|
|
259
259
|
assert "log.md" in result.output
|
|
@@ -261,7 +261,7 @@ def test_enrich_logmd_warning(tmp_path: Path):
|
|
|
261
261
|
assert (dst / "tables" / "orders.md").exists()
|
|
262
262
|
|
|
263
263
|
|
|
264
|
-
def
|
|
264
|
+
def test_bundle_readme_is_reserved(tmp_path: Path):
|
|
265
265
|
"""README.md is reserved and skipped."""
|
|
266
266
|
src = tmp_path / "notes"
|
|
267
267
|
dst = tmp_path / "bundle"
|
|
@@ -274,7 +274,7 @@ def test_enrich_readme_is_reserved(tmp_path: Path):
|
|
|
274
274
|
"# Orders\n\n> One row.\n\nBody."
|
|
275
275
|
)
|
|
276
276
|
|
|
277
|
-
result = runner.invoke(app, [str(src), str(dst)])
|
|
277
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
278
278
|
assert result.exit_code == 0, result.output
|
|
279
279
|
assert not (dst / "tables" / "README.md").exists()
|
|
280
280
|
assert (dst / "tables" / "orders.md").exists()
|
|
@@ -291,7 +291,7 @@ def test_frontmatter_yaml_parseable(tmp_path: Path):
|
|
|
291
291
|
"# True/False\n\n> Yes, no, true, false values\n\nBody."
|
|
292
292
|
)
|
|
293
293
|
|
|
294
|
-
result = runner.invoke(app, [str(src), str(dst), "--default-type", "ref"])
|
|
294
|
+
result = runner.invoke(app, ["bundle", str(src), str(dst), "--default-type", "ref"])
|
|
295
295
|
assert result.exit_code == 0, result.output
|
|
296
296
|
|
|
297
297
|
content = (dst / "data.md").read_text()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|