diff --git a/.gitignore b/.gitignore index 80dbe6a..1c0e109 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ assets/ # Local reference material — not for distribution .reference/ + +# PDF build artifacts +book-combined.md +claude-code-from-source.pdf diff --git a/.md-to-pdf.config.js b/.md-to-pdf.config.js new file mode 100644 index 0000000..e0c1b3e --- /dev/null +++ b/.md-to-pdf.config.js @@ -0,0 +1,32 @@ +const path = require('path'); + +const CHROME_PATHS = { + darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', + win32: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', + linux: '/usr/bin/google-chrome', +}; + +const executablePath = process.env.CHROME_PATH || CHROME_PATHS[process.platform]; + +module.exports = { + stylesheet: [path.resolve(__dirname, 'book-style.css')], + body_class: 'book', + highlight_style: 'atom-one-dark', + launch_options: { + executablePath, + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }, + pdf_options: { + format: 'Letter', + margin: { + top: '0.85in', + right: '0.85in', + bottom: '0.9in', + left: '0.9in', + }, + printBackground: true, + displayHeaderFooter: true, + headerTemplate: `
CLAUDE CODE FROM SOURCE
`, + footerTemplate: `
`, + }, +}; diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..77edce9 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +.PHONY: pdf clean-pdf help + +## Generate the book as a PDF (output: claude-code-from-source.pdf) +pdf: + @command -v md-to-pdf >/dev/null 2>&1 || { echo "Installing md-to-pdf..."; npm install -g md-to-pdf; } + @echo "Building book-combined.md..." + @node scripts/build-pdf.js + @echo "Rendering PDF..." + @md-to-pdf --config-file .md-to-pdf.config.js book-combined.md + @mv book-combined.pdf claude-code-from-source.pdf + @echo "✔ claude-code-from-source.pdf" + +## Remove generated PDF artifacts +clean-pdf: + @rm -f book-combined.md claude-code-from-source.pdf + @echo "✔ cleaned" + +## Show available targets +help: + @grep -E '^##' Makefile | sed 's/## / /' diff --git a/README.md b/README.md index ceba45c..36abfa5 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,25 @@ Every chapter has layered depth: a narrative flow for technical leaders, deep-di --- +## Generate the PDF + +You can render the entire book as a styled PDF locally: + +```bash +# Requires Node.js and Google Chrome installed +make pdf +``` + +This runs `scripts/build-pdf.js` to assemble all chapters into a single document (with cover page, table of contents, and part dividers), then uses [`md-to-pdf`](https://github.com/simonhaenisch/md-to-pdf) to render it via headless Chrome. + +The output is `claude-code-from-source.pdf` in the repo root. If Chrome is not at the default path for your OS, set `CHROME_PATH`: + +```bash +CHROME_PATH="/path/to/chrome" make pdf +``` + +--- + ## The 10 Patterns That Make It Work If you read nothing else: diff --git a/book-style.css b/book-style.css new file mode 100644 index 0000000..72f571d --- /dev/null +++ b/book-style.css @@ -0,0 +1,435 @@ +@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,400;1,8..60,600&family=JetBrains+Mono:wght@400;500&family=Inter:wght@300;400;500;600;700;800&display=swap'); + +* { + box-sizing: border-box; +} + +body { + font-family: 'Source Serif 4', 'Georgia', serif; + font-size: 10.5pt; + line-height: 1.75; + color: #1a1a2e; + background: #fff; + margin: 0; + padding: 0; +} + +/* ─── Cover Page ─────────────────────────────────────────── */ +.cover-page { + width: 100%; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + margin: 0; + padding: 0; + page-break-after: always; + overflow: hidden; +} + +.cover-img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* ─── Title Page ─────────────────────────────────────────── */ +.title-page { + width: 100%; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: #0f0f23; + page-break-after: always; +} + +.title-page-inner { + text-align: center; + padding: 3em 2em; + color: #fff; +} + +.title-page-subtitle { + font-family: 'Inter', sans-serif; + font-size: 7.5pt; + font-weight: 600; + letter-spacing: 3px; + text-transform: uppercase; + color: #e07b39; + margin: 0 0 1.5em 0; +} + +.title-page-title { + font-family: 'Inter', sans-serif; + font-size: 38pt; + font-weight: 800; + color: #ffffff; + line-height: 1.05; + margin: 0 0 0.5em 0; + letter-spacing: -1.5px; +} + +.title-page-desc { + font-family: 'Source Serif 4', serif; + font-size: 12pt; + font-weight: 300; + color: #a0a0c0; + line-height: 1.6; + margin: 0 0 2em 0; +} + +.title-page-rule { + width: 60px; + height: 3px; + background: #e07b39; + margin: 0 auto 2em auto; +} + +.title-page-year { + font-family: 'Inter', sans-serif; + font-size: 9pt; + color: #555577; + margin: 0; + letter-spacing: 2px; +} + +/* ─── Copyright Page ─────────────────────────────────────── */ +.copyright-page { + width: 100%; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + page-break-after: always; +} + +.copyright-inner { + max-width: 420px; + text-align: center; + color: #444; + font-family: 'Inter', sans-serif; + font-size: 9pt; + line-height: 1.7; +} + +.copyright-title { + font-family: 'Inter', sans-serif; + font-size: 13pt; + font-weight: 700; + color: #1a1a2e; + margin: 0 0 0.3em 0; +} + +.copyright-subtitle { + font-family: 'Inter', sans-serif; + font-size: 9pt; + color: #777; + margin: 0 0 1.5em 0; +} + +.copyright-divider { + width: 40px; + height: 2px; + background: #e07b39; + margin: 1.5em auto; +} + +.copyright-meta { + font-family: 'Inter', sans-serif; + font-size: 8pt; + color: #999; + margin: 0; + line-height: 1.8; +} + +/* ─── Table of Contents ──────────────────────────────────── */ +.toc-page { + page-break-after: always; + padding: 0; +} + +.toc-title { + font-family: 'Inter', sans-serif; + font-size: 24pt; + font-weight: 700; + color: #1a1a2e; + border-bottom: 3px solid #e07b39; + padding-bottom: 0.3em; + margin: 0 0 1.5em 0; + letter-spacing: -0.5px; +} + +.toc-body { + font-family: 'Inter', sans-serif; +} + +.toc-part { + font-family: 'Inter', sans-serif; + font-size: 8pt; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 1.5px; + color: #e07b39; + margin: 1.4em 0 0.5em 0; + padding-bottom: 0.3em; + border-bottom: 1px solid #ececf4; +} + +.toc-entry { + display: flex; + align-items: baseline; + gap: 0.8em; + padding: 0.3em 0; + border-bottom: 1px dotted #e8e8f0; + font-size: 9.5pt; +} + +.toc-ch { + font-weight: 600; + color: #555577; + min-width: 72px; + font-size: 8.5pt; + flex-shrink: 0; +} + +.toc-title-text { + color: #1a1a2e; + font-weight: 400; +} + +/* ─── Part Dividers ──────────────────────────────────────── */ +.part-divider { + width: 100%; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #0f0f23 0%, #1a1a3e 100%); + page-break-after: always; +} + +.part-divider-inner { + text-align: center; + padding: 3em 2em; + color: #fff; + max-width: 480px; +} + +.part-label { + font-family: 'Inter', sans-serif; + font-size: 8pt; + font-weight: 700; + letter-spacing: 4px; + text-transform: uppercase; + color: #e07b39; + margin: 0 0 0.8em 0; +} + +.part-title { + font-family: 'Inter', sans-serif; + font-size: 32pt; + font-weight: 800; + color: #ffffff; + line-height: 1.1; + margin: 0 0 0.6em 0; + letter-spacing: -1px; + border: none; + padding: 0; +} + +.part-tagline { + font-family: 'Source Serif 4', serif; + font-size: 11pt; + font-style: italic; + color: #8888aa; + margin: 0; + line-height: 1.6; +} + +/* ─── Page Breaks ────────────────────────────────────────── */ +.page-break { + page-break-after: always; + height: 0; + margin: 0; + padding: 0; +} + +/* ─── Chapter Content ────────────────────────────────────── */ +h1 { + font-family: 'Inter', 'Helvetica Neue', sans-serif; + font-size: 22pt; + font-weight: 700; + color: #0f0f23; + margin: 0 0 0.4em 0; + padding-bottom: 0.2em; + border-bottom: 3px solid #e07b39; + line-height: 1.2; + letter-spacing: -0.5px; +} + +h2 { + font-family: 'Inter', 'Helvetica Neue', sans-serif; + font-size: 14pt; + font-weight: 600; + color: #1a1a2e; + margin: 1.8em 0 0.5em 0; + letter-spacing: -0.3px; + border-bottom: 1px solid #e8e8f0; + padding-bottom: 0.25em; +} + +h3 { + font-family: 'Inter', 'Helvetica Neue', sans-serif; + font-size: 11.5pt; + font-weight: 600; + color: #2d2d4e; + margin: 1.4em 0 0.4em 0; + letter-spacing: -0.2px; +} + +h4 { + font-family: 'Inter', 'Helvetica Neue', sans-serif; + font-size: 9.5pt; + font-weight: 600; + color: #555577; + text-transform: uppercase; + letter-spacing: 0.8px; + margin: 1.2em 0 0.3em 0; +} + +p { + margin: 0 0 0.85em 0; + orphans: 3; + widows: 3; +} + +hr { + border: none; + border-top: 2px solid #e07b39; + margin: 2.5em auto; + width: 60px; +} + +/* Blockquotes */ +blockquote { + margin: 1.2em 0; + padding: 0.8em 1.2em; + border-left: 4px solid #e07b39; + background: #fdf6f0; + border-radius: 0 6px 6px 0; + color: #3a3a5c; + font-style: italic; +} + +blockquote p { + margin: 0; +} + +blockquote strong { + font-style: normal; + font-weight: 600; + display: block; + margin-bottom: 0.4em; + font-family: 'Inter', sans-serif; + font-size: 8.5pt; + text-transform: uppercase; + letter-spacing: 0.6px; + color: #e07b39; +} + +/* Code */ +pre { + background: #0f0f1a; + color: #e8e8f8; + padding: 1em 1.2em; + border-radius: 6px; + overflow-x: auto; + font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; + font-size: 8.5pt; + line-height: 1.55; + margin: 1em 0 1.2em 0; + border: 1px solid #2a2a3e; + page-break-inside: avoid; +} + +code { + font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; + font-size: 8.5pt; + background: #f0f0f8; + color: #c0392b; + padding: 0.1em 0.35em; + border-radius: 3px; + border: 1px solid #e0e0ec; +} + +pre code { + background: transparent; + color: inherit; + padding: 0; + border: none; + font-size: inherit; +} + +/* Tables */ +table { + width: 100%; + border-collapse: collapse; + margin: 1.2em 0; + font-size: 9.5pt; + page-break-inside: avoid; +} + +th { + background: #1a1a2e; + color: #fff; + font-family: 'Inter', sans-serif; + font-weight: 600; + padding: 0.5em 0.8em; + text-align: left; + font-size: 8.5pt; + letter-spacing: 0.3px; +} + +td { + padding: 0.45em 0.8em; + border-bottom: 1px solid #e8e8f0; + vertical-align: top; +} + +tr:nth-child(even) td { + background: #f8f8fc; +} + +/* Lists */ +ul, ol { + margin: 0.5em 0 0.85em 0; + padding-left: 1.6em; +} + +li { + margin-bottom: 0.3em; + line-height: 1.65; +} + +li > ul, li > ol { + margin: 0.2em 0; +} + +strong { + font-weight: 700; + color: #0f0f23; +} + +em { + font-style: italic; + color: #333355; +} + +/* Mermaid diagrams */ +.mermaid { + text-align: center; + margin: 1.5em 0; + page-break-inside: avoid; +} diff --git a/scripts/build-pdf.js b/scripts/build-pdf.js new file mode 100644 index 0000000..e730121 --- /dev/null +++ b/scripts/build-pdf.js @@ -0,0 +1,154 @@ +#!/usr/bin/env node +/** + * Combines all 18 book chapters into a single markdown file + * with cover, title page, copyright, table of contents, and part dividers. + * Output: book-combined.md (gitignored — run `make pdf` to regenerate) + */ + +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '..'); + +const CHAPTERS = [ + 'ch01-architecture', + 'ch02-bootstrap', + 'ch03-state', + 'ch04-api-layer', + 'ch05-agent-loop', + 'ch06-tools', + 'ch07-concurrency', + 'ch08-sub-agents', + 'ch09-fork-agents', + 'ch10-coordination', + 'ch11-memory', + 'ch12-extensibility', + 'ch13-terminal-ui', + 'ch14-input-interaction', + 'ch15-mcp', + 'ch16-remote', + 'ch17-performance', + 'ch18-epilogue', +]; + +const PARTS = { + 1: { num: 'Part I', name: 'Foundations', tagline: 'Before the agent can think, the process must exist.' }, + 5: { num: 'Part II', name: 'The Core Loop', tagline: 'The heartbeat of the agent: stream, act, observe, repeat.' }, + 8: { num: 'Part III', name: 'Multi-Agent Orchestration', tagline: 'One agent is powerful. Many agents working together are transformative.' }, + 11: { num: 'Part IV', name: 'Persistence and Intelligence', tagline: 'An agent without memory makes the same mistakes forever.' }, + 13: { num: 'Part V', name: 'The Interface', tagline: 'Everything the user sees passes through this layer.' }, + 15: { num: 'Part VI', name: 'Connectivity', tagline: 'The agent reaches beyond localhost.' }, + 17: { num: 'Part VII', name: 'Performance Engineering', tagline: "Making it all fast enough that humans don't notice the machinery." }, +}; + +const TOC = [ + [1, 'The Architecture of an AI Agent', 'The 6 key abstractions, data flow, permission system'], + [2, 'Starting Fast — The Bootstrap Pipeline', '5-phase init, module-level I/O parallelism, trust boundary'], + [3, 'State — The Two-Tier Architecture', 'Bootstrap singleton, AppState store, sticky latches, cost tracking'], + [4, 'Talking to Claude — The API Layer', 'Multi-provider client, prompt cache, streaming, error recovery'], + [5, 'The Agent Loop', '4-layer compression, error recovery, token budgets'], + [6, 'Tools — From Definition to Execution', 'Tool interface, 14-step pipeline, permission system'], + [7, 'Concurrent Tool Execution', 'Partition algorithm, streaming executor, speculative execution'], + [8, 'Spawning Sub-Agents', 'AgentTool, 15-step runAgent lifecycle, built-in agent types'], + [9, 'Fork Agents and the Prompt Cache', 'Byte-identical prefix trick, cache sharing, cost optimization'], + [10, 'Tasks, Coordination, and Swarms', 'Task state machine, coordinator mode, swarm messaging'], + [11, 'Memory — Learning Across Conversations', 'File-based memory, 4-type taxonomy, LLM recall, staleness'], + [12, 'Extensibility — Skills and Hooks', 'Two-phase skill loading, lifecycle hooks, snapshot security'], + [13, 'The Terminal UI', 'Custom Ink fork, rendering pipeline, double-buffer, pools'], + [14, 'Input and Interaction', 'Key parsing, keybindings, chord support, vim mode'], + [15, 'MCP — The Universal Tool Protocol', '8 transports, OAuth for MCP, tool wrapping'], + [16, 'Remote Control and Cloud Execution', 'Bridge v1/v2, CCR, upstream proxy'], + [17, 'Performance — Every Millisecond and Token Counts', 'Startup, context window, prompt cache, rendering, search'], + [18, 'Epilogue — What We Learned', 'The 5 architectural bets, what transfers, where agents are heading'], +]; + +const TOC_PARTS = [ + { name: 'Part I — Foundations', chapters: [1, 2, 3, 4] }, + { name: 'Part II — The Core Loop', chapters: [5, 6, 7] }, + { name: 'Part III — Multi-Agent Orchestration', chapters: [8, 9, 10] }, + { name: 'Part IV — Persistence and Intelligence',chapters: [11, 12] }, + { name: 'Part V — The Interface', chapters: [13, 14] }, + { name: 'Part VI — Connectivity', chapters: [15, 16] }, + { name: 'Part VII — Performance Engineering', chapters: [17, 18] }, +]; + +function block(html) { + return html + '\n'; +} + +const lines = []; + +// Cover page +lines.push(block(`
Claude Code from Source
`)); +lines.push(block(`
`)); + +// Title page +lines.push(block(`
+
+

AN IN-DEPTH TECHNICAL ANALYSIS

+

Claude Code
from Source

+

Architecture, Patterns & Internals of
Anthropic's AI Coding Agent

+
+

2024

+
+
`)); +lines.push(block(`
`)); + +// Copyright page +lines.push(block(``)); +lines.push(block(`
`)); + +// Table of contents +const tocEntries = TOC_PARTS.map(part => { + const entries = part.chapters + .map(n => { + const [, title] = TOC.find(([num]) => num === n); + return `
Chapter ${n}${title}
`; + }) + .join('\n '); + return `
${part.name}
\n ${entries}`; +}).join('\n\n '); + +lines.push(block(`
+

Contents

+
+ ${tocEntries} +
+
`)); +lines.push(block(`
`)); + +// Chapters with part dividers +CHAPTERS.forEach((slug, i) => { + const chNum = i + 1; + if (PARTS[chNum]) { + const { num, name, tagline } = PARTS[chNum]; + lines.push(block(`
+
+

${num}

+

${name}

+

${tagline}

+
+
`)); + lines.push(block(`
`)); + } + + const content = fs.readFileSync(path.join(ROOT, 'book', `${slug}.md`), 'utf8'); + lines.push(content + '\n'); + lines.push(block(`
`)); +}); + +const output = lines.join('\n'); +fs.writeFileSync(path.join(ROOT, 'book-combined.md'), output); +console.log(`✔ book-combined.md written (${output.split('\n').length} lines)`);