repotemplates 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. repotemplates-0.13.0/.gitignore +31 -0
  2. repotemplates-0.13.0/CHANGELOG.md +592 -0
  3. repotemplates-0.13.0/PKG-INFO +271 -0
  4. repotemplates-0.13.0/README.md +260 -0
  5. repotemplates-0.13.0/VERSION +1 -0
  6. repotemplates-0.13.0/pyproject.toml +83 -0
  7. repotemplates-0.13.0/scripts/init.sh +373 -0
  8. repotemplates-0.13.0/scripts/renovatabot-enroll.sh +193 -0
  9. repotemplates-0.13.0/src/repotemplates/__init__.py +1 -0
  10. repotemplates-0.13.0/src/repotemplates/__main__.py +3 -0
  11. repotemplates-0.13.0/src/repotemplates/check.py +330 -0
  12. repotemplates-0.13.0/src/repotemplates/classes.py +291 -0
  13. repotemplates-0.13.0/src/repotemplates/cli.py +265 -0
  14. repotemplates-0.13.0/src/repotemplates/data/manifest.json +346 -0
  15. repotemplates-0.13.0/src/repotemplates/host.py +27 -0
  16. repotemplates-0.13.0/src/repotemplates/manifest.py +219 -0
  17. repotemplates-0.13.0/src/repotemplates/paths.py +79 -0
  18. repotemplates-0.13.0/src/repotemplates/prompts/__init__.py +1 -0
  19. repotemplates-0.13.0/src/repotemplates/prompts/install-renovatabot/SKILL.md +48 -0
  20. repotemplates-0.13.0/src/repotemplates/prompts/install-template/SKILL.md +405 -0
  21. repotemplates-0.13.0/src/repotemplates/render.py +130 -0
  22. repotemplates-0.13.0/src/repotemplates/upgrade.py +244 -0
  23. repotemplates-0.13.0/src/repotemplates/versions.py +27 -0
  24. repotemplates-0.13.0/template/.claude/CLAUDE.md +42 -0
  25. repotemplates-0.13.0/template/.claude/hooks/lint-typecheck.sh +42 -0
  26. repotemplates-0.13.0/template/.claude/settings.json +15 -0
  27. repotemplates-0.13.0/template/.gitattributes +1 -0
  28. repotemplates-0.13.0/template/.github/dependabot.yml +25 -0
  29. repotemplates-0.13.0/template/.github/renovate.json +30 -0
  30. repotemplates-0.13.0/template/.github/workflows/publish.yml +34 -0
  31. repotemplates-0.13.0/template/.github/workflows/renovate.yml +47 -0
  32. repotemplates-0.13.0/template/.github/workflows/test.yml +29 -0
  33. repotemplates-0.13.0/template/.gitignore +22 -0
  34. repotemplates-0.13.0/template/.planners/README.md +4 -0
  35. repotemplates-0.13.0/template/.planners/plans/.gitkeep +0 -0
  36. repotemplates-0.13.0/template/.pre-commit-config.yaml +34 -0
  37. repotemplates-0.13.0/template/.python-version +1 -0
  38. repotemplates-0.13.0/template/CHANGELOG.md +20 -0
  39. repotemplates-0.13.0/template/MODULE__NAME/__init__.py +0 -0
  40. repotemplates-0.13.0/template/MODULE__NAME/cli.py +11 -0
  41. repotemplates-0.13.0/template/MODULE__NAME/py.typed +0 -0
  42. repotemplates-0.13.0/template/README.md +1 -0
  43. repotemplates-0.13.0/template/pyproject.toml +91 -0
  44. repotemplates-0.13.0/template/tests/__init__.py +0 -0
  45. repotemplates-0.13.0/template/tests/test_MODULE__NAME.py +13 -0
@@ -0,0 +1,31 @@
1
+ .archive/
2
+ # .claude/
3
+ .hypothesis/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .venv/
7
+ .vscode/
8
+ .worktrees/
9
+
10
+ __pycache__/
11
+ build/
12
+ dist/
13
+ *.egg-info/
14
+
15
+ .DS_Store
16
+
17
+ .coverage
18
+ .coverage.*
19
+
20
+ # template/ is a real, resolvable uv project, so any uv command run from inside
21
+ # it (export, sync, run, lock) writes template/uv.lock as a side effect. That
22
+ # lock is scaffolding input, never an artifact of this repo: init.sh
23
+ # rsyncs template/ into each new project and only excludes __pycache__, so a
24
+ # committed lock would seed every scaffolded repo with stale, PROJECT-named
25
+ # pins instead of the one `uv sync` builds for it. Ignore here, not in
26
+ # template/.gitignore — scaffolded projects do commit their own uv.lock.
27
+ /template/uv.lock
28
+
29
+ .env
30
+ .env.*
31
+ !.env.example
@@ -0,0 +1,592 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.13.0] - 2026-11-09
11
+
12
+ ### Added
13
+
14
+ - A `repotemplates` command line, installable as a uv tool. `repotemplates check`
15
+ reports a repo's template stamp and how its Claude hook scripts and the hook
16
+ entries in `.claude/settings.json` compare with the template: current, an earlier
17
+ release by number, a one-off, or missing, plus whether each Claude settings file is
18
+ tracked, ignored, or untracked. `--repos-dir` checks every clone under a directory
19
+ as one table, `--json` and `--markdown` change the output, and the exit status is 0
20
+ when everything is current and 1 when anything differs. The package ships the
21
+ template and a manifest of every release's files, so it needs no clone.
22
+ - A publish workflow for the package, off until the `PUBLISH_ENABLED` repository
23
+ variable is `true`.
24
+ - The `install-template` and `install-renovatabot` skills ship inside the package.
25
+ `repotemplates install` writes version-stamped stubs for Claude Code and Codex
26
+ (globally by default, `--local` for one repo), and `repotemplates skill <name>`
27
+ prints a skill from the installed release. `repotemplates path template` and
28
+ `repotemplates path scripts` print the directories the release ships, which the
29
+ skills now read instead of a checkout's `template/` and `scripts/`.
30
+ - `repotemplates init <path>` scaffolds a repo from the template the installed release
31
+ ships, stamped with that release. It runs the release's own `init.sh`, which gains
32
+ `--template-dir` (copy a plain template directory instead of cloning) and
33
+ `--template-version`. The curl-to-bash path is unchanged.
34
+ - `repotemplates check` covers more template-owned files: the pre-commit config and the
35
+ three workflows (compared with pins and the Python matrix masked, with a separate pin
36
+ verdict in which a newer pin is not drift), `dependabot.yml` and `renovate.json`
37
+ (exact), and `.gitignore` and `.gitattributes` (each template rule must hold, checked
38
+ with `git check-ignore` and `git check-attr`, and waived where the repo tracks what
39
+ the rule would ignore). A file the repo leaves out on purpose reads `omitted`: the
40
+ other updater's files, or a path in the stamp table's new `omit` list.
41
+ - `repotemplates upgrade` applies the mechanical part of a template upgrade: it rewrites
42
+ the template-owned files that equal an earlier release's copy (keeping a pinned file's
43
+ newer pins and Python matrix, and replacing only the `hooks` block of
44
+ `.claude/settings.json`), raises pins that are behind, reports everything else with
45
+ the reason, and stamps the release only when nothing is left. `--dry-run` prints the
46
+ plan without writing.
47
+ - The `install-template` skill reads `repotemplates check --json` in its preflight
48
+ instead of diffing template-owned files by hand, and runs `repotemplates upgrade`
49
+ before working through the rows that need judgment.
50
+
51
+ ## [0.12.0] - 2026-11-06
52
+
53
+ ### Changed
54
+
55
+ - The repo is renamed from `proj-template` to `repotemplates`, which fits the several
56
+ templates it is growing to hold. GitHub redirects the old URL for clones, fetches, and web
57
+ links. The template's Dependabot and Renovate comments link the guides at the new URL.
58
+ - Scaffolded repos record the template release in `[tool.repotemplates]` instead of
59
+ `[tool.proj-template]`; the `version` key is unchanged. The install-template skill renames
60
+ an old `[tool.proj-template]` table during an upgrade, so a repo never carries both. Tools
61
+ that read the stamp should accept either table until the fleet has moved.
62
+ - The scaffold script is renamed from `scripts/proj-init.sh` to `scripts/init.sh`, its test
63
+ from `tests/proj-init.test.sh` to `tests/init.test.sh`, and the `test-proj-init` skill to
64
+ `test-init`. The README's suggested alias is now `repo-init`. An alias or bookmark that
65
+ fetches `scripts/proj-init.sh` from `main` stops working once this release merges: point it
66
+ at `https://raw.githubusercontent.com/gitronald/repotemplates/main/scripts/init.sh`.
67
+
68
+ ## [0.11.7] - 2026-10-21
69
+
70
+ ### Changed
71
+
72
+ - The install-template skill refreshes a target's "Before finishing a task" check list in
73
+ `.claude/CLAUDE.md` along with the `## Development` tooling bullets. The list names the
74
+ checks the `Stop` hook runs, so an upgrade that brings the current hook also brings the
75
+ `ruff format --check` step that lists from before 0.8.2 lack.
76
+
77
+ ## [0.11.6] - 2026-10-21
78
+
79
+ ### Security
80
+
81
+ - The template's publish workflow pins `pypa/gh-action-pypi-publish` v1.14.3, which fixes
82
+ GHSA-5xjx-3w67-w9x9. The fix upgrades the action's bundled `certifi` and `sigstore` to drop
83
+ the emSign root certificates, whose intermediates were revoked after a key compromise
84
+ (certifi GHSA-6q48-6c7w-9gw2). The TestPyPI example in the trusted-publishers guide pins the
85
+ same release.
86
+
87
+ ### Changed
88
+
89
+ - The scaffolded `pyproject.toml` cuts its multi-line comments to one line each, two for the
90
+ sdist `only-include` note. The repo's own `pyproject.toml` does the same.
91
+ - The scaffolded `[tool.ruff]` narrows `extend-exclude` from `*.md` to `.planners/` and
92
+ `CHANGELOG.md`. ruff now formats Python fences in the README and `docs/`, and leaves plans
93
+ and the changelog alone, since they quote code as it was.
94
+ - The scaffolded `dev` group pins `ruff>=0.17,<0.18`, and the `ruff-pre-commit` hook moves to
95
+ v0.17.1 to match. The template sets `select` and `target-version`, so 0.17's new defaults
96
+ for both do not apply; imports with trailing pragma comments may be reformatted.
97
+
98
+ ## [0.11.5] - 2026-10-19
99
+
100
+ ### Removed
101
+
102
+ - The template no longer ships `.claude/settings.local.json`. Its permission lists duplicated
103
+ the global `~/.claude/settings.json` baseline. `.claude/settings.json` still carries the
104
+ `Stop` lint and type-check hook.
105
+ - The install-template skill no longer treats a target's `settings.local.json` as
106
+ template-managed. An upgrade leaves the file alone, because it holds the user's own grants.
107
+ This includes a copy seeded by templates 0.8.5 through 0.11.4. For `settings.json`, the
108
+ skill merges in the `Stop` hook and keeps everything else the repo has.
109
+
110
+ ### Fixed
111
+
112
+ - The install-template skill's close-out pulls the base branch before it deletes the local
113
+ upgrade branch. `git branch -d` checks against the local base, so it refused a branch
114
+ merged on GitHub until the merge was pulled.
115
+
116
+ ## [0.11.4] - 2026-10-19
117
+
118
+ ### Changed
119
+
120
+ - The install-template skill's upgrade mode no longer writes a `template-upgrade` plan in
121
+ the target repo. The PR body records the repo's classification, the action per sync-matrix
122
+ row, the owner's answers, and what was skipped. A plan is written only when the upgrade
123
+ spans sessions or the user asks, through `/planners`. The sync matrix still scaffolds
124
+ `.planners/` in a repo that lacks it.
125
+
126
+ ## [0.11.3] - 2026-10-19
127
+
128
+ ### Changed
129
+
130
+ - The scaffolded `test.yml` keeps one single-line comment, on `UV_PYTHON`, and drops the one
131
+ on the repeated `--python`. The full rationale for both stays in the install-template skill.
132
+ - The scaffolded `renovate.yml`, `publish.yml`, `dependabot.yml`, and
133
+ `.pre-commit-config.yaml` cut their multi-line comments to one line each. The rationale
134
+ stays in the guides under `docs/guides/`.
135
+ - The scaffolded workflows pin `astral-sh/setup-uv` v10.3.0, `actions/upload-artifact`
136
+ v7.0.2, `actions/download-artifact` v8.0.2, `actions/create-github-app-token` v3.3.0, and
137
+ `renovatebot/github-action` v46.3.8, each by commit SHA.
138
+ - The scaffolded `ruff-pre-commit` hook moves to v0.16.10, still within the `ruff<0.17`
139
+ pin, and the `dev` group raises its floors to `pyrefly>=1.3.2` and `pytest>=9.1.1`.
140
+
141
+ ## [0.11.2] - 2026-10-10
142
+
143
+ ### Changed
144
+
145
+ - The scaffolded Stop hook (`.claude/hooks/lint-typecheck.sh`) checks the whole uv
146
+ workspace from inside a member package. It still walks up to the nearest
147
+ `pyproject.toml`, but when the git toplevel of the session's directory (the worktree root
148
+ inside a worktree) declares `[tool.uv.workspace]`, it checks from there instead. It used to
149
+ stop at the member, so it linted only that package and passed while CI failed on the rest
150
+ of the repo. A nested project that is not a workspace member keeps its own root. The hook
151
+ is template-owned plumbing, so an upgrade replaces an older copy without asking.
152
+ - The scaffolded `dev` group pins `ruff>=0.16,<0.17`, the same minor as the
153
+ `ruff-pre-commit` hook, so the commit hook and CI apply one rule set. The install-template
154
+ skill and the pre-commit guide say to keep the two in step.
155
+ - The scaffolded `[tool.ruff]` sets `extend-exclude = ["*.md"]`, so ruff 0.16 no longer
156
+ reformats Python fences in READMEs, changelogs, and plans.
157
+ - The install-template skill says to commit annotation chunks with `--no-verify` and gate on
158
+ `pre-commit run --all-files`, since the whole-tree pyrefly hook fails every partial chunk.
159
+
160
+ ## [0.11.1] - 2026-09-27
161
+
162
+ ### Changed
163
+
164
+ - Template pinned versions bumped: `astral-sh/setup-uv` v10.0.1 -> v10.2.0 in both `test.yml`
165
+ and `publish.yml`, and `renovatebot/github-action` v46.2.1 -> v46.3.3 in `renovate.yml`.
166
+ Neither bump changes an input the template workflows use.
167
+
168
+ ## [0.11.0] - 2026-09-27
169
+
170
+ ### Added
171
+
172
+ - `proj-init.sh` checks for `git`, `rsync`, `gh`, `uv`, and `stanza` before creating anything
173
+ and names every missing tool at once. A missing `gh` or `stanza` used to fail partway
174
+ through and leave a half-built project directory that blocked the re-run.
175
+ - The README lists the scaffold script's prerequisites and how to set up a checkout of
176
+ proj-template for development.
177
+ - Dependabot version updates for the proj-template repo's own workflow actions and dev
178
+ tooling.
179
+
180
+ ### Changed
181
+
182
+ - CI runs the `shellcheck` pinned in `uv.lock` instead of the one the runner ships, so CI
183
+ and a local run use the same version.
184
+ - The scaffolded `.pre-commit-config.yaml` sets `default_stages: [pre-commit]`, so a merge
185
+ runs only the `planners-index` hook. Previously the ruff, pyrefly, and `planners-validate`
186
+ hooks also ran on the `post-merge` stage and each reported "Skipped".
187
+ - The scaffolded ruff lint hook uses the id `ruff-check` in place of `ruff`, which
188
+ ruff-pre-commit keeps only as a legacy alias.
189
+ - The proj-template repo has a tooling-only root `pyproject.toml` whose `dev` group supplies
190
+ `pre-commit` and `shellcheck`, so contributors run `uv sync` instead of installing either
191
+ machine-wide. It has no `[project]` table; `VERSION` stays the only version source. A root
192
+ `.python-version` pins the interpreter so `uv sync --locked` resolves the same
193
+ `requires-python` on every machine.
194
+
195
+ ## [0.10.1] - 2026-09-27
196
+
197
+ ### Fixed
198
+
199
+ - Scaffolded repos now carry the full planners integration: a `.gitattributes` marking
200
+ `.planners/README.md merge=union`, the `planners-index` post-merge hook in
201
+ `.pre-commit-config.yaml`, and `proj-init.sh` installs the `post-merge` hook stage so
202
+ that hook actually runs. Previously the template shipped neither the attribute nor the
203
+ hook, so new projects failed `planners install --check`.
204
+ - The `install-template` skill's upgrade mode and the pre-commit guide install both hook
205
+ types (`pre-commit install --hook-type pre-commit --hook-type post-merge`), and the
206
+ skill's sync matrix merges `.gitattributes`. A plain `pre-commit install` registered only
207
+ `pre-commit`, so an upgraded repo got the `planners-index` hook configured but never run.
208
+
209
+ ### Changed
210
+
211
+ - The scaffolded `planners-validate` hook runs `uv run planners validate` instead of bare
212
+ `planners validate`, matching the other local hooks.
213
+ - `tests/proj-init.test.sh` asserts that a scaffold carries `.gitattributes` and the
214
+ `planners-index` hook, and that `proj-init.sh` installs the `post-merge` hook type.
215
+ - The `install-template` skill's upgrade mode names two worktree traps that cost a real upgrade
216
+ time. Plan edits made after the worktree exists must target the worktree's copy of `plan.md` —
217
+ the file exists at both paths, so an edit aimed at the repo root lands on the mainline and the
218
+ follow-up commit reports "nothing to commit" from a clean worktree. And `pre-commit install`
219
+ run inside a worktree rewrites the *shared* `.git/hooks/pre-commit` with that worktree's venv
220
+ as `INSTALL_PYTHON`, which exits zero and skips every hook once the worktree is removed; the
221
+ skill now repairs it right after the verification run instead of at teardown.
222
+
223
+ ## [0.10.0] - 2026-09-11
224
+
225
+ ### Added
226
+
227
+ - `proj-init.sh --source <repo>` scaffolds from a local proj-template checkout or a fork's git URL
228
+ instead of the GitHub repo. It still clones, so the output is exactly the source's committed
229
+ tree — a checkout's gitignored strays (`template/uv.lock`, `.venv`) never leak in. Without
230
+ `--branch` it clones the source's HEAD: `main` on GitHub, or whatever branch a local checkout
231
+ has checked out. The script prints the commit it scaffolded from, and refuses a source with no
232
+ `template/MODULE__NAME/` before creating anything.
233
+ - Tests and CI for the scaffolding scripts: `tests/proj-init.test.sh` scaffolds from the checkout
234
+ with `gh`, `uv`, and `stanza` stubbed and asserts on the result; `tests/portability.test.sh`
235
+ fails on constructs that break on macOS (`sed -i`, `\b`, bash 4 syntax); and
236
+ `.github/workflows/test.yml` runs both on `ubuntu-latest` and `macos-latest`, checks the two
237
+ scaffolds are byte-identical, and runs `shellcheck`.
238
+
239
+ ### Changed
240
+
241
+ - Template placeholders are renamed `PROJECT` to `PROJECT__NAME` and `MODULE` to `MODULE__NAME`.
242
+ Neither can occur inside another identifier, so substitution is a plain literal match with no
243
+ `\b` word boundaries. `PROJECT__NAME` is still a valid package name, so `template/` keeps
244
+ resolving as a uv project (`__PROJECT__` would not). Existing scaffolded repos are unaffected.
245
+
246
+ ### Fixed
247
+
248
+ - `proj-init.sh` runs on stock macOS. Under bash 3.2 `${LICENSE,,}` aborted with a syntax error,
249
+ and BSD `sed` read the `\b` word boundaries as a literal `b` — so the placeholder pass matched
250
+ nothing, the script still printed "Done", and the new repo shipped with its placeholders intact.
251
+ The script now needs only bash 3.2 and POSIX `sed`/`grep`, edits files without `sed -i` while
252
+ keeping their modes, and exits with a clear error when run under a shell that is not bash, such
253
+ as `dash`.
254
+ - `proj-init.sh` rejects an empty option value such as `--branch ""` instead of silently falling
255
+ back to the default, so an unset variable passed as a flag cannot scaffold the wrong branch.
256
+
257
+ ## [0.9.2] - 2026-09-09
258
+
259
+ ### Fixed
260
+
261
+ - `proj-init.sh` no longer corrupts `CLAUDE_PROJECT_DIR` when substituting placeholders. The
262
+ content pass ran an unanchored `s/PROJECT/<name>/g`, which also rewrote the variable inside
263
+ `.claude/settings.json` and `.claude/hooks/lint-typecheck.sh` — so every freshly scaffolded repo
264
+ got a Stop hook pointing at a variable that does not exist, silently falling back to the cwd,
265
+ which is the exact failure that variable was added in 0.8.2 to fix. Both placeholders are now
266
+ matched with `\b` word boundaries; `_` is a word character, so `CLAUDE_PROJECT_DIR` cannot match
267
+ while every real placeholder still does.
268
+ - `proj-init.sh` no longer strips the executable bit from `.claude/hooks/lint-typecheck.sh`. The
269
+ substitution pass wrote through `sed > file.tmp && mv`, which replaced each file with a fresh
270
+ 0644 one; a non-executable hook command fails, so the scaffolded lint/type-check gate never ran.
271
+ The pass now uses `sed -i`, which preserves file modes.
272
+ - `proj-init.sh` falls back to the GitHub login when an account has no display name set. `.name` is
273
+ `null` for those accounts and jq renders it as the string `null`, which was written into the
274
+ scaffolded LICENSE as `Copyright (c) <year> null`.
275
+ - `proj-init.sh` no longer writes an empty LICENSE when the license fetch fails. `gh api | sed >
276
+ LICENSE` reported the exit status of `sed`, so a failed fetch left `set -e` unfired and produced
277
+ an empty file; the body is now captured in a command substitution, where the failure aborts.
278
+ - `proj-init.sh` rejects a second path argument instead of silently scaffolding the last one, and
279
+ renames `MODULE` paths by basename so a destination nested under a directory named `MODULE` no
280
+ longer has its parent rewritten into a failing `mv`.
281
+ - `proj-init.sh` no longer assigns its template clone to `TMPDIR`, a standard environment variable
282
+ that child processes read to place their own temporary files. It now uses `TEMPLATE_TMP`.
283
+ - `proj-init.sh` runs under `set -euo pipefail` rather than `set -e` alone, so an unset variable and
284
+ a failing stage anywhere in a pipeline both stop the scaffold instead of passing silently. The
285
+ placeholder-substitution pass reads its file list from a variable rather than a pipeline, so a
286
+ failing `sed` aborts instead of dying in a subshell, and `grep`'s "no matches" exit 1 is tolerated
287
+ explicitly while a real `grep` error still stops the run.
288
+ - `proj-init.sh` validates `--branch` with `git check-ref-format --branch`, rejecting a leading `-`
289
+ (which `git clone` would read as an option), embedded spaces, and `..`.
290
+ - `proj-init.sh` now validates `--license` before interpolating it into the GitHub API path.
291
+ The value went straight into `gh api "licenses/${LICENSE}"`, so a key containing `/` or `..`
292
+ walked the path and called a different endpoint (`--license ../user` reaching `/user`), and a
293
+ `?` appended a query string. Keys are now normalized to lowercase — the endpoint is
294
+ case-insensitive, so `--license MIT` keeps working — and must match `^[a-z0-9][a-z0-9.-]*$`,
295
+ which all 13 keys GitHub currently returns satisfy.
296
+
297
+ ## [0.9.1] - 2026-09-09
298
+
299
+ ### Fixed
300
+
301
+ - `proj-init.sh` no longer breaks when your GitHub display name contains a sed metacharacter. The
302
+ name is fetched with `gh api user --jq .name` and substituted into the LICENSE body, so a name
303
+ like "Ada / Lovelace" aborted the scaffold mid-run (`set -e` on a failed `s///`) and one like
304
+ "Smith & Co" silently wrote `[fullname]` into the license instead of the name. Escaping is now
305
+ centralized in a `sed_escape` helper, which the version stamp added in 0.9.0 also uses. The helper
306
+ also folds newlines to spaces, since sed reads a literal newline in a replacement as an
307
+ unterminated command and aborts the same way.
308
+
309
+ ## [0.9.0] - 2026-09-09
310
+
311
+ ### Added
312
+
313
+ - Scaffolded repos now carry a `[tool.proj-template]` table in `pyproject.toml` recording the
314
+ template release they came from, so template drift is auditable from inside each repo without a
315
+ central registry or a new file. `proj-init.sh` stamps it from the *cloned* `VERSION` rather than
316
+ the local checkout's, so `--branch dev` records the prerelease actually applied. The table is
317
+ inert to package consumers: build backends translate only `[project]`, so it never reaches the
318
+ wheel or the PyPI metadata.
319
+ - `install-template` gained a matrix row and note for re-stamping the version on upgrade, including
320
+ the rule that a partial upgrade must not stamp the newer release.
321
+
322
+ ### Changed
323
+
324
+ - The sdist `only-include` comment in `template/pyproject.toml` no longer implies the list is
325
+ exhaustive: hatchling force-includes `pyproject.toml`, `PKG-INFO`, and `.gitignore` in every
326
+ sdist regardless of the list, and neither `exclude` nor `ignore-vcs` drops them.
327
+
328
+ ## [0.8.5] - 2026-09-09
329
+
330
+ ### Changed
331
+
332
+ - `docs/guides/github-automation.md` now states why the config files link back to it as absolute
333
+ GitHub URLs rather than relative paths — `docs/` sits outside `template/`, so a scaffolded repo
334
+ gets the configs but never the guide — along with the editing rule that follows from it (thin
335
+ comments in the configs, rationale in the guide) and what to grep if the guide is ever moved.
336
+ `template/.github/workflows/renovate.yml` was the one back-link still written as a bare
337
+ `docs/guides/github-automation.md` path, unresolvable in the repos that receive it; it now
338
+ carries the same absolute URL as `dependabot.yml` and `renovate.json`.
339
+ - `template/.github/dependabot.yml` lost its `target-branch` rationale comment, which restated the
340
+ guide's Dependabot section (the options-reference quote, the manifest-scan scoping, the
341
+ security-updates caveat) in full. The header keeps the guide link — absolute, since a scaffolded
342
+ repo has no local `docs/` copy — plus a two-line note that Dependabot reads the file from the
343
+ default branch only, the one fact that makes an edit to this file silently do nothing and so
344
+ worth having where the edit happens.
345
+ - The template's Claude permissions moved out of `.claude/settings.json` into a new
346
+ `.claude/settings.local.json`, leaving `settings.json` to carry only the shared `Stop` hook.
347
+ `settings.local.json` is the file Claude Code writes machine-local permission grants to, so
348
+ scaffolded repos now accumulate their own grants in the same file the template seeds — and it
349
+ stays untracked in every target (`.claude/` is gitignored there) while proj-template tracks its
350
+ copy as the payload upgrades ship from. `git push` also moved from `ask` to `allow`.
351
+ - `install-template` skill: the `.claude/settings*.json` row is now ask-first and exempt from the
352
+ "stale template content replaces silently" path. A target's `settings.local.json` diverges by
353
+ design — Claude Code appends every grant the user accepts there — so an upgrade must show what
354
+ adopting the template's copy would change (entries added, entries removed, and entries that move
355
+ between `allow`/`ask`/`deny`, each with its direction) rather than overwriting grants the user
356
+ chose. Moves need their own approval; hook `command`/`timeout` changes in `settings.json` stay
357
+ silent-replace, since those are template-owned plumbing.
358
+ - The template's `test.yml` now passes `--python ${{ matrix.python-version }}` to `uv sync` as well
359
+ as setting the job-level `UV_PYTHON`. The flag is redundant — `UV_PYTHON` already governs that
360
+ step and every bare `uv run` after it — but the sync step is the one whose interpreter choice the
361
+ rest of the job inherits, so restating it there means a copied-out sync line, or an edit that
362
+ drops the `env` block, keeps testing the intended interpreter instead of silently falling back to
363
+ `.python-version` and running the same Python in every matrix cell. A comment records that the
364
+ restatement is deliberate, so a later reader does not "clean it up".
365
+ - `install-template` skill: the `test.yml` sync row and reconcile notes now cover the `--python`
366
+ flag on `uv sync` alongside the `UV_PYTHON` env pin, with an explicit "don't simplify this away"
367
+ note. Without it the flag reads as redundant on inspection — which it is — and an upgrade that
368
+ tidied it out would leave the repo one `env`-block edit away from the silently-no-op matrix the
369
+ neighbouring note already warns about.
370
+ - Template pinned version bumped: `astral-sh/setup-uv` v9.0.0 -> v10.0.1, in both `test.yml` and
371
+ `publish.yml`. v10's breaking change disables `enable-cache: auto` for the `pull_request_target`,
372
+ `workflow_run`, and `release` events; neither template workflow uses those triggers (`test.yml`
373
+ runs on `push`/`pull_request`, `publish.yml` on `push: tags`), so nothing changes for a scaffolded
374
+ repo beyond picking up v10's checksum-verification and manifest-timeout fixes.
375
+
376
+ ## [0.8.4] - 2026-09-09
377
+
378
+ ### Fixed
379
+
380
+ - The template's `dependabot.yml` now sets `target-branch: dev` for both ecosystems, so
381
+ dependency-update PRs open against the active branch instead of the default one. Previously they
382
+ targeted `main`, so every batch had to be retargeted by hand before it could merge into `dev`, and
383
+ Dependabot resolved manifests against `main` rather than the tree the updates would merge into.
384
+ The `github-automation` guide claimed this was a Dependabot limitation ("it can't target `dev`
385
+ directly") and used it to motivate the Renovate option; that was incorrect — `target-branch` is a
386
+ supported option and GitHub documents this exact use case. The guide now states the real
387
+ constraint instead: `dependabot.yml` is read from the default branch, so edits to it stay inert
388
+ until they reach `main`. Renovate's other advantages (no silent digest mutation, fail-closed
389
+ cooldown, no auto-merge) are unaffected.
390
+ - `install-template` skill: the `dependabot.yml` sync row now reconciles `target-branch`
391
+ alongside `groups` and `cooldown`, so a repo upgraded from an older template revision picks
392
+ up the change above instead of silently keeping PRs pointed at the default branch. The
393
+ accompanying note gates the edit on the repo actually having a `dev` branch — scaffolded
394
+ repos always do, but an older or non-template repo may not, and pointing Dependabot at a
395
+ missing branch stops its updates.
396
+
397
+ ## [0.8.3] - 2026-09-08
398
+
399
+ ### Changed
400
+
401
+ - Template pinned version bumped: the `ruff-pre-commit` hook to v0.16.6.
402
+
403
+ ## [0.8.2] - 2026-09-08
404
+
405
+ ### Changed
406
+
407
+ - `install-template` skill: the upgrade path now triages every managed file by diffing it against `template/` before applying a sync-matrix row, instead of treating "the file exists" as satisfied. A file that is absent or identical is applied silently; one that diverges but carries nothing the template lacks is replaced and reported (the repo is simply on an older template revision); one that carries content the template would drop or change — a customized value, an extra entry, a different command or timeout — is put to the user before being touched. Those questions are gathered across the whole matrix and asked in a single batched round offering replace / merge / keep, with the answers recorded in the upgrade plan's Log so the next upgrade does not re-litigate them.
408
+
409
+ ### Fixed
410
+
411
+ - The template's `Stop` hook now runs `ruff format --check .` alongside `ruff check` and `pyrefly`, mirroring the checks the test workflow enforces. The linter does not police layout — quote style, wrapping, trailing-comma expansion — so the gate previously stayed green on code CI would reject.
412
+ - The template's `Stop` hook now resolves paths independently of the agent's working directory. `.claude/settings.json` invokes the hook through `${CLAUDE_PROJECT_DIR:-.}` rather than a relative path, which previously failed whenever the agent's directory was not the project root, and `lint-typecheck.sh` walks up from the working directory to the nearest `pyproject.toml` before running `ruff` and `pyrefly` — so work inside a git worktree under `.worktrees/` is checked against that worktree's own tree and `.venv`, not the main checkout — falling back to `CLAUDE_PROJECT_DIR` when the directory sits outside any project.
413
+
414
+ ### Security
415
+
416
+ - Raise the template's `pytest` dev-dependency floor to `>=9.0.3`, past GHSA-6w46-j5rx-g56g (insecure `/tmp/pytest-of-{user}` tmpdir handling, affecting `< 9.0.3`). The previous `>=9.0.2` floor resolved to a safe version in practice but permitted a vulnerable one.
417
+
418
+ ## [0.8.1] - 2026-09-06
419
+
420
+ ### Changed
421
+
422
+ - Template workflows (`test.yml`, `publish.yml`, `renovate.yml`) now ship actions pinned to commit SHAs with a `# vX.Y.Z` comment (e.g. `actions/checkout@3d3c42e… # v7.0.1`) instead of bare version tags. This reverses the 0.6.0 switch to tags, which assumed Dependabot would not keep SHA pins current; in practice Dependabot bumps the SHA and the comment together, as observed in a downstream repo that had re-pinned to SHAs. The `install-template` skill now syncs pins to the template's SHAs and converts bare tags on upgrade, and the `install-renovatabot` skill and the GitHub automation guide describe `helpers:pinGitHubActionDigests` as a backstop for newly added actions rather than the step that introduces SHA pins; the TestPyPI snippet in the trusted-publishers guide uses the same pinned form.
423
+ - GitHub automation guide: drop the stale claim that Dependabot has no release cooldown (the template has configured one since 0.6.2).
424
+
425
+ ## [0.8.0] - 2026-09-05
426
+
427
+ ### Added
428
+
429
+ - Coverage configuration in the template `pyproject.toml`: `pytest` now runs with `--cov` by default via `addopts`, and a `[tool.coverage]` section sets the package as `source`, enables branch coverage, excludes common boilerplate lines, and enforces a `fail_under = 50` floor that projects can raise. The test workflow runs plain `uv run pytest` and inherits the same settings. The scaffolded placeholder test now invokes the CLI through `typer.testing.CliRunner` so a fresh project starts above the floor. The `install-template` sync matrix merges `[tool.coverage.*]` into upgraded repos.
430
+ - Plan 006 (parked): options for publishing coverage to Codecov, Coveralls, or a workflow artifact.
431
+
432
+ ### Changed
433
+
434
+ - `install-template` skill: apply the gitignored `.claude/*` payload in the main checkout rather than the upgrade worktree, where it would vanish on removal; pin `fail_under` to an existing repo's current total; prefer bare `--cov` over `--cov=<pkg>`; and refresh the template-owned `## Development` bullets in an existing `.claude/CLAUDE.md` instead of never touching its content.
435
+ - `test-proj-init` skill: assert the scaffold's coverage table lists the renamed module and reports the floor as reached.
436
+
437
+ ## [0.7.0] - 2026-08-09
438
+
439
+ ### Added
440
+
441
+ - `test-proj-init` repo skill: end-to-end verification of `proj-init.sh` — scaffold a dash-named project (exercising the `PROJECT`/`MODULE` placeholder split), verify placeholders, build, CLI, tests, and GitHub repo state, with a local-only mode that creates no repo.
442
+
443
+ ### Changed
444
+
445
+ - `proj-init.sh` now installs `planners` as a global uv tool when it is missing — the scaffolded pre-commit hook shells out to `planners` on PATH, so a fresh machine no longer fails its first commit.
446
+ - Template pinned versions bumped: `actions/checkout` v7.0.1, `astral-sh/setup-uv` v9.0.0, `pypa/gh-action-pypi-publish` v1.14.2, `renovatebot/github-action` v46.2.1, and the ruff pre-commit hook v0.16.1.
447
+ - GitHub automation guide: document the **Commit statuses** permission Renovate needs to record the `minimumReleaseAge` cooldown status, the optional **Administration: read** scope (automerge-only — skip it by default), and rework first-run troubleshooting as a cascade of permission gaps rather than three independent stages.
448
+
449
+ ### Fixed
450
+
451
+ - `proj-init.sh`: dashed project names (e.g. `my-tool`) no longer break the scaffold. The template now uses two placeholders — `PROJECT` for the project/repo name and `MODULE` for the Python module name — and the script derives the module name by mapping dashes to underscores, validating it before scaffolding. Previously a dashed name produced an invalid module directory and `uv sync` failed hatchling's wheel file-selection heuristic mid-scaffold.
452
+
453
+ - Template `dependabot.yml`: drop `semver-major-days` from the `github-actions` cooldown. The granular `semver-*-days` cooldown subkeys are invalid for the `github-actions` ecosystem — GitHub rejects the entire config on one, silently disabling Dependabot (no PRs). `default-days` stays on `github-actions`; `uv` keeps both. (The 0.6.2 entry below set it on both ecosystems; only `uv` is valid.)
454
+
455
+ ## [0.6.3] - 2026-06-21
456
+
457
+ ### Changed
458
+
459
+ - `install-template`: the upgrade sync matrix now reconciles each managed file's *inner* config against `template/`, not just its presence. The `test.yml` row calls out the job-level `UV_PYTHON` env pin (without it a multi-version matrix silently tests one interpreter), and the `dependabot.yml` row reconciles each ecosystem's `groups` and `cooldown` blocks.
460
+ - `install-template`: document the intentional Dependabot repo-settings policy — alerts on, security updates (`automated-security-fixes`) off — so scheduled version-update PRs plus the release-time advisory audit stay the update path, with alerts kept on purely as a warning layer.
461
+
462
+ ## [0.6.2] - 2026-06-21
463
+
464
+ ### Added
465
+
466
+ - `install-template` upgrade mode now relocates misplaced config files: when a template-managed file (e.g. a root-level `CLAUDE.md`) is genuinely the right file but in an old spot, it is `git mv`'d to the canonical path before syncing, instead of being left or duplicated. Documents that tracking `.claude/` is a per-repo decision and the `.claude/*` + `!.claude/CLAUDE.md` pattern for keeping one file tracked.
467
+ - Template `dependabot.yml` now sets a release `cooldown` (`default-days: 5`, `semver-major-days: 14`) on both the `github-actions` and `uv` ecosystems, so dependency-update PRs wait for a release to season before opening.
468
+
469
+ ### Fixed
470
+
471
+ - `install-template` upgrade mode now uses the conventional `.worktrees/template-upgrade` path (and `.worktrees/` gitignore entry) instead of `.claude/worktrees/`.
472
+
473
+ ## [0.6.1] - 2026-06-10
474
+
475
+ ### Changed
476
+
477
+ - The `install-template` skill's upgrade mode now runs through the planners lifecycle: scaffold/activate a plan in the target repo, work in a worktree at `.claude/worktrees/template-upgrade`, open a PR into `dev`, and close the plan on merge (index regen, worktree removal, pre-commit hook re-install check).
478
+
479
+ ### Fixed
480
+
481
+ - Template `test.yml` matrix was false green: bare `uv run` re-resolves to the `.python-version` pin (3.14), so every matrix cell ran the same interpreter regardless of `uv sync --python`. A job-level `UV_PYTHON: ${{ matrix.python-version }}` now outranks the pin, so each cell tests its own Python.
482
+
483
+ ## [0.6.0] - 2026-06-10
484
+
485
+ ### Added
486
+
487
+ - `install-template` skill: scaffold a new repo from the template, or upgrade an existing repo to the latest standard via a per-file sync matrix that adapts to repo type (publishable package vs app/site), requiring green ruff/pyrefly/pre-commit checks before the Claude Stop hook lands.
488
+
489
+ ### Changed
490
+
491
+ - Migrate this repo's plan tracking to the `.planners/` layout: plans moved to `.planners/plans/<NNN>-<slug>/plan.md` with a generated `.planners/README.md` index, `TODO.md` retired, and a `planners-validate` pre-commit hook.
492
+ - Template scaffold now stamps the `.planners/` layout instead of `docs/` + `TODO.md`: empty plans index, `plans/.gitkeep`, and the `planners-validate` hook block in the generated `.pre-commit-config.yaml`.
493
+ - Move `template/CLAUDE.md` into `template/.claude/`, and ignore `.claude/` in the template `.gitignore` so generated repos receive the Claude payload on disk but never track it; expand the template ignore entries (`.worktrees/`, `build/`, `dist/`, coverage files).
494
+ - Fold the per-repo `lint-and-typecheck` skill and guide into the machine-global skill — the template no longer ships `docs/guides/lint-and-typecheck.md` or `.claude/skills/lint-and-typecheck/`.
495
+ - Move `proj-init.sh` into `scripts/` — the raw-URL alias changes to `scripts/proj-init.sh`.
496
+ - Ship workflow actions pinned to specific version tags (e.g. `actions/checkout@v6.0.3`) instead of commit SHAs so Dependabot, the default updater, keeps them current; Renovate enrollment re-pins to SHA digests via `helpers:pinGitHubActionDigests`.
497
+ - Rename the `renovatabot-enroll` skill to `install-renovatabot` (script name unchanged) and document the SHA-pinning handoff in the skill and the GitHub automation guide.
498
+
499
+ ## [0.5.3] - 2026-06-08
500
+
501
+ ### Changed
502
+
503
+ - Restrict the scaffolded project's sdist to the package and core files via `[tool.hatch.build.targets.sdist]` `only-include` (`/PACKAGE`, `/README.md`, `/CHANGELOG.md`, `/LICENSE`), keeping internal plan files (`.planners/`), guides (`docs/`), the `.claude/` directory, tests, and tooling out of the published sdist.
504
+
505
+ ## [0.5.2] - 2026-06-08
506
+
507
+ ### Added
508
+
509
+ - `renovatabot-enroll.sh` detects repository visibility and warns before enrolling a **private** repo, where the least-privilege App (no Contents permission) cannot read refs and the first Renovate run fails at init. Added a "Troubleshooting the first run" section to the GitHub automation guide mapping each failure stage to its setup gap.
510
+
511
+ ### Fixed
512
+
513
+ - Align the enroll guide and skill with the script: drop the stale `--env-file` caveat (the script streams the key file) and correct the `.env` precondition to `RENOVATE_APP_PRIVATE_KEY_PATH`.
514
+
515
+ ## [0.5.1] - 2026-06-08
516
+
517
+ ### Changed
518
+
519
+ - Rename the self-hosted Renovate enrollment tooling to **renovatabot** to match the GitHub App — `scripts/renovatabot-enroll.sh`, the `renovatabot-enroll` skill, and the credentials directory `~/.config/renovatabot`. The enroll script now stores the App key as a path (`RENOVATE_APP_PRIVATE_KEY_PATH`) and streams the file into the secret, and gains `--dry-run` and `--yes` flags with a pre-change confirmation prompt.
520
+ - Expand the GitHub automation guide with the GitHub App install walkthrough and the exact App permissions Renovate needs: Dependabot-alerts read for security PRs, and Issues read-write for the dependency dashboard.
521
+
522
+ ## [0.5.0] - 2026-06-06
523
+
524
+ ### Added
525
+
526
+ - Optional self-hosted Renovate dependency automation as a scaffold-time choice (`proj-init.sh --deps dependabot|renovate`, default `dependabot`). Choosing `renovate` ships `renovate.json` + a scheduled `renovate.yml` instead of `dependabot.yml`, with security-hardened defaults: `dev`-targeted PRs, per-ecosystem grouping, a 5-day release cooldown (`minimumReleaseAge` with `timestamp-required`), no auto-merge, no silent action-digest mutation in workflows, and a least-privilege GitHub App token so update PRs trigger CI. Dependabot remains the zero-setup default.
527
+ - Maintainer tooling to enroll a scaffolded repo in self-hosted Renovate in one step: `scripts/renovatabot-enroll.sh <owner/repo>` (and the `renovatabot-enroll` skill) pushes the GitHub App secrets from a local `.env`, keeps Dependabot vulnerability alerts on while turning its security-update PRs off so only Renovate opens PRs (`--no-dependabot-toggle` to skip), and triggers the first Renovate run. Not shipped into scaffolded projects.
528
+
529
+ ### Changed
530
+
531
+ - Disable the template's PyPI publish workflow by default; it now runs only when the `PUBLISH_ENABLED` repository variable is set to `true`.
532
+ - Move the template's `.claude` permission grants from the personal `settings.local.json` into the shared, committed `settings.json`, and stop shipping a `settings.local.json` (including its `.gitignore` negations).
533
+
534
+ ## [0.4.0] - 2026-05-25
535
+
536
+ ### Added
537
+
538
+ - Agentic lint/type-check integration for scaffolded projects: a committed `Stop` hook, a `CLAUDE.md` "before finishing a task" directive, a `lint-and-typecheck` skill, and a combined ruff + pyrefly guide.
539
+
540
+ ### Changed
541
+
542
+ - Adopt the pyrefly v1.0 `strict` preset, relaxing `implicit-any` in `tests/` via a sub-config.
543
+ - Bump ruff-pre-commit to v0.15.14, and pin `pyrefly>=1.0.0` and `ruff>=0.15` in the template's dev dependencies.
544
+
545
+ ## [0.3.2] - 2026-04-30
546
+
547
+ ### Removed
548
+
549
+ - Drop GitHub issue templates from the project template.
550
+
551
+ ## [0.3.1] - 2026-04-28
552
+
553
+ ### Changed
554
+
555
+ - Bump GitHub Action versions (setup-uv 8.1.0, upload-artifact v7, and download-artifact v8).
556
+ - Run the pyrefly pre-commit hook as a local entry, and add typer as a template dependency.
557
+ - Ignore `.env` files in the template `.gitignore`.
558
+
559
+ ## [0.3.0] - 2026-04-10
560
+
561
+ ### Added
562
+
563
+ - Template infrastructure: publish workflow, Dependabot config, issue/PR templates, a `py.typed` marker, a Keep a Changelog template for scaffolded projects, and a trusted-publishers setup guide.
564
+
565
+ ### Fixed
566
+
567
+ - Correct the publish workflow and Dependabot config.
568
+
569
+ ## [0.2.0] - 2026-04-09
570
+
571
+ ### Added
572
+
573
+ - Add the pyrefly type checker to the template.
574
+ - Add a pre-commit installation guide.
575
+
576
+ ## [0.1.3] - 2026-04-01
577
+
578
+ ### Fixed
579
+
580
+ - Fix cross-platform `sed -i` compatibility in `proj-init.sh`.
581
+
582
+ ## [0.1.2] - 2026-03-30
583
+
584
+ ### Added
585
+
586
+ - Add a `--branch` flag to `proj-init.sh` (defaults to `main`).
587
+
588
+ ## [0.1.1] - 2026-03-30
589
+
590
+ ### Added
591
+
592
+ - Initial release: a uv-based Python project template with a clone-based `proj-init.sh` scaffold script, license selection, VERSION tracking, and dev/main branch setup.