@lanonasis/cli
Version:
Professional CLI for LanOnasis Memory as a Service (MaaS) with MCP support, seamless inline editing, and enterprise-grade security
570 lines (569 loc) • 21.4 kB
JavaScript
/**
* Main Dashboard Command Center
* The central hub for all CLI operations after authentication
*/
import chalk from 'chalk';
import inquirer from 'inquirer';
import boxen from 'boxen';
import Table from 'cli-table3';
export class DashboardCommandCenter {
stateManager;
stats;
constructor(stateManager) {
this.stateManager = stateManager;
this.stats = this.loadStats();
}
async show() {
console.clear();
await this.render();
await this.handleUserInput();
}
async render() {
const userContext = this.stateManager.getUserContext();
const email = userContext.email || 'user@example.com';
// Header
console.log(this.renderHeader(email));
// Stats and Activity sections
console.log(this.renderStatsAndActivity());
// Main menu
console.log(this.renderMainMenu());
// Footer with shortcuts
console.log(this.renderFooter());
}
renderHeader(email) {
const header = `Onasis Command Center`;
const userInfo = `◐ ${email}`;
const padding = 72 - header.length - userInfo.length - 4;
return boxen(chalk.bold(header) + ' '.repeat(padding) + chalk.dim(userInfo), {
borderStyle: 'round',
borderColor: 'cyan',
padding: 0
});
}
renderStatsAndActivity() {
const statsBox = this.renderQuickStats();
const activityBox = this.renderRecentActivity();
// Create side-by-side layout
const statsLines = statsBox.split('\n');
const activityLines = activityBox.split('\n');
const maxLines = Math.max(statsLines.length, activityLines.length);
let combined = '';
for (let i = 0; i < maxLines; i++) {
const statLine = (statsLines[i] || '').padEnd(35);
const activityLine = activityLines[i] || '';
combined += ` ${statLine} ${activityLine}\n`;
}
return combined;
}
renderQuickStats() {
const stats = [
`├─ ${chalk.bold(this.stats.totalMemories)} Memories`,
`├─ ${chalk.bold(this.stats.totalTopics)} Topics`,
`└─ ${chalk.bold(this.stats.apiKeys)} API Keys`
];
return chalk.blue('📊 Quick Stats\n') + stats.join('\n');
}
renderRecentActivity() {
const activities = this.stats.recentActivity.slice(0, 3).map((activity, index) => {
const isLast = index === this.stats.recentActivity.slice(0, 3).length - 1;
const prefix = isLast ? '└─' : '├─';
return `${prefix} "${activity.target}" ${chalk.dim(`(${activity.timestamp})`)}`;
});
return chalk.green('🎯 Recent Activity\n') + activities.join('\n');
}
renderMainMenu() {
const menuItems = [
['1. 📝 Create Memory', '4. 🔍 Search Everything'],
['2. 📁 Browse Topics', '5. 🔑 Manage API Keys'],
['3. 📊 View Analytics', '6. ⚙️ Settings']
];
const menuContent = chalk.bold('What would you like to do?\n\n') +
menuItems.map(row => ` ${row[0]} ${row[1]}`).join('\n') +
'\n\n' +
chalk.dim('Type a number, command, or describe what you need...');
return boxen(menuContent, {
padding: 1,
borderStyle: 'single',
borderColor: 'gray'
});
}
renderFooter() {
return chalk.dim(' [↵] Smart Command [/] Search [Tab] Complete [?] Help');
}
async handleUserInput() {
const { input } = await inquirer.prompt({
type: 'input',
name: 'input',
message: chalk.green('>'),
transformer: (input) => {
// Real-time input transformation/hints
if (input.startsWith('/')) {
return chalk.cyan(input) + chalk.dim(' (search mode)');
}
return input;
}
});
await this.processInput(input);
}
async processInput(input) {
// Smart command parsing
const normalized = input.toLowerCase().trim();
// Check for numbered options
if (['1', '2', '3', '4', '5', '6'].includes(normalized)) {
await this.handleNumberedOption(normalized);
return;
}
// Check for natural language commands
if (normalized.includes('create') || normalized.includes('new')) {
await this.createMemory();
}
else if (normalized.includes('search') || normalized.startsWith('/')) {
await this.searchMemories(input.replace('/', ''));
}
else if (normalized.includes('browse') || normalized.includes('topics')) {
await this.browseTopics();
}
else if (normalized.includes('analytics') || normalized.includes('stats')) {
await this.viewAnalytics();
}
else if (normalized.includes('api') || normalized.includes('keys')) {
await this.manageApiKeys();
}
else if (normalized.includes('settings') || normalized.includes('config')) {
await this.openSettings();
}
else if (normalized === '?' || normalized === 'help') {
await this.showHelp();
}
else {
// Try to interpret as a smart command
await this.interpretSmartCommand(input);
}
}
async handleNumberedOption(option) {
switch (option) {
case '1':
await this.createMemory();
break;
case '2':
await this.browseTopics();
break;
case '3':
await this.viewAnalytics();
break;
case '4':
await this.searchMemories('');
break;
case '5':
await this.manageApiKeys();
break;
case '6':
await this.openSettings();
break;
}
}
async createMemory() {
const creator = new InteractiveMemoryCreator(this.stateManager);
await creator.create();
}
async searchMemories(_query) {
const search = new InteractiveSearch(this.stateManager);
await search.search(_query);
}
async browseTopics() {
console.log(chalk.yellow('Browse Topics - Coming soon...'));
}
async viewAnalytics() {
const analytics = new AnalyticsView(this.stateManager);
await analytics.show();
}
async manageApiKeys() {
console.log(chalk.yellow('API Key Management - Coming soon...'));
}
async openSettings() {
console.log(chalk.yellow('Settings - Coming soon...'));
}
async showHelp() {
console.log(boxen(chalk.bold('📚 Help & Commands\n\n') +
chalk.cyan('Creating Memories:\n') +
' create, new, add - Start creating a new memory\n' +
' "remember that..." - Natural language memory creation\n\n' +
chalk.cyan('Searching:\n') +
' search, find, / - Search your memories\n' +
' /keyword - Quick search for keyword\n\n' +
chalk.cyan('Navigation:\n') +
' 1-6 - Select numbered menu options\n' +
' back, exit - Return to previous screen\n' +
' clear - Clear the screen\n\n' +
chalk.dim('Pro tip: Use Tab for auto-completion'), {
padding: 1,
borderStyle: 'single',
borderColor: 'gray'
}));
}
async interpretSmartCommand(input) {
// AI-powered command interpretation
console.log(chalk.dim(`Interpreting: "${input}"...`));
// Simulate smart interpretation
if (input.toLowerCase().includes('remember')) {
const content = input.replace(/remember( that)?/i, '').trim();
const creator = new InteractiveMemoryCreator(this.stateManager);
await creator.createQuick(content);
}
else {
console.log(chalk.yellow(`Command not recognized. Type '?' for help.`));
}
}
loadStats() {
// Load real stats from API or cache
return {
totalMemories: 247,
totalTopics: 12,
apiKeys: 3,
recentActivity: [
{ action: 'created', target: 'Project Alpha specs', timestamp: '2 min ago' },
{ action: 'searched', target: 'Meeting notes Q4', timestamp: '1 hour ago' },
{ action: 'updated', target: 'Customer feedback', timestamp: 'today' }
]
};
}
}
/**
* Interactive Memory Creator
*/
export class InteractiveMemoryCreator {
stateManager;
constructor(stateManager) {
this.stateManager = stateManager;
}
async create() {
console.clear();
console.log(chalk.bold.blue('📝 Creating New Memory\n'));
console.log("Let's capture your knowledge. I'll guide you through it:\n");
// Title input with validation
const { title } = await inquirer.prompt([
{
type: 'input',
name: 'title',
message: 'Title: (What should we call this memory?)',
validate: (input) => {
if (input.length < 3) {
return 'Title must be at least 3 characters';
}
if (input.length > 100) {
return 'Title must be less than 100 characters';
}
return true;
},
transformer: (input) => {
if (input.length > 0) {
return input + chalk.dim(` (${input.length}/100)`);
}
return input;
}
}
]);
console.log(chalk.green('✓ Great title! Clear and searchable.\n'));
// Content input
const { content } = await inquirer.prompt([
{
type: 'editor',
name: 'content',
message: 'Content: (Press Ctrl+E for editor, or type/paste below)',
default: '',
postfix: '.md'
}
]);
// Analyze content and suggest metadata
const suggestions = this.analyzeContent(content);
if (suggestions.topic) {
console.log(chalk.cyan(`📎 I noticed this looks like ${suggestions.contentType}. Would you like to:`));
const { topicChoice } = await inquirer.prompt([
{
type: 'select',
name: 'topicChoice',
message: 'Select topic:',
choices: [
{ name: `Add to "${suggestions.topic}" topic (recommended)`, value: suggestions.topic },
{ name: 'Create new topic', value: 'new' },
{ name: 'Skip categorization', value: 'skip' }
]
}
]);
if (topicChoice === 'new') {
const { newTopic } = await inquirer.prompt([
{
type: 'input',
name: 'newTopic',
message: 'New topic name:'
}
]);
suggestions.topic = newTopic;
}
else if (topicChoice === 'skip') {
suggestions.topic = null;
}
}
// Tag selection
if (suggestions.tags.length > 0) {
console.log(chalk.cyan('🏷️ Suggested tags based on content:'));
const { selectedTags } = await inquirer.prompt([
{
type: 'checkbox',
name: 'selectedTags',
message: 'Select tags:',
choices: suggestions.tags.map(tag => ({
name: tag,
value: tag,
checked: true
})),
pageSize: 10
}
]);
const { additionalTags } = await inquirer.prompt([
{
type: 'input',
name: 'additionalTags',
message: 'Additional tags (comma-separated):',
default: ''
}
]);
if (additionalTags) {
selectedTags.push(...additionalTags.split(',').map((t) => t.trim()));
}
suggestions.tags = selectedTags;
}
// Memory type selection
const { memoryType } = await inquirer.prompt([
{
type: 'select',
name: 'memoryType',
message: 'Memory Type:',
choices: [
{ name: '[C]ontext - Situational information', value: 'context' },
{ name: '[K]nowledge - Facts and learnings', value: 'knowledge' },
{ name: '[R]eference - Documentation and guides', value: 'reference' },
{ name: '[P]roject - Project-specific information', value: 'project' }
]
}
]);
// Preview
this.showPreview({
title,
content,
topic: suggestions.topic,
tags: suggestions.tags,
type: memoryType
});
// Confirm save
const { action } = await inquirer.prompt([
{
type: 'select',
name: 'action',
message: 'Ready to save?',
choices: [
{ name: '[S]ave', value: 'save' },
{ name: '[E]dit', value: 'edit' },
{ name: '[C]ancel', value: 'cancel' }
]
}
]);
if (action === 'save') {
console.log(chalk.green('\n✓ Memory saved successfully!'));
console.log(chalk.dim('ID: mem_abc123'));
}
else if (action === 'edit') {
// Restart the process
await this.create();
}
}
async createQuick(content) {
console.log(chalk.green(`✓ Quick memory created: "${content}"`));
}
analyzeContent(content) {
// Simple content analysis
const suggestions = {
contentType: 'a technical decision',
topic: null,
tags: []
};
// Detect content type and suggest metadata
if (content.toLowerCase().includes('architecture')) {
suggestions.topic = 'Architecture';
suggestions.tags = ['architecture'];
}
if (content.toLowerCase().includes('microservice')) {
suggestions.tags.push('microservices');
}
if (content.toLowerCase().includes('payment')) {
suggestions.tags.push('payments');
}
if (content.toLowerCase().includes('api')) {
suggestions.tags.push('api');
}
return suggestions;
}
showPreview(memory) {
const typeIcon = {
context: '🔍',
knowledge: '📚',
reference: '📖',
project: '🎯'
}[memory.type] || '📝';
console.log(boxen(`${typeIcon} ${chalk.bold(memory.type.charAt(0).toUpperCase() + memory.type.slice(1))} Memory\n\n` +
`${chalk.bold('Title:')} ${memory.title}\n` +
`${chalk.bold('Topic:')} ${memory.topic || 'None'}\n` +
`${chalk.bold('Tags:')} ${memory.tags.join(', ')}\n\n` +
chalk.dim(memory.content.substring(0, 100) + '...'), {
padding: 1,
borderStyle: 'single',
borderColor: 'green',
title: 'Preview',
titleAlignment: 'left'
}));
}
}
/**
* Interactive Search Experience
*/
export class InteractiveSearch {
stateManager;
constructor(stateManager) {
this.stateManager = stateManager;
}
async search(initialQuery = '') {
console.clear();
console.log(chalk.bold.blue('🔍 Search Everything\n'));
const { query } = await inquirer.prompt([
{
type: 'input',
name: 'query',
message: 'Search:',
default: initialQuery,
transformer: (input) => {
if (input.length > 0) {
return chalk.cyan(input);
}
return input;
}
}
]);
if (!query) {
return;
}
// Simulate search
console.log(chalk.dim(`\nSearching for "${query}"...`));
await this.simulateDelay(500);
// Display results
this.displayResults(query);
// Result actions
const { action } = await inquirer.prompt([
{
type: 'select',
name: 'action',
message: 'Actions:',
choices: [
{ name: '[↵] View first result', value: 'view' },
{ name: '[E]dit result', value: 'edit' },
{ name: '[D]elete result', value: 'delete' },
{ name: '[R]efine search', value: 'refine' },
{ name: '[N]ew search', value: 'new' },
{ name: '[B]ack to dashboard', value: 'back' }
]
}
]);
switch (action) {
case 'refine':
await this.search(query);
break;
case 'new':
await this.search('');
break;
case 'view':
console.log(chalk.green('Viewing result...'));
break;
}
}
displayResults(_query) {
const results = [
{ score: 94, title: 'Payment Gateway Integration Guide', age: '3 days ago', type: 'Knowledge', tags: 'api, payments, stripe' },
{ score: 87, title: 'Payment Error Handling Strategy', age: '1 week ago', type: 'Context', tags: 'error-handling, payments' },
{ score: 76, title: 'Q3 Payment Provider Comparison', age: '2 weeks ago', type: 'Reference', tags: 'analysis, vendors' }
];
console.log(boxen(chalk.bold(`Found ${results.length} relevant memories`) + chalk.dim(' (137ms)\n\n') +
results.map((r, i) => {
const marker = i === 0 ? '▶' : ' ';
return `${marker} ${chalk.green(r.score + '%')} │ ${chalk.bold(r.title)}\n` +
` │ ${chalk.dim(r.age + ' • ' + r.type + ' • ' + r.tags)}\n` +
` │ ${chalk.dim('"...complete integration guide for Stripe payment..."')}`;
}).join('\n\n'), {
padding: 1,
borderStyle: 'single',
borderColor: 'cyan',
title: 'Search Results',
titleAlignment: 'left'
}));
}
async simulateDelay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
/**
* Analytics View
*/
export class AnalyticsView {
stateManager;
constructor(stateManager) {
this.stateManager = stateManager;
}
async show() {
console.clear();
console.log(chalk.bold.blue('📊 Analytics Dashboard\n'));
// Create analytics table
const table = new Table({
head: ['Metric', 'Value', 'Change'],
colWidths: [25, 15, 15],
style: {
head: ['cyan'],
border: ['gray']
}
});
table.push(['Total Memories', '247', chalk.green('+12')], ['Topics', '12', chalk.green('+2')], ['Tags Used', '89', chalk.green('+8')], ['API Calls (Month)', '1,432', chalk.yellow('+5%')], ['Search Queries', '342', chalk.green('+18%')], ['Avg Response Time', '124ms', chalk.green('-8%')]);
console.log(table.toString());
// Memory growth chart
console.log('\n' + chalk.bold('Memory Growth (Last 7 Days)'));
this.renderChart();
// Top topics
console.log('\n' + chalk.bold('Top Topics'));
const topics = [
{ name: 'Architecture', count: 45 },
{ name: 'API', count: 38 },
{ name: 'Documentation', count: 32 },
{ name: 'Meetings', count: 28 },
{ name: 'Projects', count: 24 }
];
topics.forEach(topic => {
const bar = '█'.repeat(Math.floor(topic.count / 2));
console.log(` ${topic.name.padEnd(15)} ${chalk.cyan(bar)} ${topic.count}`);
});
}
renderChart() {
const data = [35, 38, 42, 39, 45, 48, 52];
const max = Math.max(...data);
const height = 5;
for (let row = height; row > 0; row--) {
let line = ' ';
for (const value of data) {
const barHeight = Math.round((value / max) * height);
if (barHeight >= row) {
line += chalk.cyan('█ ');
}
else {
line += ' ';
}
}
console.log(line);
}
console.log(' ' + chalk.dim('M T W T F S S'));
}
}