@deep-assistant/hive-mind
Version:
AI-powered issue solver and hive mind for collaborative problem solving
314 lines (264 loc) ⢠10.2 kB
JavaScript
// Early exit paths - handle these before loading all modules to speed up testing
const earlyArgs = process.argv.slice(2);
if (earlyArgs.includes('--version')) {
// Quick version output without loading modules
const { readFileSync } = await import('fs');
const { dirname, join } = await import('path');
const { fileURLToPath } = await import('url');
const { getGitVersion } = await import('./git.lib.mjs');
const { execSync } = await import('child_process');
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packagePath = join(__dirname, '..', 'package.json');
try {
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
const currentVersion = packageJson.version;
const version = await getGitVersion(execSync, currentVersion);
console.log(version);
} catch (versionError) {
// Fallback to hardcoded version if all else fails
console.log('0.10.4');
}
process.exit(0);
}
if (earlyArgs.includes('--help') || earlyArgs.includes('-h')) {
// Show help and exit
console.log('Usage: task.mjs <task-description> [options]');
console.log('\nOptions:');
console.log(' --version Show version number');
console.log(' --help, -h Show help');
console.log(' --clarify Enable clarification mode [default: true]');
console.log(' --decompose Enable decomposition mode [default: true]');
console.log(' --only-clarify Only run clarification mode');
console.log(' --only-decompose Only run decomposition mode');
console.log(' --model, -m Model to use (opus or sonnet) [default: sonnet]');
console.log(' --verbose, -v Enable verbose logging');
console.log(' --output-format Output format (text or json) [default: text]');
process.exit(0);
}
// Use use-m to dynamically import modules for cross-runtime compatibility
const { use } = eval(await (await fetch('https://unpkg.com/use-m/use.js')).text());
// Use command-stream for consistent $ behavior across runtimes
const { $ } = await use('command-stream');
const yargs = (await use('yargs@latest')).default;
const os = (await use('os')).default;
const path = (await use('path')).default;
const fs = (await use('fs')).promises;
const crypto = (await use('crypto')).default;
const { spawn } = (await use('child_process')).default;
// Import Claude execution functions
import { validateClaudeConnection } from './claude.lib.mjs';
// Global log file reference
let logFile = null;
// Helper function to log to both console and file
const log = async (message, options = {}) => {
const { level = 'info', verbose = false } = options;
// Skip verbose logs unless --verbose is enabled
if (verbose && !global.verboseMode) {
return;
}
// Write to file if log file is set
if (logFile) {
const logMessage = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message}`;
await fs.appendFile(logFile, logMessage + '\n').catch(() => {});
}
// Write to console based on level
switch (level) {
case 'error':
console.error(message);
break;
case 'warning':
case 'warn':
console.warn(message);
break;
case 'info':
default:
console.log(message);
break;
}
};
// Configure command line arguments - task description as positional argument
const argv = yargs(process.argv.slice(2))
.usage('Usage: $0 <task-description> [options]')
.positional('task-description', {
type: 'string',
description: 'The task to clarify and decompose'
})
.option('clarify', {
type: 'boolean',
description: 'Enable clarification mode (asks clarifying questions about the task)',
default: true
})
.option('decompose', {
type: 'boolean',
description: 'Enable decomposition mode (breaks down the task into subtasks)',
default: true
})
.option('only-clarify', {
type: 'boolean',
description: 'Only run clarification mode, skip decomposition',
default: false
})
.option('only-decompose', {
type: 'boolean',
description: 'Only run decomposition mode, skip clarification',
default: false
})
.option('model', {
type: 'string',
description: 'Model to use (opus or sonnet)',
alias: 'm',
default: 'sonnet',
choices: ['opus', 'sonnet']
})
.option('verbose', {
type: 'boolean',
description: 'Enable verbose logging for debugging',
alias: 'v',
default: false
})
.option('output-format', {
type: 'string',
description: 'Output format (text or json)',
alias: 'o',
default: 'text',
choices: ['text', 'json']
})
.check((argv) => {
if (!argv._[0]) {
throw new Error('Please provide a task description');
}
// Handle mutual exclusivity of only-clarify and only-decompose
if (argv['only-clarify'] && argv['only-decompose']) {
throw new Error('Cannot use both --only-clarify and --only-decompose at the same time');
}
// If only-clarify is set, disable decompose
if (argv['only-clarify']) {
argv.decompose = false;
}
// If only-decompose is set, disable clarify
if (argv['only-decompose']) {
argv.clarify = false;
}
return true;
})
.help()
.alias('h', 'help')
.argv;
const taskDescription = argv._[0];
// Set global verbose mode for log function
global.verboseMode = argv.verbose;
// Create permanent log file immediately with timestamp
const scriptDir = path.dirname(process.argv[1]);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
logFile = path.join(scriptDir, `task-${timestamp}.log`);
// Create the log file immediately
await fs.writeFile(logFile, `# Task.mjs Log - ${new Date().toISOString()}\n\n`);
await log(`š Log file: ${logFile}`);
await log(` (All output will be logged here)`);
// Helper function to format aligned console output
const formatAligned = (icon, label, value, indent = 0) => {
const spaces = ' '.repeat(indent);
const labelWidth = 25 - indent;
const paddedLabel = label.padEnd(labelWidth, ' ');
return `${spaces}${icon} ${paddedLabel} ${value || ''}`;
};
await log(`\nšÆ Task Processing Started`);
await log(formatAligned('š', 'Task description:', taskDescription));
await log(formatAligned('š¤', 'Model:', argv.model));
await log(formatAligned('š”', 'Clarify mode:', argv.clarify ? 'enabled' : 'disabled'));
await log(formatAligned('š', 'Decompose mode:', argv.decompose ? 'enabled' : 'disabled'));
await log(formatAligned('š', 'Output format:', argv.outputFormat));
const claudePath = process.env.CLAUDE_PATH || 'claude';
// Helper function to execute Claude command
const executeClaude = (prompt, model) => {
return new Promise((resolve, reject) => {
const args = [
'-p', prompt,
'--output-format', 'text',
'--dangerously-skip-permissions',
'--model', model
];
const child = spawn(claudePath, args, {
stdio: ['ignore', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
resolve(stdout.trim());
} else {
reject(new Error(`Claude exited with code ${code}: ${stderr}`));
}
});
child.on('error', (error) => {
reject(error);
});
});
};
try {
const results = {
task: taskDescription,
timestamp: new Date().toISOString(),
clarification: null,
decomposition: null
};
// Phase 1: Clarification
if (argv.clarify) {
await log(`\nš¤ Phase 1: Task Clarification`);
await log(` Analyzing task and generating clarifying questions...`);
const clarifyPrompt = `Task: "${taskDescription}"
Please help clarify this task by:
1. Identifying any ambiguous aspects of the task
2. Asking 3-5 specific clarifying questions that would help someone implement this task more effectively
3. Suggesting potential assumptions that could be made if these questions aren't answered
4. Identifying any missing context or requirements
Provide your response in a clear, structured format that helps refine the task understanding.`;
const clarificationOutput = await executeClaude(clarifyPrompt, argv.model);
if (!argv.verbose) {
console.log('\nš Clarification Results:');
console.log(clarificationOutput);
}
results.clarification = clarificationOutput;
await log(`\nā
Clarification phase completed`);
}
// Phase 2: Decomposition
if (argv.decompose) {
await log(`\nš Phase 2: Task Decomposition`);
await log(` Breaking down task into manageable subtasks...`);
let decomposePrompt = `Task: "${taskDescription}"`;
if (results.clarification) {
decomposePrompt += `\n\nClarification analysis:\n${results.clarification}`;
}
decomposePrompt += `\n\nPlease decompose this task by:
1. Breaking it down into 3-8 specific, actionable subtasks
2. Ordering the subtasks logically (dependencies and sequence)
3. Estimating relative complexity/effort for each subtask (simple/medium/complex)
4. Identifying any potential risks or challenges for each subtask
5. Suggesting success criteria for each subtask
Provide your response as a structured breakdown that someone could use as a implementation roadmap.`;
const decompositionOutput = await executeClaude(decomposePrompt, argv.model);
if (!argv.verbose) {
console.log('\nš Decomposition Results:');
console.log(decompositionOutput);
}
results.decomposition = decompositionOutput;
await log(`\nā
Decomposition phase completed`);
}
// Output results
if (argv.outputFormat === 'json') {
console.log('\n' + JSON.stringify(results, null, 2));
}
await log(`\nš Task processing completed successfully`);
await log(`š” Review the session log for details: ${logFile}`);
} catch (error) {
await log(`ā Error processing task: ${error.message}`, { level: 'error' });
process.exit(1);
}