claude-code/learn/10-configuration-and-system-prompt.md

11 KiB

10. Configuration and System Prompt

How settings cascade from enterprise policy down to project CLAUDE.md, and how the system prompt is assembled.


Configuration Hierarchy

Claude Code loads settings from 5 layers, each with strict priority. Higher layers override lower ones.

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
flowchart TD
    subgraph Sources["Configuration Sources — Highest to Lowest Priority"]
        direction TB
        ENT["Enterprise / MDM Policy<br/>managed-settings.json<br/>Org-level, cannot override"]:::ent
        USER["User Settings<br/>~/.claude/settings.json<br/>Per-user global defaults"]:::user
        PROJ["Project Settings<br/>.claude/settings.json<br/>Per-repo configuration"]:::proj
        MCP_CFG[".mcp.json<br/>Project MCP servers<br/>Committed to repo"]:::proj
        CLAUDE_MD["CLAUDE.md files<br/>Instructions, rules,<br/>memory for the model"]:::md
    end

    ENT --> MERGE
    USER --> MERGE
    PROJ --> MERGE
    MCP_CFG --> MERGE
    CLAUDE_MD --> MERGE

    MERGE["getInitialSettings<br/>Merge all layers<br/>Higher priority wins"]:::merge

    APPSTATE["AppState.settings<br/>Runtime configuration"]:::state

    MERGE --> APPSTATE

    classDef ent fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
    classDef user fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
    classDef proj fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
    classDef md fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
    classDef merge fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
    classDef state fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px

Enterprise / MDM Policy

Highest priority. Set by organization admins via Mobile Device Management (macOS profiles). Stored at a managed file path. Users cannot override these settings. Controls things like:

  • Allowed/denied MCP servers
  • Permission mode restrictions
  • Feature availability
  • Analytics opt-out

User Settings (~/.claude/settings.json)

Per-user defaults. Controls preferences like allowed tools, custom deny rules, and hooks.

Project Settings (.claude/settings.json)

Per-repository settings. Committed to the repo so all collaborators share the same configuration.

.mcp.json

MCP server definitions for the project. Lives at the repo root. Defines which MCP servers are available. Validated via Zod schema (McpServerConfigSchema).

CLAUDE.md

Markdown instruction files that the model reads as part of its system prompt. These are the "memory" files that tell Claude about this specific project.


CLAUDE.md Discovery

CLAUDE.md files are discovered from multiple locations and merged:

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#6f42c1', 'primaryBorderColor': '#6f42c1'}}}%%
flowchart TD
    subgraph Discovery["CLAUDE.md Discovery — getMemoryFiles"]
        GLOBAL["~/.claude/CLAUDE.md<br/>Global instructions"]:::global
        CWD_WALK["Walk from cwd up to git root<br/>Check each dir for CLAUDE.md<br/>and .claude/CLAUDE.md"]:::walk
        ADDITIONAL["--add-dir paths<br/>Extra directories to scan"]:::additional
    end

    FILTER["filterInjectedMemoryFiles<br/>Remove duplicates,<br/>check setting sources"]:::filter

    PARSE["getClaudeMds<br/>Read and concatenate<br/>all discovered files"]:::parse

    CACHE["setCachedClaudeMdContent<br/>Cache for auto-mode<br/>classifier to read"]:::cache

    INJECT["Injected into getUserContext<br/>becomes part of system prompt"]:::inject

    Discovery --> FILTER --> PARSE --> CACHE --> INJECT

    classDef global fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
    classDef walk fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
    classDef additional fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
    classDef filter fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
    classDef parse fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
    classDef cache fill:#333,stroke:#888,color:#e0e0e0,stroke-width:1px
    classDef inject fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px

The --bare flag skips auto-discovery but still honors explicit --add-dir paths.


System Prompt Assembly

The system prompt is what the model "sees" before any conversation messages. It's assembled from multiple pieces:

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#e83e8c', 'primaryBorderColor': '#e83e8c'}}}%%
flowchart TD
    subgraph Priority["System Prompt Priority — buildEffectiveSystemPrompt"]
        OVERRIDE["1. Override System Prompt<br/>Set via loop mode<br/>REPLACES everything"]:::override
        COORD["2. Coordinator System Prompt<br/>If coordinator mode active"]:::coord
        AGENT["3. Agent System Prompt<br/>If mainThreadAgentDefinition set<br/>In proactive mode: APPENDED<br/>Otherwise: REPLACES default"]:::agent
        CUSTOM["4. Custom System Prompt<br/>Via --system-prompt flag"]:::custom
        DEFAULT["5. Default System Prompt<br/>The standard Claude Code prompt"]:::default
    end

    APPEND["appendSystemPrompt<br/>Always appended at end<br/>except when override is set"]:::append

    EFFECTIVE["Effective System Prompt<br/>Sent as system param<br/>in API request"]:::result

    OVERRIDE -->|"if set"| EFFECTIVE
    COORD -->|"if coordinator"| EFFECTIVE
    AGENT -->|"if agent defined"| EFFECTIVE
    CUSTOM -->|"if --system-prompt"| EFFECTIVE
    DEFAULT -->|"fallback"| EFFECTIVE
    APPEND --> EFFECTIVE

    classDef override fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
    classDef coord fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
    classDef agent fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px
    classDef custom fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
    classDef default fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
    classDef append fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
    classDef result fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px

System Prompt Sections

Individual sections of the system prompt are defined via systemPromptSection():

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
flowchart LR
    subgraph Sections["System Prompt Sections"]
        CACHED["Cached Sections<br/>Computed once at start<br/>Reused every turn<br/>Cleared on /clear or /compact"]:::cached
        VOLATILE["DANGEROUS Uncached Sections<br/>Recomputed every turn<br/>BREAKS prompt cache"]:::volatile
    end

    RESOLVE["resolveSystemPromptSections<br/>Check cache, compute if needed"]:::resolve

    PROMPT["Final system prompt string"]:::result

    CACHED --> RESOLVE
    VOLATILE --> RESOLVE
    RESOLVE --> PROMPT

    classDef cached fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
    classDef volatile fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
    classDef resolve fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
    classDef result fill:#2d1b4e,stroke:#6f42c1,color:#e0e0e0,stroke-width:2px

Most sections are cached (computed once, reused every turn) to preserve prompt cache hits. Volatile sections that recompute every turn are explicitly named DANGEROUS_uncachedSystemPromptSection as a warning.


Context Injection

Beyond the system prompt, two context objects are injected into every conversation:

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#28a745', 'primaryBorderColor': '#28a745'}}}%%
flowchart LR
    subgraph SystemCtx["getSystemContext — memoized"]
        GIT["Git status snapshot<br/>branch, status, log,<br/>default branch, user"]
        INJECT_SYS["System prompt injection<br/>internal debugging only"]
    end

    subgraph UserCtx["getUserContext — memoized"]
        CMD["CLAUDE.md content<br/>all discovered files<br/>concatenated"]
        DATE["Current date<br/>ISO format"]
    end

    PREPEND["prependUserContext<br/>Prepended to messages<br/>before API call"]:::merge

    SystemCtx --> PREPEND
    UserCtx --> PREPEND

    classDef merge fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px

Both are memoized and cached for the duration of the conversation. They're cleared on /clear and /compact.


Markdown Config Discovery

Commands, agents, skills, and workflows are all loaded via markdownConfigLoader.ts:

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#fd7e14', 'primaryBorderColor': '#fd7e14'}}}%%
flowchart TD
    subgraph Dirs["Search Directories — Priority Order"]
        MANAGED["Managed (enterprise)<br/>Policy-level configs"]:::ent
        USER_DIR["User (~/.claude/)<br/>Personal configs"]:::user
        PROJ_DIRS["Project (.claude/)<br/>Walk cwd up to git root<br/>Check each ancestor"]:::proj
    end

    LOADER["loadMarkdownFilesForSubdir<br/>Discover + parse YAML frontmatter<br/>+ markdown content"]:::loader

    DEDUP["Deduplicate by inode<br/>Handle symlinks gracefully"]:::dedup

    FILES["MarkdownFile array<br/>filePath, frontmatter,<br/>content, source"]:::result

    MANAGED --> LOADER
    USER_DIR --> LOADER
    PROJ_DIRS --> LOADER
    LOADER --> DEDUP --> FILES

    classDef ent fill:#4a1a1a,stroke:#dc3545,color:#e0e0e0,stroke-width:2px
    classDef user fill:#2d2d0d,stroke:#ffc107,color:#e0e0e0,stroke-width:2px
    classDef proj fill:#0d4f4f,stroke:#17a2b8,color:#e0e0e0,stroke-width:2px
    classDef loader fill:#1a2d4a,stroke:#4a9eff,color:#e0e0e0,stroke-width:2px
    classDef dedup fill:#3d2b00,stroke:#fd7e14,color:#e0e0e0,stroke-width:2px
    classDef result fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px

This same loader powers:

  • Slash commands (.claude/commands/)
  • Agents (.claude/agents/)
  • Skills (.claude/skills/)
  • Workflows (.claude/workflows/)
  • Output styles (.claude/output-styles/)

Each entry is parsed with YAML frontmatter for metadata (description, tools, etc.) and the markdown body becomes the instructions.


Key Files

  • src/utils/systemPrompt.tsbuildEffectiveSystemPrompt() logic
  • src/constants/systemPromptSections.ts — Section caching system
  • src/context.tsgetSystemContext(), getUserContext(), git status
  • src/utils/markdownConfigLoader.ts — CLAUDE.md and .claude/ directory discovery
  • src/utils/claudemd.ts — Memory file reading and concatenation
  • src/utils/settings/settings.ts — Settings merge logic

Previous: <- UI Architecture | Next: MCP Deep Dive ->