Unofficial TypeScript SDK for Claude Code - the powerful CLI tool for interacting with Claude.
✨ What's New in v0.3.3:
- 🎬 Interactive streaming session with working visual typewriter effects
- 🛡️ Advanced error handling with retry strategies and typed errors
- 📊 Token streaming analysis with honest documentation about current behavior
- 🔧 Production-ready examples that actually work as advertised
Note: For the classic async generator API, see Classic API Documentation.
npm install @instantlyeasy/claude-code-sdk-ts # or yarn add @instantlyeasy/claude-code-sdk-ts # or pnpm add @instantlyeasy/claude-code-sdk-tsLatest Version: v0.3.3 with enhanced features and working visual streaming!
Prerequisites:
- Node.js 18 or later
- Claude Code CLI installed (
npm install -g @anthropic-ai/claude-code)
import { claude } from '@instantlyeasy/claude-code-sdk-ts'; // Simple query const response = await claude() .query('Say "Hello World!"') .asText(); console.log(response); // "Hello World!"This SDK delegates all authentication to the Claude CLI:
# One-time setup - login with your Claude account claude loginThe SDK does not handle authentication directly. If you see authentication errors, authenticate using the Claude CLI first.
Chain methods for clean, readable code:
const result = await claude() .withModel('sonnet') // Choose model .allowTools('Read', 'Write') // Configure permissions .skipPermissions() // Auto-accept edits .inDirectory('/path/to/project') // Set working directory .query('Refactor this code') // Your prompt .asText(); // Get response as textExtract exactly what you need:
// Get plain text const text = await claude() .query('Explain this concept') .asText(); // Parse JSON response const data = await claude() .query('Return a JSON array of files') .asJSON<string[]>(); // Get the final result const result = await claude() .query('Complete this task') .asResult(); // Analyze tool usage const tools = await claude() .allowTools('Read', 'Grep') .query('Find all TODO comments') .asToolExecutions(); for (const execution of tools) { console.log(`${execution.tool}: ${execution.isError ? 'Failed' : 'Success'}`); }Fine-grained control over Claude's capabilities:
// Allow specific tools await claude() .allowTools('Read', 'Grep', 'LS') .query('Analyze this codebase') .asText(); // Deny dangerous tools await claude() .denyTools('Bash', 'Write') .query('Review this code') .asText(); // Read-only mode (no tools) await claude() .allowTools() // Empty = deny all .query('Explain this architecture') .asText();Maintain conversation context across queries:
const session = claude() .withModel('sonnet') .skipPermissions(); // First query const response1 = await session .query('Pick a random number between 1 and 100') .asText(); // Continue with context const sessionId = await session.query('').getSessionId(); const response2 = await session .withSessionId(sessionId) .query('What number did you pick?') .asText(); // Claude remembers the number!Cancel long-running operations:
const controller = new AbortController(); // Cancel after 5 seconds setTimeout(() => controller.abort(), 5000); try { const response = await claude() .withSignal(controller.signal) .query('Long running task') .asText(); } catch (error) { if (error instanceof AbortError) { console.log('Query was cancelled'); } }Built-in logging with multiple implementations:
import { ConsoleLogger, LogLevel } from '@instantlyeasy/claude-code-sdk-ts'; const logger = new ConsoleLogger(LogLevel.DEBUG); const response = await claude() .withLogger(logger) .query('Debug this issue') .asText(); // Also available: JSONLogger, MultiLogger, NullLoggerReact to events during execution:
await claude() .onMessage(msg => console.log('Message:', msg.type)) .onAssistant(msg => console.log('Claude:', msg)) .onToolUse(tool => console.log(`Using ${tool.name}...`)) .query('Perform analysis') .stream(async (message) => { // Handle streaming messages });The SDK automatically loads safe configuration from environment:
DEBUG- Enable debug mode (values:true,1,yes,on)VERBOSE- Enable verbose outputLOG_LEVEL- Set log level (0-4)NODE_ENV- Node environment
ANTHROPIC_API_KEY for safety. This prevents accidental billing charges. See Environment Variables Documentation.
Enhanced error handling with categories and resolution hints:
import { isEnhancedError, hasResolution } from '@instantlyeasy/claude-code-sdk-ts'; try { await claude().query('Task').asText(); } catch (error) { if (isEnhancedError(error)) { console.error(`${error.category} error: ${error.message}`); if (hasResolution(error)) { console.error('Try:', error.resolution); } } }Error categories include:
network- Connection issuesauthentication- Auth problemspermission- Access deniedtimeout- Operation timeoutsvalidation- Invalid inputcli- Claude CLI issuesconfiguration- Config problems
Load settings and define reusable roles from YAML or JSON:
// Load configuration with roles await claude() .withConfigFile('./config/claude.yaml') .withRole('developer', { language: 'TypeScript', framework: 'React' }) .query('Generate component') .asText();Roles provide reusable configurations with:
- Model preferences
- Tool permissions
- Custom prompts with template variables
- Context settings (temperature, max tokens)
- Inheritance support
Example YAML config with roles:
version: "1.0" globalSettings: model: opus timeout: 60000 # Define reusable roles roles: developer: model: sonnet tools: allowed: [Read, Write, Edit] denied: [Delete] prompts: prefix: "You are an expert ${language} developer using ${framework}." senior-developer: extends: developer # Inherit from developer role model: opus permissions: mode: acceptEdits tools: allowed: [TodoRead, TodoWrite] # Additional tools// Using roles with template variables const response = await claude() .withRolesFile('./roles.yaml') .withRole('senior-developer', { language: 'TypeScript', framework: 'Next.js', specialty: 'performance optimization' }) .query('Optimize this React component') .asText();See Roles Documentation for complete details.
const parser = await claude() .query('Complex task') .getParser(); const usage = await parser.getUsage(); console.log('Tokens:', usage.totalTokens); console.log('Cost: $', usage.totalCost);await claude() .query('Tell me a story') .stream(async (message) => { if (message.type === 'assistant') { // Stream complete messages (not individual tokens) console.log(message.content[0].text); } });const response = await claude() .withModel('claude-3-opus-20240229') .withTimeout(30000) .query('Complex analysis') .asText();Create typewriter effects and real-time response display:
import { claude, createTokenStream } from '@instantlyeasy/claude-code-sdk-ts'; // Collect response for controlled display const messageGenerator = claude() .withModel('sonnet') .queryRaw('Write a story about AI'); const tokenStream = createTokenStream(messageGenerator); const allTokens = []; for await (const chunk of tokenStream.tokens()) { allTokens.push(chunk.token); } // Display with typewriter effect const fullText = allTokens.join(''); for (const char of fullText) { process.stdout.write(char); await new Promise(resolve => setTimeout(resolve, 30)); }Handle specific error types with smart retry logic:
import { claude, detectErrorType, withRetry } from '@instantlyeasy/claude-code-sdk-ts'; try { const result = await withRetry( async () => claude().query('Complex task').asText(), { maxAttempts: 3, strategy: 'exponential', shouldRetry: (error) => { const errorType = detectErrorType(error.message); return ['network_error', 'timeout_error'].includes(errorType); } } ); } catch (error) { const errorType = detectErrorType(error.message); console.log(`Failed with error type: ${errorType}`); }NEW! Complete chat interface with visual streaming:
# Try the interactive streaming example node examples/fluent-api/new-features/interactive-streaming.jsFeatures working character-by-character display, conversation history, speed control, and model switching!
Comprehensive examples are available in the examples directory:
- fluent-api-demo.js - Complete fluent API showcase
- sessions.js - Session management patterns
- yaml-config-demo.js - Configuration examples
Advanced Features (new-features directory)
- interactive-streaming.js - 🎬 Interactive chat with visual streaming
- token-streaming.js - Working typewriter effects
- error-handling.js - Advanced error patterns
- retry-strategies.js - Multiple retry strategies
- File Operations - Reading, writing, and analyzing code
- Web Research - Using Claude's web capabilities
- Interactive Sessions - Building conversational interfaces
The SDK maintains full backward compatibility. The classic query() function still works:
import { query } from '@instantlyeasy/claude-code-sdk-ts'; for await (const message of query('Hello')) { // Classic async generator API }However, we recommend the fluent API for new projects. See Migration Guide.
Creates a new query builder:
claude() .withModel(model: string) .allowTools(...tools: ToolName[]) .denyTools(...tools: ToolName[]) .skipPermissions() .withTimeout(ms: number) .inDirectory(path: string) .withSessionId(id: string) .withSignal(signal: AbortSignal) .withLogger(logger: Logger) .withConfigFile(path: string) .withRole(name: string, vars?: Record<string, string>) .onMessage(handler: (msg: Message) => void) .onAssistant(handler: (msg: AssistantMessage) => void) .onToolUse(handler: (tool: ToolUseBlock) => void) .query(prompt: string): ResponseParserasText()- Extract plain textasJSON<T>()- Parse JSON responseasResult()- Get final result messageasToolExecutions()- Get tool execution detailsfindToolResults(name)- Find specific tool resultsgetUsage()- Get token usage statsgetSessionId()- Get session IDstream(callback)- Stream messages
See TypeScript definitions for complete type information.
See CHANGELOG.md for version history.
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
MIT © Daniel King & Claude