- Added Claude Code integration with full context compaction support - Added OpenClaw integration with deterministic pipeline support - Implemented parallel agent execution (4 projects x 3 roles pattern) - Added workspace isolation with permissions and quotas - Implemented Lobster-compatible YAML workflow parser - Added persistent memory store for cross-session context - Created comprehensive README with hero section This project was 100% autonomously built by Z.AI GLM-5
655 lines
19 KiB
TypeScript
655 lines
19 KiB
TypeScript
/**
|
|
* 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<string, any>;
|
|
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<string, any>;
|
|
timeout?: number;
|
|
priority?: 'low' | 'medium' | 'high';
|
|
}
|
|
|
|
export interface SubagentResult {
|
|
success: boolean;
|
|
output: string;
|
|
tokens: number;
|
|
duration: number;
|
|
filesModified?: string[];
|
|
artifacts?: Record<string, any>;
|
|
}
|
|
|
|
// ============================================================================
|
|
// 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<ClaudeCodeConfig>;
|
|
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<CompactionResult> {
|
|
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<SubagentResult> {
|
|
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<SubagentResult[]> {
|
|
return Promise.all(tasks.map(task => this.spawnSubagent(task)));
|
|
}
|
|
|
|
// ============================================================================
|
|
// Memory Management
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Store a value in persistent memory
|
|
*/
|
|
async remember(key: string, value: any): Promise<void> {
|
|
if (this.memoryStore) {
|
|
await this.memoryStore.set(`session:${this.sessionId}:${key}`, value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve a value from persistent memory
|
|
*/
|
|
async recall<T>(key: string): Promise<T | null> {
|
|
if (this.memoryStore) {
|
|
return this.memoryStore.get<T>(`session:${this.sessionId}:${key}`);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Store important context for cross-session persistence
|
|
*/
|
|
async saveContext(name: string): Promise<void> {
|
|
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<boolean> {
|
|
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<ClaudeCodeConfig>;
|
|
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<typeof this.exportSession>): 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;
|