Sanitize captured terminal output before replay - #2552
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2552 +/- ##
==========================================
+ Coverage 93.63% 93.65% +0.01%
==========================================
Files 134 135 +1
Lines 28842 28995 +153
==========================================
+ Hits 27006 27155 +149
- Misses 1836 1840 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR addresses terminal-output manipulation seen with nested runs (Issue #2550) by sanitizing captured PTY output before replaying it, while still preserving SGR color styling. It centralizes terminal capability handling and replaces ad-hoc ANSI stripping in the live preview path with an incremental ANSI/control-sequence parser.
Changes:
- Add a
terminalmodule that filters captured PTY output into a safe linear transcript (keeps SGR, resolves CR/EL overwrites, discards other controls). - Change streaming output plumbing to send decoded, ANSI-free preview text (
&str) to the progress reporter. - Add regression coverage for PTY output replay behavior and chunk-splitting parsing.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| crates/prek/src/terminal.rs | New terminal capability + captured-output filtering logic (PTY transcript + preview filtering). |
| crates/prek/src/process.rs | Streams sanitized preview text to sinks; sanitizes PTY-captured bytes before returning/storing. |
| crates/prek/src/cli/run/reporter.rs | Updates preview sink API from raw bytes to decoded preview text. |
| crates/prek/tests/run.rs | Adds regression test ensuring color is preserved but terminal controls are not replayed. |
| crates/prek/src/printer.rs | Moves Windows VT progress target implementation behind terminal::progress_draw_target. |
| crates/prek/src/main.rs | Wires in mod terminal, uses shared USE_COLOR, and centralizes ANSI enablement. |
| crates/prek/src/run.rs | Removes duplicated USE_COLOR definition (now in terminal). |
| crates/prek/src/languages/docker.rs | Updates USE_COLOR import source. |
| crates/prek/src/cli/run/run.rs | Updates USE_COLOR import source. |
| crates/prek/src/process.rs | Updates module docs and integrates new filters. |
| crates/prek/Cargo.toml | Adds anstyle-parse dependency. |
| Cargo.toml | Adds workspace pin for anstyle-parse. |
| Cargo.lock | Locks anstyle-parse into the dependency graph. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4f1fa4af7c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
📦 Cargo Bloat ComparisonBinary size change: +0.66% (30.3 MiB → 30.5 MiB) Expand for cargo-bloat outputHead Branch ResultsBase Branch Results |
⚡️ Hyperfine BenchmarksSummary: 10 regressions, 5 improvements above the 10% threshold. Environment
CLI CommandsBenchmarking basic commands in the main repo:
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base --version |
1.7 ± 0.1 | 1.6 | 2.0 | 1.01 ± 0.05 |
prek-head --version |
1.7 ± 0.1 | 1.6 | 1.9 | 1.00 |
prek list
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base list |
8.3 ± 5.7 | 6.8 | 46.7 | 1.00 |
prek-head list |
9.4 ± 11.8 | 6.8 | 107.1 | 1.14 ± 1.62 |
prek list: 13.5600% slower
prek validate-config .pre-commit-config.yaml
⏭️ Skipped: .pre-commit-config.yaml not found
prek sample-config
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base sample-config |
3.9 ± 13.1 | 1.9 | 94.4 | 1.75 ± 6.05 |
prek-head sample-config |
2.2 ± 1.5 | 1.9 | 12.3 | 1.00 |
✅ Performance improvement for prek sample-config: 42.9400% faster
Cold vs Warm Runs
Comparing first run (cold) vs subsequent runs (warm cache):
prek run --all-files (cold - no cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --all-files |
25.5 ± 1.3 | 23.6 | 27.5 | 1.00 |
prek-head run --all-files |
26.0 ± 2.1 | 23.8 | 29.8 | 1.02 ± 0.10 |
prek run --all-files (warm - with cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --all-files |
24.4 ± 0.9 | 22.8 | 26.4 | 1.00 |
prek-head run --all-files |
24.8 ± 0.8 | 23.2 | 26.8 | 1.02 ± 0.05 |
Full Hook Suite
Running the builtin hook suite on the benchmark workspace:
prek run --all-files (full builtin hook suite)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --all-files |
27.0 ± 10.4 | 23.1 | 91.2 | 1.00 |
prek-head run --all-files |
29.2 ± 27.9 | 22.6 | 222.6 | 1.08 ± 1.12 |
Individual Hook Performance
Benchmarking each hook individually on the test repo:
prek run trailing-whitespace --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run trailing-whitespace --all-files |
8.8 ± 1.4 | 8.1 | 16.2 | 1.03 ± 0.17 |
prek-head run trailing-whitespace --all-files |
8.5 ± 0.3 | 8.1 | 9.1 | 1.00 |
prek run end-of-file-fixer --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run end-of-file-fixer --all-files |
7.6 ± 0.6 | 7.0 | 10.3 | 1.00 |
prek-head run end-of-file-fixer --all-files |
12.8 ± 16.3 | 7.5 | 76.0 | 1.67 ± 2.13 |
prek run end-of-file-fixer --all-files: 66.8500% slower
prek run check-json --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-json --all-files |
11.7 ± 17.4 | 5.3 | 63.9 | 1.32 ± 2.66 |
prek-head run check-json --all-files |
8.9 ± 12.0 | 5.3 | 62.0 | 1.00 |
✅ Performance improvement for prek run check-json --all-files: 24.2200% faster
prek run check-yaml --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-yaml --all-files |
8.4 ± 9.6 | 5.2 | 53.0 | 1.00 |
prek-head run check-yaml --all-files |
11.1 ± 21.1 | 5.2 | 90.9 | 1.32 ± 2.91 |
prek run check-yaml --all-files: 31.7200% slower
prek run check-toml --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-toml --all-files |
8.2 ± 9.7 | 5.3 | 49.6 | 1.00 |
prek-head run check-toml --all-files |
9.9 ± 16.9 | 5.2 | 88.5 | 1.21 ± 2.51 |
prek run check-toml --all-files: 21.1400% slower
prek run check-xml --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-xml --all-files |
7.6 ± 12.5 | 5.0 | 74.0 | 1.00 |
prek-head run check-xml --all-files |
8.8 ± 14.7 | 5.2 | 82.5 | 1.16 ± 2.70 |
prek run check-xml --all-files: 15.6100% slower
prek run detect-private-key --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run detect-private-key --all-files |
7.5 ± 0.5 | 6.7 | 9.2 | 1.00 |
prek-head run detect-private-key --all-files |
13.2 ± 25.4 | 7.0 | 145.1 | 1.76 ± 3.39 |
prek run detect-private-key --all-files: 75.5400% slower
prek run fix-byte-order-marker --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run fix-byte-order-marker --all-files |
10.0 ± 1.2 | 9.1 | 16.1 | 1.00 |
prek-head run fix-byte-order-marker --all-files |
21.8 ± 57.9 | 9.5 | 327.5 | 2.18 ± 5.79 |
prek run fix-byte-order-marker --all-files: 118.0400% slower
Installation Performance
Benchmarking hook installation (fast path hooks skip Python setup):
prek install-hooks (cold - no cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base install-hooks |
3.5 ± 0.1 | 3.4 | 3.6 | 1.01 ± 0.03 |
prek-head install-hooks |
3.5 ± 0.1 | 3.4 | 3.6 | 1.00 |
prek install-hooks (warm - with cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base install-hooks |
57.0 ± 117.4 | 3.3 | 267.0 | 15.32 ± 31.59 |
prek-head install-hooks |
3.7 ± 0.2 | 3.6 | 4.0 | 1.00 |
✅ Performance improvement for prek install-hooks (warm - with cache): 93.4800% faster
File Filtering/Scoping Performance
Testing different file selection modes:
prek run (staged files only)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run |
23.0 ± 20.8 | 15.8 | 87.2 | 1.41 ± 1.27 |
prek-head run |
16.4 ± 0.6 | 15.8 | 18.2 | 1.00 |
✅ Performance improvement for prek run (staged files only): 28.8700% faster
prek run --files '*.json' (specific file type)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --files '*.json' |
4.1 ± 0.2 | 3.8 | 4.6 | 1.00 |
prek-head run --files '*.json' |
4.5 ± 1.5 | 3.8 | 9.7 | 1.11 ± 0.37 |
prek run --files '*.json' (specific file type): 11.3900% slower
Workspace Discovery & Initialization
Benchmarking hook discovery and initialization overhead:
prek run --dry-run --all-files (measures init overhead)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --dry-run --all-files |
5.5 ± 1.9 | 4.9 | 13.7 | 1.00 |
prek-head run --dry-run --all-files |
17.8 ± 55.0 | 4.8 | 251.4 | 3.22 ± 10.01 |
prek run --dry-run --all-files (measures init overhead): 222.0200% slower
Meta Hooks Performance
Benchmarking meta hooks separately:
prek run check-hooks-apply --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-hooks-apply --all-files |
5.9 ± 0.1 | 5.7 | 6.2 | 1.00 |
prek-head run check-hooks-apply --all-files |
6.5 ± 1.9 | 5.9 | 13.6 | 1.11 ± 0.33 |
prek run check-hooks-apply --all-files: 11.2700% slower
prek run check-useless-excludes --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-useless-excludes --all-files |
11.7 ± 22.4 | 5.7 | 92.7 | 1.98 ± 3.81 |
prek-head run check-useless-excludes --all-files |
5.9 ± 0.1 | 5.7 | 6.0 | 1.00 |
✅ Performance improvement for prek run check-useless-excludes --all-files: 49.5800% faster
prek run identity --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run identity --all-files |
5.2 ± 0.1 | 5.0 | 5.4 | 1.01 ± 0.03 |
prek-head run identity --all-files |
5.1 ± 0.1 | 5.0 | 5.4 | 1.00 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (2)
crates/prek/src/terminal.rs:354
ESC[m(SGR with no parameters) is defined as a full reset (equivalent toESC[0m). ReturningNonehere drops that reset, which can leak styles into subsequent transcript output.
if !has_params {
return None;
}
crates/prek/src/terminal.rs:49
USE_COLORpanics onColorChoice::Autoviaunreachable!(). Even if this is currently expected, being defensive avoids a hard crash ifanstreambehavior changes or a platform returnsAutounexpectedly.
ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
ColorChoice::Never => false,
// We just asked anstream for a choice, that can't be auto.
ColorChoice::Auto => unreachable!(),
});
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ffb8e73863
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Closes #2550