agent-docs-bootstrap 0.2.0 → 0.4.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 (30) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +32 -14
  3. package/THIRD_PARTY.md +8 -6
  4. package/agents/docs-scout.md +0 -1
  5. package/install.mjs +89 -30
  6. package/package.json +3 -3
  7. package/skills/docs-bootstrap/SKILL.md +11 -7
  8. package/skills/docs-bootstrap/references/scout-briefs.md +2 -2
  9. package/skills/docs-bootstrap/references/signals.md +1 -1
  10. package/skills/docs-check/SKILL.md +3 -3
  11. package/skills/docs-check/scripts/reader-run.mjs +18 -7
  12. package/skills/third-party/grill-with-docs/SKILL.md +0 -7
  13. package/skills/third-party/grill-with-docs/agents/openai.yaml +0 -5
  14. package/skills/third-party/writing-for-agents/LICENSE +0 -21
  15. /package/skills/{third-party → docs-bootstrap/vendor}/diataxis-audit/LICENSE +0 -0
  16. /package/skills/{third-party/diataxis-audit/SKILL.md → docs-bootstrap/vendor/diataxis-audit/diataxis-audit.md} +0 -0
  17. /package/skills/{third-party → docs-bootstrap/vendor}/doc-coauthoring/LICENSE +0 -0
  18. /package/skills/{third-party/doc-coauthoring/SKILL.md → docs-bootstrap/vendor/doc-coauthoring/doc-coauthoring.md} +0 -0
  19. /package/skills/{third-party → docs-bootstrap/vendor}/domain-modeling/ADR-FORMAT.md +0 -0
  20. /package/skills/{third-party → docs-bootstrap/vendor}/domain-modeling/CONTEXT-FORMAT.md +0 -0
  21. /package/skills/{third-party → docs-bootstrap/vendor}/domain-modeling/LICENSE +0 -0
  22. /package/skills/{third-party → docs-bootstrap/vendor}/domain-modeling/agents/openai.yaml +0 -0
  23. /package/skills/{third-party/domain-modeling/SKILL.md → docs-bootstrap/vendor/domain-modeling/domain-modeling.md} +0 -0
  24. /package/skills/{third-party/grill-with-docs → docs-bootstrap/vendor/grilling}/LICENSE +0 -0
  25. /package/skills/{third-party → docs-bootstrap/vendor}/grilling/agents/openai.yaml +0 -0
  26. /package/skills/{third-party/grilling/SKILL.md → docs-bootstrap/vendor/grilling/grilling.md} +0 -0
  27. /package/skills/{third-party/grilling → docs-bootstrap/vendor/writing-for-agents}/LICENSE +0 -0
  28. /package/skills/{third-party → docs-bootstrap/vendor}/writing-for-agents/SKILL-MECHANICS.md +0 -0
  29. /package/skills/{third-party → docs-bootstrap/vendor}/writing-for-agents/agents/openai.yaml +0 -0
  30. /package/skills/{third-party/writing-for-agents/SKILL.md → docs-bootstrap/vendor/writing-for-agents/writing-for-agents.md} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versions follo
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.4.0] - 2026-10-27
8
+
9
+ ### Changed
10
+ - `.agent-docs-bootstrap` lines are `<path> <content hash>`. A path is replaced or removed only if its content still matches what the kit wrote; lines without a hash, from 0.3.0 and earlier, are checked against the hashes of everything those versions installed.
11
+
12
+ ### Fixed
13
+ - An update or `--uninstall` removed a listed path whatever was in it, so a skill the user had replaced with their own version under the same name, such as `grilling` after 0.2.0, was deleted. A changed path is now left in place as the user's, with a warning, and drops out of the marker. Subagent files a 0.3.0 or earlier install rendered with `--scout-model` or `--reader-model` do not match the known hashes and are left in place too: delete them and run the installer again.
14
+
15
+ ## [0.3.0] - 2026-10-27
16
+
17
+ ### Changed
18
+ - Third-party skills are bundled inside `docs-bootstrap` as guides in `vendor/<name>/`, read by path, instead of being installed as separate skills. Each `SKILL.md` is renamed to `<name>.md`, content unchanged: Codex and OpenCode find a nested `SKILL.md` and register it as a skill, and OpenCode silently keeps one of two skills with the same name. A user's own `grilling` or `domain-modeling` no longer clashes with the kit's copy. The install now has two skills, `docs-bootstrap` and `docs-check`.
19
+ - The installer removes paths an earlier version installed into the same targets and this version no longer ships, so an update from 0.2.0 drops the old third-party skill folders.
20
+ - README, package description and keywords: a set of agent skills for any agent that runs them; the installer sets up Claude Code, Codex and OpenCode. New "Other agents" section. `docs-check` falls back to `--snapshot-only` for any other harness.
21
+ - `docs-scout` for OpenCode no longer needs the `diataxis-audit` skill permission; the brief gives it the guide's path.
22
+
23
+ ### Fixed
24
+ - `--uninstall` and updates removed every path listed in `.agent-docs-bootstrap`, which is committed, so a line added by a bad merge (`src`, `../x`) deleted project or outside folders. Only lines shaped like a path the kit installs, `<layout folder>/<kit name>`, are now trusted; others are ignored with a warning and dropped from the marker. `reader-run.mjs` applies the same rule, so a stray `docs` line no longer hides every document from the snapshot.
25
+ - `docs-check` snapshot: a nested `CLAUDE.md` came from the parent commit instead of the working tree. `README` stays from the parent commit on purpose, now stated in the skill.
26
+ - README no longer says the Codex reader has no access to the repository: its read-only sandbox blocks writes, not reading other folders.
27
+ - `docs-check`: global instructions wording no longer claims `claude -p --restricted` loads `~/.claude/CLAUDE.md`.
28
+ - `reader-run.mjs`: `--timeout-min` is documented; on Windows the timeout stops the CLI, not only its shell; snapshot folders of two repositories with the same name no longer collide.
29
+ - `install.mjs` accepts `--flag=value` as well as `--flag value`.
30
+
31
+ ### Removed
32
+ - `grill-with-docs`: the kit did not use it, and it only calls the `grilling` and `domain-modeling` skills, which are no longer installed as skills.
33
+
7
34
  ## [0.2.0] - 2026-10-27
8
35
 
9
36
  ### Added
package/README.md CHANGED
@@ -4,7 +4,9 @@
4
4
  [![check](https://github.com/TraneDev/agent-docs-bootstrap/actions/workflows/check.yml/badge.svg)](https://github.com/TraneDev/agent-docs-bootstrap/actions/workflows/check.yml)
5
5
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
6
6
 
7
- **agent-docs-bootstrap** is a set of skills and subagents for **Claude Code**, **Codex** and **OpenCode**. It interviews your team and writes the glossary, decision records, architecture, how-tos and `AGENTS.md` that coding agents and new developers need. If you want to see how well they work, an **optional** check has a fresh agent plan a task from the docs alone: one you bring, or real past tasks from your git history compared with the commits that solved them.
7
+ **agent-docs-bootstrap** is a set of agent skills and instructions, not tied to one tool. It interviews your team and writes the glossary, decision records, architecture, how-tos and `AGENTS.md` that coding agents and new developers need. If you want to see how well they work, an **optional** check has a fresh agent plan a task from the docs alone: one you bring, or real past tasks from your git history compared with the commits that solved them.
8
+
9
+ The installer sets up **Claude Code**, **Codex** and **OpenCode**. Any other agent that runs skills can use the two skill folders as they are: see [Other agents](#other-agents).
8
10
 
9
11
  [Quick start](#quick-start) · [How it works](#how-it-works) · [Optional: test the docs](#optional-test-the-docs-with-docs-check) · [Install options](#install-options) · [Harness differences](#harness-differences)
10
12
 
@@ -15,7 +17,7 @@ You need Node.js 18+ and a git repository.
15
17
  1. From your project root, install the skills and subagents:
16
18
 
17
19
  ```bash
18
- npx agent-docs-bootstrap@0.2.0 --target claude
20
+ npx agent-docs-bootstrap@0.4.0 --target claude
19
21
  ```
20
22
 
21
23
  Replace `claude` with `codex`, `opencode`, a list such as `claude,codex`, or `all`.
@@ -79,7 +81,7 @@ flowchart LR
79
81
 
80
82
  1. The agent picks three real past tasks from git history, preferring commits made after the docs were written, and drops any whose solution is already in the code or the docs.
81
83
  2. For each task, the code is extracted **as it was before the task**, outside your repository, and your current documents from the working tree go on top.
82
- 3. A fresh reader plans the task. It runs as a **separate process** of your harness (`claude -p`, `codex exec`, `opencode run`), read-only, with no history, no MCP tools and no access to your repository: a subagent of the same session could find the solution in your current code.
84
+ 3. A fresh reader plans the task. It runs as a **separate process** of your harness (`claude -p`, `codex exec`, `opencode run`), read-only, with no history and no MCP tools, in a snapshot outside your repository: a subagent of the same session could find the solution in your current code. Claude Code and OpenCode keep the reader's file access inside the snapshot; Codex's read-only sandbox blocks writes but not reading elsewhere, so there only the reader's instructions keep it in the snapshot (see [Harness differences](#harness-differences)).
83
85
  4. Its plan is compared with what the real commit changed. Missed files, missed verification commands, wrong terms, rejected approaches and guesses are **gaps**.
84
86
  5. The gaps become one list of fixes, grouped by document. You strike out what came from a poor commit rather than the docs, and the rest is applied.
85
87
 
@@ -101,19 +103,19 @@ All options run the same installer from your project root. Pin the version: the
101
103
  **From npm** (recommended):
102
104
 
103
105
  ```bash
104
- npx agent-docs-bootstrap@0.2.0 --target claude
106
+ npx agent-docs-bootstrap@0.4.0 --target claude
105
107
  ```
106
108
 
107
109
  **From a GitHub release tag**, when the npm registry is blocked or unavailable:
108
110
 
109
111
  ```bash
110
- npx github:TraneDev/agent-docs-bootstrap#v0.2.0 --target claude
112
+ npx github:TraneDev/agent-docs-bootstrap#v0.4.0 --target claude
111
113
  ```
112
114
 
113
115
  **From a clone**, to read or change the kit first:
114
116
 
115
117
  ```bash
116
- git clone --branch v0.2.0 https://github.com/TraneDev/agent-docs-bootstrap.git ~/agent-docs-bootstrap
118
+ git clone --branch v0.4.0 https://github.com/TraneDev/agent-docs-bootstrap.git ~/agent-docs-bootstrap
117
119
  node ~/agent-docs-bootstrap/install.mjs --target claude
118
120
  ```
119
121
 
@@ -127,11 +129,26 @@ Files are copied into the project, not linked, so they get committed with it.
127
129
  | `codex` | `.agents/skills/` | `.codex/agents/*.toml` |
128
130
  | `opencode` | `.agents/skills/` | `.opencode/agents/*.md`, plus `/docs-bootstrap` and `/docs-check` commands |
129
131
 
130
- `.agent-docs-bootstrap` lists every installed path. The installer replaces or removes only those paths and never touches files it did not create.
132
+ Two skills are installed, `docs-bootstrap` and `docs-check`. The guides by other authors that `docs-bootstrap` relies on travel inside it, in `docs-bootstrap/vendor/`, and are not registered as skills, so a skill of yours with the same name, such as `grilling`, never clashes with them.
133
+
134
+ `.agent-docs-bootstrap` lists every installed path with a hash of what was written. The installer replaces or removes a path only if it is listed there and its content still matches, so it never touches files it did not create or that you changed:
135
+
136
+ - a path that exists but is not listed is skipped with a warning;
137
+ - a listed path whose content you changed, for example a skill you replaced with your own version, is left in place as yours, with a warning, and drops out of the marker;
138
+ - a marker line not shaped like a path the kit installs (for example `src` or `../x`, added by a bad merge) is ignored with a warning.
139
+
140
+ ### Other agents
141
+
142
+ The skills are plain `SKILL.md` folders with Markdown references and templates. For an agent the installer does not know, copy `skills/docs-bootstrap` and `skills/docs-check` from a clone (see above) into the folder where that agent looks for skills, and start it with "document this project". An agent without skill support can be pointed at `docs-bootstrap/SKILL.md` directly.
143
+
144
+ What changes without the installer's targets:
145
+
146
+ - **No subagents.** `docs-bootstrap` does the scout's briefs itself and keeps only summaries in the conversation.
147
+ - **`docs-check`** builds the snapshot with `reader-run.mjs --snapshot-only`; you start a fresh session of your agent in that folder and paste the task. Starting the reader automatically works only with `claude`, `codex` and `opencode`.
131
148
 
132
149
  ### Update or remove
133
150
 
134
- - **Update:** run the installer again with the new version and commit the result.
151
+ - **Update:** run the installer again with the new version and commit the result. Paths the previous version installed and this one no longer ships are removed. Do not install an older version over a newer one: an older installer does not read the newer marker and treats the kit's files as yours. Run `--uninstall` with the newer version first.
135
152
  - **Remove:** run it with `--uninstall`. Everything listed in `.agent-docs-bootstrap` is deleted, along with the marker itself.
136
153
 
137
154
  <details>
@@ -155,19 +172,20 @@ The protocol is the same everywhere; how strictly each harness can enforce the l
155
172
 
156
173
  | Guarantee | Claude Code | Codex | OpenCode |
157
174
  |---|---|---|---|
158
- | Reader cannot see the solution or history | Snapshot outside the repo, no history; `--restricted` keeps file tools inside it | Snapshot outside the repo, no history | Snapshot outside the repo, no history; `external_directory: deny` |
175
+ | Reader cannot see the solution or history | Snapshot outside the repo, no history; `--restricted` keeps file tools inside it | Snapshot outside the repo, no history; reading other folders is not blocked, only the prompt forbids it | Snapshot outside the repo, no history; `external_directory: deny` |
159
176
  | Reader cannot edit or run commands | `--restricted --tools Read,Grep,Glob` | `-s read-only` sandbox | Permissions (`"*": deny`) |
160
177
  | Scout writes only to the scratch folder | Instructions (edit tool removed) | Instructions only | Path permissions |
161
178
  | External docs are never modified | Instructions | Instructions | Instructions |
179
+ | Scout reads the bundled guides | File tools | File tools | Read permission; a `--global` install is outside the project, so `external_directory` asks once |
162
180
 
163
181
  Where a guarantee is "instructions only", tighten it with your harness's own permission settings if you need to. For example, deny the write tools of your wiki's MCP server.
164
182
 
165
183
  ## Repository layout
166
184
 
167
185
  ```text
168
- skills/docs-bootstrap/ orchestrator: SKILL.md, references/ (formats, scout briefs), templates/
186
+ skills/docs-bootstrap/ orchestrator: SKILL.md, references/ (formats, scout briefs), templates/,
187
+ vendor/ (guides by other authors, each with its LICENSE)
169
188
  skills/docs-check/ optional reader test: SKILL.md, templates/, scripts/reader-run.mjs (snapshot and reader process)
170
- skills/third-party/ skills by other authors, unmodified, each with its LICENSE
171
189
  agents/ docs-scout, docs-reader: one prompt + per-harness frontmatter blocks
172
190
  package.json npm package metadata and the version
173
191
  install.mjs installer (the package's `bin`)
@@ -178,12 +196,12 @@ To work on the kit itself, start with [AGENTS.md](AGENTS.md) and run `node scrip
178
196
 
179
197
  ## Credits
180
198
 
181
- The workflow builds on skills by other authors, bundled unmodified under their licenses. Pinned commits and update steps are in [THIRD_PARTY.md](THIRD_PARTY.md).
199
+ The workflow builds on skills by other authors, bundled inside `docs-bootstrap` under their licenses, unmodified except that each `SKILL.md` is renamed so it does not register as a separate skill. Pinned commits and update steps are in [THIRD_PARTY.md](THIRD_PARTY.md).
182
200
 
183
- - Matt Pocock, [mattpocock/skills](https://github.com/mattpocock/skills): `domain-modeling`, `grilling`, `grill-with-docs` and `writing-for-agents` (MIT).
201
+ - Matt Pocock, [mattpocock/skills](https://github.com/mattpocock/skills): `domain-modeling`, `grilling` and `writing-for-agents` (MIT).
184
202
  - Georges Martin, [jrjsmrtn/diataxis-skills](https://github.com/jrjsmrtn/diataxis-skills): `diataxis-audit` (MIT).
185
203
  - Anthropic, [anthropics/skills](https://github.com/anthropics/skills): `doc-coauthoring` (Apache 2.0).
186
204
 
187
205
  ## License
188
206
 
189
- [MIT](LICENSE) for the kit's own files. Third-party skills keep their own licenses.
207
+ [MIT](LICENSE) for the kit's own files. Bundled third-party guides keep their own licenses.
package/THIRD_PARTY.md CHANGED
@@ -1,12 +1,13 @@
1
- # Third-party skills
1
+ # Third-party guides
2
2
 
3
- Copies of skills by other authors, pinned to a commit and **not modified**. Each folder under `skills/third-party/` carries the license of its source, so a skill copied on its own keeps it.
3
+ Copies of skills by other authors, pinned to a commit and **not modified**, bundled inside `docs-bootstrap` as guides it reads by path. Each folder under `skills/docs-bootstrap/vendor/` carries the license of its source.
4
+
5
+ The only change is a rename: each `SKILL.md` becomes `<name>.md`. Codex and OpenCode look for `SKILL.md` recursively inside skill folders, and Claude Code does not document how deep it looks, so a bundled `SKILL.md` would register as a separate skill and clash with a user's own skill of the same name. Other files keep their names, so links between them still resolve.
4
6
 
5
7
  | Skill | Source | Commit | Author | License |
6
8
  |---|---|---|---|---|
7
9
  | `domain-modeling` | [mattpocock/skills](https://github.com/mattpocock/skills), `skills/engineering/domain-modeling` | `3cca18b` | Matt Pocock | MIT |
8
10
  | `grilling` | [mattpocock/skills](https://github.com/mattpocock/skills), `skills/productivity/grilling` | `3cca18b` | Matt Pocock | MIT |
9
- | `grill-with-docs` | [mattpocock/skills](https://github.com/mattpocock/skills), `skills/engineering/grill-with-docs` | `3cca18b` | Matt Pocock | MIT |
10
11
  | `writing-for-agents` | [mattpocock/skills](https://github.com/mattpocock/skills), `skills/productivity/writing-for-agents` | `3cca18b` | Matt Pocock | MIT |
11
12
  | `diataxis-audit` | [jrjsmrtn/diataxis-skills](https://github.com/jrjsmrtn/diataxis-skills), `skills/diataxis-audit` | `4f36079` | Georges Martin | MIT |
12
13
  | `doc-coauthoring` | [anthropics/skills](https://github.com/anthropics/skills), `skills/doc-coauthoring` | `34040c9` | Anthropic | Apache 2.0 |
@@ -17,17 +18,18 @@ Full commit hashes: `3cca18b368ae95cdbdebbff572ccafa662551015`, `4f36079fd36e498
17
18
 
18
19
  ## Updating
19
20
 
20
- Fresh clone of the source at the new commit → diff against the copy → review for harmful or harness-specific instructions → replace the folder (keep its `LICENSE`) → update the table. `node scripts/check.mjs` verifies every folder has a `LICENSE` and a row here.
21
+ Fresh clone of the source at the new commit → diff against the copy (compare the source's `SKILL.md` with `<name>.md`) → review for harmful or harness-specific instructions → replace the folder, rename `SKILL.md` to `<name>.md`, keep its `LICENSE` → update the table. `node scripts/check.mjs` verifies every folder has `<name>.md`, a `LICENSE`, a row here and no `SKILL.md`.
21
22
 
22
23
  ## Known quirks
23
24
 
24
- - `grill-with-docs` sets `disable-model-invocation: true`: Claude Code and Codex (via `agents/openai.yaml`) only run it when the user asks; OpenCode ignores the field. The kit's own workflow does not depend on it.
25
25
  - `doc-coauthoring`: its "Reader Testing" step suggests opening a new chat on claude.ai. In this kit that step is skipped (the optional `docs-check` skill covers it); only the Context Gathering and Refinement stages are used.
26
26
  - `diataxis-audit` links to `diataxis-create`, `diataxis-convert` and `diataxis-plan`, which are not bundled; those links do not resolve, which is expected.
27
- - `agents/openai.yaml` inside Matt Pocock's skills is Codex UI metadata; other harnesses ignore it.
27
+ - `writing-for-agents/SKILL-MECHANICS.md` links back to `SKILL.md`, which is `writing-for-agents.md` here; that file is about writing skills and is not used for `AGENTS.md`.
28
+ - `agents/openai.yaml` inside Matt Pocock's skills is Codex UI metadata for a skill; as bundled guides they are inert.
28
29
 
29
30
  ## Considered and not bundled
30
31
 
31
32
  - `setup-matt-pocock-skills` — writes its own config and creates issues via `gh`/`glab`; its role (wiring docs into the other skills) is played by `AGENTS.md` here.
32
33
  - `improve-codebase-architecture`, `codebase-design` — not about documentation; they work well on top of a finished `CONTEXT.md` and ADRs.
33
34
  - `diataxis-create`, `diataxis-convert`, `diataxis-plan` — not needed by the workflow.
35
+ - `grill-with-docs` — bundled up to 0.2.0; it only calls the `grilling` and `domain-modeling` skills, which are no longer installed as skills.
@@ -45,7 +45,6 @@ opencode: |
45
45
  "*": deny
46
46
  "docs-bootstrap": allow
47
47
  "docs-check": allow
48
- "diataxis-audit": allow
49
48
  ---
50
49
 
51
50
  You are a scout. Your job is to collect facts about the project and return them to the lead agent. You write to the repository only in the scratch folder `docs/agents/scratch/`, when the brief asks for a file.
package/install.mjs CHANGED
@@ -10,13 +10,17 @@
10
10
  // claude skills → .claude/skills/ agents → .claude/agents/*.md
11
11
  // codex skills → .agents/skills/ agents → .codex/agents/*.toml
12
12
  // opencode skills → .agents/skills/ agents → .opencode/agents/*.md commands → .opencode/commands/*.md
13
- // Installed paths are listed in .agent-docs-bootstrap; only those are ever replaced or removed.
13
+ // Installed paths are listed in .agent-docs-bootstrap with a hash of what was written. A path is replaced or removed
14
+ // only if it is listed, has the shape of a path the kit writes (<layout folder>/<kit name>), and its content still
15
+ // matches that hash; a changed path is left in place as the user's. On an update, listed paths this version no
16
+ // longer ships are removed under the same rule.
14
17
  // No network access, no changes to harness settings, no admin rights.
15
18
 
16
19
  import fs from 'node:fs';
17
20
  import path from 'node:path';
18
21
  import os from 'node:os';
19
22
  import { fileURLToPath } from 'node:url';
23
+ import { createHash } from 'node:crypto';
20
24
 
21
25
  const KIT = path.dirname(fileURLToPath(import.meta.url));
22
26
  const TARGETS = ['claude', 'codex', 'opencode'];
@@ -36,7 +40,42 @@ const exists = (p) => { try { fs.lstatSync(p); return true; } catch { return fal
36
40
  if (!GLOBAL && (BASE === KIT || BASE.startsWith(KIT + path.sep)))
37
41
  die(`This is the kit's own directory (${BASE}). Run it from the project root: cd <project> && node ${path.join(KIT, 'install.mjs')} --target …`);
38
42
 
39
- const oldList = read(MARKER).split('\n').filter(l => l && !l.startsWith('version='));
43
+ const codexHome = process.env.CODEX_HOME ? path.relative(BASE, process.env.CODEX_HOME) : '.codex';
44
+ const ocConfig = process.env.OPENCODE_CONFIG_DIR ? path.relative(BASE, process.env.OPENCODE_CONFIG_DIR) : path.join('.config', 'opencode');
45
+ const LAYOUT = {
46
+ claude: { skills: '.claude/skills', agents: '.claude/agents' },
47
+ codex: { skills: '.agents/skills', agents: GLOBAL ? path.join(codexHome, 'agents') : '.codex/agents' },
48
+ opencode: { skills: '.agents/skills', agents: GLOBAL ? path.join(ocConfig, 'agents') : '.opencode/agents',
49
+ commands: GLOBAL ? path.join(ocConfig, 'commands') : '.opencode/commands' },
50
+ };
51
+
52
+ // Every name any version of the kit installed. Add to it, never remove: an old install's marker may list them.
53
+ const SHIPPED = new Set(['docs-bootstrap', 'docs-check', 'docs-scout', 'docs-reader',
54
+ 'grilling', 'domain-modeling', 'writing-for-agents', 'doc-coauthoring', 'diataxis-audit', 'grill-with-docs']);
55
+ const layoutDirs = new Set(Object.values(LAYOUT).flatMap(L => Object.values(L)).map(d => path.normalize(d)));
56
+ // The marker is committed, so a bad merge or PR can add any line to it. Only a line shaped like a path the kit
57
+ // writes, <layout folder>/<shipped name>[.md|.toml], is trusted; nothing else is ever replaced or removed.
58
+ const isOwnedPath = (r) => layoutDirs.has(path.normalize(path.dirname(r))) && SHIPPED.has(path.basename(r).replace(/\.(md|toml)$/, ''));
59
+ // Marker lines are `<path> <hash>`; versions up to 0.3.0 wrote the path alone, so their content is checked
60
+ // against everything those versions installed (LEGACY).
61
+ const old = new Map();
62
+ for (const l of read(MARKER).split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('version='))) {
63
+ const [r, h] = l.split(/\s+/);
64
+ if (isOwnedPath(r)) old.set(r, h);
65
+ else warn(`ignored '${r}' in .agent-docs-bootstrap: not a path this kit installs`);
66
+ }
67
+ // Hashes of every path versions 0.1.0–0.3.0 installed (all targets, no model flags). Fixed: never edit.
68
+ const LEGACY = new Set([
69
+ '00ca80d7077c0e4b', '122ae594f7eb38bd', '185ec7d02d9cf70b', '1f5a0fe496f38dd6', '3798ad63dd954bac',
70
+ '4c36074d70c5a88c', '562d1e8f3a090ffd', '698cadefe39cb46f', '6acf8481d4dd6a1a', '72bcff6d0e5476ba',
71
+ '790d7e6ae5034b3e', '79d9da94af685633', '7cd7eeac11324b51', '855778c0459cea17', '8c5adb3f63300a43',
72
+ '900cf27ab683e5d1', '96288a3e60126264', '9e0e5c30da22c059', 'b45162299d9aa78b', 'c5d47d8811aa61ee',
73
+ 'c87450584ed42c8b', 'cf2f8a40c91f3c45', 'dac09810ceb351f7', 'dc418221c7363caa', 'f741ba5711364c55',
74
+ 'fceff4c3b3159c80', 'fd975138dcda6b38', 'fe412d3c3c312f7a',
75
+ ]);
76
+ // The path is the kit's and nobody changed it since it was installed.
77
+ const isUnchanged = (r) => { const h = contentHash(abs(r)); return old.get(r) ? h === old.get(r) : LEGACY.has(h); };
78
+ const changedNote = (r) => `${r} changed since it was installed, so it is left in place as yours`;
40
79
  if (a.uninstall) { uninstall(); process.exit(0); }
41
80
 
42
81
  if (!a.target) usage(1, 'Missing --target.');
@@ -49,25 +88,17 @@ if (!GLOBAL) {
49
88
  die(`No .git here (${BASE}) — is this the project root? Run from the root or add --yes.`);
50
89
  }
51
90
 
52
- const codexHome = process.env.CODEX_HOME ? path.relative(BASE, process.env.CODEX_HOME) : '.codex';
53
- const ocConfig = process.env.OPENCODE_CONFIG_DIR ? path.relative(BASE, process.env.OPENCODE_CONFIG_DIR) : path.join('.config', 'opencode');
54
- const LAYOUT = {
55
- claude: { skills: '.claude/skills', agents: '.claude/agents' },
56
- codex: { skills: '.agents/skills', agents: GLOBAL ? path.join(codexHome, 'agents') : '.codex/agents' },
57
- opencode: { skills: '.agents/skills', agents: GLOBAL ? path.join(ocConfig, 'agents') : '.opencode/agents',
58
- commands: GLOBAL ? path.join(ocConfig, 'commands') : '.opencode/commands' },
59
- };
60
-
61
- const installed = new Set();
62
- const ours = (r) => oldList.includes(r);
91
+ const installed = new Map();
92
+ const released = new Set();
63
93
  function writeOwned(r, fn) {
64
- if (installed.has(r)) return false;
94
+ if (installed.has(r) || released.has(r)) return false;
65
95
  const dst = abs(r);
66
- if (exists(dst) && !ours(r)) { warn(`skipped ${r}: exists and was not installed by this kit`); return false; }
96
+ if (exists(dst) && !old.has(r)) { warn(`skipped ${r}: exists and was not installed by this kit`); return false; }
97
+ if (exists(dst) && !isUnchanged(r)) { warn(`skipped ${changedNote(r)}; delete it and run again to get this version`); released.add(r); return false; }
67
98
  fs.rmSync(dst, { recursive: true, force: true });
68
99
  fs.mkdirSync(path.dirname(dst), { recursive: true });
69
100
  fn(dst);
70
- installed.add(r);
101
+ installed.set(r, contentHash(dst));
71
102
  return true;
72
103
  }
73
104
 
@@ -94,9 +125,17 @@ for (const t of targets) {
94
125
  }
95
126
  }
96
127
 
97
- // Keep entries of earlier installs (e.g. another target) that are still on disk.
98
- const keep = oldList.filter(r => !installed.has(r) && exists(abs(r)));
99
- fs.writeFileSync(MARKER, [`version=${VERSION}`, ...keep, ...installed].join('\n') + '\n');
128
+ // Paths an earlier version installed into these targets' folders and this version no longer ships.
129
+ const ownDirs = new Set(targets.flatMap(t => Object.values(LAYOUT[t])));
130
+ for (const r of old.keys()) {
131
+ if (installed.has(r) || released.has(r) || !ownDirs.has(path.dirname(r)) || !exists(abs(r))) continue;
132
+ if (isUnchanged(r)) { fs.rmSync(abs(r), { recursive: true, force: true }); log(`removed ${r} (no longer shipped)`); }
133
+ else { warn(`not removed: ${changedNote(r)}`); released.add(r); }
134
+ }
135
+
136
+ // Keep entries of earlier installs (e.g. another target) that are still on disk; a changed path is no longer listed.
137
+ const keep = [...old].filter(([r]) => !installed.has(r) && !released.has(r) && exists(abs(r)));
138
+ fs.writeFileSync(MARKER, [`version=${VERSION}`, ...[...keep, ...installed].map(([r, h]) => h ? `${r} ${h}` : r)].join('\n') + '\n');
100
139
 
101
140
  console.log(`\nDone: agent-docs-bootstrap ${VERSION}, ${targets.join(', ')}${GLOBAL ? ' (global)' : ''}.`);
102
141
  if (!GLOBAL) log(`commit the installed folders and .agent-docs-bootstrap so the whole team gets the same version`);
@@ -139,27 +178,33 @@ function parseAgent(file) {
139
178
  // ---------- helpers
140
179
  function skillDirs() {
141
180
  const out = [];
142
- for (const base of [path.join(KIT, 'skills'), path.join(KIT, 'skills', 'third-party')])
143
- for (const e of fs.readdirSync(base, { withFileTypes: true })) {
144
- const d = path.join(base, e.name);
145
- if (!e.isDirectory() || !fs.existsSync(path.join(d, 'SKILL.md'))) continue;
146
- const fm = (read(path.join(d, 'SKILL.md')).match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m) || ['', ''])[1].trim();
147
- if (!NAME_RE.test(e.name) || fm !== e.name) { warn(`skipped skill ${e.name}: frontmatter name '${fm}' must match the folder`); continue; }
148
- out.push(d);
149
- }
181
+ const base = path.join(KIT, 'skills');
182
+ for (const e of fs.readdirSync(base, { withFileTypes: true })) {
183
+ const d = path.join(base, e.name);
184
+ if (!e.isDirectory() || !fs.existsSync(path.join(d, 'SKILL.md'))) continue;
185
+ const fm = (read(path.join(d, 'SKILL.md')).match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m) || ['', ''])[1].trim();
186
+ if (!NAME_RE.test(e.name) || fm !== e.name) { warn(`skipped skill ${e.name}: frontmatter name '${fm}' must match the folder`); continue; }
187
+ out.push(d);
188
+ }
150
189
  return out;
151
190
  }
152
191
  function uninstall() {
153
- if (!oldList.length) die(`Nothing to remove: no ${MARKER}.`);
192
+ if (!old.size) die(`Nothing to remove: no ${MARKER}.`);
154
193
  console.log(`Removing agent-docs-bootstrap from ${BASE}`);
155
- for (const r of oldList) if (exists(abs(r))) { fs.rmSync(abs(r), { recursive: true, force: true }); log(`removed ${r}`); }
194
+ for (const r of old.keys()) {
195
+ if (!exists(abs(r))) continue;
196
+ if (isUnchanged(r)) { fs.rmSync(abs(r), { recursive: true, force: true }); log(`removed ${r}`); }
197
+ else warn(`not removed: ${changedNote(r)}`);
198
+ }
156
199
  fs.rmSync(MARKER, { force: true });
157
- const dirs = new Set(oldList.flatMap(r => [path.dirname(r), path.dirname(path.dirname(r))]));
200
+ const dirs = new Set([...old.keys()].flatMap(r => [path.dirname(r), path.dirname(path.dirname(r))]));
158
201
  for (const d of [...dirs].sort((x, y) => y.length - x.length)) try { fs.rmdirSync(abs(d)); } catch {}
159
202
  }
160
203
  function parseArgs(argv) {
161
204
  const o = {}; const withVal = new Set(['target', 'project', 'scout-model', 'reader-model']);
162
205
  for (let i = 0; i < argv.length; i++) {
206
+ const eq = argv[i].match(/^--([a-z-]+)=(.*)$/);
207
+ if (eq && withVal.has(eq[1])) { o[eq[1]] = eq[2]; continue; }
163
208
  const k = argv[i].replace(/^--/, '');
164
209
  if (argv[i] === '-h' || k === 'help') o.help = true;
165
210
  else if (withVal.has(k)) { if (i + 1 >= argv.length) die(`--${k} needs a value`); o[k] = argv[++i]; }
@@ -174,6 +219,20 @@ function usage(code, msg) {
174
219
  console.log(src.slice(0, src.findIndex(l => !l.startsWith('//'))).map(l => l.replace(/^\/\/ ?/, '')).join('\n'));
175
220
  process.exit(code);
176
221
  }
222
+ // Content hash of an installed file or folder: relative names and text with line endings normalised, so a
223
+ // checkout with CRLF line endings still matches.
224
+ function contentHash(p) {
225
+ const h = createHash('sha256');
226
+ const files = fs.lstatSync(p).isDirectory() ? listFiles(p).sort() : [''];
227
+ for (const f of files) h.update(`${f}\0${read(path.join(p, f)).replace(/\r\n/g, '\n')}\0`);
228
+ return h.digest('hex').slice(0, 16);
229
+ }
230
+ function listFiles(dir, base = dir) {
231
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => {
232
+ const p = path.join(dir, e.name);
233
+ return e.isDirectory() ? listFiles(p, base) : [path.relative(base, p).split(path.sep).join('/')];
234
+ });
235
+ }
177
236
  function read(p) { try { return fs.readFileSync(p, 'utf8'); } catch { return ''; } }
178
237
  function log(s) { console.log(` ${s}`); }
179
238
  function warn(s) { console.error(`WARNING: ${s}`); }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-docs-bootstrap",
3
- "version": "0.2.0",
4
- "description": "Skills and subagents that build reference documentation for a codebase and test it with a fresh agent. For Claude Code, Codex and OpenCode.",
3
+ "version": "0.4.0",
4
+ "description": "Agent skills that build reference documentation for a codebase (glossary, ADRs, architecture, how-tos, AGENTS.md) through interviews with the team, with an optional check by a fresh agent. Works with any agent that runs skills; the installer sets up Claude Code, Codex and OpenCode.",
5
5
  "license": "MIT",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/TraneDev/agent-docs-bootstrap.git" },
7
7
  "homepage": "https://github.com/TraneDev/agent-docs-bootstrap#readme",
@@ -9,5 +9,5 @@
9
9
  "bin": { "agent-docs-bootstrap": "install.mjs" },
10
10
  "files": ["install.mjs", "agents/", "skills/", "THIRD_PARTY.md", "CHANGELOG.md"],
11
11
  "engines": { "node": ">=18" },
12
- "keywords": ["claude-code", "codex", "opencode", "agents-md", "documentation", "skills"]
12
+ "keywords": ["claude-code", "codex", "opencode", "agents-md", "documentation", "skills", "agent-skills", "ai-agents", "coding-agents"]
13
13
  }
@@ -27,7 +27,7 @@ They hold at every stage.
27
27
  - **Keep your own context for the conversation.** Do not read external docs or scan the whole repository yourself: delegate to `docs-scout` and read its results from `docs/agents/scratch/` in parts.
28
28
  - **Secrets never reach the documents.** Passwords, tokens, keys and credentials, test environments included, are not written down: only where to get them (a vault, an environment variable, who to ask). If the human dictates a secret, record its source instead of the value and say why.
29
29
  - **Do not judge documentation quality yourself.** The human confirms what is written. If they want a measured check, the optional `docs-check` skill compares a fresh agent's plan with real commits.
30
- - **Formats are not duplicated.** Glossary and ADRs follow the `domain-modeling` skill (its `CONTEXT-FORMAT.md`, `ADR-FORMAT.md`). Diagrams — [references/c4-format.md](references/c4-format.md). `AGENTS.md` — [references/agents-md-format.md](references/agents-md-format.md). Reader test — the optional `docs-check` skill.
30
+ - **Formats are not duplicated.** Glossary and ADRs follow `domain-modeling`: [CONTEXT-FORMAT.md](vendor/domain-modeling/CONTEXT-FORMAT.md), [ADR-FORMAT.md](vendor/domain-modeling/ADR-FORMAT.md). Diagrams — [references/c4-format.md](references/c4-format.md). `AGENTS.md` — [references/agents-md-format.md](references/agents-md-format.md). Reader test — the optional `docs-check` skill.
31
31
  - **Language.** Documents are written in the language recorded in the state file; identifiers and code terms stay as they are, and a glossary term carries its code name: "**Order request** (`Request`)". Templates in this skill are in English: translate headings and text when the documents language differs, keep file names.
32
32
 
33
33
  ## Subagents
@@ -38,6 +38,10 @@ Independent calls run in parallel, such as the stage 1.5 batches. Start them tog
38
38
 
39
39
  If the harness cannot start the `docs-scout` subagent, do the brief yourself but write the result to the scratch file and keep only a summary in the conversation.
40
40
 
41
+ ## Bundled guides
42
+
43
+ `vendor/` holds guides by other authors (`grilling`, `domain-modeling`, `writing-for-agents`, `doc-coauthoring`, `diataxis-audit`), unmodified except that each `SKILL.md` is renamed to `<name>.md`. They are not separate skills: read the file at the stage that names it and follow it, even if a skill with the same name is installed in the session — this kit is tested with these copies. When a brief names a file under `vendor/`, give the scout its full path: the scout does not know where this skill is installed. This skill's folder is `.claude/skills/docs-bootstrap` or `.agents/skills/docs-bootstrap` in the project, or the same under the home directory for a global install; if unsure, search for `vendor/diataxis-audit/diataxis-audit.md`.
44
+
41
45
  ## Stage 0. Session start
42
46
 
43
47
  1. Read `docs/agents/bootstrap-state.md`. No file → ask the questions below, create `docs/agents/` and copy the templates from [templates/](templates/) into it: `bootstrap-state.md`, `sources.md`. Create `docs/agents/scratch/` and add the line `docs/agents/scratch/` to the project's `.gitignore`. Record the mode, the documents language and the kit version (first line of `.agent-docs-bootstrap` in the project root, if present) in the state file.
@@ -77,8 +81,8 @@ Skipped when `sources.md` lists no external sources, or the agent has no tools t
77
81
  **Scout.** Brief "Stage 2": draft terms from code with evidence and synonyms; name conflicts; ADR candidates from traces of decisions with reasons marked `ASSUMPTION`; comparison with facts from external docs (matches / differs / not in code).
78
82
 
79
83
  **You.**
80
- 1. Load the `grilling` and `domain-modeling` skills.
81
- 2. First decide single- or multi-context — the rule is in `CONTEXT-FORMAT.md`.
84
+ 1. Read [vendor/grilling/grilling.md](vendor/grilling/grilling.md) and [vendor/domain-modeling/domain-modeling.md](vendor/domain-modeling/domain-modeling.md) and follow them.
85
+ 2. First decide single- or multi-context — the rule is in [CONTEXT-FORMAT.md](vendor/domain-modeling/CONTEXT-FORMAT.md).
82
86
  3. Interview along the tree: term conflicts → definitions → each ADR candidate (reason, rejected alternatives, still valid?) → open questions accumulated at stages 1–1.5.
83
87
  4. Write as you go: `CONTEXT.md` (or `CONTEXT-MAP.md` + one `CONTEXT.md` per context), `docs/adr/NNNN-slug.md`. No ADR without a confirmed reason.
84
88
 
@@ -104,14 +108,14 @@ A how-to is a step-by-step procedure for a recurring task: "add a migration", "a
104
108
 
105
109
  **You.** Show the list; the human picks the 3–7 most frequent. For each:
106
110
  - an up-to-date external page exists → transfer it to `docs/how-to/<slug>.md`, checking every step against the code;
107
- - an outdated one exists → load `doc-coauthoring` and rewrite it with the human (stages Context Gathering and Refinement; its Reader Testing step is skipped; the optional `docs-check` skill covers it);
111
+ - an outdated one exists → read [vendor/doc-coauthoring/doc-coauthoring.md](vendor/doc-coauthoring/doc-coauthoring.md) and rewrite it with the human (stages Context Gathering and Refinement; its Reader Testing step is skipped; the optional `docs-check` skill covers it);
108
112
  - none → do not write it from memory. List it as deferred: real team tasks will supply the material. If `docs/agents/reader-tests.md` exists, do not write how-tos from its tasks: a document written from a test task holds its answer, and the check on that task stops meaning anything.
109
113
 
110
114
  **Done when:** every step of every how-to points to an existing file or a command verified at stage 1; the deferred how-tos are listed in the state file.
111
115
 
112
116
  ## Stage 5. AGENTS.md
113
117
 
114
- **You.** Load `writing-for-agents`. Format — `agents-md-format.md`. Order:
118
+ **You.** Read [vendor/writing-for-agents/writing-for-agents.md](vendor/writing-for-agents/writing-for-agents.md) and follow it. Format — `agents-md-format.md`. Order:
115
119
  1. Root `AGENTS.md`: up to 150 lines, only what is needed on every turn. Commands from stage 1, directory map from stage 3, pointers to `CONTEXT.md`, `docs/adr/`, `docs/how-to/`, `docs/architecture.md`.
116
120
  2. Nested `AGENTS.md` in large areas (an app in a monorepo, a big module): only rules specific to that area.
117
121
  3. Behaviour rules only from the human ("don't run e2e locally", "don't touch generated code").
@@ -131,7 +135,7 @@ There is no code, or very little, so there is nothing to reverse-engineer: all t
131
135
 
132
136
  ### N1. Interview the idea
133
137
 
134
- Load `grilling` and `domain-modeling`. Tree: what the system does and for whom → the main entities and their names → boundaries (single- or multi-context per `CONTEXT-FORMAT.md`) → decisions already made before the start (stack, state, storage, integrations) with reasons and rejected alternatives. Rounds as in stage 2.
138
+ Read `grilling` and `domain-modeling` from `vendor/` as in stage 2. Tree: what the system does and for whom → the main entities and their names → boundaries (single- or multi-context per `CONTEXT-FORMAT.md`) → decisions already made before the start (stack, state, storage, integrations) with reasons and rejected alternatives. Rounds as in stage 2.
135
139
 
136
140
  **Write:** `CONTEXT.md` (or `CONTEXT-MAP.md`), `docs/adr/0001-…` onwards — only with a reason from a human. Also `docs/agents/sources.md` if the project already has external pages (spec, design).
137
141
 
@@ -145,7 +149,7 @@ From the design, not the code: C4 Context and Container per `c4-format.md`. A co
145
149
 
146
150
  ### N3. Starter AGENTS.md
147
151
 
148
- Load `writing-for-agents`. Use [templates/AGENTS.starter.md](templates/AGENTS.starter.md): commands already in the manifest (the scout verifies them by running, brief "Stage 1", sections 1–2), the planned directory map, rules from the human, pointers to `CONTEXT.md`, `docs/adr/`, `docs/architecture.md`. The how-to section stays empty with the note "will appear after the first features". Handle `CLAUDE.md` as in stage 5, step 4.
152
+ Read `writing-for-agents` from `vendor/` as in stage 5. Use [templates/AGENTS.starter.md](templates/AGENTS.starter.md): commands already in the manifest (the scout verifies them by running, brief "Stage 1", sections 1–2), the planned directory map, rules from the human, pointers to `CONTEXT.md`, `docs/adr/`, `docs/architecture.md`. The how-to section stays empty with the note "will appear after the first features". Handle `CLAUDE.md` as in stage 5, step 4.
149
153
 
150
154
  **Done when:** the file is within 80 lines; every command is verified; every rule is confirmed.
151
155
 
@@ -27,7 +27,7 @@ Use the tools the session has for the source listed in `docs/agents/sources.md`
27
27
 
28
28
  **Batches 1..K — classification.** For each page in the range:
29
29
  1. Get the structure, if the source can return it without the body: section headings, presence of code, tables, step lists.
30
- 2. Determine the Diátaxis type from title and structure (criteria — the `diataxis-audit` skill). Numbered steps → how-to; parameter and API tables → reference; "why" and history → explanation; learning from zero → tutorial; a mix → mixed.
30
+ 2. Determine the Diátaxis type from title and structure (criteria — `vendor/diataxis-audit/diataxis-audit.md` in the `docs-bootstrap` skill folder; the lead gives its full path). Numbered steps → how-to; parameter and API tables → reference; "why" and history → explanation; learning from zero → tutorial; a mix → mixed.
31
31
  3. Only if the type is unclear, read the start of the body: at most 2000 characters. Read no further.
32
32
  4. Note whether the page refers to files or commands (code blocks, paths) — without checking; checks happen at stages 2 and 4.
33
33
 
@@ -43,7 +43,7 @@ Return these sections:
43
43
 
44
44
  1. **Terms** — for each: term, name in code, a one-sentence draft definition without implementation details, evidence files (up to 3), synonyms seen in code, UI strings, API. Leave out framework concepts.
45
45
  2. **Name conflicts** — one entity under different names (frontend/backend, DTO/UI, code/UI strings) and one name for different entities.
46
- 3. **ADR candidates** — what was decided (fact), evidence, a hypothesis for the reason marked `ASSUMPTION`. Run each through the three criteria in `ADR-FORMAT.md` of the `domain-modeling` skill; for rejected ones name the failed criterion.
46
+ 3. **ADR candidates** — what was decided (fact), evidence, a hypothesis for the reason marked `ASSUMPTION`. Run each through the three criteria in `vendor/domain-modeling/ADR-FORMAT.md` of the `docs-bootstrap` skill folder (the lead gives its full path); for rejected ones name the failed criterion.
47
47
  4. **Check against external docs** — for each term and decision mentioned in the 1.5 audit: matches the code / differs (how) / not found in code. Read pages selectively: find the relevant section first, then read only that part, in chunks of at most 4000 characters. No more than 5 pages per run; if more are needed, return the list and the lead will start another run.
48
48
 
49
49
  ## Stage 3. Architecture
@@ -15,7 +15,7 @@ The scout does not know in advance where things live in a given stack. It determ
15
15
 
16
16
  - Framework concepts (component, service, store, guard, DTO as a pattern) are not domain terms.
17
17
  - A code folder is not a container unless it deploys on its own.
18
- - A dependency is not an ADR until it passes the three criteria in `ADR-FORMAT.md`.
18
+ - A dependency is not an ADR until it passes the three criteria in [ADR-FORMAT.md](../vendor/domain-modeling/ADR-FORMAT.md).
19
19
 
20
20
  ## Name conflict detector
21
21
 
@@ -53,16 +53,16 @@ The reader must not see the solution, the history, your session's instructions o
53
53
 
54
54
  What the script does:
55
55
 
56
- - the snapshot is the parent's code without the documents and the kit's installed files, with the current documents from the working tree on top, committed or not (git-ignored and deleted ones are skipped, `docs/agents/` never goes in). Do not keep unfinished hand edits in the documents during a run: the reader gets them as they are;
56
+ - the snapshot is the parent's code without the documents and the kit's installed files, with the current documents from the working tree on top, committed or not (git-ignored and deleted ones are skipped, `docs/agents/` never goes in). Do not keep unfinished hand edits in the documents during a run: the reader gets them as they are. `README` and other files stay as they were in the parent commit, like the code: today's `README` may already describe the solution;
57
57
  - the snapshot gets an empty `git init`, which stops the harness from loading `AGENTS.md` or `CLAUDE.md` from parent directories. No installed dependencies: the reader builds a plan, it does not execute it;
58
58
  - the reader: Claude Code `claude -p --restricted` with only `Read`, `Grep`, `Glob` and no MCP servers; Codex `codex exec -s read-only` with every MCP server, plugin and app turned off; OpenCode `opencode run --pure` with the `docs-reader` agent, whose permissions deny everything but reading, MCP tools included. The prompt is the installed `docs-reader`, the task goes through stdin;
59
59
  - it writes `rt-<N>-plan.md` and `rt-<N>-log.md` to the run directory and prints a three-line summary.
60
60
 
61
61
  Non-zero exit or "plan EMPTY" → show the human the summary and `rt-<N>-log.md`; do not guess. A plan that cites files outside the snapshot, the history, a tracker or external docs is invalid: note why in `reader-tests.md` and rerun.
62
62
 
63
- The user's global instructions (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, `~/.config/opencode/AGENTS.md`) cannot be switched off this way. If they are about this project, ask the human to move them aside for the test.
63
+ The user's global instructions may still reach the reader: Codex loads `~/.codex/AGENTS.md`, OpenCode `~/.config/opencode/AGENTS.md`; whether `claude -p --restricted` loads `~/.claude/CLAUDE.md` is not documented. If they are about this project, ask the human to move them aside for the test.
64
64
 
65
- No `node`, or the harness CLI is not in PATH → do not play the reader yourself: you have seen the docs and the history. Run the script with `--snapshot-only` (it prints the snapshot path) and ask the human to start a new agent session with its working directory set to that path, paste the `docs-reader` prompt and the task, and save the answer to `rt-<N>-plan.md`.
65
+ Your harness is not `claude`, `codex` or `opencode`, its CLI is not in PATH, or there is no `node` → do not play the reader yourself: you have seen the docs and the history. Run the script with `--snapshot-only` (it prints the snapshot path) and ask the human to start a new agent session with its working directory set to that path, paste the `docs-reader` prompt and the task, and save the answer to `rt-<N>-plan.md`.
66
66
 
67
67
  ## Step 4. Gaps
68
68
 
@@ -2,20 +2,23 @@
2
2
  // docs-check steps 2–3: a history-free snapshot outside the repository and a fresh reader started as a
3
3
  // separate process of your harness. Works the same from bash, PowerShell and cmd; needs git and Node 18+.
4
4
  //
5
- // node <skills>/docs-check/scripts/reader-run.mjs <run dir> <N> --harness claude|codex|opencode [--reader <path>]
5
+ // node <skills>/docs-check/scripts/reader-run.mjs <run dir> <N> --harness claude|codex|opencode [--reader <path>] [--timeout-min <m>]
6
6
  // node <skills>/docs-check/scripts/reader-run.mjs <run dir> <N> --snapshot-only
7
7
  //
8
8
  // <run dir> holds tasks.json: { "tasks": [ { "n": 1, "parent": "<hash>", "task": "<wording>" } ] }.
9
9
  // Writes rt-<N>-plan.md (the reader's answer) and rt-<N>-log.md there; prints a short summary.
10
+ // --timeout-min stops the reader after that many minutes (default 12).
10
11
 
11
12
  import fs from 'node:fs';
12
13
  import path from 'node:path';
13
14
  import os from 'node:os';
14
15
  import { execFileSync, spawn } from 'node:child_process';
16
+ import { createHash } from 'node:crypto';
15
17
 
16
18
  const a = parseArgs(process.argv.slice(2));
17
19
  if (a.help || a._.length < 2) {
18
- console.log(fs.readFileSync(new URL(import.meta.url), 'utf8').split('\n').slice(1, 10).map(l => l.replace(/^\/\/ ?/, '')).join('\n'));
20
+ const src = fs.readFileSync(new URL(import.meta.url), 'utf8').split('\n').slice(1);
21
+ console.log(src.slice(0, src.findIndex(l => !l.startsWith('//'))).map(l => l.replace(/^\/\/ ?/, '')).join('\n'));
19
22
  process.exit(a.help ? 0 : 1);
20
23
  }
21
24
 
@@ -26,15 +29,21 @@ const spec = JSON.parse(read(path.join(RUN, 'tasks.json'), 'no tasks.json in the
26
29
  const task = (spec.tasks || []).find(t => Number(t.n) === N) || die(`tasks.json has no task n=${N}`);
27
30
  if (!task.parent || !task.task) die(`task ${N} has no parent or task`);
28
31
 
29
- const SNAPS = path.join(os.tmpdir(), `agent-docs-check-${path.basename(ROOT)}`);
32
+ // The path hash keeps two repositories with the same folder name apart.
33
+ const SNAPS = path.join(os.tmpdir(), `agent-docs-check-${path.basename(ROOT)}-${createHash('sha1').update(ROOT).digest('hex').slice(0, 8)}`);
30
34
  const SNAP = path.join(SNAPS, `rt-${N}`);
31
35
  const TIMEOUT_MS = Number(a['timeout-min'] || 12) * 60 * 1000;
32
36
  const win = process.platform === 'win32';
33
37
 
34
- // Documents the kit builds; kit-installed paths and its run files never reach the reader.
35
- const kitPaths = read(path.join(ROOT, '.agent-docs-bootstrap'), '', true).split('\n').filter(l => l && !l.startsWith('version='));
38
+ // Documents the kit builds; kit-installed paths and its run files never reach the reader. The marker is committed
39
+ // and could hold any line, so only lines shaped like an installed path count (a line `docs` would hide every document).
40
+ const KIT_DIRS = ['.claude/skills', '.claude/agents', '.agents/skills', '.codex/agents', '.opencode/agents', '.opencode/commands'];
41
+ const kitPaths = read(path.join(ROOT, '.agent-docs-bootstrap'), '', true).split('\n').map(l => l.trim().split(/\s+/)[0])
42
+ .filter(l => KIT_DIRS.includes(path.posix.dirname(l)) && /^[a-z0-9-]+(\.md|\.toml)?$/.test(path.posix.basename(l)));
36
43
  const isKit = (p) => p === '.agent-docs-bootstrap' || p.startsWith('docs/agents/') || kitPaths.some(k => p === k || p.startsWith(k + '/'));
37
- const isDoc = (p) => /(^|\/)(AGENTS\.md|CONTEXT[^/]*\.md)$/.test(p) || p === 'CLAUDE.md' || p.startsWith('docs/');
44
+ // README and other files stay as they were in the parent commit, like the code: they are the project as it was,
45
+ // not the documents under test, and today's README may already describe the solution.
46
+ const isDoc = (p) => /(^|\/)(AGENTS\.md|CLAUDE\.md|CONTEXT[^/]*\.md)$/.test(p) || p.startsWith('docs/');
38
47
 
39
48
  // ---------- snapshot: the parent's code without documents, today's documents from the working tree on top
40
49
  fs.rmSync(SNAP, { recursive: true, force: true });
@@ -97,7 +106,9 @@ child.stdin.end(input);
97
106
  let out = '', err = '';
98
107
  child.stdout.on('data', d => { out += d; });
99
108
  child.stderr.on('data', d => { err += d; });
100
- const timer = setTimeout(() => { err += `\nstopped after ${TIMEOUT_MS / 60000} min`; child.kill(); }, TIMEOUT_MS);
109
+ // With shell: true on Windows, child.kill() stops only cmd.exe; taskkill /T takes the CLI with it.
110
+ const stop = () => win ? spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore' }) : child.kill();
111
+ const timer = setTimeout(() => { err += `\nstopped after ${TIMEOUT_MS / 60000} min`; stop(); }, TIMEOUT_MS);
101
112
  child.on('error', e => die(`${cmd} did not start: ${e.message}`));
102
113
  child.on('close', code => {
103
114
  clearTimeout(timer);
@@ -1,7 +0,0 @@
1
- ---
2
- name: grill-with-docs
3
- description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
4
- disable-model-invocation: true
5
- ---
6
-
7
- Call the Skill tool twice, for "grilling" and "domain-modeling".
@@ -1,5 +0,0 @@
1
- interface:
2
- display_name: "Grill with Docs"
3
- short_description: "Grill a design and write its docs"
4
- policy:
5
- allow_implicit_invocation: false
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Matt Pocock
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.