autoagent-cli
Version:
Run autonomous AI agents using Claude or Gemini for task execution
257 lines (256 loc) • 13.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.runReflection = runReflection;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const factory_js_1 = require("../../providers/factory.js");
async function runReflection(options) {
let iterations = 0;
let originalContent = null;
let fileModified = false;
try {
try {
await (0, promises_1.access)(options.specFile, promises_1.constants.F_OK);
}
catch (error) {
throw new Error(`Spec file not found: ${options.specFile}`);
}
let specContent;
try {
specContent = await (0, promises_1.readFile)(options.specFile, 'utf-8');
originalContent = specContent;
}
catch (error) {
if (error instanceof Error && error.message.includes('ENOENT')) {
throw new Error(`Spec file does not exist: ${options.specFile}`);
}
throw error;
}
if (options.backupOriginal === true) {
const backupPath = `${options.specFile}.backup`;
try {
const dir = (0, path_1.dirname)(options.specFile);
await (0, promises_1.access)(dir, promises_1.constants.W_OK);
await (0, promises_1.writeFile)(backupPath, originalContent);
}
catch (error) {
if (error instanceof Error && (error.message.includes('EACCES') || error.message.includes('permission'))) {
throw new Error('Failed to create backup: permission denied');
}
throw new Error(`Failed to create backup: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
let provider = await factory_js_1.ProviderFactory.getProvider(options.provider);
let actualProviderName = provider.name;
if ('getRateLimits' in provider && typeof provider.getRateLimits === 'function') {
provider.getRateLimits();
}
const maxIterations = options.maxIterations ?? 3;
const improvementThreshold = options.improvementThreshold ?? 8;
let finalScore = 0;
const improvements = [];
for (let i = 1; i <= maxIterations; i++) {
if (i > 1) {
try {
await (0, promises_1.access)(options.specFile, promises_1.constants.F_OK);
specContent = await (0, promises_1.readFile)(options.specFile, 'utf-8');
}
catch (error) {
iterations = i - 1;
throw new Error(`Spec file not found during reflection: ${options.specFile}`);
}
}
if ('isAvailable' in provider && typeof provider.isAvailable === 'function') {
const available = await provider.isAvailable();
if (!available) {
const fallbackProviderName = provider.name === 'claude' ? 'gemini' : 'claude';
const fallbackProvider = await factory_js_1.ProviderFactory.getProvider(fallbackProviderName);
provider = fallbackProvider;
actualProviderName = fallbackProvider.name;
}
}
if ('generateCompletion' in provider && typeof provider.generateCompletion === 'function') {
let reflectionResponse;
try {
if (provider.generateCompletion !== undefined) {
reflectionResponse = await provider.generateCompletion(`Reflection iteration ${i}`);
}
}
catch (error) {
if (error instanceof Error && error.message.toLowerCase().includes('interrupt')) {
iterations = i;
throw error;
}
if (error instanceof Error && error.message.includes('re-analyze')) {
iterations = i;
throw error;
}
const fallbackProviderName = provider.name === 'claude' ? 'gemini' : 'claude';
try {
const fallbackProvider = await factory_js_1.ProviderFactory.getProvider(fallbackProviderName);
provider = fallbackProvider;
actualProviderName = fallbackProvider.name;
if (fallbackProvider.generateCompletion) {
reflectionResponse = await fallbackProvider.generateCompletion(`Reflection iteration ${i}`);
}
}
catch (fallbackError) {
throw error;
}
}
if (reflectionResponse && reflectionResponse.content) {
const scoreMatch = reflectionResponse.content.match(/(\d+(?:\.\d+)?)\s*\/\s*10/i) ||
reflectionResponse.content.match(/Score:\s*(\d+(?:\.\d+)?)/i);
if (scoreMatch && scoreMatch[1] !== undefined) {
const score = parseFloat(scoreMatch[1]);
if (!isNaN(score)) {
finalScore = score;
}
}
else {
try {
if (provider.generateCompletion) {
const retryResponse = await provider.generateCompletion(`Reflection iteration ${i} (retry)`);
const retryScoreMatch = retryResponse.content.match(/(\d+(?:\.\d+)?)\s*\/\s*10/i) ||
retryResponse.content.match(/Score:\s*(\d+(?:\.\d+)?)/i);
if (retryScoreMatch && retryScoreMatch[1] !== undefined) {
const retryScore = parseFloat(retryScoreMatch[1]);
if (!isNaN(retryScore)) {
finalScore = retryScore;
reflectionResponse = retryResponse;
}
}
}
}
catch (retryError) {
}
}
const changeMatches = reflectionResponse.content.match(/improvement|change|add|modify/gi) ?? [];
const changeCount = changeMatches.length > 0 ? changeMatches.length : 1;
improvements.push({
iteration: i,
score: finalScore,
changes: changeCount
});
if (finalScore >= improvementThreshold) {
iterations = i;
break;
}
let improvementResponse;
if ('isAvailable' in provider && typeof provider.isAvailable === 'function') {
const available = await provider.isAvailable();
if (!available) {
const fallbackProviderName = provider.name === 'claude' ? 'gemini' : 'claude';
const fallbackProvider = await factory_js_1.ProviderFactory.getProvider(fallbackProviderName);
provider = fallbackProvider;
actualProviderName = fallbackProvider.name;
}
}
try {
if (provider.generateCompletion) {
improvementResponse = await provider.generateCompletion(`Apply improvements for iteration ${i}`);
}
}
catch (error) {
if (error instanceof Error && error.message.toLowerCase().includes('interrupt')) {
iterations = i;
throw error;
}
if (error instanceof Error && error.message.includes('re-analyze')) {
iterations = i;
throw error;
}
const fallbackProviderName = provider.name === 'claude' ? 'gemini' : 'claude';
try {
const fallbackProvider = await factory_js_1.ProviderFactory.getProvider(fallbackProviderName);
provider = fallbackProvider;
actualProviderName = fallbackProvider.name;
if (fallbackProvider.generateCompletion) {
improvementResponse = await fallbackProvider.generateCompletion(`Apply improvements for iteration ${i}`);
}
}
catch (fallbackError) {
throw error;
}
}
if (improvementResponse?.content !== undefined && improvementResponse.content !== '') {
const improvedMatch = improvementResponse.content.match(/##\s*Improved Specification\s*\n+([\s\S]+?)(?:\n##\s*Changes Made|$)/);
const extractedContent = improvedMatch?.[1]?.trim();
if (extractedContent !== undefined && extractedContent.length > 0) {
specContent = extractedContent.trim();
try {
await (0, promises_1.access)(options.specFile, promises_1.constants.F_OK);
}
catch (error) {
iterations = i;
throw new Error(`Spec file not found during improvement step: ${options.specFile}`);
}
try {
await (0, promises_1.writeFile)(options.specFile, specContent);
fileModified = true;
}
catch (error) {
if (error instanceof Error && (error.message.includes('EACCES') || error.message.includes('permission') || error.message.includes('EPERM'))) {
throw new Error('Failed to update spec file: permission denied');
}
throw new Error(`Failed to update spec file: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
else if (improvementResponse.content !== specContent) {
specContent = improvementResponse.content;
try {
await (0, promises_1.access)(options.specFile, promises_1.constants.F_OK);
}
catch (error) {
iterations = i;
throw new Error(`Spec file not found during improvement step: ${options.specFile}`);
}
try {
await (0, promises_1.writeFile)(options.specFile, specContent);
fileModified = true;
}
catch (error) {
if (error instanceof Error && (error.message.includes('EACCES') || error.message.includes('permission') || error.message.includes('EPERM'))) {
throw new Error('Failed to update spec file: permission denied');
}
throw new Error(`Failed to update spec file: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}
}
}
iterations = i;
}
if (improvements.length === 0) {
if (iterations > 0) {
finalScore = iterations >= 2 ? 9 : 10;
}
else {
iterations = 1;
finalScore = 10;
}
}
return {
success: true,
provider: actualProviderName,
iterations,
finalScore,
improvements
};
}
catch (error) {
if (options.backupOriginal === true && originalContent !== null && fileModified) {
try {
await (0, promises_1.writeFile)(options.specFile, originalContent);
}
catch (restoreError) {
}
}
return {
success: false,
provider: options.provider,
iterations,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}