@soulcraft/brainy 3.19.0 → 3.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/brainy.js CHANGED
@@ -1,2340 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Brainy CLI - Cleaned Up & Beautiful
5
- * šŸ§ āš›ļø ONE way to do everything
6
- *
7
- * After the Great Cleanup of 2025:
8
- * - 5 commands total (was 40+)
9
- * - Clear, obvious naming
10
- * - Interactive mode for beginners
4
+ * Brainy CLI Wrapper
5
+ *
6
+ * Imports the compiled TypeScript CLI from dist/cli/index.js
7
+ * This ensures TypeScript features work correctly
11
8
  */
12
9
 
13
- // @ts-ignore
14
- import { program } from 'commander'
15
- import { BrainyData } from '../dist/brainyData.js'
16
- // @ts-ignore
17
- import chalk from 'chalk'
18
- import { readFileSync } from 'fs'
19
- import { dirname, join } from 'path'
20
- import { fileURLToPath } from 'url'
21
- import { createInterface } from 'readline'
22
- // @ts-ignore
23
- import Table from 'cli-table3'
24
-
25
- const __dirname = dirname(fileURLToPath(import.meta.url))
26
- const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'))
27
-
28
- // Create single BrainyData instance (the ONE data orchestrator)
29
- let brainy = null
30
- const getBrainy = async () => {
31
- if (!brainy) {
32
- brainy = new BrainyData()
33
- await brainy.init()
34
- }
35
- return brainy
36
- }
37
-
38
- // Beautiful colors matching brainy.png logo
39
- const colors = {
40
- primary: chalk.hex('#3A5F4A'), // Teal container (from logo)
41
- success: chalk.hex('#2D4A3A'), // Deep teal frame (from logo)
42
- info: chalk.hex('#4A6B5A'), // Medium teal
43
- warning: chalk.hex('#D67441'), // Orange (from logo)
44
- error: chalk.hex('#B85C35'), // Deep orange
45
- brain: chalk.hex('#D67441'), // Brain orange (from logo)
46
- cream: chalk.hex('#F5E6A3'), // Cream background (from logo)
47
- dim: chalk.dim,
48
- blue: chalk.blue,
49
- green: chalk.green,
50
- yellow: chalk.yellow,
51
- cyan: chalk.cyan
52
- }
53
-
54
- // Helper functions
55
- const exitProcess = (code = 0) => {
56
- setTimeout(() => process.exit(code), 100)
57
- }
58
-
59
- // Initialize Brainy instance
60
- const initBrainy = async () => {
61
- return new BrainyData()
62
- }
63
-
64
- /**
65
- * Enhanced result formatting using display augmentation
66
- * @param {any} result - The result object from search/get/find
67
- * @param {number} index - Result index for numbering
68
- * @returns {Promise<string>} Formatted result string
69
- */
70
- const formatResultWithDisplay = async (result, index) => {
71
- try {
72
- // Check if result has display capabilities (enhanced by display augmentation)
73
- if (result.getDisplay && typeof result.getDisplay === 'function') {
74
- const displayFields = await result.getDisplay()
75
-
76
- // Format with enhanced display fields (clean, no icons)
77
- let output = colors.primary(`\n${index + 1}. ${displayFields.title}`)
78
-
79
- if (displayFields.type) {
80
- output += colors.dim(` (${displayFields.type})`)
81
- }
82
-
83
- if (result.score) {
84
- output += colors.info(`\n šŸŽÆ Relevance: ${(result.score * 100).toFixed(1)}%`)
85
- }
86
-
87
- if (result.fusionScore) {
88
- output += colors.info(`\n 🧠 AI Score: ${(result.fusionScore * 100).toFixed(1)}%`)
89
- }
90
-
91
- if (displayFields.description && displayFields.description !== displayFields.title) {
92
- output += colors.info(`\n šŸ“„ ${displayFields.description}`)
93
- }
94
-
95
- if (displayFields.tags && displayFields.tags.length > 0) {
96
- output += colors.cyan(`\n šŸ·ļø ${displayFields.tags.join(', ')}`)
97
- }
98
-
99
- // Show relationship info for verbs
100
- if (displayFields.relationship) {
101
- output += colors.yellow(`\n šŸ”— ${displayFields.relationship}`)
102
- }
103
-
104
- // Show metadata only if there's additional useful info
105
- if (result.metadata && Object.keys(result.metadata).length > 0) {
106
- const filteredMetadata = Object.fromEntries(
107
- Object.entries(result.metadata).filter(([key]) =>
108
- !key.startsWith('_') && !['type', 'title', 'description', 'icon'].includes(key)
109
- )
110
- )
111
- if (Object.keys(filteredMetadata).length > 0) {
112
- output += colors.dim(`\n šŸ“ ${JSON.stringify(filteredMetadata)}`)
113
- }
114
- }
115
-
116
- return output
117
- }
118
- } catch (error) {
119
- // Fallback silently to basic formatting if display augmentation fails
120
- }
121
-
122
- // Fallback: Basic formatting without display augmentation
123
- let output = colors.primary(`\n${index + 1}. ${result.content || result.id}`)
124
-
125
- if (result.score) {
126
- output += colors.info(`\n Relevance: ${(result.score * 100).toFixed(1)}%`)
127
- }
128
-
129
- if (result.fusionScore) {
130
- output += colors.info(`\n AI Score: ${(result.fusionScore * 100).toFixed(1)}%`)
131
- }
132
-
133
- if (result.type) {
134
- output += colors.info(`\n Type: ${result.type}`)
135
- }
136
-
137
- if (result.metadata && Object.keys(result.metadata).length > 0) {
138
- output += colors.dim(`\n Metadata: ${JSON.stringify(result.metadata)}`)
139
- }
140
-
141
- return output
142
- }
143
-
144
- /**
145
- * Enhanced single item formatting for get command
146
- * @param {any} item - The item object
147
- * @param {string} format - Output format (json, table, plain)
148
- * @returns {Promise<string>} Formatted item string
149
- */
150
- const formatItemWithDisplay = async (item, format = 'plain') => {
151
- if (format === 'json') {
152
- return JSON.stringify(item, null, 2)
153
- }
154
-
155
- try {
156
- // Check if item has display capabilities
157
- if (item.getDisplay && typeof item.getDisplay === 'function') {
158
- const displayFields = await item.getDisplay()
159
-
160
- if (format === 'table') {
161
- const table = new Table({
162
- head: [colors.brain('Property'), colors.brain('Value')],
163
- style: { head: [], border: [] }
164
- })
165
-
166
- table.push(['ID', colors.primary(item.id)])
167
- table.push(['Title', colors.primary(displayFields.title)])
168
- table.push(['Type', colors.info(displayFields.type)])
169
- table.push(['Description', colors.info(displayFields.description)])
170
-
171
- if (displayFields.tags && displayFields.tags.length > 0) {
172
- table.push(['Tags', colors.cyan(displayFields.tags.join(', '))])
173
- }
174
-
175
- if (displayFields.relationship) {
176
- table.push(['Relationship', colors.yellow(displayFields.relationship)])
177
- }
178
-
179
- if (item.content && item.content !== displayFields.title) {
180
- table.push(['Content', colors.dim(item.content)])
181
- }
182
-
183
- // Add non-internal metadata
184
- if (item.metadata) {
185
- Object.entries(item.metadata).forEach(([key, value]) => {
186
- if (!key.startsWith('_') && !['type', 'title', 'description', 'icon'].includes(key)) {
187
- table.push([key, colors.dim(JSON.stringify(value))])
188
- }
189
- })
190
- }
191
-
192
- return table.toString()
193
- } else {
194
- // Plain format with display enhancement
195
- let output = colors.primary(`ID: ${item.id}`)
196
- output += colors.primary(`\nTitle: ${displayFields.title}`)
197
- output += colors.info(`\nType: ${displayFields.type}`)
198
- output += colors.info(`\nDescription: ${displayFields.description}`)
199
-
200
- if (displayFields.tags && displayFields.tags.length > 0) {
201
- output += colors.cyan(`\nTags: ${displayFields.tags.join(', ')}`)
202
- }
203
-
204
- if (displayFields.relationship) {
205
- output += colors.yellow(`\nRelationship: ${displayFields.relationship}`)
206
- }
207
-
208
- if (item.content && item.content !== displayFields.title) {
209
- output += colors.dim(`\nOriginal Content: ${item.content}`)
210
- }
211
-
212
- // Show additional metadata
213
- if (item.metadata) {
214
- const additionalMetadata = Object.fromEntries(
215
- Object.entries(item.metadata).filter(([key]) =>
216
- !key.startsWith('_') && !['type', 'title', 'description', 'icon'].includes(key)
217
- )
218
- )
219
- if (Object.keys(additionalMetadata).length > 0) {
220
- output += colors.dim(`\nAdditional Metadata: ${JSON.stringify(additionalMetadata, null, 2)}`)
221
- }
222
- }
223
-
224
- return output
225
- }
226
- }
227
- } catch (error) {
228
- // Fallback silently to basic formatting
229
- }
230
-
231
- // Fallback: Basic formatting
232
- if (format === 'table') {
233
- const table = new Table({
234
- head: [colors.brain('Property'), colors.brain('Value')],
235
- style: { head: [], border: [] }
236
- })
237
-
238
- table.push(['ID', colors.primary(item.id)])
239
- table.push(['Content', colors.info(item.content || 'N/A')])
240
- if (item.metadata) {
241
- Object.entries(item.metadata).forEach(([key, value]) => {
242
- table.push([key, colors.dim(JSON.stringify(value))])
243
- })
244
- }
245
- return table.toString()
246
- } else {
247
- let output = colors.primary(`ID: ${item.id}`)
248
- if (item.content) {
249
- output += colors.info(`\nContent: ${item.content}`)
250
- }
251
- if (item.metadata && Object.keys(item.metadata).length > 0) {
252
- output += colors.info(`\nMetadata: ${JSON.stringify(item.metadata, null, 2)}`)
253
- }
254
- return output
255
- }
256
- }
257
-
258
- const wrapAction = (fn) => {
259
- return async (...args) => {
260
- try {
261
- await fn(...args)
262
- exitProcess(0)
263
- } catch (error) {
264
- console.error(colors.error('Error:'), error.message)
265
- exitProcess(1)
266
- }
267
- }
268
- }
269
-
270
- // AI Response Generation with multiple model support
271
- async function generateAIResponse(message, brainy, options) {
272
- const model = options.model || 'local'
273
-
274
- // Get relevant context from user's data
275
- const contextResults = await brainy.search(message, {
276
- limit: 5,
277
- includeContent: true,
278
- scoreThreshold: 0.3
279
- })
280
-
281
- const context = contextResults.map(r => r.content).join('\n')
282
- const prompt = `Based on the following context from the user's data, answer their question:
283
-
284
- Context:
285
- ${context}
286
-
287
- Question: ${message}
288
-
289
- Answer:`
290
-
291
- switch (model) {
292
- case 'local':
293
- case 'ollama':
294
- return await callOllamaModel(prompt, options)
295
-
296
- case 'openai':
297
- case 'gpt-3.5-turbo':
298
- case 'gpt-4':
299
- return await callOpenAI(prompt, options)
300
-
301
- case 'claude':
302
- case 'claude-3':
303
- return await callClaude(prompt, options)
304
-
305
- default:
306
- return await callOllamaModel(prompt, options)
307
- }
308
- }
309
-
310
- // Ollama (local) integration
311
- async function callOllamaModel(prompt, options) {
312
- const baseUrl = options.baseUrl || 'http://localhost:11434'
313
- const model = options.model === 'local' ? 'llama2' : options.model
314
-
315
- try {
316
- const response = await fetch(`${baseUrl}/api/generate`, {
317
- method: 'POST',
318
- headers: { 'Content-Type': 'application/json' },
319
- body: JSON.stringify({
320
- model: model,
321
- prompt: prompt,
322
- stream: false
323
- })
324
- })
325
-
326
- if (!response.ok) {
327
- throw new Error(`Ollama error: ${response.statusText}. Make sure Ollama is running: ollama serve`)
328
- }
329
-
330
- const data = await response.json()
331
- return data.response || 'No response from local model'
332
-
333
- } catch (error) {
334
- throw new Error(`Local model error: ${error.message}. Try: ollama run llama2`)
335
- }
336
- }
337
-
338
- // OpenAI integration
339
- async function callOpenAI(prompt, options) {
340
- if (!options.apiKey) {
341
- throw new Error('OpenAI API key required. Use --api-key <key> or set OPENAI_API_KEY environment variable')
342
- }
343
-
344
- const model = options.model === 'openai' ? 'gpt-3.5-turbo' : options.model
345
-
346
- try {
347
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
348
- method: 'POST',
349
- headers: {
350
- 'Authorization': `Bearer ${options.apiKey}`,
351
- 'Content-Type': 'application/json'
352
- },
353
- body: JSON.stringify({
354
- model: model,
355
- messages: [{ role: 'user', content: prompt }],
356
- max_tokens: 500
357
- })
358
- })
359
-
360
- if (!response.ok) {
361
- throw new Error(`OpenAI error: ${response.statusText}`)
362
- }
363
-
364
- const data = await response.json()
365
- return data.choices[0]?.message?.content || 'No response from OpenAI'
366
-
367
- } catch (error) {
368
- throw new Error(`OpenAI error: ${error.message}`)
369
- }
370
- }
371
-
372
- // Claude integration
373
- async function callClaude(prompt, options) {
374
- if (!options.apiKey) {
375
- throw new Error('Anthropic API key required. Use --api-key <key> or set ANTHROPIC_API_KEY environment variable')
376
- }
377
-
378
- try {
379
- const response = await fetch('https://api.anthropic.com/v1/messages', {
380
- method: 'POST',
381
- headers: {
382
- 'x-api-key': options.apiKey,
383
- 'Content-Type': 'application/json',
384
- 'anthropic-version': '2023-06-01'
385
- },
386
- body: JSON.stringify({
387
- model: 'claude-3-haiku-20240307',
388
- max_tokens: 500,
389
- messages: [{ role: 'user', content: prompt }]
390
- })
391
- })
392
-
393
- if (!response.ok) {
394
- throw new Error(`Claude error: ${response.statusText}`)
395
- }
396
-
397
- const data = await response.json()
398
- return data.content[0]?.text || 'No response from Claude'
399
-
400
- } catch (error) {
401
- throw new Error(`Claude error: ${error.message}`)
402
- }
403
- }
404
-
405
- // ========================================
406
- // MAIN PROGRAM - CLEAN & SIMPLE
407
- // ========================================
408
-
409
- program
410
- .name('brainy')
411
- .description('šŸ§ āš›ļø Brainy - Your AI-Powered Second Brain')
412
- .version(packageJson.version)
413
- .option('-i, --interactive', 'Start interactive mode')
414
- .addHelpText('after', `
415
- ${colors.dim('Examples:')}
416
- ${colors.success('brainy add "Meeting notes from today"')}
417
- ${colors.success('brainy search "project deadline"')}
418
- ${colors.success('brainy chat')} ${colors.dim('# Interactive AI chat')}
419
- ${colors.success('brainy -i')} ${colors.dim('# Interactive mode')}
420
-
421
- ${colors.dim('For more help:')}
422
- ${colors.info('brainy <command> --help')} ${colors.dim('# Command-specific help')}
423
- ${colors.info('https://github.com/TimeSoul/brainy')} ${colors.dim('# Documentation')}`)
424
-
425
- // ========================================
426
- // THE 5 COMMANDS (ONE WAY TO DO EVERYTHING)
427
- // ========================================
428
-
429
- // Command 0: INIT - Initialize brainy (essential setup)
430
- program
431
- .command('init')
432
- .description('Initialize Brainy in current directory')
433
- .option('-s, --storage <type>', 'Storage type (filesystem, memory, s3, r2, gcs)')
434
- .option('-e, --encryption', 'Enable encryption for sensitive data')
435
- .option('--s3-bucket <bucket>', 'S3 bucket name')
436
- .option('--s3-region <region>', 'S3 region')
437
- .option('--access-key <key>', 'Storage access key')
438
- .option('--secret-key <key>', 'Storage secret key')
439
- .action(wrapAction(async (options) => {
440
- console.log(colors.primary('🧠 Initializing Brainy'))
441
- console.log()
442
-
443
- const { BrainyData } = await import('../dist/brainyData.js')
444
-
445
- const config = {
446
- storage: options.storage || 'filesystem',
447
- encryption: options.encryption || false
448
- }
449
-
450
- // Storage-specific configuration
451
- if (options.storage === 's3' || options.storage === 'r2' || options.storage === 'gcs') {
452
- if (!options.accessKey || !options.secretKey) {
453
- console.log(colors.warning('āš ļø Cloud storage requires access credentials'))
454
- console.log(colors.info('Use: --access-key <key> --secret-key <secret>'))
455
- console.log(colors.info('Or set environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY'))
456
- process.exit(1)
457
- }
458
-
459
- config.storageOptions = {
460
- bucket: options.s3Bucket,
461
- region: options.s3Region || 'us-east-1',
462
- accessKeyId: options.accessKey,
463
- secretAccessKey: options.secretKey
464
- }
465
- }
466
-
467
- try {
468
- const brainy = new BrainyData(config)
469
- await brainy.init()
470
-
471
- console.log(colors.success('āœ… Brainy initialized successfully!'))
472
- console.log(colors.info(`šŸ“ Storage: ${config.storage}`))
473
- console.log(colors.info(`šŸ”’ Encryption: ${config.encryption ? 'Enabled' : 'Disabled'}`))
474
-
475
- if (config.encryption) {
476
- console.log(colors.warning('šŸ” Encryption enabled - keep your keys secure!'))
477
- }
478
-
479
- console.log()
480
- console.log(colors.success('šŸš€ Ready to go! Try:'))
481
- console.log(colors.info(' brainy add "Hello, World!"'))
482
- console.log(colors.info(' brainy search "hello"'))
483
-
484
- } catch (error) {
485
- console.log(colors.error('āŒ Initialization failed:'))
486
- console.log(colors.error(error.message))
487
- process.exit(1)
488
- }
489
- }))
490
-
491
- // Command 1: ADD - Add data (smart by default)
492
- program
493
- .command('add [data]')
494
- .description('Add data to your brain (smart auto-detection)')
495
- .option('-m, --metadata <json>', 'Metadata as JSON')
496
- .option('-i, --id <id>', 'Custom ID')
497
- .option('--literal', 'Skip AI processing (literal storage)')
498
- .option('--encrypt', 'Encrypt this data (for sensitive information)')
499
- .action(wrapAction(async (data, options) => {
500
- if (!data) {
501
- console.log(colors.info('🧠 Interactive add mode'))
502
- const rl = createInterface({
503
- input: process.stdin,
504
- output: process.stdout
505
- })
506
-
507
- data = await new Promise(resolve => {
508
- rl.question(colors.primary('What would you like to add? '), (answer) => {
509
- rl.close()
510
- resolve(answer)
511
- })
512
- })
513
- }
514
-
515
- let metadata = {}
516
- if (options.metadata) {
517
- try {
518
- metadata = JSON.parse(options.metadata)
519
- } catch {
520
- console.error(colors.error('Invalid JSON metadata'))
521
- process.exit(1)
522
- }
523
- }
524
- if (options.id) {
525
- metadata.id = options.id
526
- }
527
- if (options.encrypt) {
528
- metadata.encrypted = true
529
- }
530
-
531
- console.log(options.literal
532
- ? colors.info('šŸ”’ Literal storage')
533
- : colors.success('🧠 Smart mode (auto-detects types)')
534
- )
535
-
536
- if (options.encrypt) {
537
- console.log(colors.warning('šŸ” Encrypting sensitive data...'))
538
- }
539
-
540
- const brainyInstance = await getBrainy()
541
-
542
- // Handle encryption at data level if requested
543
- let processedData = data
544
- if (options.encrypt) {
545
- processedData = await brainyInstance.encryptData(data)
546
- metadata.encrypted = true
547
- }
548
-
549
- const id = await brainyInstance.add({ data: processedData, type: 'content', metadata })
550
- console.log(colors.success(`āœ… Added successfully! ID: ${id}`))
551
- }))
552
-
553
- // Command 2: CHAT - Talk to your data with AI
554
- program
555
- .command('chat [message]')
556
- .description('AI chat with your brain data (supports local & cloud models)')
557
- .option('-s, --session <id>', 'Use specific chat session')
558
- .option('-n, --new', 'Start a new session')
559
- .option('-l, --list', 'List all chat sessions')
560
- .option('-h, --history [limit]', 'Show conversation history (default: 10)')
561
- .option('--search <query>', 'Search all conversations')
562
- .option('-m, --model <model>', 'LLM model (local/openai/claude/ollama)', 'local')
563
- .option('--api-key <key>', 'API key for cloud models')
564
- .option('--base-url <url>', 'Base URL for local models (default: http://localhost:11434)')
565
- .action(wrapAction(async (message, options) => {
566
- const { BrainyData } = await import('../dist/brainyData.js')
567
- const { BrainyChat } = await import('../dist/chat/BrainyChat.js')
568
-
569
- console.log(colors.primary('šŸ§ šŸ’¬ Brainy Chat - AI-Powered Conversation with Your Data'))
570
- console.log(colors.info('Talk to your brain using your data as context'))
571
- console.log()
572
-
573
- // Initialize brainy and chat
574
- const brainy = new BrainyData()
575
- await brainy.init()
576
- const chat = new BrainyChat(brainy)
577
-
578
- // Handle different options
579
- if (options.list) {
580
- console.log(colors.primary('šŸ“‹ Chat Sessions'))
581
- const sessions = await chat.getSessions(20)
582
- if (sessions.length === 0) {
583
- console.log(colors.warning('No chat sessions found. Start chatting to create your first session!'))
584
- } else {
585
- sessions.forEach((session, i) => {
586
- console.log(colors.success(`${i + 1}. ${session.id}`))
587
- if (session.title) console.log(colors.info(` Title: ${session.title}`))
588
- console.log(colors.info(` Messages: ${session.messageCount}`))
589
- console.log(colors.info(` Last active: ${session.lastMessageAt.toLocaleDateString()}`))
590
- })
591
- }
592
- return
593
- }
594
-
595
- if (options.search) {
596
- console.log(colors.primary(`šŸ” Searching conversations for: "${options.search}"`))
597
- const results = await chat.searchMessages(options.search, { limit: 10 })
598
- if (results.length === 0) {
599
- console.log(colors.warning('No messages found'))
600
- } else {
601
- results.forEach((msg, i) => {
602
- console.log(colors.success(`\n${i + 1}. [${msg.sessionId}] ${colors.info(msg.speaker)}:`))
603
- console.log(` ${msg.content.substring(0, 200)}${msg.content.length > 200 ? '...' : ''}`)
604
- })
605
- }
606
- return
607
- }
608
-
609
- if (options.history) {
610
- const limit = parseInt(options.history) || 10
611
- console.log(colors.primary(`šŸ“œ Recent Chat History (${limit} messages)`))
612
- const history = await chat.getHistory(limit)
613
- if (history.length === 0) {
614
- console.log(colors.warning('No chat history found'))
615
- } else {
616
- history.forEach(msg => {
617
- const speaker = msg.speaker === 'user' ? colors.success('You') : colors.info('AI')
618
- console.log(`${speaker}: ${msg.content}`)
619
- console.log(colors.info(` ${msg.timestamp.toLocaleString()}`))
620
- console.log()
621
- })
622
- }
623
- return
624
- }
625
-
626
- // Start interactive chat or process single message
627
- if (!message) {
628
- console.log(colors.success('šŸŽÆ Interactive mode - type messages or "exit" to quit'))
629
- console.log(colors.info(`Model: ${options.model}`))
630
- console.log()
631
-
632
- // Auto-discover previous session
633
- const session = options.new ? null : await chat.initialize()
634
- if (session) {
635
- console.log(colors.success(`šŸ“‹ Resumed session: ${session.id}`))
636
- console.log()
637
- } else {
638
- const newSession = await chat.startNewSession()
639
- console.log(colors.success(`šŸ†• Started new session: ${newSession.id}`))
640
- console.log()
641
- }
642
-
643
- // Interactive chat loop
644
- const rl = createInterface({
645
- input: process.stdin,
646
- output: process.stdout,
647
- prompt: colors.primary('You: ')
648
- })
649
-
650
- rl.prompt()
651
-
652
- rl.on('line', async (input) => {
653
- if (input.trim().toLowerCase() === 'exit') {
654
- console.log(colors.success('šŸ‘‹ Chat session saved to your brain!'))
655
- rl.close()
656
- return
657
- }
658
-
659
- if (input.trim()) {
660
- // Store user message
661
- await chat.addMessage(input.trim(), 'user')
662
-
663
- // Generate AI response
664
- try {
665
- const response = await generateAIResponse(input.trim(), brainy, options)
666
- console.log(colors.info('AI: ') + response)
667
-
668
- // Store AI response
669
- await chat.addMessage(response, 'assistant', { model: options.model })
670
- console.log()
671
- } catch (error) {
672
- console.log(colors.error('AI Error: ') + error.message)
673
- console.log(colors.warning('šŸ’” Tip: Try setting --model local or providing --api-key'))
674
- console.log()
675
- }
676
- }
677
-
678
- rl.prompt()
679
- })
680
-
681
- rl.on('close', () => {
682
- exitProcess(0)
683
- })
684
-
685
- } else {
686
- // Single message mode
687
- console.log(colors.success('You: ') + message)
688
-
689
- try {
690
- const response = await generateAIResponse(message, brainy, options)
691
- console.log(colors.info('AI: ') + response)
692
-
693
- // Store conversation
694
- await chat.addMessage(message, 'user')
695
- await chat.addMessage(response, 'assistant', { model: options.model })
696
-
697
- } catch (error) {
698
- console.log(colors.error('Error: ') + error.message)
699
- console.log(colors.info('šŸ’” Try: brainy chat --model local or provide --api-key'))
700
- }
701
- }
702
- }))
703
-
704
- // Command 3: IMPORT - Bulk/external data
705
- program
706
- .command('import [source]')
707
- .description('Import bulk data from files, URLs, or streams')
708
- .option('-t, --type <type>', 'Source type (file, url, stream)')
709
- .option('-c, --chunk-size <size>', 'Chunk size for large imports', '1000')
710
- .action(wrapAction(async (source, options) => {
711
-
712
- // Interactive mode if no source provided
713
- if (!source) {
714
- console.log(colors.primary('šŸ“„ Interactive Import Mode'))
715
- console.log(colors.dim('Import data from various sources\n'))
716
-
717
- const rl = createInterface({
718
- input: process.stdin,
719
- output: process.stdout
720
- })
721
-
722
- // Ask for source type first
723
- console.log(colors.cyan('Source types:'))
724
- console.log(colors.info(' 1. Local file'))
725
- console.log(colors.info(' 2. URL'))
726
- console.log(colors.info(' 3. Direct input'))
727
- console.log()
728
-
729
- const sourceType = await new Promise(resolve => {
730
- rl.question(colors.cyan('Select source type (1-3): '), (answer) => {
731
- resolve(answer)
732
- })
733
- })
734
-
735
- if (sourceType === '3') {
736
- // Direct input mode
737
- console.log(colors.info('\nEnter your data (type END on a new line when done):\n'))
738
- let data = ''
739
- let line = ''
740
-
741
- while ((line = await new Promise(resolve => {
742
- rl.question('', resolve)
743
- })) !== 'END') {
744
- data += line + '\n'
745
- }
746
-
747
- rl.close()
748
-
749
- // Save to temp file
750
- const fs = require('fs')
751
- source = `/tmp/brainy-import-${Date.now()}.json`
752
- fs.writeFileSync(source, data.trim())
753
- console.log(colors.info(`\nSaved to temporary file: ${source}`))
754
- } else {
755
- // File or URL
756
- source = await new Promise(resolve => {
757
- const prompt = sourceType === '2' ? 'Enter URL: ' : 'Enter file path: '
758
- rl.question(colors.cyan(prompt), (answer) => {
759
- rl.close()
760
- resolve(answer)
761
- })
762
- })
763
-
764
- if (!source.trim()) {
765
- console.log(colors.warning('No source provided'))
766
- process.exit(1)
767
- }
768
- }
769
- }
770
- console.log(colors.info('šŸ“„ Starting neural import...'))
771
- console.log(colors.info(`Source: ${source}`))
772
-
773
- // Read and prepare data for import
774
- const fs = require('fs')
775
- let data
776
-
777
- try {
778
- if (source.startsWith('http')) {
779
- // Handle URL import
780
- const response = await fetch(source)
781
- data = await response.text()
782
- } else {
783
- // Handle file import
784
- data = fs.readFileSync(source, 'utf8')
785
- }
786
-
787
- // Parse data if JSON
788
- try {
789
- data = JSON.parse(data)
790
- } catch {
791
- // Keep as string if not JSON
792
- }
793
- } catch (error) {
794
- console.log(colors.error(`Failed to read source: ${error.message}`))
795
- process.exit(1)
796
- }
797
-
798
- const brainyInstance = await getBrainy()
799
- const result = await brainyInstance.import(data, {
800
- batchSize: parseInt(options.chunkSize) || 50
801
- })
802
-
803
- console.log(colors.success(`āœ… Imported ${result.length} items`))
804
- }))
805
-
806
- // Command 3: FIND - Intelligent search using Triple Intelligence
807
- program
808
- .command('find [query]')
809
- .description('Intelligent search using natural language and structured queries')
810
- .option('-l, --limit <number>', 'Results limit', '10')
811
- .option('-m, --mode <mode>', 'Search mode (auto, semantic, structured)', 'auto')
812
- .option('--like <term>', 'Vector similarity search term')
813
- .option('--where <json>', 'Metadata filters as JSON')
814
- .action(wrapAction(async (query, options) => {
815
-
816
- if (!query && !options.like) {
817
- console.log(colors.primary('🧠 Intelligent Find Mode'))
818
- console.log(colors.dim('Use natural language or structured queries\n'))
819
-
820
- const rl = createInterface({
821
- input: process.stdin,
822
- output: process.stdout
823
- })
824
-
825
- query = await new Promise(resolve => {
826
- rl.question(colors.cyan('What would you like to find? '), (answer) => {
827
- rl.close()
828
- resolve(answer)
829
- })
830
- })
831
-
832
- if (!query.trim()) {
833
- console.log(colors.warning('No query provided'))
834
- process.exit(1)
835
- }
836
- }
837
-
838
- console.log(colors.info(`🧠 Finding: "${query || options.like}"`))
839
-
840
- const brainyInstance = await getBrainy()
841
-
842
- // Build query object for find() API
843
- let findQuery = query
844
-
845
- // Handle structured queries
846
- if (options.like || options.where) {
847
- findQuery = {}
848
- if (options.like) findQuery.like = options.like
849
- if (options.where) {
850
- try {
851
- findQuery.where = JSON.parse(options.where)
852
- } catch {
853
- console.error(colors.error('Invalid JSON in --where option'))
854
- process.exit(1)
855
- }
856
- }
857
- }
858
-
859
- const findOptions = {
860
- limit: parseInt(options.limit),
861
- mode: options.mode
862
- }
863
-
864
- const results = await brainyInstance.find(findQuery, findOptions)
865
-
866
- if (results.length === 0) {
867
- console.log(colors.warning('No results found'))
868
- return
869
- }
870
-
871
- console.log(colors.success(`āœ… Found ${results.length} intelligent results:`))
872
-
873
- // Use enhanced formatting with display augmentation
874
- for (let i = 0; i < results.length; i++) {
875
- const formattedResult = await formatResultWithDisplay(results[i], i)
876
- console.log(formattedResult)
877
- }
878
- }))
879
-
880
- // Command 4: SEARCH - Triple-power search
881
- program
882
- .command('search [query]')
883
- .description('Search your brain (vector + graph + facets)')
884
- .option('-l, --limit <number>', 'Results limit', '10')
885
- .option('-f, --filter <json>', 'Metadata filters (see "brainy fields" for available fields)')
886
- .option('-d, --depth <number>', 'Relationship depth', '2')
887
- .option('--fields', 'Show available filter fields and exit')
888
- .action(wrapAction(async (query, options) => {
889
-
890
- // Interactive mode if no query provided
891
- if (!query) {
892
- console.log(colors.primary('šŸ” Interactive Search Mode'))
893
- console.log(colors.dim('Search your neural database with natural language\n'))
894
-
895
- const rl = createInterface({
896
- input: process.stdin,
897
- output: process.stdout
898
- })
899
-
900
- query = await new Promise(resolve => {
901
- rl.question(colors.cyan('What would you like to search for? '), (answer) => {
902
- rl.close()
903
- resolve(answer)
904
- })
905
- })
906
-
907
- if (!query.trim()) {
908
- console.log(colors.warning('No search query provided'))
909
- process.exit(1)
910
- }
911
- }
912
-
913
- // Handle --fields option
914
- if (options.fields) {
915
- console.log(colors.primary('šŸ” Available Filter Fields'))
916
- console.log(colors.primary('=' .repeat(30)))
917
-
918
- try {
919
- const { BrainyData } = await import('../dist/brainyData.js')
920
- const brainy = new BrainyData()
921
- await brainy.init()
922
-
923
- const filterFields = await brainy.getFilterFields()
924
- if (filterFields.length > 0) {
925
- console.log(colors.success('Available fields for --filter option:'))
926
- filterFields.forEach(field => {
927
- console.log(colors.info(` ${field}`))
928
- })
929
- console.log()
930
- console.log(colors.primary('Usage Examples:'))
931
- console.log(colors.info(` brainy search "query" --filter '{"type":"person"}'`))
932
- console.log(colors.info(` brainy search "query" --filter '{"category":"work","status":"active"}'`))
933
- } else {
934
- console.log(colors.warning('No indexed fields available yet.'))
935
- console.log(colors.info('Add some data with metadata to see available fields.'))
936
- }
937
-
938
- } catch (error) {
939
- console.log(colors.error(`Error: ${error.message}`))
940
- }
941
- return
942
- }
943
- console.log(colors.info(`šŸ” Searching: "${query}"`))
944
-
945
- const searchOptions = {
946
- limit: parseInt(options.limit),
947
- depth: parseInt(options.depth)
948
- }
949
-
950
- if (options.filter) {
951
- try {
952
- searchOptions.filter = JSON.parse(options.filter)
953
- } catch {
954
- console.error(colors.error('Invalid filter JSON'))
955
- process.exit(1)
956
- }
957
- }
958
-
959
- const brainyInstance = await getBrainy()
960
- const results = await brainyInstance.search(query, searchOptions)
961
-
962
- if (results.length === 0) {
963
- console.log(colors.warning('No results found'))
964
- return
965
- }
966
-
967
- console.log(colors.success(`āœ… Found ${results.length} results:`))
968
-
969
- // Use enhanced formatting with display augmentation
970
- for (let i = 0; i < results.length; i++) {
971
- const formattedResult = await formatResultWithDisplay(results[i], i)
972
- console.log(formattedResult)
973
- }
974
- }))
975
-
976
- // Command 4: GET - Retrieve specific data by ID
977
- program
978
- .command('get [id]')
979
- .description('Get a specific item by ID')
980
- .option('-f, --format <format>', 'Output format (json, table, plain)', 'plain')
981
- .option('--display-debug', 'Show debug information about display augmentation')
982
- .action(wrapAction(async (id, options) => {
983
- if (!id) {
984
- console.log(colors.primary('šŸ” Interactive Get Mode'))
985
- console.log(colors.dim('Retrieve a specific item by ID\n'))
986
-
987
- const rl = createInterface({
988
- input: process.stdin,
989
- output: process.stdout
990
- })
991
-
992
- id = await new Promise(resolve => {
993
- rl.question(colors.cyan('Enter item ID: '), (answer) => {
994
- rl.close()
995
- resolve(answer)
996
- })
997
- })
998
-
999
- if (!id.trim()) {
1000
- console.log(colors.warning('No ID provided'))
1001
- process.exit(1)
1002
- }
1003
- }
1004
-
1005
- console.log(colors.info(`šŸ” Getting item: "${id}"`))
1006
-
1007
- const brainyInstance = await getBrainy()
1008
- const item = await brainyInstance.getNoun(id)
1009
-
1010
- if (!item) {
1011
- console.log(colors.warning('Item not found'))
1012
- return
1013
- }
1014
-
1015
- // Show display debug information if requested
1016
- if (options.displayDebug) {
1017
- console.log(colors.primary('šŸ” Display Augmentation Debug Information'))
1018
- console.log('=' .repeat(50))
1019
-
1020
- try {
1021
- if (item.getDisplay && typeof item.getDisplay === 'function') {
1022
- console.log(colors.success('āœ… Display augmentation active'))
1023
-
1024
- const displayFields = await item.getDisplay()
1025
- console.log(colors.info('\nšŸŽØ Computed Display Fields:'))
1026
- Object.entries(displayFields).forEach(([key, value]) => {
1027
- console.log(colors.cyan(` ${key}: ${JSON.stringify(value)}`))
1028
- })
1029
-
1030
- // Show available fields
1031
- if (item.getAvailableFields && typeof item.getAvailableFields === 'function') {
1032
- const availableFields = item.getAvailableFields('display')
1033
- console.log(colors.info('\nšŸ“‹ Available Display Fields:'))
1034
- availableFields.forEach(field => {
1035
- console.log(colors.dim(` - ${field}`))
1036
- })
1037
- }
1038
-
1039
- // Show augmentation info
1040
- if (item.getAvailableAugmentations && typeof item.getAvailableAugmentations === 'function') {
1041
- const augs = item.getAvailableAugmentations()
1042
- console.log(colors.info('\nšŸ”Œ Available Augmentations:'))
1043
- augs.forEach(aug => {
1044
- console.log(colors.dim(` - ${aug}`))
1045
- })
1046
- }
1047
- } else {
1048
- console.log(colors.warning('āš ļø Display augmentation not active or not enhanced'))
1049
- console.log(colors.dim(' Item does not have getDisplay() method'))
1050
- }
1051
- } catch (error) {
1052
- console.log(colors.error(`āŒ Display debug error: ${error.message}`))
1053
- }
1054
-
1055
- console.log('\n' + '=' .repeat(50))
1056
- }
1057
-
1058
- // Use enhanced formatting with display augmentation
1059
- const formattedItem = await formatItemWithDisplay(item, options.format)
1060
- console.log(formattedItem)
1061
- }))
1062
-
1063
- // Command 5: UPDATE - Update existing data
1064
- program
1065
- .command('update [id]')
1066
- .description('Update existing data with new content or metadata')
1067
- .option('-d, --data <data>', 'New data content')
1068
- .option('-m, --metadata <json>', 'New metadata as JSON')
1069
- .option('--no-merge', 'Replace metadata instead of merging')
1070
- .option('--no-reindex', 'Skip reindexing (faster but less accurate search)')
1071
- .option('--cascade', 'Update related verbs')
1072
- .action(wrapAction(async (id, options) => {
1073
-
1074
- // Interactive mode if no ID provided
1075
- if (!id) {
1076
- console.log(colors.primary('šŸ”„ Interactive Update Mode'))
1077
- console.log(colors.dim('Select an item to update\n'))
1078
-
1079
- // Show recent items
1080
- const brainyInstance = await getBrainy()
1081
- const recent = await brainyInstance.search('*', { limit: 10, sortBy: 'timestamp' })
1082
-
1083
- if (recent.length > 0) {
1084
- console.log(colors.cyan('Recent items:'))
1085
-
1086
- // Enhanced display for recent items
1087
- for (let i = 0; i < Math.min(recent.length, 10); i++) {
1088
- const item = recent[i]
1089
- try {
1090
- if (item.getDisplay && typeof item.getDisplay === 'function') {
1091
- const displayFields = await item.getDisplay()
1092
- console.log(colors.info(` ${i + 1}. ${item.id} - ${displayFields.title}`))
1093
- } else {
1094
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 50)}...`))
1095
- }
1096
- } catch {
1097
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 50)}...`))
1098
- }
1099
- }
1100
- console.log()
1101
- }
1102
-
1103
- const rl = createInterface({
1104
- input: process.stdin,
1105
- output: process.stdout
1106
- })
1107
-
1108
- id = await new Promise(resolve => {
1109
- rl.question(colors.cyan('Enter ID to update: '), (answer) => {
1110
- rl.close()
1111
- resolve(answer)
1112
- })
1113
- })
1114
-
1115
- if (!id.trim()) {
1116
- console.log(colors.warning('No ID provided'))
1117
- process.exit(1)
1118
- }
1119
- }
1120
- console.log(colors.info(`šŸ”„ Updating: "${id}"`))
1121
-
1122
- if (!options.data && !options.metadata) {
1123
- console.error(colors.error('Error: Must provide --data or --metadata'))
1124
- process.exit(1)
1125
- }
1126
-
1127
- let metadata = undefined
1128
- if (options.metadata) {
1129
- try {
1130
- metadata = JSON.parse(options.metadata)
1131
- } catch {
1132
- console.error(colors.error('Invalid JSON metadata'))
1133
- process.exit(1)
1134
- }
1135
- }
1136
-
1137
- const brainyInstance = await getBrainy()
1138
-
1139
- const success = await brainyInstance.updateNoun(id, options.data, metadata, {
1140
- merge: options.merge !== false, // Default true unless --no-merge
1141
- reindex: options.reindex !== false, // Default true unless --no-reindex
1142
- cascade: options.cascade || false
1143
- })
1144
-
1145
- if (success) {
1146
- console.log(colors.success('āœ… Updated successfully!'))
1147
- if (options.cascade) {
1148
- console.log(colors.info('šŸ“Ž Related verbs updated'))
1149
- }
1150
- } else {
1151
- console.log(colors.error('āŒ Update failed'))
1152
- }
1153
- }))
1154
-
1155
- // Command 5: DELETE - Remove data (soft delete by default)
1156
- program
1157
- .command('delete [id]')
1158
- .description('Delete data (soft delete by default, preserves indexes)')
1159
- .option('--hard', 'Permanent deletion (removes from indexes)')
1160
- .option('--cascade', 'Delete related verbs')
1161
- .option('--force', 'Force delete even if has relationships')
1162
- .action(wrapAction(async (id, options) => {
1163
-
1164
- // Interactive mode if no ID provided
1165
- if (!id) {
1166
- console.log(colors.warning('šŸ—‘ļø Interactive Delete Mode'))
1167
- console.log(colors.dim('Select an item to delete\n'))
1168
-
1169
- // Show recent items for selection
1170
- const brainyInstance = await getBrainy()
1171
- const recent = await brainyInstance.search('*', { limit: 10, sortBy: 'timestamp' })
1172
-
1173
- if (recent.length > 0) {
1174
- console.log(colors.cyan('Recent items:'))
1175
-
1176
- // Enhanced display for recent items
1177
- for (let i = 0; i < Math.min(recent.length, 10); i++) {
1178
- const item = recent[i]
1179
- try {
1180
- if (item.getDisplay && typeof item.getDisplay === 'function') {
1181
- const displayFields = await item.getDisplay()
1182
- console.log(colors.info(` ${i + 1}. ${item.id} - ${displayFields.title}`))
1183
- } else {
1184
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 50)}...`))
1185
- }
1186
- } catch {
1187
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 50)}...`))
1188
- }
1189
- }
1190
- console.log()
1191
- }
1192
-
1193
- const rl = createInterface({
1194
- input: process.stdin,
1195
- output: process.stdout
1196
- })
1197
-
1198
- id = await new Promise(resolve => {
1199
- rl.question(colors.warning('Enter ID to delete (or "cancel"): '), (answer) => {
1200
- rl.close()
1201
- resolve(answer)
1202
- })
1203
- })
1204
-
1205
- if (!id.trim() || id.toLowerCase() === 'cancel') {
1206
- console.log(colors.info('Delete cancelled'))
1207
- process.exit(0)
1208
- }
1209
-
1210
- // Confirm deletion in interactive mode
1211
- const confirmRl = createInterface({
1212
- input: process.stdin,
1213
- output: process.stdout
1214
- })
1215
-
1216
- const confirm = await new Promise(resolve => {
1217
- const deleteType = options.hard ? 'permanently delete' : 'soft delete'
1218
- confirmRl.question(colors.warning(`Are you sure you want to ${deleteType} "${id}"? (yes/no): `), (answer) => {
1219
- confirmRl.close()
1220
- resolve(answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y')
1221
- })
1222
- })
1223
-
1224
- if (!confirm) {
1225
- console.log(colors.info('Delete cancelled'))
1226
- process.exit(0)
1227
- }
1228
- }
1229
- console.log(colors.info(`šŸ—‘ļø Deleting: "${id}"`))
1230
-
1231
- if (options.hard) {
1232
- console.log(colors.warning('āš ļø Hard delete - data will be permanently removed'))
1233
- } else {
1234
- console.log(colors.info('šŸ”’ Soft delete - data marked as deleted but preserved'))
1235
- }
1236
-
1237
- const brainyInstance = await getBrainy()
1238
-
1239
- try {
1240
- const success = await brainyInstance.deleteNoun(id, {
1241
- soft: !options.hard, // Soft delete unless --hard specified
1242
- cascade: options.cascade || false,
1243
- force: options.force || false
1244
- })
1245
-
1246
- if (success) {
1247
- console.log(colors.success('āœ… Deleted successfully!'))
1248
- if (options.cascade) {
1249
- console.log(colors.info('šŸ“Ž Related verbs also deleted'))
1250
- }
1251
- } else {
1252
- console.log(colors.error('āŒ Delete failed'))
1253
- }
1254
- } catch (error) {
1255
- console.error(colors.error(`āŒ Delete failed: ${error.message}`))
1256
- if (error.message.includes('has relationships')) {
1257
- console.log(colors.info('šŸ’” Try: --cascade to delete relationships or --force to ignore them'))
1258
- }
1259
- }
1260
- }))
1261
-
1262
- // Command 6A: ADD-NOUN - Create typed entities (Method #4)
1263
- program
1264
- .command('add-noun [name]')
1265
- .description('Add a typed entity to your knowledge graph')
1266
- .option('-t, --type <type>', 'Noun type (Person, Organization, Project, Event, Concept, Location, Product)', 'Concept')
1267
- .option('-m, --metadata <json>', 'Metadata as JSON')
1268
- .option('--encrypt', 'Encrypt this entity')
1269
- .action(wrapAction(async (name, options) => {
1270
-
1271
- // Interactive mode if no name provided
1272
- if (!name) {
1273
- console.log(colors.primary('šŸ‘¤ Interactive Entity Creation'))
1274
- console.log(colors.dim('Create a typed entity in your knowledge graph\n'))
1275
-
1276
- const rl = createInterface({
1277
- input: process.stdin,
1278
- output: process.stdout
1279
- })
1280
-
1281
- name = await new Promise(resolve => {
1282
- rl.question(colors.cyan('Enter entity name: '), (answer) => {
1283
- resolve(answer)
1284
- })
1285
- })
1286
-
1287
- if (!name.trim()) {
1288
- rl.close()
1289
- console.log(colors.warning('No name provided'))
1290
- process.exit(1)
1291
- }
1292
-
1293
- // Interactive type selection if not provided
1294
- if (!options.type || options.type === 'Concept') {
1295
- console.log(colors.cyan('\nSelect entity type:'))
1296
- const types = ['Person', 'Organization', 'Project', 'Event', 'Concept', 'Location', 'Product']
1297
- types.forEach((t, i) => {
1298
- console.log(colors.info(` ${i + 1}. ${t}`))
1299
- })
1300
- console.log()
1301
-
1302
- const typeIndex = await new Promise(resolve => {
1303
- rl.question(colors.cyan('Select type (1-7): '), (answer) => {
1304
- resolve(parseInt(answer) - 1)
1305
- })
1306
- })
1307
-
1308
- if (typeIndex >= 0 && typeIndex < types.length) {
1309
- options.type = types[typeIndex]
1310
- }
1311
- }
1312
-
1313
- rl.close()
1314
- }
1315
- const brainy = await getBrainy()
1316
-
1317
- // Validate noun type
1318
- const validTypes = ['Person', 'Organization', 'Project', 'Event', 'Concept', 'Location', 'Product']
1319
- if (!validTypes.includes(options.type)) {
1320
- console.log(colors.error(`āŒ Invalid noun type: ${options.type}`))
1321
- console.log(colors.info(`Valid types: ${validTypes.join(', ')}`))
1322
- process.exit(1)
1323
- }
1324
-
1325
- let metadata = {}
1326
- if (options.metadata) {
1327
- try {
1328
- metadata = JSON.parse(options.metadata)
1329
- } catch {
1330
- console.error(colors.error('āŒ Invalid JSON metadata'))
1331
- process.exit(1)
1332
- }
1333
- }
1334
-
1335
- if (options.encrypt) {
1336
- metadata.encrypted = true
1337
- }
1338
-
1339
- try {
1340
- // Use modern 3.0 API with parameter object
1341
- const id = await brainy.add({ data: name, type: options.type, metadata })
1342
-
1343
- console.log(colors.success('āœ… Noun added successfully!'))
1344
- console.log(colors.info(`šŸ†” ID: ${id}`))
1345
- console.log(colors.info(`šŸ‘¤ Name: ${name}`))
1346
- console.log(colors.info(`šŸ·ļø Type: ${options.type}`))
1347
- if (Object.keys(metadata).length > 0) {
1348
- console.log(colors.info(`šŸ“ Metadata: ${JSON.stringify(metadata, null, 2)}`))
1349
- }
1350
- } catch (error) {
1351
- console.log(colors.error('āŒ Failed to add noun:'))
1352
- console.log(colors.error(error.message))
1353
- process.exit(1)
1354
- }
1355
- }))
1356
-
1357
- // Command 6B: ADD-VERB - Create relationships (Method #5)
1358
- program
1359
- .command('add-verb [source] [target]')
1360
- .description('Create a relationship between two entities')
1361
- .option('-t, --type <type>', 'Verb type (WorksFor, Knows, CreatedBy, BelongsTo, Uses, etc.)', 'RelatedTo')
1362
- .option('-m, --metadata <json>', 'Relationship metadata as JSON')
1363
- .option('--encrypt', 'Encrypt this relationship')
1364
- .action(wrapAction(async (source, target, options) => {
1365
-
1366
- // Interactive mode if parameters missing
1367
- if (!source || !target) {
1368
- console.log(colors.primary('šŸ”— Interactive Relationship Builder'))
1369
- console.log(colors.dim('Connect two entities with a semantic relationship\n'))
1370
-
1371
- const brainyInstance = await getBrainy()
1372
- const rl = createInterface({
1373
- input: process.stdin,
1374
- output: process.stdout
1375
- })
1376
-
1377
- // Get source if not provided
1378
- if (!source) {
1379
- // Show recent items
1380
- const recent = await brainyInstance.search('*', { limit: 10, sortBy: 'timestamp' })
1381
- if (recent.length > 0) {
1382
- console.log(colors.cyan('Recent items (source):'))
1383
-
1384
- // Enhanced display for recent items
1385
- for (let i = 0; i < Math.min(recent.length, 10); i++) {
1386
- const item = recent[i]
1387
- try {
1388
- if (item.getDisplay && typeof item.getDisplay === 'function') {
1389
- const displayFields = await item.getDisplay()
1390
- console.log(colors.info(` ${i + 1}. ${displayFields.icon} ${item.id} - ${displayFields.title}`))
1391
- } else {
1392
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 40)}...`))
1393
- }
1394
- } catch {
1395
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 40)}...`))
1396
- }
1397
- }
1398
- console.log()
1399
- }
1400
-
1401
- source = await new Promise(resolve => {
1402
- rl.question(colors.cyan('Enter source entity ID: '), (answer) => {
1403
- resolve(answer)
1404
- })
1405
- })
1406
-
1407
- if (!source.trim()) {
1408
- rl.close()
1409
- console.log(colors.warning('No source provided'))
1410
- process.exit(1)
1411
- }
1412
- }
1413
-
1414
- // Interactive verb type selection
1415
- if (!options.type || options.type === 'RelatedTo') {
1416
- console.log(colors.cyan('\nSelect relationship type:'))
1417
- const verbs = ['WorksFor', 'Knows', 'CreatedBy', 'BelongsTo', 'Uses', 'Manages', 'LocatedIn', 'RelatedTo', 'Custom...']
1418
- verbs.forEach((v, i) => {
1419
- console.log(colors.info(` ${i + 1}. ${v}`))
1420
- })
1421
- console.log()
1422
-
1423
- const verbIndex = await new Promise(resolve => {
1424
- rl.question(colors.cyan('Select type (1-9): '), (answer) => {
1425
- resolve(parseInt(answer) - 1)
1426
- })
1427
- })
1428
-
1429
- if (verbIndex >= 0 && verbIndex < verbs.length - 1) {
1430
- options.type = verbs[verbIndex]
1431
- } else if (verbIndex === verbs.length - 1) {
1432
- // Custom verb
1433
- options.type = await new Promise(resolve => {
1434
- rl.question(colors.cyan('Enter custom relationship: '), (answer) => {
1435
- resolve(answer)
1436
- })
1437
- })
1438
- }
1439
- }
1440
-
1441
- // Get target if not provided
1442
- if (!target) {
1443
- // Show recent items again
1444
- const recent = await brainyInstance.search('*', { limit: 10, sortBy: 'timestamp' })
1445
- if (recent.length > 0) {
1446
- console.log(colors.cyan('\nRecent items (target):'))
1447
-
1448
- // Enhanced display for recent items
1449
- for (let i = 0; i < Math.min(recent.length, 10); i++) {
1450
- const item = recent[i]
1451
- try {
1452
- if (item.getDisplay && typeof item.getDisplay === 'function') {
1453
- const displayFields = await item.getDisplay()
1454
- console.log(colors.info(` ${i + 1}. ${displayFields.icon} ${item.id} - ${displayFields.title}`))
1455
- } else {
1456
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 40)}...`))
1457
- }
1458
- } catch {
1459
- console.log(colors.info(` ${i + 1}. ${item.id} - ${item.content?.substring(0, 40)}...`))
1460
- }
1461
- }
1462
- console.log()
1463
- }
1464
-
1465
- target = await new Promise(resolve => {
1466
- rl.question(colors.cyan('Enter target entity ID: '), (answer) => {
1467
- resolve(answer)
1468
- })
1469
- })
1470
-
1471
- if (!target.trim()) {
1472
- rl.close()
1473
- console.log(colors.warning('No target provided'))
1474
- process.exit(1)
1475
- }
1476
- }
1477
-
1478
- rl.close()
1479
- }
1480
- const brainy = await getBrainy()
1481
-
1482
- // Common verb types for validation
1483
- const commonTypes = ['WorksFor', 'Knows', 'CreatedBy', 'BelongsTo', 'Uses', 'LeadsProject', 'MemberOf', 'RelatedTo', 'InteractedWith']
1484
- if (!commonTypes.includes(options.type)) {
1485
- console.log(colors.warning(`āš ļø Uncommon verb type: ${options.type}`))
1486
- console.log(colors.info(`Common types: ${commonTypes.join(', ')}`))
1487
- }
1488
-
1489
- let metadata = {}
1490
- if (options.metadata) {
1491
- try {
1492
- metadata = JSON.parse(options.metadata)
1493
- } catch {
1494
- console.error(colors.error('āŒ Invalid JSON metadata'))
1495
- process.exit(1)
1496
- }
1497
- }
1498
-
1499
- if (options.encrypt) {
1500
- metadata.encrypted = true
1501
- }
1502
-
1503
- try {
1504
- const { VerbType } = await import('../dist/types/graphTypes.js')
1505
-
1506
- // Use the provided type or fall back to RelatedTo
1507
- const verbType = VerbType[options.type] || options.type
1508
- const id = await brainy.relate({ from: source, to: target, type: verbType, metadata })
1509
-
1510
- console.log(colors.success('āœ… Relationship added successfully!'))
1511
- console.log(colors.info(`šŸ†” ID: ${id}`))
1512
- console.log(colors.info(`šŸ”— ${source} --[${options.type}]--> ${target}`))
1513
- if (Object.keys(metadata).length > 0) {
1514
- console.log(colors.info(`šŸ“ Metadata: ${JSON.stringify(metadata, null, 2)}`))
1515
- }
1516
- } catch (error) {
1517
- console.log(colors.error('āŒ Failed to add relationship:'))
1518
- console.log(colors.error(error.message))
1519
- process.exit(1)
1520
- }
1521
- }))
1522
-
1523
- // Command 7: STATUS - Database health & info
1524
- program
1525
- .command('status')
1526
- .description('Show brain status and comprehensive statistics')
1527
- .option('-v, --verbose', 'Show raw JSON statistics')
1528
- .option('-s, --simple', 'Show only basic info')
1529
- .action(wrapAction(async (options) => {
1530
- console.log(colors.primary('🧠 Brain Status & Statistics'))
1531
- console.log(colors.primary('=' .repeat(50)))
1532
-
1533
- try {
1534
- const { BrainyData } = await import('../dist/brainyData.js')
1535
- const brainy = new BrainyData()
1536
- await brainy.init()
1537
-
1538
- // Get comprehensive stats
1539
- const stats = await brainy.getStatistics()
1540
- const memUsage = process.memoryUsage()
1541
-
1542
- // Basic Health Status
1543
- console.log(colors.success('šŸ’š Status: Healthy'))
1544
- console.log(colors.info(`šŸš€ Version: ${packageJson.version}`))
1545
- console.log()
1546
-
1547
- if (options.simple) {
1548
- console.log(colors.info(`šŸ“Š Total Items: ${stats.total || 0}`))
1549
- console.log(colors.info(`🧠 Memory: ${(memUsage.heapUsed / 1024 / 1024).toFixed(1)} MB`))
1550
- return
1551
- }
1552
-
1553
- // Core Statistics
1554
- console.log(colors.primary('šŸ“Š Core Database Statistics'))
1555
- console.log(colors.info(` Total Items: ${colors.success(stats.total || 0)}`))
1556
- console.log(colors.info(` Nouns: ${colors.success(stats.nounCount || 0)}`))
1557
- console.log(colors.info(` Verbs (Relationships): ${colors.success(stats.verbCount || 0)}`))
1558
- console.log(colors.info(` Metadata Records: ${colors.success(stats.metadataCount || 0)}`))
1559
- console.log()
1560
-
1561
- // Per-Service Breakdown (if available)
1562
- if (stats.serviceBreakdown && Object.keys(stats.serviceBreakdown).length > 0) {
1563
- console.log(colors.primary('šŸ”§ Per-Service Breakdown'))
1564
- Object.entries(stats.serviceBreakdown).forEach(([service, serviceStats]) => {
1565
- console.log(colors.info(` ${colors.success(service)}:`))
1566
- console.log(colors.info(` Nouns: ${serviceStats.nounCount}`))
1567
- console.log(colors.info(` Verbs: ${serviceStats.verbCount}`))
1568
- console.log(colors.info(` Metadata: ${serviceStats.metadataCount}`))
1569
- })
1570
- console.log()
1571
- }
1572
-
1573
- // Storage Information
1574
- if (stats.storage) {
1575
- console.log(colors.primary('šŸ’¾ Storage Information'))
1576
- console.log(colors.info(` Type: ${colors.success(stats.storage.type || 'Unknown')}`))
1577
- if (stats.storage.size) {
1578
- const sizeInMB = (stats.storage.size / 1024 / 1024).toFixed(2)
1579
- console.log(colors.info(` Size: ${colors.success(sizeInMB)} MB`))
1580
- }
1581
- if (stats.storage.location) {
1582
- console.log(colors.info(` Location: ${colors.success(stats.storage.location)}`))
1583
- }
1584
- console.log()
1585
- }
1586
-
1587
- // Performance Metrics
1588
- if (stats.performance) {
1589
- console.log(colors.primary('⚔ Performance Metrics'))
1590
- if (stats.performance.avgQueryTime) {
1591
- console.log(colors.info(` Avg Query Time: ${colors.success(stats.performance.avgQueryTime.toFixed(2))} ms`))
1592
- }
1593
- if (stats.performance.totalQueries) {
1594
- console.log(colors.info(` Total Queries: ${colors.success(stats.performance.totalQueries)}`))
1595
- }
1596
- if (stats.performance.cacheHitRate) {
1597
- console.log(colors.info(` Cache Hit Rate: ${colors.success((stats.performance.cacheHitRate * 100).toFixed(1))}%`))
1598
- }
1599
- console.log()
1600
- }
1601
-
1602
- // Vector Index Information
1603
- if (stats.index) {
1604
- console.log(colors.primary('šŸŽÆ Vector Index'))
1605
- console.log(colors.info(` Dimensions: ${colors.success(stats.index.dimensions || 'N/A')}`))
1606
- console.log(colors.info(` Indexed Vectors: ${colors.success(stats.index.vectorCount || 0)}`))
1607
- if (stats.index.indexSize) {
1608
- console.log(colors.info(` Index Size: ${colors.success((stats.index.indexSize / 1024 / 1024).toFixed(2))} MB`))
1609
- }
1610
- console.log()
1611
- }
1612
-
1613
- // Memory Usage Breakdown
1614
- console.log(colors.primary('🧠 Memory Usage'))
1615
- console.log(colors.info(` Heap Used: ${colors.success((memUsage.heapUsed / 1024 / 1024).toFixed(1))} MB`))
1616
- console.log(colors.info(` Heap Total: ${colors.success((memUsage.heapTotal / 1024 / 1024).toFixed(1))} MB`))
1617
- console.log(colors.info(` RSS: ${colors.success((memUsage.rss / 1024 / 1024).toFixed(1))} MB`))
1618
- console.log()
1619
-
1620
- // Active Augmentations
1621
- console.log(colors.primary('šŸ”Œ Active Augmentations'))
1622
- try {
1623
- // Check for display augmentation specifically
1624
- const displayAugmentation = (brainy as any).augmentations?.get('display')
1625
- if (displayAugmentation) {
1626
- console.log(colors.success(` āœ… display - Universal Display Augmentation`))
1627
- console.log(colors.info(` šŸŽØ AI-powered titles and descriptions`))
1628
-
1629
- // Get display augmentation stats if available
1630
- if (displayAugmentation.getStats) {
1631
- const stats = displayAugmentation.getStats()
1632
- if (stats.totalComputations > 0) {
1633
- console.log(colors.dim(` šŸ“Š ${stats.totalComputations} computations, ${(stats.cacheHitRatio * 100).toFixed(1)}% cache hit rate`))
1634
- }
1635
- }
1636
- }
1637
-
1638
- // Show other augmentations
1639
- const otherAugs = (brainy as any).augmentations ?
1640
- Array.from((brainy as any).augmentations.values()).filter((aug: any) => aug.name !== 'display') :
1641
- []
1642
-
1643
- otherAugs.forEach((aug: any) => {
1644
- console.log(colors.success(` āœ… ${aug.name}`))
1645
- if (aug.version) {
1646
- console.log(colors.info(` v${aug.version} - ${aug.description || 'No description'}`))
1647
- }
1648
- })
1649
-
1650
- if (!displayAugmentation && otherAugs.length === 0) {
1651
- console.log(colors.warning(' No augmentations currently active'))
1652
- }
1653
- } catch (error) {
1654
- console.log(colors.warning(' Augmentation status unavailable'))
1655
- }
1656
- console.log()
1657
-
1658
- // Configuration Summary
1659
- if (stats.config) {
1660
- console.log(colors.primary('āš™ļø Configuration'))
1661
- Object.entries(stats.config).forEach(([key, value]) => {
1662
- // Don't show sensitive values
1663
- if (key.toLowerCase().includes('key') || key.toLowerCase().includes('secret')) {
1664
- console.log(colors.info(` ${key}: ${colors.warning('[HIDDEN]')}`))
1665
- } else {
1666
- console.log(colors.info(` ${key}: ${colors.success(value)}`))
1667
- }
1668
- })
1669
- console.log()
1670
- }
1671
-
1672
- // Available Fields for Advanced Search
1673
- console.log(colors.primary('šŸ” Available Search Fields'))
1674
- try {
1675
- const filterFields = await brainy.getFilterFields()
1676
- if (filterFields.length > 0) {
1677
- console.log(colors.info(' Use these fields for advanced filtering:'))
1678
- filterFields.forEach(field => {
1679
- console.log(colors.success(` ${field}`))
1680
- })
1681
- console.log(colors.info('\n Example: brainy search "query" --filter \'{"type":"person"}\''))
1682
- } else {
1683
- console.log(colors.warning(' No indexed fields available yet'))
1684
- console.log(colors.info(' Add some data to see available fields'))
1685
- }
1686
- } catch (error) {
1687
- console.log(colors.warning(' Field discovery not available'))
1688
- }
1689
- console.log()
1690
-
1691
- // Show raw JSON if verbose
1692
- if (options.verbose) {
1693
- console.log(colors.primary('šŸ“‹ Raw Statistics (JSON)'))
1694
- console.log(colors.info(JSON.stringify(stats, null, 2)))
1695
- }
1696
-
1697
- } catch (error) {
1698
- console.log(colors.error('āŒ Status: Error'))
1699
- console.log(colors.error(`Error: ${error.message}`))
1700
- if (options.verbose) {
1701
- console.log(colors.error('Stack trace:'))
1702
- console.log(error.stack)
1703
- }
1704
- }
1705
- }))
1706
-
1707
- // Command 5: CONFIG - Essential configuration
1708
- program
1709
- .command('config <action> [key] [value]')
1710
- .description('Configure brainy (get, set, list)')
1711
- .action(wrapAction(async (action, key, value) => {
1712
- const configActions = {
1713
- get: async () => {
1714
- if (!key) {
1715
- console.error(colors.error('Please specify a key: brainy config get <key>'))
1716
- process.exit(1)
1717
- }
1718
- const result = await cortex.configGet(key)
1719
- console.log(colors.success(`${key}: ${result || 'not set'}`))
1720
- },
1721
- set: async () => {
1722
- if (!key || !value) {
1723
- console.error(colors.error('Usage: brainy config set <key> <value>'))
1724
- process.exit(1)
1725
- }
1726
- await cortex.configSet(key, value)
1727
- console.log(colors.success(`āœ… Set ${key} = ${value}`))
1728
- },
1729
- list: async () => {
1730
- const config = await cortex.configList()
1731
- console.log(colors.primary('šŸ”§ Current Configuration:'))
1732
- Object.entries(config).forEach(([k, v]) => {
1733
- console.log(colors.info(` ${k}: ${v}`))
1734
- })
1735
- }
1736
- }
1737
-
1738
- if (configActions[action]) {
1739
- await configActions[action]()
1740
- } else {
1741
- console.error(colors.error('Valid actions: get, set, list'))
1742
- process.exit(1)
1743
- }
1744
- }))
1745
-
1746
- // Command 6: AUGMENT - Manage augmentations (The 8th Unified Method!)
1747
- program
1748
- .command('augment <action>')
1749
- .description('Manage augmentations to extend Brainy\'s capabilities')
1750
- .option('-n, --name <name>', 'Augmentation name')
1751
- .option('-t, --type <type>', 'Augmentation type (sense, conduit, cognition, memory)')
1752
- .option('-p, --path <path>', 'Path to augmentation module')
1753
- .option('-l, --list', 'List all augmentations')
1754
- .action(wrapAction(async (action, options) => {
1755
- const brainy = await initBrainy()
1756
- console.log(colors.brain('🧩 Augmentation Management'))
1757
-
1758
- const actions = {
1759
- list: async () => {
1760
- try {
1761
- // Use soulcraft.com registry API
1762
- const REGISTRY_URL = 'https://api.soulcraft.com/v1/augmentations'
1763
- const response = await fetch(REGISTRY_URL)
1764
-
1765
- if (response && response.ok) {
1766
- console.log(colors.brain('šŸ¢ SOULCRAFT PROFESSIONAL SUITE\n'))
1767
-
1768
- const data = await response.json()
1769
- const augmentations = data.data || [] // NEW: data is in .data array
1770
-
1771
- // Get local augmentations to check what's installed
1772
- const localAugmentations = brainy.listAugmentations()
1773
- const localPackageNames = localAugmentations.map(aug => aug.package || aug.name).filter(Boolean)
1774
-
1775
- // Find installed registry augmentations
1776
- const installed = augmentations.filter(aug =>
1777
- aug.package && localPackageNames.includes(aug.package)
1778
- )
1779
-
1780
- // Display installed augmentations first
1781
- if (installed.length > 0) {
1782
- console.log(colors.success('āœ… INSTALLED AUGMENTATIONS'))
1783
- installed.forEach(aug => {
1784
- const pricing = aug.price
1785
- ? `$${aug.price.monthly}/mo`
1786
- : (aug.tier === 'free' ? 'FREE' : 'TBD')
1787
- const pricingColor = aug.tier === 'free' ? colors.success(pricing) : colors.yellow(pricing)
1788
-
1789
- console.log(` ${aug.name.padEnd(20)} ${pricingColor.padEnd(15)} ${colors.success('āœ… ACTIVE')}`)
1790
- console.log(` ${colors.dim(aug.description)}`)
1791
- console.log('')
1792
- })
1793
- console.log('') // Extra space before available augmentations
1794
- }
1795
-
1796
- // Filter out installed ones from the available lists
1797
- const availableAugmentations = augmentations.filter(aug =>
1798
- !installed.find(inst => inst.id === aug.id)
1799
- )
1800
-
1801
- // NEW: Use new tier names - "premium" instead of "professional"
1802
- const premium = availableAugmentations.filter(a => a.tier === 'premium')
1803
- const free = availableAugmentations.filter(a => a.tier === 'free')
1804
- const community = availableAugmentations.filter(a => a.tier === 'community')
1805
- const comingSoon = availableAugmentations.filter(a => a.status === 'coming_soon')
1806
-
1807
- // Display premium augmentations
1808
- if (premium.length > 0) {
1809
- console.log(colors.primary('šŸš€ PREMIUM AUGMENTATIONS'))
1810
- premium.forEach(aug => {
1811
- // NEW: price object format
1812
- const pricing = aug.price
1813
- ? `$${aug.price.monthly}/mo`
1814
- : (aug.tier === 'free' ? 'FREE' : 'TBD')
1815
- const pricingColor = aug.tier === 'free' ? colors.success(pricing) : colors.yellow(pricing)
1816
-
1817
- // NEW: status instead of verified
1818
- const status = aug.status === 'available' ? colors.blue('āœ“') :
1819
- aug.status === 'coming_soon' ? colors.yellow('ā³') :
1820
- colors.dim('•')
1821
-
1822
- console.log(` ${aug.name.padEnd(20)} ${pricingColor.padEnd(15)} ${status}`)
1823
- console.log(` ${colors.dim(aug.description)}`)
1824
-
1825
- if (aug.status === 'coming_soon' && aug.eta) {
1826
- console.log(` ${colors.cyan('→ Coming ' + aug.eta)}`)
1827
- }
1828
-
1829
- if (aug.features && aug.features.length > 0) {
1830
- console.log(` ${colors.cyan('→ ' + aug.features.join(', '))}`)
1831
- }
1832
- console.log('')
1833
- })
1834
- }
1835
-
1836
- // Display free augmentations
1837
- if (free.length > 0) {
1838
- console.log(colors.primary('šŸ†“ FREE AUGMENTATIONS'))
1839
- free.forEach(aug => {
1840
- const status = aug.status === 'available' ? colors.blue('āœ“') :
1841
- aug.status === 'coming_soon' ? colors.yellow('ā³') :
1842
- colors.dim('•')
1843
-
1844
- console.log(` ${aug.name.padEnd(20)} ${colors.success('FREE').padEnd(15)} ${status}`)
1845
- console.log(` ${colors.dim(aug.description)}`)
1846
-
1847
- if (aug.status === 'coming_soon' && aug.eta) {
1848
- console.log(` ${colors.cyan('→ Coming ' + aug.eta)}`)
1849
- }
1850
- console.log('')
1851
- })
1852
- }
1853
-
1854
- // Display community augmentations
1855
- if (community.length > 0) {
1856
- console.log(colors.primary('šŸ‘„ COMMUNITY AUGMENTATIONS'))
1857
- community.forEach(aug => {
1858
- const status = aug.status === 'available' ? colors.blue('āœ“') :
1859
- aug.status === 'coming_soon' ? colors.yellow('ā³') :
1860
- colors.dim('•')
1861
-
1862
- console.log(` ${aug.name.padEnd(20)} ${colors.success('COMMUNITY').padEnd(15)} ${status}`)
1863
- console.log(` ${colors.dim(aug.description)}`)
1864
- console.log('')
1865
- })
1866
- }
1867
-
1868
- // Display truly local (non-registry) augmentations
1869
- const localOnly = localAugmentations.filter(aug =>
1870
- !aug.package || !augmentations.find(regAug => regAug.package === aug.package)
1871
- )
1872
-
1873
- if (localOnly.length > 0) {
1874
- console.log(colors.primary('šŸ“¦ CUSTOM AUGMENTATIONS'))
1875
- localOnly.forEach(aug => {
1876
- const status = aug.enabled ? colors.success('āœ… Enabled') : colors.dim('⚪ Disabled')
1877
- console.log(` ${aug.name.padEnd(20)} ${status}`)
1878
- console.log(` ${colors.dim(aug.description || 'Custom augmentation')}`)
1879
- console.log('')
1880
- })
1881
- }
1882
-
1883
- console.log(colors.cyan('šŸŽÆ GET STARTED'))
1884
- console.log(' brainy install <name> Install augmentation')
1885
- console.log(' brainy cloud Access Brain Cloud features')
1886
- console.log(` ${colors.blue('Learn more:')} https://soulcraft.com/augmentations`)
1887
-
1888
- } else {
1889
- throw new Error('Registry unavailable')
1890
- }
1891
- } catch (error) {
1892
- // Fallback to local augmentations only
1893
- console.log(colors.warning('⚠ Professional catalog unavailable, showing local augmentations'))
1894
- const augmentations = brainy.listAugmentations()
1895
- if (augmentations.length === 0) {
1896
- console.log(colors.warning('No augmentations registered'))
1897
- return
1898
- }
1899
-
1900
- const table = new Table({
1901
- head: [colors.brain('Name'), colors.brain('Type'), colors.brain('Status'), colors.brain('Description')],
1902
- style: { head: [], border: [] }
1903
- })
1904
-
1905
- augmentations.forEach(aug => {
1906
- table.push([
1907
- colors.primary(aug.name),
1908
- colors.info(aug.type),
1909
- aug.enabled ? colors.success('āœ… Enabled') : colors.dim('⚪ Disabled'),
1910
- colors.dim(aug.description || '')
1911
- ])
1912
- })
1913
-
1914
- console.log(table.toString())
1915
- console.log(colors.info(`\nTotal: ${augmentations.length} augmentations`))
1916
- }
1917
- },
1918
-
1919
- enable: async () => {
1920
- if (!options.name) {
1921
- console.log(colors.error('Name required: --name <augmentation-name>'))
1922
- return
1923
- }
1924
- const success = brainy.enableAugmentation(options.name)
1925
- if (success) {
1926
- console.log(colors.success(`āœ… Enabled augmentation: ${options.name}`))
1927
- } else {
1928
- console.log(colors.error(`Failed to enable: ${options.name} (not found)`))
1929
- }
1930
- },
1931
-
1932
- disable: async () => {
1933
- if (!options.name) {
1934
- console.log(colors.error('Name required: --name <augmentation-name>'))
1935
- return
1936
- }
1937
- const success = brainy.disableAugmentation(options.name)
1938
- if (success) {
1939
- console.log(colors.warning(`⚪ Disabled augmentation: ${options.name}`))
1940
- } else {
1941
- console.log(colors.error(`Failed to disable: ${options.name} (not found)`))
1942
- }
1943
- },
1944
-
1945
- register: async () => {
1946
- if (!options.path) {
1947
- console.log(colors.error('Path required: --path <augmentation-module>'))
1948
- return
1949
- }
1950
-
1951
- try {
1952
- // Dynamic import of custom augmentation
1953
- const customModule = await import(options.path)
1954
- const AugmentationClass = customModule.default || customModule[Object.keys(customModule)[0]]
1955
-
1956
- if (!AugmentationClass) {
1957
- console.log(colors.error('No augmentation class found in module'))
1958
- return
1959
- }
1960
-
1961
- const augmentation = new AugmentationClass()
1962
- brainy.register(augmentation)
1963
- console.log(colors.success(`āœ… Registered augmentation: ${augmentation.name}`))
1964
- console.log(colors.info(`Type: ${augmentation.type}`))
1965
- if (augmentation.description) {
1966
- console.log(colors.dim(`Description: ${augmentation.description}`))
1967
- }
1968
- } catch (error) {
1969
- console.log(colors.error(`Failed to register augmentation: ${error.message}`))
1970
- }
1971
- },
1972
-
1973
- unregister: async () => {
1974
- if (!options.name) {
1975
- console.log(colors.error('Name required: --name <augmentation-name>'))
1976
- return
1977
- }
1978
-
1979
- brainy.unregister(options.name)
1980
- console.log(colors.warning(`šŸ—‘ļø Unregistered augmentation: ${options.name}`))
1981
- },
1982
-
1983
- 'enable-type': async () => {
1984
- if (!options.type) {
1985
- console.log(colors.error('Type required: --type <augmentation-type>'))
1986
- console.log(colors.info('Valid types: sense, conduit, cognition, memory, perception, dialog, activation'))
1987
- return
1988
- }
1989
-
1990
- const count = brainy.enableAugmentationType(options.type)
1991
- console.log(colors.success(`āœ… Enabled ${count} ${options.type} augmentations`))
1992
- },
1993
-
1994
- 'disable-type': async () => {
1995
- if (!options.type) {
1996
- console.log(colors.error('Type required: --type <augmentation-type>'))
1997
- console.log(colors.info('Valid types: sense, conduit, cognition, memory, perception, dialog, activation'))
1998
- return
1999
- }
2000
-
2001
- const count = brainy.disableAugmentationType(options.type)
2002
- console.log(colors.warning(`⚪ Disabled ${count} ${options.type} augmentations`))
2003
- }
2004
- }
2005
-
2006
- if (actions[action]) {
2007
- await actions[action]()
2008
- } else {
2009
- console.log(colors.error('Valid actions: list, enable, disable, register, unregister, enable-type, disable-type'))
2010
- console.log(colors.info('\nExamples:'))
2011
- console.log(colors.dim(' brainy augment list # List all augmentations'))
2012
- console.log(colors.dim(' brainy augment enable --name neural-import # Enable an augmentation'))
2013
- console.log(colors.dim(' brainy augment register --path ./my-augmentation.js # Register custom augmentation'))
2014
- console.log(colors.dim(' brainy augment enable-type --type sense # Enable all sense augmentations'))
2015
- }
2016
- }))
2017
-
2018
- // Command 7: CLEAR - Clear all data
2019
- program
2020
- .command('clear')
2021
- .description('Clear all data from your brain (with safety prompt)')
2022
- .option('--force', 'Force clear without confirmation')
2023
- .option('--backup', 'Create backup before clearing')
2024
- .action(wrapAction(async (options) => {
2025
- if (!options.force) {
2026
- console.log(colors.warning('🚨 This will delete ALL data in your brain!'))
2027
-
2028
- const rl = createInterface({
2029
- input: process.stdin,
2030
- output: process.stdout
2031
- })
2032
-
2033
- const confirmed = await new Promise(resolve => {
2034
- rl.question(colors.warning('Type "DELETE EVERYTHING" to confirm: '), (answer) => {
2035
- rl.close()
2036
- resolve(answer === 'DELETE EVERYTHING')
2037
- })
2038
- })
2039
-
2040
- if (!confirmed) {
2041
- console.log(colors.info('Clear operation cancelled'))
2042
- return
2043
- }
2044
- }
2045
-
2046
- const brainyInstance = await getBrainy()
2047
-
2048
- if (options.backup) {
2049
- console.log(colors.info('šŸ’¾ Creating backup...'))
2050
- // Future: implement backup functionality
2051
- console.log(colors.success('āœ… Backup created'))
2052
- }
2053
-
2054
- console.log(colors.info('šŸ—‘ļø Clearing all data...'))
2055
- await brainyInstance.clear({ force: true })
2056
- console.log(colors.success('āœ… All data cleared successfully'))
2057
- }))
2058
-
2059
- // Command 8: EXPORT - Export your data
2060
- program
2061
- .command('export')
2062
- .description('Export your brain data in various formats')
2063
- .option('-f, --format <format>', 'Export format (json, csv, graph, embeddings)', 'json')
2064
- .option('-o, --output <file>', 'Output file path')
2065
- .option('--vectors', 'Include vector embeddings')
2066
- .option('--no-metadata', 'Exclude metadata')
2067
- .option('--no-relationships', 'Exclude relationships')
2068
- .option('--filter <json>', 'Filter by metadata')
2069
- .option('-l, --limit <number>', 'Limit number of items')
2070
- .action(wrapAction(async (options) => {
2071
- const brainy = await initBrainy()
2072
- console.log(colors.brain('šŸ“¤ Exporting Brain Data'))
2073
-
2074
- const spinner = ora('Exporting data...').start()
2075
-
2076
- try {
2077
- const exportOptions = {
2078
- format: options.format,
2079
- includeVectors: options.vectors || false,
2080
- includeMetadata: options.metadata !== false,
2081
- includeRelationships: options.relationships !== false,
2082
- filter: options.filter ? JSON.parse(options.filter) : {},
2083
- limit: options.limit ? parseInt(options.limit) : undefined
2084
- }
2085
-
2086
- const data = await brainy.export(exportOptions)
2087
-
2088
- spinner.succeed('Export complete')
2089
-
2090
- if (options.output) {
2091
- // Write to file
2092
- const fs = require('fs')
2093
- const content = typeof data === 'string' ? data : JSON.stringify(data, null, 2)
2094
- fs.writeFileSync(options.output, content)
2095
- console.log(colors.success(`āœ… Exported to: ${options.output}`))
2096
-
2097
- // Show summary
2098
- const items = Array.isArray(data) ? data.length : (data.nodes ? data.nodes.length : 1)
2099
- console.log(colors.info(`šŸ“Š Format: ${options.format}`))
2100
- console.log(colors.info(`šŸ“ Items: ${items}`))
2101
- if (options.vectors) {
2102
- console.log(colors.info(`šŸ”¢ Vectors: Included`))
2103
- }
2104
- } else {
2105
- // Output to console
2106
- if (typeof data === 'string') {
2107
- console.log(data)
2108
- } else {
2109
- console.log(JSON.stringify(data, null, 2))
2110
- }
2111
- }
2112
- } catch (error) {
2113
- spinner.fail('Export failed')
2114
- console.error(colors.error(error.message))
2115
- process.exit(1)
2116
- }
2117
- }))
2118
-
2119
- // Command 8: CLOUD - Premium features connection
2120
- program
2121
- .command('cloud <action>')
2122
- .description('ā˜ļø Brain Cloud - AI Memory, Team Sync, Enterprise Connectors (FREE TRIAL!)')
2123
- .option('-i, --instance <id>', 'Brain Cloud instance ID')
2124
- .option('-e, --email <email>', 'Your email for signup')
2125
- .action(wrapAction(async (action, options) => {
2126
- console.log(boxen(
2127
- colors.brain('ā˜ļø BRAIN CLOUD - SUPERCHARGE YOUR BRAIN! šŸš€\n\n') +
2128
- colors.success('✨ FREE TRIAL: First 100GB FREE!\n') +
2129
- colors.info('šŸ’° Then just $9/month (individuals) or $49/month (teams)\n\n') +
2130
- colors.primary('Features:\n') +
2131
- colors.dim(' • AI Memory that persists across sessions\n') +
2132
- colors.dim(' • Multi-agent coordination\n') +
2133
- colors.dim(' • Automatic backups & sync\n') +
2134
- colors.dim(' • Premium connectors (Notion, Slack, etc.)'),
2135
- { padding: 1, borderStyle: 'round', borderColor: 'cyan' }
2136
- ))
2137
-
2138
- const cloudActions = {
2139
- setup: async () => {
2140
- console.log(colors.brain('\nšŸš€ Quick Setup - 30 seconds to superpowers!\n'))
2141
-
2142
- if (!options.email) {
2143
- const { email } = await prompts({
2144
- type: 'text',
2145
- name: 'email',
2146
- message: 'Enter your email for FREE trial:',
2147
- validate: (value) => value.includes('@') || 'Please enter a valid email'
2148
- })
2149
- options.email = email
2150
- }
2151
-
2152
- console.log(colors.success(`\nāœ… Setting up Brain Cloud for: ${options.email}`))
2153
- console.log(colors.info('\nšŸ“§ Check your email for activation link!'))
2154
- console.log(colors.dim('\nOr visit: https://app.soulcraft.com/activate\n'))
2155
-
2156
- // Cloud features planned for future release
2157
- console.log(colors.brain('šŸŽ‰ Your Brain Cloud trial is ready!'))
2158
- console.log(colors.success('\nNext steps:'))
2159
- console.log(colors.dim(' 1. Check your email for API key'))
2160
- console.log(colors.dim(' 2. Run: brainy cloud connect --key YOUR_KEY'))
2161
- console.log(colors.dim(' 3. Start using persistent AI memory!'))
2162
- },
2163
- connect: async () => {
2164
- console.log(colors.info('šŸ”— Connecting to Brain Cloud...'))
2165
- // Dynamic import to avoid loading premium code unnecessarily
2166
- console.log(colors.info('šŸ”— Cloud features coming soon...'))
2167
- console.log(colors.info('Brainy works offline by default'))
2168
- },
2169
- status: async () => {
2170
- console.log(colors.info('ā˜ļø Cloud Status: Available in future release'))
2171
- console.log(colors.info('Current version works offline'))
2172
- },
2173
- augmentations: async () => {
2174
- console.log(colors.info('🧩 Cloud augmentations coming in future release'))
2175
- console.log(colors.info('Local augmentations available now'))
2176
- }
2177
- }
2178
-
2179
- if (cloudActions[action]) {
2180
- await cloudActions[action]()
2181
- } else {
2182
- console.log(colors.error('Valid actions: connect, status, augmentations'))
2183
- console.log(colors.info('Example: brainy cloud connect --instance demo-test-auto'))
2184
- }
2185
- }))
2186
-
2187
- // Command 7: MIGRATE - Migration tools
2188
- program
2189
- .command('migrate <action>')
2190
- .description('Migration tools for upgrades')
2191
- .option('-f, --from <version>', 'Migrate from version')
2192
- .option('-b, --backup', 'Create backup before migration')
2193
- .action(wrapAction(async (action, options) => {
2194
- console.log(colors.primary('šŸ”„ Brainy Migration Tools'))
2195
-
2196
- const migrateActions = {
2197
- check: async () => {
2198
- console.log(colors.info('šŸ” Checking for migration needs...'))
2199
- // Check for deprecated methods, old config, etc.
2200
- const issues = []
2201
-
2202
- try {
2203
- const { BrainyData } = await import('../dist/brainyData.js')
2204
- const brainy = new BrainyData()
2205
-
2206
- // Check for old API usage
2207
- console.log(colors.success('āœ… No migration issues found'))
2208
- } catch (error) {
2209
- console.log(colors.warning(`āš ļø Found issues: ${error.message}`))
2210
- }
2211
- },
2212
- backup: async () => {
2213
- console.log(colors.info('šŸ’¾ Creating backup...'))
2214
- const { BrainyData } = await import('../dist/brainyData.js')
2215
- const brainy = new BrainyData()
2216
- const backup = await brainy.createBackup()
2217
- console.log(colors.success(`āœ… Backup created: ${backup.path}`))
2218
- },
2219
- restore: async () => {
2220
- if (!options.from) {
2221
- console.error(colors.error('Please specify backup file: --from <path>'))
2222
- process.exit(1)
2223
- }
2224
- console.log(colors.info(`šŸ“„ Restoring from: ${options.from}`))
2225
- const { BrainyData } = await import('../dist/brainyData.js')
2226
- const brainy = new BrainyData()
2227
- await brainy.restoreBackup(options.from)
2228
- console.log(colors.success('āœ… Restore complete'))
2229
- }
2230
- }
2231
-
2232
- if (migrateActions[action]) {
2233
- await migrateActions[action]()
2234
- } else {
2235
- console.log(colors.error('Valid actions: check, backup, restore'))
2236
- console.log(colors.info('Example: brainy migrate check'))
2237
- }
2238
- }))
2239
-
2240
- // Command 8: HELP - Interactive guidance
2241
- program
2242
- .command('help [command]')
2243
- .description('Get help or enter interactive mode')
2244
- .action(wrapAction(async (command) => {
2245
- if (command) {
2246
- program.help()
2247
- return
2248
- }
2249
-
2250
- // Interactive mode for beginners
2251
- console.log(colors.primary('šŸ§ āš›ļø Welcome to Brainy!'))
2252
- console.log(colors.info('Your AI-powered second brain'))
2253
- console.log()
2254
-
2255
- const rl = createInterface({
2256
- input: process.stdin,
2257
- output: process.stdout
2258
- })
2259
-
2260
- console.log(colors.primary('What would you like to do?'))
2261
- console.log(colors.info('1. Add some data'))
2262
- console.log(colors.info('2. Chat with AI using your data'))
2263
- console.log(colors.info('3. Search your brain'))
2264
- console.log(colors.info('4. Update existing data'))
2265
- console.log(colors.info('5. Delete data'))
2266
- console.log(colors.info('6. Import a file'))
2267
- console.log(colors.info('7. Check status'))
2268
- console.log(colors.info('8. Connect to Brain Cloud'))
2269
- console.log(colors.info('9. Configuration'))
2270
- console.log(colors.info('10. Show all commands'))
2271
- console.log()
2272
-
2273
- const choice = await new Promise(resolve => {
2274
- rl.question(colors.primary('Enter your choice (1-10): '), (answer) => {
2275
- rl.close()
2276
- resolve(answer)
2277
- })
2278
- })
2279
-
2280
- switch (choice) {
2281
- case '1':
2282
- console.log(colors.success('\n🧠 Use: brainy add "your data here"'))
2283
- console.log(colors.info('Example: brainy add "John works at Google"'))
2284
- break
2285
- case '2':
2286
- console.log(colors.success('\nšŸ’¬ Use: brainy chat "your question"'))
2287
- console.log(colors.info('Example: brainy chat "Tell me about my data"'))
2288
- console.log(colors.info('Supports: local (Ollama), OpenAI, Claude'))
2289
- break
2290
- case '3':
2291
- console.log(colors.success('\nšŸ” Use: brainy search "your query"'))
2292
- console.log(colors.info('Example: brainy search "Google employees"'))
2293
- break
2294
- case '4':
2295
- console.log(colors.success('\nšŸ“„ Use: brainy import <file-or-url>'))
2296
- console.log(colors.info('Example: brainy import data.txt'))
2297
- break
2298
- case '5':
2299
- console.log(colors.success('\nšŸ“Š Use: brainy status'))
2300
- console.log(colors.info('Shows comprehensive brain statistics'))
2301
- console.log(colors.info('Options: --simple (quick) or --verbose (detailed)'))
2302
- break
2303
- case '6':
2304
- console.log(colors.success('\nā˜ļø Use: brainy cloud connect'))
2305
- console.log(colors.info('Example: brainy cloud connect --instance demo-test-auto'))
2306
- break
2307
- case '7':
2308
- console.log(colors.success('\nšŸ”§ Use: brainy config <action>'))
2309
- console.log(colors.info('Example: brainy config list'))
2310
- break
2311
- case '8':
2312
- program.help()
2313
- break
2314
- default:
2315
- console.log(colors.warning('Invalid choice. Use "brainy --help" for all commands.'))
2316
- }
2317
- }))
2318
-
2319
- // ========================================
2320
- // FALLBACK - Show interactive help if no command
2321
- // ========================================
2322
-
2323
- // Handle --interactive flag
2324
- if (process.argv.includes('-i') || process.argv.includes('--interactive')) {
2325
- // Start full interactive mode
2326
- console.log(colors.primary('🧠 Starting Interactive Mode...'))
2327
- import('./brainy-interactive.js').then(module => {
2328
- module.startInteractiveMode()
2329
- }).catch(error => {
2330
- console.error(colors.error('Failed to start interactive mode:'), error.message)
2331
- // Fallback to simple interactive prompt
2332
- program.parse(['node', 'brainy', 'help'])
2333
- })
2334
- } else if (process.argv.length === 2) {
2335
- // No arguments - show interactive help
2336
- program.parse(['node', 'brainy', 'help'])
2337
- } else {
2338
- // Parse normally
2339
- program.parse(process.argv)
2340
- }
10
+ import('../dist/cli/index.js').catch((error) => {
11
+ console.error('Failed to load Brainy CLI:', error.message)
12
+ console.error('Make sure you have built the project: npm run build')
13
+ process.exit(1)
14
+ })