@puberty-labs/bi-tch
Version:
BiTCH-MCP: Autonomous AI Coordination Platform - BETA: ZombieDust Protocol operational! Revolutionary AI-to-AI coordination with pure MCP integration. FOLLOW BETA RELEASES for latest features!
1,085 lines (919 loc) โข 42.1 kB
JavaScript
/**
* BiTCH CLI Entry Point
* Because sometimes you need to bitch at your other workspace
* and copy/paste is for peasants.
*/
const path = require('path');
const { spawn } = require('child_process');
const { program } = require('commander');
const chalk = require('chalk');
const figlet = require('figlet');
const fs = require('fs');
// Import components
const ConfigManager = require('../src/config/ConfigManager');
const BitchWizard = require('../src/wizard/BitchWizard');
const WorkspaceDetector = require('../src/workspace/WorkspaceDetector');
const BitchNotifier = require('../src/utils/BitchNotifier');
const MedusaClient = require('../src/medusa/client/MedusaClient');
const HelpSystem = require('../src/utils/HelpSystem');
// Use built-in fetch (Node.js 18+)
const fetch = globalThis.fetch;
// Get version from package.json
const packageJson = require('../package.json');
// Display ASCII art banner
console.log(chalk.magenta(figlet.textSync('BiTCH', { horizontalLayout: 'full' })));
console.log(chalk.yellow(`Bi-directional Interface Terminal for Chat Handoffs v${packageJson.version}`));
console.log(chalk.cyan('BiTCH-MCP: Autonomous AI workspace coordination via Medusa Chat Protocol'));
console.log(chalk.gray('Making AI workspace coordination inappropriately efficient since 2025\n'));
// Create the program
program
.name('bitch')
.description('BiTCH - Making AI assistants work together like they\'re in couples therapy')
.version(packageJson.version);
// Status command
program
.command('status')
.description('Check the BiTCH status and workspace relationships')
.action(async () => {
try {
const configManager = new ConfigManager();
const config = await configManager.getBitchConfig();
const detector = new WorkspaceDetector();
const workspaceName = await detector.getCurrentWorkspace();
const currentWorkspace = workspaceName ? {
name: workspaceName,
path: process.cwd(),
type: 'cursor'
} : null;
console.log(chalk.cyan('๐ BiTCH Status Report\n'));
// Current workspace
if (currentWorkspace) {
console.log(chalk.green('๐ Current Workspace:'));
console.log(` Name: ${chalk.bold(currentWorkspace.name)}`);
console.log(` Path: ${currentWorkspace.path}`);
console.log(` Type: ${currentWorkspace.type}\n`);
} else {
console.log(chalk.yellow('โ ๏ธ Not in a recognized workspace\n'));
}
// Medusa status
const medusaClient = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
const medusaHealth = await medusaClient.checkHealth();
if (medusaHealth.available) {
console.log(chalk.green('โ
Medusa Protocol is running'));
console.log(chalk.gray(` Workspaces: ${medusaHealth.workspaces}`));
console.log(chalk.gray(` Messages: ${medusaHealth.messages}\n`));
} else {
console.log(chalk.red('โ Medusa Protocol is not running'));
console.log(chalk.gray(' Run "bitch medusa start" to start it\n'));
}
// Workspace relationships
if (config) {
console.log(chalk.cyan('\n๐ Workspace Relationship:'));
console.log(` ${chalk.bold(config.dev)} โ๏ธ ${chalk.bold(config.beta)}`);
console.log(` Established: ${new Date(config.createdAt).toLocaleDateString()}`);
console.log(chalk.gray(` Snark Level: ${config.settings?.snarkLevel || 'maximum'}\n`));
} else {
console.log(chalk.gray('\n๐ No workspace relationship established yet'));
console.log(chalk.gray(' Run "bitch setup" to create your first relationship\n'));
}
} catch (error) {
console.error(chalk.red('Error checking status:'), error.message);
process.exit(1);
}
});
// Setup command
program
.command('setup')
.description('Set up BiTCH workspace relationships')
.action(async () => {
try {
const wizard = new BitchWizard();
await wizard.run();
} catch (error) {
console.error(chalk.red('Setup failed:'), error.message);
process.exit(1);
}
});
// BiTCH Slap command - Hard reset everything
program
.command('slap')
.description('๐ฅ BiTCH slap - Hard reset everything when shit hits the fan')
.option('--medusa', 'Reset Medusa Protocol only')
.option('--config', 'Reset configuration only')
.option('--npm', 'Fix NPM binary resolution issues')
.option('--nuclear', 'Nuclear option - reset EVERYTHING')
.action(async (options) => {
try {
console.log(chalk.red('๐ฅ INCOMING BiTCH SLAP! ๐ฅ\n'));
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
let resetCount = 0;
// Fix NPM binary resolution
if (options.npm || options.nuclear) {
console.log(chalk.yellow('๐ฆ Slapping NPM binary resolution back to sanity...'));
try {
// Clear npm cache completely
await execAsync('npm cache clean --force');
console.log(chalk.green(' โ
Cleared NPM cache'));
// Remove node_modules if it exists
if (fs.existsSync('node_modules')) {
await execAsync('rm -rf node_modules');
console.log(chalk.green(' โ
Nuked node_modules'));
}
// Remove package-lock.json if it exists
if (fs.existsSync('package-lock.json')) {
fs.unlinkSync('package-lock.json');
console.log(chalk.green(' โ
Removed package-lock.json'));
}
// Reinstall with latest
console.log(chalk.cyan(' ๐ Reinstalling @puberty-labs/bi-tch@latest...'));
await execAsync('npm install @puberty-labs/bi-tch@latest');
console.log(chalk.green(' โ
Fresh installation complete'));
resetCount++;
} catch (error) {
console.log(chalk.red(' โ NPM slap failed:'), error.message);
}
}
// Reset Medusa Protocol
if (options.medusa || options.nuclear || (!options.config && !options.medusa && !options.npm)) {
console.log(chalk.yellow('๐ Slapping Medusa Protocol back to reality...'));
try {
// Kill any running Medusa processes
await execAsync('pkill -f "medusa-server" || true');
await execAsync('pkill -f "medusa listen" || true');
// Clear workspace registry
const registryPath = path.join(process.cwd(), '.medusa-registry.json');
if (fs.existsSync(registryPath)) {
fs.unlinkSync(registryPath);
console.log(chalk.green(' โ
Cleared workspace registry'));
}
resetCount++;
} catch (error) {
console.log(chalk.red(' โ Medusa slap failed:'), error.message);
}
}
// Reset configuration
if (options.config || options.nuclear) {
console.log(chalk.yellow('โ๏ธ Slapping configuration into submission...'));
try {
const configManager = new ConfigManager();
await configManager.resetConfiguration();
console.log(chalk.green(' โ
Configuration reset'));
resetCount++;
} catch (error) {
console.log(chalk.red(' โ Config slap failed:'), error.message);
}
}
// Nuclear option
if (options.nuclear) {
console.log(chalk.red('โข๏ธ NUCLEAR BiTCH SLAP ENGAGED! โข๏ธ'));
try {
// Clear all temp files
const tempFiles = ['.bitch-lock', '.medusa-lock', 'bitch-listener.log', 'listener.log'];
tempFiles.forEach(file => {
const filePath = path.join(process.cwd(), file);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
console.log(chalk.green(` โ
Nuked ${file}`));
}
});
// Clear node_modules/.cache if it exists
const cacheDir = path.join(process.cwd(), 'node_modules', '.cache');
if (fs.existsSync(cacheDir)) {
await execAsync(`rm -rf "${cacheDir}"`);
console.log(chalk.green(' โ
Nuked node cache'));
}
resetCount++;
} catch (error) {
console.log(chalk.red(' โ Nuclear option failed:'), error.message);
}
}
// Results
console.log(chalk.magenta('\n๐ฅ BiTCH SLAP COMPLETE! ๐ฅ'));
if (resetCount === 0) {
console.log(chalk.yellow('๐คท Nothing got slapped... maybe specify what needs slapping?'));
console.log(chalk.gray(' --medusa : Reset Medusa Protocol'));
console.log(chalk.gray(' --config : Reset configuration'));
console.log(chalk.gray(' --npm : Fix NPM binary resolution'));
console.log(chalk.gray(' --nuclear : Reset EVERYTHING'));
} else {
console.log(chalk.green(`โ
Successfully slapped ${resetCount} system(s) back into line!`));
console.log(chalk.yellow('๐ฏ BiTCH is ready for a fresh start!'));
if (options.medusa || options.nuclear) {
console.log(chalk.cyan('\n๐ To restart Medusa: bitch medusa start'));
}
if (options.config || options.nuclear) {
console.log(chalk.cyan('โ๏ธ To reconfigure: bitch setup'));
}
}
} catch (error) {
console.error(chalk.red('BiTCH slap failed:'), error.message);
process.exit(1);
}
});
// Medusa Protocol commands
const medusa = program
.command('medusa')
.description('๐ Medusa Chat Protocol - Revolutionary workspace coordination');
medusa
.command('start')
.description('Start the Medusa Protocol server')
.option('--force', 'Force start even if another server is detected')
.action(async (options) => {
try {
// NEW: Check for existing servers first
const MedusaListener = require('../src/medusa/MedusaListener');
const listener = new MedusaListener('temp-start-check');
console.log(chalk.cyan('๐ Checking for existing Medusa servers...'));
const availability = await listener.checkServerAvailabilityForStartup();
if (availability.existing && !options.force) {
if (availability.ours) {
console.log(chalk.green('โ
Medusa server already running under this workspace'));
console.log(chalk.yellow('๐ก Server is healthy and ready to use!'));
return;
} else if (availability.stale) {
console.log(chalk.yellow('โ ๏ธ Cleaning up stale processes before starting...'));
const { execSync } = require('child_process');
execSync('pkill -f "medusa-server" 2>/dev/null || true', { stdio: 'ignore' });
await new Promise(resolve => setTimeout(resolve, 2000));
} else {
console.log(chalk.red('โ Another workspace is already running Medusa server!'));
console.log(chalk.cyan(` Controlling Workspace: ${availability.controllingWorkspace}`));
console.log(chalk.cyan(` Status: ${availability.status}`));
console.log(chalk.yellow('\n๐ค Use existing server instead:'));
console.log(chalk.gray(' bitch medusa register # Register with existing server'));
console.log(chalk.gray(' bitch medusa listen # Start listening to existing server'));
console.log(chalk.yellow('\n๐ง Or force start (not recommended):'));
console.log(chalk.gray(' bitch medusa start --force'));
return;
}
}
const { spawn } = require('child_process');
const serverPath = path.join(__dirname, '..', 'src', 'medusa', 'medusa-server.js');
console.log(chalk.green('๐ Starting Medusa Chat Protocol...'));
const medusaProcess = spawn('node', [serverPath], {
detached: true,
stdio: 'inherit'
});
medusaProcess.unref();
console.log(chalk.green('โ
Medusa Protocol servers starting...'));
console.log(chalk.cyan(` Protocol API: http://localhost:3009`));
console.log(chalk.cyan(` Dashboard: http://localhost:8181`));
console.log(chalk.yellow('\nThe two medusas are ready to bitch! ๐๐ฅ๐'));
} catch (error) {
console.error(chalk.red('Failed to start Medusa:'), error.message);
process.exit(1);
}
});
medusa
.command('stop')
.description('Stop the Medusa Protocol server')
.option('--force', 'Force stop all Medusa processes')
.action(async (options) => {
try {
console.log(chalk.yellow('๐ Stopping Medusa Protocol server...'));
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
try {
// Kill medusa-server processes
await execAsync('pkill -f "medusa-server" || true');
console.log(chalk.green(' โ
Stopped Medusa server'));
// Clean up any stale process locks
const ProcessLock = require('../src/utils/ProcessLock');
await ProcessLock.cleanupStaleLocks();
console.log(chalk.green(' โ
Cleaned up process locks'));
console.log(chalk.green('\n๐ฏ Medusa Protocol stopped successfully!'));
console.log(chalk.gray(' Use "bitch medusa start" to restart when needed'));
} catch (error) {
if (options.force) {
console.log(chalk.yellow('โ ๏ธ Some processes may not have been running'));
} else {
console.log(chalk.red('โ Error stopping server:'), error.message);
console.log(chalk.yellow('๐ก Try using --force flag for aggressive cleanup'));
}
}
} catch (error) {
console.error(chalk.red('Failed to stop Medusa:'), error.message);
process.exit(1);
}
});
medusa
.command('register')
.description('Register this workspace with Medusa')
.option('-n, --name <name>', 'Workspace name (defaults to folder name)')
.action(async (options) => {
try {
const detector = new WorkspaceDetector();
const workspaceName = await detector.getCurrentWorkspace();
const workspace = {
name: workspaceName,
path: process.cwd(),
type: 'cursor'
};
if (!workspace) {
console.error(chalk.red('Not in a recognized workspace!'));
process.exit(1);
}
const name = options.name || workspace.name;
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
// Check if server is running
const health = await client.checkHealth();
if (!health.available) {
console.error(chalk.red('Medusa server not running! Start it with "bitch medusa start"'));
process.exit(1);
}
// Register workspace
const registration = await client.register(name, workspace.path, workspace.type);
console.log(chalk.green('โ
Workspace registered with Medusa!'));
console.log(chalk.cyan(` ID: ${registration.id}`));
console.log(chalk.cyan(` Name: ${registration.name}`));
console.log(chalk.yellow('\nReady to bitch with other workspaces! ๐'));
} catch (error) {
console.error(chalk.red('Registration failed:'), error.message);
process.exit(1);
}
});
medusa
.command('listen')
.description('Start listening for incoming messages and auto-respond')
.option('-i, --interval <ms>', 'Polling interval in milliseconds', '5000')
.option('-d, --delay <ms>', 'Response delay in milliseconds', '2000')
.action(async (options) => {
try {
const MedusaListener = require('../src/medusa/MedusaListener');
const detector = new WorkspaceDetector();
const workspaceName = await detector.getCurrentWorkspace();
if (!workspaceName) {
console.error(chalk.red('Not in a recognized workspace!'));
process.exit(1);
}
// Load existing workspace ID from config
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
await client.loadConfig();
if (!client.workspaceId) {
console.error(chalk.red('This workspace is not registered! Run "bitch medusa register" first.'));
process.exit(1);
}
console.log(chalk.green('๐ง Starting BiTCH Medusa Listener...'));
console.log(chalk.cyan(` Workspace: ${workspaceName}`));
console.log(chalk.cyan(` Workspace ID: ${client.workspaceId}`));
console.log(chalk.cyan(` Poll Interval: ${options.interval}ms`));
console.log(chalk.cyan(` Response Delay: ${options.delay}ms`));
const listener = new MedusaListener(client.workspaceId, {
pollInterval: parseInt(options.interval),
responseDelay: parseInt(options.delay)
});
await listener.startListening();
console.log(chalk.yellow('\n๐ BiTCH is now listening and ready to auto-respond!'));
console.log(chalk.gray('Press Ctrl+C to stop listening\n'));
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log(chalk.yellow('\n๐ Stopping listener...'));
listener.stopListening();
process.exit(0);
});
} catch (error) {
console.error(chalk.red('Failed to start listener:'), error.message);
process.exit(1);
}
});
medusa
.command('list')
.description('List all registered workspaces with autonomous conversation status')
.action(async () => {
try {
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
// Check if server is running
const health = await client.checkHealth();
if (!health.available) {
console.error(chalk.red('Medusa server not running! Start it with "bitch medusa start"'));
process.exit(1);
}
const response = await client.listWorkspaces();
const workspaces = response.workspaces || response; // Handle both old and new response formats
const telemetry = response.telemetry;
if (workspaces.length === 0) {
console.log(chalk.yellow('No workspaces registered yet'));
return;
}
console.log(chalk.cyan('๐ Workspace Autonomous Conversation Status:\n'));
// Show telemetry summary if available
if (telemetry) {
console.log(chalk.magenta('๐ Autonomous Conversation Readiness:'));
console.log(chalk.cyan(` Ready: ${telemetry.autonomousConversationReady}/${telemetry.totalWorkspaces} (${telemetry.readinessPercentage}%)`));
console.log('');
}
workspaces.forEach(ws => {
// Enhanced status indicators
const connectionStatus = ws.connection?.webSocket ? chalk.green('๐') : chalk.red('๐');
const listenerStatus = ws.listener?.active ? chalk.green('๐ง') : chalk.red('๐ง');
const autonomousReady = ws.autonomousConversationReady ? chalk.green('๐ค') : chalk.red('๐ค');
console.log(`${autonomousReady} ${chalk.bold(ws.name)} (${ws.id})`);
console.log(` Path: ${ws.path}`);
console.log(` Type: ${ws.type}`);
console.log(` Registered: ${new Date(ws.registeredAt).toLocaleString()}`);
// Connection telemetry
if (ws.connection) {
console.log(` ${connectionStatus} WebSocket: ${ws.connection.webSocket ? chalk.green('Connected') : chalk.red('Disconnected')} (${ws.connection.connectionCount} connections)`);
}
// Listener telemetry
if (ws.listener) {
const listenerText = ws.listener.active ? chalk.green('Active') : chalk.red('Inactive');
const autonomousText = ws.listener.autonomousMode ? chalk.green('ON') : chalk.red('OFF');
console.log(` ${listenerStatus} Listener: ${listenerText} | Autonomous Mode: ${autonomousText}`);
if (ws.listener.lastHeartbeat) {
const heartbeatTime = new Date(ws.listener.lastHeartbeat).toLocaleTimeString();
console.log(` ๐ Last Heartbeat: ${heartbeatTime}`);
}
}
// Overall readiness
const readinessText = ws.autonomousConversationReady ?
chalk.green('โ
READY for autonomous conversations') :
chalk.yellow('โ ๏ธ Not ready - need active listener + WebSocket');
console.log(` ${readinessText}\n`);
});
} catch (error) {
console.error(chalk.red('Failed to list workspaces:'), error.message);
process.exit(1);
}
});
medusa
.command('send <workspace> <message>')
.description('Send a direct message to another workspace')
.option('--wait', 'Wait for response (blocks terminal)')
.action(async (workspace, message, options) => {
try {
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
// Load config without full connection setup
await client.loadConfig();
if (!client.workspaceId) {
console.error(chalk.red('This workspace is not registered! Run "bitch medusa register" first.'));
process.exit(1);
}
// Fire-and-forget send: Just make the HTTP request without WebSocket/polling
const response = await client.sendMessage(workspace, message);
console.log(chalk.green('โ
Message sent!'));
console.log(chalk.gray(` Message ID: ${response.messageId}`));
console.log(chalk.yellow(' Returning to prompt (fire-and-forget mode)'));
// Exit immediately unless --wait flag is used
if (!options.wait) {
process.exit(0);
} else {
console.log(chalk.cyan(' Waiting for response... (use Ctrl+C to stop)'));
// Only if --wait is specified, establish full connection
await client.connect();
// Keep process alive to listen for responses
process.stdin.resume();
// Handle Ctrl+C
process.on('SIGINT', () => {
console.log(chalk.yellow('\n๐ Stopping listener...'));
client.disconnect();
process.exit(0);
});
}
} catch (error) {
console.error(chalk.red('Failed to send message:'), error.message);
process.exit(1);
}
});
medusa
.command('broadcast <message>')
.description('Broadcast a message to all workspaces')
.option('--wait', 'Wait for responses (blocks terminal)')
.action(async (message, options) => {
try {
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
// Load config without full connection setup
await client.loadConfig();
if (!client.workspaceId) {
console.error(chalk.red('This workspace is not registered! Run "bitch medusa register" first.'));
process.exit(1);
}
// Fire-and-forget broadcast: Just make the HTTP request without WebSocket/polling
const response = await client.broadcast(message);
console.log(chalk.green('โ
Broadcast sent!'));
console.log(chalk.gray(` Recipients: ${response.recipients}`));
console.log(chalk.gray(` Message ID: ${response.messageId}`));
console.log(chalk.yellow(' Returning to prompt (fire-and-forget mode)'));
// Exit immediately unless --wait flag is used
if (!options.wait) {
process.exit(0);
} else {
console.log(chalk.cyan(' Waiting for responses... (use Ctrl+C to stop)'));
// Only if --wait is specified, establish full connection
await client.connect();
// Keep process alive to listen for responses
process.stdin.resume();
// Handle Ctrl+C
process.on('SIGINT', () => {
console.log(chalk.yellow('\n๐ Stopping listener...'));
client.disconnect();
process.exit(0);
});
}
} catch (error) {
console.error(chalk.red('Failed to broadcast:'), error.message);
process.exit(1);
}
});
medusa
.command('test-ai [workspace]')
.description('Send a test message that requires real AI to respond (not templates)')
.action(async (workspace = 'tilt') => {
try {
const MedusaListener = require('../src/medusa/MedusaListener');
const detector = new WorkspaceDetector();
const workspaceName = await detector.getCurrentWorkspace();
if (!workspaceName) {
console.error(chalk.red('Not in a recognized workspace!'));
process.exit(1);
}
const listener = new MedusaListener(workspaceName);
console.log(chalk.cyan('๐งช Testing Real AI Integration'));
console.log(chalk.gray(`This will send a unique question that requires real AI to answer correctly.`));
console.log(chalk.gray(`If you get template responses, AI integration is NOT working.`));
console.log('');
const success = await listener.testRealAIIntegration(workspace);
if (success) {
console.log('');
console.log(chalk.green('โ
Test message sent!'));
console.log(chalk.yellow('๐ Expected Response Indicators:'));
console.log(chalk.gray(' โข Mathematical answer: 365 (127 + 238)'));
console.log(chalk.gray(' โข Exactly 3 sentences about quantum computing & JavaScript'));
console.log(chalk.gray(' โข Acknowledges the timestamp and test nature'));
console.log(chalk.gray(' โข NOT a generic template response'));
console.log('');
console.log(chalk.cyan('๐ง Watch your medusa listener for the response!'));
} else {
console.error(chalk.red('โ Failed to send test message'));
process.exit(1);
}
} catch (error) {
console.error(chalk.red('Failed to run AI integration test:'), error.message);
process.exit(1);
}
});
medusa
.command('reset-loops')
.description('Reset conversation counters to prevent reflection loops')
.action(async () => {
try {
const MedusaListener = require('../src/medusa/MedusaListener');
const detector = new WorkspaceDetector();
const workspaceName = await detector.getCurrentWorkspace();
if (!workspaceName) {
console.error(chalk.red('Not in a recognized workspace!'));
process.exit(1);
}
// Load existing workspace ID from config
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
await client.loadConfig();
if (!client.workspaceId) {
console.error(chalk.red('This workspace is not registered! Run "bitch medusa register" first.'));
process.exit(1);
}
// Create temporary listener to reset counters
const listener = new MedusaListener(client.workspaceId);
listener.resetConversationCounters();
console.log(chalk.green('โ
Conversation counters reset successfully!'));
console.log(chalk.yellow(' All reflection loop prevention measures cleared.'));
console.log(chalk.gray(' Safe to restart automated conversations.'));
} catch (error) {
console.error(chalk.red('Failed to reset conversation counters:'), error.message);
process.exit(1);
}
});
medusa
.command('interactive')
.description('Listen for incoming messages (interactive mode)')
.action(async () => {
try {
const client = MedusaClient.getInstance({
workspaceKey: process.cwd()
});
// Connect and load config
const workspace = await client.connect();
if (!workspace) {
console.error(chalk.red('This workspace is not registered! Run "bitch medusa register" first.'));
process.exit(1);
}
console.log(chalk.green(`๐ Listening for messages as ${workspace.name}...`));
console.log(chalk.gray('Press Ctrl+C to stop\n'));
// Listen for messages
client.on('messageReceived', (message) => {
console.log(MedusaClient.formatMessage(message));
// Send notification (with error handling)
try {
const BitchNotifier = require('../src/utils/BitchNotifier');
const notifier = new BitchNotifier();
notifier.sendCustomNotification('Medusa Message', `From ${message.fromName || message.from}: ${message.message}`, {
type: 'info',
timeout: 5
}).catch(err => {
console.log(chalk.gray(`โ ๏ธ Notification failed: ${err.message}`));
});
} catch (notificationError) {
console.log(chalk.gray(`โ ๏ธ Notification system unavailable: ${notificationError.message}`));
}
});
// Keep process alive
process.stdin.resume();
// Handle Ctrl+C
process.on('SIGINT', () => {
console.log(chalk.yellow('\n๐ Stopping message listener...'));
client.disconnect();
process.exit(0);
});
} catch (error) {
console.error(chalk.red('Failed to start listener:'), error.message);
process.exit(1);
}
});
medusa
.command('restart')
.description('Restart Cursor MCP connection without closing Cursor')
.action(async () => {
try {
console.log(chalk.cyan('๐ BiTCH MCP Connection Restart'));
console.log(chalk.yellow(' Automated fix for stuck MCP servers\n'));
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
// Step 1: Find extension host process
console.log(chalk.gray('๐ Finding Cursor extension host process...'));
try {
const { stdout } = await execAsync('ps aux | grep "extension-host" | grep -v grep');
const lines = stdout.trim().split('\n').filter(line => line.includes('extension-host'));
if (lines.length === 0) {
console.log(chalk.red('โ No extension host process found'));
console.log(chalk.yellow('๐ก Make sure Cursor is running and try again'));
return;
}
// Extract PID from first matching process
const pid = lines[0].trim().split(/\s+/)[1];
console.log(chalk.green(` โ
Found extension host (PID: ${pid})`));
// Step 2: Kill extension host process
console.log(chalk.gray('๐จ Restarting extension host...'));
await execAsync(`kill ${pid}`);
console.log(chalk.green(' โ
Extension host restarted'));
// Step 3: Instructions
console.log(chalk.cyan('\n๐ฏ Medusa MCP Restart Complete!'));
console.log(chalk.yellow('๐ Next steps:'));
console.log(chalk.gray(' 1. If Cursor shows "Extension host terminated" dialog โ Click "Restart Extension Host"'));
console.log(chalk.gray(' 2. Go to Cursor Settings โ MCP Tools'));
console.log(chalk.gray(' 3. Toggle "medusa" server OFF then ON'));
console.log(chalk.gray(' 4. Should connect immediately (green with tools)'));
console.log(chalk.green('\n๐ Much faster than closing/reopening Cursor!'));
console.log(chalk.yellow('๐ก Alternative: If no dialog appears, toggle OFF โ ON manually'));
} catch (error) {
console.log(chalk.red('โ Failed to find or restart extension host'));
console.log(chalk.yellow('๐ก Fallback: Close and reopen Cursor manually'));
console.log(chalk.gray(` Error: ${error.message}`));
}
} catch (error) {
console.error(chalk.red('MCP restart failed:'), error.message);
process.exit(1);
}
});
medusa
.command('version-check')
.description('Check version compatibility between CLI and server')
.action(async () => {
try {
// Get current CLI version
const packagePath = path.join(__dirname, '..', 'package.json');
const packageJson = require(packagePath);
const cliVersion = packageJson.version;
console.log(chalk.cyan('๐ BiTCH Version Compatibility Check'));
console.log(chalk.gray(` CLI Version: v${cliVersion}`));
// Check server version
try {
const response = await fetch('http://localhost:3009/health');
if (response.ok) {
const health = await response.json();
console.log(chalk.gray(` Server Version: v${health.version}`));
if (health.version === cliVersion) {
console.log(chalk.green('โ
Versions match! Everything is bitching properly.'));
console.log(chalk.cyan(` Controlling Workspace: ${health.controllingWorkspace}`));
console.log(chalk.cyan(` Server Status: ${health.status}`));
console.log(chalk.cyan(` Uptime: ${Math.floor(health.uptime)}s`));
} else {
console.log(chalk.yellow('โ ๏ธ Version mismatch detected!'));
console.log(chalk.red(` This can cause compatibility issues.`));
console.log(chalk.cyan('\n๐ Recommended actions:'));
console.log(chalk.gray(' 1. Stop current listener (Ctrl+C)'));
console.log(chalk.gray(' 2. Restart listener: bitch medusa listen'));
console.log(chalk.gray(' 3. Server will auto-restart with correct version'));
}
} else {
console.log(chalk.red('โ Server not responding'));
console.log(chalk.yellow('๐ก Start server: bitch medusa start'));
}
} catch (error) {
console.log(chalk.red('โ Server not running'));
console.log(chalk.yellow('๐ก Start server: bitch medusa start'));
}
} catch (error) {
console.error(chalk.red('Version check failed:'), error.message);
process.exit(1);
}
});
medusa
.command('check')
.description('Check Medusa server status and workspace coordination')
.option('--json', 'Output results in JSON format')
.action(async (options) => {
try {
const MedusaListener = require('../src/medusa/MedusaListener');
const listener = new MedusaListener('temp-check-id');
console.log(chalk.cyan('๐ BiTCH-MCP Server Coordination Check'));
console.log(chalk.gray(' Checking for existing Medusa servers and workspace conflicts...\n'));
const availability = await listener.checkServerAvailabilityForStartup();
if (options.json) {
console.log(JSON.stringify(availability, null, 2));
return;
}
// Human-readable output
if (availability.existing) {
if (availability.ours) {
console.log(chalk.green('โ
Medusa server is running and controlled by this workspace'));
console.log(chalk.cyan(` Status: ${availability.status}`));
console.log(chalk.cyan(` Version: v${availability.version}`));
console.log(chalk.yellow('\n๐ก Safe to use existing server - no conflicts detected'));
} else if (availability.stale) {
console.log(chalk.yellow('โ ๏ธ Stale Medusa processes detected'));
console.log(chalk.gray(' Server processes exist but not responding'));
console.log(chalk.yellow('\n๐ง Recommended actions:'));
console.log(chalk.gray(' 1. Clean up stale processes: pkill -f medusa'));
console.log(chalk.gray(' 2. Start fresh server: bitch medusa start'));
} else {
console.log(chalk.yellow('๐ค Medusa server managed by another workspace'));
console.log(chalk.cyan(` Controlling Workspace: ${availability.controllingWorkspace}`));
console.log(chalk.cyan(` Status: ${availability.status}`));
console.log(chalk.cyan(` Version: v${availability.version}`));
console.log(chalk.yellow('\nโ ๏ธ DO NOT START NEW SERVER - Use existing one instead'));
console.log(chalk.green('โ
Register with existing server: bitch medusa register'));
}
} else if (availability.available) {
console.log(chalk.green('โ
No Medusa server detected - safe to start'));
console.log(chalk.yellow('\n๐ Ready to start server: bitch medusa start'));
} else {
console.log(chalk.red('โ Server check failed'));
console.log(chalk.red(` Error: ${availability.error || 'Unknown error'}`));
}
} catch (error) {
if (options.json) {
console.log(JSON.stringify({ error: error.message }, null, 2));
} else {
console.error(chalk.red('Server check failed:'), error.message);
}
process.exit(1);
}
});
/**
* ๐ฏ Comprehensive Help System Integration
* Revolutionary auto-discovering help system with search and examples
*/
// Comprehensive help command that actually works
program
.command('comprehensive-help [command]')
.alias('ch')
.alias('?')
.description('๐ฏ Show comprehensive help with auto-discovery, examples, and search')
.option('--list', 'Show compact command list')
.option('--search <keyword>', 'Search commands by keyword')
.option('--stats', 'Show command statistics')
.option('--discover', 'Auto-discover and show all available commands')
.action(async (command, options) => {
try {
const helpSystem = new HelpSystem(program);
if (options.stats) {
const stats = helpSystem.getStats();
console.log(chalk.cyan('๐ BiTCH Command Statistics:\n'));
console.log(chalk.yellow(`Total Commands: ${stats.totalCommands}`));
console.log(chalk.yellow(`Total Aliases: ${stats.totalAliases}`));
console.log(chalk.yellow('Commands by Category:'));
for (const [category, count] of Object.entries(stats.categories)) {
console.log(` ${category}: ${count}`);
}
return;
}
if (options.search) {
helpSystem.searchCommands(options.search);
return;
}
if (options.list) {
helpSystem.showCommandList();
return;
}
if (options.discover) {
await helpSystem.showAutoDiscoveredHelp();
return;
}
// Default comprehensive help
helpSystem.showHelp(command);
} catch (error) {
console.error(chalk.red('Help system error:'), error.message);
process.exit(1);
}
});
/**
* Commands command - Show all available commands
*/
program
.command('commands')
.alias('cmds')
.description('Show all available commands in compact format')
.action(async () => {
try {
const helpSystem = new HelpSystem(program);
helpSystem.showCommandList();
} catch (error) {
console.error(chalk.red('Commands list error:'), error.message);
process.exit(1);
}
});
/**
* ๐ง ZombieDust - AI Monitoring and Autonomous Coordination
*/
program
.command('zombify <workspace>')
.description('๐ง Start ZombieDust AI monitoring for autonomous workspace coordination')
.option('-m, --mode <mode>', 'Monitoring mode: zombify (continuous) or once (single check)', 'zombify')
.action(async (workspace, options) => {
try {
console.log(chalk.magenta('๐ง Starting ZombieDust AI Monitoring...'));
console.log(chalk.cyan(` Workspace: ${workspace}`));
console.log(chalk.cyan(` Mode: ${options.mode}`));
const zombiedustPath = path.join(__dirname, '..', 'ZombieDust.js');
// Validate workspace parameter
const validWorkspaces = ['tilt', 'bitch', 'auto-detect'];
if (!validWorkspaces.includes(workspace.toLowerCase())) {
console.log(chalk.yellow(`โ ๏ธ Unknown workspace: ${workspace}`));
console.log(chalk.gray(` Valid workspaces: ${validWorkspaces.join(', ')}`));
console.log(chalk.cyan(' Proceeding anyway - ZombieDust will auto-detect...\n'));
}
// Validate mode parameter
const validModes = ['zombify', 'once'];
if (!validModes.includes(options.mode)) {
console.log(chalk.red(`โ Invalid mode: ${options.mode}`));
console.log(chalk.gray(` Valid modes: ${validModes.join(', ')}`));
process.exit(1);
}
console.log(chalk.yellow('๐งโโ๏ธ Transforming AI into autonomous coordination zombie...\n'));
// Spawn ZombieDust process in VISIBLE FOREGROUND MODE as promised in protocol
const zombieProcess = spawn('node', [zombiedustPath, workspace, options.mode], {
stdio: 'inherit', // Full foreground mode - visible monitoring with user control
cwd: path.join(__dirname, '..')
});
// Handle process events
zombieProcess.on('close', (code) => {
if (code === 0) {
console.log(chalk.green('\nโ
ZombieDust monitoring completed successfully'));
} else {
console.log(chalk.red(`\nโ ZombieDust exited with code ${code}`));
}
process.exit(code);
});
zombieProcess.on('error', (error) => {
console.error(chalk.red('ZombieDust spawn error:'), error.message);
process.exit(1);
});
// Handle Ctrl+C gracefully
process.on('SIGINT', () => {
console.log(chalk.yellow('\n๐ง Stopping ZombieDust monitoring...'));
zombieProcess.kill('SIGINT');
});
} catch (error) {
console.error(chalk.red('Failed to start ZombieDust:'), error.message);
process.exit(1);
}
});
// Parse commander commands
program.parse(process.argv);
// If no command was provided, show comprehensive help
if (!process.argv.slice(2).length) {
try {
const helpSystem = new HelpSystem(program);
helpSystem.showHelp();
} catch (error) {
program.outputHelp();
}
}