@codemafia0000/d0
Version:
Claude Multi-Agent Automated Development AI - Revolutionary development environment where multiple AI agents collaborate to automate software development
187 lines (152 loc) ⢠6.32 kB
JavaScript
const fs = require('fs');
const path = require('path');
const os = require('os');
const { execSync, spawn } = require('child_process');
const ora = require('ora');
const chalk = require('chalk');
const CONSTANTS = require('../constants');
const { setupShellCompletion } = require('./shell-completion');
const { updateGitignore } = require('../utils/git-utils');
const { getHistoryDir, logSessionEvent } = require('../history/session-history');
const { hookAgentCommunication } = require('../communication/agent-communication');
const { setupClaudeAutoApprove, setClaudeEnvironment } = require('../utils/claude-config');
function ensureConfigDir() {
const homeDir = os.homedir();
const configDir = path.join(homeDir, '.d0');
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
return configDir;
}
function checkClaudeCLI() {
try {
execSync('claude --version', { stdio: 'ignore', timeout: 5000 });
return true;
} catch {
return false;
}
}
async function setupProject(options) {
const spinner = ora('Setting up Claude agents environment...').start();
try {
// Validate number of workers
const numWorkers = parseInt(options.workers, 10);
if (isNaN(numWorkers) || numWorkers < CONSTANTS.MIN_WORKERS || numWorkers > CONSTANTS.MAX_WORKERS) {
spinner.fail(`Number of workers must be between ${CONSTANTS.MIN_WORKERS} and ${CONSTANTS.MAX_WORKERS}`);
process.exit(1);
}
// Check prerequisites (Claude CLI only, no tmux required)
spinner.text = 'Checking prerequisites...';
if (!checkClaudeCLI()) {
spinner.warn('Claude CLI is not found - you may need to install it later.');
console.log(chalk.yellow('š” Install Claude CLI when ready: https://docs.anthropic.com/en/docs/claude-code'));
} else {
spinner.text = 'Claude CLI found';
}
// Create .d0 directory first
const currentDir = CONSTANTS.PROJECT_ROOT;
const d0Dir = CONSTANTS.D0_DIR;
// Handle existing .d0 (file or directory)
if (fs.existsSync(d0Dir)) {
const stats = fs.statSync(d0Dir);
if (stats.isFile()) {
// Remove existing .d0 file to create directory
fs.unlinkSync(d0Dir);
fs.mkdirSync(d0Dir);
} else if (stats.isDirectory()) {
// Directory already exists, that's fine
}
} else {
fs.mkdirSync(d0Dir);
}
// Skip Claude CLI authentication - user can authenticate manually when needed
spinner.text = 'Skipping Claude CLI authentication (will be done when needed)...';
// Setup Claude CLI auto-approve mode
spinner.text = 'Setting up Claude CLI auto-approve mode...';
setupClaudeAutoApprove();
setClaudeEnvironment();
// Check existing project
if (isProjectSetup() && !options.force) {
spinner.fail('Claude agents environment already exists in this directory.');
console.log(chalk.yellow('Use --force to overwrite existing setup.'));
process.exit(1);
}
spinner.text = 'Copying template files...';
// Copy template files
const templatePath = CONSTANTS.TEMPLATES_DIR;
// Copy main files
const filesToCopy = CONSTANTS.TEMPLATE_FILES;
for (const file of filesToCopy) {
const srcPath = path.join(templatePath, file);
const destPath = path.join(currentDir, file);
if (fs.existsSync(srcPath)) {
fs.copyFileSync(srcPath, destPath);
console.log(` ā Copied ${file}`);
}
}
// Create tmp subdirectory
const setupTmpDir = CONSTANTS.TMP_DIR;
if (!fs.existsSync(setupTmpDir)) {
fs.mkdirSync(setupTmpDir);
}
// Create instructions subdirectory and copy instructions files
const instructionsDir = CONSTANTS.INSTRUCTIONS_DIR;
if (!fs.existsSync(instructionsDir)) {
fs.mkdirSync(instructionsDir);
}
// Copy instructions files to .d0/instructions/
const templateInstructionsPath = path.join(CONSTANTS.TEMPLATES_DIR, 'instructions');
if (fs.existsSync(templateInstructionsPath)) {
fs.cpSync(templateInstructionsPath, instructionsDir, { recursive: true, force: true });
}
// Create config.json
fs.writeFileSync(CONSTANTS.CONFIG_FILE, JSON.stringify({
created: new Date().toISOString(),
version: CONSTANTS.VERSION,
workers: numWorkers
}, null, 2));
// Initialize history management
getHistoryDir();
hookAgentCommunication();
// Update .gitignore to exclude temporary files
const gitignoreUpdated = updateGitignore(currentDir);
// Log session start
await logSessionEvent('setup', { type: 'basic' });
spinner.succeed('Claude agents environment setup completed!');
console.log(chalk.green('\nā
Setup completed successfully!'));
if (gitignoreUpdated) {
console.log(chalk.blue('š Updated .gitignore to exclude temporary files'));
}
// Setup shell completion
await setupShellCompletion();
console.log(chalk.green('\nš§ Configuration complete:'));
console.log(chalk.gray(' ⢠Claude CLI authentication: ') + chalk.yellow('ā Manual setup required (run: claude auth login)'));
console.log(chalk.gray(' ⢠Agent configuration: ') + chalk.green('ā Ready'));
console.log(chalk.gray(' ⢠Communication system: ') + chalk.green('ā Initialized'));
console.log(chalk.yellow('\nš Ready to start:'));
console.log(chalk.blue(' d0 start') + chalk.gray(' - Launch all agents automatically'));
console.log(chalk.gray('\nThen:'));
console.log(chalk.blue(' d0 tell "Create a todo app"') + chalk.gray(' - Start your first project'));
} catch (error) {
spinner.fail('Setup failed');
console.error(chalk.red(error.message));
process.exit(1);
}
}
function isProjectSetup() {
return fs.existsSync(CONSTANTS.D0_DIR) && fs.existsSync(CONSTANTS.CONFIG_FILE);
}
function getProjectConfig() {
if (!isProjectSetup()) {
throw new Error('Project not initialized. Run "d0 init" first.');
}
const configData = JSON.parse(fs.readFileSync(CONSTANTS.CONFIG_FILE, 'utf8'));
return configData;
}
module.exports = {
setupProject,
isProjectSetup,
getProjectConfig,
ensureConfigDir,
checkClaudeCLI
};