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 ❤️.
111 lines (105 loc) • 4.5 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) {
var _a;
context.analysis ?? (context.analysis = {});
(_a = context.analysis).focus ?? (_a.focus = { relevantFiles: [], missingFiles: [], rationale: "" });
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 with safe defaults
const relevantFiles = extractedFiles.filter(f => knownFiles.has(f));
const missingFiles = extractedFiles.filter(f => !knownFiles.has(f));
context.analysis.focus.relevantFiles = relevantFiles;
context.analysis.focus.missingFiles = missingFiles;
context.analysis.focus.rationale = `Pre-check: ${relevantFiles.length} files already available, ${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();
try {
const ai = await generate({
query: context.initContext?.userQuery ?? '',
content: prompt
});
let cleaned;
try {
cleaned = await cleanupModule.run({
query: context.initContext?.userQuery ?? '',
content: ai.data,
});
}
catch (cleanupErr) {
console.warn("[preFileSearchCheckStep] cleanupModule failed, using raw AI output", cleanupErr);
cleaned = { data: ai.data, content: ai.data };
}
let parsed = {};
try {
if (typeof cleaned.data === "object" && cleaned.data !== null) {
parsed = cleaned.data;
}
else if (typeof cleaned.content === "string") {
parsed = JSON.parse(cleaned.content);
}
}
catch (parseErr) {
console.warn("[preFileSearchCheckStep] Failed to parse cleanup output, using defaults", parseErr);
}
// Merge parsed output safely
context.analysis.focus.relevantFiles = Array.isArray(parsed.relevantFiles)
? parsed.relevantFiles
: context.analysis.focus.relevantFiles;
context.analysis.focus.missingFiles = Array.isArray(parsed.missingFiles)
? parsed.missingFiles
: context.analysis.focus.missingFiles;
context.analysis.focus.rationale = typeof parsed.rationale === "string"
? parsed.rationale
: context.analysis.focus.rationale;
if (parsed.understanding && typeof parsed.understanding === "object") {
context.analysis.understanding = {
...context.analysis.understanding,
...parsed.understanding,
};
}
logInputOutput("preFileSearchCheckStep", "output", parsed);
}
catch (err) {
console.warn("[preFileSearchCheckStep] AI pre-check failed, using defaults", err);
}
// Simple regex-based extractor (always returns array)
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()))) : [];
}
}