UNPKG

autoagent-cli

Version:

Run autonomous AI agents using Claude or Gemini for task execution

205 lines (196 loc) 8.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldSkipReflection = shouldSkipReflection; exports.reflectOnIssuesAndPlans = reflectOnIssuesAndPlans; exports.reflectiveDecomposition = reflectiveDecomposition; const logger_js_1 = require("../utils/logger.js"); const reflection_defaults_js_1 = require("./reflection-defaults.js"); function shouldSkipReflection(result, config) { if (!config.enabled) { return { skip: true, reason: 'Reflection is disabled' }; } if (config.skipForSimpleSpecs && result.totalWordCount < 500) { return { skip: true, reason: 'Skipping reflection for simple spec (< 500 words)' }; } if (result.issueCount === 0) { return { skip: true, reason: 'No issues were generated' }; } return { skip: false }; } function buildReflectionPrompt(specContent, currentIssuesAndPlans, iterationNumber) { return `You are reviewing a project decomposition. Analyze the generated issues and plans for gaps, improvements, and missing details. ## Original Specification ${specContent} ## Current Issues and Plans ${currentIssuesAndPlans} ## Reflection Instructions (Iteration ${iterationNumber}) Now think deeply about the issues and plans and see if there are still some gaps in there. Consider: 1. **Missing Dependencies**: Are there unstated dependencies between issues? 2. **Scope Gaps**: Does the decomposition cover all aspects of the original spec? 3. **Detail Level**: Do issues have sufficient acceptance criteria and technical details? 4. **Task Granularity**: Are issues appropriately sized and scoped? 5. **Implementation Order**: Is the sequence of work logical and efficient? 6. **Edge Cases**: Are important edge cases and error scenarios covered? 7. **Testing Strategy**: Is testing adequately addressed in the plans? ## Required Output Format Provide a structured improvement analysis in valid JSON format: { "improvementScore": <number between 0.0 and 1.0>, "identifiedGaps": [ { "type": "string", "description": "string", "relatedIssues": ["string"] } ], "recommendedChanges": [ { "type": "ADD_ISSUE" | "MODIFY_ISSUE" | "ADD_PLAN" | "MODIFY_PLAN" | "ADD_DEPENDENCY", "target": "string", "description": "string", "content": "string", "rationale": "string" } ], "reasoning": "string" } Where: - improvementScore: Rate how much improvement is needed (0.0 = perfect, 1.0 = major gaps) - identifiedGaps: List specific gaps or missing elements - recommendedChanges: Specific changes to make - reasoning: Overall rationale for the most important changes Ensure your response is valid JSON that can be parsed.`; } function parseReflectionResponse(response) { try { const cleanedResponse = response.trim(); const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error('No JSON object found in response'); } const parsed = JSON.parse(jsonMatch[0]); const changes = (parsed.recommendedChanges ?? []).map((change) => ({ type: change.type, target: change.target ?? '', description: change.description ?? '', content: change.content ?? '', rationale: change.rationale ?? '' })); const analysis = { score: Math.max(0, Math.min(1, parsed.improvementScore ?? 0)), gaps: parsed.identifiedGaps ?? [], changes, reasoning: parsed.reasoning ?? '', iterationNumber: 0, timestamp: new Date().toISOString() }; return analysis; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger_js_1.Logger.error(`Failed to parse reflection response: ${errorMessage}`); throw new Error(`Invalid reflection response format: ${errorMessage}`); } } async function reflectOnIssuesAndPlans(provider, specContent, currentIssuesAndPlans, iterationNumber) { try { logger_js_1.Logger.info(`🔍 Performing reflection analysis (iteration ${iterationNumber})`); const prompt = buildReflectionPrompt(specContent, currentIssuesAndPlans, iterationNumber); const response = await provider.chat(prompt); const analysis = parseReflectionResponse(response); analysis.iterationNumber = iterationNumber; logger_js_1.Logger.info(`📊 Reflection score: ${analysis.score.toFixed(2)} (${analysis.changes.length} changes recommended)`); return analysis; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger_js_1.Logger.error(`Reflection analysis failed: ${errorMessage}`); const isParsingError = error instanceof Error && (error.message.includes('Invalid reflection response format') || error.message.includes('No JSON object found') || error.message.includes('JSON.parse')); return { score: 0, gaps: [], changes: [], reasoning: isParsingError ? 'Malformed response' : 'Provider error', iterationNumber, timestamp: new Date().toISOString() }; } } async function reflectiveDecomposition(provider, initialResult, config) { const reflectionConfig = (0, reflection_defaults_js_1.mergeReflectionConfig)(reflection_defaults_js_1.DEFAULT_REFLECTION_CONFIG, config.reflection); const skipCheck = shouldSkipReflection(initialResult, reflectionConfig); if (skipCheck.skip) { logger_js_1.Logger.info(`⏩ ${skipCheck.reason}`); return { enabled: false, performedIterations: 0, totalImprovements: 0, finalScore: 0, skippedReason: skipCheck.reason }; } const state = { currentIteration: 0, improvements: [], currentScore: 1.0, isComplete: false, currentIssues: initialResult.issueFiles, currentPlans: initialResult.planFiles }; logger_js_1.Logger.info(`🔄 Starting reflective improvement (max ${reflectionConfig.maxIterations} iterations)`); for (let iteration = 1; iteration <= reflectionConfig.maxIterations; iteration++) { logger_js_1.Logger.info(`🔍 Reflection iteration ${iteration}/${reflectionConfig.maxIterations}`); const currentContent = getCurrentIssuesAndPlansContent(state.currentIssues, state.currentPlans); const analysis = await reflectOnIssuesAndPlans(provider, initialResult.specContent, currentContent, iteration); if (analysis.reasoning !== 'Provider error' && analysis.reasoning !== 'Malformed response') { state.improvements.push(analysis); } state.currentScore = analysis.score; if (analysis.reasoning === 'Provider error') { logger_js_1.Logger.info('❌ Reflection stopped due to provider error'); state.isComplete = true; break; } if (analysis.reasoning === 'Malformed response') { logger_js_1.Logger.info('⚠️ Malformed response, continuing with fallback analysis'); state.improvements.push(analysis); } state.currentIteration = iteration; if (analysis.reasoning !== 'Malformed response' && analysis.score <= reflectionConfig.improvementThreshold) { logger_js_1.Logger.info(`✅ Reflection complete - minimal improvements found (score: ${analysis.score.toFixed(2)})`); state.isComplete = true; break; } if (analysis.changes.length > 0) { logger_js_1.Logger.info(`📈 ${analysis.changes.length} improvements identified`); for (const change of analysis.changes) { logger_js_1.Logger.info(` - ${change.type}: ${change.description}`); } } } const totalImprovements = state.improvements.reduce((sum, imp) => sum + imp.changes.length, 0); return { enabled: true, performedIterations: state.currentIteration, totalImprovements, finalScore: state.currentScore, improvements: state.improvements }; } function getCurrentIssuesAndPlansContent(issueFiles, planFiles) { let content = '### Issues:\n\n'; for (const file of issueFiles) { content += `File: ${file}\n`; content += '---\n\n'; } content += '\n### Plans:\n\n'; for (const file of planFiles) { content += `File: ${file}\n`; content += '---\n\n'; } return content; }