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.
- okf_cli-0.4.5/.last-update.json +1 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/PKG-INFO +1 -1
- {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/architecture.md +1 -1
- {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/domain-model.md +3 -2
- {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/operations.md +3 -4
- {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/quickstart.md +5 -5
- {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/testing.md +1 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/openwiki/workflows.md +8 -8
- {okf_cli-0.4.1 → okf_cli-0.4.5}/pyproject.toml +1 -1
- okf_cli-0.4.5/skills/writing-knowledge-base/SKILL.md +61 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/bundle.py +34 -20
- {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_bundle.py +37 -14
- {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/e2e/test_e2e.py +15 -6
- {okf_cli-0.4.1 → okf_cli-0.4.5}/uv.lock +1 -1
- okf_cli-0.4.1/.last-update.json +0 -5
- okf_cli-0.4.1/skills/writing-knowledge-base/SKILL.md +0 -127
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/README.md +0 -12
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/datasets/sales.md +0 -7
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/domain.md +0 -6
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/loose/deep/no-heading.md +0 -4
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/loose/no-desc.md +0 -6
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/loose/no-title.md +0 -5
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/playbooks/incident-response.md +0 -14
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/playbooks/oncall-guide.md +0 -13
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/customers.md +0 -16
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/orders.md +0 -16
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/partitions/2026/q1/jan/january.md +0 -5
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/tables/partitions/daily.md +0 -12
- okf_cli-0.4.1/skills/writing-knowledge-base/reference/knowledge-base.md +0 -65
- {okf_cli-0.4.1 → okf_cli-0.4.5}/.github/workflows/test.yml +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/.gitignore +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/AGENTS.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/LICENSE +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/OKF_SPEC.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/README.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/.okfignore +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/README.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/datasets/sales.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/domain.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/loose/deep/no-heading.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/loose/no-desc.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/loose/no-title.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/playbooks/incident-response.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/playbooks/oncall-guide.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/smoke-ignore.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/customers.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/orders.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/partitions/2026/q1/jan/january.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/example/tables/partitions/daily.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/skills/okf-cli-manual/SKILL.md +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/__init__.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/cli.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/__init__.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/list.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/show.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/commands/validate.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/src/okf/core.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_list.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_show.py +0 -0
- {okf_cli-0.4.1 → okf_cli-0.4.5}/tests/cli/test_validate.py +0 -0
- {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"}
|
|
@@ -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`.)
|
|
@@ -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
|
-
|
|
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,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 '
|
|
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
|
-
|
|
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
|
|
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
|
|
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`,
|
|
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)`)
|
|
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
|
|
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):
|
|
@@ -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
|
|
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":
|
|
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":
|
|
362
|
-
|
|
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":
|
|
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":
|
|
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":
|
|
433
|
-
|
|
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
|
|
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):
|
|
@@ -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":
|
|
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":
|
|
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
|
|
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."})
|
okf_cli-0.4.1/.last-update.json
DELETED
|
@@ -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
|
-
```
|
okf_cli-0.4.1/skills/writing-knowledge-base/reference/example/playbooks/incident-response.md
DELETED
|
@@ -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,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
|
|
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
|