How Claude Code builds its system prompt, spawns agents, and how you can write better prompts
Claude Code's system prompt is not a single static string. It's dynamically assembled from modular sections with a smart caching strategy. The file utils/systemPrompt.ts orchestrates everything.
buildEffectiveSystemPrompt() checks 5 priority levels in order:
CLAUDE_CODE_COORDINATOR_MODE=1--system-prompt flag setEverything before __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__ is identical for all users and cached across sessions:
Everything after the boundary is session-specific:
The full prompt is defined in constants/prompts.ts (914 lines). Here are the key sections:
Identifies Claude as "Claude Code, Anthropic's official CLI for Claude." Sets the security policy and output style configuration.
You are Claude Code, Anthropic's official CLI for Claude. You are an interactive agent that helps users with software engineering tasks. IMPORTANT: Assist with authorized security testing, defensive security, CTF challenges, and educational contexts. Refuse requests for destructive techniques...
Core guidelines for code generation:
The reversibility principle — freely take local, reversible actions but confirm risky ones:
Tool preference hierarchy:
// Instead of Bash commands, use dedicated tools: Read → NOT cat, head, tail, sed Edit → NOT sed, awk Write → NOT echo/cat heredoc Glob → NOT find, ls Grep → NOT grep, rg Agent → for complex multi-step research TaskCreate → for tracking work
Key rule: Maximize parallel tool calls for independent operations.
Injected dynamically every session with:
# Environment - Working directory: /path/to/project - Is a git repository: true - Platform: darwin - Shell: zsh - OS Version: Darwin 25.3.0 - Model: Claude Opus 4.6 - Knowledge cutoff: May 2025 - Claude Code availability: CLI, desktop, web, IDE
Two types of sections for efficient token usage:
// Cached until /clear or /compact
systemPromptSection('session_guidance',
() => getSessionSpecificGuidanceSection(...)
)
// Re-computed every turn (breaks cache!)
DANGEROUS_uncachedSystemPromptSection(
'mcp_instructions',
() => getMcpInstructions(...),
'MCP servers connect/disconnect'
)The __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__ marker splits globally-cacheable content from session-specific content.
The memory system injects context from MEMORY.md files into the prompt:
// memdir/memdir.ts - loadMemoryPrompt() 1. Load MEMORY.md from project's .claude/ directory 2. Truncate to 200 lines max (warn if truncated) 3. Inject as dynamic system prompt section 4. Cache until /clear or /compact // MEMORY.md format: --- name: memory_name description: one-line description type: user|feedback|project|reference --- Memory content here...
Limits: 200 lines, 25KB max. The Dream system auto-updates memory during sleep.