Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions docs/guide/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,16 @@ max_width = 120 # maximum output width
ignore_dirs = [".git", "node_modules", "target", "__pycache__", ".venv", "vendor"]
ignore_files = ["*.lock", "*.min.js", "*.min.css"]

[tee]
enabled = true # save raw output on failure
mode = "failures" # "failures" (default), "always", "never"
max_files = 20 # rotation: keep last N files
# directory = "/custom/tee/path" # optional override
[retriever]
mode = "sqlite" # sqlite (default) | tee (legacy files) | disabled
max_entry_bytes = 10485760 # sqlite: 10 MiB per entry
max_entries = 200 # sqlite: FIFO cap
retention_days = 30 # sqlite: 0 disables age eviction
compression = true # sqlite: gzip blobs (lossless)
# database_path = "/custom/recall.db"
tee_max_files = 20 # tee mode: rotation
tee_max_file_size = 1048576 # tee mode: per-file cap
# tee_directory = "/custom/tee/dir"

[telemetry]
enabled = true # anonymous daily ping — see Telemetry & Privacy for full details
Expand All @@ -58,28 +63,30 @@ For full details on what is collected, opt-out options, and GDPR rights, see [Te
| Variable | Description |
|----------|-------------|
| `RTK_DISABLED=1` | Disable RTK for a single command (`RTK_DISABLED=1 git status`) |
| `RTK_TEE_DIR` | Override the tee directory |
| `RTK_RECALL=0` | Disable the recall store for a single command |
| `RTK_RECALL_DB` | Override the recall database path |
| `RTK_TELEMETRY_DISABLED=1` | Disable telemetry |
| `RTK_HOOK_AUDIT=1` | Enable hook audit logging |
| `SKIP_ENV_VALIDATION=1` | Skip env validation (useful with Next.js) |

## Tee system
## Recall system

When a command fails, RTK saves the full raw output to a local file and prints the path:
When a command fails — or a filter trims a long list — RTK persists the full output to an embedded database and prints a recall hint:

```
FAILED: 2/15 tests
[full output: ~/.local/share/rtk/tee/1707753600_cargo_test.log]
[full output: rtk recall 36365b69eda6]
```

Your AI assistant can then read the file if it needs more detail, without re-running the command.
Your AI assistant runs `rtk recall <hash>` to get back exactly what was elided (or just the hidden tail of a trimmed list), without re-running the command. Other forms: `rtk recall <hash> --full | --from N | --lines N | --grep PAT`, `rtk recall --command "cargo test"`, and `rtk recall --list`. Storage is byte-faithful (`BLOB` + lossless gzip).

| Setting | Default | Description |
|---------|---------|-------------|
| `tee.enabled` | `true` | Enable/disable |
| `tee.mode` | `"failures"` | `"failures"`, `"always"`, `"never"` |
| `tee.max_files` | `20` | Rotation: keep last N files |
| Min size | 500 bytes | Outputs shorter than this are not saved |
| `retriever.mode` | `"sqlite"` | `sqlite` (default), `tee` (legacy files), `disabled` |
| `retriever.max_entry_bytes` | `10485760` | Per-entry storage cap (10 MiB) |
| `retriever.max_entries` | `200` | FIFO cap on retained entries |
| `retriever.retention_days` | `30` | Age eviction in days (0 = off) |
| `retriever.compression` | `true` | gzip stored blobs (lossless) |
| Max file size | 1 MB | Truncated above this |

## Excluding commands from auto-rewrite
Expand Down
2 changes: 1 addition & 1 deletion src/cmds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ When a filter caps a list at N items (e.g. `take(20)`), the remaining items must

**Cap values come from `src/core/truncate.rs`.** Pick the `CAP_*` matching your data class (`CAP_ERRORS`, `CAP_WARNINGS`, `CAP_LIST`, `CAP_INVENTORY`) and bind it to a local `const MAX_XXX: usize = CAP_Y;`. Derive `take(MAX_XXX)`, `> MAX_XXX`, and the offset `MAX_XXX + 1` from the local. These CAPs will later become the configuration surface for per-filter cap tuning (user-overridable via config) — keep all truncation values routed through them so that hook lands as a single switch rather than a codebase-wide hunt. A filter that genuinely needs to deviate uses **`truncate::reduced(CAP_Y, n)`** (e.g. `reduced(CAP_WARNINGS, 5)`) so it still tracks the global when reconfigured — never a bare literal, never `cap - n` (underflows once caps are runtime-configurable), and never `*`/`/` (those scale unboundedly). `reduced` falls back to the full cap if the reduction would empty the list. Each deviation needs a one-line comment stating why; if there's no real reason, just use the plain CAP. See `src/core/README.md` ("Truncation Caps") for the full rationale.

**The tee content must match what `tail` produces.** For `force_tee_tail_hint`, build the tee from the same formatted values shown in the output — not raw/intermediate data. If the filter reformats items before displaying them, pre-build a `Vec<String>` of formatted lines and use it for both the display loop and the tee.
**The stored content must match what was shown.** For `force_tee_tail_hint`, build the recall blob from the same formatted values displayed — not raw/intermediate data — and pass the 1-based first-hidden line as `offset`. `rtk recall <hash>` slices the stored content from that offset, so the agent gets exactly the hidden items. If the filter reformats items before displaying them, pre-build a `Vec<String>` of formatted lines and use it for both the display loop and the recall blob.

### Stderr Handling

Expand Down
24 changes: 14 additions & 10 deletions src/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,16 @@ colors = true
emoji = true
max_width = 120

[tee]
enabled = true
mode = "failures" # failures | always | never
max_files = 20
max_file_size = 1048576
directory = "/custom/tee/dir"
[retriever]
mode = "sqlite" # sqlite (default) | tee (legacy files) | disabled
max_entry_bytes = 10485760 # sqlite: 10 MiB per entry
max_entries = 200 # sqlite: FIFO cap
retention_days = 30 # sqlite: 0 disables age eviction
compression = true # sqlite: gzip blobs (lossless)
# database_path = "/custom/recall.db"
tee_max_files = 20 # tee mode: rotation
tee_max_file_size = 1048576 # tee mode: per-file cap
# tee_directory = "/custom/tee/dir"

[telemetry]
enabled = true
Expand Down Expand Up @@ -115,13 +119,13 @@ Core provides infrastructure that `cmds/` and other components consume. These co

Consumers must call `timer.track()` on **all** code paths — success, failure, and fallback. Calling `std::process::exit()` before `track()` loses metrics. The raw string passed to `track()` should include both stdout and stderr to produce accurate savings percentages.

### Tee (`tee_and_hint`)
### Output recovery (`tee_and_hint` + recall store)

Consumers that parse structured output (JSON, NDJSON, state machines) should call `tee::tee_and_hint()` to save raw output for LLM recovery on failure. Tee must be called before `std::process::exit()`.
Consumers that parse structured output (JSON, NDJSON, state machines) should call `tee::tee_and_hint()` to persist raw output for LLM recovery on failure. It must be called before `std::process::exit()`.

For truncation recovery on **success** (e.g., list truncated at 20 items), use `tee::force_tee_hint()` which bypasses the tee mode check and writes regardless of exit code. This ensures LLMs always have a `[full output: ...]` recovery path instead of burning tokens working around missing data.
For truncation recovery on **success** (e.g. a list capped at 20 items), use `tee::force_tee_hint()` (multi-line blocks) or `tee::force_tee_tail_hint(content, slug, offset)` (flat lists). All three persist the full output to the content-addressed recall store ([`retriever.rs`](retriever.rs)) and emit a runnable hint — `[full output: rtk recall <hash>]` or `[+N hidden: rtk recall <hash>]` — instead of burning tokens working around missing data.

When the truncated output is a **flat list** and the hidden items start at a predictable line, prefer `tee::force_tee_tail_hint(content, slug, offset)`. It writes the same tee file but emits a directly runnable hint — `[see remaining: tail -n +{offset} ~/path]` — so the agent jumps to exactly the first hidden item without scanning the whole file. The offset is `header_lines + MAX_CAP + 1`. Use `force_tee_hint` instead when the output has multiple sections (e.g. running + stopped containers) and no single offset cleanly covers the gap.
The agent runs `rtk recall <hash>` to get back exactly what was elided. For `force_tee_tail_hint`, `offset` is the 1-based first hidden line (`header_lines + MAX_CAP + 1`); it is stored so the default recall returns only the hidden tail. Storage is byte-faithful (`BLOB` + lossless gzip); tune limits via the `[retriever]` config section.

### Truncation Caps (`truncate`)

Expand Down
2 changes: 1 addition & 1 deletion src/core/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct Config {
#[serde(default)]
pub filters: FilterConfig,
#[serde(default)]
pub tee: crate::core::tee::TeeConfig,
pub retriever: crate::core::retriever::RetrieverConfig,
#[serde(default)]
pub telemetry: TelemetryConfig,
#[serde(default)]
Expand Down
1 change: 1 addition & 0 deletions src/core/constants.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub const RTK_DATA_DIR: &str = "rtk";
pub const HISTORY_DB: &str = "history.db";
pub const RECALL_DB: &str = "recall.db";
pub const CONFIG_TOML: &str = "config.toml";
pub const FILTERS_TOML: &str = "filters.toml";
pub const TRUSTED_FILTERS_JSON: &str = "trusted_filters.json";
Expand Down
2 changes: 2 additions & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ pub mod constants;
pub mod display_helpers;
pub mod filter;
pub mod guard;
pub mod retriever;
pub mod runner;
pub mod stream;
pub mod tee;
pub mod tee_file;
pub mod telemetry;
pub mod telemetry_cmd;
pub mod toml_filter;
Expand Down
Loading