UNPKG

@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

349 lines (335 loc) 12.5 kB
"use strict"; /** * Memory System for Grok CLI * Handles GROK.md files and project context injection */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MemoryManager = void 0; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); /** * Memory Manager - handles GROK.md files and project context */ class MemoryManager { constructor(baseDir, config) { this.memoryFiles = new Map(); this.baseDir = baseDir; this.config = config; } /** * Load all memory files from the project */ async loadMemoryFiles() { console.log(chalk_1.default.blue('🧠 Loading memory files...')); this.memoryFiles.clear(); try { // Load GROK.md from current directory await this.loadMemoryFile(path_1.default.join(this.baseDir, 'GROK.md'), 'project'); // Load GROK.md from parent directories (up to maxDepth) await this.loadParentMemoryFiles(); // Load user-level memory await this.loadUserMemory(); console.log(chalk_1.default.green(`✅ Loaded ${this.memoryFiles.size} memory files`)); } catch (error) { console.warn(chalk_1.default.yellow('⚠️ No memory files found or error loading:'), error instanceof Error ? error.message : String(error)); } } /** * Load GROK.md files from parent directories */ async loadParentMemoryFiles() { let currentDir = path_1.default.dirname(this.baseDir); let depth = 0; while (depth < this.config.maxDepth && currentDir !== path_1.default.dirname(currentDir)) { const memoryPath = path_1.default.join(currentDir, 'GROK.md'); try { await this.loadMemoryFile(memoryPath, 'local'); } catch { // File doesn't exist, continue } currentDir = path_1.default.dirname(currentDir); depth++; } } /** * Load user-level memory from home directory */ async loadUserMemory() { const homeDir = process.env['HOME'] || process.env['USERPROFILE']; if (!homeDir) return; const userMemoryPath = path_1.default.join(homeDir, '.grok-cli', 'GROK.md'); try { await this.loadMemoryFile(userMemoryPath, 'user'); } catch { // User memory file doesn't exist, that's fine } } /** * Load a specific memory file */ async loadMemoryFile(filePath, type) { try { const content = await fs_1.promises.readFile(filePath, 'utf-8'); const imports = this.extractImports(content); const memoryFile = { path: filePath, content, imports, type }; this.memoryFiles.set(filePath, memoryFile); console.log(chalk_1.default.gray(` 📄 Loaded ${type} memory: ${path_1.default.relative(this.baseDir, filePath)}`)); // Load imported files for (const importPath of imports) { await this.loadImportedFile(importPath, filePath); } } catch (error) { throw new Error(`Failed to load memory file ${filePath}: ${error instanceof Error ? error.message : String(error)}`); } } /** * Load files referenced by memory files */ async loadImportedFile(importPath, fromFile) { try { const resolvedPath = path_1.default.resolve(path_1.default.dirname(fromFile), importPath); // Check if path should be excluded if (this.shouldExcludePath(resolvedPath)) { return; } const content = await fs_1.promises.readFile(resolvedPath, 'utf-8'); const imports = this.extractImports(content); const memoryFile = { path: resolvedPath, content, imports, type: 'local' }; this.memoryFiles.set(resolvedPath, memoryFile); console.log(chalk_1.default.gray(` 🔗 Imported: ${path_1.default.relative(this.baseDir, resolvedPath)}`)); } catch (error) { console.warn(chalk_1.default.yellow(`⚠️ Failed to load import ${importPath}:`), error instanceof Error ? error.message : String(error)); } } /** * Extract imports from memory file content */ extractImports(content) { const imports = []; // Look for @import directives const importRegex = /@import\s+["']([^"']+)["']/g; let match; while ((match = importRegex.exec(content)) !== null) { if (match[1]) { imports.push(match[1]); } } // Look for file references const fileRefRegex = /\[([^\]]+)\]\(([^)]+)\)/g; while ((match = fileRefRegex.exec(content)) !== null) { const filePath = match[2]; if (filePath && (filePath.startsWith('./') || filePath.startsWith('../') || !filePath.includes('://'))) { imports.push(filePath); } } return imports; } /** * Check if a path should be excluded based on configuration */ shouldExcludePath(filePath) { const relativePath = path_1.default.relative(this.baseDir, filePath); return this.config.excludePatterns.some(pattern => { // Convert glob pattern to regex const regexPattern = pattern .replace(/\*\*/g, '.*') .replace(/\*/g, '[^/]*') .replace(/\?/g, '[^/]'); const regex = new RegExp(`^${regexPattern}$`); return regex.test(relativePath); }); } /** * Get compiled memory content for injection into conversations */ getMemoryContent() { if (this.memoryFiles.size === 0) { return ''; } const sections = []; // Add user memory first const userMemory = Array.from(this.memoryFiles.values()).filter(f => f.type === 'user'); if (userMemory.length > 0) { sections.push('# User Context\n'); userMemory.forEach(file => { sections.push(`## ${path_1.default.basename(file.path, '.md')}\n${file.content}\n`); }); } // Add project memory const projectMemory = Array.from(this.memoryFiles.values()).filter(f => f.type === 'project'); if (projectMemory.length > 0) { sections.push('# Project Context\n'); projectMemory.forEach(file => { sections.push(`## ${path_1.default.basename(file.path, '.md')}\n${file.content}\n`); }); } // Add local memory const localMemory = Array.from(this.memoryFiles.values()).filter(f => f.type === 'local'); if (localMemory.length > 0) { sections.push('# Local Context\n'); localMemory.forEach(file => { const relativePath = path_1.default.relative(this.baseDir, file.path); sections.push(`## ${relativePath}\n${file.content}\n`); }); } return sections.join('\n'); } /** * Get memory statistics */ getMemoryStats() { const files = Array.from(this.memoryFiles.values()); return { totalFiles: files.length, userFiles: files.filter(f => f.type === 'user').length, projectFiles: files.filter(f => f.type === 'project').length, localFiles: files.filter(f => f.type === 'local').length, totalSize: files.reduce((sum, f) => sum + f.content.length, 0) }; } /** * Get all loaded memory files */ getMemoryFiles() { return Array.from(this.memoryFiles.values()); } /** * Add content to memory */ async addToMemory(content, type = 'note') { const memoryPath = path_1.default.join(this.baseDir, 'GROK.md'); try { let existingContent = ''; try { existingContent = await fs_1.promises.readFile(memoryPath, 'utf-8'); } catch { // File doesn't exist yet } const timestamp = new Date().toISOString(); const newEntry = `\n## ${type === 'note' ? 'Note' : 'Context'} - ${timestamp}\n\n${content}\n`; const updatedContent = existingContent + newEntry; await fs_1.promises.writeFile(memoryPath, updatedContent, 'utf-8'); // Reload memory files to include the new content await this.loadMemoryFiles(); console.log(chalk_1.default.green(`✅ Added to memory: ${type}`)); } catch (error) { console.error(chalk_1.default.red('❌ Failed to add to memory:'), error instanceof Error ? error.message : String(error)); } } /** * Create a new GROK.md file with template */ async createMemoryFile(filePath) { const targetPath = filePath || path_1.default.join(this.baseDir, 'GROK.md'); const template = `# Grok CLI Memory ## Project Overview Describe your project goals, architecture, and key concepts here. ## Key Files List and describe the most important files in your project: - \`src/\` - Main source code - \`tests/\` - Test files - \`docs/\` - Documentation ## Development Workflow Document your development process: - Build: \`npm run build\` - Test: \`npm test\` - Deploy: \`npm run deploy\` ## Important Notes Add any important notes, decisions, or context that should be remembered across sessions. ## Custom Commands Document project-specific workflows and commands: - Setup: \`/custom run setup-dev\` - Test: \`/custom run run-tests\` ## Dependencies Key dependencies and their purposes: - **Framework**: What framework you're using and why - **Tools**: Important development tools - **Services**: External services and APIs ## Architecture Decisions Record important architectural decisions and their rationales. `; try { // Check if file already exists try { await fs_1.promises.access(targetPath); console.log(chalk_1.default.yellow(`⚠️ Memory file already exists: ${targetPath}`)); return; } catch { // File doesn't exist, proceed } await fs_1.promises.writeFile(targetPath, template, 'utf-8'); console.log(chalk_1.default.green(`✅ Created memory file: ${targetPath}`)); // Load the new file await this.loadMemoryFiles(); } catch (error) { console.error(chalk_1.default.red('❌ Failed to create memory file:'), error instanceof Error ? error.message : String(error)); } } /** * Search memory content */ searchMemory(query) { const results = []; const searchRegex = new RegExp(query, 'gi'); for (const file of this.memoryFiles.values()) { const lines = file.content.split('\n'); const matches = []; lines.forEach((line, index) => { if (searchRegex.test(line)) { const start = Math.max(0, index - 2); const end = Math.min(lines.length - 1, index + 2); const context = lines.slice(start, end + 1).join('\n'); matches.push({ line: index + 1, content: line, context }); } }); if (matches.length > 0) { results.push({ file, matches }); } } return results; } /** * Clear all loaded memory */ clearMemory() { this.memoryFiles.clear(); console.log(chalk_1.default.blue('🧠 Memory cleared')); } /** * Reload memory files */ async reloadMemory() { console.log(chalk_1.default.blue('🔄 Reloading memory files...')); await this.loadMemoryFiles(); } } exports.MemoryManager = MemoryManager; //# sourceMappingURL=memory.js.map