2025-08-26 12:32:21 -07:00
/ * *
* Core CLI Commands - TypeScript Implementation
*
* Essential database operations : add , search , get , relate , import , export
* /
import chalk from 'chalk'
import ora from 'ora'
import { readFileSync , writeFileSync } from 'fs'
2025-09-11 16:23:32 -07:00
import { Brainy } from '../../brainy.js'
2025-09-01 09:37:36 -07:00
import { BrainyTypes , NounType , VerbType } from '../../index.js'
2025-08-26 12:32:21 -07:00
interface CoreOptions {
verbose? : boolean
json? : boolean
pretty? : boolean
}
interface AddOptions extends CoreOptions {
id? : string
metadata? : string
type ? : string
}
interface SearchOptions extends CoreOptions {
limit? : string
threshold? : string
metadata? : string
}
interface GetOptions extends CoreOptions {
withConnections? : boolean
}
interface RelateOptions extends CoreOptions {
weight? : string
metadata? : string
}
interface ImportOptions extends CoreOptions {
format ? : 'json' | 'csv' | 'jsonl'
batchSize? : string
}
interface ExportOptions extends CoreOptions {
format ? : 'json' | 'csv' | 'jsonl'
}
2025-09-11 16:23:32 -07:00
let brainyInstance : Brainy | null = null
2025-08-26 12:32:21 -07:00
2025-09-11 16:23:32 -07:00
const getBrainy = async ( ) : Promise < Brainy > = > {
2025-08-26 12:32:21 -07:00
if ( ! brainyInstance ) {
2025-09-11 16:23:32 -07:00
brainyInstance = new Brainy ( )
2025-08-26 12:32:21 -07:00
await brainyInstance . init ( )
}
return brainyInstance
}
const formatOutput = ( data : any , options : CoreOptions ) : void = > {
if ( options . json ) {
console . log ( options . pretty ? JSON . stringify ( data , null , 2 ) : JSON . stringify ( data ) )
}
}
export const coreCommands = {
/ * *
* Add data to the neural database
* /
async add ( text : string , options : AddOptions ) {
const spinner = ora ( 'Adding to neural database...' ) . start ( )
try {
const brain = await getBrainy ( )
let metadata : any = { }
if ( options . metadata ) {
try {
metadata = JSON . parse ( options . metadata )
} catch {
spinner . fail ( 'Invalid metadata JSON' )
process . exit ( 1 )
}
}
if ( options . id ) {
metadata . id = options . id
}
2025-09-01 09:37:36 -07:00
// Determine noun type
let nounType : NounType
2025-08-26 12:32:21 -07:00
if ( options . type ) {
2025-09-01 09:37:36 -07:00
// Validate provided type
if ( ! BrainyTypes . isValidNoun ( options . type ) ) {
spinner . fail ( ` Invalid noun type: ${ options . type } ` )
console . log ( chalk . dim ( 'Run "brainy types --noun" to see valid types' ) )
process . exit ( 1 )
}
nounType = options . type as NounType
} else {
// Use AI to suggest type
spinner . text = 'Detecting type with AI...'
const suggestion = await BrainyTypes . suggestNoun (
typeof text === 'string' ? { content : text , . . . metadata } : text
)
if ( suggestion . confidence < 0.6 ) {
spinner . fail ( 'Could not determine type with confidence' )
2025-09-11 16:23:32 -07:00
console . log ( chalk . yellow ( ` Suggestion: ${ suggestion . type } ( ${ ( suggestion . confidence * 100 ) . toFixed ( 1 ) } %) ` ) )
2025-09-01 09:37:36 -07:00
console . log ( chalk . dim ( 'Use --type flag to specify explicitly' ) )
process . exit ( 1 )
}
nounType = suggestion . type as NounType
spinner . text = ` Using detected type: ${ nounType } `
2025-08-26 12:32:21 -07:00
}
2025-09-01 09:37:36 -07:00
// Add with explicit type
2025-09-11 16:23:32 -07:00
const result = await brain . add ( {
data : text ,
type : nounType ,
metadata
} )
2025-08-26 12:32:21 -07:00
spinner . succeed ( 'Added successfully' )
if ( ! options . json ) {
console . log ( chalk . green ( ` ✓ Added with ID: ${ result } ` ) )
if ( options . type ) {
console . log ( chalk . dim ( ` Type: ${ options . type } ` ) )
}
if ( Object . keys ( metadata ) . length > 0 ) {
console . log ( chalk . dim ( ` Metadata: ${ JSON . stringify ( metadata ) } ` ) )
}
} else {
formatOutput ( { id : result , metadata } , options )
}
} catch ( error : any ) {
spinner . fail ( 'Failed to add data' )
console . error ( chalk . red ( error . message ) )
process . exit ( 1 )
}
} ,
/ * *
* Search the neural database
* /
async search ( query : string , options : SearchOptions ) {
const spinner = ora ( 'Searching neural database...' ) . start ( )
try {
const brain = await getBrainy ( )
const searchOptions : any = {
limit : options.limit ? parseInt ( options . limit ) : 10
}
if ( options . threshold ) {
searchOptions . threshold = parseFloat ( options . threshold )
}
if ( options . metadata ) {
try {
searchOptions . filter = JSON . parse ( options . metadata )
} catch {
spinner . fail ( 'Invalid metadata filter JSON' )
process . exit ( 1 )
}
}
2025-09-11 16:23:32 -07:00
const results = await brain . search ( query , searchOptions . limit )
2025-08-26 12:32:21 -07:00
spinner . succeed ( ` Found ${ results . length } results ` )
if ( ! options . json ) {
if ( results . length === 0 ) {
console . log ( chalk . yellow ( 'No results found' ) )
} else {
results . forEach ( ( result , i ) = > {
console . log ( chalk . cyan ( ` \ n ${ i + 1 } . ${ ( result as any ) . content || result . id } ` ) )
if ( result . score !== undefined ) {
console . log ( chalk . dim ( ` Similarity: ${ ( result . score * 100 ) . toFixed ( 1 ) } % ` ) )
}
2025-09-11 16:23:32 -07:00
if ( result . entity && result . entity . metadata ) {
console . log ( chalk . dim ( ` Metadata: ${ JSON . stringify ( result . entity . metadata ) } ` ) )
2025-08-26 12:32:21 -07:00
}
} )
}
} else {
formatOutput ( results , options )
}
} catch ( error : any ) {
spinner . fail ( 'Search failed' )
console . error ( chalk . red ( error . message ) )
process . exit ( 1 )
}
} ,
/ * *
* Get item by ID
* /
async get ( id : string , options : GetOptions ) {
const spinner = ora ( 'Fetching item...' ) . start ( )
try {
const brain = await getBrainy ( )
// Try to get the item
const results = await brain . search ( id , 1 )
if ( results . length === 0 ) {
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' } ` )
2025-09-11 16:23:32 -07:00
if ( item . entity && item . entity . metadata ) {
console . log ( ` Metadata: ${ JSON . stringify ( item . entity . metadata , null , 2 ) } ` )
2025-08-26 12:32:21 -07:00
}
if ( options . withConnections ) {
// Get verbs/relationships
// Get connections if method exists
const connections = ( brain as any ) . getConnections ? await ( brain as any ) . getConnections ( id ) : [ ]
if ( connections && connections . length > 0 ) {
console . log ( chalk . cyan ( '\nConnections:' ) )
connections . forEach ( ( conn : any ) = > {
console . log ( ` ${ conn . source } --[ ${ conn . type } ]--> ${ conn . target } ` )
} )
}
}
} else {
formatOutput ( item , options )
}
} catch ( error : any ) {
spinner . fail ( 'Failed to get item' )
console . error ( chalk . red ( error . message ) )
process . exit ( 1 )
}
} ,
/ * *
* Create relationship between items
* /
async relate ( source : string , verb : string , target : string , options : RelateOptions ) {
const spinner = ora ( 'Creating relationship...' ) . start ( )
try {
const brain = await getBrainy ( )
let metadata : any = { }
if ( options . metadata ) {
try {
metadata = JSON . parse ( options . metadata )
} catch {
spinner . fail ( 'Invalid metadata JSON' )
process . exit ( 1 )
}
}
if ( options . weight ) {
metadata . weight = parseFloat ( options . weight )
}
// Create the relationship
2025-09-11 16:23:32 -07:00
const result = await brain . relate ( {
from : source ,
to : target ,
type : verb as any ,
metadata
} )
2025-08-26 12:32:21 -07:00
spinner . succeed ( 'Relationship created' )
if ( ! options . json ) {
console . log ( chalk . green ( ` ✓ Created relationship with ID: ${ result } ` ) )
console . log ( chalk . dim ( ` ${ source } --[ ${ verb } ]--> ${ target } ` ) )
if ( metadata . weight ) {
console . log ( chalk . dim ( ` Weight: ${ metadata . weight } ` ) )
}
} else {
formatOutput ( { id : result , source , verb , target , metadata } , options )
}
} catch ( error : any ) {
spinner . fail ( 'Failed to create relationship' )
console . error ( chalk . red ( error . message ) )
process . exit ( 1 )
}
} ,
/ * *
* Import data from file
* /
async import ( file : string , options : ImportOptions ) {
const spinner = ora ( 'Importing data...' ) . start ( )
try {
const brain = await getBrainy ( )
const format = options . format || 'json'
const batchSize = options . batchSize ? parseInt ( options . batchSize ) : 100
// Read file content
const content = readFileSync ( file , 'utf-8' )
let items : any [ ] = [ ]
switch ( format ) {
case 'json' :
items = JSON . parse ( content )
if ( ! Array . isArray ( items ) ) {
items = [ items ]
}
break
case 'jsonl' :
items = content . split ( '\n' )
. filter ( line = > line . trim ( ) )
. map ( line = > JSON . parse ( line ) )
break
case 'csv' :
// Simple CSV parsing (first line is headers)
const lines = content . split ( '\n' ) . filter ( line = > line . trim ( ) )
const headers = lines [ 0 ] . split ( ',' ) . map ( h = > h . trim ( ) )
items = lines . slice ( 1 ) . map ( line = > {
const values = line . split ( ',' ) . map ( v = > v . trim ( ) )
const obj : any = { }
headers . forEach ( ( h , i ) = > {
obj [ h ] = values [ i ]
} )
return obj
} )
break
}
spinner . text = ` Importing ${ items . length } items... `
// Process in batches
let imported = 0
for ( let i = 0 ; i < items . length ; i += batchSize ) {
const batch = items . slice ( i , i + batchSize )
for ( const item of batch ) {
2025-09-01 09:37:36 -07:00
let content : string
let metadata : any = { }
2025-08-26 12:32:21 -07:00
if ( typeof item === 'string' ) {
2025-09-01 09:37:36 -07:00
content = item
2025-08-26 12:32:21 -07:00
} else if ( item . content || item . text ) {
2025-09-01 09:37:36 -07:00
content = item . content || item . text
metadata = item . metadata || item
2025-08-26 12:32:21 -07:00
} else {
2025-09-01 09:37:36 -07:00
content = JSON . stringify ( item )
metadata = { originalData : item }
2025-08-26 12:32:21 -07:00
}
2025-09-01 09:37:36 -07:00
// Use AI to detect type for each item
const suggestion = await BrainyTypes . suggestNoun (
typeof content === 'string' ? { content , . . . metadata } : content
)
// Use suggested type or default to Content if low confidence
const nounType = suggestion . confidence >= 0.5 ? suggestion.type : NounType.Content
2025-09-11 16:23:32 -07:00
await brain . add ( {
data : content ,
type : nounType as NounType ,
metadata
} )
2025-08-26 12:32:21 -07:00
imported ++
}
spinner . text = ` Imported ${ imported } / ${ items . length } items... `
}
spinner . succeed ( ` Imported ${ imported } items ` )
if ( ! options . json ) {
console . log ( chalk . green ( ` ✓ Successfully imported ${ imported } items from ${ file } ` ) )
console . log ( chalk . dim ( ` Format: ${ format } ` ) )
console . log ( chalk . dim ( ` Batch size: ${ batchSize } ` ) )
} else {
formatOutput ( { imported , file , format , batchSize } , options )
}
} catch ( error : any ) {
spinner . fail ( 'Import failed' )
console . error ( chalk . red ( error . message ) )
process . exit ( 1 )
}
} ,
/ * *
* Export database
* /
async export ( file : string | undefined , options : ExportOptions ) {
const spinner = ora ( 'Exporting database...' ) . start ( )
try {
const brain = await getBrainy ( )
const format = options . format || 'json'
// Export all data
2025-09-11 16:23:32 -07:00
const dataApi = await brain . data ( )
const data = await dataApi . export ( { format : 'json' } )
2025-08-26 12:32:21 -07:00
let output = ''
switch ( format ) {
case 'json' :
output = options . pretty
? JSON . stringify ( data , null , 2 )
: JSON . stringify ( data )
break
case 'jsonl' :
if ( Array . isArray ( data ) ) {
output = data . map ( item = > JSON . stringify ( item ) ) . join ( '\n' )
} else {
output = JSON . stringify ( data )
}
break
case 'csv' :
if ( Array . isArray ( data ) && data . length > 0 ) {
// Get all unique keys for headers
const headers = new Set < string > ( )
data . forEach ( item = > {
Object . keys ( item ) . forEach ( key = > headers . add ( key ) )
} )
const headerArray = Array . from ( headers )
// Create CSV
output = headerArray . join ( ',' ) + '\n'
output += data . map ( item = > {
return headerArray . map ( h = > {
const value = item [ h ]
if ( typeof value === 'object' ) {
return JSON . stringify ( value )
}
return value || ''
} ) . join ( ',' )
} ) . join ( '\n' )
}
break
}
if ( file ) {
writeFileSync ( file , output )
spinner . succeed ( ` Exported to ${ file } ` )
if ( ! options . json ) {
console . log ( chalk . green ( ` ✓ Successfully exported database to ${ file } ` ) )
console . log ( chalk . dim ( ` Format: ${ format } ` ) )
console . log ( chalk . dim ( ` Items: ${ Array . isArray ( data ) ? data.length : 1 } ` ) )
} else {
formatOutput ( { file , format , count : Array.isArray ( data ) ? data.length : 1 } , options )
}
} else {
spinner . succeed ( 'Export complete' )
console . log ( output )
}
} catch ( error : any ) {
spinner . fail ( 'Export failed' )
console . error ( chalk . red ( error . message ) )
process . exit ( 1 )
}
}
}