| name | claude-code-architecture-patterns |
| description | Architectural patterns and design principles from Anthropic's Claude Code agent, reverse-engineered for building production AI coding agents |
| triggers | ["how does Claude Code architecture work","show me AI agent patterns from Claude Code","implement async generator agent loop","build multi-agent orchestration system","show me Claude Code state management","implement tool execution pipeline like Claude Code","how to build production AI coding agent","explain Claude Code internals"] |
Claude Code Architecture Patterns
Skill by ara.so — Claude Code Skills collection.
This skill provides expertise in the architectural patterns, design principles, and implementation strategies extracted from Anthropic's Claude Code agent. Use these patterns to build production-grade AI coding agents with robust state management, efficient tool execution, multi-agent orchestration, and context management.
What This Provides
Claude Code from Source is a comprehensive technical analysis of Claude Code's architecture, distilled into 18 chapters covering:
- Agent Loop Architecture: AsyncGenerator-based control flow
- Tool Execution: Concurrent-safe batching and speculative execution
- Multi-Agent Orchestration: Fork agents, task coordination, swarms
- State Management: Two-tier architecture with bootstrap singleton and AppState
- Context Management: 4-layer compression, prompt cache optimization
- Memory Systems: File-based memory with LLM recall
- Performance: Token budgets, cache sharing, rendering optimization
Installation
git clone https://github.com/alejandrobalderas/claude-code-from-source.git
cd claude-code-from-source
npm install
npm run build
npm run dev
Key Architectural Patterns
1. AsyncGenerator Agent Loop
The core pattern for agent execution - yields messages during execution, returns terminal state.
async function* agentLoop(
query: string,
context: ExecutionContext
): AsyncGenerator<Message, TerminalState> {
let conversationHistory: Message[] = [];
let tokenBudget = context.maxTokens;
while (tokenBudget > 0) {
if (shouldCompress(conversationHistory)) {
conversationHistory = await compressContext(conversationHistory);
}
const stream = await streamCompletion({
messages: conversationHistory,
tools: context.availableTools,
});
let currentMessage = { role: 'assistant', content: '' };
let toolCalls: ToolCall[] = [];
for await (const chunk of stream) {
if (chunk.type === 'text') {
currentMessage.content += chunk.text;
yield { ...currentMessage, streaming: };
} (chunk. === ) {
toolCalls.(chunk.);
}
tokenBudget -= chunk.;
}
{ ...currentMessage, : };
conversationHistory.(currentMessage);
(toolCalls. > ) {
results = (toolCalls, context);
( result results) {
toolMessage = {
: ,
: (result),
};
toolMessage;
conversationHistory.(toolMessage);
}
} {
{
: ,
: currentMessage,
: context. - tokenBudget,
};
}
}
{ : , : context. };
}
2. Concurrent-Safe Tool Execution
Partition tools by safety guarantees, execute reads in parallel, serialize writes.
interface ToolCall {
id: string;
name: string;
args: Record<string, unknown>;
}
interface ToolDefinition {
name: string;
execute: (args: Record<string, unknown>) => Promise<unknown>;
readonly: boolean;
requiresConfirmation: boolean;
}
async function executeToolsConcurrent(
calls: ToolCall[],
context: ExecutionContext
): Promise<ToolResult[]> {
const readOnly = calls.filter(c =>
context.tools[c.name]?.readonly === true
);
const writeOps = calls.filter(c =>
context.tools[c.name]?.readonly !== true
);
: [] = [];
(readOnly. > ) {
readResults = .(
readOnly.( (call, context))
);
results.(...readResults);
}
( call writeOps) {
(context.[call.]?.) {
approved = (call);
(!approved) {
results.({
: call.,
: ,
: ,
});
;
}
}
result = (call, context);
results.(result);
}
results;
}
(): <> {
tool = context.[call.];
(!tool) {
{ : call., : , : };
}
{
output = tool.(call.);
{ : call., : , output };
} (error) {
{
: call.,
: ,
: error ? error. :
};
}
}
3. Speculative Tool Execution
Start read-only tools during streaming, before response completes.
async function* agentLoopWithSpeculation(
query: string,
context: ExecutionContext
): AsyncGenerator<Message, TerminalState> {
const stream = await streamCompletion({
messages: context.history,
tools: context.availableTools,
});
const toolCalls: ToolCall[] = [];
const speculativeResults = new Map<string, Promise<ToolResult>>();
for await (const chunk of stream) {
if (chunk.type === 'tool_use') {
const call = chunk.toolCall;
toolCalls.push(call);
const tool = context.tools[call.name];
if (tool?.readonly) {
speculativeResults.set(
call.id,
executeTool(call, context)
);
yield {
role: 'system',
content: ,
};
}
}
}
: [] = [];
( call toolCalls) {
(speculativeResults.(call.)) {
results.( speculativeResults.(call.)!);
} {
results.( (call, context));
}
}
{ : , results };
}
4. Fork Agents for Cache Sharing
Spawn parallel agents with byte-identical prompt prefixes to share prompt cache.
interface ForkAgentConfig {
parentContext: ConversationHistory;
tasks: string[];
sharedPrefix: Message[];
}
async function forkAgents(
config: ForkAgentConfig
): Promise<AgentResult[]> {
const sharedPrefix = config.sharedPrefix.map(msg => ({
role: msg.role,
content: msg.content,
_cacheKey: JSON.stringify({ role: msg.role, content: msg.content }),
}));
const agents = config.tasks.map(async (task) => {
const childMessages = [
...sharedPrefix,
{ role: 'user', content: task },
];
const result: AgentResult = {
task,
messages: [],
status: 'running',
};
loop = (task, {
...config.,
: childMessages,
});
( msg loop) {
result..(msg);
}
terminal = loop.();
result. = terminal..;
result;
});
.(agents);
}
results = ({
: mainContext,
: conversationHistory.(, -),
: [
,
,
,
],
});
5. Four-Layer Context Compression
Progressive compression strategies to fit within token budget.
type CompressionLevel = 'snip' | 'microcompact' | 'collapse' | 'autocompact';
interface CompressionStrategy {
level: CompressionLevel;
apply: (messages: Message[]) => Promise<Message[]>;
estimatedReduction: number;
}
const compressionStrategies: CompressionStrategy[] = [
{
level: 'snip',
estimatedReduction: 0.3,
apply: async (messages) => {
return messages.map(msg => {
if (msg.role === 'user' && msg.toolResult) {
const output = msg.toolResult.output as string;
if (output.length > 4000) {
const head = output.slice(0, 1500);
const tail = output.(-);
{
...msg,
: {
...msg.,
: ,
},
};
}
}
msg;
});
},
},
{
: ,
: ,
: (messages) => {
cutoff = messages. - ;
toCompress = messages.(, cutoff);
toKeep = messages.(cutoff);
(toCompress. === ) messages;
summary = (toCompress);
[
{ : , : },
...toKeep,
];
},
},
{
: ,
: ,
: (messages) => {
: [] = [];
( msg messages) {
last = collapsed[collapsed. - ];
(last && last. === msg.) {
last. += + msg.;
} {
collapsed.({ ...msg });
}
}
collapsed;
},
},
{
: ,
: ,
: (messages) => {
system = messages.( m. === );
recent = messages.(-);
system ? [system, ...recent] : recent;
},
},
];
(): <[]> {
current = messages;
currentTokens = (current);
( strategy compressionStrategies) {
(currentTokens <= targetTokens) ;
.();
current = strategy.(current);
currentTokens = (current);
}
current;
}
6. Two-Tier State Management
Bootstrap singleton for process-level config, AppState for runtime state.
class Bootstrap {
private static instance: Bootstrap;
readonly config: {
apiKey: string;
model: string;
maxTokens: number;
enableCache: boolean;
};
readonly tools: Map<string, ToolDefinition>;
readonly skills: Map<string, SkillDefinition>;
private constructor() {
this.config = {
apiKey: process.env.ANTHROPIC_API_KEY!,
model: process.env.MODEL || 'claude-3-5-sonnet-20241022',
maxTokens: parseInt(process.env.MAX_TOKENS || '100000', 10),
enableCache: process.env.ENABLE_CACHE !== 'false',
};
this.tools = .();
. = .();
}
(): {
(!.) {
. = ();
}
.;
}
(): <, > {
();
}
(): <, > {
();
}
}
{
: [] = [];
: ;
: {
: ;
: ;
: ;
: ;
} = {
: ,
: ,
: ,
: ,
};
betaHeaders = <>();
() {
bootstrap = .();
. = bootstrap..;
}
(: ): {
.. += usage.;
.. += usage.;
.. += usage. || ;
.. += usage. || ;
. -= (usage. + usage.);
}
(: ): {
..(feature);
}
(): {
inputCost = .. * ;
outputCost = .. * ;
cacheCost = .. * ;
inputCost + outputCost + cacheCost;
}
}
7. File-Based Memory with LLM Recall
Store memories as markdown files, use LLM to select relevant ones.
import { promises as fs } from 'fs';
import path from 'path';
interface Memory {
id: string;
type: 'fact' | 'preference' | 'context' | 'decision';
content: string;
timestamp: string;
tags: string[];
}
class MemorySystem {
private memoryDir: string;
constructor(projectRoot: string) {
this.memoryDir = path.join(projectRoot, '.claude', 'memory');
}
async initialize(): Promise<void> {
await fs.mkdir(this.memoryDir, { recursive: true });
}
async store(memory: Memory): Promise<void> {
const filename = `.md`;
filepath = path.(., filename);
content = [
,
,
,
,
,
memory.,
].();
fs.(filepath, content, );
}
(: ): <[]> {
files = fs.(.);
: [] = [];
( file files) {
(!file.()) ;
content = fs.(
path.(., file),
);
memory = .(content, file);
memories.(memory);
}
relevant = .(query, memories);
relevant;
}
(: , : ): {
lines = content.();
header = lines[];
= header.()?.[].() [];
id = filename.(, );
timestampLine = lines.( l.());
timestamp = timestampLine?.()[] || ().();
tagsLine = lines.( l.());
tags = tagsLine?.()[].() || [];
contentStart = lines.( l.()) + ;
memoryContent = lines.(contentStart).();
{ id, , : memoryContent, timestamp, tags };
}
(
: ,
: []
): <[]> {
(allMemories. === ) [];
prompt = ;
response = ({
: ,
: [{ : , : prompt }],
: ,
});
indices = response.
.()
?.( (n, ) - ) || [];
indices
.( i >= && i < allMemories.)
.( allMemories[i]);
}
}
memory = (process.());
memory.();
memory.({
: ,
: ,
: ,
: ().(),
: [, , ],
});
relevant = memory.();
Common Implementation Patterns
Multi-Agent Coordination
interface Task {
id: string;
description: string;
status: 'pending' | 'running' | 'complete' | 'failed';
assignedAgent?: string;
result?: unknown;
}
class TaskCoordinator {
private tasks: Map<string, Task> = new Map();
private agents: Map<string, AgentInstance> = new Map();
async coordinate(taskDescriptions: string[]): Promise<Map<string, unknown>> {
for (const desc of taskDescriptions) {
const task: Task = {
id: crypto.randomUUID(),
description: desc,
status: 'pending',
};
this.tasks.(task., task);
}
agentPromises = .(..()).(
.(task)
);
.(agentPromises);
results = <, >();
( [id, task] .) {
(task. === ) {
results.(id, task.);
}
}
results;
}
(: ): <> {
task. = ;
agent = (task., {
: .().,
: ,
: [],
});
: [] = [];
( msg agent) {
messages.(msg);
}
terminal = agent.();
(terminal.. === ) {
task. = ;
task. = terminal..;
} {
task. = ;
}
}
}
Token Budget Management
class TokenBudgetManager {
private budget: number;
private reserved: number = 8192;
constructor(maxTokens: number) {
this.budget = maxTokens;
}
checkAndReserve(estimatedTokens: number): boolean {
const available = this.budget - this.reserved;
return estimatedTokens <= available;
}
consume(actualTokens: number): void {
this.budget -= actualTokens;
}
escalateReservation(newReservation: number): void {
if (newReservation > this.reserved) {
this.reserved = newReservation;
}
}
getRemaining(): number {
return this.budget - this.reserved;
}
(): {
.(., .);
}
}
Reading the Book
Online
Visit claude-code-from-source.com to read all 18 chapters with rendered diagrams.
Locally
npm run dev
Chapter Organization
Each chapter follows this structure:
- Narrative flow - Technical leaders can read straight through
- Deep-dive sections - Implementers get code-level detail
- Diagrams - Mermaid diagrams for visual learners
- Apply This - Transferable patterns you can steal
Key Chapters by Use Case
Building an agent from scratch?
- Ch 1: The Architecture of an AI Agent
- Ch 5: The Agent Loop
- Ch 6: Tools — From Definition to Execution
Optimizing token usage?
- Ch 4: Talking to Claude — The API Layer
- Ch 9: Fork Agents and the Prompt Cache
- Ch 17: Performance
Multi-agent systems?
- Ch 8: Spawning Sub-Agents
- Ch 10: Tasks, Coordination, and Swarms
Adding memory/learning?
- Ch 11: Memory — Learning Across Conversations
- Ch 12: Extensibility — Skills and Hooks
Configuration
The book itself is a static site built with Next.js. Configuration is in web/:
module.exports = {
output: 'export',
images: { unoptimized: true },
basePath: process.env.BASE_PATH || '',
};
To customize the reading experience:
cd web/styles
cd web/components
Troubleshooting
"Module not found" when building
rm -rf node_modules package-lock.json
npm install
Diagrams not rendering
Mermaid diagrams render in:
- GitHub (natively)
- The web build (using mermaid.js)
- VSCode (with Markdown Preview Mermaid Support extension)
If diagrams don't show, ensure you're viewing the built site or GitHub.
Port 3000 already in use
PORT=3001 npm run dev
Environment Variables
The book itself needs no environment variables. If you're implementing the patterns:
ANTHROPIC_API_KEY=your_key_here
MODEL=claude-3-5-sonnet-20241022
MAX_TOKENS=100000
ENABLE_CACHE=true
MEMORY_DIR=./.claude/memory
MAX_CONCURRENT_AGENTS=10
AGENT_TIMEOUT_MS=300000
Best Practices
- Start with the agent loop - AsyncGenerator is the foundation
- Add tools incrementally - Begin with read-only, add writes carefully
- Use the compression ladder - Don't jump straight to aggressive compression
- Cache-optimize for parallel agents - Fork agents share prefix = 95% savings
- Track token usage - Always know your budget and consumption
- Memory is opt-in - Don't store everything; be selective
- Test with real workloads - Synthetic examples hide edge cases
Further Reading
- Ch 17: Performance - Deep dive on every optimization technique
- Ch 18: Epilogue - The 5 architectural bets that make Claude Code work
- MCP Integration (Ch 15) - Connect to external tools and services
- Remote Execution (Ch 16) - Cloud-based agent orchestration
Resources
Remember: This book contains no proprietary code. Every example is original pseudocode written to teach the patterns. Use these patterns to build your own production AI agents.