UNPKG

scai

Version:

> **A local-first AI CLI for understanding, querying, and iterating on large codebases.** > **100% local • No token costs • No cloud • No prompt injection • Private by design**

79 lines (78 loc) 3.98 kB
import { logInputOutput } from "../utils/promptLogHelper.js"; /** * REASON NEXT STEP * * Only reasons about the current taskStep: * - Is it validated / complete? * - Does it require redo? * - Updates taskStep.status and stores reasoning in taskStep.result */ export const reasonNextStep = { name: "reasonNextStep", description: "Determines completion status for the current task step.", async run(context, taskStep) { context.execution || (context.execution = {}); const isAnalyzeOnly = context.executionControl?.constraints?.allowFileWrites === false; const fileAnalysis = context.analysis?.fileAnalysis?.[taskStep.filePath]; const semanticAnalyzed = fileAnalysis?.semanticAnalyzed === true; const isLikelyNonSource = /\.(json|md|txt|ya?ml|toml|lock)$/i.test(taskStep.filePath); const validations = context.analysis?.executionOutcome?.validations ?? []; const currentValidation = validations.find((v) => v.filePath === taskStep.filePath); taskStep.result || (taskStep.result = {}); const priorAnalysisAttempts = typeof taskStep.result.analysisAttempts === "number" ? taskStep.result.analysisAttempts : 0; const analysisAttempts = isAnalyzeOnly ? priorAnalysisAttempts + 1 : priorAnalysisAttempts; taskStep.result.analysisAttempts = analysisAttempts; // Default reasoning object for this step const stepReasoning = { nextAction: "continue", rationale: "", confidence: 0.5, }; // ✅ Analyze-only workflows complete once semantic analysis exists for the current file. if (isAnalyzeOnly && semanticAnalyzed) { taskStep.status = "completed"; stepReasoning.nextAction = "complete"; stepReasoning.rationale = `TaskStep ${taskStep.filePath} completed in analysis mode (semantic analysis available).`; stepReasoning.confidence = 0.98; } // ✅ Analyze-only fallback for files that do not emit semanticAnalyzed reliably. else if (isAnalyzeOnly && (isLikelyNonSource || analysisAttempts >= 2)) { taskStep.status = "completed"; stepReasoning.nextAction = "complete"; stepReasoning.rationale = isLikelyNonSource ? `TaskStep ${taskStep.filePath} completed in analysis mode (non-source file; bounded single-pass analysis).` : `TaskStep ${taskStep.filePath} completed after ${analysisAttempts} analysis attempt(s) without semantic marker.`; stepReasoning.confidence = 0.85; } // ✅ Completed by validation else if (currentValidation?.status === "valid" && !currentValidation.requiresRedo) { taskStep.status = "completed"; stepReasoning.nextAction = "complete"; stepReasoning.rationale = `TaskStep ${taskStep.filePath} validated successfully.`; stepReasoning.confidence = 0.99; } // 🔁 Requires redo else if (currentValidation?.requiresRedo) { taskStep.status = "pending"; // keep pending until redo runs stepReasoning.nextAction = "redo-step"; stepReasoning.rationale = `TaskStep ${taskStep.filePath} requires redo due to failed validation.`; stepReasoning.confidence = 0.7; } // ⚠️ Not yet validated / in-progress else { taskStep.status = "pending"; stepReasoning.nextAction = "continue"; stepReasoning.rationale = `TaskStep ${taskStep.filePath} not yet validated or transformed.`; stepReasoning.confidence = 0.5; } // Store reasoning in taskStep.result for later inspection taskStep.result.stepReasoning = stepReasoning; logInputOutput("reasonNextStep", "output", { taskStep, validation: currentValidation, stepReasoning, }); }, };