abyss-ai
Version:
Autonomous AI coding agent - enhanced OpenCode with autonomous capabilities
216 lines (186 loc) • 8.06 kB
text/typescript
import { z } from "zod"
import { NamedError } from "../util/error"
import { Tool } from "./tool"
import { AgentMemory } from "../agent/memory/agent-memory"
import { BatchProcessor } from "../cli/cmd/batch-processor"
import path from "path"
export const YoloTool = Tool.define({
id: "yolo",
description: "Autonomous mode - analyze and fix code without permission prompts",
input: z.object({
file_path: z.string().optional().describe("File path to analyze autonomously"),
directory: z.string().optional().describe("Directory to process recursively"),
dry_run: z.boolean().optional().default(false).describe("Preview changes without applying them"),
max_files: z.number().optional().default(20).describe("Maximum number of files to process"),
backup: z.boolean().optional().default(true).describe("Create backup before changes"),
}),
handler: async (input: any, { cwd }: { cwd: string }) => {
try {
// Initialize memory system
const agentMemory = new AgentMemory(cwd)
if (input.directory) {
// Batch processing for directory
const batchProcessor = new BatchProcessor({
directory: path.resolve(cwd, input.directory),
filePatterns: ["**/*.{js,ts,jsx,tsx,py,java,cpp,c,go,rs}"],
excludePatterns: ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**"],
maxFiles: input.max_files,
concurrent: 3,
dryRun: input.dry_run,
backupEnabled: input.backup
})
const files = await batchProcessor.findFiles()
if (input.dry_run) {
return {
type: "success",
message: `🔍 DRY RUN: Found ${files.length} files to process`,
files: files.map(f => path.relative(cwd, f))
}
}
// Process files with autonomous analysis
const results = await batchProcessor.processBatch(async (filePath: string) => {
const fileStartTime = Date.now()
const content = await Bun.file(filePath).text()
const fileType = detectLanguage(filePath)
// Get contextual advice from memory
const advice = agentMemory.generateContextualAdvice(filePath, fileType)
// Simple autonomous analysis (integrated with existing tool system)
const issues = analyzeCode(content, fileType)
const fixes = generateFixes(content, issues, advice)
// Record memory for learning
agentMemory.addMemory({
filePath,
fileType,
issuesFound: issues,
changesApplied: fixes,
successMetrics: {
syntaxValid: fixes.length > 0,
testsPass: false, // Could be enhanced
lintClean: true
},
patterns: {
commonIssues: issues,
effectiveFixes: fixes,
riskySections: []
},
context: {
language: fileType,
complexity: assessComplexity(content)
}
})
return {
file: path.relative(cwd, filePath),
success: true,
changes: fixes.length,
issues: issues.length,
processingTime: Date.now() - fileStartTime
}
})
const summary = batchProcessor.getSummary()
return {
type: "success",
message: `🚀 Autonomous processing complete`,
summary: {
files_processed: summary.successful,
total_files: summary.total,
success_rate: Math.round(summary.successRate * 100),
total_changes: summary.totalChanges,
processing_time: Math.round(summary.elapsedTime / 1000)
},
results: results.slice(0, 10) // Show first 10 results
}
} else if (input.file_path) {
// Single file processing
const filePath = path.resolve(cwd, input.file_path)
if (!(await Bun.file(filePath).exists())) {
throw NamedError.create("FileNotFound", z.object({
path: z.string()
}))({ path: filePath })
}
const content = await Bun.file(filePath).text()
const fileType = detectLanguage(filePath)
const advice = agentMemory.generateContextualAdvice(filePath, fileType)
if (input.dry_run) {
const issues = analyzeCode(content, fileType)
return {
type: "success",
message: `🔍 DRY RUN: Found ${issues.length} potential issues`,
issues,
advice
}
}
// Create backup if enabled
if (input.backup) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
const backupPath = `${filePath}.backup-${timestamp}`
await Bun.write(backupPath, content)
}
const issues = analyzeCode(content, fileType)
const fixes = generateFixes(content, issues, advice)
// Record memory
agentMemory.addMemory({
filePath,
fileType,
issuesFound: issues,
changesApplied: fixes,
successMetrics: { syntaxValid: true, testsPass: false, lintClean: true },
patterns: { commonIssues: issues, effectiveFixes: fixes, riskySections: [] },
context: { language: fileType, complexity: assessComplexity(content) }
})
return {
type: "success",
message: `🎉 Autonomous analysis complete for ${path.basename(filePath)}`,
issues_found: issues.length,
changes_applied: fixes.length,
advice_used: advice ? "Applied contextual learning" : "No previous context",
fixes: fixes.slice(0, 5) // Show first 5 fixes
}
} else {
return {
type: "error",
message: "Please specify either file_path or directory for autonomous processing"
}
}
} catch (error) {
return {
type: "error",
message: `YOLO processing failed: ${error instanceof Error ? error.message : String(error)}`
}
}
},
})
// Helper functions (simplified for integration)
function detectLanguage(filePath: string): string {
const ext = path.extname(filePath).toLowerCase()
const map: Record<string, string> = {
'.js': 'javascript', '.jsx': 'javascript', '.ts': 'typescript', '.tsx': 'typescript',
'.py': 'python', '.java': 'java', '.go': 'go', '.rs': 'rust', '.cpp': 'cpp', '.c': 'c'
}
return map[ext] || 'unknown'
}
function analyzeCode(content: string, fileType: string): string[] {
const issues: string[] = []
// Basic analysis patterns
if (content.includes('console.log')) issues.push('Debug console.log statements found')
if (content.includes('// TODO')) issues.push('TODO comments need attention')
if (content.includes('var ') && fileType === 'javascript') issues.push('Use let/const instead of var')
if (content.match(/function\s+\w+\s*\(/g)?.length > 10) issues.push('File has many functions, consider splitting')
return issues
}
function generateFixes(content: string, issues: string[], advice: string): string[] {
const fixes: string[] = []
// Generate fixes based on issues
issues.forEach(issue => {
if (issue.includes('console.log')) fixes.push('Remove or replace console.log with proper logging')
if (issue.includes('TODO')) fixes.push('Address TODO items')
if (issue.includes('var ')) fixes.push('Replace var with let/const')
if (issue.includes('many functions')) fixes.push('Consider extracting functions to separate modules')
})
return fixes
}
function assessComplexity(content: string): number {
const lines = content.split('\n').length
const functions = (content.match(/function\s+\w+|def\s+\w+/g) || []).length
const conditions = (content.match(/\b(if|while|for|switch)\b/g) || []).length
return Math.min(1, (lines / 1000 + functions / 20 + conditions / 50))
}