forge-guardrails 0.1.0

Foundation types for an LLM-agent workflow framework
Documentation
pub(super) mod cargo;
mod docker;
mod fs;
mod generic;
mod git;
mod make;
mod npm;
mod pip;
mod test;

use super::filters::{filter_glob_output, filter_grep_output, filter_read_output};

pub(super) use generic::filter_generic_output;

pub(super) fn preserve_unknown_or_empty_summary(output: &str, empty_summary: &str) -> String {
    if output.trim().is_empty() {
        empty_summary.to_string()
    } else {
        output.to_string()
    }
}

pub(super) fn filter_bash_output(
    family: &str,
    command: &str,
    output: &str,
    max_output_bytes: usize,
) -> String {
    if is_grep_command(command) {
        return filter_grep_output(output);
    }
    match family {
        "git" => git::filter_git_output(command, output),
        "cargo" => cargo::filter_cargo_output(command, output),
        "npm" => npm::filter_npm_output(command, output),
        "test" => test::filter_test_output(command, output),
        "docker" => docker::filter_docker_output(command, output),
        "pip" => pip::filter_pip_output(command, output),
        "make" => make::filter_make_output(output),
        "fs" | "glob" => fs::filter_fs_output(command, output),
        "grep" => filter_grep_output(output),
        "read" => filter_bash_read(command, output),
        _ => generic::filter_generic_output_with_limit(output, max_output_bytes / 2),
    }
}

fn is_grep_command(command: &str) -> bool {
    command
        .split_whitespace()
        .any(|token| matches!(token, "grep" | "rg" | "ag" | "ack"))
}

fn filter_bash_read(command: &str, output: &str) -> String {
    let path = parse_plain_cat_path(command).unwrap_or(command);
    if command.trim_start().starts_with("ls") || command.trim_start().starts_with("find") {
        return filter_glob_output(output);
    }
    filter_read_output(path, output)
}

fn parse_plain_cat_path(command: &str) -> Option<&str> {
    let trimmed = command.trim();
    let rest = trimmed.strip_prefix("cat ")?;
    if rest.starts_with('-')
        || rest.contains('|')
        || rest.contains('&')
        || rest.contains(';')
        || rest.contains('>')
        || rest.contains('<')
        || rest.contains('*')
        || rest.contains('"')
        || rest.split_whitespace().count() != 1
    {
        return None;
    }
    Some(rest)
}