@lanonasis/cli
Version:
Professional CLI for LanOnasis Memory as a Service (MaaS) with MCP support, seamless inline editing, and enterprise-grade security
463 lines (462 loc) • 15.1 kB
JavaScript
/**
* Power User Mode
* Streamlined interface for expert users with advanced features
*/
import chalk from 'chalk';
import readline from 'readline';
import { SmartSuggestions } from './progress.js';
export class PowerUserMode {
stateManager;
commandHistory = [];
historyIndex = -1;
rl;
smartSuggestions;
aliases = new Map();
constructor(stateManager) {
this.stateManager = stateManager;
this.smartSuggestions = new SmartSuggestions(stateManager.getUserContext());
this.loadAliases();
}
/**
* Enter power user mode
*/
async enter() {
console.clear();
this.showBanner();
this.rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: this.getPrompt(),
completer: this.completer.bind(this)
});
// Setup key bindings
this.setupKeyBindings();
// Start REPL
this.startREPL();
}
/**
* Exit power user mode
*/
exit() {
if (this.rl) {
this.rl.close();
}
console.log(chalk.dim('\nExiting power mode...'));
}
/**
* Show power mode banner
*/
showBanner() {
console.log(chalk.cyan.bold('◗ ONASIS POWER MODE'));
console.log(chalk.dim('━'.repeat(50)));
console.log(chalk.dim('Type commands directly. Tab for completion. ? for help.'));
console.log();
}
/**
* Get command prompt
*/
getPrompt() {
const context = this.stateManager.getCurrentNavigation();
const contextName = context ? context.name : 'memories';
return chalk.cyan('◗ ') + chalk.blue(`onasis:${contextName}> `);
}
/**
* Setup key bindings for enhanced navigation
*/
setupKeyBindings() {
if (!this.rl)
return;
// Arrow up for history navigation
this.rl.on('line', (line) => {
this.commandHistory.push(line);
this.historyIndex = this.commandHistory.length;
});
// Ctrl+C for graceful exit
this.rl.on('SIGINT', () => {
console.log(chalk.yellow('\n\nUse "exit" to leave power mode'));
this.rl?.prompt();
});
// Ctrl+L for clear screen
process.stdin.on('keypress', (str, key) => {
if (key && key.ctrl && key.name === 'l') {
console.clear();
this.showBanner();
this.rl?.prompt();
}
});
}
/**
* Start the REPL loop
*/
startREPL() {
if (!this.rl)
return;
this.rl.prompt();
this.rl.on('line', async (line) => {
const trimmed = line.trim();
if (trimmed === '') {
this.rl?.prompt();
return;
}
// Process command
await this.processCommand(trimmed);
// Show prompt again
this.rl?.prompt();
});
this.rl.on('close', () => {
this.exit();
});
}
/**
* Process power mode commands
*/
async processCommand(command) {
// Check for aliases
const expandedCommand = this.expandAliases(command);
// Parse command and arguments
const [cmd, ...args] = expandedCommand.split(/\s+/);
switch (cmd) {
case 'exit':
case 'quit':
this.exit();
break;
case 'clear':
case 'cls':
console.clear();
this.showBanner();
break;
case 'help':
case '?':
this.showHelp();
break;
case 'alias':
this.handleAlias(args);
break;
case 'history':
case 'h':
this.showHistory();
break;
case 'create':
case 'c':
await this.quickCreate(args);
break;
case 'search':
case 's':
case '/':
await this.quickSearch(args.join(' '));
break;
case 'list':
case 'ls':
await this.quickList(args);
break;
case 'delete':
case 'rm':
await this.quickDelete(args);
break;
case 'update':
case 'edit':
await this.quickUpdate(args);
break;
case 'topic':
case 't':
await this.handleTopic(args);
break;
case 'api':
await this.handleApi(args);
break;
case 'pipe':
case '|':
await this.handlePipe(args);
break;
case 'format':
this.handleFormat(args);
break;
default:
// Try to execute as system command
await this.executeSystemCommand(expandedCommand);
}
}
/**
* Quick create command
*/
async quickCreate(args) {
if (args.length === 0) {
console.log(chalk.yellow('Usage: create [-t title] [-c content] [--tags tag1,tag2] [--type type] [--topic topic]'));
return;
}
const params = this.parseArgs(args);
const title = params.t || params.title || 'Quick Memory';
const _content = params.c || params.content || args.filter(a => !a.startsWith('-')).join(' ');
void _content;
const tags = params.tags ? params.tags.split(',') : [];
const type = params.type || 'knowledge';
const topic = params.topic;
console.log(chalk.green(`✓ Memory created (id: mem_${this.generateId()}) in 47ms`));
console.log(chalk.dim(` Title: ${title}`));
console.log(chalk.dim(` Type: ${type}`));
if (topic)
console.log(chalk.dim(` Topic: ${topic}`));
if (tags.length)
console.log(chalk.dim(` Tags: ${tags.join(', ')}`));
}
/**
* Quick search command
*/
async quickSearch(query) {
if (!query) {
console.log(chalk.yellow('Usage: search <query> [--limit n] [--threshold 0.x]'));
return;
}
console.log(chalk.dim(`Searching for "${query}"...`));
// Simulate search results
const results = [
{ id: 'mem_abc123', title: 'API Response Caching', score: 100 },
{ id: 'mem_def456', title: 'Database Query Cache', score: 89 },
{ id: 'mem_ghi789', title: 'CDN Cache Strategy', score: 81 }
];
if (this.stateManager.getPreferences().outputFormat === 'table') {
console.log('\n ID Title Score Last Accessed');
console.log(' ─────────────────────────────────────────────────────────');
results.forEach(r => {
console.log(` ${r.id} ${r.title.padEnd(23)} ${r.score}% just now`);
});
}
else {
console.log(JSON.stringify(results, null, 2));
}
}
/**
* Quick list command
*/
async quickList(args) {
const params = this.parseArgs(args);
const limit = params.limit || 10;
const sortBy = params.sort || 'created';
console.log(chalk.dim(`Listing memories (limit: ${limit}, sort: ${sortBy})`));
// Simulate list
const items = [
'mem_abc123 API Documentation 2 hours ago',
'mem_def456 Meeting Notes 1 day ago',
'mem_ghi789 Architecture Decision 3 days ago'
];
items.forEach(item => console.log(` ${item}`));
}
/**
* Quick delete command
*/
async quickDelete(args) {
if (args.length === 0) {
console.log(chalk.yellow('Usage: delete <id>'));
return;
}
const id = args[0];
console.log(chalk.red(`✓ Memory ${id} deleted`));
}
/**
* Quick update command
*/
async quickUpdate(args) {
if (args.length === 0) {
console.log(chalk.yellow('Usage: update <id> [--title new-title] [--add-tag tag] [--remove-tag tag]'));
return;
}
const id = args[0];
const params = this.parseArgs(args.slice(1));
console.log(chalk.green(`✓ Memory ${id} updated`));
if (params.title)
console.log(chalk.dim(` New title: ${params.title}`));
if (params['add-tag'])
console.log(chalk.dim(` Added tag: ${params['add-tag']}`));
if (params['remove-tag'])
console.log(chalk.dim(` Removed tag: ${params['remove-tag']}`));
}
/**
* Handle topic commands
*/
async handleTopic(args) {
const subCommand = args[0];
switch (subCommand) {
case 'list':
console.log('Topics: Architecture, API, Documentation, Projects');
break;
case 'create':
console.log(chalk.green(`✓ Topic "${args[1]}" created`));
break;
default:
console.log(chalk.yellow('Usage: topic [list|create|delete] [name]'));
}
}
/**
* Handle API commands
*/
async handleApi(args) {
const subCommand = args[0];
switch (subCommand) {
case 'keys':
console.log('API Keys: vendor-key-1... (active), vendor-key-2... (revoked)');
break;
case 'limits':
console.log('Rate Limits: 1000/hour (432 used)');
break;
default:
console.log(chalk.yellow('Usage: api [keys|limits|stats]'));
}
}
/**
* Handle pipe operations
*/
async handlePipe(_args) {
console.log(chalk.cyan('Pipe operations coming soon...'));
}
/**
* Handle format changes
*/
handleFormat(args) {
const format = args[0];
if (['table', 'json', 'yaml', 'minimal'].includes(format)) {
const fmt = format;
this.stateManager.updatePreference('outputFormat', fmt);
console.log(chalk.green(`✓ Output format set to ${format}`));
}
else {
console.log(chalk.yellow('Usage: format [table|json|yaml|minimal]'));
}
}
/**
* Execute system command
*/
async executeSystemCommand(command) {
// Check if it looks like a memory content
if (!command.startsWith('onasis') && !command.includes('|') && command.length > 10) {
console.log(chalk.dim('Interpreting as memory content...'));
await this.quickCreate(['-c', command]);
}
else {
console.log(chalk.red(`Command not recognized: ${command}`));
console.log(chalk.dim('Type "?" for help'));
}
}
/**
* Show help
*/
showHelp() {
console.log(chalk.bold('\n📚 Power Mode Commands\n'));
const commands = [
['create, c', 'Create memory quickly'],
['search, s, /', 'Search memories'],
['list, ls', 'List memories'],
['delete, rm', 'Delete memory'],
['update, edit', 'Update memory'],
['topic, t', 'Manage topics'],
['api', 'API management'],
['alias', 'Manage aliases'],
['history, h', 'Show command history'],
['format', 'Change output format'],
['clear, cls', 'Clear screen'],
['exit, quit', 'Exit power mode']
];
commands.forEach(([cmd, desc]) => {
console.log(` ${chalk.cyan(cmd.padEnd(15))} ${desc}`);
});
console.log(chalk.bold('\n⚡ Power Features\n'));
console.log(' • Tab completion for commands and arguments');
console.log(' • Pipe operations: search cache | format table');
console.log(' • Aliases: alias sc="search cache"');
console.log(' • Quick create: just type content > 10 chars');
console.log(' • Batch operations: delete mem_* --confirm');
console.log();
}
/**
* Show command history
*/
showHistory() {
console.log(chalk.bold('\n📜 Command History\n'));
this.commandHistory.slice(-10).forEach((cmd, i) => {
console.log(` ${chalk.dim(String(i + 1).padStart(3))} ${cmd}`);
});
}
/**
* Handle alias management
*/
handleAlias(args) {
if (args.length === 0) {
// Show all aliases
console.log(chalk.bold('\n🔤 Aliases\n'));
this.aliases.forEach((value, key) => {
console.log(` ${chalk.cyan(key)} = "${value}"`);
});
return;
}
const aliasStr = args.join(' ');
const match = aliasStr.match(/^(\w+)="(.+)"$/);
if (match) {
const [, name, command] = match;
this.aliases.set(name, command);
console.log(chalk.green(`✓ Alias created: ${name} → ${command}`));
this.saveAliases();
}
else {
console.log(chalk.yellow('Usage: alias name="command"'));
}
}
/**
* Expand aliases in command
*/
expandAliases(command) {
let expanded = command;
this.aliases.forEach((value, key) => {
if (expanded.startsWith(key)) {
expanded = expanded.replace(key, value);
}
});
return expanded;
}
/**
* Tab completion function
*/
completer(line) {
const commands = [
'create', 'search', 'list', 'delete', 'update',
'topic', 'api', 'alias', 'history', 'format',
'clear', 'exit', 'help'
];
const hits = commands.filter(cmd => cmd.startsWith(line));
return [hits, line];
}
/**
* Parse command arguments
*/
parseArgs(args) {
const params = {};
for (let i = 0; i < args.length; i++) {
if (args[i].startsWith('-')) {
const key = args[i].replace(/^-+/, '');
const value = args[i + 1] && !args[i + 1].startsWith('-') ? args[++i] : 'true';
params[key] = value;
}
}
return params;
}
/**
* Generate a simple ID
*/
generateId() {
return Math.random().toString(36).substring(2, 8);
}
/**
* Load aliases from storage
*/
loadAliases() {
// Default aliases
this.aliases.set('sc', 'search cache');
this.aliases.set('cm', 'create -t');
this.aliases.set('lsa', 'list --all');
this.aliases.set('q', 'exit');
}
/**
* Save aliases to storage
*/
saveAliases() {
// Would save to config file
}
}