SDK for programmatic control of Claude Code CLI sessions.
Built on top of tmux-core for reliable terminal management.
npm install claude-code-sdk- Session Control - Start, stop, and manage Claude Code sessions
- Status Detection - Know when Claude is thinking, executing tools, or idle
- Mode Detection - Detect Plan Mode, Accept Edits, Bypass Permissions
- Interactive Menus - Parse and respond to model selection, permission prompts
- Message Filtering - Clean output by removing tool markers and status indicators
- Input Management - Detect unsent input, send commands vs messages
- Interrupt Handling - Cancel operations, send Ctrl+C sequences
import { ClaudeCodeSession } from 'claude-code-sdk';
// Create and start a session
const session = new ClaudeCodeSession('my-agent', {
model: 'haiku',
workingDirectory: '/workspace/my-project',
});
await session.start();
// Wait for Claude to be ready
await session.waitForIdle(10000);
// Send a message
await session.sendMessage('What files are in this directory?');
// Wait for response
await session.waitForIdle(30000);
// Get the response (filtered)
const response = await session.getLastResponse();
console.log(response);
// Clean up
await session.exit();import { ClaudeCodeSession } from 'claude-code-sdk';
const session = new ClaudeCodeSession(name, {
model?: 'haiku' | 'sonnet' | 'opus',
workingDirectory?: string,
socket?: string, // For test isolation
agentId?: string, // For hook file lookups
});
await session.start();
await session.exit();
await session.isRunning(): Promise<boolean>;// Get current processing state
const state = await session.getState();
// 'idle' | 'thinking' | 'tool_executing' | 'waiting' | 'unknown'
// Wait for specific state
await session.waitForIdle(timeout);
await session.waitForState('idle', timeout);
// Check readiness
const ready = await session.isReady(); // idle + prompt visible// Get current operating mode
const mode = await session.getMode();
// 'normal' | 'plan' | 'acceptEdits' | 'bypassPermissions' | 'dontAsk'
// Check specific modes
const isPlanMode = await session.isInPlanMode();
const isBypass = await session.isBypassingPermissions();// Check for active menu
const menu = await session.getActiveMenu();
if (menu) {
console.log(menu.type); // 'model_selection', 'permission_prompt', etc.
console.log(menu.options); // Available choices
console.log(menu.prompt); // Question text
// Select an option
await session.selectMenuOption(1); // Select first option
// Or type custom response
await session.respondToMenu('custom input');
}// Send a message (waits for ready state)
await session.sendMessage('Hello Claude');
// Send a command (starts with /)
await session.sendCommand('/model');
await session.sendCommand('/clear');
// Send without waiting for ready (use carefully)
await session.sendRaw('text');import { filterOutput } from 'claude-code-sdk';
const clean = filterOutput(rawOutput, {
removeToolMarkers: true, // Remove ● markers
removeToolOutput: true, // Remove ⎿ output sections
removeStatusIndicators: true, // Remove ✦ ✢ ✻
removeAnsiCodes: true, // Remove terminal colors
});// Cancel current operation (Escape)
await session.cancel();
// Soft interrupt (Ctrl+C once)
await session.interrupt();
// Hard interrupt (Ctrl+C twice)
await session.hardInterrupt();// Check for unsent input
const buffer = await session.getInputBuffer();
if (buffer) {
console.log(`User typed but didn't send: ${buffer}`);
}
// Clear input buffer
await session.clearInputBuffer();For testing, use isolated tmux sockets to avoid affecting production sessions:
const session = new ClaudeCodeSession('test-agent', {
socket: 'my-test-socket',
model: 'haiku', // Always use haiku for tests (cheapest)
});Live tests that interact with Claude consume API tokens. The SDK includes token budget tracking for test suites:
import { hasTokenBudget, recordTokenUsage } from 'claude-code-sdk/testing';
if (hasTokenBudget(100)) {
// Safe to run this test
await session.sendMessage('What is 2+2?');
recordTokenUsage(15); // Track usage
}MIT