UNPKG

@jimmy2822/claude-code-sub-agents-mode

Version:

TypeScript Clean Architecture Multi-Agent Framework for Claude Code CLI Integration

152 lines (129 loc) 3.91 kB
/** * WebSocket Handler - Presentation Layer * Handles WebSocket connections and messages */ import { WebSocket } from 'ws'; import { IProjectRepository } from '../../domain/repositories/IProjectRepository'; import { CreateProjectUseCase } from '../../application/use-cases/CreateProjectUseCase'; import { ApproveProjectUseCase } from '../../application/use-cases/ApproveProjectUseCase'; export class WebSocketHandler { private clients: Set<WebSocket> = new Set(); constructor( private projectRepository: IProjectRepository, private createProjectUseCase: CreateProjectUseCase, private approveProjectUseCase: ApproveProjectUseCase ) {} handleConnection(ws: WebSocket): void { this.clients.add(ws); ws.on('message', async (message) => { try { const data = JSON.parse(message.toString()); await this.handleMessage(ws, data); } catch (error) { ws.send(JSON.stringify({ type: 'error', message: 'Invalid message format' })); } }); ws.on('close', () => { this.clients.delete(ws); console.log('🔌 Client disconnected'); }); // Send initial status this.sendStatus(ws); } private async handleMessage(ws: WebSocket, data: any): Promise<void> { switch (data.type) { case 'analyze-requirements': await this.handleAnalyzeRequirements(ws, data); break; case 'approve-breakdown': await this.handleApproveBreakdown(ws, data); break; case 'start-execution': await this.handleStartExecution(ws, data); break; case 'get-status': await this.sendStatus(ws); break; default: ws.send(JSON.stringify({ type: 'error', message: `Unknown message type: ${data.type}` })); } } private async handleAnalyzeRequirements(ws: WebSocket, data: any): Promise<void> { const result = await this.createProjectUseCase.execute({ requirement: data.requirement }); if (result.success) { this.broadcast({ type: 'project-created', project: result.project }); } else { ws.send(JSON.stringify({ type: 'error', message: result.message })); } } private async handleApproveBreakdown(ws: WebSocket, data: any): Promise<void> { const result = await this.approveProjectUseCase.execute({ projectId: data.projectId }); if (result.success) { const project = await this.projectRepository.findById(data.projectId); this.broadcast({ type: 'project-approved', project }); } else { ws.send(JSON.stringify({ type: 'error', message: result.message })); } } private async handleStartExecution(ws: WebSocket, data: any): Promise<void> { const project = await this.projectRepository.findById(data.projectId); if (!project) { ws.send(JSON.stringify({ type: 'error', message: 'Project not found' })); return; } if (!project.canExecute()) { ws.send(JSON.stringify({ type: 'error', message: 'Project cannot be executed in current status' })); return; } project.updateStatus('executing'); project.addUpdate('Execution started'); await this.projectRepository.update(project); this.broadcast({ type: 'execution-started', project }); } private async sendStatus(ws: WebSocket): Promise<void> { const projects = await this.projectRepository.findAll(); ws.send(JSON.stringify({ type: 'status', projects })); } private broadcast(message: any): void { const data = JSON.stringify(message); this.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(data); } }); } }