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.
Files changed (47) hide show
  1. okf_cli-0.4.5/.last-update.json +1 -0
  2. {okf_cli-0.4.3 → okf_cli-0.4.5}/PKG-INFO +1 -1
  3. {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/architecture.md +1 -1
  4. {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/domain-model.md +3 -2
  5. {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/operations.md +3 -4
  6. {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/quickstart.md +5 -5
  7. {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/testing.md +1 -0
  8. {okf_cli-0.4.3 → okf_cli-0.4.5}/openwiki/workflows.md +8 -8
  9. {okf_cli-0.4.3 → okf_cli-0.4.5}/pyproject.toml +1 -1
  10. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/bundle.py +21 -17
  11. {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_bundle.py +9 -10
  12. {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/e2e/test_e2e.py +5 -3
  13. {okf_cli-0.4.3 → okf_cli-0.4.5}/uv.lock +1 -1
  14. okf_cli-0.4.3/.last-update.json +0 -5
  15. {okf_cli-0.4.3 → okf_cli-0.4.5}/.github/workflows/test.yml +0 -0
  16. {okf_cli-0.4.3 → okf_cli-0.4.5}/.gitignore +0 -0
  17. {okf_cli-0.4.3 → okf_cli-0.4.5}/AGENTS.md +0 -0
  18. {okf_cli-0.4.3 → okf_cli-0.4.5}/LICENSE +0 -0
  19. {okf_cli-0.4.3 → okf_cli-0.4.5}/OKF_SPEC.md +0 -0
  20. {okf_cli-0.4.3 → okf_cli-0.4.5}/README.md +0 -0
  21. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/.okfignore +0 -0
  22. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/README.md +0 -0
  23. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/datasets/sales.md +0 -0
  24. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/domain.md +0 -0
  25. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/loose/deep/no-heading.md +0 -0
  26. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/loose/no-desc.md +0 -0
  27. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/loose/no-title.md +0 -0
  28. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/playbooks/incident-response.md +0 -0
  29. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/playbooks/oncall-guide.md +0 -0
  30. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/smoke-ignore.md +0 -0
  31. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/customers.md +0 -0
  32. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/orders.md +0 -0
  33. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/partitions/2026/q1/jan/january.md +0 -0
  34. {okf_cli-0.4.3 → okf_cli-0.4.5}/example/tables/partitions/daily.md +0 -0
  35. {okf_cli-0.4.3 → okf_cli-0.4.5}/skills/okf-cli-manual/SKILL.md +0 -0
  36. {okf_cli-0.4.3 → okf_cli-0.4.5}/skills/writing-knowledge-base/SKILL.md +0 -0
  37. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/__init__.py +0 -0
  38. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/cli.py +0 -0
  39. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/__init__.py +0 -0
  40. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/list.py +0 -0
  41. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/show.py +0 -0
  42. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/commands/validate.py +0 -0
  43. {okf_cli-0.4.3 → okf_cli-0.4.5}/src/okf/core.py +0 -0
  44. {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_list.py +0 -0
  45. {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_show.py +0 -0
  46. {okf_cli-0.4.3 → okf_cli-0.4.5}/tests/cli/test_validate.py +0 -0
  47. {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"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: okf-cli
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: Open Knowledge Format tooling
5
5
  License: MIT License
6
6
 
@@ -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 only `index.md`, `log.md` (`SPEC_RESERVED`).
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 bundled --default-type reference --force
32
- uv run okf validate bundled
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
- - `bundled-smoke/` contains generated sample output used for smoke/reference.
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 bundled --default-type reference --force
15
- uv run okf validate bundled
16
- uv run okf list bundled
17
- uv run okf show bundled tables/customers
18
- uv run okf bundle example bundled --default-type reference --force --strict-links
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> <output-dir> --default-type reference --force
23
- uv run okf bundle <input-dir> <output-dir> --default-type reference --force --strict-links
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 requires `--default-type`, else skipped with warning.
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 bundled-smoke --default-type reference --force
100
- uv run okf validate bundled-smoke
101
- uv run okf list bundled-smoke
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 `bundled/`; see `.gitignore`.)
104
+ (Generated output directory is ignored by git for default `example_knowledge_base/`; see `.gitignore`.)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "okf-cli"
3
- version = "0.4.3"
3
+ version = "0.4.5"
4
4
  description = "Open Knowledge Format tooling"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -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
- "bundled", help="Output directory for the OKF bundle (default: bundled)"
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 (skip root files if omitted)"
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 'bundled'.
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
- if default_type is None:
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 **{dst.name}**.\n\n"
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 test_bundle_skip_root_file_without_default(tmp_path: Path, capsys):
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 not (dst / "standalone.md").exists()
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 only (standalone was skipped)
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" not in root_idx
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 test_bundle_okfignore_skips_root_file_before_default_type_check(tmp_path: Path):
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 test_bundle_skips_root_files_without_default_type(tmp_path: Path):
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 not (dst / "domain.md").exists()
80
- assert "root-level file needs --default-type" in result.output
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):
@@ -52,7 +52,7 @@ wheels = [
52
52
 
53
53
  [[package]]
54
54
  name = "okf-cli"
55
- version = "0.4.1"
55
+ version = "0.4.5"
56
56
  source = { editable = "." }
57
57
  dependencies = [
58
58
  { name = "pyyaml" },
@@ -1,5 +0,0 @@
1
- {
2
- "lastHead": "ddca03b",
3
- "lastRun": "2026-07-11",
4
- "mode": "update"
5
- }
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