@blitzjb/claude-code-sdk
v0.3.0
Published
A conversational TypeScript SDK for Claude CLI with streaming support
Maintainers
Readme
Claude Code SDK
A conversational TypeScript SDK for Claude CLI with session management, robust error handling, and full subagent support.
🚨 Important Note for Newer Claude CLI Versions
Due to known issues with programmatic execution of Claude CLI (particularly versions with stream-json hanging), this SDK focuses on session management and history loading rather than direct CLI execution.
The SDK excels at:
- ✅ Loading and parsing existing Claude CLI session history
- ✅ Type-safe conversation management
- ✅ Session resumption with full context
- ✅ Unified message handling across all interaction types
- ✅ Full subagent detection and tracking
- ✅ Hierarchical message parsing with parent-child relationships
- ✅ Complete project and session discovery
- ✅ Directory enumeration for all Claude projects
For new conversations:
- The SDK provides the exact command to run manually
- Session history is automatically loaded once Claude CLI creates the session file
- Full conversation context is maintained for subsequent operations
Features
- 🗣️ Conversational API: Natural
.say()and.ask()methods - 💾 History Integration: Uses Claude's built-in session storage
- 🔒 Type Safety: Full TypeScript support with discriminated unions
- 🛡️ Robust Error Handling: Graceful parsing with unknown message recovery
- ⚡ Session Management: Automatic loading and resumption
- 🎯 Zero Dependencies: Lightweight (only node-pty for terminal compatibility)
Installation
npm install @blitzjb/claude-code-sdkQuick Start
Basic Session Management
import { Claude } from '@blitzjb/claude-code-sdk';
// Start a conversation (provides manual command to run)
const conversation = await Claude.start();
// The SDK will provide the command to run:
// claude -p "your prompt" --output-format json --dangerously-skip-permissions
// After running the command manually, resume the session
const resumedChat = await Claude.resume(conversation.sessionId);
const history = resumedChat.getHistory();
console.log(`Loaded ${history.length} messages`);Working with Existing Sessions
// Resume an existing session ID
const chat = await Claude.resume('existing-session-id');
await chat.load();
const history = chat.getHistory();
console.log(`Session has ${history.length} messages`);
// Get specific message types
const userMessages = history.filter(msg => msg.type === 'user');
const assistantMessages = history.filter(msg => msg.type === 'assistant');
// Access conversation content
history.forEach((msg, index) => {
console.log(`${index + 1}. [${msg.timestamp.toLocaleTimeString()}] ${msg.type}: ${
typeof msg.content === 'string' ? msg.content.substring(0, 50) + '...' : 'Tool use'
}`);
});Type-Safe Message Handling
const conversation = await Claude.resume('session-id');
await conversation.load();
const messages = conversation.getHistory();
messages.forEach(msg => {
switch(msg.type) {
case 'user':
console.log(`User: ${msg.content}`);
break;
case 'assistant':
if (typeof msg.content === 'string') {
console.log(`Claude: ${msg.content}`);
} else {
console.log(`Tool: ${msg.content.name}`);
}
break;
case 'tool_result':
console.log(`Result: ${msg.content}`);
break;
case 'system':
console.log(`System: ${msg.subtype}`);
break;
}
});API Reference
Claude Class
// Start a new conversation (returns instructions for manual execution)
const chat = await Claude.start({ debug: true });
// Resume existing conversation
const chat = await Claude.resume('session-id');
// Check if session exists
const exists = await Claude.sessionExists('session-id');
// Get session summary
const summary = await Claude.getSessionSummary('session-id');Conversation Class
// Load conversation history
await conversation.load();
// Get conversation history
const messages = conversation.getHistory();
const count = conversation.getMessageCount();
const lastMsg = conversation.getLastMessage();
// Get messages by type
const userMessages = conversation.getMessagesByType('user');Subagent Support
The SDK automatically detects and parses subagent interactions from Claude CLI sessions. Subagent messages are included as part of the regular conversation history with additional metadata.
Simple History Loading
import { Claude } from '@blitzjb/claude-code-sdk';
// Load all history from a session (includes subagent interactions)
const history = await Claude.loadHistory('session-id');
// Filter subagent-related messages
const subagentMessages = history.filter(msg => (msg as any).parentToolUseId);
console.log(`Found ${subagentMessages.length} subagent messages`);
// Find Task tool calls (subagent initiations)
const taskCalls = history.filter(msg =>
msg.type === 'assistant' &&
typeof msg.content === 'object' &&
msg.content?.name === 'Task'
);
console.log(`Found ${taskCalls.length} subagent tasks`);Working with Subagent Data
// Load conversation with subagent data
const conversation = await Claude.resume('session-id');
const messages = conversation.getHistory();
// Process each message
messages.forEach((msg, index) => {
console.log(`Message ${index}: ${msg.type}`);
// Check if this message is part of a subagent
if ((msg as any).parentToolUseId) {
console.log(` Part of subagent: ${(msg as any).parentToolUseId}`);
}
// Check if this is a subagent task initiation
if ((msg as any).isSubagentTask) {
console.log(` Subagent type: ${(msg as any).subagentType}`);
}
});Subagent Types
Supports any custom subagent type as a string:
type SubagentType = string; // 'general-purpose', 'custom-agent', etc.Example: Manual Command
// Run this to generate subagent interactions:
// claude -p "Please use the general-purpose agent to analyze this code" --output-format stream-json --verbose
// Then load the session:
const sessionId = 'your-session-id-from-cli';
const history = await Claude.loadHistory(sessionId);
// Subagent interactions are automatically parsed and included
console.log(`Total messages: ${history.length}`);Project and Session Discovery
The SDK provides comprehensive methods to discover and enumerate all Claude projects and sessions.
List All Projects
// Get all project directories in ~/.claude/projects/
const projects = await Claude.listProjects();
console.log(`Found ${projects.length} projects:`);
projects.forEach(project => {
const realPath = Claude.getProjectNameFromPath(project);
console.log(`${project} → ${realPath}`);
});List Sessions in a Project
// Get all sessions within a specific project
const sessions = await Claude.listSessionsInProject('project-name');
console.log(`Found ${sessions.length} sessions in project`);
sessions.forEach(sessionId => {
console.log(`Session: ${sessionId}`);
});Get All Sessions Globally
// Get all sessions across all projects
const allSessions = await Claude.getAllSessions();
console.log(`Total sessions: ${allSessions.length}`);
allSessions.forEach(({project, sessionId}) => {
const realPath = Claude.getProjectNameFromPath(project);
console.log(`${realPath}: ${sessionId}`);
});Utility Methods
// Check if a project exists
const exists = await Claude.projectExists('project-name');
// Convert encoded project path to real path
const realPath = Claude.getProjectNameFromPath('-Users-user-Projects-myapp');
console.log(realPath); // Users/user/Projects/myapp
// Load history from a specific project and session
const sessionFile = ClaudePaths.getSessionFileFromProject(sessionId, projectName);Complete Discovery Example
import { Claude } from '@blitzjb/claude-code-sdk';
async function discoverAllSessions() {
// Get all projects
const projects = await Claude.listProjects();
console.log(`📁 Found ${projects.length} projects`);
// Get all sessions globally
const allSessions = await Claude.getAllSessions();
console.log(`💬 Found ${allSessions.length} total sessions`);
// Group sessions by project
const sessionsByProject = allSessions.reduce((acc, {project, sessionId}) => {
if (!acc[project]) acc[project] = [];
acc[project].push(sessionId);
return acc;
}, {} as Record<string, string[]>);
// Display summary
for (const [project, sessions] of Object.entries(sessionsByProject)) {
const realPath = Claude.getProjectNameFromPath(project);
console.log(`\n${realPath}:`);
console.log(` ${sessions.length} sessions`);
// Load a sample session
if (sessions.length > 0) {
const history = await Claude.loadHistory(sessions[0]);
console.log(` Sample session has ${history.length} messages`);
}
}
}Message Types
All messages use a unified type system:
type Message =
| UserMessage // { type: 'user', content: string }
| AssistantMessage // { type: 'assistant', content: string | ToolUse }
| ToolMessage // { type: 'tool_result', toolUseId, content }
| SystemMessage // { type: 'system', subtype, data }
| UnknownMessage; // { type: 'unknown', raw, parseError }Workflow
1. Starting a New Conversation
const conversation = await Claude.start();
console.log(`Session ID: ${conversation.sessionId}`);
// SDK provides the command to run manually:
// claude -p "your prompt" --output-format json --dangerously-skip-permissions2. Working with Session History
// After running Claude CLI manually, resume the session
const chat = await Claude.resume('session-id-from-step-1');
await chat.load();
const history = chat.getHistory();
console.log(`Loaded ${history.length} messages from Claude CLI session`);3. Continuing Conversations
// For continued conversations, the SDK provides the resume command:
// claude -p "follow up question" --resume session-id --output-format json --dangerously-skip-permissions
// Then load the updated session:
await chat.load(); // Reloads with new messages
const updatedHistory = chat.getHistory();Why This Approach?
- Claude CLI Reliability Issues: Programmatic execution of Claude CLI has known hanging issues with stream-json format
- Session Persistence: Manual execution ensures sessions are properly saved to Claude's storage
- Full Feature Access: Manual execution provides access to all Claude CLI features (tools, etc.)
- Type Safety: SDK provides full TypeScript safety for session management
- Best of Both Worlds: Combines Claude CLI's full capabilities with SDK convenience
Session Storage
The SDK reads from Claude CLI's built-in storage:
- Location:
~/.claude/projects/ - Format: JSONL files with UUID names
- Content: Complete conversation history with tools, timestamps, and metadata
Configuration
// Debug mode for detailed logging
const conversation = await Claude.start({ debug: true });
// Work with specific session ID
const conversation = await Claude.start({ sessionId: 'your-uuid' });
// Validate session ID format
if (Claude.validateSessionId(sessionId)) {
// Valid UUID format
}Requirements
- Node.js 16+
- Claude CLI installed and configured
- TypeScript 5+ (for development)
Contributing
- Clone the repository
- Run
npm install - Run
npm run buildto compile TypeScript - Test with existing Claude CLI sessions
License
MIT
Support
For issues related to:
- Claude CLI execution: Check Claude Code GitHub Issues
- SDK functionality: Open an issue in this repository
The SDK provides a robust, type-safe interface for working with Claude CLI sessions while respecting the CLI's execution limitations.
