@webdevtoday/grok-cli
Version:
A sophisticated CLI tool for interacting with xAI Grok 4, featuring conversation history, file reference, custom commands, memory system, and genetic development workflows
837 lines • 39.5 kB
JavaScript
;
/**
* Grok CLI - Main entry point
* A sophisticated CLI tool for interacting with xAI Grok 4
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
const commander_1 = require("commander");
const chalk_1 = __importDefault(require("chalk"));
const config_1 = require("./core/config");
const session_1 = require("./core/session");
const hooks_1 = require("./core/hooks");
const base_1 = require("./tools/base");
const tools_1 = require("./tools");
const mcp_client_1 = require("./core/mcp-client");
const mcp_1 = require("./tools/mcp");
const memory_1 = require("./core/memory");
const file_reference_1 = require("./core/file-reference");
const setup_1 = require("./core/setup");
const enhanced_commands_1 = require("./core/enhanced-commands");
const permissions_1 = require("./core/permissions");
const package_json_1 = require("../package.json");
const program = new commander_1.Command();
// ASCII Art for Grok CLI
const GROK_ASCII = `
██████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗ ██╗ ██████╗██╗ ██╗
██╔════╝ ██╔══██╗██╔═══██╗██║ ██╔╝ ██║ ██║ ██╔════╝██║ ██║
██║ ███╗██████╔╝██║ ██║█████╔╝ ███████║ ██║ ██║ ██║
██║ ██║██╔══██╗██║ ██║██╔═██╗ ╚════██║ ██║ ██║ ██║
╚██████╔╝██║ ██║╚██████╔╝██║ ██╗ ██║ ╚██████╗███████╗██║
╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═╝
`;
async function main() {
program
.name('grok')
.description('A sophisticated CLI tool for interacting with xAI Grok 4')
.version(package_json_1.version)
.option('--api-key <key>', 'xAI API key (can also use XAI_API_KEY env var)')
.option('--dev', 'use OpenAI model for development (requires OPENAI_API_KEY)')
.option('--model <model>', 'specify the model to use')
.option('--verbose', 'enable verbose logging')
.option('--debug', 'enable debug mode')
.option('--config <path>', 'path to configuration file')
.option('--resume <sessionId>', 'resume a previous session')
.option('--continue', 'continue the most recent session')
.option('--add-dir <dirs...>', 'additional working directories')
.option('--allowed-tools <tools...>', 'tools allowed without permission prompts')
.option('--disallowed-tools <tools...>', 'tools that are disallowed')
.option('--permission-mode <mode>', 'permission mode: ask, plan, or full')
.option('--dangerously-skip-permissions', 'skip all permission prompts (dangerous!)')
.option('--print', 'print mode - output response and exit')
.option('--output-format <format>', 'output format: text, json, or stream-json');
// Parse command line arguments
program.parse(process.argv);
const options = program.opts();
try {
// Check for first-time setup
const setupManager = new setup_1.SetupManager();
let apiKey = null;
if (!(await setupManager.hasApiKey())) {
console.log(chalk_1.default.blue('🔑 First time setup detected...'));
apiKey = await setupManager.setupApiKey();
if (apiKey) {
await setupManager.initializeProject();
}
}
else {
apiKey = await setupManager.getApiKey();
}
// Load configuration
const configManager = config_1.ConfigManager.getInstance();
const config = await configManager.loadConfig();
// Update config with API key from local file if available
if (apiKey) {
config.apiKeys.xai = apiKey;
}
// Show ASCII art and welcome message
console.log(chalk_1.default.cyan(GROK_ASCII));
console.log(chalk_1.default.green('Chat with Grok 4 in your terminal.'));
console.log(chalk_1.default.gray("Type 'exit' to quit or '/help' for commands."));
console.log();
// Show configuration status
if (options['verbose'] || options['debug']) {
console.log(chalk_1.default.blue('📊 Configuration Status:'));
console.log(chalk_1.default.gray(`Model: ${config.model}`));
console.log(chalk_1.default.gray(`Permission Mode: ${config.permissions.mode}`));
console.log(chalk_1.default.gray(`Working Directory: ${process.cwd()}`));
if (config.apiKeys.xai) {
console.log(chalk_1.default.green('✅ xAI API key configured'));
}
else {
console.log(chalk_1.default.yellow('⚠️ xAI API key not configured'));
}
if (config.apiKeys.openai && options['dev']) {
console.log(chalk_1.default.green('🚧 Development mode: Using OpenAI'));
}
console.log();
}
// Start interactive mode or execute command
if (options['print']) {
// TODO: Implement print mode
console.log(chalk_1.default.yellow('Print mode not yet implemented'));
process.exit(1);
}
else {
// Start interactive REPL
await startInteractiveMode(config, options);
}
}
catch (error) {
console.error(chalk_1.default.red('❌ Error starting Grok CLI:'));
console.error(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
if (options['debug']) {
console.error(chalk_1.default.gray('Debug information:'));
console.error(error);
}
process.exit(1);
}
}
/**
* Start interactive REPL mode
*/
async function startInteractiveMode(config, options) {
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
// Initialize core systems
const sessionManager = new session_1.SessionManager(config);
const hookManager = new hooks_1.HookManager(config);
const toolRegistry = (0, tools_1.setupToolRegistry)();
const permissionManager = new permissions_1.EnhancedPermissionManager(config, process.cwd());
const toolExecutor = new base_1.ToolExecutor(toolRegistry, permissionManager);
const fileReferenceManager = new file_reference_1.FileReferenceManager();
const setupManager = new setup_1.SetupManager();
// Initialize MCP client and tool manager
const mcpClient = new mcp_client_1.McpClient();
new mcp_1.McpToolManager(toolExecutor, mcpClient); // Auto-manages tool registration
// Initialize memory manager
const memoryManager = new memory_1.MemoryManager(process.cwd(), config.memory);
// Auto-load memory if configured
if (config.memory.autoLoad) {
try {
await memoryManager.loadMemoryFiles();
}
catch (error) {
console.warn(chalk_1.default.yellow('⚠️ Failed to auto-load memory:'), error instanceof Error ? error.message : String(error));
}
}
// Initialize command manager with all components
const commandManager = new enhanced_commands_1.EnhancedCommandManager(toolExecutor, sessionManager, fileReferenceManager, setupManager);
// Connect hook manager to tool executor
toolExecutor.setHookManager(hookManager);
// Handle continuation from previous session
let session;
if (options['continue']) {
console.log(chalk_1.default.blue('🔄 Continuing from last conversation...'));
session = await sessionManager.continueLastConversation();
if (session) {
console.log(chalk_1.default.green(`✅ Resumed conversation with ${session.history.length} previous messages`));
}
else {
console.log(chalk_1.default.yellow('⚠️ No previous conversation found, starting new session'));
session = await sessionManager.createSession();
}
}
else if (options['resume']) {
const sessionId = options['resume'];
console.log(chalk_1.default.blue(`🔄 Resuming conversation ${sessionId}...`));
session = await sessionManager.continueFromSession(sessionId);
if (session) {
console.log(chalk_1.default.green(`✅ Resumed conversation with ${session.history.length} previous messages`));
}
else {
console.log(chalk_1.default.yellow(`⚠️ Conversation ${sessionId} not found, starting new session`));
session = await sessionManager.createSession();
}
}
else {
// Create new session
session = await sessionManager.createSession();
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: chalk_1.default.blue('You: '),
});
console.log(chalk_1.default.gray('🧠 Loading memory files...'));
console.log(chalk_1.default.gray('🔧 Initializing hook system...'));
console.log(chalk_1.default.gray('🔌 Connecting to MCP servers...'));
console.log(chalk_1.default.green(`📝 Session started: ${session.id}`));
console.log();
rl.prompt();
rl.on('line', async (input) => {
const trimmedInput = input.trim();
if (trimmedInput.toLowerCase() === 'exit') {
console.log(chalk_1.default.green('Goodbye! 👋'));
rl.close();
return;
}
if (trimmedInput.startsWith('/')) {
// Handle slash commands using the new command system
const commandContext = {
session,
config,
cwd: process.cwd(),
mcpClient, // Add MCP client to context
memoryManager, // Add memory manager to context
};
const result = await commandManager.executeCommand(trimmedInput, commandContext);
if (!result.success && result.error) {
console.log(chalk_1.default.red(`❌ ${result.error}`));
}
}
else if (trimmedInput.startsWith('@')) {
// Handle file reference commands
await handleFileReference(trimmedInput.slice(1), fileReferenceManager, config);
}
else if (trimmedInput.startsWith('#')) {
// Handle memory addition
await handleMemoryAddition(trimmedInput.slice(1), memoryManager);
}
else if (trimmedInput) {
// Handle chat message
await handleChatMessage(trimmedInput, config, hookManager, session, memoryManager);
}
rl.prompt();
});
rl.on('close', () => {
console.log(chalk_1.default.green('Goodbye! 👋'));
process.exit(0);
});
}
/**
* Handle file reference commands
*/
async function handleFileReference(query, fileManager, _config) {
if (!query.trim()) {
// Show file reference help
console.log(chalk_1.default.blue('📁 File Reference System:'));
console.log();
console.log(chalk_1.default.cyan('@<filename>') + chalk_1.default.gray(' - Search for files by name'));
console.log(chalk_1.default.cyan('@path/to/file') + chalk_1.default.gray(' - Reference specific file path'));
console.log(chalk_1.default.cyan('@recent') + chalk_1.default.gray(' - Show recently modified files'));
console.log(chalk_1.default.cyan('@git') + chalk_1.default.gray(' - Show git tracked files'));
console.log(chalk_1.default.cyan('@ext:<extension>') + chalk_1.default.gray(' - Show files by extension (e.g., @ext:ts)'));
console.log();
console.log(chalk_1.default.gray('Examples:'));
console.log(chalk_1.default.gray(' @package.json'));
console.log(chalk_1.default.gray(' @src/cli.ts'));
console.log(chalk_1.default.gray(' @recent'));
console.log(chalk_1.default.gray(' @ext:py'));
return;
}
try {
if (query === 'recent') {
const recentFiles = await fileManager.getRecentFiles(15);
if (recentFiles.length === 0) {
console.log(chalk_1.default.yellow('No recent files found'));
return;
}
console.log(chalk_1.default.blue('📁 Recent Files:'));
recentFiles.forEach((file, index) => {
const timeAgo = getTimeAgo(file.modified);
const size = formatFileSize(file.size);
const gitIcon = file.isGitTracked ? '📝' : '📄';
console.log(`${index + 1}.`.padStart(3) + ` ${gitIcon} ${chalk_1.default.cyan(file.relativePath)} ${chalk_1.default.gray(`(${size}, ${timeAgo})`)}`);
});
return;
}
if (query === 'git') {
const gitFiles = await fileManager.getGitTrackedFiles();
if (gitFiles.length === 0) {
console.log(chalk_1.default.yellow('No git tracked files found'));
return;
}
console.log(chalk_1.default.blue('📝 Git Tracked Files:'));
gitFiles.slice(0, 20).forEach((file, index) => {
const size = formatFileSize(file.size);
console.log(`${index + 1}.`.padStart(3) + ` 📝 ${chalk_1.default.cyan(file.relativePath)} ${chalk_1.default.gray(`(${size})`)}`);
});
if (gitFiles.length > 20) {
console.log(chalk_1.default.gray(`... and ${gitFiles.length - 20} more files`));
}
return;
}
if (query.startsWith('ext:')) {
const extension = query.slice(4);
const files = await fileManager.getFilesByExtension(extension);
if (files.length === 0) {
console.log(chalk_1.default.yellow(`No .${extension} files found`));
return;
}
console.log(chalk_1.default.blue(`📁 .${extension} Files:`));
files.slice(0, 20).forEach((file, index) => {
const size = formatFileSize(file.size);
const gitIcon = file.isGitTracked ? '📝' : '📄';
console.log(`${index + 1}.`.padStart(3) + ` ${gitIcon} ${chalk_1.default.cyan(file.relativePath)} ${chalk_1.default.gray(`(${size})`)}`);
});
if (files.length > 20) {
console.log(chalk_1.default.gray(`... and ${files.length - 20} more files`));
}
return;
}
// Search for files
const files = await fileManager.searchFiles(query, { maxResults: 15 });
if (files.length === 0) {
console.log(chalk_1.default.yellow(`No files found matching "${query}"`));
// Show suggestions
const suggestions = await fileManager.getFileSuggestions(query.slice(0, 3));
if (suggestions.length > 0) {
console.log(chalk_1.default.gray('Did you mean:'));
suggestions.slice(0, 5).forEach(file => {
console.log(chalk_1.default.gray(` @${file.relativePath}`));
});
}
return;
}
if (files.length === 1) {
// Single file found - show details and content preview
const file = files[0];
if (!file)
return;
console.log(chalk_1.default.blue('📁 File Details:'));
console.log(chalk_1.default.cyan(`Path: ${file.relativePath}`));
console.log(chalk_1.default.gray(`Size: ${formatFileSize(file.size)}`));
console.log(chalk_1.default.gray(`Modified: ${getTimeAgo(file.modified)}`));
console.log(chalk_1.default.gray(`Type: ${file.type}`));
if (file.extension) {
console.log(chalk_1.default.gray(`Extension: .${file.extension}`));
}
console.log(chalk_1.default.gray(`Git tracked: ${file.isGitTracked ? 'Yes' : 'No'}`));
// Show content preview for small text files
if (file.type === 'file' && file.size < 10000 && isTextFile(file.extension)) {
try {
const { readFile } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
const content = await readFile(file.path, 'utf-8');
const lines = content.split('\n');
console.log();
console.log(chalk_1.default.blue('📄 Content Preview:'));
lines.slice(0, 10).forEach((line, index) => {
console.log(chalk_1.default.gray(`${(index + 1).toString().padStart(3)}: ${line}`));
});
if (lines.length > 10) {
console.log(chalk_1.default.gray(`... ${lines.length - 10} more lines`));
}
}
catch {
console.log(chalk_1.default.gray('Unable to preview file content'));
}
}
return;
}
// Multiple files found - show list
console.log(chalk_1.default.blue(`📁 Found ${files.length} files matching "${query}":`));
files.forEach((file, index) => {
const size = formatFileSize(file.size);
const gitIcon = file.isGitTracked ? '📝' : '📄';
console.log(`${index + 1}.`.padStart(3) + ` ${gitIcon} ${chalk_1.default.cyan(file.relativePath)} ${chalk_1.default.gray(`(${size})`)}`);
});
console.log();
console.log(chalk_1.default.gray('Use @<exact-path> to view a specific file'));
}
catch (error) {
console.log(chalk_1.default.red('Error searching files:'), error instanceof Error ? error.message : String(error));
}
}
/**
* Handle memory addition
*/
async function handleMemoryAddition(content, memoryManager) {
if (!content.trim()) {
console.log(chalk_1.default.yellow('Usage: #<content to add to memory>'));
return;
}
console.log(chalk_1.default.blue('📝 Adding to memory:'), content);
try {
await memoryManager.addToMemory(content.trim(), 'note');
console.log(chalk_1.default.green('✅ Added to memory'));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to add to memory:'), error instanceof Error ? error.message : String(error));
}
}
/**
* Handle chat messages
*/
async function handleChatMessage(message, config, hookManager, session, memoryManager) {
// Inject memory content if available and auto-load is enabled
let enhancedMessage = message;
if (memoryManager && config.memory.autoLoad) {
const memoryContent = memoryManager.getMemoryContent();
if (memoryContent.trim()) {
enhancedMessage = `Context from memory:\n\n${memoryContent}\n\n---\n\nUser message: ${message}`;
console.log(chalk_1.default.gray('🧠 Memory context injected'));
}
}
// Execute UserPromptSubmit hooks
const userPromptEvent = {
sessionId: session.id,
transcriptPath: session.transcriptPath,
cwd: session.cwd,
hookEventName: 'UserPromptSubmit',
prompt: enhancedMessage, // Use enhanced message with memory
timestamp: new Date(),
};
await hookManager.executeHooks('UserPromptSubmit', userPromptEvent);
console.log(chalk_1.default.gray('🤖 Processing...'));
// TODO: Implement agent chat
console.log(chalk_1.default.yellow('Chat functionality not yet implemented'));
console.log(chalk_1.default.gray('Your message:'), message);
if (memoryManager && config.memory.autoLoad && enhancedMessage !== message) {
console.log(chalk_1.default.gray('📊 Enhanced message length:'), enhancedMessage.length, 'characters');
}
}
/**
* Show help information
*/
/**
* Test hooks functionality
*/
async function testHooks(toolExecutor, session) {
console.log(chalk_1.default.blue('🔧 Testing hooks...'));
const context = {
sessionId: session.id,
cwd: session.cwd,
timestamp: new Date(),
};
// Test tool execution with hooks
const result = await toolExecutor.executeTool('Read', {
file_path: 'package.json'
}, context);
console.log(chalk_1.default.green('✅ Hook test result:'), result.success ? 'Success' : 'Failed');
if (result.error) {
console.log(chalk_1.default.red('Error:'), result.error);
}
else if (result.output) {
console.log(chalk_1.default.gray('Output preview:'), result.output.substring(0, 100) + '...');
}
}
/**
* Show available tools
*/
function showTools(toolExecutor) {
const tools = toolExecutor.getRegistry().getAllTools();
console.log(chalk_1.default.blue('🔧 Available tools:'));
tools.forEach(tool => {
console.log(` ${chalk_1.default.green(tool.name)} - ${tool.description}`);
});
}
/**
* Handle custom command management
*/
async function handleCustomCommands(args, toolExecutor, session) {
const subcommand = args[0] || 'list';
const context = {
sessionId: session.id,
cwd: session.cwd,
timestamp: new Date(),
};
switch (subcommand) {
case 'list':
const listResult = await toolExecutor.executeTool('Custom', { action: 'list' }, context);
if (listResult.success) {
console.log(chalk_1.default.blue('📦 Custom Commands:'));
console.log(listResult.output);
}
else {
console.log(chalk_1.default.red('Error:'), listResult.error);
}
break;
case 'run':
const commandName = args[1];
if (!commandName) {
console.log(chalk_1.default.red('Usage: /custom run <command-name>'));
return;
}
const runResult = await toolExecutor.executeTool('Custom', {
action: 'run',
name: commandName
}, context);
if (runResult.success) {
console.log(chalk_1.default.green(`✅ Custom command '${commandName}' executed successfully`));
if (runResult.output) {
console.log(runResult.output);
}
}
else {
console.log(chalk_1.default.red(`❌ Custom command '${commandName}' failed:`), runResult.error);
}
break;
case 'help':
console.log(chalk_1.default.blue('📦 Custom Commands Help:'));
console.log();
console.log(chalk_1.default.cyan('/custom list') + chalk_1.default.gray(' - List all custom commands'));
console.log(chalk_1.default.cyan('/custom run <name>') + chalk_1.default.gray(' - Run a custom command'));
console.log(chalk_1.default.cyan('/init-commands') + chalk_1.default.gray(' - Initialize genetic development commands'));
console.log();
console.log(chalk_1.default.gray('Custom commands allow you to create shortcuts for complex workflows'));
break;
default:
console.log(chalk_1.default.yellow(`Unknown custom command: ${subcommand}`));
console.log(chalk_1.default.gray("Type '/custom help' for available subcommands"));
}
}
/**
* Initialize genetic development commands
*/
async function initializeGeneticCommands(toolExecutor, session) {
console.log(chalk_1.default.blue('🧬 Initializing genetic development commands...'));
const context = {
sessionId: session.id,
cwd: session.cwd,
timestamp: new Date(),
};
// Import the genetic development commands
const { CustomTool } = await Promise.resolve().then(() => __importStar(require('./tools/custom')));
const geneticCommands = CustomTool.getGeneticDevCommands();
let added = 0;
let skipped = 0;
for (const command of geneticCommands) {
const result = await toolExecutor.executeTool('Custom', {
action: 'add',
command,
}, context);
if (result.success) {
added++;
console.log(chalk_1.default.green(`✅ Added: ${command.name}`));
}
else if (result.error?.includes('already exists')) {
skipped++;
console.log(chalk_1.default.yellow(`⚠️ Skipped: ${command.name} (already exists)`));
}
else {
console.log(chalk_1.default.red(`❌ Failed to add ${command.name}:`), result.error);
}
}
console.log();
console.log(chalk_1.default.blue(`📊 Summary: Added ${added} commands, skipped ${skipped} existing commands`));
console.log(chalk_1.default.gray('Use /custom list to see all available commands'));
}
/**
* Handle history commands
*/
async function handleHistoryCommand(args, sessionManager) {
const subcommand = args[0] || 'list';
switch (subcommand) {
case 'list':
const limit = args[1] ? parseInt(args[1]) || 10 : 10;
const conversations = await sessionManager.getHistoryManager().getRecentConversations(limit);
if (conversations.length === 0) {
console.log(chalk_1.default.yellow('No conversation history found'));
return;
}
console.log(chalk_1.default.blue(`📚 Recent Conversations (${conversations.length}):`));
console.log();
conversations.forEach((conv, index) => {
const timeAgo = getTimeAgo(conv.lastUpdateTime);
const messageCount = chalk_1.default.gray(`${conv.messageCount} messages`);
const preview = conv.firstMessage.length > 50
? conv.firstMessage.slice(0, 50) + '...'
: conv.firstMessage;
console.log(`${(index + 1).toString().padStart(2)}. ${chalk_1.default.cyan(conv.id.slice(0, 8))} ${chalk_1.default.gray(timeAgo)} ${messageCount}`);
console.log(` ${chalk_1.default.gray(preview)}`);
console.log();
});
break;
case 'search':
const query = args.slice(1).join(' ');
if (!query) {
console.log(chalk_1.default.red('Usage: /history search <query>'));
return;
}
const searchResults = await sessionManager.searchHistory(query);
if (searchResults.length === 0) {
console.log(chalk_1.default.yellow(`No conversations found matching "${query}"`));
return;
}
console.log(chalk_1.default.blue(`🔍 Search Results for "${query}" (${searchResults.length}):`));
console.log();
searchResults.forEach((conv, index) => {
const timeAgo = getTimeAgo(conv.lastUpdateTime);
console.log(`${(index + 1).toString().padStart(2)}. ${chalk_1.default.cyan(conv.id.slice(0, 8))} ${chalk_1.default.gray(timeAgo)}`);
console.log(` ${chalk_1.default.gray(conv.firstMessage.slice(0, 80))}`);
console.log();
});
break;
case 'stats':
const stats = await sessionManager.getHistoryStatistics();
console.log(chalk_1.default.blue('📊 History Statistics:'));
console.log();
console.log(chalk_1.default.cyan('Total conversations:'), stats.totalConversations);
console.log(chalk_1.default.cyan('Total messages:'), stats.totalMessages);
console.log(chalk_1.default.cyan('Average messages per conversation:'), stats.avgMessagesPerConversation);
if (stats.oldestConversation) {
console.log(chalk_1.default.cyan('Oldest conversation:'), getTimeAgo(stats.oldestConversation));
}
if (stats.newestConversation) {
console.log(chalk_1.default.cyan('Newest conversation:'), getTimeAgo(stats.newestConversation));
}
break;
case 'delete':
const sessionId = args[1];
if (!sessionId) {
console.log(chalk_1.default.red('Usage: /history delete <session-id>'));
return;
}
const deleted = await sessionManager.deleteFromHistory(sessionId);
if (deleted) {
console.log(chalk_1.default.green(`✅ Deleted conversation ${sessionId}`));
}
else {
console.log(chalk_1.default.red(`❌ Failed to delete conversation ${sessionId}`));
}
break;
case 'help':
console.log(chalk_1.default.blue('📚 History Commands:'));
console.log();
console.log(chalk_1.default.cyan('/history list [limit]') + chalk_1.default.gray(' - List recent conversations'));
console.log(chalk_1.default.cyan('/history search <query>') + chalk_1.default.gray(' - Search conversations'));
console.log(chalk_1.default.cyan('/history stats') + chalk_1.default.gray(' - Show history statistics'));
console.log(chalk_1.default.cyan('/history delete <id>') + chalk_1.default.gray(' - Delete a conversation'));
console.log();
console.log(chalk_1.default.gray('Use conversation IDs to continue or reference specific sessions'));
break;
default:
console.log(chalk_1.default.yellow(`Unknown history command: ${subcommand}`));
console.log(chalk_1.default.gray("Type '/history help' for available subcommands"));
}
}
/**
* Handle continue commands
*/
async function handleContinueCommand(args, sessionManager) {
const sessionId = args[0];
if (sessionId) {
console.log(chalk_1.default.blue(`🔄 Continuing from conversation ${sessionId}...`));
const session = await sessionManager.continueFromSession(sessionId);
if (session) {
console.log(chalk_1.default.green(`✅ Continued conversation with ${session.history.length} previous messages`));
}
else {
console.log(chalk_1.default.red(`❌ Conversation ${sessionId} not found`));
}
}
else {
console.log(chalk_1.default.blue('🔄 Continuing from last conversation...'));
const session = await sessionManager.continueLastConversation();
if (session) {
console.log(chalk_1.default.green(`✅ Continued conversation with ${session.history.length} previous messages`));
}
else {
console.log(chalk_1.default.yellow('⚠️ No previous conversation found'));
}
}
}
/**
* Handle setup commands
*/
async function handleSetupCommand(args) {
const subcommand = args[0] || 'status';
const setupManager = new setup_1.SetupManager();
switch (subcommand) {
case 'status':
await setupManager.showSetupStatus();
break;
case 'api-key':
console.log(chalk_1.default.blue('🔑 Setting up API key...'));
const apiKey = await setupManager.setupApiKey();
if (apiKey) {
console.log(chalk_1.default.green('✅ API key configured successfully'));
}
break;
case 'init':
const success = await setupManager.initializeProject();
if (success) {
console.log(chalk_1.default.green('✅ Project initialized successfully'));
}
break;
case 'detect':
const projectTypes = await setupManager.detectProjectType();
console.log(chalk_1.default.blue('🔍 Detected project types:'));
if (projectTypes.length > 0) {
projectTypes.forEach(type => {
console.log(` ${chalk_1.default.green('✓')} ${type}`);
});
}
else {
console.log(chalk_1.default.gray(' No specific project type detected'));
}
break;
case 'help':
console.log(chalk_1.default.blue('🔧 Setup Commands:'));
console.log();
console.log(chalk_1.default.cyan('/setup status') + chalk_1.default.gray(' - Show setup status'));
console.log(chalk_1.default.cyan('/setup api-key') + chalk_1.default.gray(' - Configure API key'));
console.log(chalk_1.default.cyan('/setup init') + chalk_1.default.gray(' - Initialize project'));
console.log(chalk_1.default.cyan('/setup detect') + chalk_1.default.gray(' - Detect project type'));
console.log();
console.log(chalk_1.default.gray('Setup commands help configure Grok CLI for your project'));
break;
default:
console.log(chalk_1.default.yellow(`Unknown setup command: ${subcommand}`));
console.log(chalk_1.default.gray("Type '/setup help' for available subcommands"));
}
}
/**
* Utility functions for file handling
*/
function formatFileSize(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(1)) + ' ' + sizes[i];
}
function getTimeAgo(date) {
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffMinutes = Math.floor(diffMs / (1000 * 60));
if (diffDays > 0)
return `${diffDays}d ago`;
if (diffHours > 0)
return `${diffHours}h ago`;
if (diffMinutes > 0)
return `${diffMinutes}m ago`;
return 'just now';
}
function isTextFile(extension) {
if (!extension)
return false;
const textExtensions = ['txt', 'md', 'json', 'js', 'ts', 'py', 'html', 'css', 'yml', 'yaml', 'xml', 'csv', 'log', 'sh', 'bash'];
return textExtensions.includes(extension.toLowerCase());
}
function showHelp() {
console.log(chalk_1.default.blue('📚 Available Commands:'));
console.log();
console.log(chalk_1.default.cyan('/help') + chalk_1.default.gray(' - Show this help message'));
console.log(chalk_1.default.cyan('/status') + chalk_1.default.gray(' - Show system status'));
console.log(chalk_1.default.cyan('/config') + chalk_1.default.gray(' - Show configuration'));
console.log(chalk_1.default.cyan('/hooks') + chalk_1.default.gray(' - Test hooks functionality'));
console.log(chalk_1.default.cyan('/tools') + chalk_1.default.gray(' - Show available tools'));
console.log(chalk_1.default.cyan('/custom') + chalk_1.default.gray(' - Manage custom commands'));
console.log(chalk_1.default.cyan('/init-commands') + chalk_1.default.gray(' - Initialize genetic dev commands'));
console.log(chalk_1.default.cyan('/setup') + chalk_1.default.gray(' - Project setup and configuration'));
console.log(chalk_1.default.cyan('/history') + chalk_1.default.gray(' - Manage conversation history'));
console.log(chalk_1.default.cyan('/continue [session-id]') + chalk_1.default.gray(' - Continue from last or specific conversation'));
console.log(chalk_1.default.cyan('/clear') + chalk_1.default.gray(' - Clear conversation history'));
console.log(chalk_1.default.cyan('/memory') + chalk_1.default.gray(' - Edit memory files'));
console.log(chalk_1.default.cyan('/mcp') + chalk_1.default.gray(' - Manage MCP servers'));
console.log();
console.log(chalk_1.default.blue('💡 Tips:'));
console.log(chalk_1.default.gray('• Start with # to add content to memory'));
console.log(chalk_1.default.gray('• Use @ to search and reference files (@package.json, @recent, @git)'));
console.log(chalk_1.default.gray('• Create custom commands for repeated workflows'));
console.log(chalk_1.default.gray('• Use permission modes: ask, plan, auto, full'));
console.log(chalk_1.default.gray('• Use --continue to resume your last conversation'));
console.log(chalk_1.default.gray('• Type "exit" to quit'));
}
/**
* Show system status
*/
function showStatus(config) {
console.log(chalk_1.default.blue('📊 System Status:'));
console.log();
console.log(chalk_1.default.cyan('Model:'), config.model);
console.log(chalk_1.default.cyan('Permission Mode:'), config.permissions.mode);
console.log(chalk_1.default.cyan('Working Directory:'), process.cwd());
console.log(chalk_1.default.cyan('Memory Auto-load:'), config.memory.autoLoad ? '✅' : '❌');
console.log();
// API Keys status
console.log(chalk_1.default.blue('🔑 API Keys:'));
console.log(chalk_1.default.cyan('xAI:'), config.apiKeys.xai ? '✅ Configured' : '❌ Missing');
console.log(chalk_1.default.cyan('OpenAI:'), config.apiKeys.openai ? '✅ Configured' : '❌ Missing');
console.log(chalk_1.default.cyan('Composio:'), config.apiKeys.composio ? '✅ Configured' : '❌ Missing');
}
/**
* Show configuration
*/
async function showConfig(config) {
console.log(chalk_1.default.blue('⚙️ Configuration:'));
console.log(JSON.stringify(config, null, 2));
}
// Handle uncaught errors
process.on('uncaughtException', (error) => {
console.error(chalk_1.default.red('❌ Uncaught Exception:'), error);
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error(chalk_1.default.red('❌ Unhandled Rejection:'), reason);
process.exit(1);
});
// Start the CLI
if (require.main === module) {
main().catch((error) => {
console.error(chalk_1.default.red('❌ Fatal Error:'), error);
process.exit(1);
});
}
//# sourceMappingURL=cli.js.map