#!/usr/bin/env node /** * Brainy CLI - Enterprise Neural Intelligence System * * Full TypeScript implementation with type safety and shared code */ import { Command } from 'commander' import chalk from 'chalk' import ora from 'ora' import { Brainy } from '../brainy.js' import conversationCommand from './commands/conversation.js' import { readFileSync } from 'fs' import { fileURLToPath } from 'url' import { dirname, join } from 'path' const __dirname = dirname(fileURLToPath(import.meta.url)) const packageJson = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8')) const version = packageJson.version // CLI Configuration const program = new Command() program .name('brainy') .description('🧠 Enterprise Neural Intelligence Database') .version(version) .option('-v, --verbose', 'Verbose output') .option('--json', 'JSON output format') .option('--pretty', 'Pretty JSON output') .option('--no-color', 'Disable colored output') // ===== Core Commands ===== program .command('add ') .description('Add text or JSON to the neural database') .option('-i, --id ', 'Specify custom ID') .option('-m, --metadata ', 'Add metadata') .option('-t, --type ', 'Specify noun type') .action(coreCommands.add) program .command('search ') .description('Search the neural database') .option('-k, --limit ', 'Number of results', '10') .option('-t, --threshold ', 'Similarity threshold') .option('--metadata ', 'Filter by metadata') .action(coreCommands.search) program .command('get ') .description('Get item by ID') .option('--with-connections', 'Include connections') .action(coreCommands.get) program .command('relate ') .description('Create a relationship between items') .option('-w, --weight ', 'Relationship weight') .option('-m, --metadata ', 'Relationship metadata') .action(coreCommands.relate) program .command('import ') .description('Import data from file') .option('-f, --format ', 'Input format (json|csv|jsonl)', 'json') .option('--batch-size ', 'Batch size for import', '100') .action(coreCommands.import) program .command('export [file]') .description('Export database') .option('-f, --format ', 'Output format (json|csv|jsonl)', 'json') .action(coreCommands.export) // ===== Neural Commands ===== program .command('similar ') .alias('sim') .description('Calculate similarity between two items') .option('--explain', 'Show detailed explanation') .option('--breakdown', 'Show similarity breakdown') .action(neuralCommands.similar) program .command('cluster') .alias('clusters') .description('Find semantic clusters in the data') .option('--algorithm ', 'Clustering algorithm (hierarchical|kmeans|dbscan)', 'hierarchical') .option('--threshold ', 'Similarity threshold', '0.7') .option('--min-size ', 'Minimum cluster size', '2') .option('--max-clusters ', 'Maximum number of clusters') .option('--near ', 'Find clusters near a query') .option('--show', 'Show visual representation') .action(neuralCommands.cluster) program .command('related ') .alias('neighbors') .description('Find semantically related items') .option('-l, --limit ', 'Number of results', '10') .option('-r, --radius ', 'Semantic radius', '0.3') .option('--with-scores', 'Include similarity scores') .option('--with-edges', 'Include connections') .action(neuralCommands.related) program .command('hierarchy ') .alias('tree') .description('Show semantic hierarchy for an item') .option('-d, --depth ', 'Hierarchy depth', '3') .option('--parents-only', 'Show only parent hierarchy') .option('--children-only', 'Show only child hierarchy') .action(neuralCommands.hierarchy) program .command('path ') .description('Find semantic path between items') .option('--steps', 'Show step-by-step path') .option('--max-hops ', 'Maximum path length', '5') .action(neuralCommands.path) program .command('outliers') .alias('anomalies') .description('Detect semantic outliers') .option('-t, --threshold ', 'Outlier threshold', '0.3') .option('--explain', 'Explain why items are outliers') .action(neuralCommands.outliers) program .command('visualize') .alias('viz') .description('Generate visualization data') .option('-f, --format ', 'Output format (json|d3|graphml)', 'json') .option('--max-nodes ', 'Maximum nodes', '500') .option('--dimensions ', '2D or 3D', '2') .option('-o, --output ', 'Output file') .action(neuralCommands.visualize) // ===== Conversation Commands (Infinite Memory) ===== program .command('conversation') .alias('conv') .description('💬 Infinite agent memory and context management') .addCommand( new Command('setup') .description('Set up MCP server for Claude Code integration') .action(async () => { await conversationCommand.handler({ action: 'setup', _: [] }) }) ) .addCommand( new Command('search') .description('Search messages across conversations') .requiredOption('-q, --query ', 'Search query') .option('-c, --conversation-id ', 'Filter by conversation') .option('-r, --role ', 'Filter by role') .option('-l, --limit ', 'Maximum results', '10') .action(async (options) => { await conversationCommand.handler({ action: 'search', ...options as any, _: [] }) }) ) .addCommand( new Command('context') .description('Get relevant context for a query') .requiredOption('-q, --query ', 'Context query') .option('-l, --limit ', 'Maximum messages', '10') .action(async (options) => { await conversationCommand.handler({ action: 'context', ...options as any, _: [] }) }) ) .addCommand( new Command('thread') .description('Get full conversation thread') .requiredOption('-c, --conversation-id ', 'Conversation ID') .action(async (options) => { await conversationCommand.handler({ action: 'thread', ...options as any, _: [] }) }) ) .addCommand( new Command('stats') .description('Show conversation statistics') .action(async () => { await conversationCommand.handler({ action: 'stats', _: [] }) }) ) // ===== Utility Commands ===== program .command('stats') .alias('statistics') .description('Show database statistics') .option('--by-service', 'Group by service') .option('--detailed', 'Show detailed stats') .action(utilityCommands.stats) program .command('clean') .description('Clean and optimize database') .option('--remove-orphans', 'Remove orphaned items') .option('--rebuild-index', 'Rebuild search index') .action(utilityCommands.clean) program .command('benchmark') .alias('bench') .description('Run performance benchmarks') .option('--operations ', 'Operations to benchmark', 'all') .option('--iterations ', 'Number of iterations', '100') .action(utilityCommands.benchmark) // ===== Interactive Mode ===== program .command('interactive') .alias('i') .description('Start interactive REPL mode') .action(async () => { const { startInteractiveMode } = await import('./interactive.js') await startInteractiveMode() }) // ===== Error Handling ===== program.exitOverride() try { await program.parseAsync(process.argv) } catch (error: any) { if (error.code === 'commander.helpDisplayed') { process.exit(0) } console.error(chalk.red('Error:'), error.message) if (program.opts().verbose) { console.error(chalk.gray(error.stack)) } process.exit(1) } // Handle no command if (!process.argv.slice(2).length) { program.outputHelp() }