pdca
Version:
🎯 AI 驅動的 PDCA 多代理開發系統 - 智能循環控制 + 成本管理 + Token 優化 + 多 AI 引擎支援
291 lines (285 loc) • 9.29 kB
JavaScript
/**
* Multi-Claude CLI Manager
* 管理多個獨立的 Claude CLI 實例
*/
import { spawn } from 'child_process';
import { EventEmitter } from 'events';
import { writeFileSync, mkdirSync, existsSync } from 'fs';
import { join } from 'path';
export class MultiClaudeManager extends EventEmitter {
agents = new Map();
communicationDir;
sessionName;
constructor(sessionName = 'pdca') {
super();
this.sessionName = sessionName;
this.communicationDir = '.raiy-pdca/communication';
this.initializeCommunicationDir();
}
initializeCommunicationDir() {
const dirs = [
'.raiy-pdca',
'.raiy-pdca/communication',
'.raiy-pdca/agents',
'.raiy-pdca/logs',
'.pdca'
];
dirs.forEach(dir => {
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
});
}
/**
* 初始化代理配置
*/
initializeAgents() {
const agentConfigs = [
{
name: 'plan',
role: '規劃師',
windowIndex: 0,
prompt: `你是 PDCA 系統中的 Plan 代理。你的職責是:
1. 分析用戶需求,制定詳細計畫
2. 將任務分解為可執行的步驟
3. 與其他代理協調工作
4. 監控 .raiy-pdca/communication/ 目錄接收任務
5. 將計畫寫入 .raiy-pdca/communication/plan.output
請持續監聽新任務並主動規劃。`
},
{
name: 'do',
role: '執行者',
windowIndex: 1,
prompt: `你是 PDCA 系統中的 Do 代理。你的職責是:
1. 根據 Plan 代理的計畫實作功能
2. 編寫高品質的程式碼
3. 監控 .raiy-pdca/communication/ 目錄接收任務
4. 將實作結果寫入 .raiy-pdca/communication/do.output
請持續監聽任務並執行實作。`
},
{
name: 'check',
role: '檢查員',
windowIndex: 2,
prompt: `你是 PDCA 系統中的 Check 代理。你的職責是:
1. 檢查 Do 代理的實作品質
2. 執行測試和驗證
3. 監控 .raiy-pdca/communication/ 目錄
4. 將檢查結果寫入 .raiy-pdca/communication/check.output
請持續監控並提供品質回饋。`
},
{
name: 'act',
role: '改善者',
windowIndex: 3,
prompt: `你是 PDCA 系統中的 Act 代理。你的職責是:
1. 根據 Check 的結果進行優化
2. 實施改進措施
3. 監控 .raiy-pdca/communication/ 目錄
4. 將改進結果寫入 .raiy-pdca/communication/act.output
請持續尋找改進機會。`
},
{
name: 'knowledge',
role: '知識管理',
windowIndex: 4,
prompt: `你是 PDCA 系統中的 Knowledge 代理。你的職責是:
1. 記錄所有代理的重要決策和學習
2. 維護專案知識庫
3. 監控 .raiy-pdca/communication/ 目錄
4. 將知識整理到 memories/ 目錄
請持續收集和整理知識。`
}
];
agentConfigs.forEach(config => {
this.agents.set(config.name, {
...config,
status: 'stopped'
});
});
}
/**
* 啟動所有代理
*/
async startAllAgents() {
// 先確保沒有現有的 session
await this.killExistingSession();
// 創建新的 tmux session
await this.createTmuxSession();
// 啟動每個代理
for (const [name, agent] of this.agents) {
await this.startAgent(name);
}
// 啟動監控窗口
await this.startMonitor();
}
/**
* 停止現有 session
*/
async killExistingSession() {
return new Promise((resolve) => {
const kill = spawn('tmux', ['kill-session', '-t', this.sessionName], {
stdio: 'pipe'
});
kill.on('close', () => resolve());
kill.on('error', () => resolve());
});
}
/**
* 創建 tmux session
*/
async createTmuxSession() {
return new Promise((resolve, reject) => {
const create = spawn('tmux', [
'new-session', '-d', '-s', this.sessionName, '-n', 'plan'
], { stdio: 'pipe' });
create.on('close', (code) => {
if (code === 0) {
resolve();
}
else {
reject(new Error('Failed to create tmux session'));
}
});
});
}
/**
* 啟動單個代理
*/
async startAgent(name) {
const agent = this.agents.get(name);
if (!agent)
return;
agent.status = 'starting';
// 寫入初始 prompt
const promptFile = join(this.communicationDir, `${name}.prompt`);
writeFileSync(promptFile, agent.prompt);
// 創建啟動腳本
const startScript = `#!/bin/bash
echo "🚀 啟動 ${agent.role} (${name})"
echo "${agent.prompt}" > ${promptFile}
echo "代理已初始化,等待任務..."
echo "監控目錄: ${this.communicationDir}"
# 持續運行,監控任務
while true; do
if [ -f "${this.communicationDir}/current.task" ]; then
echo "📋 收到新任務"
cat "${this.communicationDir}/current.task"
fi
sleep 5
done
`;
const scriptFile = join('.raiy-pdca', `start-${name}.sh`);
writeFileSync(scriptFile, startScript, { mode: 0o755 });
// 在 tmux 中啟動
if (agent.windowIndex === 0) {
// 第一個窗口已存在,直接發送命令
await this.sendToTmux(this.sessionName, agent.windowIndex, `bash ${scriptFile}`);
}
else {
// 創建新窗口
await this.createTmuxWindow(name, agent.windowIndex);
await this.sendToTmux(this.sessionName, agent.windowIndex, `bash ${scriptFile}`);
}
agent.status = 'running';
this.updateAgentStatus(name, 'running');
}
/**
* 創建 tmux 窗口
*/
async createTmuxWindow(name, index) {
return new Promise((resolve, reject) => {
const create = spawn('tmux', [
'new-window', '-t', `${this.sessionName}:${index}`, '-n', name
], { stdio: 'pipe' });
create.on('close', (code) => {
if (code === 0) {
resolve();
}
else {
reject(new Error(`Failed to create window for ${name}`));
}
});
});
}
/**
* 發送命令到 tmux 窗口
*/
async sendToTmux(session, window, command) {
return new Promise((resolve, reject) => {
const send = spawn('tmux', [
'send-keys', '-t', `${session}:${window}`, command, 'Enter'
], { stdio: 'pipe' });
send.on('close', (code) => {
if (code === 0) {
resolve();
}
else {
reject(new Error('Failed to send command to tmux'));
}
});
});
}
/**
* 啟動監控
*/
async startMonitor() {
await this.createTmuxWindow('monitor', 5);
await this.sendToTmux(this.sessionName, 5, 'node dist/core/monitor.js');
}
/**
* 更新代理狀態
*/
updateAgentStatus(name, status) {
const statusFile = join('.raiy-pdca/agents', `${name}.status`);
const agent = this.agents.get(name);
if (agent) {
writeFileSync(statusFile, `${agent.role} - ${status}\n時間: ${new Date().toISOString()}`);
}
}
/**
* 發送任務給所有代理
*/
async sendTask(task) {
const taskMessage = {
type: 'TASK',
content: task,
timestamp: new Date().toISOString(),
from: 'coordinator'
};
const taskFile = join(this.communicationDir, 'current.task');
writeFileSync(taskFile, JSON.stringify(taskMessage, null, 2));
// 通知所有代理
for (const [name] of this.agents) {
const notifyFile = join(this.communicationDir, `${name}.notify`);
writeFileSync(notifyFile, `新任務: ${task}`);
}
this.emit('task-sent', task);
}
/**
* 獲取所有代理狀態
*/
getAgentStatuses() {
return this.agents;
}
/**
* 停止所有代理
*/
async stopAllAgents() {
// 發送停止信號
const stopFile = join(this.communicationDir, 'system.cmd');
writeFileSync(stopFile, 'SYSTEM_SHUTDOWN');
// 等待代理回應
await new Promise(resolve => setTimeout(resolve, 2000));
// 停止 tmux session
await this.killExistingSession();
// 更新狀態
for (const [name] of this.agents) {
this.updateAgentStatus(name, 'stopped');
}
this.emit('system-stopped');
}
}
export default MultiClaudeManager;
//# sourceMappingURL=multi-claude-manager.js.map