Module 10: Claude Code — Deep Mastery Guide

Goal: Achieve deep, practical mastery of Claude Code as a daily engineering tool. This module covers every major feature — CLI usage, memory, MCP, hooks, subagents, permissions, and productivity workflows — with enough depth to use Claude Code confidently in real projects and discuss it fluently in technical interviews.


Table of Contents

  1. What Is Claude Code
  2. Core CLI Usage
  3. Slash Commands Reference
  4. Keyboard Shortcuts
  5. Permission Modes
  6. Memory System Deep Dive
  7. MCP Integration
  8. Subagents and the Agent Tool
  9. Hooks — Power User Feature
  10. CLAUDE.md Best Practices
  11. Productivity Workflows
  12. Interview Flashcards

1. What Is Claude Code

Overview

Claude Code is Anthropic’s official CLI (command-line interface) and agentic development environment for Claude. It is not merely a terminal-based chat window — it is a full software engineering agent that can read files, write code, run shell commands, call external APIs via MCP, manage memory across sessions, delegate work to subagents, and be extended with hooks and skills.

Where It Runs

Claude Code is available in multiple surfaces:

SurfaceDescription
Terminal (CLI)claude command — the primary surface, runs in any shell
VS Code extensionSidebar panel + inline edits, same agent underneath
JetBrains pluginIntelliJ, PyCharm, WebStorm, etc.
Desktop appStandalone GUI wrapper around the same CLI engine
claude.ai/codeBrowser-based interface, same agent, no local shell access

The terminal is the most powerful surface. The extensions add convenience (inline diffs, file tree integration) but the underlying agent is identical.

Not Just a Chat Interface

What separates Claude Code from a simple chat product like ChatGPT or even Cursor:

  1. File access by default: Claude Code has direct read/write access to your filesystem. When you ask it to “fix the bug in auth.py”, it actually reads the file, edits it, and saves it — without you pasting code.

  2. Shell execution: Claude Code can run terminal commands — tests, builds, git operations, package installs — as part of fulfilling a request. It is not limited to text generation.

  3. Git integration: It understands your repository, can read diffs, write commit messages, review PRs, and understand history.

  4. MCP (Model Context Protocol): A standardized protocol for connecting external tools and data sources. Any MCP server (a database, a browser, a GitHub API client) can be wired into Claude Code and its tools become available to the agent.

  5. Memory system: Claude Code persists information across sessions using structured memory files. It can remember project conventions, your preferences, and feedback you’ve given — without needing re-explanation every session.

  6. Hooks: Shell scripts that fire on agent lifecycle events (before/after tool use, on stop). Used for logging, blocking dangerous actions, formatting code, sending notifications, syncing state.

  7. Skills: Reusable slash commands defined as prompt templates. Custom skills appear alongside built-in ones like /commit and /review-pr.

  8. Subagents: Claude Code can spin up child agents for parallel work, isolating them in git worktrees so they don’t interfere with each other.

How It Differs from Cursor

Cursor is an IDE fork (based on VS Code) with embedded AI assistance. Claude Code is a standalone agent that happens to have IDE extensions. Key differences:

  • Claude Code is agentic by default: it plans and executes multi-step tasks autonomously, asking for confirmation before destructive actions.
  • Claude Code has persistent memory across sessions; Cursor relies on the open project and codebase indexing.
  • Claude Code supports MCP out of the box, giving it access to arbitrary tools via a standard protocol.
  • Claude Code runs in the terminal and can be scripted, piped, and embedded in CI pipelines.
  • Claude Code supports hooks for lifecycle automation that Cursor has no equivalent to.

2. Core CLI Usage

Starting a Session

# Interactive session — opens a REPL-style conversation
claude
 
# One-shot mode — runs a single prompt and exits
claude "explain the architecture of this project"
 
# One-shot with a file piped in
cat stacktrace.txt | claude "what's causing this error?"
 
# One-shot reading from a file explicitly
claude "review this file for security issues" --file src/auth.py
 
# Start with a specific model
claude --model claude-opus-4-5
 
# Skip all confirmation prompts (CI/script use only)
claude --dangerously-skip-prompts "run the test suite and fix failures"

The Required Read-Before-Edit Flow

Claude Code enforces a mental model where it must read a file before editing it. This is not just a safety constraint — it is how the agent builds accurate context. If you ask Claude to modify a file it has not read in the current session, it will first invoke the Read tool, then plan its edit, then invoke the Edit tool.

This flow matters when:

  • Writing hooks (your hook will fire on Read AND Edit)
  • Debugging unexpected behavior (check if the read happened correctly)
  • Understanding token usage (every file read costs tokens)

Inline Shell Commands: The ! Prefix

Inside an interactive session, prefixing any line with ! runs it as a shell command and places the output into the conversation:

> !git log --oneline -10
(git log output appears in conversation)

> !pytest tests/ -v --tb=short
(test output appears in conversation)

> !cat pyproject.toml
(file contents appear in conversation)

This is useful when you want to give Claude raw output from a command without leaving the conversation. The output becomes part of Claude’s context and it can reason about it directly.

Key Built-in Slash Commands (Quick Reference)

CommandWhat it does
/helpLists all available slash commands
/clearResets the conversation (clears all context)
/compactCompresses conversation history to save tokens
/costShows the token count and estimated cost for the session
/doctorDiagnoses MCP connections, permissions, and configuration
/initGenerates a CLAUDE.md file for the current project
/commitStages changes and writes a smart git commit message
/review-prReviews a pull request (accepts PR number or URL)
/memoryOpens the memory management interface

Custom skills you add will appear here as /<skill-name>.

Useful CLI Flags

# Specify which tools are allowed (whitelist)
claude --allowedTools "Read,Edit,Bash"
 
# Run against a specific directory
claude --project /path/to/project "audit this codebase"
 
# Set verbosity
claude --verbose
 
# Output in JSON (for scripting)
claude --output-format json "list the files in src/"
 
# Specify a session ID to continue a previous session
claude --session <session-id>

3. Slash Commands Reference

Built-in Commands

/help
Prints all available slash commands with brief descriptions. Essential when exploring a new installation or checking what custom skills are available.

/clear
Completely resets the conversation context. All prior messages, tool calls, and results are gone. Use this when:

  • You have finished one task and are starting an unrelated one
  • The context has grown too large (approaching the token limit)
  • Claude seems confused by accumulated context and you want a fresh start

/compact
Compresses the conversation history in place. Claude summarizes previous turns into a compact representation, reducing token usage while keeping the essential thread. Unlike /clear, you do not lose context — it just becomes compressed. Use this in long sessions to extend the working window.

/cost
Displays the current session’s token consumption and estimated cost. Useful for budget-conscious usage and understanding which operations are expensive.

/doctor
Runs a diagnostic pass. Checks:

  • Whether all configured MCP servers are reachable and responding
  • Whether required permissions are configured
  • Whether the CLAUDE.md file is found and readable
  • Whether there are any obvious configuration errors in settings.json

Use /doctor whenever you add a new MCP server or change settings.json.

/init
Analyzes the current project and auto-generates a CLAUDE.md file with:

  • Detected tech stack
  • Common commands (test, build, lint)
  • Project structure overview
  • Suggested conventions

After running /init, review and refine the output. The auto-generated file is a starting point, not a final product.

/commit
Runs git diff --staged, analyzes the changes, and writes a commit message following conventional commits format. It then asks for confirmation before running git commit. This is a skill (defined in ~/.claude/skills/) not a built-in command — but it ships with Claude Code by default.

/review-pr
Takes a PR number or URL, fetches the diff, and produces a detailed code review covering:

  • Logic errors and bugs
  • Security issues
  • Performance concerns
  • Code style and readability
  • Missing tests

/memory
Opens the memory management interface. You can:

  • List all memory files
  • View a specific memory file
  • Edit a memory file directly
  • Delete a memory entry
  • Ask Claude to add a new memory

Custom Skills

Skills are prompt templates stored in ~/.claude/skills/ as Markdown files. Any .md file in that directory becomes a slash command with the filename as the command name.

~/.claude/skills/
├── commit.md          → /commit
├── review-pr.md       → /review-pr
├── weekly-report.md   → /weekly-report  (custom)
└── security-audit.md  → /security-audit (custom)

A skill file format:

---
description: Summarize git activity for the past week
---
 
Review git log from the past 7 days using:
  !git log --since="7 days ago" --oneline --author="$(git config user.name)"
 
Then summarize:
1. What was shipped (features, fixes)
2. Open PRs or branches pending review
3. Any notable refactors or architectural changes

4. Keyboard Shortcuts

Default Shortcuts (Mac)

ActionShortcut
Submit messageEnter
Insert newline (multi-line input)Shift + Enter
Cancel current running taskEscape
Clear conversationCtrl + L (or /clear)
Navigate history (previous message)Up Arrow
Navigate history (next message)Down Arrow
Scroll output upPage Up
Scroll output downPage Down

Windows/Linux Equivalents

All Mac shortcuts translate directly to Windows/Linux. There is no Cmd key usage — all shortcuts use Ctrl. The behavior is identical.

Customizing Shortcuts

Keyboard shortcuts are configured in ~/.claude/keybindings.json. This file uses the same format as VS Code keybindings.

[
  {
    "key": "ctrl+shift+enter",
    "command": "submit"
  },
  {
    "key": "ctrl+k",
    "command": "clear"
  },
  {
    "key": "ctrl+shift+c",
    "command": "compact"
  }
]

Available commands for keybindings:

  • submit — send the current message
  • clear — clear conversation
  • compact — compress conversation
  • cancel — cancel the current operation
  • newline — insert a newline without submitting

If ~/.claude/keybindings.json does not exist, Claude Code uses defaults. Create the file to override any subset of defaults — you do not need to redeclare everything.


5. Permission Modes

Why Permissions Exist

Claude Code can execute arbitrary shell commands, edit any file it has access to, and call external services via MCP. Unchecked, this is powerful but risky. The permission system is the guardrail that keeps the agent from doing unexpected or dangerous things.

The default philosophy: Claude asks before acting. Every tool call that modifies state (write a file, run a shell command, call an external API) prompts for confirmation unless you have explicitly allowed it.

Interactive Permissions

In an interactive session, Claude presents a permission prompt like:

Claude wants to run: rm -rf build/
Allow? (y/n/a = always allow this tool)

Options:

  • y — allow this one time
  • n — deny, Claude will try another approach or ask for guidance
  • a — always allow this tool for the rest of the session

The --dangerously-skip-prompts Flag

claude --dangerously-skip-prompts "run tests and commit"

This bypasses all permission prompts. Every tool call is auto-approved. Use this:

  • In CI/CD pipelines where there is no human present
  • In automated scripts where the scope of action is tightly controlled
  • When you are certain of what Claude will do and want zero friction

Never use this flag in interactive development sessions on production code. If Claude misunderstands the task, it will execute without asking.

--allowedTools Flag

Whitelist specific tools by name:

# Only allow Read and Edit — no shell execution
claude --allowedTools "Read,Edit" "refactor the auth module"
 
# Allow Read, Edit, and Bash but not MCP tools
claude --allowedTools "Read,Edit,Bash" "run the linter and fix issues"

Tool names correspond to the internal tool registry:

  • Read — file reading
  • Edit — file editing (single replacement)
  • Write — full file write
  • Bash — shell command execution
  • mcp__<server>__<tool> — any MCP tool

settings.json Permissions

For persistent permission configuration, use settings.json (located at ~/.claude/settings.json for global or <project>/.claude/settings.json for project-level):

{
  "permissions": {
    "allow": [
      "Bash(git *)",
      "Bash(pytest *)",
      "Bash(npm run *)",
      "Edit",
      "Read"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(sudo *)",
      "Bash(curl * | bash)"
    ]
  }
}

Permission rules use glob-style matching against the tool name and its arguments. Rules are evaluated in order; the first match wins.

Allow rule examples:

  • "Bash(git *)" — allow any git command
  • "Edit" — allow all file edits without prompting
  • "mcp__github__*" — allow all tools from the github MCP server

Deny rule examples:

  • "Bash(rm -rf *)" — always block recursive deletes
  • "Bash(sudo *)" — never allow sudo

Permission Hierarchy

Settings are merged in order of specificity:

  1. Built-in defaults (most permissive for reads, most restrictive for writes)
  2. Global ~/.claude/settings.json
  3. Project .claude/settings.json (overrides global)
  4. CLI flags (--allowedTools, --dangerously-skip-prompts) (highest precedence)

6. Memory System Deep Dive

What Is the Memory System

Claude Code’s memory system lets the agent persist information between sessions. Without it, every new claude session starts fresh — no knowledge of your preferences, past feedback, or project-specific conventions beyond what is in CLAUDE.md.

With memory, Claude can:

  • Remember that you prefer type hints in Python
  • Remember that the auth module uses a custom decorator pattern
  • Remember that you gave feedback about verbose commit messages
  • Remember team conventions that are not yet documented in code

Directory Structure

Memory files live at:

~/.claude/projects/<path-encoded-project>/memory/
├── MEMORY.md          ← index file, listed first
├── user-prefs.md
├── project-conventions.md
├── feedback.md
└── reference-patterns.md

The <path-encoded-project> is the project root path with / replaced by _ (e.g., /Users/alice/myapp becomes _Users_alice_myapp).

Memory Types

Memory files have frontmatter that declares their type. Claude uses the type to decide when and how to surface the memory.

---
name: Python Style Preferences
description: My personal preferences for Python code style
type: user
---
 
- Always use type hints on function signatures
- Prefer dataclasses over plain dicts for structured data
- Use f-strings, not .format() or %
TypePurposeWhen Claude reads it
userPersonal preferences that apply across all projectsEvery session
projectProject-specific conventions and architecture notesWhen working in this project
feedbackCorrections and feedback you’ve givenWhen similar situations arise
referenceReference material (schemas, API docs, patterns)When referenced by a task

The MEMORY.md Index

MEMORY.md is a table of contents for all memory files. Claude reads this first to decide which other files to load. Format:

# Memory Index
 
## Active Memory Files
 
- **user-prefs.md** — Personal Python/TypeScript preferences
- **project-conventions.md** — Conventions for the FastAPI backend
- **feedback.md** — Past corrections and style notes
- **auth-patterns.md** — Reference: how authentication is implemented

How Claude Decides to Read/Write Memory

Reading: At session start, Claude reads MEMORY.md and then loads relevant files based on the task. It does not load all memory files unconditionally — it selects based on type and relevance to avoid wasting tokens.

Writing: Claude writes to memory when:

  • You explicitly ask it to remember something
  • It detects a strong user preference from feedback
  • It completes a significant task and wants to capture learnings

Claude will always ask before creating or updating a memory file, unless you have configured auto-memory in settings.

How to Ask Claude to Remember Something

In conversation:

> remember that I use poetry for dependency management, not pip

> add a note to project memory that the database models are in src/models/ not models/

> update your memory: I prefer smaller, focused commits over large commits

Using /memory:

/memory add "Always run black and isort before committing Python files"
/memory list
/memory edit user-prefs.md

Manually Editing Memory Files

Memory files are plain Markdown — you can edit them with any text editor:

vim ~/.claude/projects/_Users_alice_myapp/memory/project-conventions.md

This is useful for bulk updates, importing conventions from documentation, or cleaning up outdated entries.

Memory vs CLAUDE.md vs Conversation History

ArtifactScopePersistenceWho manages it
CLAUDE.mdProject-level instructionsIn the repo (version controlled)You (developer)
Memory filesUser or project levelLocal, per-machineClaude + you
Conversation historySession-onlyEphemeralAutomatic

CLAUDE.md: Static instructions that every Claude instance working on this project should know. Checked into the repo so the whole team benefits. Does not change during a session.

Memory files: Dynamic notes that accumulate over time. Personal preferences, feedback, patterns discovered during sessions. Not version-controlled by default — they are your local working notes.

Conversation history: Active context for the current session only. Cleared with /clear. Can be compressed with /compact but is never persisted beyond the session.


7. MCP Integration

What Is MCP

The Model Context Protocol (MCP) is an open standard (published by Anthropic) for connecting AI agents to external tools and data sources. An MCP server is a process that exposes a set of typed tools — functions the agent can call. Claude Code acts as an MCP client: it discovers available tools from each connected server and can invoke them during task execution.

Think of MCP servers as plugins. A “GitHub MCP server” gives Claude tools like create_pr, list_issues, get_file_contents. A “Postgres MCP server” gives it query, list_tables. Claude can use these tools the same way it uses built-in tools like Read and Bash.

How Claude Code Loads MCP Servers

At startup, Claude Code reads settings.json and looks for the mcp.servers section. For each configured server, it:

  1. Starts the server process (using the command and args specified)
  2. Performs the MCP handshake to discover available tools
  3. Registers those tools in its tool registry with the naming prefix mcp__<server-name>__

If a server fails to start, the tools from that server are simply unavailable — Claude will not crash, but /doctor will report the failure.

settings.json MCP Configuration

{
  "mcp": {
    "servers": {
      "filesystem": {
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"],
        "env": {}
      },
      "github": {
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-github"],
        "env": {
          "GITHUB_TOKEN": "ghp_yourtoken"
        }
      },
      "postgres": {
        "command": "python",
        "args": ["/path/to/mcp-postgres/server.py"],
        "env": {
          "DATABASE_URL": "postgresql://localhost/mydb"
        }
      },
      "brave-search": {
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-brave-search"],
        "env": {
          "BRAVE_API_KEY": "your-api-key"
        }
      }
    }
  }
}

Key fields:

  • command: The executable to run (node, python, npx, etc.)
  • args: Arguments passed to the executable
  • env: Environment variables injected into the server process (use for secrets — do not hardcode in args)

MCP Tool Naming Convention

All MCP tools are named mcp__<server-name>__<tool-name>. Examples:

Server nameTool nameFull identifier
githubcreate_pull_requestmcp__github__create_pull_request
postgresquerymcp__postgres__query
filesystemread_filemcp__filesystem__read_file
brave-searchsearchmcp__brave-search__search

You use these names in --allowedTools and in settings.json permission rules:

"permissions": {
  "allow": [
    "mcp__github__*",
    "mcp__postgres__query"
  ],
  "deny": [
    "mcp__postgres__drop_table"
  ]
}

Debugging MCP

/doctor: Run this first. It reports which servers started successfully, which failed, and error messages for failures.

Check stderr: MCP server processes log errors to stderr. When debugging, run the server command manually:

python /path/to/mcp-postgres/server.py

Watch for import errors, missing environment variables, or connection failures.

Common issues:

  • Missing environment variable → server process exits immediately
  • Wrong Python/Node version → import error on startup
  • Port conflict → server cannot bind (for HTTP-based MCP servers)
  • Permission error → cannot read the files/databases the server needs

Verbose logging: Set CLAUDE_MCP_DEBUG=1 in your environment to get detailed MCP protocol logs.

Notable Community MCP Servers

ServerPackage / RepoWhat it provides
Filesystem@modelcontextprotocol/server-filesystemFile read/write/search in a sandboxed path
GitHub@modelcontextprotocol/server-githubPRs, issues, repos, file access via GitHub API
PostgreSQL@modelcontextprotocol/server-postgresSQL queries against a Postgres database
Brave Search@modelcontextprotocol/server-brave-searchWeb search via Brave API
Puppeteer@modelcontextprotocol/server-puppeteerHeadless browser, page scraping, screenshots
SQLite@modelcontextprotocol/server-sqliteQuery local SQLite databases
Slack@modelcontextprotocol/server-slackRead/send Slack messages
Memory (KG)@modelcontextprotocol/server-memoryPersistent knowledge graph (entity/relation store)

8. Subagents and the Agent Tool

What Is the Agent Tool

Claude Code has a built-in Agent tool that lets it delegate work to a child agent. The child agent is a full Claude Code instance that receives a task prompt and executes it independently, with its own tool calls and context window. When it finishes, it returns a result to the parent agent.

This is used for:

  • Parallelizing work (multiple agents working on different parts simultaneously)
  • Isolating risky operations (the child runs in a fresh git worktree)
  • Breaking up large tasks that would exhaust a single context window
  • Running a specialized agent (e.g., one focused only on tests while another handles implementation)

Available Agent Types

Agent typeBest for
general-purposeDefault — any task that does not require specialization
ExploreCodebase exploration, mapping architecture, finding patterns
PlanDecomposing large tasks into a structured execution plan
claude-code-guideMeta-tasks about Claude Code itself (explaining features, debugging config)

When to Use Agents vs Direct Tool Calls

Use direct tool calls (no subagent) when:

  • The task is small and fits comfortably in the current context
  • You need the result immediately to continue reasoning
  • The task requires tight back-and-forth with the user

Use a subagent when:

  • The task is large and self-contained (“refactor the entire auth module”)
  • You want parallel execution (“simultaneously write tests for module A and B”)
  • The task is risky and you want isolation (“delete all deprecated endpoints”)
  • The task would fill up the current context window

Foreground vs Background Agents

Foreground agents: Execute synchronously. The parent waits for the child to complete before continuing. Used when the result is needed to proceed.

Background agents: Execute asynchronously. The parent continues working and the child runs in parallel. Used for fire-and-forget tasks or parallel workloads. The parent can later query whether the background agent has finished.

Worktree Isolation

The most powerful isolation feature. When you run an agent with isolation: "worktree", Claude Code:

  1. Creates a new git worktree for the agent (a separate working directory pointing to a new branch)
  2. Runs the agent entirely within that worktree
  3. The agent’s file changes, shell commands, and git operations are isolated to that branch
  4. When the agent completes, the parent can merge, review, or discard the branch
# The parent agent instructs Claude to use worktree isolation:
"Run the refactor task on a new branch called feature/refactor-auth,
use worktree isolation so changes are isolated"

Why this matters:

  • The agent can make large, sweeping changes without affecting your current working branch
  • Multiple agents can work simultaneously without file conflicts (each in its own worktree)
  • If the agent makes a mistake, you discard the branch — no cleanup needed

Crafting Good Agent Prompts

A subagent receives a single prompt and no ability to ask follow-up questions mid-execution. Every piece of context must be in the initial prompt.

Bad agent prompt:

"Fix the authentication bug"

Good agent prompt:

"Fix the authentication bug in src/auth/jwt_handler.py.

Context:
- The bug is in the `verify_token` function — it does not check token expiry
- Use the `datetime.utcnow()` approach consistent with the rest of the codebase
- The test file is tests/test_jwt_handler.py — add a test for the expiry case
- Run pytest tests/test_jwt_handler.py after fixing to verify

Constraints:
- Do not modify any other files
- The fix should be backwards-compatible (no changes to the function signature)

Return:
- A summary of what you changed and why
- The result of the test run
"

Principles for good agent prompts:

  1. Be specific about the target: files, functions, line ranges
  2. Include relevant context: related patterns, conventions, constraints
  3. Specify what tools to use: “run pytest”, “check with mypy”
  4. Define what “done” looks like: tests pass, no type errors, etc.
  5. Request a structured return value: summary + evidence

Receiving and Verifying Agent Results

After a subagent completes, verify before integrating:

  • Read the diff produced by the agent
  • Run the test suite
  • Check for unexpected file changes
  • Review the agent’s summary for omissions or misunderstandings

For worktree agents, review the branch before merging:

git diff main..feature/agent-branch
git log main..feature/agent-branch --oneline

9. Hooks — Power User Feature

What Are Hooks

Hooks are shell scripts that fire at specific points in the Claude Code agent lifecycle. They give you programmatic control over the agent’s behavior: you can log what it does, block dangerous actions, trigger side effects, and integrate Claude Code into external systems.

Hooks are configured in settings.json under the hooks key and run as shell scripts with JSON data piped to stdin.

The Four Hook Events

EventWhen it firesCan block?
PreToolUseBefore Claude executes any tool callYes — exit non-zero to block
PostToolUseAfter a tool call completesNo — action already happened
NotificationWhen Claude sends a notificationNo
StopWhen the agent finishes a taskNo

Hook Input: JSON on stdin

Every hook receives a JSON payload on stdin describing what happened. The structure varies by event:

PreToolUse / PostToolUse:

{
  "event": "PreToolUse",
  "tool": "Bash",
  "input": {
    "command": "rm -rf build/"
  },
  "session_id": "abc123"
}

Stop:

{
  "event": "Stop",
  "session_id": "abc123",
  "summary": "Refactored auth module and added tests",
  "duration_seconds": 142
}

Notification:

{
  "event": "Notification",
  "message": "Claude needs clarification about X",
  "session_id": "abc123"
}

Hook Output: Exit Code Controls Behavior

Exit codeMeaning
0Proceed normally
Non-zeroBlock the action; Claude receives the script’s stderr as an error message

For PreToolUse hooks, exiting non-zero prevents the tool call and passes the stderr output back to Claude as a message explaining why it was blocked. Claude can then try a different approach or ask the user.

Configuring Hooks in settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/block-dangerous-commands.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/log-edits.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/notify-complete.sh"
          }
        ]
      }
    ]
  }
}

The matcher field selects which tool triggers the hook. It can be:

  • An exact tool name: "Bash", "Edit", "Write"
  • An MCP tool: "mcp__github__create_pull_request"
  • Omitted (fires for all tools of that event type)

Hook Examples

1. Log all file edits to CSV:

#!/bin/bash
# PostToolUse hook — fires after every Edit/Write
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
FILE=$(echo "$INPUT" | jq -r '.input.file_path // "unknown"')
echo "$TIMESTAMP,$TOOL,$FILE" >> ~/.claude/edit_log.csv
exit 0

2. Block rm commands:

#!/bin/bash
# PreToolUse hook — fires before Bash commands
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.input.command // ""')
if echo "$COMMAND" | grep -qE '^\s*rm\s'; then
  echo "ERROR: rm commands are blocked. Use trash or git rm instead." >&2
  exit 1
fi
exit 0

3. Auto-run black after Python edits:

#!/bin/bash
# PostToolUse hook — fires after Edit on .py files
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.input.file_path // ""')
if [[ "$FILE" == *.py ]]; then
  black "$FILE" --quiet 2>/dev/null || true
fi
exit 0

4. Send macOS notification on Stop:

#!/bin/bash
# Stop hook — fires when agent completes
INPUT=$(cat)
SUMMARY=$(echo "$INPUT" | jq -r '.summary // "Task complete"')
osascript -e "display notification \"$SUMMARY\" with title \"Claude Code\" sound name \"Glass\""
exit 0

5. Sync memory to remote server on Stop:

#!/bin/bash
# Stop hook — sync memory files to a remote backup
rsync -az ~/.claude/projects/ user@backup-server:~/claude-memory/ 2>/dev/null || true
exit 0

Hook Best Practices

  1. Always exit 0 for PostToolUse hooks unless you have a very specific reason to block post-hoc (the action is already done — blocking changes nothing except confusing Claude).

  2. Keep hooks fast. They run synchronously in the agent’s execution path. A slow hook (network call, heavy computation) blocks the entire session. Use background jobs for expensive operations.

  3. Handle missing jq gracefully. Not all systems have jq. Add a fallback:

    if ! command -v jq &>/dev/null; then exit 0; fi
  4. Write stderr messages clearly for PreToolUse blocking hooks. Claude reads your error message and uses it to decide what to do next. “rm commands are blocked. Use trash instead.” is actionable; “Error” is not.

  5. Test hooks independently:

    echo '{"tool":"Bash","input":{"command":"rm -rf ."}}' | bash ~/.claude/hooks/block-dangerous.sh
    echo "Exit: $?"

10. CLAUDE.md Best Practices

What Is CLAUDE.md

CLAUDE.md is a Markdown file at the root of your project that Claude Code reads at the start of every session. It is the canonical place to describe your project in terms that help Claude be immediately useful without requiring re-explanation.

Think of it as an onboarding document written for Claude (and new human contributors). It captures what is not obvious from reading the code.

What Belongs in CLAUDE.md

Always include:

  • Tech stack overview (language version, framework, key libraries)
  • How to run the project locally (setup commands)
  • How to run tests, linting, type checking
  • Build and deployment commands
  • Project structure — which directories contain what
  • Conventions that are not enforced by tooling (naming, commit style, PR process)
  • Known gotchas and footguns specific to this codebase

Good candidates:

  • Architecture decisions that affect how code is written
  • External dependencies and how to configure them (without secrets)
  • Code patterns that are idiomatic for this project

Do not include:

  • Secrets, tokens, API keys — never
  • Information Claude can discover by reading the code
  • Personal preferences (put those in memory files)
  • Team-specific conventions that are not relevant to coding (meeting times, Slack channels)

Project-Level vs Directory-Level CLAUDE.md

You can have CLAUDE.md files at multiple levels:

project/
├── CLAUDE.md           ← project-wide conventions
├── src/
│   ├── CLAUDE.md       ← conventions specific to src/
│   └── auth/
│       └── CLAUDE.md   ← auth module specifics
└── tests/
    └── CLAUDE.md       ← testing conventions

Claude reads the CLAUDE.md nearest to the files it is working with. Directory-level files let you document subsystems without cluttering the root file.

Using /init

# In the project root
/init

Claude will:

  1. List files in the project root
  2. Read package.json, pyproject.toml, go.mod, etc. to detect the stack
  3. Look for existing test/lint/build scripts
  4. Generate a draft CLAUDE.md

Review and refine the output. Common things to add after /init:

  • Conventions that are not detectable from config files
  • The “why” behind non-obvious architecture choices
  • Custom tooling or scripts the team uses

What Future Claude Instances Need to Know

Ask yourself: if a Claude instance opens this project with no prior conversation, what does it need to know to:

  • Run the tests without breaking anything?
  • Write a new endpoint in the existing style?
  • Understand what “auth” means in this codebase?
  • Know which directories to look in for which concerns?

That is what belongs in CLAUDE.md. Code style, type information, and architecture can mostly be inferred from reading the code. Workflow, conventions, and “the way we do things here” cannot.

Example CLAUDE.md Structure

# Project: AuthService
 
## Stack
- Python 3.12, FastAPI 0.111, SQLAlchemy 2.0
- PostgreSQL 15 (dev: Docker, prod: RDS)
- pytest + httpx for testing
 
## Setup
```bash
poetry install
cp .env.example .env
docker-compose up -d  # starts postgres
alembic upgrade head

Common Commands

make test          # run full test suite
make lint          # black + isort + mypy
make migrate       # run pending migrations
uvicorn main:app --reload  # dev server

Project Structure

  • src/routers/ — FastAPI route handlers
  • src/services/ — business logic (no DB access)
  • src/repositories/ — all database access
  • src/models/ — SQLAlchemy models
  • tests/ — mirrors src/ structure

Conventions

  • Services never import from repositories directly — use dependency injection
  • All endpoints return Pydantic models, not raw dicts
  • DB sessions are managed via get_db dependency
  • Use async/await throughout — no sync DB calls

Known Issues

  • The user_service.py has a legacy sync method get_by_email_sync — do not call it from new code

---

## 11. Productivity Workflows

### Morning Standup

Get a quick summary of what you did yesterday and what needs attention:

```bash
claude "summarize my git activity from the last 24 hours and list any open PRs that need attention"

Claude will:

  • Run git log --since="24 hours ago" to gather your commits
  • Use the GitHub MCP server (if configured) to list open PRs
  • Produce a clean summary suitable for a standup

Code Review

Review your own changes before pushing:

# Review the latest commit
claude "review the diff in the last commit for bugs, logic errors, and code quality issues"
 
# Review staged changes
claude "review my staged changes for correctness before I commit"
 
# Review a PR (with GitHub MCP)
claude /review-pr 342

Debugging

Pass a stacktrace directly:

# Pipe the error to Claude
pytest tests/ 2>&1 | claude "here's a test failure — what's wrong and how do I fix it?"
 
# Or paste in the interactive session
claude
> I'm getting this error when calling /api/auth/login:
> [paste stacktrace]
> The auth middleware is in src/auth/middleware.py

Claude will read the referenced files, trace through the logic, and suggest a fix with explanation.

Writing Documentation

# Docstrings for all public functions in a module
claude "write Google-style docstrings for all public functions in src/services/user_service.py"
 
# README for a new module
claude "write a README.md for the src/auth/ directory explaining what it does and how to use it"
 
# OpenAPI descriptions
claude "add description fields to all FastAPI endpoint decorators in src/routers/"

Refactoring

# Specific pattern refactor
claude "refactor user_service.py to use the repository pattern — move all DB access to a UserRepository class"
 
# Extract a class
claude "the Payment class in payment.py is too large — extract billing logic into a BillingService"
 
# Modernize
claude "update auth.py to use Python 3.12 features where appropriate — type aliases, match statements, etc."

Running Tests and Fixing Failures

# Interactive: let Claude run tests and fix failures
claude "run the test suite with pytest and fix any failing tests"
 
# Non-interactive CI mode
claude --dangerously-skip-prompts "run pytest, fix any failures, then commit the fixes"

CI Pipeline Integration

# In a GitHub Actions workflow
- name: Claude code review
  run: |
    claude --dangerously-skip-prompts \
      --allowedTools "Read,Bash" \
      "review the diff in this PR for obvious bugs and security issues, output findings as JSON"

Generating Tests

# Generate tests for a module
claude "write comprehensive pytest tests for src/services/payment_service.py — cover happy paths, edge cases, and error cases"
 
# TDD: write tests first
claude "write failing pytest tests for a new UserExporter class that exports users to CSV — I'll implement it next"

Architecture Exploration

When joining a new codebase:

claude "explore this codebase and give me:
1. A high-level architecture overview
2. The main data flows (request → handler → service → DB → response)
3. Any patterns or conventions I should follow
4. The areas of the code most likely to be relevant for adding a new feature"

This is a great use case for the Explore agent type.

Dependency Auditing

claude "check our Python dependencies for known security vulnerabilities and outdated versions, summarize what needs updating"

12. Interview Flashcards

Q1: What is Claude Code and how does it differ from a chat interface?

A: Claude Code is Anthropic’s agentic development CLI. Unlike a chat interface where you paste code and receive suggestions, Claude Code has direct access to your filesystem, shell, and git repository. It reads files before editing them, executes tests, runs build commands, and manages multi-step tasks autonomously. It persists context across sessions via a structured memory system, supports external tool integrations via MCP, and can delegate work to subagents running in isolated git worktrees. The key difference is: a chat interface produces text; Claude Code takes actions.


Q2: What are MCP servers and how do you connect one to Claude Code?

A: MCP (Model Context Protocol) servers are processes that expose typed tools to an AI agent via a standard protocol. A server might expose database query tools, GitHub API wrappers, or a web browser. Claude Code acts as the MCP client — at startup it reads the mcp.servers section of settings.json, starts each configured server process, performs the protocol handshake to discover available tools, and registers them with the prefix mcp__<server-name>__. To connect a server, you add it to settings.json:

"mcp": { "servers": { "myserver": { "command": "python", "args": ["server.py"], "env": {} } } }

Then run /doctor to verify the connection.


Q3: What is the Claude Code memory system and what types of memory does it support?

A: The memory system lets Claude persist information between sessions in structured Markdown files stored at ~/.claude/projects/<project>/memory/. There are four memory types: user (personal preferences, applies across all projects), project (project-specific conventions and architecture notes), feedback (corrections you’ve given Claude), and reference (reference material like schemas and patterns). The MEMORY.md file is the index — Claude reads it first to decide which files to load. Memory files are separate from CLAUDE.md (which is version-controlled, project-level, static) and conversation history (which is ephemeral).


Q4: How do hooks work in Claude Code?

A: Hooks are shell scripts configured in settings.json that fire at lifecycle events: PreToolUse (before any tool call), PostToolUse (after a tool call), Notification, and Stop. Claude pipes a JSON payload to the hook’s stdin describing the event and tool input. For PreToolUse hooks, exiting non-zero blocks the action and passes stderr back to Claude as an error message. For all other events, the exit code is ignored. Hooks enable logging tool usage, blocking dangerous commands, auto-formatting code after edits, sending notifications, and syncing state to external systems. They run synchronously, so keeping them fast is important.


Q5: What is worktree isolation in the Agent tool?

A: When you launch a subagent with isolation: "worktree", Claude Code creates a new git worktree — a separate working directory linked to a new branch. The subagent runs entirely within that worktree: its file edits, shell commands, and git operations are isolated to that branch and do not affect your current working directory. This enables parallel agents (each in their own worktree with no file conflicts), safe execution of large refactors (discard the branch if something goes wrong), and clean review of agent work (review the branch diff before merging).


Q6: How do you customize permissions in Claude Code?

A: Permissions are controlled at three levels. The settings.json file (global at ~/.claude/settings.json or project-level at .claude/settings.json) supports allow and deny arrays under permissions, using glob-style patterns like "Bash(git *)" or "mcp__github__*". The --allowedTools CLI flag whitelists specific tools for a single invocation. The --dangerously-skip-prompts flag auto-approves all tool calls (for CI use only). In interactive sessions, you can approve individual calls with y, deny with n, or always-allow with a. Settings merge in order: defaults → global → project → CLI flags.


Q7: What is a CLAUDE.md file and what should go in it?

A: CLAUDE.md is a Markdown file at the project root (and optionally in subdirectories) that Claude reads at the start of every session. It contains project-specific context that Claude needs to work effectively: tech stack, setup and run commands, project structure, coding conventions, and known gotchas. It is checked into version control so the whole team benefits. What goes in CLAUDE.md (not discoverable from code): workflow, conventions, architecture decisions, “the right way” to do things in this project. What does not belong: secrets, personal preferences (use memory files), information inferrable from reading the code.


Q8: How would you set up Claude Code for a new team joining a large Python monorepo?

A: First, run /init at the repo root to auto-generate a CLAUDE.md, then refine it: add the monorepo structure, which directories contain which services, the correct commands for each service, and cross-service conventions. Next, configure settings.json with appropriate permissions — deny dangerous commands like rm -rf and sudo, allow the team’s standard commands (pytest *, git *, make *). Add MCP servers for shared tooling (GitHub for PRs, the internal database for queries). Create shared hooks for logging and enforcement (e.g., block any command touching production environment variables). Create team-standard skills like /deploy-service, /run-service-tests. Commit .claude/settings.json to the repo so all team members get the same base configuration. Individually, each developer manages their ~/.claude/settings.json for personal preferences and memory/ for their working notes.


Quick Reference Card

# Start Claude Code
claude                          # interactive
claude "do X"                   # one-shot

# Common session commands
/help                           # all commands
/clear                          # reset context
/compact                        # compress history
/cost                           # session cost
/doctor                         # diagnose config
/init                           # generate CLAUDE.md
/commit                         # smart git commit
/review-pr <number>             # PR review
/memory                         # manage memory

# Shell command in session
!git log --oneline -10          # runs in shell, output in context

# Permission flags
--dangerously-skip-prompts      # CI: auto-approve all
--allowedTools "Read,Edit"      # whitelist tools

# Memory location
~/.claude/projects/<encoded-path>/memory/

# Settings location
~/.claude/settings.json         # global
.claude/settings.json           # project

# Hooks location
~/.claude/hooks/                # convention (any path works)

# Skills location
~/.claude/skills/               # *.md files → /commands