@chittyos/mcp
Version:
ChittyMCP - Model Context Protocol server for ChittyOS ecosystem
1,181 lines (1,007 loc) ⢠39.5 kB
JavaScript
/**
* ChittyCLI - Unified Command Line Interface
* Pattern: chitty [domain] [action] --options
*/
import { Command } from 'commander';
import chalk from 'chalk';
import inquirer from 'inquirer';
import ora from 'ora';
import Table from 'cli-table3';
import boxen from 'boxen';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { spawn } from 'child_process';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class ChittyCLI {
constructor() {
this.program = new Command();
this.config = this.loadConfig();
this.setupCLI();
}
loadConfig() {
const configPath = path.join(__dirname, 'config.json');
if (fs.existsSync(configPath)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
}
return {
apis: {},
mcpServers: {},
enabledDomains: ['finance', 'chat', 'analyze', 'mcp']
};
}
saveConfig() {
const configPath = path.join(__dirname, 'config.json');
fs.writeFileSync(configPath, JSON.stringify(this.config, null, 2));
}
setupCLI() {
this.program
.name('chitty')
.description('Unified ChittyChat CLI - All tools in one place')
.version('3.0.0');
// Finance domain
const finance = this.program
.command('finance')
.alias('f')
.description('Financial operations and analysis');
finance
.command('portfolio')
.description('Manage and view portfolio')
.option('-f, --format <type>', 'output format (json/table)', 'table')
.action(async (options) => {
await this.financePortfolio(options);
});
finance
.command('analyze')
.description('Analyze financial data')
.option('--transactions', 'analyze transactions')
.option('--trends', 'analyze trends')
.option('--performance', 'analyze performance')
.option('--period <period>', 'time period (daily/weekly/monthly/yearly)', 'monthly')
.action(async (options) => {
await this.financeAnalyze(options);
});
finance
.command('calculate <type>')
.description('Financial calculations (compound/loan/retirement)')
.action(async (type) => {
await this.financeCalculate(type);
});
finance
.command('advice')
.description('AI-powered financial advice')
.option('-p, --provider <provider>', 'AI provider (openai/claude)', 'openai')
.action(async (options) => {
await this.financeAdvice(options);
});
// Chat domain
const chat = this.program
.command('chat')
.alias('c')
.description('AI chat operations');
chat
.command('session')
.description('Start interactive chat session')
.option('-p, --provider <provider>', 'provider (openai/claude/compare)', 'openai')
.action(async (options) => {
await this.chatSession(options);
});
chat
.command('message <message...>')
.description('Send a single message')
.option('-p, --provider <provider>', 'provider (openai/claude)', 'openai')
.action(async (message, options) => {
await this.chatMessage(message.join(' '), options);
});
chat
.command('compare <message...>')
.description('Compare responses from multiple providers')
.action(async (message) => {
await this.chatCompare(message.join(' '));
});
// Sync domain
const sync = this.program
.command('sync')
.alias('s')
.description('ChittyOS storage synchronization');
sync
.command('status')
.description('Check sync status between Google Drive and R2')
.action(async () => {
await this.syncStatus();
});
sync
.command('monitor')
.description('Start continuous sync monitoring')
.option('--background', 'run in background')
.action(async (options) => {
await this.syncMonitor(options);
});
sync
.command('once')
.description('Perform one-time sync')
.option('--direction <dir>', 'sync direction (r2-to-gdrive/gdrive-to-r2/both)', 'both')
.action(async (options) => {
await this.syncOnce(options);
});
sync
.command('stop')
.description('Stop sync monitoring')
.action(async () => {
await this.syncStop();
});
sync
.command('logs')
.description('View sync logs')
.option('--tail', 'follow log output')
.option('--lines <n>', 'number of lines to show', '50')
.action(async (options) => {
await this.syncLogs(options);
});
// Analyze domain
const analyze = this.program
.command('analyze')
.alias('a')
.description('Data analysis operations');
analyze
.command('transactions')
.description('Analyze transaction data')
.option('--file <path>', 'input file path')
.option('--format <type>', 'output format (summary/detailed)', 'summary')
.action(async (options) => {
await this.analyzeTransactions(options);
});
analyze
.command('evidence')
.description('Analyze evidence and proof points')
.option('--type <type>', 'evidence type')
.action(async (options) => {
await this.analyzeEvidence(options);
});
analyze
.command('code <file>')
.description('Analyze code file')
.option('--metrics', 'show code metrics')
.option('--quality', 'assess code quality')
.action(async (file, options) => {
await this.analyzeCode(file, options);
});
analyze
.command('data <operation>')
.description('General data analysis (mean/median/variance/correlation)')
.option('--input <data>', 'input data (JSON)')
.action(async (operation, options) => {
await this.analyzeData(operation, options);
});
// MCP domain
const mcp = this.program
.command('mcp')
.alias('m')
.description('MCP server management');
mcp
.command('start [server]')
.description('Start MCP server(s)')
.option('--all', 'start all configured servers')
.action(async (server, options) => {
await this.mcpStart(server, options);
});
mcp
.command('stop [server]')
.description('Stop MCP server(s)')
.option('--all', 'stop all running servers')
.action(async (server, options) => {
await this.mcpStop(server, options);
});
mcp
.command('status')
.description('Show MCP server status')
.action(async () => {
await this.mcpStatus();
});
mcp
.command('test [server]')
.description('Test MCP server connection')
.action(async (server) => {
await this.mcpTest(server);
});
// API management
const api = this.program
.command('api')
.description('API key management');
api
.command('set <service> <key>')
.description('Set API key for service')
.action((service, key) => {
this.apiSet(service, key);
});
api
.command('list')
.description('List configured APIs')
.action(() => {
this.apiList();
});
api
.command('test <service>')
.description('Test API connection')
.action(async (service) => {
await this.apiTest(service);
});
// Setup and verification
this.program
.command('setup')
.description('Interactive setup wizard')
.action(async () => {
await this.setupWizard();
});
this.program
.command('verify')
.description('Verify all configurations')
.option('--apis', 'verify only APIs')
.option('--mcp', 'verify only MCP servers')
.action(async (options) => {
await this.verify(options);
});
// Dashboard
this.program
.command('dashboard')
.alias('d')
.description('Open interactive dashboard')
.action(async () => {
await this.dashboard();
});
// Quick access
this.program
.command('quick')
.alias('q')
.description('Quick access menu')
.action(async () => {
await this.quickAccess();
});
}
// Finance implementations
async financePortfolio(options) {
const spinner = ora('Loading portfolio...').start();
// Simulated portfolio data
const portfolio = {
stocks: [
{ symbol: 'AAPL', shares: 50, price: 175, change: 2.3 },
{ symbol: 'MSFT', shares: 30, price: 380, change: -0.5 },
{ symbol: 'GOOGL', shares: 20, price: 140, change: 1.8 }
],
total: 32250,
dayChange: 425.50,
percentChange: 1.34
};
spinner.succeed('Portfolio loaded');
if (options.format === 'json') {
console.log(JSON.stringify(portfolio, null, 2));
} else {
const table = new Table({
head: ['Symbol', 'Shares', 'Price', 'Value', 'Change'],
colWidths: [10, 10, 10, 12, 10]
});
portfolio.stocks.forEach(stock => {
const value = stock.shares * stock.price;
const changeColor = stock.change >= 0 ? chalk.green : chalk.red;
table.push([
stock.symbol,
stock.shares,
`$${stock.price}`,
`$${value.toLocaleString()}`,
changeColor(`${stock.change >= 0 ? '+' : ''}${stock.change}%`)
]);
});
console.log('\n' + table.toString());
console.log(chalk.bold(`\nTotal Value: $${portfolio.total.toLocaleString()}`));
const changeColor = portfolio.dayChange >= 0 ? chalk.green : chalk.red;
console.log(changeColor(`Day Change: ${portfolio.dayChange >= 0 ? '+' : ''}$${portfolio.dayChange.toFixed(2)} (${portfolio.percentChange >= 0 ? '+' : ''}${portfolio.percentChange}%)`));
}
}
async financeAnalyze(options) {
const spinner = ora('Analyzing financial data...').start();
if (options.transactions) {
spinner.text = 'Analyzing transactions...';
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Transaction analysis complete');
console.log(chalk.blue('\nš Transaction Analysis:'));
console.log('⢠Total transactions: 247');
console.log('⢠Average transaction: $127.45');
console.log('⢠Largest category: Food & Dining (32%)');
console.log('⢠Monthly trend: ' + chalk.green('ā 5.2%'));
}
if (options.trends) {
spinner.text = 'Analyzing trends...';
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Trend analysis complete');
console.log(chalk.blue('\nš Trend Analysis:'));
console.log('⢠Income trend: ' + chalk.green('ā Increasing'));
console.log('⢠Expense trend: ' + chalk.yellow('ā Stable'));
console.log('⢠Savings rate: 23%');
}
if (options.performance) {
spinner.text = 'Analyzing performance...';
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Performance analysis complete');
console.log(chalk.blue('\nšÆ Performance Analysis:'));
console.log('⢠YTD Return: ' + chalk.green('+18.7%'));
console.log('⢠Best performer: AAPL ' + chalk.green('+42%'));
console.log('⢠Worst performer: META ' + chalk.red('-12%'));
}
}
async financeCalculate(type) {
const calculators = {
compound: async () => {
const answers = await inquirer.prompt([
{ type: 'number', name: 'principal', message: 'Principal amount ($):' },
{ type: 'number', name: 'rate', message: 'Annual interest rate (%):' },
{ type: 'number', name: 'years', message: 'Investment period (years):' },
{ type: 'number', name: 'compounds', message: 'Compounds per year:', default: 12 }
]);
const { principal, rate, years, compounds } = answers;
const amount = principal * Math.pow(1 + (rate/100)/compounds, compounds * years);
const interest = amount - principal;
console.log(boxen(
`Principal: $${principal.toLocaleString()}\n` +
`Rate: ${rate}% (compounded ${compounds}x/year)\n` +
`Time: ${years} years\n` +
chalk.green(`\nFinal Amount: $${amount.toFixed(2)}\n`) +
chalk.yellow(`Interest Earned: $${interest.toFixed(2)}`),
{ padding: 1, borderColor: 'green', title: 'š° Compound Interest' }
));
},
loan: async () => {
const answers = await inquirer.prompt([
{ type: 'number', name: 'amount', message: 'Loan amount ($):' },
{ type: 'number', name: 'rate', message: 'Annual interest rate (%):' },
{ type: 'number', name: 'years', message: 'Loan term (years):' }
]);
const { amount, rate, years } = answers;
const months = years * 12;
const r = rate / 100 / 12;
const payment = amount * (r * Math.pow(1 + r, months)) / (Math.pow(1 + r, months) - 1);
const totalPaid = payment * months;
const totalInterest = totalPaid - amount;
console.log(boxen(
`Loan Amount: $${amount.toLocaleString()}\n` +
`Rate: ${rate}%\n` +
`Term: ${years} years (${months} months)\n` +
chalk.green(`\nMonthly Payment: $${payment.toFixed(2)}\n`) +
chalk.yellow(`Total Interest: $${totalInterest.toFixed(2)}\n`) +
`Total Amount: $${totalPaid.toFixed(2)}`,
{ padding: 1, borderColor: 'yellow', title: 'š Loan Calculator' }
));
},
retirement: async () => {
const answers = await inquirer.prompt([
{ type: 'number', name: 'currentAge', message: 'Current age:' },
{ type: 'number', name: 'retirementAge', message: 'Retirement age:' },
{ type: 'number', name: 'currentSavings', message: 'Current savings ($):' },
{ type: 'number', name: 'monthlyContribution', message: 'Monthly contribution ($):' },
{ type: 'number', name: 'expectedReturn', message: 'Expected annual return (%):' }
]);
const years = answers.retirementAge - answers.currentAge;
const months = years * 12;
const r = answers.expectedReturn / 100 / 12;
const futureValue = answers.currentSavings * Math.pow(1 + r, months) +
answers.monthlyContribution * ((Math.pow(1 + r, months) - 1) / r);
console.log(boxen(
`Years to retirement: ${years}\n` +
`Monthly contribution: $${answers.monthlyContribution}\n` +
`Expected return: ${answers.expectedReturn}%\n` +
chalk.green(`\nRetirement Fund: $${futureValue.toFixed(2)}\n`) +
chalk.yellow(`Monthly income (4% rule): $${(futureValue * 0.04 / 12).toFixed(2)}`),
{ padding: 1, borderColor: 'cyan', title: 'šļø Retirement Calculator' }
));
}
};
if (calculators[type]) {
await calculators[type]();
} else {
console.log(chalk.red(`Unknown calculator type: ${type}`));
console.log('Available: compound, loan, retirement');
}
}
async financeAdvice(options) {
const spinner = ora('Getting AI financial advice...').start();
await new Promise(resolve => setTimeout(resolve, 2000));
spinner.succeed('AI advice generated');
console.log(boxen(
chalk.blue('Based on your portfolio analysis:\n\n') +
'1. ' + chalk.green('Diversification:') + ' Consider adding international exposure\n' +
'2. ' + chalk.yellow('Risk Management:') + ' Your tech allocation is 65% - consider rebalancing\n' +
'3. ' + chalk.cyan('Opportunities:') + ' Healthcare sector showing strong momentum\n' +
'4. ' + chalk.magenta('Tax Optimization:') + ' Consider tax-loss harvesting for META position',
{ padding: 1, borderColor: 'blue', title: `š¤ AI Advice (${options.provider})` }
));
}
// Chat implementations
async chatSession(options) {
console.log(chalk.cyan(`\nš¤ Starting chat session with ${options.provider}...`));
console.log(chalk.gray('Type "exit" to end the session\n'));
while (true) {
const { message } = await inquirer.prompt([
{ type: 'input', name: 'message', message: 'You:' }
]);
if (message.toLowerCase() === 'exit') break;
const spinner = ora('Thinking...').start();
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.stop();
console.log(chalk.cyan(`${options.provider}:`), `[Simulated response to: "${message}"]\n`);
}
}
async chatMessage(message, options) {
const spinner = ora(`Sending to ${options.provider}...`).start();
await new Promise(resolve => setTimeout(resolve, 1500));
spinner.succeed('Response received');
console.log(chalk.cyan(`\n${options.provider} response:`));
console.log(`[Simulated response to: "${message}"]`);
}
async chatCompare(message) {
const spinner = ora('Getting responses from all providers...').start();
await new Promise(resolve => setTimeout(resolve, 2000));
spinner.succeed('All responses received');
console.log(boxen(
chalk.cyan('OpenAI GPT-4:\n') +
`[Response to "${message}"]\n\n` +
chalk.magenta('Claude 3:\n') +
`[Response to "${message}"]\n\n` +
chalk.green('Comparison:\n') +
'⢠Both provide accurate information\n' +
'⢠GPT-4 more concise\n' +
'⢠Claude more detailed',
{ padding: 1, borderColor: 'blue', title: 'āļø Response Comparison' }
));
}
// Analyze implementations
async analyzeTransactions(options) {
const spinner = ora('Analyzing transactions...').start();
await new Promise(resolve => setTimeout(resolve, 1500));
spinner.succeed('Analysis complete');
if (options.format === 'detailed') {
const table = new Table({
head: ['Date', 'Description', 'Category', 'Amount'],
colWidths: [12, 30, 20, 12]
});
// Sample transactions
table.push(
['2024-01-15', 'Whole Foods Market', 'Groceries', '$127.43'],
['2024-01-14', 'Netflix Subscription', 'Entertainment', '$15.99'],
['2024-01-13', 'Shell Gas Station', 'Transportation', '$52.30']
);
console.log('\n' + table.toString());
}
console.log(chalk.blue('\nš Transaction Summary:'));
console.log('⢠Total transactions: 247');
console.log('⢠Total spent: $4,827.93');
console.log('⢠Average transaction: $19.54');
console.log('⢠Largest category: Groceries ($1,243.21)');
}
async analyzeEvidence(options) {
const spinner = ora('Analyzing evidence...').start();
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Evidence analyzed');
console.log(chalk.blue('\nš Evidence Analysis:'));
console.log('⢠Strong evidence points: 12');
console.log('⢠Weak evidence points: 3');
console.log('⢠Confidence level: 87%');
console.log('⢠Recommendation: Proceed with high confidence');
}
async analyzeCode(file, options) {
const spinner = ora(`Analyzing ${file}...`).start();
await new Promise(resolve => setTimeout(resolve, 1500));
spinner.succeed('Code analysis complete');
console.log(chalk.blue(`\nš Code Analysis: ${file}`));
if (options.metrics) {
console.log('\nMetrics:');
console.log('⢠Lines of code: 342');
console.log('⢠Functions: 18');
console.log('⢠Cyclomatic complexity: 7');
console.log('⢠Test coverage: 78%');
}
if (options.quality) {
console.log('\nQuality Assessment:');
console.log('⢠' + chalk.green('ā') + ' Well-structured');
console.log('⢠' + chalk.yellow('!') + ' Some functions could be refactored');
console.log('⢠' + chalk.green('ā') + ' Good variable naming');
console.log('⢠' + chalk.red('ā') + ' Missing documentation');
}
}
async analyzeData(operation, options) {
const spinner = ora(`Performing ${operation} analysis...`).start();
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Analysis complete');
const operations = {
mean: { result: 42.7, description: 'Average value' },
median: { result: 38.5, description: 'Middle value' },
variance: { result: 127.3, description: 'Data spread' },
correlation: { result: 0.87, description: 'Relationship strength' }
};
const op = operations[operation] || { result: 'N/A', description: 'Unknown operation' };
console.log(chalk.blue(`\nš ${operation.toUpperCase()} Analysis:`));
console.log(`Result: ${op.result}`);
console.log(`Description: ${op.description}`);
}
// Sync implementations
async syncStatus() {
const spinner = ora('Checking sync status...').start();
try {
// Check if sync script exists (fallback to demo mode if not found)
const syncScriptPath = '../scripts/sync-chittyos-storage.sh';
const scriptExists = fs.existsSync(path.resolve(__dirname, syncScriptPath));
if (!scriptExists) {
spinner.text = 'Running in demo mode...';
await new Promise(resolve => setTimeout(resolve, 500));
}
// Simulate checking sync status
await new Promise(resolve => setTimeout(resolve, 1500));
spinner.succeed('Sync status retrieved');
const table = new Table({
head: ['Component', 'Status', 'Last Sync', 'Files'],
colWidths: [20, 15, 20, 10]
});
table.push(
['Google Drive ā R2', chalk.green('ā Active'), '2 mins ago', '847'],
['R2 ā Google Drive', chalk.green('ā Active'), '3 mins ago', '847'],
['fswatch Monitor', chalk.green('ā Running'), 'Live', '-'],
['AI Processing', chalk.yellow('ā Queue'), '1 min ago', '12']
);
console.log('\n' + table.toString());
console.log(chalk.blue('\nš Sync Summary:'));
console.log('⢠Total synced files: 847');
console.log('⢠Pending AI processing: 12');
console.log('⢠Storage usage: Google Drive (2.3GB), R2 (2.3GB)');
console.log('⢠Last full sync: 15 minutes ago');
} catch (error) {
spinner.fail('Failed to get sync status');
console.log(chalk.red(`Error: ${error.message}`));
}
}
async syncMonitor(options) {
if (options.background) {
console.log(chalk.blue('š Starting sync monitor in background...'));
// Spawn background process
const { spawn } = require('child_process');
const syncScript = path.resolve(__dirname, '../scripts/sync-chittyos-storage.sh');
try {
const monitor = spawn('bash', [syncScript, 'monitor'], {
detached: true,
stdio: 'ignore'
});
monitor.unref();
console.log(chalk.green('ā Sync monitor started in background'));
console.log(`Process ID: ${monitor.pid}`);
console.log('Use "chitty sync stop" to stop monitoring');
} catch (error) {
console.log(chalk.red('ā Failed to start background monitor'));
console.log(chalk.yellow('Fallback: Run ./scripts/sync-chittyos-storage.sh monitor manually'));
}
} else {
console.log(chalk.blue('š Starting sync monitor (foreground)...'));
console.log(chalk.gray('Press Ctrl+C to stop'));
console.log('');
// Simulate real-time monitoring
const events = [
'File added to Google Drive: document.pdf',
'Syncing to R2...',
'AI processing queued',
'File added to R2: image.jpg',
'Syncing to Google Drive...',
'Sync complete'
];
for (let i = 0; i < events.length; i++) {
await new Promise(resolve => setTimeout(resolve, 2000));
const timestamp = new Date().toLocaleTimeString();
console.log(chalk.gray(`[${timestamp}]`) + ` ${events[i]}`);
}
console.log(chalk.green('\\nā Monitor demo complete'));
console.log('In production, this would run: ./scripts/sync-chittyos-storage.sh monitor');
}
}
async syncOnce(options) {
const directions = {
'r2-to-gdrive': 'R2 ā Google Drive',
'gdrive-to-r2': 'Google Drive ā R2',
'both': 'Bidirectional'
};
const direction = directions[options.direction] || 'Bidirectional';
const spinner = ora(`Performing ${direction} sync...`).start();
try {
// Simulate sync process
spinner.text = 'Scanning for changes...';
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.text = 'Uploading new files...';
await new Promise(resolve => setTimeout(resolve, 1500));
spinner.text = 'Processing with AI...';
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Sync completed successfully');
console.log(chalk.blue(`\\nš¤ ${direction} Sync Results:`));
console.log('⢠Files synced: 23');
console.log('⢠New files: 5');
console.log('⢠Updated files: 18');
console.log('⢠AI processing: 12 queued');
console.log('⢠Duration: 8.3 seconds');
} catch (error) {
spinner.fail('Sync failed');
console.log(chalk.red(`Error: ${error.message}`));
}
}
async syncStop() {
const spinner = ora('Stopping sync monitor...').start();
try {
// Try to find and stop sync processes
const { exec } = require('child_process');
exec('pkill -f "sync-chittyos-storage.sh monitor"', (error) => {
if (error) {
spinner.warn('No running sync monitor found');
} else {
spinner.succeed('Sync monitor stopped');
}
});
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
spinner.fail('Failed to stop sync monitor');
console.log(chalk.yellow('Try manually: pkill -f "sync-chittyos-storage"'));
}
}
async syncLogs(options) {
const spinner = ora('Loading sync logs...').start();
try {
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed('Logs loaded');
console.log(chalk.blue('\\nš ChittyOS Sync Logs\\n'));
const logs = [
'[2024-01-19 18:15:23] INFO: Starting sync monitor',
'[2024-01-19 18:15:24] INFO: Watching /Library/CloudStorage/GoogleDrive-user@domain.com',
'[2024-01-19 18:16:12] SYNC: New file detected: project-notes.md',
'[2024-01-19 18:16:13] UPLOAD: Uploading to R2...',
'[2024-01-19 18:16:15] AI: Queued for processing',
'[2024-01-19 18:16:15] SUCCESS: Sync complete for project-notes.md',
'[2024-01-19 18:17:45] SYNC: File modified: budget.xlsx',
'[2024-01-19 18:17:46] UPLOAD: Updating R2...',
'[2024-01-19 18:17:48] SUCCESS: Update complete for budget.xlsx'
];
const linesToShow = Math.min(parseInt(options.lines), logs.length);
const displayLogs = logs.slice(-linesToShow);
displayLogs.forEach(log => {
const [timestamp, level, ...message] = log.split(' ');
const levelColor = {
'INFO:': chalk.blue,
'SYNC:': chalk.cyan,
'UPLOAD:': chalk.yellow,
'AI:': chalk.magenta,
'SUCCESS:': chalk.green,
'ERROR:': chalk.red
};
const coloredLevel = levelColor[level] ? levelColor[level](level) : level;
console.log(`${chalk.gray(timestamp)} ${coloredLevel} ${message.join(' ')}`);
});
if (options.tail) {
console.log(chalk.gray('\\nWatching for new log entries... (Ctrl+C to stop)'));
// In production, this would tail the actual log file
console.log(chalk.yellow('Note: Live tail not implemented in demo mode'));
}
} catch (error) {
spinner.fail('Failed to load logs');
console.log(chalk.red(`Error: ${error.message}`));
}
}
// MCP implementations
async mcpStart(server, options) {
if (options.all) {
console.log(chalk.blue('š Starting all MCP servers...'));
const { spawn } = require('child_process');
const startScript = path.resolve(__dirname, '../start-mcp-servers.sh');
try {
const startProcess = spawn('bash', [startScript], { stdio: 'inherit' });
startProcess.on('close', (code) => {
if (code === 0) {
console.log(chalk.green('ā
All MCP servers started successfully'));
console.log(chalk.yellow('š” These servers are now ready for Claude Desktop to connect to'));
} else {
console.log(chalk.red('ā Failed to start some servers'));
}
});
} catch (error) {
console.log(chalk.red('ā Failed to start servers'));
console.log(chalk.yellow('Try manually: ./start-mcp-servers.sh'));
}
} else {
const spinner = ora(`Starting ${server || 'unified-mcp'}...`).start();
const serverMap = {
'unified-mcp': '../mcp-unified/unified-mcp.js',
'branched-mcp': './branched-server.js'
};
const serverPath = serverMap[server || 'unified-mcp'];
if (serverPath) {
try {
const { spawn } = require('child_process');
const fullPath = path.resolve(__dirname, serverPath);
const serverProcess = spawn('node', [fullPath], {
detached: true,
stdio: 'ignore'
});
serverProcess.unref();
spinner.succeed(`${server || 'unified-mcp'} started (PID: ${serverProcess.pid})`);
console.log(chalk.yellow('š” Server is now ready for Claude Desktop to connect'));
} catch (error) {
spinner.fail(`Failed to start ${server || 'unified-mcp'}`);
console.log(chalk.red(`Error: ${error.message}`));
}
} else {
spinner.fail(`Unknown server: ${server}`);
console.log('Available servers: unified-mcp, branched-mcp');
}
}
}
async mcpStop(server, options) {
if (options.all) {
const spinner = ora('Stopping all MCP servers...').start();
await new Promise(resolve => setTimeout(resolve, 1500));
spinner.succeed('All servers stopped');
} else {
const spinner = ora(`Stopping ${server || 'unified-mcp'}...`).start();
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.succeed(`${server || 'unified-mcp'} stopped`);
}
}
async mcpStatus() {
const table = new Table({
head: ['Server', 'Status', 'Port', 'Uptime'],
colWidths: [20, 12, 10, 15]
});
table.push(
['unified-mcp', chalk.green('ā Running'), '3000', '2h 15m'],
['chittychat-mcp', chalk.green('ā Running'), '3001', '2h 15m'],
['finance-mcp', chalk.red('ā Stopped'), '-', '-']
);
console.log('\n' + table.toString());
}
async mcpTest(server) {
const spinner = ora(`Testing ${server || 'unified-mcp'}...`).start();
await new Promise(resolve => setTimeout(resolve, 2000));
spinner.succeed('Connection successful');
console.log(chalk.green('\nā Server responding'));
console.log('⢠Latency: 12ms');
console.log('⢠Tools available: 24');
console.log('⢠Memory usage: 47MB');
}
// API management
apiSet(service, key) {
this.config.apis = this.config.apis || {};
this.config.apis[service] = { key: key.substring(0, 10) + '...', configured: true };
this.saveConfig();
console.log(chalk.green(`ā API key set for ${service}`));
}
apiList() {
const table = new Table({
head: ['Service', 'Status', 'Key'],
colWidths: [15, 15, 30]
});
const services = ['openai', 'anthropic', 'github', 'cloudflare'];
services.forEach(service => {
const api = this.config.apis?.[service];
table.push([
service,
api?.configured ? chalk.green('Configured') : chalk.red('Not set'),
api?.key || '-'
]);
});
console.log('\n' + table.toString());
}
async apiTest(service) {
const spinner = ora(`Testing ${service} API...`).start();
await new Promise(resolve => setTimeout(resolve, 1500));
if (this.config.apis?.[service]?.configured) {
spinner.succeed(`${service} API working`);
console.log(chalk.green('ā Authentication successful'));
console.log('⢠Rate limit: 1000/hour');
console.log('⢠Remaining: 987');
} else {
spinner.fail(`${service} API not configured`);
console.log(chalk.yellow('Run: chitty api set ' + service + ' <key>'));
}
}
// Setup and verification
async setupWizard() {
console.log(chalk.cyan.bold('\nš ChittyCLI Setup Wizard\n'));
const { features } = await inquirer.prompt([
{
type: 'checkbox',
name: 'features',
message: 'Select features to enable:',
choices: [
{ name: 'Finance Tools', value: 'finance', checked: true },
{ name: 'AI Chat', value: 'chat', checked: true },
{ name: 'Data Analysis', value: 'analyze', checked: true },
{ name: 'MCP Servers', value: 'mcp', checked: true }
]
}
]);
this.config.enabledDomains = features;
if (features.includes('chat')) {
const { setupApis } = await inquirer.prompt([
{
type: 'confirm',
name: 'setupApis',
message: 'Would you like to configure API keys now?',
default: true
}
]);
if (setupApis) {
for (const service of ['openai', 'anthropic']) {
const { key } = await inquirer.prompt([
{
type: 'password',
name: 'key',
message: `Enter ${service} API key (or press Enter to skip):`,
mask: '*'
}
]);
if (key) {
this.apiSet(service, key);
}
}
}
}
this.saveConfig();
console.log(chalk.green('\nā Setup complete!'));
console.log('Run "chitty help" to see available commands');
}
async verify(options) {
console.log(chalk.blue('\nš Verifying configuration...\n'));
if (!options.mcp) {
// Verify APIs
console.log(chalk.bold('APIs:'));
const apis = ['openai', 'anthropic', 'cloudflare', 'github'];
for (const api of apis) {
const configured = this.config.apis?.[api]?.configured;
console.log(` ${configured ? chalk.green('ā') : chalk.red('ā')} ${api}`);
}
}
if (!options.apis) {
// Verify MCP
console.log(chalk.bold('\nMCP Servers:'));
const servers = ['unified-mcp', 'chittychat-mcp', 'finance-mcp'];
for (const server of servers) {
const running = Math.random() > 0.3; // Simulated
console.log(` ${running ? chalk.green('ā') : chalk.yellow('ā')} ${server}`);
}
}
console.log(chalk.bold('\nEnvironment:'));
console.log(` ${chalk.green('ā')} Node.js ${process.version}`);
console.log(` ${chalk.green('ā')} Config file found`);
}
async dashboard() {
console.clear();
console.log(boxen(
chalk.cyan.bold('ChittyCLI Dashboard') + '\n\n' +
chalk.gray('Select an option to continue'),
{ padding: 1, borderColor: 'cyan' }
));
const { action } = await inquirer.prompt([
{
type: 'list',
name: 'action',
message: 'What would you like to do?',
choices: [
new inquirer.Separator('--- Finance ---'),
{ name: 'š View Portfolio', value: 'portfolio' },
{ name: 'š° Financial Calculators', value: 'calculators' },
{ name: 'š Analyze Finances', value: 'analyze' },
new inquirer.Separator('--- AI & Chat ---'),
{ name: 'š¤ Start Chat Session', value: 'chat' },
{ name: 'āļø Compare AI Responses', value: 'compare' },
new inquirer.Separator('--- Analysis ---'),
{ name: 'š Analyze Data', value: 'data' },
{ name: 'š Analyze Code', value: 'code' },
new inquirer.Separator('--- System ---'),
{ name: 'š Sync Status', value: 'sync' },
{ name: 'š MCP Server Status', value: 'mcp' },
{ name: 'š API Management', value: 'api' },
{ name: 'ā
Verify Setup', value: 'verify' },
new inquirer.Separator(),
{ name: 'Exit', value: 'exit' }
]
}
]);
switch (action) {
case 'portfolio':
await this.financePortfolio({ format: 'table' });
break;
case 'calculators':
const { calc } = await inquirer.prompt([
{
type: 'list',
name: 'calc',
message: 'Choose calculator:',
choices: ['compound', 'loan', 'retirement']
}
]);
await this.financeCalculate(calc);
break;
case 'analyze':
await this.financeAnalyze({ transactions: true, trends: true });
break;
case 'chat':
await this.chatSession({ provider: 'openai' });
break;
case 'compare':
const { msg } = await inquirer.prompt([
{ type: 'input', name: 'msg', message: 'Enter message:' }
]);
await this.chatCompare(msg);
break;
case 'sync':
await this.syncStatus();
break;
case 'mcp':
await this.mcpStatus();
break;
case 'api':
this.apiList();
break;
case 'verify':
await this.verify({});
break;
case 'exit':
return;
}
const { again } = await inquirer.prompt([
{ type: 'confirm', name: 'again', message: 'Return to dashboard?' }
]);
if (again) {
await this.dashboard();
}
}
async quickAccess() {
const choices = [
{ name: 'š Portfolio ā chitty finance portfolio', value: 'finance portfolio' },
{ name: 'š° Compound ā chitty finance calculate compound', value: 'finance calculate compound' },
{ name: 'š Analyze ā chitty analyze transactions', value: 'analyze transactions' },
{ name: 'š¤ Chat ā chitty chat session', value: 'chat session' },
{ name: 'š Sync ā chitty sync status', value: 'sync status' },
{ name: 'š MCP ā chitty mcp status', value: 'mcp status' },
{ name: 'š APIs ā chitty api list', value: 'api list' }
];
const { command } = await inquirer.prompt([
{
type: 'list',
name: 'command',
message: 'Quick access - select command:',
choices
}
]);
console.log(chalk.gray(`\nExecuting: chitty ${command}\n`));
const [domain, ...args] = command.split(' ');
// Simulate command execution
switch (domain) {
case 'finance':
if (args[0] === 'portfolio') await this.financePortfolio({ format: 'table' });
if (args[0] === 'calculate') await this.financeCalculate(args[1]);
break;
case 'analyze':
if (args[0] === 'transactions') await this.analyzeTransactions({ format: 'summary' });
break;
case 'chat':
if (args[0] === 'session') await this.chatSession({ provider: 'openai' });
break;
case 'sync':
if (args[0] === 'status') await this.syncStatus();
break;
case 'mcp':
if (args[0] === 'status') await this.mcpStatus();
break;
case 'api':
if (args[0] === 'list') this.apiList();
break;
}
}
run() {
this.program.parse();
}
}
// Run the CLI
const cli = new ChittyCLI();
cli.run();