/** * The payload fields this hook reads, validated. A payload with no `file_path` * is harness-contract drift, not a clean file: reporting "no findings" for a * file we never identified is the one answer that must never be reachable, so * this throws into the declared fault posture instead. * @param {unknown} payload * @returns {{ filePath: string, loadReason: string }} */ export function readLoadedFile(payload: unknown): { filePath: string; loadReason: string; }; /** * Scan one loaded instruction file. Returns the report and what to do with it: * `cleaned` says the payload is gone from disk, and `reason` says why it is not * when it is not — which is what routes the file to the PreToolUse gate. * * The event names the file but carries none of its bytes, so the scan reads the * path. That is also what keeps scan and clean coherent: `cleanFile` rewrites * what is on disk, and this scan is what decides whether it should. * * A read that fails is never an empty findings list — an instruction file * already in context that could not be scanned is exactly what this hook's fault * posture exists to announce, so the error propagates to it. * @param {string} filePath * @param {{ projectDir?: string, clean?: typeof cleanFile, * read?: (path: string) => string }} [opts] injectable for tests; the * defaults read the real file and clean through the SSOT's guarded rewrite * @returns {{ report: string, cleaned: boolean, reason: string | null } | null} * null when the file is clean */ export function scanLoadedFile(filePath: string, { projectDir, clean, read }?: { projectDir?: string; clean?: typeof cleanFile; read?: (path: string) => string; }): { report: string; cleaned: boolean; reason: string | null; } | null; /** * The operator-facing line for a file whose path contradicts the scope table, or * null when it does not. This hook is where that check belongs and the only * place it can run: the host naming a file as it loads is the one observation * that can prove the SessionStart scan's scope wrong, and a scope that is wrong * about a `.claude/` subdirectory is a launch scan with a hole in it. * * Separate from the finding channels below: this is a maintenance signal about * THIS package, not a verdict about the file, so it never reaches the model and * never arms the tool-call gate. * @param {string} filePath * @param {string} loadReason why the host loaded it, which decides whether the * load is evidence about the scan's scope at all * @returns {string | null} */ export function scopeNotice(filePath: string, loadReason: string): string | null; /** * The operator- and model-facing text for a scanned file. Both channels carry * it: the bytes are already in context, so the model is told to distrust what it * just read, and the user is told what changed on disk. * @param {{ report: string, cleaned: boolean, reason: string | null }} result * @param {string} filePath * @returns {string} */ export function loadedFileMessage({ report, cleaned, reason }: { report: string; cleaned: boolean; reason: string | null; }, filePath: string): string; /** * The hook's CLI: read the event, scan the loaded bytes, clean and report. * Exported so a bundle entry (which must claim the CLI slot before this module * loads) can run the exact same wiring instead of duplicating it. * @param {{ trace?: import("./lib/trace.mjs").TraceFn }} [opts] `trace` is * where this scan announces engagement; a host with its own trace channel * passes its sink (see lib/trace.mjs) * @returns {Promise} */ export function cliMain({ trace: sink }?: { trace?: import("./lib/trace.mjs").TraceFn; }): Promise; declare const cleanFile: typeof import("agent-sanitizer/instructions").cleanFile; export const HOOK_NAME: "scan-loaded-instructions"; export {};