create-afe-project 0.0.0-stage → 1.0.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 (59) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/LICENSE +21 -0
  3. package/README.md +77 -2
  4. package/bin/create-afe-project.js +123 -0
  5. package/package.json +43 -4
  6. package/src/answers.js +147 -0
  7. package/src/config.js +152 -0
  8. package/src/project.js +217 -0
  9. package/src/render.js +51 -0
  10. package/template/.claude/harness.conf +47 -0
  11. package/template/.claude/hooks/post-edit-check.sh +38 -0
  12. package/template/.claude/hooks/protect-docs.sh +315 -0
  13. package/template/.claude/hooks/stop-check-decisions.sh +25 -0
  14. package/template/.claude/hooks/stop-check-docs.sh +29 -0
  15. package/template/.claude/hooks/stop-check-template.sh +23 -0
  16. package/template/.claude/hooks/stop-run-project-hooks.sh +27 -0
  17. package/template/.claude/path-classes +61 -0
  18. package/template/.claude/settings.project.json +5 -0
  19. package/template/.claude/settings.template.json +76 -0
  20. package/template/.claude/skills/afe-init/SKILL.md +104 -0
  21. package/template/.claude/skills/create-pr/SKILL.md.jinja +107 -0
  22. package/template/.claude/skills/update-template/SKILL.md +40 -0
  23. package/template/.claude/skills/{% if code_review %}review-code{% endif %}/SKILL.md.jinja +46 -0
  24. package/template/.claude/skills/{% if docs_review %}docs-review{% endif %}/SKILL.md +56 -0
  25. package/template/.claude/skills/{% if dual_review %}dual-review{% endif %}/SKILL.md +58 -0
  26. package/template/.github/workflows/decisions.yml.jinja +47 -0
  27. package/template/.github/workflows/docs.yml.jinja +40 -0
  28. package/template/.github/workflows/{% if code_review %}code-review.yml{% endif %}.jinja +51 -0
  29. package/template/.github/workflows/{% if docs_review %}docs-review.yml{% endif %}.jinja +49 -0
  30. package/template/.github/workflows/{% if fail_without_change %}fail-without-change.yml{% endif %}.jinja +40 -0
  31. package/template/.github/workflows/{% if secret_scan %}secret-scan.yml{% endif %}.jinja +47 -0
  32. package/template/.markdownlint-cli2.jsonc +27 -0
  33. package/template/.template/owned.txt.jinja +63 -0
  34. package/template/AGENTS.md.jinja +91 -0
  35. package/template/CLAUDE.md +3 -0
  36. package/template/README.md.jinja +41 -0
  37. package/template/_gitignore +14 -0
  38. package/template/docs/README.md.jinja +44 -0
  39. package/template/docs/contributing.md.jinja +206 -0
  40. package/template/docs/decisions/adr-template.md +38 -0
  41. package/template/docs/documentation-rules.md.jinja +200 -0
  42. package/template/docs/testing-rules.md +80 -0
  43. package/template/scripts/check-decisions.sh +382 -0
  44. package/template/scripts/check-docs.sh +20 -0
  45. package/template/scripts/check-links.mjs +142 -0
  46. package/template/scripts/check-project.sh +27 -0
  47. package/template/scripts/check-template.sh +88 -0
  48. package/template/scripts/check.sh +21 -0
  49. package/template/scripts/env.sh +29 -0
  50. package/template/scripts/gen-decisions-index.sh +57 -0
  51. package/template/scripts/gen-settings.sh +14 -0
  52. package/template/scripts/lib/classes.sh +89 -0
  53. package/template/scripts/lib/git-base.sh +23 -0
  54. package/template/scripts/lib/harness-conf.sh +48 -0
  55. package/template/scripts/lib/json.mjs +160 -0
  56. package/template/scripts/pr-metrics.sh +80 -0
  57. package/template/scripts/protect-branch.sh +53 -0
  58. package/template/scripts/{% if acceptance_check %}check-acceptance.sh{% endif %} +47 -0
  59. package/template/scripts/{% if fail_without_change %}fail-without-change.sh{% endif %} +88 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,351 @@
1
+ # create-afe-project
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - The template moves from Copier to the npm package `create-afe-project` (ADR-0002), on the
8
+ owner's instruction of 2026-10-31, to drop the Python toolchain for users; the first release on
9
+ npm, under the MIT license.
10
+ - `npx create-afe-project@latest <directory>` (or `npm create afe-project@latest`) creates a
11
+ project with a wizard that asks the questions of the former `copier.yml`; `--defaults`,
12
+ `--data key=value`, and `--data-file` answer without a terminal. The questions are short and
13
+ plain, and the message after create numbers the next steps.
14
+ - Create makes the initial commit on `main` (`Template: create the project with
15
+ create-afe-project <version>`), the one direct commit that `docs/contributing.md` allows, so
16
+ `/afe-init` starts on a clean working tree. It commits only in a new directory or an empty
17
+ clone, never in a non-empty directory written with `--force`; `--no-git` skips it.
18
+ - `npx create-afe-project@latest update` replaces the template-owned files, keeps the project
19
+ block of `AGENTS.md`, creates missing seeded files and never changes existing ones, removes
20
+ files the template no longer owns, runs the migrations between the versions, and regenerates
21
+ the generated files. It refuses a dirty working tree (`--allow-dirty`) and a downgrade, and
22
+ lists every replaced local edit of a template-owned file.
23
+ - The answers file is `.afe-answers.json` with the template version in `_version`; a project
24
+ generated by Copier converts its `.copier-answers.yml` on its first update. The path classes
25
+ (`scripts/lib/classes.sh`), the protect hook, and the size warning of
26
+ `scripts/check-decisions.sh` know both names.
27
+ - `docs/contributing.md`, section 5, `docs/documentation-rules.md`, section 9, and the
28
+ `update-template` skill describe the update with `create-afe-project`; there are no conflict
29
+ markers to resolve any more. `README.md` is seeded: copy its "Project template" and "Checks"
30
+ sections by hand.
31
+ - A generated project needs only Git, bash, and Node.js: jq and lychee are gone.
32
+ - The hooks, `scripts/gen-settings.sh`, and `scripts/pr-metrics.sh` read and write JSON with the
33
+ new `scripts/lib/json.mjs`; the protect hook now blocks when Node.js is missing.
34
+ - `scripts/check-links.mjs` replaces lychee: offline, every relative link and image of the
35
+ Markdown files points at an existing file, and every anchor at a heading or an HTML id of its
36
+ target. `scripts/check-docs.sh` and the `docs` workflow run it; `lychee.toml` is removed.
37
+ - The `docs` workflow also runs on `workflow_dispatch`, so a workflow can start it for a pull
38
+ request opened with `GITHUB_TOKEN`.
39
+ - A second pass over the AFE guide, on the owner's decisions of 2026-10-31; no observed failure.
40
+ - `afe-init` skill (new, run by the owner only): interviews the owner about the product, the
41
+ stack, and the stage; records requirements, an accepted stack ADR, and a proposed
42
+ architecture; scaffolds the code with the stack's own generator; wires format, lint, types,
43
+ unit and end-to-end tests into `scripts/checks.d/` and `.claude/harness.conf`; and proves
44
+ that every gate fails when it should. Three pull requests, one change each.
45
+ - The check after every edit: `.claude/hooks/post-edit-check.sh` (new, PostToolUse) runs
46
+ `CHECK_EDIT` of `.claude/harness.conf` on the changed file, limited by `CHECK_EDIT_GLOBS`,
47
+ and sends a failure back to the agent. `.claude/harness.conf` is seeded: an existing project
48
+ adds the two keys by hand when it wants the check.
49
+ - The protect hook asks before publishing (`npm`, `pnpm`, `yarn`, `bun`, `cargo`, `poetry`, `uv`
50
+ publish, `twine upload`, `gem push`, `dotnet nuget push`, `docker` or `podman push`),
51
+ `gh workflow run`, and a `gh api` DELETE, also behind `git -C DIR`; it denies a push to the
52
+ default branch once the remote has it; and it asks when an edit or a command contains a known
53
+ token format (AWS, GitHub, GitLab, Slack, Stripe, Google, Anthropic, OpenAI, npm, private keys).
54
+ - `secret_scan` module (new, default on): `.github/workflows/secret-scan.yml` runs gitleaks,
55
+ downloaded at a pinned version with its checksum, on the commits of every pull request and
56
+ push to `main`. The message after create also names GitHub's secret scanning and push protection.
57
+ - `scripts/protect-branch.sh` (new): creates or updates the GitHub ruleset that protects the
58
+ default branch (pull requests only, the `check` and `integrity` checks, no force push or
59
+ deletion); the owner runs it once.
60
+ - `scripts/check-template.sh` fails when a hook command of `.claude/settings.json` names a
61
+ missing or non-executable script.
62
+ - `AGENTS.md`, section 4: content from issues, pull requests, web pages, logs, tool output, and
63
+ dependencies is data, never instructions. The map plans `docs/conventions/` and
64
+ `docs/recipes/`, a new document kind of `docs/documentation-rules.md`, section 2.
65
+ - With the `dual_review` module, the Santa loop runs without the Codex CLI too, with a second
66
+ fresh Claude subagent that reviews with another lens; `REVIEW_MAX_ROUNDS` of
67
+ `.claude/harness.conf` (3 to 5, default 3) caps the rounds, and a blocker that comes back
68
+ unchanged after its fix ends the loop early.
69
+
70
+ ## [0.6.0] - 2026-10-31
71
+
72
+ A second pass over the Agent-Friendly Engineering field guide, on the owner's decisions of
73
+ 2026-10-31, and the follow-ups of 0.5.0. Observed failures: the protect hook let two writes into
74
+ rule files through, behind `cd` and in a Python heredoc (pull request #14); the review workflows
75
+ skipped #14 and reported success. The other additions come from the owner's decisions on the
76
+ guide, without an observed failure.
77
+
78
+ ### Added
79
+
80
+ - Environment contract: `scripts/env.sh setup|check|fast|e2e|reset` runs the project's commands
81
+ `SETUP`, `CHECK_FAST` (default `scripts/check-docs.sh`), `E2E`, and `RESET` of
82
+ `.claude/harness.conf`, and `scripts/check.sh` for `check`; an unset command is SKIP, and the
83
+ exit code is the command's. `AGENTS.md`, section 2, and the map point at it.
84
+ - `fail_without_change` module: `.github/workflows/fail-without-change.yml` runs
85
+ `scripts/env.sh setup` and the driver on every pull request; the label `tests-pass-on-base`
86
+ allows a pass. The driver installs with `SETUP` when `FWC_INSTALL` is empty, and a test must
87
+ now also pass with the change, so a missing toolchain is no longer evidence.
88
+ - Access control: `.claude/settings.json` denies reading `.env`, `.env.*`, `*.pem`, and `*.key`
89
+ (also through `cat` and the like); a project that needs one, such as `.env.example`, takes the
90
+ rule out with `"remove": {"permissions": {"deny": ["Read(**/.env.*)"]}}` in
91
+ `.claude/settings.project.json`, which `scripts/gen-settings.sh` now supports.
92
+ - Metrics: the measure "Regression of" (`none` or `#N`), required by readiness;
93
+ `scripts/pr-metrics.sh` adds the regression rate, the hours to merge, and the median and
94
+ maximum of each measure.
95
+ - A small diff: readiness warns, without failing, above `DIFF_WARN_LINES` (default 400) changed
96
+ lines, generated files left out.
97
+ - `contracts/*` is class high by default in `.claude/path-classes`.
98
+
99
+ ### Changed
100
+
101
+ - Review before the pull request runs without the `dual_review` module too: one pass by a fresh
102
+ Claude subagent, which `create-pr` runs, for classes normal, gate, and high; the module adds
103
+ the Codex CLI and the Santa loop. `review-code` always checks that "Checks" names it.
104
+ - `.claude/hooks/protect-docs.sh`: a target behind `cd DIR` or `git -C DIR` resolves against
105
+ that directory; the file names in an interpreter's heredoc are targets; the body of another
106
+ heredoc is not read as commands.
107
+ - `docs/contributing.md`: section 1, a session that stops unfinished leaves a checkpoint
108
+ (commits pushed, progress and blockers in the plan or the description), and GitHub deletes
109
+ merged branches; section 5, shorter; section 6, a defect fix gives the evidence pack.
110
+ - `docs/documentation-rules.md`, section 2: acceptance criteria as Given, When, Then with their
111
+ checks; architecture holds the system map with domain ownership and dependency rules.
112
+ `docs/testing-rules.md`: the project names the levels of its verification ladder.
113
+
114
+ ### Seeded files: what an existing project does by hand
115
+
116
+ - `.claude/harness.conf`: add `SETUP=`, `CHECK_FAST=scripts/check-docs.sh` (or the project's
117
+ fast check), `E2E=`, `RESET=`, and `DIFF_WARN_LINES=400`.
118
+ - `.claude/path-classes`: add `high contracts/*` when the project has contracts.
119
+ - `docs/README.md`: the row "Environment commands" names `scripts/env.sh`; the planned rows of
120
+ architecture and operations name the system map, domain ownership, and dependency rules, and
121
+ logs, traces, metrics, and scenario replay.
122
+ - On GitHub, turn on "Automatically delete head branches" in the repository settings.
123
+
124
+ ## [0.5.0] - 2026-10-31
125
+
126
+ The process follows the Agent-Friendly Engineering principles: the owner sets outcomes and
127
+ risks, agents report evidence, checks enforce, and governance follows risk and reversibility.
128
+ Recurring failure addressed: in `xandronak/brain`, pull requests #14, #15, and #18 each needed
129
+ three or more AI review rounds that never converged, because every round found new edge cases;
130
+ and each owner decision needed a separate confirmation. Additions without an observed failure,
131
+ from the owner's decisions on the AFE field guide (2026-10-30): the invariants register, the
132
+ environment commands, access control for irreversible actions, and the size budget. Open pull
133
+ requests of an existing project need the new description sections before readiness passes.
134
+
135
+ ### Added
136
+
137
+ - Path classes (`docs/documentation-rules.md`, section 8): low, normal, gate, high, approval, by
138
+ built-in rules and the new seeded `.claude/path-classes` (`<class> <glob>` lines), with
139
+ defaults for several ecosystems. Each class decides approval before the edit (approval: the
140
+ protect hook asks), the listing in the pull request (gate: "Gate files"), and the review
141
+ before it (none, one reviewer, or the Santa loop for high). `scripts/lib/classes.sh` and
142
+ `scripts/check-decisions.sh --classes` print the class of each changed path and the review it
143
+ needs. `AGENTS.md`, section 4, follows the classes.
144
+ - Loops (`docs/contributing.md`, sections 3 and 6): the Ralph loop on `scripts/check.sh` for
145
+ every change, capped by `RALPH_MAX_ITERATIONS` (default 5) in the new seeded
146
+ `.claude/harness.conf`; the Santa loop for class high only, with a Claude subagent and the
147
+ Codex CLI until neither finds a blocker, and after the third round the open blockers go to the
148
+ owner. Other classes get one pass; only the fix of a blocker is checked again.
149
+ - Definition of done in `docs/contributing.md`, section 3, which `AGENTS.md`, section 6, points
150
+ at; it replaces the definition-of-done line of `docs/documentation-rules.md`, section 6.
151
+ - Modules `acceptance_check` (`scripts/check-acceptance.sh`: every identifier of the Acceptance
152
+ section appears in a test file of `TEST_GLOBS`; run by readiness) and `fail_without_change`
153
+ (`scripts/fail-without-change.sh`: each new or changed test fails on the base, in a temporary
154
+ worktree, with the project's own `FWC_INSTALL` and `FWC_TEST`, an optional `FWC_CLASSIFIER`,
155
+ and `--allow-pass-on-base` for the label `tests-pass-on-base`). Both off by default.
156
+ - `scripts/check-decisions.sh --ready` also fails on: no "Acceptance and test plan"; an
157
+ Acceptance item that is neither "None" with a reason nor names an existing path in backticks;
158
+ an added `Decided by the owner, YYYY-MM-DD` marker in a Markdown file that "Owner decisions"
159
+ does not name; a touched gate file that "Gate files" does not name; Metrics tables outside
160
+ their forms, answers that do not add up, a blocker recorded as a limit, or a missing measure.
161
+ - `scripts/pr-metrics.sh`: sums the Metrics tables of merged pull requests with `gh`, names the
162
+ sensors without a finding, and warns when `--limit` cuts the period. `docs/maintaining.md`,
163
+ section 6 (this repository): at each release a rule or check that never fired is proposed for
164
+ removal, and a new one names the recurring failure it addresses.
165
+ - Access control: `.claude/settings.json` is now generated by `scripts/gen-settings.sh` from the
166
+ template-owned `.claude/settings.template.json` and the new seeded
167
+ `.claude/settings.project.json`, where the project adds its own permission rules; the template
168
+ denies force pushes and remote branch deletions in the forms its rules spell out.
169
+ `scripts/check-template.sh` fails on a stale `.claude/settings.json`.
170
+ - `.claude/hooks/protect-docs.sh`: asks for class approval paths (`approval` lines of
171
+ `.claude/path-classes`, `.claude/settings.project.json`, `.mcp.json`, the generated settings),
172
+ and for the irreversible commands it names: `git push` that forces, deletes, mirrors, prunes,
173
+ or pushes tags (`--tags`, `refs/tags/...`, `v<digit>...`), `gh pr merge`, `gh release create`
174
+ and `delete`, `gh repo delete`.
175
+ - Line budgets checked by `scripts/check-template.sh`: `AGENTS.md` 90 template lines,
176
+ `docs/documentation-rules.md` 200, `docs/contributing.md` 200, `docs/testing-rules.md` 80. The
177
+ four rule documents together are shorter than in 0.4.1.
178
+ - The invariants register `docs/invariants.md` as a planned document kind; its invariants are
179
+ criteria of the Santa loop. Environment commands in the map, pointed at from `AGENTS.md`. One
180
+ git worktree per parallel session (`docs/contributing.md`, section 1).
181
+
182
+ ### Changed
183
+
184
+ - `docs/contributing.md`, section 3: description sections Summary, Acceptance and test plan,
185
+ Checks, Assumptions and open questions, Owner decisions, Gate files, Metrics (two fixed
186
+ tables), Notes for the owner. The new label `limit:` records a suggestion the branch does not
187
+ fix. A failing check keeps its command and output. The owner's merge confirms the listed
188
+ markers; readiness waits for no confirming comment and no second round.
189
+ - `docs/contributing.md`, section 6: AI review is a sensor; classes `blocker` and `suggestion`;
190
+ each finding is answered once; a suggestion never drives a loop. The `docs-review` and
191
+ `code-review` workflows run once, when a pull request opens or leaves draft; a push or a reopen
192
+ does not run them again.
193
+ - `docs/testing-rules.md`: the agent derives the test plan from the identifiers, as a report; a
194
+ refactoring keeps the tests; a behaviour change changes the requirement first, then the test
195
+ with the same identifier; gate configurations are class gate; the threat model of development
196
+ gates only decides which bypass is a blocker.
197
+ - Skills: `dual-review` reviews by class; `create-pr` runs the Ralph loop, writes the new
198
+ sections, waits for the review runs once, and has a fixed blocker checked once by a fresh
199
+ subagent; `review-code` and `docs-review` use the two classes and no longer judge whether a
200
+ marker matches the owner's words.
201
+
202
+ ### Removed
203
+
204
+ - The `should`/`nit` classes and their example table, the brief rule for later rounds, the
205
+ escalation after three rounds outside the Santa loop, "Owner decisions to confirm" and its
206
+ confirming comment, the template-update exemption of 0.4.1 (now class low), and the checklist
207
+ of `docs/contributing.md`, section 4, which is now one paragraph.
208
+ - `.claude/protected-paths` is no longer seeded; the hook and the class checks still read its
209
+ glob lines as class approval.
210
+
211
+ ### Seeded files: what an existing project does by hand
212
+
213
+ - An update creates `.claude/path-classes`, `.claude/harness.conf`, and
214
+ `.claude/settings.project.json` with the defaults, and deletes `.claude/protected-paths`; a
215
+ `_migrations` entry of `copier.yml` appends its glob lines to `.claude/path-classes` as
216
+ `approval <glob>`. Check them by hand: gate configuration, such as test runner and linter
217
+ files, becomes `gate <glob>`. Add the project's own high paths
218
+ (brain: policy, limits, accounting). Set `TEST_GLOBS`, `FWC_TEST`, and `FWC_INSTALL` in
219
+ `.claude/harness.conf` before turning on the new modules (`copier update --data
220
+ acceptance_check=true --data fail_without_change=true`).
221
+ - `.claude/settings.json` becomes generated: a project that edited it moves its own rules into
222
+ `.claude/settings.project.json` and runs `scripts/gen-settings.sh`.
223
+ - `docs/README.md`: add the row "Environment commands: set up, check, fast check, end-to-end
224
+ tests, reset" (check: `scripts/check.sh`; the others the project's commands or "not
225
+ decided"), the planned row "Invariants" (`docs/invariants.md`), and in the rows of the agent
226
+ rules, the documentation rules, and checks and enforcement the new terms: autonomy by risk,
227
+ when a change is done, path classes and access, `.claude/path-classes`,
228
+ `.claude/harness.conf`, `.claude/settings.project.json`, `scripts/pr-metrics.sh`.
229
+
230
+ ## [0.4.1] - 2026-10-30
231
+
232
+ ### Changed
233
+
234
+ - `docs/contributing.md`, sections 3, 4, and 6 (with `dual_review`): a template update that
235
+ changes only template-owned and generated files is exempt from the dual review; its
236
+ description says "Dual review: not run: template update to X.Y.Z". The `create-pr`,
237
+ `dual-review`, and `review-code` skills apply the exemption: `review-code` takes that line in
238
+ place of a verdict.
239
+
240
+ ## [0.4.0] - 2026-10-30
241
+
242
+ ### Changed
243
+
244
+ - `docs/testing-rules.md`, section 4: a threat model of development gates. Gates catch honest
245
+ mistakes and shortcuts; deliberate evasion is left to the review of the diff; product
246
+ invariants are not development gates. The protected-paths bullet now names the forms the
247
+ protect hook catches and what is left to the review, instead of claiming that no agent loosens
248
+ a gate.
249
+ - `docs/documentation-rules.md`, section 7: the merge-readiness row names what
250
+ `scripts/check-decisions.sh --ready` checks and what is left to the owner.
251
+ - `docs/contributing.md`, section 6: a finding that only deliberate evasion can exploit to bypass
252
+ a development gate is a `nit`, a bypass of a product invariant keeps its severity, and a table
253
+ of `should` and `nit` examples calibrates every reviewer. The Tests criterion includes the test
254
+ plan. With `dual_review`, later rounds add the owner's decisions on the branch to the brief, and
255
+ nothing else.
256
+ - `docs/contributing.md`, section 1: "one logical change" means one mechanism with its tests and
257
+ owner documents, with examples.
258
+ - `docs/contributing.md`, section 3: the description gains the sections "Test plan" (always,
259
+ written before the implementation) and "Owner decisions to confirm" (each `Decided by the
260
+ owner` marker added on a chat instruction, confirmed by the owner with one comment); a pull
261
+ request is ready to merge only after that confirmation. `scripts/check-decisions.sh --ready`
262
+ does not check the confirmation.
263
+ - `docs/documentation-rules.md`, sections 3 and 7: a statement about a check names the forms it
264
+ catches and what is left to the review; an absolute claim needs a test for each form it covers.
265
+ - `create-pr` skill: writes the two new sections and reports unconfirmed owner decisions as
266
+ waiting on the owner. `review-code` skill: compares the code and tests with the test plan.
267
+ `dual-review` skill: an author's self-check before the first round, the severity examples and
268
+ the test plan in the brief, and the owner's decisions from the second round.
269
+
270
+ ### Fixed
271
+
272
+ - `.claude/hooks/protect-docs.sh`: judges a target path by the git repository that holds it when
273
+ that repository has `.template/owned.txt`, so an edit in a sibling git worktree, or in another
274
+ generated project, asks the owner like an edit in the session's own checkout. Other paths
275
+ inside the session's project are judged by the project as before (no git yet, a nested
276
+ repository); paths elsewhere stay unguarded. A relative path of a shell command resolves
277
+ against the hook input's `cwd`.
278
+ - `.claude/hooks/protect-docs.sh`: folds `.` and `..` in the part of a target path that does not
279
+ exist yet before the match, so a write to `newdir/../AGENTS.md` or `docs/new/../README.md` asks
280
+ the owner, and a path whose `..` leaves the repository no longer matches a protected glob.
281
+
282
+ ## [0.3.1] - 2026-10-30
283
+
284
+ ### Changed
285
+
286
+ - `docs/contributing.md`, section 6: defines the severities `blocker`, `should`, and `nit`, and
287
+ that a review fails on a `blocker` or `should` only; a reviewer who hesitates picks `nit`. The
288
+ `review-code`, `docs-review`, and `dual-review` skills apply these definitions, and a `nit` no
289
+ longer fails a round of the dual review.
290
+ - `review-code` and `docs-review` skills: in a pull request run they read changed files of
291
+ `.claude/`, `CLAUDE.md`, and `.mcp.json` from the diff and `.claude-pr/`, because
292
+ `anthropics/claude-code-action` restores those paths from the base branch.
293
+
294
+ ## [0.3.0] - 2026-10-30
295
+
296
+ ### Added
297
+
298
+ - Testing rules (`docs/testing-rules.md`), template-owned: what a test is for, how it proves
299
+ behaviour, tests and the change they cover, and the gates no agent loosens. `AGENTS.md`,
300
+ section 2, points to it; `docs/documentation-rules.md`, sections 2, 6, and 7, list it.
301
+ - Review of pull requests (`docs/contributing.md`, section 6): a rubric that every reviewer
302
+ applies, the project's own criteria in an owner document that the map names under "Review
303
+ criteria", and the rule that a defect found after the merge names what keeps the next one out.
304
+ - Module `code_review` (question `code_review`, default on): the `code-review` workflow
305
+ (`.github/workflows/code-review.yml`) and the `review-code` skill
306
+ (`.claude/skills/review-code/SKILL.md`) review pull requests that change code, contracts,
307
+ checks, or workflows against that rubric with Claude Code, with the same secret and app as
308
+ `docs-review`, and install no plugin.
309
+ - Module `dual_review` (question `dual_review`, default off): the `dual-review` skill
310
+ (`.claude/skills/dual-review/SKILL.md`) reviews a branch before its pull request with a Claude
311
+ subagent and the Codex CLI in a read-only sandbox, after a passing `scripts/check.sh`, for at
312
+ most three rounds, without commits; its verdict goes under "Checks" in the description
313
+ (`docs/contributing.md`, sections 3 and 4).
314
+
315
+ ### Changed
316
+
317
+ - `create-pr` skill: waits for and answers the `code-review` run next to `docs-review`, and with
318
+ the `dual_review` module runs the dual review when the branch has no verdict for its tree.
319
+ - `docs/contributing.md`, section 3: every review comment is answered before the merge, from
320
+ either review workflow.
321
+ - Linter configuration (`.markdownlint-cli2.jsonc`): in `CHANGELOG.md`, headings need to be
322
+ unique among siblings only, so that each version can have its own Added and Changed.
323
+ - Seeded `docs/README.md`: rows for the testing rules and the review of pull requests, and the
324
+ planned "Review criteria" section. An existing project copies these rows by hand.
325
+
326
+ ## [0.2.0] - 2026-10-29
327
+
328
+ ### Changed
329
+
330
+ - `create-pr` skill (`.claude/skills/create-pr/SKILL.md`), with the `docs_review` module: after
331
+ opening the pull request it waits for the `docs-review` run, answers every comment (a fix in
332
+ its own commit, a decline with a reason, or a question for the owner), records the outcome in
333
+ the description, and reports it; a draft or a run that does not finish is reported instead of
334
+ waited for. `docs/contributing.md`, section 4, names the new step.
335
+
336
+ ## [0.1.0] - 2026-10-29
337
+
338
+ ### Added
339
+
340
+ - Agent rules (`AGENTS.md` with one project block, `CLAUDE.md`), documentation rules with file
341
+ ownership (`docs/documentation-rules.md`), contribution and template update rules
342
+ (`docs/contributing.md`), and the seeded documentation map (`docs/README.md`).
343
+ - Short decision records: `docs/decisions/adr-template.md` and the generated index
344
+ `docs/decisions/README.md` (`scripts/gen-decisions-index.sh`).
345
+ - Checks behind one entry point, `scripts/check.sh`: documentation (markdownlint-cli2, lychee),
346
+ decision records (integrity, next number, index, merge readiness), template integrity
347
+ (`.template/owned.sha256`), and project checks from `scripts/checks.d/`.
348
+ - Claude Code hooks (protect, Stop checks, project Stop hooks from `.claude/hooks/project/stop/`)
349
+ and skills (`create-pr`, `update-template`, and `docs-review` as the `docs_review` module).
350
+ - CI workflows `docs` (with Node.js set up), `decisions`, and `docs-review`, with the runner chosen
351
+ by a question and overridable by the repository variable `CI_RUNS_ON`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dev-oleksandrv and the contributors of xandronak/agent-docs-template
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,78 @@
1
- # Temporary Holding Version
1
+ # create-afe-project
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
+ An npm package that creates agent-friendly repositories and keeps them up to date. It gives a new
4
+ project its documentation rules, short decision records with a generated index, contribution and
5
+ pull request rules, checks that run the same way locally, in Claude Code hooks, and in CI, and the
6
+ Claude Code skills that apply them. It implements the pillars of
7
+ [Agent-Friendly Engineering](https://github.com/dev-oleksandrv/afe/blob/main/GUIDE.md). Repository:
8
+ `dev-oleksandrv/create-afe-project`; it started as a fork of the Copier template
9
+ `xandronak/agent-docs-template`.
10
+
11
+ ## What a generated project gets
12
+
13
+ | Part | Files |
14
+ | --- | --- |
15
+ | Agent rules | `AGENTS.md` with one project block, `CLAUDE.md` |
16
+ | Testing rules: what a test is for and must prove | `docs/testing-rules.md` |
17
+ | Documentation rules, statuses, and file ownership | `docs/documentation-rules.md`, `docs/README.md` |
18
+ | Branches, commits, pull requests, template updates, review of pull requests | `docs/contributing.md` |
19
+ | Decision records | `docs/decisions/adr-template.md`, a generated `docs/decisions/README.md` |
20
+ | Checks and environment | `scripts/check.sh` and the scripts it runs, the link check `scripts/check-links.mjs` included; `scripts/env.sh` for the environment commands; `scripts/protect-branch.sh` for the GitHub ruleset of `main`; optional `scripts/check-acceptance.sh` and `scripts/fail-without-change.sh` |
21
+ | Claude Code hooks, permissions, and skills | `.claude/settings.json` (generated): deny rules for secrets and destructive pushes; `.claude/hooks/`: the protect hook (asks before protected edits, publishing, and a known token format; denies a push to `main`), the check after every edit, and the Stop hooks; `.claude/skills/`: `afe-init` (sets up the code, its stack, and its checks with the owner), `create-pr`, `update-template`, optional `docs-review`, `review-code`, and `dual-review` |
22
+ | CI | `.github/workflows/` (`docs`, `decisions`, optional `secret-scan`, `docs-review`, `code-review`, and `fail-without-change`) |
23
+
24
+ Every file has one owner: the template, the project, or a generator. The rules are in
25
+ [`template/docs/documentation-rules.md.jinja`](template/docs/documentation-rules.md.jinja),
26
+ section 9.
27
+
28
+ ## Requirements
29
+
30
+ Node.js 20 or later with npm, Git, and bash. A generated project needs nothing more: its hooks
31
+ and checks, the link check included, run on Node.js.
32
+
33
+ ## Create a project
34
+
35
+ ```bash
36
+ npm create afe-project@latest <directory>
37
+ # or
38
+ npx create-afe-project@latest <directory>
39
+ ```
40
+
41
+ The wizard asks for the project name, a one-sentence description, the GitHub repository, the CI
42
+ runner (GitHub-hosted, or a self-hosted Linux runner), whether pull requests get the Claude
43
+ documentation review and code review, whether the review before a pull request adds the Santa
44
+ loop (with the Codex CLI when it is installed, otherwise with two Claude subagents), whether CI
45
+ scans every change for secrets with gitleaks, and whether to add the Acceptance check and the
46
+ fail-without-change driver. It then writes the generated files and prints the next
47
+ steps. Without a terminal, or with `--defaults`, it takes answers from `--data key=value` and
48
+ `--data-file answers.json`, then the defaults:
49
+
50
+ ```bash
51
+ npx create-afe-project@latest acme --defaults --data project_name=acme \
52
+ --data 'project_description=A sample project.' --data github_repo=acme/acme
53
+ ```
54
+
55
+ `npx create-afe-project --help` lists every option.
56
+
57
+ ## Update a project
58
+
59
+ In the project, the `update-template` skill does it, following the project's
60
+ `docs/contributing.md`, section 5: a branch from a clean `main`, then
61
+
62
+ ```bash
63
+ npx create-afe-project@latest update --defaults
64
+ ```
65
+
66
+ It replaces the template-owned files, keeps the project block of `AGENTS.md`, creates missing
67
+ seeded files and never changes existing ones, runs the migrations between the two versions, and
68
+ regenerates the generated files; then the checks and a pull request. A project generated by the
69
+ Copier template converts its `.copier-answers.yml` to `.afe-answers.json` on its first update.
70
+
71
+ ## Versions
72
+
73
+ Releases are npm versions, tagged `vMAJOR.MINOR.PATCH` in Git and described in [`CHANGELOG.md`](CHANGELOG.md).
74
+ How versions are chosen and released: [`docs/maintaining.md`](docs/maintaining.md).
75
+
76
+ ## License
77
+
78
+ [MIT](LICENSE).
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ // Command line of create-afe-project: create a project, or update one to this package's template.
3
+ import { readFileSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ import { parseArgs } from 'node:util';
6
+ import * as p from '@clack/prompts';
7
+ import { UsageError } from '../src/answers.js';
8
+ import { VERSION, create, update } from '../src/project.js';
9
+
10
+ const HELP = `create-afe-project ${VERSION}
11
+
12
+ Usage:
13
+ npm create afe-project@latest [directory] [-- options]
14
+ npx create-afe-project@latest [directory] [options] create a project
15
+ npx create-afe-project@latest update [options] update the project in the current directory
16
+
17
+ Options:
18
+ --defaults do not ask: take --data, then the recorded answers, then the defaults
19
+ --data key=value answer a question; repeatable
20
+ --data-file <file> answers from a JSON file; --data wins over it
21
+ --force create: write into a directory that is not empty
22
+ --no-git create: do not make the initial Git commit on main
23
+ --allow-dirty update: run with uncommitted changes
24
+ --quiet print only errors and the result
25
+ -h, --help print this help
26
+ -v, --version print the version
27
+ `;
28
+
29
+ function given(values) {
30
+ const data = values['data-file'] ? JSON.parse(readFileSync(values['data-file'], 'utf8')) : {};
31
+ for (const pair of values.data ?? []) {
32
+ const i = pair.indexOf('=');
33
+ if (i < 1) throw new UsageError(`--data expects key=value, got ${pair}`);
34
+ data[pair.slice(0, i)] = pair.slice(i + 1);
35
+ }
36
+ return Object.fromEntries(Object.entries(data).filter(([k]) => !k.startsWith('_')));
37
+ }
38
+
39
+ async function main() {
40
+ const { values, positionals } = parseArgs({
41
+ allowPositionals: true,
42
+ options: {
43
+ defaults: { type: 'boolean' },
44
+ data: { type: 'string', multiple: true },
45
+ 'data-file': { type: 'string' },
46
+ force: { type: 'boolean' },
47
+ 'no-git': { type: 'boolean' },
48
+ 'allow-dirty': { type: 'boolean' },
49
+ quiet: { type: 'boolean' },
50
+ help: { type: 'boolean', short: 'h' },
51
+ version: { type: 'boolean', short: 'v' },
52
+ },
53
+ });
54
+ if (values.help) return void process.stdout.write(HELP);
55
+ if (values.version) return void process.stdout.write(`${VERSION}\n`);
56
+
57
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !values.defaults;
58
+ const quiet = Boolean(values.quiet);
59
+ const common = { given: given(values), useDefaults: Boolean(values.defaults), interactive, quiet };
60
+ if (interactive) p.intro(`create-afe-project ${VERSION}`);
61
+
62
+ if (positionals[0] === 'update') {
63
+ if (positionals.length > 1) throw new UsageError('update takes no directory; run it in the project');
64
+ const r = await update(process.cwd(), { ...common, allowDirty: Boolean(values['allow-dirty']) });
65
+ const lines = [`Updated the template from ${r.from ?? 'an unrecorded version'} to ${r.to}.`];
66
+ if (r.legacy) lines.push('Converted .copier-answers.yml to .afe-answers.json.');
67
+ if (r.migrations.length) lines.push(`Migrations run: ${r.migrations.join(', ')}.`);
68
+ if (r.removed.length) lines.push(`Removed, no longer template-owned: ${r.removed.join(', ')}.`);
69
+ if (r.seeded.length) lines.push(`Created missing seeded files: ${r.seeded.join(', ')}.`);
70
+ if (r.unowned.length) lines.push(`Created files without an owner class (a template defect): ${r.unowned.join(', ')}.`);
71
+ if (r.edited.length) {
72
+ lines.push(`Replaced local edits of template-owned files; review them in git diff: ${r.edited.join(', ')}.`);
73
+ }
74
+ lines.push('Next: scripts/check.sh, then a pull request (docs/contributing.md, section 5).');
75
+ return void (interactive ? p.outro(lines.join('\n')) : console.log(lines.join('\n')));
76
+ }
77
+
78
+ if (positionals.length > 1) throw new UsageError('expected at most one directory');
79
+ let dir = positionals[0];
80
+ if (!dir) {
81
+ if (!interactive) throw new UsageError('name the directory to create the project in');
82
+ dir = await p.text({ message: 'Where should the project go? (a new or empty directory)', placeholder: 'my-project', validate: (v) => (v?.trim() ? undefined : 'Required') });
83
+ if (p.isCancel(dir)) {
84
+ p.cancel('Cancelled; nothing was written.');
85
+ process.exit(130);
86
+ }
87
+ }
88
+ if (interactive) {
89
+ p.note(
90
+ 'A few questions about the project, then yes/no questions that turn optional checks on.\n' +
91
+ 'Press Enter to keep a default. To change an answer later, run\n' +
92
+ 'npx create-afe-project@latest update in the project.',
93
+ );
94
+ }
95
+ const spinner = interactive && !quiet ? p.spinner() : null;
96
+ let spinning = false;
97
+ const { message } = await create(resolve(dir), {
98
+ ...common,
99
+ force: Boolean(values.force),
100
+ git: !values['no-git'],
101
+ quiet: quiet || interactive,
102
+ // Start the spinner once: a second start() leaves its first timer running, and the process
103
+ // never exits.
104
+ log: (msg) => {
105
+ if (!spinner) return;
106
+ if (spinning) spinner.message(msg);
107
+ else spinner.start(msg);
108
+ spinning = true;
109
+ },
110
+ });
111
+ spinner?.stop('Generated files written');
112
+ if (interactive) p.outro(message);
113
+ else console.log(message);
114
+ }
115
+
116
+ main().catch((error) => {
117
+ if (error instanceof UsageError || error?.code?.startsWith?.('ERR_PARSE_ARGS')) {
118
+ console.error(`create-afe-project: ${error.message}\nRun create-afe-project --help for usage.`);
119
+ process.exit(2);
120
+ }
121
+ console.error(`create-afe-project: ${error.message}`);
122
+ process.exit(1);
123
+ });
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "create-afe-project",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Create and update agent-friendly repositories: documentation rules, decision records, checks, and Claude Code hooks and skills.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-afe-project": "bin/create-afe-project.js"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "src/",
13
+ "template/",
14
+ "CHANGELOG.md"
15
+ ],
16
+ "scripts": {
17
+ "test": "node --test test/*.test.js",
18
+ "changeset": "changeset",
19
+ "version-packages": "changeset version && npm install --package-lock-only --ignore-scripts && scripts/sync-self.sh"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/dev-oleksandrv/create-afe-project.git"
27
+ },
28
+ "keywords": [
29
+ "create",
30
+ "scaffold",
31
+ "template",
32
+ "agents",
33
+ "claude-code",
34
+ "afe"
35
+ ],
36
+ "dependencies": {
37
+ "@clack/prompts": "^1.8.1",
38
+ "nunjucks": "^3.2.4",
39
+ "yaml": "^2.9.1"
40
+ },
41
+ "devDependencies": {
42
+ "@changesets/changelog-github": "1.0.1",
43
+ "@changesets/cli": "3.0.3"
44
+ }
45
+ }