Back to the catalog

trail

Trail wraps any command (trail run -- <cmd>) or Docker container and captures its stdout/stderr into per-session JSONL files on local disk.

Open source Repository Open in the app JSON README (API)

About

Trail wraps any command (trail run -- <cmd>) or Docker container and captures its stdout/stderr into per-session JSONL files on local disk. The bundled MCP server exposes list_sessions and get_logs so Claude Code can filter by level, regex, time window, or line range — server-side, without burning context window. Includes a debug-with-trail skill that walks Claude through a 4-phase debug flow: read existing logs first, add marked instrumentation only when needed, and a mandatory cleanup step verified by grep. Local-first, no cloud, no daemon, single static binary.

Details

Kind
Plugins
Topic
Cloud & DevOps
Publisher
pratham-mishra04
Origin
marketplace
Category
ferramentas
Stars
8
Forks
1
Open pull requests
2
Last push
2026-06-09T19:24:43Z
Repository state
ativo
Language
Go
License
MIT
Added
2026-08-30 01:48:58
Updated
2026-08-30 01:48:58
Origin id
pratham-mishra04/trail/trail

README

# trail

Local stdout/stderr capture for AI coding agents.

Run any process under `trail`, then ask Codex, Claude Code, Cursor, Windsurf, or another MCP-capable agent to query the captured logs without pasting terminal output into chat.

Single static binary. Plain JSONL files on disk. No daemon, no database, no cloud.

## Guided Debugging At A Glance


Trail is most useful when the agent needs to move beyond "read the last error" and run a disciplined debugging loop: inspect captured logs, keep a ledger, add temporary probes only when needed, wait for the dev server to restart, reproduce the issue, verify the fix, and remove every probe.

![Trail guided debugging workflow: Codex queries Trail MCP, adds temporary probes, waits for restart, reproduces the issue, fixes it, and cleans up](docs/guided-debugging-flow.svg)

The important constraint is cleanup. Every temporary log line carries a unique `TRAIL-DEBUG-<id>` marker, and the workflow is not done until the agent verifies that marker is gone from the repo.

## Fast Setup

All integrations need the `trail` binary on your `PATH` first:

```bash
curl -fsSL https://raw.githubusercontent.com/Pratham-Mishra04/trail/main/install.sh | sh
trail version
```

Then add the integration for your agent.

### Codex

```bash
codex plugin marketplace add Pratham-Mishra04/trail --ref main
codex plugin add trail@pratham
```

Start a new Codex thread, then ask:

```text
Use Trail to list my captured sessions.
```

### Claude Code

```text
/plugin marketplace add Pratham-Mishra04/trail
/plugin install trail@pratham
/reload-plugins
```

Verify by asking:

```text
What skills are available?
```

You should see `pratham:trail:debug-with-trail`.

### Other MCP Agents

Use the manual MCP config in [Editor Setup](#editor-setup). You will get the `list_sessions` and `get_logs` tools, but not the bundled Codex/Claude guided debugging skill.

![trail architecture: an app wrapped by `trail run` writes stdout/stderr to a JSONL session file on local disk; `trail mcp` reads that file and serves it over stdio MCP to Codex, Claude Code, Cursor, Windsurf, or Claude Desktop](docs/architecture.png)

## First Capture

Wrap the process you want to debug:

```bash
trail run -- npm run dev
```

Trail prints a session id and file path, then forwards your app's stdout/stderr normally while also capturing every line:

```text
trail: capturing "npm run dev" -> 39f6875e-3417-4146-867b-c430971b7489 (file: /Users/you/.config/trail/sessions/39f6875e-3417-4146-867b-c430971b7489.jsonl)
```

Now ask your agent:

```text
List my Trail sessions, then show me the errors from the most recent one.
```

Or query from the terminal:

```bash
trail sessions
trail logs --session 39f6875e-3417-4146-867b-c430971b7489 --level error
trail logs --session 39f6875e-3417-4146-867b-c430971b7489 --query "ECONNREFUSED"
```

The agent calls `list_sessions` and `get_logs` over MCP. Trail filters the logs in the Go process and returns structured results instead of a wall of text.

## What Trail Gives You

- **Process capture:** `trail run -- <cmd>` wraps any command, captures stdout/stderr, forwards signals, and mirrors the child's exit code.
- **Docker capture:** `trail docker <container>` captures `docker logs -f` for an already-running container.
- **Agent tools:** `trail mcp` exposes `list_sessions` and `get_logs` over stdio MCP.
- **Human tools:** `trail sessions` and `trail logs --session <id>` expose the same query surface in the terminal.
- **Local files:** captured logs are plain JSONL under `~/.config/trail/sessions/`.

## Guided Debugging Workflow

The Codex and Claude Code plugins ship a `debug-with-trail` skill for runtime debugging. It turns Trail into a repeatable agent workflow for server crashes, failing tests, silent failures, and "what happened in the logs?" investigations.

Ask in plain English:

```text
My Express server returns 500 on POST /orders. Use Trail to debug it.
```

The skill walks the agent through:

1. **Prerequisites:** verify `trail` is installed and the relevant process is being captured.
2. **Read existing logs first:** query errors, recent output, and relevant terms before touching code.
3. **Instrument only when needed:** add temporary targeted log lines with a unique `TRAIL-DEBUG-<id>` marker.
4. **Verify the fix:** query the marker output after the change, not just static code.
5. **Clean up:** remove every temporary log line and verify `rg TRAIL-DEBUG-<id>` returns nothing.

The cleanup rule is the important part: every added probe carries a unique marker, and the agent is instructed to remove the whole log statement after the fix is verified.

Read the exact skill instructions:

- [Codex skill](integrations/codex/skills/debug-with-trail/SKILL.md)
- [Claude Code skill](integrations/claude-code/skills/debug-with-trail/SKILL.md)

## Use Cases

- **Debugging a running app:** wrap `npm run dev`, `python manage.py runserver`, `go run .`, or any other server and let the agent query only the relevant logs.
- **Investigating long test runs:** `trail run -- go test -v ./...`, `trail run -- pytest -v`, or `trail run -- npm test`, then ask what failed and why.
- **Build, lint, and typecheck output:** capture long compiler or linter output once, then filter by level, file name, symbol, or regex.
- **Comparing flaky runs:** capture a passing run and a failing run, then ask the agent what changed.
- **Long-running scripts and migrations:** keep a permanent, queryable local record instead of relying on terminal scrollback.
- **Noisy Docker containers:** use `trail docker <name>` to make container logs queryable without flooding the agent context.

In every case, the pattern is the same: attach terminal output to your agent by handing it a session id, instead of piping logs through prompts.

## Editor Setup

The Codex and Claude Code plugins are the recommended setup because they include both MCP wiring and the guided debugging skill.

For manual MCP setup, make sure `trail` is on the `$PATH` of the shell that launches your editor. macOS GUI apps do not always inherit your shell path; if the MCP server fails to start, check `which trail` from the editor's terminal.

### Claude Code Without The Plugin

```bash
claude mcp add trail -s user -- trail mcp
```

This path wires up the raw MCP tools (`list_sessions`, `get_logs`) only — it does not include the bundled `debug-with-trail` skill.

### Cursor

`.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "trail": {
      "command": "trail",
      "args": ["mcp"]
    }
  }
}
```

### Windsurf

Settings -> MCP:

```json
{
  "mcpServers": {
    "trail": {
      "command": "trail",
      "args": ["mcp"]
    }
  }
}
```

### Claude Desktop

`claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "trail": {
      "command": "trail",
      "args": ["mcp"]
    }
  }
}
```

## Install Options

### One-liner

```bash
curl -fsSL https://raw.githubusercontent.com/Pratham-Mishra04/trail/main/install.sh | sh
```

The installer detects your OS/arch, downloads the matching prebuilt binary from the latest GitHub Release, verifies its SHA-256 checksum, and installs `trail` to `~/.local/bin/trail`.

If `~/.local/bin` is not on your `$PATH`, the installer prints the exact `export` line to add. Override the destination with `BIN_DIR=/usr/local/bin` or pin a version with `VERSION=v0.1.0`.

`wget` works too:

```bash
wget -qO- https://raw.githubusercontent.com/Pratham-Mishra04/trail/main/install.sh | sh
```

### Manual Download

```bash
ARCHIVE=trail_darwin_arm64.tar.gz
BASE=https://github.com/Pratham-Mishra04/trail/releases/latest/download

wget "$BASE/$ARCHIVE"
wget "$BASE/checksums.txt"
grep " $ARCHIVE\$" checksums.txt | shasum -a 256 -c -

tar xzf "$ARCHIVE"
sudo mv trail /usr/local/bin/
```

Available archives:

- `trail_darwin_arm64.tar.gz`
- `trail_darwin_amd64.tar.gz`
- `trail_linux_arm64.tar.gz`
- `trail_linux_amd64.tar.gz`

### Go Install

```bash
go install github.com/Pratham-Mishra04/trail@latest
```

Go's `go install` does not inject release metadata, so `trail version` may print `trail dev (commit none, built unknown)`. The binary still works normally.

### Build From Source

```bash
git clone https://github.com/Pratham-Mishra04/trail
cd trail
make install
```

This runs `go install` with version metadata baked in. The binary lands at `$(go env GOPATH)/bin/trail`.

### Supported Platforms

macOS and Linux on amd64/arm64. Windows is not supported.

## Why Server-side Queries

When an agent reads raw log files into its context to filter them, every query pays for re-loading the file's tokens through the LLM. Trail filters in the Go process and returns only matching entries.

This means:

- The agent does not burn context on log lines that will not matter.
- Regex and level filters are deterministic.
- Latency is bounded by the filter work, not token generation speed.

## What It Captures

| Source | Command | Notes |
|---|---|---|
| Wrapped command | `trail run -- <cmd>` | Wraps any binary; separate stdout/stderr pipes preserve stream attribution. |
| Docker container | `trail docker <name>` | Wraps `docker logs -f`; passes `--since` through. |

Both commands accept `--name <label>` to override the auto-derived session name and `--ephemeral` to delete the session file when the capturer exits cleanly. The file survives a `SIGKILL`; only graceful shutdown triggers cleanup.

Trail does not capture:

- A bare PID you did not start under Trail. That would require `ptrace` or `eBPF`.
- Arbitrary log files written to disk by another tool. Use `tail`, `lnav`, or normal file tools for those.

## Querying Captured Logs

### From An Agent

The MCP server exposes two tools:

- **`list_sessions(active_only?, limit?)`:** returns session metadata including the absolute file path of each session JSONL file, ordered active-first.
- **`get_logs(session_id, filters?)`:** returns matching entries.

`get_logs` filters include:

- `limit`, `page`, and `order`
- `query` as a case-insensitive regex
- `level`: `error`, `warn`, `info`, `debug`, `unknown`, or `all`
- `start_time` / `end_time` as RFC3339 timestamps
- `duration` as a Go-style duration, such as `"10m"` or `"2h"`
- `start_line` / `end_line`

Time-window, duration, and line-range filters are mutually exclusive. Combining them returns an error.

If a result looks incomplete, the response includes the absolute `file_path` and the agent can read the JSONL file directly with its file tools.

### From The Terminal

```bash
trail sessions                                       # table
trail sessions --json                                # JSON for scripting
trail sessions rm <id>                               # delete one
trail sessions rm --all                              # delete all

trail logs --session <id>                            # last 100, pretty
trail logs --session <id> -n 50                      # last 50
trail logs --session <id> --level error              # errors only
trail logs --session <id> --query "ECONNREFUSED"     # case-insensitive regex
trail logs --session <id> --format json              # raw JSON entries
```

`trail logs --session` is required. There is no implicit "most recent" default; the MCP tool follows the same rule.

## How It Works

- **Two-process model:** capture processes (`trail run` / `trail docker`) own session files. The MCP server is read-only and spawned by the editor on demand; it never starts, modifies, or stops captures.
- **One JSONL file per session:** files live at `~/.config/trail/sessions/<uuid>.jsonl`. The first line is a meta header; every subsequent line is one captured entry.
- **Append-only writes + tolerant reader:** entries are appended to a regular file (`os.OpenFile(O_APPEND)`); `maxRawLen` caps the `Raw`/`Message` fields but not the whole marshaled JSON line, so concurrent reads rely on the reader being tolerant of malformed or partial trailing lines (see `internal/store/reverse.go` and `TestRead_TolerantOfPartialLastLine`) rather than on kernel-level atomicity.
- **Server-side filtering:** cheap filters run before full JSON decode; eligible newest-first queries (`Order=newest`, `Limit>0`, `Page≤1`, no time/line bounds) use a reverse scan from the end of the file.
- **Conservative log-level detection:** Trail only assigns a level when there is clear evidence, such as a JSON `level` field, logfmt `level=`, or an anchored prefix like `ERROR:`.

## License

MIT

More