claude-code-company
Version:
Multi-agent tmux coordination system for Claude Code with perfect Unicode support
161 lines (136 loc) • 6.56 kB
JavaScript
const readline = require('readline');
const { colors } = require('./config');
const { setupSystem } = require('./setup');
const { chainCommand, destroyPanes } = require('./tasks');
// colors変数の検証
if (!colors || typeof colors !== 'object') {
console.error('❌ Error: colors configuration not loaded properly');
process.exit(1);
}
// 安全な入力取得
function safeRead(prompt) {
return new Promise((resolve) => {
// 各質問に対して新しいreadlineインスタンスを作成
const rlInstance = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rlInstance.question(prompt, (answer) => {
rlInstance.close();
resolve(answer);
});
});
}
// 超シンプルメニュー
async function interactiveMenu() {
try {
errorStats.menuCallCount++;
errorStats.isMenuActive = true;
console.log(`${colors.purple}🎯 Claude Code Company - 超軽量システム${colors.nc}\n`);
console.log(`${colors.yellow}📋 基本操作メニュー${colors.nc}`);
console.log(` 1) 🔧 setup - tmuxセッション初期化`);
console.log(` 2) 📤 send - 指示・報告送信`);
console.log(` 3) 🛠️ destroy - pane 0以外を削除`);
console.log(` 4) 👋 exit - 終了\n`);
const choice = await safeRead(`${colors.green}🎯 選択してください (1-4): ${colors.nc}`);
// 入力値の検証と正規化
const normalizedChoice = choice.trim();
if (!normalizedChoice || !/^[1-4]$/.test(normalizedChoice)) {
console.log(`${colors.red}❌ 無効な選択: "${choice}". 1-4の数字を入力してください${colors.nc}`);
await promptForContinue();
return;
}
switch (normalizedChoice) {
case '1':
console.log(`${colors.blue}⚙️ システムセットアップを開始...${colors.nc}`);
await setupSystem(3, null, 1);
console.log(`${colors.green}✅ セットアップ完了${colors.nc}`);
break;
case '2':
console.log(`\n${colors.yellow}📋 送信先一覧${colors.nc}`);
console.log(`${colors.blue}┌─────────────┬────────────────────────────────────────┐${colors.nc}`);
console.log(`${colors.blue}│ 送信先 │ 説明 │${colors.nc}`);
console.log(`${colors.blue}├─────────────┼────────────────────────────────────────┤${colors.nc}`);
console.log(`${colors.blue}│ ${colors.purple}president${colors.blue} │ 統括者(プロジェクト全体を管理) │${colors.nc}`);
console.log(`${colors.blue}│ ${colors.cyan}boss1${colors.blue} │ 調整者(タスクを分解してWorkerに分散) │${colors.nc}`);
console.log(`${colors.blue}│ ${colors.green}workers${colors.blue} │ 全Worker(並列でタスクを実行) │${colors.nc}`);
console.log(`${colors.blue}│ ${colors.green}worker1-3${colors.blue} │ 個別Worker(特定のWorkerに送信) │${colors.nc}`);
console.log(`${colors.blue}└─────────────┴────────────────────────────────────────┘${colors.nc}`);
console.log(`\n${colors.yellow}💡 ヒント: 送信先は役割に応じて選択してください${colors.nc}\n`);
const target = await safeRead(`${colors.cyan}🎯 送信先を入力 (デフォルト: president): ${colors.nc}`);
const message = await safeRead(`${colors.cyan}📤 メッセージを入力: ${colors.nc}`);
// デフォルト送信先の処理
const finalTarget = target.trim() || 'president';
if (message.trim()) {
console.log(`${colors.blue}🔄 メッセージを送信中...${colors.nc}`);
await chainCommand(finalTarget, message);
console.log(`${colors.green}✨ 送信完了${colors.nc}`);
} else {
console.log(`${colors.yellow}⚠️ メッセージが必要です${colors.nc}`);
}
break;
case '3':
console.log(`${colors.blue}🛠️ pane 0以外を削除中...${colors.nc}`);
await destroyPanes();
console.log(`${colors.green}✅ 削除完了${colors.nc}`);
break;
case '4':
console.log(`${colors.green}👋 Goodbye!${colors.nc}`);
errorStats.isMenuActive = false;
process.exit(0);
}
// ユーザーにメニュー継続の選択肢を提供
if (normalizedChoice !== '4') {
await promptForContinue();
}
} catch (error) {
errorStats.errorCount++;
errorStats.recentErrors.push({
message: error.message,
timestamp: new Date().toISOString()
});
// 最新10個のエラーのみ保持
if (errorStats.recentErrors.length > 10) {
errorStats.recentErrors = errorStats.recentErrors.slice(-10);
}
console.error(`${colors.red}❌ Menu error: ${error.message}${colors.nc}`);
console.log(`${colors.yellow}⚠️ エラーが発生しました。メインメニューに戻りますか?${colors.nc}`);
await promptForContinue();
} finally {
errorStats.isMenuActive = false;
}
}
// ユーザーに継続の選択肢を提供
async function promptForContinue() {
try {
const continueChoice = await safeRead(`\n${colors.cyan}🔄 メインメニューに戻りますか? (y/n): ${colors.nc}`);
const normalized = continueChoice.trim().toLowerCase();
if (normalized === 'y' || normalized === 'yes' || normalized === '') {
console.log(`\n${colors.purple}${'═'.repeat(50)}${colors.nc}`);
await interactiveMenu();
} else {
console.log(`${colors.green}👋 お疲れ様でした!${colors.nc}`);
cleanupRl();
process.exit(0);
}
} catch (error) {
console.error(`${colors.red}❌ Input error: ${error.message}${colors.nc}`);
cleanupRl();
process.exit(1);
}
}
// 安全なクリーンアップ
function cleanupRl() {
// readline instances are managed by safeRead
}
// エラー統計情報
const errorStats = {
errorCount: 0,
menuCallCount: 0,
isMenuActive: false,
recentErrors: []
};
module.exports = {
interactiveMenu,
cleanupRl
};