Claude Code SDK for TypeScript

June 27, 2025 ยท View on GitHub

npm version npm downloads License: MIT TypeScript Node.js Version

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.

Installation

npm install @instantlyeasy/claude-code-sdk-ts
# or
yarn add @instantlyeasy/claude-code-sdk-ts
# or  
pnpm add @instantlyeasy/claude-code-sdk-ts

Latest 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)

Quick Start

import { claude } from '@instantlyeasy/claude-code-sdk-ts';

// Simple query
const response = await claude()
  .query('Say "Hello World!"')
  .asText();

console.log(response); // "Hello World!"

Authentication

This SDK delegates all authentication to the Claude CLI:

# One-time setup - login with your Claude account
claude login

The SDK does not handle authentication directly. If you see authentication errors, authenticate using the Claude CLI first.

Core Features

๐ŸŽฏ Fluent API

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 text

๐Ÿ“Š Response Parsing

Extract 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'}`);
}

๐Ÿ”ง Tool Management

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();

๐Ÿ’ฌ Session Management

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!

๐Ÿšฆ Cancellation Support

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');
  }
}

๐Ÿ“ Logging

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, NullLogger

๐ŸŽญ Event Handlers

React 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
  });

Environment Variables

The SDK automatically loads safe configuration from environment:

  • DEBUG - Enable debug mode (values: true, 1, yes, on)
  • VERBOSE - Enable verbose output
  • LOG_LEVEL - Set log level (0-4)
  • NODE_ENV - Node environment

โš ๏ธ Important: API keys are NOT automatically loaded from ANTHROPIC_API_KEY for safety. This prevents accidental billing charges. See Environment Variables Documentation.

Error Handling

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 issues
  • authentication - Auth problems
  • permission - Access denied
  • timeout - Operation timeouts
  • validation - Invalid input
  • cli - Claude CLI issues
  • configuration - Config problems

Advanced Usage

Configuration Files & Roles

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();

Role System

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.

Production Features

Token Usage & Costs

const parser = await claude()
  .query('Complex task')
  .getParser();

const usage = await parser.getUsage();
console.log('Tokens:', usage.totalTokens);
console.log('Cost: $', usage.totalCost);

Streaming

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);
    }
  });

Custom Models & Endpoints

const response = await claude()
  .withModel('claude-3-opus-20240229')
  .withTimeout(30000)
  .query('Complex analysis')
  .asText();

๐Ÿš€ Enhanced Features (v0.3.3)

โœจ Visual Token Streaming

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));
}

๐Ÿ›ก๏ธ Advanced Error Handling

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}`);
}

๐ŸŽฌ Interactive Streaming Session

NEW! Complete chat interface with visual streaming:

# Try the interactive streaming example
node examples/fluent-api/new-features/interactive-streaming.js

Features working character-by-character display, conversation history, speed control, and model switching!

Examples

Comprehensive examples are available in the examples directory:

Basic Examples

Advanced Features (new-features directory)

Core Examples

  • File Operations - Reading, writing, and analyzing code
  • Web Research - Using Claude's web capabilities
  • Interactive Sessions - Building conversational interfaces

Migration from Classic API

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.

API Reference

claude(): QueryBuilder

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): ResponseParser

Response Parser Methods

  • asText() - Extract plain text
  • asJSON<T>() - Parse JSON response
  • asResult() - Get final result message
  • asToolExecutions() - Get tool execution details
  • findToolResults(name) - Find specific tool results
  • getUsage() - Get token usage stats
  • getSessionId() - Get session ID
  • stream(callback) - Stream messages

Types

See TypeScript definitions for complete type information.

Changelog

See CHANGELOG.md for version history.

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

License

MIT ยฉ Daniel King & Claude