okf-cli 0.4.3__tar.gz → 0.4.5__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.4.5/.last-update.json +1 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/PKG-INFO +1 -1
- {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/architecture.md +1 -1
- {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/domain-model.md +3 -2
- {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/operations.md +3 -4
- {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/quickstart.md +5 -5
- {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/testing.md +1 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/workflows.md +8 -8
- {okf_cli-0.4.3 → okf_cli-0.4.5}/pyproject.toml +1 -1
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/bundle.py +21 -17
- {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_bundle.py +9 -10
- {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/e2e/test_e2e.py +5 -3
- {okf_cli-0.4.3 → okf_cli-0.4.5}/uv.lock +1 -1
- okf_cli-0.4.3/.last-update.json +0 -5
- {okf_cli-0.4.3 → okf_cli-0.4.5}/.github/workflows/test.yml +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/.gitignore +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/AGENTS.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/LICENSE +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/OKF_SPEC.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/README.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/.okfignore +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/README.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/datasets/sales.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/domain.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/loose/deep/no-heading.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/loose/no-desc.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/loose/no-title.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/playbooks/incident-response.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/playbooks/oncall-guide.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/smoke-ignore.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/customers.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/orders.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/partitions/2026/q1/jan/january.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/partitions/daily.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/skills/okf-cli-manual/SKILL.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/skills/writing-knowledge-base/SKILL.md +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/__init__.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/cli.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/__init__.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/list.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/show.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/validate.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/core.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_list.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_show.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_validate.py +0 -0
- {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/test_core.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"head": "a841967e2873ae08f6af8976cc96d30cd90857c4", "timestamp": "2026-07-13T17:04:00+05:30", "mode": "update"}
|
|
@@ -48,7 +48,7 @@ Source: `src/okf/commands/list.py`, `src/okf/commands/show.py`.
|
|
|
48
48
|
|
|
49
49
|
- Reserved name handling differs by phase:
|
|
50
50
|
- Bundling phase skips `index.md`, `log.md`, `README.md` (`RESERVED`).
|
|
51
|
-
- Spec-conformance phase reserves
|
|
51
|
+
- Spec-conformance phase reserves `index.md`, `log.md`, `agents.md` (`SPEC_RESERVED`). `agents.md` is reserved but skipped during conformance checks (not an error).
|
|
52
52
|
- Non-UTF-8 markdown is a conformance error.
|
|
53
53
|
- `type` frontmatter is required and must be non-empty for non-reserved concept files.
|
|
54
54
|
|
|
@@ -34,9 +34,9 @@ Implementation: `src/okf/commands/list.py`, `src/okf/commands/show.py`.
|
|
|
34
34
|
Two rule sets:
|
|
35
35
|
|
|
36
36
|
- Bundling input skip set: `index.md`, `log.md`, `README.md` (`RESERVED`)
|
|
37
|
-
- Spec reserved set: `index.md`, `log.md` (`SPEC_RESERVED`)
|
|
37
|
+
- Spec reserved set: `index.md`, `log.md`, `agents.md` (`SPEC_RESERVED`)
|
|
38
38
|
|
|
39
|
-
Why dual sets: raw input dirs often include README-like repo artifacts; spec-level bundle reading should only reserve spec names.
|
|
39
|
+
Why dual sets: raw input dirs often include README-like repo artifacts; spec-level bundle reading should only reserve spec names plus `agents.md` (generated at bundle root, never a concept).
|
|
40
40
|
|
|
41
41
|
Implementation: `src/okf/core.py` + command logic.
|
|
42
42
|
|
|
@@ -99,6 +99,7 @@ Source: `src/okf/commands/bundle.py`.
|
|
|
99
99
|
|
|
100
100
|
1. non-reserved markdown must have parseable YAML frontmatter;
|
|
101
101
|
1. non-reserved markdown must include non-empty string `type`;
|
|
102
|
+
1. `agents.md` is skipped entirely (no frontmatter/validation);
|
|
102
103
|
1. `index.md` and `log.md` structure rules:
|
|
103
104
|
- non-root `index.md` must not have frontmatter;
|
|
104
105
|
- root `index.md` may have frontmatter only containing `okf_version`;
|
|
@@ -28,8 +28,8 @@ uvx ruff format .
|
|
|
28
28
|
For bundle smoke:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
uv run okf bundle example
|
|
32
|
-
uv run okf validate
|
|
31
|
+
uv run okf bundle example --default-type reference --force
|
|
32
|
+
uv run okf validate example_knowledge_base
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## CI/CD
|
|
@@ -48,8 +48,7 @@ No deploy pipeline in repo; CI currently focused on correctness tests.
|
|
|
48
48
|
## Repo artifacts and hygiene
|
|
49
49
|
|
|
50
50
|
- `example/` contains sample source markdown.
|
|
51
|
-
-
|
|
52
|
-
- `bundled/` is git-ignored local artifact (`.gitignore`).
|
|
51
|
+
- Default output (`<input>_knowledge_base`) is git-ignored local artifact (`.gitignore`).
|
|
53
52
|
|
|
54
53
|
When changing command behavior, update sample docs/output only if behavior change is intentional and tested.
|
|
55
54
|
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
```bash
|
|
12
12
|
uv sync
|
|
13
13
|
uv run okf --help
|
|
14
|
-
uv run okf bundle example
|
|
15
|
-
uv run okf validate
|
|
16
|
-
uv run okf list
|
|
17
|
-
uv run okf show
|
|
18
|
-
uv run okf bundle example
|
|
14
|
+
uv run okf bundle example --default-type reference --force
|
|
15
|
+
uv run okf validate example_knowledge_base
|
|
16
|
+
uv run okf list example_knowledge_base
|
|
17
|
+
uv run okf show example_knowledge_base tables/customers
|
|
18
|
+
uv run okf bundle example --default-type reference --force --strict-links
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Why this sequence:
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- parsing helpers (`parse_md`, `parse_frontmatter`)
|
|
7
7
|
- frontmatter generation (`build_frontmatter`)
|
|
8
8
|
- conformance engine (`check_conformance`)
|
|
9
|
+
- End-to-end smoke tests: `tests/e2e/test_e2e.py` (every CLI feature)
|
|
9
10
|
- CLI integration tests:
|
|
10
11
|
- bundling: `tests/cli/test_bundle.py`
|
|
11
12
|
- list: `tests/cli/test_list.py`
|
|
@@ -19,13 +19,14 @@ Lenient fallback exists for imperfect files, but strict shape gives better metad
|
|
|
19
19
|
### 2) Bundle to OKF
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
uv run okf bundle <input-dir>
|
|
23
|
-
|
|
22
|
+
uv run okf bundle <input-dir> [output-dir] [--default-type <type>] [--force] [--strict-links]
|
|
23
|
+
# output-dir defaults to <input-dir>_knowledge_base
|
|
24
|
+
# --default-type defaults to input directory name
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
Important behavior:
|
|
27
28
|
|
|
28
|
-
- Root-level markdown
|
|
29
|
+
- Root-level markdown uses input directory name as type if `--default-type` not specified.
|
|
29
30
|
- Reserved filenames skipped during bundling (`index.md`, `log.md`, `README.md`).
|
|
30
31
|
- `.okfignore` in input root can skip exact bundle-relative paths.
|
|
31
32
|
- `--strict-links` fails if any local `.md` link is missing or points outside bundle.
|
|
@@ -91,14 +92,13 @@ Common pitfalls:
|
|
|
91
92
|
Repo contains:
|
|
92
93
|
|
|
93
94
|
- source sample: `example/`
|
|
94
|
-
- generated sample: `bundled-smoke/`
|
|
95
95
|
|
|
96
96
|
Use to verify end-to-end behavior quickly:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
|
-
uv run okf bundle example
|
|
100
|
-
uv run okf validate
|
|
101
|
-
uv run okf list
|
|
99
|
+
uv run okf bundle example --default-type reference --force
|
|
100
|
+
uv run okf validate example_knowledge_base
|
|
101
|
+
uv run okf list example_knowledge_base
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
(Generated output directory is ignored by git for default `
|
|
104
|
+
(Generated output directory is ignored by git for default `example_knowledge_base/`; see `.gitignore`.)
|
|
@@ -78,11 +78,13 @@ def bundle(
|
|
|
78
78
|
input_dir: str = typer.Argument(
|
|
79
79
|
..., help="Source directory of plain markdown files"
|
|
80
80
|
),
|
|
81
|
-
output_dir: str = typer.Argument(
|
|
82
|
-
|
|
81
|
+
output_dir: str | None = typer.Argument(
|
|
82
|
+
None,
|
|
83
|
+
help="Output directory for the OKF bundle "
|
|
84
|
+
"(default: <input-dir>_knowledge_base)",
|
|
83
85
|
),
|
|
84
86
|
default_type: str = typer.Option(
|
|
85
|
-
None, help="Type for root-level files (
|
|
87
|
+
None, help="Type for root-level files (default: input directory name)"
|
|
86
88
|
),
|
|
87
89
|
force: bool = typer.Option(
|
|
88
90
|
False, "--force", "-f", help="Overwrite output directory if it exists"
|
|
@@ -97,9 +99,13 @@ def bundle(
|
|
|
97
99
|
|
|
98
100
|
Each .md file must start with '# Title' followed by a '>' description block.
|
|
99
101
|
Directory name determines the concept 'type'. Root-level files need --default-type.
|
|
100
|
-
If output-dir is omitted, defaults to '
|
|
102
|
+
If output-dir is omitted, defaults to '<input-dir>_knowledge_base'.
|
|
101
103
|
"""
|
|
102
104
|
src = Path(input_dir)
|
|
105
|
+
|
|
106
|
+
if output_dir is None:
|
|
107
|
+
output_dir = f"{src.name}_knowledge_base"
|
|
108
|
+
|
|
103
109
|
dst = Path(output_dir)
|
|
104
110
|
|
|
105
111
|
if not src.is_dir():
|
|
@@ -156,11 +162,7 @@ def bundle(
|
|
|
156
162
|
typer.echo("No markdown files found (excluding index.md, log.md)", err=True)
|
|
157
163
|
raise typer.Exit(code=1)
|
|
158
164
|
|
|
159
|
-
planned_files =
|
|
160
|
-
f
|
|
161
|
-
for f in md_files
|
|
162
|
-
if not (f.relative_to(src).parent == Path(".") and default_type is None)
|
|
163
|
-
]
|
|
165
|
+
planned_files = md_files
|
|
164
166
|
planned_rels = [f.relative_to(src) for f in planned_files]
|
|
165
167
|
bundle_targets = {rel.as_posix() for rel in planned_rels}
|
|
166
168
|
|
|
@@ -214,13 +216,7 @@ def bundle(
|
|
|
214
216
|
|
|
215
217
|
# Determine type
|
|
216
218
|
if rel.parent == Path("."):
|
|
217
|
-
|
|
218
|
-
typer.echo(
|
|
219
|
-
f"Warning: Skipping {rel} — root-level file needs --default-type",
|
|
220
|
-
err=True,
|
|
221
|
-
)
|
|
222
|
-
continue
|
|
223
|
-
type_name = default_type
|
|
219
|
+
type_name = default_type or src.name
|
|
224
220
|
else:
|
|
225
221
|
type_name = rel.parent.name
|
|
226
222
|
|
|
@@ -302,7 +298,15 @@ def bundle(
|
|
|
302
298
|
# Generate AGENTS.md at bundle root
|
|
303
299
|
(dst / "AGENTS.md").write_text(
|
|
304
300
|
f"# Knowledge Base: {dst.name}\n\n"
|
|
305
|
-
f"You are in an OKF knowledge base called
|
|
301
|
+
f"You are in an OKF (Open Knowledge Format) knowledge base called "
|
|
302
|
+
f"**{dst.name}**. OKF is a structured markdown format where each "
|
|
303
|
+
f"`.md` file is a concept with YAML frontmatter (`type`, `title`, "
|
|
304
|
+
f"`description`) and cross-links between related concepts.\n\n"
|
|
305
|
+
"## Instructions\n\n"
|
|
306
|
+
"- **Answer from this knowledge base only.** Use the concepts and "
|
|
307
|
+
"links here as your source of truth.\n"
|
|
308
|
+
"- **If the answer is not in the knowledge base, say so directly.** "
|
|
309
|
+
"Do not fabricate, guess, or pull from external knowledge.\n\n"
|
|
306
310
|
"## Getting Started\n\n"
|
|
307
311
|
"Read [index.md](index.md) first — it lists all concepts "
|
|
308
312
|
"and subdirectories.\n\n"
|
|
@@ -82,7 +82,8 @@ def test_bundle_root_file_with_default_type(tmp_path: Path):
|
|
|
82
82
|
assert "[Standalone](standalone.md)" in root_idx
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
def
|
|
85
|
+
def test_bundle_root_file_uses_input_dir_name_without_default(tmp_path: Path):
|
|
86
|
+
"""Root-level files get type from input directory name when --default-type omitted."""
|
|
86
87
|
src = tmp_path / "notes"
|
|
87
88
|
dst = tmp_path / "bundle"
|
|
88
89
|
src.mkdir()
|
|
@@ -97,14 +98,15 @@ def test_bundle_skip_root_file_without_default(tmp_path: Path, capsys):
|
|
|
97
98
|
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
98
99
|
assert result.exit_code == 0, result.output
|
|
99
100
|
|
|
100
|
-
assert
|
|
101
|
+
assert (dst / "standalone.md").exists()
|
|
102
|
+
stand = (dst / "standalone.md").read_text()
|
|
103
|
+
assert 'type: "notes"' in stand
|
|
101
104
|
assert (dst / "tables" / "data.md").exists()
|
|
102
|
-
assert "Skipping" in result.output
|
|
103
105
|
|
|
104
|
-
# Root index should list subdir
|
|
106
|
+
# Root index should list subdir + root file
|
|
105
107
|
root_idx = (dst / "index.md").read_text()
|
|
106
108
|
assert "[tables](tables/)" in root_idx
|
|
107
|
-
assert "[Standalone"
|
|
109
|
+
assert "[Standalone](standalone.md)" in root_idx
|
|
108
110
|
|
|
109
111
|
|
|
110
112
|
def test_bundle_lenient_parse(tmp_path: Path):
|
|
@@ -272,7 +274,7 @@ def test_bundle_okfignore_skips_matching_files(tmp_path: Path):
|
|
|
272
274
|
assert (dst / "tables" / "customers.md").exists()
|
|
273
275
|
|
|
274
276
|
|
|
275
|
-
def
|
|
277
|
+
def test_bundle_okfignore_skips_root_file(tmp_path: Path):
|
|
276
278
|
src = tmp_path / "notes"
|
|
277
279
|
dst = tmp_path / "bundle"
|
|
278
280
|
src.mkdir()
|
|
@@ -287,7 +289,6 @@ def test_bundle_okfignore_skips_root_file_before_default_type_check(tmp_path: Pa
|
|
|
287
289
|
|
|
288
290
|
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
289
291
|
assert result.exit_code == 0, result.output
|
|
290
|
-
assert "root-level file needs --default-type" not in result.output
|
|
291
292
|
assert not (dst / "standalone.md").exists()
|
|
292
293
|
assert (dst / "tables" / "data.md").exists()
|
|
293
294
|
|
|
@@ -452,9 +453,7 @@ def test_bundle_strict_links_passes_with_valid_local_links(tmp_path: Path):
|
|
|
452
453
|
"tables/orders.md": (
|
|
453
454
|
"# Orders\n\n> One row.\n\nSee [Customers](./customers.md)."
|
|
454
455
|
),
|
|
455
|
-
"tables/customers.md": (
|
|
456
|
-
"# Customers\n\n> All customers.\n\nBody."
|
|
457
|
-
),
|
|
456
|
+
"tables/customers.md": ("# Customers\n\n> All customers.\n\nBody."),
|
|
458
457
|
},
|
|
459
458
|
)
|
|
460
459
|
|
|
@@ -62,7 +62,8 @@ def test_bundle_default_type_includes_root_files(tmp_path: Path):
|
|
|
62
62
|
assert 'type: "reference"' in content
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def
|
|
65
|
+
def test_bundle_root_files_get_type_from_input_dir(tmp_path: Path):
|
|
66
|
+
"""Root-level files get type from input directory name when --default-type omitted."""
|
|
66
67
|
src = tmp_path / "src"
|
|
67
68
|
dst = tmp_path / "out"
|
|
68
69
|
src.mkdir()
|
|
@@ -76,8 +77,9 @@ def test_bundle_skips_root_files_without_default_type(tmp_path: Path):
|
|
|
76
77
|
|
|
77
78
|
result = runner.invoke(app, ["bundle", str(src), str(dst)])
|
|
78
79
|
assert result.exit_code == 0, result.output
|
|
79
|
-
assert
|
|
80
|
-
|
|
80
|
+
assert (dst / "domain.md").exists()
|
|
81
|
+
content = (dst / "domain.md").read_text()
|
|
82
|
+
assert 'type: "src"' in content
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
def test_bundle_force_overwrites(tmp_path: Path):
|
okf_cli-0.4.3/.last-update.json
DELETED
|
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
|
|
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
|
|
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
|