Make AI agent scrollback readable.
Automatic source detection · Structured document model · Layered diff rendering · Tree-sitter highlighting
agentlog.nvim turns terminal scrollback produced by AI agents - Codex, Claude Code, and Cursor Agent, opened from Zellij - into a structured, navigable Neovim buffer without changing its original text.
I spend a lot of time running AI coding agents - usually Codex, Claude Code, and
Cursor Agent - inside Zellij. When I need to jump back through their history or
copy part of an agent's output, I prefer to stay on the keyboard: Ctrl+S, then
E, opens the active pane's scrollback in my default editor, Neovim.
That workflow is fast, but raw agent scrollback is not especially pleasant to read. Prose, tool calls, code, and diffs all compete for attention in what is ultimately a plain-text buffer. I wanted the output to become structured and syntax-highlighted as soon as it reached Neovim, while keeping the original text untouched. agentlog.nvim grew out of that need: preserving Zellij's quick, mouse-free workflow while making AI agent output much easier to revisit, navigate, and copy.
- Neovim 0.10 or newer
- A Tree-sitter parser for each language you want highlighted inside diffs
Using lazy.nvim:
{
"illegalstudio/agentlog.nvim",
dependencies = {
"nvim-treesitter/nvim-treesitter", -- Optional; enables immediate code highlighting.
},
opts = {
-- auto_attach = true, -- Uncomment to enable automatic attachment.
},
}Calling setup() is optional. The commands are available with the default
configuration as soon as the plugin is on runtimepath.
Open a scrollback dump in Neovim. For example, in Zellij, focus the pane running
the agent, press Ctrl+S to enter scroll mode, then press E to open that
pane's scrollback in your default editor. Once the buffer is open, run:
:AgentlogAttachThe command detects the matching Codex, Claude Code, or Cursor Agent adapter from the buffer.
The initial commands are:
:AgentlogAttach- parse and render the current buffer;:AgentlogRefresh- rebuild the document and its decorations;:AgentlogDetach- remove decorations and restore the previous filetype;:AgentlogNext {action|diff|error|file|hunk|response}- jump to the next region;:AgentlogPrevious {action|diff|error|file|hunk|response}- jump backward;:checkhealth agentlog- inspect the local runtime.
Automatic attachment is off by default:
require("agentlog").setup({
-- auto_attach = true, -- Uncomment to enable automatic attachment.
adapters = {
claude = { enabled = true },
codex = { enabled = true },
cursor = { enabled = true },
},
render = {
diff_background = true,
diff_code_padding = 1,
},
syntax = {
enabled = true,
treesitter = true,
max_region_lines = 500,
},
navigation = {
wrap = true,
},
mappings = {
enabled = true,
next_action = "]a",
previous_action = "[a",
next_diff = "]d",
previous_diff = "[d",
next_response = "]r",
previous_response = "[r",
next_file = "]f",
previous_file = "[f",
next_error = "]e",
previous_error = "[e",
next_hunk = "]h",
previous_hunk = "[h",
open_file = "gf",
},
})When enabled, automatic attachment considers only *.dump files with a strong
Codex, Claude Code, or Cursor Agent signature and enough independent evidence. Set
vim.b.agentlog_disable = true before BufReadPost to opt a buffer out.
Attached buffers receive these buffer-local normal-mode mappings by default:
| Mapping | Action |
|---|---|
]a / [a |
Next/previous agent action |
]d / [d |
Next/previous changed diff block |
]r / [r |
Next/previous assistant response |
]f / [f |
Next/previous recognized file occurrence |
]e / [e |
Next/previous error or warning |
]h / [h |
Next/previous explicit unified-diff hunk |
gf |
Open the recognized file under the cursor |
Counts are supported, so 3]a jumps forward three actions. Navigation wraps at
the buffer boundaries by default and records jumps so regular jump-list motions
can return to the previous location. A diff containing several rendered rows is
one destination; read-only source previews are not treated as changes.
File navigation still includes those read-only previews and groups all rows from
one structured file operation into a single destination.
Hunk navigation visits each @@ … @@ section within a unified diff. Compact
Codex, Claude, and Cursor previews do not expose hunk headers, so agentlog does
not invent extra boundaries for them.
Mappings are installed only when the same key is not already mapped locally in
the attached buffer, and detach removes only mappings installed by agentlog.
Set mappings.enabled = false to disable all defaults, or replace any individual
mapping in setup(). gf falls back to Neovim's native behavior when the cursor
is not on a file recognized by the active adapter. Relative references are
resolved against the session workspace root when available, then the current Git
root and working directory. Recognized line and column coordinates position the
cursor after opening the file. When Cursor reports only a filename and direct
resolution fails, agentlog searches that filename recursively from the project
root. One match opens immediately; multiple matches are presented through
vim.ui.select with project-relative labels.
For Codex Edited, Added, and Deleted blocks, Claude Update blocks, and
Cursor Edited previews, agentlog separates display prefixes and diff markers
from the source, infers the language from the file path, and parses normalized
old and new snapshots. Claude Write previews receive the same syntax
highlighting without inventing a diff marker or visual padding. If a parser or
highlight query is unavailable, structural highlighting continues to work.
diff_code_padding inserts virtual screen cells, so the extra spacing never
changes copied text. Cursor previews preserve their native separator spacing and
ignore this option.
Cursor Agent's plain scrollback does not retain an explicit marker for every prompt and response. The adapter recognizes turn boundaries supported by the banner and surrounding tool activity, and deliberately leaves ambiguous later turns neutral.
doc/agentlog.txtis the Neovim:help agentlogmanual.docs/contains internal architecture and development documentation for maintainers.
Run the headless test suite with:
make testReal scrollback fixtures must be anonymized before being committed. See the
fixture notes under tests/fixtures/.
The immediate next milestone is to expand the anonymized Codex, Claude, and Cursor fixture corpus, then add folding, copying, and coverage for additional output variants.
MIT © 2026 Vincenzo Petrucci. See LICENSE.
