import { configLoader } from "../../src/shared/config"; import { compilePolicies, normalizeTarget } from "../guardrails/rules"; /** * Predicate over result paths for the search tools: true when the path may be * shown to the agent. Filters files matched by enabled noAccess policy rules * so protected contents (e.g. .env in a workspace root) cannot enter the * agent's context through our own tools. */ export function createNoAccessFilter(cwd: string): (path: string) => boolean { const config = configLoader.getConfig(); if (!config.enabled || !config.features.policies) return () => true; const policies = compilePolicies(config.policies.rules).filter( (policy) => policy.protection === "noAccess", ); return (path) => { const normalized = normalizeTarget(path, cwd); return !policies.some( (policy) => policy.patterns.some((pattern) => pattern.test(normalized)) && !policy.allowedPatterns.some((pattern) => pattern.test(normalized)), ); }; } /** * Extract the file path prefix of a ripgrep output line ("path:match"), * tolerating Windows drive letters ("C:\repo\file.ts:match"). */ export function rgLinePath(line: string): string { const searchFrom = /^[A-Za-z]:[\\/]/.test(line) ? 2 : 0; const idx = line.indexOf(":", searchFrom); return idx === -1 ? line : line.slice(0, idx); } /** * Drop ripgrep output lines whose file is protected by a noAccess policy. * Returns the kept text and how many lines were hidden. */ export function filterRipgrepOutput( output: string, allowPath: (path: string) => boolean, ): { text: string; hidden: number } { const lines = output.split("\n"); const kept = lines.filter((line) => !line || allowPath(rgLinePath(line))); return { text: kept.join("\n"), hidden: lines.length - kept.length }; }