@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
text/typescript
/**
* 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);
}
});
}
}