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 ❤️.
127 lines (117 loc) • 4.76 kB
JavaScript
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;
/**
* INFO PLAN GENERATOR
* Generates information-gathering steps only.
*/
export const infoPlanGen = {
name: 'infoPlanGen',
description: 'Generates an information-acquisition plan.',
requires: ['userQuery', 'analysis.intent'],
produces: ['analysis.planSuggestion'],
async run(context) {
context.analysis || (context.analysis = {});
// --------------------------------------------------
// Always discard any existing info plan
// (planner may loop; old plans must not survive)
// --------------------------------------------------
delete context.analysis.planSuggestion?.plan;
// --------------------------------------------------
// Check for missing files
// --------------------------------------------------
const missingFiles = Array.isArray(context.analysis.focus?.missingFiles)
? context.analysis.focus.missingFiles
: [];
if (missingFiles.length === 0) {
console.log('ℹ️ No missing files — returning empty info plan.');
context.analysis.planSuggestion = { plan: { steps: [] } };
logInputOutput('infoPlanGen', 'output', { steps: [] });
return;
}
// --------------------------------------------------
// Restrict actions to INFO phase only
// --------------------------------------------------
const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('info'));
const actionsJson = JSON.stringify(effectiveActions, null, 2);
console.log('Actions: ', actionsJson);
const intentText = context.analysis.intent?.normalizedQuery ??
context.initContext?.userQuery ??
'';
const intentCategory = context.analysis.intent?.intentCategory ?? '';
const prompt = `
You are an autonomous coding agent.
Based on the allowed actions you task is to produce a structured plan describing what information should be gathered
in order to understand and solve the task.
Allowed actions (information acquisition only):
${actionsJson}
Intent / task description:
${intentText}
Below is gathered information about the repository:
Task category:
${intentCategory}
Folder structure:
${context.analysis.folderCapsulesHuman ?? ''}
Existing relevant files:
${JSON.stringify(context.analysis.focus?.relevantFiles ?? {}, null, 2)}
Missing files:
${JSON.stringify(missingFiles, null, 2)}
Return a structured JSON plan describing which information to gather.
JSON schema:
{
"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
});
const jsonString = typeof cleaned.content === 'string'
? cleaned.content
: JSON.stringify(cleaned.content ?? '{}');
let plan = JSON.parse(jsonString);
if (!plan || !Array.isArray(plan.steps)) {
throw new Error('Invalid info plan structure');
}
if (plan.steps.length > 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.planSuggestion = { plan };
logInputOutput('infoPlanGen', 'output', plan);
}
catch (err) {
console.warn('⚠️ Failed to generate info plan:', err);
context.analysis.planSuggestion = { plan: { steps: [] } };
logInputOutput('infoPlanGen', 'output', { steps: [] });
}
}
};