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

380 lines 14 kB
"use strict"; /** * File reference system for @ commands * Provides file discovery, selection, and reference management */ 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.FileReferenceManager = void 0; const globby_1 = require("globby"); const fs_extra_1 = require("fs-extra"); const path_1 = require("path"); /** * File reference manager for @ command system */ class FileReferenceManager { constructor(baseDir = process.cwd()) { this.gitTrackedFiles = new Set(); this.fileCache = new Map(); this.lastCacheUpdate = 0; this.cacheTimeout = 30000; // 30 seconds this.baseDir = (0, path_1.resolve)(baseDir); } /** * Search for files matching a pattern */ async searchFiles(query, options = {}) { const { maxResults = 50, includeDirectories = false, extensions = [], sortBy = 'relevance' } = options; // Update cache if needed await this.updateCacheIfNeeded(); let files = Array.from(this.fileCache.values()); // Filter by type if (!includeDirectories) { files = files.filter(f => f.type === 'file'); } // Filter by extensions if (extensions.length > 0) { const extSet = new Set(extensions.map(ext => ext.toLowerCase())); files = files.filter(f => f.extension && extSet.has(f.extension.toLowerCase())); } // Filter by query if (query) { const queryLower = query.toLowerCase(); files = files.filter(f => { const name = f.name.toLowerCase(); const path = f.relativePath.toLowerCase(); return name.includes(queryLower) || path.includes(queryLower) || this.fuzzyMatch(name, queryLower) || this.fuzzyMatch(path, queryLower); }); } // Sort files files = this.sortFiles(files, sortBy, query); // Limit results return files.slice(0, maxResults); } /** * Get file suggestions for autocomplete */ async getFileSuggestions(partial) { // If partial contains path separators, treat as path if (partial.includes('/') || partial.includes('\\')) { return this.getPathSuggestions(partial); } // Otherwise, search by filename return this.searchFiles(partial, { maxResults: 20, sortBy: 'relevance' }); } /** * Get path-based suggestions */ async getPathSuggestions(partial) { const normalizedPartial = partial.replace(/\\/g, '/'); const pathParts = normalizedPartial.split('/'); const directory = pathParts.slice(0, -1).join('/'); const filename = pathParts[pathParts.length - 1]; const searchDir = directory ? (0, path_1.resolve)(this.baseDir, directory) : this.baseDir; try { const entries = await (0, fs_extra_1.readdir)(searchDir, { withFileTypes: true }); const files = []; for (const entry of entries) { if (filename && !entry.name.toLowerCase().includes(filename.toLowerCase())) { continue; } const fullPath = (0, path_1.join)(searchDir, entry.name); const relativePath = (0, path_1.relative)(this.baseDir, fullPath); const stats = await (0, fs_extra_1.stat)(fullPath); const extension = entry.isFile() ? (0, path_1.extname)(entry.name).slice(1) : ''; const fileRef = { path: fullPath, relativePath, name: entry.name, size: stats.size, modified: stats.mtime, type: entry.isDirectory() ? 'directory' : 'file', isGitTracked: this.gitTrackedFiles.has(relativePath), ...(extension && { extension }), }; files.push(fileRef); } return files.slice(0, 20); } catch { return []; } } /** * Get file details by path */ async getFileDetails(path) { const resolvedPath = (0, path_1.resolve)(this.baseDir, path); const relativePath = (0, path_1.relative)(this.baseDir, resolvedPath); try { const stats = await (0, fs_extra_1.stat)(resolvedPath); const extension = stats.isFile() ? (0, path_1.extname)(resolvedPath).slice(1) : ''; const fileRef = { path: resolvedPath, relativePath, name: (0, path_1.basename)(resolvedPath), size: stats.size, modified: stats.mtime, type: stats.isDirectory() ? 'directory' : 'file', isGitTracked: this.gitTrackedFiles.has(relativePath), ...(extension && { extension }), }; return fileRef; } catch { return null; } } /** * Get recently modified files */ async getRecentFiles(limit = 20) { await this.updateCacheIfNeeded(); return Array.from(this.fileCache.values()) .filter(f => f.type === 'file') .sort((a, b) => b.modified.getTime() - a.modified.getTime()) .slice(0, limit); } /** * Get git tracked files */ async getGitTrackedFiles() { await this.updateGitTrackedFiles(); return Array.from(this.fileCache.values()) .filter(f => f.isGitTracked); } /** * Get files by extension */ async getFilesByExtension(extension) { await this.updateCacheIfNeeded(); const ext = extension.toLowerCase(); return Array.from(this.fileCache.values()) .filter(f => f.extension?.toLowerCase() === ext); } /** * Get commonly used file extensions in project */ async getCommonExtensions() { await this.updateCacheIfNeeded(); const extensionCounts = new Map(); for (const file of this.fileCache.values()) { if (file.extension && file.type === 'file') { const ext = file.extension.toLowerCase(); extensionCounts.set(ext, (extensionCounts.get(ext) || 0) + 1); } } return Array.from(extensionCounts.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 20) .map(([ext]) => ext); } /** * Update file cache if needed */ async updateCacheIfNeeded() { const now = Date.now(); if (now - this.lastCacheUpdate < this.cacheTimeout) { return; } await this.updateFileCache(); await this.updateGitTrackedFiles(); this.lastCacheUpdate = now; } /** * Update the file cache */ async updateFileCache() { try { const patterns = [ '**/*', '!node_modules/**', '!.git/**', '!dist/**', '!build/**', '!coverage/**', '!.next/**', '!.nuxt/**', '!.vscode/**', '!.idea/**', ]; const files = await (0, globby_1.globby)(patterns, { cwd: this.baseDir, absolute: true, onlyFiles: false, }); this.fileCache.clear(); for (const filePath of files) { const stats = await (0, fs_extra_1.stat)(filePath); const relativePath = (0, path_1.relative)(this.baseDir, filePath); const extension = stats.isFile() ? (0, path_1.extname)(filePath).slice(1) : ''; const fileRef = { path: filePath, relativePath, name: (0, path_1.basename)(filePath), size: stats.size, modified: stats.mtime, type: stats.isDirectory() ? 'directory' : 'file', isGitTracked: false, // Will be updated separately ...(extension && { extension }), }; this.fileCache.set(relativePath, fileRef); } } catch (error) { console.error('Failed to update file cache:', error); } } /** * Update git tracked files */ async updateGitTrackedFiles() { try { const { execa } = await Promise.resolve().then(() => __importStar(require('execa'))); const result = await execa('git', ['ls-files'], { cwd: this.baseDir, reject: false, }); this.gitTrackedFiles.clear(); if (result.exitCode === 0) { const files = result.stdout.split('\n').filter(Boolean); for (const file of files) { this.gitTrackedFiles.add(file); // Update cache entry if it exists const cached = this.fileCache.get(file); if (cached) { cached.isGitTracked = true; } } } } catch { // Git not available or not in a git repo } } /** * Fuzzy matching for file names */ fuzzyMatch(text, pattern) { const textLower = text.toLowerCase(); const patternLower = pattern.toLowerCase(); let patternIndex = 0; for (let i = 0; i < textLower.length && patternIndex < patternLower.length; i++) { if (textLower[i] === patternLower[patternIndex]) { patternIndex++; } } return patternIndex === patternLower.length; } /** * Sort files based on criteria */ sortFiles(files, sortBy, query) { switch (sortBy) { case 'name': return files.sort((a, b) => a.name.localeCompare(b.name)); case 'modified': return files.sort((a, b) => b.modified.getTime() - a.modified.getTime()); case 'size': return files.sort((a, b) => b.size - a.size); case 'relevance': default: if (!query) { // Default to modified time if no query return files.sort((a, b) => b.modified.getTime() - a.modified.getTime()); } return files.sort((a, b) => { const aScore = this.calculateRelevanceScore(a, query); const bScore = this.calculateRelevanceScore(b, query); return bScore - aScore; }); } } /** * Calculate relevance score for file matching */ calculateRelevanceScore(file, query) { const queryLower = query.toLowerCase(); const nameLower = file.name.toLowerCase(); const pathLower = file.relativePath.toLowerCase(); let score = 0; // Exact name match gets highest score if (nameLower === queryLower) score += 100; // Name starts with query if (nameLower.startsWith(queryLower)) score += 50; // Name contains query if (nameLower.includes(queryLower)) score += 25; // Path contains query if (pathLower.includes(queryLower)) score += 10; // Fuzzy match if (this.fuzzyMatch(nameLower, queryLower)) score += 5; // Git tracked files get slight boost if (file.isGitTracked) score += 2; // Recent files get slight boost const daysSinceModified = (Date.now() - file.modified.getTime()) / (1000 * 60 * 60 * 24); if (daysSinceModified < 7) score += 3; if (daysSinceModified < 1) score += 2; // Common development file extensions get boost const importantExtensions = ['ts', 'js', 'py', 'md', 'json', 'yaml', 'yml']; if (file.extension && importantExtensions.includes(file.extension.toLowerCase())) { score += 1; } return score; } /** * Set base directory */ setBaseDirectory(dir) { this.baseDir = (0, path_1.resolve)(dir); this.fileCache.clear(); this.lastCacheUpdate = 0; } } exports.FileReferenceManager = FileReferenceManager; //# sourceMappingURL=file-reference.js.map