AnyLearn
All lessons
Programmingintermediate

Claude Code: Niche Features You're Probably Not Using

A tour of the underused Claude Code power features beyond basic prompting: CLAUDE.md memory, plan mode, rewind, custom slash commands, subagents, hooks, MCP servers, statusline, and headless mode.

Not signed in — your progress and quiz score won't be saved.
Lesson progress1 / 11

Beyond the prompt box

If your Claude Code usage is mostly typing prompts and pressing Enter, you're using maybe 20% of the tool. The CLI ships with a layered configuration system, a hook engine, a permission state machine, custom commands, subagents, MCP integrations, a customizable statusline, and a headless mode designed for CI. None of that shows up in the welcome screen.

This lesson walks through the most impactful niche features, roughly ordered from "start using today" to "set up once, win forever." Every snippet is real syntax against Claude Code as it ships in 2026. By the end you should have a concrete list of things to add to your .claude/ folder tonight.

Ground rules: configs live in ~/.claude/ (personal, every project) and .claude/ (per-repo, commit it). The CLI merges them. Anything in this lesson works in the terminal; most of it also works inside the VS Code and JetBrains extensions, which embed the same engine.

Full lesson text

All 11 steps on one page — for reading, reference, and search.

Show

1. Beyond the prompt box

If your Claude Code usage is mostly typing prompts and pressing Enter, you're using maybe 20% of the tool. The CLI ships with a layered configuration system, a hook engine, a permission state machine, custom commands, subagents, MCP integrations, a customizable statusline, and a headless mode designed for CI. None of that shows up in the welcome screen.

This lesson walks through the most impactful niche features, roughly ordered from "start using today" to "set up once, win forever." Every snippet is real syntax against Claude Code as it ships in 2026. By the end you should have a concrete list of things to add to your .claude/ folder tonight.

Ground rules: configs live in ~/.claude/ (personal, every project) and .claude/ (per-repo, commit it). The CLI merges them. Anything in this lesson works in the terminal; most of it also works inside the VS Code and JetBrains extensions, which embed the same engine.

2. Plan mode and rewind: two keystrokes that change everything

Two keyboard shortcuts most people miss:

  • Shift+Tab cycles the permission mode. The status bar updates: default -> acceptEdits (no more "may I edit?" prompts) -> plan (read-only; Claude drafts a plan you approve before any tool runs). Tap until you get the mode you want.
  • Esc Esc opens the rewind menu. You can rewind the conversation only, the code only, or both. This is a checkpoint system, not a chat undo: it snapshots your working tree, so you can let Claude go wild, hate the result, and pop back to a known-good state in one keystroke.

Pair them: enter plan mode for ambiguous tasks ("refactor the auth layer"), let Claude propose, accept the plan, then if the execution drifts you Esc Esc and restart from the plan. This is far cheaper than reverting via git and asking Claude to try again from a stale context.

3. CLAUDE.md: the memory layer you keep forgetting to write

When Claude Code starts in a directory, it walks up the tree collecting every CLAUDE.md file and concatenates them into the system prompt. You get four useful tiers:

  • ~/.claude/CLAUDE.md -- your global preferences ("prefer pnpm over npm", "never use emojis").
  • Repo-root CLAUDE.md -- project conventions, commit message style, test command.
  • subdir/CLAUDE.md -- module-specific rules ("this folder is Python 3.12 only").
  • Imports via @path/to/file.md inside any of the above, up to 5 levels deep.

A minimal repo CLAUDE.md:

# Project: anylearn-v0
- Package manager: pnpm. Never run npm install.
- Tests: `pnpm test` runs Vitest. Mark slow tests with `.slow`.
- Style: see @docs/style.md
- Never edit `web/lib/generated/*` -- it's codegen output.

The @docs/style.md line pulls in that file's full contents, so you can share a canonical doc with other tools (Cursor, AGENTS.md) without duplication.

4. Custom slash commands: stop retyping the same prompt

Drop a markdown file at .claude/commands/<name>.md and it becomes /<name> in the picker. Personal commands live at ~/.claude/commands/. The body is the prompt; YAML frontmatter configures behavior.

A real example -- .claude/commands/review-pr.md:

---
description: Review the current branch diff against main
argument-hint: "[optional focus area]"
allowed-tools: ["Bash(git:*)", "Bash(gh:*)", "Read", "Grep"]
model: claude-sonnet-4-6
---

Review the diff between HEAD and origin/main.
Focus: $ARGUMENTS

Flag:
- Missing test coverage on changed functions
- Public API changes without a CHANGELOG entry
- Anything that touches `web/lib/auth/`

Use `git diff origin/main...HEAD` and `gh pr view` if a PR exists.

Now /review-pr auth flow runs a scoped review with a tight tool allowlist. The $ARGUMENTS placeholder gets whatever the user typed after the command. Positional $1, $2 also work. Note that .claude/skills/<name>/SKILL.md is the newer cousin -- same idea, but a folder so you can ship helper scripts alongside the prompt.

5. Subagents: parallel context windows

When the main agent invokes the Task tool it spawns a subagent: a fresh context window with its own system prompt and tool set. You can ship custom subagents as markdown files at .claude/agents/<name>.md (or ~/.claude/agents/ for personal ones).

Example -- .claude/agents/test-runner.md:

---
name: test-runner
description: Runs the test suite, parses failures, returns a structured report
tools: ["Bash", "Read", "Grep"]
model: claude-haiku-4-6
---

You are a focused test-runner. Run `pnpm test`.
If any test fails, isolate the failing file, read it, and
return JSON: { "passed": int, "failed": int, "failures": [{file, name, message}] }.
Do NOT attempt to fix anything. Just report.

Why bother? Three wins: (1) the subagent's noisy bash output never pollutes your main thread; (2) you can pin a cheaper/faster model to grunt work; (3) the lead agent can fan out -- run test-runner, lint-runner, and typecheck-runner in parallel, then aggregate. List active definitions with /agents.

6. Hooks: deterministic guardrails around the LLM

Hooks are shell commands the CLI runs at fixed lifecycle points -- not the model deciding to run them. Twelve events exist; the four you'll actually use:

  • PreToolUse -- before any tool call. Exit code 2 blocks the call. Use this for security (block edits to .env, deny rm -rf /).
  • PostToolUse -- after a tool finishes. Can't undo, but great for autoformat-on-save.
  • UserPromptSubmit -- when you press Enter. Inject extra context (current branch, ticket number).
  • Stop -- when Claude finishes responding. Trigger a desktop notification.

Matchers use a regex against the tool name (Edit, Write, Bash, mcp__github__.*). The hook receives the tool input as JSON on stdin, so you can jq it. PreToolUse stdout becomes additional context for the model; non-zero exit codes signal the result.

Hooks turn "the LLM usually does the right thing" into "the LLM cannot do the wrong thing." That's the difference between a demo and a production workflow.

7. Where each config layer plugs in

Personal config layers under project config; hooks and permission modes gate every tool call before it runs.

flowchart TD
  A["You type a prompt"] --> B["UserPromptSubmit hook"]
  B --> C["CLAUDE.md memory (global + project)"]
  C --> D["Model plans a tool call"]
  D --> E["Permission mode (default / acceptEdits / plan / bypassPermissions)"]
  E --> F["PreToolUse hook (can block)"]
  F --> G["Tool runs (Edit / Bash / MCP / Task)"]
  G --> H["PostToolUse hook"]
  H --> I["Statusline command (re-renders)"]
  I --> J["Response to you"]
  J --> K["Stop hook"]

8. A complete hooks + statusLine settings.json

Here is a real .claude/settings.json that blocks .env edits, formats TypeScript after every write, and shows the current git branch and token usage in the statusline:

{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": ["Bash(pnpm:*)", "Bash(git:*)"],
    "deny": ["Bash(rm -rf:*)"]
  },
  "hooks": {
    "PreToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "jq -e '.tool_input.file_path | test(\"\\\\.env\") | not' >/dev/null || (echo 'blocked: .env is off-limits' >&2; exit 2)"
      }]
    }],
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path' | xargs -I{} pnpm prettier --write {} 2>/dev/null || true"
      }]
    }]
  },
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh"
  }
}

The statusline script receives session JSON (model, cwd, cost, context %) on stdin and prints one line. Run /statusline and Claude will scaffold the script for you.

9. MCP servers: real integrations, not browser tricks

Model Context Protocol is how Claude Code talks to external systems -- GitHub, your filesystem, Slack, Sentry, Postgres, Linear, Playwright, you name it. Each server is a process that advertises tools; the CLI auto-discovers them.

Add one with the CLI -- scope it to the project so teammates inherit it:

claude mcp add --scope project --transport stdio github -- npx -y @modelcontextprotocol/server-github
claude mcp add --scope project filesystem -- npx -y @modelcontextprotocol/server-filesystem /Users/me/work

This writes .mcp.json at the repo root -- commit it. Tool names appear in Claude's tool list as mcp__github__create_issue, mcp__filesystem__read_file, etc., and you can match them in hooks and allowedTools like any built-in. Set --scope user for personal servers with secrets you don't want to share.

The killer combo is filesystem + github + your team's domain server (Sentry, PagerDuty). Now "triage yesterday's errors and open issues for the top three" is one prompt, not a tab-switching marathon.

10. Headless mode and the bang prefix

Two escapes from the interactive loop:

! prefix -- inside the prompt, type !git status and Claude Code runs it in your shell, immediately. No model call, no token cost. Output is captured into the conversation so Claude can act on it. Great for cheap "show me what's there before you do anything" steps. Ctrl+B while a long command is running ships it to the background.

Headless mode (-p / --print) -- run Claude Code as a Unix tool:

claude -p "Summarize the diff and propose a CHANGELOG entry" \
  --allowedTools "Read,Bash(git:*)" \
  --output-format json < /dev/null

It prints the answer to stdout and exits. Pipe it into anything. Resume with --continue (last session) or --resume <id>. This is how you wire Claude into pre-commit hooks, GitHub Actions, cron jobs, or your own shell scripts. Combine with --allowedTools and a tight CLAUDE.md for fully unattended runs.

Lesser-known slash commands worth knowing: /compact (summarize old turns to free context), /cost (session spend), /model (swap mid-session), /status, /hooks, /agents, /mcp, /resume.

11. Putting it together: a 30-minute setup

Here's the recommended once-and-done config to graduate from casual to power user:

  1. Write ~/.claude/CLAUDE.md -- your global preferences (package manager, code style tics, things you never want Claude to do).
  2. Set a default permission mode in ~/.claude/settings.json: "permissions": { "defaultMode": "acceptEdits" }. Stops the constant "may I edit?" prompts.
  3. Add 2-3 slash commands for tasks you run weekly: /review-pr, /write-tests, /explain-this-error. Keep prompts in .md so they're diffable.
  4. One hook: PreToolUse on Edit|Write that blocks .env and any path matching your secrets pattern. Cheap, huge safety win.
  5. One MCP server: whichever external system you context-switch into most -- GitHub for most devs, Linear or Sentry for others.
  6. A statusline that shows model + context % + git branch. You will catch context exhaustion before it bites you.
  7. Bookmark headless mode for your next CI task.

Don't try to build the perfect agent zoo on day one. Add one piece at a time, notice when it earns its keep, then add the next.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. You press Shift+Tab three times from the default permission mode. What mode are you in, and what can Claude do?
    • acceptEdits -- Claude can edit files without asking but still prompts for other commands
    • plan -- Claude can only use read-only tools and drafts a plan you approve before executing anything
    • bypassPermissions -- Claude can run anything with no checks
    • default -- Claude asks before every edit and every command
  2. A repo-level `CLAUDE.md` contains the line `@docs/style.md`. What happens when Claude Code starts?
    • It opens `docs/style.md` in your editor as a side-by-side reference
    • It treats `@docs/style.md` as a literal string with no special meaning
    • It inlines the contents of `docs/style.md` into the active instruction set, recursively resolving any further @-imports up to depth 5
    • It uploads `docs/style.md` to a shared Anthropic memory store for use across machines
  3. Which Claude Code lifecycle hook can actually *block* a tool call from running by exiting non-zero?
    • PostToolUse
    • Stop
    • UserPromptSubmit
    • PreToolUse (exit code 2 blocks)
  4. You add an MCP server at project scope with `claude mcp add --scope project github -- npx -y @modelcontextprotocol/server-github`. Where is the configuration stored and what shows up?
    • In `~/.claude/mcp.json`; tools appear as `github_*` and only you see them
    • In a `.mcp.json` file at the repo root that you can commit, exposing tools named `mcp__github__*` to everyone who clones the repo
    • Inside `~/.claude/settings.json` under a `mcpServers` key; the server is rebuilt on every session start
    • In a hidden global registry; the server is shared across all Claude users on your machine but not committed
  5. You want a shell script in CI to ask Claude Code to summarize a diff and print JSON to stdout, no interaction. Which invocation fits?
    • `claude --interactive 'summarize diff' --json`
    • `claude -p 'Summarize the diff as JSON' --allowedTools 'Read,Bash(git:*)' --output-format json`
    • `claude chat --headless --plan 'summarize diff'`
    • `claude run --silent 'summarize diff' > out.json`

Related lessons