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 (90 loc) • 3.8 kB
JavaScript
// File: src/modules/preFileSearchCheckStep.ts
import { generate } from "../lib/generate.js";
import { logInputOutput } from "../utils/promptLogHelper.js";
import { cleanupModule } from "../pipeline/modules/cleanupModule.js";
export async function preFileSearchCheckStep(context) {
if (!context.analysis)
context.analysis = {};
const intent = context.analysis.intent;
const planSuggestion = context.analysis.planSuggestion?.text ?? "";
// Step 1: gather known files from initContext only
const knownFiles = new Set(context.initContext?.relatedFiles ?? []);
// Step 2: extract file names from normalizedQuery or planSuggestion
const extractedFiles = extractFilesFromAnalysis(context.analysis);
// Step 3: populate focus
context.analysis.focus = context.analysis.focus || { relevantFiles: [] };
context.analysis.focus.relevantFiles = extractedFiles.filter(f => knownFiles.has(f));
context.analysis.focus.missingFiles = extractedFiles.filter(f => !knownFiles.has(f));
context.analysis.focus.rationale = `Pre-check: ${context.analysis.focus.relevantFiles.length} files already available, ${context.analysis.focus.missingFiles.length} missing.`;
// Optional: LLM call for semantic understanding (assumptions, constraints, risks)
const prompt = `
You are an AI meta-agent assisting with context verification. The user intent and plan suggestion are below.
User intent:
${JSON.stringify(intent ?? {}, null, 2)}
Plan suggestion:
${planSuggestion}
Known files in context:
${JSON.stringify([...knownFiles], null, 2)}
Task:
1. Confirm which files are already available to satisfy the intent.
2. List missing files if any.
3. Suggest any assumptions, constraints, or risks that may affect execution.
4. Return STRICT JSON with shape:
{
"relevantFiles": string[],
"missingFiles": string[],
"rationale": string,
"understanding": {
"assumptions"?: string[],
"constraints"?: string[],
"risks"?: string[]
}
}
`.trim();
const ai = await generate({
query: context.initContext?.userQuery ?? '',
content: prompt
});
try {
const cleaned = await cleanupModule.run({
query: context.initContext?.userQuery ?? '',
content: ai.data,
});
let parsed;
if (typeof cleaned.data === "object" && cleaned.data !== null) {
parsed = cleaned.data;
}
else if (typeof cleaned.content === "string") {
parsed = JSON.parse(cleaned.content);
}
else {
throw new Error("Cleanup output is neither object nor string");
}
context.analysis.focus.relevantFiles =
parsed.relevantFiles ?? context.analysis.focus.relevantFiles;
context.analysis.focus.missingFiles =
parsed.missingFiles ?? context.analysis.focus.missingFiles;
context.analysis.focus.rationale =
parsed.rationale ?? context.analysis.focus.rationale;
if (parsed.understanding) {
context.analysis.understanding = {
...context.analysis.understanding,
...parsed.understanding,
};
}
logInputOutput("preFileSearchCheckStep", "output", parsed);
}
catch (err) {
console.warn("[preFileSearchCheckStep] Failed to parse AI output, using defaults", err);
}
// Simple regex-based extractor
function extractFilesFromAnalysis(analysis) {
const sources = [
analysis?.intent?.normalizedQuery ?? "",
analysis?.planSuggestion?.text ?? ""
].join("\n");
const regex = /\b([\w\-\./]+\.js|[\w\-\./]+\.ts)\b/g;
const matches = sources.match(regex);
return matches ? Array.from(new Set(matches.map(m => m.trim()))) : [];
}
}