Skip to content

Claude Code: Advanced

Advanced

Claude Code is now part of your daily work, and the defaults are getting in your way: too many approval prompts, the same rule re-typed every session, the same “looks done” code that misses an edge case. At this level you stop using the harness as shipped and start shaping it: permissions tuned to your trust, rules made deterministic, and checks that close the loop so you don’t have to. This is where a reliable workflow gets built.

Shift+Tab cycles three permission modes by default: Default (asks before edits and shell), Auto-accept edits, and Plan mode. A fourth, Auto mode, joins the cycle only when your account and model qualify and you opt in.

Permission mode is a dial that trades your attention for speed. Two ways to keep the speed without the blindness:

  • Allowlists. Permit known-safe commands in .claude/settings.json (npm run lint, git commit) so Claude stops asking for the ones you would always approve.
  • Sandboxing. OS-level filesystem and network isolation, so Claude works freely inside a boundary instead of asking at each step.

A starting allowlist, in .claude/settings.json at your project root:

{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(git status)",
"Bash(git diff *)"
],
"deny": [
"Bash(git push *)",
"Read(./.env)"
]
}
}

* is a wildcard within one segment, ** spans directories, and a deny rule beats an allow for the same command. Grow the list from your own approval history, not from guesses about what you might need.

The single highest-value habit at this level: stop being the verification loop yourself. Claude stops when work looks done; without a check it can read, every mistake waits for you to notice it. A check is anything that returns a pass/fail in the conversation: a test suite, a build exit code, a linter, a fixture diff, a screenshot. The escalation ladder, from least to most setup, is the core idea behind verification:

GateMechanismTrade-off
In one prompt”Run the check and iterate until it passes.”Least setup, most attention
Across a sessionA /goal condition; an evaluator re-checks every turn until it holdsMore setup
Deterministic gateA Stop hook runs your check and blocks the turn from ending until it passesMore setup, runs unattended
Second opinionA fresh subagent gets the diff and your criteria and tries to refute the resultMost setup, least attention

Always ask for evidence, not assertions: the pasted test output, the command and what it returned. Reviewing the proof beats re-running the work yourself, and it works for runs you did not watch.

CLAUDE.md and memory are context: advice Claude can choose to follow. A hook is a shell script that runs automatically at a lifecycle point, and it fires regardless of what the model decides.

  • A PreToolUse hook can block an action, for example refuse any write to the migrations folder.
  • A Stop hook can run node --test and block the turn from ending until it passes. (Claude Code overrides a Stop hook after 8 consecutive blocks, so it cannot wedge forever.)

Configure hooks in .claude/settings.json; browse them with /hooks. A minimal Stop gate looks like this:

{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "./scripts/verify.sh" }
]
}
]
}
}

The script’s exit code is the contract: exit 0 lets the turn end, exit 2 blocks it and feeds stderr back to Claude as the next thing to fix.

scripts/verify.sh
#!/bin/sh
npm test >/dev/null 2>&1 && exit 0
echo "Tests are failing. Run npm test and fix them before finishing." >&2
exit 2

Claude can also write hooks for you: “add a hook that runs eslint after every edit to src/.” The rule of thumb: if something must happen every time, it is a hook, not a sentence in a file.

Two ways to extend Claude without bloating the main conversation:

  • Skills. A SKILL.md in .claude/skills/ packages domain knowledge or a workflow. Claude sees the short descriptions at session start and loads the full body only when it is relevant, so a skill costs almost no context until used. For manual, side-effecting workflows, set disable-model-invocation: true and invoke it yourself with /skill-name, passing $ARGUMENTS.
  • Subagents. A .md file in .claude/agents/, with its own context window, tools, and model. Hand a subagent a heavy reading job (“investigate how due dates are represented across the codebase”) and it works in a separate window, returning a summary while your main context stays clean. Configure with /agents.

When a service has no good CLI, MCP connects it. claude mcp add wires up tools like Notion, Figma, databases, or monitoring. Tool definitions are deferred by default: only the names cost context until a tool is actually needed, so adding a server does not blow your window. Check per-server cost with /mcp.

Reach for a CLI you already have first (gh, aws, gcloud): a CLI costs one command and its output, far less than a service that loads many tool definitions. Use MCP when no CLI exists.

GoalReach for
Advisory knowledgeCLAUDE.md or a skill
Must happen every timeA hook
Reach an external serviceA CLI, or MCP
Isolate heavy readingA subagent
  • ✅ You’ve added an allowlist (or sandbox) so common commands stop prompting you
  • ✅ A Stop hook or /goal condition runs your check automatically
  • ✅ You’ve created a skill or a subagent and know which problem each one solves
  • ✅ You can name which row of the table above a new need lands on

Ready to run the same loop wide and unattended: headless scripts, parallel sessions, agent teams, and adversarial review?