UNPKG

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**

79 lines (78 loc) 3.55 kB
import { logInputOutput } from "../utils/promptLogHelper.js"; /** * Computes an initial routing decision from existing analysis signals. * This step is intentionally deterministic and non-invasive: * it only writes analysis.routingDecision for observability. */ export const routingDecisionStep = { name: "routingDecision", description: "Derive a lightweight routing decision for logging and downstream planning context.", run: async (context) => { context.analysis || (context.analysis = {}); const scope = (context.analysis.scopeType ?? "repo-wide"); const intentCategory = context.analysis.intent?.intentCategory ?? "request"; const intentConfidence = context.analysis.intent?.confidence ?? 0.5; const query = context.initContext?.userQuery?.trim() ?? ""; const canWrite = context.executionControl?.constraints?.allowFileWrites ?? false; const complexitySignals = [ /\b(and|also|plus|as well as|in addition)\b/gi, /\b(compare|trade[\s-]?off|pros and cons|strategy|architecture)\b/gi, /\?/g, ].reduce((sum, rx) => sum + ((query.match(rx) ?? []).length > 0 ? 1 : 0), 0); const isResearchScope = scope === "repo-wide"; const hasExplicitTargets = (context.analysis.intent?.targetFiles?.length ?? 0) > 0; const ambiguousIntent = intentConfidence < 0.45; const isRefactorLike = intentCategory === "refactorTask" || intentCategory === "codingTask"; const isAnalysisLike = intentCategory === "question" || intentCategory === "analysis" || intentCategory === "explanation"; let decision = "has-info"; let allowSearch = true; let scopeLocked = false; if (scope === "none") { allowSearch = false; scopeLocked = true; } else if (scope === "single-file" && hasExplicitTargets) { scopeLocked = true; } else if (scope === "repo-wide") { scopeLocked = false; } if (isResearchScope || ambiguousIntent || complexitySignals >= 2) { decision = "needs-info"; } if (!allowSearch) { decision = "has-info"; } const routeLabel = !allowSearch ? "direct-answer" : isResearchScope ? "repo-research" : scope === "single-file" ? "single-file-focused" : "bounded-analysis"; const allowResearch = scope !== "none" && ((isAnalysisLike && isResearchScope && (decision === "needs-info" || complexitySignals >= 2)) || (isRefactorLike && complexitySignals >= 1)); const confidence = Math.max(0, Math.min(1, 0.55 + intentConfidence * 0.35 - (ambiguousIntent ? 0.2 : 0))); const routingDecision = { decision, allowSearch, allowResearch, allowTransform: canWrite && scope !== "none", scopeLocked, confidence: Number(confidence.toFixed(2)), rationale: [ `route=${routeLabel}`, `scope=${scope}`, `intent=${intentCategory}`, `complexitySignals=${complexitySignals}`, `hasTargets=${hasExplicitTargets}`, ].join("; "), }; context.analysis.routingDecision = routingDecision; logInputOutput("routingDecisionStep", "output", routingDecision); return routingDecision; }, };