The Ultimate Guide to Advanced Claude Code Usage: Parallel Development, Plan Mode, and Hooks
Summary: Based on official Claude Code documentation and internal team best practices, this comprehensive guide covers advanced workflows including Git worktree parallel sessions, Plan Mode for complex task planning, CLAUDE.md knowledge management, Skills automation, Subagents for multi-threading, Hooks for event-driven automation, and 10 core technical strategies for data analysis and terminal optimization.
Core Claude Code Workflows
Understanding New Codebases
Claude Code provides streamlined workflows for rapidly comprehending unfamiliar codebases. When you join a new project, you can master its structure through several key steps:
-
Get a Codebase Overview: Ask Claude to analyze the directory structure, main modules, and architecture. -
Find Relevant Code: Use Glob and Grep tools to locate files specific to a particular feature or functionality. -
Understand Relationships: Use the Read tool to examine key files and establish connections between code components.
These tools can be combined to form a complete code exploration workflow. For example, start by using Glob to locate all configuration files, then use Grep to search for specific function names, and finally use Read to examine key implementations.
Fixing Bugs Efficiently
When encountering an error message, Claude Code can quickly locate and fix the source of the problem:
-
Error Analysis: Paste the error log directly; Claude will analyze the error type and potential causes. -
Code Location: Use Grep to search for functions, classes, or variables related to the error. -
Automatic Fixes: Let Claude generate a fix and apply changes using the Edit tool. -
Verification: Run tests or check the code to confirm the issue is resolved.
Team experience suggests that most bugs can be fixed by Claude independently. A common scenario involves enabling the Slack MCP and pasting a bug report from Slack directly to Claude with a single word: “fix.” No context explanation or manual location is required. Claude will examine the code, understand the problem, and repair it.
For more complex scenarios like distributed system issues, pointing Docker logs to Claude allows it to help troubleshoot. Claude is “surprisingly capable” in this regard.
Refactoring Code
When updating old code to use modern patterns and practices, Claude Code offers a systematic refactoring workflow:
-
Code Analysis: First, analyze the structure and issues of existing code. -
Refactoring Planning: Discuss the refactoring plan in Plan Mode. -
Step-by-Step Implementation: Use Edit and Write tools to gradually replace old code. -
Test Validation: Run tests to ensure refactoring hasn’t broken functionality.
Core Principle: For complex tasks, use Plan Mode to discuss the solution with Claude first. Iterate until you are satisfied with the plan, then switch to auto-edit mode for Claude to execute. A good plan usually means Claude can get it right in one go, without back-and-forth changes.
Working with Tests
When adding tests for uncovered code, Claude Code can:
-
Analyze Coverage: Identify code paths lacking tests. -
Generate Test Cases: Generate new tests based on existing test patterns. -
Verify Tests: Run tests to ensure they pass. -
Discover Edge Cases: Analyze code paths to suggest tests for error conditions, boundary values, and unexpected inputs.
Claude can generate tests that follow your project’s existing patterns and conventions. When asking for tests, be specific about what behavior you want to verify. For comprehensive coverage, ask Claude to identify edge cases you might have missed.
Creating Pull Requests
You can create a PR by directly asking Claude (“create a pr for my changes”) or by using the /commit-push-pr skill, which commits, pushes, and opens a PR in one step.
> /commit-push-pr
If you have a Slack MCP server configured and specify channels in your CLAUDE.md (e.g., “post PR URLs to #team-prs”), the skill automatically posts the PR URL to those channels. For more control, guide Claude step-by-step or create your own skill.
When you create a PR using gh pr create, the session is automatically linked to that PR. You can resume it later with claude --from-pr <number>.
Plan Mode: The Planning Tool for Complex Tasks
When to Use Plan Mode
Plan Mode instructs Claude to create a plan by analyzing the codebase with read-only operations, making it perfect for exploring codebases, planning complex changes, or safely reviewing code. In Plan Mode, Claude uses AskUserQuestion to gather requirements and clarify your goals before proposing a plan.
Use Plan Mode when:
-
Multi-step Implementation: When your feature requires edits to multiple files. -
Code Exploration: When you want to thoroughly research the codebase before changing anything. -
Interactive Development: When you want to iterate on the direction with Claude.
How to Activate Plan Mode
Switch to Plan Mode during a session: You can switch into Plan Mode during a session using Shift+Tab to cycle through permission modes. If you are in Normal Mode, Shift+Tab first switches into Auto-Accept Mode, indicated by ⏵⏵ accept edits on at the bottom of the terminal. A subsequent Shift+Tab will switch into Plan Mode, indicated by ⏸ plan mode on.
Start a new session in Plan Mode: Use the --permission-mode plan flag to start a new session:
claude --permission-mode plan
Run “headless” queries in Plan Mode: You can also run a query in Plan Mode directly with -p (i.e., in “headless mode”):
claude --permission-mode plan -p "Analyze the authentication system and suggest improvements"
Practical Example
Planning a complex refactor:
claude --permission-mode plan
> I need to refactor our authentication system to use OAuth2. Create a detailed migration plan.
Claude analyzes the current implementation and creates a comprehensive plan. Refine with follow-ups:
> What about backward compatibility?
> How should we handle database migration?
Configure Plan Mode as Default
// .claude/settings.json
{
"permissions": {
"defaultMode": "plan"
}
}
Hooks: Event-Driven Automation
Hook Lifecycle
Hooks are user-defined shell commands or LLM prompts that execute automatically at specific points in Claude Code’s lifecycle. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler.
Some events fire once per session, while others fire repeatedly inside the agentic loop:
| Event | When it fires |
|---|---|
SessionStart |
When a session begins or resumes |
UserPromptSubmit |
When you submit a prompt, before Claude processes it |
PreToolUse |
Before a tool call executes. Can block it |
PermissionRequest |
When a permission dialog appears |
PostToolUse |
After a tool call succeeds |
PostToolUseFailure |
After a tool call fails |
Notification |
When Claude Code sends a notification |
SubagentStart |
When a subagent is spawned |
SubagentStop |
When a subagent finishes |
Stop |
When Claude finishes responding |
PreCompact |
Before context compaction |
SessionEnd |
When a session terminates |
How a Hook Resolves
Consider this PreToolUse hook that blocks destructive shell commands. The hook runs block-rm.sh before every Bash tool call:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-rm.sh"
}
]
}
]
}
}
The script reads JSON input from stdin, extracts the command, and returns a permissionDecision of "deny" if it contains rm -rf:
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}'
else
exit 0 # allow the command
fi
When Claude Code decides to run Bash "rm -rf /tmp/build", the process is:
-
Claude Code generates JSON input. -
The PreToolUse hook matcher matches “Bash”. -
The block-rm.sh script executes and reads the JSON input. -
The script detects rm -rfand returns a deny decision. -
Claude Code reads the JSON output and blocks the command.
Hook Configuration
Hooks are defined in JSON settings files. The configuration has three levels of nesting:
-
Choose a hook event, like PreToolUseorStop. -
Add a matcher group to filter when it fires, like “only for the Bash tool”. -
Define one or more hook handlers to run when matched.
Hook Locations
Where you define a hook determines its scope:
| Location | Scope | Shareable |
|---|---|---|
~/.claude/settings.json |
All your projects | No, local to your machine |
.claude/settings.json |
Single project | Yes, can be committed to repo |
.claude/settings.local.json |
Single project | No, gitignored |
| Managed policy settings | Organization-wide | Yes, admin-controlled |
Plugin hooks/hooks.json |
When plugin enabled | Yes, bundled with the plugin |
| Skill or agent frontmatter | While component active | Yes, defined in component file |
Matcher Patterns
The matcher field is a regex string that filters when hooks fire. Use "*", "", or omit matcher entirely to match all occurrences. Each event type matches on a different field:
| Event | Matcher filters | Example matcher values |
|---|---|---|
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest |
tool name | Bash, `Edit |
SessionStart |
how session started | startup, resume, clear, compact |
SessionEnd |
why session ended | clear, logout, prompt_input_exit |
Notification |
notification type | permission_prompt, idle_prompt |
SubagentStart |
agent type | Bash, Explore, Plan |
PreCompact |
triggered compaction | manual, auto |
SubagentStop |
agent type | Same values as SubagentStart |
UserPromptSubmit, Stop |
No matcher support | Fires on every occurrence |
The matcher is a regex, so Edit|Write matches either tool and Notebook.* matches any tool starting with Notebook.
Hook Handler Fields
Each object in the inner hooks array is a hook handler: the shell command, LLM prompt, or agent that runs when the matcher matches. There are three types:
-
Command hooks ( type: "command"): Run a shell command. The script receives the event’s JSON input on stdin and communicates results back via exit codes and stdout. -
Prompt hooks ( type: "prompt"): Send a prompt to a Claude model for single-turn evaluation. The model returns a yes/no decision as JSON. -
Agent hooks ( type: "agent"): Spawn a subagent that can use tools like Read, Grep, and Glob to verify conditions before returning a decision.
Common fields:
| Field | Required | Description |
|---|---|---|
type |
yes | "command", "prompt", or "agent" |
timeout |
no | Seconds before canceling. Defaults: 600 for command, 30 for prompt, 60 for agent |
statusMessage |
no | Custom spinner message displayed while hook runs |
once |
no | If true, runs only once per session then is removed. Skills only |
Command hook fields:
| Field | Required | Description |
|---|---|---|
command |
yes | Shell command to execute |
async |
no | If true, runs in background without blocking |
Prompt and agent hook fields:
| Field | Required | Description |
|---|---|---|
prompt |
yes | Prompt text to send to model. Use $ARGUMENTS as placeholder for hook input JSON |
model |
no | Model to use for evaluation. Defaults to a fast model |
Hook Input and Output
Hooks receive JSON data via stdin and communicate results through exit codes, stdout, and stderr.
Common Input Fields
All hook events receive these fields via stdin as JSON:
| Field | Description |
|---|---|
session_id |
Current session identifier |
transcript_path |
Path to conversation JSON |
cwd |
Current working directory when hook is invoked |
permission_mode |
Current permission mode: "default", "plan", "acceptEdits", "dontAsk", "bypassPermissions" |
hook_event_name |
Name of the event that fired |
For example, a PreToolUse hook for a Bash command receives:
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
Exit Code Output
The exit code from your hook command tells Claude Code whether the action should proceed, be blocked, or be ignored.
Exit 0 means success. Claude Code parses stdout for JSON output fields. JSON output is only processed on exit 0. For most events, stdout is only shown in verbose mode (Ctrl+O). The exceptions are UserPromptSubmit and SessionStart, where stdout is added as context that Claude can see and act on.
Exit 2 means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message. The effect depends on the event: PreToolUse blocks the tool call, UserPromptSubmit rejects the prompt, etc.
Any other exit code is a non-blocking error. stderr is shown in verbose mode (Ctrl+O) and execution continues.
JSON Output
Exit codes allow allow/block, but JSON output gives finer-grained control. Instead of exiting with code 2 to block, exit 0 and print a JSON object to stdout. Claude Code reads specific fields from that JSON to control behavior.
The JSON object supports three kinds of fields:
-
Universal fields like continuework across all events. -
Top-level decisionandreasonare used by some events to block or provide feedback. -
hookSpecificOutputis a nested object for events that need richer control. It requires ahookEventNamefield set to the event name.
| Field | Default | Description |
|---|---|---|
continue |
true |
If false, Claude stops processing entirely after hook runs. |
stopReason |
none | Message shown to user when continue is false. Not shown to Claude. |
suppressOutput |
false |
If true, hides stdout from verbose mode output |
systemMessage |
none | Warning message shown to user |
Key Hook Events
SessionStart
Runs when Claude Code starts a new session or resumes an existing session. Useful for loading development context like existing issues or recent changes, or setting environment variables. For static context, use CLAUDE.md instead.
Matcher values correspond to how the session was started:
| Matcher | When it fires |
|---|---|
startup |
New session |
resume |
--resume, --continue, or /resume |
clear |
/clear |
compact |
Auto or manual compaction |
In addition to common input fields, SessionStart hooks receive source, model, and optionally agent_type.
Persist Environment Variables: SessionStart hooks have access to the CLAUDE_ENV_FILE environment variable, which provides a file path where you can persist environment variables for subsequent Bash commands. To set individual environment variables, write export statements to CLAUDE_ENV_FILE.
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0
PreToolUse
Runs after Claude creates tool parameters and before processing the tool call. Matches on tool name: Bash, Edit, Write, Read, Glob, Grep, Task, WebFetch, WebSearch, and any MCP tool names. Use PreToolUse decision control to allow, deny, or ask for permission to use the tool.
Input Fields:
In addition to common fields, PreToolUse hooks receive tool_name, tool_input, and tool_use_id. The tool_input fields depend on the tool.
Bash Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
command |
string | "npm test" |
The shell command to execute |
description |
string | "Run test suite" |
Optional description of command |
timeout |
number | 120000 |
Optional timeout in milliseconds |
run_in_background |
boolean | false |
Whether to run command in background |
Write Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
file_path |
string | "/path/to/file.txt" |
Absolute path to file to write |
content |
string | "file content" |
Content to write to file |
Edit Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
file_path |
string | "/path/to/file.txt" |
Absolute path to file to edit |
old_string |
string | "original text" |
Text to find and replace |
new_string |
string | "replacement text" |
Replacement text |
replace_all |
boolean | false |
Whether to replace all occurrences |
Read Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
file_path |
string | "/path/to/file.txt" |
Absolute path to file to read |
offset |
number | 10 |
Optional line number to start reading from |
limit |
number | 50 |
Optional number of lines to read |
Glob Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
pattern |
string | "**/*.ts" |
Glob pattern to match files against |
path |
string | "/path/to/dir" |
Optional directory to search in. Default: cwd |
Grep Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
pattern |
string | "TODO.*fix" |
Regular expression pattern to search for |
path |
string | "/path/to/dir" |
Optional file or directory to search in |
glob |
string | "*.ts" |
Optional glob pattern to filter files |
output_mode |
string | "content" |
"content", "files_with_matches", or "count". Default: "files_with_matches" |
-i |
boolean | true |
Case insensitive search |
multiline |
boolean | false |
Enable multiline matching |
WebFetch Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
url |
string | "https://example.com/api" |
URL to fetch content from |
prompt |
string | "Extract the API endpoints" |
Prompt to run on fetched content |
WebSearch Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
query |
string | "react hooks best practices" |
Search query |
allowed_domains |
array | ["docs.example.com"] |
Optional: only include results from these domains |
blocked_domains |
array | ["spam.example.com"] |
Optional: exclude results from these domains |
Task Input Fields:
| Field | Type | Example | Description |
|---|---|---|---|
prompt |
string | "Find all API endpoints" |
The task for the agent to perform |
description |
string | "Find API endpoints" |
Short description of the task |
subagent_type |
string | "Explore" |
Type of specialized agent to use |
model |
string | "sonnet" |
Optional model alias to override default |
PreToolUse Decision Control: PreToolUse hooks can control whether a tool call proceeds. Unlike other hooks that use a top-level decision field, PreToolUse returns its decision inside a hookSpecificOutput object.
| Field | Description |
|---|---|
permissionDecision |
"allow" bypasses permission system, "deny" prevents tool call, "ask" prompts user |
permissionDecisionReason |
For "allow"/"ask": shown to user not Claude. For "deny": shown to Claude |
updatedInput |
Modifies tool input parameters before execution. Combine with "allow" to auto-approve |
additionalContext |
String added to Claude’s context before tool executes |
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
PermissionRequest
Runs when the user is shown a permission dialog. Use PermissionRequest decision control to allow or deny on behalf of the user. Matches tool name, same values as PreToolUse.
In addition to common input fields, PermissionRequest hooks receive tool_name and tool_input fields like PreToolUse hooks, but without tool_use_id. An optional permission_suggestions array contains the “always allow” options the user would normally see.
PermissionRequest Decision Control:
| Field | Description |
|---|---|
behavior |
"allow" grants permission, "deny" denies it |
updatedInput |
For "allow" only: modifies tool input parameters before execution |
updatedPermissions |
For "allow" only: applies permission rule updates |
message |
For "deny" only: tells Claude why permission was denied |
interrupt |
For "deny" only: if true, stops Claude |
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
PostToolUse
Runs immediately after a tool completes successfully. Matches tool name, same values as PreToolUse.
PostToolUse hooks fire after a tool has already executed successfully. The input includes both tool_input (arguments sent to tool) and tool_response (result it returned).
PostToolUse Decision Control:
| Field | Description |
|---|---|
decision |
"block" prompts Claude with reason. Omit to allow action proceed |
reason |
Explanation shown to Claude when decision is "block" |
additionalContext |
Additional context for Claude to consider |
updatedMCPToolOutput |
For MCP tools only: replaces tool’s output with provided value |
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude"
}
}
PostToolUseFailure
Runs when a tool execution fails. This event fires for tool calls that throw errors or return failure results. Use this to log failures, send alerts, or provide corrective feedback to Claude.
In addition to common input fields, PostToolUseFailure hooks receive the same tool_name and tool_input fields as PostToolUse, along with error information as top-level fields:
{
"session_id": "abc123",
"hook_event_name": "PostToolUseFailure",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
},
"tool_use_id": "toolu_01ABC123...",
"error": "Command exited with non-zero status code 1",
"is_interrupt": false
}
| Field | Description |
|---|---|
error |
String describing what went wrong |
is_interrupt |
Optional boolean indicating if failure was caused by user interruption |
PostToolUseFailure Decision Control:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": "Additional information about the failure for Claude"
}
}
Practical Tips and Best Practices
Invest in Your CLAUDE.md
This is arguably the highest ROI technique. CLAUDE.md is a file in the project root that Claude Code reads every time it starts. You can write code standards, design principles, PR templates, common error reminders—anything you want Claude to remember.
The key is maintenance. The team’s practice is: after correcting Claude’s mistake, let it update CLAUDE.md itself. A specific prompt could be: “Update your CLAUDE.md so you don’t make that mistake again.”
One engineer’s approach is more systematic: he maintains a notes directory for each project/task, updating after every PR. He then points to these notes in CLAUDE.md, essentially building a continuously updating knowledge base for Claude.
Note: CLAUDE.md should not contain too much content; it will be counterproductive. Only include the most important content AI hasn’t been trained on. Use file links for more content to be read on demand. Claude Code’s official project CLAUDE.md is only about 2.5k tokens:
-
Common Bash Commands: Let AI know how to operate the command line like a developer. -
Code Style Conventions: Ensure AI-written code meets team standards. -
UI and Content Design Guidelines: Guide AI on interface design and copywriting. -
Core Technical Implementation Flows: Teach AI how to handle State Management, Logging, Error Handling, Feature Gating, and Debugging. -
Pull Request Template: Standardize documentation format when submitting code.
Create Custom Skills
If you do something twice a day, it’s worth turning it into a skill or slash command. A Skill is a set of reusable instructions in a project, invoked with a slash command. For example, /commit-push-pr can commit, push, and create a PR in one step.
Team examples of skills in use:
-
/techdebt: Runs at the end of every session to check and clean up duplicate code.
Some have built a slash command that syncs the last 7 days of Slack messages, Google Drive docs, Asana tasks, and GitHub activity into a single context—effectively a one-click “what happened this week” panoramic view.
Advanced usage: Some use skills to build “Data Analysis Engineer” type agents that can automatically write dbt models, review code, and test changes in dev.
The benefit of Skills is they can be committed to git and reused across projects.
Elevate Your Prompting Techniques
Let Claude Quiz You:
Prompt example: “Grill me on these changes and don’t make a PR until I pass your test.”
Or: “Prove to me this works.” Let Claude compare the behavior of the main branch against your feature branch.
This turns Claude from an “executor” into an “auditor,” having it review you.
Scrap and Rebuild:
When Claude’s solution isn’t good enough, don’t patch it. Just say: “Knowing everything you know now, scrap this and implement the elegant solution.”
Usually, use git to roll back code, start a new session, adjust the prompt, and try again. Continuing in the same session means previous error info might interfere with Claude’s judgment.
Reduce Ambiguity:
Be as detailed as possible when assigning tasks. The more specific you are, the more accurate Claude’s output.
Terminal and Environment Configuration
Many in the team use Ghostty terminal because of its synchronized rendering, 24-bit true color, and full Unicode support. These are important when running multiple Claude sessions simultaneously.
Another practical tip: Use /statusline to customize the status bar to always show current context usage and git branch. This lets you see the status of each session at a glance.
Some use tmux to manage multiple sessions, coloring and naming each tab—one tab per task or worktree.
An often overlooked suggestion: Use voice input. Your speaking speed is three times your typing speed. More importantly, when speaking, you unconsciously say things in more detail, leading to higher quality prompts. On macOS, press fn twice to start voice input.
Using Subagents
This is an advanced technique that is very powerful when used well.
Simplest Usage: Add “use subagents” to any request. Claude will automatically split the task among multiple Subagents processing in parallel, effectively letting it “open more threads” to solve the problem.
Another Usage: Use Subagents to keep the main session context clean. Dispatch independent subtasks, while the main session only handles overall coordination. This prevents the main session’s context window from getting stuffed with intermediate processes.
Subagents allow for parallel tasking, saving significant time.
Advanced Usage: Use hooks to route permission requests to a stronger model, letting it judge which operations are safe to auto-approve and which require human confirmation. It’s like adding a “security auditor” to Claude.
Data Analysis with Claude Code
This usage might surprise many. The team has encapsulated BigQuery usage into a skill, allowing everyone to query data directly using the bq command line in Claude Code.
This isn’t limited to BigQuery. Any database with a CLI, MCP, or API can be used this way. PostgreSQL, MySQL, MongoDB—you can let Claude write queries, run analyses, and generate reports.
This might be even more valuable for non-engineers. Data scientists on the team are now using Claude Code to write queries and create visualizations. The boundaries of tools are blurring.
Learning with Claude Code
Finally, this technique is about using Claude Code to learn new things.
First: Enable “Explanatory” or “Learning” output style in /config. This way, when Claude edits code, it explains “why” it’s making the change, rather than just doing it.
Second: Ask Claude to generate HTML slides to explain unfamiliar code. The results are surprisingly good. You can view a richly illustrated code explanation directly in the browser.
Third: Ask Claude to draw ASCII diagrams to explain protocols, architectures, and data flows. Pure text charts are unexpectedly helpful for understanding complex systems.
Terminal Setup Optimization
Themes and Appearance
Claude cannot control your terminal’s theme. That is handled by your terminal application. You can match Claude Code’s theme to your terminal any time via the /config command. For additional customization of the Claude Code interface itself, you can configure a custom status line to display contextual information like the current model, working directory, or git branch.
Line Breaks
You have several options for entering line breaks into Claude Code:
-
Quick escape: Type \followed by Enter to create a newline -
Shift+Enter: Works out of the box in iTerm2, WezTerm, Ghostty, and Kitty -
Keyboard shortcut: Set up a keybinding to insert a newline in other terminals
Set up Shift+Enter for other terminals: Run /terminal-setup within Claude Code to automatically configure Shift+Enter for VS Code, Alacritty, Zed, and Warp.
Set up Option+Enter (VS Code, iTerm2 or macOS Terminal.app)
For Mac Terminal.app:
-
Open Settings → Profiles → Keyboard -
Check “Use Option as Meta Key”
For iTerm2 and VS Code terminal:
-
Open Settings → Profiles → Keys -
Under General, set Left/Right Option key to “Esc+”
Vim Mode
Claude Code supports a subset of Vim keybindings that can be enabled with /vim or configured via /config. The supported subset includes:
-
Mode switching: Esc(to NORMAL),i/I,a/A,o/O(to INSERT) -
Navigation: h/j/k/l,w/e/b,0/^,gg/G,f/F/t/Twith;/,repeat -
Editing: x,dw/de/db/dd/D,cw/ce/cb/cc/C,.(repeat) -
Yank/paste: yy/Y,yw/ye/yb,p/P -
Text objects: iw/aw,iW/aW,i"/a",i'/a',i(/a(,i[/a[,i{/a{ -
Indentation: >>/<< -
Line operations: J(join lines)
Extended Thinking (Thinking Mode)
Extended thinking is enabled by default, reserving a portion of the output token budget (up to 31,999 tokens) for Claude to reason through complex problems step-by-step. This reasoning is visible in verbose mode, which you can toggle on with Ctrl+O. Extended thinking is particularly valuable for complex architectural decisions, challenging bugs, multi-step implementation planning, and evaluating tradeoffs.
Configure thinking mode:
Thinking is enabled by default, but you can adjust or disable it.
| Scope | How to configure | Details |
|---|---|---|
| Toggle shortcut | Press Option+T (macOS) or Alt+T (Windows/Linux) |
Toggle thinking on/off for current session. May require terminal config. |
| Global default | Use /config to toggle thinking mode |
Sets your default across all projects. |
Saved as alwaysThinkingEnabled in ~/.claude/settings.json |
||
| Limit token budget | Set MAX_THINKING_TOKENS environment variable |
Limit thinking budget to specific number of tokens. Example: export MAX_THINKING_TOKENS=10000 |
To view Claude’s thinking process, press Ctrl+O to toggle verbose mode and see the internal reasoning displayed as gray italic text.
How extended thinking token budgets work:
Extended thinking uses a token budget that controls how much internal reasoning Claude can perform. A larger thinking token budget provides:
-
More space to explore multiple solution approaches step-by-step -
Room to analyze edge cases and evaluate tradeoffs thoroughly -
Ability to revise reasoning and self-correct mistakes
Token budgets for thinking mode:
-
When thinking is enabled, Claude can use up to 31,999 tokens from your output budget for internal reasoning. -
When thinking is disabled (via toggle or /config), Claude uses 0 tokens for thinking.
Limit the thinking budget: Use the MAX_THINKING_TOKENS environment variable to cap the thinking budget.
Frequently Asked Questions
How do I handle multiple tasks simultaneously?
Use Git worktree to check out 3-5 working directories simultaneously, running an independent Claude Code session in each. For instance, Directory A is refactoring a module, Directory B is writing tests, and Directory C is updating documentation—advancing three tasks in parallel. Git worktree lets you have multiple branch working directories open in the same repo without switching back and forth.
When should I use Plan Mode?
When encountering complex tasks, discuss the solution with Claude in Plan Mode first. Iterate until you are satisfied with the plan, then switch to auto-edit mode for execution. If things go off track, immediately return to Plan Mode to re-plan—don’t push blindly.
What should I include in CLAUDE.md?
Only the most important content AI hasn’t been trained on, such as project-specific Bash commands, code style conventions, UI design guidelines, core technical implementation flows, and PR templates. Use file links for more content to be read on demand.
How do I create effective Skills?
If you do something twice a day, it’s worth turning it into a skill or slash command. Automate repetitive tasks like the full commit/push/create PR workflow, or integrate multiple data sources to get a panoramic view.
What is the difference between Hooks and Skills?
Hooks are event-driven automations that execute when specific events trigger, such as before/after tool calls or session start/end. Skills are reusable instruction sets invoked manually with slash commands, suitable for repeatable workflows.
How can I get Claude to fix bugs more accurately?
Be clear when describing bugs: how to reproduce, the expected result, and the actual problem (e.g., error logs, screenshots). With this basic information, the AI has enough context to locate and validate the issue. Give Claude enough context and permissions, then trust it—don’t micromanage step-by-step.
How do I switch between multiple Claude sessions?
From inside an active session, use /resume to switch to a different conversation. Sessions are stored per project directory. The /resume picker shows sessions from the same git repository, including worktrees. When starting Claude Code, you can resume a previous session: claude --continue continues the most recent conversation in the current directory, claude --resume opens a conversation picker or resumes by name.
How do I manage Hooks?
Use the /hooks command to open an interactive hooks manager where you can view, add, and delete hooks without editing settings files directly. Each hook in the menu is labeled with a bracket prefix indicating its source: [User] from ~/.claude/settings.json, [Project] from .claude/settings.json, [Local] from .claude/settings.local.json, [Plugin] from a plugin’s hooks/hooks.json (read-only).
How do I use Subagents in Claude Code?
Add “use subagents” to any request, and Claude will automatically split the task among multiple Subagents processing in parallel. Alternatively, use Subagents to keep the main session context clean by dispatching independent subtasks while the main session only handles overall coordination.

