UNPKG

rawi

Version:

Rawi (راوي) is the developer-friendly AI CLI that brings the power of 12 major AI providers directly to your terminal. With seamless shell integration, persistent conversations, and 200+ specialized prompt templates, Rawi transforms your command line into

1 lines 22.2 kB
{"version":3,"sources":["/home/mkabumattar/withrawi/rawi/dist/chunk-Z6PI7SD3.cjs","../src/cli/commands/history.command.ts"],"names":["createHistoryCommand","historyCommand","Command","chalk","DEFAULT_PROFILE","options","dbManager","DatabaseManager","searchOptions","pageSize","messages","table","Table","message","truncateText","formatRelativeTime"],"mappings":"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","file":"/home/mkabumattar/withrawi/rawi/dist/chunk-Z6PI7SD3.cjs","sourcesContent":[null,"import {writeFile} from 'node:fs/promises';\nimport {select} from '@inquirer/prompts';\nimport chalk from 'chalk';\nimport Table from 'cli-table3';\nimport {Command} from 'commander';\nimport {DatabaseManager} from '../../core/database/manager.js';\nimport {\n DEFAULT_HISTORY_LIMIT,\n DEFAULT_PROFILE,\n} from '../../core/shared/constants.js';\nimport type {ChatHistoryOptions} from '../../core/shared/types.js';\nimport {formatRelativeTime, truncateText} from '../../core/shared/utils.js';\n\nexport const createHistoryCommand = (): Command => {\n const historyCommand = new Command('history');\n\n historyCommand\n .description(\n [\n chalk.bold('Manage chat history and sessions.'),\n '',\n chalk.gray(\n 'Search, filter, export, and clean up your AI chat history.',\n ),\n chalk.gray('Use --limit for pagination, --search for filtering.'),\n ].join('\\n'),\n )\n .option(\n '-p, --profile <profile>',\n chalk.white('Profile to show history for'),\n DEFAULT_PROFILE,\n )\n .option(\n '-l, --limit <number>',\n chalk.white('Number of sessions to show'),\n DEFAULT_HISTORY_LIMIT.toString(),\n )\n .option('--all', chalk.white('Show all sessions without pagination limit'))\n .option('--all-profiles', chalk.white('Show sessions from all profiles'))\n .option(\n '-s, --search <query>',\n chalk.white('Search messages containing text'),\n )\n .option('--provider <provider>', chalk.white('Filter by AI provider'))\n .option('--model <model>', chalk.white('Filter by AI model'))\n .option(\n '--from <date>',\n chalk.white('Show sessions from date (YYYY-MM-DD)'),\n )\n .option('--to <date>', chalk.white('Show sessions to date (YYYY-MM-DD)'))\n .addHelpText(\n 'after',\n [\n chalk.bold.cyan('\\nSee also:'),\n chalk.gray(' rawi history sessions'),\n chalk.gray(' rawi history show <sessionId>'),\n chalk.gray(' rawi history stats'),\n chalk.gray(' rawi history cleanup'),\n chalk.gray(' rawi history export'),\n ].join('\\n'),\n )\n .action(async (options) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n\n const searchOptions: ChatHistoryOptions = {\n profile: options.allProfiles ? undefined : options.profile,\n limit: options.all\n ? 1000\n : Number.parseInt(options.limit) || DEFAULT_HISTORY_LIMIT,\n search: options.search,\n provider: options.provider,\n model: options.model,\n fromDate: options.from,\n toDate: options.to,\n };\n\n const pageSize = 10;\n\n if (options.search) {\n const messages = await dbManager.searchMessages(searchOptions);\n if (messages.length === 0) {\n console.log(\n chalk.yellow('📭 No messages found matching your search.'),\n );\n return;\n }\n console.log(chalk.cyan(`🔍 Found ${messages.length} messages:`));\n console.log();\n const table = new Table({\n head: [\n chalk.cyan('Session'),\n chalk.cyan('Role'),\n chalk.cyan('Content'),\n chalk.cyan('Time'),\n chalk.cyan('Provider/Model'),\n ],\n style: {head: ['cyan']},\n colWidths: [18, 8, 40, 16, 22],\n wordWrap: true,\n });\n messages.forEach((message) => {\n table.push([\n truncateText(message.sessionId, 16),\n message.role === 'user'\n ? chalk.blue('👤 user')\n : chalk.green('🤖 assistant'),\n truncateText(message.content, 38),\n formatRelativeTime(message.timestamp),\n `${message.provider}/${message.model}`,\n ]);\n });\n console.log(table.toString());\n } else {\n const profileMessage = options.allProfiles\n ? 'all profiles'\n : `profile: ${searchOptions.profile}`;\n console.log(\n chalk.dim(`Searching for sessions with ${profileMessage}`),\n );\n const sessions = await dbManager.getSessions(searchOptions);\n if (sessions.length === 0) {\n console.log(chalk.yellow('📭 No chat sessions found.'));\n console.log(\n chalk.dim(\n `Run 'rawi ask \"Hello\"' to start your first conversation.`,\n ),\n );\n return;\n }\n const totalPages = Math.ceil(sessions.length / pageSize);\n let page = 0;\n const renderPage = (pageIdx: number) => {\n const table = new Table({\n head: [\n chalk.cyan('Title'),\n chalk.cyan('ID'),\n chalk.cyan('Profile'),\n chalk.cyan('Messages'),\n chalk.cyan('Updated'),\n ],\n style: {head: ['cyan']},\n colWidths: [22, 38, 12, 10, 18],\n wordWrap: true,\n });\n sessions\n .slice(pageIdx * pageSize, (pageIdx + 1) * pageSize)\n .forEach((session) => {\n table.push([\n truncateText(session.title || 'Untitled', 20),\n session.id,\n session.profile,\n session.messageCount,\n formatRelativeTime(session.updatedAt),\n ]);\n });\n console.log(table.toString());\n console.log(chalk.gray(`Page ${pageIdx + 1} of ${totalPages}`));\n };\n if (sessions.length > pageSize) {\n let exit = false;\n while (!exit) {\n renderPage(page);\n const choices = [];\n if (page > 0) choices.push({name: 'Previous', value: 'prev'});\n if (page < totalPages - 1)\n choices.push({name: 'Next', value: 'next'});\n choices.push({name: 'Exit', value: 'exit'});\n const nav = await select({\n message: 'Navigate pages:',\n choices,\n default: page < totalPages - 1 ? 'next' : 'exit',\n });\n if (nav === 'prev') page--;\n else if (nav === 'next') page++;\n else exit = true;\n if (!exit) console.clear();\n }\n } else {\n renderPage(0);\n }\n }\n\n console.log(chalk.dim('💡 Tips:'));\n console.log(chalk.dim(' • Use --search to find specific messages'));\n console.log(\n chalk.dim(\n ' • Use --session <id> with ask command to continue a session',\n ),\n );\n console.log(\n chalk.dim(' • Use \"rawi history sessions\" to manage sessions'),\n );\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error retrieving history: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n historyCommand\n .command('sessions')\n .description(\n [\n chalk.bold('List and manage chat sessions.'),\n '',\n chalk.gray('View, filter, and manage your chat sessions.'),\n ].join('\\n'),\n )\n .option(\n '-p, --profile <profile>',\n chalk.white('Profile to show sessions for'),\n DEFAULT_PROFILE,\n )\n .option(\n '-l, --limit <number>',\n chalk.white('Number of sessions to show'),\n DEFAULT_HISTORY_LIMIT.toString(),\n )\n .option('--all', chalk.white('Show all sessions without pagination limit'))\n .action(async (options) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n const sessions = await dbManager.getSessions({\n profile: options.profile,\n limit: options.all\n ? 1000\n : Number.parseInt(options.limit) || DEFAULT_HISTORY_LIMIT,\n });\n\n if (sessions.length === 0) {\n console.log(chalk.yellow('📭 No sessions found.'));\n return;\n }\n\n console.log(\n chalk.cyan(`💬 Sessions for profile \"${options.profile}\":`),\n );\n console.log();\n\n for (const session of sessions) {\n const title = session.title || 'Untitled Session';\n\n console.log(chalk.bold(`${title}`));\n console.log(chalk.dim(` ID: ${session.id}`));\n console.log(chalk.dim(` Messages: ${session.messageCount}`));\n console.log(\n chalk.dim(` Created: ${formatRelativeTime(session.createdAt)}`),\n );\n console.log(\n chalk.dim(` Updated: ${formatRelativeTime(session.updatedAt)}`),\n );\n console.log();\n }\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error retrieving sessions: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n historyCommand\n .command('show <sessionId>')\n .description(\n [\n chalk.bold('Show all messages in a specific session.'),\n '',\n chalk.gray('Display the full conversation for a session.'),\n ].join('\\n'),\n )\n .action(async (sessionId) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n\n const session = await dbManager.getSession(sessionId);\n if (!session) {\n console.error(chalk.red(`❌ Session '${sessionId}' not found.`));\n process.exit(1);\n }\n\n const messages = await dbManager.getMessages(sessionId);\n\n console.log(\n chalk.cyan(`💬 Session: ${session.title || 'Untitled Session'}`),\n );\n console.log(\n chalk.dim(\n `Profile: ${session.profile} • Created: ${formatRelativeTime(session.createdAt)}`,\n ),\n );\n console.log(chalk.dim('═'.repeat(60)));\n console.log();\n\n for (const message of messages) {\n const icon = message.role === 'user' ? '👤' : '🤖';\n const roleColor = message.role === 'user' ? chalk.blue : chalk.green;\n\n console.log(roleColor.bold(`${icon} ${message.role.toUpperCase()}:`));\n console.log(message.content);\n console.log(\n chalk.dim(\n `${formatRelativeTime(message.timestamp)} • ${message.provider}/${message.model}`,\n ),\n );\n console.log();\n }\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error showing session: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n historyCommand\n .command('delete <sessionId>')\n .description(\n [\n chalk.bold('Delete a specific session.'),\n '',\n chalk.gray('Remove a chat session and its messages.'),\n ].join('\\n'),\n )\n .action(async (sessionId) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n\n const session = await dbManager.getSession(sessionId);\n if (!session) {\n console.error(chalk.red(`❌ Session '${sessionId}' not found.`));\n process.exit(1);\n }\n\n const deleted = await dbManager.deleteSession(sessionId);\n if (deleted) {\n console.log(\n chalk.green(`✅ Deleted session: ${session.title || sessionId}`),\n );\n } else {\n console.error(\n chalk.red(`❌ Failed to delete session '${sessionId}'.`),\n );\n process.exit(1);\n }\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error deleting session: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n historyCommand\n .command('stats')\n .description(\n [\n chalk.bold('Show usage statistics.'),\n '',\n chalk.gray('Display usage stats for your chat history.'),\n ].join('\\n'),\n )\n .option('-p, --profile <profile>', chalk.white('Profile to show stats for'))\n .action(async (options) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n const stats = await dbManager.getStats(options.profile);\n\n console.log(chalk.cyan('📊 Usage Statistics:'));\n console.log();\n\n console.log(chalk.bold('Overall:'));\n console.log(` Sessions: ${stats.totalSessions}`);\n console.log(` Messages: ${stats.totalMessages}`);\n\n if (stats.oldestMessage) {\n console.log(\n ` First message: ${formatRelativeTime(stats.oldestMessage)}`,\n );\n }\n\n if (stats.newestMessage) {\n console.log(\n ` Latest message: ${formatRelativeTime(stats.newestMessage)}`,\n );\n }\n\n console.log();\n\n if (Object.keys(stats.messagesByProvider).length > 0) {\n console.log(chalk.bold('By Provider:'));\n for (const [provider, count] of Object.entries(\n stats.messagesByProvider,\n )) {\n console.log(` ${provider}: ${count} messages`);\n }\n console.log();\n }\n\n if (Object.keys(stats.messagesByModel).length > 0) {\n console.log(chalk.bold('Top Models:'));\n const sortedModels = Object.entries(stats.messagesByModel)\n .sort(([, a], [, b]) => b - a)\n .slice(0, 10);\n\n for (const [model, count] of sortedModels) {\n console.log(` ${model}: ${count} messages`);\n }\n console.log();\n }\n\n if (\n !options.profile &&\n Object.keys(stats.messagesByProfile).length > 0\n ) {\n console.log(chalk.bold('By Profile:'));\n for (const [profile, count] of Object.entries(\n stats.messagesByProfile,\n )) {\n console.log(` ${profile}: ${count} messages`);\n }\n }\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error retrieving stats: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n historyCommand\n .command('cleanup')\n .description(\n [\n chalk.bold('Clean up old sessions.'),\n '',\n chalk.gray('Delete sessions older than a specified number of days.'),\n ].join('\\n'),\n )\n .option(\n '-p, --profile <profile>',\n chalk.white('Profile to clean up'),\n DEFAULT_PROFILE,\n )\n .option(\n '-d, --days <number>',\n chalk.white('Delete sessions older than N days'),\n '30',\n )\n .option('--confirm', chalk.white('Confirm deletion without prompt'))\n .action(async (options) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n const days = Number.parseInt(options.days) || 30;\n\n if (!options.confirm) {\n console.log(\n chalk.yellow(\n `⚠️ This will delete sessions older than ${days} days for profile \"${options.profile}\".`,\n ),\n );\n console.log(chalk.yellow(' Add --confirm to proceed.'));\n return;\n }\n\n const deletedCount = await dbManager.deleteOldSessions(\n options.profile,\n days,\n );\n\n if (deletedCount > 0) {\n console.log(chalk.green(`✅ Deleted ${deletedCount} old sessions.`));\n dbManager.vacuum();\n console.log(chalk.dim('💾 Database optimized.'));\n } else {\n console.log(chalk.blue('ℹ️ No old sessions found to delete.'));\n }\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error during cleanup: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n historyCommand\n .command('export')\n .description(\n [\n chalk.bold('Export chat history to JSON.'),\n '',\n chalk.gray('Save your chat history to a file for backup or analysis.'),\n ].join('\\n'),\n )\n .option('-p, --profile <profile>', chalk.white('Profile to export'))\n .option(\n '-o, --output <file>',\n chalk.white('Output file path'),\n 'rawi-history-export.json',\n )\n .action(async (options) => {\n let dbManager: DatabaseManager | null = null;\n\n try {\n dbManager = new DatabaseManager();\n const exportData = await dbManager.exportChatHistory({\n profile: options.profile,\n });\n\n const jsonData = JSON.stringify(exportData, null, 2);\n await writeFile(options.output, jsonData, 'utf8');\n\n console.log(\n chalk.green(`✅ Exported chat history to: ${options.output}`),\n );\n console.log(chalk.dim(` Sessions: ${exportData.sessions.length}`));\n console.log(\n chalk.dim(\n ` Total messages: ${Object.values(exportData.messages).flat().length}`,\n ),\n );\n } catch (error) {\n console.error(\n chalk.red(\n `❌ Error exporting history: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n process.exit(1);\n } finally {\n if (dbManager) {\n dbManager.close();\n }\n }\n });\n\n return historyCommand;\n};\n"]}