template-dev
Skill144 starsChanged 15 months agoMITView on GitHub
Use when developing or modifying Copier templates in aegis/templates, or backporting a change from a generated project (e.g. a prototype scratch project) back into the templates. Covers the two development workflows, the three rendering modes that resolve where Copier reads template content from, and the template-specific gotchas that silently break generation or update.
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 3 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 15
Use when the task touches template content directly (docker-compose.yml.jinja, Makefile.jinja, any file under app/, pyproject.toml.jinja, …
- Very specific: line 20
Do NOT use this skill for adding a brand-new component or service (a ComponentSpec/ServiceSpec entry, its FileManifest, and its …
- Distinctive: line 71
Every file in the tree is governed by exactly one mechanism, and which one is derived, not declared — there is no registration list.
- Very specific: line 100
No annotation means the default: overwrite while pristine (with a .backup), 3-way merge once the user has edited it.
- Distinctive: line 129
Verify the generated project behaves as expected.
- Very specific: line 146
Default (installed package, or local git repo without --dev): Copier renders the COMMITTED git state, either via a GitHub URL (pip/uvx …
- Distinctive: line 168
Start with the fast ones. These run in under a second against rendered output and catch the two mistakes template edits actually make:
- Very specific: line 178
Run those *before* generating a project.
- Very specific: line 210
Every new copier.yml question needs a matching line in .copier-answers.yml.jinja, except fields gated by a when: clause.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Template dev
Develops and backports changes to the Copier templates that every generated
Aegis Stack project is rendered from. This is the general template-editing
workflow: how to iterate, how rendering picks up (or ignores) your edits, and
the traps that only show up on aegis update, not on a fresh aegis init.
When to use
Use when the task touches template content directly (docker-compose.yml.jinja,
Makefile.jinja, any file under app/, pyproject.toml.jinja, copier.yml,
etc.) or when a fix was made inside a generated project and needs to be
carried back into the templates.
Do NOT use this skill for adding a brand-new component or service (a
ComponentSpec/ServiceSpec entry, its FileManifest, and its
generation/update plumbing) - use the add-component or add-service skill
instead; they cover the plugin-spec registry work this skill assumes is
already done. Do NOT use for ordinary feature work inside the CLI tool itself
(aegis/commands/, aegis/core/ logic unrelated to rendering) unless that
work is specifically about how templates render or update.
Files that change
Source-of-truth law: templates ship, generated projects are prototypes. The templates live under:
aegis/templates/copier-aegis-project/{{ project_slug }}/: every file a generated project contains, one-to-one. A plain file with no Jinja logic ships as-is (e.g.app/components/worker/registry.py); a file with conditional content ships with a.jinjasuffix (e.g.app/components/worker/events.py.jinja).copier.yml(repo root, not undertemplates/): the question set Copier prompts for or reads answers from.aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja: the answers file template; see Pitfalls for which fields belong here.aegis/core/copier_manager.py: renders a new project (aegis init), including the dev-mode/git-mode source selection and the post-generation answers backfill.aegis/core/copier_updater.py: resolves the template source and version ref foraegis update.aegis/core/manual_updater.py: adds/removes a single component or service on an existing project without a full Copier update.aegis/core/render_diff.py: the enginemanual_updateruses to decide what a shared file needs on add/remove. Reads the file's policy annotation; you rarely edit this, but its module docstring is the reference for the decision table below.Makefile(repo root): thetest-template*andtest-stacks*targets used to validate template changes.
Backport target mapping: any file you edit inside a generated project at
<project>/<path> has its template source at
aegis/templates/copier-aegis-project/{{ project_slug }}/<path> (or
<path>.jinja if that file needs Jinja conditionals). Backport with:
cp my-app/app/components/worker/registry.py \
"aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py"Quote the destination path; the directory name contains the literal {{ project_slug }}
placeholder, which a shell would otherwise try to expand.
Who owns a template file
Every file in the tree is governed by exactly one mechanism, and which one is derived, not declared — there is no registration list. Adding a new template file requires no bookkeeping anywhere.
- Component/service-owned — the path appears in some spec's
FileManifest(aegis/core/components.py/services.py). Its existence is decided by manifest membership:aegis addcopies it,aegis removedeletes it. Most of a component's own files. - Shared — nothing claims it. On add/remove the render-diff engine
renders the tree at the old answers and the new answers and diffs; a file
whose output changed gets written. This is where
{% if include_x %}conditionals belong.
Consequence worth internalising: a {% if include_worker %} block inside a
file the auth manifest owns will never re-render when worker is added,
because owned files are invisible to the engine by design. Cross-cutting
conditionals go in shared files.
Per-file policy annotations
A shared file can override the default handling with a comment on its very first line, read and stripped before rendering:
{#- aegis: user-owned -#} create once, then never touch (README, docs/)
{#- aegis: warn-if-diverged -#} overwrite while pristine, else preserve + report
{#- aegis: no-backup -#} overwrite without writing a .backupNo annotation means the default: overwrite while pristine (with a
.backup), 3-way merge once the user has edited it.
The -# / #- trim markers are required, not stylistic. aegis init
and aegis update render through Copier, which knows nothing about these
annotations; without the trim markers the comment leaves a blank line at
the top of every generated file. A recognised word missing its markers
raises rather than silently degrading.
Whole-file gates leave a stub
{%- if include_x -%}...{%- endif -%} wrapping an entire file still emits a
newline when the gate is off, so Copier writes a 1-byte file. That stub is
what the template currently produces, so it counts as pristine and gets
populated when the gate flips on. Don't "tidy" it by making the engine treat
existing-but-empty as user content — that is the exact bug that once left
docker-compose.prod.yml empty forever after aegis add ingress.
Procedure
Pick one of two workflows, then apply the rendering mode that matches it.
Template-first (the change is already known):
- Edit the file(s) directly under
aegis/templates/copier-aegis-project/{{ project_slug }}/.... - Generate a test project with
aegis init test-project --dev(working tree, no commit needed) ormake test-template-quick/make test-template(committed state, see rendering modes below). - Verify the generated project behaves as expected.
- Clean up with
make clean-test-projects.
Prototype-first (exploratory, the fix is easier to find by iterating directly on generated code):
- Generate a test project (
aegis init <name> --dev, or anymake test-template*target). - Iterate directly in the generated project until it works.
- Backport immediately, in the same session, using the
cppattern above for every file you touched. Do not defer this; changes left only in a generated project are lost on the nextaegis init. - Regenerate a fresh project from the templates to confirm the backport is correct, then clean up.
Rendering modes (this resolves which template content Copier actually reads):
- Default (installed package, or local git repo without
--dev): Copier renders the COMMITTED git state, either via a GitHub URL (pip/uvx install) or agit+file://URL pointing at the local repo (aegis/core/copier_manager.py, theis_git_repo(template_root)branch). Uncommitted template edits are invisible even though the files look changed locally. aegis init --dev: renders the WORKING TREE.aegis/core/copier_manager.py'sdev_modebranch copiescopier.ymlandaegis/templates/copier-aegis-projectfrom the working tree into a temporary directory and generates from that plain path, so uncommitted edits show up immediately. Use this for local iteration; no commit required. Projects generated this way have no_commitpin and cannot runaegis updatelater.- External project update:
aegis update -y -p <project> -t <aegis-stack repo> --to-version HEAD.-p/--project-pathpoints at the target project,-t/--template-pathpoints Copier at a local aegis-stack checkout instead of the installed package, and--to-version HEADresolves to the latest commit on the current branch instead of a version tag. This still reads the COMMITTED state of that checkout, not its working tree, so template changes must be committed first.
Gates
Start with the fast ones. These run in under a second against rendered output and catch the two mistakes template edits actually make:
uv run pytest tests/core/test_template_tree_hygiene.py— every template parses, and no file is shadowed by a.jinjatwin.uv run pytest tests/core/test_shared_scope_completeness.py tests/core/test_render_diff_transition_coverage.py— a new stack-dependent file is handled by something, and every add/remove transition writes the files it needs.
Run those before generating a project; a template that fails to parse
wastes a 40-second aegis init to tell you so.
Then the generation gates:
make test-templateafter any template edit (generates a project and runs its full validation, includingmake checkinside the generated project).- Whichever narrower target matches the touched component or service, for
faster iteration:
make test-template-quick(no validation),make test-template-with-components,make test-template-auth,make test-template-worker,make test-template-database,make test-template-full,make test-template-ai,make test-template-ai-memory,make test-template-ai-sqlite. make test-stacks-quickwhile iterating on a cross-cutting template change (fast feedback against a representative subset: base, everything, insights).make test-stacks-fullbefore calling multi-component template work done (generation-only pass, the slow build/validation pass, and the kitchen-sinkeverythingstack).make checkfor any non-template Python change made along the way (for example, editingcopier_manager.pyormanual_updater.py).make clean-test-projectsto remove generated test project directories once verification is done.
Pitfalls
- Gate conditional content with a full-body
{% if %}...{% endif %}wrap only. Never use Copier conditional filenames; that convention broke on Windows and is banned outright. - Carry no inline comments in
docker-compose.yml.jinjaor its.dev/.prodvariants. Which components a given project selects varies per stack, so a comment written for one combination misleads for another; state conditions through the Jinja gate itself. - Every new
copier.ymlquestion needs a matching line in.copier-answers.yml.jinja, except fields gated by awhen:clause. Copier itself dropswhen-gated answers from the rendered answers file (observed forworker_backend,scheduler_backend,include_oauth), so the actual persistence path for those iscopier_manager.py's post-generation backfill loop, which patches anycopier_datakey Copier dropped directly into the written.copier-answers.yml. Adding awhen-gated field to the.jinjalist anyway is harmless but is not what makes it survive. copieris pinned below 9.15 inpyproject.toml(9.15+ relocates.copier-answers.ymlout of the generated project directory, breakingaegis update). A dependabot ignore rule blocks upgrade PRs past that ceiling; do not bump the pin even if a bot proposes it.uv.lockhides fresh-install dependency drift. Validate a template dependency change (newpyproject.toml.jinjagates, new packages) withuvxor a freshuv syncinside a freshly generated project, not the locally locked dev environment, since the lock file can mask a resolution that only fails on a clean install.
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- a20070322/claude-code-boilerplate★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository14 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- tests/CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- llms.txtllms.txt
- add-cli-commandSkill
- add-componentSkill
- add-serviceSkill
- create-pluginSkill
- example-skillSkill
- execute-issueSkill
- i18nSkill
- releaseSkill
- squash-branchSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

