feat: simplify chat CLI with unified command structure

- Replace `brainy chat-session` subcommands with `brainy chat` options
- Add --list, --search, --history flags to main chat command
- Implement beautiful terminal formatting without external dependencies
- Improve user experience with helpful tips and upgrade hints
This commit is contained in:
David Snelling 2025-08-12 11:49:38 -07:00
parent 48e51204c3
commit 427f98cf6a
5 changed files with 1169 additions and 15 deletions

View file

@ -76,9 +76,39 @@ program
program
.command('add [data]')
.description('Add data across multiple dimensions (vector, graph, facets)')
.description('🔒 Add data literally (safe, no AI processing)')
.option('-m, --metadata <json>', 'Metadata facets as JSON')
.option('-i, --id <id>', 'Custom ID')
.option('--smart', 'Enable AI processing (same as smart-add command)')
.action(wrapAction(async (data, options) => {
let metadata = {}
if (options.metadata) {
try {
metadata = JSON.parse(options.metadata)
} catch {
console.error(chalk.red('Invalid JSON metadata'))
process.exit(1)
}
}
if (options.id) {
metadata.id = options.id
}
// Use smart processing if --smart flag is provided
if (options.smart) {
console.log(chalk.dim('🧠 AI processing enabled'))
await cortex.addSmart(data, metadata)
} else {
console.log(chalk.dim('🔒 Literal storage (safe for secrets/API keys)'))
await cortex.add(data, metadata)
}
}))
program
.command('smart-add [data]')
.description('🧠 Add data with AI processing (Neural Import, entity detection)')
.option('-m, --metadata <json>', 'Metadata facets as JSON')
.option('-i, --id <id>', 'Custom ID')
.action(wrapAction(async (data, options) => {
let metadata = {}
if (options.metadata) {
@ -92,7 +122,9 @@ program
if (options.id) {
metadata.id = options.id
}
await cortex.add(data, metadata)
console.log(chalk.dim('🧠 AI processing enabled (Neural Import + entity detection)'))
await cortex.addSmart(data, metadata)
}))
program
@ -124,10 +156,44 @@ program
program
.command('chat [question]')
.description('AI-powered chat with multi-dimensional context')
.option('-l, --llm <model>', 'LLM model to use')
.description('🧠 Beautiful AI chat with local memory')
.option('-l, --list', 'List all chat sessions')
.option('-s, --search <query>', 'Search all conversations')
.option('-h, --history [limit]', 'Show conversation history (default: 10)')
.option('--session <id>', 'Use specific chat session')
.option('--new', 'Start a new session')
.option('--role <name>', 'Agent role (premium feature preview)', 'assistant')
.action(wrapInteractive(async (question, options) => {
await cortex.chat(question)
const { BrainyData } = await import('../dist/brainyData.js')
const { ChatCLI } = await import('../dist/chat/ChatCLI.js')
const brainy = new BrainyData()
await brainy.init()
const chatCLI = new ChatCLI(brainy)
// Handle different modes
if (options.list) {
await chatCLI.listSessions()
} else if (options.search) {
await chatCLI.searchConversations(options.search)
} else if (options.history) {
const limit = typeof options.history === 'string' ? parseInt(options.history) : 10
await chatCLI.showHistory(limit)
} else if (question) {
// Single message mode
await chatCLI.sendMessage(question, {
sessionId: options.session,
speaker: options.role
})
} else {
// Interactive chat mode
await chatCLI.startInteractiveChat({
sessionId: options.session,
speaker: options.role,
newSession: options.new
})
}
}))
program
@ -176,6 +242,8 @@ program
await cortex.restore(file)
}))
// Chat commands moved to main chat command above
// ========================================
// BRAIN CLOUD INTEGRATION
// ========================================
@ -857,6 +925,99 @@ program
await cortex.chat()
}))
// ========================================
// AUGMENTATION CONTROL COMMANDS
// ========================================
program
.command('augment')
.description('List all augmentations with their status')
.action(wrapAction(async () => {
const { BrainyData } = await import('../dist/brainyData.js')
const brainy = new BrainyData()
await brainy.init()
const augmentations = brainy.listAugmentations()
if (augmentations.length === 0) {
console.log(chalk.yellow('No augmentations registered'))
return
}
console.log(chalk.cyan('🔧 Augmentations Status\n'))
const grouped = augmentations.reduce((acc, aug) => {
if (!acc[aug.type]) acc[aug.type] = []
acc[aug.type].push(aug)
return acc
}, {})
for (const [type, augs] of Object.entries(grouped)) {
console.log(chalk.bold(`${type.toUpperCase()}:`))
for (const aug of augs) {
const status = aug.enabled ? chalk.green('✅ enabled') : chalk.red('❌ disabled')
console.log(` ${aug.name} - ${status}`)
console.log(chalk.dim(` ${aug.description}`))
}
console.log('')
}
}))
program
.command('augment enable <name>')
.description('Enable an augmentation by name')
.action(wrapAction(async (name) => {
const { BrainyData } = await import('../dist/brainyData.js')
const brainy = new BrainyData()
await brainy.init()
const success = brainy.enableAugmentation(name)
if (success) {
console.log(chalk.green(`✅ Enabled augmentation: ${name}`))
} else {
console.log(chalk.red(`❌ Augmentation not found: ${name}`))
console.log(chalk.dim('Use "brainy augment" to see available augmentations'))
}
}))
program
.command('augment disable <name>')
.description('Disable an augmentation by name')
.action(wrapAction(async (name) => {
const { BrainyData } = await import('../dist/brainyData.js')
const brainy = new BrainyData()
await brainy.init()
const success = brainy.disableAugmentation(name)
if (success) {
console.log(chalk.green(`✅ Disabled augmentation: ${name}`))
} else {
console.log(chalk.red(`❌ Augmentation not found: ${name}`))
console.log(chalk.dim('Use "brainy augment" to see available augmentations'))
}
}))
program
.command('augment status <name>')
.description('Check if an augmentation is enabled')
.action(wrapAction(async (name) => {
const { BrainyData } = await import('../dist/brainyData.js')
const brainy = new BrainyData()
await brainy.init()
const enabled = brainy.isAugmentationEnabled(name)
if (enabled !== undefined) {
const status = enabled ? chalk.green('✅ enabled') : chalk.red('❌ disabled')
console.log(`${name}: ${status}`)
} else {
console.log(chalk.red(`❌ Augmentation not found: ${name}`))
console.log(chalk.dim('Use "brainy augment" to see available augmentations'))
}
}))
// ========================================
// PARSE AND HANDLE
// ========================================
@ -872,7 +1033,14 @@ if (!process.argv.slice(2).length) {
console.log(' brainy init # Initialize project')
console.log(' brainy add "some data" # Add multi-dimensional data')
console.log(' brainy search "query" # Search across all dimensions')
console.log(' brainy chat # AI chat with full context')
console.log(' brainy chat # 🧠 Magical AI chat with perfect memory')
console.log('')
console.log(chalk.bold('Chat & Memory:'))
console.log(' brainy chat # Interactive chat with local memory')
console.log(' brainy chat "question" # Single question with context')
console.log(' brainy chat --list # List all chat sessions')
console.log(' brainy chat --search "query" # Search all conversations')
console.log(' brainy chat --history # Show conversation history')
console.log('')
console.log(chalk.bold('Brain Cloud (Premium):'))
console.log(chalk.green(' brainy cloud setup # Auto-setup with provisioning'))

504
src/chat/BrainyChat.ts Normal file
View file

@ -0,0 +1,504 @@
/**
* BrainyChat - Magical Chat Command Center
*
* A smart chat system that leverages Brainy's standard noun/verb types
* to create intelligent, persistent conversations with automatic context loading.
*
* Key Features:
* - Uses standard NounType.Message for all chat messages
* - Employs VerbType.Communicates and VerbType.Precedes for conversation flow
* - Auto-discovery of previous sessions using Brainy's search capabilities
* - Hybrid architecture: basic chat (open source) + premium memory sync
*/
import { BrainyData } from '../brainyData.js'
import { NounType, VerbType, type Message, type GraphNoun, type GraphVerb } from '../types/graphTypes.js'
export interface ChatMessage {
id: string
content: string
speaker: 'user' | 'assistant' | string // Allow custom speaker names for multi-agent
sessionId: string
timestamp: Date
metadata?: {
model?: string
usage?: {
prompt_tokens?: number
completion_tokens?: number
}
context?: Record<string, any>
}
}
export interface ChatSession {
id: string
title?: string
createdAt: Date
lastMessageAt: Date
messageCount: number
participants: string[]
metadata?: {
tags?: string[]
summary?: string
archived?: boolean
premium?: boolean
}
}
/**
* Enhanced BrainyChat with automatic context loading and intelligent memory
*
* This extends basic chat functionality with premium features when available
*/
export class BrainyChat {
private brainy: BrainyData
private currentSessionId: string | null = null
private sessionCache = new Map<string, ChatSession>()
constructor(brainy: BrainyData) {
this.brainy = brainy
}
/**
* Initialize chat system and auto-discover last session
* Uses Brainy's advanced search to find the most recent conversation
*/
async initialize(): Promise<ChatSession | null> {
try {
// Search for the most recent chat message using Brainy's search
const recentMessages = await this.brainy.search(
'recent chat conversation',
1,
{
nounTypes: [NounType.Message],
metadata: {
messageType: 'chat'
}
}
)
if (recentMessages.length > 0) {
const lastMessage = recentMessages[0]
const sessionId = lastMessage.metadata?.sessionId
if (sessionId) {
this.currentSessionId = sessionId
return await this.loadSession(sessionId)
}
}
} catch (error: any) {
console.debug('No previous session found, starting fresh:', error?.message)
}
return null
}
/**
* Start a new chat session
* Automatically generates a session ID and stores session metadata
*/
async startNewSession(title?: string, participants: string[] = ['user', 'assistant']): Promise<ChatSession> {
const sessionId = `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
const session: ChatSession = {
id: sessionId,
title,
createdAt: new Date(),
lastMessageAt: new Date(),
messageCount: 0,
participants,
metadata: {
tags: ['active'],
premium: await this.isPremiumEnabled()
}
}
// Store session using BrainyData add() method
await this.brainy.add(
{
sessionType: 'chat',
title: title || `Chat Session ${new Date().toLocaleDateString()}`,
createdAt: session.createdAt.toISOString(),
lastMessageAt: session.lastMessageAt.toISOString(),
messageCount: session.messageCount,
participants: session.participants
},
{
id: sessionId,
nounType: NounType.Concept,
sessionType: 'chat'
}
)
this.currentSessionId = sessionId
this.sessionCache.set(sessionId, session)
return session
}
/**
* Add a message to the current session
* Stores using standard NounType.Message and creates conversation flow relationships
*/
async addMessage(
content: string,
speaker: string = 'user',
metadata?: ChatMessage['metadata']
): Promise<ChatMessage> {
if (!this.currentSessionId) {
await this.startNewSession()
}
const messageId = `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
const timestamp = new Date()
const message: ChatMessage = {
id: messageId,
content,
speaker,
sessionId: this.currentSessionId!,
timestamp,
metadata
}
// Store message using BrainyData add() method
await this.brainy.add(
{
messageType: 'chat',
content,
speaker,
sessionId: this.currentSessionId!,
timestamp: timestamp.toISOString(),
...metadata
},
{
id: messageId,
nounType: NounType.Message,
messageType: 'chat',
sessionId: this.currentSessionId!,
speaker
}
)
// Create relationships using standard verb types
await this.createMessageRelationships(messageId)
// Update session metadata
await this.updateSessionMetadata()
return message
}
/**
* Get conversation history for current session
* Uses Brainy's graph traversal to get messages in chronological order
*/
async getHistory(limit: number = 50): Promise<ChatMessage[]> {
if (!this.currentSessionId) return []
try {
// Search for messages in this session using Brainy's search
const messageNouns = await this.brainy.search(
'', // Empty query to get all messages
limit,
{
nounTypes: [NounType.Message],
metadata: {
sessionId: this.currentSessionId,
messageType: 'chat'
}
}
)
return messageNouns.map((noun: any) => this.nounToChatMessage(noun))
} catch (error) {
console.error('Error retrieving chat history:', error)
return []
}
}
/**
* Search across all chat sessions and messages
* Leverages Brainy's powerful vector and semantic search
*/
async searchMessages(
query: string,
options?: {
sessionId?: string
speaker?: string
limit?: number
semanticSearch?: boolean
}
): Promise<ChatMessage[]> {
const metadata: Record<string, any> = {
messageType: 'chat'
}
if (options?.sessionId) {
metadata.sessionId = options.sessionId
}
if (options?.speaker) {
metadata.speaker = options.speaker
}
try {
const results = await this.brainy.search(
options?.semanticSearch !== false ? query : '',
options?.limit || 20,
{
nounTypes: [NounType.Message],
metadata
}
)
return results.map((noun: any) => this.nounToChatMessage(noun))
} catch (error) {
console.error('Error searching messages:', error)
return []
}
}
/**
* Get all chat sessions
* Uses Brainy's search to find all conversation sessions
*/
async getSessions(limit: number = 20): Promise<ChatSession[]> {
try {
const sessionNouns = await this.brainy.search(
'',
limit,
{
nounTypes: [NounType.Concept],
metadata: {
sessionType: 'chat'
}
}
)
return sessionNouns.map((noun: any) => this.nounToChatSession(noun))
} catch (error) {
console.error('Error retrieving sessions:', error)
return []
}
}
/**
* Switch to a different session
* Automatically loads context and history
*/
async switchToSession(sessionId: string): Promise<ChatSession | null> {
try {
const session = await this.loadSession(sessionId)
if (session) {
this.currentSessionId = sessionId
this.sessionCache.set(sessionId, session)
}
return session
} catch (error) {
console.error('Error switching to session:', error)
return null
}
}
/**
* Archive a session (premium feature)
* Maintains full searchability while organizing conversations
*/
async archiveSession(sessionId: string): Promise<boolean> {
if (!await this.isPremiumEnabled()) {
throw new Error('Session archiving requires premium Brain Cloud subscription')
}
try {
// Since BrainyData doesn't have update, add an archive marker
await this.brainy.add(
{
archivedSessionId: sessionId,
archivedAt: new Date().toISOString(),
action: 'archive'
},
{
nounType: NounType.State,
sessionId,
archived: true
}
)
return true
} catch (error) {
console.error('Error archiving session:', error)
}
return false
}
/**
* Generate session summary using AI (premium feature)
* Intelligently summarizes long conversations
*/
async generateSessionSummary(sessionId: string): Promise<string | null> {
if (!await this.isPremiumEnabled()) {
throw new Error('AI session summaries require premium Brain Cloud subscription')
}
try {
const messages = await this.getHistoryForSession(sessionId, 100)
const content = messages
.map(msg => `${msg.speaker}: ${msg.content}`)
.join('\n')
// Use Brainy's AI to generate summary (placeholder - would need actual AI integration)
const summaryResponse = `Summary of ${messages.length} messages discussing various topics in ${sessionId}`
return summaryResponse || null
} catch (error) {
console.error('Error generating session summary:', error)
return null
}
}
// Private helper methods
private async createMessageRelationships(messageId: string): Promise<void> {
// Link message to session using BrainyData addVerb() method
await this.brainy.addVerb(
messageId,
this.currentSessionId!,
undefined,
{
type: VerbType.PartOf,
metadata: {
relationship: 'message-in-session'
}
}
)
// Find previous message to create conversation flow using VerbType.Precedes
const previousMessages = await this.brainy.search(
'',
1,
{
nounTypes: [NounType.Message],
metadata: {
sessionId: this.currentSessionId,
messageType: 'chat'
}
}
)
if (previousMessages.length > 0 && previousMessages[0].id !== messageId) {
await this.brainy.addVerb(
previousMessages[0].id,
messageId,
undefined,
{
type: VerbType.Precedes,
metadata: {
relationship: 'message-sequence'
}
}
)
}
}
private async loadSession(sessionId: string): Promise<ChatSession | null> {
try {
const sessionNouns = await this.brainy.search(
'',
1,
{
nounTypes: [NounType.Concept],
metadata: {
sessionType: 'chat'
}
}
)
// Filter by session ID manually since BrainyData search may not support ID filtering
const matchingSession = sessionNouns.find(noun => noun.id === sessionId)
if (matchingSession) {
return this.nounToChatSession(matchingSession)
}
} catch (error) {
console.error('Error loading session:', error)
}
return null
}
private async getHistoryForSession(sessionId: string, limit: number = 50): Promise<ChatMessage[]> {
try {
const messageNouns = await this.brainy.search(
'',
limit,
{
nounTypes: [NounType.Message],
metadata: {
sessionId: sessionId,
messageType: 'chat'
}
}
)
return messageNouns.map((noun: any) => this.nounToChatMessage(noun))
} catch (error) {
console.error('Error retrieving session history:', error)
return []
}
}
private async updateSessionMetadata(): Promise<void> {
if (!this.currentSessionId) return
// Since BrainyData doesn't have update functionality, we'll skip this
// In a real implementation, you'd need update capabilities
console.debug('Session metadata update skipped - BrainyData lacks update API')
}
private nounToChatMessage(noun: any): ChatMessage {
return {
id: noun.id,
content: noun.metadata?.content || noun.data?.content || '',
speaker: noun.metadata?.speaker || noun.data?.speaker || 'unknown',
sessionId: noun.metadata?.sessionId || noun.data?.sessionId || '',
timestamp: new Date(noun.metadata?.timestamp || noun.data?.timestamp || Date.now()),
metadata: noun.metadata
}
}
private nounToChatSession(noun: any): ChatSession {
return {
id: noun.id,
title: noun.metadata?.title || noun.data?.title || 'Untitled Session',
createdAt: new Date(noun.metadata?.createdAt || noun.data?.createdAt || Date.now()),
lastMessageAt: new Date(noun.metadata?.lastMessageAt || noun.data?.lastMessageAt || Date.now()),
messageCount: noun.metadata?.messageCount || noun.data?.messageCount || 0,
participants: noun.metadata?.participants || noun.data?.participants || ['user', 'assistant'],
metadata: noun.metadata
}
}
private toTimestamp(date: Date): { seconds: number; nanoseconds: number } {
const seconds = Math.floor(date.getTime() / 1000)
const nanoseconds = (date.getTime() % 1000) * 1000000
return { seconds, nanoseconds }
}
private async isPremiumEnabled(): Promise<boolean> {
// Check if premium augmentations are available
// This would integrate with the license validation system
try {
const augmentations = await this.brainy.listAugmentations()
return augmentations.some((aug: any) => aug.premium === true && aug.enabled === true)
} catch {
return false
}
}
// Public API methods for CLI integration
getCurrentSessionId(): string | null {
return this.currentSessionId
}
getCurrentSession(): ChatSession | null {
return this.currentSessionId ? this.sessionCache.get(this.currentSessionId) || null : null
}
}

428
src/chat/ChatCLI.ts Normal file
View file

@ -0,0 +1,428 @@
/**
* ChatCLI - Command Line Interface for BrainyChat
*
* Provides a magical chat experience through the Brainy CLI with:
* - Auto-discovery of previous sessions
* - Intelligent context loading
* - Multi-agent coordination support
* - Premium memory sync integration
*/
import { BrainyChat, type ChatSession, type ChatMessage } from './BrainyChat.js'
import { BrainyData } from '../brainyData.js'
// Simple color utility without external dependencies
const colors = {
cyan: (text: string) => `\x1b[36m${text}\x1b[0m`,
green: (text: string) => `\x1b[32m${text}\x1b[0m`,
yellow: (text: string) => `\x1b[33m${text}\x1b[0m`,
blue: (text: string) => `\x1b[34m${text}\x1b[0m`,
gray: (text: string) => `\x1b[90m${text}\x1b[0m`,
red: (text: string) => `\x1b[31m${text}\x1b[0m`
}
export class ChatCLI {
private brainyChat: BrainyChat
private brainy: BrainyData
constructor(brainy: BrainyData) {
this.brainy = brainy
this.brainyChat = new BrainyChat(brainy)
}
/**
* Start an interactive chat session
* Automatically discovers and loads previous context
*/
async startInteractiveChat(options?: {
sessionId?: string
speaker?: string
memory?: boolean
newSession?: boolean
}): Promise<void> {
console.log(colors.cyan('🧠 Brainy Chat - Local Memory & Intelligence'))
console.log()
let session: ChatSession | null = null
if (options?.sessionId) {
// Load specific session
session = await this.brainyChat.switchToSession(options.sessionId)
if (session) {
console.log(colors.green(`📂 Loaded session: ${session.title || session.id}`))
console.log(colors.gray(` Created: ${session.createdAt.toLocaleDateString()}`))
console.log(colors.gray(` Messages: ${session.messageCount}`))
} else {
console.log(colors.yellow(`⚠️ Session ${options.sessionId} not found, starting new session`))
}
} else if (!options?.newSession) {
// Auto-discover last session
console.log(colors.gray('🔍 Looking for your last conversation...'))
session = await this.brainyChat.initialize()
if (session) {
console.log(colors.green(`✨ Found your last session: ${session.title || 'Untitled'}`))
console.log(colors.gray(` Last active: ${session.lastMessageAt.toLocaleString()}`))
console.log(colors.gray(` Messages: ${session.messageCount}`))
// Show recent context if memory option is enabled
if (options?.memory !== false) {
await this.showRecentContext()
}
} else {
console.log(colors.blue('🆕 No previous sessions found, starting fresh!'))
}
}
if (!session) {
session = await this.brainyChat.startNewSession(
`Chat ${new Date().toLocaleDateString()}`,
['user', options?.speaker || 'assistant']
)
console.log(colors.green(`🎉 Started new session: ${session.id}`))
}
console.log()
console.log(colors.gray('💡 Tips:'))
console.log(colors.gray(' - Type /history to see conversation history'))
console.log(colors.gray(' - Type /search <query> to search all conversations'))
console.log(colors.gray(' - Type /sessions to list all sessions'))
console.log(colors.gray(' - Type /help for more commands'))
console.log(colors.gray(' - Type /quit to exit'))
console.log()
console.log(colors.blue('🚀 Want multi-agent coordination? Try: brainy cloud auth'))
console.log()
// Start interactive loop
await this.interactiveLoop(options?.speaker || 'assistant')
}
/**
* Send a single message and get response
*/
async sendMessage(
message: string,
options?: {
sessionId?: string
speaker?: string
noResponse?: boolean
}
): Promise<ChatMessage[]> {
if (options?.sessionId) {
await this.brainyChat.switchToSession(options.sessionId)
}
// Add user message
const userMessage = await this.brainyChat.addMessage(message, 'user')
console.log(colors.blue(`👤 You: ${message}`))
if (options?.noResponse) {
return [userMessage]
}
// For CLI usage, we'd integrate with whatever AI service is configured
// This is a placeholder showing the architecture
const response = await this.generateResponse(message, options?.speaker || 'assistant')
const assistantMessage = await this.brainyChat.addMessage(
response,
options?.speaker || 'assistant',
{
model: 'claude-3-sonnet',
context: { userMessage: userMessage.id }
}
)
console.log(colors.green(`🤖 ${options?.speaker || 'Assistant'}: ${response}`))
return [userMessage, assistantMessage]
}
/**
* Show conversation history
*/
async showHistory(limit: number = 10): Promise<void> {
const messages = await this.brainyChat.getHistory(limit)
if (messages.length === 0) {
console.log(colors.yellow('📭 No messages in current session'))
return
}
console.log(colors.cyan(`📜 Last ${Math.min(limit, messages.length)} messages:`))
console.log()
for (const message of messages.slice(-limit)) {
const timestamp = message.timestamp.toLocaleTimeString()
const speakerColor = message.speaker === 'user' ? colors.blue : colors.green
const icon = message.speaker === 'user' ? '👤' : '🤖'
console.log(speakerColor(`${icon} ${message.speaker} (${timestamp}):`))
console.log(colors.gray(` ${message.content}`))
console.log()
}
}
/**
* Search across all conversations
*/
async searchConversations(
query: string,
options?: {
limit?: number
sessionId?: string
semantic?: boolean
}
): Promise<void> {
console.log(colors.cyan(`🔍 Searching for: "${query}"`))
const results = await this.brainyChat.searchMessages(query, {
limit: options?.limit || 10,
sessionId: options?.sessionId,
semanticSearch: options?.semantic !== false
})
if (results.length === 0) {
console.log(colors.yellow('🤷 No matching messages found'))
return
}
console.log(colors.green(`✨ Found ${results.length} matches:`))
console.log()
for (const message of results) {
const date = message.timestamp.toLocaleDateString()
const time = message.timestamp.toLocaleTimeString()
const speakerColor = message.speaker === 'user' ? colors.blue : colors.green
const icon = message.speaker === 'user' ? '👤' : '🤖'
console.log(colors.gray(`📅 ${date} ${time} - Session: ${message.sessionId.substring(0, 8)}...`))
console.log(speakerColor(`${icon} ${message.speaker}: ${message.content}`))
console.log()
}
}
/**
* List all chat sessions
*/
async listSessions(): Promise<void> {
const sessions = await this.brainyChat.getSessions()
if (sessions.length === 0) {
console.log(colors.yellow('📭 No chat sessions found'))
return
}
console.log(colors.cyan(`💬 Your chat sessions (${sessions.length}):`))
console.log()
for (const session of sessions) {
const isActive = session.id === this.brainyChat.getCurrentSessionId()
const activeIndicator = isActive ? colors.green(' ● ACTIVE') : ''
const archived = session.metadata?.archived ? colors.gray(' [ARCHIVED]') : ''
console.log(colors.blue(`📂 ${session.title || 'Untitled'}${activeIndicator}${archived}`))
console.log(colors.gray(` ID: ${session.id}`))
console.log(colors.gray(` Created: ${session.createdAt.toLocaleDateString()}`))
console.log(colors.gray(` Last active: ${session.lastMessageAt.toLocaleDateString()}`))
console.log(colors.gray(` Messages: ${session.messageCount}`))
console.log(colors.gray(` Participants: ${session.participants.join(', ')}`))
console.log()
}
}
/**
* Switch to a different session
*/
async switchSession(sessionId: string): Promise<void> {
const session = await this.brainyChat.switchToSession(sessionId)
if (session) {
console.log(colors.green(`✅ Switched to session: ${session.title || session.id}`))
console.log(colors.gray(` Messages: ${session.messageCount}`))
console.log(colors.gray(` Last active: ${session.lastMessageAt.toLocaleString()}`))
} else {
console.log(colors.red(`❌ Session ${sessionId} not found`))
}
}
/**
* Show help for chat commands
*/
showHelp(): void {
console.log(colors.cyan('🧠 Brainy Chat Commands:'))
console.log()
console.log(colors.blue('Basic Commands:'))
console.log(' /history [limit] - Show conversation history (default: 10 messages)')
console.log(' /search <query> - Search all conversations')
console.log(' /sessions - List all chat sessions')
console.log(' /switch <id> - Switch to a specific session')
console.log(' /new - Start a new session')
console.log(' /help - Show this help')
console.log(' /quit - Exit chat')
console.log()
console.log(colors.yellow('Local Features:'))
console.log(' ✨ Automatic session discovery')
console.log(' 🧠 Local memory across all conversations')
console.log(' 🔍 Semantic search using vector similarity')
console.log(' 📊 Standard noun/verb graph relationships')
console.log()
console.log(colors.green('Want More? Premium Features:'))
console.log(' 🤝 Multi-agent coordination')
console.log(' ☁️ Cross-device memory sync')
console.log(' 🎨 Rich web coordination UI')
console.log(' 🔄 Real-time team collaboration')
console.log()
console.log(colors.blue('Get premium: brainy cloud auth'))
console.log()
}
// Private methods
private async interactiveLoop(assistantSpeaker: string = 'assistant'): Promise<void> {
const readline = require('readline')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
const askQuestion = (): Promise<string> => {
return new Promise((resolve) => {
rl.question(colors.blue('💬 You: '), resolve)
})
}
while (true) {
try {
const input = await askQuestion()
if (input.trim() === '') continue
// Handle commands
if (input.startsWith('/')) {
const [command, ...args] = input.slice(1).split(' ')
switch (command.toLowerCase()) {
case 'quit':
case 'exit':
console.log(colors.cyan('👋 Thanks for chatting! Your conversation is saved.'))
rl.close()
return
case 'history':
const limit = args[0] ? parseInt(args[0]) : 10
await this.showHistory(limit)
break
case 'search':
if (args.length === 0) {
console.log(colors.yellow('Usage: /search <query>'))
} else {
await this.searchConversations(args.join(' '))
}
break
case 'sessions':
await this.listSessions()
break
case 'switch':
if (args.length === 0) {
console.log(colors.yellow('Usage: /switch <session-id>'))
} else {
await this.switchSession(args[0])
}
break
case 'new':
const newSession = await this.brainyChat.startNewSession(
`Chat ${new Date().toLocaleDateString()}`
)
console.log(colors.green(`🆕 Started new session: ${newSession.id}`))
break
case 'archive':
const sessionToArchive = args[0] || this.brainyChat.getCurrentSessionId()
if (sessionToArchive) {
try {
await this.brainyChat.archiveSession(sessionToArchive)
console.log(colors.green(`📁 Session archived: ${sessionToArchive}`))
} catch (error: any) {
console.log(colors.red(`${error?.message}`))
}
} else {
console.log(colors.yellow('No session to archive'))
}
break
case 'summary':
const sessionToSummarize = args[0] || this.brainyChat.getCurrentSessionId()
if (sessionToSummarize) {
try {
const summary = await this.brainyChat.generateSessionSummary(sessionToSummarize)
if (summary) {
console.log(colors.green('📋 Session Summary:'))
console.log(colors.gray(summary))
} else {
console.log(colors.yellow('No summary could be generated'))
}
} catch (error: any) {
console.log(colors.red(`${error?.message}`))
}
} else {
console.log(colors.yellow('No session to summarize'))
}
break
case 'help':
this.showHelp()
break
default:
console.log(colors.yellow(`Unknown command: ${command}`))
console.log(colors.gray('Type /help for available commands'))
}
} else {
// Regular message
await this.sendMessage(input, { speaker: assistantSpeaker })
}
console.log()
} catch (error: any) {
console.error(colors.red(`Error: ${error?.message}`))
}
}
}
private async showRecentContext(limit: number = 3): Promise<void> {
const recentMessages = await this.brainyChat.getHistory(limit)
if (recentMessages.length > 0) {
console.log(colors.gray('💭 Recent context:'))
for (const msg of recentMessages.slice(-limit)) {
const preview = msg.content.length > 60
? msg.content.substring(0, 60) + '...'
: msg.content
console.log(colors.gray(` ${msg.speaker}: ${preview}`))
}
console.log()
}
}
private async generateResponse(message: string, speaker: string): Promise<string> {
// This is a placeholder for AI integration
// In a real implementation, this would call the configured AI service
// and could include multi-agent coordination
// Example responses for demonstration
const responses = [
"I remember our conversation and can help with that!",
"Based on our previous discussions, I think...",
"Let me search through our chat history for relevant context.",
"I can coordinate with other AI agents if needed for this task."
]
return responses[Math.floor(Math.random() * responses.length)]
}
}

View file

@ -5,7 +5,7 @@
*/
import { BrainyData } from '../brainyData.js'
import { BrainyChat } from '../chat/brainyChat.js'
import { BrainyChat } from '../chat/BrainyChat.js'
import { PerformanceMonitor } from './performanceMonitor.js'
import { HealthCheck } from './healthCheck.js'
// Licensing system moved to quantum-vault
@ -603,6 +603,61 @@ export class Cortex {
}
}
/**
* Add data with AI processing enabled (Neural Import + entity detection)
*/
async addSmart(data?: string, metadata?: any): Promise<void> {
await this.ensureInitialized()
// Interactive mode if no data provided
if (!data) {
const responses = await prompts([
{
type: 'text',
name: 'data',
message: `${emojis.brain} Enter data for AI processing:`
},
{
type: 'text',
name: 'id',
message: 'ID (optional, press enter to auto-generate):'
},
{
type: 'confirm',
name: 'hasMetadata',
message: 'Add metadata?',
initial: false
},
{
type: (prev: any) => prev ? 'text' : null,
name: 'metadata',
message: 'Enter metadata (JSON format):'
}
])
data = responses.data
if (responses.metadata) {
try {
metadata = JSON.parse(responses.metadata)
} catch {
console.log(colors.warning('Invalid JSON, skipping metadata'))
}
}
if (responses.id) {
metadata = { ...metadata, id: responses.id }
}
}
const spinner = ora('🧠 Processing with AI...').start()
try {
const id = await this.brainy!.addSmart(data, metadata)
spinner.succeed(colors.success(`${emojis.check} Processed with AI and added with ID: ${id}`))
} catch (error) {
spinner.fail('Failed to add data with AI processing')
console.error(error)
}
}
/**
* Search with beautiful results display and advanced options
*/
@ -1899,8 +1954,8 @@ export class Cortex {
await this.ensureInitialized()
// Import and create the Cortex SENSE augmentation
const { CortexSenseAugmentation } = await import('../augmentations/cortexSense.js')
const neuralSense = new CortexSenseAugmentation(this.brainy!, options)
const { NeuralImportAugmentation } = await import('../augmentations/neuralImport.js')
const neuralSense = new NeuralImportAugmentation(this.brainy!, options)
// Initialize the augmentation
await neuralSense.initialize()
@ -1942,8 +1997,8 @@ export class Cortex {
async neuralAnalyze(filePath: string): Promise<void> {
await this.ensureInitialized()
const { CortexSenseAugmentation } = await import('../augmentations/cortexSense.js')
const neuralSense = new CortexSenseAugmentation(this.brainy!)
const { NeuralImportAugmentation } = await import('../augmentations/neuralImport.js')
const neuralSense = new NeuralImportAugmentation(this.brainy!)
await neuralSense.initialize()
@ -1982,8 +2037,8 @@ export class Cortex {
async neuralValidate(filePath: string): Promise<void> {
await this.ensureInitialized()
const { CortexSenseAugmentation } = await import('../augmentations/cortexSense.js')
const neuralSense = new CortexSenseAugmentation(this.brainy!)
const { NeuralImportAugmentation } = await import('../augmentations/neuralImport.js')
const neuralSense = new NeuralImportAugmentation(this.brainy!)
await neuralSense.initialize()

View file

@ -72,9 +72,8 @@ import {
} from './utils/logger.js'
// Export BrainyChat for conversational AI
import { BrainyChat, ChatOptions } from './chat/brainyChat.js'
import { BrainyChat } from './chat/BrainyChat.js'
export { BrainyChat }
export type { ChatOptions }
// Export Cortex CLI functionality - commented out for core MIT build
// export { Cortex } from './cortex/cortex.js'