Rewind file changes with checkpointing
Every file the agent writes is tracked. The SDKs surface those writes as `file_modified` events; the CLI records them on disk so you can review or revert later. This page covers both layers.
How tracking works
Whenever the agent calls a write tool (`write_file`, `edit_file`, `apply_patch`, or any custom MCP tool that reports a modification), the CLI emits a `file_modified` event over JSON-RPC. The CLI also stores a record of the change under `~/.autohand/runs/<run-id>/` so the change can be inspected after the fact.
The SDK's job is to surface those events to your app; the CLI's job is to persist them.
Observe file modifications in real time
Match `file_modified` events while you stream a prompt. Use them to invalidate caches, refresh editor buffers, or track what the agent changed for an audit log.
TypeScript
const modified = new Set();
for await (const event of sdk.streamPrompt({ message: prompt })) {
if (event.type === 'file_modified') {
modified.add(event.path);
editor.refresh(event.path);
}
if (event.type === 'agent_end') {
console.log('Files touched:', [...modified]);
}
}
Python
modified = set()
async for event in sdk.stream_prompt(prompt):
if event["type"] == "file_modified":
modified.add(event.get("path"))
editor.refresh(event.get("path"))
if event["type"] == "agent_end":
print("Files touched:", sorted(modified))
Go
modified := map[string]struct{}{}
for event := range events {
switch e := event.(type) {
case autohand.FileModifiedEvent:
modified[e.Path] = struct{}{}
editor.Refresh(e.Path)
case autohand.AgentEndEvent:
fmt.Println("Files touched:", modified)
}
}
Java
Set<String> modified = new HashSet<>();
sdk.streamPrompt(new PromptParams(prompt), event -> {
if (event instanceof Events.FileModifiedEvent fme) {
modified.add(fme.path());
editor.refresh(fme.path());
} else if (event instanceof Events.AgentEndEvent) {
System.out.println("Files touched: " + modified);
}
});Revert with git
The simplest, safest revert path is git. Run inside a clean working tree (or commit before each agent run) so you can `git restore`, `git stash`, or `git reset --hard` if the agent goes off the rails.
bash
# Snapshot before the agent runs
git add -A && git commit -m "pre-agent snapshot"
# Run the agent
bun run my-agent.ts
# If unhappy, revert
git reset --hard HEAD@{1}If you let the agent commit on your behalf, restrict it to its own branch:
bash
git switch -c agent/$(date +%s)
bun run my-agent.ts
# Review the diff before merging back to main
git diff main...HEADInspect run history with the CLI
The CLI keeps a per-run history under `~/.autohand/runs/`. Use it when you need to inspect what an agent did across multiple sessions, including ones that did not commit to git.
bash
# List recent runs
autohand runs list
# Show every file the run wrote
autohand runs show <run-id> --files
# Restore a single file from a run
autohand runs restore <run-id> src/auth.tsThe exact subcommands depend on your CLI version. Run `autohand runs --help` to see what is available locally.
Best practices
- Run agents inside a clean git working tree so revert is one command away.
- Stream `file_modified` into your editor / IDE integration so users see changes the moment they happen.
- For unattended runs, write `file_modified` paths to an audit log alongside the run ID. That pair lets you tie back any change to the agent and prompt that produced it.
- Do not rely on CLI run history for compliance. Treat it as a developer convenience; treat git (or your VCS of choice) as the source of truth.