跳转到内容

Configuration System

此内容尚不支持你的语言。

Claude Code’s configuration system is a multi-layered architecture where settings flow from 5+ sources with well-defined precedence. Understanding this system is crucial because configuration affects everything: available tools, permission rules, model selection, and behavior.

Settings are loaded from multiple sources, with later sources overriding earlier ones:

graph TB
subgraph "Lowest Priority"
A["1. User Settings<br/>~/.claude/settings.json"]
end
subgraph "Medium Priority"
B["2. Project Settings<br/>.claude/settings.json"]
C["3. Local Settings<br/>.claude/settings.local.json"]
end
subgraph "High Priority"
D["4. Flag Settings<br/>--settings CLI flag"]
end
subgraph "Highest Priority"
E["5. Policy Settings<br/>managed-settings.json / remote API"]
end
A --> B --> C --> D --> E
style E fill:#f96,stroke:#333,stroke-width:2px

From src/utils/settings/constants.ts:

src/utils/settings/constants.ts
export const SETTING_SOURCES = [
'userSettings', // Global: ~/.claude/settings.json
'projectSettings', // Shared: .claude/settings.json (checked into VCS)
'localSettings', // Private: .claude/settings.local.json (gitignored)
'flagSettings', // CLI: --settings flag
'policySettings', // Enterprise: managed-settings.json or remote API
] as const
SourceLocationVCSUse Case
userSettings~/.claude/settings.jsonN/A (home dir)Personal global preferences
projectSettings.claude/settings.jsonCommittedTeam-shared project rules
localSettings.claude/settings.local.jsonGitignoredPrivate project overrides
flagSettings--settings CLI argN/ACI/CD, scripting
policySettings/etc/claude-code/managed-settings.jsonN/AEnterprise IT management

The override logic uses lodash’s mergeWith with a custom merge strategy:

src/utils/settings/settings.ts
function settingsMergeCustomizer(objValue: unknown, srcValue: unknown): unknown {
// Arrays are replaced, not concatenated
if (Array.isArray(srcValue)) {
return srcValue
}
// Objects are deep-merged
return undefined // fall through to default deep merge
}

Key behavior: Arrays are replaced (not merged) by higher-priority sources. This means a project can completely override user-level permission rules.

Policy settings have the highest priority and support two mechanisms:

src/utils/settings/settings.ts
export function loadManagedFileSettings(): {
settings: SettingsJson | null
errors: ValidationError[]
} {
// Base: /etc/claude-code/managed-settings.json (or platform equivalent)
const { settings } = parseSettingsFile(getManagedSettingsFilePath())
// Drop-ins: /etc/claude-code/managed-settings.d/*.json
// Sorted alphabetically, higher precedence
const entries = readdirSync(dropInDir)
.filter(d => d.name.endsWith('.json') && !d.name.startsWith('.'))
.sort()
// Merge each on top
}

The drop-in pattern mirrors systemd conventions — teams can ship independent policy fragments (e.g., 10-otel.json, 20-security.json) without editing a single file.

For organizations using Anthropic’s management API, settings are fetched remotely and cached:

src/services/remoteManagedSettings/index.ts
export function initializeRemoteManagedSettingsLoadingPromise(): void
export function waitForRemoteManagedSettingsToLoad(): Promise<void>

On macOS and Windows, Claude Code also reads from platform-native MDM:

src/utils/settings/mdm/rawRead.ts
export function startMdmRawRead(): void {
// macOS: plutil to read com.anthropic.claude-code preferences
// Windows: registry query for HKCU\Software\Anthropic\Claude Code
}

MDM reads are fired at import time (before init) to run in parallel with module loading.

All settings are validated against a Zod schema:

src/utils/settings/types.ts
import { z } from 'zod/v4'
export const SettingsSchema = z.object({
// Permission rules
permissions: z.object({
allow: z.array(PermissionRuleSchema).optional(),
deny: z.array(PermissionRuleSchema).optional(),
ask: z.array(PermissionRuleSchema).optional(),
}).optional(),
// Environment variables
env: z.record(z.string()).optional(),
// MCP server configuration
mcpServers: z.record(McpServerConfigSchema).optional(),
// Hooks
hooks: z.record(HookConfigSchema).optional(),
// Model preferences
model: z.string().optional(),
// ... many more fields
}).passthrough()
export type SettingsJson = z.infer<typeof SettingsSchema>

Invalid settings are reported but don’t crash the application:

src/utils/settings/validation.ts
export type ValidationError = {
source: SettingSource
field: string
message: string
}
// Settings loading always returns both valid settings and errors
export type SettingsWithErrors = {
settings: SettingsJson
errors: ValidationError[]
}

Beyond JSON settings, Claude Code loads markdown instruction files — the CLAUDE.md memory system:

// src/utils/claudemd.ts — File loading hierarchy (from comments)
// 1. Managed memory → /etc/claude-code/CLAUDE.md
// 2. User memory → ~/.claude/CLAUDE.md
// 3. Project memory → CLAUDE.md, .claude/CLAUDE.md, .claude/rules/*.md
// 4. Local memory → CLAUDE.local.md

Files are loaded in reverse priority order — the last-loaded file has the highest priority:

graph TB
A["1. Managed Memory<br/>/etc/claude-code/CLAUDE.md<br/>(Lowest priority)"]
B["2. User Memory<br/>~/.claude/CLAUDE.md"]
C["3. Project Memory - Git Root<br/>CLAUDE.md in repo root"]
D["4. Project Memory - Current Dir<br/>CLAUDE.md in cwd"]
E["5. Local Memory<br/>CLAUDE.local.md<br/>(Highest priority)"]
A --> B --> C --> D --> E
style E fill:#9f6,stroke:#333,stroke-width:2px

Project memory files are discovered by traversing from the current directory up to the repository root:

// src/utils/claudemd.ts (conceptual)
// For each directory from cwd up to git root:
// 1. Check CLAUDE.md
// 2. Check .claude/CLAUDE.md
// 3. Check .claude/rules/*.md (all .md files)
// Files closer to cwd have higher priority

Memory files support including other files:

<!-- In CLAUDE.md -->
@./coding-standards.md
@./api-conventions.md
@~/personal-preferences.md
src/utils/claudemd.ts
const MEMORY_INSTRUCTION_PROMPT =
'Codebase and user instructions are shown below. Be sure to adhere to these instructions. ' +
'IMPORTANT: These instructions OVERRIDE any default behavior and you MUST follow them exactly as written.'
// Recommended max character count for a memory file
export const MAX_MEMORY_CHARACTER_COUNT = 40000

Rules for @include:

  • @path or @./path — Relative to the including file
  • @~/path — Relative to home directory
  • @/path — Absolute path
  • Only works in leaf text nodes (not inside code blocks)
  • Circular references are prevented
  • Non-existent files are silently ignored
  • Only text file extensions are allowed (.md, .txt, .ts, .py, etc.)

Settings are aggressively cached to avoid repeated filesystem reads:

src/utils/settings/settingsCache.ts
export function getCachedSettingsForSource(source: SettingSource): SettingsJson | null
export function setCachedSettingsForSource(source: SettingSource, settings: SettingsJson): void
export function resetSettingsCache(): void

Cache invalidation happens:

  • On resetSettingsCache() calls (after worktree creation, plugin reload)
  • On settings file changes detected by the settings change detector
  • On remote settings refresh
src/utils/settings/changeDetector.ts
export function settingsChangeDetector(): void {
// Watches settings files for changes
// Triggers cache invalidation and hook re-capture
}

Settings can set environment variables:

{
"env": {
"ANTHROPIC_MODEL": "claude-sonnet-4-20250514",
"NODE_OPTIONS": "--max-old-space-size=4096"
}
}

Environment variables are applied in two phases:

  1. Safe phase (before trust dialog) — Only non-sensitive variables:
src/utils/managedEnv.ts
export function applySafeConfigEnvironmentVariables(): void
// Applied during init(), before any trust dialog
  1. Full phase (after trust) — All variables including sensitive ones:
export function applyConfigEnvironmentVariables(): void
// Applied after trust is established

The --setting-sources CLI flag allows restricting which sources are loaded:

src/utils/settings/constants.ts
export function parseSettingSourcesFlag(flag: string): SettingSource[] {
// "user,project" → ['userSettings', 'projectSettings']
// Valid names: user, project, local
}
export function getEnabledSettingSources(): SettingSource[] {
const allowed = getAllowedSettingSources()
// Always include policy and flag settings (cannot be disabled)
const result = new Set<SettingSource>(allowed)
result.add('policySettings')
result.add('flagSettings')
return Array.from(result)
}

Critical rule: policySettings and flagSettings are always included — they cannot be disabled by any flag. This ensures enterprise policy and explicit CLI flags always take effect.

graph LR
S["settings.ts"] --> P["permissionSetup.ts"]
P --> TPC["ToolPermissionContext"]
TPC --> T["tools.ts → getTools()"]
T --> POOL["Tool pool for session"]
S --> E["managedEnv.ts"]
E --> ENV["process.env"]
ENV --> TOOLS["Tool implementations"]
graph LR
CM["claudemd.ts"] --> QC["queryContext.ts"]
QC --> SP["System prompt assembly"]
SP --> C["claude.ts"]
C --> API["Anthropic API"]