UNPKG

scai

Version:

> **AI-powered CLI for local code analysis, commit message suggestions, and natural-language queries.** 100% local, private, GDPR-friendly, made in Denmark/EU with ❤️.

119 lines (107 loc) 5.12 kB
import { generate } from '../lib/generate.js'; import { PLAN_ACTIONS } from '../utils/planActions.js'; import { logInputOutput } from '../utils/promptLogHelper.js'; import { cleanupModule } from '../pipeline/modules/cleanupModule.js'; const MAX_STEPS = 100; export const planGeneratorStep = { name: 'planGenerator', description: 'Converts a textual plan into structured JSON steps.', requires: ['userQuery', 'intent', 'analysis.routingDecision'], produces: ['analysis.planSuggestion.plan'], async run(context) { var _a, _b; context.analysis || (context.analysis = {}); // We check for missing files detected in the preFileSearchCheckStep // If none we filter out fileSearch actions. const missingFiles = Array.isArray(context.analysis.focus?.missingFiles) ? context.analysis.focus.missingFiles : []; const hasMissingFiles = missingFiles.length > 0; const effectiveActions = PLAN_ACTIONS.filter(a => { if (!hasMissingFiles && a.action === 'fileSearch') { return false; } return true; }); const actionsJson = JSON.stringify(effectiveActions, null, 2); const intentText = context.analysis?.intent?.normalizedQuery ?? context.initContext?.userQuery; const intentCategory = context.analysis?.intent?.intentCategory ?? ''; const suggestionText = context.analysis?.planSuggestion?.text ?? ''; const prompt = ` You are an autonomous coding agent. Convert the following textual suggestion into a structured JSON plan. Intent / task description: ${intentText} Suggest task type: ${intentCategory}; Suggested plan / guidance: ${suggestionText} Folder info: ${context.analysis?.folderCapsulesHuman} Allowed actions and their groups (JSON format): ${actionsJson} Existing relevant files: ${JSON.stringify(context.analysis?.focus?.relevantFiles ?? {}, null, 2)} Only implement search if there are missing files: ${JSON.stringify(context.analysis?.focus?.missingFiles ?? {}, null, 2)} ⚡ Phase guidance: - Actions are grouped into phases: info, transform, finalize. - Respect group boundaries: do all "info" steps first, then "transform", then "finalize". - Each step must include: "action", "targetFile" (optional), "description", "metadata" ❌ IMPORTANT: Do NOT use "codeTransform" as an analysis step. All analysis tasks are already handled by semantic/structural analysis modules. Only use "codeTransform" if you are generating actual code changes in the transform phase. Rules: - Use only the actions listed above. - Limit to a maximum of ${MAX_STEPS} steps. - Return strictly valid JSON matching: { "steps": [ { "action": "stepName", "targetFile": "optional/path.ts", "description": "explanation", "metadata": {} } ] } `.trim(); try { console.dir("planGeneratorPrompt: ", prompt); const genInput = { query: intentText ?? '', content: prompt }; const genOutput = await generate(genInput); const llmOutput = typeof genOutput.data === 'string' ? genOutput.data : JSON.stringify(genOutput.data ?? '{}'); const cleaned = await cleanupModule.run({ query: intentText ?? '', content: llmOutput, }); const jsonString = typeof cleaned.content === 'string' ? cleaned.content : JSON.stringify(cleaned.content ?? '{}'); let plan = JSON.parse(jsonString); if (!plan || typeof plan !== 'object' || !Array.isArray(plan.steps)) { throw new Error('Invalid plan structure'); } if (plan.steps.length > MAX_STEPS) { console.warn(`⚠️ Truncating plan steps: got ${plan.steps.length}, limiting to ${MAX_STEPS}`); plan.steps = plan.steps.slice(0, MAX_STEPS); } plan.steps = plan.steps.map(step => { const actionDef = PLAN_ACTIONS.find(a => a.action === step.action); return { ...step, metadata: { ...step.metadata, routingConfidence: context.analysis?.routingDecision?.confidence ?? 0 }, groups: actionDef?.groups ?? [] }; }); context.analysis ?? (context.analysis = {}); (_a = context.analysis).planSuggestion ?? (_a.planSuggestion = { text: '' }); context.analysis.planSuggestion.plan = plan; logInputOutput('planGenerator', 'output', { generatedPlan: plan }); } catch (err) { console.warn('⚠️ Failed to parse plan JSON:', err); context.analysis ?? (context.analysis = {}); (_b = context.analysis).planSuggestion ?? (_b.planSuggestion = { text: '' }); context.analysis.planSuggestion.plan = { steps: [] }; logInputOutput('planGenerator', 'output', { generatedPlan: { steps: [] } }); } }, };