AIDive

Video pack

Claude Code habits as commands: hook, /goal, reviewer and checklist files

5 min read

TL;DR and verdict

A rule you write in CLAUDE.md is advice Claude weighs. A rule you turn into a command runs whether Claude reads it or not. We took an invoicing API (Node 20, TypeScript, vitest) on our own machine, with Claude Code 2.1.294, and ran five habits as five commands.

  • Done: with /goal, the reply printed the full vitest output, "Tests 44 passed (44)", and the goal was marked achieved after 1 turn. Without it, Claude only said "pnpm test passes (37/37)" and the output stayed folded in a tool call.
  • A rule that must hold: a PreToolUse hook blocked 2 edits to the protected migrations file, and Claude edited the rest.
  • One task per context: /clear took the conversation from 26.4k tokens to 130 tokens.
  • Load what you use: /skill-doctor found 52 skills loaded and never invoked (13 + 14 + 25).
  • Fresh review: a read-only reviewer found the same tie-order bug as the author session, plus a missing test, and claimed a clean typecheck it could not have run.

Verdict: set up the hook first, on the rule that has already been broken in your repo. /clear and /skill-doctor cost nothing. Keep /goal for long runs and the reviewer for the merge. The pack gives you the files.

Sourced synthesis

A CLAUDE.md line is not enforced. Anthropic's best practices say a line like Always typecheck before you finish works most of the time, not every time s1. The memory docs add a target of under 200 lines per CLAUDE.md, because longer files consume more context and reduce adherence s6. Our bench file had four rules, one of them in capitals: "NEVER edit src/store/migrations.ts".

/goal judges what is on screen. The evaluator does not run commands or read files, so the condition has to be something Claude's own output can demonstrate s2. That is why our condition asks for the full test output to be printed in the conversation. A field report on a team with about 600 PRs found its worst bug passed tsc, lint and the agent's own tests s8. A done-check that can never be met loops until a block cap s9.

A hook exiting 2 blocks the call. In the hooks guide, exit 2 means block the action s3. On our run, with the rule removed from CLAUDE.md, Claude went for the migrations file twice and both edits came back blocked with our message. With the capitals line in place, Claude refused on its own, and in 2 later runs it read the hook script first and never tried the file. A thread on Hacker News makes the sharpest criticism: a block is deterministic, what the model does next is not s10.

/clear is free, and it empties the right thing. Best practices say /clear between unrelated tasks costs nothing s1. After our feature, /context showed 57.5k of 1m tokens, with Messages at 26.4k. After /clear it showed 31.1k, with Messages at 130 tokens. Instructions, tools and skills stayed.

/skill-doctor names what you never use. It flags skills in the listing that have never been invoked and says where to turn them off s7. On our machine it reported 13 skills loaded but never invoked, 14 synced from claude.ai, and 25 plugin skills: 52 in all, plus 4 plugins not used recently. Listing space is tight: one write-up found that 267 extra characters in one description turned 7 other skills into bare names s12. The gain is noise, not a halved context.

A fresh reviewer helps because it has not seen your reasoning. Best practices say so directly, and add that handing the same task to a subagent does not make the answer better s1. Each subagent starts with a fresh, isolated context window s5. That has a cost: one user measured each subagent at around 51k tokens at start s14, so call it once per feature. Our reviewer ran 28 s in the background.

Code

  • goal-conditions.md

# /goal conditions

The /goal judge reads only the conversation. It does not run commands or read files, so write the condition as something Claude's own output can demonstrate. Name one end state and the proof you want printed on screen.

## 1. The one we used (overdue invoices endpoint)

```
/goal GET /invoices/overdue returns the overdue invoices with page and pageSize query params, new tests cover it, and the full pnpm test output showing every test passing is printed in this conversation
```

Result on our run: the reply printed the full vitest output, then the goal was marked achieved.

## 2. Template: a bug fix

```
/goal <the failing behaviour> no longer happens, a new test reproduces it and passes, and the full <your test command> output showing every test passing is printed in this conversation
```

## 3. Template: a refactor

```
/goal <the old name or pattern> no longer appears in the code, the output of <your search command> showing no matches is printed in this conversation, and the full <your test command> output showing every test passing is printed too
```

## Rules for all three

- One end state, not a list of wishes.
- The proof is output Claude prints (test results, a search result), never "it works".
- Do not write a condition Claude cannot meet; an unsatisfiable goal loops.
- Check it with `/goal` alone; remove it with `/goal clear`.

goal-conditions.md on GitHub

: the exact `/goal` condition we used, plus 2 templates built on the same rule. Paste one after `/goal` before you ask for a feature. -

protect-migrations.sh

#!/bin/sh
# Block edits to the append-only migrations file
path=$(jq -r '.tool_input.file_path // empty')
case "$path" in
  *src/store/migrations.ts)
    echo "migrations.ts is append-only: add a new migration file instead" >&2
    exit 2 ;;
esac
exit 0

protect-migrations.sh on GitHub

: the hook script. It reads the file path and exits 2 with a reason when the path ends in `src/store/migrations.ts`. Save it as `.claude/hooks/protect-migrations.sh` and run `chmod +x` on it. It needs `jq`. Change the path to the file you protect. -

settings.json

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Edit|Write",
        "hooks": [ { "type": "command", "command": ".claude/hooks/protect-migrations.sh" } ] }
    ]
  }
}

settings.json on GitHub

: registers the script as a PreToolUse hook on `Edit|Write`. Merge it into `.claude/settings.json`. -

reviewer.md

---
name: reviewer
description: Read-only code reviewer. Use it to review the current git diff before a merge, against the conventions in README.md.
tools: Read, Grep, Glob, Bash(git diff:*), Bash(git status:*)
---

You review a diff you did not write. You never edit files.

1. Run `git diff` and read every changed file in full.
2. Read README.md and check the diff against each of its conventions.
3. Look for bugs: wrong edge cases, missing validation, untested branches, behaviour that breaks existing routes.

Report each finding with the file, the line, what is wrong and why it matters. Say plainly when you find nothing real. No style nits.

reviewer.md on GitHub

: the read-only reviewer subagent, with tools Read, Grep, Glob, `Bash(git diff:*)` and `Bash(git status:*)`. Save it as `.claude/agents/reviewer.md`. -

checklist.md

# Clear and skill-doctor checklist

After every finished task
1. Run `/context` and read what the session carries.
2. Run `/clear` before the next unrelated task.
3. Run `/context` again; the conversation should be near empty while instructions, tools and skills stay.
4. In the middle of a task, do not clear: use `/compact` with a focus instead.

Once a month
1. Run `/skill-doctor` (needs a recent Claude Code release).
2. Skills loaded but never invoked: turn them off in `/skills` (space bar cycles).
3. Skills synced from claude.ai: switch them off on the web side, deleting them locally brings them back.
4. Plugin skills: disable the plugin in the Installed tab.
5. Keep what you use rarely but still need.

checklist.md on GitHub

: the `/context`, `/clear` and `/skill-doctor` routine on one page.

Do this Monday

  • Open your CLAUDE.md and mark every line that must hold every time, not most of the time.
  • Copy protect-migrations.sh and settings.json into .claude/, point the path at your protected file, then ask Claude to edit it and watch the block.
  • Before your next feature, type /goal with a condition that asks for the full test output to be printed.
  • When that task is done, run /context, then /clear, then /context again.
  • Run /skill-doctor and switch off what you never invoked. For synced skills, do it on the web side.
  • Copy reviewer.md into .claude/agents/ and ask for a review of the diff before your next merge.
  • Run the typecheck yourself after the review. Our reviewer claimed one it could not run.

Go further

We left these out.

  • Prompt audit. /doctor prompt-audit and the "louder rule, longer file" argument. Check the commands reference for the version floor s4.
  • Skills that never trigger. That is about descriptions, not about what loads. The primeline write-up is the place to start s12.
  • MCP pruning. One article measured 12 MCP servers at 5.8k tokens with tool search and 63k without, so it matters less than it used to s13.
  • Bigger CLAUDE.md files. One post cut 455,530 characters to 88,584 across nine files, and notes that @path imports load eagerly while plain links load lazily s11.
  • /batch. For large refactors, the commands reference lists 5 to 30 units, one worktree each s4. We did not run it.
  • Manual /compact thresholds. We found no source for a fixed percentage, so we give none.

FAQ

Does /goal replace running the tests myself? No. The judge only reads the conversation. It checks that the output was shown, not that it is true.

Does the hook stop Claude from trying the edit? No. It stops the edit once Claude tries. On our run, Claude went for the file twice without a CLAUDE.md rule, and both edits were blocked.

Should I delete my CLAUDE.md rules once I have a hook? No. With the capitals line, Claude refused the edit on its own, which saves a blocked call. Keep it for what Claude cannot work out from your code.

Why not use the reviewer on every edit? Each subagent starts with its own full context, so it costs real tokens. Use it once per feature, before the merge.

Will /skill-doctor run on my version? It is gated by a feature flag and a recent release in the commands reference s4. On 2.1.294 it ran on our machine, but update Claude Code first if yours does not.

Files