Three months after generating your repo, the boilerplate upstream has improved. Two new guard hooks. A rule that catches a mistake your team made twice. Eight new solution docs for the batteries you selected. You would like those.
What you would not like is anything touching src/, which is now 40,000 lines
of your product and shares almost nothing with what was generated.
Every mechanism people reach for here makes that trade badly.
The wrong ways
Add the boilerplate as a git remote and merge. The generated repo was never a fork: it is the output of a program run with your selection. Your first commit is a squashed snapshot with no shared ancestry, so a merge is a several-hundred-file conflict in which every hunk is a manual decision.
Regenerate over the top of your repo. Some generators support this. It
overwrites package.json, CLAUDE.md, .env.example and every merged file,
losing the dependencies you added and the rules you wrote. You find out which
ones tomorrow.
Copy files across by hand from memory. You get the three you remember and miss the two that mattered, and there is no record of what you skipped, so the next person starts from zero.
The right way: regenerate beside, diff, copy deliberately
agentic.config.json, committed at the root of your repo, records exactly what
was generated: the selection, the resolved plugin ids, the layer counts, and a
content hash.
{
"version": 1,
"selection": {
"projectName": "acme",
"stack": "nextjs-vercel",
"pm": "bun",
"batteries": ["better-auth", "neon", "prisma", "stripe"],
"design": "editorial",
"mode": "team",
"targets": ["claude", "codex"],
"admin": true
},
"plugins": ["nextjs-vercel", "neon", "prisma", "better-auth", "stripe", "editorial"],
"counts": { "agents": 6, "skills": 11, "rules": 14, "hooks": 8, "solutions": 27, "mcp": 3 },
"hash": "9f2c…"
}
Because generation is deterministic (no timestamps, no random ids, sorted keys) the same config always produces byte-identical output. That is what makes a diff meaningful: every difference is either an upstream change or a change you made. Nothing is noise.
1. Regenerate into a sibling directory
bunx create-agentic-boilerplate@latest --config ./agentic.config.json --dir ../acme-upstream
Never into your repo, never with --dir .. The point is to produce a clean
reference copy to compare against.
2. Diff the agent layer only
diff -ru --new-file \
--exclude=node_modules --exclude=.next --exclude=.git \
./.claude ../acme-upstream/.claude | less
diff -ru --new-file ./docs/solutions ../acme-upstream/docs/solutions
Read it in three passes: files present upstream and absent locally (new rules, hooks, docs (usually pure gain); files present locally and absent upstream (yours, keep them); files present in both and different (the only ones needing thought) did you edit it, or did upstream?).
3. Copy what is safe
| Path | Safe to take | Why |
|---|---|---|
.claude/hooks/ | Yes, unless you edited a script | Self-contained; verify after |
.claude/rules/ | New files yes; changed files read first | You may have tightened one |
.claude/agents/, .claude/skills/ | Yes for new, read for changed | Same |
docs/solutions/ | Yes, always | Additive knowledge, no behaviour |
src/** | No | This is your product now |
package.json, tsconfig.json | No: read and apply by hand | Merged files; you have added to them |
CLAUDE.md, README.md, .env.example | No: apply by hand | Same |
A copy that is genuinely safe:
# new hooks and rules only, never overwrites a file you have edited
cp -rn ../acme-upstream/.claude/hooks/. ./.claude/hooks/
cp -rn ../acme-upstream/.claude/rules/. ./.claude/rules/
cp -rn ../acme-upstream/docs/solutions/. ./docs/solutions/
cp -n never overwrites. Anything it skipped is a file that exists in both and
needs a human decision: go back to the diff for those, one at a time.
4. Wire up and verify
A new hook script is inert until .claude/settings.json references it. Copy the
matching entry from the upstream settings file by hand (that file is merged, so
never replace it wholesale) then:
bun run verify:hooks
bun run typecheck
bun run lint
bun run test
verify:hooks is the one that matters here: it attempts each blocked action and
confirms it was stopped, which is the only proof a newly copied guard is
actually wired in.
5. Commit it separately
git checkout -b chore/upstream-agentic-layer
git add .claude docs/solutions
git commit -m "chore(agentic): pull upstream rules, hooks and solution docs"
One commit, nothing else in it. If a new guard turns out to be wrong for your repo, you revert one commit rather than untangling it from a feature.
Then delete ../acme-upstream. It is a scratch artefact: regenerating it again
costs seconds, and keeping it around invites someone to edit the wrong copy.
Checking whether anything changed at all
The hash in agentic.config.json is a sha256 over the sorted (path, content)
pairs of the generated output. Regenerate and compare:
bunx create-agentic-boilerplate@latest --config ./agentic.config.json --dir ../acme-upstream
diff <(grep '"hash"' agentic.config.json) <(grep '"hash"' ../acme-upstream/agentic.config.json) \
&& echo "identical: upstream has not changed for this selection"
Same hash, nothing to do. Worth running monthly; it takes a minute and answers the question honestly.
Two caveats
Changing the selection is not an upgrade. Adding a battery to
agentic.config.json and regenerating produces files for a battery your src/
has never integrated with. Add batteries deliberately, one at a time, reading
their onboarding steps, not through a diff.
Keep agentic.config.json committed and current. It is the only record of
what this repo was generated from. If someone deletes it, this entire workflow
stops being available, and reconstructing the selection from the file tree is
guesswork.
A native --diff flag that does the compare-and-report step for you is on the
roadmap. Until then the four commands above are the whole mechanism, and they
work today.