okfcli 0.0.0-stage → 0.1.0

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 (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -2
  3. package/SPEC.md +243 -0
  4. package/dist/bin/okfcli.d.ts +2 -0
  5. package/dist/bin/okfcli.js +9 -0
  6. package/dist/src/cli.d.ts +7 -0
  7. package/dist/src/cli.js +122 -0
  8. package/dist/src/commands/init.d.ts +13 -0
  9. package/dist/src/commands/init.js +78 -0
  10. package/dist/src/commands/new.d.ts +10 -0
  11. package/dist/src/commands/new.js +53 -0
  12. package/dist/src/commands/validate.d.ts +7 -0
  13. package/dist/src/commands/validate.js +49 -0
  14. package/dist/src/commands/verify.d.ts +12 -0
  15. package/dist/src/commands/verify.js +77 -0
  16. package/dist/src/core/frontmatter.d.ts +7 -0
  17. package/dist/src/core/frontmatter.js +27 -0
  18. package/dist/src/core/profile.d.ts +12 -0
  19. package/dist/src/core/profile.js +101 -0
  20. package/dist/src/core/trust.d.ts +10 -0
  21. package/dist/src/core/trust.js +42 -0
  22. package/dist/src/core/types.d.ts +61 -0
  23. package/dist/src/core/types.js +1 -0
  24. package/dist/src/core/util.d.ts +17 -0
  25. package/dist/src/core/util.js +67 -0
  26. package/dist/src/generate/agents.d.ts +24 -0
  27. package/dist/src/generate/agents.js +128 -0
  28. package/dist/src/generate/targets.d.ts +26 -0
  29. package/dist/src/generate/targets.js +18 -0
  30. package/dist/src/generate/templates.d.ts +13 -0
  31. package/dist/src/generate/templates.js +54 -0
  32. package/dist/src/index.d.ts +6 -0
  33. package/dist/src/index.js +7 -0
  34. package/dist/src/wiki/fixes.d.ts +2 -0
  35. package/dist/src/wiki/fixes.js +45 -0
  36. package/dist/src/wiki/indexer.d.ts +5 -0
  37. package/dist/src/wiki/indexer.js +47 -0
  38. package/dist/src/wiki/pages.d.ts +19 -0
  39. package/dist/src/wiki/pages.js +70 -0
  40. package/dist/src/wiki/root.d.ts +1 -0
  41. package/dist/src/wiki/root.js +29 -0
  42. package/dist/src/wiki/rules.d.ts +3 -0
  43. package/dist/src/wiki/rules.js +150 -0
  44. package/package.json +65 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lucas Deprit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,102 @@
1
- # Temporary Holding Version
1
+ # okfcli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/okfcli)](https://www.npmjs.com/package/okfcli)
4
+ [![CI](https://github.com/lucasdeprit/okfcli/actions/workflows/ci.yml/badge.svg)](https://github.com/lucasdeprit/okfcli/actions/workflows/ci.yml)
5
+ [![license](https://img.shields.io/npm/l/okfcli)](LICENSE)
6
+
7
+ Create and maintain project wikis in [Open Knowledge Format (OKF)](https://github.com/GoogleCloudPlatform/open-knowledge-format), with the same structure in every project.
8
+
9
+ ## Commands
10
+
11
+ | Command | What it does |
12
+ |---|---|
13
+ | `okfcli init [folder]` | Create the wiki (default `./wiki`) and connect it to your coding agents (Copilot by default; `--claude`, `--codex`, `--opencode`, `--cursor`) |
14
+ | `okfcli new <type> <name> --description "..."` | Create a page from its template and add it to the index |
15
+ | `okfcli validate [--fix]` | Check the wiki and list what nobody has reviewed. `--fix` repairs the mechanical issues |
16
+ | `okfcli verify <path> --by human:<id>` | Mark a page as reviewed |
17
+
18
+ ```bash
19
+ okfcli init docs/wiki
20
+ okfcli new pattern state-management --description "How screen state is structured."
21
+ okfcli validate --fix
22
+ okfcli verify patterns/state-management --by human:ana
23
+ ```
24
+
25
+ - **Types for `new`:** `overview` `architecture` `pattern` `convention` `workflow` `testing` `feature` `module` `interface` `operation` `troubleshooting`
26
+ - **Every command:** `--json` for machine-readable output, `--wiki <folder>` to pick a wiki (otherwise it is found from the current directory)
27
+ - **Help:** `okfcli --help`, `okfcli <command> --help`
28
+
29
+ ## Install
30
+
31
+ Needs Node 22 or later.
32
+
33
+ ```bash
34
+ npm install -g okfcli # gives you the `okfcli` command
35
+ npx okfcli init docs/wiki # or run it without installing
36
+ ```
37
+
38
+ Pin a version with `npm install -g okfcli@0.1.0`. To upgrade, install again, then run `okfcli init` in each project to refresh the generated agent files.
39
+
40
+ ## What you get
41
+
42
+ ```
43
+ docs/wiki/
44
+ ├── index.md
45
+ ├── .okf-profile.yaml settings: extra folders, rule severities
46
+ ├── overview/ what the project is
47
+ ├── architecture/ structure and rules
48
+ ├── patterns/ how we implement things
49
+ ├── conventions/ style and naming
50
+ ├── workflows/ how we work
51
+ ├── testing/ how we test
52
+ └── (features/ modules/ interfaces/ operations/ troubleshooting/) optional, created by `okfcli new`
53
+ ```
54
+
55
+ Every folder has an `index.md`, generated for you. Every page has `type`, `title` and `description`, plus the sections its type requires.
56
+
57
+ ## How it works
58
+
59
+ - **The CLI does the mechanical work:** folders, indexes, page skeletons, structure checks. Nobody edits `index.md` by hand.
60
+ - **Agents write the content:** they fill in the sections of the pages `okfcli new` creates.
61
+ - **People review:** a page is unreviewed until someone runs `okfcli verify` (or writes the `verified` entry by hand). If a verified page changes afterwards, `validate` reports it as outdated.
62
+ - **No history files:** the history of the wiki is left to git.
63
+
64
+ ## Agents
65
+
66
+ `okfcli init` also writes, relative to the directory where you run it, the files that connect the wiki to your coding agents. Pass one flag per tool you use; without any flag it connects GitHub Copilot:
67
+
68
+ ```bash
69
+ okfcli init # Copilot
70
+ okfcli init --claude # Claude Code
71
+ okfcli init --claude --codex # several at once
72
+ ```
73
+
74
+ | Flag | Pointer block in | Skills in |
75
+ |---|---|---|
76
+ | `--copilot` (default) | `AGENTS.md` (or `.github/copilot-instructions.md` if that is what the project uses) | `.github/skills/` |
77
+ | `--claude` | `CLAUDE.md` | `.claude/skills/` |
78
+ | `--codex`, `--opencode`, `--cursor` | `AGENTS.md` | `.agents/skills/` |
79
+
80
+ The pointer block makes the agent always know the wiki exists. The three skills are `okf-consult-wiki`, `okf-update-wiki` and `okf-review-wiki`. `okfcli` never deletes files, so files of an agent you stop passing stay where they are.
81
+
82
+ The skills call `okfcli`, so install it globally on the machines where agents run. Run `okfcli init` again to refresh these files; it never touches the wiki pages. `--no-agents` skips them.
83
+
84
+ ## Contract
85
+
86
+ The rules a wiki must follow (folders, page types, required sections, review, checks) are in [SPEC.md](https://github.com/lucasdeprit/okfcli/blob/main/SPEC.md).
87
+
88
+ ## Development
89
+
90
+ ```bash
91
+ npm install
92
+ npm test # builds to dist/ and runs the tests
93
+ node dist/bin/okfcli.js --help # run from a clone
94
+ ```
95
+
96
+ ## Contributing
97
+
98
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Security issues: [SECURITY.md](SECURITY.md).
99
+
100
+ ## License
101
+
102
+ [MIT](LICENSE)
package/SPEC.md ADDED
@@ -0,0 +1,243 @@
1
+ # Project wiki contract on top of OKF (draft 0.1)
2
+
3
+ Status: **draft under review**. This defines what a wiki must satisfy; the `okfcli` CLI implements and enforces it.
4
+
5
+ ## 1. Goal
6
+
7
+ Every project wiki must have the same shape, so that:
8
+
9
+ - an agent (or a person) knows where to look in any project without knowing it beforehand;
10
+ - agents can maintain the wiki by following fixed rules;
11
+ - the structure lasts over time without growing in a disorderly way.
12
+
13
+ ### Principle: the CLI does the mechanical work, the agent only writes content
14
+
15
+ Anything that can be decided with rules is done by the CLI, deterministically and without spending tokens. The agent is limited to what requires judgement: writing the content of the pages.
16
+
17
+ | The CLI does | The agent does |
18
+ |---|---|
19
+ | Create folders and `index.md` | Decide what deserves a page |
20
+ | Create a page with frontmatter and a section skeleton | Fill in the sections |
21
+ | Register the page in the `index.md` of its folder | Write the `# Why` and the examples |
22
+ | Stamp `verified` with actor, date and content hash | Decide whether something should be marked `deprecated` |
23
+ | Validate structure, sections, links and indexes | Review content against the code |
24
+ | Fix only the mechanical issues (`validate --fix`) | Fix the content problems that `validate` reports |
25
+ | Fix only the mechanical issues (`validate --fix`) | |
26
+
27
+ Design consequences:
28
+
29
+ - One command per operation. `new` creates the page and registers it in the index, so the agent does not have to edit two files.
30
+ - The skills generated for agents tell them to **use the CLI** and not to create or hand-edit `index.md` or the `verified` entries.
31
+ - `validate` messages say which file, which rule and how to fix it, so the agent reads only what it needs.
32
+ - `validate` and `index` can run in CI or in a hook without any agent.
33
+
34
+ ## 2. Layers
35
+
36
+ 1. **OKF v0.2** (Google, [open-knowledge-format](https://github.com/GoogleCloudPlatform/open-knowledge-format)): the base format. A wiki is a *bundle*: a tree of `.md` files with YAML frontmatter and reserved `index.md` (and, optionally, `log.md`).
37
+ 2. **Base profile** (this document): additional rules, stricter than OKF.
38
+ 3. **Project extension**: folders and rules specific to a project, without removing those of the base profile.
39
+
40
+ A wiki that breaks the profile but satisfies OKF is still valid OKF.
41
+
42
+ ## 3. Structure
43
+
44
+ ```
45
+ wiki/
46
+ ├── index.md
47
+ ├── .okf-profile.yaml
48
+ ├── overview/ # core
49
+ ├── architecture/ # core
50
+ ├── patterns/ # core
51
+ ├── conventions/ # core
52
+ ├── workflows/ # core
53
+ ├── testing/ # core
54
+ ├── features/ # optional
55
+ ├── modules/ # optional
56
+ ├── interfaces/ # optional
57
+ ├── operations/ # optional
58
+ └── troubleshooting/ # optional
59
+ ```
60
+
61
+ Folder names are fixed and do not depend on any technology. Whatever is specific to a project (a framework, an internal package) goes in pages, not in folders of the standard.
62
+
63
+ ### General rules
64
+
65
+ - Every folder has an `index.md`.
66
+ - File and folder names in `kebab-case`, without repeating the folder name (`modules/auth/core.md`, not `modules/auth/auth-core.md`).
67
+ - Own folders can be added (declared in `extra_folders`); core folders cannot be removed or renamed.
68
+ - There is no decisions folder. The reason for a rule lives in the `# Why` section of its page. What changes is edited in the page; the history is left to git. What stops applying is marked `status: deprecated`.
69
+ - Page content can be in any language. The `type` names, frontmatter fields and required section names are in English.
70
+
71
+ ### Folder, `type` and minimum sections
72
+
73
+ | Folder | `type` | Sections required in every page |
74
+ |---|---|---|
75
+ | `overview/` | `Overview` | `# Purpose`, `# Scope` |
76
+ | `architecture/` | `Architecture` | `# Overview`, `# Rules`, `# Why` |
77
+ | `patterns/` | `Pattern` | `# When to use`, `# How`, `# Example`, `# Why` |
78
+ | `conventions/` | `Convention` | `# Rule`, `# Examples` |
79
+ | `workflows/` | `Workflow` | `# Goal`, `# Steps` |
80
+ | `testing/` | `Testing` | `# Scope`, `# How to run`, `# Rules` |
81
+ | `features/` | `Feature` | `# Summary`, `# Behavior` |
82
+ | `modules/` | `Module` | `# Responsibility`, `# Public API` |
83
+ | `interfaces/` | `Interface` | `# Contract`, `# Consumers` |
84
+ | `operations/` | `Operation` | `# When`, `# Steps` |
85
+ | `troubleshooting/` | `Troubleshooting` | `# Symptom`, `# Cause`, `# Fix` |
86
+
87
+ A page may have more sections. Only these are required.
88
+
89
+ ### Required pages
90
+
91
+ - `overview/project.md`: what the project is.
92
+ - `workflows/update-wiki.md`: when and how the wiki is updated (see section 6).
93
+ - `testing/` must contain at least one testing strategy page (so `testing/index.md` links to it). `okfcli init` scaffolds `testing/strategy.md` (title, frontmatter and the required sections, unfilled); any page in `testing/` satisfies the rule, so a project may rename or replace it.
94
+
95
+ ## 4. Frontmatter
96
+
97
+ Every concept page (not `index.md`) carries:
98
+
99
+ | Field | Requirement | Notes |
100
+ |---|---|---|
101
+ | `type` | error if missing | Must match the type of its folder |
102
+ | `title` | error if missing | |
103
+ | `description` | error if missing | One sentence |
104
+ | `tags` | optional | List of strings |
105
+ | `verified` | see below | List of `{ by, at, hash }`: who reviewed the page, when, and a fingerprint of the content they read |
106
+ | `status` | optional | Only `deprecated` is acted upon; `draft` and `stable` are accepted (absent = `stable`) |
107
+
108
+ There are no `generated` or `stale_after` fields: nothing expires by date, and who created or changed a page is left to git.
109
+
110
+ ### Review: `verified`
111
+
112
+ Every page is born **unreviewed**. A page counts as reviewed when it has a valid `verified` entry:
113
+
114
+ - **Who decides.** A person, not the tool. A person verifies either by running `okfcli verify <path> --by human:<id>` or by writing the entry by hand in the page frontmatter. Agents may help (the review skill walks the person through the unreviewed pages), but they only run `okfcli verify --by human:<id>` after the person has confirmed.
115
+ - **Tiers** (from OKF): no entry = *unverified*; only machine actors = *machine-confirmed*; any `human:<id>` = *human-reviewed*.
116
+ - **Outdated.** `okfcli verify` stores a `hash` of the content that was read (frontmatter except `verified` and `status`, plus body). If the page is edited afterwards the hash no longer matches and the verification is reported as *outdated* until it is verified again. Entries without a hash (written by hand) are always accepted and never go outdated.
117
+ - **Nagging.** `validate` warns about unreviewed and outdated pages, so a team can see what nobody has read yet. Projects that want to enforce review can raise `frontmatter.verified` and `frontmatter.verified-outdated` to `error` in their profile. Deprecated pages are not asked to be reviewed.
118
+ - **Deprecating.** `status: deprecated` is only ever set by a person or an agent editing the page; the tool never deprecates anything by itself.
119
+
120
+ Actor convention (OKF): `human:<id>`, `process:<id>` or `<tool>/<version>`.
121
+
122
+ ## 5. `index.md`
123
+
124
+ **`index.md`** (every folder). No frontmatter, except the root one, which carries `okf_version: "0.2"`. One line per page, as OKF asks:
125
+
126
+ ```markdown
127
+ # Patterns
128
+
129
+ * [State management](/patterns/state-management.md) - How the state of a screen is structured.
130
+ ```
131
+
132
+ Indexes are generated by `okfcli index` and are never edited by hand. Every page of the folder must appear in its `index.md`, and every entry must point to a page that exists.
133
+
134
+ **`log.md`.** OKF reserves this name for an update history, but `okfcli` does not create or manage it: the history of a wiki is left to git. If a project keeps its own `log.md`, it is never treated as a concept page.
135
+
136
+ ## 6. Maintenance by agents
137
+
138
+ Agents update the wiki. `workflows/update-wiki.md` defines the standard procedure:
139
+
140
+ 1. If a change affects a rule, a pattern or a convention, the affected page is edited; a new one is not added.
141
+ 2. The `index.md` of the folder is updated (by `okfcli`, never by hand).
142
+ 3. The page stays unreviewed until a person verifies it; the agent does not verify its own work as a person.
143
+ 4. If a page stops applying, it is marked `status: deprecated`.
144
+
145
+ `workflows/` links to `patterns/`: every workflow step that depends on a way of implementing something links to its pattern.
146
+
147
+ ## 7. Configuration: `.okf-profile.yaml`
148
+
149
+ ```yaml
150
+ profile: base@0.1 # base profile and version
151
+ language: en # language of the content (informational)
152
+ severity: # overrides the severity of a rule: error | warning | off
153
+ frontmatter.verified: error
154
+ frontmatter.verified-outdated: error
155
+ extra_folders: # project-specific folders
156
+ - name: mobile
157
+ type: Mobile
158
+ sections: ["# Summary"]
159
+ ```
160
+
161
+ ## 8. Checks (`validate`)
162
+
163
+ Every rule has a stable id that can be used in `severity`.
164
+
165
+ | Rule id | Check | Default severity | Fixed by `--fix` |
166
+ |---|---|---|---|
167
+ | `okf.frontmatter` | Concept pages have parseable YAML frontmatter | error | |
168
+ | `okf.type` | `type` is present and non-empty | error | |
169
+ | `okf.index` | `index.md` has no frontmatter (the root one only `okf_version`) | error | |
170
+ | `structure.core-folder` | Every core folder exists | error | yes |
171
+ | `structure.required-page` | Required pages exist | error | yes |
172
+ | `structure.testing-page` | `testing/` has at least one strategy page | error | yes (scaffolds `testing/strategy.md`) |
173
+ | `structure.unknown-folder` | Pages live in a core, optional or declared folder | warning | |
174
+ | `page.type` | `type` matches the type of the folder | error | |
175
+ | `page.sections` | Required sections are present | error | yes |
176
+ | `page.todo` | No unfilled `<!-- TODO -->` placeholders | warning | |
177
+ | `frontmatter.title` | `title` present | error | |
178
+ | `frontmatter.description` | `description` present | error | |
179
+ | `frontmatter.status` | `status`, if present, is `draft`, `stable` or `deprecated` | error | |
180
+ | `frontmatter.verified` | Page has been reviewed (has a `verified` entry) | warning | |
181
+ | `frontmatter.verified-outdated` | The page has not changed since it was verified | warning | |
182
+ | `name.kebab-case` | File and folder names are `kebab-case` | error | |
183
+ | `index.out-of-date` | Every `index.md` matches the pages in its folder: no page missing from it, no entry pointing to a page that no longer exists, no folder without an `index.md` | error | yes |
184
+ | `link.broken` | Internal links point to existing pages | warning | |
185
+ | `page.orphan` | Some other concept page links to it | warning | |
186
+
187
+ `validate` exits with code 1 if there is any error (`--strict`: any warning too). `--json` prints every issue with `rule`, `severity`, `file`, `message` and `fix`.
188
+
189
+ ## 9. CLI (Node, `npx`)
190
+
191
+ | Command | What it does |
192
+ |---|---|
193
+ | `init` | Creates the wiki with the core, the indexes and `.okf-profile.yaml` |
194
+ | `new <type> <name>` | Creates a page with its template and frontmatter and registers it in the index |
195
+ | `validate` | Runs the checks of section 8, including which pages nobody has reviewed or that changed after being verified, with a review summary; `--fix` repairs the mechanical issues (folders, required pages, section skeletons, indexes) |
196
+ | `verify <path>` | Records a review in `verified` (`--by <actor>`, one entry per actor) with a hash of the content. Refuses pages with unfilled TODOs unless `--force` |
197
+
198
+ There are deliberately only four commands: people should not have to learn an ecosystem. Indexes are kept up to date by `new` and `validate --fix`, so there is no `index` command; the review state is part of `validate`, so there is no `status` command; the agent files are refreshed by running `init` again, so there is no `update` command.
199
+
200
+ ## 10. Agent integration
201
+
202
+ As OpenSpec does, the connection with agents is made with **generated files** in the paths each tool reads, plus a CLI with `--json` output. There is no MCP server and no hooks in the first version.
203
+
204
+ Supported agents, chosen with one flag each on `init` (`--copilot` is the default when none is given). Several can be combined; agents that share a destination write it once:
205
+
206
+ | Flag | Instructions file (carries the pointer block) | Skills folder |
207
+ |---|---|---|
208
+ | `--copilot` | `AGENTS.md`, or `.github/copilot-instructions.md` if the project already uses it | `.github/skills/` |
209
+ | `--claude` | `CLAUDE.md` (or `.claude/CLAUDE.md` if that is the one that exists) | `.claude/skills/` |
210
+ | `--codex` | `AGENTS.md` | `.agents/skills/` |
211
+ | `--opencode` | `AGENTS.md` | `.agents/skills/` |
212
+ | `--cursor` | `AGENTS.md` | `.agents/skills/` |
213
+
214
+ Notes on Claude Code: it reads `AGENTS.md` only when the project has no `CLAUDE.md` at all, so with `--claude` the block goes into `CLAUDE.md`. If `CLAUDE.md` does not exist yet and `AGENTS.md` does (or is written in the same run), the new `CLAUDE.md` starts with `@AGENTS.md`, so it never hides the project's own instructions. Combining `--copilot --claude` therefore shows the block in both files, which is harmless. `init` never deletes files: those of an agent you stop passing stay where they are. The Cursor skills path comes from third-party documentation and is the least verified.
215
+
216
+ The rest of this section describes the files for GitHub Copilot in VS Code (paths verified in the VS Code documentation, vscode-docs, October 2026); the other agents get the same files at their own paths:
217
+
218
+ | Piece | File generated by `init` (running it again refreshes them) | Purpose |
219
+ |---|---|---|
220
+ | Pointer to the wiki | Marked block inside `AGENTS.md` (or `.github/copilot-instructions.md` if the project already uses it) | So the agent always knows the wiki exists and consults it before touching code |
221
+ | Consult skill | `.github/skills/okf-consult-wiki/SKILL.md` | Read `index.md` and drill into the relevant pages |
222
+ | Update skill | `.github/skills/okf-update-wiki/SKILL.md` | Edit pages in place, create new ones with `okfcli new`, then `okfcli validate --fix` and fix what it reports (so there is no separate validation skill) |
223
+ | Review skill | `.github/skills/okf-review-wiki/SKILL.md` | List unreviewed pages with `validate --json`, walk the person through them one by one, and run `verify --by human:<id>` only after the person confirms |
224
+
225
+ Notes on Copilot:
226
+
227
+ - **Skills** (`SKILL.md` with required `name` and `description`, `name` equal to the folder name) follow the open standard agentskills.io. Copilot reads them in `.github/skills/`, `.claude/skills/` and `.agents/skills/`, and they show up as `/` commands in chat. They are used instead of *prompt files* (`.github/prompts/*.prompt.md`), which only work in the Local harness.
228
+ - `AGENTS.md` is read by Copilot, and also by Codex and others. In the Local harness it depends on the `chat.useAgentsMdFile` setting.
229
+ - `.github/copilot-instructions.md` is also read, with `github.copilot.chat.codeGeneration.useInstructionFiles`.
230
+ - A skill is activated by its `description` and the agent decides whether to load it; that is why the pointer exists, since it is always loaded.
231
+ - Generated files belong to the CLI: running `init` again regenerates them (and never touches the pages). Project content goes outside the markers. Each skill carries a header comment saying so.
232
+ - Paths are relative to the directory where `init` runs (the project root). The pointer block goes in the file that already has it, else in `AGENTS.md` if it exists, else in `.github/copilot-instructions.md` if it exists, else a new `AGENTS.md`.
233
+ - The wiki path is written into the generated files, and the update skill lists the types and sections of every folder of the profile, including `extra_folders`.
234
+ - `--no-agents` skips all of this, whatever other agent flags are present.
235
+
236
+ Output for agents: `--json` on every command. `new` already returns the sections to fill in, so no separate command is needed to get a page template.
237
+
238
+ ## 11. Open questions
239
+
240
+ - Minimum sections per folder: are they the right ones or too rigid?
241
+ - Is `# Why` required in `conventions/`? Currently not.
242
+ - Is a folder needed for raw sources (original documents the wiki is derived from)?
243
+ - Exact template of `workflows/update-wiki.md`.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { run } from '../src/cli.js';
3
+ run(process.argv.slice(2)).then((code) => {
4
+ process.exitCode = code;
5
+ }, (error) => {
6
+ console.error(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);
7
+ console.error('This is a bug in okfcli; please report it with the command you ran.');
8
+ process.exitCode = 2;
9
+ });
@@ -0,0 +1,7 @@
1
+ interface RunOptions {
2
+ cwd?: string;
3
+ out?: (text: string) => void;
4
+ err?: (text: string) => void;
5
+ }
6
+ export declare function run(argv: string[], { cwd, out, err }?: RunOptions): Promise<number>;
7
+ export {};
@@ -0,0 +1,122 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { readFileSync } from 'node:fs';
3
+ import { UserError, errorCode } from './core/util.js';
4
+ import { findWikiRoot } from './wiki/root.js';
5
+ import { init, initHelp } from './commands/init.js';
6
+ import { AGENT_NAMES, DEFAULT_AGENTS } from './generate/targets.js';
7
+ import { newPage, newHelp } from './commands/new.js';
8
+ import { validateCommand, validateHelp } from './commands/validate.js';
9
+ import { verifyCommand, verifyHelp } from './commands/verify.js';
10
+ // The compiled file lives in dist/src/, two levels below package.json.
11
+ const { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
12
+ const HELP = `okfcli ${version} - scaffolding and management of OKF wikis
13
+
14
+ Commands:
15
+ init [folder] Create a wiki and connect it to your agents (${AGENT_NAMES.map((n) => `--${n}`).join(' ')}; default: ${DEFAULT_AGENTS.join(', ')})
16
+ new <type> <name> Create a page and register it in its index
17
+ validate [--fix] Check the wiki (structure, indexes, review state); --fix repairs the mechanical issues
18
+ verify <path> Record that a page has been reviewed
19
+
20
+ Common options:
21
+ --wiki <folder> Wiki to operate on (by default it is searched from the current directory)
22
+ --json JSON output, for agents and scripts
23
+ -h, --help Help for a command
24
+ -v, --version
25
+
26
+ Run "okfcli <command> --help" for details.`;
27
+ const COMMON = {
28
+ wiki: { type: 'string' },
29
+ json: { type: 'boolean' },
30
+ help: { type: 'boolean', short: 'h' },
31
+ };
32
+ const str = (v) => (typeof v === 'string' ? v : undefined);
33
+ const flag = (v) => v === true;
34
+ // The agents named by flags; undefined (not an empty list) means none was given, so init uses its default.
35
+ const pickedAgents = (values) => {
36
+ const picked = AGENT_NAMES.filter((name) => flag(values[name]));
37
+ return picked.length ? picked : undefined;
38
+ };
39
+ const COMMANDS = {
40
+ init: {
41
+ help: initHelp,
42
+ options: {
43
+ with: { type: 'string' },
44
+ language: { type: 'string' },
45
+ 'no-agents': { type: 'boolean' },
46
+ // One boolean flag per supported agent (--claude, --codex, ...).
47
+ ...Object.fromEntries(AGENT_NAMES.map((name) => [name, { type: 'boolean' }])),
48
+ },
49
+ run: ({ values, positionals, cwd }) => init({
50
+ cwd,
51
+ dir: positionals[0],
52
+ withFolders: str(values.with)?.split(',').map((s) => s.trim()).filter(Boolean) ?? [],
53
+ language: str(values.language),
54
+ agents: flag(values['no-agents']) ? [] : pickedAgents(values),
55
+ version,
56
+ }),
57
+ },
58
+ new: {
59
+ help: newHelp,
60
+ options: { title: { type: 'string' }, description: { type: 'string' } },
61
+ run: ({ values, positionals, root }) => {
62
+ const wiki = root();
63
+ const [type, name] = positionals;
64
+ if (!type || !name)
65
+ throw new UserError('Usage: okfcli new <type> <name> --description "..."');
66
+ return newPage({ root: wiki, type, name, title: str(values.title), description: str(values.description) });
67
+ },
68
+ },
69
+ verify: {
70
+ help: verifyHelp,
71
+ options: { by: { type: 'string' }, force: { type: 'boolean' } },
72
+ run: ({ values, positionals, root, cwd }) => verifyCommand({ root: root(), cwd, path: positionals[0], by: str(values.by), force: flag(values.force), version }),
73
+ },
74
+ validate: {
75
+ help: validateHelp,
76
+ options: { fix: { type: 'boolean' }, strict: { type: 'boolean' } },
77
+ run: ({ values, root }) => validateCommand({ root: root(), fix: flag(values.fix), strict: flag(values.strict) }),
78
+ },
79
+ };
80
+ export async function run(argv, { cwd = process.cwd(), out = console.log, err = console.error } = {}) {
81
+ const [name, ...rest] = argv;
82
+ if (name === undefined || name === '-h' || name === '--help' || name === 'help') {
83
+ out(HELP);
84
+ return 0;
85
+ }
86
+ if (name === '-v' || name === '--version') {
87
+ out(version);
88
+ return 0;
89
+ }
90
+ const command = Object.hasOwn(COMMANDS, name) ? COMMANDS[name] : undefined;
91
+ if (!command) {
92
+ err(`Unknown command: ${name}\n\n${HELP}`);
93
+ return 1;
94
+ }
95
+ try {
96
+ const { values, positionals } = parseArgs({
97
+ args: rest,
98
+ options: { ...COMMON, ...command.options },
99
+ allowPositionals: true,
100
+ });
101
+ if (values.help) {
102
+ out(command.help);
103
+ return 0;
104
+ }
105
+ const result = await command.run({ values, positionals, cwd, root: () => findWikiRoot(cwd, str(values.wiki)) });
106
+ if (values.json) {
107
+ const { text: _text, ...data } = result;
108
+ out(JSON.stringify(data, null, 2));
109
+ }
110
+ else {
111
+ out(result.text);
112
+ }
113
+ return result.ok === false ? 1 : 0;
114
+ }
115
+ catch (e) {
116
+ if (e instanceof UserError || errorCode(e)?.startsWith('ERR_PARSE_ARGS')) {
117
+ err(`Error: ${e.message}`);
118
+ return 1;
119
+ }
120
+ throw e;
121
+ }
122
+ }
@@ -0,0 +1,13 @@
1
+ import { type AgentName } from '../generate/targets.js';
2
+ import type { CommandResult } from '../core/types.js';
3
+ export declare const initHelp: string;
4
+ export interface InitOptions {
5
+ cwd: string;
6
+ dir?: string | undefined;
7
+ withFolders?: string[];
8
+ language?: string | undefined;
9
+ /** Agents to connect. Default: Copilot. An empty list generates no agent files. */
10
+ agents?: AgentName[] | undefined;
11
+ version: string;
12
+ }
13
+ export declare function init({ cwd, dir, withFolders, language, agents, version }: InitOptions): CommandResult;
@@ -0,0 +1,78 @@
1
+ import { existsSync, mkdirSync } from 'node:fs';
2
+ import { join, relative, resolve } from 'node:path';
3
+ import { stringify } from 'yaml';
4
+ import { CORE_FOLDERS, OPTIONAL_FOLDERS, PROFILE_FILE, PROFILE_ID, loadProfile } from '../core/profile.js';
5
+ import { regenerateIndexes } from '../wiki/indexer.js';
6
+ import { requiredPages, testingStrategyPage } from '../generate/templates.js';
7
+ import { writeAgentFiles } from '../generate/agents.js';
8
+ import { AGENT_NAMES, AGENT_TARGETS, DEFAULT_AGENTS } from '../generate/targets.js';
9
+ import { findWikiRoot } from '../wiki/root.js';
10
+ import { UserError, toPosix, writeText } from '../core/util.js';
11
+ export const initHelp = `okfcli init [folder] [--with features,modules] [--language en] ${AGENT_NAMES.map((n) => `[--${n}]`).join(' ')} [--no-agents]
12
+
13
+ Creates a wiki with the base profile core (in ./wiki by default) and the files
14
+ that connect it to your coding agents, relative to the current directory:
15
+ a pointer block to the wiki in the instructions file the agent loads
16
+ okf-consult-wiki, okf-update-wiki, okf-review-wiki skills (consult, update and review the wiki)
17
+
18
+ Pass the agents you use; without any flag it connects ${DEFAULT_AGENTS.join(', ')}:
19
+ ${AGENT_NAMES.map((n) => ` --${n.padEnd(10)} ${AGENT_TARGETS[n].instructions[0]} + ${AGENT_TARGETS[n].skillsDir}/`).join('\n')}
20
+ Flags can be combined (okfcli init --claude --codex). okfcli never deletes
21
+ files, so files of an agent you stop passing stay where they are.
22
+
23
+ Run it again in a project that already has a wiki to refresh those files (for
24
+ example after upgrading okfcli). It never touches the wiki pages.
25
+ --with optional folders to create now: ${OPTIONAL_FOLDERS.map((f) => f.name).join(', ')}
26
+ --language language of the wiki content, informational only (default: en)
27
+ --no-agents do not generate the agent files`;
28
+ function findExisting(cwd, dir) {
29
+ if (dir !== undefined) {
30
+ const root = resolve(cwd, dir);
31
+ return existsSync(join(root, PROFILE_FILE)) ? root : null;
32
+ }
33
+ try {
34
+ return findWikiRoot(cwd);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ function createWiki(root, withFolders, language) {
41
+ const known = new Map(OPTIONAL_FOLDERS.map((f) => [f.name, f]));
42
+ for (const name of withFolders) {
43
+ if (!known.has(name)) {
44
+ throw new UserError(`"${name}" is not an optional folder. Options: ${[...known.keys()].join(', ')}.`);
45
+ }
46
+ }
47
+ const profileText = `# okfcli profile of this wiki. See https://github.com/lucasdeprit/okfcli/blob/main/SPEC.md\n${stringify({ profile: PROFILE_ID, language, extra_folders: [] })}`;
48
+ writeText(join(root, PROFILE_FILE), profileText);
49
+ const created = [PROFILE_FILE];
50
+ for (const folder of [...CORE_FOLDERS, ...withFolders.map((n) => known.get(n))]) {
51
+ mkdirSync(join(root, folder.name), { recursive: true });
52
+ created.push(`${folder.name}/`);
53
+ }
54
+ for (const page of [...requiredPages(), testingStrategyPage()]) {
55
+ writeText(join(root, page.path), page.text);
56
+ created.push(page.path);
57
+ }
58
+ created.push(...regenerateIndexes(root, loadProfile(root)));
59
+ return created;
60
+ }
61
+ export function init({ cwd, dir, withFolders = [], language = 'en', agents = DEFAULT_AGENTS, version }) {
62
+ const existing = findExisting(cwd, dir);
63
+ if (existing && withFolders.length) {
64
+ throw new UserError('A wiki already exists here: --with only applies when creating one. Add folders by creating pages with "okfcli new".');
65
+ }
66
+ const root = existing ?? resolve(cwd, dir ?? 'wiki');
67
+ const wikiRel = toPosix(relative(cwd, root)) || '.';
68
+ const created = existing ? [] : createWiki(root, withFolders, language);
69
+ const agentFiles = agents.length ? writeAgentFiles({ projectRoot: cwd, wiki: wikiRel, profile: loadProfile(root), version, agents }) : [];
70
+ const lines = [];
71
+ if (existing)
72
+ lines.push(`A wiki already exists in ${wikiRel}; its pages were not touched.`);
73
+ else
74
+ lines.push(`Wiki created in ${wikiRel}`, ...created.map((c) => ` ${c}`));
75
+ if (agentFiles.length)
76
+ lines.push('', 'Agent files:', ...agentFiles.map((f) => ` ${f.status.padEnd(9)} ${f.path}`));
77
+ return { root, created, agent_files: agentFiles, text: lines.join('\n') };
78
+ }
@@ -0,0 +1,10 @@
1
+ import type { CommandResult } from '../core/types.js';
2
+ export declare const newHelp = "okfcli new <type> <name> --description \"...\" [--title \"...\"]\n\nCreates a page with frontmatter and a section skeleton, and registers it in\nthe index.md of its folder. The name is relative to the folder of the type\n(e.g. \"state-management\" or \"ui/forms\"). The type is the singular name: overview, architecture, pattern, convention, workflow,\ntesting, feature, module, interface, operation, troubleshooting.";
3
+ export interface NewPageOptions {
4
+ root: string;
5
+ type: string;
6
+ name: string;
7
+ title?: string | undefined;
8
+ description?: string | undefined;
9
+ }
10
+ export declare function newPage({ root, type, name, title, description }: NewPageOptions): CommandResult;