Timeline and the folklore-to-fact pipeline
Every few years, the industry relives the same story with different protagonists: a production artifact ships with more truth than the marketing site admits. In early 2026, discussion coalesced around Anthropic’s Claude Code CLI—the official terminal experience for Claude—and the claim that its NPM distribution included source maps detailed enough to reconstruct a meaningful slice of the TypeScript tree.
Social posts (example thread on X) acted as the ignition event: developers noticed that the “black box” was, in a sense, a glass box with minification as frosted glass. Artefacts and mirrors then appeared in community hosting; independent analysts validated file counts, module boundaries, and the sheer volume of recovered identifiers—on the order of nearly two thousand TypeScript files and hundreds of directories in circulated reconstructions.
The important correction: this was not necessarily a “git leak” in the breach sense. It was closer to a supply-chain transparency accident—maps turning bundled output back into human-authored structure for anyone with a parser and patience.
At Teamjaaf we archived the architectural narrative, not the drama: our repository github.com/teamjaf/anthropic-ai-claude-code pairs a comprehensive README with a folder-level tour so security architects can reason about capabilities without chasing ephemeral file dumps.
Recovery path: from npm tarball to TypeScript forest
Community reconstructions of @anthropic-ai/claude-code describe on the order of ~1,884 TypeScript sources—about ~1,332 .ts and ~552 .tsx—spread across roughly 300 directories.
That scale matters: this is not a few stray strings in a bundle; it is a navigable product tree suitable for architectural study, competitive reverse engineering, and security review.
Per the Chrome DevTools source map model, published *.js.map files can carry original filenames, directory structure, and—when authors opt in—sourcesContent embedding full original source. NPM tarballs are public by default; anyone who runs npm pack or installs the package receives the same artifacts CI published.
How to preserve evidence (forensics & compliance)
Adapted from our GitHub README—useful for legal, DFIR, or supply-chain teams auditing what the registry actually served on a given date.
| Goal | What to do |
|---|---|
| Keep a git mirror | git clone an architectural repo (or fork), tag locally: e.g. git tag archive-2026-03-31 && git push origin archive-2026-03-31. |
| Freeze the npm artifact | Run npm pack @anthropic-ai/claude-code@<version>—the .tgz is byte-for-byte what the registry distributed (including any bundled maps). |
| Offline / air-gap | Copy the .tgz or a git clone --mirror to removable media or a vault; verify with sha256sum / shasum. |
| If you publish JS | Prefer hidden-source-map, strip *.map from published packages, or omit sourcesContent. Never assume minification alone hides logic. |
Legal / ethical: Anthropic owns Claude and Claude Code. Third-party trees are educational architecture reference. Redistribution may implicate license or copyright—prefer local study, security review, and interoperability research that respects vendor terms.
How source maps accidentally ship an organization chart
Modern bundles transform code for speed and size: tree-shaking, renaming, dead-branch elimination, sometimes multi-target builds.
Source maps exist so engineers can debug production stacks without psychic powers. A *.js.map file typically encodes:
- Mappings between generated columns/lines and original source spans.
- Sources list—often full virtual paths like
../src/services/api/claude.ts. - SourcesContent (when inlined), which can resurrect entire source files verbatim inside JSON.
When sourcesContent is present, the map is not just a compass; it is a compressed archive of intent.
Paired with npm’s world-readable registry, that becomes a reproducible recipe for adversaries, competitive cloners, and yes—serious researchers—to profile internal module naming conventions, feature-flag skeletons, and integration seams (MCP, OAuth flows, GrowthBook hooks).
Why vendors struggle to unwind the mistake
Fixes are asymmetric. Removing maps from future releases is easy; revoking history on the public registry is not. Consumers may pin older versions for stability. Mirrors exist within minutes. Legal recourse is narrow when the artifact was voluntarily published under package distribution norms. The durable lesson is build hygiene as a security control, not as a dev convenience afterthought.
Why this “leak” happened (root causes, not excuses)
Calling it a “leak” is catchy; the mechanics are closer to an intentional developer feature colliding with public package distribution. Several reinforcing causes show up in almost every incident of this shape:
- Debuggability is shipped by default. Bundlers emit source maps when configured for
source-mapor inline maps. Teams want stack traces that point to TypeScript; CI copiesdist/**into the package without a second “red-team” pass. sourcesContentremoves the need for separate.tsfiles on disk—so the map becomes a self-contained archive. One file innode_modulescan unpack an entire module graph.- NPM is a world-readable artifact store. There is no authentication wall for metadata or tarballs.
npm pack, mirrors, and registries replicate faster than policy catches up. - Org silos between “build” and “release.” Security review often covers secrets in env and SBOM licenses—but not “is our whole TypeScript tree stapled to the JS?” Release engineering assumes minification equals obscurity.
- Speed of iteration on CLIs. Fast-moving agent products prioritize feature velocity. Maps accelerate internal QA and customer support (“send us the stack trace”). The cost is structural transparency.
- Dead-code elimination is not confidentiality. Bun/webpack may strip branches at build time, but source maps recover authoring-time files—including feature-gated and
USER_TYPE === 'ant'code paths that never run in public binaries. Attackers read the map; defenders read the map; everyone gets the full manuscript, not the edited performance.
None of these require malice or a breached git host. They require ordinary incentives + one unchecked publish step.
How publishers prevent a repeat (SDLC controls that actually stick)
If you ship JavaScript to a public registry—or any customer-facing channel—the goal is debuggability for you, opacity for everyone else. Practical patterns:
- Use
hidden-source-map(or equivalent) in production bundles so maps never point browsers or consumers at filenames, while you retain maps in a private crash-symbol bucket (Sentry, internal artifact storage). - Post-build strip: a CI step that deletes
*.mapfrom the tarball, or runsnpm publishfrom afiles-whitelisted directory that excludes maps. - Omit
sourcesContent: configure the bundler to emit mappings without inlining sources—raises the bar from “unpack” to “infer.” Pair with private source hosting for crash reports. - Dual artifacts:
my-cli-lite.tgz(public, no maps) vsmy-cli-debug.tgz(private registry, maps on) for enterprise support contracts. - Preflight with
npm pack --dry-runand a policy test: fail CI if any.mapor disallowed extension entersfiles. - Symbol server discipline: treat maps like signing keys—scoped storage, retention policies, audit logging.
- Assume registry immutability: once published, assume mirrors forever; prevention is pre-publish, not takedown PR.
For buyers: add “no sourcesContent in shipped artifacts” to vendor questionnaires alongside SOC2 and data residency. It is a cheap check with outsized risk reduction.
Project overview: what the recovered tree says about the product
Claude Code is Anthropic’s official CLI for Claude—framed in recovered sources as a full-stack terminal application: TypeScript (strict), React 18, and a custom fork of Ink (React renderer for TTYs), Yoga for flex layout, MCP for tool extensibility, and @anthropic-ai/sdk for the model API.
| Characteristic | Detail (from README / recovered tree) |
|---|---|
| Language | TypeScript (strict) |
| UI | React 18 + custom Ink fork |
| Runtime | Node.js 18+ (bundling described around Bun) |
| Bundler | Bun with compile-time feature() DCE |
| Layout | Yoga (terminal flexbox) |
| API | @anthropic-ai/sdk |
| Tools protocol | MCP + built-in tool pool |
| State | Zustand-style store via React context |
| Lint / format | Biome |
Top-level /src directory sketch (abbrev.)
Full tree in the GitHub README.
assistant/ bootstrap/ bridge/ buddy/ cli/ commands/ components/ constants/
context/ coordinator/ entrypoints/ hooks/ ink/ keybindings/ memdir/
migrations/ moreright/ native-ts/ outputStyles/ plugins/ query/ remote/
schemas/ screens/ server/ services/ skills/ state/ tasks/ tools/ types/
upstreamproxy/ utils/ vim/ voice/
main.tsx query.ts QueryEngine.ts Tool.ts tools.ts commands.ts ...
The runtime shape: not a script—an operating system skin
If you expected a thin wrapper around curl and a REPL, the recovered architecture says otherwise.
Claude Code presents as a full terminal application: React 18 rendering through a custom fork of Ink (React for CLIs), Yoga for layout, Zustand-style stores holding conversation state, file overlays, permission context, and MCP connections.
That choice explains why the codebase is large: terminals are hostile UIs—resize storms, partial redraws, focus modes, modal stacks, diff viewers, syntax highlighting—problems web developers already solved, now ported to ANSI-world with obsessive care.
Diagram: conceptual layering derived from the public architectural README. It is a mental model for threat modeling, not a literal call graph.
Core entry points (where the story starts)
Recovered entrypoints/cli.tsx is the true bootstrap: it handles fast paths before heavy imports—for example --version with minimal module load, --dump-system-prompt (debug / regression tooling when compiled in), Chrome native-host flags for MCP bridges, then a dynamic import of main.tsx.
main.tsx (~4.7k lines in README estimates) sequences MDM policy prefetch, macOS keychain warm-up, Bun DCE feature() gates, command/skill/plugin loading, memoized git and CLAUDE.md context, permission initialization, GrowthBook, MCP tool assembly, and finally the Ink root render.
setup.ts validates Node ≥18, starts the UDS messaging server on Unix, captures “teammate” snapshots for swarm mode, restores terminal state for common macOS terminals, snapshots hooks, optional git worktrees, prefetches plugins—and logs session telemetry such as tengu_started / tengu_exit.
For automation without Ink, entrypoints/sdk/ exposes a headless path when CLAUDE_CODE_ENTRYPOINT=agent_sdk: structured JSON I/O instead of terminal rendering—useful when threat-modeling headless agents in CI.
Query pipeline and the tool ocean
The central loop is familiar to anyone building agents, but unusually hardened for product-scale deployment.
User input becomes structured messages; attachments arrive from project memory (CLAUDE.md discovery), optional images, git context.
Normalization strips ANSI noise, deduplicates attachments, enforces budgets—detail work that separates demos from daily drivers.
QueryEngine.ts (as described in mirrored analysis) owns the inner loop: stream parsing, tool-use detection, parallel independent tool execution, retry classification, and cost attribution.
Think of it as a miniature job scheduler whose tasks are model-generated function calls, each gated by policy.
Tooling surface area (why attackers smile)
Forty-plus tools are not forty gimmicks—they are forty attack primitives if permissioning fails: bash, file write/read, glob, grep, LSP integration, web fetch/search, notebook edit, PowerShell on Windows, task orchestration, plan-mode transitions, worktrees, MCP bridges, and coordinator-style agent spawning behind feature flags. The README-style inventory is a gift to defenders: it is the exact checklist you use when drafting least-capability profiles for enterprise rollouts.
UserMessage → normalizeMessagesForAPI()
→ Claude API (streaming)
→ tool_use blocks
→ permission check + overlay snapshot
→ execute (possibly parallel)
→ tool_result → loop until quiet
context.ts exposes getSystemContext() (git status, branch, commits, cache-breakers) and getUserContext() (CLAUDE.md, date)—both memoized per session until settings change.
cost-tracker.ts tracks input/output tokens, cache creation vs cache read, USD estimates, and API duration.
Tool type system (contract the model sees)
interface Tool {
name: string
description: string
inputSchema: JSONSchema7 // Zod-validated
isEnabled(context): boolean
execute(input, context): Promise<ToolResult>
}
tools.ts assembles getAllBaseTools(), filters via getTools(permissionCtx), merges MCP tools in assembleToolPool().
Modes: default; CLAUDE_CODE_SIMPLE (Bash + FileRead + FileEdit only); REPL sandbox; coordinator mode (adds AgentTool, strips direct file tools per README).
Full built-in tool inventory (40+)
File ops, AI/search, UX, workflow, tasks, MCP, code execution—straight from the architectural map.
File operations
| Tool | Path | Role |
|---|---|---|
BashTool | tools/bash/ | Shell with streaming, timeouts, output capture |
FileReadTool | tools/fileRead/ | Read with offsets for large files |
FileEditTool | tools/fileEdit/ | Unified diff application |
FileWriteTool | tools/fileWrite/ | Create/overwrite |
GlobTool | tools/glob/ | Pattern match |
GrepTool | tools/grep/ | Regex search |
NotebookEditTool | tools/notebookEdit/ | Jupyter cells |
LSPTool | tools/lsp/ | Hover, definitions, diagnostics |
AI & search
| Tool | Path | Role |
|---|---|---|
WebSearchTool | tools/webSearch/ | Google Custom Search |
WebFetchTool | tools/webFetch/ | HTTP / browser-like fetch |
AgentTool | tools/agent/ | Sub-agents (coordinator) |
SkillTool | tools/skill/ | Slash skills as sub-prompts |
BriefTool | tools/brief/ | KAIROS_BRIEF summaries |
User interaction & tasks
| Tool | Path | Role |
|---|---|---|
AskUserQuestionTool | tools/askUser/ | Interactive prompts |
SendMessageTool | tools/sendMessage/ | Swarm / teammate messages |
TaskOutputTool | tools/taskOutput/ | Stream to task system |
Plan/workflow: EnterPlanModeTool, ExitPlanModeTool, EnterWorktreeTool, ExitWorktreeTool, ScheduleCronTool, ConfigTool | ||
Tasks: TaskCreateTool, TaskUpdateTool, TaskListTool, TaskGetTool, TaskStopTool | ||
MCP: MCPTool, ListMcpResourcesTool | ||
Execution: REPLTool (internal ant), PowerShellTool | ||
Command system: 90+ slash commands & skills
commands.ts merges built-ins, skills under skills/, user .claude/skills/, and plugin commands from .claude/plugins/, with file-watcher based discovery.
Filters include USER_TYPE=ant, feature flags, provider (Claude.ai vs console), and allowlists like REMOTE_SAFE_COMMANDS / BRIDGE_SAFE_COMMANDS.
Representative slash commands (from README)
| Cluster | Examples |
|---|---|
| Dev workflow | /review /diff /commit /branch /pr_comments /teleport |
| AI modes | /plan /agents /skills /compact |
| Config | /config /keybindings /model /effort /theme |
| Utility | /help /cost /clear /exit /copy /btw /session |
Skills are markdown with YAML frontmatter (description, whenToUse); the model can invoke them via SkillTool without extra UI steps.
State management, speculation, and persistence
AppStateStore anchors messages, tools, MCP connections, permissionContext, fileStateCache, settings, speculationState, session id, model id, and cost tracker.
The speculation subsystem tracks predicted messages, speculative file overlays, completion boundaries, and pipelining—supporting UI that feels instant while the model is still streaming.
On disk, history.ts writes ~/.claude_code/history.jsonl; sessions live under ~/.claude_code/sessions/ as session-{uuid}.jsonl plus metadata JSON.
memdir/ walks the tree for .claude/CLAUDE.md, hot-reloads, and injects memory into context.
Compaction: extract memories → summarize → MicrocompactBoundaryMessage → rebuild transcript—manual or automatic; /teleport exports state (QR/URL) for cross-machine resume with working-directory checks.
Permissions, overlays, and the illusion of immediacy
One of the most sophisticated ideas visible in the reconstruction is the file state overlay. Tools manipulate an in-memory map of reads/writes/deletes; disk is touched only after approval. That pattern powers safe previews (“here is the diff you would get”) and rollback on denial—essential when a model hallucinates a path or overreaches scope.
Permission modes rhyme with what enterprises already enforce on endpoints—default (prompt), auto (heuristic allow for read-only), bypass (dangerous; guarded by environment checks like root/sandbox posture). Deny rules support glob patterns (for example blocking risky bash prefixes). This is policy-as-code intersecting UX in real time.
Remote and bridge modes
Feature-gated code paths reference remote / CCR (cloud-controlled runtime) behaviors: constrained command subsets, bridge protocols for mobile clients, different trust boundaries when execution is orchestrated elsewhere.
For threat modeling, treat those gates as trust-domain switches: the same binary may embed multiple products behind compile-time flags—Bun’s feature() dead-code elimination story in the README is the engineering correlate of “different SKUs, one artifact.”
Feature flags & Bun dead-code elimination
The recovered tree shows Bun feature() used for compile-time branching: disabled flags erase entire dynamic import subgraphs from the shipped bundle—but authoring-time TypeScript in maps can still mention them.
Example pattern from the README:
if (feature('COORDINATOR_MODE')) {
const { Coordinator } = await import('./coordinator/index.js')
// eliminated when flag false at build time
}
Known feature() / internal flag names (from README)
| Flag | Purpose | Notes |
|---|---|---|
KAIROS | Internal assistant mode | Internal-only tooling paths |
KAIROS_BRIEF | Brief/summary tool | Tied to assistant mode |
COORDINATOR_MODE | Multi-agent coordinator | Workers, tasks, parallel tools |
VOICE_MODE | STT/TTS | Beta / internal in map |
PROACTIVE | Unprompted suggestions | Experimental |
BRIDGE_MODE | Remote/mobile bridge | Internal |
WORKFLOW_SCRIPTS | Workflow scripting | Experimental |
HISTORY_SNIP | History pruning | Internal |
COMMIT_ATTRIBUTION | Annotate git commits from tools | Internal |
CONTEXT_COLLAPSE | Semantic compression | Experimental |
MCP_SKILLS | MCP-provided skills | Beta |
EXPERIMENTAL_SKILL_SEARCH | Fuzzy skill search | Experimental |
UDS_INBOX | Unix socket inbox | Internal |
BUDDY | Buddy collaboration | Internal |
MONITOR_TOOL | Background monitor | Internal |
DUMP_SYSTEM_PROMPT | CLI dump of system prompt | Gated in cli.tsx |
ABLATION_BASELINE | Science harness defaults | Must run before tool env capture |
Build pipeline (mental model)
README-style builds center on bun build src/entrypoints/cli.tsx --target node --outfile dist/claude-code.js with --define feature.*=false toggles per SKU.
TypeScript compiles without a separate tsc pass; Biome handles lint/format. Source maps generated for that pipeline are what enabled recovery.
Telemetry, experimentation, and internal codenames
Recovered strings and module lists highlight analytics plumbing—GrowthBook for flags and experiments, structured events like session start/exit, tool use, permission prompts, compaction triggers. Internal feature names (KAIROS, coordinator modes, voice stacks) read like a roadmap etched into the bundle.
None of this is intrinsically nefarious; every serious product instruments itself. The lesson for buyers is make the data plane explicit in procurement: what leaves the machine, when, with which identifiers, under which retention regime, and how to disable or proxy it in regulated environments.
MDM hooks and keychain prefetch modules (macOS) remind us that Claude Code is not only a dev toy—it is positioned for managed fleets where policy arrives from enterprise mobility tooling. That is exactly where SBOM discipline and signed artifact review pay rent.
Representative tengu_* events (from README)
| Event | When |
|---|---|
tengu_started | Session begins |
tengu_exit | Session end + cost/duration |
tengu_worktree_created | Git worktree setup |
tengu_tool_use | Each tool execution |
tengu_permission_request | Permission modal shown |
tengu_permission_denied | User denied tool |
tengu_compact | Context compaction |
tengu_error | Unhandled error |
Sessions also record latency, per-tool duration, token/cache splits, git diff line stats, and UI FPS—useful for perf teams; sensitive for privacy reviews.
Environment variables & message schema (contract surface)
Selected environment variables
| Variable | Effect |
|---|---|
USER_TYPE=ant | Internal commands/tools (constant-folded out of public builds) |
IS_DEMO=true | Sanitized demo output |
CLAUDE_CODE_SIMPLE=true | Bash + FileRead + FileEdit only |
CLAUDE_CODE_REMOTE=true | Remote / CCR mode |
CLAUDE_CODE_DISABLE_AUTO_MEMORY | Disable memory injection |
CLAUDE_CODE_DISABLE_THINKING | Disable extended thinking |
DISABLE_BACKGROUND_TASKS | Skip prefetch jobs |
CLAUDE_CODE_ABLATION_BASELINE | Science harness defaults (ABLATION_BASELINE) |
CLAUDE_CODE_UNDERCOVER=1 | Undercover attribution scrubbing (internal builds) |
ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL | API auth / custom endpoint |
CLAUDE_CODE_FORCE_LOGIN | Force OAuth even with API key |
GROQ_API_KEY / OPENROUTER_API_KEY | Alternate providers |
CLAUDE_CODE_MESSAGING_SOCKET | Unix domain socket path (IPC) |
CLAUDE_CODE_ENTRYPOINT | desktop, local-agent, agent_sdk, … |
HOMESERVER_URL | Teleport / deep-link target |
Message & content-block types (types/message.ts)
High-level shapes from the README—useful for anyone building compatible tooling or IR parsers.
type Message =
| UserMessage | AssistantMessage | ProgressMessage
| ToolUseSummaryMessage | SystemMessage | TombstoneMessage
| AttachmentMessage
type ContentBlock =
| TextBlock | CodeBlock
| ToolUseBlock | ToolResultBlock | ImageBlock
End-to-end query flow (user input → disk) matches the README’s pipeline: typeahead command match; else query.ts builds messages and normalizes; QueryEngine.ts streams API, runs tools under policy, loops until idle; MessageResponse.tsx renders; history.ts appends to ~/.claude_code/history.jsonl.
Architecture summary (systems view)
┌─────────────────────────────────────────────────────────────┐
│ Terminal UI — React + Ink + Yoga │
├───────────────────────┬─────────────────────────────────────┤
│ Commands + skills │ context → query → QueryEngine → API │
├───────────────────────┴─────────────────────────────────────┤
│ Tool ecosystem — Bash, files, MCP, agents, … │
├───────────────────────┬─────────────────────────────────────┤
│ Permissions + overlay │ AppState + speculation │
├───────────────────────┴─────────────────────────────────────┤
│ Services — API, MCP, auth, analytics, plugins, settings │
├─────────────────────────────────────────────────────────────┤
│ ~/.claude_code — history, sessions, settings │
└─────────────────────────────────────────────────────────────┘
Basis: npm-published bundles + source maps per teamjaf/anthropic-ai-claude-code README (last refresh 2026-03-31). Not affiliated with Anthropic.
What enterprises should internalize this week
- Treat agent CLIs as privileged software. They combine credential material, broad filesystem reach, shell execution, and extensible tool protocols (MCP). Your EDR story must account for child processes spawned on behalf of a model.
-
Re-evaluate source map policies for shipped packages.
Private registries, CI stripping of
sourcesContent, and post-publish audits belong in SOC and SSDLC checklists—not just front-end SPAs. - Demand transparency on capability matrices. The reconstructed tool grid is the level of specificity you should require from any vendor selling “autonomous coding agents.”
- Plan for parallel realities in one binary. Feature flags and compile-time gates mean two enterprises might run “the same version” with different latent capabilities. Version pinning is necessary but not sufficient—configuration and environment drive behavior.
Teamjaaf helps operators translate those abstractions into controls: zero-trust paths for AI workstations, monitoring for anomalous tool spikes, and architecture reviews for ERP/cyber stacks that integrate copilots. Start at our cybersecurity practice or research programs if you want a partner that speaks both boardroom and bytecode.
Closing: the map is not the mission
Source maps will keep appearing in unexpected places because developer ergonomics and security margins pull in opposite directions.
Claude Code’s reconstruction is a rare, textbook-quality window into how a frontier AI shop operationalizes agentic UX—permissions, overlays, MCP, multitasking, speculative UI, compaction, session portability (/teleport style flows), and compile-time product splitting.
The mission for builders is unchanged: ship systems that stay intelligible under stress, auditable under scrutiny, and resilient when the threat model includes your own tools. For the full structured tour—directory-by-directory notes, tables of tools, environment variables, message schemas—open the companion README on GitHub and decide how your organization maps product promises to executable reality.
This page is a quick lab note, built with humans and Anthropic AI working together—research, structure, and implementation were a collaborative pass.
Published March 31, 2026 · Teamjaaf lab note · mjh@teamjaaf.com