/** * Claude Code Integration * * Provides seamless integration with Claude Code CLI and IDE extensions. * Enables context compaction, subagent spawning, and task orchestration * within Claude Code workflows. */ import { ContextManager, ContextManagerConfig } from '../core/context-manager'; import { TokenCounter } from '../core/token-counter'; import { Summarizer, SummarizerOptions } from '../core/summarizer'; import { Orchestrator, OrchestratorConfig, Task } from '../core/orchestrator'; import { SubagentSpawner, SubagentType } from '../core/subagent-spawner'; import { MemoryStore } from '../storage/memory-store'; // ============================================================================ // Types // ============================================================================ export interface ClaudeCodeConfig { /** Maximum tokens for context (default: 200000 for Claude models) */ maxContextTokens?: number; /** Reserve tokens for response generation */ reserveTokens?: number; /** Compaction strategy */ compactionStrategy?: 'sliding-window' | 'summarize-old' | 'priority-retention' | 'hybrid'; /** Priority keywords for retention */ priorityKeywords?: string[]; /** Enable automatic compaction */ autoCompact?: boolean; /** Compaction threshold percentage (0-1) */ compactionThreshold?: number; /** Model identifier for Claude */ model?: string; /** Enable subagent spawning */ enableSubagents?: boolean; /** Max concurrent subagents */ maxSubagents?: number; /** Working directory for Claude Code */ workingDirectory?: string; /** Enable persistent memory */ persistentMemory?: boolean; /** Memory store path */ memoryStorePath?: string; } export interface ClaudeMessage { role: 'user' | 'assistant' | 'system'; content: string; metadata?: { timestamp?: number; tokens?: number; priority?: number; toolUse?: boolean; fileReferences?: string[]; }; } export interface ClaudeToolDefinition { name: string; description: string; input_schema: { type: 'object'; properties: Record; required?: string[]; }; } export interface ClaudeCodeSession { id: string; createdAt: Date; lastActivity: Date; messageCount: number; tokenUsage: number; status: 'active' | 'compacted' | 'idle' | 'error'; } export interface CompactionResult { success: boolean; tokensBefore: number; tokensAfter: number; tokensSaved: number; messagesRemoved: number; summary?: string; keyPoints?: string[]; decisions?: string[]; } export interface SubagentTask { type: SubagentType; prompt: string; context?: Record; timeout?: number; priority?: 'low' | 'medium' | 'high'; } export interface SubagentResult { success: boolean; output: string; tokens: number; duration: number; filesModified?: string[]; artifacts?: Record; } // ============================================================================ // Claude Code Integration Class // ============================================================================ export class ClaudeCodeIntegration { private contextManager: ContextManager; private tokenCounter: TokenCounter; private summarizer: Summarizer; private orchestrator: Orchestrator | null = null; private subagentSpawner: SubagentSpawner | null = null; private memoryStore: MemoryStore | null = null; private config: Required; private sessionId: string; private messages: ClaudeMessage[] = []; private toolDefinitions: ClaudeToolDefinition[] = []; private compactionHistory: CompactionResult[] = []; constructor(config: ClaudeCodeConfig = {}) { this.config = { maxContextTokens: config.maxContextTokens ?? 200000, reserveTokens: config.reserveTokens ?? 40000, compactionStrategy: config.compactionStrategy ?? 'hybrid', priorityKeywords: config.priorityKeywords ?? [ 'error', 'important', 'decision', 'critical', 'remember', 'todo', 'fixme' ], autoCompact: config.autoCompact ?? true, compactionThreshold: config.compactionThreshold ?? 0.8, model: config.model ?? 'claude-sonnet-4-20250514', enableSubagents: config.enableSubagents ?? true, maxSubagents: config.maxSubagents ?? 6, workingDirectory: config.workingDirectory ?? process.cwd(), persistentMemory: config.persistentMemory ?? true, memoryStorePath: config.memoryStorePath ?? '.claude-code/memory' }; // Initialize core components this.tokenCounter = new TokenCounter(this.config.maxContextTokens); this.summarizer = new Summarizer(); this.contextManager = new ContextManager( this.tokenCounter, this.summarizer, { maxTokens: this.config.maxContextTokens - this.config.reserveTokens, compactionStrategy: this.config.compactionStrategy, priorityKeywords: this.config.priorityKeywords, reserveTokens: this.config.reserveTokens } ); // Initialize orchestrator if subagents enabled if (this.config.enableSubagents) { this.orchestrator = new Orchestrator({ maxAgents: this.config.maxSubagents, taskTimeout: 300000, retryAttempts: 3 }); this.subagentSpawner = new SubagentSpawner(); } // Initialize memory store if persistent if (this.config.persistentMemory) { this.memoryStore = new MemoryStore(this.config.memoryStorePath); } this.sessionId = this.generateSessionId(); this.registerDefaultTools(); } // ============================================================================ // Session Management // ============================================================================ private generateSessionId(): string { return `claude-code-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } getSessionId(): string { return this.sessionId; } getSessionInfo(): ClaudeCodeSession { const usage = this.tokenCounter.getUsagePercentage(); return { id: this.sessionId, createdAt: new Date(parseInt(this.sessionId.split('-')[2])), lastActivity: new Date(), messageCount: this.messages.length, tokenUsage: this.tokenCounter.getCurrentUsage(), status: usage > this.config.compactionThreshold ? 'compacted' : this.messages.length === 0 ? 'idle' : 'active' }; } // ============================================================================ // Message Handling // ============================================================================ /** * Add a message to the context */ addMessage(message: ClaudeMessage): void { // Estimate tokens for this message const tokens = this.tokenCounter.countTokens(message.content); message.metadata = { ...message.metadata, timestamp: message.metadata?.timestamp ?? Date.now(), tokens }; this.messages.push(message); this.tokenCounter.addUsage(tokens); // Add to context manager this.contextManager.addMessage({ role: message.role, content: message.content, priority: message.metadata?.priority, timestamp: message.metadata?.timestamp }); // Check for auto-compaction if (this.config.autoCompact && this.needsCompaction()) { this.compact(); } } /** * Get all messages in context */ getMessages(): ClaudeMessage[] { return [...this.messages]; } /** * Get context for Claude API call */ getContextForAPI(): { messages: ClaudeMessage[]; systemPrompt?: string } { const activeContext = this.contextManager.getActiveContext(); const messages: ClaudeMessage[] = activeContext.messages.map(m => ({ role: m.role as 'user' | 'assistant', content: m.content, metadata: { timestamp: m.timestamp, priority: m.priority } })); return { messages, systemPrompt: activeContext.summary ? `[Previous Context Summary]\n${activeContext.summary}\n\n[End of Summary]` : undefined }; } // ============================================================================ // Context Compaction // ============================================================================ /** * Check if compaction is needed */ needsCompaction(): boolean { return this.tokenCounter.getUsagePercentage() >= this.config.compactionThreshold; } /** * Perform context compaction */ async compact(): Promise { const tokensBefore = this.tokenCounter.getCurrentUsage(); try { const result = await this.contextManager.compact(); // Update local messages to match compacted context const activeContext = this.contextManager.getActiveContext(); this.messages = activeContext.messages.map(m => ({ role: m.role as 'user' | 'assistant', content: m.content, metadata: { timestamp: m.timestamp, priority: m.priority } })); // Reset token counter and recalculate this.tokenCounter.reset(); const newTokens = this.messages.reduce( (sum, m) => sum + this.tokenCounter.countTokens(m.content), 0 ); this.tokenCounter.addUsage(newTokens); const compactionResult: CompactionResult = { success: true, tokensBefore, tokensAfter: this.tokenCounter.getCurrentUsage(), tokensSaved: tokensBefore - this.tokenCounter.getCurrentUsage(), messagesRemoved: result.messagesRemoved, summary: result.summary, keyPoints: result.keyPoints, decisions: result.decisions }; this.compactionHistory.push(compactionResult); return compactionResult; } catch (error) { return { success: false, tokensBefore, tokensAfter: tokensBefore, tokensSaved: 0, messagesRemoved: 0 }; } } /** * Get compaction history */ getCompactionHistory(): CompactionResult[] { return [...this.compactionHistory]; } /** * Get current token usage stats */ getTokenStats(): { used: number; total: number; remaining: number; percentage: number; } { return { used: this.tokenCounter.getCurrentUsage(), total: this.config.maxContextTokens, remaining: this.tokenCounter.getRemainingBudget(), percentage: this.tokenCounter.getUsagePercentage() * 100 }; } // ============================================================================ // Tool Registration // ============================================================================ private registerDefaultTools(): void { // Register context management tools this.registerTool({ name: 'compact_context', description: 'Compact the conversation context to save tokens while preserving important information', input_schema: { type: 'object', properties: { force: { type: 'boolean', description: 'Force compaction even if threshold not reached' } } } }); // Register subagent tools if (this.config.enableSubagents) { this.registerTool({ name: 'spawn_explorer', description: 'Spawn an explorer agent to quickly search and navigate the codebase', input_schema: { type: 'object', properties: { query: { type: 'string', description: 'Search query or file pattern to explore' } }, required: ['query'] } }); this.registerTool({ name: 'spawn_researcher', description: 'Spawn a researcher agent to deeply analyze and research a topic', input_schema: { type: 'object', properties: { topic: { type: 'string', description: 'Topic to research' }, depth: { type: 'string', enum: ['shallow', 'medium', 'deep'], description: 'Research depth level' } }, required: ['topic'] } }); this.registerTool({ name: 'spawn_coder', description: 'Spawn a coder agent to implement or modify code', input_schema: { type: 'object', properties: { task: { type: 'string', description: 'Coding task description' }, files: { type: 'array', items: { type: 'string' }, description: 'Files to work on' } }, required: ['task'] } }); this.registerTool({ name: 'spawn_reviewer', description: 'Spawn a reviewer agent to review code quality and suggest improvements', input_schema: { type: 'object', properties: { files: { type: 'array', items: { type: 'string' }, description: 'Files to review' }, focus: { type: 'string', description: 'Review focus area (security, performance, style, all)' } }, required: ['files'] } }); } } /** * Register a custom tool */ registerTool(tool: ClaudeToolDefinition): void { this.toolDefinitions.push(tool); } /** * Get all registered tools */ getTools(): ClaudeToolDefinition[] { return [...this.toolDefinitions]; } // ============================================================================ // Subagent Spawning // ============================================================================ /** * Spawn a subagent for a specific task */ async spawnSubagent(task: SubagentTask): Promise { if (!this.subagentSpawner || !this.orchestrator) { throw new Error('Subagents are not enabled in this configuration'); } const startTime = Date.now(); try { // Create subagent const agent = this.subagentSpawner.spawn(task.type, { taskId: `${this.sessionId}-${task.type}-${Date.now()}`, memory: this.memoryStore || undefined }); // Execute task const result = await agent.execute({ prompt: task.prompt, ...task.context }); const duration = Date.now() - startTime; const outputTokens = this.tokenCounter.countTokens(result.output || ''); return { success: result.success !== false, output: result.output || '', tokens: outputTokens, duration, filesModified: result.filesModified, artifacts: result.artifacts }; } catch (error) { return { success: false, output: `Subagent error: ${error instanceof Error ? error.message : 'Unknown error'}`, tokens: 0, duration: Date.now() - startTime }; } } /** * Execute multiple subagent tasks in parallel */ async executeParallelSubagents(tasks: SubagentTask[]): Promise { return Promise.all(tasks.map(task => this.spawnSubagent(task))); } // ============================================================================ // Memory Management // ============================================================================ /** * Store a value in persistent memory */ async remember(key: string, value: any): Promise { if (this.memoryStore) { await this.memoryStore.set(`session:${this.sessionId}:${key}`, value); } } /** * Retrieve a value from persistent memory */ async recall(key: string): Promise { if (this.memoryStore) { return this.memoryStore.get(`session:${this.sessionId}:${key}`); } return null; } /** * Store important context for cross-session persistence */ async saveContext(name: string): Promise { if (this.memoryStore) { await this.memoryStore.set(`context:${name}`, { sessionId: this.sessionId, messages: this.messages, createdAt: Date.now() }); } } /** * Load a previously saved context */ async loadContext(name: string): Promise { if (this.memoryStore) { const saved = await this.memoryStore.get<{ sessionId: string; messages: ClaudeMessage[]; }>(`context:${name}`); if (saved) { this.messages = saved.messages; this.tokenCounter.reset(); for (const msg of this.messages) { this.tokenCounter.addUsage(this.tokenCounter.countTokens(msg.content)); } return true; } } return false; } // ============================================================================ // Utility Methods // ============================================================================ /** * Reset the session */ reset(): void { this.messages = []; this.tokenCounter.reset(); this.contextManager = new ContextManager( this.tokenCounter, this.summarizer, { maxTokens: this.config.maxContextTokens - this.config.reserveTokens, compactionStrategy: this.config.compactionStrategy, priorityKeywords: this.config.priorityKeywords, reserveTokens: this.config.reserveTokens } ); this.sessionId = this.generateSessionId(); this.compactionHistory = []; } /** * Export session data */ exportSession(): { sessionId: string; config: Required; messages: ClaudeMessage[]; compactionHistory: CompactionResult[]; toolDefinitions: ClaudeToolDefinition[]; } { return { sessionId: this.sessionId, config: this.config, messages: this.messages, compactionHistory: this.compactionHistory, toolDefinitions: this.toolDefinitions }; } /** * Import session data */ importSession(data: ReturnType): void { this.sessionId = data.sessionId; this.messages = data.messages; this.compactionHistory = data.compactionHistory; this.toolDefinitions = data.toolDefinitions; // Rebuild token counter state this.tokenCounter.reset(); for (const msg of this.messages) { this.tokenCounter.addUsage(this.tokenCounter.countTokens(msg.content)); } } } // ============================================================================ // Factory Function // ============================================================================ /** * Create a Claude Code integration instance with sensible defaults */ export function createClaudeCodeIntegration( config?: ClaudeCodeConfig ): ClaudeCodeIntegration { return new ClaudeCodeIntegration(config); } // ============================================================================ // Export // ============================================================================ export default ClaudeCodeIntegration;