Completing Context Engineering for Claude Code means Core 1–3 plus the takeover test. This module is optional, for one specific situation: a procedure in your own project that you have now done by hand at least twice.
You end with a skill that encodes the procedure, and a hook that checks it never gets skipped — the same shape as the updating-claude-md skill you already ran in Core 2, whose own build is the worked example below.
The lessons
In order. Skill or command first, since the wrong choice there makes everything after it harder than it needs to be.
Skills versus custom commands versus Projects
A custom command inserts text you already wrote. It adds no new ability, and Claude can't choose to use it — you have to type it. A skill loads by its own description, so Claude picks it up on its own when the situation matches. A Project bundles instructions and files for one workspace, with no pickup logic at all. If the thing you're automating should just happen when the situation calls for it, it's a skill.
Where skills live: user, project, public
A user-level skill lives in your home folder and follows you into every project. A project-level skill ships inside the repo, so anyone who clones it gets it too. A public skill or plugin comes from someone else's marketplace listing — same shape, different trust level.
Plugins and reviewing before you install
A plugin bundles skills, commands and hooks someone else wrote. A hook runs shell commands on your machine with your session's permissions the moment it fires — read the SKILL.md and every hook script before you install, the same way you'd read a package before adding it as a dependency.
Hooks: the nine events you will actually use
Claude Code has more than 30 hook events, and the list grows with each release. Nine of them cover almost every hook worth writing, from the moment a session starts to the moment it ends. Run /hooks in your own session for the full current list. Pick the event that matches when the thing should happen, not the event that happens to be running when you write the hook.
Wiring a hook in settings.json
Registering a hook is a few lines in settings.json: which event, which command to run, an optional timeout. Nothing runs until the wiring exists — the skill and the hook script are inert on their own.
Bonus: how updating-claude-md was built
The skill from Core 2 that already keeps your CLAUDE.md current wasn't handed to you finished. It was built test-first, it failed once in a documented way, and the fix is below — the real build-along, not a description of one.
Hooks: the nine events you will actually use
Every hook you'll ever write fires on one of these. Pick the event that matches when the thing should happen.
| Event | Fires when |
|---|---|
| SessionStart | A new session begins. |
| UserPromptSubmit | You submit a prompt, before Claude reads it. |
| PreToolUse | Before a tool call runs. Can block it. |
| PostToolUse | After a tool call finishes. |
| PermissionRequest | Claude Code needs your permission for something. |
| Notification | Claude Code shows a notification, e.g. a permission prompt. |
| Stop | The main reply finishes. |
| TaskCompleted | A background task finishes. |
| SessionEnd | A session ends — the one this page's build-along uses. |
We cover what a skill actually is, and where it fits next to a custom command, in more depth on the Claude Skills page.
The assignment
This one runs on your own project. There's no reference-project snapshot for specialists — your repeated procedure stands in for the “add a competitor” step Move from idea to evidence shipped a working page from.
Your own finished core project, where "add a competitor" (or your project's equivalent recurring step) has now been done twice by hand.
A skill encoding that procedure, plus one hook.
The skill runs on a third instance and produces the same shape as the two you did by hand. The hook fires at the moment you claimed it would, verified in its log.
The Claude Code skill, the hook, the wiring
This example adapts the updating-claude-md skill from Core 2, its hook and its wiring, in the order you build them: skill first, then the hook that runs it, then the wiring that fires the hook. A reader who writes the hook first has nothing to point it at. Project details use a fictional reading-list app.
1 --- 2 name: updating-claude-md 3 description: Use when a session is about to end, when CLAUDE.md has no 4 last_updated marker, or when files, dev-docs or sessions may have changed 5 since CLAUDE.md was last updated. 6 --- 7 8 # Updating CLAUDE.md 9 10 ## Overview 11 12 CLAUDE.md loads into every session, so it holds the gist of the project and 13 says where the detail lives. The detail stays in its own folder. A marker in 14 the frontmatter records when CLAUDE.md was last brought up to date, so each 15 run only has to look at what changed since then. 16 17 Write straight to CLAUDE.md. This runs before a session ends, so there is no 18 one to approve a draft. 19 20 ## The shape CLAUDE.md always has 21 22 ```markdown 23 --- 24 last_updated: 2026-09-14T16:40 25 --- 26 # <Project name> 27 28 ## Overview 29 What it is and who it is for, in 2 to 3 lines. 30 31 ## Active work 32 - `dev-docs/2026_09_12-PROGRESS-pt1-Reading_List_App.md`: Build a 33 reading-list app that saves books and tracks reading progress. 34 ``` 35 36 ## Common mistakes 37 38 | Mistake | Fix | 39 |---|---| 40 | Progress numbers or "status as of" lines in CLAUDE.md | Active work lists the dev-doc file name and its gist | 41 | Field lists, API tables, long explanations copied in | One line and the path of the file that holds them | 42 | Marker left unchanged | The marker moves every time, even when nothing else changed | 43 | Only the latest dev-doc read | Every dev-doc not DONE appears in Active work | 44 | Hand-written guardrails dropped while restructuring | They stay; only their placement changes |
Fires on SessionEnd. The four guards are annotated inline — each one is a real failure the hook had to be corrected for. It uses your configured permissions. Test the skill interactively first and allow only the tools it needs; a background run cannot ask you to approve a blocked action. Check its log for failures.
1 #!/usr/bin/env bash 2 # SessionEnd hook: when a session ends on purpose in a project that has a 3 # CLAUDE.md, spawn a headless Claude that runs the updating-claude-md skill. 4 # Never blocks the exit. 5 LOG="$HOME/.claude/claude-md-update.log" 6 [[ "${CLAUDE_MD_UPDATER:-}" == "1" ]] && exit 0 # guard 1 — don't recurse 7 IN=$(cat 2>/dev/null || true) 8 command -v jq >/dev/null || exit 0 9 reason=$(jq -r '.reason // ""' <<<"$IN") 10 sid=$(jq -r '.session_id // ""' <<<"$IN") 11 cwd=$(jq -r '.cwd // ""' <<<"$IN") 12 case "$reason" in prompt_input_exit|logout) ;; *) exit 0 ;; esac # guard 2 — only deliberate exits 13 [[ -z "$cwd" || "$cwd" == "$HOME" || "$cwd" == *"/.claude/plugins/"* ]] && exit 0 # guard 3a 14 [[ -f "$cwd/CLAUDE.md" ]] || exit 0 # guard 3b — only projects that already have a CLAUDE.md 15 lock="$HOME/.claude/claude-md-update.lock.$(printf '%s' "$cwd" | md5 -q)" 16 mkdir "$lock" 2>/dev/null || exit 0 # guard 4 — one updater per project at a time 17 echo "$(date '+%F %T') start cwd=$cwd sid=$sid" >> "$LOG" 18 ( 19 cd "$cwd" && CLAUDE_MD_UPDATER=1 claude -p --model sonnet \ 20 --permission-mode acceptEdits \ 21 "Use the updating-claude-md skill now and update CLAUDE.md. Do not ask questions." \ 22 >> "$LOG" 2>&1 23 echo "$(date '+%F %T') done cwd=$cwd rc=$?" >> "$LOG" 24 rmdir "$lock" 2>/dev/null 25 ) </dev/null >/dev/null 2>&1 & 26 disown 27 exit 0
The four guards
Don't recurse.
The Claude the hook spawns also ends a session, which would fire the hook again, forever. An environment variable set only inside the spawned run breaks the loop.
Only deliberate exits.
Matches prompt_input_exit and logout specifically. A crash shouldn't trigger a rewrite of a file nobody was ready to have touched.
Only projects that already have a CLAUDE.md.
Skips the home directory and plugin folders, and skips any project that hasn't been introduced to the skill yet — the hook maintains a file, it doesn't create the first one.
One at a time.
A lock keyed on the project's path stops two sessions closing at once from overwriting each other's update.
Three lines in settings.json register the script against the event. Nothing runs until this exists — the skill and the hook script are inert on their own.
"SessionEnd": [
{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/claude-md-update.sh" }] }
]“A hook is when. A skill is how. claude -p is who does it — a second, headless Claude in the background, so you're never waiting.”
Skills and hooks are one way to wire a tool to Claude. When the choice is between that and a direct API call, see MCP or a single API call.
The method correcting itself
This is the skill's own documented failure, from when it was first built and tested — not a hypothetical. The first baseline run wrote progress numbers straight into CLAUDE.md, where they went stale the moment the numbers changed.
Before## Active work
- pt1-Reading_List_App: Book search working. 3 of 5 tests
passing as of 2026-09-12. Reading-progress filter still failing —
investigating filter state, ~60% done overall.Every session that opened this file after that point read a number that was already wrong. The fix wasn't a longer file — it was a rule: point-in-time progress belongs in the dev-doc it describes, never in CLAUDE.md itself. That rule is now the first row of the skill's own Common mistakes table, above.
After## Active work
- `dev-docs/2026_09_12-PROGRESS-pt1-Reading_List_App.md`: Build a
reading-list app that saves books and tracks reading progress.“Gist in CLAUDE.md, detail in the file it points to — that's the difference between instructions that stay useful and instructions nobody reads.”
Turn a job you repeat into a skill
A blank skill file to fill in, plus a one-page card of the nine hook events you will actually use, so the skill runs without you starting it.
A blank skill file and a hooks cheat sheet
Fill in the blanks to turn a job you repeat into a skill, then pick the moment it should fire from the nine most useful events on the card.
A procedure in a document“At the end of a session, update the project notes: read the work records, keep the gist, leave the detail in its own file.”
It only works when someone remembers to open the document, and Claude cannot pick it up on its own.
The same procedure as a skillSKILL.md, plus the line that fires it
A skill is how · a hook is when
- The description line
- “Use when a session is about to end, or when files may have changed since the notes were last updated.” Claude reads that line and picks the skill up without being told.
- The steps
- Read the marker, collect what changed since, write the gist into the notes, move the marker.
- The hook
- Three lines in settings.json fire it the moment a session ends, in the background, so nobody waits.
- The guards
- Do not fire on a crash, do not run twice at once, skip folders with no notes, and never let the spawned session trigger itself.
FAQ
Want your own recurring work automated, not just read about?
A working session that turns your team's repeated procedures into skills and hooks.
The course
Core modules first. Specialists are optional — take the one that matches your work.
- Core 1
Stop Claude guessing
Turn a vague idea into a brief Claude can build from: facts, assumptions marked as assumptions, agreed success criteria.
- Core 2
Stop starting over
Set up project memory so a fresh session, or a teammate, can pick the work up from the files alone.
- Core 3
Move from idea to evidence
The route from brief to reviewed change: research, one shared page, a prototype, a plan, a small build, a second opinion.
- Specialist 4
Automate recurring work
Encode a procedure you repeat as a skill, and fire it automatically with a hook.
You are here - Specialist 5
Build in parallel
Run two pieces of work at once without them blocking each other, then review how they fit together.
- Specialist 6
Run research you can trace
Research where every claim traces back to a saved source, and gaps are marked instead of filled in.
- Standalone
MCP or a single API call
A judgment call on two axes: what it costs you in context window, and how much access it opens.

Behrad Mirafshar
Founder, Bonanza Design
Founder of Bonanza Design. Builds operating brains for companies in the AI knowledge crisis. Multi-week engagements, run on the client's infrastructure, owned by the client.
Connect on LinkedIn