@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
JavaScript
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
};