┌─────────────────────────────────────────────────────────────┐
│ SYSTEM PROMPT (constants/prompts.ts) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ CONTEXT LAYER (context.ts) — memoized until /compact │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ CONFIGURATION (utils/config.ts, env.ts) │ │ │
│ │ │ ┌───────────────────────────────────────────┐ │ │ │
│ │ │ │ MEMORY (~/.claude/memory/) │ │ │ │
│ │ │ │ ┌─────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ MESSAGES (query.ts, messages.tsx) │ │ │ │ │
│ │ │ │ │ ┌───────────────────────────────┐ │ │ │ │ │
│ │ │ │ │ │ PERSISTENCE & RESUME │ │ │ │ │ │
│ │ │ │ │ │ (log.ts, ResumeConversation) │ │ │ │ │ │
│ │ │ │ │ └───────────────────────────────┘ │ │ │ │ │
│ │ │ │ └─────────────────────────────────────┘ │ │ │ │
│ │ │ └───────────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
getSystemPrompt() returns string[] (3 elements), later converted to TextBlockParam[] in querySonnetWithPromptCaching():
| Segment / Function | Content |
|---|---|
getCLISyspromptPrefix() |
Identity, capabilities, tool usage rules |
getEnvInfo() |
cwd, platform, model, date, git repo status |
| Security warnings | Injection defense, permission boundaries |
| Memory instructions | CLAUDE.md conventions |
getContext() — memoized; gathered once at startup, cleared by /compact.
Returns a dict; each key wrapped as XML in the system prompt via formatSystemPromptWithContext() (services/claude.ts):
<context name="codeStyle">...merged CLAUDE.md content...</context>
<context name="gitStatus">...branch, staging, commits...</context>| Key | Source | Notes |
|---|---|---|
codeStyle |
getCodeStyle() (utils/style.ts) — walks cwd to root |
Root-first merge (deepest appears last) |
gitStatus |
Branch, staging, recent commits, main branch, author's log | Status truncated to 200 lines |
directoryStructure |
File tree snapshot | Taken once at startup |
readme |
Project README.md | Raw content |
claudeFiles |
**/*/CLAUDE.md in subdirectories |
Additional per-dir instructions |
| Custom context | projectConfig.context |
Set via claude context set k v |
/ ← /CLAUDE.md (if exists) ← appears FIRST in output
/home/user/ ← /home/user/CLAUDE.md
/home/user/project/ ← /home/user/project/CLAUDE.md ← appears LAST (deepest)
Collected deepest-first, then styles.reverse() before joining — root-first in final output.
| Scope | Path | Schema |
|---|---|---|
| Global | ~/.claude.json (default); ~/.claude/config.json if CLAUDE_CONFIG_DIR set |
GlobalConfig |
| Project | Inside global file → projects[absolutePath] |
ProjectConfig |
| Memory | ~/.claude/memory/ |
Flat files |
- GlobalConfig:
{ theme, primaryApiKey, mcpServers, onboarding, numStartups, projects: { [path]: ProjectConfig } } - ProjectConfig:
{ allowedTools, context: {k:v}, mcpServers, dontCrawlDirectory, mcpContextUris } - MCP Server Precedence:
Project config > .mcprc file > Global config
~/.claude/memory/
MEMORY.md ← index file (<200 lines)
user_role.md ← individual memory files with YAML frontmatter
feedback_testing.md
project_auth.md
...
System prompt instructs Claude to manage memory via Read/Write/Edit tools targeting ~/.claude/memory/. Each file has frontmatter:
---
name: memory name
description: one-line description
type: user | feedback | project | reference
---| Type | Purpose |
|---|---|
user |
Role, preferences, knowledge |
feedback |
Corrections and confirmed approaches |
project |
Ongoing work, goals, decisions |
reference |
Pointers to external resources |
| Type | Role | Content |
|---|---|---|
UserMessage |
user |
Text input or tool_result blocks |
AssistantMessage |
assistant |
Text + tool_use blocks + cost/duration |
ProgressMessage |
(internal) | Filtered before API |
- Filter out
ProgressMessage - Merge consecutive
tool_resultmessages into oneusermessage
Adds cache_control: { type: "ephemeral" } to last 2 messages (index > messages.length - 3). Reduces re-tokenization costs in recursive tool-use loops.
~/.cache/claude-cli/{project-dir-hash}/messages/
2025-01-27T01-31-35-104Z.json ← initial conversation
2025-01-27T01-31-35-104Z-1.json ← fork 1
2025-01-27T01-31-35-104Z-1-sidechain-1.json ← sidechain off fork 1
ISO date filenames via dateToFilename(). Each file is an array of message objects with per-message metadata: { cwd, userType, sessionId, timestamp, version }.
At read-time, loadLogList() computes: date, forkNumber, firstPrompt, messageCount.
useLogMessages() hook auto-saves after each message array mutation.
/resume → loadLogList() → user selects → deserializeMessages() (utils/conversationRecovery.ts) → new fork number → REPL
/compact → send all messages + "summarize" prompt → API
→ reset token counts to 0, clear messages
→ inject summary as new starting context
→ clear memoized caches (getContext, getCodeStyle)
Restarts the conversation with compressed history; cache clearing forces fresh context on next query.
Public entry: querySonnet() wraps internal querySonnetWithPromptCaching().
| Field | Value |
|---|---|
system |
TextBlockParam[] with cache_control on segments |
messages |
Normalized, cache breakpoints on tail |
tools |
Generated from zodToJsonSchema |
thinking |
Optional budget_tokens (ANT-only, triggered by "think hard") |
temperature |
1.0 |
Retry logic: exponential backoff (500ms base, 32s max), up to 10 retries on 408/429/5xx.
Singleton shell per session via getInstance(). Spawns login shell (-l) with GIT_EDITOR: 'true' to prevent interactive editors. File-based IPC (/tmp/claude-*). Queue-based serial execution (concurrency=1). Tracks cwd and exit codes.
CLI Startup
│
▼
getSystemPrompt() ──────────────────────────────┐
│ │
getContext() [MEMOIZED] │
│ walks CLAUDE.md, git, dir tree, readme │
▼ ▼
┌─────────────────────────────────────────────────────┐
│ SYSTEM: [core instructions | env info | security] │
│ + <context name="...">...</context> per key │
└───────────────────────────┬─────────────────────────┘
│
User Input ─── processUserInput() ──▶ UserMessage
│
▼
┌───────────────────────┐
│ query() │
│ formatSystemPrompt │
│ + messages + tools │
└─────────┬─────────────┘
│
▼
querySonnet() → API call with prompt caching
│
▼
AssistantMessage
│
┌─────────────┴──────────────┐
│ │
has tool_use? no tool_use
│ │
▼ ▼
Execute tool(s) Render final response
(concurrent if read-only, │
serial otherwise, max 10) ▼
│ useLogMessages() → disk
▼
tool_result → UserMessage
│
└──────▶ recurse query()