repository-analyzer
Version:
Transform code repositories into strategic intelligence using extensible AI agents. Analyze technical debt, business value, and deployment readiness automatically.
330 lines ⢠15 kB
JavaScript
;
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.AgentOrchestrator = exports.AgentDiscovery = exports.RepositoryAnalyzer = void 0;
const commander_1 = require("commander");
const inquirer_1 = __importDefault(require("inquirer"));
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const AgentOrchestrator_1 = require("./AgentOrchestrator");
Object.defineProperty(exports, "AgentOrchestrator", { enumerable: true, get: function () { return AgentOrchestrator_1.AgentOrchestrator; } });
const AgentDiscovery_1 = require("./AgentDiscovery");
Object.defineProperty(exports, "AgentDiscovery", { enumerable: true, get: function () { return AgentDiscovery_1.AgentDiscovery; } });
class RepositoryAnalyzer {
constructor() {
this.orchestrator = new AgentOrchestrator_1.AgentOrchestrator();
this.discovery = new AgentDiscovery_1.AgentDiscovery();
this.program = new commander_1.Command();
this.setupCLI();
}
setupCLI() {
this.program
.name('repo-analyze')
.description('Transform code repositories into strategic intelligence using extensible AI agents')
.version(this.getVersion())
.argument('[path]', 'Path to repository or directory containing repositories')
.option('--config', 'Interactive configuration setup')
.option('--validate', 'Validate system setup')
.option('--list-agents', 'List all available agents')
.option('--agents <list>', 'Comma-separated list of agents to use')
.option('--quick', 'Quick analysis mode (core agents only)')
.option('--deep', 'Deep analysis mode (all available agents)')
.option('--output <dir>', 'Custom output directory')
.option('--jobs <number>', 'Number of parallel jobs (1-8)', '3')
.option('--preview', 'Preview what would be analyzed')
.option('--no-open', 'Don\'t auto-open results')
.option('--claude-cmd <command>', 'Claude command to use', 'claude')
.option('--verbose', 'Verbose output');
}
getVersion() {
try {
const packagePath = path.join(__dirname, '..', 'package.json');
const packageJson = require(packagePath);
return packageJson.version;
}
catch {
return '1.0.0';
}
}
async main() {
try {
this.program.parse();
const options = this.program.opts();
const [targetPath] = this.program.args;
this.printBanner();
// Handle special commands
if (options.validate) {
const valid = await this.orchestrator.validateSetup(options.claudeCmd);
return valid ? 0 : 1;
}
if (options.listAgents) {
await this.orchestrator.listAgents();
return 0;
}
if (options.config) {
await this.interactiveConfig();
if (!targetPath)
return 0;
}
// Validate setup first
const setupValid = await this.orchestrator.validateSetup(options.claudeCmd);
if (!setupValid) {
console.log(chalk_1.default.red('\\nā Setup validation failed. Run with --validate for details.'));
return 1;
}
// Get target path
const analysisPath = targetPath || await this.promptForPath();
if (!analysisPath) {
console.log(chalk_1.default.red('ā No path provided for analysis'));
return 1;
}
// Validate path exists
if (!await fs.pathExists(analysisPath)) {
console.log(chalk_1.default.red(`ā Path does not exist: ${analysisPath}`));
return 1;
}
// Determine output directory
const outputDir = options.output || this.getDefaultOutputDir(analysisPath);
await fs.ensureDir(outputDir);
// Determine which agents to use
const selectedAgents = await this.determineAgents(options);
// Show preview if requested
if (options.preview) {
await this.showPreview(analysisPath, selectedAgents, outputDir);
return 0;
}
// Confirm execution
if (!await this.confirmExecution(analysisPath, selectedAgents, outputDir)) {
console.log(chalk_1.default.yellow('Analysis cancelled'));
return 0;
}
// Execute analysis
const results = await this.orchestrator.executeAnalysis({
repositoryPath: analysisPath,
outputDir,
selectedAgents,
parallelJobs: Math.min(Math.max(parseInt(options.jobs), 1), 8),
verbose: options.verbose,
claudeCommand: options.claudeCmd
});
// Display results
this.displayResults(results, outputDir);
// Auto-open results if requested
if (!options.noOpen) {
await this.openResults(outputDir);
}
const successCount = results.filter(r => r.success).length;
console.log(chalk_1.default.green(`\\nš Analysis complete! ${successCount}/${results.length} agents succeeded`));
return results.every(r => r.success) ? 0 : 1;
}
catch (error) {
console.error(chalk_1.default.red(`\\nā Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
if (this.program.opts().verbose) {
console.error(chalk_1.default.gray(error instanceof Error ? error.stack : 'No stack trace'));
}
return 1;
}
}
printBanner() {
console.log(chalk_1.default.blue(`
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā š Repository Analyzer v${this.getVersion().padEnd(17)}ā
ā Transform Code into Strategic Intelligence ā
ā Extensible AI Agents ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
`));
}
async promptForPath() {
const { path: targetPath } = await inquirer_1.default.prompt([
{
type: 'input',
name: 'path',
message: 'š Enter path to repository or directory:',
validate: (input) => {
if (!input.trim())
return 'Path is required';
return true;
}
}
]);
return targetPath.trim();
}
getDefaultOutputDir(analysisPath) {
const baseName = path.basename(analysisPath);
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
return path.join(process.cwd(), 'analysis', `${baseName}-${timestamp}`);
}
async determineAgents(options) {
if (options.agents) {
return options.agents.split(',').map((s) => s.trim());
}
if (options.quick) {
const coreAgents = await this.discovery.getAgentsByCategory('core');
return coreAgents.slice(0, 2).map(a => a.id); // Only first 2 core agents for quick mode
}
if (options.deep) {
const allAgents = await this.discovery.discoverAgents();
return allAgents.map(a => a.id);
}
// Default: use all core agents
const coreAgents = await this.discovery.getAgentsByCategory('core');
return coreAgents.map(a => a.id);
}
async showPreview(analysisPath, selectedAgents, outputDir) {
console.log(chalk_1.default.blue('\\nš Analysis Preview:'));
console.log(chalk_1.default.gray('ā'.repeat(50)));
console.log(`š Target: ${analysisPath}`);
console.log(`š Output: ${outputDir}`);
if (selectedAgents) {
console.log(`š¤ Agents: ${selectedAgents.length}`);
const agents = await this.discovery.discoverAgents();
const agentMap = new Map(agents.map(a => [a.id, a]));
for (const agentId of selectedAgents) {
const agent = agentMap.get(agentId);
if (agent) {
console.log(` ⢠${agent.name} (${agent.id})`);
}
}
}
// Estimate time
const agentCount = selectedAgents?.length || 4;
const estimatedMinutes = Math.ceil(agentCount * 2); // 2 minutes per agent average
console.log(`ā±ļø Estimated time: ~${estimatedMinutes} minutes`);
}
async confirmExecution(analysisPath, selectedAgents, outputDir) {
console.log(chalk_1.default.blue('\\nš Analysis Plan:'));
console.log(chalk_1.default.gray('ā'.repeat(50)));
await this.showPreview(analysisPath, selectedAgents, outputDir);
const { proceed } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'proceed',
message: 'š¤ Proceed with analysis?',
default: true
}
]);
return proceed;
}
displayResults(results, outputDir) {
console.log(chalk_1.default.blue('\\nš Analysis Results:'));
console.log(chalk_1.default.gray('ā'.repeat(50)));
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
if (successful.length > 0) {
console.log(chalk_1.default.green(`\\nā
Successful Agents (${successful.length}):`));
for (const result of successful) {
const time = (result.executionTime / 1000).toFixed(1);
console.log(` ⢠${result.agentId} (${time}s)`);
}
}
if (failed.length > 0) {
console.log(chalk_1.default.red(`\\nā Failed Agents (${failed.length}):`));
for (const result of failed) {
console.log(` ⢠${result.agentId}: ${result.error}`);
}
}
console.log(chalk_1.default.blue(`\\nš Results saved to: ${outputDir}`));
}
async openResults(outputDir) {
try {
const { execa } = await Promise.resolve().then(() => __importStar(require('execa')));
// Look for main report file
const reportFile = path.join(outputDir, 'analysis-report.md');
const fileToOpen = await fs.pathExists(reportFile) ? reportFile : outputDir;
if (process.platform === 'darwin') {
await execa('open', [fileToOpen]);
}
else if (process.platform === 'win32') {
await execa('start', [fileToOpen], { shell: true });
}
else {
await execa('xdg-open', [fileToOpen]);
}
}
catch {
// Silently ignore if we can't open
console.log(chalk_1.default.gray(`\\nOpen manually: ${outputDir}`));
}
}
async interactiveConfig() {
console.log(chalk_1.default.blue('\\nāļø Interactive Configuration'));
console.log(chalk_1.default.gray('ā'.repeat(50)));
const agents = await this.discovery.discoverAgents();
const coreAgents = agents.filter(a => a.category === 'core');
const customAgents = agents.filter(a => a.category !== 'core');
console.log(`\\nš§ Found ${coreAgents.length} core agents`);
if (customAgents.length > 0) {
console.log(`šØ Found ${customAgents.length} custom agents`);
}
const { showAgents } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'showAgents',
message: 'Would you like to see available agents?',
default: true
}
]);
if (showAgents) {
this.discovery.printAgentList(agents);
}
const { claudeCommand } = await inquirer_1.default.prompt([
{
type: 'input',
name: 'claudeCommand',
message: 'Claude command (if not "claude"):',
default: 'claude'
}
]);
// Test Claude command
const spinner = (0, ora_1.default)('Testing Claude CLI...').start();
const setupValid = await this.orchestrator.validateSetup(claudeCommand);
if (setupValid) {
spinner.succeed('Claude CLI is working!');
}
else {
spinner.fail('Claude CLI test failed');
console.log(chalk_1.default.yellow('Please ensure Claude CLI is installed: https://claude.ai/code'));
}
console.log(chalk_1.default.green('\\nā
Configuration complete!'));
console.log(chalk_1.default.blue('Now you can run: repo-analyze <path-to-analyze>'));
}
}
exports.RepositoryAnalyzer = RepositoryAnalyzer;
//# sourceMappingURL=index.js.map