Skip to content
Merged
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
260 changes: 61 additions & 199 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,98 +12,89 @@
<a href="https://pypi.org/project/szr/">
<img src="https://img.shields.io/pypi/v/szr?logo=pypi&logoColor=white" alt="PyPI version" />
</a>
<a href="https://github.com/devr-tools/szr/actions/workflows/cd.yml">
<img src="https://github.com/devr-tools/szr/actions/workflows/cd.yml/badge.svg?branch=main&event=push" alt="CD" />
</a>
<a href="https://github.com/devr-tools/szr/actions/workflows/ci.yml">
<img src="https://github.com/devr-tools/szr/actions/workflows/ci.yml/badge.svg" alt="CI" />
</a>
<a href="https://github.com/devr-tools/szr/actions/workflows/homebrew-validation.yml">
<img src="https://github.com/devr-tools/szr/actions/workflows/homebrew-validation.yml/badge.svg" alt="homebrew-validation" />
</a>
<a href="https://pkg.go.dev/github.com/devr-tools/szr/pkg/szr">
<img src="https://pkg.go.dev/badge/github.com/devr-tools/szr/pkg/szr.svg" alt="Go Reference" />
</a>
<a href="https://goreportcard.com/report/github.com/devr-tools/szr">
<img src="https://goreportcard.com/badge/github.com/devr-tools/szr" alt="Go Report Card" />
</a>
<a href="https://opensource.org/licenses/MIT">
<a href="https://opensource.org/licenses/Apache-2.0">
<img src="https://img.shields.io/badge/License-Apache-green.svg" alt="License: Apache 2.0" />
</a>
<a href="https://www.linkedin.com/in/alxjohn">
<img src="https://img.shields.io/badge/LinkedIn-alxjohn-blue?logo=linkedin" alt="LinkedIn" />
</a>
</p>

# szr

`szr` is a Go-native CLI proxy that reduces noisy terminal output before it reaches an LLM context.
`szr` makes noisy terminal output easier to read and cheaper to send to an AI assistant. Run the commands you already use through `szr`; it keeps errors, file locations, and exit codes while trimming the noise.

It keeps the useful signal, preserves the wrapped command's exit code, and helps you spend fewer tokens on logs, diffs, and test output.
It is useful whether you are working in a terminal yourself or asking an AI coding tool to help.

## What it does
## 1. Install szr

- wraps the commands you already run
- rewrites supported tools into more compact machine-friendly output when possible
- summarizes noisy output without hiding the important anchors
- records local history so you can inspect token savings over time
Choose the option that fits your setup, then run `szr self doctor` to check that everything is ready.

## Why szr
<details open>
<summary><strong>Homebrew</strong> — macOS or Linux</summary>

**Fidelity is a runtime guarantee, not a promise.** A built-in retention verifier checks every render against the raw output — error lines, `file:line` anchors, diagnostic codes, failing test names — and repairs anything a filter dropped by appending the missing detail. Failing commands can never render content-free, explicit flags you pass are never overridden, and exit codes always match the wrapped command.
```bash
brew install devr-tools/tap/szr
szr self doctor
```

**Compression is never worse than raw.** The finished display — retention repairs and artifact pointers included — never costs more tokens than relaying the raw output; when it would, szr emits the raw output instead. This holds for failure renders too: failure detail expansion stays within raw size. (Ultra-compact mode opts out — there the reshaped display is the point.) Profiles that read a single stream still surface the other stream's message when theirs is empty, so kubectl's `No resources found` on stderr renders as a compact line instead of a raw fallback.
</details>

**Re-runs cost almost nothing.** Agents run `git status`, `git diff`, and test suites over and over. When output is byte-identical to a recent run, szr emits a two-line reference instead (`unchanged from previous run (39s ago, x3 identical) [ref: …]`) — and `szr expand <ref>` recovers the original byte-exact, on demand. The store is machine-level, so concurrent agents share it automatically: what one agent already saw, another can reference.
<details>
<summary><strong>npm</strong> — prebuilt binary; no Go required</summary>

- **Edit-test loops render as deltas.** When a rerun's output *changed* instead of repeating, szr can emit a compact change digest — `since last run (2m ago): +3 -1 lines` plus the changed lines themselves — but only when that is strictly cheaper than the normal summary, and never at the cost of a critical line: a newly-failing test always appears in the digest. The baseline stays one `szr expand <ref>` away.
- **Agent fleets get their own scope.** Export `SZR_SESSION=<id>` before launching parallel agents and the whole fleet shares one dedup/delta scope: what one agent rendered, its siblings reference. Runs without the variable stay in the machine scope and never cross-match scoped sessions; `szr expand` resolves refs from any scope.
```bash
npm install -g @devr-tools/szr
szr self doctor
```

</details>

**Anomalies are the payload.** List and table summaries always keep the rows that differ — the one stopped instance among 800 running, the `past_due` row in a result set — plus exact counts. Uniform JSON arrays render as a compact table that halves the token cost of list-shaped API responses at equal information.
<details>
<summary><strong>pip</strong> — prebuilt binary; no Go required</summary>

**Everything is recoverable.** Whatever a summary omits is preserved in a local artifact with an exact pointer — compression never costs you the ability to look at the full output.
```bash
pip install szr
szr self doctor
```

## Install
</details>

Install the CLI with Go:
<details>
<summary><strong>Go</strong> — build from source</summary>

```bash
go install github.com/devr-tools/szr/cmd/szr@latest
szr self doctor
```

Or build from a local checkout:
</details>

<details>
<summary><strong>Local checkout</strong> — for contributors</summary>

```bash
make build
./bin/szr self install
szr self doctor
```

Homebrew install via tap:
</details>

```bash
brew install devr-tools/tap/szr
szr self doctor
```
## 2. Connect szr to your agent

npm (installs a prebuilt binary, no Go toolchain required):
Choose the coding agent you use. This lets it use szr while it works:

```bash
npm install -g @devr-tools/szr
szr self doctor
# Choose the assistant you use:
szr install codex
```

pip (installs a prebuilt binary per platform):
Other options: `szr install claude-code`, `szr install cursor`, or `szr install gemini`. Use `szr uninstall <tool>` to remove an integration. The [user guide](docs/USER_GUIDE.md#ai-tool-integrations) explains what each setup changes.

```bash
pip install szr
szr self doctor
```
## 3. Use szr

## Main commands

Run your normal commands through `szr`:
Your agent can now use szr when it runs terminal commands. If you work directly in a terminal, add `szr` before a command yourself:

```bash
szr git status
Expand All @@ -112,163 +103,34 @@ szr go test ./...
szr find . --name "*.py"
```

Transparent wrapper prefixes — `env KEY=VAL`, `env -u NAME`, `command`, `nice [-n N]`, bare `time`, and leading `VAR=value` assignments — route to the inner command's profile, so `szr env -u GOROOT go test ./...` compresses like `szr go test ./...`. Prefixes stack, work inside `sh -c "..."` strings, and the wrapper words themselves always execute verbatim. A bare `env` with no wrapped command is a command of its own (the `env-print` profile compacts its dump and redacts secret-looking values).

## AI tool support

AI bootstrap targets:

| Tool | Install command | Uninstall command | Description |
| --- | --- | --- | --- |
| Codex | `szr install codex` | `szr uninstall codex` | Writes `~/.codex/szr.md` (or `$CODEX_HOME/szr.md`) and patches the repo `AGENTS.md` to reference it. |
| Claude Code | `szr install claude-code` | `szr uninstall claude-code` | Installs `~/.claude/szr.md`, a Claude hook script, and a `settings.json` hook registration. |
| Cursor | `szr install cursor` | `szr uninstall cursor` | Installs `~/.cursor/hooks.json` plus a `preToolUse` hook script under `~/.cursor/hooks/`. |
| Gemini | `szr install gemini` | `szr uninstall gemini` | Installs `~/.gemini/settings.json` BeforeTool registration plus a hook script under `~/.gemini/hooks/`. |
Whether you or an assistant runs it, szr keeps the command's normal success or failure result. It only makes the output easier to scan; if it cannot safely shorten something, it shows the original output.

## Integration surface
## 4. See what you saved

External agents, hooks, and plugins can call `szr rewrite --json` to reuse the same shell-routing policy that powers the built-in Claude, Cursor, and Gemini integrations.

Example:
After using szr, check how much output and how many tokens it has saved:

```bash
szr rewrite --json --command 'git diff HEAD~1..HEAD --stat | tail -30'
```

Example response:

```json
{
"command": "git diff HEAD~1..HEAD --stat | tail -30",
"rewrite": "szr proxy git diff HEAD~1..HEAD --stat | tail -30",
"hint": "szr git diff ... --stat or szr proxy git diff ... -- path/to/file | head -200",
"reason": "wrap noisy producer inside shell pipeline",
"auto_rewrite": true,
"wrap_mode": "proxy",
"producer_only": true,
"already_routed": false
}
szr spread
szr usage
```

Use this surface when you want to:

- apply the same routing logic from custom Codex tooling or future plugins
- distinguish between safe auto-rewrites and hint-only guidance
- avoid re-encoding `git diff`, `grep`, `find`, and pipeline policy in multiple places

## User-defined filters

Drop a JSON spec into the `filters/` directory next to `config.json` and szr loads it as an extra profile — a `match` section routes commands, and the same declarative reducer keys the builtins use (`keep_patterns`, `strip_patterns`, `head`, `tail`, `dedup_consecutive`, …) shape the output. Project-local specs in `.szr/filters/` load too, once `advanced.project_filters` is enabled via `szr settings` (off by default). Name collisions with project rules, builtins, or earlier user filters are skipped with a warning, never silently shadowed. `szr profiles` marks loaded specs with `source: user` or `source: project`.

See [docs/FILTERS.md](docs/FILTERS.md) for the spec format and a worked example.

## Savings insight

`szr spread` summarizes recorded savings; three additions make the numbers actionable:

- **`szr spread --cost`** appends an estimated-cost section: dollars avoided at a USD-per-million-input-tokens rate, raw versus emitted output cost, and a `≈ N× a 200k-token context` anchor. The rate resolves from `--rate <usd-per-mtok>` first, then the `cost_rate_per_mtok` config key, then the default `3.00`; passing `--rate` implies `--cost`. With `--json`, the figures appear as a `cost` object. `szr gain` accepts the same flags.
- **`szr discover`** scans local AI-agent session transcripts (`~/.claude/projects/`) for shell commands that ran *without* szr, routes each through szr's own profile matching, and estimates the missed token savings using your own per-profile history ratios (60% fallback for profiles without enough history). Read-only and local-only: transcripts are never modified or transmitted. By default it covers the current project's transcripts from the last 30 days; `--all` scans every project, `--since <days>` widens or narrows the window, `--top <n>` sizes the command table (default 15), and `--json` emits the full report.
- **`szr usage`** joins the two sides: per agent session it reports the model-billed tokens recorded in the transcripts (fresh input, cache reads, output — exact, as recorded by the agent runtime) next to szr's estimated emitted and avoided tokens for that session, including szr's share of fresh input and how much larger fresh input would have been without szr, plus a per-subagent breakdown (an `agents` column, and a per-agent table when `--session` matches one session). Records are correlated by session scope when present (`SZR_SESSION`), otherwise by directory and session time window; cache reads are always excluded from the derived percentages. On a terminal, an interactive picker lets you drill into any listed session's agents (`--no-input` disables it). Flags: `--all`, `--since <days>` (default 7), `--session <id-prefix>`, `--json`, `--no-input`.
- **`szr watch --jsonl`** streams local, sanitized execution events for agent or IDE integrations. Events contain reducer and token/latency measurements only—never commands, paths, rendered output, tee artifacts, or transcript content. Add `--once` for a snapshot instead of following the stream. See [the integration guide](docs/INTEGRATIONS.md) for the stable event contract and a minimal live-results consumer.
- **Optional diagnostics export** can send the same allowlisted event schema to an HTTPS gateway. It is disabled by default; when enabled, szr queues events in a bounded, owner-only local outbox, retries them in background batches, and makes one final bounded delivery attempt on command exit. Export failures never change the wrapped command's result. Configure an explicit endpoint in `config.json`:

```json
{
"diagnostics": {
"enabled": true,
"endpoint": "https://gateway.example/v1/events",
"max_outbox_mb": 8
}
}
```
`szr spread` shows savings from terminal output. `szr usage` compares that with the token usage recorded by supported local AI-agent sessions.

Gateway requests are JSON objects with `version` and an `events` array. Events never include command text, paths, output, tee artifacts, or transcript content. A gateway may emit a `provider_usage_aggregate` with only opaque gateway correlation and aggregate szr/provider token counters; it must not contain agent/provider session IDs, prompts, transcripts, model names, or account identifiers.
- **`szr diagnostics status [--json]`** inspects local event and pending-export storage without making a network request, including exporter endpoint host, drops, last success/failure, and next retry. **`szr diagnostics flush`** is an explicit, five-second-bounded export attempt; it is never run on a wrapped-command path. **`szr diagnostics purge --yes`** explicitly removes local event, outbox, and exporter-status stores.

## Upcoming features

- stdin pipe mode for filtering output already in flight
- broader reducer coverage and better fallback handling for noisy real-world commands

## Command reference
## Main commands

| Command | Description |
| Command | What it does |
| --- | --- |
| `szr git status` | Run common Git commands through `szr` with reduced output. |
| `szr go test ./...` | Compress noisy test output while preserving failures and anchors. |
| `szr find <path> --name "*.py"` | Find files or directories with repo-noise suppression and a bounded match summary. |
| `szr grep <pattern> <path>` | Group search matches by file via ripgrep-backed summaries with conservative repo-noise excludes. |
| `szr run /usr/bin/grep ...` | Preserve exact grep semantics while still routing output through `szr`. |
| `szr rewrite --json --command '<cmd>'` | Return the shared shell-routing decision for external agents and integrations. |
| `szr spread` | Show token savings, usage patterns, hotspot summaries, and per-profile fallback and empty-result rates. |
| `szr spread --history` | Inspect savings history across recent commands. |
| `szr spread --cost [--rate <usd-per-mtok>]` | Append estimated dollar figures and a 200k-context anchor to the savings summary. |
| `szr discover [--all\|--since <n>\|--top <n>\|--json]` | Scan local agent transcripts read-only for commands that ran without szr and estimate the missed savings. |
| `szr usage [--all\|--since <n>\|--session <id>\|--json\|--no-input]` | Compare model-billed tokens per agent session and subagent against szr-side emitted and avoided estimates. |
| `szr watch --jsonl [--once]` | Stream sanitized local execution diagnostics as JSON Lines. |
| `szr diagnostics status [--json]` | Inspect local diagnostics event and outbox health without network access. |
| `szr diagnostics flush` | Explicitly attempt one bounded upload of the diagnostics outbox. |
| `szr diagnostics purge --yes` | Explicitly remove local diagnostics events and pending exports. |
| `szr gateway hints-refresh` | Fetch, verify, and atomically install signed gateway budget hints. |
| `szr doctor [--json]` | Check runtime diagnostics and local history health. |
| `szr self doctor [--json] [--refresh]` | Check install state, `PATH`, config, cache, and version details; `--refresh` bypasses the release-check cache for a live lookup. |
| `szr settings` | Open the interactive settings menu for update checks, auto update, and other local preferences. |
| `szr expand <ref>` | Recover the byte-exact original output behind a dedup or delta baseline reference. |
| `SZR_SESSION=<id> szr <cmd...>` | Scope dedup and delta references to one agent session; export it fleet-wide so parallel agents share a scope. |
| `szr tee --latest` | Inspect the latest preserved full-output artifact. |
| `szr explain go test ./...` | Show the matched profile, budget, and rewrite decisions for a command. |
| `szr commands` | Show the full command catalog for power users and agents. |
| `szr profiles` | List built-in and user-defined reducer profiles. |

Reasoning budget modes:

- `standard`: balanced for human readability
- `agent`: tighter defaults for agent loops
- `aggressive`: smallest previews for spread-heavy workflows, including terser `git diff` summaries

## Update notices

- Interactive shells: `szr` can print update notices on `stderr` when update checks are enabled.
- Agent or non-interactive tool runs: inline update notices are suppressed to keep tool output stable.
- Hosts can poll `szr doctor --json` or `szr self doctor --json` and render their own user-facing notification with the returned `update` object; add `--refresh` to force a live release lookup instead of the cached one.
- Opt-in auto update is available for recognized Homebrew or `go install` installs:

```json
{
"update_check": {
"enabled": true,
"interval_hours": 24,
"auto_update": true
}
}
```

## Tee artifact retention

Full-output captures are tee'd into the local data directory so compressed renders stay recoverable. Retention is bounded by default:

- a single artifact is capped at 4 MiB; a larger capture keeps the head and tail of the stream around a truncation marker that records how many bytes were omitted
- the tee directory is pruned oldest-first past 200 files or 256 MiB total; the newest artifact always survives, and pruned files are dropped from the tee index so lookups never dangle
- orphaned in-progress captures are cleaned up after one hour

All three caps are adjustable via `szr settings` (`tee max file mb`, `tee max dir files`, `tee max dir mb`) or in the config file:

```json
{
"tee_max_file_mb": 4,
"tee_max_dir_files": 200,
"tee_max_dir_mb": 256
}
```

Zero or negative values fall back to the defaults rather than meaning "unlimited".

## More docs

| `szr <command>` | Run a command with cleaner, more compact output. |
| `szr spread` | See the token savings from recent runs. |
| `szr explain <command>` | See how szr would handle a command. |
| `szr expand <ref>` | Recover the complete original output from a saved reference. |
| `szr self doctor` | Check your installation, configuration, and updates. |
| `szr settings` | Open interactive local settings. |

## Learn more

- [User guide](docs/USER_GUIDE.md) — advanced commands, integrations, history, diagnostics, and updates
- [Profiles](docs/PROFILES.MD) — supported command families
- [User-defined filters](docs/FILTERS.md) — add your own command reducers
- [Contributing](CONTRIBUTING.md)
- [Local CI](docs/LOCAL_CI.md)
- [Releasing](docs/RELEASING.md)
- [Go package](docs/GO_PACKAGE.md)
- [Architecture](docs/ARCHITECTURE.md)
- [Profiles](docs/PROFILES.MD)
- [User-defined filters](docs/FILTERS.md)
Loading
Loading