use crate::core::patterns;
use crate::core::tokens::{COUNTING_FAMILY, TokenizerFamily, count_tokens_for};
use super::classification::{
has_structural_output, is_search_output, is_verbatim_output, looks_like_toon,
};
use super::footer::shell_savings_footer;
pub(in crate::shell) fn compress_and_measure(
command: &str,
stdout: &str,
stderr: &str,
exit_code: i32,
) -> (String, usize) {
compress_and_measure_for(command, stdout, stderr, exit_code, COUNTING_FAMILY)
}
pub(in crate::shell) fn compress_and_measure_for(
command: &str,
stdout: &str,
stderr: &str,
exit_code: i32,
family: TokenizerFamily,
) -> (String, usize) {
let compressed_stdout = compress_for_outcome_for(command, stdout, exit_code, family);
let compressed_stderr = compress_for_outcome_for(command, stderr, exit_code, family);
let mut result = String::new();
if !compressed_stdout.is_empty() {
result.push_str(&compressed_stdout);
}
if !compressed_stderr.is_empty() {
if !result.is_empty() {
if exit_code != 0 {
result.push('\n');
result.push_str(crate::shell::STDERR_LABEL);
}
result.push('\n');
}
result.push_str(&compressed_stderr);
}
let content_for_counting = if let Some(pos) = result.rfind("\n[lean-ctx: ") {
&result[..pos]
} else {
&result
};
let output_tokens = count_tokens_for(content_for_counting, family);
(result, output_tokens)
}
pub(crate) fn compress_for_outcome(command: &str, output: &str, exit_code: i32) -> String {
compress_for_outcome_for(command, output, exit_code, COUNTING_FAMILY)
}
pub(crate) fn compress_for_outcome_for(
command: &str,
output: &str,
exit_code: i32,
family: TokenizerFamily,
) -> String {
if exit_code != 0 && !output.trim().is_empty() && !crate::core::protect::has_markers(output) {
return truncate_verbatim(output, count_tokens_for(output, family), family);
}
compress_if_beneficial_with_exit(command, output, exit_code, family)
}
#[allow(dead_code)]
pub(crate) fn verbatim_json_crush(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_json_crush_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_json_crush_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let crushed = patterns::json_schema::crush_verbatim(output)?;
let crushed_tokens = count_tokens_for(&crushed, family);
(crushed_tokens >= min_output_tokens && crushed_tokens < original_tokens)
.then(|| shell_savings_footer(&crushed, original_tokens, crushed_tokens))
}
const MIN_USEFUL_SAVINGS: usize = 100;
const LOSSY_DROP_ENTROPY: f64 = 0.9;
#[allow(dead_code)]
pub(crate) fn verbatim_json_crush_lossy(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_json_crush_lossy_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_json_crush_lossy_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let res = crate::core::json_crush::crush_text_lossy_if_beneficial(output, LOSSY_DROP_ENTROPY)?;
let crushed_tokens = count_tokens_for(&res.text, family);
if crushed_tokens < min_output_tokens || crushed_tokens >= original_tokens {
return None;
}
let handle = crate::proxy::ccr::persist_json(output)?;
let body = shell_savings_footer(&res.text, original_tokens, crushed_tokens);
Some(format!(
"{body}\n[lean-ctx: high-entropy column(s) dropped — full data at {handle}, \
ctx_expand(id=\"{handle}\", json_path=\"…\"|search=\"…\") for a slice]"
))
}
fn tabular_delim_crush<T>(output: &str, crush: impl Fn(&str, char) -> Option<T>) -> Option<T> {
[',', '\t'].into_iter().find_map(|d| crush(output, d))
}
#[allow(dead_code)]
pub(crate) fn verbatim_tabular_crush(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_tabular_crush_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_tabular_crush_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let crushed =
tabular_delim_crush(output, crate::core::tabular_crush::crush_text_if_beneficial)?;
let crushed_tokens = count_tokens_for(&crushed, family);
(crushed_tokens >= min_output_tokens && crushed_tokens < original_tokens)
.then(|| shell_savings_footer(&crushed, original_tokens, crushed_tokens))
}
#[allow(dead_code)]
pub(crate) fn verbatim_tabular_crush_lossy(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_tabular_crush_lossy_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_tabular_crush_lossy_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let res = tabular_delim_crush(output, |text, delim| {
crate::core::tabular_crush::crush_text_lossy_if_beneficial(text, delim, LOSSY_DROP_ENTROPY)
})?;
let crushed_tokens = count_tokens_for(&res.text, family);
if crushed_tokens < min_output_tokens || crushed_tokens >= original_tokens {
return None;
}
let handle = crate::proxy::ccr::persist_tabular(output)?;
let body = shell_savings_footer(&res.text, original_tokens, crushed_tokens);
Some(format!(
"{body}\n[lean-ctx: high-entropy column(s) dropped — full data at {handle}, \
ctx_expand(id=\"{handle}\", search=\"…\") for a slice]"
))
}
#[allow(dead_code)]
pub(crate) fn verbatim_yaml_crush(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_yaml_crush_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_yaml_crush_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let crushed = crate::core::yaml_crush::crush_text_if_beneficial(output)?;
let crushed_tokens = count_tokens_for(&crushed, family);
(crushed_tokens >= min_output_tokens && crushed_tokens < original_tokens)
.then(|| shell_savings_footer(&crushed, original_tokens, crushed_tokens))
}
#[allow(dead_code)]
pub(crate) fn verbatim_yaml_crush_lossy(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_yaml_crush_lossy_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_yaml_crush_lossy_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let res = crate::core::yaml_crush::crush_text_lossy_if_beneficial(output, LOSSY_DROP_ENTROPY)?;
let crushed_tokens = count_tokens_for(&res.text, family);
if crushed_tokens < min_output_tokens || crushed_tokens >= original_tokens {
return None;
}
let handle = crate::proxy::ccr::persist_yaml(output)?;
let body = shell_savings_footer(&res.text, original_tokens, crushed_tokens);
Some(format!(
"{body}\n[lean-ctx: high-entropy column(s) dropped — full data at {handle}, \
ctx_expand(id=\"{handle}\", search=\"…\") for a slice]"
))
}
#[allow(dead_code)]
pub(crate) fn verbatim_html_crush(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
) -> Option<String> {
verbatim_html_crush_for(
output,
original_tokens,
min_output_tokens,
enabled,
COUNTING_FAMILY,
)
}
pub(crate) fn verbatim_html_crush_for(
output: &str,
original_tokens: usize,
min_output_tokens: usize,
enabled: bool,
family: TokenizerFamily,
) -> Option<String> {
if !enabled {
return None;
}
let result = crate::core::html_crush::crush_if_beneficial(output)?;
let crushed_tokens = count_tokens_for(&result.text, family);
if crushed_tokens < min_output_tokens || crushed_tokens >= original_tokens {
return None;
}
let handle = crate::proxy::ccr::persist_html(output)?;
let body = shell_savings_footer(&result.text, original_tokens, crushed_tokens);
Some(format!(
"{body}\n[lean-ctx: HTML extracted to markdown — full page at {handle}, \
ctx_expand(id=\"{handle}\", search=\"…\") for a section]"
))
}
#[allow(dead_code)]
pub(crate) fn compress_if_beneficial(command: &str, output: &str) -> String {
compress_if_beneficial_for(command, output, COUNTING_FAMILY)
}
pub(crate) fn compress_if_beneficial_for(
command: &str,
output: &str,
family: TokenizerFamily,
) -> String {
compress_if_beneficial_with_exit(command, output, -1, family)
}
fn compress_if_beneficial_with_exit(
command: &str,
output: &str,
exit_code: i32,
family: TokenizerFamily,
) -> String {
if output.trim().is_empty() {
return String::new();
}
if crate::core::protect::has_markers(output) {
let original_tokens = count_tokens_for(output, family);
let spliced = crate::core::protect::compress_preserving(output, |seg| {
strip_shell_footer(&compress_if_beneficial_with_exit(
command, seg, exit_code, family,
))
.to_string()
});
let spliced_tokens = count_tokens_for(&spliced, family);
return if spliced_tokens < original_tokens {
shell_savings_footer(&spliced, original_tokens, spliced_tokens)
} else {
spliced
};
}
if is_error_output_from_build_tool(command, output) {
let base = maybe_fold_progress(output, count_tokens_for(output, family), family)
.unwrap_or_else(|| output.to_string());
let base = dedup_build_diagnostics(&base);
return truncate_verbatim(&base, count_tokens_for(&base, family), family);
}
if is_test_runner_command(command) {
let base = maybe_fold_progress(output, count_tokens_for(output, family), family)
.unwrap_or_else(|| output.to_string());
if exit_code == 0
&& let Some(compressed) = patterns::test::compress(&base)
{
let original_tokens = count_tokens_for(output, family);
let compressed_tokens = count_tokens_for(&compressed, family);
if compressed_tokens < original_tokens {
return shell_savings_footer(&compressed, original_tokens, compressed_tokens);
}
}
return truncate_verbatim(&base, count_tokens_for(&base, family), family);
}
if !is_search_output(command) && crate::tools::ctx_shell::contains_auth_flow(output) {
return output.to_string();
}
let original_tokens = count_tokens_for(output, family);
if original_tokens < 200 {
return output.to_string();
}
let min_output_tokens = 20;
let cfg = crate::core::config::Config::load();
let policy = crate::shell::output_policy::classify(command, &cfg.excluded_commands);
if policy == crate::shell::output_policy::OutputPolicy::Verbatim
|| policy == crate::shell::output_policy::OutputPolicy::Passthrough
{
if policy == crate::shell::output_policy::OutputPolicy::Verbatim {
let enabled = cfg.crush_verbatim_json_enabled();
if let Some(crushed) =
verbatim_json_crush_for(output, original_tokens, min_output_tokens, enabled, family)
{
return crushed;
}
if let Some(crushed) = verbatim_json_crush_lossy_for(
output,
original_tokens,
min_output_tokens,
enabled,
family,
) {
return crushed;
}
if let Some(crushed) = verbatim_tabular_crush_for(
output,
original_tokens,
min_output_tokens,
enabled,
family,
) {
return crushed;
}
if let Some(crushed) = verbatim_tabular_crush_lossy_for(
output,
original_tokens,
min_output_tokens,
enabled,
family,
) {
return crushed;
}
if let Some(crushed) =
verbatim_yaml_crush_for(output, original_tokens, min_output_tokens, enabled, family)
{
return crushed;
}
if let Some(crushed) = verbatim_yaml_crush_lossy_for(
output,
original_tokens,
min_output_tokens,
enabled,
family,
) {
return crushed;
}
if let Some(crushed) =
verbatim_html_crush_for(output, original_tokens, min_output_tokens, enabled, family)
{
return crushed;
}
}
return truncate_verbatim(output, original_tokens, family);
}
if cfg
.preserve_compact_formats
.iter()
.any(|f| f.eq_ignore_ascii_case("toon"))
&& looks_like_toon(output)
{
return truncate_verbatim(output, original_tokens, family);
}
if is_verbatim_output(command) {
return truncate_verbatim(output, original_tokens, family);
}
if !is_chained_command(command)
&& (has_structural_output(command) || patterns::has_vcs_owner(command))
{
let cl = command.to_ascii_lowercase();
if let Some(compressed) = patterns::try_specific_pattern(&cl, output)
&& !compressed.trim().is_empty()
{
let compressed_tokens = count_tokens_for(&compressed, family);
let savings = original_tokens.saturating_sub(compressed_tokens);
if compressed_tokens >= min_output_tokens
&& compressed_tokens < original_tokens
&& savings >= MIN_USEFUL_SAVINGS
{
return shell_savings_footer(&compressed, original_tokens, compressed_tokens);
}
}
return output.to_string();
}
if !is_chained_command(command)
&& let Some(mut compressed) = patterns::compress_output(command, output)
&& !compressed.trim().is_empty()
{
let level = crate::core::config::CompressionLevel::effective(&cfg);
if level.is_active() {
let terse_result =
crate::core::terse::pipeline::compress(output, &level, Some(&compressed));
if terse_result.quality_passed {
compressed = terse_result.output;
}
}
let compressed_tokens = count_tokens_for(&compressed, family);
let savings = original_tokens.saturating_sub(compressed_tokens);
if compressed_tokens >= min_output_tokens
&& compressed_tokens < original_tokens
&& savings >= MIN_USEFUL_SAVINGS
{
let ratio = compressed_tokens as f64 / original_tokens as f64;
if ratio < 0.05 && original_tokens > 100 && original_tokens < 2000 {
tracing::warn!("compression removed >95% of small output, returning original");
return output.to_string();
}
return shell_savings_footer(&compressed, original_tokens, compressed_tokens);
}
if compressed_tokens < min_output_tokens {
return output.to_string();
}
}
{
let level = crate::core::config::CompressionLevel::effective(&cfg);
if level.is_active() {
let terse_result = crate::core::terse::pipeline::compress(output, &level, None);
if terse_result.quality_passed && terse_result.savings_pct >= 3.0 {
return shell_savings_footer(
&terse_result.output,
terse_result.tokens_before as usize,
terse_result.tokens_after as usize,
);
}
}
}
let cleaned = crate::core::compressor::lightweight_cleanup(output);
let cleaned_tokens = count_tokens_for(&cleaned, family);
if cleaned_tokens < original_tokens {
let lines: Vec<&str> = cleaned.lines().collect();
if lines.len() > 30 {
let compressed = truncate_with_safety_scan(&lines, original_tokens, family);
if let Some(c) = compressed {
return c;
}
}
if cleaned_tokens < original_tokens {
return shell_savings_footer(&cleaned, original_tokens, cleaned_tokens);
}
}
let lines: Vec<&str> = output.lines().collect();
if lines.len() > 30
&& let Some(c) = truncate_with_safety_scan(&lines, original_tokens, family)
{
return c;
}
output.to_string()
}
fn strip_shell_footer(s: &str) -> &str {
match s.rfind("\n[lean-ctx: ") {
Some(pos) => &s[..pos],
None => s,
}
}
pub(crate) fn dedup_build_diagnostics(output: &str) -> String {
let lines: Vec<&str> = output.lines().collect();
if lines.len() < 10 {
return output.to_string();
}
let mut seen = std::collections::HashSet::new();
let mut deduped_count = 0usize;
let mut result = Vec::with_capacity(lines.len());
for line in &lines {
let trimmed = line.trim();
if is_diagnostic_line(trimmed) {
let key = normalize_diagnostic_key(trimmed);
if !seen.insert(key) {
deduped_count += 1;
continue;
}
}
result.push(*line);
}
if deduped_count > 0 {
result.push("");
let note = format!("[lean-ctx: {deduped_count} duplicate diagnostic(s) removed]");
let mut out: String = result.join("\n");
out.push('\n');
out.push_str(¬e);
return out;
}
output.to_string()
}
fn normalize_diagnostic_key(line: &str) -> String {
let trimmed = line.trim();
if let Some(bracket) = trimmed.rfind(" [")
&& trimmed.ends_with(']')
{
return trimmed[..bracket].to_string();
}
trimmed.to_string()
}
fn is_diagnostic_line(line: &str) -> bool {
if line.is_empty() {
return false;
}
if (line.contains("): warning ") || line.contains("): error ")) && line.contains('(') {
return true;
}
if (line.contains(": warning:") || line.contains(": error:") || line.contains(": warning["))
&& line.contains(':')
{
return true;
}
false
}
fn is_error_output_from_build_tool(command: &str, output: &str) -> bool {
let cmd = command.trim().to_ascii_lowercase();
let is_build_tool = cmd.starts_with("cargo check")
|| cmd.starts_with("cargo build")
|| cmd.starts_with("cargo clippy")
|| cmd.starts_with("cargo test")
|| cmd.starts_with("cargo fmt")
|| cmd.starts_with("cargo run")
|| cmd.starts_with("rustc ")
|| cmd.starts_with("gcc ")
|| cmd.starts_with("g++ ")
|| cmd.starts_with("clang ")
|| cmd.starts_with("clang++ ")
|| cmd.starts_with("make ")
|| cmd.starts_with("cmake ")
|| cmd.starts_with("go build")
|| cmd.starts_with("go vet")
|| cmd.starts_with("go test")
|| cmd.starts_with("golangci-lint")
|| cmd.starts_with("tsc ")
|| cmd.starts_with("tsc\t")
|| cmd == "tsc"
|| cmd.starts_with("npx tsc")
|| cmd.starts_with("eslint")
|| cmd.starts_with("npx eslint")
|| cmd.starts_with("biome ")
|| cmd.starts_with("prettier ")
|| cmd.starts_with("mypy ")
|| cmd.starts_with("pyright ")
|| cmd.starts_with("pylint ")
|| cmd.starts_with("ruff check")
|| cmd.starts_with("flake8")
|| cmd.starts_with("black --check")
|| cmd.starts_with("swift build")
|| cmd.starts_with("swiftc ")
|| cmd.starts_with("xcodebuild ")
|| cmd.starts_with("javac ")
|| cmd.starts_with("gradle ")
|| cmd.starts_with("./gradlew ")
|| cmd.starts_with("mvn ")
|| cmd.starts_with("./mvnw ")
|| cmd.starts_with("dotnet build")
|| cmd.starts_with("dotnet test")
|| cmd.starts_with("msbuild")
|| cmd.starts_with("zig build")
|| cmd.starts_with("nim c ")
|| cmd.starts_with("ghc ")
|| cmd.starts_with("stack build")
|| cmd.starts_with("cabal build")
|| cmd.starts_with("mix compile")
|| cmd.starts_with("mix test")
|| cmd.starts_with("mix credo")
|| cmd.starts_with("shellcheck ")
|| cmd.starts_with("hadolint ")
|| cmd.starts_with("terraform validate")
|| cmd.starts_with("terraform plan")
|| cmd.starts_with("ansible-lint")
|| cmd.starts_with("rubocop ")
|| cmd.starts_with("solhint ")
|| cmd.starts_with("slither ");
if !is_build_tool {
return false;
}
output.contains("error[")
|| output.contains("error:")
|| output.contains("Error:")
|| output.contains("ERROR:")
|| output.contains(" error ")
|| output.contains("warning[")
|| output.contains("warning:")
|| output.contains("failed")
|| output.contains("FAILED")
|| output.contains("panicked at")
|| output.contains("cannot find")
|| output.contains("not found")
|| output.contains("undefined")
|| output.contains("unresolved")
|| output.contains("expected ")
|| output.contains("mismatched types")
|| output.contains("aborting due to")
|| output.contains("could not compile")
}
fn strip_env_prefix(segment: &str) -> &str {
let mut rest = segment.trim_start();
loop {
let Some(first) = rest.split_whitespace().next() else {
return rest;
};
let is_env_assignment = first.contains('=')
&& !first.starts_with('-')
&& first.split('=').next().is_some_and(|name| {
!name.is_empty() && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
});
if !is_env_assignment {
return rest;
}
rest = rest[first.len()..].trim_start();
}
}
fn is_test_runner_command(command: &str) -> bool {
command
.split('|')
.map(|seg| strip_env_prefix(seg.trim()).to_ascii_lowercase())
.any(|seg| {
seg.starts_with("cargo test")
|| seg.starts_with("cargo nextest")
|| seg.starts_with("nextest")
|| seg.starts_with("pytest")
|| seg.starts_with("python -m pytest")
|| seg.starts_with("python3 -m pytest")
|| seg.starts_with("py.test")
|| seg.starts_with("go test")
|| seg.starts_with("gotestsum")
|| seg.starts_with("npm test")
|| seg.starts_with("npm run test")
|| seg.starts_with("pnpm test")
|| seg.starts_with("pnpm run test")
|| seg.starts_with("yarn test")
|| seg.starts_with("bun test")
|| seg.starts_with("deno test")
|| seg.starts_with("jest")
|| seg.starts_with("npx jest")
|| seg.starts_with("vitest")
|| seg.starts_with("npx vitest")
|| seg.starts_with("mocha")
|| seg.starts_with("npx mocha")
|| seg.starts_with("dotnet test")
|| seg.starts_with("mix test")
|| seg.starts_with("rspec")
|| seg.starts_with("bundle exec rspec")
|| seg.starts_with("phpunit")
|| seg.starts_with("./vendor/bin/phpunit")
|| seg.starts_with("./gradlew test")
|| seg.starts_with("gradle test")
|| seg.starts_with("mvn test")
|| seg.starts_with("ctest")
})
}
const MAX_VERBATIM_TOKENS: usize = 8000;
fn truncate_verbatim(output: &str, original_tokens: usize, family: TokenizerFamily) -> String {
if original_tokens <= MAX_VERBATIM_TOKENS {
return output.to_string();
}
let lines: Vec<&str> = output.lines().collect();
let total = lines.len();
if total <= 60 {
return output.to_string();
}
let head = 30.min(total);
let tail = 20.min(total.saturating_sub(head));
let middle = &lines[head..total - tail];
let preserved = crate::core::safety_needles::extract_safety_lines(middle, 200);
let omitted = middle.len() - preserved.len();
let mut result = String::with_capacity(output.len() / 2);
for line in &lines[..head] {
result.push_str(line);
result.push('\n');
}
if preserved.is_empty() {
result.push_str(&format!(
"\n[{omitted} lines omitted — output too large for context window]\n\n"
));
} else {
result.push_str(&format!(
"\n[{omitted} lines omitted, {} test/diagnostic lines preserved]\n",
preserved.len()
));
for line in &preserved {
result.push_str(line);
result.push('\n');
}
result.push('\n');
}
for line in lines.iter().skip(total - tail) {
result.push_str(line);
result.push('\n');
}
let truncated_tokens = count_tokens_for(&result, family);
if crate::core::protocol::savings_footer_visible() {
result.push_str(&format!(
"[lean-ctx: {original_tokens}→{truncated_tokens} tok, verbatim truncated]"
));
}
result
}
fn truncate_with_safety_scan(
lines: &[&str],
original_tokens: usize,
family: TokenizerFamily,
) -> Option<String> {
use crate::core::safety_needles;
let first = &lines[..5];
let last = &lines[lines.len() - 5..];
let middle = &lines[5..lines.len() - 5];
let safety_lines = safety_needles::extract_safety_lines(middle, 80);
let safety_count = safety_lines.len();
let omitted = middle.len() - safety_count;
let mut parts = Vec::new();
parts.push(first.join("\n"));
if safety_count > 0 {
parts.push(format!(
"[{omitted} lines omitted, {safety_count} safety-relevant lines preserved]"
));
parts.push(safety_lines.join("\n"));
} else {
parts.push(format!("[{omitted} lines omitted]"));
}
parts.push(last.join("\n"));
let compressed = parts.join("\n");
let ct = count_tokens_for(&compressed, family);
if ct >= original_tokens {
return None;
}
Some(shell_savings_footer(&compressed, original_tokens, ct))
}
fn fold_repetitive_progress(output: &str) -> Option<String> {
let mut out: Vec<String> = Vec::new();
let mut pending_kind: Option<ProgressKind> = None;
let mut pending: Vec<&str> = Vec::new();
let mut omitted_low_signal = 0usize;
for line in output.lines() {
if is_low_signal_progress(line) {
omitted_low_signal += 1;
continue;
}
let kind = classify_foldable_progress(line);
if kind.is_some() && kind == pending_kind {
pending.push(line);
continue;
}
flush_progress_run(&mut out, pending_kind, &pending);
pending.clear();
pending_kind = kind;
if kind.is_some() {
pending.push(line);
} else {
out.push(line.to_string());
}
}
flush_progress_run(&mut out, pending_kind, &pending);
if omitted_low_signal > 0 {
out.push(format!(
"[{omitted_low_signal} low-signal progress lines omitted]"
));
}
let folded = out.join("\n") + "\n";
(folded.len() < output.len()).then_some(folded)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProgressKind {
CargoCompile,
CargoFresh,
CargoTestOk,
PytestPassed,
NpmProgress,
}
fn classify_foldable_progress(line: &str) -> Option<ProgressKind> {
let trimmed = line.trim_start();
if trimmed.starts_with("Compiling ") || trimmed.starts_with("Checking ") {
return Some(ProgressKind::CargoCompile);
}
if trimmed.starts_with("Fresh ")
|| trimmed.starts_with("Downloaded ")
|| trimmed.starts_with("Downloading ")
{
return Some(ProgressKind::CargoFresh);
}
if trimmed.starts_with("test ") && trimmed.ends_with("... ok") {
return Some(ProgressKind::CargoTestOk);
}
if line.contains(" PASSED [") {
return Some(ProgressKind::PytestPassed);
}
if trimmed.starts_with('[') && trimmed.contains('%') && trimmed.contains('/') {
return Some(ProgressKind::NpmProgress);
}
None
}
fn is_low_signal_progress(line: &str) -> bool {
let trimmed = line.trim();
!trimmed.is_empty() && trimmed.bytes().all(|b| b == b'.')
}
fn flush_progress_run(out: &mut Vec<String>, kind: Option<ProgressKind>, lines: &[&str]) {
let Some(kind) = kind else {
return;
};
if lines.is_empty() {
return;
}
let threshold = match kind {
ProgressKind::CargoCompile | ProgressKind::PytestPassed | ProgressKind::CargoTestOk => 3,
ProgressKind::CargoFresh | ProgressKind::NpmProgress => 5,
};
if lines.len() < threshold {
out.extend(lines.iter().map(|line| (*line).to_string()));
return;
}
out.push(format!(
"[{} {} lines folded]",
lines.len(),
match kind {
ProgressKind::CargoCompile => "cargo compile/check",
ProgressKind::CargoFresh => "cargo download/fresh",
ProgressKind::CargoTestOk => "cargo test ok",
ProgressKind::PytestPassed => "pytest PASSED",
ProgressKind::NpmProgress => "package-manager progress",
}
));
out.push(lines.first().unwrap_or(&"").to_string());
if lines.len() > 2 {
out.push("…".to_string());
}
if lines.len() > 1 {
out.push(lines.last().unwrap_or(&"").to_string());
}
}
fn maybe_fold_progress(
output: &str,
original_tokens: usize,
family: TokenizerFamily,
) -> Option<String> {
let folded = fold_repetitive_progress(output)?;
(count_tokens_for(&folded, family) < original_tokens).then_some(folded)
}
pub(crate) fn is_chained_command(command: &str) -> bool {
let mut in_single = false;
let mut in_double = false;
let bytes = command.as_bytes();
let len = bytes.len();
for i in 0..len {
match bytes[i] {
b'\'' if !in_double => in_single = !in_single,
b'"' if !in_single => in_double = !in_double,
b'&' if !in_single && !in_double && i + 1 < len && bytes[i + 1] == b'&' => {
return true;
}
b';' if !in_single && !in_double => return true,
_ => {}
}
}
false
}
pub fn compress_if_beneficial_pub(command: &str, output: &str) -> String {
compress_if_beneficial_pub_for(command, output, COUNTING_FAMILY)
}
pub(crate) fn compress_if_beneficial_pub_for(
command: &str,
output: &str,
family: TokenizerFamily,
) -> String {
compress_if_beneficial_for(command, output, family)
}
#[allow(dead_code)]
pub(crate) fn preserve_verbatim_pub(output: &str) -> String {
preserve_verbatim_pub_for(output, COUNTING_FAMILY)
}
pub(crate) fn preserve_verbatim_pub_for(output: &str, family: TokenizerFamily) -> String {
truncate_verbatim(output, count_tokens_for(output, family), family)
}