UNPKG

hikma-engine

Version:

Code Knowledge Graph Indexer - A sophisticated TypeScript-based indexer that transforms Git repositories into multi-dimensional knowledge stores for AI agents

274 lines (271 loc) • 10.1 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphQueryCLI = void 0; // Load environment variables from .env file require("dotenv/config"); const connection_1 = require("../persistence/db/connection"); const in_memory_graph_1 = require("../utils/in-memory-graph"); const logger_1 = require("../utils/logger"); const error_handling_1 = require("../utils/error-handling"); const path_1 = __importDefault(require("path")); /** * CLI utility for querying the graph data in memory * Usage: npm run graph-query [command] [args...] */ const logger = (0, logger_1.getLogger)('GraphQueryCLI'); class GraphQueryCLI { constructor() { // Initialize SQLite client with the metadata database const dbPath = path_1.default.join(process.cwd(), 'data', 'metadata.db'); this.sqliteClient = new connection_1.SQLiteClient(dbPath); this.graphService = new in_memory_graph_1.InMemoryGraphService(this.sqliteClient); } async initialize() { try { await this.sqliteClient.connect(); await this.graphService.loadGraph(); console.log('āœ… Graph loaded successfully into memory'); } catch (error) { console.error('āŒ Failed to initialize graph:', (0, error_handling_1.getErrorMessage)(error)); throw error; } } async cleanup() { try { await this.sqliteClient.disconnect(); } catch (error) { logger.error('Error during cleanup', { error: (0, error_handling_1.getErrorMessage)(error) }); } } /** * Show graph statistics */ showStats() { const stats = this.graphService.getStats(); console.log('\nšŸ“Š Graph Statistics:'); console.log(`Total Nodes: ${stats.nodeCount}`); console.log(`Total Edges: ${stats.edgeCount}`); console.log('\nNode Types:'); for (const [type, count] of Object.entries(stats.nodeTypes)) { console.log(` ${type}: ${count}`); } console.log('\nEdge Types:'); for (const [type, count] of Object.entries(stats.edgeTypes)) { console.log(` ${type}: ${count}`); } } /** * List all functions in a file */ listFunctionsInFile(filePath) { const functions = this.graphService.getFunctionsInFile(filePath); console.log(`\nšŸ” Functions in ${filePath}:`); if (functions.length === 0) { console.log(' No functions found'); return; } functions.forEach(func => { const name = func.properties.name || 'anonymous'; const line = func.line || 'unknown'; console.log(` ${name} (line ${line})`); }); } /** * Show function call relationships */ showFunctionCalls(functionName) { // Find function by name const functions = this.graphService.getNodesByType('FunctionNode') .filter(func => func.properties.name === functionName); if (functions.length === 0) { console.log(`āŒ Function '${functionName}' not found`); return; } if (functions.length > 1) { console.log(`āš ļø Multiple functions named '${functionName}' found:`); functions.forEach((func, index) => { console.log(` ${index + 1}. ${func.filePath}:${func.line}`); }); return; } const func = functions[0]; console.log(`\nšŸ“ž Function Calls for '${functionName}' (${func.filePath}:${func.line}):`); // Show what this function calls const calls = this.graphService.getFunctionCalls(func.id); console.log(`\nCalls (${calls.length}):`); calls.forEach(calledFunc => { const name = calledFunc.properties.name || 'anonymous'; console.log(` → ${name} (${calledFunc.filePath}:${calledFunc.line})`); }); // Show what calls this function const callers = this.graphService.getFunctionCallers(func.id); console.log(`\nCalled by (${callers.length}):`); callers.forEach(callerFunc => { const name = callerFunc.properties.name || 'anonymous'; console.log(` ← ${name} (${callerFunc.filePath}:${callerFunc.line})`); }); } /** * Find call chain between two functions */ findCallChain(fromFunction, toFunction) { // Find functions by name const fromFuncs = this.graphService.getNodesByType('FunctionNode') .filter(func => func.properties.name === fromFunction); const toFuncs = this.graphService.getNodesByType('FunctionNode') .filter(func => func.properties.name === toFunction); if (fromFuncs.length === 0) { console.log(`āŒ Source function '${fromFunction}' not found`); return; } if (toFuncs.length === 0) { console.log(`āŒ Target function '${toFunction}' not found`); return; } if (fromFuncs.length > 1 || toFuncs.length > 1) { console.log('āš ļø Multiple functions found. Please be more specific.'); return; } const chain = this.graphService.findCallChain(fromFuncs[0].id, toFuncs[0].id); if (!chain) { console.log(`āŒ No call chain found from '${fromFunction}' to '${toFunction}'`); return; } console.log(`\nšŸ”— Call chain from '${fromFunction}' to '${toFunction}':`); chain.forEach((func, index) => { const name = func.properties.name || 'anonymous'; const arrow = index < chain.length - 1 ? ' →' : ''; console.log(` ${index + 1}. ${name} (${func.filePath}:${func.line})${arrow}`); }); } /** * Search functions by pattern */ searchFunctions(pattern) { const regex = new RegExp(pattern, 'i'); const functions = this.graphService.searchNodes(node => node.nodeType === 'FunctionNode' && regex.test(node.properties.name || '')); console.log(`\nšŸ” Functions matching '${pattern}':`); if (functions.length === 0) { console.log(' No functions found'); return; } functions.forEach(func => { const name = func.properties.name || 'anonymous'; console.log(` ${name} (${func.filePath}:${func.line})`); }); } /** * Show file dependencies */ showFileDependencies(filePath) { const dependents = this.graphService.getFileDependents(filePath); console.log(`\nšŸ“ Files that depend on '${filePath}':`); if (dependents.length === 0) { console.log(' No dependencies found'); return; } dependents.forEach(file => { console.log(` ${file.filePath}`); }); } /** * Show help */ showHelp() { console.log(` šŸ“– Graph Query CLI Usage: Commands: stats Show graph statistics functions <file-path> List functions in a file calls <function-name> Show function call relationships chain <from-func> <to-func> Find call chain between functions search <pattern> Search functions by name pattern deps <file-path> Show file dependencies help Show this help Examples: npm run graph-query stats npm run graph-query functions src/utils/logger.ts npm run graph-query calls getLogger npm run graph-query chain main processFile npm run graph-query search "test" npm run graph-query deps src/utils/logger.ts `); } } exports.GraphQueryCLI = GraphQueryCLI; async function main() { const args = process.argv.slice(2); const command = args[0]; if (!command || command === 'help') { const cli = new GraphQueryCLI(); cli.showHelp(); return; } const cli = new GraphQueryCLI(); try { await cli.initialize(); switch (command) { case 'stats': cli.showStats(); break; case 'functions': if (!args[1]) { console.log('āŒ Please provide a file path'); return; } cli.listFunctionsInFile(args[1]); break; case 'calls': if (!args[1]) { console.log('āŒ Please provide a function name'); return; } cli.showFunctionCalls(args[1]); break; case 'chain': if (!args[1] || !args[2]) { console.log('āŒ Please provide both source and target function names'); return; } cli.findCallChain(args[1], args[2]); break; case 'search': if (!args[1]) { console.log('āŒ Please provide a search pattern'); return; } cli.searchFunctions(args[1]); break; case 'deps': if (!args[1]) { console.log('āŒ Please provide a file path'); return; } cli.showFileDependencies(args[1]); break; default: console.log(`āŒ Unknown command: ${command}`); cli.showHelp(); } } catch (error) { console.error('āŒ Error:', (0, error_handling_1.getErrorMessage)(error)); process.exit(1); } finally { await cli.cleanup(); } } if (require.main === module) { main().catch(error => { console.error('āŒ Unhandled error:', (0, error_handling_1.getErrorMessage)(error)); process.exit(1); }); }