How RTK's Tee System Facilitates Failure Recovery and Full-Output Retrieval

RTK's tee system captures raw, unfiltered command output to timestamped log files when failures occur or filters truncate data, then surfaces a retrievable file path hint that enables LLMs and users to access the complete original output for debugging.

The rtk-ai/rtk repository implements a sophisticated tee subsystem that solves the critical problem of lost diagnostic information in AI-assisted command workflows. When token-saving filters compress or truncate command output, this system automatically persists full execution logs to disk, ensuring that no essential debugging data disappears during the filtering pipeline.

Configuration and Operating Modes

The tee behavior is governed by TeeConfig in src/core/config.rs (lines 41‑50), which exposes several tunable parameters:

  • enabled – Global boolean switch that activates the entire subsystem.
  • mode – Enum controlling when files are written: failures (only on non‑zero exit codes), always (every execution), or never (disabled entirely). Defaults to failures.
  • max_files – Rotation limit keeping only the 20 most recent logs per directory.
  • max_file_size – 1,048,576 byte cap per file; larger output truncates safely at UTF‑8 boundaries.
  • directory – Optional custom path; otherwise defaults to ~/.local/share/rtk/tee/.

The TeeMode enum itself is defined in src/core/tee.rs (lines 31‑39), providing the type safety for these configuration options.

Decision Logic: When to Capture Output

The core decision function should_tee in src/core/tee.rs (lines 84‑100) applies a series of guards before writing any data:

  1. Global enable check – Returns immediately if config.enabled is false.
  2. Mode evaluation – Never aborts; Failures requires exit_code != 0; Always proceeds unconditionally.
  3. Minimum size threshold – Outputs smaller than 500 bytes (MIN_TEE_SIZE) are ignored to prevent clutter.
  4. Directory resolution – Confirms a valid tee directory exists via get_tee_dir, which checks the RTK_TEE_DIR environment variable, the config field, or falls back to the platform data directory.
fn should_tee(
    config: &TeeConfig,
    raw_len: usize,
    exit_code: i32,
    tee_dir: Option<PathBuf>,
) -> Option<PathBuf> {
    if !config.enabled { return None; }

    match config.mode {
        TeeMode::Never => return None,
        TeeMode::Failures => { if exit_code == 0 { return None; } }
        TeeMode::Always => {}
    }

    if raw_len < MIN_TEE_SIZE { return None; }

    tee_dir
}

File Writing and Rotation Mechanisms

Once should_tee approves the operation, write_tee_file in src/core/tee.rs (lines 112‑155) handles the persistence:

  • Safe filename construction – Generates {epoch}_{command_slug}.log where non‑alphanumeric characters in the slug convert to underscores and truncate to 40 characters.
  • UTF‑8 safe truncation – Uses char_indices to split only at valid character boundaries when output exceeds max_file_size, appending a --- truncated ... --- notice.
  • Automatic rotation – cleanup_old_files sorts existing logs by epoch prefix and deletes the oldest entries when the count exceeds max_files.

The system creates the directory tree recursively if missing, ensuring robust operation in fresh environments.

User Hints and Recovery Workflow

The public entry point tee_and_hint in src/core/tee.rs (lines 188‑191) orchestrates the capture and user notification:

  1. Checks for the RTK_TEE=0 environment override.
  2. Loads configuration and resolves the tee directory.
  3. Executes the should_tee guard logic.
  4. Writes the file and returns a formatted hint string.
pub fn tee_and_hint(raw: &str, command_slug: &str, exit_code: i32) -> Option<String> {
    // Environment override & config loading...
    let tee_dir = should_tee(&config.tee, raw.len(), exit_code, Some(tee_dir))?;
    write_tee_file(...).map(format_hint)
}

When RTK prints [full output: ~/rtk/tee/1701234567_cargo_test.log], users or LLMs can read that file to obtain the unfiltered original, bypassing any compression or truncation that occurred in the main output stream.

Forced Tees on Filter Truncation

Some filters (such as the AWS output processor) may truncate content while still returning a successful exit code. The force_tee_hint function in src/core/tee.rs (lines 199‑213) bypasses the mode check and always writes the file when FilterResult.truncated is true, provided the output meets the minimum size threshold. This ensures that large payloads intentionally clipped by filters remain recoverable even when the command technically succeeded.

Integration Points

The tee system hooks into RTK's execution flow at three critical locations:

Command Runner (src/main.rs, lines 66‑78): After a subprocess completes, if !output.status.success(), the system calls tee_and_hint with the merged stdout and stderr, then prints the hint after the filtered output.

Filtered Runners (src/core/runner.rs, lines 9‑12): When filtered commands return successfully but the filter forces a tee, print_with_hint appends the path to the filtered output.

Streamed Mode (src/core/runner.rs, lines 25‑31): Even during real‑time streaming, the system checks opts.tee_label and prints the hint after capture completes, ensuring streamed failures also leave a recoverable log.

Environment Overrides

Two environment variables provide runtime control without editing configuration files:

  • RTK_TEE=0 – Disables teeing entirely for the current process, checked at the entry points of tee_raw, tee_and_hint, and force_tee_hint.
  • RTK_TEE_DIR=/custom/path – Overrides the storage location, useful in CI/CD pipelines where the default ~/.local/share/rtk/tee/ may not be writable or persistent.

Example usage:

export RTK_TEE_DIR=/tmp/rtk_logs
rtk cargo test   # Failure logs write to /tmp/rtk_logs

Summary

  • RTK's tee system automatically persists raw command output to log files when configured conditions are met.
  • The should_tee function in src/core/tee.rs gates writes based on mode, exit code, and minimum size thresholds.
  • Files are safely truncated at UTF‑8 boundaries and automatically rotated to respect max_files limits.
  • User hints provide direct paths to full output, enabling LLMs to reference complete logs even after aggressive filtering.
  • Environment variables RTK_TEE and RTK_TEE_DIR offer flexible runtime control for CI pipelines and debugging sessions.

Frequently Asked Questions

Where does RTK store tee log files by default?

By default, RTK writes tee logs to ~/.local/share/rtk/tee/ on Linux and macOS, or the equivalent platform data directory on other systems. You can customize this path via the directory field in TeeConfig or by setting the RTK_TEE_DIR environment variable to an absolute path.

How can I force RTK to save output even when a command succeeds?

Set the tee mode to always in your configuration, or use the force_tee_hint function programmatically when implementing custom filters. The always mode bypasses the exit code check in should_tee, writing a log file for every execution that meets the minimum size requirement.

What happens when tee output exceeds the maximum file size?

When output exceeds the max_file_size limit (default 1,048,576 bytes), RTK truncates the content at the nearest valid UTF‑8 character boundary using char_indices and appends a --- truncated ... --- marker. This prevents file corruption while still preserving the initial actionable portion of large outputs.

Can I completely disable the tee system for specific commands?

Yes. Set the environment variable RTK_TEE=0 before running RTK, or configure enabled: false in the TeeConfig section of your settings file. The environment variable is checked early in the tee_and_hint execution path, ensuring zero overhead for that process.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →