Claude Code Audit Log: Record Every Shell Command It Runs
Keep a local audit log of every shell command Claude Code, Codex and Cursor run, then trace a failed run to its prompt, its error and the files it changed.
When you hand a task to Claude Code or Cursor, it doesn't just edit files — it runs shell commands. Installs, migrations, git operations, test runs, the occasional rm. Most of these scroll past in a tool panel and are hard to find again once the session ends. If something breaks an hour later, you want a record of what the agent actually ran. This guide shows how to capture the shell commands your supported agent integrations report, investigate a failure with them, and understand what the record does not contain.
Why agent commands are hard to see
- They don't land in your shell history. Agents usually run commands in their own subprocess, so
~/.zsh_historynever sees them. - The agent UI is a chat log. The tool-call record lives inside the conversation, mixed in with file reads, edits, and prose. It isn't something you can filter by exit code or directory.
- You can't tell who ran what. Even when commands do hit your history, there's nothing marking which were yours and which were the agent's.
The result is a visibility gap exactly where you'd want the most accountability: automated commands running with your permissions.
Record commands with their executor and prompt
Suvadu stores captured shell and agent commands in a local SQLite database. Each record includes the executor (for example claude-code or cursor), working directory, timestamp, and the exit code when the source reports one. Prompt text is attached only when the configured integration captures prompt events; environment-variable detection alone does not capture a prompt.
1. Install Suvadu
curl -fsSL https://downloads.appachi.tech/suvadu/install.sh | bash The installer provides the binary and prints shell-setup instructions. It never reloads Zsh/Bash, and it edits a startup file only if you accept its offer to add the hook line, so complete the appropriate installation and shell-integration steps before expecting terminal commands to be recorded.
2. Connect your agents
suv init claude-code
suv init cursor Each command installs that agent's hooks, so the shell commands it reports are recorded and attributed. Relaunch Claude Code and restart Cursor afterwards so they load the new hooks. Other agents, restart requirements, and per-agent differences are covered in agent integration and the compatibility matrix.
3. Review what the agent ran
# Claude Code commands recorded today, newest first
suv history --executor claude-code --after today
# Interactive search, scoped to Claude Code's failed commands
suv search --executor claude-code --failed The search TUI lets you narrow by executor, directory, time, and exit code. An explicit --executor filter shows agent commands even though they are hidden from everyday Ctrl+R recall.
Walkthrough: investigate a failed agent run
Here is a synthetic but realistic case. You asked Claude Code to "add rate limiting to the login route" in ~/src/shop-api. It reported success, but CI on your branch fails in the test suite. You want to know what the agent actually ran before you start guessing.
Step 1: Get the report for this project
From the directory the agent worked in, generate today's report for Claude Code. --here matches that exact directory, not its subdirectories.
$ suv agent report --executor claude-code --here
═══════════════════════════════════════════════════════
AGENT ACTIVITY REPORT — Sep 18, 2026
═══════════════════════════════════════════════════════
Period: 14:02 — 14:09
Agents: claude-code (7 cmds)
Success: 5/7 (71.4%)
Risk: 1 high
───────────────────────────────────────────────────────
⚠ HIGH RISK COMMANDS
───────────────────────────────────────────────────────
[claude-code] npm install express-rate-limit
~/src/shop-api · 14:03 · exit 0
Category: package-install
───────────────────────────────────────────────────────
📦 PACKAGES INSTALLED
───────────────────────────────────────────────────────
npm: express-rate-limit
───────────────────────────────────────────────────────
✘ FAILED COMMANDS (2)
───────────────────────────────────────────────────────
[claude-code] npm test -- login exit 1 14:05
[claude-code] npm test exit 1 14:08
═══════════════════════════════════════════════════════ Two things stand out. The agent added a dependency, and it ran the full test suite once, which failed. A failed scoped run like the one at 14:05 is normal, since agents often run, fix, and rerun (the report lists failures, not the passing reruns). The 14:08 failure is the interesting one: nothing recorded after it shows the full suite passing. The high label on the install is informational: risk assessment flags package installs because they bring in external code, not because this one is wrong.
Step 2: List the failures with timestamps
$ suv history --executor claude-code --exit-code 1 --here
2026-09-18 14:08:41 ✗1 0ms ~/src/shop-api npm test
2026-09-18 14:05:12 ✗1 0ms ~/src/shop-api npm test -- login The 0ms is expected. Claude Code's hook events don't include a duration, so Suvadu records the time the hook was received rather than inventing a runtime. Integrations that report duration, such as Cursor's shell-execution hook, show real values here.
Step 3: Connect the failure to the prompt
suv agent prompts --executor claude-code --here The Prompt Explorer lists captured prompts for the period. Select the rate-limiting prompt and press Enter to see the commands recorded under it, with path, time, exit code, and executor. Ctrl+S jumps to that prompt's session timeline.
Step 4: Read what the agent said about it
suv sessions In the session browser, Ctrl+T cycles to AI sessions. A Claude Code timeline interleaves your prompts, the assistant's text responses, the recorded commands, and the model in use. In this case, the assistant's final message is where you'd find out whether it noticed the 14:08 failure and called it "unrelated", or never mentioned it. See the sessions guide for how to read each field.
Step 5: Read why it failed, or reproduce it
By default Suvadu stores the command line, not its output, so the error text isn't in the record. Since 0.5.1 you can change that for agent commands: with Keep Failure Output on in suv settings (off by default), a failed Bash call keeps the last 4 KB of Claude Code's PostToolUseFailure error text, redacted. Had it been on during this run, selecting the 14:08 npm test in suv search --executor claude-code --failed and pressing Tab would show that text under an Output row, and an MCP-connected assistant asking "what failed in this project today?" would get its last three lines from what_failed, which groups recent failures by the prompt that triggered them. Output is never kept for commands typed in your own shell.
Without it, you still know the exact command, directory, and time, so rerun npm test in ~/src/shop-api and compare the result with the agent's final message. For a command that has failed before, the detail pane may also say Last time this failed, you fixed it with …, worked out from what ran next in earlier sessions. Treat that as a hint, not a cause.
Step 6: See which files the session changed
Since 0.5.1, a Claude Code session in a git repository gets a git snapshot at its first prompt and at each turn end: the commit, the branch, and which paths git status reports, never their contents. In suv sessions, the session's timeline header then reads something like Changed: 4 files, 0 commits, and the handoff scaffold (h) lists each path. An MCP client can call what_changed with the session's id to get the same list with provenance: observed. It shows what changed in the repository during the session, not necessarily what the agent itself changed, but it is enough to tell whether the agent touched the tests CI is now failing.
What gets captured, and what doesn't
An audit trail is only useful if you know its edges. Suvadu records what its hooks are told about; it does not watch processes independently.
Commands
- Dedicated integrations (Claude Code, Codex, Cursor, OpenCode, pi) record shell commands from the host's own shell-tool events. Claude Code and Codex hooks capture only the Bash tool; Cursor uses its shell-execution hook; OpenCode and pi use their bash tool events.
- Shell hooks record commands typed in an interactive Zsh or Bash session that has loaded
suv init zshorsuv init bash. IDE terminals such as VS Code, Cursor's terminal, Windsurf, and Antigravity are labeled by environment detection.
Prompts and sessions
- Prompt text is captured for Claude Code, Codex, Cursor, OpenCode, and pi, because those integrations receive prompt events. It is redacted and length-capped before storage.
- Full sessions, including assistant responses, observed models, and provider-reported token counts, are imported for Claude Code, Codex, and OpenCode. Claude Code and Codex import at the end of each turn; OpenCode imports when the session goes idle. Since 0.5.1 Cursor sessions are captured from its hooks too: prompts, each turn's final response, and the model, but no token usage, which Cursor does not report.
- Since 0.5.1, git snapshots of each session in a repository (paths and their status, never contents), on by default and turned off with Record Agent Git Snapshots.
- Environment-detected tools, such as Antigravity and IDE terminals, get executor labels but no prompts.
Not captured
- Non-shell tool calls. File reads, edits, searches, and web fetches made through an agent's own tools are not shell commands, so they don't appear as command records.
- Command output, by default. Stdout and stderr are not stored, except the opt-in end of a failed agent or
suv wrapcommand's output. Claude Code session import also excludes tool results, thinking blocks, attachments, and file contents. - Commands with no event. If the host doesn't emit a hook (for example, hooks not yet trusted in Codex, the agent not relaunched after
suv init, or a crash before the event fires), there is nothing to record. - Unhooked shells. A command run by an agent without a dedicated integration, in a non-interactive shell that doesn't load Suvadu's hooks, is not recorded.
- Commands inside commands. Suvadu records the command line that was run. If that line runs a script, the script's internal commands are not recorded separately.
- Child sessions. Sessions started by a sub-agent are not merged into the parent session's view. (Since 0.5.1 a forked or subagent Codex session is captured as its own session, linked to its parent.)
- Paused or excluded recording. Anything run while recording is disabled or paused, or matching one of your command exclusion patterns, is intentionally skipped.
Exit codes are only as precise as the host event. Claude Code reports failures through a separate event whose error message Suvadu parses; some Codex events carry no exit code, and those records show a neutral marker rather than a guessed code. If a command you expected is missing, run suv doctor to check the installed hooks, and see the compatibility matrix for per-agent gaps.
Auditing for risk
Visibility is step one; triage is step two. Suvadu's risk assessment flags destructive or sensitive commands — recursive deletes, hard resets, force pushes, installing a named package, piping the network into a shell — and since 0.5.1 rates each command of a chained or wrapped line on its own, so you can scan an agent's commands for the ones that deserve attention instead of reading every line. It is pattern-based and informational; it never blocks a command unless you wire suv guard into a hook yourself.
For a higher-level view, agent reports and the agent dashboard summarise recorded agent activity over a period: how many commands, how many failed, and which were flagged.
Replaying a session
When you need the chronological story of a single run, command replay walks through recorded commands in order, with directories and exit codes. For prompts and responses alongside the commands, use suv sessions.
Turning history into agent memory
Once your history knows about agents, a connected AI client can query it through Suvadu's local MCP server. The returned records are available to the client and may be sent to its provider under the client's settings.
The takeaway
AI agents run real commands with your permissions. With executor attribution, prompt context where the integration provides it, and risk flags, you can answer "what did it run, where, and did it fail?" from a local record, and you know which questions the record can't answer.
Next steps: Set up your agent integration → · Agent report reference →
Builder of Suvadu. Writes Rust, thinks about shell history more than most people, and believes developer tools should be local-first.