```
██████ ██████
███▒▒███ ███▒▒███
▒███ ▒███▒███ ▒███
▒███ ▒███▒███ ▒███
▒▒██████ ▒▒██████
▒▒▒▒▒▒ ▒▒▒▒▒▒
```
<br /><br />
[](https://github.com/randomm/oo/actions/workflows/ci.yml)
[](https://crates.io/crates/double-o)
[](https://docs.rs/double-o)
[](https://opensource.org/licenses/Apache-2.0)
[](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html)
*"Double-o, agent's best friend."*
*Or: how I learned to stop worrying and love my context-efficient command runner for AI coding agents.*
---
## Why oo?
AI coding agents waste context tokens on verbose command output. A single `cargo build`
can produce thousands of lines that the agent must process but rarely needs in full.
**oo solves this transparently:**
- Commands run normally — oo wraps them, not replaces them
- Output is classified and compressed using patterns
- Agents get the signal (pass/fail, errors, summaries) without the noise
- No agent modification required — just prefix commands with `oo`
Unlike manual truncation (`head`/`tail`), oo understands command semantics. Unlike
agent-native output limits, oo preserves the information the agent actually needs.
---
## The Problem
AI agents see everything you print. A `cargo test` run producing 8 KB of output
costs the agent hundreds of tokens just to learn "tests passed." Multiply that
across a session and the context window fills with noise, not signal. `oo` runs
commands for you and collapses their output to what the agent actually needs.
---
## Output Tiers
**Without oo:** Your agent receives the full output.
```
$ cargo test
Compiling myapp v0.1.0 (/path/to/myapp)
Finished test [unoptimized + debuginfo] target(s) in 0.52s
Running unittests src/lib.rs (target/debug/deps/myapp)
running 47 tests
test auth::tests::login_success ... ok
test auth::tests::login_invalid_password ... ok
test db::tests::connection_pool ... ok
... 44 more tests ...
test result: ok. 47 passed; 0 failed; finished in 2.1s
```
**With oo:** Your agent gets the signal.
```
$ oo cargo test
✓ cargo test (47 passed, 2.1s)
```
Large output with a known success pattern collapses to a single summary line.
**When things fail:** Actionable errors, no noise.
```
$ oo pytest tests/
✗ pytest
FAILED tests/test_api.py::test_login - AssertionError: expected 200, got 401
FAILED tests/test_api.py::test_create_user - ValueError: email already exists
=== 2 failed, 45 passed in 1.8s ===
```
Failure output is filtered to the actionable tail. When the displayed output does not match the full output of the failed run line for line (ignoring trailing empty lines), that full output is indexed too, so `oo recall` can find what the display left out.
**Large unrecognised output:** Indexed for retrieval.
```
$ oo gh issue list
● gh (indexed 47.2 KiB → use `oo recall` to query)
```
Query indexed output with `oo recall "<terms>"`. Small successful outputs (≤ 4 KiB) pass through
unchanged; failed commands are always filtered (see above). Larger unpatterned Data, Content, and Unknown output is indexed in full; successful Status output (test runners, builds, linters) over 4 KiB with no pattern summary gets a quiet `✓` line and is not indexed. Data commands like
`git log` and `ls` show only an indicator line; content commands like `git show` and `git diff`
and unknown commands like `jq` or `sh -c` show a byte-bounded head+tail slice with a truncation
marker (`... [N bytes truncated → use `oo recall` to query] ...`), so large `cat`, `jq`, or
`sh -c` output cannot blow an agent's context window. See the [patterns guide](docs/patterns.md#command-categories)
for details.
---
## Installation
### Quick install (macOS and Linux)
```bash
> The installer detects your platform, downloads the correct binary, and adds it to `~/.cargo/bin/` by default. If you don't use Rust, restart your shell or run `export PATH="$HOME/.cargo/bin:$PATH"` to make `oo` available. Use `--prefix=/usr/local` for system-wide installs.
### Pre-built binaries (recommended)
Download from [GitHub Releases](https://github.com/randomm/oo/releases/latest):
```bash
# macOS (Apple Silicon)
curl -LO https://github.com/randomm/oo/releases/latest/download/double-o-aarch64-apple-darwin.tar.xz
tar xf double-o-aarch64-apple-darwin.tar.xz
sudo mv double-o-aarch64-apple-darwin/oo /usr/local/bin/
# Linux (x86_64)
curl -LO https://github.com/randomm/oo/releases/latest/download/double-o-x86_64-unknown-linux-gnu.tar.xz
tar xf double-o-x86_64-unknown-linux-gnu.tar.xz
sudo mv double-o-x86_64-unknown-linux-gnu/oo /usr/local/bin/
# Linux (ARM64)
curl -LO https://github.com/randomm/oo/releases/latest/download/double-o-aarch64-unknown-linux-gnu.tar.xz
tar xf double-o-aarch64-unknown-linux-gnu.tar.xz
sudo mv double-o-aarch64-unknown-linux-gnu/oo /usr/local/bin/
```
### From crates.io
```bash
cargo install double-o
```
### From source
```bash
git clone https://github.com/randomm/oo.git
cd oo
cargo build --release
cp target/release/oo /usr/local/bin/
```
---
## Quick Start
```bash
# Wrap any command — output is classified and compressed
$ oo cargo test
✓ cargo test (47 passed, 2.1s)
# Large unrecognised output is indexed — query it later
$ oo git log --oneline -100
● git (indexed 12.4 KiB → use `oo recall` to query)
$ oo recall "merge commit"
# Teach oo a new pattern for an unrecognised command
$ oo learn rspec spec/
```
**Shell semantics:** `oo` runs programs directly — it does not invoke a shell. Use
`oo sh -c '<command>'` when you need shell syntax (pipes, `&&`, globbing):
```bash
See the [savings indicator spec](docs/cli-reference.md#savings-indicator) for how the
`[saved N]` figure on the indicator line works.
---
## Commands
| `oo <cmd> [args...]` | Run a shell command with context-efficient output |
| `oo recall [--full] <query>` | Search indexed output for this project |
| `oo forget` | Clear all indexed output for this project |
| `oo learn <cmd> [args...]` | Run command and teach `oo` a new output pattern via LLM |
| `oo help <cmd>` | Fetch a cheat sheet for `cmd` from cheat.sh (`oo help <reserved-subcommand>` prints local usage) |
| `oo init` | Set up hooks for agent frameworks (`--format claude` or `--format generic`; or `--agent pi` or `--agent claude-code`, with optional `--global`) |
| `oo patterns` | List loaded output patterns (built-in, project and user) |
| `oo rewrite <command...>` | Print an `oo`-prefixed form of a command for agent hooks (experimental) |
| `oo hook <agent>` | Agent hook processor (e.g. `oo hook claude` reads PreToolUse JSON on stdin) |
| `oo version` | Print version |
> **Note:** Any reserved subcommand accepts `--help` / `-h` as its first argument to print its usage and exit 0 (e.g. `oo init --help`). The subcommand itself does not run, but a pending background-learn status message is still printed and cleared first.
>
> `oo help` sources from [cheat.sh](https://cheat.sh) which covers common Unix tools. For modern CLIs not yet in cheat.sh (e.g., `gh`, `kamal`), use `oo learn <cmd>` to teach `oo` the command's output patterns.
---
## Agent Integration
Add this to your system prompt or `CLAUDE.md`:
```
Prefix all shell commands with `oo`. Use `oo recall "<query>"` to search large outputs.
```
That's it. The agent runs `oo cargo test`, gets `✓ cargo test (47 passed, 2.1s)`,
and moves on.
### Use with pi
`oo init --agent pi` installs a pi extension (`.pi/extensions/oo.ts` at the git
root, or `~/.pi/agent/extensions/oo.ts` with `--global` — or
`$OO_PI_EXTENSIONS_DIR/oo.ts` when `OO_PI_EXTENSIONS_DIR` is set, since the
variable is the final extensions directory) that hooks pi's bash
tool calls and rewrites bare shell commands to their `oo`-prefixed form via
`oo rewrite`. pi agents then get token-efficient output automatically — no
prompt instruction required.
```bash
oo init --agent pi # project: <git-root>/.pi/extensions/oo.ts
oo init --agent pi --global # user: ~/.pi/agent/extensions/oo.ts (or $OO_PI_EXTENSIONS_DIR/oo.ts)
```
- The extension carries an `ooAvailable` flag, initially true: if the load
probe fails (binary missing, error, or timeout) it is set false and the
`tool_call` handler passes every command through untouched (status note
only).
- It leaves nested (codemode) tool calls untouched, skips commands already
starting with `oo `, and fails open on any error — a failing handler never
blocks your shell commands.
- Disable for a session: `OO_DISABLE=1`.
- Uninstall: delete the installed `oo.ts` file.
Plain `oo init` (the `--format claude` default) only writes the legacy
`.claude/hooks.json`; for the rewriting Claude Code hook use `--agent claude-code`
(below). See
the [CLI reference](docs/cli-reference.md#oo-init---format-format---agent-agent---global) for the full option matrix.
### Use with Claude Code
`oo init --agent claude-code` installs a Claude Code PreToolUse hook for the
Bash tool (merging `oo hook claude` into `.claude/settings.json`, or
`~/.claude/settings.json` with `--global`). When Claude Code runs a Bash
command, the hook pipes the PreToolUse JSON to `oo hook claude`, which
rewrites the command to its `oo`-prefixed form via `updatedInput.command` and
prints it back — token-efficient output with no prompt instruction required.
```bash
oo init --agent claude-code # project: <git-root>/.claude/settings.json
oo init --agent claude-code --global # user: ~/.claude/settings.json (or $OO_CLAUDE_DIR/settings.json)
```
- Only Bash tool calls are rewritten; other tools pass through unchanged.
- The hook is **fail-open**: any parse error, empty/invalid input, or no
matching pattern prints nothing and exits 0, so the original command always
runs. `OO_DISABLE=1` disables only the hook's rewriting (the hook passes
commands through untouched); it does not disable the `oo` runner itself.
- The installed hook is idempotent (re-running leaves exactly one entry) and
never overwrites a malformed `settings.json` (it errors instead).
- Uninstall: remove the `oo hook claude` entry from `settings.json`.
---
## Built-in Patterns
`oo` ships with 24 patterns that match commands automatically:
| `pytest` | `{passed} passed, {time}s` | tail 30 lines |
| `cargo test` | `{passed} passed, {time}s` | tail 40 lines |
| `go test` | `ok ({time}s)` | tail 30 lines |
| `jest` / `vitest` / `npx jest` | `{passed} passed, {time}s` | tail 30 lines |
| `ruff check` | quiet (no output on pass) | smart truncate |
| `eslint` | quiet | smart truncate |
| `cargo build` | quiet | head 20 lines |
| `go build` | quiet | head 20 lines |
| `tsc` | quiet | head 20 lines |
| `cargo clippy` | quiet | smart truncate |
| `npm test` | `{passed} passed, {time}s` | tail 30 lines |
| `yarn test` | `{passed} passed, {time}s` | tail 30 lines |
| `pnpm test` | `{passed} passed, {time}s` | tail 30 lines |
| `bun test` | `{passed} passed, {time}s` | tail 30 lines |
| `cargo tarpaulin` | `{cov}% coverage` | tail 20 lines |
| `cargo fmt` | quiet | grep for "Diff in" |
| `mypy` | `Success: no issues found` / error count | head 20 lines |
| `rubocop` | `{offenses} offenses` | head 30 lines |
| `ruff format` | `{count} files reformatted` | head 20 lines |
| `prettier` | quiet | head 20 lines |
| `npm run build` | quiet | head 20 lines |
| `yarn build` | `Done in {time}s` | head 20 lines |
| `pnpm build` | quiet | head 20 lines |
| `bun build` | quiet | head 20 lines |
Add your own patterns with `oo learn <cmd>` (generates a TOML pattern file via
LLM) or write one manually in the user patterns directory (`oo patterns` prints the path).
---
## FAQ
**What if oo doesn't recognize my command?**
Successful unknown commands pass through unchanged at up to 4 KiB; failed ones are always filtered. Larger Data, Content, and Unknown output
is indexed in full for later retrieval via `oo recall` (unless a matching success pattern shows its summary instead); entries older than the retention period (7 days by default) are purged the next time the same project indexes output (see [configuration](docs/configuration.md#store-section)). Data commands show only an indicator
line; other commands show a byte-bounded head+tail slice (marked with a truncation line).
Successful Status commands such as `make` with more than 4 KiB of output and no pattern summary print only a `✓ label` line, with ` [saved N]` appended when the output minus that line is over 4 KiB (binary units from KiB upward, chosen by magnitude; see the [savings indicator spec](docs/cli-reference.md#savings-indicator)), and are not indexed. Use `oo learn <command>` to teach oo a compression pattern.
**Can I disable compression for a command?**
Larger unpatterned unknown commands already show a bounded head+tail slice with the full output stored for
`oo recall`; entries older than the retention period (7 days by default) are purged the next time the same project indexes output (see [configuration](docs/configuration.md#store-section)). For commands with built-in patterns, you can override with
a custom TOML pattern. See [Custom Patterns](docs/patterns.md).
**Does oo work in CI?**
It can, but oo is designed for interactive AI agent sessions where context tokens matter.
In CI, full output is usually fine.
**Which LLM providers does `oo learn` support?**
Anthropic only. Set `ANTHROPIC_API_KEY` in your environment.
See [Learning Patterns](docs/learn.md).
---
## Troubleshooting
**`oo: command not found`**
Ensure `~/.cargo/bin` is in your PATH, or use the full path to the binary.
**Pattern not matching my command**
Run `oo patterns` to see all loaded patterns and their command regexes.
Project patterns (`.oo/patterns/` in the git root, or the current directory when there is no git root) and user patterns are checked before built-ins: project first, then user, then built-in; the first match wins. The foreground display in `oo learn` uses built-ins only (#151).
**`oo learn` fails or produces bad patterns**
Ensure `ANTHROPIC_API_KEY` is set. Run the command normally first so oo has
real output to analyze. See [Learning Patterns](docs/learn.md).
---
## Documentation
**[Documentation Index](docs/README.md)** — Extensive project documentation:
- [Testing Guide](docs/testing.md) — How to run, write, and understand tests
- [Architecture](docs/architecture.md) — System design and module responsibilities
- [Security Model](docs/security-model.md) — Trust assumptions and data handling
- [CLI Reference](docs/cli-reference.md) — All subcommands, output tiers, and the savings indicator spec
- [Configuration](docs/configuration.md) — Environment variables, config files, and platform paths
- [Custom Patterns](docs/patterns.md) — Creating patterns for command output compression
- [Learning Patterns](docs/learn.md) — Using `oo learn` to generate patterns automatically
- [Changelog](CHANGELOG.md) — Release history
**For contributors**: See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and workflow.
**For agents**: See [AGENTS.md](AGENTS.md) for project-specific agent conventions.
---
## License
Apache-2.0 — see [LICENSE](LICENSE).
`oo help` fetches content from [cheat.sh](https://cheat.sh), which includes
[tldr-pages](https://github.com/tldr-pages/tldr) content (CC BY 4.0). See
[NOTICE](NOTICE).