Features
What you get with agent-tail
Readable, greppable logs
Logs are plain text files written to tmp/logs/latest/ relative to your project root, with a consistent format. Timestamps, levels, source locations, and stack traces are all there — easy for you to scan and easy for an AI to parse.
[10:30:00.123] [LOG ] User clicked button[10:30:00.456] [WARN ] Deprecated API call[10:30:01.789] [ERROR ] Failed to fetch data (http://localhost:5173/src/api.ts:42:10)Error: Network errorat fetchData (http://localhost:5173/src/api.ts:42:10)at handleClick (http://localhost:5173/src/app.ts:15:5)
Levels are padded to 7 characters for alignment. Stack traces are indented. Source URLs are included for errors.
Log filtering
Not every log line is useful — HMR updates, noisy debug output, and framework internals add clutter that wastes AI context. The excludes option lets you filter them out before they hit disk.
The CLI supports it too, with repeatable --exclude flags:
agent-tail run --exclude "[HMR]" --exclude "/^DEBUG/" 'fe: npm run dev'
Plain strings are substring matches. Patterns starting with / are parsed as regex (e.g. /^HMR/i).
Muting services
Different from --exclude which filters log content, --mute silences entire services from your terminal and combined.log. Muted services still run and their output is still captured to individual log files — they just don’t clutter your terminal while you’re debugging something else.
agent-tail run --mute fe --mute worker 'fe: npm run dev' 'api: uv run server' 'worker: uv run worker'
In this example, only api output appears in your terminal. All three services still log to fe.log, api.log, and worker.log — so agents can still read everything.
--exclude filters noisy log lines by content (e.g. HMR messages). --mute hides entire services by name. Use --exclude to clean up what gets written to disk. Use --mute to focus your terminal on one service while debugging.
Multi-server log aggregation
Most projects run more than one process — a frontend, an API, maybe a worker. agent-tail can aggregate all of them into one session directory.
1. Use agent-tail run (recommended)
Run everything from one command. All output goes to the same session automatically.
2. Wrap services independently
Run each service in its own terminal. The wrap command detects the existing session:
# Terminal 1: Start the frontend (creates the session)npm run dev# Terminal 2: Wrap the API server (reuses the session)npx agent-tail wrap api -- uv run fastapi-server# Terminal 3: Tail everythingtail -f tmp/logs/latest/*.log# oragent-tail tail -f
3. Direct file writes (no CLI needed)
Point your server's logging at the latest symlink. Works with any language:
# Pythonlog_dir = os.path.join(os.getcwd(), "tmp", "logs", "latest")handler = logging.FileHandler(os.path.join(log_dir, "api.log"))# Node.jsconst log_stream = fs.createWriteStream(path.join("tmp/logs/latest", "server.log"),{ flags: "a" })
Monorepos & package runners
If you use Turborepo, Nx, Lerna, or any other monorepo runner, you can use agent-tail init to create a shared session at the monorepo root, then let the runner start each package with agent-tail wrap.
The pattern is: agent-tail init creates the session directory and latest symlink once, then your runner (Turborepo, Nx, etc.) starts each app in parallel. Each app's dev script uses agent-tail wrap with --log-dir pointing back to the root's log directory.
// Root package.json{"scripts": {"dev": "agent-tail init && turbo dev"}}
// apps/web/package.json{"scripts": {"dev": "agent-tail wrap web --log-dir ../../tmp/logs -- vite"}}
The --log-dir path is relative to each package's directory. Adjust the relative path based on your monorepo depth. This pattern works with any runner that starts multiple packages in parallel.
Searching and tailing logs
Both work: direct Unix tools on the log files themselves, or agent-tail tail when you want the CLI to resolve the latest session for you.
# Follow all logs in real timetail -f tmp/logs/latest/*.logagent-tail tail -f# Follow a specific servicetail -f tmp/logs/latest/browser.logagent-tail tail browser -f# Forward any normal tail flagsagent-tail tail combined -n 100# Find all errors across every servicegrep -r "ERROR" tmp/logs/latest/# Case-insensitive searchgrep -ri "failed\|timeout\|exception" tmp/logs/latest/# Show context around each matchgrep -r -C 5 "ERROR" tmp/logs/latest/# Only ERROR and WARN linesawk '/\[ERROR|\[WARN/' tmp/logs/latest/browser.log# Count errors per servicegrep -rc "ERROR" tmp/logs/latest/# Use ripgrep for faster searchesrg "ERROR|WARN" tmp/logs/latest/
Captured browser events
The framework plugins capture more than just console.* calls:
- Unhandled errors (
window.onerror) — logged asUNCAUGHT_ERRORwith full stack traces - Unhandled promise rejections — logged as
UNHANDLED_REJECTION
These are the errors that silently break your app in the browser. Disable with captureErrors: false and captureRejections: false.
Session management
Each agent-tail run (or dev server start with a framework plugin) creates a new session — a timestamped directory under tmp/logs/ that holds all log files for that run. A latest symlink always points to the most recent session, so tmp/logs/latest/ is always the right path to give your agent.
- Timestamped directories — e.g.
2024-01-15T10-30-00-123Z/ - Latest symlink — updated on every new session, always points to the newest one
- Auto-pruning — old sessions beyond the limit are removed (default: keep 10)
- Gitignore detection — warns if your log directory isn't in
.gitignore