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 ❤️.
96 lines (88 loc) • 3.57 kB
JavaScript
import { generate } from '../lib/generate.js';
import { logInputOutput } from '../utils/promptLogHelper.js';
function logRoutingDecision(context) {
logInputOutput('planResolver', 'output', context.analysis?.routingDecision ?? {});
}
export const planResolverStep = {
name: 'planResolver',
// 🔒 Hard contract
requires: ['userQuery', 'intent'],
produces: [
'analysis.routingDecision',
'analysis.planSuggestion',
],
async run(context) {
context.analysis || (context.analysis = {});
const prompt = `
You are a routing classifier, not a problem solver.
User query:
${context.initContext?.userQuery}
Repo section:
${context.initContext?.repoTree}
${context.analysis?.folderCapsulesHuman}
Decide ONE of the following:
RESOLVED:
- Only if the user can be answered immediately
- The answer must be short (1–2 paragraphs)
- No code blocks
- No step-by-step instructions
- No file-level documentation
PLAN:
- If the task requires reading, modifying, generating, or validating code
- If the answer would be long, structured, or procedural
- If files are mentioned or implied
`.trim();
try {
const genInput = {
query: context.initContext?.userQuery ?? '',
content: prompt,
};
const genOutput = await generate(genInput);
console.log('PlanResolverOutput: ', genInput.data);
const raw = String(genOutput.data ?? '').trim();
logInputOutput('planResolver raw', 'output', raw);
const isResolved = raw.startsWith('RESOLVED:');
const isPlan = raw.startsWith('PLAN:');
const resolvedText = raw.replace(/^RESOLVED:\s*/i, '').trim();
const incompleteAnswerPatterns = [
/possible/i, /provide/i, /need/i, /cannot/i, /missing/i,
/unknown/i, /unspecified/i, /unavailable/i, /undefined/i,
/incomplete/i, /insufficient/i, /ambiguous/i, /unclear/i,
/requires/i, /lack/i, /omitted/i,
];
const requiresMoreWork = incompleteAnswerPatterns.some(p => p.test(resolvedText));
if (isResolved && resolvedText && !requiresMoreWork) {
context.analysis.routingDecision = {
decision: 'final-answer',
answer: resolvedText,
rationale: 'Request can be fully resolved immediately without further steps.',
confidence: 0.8,
};
}
else {
const planText = isPlan
? raw.replace(/^PLAN:\s*/i, '').trim()
: resolvedText;
context.analysis.planSuggestion = {
text: planText,
};
context.analysis.routingDecision = {
decision: 'needs-plan',
rationale: isPlan
? 'Model explicitly indicated planning or execution is required.'
: 'Request cannot be completed without further steps or execution.',
confidence: isPlan ? 0.6 : 0.4,
};
}
logRoutingDecision(context);
}
catch (err) {
console.warn('⚠️ planResolver failed:', err);
context.analysis.routingDecision = {
decision: 'needs-plan',
rationale: 'planResolver encountered an internal error.',
confidence: 0.2,
};
}
},
};