@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!
512 lines (439 loc) ⢠18.3 kB
JavaScript
/**
* BitchWizard - Interactive BiTCH Setup Wizard
*
* Guides users through the Bitch-lationship setup process with
* maximum snark, inappropriate humor, and intelligent workspace detection.
*
* Because even relationship setup should be automated and bitchy.
*/
const inquirer = require('inquirer');
const chalk = require('chalk');
const figlet = require('figlet');
const ConfigManager = require('../config/ConfigManager');
const WorkspaceDetector = require('../workspace/WorkspaceDetector');
const BitchNotifier = require('../utils/BitchNotifier');
const { BitchError, successWithCelebration, infoWithPersonality } = require('../utils/ErrorHandler');
// Get version from package.json dynamically
const packageJson = require('../../package.json');
class BitchWizard {
constructor() {
this.configManager = new ConfigManager();
this.workspaceDetector = new WorkspaceDetector();
this.notifier = new BitchNotifier();
this.wizardState = {
step: 0,
totalSteps: 6,
config: {},
detectedWorkspaces: [],
activeWorkspaces: []
};
}
/**
* Start the BiTCH Wizard experience
*/
async startWizard() {
try {
this.showWizardIntro();
await this.stepWelcome();
await this.stepWorkspaceDetection();
await this.stepBitchRelationshipSetup();
await this.stepValidateWorkspaces();
await this.stepFinalConfiguration();
this.showWizardCompletion();
} catch (error) {
this.handleWizardError(error);
}
}
/**
* Run method for compatibility with CLI
*/
async run() {
return this.startWizard();
}
/**
* Show the wizard introduction with proper BiTCH branding
*/
showWizardIntro() {
console.clear();
// Show the BiTCH logo
console.log(chalk.magenta(figlet.textSync('BiTCH', {
font: 'isometric3',
horizontalLayout: 'default',
verticalLayout: 'default'
})));
console.log(chalk.gray('Bi-directional Interface Terminal for Chat Handoffs'));
console.log(chalk.magenta(`š§āāļø Interactive Setup Wizard v${packageJson.version}`));
console.log(chalk.yellow('Because even toxic relationships need proper configuration\n'));
}
/**
* Step 1: Welcome and user introduction
*/
async stepWelcome() {
console.log(chalk.cyan('š Step 1: Welcome to the Bitch-uation\n'));
// Simple universal logic: Initiator = BETA, Other workspace = DEV
const currentWorkspace = await this.workspaceDetector.getCurrentWorkspace();
const workspaceInfo = await this.workspaceDetector.getActiveWorkspaces();
// Whoever initiates BiTCH is assumed to be BETA
const smartDefault = 'BETA';
// Find other active workspaces as potential DEV candidates
const otherWorkspaces = workspaceInfo.active.filter(w => w !== currentWorkspace);
if (otherWorkspaces.length > 0) {
// Suggest the first other workspace as DEV
this.wizardState.suggestedDevWorkspace = otherWorkspaces[0];
console.log(chalk.gray(`š” Smart detection: Current workspace (${currentWorkspace}) = BETA`));
console.log(chalk.gray(`š” Smart detection: Found potential DEV workspace "${otherWorkspaces[0]}"`));
} else {
console.log(chalk.gray(`š” Smart detection: Current workspace (${currentWorkspace}) = BETA`));
console.log(chalk.yellow(`ā ļø No other workspaces detected - you'll need to open your DEV workspace`));
}
const answers = await inquirer.prompt([
{
type: 'input',
name: 'userName',
message: 'What should we call you, you beautiful developer?',
default: smartDefault
},
{
type: 'list',
name: 'experience',
message: 'How familiar are you with workspace drama?',
choices: [
{ name: 'š New to this (be gentle)', value: 'beginner' },
{ name: 'š I know my way around (standard snark)', value: 'intermediate' },
{ name: 'š Maximum bitchiness please', value: 'expert' }
]
},
{
type: 'confirm',
name: 'readyToProceed',
message: 'Ready to establish your Bitch-lationship?',
default: true
}
]);
if (!answers.readyToProceed) {
console.log(chalk.yellow('š Setup cancelled. Come back when you\'re ready for commitment.'));
process.exit(0);
}
this.wizardState.config.userName = answers.userName;
this.wizardState.config.experience = answers.experience;
const greeting = this.getPersonalizedGreeting(answers.experience);
console.log(chalk.green(`${greeting} ${answers.userName}! Let's set up your BiTCH properly.\n`));
}
/**
* Step 2: Detect active workspaces
*/
async stepWorkspaceDetection() {
console.log(chalk.cyan('š Step 2: Workspace Detection\n'));
console.log(chalk.yellow('Scanning for active Cursor workspaces...'));
const workspaceInfo = await this.workspaceDetector.getActiveWorkspaces();
this.wizardState.activeWorkspaces = workspaceInfo.active;
this.wizardState.currentWorkspace = workspaceInfo.current;
if (workspaceInfo.active.length === 0) {
console.log(chalk.red('ā No active Cursor workspaces detected!'));
console.log(chalk.yellow('You need to have at least 2 Cursor workspaces open for BiTCH to work.'));
const answer = await inquirer.prompt([{
type: 'confirm',
name: 'openWorkspaces',
message: 'Would you like me to wait while you open your workspaces?',
default: true
}]);
if (answer.openWorkspaces) {
await this.waitForWorkspaces();
} else {
console.log(chalk.red('š BiTCH needs workspaces to create relationships. Exiting...'));
process.exit(1);
}
} else {
console.log(chalk.green(`ā
Found ${workspaceInfo.active.length} active workspace(s):`));
workspaceInfo.active.forEach((workspace, index) => {
const current = workspace === workspaceInfo.current ? chalk.green(' (current)') : '';
console.log(chalk.white(` ${index + 1}. ${workspace}${current}`));
});
// Show a warning if we detected too many generic workspaces
if (workspaceInfo.active.length > 10) {
console.log(chalk.yellow('\nā ļø Detected many workspaces - some may be system processes.'));
console.log(chalk.gray('BiTCH will help you select the right ones for your Bitch-lationship.'));
}
console.log('');
}
}
/**
* Step 3: Set up the Bitch-lationship intelligently
*/
async stepBitchRelationshipSetup() {
console.log(chalk.cyan('š Step 3: Bitch-lationship Configuration\n'));
const activeWorkspaces = this.wizardState.activeWorkspaces;
const currentWorkspace = this.wizardState.currentWorkspace;
const suggestedDevWorkspace = this.wizardState.suggestedDevWorkspace;
if (activeWorkspaces.length < 2) {
console.log(chalk.red('ā You need at least 2 workspaces for a proper Bitch-lationship!'));
await this.promptForMoreWorkspaces();
return;
}
let devWorkspace, betaWorkspace;
// Use simple universal logic: Current = BETA, Other = DEV
if (suggestedDevWorkspace) {
console.log(chalk.yellow('š¤ Smart suggestions based on who initiated BiTCH:'));
console.log(chalk.white(` BETA (initiator): ${currentWorkspace}`));
console.log(chalk.white(` DEV (detected): ${suggestedDevWorkspace}`));
const roleChoice = await inquirer.prompt([{
type: 'list',
name: 'useRoles',
message: 'How would you like to assign workspace roles?',
choices: [
{
name: `ā
Use suggested roles (${currentWorkspace} = BETA, ${suggestedDevWorkspace} = DEV)`,
value: 'suggested'
},
{
name: `š Reverse roles (${currentWorkspace} = DEV, ${suggestedDevWorkspace} = BETA)`,
value: 'reversed'
},
{
name: 'āļø Manual selection',
value: 'manual'
}
],
default: 'suggested'
}]);
if (roleChoice.useRoles === 'suggested') {
betaWorkspace = currentWorkspace;
devWorkspace = suggestedDevWorkspace;
} else if (roleChoice.useRoles === 'reversed') {
devWorkspace = currentWorkspace;
betaWorkspace = suggestedDevWorkspace;
console.log(chalk.cyan('š Roles reversed - you know your bitch-uation better than I do!'));
} else {
const manual = await this.manualWorkspaceSelection();
devWorkspace = manual.devWorkspace;
betaWorkspace = manual.betaWorkspace;
}
} else {
// No other workspace detected, go straight to manual
console.log(chalk.yellow('𤷠Only one workspace detected. Let\'s set this up manually.'));
const manual = await this.manualWorkspaceSelection();
devWorkspace = manual.devWorkspace;
betaWorkspace = manual.betaWorkspace;
}
this.wizardState.config.devWorkspace = devWorkspace;
this.wizardState.config.betaWorkspace = betaWorkspace;
console.log(chalk.green(`š Bitch-lationship configured: ${devWorkspace} ā ${betaWorkspace}\n`));
}
/**
* Step 4: Validate both workspaces are actually open
*/
async stepValidateWorkspaces() {
console.log(chalk.cyan('š Step 4: Validating Bitch-lationship\n'));
const { devWorkspace, betaWorkspace } = this.wizardState.config;
const validation = await this.workspaceDetector.validateBitchRelationshipActive(devWorkspace, betaWorkspace);
console.log(chalk.yellow('Checking workspace status...'));
console.log(chalk.white(` DEV (${devWorkspace}): ${validation.devActive ? chalk.green('ā
Active') : chalk.red('ā Not found')}`));
console.log(chalk.white(` BETA (${betaWorkspace}): ${validation.betaActive ? chalk.green('ā
Active') : chalk.red('ā Not found')}`));
if (!validation.bothActive) {
console.log(chalk.red('\nš Houston, we have a bitch-uation!'));
if (!validation.devActive) {
console.log(chalk.yellow(`Please open your DEV workspace: ${devWorkspace}`));
}
if (!validation.betaActive) {
console.log(chalk.yellow(`Please open your BETA workspace: ${betaWorkspace}`));
}
const wait = await inquirer.prompt([{
type: 'confirm',
name: 'waitForWorkspaces',
message: 'Wait for you to open the missing workspace(s)?',
default: true
}]);
if (wait.waitForWorkspaces) {
await this.waitForMissingWorkspaces(devWorkspace, betaWorkspace);
} else {
console.log(chalk.red('š Can\'t establish Bitch-lationship without both workspaces. Exiting...'));
process.exit(1);
}
} else {
console.log(chalk.green('\nš Both workspaces are active! Bitch-lationship validated!'));
}
}
/**
* Step 5: Final configuration and preferences
*/
async stepFinalConfiguration() {
console.log(chalk.cyan('āļø Step 5: Final Configuration\n'));
const preferences = await inquirer.prompt([
{
type: 'list',
name: 'sassLevel',
message: 'Choose your default sass level:',
choices: [
{ name: 'š Professional (minimal snark)', value: 'none' },
{ name: 'š Sassy (moderate attitude)', value: 'low' },
{ name: 'š¤ Bitchy (standard operations)', value: 'medium' },
{ name: 'š Maximum Chaos (full BiTCH mode)', value: 'max' }
],
default: 'medium'
},
{
type: 'checkbox',
name: 'features',
message: 'Enable additional BiTCH features:',
choices: [
{ name: 'š System notifications', value: 'notifications', checked: true },
{ name: 'š Random memes and quotes', value: 'humor', checked: true },
{ name: 'š§ Therapy sessions', value: 'therapy', checked: true },
{ name: 'šŖ Easter eggs and chaos', value: 'easterEggs', checked: false }
]
}
]);
this.wizardState.config.sassLevel = preferences.sassLevel;
this.wizardState.config.enabledFeatures = preferences.features;
// Save the configuration
await this.saveConfiguration();
}
/**
* Manual workspace selection
*/
async manualWorkspaceSelection() {
const choices = this.wizardState.activeWorkspaces.map(workspace => ({
name: workspace,
value: workspace
}));
// First prompt - select DEV workspace
const devAnswer = await inquirer.prompt([
{
type: 'list',
name: 'devWorkspace',
message: 'Select your DEV workspace:',
choices: choices
}
]);
// Second prompt - select BETA workspace (excluding DEV choice)
const betaAnswer = await inquirer.prompt([
{
type: 'list',
name: 'betaWorkspace',
message: 'Select your BETA workspace:',
choices: choices.filter(choice => choice.value !== devAnswer.devWorkspace)
}
]);
return {
devWorkspace: devAnswer.devWorkspace,
betaWorkspace: betaAnswer.betaWorkspace
};
}
/**
* Wait for workspaces to be opened
*/
async waitForWorkspaces() {
console.log(chalk.yellow('\nā³ Waiting for workspaces to be detected...'));
console.log(chalk.gray('Open your Cursor workspaces and press Enter when ready.'));
await inquirer.prompt([{
type: 'input',
name: 'continue',
message: 'Press Enter when you have your workspaces open...'
}]);
// Re-detect workspaces
await this.stepWorkspaceDetection();
}
/**
* Wait for missing workspaces
*/
async waitForMissingWorkspaces(devWorkspace, betaWorkspace) {
let attempts = 0;
const maxAttempts = 5;
while (attempts < maxAttempts) {
console.log(chalk.yellow(`\nā³ Attempt ${attempts + 1}/${maxAttempts}: Checking for workspaces...`));
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
const validation = await this.workspaceDetector.validateBitchRelationshipActive(devWorkspace, betaWorkspace);
if (validation.bothActive) {
console.log(chalk.green('š Both workspaces detected! Continuing...'));
return;
}
attempts++;
if (attempts < maxAttempts) {
console.log(chalk.yellow('Still waiting... Make sure both workspaces are open in Cursor.'));
}
}
console.log(chalk.red('š Timeout waiting for workspaces. Please run the wizard again when both are open.'));
process.exit(1);
}
/**
* Prompt for more workspaces
*/
async promptForMoreWorkspaces() {
console.log(chalk.yellow('You need to open another Cursor workspace to create a proper Bitch-lationship.'));
const answer = await inquirer.prompt([{
type: 'confirm',
name: 'openAnother',
message: 'Open another workspace and continue?',
default: true
}]);
if (answer.openAnother) {
await this.waitForWorkspaces();
await this.stepBitchRelationshipSetup(); // Retry this step
} else {
console.log(chalk.red('š BiTCH needs multiple workspaces. Come back when you\'re ready for polyamory.'));
process.exit(1);
}
}
/**
* Save the configuration
*/
async saveConfiguration() {
try {
const config = {
dev: this.wizardState.config.devWorkspace,
beta: this.wizardState.config.betaWorkspace,
userName: this.wizardState.config.userName,
sassLevel: this.wizardState.config.sassLevel,
enabledFeatures: this.wizardState.config.enabledFeatures,
setupDate: new Date().toISOString(),
version: packageJson.version
};
await this.configManager.saveBitchConfig(config);
console.log(chalk.green('ā
Configuration saved successfully!'));
} catch (error) {
console.log(chalk.red('ā Failed to save configuration:'), error.message);
throw error;
}
}
/**
* Get personalized greeting based on experience level
*/
getPersonalizedGreeting(experience) {
const greetings = {
beginner: 'šø Welcome, sweet summer child!',
intermediate: 'š Alright, let\'s get this bitch-uation sorted!',
expert: 'š Oh, you want the full BiTCH experience? Buckle up!'
};
return greetings[experience] || greetings.intermediate;
}
/**
* Show completion with style
*/
showWizardCompletion() {
console.log(chalk.green(`\nš BiTCH Wizard v${packageJson.version} Complete!\n`));
// Show the configuration summary
console.log(chalk.cyan('š Your Bitch-lationship Summary:'));
console.log(chalk.white(` š¤ User: ${this.wizardState.config.userName}`));
console.log(chalk.white(` š DEV ā BETA: ${this.wizardState.config.devWorkspace} ā ${this.wizardState.config.betaWorkspace}`));
console.log(chalk.white(` š Sass Level: ${this.wizardState.config.sassLevel}`));
console.log(chalk.white(` šŖ Features: ${this.wizardState.config.enabledFeatures?.join(', ') || 'Standard'}`));
console.log(chalk.white(` š BiTCH Version: ${packageJson.version}`));
console.log(chalk.magenta('\nš Ready to start bitching!'));
console.log(chalk.yellow('Try these commands:'));
console.log(chalk.gray(' bitch status - Check your bitch-uation'));
console.log(chalk.gray(' bitch send --to <workspace> --msg "Hello!"'));
console.log(chalk.gray(' bitch therapy - Get emotional support'));
console.log(chalk.gray(' bitch meme - Random entertainment\n'));
console.log(chalk.green('š Welcome to the BiTCH family! š'));
}
/**
* Handle errors with appropriate snark
*/
handleWizardError(error) {
console.log(chalk.red('\nš„ Wizard encountered a bitch-uation!'));
console.log(chalk.yellow(error.message));
console.log(chalk.gray('\nTry running: bitch wizard --help'));
console.log(chalk.gray('Or: bitch therapy (for emotional support)'));
}
}
module.exports = BitchWizard;