You’ve been vibing with Claude Code for a bit. Fixing bugs, generating tests, maybe letting it loose on a feature while you grab coffee. Life is good.

Then you stumble into a Discord thread where someone casually mentions “yeah just set up a hook to trigger your skill via the subagent” and suddenly you’re questioning everything. Skills? Hooks? Subagents? Isn’t CLAUDE.md basically the same thing? Why are there five different ways to tell Claude how to behave?

I spent way too long figuring this out. Let me save you the headache.

The Cheat Sheet

ThingWhat It Actually IsReliable?
CLAUDE.mdA markdown file Claude reads at startup. That’s it.✅ Always works
Slash CommandsSaved prompts you run with /whatever✅ Always works
HooksShell scripts that fire on events. Like GitHub Actions but for Claude.✅ Always works
SkillsFancy instruction folders Claude should load automatically🎲 Sometimes works
SubagentsMini-Claudes with their own brains🎲 Sometimes works

Notice a pattern? The top three are deterministic. You control when they run. The bottom two rely on Claude making good decisions. Claude is smart. Claude is also easily distracted by shiny objects.


CLAUDE.md: The README Claude Actually Reads

Every project has a README that nobody reads. CLAUDE.md is the opposite—Claude reads it every single time, whether you like it or not.

It’s just a markdown file. Put it in your project root, Claude loads it at session start. Revolutionary stuff.

~/.claude/CLAUDE.md           # Global (follows you everywhere)
~/project/CLAUDE.md           # Project root
~/project/.claude/CLAUDE.md   # Hidden in the config folder
~/project/src/CLAUDE.md       # Nested (for monorepo people)

Most specific wins. There’s also CLAUDE.local.md which gets gitignored—perfect for your “I know the team said tabs but I’m using spaces, fight me” preferences.

What to put in it

# Project Context

## Stack
- Next.js 14, TypeScript, Prisma, Tailwind

## Commands
- `npm run dev` - dev server
- `npm run test` - jest
- `npm run lint` - eslint

## Rules
- Functional components only
- Named exports, not default
- Tests next to source files

What NOT to put in it

Your life story. This thing loads every single message. Each token costs money and context window space. The Anthropic docs literally say “you’re writing for Claude, not onboarding a junior dev.”

Keep it tight. Use actual linters for style enforcement—Claude is way too expensive to be checking your semicolons.

Pro tip: Press # during a session to add stuff directly to CLAUDE.md. Like training a very expensive dog.


Slash Commands: Prompt Templates With a Cool Name

You know how you keep typing the same “review this PR for security issues, check for X, Y, Z, then…” prompt? Slash commands let you save that nonsense to a file and invoke it with /review.

.claude/commands/review.md      → /review
~/.claude/commands/yolo.md      → /yolo

Here’s a few I actually use:

The PR Review (.claude/commands/review.md):

---
description: Security-focused code review
allowed-tools: Read, Grep, Glob, Bash(git diff:*)
---

## Review Current Changes

Look at: !`git diff --name-only HEAD~1`

For each changed file:
1. Check for SQL injection, XSS, auth bypasses
2. Look for hardcoded secrets (yes, even "temporary" ones)
3. Verify input validation exists
4. Check error handling doesn't leak info
5. Flag any `// TODO: fix later` comments that are clearly lies

Be paranoid. Better to flag a false positive than miss something real.

The !git diff…`` syntax runs that command and injects the output into the prompt. Very handy.

The Context Dump (.claude/commands/wtf.md):

---
description: What even is this codebase
allowed-tools: Read, Grep, Glob
---

I just joined this project. Explain:
1. What does this thing do (read package.json, README, main entry points)
2. Folder structure - what lives where
3. Key dependencies and why they're here
4. How to run it locally
5. Any gotchas I should know about

Keep it brief. I'll ask follow-ups.

A really good example of how to write your research codebase command Humanlayer’s Research Codebase Command

For when you clone a repo and have no idea what you’re looking at.

Your team commits these to the repo, everyone gets the same workflows. No more “wait how do you do deploys again?” Slack messages.

The frontmatter is optional but useful—lock down which tools the command can use, specify a model, whatever. allowed-tools: Read, Grep means Claude can look but can’t touch. Like a museum.


Hooks: The Actually Reliable Automation

Hooks are where Claude Code gets genuinely powerful. They’re shell scripts that run at specific lifecycle events, and they always run. No “Claude forgot” energy here.

EventWhen
SessionStartClaude wakes up
UserPromptSubmitYou hit enter (before Claude sees it)
PreToolUseBefore Claude touches a tool
PostToolUseAfter a tool finishes
StopClaude stops talking
NotificationClaude wants your attention
…and moreCheck the docs

Configure in .claude/settings.json:

{
    "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bun run format || true"
          }
        ]
      }
    ]
  }
}

Or just run /hooks and use the interactive menu like a normal person.

The fun part: blocking stuff

Exit code 2 stops Claude in its tracks. Here are some I run:

Don’t touch my secrets:

#!/bin/bash
# block-sensitive-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

BLOCKED_PATTERNS=(".env" ".env.local" "credentials" "secrets" ".pem" ".key")

for pattern in "${BLOCKED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: Not touching $FILE_PATH" >&2
    exit 2
  fi
done
exit 0

No force pushing, ever:

#!/bin/bash
# no-force-push.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if echo "$COMMAND" | grep -qE 'git\s+push.*(-f|--force)'; then
  echo "Force push blocked. We don't do that here." >&2
  exit 2
fi
exit 0

Lock files are sacred:

#!/bin/bash
# protect-lockfiles.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

LOCKFILES=("package-lock.json" "yarn.lock" "pnpm-lock.yaml" "Gemfile.lock" "poetry.lock" "go.sum")

for lockfile in "${LOCKFILES[@]}"; do
  if [[ "$FILE_PATH" == *"$lockfile"* ]]; then
    echo "Lock files are managed by package managers, not AI. Run the install command instead." >&2
    exit 2
  fi
done
exit 0

Wire these up in your settings:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/block-sensitive-files.sh" },
          { "type": "command", "command": "~/.claude/hooks/protect-lockfiles.sh" }
        ]
      },
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/no-force-push.sh" }
        ]
      }
    ]
  }
}

I use hooks for auto-formatting, lint checks, desktop notifications when Claude needs input, and all the blocking above. It’s parental controls for AI, and honestly Claude needs them sometimes.

The key insight: Hooks are deterministic. Skills are probabilistic. If something absolutely must happen, hook it.


Skills: The Promise vs The Reality

Skills are Anthropic’s answer to “what if Claude could learn specialized workflows on-demand?” They’re folders with a SKILL.md file containing instructions that Claude loads when relevant.

~/.claude/skills/research/
├── SKILL.md
├── scripts/
│   └── verify-sources.py
└── templates/
    └── report.md

The SKILL.md:

---
name: research
description: Deep research with source verification. Use when asked to research or investigate topics.
---

# Research Skill

When researching:
1. Search authoritative sources
2. Cross-reference multiple sources
3. Cite everything
4. Distinguish facts from opinions

The theory is beautiful: Claude reads skill descriptions at startup (just the metadata, super efficient), then autonomously loads the full instructions when your request matches. Token-efficient progressive disclosure. Very elegant.

The practice: Claude sometimes just… doesn’t. You ask it to “research X” and it vibes off doing its own thing. Ask Claude to list its skills? Yep, research is right there. Ask Claude to actually use it? 🤷

I’ve been experimenting with hooks to force skill activation. I’ll write about it once I figure out what actually works consistently.

For now: skills are cool conceptually, but don’t bet your workflow on auto-activation. Invoke explicitly or have a backup plan.


Subagents: Delegation for Control Freaks

Subagents are separate Claude instances with their own context window and personality. Like hiring contractors: they do one thing, don’t know about your other problems, and peace out when done.

Claude Code ships with two built-in:

  • Explore — Fast read-only codebase search. Runs on Haiku. Cheap and quick.
  • General Purpose — Full Claude for complex multi-step stuff.

Rolling your own

Drop a file in .claude/agents/:

---
name: security-reviewer
description: Security-focused code review. Use PROACTIVELY for PRs.
tools: Read, Grep, Glob
model: sonnet
---

You're a paranoid security reviewer. Assume everything is compromised.

Check for:
1. Injection vulnerabilities
2. Missing input validation
3. Hardcoded secrets (yes, even "temporary" ones)
4. Auth bypasses
5. The obvious stuff everyone misses

Be thorough. Be annoying. That's the job.

Invoke with “Use the security-reviewer subagent on this diff” or let Claude auto-delegate based on the description (with the usual reliability caveats).

The gotcha: Subagents can’t spawn other subagents. No inception. They’re also stateless—every invocation starts fresh with zero memory of previous runs. Keep that in mind.

Good for: parallel work, context isolation, specialized reviewers that shouldn’t see your whole codebase.

Bad for: anything requiring coordination or memory across invocations.


The Bottom Line

The Claude Code ecosystem has layers. Some layers are rock solid (CLAUDE.md, commands, hooks). Some layers are reliable when you invoke them explicitly but flaky when you rely on auto-activation (skills, subagents).

Start with the deterministic stuff. Add complexity when you hit actual walls. And when skills don’t auto-activate for the fifth time in a row, remember: you can always invoke them explicitly, or there’s a hook for that.

Next time: making skills actually work.