Foundation · 03 · Skills, commands & automationLesson 3 of 4
Hooks & running without the chat
- Automate checks and actions around Claude's work with hooks
- Run Claude Code headless for scripted, repeatable jobs
- Understand what should be a skill vs a hook vs a script
Hooks: the thing CLAUDE.md cannot guarantee
Two things will exist after this lesson that did not this morning: a guard hook that blocks bad commands on every machine that clones the project, and your lesson-two skill running headless with its cost on a receipt. First, hooks. CLAUDE.md says "never touch the archive folder" and Claude follows it - almost always. A hook makes "almost always" into "always": a check that runs automatically at a fixed moment in Claude's work, enforced by the tool itself, not by the model's judgment.
That is the whole distinction to carry: CLAUDE.md is advisory, hooks are deterministic. Anything you would put in bold with three exclamation marks in CLAUDE.md is a candidate for a hook instead.
Hooks fire at lifecycle events. There are 33 as of September 2026 (recent additions include PreModelSwitch and PostModelSwitch, so a hook can veto a model change); four cover nearly every operator need: PreToolUse (before Claude uses a tool - your chance to block), PostToolUse (after - your chance to clean up or check), SessionStart (set the stage), and Stop (when Claude tries to finish - your chance to refuse until the work passes checks).
Two starter hooks, copy-paste ready
Hooks are configured in settings.json under a hooks key: which event, an optional matcher for which tool, and what to run. Exit code 0 means proceed; exit code 2 means block, with the message shown to Claude so it can adjust.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/guard.py"
}
]
}
]
}
}import json, sys
payload = json.load(sys.stdin)
cmd = payload.get("tool_input", {}).get("command", "")
BLOCKED = ["rm -rf", "archive/"]
for bad in BLOCKED:
if bad in cmd:
print(f"Blocked by team policy: {bad}", file=sys.stderr)
sys.exit(2)
sys.exit(0)The second canonical pattern is PostToolUse on Edit and Write events - run a formatter or checker on every file Claude touches, automatically, every time. And the third is a Stop hook that blocks the session from ending until your checks pass - the strongest rung of week two's verification ladder, now enforced by machinery.
- Browse and manage hooks in-session with /hooks.
- PreToolUse hooks can extend permission checks but never override your deny rules - deny still always wins.
- Have Claude write your hooks: "Write a PreToolUse hook that blocks any Bash command touching the archive folder." It knows the format.
Headless: Claude as a command, not a conversation
Everything so far happened in a chat session. Headless mode removes the chat: claude -p runs one prompt, prints the result, and exits. Which means anything that can run a command on a schedule - and every computer can - can now run your Claude workflows.
# one-shot prompt, plain text out
claude -p "Summarize the files in inbox/ as a markdown list"
# JSON out - includes the result, session id, and total_cost_usd
claude -p "Count rows per CSV in exports/" --output-format json
# run a SKILL headless - this is the payoff of lesson 2
claude -p "/call-notes transcripts/latest.txt" \
--permission-mode dontAsk \
--allowedTools "Read,Write,Bash(ls *)"- --output-format json gives structured output including
total_cost_usdand a per-model breakdown; --json-schema goes further and returns typed fields you define (and now errors on an invalid schema instead of silently ignoring it) - report numbers as data, not prose. - --allowedTools whitelists exactly what the run may do; combined with --permission-mode dontAsk, anything not allowed is refused rather than waiting on a prompt nobody will answer. Headless runs start in Manual on every plan, so you must pass the mode; auto mode being the interactive default does not carry over.
- --permission-prompts none is the belt to dontAsk's braces on an unattended host: anything that would have prompted is denied outright.
- --resume continues a previous session by id when a job needs memory between runs.
One billing note from week one, now relevant: the separate Agent SDK credit announced for June 15, 2026 was paused the same day and never took effect. Headless and SDK runs on a subscription still draw from your plan's usage limits. A job that runs every hour competes with your own sessions; give it an API key and its own budget line (the two-wallets rule).
Skill vs hook vs script: the routing question
- Skill - a procedure someone (you or Claude) chooses to run. "How to process a call transcript."
- Hook - a guarantee that fires automatically at a moment. "Nothing ever deletes from archive/." Nobody chooses it; that is the point.
- Headless script - a skill or prompt that runs on a trigger with no human. "The Monday report assembles itself at 8am."
They stack: the skill encodes the procedure, hooks guard it while it runs, headless runs it on schedule. Your capstone next week is exactly this stack, plus a connected tool from the MCP module.
This lesson is rung three of the Ladder. Rung one: do it once, in chat. Rung two: do it twice, find or build a skill. Rung three: it runs on a schedule - hooks and headless make it an automation, /loop 30m <prompt> repeats a prompt inside a live session (self-paced without an interval, expires after seven days; ask Claude to cancel it sooner), and the capstone adds Routines, cloud-scheduled runs on Anthropic's infrastructure. Rung four, when the work needs judgment mid-flow, is an agent - the vertical tracks build those. Each rung removes a human from one more step.
Do this now
Sources and further reading
Want us to set it up with you, end to end?
Three one-on-one sessions. We train you on your real stack and build your first agents together, until you can run it yourself. You keep everything.