/** * Bash-command approval interception decision logic. * * Mirror image of `./guard.ts` (edit approval): pure functions only — given * the plugin settings and the tool call, decide whether the command may run * untouched (`pass`) or must go through the approval panel (`ask` with a * reason that carries the model's own description plus the verbatim command). * * Deliberately fs-free: unlike edits, judging a command needs no file state. * * Compatibility contract (see also the namespace schema in `./index.ts`): * - the `allow` list is LOCKED to prefix matching over whitespace-normalized * commands; extending it (e.g. regex rules) must add a NEW field, never * change this semantic; * - settings fields may only be added (with defaults), never removed or * reinterpreted. * * @module dsh-edit-approval/bash-guard */ import type { GuardResult } from './guard.ts'; /** Fully resolved bash-approval settings (schema defaults + row config + user layer). */ export interface BashApprovalSettings { /** Master switch; off means commands execute without asking. */ enabled: boolean; /** Whitelist of intercepted tool names (default `['bash']`). */ tools: readonly string[]; /** Always-allow command prefixes; matched after whitespace normalization. */ allow: readonly string[]; } export interface BashGuardInput { readonly settings: BashApprovalSettings; readonly toolName: string; readonly args: Readonly>; } /** * Normalize a command for allow matching: trim the edges and collapse every * run of whitespace (including newlines and tabs) into a single space, so a * model cannot bypass an `allow` prefix by varying spacing (`git push` * matches `git push`). Idempotent. */ export declare function normalizeCommand(raw: string): string; /** * Whether a normalized command starts with any non-empty allow prefix. Both * the command and each pattern are whitespace-normalized here, so callers may * pass raw text safely; empty patterns are skipped. */ export declare function matchesAllow(command: string, allow: readonly string[]): boolean; /** * Whether the call requests a sandbox escalation: both `sandbox_permissions` * and `justification` must be present and non-blank. Such calls are left to * the tool's own escalation approval (one prompt, not two). */ export declare function isEscalation(args: Readonly>): boolean; /** * Build the approval reason: a single header line mirroring the edit header * (`tool · subject`). * * Deliberately minimal by design — the panel's two seats have a strict * division of labor: * - headline (this reason): the model's own description (`bash · …`); * - command row (harness-native, `ApprovalPanel` `commandOf`): the exact * command string from the tool call args. * The reason therefore must not repeat the command, nor carry execution * flags — the user approved that split. */ export declare function formatBashReason(description: unknown): string; /** * Decide whether one bash-family call must be approved. * * `pass` covers every case that should not block the call: disabled, tool not * whitelisted, a sandbox-escalation call (the escalation approval gates it), * a missing/blank command (the tool fails on its own), and an allow-listed * command. `ask` carries the reason the approval panel headlines. */ export declare function decideCommandApproval(input: BashGuardInput): GuardResult;