Claude Code skills, memory files, and hooks turn Anthropic’s terminal agent from a generic assistant into a tool that knows your codebase and your workflow. If you spend any time in AI developer tools, mastering claude code skills is one of the highest-leverage things you can do.
This guide walks through the full customization stack: CLAUDE.md memory files, hooks defined in settings.json, custom slash commands, and the newer Skills system. Every example uses real, working configuration you can adapt today.
Before starting, make sure you have the CLI set up — if not, follow our guide to install Claude CLI first, then come back here.
What Are Claude Code Skills?
When people say “claude code skills,” they usually mean the whole customization layer rather than one feature. It covers everything that makes Claude behave consistently across sessions: memory, automation, commands, and skill packages.
These layers are complementary, not competing. Memory tells Claude what to know, hooks tell it what to run, commands give you shortcuts, and Skills bundle related capabilities into shareable units.
CLAUDE.md: Persistent Memory Files
CLAUDE.md is a plain Markdown file that Claude Code reads automatically at the start of every session. Anything in it becomes part of the model’s context, so it survives across conversations without you repeating yourself.
The CLAUDE.md Hierarchy
Claude Code loads memory from several locations, merging them with more specific files taking priority. The main levels are:
- User level (
~/.claude/CLAUDE.md) — applies to every project on your machine. Good for personal preferences like code style or commit message format. - Project level (
./CLAUDE.mdat the repo root) — shared with the team via version control. Good for build commands, architecture notes, and testing conventions. - Local project level (
./CLAUDE.local.md) — project-specific but personal; usually added to.gitignore.
You can also drop CLAUDE.md files into subdirectories. Claude picks them up when it works with files in those paths, which is handy for monorepos with per-package conventions.
Writing Effective Instructions
Memory files work best when they are short, concrete, and imperative. Claude treats them as standing instructions, so vague prose wastes context tokens and gets ignored.
A solid project-level CLAUDE.md looks like this:
# Project: payments-api
## Commands
- Install deps: `npm ci`
- Run tests: `npm test -- --run`
- Lint: `npm run lint`
## Conventions
- TypeScript strict mode; no `any` types
- Use Zod for all request validation
- Never modify files in /migrations without asking
## Architecture
- Express server in src/server.ts
- DB access only through src/db/repository.ts
Notice the pattern: commands Claude can run verbatim, rules it must follow, and a map of the codebase. Aim for roughly 50–150 lines; anything longer dilutes the important parts.
You can bootstrap a first draft by running the built-in init command:
claude
> /init
This scans your repo and generates a starting CLAUDE.md. Treat it as a draft — trim it down to what actually matters.
Hooks: Automating Around Tool Calls
Hooks are shell commands that Claude Code runs automatically at specific points in its lifecycle — before or after a tool call, when a session starts, and more. They live in your settings.json file.
Where Hooks Are Defined
Settings are read from ~/.claude/settings.json (user level) and .claude/settings.json (project level, commit it to share with the team). Hooks go under the hooks key, grouped by event.
The most useful events are PreToolUse (before a tool runs, can block it) and PostToolUse (after a tool runs, great for formatting and testing). A matcher field filters which tools trigger the hook.
Example: Auto-Format on Save
This hook runs Prettier on any file Claude just edited or wrote. It keeps every AI-generated change consistent with your code style without any manual step.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write "$(jq -r '.tool_input.file_path' /dev/stdin)""
}
]
}
]
}
}
Hook commands receive the tool call’s details as JSON on stdin, which is why the example pipes through jq to extract the file path. If you don’t have jq, a small Node or Python one-liner works too.
Example: Test Runner Hook
A PreToolUse hook can block dangerous operations. This one stops Claude from committing if the test suite fails:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "grep -q 'git commit' /dev/stdin && npm test --silent || true"
}
]
}
]
}
}
PreToolUse hooks that exit with a non-zero status block the tool call and feed the error back to Claude, so it can see the failing tests and fix them before retrying. This turns your quality gates into something the agent respects automatically.
Custom Slash Commands
Slash commands are reusable prompt templates stored as Markdown files. Drop a file into .claude/commands/ (project) or ~/.claude/commands/ (user) and it becomes available as /filename.
For example, a file at .claude/commands/review.md containing this:
Review the staged git changes. Check for:
1. Logic errors and edge cases
2. Missing error handling
3. Deviations from CLAUDE.md conventions
Report findings as a numbered list, most critical first.
Now typing /review in a session runs that whole prompt. Commands also support a $ARGUMENTS placeholder, so /review src/auth can inject a path into the template.
Slash commands pair naturally with MCP servers. Once you add servers, verify what’s connected with the claude mcp list command, then reference those tools inside your command templates.
The Skills System
Skills are the newest layer: self-contained packages of instructions, scripts, and resources that Claude loads on demand when a task matches them. Where CLAUDE.md is always in context, a Skill only activates when relevant, which keeps your context window lean.
A Skill is a directory containing a SKILL.md file with frontmatter (name, description) plus any supporting files. Place them in .claude/skills/ for a project or ~/.claude/skills/ for personal use.
---
name: db-migration
description: Generate safe, reversible database migrations for this schema
---
When asked to create a migration:
1. Read the current schema in db/schema.sql
2. Write both up and down migrations
3. Name files with a timestamp prefix
4. Never use DROP without a preceding backup step
The description field is critical — Claude uses it to decide when to invoke the Skill. Write it like a search query the user might implicitly make: “generate database migrations” works better than “database helper.”
Choosing the Right Layer
The four mechanisms overlap, so it helps to know when each one fits. This table summarizes the trade-offs:
| Mechanism | Best for | Scope | Always active? |
|---|---|---|---|
| CLAUDE.md | Standing rules, project context | User or project | Yes |
| Hooks | Automation around tool calls | User or project | Yes (on matching events) |
| Slash commands | Reusable prompt shortcuts | User or project | No (manual trigger) |
| Skills | On-demand packaged capabilities | User or project | No (loaded when relevant) |
A practical rule of thumb: if it should always apply, use CLAUDE.md; if it should happen automatically, use a hook; if you trigger it yourself, use a command; if it’s a big capability used occasionally, make it a Skill.
Conclusion
Claude Code skills are really a layered system: CLAUDE.md for persistent memory, hooks for automation, slash commands for shortcuts, and Skills for on-demand capabilities. Used together, they eliminate most of the repetitive prompting that makes AI coding assistants feel generic.
Start small: write a tight project CLAUDE.md, add one PostToolUse formatter hook, and create a single slash command for your most common task. You’ll feel the difference within a day, and you can grow the setup from there.