UNPKG

scai

Version:

> **AI-powered CLI for local code analysis, commit message suggestions, and natural-language queries.** > **100% local • No token cost • Private by design • GDPR-friendly** — made in Denmark/EU with ❤️.

126 lines (115 loc) 5.45 kB
// File: src/agents/transformPlanGenStep.ts 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'; import { MODULE_STEP_INDENTATION } from '../constants.js'; const MAX_STEPS = 100; /** * TRANSFORM PLAN GENERATOR * Generates steps that perform concrete transformations or code changes. */ export const transformPlanGenStep = { name: 'transformPlanGen', description: 'Generates code transformation / modification steps.', requires: ['analysis.intent', 'analysis.focus'], produces: ['analysis.planSuggestion'], async run(context) { var _a, _b; context.analysis || (context.analysis = {}); (_a = context.analysis).planSuggestion || (_a.planSuggestion = {}); (_b = context.analysis.planSuggestion).plan || (_b.plan = { steps: [] }); // Restrict actions to TRANSFORM group only const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('transform')); const actionsJson = JSON.stringify(effectiveActions, null, 2); const intentText = context.analysis.intent?.normalizedQuery ?? ''; const intentCategory = context.analysis.intent?.intentCategory ?? ''; const prompt = ` You are an autonomous coding agent. Your task is to produce a structured plan describing how to implement actual code transformations or modifications in the system to achieve the intended task. Intent / task description: ${intentText} Task category: ${intentCategory} Allowed actions (transformation only): ${actionsJson} Existing relevant files: ${JSON.stringify(context.analysis.focus?.relevantFiles ?? {}, null, 2)} If the intent indicates that this is NOT a coding, refactoring, or inline commenting task, then return an empty plan object with an empty "steps" array: { "steps": [] } Only perform transformations that are safe based on the existing analysis. ⚡ Phase guidance: - Only include transform steps in this phase. - Each step must include: "action", "targetFile" (optional), "description", "metadata" ❌ IMPORTANT: Do NOT include "info" steps here. Return a strictly valid JSON plan: { "steps": [ { "action": "stepName", "targetFile": "optional/path.ts", "description": "explanation", "metadata": {} } ] } `.trim(); try { const genInput = { query: intentText, content: prompt }; const genOutput = await generate(genInput); const raw = typeof genOutput.data === 'string' ? genOutput.data : JSON.stringify(genOutput.data ?? '{}'); const cleaned = await cleanupModule.run({ query: intentText, content: raw }); let plan = null; // --- Parse strategy --- if (cleaned.data && typeof cleaned.data === 'object') { process.stdout.write("\r\x1b[K"); console.log(`${MODULE_STEP_INDENTATION}[transformPlanGen][debug] Using parsed JSON from cleanupModule`); plan = cleaned.data; } else if (typeof cleaned.data === 'string') { console.log(`${MODULE_STEP_INDENTATION}[transformPlanGen][debug] Attempting JSON extraction fallback`); const match = cleaned.data.match(/\{[\s\S]*\}/); if (match) { try { plan = JSON.parse(match[0]); console.log(`${MODULE_STEP_INDENTATION}[transformPlanGen][debug] Fallback JSON.parse succeeded`); } catch (err) { console.error(`${MODULE_STEP_INDENTATION}[transformPlanGen][debug] Fallback JSON.parse FAILED`, err); } } else { console.warn(`${MODULE_STEP_INDENTATION}[transformPlanGen][debug] No JSON object found in string`); } } // --- Final validation (single source of truth) --- if (!plan || !Array.isArray(plan.steps)) { console.error(`${MODULE_STEP_INDENTATION}[transformPlanGen][debug] Invalid or missing plan.steps`, plan); throw new Error('Invalid transform plan structure'); } if (plan.steps.length > MAX_STEPS) { plan.steps = plan.steps.slice(0, MAX_STEPS); } // Map groups & metadata 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 ?? ['transform'] }; }); // Replace existing transform steps in planSuggestion context.analysis.planSuggestion.plan.steps = [ ...context.analysis.planSuggestion.plan.steps.filter(s => !s.groups?.includes('transform')), ...plan.steps ]; logInputOutput('transformPlanGen', 'output', plan); } catch (err) { console.warn('⚠️ Failed to generate transform plan:', err); } } };