use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
const TIER_INSTANT_REJECT: &str = "instant_reject";
const TIER_STRUCTURE_ANALYSIS: &str = "structure_analysis";
const TIER_KEYWORD_FILTER: &str = "keyword_filter";
const TIER_NEVER_INTERCEPT: &str = "never_intercept";
const TIER_FULL_CLASSIFICATION: &str = "full_classification";
pub static COMPILATION_KEYWORDS: &[&str] = &[
"cargo",
"cargo-zigbuild",
"cargo-xwin",
"rustc",
"gcc",
"g++",
"clang",
"clang++",
"make",
"cmake",
"ninja",
"meson",
"cc",
"c++",
"bun",
"nextest",
"nix",
"nix-build",
"go",
"tsc",
"npx",
];
pub static NEVER_INTERCEPT: &[&str] = &[
"cargo install",
"cargo login",
"cargo fmt",
"cargo fix",
"cargo clean",
"cargo new",
"cargo init",
"cargo add",
"cargo remove",
"cargo update",
"cargo generate-lockfile",
"cargo watch",
"cargo --version",
"cargo -V",
"rustc --version",
"rustc -V",
"gcc --version",
"gcc -v",
"clang --version",
"clang -v",
"make --version",
"make -v",
"cmake --version",
"bun install",
"bun add",
"bun remove",
"bun link",
"bun unlink",
"bun pm",
"bun init",
"bun create",
"bun upgrade",
"bun completions",
"bun run",
"bun build",
"bun --help",
"bun -h",
"bun --version",
"bun -v",
"bun dev",
"bun repl",
"cargo nextest list", "cargo nextest archive", "cargo nextest show", "go get", "go mod", "go work", "go install", "go run", "go generate", "go fmt", "go clean", "go env", "go doc", "go version", "go tool", "go list", "tsc --version",
"tsc -v",
"tsc --init",
];
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Classification {
pub is_compilation: bool,
pub confidence: f64,
pub kind: Option<CompilationKind>,
pub reason: Cow<'static, str>,
pub command_prefix: Option<String>,
pub extracted_command: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum TierDecision {
Pass,
Reject,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ClassificationTier {
pub tier: u8,
pub name: Cow<'static, str>,
pub decision: TierDecision,
pub reason: Cow<'static, str>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ClassificationDetails {
pub original: String,
pub normalized: String,
pub tiers: Vec<ClassificationTier>,
pub classification: Classification,
}
impl Classification {
pub fn not_compilation(reason: impl Into<Cow<'static, str>>) -> Self {
Self {
is_compilation: false,
confidence: 0.0,
kind: None,
reason: reason.into(),
command_prefix: None,
extracted_command: None,
}
}
pub fn compilation(
kind: CompilationKind,
confidence: f64,
reason: impl Into<Cow<'static, str>>,
) -> Self {
Self {
is_compilation: true,
confidence,
kind: Some(kind),
reason: reason.into(),
command_prefix: None,
extracted_command: None,
}
}
pub fn compound_compilation(
kind: CompilationKind,
confidence: f64,
reason: impl Into<Cow<'static, str>>,
prefix: String,
extracted: String,
) -> Self {
Self {
is_compilation: true,
confidence,
kind: Some(kind),
reason: reason.into(),
command_prefix: Some(prefix),
extracted_command: Some(extracted),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CompilationKind {
CargoBuild,
CargoTest,
CargoCheck,
CargoClippy,
CargoDoc,
CargoNextest,
CargoBench,
CargoZigbuild,
Rustc,
Gcc,
Gpp,
Clang,
Clangpp,
Make,
CmakeBuild,
Ninja,
Meson,
BunTest,
BunTypecheck,
GoBuild,
GoTest,
GoVet,
Tsc,
NixBuild,
Job,
}
impl CompilationKind {
pub fn is_test_command(&self) -> bool {
matches!(
self,
CompilationKind::CargoTest
| CompilationKind::CargoNextest
| CompilationKind::CargoBench
| CompilationKind::BunTest
| CompilationKind::GoTest
)
}
pub fn command_base(&self) -> &'static str {
match self {
CompilationKind::CargoBuild
| CompilationKind::CargoTest
| CompilationKind::CargoCheck
| CompilationKind::CargoClippy
| CompilationKind::CargoDoc
| CompilationKind::CargoBench => "cargo",
CompilationKind::CargoNextest => "cargo", CompilationKind::CargoZigbuild => "cargo",
CompilationKind::Rustc => "rustc",
CompilationKind::Gcc => "gcc",
CompilationKind::Gpp => "g++",
CompilationKind::Clang => "clang",
CompilationKind::Clangpp => "clang++",
CompilationKind::Make => "make",
CompilationKind::CmakeBuild => "cmake",
CompilationKind::Ninja => "ninja",
CompilationKind::Meson => "meson",
CompilationKind::BunTest | CompilationKind::BunTypecheck => "bun",
CompilationKind::NixBuild => "nix",
CompilationKind::GoBuild | CompilationKind::GoTest | CompilationKind::GoVet => "go",
CompilationKind::Tsc => "tsc",
CompilationKind::Job => "job",
}
}
}
pub fn classify_command(cmd: &str) -> Classification {
classify_command_inner(cmd, 0)
}
pub fn declined_compilation_due_to_structure(cmd: &str) -> Option<&'static str> {
let trimmed = cmd.trim();
if trimmed.is_empty() {
return None;
}
if classify_command(trimmed).is_compilation {
return None;
}
let reason = check_structure(trimmed)?;
let normalized = normalize_command(trimmed);
if starts_with_compilation_command(normalized.as_ref()) {
Some(reason)
} else {
None
}
}
#[allow(dead_code)] const MAX_CLASSIFY_DEPTH: u8 = 1;
#[allow(dead_code)] const MAX_SPLIT_INPUT_LEN: usize = 10 * 1024;
fn classify_command_inner(cmd: &str, depth: u8) -> Classification {
let cmd = cmd.trim();
if cmd.is_empty() {
return Classification::not_compilation("empty command");
}
if depth == 0
&& cmd.len() < MAX_SPLIT_INPUT_LEN
&& let Some(result) = try_classify_compound_command(cmd)
{
return result;
}
if depth == 0 && cmd.len() < MAX_SPLIT_INPUT_LEN {
if let Some(result) = try_classify_wrapped_command(cmd) {
return result;
}
if let Some(result) = try_classify_benign_suffix_command(cmd) {
return result;
}
}
if let Some(reason) = check_structure(cmd) {
return Classification::not_compilation(reason);
}
let normalized_cow = normalize_command(cmd);
let normalized = normalized_cow.as_ref();
if !starts_with_compilation_command(normalized) {
return Classification::not_compilation("no compilation keyword");
}
for pattern in NEVER_INTERCEPT {
if let Some(rest) = normalized.strip_prefix(pattern) {
if rest.is_empty() || rest.starts_with(' ') {
return Classification::not_compilation("matches never-intercept pattern");
}
}
}
classify_full(normalized)
}
fn try_classify_compound_command(cmd: &str) -> Option<Classification> {
if !cmd.contains("&&") {
return None;
}
if cmd.contains('|') {
return None; }
if has_file_redirect(cmd) {
return None; }
if cmd.contains('(') || cmd.contains('`') || cmd.contains("$(") {
return None; }
if cmd.contains(';') {
return None; }
let segments = split_and_chain(cmd)?;
if segments.is_empty() {
return None;
}
let last_segment = segments.last()?.trim();
if last_segment.is_empty() {
return None;
}
let last_classification = classify_command_inner(last_segment, 1);
if !last_classification.is_compilation {
return None;
}
if segments.len() == 1 {
return None;
}
let mut prefix = String::with_capacity(cmd.len() + segments.len() * EXEC_REWRITE_PREFIX.len());
for segment in &segments[..segments.len() - 1] {
if classify_command_inner(segment, 1).is_compilation {
prefix.push_str(EXEC_REWRITE_PREFIX);
}
prefix.push_str(segment);
prefix.push_str(" && ");
}
Some(Classification::compound_compilation(
last_classification.kind?,
last_classification.confidence,
"compound command with compilation suffix",
prefix,
last_segment.to_string(),
))
}
const EXEC_REWRITE_PREFIX: &str = "rch exec -- ";
pub fn compound_local_compilation_segments(cmd: &str) -> Vec<String> {
let trimmed = cmd.trim();
if trimmed.len() >= MAX_SPLIT_INPUT_LEN {
return Vec::new();
}
let Some(segments) = split_and_chain(trimmed) else {
return Vec::new();
};
if segments.len() < 2 {
return Vec::new();
}
segments[..segments.len() - 1]
.iter()
.filter(|segment| {
!classify_command_inner(segment, 1).is_compilation
&& starts_with_compilation_command(normalize_command(segment).as_ref())
})
.map(|segment| (*segment).to_string())
.collect()
}
const BENIGN_PAGER_COMMANDS: &[&str] = &[
"tee", "head", "cat", "grep", "less", "more", "tail", "wc", "rg",
];
fn try_classify_wrapped_command(cmd: &str) -> Option<Classification> {
let cmd = cmd.trim();
let rest = strip_shell_dash_c_prefix(cmd)?;
let rest = rest.trim_start();
let inner = extract_single_quoted_argument(rest)?;
if inner.trim().is_empty() {
return None;
}
let inner_classification = classify_command_inner(&inner, 0);
if !inner_classification.is_compilation {
return None;
}
let (prefix, extracted) = match (
&inner_classification.command_prefix,
&inner_classification.extracted_command,
) {
(Some(p), Some(e)) => (p.clone(), e.clone()),
_ => (String::new(), inner.trim().to_string()),
};
Some(Classification::compound_compilation(
inner_classification.kind?,
inner_classification.confidence,
"shell -c wrapper with compilation command",
prefix,
extracted,
))
}
fn strip_shell_dash_c_prefix(cmd: &str) -> Option<&str> {
let trimmed = cmd.trim_start();
let mut idx = 0;
let bytes = trimmed.as_bytes();
let word1_end = next_word_end(bytes, idx)?;
let shell = &trimmed[idx..word1_end];
if !shell_basename_is(shell, "bash") && !shell_basename_is(shell, "sh") {
return None;
}
idx = skip_spaces(bytes, word1_end);
let word2_end = next_word_end(bytes, idx)?;
if &trimmed[idx..word2_end] != "-c" {
return None;
}
idx = skip_spaces(bytes, word2_end);
if idx >= trimmed.len() {
return None;
}
Some(&trimmed[idx..])
}
fn shell_basename_is(word: &str, name: &str) -> bool {
word.rsplit('/').next().map(|b| b == name).unwrap_or(false)
}
fn next_word_end(bytes: &[u8], start: usize) -> Option<usize> {
if start >= bytes.len() || bytes[start].is_ascii_whitespace() {
return None;
}
let mut i = start;
while i < bytes.len() && !bytes[i].is_ascii_whitespace() {
i += 1;
}
Some(i)
}
fn skip_spaces(bytes: &[u8], start: usize) -> usize {
let mut i = start;
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
i
}
fn extract_single_quoted_argument(s: &str) -> Option<String> {
let s = s.trim();
let bytes = s.as_bytes();
if bytes.len() < 2 {
return None;
}
let quote = bytes[0];
if quote != b'"' && quote != b'\'' {
return None;
}
let mut out = String::with_capacity(s.len());
let mut i = 1;
while i < bytes.len() {
let b = bytes[i];
if quote == b'"' && b == b'\\' && i + 1 < bytes.len() {
out.push(bytes[i + 1] as char);
i += 2;
continue;
}
if b == quote {
if s[i + 1..].trim().is_empty() {
return Some(out);
}
return None;
}
out.push(b as char);
i += 1;
}
None
}
fn try_classify_benign_suffix_command(cmd: &str) -> Option<Classification> {
let cmd = cmd.trim();
if cmd.bytes().any(|byte| matches!(byte, b'\n' | b'\r')) {
return None;
}
if cmd.contains("&&") || cmd.contains("||") || cmd.contains(';') {
return None;
}
if cmd.contains('(') || cmd.contains('`') || cmd.contains("$(") {
return None;
}
let boundary = find_first_structural_boundary(cmd)?;
let (leading_raw, suffix_raw) = cmd.split_at(boundary.byte_pos);
let leading = leading_raw.trim();
let suffix = suffix_raw; if leading.is_empty() {
return None;
}
if !suffix_is_benign(boundary.kind, suffix) {
return None;
}
let leading_classification = classify_command_inner(leading, 1);
if !leading_classification.is_compilation {
return None;
}
let extracted = cmd.to_string();
Some(Classification::compound_compilation(
leading_classification.kind?,
leading_classification.confidence,
"compilation command with benign trailing redirect/pipe/background",
String::new(),
extracted,
))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BoundaryKind {
Pipe,
Background,
OutputRedirect,
InputRedirect,
}
struct StructuralBoundary {
byte_pos: usize,
kind: BoundaryKind,
}
fn find_first_structural_boundary(cmd: &str) -> Option<StructuralBoundary> {
let bytes = cmd.as_bytes();
let len = bytes.len();
let mut in_single = false;
let mut in_double = false;
let mut escaped = false;
let mut i = 0;
while i < len {
let b = bytes[i];
if escaped {
escaped = false;
i += 1;
continue;
}
if b == b'\\' && !in_single {
escaped = true;
i += 1;
continue;
}
if b == b'\'' && !in_double {
in_single = !in_single;
i += 1;
continue;
}
if b == b'"' && !in_single {
in_double = !in_double;
i += 1;
continue;
}
if in_single || in_double {
i += 1;
continue;
}
match b {
b'|' => {
if i + 1 < len && bytes[i + 1] == b'|' {
return None;
}
return Some(StructuralBoundary {
byte_pos: i,
kind: BoundaryKind::Pipe,
});
}
b'&' => {
if i + 1 < len && bytes[i + 1] == b'&' {
return None;
}
let prev = if i > 0 { bytes[i - 1] } else { 0 };
if prev == b'>' {
i += 1;
continue;
}
return Some(StructuralBoundary {
byte_pos: i,
kind: BoundaryKind::Background,
});
}
b'>' => {
if i + 1 < len && bytes[i + 1] == b'&' {
i += 2;
if i < len && bytes[i].is_ascii_digit() {
i += 1;
}
continue;
}
if i + 1 < len && bytes[i + 1] == b'(' {
return None;
}
return Some(StructuralBoundary {
byte_pos: i,
kind: BoundaryKind::OutputRedirect,
});
}
b'<' => {
if i + 1 < len && bytes[i + 1] == b'(' {
return None;
}
return Some(StructuralBoundary {
byte_pos: i,
kind: BoundaryKind::InputRedirect,
});
}
_ => {}
}
i += 1;
}
None
}
fn suffix_is_benign(kind: BoundaryKind, suffix: &str) -> bool {
match kind {
BoundaryKind::InputRedirect => false,
BoundaryKind::Background => {
let after = suffix.trim_start_matches('&').trim();
after.is_empty()
}
BoundaryKind::OutputRedirect => {
!suffix_has_disallowed_followup(suffix)
}
BoundaryKind::Pipe => {
let rhs = suffix.trim_start_matches('|').trim();
if rhs.is_empty() {
return false;
}
let rhs_cmd = rhs.split_whitespace().next().unwrap_or("");
let rhs_base = rhs_cmd.rsplit('/').next().unwrap_or(rhs_cmd);
if !BENIGN_PAGER_COMMANDS.contains(&rhs_base) {
return false;
}
!suffix_has_disallowed_followup(rhs)
}
}
}
fn suffix_has_disallowed_followup(s: &str) -> bool {
let mut rest = s;
loop {
match find_first_structural_boundary(rest) {
Some(b) => {
if matches!(
b.kind,
BoundaryKind::Pipe | BoundaryKind::Background | BoundaryKind::InputRedirect
) {
return true;
}
let next = b.byte_pos + 1;
if next >= rest.len() {
return false;
}
rest = &rest[next..];
}
None => return false,
}
}
}
fn split_and_chain(cmd: &str) -> Option<Vec<&str>> {
if !cmd.contains("&&") {
return None;
}
let mut segments = Vec::new();
let mut current_start = 0;
let mut in_single = false;
let mut in_double = false;
let mut escaped = false;
let bytes = cmd.as_bytes();
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if escaped {
escaped = false;
i += 1;
continue;
}
if b == b'\\' && !in_single {
escaped = true;
i += 1;
continue;
}
if b == b'\'' && !in_double {
in_single = !in_single;
} else if b == b'"' && !in_single {
in_double = !in_double;
} else if !in_single
&& !in_double
&& b == b'&'
&& i + 1 < bytes.len()
&& bytes[i + 1] == b'&'
{
let segment = &cmd[current_start..i];
let trimmed = segment.trim();
if !trimmed.is_empty() {
segments.push(trimmed);
}
current_start = i + 2;
i += 1; }
i += 1;
}
let final_segment = &cmd[current_start..];
let trimmed = final_segment.trim();
if !trimmed.is_empty() {
segments.push(trimmed);
}
if segments.len() > 1 {
Some(segments)
} else {
None
}
}
#[allow(dead_code)] fn split_multi_command(cmd: &str) -> Option<Vec<&str>> {
if !cmd.contains("&&") && !cmd.contains("||") && !cmd.contains(';') {
return None;
}
let mut segments = Vec::new();
let mut current_start = 0;
let mut in_single = false;
let mut in_double = false;
let mut escaped = false;
let chars: Vec<char> = cmd.chars().collect();
let mut i = 0;
while i < chars.len() {
let c = chars[i];
if escaped {
escaped = false;
i += 1;
continue;
}
if c == '\\' && !in_single {
escaped = true;
i += 1;
continue;
}
if c == '\'' && !in_double {
in_single = !in_single;
} else if c == '"' && !in_single {
in_double = !in_double;
} else if !in_single && !in_double {
if c == ';' {
let segment = &cmd[current_start..byte_index(&chars, i)];
let trimmed = segment.trim();
if !trimmed.is_empty() {
segments.push(trimmed);
}
current_start = byte_index(&chars, i + 1);
} else if c == '&' && i + 1 < chars.len() && chars[i + 1] == '&' {
let segment = &cmd[current_start..byte_index(&chars, i)];
let trimmed = segment.trim();
if !trimmed.is_empty() {
segments.push(trimmed);
}
current_start = byte_index(&chars, i + 2);
i += 1; } else if c == '|' && i + 1 < chars.len() && chars[i + 1] == '|' {
let segment = &cmd[current_start..byte_index(&chars, i)];
let trimmed = segment.trim();
if !trimmed.is_empty() {
segments.push(trimmed);
}
current_start = byte_index(&chars, i + 2);
i += 1; }
}
i += 1;
}
let final_segment = &cmd[current_start..];
let trimmed = final_segment.trim();
if !trimmed.is_empty() {
segments.push(trimmed);
}
if segments.len() > 1 {
Some(segments)
} else {
None
}
}
#[allow(dead_code)] fn byte_index(chars: &[char], char_idx: usize) -> usize {
chars.iter().take(char_idx).map(|c| c.len_utf8()).sum()
}
#[allow(dead_code)] fn classify_multi_command(segments: &[&str], depth: u8) -> Classification {
let mut best_compilation: Option<Classification> = None;
for &segment in segments {
let result = classify_command_inner(segment, depth + 1);
if result.is_compilation {
let dominated = match &best_compilation {
Some(prev) => result.confidence > prev.confidence,
None => true,
};
if dominated {
best_compilation = Some(result);
}
}
}
if let Some(compilation) = best_compilation {
return compilation;
}
Classification::not_compilation("no sub-command is compilation")
}
pub fn classify_command_detailed(cmd: &str) -> ClassificationDetails {
let original = cmd.to_string();
let cmd = cmd.trim();
let mut tiers = Vec::new();
if cmd.is_empty() {
let classification = Classification::not_compilation("empty command");
tiers.push(ClassificationTier {
tier: 0,
name: Cow::Borrowed(TIER_INSTANT_REJECT),
decision: TierDecision::Reject,
reason: Cow::Borrowed("empty command"),
});
return ClassificationDetails {
original,
normalized: cmd.to_string(),
tiers,
classification,
};
}
tiers.push(ClassificationTier {
tier: 0,
name: Cow::Borrowed(TIER_INSTANT_REJECT),
decision: TierDecision::Pass,
reason: Cow::Borrowed("command present"),
});
if cmd.len() < MAX_SPLIT_INPUT_LEN
&& let Some((classification, structure_reason)) = try_classify_compound_command(cmd)
.map(|c| (c, "safe compound command with compilation suffix"))
.or_else(|| {
try_classify_wrapped_command(cmd)
.map(|c| (c, "shell -c wrapper with compilation command"))
})
.or_else(|| {
try_classify_benign_suffix_command(cmd).map(|c| {
(
c,
"compilation command with benign trailing redirect/pipe/background",
)
})
})
{
let normalized = classification
.extracted_command
.clone()
.unwrap_or_else(|| cmd.to_string());
tiers.push(ClassificationTier {
tier: 1,
name: Cow::Borrowed(TIER_STRUCTURE_ANALYSIS),
decision: TierDecision::Pass,
reason: Cow::Borrowed(structure_reason),
});
tiers.push(ClassificationTier {
tier: 2,
name: Cow::Borrowed(TIER_KEYWORD_FILTER),
decision: TierDecision::Pass,
reason: Cow::Borrowed("keyword present in extracted command"),
});
tiers.push(ClassificationTier {
tier: 3,
name: Cow::Borrowed(TIER_NEVER_INTERCEPT),
decision: TierDecision::Pass,
reason: Cow::Borrowed("extracted command passed never-intercept checks"),
});
tiers.push(ClassificationTier {
tier: 4,
name: Cow::Borrowed(TIER_FULL_CLASSIFICATION),
decision: TierDecision::Pass,
reason: classification.reason.clone(),
});
return ClassificationDetails {
original,
normalized,
tiers,
classification,
};
}
if let Some(reason) = check_structure(cmd) {
let classification = Classification::not_compilation(reason);
tiers.push(ClassificationTier {
tier: 1,
name: Cow::Borrowed(TIER_STRUCTURE_ANALYSIS),
decision: TierDecision::Reject,
reason: Cow::Borrowed(reason),
});
return ClassificationDetails {
original,
normalized: cmd.to_string(),
tiers,
classification,
};
}
tiers.push(ClassificationTier {
tier: 1,
name: Cow::Borrowed(TIER_STRUCTURE_ANALYSIS),
decision: TierDecision::Pass,
reason: Cow::Borrowed("no pipes/redirects/backgrounding"),
});
let normalized_cow = normalize_command(cmd);
let normalized = normalized_cow.as_ref();
if !starts_with_compilation_command(normalized) {
let classification = Classification::not_compilation("no compilation keyword");
tiers.push(ClassificationTier {
tier: 2,
name: Cow::Borrowed(TIER_KEYWORD_FILTER),
decision: TierDecision::Reject,
reason: Cow::Borrowed("no compilation keyword"),
});
return ClassificationDetails {
original,
normalized: normalized.to_string(),
tiers,
classification,
};
}
tiers.push(ClassificationTier {
tier: 2,
name: Cow::Borrowed(TIER_KEYWORD_FILTER),
decision: TierDecision::Pass,
reason: Cow::Borrowed("keyword present"),
});
for pattern in NEVER_INTERCEPT {
if let Some(rest) = normalized.strip_prefix(pattern)
&& (rest.is_empty() || rest.starts_with(' '))
{
let reason: Cow<'static, str> =
Cow::Owned(format!("matches never-intercept: {pattern}"));
let classification = Classification::not_compilation(reason.clone());
tiers.push(ClassificationTier {
tier: 3,
name: Cow::Borrowed(TIER_NEVER_INTERCEPT),
decision: TierDecision::Reject,
reason,
});
return ClassificationDetails {
original,
normalized: normalized.to_string(),
tiers,
classification,
};
}
}
tiers.push(ClassificationTier {
tier: 3,
name: Cow::Borrowed(TIER_NEVER_INTERCEPT),
decision: TierDecision::Pass,
reason: Cow::Borrowed("no never-intercept match"),
});
let classification = classify_full(normalized);
let decision = if classification.is_compilation {
TierDecision::Pass
} else {
TierDecision::Reject
};
tiers.push(ClassificationTier {
tier: 4,
name: Cow::Borrowed(TIER_FULL_CLASSIFICATION),
decision,
reason: classification.reason.clone(),
});
ClassificationDetails {
original,
normalized: normalized.to_string(),
tiers,
classification,
}
}
fn wrapper_flag_takes_value(wrapper: &str, flag: &str) -> bool {
match wrapper {
"env" => matches!(flag, "-u" | "--unset"),
"nice" => matches!(flag, "-n" | "--adjustment"),
"ionice" => matches!(
flag,
"-c" | "--class" | "-n" | "--classdata" | "-p" | "--pid"
),
"taskset" => matches!(flag, "-c" | "--cpu-list" | "-p" | "--pid"),
"chrt" => matches!(flag, "-p" | "--pid"),
"timeout" => matches!(flag, "-s" | "--signal" | "-k" | "--kill-after"),
_ => false,
}
}
fn looks_like_duration_or_priority(token: &str) -> bool {
token.chars().next().is_some_and(|c| c.is_ascii_digit())
}
pub(crate) fn split_wrapper_word(input: &str) -> Option<(&str, &str)> {
let input = input.trim_start();
let first = input.chars().next()?;
if matches!(first, '\'' | '"') {
let end = input[1..].find(first)? + 1;
let word = &input[1..end];
let rest = &input[end + 1..];
if !rest.is_empty() && !rest.starts_with(char::is_whitespace) {
return None;
}
if first == '"' && word.contains(['\\', '$', '`']) {
return None;
}
Some((word, rest.trim_start()))
} else {
let (word, rest) = input.split_once(char::is_whitespace).unwrap_or((input, ""));
if word.contains(['\'', '"', '\\']) {
return None;
}
Some((word, rest.trim_start()))
}
}
pub fn normalize_command(cmd: &str) -> Cow<'_, str> {
let mut result = cmd.trim();
let wrappers = [
"sudo", "env", "time", "timeout", "nice", "ionice", "chrt", "strace", "ltrace", "perf",
"taskset", "numactl",
];
loop {
let mut changed = false;
for wrapper in wrappers {
if let Some(rest) = result.strip_prefix(wrapper) {
if rest.is_empty() || rest.starts_with(char::is_whitespace) {
result = rest.trim_start();
changed = true;
while let Some((flag, rest_after_flag)) = split_wrapper_word(result) {
if !flag.starts_with('-') {
break;
}
result = rest_after_flag;
if wrapper_flag_takes_value(wrapper, flag) {
let Some((_value, rest_after_value)) = split_wrapper_word(result)
else {
break;
};
result = rest_after_value;
}
}
if matches!(wrapper, "timeout" | "chrt")
&& let Some((first, after_first)) = result.split_once(char::is_whitespace)
&& !first.starts_with('-')
&& looks_like_duration_or_priority(first)
{
result = after_first.trim_start();
}
}
}
}
let chars = result.chars();
let mut token_len = 0;
let mut in_quote = None; let mut escaped = false;
let mut has_equals = false;
let mut has_space = false;
for c in chars {
if escaped {
escaped = false;
token_len += c.len_utf8();
continue;
}
if c == '\\' {
escaped = true;
token_len += c.len_utf8();
continue;
}
if let Some(q) = in_quote {
if c == q {
in_quote = None;
} else if c == '=' {
has_equals = true;
}
token_len += c.len_utf8();
} else if c == '"' || c == '\'' {
in_quote = Some(c);
token_len += c.len_utf8();
} else if c.is_whitespace() {
has_space = true;
break;
} else {
if c == '=' {
has_equals = true;
}
token_len += c.len_utf8();
}
}
if has_equals && in_quote.is_none() && has_space {
result = result[token_len..].trim_start();
changed = true;
}
if result.starts_with('/') {
if let Some(space_idx) = result.find(' ') {
let cmd_part = &result[..space_idx];
if let Some(last_slash) = cmd_part.rfind('/') {
result = &result[last_slash + 1..];
changed = true;
}
} else {
if let Some(last_slash) = result.rfind('/') {
result = &result[last_slash + 1..];
changed = true;
}
}
}
if !changed {
break;
}
}
if result.eq(cmd) {
Cow::Borrowed(cmd)
} else {
Cow::Owned(result.to_string())
}
}
fn has_file_redirect(cmd: &str) -> bool {
let bytes = cmd.as_bytes();
let len = bytes.len();
let mut in_single = false;
let mut in_double = false;
let mut escaped = false;
let mut i = 0;
while i < len {
let b = bytes[i];
if escaped {
escaped = false;
i += 1;
continue;
}
if b == b'\\' && !in_single {
escaped = true;
i += 1;
continue;
}
match b {
b'\'' if !in_double => {
in_single = !in_single;
}
b'"' if !in_single => {
in_double = !in_double;
}
b'>' if !in_single && !in_double => {
if i + 1 < len && bytes[i + 1] == b'&' {
i += 2; if i < len && bytes[i].is_ascii_digit() {
i += 1;
}
continue;
}
if i + 1 < len && bytes[i + 1] == b'(' {
i += 1;
continue;
}
return true; }
b'<' if !in_single && !in_double => {
if i + 1 < len && bytes[i + 1] == b'(' {
i += 1;
continue;
}
return true; }
_ => {}
}
i += 1;
}
false
}
fn check_structure(cmd: &str) -> Option<&'static str> {
let bytes = cmd.as_bytes();
let len = bytes.len();
let mut in_single = false;
let mut in_double = false;
let mut escaped = false;
let mut found_backgrounded = false;
let mut found_piped = false;
let mut found_subshell = false;
let mut found_output_redirect = false;
let mut found_input_redirect = false;
let mut found_semicolon = false;
let mut found_and_chain = false;
let mut found_or_chain = false;
let mut found_subshell_capture = false;
let mut i = 0;
while i < len {
let b = bytes[i];
if escaped {
escaped = false;
i += 1;
continue;
}
if b == b'\\' && !in_single {
escaped = true;
i += 1;
continue;
}
if b == b'\'' && !in_double {
in_single = !in_single;
i += 1;
continue;
}
if b == b'"' && !in_single {
in_double = !in_double;
i += 1;
continue;
}
if in_single || in_double {
i += 1;
continue;
}
match b {
b'\n' | b'\r' => return Some("contains embedded newline"),
b'&' => {
if i + 1 < len && bytes[i + 1] == b'&' {
found_and_chain = true;
i += 1; } else {
let prev = if i > 0 { bytes[i - 1] } else { 0 };
let next = if i + 1 < len { bytes[i + 1] } else { 0 };
if prev != b'>' && next != b'>' {
found_backgrounded = true;
}
}
}
b'|' => {
if i + 1 < len && bytes[i + 1] == b'|' {
found_or_chain = true;
i += 1; } else {
found_piped = true;
}
}
b'(' => found_subshell = true,
b'>' => {
if i + 1 < len && bytes[i + 1] == b'(' {
found_subshell = true;
} else if i + 1 < len && bytes[i + 1] == b'&' {
i += 1; if i + 1 < len && bytes[i + 1].is_ascii_digit() {
i += 1; }
} else if i + 1 < len && bytes[i + 1] == b'>' {
found_output_redirect = true;
i += 1; } else {
found_output_redirect = true;
}
}
b'<' => {
if i + 1 < len && bytes[i + 1] == b'(' {
found_subshell = true;
} else {
found_input_redirect = true;
}
}
b';' => found_semicolon = true,
b'`' => found_subshell_capture = true,
b'$' if i + 1 < len && bytes[i + 1] == b'(' => {
found_subshell_capture = true;
}
_ => {}
}
i += 1;
}
if found_backgrounded {
return Some("backgrounded command");
}
if found_piped && !found_or_chain {
return Some("piped command");
}
if found_subshell {
return Some("subshell execution");
}
if found_output_redirect {
return Some("output redirected");
}
if found_input_redirect {
return Some("input redirected");
}
if found_semicolon {
return Some("chained command (;)");
}
if found_and_chain {
return Some("chained command (&&)");
}
if found_or_chain {
return Some("chained command (||)");
}
if found_subshell_capture {
return Some("subshell capture");
}
None
}
fn starts_with_compilation_command(cmd: &str) -> bool {
let cmd = cmd.trim_start();
COMPILATION_KEYWORDS
.iter()
.any(|keyword| command_word_matches(cmd, keyword))
}
fn command_word_matches(cmd: &str, keyword: &str) -> bool {
let Some(rest) = cmd.strip_prefix(keyword) else {
return false;
};
rest.is_empty() || rest.starts_with(char::is_whitespace)
}
fn positional_build_targets<'a>(cmd: &'a str, value_flags: &[&str]) -> Vec<&'a str> {
let mut targets = Vec::new();
let toks: Vec<&str> = cmd.split_whitespace().skip(1).collect();
let mut i = 0;
while i < toks.len() {
let tok = toks[i];
if value_flags.contains(&tok) {
i += 2; } else if tok.starts_with('-') || tok.contains('=') {
i += 1; } else {
targets.push(tok);
i += 1;
}
}
targets
}
fn classify_full(cmd: &str) -> Classification {
if cmd.starts_with("cargo ") || cmd.eq("cargo") {
return classify_cargo(cmd);
}
if cmd.starts_with("cargo-zigbuild ") || cmd.eq("cargo-zigbuild") {
return classify_cargo_zigbuild(cmd);
}
if cmd.starts_with("cargo-xwin ") || cmd.eq("cargo-xwin") {
return if let Some(kind) = cargo_xwin_compilation_kind(cmd) {
Classification::compilation(kind, 0.95, "cargo-xwin compilation")
} else {
Classification::not_compilation("cargo-xwin subcommand not interceptable")
};
}
if cmd.starts_with("rustc ") || cmd.eq("rustc") {
return Classification::compilation(CompilationKind::Rustc, 0.95, "rustc invocation");
}
if cmd.starts_with("gcc ")
&& (cmd.contains(" -c ") || cmd.contains(" -o ") || cmd.contains(".c"))
{
return Classification::compilation(CompilationKind::Gcc, 0.90, "gcc compilation");
}
if cmd.starts_with("g++ ")
&& (cmd.contains(" -c ")
|| cmd.contains(" -o ")
|| cmd.contains(".cpp")
|| cmd.contains(".cc"))
{
return Classification::compilation(CompilationKind::Gpp, 0.90, "g++ compilation");
}
if cmd.starts_with("clang ")
&& !cmd.starts_with("clang++ ")
&& (cmd.contains(" -c ") || cmd.contains(" -o ") || cmd.contains(".c"))
{
return Classification::compilation(CompilationKind::Clang, 0.90, "clang compilation");
}
if cmd.starts_with("clang++ ")
&& (cmd.contains(" -c ")
|| cmd.contains(" -o ")
|| cmd.contains(".cpp")
|| cmd.contains(".cc"))
{
return Classification::compilation(CompilationKind::Clangpp, 0.90, "clang++ compilation");
}
if cmd.starts_with("cc ")
&& (cmd.contains(" -c ") || cmd.contains(" -o ") || cmd.contains(".c"))
{
return Classification::compilation(CompilationKind::Gcc, 0.85, "cc compilation");
}
if cmd.starts_with("c++ ")
&& (cmd.contains(" -c ")
|| cmd.contains(" -o ")
|| cmd.contains(".cpp")
|| cmd.contains(".cc"))
{
return Classification::compilation(CompilationKind::Gpp, 0.85, "c++ compilation");
}
if cmd.starts_with("make") && (cmd.eq("make") || cmd.starts_with("make ")) {
const MAKE_VALUE_FLAGS: [&str; 8] = [
"-f",
"-C",
"-I",
"-o",
"-W",
"--file",
"--directory",
"--include-dir",
];
const MAKE_MAINTENANCE_TARGETS: [&str; 4] = ["clean", "install", "distclean", "uninstall"];
let is_maintenance = positional_build_targets(cmd, &MAKE_VALUE_FLAGS)
.iter()
.any(|target| MAKE_MAINTENANCE_TARGETS.contains(target));
if is_maintenance {
return Classification::not_compilation("make maintenance command");
}
return Classification::compilation(CompilationKind::Make, 0.85, "make build");
}
if cmd.starts_with("cmake ") || cmd.eq("cmake") {
let mut tokens = cmd.split_whitespace();
let _ = tokens.next(); if tokens.any(|token| token.eq("--build") || token.starts_with("--build=")) {
return Classification::compilation(CompilationKind::CmakeBuild, 0.90, "cmake --build");
}
}
if cmd.starts_with("ninja") && (cmd.eq("ninja") || cmd.starts_with("ninja ")) {
const NINJA_VALUE_FLAGS: [&str; 8] = ["-C", "-f", "-j", "-k", "-l", "-d", "-t", "-w"];
let raw: Vec<&str> = cmd.split_whitespace().collect();
let is_clean_tool = raw.windows(2).any(|w| w[0] == "-t" && w[1] == "clean");
let is_clean_target = positional_build_targets(cmd, &NINJA_VALUE_FLAGS).contains(&"clean");
if is_clean_tool || is_clean_target {
return Classification::not_compilation("ninja clean");
}
return Classification::compilation(CompilationKind::Ninja, 0.90, "ninja build");
}
if cmd.starts_with("meson ") || cmd.eq("meson") {
let mut tokens = cmd.split_whitespace();
let _ = tokens.next(); let mut subcommand = None;
for token in tokens {
if token.starts_with('-') {
continue;
}
subcommand = Some(token);
break;
}
if matches!(subcommand, Some("compile")) {
return Classification::compilation(CompilationKind::Meson, 0.85, "meson compile");
}
}
if cmd.eq("nix-build")
|| cmd.starts_with("nix-build ")
|| cmd.eq("nix")
|| cmd.starts_with("nix ")
{
return classify_nix(cmd);
}
let mut tokens = cmd.split_whitespace();
if tokens.next().is_some_and(|token| token.eq("bun")) {
match tokens.next() {
Some("test") => {
if tokens.any(|a| a.eq("-w") || a.eq("--watch")) {
return Classification::not_compilation(
"bun test --watch is interactive (not intercepted)",
);
}
return Classification::compilation(
CompilationKind::BunTest,
0.95,
"bun test command",
);
}
Some("typecheck") => {
if tokens.any(|a| a.eq("-w") || a.eq("--watch")) {
return Classification::not_compilation(
"bun typecheck --watch is interactive (not intercepted)",
);
}
return Classification::compilation(
CompilationKind::BunTypecheck,
0.95,
"bun typecheck command",
);
}
Some("x") => {
return Classification::not_compilation("bun x runs arbitrary packages");
}
_ => {}
}
}
if cmd.starts_with("go ") {
return classify_go(cmd);
}
if let Some(rest) = cmd
.strip_prefix("tsc ")
.or_else(|| cmd.strip_prefix("npx tsc "))
{
return classify_tsc(rest);
}
Classification::not_compilation("no matching pattern")
}
fn classify_tsc(args: &str) -> Classification {
let tokens = args.split_whitespace();
let mut no_emit = false;
for token in tokens {
if token.eq("-w") || token.eq("--watch") || token.eq("--watchFile") {
return Classification::not_compilation("tsc --watch is interactive (not intercepted)");
}
if token.eq_ignore_ascii_case("--noemit") {
no_emit = true;
}
}
if !no_emit {
return Classification::not_compilation(
"tsc without --noEmit emits output files locally (not intercepted)",
);
}
Classification::compilation(CompilationKind::Tsc, 0.95, "tsc --noEmit typecheck")
}
fn literal_flag_words(mut command: &str) -> Option<Vec<&str>> {
if command.len() > 65_536 || command.contains(['\0', '\r', '\n']) {
return None;
}
let mut words = Vec::new();
while !command.trim().is_empty() {
command = command.trim_start();
let quoted = command.starts_with(['\'', '"']);
let (word, rest) = split_wrapper_word(command)?;
if !quoted
&& word.contains([
'$', '`', '*', '?', '[', '{', '}', '~', '|', '&', ';', '<', '>', '(', ')', '#',
])
{
return None;
}
words.push(word);
if words.len() > 4096 {
return None;
}
command = rest;
}
Some(words)
}
fn go_linker_flags_have_one_output(value: &str) -> bool {
let mut flags = value.split_whitespace();
let mut present = false;
while let Some(flag) = flags.next() {
present = true;
match flag {
"-s" | "-w" | "-s=true" | "-s=false" | "-w=true" | "-w=false" => {}
"-X" => {
if !flags.next().is_some_and(|value| {
value
.split_once('=')
.is_some_and(|(name, _)| !name.is_empty())
}) {
return false;
}
}
_ => return false,
}
}
present
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GoBuildOutput {
pub path: String,
pub option_index: usize,
}
#[must_use]
pub fn go_build_output(command: &str) -> Option<GoBuildOutput> {
let normalized = normalize_command(command);
let words = literal_flag_words(&normalized)?;
let mut args = words.into_iter().enumerate();
if args.next()?.1 != "go" || args.next()?.1 != "build" {
return None;
}
let mut output = None;
let mut packages_started = false;
while let Some((option_index, arg)) = args.next() {
if !arg.starts_with('-') {
packages_started = true;
continue;
}
if packages_started {
return None;
}
let (flag, inline) = arg
.split_once('=')
.map_or((arg, None), |(flag, value)| (flag, Some(value)));
match flag {
"-o" => {
let path = inline.or_else(|| args.next().map(|(_, value)| value))?;
if output
.replace(GoBuildOutput {
path: path.to_owned(),
option_index,
})
.is_some()
|| path.is_empty()
|| path == "-"
|| path.ends_with('/')
|| path.chars().any(char::is_control)
|| !std::path::Path::new(path).components().all(|part| {
matches!(
part,
std::path::Component::Normal(_) | std::path::Component::CurDir
)
})
|| !std::path::Path::new(path)
.components()
.any(|part| matches!(part, std::path::Component::Normal(_)))
{
return None;
}
}
"-buildmode" => {
if !matches!(
inline.or_else(|| args.next().map(|(_, value)| value))?,
"default" | "exe" | "pie"
) {
return None;
}
}
"-compiler" => {
if inline.or_else(|| args.next().map(|(_, value)| value))? != "gc" {
return None;
}
}
"-p" | "-tags" | "-covermode" | "-coverpkg" => {
if inline
.or_else(|| args.next().map(|(_, value)| value))?
.is_empty()
{
return None;
}
}
"-ldflags" => {
if !go_linker_flags_have_one_output(
inline.or_else(|| args.next().map(|(_, value)| value))?,
) {
return None;
}
}
"-mod" => {
if !matches!(
inline.or_else(|| args.next().map(|(_, value)| value))?,
"readonly" | "vendor"
) {
return None;
}
}
"-buildvcs" => {
if inline.is_some_and(|value| !matches!(value, "auto" | "true" | "false")) {
return None;
}
}
"-a" | "-race" | "-msan" | "-asan" | "-v" | "-x" | "-trimpath" | "-modcacherw"
| "-cover" => {
if inline.is_some_and(|value| !matches!(value, "true" | "false")) {
return None;
}
}
_ => return None,
}
}
output
}
fn classify_go(cmd: &str) -> Classification {
let mut tokens = cmd.split_whitespace();
tokens.next();
let subcommand = match tokens.next() {
Some(sub) => sub,
None => return Classification::not_compilation("bare `go` is not a compilation command"),
};
let args: Vec<&str> = tokens.collect();
match subcommand {
"build" => {
if go_build_output(cmd).is_none() {
return Classification::not_compilation(
"go build requires a supported explicit -o file for artifact delivery",
);
}
if !cfg!(unix) {
return Classification::not_compilation(
"Go output delivery requires a Unix dispatcher and worker",
);
}
Classification::compilation(CompilationKind::GoBuild, 0.95, "go build command")
}
"test" => {
if args.iter().any(|arg| {
let arg = arg.trim_matches(['\'', '"']);
if !arg.starts_with('-') {
return false;
}
let flag = arg.split_once('=').map_or(arg, |(flag, _)| flag);
let flag = flag.trim_start_matches('-');
let flag = flag.strip_prefix("test.").unwrap_or(flag);
matches!(
flag,
"c" | "o"
| "exec"
| "coverprofile"
| "cpuprofile"
| "memprofile"
| "blockprofile"
| "mutexprofile"
| "trace"
| "outputdir"
| "fuzz"
| "fuzzcachedir"
| "gocoverdir"
| "testlogfile"
)
}) {
return Classification::not_compilation(
"go test emits files or uses a local execution wrapper (not intercepted)",
);
}
Classification::compilation(CompilationKind::GoTest, 0.95, "go test command")
}
"vet" => Classification::compilation(CompilationKind::GoVet, 0.95, "go vet command"),
_ => Classification::not_compilation("go subcommand is not a compilation command"),
}
}
pub fn is_cargo_package_verification(command: &str) -> bool {
let normalized = normalize_command(command);
let cmd = normalized.as_ref();
if check_structure(cmd).is_some() || cmd.contains(['\'', '"', '\\', '$', '`']) {
return false;
}
let mut tokens = cmd.split_whitespace();
if tokens.next() != Some("cargo") {
return false;
}
let mut subcommand = None;
let mut dry_run = false;
const VALUE_FLAGS: &[&str] = &[
"--index",
"--registry",
"--token",
"--color",
"--config",
"-Z",
"-C",
"-p",
"--package",
"--exclude",
"-F",
"--features",
"-j",
"--jobs",
"--target",
"--target-dir",
"-m",
"--manifest-path",
"--lockfile-path",
"--message-format",
];
while let Some(token) = tokens.next() {
if subcommand.is_none() && token.starts_with('+') {
continue;
}
if matches!(token, "package" | "publish") && subcommand.is_none() {
subcommand = Some(token);
continue;
}
if VALUE_FLAGS.contains(&token) {
if tokens.next().is_none_or(|value| value.starts_with('-')) {
return false;
}
continue;
}
if let Some((flag, value)) = token.split_once('=')
&& VALUE_FLAGS.contains(&flag)
&& !value.is_empty()
{
continue;
}
if ["-j", "-p", "-F", "-m", "-Z"]
.iter()
.any(|flag| token.starts_with(flag) && token.len() > flag.len())
{
continue;
}
match token {
"--dry-run" | "-n" => dry_run = true,
"--workspace"
| "--allow-dirty"
| "--no-metadata"
| "--exclude-lockfile"
| "--all-features"
| "--no-default-features"
| "--keep-going"
| "--locked"
| "--offline"
| "--frozen"
| "--verbose"
| "-v"
| "-vv"
| "-vvv"
| "--quiet"
| "-q" => {}
_ => return false,
}
}
matches!(subcommand, Some("package")) || (subcommand == Some("publish") && dry_run)
}
#[must_use]
pub fn cargo_xwin_compilation_kind(command: &str) -> Option<CompilationKind> {
let normalized = normalize_command(command);
let cmd = normalized.as_ref();
let mut tokens = cmd.split_whitespace().map(unquote_literal_option_word);
if check_structure(cmd).is_some()
|| tokens
.clone()
.any(|token| token.contains(['\'', '"', '\\', '$', '`']))
{
return None;
}
let subcommand = match tokens.next() {
Some("cargo-xwin") => {
let subcommand = tokens.next();
if subcommand == Some("xwin") {
tokens.next()
} else {
subcommand
}
}
Some("cargo") => loop {
match tokens.next() {
Some("xwin") => break tokens.next(),
Some("--color" | "--config" | "-Z" | "-C") => {
if tokens.next().is_none_or(|value| value.starts_with('-')) {
return None;
}
}
Some("--locked" | "--offline" | "--frozen" | "-v" | "-vv" | "-q") => {}
Some(token)
if token.starts_with('+')
|| token.starts_with("--color=")
|| token.starts_with("--config=") => {}
_ => return None,
}
},
_ => return None,
};
let kind = match subcommand? {
"build" => CompilationKind::CargoBuild,
"check" => CompilationKind::CargoCheck,
"clippy" => CompilationKind::CargoClippy,
_ => return None,
};
if tokens.any(|token| matches!(token, "--help" | "-h" | "--version" | "-V")) {
return None;
}
Some(kind)
}
#[must_use]
pub fn is_cargo_xwin_build(command: &str) -> bool {
matches!(
cargo_xwin_compilation_kind(command),
Some(CompilationKind::CargoBuild)
)
}
fn unquote_whole_word(token: &str) -> &str {
token
.strip_prefix('\'')
.and_then(|rest| rest.strip_suffix('\''))
.filter(|inner| !inner.contains('\''))
.unwrap_or(token)
}
pub(crate) fn unquote_literal_option_word(token: &str) -> &str {
let word = unquote_whole_word(token);
if word.starts_with('-')
&& word
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || b"-_./=:+,".contains(&byte))
{
word
} else {
token
}
}
fn classify_cargo(cmd: &str) -> Classification {
let mut tokens = cmd.split_whitespace();
let _cargo = tokens.next();
const CARGO_VALUE_FLAGS: &[&str] = &["--color", "-Z", "-C", "--config"];
let subcommand = loop {
let Some(tok) = tokens.next().map(unquote_whole_word) else {
return Classification::not_compilation("bare cargo command");
};
if tok.starts_with('+') {
continue;
}
if tok.starts_with('-') {
if !tok.contains('=') && CARGO_VALUE_FLAGS.contains(&tok) {
let _ = tokens.next();
}
continue;
}
break tok;
};
match subcommand {
"build" | "b" => {
Classification::compilation(CompilationKind::CargoBuild, 0.95, "cargo build")
}
"package" | "publish" => {
if is_cargo_package_verification(cmd) {
Classification::compilation(
CompilationKind::CargoBuild,
0.95,
"cargo package verification (includes build)",
)
} else {
Classification::not_compilation(
"cargo package/publish without supported verification (never-intercept)",
)
}
}
"test" | "t" => Classification::compilation(CompilationKind::CargoTest, 0.95, "cargo test"),
"check" | "c" => {
Classification::compilation(CompilationKind::CargoCheck, 0.90, "cargo check")
}
"clippy" => Classification::compilation(CompilationKind::CargoClippy, 0.90, "cargo clippy"),
"doc" => Classification::compilation(CompilationKind::CargoDoc, 0.85, "cargo doc"),
"run" | "r" => {
Classification::compilation(
CompilationKind::CargoBuild,
0.85,
"cargo run (includes build)",
)
}
"bench" => Classification::compilation(CompilationKind::CargoBench, 0.90, "cargo bench"),
"zigbuild" => {
Classification::compilation(CompilationKind::CargoZigbuild, 0.95, "cargo zigbuild")
}
"xwin" => {
if let Some(kind) = cargo_xwin_compilation_kind(cmd) {
Classification::compilation(kind, 0.95, "cargo xwin compilation")
} else {
Classification::not_compilation("cargo xwin subcommand not interceptable")
}
}
"nextest" => {
let Some(nextest_sub) = tokens.next() else {
return Classification::not_compilation("bare cargo nextest without subcommand");
};
match nextest_sub {
"run" | "r" => Classification::compilation(
CompilationKind::CargoNextest,
0.95,
"cargo nextest run",
),
_ => Classification::not_compilation("cargo nextest subcommand not interceptable"),
}
}
_ => Classification::not_compilation("cargo subcommand not interceptable"),
}
}
fn classify_cargo_zigbuild(cmd: &str) -> Classification {
let mut tokens = cmd.split_whitespace();
let _bin = tokens.next();
const CARGO_VALUE_FLAGS: &[&str] = &["--color", "-Z", "-C", "--config"];
let subcommand = loop {
let Some(tok) = tokens.next().map(unquote_whole_word) else {
return Classification::not_compilation("bare cargo-zigbuild command");
};
if tok.starts_with('+') {
continue;
}
if tok.starts_with('-') {
if !tok.contains('=') && CARGO_VALUE_FLAGS.contains(&tok) {
let _ = tokens.next();
}
continue;
}
break tok;
};
match subcommand {
"zigbuild" | "build" | "b" => Classification::compilation(
CompilationKind::CargoZigbuild,
0.95,
"cargo-zigbuild build",
),
_ => Classification::not_compilation("cargo-zigbuild subcommand not interceptable"),
}
}
const NIX_GLOBAL_VALUE_FLAGS: &[&str] = &[
"--extra-experimental-features",
"--experimental-features",
"--log-format",
"--max-jobs",
"-j",
"--cores",
"--builders",
"--store",
"--file",
"-f",
"-I",
"--include",
];
fn classify_nix(cmd: &str) -> Classification {
if cmd.eq("nix-build") || cmd.starts_with("nix-build ") {
return Classification::compilation(
CompilationKind::NixBuild,
0.9,
"nix-build (legacy) derivation build",
);
}
let mut tokens = cmd.split_whitespace();
let _nix = tokens.next();
let subcommand = loop {
let Some(tok) = tokens.next() else {
return Classification::not_compilation("bare nix command (no subcommand)");
};
if tok.starts_with('-') {
if tok.eq("--option") {
let _ = tokens.next();
let _ = tokens.next();
} else if !tok.contains('=') && NIX_GLOBAL_VALUE_FLAGS.contains(&tok) {
let _ = tokens.next();
}
continue;
}
break tok;
};
let rest: Vec<&str> = tokens.collect();
let wants_help = rest.iter().any(|t| t.eq(&"--help") || t.eq(&"-h"));
if wants_help {
return Classification::not_compilation("nix --help (not a build)");
}
let has_command_flag = rest.iter().any(|t| t.eq(&"-c") || t.eq(&"--command"));
match subcommand {
"build" => {
Classification::compilation(CompilationKind::NixBuild, 0.9, "nix build derivation")
}
"flake" => {
let flake_sub = rest.iter().find(|t| !t.starts_with('-'));
if flake_sub == Some(&"check") {
Classification::compilation(
CompilationKind::NixBuild,
0.9,
"nix flake check (evaluates + builds all outputs)",
)
} else {
Classification::not_compilation("nix flake subcommand is not a build")
}
}
"develop" | "shell" => {
if has_command_flag {
let reason = if subcommand == "develop" {
"nix develop -c (non-interactive command in dev shell)"
} else {
"nix shell -c (non-interactive command with packages)"
};
Classification::compilation(CompilationKind::NixBuild, 0.9, reason)
} else {
Classification::not_compilation(
"interactive nix shell (no -c/--command; not intercepted)",
)
}
}
_ => Classification::not_compilation("nix subcommand is not an offloadable build"),
}
}
pub fn split_shell_commands(cmd: &str) -> Vec<&str> {
let bytes = cmd.as_bytes();
let len = bytes.len();
let mut segments: Vec<&str> = Vec::new();
let mut start = 0;
let mut i = 0;
let mut in_single = false;
let mut in_double = false;
let mut in_backtick = false;
let mut escaped = false;
while i < len {
let b = bytes[i];
if escaped {
escaped = false;
i += 1;
continue;
}
if b == b'\\' && !in_single {
escaped = true;
i += 1;
continue;
}
if !in_double && !in_backtick && b == b'\'' {
in_single = !in_single;
i += 1;
continue;
}
if !in_single && !in_backtick && b == b'"' {
in_double = !in_double;
i += 1;
continue;
}
if !in_single && !in_double && b == b'`' {
in_backtick = !in_backtick;
i += 1;
continue;
}
if in_single || in_double || in_backtick {
i += 1;
continue;
}
if b == b'&' && i + 1 < len && bytes[i + 1] == b'&' {
let seg = cmd[start..i].trim();
if !seg.is_empty() {
segments.push(seg);
}
i += 2;
start = i;
continue;
}
if b == b'|' && i + 1 < len && bytes[i + 1] == b'|' {
let seg = cmd[start..i].trim();
if !seg.is_empty() {
segments.push(seg);
}
i += 2;
start = i;
continue;
}
if b == b';' {
let seg = cmd[start..i].trim();
if !seg.is_empty() {
segments.push(seg);
}
i += 1;
start = i;
continue;
}
i += 1;
}
let seg = cmd[start..].trim();
if !seg.is_empty() {
segments.push(seg);
}
segments
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cargo_xwin_compilations_use_their_cargo_contracts() {
for (subcommand, kind, arguments) in [
(
"build",
CompilationKind::CargoBuild,
"--release --locked --target x86_64-pc-windows-msvc",
),
(
"check",
CompilationKind::CargoCheck,
"--locked --all-targets --target aarch64-pc-windows-msvc",
),
(
"clippy",
CompilationKind::CargoClippy,
"--locked --all-targets --target=x86_64-pc-windows-msvc -- -D warnings",
),
] {
for prefix in [
"cargo xwin",
"cargo +nightly-2026-08-31 xwin",
"cargo --color never --locked xwin",
"cargo --config build.jobs=2 xwin",
"env CARGO_HOME=/root/.cargo cargo xwin",
"cargo-xwin",
"cargo-xwin xwin",
"/home/ubuntu/.local/bin/cargo-xwin xwin",
] {
let command = format!("{prefix} {subcommand} {arguments}");
assert_eq!(
cargo_xwin_compilation_kind(&command),
Some(kind),
"{command}"
);
let classification = classify_command(&command);
assert!(classification.is_compilation, "{command}");
assert_eq!(classification.kind, Some(kind), "{command}");
}
}
}
#[test]
fn cargo_xwin_recognition_declines_unsupported_commands_and_argument_mentions() {
for command in [
"cargo xwin",
"cargo xwin env",
"cargo xwin cache clear",
"cargo xwin publish",
"cargo xwin test",
"cargo xwin run",
"cargo xwin unknown",
"cargo xwin build --help",
"cargo xwin check --help",
"cargo xwin clippy --version",
"cargo-xwin --version",
"cargo-xwin cache clear",
"cargo-xwin xwin build --help",
"cargo-xwin xwin check -h",
"cargo-xwin clippy -V",
"cargo-xwin xwin env",
"cargo-xwin check-extra",
"cargo xwin --help check",
"cargo xwin 'check'",
"cargo xwin $SUBCOMMAND",
"cargo --config xwin build --target x86_64-pc-windows-msvc",
"cargo --config xwin check --target x86_64-pc-windows-msvc",
"cargo --config xwin clippy --target x86_64-pc-windows-msvc",
"cargo build --manifest-path cargo-xwin --target x86_64-pc-windows-msvc",
"cargo check --manifest-path 'cargo xwin check' --target x86_64-pc-windows-msvc",
"cargo clippy --config 'alias.cross=xwin' --target x86_64-pc-windows-msvc",
"echo cargo xwin check",
"cargo xwin build && cargo build",
"cargo xwin check && cargo check",
"cargo xwin clippy | cat",
] {
assert_eq!(cargo_xwin_compilation_kind(command), None, "{command}");
}
for command in [
"cargo xwin env",
"cargo xwin cache clear",
"cargo xwin publish",
"cargo xwin test",
"cargo xwin run",
"cargo xwin unknown",
"cargo xwin check --help",
"cargo xwin clippy --version",
"cargo-xwin --version",
"cargo-xwin xwin check -h",
"cargo-xwin clippy -V",
"cargo-xwin xwin env",
"cargo-xwin check-extra",
"cargo xwin 'check'",
] {
assert!(!classify_command(command).is_compilation, "{command}");
}
}
use crate::test_guard;
#[test]
fn test_cargo_build_with_toolchain() {
let _guard = test_guard!();
let result = classify_command("cargo +nightly build");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
}
#[test]
fn test_nix_build_offloadable() {
let _guard = test_guard!();
for cmd in [
"nix build",
"nix build .#foo",
"nix build .#packages.x86_64-linux.default",
"nix build /path/to/flake#pkg",
"nix build --extra-experimental-features 'nix-command flakes' .#foo",
"nix --option sandbox true build .#foo",
"nix -j4 build .#foo",
"nix build nixpkgs#hello -o result-hello",
] {
let result = classify_command(cmd);
assert!(result.is_compilation, "expected offload for `{cmd}`");
assert_eq!(result.kind, Some(CompilationKind::NixBuild), "`{cmd}`");
assert!(result.confidence >= 0.85, "`{cmd}` confidence too low");
}
}
#[test]
fn test_nix_build_legacy_offloadable() {
let _guard = test_guard!();
for cmd in ["nix-build", "nix-build .", "nix-build -A hello"] {
let result = classify_command(cmd);
assert!(result.is_compilation, "expected offload for `{cmd}`");
assert_eq!(result.kind, Some(CompilationKind::NixBuild), "`{cmd}`");
}
}
#[test]
fn test_nix_flake_check_offloadable_but_metadata_not() {
let _guard = test_guard!();
let check = classify_command("nix flake check");
assert!(check.is_compilation, "nix flake check should offload");
assert_eq!(check.kind, Some(CompilationKind::NixBuild));
for cmd in [
"nix flake metadata",
"nix flake show",
"nix flake update",
"nix flake lock",
"nix flake info",
] {
let result = classify_command(cmd);
assert!(!result.is_compilation, "`{cmd}` must NOT offload");
}
}
#[test]
fn test_nix_develop_and_shell_only_with_command() {
let _guard = test_guard!();
for cmd in [
"nix develop -c make",
"nix develop --command cargo build",
"nix develop .#dev -c bun test",
"nix shell nixpkgs#gcc -c make",
"nix shell nixpkgs#hello --command hello",
] {
let result = classify_command(cmd);
assert!(result.is_compilation, "expected offload for `{cmd}`");
assert_eq!(result.kind, Some(CompilationKind::NixBuild), "`{cmd}`");
}
for cmd in ["nix develop", "nix develop .#dev", "nix shell nixpkgs#gcc"] {
let result = classify_command(cmd);
assert!(
!result.is_compilation,
"`{cmd}` is interactive; must run local"
);
}
}
#[test]
fn test_nix_execution_and_mutation_not_offloaded() {
let _guard = test_guard!();
for cmd in [
"nix run .#app",
"nix run nixpkgs#hello",
"nix repl",
"nix eval .#foo",
"nix search nixpkgs hello",
"nix profile install nixpkgs#hello",
"nix registry list",
"nix store gc",
"nix copy --to ssh://host .#foo",
"nix path-info .#foo",
"nix --version",
"nix build --help",
"nix",
] {
let result = classify_command(cmd);
assert!(!result.is_compilation, "`{cmd}` must NOT be intercepted");
}
}
#[test]
fn test_nix_legacy_mutating_tools_have_no_keyword() {
let _guard = test_guard!();
for cmd in [
"nix-env -iA nixpkgs.hello",
"nix-shell",
"nix-shell -p gcc",
"nix-store --gc",
"nix-collect-garbage -d",
"nix-channel --update",
] {
assert!(
!starts_with_compilation_command(cmd),
"`{cmd}` should not match any compilation keyword"
);
let result = classify_command(cmd);
assert!(!result.is_compilation, "`{cmd}` must NOT be intercepted");
}
}
#[test]
fn test_nix_build_maps_to_nix_runtime_base() {
let _guard = test_guard!();
assert_eq!(CompilationKind::NixBuild.command_base(), "nix");
assert!(!CompilationKind::NixBuild.is_test_command());
}
#[test]
fn test_nix_build_compound_and_wrapped() {
let _guard = test_guard!();
let compound = classify_command("cd /repo && nix build .#foo");
assert!(compound.is_compilation, "compound nix build should offload");
assert_eq!(compound.kind, Some(CompilationKind::NixBuild));
let wrapped = classify_command("bash -c \"nix build .#foo\"");
assert!(wrapped.is_compilation, "wrapped nix build should offload");
assert_eq!(wrapped.kind, Some(CompilationKind::NixBuild));
}
#[test]
fn test_job_kind_serde_and_base_token() {
let _guard = test_guard!();
let json = serde_json::to_string(&CompilationKind::Job).unwrap();
assert_eq!(json, "\"job\"");
let round: CompilationKind = serde_json::from_str(&json).unwrap();
assert_eq!(round, CompilationKind::Job);
assert_eq!(CompilationKind::Job.command_base(), "job");
assert!(!CompilationKind::Job.is_test_command());
}
#[test]
fn test_cargo_test_with_toolchain() {
let _guard = test_guard!();
let result = classify_command("cargo +1.80.0 test");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_nextest_with_toolchain() {
let _guard = test_guard!();
let result = classify_command("cargo +nightly nextest run");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
}
#[test]
fn test_cargo_build() {
let _guard = test_guard!();
let result = classify_command("cargo build");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
assert!(result.confidence >= 0.90);
}
#[test]
fn test_cargo_build_release() {
let _guard = test_guard!();
let result = classify_command("cargo build --release");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
}
#[test]
fn test_cargo_test() {
let _guard = test_guard!();
let result = classify_command("cargo test");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_fmt_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo fmt");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_cargo_install_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo install ripgrep");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_piped_to_benign_pager_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build 2>&1 | grep error");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo build 2>&1 | grep error")
);
}
#[test]
fn test_piped_to_nonbenign_command_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build | xargs rm");
assert!(!result.is_compilation);
assert!(result.reason.contains("piped"));
}
#[test]
fn test_backgrounded_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build &");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
assert_eq!(result.extracted_command.as_deref(), Some("cargo build &"));
}
#[test]
fn test_newline_injection_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build\nrm -rf /");
assert!(!result.is_compilation);
assert!(result.reason.contains("newline"));
let result = classify_command("cargo build\r\nrm -rf /");
assert!(!result.is_compilation);
assert!(result.reason.contains("newline"));
for command in [
"cargo build > out.log\nrm -rf /",
"cargo build | tee out.log\r\nrm -rf /",
"cargo build &\nrm -rf /",
] {
let result = classify_command(command);
assert!(
!result.is_compilation,
"newline-bearing benign suffix must fail closed: {command:?}"
);
assert!(result.reason.contains("newline"), "{result:?}");
}
}
#[test]
fn test_redirected_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build > log.txt");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo build > log.txt")
);
let result = classify_command("cargo test --workspace > out.log 2>&1");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo test --workspace > out.log 2>&1")
);
for command in [
"cargo build 2>errors.txt",
"cargo build 2>>errors.txt",
"cargo build >out.log 2>&1",
] {
let result = classify_command(command);
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.extracted_command.as_deref(), Some(command));
}
}
#[test]
fn test_input_redirected_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build < input.txt");
assert!(!result.is_compilation);
assert!(result.reason.contains("input redirected"));
}
#[test]
fn test_process_substitution_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo build --config <(echo ...)");
assert!(!result.is_compilation);
assert!(result.reason.contains("subshell execution"));
}
#[test]
fn test_subshell_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("(cargo build)");
assert!(!result.is_compilation);
assert!(result.reason.contains("subshell execution"));
}
#[test]
fn test_make_maintenance_matches_whole_target_tokens() {
let _guard = test_guard!();
for cmd in [
"make clean",
"make install",
"make distclean",
"make uninstall",
] {
let r = classify_command(cmd);
assert!(!r.is_compilation, "{cmd} should not be intercepted");
assert!(r.reason.contains("make maintenance command"), "{cmd}");
}
let r = classify_command("make -j8 clean");
assert!(!r.is_compilation);
for cmd in [
"make CFLAGS=-Iinstall/include",
"make build INSTALL_DIR=/x",
"make TARGET=cleanup",
"make clean-all", "make -C install", ] {
let r = classify_command(cmd);
assert!(r.is_compilation, "{cmd} should be intercepted as a build");
assert_eq!(r.kind, Some(CompilationKind::Make), "{cmd}");
}
}
#[test]
fn test_ninja_clean_matches_tool_and_whole_target() {
let _guard = test_guard!();
for cmd in ["ninja clean", "ninja -t clean", "ninja -j8 clean"] {
let r = classify_command(cmd);
assert!(!r.is_compilation, "{cmd} should not be intercepted");
assert!(r.reason.contains("ninja clean"), "{cmd}");
}
for cmd in [
"ninja build/clean_obj.o", "ninja -C clean", "ninja -j4",
] {
let r = classify_command(cmd);
assert!(r.is_compilation, "{cmd} should be intercepted as a build");
assert_eq!(r.kind, Some(CompilationKind::Ninja), "{cmd}");
}
}
#[test]
fn test_gcc_compile() {
let _guard = test_guard!();
let result = classify_command("gcc -c main.c -o main.o");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::Gcc));
}
#[test]
fn test_make() {
let _guard = test_guard!();
let result = classify_command("make -j8");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::Make));
}
#[test]
fn test_make_clean_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("make clean");
assert!(!result.is_compilation);
assert!(result.reason.contains("make maintenance command"));
}
#[test]
fn test_cmake_build_requires_cmake_command() {
let _guard = test_guard!();
let result = classify_command("echo cmake --build .");
assert!(!result.is_compilation);
}
#[test]
fn test_cmake_build_with_equals_flag() {
let _guard = test_guard!();
let result = classify_command("cmake --build=build");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CmakeBuild));
}
#[test]
fn test_meson_compile_requires_meson_command() {
let _guard = test_guard!();
let result = classify_command("echo meson compile");
assert!(!result.is_compilation);
}
#[test]
fn test_non_compilation() {
let _guard = test_guard!();
let result = classify_command("ls -la");
assert!(!result.is_compilation);
assert!(result.reason.contains("no compilation keyword"));
}
#[test]
fn test_empty_command() {
let _guard = test_guard!();
let result = classify_command("");
assert!(!result.is_compilation);
assert!(result.reason.contains("empty command"));
}
#[test]
fn test_bun_keyword_detected() {
let _guard = test_guard!();
assert!(starts_with_compilation_command("bun test"));
assert!(starts_with_compilation_command("bun typecheck"));
assert!(starts_with_compilation_command("bun install"));
}
#[test]
fn test_bun_install_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun install");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_add_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun add lodash");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_remove_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun remove lodash");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_run_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun run build");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_build_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun build ./src/index.ts");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_version_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun --version");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
let result = classify_command("bun -v");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_dev_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun dev");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_repl_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun repl");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_link_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun link");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_init_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun init");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_create_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun create next-app");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_pm_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun pm cache");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_help_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun --help");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
let result = classify_command("bun -h");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bun_test_classification() {
let _guard = test_guard!();
let result = classify_command("bun test");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
assert!((result.confidence - 0.95).abs() < 0.001);
let result = classify_command("bun test src/");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun test --coverage");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun test auth.test.ts");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun test --bail --timeout 5000");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun test --reporter json");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
}
#[test]
fn test_bun_test_watch_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun test --watch");
assert!(!result.is_compilation);
assert!(result.reason.contains("interactive"));
let result = classify_command("bun test --watch --coverage");
assert!(!result.is_compilation);
assert!(result.reason.contains("interactive"));
let result = classify_command("bun test src/ --watch");
assert!(!result.is_compilation);
let result = classify_command("bun test -w");
assert!(!result.is_compilation);
let result = classify_command("bun test -w src/");
assert!(!result.is_compilation);
}
#[test]
fn test_bun_typecheck_watch_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun typecheck --watch");
assert!(!result.is_compilation);
assert!(result.reason.contains("interactive"));
let result = classify_command("bun typecheck -w");
assert!(!result.is_compilation);
}
#[test]
fn test_bun_typecheck_classification() {
let _guard = test_guard!();
let result = classify_command("bun typecheck");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTypecheck));
assert!((result.confidence - 0.95).abs() < 0.001);
let result = classify_command("bun typecheck src/");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::BunTypecheck));
}
#[test]
fn test_bun_edge_cases_not_matched() {
let _guard = test_guard!();
let result = classify_command("bun testing");
assert!(!result.is_compilation);
assert!(result.reason.contains("no matching pattern"));
let result = classify_command("bun typechecker");
assert!(!result.is_compilation);
assert!(result.reason.contains("no matching pattern"));
let result = classify_command("bun type");
assert!(!result.is_compilation);
assert!(result.reason.contains("no matching pattern"));
let result = classify_command("bun x eslint");
assert!(!result.is_compilation);
assert!(result.reason.contains("bun x runs arbitrary packages"));
let result = classify_command("bun x prettier --write .");
assert!(!result.is_compilation);
assert!(result.reason.contains("bun x runs arbitrary packages"));
let result = classify_command("bun x vitest run");
assert!(!result.is_compilation);
assert!(result.reason.contains("bun x runs arbitrary packages"));
}
#[test]
fn test_bun_test_vs_never_intercept() {
let _guard = test_guard!();
let result = classify_command("bun test");
assert!(!result.reason.contains("never-intercept"));
assert!(result.is_compilation);
}
#[test]
fn test_bun_typecheck_vs_never_intercept() {
let _guard = test_guard!();
let result = classify_command("bun typecheck");
assert!(!result.reason.contains("never-intercept"));
assert!(result.is_compilation);
}
#[test]
fn test_bun_compilation_kind_serde() {
let _guard = test_guard!();
let kind = CompilationKind::BunTest;
let json = serde_json::to_string(&kind).unwrap();
assert_eq!(json, "\"bun_test\"");
let parsed: CompilationKind = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, CompilationKind::BunTest);
let kind = CompilationKind::BunTypecheck;
let json = serde_json::to_string(&kind).unwrap();
assert_eq!(json, "\"bun_typecheck\"");
let parsed: CompilationKind = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, CompilationKind::BunTypecheck);
}
#[test]
fn test_bun_kinds_are_distinct() {
let _guard = test_guard!();
assert_ne!(CompilationKind::BunTest, CompilationKind::BunTypecheck);
assert_ne!(CompilationKind::BunTest, CompilationKind::CargoTest);
}
#[test]
fn test_wrapped_command_classification_repro() {
let _guard = test_guard!();
let result = classify_command("time cargo build");
assert!(
result.is_compilation,
"Should classify 'time cargo build' as compilation"
);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
let result = classify_command("sudo cargo check");
assert!(
result.is_compilation,
"Should classify 'sudo cargo check' as compilation"
);
let result = classify_command("env RUST_BACKTRACE=1 cargo test");
assert!(
result.is_compilation,
"Should classify env-wrapped cargo test as compilation"
);
let result = classify_command("env 'CARGO_TARGET_DIR=/data/tmp/rch-target' cargo build");
assert!(
result.is_compilation,
"Should classify shell-quoted env assignment before cargo build as compilation"
);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
}
#[test]
fn test_bun_piped_to_benign_pager_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun test | grep error");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::BunTest));
assert_eq!(
result.extracted_command.as_deref(),
Some("bun test | grep error")
);
let result = classify_command("bun test 2>&1 | tee output.log");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("bun test 2>&1 | tee output.log")
);
let result = classify_command("bun typecheck | head -20");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::BunTypecheck));
}
#[test]
fn test_bunx_tsc_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun x tsc --noEmit");
assert!(!result.is_compilation);
assert!(result.reason.contains("bun x"));
let result = classify_command("bun x eslint .");
assert!(!result.is_compilation);
let result = classify_command("bun x prettier --write .");
assert!(!result.is_compilation);
let result = classify_command("bun x vitest run");
assert!(!result.is_compilation);
}
#[test]
fn test_bun_redirected_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun test > results.txt");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("bun test > results.txt")
);
let result = classify_command("bun typecheck > errors.log");
assert!(result.is_compilation, "got: {:?}", result.reason);
}
#[test]
fn test_bun_backgrounded_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun test &");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.extracted_command.as_deref(), Some("bun test &"));
}
#[test]
fn test_bun_chained_commands_classified() {
let _guard = test_guard!();
let result = classify_command("bun test && echo done");
assert!(
!result.is_compilation,
"chained commands should be rejected"
);
assert!(result.reason.contains("chained"));
let result = classify_command("bun typecheck; bun test");
assert!(
!result.is_compilation,
"chained commands should be rejected"
);
assert!(result.reason.contains("chained"));
}
#[test]
fn test_bun_subshell_capture_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("bun test $(echo src/)");
assert!(!result.is_compilation);
assert!(result.reason.contains("subshell"));
}
#[test]
fn test_bun_wrapped_commands() {
let _guard = test_guard!();
let result = classify_command("time bun test");
if result.is_compilation {
assert_eq!(result.kind, Some(CompilationKind::BunTest));
}
let result = classify_command("env DEBUG=1 bun test");
if result.is_compilation {
assert_eq!(result.kind, Some(CompilationKind::BunTest));
}
}
#[test]
fn test_classify_command_detailed_matches_basic() {
let _guard = test_guard!();
let commands = [
"cargo build",
"cargo test --release",
"bun typecheck",
"gcc -c main.c -o main.o",
"cd /tmp && cargo build --release",
"ls -la",
];
for cmd in commands {
let basic = classify_command(cmd);
let detailed = classify_command_detailed(cmd);
assert_eq!(basic, detailed.classification);
}
}
#[test]
fn test_classify_command_detailed_rejects_piped() {
let _guard = test_guard!();
let detailed = classify_command_detailed("cargo build | xargs rm");
assert!(!detailed.classification.is_compilation);
let tier1 = detailed.tiers.iter().find(|t| t.tier.eq(&1)).unwrap();
assert_eq!(tier1.decision, TierDecision::Reject);
assert!(tier1.reason.contains("piped"));
let detailed = classify_command_detailed("cargo build | tee log.txt");
assert!(detailed.classification.is_compilation);
assert_eq!(
detailed.classification.extracted_command.as_deref(),
Some("cargo build | tee log.txt")
);
}
#[test]
fn test_classify_command_detailed_normalizes_wrappers() {
let _guard = test_guard!();
for (cmd, normalized) in [
("sudo cargo check", "cargo check"),
("env -u RUST_LOG cargo test", "cargo test"),
] {
let detailed = classify_command_detailed(cmd);
assert_eq!(
(
detailed.normalized.as_str(),
detailed.classification.is_compilation
),
(normalized, true),
"{cmd}"
);
}
}
#[test]
fn test_classify_command_detailed_matches_compound_command() {
let _guard = test_guard!();
let detailed = classify_command_detailed("cd /tmp && cargo build --release");
assert!(detailed.classification.is_compilation);
assert_eq!(
detailed.classification.kind,
Some(CompilationKind::CargoBuild)
);
assert_eq!(
detailed.classification.command_prefix.as_deref(),
Some("cd /tmp && ")
);
assert_eq!(
detailed.classification.extracted_command.as_deref(),
Some("cargo build --release")
);
assert_eq!(detailed.normalized, "cargo build --release");
assert_eq!(detailed.tiers.len(), 5);
assert!(
detailed
.tiers
.iter()
.all(|tier| tier.decision == TierDecision::Pass)
);
}
#[test]
fn test_cargo_test_release() {
let _guard = test_guard!();
let result = classify_command("cargo test --release");
assert!(
result.is_compilation,
"cargo test --release should be classified as compilation"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
assert!(result.confidence >= 0.90);
}
#[test]
fn test_cargo_test_specific_test_name() {
let _guard = test_guard!();
let result = classify_command("cargo test my_test_function");
assert!(
result.is_compilation,
"cargo test with specific test name should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_with_nocapture() {
let _guard = test_guard!();
let result = classify_command("cargo test -- --nocapture");
assert!(
result.is_compilation,
"cargo test -- --nocapture should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_workspace() {
let _guard = test_guard!();
let result = classify_command("cargo test --workspace");
assert!(
result.is_compilation,
"cargo test --workspace should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_package() {
let _guard = test_guard!();
let result = classify_command("cargo test -p rch-common");
assert!(result.is_compilation, "cargo test -p should be classified");
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_short_alias() {
let _guard = test_guard!();
let result = classify_command("cargo t");
assert!(
result.is_compilation,
"cargo t (short alias) should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_all_features() {
let _guard = test_guard!();
let result = classify_command("cargo test --all-features");
assert!(
result.is_compilation,
"cargo test --all-features should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_no_default_features() {
let _guard = test_guard!();
let result = classify_command("cargo test --no-default-features");
assert!(
result.is_compilation,
"cargo test --no-default-features should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_with_env_var() {
let _guard = test_guard!();
let result = classify_command("RUST_BACKTRACE=1 cargo test");
assert!(
result.is_compilation,
"cargo test with env var should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_with_multiple_flags() {
let _guard = test_guard!();
let result = classify_command("cargo test --release --workspace -p rch -- --nocapture");
assert!(
result.is_compilation,
"cargo test with multiple flags should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_with_jobs() {
let _guard = test_guard!();
let result = classify_command("cargo test -j 8");
assert!(result.is_compilation, "cargo test -j should be classified");
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_target() {
let _guard = test_guard!();
let result = classify_command("cargo test --target x86_64-unknown-linux-gnu");
assert!(
result.is_compilation,
"cargo test --target should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_lib() {
let _guard = test_guard!();
let result = classify_command("cargo test --lib");
assert!(
result.is_compilation,
"cargo test --lib should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_bins() {
let _guard = test_guard!();
let result = classify_command("cargo test --bins");
assert!(
result.is_compilation,
"cargo test --bins should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_doc() {
let _guard = test_guard!();
let result = classify_command("cargo test --doc");
assert!(
result.is_compilation,
"cargo test --doc should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_filter_pattern() {
let _guard = test_guard!();
let result = classify_command("cargo test test_classification");
assert!(
result.is_compilation,
"cargo test with filter pattern should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_test_exact() {
let _guard = test_guard!();
let result = classify_command("cargo test --exact my_test");
assert!(
result.is_compilation,
"cargo test --exact should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_nextest_keyword_detected() {
let _guard = test_guard!();
assert!(starts_with_compilation_command("cargo nextest run"));
assert!(starts_with_compilation_command("cargo nextest list"));
}
#[test]
fn test_cargo_nextest_run_classification() {
let _guard = test_guard!();
let result = classify_command("cargo nextest run");
assert!(
result.is_compilation,
"cargo nextest run should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
assert!((result.confidence - 0.95).abs() < 0.001);
}
#[test]
fn test_cargo_nextest_run_with_flags() {
let _guard = test_guard!();
let result = classify_command("cargo nextest run --release");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
let result = classify_command("cargo nextest run --cargo-profile ci");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
let result = classify_command("cargo nextest run --workspace");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
let result = classify_command("cargo nextest run -p rch-common");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
let result = classify_command("cargo nextest run test_classification");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
let result = classify_command("cargo nextest run --release --no-fail-fast -j 8");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
}
#[test]
fn test_cargo_nextest_run_short_alias() {
let _guard = test_guard!();
let result = classify_command("cargo nextest r");
assert!(
result.is_compilation,
"cargo nextest r should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
}
#[test]
fn test_cargo_nextest_list_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest list");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_cargo_nextest_archive_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest archive");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_cargo_nextest_show_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest show");
assert!(!result.is_compilation);
assert!(result.reason.contains("never-intercept"));
}
#[test]
fn test_bare_cargo_nextest_not_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest");
assert!(!result.is_compilation);
assert!(result.reason.contains("without subcommand"));
}
#[test]
fn test_cargo_nextest_wrapped_commands() {
let _guard = test_guard!();
let result = classify_command("time cargo nextest run");
assert!(
result.is_compilation,
"time cargo nextest run should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
let result = classify_command("RUST_BACKTRACE=1 cargo nextest run");
assert!(
result.is_compilation,
"env-wrapped cargo nextest run should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
}
#[test]
fn test_cargo_nextest_compilation_kind_serde() {
let _guard = test_guard!();
let kind = CompilationKind::CargoNextest;
let json = serde_json::to_string(&kind).unwrap();
assert_eq!(json, "\"cargo_nextest\"");
let parsed: CompilationKind = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, CompilationKind::CargoNextest);
}
#[test]
fn test_cargo_nextest_vs_cargo_test_distinct() {
let _guard = test_guard!();
assert_ne!(CompilationKind::CargoNextest, CompilationKind::CargoTest);
let nextest_result = classify_command("cargo nextest run");
let test_result = classify_command("cargo test");
assert!(nextest_result.is_compilation);
assert!(test_result.is_compilation);
assert_eq!(nextest_result.kind, Some(CompilationKind::CargoNextest));
assert_eq!(test_result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_nextest_piped_to_pager_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest run | grep FAIL");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoNextest));
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo nextest run | grep FAIL")
);
}
#[test]
fn test_cargo_nextest_redirected_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest run > results.txt");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo nextest run > results.txt")
);
}
#[test]
fn test_cargo_nextest_backgrounded_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo nextest run &");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo nextest run &")
);
}
#[test]
fn test_cargo_bench_classification() {
let _guard = test_guard!();
let result = classify_command("cargo bench");
assert!(result.is_compilation, "cargo bench should be classified");
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
assert!((result.confidence - 0.90).abs() < 0.001);
}
#[test]
fn test_cargo_bench_with_filter() {
let _guard = test_guard!();
let result = classify_command("cargo bench my_bench");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
}
#[test]
fn test_cargo_bench_with_flags() {
let _guard = test_guard!();
let result = classify_command("cargo bench --release");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
let result = classify_command("cargo bench -p rch-common");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
let result = classify_command("cargo bench --features benchmarks");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
let result = classify_command("cargo bench --bench criterion_bench");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
}
#[test]
fn test_cargo_bench_wrapped() {
let _guard = test_guard!();
let result = classify_command("time cargo bench");
assert!(
result.is_compilation,
"time cargo bench should be classified"
);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
let result = classify_command("CARGO_INCREMENTAL=0 cargo bench");
assert!(result.is_compilation);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
}
#[test]
fn test_cargo_bench_serde() {
let _guard = test_guard!();
let kind = CompilationKind::CargoBench;
let json = serde_json::to_string(&kind).unwrap();
assert_eq!(json, "\"cargo_bench\"");
let parsed: CompilationKind = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, CompilationKind::CargoBench);
}
#[test]
fn test_cargo_bench_distinct_from_test() {
let _guard = test_guard!();
assert_ne!(CompilationKind::CargoBench, CompilationKind::CargoTest);
let bench_result = classify_command("cargo bench");
let test_result = classify_command("cargo test");
assert!(bench_result.is_compilation);
assert!(test_result.is_compilation);
assert_eq!(bench_result.kind, Some(CompilationKind::CargoBench));
assert_eq!(test_result.kind, Some(CompilationKind::CargoTest));
}
#[test]
fn test_cargo_bench_piped_to_pager_intercepted() {
let _guard = test_guard!();
let result = classify_command("cargo bench | tee output.txt");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoBench));
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo bench | tee output.txt")
);
}
#[test]
fn test_normalize_path_and_wrapper_bug_repro() {
let _guard = test_guard!();
let normalized = normalize_command("/usr/bin/time cargo build");
assert_eq!(normalized, "cargo build");
let result = classify_command("/usr/bin/time cargo build");
assert!(result.is_compilation);
}
#[test]
fn test_command_base_rust() {
let _guard = test_guard!();
assert_eq!(CompilationKind::CargoBuild.command_base(), "cargo");
assert_eq!(CompilationKind::CargoTest.command_base(), "cargo");
assert_eq!(CompilationKind::CargoCheck.command_base(), "cargo");
assert_eq!(CompilationKind::CargoClippy.command_base(), "cargo");
assert_eq!(CompilationKind::CargoDoc.command_base(), "cargo");
assert_eq!(CompilationKind::CargoBench.command_base(), "cargo");
assert_eq!(CompilationKind::CargoNextest.command_base(), "cargo");
assert_eq!(CompilationKind::Rustc.command_base(), "rustc");
}
#[test]
fn test_command_base_c_cpp() {
let _guard = test_guard!();
assert_eq!(CompilationKind::Gcc.command_base(), "gcc");
assert_eq!(CompilationKind::Gpp.command_base(), "g++");
assert_eq!(CompilationKind::Clang.command_base(), "clang");
assert_eq!(CompilationKind::Clangpp.command_base(), "clang++");
}
#[test]
fn test_command_base_build_systems() {
let _guard = test_guard!();
assert_eq!(CompilationKind::Make.command_base(), "make");
assert_eq!(CompilationKind::CmakeBuild.command_base(), "cmake");
assert_eq!(CompilationKind::Ninja.command_base(), "ninja");
assert_eq!(CompilationKind::Meson.command_base(), "meson");
}
#[test]
fn test_command_base_bun() {
let _guard = test_guard!();
assert_eq!(CompilationKind::BunTest.command_base(), "bun");
assert_eq!(CompilationKind::BunTypecheck.command_base(), "bun");
}
mod proptest_classification {
use super::*;
use proptest::prelude::*;
fn arbitrary_string() -> impl Strategy<Value = String> {
prop::string::string_regex(".{0,500}").unwrap()
}
fn command_like_string() -> impl Strategy<Value = String> {
let prefixes = prop::sample::select(vec![
"cargo", "rustc", "gcc", "g++", "clang", "make", "cmake", "ninja", "bun", "ls",
"cd", "echo", "cat", "grep", "find", "rm", "mv", "cp", "mkdir",
]);
(prefixes, "[ a-zA-Z0-9_.-]{0,200}")
.prop_map(|(prefix, suffix)| format!("{}{}", prefix, suffix))
}
fn known_command_with_flags() -> impl Strategy<Value = String> {
let base_commands = prop::sample::select(vec![
"cargo build",
"cargo test",
"cargo check",
"cargo clippy",
"cargo run",
"rustc",
"gcc",
"g++",
"make",
"bun test",
"bun typecheck",
]);
let flags = prop::collection::vec(
prop::sample::select(vec![
"--release",
"--verbose",
"-j8",
"-p",
"--all",
"--workspace",
"--lib",
"--bin",
"-o",
"-c",
]),
0..5,
);
(base_commands, flags).prop_map(|(cmd, flags)| {
if flags.is_empty() {
cmd.to_string()
} else {
format!("{} {}", cmd, flags.join(" "))
}
})
}
fn unicode_and_control_chars() -> impl Strategy<Value = String> {
prop::string::string_regex(
r"[\x00-\x1f\x80-\xff\u{100}-\u{10FFFF}a-zA-Z0-9 |>&;$()]{0,100}",
)
.unwrap()
}
fn shell_special_commands() -> impl Strategy<Value = String> {
let components = prop::sample::select(vec![
"cargo build",
"ls -la",
"echo test",
"cat file.txt",
"grep pattern",
]);
let operators = prop::sample::select(vec![
" | ", " && ", " || ", " ; ", " > ", " < ", " & ", " 2>&1 ", " $(", " `",
]);
prop::collection::vec((components, operators), 1..4).prop_map(|pairs| {
let mut result = String::new();
for (i, (comp, op)) in pairs.iter().enumerate() {
if i > 0 {
result.push_str(op);
}
result.push_str(comp);
}
result
})
}
fn wrapped_commands() -> impl Strategy<Value = String> {
let wrappers = prop::sample::select(vec![
"sudo ",
"env ",
"time ",
"nice ",
"/usr/bin/time ",
"RUST_BACKTRACE=1 ",
"CC=clang ",
"",
]);
let commands = prop::sample::select(vec![
"cargo build",
"cargo test",
"make",
"gcc -c main.c",
"ls -la",
]);
(wrappers, commands).prop_map(|(wrapper, cmd)| format!("{}{}", wrapper, cmd))
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(1000))]
#[test]
fn test_classify_arbitrary_no_panic(s in arbitrary_string()) {
let _ = classify_command(&s);
}
#[test]
fn test_classify_command_like_no_panic(s in command_like_string()) {
let _ = classify_command(&s);
}
#[test]
fn test_classify_known_commands_valid(s in known_command_with_flags()) {
let result = classify_command(&s);
prop_assert!(result.confidence >= 0.0 && result.confidence <= 1.0,
"Confidence {} out of range for command: {}", result.confidence, s);
if result.is_compilation {
prop_assert!(result.kind.is_some(),
"is_compilation=true but kind=None for: {}", s);
}
}
#[test]
fn test_classify_unicode_no_panic(s in unicode_and_control_chars()) {
let _ = classify_command(&s);
}
#[test]
fn test_classify_shell_special(s in shell_special_commands()) {
let result = classify_command(&s);
if s.contains(" | ") || s.contains(" > ") || s.contains(" < ") || s.contains(" & ") {
let _ = result; }
}
#[test]
fn test_classify_wrapped_commands(s in wrapped_commands()) {
let result = classify_command(&s);
prop_assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
}
#[test]
fn test_classify_deterministic(s in arbitrary_string()) {
let result1 = classify_command(&s);
let result2 = classify_command(&s);
prop_assert_eq!(result1, result2,
"Non-deterministic classification for: {}", s);
}
#[test]
fn test_classify_whitespace_variants(s in "[ \t\n\r]{0,20}") {
let _guard = test_guard!();
let result = classify_command(&s);
prop_assert!(!result.is_compilation,
"Whitespace-only command should not be classified as compilation: {:?}", s);
prop_assert!(result.reason.contains("empty") || result.reason.contains("keyword"),
"Unexpected reason for whitespace: {}", result.reason);
}
#[test]
fn test_classify_long_commands(
prefix in "cargo (build|test|check)",
suffix in "[a-zA-Z0-9_ -]{0,10000}"
) {
let long_cmd = format!("{} {}", prefix, suffix);
let result = classify_command(&long_cmd);
prop_assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
}
#[test]
fn test_classify_special_bytes(s in prop::collection::vec(any::<u8>(), 0..200)) {
if let Ok(valid_str) = String::from_utf8(s.clone()) {
let _ = classify_command(&valid_str);
}
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(500))]
#[test]
fn test_cargo_subcommand_robustness(
subcommand in "(build|test|check|clippy|fmt|clean|run|doc|bench|nextest)",
suffix in "[a-zA-Z0-9_ -]{0,50}"
) {
let cmd = format!("cargo {} {}", subcommand, suffix);
let result = classify_command(&cmd);
prop_assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
if subcommand.eq("fmt") || subcommand.eq("clean") {
prop_assert!(!result.is_compilation,
"cargo {} should not be compilation: {}", subcommand, cmd);
}
}
#[test]
fn test_bun_command_robustness(
subcommand in "(test|typecheck|install|add|remove|run|build|dev)",
suffix in "[a-zA-Z0-9_ -]{0,30}"
) {
let cmd = format!("bun {} {}", subcommand, suffix);
let result = classify_command(&cmd);
prop_assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
if matches!(subcommand.as_str(), "install" | "add" | "remove" | "run" | "build" | "dev") {
prop_assert!(!result.is_compilation,
"bun {} should not be compilation", subcommand);
}
}
#[test]
fn test_c_compiler_robustness(
compiler in "(gcc|g\\+\\+|clang|clang\\+\\+)",
flags in "(-[cCoOgW][a-z0-9]* )*",
file in "[a-zA-Z_][a-zA-Z0-9_]*\\.(c|cpp|cc|h|hpp)"
) {
let cmd = format!("{} {}{}", compiler, flags, file);
let result = classify_command(&cmd);
prop_assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
}
#[test]
fn test_quoted_args_handling(
base in "(cargo build|cargo test|gcc|make)",
quoted_content in "[a-zA-Z0-9 _-]{0,30}"
) {
let cmd_single = format!("{} '{}'", base, quoted_content);
let _ = classify_command(&cmd_single);
let cmd_double = format!("{} \"{}\"", base, quoted_content);
let _ = classify_command(&cmd_double);
}
}
#[test]
fn test_known_edge_cases() {
let _guard = test_guard!();
let edge_cases = [
"",
" ",
"\t",
"\n",
"cargo",
"cargo ",
"cargo ",
"cargo\tbuild",
"cargo\nbuild",
"cargo\rbuild",
"cargo+nightly",
"cargo +nightly",
"cargo +nightly build",
" cargo build ",
"CARGO_TARGET_DIR=/tmp cargo build",
"/usr/local/bin/cargo build",
"~/.cargo/bin/cargo build",
"./cargo build",
"../cargo build",
"cargo build 'test file.rs'",
"cargo build \"test file.rs\"",
"cargo build test\\ file.rs",
"cargo build -- --test-threads=1",
"cargo build --features \"feat1 feat2\"",
"cargo build 2>&1",
"cargo build 2>/dev/null",
"gcc -DFOO=\"bar baz\" main.c",
"make -j$(nproc)",
"ninja -C build",
"cmake --build . --target all",
"bun test --timeout 5000 src/",
"env -i PATH=/usr/bin cargo build",
];
for cmd in edge_cases {
let result = classify_command(cmd);
assert!(
result.confidence >= 0.0 && result.confidence <= 1.0,
"Invalid confidence for: {:?}",
cmd
);
}
}
#[test]
fn test_detailed_matches_simple_proptest() {
let _guard = test_guard!();
let test_cases = [
"cargo build --release",
"cargo test",
"make -j8",
"ls -la",
"echo hello",
];
for cmd in test_cases {
let simple = classify_command(cmd);
let detailed = classify_command_detailed(cmd);
assert_eq!(simple, detailed.classification, "Mismatch for: {}", cmd);
}
}
#[test]
fn test_split_single_command() {
let _guard = test_guard!();
assert_eq!(split_shell_commands("cargo build"), vec!["cargo build"]);
}
#[test]
fn test_split_and_operator() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("cargo fmt && cargo build"),
vec!["cargo fmt", "cargo build"]
);
}
#[test]
fn test_split_quoted_operator() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("echo '&&' && cargo build"),
vec!["echo '&&'", "cargo build"]
);
}
#[test]
fn test_split_mixed_operators() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("cd /tmp && make -j4 || echo fail"),
vec!["cd /tmp", "make -j4", "echo fail"]
);
}
#[test]
fn test_split_semicolons() {
let _guard = test_guard!();
assert_eq!(split_shell_commands("a ; b ; c"), vec!["a", "b", "c"]);
}
#[test]
fn test_split_quoted_semicolon() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("echo 'hello;world' && make"),
vec!["echo 'hello;world'", "make"]
);
}
#[test]
fn test_split_pipe_preserved() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("cargo build 2>&1 | tee log"),
vec!["cargo build 2>&1 | tee log"]
);
}
#[test]
fn test_split_double_quoted_operator() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands(r#"echo "&&" && cargo build"#),
vec![r#"echo "&&""#, "cargo build"]
);
}
#[test]
fn test_split_nested_quotes() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("echo \"he said 'hello && bye'\" && cargo test"),
vec!["echo \"he said 'hello && bye'\"", "cargo test"]
);
}
#[test]
fn test_split_escaped_quote() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands(r"echo it\'s && cargo build"),
vec![r"echo it\'s", "cargo build"]
);
}
#[test]
fn test_split_backslash_literal_in_single_quotes() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands(r"cargo build 'a\' && rm -rf x"),
vec![r"cargo build 'a\'", "rm -rf x"]
);
}
#[test]
fn test_split_empty_string() {
let _guard = test_guard!();
assert!(split_shell_commands("").is_empty());
}
#[test]
fn test_split_only_whitespace() {
let _guard = test_guard!();
assert!(split_shell_commands(" ").is_empty());
}
#[test]
fn test_split_backtick_quoting() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("echo `echo && fail` && cargo build"),
vec!["echo `echo && fail`", "cargo build"]
);
}
#[test]
fn test_split_trailing_operator() {
let _guard = test_guard!();
assert_eq!(split_shell_commands("cargo build &&"), vec!["cargo build"]);
}
#[test]
fn test_split_unclosed_single_quote() {
let _guard = test_guard!();
let result = split_shell_commands("echo 'hello && cargo build");
assert_eq!(result, vec!["echo 'hello && cargo build"]);
}
#[test]
fn test_split_unclosed_double_quote() {
let _guard = test_guard!();
let result = split_shell_commands("echo \"hello && cargo build");
assert_eq!(result, vec!["echo \"hello && cargo build"]);
}
#[test]
fn test_split_unclosed_backtick() {
let _guard = test_guard!();
let result = split_shell_commands("echo `hello && cargo build");
assert_eq!(result, vec!["echo `hello && cargo build"]);
}
#[test]
fn test_split_embedded_nulls() {
let _guard = test_guard!();
let input = "cargo build\0 && echo done";
let result = split_shell_commands(input);
assert_eq!(result.len(), 2);
}
#[test]
fn test_split_unicode_input() {
let _guard = test_guard!();
let result = split_shell_commands("echo 'こんにちは' && cargo build");
assert_eq!(result, vec!["echo 'こんにちは'", "cargo build"]);
}
#[test]
fn test_split_extremely_long_input() {
let _guard = test_guard!();
let long_cmd = format!("echo {} && cargo build", "x".repeat(20_000));
let result = split_shell_commands(&long_cmd);
assert_eq!(result.len(), 2);
}
#[test]
fn test_classify_long_input_skips_splitting() {
let _guard = test_guard!();
let long_cmd = format!("cargo build && echo {}", "x".repeat(11_000));
let result = classify_command(&long_cmd);
assert!(!result.is_compilation);
assert!(
result.reason.to_string().contains("chained"),
"long input should be rejected by check_structure, not split"
);
}
#[test]
fn test_split_only_operators() {
let _guard = test_guard!();
let result = split_shell_commands("&& || ;");
assert!(
result.is_empty(),
"only operators should yield empty result"
);
}
#[test]
fn test_split_consecutive_operators() {
let _guard = test_guard!();
let result = split_shell_commands("cargo build && && echo done");
assert_eq!(result, vec!["cargo build", "echo done"]);
}
#[test]
fn test_classify_empty_after_split() {
let _guard = test_guard!();
let result = classify_command("echo hello && ls -la || pwd");
assert!(!result.is_compilation);
}
#[test]
fn test_split_three_segment_chain() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("cd /proj && cmake .. && make"),
vec!["cd /proj", "cmake ..", "make"]
);
}
#[test]
fn test_split_single_with_flags() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("cargo build --release"),
vec!["cargo build --release"]
);
}
#[test]
fn test_split_nested_quotes_semicolon() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("echo \"it's && done\" && make"),
vec!["echo \"it's && done\"", "make"]
);
}
#[test]
fn test_split_pipe_then_and() {
let _guard = test_guard!();
assert_eq!(
split_shell_commands("make 2>&1 | grep error && echo done"),
vec!["make 2>&1 | grep error", "echo done"]
);
}
#[test]
fn test_split_leading_operator() {
let _guard = test_guard!();
assert_eq!(split_shell_commands("&& cargo build"), vec!["cargo build"]);
}
#[test]
fn test_classify_cargo_fmt_and_build() {
let _guard = test_guard!();
let result = classify_command("cargo fmt && cargo build");
assert!(
result.is_compilation,
"compound command with compilation suffix should be accepted"
);
assert!(result.reason.contains("compound"));
}
#[test]
fn test_classify_cd_and_make() {
let _guard = test_guard!();
let result = classify_command("cd /project && make -j8");
assert!(
result.is_compilation,
"compound command with compilation suffix should be accepted"
);
assert!(result.reason.contains("compound"));
}
#[test]
fn test_classify_export_and_cargo_build() {
let _guard = test_guard!();
let result =
classify_command("export RUSTFLAGS='-C opt-level=3' && cargo build --release");
assert!(
result.is_compilation,
"compound command with compilation suffix should be accepted"
);
assert!(result.reason.contains("compound"));
}
#[test]
fn test_classify_mkdir_cmake_chain() {
let _guard = test_guard!();
let result =
classify_command("mkdir -p build && cmake -B build && cmake --build build");
assert!(
result.is_compilation,
"compound command with compilation suffix should be accepted"
);
assert!(result.reason.contains("compound"));
}
#[test]
fn test_classify_echo_and_cargo_test() {
let _guard = test_guard!();
let result = classify_command("echo 'Starting...' && cargo test");
assert!(
result.is_compilation,
"compound command with compilation suffix should be accepted"
);
assert!(result.reason.contains("compound"));
}
#[test]
fn test_classify_semicolon_chain_with_compilation() {
let _guard = test_guard!();
let result = classify_command("cargo fmt; cargo build; cargo test");
assert!(
!result.is_compilation,
"semicolon chained commands should be rejected"
);
assert!(result.reason.contains("chained"));
}
#[test]
fn test_classify_echo_chain_non_compilation() {
let _guard = test_guard!();
let result = classify_command("echo hello && echo world");
assert!(!result.is_compilation);
}
#[test]
fn test_classify_ls_cat_non_compilation() {
let _guard = test_guard!();
let result = classify_command("ls -la && cat file.txt");
assert!(!result.is_compilation);
}
#[test]
fn test_classify_git_chain_non_compilation() {
let _guard = test_guard!();
let result = classify_command("git status && git log");
assert!(!result.is_compilation);
}
#[test]
fn test_classify_multi_command_performance() {
let _guard = test_guard!();
let start = std::time::Instant::now();
for _ in 0..100 {
let _ = classify_command("cargo fmt && cargo build && cargo test");
}
let elapsed = start.elapsed();
let per_call_us = elapsed.as_micros() / 100;
assert!(
per_call_us < 5_000,
"classify_command took {}us per call, should be <5000us",
per_call_us
);
eprintln!(
"[perf] classify_command multi-command: {}us avg per call",
per_call_us
);
}
#[test]
fn test_classify_pipe_to_pager_preserved() {
let _guard = test_guard!();
let result = classify_command("cargo build 2>&1 | tee log");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo build 2>&1 | tee log")
);
}
#[test]
fn test_classify_pipe_segment_and_operator() {
let _guard = test_guard!();
let result = classify_command("make 2>&1 | grep error && cargo build");
assert!(
!result.is_compilation,
"chained commands should be rejected"
);
assert!(result.reason.contains("piped") || result.reason.contains("chained"));
}
}
#[allow(clippy::ptr_arg)]
fn assert_cow_borrowed(cow: &Cow<'static, str>, context: &str) {
assert!(
matches!(cow, Cow::Borrowed(_)),
"{context}: expected Cow::Borrowed, got Cow::Owned({:?})",
cow,
);
}
#[test]
fn test_cow_reject_empty_command_correctness() {
let _guard = test_guard!();
let result = classify_command("");
assert!(!result.is_compilation);
assert_eq!(result.confidence, 0.0);
assert_eq!(result.kind, None);
assert!(result.reason.contains("empty"), "reason: {}", result.reason);
}
#[test]
fn test_cow_reject_non_compilation_commands() {
let _guard = test_guard!();
let non_compilation = [
"ls -la",
"cat file.txt",
"echo hello",
"git status",
"pwd",
"cd /tmp",
];
for cmd in non_compilation {
let result = classify_command(cmd);
assert!(
!result.is_compilation,
"'{cmd}' should NOT be classified as compilation"
);
assert_eq!(result.confidence, 0.0, "'{cmd}' should have 0.0 confidence");
assert_eq!(result.kind, None, "'{cmd}' should have no CompilationKind");
assert!(!result.reason.is_empty(), "'{cmd}' should have a reason");
}
}
#[test]
fn test_cow_accept_compilation_commands() {
let _guard = test_guard!();
let cases: &[(&str, CompilationKind)] = &[
("cargo build", CompilationKind::CargoBuild),
("cargo test", CompilationKind::CargoTest),
("cargo check", CompilationKind::CargoCheck),
("cargo clippy", CompilationKind::CargoClippy),
("gcc -o hello hello.c", CompilationKind::Gcc),
("make", CompilationKind::Make),
("bun test", CompilationKind::BunTest),
];
for &(cmd, expected_kind) in cases {
let result = classify_command(cmd);
assert!(
result.is_compilation,
"'{cmd}' should be classified as compilation"
);
assert_eq!(
result.kind,
Some(expected_kind),
"'{cmd}' should have kind {expected_kind:?}"
);
assert!(
result.confidence > 0.0,
"'{cmd}' should have positive confidence"
);
assert!(!result.reason.is_empty(), "'{cmd}' should have a reason");
}
}
#[test]
fn test_cow_borrowed_on_tier0_reject() {
let _guard = test_guard!();
let result = classify_command("");
assert_cow_borrowed(&result.reason, "Tier 0 empty command reason");
}
#[test]
fn test_cow_borrowed_on_tier1_reject() {
let _guard = test_guard!();
let result = classify_command("cargo build | xargs rm");
assert!(!result.is_compilation);
assert_cow_borrowed(&result.reason, "Tier 1 piped command reason");
let result = classify_command("cargo build < input.txt");
assert!(!result.is_compilation);
assert_cow_borrowed(&result.reason, "Tier 1 input redirect reason");
let result = classify_command("cargo build $(echo args)");
assert!(!result.is_compilation);
assert_cow_borrowed(&result.reason, "Tier 1 subshell reason");
}
#[test]
fn test_cow_borrowed_on_tier2_reject() {
let _guard = test_guard!();
let non_keyword_commands = [
"ls -la",
"cat file.txt",
"echo hello world",
"git status",
"pwd",
"cd /tmp",
"grep pattern file",
"find . -name '*.rs'",
"cp src dst",
"rm file.txt",
];
for cmd in non_keyword_commands {
let result = classify_command(cmd);
assert!(!result.is_compilation, "'{cmd}' should be rejected");
assert_cow_borrowed(&result.reason, &format!("Tier 2 reject for '{cmd}'"));
}
}
#[test]
fn test_cow_borrowed_on_tier3_reject() {
let _guard = test_guard!();
let never_intercept = ["cargo fmt", "cargo install ripgrep", "cargo clean"];
for cmd in never_intercept {
let result = classify_command(cmd);
assert!(!result.is_compilation, "'{cmd}' should be rejected");
assert!(
result.reason.contains("never-intercept"),
"'{cmd}' reason should mention never-intercept: {}",
result.reason
);
assert_cow_borrowed(&result.reason, &format!("Tier 3 reject for '{cmd}'"));
}
}
#[test]
fn test_detailed_tiers_use_borrowed_names() {
let _guard = test_guard!();
let details = classify_command_detailed("ls -la");
for tier in &details.tiers {
assert_cow_borrowed(&tier.name, &format!("Tier {} name", tier.tier));
}
}
#[test]
fn test_detailed_tier_reasons_borrowed_on_reject() {
let _guard = test_guard!();
let details = classify_command_detailed("ls -la");
assert!(!details.classification.is_compilation);
for tier in &details.tiers {
assert_cow_borrowed(
&tier.reason,
&format!("Tier {} reason for 'ls -la'", tier.tier),
);
}
}
#[test]
fn test_detailed_compilation_tier_names_borrowed() {
let _guard = test_guard!();
let details = classify_command_detailed("cargo build");
assert!(details.classification.is_compilation);
for tier in &details.tiers {
assert_cow_borrowed(
&tier.name,
&format!("Tier {} name for 'cargo build'", tier.tier),
);
}
}
#[test]
fn test_classification_serde_roundtrip_borrowed() {
let _guard = test_guard!();
let original = Classification::not_compilation("no compilation keyword");
assert_cow_borrowed(&original.reason, "before serialize");
let json = serde_json::to_string(&original).expect("serialize");
let deserialized: Classification = serde_json::from_str(&json).expect("deserialize");
assert_eq!(deserialized.is_compilation, original.is_compilation);
assert_eq!(deserialized.confidence, original.confidence);
assert_eq!(deserialized.kind, original.kind);
assert_eq!(deserialized.reason, original.reason);
}
#[test]
fn test_classification_serde_roundtrip_compilation() {
let _guard = test_guard!();
let original =
Classification::compilation(CompilationKind::CargoBuild, 0.95, "cargo build detected");
let json = serde_json::to_string(&original).expect("serialize");
let deserialized: Classification = serde_json::from_str(&json).expect("deserialize");
assert_eq!(deserialized.is_compilation, original.is_compilation);
assert_eq!(deserialized.confidence, original.confidence);
assert_eq!(deserialized.kind, original.kind);
assert_eq!(deserialized.reason.as_ref(), original.reason.as_ref());
}
#[test]
fn test_classification_tier_serde_roundtrip() {
let _guard = test_guard!();
let tier = ClassificationTier {
tier: 2,
name: Cow::Borrowed(TIER_KEYWORD_FILTER),
decision: TierDecision::Reject,
reason: Cow::Borrowed("no compilation keyword"),
};
assert_cow_borrowed(&tier.name, "tier name before serialize");
assert_cow_borrowed(&tier.reason, "tier reason before serialize");
let json = serde_json::to_string(&tier).expect("serialize");
let deserialized: ClassificationTier = serde_json::from_str(&json).expect("deserialize");
assert_eq!(deserialized.tier, tier.tier);
assert_eq!(deserialized.name.as_ref(), tier.name.as_ref());
assert_eq!(deserialized.decision, tier.decision);
assert_eq!(deserialized.reason.as_ref(), tier.reason.as_ref());
}
#[test]
fn test_classification_details_serde_roundtrip() {
let _guard = test_guard!();
let details = classify_command_detailed("cargo build");
assert!(details.classification.is_compilation);
let json = serde_json::to_string(&details).expect("serialize");
let deserialized: ClassificationDetails = serde_json::from_str(&json).expect("deserialize");
assert_eq!(deserialized.original, details.original);
assert_eq!(deserialized.normalized, details.normalized);
assert_eq!(deserialized.tiers.len(), details.tiers.len());
assert_eq!(
deserialized.classification.is_compilation,
details.classification.is_compilation
);
assert_eq!(
deserialized.classification.kind,
details.classification.kind
);
}
#[test]
fn test_cow_reason_display() {
let _guard = test_guard!();
let result = classify_command("ls");
let displayed = format!("{}", result.reason);
assert!(!displayed.is_empty());
let debugged = format!("{:?}", result.reason);
assert!(!debugged.is_empty());
}
#[test]
fn test_cow_reason_comparison() {
let _guard = test_guard!();
let borrowed: Cow<'static, str> = Cow::Borrowed("no compilation keyword");
let owned: Cow<'static, str> = Cow::Owned("no compilation keyword".to_string());
assert_eq!(borrowed, owned);
let r1 = classify_command("ls");
let r2 = classify_command("pwd");
assert_eq!(r1.reason, r2.reason);
}
}
#[cfg(test)]
mod tests_bun_whitespace {
use super::*;
use crate::test_guard;
#[test]
fn test_bun_whitespace_resilience() {
let _guard = test_guard!();
let result = classify_command("bun test");
assert!(result.is_compilation, "bun test failed");
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun test");
assert!(result.is_compilation, "bun test failed");
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun\ttest");
assert!(result.is_compilation, "bun\\ttest failed");
assert_eq!(result.kind, Some(CompilationKind::BunTest));
let result = classify_command("bun typecheck src/");
assert!(result.is_compilation, "bun typecheck with spaces failed");
assert_eq!(result.kind, Some(CompilationKind::BunTypecheck));
}
#[test]
fn test_bun_watch_with_whitespace() {
let _guard = test_guard!();
let result = classify_command("bun test --watch");
assert!(
!result.is_compilation,
"bun test --watch should be rejected"
);
assert!(result.reason.contains("interactive"));
let result = classify_command("bun\ttypecheck\t-w");
assert!(
!result.is_compilation,
"bun typecheck -w should be rejected"
);
assert!(result.reason.contains("interactive"));
}
#[test]
fn test_bun_x_whitespace() {
let _guard = test_guard!();
let result = classify_command("bun x vitest");
assert!(!result.is_compilation, "bun x should be rejected");
assert!(result.reason.contains("bun x"));
}
}
#[cfg(test)]
mod tests_normalize_whitespace {
use super::*;
use crate::test_guard;
#[test]
fn test_wrapper_whitespace_resilience() {
let _guard = test_guard!();
assert_eq!(normalize_command("sudo cargo"), "cargo");
assert_eq!(normalize_command("sudo\tcargo"), "cargo");
assert_eq!(normalize_command("time\tsudo cargo"), "cargo");
assert_eq!(normalize_command("sudocargo"), "sudocargo");
assert_eq!(normalize_command("sudo"), "");
}
#[test]
fn wrapper_value_flags_consume_their_value_token() {
let _guard = test_guard!();
assert_eq!(normalize_command("nice -n 10 cargo build"), "cargo build");
assert_eq!(
normalize_command("taskset -c 0-3 cargo build"),
"cargo build"
);
assert_eq!(normalize_command("ionice -c 2 cargo test"), "cargo test");
assert_eq!(
normalize_command("ionice -c 2 -n 4 cargo build"),
"cargo build"
);
assert_eq!(normalize_command("chrt -r 10 cargo build"), "cargo build");
assert_eq!(normalize_command("timeout 60 cargo build"), "cargo build");
assert_eq!(
normalize_command("timeout -s KILL 90 cargo build"),
"cargo build"
);
assert_eq!(
normalize_command("sudo nice -n 5 cargo build"),
"cargo build"
);
assert_eq!(normalize_command("nice -n10 cargo build"), "cargo build");
assert_eq!(normalize_command("ionice -c2 cargo build"), "cargo build");
assert_eq!(
normalize_command("env -u RUSTFLAGS cargo build"),
"cargo build"
);
}
#[test]
fn positional_wrapper_does_not_eat_a_non_numeric_command() {
let _guard = test_guard!();
assert_eq!(normalize_command("timeout cargo build"), "cargo build");
}
#[test]
fn wrapped_cargo_build_is_still_classified_as_compilation() {
let _guard = test_guard!();
for cmd in [
"nice -n 10 cargo build",
"taskset -c 0-3 cargo build",
"ionice -c 2 cargo build",
"chrt -r 10 cargo build",
"timeout 60 cargo build",
"timeout -s KILL 90 cargo test",
"time '-f%U' cargo build",
"time \"-f%U\" cargo build",
"nice '--adjustment=+5' cargo build",
"nice '-n' '5' cargo build",
"env '--unset' 'RUSTFLAGS' cargo build",
] {
assert!(
classify_command(cmd).is_compilation,
"wrapper must not defeat offload: {cmd}"
);
}
assert!(!classify_command("nice -n 10 ls -la").is_compilation);
assert!(!classify_command("timeout 60 echo hi").is_compilation);
assert!(!classify_command("time '-f%U' echo cargo build").is_compilation);
assert!(!classify_command("time '-f%U cargo build").is_compilation);
assert!(!classify_command("time '-f%U'echo cargo build").is_compilation);
}
#[test]
fn test_env_var_whitespace() {
let _guard = test_guard!();
for (cmd, normalized) in [
("env RUST_BACKTRACE=1 cargo", "cargo"),
("env\tRUST_BACKTRACE=1\tcargo", "cargo"),
("env -u RUST_LOG cargo test", "cargo test"),
("env --unset RUST_LOG cargo test", "cargo test"),
("env --unset=RUST_LOG cargo test", "cargo test"),
] {
assert_eq!(normalize_command(cmd), normalized, "{cmd}");
}
}
}
#[cfg(test)]
mod regression_classification {
use super::*;
use crate::test_guard;
fn assert_golden_json(
actual: serde_json::Value,
expected: &serde_json::Value,
fixture_path: &str,
) {
if &actual == expected {
return;
}
let actual_json = serde_json::to_string_pretty(&actual).expect("actual JSON renders");
let expected_json = serde_json::to_string_pretty(expected).expect("expected JSON renders");
panic!(
"golden mismatch in {fixture_path}\n\
expected_blake3={}\n\
actual_blake3={}\n\
bless path: review the semantic diff, update the fixture expected_* field, \
and record both hashes in the Beads closeout.\n\
expected:\n{expected_json}\n\
actual:\n{actual_json}",
blake3::hash(expected_json.as_bytes()),
blake3::hash(actual_json.as_bytes())
);
}
struct Case {
cmd: &'static str,
expect_compilation: bool,
expected_kind: Option<CompilationKind>,
reason_contains: &'static str,
min_confidence: f64,
}
fn run_cases(cases: &[Case]) {
for (i, c) in cases.iter().enumerate() {
let result = classify_command(c.cmd);
assert_eq!(
result.is_compilation, c.expect_compilation,
"Case {i} ({:?}): expected is_compilation={}, got={}. reason={:?}",
c.cmd, c.expect_compilation, result.is_compilation, result.reason
);
if c.expect_compilation {
assert_eq!(
result.kind, c.expected_kind,
"Case {i} ({:?}): expected kind={:?}, got={:?}",
c.cmd, c.expected_kind, result.kind
);
assert!(
result.confidence >= c.min_confidence,
"Case {i} ({:?}): expected confidence >= {}, got={}",
c.cmd,
c.min_confidence,
result.confidence
);
}
if !c.reason_contains.is_empty() {
assert!(
result.reason.contains(c.reason_contains),
"Case {i} ({:?}): expected reason containing {:?}, got {:?}",
c.cmd,
c.reason_contains,
result.reason
);
}
}
}
#[test]
fn regression_cargo_compilation_positive() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "cargo build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo build --release",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo build --workspace",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo build -p my-crate",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo b",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test --release",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test my_test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test -- --nocapture",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test --workspace",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test -p rch-common",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo t",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test --lib",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test --bins",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test --doc",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo test -- --test-threads=4",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo check",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoCheck),
reason_contains: "cargo check",
min_confidence: 0.85,
},
Case {
cmd: "cargo check --workspace --all-targets",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoCheck),
reason_contains: "cargo check",
min_confidence: 0.85,
},
Case {
cmd: "cargo c",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoCheck),
reason_contains: "cargo check",
min_confidence: 0.85,
},
Case {
cmd: "cargo clippy",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoClippy),
reason_contains: "cargo clippy",
min_confidence: 0.85,
},
Case {
cmd: "cargo clippy --workspace --all-targets -- -D warnings",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoClippy),
reason_contains: "cargo clippy",
min_confidence: 0.85,
},
Case {
cmd: "cargo doc",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoDoc),
reason_contains: "cargo doc",
min_confidence: 0.80,
},
Case {
cmd: "cargo doc --no-deps",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoDoc),
reason_contains: "cargo doc",
min_confidence: 0.80,
},
Case {
cmd: "cargo run",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo run",
min_confidence: 0.80,
},
Case {
cmd: "cargo run --release",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo run",
min_confidence: 0.80,
},
Case {
cmd: "cargo r",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo run",
min_confidence: 0.80,
},
Case {
cmd: "cargo bench",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBench),
reason_contains: "cargo bench",
min_confidence: 0.85,
},
Case {
cmd: "cargo bench --bench my_bench",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBench),
reason_contains: "cargo bench",
min_confidence: 0.85,
},
Case {
cmd: "cargo nextest run",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoNextest),
reason_contains: "cargo nextest run",
min_confidence: 0.90,
},
Case {
cmd: "cargo nextest run --workspace",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoNextest),
reason_contains: "cargo nextest run",
min_confidence: 0.90,
},
Case {
cmd: "cargo nextest r",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoNextest),
reason_contains: "cargo nextest run",
min_confidence: 0.90,
},
Case {
cmd: "cargo +nightly build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo +1.80.0 test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo +nightly nextest run",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoNextest),
reason_contains: "cargo nextest run",
min_confidence: 0.90,
},
Case {
cmd: "cargo --offline build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo -q test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "cargo --locked --frozen check",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoCheck),
reason_contains: "cargo check",
min_confidence: 0.85,
},
Case {
cmd: "cargo --color never build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo +nightly --offline -Z unstable-options build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "cargo '--config=build.jobs=2' -vvv clippy",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoClippy),
reason_contains: "cargo clippy",
min_confidence: 0.85,
},
Case {
cmd: "cargo-zigbuild '--color=never' zigbuild",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoZigbuild),
reason_contains: "cargo-zigbuild build",
min_confidence: 0.90,
},
Case {
cmd: "cargo 'fetch'",
expect_compilation: false,
expected_kind: None,
reason_contains: "",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_cargo_package_verification_is_compilation() {
let _guard = test_guard!();
for command in [
"cargo package",
"cargo package --workspace --locked -j2",
"cargo package -p asupersync --features tls --target x86_64-unknown-linux-gnu",
"cargo +nightly --offline package --allow-dirty --no-metadata",
"cargo --color never package --target-dir=/data/tmp/package-check",
"cargo publish --dry-run",
"cargo publish -n --workspace --locked --keep-going -j 2",
"cargo publish --registry crates-io -p asupersync --dry-run",
"cargo +nightly --locked publish --dry-run --features=tls",
"env CARGO_INCREMENTAL=0 cargo publish --dry-run --workspace",
] {
let classification = classify_command(command);
assert!(
classification.is_compilation,
"{command}: {classification:?}"
);
assert_eq!(classification.kind, Some(CompilationKind::CargoBuild));
assert_eq!(
classify_command_detailed(command).classification,
classification,
"diagnosis must agree with execution for {command}"
);
}
}
#[test]
fn regression_cargo_package_verification_declines_publication_and_metadata() {
let _guard = test_guard!();
for command in [
"cargo publish",
"cargo publish --workspace --locked",
"cargo +nightly --offline publish",
"cargo publish --no-verify",
"cargo publish --dry-run --no-verify",
"cargo publish --no-verify -n",
"cargo publish -qn --no-verify",
"cargo package --no-verify",
"cargo package --list",
"cargo package -l",
"cargo package -vl",
"cargo package --help",
"cargo --help package",
"cargo publish --dry-run -h",
"cargo publish -- --dry-run",
"cargo publish --dry-run -- --no-verify",
"cargo publish --dry-run=false",
"cargo publish --registry=--dry-run",
"cargo publish -p--dry-run",
"cargo publish --token=--dry-run",
"cargo publish --config --dry-run",
"cargo publish --config alias.note=--dry-run",
"cargo publish --unknown-option --dry-run",
"cargo package --target --no-verify",
"cargo package --package --list",
"cargo package --config",
"cargo publish --config 'alias.note=\"--dry-run\"'",
"cargo publish --registry 'example --dry-run registry'",
"cargo publish --dry-run --config 'alias.note=\"--no-verify\"'",
"cargo publish --dry-run $EXTRA_CARGO_OPTIONS",
] {
let classification = classify_command(command);
assert!(
!classification.is_compilation,
"{command}: {classification:?}"
);
let detailed = classify_command_detailed(command).classification;
assert!(!detailed.is_compilation, "{command}: {detailed:?}");
assert!(!is_cargo_package_verification(command), "{command}");
}
}
#[test]
fn regression_cargo_never_intercept() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "cargo install ripgrep",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo publish",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo login",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo fmt",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo fmt --check",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo fix",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo clean",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo new my_project",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo init",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo add serde",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo remove serde",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo update",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo generate-lockfile",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo watch -x test",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo -V",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo nextest list",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo nextest archive",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo nextest show",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cargo",
expect_compilation: false,
expected_kind: None,
reason_contains: "bare cargo",
min_confidence: 0.0,
},
Case {
cmd: "cargo nextest",
expect_compilation: false,
expected_kind: None,
reason_contains: "bare cargo nextest",
min_confidence: 0.0,
},
Case {
cmd: "cargo tree",
expect_compilation: false,
expected_kind: None,
reason_contains: "not interceptable",
min_confidence: 0.0,
},
Case {
cmd: "cargo metadata",
expect_compilation: false,
expected_kind: None,
reason_contains: "not interceptable",
min_confidence: 0.0,
},
Case {
cmd: "cargo search serde",
expect_compilation: false,
expected_kind: None,
reason_contains: "not interceptable",
min_confidence: 0.0,
},
Case {
cmd: "cargo vendor",
expect_compilation: false,
expected_kind: None,
reason_contains: "not interceptable",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_bun_compilation_positive() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "bun test",
expect_compilation: true,
expected_kind: Some(CompilationKind::BunTest),
reason_contains: "bun test",
min_confidence: 0.90,
},
Case {
cmd: "bun test src/",
expect_compilation: true,
expected_kind: Some(CompilationKind::BunTest),
reason_contains: "bun test",
min_confidence: 0.90,
},
Case {
cmd: "bun test --bail",
expect_compilation: true,
expected_kind: Some(CompilationKind::BunTest),
reason_contains: "bun test",
min_confidence: 0.90,
},
Case {
cmd: "bun test --timeout 5000",
expect_compilation: true,
expected_kind: Some(CompilationKind::BunTest),
reason_contains: "bun test",
min_confidence: 0.90,
},
Case {
cmd: "bun typecheck",
expect_compilation: true,
expected_kind: Some(CompilationKind::BunTypecheck),
reason_contains: "bun typecheck",
min_confidence: 0.90,
},
Case {
cmd: "bun typecheck src/",
expect_compilation: true,
expected_kind: Some(CompilationKind::BunTypecheck),
reason_contains: "bun typecheck",
min_confidence: 0.90,
},
];
run_cases(&cases);
}
#[test]
fn regression_bun_never_intercept() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "bun install",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun add react",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun remove react",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun link",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun unlink",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun pm ls",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun init",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun create my-app",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun upgrade",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun run dev",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun build src/index.ts",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun dev",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun repl",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun x vitest",
expect_compilation: false,
expected_kind: None,
reason_contains: "bun x",
min_confidence: 0.0,
},
Case {
cmd: "bun x tsc --noEmit",
expect_compilation: false,
expected_kind: None,
reason_contains: "bun x",
min_confidence: 0.0,
},
Case {
cmd: "bun test --watch",
expect_compilation: false,
expected_kind: None,
reason_contains: "interactive",
min_confidence: 0.0,
},
Case {
cmd: "bun test -w",
expect_compilation: false,
expected_kind: None,
reason_contains: "interactive",
min_confidence: 0.0,
},
Case {
cmd: "bun typecheck --watch",
expect_compilation: false,
expected_kind: None,
reason_contains: "interactive",
min_confidence: 0.0,
},
Case {
cmd: "bun typecheck -w",
expect_compilation: false,
expected_kind: None,
reason_contains: "interactive",
min_confidence: 0.0,
},
Case {
cmd: "bun --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun -v",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun --help",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun -h",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "bun completions",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_c_cpp_compilation_positive() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "gcc -c main.c -o main.o",
expect_compilation: true,
expected_kind: Some(CompilationKind::Gcc),
reason_contains: "gcc",
min_confidence: 0.85,
},
Case {
cmd: "gcc main.c -o main",
expect_compilation: true,
expected_kind: Some(CompilationKind::Gcc),
reason_contains: "gcc",
min_confidence: 0.85,
},
Case {
cmd: "g++ -c main.cpp -o main.o",
expect_compilation: true,
expected_kind: Some(CompilationKind::Gpp),
reason_contains: "g++",
min_confidence: 0.85,
},
Case {
cmd: "g++ main.cc -o main",
expect_compilation: true,
expected_kind: Some(CompilationKind::Gpp),
reason_contains: "g++",
min_confidence: 0.85,
},
Case {
cmd: "clang -c main.c -o main.o",
expect_compilation: true,
expected_kind: Some(CompilationKind::Clang),
reason_contains: "clang",
min_confidence: 0.85,
},
Case {
cmd: "clang++ -c main.cpp -o main.o",
expect_compilation: true,
expected_kind: Some(CompilationKind::Clangpp),
reason_contains: "clang++",
min_confidence: 0.85,
},
Case {
cmd: "clang++ main.cc -o main",
expect_compilation: true,
expected_kind: Some(CompilationKind::Clangpp),
reason_contains: "clang++",
min_confidence: 0.85,
},
Case {
cmd: "cc -c main.c -o main.o",
expect_compilation: true,
expected_kind: Some(CompilationKind::Gcc),
reason_contains: "cc",
min_confidence: 0.80,
},
Case {
cmd: "c++ -c main.cpp -o main.o",
expect_compilation: true,
expected_kind: Some(CompilationKind::Gpp),
reason_contains: "c++",
min_confidence: 0.80,
},
];
run_cases(&cases);
}
#[test]
fn regression_c_cpp_version_checks_not_intercepted() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "rustc --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "rustc -V",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "gcc --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "gcc -v",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "clang --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "clang -v",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "make --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "make -v",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
Case {
cmd: "cmake --version",
expect_compilation: false,
expected_kind: None,
reason_contains: "never-intercept",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_build_systems_positive() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "make",
expect_compilation: true,
expected_kind: Some(CompilationKind::Make),
reason_contains: "make build",
min_confidence: 0.80,
},
Case {
cmd: "make -j8",
expect_compilation: true,
expected_kind: Some(CompilationKind::Make),
reason_contains: "make build",
min_confidence: 0.80,
},
Case {
cmd: "make all",
expect_compilation: true,
expected_kind: Some(CompilationKind::Make),
reason_contains: "make build",
min_confidence: 0.80,
},
Case {
cmd: "cmake --build build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CmakeBuild),
reason_contains: "cmake --build",
min_confidence: 0.85,
},
Case {
cmd: "cmake --build=build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CmakeBuild),
reason_contains: "cmake --build",
min_confidence: 0.85,
},
Case {
cmd: "ninja",
expect_compilation: true,
expected_kind: Some(CompilationKind::Ninja),
reason_contains: "ninja build",
min_confidence: 0.85,
},
Case {
cmd: "ninja -j4",
expect_compilation: true,
expected_kind: Some(CompilationKind::Ninja),
reason_contains: "ninja build",
min_confidence: 0.85,
},
Case {
cmd: "meson compile",
expect_compilation: true,
expected_kind: Some(CompilationKind::Meson),
reason_contains: "meson compile",
min_confidence: 0.80,
},
];
run_cases(&cases);
}
#[test]
fn regression_build_systems_not_intercepted() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "make clean",
expect_compilation: false,
expected_kind: None,
reason_contains: "make maintenance",
min_confidence: 0.0,
},
Case {
cmd: "make install",
expect_compilation: false,
expected_kind: None,
reason_contains: "make maintenance",
min_confidence: 0.0,
},
Case {
cmd: "make distclean",
expect_compilation: false,
expected_kind: None,
reason_contains: "make maintenance",
min_confidence: 0.0,
},
Case {
cmd: "ninja -t clean",
expect_compilation: false,
expected_kind: None,
reason_contains: "ninja clean",
min_confidence: 0.0,
},
Case {
cmd: "ninja clean",
expect_compilation: false,
expected_kind: None,
reason_contains: "ninja clean",
min_confidence: 0.0,
},
Case {
cmd: "meson setup build",
expect_compilation: false,
expected_kind: None,
reason_contains: "no matching pattern",
min_confidence: 0.0,
},
Case {
cmd: "meson configure",
expect_compilation: false,
expected_kind: None,
reason_contains: "no matching pattern",
min_confidence: 0.0,
},
Case {
cmd: "cmake .",
expect_compilation: false,
expected_kind: None,
reason_contains: "no matching pattern",
min_confidence: 0.0,
},
Case {
cmd: "cmake -DCMAKE_BUILD_TYPE=Release ..",
expect_compilation: false,
expected_kind: None,
reason_contains: "no matching pattern",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_rustc_positive() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "rustc main.rs",
expect_compilation: true,
expected_kind: Some(CompilationKind::Rustc),
reason_contains: "rustc",
min_confidence: 0.90,
},
Case {
cmd: "rustc main.rs -o main",
expect_compilation: true,
expected_kind: Some(CompilationKind::Rustc),
reason_contains: "rustc",
min_confidence: 0.90,
},
Case {
cmd: "rustc --edition 2021 main.rs",
expect_compilation: true,
expected_kind: Some(CompilationKind::Rustc),
reason_contains: "rustc",
min_confidence: 0.90,
},
];
run_cases(&cases);
}
#[test]
fn regression_shell_structure_exclusions() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "cargo build 2>&1 | xargs rm",
expect_compilation: false,
expected_kind: None,
reason_contains: "piped",
min_confidence: 0.0,
},
Case {
cmd: "cargo build < input.txt",
expect_compilation: false,
expected_kind: None,
reason_contains: "input redirect",
min_confidence: 0.0,
},
Case {
cmd: "(cargo build)",
expect_compilation: false,
expected_kind: None,
reason_contains: "subshell",
min_confidence: 0.0,
},
Case {
cmd: "$(cargo build)",
expect_compilation: false,
expected_kind: None,
reason_contains: "subshell",
min_confidence: 0.0,
},
Case {
cmd: "`cargo build`",
expect_compilation: false,
expected_kind: None,
reason_contains: "subshell capture",
min_confidence: 0.0,
},
Case {
cmd: "cargo build --config <(echo ...)",
expect_compilation: false,
expected_kind: None,
reason_contains: "subshell",
min_confidence: 0.0,
},
Case {
cmd: "cargo build; cargo test",
expect_compilation: false,
expected_kind: None,
reason_contains: "chained",
min_confidence: 0.0,
},
Case {
cmd: "cargo build || echo failed",
expect_compilation: false,
expected_kind: None,
reason_contains: "chained",
min_confidence: 0.0,
},
Case {
cmd: "cargo build\nrm -rf /",
expect_compilation: false,
expected_kind: None,
reason_contains: "newline",
min_confidence: 0.0,
},
Case {
cmd: "cargo build\r\nevil",
expect_compilation: false,
expected_kind: None,
reason_contains: "newline",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_compound_chain_offloads_every_compilation_segment() {
let _guard = test_guard!();
let result = classify_command("cargo build --release && cargo test");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
assert_eq!(
result.command_prefix.as_deref(),
Some("rch exec -- cargo build --release && ")
);
assert_eq!(result.extracted_command.as_deref(), Some("cargo test"));
let result = classify_command("touch marker && cargo build && cargo test --workspace");
assert_eq!(
result.command_prefix.as_deref(),
Some("touch marker && rch exec -- cargo build && ")
);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo test --workspace")
);
let result = classify_command("go vet ./... && cargo check");
assert_eq!(
result.command_prefix.as_deref(),
Some("rch exec -- go vet ./... && ")
);
let result = classify_command("cd /repo && cargo build");
assert_eq!(result.command_prefix.as_deref(), Some("cd /repo && "));
let result = classify_command("go build ./cmd && cargo test");
assert!(result.is_compilation);
assert_eq!(result.command_prefix.as_deref(), Some("go build ./cmd && "));
assert_eq!(
compound_local_compilation_segments("go build ./cmd && cargo test"),
vec!["go build ./cmd".to_string()]
);
assert!(
compound_local_compilation_segments("cargo build --release && cargo test").is_empty()
);
assert!(compound_local_compilation_segments("cd /repo && cargo build").is_empty());
assert!(compound_local_compilation_segments("cargo test").is_empty());
assert!(
compound_local_compilation_segments("bash -c \"cargo build && cargo test\"").is_empty(),
"quoted operators are not chain separators"
);
assert!(!classify_command("cargo build ; cargo test").is_compilation);
assert!(declined_compilation_due_to_structure("cargo build ; cargo test").is_some());
assert!(declined_compilation_due_to_structure("cargo build || echo failed").is_some());
assert!(declined_compilation_due_to_structure("cargo build && ./run").is_some());
}
#[test]
fn regression_benign_trailing_structure_offloaded() {
let _guard = test_guard!();
let expectations: &[(&str, CompilationKind, &str)] = &[
(
"cargo build > log.txt",
CompilationKind::CargoBuild,
"cargo build > log.txt",
),
(
"cargo build >> log.txt",
CompilationKind::CargoBuild,
"cargo build >> log.txt",
),
(
"cargo test --workspace > out.log 2>&1",
CompilationKind::CargoTest,
"cargo test --workspace > out.log 2>&1",
),
(
"cargo build 2>&1 | grep error",
CompilationKind::CargoBuild,
"cargo build 2>&1 | grep error",
),
(
"cargo test | tee output.log",
CompilationKind::CargoTest,
"cargo test | tee output.log",
),
(
"cargo check --lib 2>&1 | head",
CompilationKind::CargoCheck,
"cargo check --lib 2>&1 | head",
),
(
"cargo build &",
CompilationKind::CargoBuild,
"cargo build &",
),
];
for (cmd, kind, extracted) in expectations {
let result = classify_command(cmd);
assert!(
result.is_compilation,
"'{cmd}' should be offloaded, got: {:?}",
result.reason
);
assert_eq!(result.kind, Some(*kind), "kind mismatch for '{cmd}'");
assert_eq!(
result.extracted_command.as_deref(),
Some(*extracted),
"extracted command mismatch for '{cmd}'"
);
assert_eq!(result.command_prefix.as_deref(), Some(""));
}
}
#[test]
fn regression_unsafe_trailing_structure_still_rejected() {
let _guard = test_guard!();
let rejected = [
"cargo build < input.txt", "cargo build | xargs rm", "cargo build 2>&1 | grep e | sh", "cargo test | tee log && cargo run", "cargo build > a.log < in.txt", ];
for cmd in rejected {
let result = classify_command(cmd);
assert!(
!result.is_compilation,
"'{cmd}' should NOT be offloaded, got: {:?}",
result
);
}
}
#[test]
fn regression_shell_dash_c_wrapper_offloaded() {
let _guard = test_guard!();
let result = classify_command("bash -c \"cargo test --workspace\"");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo test --workspace")
);
let result = classify_command("sh -c 'cargo build --release'");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo build --release")
);
let result = classify_command("/bin/bash -c \"cargo check\"");
assert!(result.is_compilation, "got: {:?}", result.reason);
let result = classify_command("bash -c \"cd /repo && cargo build\"");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo build"),
"inner compound should keep extracted command"
);
assert_eq!(result.command_prefix.as_deref(), Some("cd /repo && "));
let result = classify_command("bash -c \"cargo test > log 2>&1\"");
assert!(result.is_compilation, "got: {:?}", result.reason);
assert_eq!(
result.extracted_command.as_deref(),
Some("cargo test > log 2>&1")
);
let result = classify_command("bash -c \"ls -la\"");
assert!(!result.is_compilation);
let result = classify_command("bash -c \"cargo fmt\"");
assert!(!result.is_compilation);
}
#[test]
fn regression_fd_redirect_safe() {
let _guard = test_guard!();
let result = classify_command("cargo build 2>&1");
assert!(
result.is_compilation,
"cargo build 2>&1 should be classified as compilation (fd redirect is safe), got: {:?}",
result.reason
);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
}
#[test]
fn regression_compound_commands() {
let _guard = test_guard!();
let result = classify_command("cd /path && cargo build");
assert!(
result.is_compilation,
"cd && cargo build should compile, got: {:?}",
result.reason
);
assert_eq!(result.kind, Some(CompilationKind::CargoBuild));
assert!(result.command_prefix.is_some(), "should have prefix");
assert!(
result.extracted_command.is_some(),
"should have extracted command"
);
let result = classify_command("cd /path && cargo test --workspace");
assert!(
result.is_compilation,
"cd && cargo test should compile, got: {:?}",
result.reason
);
assert_eq!(result.kind, Some(CompilationKind::CargoTest));
let result = classify_command("cd /path && cargo fmt");
assert!(!result.is_compilation, "cd && cargo fmt should NOT compile");
let result = classify_command("cd /path && ls -la");
assert!(!result.is_compilation, "cd && ls should NOT compile");
}
#[test]
fn regression_wrapper_normalization() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "sudo cargo build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "time cargo test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "env RUST_BACKTRACE=1 cargo test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "env -u RUST_LOG cargo test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "RUST_BACKTRACE=1 cargo test",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoTest),
reason_contains: "cargo test",
min_confidence: 0.90,
},
Case {
cmd: "nice cargo build --release",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "/usr/bin/cargo build",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoBuild),
reason_contains: "cargo build",
min_confidence: 0.90,
},
Case {
cmd: "sudo env RUSTFLAGS=-Dwarnings cargo clippy",
expect_compilation: true,
expected_kind: Some(CompilationKind::CargoClippy),
reason_contains: "cargo clippy",
min_confidence: 0.85,
},
];
run_cases(&cases);
}
#[test]
fn regression_non_compilation_passthrough() {
let _guard = test_guard!();
let cases = [
Case {
cmd: "ls -la",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "pwd",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "echo hello",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "cat Cargo.toml",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "git status",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "git commit -m 'fix'",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "npm install",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "yarn build",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "python main.py",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
#[cfg(unix)]
Case {
cmd: "go build -o bin/app .",
expect_compilation: true,
expected_kind: Some(CompilationKind::GoBuild),
reason_contains: "go build command",
min_confidence: 0.9,
},
Case {
cmd: "go build ./cmd/app",
expect_compilation: false,
expected_kind: None,
reason_contains: "requires a supported explicit -o file",
min_confidence: 0.0,
},
Case {
cmd: "docker build -t myapp .",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "pip install -r requirements.txt",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "curl https://example.com",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "mkdir -p build",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "rm -rf target/",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "cp -r src/ backup/",
expect_compilation: false,
expected_kind: None,
reason_contains: "no compilation keyword",
min_confidence: 0.0,
},
Case {
cmd: "",
expect_compilation: false,
expected_kind: None,
reason_contains: "empty command",
min_confidence: 0.0,
},
Case {
cmd: " ",
expect_compilation: false,
expected_kind: None,
reason_contains: "empty command",
min_confidence: 0.0,
},
];
run_cases(&cases);
}
#[test]
fn regression_classification_details_fields() {
let _guard = test_guard!();
let details = classify_command_detailed("cargo build --release");
assert_eq!(details.original, "cargo build --release");
assert!(details.classification.is_compilation);
assert_eq!(
details.classification.kind,
Some(CompilationKind::CargoBuild)
);
assert!(details.classification.confidence >= 0.90);
assert_eq!(details.tiers.len(), 5, "Should have 5 tier decisions");
for tier in &details.tiers[..4] {
assert_eq!(tier.decision, TierDecision::Pass);
}
assert_eq!(details.tiers[4].decision, TierDecision::Pass);
assert_eq!(details.tiers[4].tier, 4);
let details = classify_command_detailed("ls -la");
assert!(!details.classification.is_compilation);
assert!(
details.tiers.len() >= 3,
"Should have at least 3 tiers for keyword rejection"
);
assert_eq!(details.tiers.last().unwrap().decision, TierDecision::Reject);
let details = classify_command_detailed("cargo fmt --check");
assert!(!details.classification.is_compilation);
assert!(
details.tiers.len() >= 4,
"Should have at least 4 tiers for never-intercept rejection"
);
let tier3 = details.tiers.iter().find(|t| t.tier == 3).unwrap();
assert_eq!(tier3.decision, TierDecision::Reject);
assert!(
tier3.reason.contains("never-intercept"),
"Tier 3 reason should mention never-intercept, got: {:?}",
tier3.reason
);
let details = classify_command_detailed("cargo build | xargs rm");
assert!(!details.classification.is_compilation);
let tier1 = details.tiers.iter().find(|t| t.tier == 1).unwrap();
assert_eq!(tier1.decision, TierDecision::Reject);
assert!(tier1.reason.contains("piped"));
let details = classify_command_detailed("");
assert!(!details.classification.is_compilation);
assert_eq!(details.tiers[0].tier, 0);
assert_eq!(details.tiers[0].decision, TierDecision::Reject);
}
#[test]
fn regression_every_compilation_has_reason_and_confidence() {
let _guard = test_guard!();
let compilation_cmds = [
"cargo build",
"cargo test",
"cargo check",
"cargo clippy",
"cargo doc",
"cargo run",
"cargo bench",
"cargo nextest run",
"rustc main.rs",
"gcc -c main.c -o main.o",
"g++ -c main.cpp -o main.o",
"clang -c main.c -o main.o",
"clang++ -c main.cpp -o main.o",
"make",
"cmake --build build",
"ninja",
"meson compile",
"bun test",
"bun typecheck",
];
for cmd in compilation_cmds {
let result = classify_command(cmd);
assert!(result.is_compilation, "{cmd:?} should be compilation");
assert!(
result.kind.is_some(),
"{cmd:?} should have a CompilationKind"
);
assert!(
result.confidence > 0.0,
"{cmd:?} should have non-zero confidence"
);
assert!(
!result.reason.is_empty(),
"{cmd:?} should have a non-empty reason"
);
}
}
#[test]
fn regression_every_rejection_has_reason() {
let _guard = test_guard!();
let non_compilation_cmds = [
"",
"ls",
"pwd",
"git status",
"cargo fmt",
"cargo install ripgrep",
"cargo clean",
"bun install",
"bun add react",
"bun run dev",
"bun dev",
"bun repl",
"bun x vitest",
"bun test --watch",
"bun typecheck -w",
"make clean",
"ninja clean",
"gcc --version",
"rustc --version",
"cargo build | xargs rm",
"cargo build < input.txt",
"cargo build || echo failed",
"(cargo build)",
];
for cmd in non_compilation_cmds {
let result = classify_command(cmd);
assert!(
!result.is_compilation,
"{cmd:?} should NOT be compilation, got kind={:?}",
result.kind
);
assert!(
!result.reason.is_empty(),
"{cmd:?} should have a non-empty rejection reason"
);
assert_eq!(result.kind, None, "{cmd:?} should have no CompilationKind");
assert_eq!(
result.confidence, 0.0,
"{cmd:?} should have zero confidence"
);
}
}
#[test]
fn regression_real_world_agent_workflows() {
let _guard = test_guard!();
let cd_build = classify_command(
"cd /data/projects/remote_compilation_helper && cargo build --workspace --all-targets",
);
assert!(
cd_build.is_compilation,
"agent cd+build workflow should be intercepted"
);
let test_ws = classify_command("cargo test --workspace -- --nocapture");
assert!(test_ws.is_compilation);
assert_eq!(test_ws.kind, Some(CompilationKind::CargoTest));
let clippy = classify_command("cargo clippy --workspace --all-targets -- -D warnings");
assert!(clippy.is_compilation);
assert_eq!(clippy.kind, Some(CompilationKind::CargoClippy));
let fmt = classify_command("cargo fmt --check");
assert!(!fmt.is_compilation);
let cat = classify_command("cat Cargo.toml");
assert!(!cat.is_compilation);
let gs = classify_command("git status");
assert!(!gs.is_compilation);
let install = classify_command("cargo install cargo-nextest");
assert!(!install.is_compilation);
let pkg_test = classify_command("cargo test -p rch-common");
assert!(pkg_test.is_compilation);
assert_eq!(pkg_test.kind, Some(CompilationKind::CargoTest));
let env_test = classify_command("RUST_BACKTRACE=1 cargo test --workspace");
assert!(env_test.is_compilation);
assert_eq!(env_test.kind, Some(CompilationKind::CargoTest));
let nxt = classify_command("cargo nextest run --workspace --no-fail-fast");
assert!(nxt.is_compilation);
assert_eq!(nxt.kind, Some(CompilationKind::CargoNextest));
}
#[test]
fn regression_keyword_in_non_compilation_context() {
let _guard = test_guard!();
let result = classify_command("echo cargo build");
assert!(
!result.is_compilation,
"echo cargo should not compile, got: {:?}",
result.reason
);
let result = classify_command("grep make Makefile");
assert!(
!result.is_compilation,
"grep make should not compile, got: {:?}",
result.reason
);
let result = classify_command("cat gcc_output.log");
assert!(
!result.is_compilation,
"cat gcc_output should not compile, got: {:?}",
result.reason
);
}
#[test]
fn regression_build_words_in_non_build_arguments_are_not_compilation() {
let _guard = test_guard!();
let cases = [
r#"br comments add ft-4tp7g.1 "remote proof blocked: cargo test -p rchd --lib""#,
r#"AGENT_NAME=Codex br comments add ft-4tp7g.1 "proof lane: cargo clippy --workspace""#,
r#"printf "cargo build --release""#,
r#"echo 'cargo test -p rchd -- --nocapture'"#,
r#"grep "cmake --build" docs/proof-notes.md"#,
];
for cmd in cases {
let result = classify_command(cmd);
assert!(
!result.is_compilation,
"embedded build text should not compile for {cmd:?}: {:?}",
result
);
assert_eq!(result.reason.as_ref(), "no compilation keyword");
}
}
#[test]
fn golden_non_build_arguments_with_build_words_stay_non_compilation() {
let _guard = test_guard!();
const FIXTURE_PATH: &str =
"../../tests/goldens/ft_4tp7g/classification_non_build_arguments.json";
#[derive(serde::Deserialize)]
struct Golden {
schema_version: String,
cases: Vec<Case>,
expected_results: serde_json::Value,
}
#[derive(serde::Deserialize)]
struct Case {
name: String,
command: String,
}
let golden: Golden = serde_json::from_str(include_str!(
"../../tests/goldens/ft_4tp7g/classification_non_build_arguments.json"
))
.expect("classification golden fixture parses");
assert_eq!(
golden.schema_version,
"rch.golden.classification_non_build_arguments.v1"
);
let mut actual_results = Vec::with_capacity(golden.cases.len());
for case in golden.cases {
let result = classify_command(&case.command);
actual_results.push(serde_json::json!({
"name": case.name,
"is_compilation": result.is_compilation,
"kind": result.kind,
"reason": result.reason.as_ref(),
}));
}
assert_golden_json(
serde_json::Value::Array(actual_results),
&golden.expected_results,
FIXTURE_PATH,
);
}
#[test]
fn regression_keyword_filter_still_accepts_normalized_build_invocations() {
let _guard = test_guard!();
let cases = [
("RUST_BACKTRACE=1 cargo test", CompilationKind::CargoTest),
(
"env 'CARGO_TARGET_DIR=/data/tmp/rch-target' cargo build",
CompilationKind::CargoBuild,
),
("sudo -E cargo check", CompilationKind::CargoCheck),
("/usr/bin/time cargo build", CompilationKind::CargoBuild),
];
for (cmd, expected_kind) in cases {
let result = classify_command(cmd);
assert!(
result.is_compilation,
"normalized build invocation should compile for {cmd:?}: {:?}",
result
);
assert_eq!(result.kind, Some(expected_kind));
}
}
#[test]
fn regression_compilation_kind_properties() {
let _guard = test_guard!();
assert!(CompilationKind::CargoTest.is_test_command());
assert!(CompilationKind::CargoNextest.is_test_command());
assert!(CompilationKind::CargoBench.is_test_command());
assert!(CompilationKind::BunTest.is_test_command());
assert!(!CompilationKind::CargoBuild.is_test_command());
assert!(!CompilationKind::CargoCheck.is_test_command());
assert!(!CompilationKind::CargoClippy.is_test_command());
assert!(!CompilationKind::CargoDoc.is_test_command());
assert!(!CompilationKind::BunTypecheck.is_test_command());
assert!(!CompilationKind::Gcc.is_test_command());
assert!(!CompilationKind::Gpp.is_test_command());
assert!(!CompilationKind::Clang.is_test_command());
assert!(!CompilationKind::Clangpp.is_test_command());
assert!(!CompilationKind::Make.is_test_command());
assert!(!CompilationKind::CmakeBuild.is_test_command());
assert!(!CompilationKind::Ninja.is_test_command());
assert!(!CompilationKind::Meson.is_test_command());
assert!(!CompilationKind::Rustc.is_test_command());
assert_eq!(CompilationKind::CargoBuild.command_base(), "cargo");
assert_eq!(CompilationKind::CargoTest.command_base(), "cargo");
assert_eq!(CompilationKind::CargoNextest.command_base(), "cargo");
assert_eq!(CompilationKind::Rustc.command_base(), "rustc");
assert_eq!(CompilationKind::Gcc.command_base(), "gcc");
assert_eq!(CompilationKind::Gpp.command_base(), "g++");
assert_eq!(CompilationKind::Clang.command_base(), "clang");
assert_eq!(CompilationKind::Clangpp.command_base(), "clang++");
assert_eq!(CompilationKind::Make.command_base(), "make");
assert_eq!(CompilationKind::CmakeBuild.command_base(), "cmake");
assert_eq!(CompilationKind::Ninja.command_base(), "ninja");
assert_eq!(CompilationKind::Meson.command_base(), "meson");
assert_eq!(CompilationKind::BunTest.command_base(), "bun");
assert_eq!(CompilationKind::BunTypecheck.command_base(), "bun");
}
}
#[cfg(test)]
mod tests_go_and_typescript {
use super::*;
fn kind_of(cmd: &str) -> Option<CompilationKind> {
classify_command(cmd).kind
}
#[test]
fn go_build_test_vet_are_classified() {
assert_eq!(
kind_of("go build -o bin/app ."),
cfg!(unix).then_some(CompilationKind::GoBuild)
);
assert_eq!(kind_of("go test ./..."), Some(CompilationKind::GoTest));
assert_eq!(kind_of("go vet ./..."), Some(CompilationKind::GoVet));
assert_eq!(
kind_of("go test -tags=e2e ./e2e -run ^TestE2EAtomicAssignment$"),
Some(CompilationKind::GoTest)
);
assert_eq!(
kind_of("go test ./internal/robot ./internal/cli -run TestExitCode"),
Some(CompilationKind::GoTest)
);
}
#[test]
fn go_commands_clear_the_confidence_threshold() {
for cmd in ["go build -o app .", "go test ./...", "go vet ./..."] {
if !cfg!(unix) && cmd.starts_with("go build") {
continue;
}
let c = classify_command(cmd);
assert!(
c.confidence > 0.85,
"{cmd} scored {} which does not clear the 0.85 threshold",
c.confidence
);
}
}
#[test]
fn go_test_is_a_test_command() {
assert!(CompilationKind::GoTest.is_test_command());
assert!(!CompilationKind::GoBuild.is_test_command());
assert!(!CompilationKind::GoVet.is_test_command());
}
#[test]
fn go_build_explicit_outputs_share_one_literal_contract() {
for (command, path, option_index) in [
("go build -o bin/app ./cmd/app", "bin/app", 2),
("go build -o=bin/app ./cmd/app", "bin/app", 2),
("go build '-o=bin/app' main.go", "bin/app", 2),
(
"go build -trimpath -p=2 -o './products/app [dev]*?' .",
"./products/app [dev]*?",
4,
),
("go build -ldflags '-s -w' -o app", "app", 4),
("go build -ldflags '-s -X main.version=v2' -o app", "app", 4),
("go build '-buildmode=pie' -o app .", "app", 3),
("go build -tags -o -o app .", "app", 4),
("env GOFLAGS= go build -o app .", "app", 2),
] {
let selection = go_build_output(command).unwrap_or_else(|| panic!("{command}"));
assert_eq!(selection.path, path, "{command}");
assert_eq!(selection.option_index, option_index, "{command}");
assert_eq!(
kind_of(command),
cfg!(unix).then_some(CompilationKind::GoBuild),
"{command}"
);
}
}
#[test]
fn go_build_implicit_ambiguous_and_extra_outputs_stay_local() {
for command in [
"go build",
"go build .",
"go build ./...",
"go build main.go",
"go build -o",
"go build -o /tmp/app .",
"go build -o ../app .",
"go build -o . .",
"go build -o bin/ .",
"go build -o - .",
"go build -o one -o two .",
"go build -n -o app .",
"go build -n=true -o app .",
"go build -buildmode=c-shared -o app .",
"go build -buildmode=c-archive -o app .",
"go build -buildmode=plugin -o app .",
"go build -toolexec wrapper -o app .",
"go build -o $OUTPUT .",
"go build -o app* .",
"go build . -o app",
"go build -tags -o app .",
"go build -overlay overlay.json -o app .",
"go build -mod=mod -o app .",
"go build -ldflags '-cpuprofile=cpu.out' -o app .",
"go build -ldflags '-s -w -tmpdir=products' -o app .",
"go build -gcflags '-cpuprofile=cpu.out' -o app .",
"go build -asmflags '-debug' -o app .",
] {
assert!(go_build_output(command).is_none(), "{command}");
assert_eq!(kind_of(command), None, "{command}");
}
assert!(go_build_output(&format!("go build -o {} .", "x".repeat(65_536))).is_none());
assert!(go_build_output("go build -o 'app\nnext' .").is_none());
}
#[test]
fn go_test_binary_profile_and_exec_outputs_stay_local() {
for command in [
"go test -c ./pkg",
"go test -c=true ./pkg",
"go test '-c=true' ./pkg",
"go test -exec sudo ./pkg",
"go test '-exec=sudo' ./pkg",
"go test -o test.bin ./pkg",
"go test -o=test.bin ./pkg",
"go test '-o=test binary' ./pkg",
"go test -coverprofile=coverage.out ./pkg",
"go test -cpuprofile cpu.out ./pkg",
"go test -memprofile=mem.out ./pkg",
"go test -blockprofile=block.out ./pkg",
"go test -mutexprofile=mutex.out ./pkg",
"go test -trace trace.out ./pkg",
"go test ./pkg -args -test.cpuprofile=cpu.out",
"go test ./pkg '-test.coverprofile=coverage.out'",
"go test -outputdir profiles ./pkg",
"go test -fuzz=FuzzParser ./pkg",
"go test ./pkg -args -test.fuzz FuzzParser",
"go test ./pkg -args -test.fuzzcachedir=fuzz-corpus",
"go test ./pkg -args -test.gocoverdir=coverage",
"go test ./pkg -args '-test.testlogfile=test log'",
] {
assert_eq!(kind_of(command), None, "{command}");
}
assert_eq!(kind_of("go test c o exec"), Some(CompilationKind::GoTest));
}
#[test]
fn go_state_mutating_subcommands_are_never_intercepted() {
for cmd in [
"go get github.com/foo/bar",
"go mod tidy",
"go mod download",
"go install ./cmd/tool",
"go run main.go",
"go generate ./...",
"go fmt ./...",
"go clean -cache",
"go work sync",
"go env GOPATH",
"go version",
"go list ./...",
"go tool pprof",
] {
assert_eq!(kind_of(cmd), None, "{cmd} must never be intercepted");
}
}
#[test]
fn go_prefixed_binaries_are_not_matched() {
assert_eq!(kind_of("gofmt -l ."), None);
assert_eq!(kind_of("golangci-lint run"), None);
}
#[test]
fn tsc_noemit_is_classified() {
assert_eq!(kind_of("tsc --noEmit"), Some(CompilationKind::Tsc));
assert_eq!(kind_of("npx tsc --noEmit"), Some(CompilationKind::Tsc));
assert_eq!(
kind_of("npx tsc --noEmit --incremental false"),
Some(CompilationKind::Tsc)
);
assert_eq!(kind_of("tsc --noemit"), Some(CompilationKind::Tsc));
}
#[test]
fn emitting_tsc_stays_local() {
assert_eq!(kind_of("tsc"), None);
assert_eq!(kind_of("tsc -p tsconfig.json"), None);
assert_eq!(kind_of("npx tsc --outDir dist"), None);
}
#[test]
fn tsc_watch_is_not_intercepted() {
assert_eq!(kind_of("tsc --noEmit --watch"), None);
assert_eq!(kind_of("tsc --noEmit -w"), None);
}
#[test]
fn tsc_trivial_forms_are_not_intercepted() {
assert_eq!(kind_of("tsc --version"), None);
assert_eq!(kind_of("tsc --init"), None);
}
#[test]
fn npx_non_tsc_is_not_intercepted() {
assert_eq!(kind_of("npx jest"), None);
assert_eq!(kind_of("npx prettier --write ."), None);
}
#[test]
fn command_base_is_allowlisted() {
use crate::types::ExecutionConfig;
let exec = ExecutionConfig::default();
for kind in [
CompilationKind::GoBuild,
CompilationKind::GoTest,
CompilationKind::GoVet,
CompilationKind::Tsc,
] {
let base = kind.command_base();
assert!(
exec.is_allowed(base),
"{base} missing from the execution allowlist — the hook would fail \
open to LOCAL execution, silently losing offload"
);
}
assert_eq!(CompilationKind::GoBuild.command_base(), "go");
assert_eq!(CompilationKind::Tsc.command_base(), "tsc");
}
}