UNPKG

@codemafia0000/d0

Version:

Claude Multi-Agent Automated Development AI - Revolutionary development environment where multiple AI agents collaborate to automate software development

296 lines (256 loc) • 10 kB
const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const { spawn } = require('child_process'); const CONSTANTS = require('../constants'); const { isProjectSetup, getProjectConfig } = require('../setup/project-setup'); const { sendMessageToAgent, checkCommunicationStatus, clearCommunicationNotifications } = require('../communication/message-handler'); // Tell command handler async function handleTell(messageOrRecipient, messageRest, options) { let recipient = 'president'; let message = ''; // Get valid recipients list const validRecipients = CONSTANTS.VALID_RECIPIENTS.slice(); // copy array try { const configData = getProjectConfig(); const numWorkers = configData.workers || CONSTANTS.DEFAULT_WORKERS; for (let i = 1; i <= numWorkers; i++) { validRecipients.push(`worker${i}`); } } catch { // Fallback validRecipients.push('worker1', 'worker2', 'worker3'); } // Handle different argument patterns if (options.file) { // File mode: d0 tell --file path or d0 tell recipient --file path if (validRecipients.includes(messageOrRecipient)) { recipient = messageOrRecipient; } else { recipient = 'president'; } message = ''; // Will be read from file below } else if (!messageRest || messageRest.length === 0) { // Single argument: d0 tell "message" message = messageOrRecipient; recipient = 'president'; } else { // Multiple arguments: d0 tell recipient message... if (validRecipients.includes(messageOrRecipient)) { recipient = messageOrRecipient; message = messageRest.join(' '); } else { // First arg isn't a recipient, treat everything as message message = [messageOrRecipient, ...messageRest].join(' '); recipient = 'president'; } } if (!isProjectSetup()) { console.log(chalk.red('āŒ Claude agents environment not found.')); console.log(chalk.yellow('Run "d0 init" first.')); return; } // Check if sessions are running const sessionFile = CONSTANTS.SESSIONS_FILE; if (!fs.existsSync(sessionFile)) { console.log(chalk.red('āŒ No agent sessions are running.')); console.log(chalk.yellow('Start sessions with: d0 start')); return; } let finalMessage = message; // Read message from file if specified if (options.file) { try { finalMessage = fs.readFileSync(options.file, 'utf8').trim(); } catch (error) { console.log(chalk.red(`āŒ Error reading file: ${error.message}`)); return; } } // Handle 'everyone' as special case for broadcast if (recipient.toLowerCase() === 'everyone') { // Broadcast to all agents const recipients = ['president', 'boss']; // Get numWorkers from configuration let numWorkers = CONSTANTS.DEFAULT_WORKERS; if (fs.existsSync(CONSTANTS.D0_DIR)) { try { const configData = getProjectConfig(); numWorkers = configData.workers || CONSTANTS.DEFAULT_WORKERS; for (let i = 1; i <= numWorkers; i++) { recipients.push(`worker${i}`); } } catch { recipients.push('worker1', 'worker2', 'worker3'); } } console.log(chalk.blue(`šŸ“¢ Telling everyone (${recipients.length} agents)...`)); let successCount = 0; let failCount = 0; for (const rec of recipients) { try { const success = sendMessageToAgent(rec, finalMessage, numWorkers); if (success) { successCount++; } else { failCount++; } } catch (error) { console.error(chalk.red(`Failed to send to ${rec}: ${error.message}`)); failCount++; } } console.log(chalk.green(`\nāœ… Broadcast complete: ${successCount} sent, ${failCount} failed`)); return; } // Send to single recipient try { let numWorkers = CONSTANTS.DEFAULT_WORKERS; if (fs.existsSync(CONSTANTS.D0_DIR)) { try { const configData = getProjectConfig(); numWorkers = configData.workers || CONSTANTS.DEFAULT_WORKERS; } catch (error) { console.warn(chalk.yellow(`Warning: Could not read config: ${error.message}`)); } } const success = sendMessageToAgent(recipient, finalMessage, numWorkers); if (!success) { process.exit(1); } } catch (error) { console.error(chalk.red(`āŒ Failed to send message: ${error.message}`)); process.exit(1); } } // Communication status command handler async function handleCommunication(options) { if (!isProjectSetup()) { console.log(chalk.red('āŒ Claude agents environment not found.')); console.log(chalk.yellow('Run "d0 init" first.')); return; } console.log(chalk.blue('šŸ“” Agent Communication Status')); console.log('─'.repeat(50)); const { roles, hasMessages } = checkCommunicationStatus(); if (Object.keys(roles).length === 0) { console.log(chalk.yellow('šŸ“­ No inbox directory found')); return; } for (const [role, status] of Object.entries(roles)) { const statusText = status.hasUnread ? chalk.red('šŸ”” UNREAD') : chalk.green('āœ“ Read'); console.log(chalk.white(`${role.toUpperCase().padEnd(10)} ${status.messageCount} messages ${statusText}`)); if (status.hasUnread && options.clear) { const notificationFile = CONSTANTS.getInboxPath(`${role}_notification.txt`); try { fs.unlinkSync(notificationFile); console.log(chalk.gray(` → Cleared notification for ${role}`)); } catch (error) { console.warn(chalk.yellow(` āš ļø Could not clear notification for ${role}`)); } } } // Show message status const statusFile = CONSTANTS.MESSAGE_STATUS_FILE; if (fs.existsSync(statusFile)) { try { const messageStatus = JSON.parse(fs.readFileSync(statusFile, 'utf8')); const recentMessages = Object.values(messageStatus) .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)) .slice(0, 5); if (recentMessages.length > 0) { console.log(chalk.blue('\nšŸ“‹ Recent Messages:')); recentMessages.forEach(msg => { const time = new Date(msg.timestamp).toLocaleTimeString(); console.log(chalk.gray(` ${time} → ${msg.recipient}: ${msg.message}`)); }); } } catch (error) { console.warn(chalk.yellow('Warning: Could not read message status')); } } if (options.clear) { const clearedCount = clearCommunicationNotifications(); if (clearedCount > 0) { console.log(chalk.green(`\nāœ… Cleared ${clearedCount} notification(s)`)); } } console.log(chalk.gray('\nšŸ’” Tip: Use --clear to mark all messages as read')); } // Team completion command handler async function handleTeam(options) { if (!isProjectSetup()) { console.log(chalk.red('āŒ Claude agents environment not found.')); console.log(chalk.yellow('Run "d0 init" first.')); return; } const scriptPath = path.join(__dirname, '..', '..', 'scripts', 'check_team_completion.js'); const args = []; if (options.statusOnly) args.push('--status-only'); if (options.reset) args.push('--reset'); if (options.workers) args.push('--workers', options.workers); try { const child = spawn('node', [scriptPath, ...args], { stdio: 'inherit', cwd: process.cwd() }); child.on('exit', (code) => { process.exit(code || 0); }); child.on('error', (error) => { console.error(chalk.red('āŒ Failed to run team checker:'), error.message); process.exit(1); }); } catch (error) { console.error(chalk.red('āŒ Error running team checker:'), error.message); process.exit(1); } } // Hi command handler - Quick connect to an agent session function handleHi(session) { if (!isProjectSetup()) { console.log(chalk.red('āŒ Claude agents environment not found.')); console.log(chalk.yellow('Run "d0 init" first.')); return; } console.log(chalk.blue(`šŸ‘‹ Hi ${session.toUpperCase()}!`)); // Check for messages first const inboxFile = CONSTANTS.getInboxPath(`${session.toLowerCase()}.txt`); const notificationFile = CONSTANTS.getInboxPath(`${session.toLowerCase()}_notification.txt`); if (fs.existsSync(notificationFile)) { console.log(chalk.yellow('šŸ“¬ You have new messages!')); if (fs.existsSync(inboxFile)) { console.log(chalk.cyan('\nMessages:')); const messages = fs.readFileSync(inboxFile, 'utf8').trim(); console.log(messages); // Mark as read fs.unlinkSync(notificationFile); console.log(chalk.green('\nāœ… Messages marked as read')); } } else { console.log(chalk.gray('šŸ“­ No new messages')); } console.log(chalk.blue(`\nšŸŽÆ Connected as ${session.toUpperCase()}`)); console.log(chalk.gray('You can now work on tasks and communicate with other agents.')); // Show relevant commands for this role if (session.toLowerCase() === 'president') { console.log(chalk.yellow('\nšŸ’” President Commands:')); console.log(chalk.gray(' d0 tell boss "Create a React app" - Delegate to boss')); console.log(chalk.gray(' d0 status - Check overall status')); } else if (session.toLowerCase() === 'boss') { console.log(chalk.yellow('\nšŸ’” Boss Commands:')); console.log(chalk.gray(' d0 tell worker1 "Build UI" - Assign to worker')); console.log(chalk.gray(' d0 team - Check team progress')); console.log(chalk.gray(' d0 tell president "Update" - Report to president')); } else if (session.toLowerCase().startsWith('worker')) { console.log(chalk.yellow('\nšŸ’” Worker Commands:')); console.log(chalk.gray(' d0 tell boss "Task completed" - Report completion')); console.log(chalk.gray(' d0 tell boss "Need help with X" - Request assistance')); } } module.exports = { handleTell, handleCommunication, handleTeam, handleHi };