diff --git a/package.json b/package.json index 81e7c922..7a3e3077 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "types": "dist/index.d.ts", "type": "module", "bin": { - "brainy": "bin/brainy-minimal.js" + "brainy": "bin/brainy.js" }, "sideEffects": [ "./dist/setup.js", diff --git a/src/cli/commands/core.ts b/src/cli/commands/core.ts index 8cc04dc3..d646281b 100644 --- a/src/cli/commands/core.ts +++ b/src/cli/commands/core.ts @@ -24,8 +24,20 @@ interface AddOptions extends CoreOptions { interface SearchOptions extends CoreOptions { limit?: string + offset?: string threshold?: string - metadata?: string + type?: string + where?: string + near?: string + connectedTo?: string + connectedFrom?: string + via?: string + explain?: boolean + includeRelations?: boolean + fusion?: string + vectorWeight?: string + graphWeight?: string + fieldWeight?: string } interface GetOptions extends CoreOptions { @@ -48,10 +60,9 @@ interface ExportOptions extends CoreOptions { let brainyInstance: Brainy | null = null -const getBrainy = async (): Promise => { +const getBrainy = (): Brainy => { if (!brainyInstance) { brainyInstance = new Brainy() - await brainyInstance.init() } return brainyInstance } @@ -70,7 +81,7 @@ export const coreCommands = { const spinner = ora('Adding to neural database...').start() try { - const brain = await getBrainy() + const brain = getBrainy() let metadata: any = {} if (options.metadata) { @@ -142,48 +153,183 @@ export const coreCommands = { }, /** - * Search the neural database + * Search the neural database with Triple Intelligence™ */ async search(query: string, options: SearchOptions) { - const spinner = ora('Searching neural database...').start() - + const spinner = ora('Searching with Triple Intelligence™...').start() + try { - const brain = await getBrainy() - - const searchOptions: any = { + const brain = getBrainy() + + // Build comprehensive search params + const searchParams: any = { + query, limit: options.limit ? parseInt(options.limit) : 10 } - - if (options.threshold) { - searchOptions.threshold = parseFloat(options.threshold) + + // Pagination + if (options.offset) { + searchParams.offset = parseInt(options.offset) } - - if (options.metadata) { + + // Vector Intelligence - similarity threshold + if (options.threshold) { + searchParams.near = { threshold: parseFloat(options.threshold) } + } + + // Metadata Intelligence - type filtering + if (options.type) { + const types = options.type.split(',').map(t => t.trim()) + searchParams.type = types.length === 1 ? types[0] : types + } + + // Metadata Intelligence - field filtering + if (options.where) { try { - searchOptions.filter = JSON.parse(options.metadata) + searchParams.where = JSON.parse(options.where) } catch { - spinner.fail('Invalid metadata filter JSON') + spinner.fail('Invalid --where JSON') + console.log(chalk.dim('Example: --where \'{"status":"active","priority":{"$gte":5}}\'')) process.exit(1) } } - - const results = await brain.search(query, searchOptions.limit) - + + // Vector Intelligence - proximity search + if (options.near) { + searchParams.near = { + id: options.near, + threshold: options.threshold ? parseFloat(options.threshold) : 0.7 + } + } + + // Graph Intelligence - connection constraints + if (options.connectedTo || options.connectedFrom || options.via) { + searchParams.connected = {} + + if (options.connectedTo) { + searchParams.connected.to = options.connectedTo + } + + if (options.connectedFrom) { + searchParams.connected.from = options.connectedFrom + } + + if (options.via) { + const vias = options.via.split(',').map(v => v.trim()) + searchParams.connected.via = vias.length === 1 ? vias[0] : vias + } + } + + // Explanation + if (options.explain) { + searchParams.explain = true + } + + // Include relationships + if (options.includeRelations) { + searchParams.includeRelations = true + } + + // Triple Intelligence Fusion - custom weighting + if (options.fusion || options.vectorWeight || options.graphWeight || options.fieldWeight) { + searchParams.fusion = { + strategy: options.fusion || 'adaptive', + weights: {} + } + + if (options.vectorWeight) { + searchParams.fusion.weights.vector = parseFloat(options.vectorWeight) + } + if (options.graphWeight) { + searchParams.fusion.weights.graph = parseFloat(options.graphWeight) + } + if (options.fieldWeight) { + searchParams.fusion.weights.field = parseFloat(options.fieldWeight) + } + } + + const results = await brain.find(searchParams) + spinner.succeed(`Found ${results.length} results`) - + if (!options.json) { if (results.length === 0) { - console.log(chalk.yellow('No results found')) + console.log(chalk.yellow('\nNo results found')) + + // Show helpful hints + console.log(chalk.dim('\nTips:')) + console.log(chalk.dim(' • Try different search terms')) + console.log(chalk.dim(' • Remove filters (--type, --where, --connected-to)')) + console.log(chalk.dim(' • Lower the --threshold value')) } else { + console.log(chalk.cyan(`\n📊 Triple Intelligence Results:\n`)) + results.forEach((result, i) => { - console.log(chalk.cyan(`\n${i + 1}. ${(result as any).content || result.id}`)) + const entity = result.entity || result + console.log(chalk.bold(`${i + 1}. ${entity.id}`)) + + // Show score with breakdown if (result.score !== undefined) { - console.log(chalk.dim(` Similarity: ${(result.score * 100).toFixed(1)}%`)) + console.log(chalk.green(` Score: ${(result.score * 100).toFixed(1)}%`)) + + if (options.explain && (result as any).scores) { + const scores = (result as any).scores + if (scores.vector !== undefined) { + console.log(chalk.dim(` Vector: ${(scores.vector * 100).toFixed(1)}%`)) + } + if (scores.graph !== undefined) { + console.log(chalk.dim(` Graph: ${(scores.graph * 100).toFixed(1)}%`)) + } + if (scores.field !== undefined) { + console.log(chalk.dim(` Field: ${(scores.field * 100).toFixed(1)}%`)) + } + } } - if (result.entity && result.entity.metadata) { - console.log(chalk.dim(` Metadata: ${JSON.stringify(result.entity.metadata)}`)) + + // Show type + if ((entity as any).type) { + console.log(chalk.dim(` Type: ${(entity as any).type}`)) } + + // Show content preview + if ((entity as any).content) { + const preview = (entity as any).content.substring(0, 80) + console.log(chalk.dim(` Content: ${preview}${(entity as any).content.length > 80 ? '...' : ''}`)) + } + + // Show metadata + if ((entity as any).metadata && Object.keys((entity as any).metadata).length > 0) { + console.log(chalk.dim(` Metadata: ${JSON.stringify((entity as any).metadata)}`)) + } + + // Show relationships + if (options.includeRelations && (result as any).relations) { + const relations = (result as any).relations + if (relations.length > 0) { + console.log(chalk.dim(` Relations: ${relations.length} connections`)) + } + } + + console.log() }) + + // Show search summary + console.log(chalk.cyan('Search Configuration:')) + if (searchParams.type) { + console.log(chalk.dim(` Type filter: ${Array.isArray(searchParams.type) ? searchParams.type.join(', ') : searchParams.type}`)) + } + if (searchParams.where) { + console.log(chalk.dim(` Field filter: ${JSON.stringify(searchParams.where)}`)) + } + if (searchParams.connected) { + console.log(chalk.dim(` Graph filter: ${JSON.stringify(searchParams.connected)}`)) + } + if (searchParams.fusion) { + console.log(chalk.dim(` Fusion: ${searchParams.fusion.strategy}`)) + if (searchParams.fusion.weights && Object.keys(searchParams.fusion.weights).length > 0) { + console.log(chalk.dim(` Weights: ${JSON.stringify(searchParams.fusion.weights)}`)) + } + } } } else { formatOutput(results, options) @@ -191,6 +337,9 @@ export const coreCommands = { } catch (error: any) { spinner.fail('Search failed') console.error(chalk.red(error.message)) + if (options.verbose) { + console.error(chalk.dim(error.stack)) + } process.exit(1) } }, @@ -202,26 +351,24 @@ export const coreCommands = { const spinner = ora('Fetching item...').start() try { - const brain = await getBrainy() + const brain = getBrainy() // Try to get the item - const results = await brain.search(id, 1) - - if (results.length === 0) { + const item = await brain.get(id) + + if (!item) { spinner.fail('Item not found') console.log(chalk.yellow(`No item found with ID: ${id}`)) process.exit(1) } - - const item = results[0] spinner.succeed('Item found') if (!options.json) { console.log(chalk.cyan('\nItem Details:')) console.log(` ID: ${item.id}`) console.log(` Content: ${(item as any).content || 'N/A'}`) - if (item.entity && item.entity.metadata) { - console.log(` Metadata: ${JSON.stringify(item.entity.metadata, null, 2)}`) + if (item.metadata) { + console.log(` Metadata: ${JSON.stringify(item.metadata, null, 2)}`) } if (options.withConnections) { @@ -252,7 +399,7 @@ export const coreCommands = { const spinner = ora('Creating relationship...').start() try { - const brain = await getBrainy() + const brain = getBrainy() let metadata: any = {} if (options.metadata) { @@ -301,7 +448,7 @@ export const coreCommands = { const spinner = ora('Importing data...').start() try { - const brain = await getBrainy() + const brain = getBrainy() const format = options.format || 'json' const batchSize = options.batchSize ? parseInt(options.batchSize) : 100 @@ -401,7 +548,7 @@ export const coreCommands = { const spinner = ora('Exporting database...').start() try { - const brain = await getBrainy() + const brain = getBrainy() const format = options.format || 'json' // Export all data diff --git a/src/cli/commands/data.ts b/src/cli/commands/data.ts new file mode 100644 index 00000000..f8fd021c --- /dev/null +++ b/src/cli/commands/data.ts @@ -0,0 +1,164 @@ +/** + * Data Management Commands + * + * Backup, restore, import, export operations + */ + +import chalk from 'chalk' +import ora from 'ora' +import { readFileSync, writeFileSync } from 'node:fs' +import { Brainy } from '../../brainy.js' + +interface DataOptions { + verbose?: boolean + json?: boolean + pretty?: boolean +} + +let brainyInstance: Brainy | null = null + +const getBrainy = (): Brainy => { + if (!brainyInstance) { + brainyInstance = new Brainy() + } + return brainyInstance +} + +const formatOutput = (data: any, options: DataOptions): void => { + if (options.json) { + console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)) + } +} + +export const dataCommands = { + /** + * Backup database + */ + async backup(file: string, options: DataOptions & { compress?: boolean }) { + const spinner = ora('Creating backup...').start() + + try { + const brain = getBrainy() + const dataApi = await brain.data() + + const backup = await dataApi.backup({ + compress: options.compress + }) + + spinner.text = 'Writing backup file...' + + // Write backup to file + const content = typeof backup === 'string' + ? backup + : JSON.stringify(backup, null, options.pretty ? 2 : 0) + + writeFileSync(file, content) + + spinner.succeed('Backup created') + + if (!options.json) { + console.log(chalk.green(`✓ Backup saved to: ${file}`)) + if ((backup as any).compressed) { + console.log(chalk.dim(` Original size: ${formatBytes((backup as any).originalSize)}`)) + console.log(chalk.dim(` Compressed size: ${formatBytes((backup as any).compressedSize)}`)) + console.log(chalk.dim(` Compression ratio: ${(((backup as any).compressedSize / (backup as any).originalSize) * 100).toFixed(1)}%`)) + } + } else { + formatOutput({ file, backup: true }, options) + } + } catch (error: any) { + spinner.fail('Backup failed') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Restore from backup + */ + async restore(file: string, options: DataOptions & { merge?: boolean }) { + const spinner = ora('Restoring from backup...').start() + + try { + const brain = getBrainy() + const dataApi = await brain.data() + + // Read backup file + const content = readFileSync(file, 'utf-8') + const backup = JSON.parse(content) + + // Restore + await dataApi.restore({ + backup, + merge: options.merge || false + }) + + spinner.succeed('Restore complete') + + if (!options.json) { + console.log(chalk.green(`✓ Restored from: ${file}`)) + if (options.merge) { + console.log(chalk.dim(' Mode: Merged with existing data')) + } else { + console.log(chalk.dim(' Mode: Replaced all data')) + } + } else { + formatOutput({ file, restored: true }, options) + } + } catch (error: any) { + spinner.fail('Restore failed') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Get database statistics + */ + async stats(options: DataOptions) { + const spinner = ora('Gathering statistics...').start() + + try { + const brain = getBrainy() + const dataApi = await brain.data() + + const stats = await dataApi.getStats() + + spinner.succeed('Statistics gathered') + + if (!options.json) { + console.log(chalk.cyan('\n📊 Database Statistics:\n')) + + console.log(chalk.bold('Entities:')) + console.log(` Total: ${chalk.green(stats.entities)}`) + + console.log(chalk.bold('\nRelationships:')) + console.log(` Total: ${chalk.green(stats.relations)}`) + + if ((stats as any).storageSize) { + console.log(chalk.bold('\nStorage:')) + console.log(` Size: ${chalk.green(formatBytes((stats as any).storageSize))}`) + } + + if ((stats as any).vectorDimensions) { + console.log(chalk.bold('\nVector Index:')) + console.log(` Dimensions: ${(stats as any).vectorDimensions}`) + } + } else { + formatOutput(stats, options) + } + } catch (error: any) { + spinner.fail('Failed to get statistics') + console.error(chalk.red(error.message)) + process.exit(1) + } + } +} + +function formatBytes(bytes: number): string { + if (bytes === 0) return '0 B' + const k = 1024 + const sizes = ['B', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] +} \ No newline at end of file diff --git a/src/cli/commands/neural.ts b/src/cli/commands/neural.ts index f292d4ea..63ad9d38 100644 --- a/src/cli/commands/neural.ts +++ b/src/cli/commands/neural.ts @@ -9,8 +9,7 @@ import chalk from 'chalk' import ora from 'ora' import fs from 'node:fs' import path from 'node:path' -import { Brainy } from '../../brainyData.js' -import { NeuralAPI } from '../../neural/neuralAPI.js' +import { Brainy } from '../../brainy.js' interface CommandArguments { action?: string; @@ -96,10 +95,10 @@ export const neuralCommand = { handler: async (argv: CommandArguments) => { console.log(chalk.cyan('\n🧠 NEURAL SIMILARITY API')) console.log(chalk.gray('━'.repeat(50))) - + // Initialize Brainy and Neural API const brain = new Brainy() - const neural = new NeuralAPI(brain) + const neural = brain.neural() try { const action = argv.action || await promptForAction() @@ -118,7 +117,9 @@ export const neuralCommand = { await handleNeighborsCommand(neural, argv) break case 'path': - await handlePathCommand(neural, argv) + console.log(chalk.yellow('\n⚠️ Semantic path finding coming in v3.21.0')) + console.log(chalk.dim('This feature requires implementing graph traversal algorithms')) + console.log(chalk.dim('Use "neighbors" and "hierarchy" commands to explore connections')) break case 'outliers': await handleOutliersCommand(neural, argv) @@ -147,7 +148,7 @@ async function promptForAction(): Promise { { name: '🎯 Find semantic clusters', value: 'clusters' }, { name: '🌳 Show item hierarchy', value: 'hierarchy' }, { name: '🕸️ Find semantic neighbors', value: 'neighbors' }, - { name: '🛣️ Find semantic path between items', value: 'path' }, + { name: '🛣️ Find semantic path between items (v3.21.0)', value: 'path', disabled: true }, { name: '🚨 Detect outliers', value: 'outliers' }, { name: '📊 Generate visualization data', value: 'visualize' } ] @@ -156,7 +157,7 @@ async function promptForAction(): Promise { return answer.action } -async function handleSimilarCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handleSimilarCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('🧠 Calculating semantic similarity...').start() try { @@ -230,7 +231,7 @@ async function handleSimilarCommand(neural: NeuralAPI, argv: CommandArguments): } } -async function handleClustersCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handleClustersCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('🎯 Finding semantic clusters...').start() try { @@ -277,7 +278,7 @@ async function handleClustersCommand(neural: NeuralAPI, argv: CommandArguments): } } -async function handleHierarchyCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handleHierarchyCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('🌳 Building semantic hierarchy...').start() try { @@ -342,7 +343,7 @@ function displayHierarchy(hierarchy: any): void { } } -async function handleNeighborsCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handleNeighborsCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('🕸️ Finding semantic neighbors...').start() try { @@ -398,7 +399,7 @@ async function handleNeighborsCommand(neural: NeuralAPI, argv: CommandArguments) } } -async function handlePathCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handlePathCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('🛣️ Finding semantic path...').start() try { @@ -455,7 +456,7 @@ async function handlePathCommand(neural: NeuralAPI, argv: CommandArguments): Pro } } -async function handleOutliersCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handleOutliersCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('🚨 Detecting semantic outliers...').start() try { @@ -484,7 +485,7 @@ async function handleOutliersCommand(neural: NeuralAPI, argv: CommandArguments): } } -async function handleVisualizeCommand(neural: NeuralAPI, argv: CommandArguments): Promise { +async function handleVisualizeCommand(neural: any, argv: CommandArguments): Promise { const spinner = ora('📊 Generating visualization data...').start() try { @@ -574,4 +575,14 @@ function showHelp(): void { console.log('') } +export const neuralCommands = { + similar: handleSimilarCommand, + cluster: handleClustersCommand, + hierarchy: handleHierarchyCommand, + related: handleNeighborsCommand, + // path: handlePathCommand, // Coming in v3.21.0 - requires graph traversal implementation + outliers: handleOutliersCommand, + visualize: handleVisualizeCommand +} + export default neuralCommand \ No newline at end of file diff --git a/src/cli/commands/utility.ts b/src/cli/commands/utility.ts index 04d61ceb..7520f914 100644 --- a/src/cli/commands/utility.ts +++ b/src/cli/commands/utility.ts @@ -7,7 +7,8 @@ import chalk from 'chalk' import ora from 'ora' import Table from 'cli-table3' -import { Brainy } from '../../brainyData.js' +import { Brainy } from '../../brainy.js' +import { NounType } from '../../types/graphTypes.js' interface UtilityOptions { verbose?: boolean @@ -32,10 +33,9 @@ interface BenchmarkOptions extends UtilityOptions { let brainyInstance: Brainy | null = null -const getBrainy = async (): Promise => { +const getBrainy = (): Brainy => { if (!brainyInstance) { brainyInstance = new Brainy() - await brainyInstance.init() } return brainyInstance } @@ -60,132 +60,59 @@ export const utilityCommands = { */ async stats(options: StatsOptions) { const spinner = ora('Gathering statistics...').start() - + try { - const brain = await getBrainy() - const stats = await brain.getStatistics() + const brain = getBrainy() + const nounCount = await brain.getNounCount() + const verbCount = await brain.getVerbCount() const memUsage = process.memoryUsage() - + spinner.succeed('Statistics gathered') - + + const stats = { + nounCount, + verbCount, + totalItems: nounCount + verbCount + } + if (options.json) { formatOutput(stats, options) return } - + console.log(chalk.cyan('\n📊 Database Statistics\n')) - + // Core stats table const coreTable = new Table({ head: [chalk.cyan('Metric'), chalk.cyan('Value')], style: { head: [], border: [] } }) - + coreTable.push( - ['Total Items', chalk.green(stats.nounCount + stats.verbCount + stats.metadataCount || 0)], - ['Nouns', chalk.green(stats.nounCount || 0)], - ['Verbs (Relationships)', chalk.green(stats.verbCount || 0)], - ['Metadata Records', chalk.green(stats.metadataCount || 0)] + ['Total Items', chalk.green(stats.totalItems)], + ['Nouns', chalk.green(stats.nounCount)], + ['Verbs (Relationships)', chalk.green(stats.verbCount)] ) - + console.log(coreTable.toString()) - - // Service breakdown if available - if (options.byService && stats.serviceBreakdown) { - console.log(chalk.cyan('\n🔧 Service Breakdown\n')) - - const serviceTable = new Table({ - head: [chalk.cyan('Service'), chalk.cyan('Nouns'), chalk.cyan('Verbs'), chalk.cyan('Metadata')], - style: { head: [], border: [] } - }) - - Object.entries(stats.serviceBreakdown).forEach(([service, serviceStats]: [string, any]) => { - serviceTable.push([ - service, - serviceStats.nounCount || 0, - serviceStats.verbCount || 0, - serviceStats.metadataCount || 0 - ]) - }) - - console.log(serviceTable.toString()) - } - - // Storage info - if (stats.storage) { - console.log(chalk.cyan('\n💾 Storage\n')) - - const storageTable = new Table({ - head: [chalk.cyan('Property'), chalk.cyan('Value')], - style: { head: [], border: [] } - }) - - storageTable.push( - ['Type', stats.storage.type || 'Unknown'], - ['Size', stats.storage.size ? formatBytes(stats.storage.size) : 'N/A'], - ['Location', stats.storage.location || 'N/A'] - ) - - console.log(storageTable.toString()) - } - - // Performance metrics - if (stats.performance && options.detailed) { - console.log(chalk.cyan('\n⚡ Performance\n')) - - const perfTable = new Table({ - head: [chalk.cyan('Metric'), chalk.cyan('Value')], - style: { head: [], border: [] } - }) - - if (stats.performance.avgQueryTime) { - perfTable.push(['Avg Query Time', `${stats.performance.avgQueryTime.toFixed(2)} ms`]) - } - if (stats.performance.totalQueries) { - perfTable.push(['Total Queries', stats.performance.totalQueries]) - } - if (stats.performance.cacheHitRate) { - perfTable.push(['Cache Hit Rate', `${(stats.performance.cacheHitRate * 100).toFixed(1)}%`]) - } - - console.log(perfTable.toString()) - } - + // Memory usage console.log(chalk.cyan('\n🧠 Memory Usage\n')) - + const memTable = new Table({ head: [chalk.cyan('Type'), chalk.cyan('Size')], style: { head: [], border: [] } }) - + memTable.push( ['Heap Used', formatBytes(memUsage.heapUsed)], ['Heap Total', formatBytes(memUsage.heapTotal)], ['RSS', formatBytes(memUsage.rss)], ['External', formatBytes(memUsage.external)] ) - + console.log(memTable.toString()) - // Index info - if (stats.index && options.detailed) { - console.log(chalk.cyan('\n🎯 Vector Index\n')) - - const indexTable = new Table({ - head: [chalk.cyan('Property'), chalk.cyan('Value')], - style: { head: [], border: [] } - }) - - indexTable.push( - ['Dimensions', stats.index.dimensions || 'N/A'], - ['Indexed Vectors', stats.index.vectorCount || 0], - ['Index Size', stats.index.indexSize ? formatBytes(stats.index.indexSize) : 'N/A'] - ) - - console.log(indexTable.toString()) - } - } catch (error: any) { spinner.fail('Failed to gather statistics') console.error(chalk.red(error.message)) @@ -198,47 +125,36 @@ export const utilityCommands = { */ async clean(options: CleanOptions) { const spinner = ora('Cleaning database...').start() - + try { - const brain = await getBrainy() - const tasks: string[] = [] - - if (options.removeOrphans) { - spinner.text = 'Removing orphaned items...' - tasks.push('Removed orphaned items') - // Implementation would go here - await new Promise(resolve => setTimeout(resolve, 500)) // Simulate work + const brain = getBrainy() + + // For now, only support full clear + // removeOrphans and rebuildIndex would require new Brainy APIs + if (options.removeOrphans || options.rebuildIndex) { + spinner.warn('Advanced cleanup options not yet implemented') + console.log(chalk.yellow('\n⚠️ Advanced cleanup features coming in v3.21.0:')) + console.log(chalk.dim(' • --remove-orphans: Remove disconnected items')) + console.log(chalk.dim(' • --rebuild-index: Rebuild vector index')) + console.log(chalk.dim('\nUse "brainy clean" without options to clear the database')) + return } - - if (options.rebuildIndex) { - spinner.text = 'Rebuilding search index...' - tasks.push('Rebuilt search index') - // Implementation would go here - await new Promise(resolve => setTimeout(resolve, 1000)) // Simulate work - } - - if (tasks.length === 0) { - spinner.text = 'Running general cleanup...' - tasks.push('General cleanup completed') - // Run general cleanup tasks - await new Promise(resolve => setTimeout(resolve, 500)) // Simulate work - } - - spinner.succeed('Database cleaned') - + + // Show warning before clearing + console.log(chalk.yellow('\n⚠️ WARNING: This will permanently delete ALL data!')) + const dataApi = await brain.data() + + // Clear all data + spinner.text = 'Clearing all data...' + await dataApi.clear({ entities: true, relations: true }) + + spinner.succeed('Database cleared') + if (!options.json) { - console.log(chalk.green('\n✓ Cleanup completed:')) - tasks.forEach(task => { - console.log(chalk.dim(` • ${task}`)) - }) - - // Get new stats - const stats = await brain.getStatistics() - console.log(chalk.cyan('\nDatabase Status:')) - console.log(` Total items: ${stats.nounCount + stats.verbCount}`) - console.log(` Index status: ${chalk.green('Healthy')}`) + console.log(chalk.green('\n✓ Database cleared successfully')) + console.log(chalk.dim(' All nouns, verbs, and metadata have been removed')) } else { - formatOutput({ tasks, success: true }, options) + formatOutput({ cleared: true, success: true }, options) } } catch (error: any) { spinner.fail('Cleanup failed') @@ -262,7 +178,7 @@ export const utilityCommands = { } try { - const brain = await getBrainy() + const brain = getBrainy() // Benchmark different operations const benchmarks = [ @@ -283,17 +199,17 @@ export const utilityCommands = { switch (bench.name) { case 'add': - await brain.add(`Test item ${i}`, { benchmark: true }) + await brain.add({ data: `Test item ${i}`, type: NounType.Thing, metadata: { benchmark: true } }) break case 'search': - await brain.search('test', 10) + await brain.find({ query: 'test', limit: 10 }) break case 'similarity': - const neural = brain.neural + const neural = brain.neural() await neural.similar('test1', 'test2') break case 'cluster': - const neuralApi = brain.neural + const neuralApi = brain.neural() await neuralApi.clusters() break } @@ -319,12 +235,12 @@ export const utilityCommands = { } // Calculate summary - const totalOps = Object.values(results.operations).reduce((sum: number, op: any) => + const totalOps: number = (Object.values(results.operations) as any[]).reduce((sum: number, op: any) => sum + parseFloat(op.ops), 0) - + results.summary = { totalOperations: Object.keys(results.operations).length, - averageOpsPerSec: (totalOps / Object.keys(results.operations).length).toFixed(2) + averageOpsPerSec: totalOps > 0 ? (totalOps / Object.keys(results.operations).length).toFixed(2) : '0' } if (!options.json) { diff --git a/src/cli/commands/vfs.ts b/src/cli/commands/vfs.ts new file mode 100644 index 00000000..5b0c17ef --- /dev/null +++ b/src/cli/commands/vfs.ts @@ -0,0 +1,411 @@ +/** + * VFS CLI Commands - Virtual File System Operations + * + * Complete filesystem-like interface for Brainy's VFS + */ + +import chalk from 'chalk' +import ora from 'ora' +import Table from 'cli-table3' +import { readFileSync, writeFileSync } from 'node:fs' +import { Brainy } from '../../brainy.js' + +interface VFSOptions { + verbose?: boolean + json?: boolean + pretty?: boolean +} + +let brainyInstance: Brainy | null = null + +const getBrainy = (): Brainy => { + if (!brainyInstance) { + brainyInstance = new Brainy() + } + return brainyInstance +} + +const formatOutput = (data: any, options: VFSOptions): void => { + if (options.json) { + console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data)) + } +} + +const formatBytes = (bytes: number): string => { + if (bytes === 0) return '0 B' + const k = 1024 + const sizes = ['B', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] +} + +const formatDate = (date: Date): string => { + return date.toLocaleString() +} + +export const vfsCommands = { + /** + * Read file from VFS + */ + async read(path: string, options: VFSOptions & { output?: string; encoding?: string }) { + const spinner = ora('Reading file...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const buffer = await vfs.readFile(path, { + encoding: options.encoding as any + }) + + spinner.succeed('File read successfully') + + if (options.output) { + // Write to local filesystem + writeFileSync(options.output, buffer) + console.log(chalk.green(`✓ Saved to: ${options.output}`)) + } else if (!options.json) { + // Display content + console.log('\n' + buffer.toString()) + } else { + formatOutput({ path, content: buffer.toString(), size: buffer.length }, options) + } + } catch (error: any) { + spinner.fail('Failed to read file') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Write file to VFS + */ + async write(path: string, options: VFSOptions & { content?: string; file?: string; encoding?: string }) { + const spinner = ora('Writing file...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + let data: string + if (options.file) { + // Read from local file + data = readFileSync(options.file, 'utf-8') + } else if (options.content) { + data = options.content + } else { + spinner.fail('Must provide --content or --file') + process.exit(1) + } + + await vfs.writeFile(path, data, { + encoding: options.encoding as any + }) + + spinner.succeed('File written successfully') + + if (!options.json) { + console.log(chalk.green(`✓ Written to: ${path}`)) + console.log(chalk.dim(` Size: ${formatBytes(Buffer.byteLength(data))}`)) + } else { + formatOutput({ path, size: Buffer.byteLength(data) }, options) + } + } catch (error: any) { + spinner.fail('Failed to write file') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * List directory contents + */ + async ls(path: string, options: VFSOptions & { long?: boolean; all?: boolean }) { + const spinner = ora('Listing directory...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const entries = await vfs.readdir(path, { withFileTypes: true }) + + spinner.succeed(`Found ${Array.isArray(entries) ? entries.length : 0} items`) + + if (!options.json) { + if (!Array.isArray(entries) || entries.length === 0) { + console.log(chalk.yellow('Directory is empty')) + return + } + + if (options.long) { + // Long format with details + const table = new Table({ + head: [chalk.cyan('Type'), chalk.cyan('Size'), chalk.cyan('Modified'), chalk.cyan('Name')], + style: { head: [], border: [] } + }) + + for (const entry of entries as any[]) { + if (!options.all && entry.name.startsWith('.')) continue + + const stat = await vfs.stat(`${path}/${entry.name}`) + table.push([ + entry.isDirectory() ? chalk.blue('DIR') : 'FILE', + entry.isDirectory() ? '-' : formatBytes(stat.size), + formatDate(stat.mtime), + entry.name + ]) + } + + console.log('\n' + table.toString()) + } else { + // Simple format + console.log() + for (const entry of entries as any[]) { + if (!options.all && entry.name.startsWith('.')) continue + + if (entry.isDirectory()) { + console.log(chalk.blue(entry.name + '/')) + } else { + console.log(entry.name) + } + } + } + } else { + formatOutput(entries, options) + } + } catch (error: any) { + spinner.fail('Failed to list directory') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Get file/directory stats + */ + async stat(path: string, options: VFSOptions) { + const spinner = ora('Getting file stats...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const stats = await vfs.stat(path) + + spinner.succeed('Stats retrieved') + + if (!options.json) { + console.log(chalk.cyan('\nFile Statistics:')) + console.log(` Path: ${path}`) + console.log(` Type: ${stats.isDirectory() ? chalk.blue('Directory') : 'File'}`) + console.log(` Size: ${formatBytes(stats.size)}`) + console.log(` Created: ${formatDate(stats.birthtime)}`) + console.log(` Modified: ${formatDate(stats.mtime)}`) + console.log(` Accessed: ${formatDate(stats.atime)}`) + } else { + formatOutput(stats, options) + } + } catch (error: any) { + spinner.fail('Failed to get stats') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Create directory + */ + async mkdir(path: string, options: VFSOptions & { parents?: boolean }) { + const spinner = ora('Creating directory...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + await vfs.mkdir(path, { recursive: options.parents }) + + spinner.succeed('Directory created') + + if (!options.json) { + console.log(chalk.green(`✓ Created: ${path}`)) + } else { + formatOutput({ path, created: true }, options) + } + } catch (error: any) { + spinner.fail('Failed to create directory') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Remove file or directory + */ + async rm(path: string, options: VFSOptions & { recursive?: boolean; force?: boolean }) { + const spinner = ora('Removing...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const stats = await vfs.stat(path) + + if (stats.isDirectory()) { + await vfs.rmdir(path, { recursive: options.recursive }) + } else { + await vfs.unlink(path) + } + + spinner.succeed('Removed successfully') + + if (!options.json) { + console.log(chalk.green(`✓ Removed: ${path}`)) + } else { + formatOutput({ path, removed: true }, options) + } + } catch (error: any) { + spinner.fail('Failed to remove') + console.error(chalk.red(error.message)) + if (!options.force) { + process.exit(1) + } + } + }, + + /** + * Search files by content + */ + async search(query: string, options: VFSOptions & { path?: string; limit?: string; type?: string }) { + const spinner = ora('Searching files...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const results = await vfs.search(query, { + path: options.path, + limit: options.limit ? parseInt(options.limit) : 10 + }) + + spinner.succeed(`Found ${results.length} results`) + + if (!options.json) { + if (results.length === 0) { + console.log(chalk.yellow('No results found')) + } else { + console.log(chalk.cyan('\n📄 Search Results:\n')) + + results.forEach((result, i) => { + console.log(chalk.bold(`${i + 1}. ${result.path}`)) + if (result.score) { + console.log(chalk.dim(` Score: ${(result.score * 100).toFixed(1)}%`)) + } + if ((result as any).excerpt) { + console.log(chalk.dim(` ${(result as any).excerpt}`)) + } + console.log() + }) + } + } else { + formatOutput(results, options) + } + } catch (error: any) { + spinner.fail('Search failed') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Find similar files + */ + async similar(path: string, options: VFSOptions & { limit?: string; threshold?: string }) { + const spinner = ora('Finding similar files...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const results = await vfs.findSimilar(path, { + limit: options.limit ? parseInt(options.limit) : 10, + threshold: options.threshold ? parseFloat(options.threshold) : 0.7 + }) + + spinner.succeed(`Found ${results.length} similar files`) + + if (!options.json) { + if (results.length === 0) { + console.log(chalk.yellow('No similar files found')) + } else { + console.log(chalk.cyan('\n🔗 Similar Files:\n')) + + results.forEach((result, i) => { + console.log(chalk.bold(`${i + 1}. ${result.path}`)) + if (result.score) { + console.log(chalk.green(` Similarity: ${(result.score * 100).toFixed(1)}%`)) + } + console.log() + }) + } + } else { + formatOutput(results, options) + } + } catch (error: any) { + spinner.fail('Failed to find similar files') + console.error(chalk.red(error.message)) + process.exit(1) + } + }, + + /** + * Get directory tree structure + */ + async tree(path: string, options: VFSOptions & { depth?: string }) { + const spinner = ora('Building tree...').start() + + try { + const brain = getBrainy() + const vfs = brain.vfs() + await vfs.init() + + const tree = await vfs.getTreeStructure(path, { + maxDepth: options.depth ? parseInt(options.depth) : 3 + }) + + spinner.succeed('Tree built') + + if (!options.json) { + console.log(chalk.cyan(`\n📁 ${path}\n`)) + displayTree(tree, '', true) + } else { + formatOutput(tree, options) + } + } catch (error: any) { + spinner.fail('Failed to build tree') + console.error(chalk.red(error.message)) + process.exit(1) + } + } +} + +function displayTree(node: any, prefix: string, isLast: boolean) { + const connector = isLast ? '└── ' : '├── ' + const name = node.isDirectory ? chalk.blue(node.name + '/') : node.name + + console.log(prefix + connector + name) + + if (node.children && node.children.length > 0) { + const childPrefix = prefix + (isLast ? ' ' : '│ ') + node.children.forEach((child: any, i: number) => { + displayTree(child, childPrefix, i === node.children.length - 1) + }) + } +} \ No newline at end of file diff --git a/src/cli/index.ts b/src/cli/index.ts index 7e946504..68241b8f 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -8,8 +8,11 @@ import { Command } from 'commander' import chalk from 'chalk' -import ora from 'ora' -import { Brainy } from '../brainy.js' +import { neuralCommands } from './commands/neural.js' +import { coreCommands } from './commands/core.js' +import { utilityCommands } from './commands/utility.js' +import { vfsCommands } from './commands/vfs.js' +import { dataCommands } from './commands/data.js' import conversationCommand from './commands/conversation.js' import { readFileSync } from 'fs' import { fileURLToPath } from 'url' @@ -42,11 +45,29 @@ program .action(coreCommands.add) program - .command('search ') - .description('Search the neural database') + .command('find ') + .description('Simple NLP search (just like code: brain.find("query"))') .option('-k, --limit ', 'Number of results', '10') - .option('-t, --threshold ', 'Similarity threshold') - .option('--metadata ', 'Filter by metadata') + .action(coreCommands.search) + +program + .command('search ') + .description('Advanced search with Triple Intelligence™ (vector + graph + field)') + .option('-k, --limit ', 'Number of results', '10') + .option('--offset ', 'Skip N results (pagination)') + .option('-t, --threshold ', 'Similarity threshold (0-1)', '0.7') + .option('--type ', 'Filter by type(s) - comma separated') + .option('--where ', 'Metadata filters (JSON)') + .option('--near ', 'Find items near this ID') + .option('--connected-to ', 'Connected to this entity') + .option('--connected-from ', 'Connected from this entity') + .option('--via ', 'Via these relationships - comma separated') + .option('--explain', 'Show scoring breakdown') + .option('--include-relations', 'Include entity relationships') + .option('--fusion ', 'Fusion strategy (adaptive|weighted|progressive)') + .option('--vector-weight ', 'Vector search weight (0-1)') + .option('--graph-weight ', 'Graph search weight (0-1)') + .option('--field-weight ', 'Field search weight (0-1)') .action(coreCommands.search) program @@ -118,10 +139,14 @@ program program .command('path ') - .description('Find semantic path between items') + .description('Find semantic path between items (v3.21.0)') .option('--steps', 'Show step-by-step path') .option('--max-hops ', 'Maximum path length', '5') - .action(neuralCommands.path) + .action(() => { + console.log(chalk.yellow('\n⚠️ Semantic path finding coming in v3.21.0')) + console.log(chalk.dim('This feature requires implementing graph traversal algorithms')) + console.log(chalk.dim('Use "brainy neighbors" and "brainy hierarchy" to explore connections')) + }) program .command('outliers') @@ -190,12 +215,217 @@ program }) ) +// ===== VFS Commands (Subcommand Group) ===== + +program + .command('vfs') + .description('📁 Virtual File System operations') + .addCommand( + new Command('read') + .argument('', 'File path') + .description('Read file from VFS') + .option('-o, --output ', 'Save to local file') + .option('--encoding ', 'File encoding', 'utf-8') + .action((path, options) => { + vfsCommands.read(path, options) + }) + ) + .addCommand( + new Command('write') + .argument('', 'File path') + .description('Write file to VFS') + .option('-c, --content ', 'File content') + .option('-f, --file ', 'Read from local file') + .option('--encoding ', 'File encoding', 'utf-8') + .action((path, options) => { + vfsCommands.write(path, options) + }) + ) + .addCommand( + new Command('ls') + .alias('list') + .argument('', 'Directory path') + .description('List directory contents') + .option('-l, --long', 'Long format with details') + .option('-a, --all', 'Show hidden files') + .action((path, options) => { + vfsCommands.ls(path, options) + }) + ) + .addCommand( + new Command('stat') + .argument('', 'File/directory path') + .description('Get file/directory statistics') + .action((path, options) => { + vfsCommands.stat(path, options) + }) + ) + .addCommand( + new Command('mkdir') + .argument('', 'Directory path') + .description('Create directory') + .option('-p, --parents', 'Create parent directories') + .action((path, options) => { + vfsCommands.mkdir(path, options) + }) + ) + .addCommand( + new Command('rm') + .argument('', 'File/directory path') + .description('Remove file or directory') + .option('-r, --recursive', 'Remove recursively') + .option('-f, --force', 'Force removal') + .action((path, options) => { + vfsCommands.rm(path, options) + }) + ) + .addCommand( + new Command('search') + .argument('', 'Search query') + .description('Search files by content') + .option('--path ', 'Search within path') + .option('-l, --limit ', 'Max results', '10') + .option('--type ', 'File type filter') + .action((query, options) => { + vfsCommands.search(query, options) + }) + ) + .addCommand( + new Command('similar') + .argument('', 'File path') + .description('Find similar files') + .option('-l, --limit ', 'Max results', '10') + .option('-t, --threshold ', 'Similarity threshold', '0.7') + .action((path, options) => { + vfsCommands.similar(path, options) + }) + ) + .addCommand( + new Command('tree') + .argument('', 'Directory path') + .description('Show directory tree') + .option('-d, --depth ', 'Max depth', '3') + .action((path, options) => { + vfsCommands.tree(path, options) + }) + ) + +// ===== VFS Commands (Backward Compatibility - Deprecated) ===== + +program + .command('vfs-read ') + .description('[DEPRECATED] Use: brainy vfs read ') + .option('-o, --output ', 'Save to local file') + .option('--encoding ', 'File encoding', 'utf-8') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-read" is deprecated. Use: brainy vfs read')) + vfsCommands.read(path, options) + }) + +program + .command('vfs-write ') + .description('[DEPRECATED] Use: brainy vfs write ') + .option('-c, --content ', 'File content') + .option('-f, --file ', 'Read from local file') + .option('--encoding ', 'File encoding', 'utf-8') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-write" is deprecated. Use: brainy vfs write')) + vfsCommands.write(path, options) + }) + +program + .command('vfs-ls ') + .alias('vfs-list') + .description('[DEPRECATED] Use: brainy vfs ls ') + .option('-l, --long', 'Long format with details') + .option('-a, --all', 'Show hidden files') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-ls" is deprecated. Use: brainy vfs ls')) + vfsCommands.ls(path, options) + }) + +program + .command('vfs-stat ') + .description('[DEPRECATED] Use: brainy vfs stat ') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-stat" is deprecated. Use: brainy vfs stat')) + vfsCommands.stat(path, options) + }) + +program + .command('vfs-mkdir ') + .description('[DEPRECATED] Use: brainy vfs mkdir ') + .option('-p, --parents', 'Create parent directories') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-mkdir" is deprecated. Use: brainy vfs mkdir')) + vfsCommands.mkdir(path, options) + }) + +program + .command('vfs-rm ') + .description('[DEPRECATED] Use: brainy vfs rm ') + .option('-r, --recursive', 'Remove recursively') + .option('-f, --force', 'Force removal') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-rm" is deprecated. Use: brainy vfs rm')) + vfsCommands.rm(path, options) + }) + +program + .command('vfs-search ') + .description('[DEPRECATED] Use: brainy vfs search ') + .option('--path ', 'Search within path') + .option('-l, --limit ', 'Max results', '10') + .option('--type ', 'File type filter') + .action((query, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-search" is deprecated. Use: brainy vfs search')) + vfsCommands.search(query, options) + }) + +program + .command('vfs-similar ') + .description('[DEPRECATED] Use: brainy vfs similar ') + .option('-l, --limit ', 'Max results', '10') + .option('-t, --threshold ', 'Similarity threshold', '0.7') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-similar" is deprecated. Use: brainy vfs similar')) + vfsCommands.similar(path, options) + }) + +program + .command('vfs-tree ') + .description('[DEPRECATED] Use: brainy vfs tree ') + .option('-d, --depth ', 'Max depth', '3') + .action((path, options) => { + console.log(chalk.yellow('⚠️ Command "vfs-tree" is deprecated. Use: brainy vfs tree')) + vfsCommands.tree(path, options) + }) + +// ===== Data Management Commands ===== + +program + .command('backup ') + .description('Create database backup') + .option('--compress', 'Compress backup') + .action(dataCommands.backup) + +program + .command('restore ') + .description('Restore from backup') + .option('--merge', 'Merge with existing data (default: replace)') + .action(dataCommands.restore) + +program + .command('data-stats') + .description('Show detailed database statistics') + .action(dataCommands.stats) + // ===== Utility Commands ===== program .command('stats') .alias('statistics') - .description('Show database statistics') + .description('Show quick database statistics') .option('--by-service', 'Group by service') .option('--detailed', 'Show detailed stats') .action(utilityCommands.stats) diff --git a/src/cli/interactive.ts b/src/cli/interactive.ts index 5f6876d4..005eea7e 100644 --- a/src/cli/interactive.ts +++ b/src/cli/interactive.ts @@ -7,7 +7,7 @@ import chalk from 'chalk' import inquirer from 'inquirer' -import fuzzy from 'fuzzy' +// import fuzzy from 'fuzzy' // TODO: Install fuzzy package or remove dependency import ora from 'ora' import { Brainy } from '../brainy.js' import { getBrainyVersion } from '../utils/version.js' @@ -126,13 +126,13 @@ export async function promptItemId( let choices: any[] = [] if (brain) { try { - const recent = await brain.search('*', { limit: 10, - sortBy: 'timestamp', - descending: true + const recent = await brain.find({ + query: '*', + limit: 10 }) choices = recent.map(item => ({ - name: `${item.id} - ${item.content?.substring(0, 50)}...`, + name: `${item.id} - ${(item as any).content?.substring(0, 50) || 'No content'}...`, value: item.id, short: item.id })) @@ -497,8 +497,19 @@ export async function promptRelationship(brain?: Brainy): Promise<{ * Smart command suggestions when user types wrong command */ export function suggestCommand(input: string, availableCommands: string[]): string[] { - const results = fuzzy.filter(input, availableCommands) - return results.slice(0, 3).map(r => r.string) + // Simple fuzzy matching without external dependency + // Filter commands that start with or contain the input + const matches = availableCommands + .filter(cmd => cmd.toLowerCase().includes(input.toLowerCase())) + .sort((a, b) => { + // Prefer commands that start with the input + const aStarts = a.toLowerCase().startsWith(input.toLowerCase()) + const bStarts = b.toLowerCase().startsWith(input.toLowerCase()) + if (aStarts && !bStarts) return -1 + if (!aStarts && bStarts) return 1 + return 0 + }) + return matches.slice(0, 3) } /** @@ -609,6 +620,16 @@ export async function promptCommand(): Promise { return command } +/** + * Start interactive REPL mode + */ +export async function startInteractiveMode() { + console.log(chalk.cyan('\n🧠 Brainy Interactive Mode\n')) + console.log(chalk.yellow('Interactive REPL mode coming in v3.20.0\n')) + console.log(chalk.dim('Use specific commands for now: brainy add, brainy search, etc.')) + process.exit(0) +} + /** * Export all interactive components */ @@ -628,5 +649,6 @@ export default { showError, ProgressTracker, showWelcome, - promptCommand + promptCommand, + startInteractiveMode } \ No newline at end of file diff --git a/tsconfig.cli.json b/tsconfig.cli.json index f511690d..9756dbce 100644 --- a/tsconfig.cli.json +++ b/tsconfig.cli.json @@ -7,7 +7,7 @@ "strict": false }, "include": [ - "src/cli/commands/conversation.ts", + "src/cli/**/*", "src/mcp/conversationTools.ts", "src/conversation/**/*" ],