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:
parent
48e51204c3
commit
427f98cf6a
5 changed files with 1169 additions and 15 deletions
180
bin/brainy.js
180
bin/brainy.js
|
|
@ -76,9 +76,39 @@ program
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('add [data]')
|
.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('-m, --metadata <json>', 'Metadata facets as JSON')
|
||||||
.option('-i, --id <id>', 'Custom ID')
|
.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) => {
|
.action(wrapAction(async (data, options) => {
|
||||||
let metadata = {}
|
let metadata = {}
|
||||||
if (options.metadata) {
|
if (options.metadata) {
|
||||||
|
|
@ -92,7 +122,9 @@ program
|
||||||
if (options.id) {
|
if (options.id) {
|
||||||
metadata.id = 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
|
program
|
||||||
|
|
@ -124,10 +156,44 @@ program
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('chat [question]')
|
.command('chat [question]')
|
||||||
.description('AI-powered chat with multi-dimensional context')
|
.description('🧠 Beautiful AI chat with local memory')
|
||||||
.option('-l, --llm <model>', 'LLM model to use')
|
.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) => {
|
.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
|
program
|
||||||
|
|
@ -176,6 +242,8 @@ program
|
||||||
await cortex.restore(file)
|
await cortex.restore(file)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// Chat commands moved to main chat command above
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// BRAIN CLOUD INTEGRATION
|
// BRAIN CLOUD INTEGRATION
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
@ -857,6 +925,99 @@ program
|
||||||
await cortex.chat()
|
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
|
// PARSE AND HANDLE
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
@ -872,7 +1033,14 @@ if (!process.argv.slice(2).length) {
|
||||||
console.log(' brainy init # Initialize project')
|
console.log(' brainy init # Initialize project')
|
||||||
console.log(' brainy add "some data" # Add multi-dimensional data')
|
console.log(' brainy add "some data" # Add multi-dimensional data')
|
||||||
console.log(' brainy search "query" # Search across all dimensions')
|
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('')
|
||||||
console.log(chalk.bold('Brain Cloud (Premium):'))
|
console.log(chalk.bold('Brain Cloud (Premium):'))
|
||||||
console.log(chalk.green(' brainy cloud setup # Auto-setup with provisioning'))
|
console.log(chalk.green(' brainy cloud setup # Auto-setup with provisioning'))
|
||||||
|
|
|
||||||
504
src/chat/BrainyChat.ts
Normal file
504
src/chat/BrainyChat.ts
Normal 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
428
src/chat/ChatCLI.ts
Normal 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)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrainyData } from '../brainyData.js'
|
import { BrainyData } from '../brainyData.js'
|
||||||
import { BrainyChat } from '../chat/brainyChat.js'
|
import { BrainyChat } from '../chat/BrainyChat.js'
|
||||||
import { PerformanceMonitor } from './performanceMonitor.js'
|
import { PerformanceMonitor } from './performanceMonitor.js'
|
||||||
import { HealthCheck } from './healthCheck.js'
|
import { HealthCheck } from './healthCheck.js'
|
||||||
// Licensing system moved to quantum-vault
|
// 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
|
* Search with beautiful results display and advanced options
|
||||||
*/
|
*/
|
||||||
|
|
@ -1899,8 +1954,8 @@ export class Cortex {
|
||||||
await this.ensureInitialized()
|
await this.ensureInitialized()
|
||||||
|
|
||||||
// Import and create the Cortex SENSE augmentation
|
// Import and create the Cortex SENSE augmentation
|
||||||
const { CortexSenseAugmentation } = await import('../augmentations/cortexSense.js')
|
const { NeuralImportAugmentation } = await import('../augmentations/neuralImport.js')
|
||||||
const neuralSense = new CortexSenseAugmentation(this.brainy!, options)
|
const neuralSense = new NeuralImportAugmentation(this.brainy!, options)
|
||||||
|
|
||||||
// Initialize the augmentation
|
// Initialize the augmentation
|
||||||
await neuralSense.initialize()
|
await neuralSense.initialize()
|
||||||
|
|
@ -1942,8 +1997,8 @@ export class Cortex {
|
||||||
async neuralAnalyze(filePath: string): Promise<void> {
|
async neuralAnalyze(filePath: string): Promise<void> {
|
||||||
await this.ensureInitialized()
|
await this.ensureInitialized()
|
||||||
|
|
||||||
const { CortexSenseAugmentation } = await import('../augmentations/cortexSense.js')
|
const { NeuralImportAugmentation } = await import('../augmentations/neuralImport.js')
|
||||||
const neuralSense = new CortexSenseAugmentation(this.brainy!)
|
const neuralSense = new NeuralImportAugmentation(this.brainy!)
|
||||||
|
|
||||||
await neuralSense.initialize()
|
await neuralSense.initialize()
|
||||||
|
|
||||||
|
|
@ -1982,8 +2037,8 @@ export class Cortex {
|
||||||
async neuralValidate(filePath: string): Promise<void> {
|
async neuralValidate(filePath: string): Promise<void> {
|
||||||
await this.ensureInitialized()
|
await this.ensureInitialized()
|
||||||
|
|
||||||
const { CortexSenseAugmentation } = await import('../augmentations/cortexSense.js')
|
const { NeuralImportAugmentation } = await import('../augmentations/neuralImport.js')
|
||||||
const neuralSense = new CortexSenseAugmentation(this.brainy!)
|
const neuralSense = new NeuralImportAugmentation(this.brainy!)
|
||||||
|
|
||||||
await neuralSense.initialize()
|
await neuralSense.initialize()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,8 @@ import {
|
||||||
} from './utils/logger.js'
|
} from './utils/logger.js'
|
||||||
|
|
||||||
// Export BrainyChat for conversational AI
|
// Export BrainyChat for conversational AI
|
||||||
import { BrainyChat, ChatOptions } from './chat/brainyChat.js'
|
import { BrainyChat } from './chat/BrainyChat.js'
|
||||||
export { BrainyChat }
|
export { BrainyChat }
|
||||||
export type { ChatOptions }
|
|
||||||
|
|
||||||
// Export Cortex CLI functionality - commented out for core MIT build
|
// Export Cortex CLI functionality - commented out for core MIT build
|
||||||
// export { Cortex } from './cortex/cortex.js'
|
// export { Cortex } from './cortex/cortex.js'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue