agx-tui 0.2.1

Step-through debugger for your agent — terminal-native session timeline viewer for Claude Code, Codex, Gemini, LangChain, Vercel AI SDK, and OpenTelemetry GenAI traces
Documentation
# Contributing to agx

Thanks for considering a contribution. agx is a small project — every PR
gets read carefully and most get merged.

## Quick start

```bash
git clone https://github.com/brevity1swos/agx.git
cd agx
cargo build
cargo test
```

Before opening a PR:

```bash
cargo fmt
cargo clippy --all-targets -- -D warnings
cargo test
```

These three must pass cleanly. CI will reject PRs that don't.

## What kind of contributions help most

In rough priority order:

1. **Anonymized session fixtures** for formats that already exist (Claude
   Code, Codex, Gemini, generic, LangChain, Vercel AI SDK, OTel). The more variety we have in
   `tests/corpus/`, the harder it is for format drift to break things
   silently. See "Contributing fixtures" below.
2. **New format parsers** (Aider, Cline, Cursor, Windsurf, etc). See
   "Adding a new agent trace format" below.
3. **Bug fixes with a reproduction.** A failing test case that shows the
   bug is worth more than a fix without one.
4. **TUI polish** — keybinding tweaks, layout improvements, color choices.
5. **Documentation fixes** — typos, outdated examples, missing keybindings.

What we generally don't want:

- Hosted components (telemetry, cloud sync, web UI). agx is terminal-native
  by design — see the "Not to Do" section in CLAUDE.md.
- Heavy new dependencies. Each new crate has to justify its weight.
  Optional heavy deps (`prost`, `fastembed`, `notify-rust`) must sit behind
  a Cargo feature flag that's off by default.
- Refactors-for-cleanliness without a concrete user-visible benefit.
- Speculative architecture changes (e.g. unifying parsers behind a shared
  Entry trait — explicitly avoided, see CLAUDE.md).

## Adding a new agent trace format

The architecture is built for this — each format is a self-contained
parser module that produces `Vec<Step>`. No shared base class, no
unification.

Step-by-step:

1. Create `crates/agx-core/src/<format>.rs` with format-specific
   deserialize types (use `serde_json::Value` for fields you don't care
   about). Parser modules live in the `agx-core` crate.
2. Define `pub fn load(path: &Path) -> Result<Vec<Step>>`. This is the
   only required public function — same shape as `codex::load` and
   `gemini::load`.
3. Build steps via the shared helpers in `crates/agx-core/src/timeline.rs`:
   `user_text_step`, `assistant_text_step`, `tool_use_step`,
   `tool_result_step`. Don't define your own Step constructors — uniformity
   happens at the `Step` level so the TUI renders every format identically.
4. Pair tool calls with their results inside your parser. Each format does
   this differently (Claude Code's `tool_use_id`, Codex's `call_id`,
   Gemini's atomic `toolCall`); your parser owns the pairing logic.
5. Add a variant to `Format` in `crates/agx-core/src/format.rs` and extend
   `format::detect` with a content-shape check that identifies your format
   unambiguously. Detection is by JSON shape, **not** file extension.
6. Re-export your module from `crates/agx-core/src/lib.rs` and add a
   match arm in `src/main.rs::load_session` dispatching to your new parser.
7. Add a synthetic fixture under `assets/sample_<format>_session.<ext>`
   following the same pattern as the others — obviously-fake UUIDs,
   generic content, **zero personal data**.
8. Add unit tests in your module's `mod tests` block. Use
   `tempfile::NamedTempFile` to write synthetic content and pass paths to
   `load()`.

## Contributing fixtures

agx's parsers degrade gracefully on unknown fields (`#[serde(other)]`),
but real-world session files surface edge cases that synthetic fixtures
miss. Anonymized real fixtures are gold.

Before contributing a fixture:

1. **Anonymize.** Replace absolute paths with `/tmp/<placeholder>`. Strip
   email addresses, API keys, tokens, and any company-specific identifiers
   via search/replace. Replace usernames with `user`. Inspect the file
   line-by-line — `grep` is not enough.
2. **Verify.** `agx --summary <your-fixture>` should produce sensible
   output. `agx --debug-unknowns <your-fixture>` should not list any
   surprising entry types (or if it does, that's a great PR — it's
   format drift we should know about).
3. **Place** the file under `tests/corpus/<format>/` with a descriptive
   name like `claude_code_branching_session.jsonl` or
   `codex_with_reasoning_blocks.jsonl`.
4. **Commit message:** include what's interesting about the fixture —
   "first fixture with parallel tool calls" or "covers Codex CLI 0.40+
   reasoning entries".

The integration test in `tests/corpus_test.rs` will pick up your file
automatically and assert it parses without error.

## Reporting format drift

If a CLI you use has changed its session format and agx now misparses or
misses content, please open an issue with the
`format_drift` template. We need:

- Which CLI and which version
- The first 10–20 lines of an affected session file (anonymized)
- What agx shows vs. what you expected

Format drift PRs are highest priority — they keep agx working for everyone.

## Code conventions

- **Formatting:** default rustfmt (`cargo fmt`). No project-specific
  `rustfmt.toml`.
- **Lints:** `cargo clippy --all-targets -- -D warnings` must be clean.
  Pedantic clippy is also clean except for two justified `#[allow]`s
  documented in CLAUDE.md.
- **Tests:** unit tests inline via `#[cfg(test)] mod tests` in each module.
  Integration tests in `tests/`.
- **Errors:** `anyhow::Result` at crate boundary, `with_context()` at
  serde error sites for line-number context.
- **Commits:** Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`,
  `test:`, `chore:`, `build:`, `perf:`).
- **MSRV:** Rust 1.85 (required by edition 2024). Don't bump without discussion.

See CLAUDE.md for the full architecture map and "Common Tasks" recipes.

## License

By contributing, you agree your contributions are dual-licensed under
MIT OR Apache-2.0, matching the project's license.