Learn & Master

Prompts

How Claude Code builds its system prompt, spawns agents, and how you can write better prompts

How the System Prompt is Built

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.

1

Priority Check

buildEffectiveSystemPrompt() checks 5 priority levels in order:

  1. Override — Loop mode replaces everything
  2. Coordinator — If CLAUDE_CODE_COORDINATOR_MODE=1
  3. Agent — If running as a sub-agent
  4. Custom — If --system-prompt flag set
  5. Default — The standard Claude Code prompt
2

Static Sections (Cached Globally)

Everything before __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__ is identical for all users and cached across sessions:

3

Dynamic Sections (Per-Session)

Everything after the boundary is session-specific:

Prompt Section Details

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:

  • Read before modify — Never propose changes to unread code
  • Minimal files — Don't create unless absolutely necessary
  • Security first — Avoid OWASP top 10 vulnerabilities
  • No gold-plating — Don't add features, docstrings, or refactoring beyond what's asked
  • No speculative abstractions — Three similar lines > premature abstraction
  • Diagnose before retry — Read the error, check assumptions

The reversibility principle — freely take local, reversible actions but confirm risky ones:

Destructive — rm -rf, git reset --hard, drop tables
Hard to Reverse — force-push, amend published commits
Visible to Others — pushing code, creating PRs, sending messages
Safe — editing files, running tests, reading code

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.

Memory Injection

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.