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**
97 lines (90 loc) • 3.9 kB
JavaScript
// File: src/agents/infoPlanGenStep.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';
/**
* INFO PLAN GENERATOR
* Generates a single information-acquisition step.
*
* NOTE:
* - This step only creates a plan; execution happens in the agent loop.
* - The agent controls when this step runs.
*/
export const infoPlanGenStep = {
name: 'infoPlanGen',
description: 'Generates one information-acquisition step.',
requires: ['userQuery', 'analysis.intent'],
produces: ['analysis.planSuggestion'],
async run(context) {
context.analysis || (context.analysis = {});
// Clear any previous plan
delete context.analysis.planSuggestion;
const analysis = context.analysis;
const intentText = analysis.intent?.normalizedQuery ?? '';
const intentCategory = analysis.intent?.intentCategory ?? '';
// Only info-type actions
const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('info'));
if (!effectiveActions.length) {
context.analysis.planSuggestion = { plan: { steps: [] } };
logInputOutput('infoPlanGen', 'output', []);
return;
}
const actionsJson = JSON.stringify(effectiveActions, null, 2);
const prompt = `
You are an autonomous coding agent.
Produce exactly ONE structured step to gather additional information needed to satisfy the user's intent.
Intent / task description:
${intentText}
Task category:
${intentCategory}
Allowed actions (info only):
${actionsJson}
Rules:
- Only produce a single info step.
- Step must include: "action", "description", "subQuery" (array), "metadata".
- Do NOT invent new actions or files.
- Return strictly valid JSON representing one step.
If no further information is required, return:
{ "step": null }
`.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 ?? '{}');
// Unwrap the step from LLM output
const parsed = JSON.parse(jsonString);
let step = parsed?.step ?? null;
// Validate minimal structure
if (!step || typeof step.action !== 'string') {
step = null;
}
// Attach groups & routing confidence if available
if (step) {
const actionDef = PLAN_ACTIONS.find(a => a.action === step.action);
step.groups = actionDef?.groups ?? ['info'];
const confidence = analysis?.routingDecision?.confidence;
step.metadata = {
...step.metadata,
...(typeof confidence === 'number' && confidence > 0
? { routingConfidence: confidence }
: {})
};
}
// Save as array in planSuggestion
context.analysis.planSuggestion = { plan: { steps: step ? [step] : [] } };
logInputOutput('infoPlanGen', 'output', context.analysis.planSuggestion.plan?.steps ?? []);
}
catch (err) {
console.warn('⚠️ Failed to generate info step:', err);
context.analysis.planSuggestion = { plan: { steps: [] } };
logInputOutput('infoPlanGen', 'output', []);
}
}
};