Skip to main content
A handful of patterns appear repeatedly across the ~1,900 source files. Recognizing them on sight will significantly reduce the time it takes to understand any given module.

buildTool() — tool factory

Every tool is created with buildTool(), defined in src/Tool.ts. The factory attaches the input schema, permission model, execution logic, and UI rendering into a single cohesive object that the Query Engine can call.
export const MyTool = buildTool({
  name: 'MyTool',
  aliases: ['my_tool'],
  description: 'What this tool does',
  inputSchema: z.object({
    param: z.string(),
  }),

  async call(args, context, canUseTool, parentMessage, onProgress) {
    // Execute and return { data: result, newMessages?: [...] }
  },

  async checkPermissions(input, context) {
    // Return { granted: boolean, reason?: string, prompt?: string }
  },

  isConcurrencySafe(input) {
    // Return true if this tool can run in parallel with other tools
  },

  isReadOnly(input) {
    // Return true if this tool makes no changes (e.g. file reads, searches)
  },

  prompt(options) {
    // Return system prompt text injected before each query
  },

  renderToolUseMessage(input, options) {
    // Return JSX rendered when Claude invokes this tool
  },

  renderToolResultMessage(content, progressMessages, options) {
    // Return JSX rendered when the tool result is shown
  },
})
isReadOnly() and isConcurrencySafe() influence whether the Query Engine can run tools in parallel. Tools that declare themselves read-only and concurrency-safe can execute simultaneously — for example, multiple FileReadTool calls in one LLM turn.

Feature flag gates

Claude Code uses Bun’s bun:bundle import for compile-time dead-code elimination. Code inside an inactive feature flag is completely stripped from the production binary — it never ships.
import { feature } from 'bun:bundle'

if (feature('VOICE_MODE')) {
  // This entire block is removed at build time if VOICE_MODE is off
  const voiceCommand = require('./commands/voice/index.js').default
}
Notable feature flags you’ll encounter:
FlagFeature
PROACTIVEProactive agent mode (autonomous background actions)
KAIROSKairos subsystem
BRIDGE_MODEIDE bridge integration
DAEMONBackground daemon mode
VOICE_MODEVoice input and output
AGENT_TRIGGERSTriggered agent actions
MONITOR_TOOLMonitoring tool
COORDINATOR_MODEMulti-agent coordinator
WORKFLOW_SCRIPTSWorkflow automation scripts
To find all feature flag usage in the source, run rg "feature\(" src/. This shows every guarded block.

Anthropic-internal gates

Some code paths are only active for Anthropic employees. These gates are a simple environment variable check:
if (process.env.USER_TYPE === 'ant') {
  // Anthropic employee-only features
}
You’ll find this pattern gating internal diagnostics, experimental modes, and development-only tooling. Use rg "USER_TYPE.*ant" src/ to locate all occurrences.

Command definition pattern

Slash commands (/commit, /review, etc.) follow a consistent satisfies Command pattern, which gives TypeScript structural type checking without requiring a class:
const command = {
  type: 'prompt',
  name: 'my-command',
  description: 'What this command does',
  progressMessage: 'working...',
  allowedTools: ['Bash(git *)', 'FileRead(*)'],
  source: 'builtin',

  async getPromptForCommand(args, context) {
    return [{ type: 'text', text: '...' }]
  },
} satisfies Command
There are three command types:
Sends a formatted prompt to the LLM with a specific set of allowed tools. The getPromptForCommand() function builds the message array. Used for /review, /commit, and most agent commands.

Index re-exports

Almost every directory has an index.ts that re-exports the module’s public API. This keeps import paths clean and lets internal structure change without affecting callers.
// src/tools/BashTool/index.ts
export { BashTool } from './BashTool.js'
When you open a directory and see only an index.ts, it is a thin facade — follow the imports to find the real implementation.

Lazy dynamic imports

Heavy modules are loaded on demand with dynamic import() rather than at startup. This keeps the initial startup time fast.
// Only loaded when OpenTelemetry is first needed (~400KB)
const { OpenTelemetry } = await import('./heavy-module.js')
You’ll see this pattern for OpenTelemetry (~400KB), gRPC (~700KB), and other optional dependencies that most users may never invoke.

ESM with .js extensions

Claude Code uses Bun’s ES module convention: all import statements reference .js extensions even though the actual files are .ts. Bun resolves these transparently at runtime.
// The import references .js — Bun will load the .ts file
import { something } from './utils.js'
Do not be confused by .js extensions in import statements — there are no compiled .js files in src/. All files are TypeScript; Bun handles the resolution.

Parallel prefetch

src/main.tsx fires several async side-effects before evaluating any heavy modules. This hides I/O latency behind module load time:
// Fires before heavy module evaluation — hides I/O latency
startMdmRawRead()
startKeychainPrefetch()
// GrowthBook feature flag fetch also starts here
The results are awaited later, after the necessary modules are loaded. This is why startup feels fast despite loading a complex React + Ink application.

Build docs developers (and LLMs) love