chore: recovery checkpoint - v3.0 API successfully recovered
CRITICAL CHECKPOINT - DO NOT PUSH TO GITHUB Recovery Status: - Successfully recovered brainy.ts from compiled JavaScript - All core v3.0 API methods functional (add, get, update, delete, relate, find, etc.) - Neural subsystem intact (562KB embedded patterns, NLP working) - Augmentation pipeline operational (20+ augmentations) - HNSW clustering system complete - Triple Intelligence compiled (needs constructor fix) - Test suite validates functionality Changes preserved: - 898 files with changes from last 3 days - 144,475 insertions - All augmentation improvements - All test coverage enhancements - Complete v3.0 feature set This is a LOCAL checkpoint only - contains recovered work after corruption incident. Created backup in .backups/brainy-full-20250910-151314.tar.gz Branch: recovery-checkpoint-20250910-151433 Date: Wed Sep 10 03:18:04 PM PDT 2025
This commit is contained in:
parent
f65455fb22
commit
8ff382ca3b
895 changed files with 143654 additions and 28268 deletions
47
.recovery-workspace/dist-backup-20250910-141917/cli/catalog.d.ts
vendored
Normal file
47
.recovery-workspace/dist-backup-20250910-141917/cli/catalog.d.ts
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Augmentation Catalog for CLI
|
||||
*
|
||||
* Displays available augmentations catalog
|
||||
* Local catalog with caching support
|
||||
*/
|
||||
interface Augmentation {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
status: 'available' | 'coming_soon' | 'deprecated';
|
||||
popular?: boolean;
|
||||
eta?: string;
|
||||
}
|
||||
interface Category {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
description: string;
|
||||
}
|
||||
interface Catalog {
|
||||
version: string;
|
||||
categories: Category[];
|
||||
augmentations: Augmentation[];
|
||||
}
|
||||
/**
|
||||
* Fetch catalog from API with caching
|
||||
*/
|
||||
export declare function fetchCatalog(): Promise<Catalog | null>;
|
||||
/**
|
||||
* Display catalog in CLI
|
||||
*/
|
||||
export declare function showCatalog(options: {
|
||||
category?: string;
|
||||
search?: string;
|
||||
detailed?: boolean;
|
||||
}): Promise<void>;
|
||||
/**
|
||||
* Show detailed info about an augmentation
|
||||
*/
|
||||
export declare function showAugmentationInfo(id: string): Promise<void>;
|
||||
/**
|
||||
* Show user's available augmentations
|
||||
*/
|
||||
export declare function showAvailable(licenseKey?: string): Promise<void>;
|
||||
export {};
|
||||
371
.recovery-workspace/dist-backup-20250910-141917/cli/catalog.js
Normal file
371
.recovery-workspace/dist-backup-20250910-141917/cli/catalog.js
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/**
|
||||
* Augmentation Catalog for CLI
|
||||
*
|
||||
* Displays available augmentations catalog
|
||||
* Local catalog with caching support
|
||||
*/
|
||||
import chalk from 'chalk';
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
const CATALOG_API = process.env.BRAINY_CATALOG_URL || null;
|
||||
const CACHE_PATH = join(homedir(), '.brainy', 'catalog-cache.json');
|
||||
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
||||
/**
|
||||
* Fetch catalog from API with caching
|
||||
*/
|
||||
export async function fetchCatalog() {
|
||||
try {
|
||||
// Check cache first
|
||||
const cached = loadCache();
|
||||
if (cached)
|
||||
return cached;
|
||||
// If external catalog API is configured, try to fetch
|
||||
if (CATALOG_API) {
|
||||
const response = await fetch(`${CATALOG_API}/api/catalog/cli`);
|
||||
if (!response.ok)
|
||||
throw new Error('API unavailable');
|
||||
const catalog = await response.json();
|
||||
// Save to cache
|
||||
saveCache(catalog);
|
||||
return catalog;
|
||||
}
|
||||
// Fall back to local catalog
|
||||
return getDefaultCatalog();
|
||||
}
|
||||
catch (error) {
|
||||
// Try loading from cache even if expired
|
||||
const cached = loadCache(true);
|
||||
if (cached) {
|
||||
console.log(chalk.yellow('📡 Using cached catalog'));
|
||||
return cached;
|
||||
}
|
||||
// Fall back to hardcoded catalog
|
||||
return getDefaultCatalog();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Display catalog in CLI
|
||||
*/
|
||||
export async function showCatalog(options) {
|
||||
const catalog = await fetchCatalog();
|
||||
if (!catalog) {
|
||||
console.log(chalk.red('❌ Could not load augmentation catalog'));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.cyan.bold('🧠 Brainy Augmentation Catalog'));
|
||||
console.log(chalk.gray(`Version ${catalog.version}`));
|
||||
console.log('');
|
||||
// Filter augmentations
|
||||
let augmentations = catalog.augmentations;
|
||||
if (options.category) {
|
||||
augmentations = augmentations.filter(a => a.category === options.category);
|
||||
}
|
||||
if (options.search) {
|
||||
const query = options.search.toLowerCase();
|
||||
augmentations = augmentations.filter(a => a.name.toLowerCase().includes(query) ||
|
||||
a.description.toLowerCase().includes(query));
|
||||
}
|
||||
// Group by category
|
||||
const grouped = groupByCategory(augmentations, catalog.categories);
|
||||
// Display
|
||||
for (const [category, augs] of Object.entries(grouped)) {
|
||||
if (augs.length === 0)
|
||||
continue;
|
||||
const cat = catalog.categories.find(c => c.id === category);
|
||||
console.log(chalk.bold(`${cat?.icon || '📦'} ${cat?.name || category}`));
|
||||
for (const aug of augs) {
|
||||
const status = getStatusIcon(aug.status);
|
||||
const popular = aug.popular ? chalk.yellow(' ⭐') : '';
|
||||
const eta = aug.eta ? chalk.gray(` (${aug.eta})`) : '';
|
||||
console.log(` ${status} ${aug.name}${popular}${eta}`);
|
||||
if (options.detailed) {
|
||||
console.log(chalk.gray(` ${aug.description}`));
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
// Show summary
|
||||
const available = augmentations.filter(a => a.status === 'available').length;
|
||||
const coming = augmentations.filter(a => a.status === 'coming_soon').length;
|
||||
console.log(chalk.gray('─'.repeat(50)));
|
||||
console.log(chalk.green(`✅ ${available} available`) + chalk.gray(` • `) +
|
||||
chalk.yellow(`🔜 ${coming} coming soon`));
|
||||
console.log('');
|
||||
console.log(chalk.dim('Configure augmentations with "brainy augment"'));
|
||||
console.log(chalk.dim('Run "brainy augment info <name>" for details'));
|
||||
}
|
||||
/**
|
||||
* Show detailed info about an augmentation
|
||||
*/
|
||||
export async function showAugmentationInfo(id) {
|
||||
const catalog = await fetchCatalog();
|
||||
if (!catalog) {
|
||||
console.log(chalk.red('❌ Could not load augmentation catalog'));
|
||||
return;
|
||||
}
|
||||
const aug = catalog.augmentations.find(a => a.id === id);
|
||||
if (!aug) {
|
||||
console.log(chalk.red(`❌ Augmentation not found: ${id}`));
|
||||
console.log('');
|
||||
console.log('Available augmentations:');
|
||||
catalog.augmentations.forEach(a => {
|
||||
console.log(` • ${a.id}`);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Fetch full details from API if available
|
||||
try {
|
||||
if (!CATALOG_API)
|
||||
throw new Error('No external catalog configured');
|
||||
const response = await fetch(`${CATALOG_API}/api/catalog/augmentation/${id}`);
|
||||
const details = await response.json();
|
||||
console.log(chalk.cyan.bold(`📦 ${details.name}`));
|
||||
if (details.popular)
|
||||
console.log(chalk.yellow('⭐ Popular'));
|
||||
console.log('');
|
||||
console.log(chalk.bold('Category:'), getCategoryName(details.category, catalog.categories));
|
||||
console.log(chalk.bold('Status:'), getStatusText(details.status));
|
||||
if (details.eta)
|
||||
console.log(chalk.bold('Expected:'), details.eta);
|
||||
console.log('');
|
||||
console.log(chalk.bold('Description:'));
|
||||
console.log(details.longDescription || details.description);
|
||||
console.log('');
|
||||
if (details.features) {
|
||||
console.log(chalk.bold('Features:'));
|
||||
details.features.forEach((f) => console.log(` ✓ ${f}`));
|
||||
console.log('');
|
||||
}
|
||||
if (details.example) {
|
||||
console.log(chalk.bold('Example:'));
|
||||
console.log(chalk.gray('─'.repeat(50)));
|
||||
console.log(details.example.code);
|
||||
console.log(chalk.gray('─'.repeat(50)));
|
||||
console.log('');
|
||||
}
|
||||
if (details.requirements?.config) {
|
||||
console.log(chalk.bold('Required Configuration:'));
|
||||
details.requirements.config.forEach((c) => console.log(` • ${c}`));
|
||||
console.log('');
|
||||
}
|
||||
if (details.pricing) {
|
||||
console.log(chalk.bold('Available in:'));
|
||||
details.pricing.tiers.forEach((t) => console.log(` • ${t}`));
|
||||
console.log('');
|
||||
}
|
||||
console.log(chalk.dim('To activate: brainy augment activate'));
|
||||
}
|
||||
catch (error) {
|
||||
// Show basic info if API fails
|
||||
console.log(chalk.cyan.bold(`📦 ${aug.name}`));
|
||||
console.log(aug.description);
|
||||
console.log('');
|
||||
console.log(chalk.dim('Full details unavailable (no external catalog configured)'));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Show user's available augmentations
|
||||
*/
|
||||
export async function showAvailable(licenseKey) {
|
||||
// Show local catalog as default
|
||||
const catalog = await fetchCatalog();
|
||||
if (!catalog) {
|
||||
console.log(chalk.red('❌ Could not load augmentation catalog'));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.cyan.bold('🧠 Available Augmentations'));
|
||||
console.log('');
|
||||
const available = catalog.augmentations.filter(a => a.status === 'available');
|
||||
const grouped = groupByCategory(available, catalog.categories);
|
||||
for (const [category, augs] of Object.entries(grouped)) {
|
||||
if (augs.length === 0)
|
||||
continue;
|
||||
const cat = catalog.categories.find(c => c.id === category);
|
||||
console.log(chalk.bold(`${cat?.icon || '📦'} ${cat?.name || category}`));
|
||||
augs.forEach(aug => {
|
||||
console.log(` ✅ ${aug.name}`);
|
||||
console.log(chalk.gray(` ${aug.description}`));
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
console.log(chalk.green(`✅ ${available.length} augmentations available`));
|
||||
// If external API is configured and license key provided, try to fetch personalized data
|
||||
if (CATALOG_API && licenseKey) {
|
||||
try {
|
||||
const response = await fetch(`${CATALOG_API}/api/catalog/available`, {
|
||||
headers: { 'x-license-key': licenseKey }
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log(chalk.gray(`Plan: ${data.plan || 'Standard'}`));
|
||||
if (data.operations) {
|
||||
const used = data.operations.used || 0;
|
||||
const limit = data.operations.limit;
|
||||
const percent = limit === 'unlimited' ? 0 : Math.round((used / limit) * 100);
|
||||
console.log(chalk.bold('Usage:'));
|
||||
if (limit === 'unlimited') {
|
||||
console.log(` Unlimited operations`);
|
||||
}
|
||||
else {
|
||||
console.log(` ${used.toLocaleString()} / ${limit.toLocaleString()} operations (${percent}%)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// Ignore external API errors - local catalog is sufficient
|
||||
}
|
||||
}
|
||||
}
|
||||
// Helper functions
|
||||
function loadCache(ignoreExpiry = false) {
|
||||
try {
|
||||
if (!existsSync(CACHE_PATH))
|
||||
return null;
|
||||
const data = JSON.parse(readFileSync(CACHE_PATH, 'utf8'));
|
||||
if (!ignoreExpiry && Date.now() - data.timestamp > CACHE_TTL) {
|
||||
return null;
|
||||
}
|
||||
return data.catalog;
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function saveCache(catalog) {
|
||||
try {
|
||||
const dir = join(homedir(), '.brainy');
|
||||
if (!existsSync(dir)) {
|
||||
require('fs').mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
writeFileSync(CACHE_PATH, JSON.stringify({
|
||||
catalog,
|
||||
timestamp: Date.now()
|
||||
}));
|
||||
}
|
||||
catch {
|
||||
// Ignore cache save errors
|
||||
}
|
||||
}
|
||||
function groupByCategory(augmentations, categories) {
|
||||
const grouped = {};
|
||||
for (const aug of augmentations) {
|
||||
if (!grouped[aug.category]) {
|
||||
grouped[aug.category] = [];
|
||||
}
|
||||
grouped[aug.category].push(aug);
|
||||
}
|
||||
// Sort by category order
|
||||
const ordered = {};
|
||||
const categoryOrder = ['memory', 'coordination', 'enterprise', 'perception', 'dialog', 'activation', 'cognition', 'websocket'];
|
||||
for (const cat of categoryOrder) {
|
||||
if (grouped[cat]) {
|
||||
ordered[cat] = grouped[cat];
|
||||
}
|
||||
}
|
||||
return ordered;
|
||||
}
|
||||
function getStatusIcon(status) {
|
||||
switch (status) {
|
||||
case 'available': return chalk.green('✅');
|
||||
case 'coming_soon': return chalk.yellow('🔜');
|
||||
case 'deprecated': return chalk.red('⚠️');
|
||||
default: return '❓';
|
||||
}
|
||||
}
|
||||
function getStatusText(status) {
|
||||
switch (status) {
|
||||
case 'available': return chalk.green('Available');
|
||||
case 'coming_soon': return chalk.yellow('Coming Soon');
|
||||
case 'deprecated': return chalk.red('Deprecated');
|
||||
default: return 'Unknown';
|
||||
}
|
||||
}
|
||||
function getCategoryName(categoryId, categories) {
|
||||
const cat = categories.find(c => c.id === categoryId);
|
||||
return cat ? `${cat.icon} ${cat.name}` : categoryId;
|
||||
}
|
||||
function readLicenseFile() {
|
||||
try {
|
||||
const licensePath = join(homedir(), '.brainy', 'license');
|
||||
if (existsSync(licensePath)) {
|
||||
return readFileSync(licensePath, 'utf8').trim();
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return null;
|
||||
}
|
||||
function getDefaultCatalog() {
|
||||
// Local catalog with current features
|
||||
return {
|
||||
version: '1.5.0',
|
||||
categories: [
|
||||
{ id: 'core', name: 'Core Features', icon: '🧠', description: 'Essential brainy functionality' },
|
||||
{ id: 'neural', name: 'Neural API', icon: '🔗', description: 'Semantic similarity and clustering' },
|
||||
{ id: 'enterprise', name: 'Enterprise', icon: '🏢', description: 'Business integrations' },
|
||||
{ id: 'storage', name: 'Storage', icon: '💾', description: 'Data persistence and caching' }
|
||||
],
|
||||
augmentations: [
|
||||
{
|
||||
id: 'vector-search',
|
||||
name: 'Vector Search',
|
||||
category: 'core',
|
||||
description: 'High-performance semantic search with HNSW indexing',
|
||||
status: 'available',
|
||||
popular: true
|
||||
},
|
||||
{
|
||||
id: 'neural-similarity',
|
||||
name: 'Neural Similarity API',
|
||||
category: 'neural',
|
||||
description: 'Advanced semantic similarity, clustering, and hierarchy detection',
|
||||
status: 'available',
|
||||
popular: true
|
||||
},
|
||||
{
|
||||
id: 'intelligent-verb-scoring',
|
||||
name: 'Intelligent Verb Scoring',
|
||||
category: 'neural',
|
||||
description: 'Smart relationship scoring with taxonomy understanding',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 'wal-augmentation',
|
||||
name: 'WAL-based Augmentation',
|
||||
category: 'enterprise',
|
||||
description: 'Write-ahead log for reliable augmentation processing',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 'connection-pooling',
|
||||
name: 'Connection Pooling',
|
||||
category: 'enterprise',
|
||||
description: 'Efficient database connection management',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 'batch-processing',
|
||||
name: 'Batch Processing',
|
||||
category: 'enterprise',
|
||||
description: 'High-throughput batch operations with deduplication',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 's3-storage',
|
||||
name: 'S3 Compatible Storage',
|
||||
category: 'storage',
|
||||
description: 'Cloud storage with optimized batch operations',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 'opfs-storage',
|
||||
name: 'OPFS Storage',
|
||||
category: 'storage',
|
||||
description: 'Browser-based persistent storage',
|
||||
status: 'available'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=catalog.js.map
|
||||
File diff suppressed because one or more lines are too long
61
.recovery-workspace/dist-backup-20250910-141917/cli/commands/core.d.ts
vendored
Normal file
61
.recovery-workspace/dist-backup-20250910-141917/cli/commands/core.d.ts
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* Core CLI Commands - TypeScript Implementation
|
||||
*
|
||||
* Essential database operations: add, search, get, relate, import, export
|
||||
*/
|
||||
interface CoreOptions {
|
||||
verbose?: boolean;
|
||||
json?: boolean;
|
||||
pretty?: boolean;
|
||||
}
|
||||
interface AddOptions extends CoreOptions {
|
||||
id?: string;
|
||||
metadata?: string;
|
||||
type?: string;
|
||||
}
|
||||
interface SearchOptions extends CoreOptions {
|
||||
limit?: string;
|
||||
threshold?: string;
|
||||
metadata?: string;
|
||||
}
|
||||
interface GetOptions extends CoreOptions {
|
||||
withConnections?: boolean;
|
||||
}
|
||||
interface RelateOptions extends CoreOptions {
|
||||
weight?: string;
|
||||
metadata?: string;
|
||||
}
|
||||
interface ImportOptions extends CoreOptions {
|
||||
format?: 'json' | 'csv' | 'jsonl';
|
||||
batchSize?: string;
|
||||
}
|
||||
interface ExportOptions extends CoreOptions {
|
||||
format?: 'json' | 'csv' | 'jsonl';
|
||||
}
|
||||
export declare const coreCommands: {
|
||||
/**
|
||||
* Add data to the neural database
|
||||
*/
|
||||
add(text: string, options: AddOptions): Promise<void>;
|
||||
/**
|
||||
* Search the neural database
|
||||
*/
|
||||
search(query: string, options: SearchOptions): Promise<void>;
|
||||
/**
|
||||
* Get item by ID
|
||||
*/
|
||||
get(id: string, options: GetOptions): Promise<void>;
|
||||
/**
|
||||
* Create relationship between items
|
||||
*/
|
||||
relate(source: string, verb: string, target: string, options: RelateOptions): Promise<void>;
|
||||
/**
|
||||
* Import data from file
|
||||
*/
|
||||
import(file: string, options: ImportOptions): Promise<void>;
|
||||
/**
|
||||
* Export database
|
||||
*/
|
||||
export(file: string | undefined, options: ExportOptions): Promise<void>;
|
||||
};
|
||||
export {};
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/**
|
||||
* Core CLI Commands - TypeScript Implementation
|
||||
*
|
||||
* Essential database operations: add, search, get, relate, import, export
|
||||
*/
|
||||
import chalk from 'chalk';
|
||||
import ora from 'ora';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { BrainyData } from '../../brainyData.js';
|
||||
let brainyInstance = null;
|
||||
const getBrainy = async () => {
|
||||
if (!brainyInstance) {
|
||||
brainyInstance = new BrainyData();
|
||||
await brainyInstance.init();
|
||||
}
|
||||
return brainyInstance;
|
||||
};
|
||||
const formatOutput = (data, options) => {
|
||||
if (options.json) {
|
||||
console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
|
||||
}
|
||||
};
|
||||
export const coreCommands = {
|
||||
/**
|
||||
* Add data to the neural database
|
||||
*/
|
||||
async add(text, options) {
|
||||
const spinner = ora('Adding to neural database...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
let metadata = {};
|
||||
if (options.metadata) {
|
||||
try {
|
||||
metadata = JSON.parse(options.metadata);
|
||||
}
|
||||
catch {
|
||||
spinner.fail('Invalid metadata JSON');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (options.id) {
|
||||
metadata.id = options.id;
|
||||
}
|
||||
if (options.type) {
|
||||
metadata.type = options.type;
|
||||
}
|
||||
// Smart detection by default
|
||||
const result = await brain.add(text, metadata);
|
||||
spinner.succeed('Added successfully');
|
||||
if (!options.json) {
|
||||
console.log(chalk.green(`✓ Added with ID: ${result}`));
|
||||
if (options.type) {
|
||||
console.log(chalk.dim(` Type: ${options.type}`));
|
||||
}
|
||||
if (Object.keys(metadata).length > 0) {
|
||||
console.log(chalk.dim(` Metadata: ${JSON.stringify(metadata)}`));
|
||||
}
|
||||
}
|
||||
else {
|
||||
formatOutput({ id: result, metadata }, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Failed to add data');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Search the neural database
|
||||
*/
|
||||
async search(query, options) {
|
||||
const spinner = ora('Searching neural database...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
const searchOptions = {
|
||||
limit: options.limit ? parseInt(options.limit) : 10
|
||||
};
|
||||
if (options.threshold) {
|
||||
searchOptions.threshold = parseFloat(options.threshold);
|
||||
}
|
||||
if (options.metadata) {
|
||||
try {
|
||||
searchOptions.filter = JSON.parse(options.metadata);
|
||||
}
|
||||
catch {
|
||||
spinner.fail('Invalid metadata filter JSON');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
const results = await brain.search(query, searchOptions.limit, searchOptions);
|
||||
spinner.succeed(`Found ${results.length} results`);
|
||||
if (!options.json) {
|
||||
if (results.length === 0) {
|
||||
console.log(chalk.yellow('No results found'));
|
||||
}
|
||||
else {
|
||||
results.forEach((result, i) => {
|
||||
console.log(chalk.cyan(`\n${i + 1}. ${result.content || result.id}`));
|
||||
if (result.score !== undefined) {
|
||||
console.log(chalk.dim(` Similarity: ${(result.score * 100).toFixed(1)}%`));
|
||||
}
|
||||
if (result.metadata) {
|
||||
console.log(chalk.dim(` Metadata: ${JSON.stringify(result.metadata)}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
formatOutput(results, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Search failed');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Get item by ID
|
||||
*/
|
||||
async get(id, options) {
|
||||
const spinner = ora('Fetching item...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
// Try to get the item
|
||||
const results = await brain.search(id, 1);
|
||||
if (results.length === 0) {
|
||||
spinner.fail('Item not found');
|
||||
console.log(chalk.yellow(`No item found with ID: ${id}`));
|
||||
process.exit(1);
|
||||
}
|
||||
const item = results[0];
|
||||
spinner.succeed('Item found');
|
||||
if (!options.json) {
|
||||
console.log(chalk.cyan('\nItem Details:'));
|
||||
console.log(` ID: ${item.id}`);
|
||||
console.log(` Content: ${item.content || 'N/A'}`);
|
||||
if (item.metadata) {
|
||||
console.log(` Metadata: ${JSON.stringify(item.metadata, null, 2)}`);
|
||||
}
|
||||
if (options.withConnections) {
|
||||
// Get verbs/relationships
|
||||
// Get connections if method exists
|
||||
const connections = brain.getConnections ? await brain.getConnections(id) : [];
|
||||
if (connections && connections.length > 0) {
|
||||
console.log(chalk.cyan('\nConnections:'));
|
||||
connections.forEach((conn) => {
|
||||
console.log(` ${conn.source} --[${conn.type}]--> ${conn.target}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
formatOutput(item, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Failed to get item');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Create relationship between items
|
||||
*/
|
||||
async relate(source, verb, target, options) {
|
||||
const spinner = ora('Creating relationship...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
let metadata = {};
|
||||
if (options.metadata) {
|
||||
try {
|
||||
metadata = JSON.parse(options.metadata);
|
||||
}
|
||||
catch {
|
||||
spinner.fail('Invalid metadata JSON');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (options.weight) {
|
||||
metadata.weight = parseFloat(options.weight);
|
||||
}
|
||||
// Create the relationship
|
||||
const result = await brain.addVerb(source, target, verb, metadata);
|
||||
spinner.succeed('Relationship created');
|
||||
if (!options.json) {
|
||||
console.log(chalk.green(`✓ Created relationship with ID: ${result}`));
|
||||
console.log(chalk.dim(` ${source} --[${verb}]--> ${target}`));
|
||||
if (metadata.weight) {
|
||||
console.log(chalk.dim(` Weight: ${metadata.weight}`));
|
||||
}
|
||||
}
|
||||
else {
|
||||
formatOutput({ id: result, source, verb, target, metadata }, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Failed to create relationship');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Import data from file
|
||||
*/
|
||||
async import(file, options) {
|
||||
const spinner = ora('Importing data...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
const format = options.format || 'json';
|
||||
const batchSize = options.batchSize ? parseInt(options.batchSize) : 100;
|
||||
// Read file content
|
||||
const content = readFileSync(file, 'utf-8');
|
||||
let items = [];
|
||||
switch (format) {
|
||||
case 'json':
|
||||
items = JSON.parse(content);
|
||||
if (!Array.isArray(items)) {
|
||||
items = [items];
|
||||
}
|
||||
break;
|
||||
case 'jsonl':
|
||||
items = content.split('\n')
|
||||
.filter(line => line.trim())
|
||||
.map(line => JSON.parse(line));
|
||||
break;
|
||||
case 'csv':
|
||||
// Simple CSV parsing (first line is headers)
|
||||
const lines = content.split('\n').filter(line => line.trim());
|
||||
const headers = lines[0].split(',').map(h => h.trim());
|
||||
items = lines.slice(1).map(line => {
|
||||
const values = line.split(',').map(v => v.trim());
|
||||
const obj = {};
|
||||
headers.forEach((h, i) => {
|
||||
obj[h] = values[i];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
break;
|
||||
}
|
||||
spinner.text = `Importing ${items.length} items...`;
|
||||
// Process in batches
|
||||
let imported = 0;
|
||||
for (let i = 0; i < items.length; i += batchSize) {
|
||||
const batch = items.slice(i, i + batchSize);
|
||||
for (const item of batch) {
|
||||
if (typeof item === 'string') {
|
||||
await brain.add(item);
|
||||
}
|
||||
else if (item.content || item.text) {
|
||||
await brain.add(item.content || item.text, item.metadata || item);
|
||||
}
|
||||
else {
|
||||
await brain.add(JSON.stringify(item), { originalData: item });
|
||||
}
|
||||
imported++;
|
||||
}
|
||||
spinner.text = `Imported ${imported}/${items.length} items...`;
|
||||
}
|
||||
spinner.succeed(`Imported ${imported} items`);
|
||||
if (!options.json) {
|
||||
console.log(chalk.green(`✓ Successfully imported ${imported} items from ${file}`));
|
||||
console.log(chalk.dim(` Format: ${format}`));
|
||||
console.log(chalk.dim(` Batch size: ${batchSize}`));
|
||||
}
|
||||
else {
|
||||
formatOutput({ imported, file, format, batchSize }, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Import failed');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Export database
|
||||
*/
|
||||
async export(file, options) {
|
||||
const spinner = ora('Exporting database...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
const format = options.format || 'json';
|
||||
// Export all data
|
||||
const data = await brain.export({ format: 'json' });
|
||||
let output = '';
|
||||
switch (format) {
|
||||
case 'json':
|
||||
output = options.pretty
|
||||
? JSON.stringify(data, null, 2)
|
||||
: JSON.stringify(data);
|
||||
break;
|
||||
case 'jsonl':
|
||||
if (Array.isArray(data)) {
|
||||
output = data.map(item => JSON.stringify(item)).join('\n');
|
||||
}
|
||||
else {
|
||||
output = JSON.stringify(data);
|
||||
}
|
||||
break;
|
||||
case 'csv':
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
// Get all unique keys for headers
|
||||
const headers = new Set();
|
||||
data.forEach(item => {
|
||||
Object.keys(item).forEach(key => headers.add(key));
|
||||
});
|
||||
const headerArray = Array.from(headers);
|
||||
// Create CSV
|
||||
output = headerArray.join(',') + '\n';
|
||||
output += data.map(item => {
|
||||
return headerArray.map(h => {
|
||||
const value = item[h];
|
||||
if (typeof value === 'object') {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
return value || '';
|
||||
}).join(',');
|
||||
}).join('\n');
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (file) {
|
||||
writeFileSync(file, output);
|
||||
spinner.succeed(`Exported to ${file}`);
|
||||
if (!options.json) {
|
||||
console.log(chalk.green(`✓ Successfully exported database to ${file}`));
|
||||
console.log(chalk.dim(` Format: ${format}`));
|
||||
console.log(chalk.dim(` Items: ${Array.isArray(data) ? data.length : 1}`));
|
||||
}
|
||||
else {
|
||||
formatOutput({ file, format, count: Array.isArray(data) ? data.length : 1 }, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
spinner.succeed('Export complete');
|
||||
console.log(output);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Export failed');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=core.js.map
|
||||
File diff suppressed because one or more lines are too long
25
.recovery-workspace/dist-backup-20250910-141917/cli/commands/neural.d.ts
vendored
Normal file
25
.recovery-workspace/dist-backup-20250910-141917/cli/commands/neural.d.ts
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* 🧠 Neural Similarity API Commands
|
||||
*
|
||||
* CLI interface for semantic similarity, clustering, and neural operations
|
||||
*/
|
||||
interface CommandArguments {
|
||||
action?: string;
|
||||
id?: string;
|
||||
query?: string;
|
||||
threshold?: number;
|
||||
format?: string;
|
||||
output?: string;
|
||||
limit?: number;
|
||||
algorithm?: string;
|
||||
dimensions?: number;
|
||||
explain?: boolean;
|
||||
_: string[];
|
||||
}
|
||||
export declare const neuralCommand: {
|
||||
command: string;
|
||||
describe: string;
|
||||
builder: (yargs: any) => any;
|
||||
handler: (argv: CommandArguments) => Promise<void>;
|
||||
};
|
||||
export default neuralCommand;
|
||||
|
|
@ -0,0 +1,508 @@
|
|||
/**
|
||||
* 🧠 Neural Similarity API Commands
|
||||
*
|
||||
* CLI interface for semantic similarity, clustering, and neural operations
|
||||
*/
|
||||
import inquirer from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
import ora from 'ora';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { BrainyData } from '../../brainyData.js';
|
||||
import { NeuralAPI } from '../../neural/neuralAPI.js';
|
||||
export const neuralCommand = {
|
||||
command: 'neural [action]',
|
||||
describe: '🧠 Neural similarity and clustering operations',
|
||||
builder: (yargs) => {
|
||||
return yargs
|
||||
.positional('action', {
|
||||
describe: 'Neural operation to perform',
|
||||
type: 'string',
|
||||
choices: ['similar', 'clusters', 'hierarchy', 'neighbors', 'path', 'outliers', 'visualize']
|
||||
})
|
||||
.option('id', {
|
||||
describe: 'Item ID for similarity operations',
|
||||
type: 'string',
|
||||
alias: 'i'
|
||||
})
|
||||
.option('query', {
|
||||
describe: 'Query text for similarity search',
|
||||
type: 'string',
|
||||
alias: 'q'
|
||||
})
|
||||
.option('threshold', {
|
||||
describe: 'Similarity threshold (0-1)',
|
||||
type: 'number',
|
||||
default: 0.7,
|
||||
alias: 't'
|
||||
})
|
||||
.option('format', {
|
||||
describe: 'Output format',
|
||||
type: 'string',
|
||||
choices: ['json', 'table', 'tree', 'graph'],
|
||||
default: 'table',
|
||||
alias: 'f'
|
||||
})
|
||||
.option('output', {
|
||||
describe: 'Output file path',
|
||||
type: 'string',
|
||||
alias: 'o'
|
||||
})
|
||||
.option('limit', {
|
||||
describe: 'Maximum number of results',
|
||||
type: 'number',
|
||||
default: 10,
|
||||
alias: 'l'
|
||||
})
|
||||
.option('algorithm', {
|
||||
describe: 'Clustering algorithm',
|
||||
type: 'string',
|
||||
choices: ['hierarchical', 'kmeans', 'dbscan', 'auto'],
|
||||
default: 'auto',
|
||||
alias: 'a'
|
||||
})
|
||||
.option('dimensions', {
|
||||
describe: 'Visualization dimensions (2 or 3)',
|
||||
type: 'number',
|
||||
choices: [2, 3],
|
||||
default: 2,
|
||||
alias: 'd'
|
||||
})
|
||||
.option('explain', {
|
||||
describe: 'Include detailed explanations',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
alias: 'e'
|
||||
});
|
||||
},
|
||||
handler: async (argv) => {
|
||||
console.log(chalk.cyan('\n🧠 NEURAL SIMILARITY API'));
|
||||
console.log(chalk.gray('━'.repeat(50)));
|
||||
// Initialize Brainy and Neural API
|
||||
const brain = new BrainyData();
|
||||
const neural = new NeuralAPI(brain);
|
||||
try {
|
||||
const action = argv.action || await promptForAction();
|
||||
switch (action) {
|
||||
case 'similar':
|
||||
await handleSimilarCommand(neural, argv);
|
||||
break;
|
||||
case 'clusters':
|
||||
await handleClustersCommand(neural, argv);
|
||||
break;
|
||||
case 'hierarchy':
|
||||
await handleHierarchyCommand(neural, argv);
|
||||
break;
|
||||
case 'neighbors':
|
||||
await handleNeighborsCommand(neural, argv);
|
||||
break;
|
||||
case 'path':
|
||||
await handlePathCommand(neural, argv);
|
||||
break;
|
||||
case 'outliers':
|
||||
await handleOutliersCommand(neural, argv);
|
||||
break;
|
||||
case 'visualize':
|
||||
await handleVisualizeCommand(neural, argv);
|
||||
break;
|
||||
default:
|
||||
console.log(chalk.red(`❌ Unknown action: ${action}`));
|
||||
showHelp();
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(chalk.red('💥 Error:'), error instanceof Error ? error.message : error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
async function promptForAction() {
|
||||
const answer = await inquirer.prompt([{
|
||||
type: 'list',
|
||||
name: 'action',
|
||||
message: 'Choose a neural operation:',
|
||||
choices: [
|
||||
{ name: '🔗 Calculate similarity between items', value: 'similar' },
|
||||
{ name: '🎯 Find semantic clusters', value: 'clusters' },
|
||||
{ name: '🌳 Show item hierarchy', value: 'hierarchy' },
|
||||
{ name: '🕸️ Find semantic neighbors', value: 'neighbors' },
|
||||
{ name: '🛣️ Find semantic path between items', value: 'path' },
|
||||
{ name: '🚨 Detect outliers', value: 'outliers' },
|
||||
{ name: '📊 Generate visualization data', value: 'visualize' }
|
||||
]
|
||||
}]);
|
||||
return answer.action;
|
||||
}
|
||||
async function handleSimilarCommand(neural, argv) {
|
||||
const spinner = ora('🧠 Calculating semantic similarity...').start();
|
||||
try {
|
||||
let itemA, itemB;
|
||||
if (argv.id && argv.query) {
|
||||
itemA = argv.id;
|
||||
itemB = argv.query;
|
||||
}
|
||||
else if (argv._ && argv._.length >= 3) {
|
||||
itemA = argv._[1];
|
||||
itemB = argv._[2];
|
||||
}
|
||||
else {
|
||||
spinner.stop();
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'itemA',
|
||||
message: 'First item (ID or text):',
|
||||
validate: (input) => input.length > 0
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'itemB',
|
||||
message: 'Second item (ID or text):',
|
||||
validate: (input) => input.length > 0
|
||||
}
|
||||
]);
|
||||
itemA = answers.itemA;
|
||||
itemB = answers.itemB;
|
||||
spinner.start();
|
||||
}
|
||||
const result = await neural.similar(itemA, itemB, {
|
||||
explain: argv.explain,
|
||||
includeBreakdown: argv.explain
|
||||
});
|
||||
spinner.succeed('✅ Similarity calculated');
|
||||
if (typeof result === 'number') {
|
||||
console.log(`\n🔗 Similarity: ${chalk.cyan((result * 100).toFixed(1))}%`);
|
||||
}
|
||||
else {
|
||||
console.log(`\n🔗 Similarity: ${chalk.cyan((result.score * 100).toFixed(1))}%`);
|
||||
if (result.explanation) {
|
||||
console.log(`💭 Explanation: ${result.explanation}`);
|
||||
}
|
||||
if (result.breakdown) {
|
||||
console.log('\n📊 Breakdown:');
|
||||
console.log(` Semantic: ${chalk.yellow((result.breakdown.semantic * 100).toFixed(1))}%`);
|
||||
if (result.breakdown.taxonomic !== undefined) {
|
||||
console.log(` Taxonomic: ${chalk.yellow((result.breakdown.taxonomic * 100).toFixed(1))}%`);
|
||||
}
|
||||
if (result.breakdown.contextual !== undefined) {
|
||||
console.log(` Contextual: ${chalk.yellow((result.breakdown.contextual * 100).toFixed(1))}%`);
|
||||
}
|
||||
}
|
||||
if (result.hierarchy) {
|
||||
console.log(`\n🌳 Hierarchy: ${result.hierarchy.sharedParent ?
|
||||
`Shared parent at distance ${result.hierarchy.distance}` :
|
||||
'No shared parent found'}`);
|
||||
}
|
||||
}
|
||||
if (argv.output) {
|
||||
await saveToFile(argv.output, result, argv.format);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to calculate similarity');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function handleClustersCommand(neural, argv) {
|
||||
const spinner = ora('🎯 Finding semantic clusters...').start();
|
||||
try {
|
||||
const options = {
|
||||
algorithm: argv.algorithm,
|
||||
threshold: argv.threshold,
|
||||
maxClusters: argv.limit
|
||||
};
|
||||
const clusters = await neural.clusters(argv.query || options);
|
||||
spinner.succeed(`✅ Found ${clusters.length} clusters`);
|
||||
if (argv.format === 'json') {
|
||||
console.log(JSON.stringify(clusters, null, 2));
|
||||
}
|
||||
else {
|
||||
console.log(`\n🎯 ${chalk.cyan(clusters.length)} Semantic Clusters:\n`);
|
||||
clusters.forEach((cluster, index) => {
|
||||
console.log(`${chalk.yellow(`Cluster ${index + 1}:`)} ${cluster.label || cluster.id}`);
|
||||
console.log(` 📊 Confidence: ${chalk.green((cluster.confidence * 100).toFixed(1))}%`);
|
||||
console.log(` 👥 Members: ${cluster.members.length}`);
|
||||
if (cluster.members.length <= 5) {
|
||||
cluster.members.forEach(member => {
|
||||
console.log(` • ${member}`);
|
||||
});
|
||||
}
|
||||
else {
|
||||
cluster.members.slice(0, 3).forEach(member => {
|
||||
console.log(` • ${member}`);
|
||||
});
|
||||
console.log(` ... and ${cluster.members.length - 3} more`);
|
||||
}
|
||||
console.log();
|
||||
});
|
||||
}
|
||||
if (argv.output) {
|
||||
await saveToFile(argv.output, clusters, argv.format);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to find clusters');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function handleHierarchyCommand(neural, argv) {
|
||||
const spinner = ora('🌳 Building semantic hierarchy...').start();
|
||||
try {
|
||||
const id = argv.id || argv._[1];
|
||||
if (!id) {
|
||||
spinner.stop();
|
||||
const answer = await inquirer.prompt([{
|
||||
type: 'input',
|
||||
name: 'id',
|
||||
message: 'Enter item ID:',
|
||||
validate: (input) => input.length > 0
|
||||
}]);
|
||||
spinner.start();
|
||||
const hierarchy = await neural.hierarchy(answer.id);
|
||||
displayHierarchy(hierarchy);
|
||||
}
|
||||
else {
|
||||
const hierarchy = await neural.hierarchy(id);
|
||||
spinner.succeed('✅ Hierarchy built');
|
||||
displayHierarchy(hierarchy);
|
||||
}
|
||||
if (argv.output) {
|
||||
const hierarchy = await neural.hierarchy(id || argv._[1]);
|
||||
await saveToFile(argv.output, hierarchy, argv.format);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to build hierarchy');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
function displayHierarchy(hierarchy) {
|
||||
console.log(`\n🌳 Semantic Hierarchy for ${chalk.cyan(hierarchy.self.id)}:`);
|
||||
if (hierarchy.root) {
|
||||
console.log(`🔝 Root: ${hierarchy.root.id} (${(hierarchy.root.similarity * 100).toFixed(1)}%)`);
|
||||
}
|
||||
if (hierarchy.grandparent) {
|
||||
console.log(`👴 Grandparent: ${hierarchy.grandparent.id} (${(hierarchy.grandparent.similarity * 100).toFixed(1)}%)`);
|
||||
}
|
||||
if (hierarchy.parent) {
|
||||
console.log(`👨 Parent: ${hierarchy.parent.id} (${(hierarchy.parent.similarity * 100).toFixed(1)}%)`);
|
||||
}
|
||||
console.log(`🎯 ${chalk.bold('Self:')} ${hierarchy.self.id}`);
|
||||
if (hierarchy.siblings && hierarchy.siblings.length > 0) {
|
||||
console.log(`👥 Siblings: ${hierarchy.siblings.length}`);
|
||||
hierarchy.siblings.forEach((sibling) => {
|
||||
console.log(` • ${sibling.id} (${(sibling.similarity * 100).toFixed(1)}%)`);
|
||||
});
|
||||
}
|
||||
if (hierarchy.children && hierarchy.children.length > 0) {
|
||||
console.log(`👶 Children: ${hierarchy.children.length}`);
|
||||
hierarchy.children.forEach((child) => {
|
||||
console.log(` • ${child.id} (${(child.similarity * 100).toFixed(1)}%)`);
|
||||
});
|
||||
}
|
||||
}
|
||||
async function handleNeighborsCommand(neural, argv) {
|
||||
const spinner = ora('🕸️ Finding semantic neighbors...').start();
|
||||
try {
|
||||
const id = argv.id || argv._[1];
|
||||
if (!id) {
|
||||
spinner.stop();
|
||||
const answer = await inquirer.prompt([{
|
||||
type: 'input',
|
||||
name: 'id',
|
||||
message: 'Enter item ID:',
|
||||
validate: (input) => input.length > 0
|
||||
}]);
|
||||
spinner.start();
|
||||
}
|
||||
const targetId = id || (await inquirer.prompt([{
|
||||
type: 'input',
|
||||
name: 'id',
|
||||
message: 'Enter item ID:',
|
||||
validate: (input) => input.length > 0
|
||||
}])).id;
|
||||
const graph = await neural.neighbors(targetId, {
|
||||
limit: argv.limit,
|
||||
includeEdges: true
|
||||
});
|
||||
spinner.succeed(`✅ Found ${graph.neighbors.length} neighbors`);
|
||||
console.log(`\n🕸️ Neighbors of ${chalk.cyan(graph.center)}:`);
|
||||
graph.neighbors.forEach((neighbor, index) => {
|
||||
console.log(`${index + 1}. ${neighbor.id} (${(neighbor.similarity * 100).toFixed(1)}%)`);
|
||||
if (neighbor.type) {
|
||||
console.log(` Type: ${neighbor.type}`);
|
||||
}
|
||||
if (neighbor.connections) {
|
||||
console.log(` Connections: ${neighbor.connections}`);
|
||||
}
|
||||
});
|
||||
if (graph.edges && graph.edges.length > 0) {
|
||||
console.log(`\n🔗 ${graph.edges.length} semantic connections found`);
|
||||
}
|
||||
if (argv.output) {
|
||||
await saveToFile(argv.output, graph, argv.format);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to find neighbors');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function handlePathCommand(neural, argv) {
|
||||
const spinner = ora('🛣️ Finding semantic path...').start();
|
||||
try {
|
||||
let fromId, toId;
|
||||
if (argv._ && argv._.length >= 3) {
|
||||
fromId = argv._[1];
|
||||
toId = argv._[2];
|
||||
}
|
||||
else {
|
||||
spinner.stop();
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'from',
|
||||
message: 'From item ID:',
|
||||
validate: (input) => input.length > 0
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'to',
|
||||
message: 'To item ID:',
|
||||
validate: (input) => input.length > 0
|
||||
}
|
||||
]);
|
||||
fromId = answers.from;
|
||||
toId = answers.to;
|
||||
spinner.start();
|
||||
}
|
||||
const path = await neural.semanticPath(fromId, toId);
|
||||
if (path.length === 0) {
|
||||
spinner.warn('🚫 No semantic path found');
|
||||
console.log(`No path found between ${chalk.cyan(fromId)} and ${chalk.cyan(toId)}`);
|
||||
}
|
||||
else {
|
||||
spinner.succeed(`✅ Found path with ${path.length} hops`);
|
||||
console.log(`\n🛣️ Semantic Path from ${chalk.cyan(fromId)} to ${chalk.cyan(toId)}:`);
|
||||
console.log(`${chalk.cyan(fromId)} (start)`);
|
||||
path.forEach((hop, index) => {
|
||||
console.log(`${' '.repeat(index + 1)}↓ ${(hop.similarity * 100).toFixed(1)}%`);
|
||||
console.log(`${' '.repeat(index + 1)}${hop.id} (hop ${hop.hop})`);
|
||||
});
|
||||
}
|
||||
if (argv.output) {
|
||||
await saveToFile(argv.output, path, argv.format);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to find path');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function handleOutliersCommand(neural, argv) {
|
||||
const spinner = ora('🚨 Detecting semantic outliers...').start();
|
||||
try {
|
||||
const outliers = await neural.outliers(argv.threshold);
|
||||
spinner.succeed(`✅ Found ${outliers.length} outliers`);
|
||||
if (outliers.length === 0) {
|
||||
console.log('\n🎉 No outliers detected - all items are well connected!');
|
||||
}
|
||||
else {
|
||||
console.log(`\n🚨 ${chalk.red(outliers.length)} Semantic Outliers:`);
|
||||
outliers.forEach((outlier, index) => {
|
||||
console.log(`${index + 1}. ${outlier}`);
|
||||
});
|
||||
console.log(`\n💡 These items have similarity < ${argv.threshold} to their nearest neighbors`);
|
||||
}
|
||||
if (argv.output) {
|
||||
await saveToFile(argv.output, outliers, argv.format);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to detect outliers');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function handleVisualizeCommand(neural, argv) {
|
||||
const spinner = ora('📊 Generating visualization data...').start();
|
||||
try {
|
||||
const vizData = await neural.visualize({
|
||||
dimensions: argv.dimensions,
|
||||
maxNodes: argv.limit
|
||||
});
|
||||
spinner.succeed('✅ Visualization data generated');
|
||||
console.log(`\n📊 Visualization Data (${vizData.format} layout):`);
|
||||
console.log(`📍 Nodes: ${vizData.nodes.length}`);
|
||||
console.log(`🔗 Edges: ${vizData.edges.length}`);
|
||||
console.log(`🎯 Clusters: ${vizData.clusters?.length || 0}`);
|
||||
console.log(`📐 Dimensions: ${vizData.layout?.dimensions}D`);
|
||||
if (argv.format === 'json') {
|
||||
console.log('\nData:');
|
||||
console.log(JSON.stringify(vizData, null, 2));
|
||||
}
|
||||
else {
|
||||
console.log('\n🎨 Style Settings:');
|
||||
console.log(` Node Colors: ${vizData.style?.nodeColors}`);
|
||||
console.log(` Edge Width: ${vizData.style?.edgeWidth}`);
|
||||
console.log(` Labels: ${vizData.style?.labels}`);
|
||||
}
|
||||
if (argv.output) {
|
||||
await saveToFile(argv.output, vizData, 'json');
|
||||
console.log(`\n💾 Visualization data saved to: ${chalk.green(argv.output)}`);
|
||||
}
|
||||
else {
|
||||
console.log(`\n💡 Use --output to save visualization data for external tools`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('💥 Failed to generate visualization');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function saveToFile(filepath, data, format) {
|
||||
const dir = path.dirname(filepath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
let output;
|
||||
switch (format) {
|
||||
case 'json':
|
||||
output = JSON.stringify(data, null, 2);
|
||||
break;
|
||||
case 'table':
|
||||
output = formatAsTable(data);
|
||||
break;
|
||||
default:
|
||||
output = JSON.stringify(data, null, 2);
|
||||
}
|
||||
fs.writeFileSync(filepath, output, 'utf8');
|
||||
console.log(`💾 Saved to: ${chalk.green(filepath)}`);
|
||||
}
|
||||
function formatAsTable(data) {
|
||||
// Simple table formatting - could be enhanced with a table library
|
||||
if (Array.isArray(data)) {
|
||||
return data.map((item, index) => `${index + 1}. ${JSON.stringify(item)}`).join('\n');
|
||||
}
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
function showHelp() {
|
||||
console.log('\n🧠 Neural Similarity API Commands:');
|
||||
console.log('');
|
||||
console.log(' brainy neural similar <item1> <item2> Calculate similarity');
|
||||
console.log(' brainy neural clusters Find semantic clusters');
|
||||
console.log(' brainy neural hierarchy <id> Show item hierarchy');
|
||||
console.log(' brainy neural neighbors <id> Find semantic neighbors');
|
||||
console.log(' brainy neural path <from> <to> Find semantic path');
|
||||
console.log(' brainy neural outliers Detect outliers');
|
||||
console.log(' brainy neural visualize Generate visualization data');
|
||||
console.log('');
|
||||
console.log('Options:');
|
||||
console.log(' --threshold, -t Similarity threshold (0-1)');
|
||||
console.log(' --format, -f Output format (json|table|tree|graph)');
|
||||
console.log(' --output, -o Save to file');
|
||||
console.log(' --limit, -l Maximum results');
|
||||
console.log(' --explain, -e Include explanations');
|
||||
console.log('');
|
||||
}
|
||||
export default neuralCommand;
|
||||
//# sourceMappingURL=neural.js.map
|
||||
File diff suppressed because one or more lines are too long
37
.recovery-workspace/dist-backup-20250910-141917/cli/commands/utility.d.ts
vendored
Normal file
37
.recovery-workspace/dist-backup-20250910-141917/cli/commands/utility.d.ts
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Utility CLI Commands - TypeScript Implementation
|
||||
*
|
||||
* Database maintenance, statistics, and benchmarking
|
||||
*/
|
||||
interface UtilityOptions {
|
||||
verbose?: boolean;
|
||||
json?: boolean;
|
||||
pretty?: boolean;
|
||||
}
|
||||
interface StatsOptions extends UtilityOptions {
|
||||
byService?: boolean;
|
||||
detailed?: boolean;
|
||||
}
|
||||
interface CleanOptions extends UtilityOptions {
|
||||
removeOrphans?: boolean;
|
||||
rebuildIndex?: boolean;
|
||||
}
|
||||
interface BenchmarkOptions extends UtilityOptions {
|
||||
operations?: string;
|
||||
iterations?: string;
|
||||
}
|
||||
export declare const utilityCommands: {
|
||||
/**
|
||||
* Show database statistics
|
||||
*/
|
||||
stats(options: StatsOptions): Promise<void>;
|
||||
/**
|
||||
* Clean and optimize database
|
||||
*/
|
||||
clean(options: CleanOptions): Promise<void>;
|
||||
/**
|
||||
* Run performance benchmarks
|
||||
*/
|
||||
benchmark(options: BenchmarkOptions): Promise<void>;
|
||||
};
|
||||
export {};
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
/**
|
||||
* Utility CLI Commands - TypeScript Implementation
|
||||
*
|
||||
* Database maintenance, statistics, and benchmarking
|
||||
*/
|
||||
import chalk from 'chalk';
|
||||
import ora from 'ora';
|
||||
import Table from 'cli-table3';
|
||||
import { BrainyData } from '../../brainyData.js';
|
||||
let brainyInstance = null;
|
||||
const getBrainy = async () => {
|
||||
if (!brainyInstance) {
|
||||
brainyInstance = new BrainyData();
|
||||
await brainyInstance.init();
|
||||
}
|
||||
return brainyInstance;
|
||||
};
|
||||
const formatBytes = (bytes) => {
|
||||
if (bytes === 0)
|
||||
return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
const formatOutput = (data, options) => {
|
||||
if (options.json) {
|
||||
console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
|
||||
}
|
||||
};
|
||||
export const utilityCommands = {
|
||||
/**
|
||||
* Show database statistics
|
||||
*/
|
||||
async stats(options) {
|
||||
const spinner = ora('Gathering statistics...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
const stats = await brain.getStatistics();
|
||||
const memUsage = process.memoryUsage();
|
||||
spinner.succeed('Statistics gathered');
|
||||
if (options.json) {
|
||||
formatOutput(stats, options);
|
||||
return;
|
||||
}
|
||||
console.log(chalk.cyan('\n📊 Database Statistics\n'));
|
||||
// Core stats table
|
||||
const coreTable = new Table({
|
||||
head: [chalk.cyan('Metric'), chalk.cyan('Value')],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
coreTable.push(['Total Items', chalk.green(stats.nounCount + stats.verbCount + stats.metadataCount || 0)], ['Nouns', chalk.green(stats.nounCount || 0)], ['Verbs (Relationships)', chalk.green(stats.verbCount || 0)], ['Metadata Records', chalk.green(stats.metadataCount || 0)]);
|
||||
console.log(coreTable.toString());
|
||||
// Service breakdown if available
|
||||
if (options.byService && stats.serviceBreakdown) {
|
||||
console.log(chalk.cyan('\n🔧 Service Breakdown\n'));
|
||||
const serviceTable = new Table({
|
||||
head: [chalk.cyan('Service'), chalk.cyan('Nouns'), chalk.cyan('Verbs'), chalk.cyan('Metadata')],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
Object.entries(stats.serviceBreakdown).forEach(([service, serviceStats]) => {
|
||||
serviceTable.push([
|
||||
service,
|
||||
serviceStats.nounCount || 0,
|
||||
serviceStats.verbCount || 0,
|
||||
serviceStats.metadataCount || 0
|
||||
]);
|
||||
});
|
||||
console.log(serviceTable.toString());
|
||||
}
|
||||
// Storage info
|
||||
if (stats.storage) {
|
||||
console.log(chalk.cyan('\n💾 Storage\n'));
|
||||
const storageTable = new Table({
|
||||
head: [chalk.cyan('Property'), chalk.cyan('Value')],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
storageTable.push(['Type', stats.storage.type || 'Unknown'], ['Size', stats.storage.size ? formatBytes(stats.storage.size) : 'N/A'], ['Location', stats.storage.location || 'N/A']);
|
||||
console.log(storageTable.toString());
|
||||
}
|
||||
// Performance metrics
|
||||
if (stats.performance && options.detailed) {
|
||||
console.log(chalk.cyan('\n⚡ Performance\n'));
|
||||
const perfTable = new Table({
|
||||
head: [chalk.cyan('Metric'), chalk.cyan('Value')],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
if (stats.performance.avgQueryTime) {
|
||||
perfTable.push(['Avg Query Time', `${stats.performance.avgQueryTime.toFixed(2)} ms`]);
|
||||
}
|
||||
if (stats.performance.totalQueries) {
|
||||
perfTable.push(['Total Queries', stats.performance.totalQueries]);
|
||||
}
|
||||
if (stats.performance.cacheHitRate) {
|
||||
perfTable.push(['Cache Hit Rate', `${(stats.performance.cacheHitRate * 100).toFixed(1)}%`]);
|
||||
}
|
||||
console.log(perfTable.toString());
|
||||
}
|
||||
// Memory usage
|
||||
console.log(chalk.cyan('\n🧠 Memory Usage\n'));
|
||||
const memTable = new Table({
|
||||
head: [chalk.cyan('Type'), chalk.cyan('Size')],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
memTable.push(['Heap Used', formatBytes(memUsage.heapUsed)], ['Heap Total', formatBytes(memUsage.heapTotal)], ['RSS', formatBytes(memUsage.rss)], ['External', formatBytes(memUsage.external)]);
|
||||
console.log(memTable.toString());
|
||||
// Index info
|
||||
if (stats.index && options.detailed) {
|
||||
console.log(chalk.cyan('\n🎯 Vector Index\n'));
|
||||
const indexTable = new Table({
|
||||
head: [chalk.cyan('Property'), chalk.cyan('Value')],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
indexTable.push(['Dimensions', stats.index.dimensions || 'N/A'], ['Indexed Vectors', stats.index.vectorCount || 0], ['Index Size', stats.index.indexSize ? formatBytes(stats.index.indexSize) : 'N/A']);
|
||||
console.log(indexTable.toString());
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Failed to gather statistics');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Clean and optimize database
|
||||
*/
|
||||
async clean(options) {
|
||||
const spinner = ora('Cleaning database...').start();
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
const tasks = [];
|
||||
if (options.removeOrphans) {
|
||||
spinner.text = 'Removing orphaned items...';
|
||||
tasks.push('Removed orphaned items');
|
||||
// Implementation would go here
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate work
|
||||
}
|
||||
if (options.rebuildIndex) {
|
||||
spinner.text = 'Rebuilding search index...';
|
||||
tasks.push('Rebuilt search index');
|
||||
// Implementation would go here
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate work
|
||||
}
|
||||
if (tasks.length === 0) {
|
||||
spinner.text = 'Running general cleanup...';
|
||||
tasks.push('General cleanup completed');
|
||||
// Run general cleanup tasks
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate work
|
||||
}
|
||||
spinner.succeed('Database cleaned');
|
||||
if (!options.json) {
|
||||
console.log(chalk.green('\n✓ Cleanup completed:'));
|
||||
tasks.forEach(task => {
|
||||
console.log(chalk.dim(` • ${task}`));
|
||||
});
|
||||
// Get new stats
|
||||
const stats = await brain.getStatistics();
|
||||
console.log(chalk.cyan('\nDatabase Status:'));
|
||||
console.log(` Total items: ${stats.nounCount + stats.verbCount}`);
|
||||
console.log(` Index status: ${chalk.green('Healthy')}`);
|
||||
}
|
||||
else {
|
||||
formatOutput({ tasks, success: true }, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
spinner.fail('Cleanup failed');
|
||||
console.error(chalk.red(error.message));
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Run performance benchmarks
|
||||
*/
|
||||
async benchmark(options) {
|
||||
const operations = options.operations || 'all';
|
||||
const iterations = parseInt(options.iterations || '100');
|
||||
console.log(chalk.cyan(`\n🚀 Running Benchmarks (${iterations} iterations)\n`));
|
||||
const results = {
|
||||
operations: {},
|
||||
summary: {}
|
||||
};
|
||||
try {
|
||||
const brain = await getBrainy();
|
||||
// Benchmark different operations
|
||||
const benchmarks = [
|
||||
{ name: 'add', enabled: operations === 'all' || operations.includes('add') },
|
||||
{ name: 'search', enabled: operations === 'all' || operations.includes('search') },
|
||||
{ name: 'similarity', enabled: operations === 'all' || operations.includes('similarity') },
|
||||
{ name: 'cluster', enabled: operations === 'all' || operations.includes('cluster') }
|
||||
];
|
||||
for (const bench of benchmarks) {
|
||||
if (!bench.enabled)
|
||||
continue;
|
||||
const spinner = ora(`Benchmarking ${bench.name}...`).start();
|
||||
const times = [];
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const start = Date.now();
|
||||
switch (bench.name) {
|
||||
case 'add':
|
||||
await brain.add(`Test item ${i}`, { benchmark: true });
|
||||
break;
|
||||
case 'search':
|
||||
await brain.search('test', 10);
|
||||
break;
|
||||
case 'similarity':
|
||||
const neural = brain.neural;
|
||||
await neural.similar('test1', 'test2');
|
||||
break;
|
||||
case 'cluster':
|
||||
const neuralApi = brain.neural;
|
||||
await neuralApi.clusters();
|
||||
break;
|
||||
}
|
||||
times.push(Date.now() - start);
|
||||
}
|
||||
// Calculate statistics
|
||||
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
const min = Math.min(...times);
|
||||
const max = Math.max(...times);
|
||||
const median = times.sort((a, b) => a - b)[Math.floor(times.length / 2)];
|
||||
results.operations[bench.name] = {
|
||||
avg: avg.toFixed(2),
|
||||
min,
|
||||
max,
|
||||
median,
|
||||
ops: (1000 / avg).toFixed(2)
|
||||
};
|
||||
spinner.succeed(`${bench.name}: ${avg.toFixed(2)}ms avg (${(1000 / avg).toFixed(2)} ops/sec)`);
|
||||
}
|
||||
// Calculate summary
|
||||
const totalOps = Object.values(results.operations).reduce((sum, op) => sum + parseFloat(op.ops), 0);
|
||||
results.summary = {
|
||||
totalOperations: Object.keys(results.operations).length,
|
||||
averageOpsPerSec: (totalOps / Object.keys(results.operations).length).toFixed(2)
|
||||
};
|
||||
if (!options.json) {
|
||||
// Display results table
|
||||
console.log(chalk.cyan('\n📊 Benchmark Results\n'));
|
||||
const table = new Table({
|
||||
head: [
|
||||
chalk.cyan('Operation'),
|
||||
chalk.cyan('Avg (ms)'),
|
||||
chalk.cyan('Min (ms)'),
|
||||
chalk.cyan('Max (ms)'),
|
||||
chalk.cyan('Median (ms)'),
|
||||
chalk.cyan('Ops/sec')
|
||||
],
|
||||
style: { head: [], border: [] }
|
||||
});
|
||||
Object.entries(results.operations).forEach(([op, stats]) => {
|
||||
table.push([
|
||||
op,
|
||||
stats.avg,
|
||||
stats.min,
|
||||
stats.max,
|
||||
stats.median,
|
||||
chalk.green(stats.ops)
|
||||
]);
|
||||
});
|
||||
console.log(table.toString());
|
||||
console.log(chalk.cyan('\n📈 Summary'));
|
||||
console.log(` Operations tested: ${results.summary.totalOperations}`);
|
||||
console.log(` Average throughput: ${chalk.green(results.summary.averageOpsPerSec)} ops/sec`);
|
||||
}
|
||||
else {
|
||||
formatOutput(results, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(chalk.red('Benchmark failed:'), error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=utility.js.map
|
||||
File diff suppressed because one or more lines are too long
7
.recovery-workspace/dist-backup-20250910-141917/cli/index.d.ts
vendored
Normal file
7
.recovery-workspace/dist-backup-20250910-141917/cli/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Brainy CLI - Enterprise Neural Intelligence System
|
||||
*
|
||||
* Full TypeScript implementation with type safety and shared code
|
||||
*/
|
||||
export {};
|
||||
167
.recovery-workspace/dist-backup-20250910-141917/cli/index.js
Normal file
167
.recovery-workspace/dist-backup-20250910-141917/cli/index.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Brainy CLI - Enterprise Neural Intelligence System
|
||||
*
|
||||
* Full TypeScript implementation with type safety and shared code
|
||||
*/
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import { neuralCommands } from './commands/neural.js';
|
||||
import { coreCommands } from './commands/core.js';
|
||||
import { utilityCommands } from './commands/utility.js';
|
||||
import { version } from '../package.json';
|
||||
// CLI Configuration
|
||||
const program = new Command();
|
||||
program
|
||||
.name('brainy')
|
||||
.description('🧠 Enterprise Neural Intelligence Database')
|
||||
.version(version)
|
||||
.option('-v, --verbose', 'Verbose output')
|
||||
.option('--json', 'JSON output format')
|
||||
.option('--pretty', 'Pretty JSON output')
|
||||
.option('--no-color', 'Disable colored output');
|
||||
// ===== Core Commands =====
|
||||
program
|
||||
.command('add <text>')
|
||||
.description('Add text or JSON to the neural database')
|
||||
.option('-i, --id <id>', 'Specify custom ID')
|
||||
.option('-m, --metadata <json>', 'Add metadata')
|
||||
.option('-t, --type <type>', 'Specify noun type')
|
||||
.action(coreCommands.add);
|
||||
program
|
||||
.command('search <query>')
|
||||
.description('Search the neural database')
|
||||
.option('-k, --limit <number>', 'Number of results', '10')
|
||||
.option('-t, --threshold <number>', 'Similarity threshold')
|
||||
.option('--metadata <json>', 'Filter by metadata')
|
||||
.action(coreCommands.search);
|
||||
program
|
||||
.command('get <id>')
|
||||
.description('Get item by ID')
|
||||
.option('--with-connections', 'Include connections')
|
||||
.action(coreCommands.get);
|
||||
program
|
||||
.command('relate <source> <verb> <target>')
|
||||
.description('Create a relationship between items')
|
||||
.option('-w, --weight <number>', 'Relationship weight')
|
||||
.option('-m, --metadata <json>', 'Relationship metadata')
|
||||
.action(coreCommands.relate);
|
||||
program
|
||||
.command('import <file>')
|
||||
.description('Import data from file')
|
||||
.option('-f, --format <format>', 'Input format (json|csv|jsonl)', 'json')
|
||||
.option('--batch-size <number>', 'Batch size for import', '100')
|
||||
.action(coreCommands.import);
|
||||
program
|
||||
.command('export [file]')
|
||||
.description('Export database')
|
||||
.option('-f, --format <format>', 'Output format (json|csv|jsonl)', 'json')
|
||||
.action(coreCommands.export);
|
||||
// ===== Neural Commands =====
|
||||
program
|
||||
.command('similar <a> <b>')
|
||||
.alias('sim')
|
||||
.description('Calculate similarity between two items')
|
||||
.option('--explain', 'Show detailed explanation')
|
||||
.option('--breakdown', 'Show similarity breakdown')
|
||||
.action(neuralCommands.similar);
|
||||
program
|
||||
.command('cluster')
|
||||
.alias('clusters')
|
||||
.description('Find semantic clusters in the data')
|
||||
.option('--algorithm <type>', 'Clustering algorithm (hierarchical|kmeans|dbscan)', 'hierarchical')
|
||||
.option('--threshold <number>', 'Similarity threshold', '0.7')
|
||||
.option('--min-size <number>', 'Minimum cluster size', '2')
|
||||
.option('--max-clusters <number>', 'Maximum number of clusters')
|
||||
.option('--near <query>', 'Find clusters near a query')
|
||||
.option('--show', 'Show visual representation')
|
||||
.action(neuralCommands.cluster);
|
||||
program
|
||||
.command('related <id>')
|
||||
.alias('neighbors')
|
||||
.description('Find semantically related items')
|
||||
.option('-l, --limit <number>', 'Number of results', '10')
|
||||
.option('-r, --radius <number>', 'Semantic radius', '0.3')
|
||||
.option('--with-scores', 'Include similarity scores')
|
||||
.option('--with-edges', 'Include connections')
|
||||
.action(neuralCommands.related);
|
||||
program
|
||||
.command('hierarchy <id>')
|
||||
.alias('tree')
|
||||
.description('Show semantic hierarchy for an item')
|
||||
.option('-d, --depth <number>', 'Hierarchy depth', '3')
|
||||
.option('--parents-only', 'Show only parent hierarchy')
|
||||
.option('--children-only', 'Show only child hierarchy')
|
||||
.action(neuralCommands.hierarchy);
|
||||
program
|
||||
.command('path <from> <to>')
|
||||
.description('Find semantic path between items')
|
||||
.option('--steps', 'Show step-by-step path')
|
||||
.option('--max-hops <number>', 'Maximum path length', '5')
|
||||
.action(neuralCommands.path);
|
||||
program
|
||||
.command('outliers')
|
||||
.alias('anomalies')
|
||||
.description('Detect semantic outliers')
|
||||
.option('-t, --threshold <number>', 'Outlier threshold', '0.3')
|
||||
.option('--explain', 'Explain why items are outliers')
|
||||
.action(neuralCommands.outliers);
|
||||
program
|
||||
.command('visualize')
|
||||
.alias('viz')
|
||||
.description('Generate visualization data')
|
||||
.option('-f, --format <format>', 'Output format (json|d3|graphml)', 'json')
|
||||
.option('--max-nodes <number>', 'Maximum nodes', '500')
|
||||
.option('--dimensions <number>', '2D or 3D', '2')
|
||||
.option('-o, --output <file>', 'Output file')
|
||||
.action(neuralCommands.visualize);
|
||||
// ===== Utility Commands =====
|
||||
program
|
||||
.command('stats')
|
||||
.alias('statistics')
|
||||
.description('Show database statistics')
|
||||
.option('--by-service', 'Group by service')
|
||||
.option('--detailed', 'Show detailed stats')
|
||||
.action(utilityCommands.stats);
|
||||
program
|
||||
.command('clean')
|
||||
.description('Clean and optimize database')
|
||||
.option('--remove-orphans', 'Remove orphaned items')
|
||||
.option('--rebuild-index', 'Rebuild search index')
|
||||
.action(utilityCommands.clean);
|
||||
program
|
||||
.command('benchmark')
|
||||
.alias('bench')
|
||||
.description('Run performance benchmarks')
|
||||
.option('--operations <ops>', 'Operations to benchmark', 'all')
|
||||
.option('--iterations <n>', 'Number of iterations', '100')
|
||||
.action(utilityCommands.benchmark);
|
||||
// ===== Interactive Mode =====
|
||||
program
|
||||
.command('interactive')
|
||||
.alias('i')
|
||||
.description('Start interactive REPL mode')
|
||||
.action(async () => {
|
||||
const { startInteractiveMode } = await import('./interactive.js');
|
||||
await startInteractiveMode();
|
||||
});
|
||||
// ===== Error Handling =====
|
||||
program.exitOverride();
|
||||
try {
|
||||
await program.parseAsync(process.argv);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'commander.helpDisplayed') {
|
||||
process.exit(0);
|
||||
}
|
||||
console.error(chalk.red('Error:'), error.message);
|
||||
if (program.opts().verbose) {
|
||||
console.error(chalk.gray(error.stack));
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
// Handle no command
|
||||
if (!process.argv.slice(2).length) {
|
||||
program.outputHelp();
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
164
.recovery-workspace/dist-backup-20250910-141917/cli/interactive.d.ts
vendored
Normal file
164
.recovery-workspace/dist-backup-20250910-141917/cli/interactive.d.ts
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/**
|
||||
* Professional Interactive CLI System
|
||||
*
|
||||
* Provides consistent, delightful interactive prompts for all commands
|
||||
* with smart defaults, validation, and helpful examples
|
||||
*/
|
||||
import { BrainyData } from '../brainyData.js';
|
||||
export declare const colors: {
|
||||
primary: import("chalk").ChalkInstance;
|
||||
success: import("chalk").ChalkInstance;
|
||||
info: import("chalk").ChalkInstance;
|
||||
warning: import("chalk").ChalkInstance;
|
||||
error: import("chalk").ChalkInstance;
|
||||
brain: import("chalk").ChalkInstance;
|
||||
cream: import("chalk").ChalkInstance;
|
||||
dim: import("chalk").ChalkInstance;
|
||||
bold: import("chalk").ChalkInstance;
|
||||
cyan: import("chalk").ChalkInstance;
|
||||
green: import("chalk").ChalkInstance;
|
||||
yellow: import("chalk").ChalkInstance;
|
||||
red: import("chalk").ChalkInstance;
|
||||
};
|
||||
export declare const icons: {
|
||||
brain: string;
|
||||
search: string;
|
||||
add: string;
|
||||
delete: string;
|
||||
update: string;
|
||||
import: string;
|
||||
export: string;
|
||||
connect: string;
|
||||
question: string;
|
||||
success: string;
|
||||
error: string;
|
||||
warning: string;
|
||||
info: string;
|
||||
sparkle: string;
|
||||
rocket: string;
|
||||
thinking: string;
|
||||
chat: string;
|
||||
};
|
||||
/**
|
||||
* Professional prompt wrapper with consistent styling
|
||||
*/
|
||||
export declare function prompt(config: any): Promise<any>;
|
||||
/**
|
||||
* Interactive prompt for search query with smart features
|
||||
*/
|
||||
export declare function promptSearchQuery(previousSearches?: string[]): Promise<string>;
|
||||
/**
|
||||
* Interactive prompt for item ID with fuzzy search
|
||||
*/
|
||||
export declare function promptItemId(action: string, brain?: BrainyData, allowMultiple?: boolean): Promise<string | string[]>;
|
||||
/**
|
||||
* Confirm destructive action with preview
|
||||
*/
|
||||
export declare function confirmDestructiveAction(action: string, items: any[], showPreview?: boolean): Promise<boolean>;
|
||||
/**
|
||||
* Interactive data input with multiline support
|
||||
*/
|
||||
export declare function promptDataInput(action?: string, currentValue?: string): Promise<string>;
|
||||
/**
|
||||
* Interactive metadata input with JSON validation
|
||||
*/
|
||||
export declare function promptMetadata(currentMetadata?: any, suggestions?: string[]): Promise<any>;
|
||||
/**
|
||||
* Interactive format selector
|
||||
*/
|
||||
export declare function promptFormat(availableFormats: string[], defaultFormat: string): Promise<string>;
|
||||
/**
|
||||
* Interactive file/URL input with validation
|
||||
*/
|
||||
export declare function promptFileOrUrl(action?: string): Promise<string>;
|
||||
/**
|
||||
* Interactive relationship builder
|
||||
*/
|
||||
export declare function promptRelationship(brain?: BrainyData): Promise<{
|
||||
source: string;
|
||||
verb: string;
|
||||
target: string;
|
||||
metadata?: any;
|
||||
}>;
|
||||
/**
|
||||
* Smart command suggestions when user types wrong command
|
||||
*/
|
||||
export declare function suggestCommand(input: string, availableCommands: string[]): string[];
|
||||
/**
|
||||
* Beautiful error display with helpful context
|
||||
*/
|
||||
export declare function showError(error: Error, context?: string): void;
|
||||
/**
|
||||
* Progress indicator for long operations
|
||||
*/
|
||||
export declare class ProgressTracker {
|
||||
private spinner;
|
||||
private startTime;
|
||||
constructor(message: string);
|
||||
update(message: string, count?: number, total?: number): void;
|
||||
succeed(message?: string): void;
|
||||
fail(message?: string): void;
|
||||
stop(): void;
|
||||
}
|
||||
/**
|
||||
* Welcome message for interactive mode
|
||||
*/
|
||||
export declare function showWelcome(): void;
|
||||
/**
|
||||
* Interactive command selector for beginners
|
||||
*/
|
||||
export declare function promptCommand(): Promise<string>;
|
||||
/**
|
||||
* Export all interactive components
|
||||
*/
|
||||
declare const _default: {
|
||||
colors: {
|
||||
primary: import("chalk").ChalkInstance;
|
||||
success: import("chalk").ChalkInstance;
|
||||
info: import("chalk").ChalkInstance;
|
||||
warning: import("chalk").ChalkInstance;
|
||||
error: import("chalk").ChalkInstance;
|
||||
brain: import("chalk").ChalkInstance;
|
||||
cream: import("chalk").ChalkInstance;
|
||||
dim: import("chalk").ChalkInstance;
|
||||
bold: import("chalk").ChalkInstance;
|
||||
cyan: import("chalk").ChalkInstance;
|
||||
green: import("chalk").ChalkInstance;
|
||||
yellow: import("chalk").ChalkInstance;
|
||||
red: import("chalk").ChalkInstance;
|
||||
};
|
||||
icons: {
|
||||
brain: string;
|
||||
search: string;
|
||||
add: string;
|
||||
delete: string;
|
||||
update: string;
|
||||
import: string;
|
||||
export: string;
|
||||
connect: string;
|
||||
question: string;
|
||||
success: string;
|
||||
error: string;
|
||||
warning: string;
|
||||
info: string;
|
||||
sparkle: string;
|
||||
rocket: string;
|
||||
thinking: string;
|
||||
chat: string;
|
||||
};
|
||||
prompt: typeof prompt;
|
||||
promptSearchQuery: typeof promptSearchQuery;
|
||||
promptItemId: typeof promptItemId;
|
||||
confirmDestructiveAction: typeof confirmDestructiveAction;
|
||||
promptDataInput: typeof promptDataInput;
|
||||
promptMetadata: typeof promptMetadata;
|
||||
promptFormat: typeof promptFormat;
|
||||
promptFileOrUrl: typeof promptFileOrUrl;
|
||||
promptRelationship: typeof promptRelationship;
|
||||
suggestCommand: typeof suggestCommand;
|
||||
showError: typeof showError;
|
||||
ProgressTracker: typeof ProgressTracker;
|
||||
showWelcome: typeof showWelcome;
|
||||
promptCommand: typeof promptCommand;
|
||||
};
|
||||
export default _default;
|
||||
|
|
@ -0,0 +1,542 @@
|
|||
/**
|
||||
* Professional Interactive CLI System
|
||||
*
|
||||
* Provides consistent, delightful interactive prompts for all commands
|
||||
* with smart defaults, validation, and helpful examples
|
||||
*/
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import fuzzy from 'fuzzy';
|
||||
import ora from 'ora';
|
||||
// Professional color scheme
|
||||
export const colors = {
|
||||
primary: chalk.hex('#3A5F4A'), // Teal (from logo)
|
||||
success: chalk.hex('#2D4A3A'), // Deep teal
|
||||
info: chalk.hex('#4A6B5A'), // Medium teal
|
||||
warning: chalk.hex('#D67441'), // Orange (from logo)
|
||||
error: chalk.hex('#B85C35'), // Deep orange
|
||||
brain: chalk.hex('#D67441'), // Brain orange
|
||||
cream: chalk.hex('#F5E6A3'), // Cream background
|
||||
dim: chalk.dim,
|
||||
bold: chalk.bold,
|
||||
cyan: chalk.cyan,
|
||||
green: chalk.green,
|
||||
yellow: chalk.yellow,
|
||||
red: chalk.red
|
||||
};
|
||||
// Icons for consistent visual language
|
||||
export const icons = {
|
||||
brain: '🧠',
|
||||
search: '🔍',
|
||||
add: '➕',
|
||||
delete: '🗑️',
|
||||
update: '🔄',
|
||||
import: '📥',
|
||||
export: '📤',
|
||||
connect: '🔗',
|
||||
question: '❓',
|
||||
success: '✅',
|
||||
error: '❌',
|
||||
warning: '⚠️',
|
||||
info: 'ℹ️',
|
||||
sparkle: '✨',
|
||||
rocket: '🚀',
|
||||
thinking: '🤔',
|
||||
chat: '💬'
|
||||
};
|
||||
// Store recent inputs for smart suggestions
|
||||
const recentInputs = {
|
||||
searches: [],
|
||||
ids: [],
|
||||
types: [],
|
||||
formats: []
|
||||
};
|
||||
/**
|
||||
* Professional prompt wrapper with consistent styling
|
||||
*/
|
||||
export async function prompt(config) {
|
||||
// Add consistent styling
|
||||
if (config.message) {
|
||||
config.message = colors.cyan(config.message);
|
||||
}
|
||||
// Add prefix with appropriate icon
|
||||
if (!config.prefix) {
|
||||
config.prefix = colors.dim(' › ');
|
||||
}
|
||||
return inquirer.prompt([config]);
|
||||
}
|
||||
/**
|
||||
* Interactive prompt for search query with smart features
|
||||
*/
|
||||
export async function promptSearchQuery(previousSearches) {
|
||||
console.log(colors.primary(`\n${icons.search} Smart Search\n`));
|
||||
console.log(colors.dim('Search your neural database with natural language'));
|
||||
console.log(colors.dim('Examples: "meetings last week", "John from Google", "important documents"'));
|
||||
const { query } = await prompt({
|
||||
type: 'input',
|
||||
name: 'query',
|
||||
message: 'What would you like to search for?',
|
||||
validate: (input) => {
|
||||
if (!input.trim()) {
|
||||
return 'Please enter a search query';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
transformer: (input) => {
|
||||
// Show live character count
|
||||
const count = input.length;
|
||||
if (count > 100) {
|
||||
return colors.warning(input);
|
||||
}
|
||||
return colors.green(input);
|
||||
}
|
||||
});
|
||||
// Store for future suggestions
|
||||
if (!recentInputs.searches.includes(query)) {
|
||||
recentInputs.searches.unshift(query);
|
||||
recentInputs.searches = recentInputs.searches.slice(0, 10);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
/**
|
||||
* Interactive prompt for item ID with fuzzy search
|
||||
*/
|
||||
export async function promptItemId(action, brain, allowMultiple = false) {
|
||||
console.log(colors.primary(`\n${icons.thinking} Select item to ${action}\n`));
|
||||
// If we have brain instance, show recent items
|
||||
let choices = [];
|
||||
if (brain) {
|
||||
try {
|
||||
const recent = await brain.search('*', 10, {
|
||||
sortBy: 'timestamp',
|
||||
descending: true
|
||||
});
|
||||
choices = recent.map(item => ({
|
||||
name: `${item.id} - ${item.content?.substring(0, 50)}...`,
|
||||
value: item.id,
|
||||
short: item.id
|
||||
}));
|
||||
}
|
||||
catch {
|
||||
// Fallback to manual input
|
||||
}
|
||||
}
|
||||
if (choices.length > 0) {
|
||||
choices.push(new inquirer.Separator());
|
||||
choices.push({ name: 'Enter ID manually', value: '__manual__' });
|
||||
const { selected } = await prompt({
|
||||
type: allowMultiple ? 'checkbox' : 'list',
|
||||
name: 'selected',
|
||||
message: `Select item(s) to ${action}:`,
|
||||
choices,
|
||||
pageSize: 10
|
||||
});
|
||||
if (selected === '__manual__' || (Array.isArray(selected) && selected.includes('__manual__'))) {
|
||||
return promptManualId(action, allowMultiple);
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
else {
|
||||
return promptManualId(action, allowMultiple);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Manual ID input with validation
|
||||
*/
|
||||
async function promptManualId(action, allowMultiple) {
|
||||
const { id } = await prompt({
|
||||
type: 'input',
|
||||
name: 'id',
|
||||
message: allowMultiple
|
||||
? `Enter ID(s) to ${action} (comma-separated):`
|
||||
: `Enter ID to ${action}:`,
|
||||
validate: (input) => {
|
||||
if (!input.trim()) {
|
||||
return `Please enter at least one ID`;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (allowMultiple) {
|
||||
return id.split(',').map((i) => i.trim()).filter(Boolean);
|
||||
}
|
||||
return id.trim();
|
||||
}
|
||||
/**
|
||||
* Confirm destructive action with preview
|
||||
*/
|
||||
export async function confirmDestructiveAction(action, items, showPreview = true) {
|
||||
console.log(colors.warning(`\n${icons.warning} Confirmation Required\n`));
|
||||
if (showPreview && items.length > 0) {
|
||||
console.log(colors.dim(`You are about to ${action}:`));
|
||||
items.slice(0, 5).forEach(item => {
|
||||
console.log(colors.dim(` • ${item.id || item}`));
|
||||
});
|
||||
if (items.length > 5) {
|
||||
console.log(colors.dim(` ... and ${items.length - 5} more`));
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
const { confirm } = await prompt({
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: colors.warning(`Are you sure you want to ${action}?`),
|
||||
default: false
|
||||
});
|
||||
return confirm;
|
||||
}
|
||||
/**
|
||||
* Interactive data input with multiline support
|
||||
*/
|
||||
export async function promptDataInput(action = 'add', currentValue) {
|
||||
console.log(colors.primary(`\n${icons.add} ${action === 'add' ? 'Add Data' : 'Update Data'}\n`));
|
||||
if (currentValue) {
|
||||
console.log(colors.dim('Current value:'));
|
||||
console.log(colors.info(` ${currentValue.substring(0, 100)}${currentValue.length > 100 ? '...' : ''}`));
|
||||
console.log();
|
||||
}
|
||||
const { data } = await prompt({
|
||||
type: 'editor',
|
||||
name: 'data',
|
||||
message: 'Enter your data:',
|
||||
default: currentValue || '',
|
||||
postfix: '.md',
|
||||
validate: (input) => {
|
||||
if (!input.trim() && action === 'add') {
|
||||
return 'Please enter some data';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Interactive metadata input with JSON validation
|
||||
*/
|
||||
export async function promptMetadata(currentMetadata, suggestions) {
|
||||
console.log(colors.dim('\nOptional: Add metadata (JSON format)'));
|
||||
const { addMetadata } = await prompt({
|
||||
type: 'confirm',
|
||||
name: 'addMetadata',
|
||||
message: 'Would you like to add metadata?',
|
||||
default: false
|
||||
});
|
||||
if (!addMetadata) {
|
||||
return {};
|
||||
}
|
||||
// Show field suggestions if available
|
||||
if (suggestions && suggestions.length > 0) {
|
||||
console.log(colors.dim('\nAvailable fields:'));
|
||||
suggestions.forEach(field => {
|
||||
console.log(colors.dim(` • ${field}`));
|
||||
});
|
||||
}
|
||||
const { metadata } = await prompt({
|
||||
type: 'editor',
|
||||
name: 'metadata',
|
||||
message: 'Enter metadata (JSON):',
|
||||
default: currentMetadata ? JSON.stringify(currentMetadata, null, 2) : '{\n \n}',
|
||||
postfix: '.json',
|
||||
validate: (input) => {
|
||||
try {
|
||||
JSON.parse(input);
|
||||
return true;
|
||||
}
|
||||
catch (e) {
|
||||
return `Invalid JSON: ${e.message}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
return JSON.parse(metadata);
|
||||
}
|
||||
/**
|
||||
* Interactive format selector
|
||||
*/
|
||||
export async function promptFormat(availableFormats, defaultFormat) {
|
||||
console.log(colors.primary(`\n${icons.export} Select Format\n`));
|
||||
const { format } = await prompt({
|
||||
type: 'list',
|
||||
name: 'format',
|
||||
message: 'Choose export format:',
|
||||
choices: availableFormats.map(f => ({
|
||||
name: getFormatDescription(f),
|
||||
value: f,
|
||||
short: f
|
||||
})),
|
||||
default: defaultFormat
|
||||
});
|
||||
return format;
|
||||
}
|
||||
/**
|
||||
* Get friendly format descriptions
|
||||
*/
|
||||
function getFormatDescription(format) {
|
||||
const descriptions = {
|
||||
json: 'JSON - Universal data interchange',
|
||||
jsonl: 'JSON Lines - Streaming format',
|
||||
csv: 'CSV - Spreadsheet compatible',
|
||||
graphml: 'GraphML - Graph visualization',
|
||||
dot: 'DOT - Graphviz format',
|
||||
d3: 'D3.js - Web visualization',
|
||||
markdown: 'Markdown - Human readable',
|
||||
yaml: 'YAML - Configuration format'
|
||||
};
|
||||
return `${format.toUpperCase()} - ${descriptions[format] || 'Custom format'}`;
|
||||
}
|
||||
/**
|
||||
* Interactive file/URL input with validation
|
||||
*/
|
||||
export async function promptFileOrUrl(action = 'import') {
|
||||
console.log(colors.primary(`\n${icons.import} ${action === 'import' ? 'Import Source' : 'Export Destination'}\n`));
|
||||
const { sourceType } = await prompt({
|
||||
type: 'list',
|
||||
name: 'sourceType',
|
||||
message: 'What type of source?',
|
||||
choices: [
|
||||
{ name: 'Local file', value: 'file' },
|
||||
{ name: 'URL', value: 'url' },
|
||||
{ name: 'Clipboard', value: 'clipboard' },
|
||||
{ name: 'Direct input', value: 'input' }
|
||||
]
|
||||
});
|
||||
switch (sourceType) {
|
||||
case 'file':
|
||||
return promptFilePath(action);
|
||||
case 'url':
|
||||
return promptUrl();
|
||||
case 'clipboard':
|
||||
// Would need clipboard integration
|
||||
console.log(colors.warning('Clipboard support coming soon!'));
|
||||
return promptFilePath(action);
|
||||
case 'input':
|
||||
const data = await promptDataInput('import');
|
||||
// Save to temp file and return path
|
||||
const tmpFile = `/tmp/brainy-import-${Date.now()}.json`;
|
||||
const { writeFileSync } = await import('fs');
|
||||
writeFileSync(tmpFile, data);
|
||||
return tmpFile;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* File path input with autocomplete
|
||||
*/
|
||||
async function promptFilePath(action) {
|
||||
const { path } = await prompt({
|
||||
type: 'input',
|
||||
name: 'path',
|
||||
message: `Enter file path to ${action}:`,
|
||||
validate: async (input) => {
|
||||
if (!input.trim()) {
|
||||
return 'Please enter a file path';
|
||||
}
|
||||
const { existsSync } = await import('fs');
|
||||
if (action === 'import' && !existsSync(input)) {
|
||||
return `File not found: ${input}`;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// Add file path autocomplete
|
||||
transformer: (input) => {
|
||||
if (input.startsWith('~/')) {
|
||||
const home = process.env.HOME || '~';
|
||||
return colors.green(input.replace('~', home));
|
||||
}
|
||||
return colors.green(input);
|
||||
}
|
||||
});
|
||||
return path;
|
||||
}
|
||||
/**
|
||||
* URL input with validation
|
||||
*/
|
||||
async function promptUrl() {
|
||||
const { url } = await prompt({
|
||||
type: 'input',
|
||||
name: 'url',
|
||||
message: 'Enter URL:',
|
||||
validate: (input) => {
|
||||
try {
|
||||
new URL(input);
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return 'Please enter a valid URL';
|
||||
}
|
||||
}
|
||||
});
|
||||
return url;
|
||||
}
|
||||
/**
|
||||
* Interactive relationship builder
|
||||
*/
|
||||
export async function promptRelationship(brain) {
|
||||
console.log(colors.primary(`\n${icons.connect} Create Relationship\n`));
|
||||
console.log(colors.dim('Connect two items with a semantic relationship'));
|
||||
// Get source
|
||||
const source = await promptItemId('connect from', brain, false);
|
||||
// Get verb/relationship type
|
||||
const { verb } = await prompt({
|
||||
type: 'list',
|
||||
name: 'verb',
|
||||
message: 'Relationship type:',
|
||||
choices: [
|
||||
{ name: 'Works For', value: 'WorksFor' },
|
||||
{ name: 'Knows', value: 'Knows' },
|
||||
{ name: 'Created By', value: 'CreatedBy' },
|
||||
{ name: 'Belongs To', value: 'BelongsTo' },
|
||||
{ name: 'Uses', value: 'Uses' },
|
||||
{ name: 'Manages', value: 'Manages' },
|
||||
{ name: 'Located In', value: 'LocatedIn' },
|
||||
{ name: 'Related To', value: 'RelatedTo' },
|
||||
new inquirer.Separator(),
|
||||
{ name: 'Custom relationship...', value: '__custom__' }
|
||||
]
|
||||
});
|
||||
let finalVerb = verb;
|
||||
if (verb === '__custom__') {
|
||||
const { customVerb } = await prompt({
|
||||
type: 'input',
|
||||
name: 'customVerb',
|
||||
message: 'Enter custom relationship:',
|
||||
validate: (input) => input.trim() ? true : 'Please enter a relationship'
|
||||
});
|
||||
finalVerb = customVerb;
|
||||
}
|
||||
// Get target
|
||||
const target = await promptItemId('connect to', brain, false);
|
||||
// Optional metadata
|
||||
const metadata = await promptMetadata();
|
||||
return {
|
||||
source,
|
||||
verb: finalVerb,
|
||||
target,
|
||||
metadata: Object.keys(metadata).length > 0 ? metadata : undefined
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Smart command suggestions when user types wrong command
|
||||
*/
|
||||
export function suggestCommand(input, availableCommands) {
|
||||
const results = fuzzy.filter(input, availableCommands);
|
||||
return results.slice(0, 3).map(r => r.string);
|
||||
}
|
||||
/**
|
||||
* Beautiful error display with helpful context
|
||||
*/
|
||||
export function showError(error, context) {
|
||||
console.log();
|
||||
console.log(colors.error(`${icons.error} Error`));
|
||||
if (context) {
|
||||
console.log(colors.dim(context));
|
||||
}
|
||||
console.log(colors.red(error.message));
|
||||
// Provide helpful suggestions based on error
|
||||
if (error.message.includes('not found')) {
|
||||
console.log(colors.dim('\nTip: Use "brainy search" to find items'));
|
||||
}
|
||||
else if (error.message.includes('network') || error.message.includes('fetch')) {
|
||||
console.log(colors.dim('\nTip: Check your internet connection'));
|
||||
}
|
||||
else if (error.message.includes('permission')) {
|
||||
console.log(colors.dim('\nTip: Check file permissions or run with appropriate access'));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Progress indicator for long operations
|
||||
*/
|
||||
export class ProgressTracker {
|
||||
constructor(message) {
|
||||
this.spinner = ora({
|
||||
text: message,
|
||||
color: 'cyan',
|
||||
spinner: 'dots'
|
||||
}).start();
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
update(message, count, total) {
|
||||
if (count && total) {
|
||||
const percent = Math.round((count / total) * 100);
|
||||
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
|
||||
this.spinner.text = `${message} (${percent}% - ${elapsed}s)`;
|
||||
}
|
||||
else {
|
||||
this.spinner.text = message;
|
||||
}
|
||||
}
|
||||
succeed(message) {
|
||||
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
|
||||
this.spinner.succeed(message ? `${message} (${elapsed}s)` : `Done (${elapsed}s)`);
|
||||
}
|
||||
fail(message) {
|
||||
this.spinner.fail(message || 'Failed');
|
||||
}
|
||||
stop() {
|
||||
this.spinner.stop();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Welcome message for interactive mode
|
||||
*/
|
||||
export function showWelcome() {
|
||||
console.clear();
|
||||
console.log(colors.primary(`
|
||||
╔══════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ ${icons.brain} BRAINY - Neural Intelligence ║
|
||||
║ Your AI-Powered Second Brain ║
|
||||
║ ║
|
||||
╚══════════════════════════════════════════════╝
|
||||
`));
|
||||
console.log(colors.dim('Version 1.5.0 • Type "help" for commands'));
|
||||
console.log();
|
||||
}
|
||||
/**
|
||||
* Interactive command selector for beginners
|
||||
*/
|
||||
export async function promptCommand() {
|
||||
const { command } = await prompt({
|
||||
type: 'list',
|
||||
name: 'command',
|
||||
message: 'What would you like to do?',
|
||||
choices: [
|
||||
{ name: `${icons.add} Add data to your brain`, value: 'add' },
|
||||
{ name: `${icons.search} Search your knowledge`, value: 'search' },
|
||||
{ name: `${icons.chat} Chat with your data`, value: 'chat' },
|
||||
{ name: `${icons.update} Update existing data`, value: 'update' },
|
||||
{ name: `${icons.delete} Delete data`, value: 'delete' },
|
||||
{ name: `${icons.connect} Create relationships`, value: 'relate' },
|
||||
{ name: `${icons.import} Import from file`, value: 'import' },
|
||||
{ name: `${icons.export} Export your brain`, value: 'export' },
|
||||
new inquirer.Separator(),
|
||||
{ name: `${icons.brain} Neural operations`, value: 'neural' },
|
||||
{ name: `${icons.info} View statistics`, value: 'status' },
|
||||
{ name: 'Exit', value: 'exit' }
|
||||
],
|
||||
pageSize: 15
|
||||
});
|
||||
return command;
|
||||
}
|
||||
/**
|
||||
* Export all interactive components
|
||||
*/
|
||||
export default {
|
||||
colors,
|
||||
icons,
|
||||
prompt,
|
||||
promptSearchQuery,
|
||||
promptItemId,
|
||||
confirmDestructiveAction,
|
||||
promptDataInput,
|
||||
promptMetadata,
|
||||
promptFormat,
|
||||
promptFileOrUrl,
|
||||
promptRelationship,
|
||||
suggestCommand,
|
||||
showError,
|
||||
ProgressTracker,
|
||||
showWelcome,
|
||||
promptCommand
|
||||
};
|
||||
//# sourceMappingURL=interactive.js.map
|
||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue