UNPKG

betterpack

Version:

A Universal Node.js Package Manager CLI with automated agent capabilities

287 lines (240 loc) 8.29 kB
const { ProjectAnalyzer } = require("./project-analyzer.cjs") const { executeCommand } = require("../index.cjs") const fs = require("fs") const path = require("path") class AutomatedAgent { constructor(options = {}) { this.options = { autoFix: options.autoFix || false, watchMode: options.watchMode || false, aggressiveness: options.aggressiveness || "moderate", // conservative, moderate, aggressive projectPath: options.projectPath || process.cwd(), ...options, } this.analyzer = new ProjectAnalyzer(this.options.projectPath) this.isRunning = false this.lastAnalysis = null this.actionHistory = [] } async start() { console.log("[Agent] Starting automated project management...") this.isRunning = true // Initial analysis await this.performAnalysis() // Execute initial recommendations if (this.options.autoFix) { await this.executeRecommendations() } // Start watch mode if enabled if (this.options.watchMode) { this.startWatching() } return this.lastAnalysis } async stop() { console.log("[Agent] Stopping automated project management...") this.isRunning = false if (this.watcher) { this.watcher.close() } } async performAnalysis() { console.log("[Agent] Performing project analysis...") this.lastAnalysis = await this.analyzer.analyzeProject() const summary = this.analyzer.getAnalysisSummary() console.log(`[Agent] Analysis complete - Health: ${summary.healthStatus} (${summary.healthScore}/100)`) console.log(`[Agent] Found ${summary.issuesCount} issues and ${summary.recommendationsCount} recommendations`) return this.lastAnalysis } async executeRecommendations() { if (!this.lastAnalysis || !this.options.autoFix) { return } console.log("[Agent] Executing automated fixes...") for (const issue of this.lastAnalysis.issues) { if (this.shouldAutoFix(issue)) { await this.executeAutoFix(issue) } } for (const recommendation of this.lastAnalysis.recommendations) { if (this.shouldExecuteRecommendation(recommendation)) { await this.executeRecommendation(recommendation) } } } shouldAutoFix(issue) { const { aggressiveness } = this.options // Conservative: only fix critical errors if (aggressiveness === "conservative") { return issue.severity === "high" && issue.type === "error" } // Moderate: fix errors and important warnings if (aggressiveness === "moderate") { return issue.severity === "high" || (issue.severity === "medium" && issue.type === "error") } // Aggressive: fix most issues if (aggressiveness === "aggressive") { return issue.severity !== "low" } return false } shouldExecuteRecommendation(recommendation) { const { aggressiveness } = this.options // Conservative: only security fixes if (aggressiveness === "conservative") { return recommendation.type === "security" } // Moderate: security and performance if (aggressiveness === "moderate") { return ["security", "performance"].includes(recommendation.type) } // Aggressive: all recommendations except workflow changes if (aggressiveness === "aggressive") { return recommendation.type !== "workflow" } return false } async executeAutoFix(issue) { if (!issue.fix) return console.log(`[Agent] Auto-fixing: ${issue.message}`) try { // Parse and execute the fix command const command = issue.fix.match(/"([^"]+)"/)?.[1] || issue.fix if ( command.startsWith("bpack ") || command.startsWith("npm ") || command.startsWith("yarn ") || command.startsWith("pnpm ") ) { // Execute package management commands const parts = command.split(" ") const cmd = parts[0] const args = parts.slice(1) if (cmd === "bpack") { // Use our own CLI const { runCli } = require("../index.cjs") process.argv = ["node", "bpack", ...args] await runCli() } else { // Execute external command executeCommand(cmd, args) } this.actionHistory.push({ timestamp: new Date(), action: "auto-fix", issue: issue.message, command: command, status: "success", }) } } catch (error) { console.error(`[Agent] Failed to auto-fix: ${issue.message}`, error.message) this.actionHistory.push({ timestamp: new Date(), action: "auto-fix", issue: issue.message, command: issue.fix, status: "failed", error: error.message, }) } } async executeRecommendation(recommendation) { console.log(`[Agent] Executing recommendation: ${recommendation.message}`) try { // Parse and execute the recommendation action const command = recommendation.action.match(/"([^"]+)"/)?.[1] || recommendation.action if (command.startsWith("Run ")) { const actualCommand = command.replace("Run ", "").replace(/"/g, "") const parts = actualCommand.split(" ") const cmd = parts[0] const args = parts.slice(1) if (cmd === "bpack") { const { runCli } = require("../index.cjs") process.argv = ["node", "bpack", ...args] await runCli() } else { executeCommand(cmd, args) } this.actionHistory.push({ timestamp: new Date(), action: "recommendation", type: recommendation.type, message: recommendation.message, command: actualCommand, status: "success", }) } } catch (error) { console.error(`[Agent] Failed to execute recommendation: ${recommendation.message}`, error.message) this.actionHistory.push({ timestamp: new Date(), action: "recommendation", type: recommendation.type, message: recommendation.message, command: recommendation.action, status: "failed", error: error.message, }) } } startWatching() { console.log("[Agent] Starting file system monitoring...") const chokidar = require("chokidar") const watchPaths = [ path.join(this.options.projectPath, "package.json"), path.join(this.options.projectPath, "package-lock.json"), path.join(this.options.projectPath, "yarn.lock"), path.join(this.options.projectPath, "pnpm-lock.yaml"), path.join(this.options.projectPath, "bun.lockb"), ] this.watcher = chokidar.watch(watchPaths, { ignored: /node_modules/, persistent: true, }) this.watcher.on("change", async (filePath) => { console.log(`[Agent] Detected change in ${path.basename(filePath)}`) // Debounce rapid changes clearTimeout(this.watchTimeout) this.watchTimeout = setTimeout(async () => { await this.performAnalysis() if (this.options.autoFix) { await this.executeRecommendations() } }, 2000) }) } getStatus() { return { isRunning: this.isRunning, lastAnalysis: this.lastAnalysis ? this.analyzer.getAnalysisSummary() : null, actionHistory: this.actionHistory.slice(-10), // Last 10 actions options: this.options, } } async generateReport() { if (!this.lastAnalysis) { await this.performAnalysis() } const report = { timestamp: new Date(), project: { path: this.options.projectPath, type: this.lastAnalysis.structure.projectType, frameworks: this.lastAnalysis.structure.frameworks, buildTools: this.lastAnalysis.structure.buildTools, }, health: this.lastAnalysis.health, dependencies: { total: this.lastAnalysis.dependencies.total, outdated: this.lastAnalysis.dependencies.outdated.length, vulnerable: this.lastAnalysis.dependencies.vulnerable.length, }, issues: this.lastAnalysis.issues, recommendations: this.lastAnalysis.recommendations, actions: this.actionHistory, } return report } } module.exports = { AutomatedAgent }