271 lines
9.2 KiB
JavaScript
271 lines
9.2 KiB
JavaScript
/**
|
|
* Agent Booster Pre-Processor
|
|
*
|
|
* Detects code editing intents in agent tasks and attempts Agent Booster
|
|
* pattern matching before falling back to LLM.
|
|
*/
|
|
import { execSync } from 'child_process';
|
|
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
import { extname } from 'path';
|
|
export class AgentBoosterPreprocessor {
|
|
confidenceThreshold;
|
|
enabledIntents;
|
|
constructor(options = {}) {
|
|
this.confidenceThreshold = options.confidenceThreshold || 0.7;
|
|
this.enabledIntents = new Set(options.enabledIntents || [
|
|
'var_to_const',
|
|
'add_types',
|
|
'add_error_handling',
|
|
'async_await',
|
|
'add_logging',
|
|
'remove_console',
|
|
'format_code'
|
|
]);
|
|
}
|
|
/**
|
|
* Detect if a task is a code editing intent that Agent Booster can handle
|
|
*/
|
|
detectIntent(task) {
|
|
const patterns = [
|
|
{
|
|
regex: /convert\s+(all\s+)?var\s+to\s+const/i,
|
|
type: 'var_to_const',
|
|
extractor: this.extractVarToConst.bind(this)
|
|
},
|
|
{
|
|
regex: /add\s+type\s+(annotations?|hints?)/i,
|
|
type: 'add_types',
|
|
extractor: this.extractAddTypes.bind(this)
|
|
},
|
|
{
|
|
regex: /add\s+error\s+handling/i,
|
|
type: 'add_error_handling',
|
|
extractor: this.extractAddErrorHandling.bind(this)
|
|
},
|
|
{
|
|
regex: /convert\s+to\s+async\s*\/?\s*await/i,
|
|
type: 'async_await',
|
|
extractor: this.extractAsyncAwait.bind(this)
|
|
},
|
|
{
|
|
regex: /add\s+(logging|console\.log)/i,
|
|
type: 'add_logging',
|
|
extractor: this.extractAddLogging.bind(this)
|
|
},
|
|
{
|
|
regex: /remove\s+(all\s+)?console\.(log|debug|warn)/i,
|
|
type: 'remove_console',
|
|
extractor: this.extractRemoveConsole.bind(this)
|
|
}
|
|
];
|
|
for (const pattern of patterns) {
|
|
if (pattern.regex.test(task) && this.enabledIntents.has(pattern.type)) {
|
|
const filePath = this.extractFilePath(task);
|
|
if (filePath && existsSync(filePath)) {
|
|
const originalCode = readFileSync(filePath, 'utf-8');
|
|
const targetCode = pattern.extractor(originalCode);
|
|
if (targetCode) {
|
|
return {
|
|
type: pattern.type,
|
|
task,
|
|
filePath,
|
|
originalCode,
|
|
targetCode,
|
|
confidence: 0.8 // Initial confidence for pattern match
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Try to apply edit using Agent Booster
|
|
*/
|
|
async tryApply(intent) {
|
|
if (!intent.filePath || !intent.originalCode || !intent.targetCode) {
|
|
return {
|
|
success: false,
|
|
method: 'llm_required',
|
|
reason: 'Missing file path or code'
|
|
};
|
|
}
|
|
try {
|
|
const language = this.detectLanguage(intent.filePath);
|
|
const cmd = `npx --yes agent-booster@0.2.2 apply --language ${language}`;
|
|
let result;
|
|
try {
|
|
result = execSync(cmd, {
|
|
encoding: 'utf-8',
|
|
input: JSON.stringify({
|
|
code: intent.originalCode,
|
|
edit: intent.targetCode
|
|
}),
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
timeout: 10000
|
|
});
|
|
}
|
|
catch (execError) {
|
|
// execSync throws on non-zero exit, but agent-booster returns JSON even on stderr
|
|
// Try to parse stdout anyway
|
|
if (execError.stdout) {
|
|
result = execError.stdout.toString();
|
|
}
|
|
else {
|
|
throw new Error(`execSync failed: ${execError.message}`);
|
|
}
|
|
}
|
|
const parsed = JSON.parse(result);
|
|
if (parsed.success && parsed.confidence >= this.confidenceThreshold) {
|
|
// High confidence - use Agent Booster result
|
|
writeFileSync(intent.filePath, parsed.output);
|
|
return {
|
|
success: true,
|
|
method: 'agent_booster',
|
|
output: parsed.output,
|
|
latency: parsed.latency,
|
|
confidence: parsed.confidence,
|
|
strategy: parsed.strategy
|
|
};
|
|
}
|
|
else {
|
|
// Low confidence - require LLM
|
|
return {
|
|
success: false,
|
|
method: 'llm_required',
|
|
confidence: parsed.confidence,
|
|
reason: `Agent Booster confidence too low (${(parsed.confidence * 100).toFixed(1)}%). Using LLM for complex edit.`
|
|
};
|
|
}
|
|
}
|
|
catch (error) {
|
|
return {
|
|
success: false,
|
|
method: 'llm_required',
|
|
reason: `Agent Booster error: ${error.message}`
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Extract file path from task description
|
|
*/
|
|
extractFilePath(task) {
|
|
// Pattern: "in <file>" or "to <file>" or just "<file.ext>"
|
|
const patterns = [
|
|
/(?:in|to|from|for|file:?)\s+([^\s]+\.(?:js|ts|tsx|jsx|py|rs|go|java|c|cpp|h|hpp))/i,
|
|
/([^\s]+\.(?:js|ts|tsx|jsx|py|rs|go|java|c|cpp|h|hpp))/i
|
|
];
|
|
for (const pattern of patterns) {
|
|
const match = task.match(pattern);
|
|
if (match) {
|
|
return match[1];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Detect programming language from file extension
|
|
*/
|
|
detectLanguage(filePath) {
|
|
const ext = extname(filePath).slice(1);
|
|
const langMap = {
|
|
'ts': 'typescript',
|
|
'tsx': 'typescript',
|
|
'js': 'javascript',
|
|
'jsx': 'javascript',
|
|
'py': 'python',
|
|
'rs': 'rust',
|
|
'go': 'go',
|
|
'java': 'java',
|
|
'c': 'c',
|
|
'cpp': 'cpp',
|
|
'h': 'c',
|
|
'hpp': 'cpp'
|
|
};
|
|
return langMap[ext] || 'javascript';
|
|
}
|
|
/**
|
|
* Extract var to const transformation
|
|
*/
|
|
extractVarToConst(code) {
|
|
// Simple transformation: replace var with const
|
|
if (code.includes('var ')) {
|
|
return code.replace(/\bvar\b/g, 'const');
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Extract add types transformation (TypeScript)
|
|
*/
|
|
extractAddTypes(code) {
|
|
// Simple type annotation: function params and return types
|
|
const functionPattern = /function\s+(\w+)\s*\(([^)]*)\)\s*\{/g;
|
|
let hasUntypedFunctions = false;
|
|
let transformed = code;
|
|
code.replace(functionPattern, (match, name, params) => {
|
|
if (!params.includes(':')) {
|
|
hasUntypedFunctions = true;
|
|
// Add basic type hints
|
|
const typedParams = params.split(',').map((p) => {
|
|
const trimmed = p.trim();
|
|
if (trimmed && !trimmed.includes(':')) {
|
|
return `${trimmed}: any`;
|
|
}
|
|
return p;
|
|
}).join(', ');
|
|
transformed = transformed.replace(match, `function ${name}(${typedParams}): any {`);
|
|
}
|
|
return match;
|
|
});
|
|
return hasUntypedFunctions ? transformed : null;
|
|
}
|
|
/**
|
|
* Extract add error handling transformation
|
|
*/
|
|
extractAddErrorHandling(code) {
|
|
// Wrap risky operations in try-catch
|
|
const riskyPatterns = [
|
|
/JSON\.parse\(/,
|
|
/fetch\(/,
|
|
/\bawait\s+/
|
|
];
|
|
for (const pattern of riskyPatterns) {
|
|
if (pattern.test(code)) {
|
|
// This is complex - better handled by LLM
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Extract async/await transformation
|
|
*/
|
|
extractAsyncAwait(code) {
|
|
// Convert .then() chains to async/await
|
|
if (code.includes('.then(')) {
|
|
// This is complex - better handled by LLM
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Extract add logging transformation
|
|
*/
|
|
extractAddLogging(code) {
|
|
// Add console.log before function returns
|
|
// This is complex - better handled by LLM
|
|
return null;
|
|
}
|
|
/**
|
|
* Extract remove console transformation
|
|
*/
|
|
extractRemoveConsole(code) {
|
|
if (code.includes('console.')) {
|
|
// Remove console statements but preserve line structure
|
|
// Match optional leading whitespace, console call, and keep one newline if present
|
|
return code.replace(/^[ \t]*console\.(log|debug|warn|info|error)\([^)]*\);?\s*$/gm, '');
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
//# sourceMappingURL=agentBoosterPreprocessor.js.map
|