Debugging / feedback capture
atty can record the recent window of terminal I/O in memory and, on a keystroke, dump it to a self-contained report — so a ghost-text, LLM, or render-artifact bug (atty mangling the shell’s output) can be captured the moment it happens and reproduced later.
Enable it
Off by default — recording keystrokes and output is sensitive, so it’s opt-in.
In src/config.zig:
pub const debug: atty.Debug = .{
.enabled = true,
.ring_bytes = 256 * 1024, // one half of the in-memory double buffer
.report_dir = "", // "" → $XDG_DATA_HOME/atty/reports (or ~/.local/share/…)
};
Recompile (make build) and run atty as usual.
Capture
When something looks wrong, press Alt+Shift+D. atty writes a JSON report
to the report directory and prints the path:
[atty debug] report saved: /home/you/.local/share/atty/reports/report-1751000000-123456789.json
Nothing is written to disk until you press it — the recorder is a bounded in-memory ring, so there is no passive log sitting around to leak.
What’s in a report
Three timestamped byte streams for the recent window (1–2× ring_bytes):
| stream | what it is |
|---|---|
in |
keystrokes atty received (stdin) |
shell |
the raw bytes the shell produced (atty’s input) |
term |
atty’s final bytes to the terminal — including its own status bar / ghost / overlay injections |
Plus context: atty version, terminal size, TERM / SHELL / LANG, the
current line-editing state, and whether incognito was on.
Having shell and term side by side is the point: you can see exactly what
atty did to the shell’s output — where an overlay was injected, where a render
fragment or stray escape crept in — which is what makes debugging atty itself
(not just modules) possible.
Privacy
The streams contain your commands and their output. Reports stay local (atty never uploads anything). Review a report before sharing it.
- Incognito sessions are excluded from recording.
- Password entry (sudo / ssh / passwd /
read -s— any time the child turns echo off) is excluded from theinstream, so typed passwords are never recorded. - Per-command
subprocess.incognito_targets(a history-persistence exclusion) is not applied to this ephemeral debug window — review a report before sharing if you rely on it. Phase 3 (anonymization) will scrub such material.
Limitations
- The
termstream captures atty’s writes through its main output path. A few one-time control sequences (kitty-keyboard / mouse enable at startup) and some module-direct writes bypass that path and aren’t teed.
Replay
Re-see a captured session (or bug) from a report:
atty debug replay report-1751000000-123456789.json
By default it replays the term stream — exactly the bytes atty wrote to the
terminal, with the recorded timing — so a render artifact reappears the way it
happened. Options:
| flag | effect |
|---|---|
--stream <name> |
replay in, shell, or term (default term) |
--fast |
skip the inter-event delays and dump instantly |
--info |
print a summary (version, size, per-stream counts) instead |
Replaying --stream shell shows what the shell produced without atty, so
comparing it against the default term replay reveals atty’s transformation.
The replay is byte-exact: the report’s \u00XX-encoded stream data decodes back
to the original raw bytes.
Anonymize + share
Before handing a report to anyone, scrub it:
atty debug anonymize report-….json > report.clean.json
The scrubber (pure, regex-free) applies:
| kind | rule |
|---|---|
$HOME |
your home path → ~ |
$USER |
your username → USER |
$HOSTNAME |
your hostname → HOST |
| emails | a@b.tld → [EMAIL] |
| IPv4 | d.d.d.d → [IP] |
| tokens / JWTs | eyJ… and long base64/hex runs (≥20 chars) → [REDACTED] |
The result is still a valid report you can atty debug replay. This is what
closes the per-command subprocess.incognito_targets gap for the ephemeral
debug window — anonymize covers the sharing path even for material the recorder
itself doesn’t exclude. Still review the output before sharing — the
scrubber is best-effort, not a guarantee.
To share a visual recording instead, export a scrubbed asciinema cast of a stream:
atty debug to-cast report-….json > bug.cast # the `term` stream
atty debug to-cast report-….json --stream shell # or another stream
bug.cast plays in any asciinema player (or renders to a GIF with agg).
Roadmap
- Convert a report into a runnable regression test — replay it through atty’s output pipeline offline and diff the resulting screen. Needs a production VT model (the same one a grid-diff replay would use); not yet built.