use sqz_engine::{CompressedContent, SqzEngine};
pub const CLI_PATTERNS: &[&str] = &[
"git", "hg", "svn", "fossil",
"cargo", "make", "cmake", "ninja", "bazel", "buck", "gradle", "mvn",
"ant", "sbt", "lein", "mix", "rebar3",
"npm", "yarn", "pnpm", "bun", "pip", "pip3", "poetry", "pipenv",
"conda", "gem", "bundle", "composer", "go", "dep", "glide",
"apt", "apt-get", "dpkg", "yum", "dnf", "rpm", "pacman", "brew",
"port", "snap", "flatpak", "nix", "guix",
"docker", "podman", "buildah", "skopeo", "kubectl", "helm", "k9s",
"minikube", "kind", "k3s", "nomad", "consul", "vault",
"aws", "az", "gcloud", "gsutil", "terraform", "pulumi", "cdk",
"serverless", "sam",
"node", "deno", "python", "python3", "ruby", "java", "kotlin",
"scala", "clojure", "elixir", "erlang", "ghc", "rustc", "clang",
"gcc", "g++",
"jest", "mocha", "pytest", "rspec", "minitest", "phpunit", "vitest",
"cypress", "playwright",
"eslint", "tslint", "prettier", "black", "isort", "flake8", "mypy",
"pylint", "rubocop", "golangci-lint", "clippy", "rustfmt",
"curl", "wget", "ssh", "scp", "rsync", "nc", "netstat", "ss",
"ping", "traceroute", "dig", "nslookup", "openssl",
"find", "grep", "rg", "ag", "fd", "ls", "tree", "cat", "less",
"head", "tail", "wc", "sort", "uniq", "awk", "sed", "jq", "yq",
"psql", "mysql", "sqlite3", "mongo", "redis-cli", "influx",
"gh", "hub", "lab", "glab", "jira", "linear",
"ansible", "chef", "puppet", "salt",
"ffmpeg", "convert", "identify",
];
fn preprocess_git_log(output: &str) -> String {
output.lines()
.filter(|l| !l.starts_with("Author:") || l.contains("@"))
.filter(|l| {
let lower = l.trim().to_lowercase();
!lower.starts_with("author:") || lower.contains("error") || lower.contains("merge")
})
.collect::<Vec<_>>()
.join("\n")
}
fn preprocess_docker_ps(output: &str) -> String {
let mut result = Vec::new();
for line in output.lines() {
let trimmed = line.trim_start();
if trimmed.len() > 12 {
let prefix = &trimmed[..12];
if prefix.chars().all(|c| c.is_ascii_hexdigit()) {
result.push(format!("[id] {}", &trimmed[12..].trim_start()));
continue;
}
}
result.push(line.to_owned());
}
result.join("\n")
}
fn preprocess_ls(output: &str) -> String {
output.lines()
.map(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() > 2 && parts[0].chars().all(|c| c.is_ascii_digit()) {
parts[1..].join(" ")
} else {
line.to_owned()
}
})
.collect::<Vec<_>>()
.join("\n")
}
fn preprocess_package_install(output: &str) -> String {
output.lines()
.filter(|l| {
let t = l.trim();
!t.starts_with("npm warn") && !t.starts_with("npm notice")
&& !t.contains("⠋") && !t.contains("⠙") && !t.contains("⠹")
&& !t.contains("downloading") && !t.contains("Downloading")
&& !t.starts_with("fetch") && !t.starts_with("Fetching")
})
.collect::<Vec<_>>()
.join("\n")
}
pub struct CliProxy {
engine: SqzEngine,
}
impl CliProxy {
pub fn new() -> sqz_engine::Result<Self> {
let engine = SqzEngine::new()?;
Ok(Self { engine })
}
#[allow(dead_code)]
pub fn with_engine(engine: SqzEngine) -> Self {
Self { engine }
}
pub fn intercept_output(&self, cmd: &str, output: &str) -> String {
match self.compress_output(cmd, output) {
Ok(compressed) => compressed.data,
Err(e) => {
eprintln!("[sqz] fallback: compression error for command '{cmd}': {e}");
output.to_owned()
}
}
}
fn compress_output(
&self,
cmd: &str,
output: &str,
) -> sqz_engine::Result<CompressedContent> {
let base_cmd = cmd.split_whitespace().next().unwrap_or(cmd)
.rsplit('/').next().unwrap_or(cmd)
.rsplit('\\').next().unwrap_or(cmd);
let preprocessed = match base_cmd {
"git" if cmd.contains("log") => preprocess_git_log(output),
"docker" if cmd.contains("ps") => preprocess_docker_ps(output),
"ls" => preprocess_ls(output),
"npm" | "yarn" | "pnpm" if cmd.contains("install") || cmd.contains("add") => {
preprocess_package_install(output)
}
_ => output.to_owned(),
};
self.engine.compress(&preprocessed)
}
#[allow(dead_code)]
pub fn is_known_command(cmd: &str) -> bool {
let base = cmd
.split_whitespace()
.next()
.unwrap_or("")
.rsplit('/')
.next()
.unwrap_or("");
CLI_PATTERNS
.iter()
.any(|p| base.eq_ignore_ascii_case(p))
}
pub fn run_proxy(&self) -> sqz_engine::Result<()> {
use std::io::{self, BufRead, Write};
let stdin = io::stdin();
let stdout = io::stdout();
let mut out = stdout.lock();
let mut buf = String::new();
for line in stdin.lock().lines() {
let line = line.map_err(|e| sqz_engine::SqzError::Other(e.to_string()))?;
buf.push_str(&line);
buf.push('\n');
}
let compressed = self.intercept_output("stdin", &buf);
out.write_all(compressed.as_bytes())
.map_err(|e| sqz_engine::SqzError::Other(e.to_string()))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_known_command_git() {
assert!(CliProxy::is_known_command("git"));
assert!(CliProxy::is_known_command("/usr/bin/git"));
assert!(CliProxy::is_known_command("git status"));
}
#[test]
fn test_is_known_command_unknown() {
assert!(!CliProxy::is_known_command("my_custom_tool"));
}
#[test]
fn test_patterns_count() {
assert!(
CLI_PATTERNS.len() >= 90,
"expected ≥90 patterns, got {}",
CLI_PATTERNS.len()
);
}
#[test]
fn test_intercept_output_returns_string() {
let proxy = CliProxy::new().expect("engine init");
let output = "hello world\nsome output\n";
let result = proxy.intercept_output("echo", output);
assert!(!result.is_empty());
}
#[test]
fn test_intercept_output_fallback_on_empty() {
let proxy = CliProxy::new().expect("engine init");
let result = proxy.intercept_output("git", "");
let _ = result;
}
}