okf-cli 0.4.1__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 (61) hide show
  1. okf_cli-0.4.5/.last-update.json +1 -0
  2. {okf_cli-0.4.1 → okf_cli-0.4.5}/PKG-INFO +1 -1
  3. {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/architecture.md +1 -1
  4. {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/domain-model.md +3 -2
  5. {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/operations.md +3 -4
  6. {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/quickstart.md +5 -5
  7. {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/testing.md +1 -0
  8. {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/workflows.md +8 -8
  9. {okf_cli-0.4.1 → okf_cli-0.4.5}/pyproject.toml +1 -1
  10. okf_cli-0.4.5/skills/writing-knowledge-base/SKILL.md +61 -0
  11. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/bundle.py +34 -20
  12. {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_bundle.py +37 -14
  13. {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/e2e/test_e2e.py +15 -6
  14. {okf_cli-0.4.1 → okf_cli-0.4.5}/uv.lock +1 -1
  15. okf_cli-0.4.1/.last-update.json +0 -5
  16. okf_cli-0.4.1/skills/writing-knowledge-base/SKILL.md +0 -127
  17. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/README.md +0 -12
  18. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/datasets/sales.md +0 -7
  19. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/domain.md +0 -6
  20. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/loose/deep/no-heading.md +0 -4
  21. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/loose/no-desc.md +0 -6
  22. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/loose/no-title.md +0 -5
  23. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/playbooks/incident-response.md +0 -14
  24. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/playbooks/oncall-guide.md +0 -13
  25. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/customers.md +0 -16
  26. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/orders.md +0 -16
  27. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/partitions/2026/q1/jan/january.md +0 -5
  28. okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/partitions/daily.md +0 -12
  29. okf_cli-0.4.1/skills/writing-knowledge-base/reference/knowledge-base.md +0 -65
  30. {okf_cli-0.4.1 → okf_cli-0.4.5}/.github/workflows/test.yml +0 -0
  31. {okf_cli-0.4.1 → okf_cli-0.4.5}/.gitignore +0 -0
  32. {okf_cli-0.4.1 → okf_cli-0.4.5}/AGENTS.md +0 -0
  33. {okf_cli-0.4.1 → okf_cli-0.4.5}/LICENSE +0 -0
  34. {okf_cli-0.4.1 → okf_cli-0.4.5}/OKF_SPEC.md +0 -0
  35. {okf_cli-0.4.1 → okf_cli-0.4.5}/README.md +0 -0
  36. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/.okfignore +0 -0
  37. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/README.md +0 -0
  38. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/datasets/sales.md +0 -0
  39. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/domain.md +0 -0
  40. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/loose/deep/no-heading.md +0 -0
  41. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/loose/no-desc.md +0 -0
  42. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/loose/no-title.md +0 -0
  43. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/playbooks/incident-response.md +0 -0
  44. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/playbooks/oncall-guide.md +0 -0
  45. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/smoke-ignore.md +0 -0
  46. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/customers.md +0 -0
  47. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/orders.md +0 -0
  48. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/partitions/2026/q1/jan/january.md +0 -0
  49. {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/partitions/daily.md +0 -0
  50. {okf_cli-0.4.1 → okf_cli-0.4.5}/skills/okf-cli-manual/SKILL.md +0 -0
  51. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/__init__.py +0 -0
  52. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/cli.py +0 -0
  53. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/__init__.py +0 -0
  54. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/list.py +0 -0
  55. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/show.py +0 -0
  56. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/validate.py +0 -0
  57. {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/core.py +0 -0
  58. {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_list.py +0 -0
  59. {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_show.py +0 -0
  60. {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_validate.py +0 -0
  61. {okf_cli-0.4.1 → 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.1
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.1"
3
+ version = "0.4.5"
4
4
  description = "Open Knowledge Format tooling"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: writing-knowledge-base
3
+ description: Turn user provided content on any topic into a plain Markdown knowledge base. Invoked as /writing-knowledge-base with content to convert.
4
+ license: MIT
5
+ ---
6
+
7
+ # Knowledge Base Construction
8
+
9
+ Convert the user's content directly into a structured Markdown knowledge base. Don't ask clarifying questions unless the content is genuinely unusable (e.g. empty or unintelligible) — infer structure, terminology, and grouping from what's given, and note any gaps inline rather than blocking on them.
10
+
11
+ ## Workflow
12
+
13
+ 1. Read the content and split it into coherent concepts (one idea per file — prefer several small linked files over one large one).
14
+ 1. Create new directory with a name relative to the content user provide (e.g. `italian-food`, `turbocharge`, `formula-one`, etc. ).
15
+ 1. Group concepts into type directories that fit the material's own structure. Let the content dictate the categories — e.g. `recipes/`, `policies/`, `species/`, `procedures/`, `terms/`, `people/`, `events/` — whatever natural groupings emerge. Don't force a data/tech taxonomy onto non-technical content.
16
+ 1. Write each file using the format below, preserving the user's own terminology and facts. Never invent details, numbers, names, or steps that weren't given.
17
+ 1. Add relative Markdown links between related concepts.
18
+ 1. Save all files under an input directory your just created.
19
+
20
+ ## Example file tree
21
+
22
+ ```
23
+ cooking/
24
+ recipes/
25
+ italian/
26
+ lasagna.md
27
+ carbonara.md
28
+ japanese/
29
+ ramen.md
30
+ sushi.md
31
+ techniques/
32
+ knife-skills.md
33
+ emulsion.md
34
+ ingredients/
35
+ eggs.md
36
+ flour-types.md
37
+ faq.md
38
+ common-mistake.md
39
+ ```
40
+
41
+ - Root files (e.g. `faq.md`) are allowed
42
+ - File can be nested as deep as necessary and semantically sound.
43
+
44
+ ## File format
45
+
46
+ ```markdown
47
+ # Clear Concept Title
48
+
49
+ > One-sentence factual summary.
50
+
51
+ Body: context, details, steps, examples, caveats — whatever fits the concept.
52
+ ```
53
+
54
+ - First line `# Title`, then a `>` description, then body detail.
55
+ - Use headings, lists, tables, or code blocks as they help retrieval — not all concepts need all of these.
56
+ - One concept per file. Directory name per type (e.g. `recipes/lasagna.md` → type `recipes`).
57
+ - Lowercase, stable filenames, no spaces.
58
+ - No `index.md`, `log.md`, `README.md` as concepts — bundler reserves these.
59
+ - No YAML frontmatter in source files
60
+
61
+ Do not fabricate any information on your own, use only what user provided to you.
@@ -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,15 +99,26 @@ 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():
106
112
  typer.echo(f"Error: input directory '{input_dir}' not found", err=True)
107
113
  raise typer.Exit(code=1)
108
114
 
115
+ if src.resolve() == dst.resolve():
116
+ typer.echo(
117
+ "Error: input and output directories must be different",
118
+ err=True,
119
+ )
120
+ raise typer.Exit(code=1)
121
+
109
122
  if dst.exists():
110
123
  if not force:
111
124
  typer.echo(
@@ -149,11 +162,7 @@ def bundle(
149
162
  typer.echo("No markdown files found (excluding index.md, log.md)", err=True)
150
163
  raise typer.Exit(code=1)
151
164
 
152
- planned_files = [
153
- f
154
- for f in md_files
155
- if not (f.relative_to(src).parent == Path(".") and default_type is None)
156
- ]
165
+ planned_files = md_files
157
166
  planned_rels = [f.relative_to(src) for f in planned_files]
158
167
  bundle_targets = {rel.as_posix() for rel in planned_rels}
159
168
 
@@ -207,13 +216,7 @@ def bundle(
207
216
 
208
217
  # Determine type
209
218
  if rel.parent == Path("."):
210
- if default_type is None:
211
- typer.echo(
212
- f"Warning: Skipping {rel} — root-level file needs --default-type",
213
- err=True,
214
- )
215
- continue
216
- type_name = default_type
219
+ type_name = default_type or src.name
217
220
  else:
218
221
  type_name = rel.parent.name
219
222
 
@@ -295,13 +298,24 @@ def bundle(
295
298
  # Generate AGENTS.md at bundle root
296
299
  (dst / "AGENTS.md").write_text(
297
300
  f"# Knowledge Base: {dst.name}\n\n"
298
- 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"
299
310
  "## Getting Started\n\n"
300
- "Read [index.md](index.md) first — it lists all concepts and subdirectories.\n\n"
311
+ "Read [index.md](index.md) first — it lists all concepts "
312
+ "and subdirectories.\n\n"
301
313
  "## Navigation\n\n"
302
314
  "- Follow markdown links between concepts.\n"
303
- "- Each `.md` file has YAML frontmatter with `type`, `title`, `description`.\n"
315
+ "- Each `.md` file has YAML frontmatter with `type`, `title`, "
316
+ "`description`.\n"
304
317
  "- Subdirectories group related concepts by topic.\n"
305
- "- Cross-links (e.g. `[Customers](/tables/customers.md)`) express relationships.\n",
318
+ "- Cross-links (e.g. `[Customers](/tables/customers.md)`) "
319
+ "express relationships.\n",
306
320
  encoding="utf-8",
307
321
  )
@@ -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):
@@ -178,6 +180,16 @@ def test_bundle_refuses_overwrite_without_force(tmp_path: Path):
178
180
  assert "--force" in result.output
179
181
 
180
182
 
183
+ def test_bundle_refuses_same_input_and_output_dir(tmp_path: Path):
184
+ src = tmp_path / "notes"
185
+ src.mkdir()
186
+ _write_fixture(src, {"tables/a.md": "# A\n\n> Desc.\n"})
187
+
188
+ result = runner.invoke(app, ["bundle", str(src), str(src), "--force"])
189
+ assert result.exit_code == 1, result.output
190
+ assert "must be different" in result.output
191
+
192
+
181
193
  def test_bundle_no_md_files(tmp_path: Path):
182
194
  src = tmp_path / "notes"
183
195
  dst = tmp_path / "bundle"
@@ -262,7 +274,7 @@ def test_bundle_okfignore_skips_matching_files(tmp_path: Path):
262
274
  assert (dst / "tables" / "customers.md").exists()
263
275
 
264
276
 
265
- 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):
266
278
  src = tmp_path / "notes"
267
279
  dst = tmp_path / "bundle"
268
280
  src.mkdir()
@@ -277,7 +289,6 @@ def test_bundle_okfignore_skips_root_file_before_default_type_check(tmp_path: Pa
277
289
 
278
290
  result = runner.invoke(app, ["bundle", str(src), str(dst)])
279
291
  assert result.exit_code == 0, result.output
280
- assert "root-level file needs --default-type" not in result.output
281
292
  assert not (dst / "standalone.md").exists()
282
293
  assert (dst / "tables" / "data.md").exists()
283
294
 
@@ -342,7 +353,9 @@ def test_bundle_warns_broken_local_markdown_link(tmp_path: Path):
342
353
  _write_fixture(
343
354
  src,
344
355
  {
345
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Customers](customers.md).",
356
+ "tables/orders.md": (
357
+ "# Orders\n\n> One row.\n\nSee [Customers](customers.md)."
358
+ ),
346
359
  },
347
360
  )
348
361
 
@@ -358,8 +371,12 @@ def test_bundle_accepts_valid_relative_and_absolute_local_links(tmp_path: Path):
358
371
  _write_fixture(
359
372
  src,
360
373
  {
361
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Customers](./customers.md).",
362
- "tables/customers.md": "# Customers\n\n> All customers.\n\nSee [Orders](/tables/orders.md).",
374
+ "tables/orders.md": (
375
+ "# Orders\n\n> One row.\n\nSee [Customers](./customers.md)."
376
+ ),
377
+ "tables/customers.md": (
378
+ "# Customers\n\n> All customers.\n\nSee [Orders](/tables/orders.md)."
379
+ ),
363
380
  },
364
381
  )
365
382
 
@@ -397,7 +414,9 @@ def test_bundle_warns_markdown_link_outside_bundle(tmp_path: Path):
397
414
  _write_fixture(
398
415
  src,
399
416
  {
400
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Secret](../../secret.md).",
417
+ "tables/orders.md": (
418
+ "# Orders\n\n> One row.\n\nSee [Secret](../../secret.md)."
419
+ ),
401
420
  },
402
421
  )
403
422
 
@@ -413,7 +432,9 @@ def test_bundle_strict_links_fails_on_broken_local_link(tmp_path: Path):
413
432
  _write_fixture(
414
433
  src,
415
434
  {
416
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Customers](customers.md).",
435
+ "tables/orders.md": (
436
+ "# Orders\n\n> One row.\n\nSee [Customers](customers.md)."
437
+ ),
417
438
  },
418
439
  )
419
440
 
@@ -429,8 +450,10 @@ def test_bundle_strict_links_passes_with_valid_local_links(tmp_path: Path):
429
450
  _write_fixture(
430
451
  src,
431
452
  {
432
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Customers](./customers.md).",
433
- "tables/customers.md": "# Customers\n\n> All customers.\n\nBody.",
453
+ "tables/orders.md": (
454
+ "# Orders\n\n> One row.\n\nSee [Customers](./customers.md)."
455
+ ),
456
+ "tables/customers.md": ("# Customers\n\n> All customers.\n\nBody."),
434
457
  },
435
458
  )
436
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):
@@ -248,7 +250,9 @@ def test_bundle_warns_broken_link(tmp_path: Path):
248
250
  _write(
249
251
  src,
250
252
  {
251
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Customers](customers.md).",
253
+ "tables/orders.md": (
254
+ "# Orders\n\n> One row.\n\nSee [Customers](customers.md)."
255
+ ),
252
256
  },
253
257
  )
254
258
 
@@ -264,7 +268,9 @@ def test_bundle_accepts_valid_links(tmp_path: Path):
264
268
  _write(
265
269
  src,
266
270
  {
267
- "tables/orders.md": "# Orders\n\n> One row.\n\nSee [Customers](./customers.md).",
271
+ "tables/orders.md": (
272
+ "# Orders\n\n> One row.\n\nSee [Customers](./customers.md)."
273
+ ),
268
274
  "tables/customers.md": "# Customers\n\n> All.\n\nBody.",
269
275
  },
270
276
  )
@@ -740,7 +746,10 @@ def test_show_valid_concept(tmp_path: Path):
740
746
 
741
747
 
742
748
  def test_show_with_md_extension_appended(tmp_path: Path):
743
- """show always appends .md — passing tables/orders.md looks for tables/orders.md.md."""
749
+ """show always appends .md.
750
+
751
+ Passing tables/orders.md looks for tables/orders.md.md.
752
+ """
744
753
  d = tmp_path / "bundle"
745
754
  d.mkdir()
746
755
  _write(d, {"tables/orders.md": "---\ntype: tables\n---\n\nBody."})
@@ -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
- }
@@ -1,127 +0,0 @@
1
- ---
2
- name: writing-knowledge-base
3
- description: Help a domain expert turn their knowledge into a plain Markdown knowledge base that can be bundled into OKF with `okf bundle`. Use reference/knowledge-base.md for content guidance and reference/example/ for structure and writing style.
4
- license: MIT
5
- ---
6
-
7
- # Knowledge Base Construction
8
-
9
- Help a domain expert turn their knowledge into a plain Markdown knowledge base that can be bundled into OKF with `okf bundle`. Use `reference/knowledge-base.md` for content guidance and `reference/example/` for structure and writing style.
10
-
11
- ## Role
12
-
13
- Act as facilitator and editor, not an authority on the domain.
14
-
15
- - Ask focused questions when facts, scope, ownership, terminology, or relationships are unclear.
16
- - Never invent domain facts, metrics, schemas, URLs, SLAs, or procedures.
17
- - Preserve the expert's terminology and meaning; improve structure and clarity only.
18
- - Prefer several small, linked concepts over one large document.
19
-
20
- ## Workflow
21
-
22
- 1. Learn the domain, audience, and intended use of the knowledge base.
23
-
24
- 1. Identify concepts and group them into directories such as `datasets/`, `tables/`, `playbooks/`, or other domain-appropriate types.
25
-
26
- 1. Propose a file tree before writing many files. Get expert agreement on names and grouping.
27
-
28
- 1. Draft or update one concept at a time. Show the draft and ask for corrections.
29
-
30
- 1. Add links between related concepts using relative Markdown links.
31
-
32
- 1. Review for missing context, contradictions, stale-looking operational details, and unsupported assumptions.
33
-
34
- 1. Keep source files under a chosen input directory, following `reference/example/`.
35
-
36
- 1. When ready, explain how to bundle and validate:
37
-
38
- ```bash
39
- okf bundle <input-dir> <output-dir> --default-type reference
40
- okf validate <output-dir>
41
- ```
42
-
43
- Use `--default-type` only when root-level files exist. Prefer placing concepts in named directories.
44
-
45
- ## References
46
-
47
- Read these files before drafting when more detail is needed:
48
-
49
- - `reference/knowledge-base.md` — what a good knowledge base and concept file look like.
50
- - `reference/example/` — complete example input structure copied from this project.
51
-
52
- ## File format
53
-
54
- Every concept should normally use this format:
55
-
56
- ```markdown
57
- # Clear Concept Title
58
-
59
- > One-sentence summary of this concept.
60
-
61
- Body content with useful context, structure, examples, procedures, or schema.
62
- ```
63
-
64
- Rules:
65
-
66
- - First line: `# Title`.
67
- - Follow with a `>` description block. Keep it concise and factual.
68
- - Put detailed knowledge in the body after the description.
69
- - Use headings, lists, tables, and code blocks where they help retrieval.
70
- - Use one file per concept.
71
- - Directory name becomes OKF `type`: `tables/orders.md` becomes type `tables`.
72
- - Use lowercase, stable, descriptive filenames. Avoid spaces when possible.
73
- - Do not create `index.md`, `log.md`, or `README.md` as concepts in source input; bundling reserves them.
74
- - Do not add frontmatter to source files. `okf bundle` generates it.
75
-
76
- ## Concept guidance
77
-
78
- For data assets, capture:
79
-
80
- - What asset contains and why it exists.
81
- - Grain or unit of each record.
82
- - Ownership and consumers, if known.
83
- - Schema with field name, type, and meaning.
84
- - Keys, joins, partitions, freshness, quality limits, and known caveats.
85
-
86
- For processes and playbooks, capture:
87
-
88
- - Trigger and scope.
89
- - Ordered steps.
90
- - Decision points and expected outcomes.
91
- - Owners, escalation paths, tools, and safety constraints.
92
- - Links to related assets and procedures.
93
-
94
- For domain concepts, capture:
95
-
96
- - Definition and boundaries.
97
- - Synonyms and terms that should not be confused.
98
- - Examples and counterexamples.
99
- - Related concepts and source evidence.
100
-
101
- ## Questions to ask
102
-
103
- Ask only questions that unblock useful writing. Examples:
104
-
105
- - Who uses this knowledge and what decision should it support?
106
- - What is the smallest useful concept here?
107
- - What does each term mean in this domain?
108
- - What is the source of truth?
109
- - What is the record grain, owner, freshness, or SLA?
110
- - What concepts does this depend on or relate to?
111
- - What should a reader do when this process fails?
112
- - Which details are confirmed, approximate, deprecated, or unknown?
113
-
114
- ## Quality check
115
-
116
- Before declaring the knowledge base ready, check:
117
-
118
- - Every concept has a title and one-sentence description.
119
- - Each file contains one coherent concept.
120
- - Directory grouping reflects meaningful concept types.
121
- - Links point to the correct relative files.
122
- - No facts were added without expert confirmation.
123
- - Procedures include ownership and failure handling where relevant.
124
- - Tables include field meaning, not only field names and types.
125
- - Unknown or disputed details are visible.
126
- - Source remains readable as plain Markdown.
127
- - Generated output passes `okf validate <output-dir>`.
@@ -1,12 +0,0 @@
1
- # OKF Example
2
-
3
- This directory contains plain markdown files written by domain experts.
4
- Run `okf bundle` to convert them into an OKF-conformant knowledge bundle.
5
-
6
- ```bash
7
- # From repo root:
8
- uv run okf bundle example output-bundle
9
-
10
- # With a default type for root-level files (if any):
11
- uv run okf bundle example output-bundle --default-type reference
12
- ```
@@ -1,7 +0,0 @@
1
- # Sales Dataset
2
-
3
- > All sales-related tables for the retail business.
4
-
5
- Contains transactional data from both online and in-store channels.
6
-
7
- Key tables include [orders](../tables/orders.md) and [customers](../tables/customers.md).
@@ -1,6 +0,0 @@
1
- # Our Domain
2
-
3
- > What is our domain of interest
4
-
5
- Our domain of interest is to allow domain expert to easily shared their
6
- knowledge with OKF bundle.
@@ -1,4 +0,0 @@
1
- I am a file deep in a subdirectory with no heading at all.
2
-
3
- Type comes from the directory name, title is omitted, and description is
4
- derived from this body text.
@@ -1,6 +0,0 @@
1
- # Has Title But No Desc
2
-
3
- This file has a proper `# Title` but no `>` blockquote description.
4
-
5
- It should still be bundled: title extracted normally, description taken
6
- from the first 80 characters of the body as a fallback.
@@ -1,5 +0,0 @@
1
- No heading at all — this file has no `# Title` line and no `>` description block.
2
-
3
- It should still be bundled: title omitted from frontmatter, description
4
- derived from the first 80 characters of this body text verbatim, with
5
- newlines collapsed to spaces.
@@ -1,14 +0,0 @@
1
- # Data Freshness Incident
2
-
3
- > Steps to triage a freshness alert on the orders pipeline.
4
-
5
- ## Trigger
6
-
7
- A freshness alert fires when the [orders](../tables/orders.md) table lags more than 30 minutes behind its expected SLA.
8
-
9
- ## Steps
10
-
11
- 1. Check the ingestion job dashboard.
12
- 1. Verify upstream source connectivity.
13
- 1. Inspect the [Sales Dataset](../datasets/sales.md) for recent changes.
14
- 1. Notify the data engineering team if the issue persists.
@@ -1,13 +0,0 @@
1
- # On-call Guide
2
-
3
- > Quick reference for on-call data engineers.
4
-
5
- ## Channels
6
-
7
- - **Critical alerts**: PagerDuty
8
- - **Team chat**: #data-eng on Slack
9
- - **Escalation**: Contact the data engineering lead
10
-
11
- ## Common Issues
12
-
13
- See [Incident Response](incident-response.md) for data freshness issues.
@@ -1,16 +0,0 @@
1
- # Customers
2
-
3
- > One row per registered customer.
4
-
5
- ## Schema
6
-
7
- | Column | Type | Description |
8
- | ----------- | --------- | -------------------------- |
9
- | customer_id | STRING | Unique customer identifier |
10
- | name | STRING | Customer display name |
11
- | email | STRING | Customer email address |
12
- | created_at | TIMESTAMP | Account creation date |
13
-
14
- ## Notes
15
-
16
- Referenced by [orders](orders.md) via `customer_id`.
@@ -1,16 +0,0 @@
1
- # Customer Orders
2
-
3
- > One row per completed customer order across all channels.
4
-
5
- ## Schema
6
-
7
- | Column | Type | Description |
8
- | ----------- | --------- | ------------------------- |
9
- | order_id | STRING | Unique order identifier |
10
- | customer_id | STRING | Foreign key to customers |
11
- | total_usd | NUMERIC | Order total in USD |
12
- | placed_at | TIMESTAMP | When the order was placed |
13
-
14
- ## Notes
15
-
16
- Part of the [Sales Dataset](../datasets/sales.md). Joined with [customers](customers.md) on `customer_id`.
@@ -1,5 +0,0 @@
1
- # January 2026
2
-
3
- > Orders placed in January 2026.
4
-
5
- Monthly slice of the [daily](../../../daily.md) partition.
@@ -1,12 +0,0 @@
1
- # Daily Partition
2
-
3
- > One partition per day for the orders table.
4
-
5
- Part of the [orders](../../tables/orders.md) table's partitioning scheme.
6
-
7
- ## Schema
8
-
9
- | Column | Type | Description |
10
- | -------------- | ----- | ------------------------------- |
11
- | partition_date | DATE | The date of this partition |
12
- | row_count | INT64 | Number of rows in the partition |
@@ -1,65 +0,0 @@
1
- # Knowledge Base Reference
2
-
3
- Knowledge base is plain Markdown organized as small, linked concepts.
4
- Domain expert supplies facts. Agent supplies structure, questions, and editing.
5
-
6
- ## Basic concept
7
-
8
- ```markdown
9
- # Customer Orders
10
-
11
- > One row per completed customer order across all channels.
12
-
13
- ## Schema
14
-
15
- | Column | Type | Description |
16
- |--------|------|-------------|
17
- | order_id | STRING | Unique order identifier |
18
- | customer_id | STRING | Foreign key to customers |
19
- | total_usd | NUMERIC | Order total in USD |
20
-
21
- ## Notes
22
-
23
- Part of the [Sales Dataset](../datasets/sales.md).
24
- ```
25
-
26
- ## Source structure
27
-
28
- ```text
29
- knowledge-base/
30
- ├── domain.md # Optional root-level domain overview
31
- ├── datasets/ # Dataset concepts
32
- │ └── sales.md
33
- ├── tables/ # Table concepts
34
- │ ├── customers.md
35
- │ ├── orders.md
36
- │ └── partitions/
37
- │ └── daily.md
38
- └── playbooks/ # Operational procedure concepts
39
- ├── incident-response.md
40
- └── oncall-guide.md
41
- ```
42
-
43
- Directory names become OKF concept types when bundled. For example,
44
- `tables/orders.md` becomes a concept with type `tables` and ID
45
- `tables/orders`.
46
-
47
- ## Writing rules
48
-
49
- - Use one coherent concept per file.
50
- - Start with `# Title`.
51
- - Follow title with a concise `>` description.
52
- - Put detail in the body.
53
- - Link related concepts with relative Markdown links.
54
- - Use lowercase, stable filenames.
55
- - Do not add YAML frontmatter to source files; bundler generates it.
56
-
57
- ## Data concept checklist
58
-
59
- Capture what asset contains, record grain, owner, consumers, schema, keys,
60
- joins, partitions, freshness, quality limits, and caveats when known.
61
-
62
- ## Playbook checklist
63
-
64
- Capture trigger, scope, ordered steps, decisions, expected outcomes, owners,
65
- escalation, tools, and failure handling when known.
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