okf-cli 0.3.1__tar.gz → 0.4.3__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 (50) hide show
  1. okf_cli-0.4.3/.last-update.json +5 -0
  2. {okf_cli-0.3.1 → okf_cli-0.4.3}/AGENTS.md +53 -11
  3. {okf_cli-0.3.1 → okf_cli-0.4.3}/OKF_SPEC.md +27 -27
  4. {okf_cli-0.3.1 → okf_cli-0.4.3}/PKG-INFO +47 -22
  5. {okf_cli-0.3.1 → okf_cli-0.4.3}/README.md +46 -21
  6. okf_cli-0.4.3/example/.okfignore +3 -0
  7. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/playbooks/incident-response.md +3 -3
  8. okf_cli-0.4.3/example/smoke-ignore.md +5 -0
  9. okf_cli-0.4.3/example/tables/customers.md +16 -0
  10. okf_cli-0.4.3/example/tables/orders.md +17 -0
  11. okf_cli-0.4.3/example/tables/partitions/daily.md +12 -0
  12. okf_cli-0.4.3/openwiki/architecture.md +72 -0
  13. okf_cli-0.4.3/openwiki/domain-model.md +108 -0
  14. okf_cli-0.4.3/openwiki/operations.md +60 -0
  15. okf_cli-0.4.3/openwiki/quickstart.md +40 -0
  16. okf_cli-0.4.3/openwiki/testing.md +61 -0
  17. okf_cli-0.4.3/openwiki/workflows.md +104 -0
  18. {okf_cli-0.3.1 → okf_cli-0.4.3}/pyproject.toml +1 -1
  19. okf_cli-0.4.3/skills/okf-cli-manual/SKILL.md +110 -0
  20. okf_cli-0.4.3/skills/writing-knowledge-base/SKILL.md +61 -0
  21. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/commands/bundle.py +160 -2
  22. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/core.py +4 -1
  23. okf_cli-0.4.3/tests/cli/test_bundle.py +483 -0
  24. okf_cli-0.4.3/tests/cli/test_list.py +93 -0
  25. okf_cli-0.4.3/tests/cli/test_show.py +90 -0
  26. okf_cli-0.4.3/tests/cli/test_validate.py +225 -0
  27. okf_cli-0.4.3/tests/e2e/test_e2e.py +847 -0
  28. okf_cli-0.4.3/tests/test_core.py +216 -0
  29. {okf_cli-0.3.1 → okf_cli-0.4.3}/uv.lock +1 -1
  30. okf_cli-0.3.1/example/tables/customers.md +0 -16
  31. okf_cli-0.3.1/example/tables/orders.md +0 -16
  32. okf_cli-0.3.1/example/tables/partitions/daily.md +0 -12
  33. okf_cli-0.3.1/tests/test_cli.py +0 -825
  34. {okf_cli-0.3.1 → okf_cli-0.4.3}/.github/workflows/test.yml +0 -0
  35. {okf_cli-0.3.1 → okf_cli-0.4.3}/.gitignore +0 -0
  36. {okf_cli-0.3.1 → okf_cli-0.4.3}/LICENSE +0 -0
  37. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/README.md +0 -0
  38. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/datasets/sales.md +0 -0
  39. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/domain.md +0 -0
  40. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/loose/deep/no-heading.md +0 -0
  41. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/loose/no-desc.md +0 -0
  42. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/loose/no-title.md +0 -0
  43. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/playbooks/oncall-guide.md +0 -0
  44. {okf_cli-0.3.1 → okf_cli-0.4.3}/example/tables/partitions/2026/q1/jan/january.md +0 -0
  45. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/__init__.py +0 -0
  46. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/cli.py +0 -0
  47. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/commands/__init__.py +0 -0
  48. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/commands/list.py +0 -0
  49. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/commands/show.py +0 -0
  50. {okf_cli-0.3.1 → okf_cli-0.4.3}/src/okf/commands/validate.py +0 -0
@@ -0,0 +1,5 @@
1
+ {
2
+ "lastHead": "ddca03b",
3
+ "lastRun": "2026-07-11",
4
+ "mode": "update"
5
+ }
@@ -2,6 +2,16 @@
2
2
 
3
3
  This file is for AI agents (and humans) picking up work on `okf-cli`.
4
4
 
5
+ ## OpenWiki (read first)
6
+
7
+ This repository has docs in `openwiki/`.
8
+
9
+ Start here:
10
+
11
+ - [OpenWiki quickstart](openwiki/quickstart.md)
12
+
13
+ Then follow links to architecture, workflows, domain model, operations, and testing notes relevant to task.
14
+
5
15
  ## Project overview
6
16
 
7
17
  `okf-cli` is a Python CLI that converts plain markdown directories into
@@ -9,6 +19,7 @@ This file is for AI agents (and humans) picking up work on `okf-cli`.
9
19
  inspect/validate them.
10
20
 
11
21
  - **Input:** plain `.md` files starting with `# Title` and a `>` description block.
22
+ `bundle` also supports optional `.okfignore` in input root.
12
23
  - **Output:** OKF-conformant markdown with YAML frontmatter, per-directory `index.md` files.
13
24
  - **Operations:** `bundle`, `list`, `show`, `validate`.
14
25
 
@@ -75,12 +86,12 @@ okf-cli
75
86
 
76
87
  ### Commands
77
88
 
78
- | Command | Input expected | Behavior |
79
- |---|---|---|
80
- | `bundle` | non-conformant plain markdown dir | generates OKF bundle; skips `index.md`, `log.md`, `README.md` with warnings; root files need `--default-type`; requires `--force` to overwrite existing output |
81
- | `list` | OKF-conformant bundle | prints concept IDs; exits 1 if dir is not conformant |
82
- | `show` | OKF-conformant bundle | prints concept file by ID; exits 1 if dir is not conformant |
83
- | `validate` | any directory | prints conformance errors and summary per §9 |
89
+ | Command | Input expected | Behavior |
90
+ | ---------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
91
+ | `bundle` | non-conformant plain markdown dir | generates OKF bundle; skips `index.md`, `log.md`, `README.md` with warnings; skips `.okfignore`-matched files; checks local `.md` links (warn by default, fail with `--strict-links`); root files need `--default-type`; requires `--force` to overwrite existing output |
92
+ | `list` | OKF-conformant bundle | prints concept IDs; exits 1 if dir is not conformant |
93
+ | `show` | OKF-conformant bundle | prints concept file by ID; exits 1 if dir is not conformant |
94
+ | `validate` | any directory | prints conformance errors and summary per §9 |
84
95
 
85
96
  ## Key conventions
86
97
 
@@ -92,14 +103,31 @@ okf-cli
92
103
  they only treat `index.md` and `log.md` as reserved per the spec.
93
104
  - `README.md` can be a valid concept in an OKF bundle.
94
105
 
106
+ ### Link checks in `bundle`
107
+
108
+ - `bundle` scans markdown body links for local `.md` targets.
109
+ - Supports relative (`./x.md`, `../x.md`) and bundle-root (`/dir/x.md`) links.
110
+ - Ignores external links (scheme URLs like `https:`/`mailto:`), fragment-only links, directory links, and non-`.md` targets.
111
+ - Default behavior: emit warnings for missing local targets or targets outside bundle.
112
+ - `--strict-links`: treat those link issues as fatal and exit non-zero.
113
+
114
+ ### `.okfignore` (bundle only)
115
+
116
+ - Location: input directory root (`<input-dir>/.okfignore`).
117
+ - Format: one bundle-relative markdown path per line.
118
+ - Example: `tables/orders.md`, `smoke-ignore.md`
119
+ - Blank lines and lines starting with `#` are ignored.
120
+ - Matching is exact path match only (no glob, no negation).
121
+ - If `.okfignore` is non-UTF-8, `bundle` exits with error.
122
+
95
123
  ### OKF conformance (§9)
96
124
 
97
125
  1. Every non-reserved `.md` must have parseable YAML frontmatter.
98
- 2. Every frontmatter must contain a non-empty `type` string.
99
- 3. Reserved filenames (`index.md`, `log.md`) follow structure per §6/§7 when present.
126
+ 1. Every frontmatter must contain a non-empty `type` string.
127
+ 1. Reserved filenames (`index.md`, `log.md`) follow structure per §6/§7 when present.
100
128
  - `index.md` must not contain frontmatter (§6), except root `index.md` may contain only `okf_version` (§11).
101
129
  - `log.md` must not contain frontmatter (§7).
102
- 4. All files must be valid UTF-8.
130
+ 1. All files must be valid UTF-8.
103
131
 
104
132
  `check_conformance()` is the source of truth for this logic.
105
133
 
@@ -137,18 +165,20 @@ Add tests for new behavior in `tests/test_cli.py`. Existing patterns:
137
165
 
138
166
  ```bash
139
167
  uv run okf bundle example bundled --default-type reference --force
168
+ uv run okf bundle example bundled --default-type reference --force --strict-links
140
169
  uv run okf validate bundled
141
170
  uv run okf list bundled
142
171
  ```
143
172
 
144
173
  `bundled/` is gitignored; it is only a local artifact. Use `--force` for re-runs.
174
+ If `example/.okfignore` exists, `bundle` will skip matched files.
145
175
 
146
176
  ### Add a new CLI command
147
177
 
148
178
  1. Create `src/okf/commands/<name>.py` with a function taking `typer` arguments.
149
- 2. Import and register it in `src/okf/cli.py` with `app.command()(fn)` or
179
+ 1. Import and register it in `src/okf/cli.py` with `app.command()(fn)` or
150
180
  `app.command("alias")(fn)`.
151
- 3. Add tests in `tests/test_cli.py`.
181
+ 1. Add tests in `tests/test_cli.py`.
152
182
 
153
183
  ### Modify conformance behavior
154
184
 
@@ -161,3 +191,15 @@ Update `check_conformance()` in `src/okf/core.py`, then ensure
161
191
  - Do not add abstractions "for later".
162
192
  - Use `Path` from `pathlib`, not string path manipulation.
163
193
  - Use `uv` for all package/runtime commands.
194
+
195
+ ## OpenWiki
196
+
197
+ This repository has documentation located in the /openwiki directory.
198
+
199
+ Start here:
200
+
201
+ - [OpenWiki quickstart](openwiki/quickstart.md)
202
+
203
+ OpenWiki includes repository overview, architecture notes, workflows, domain concepts, operations, integrations, testing guidance, and source maps.
204
+
205
+ When working in this repository, read the OpenWiki quickstart first, then follow its links to the relevant architecture, workflow, domain, operation, and testing notes.
@@ -12,7 +12,7 @@ YAML frontmatter. There is no schema registry, no central authority, and
12
12
  no required tooling. If you can `cat` a file, you can read OKF; if you
13
13
  can `git clone` a repo, you can ship it.
14
14
 
15
- ---
15
+ ______________________________________________________________________
16
16
 
17
17
  ## 1. Motivation
18
18
 
@@ -33,9 +33,9 @@ of structural conventions needed to make a knowledge corpus
33
33
  ### Goals
34
34
 
35
35
  1. Define a universal format that **enrichment agents** can write into.
36
- 2. Inform how **consumption agents** should read and traverse it.
37
- 3. Facilitate **exchange** of knowledge across systems and organizations.
38
- 4. Standardize the small number of **required** fields that must be
36
+ 1. Inform how **consumption agents** should read and traverse it.
37
+ 1. Facilitate **exchange** of knowledge across systems and organizations.
38
+ 1. Standardize the small number of **required** fields that must be
39
39
  present for content to be meaningfully consumed.
40
40
 
41
41
  ### Non-goals
@@ -45,7 +45,7 @@ of structural conventions needed to make a knowledge corpus
45
45
  - Replacing domain-specific schemas (Avro, Protobuf, OpenAPI, etc.) —
46
46
  OKF *references* them; it does not subsume them.
47
47
 
48
- ---
48
+ ______________________________________________________________________
49
49
 
50
50
  ## 2. Terminology
51
51
 
@@ -66,7 +66,7 @@ of structural conventions needed to make a knowledge corpus
66
66
  - **Citation** — A link from a concept to an external source that
67
67
  supports a claim in the body.
68
68
 
69
- ---
69
+ ______________________________________________________________________
70
70
 
71
71
  ## 3. Bundle Structure
72
72
 
@@ -97,10 +97,10 @@ A bundle MAY be distributed as:
97
97
  The following filenames have defined meaning at any level of the
98
98
  hierarchy and MUST NOT be used for concept documents:
99
99
 
100
- | Filename | Purpose |
101
- |--------------|--------------------------------------------------------|
102
- | `index.md` | Directory listing. See §6. |
103
- | `log.md` | Update history. See §7. |
100
+ | Filename | Purpose |
101
+ | ---------- | -------------------------- |
102
+ | `index.md` | Directory listing. See §6. |
103
+ | `log.md` | Update history. See §7. |
104
104
 
105
105
  All other `.md` files are concept documents.
106
106
 
@@ -109,7 +109,7 @@ frontmatter field in §4.1. OKF does not specify a separate file format
109
109
  for aggregating documents by tag; producers that want a tag-browsing
110
110
  view can synthesize one at consumption time by scanning frontmatter.
111
111
 
112
- ---
112
+ ______________________________________________________________________
113
113
 
114
114
  ## 4. Concept Documents
115
115
 
@@ -117,7 +117,7 @@ Every concept is a UTF-8 markdown file. It has two parts:
117
117
 
118
118
  1. A **YAML frontmatter block**, delimited by `---` on its own line at
119
119
  the start of the file and a closing `---` on its own line.
120
- 2. A **markdown body**, containing free-form content.
120
+ 1. A **markdown body**, containing free-form content.
121
121
 
122
122
  ### 4.1 Frontmatter
123
123
 
@@ -170,11 +170,11 @@ prose, since structure aids both human reading and agent retrieval.
170
170
  There are no required body sections. The following section headings have
171
171
  **conventional** meaning and SHOULD be used when applicable:
172
172
 
173
- | Heading | Purpose |
174
- |----------------|--------------------------------------------------------|
175
- | `# Schema` | Structured description of an asset's columns/fields. |
176
- | `# Examples` | Concrete usage examples, often as fenced code blocks. |
177
- | `# Citations` | External sources backing claims in the body. See §8. |
173
+ | Heading | Purpose |
174
+ | ------------- | ----------------------------------------------------- |
175
+ | `# Schema` | Structured description of an asset's columns/fields. |
176
+ | `# Examples` | Concrete usage examples, often as fenced code blocks. |
177
+ | `# Citations` | External sources backing claims in the body. See §8. |
178
178
 
179
179
  ### 4.3 Example: a concept bound to a resource
180
180
 
@@ -228,7 +228,7 @@ its expected SLA. See the [orders table](/tables/orders.md).
228
228
  2. …
229
229
  ```
230
230
 
231
- ---
231
+ ______________________________________________________________________
232
232
 
233
233
  ## 5. Cross-linking
234
234
 
@@ -266,7 +266,7 @@ Consumers MUST tolerate broken links — a link whose target does not
266
266
  exist in the bundle is not malformed; it may simply represent
267
267
  not-yet-written knowledge.
268
268
 
269
- ---
269
+ ______________________________________________________________________
270
270
 
271
271
  ## 6. Index Files
272
272
 
@@ -293,7 +293,7 @@ Entries SHOULD include the description from the linked concept's
293
293
  frontmatter. Producers MAY generate `index.md` automatically; consumers
294
294
  MAY synthesize one on the fly when none is present.
295
295
 
296
- ---
296
+ ______________________________________________________________________
297
297
 
298
298
  ## 7. Log Files (optional)
299
299
 
@@ -317,7 +317,7 @@ Date headings MUST use ISO 8601 `YYYY-MM-DD` form. Log entries are
317
317
  prose; the leading bold word (`**Update**`, `**Creation**`,
318
318
  `**Deprecation**`, etc.) is a convention, not a requirement.
319
319
 
320
- ---
320
+ ______________________________________________________________________
321
321
 
322
322
  ## 8. Citations
323
323
 
@@ -336,7 +336,7 @@ Citation links MAY be absolute URLs, bundle-relative paths, or paths
336
336
  into a `references/` subdirectory that mirrors external material as
337
337
  first-class OKF concepts.
338
338
 
339
- ---
339
+ ______________________________________________________________________
340
340
 
341
341
  ## 9. Conformance
342
342
 
@@ -344,8 +344,8 @@ A bundle is **conformant** with OKF v0.1 if:
344
344
 
345
345
  1. Every non-reserved `.md` file in the tree contains a parseable YAML
346
346
  frontmatter block.
347
- 2. Every frontmatter block contains a non-empty `type` field.
348
- 3. Every reserved filename (`index.md`, `log.md`) follows the structure
347
+ 1. Every frontmatter block contains a non-empty `type` field.
348
+ 1. Every reserved filename (`index.md`, `log.md`) follows the structure
349
349
  described in §6 and §7 respectively when present.
350
350
 
351
351
  Consumers SHOULD treat all other constraints as soft guidance. In
@@ -361,7 +361,7 @@ This permissive consumption model is intentional: OKF is meant to
361
361
  remain useful as bundles grow, get refactored, and are partially
362
362
  generated by agents.
363
363
 
364
- ---
364
+ ______________________________________________________________________
365
365
 
366
366
  ## 10. Relationship to other formats
367
367
 
@@ -377,7 +377,7 @@ OKF is intentionally close to several established patterns:
377
377
  OKF differs primarily in being **specified** — pinning down the small
378
378
  set of rules needed for interoperability without dictating tooling.
379
379
 
380
- ---
380
+ ______________________________________________________________________
381
381
 
382
382
  ## 11. Versioning
383
383
 
@@ -395,7 +395,7 @@ only place frontmatter is permitted in an `index.md`). Consumers that
395
395
  do not understand the declared version SHOULD attempt best-effort
396
396
  consumption rather than refusing the bundle.
397
397
 
398
- ---
398
+ ______________________________________________________________________
399
399
 
400
400
  ## Appendix A — Minimal example bundle
401
401
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: okf-cli
3
- Version: 0.3.1
3
+ Version: 0.4.3
4
4
  Summary: Open Knowledge Format tooling
5
5
  License: MIT License
6
6
 
@@ -53,22 +53,45 @@ uv run okf --help
53
53
  ### `okf bundle` — convert plain markdown to OKF bundle
54
54
 
55
55
  ```
56
- okf bundle <input-dir> [output-dir] [--default-type <name>] [--force]
56
+ okf bundle <input-dir> [output-dir] [--default-type <name>] [--force] [--strict-links]
57
57
  ```
58
58
 
59
- | Argument | Description |
60
- |---|---|
61
- | `input-dir` | Directory of plain `.md` files |
62
- | `output-dir` | Target directory (default: `bundled`) |
63
- | `--default-type` | Type for root-level files (skip root files if omitted) |
64
- | `--force`, `-f` | Overwrite output directory if it exists |
59
+ | Argument | Description |
60
+ | ---------------- | ---------------------------------------------------------------------------- |
61
+ | `input-dir` | Directory of plain `.md` files |
62
+ | `output-dir` | Target directory (default: `bundled`) |
63
+ | `--default-type` | Type for root-level files (skip root files if omitted) |
64
+ | `--force`, `-f` | Overwrite output directory if it exists |
65
+ | `--strict-links` | Fail if local markdown links point outside bundle or to missing `.md` target |
65
66
 
66
67
  ```bash
67
68
  okf bundle example --default-type reference # first run → bundled/
68
69
  okf bundle example --default-type reference --force # re-run
70
+ okf bundle example --default-type reference --force --strict-links # fail on broken local .md links
69
71
  cat bundled/tables/orders.md
70
72
  ```
71
73
 
74
+ `.okfignore` (bundle only):
75
+
76
+ - Put `.okfignore` in `input-dir`.
77
+ - Add bundle-relative markdown paths to skip, one per line.
78
+ - Blank lines and `#` comments ignored.
79
+ - Matching is exact path match (no glob, no negation).
80
+
81
+ ```text
82
+ # example/.okfignore
83
+ smoke-ignore.md
84
+ tables/orders.md
85
+ ```
86
+
87
+ Local link checks during `bundle`:
88
+
89
+ - Scans markdown body links to local `.md` targets.
90
+ - Supports relative links (`./x.md`, `../x.md`) and bundle-root links (`/tables/x.md`).
91
+ - Ignores external links (`https:`, `mailto:`), fragment-only links (`#section`), directory links, and non-`.md` targets.
92
+ - Default mode prints warnings for missing targets or links resolving outside bundle.
93
+ - `--strict-links` turns those warnings into a non-zero exit (`Error: strict link check failed`).
94
+
72
95
  ### `okf list` — list concept IDs in a bundle
73
96
 
74
97
  ```
@@ -155,19 +178,20 @@ Everything after the description block is preserved unchanged.
155
178
 
156
179
  Files that don't follow the strict format are still bundled best-effort:
157
180
 
158
- | Condition | Behavior |
159
- |---|---|
160
- | No `# Title` on line 1 | Title omitted from frontmatter |
161
- | No `>` description block | Description derived from first 80 chars of body ("…" if truncated) |
162
- | Root-level file without `--default-type` | Skip file, warn, continue |
181
+ | Condition | Behavior |
182
+ | ---------------------------------------- | ------------------------------------------------------------------ |
183
+ | No `# Title` on line 1 | Title omitted from frontmatter |
184
+ | No `>` description block | Description derived from first 80 chars of body ("…" if truncated) |
185
+ | Root-level file without `--default-type` | Skip file, warn, continue |
163
186
 
164
187
  **Rules:**
165
188
 
166
- | Rule | Why |
167
- |---|---|
168
- | Folder name = concept type | `tables/orders.md` → `type: "tables"` |
169
- | Only `.md` files processed | Non-`.md` files ignored |
189
+ | Rule | Why |
190
+ | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | Folder name = concept type | `tables/orders.md` → `type: "tables"` |
192
+ | Only `.md` files processed | Non-`.md` files ignored |
170
193
  | `index.md`, `log.md`, `README.md` skipped in `bundle` | Input may contain repo artifacts; these are not concepts. `bundle` warns when it skips them. Other commands (`list`, `show`, `validate`) operate on conformant OKF bundles where only `index.md` and `log.md` are reserved. |
194
+ | `.okfignore` entries skipped in `bundle` | Add bundle-relative markdown paths (e.g. `tables/orders.md`) to skip conversion for selected files. Blank lines and `#` comments are ignored. |
171
195
 
172
196
  Root files need `--default-type`. Otherwise put files in named folders.
173
197
 
@@ -175,11 +199,12 @@ See the [`example/`](example/) directory for a sample of how to structure files.
175
199
 
176
200
  ## How `bundle` works
177
201
 
178
- 1. Walk `input-dir` for `.md` files (skip reserved names)
179
- 2. Extract `title` from `#` on line 1, `description` from `>` block. If strict format not met, falls back: title omitted if absent, description from first 80 chars of body
180
- 3. Set `type` from parent dir name, `timestamp` from file mtime
181
- 4. Write concept files with YAML frontmatter (title field omitted if empty)
182
- 5. Generate `index.md` per directory `# Contents` for files, `# Directories` for subdirs (recursive)
202
+ 1. Walk `input-dir` for `.md` files (skip `.okfignore` matches and reserved names)
203
+ 1. Extract `title` from `#` on line 1, `description` from `>` block. If strict format not met, falls back: title omitted if absent, description from first 80 chars of body
204
+ 1. Check local markdown links against planned bundle targets (warn by default, or fail with `--strict-links`)
205
+ 1. Set `type` from parent dir name, `timestamp` from file mtime
206
+ 1. Write concept files with YAML frontmatter (title field omitted if empty)
207
+ 1. Generate `index.md` per directory — `# Contents` for files, `# Directories` for subdirs (recursive)
183
208
 
184
209
  ## Output
185
210
 
@@ -22,22 +22,45 @@ uv run okf --help
22
22
  ### `okf bundle` — convert plain markdown to OKF bundle
23
23
 
24
24
  ```
25
- okf bundle <input-dir> [output-dir] [--default-type <name>] [--force]
25
+ okf bundle <input-dir> [output-dir] [--default-type <name>] [--force] [--strict-links]
26
26
  ```
27
27
 
28
- | Argument | Description |
29
- |---|---|
30
- | `input-dir` | Directory of plain `.md` files |
31
- | `output-dir` | Target directory (default: `bundled`) |
32
- | `--default-type` | Type for root-level files (skip root files if omitted) |
33
- | `--force`, `-f` | Overwrite output directory if it exists |
28
+ | Argument | Description |
29
+ | ---------------- | ---------------------------------------------------------------------------- |
30
+ | `input-dir` | Directory of plain `.md` files |
31
+ | `output-dir` | Target directory (default: `bundled`) |
32
+ | `--default-type` | Type for root-level files (skip root files if omitted) |
33
+ | `--force`, `-f` | Overwrite output directory if it exists |
34
+ | `--strict-links` | Fail if local markdown links point outside bundle or to missing `.md` target |
34
35
 
35
36
  ```bash
36
37
  okf bundle example --default-type reference # first run → bundled/
37
38
  okf bundle example --default-type reference --force # re-run
39
+ okf bundle example --default-type reference --force --strict-links # fail on broken local .md links
38
40
  cat bundled/tables/orders.md
39
41
  ```
40
42
 
43
+ `.okfignore` (bundle only):
44
+
45
+ - Put `.okfignore` in `input-dir`.
46
+ - Add bundle-relative markdown paths to skip, one per line.
47
+ - Blank lines and `#` comments ignored.
48
+ - Matching is exact path match (no glob, no negation).
49
+
50
+ ```text
51
+ # example/.okfignore
52
+ smoke-ignore.md
53
+ tables/orders.md
54
+ ```
55
+
56
+ Local link checks during `bundle`:
57
+
58
+ - Scans markdown body links to local `.md` targets.
59
+ - Supports relative links (`./x.md`, `../x.md`) and bundle-root links (`/tables/x.md`).
60
+ - Ignores external links (`https:`, `mailto:`), fragment-only links (`#section`), directory links, and non-`.md` targets.
61
+ - Default mode prints warnings for missing targets or links resolving outside bundle.
62
+ - `--strict-links` turns those warnings into a non-zero exit (`Error: strict link check failed`).
63
+
41
64
  ### `okf list` — list concept IDs in a bundle
42
65
 
43
66
  ```
@@ -124,19 +147,20 @@ Everything after the description block is preserved unchanged.
124
147
 
125
148
  Files that don't follow the strict format are still bundled best-effort:
126
149
 
127
- | Condition | Behavior |
128
- |---|---|
129
- | No `# Title` on line 1 | Title omitted from frontmatter |
130
- | No `>` description block | Description derived from first 80 chars of body ("…" if truncated) |
131
- | Root-level file without `--default-type` | Skip file, warn, continue |
150
+ | Condition | Behavior |
151
+ | ---------------------------------------- | ------------------------------------------------------------------ |
152
+ | No `# Title` on line 1 | Title omitted from frontmatter |
153
+ | No `>` description block | Description derived from first 80 chars of body ("…" if truncated) |
154
+ | Root-level file without `--default-type` | Skip file, warn, continue |
132
155
 
133
156
  **Rules:**
134
157
 
135
- | Rule | Why |
136
- |---|---|
137
- | Folder name = concept type | `tables/orders.md` → `type: "tables"` |
138
- | Only `.md` files processed | Non-`.md` files ignored |
158
+ | Rule | Why |
159
+ | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
160
+ | Folder name = concept type | `tables/orders.md` → `type: "tables"` |
161
+ | Only `.md` files processed | Non-`.md` files ignored |
139
162
  | `index.md`, `log.md`, `README.md` skipped in `bundle` | Input may contain repo artifacts; these are not concepts. `bundle` warns when it skips them. Other commands (`list`, `show`, `validate`) operate on conformant OKF bundles where only `index.md` and `log.md` are reserved. |
163
+ | `.okfignore` entries skipped in `bundle` | Add bundle-relative markdown paths (e.g. `tables/orders.md`) to skip conversion for selected files. Blank lines and `#` comments are ignored. |
140
164
 
141
165
  Root files need `--default-type`. Otherwise put files in named folders.
142
166
 
@@ -144,11 +168,12 @@ See the [`example/`](example/) directory for a sample of how to structure files.
144
168
 
145
169
  ## How `bundle` works
146
170
 
147
- 1. Walk `input-dir` for `.md` files (skip reserved names)
148
- 2. Extract `title` from `#` on line 1, `description` from `>` block. If strict format not met, falls back: title omitted if absent, description from first 80 chars of body
149
- 3. Set `type` from parent dir name, `timestamp` from file mtime
150
- 4. Write concept files with YAML frontmatter (title field omitted if empty)
151
- 5. Generate `index.md` per directory `# Contents` for files, `# Directories` for subdirs (recursive)
171
+ 1. Walk `input-dir` for `.md` files (skip `.okfignore` matches and reserved names)
172
+ 1. Extract `title` from `#` on line 1, `description` from `>` block. If strict format not met, falls back: title omitted if absent, description from first 80 chars of body
173
+ 1. Check local markdown links against planned bundle targets (warn by default, or fail with `--strict-links`)
174
+ 1. Set `type` from parent dir name, `timestamp` from file mtime
175
+ 1. Write concept files with YAML frontmatter (title field omitted if empty)
176
+ 1. Generate `index.md` per directory — `# Contents` for files, `# Directories` for subdirs (recursive)
152
177
 
153
178
  ## Output
154
179
 
@@ -0,0 +1,3 @@
1
+ # smoke test ignore entries
2
+ smoke-ignore.md
3
+ tables/orders.md
@@ -9,6 +9,6 @@ A freshness alert fires when the [orders](../tables/orders.md) table lags more t
9
9
  ## Steps
10
10
 
11
11
  1. Check the ingestion job dashboard.
12
- 2. Verify upstream source connectivity.
13
- 3. Inspect the [Sales Dataset](../datasets/sales.md) for recent changes.
14
- 4. Notify the data engineering team if the issue persists.
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.
@@ -0,0 +1,5 @@
1
+ # Smoke Ignore
2
+
3
+ > This file should be ignored by bundle during smoke test.
4
+
5
+ If this appears in output, ignore logic failed.
@@ -0,0 +1,16 @@
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`.
@@ -0,0 +1,17 @@
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`.
17
+ Also see [Missing Concept](missing.md) and [Outside](../../secret.md).
@@ -0,0 +1,12 @@
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 |
@@ -0,0 +1,72 @@
1
+ # Architecture
2
+
3
+ ## Runtime shape
4
+
5
+ `okf-cli` is intentionally small: Typer command layer + shared core helpers.
6
+
7
+ - CLI app creation and command registration: `src/okf/cli.py`
8
+ - Core format/parsing/conformance helpers: `src/okf/core.py`
9
+ - Command handlers:
10
+ - `bundle`: `src/okf/commands/bundle.py`
11
+ - `validate`: `src/okf/commands/validate.py`
12
+ - `list`: `src/okf/commands/list.py`
13
+ - `show`: `src/okf/commands/show.py`
14
+
15
+ Why this split exists: business rules live once in `core.py`, while command files mostly handle IO/CLI errors.
16
+
17
+ ## Command execution flow
18
+
19
+ ### `okf bundle`
20
+
21
+ 1. Read source directory + optional `.okfignore` (`_load_okfignore`).
22
+ 1. Walk `*.md`, skipping reserved names and ignored paths.
23
+ 1. Parse each markdown file via `parse_md` (strict first, lenient fallback).
24
+ 1. Scan markdown body links via `_iter_links` / `_resolve_md_target` — warns on missing or out-of-bundle targets; `--strict-links` makes these fatal.
25
+ 1. Build YAML frontmatter via `build_frontmatter`.
26
+ 1. Write transformed files and generate `index.md` per directory.
27
+ 1. Write `AGENTS.md` at output root with navigation guidance for the knowledge base.
28
+
29
+ Source: `src/okf/commands/bundle.py`, `src/okf/core.py`.
30
+
31
+ ### `okf validate`
32
+
33
+ 1. Ensure target directory exists and has markdown files.
34
+ 1. Call `check_conformance` once.
35
+ 1. Print warnings/errors + summary, exit non-zero on errors.
36
+
37
+ Source: `src/okf/commands/validate.py`, `src/okf/core.py`.
38
+
39
+ ### `okf list` and `okf show`
40
+
41
+ Both commands first run `check_conformance`. If directory is non-conformant, they refuse to proceed.
42
+
43
+ Reason: reading APIs should not return misleading data from broken bundles.
44
+
45
+ Source: `src/okf/commands/list.py`, `src/okf/commands/show.py`.
46
+
47
+ ## Shared invariants
48
+
49
+ - Reserved name handling differs by phase:
50
+ - Bundling phase skips `index.md`, `log.md`, `README.md` (`RESERVED`).
51
+ - Spec-conformance phase reserves only `index.md`, `log.md` (`SPEC_RESERVED`).
52
+ - Non-UTF-8 markdown is a conformance error.
53
+ - `type` frontmatter is required and must be non-empty for non-reserved concept files.
54
+
55
+ Source: `src/okf/core.py`.
56
+
57
+ ## Evolution notes (from git history)
58
+
59
+ Major behavior shifts:
60
+
61
+ - project started bundling-focused, then added `validate`/`list`/`show` workflow;
62
+ - frontmatter parsing/conformance matured to real YAML parsing and shared conformance gating;
63
+ - markdown parsing in bundling became lenient to tolerate imperfect source docs;
64
+ - `.okfignore` added to allow selective exclusions without moving/deleting source files.
65
+
66
+ Evidence: `git log -- src/okf/core.py`, `git log -- src/okf/commands/bundle.py`, top-level `git log --oneline`.
67
+
68
+ ## Extension points
69
+
70
+ - New command: add `src/okf/commands/<name>.py`, register in `src/okf/cli.py`, add CLI tests under `tests/cli/`.
71
+ - New conformance rule: implement in `check_conformance` (`src/okf/core.py`) and update validate/list/show expectations.
72
+ - New metadata field support: no schema migration required; parser already tolerates extra YAML keys.