#!/usr/bin/env node /** * Brainy CLI - Cleaned Up & Beautiful * šŸ§ āš›ļø ONE way to do everything * * After the Great Cleanup of 2025: * - 5 commands total (was 40+) * - Clear, obvious naming * - Interactive mode for beginners */ // @ts-ignore import { program } from 'commander' import { BrainyData } from '../dist/brainyData.js' // @ts-ignore import chalk from 'chalk' import { readFileSync } from 'fs' import { dirname, join } from 'path' import { fileURLToPath } from 'url' import { createInterface } from 'readline' // @ts-ignore import Table from 'cli-table3' const __dirname = dirname(fileURLToPath(import.meta.url)) const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')) // Create single BrainyData instance (the ONE data orchestrator) let brainy = null const getBrainy = async () => { if (!brainy) { brainy = new BrainyData() await brainy.init() } return brainy } // Beautiful colors matching brainy.png logo const colors = { primary: chalk.hex('#3A5F4A'), // Teal container (from logo) success: chalk.hex('#2D4A3A'), // Deep teal frame (from logo) info: chalk.hex('#4A6B5A'), // Medium teal warning: chalk.hex('#D67441'), // Orange (from logo) error: chalk.hex('#B85C35'), // Deep orange brain: chalk.hex('#D67441'), // Brain orange (from logo) cream: chalk.hex('#F5E6A3'), // Cream background (from logo) dim: chalk.dim, blue: chalk.blue, green: chalk.green, yellow: chalk.yellow, cyan: chalk.cyan } // Helper functions const exitProcess = (code = 0) => { setTimeout(() => process.exit(code), 100) } // Initialize Brainy instance const initBrainy = async () => { return new BrainyData() } const wrapAction = (fn) => { return async (...args) => { try { await fn(...args) exitProcess(0) } catch (error) { console.error(colors.error('Error:'), error.message) exitProcess(1) } } } // AI Response Generation with multiple model support async function generateAIResponse(message, brainy, options) { const model = options.model || 'local' // Get relevant context from user's data const contextResults = await brainy.search(message, 5, { includeContent: true, scoreThreshold: 0.3 }) const context = contextResults.map(r => r.content).join('\n') const prompt = `Based on the following context from the user's data, answer their question: Context: ${context} Question: ${message} Answer:` switch (model) { case 'local': case 'ollama': return await callOllamaModel(prompt, options) case 'openai': case 'gpt-3.5-turbo': case 'gpt-4': return await callOpenAI(prompt, options) case 'claude': case 'claude-3': return await callClaude(prompt, options) default: return await callOllamaModel(prompt, options) } } // Ollama (local) integration async function callOllamaModel(prompt, options) { const baseUrl = options.baseUrl || 'http://localhost:11434' const model = options.model === 'local' ? 'llama2' : options.model try { const response = await fetch(`${baseUrl}/api/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: model, prompt: prompt, stream: false }) }) if (!response.ok) { throw new Error(`Ollama error: ${response.statusText}. Make sure Ollama is running: ollama serve`) } const data = await response.json() return data.response || 'No response from local model' } catch (error) { throw new Error(`Local model error: ${error.message}. Try: ollama run llama2`) } } // OpenAI integration async function callOpenAI(prompt, options) { if (!options.apiKey) { throw new Error('OpenAI API key required. Use --api-key or set OPENAI_API_KEY environment variable') } const model = options.model === 'openai' ? 'gpt-3.5-turbo' : options.model try { const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${options.apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: model, messages: [{ role: 'user', content: prompt }], max_tokens: 500 }) }) if (!response.ok) { throw new Error(`OpenAI error: ${response.statusText}`) } const data = await response.json() return data.choices[0]?.message?.content || 'No response from OpenAI' } catch (error) { throw new Error(`OpenAI error: ${error.message}`) } } // Claude integration async function callClaude(prompt, options) { if (!options.apiKey) { throw new Error('Anthropic API key required. Use --api-key or set ANTHROPIC_API_KEY environment variable') } try { const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'x-api-key': options.apiKey, 'Content-Type': 'application/json', 'anthropic-version': '2023-06-01' }, body: JSON.stringify({ model: 'claude-3-haiku-20240307', max_tokens: 500, messages: [{ role: 'user', content: prompt }] }) }) if (!response.ok) { throw new Error(`Claude error: ${response.statusText}`) } const data = await response.json() return data.content[0]?.text || 'No response from Claude' } catch (error) { throw new Error(`Claude error: ${error.message}`) } } // ======================================== // MAIN PROGRAM - CLEAN & SIMPLE // ======================================== program .name('brainy') .description('šŸ§ āš›ļø Brainy - Your AI-Powered Second Brain') .version(packageJson.version) .option('-i, --interactive', 'Start interactive mode') .addHelpText('after', ` ${colors.dim('Examples:')} ${colors.success('brainy add "Meeting notes from today"')} ${colors.success('brainy search "project deadline"')} ${colors.success('brainy chat')} ${colors.dim('# Interactive AI chat')} ${colors.success('brainy -i')} ${colors.dim('# Interactive mode')} ${colors.dim('For more help:')} ${colors.info('brainy --help')} ${colors.dim('# Command-specific help')} ${colors.info('https://github.com/TimeSoul/brainy')} ${colors.dim('# Documentation')}`) // ======================================== // THE 5 COMMANDS (ONE WAY TO DO EVERYTHING) // ======================================== // Command 0: INIT - Initialize brainy (essential setup) program .command('init') .description('Initialize Brainy in current directory') .option('-s, --storage ', 'Storage type (filesystem, memory, s3, r2, gcs)') .option('-e, --encryption', 'Enable encryption for sensitive data') .option('--s3-bucket ', 'S3 bucket name') .option('--s3-region ', 'S3 region') .option('--access-key ', 'Storage access key') .option('--secret-key ', 'Storage secret key') .action(wrapAction(async (options) => { console.log(colors.primary('🧠 Initializing Brainy')) console.log() const { BrainyData } = await import('../dist/brainyData.js') const config = { storage: options.storage || 'filesystem', encryption: options.encryption || false } // Storage-specific configuration if (options.storage === 's3' || options.storage === 'r2' || options.storage === 'gcs') { if (!options.accessKey || !options.secretKey) { console.log(colors.warning('āš ļø Cloud storage requires access credentials')) console.log(colors.info('Use: --access-key --secret-key ')) console.log(colors.info('Or set environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY')) process.exit(1) } config.storageOptions = { bucket: options.s3Bucket, region: options.s3Region || 'us-east-1', accessKeyId: options.accessKey, secretAccessKey: options.secretKey } } try { const brainy = new BrainyData(config) await brainy.init() console.log(colors.success('āœ… Brainy initialized successfully!')) console.log(colors.info(`šŸ“ Storage: ${config.storage}`)) console.log(colors.info(`šŸ”’ Encryption: ${config.encryption ? 'Enabled' : 'Disabled'}`)) if (config.encryption) { console.log(colors.warning('šŸ” Encryption enabled - keep your keys secure!')) } console.log() console.log(colors.success('šŸš€ Ready to go! Try:')) console.log(colors.info(' brainy add "Hello, World!"')) console.log(colors.info(' brainy search "hello"')) } catch (error) { console.log(colors.error('āŒ Initialization failed:')) console.log(colors.error(error.message)) process.exit(1) } })) // Command 1: ADD - Add data (smart by default) program .command('add [data]') .description('Add data to your brain (smart auto-detection)') .option('-m, --metadata ', 'Metadata as JSON') .option('-i, --id ', 'Custom ID') .option('--literal', 'Skip AI processing (literal storage)') .option('--encrypt', 'Encrypt this data (for sensitive information)') .action(wrapAction(async (data, options) => { if (!data) { console.log(colors.info('🧠 Interactive add mode')) const rl = createInterface({ input: process.stdin, output: process.stdout }) data = await new Promise(resolve => { rl.question(colors.primary('What would you like to add? '), (answer) => { rl.close() resolve(answer) }) }) } let metadata = {} if (options.metadata) { try { metadata = JSON.parse(options.metadata) } catch { console.error(colors.error('Invalid JSON metadata')) process.exit(1) } } if (options.id) { metadata.id = options.id } if (options.encrypt) { metadata.encrypted = true } console.log(options.literal ? colors.info('šŸ”’ Literal storage') : colors.success('🧠 Smart mode (auto-detects types)') ) if (options.encrypt) { console.log(colors.warning('šŸ” Encrypting sensitive data...')) } const brainyInstance = await getBrainy() // Handle encryption at data level if requested let processedData = data if (options.encrypt) { processedData = await brainyInstance.encryptData(data) metadata.encrypted = true } await brainyInstance.add(processedData, metadata, { process: options.literal ? 'literal' : 'auto' }) console.log(colors.success('āœ… Added successfully!')) })) // Command 2: CHAT - Talk to your data with AI program .command('chat [message]') .description('AI chat with your brain data (supports local & cloud models)') .option('-s, --session ', 'Use specific chat session') .option('-n, --new', 'Start a new session') .option('-l, --list', 'List all chat sessions') .option('-h, --history [limit]', 'Show conversation history (default: 10)') .option('--search ', 'Search all conversations') .option('-m, --model ', 'LLM model (local/openai/claude/ollama)', 'local') .option('--api-key ', 'API key for cloud models') .option('--base-url ', 'Base URL for local models (default: http://localhost:11434)') .action(wrapAction(async (message, options) => { const { BrainyData } = await import('../dist/brainyData.js') const { BrainyChat } = await import('../dist/chat/BrainyChat.js') console.log(colors.primary('šŸ§ šŸ’¬ Brainy Chat - AI-Powered Conversation with Your Data')) console.log(colors.info('Talk to your brain using your data as context')) console.log() // Initialize brainy and chat const brainy = new BrainyData() await brainy.init() const chat = new BrainyChat(brainy) // Handle different options if (options.list) { console.log(colors.primary('šŸ“‹ Chat Sessions')) const sessions = await chat.getSessions(20) if (sessions.length === 0) { console.log(colors.warning('No chat sessions found. Start chatting to create your first session!')) } else { sessions.forEach((session, i) => { console.log(colors.success(`${i + 1}. ${session.id}`)) if (session.title) console.log(colors.info(` Title: ${session.title}`)) console.log(colors.info(` Messages: ${session.messageCount}`)) console.log(colors.info(` Last active: ${session.lastMessageAt.toLocaleDateString()}`)) }) } return } if (options.search) { console.log(colors.primary(`šŸ” Searching conversations for: "${options.search}"`)) const results = await chat.searchMessages(options.search, { limit: 10 }) if (results.length === 0) { console.log(colors.warning('No messages found')) } else { results.forEach((msg, i) => { console.log(colors.success(`\n${i + 1}. [${msg.sessionId}] ${colors.info(msg.speaker)}:`)) console.log(` ${msg.content.substring(0, 200)}${msg.content.length > 200 ? '...' : ''}`) }) } return } if (options.history) { const limit = parseInt(options.history) || 10 console.log(colors.primary(`šŸ“œ Recent Chat History (${limit} messages)`)) const history = await chat.getHistory(limit) if (history.length === 0) { console.log(colors.warning('No chat history found')) } else { history.forEach(msg => { const speaker = msg.speaker === 'user' ? colors.success('You') : colors.info('AI') console.log(`${speaker}: ${msg.content}`) console.log(colors.info(` ${msg.timestamp.toLocaleString()}`)) console.log() }) } return } // Start interactive chat or process single message if (!message) { console.log(colors.success('šŸŽÆ Interactive mode - type messages or "exit" to quit')) console.log(colors.info(`Model: ${options.model}`)) console.log() // Auto-discover previous session const session = options.new ? null : await chat.initialize() if (session) { console.log(colors.success(`šŸ“‹ Resumed session: ${session.id}`)) console.log() } else { const newSession = await chat.startNewSession() console.log(colors.success(`šŸ†• Started new session: ${newSession.id}`)) console.log() } // Interactive chat loop const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: colors.primary('You: ') }) rl.prompt() rl.on('line', async (input) => { if (input.trim().toLowerCase() === 'exit') { console.log(colors.success('šŸ‘‹ Chat session saved to your brain!')) rl.close() return } if (input.trim()) { // Store user message await chat.addMessage(input.trim(), 'user') // Generate AI response try { const response = await generateAIResponse(input.trim(), brainy, options) console.log(colors.info('AI: ') + response) // Store AI response await chat.addMessage(response, 'assistant', { model: options.model }) console.log() } catch (error) { console.log(colors.error('AI Error: ') + error.message) console.log(colors.warning('šŸ’” Tip: Try setting --model local or providing --api-key')) console.log() } } rl.prompt() }) rl.on('close', () => { exitProcess(0) }) } else { // Single message mode console.log(colors.success('You: ') + message) try { const response = await generateAIResponse(message, brainy, options) console.log(colors.info('AI: ') + response) // Store conversation await chat.addMessage(message, 'user') await chat.addMessage(response, 'assistant', { model: options.model }) } catch (error) { console.log(colors.error('Error: ') + error.message) console.log(colors.info('šŸ’” Try: brainy chat --model local or provide --api-key')) } } })) // Command 3: IMPORT - Bulk/external data program .command('import [source]') .description('Import bulk data from files, URLs, or streams') .option('-t, --type ', 'Source type (file, url, stream)') .option('-c, --chunk-size ', 'Chunk size for large imports', '1000') .action(wrapAction(async (source, options) => { // Interactive mode if no source provided if (!source) { console.log(colors.primary('šŸ“„ Interactive Import Mode')) console.log(colors.dim('Import data from various sources\n')) const rl = createInterface({ input: process.stdin, output: process.stdout }) // Ask for source type first console.log(colors.cyan('Source types:')) console.log(colors.info(' 1. Local file')) console.log(colors.info(' 2. URL')) console.log(colors.info(' 3. Direct input')) console.log() const sourceType = await new Promise(resolve => { rl.question(colors.cyan('Select source type (1-3): '), (answer) => { resolve(answer) }) }) if (sourceType === '3') { // Direct input mode console.log(colors.info('\nEnter your data (type END on a new line when done):\n')) let data = '' let line = '' while ((line = await new Promise(resolve => { rl.question('', resolve) })) !== 'END') { data += line + '\n' } rl.close() // Save to temp file const fs = require('fs') source = `/tmp/brainy-import-${Date.now()}.json` fs.writeFileSync(source, data.trim()) console.log(colors.info(`\nSaved to temporary file: ${source}`)) } else { // File or URL source = await new Promise(resolve => { const prompt = sourceType === '2' ? 'Enter URL: ' : 'Enter file path: ' rl.question(colors.cyan(prompt), (answer) => { rl.close() resolve(answer) }) }) if (!source.trim()) { console.log(colors.warning('No source provided')) process.exit(1) } } } console.log(colors.info('šŸ“„ Starting neural import...')) console.log(colors.info(`Source: ${source}`)) // Use the unified import system from the cleanup plan const { NeuralImport } = await import('../dist/cortex/neuralImport.js') const importer = new NeuralImport() const result = await importer.import(source, { chunkSize: parseInt(options.chunkSize) }) console.log(colors.success(`āœ… Imported ${result.count} items`)) if (result.detectedTypes) { console.log(colors.info('šŸ” Detected types:'), result.detectedTypes) } })) // Command 3: SEARCH - Triple-power search program .command('search [query]') .description('Search your brain (vector + graph + facets)') .option('-l, --limit ', 'Results limit', '10') .option('-f, --filter ', 'Metadata filters (see "brainy fields" for available fields)') .option('-d, --depth ', 'Relationship depth', '2') .option('--fields', 'Show available filter fields and exit') .action(wrapAction(async (query, options) => { // Interactive mode if no query provided if (!query) { console.log(colors.primary('šŸ” Interactive Search Mode')) console.log(colors.dim('Search your neural database with natural language\n')) const rl = createInterface({ input: process.stdin, output: process.stdout }) query = await new Promise(resolve => { rl.question(colors.cyan('What would you like to search for? '), (answer) => { rl.close() resolve(answer) }) }) if (!query.trim()) { console.log(colors.warning('No search query provided')) process.exit(1) } } // Handle --fields option if (options.fields) { console.log(colors.primary('šŸ” Available Filter Fields')) console.log(colors.primary('=' .repeat(30))) try { const { BrainyData } = await import('../dist/brainyData.js') const brainy = new BrainyData() await brainy.init() const filterFields = await brainy.getFilterFields() if (filterFields.length > 0) { console.log(colors.success('Available fields for --filter option:')) filterFields.forEach(field => { console.log(colors.info(` ${field}`)) }) console.log() console.log(colors.primary('Usage Examples:')) console.log(colors.info(` brainy search "query" --filter '{"type":"person"}'`)) console.log(colors.info(` brainy search "query" --filter '{"category":"work","status":"active"}'`)) } else { console.log(colors.warning('No indexed fields available yet.')) console.log(colors.info('Add some data with metadata to see available fields.')) } } catch (error) { console.log(colors.error(`Error: ${error.message}`)) } return } console.log(colors.info(`šŸ” Searching: "${query}"`)) const searchOptions = { limit: parseInt(options.limit), depth: parseInt(options.depth) } if (options.filter) { try { searchOptions.filter = JSON.parse(options.filter) } catch { console.error(colors.error('Invalid filter JSON')) process.exit(1) } } const brainyInstance = await getBrainy() const results = await brainyInstance.search(query, searchOptions.limit || 10, searchOptions) if (results.length === 0) { console.log(colors.warning('No results found')) return } console.log(colors.success(`āœ… Found ${results.length} results:`)) results.forEach((result, i) => { console.log(colors.primary(`\n${i + 1}. ${result.content}`)) if (result.score) { console.log(colors.info(` Relevance: ${(result.score * 100).toFixed(1)}%`)) } if (result.type) { console.log(colors.info(` Type: ${result.type}`)) } }) })) // Command 4: UPDATE - Update existing data program .command('update [id]') .description('Update existing data with new content or metadata') .option('-d, --data ', 'New data content') .option('-m, --metadata ', 'New metadata as JSON') .option('--no-merge', 'Replace metadata instead of merging') .option('--no-reindex', 'Skip reindexing (faster but less accurate search)') .option('--cascade', 'Update related verbs') .action(wrapAction(async (id, options) => { // Interactive mode if no ID provided if (!id) { console.log(colors.primary('šŸ”„ Interactive Update Mode')) console.log(colors.dim('Select an item to update\n')) // Show recent items const brainyInstance = await getBrainy() const recent = await brainyInstance.search('*', 10, { sortBy: 'timestamp' }) if (recent.length > 0) { console.log(colors.cyan('Recent items:')) recent.forEach((item, i) => { console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 50)}...`)) }) console.log() } const rl = createInterface({ input: process.stdin, output: process.stdout }) id = await new Promise(resolve => { rl.question(colors.cyan('Enter ID to update: '), (answer) => { rl.close() resolve(answer) }) }) if (!id.trim()) { console.log(colors.warning('No ID provided')) process.exit(1) } } console.log(colors.info(`šŸ”„ Updating: "${id}"`)) if (!options.data && !options.metadata) { console.error(colors.error('Error: Must provide --data or --metadata')) process.exit(1) } let metadata = undefined if (options.metadata) { try { metadata = JSON.parse(options.metadata) } catch { console.error(colors.error('Invalid JSON metadata')) process.exit(1) } } const brainyInstance = await getBrainy() const success = await brainyInstance.updateNoun(id, options.data, metadata, { merge: options.merge !== false, // Default true unless --no-merge reindex: options.reindex !== false, // Default true unless --no-reindex cascade: options.cascade || false }) if (success) { console.log(colors.success('āœ… Updated successfully!')) if (options.cascade) { console.log(colors.info('šŸ“Ž Related verbs updated')) } } else { console.log(colors.error('āŒ Update failed')) } })) // Command 5: DELETE - Remove data (soft delete by default) program .command('delete [id]') .description('Delete data (soft delete by default, preserves indexes)') .option('--hard', 'Permanent deletion (removes from indexes)') .option('--cascade', 'Delete related verbs') .option('--force', 'Force delete even if has relationships') .action(wrapAction(async (id, options) => { // Interactive mode if no ID provided if (!id) { console.log(colors.warning('šŸ—‘ļø Interactive Delete Mode')) console.log(colors.dim('Select an item to delete\n')) // Show recent items for selection const brainyInstance = await getBrainy() const recent = await brainyInstance.search('*', 10, { sortBy: 'timestamp' }) if (recent.length > 0) { console.log(colors.cyan('Recent items:')) recent.forEach((item, i) => { console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 50)}...`)) }) console.log() } const rl = createInterface({ input: process.stdin, output: process.stdout }) id = await new Promise(resolve => { rl.question(colors.warning('Enter ID to delete (or "cancel"): '), (answer) => { rl.close() resolve(answer) }) }) if (!id.trim() || id.toLowerCase() === 'cancel') { console.log(colors.info('Delete cancelled')) process.exit(0) } // Confirm deletion in interactive mode const confirmRl = createInterface({ input: process.stdin, output: process.stdout }) const confirm = await new Promise(resolve => { const deleteType = options.hard ? 'permanently delete' : 'soft delete' confirmRl.question(colors.warning(`Are you sure you want to ${deleteType} "${id}"? (yes/no): `), (answer) => { confirmRl.close() resolve(answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') }) }) if (!confirm) { console.log(colors.info('Delete cancelled')) process.exit(0) } } console.log(colors.info(`šŸ—‘ļø Deleting: "${id}"`)) if (options.hard) { console.log(colors.warning('āš ļø Hard delete - data will be permanently removed')) } else { console.log(colors.info('šŸ”’ Soft delete - data marked as deleted but preserved')) } const brainyInstance = await getBrainy() try { const success = await brainyInstance.deleteNoun(id, { soft: !options.hard, // Soft delete unless --hard specified cascade: options.cascade || false, force: options.force || false }) if (success) { console.log(colors.success('āœ… Deleted successfully!')) if (options.cascade) { console.log(colors.info('šŸ“Ž Related verbs also deleted')) } } else { console.log(colors.error('āŒ Delete failed')) } } catch (error) { console.error(colors.error(`āŒ Delete failed: ${error.message}`)) if (error.message.includes('has relationships')) { console.log(colors.info('šŸ’” Try: --cascade to delete relationships or --force to ignore them')) } } })) // Command 6A: ADD-NOUN - Create typed entities (Method #4) program .command('add-noun [name]') .description('Add a typed entity to your knowledge graph') .option('-t, --type ', 'Noun type (Person, Organization, Project, Event, Concept, Location, Product)', 'Concept') .option('-m, --metadata ', 'Metadata as JSON') .option('--encrypt', 'Encrypt this entity') .action(wrapAction(async (name, options) => { // Interactive mode if no name provided if (!name) { console.log(colors.primary('šŸ‘¤ Interactive Entity Creation')) console.log(colors.dim('Create a typed entity in your knowledge graph\n')) const rl = createInterface({ input: process.stdin, output: process.stdout }) name = await new Promise(resolve => { rl.question(colors.cyan('Enter entity name: '), (answer) => { resolve(answer) }) }) if (!name.trim()) { rl.close() console.log(colors.warning('No name provided')) process.exit(1) } // Interactive type selection if not provided if (!options.type || options.type === 'Concept') { console.log(colors.cyan('\nSelect entity type:')) const types = ['Person', 'Organization', 'Project', 'Event', 'Concept', 'Location', 'Product'] types.forEach((t, i) => { console.log(colors.info(` ${i + 1}. ${t}`)) }) console.log() const typeIndex = await new Promise(resolve => { rl.question(colors.cyan('Select type (1-7): '), (answer) => { resolve(parseInt(answer) - 1) }) }) if (typeIndex >= 0 && typeIndex < types.length) { options.type = types[typeIndex] } } rl.close() } const brainy = await getBrainy() // Validate noun type const validTypes = ['Person', 'Organization', 'Project', 'Event', 'Concept', 'Location', 'Product'] if (!validTypes.includes(options.type)) { console.log(colors.error(`āŒ Invalid noun type: ${options.type}`)) console.log(colors.info(`Valid types: ${validTypes.join(', ')}`)) process.exit(1) } let metadata = {} if (options.metadata) { try { metadata = JSON.parse(options.metadata) } catch { console.error(colors.error('āŒ Invalid JSON metadata')) process.exit(1) } } if (options.encrypt) { metadata.encrypted = true } try { const { NounType } = await import('../dist/types/graphTypes.js') const id = await brainy.addNoun(name, NounType[options.type], metadata) console.log(colors.success('āœ… Noun added successfully!')) console.log(colors.info(`šŸ†” ID: ${id}`)) console.log(colors.info(`šŸ‘¤ Name: ${name}`)) console.log(colors.info(`šŸ·ļø Type: ${options.type}`)) if (Object.keys(metadata).length > 0) { console.log(colors.info(`šŸ“ Metadata: ${JSON.stringify(metadata, null, 2)}`)) } } catch (error) { console.log(colors.error('āŒ Failed to add noun:')) console.log(colors.error(error.message)) process.exit(1) } })) // Command 6B: ADD-VERB - Create relationships (Method #5) program .command('add-verb [source] [target]') .description('Create a relationship between two entities') .option('-t, --type ', 'Verb type (WorksFor, Knows, CreatedBy, BelongsTo, Uses, etc.)', 'RelatedTo') .option('-m, --metadata ', 'Relationship metadata as JSON') .option('--encrypt', 'Encrypt this relationship') .action(wrapAction(async (source, target, options) => { // Interactive mode if parameters missing if (!source || !target) { console.log(colors.primary('šŸ”— Interactive Relationship Builder')) console.log(colors.dim('Connect two entities with a semantic relationship\n')) const brainyInstance = await getBrainy() const rl = createInterface({ input: process.stdin, output: process.stdout }) // Get source if not provided if (!source) { // Show recent items const recent = await brainyInstance.search('*', 10, { sortBy: 'timestamp' }) if (recent.length > 0) { console.log(colors.cyan('Recent items (source):')) recent.forEach((item, i) => { console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 40)}...`)) }) console.log() } source = await new Promise(resolve => { rl.question(colors.cyan('Enter source entity ID: '), (answer) => { resolve(answer) }) }) if (!source.trim()) { rl.close() console.log(colors.warning('No source provided')) process.exit(1) } } // Interactive verb type selection if (!options.type || options.type === 'RelatedTo') { console.log(colors.cyan('\nSelect relationship type:')) const verbs = ['WorksFor', 'Knows', 'CreatedBy', 'BelongsTo', 'Uses', 'Manages', 'LocatedIn', 'RelatedTo', 'Custom...'] verbs.forEach((v, i) => { console.log(colors.info(` ${i + 1}. ${v}`)) }) console.log() const verbIndex = await new Promise(resolve => { rl.question(colors.cyan('Select type (1-9): '), (answer) => { resolve(parseInt(answer) - 1) }) }) if (verbIndex >= 0 && verbIndex < verbs.length - 1) { options.type = verbs[verbIndex] } else if (verbIndex === verbs.length - 1) { // Custom verb options.type = await new Promise(resolve => { rl.question(colors.cyan('Enter custom relationship: '), (answer) => { resolve(answer) }) }) } } // Get target if not provided if (!target) { // Show recent items again const recent = await brainyInstance.search('*', 10, { sortBy: 'timestamp' }) if (recent.length > 0) { console.log(colors.cyan('\nRecent items (target):')) recent.forEach((item, i) => { console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 40)}...`)) }) console.log() } target = await new Promise(resolve => { rl.question(colors.cyan('Enter target entity ID: '), (answer) => { resolve(answer) }) }) if (!target.trim()) { rl.close() console.log(colors.warning('No target provided')) process.exit(1) } } rl.close() } const brainy = await getBrainy() // Common verb types for validation const commonTypes = ['WorksFor', 'Knows', 'CreatedBy', 'BelongsTo', 'Uses', 'LeadsProject', 'MemberOf', 'RelatedTo', 'InteractedWith'] if (!commonTypes.includes(options.type)) { console.log(colors.warning(`āš ļø Uncommon verb type: ${options.type}`)) console.log(colors.info(`Common types: ${commonTypes.join(', ')}`)) } let metadata = {} if (options.metadata) { try { metadata = JSON.parse(options.metadata) } catch { console.error(colors.error('āŒ Invalid JSON metadata')) process.exit(1) } } if (options.encrypt) { metadata.encrypted = true } try { const { VerbType } = await import('../dist/types/graphTypes.js') // Use the provided type or fall back to RelatedTo const verbType = VerbType[options.type] || options.type const id = await brainy.addVerb(source, target, verbType, metadata) console.log(colors.success('āœ… Relationship added successfully!')) console.log(colors.info(`šŸ†” ID: ${id}`)) console.log(colors.info(`šŸ”— ${source} --[${options.type}]--> ${target}`)) if (Object.keys(metadata).length > 0) { console.log(colors.info(`šŸ“ Metadata: ${JSON.stringify(metadata, null, 2)}`)) } } catch (error) { console.log(colors.error('āŒ Failed to add relationship:')) console.log(colors.error(error.message)) process.exit(1) } })) // Command 7: STATUS - Database health & info program .command('status') .description('Show brain status and comprehensive statistics') .option('-v, --verbose', 'Show raw JSON statistics') .option('-s, --simple', 'Show only basic info') .action(wrapAction(async (options) => { console.log(colors.primary('🧠 Brain Status & Statistics')) console.log(colors.primary('=' .repeat(50))) try { const { BrainyData } = await import('../dist/brainyData.js') const brainy = new BrainyData() await brainy.init() // Get comprehensive stats const stats = await brainy.getStatistics() const memUsage = process.memoryUsage() // Basic Health Status console.log(colors.success('šŸ’š Status: Healthy')) console.log(colors.info(`šŸš€ Version: ${packageJson.version}`)) console.log() if (options.simple) { console.log(colors.info(`šŸ“Š Total Items: ${stats.total || 0}`)) console.log(colors.info(`🧠 Memory: ${(memUsage.heapUsed / 1024 / 1024).toFixed(1)} MB`)) return } // Core Statistics console.log(colors.primary('šŸ“Š Core Database Statistics')) console.log(colors.info(` Total Items: ${colors.success(stats.total || 0)}`)) console.log(colors.info(` Nouns: ${colors.success(stats.nounCount || 0)}`)) console.log(colors.info(` Verbs (Relationships): ${colors.success(stats.verbCount || 0)}`)) console.log(colors.info(` Metadata Records: ${colors.success(stats.metadataCount || 0)}`)) console.log() // Per-Service Breakdown (if available) if (stats.serviceBreakdown && Object.keys(stats.serviceBreakdown).length > 0) { console.log(colors.primary('šŸ”§ Per-Service Breakdown')) Object.entries(stats.serviceBreakdown).forEach(([service, serviceStats]) => { console.log(colors.info(` ${colors.success(service)}:`)) console.log(colors.info(` Nouns: ${serviceStats.nounCount}`)) console.log(colors.info(` Verbs: ${serviceStats.verbCount}`)) console.log(colors.info(` Metadata: ${serviceStats.metadataCount}`)) }) console.log() } // Storage Information if (stats.storage) { console.log(colors.primary('šŸ’¾ Storage Information')) console.log(colors.info(` Type: ${colors.success(stats.storage.type || 'Unknown')}`)) if (stats.storage.size) { const sizeInMB = (stats.storage.size / 1024 / 1024).toFixed(2) console.log(colors.info(` Size: ${colors.success(sizeInMB)} MB`)) } if (stats.storage.location) { console.log(colors.info(` Location: ${colors.success(stats.storage.location)}`)) } console.log() } // Performance Metrics if (stats.performance) { console.log(colors.primary('⚔ Performance Metrics')) if (stats.performance.avgQueryTime) { console.log(colors.info(` Avg Query Time: ${colors.success(stats.performance.avgQueryTime.toFixed(2))} ms`)) } if (stats.performance.totalQueries) { console.log(colors.info(` Total Queries: ${colors.success(stats.performance.totalQueries)}`)) } if (stats.performance.cacheHitRate) { console.log(colors.info(` Cache Hit Rate: ${colors.success((stats.performance.cacheHitRate * 100).toFixed(1))}%`)) } console.log() } // Vector Index Information if (stats.index) { console.log(colors.primary('šŸŽÆ Vector Index')) console.log(colors.info(` Dimensions: ${colors.success(stats.index.dimensions || 'N/A')}`)) console.log(colors.info(` Indexed Vectors: ${colors.success(stats.index.vectorCount || 0)}`)) if (stats.index.indexSize) { console.log(colors.info(` Index Size: ${colors.success((stats.index.indexSize / 1024 / 1024).toFixed(2))} MB`)) } console.log() } // Memory Usage Breakdown console.log(colors.primary('🧠 Memory Usage')) console.log(colors.info(` Heap Used: ${colors.success((memUsage.heapUsed / 1024 / 1024).toFixed(1))} MB`)) console.log(colors.info(` Heap Total: ${colors.success((memUsage.heapTotal / 1024 / 1024).toFixed(1))} MB`)) console.log(colors.info(` RSS: ${colors.success((memUsage.rss / 1024 / 1024).toFixed(1))} MB`)) console.log() // Active Augmentations console.log(colors.primary('šŸ”Œ Active Augmentations')) const augmentations = cortex.getAllAugmentations() if (augmentations.length === 0) { console.log(colors.warning(' No augmentations currently active')) } else { augmentations.forEach(aug => { console.log(colors.success(` āœ… ${aug.name}`)) if (aug.description) { console.log(colors.info(` ${aug.description}`)) } }) } console.log() // Configuration Summary if (stats.config) { console.log(colors.primary('āš™ļø Configuration')) Object.entries(stats.config).forEach(([key, value]) => { // Don't show sensitive values if (key.toLowerCase().includes('key') || key.toLowerCase().includes('secret')) { console.log(colors.info(` ${key}: ${colors.warning('[HIDDEN]')}`)) } else { console.log(colors.info(` ${key}: ${colors.success(value)}`)) } }) console.log() } // Available Fields for Advanced Search console.log(colors.primary('šŸ” Available Search Fields')) try { const filterFields = await brainy.getFilterFields() if (filterFields.length > 0) { console.log(colors.info(' Use these fields for advanced filtering:')) filterFields.forEach(field => { console.log(colors.success(` ${field}`)) }) console.log(colors.info('\n Example: brainy search "query" --filter \'{"type":"person"}\'')) } else { console.log(colors.warning(' No indexed fields available yet')) console.log(colors.info(' Add some data to see available fields')) } } catch (error) { console.log(colors.warning(' Field discovery not available')) } console.log() // Show raw JSON if verbose if (options.verbose) { console.log(colors.primary('šŸ“‹ Raw Statistics (JSON)')) console.log(colors.info(JSON.stringify(stats, null, 2))) } } catch (error) { console.log(colors.error('āŒ Status: Error')) console.log(colors.error(`Error: ${error.message}`)) if (options.verbose) { console.log(colors.error('Stack trace:')) console.log(error.stack) } } })) // Command 5: CONFIG - Essential configuration program .command('config [key] [value]') .description('Configure brainy (get, set, list)') .action(wrapAction(async (action, key, value) => { const configActions = { get: async () => { if (!key) { console.error(colors.error('Please specify a key: brainy config get ')) process.exit(1) } const result = await cortex.configGet(key) console.log(colors.success(`${key}: ${result || 'not set'}`)) }, set: async () => { if (!key || !value) { console.error(colors.error('Usage: brainy config set ')) process.exit(1) } await cortex.configSet(key, value) console.log(colors.success(`āœ… Set ${key} = ${value}`)) }, list: async () => { const config = await cortex.configList() console.log(colors.primary('šŸ”§ Current Configuration:')) Object.entries(config).forEach(([k, v]) => { console.log(colors.info(` ${k}: ${v}`)) }) } } if (configActions[action]) { await configActions[action]() } else { console.error(colors.error('Valid actions: get, set, list')) process.exit(1) } })) // Command 6: AUGMENT - Manage augmentations (The 8th Unified Method!) program .command('augment ') .description('Manage augmentations to extend Brainy\'s capabilities') .option('-n, --name ', 'Augmentation name') .option('-t, --type ', 'Augmentation type (sense, conduit, cognition, memory)') .option('-p, --path ', 'Path to augmentation module') .option('-l, --list', 'List all augmentations') .action(wrapAction(async (action, options) => { const brainy = await initBrainy() console.log(colors.brain('🧩 Augmentation Management')) const actions = { list: async () => { try { // Use new unified registry API const REGISTRY_URL = 'https://registry-api-476163328636.us-central1.run.app/v1/augmentations' const response = await fetch(REGISTRY_URL) if (response && response.ok) { console.log(colors.brain('šŸ¢ SOULCRAFT PROFESSIONAL SUITE\n')) const data = await response.json() const augmentations = data.data || [] // NEW: data is in .data array // Get local augmentations to check what's installed const localAugmentations = brainy.listAugmentations() const localPackageNames = localAugmentations.map(aug => aug.package || aug.name).filter(Boolean) // Find installed registry augmentations const installed = augmentations.filter(aug => aug.package && localPackageNames.includes(aug.package) ) // Display installed augmentations first if (installed.length > 0) { console.log(colors.success('āœ… INSTALLED AUGMENTATIONS')) installed.forEach(aug => { const pricing = aug.price ? `$${aug.price.monthly}/mo` : (aug.tier === 'free' ? 'FREE' : 'TBD') const pricingColor = aug.tier === 'free' ? colors.success(pricing) : colors.yellow(pricing) console.log(` ${aug.name.padEnd(20)} ${pricingColor.padEnd(15)} ${colors.success('āœ… ACTIVE')}`) console.log(` ${colors.dim(aug.description)}`) console.log('') }) console.log('') // Extra space before available augmentations } // Filter out installed ones from the available lists const availableAugmentations = augmentations.filter(aug => !installed.find(inst => inst.id === aug.id) ) // NEW: Use new tier names - "premium" instead of "professional" const premium = availableAugmentations.filter(a => a.tier === 'premium') const free = availableAugmentations.filter(a => a.tier === 'free') const community = availableAugmentations.filter(a => a.tier === 'community') const comingSoon = availableAugmentations.filter(a => a.status === 'coming_soon') // Display premium augmentations if (premium.length > 0) { console.log(colors.primary('šŸš€ PREMIUM AUGMENTATIONS')) premium.forEach(aug => { // NEW: price object format const pricing = aug.price ? `$${aug.price.monthly}/mo` : (aug.tier === 'free' ? 'FREE' : 'TBD') const pricingColor = aug.tier === 'free' ? colors.success(pricing) : colors.yellow(pricing) // NEW: status instead of verified const status = aug.status === 'available' ? colors.blue('āœ“') : aug.status === 'coming_soon' ? colors.yellow('ā³') : colors.dim('•') console.log(` ${aug.name.padEnd(20)} ${pricingColor.padEnd(15)} ${status}`) console.log(` ${colors.dim(aug.description)}`) if (aug.status === 'coming_soon' && aug.eta) { console.log(` ${colors.cyan('→ Coming ' + aug.eta)}`) } if (aug.features && aug.features.length > 0) { console.log(` ${colors.cyan('→ ' + aug.features.join(', '))}`) } console.log('') }) } // Display free augmentations if (free.length > 0) { console.log(colors.primary('šŸ†“ FREE AUGMENTATIONS')) free.forEach(aug => { const status = aug.status === 'available' ? colors.blue('āœ“') : aug.status === 'coming_soon' ? colors.yellow('ā³') : colors.dim('•') console.log(` ${aug.name.padEnd(20)} ${colors.success('FREE').padEnd(15)} ${status}`) console.log(` ${colors.dim(aug.description)}`) if (aug.status === 'coming_soon' && aug.eta) { console.log(` ${colors.cyan('→ Coming ' + aug.eta)}`) } console.log('') }) } // Display community augmentations if (community.length > 0) { console.log(colors.primary('šŸ‘„ COMMUNITY AUGMENTATIONS')) community.forEach(aug => { const status = aug.status === 'available' ? colors.blue('āœ“') : aug.status === 'coming_soon' ? colors.yellow('ā³') : colors.dim('•') console.log(` ${aug.name.padEnd(20)} ${colors.success('COMMUNITY').padEnd(15)} ${status}`) console.log(` ${colors.dim(aug.description)}`) console.log('') }) } // Display truly local (non-registry) augmentations const localOnly = localAugmentations.filter(aug => !aug.package || !augmentations.find(regAug => regAug.package === aug.package) ) if (localOnly.length > 0) { console.log(colors.primary('šŸ“¦ CUSTOM AUGMENTATIONS')) localOnly.forEach(aug => { const status = aug.enabled ? colors.success('āœ… Enabled') : colors.dim('⚪ Disabled') console.log(` ${aug.name.padEnd(20)} ${status}`) console.log(` ${colors.dim(aug.description || 'Custom augmentation')}`) console.log('') }) } console.log(colors.cyan('šŸŽÆ GET STARTED')) console.log(' brainy install Install augmentation') console.log(' brainy cloud Access Brain Cloud features') console.log(` ${colors.blue('Learn more:')} https://soulcraft.com/augmentations`) } else { throw new Error('Registry unavailable') } } catch (error) { // Fallback to local augmentations only console.log(colors.warning('⚠ Professional catalog unavailable, showing local augmentations')) const augmentations = brainy.listAugmentations() if (augmentations.length === 0) { console.log(colors.warning('No augmentations registered')) return } const table = new Table({ head: [colors.brain('Name'), colors.brain('Type'), colors.brain('Status'), colors.brain('Description')], style: { head: [], border: [] } }) augmentations.forEach(aug => { table.push([ colors.primary(aug.name), colors.info(aug.type), aug.enabled ? colors.success('āœ… Enabled') : colors.dim('⚪ Disabled'), colors.dim(aug.description || '') ]) }) console.log(table.toString()) console.log(colors.info(`\nTotal: ${augmentations.length} augmentations`)) } }, enable: async () => { if (!options.name) { console.log(colors.error('Name required: --name ')) return } const success = brainy.enableAugmentation(options.name) if (success) { console.log(colors.success(`āœ… Enabled augmentation: ${options.name}`)) } else { console.log(colors.error(`Failed to enable: ${options.name} (not found)`)) } }, disable: async () => { if (!options.name) { console.log(colors.error('Name required: --name ')) return } const success = brainy.disableAugmentation(options.name) if (success) { console.log(colors.warning(`⚪ Disabled augmentation: ${options.name}`)) } else { console.log(colors.error(`Failed to disable: ${options.name} (not found)`)) } }, register: async () => { if (!options.path) { console.log(colors.error('Path required: --path ')) return } try { // Dynamic import of custom augmentation const customModule = await import(options.path) const AugmentationClass = customModule.default || customModule[Object.keys(customModule)[0]] if (!AugmentationClass) { console.log(colors.error('No augmentation class found in module')) return } const augmentation = new AugmentationClass() brainy.register(augmentation) console.log(colors.success(`āœ… Registered augmentation: ${augmentation.name}`)) console.log(colors.info(`Type: ${augmentation.type}`)) if (augmentation.description) { console.log(colors.dim(`Description: ${augmentation.description}`)) } } catch (error) { console.log(colors.error(`Failed to register augmentation: ${error.message}`)) } }, unregister: async () => { if (!options.name) { console.log(colors.error('Name required: --name ')) return } brainy.unregister(options.name) console.log(colors.warning(`šŸ—‘ļø Unregistered augmentation: ${options.name}`)) }, 'enable-type': async () => { if (!options.type) { console.log(colors.error('Type required: --type ')) console.log(colors.info('Valid types: sense, conduit, cognition, memory, perception, dialog, activation')) return } const count = brainy.enableAugmentationType(options.type) console.log(colors.success(`āœ… Enabled ${count} ${options.type} augmentations`)) }, 'disable-type': async () => { if (!options.type) { console.log(colors.error('Type required: --type ')) console.log(colors.info('Valid types: sense, conduit, cognition, memory, perception, dialog, activation')) return } const count = brainy.disableAugmentationType(options.type) console.log(colors.warning(`⚪ Disabled ${count} ${options.type} augmentations`)) } } if (actions[action]) { await actions[action]() } else { console.log(colors.error('Valid actions: list, enable, disable, register, unregister, enable-type, disable-type')) console.log(colors.info('\nExamples:')) console.log(colors.dim(' brainy augment list # List all augmentations')) console.log(colors.dim(' brainy augment enable --name neural-import # Enable an augmentation')) console.log(colors.dim(' brainy augment register --path ./my-augmentation.js # Register custom augmentation')) console.log(colors.dim(' brainy augment enable-type --type sense # Enable all sense augmentations')) } })) // Command 7: EXPORT - Export your data program .command('export') .description('Export your brain data in various formats') .option('-f, --format ', 'Export format (json, csv, graph, embeddings)', 'json') .option('-o, --output ', 'Output file path') .option('--vectors', 'Include vector embeddings') .option('--no-metadata', 'Exclude metadata') .option('--no-relationships', 'Exclude relationships') .option('--filter ', 'Filter by metadata') .option('-l, --limit ', 'Limit number of items') .action(wrapAction(async (options) => { const brainy = await initBrainy() console.log(colors.brain('šŸ“¤ Exporting Brain Data')) const spinner = ora('Exporting data...').start() try { const exportOptions = { format: options.format, includeVectors: options.vectors || false, includeMetadata: options.metadata !== false, includeRelationships: options.relationships !== false, filter: options.filter ? JSON.parse(options.filter) : {}, limit: options.limit ? parseInt(options.limit) : undefined } const data = await brainy.export(exportOptions) spinner.succeed('Export complete') if (options.output) { // Write to file const fs = require('fs') const content = typeof data === 'string' ? data : JSON.stringify(data, null, 2) fs.writeFileSync(options.output, content) console.log(colors.success(`āœ… Exported to: ${options.output}`)) // Show summary const items = Array.isArray(data) ? data.length : (data.nodes ? data.nodes.length : 1) console.log(colors.info(`šŸ“Š Format: ${options.format}`)) console.log(colors.info(`šŸ“ Items: ${items}`)) if (options.vectors) { console.log(colors.info(`šŸ”¢ Vectors: Included`)) } } else { // Output to console if (typeof data === 'string') { console.log(data) } else { console.log(JSON.stringify(data, null, 2)) } } } catch (error) { spinner.fail('Export failed') console.error(colors.error(error.message)) process.exit(1) } })) // Command 8: CLOUD - Premium features connection program .command('cloud ') .description('ā˜ļø Brain Cloud - AI Memory, Team Sync, Enterprise Connectors (FREE TRIAL!)') .option('-i, --instance ', 'Brain Cloud instance ID') .option('-e, --email ', 'Your email for signup') .action(wrapAction(async (action, options) => { console.log(boxen( colors.brain('ā˜ļø BRAIN CLOUD - SUPERCHARGE YOUR BRAIN! šŸš€\n\n') + colors.success('✨ FREE TRIAL: First 100GB FREE!\n') + colors.info('šŸ’° Then just $9/month (individuals) or $49/month (teams)\n\n') + colors.primary('Features:\n') + colors.dim(' • AI Memory that persists across sessions\n') + colors.dim(' • Multi-agent coordination\n') + colors.dim(' • Automatic backups & sync\n') + colors.dim(' • Premium connectors (Notion, Slack, etc.)'), { padding: 1, borderStyle: 'round', borderColor: 'cyan' } )) const cloudActions = { setup: async () => { console.log(colors.brain('\nšŸš€ Quick Setup - 30 seconds to superpowers!\n')) if (!options.email) { const { email } = await prompts({ type: 'text', name: 'email', message: 'Enter your email for FREE trial:', validate: (value) => value.includes('@') || 'Please enter a valid email' }) options.email = email } console.log(colors.success(`\nāœ… Setting up Brain Cloud for: ${options.email}`)) console.log(colors.info('\nšŸ“§ Check your email for activation link!')) console.log(colors.dim('\nOr visit: https://app.soulcraft.com/activate\n')) // Cloud features planned for future release console.log(colors.brain('šŸŽ‰ Your Brain Cloud trial is ready!')) console.log(colors.success('\nNext steps:')) console.log(colors.dim(' 1. Check your email for API key')) console.log(colors.dim(' 2. Run: brainy cloud connect --key YOUR_KEY')) console.log(colors.dim(' 3. Start using persistent AI memory!')) }, connect: async () => { console.log(colors.info('šŸ”— Connecting to Brain Cloud...')) // Dynamic import to avoid loading premium code unnecessarily console.log(colors.info('šŸ”— Cloud features coming soon...')) console.log(colors.info('Brainy works offline by default')) }, status: async () => { console.log(colors.info('ā˜ļø Cloud Status: Available in future release')) console.log(colors.info('Current version works offline')) }, augmentations: async () => { console.log(colors.info('🧩 Cloud augmentations coming in future release')) console.log(colors.info('Local augmentations available now')) } } if (cloudActions[action]) { await cloudActions[action]() } else { console.log(colors.error('Valid actions: connect, status, augmentations')) console.log(colors.info('Example: brainy cloud connect --instance demo-test-auto')) } })) // Command 7: MIGRATE - Migration tools program .command('migrate ') .description('Migration tools for upgrades') .option('-f, --from ', 'Migrate from version') .option('-b, --backup', 'Create backup before migration') .action(wrapAction(async (action, options) => { console.log(colors.primary('šŸ”„ Brainy Migration Tools')) const migrateActions = { check: async () => { console.log(colors.info('šŸ” Checking for migration needs...')) // Check for deprecated methods, old config, etc. const issues = [] try { const { BrainyData } = await import('../dist/brainyData.js') const brainy = new BrainyData() // Check for old API usage console.log(colors.success('āœ… No migration issues found')) } catch (error) { console.log(colors.warning(`āš ļø Found issues: ${error.message}`)) } }, backup: async () => { console.log(colors.info('šŸ’¾ Creating backup...')) const { BrainyData } = await import('../dist/brainyData.js') const brainy = new BrainyData() const backup = await brainy.createBackup() console.log(colors.success(`āœ… Backup created: ${backup.path}`)) }, restore: async () => { if (!options.from) { console.error(colors.error('Please specify backup file: --from ')) process.exit(1) } console.log(colors.info(`šŸ“„ Restoring from: ${options.from}`)) const { BrainyData } = await import('../dist/brainyData.js') const brainy = new BrainyData() await brainy.restoreBackup(options.from) console.log(colors.success('āœ… Restore complete')) } } if (migrateActions[action]) { await migrateActions[action]() } else { console.log(colors.error('Valid actions: check, backup, restore')) console.log(colors.info('Example: brainy migrate check')) } })) // Command 8: HELP - Interactive guidance program .command('help [command]') .description('Get help or enter interactive mode') .action(wrapAction(async (command) => { if (command) { program.help() return } // Interactive mode for beginners console.log(colors.primary('šŸ§ āš›ļø Welcome to Brainy!')) console.log(colors.info('Your AI-powered second brain')) console.log() const rl = createInterface({ input: process.stdin, output: process.stdout }) console.log(colors.primary('What would you like to do?')) console.log(colors.info('1. Add some data')) console.log(colors.info('2. Chat with AI using your data')) console.log(colors.info('3. Search your brain')) console.log(colors.info('4. Update existing data')) console.log(colors.info('5. Delete data')) console.log(colors.info('6. Import a file')) console.log(colors.info('7. Check status')) console.log(colors.info('8. Connect to Brain Cloud')) console.log(colors.info('9. Configuration')) console.log(colors.info('10. Show all commands')) console.log() const choice = await new Promise(resolve => { rl.question(colors.primary('Enter your choice (1-10): '), (answer) => { rl.close() resolve(answer) }) }) switch (choice) { case '1': console.log(colors.success('\n🧠 Use: brainy add "your data here"')) console.log(colors.info('Example: brainy add "John works at Google"')) break case '2': console.log(colors.success('\nšŸ’¬ Use: brainy chat "your question"')) console.log(colors.info('Example: brainy chat "Tell me about my data"')) console.log(colors.info('Supports: local (Ollama), OpenAI, Claude')) break case '3': console.log(colors.success('\nšŸ” Use: brainy search "your query"')) console.log(colors.info('Example: brainy search "Google employees"')) break case '4': console.log(colors.success('\nšŸ“„ Use: brainy import ')) console.log(colors.info('Example: brainy import data.txt')) break case '5': console.log(colors.success('\nšŸ“Š Use: brainy status')) console.log(colors.info('Shows comprehensive brain statistics')) console.log(colors.info('Options: --simple (quick) or --verbose (detailed)')) break case '6': console.log(colors.success('\nā˜ļø Use: brainy cloud connect')) console.log(colors.info('Example: brainy cloud connect --instance demo-test-auto')) break case '7': console.log(colors.success('\nšŸ”§ Use: brainy config ')) console.log(colors.info('Example: brainy config list')) break case '8': program.help() break default: console.log(colors.warning('Invalid choice. Use "brainy --help" for all commands.')) } })) // ======================================== // FALLBACK - Show interactive help if no command // ======================================== // Handle --interactive flag if (process.argv.includes('-i') || process.argv.includes('--interactive')) { // Start full interactive mode console.log(colors.primary('🧠 Starting Interactive Mode...')) import('./brainy-interactive.js').then(module => { module.startInteractiveMode() }).catch(error => { console.error(colors.error('Failed to start interactive mode:'), error.message) // Fallback to simple interactive prompt program.parse(['node', 'brainy', 'help']) }) } else if (process.argv.length === 2) { // No arguments - show interactive help program.parse(['node', 'brainy', 'help']) } else { // Parse normally program.parse(process.argv) }