mahfujmustafa.dev / projects

Inspect Workflow

Developer tool, July 2026. Code on GitHub.

Inspect Workflow is a local viewer for Claude Code transcripts. You point it at a folder of .jsonl logs and read each one as a conversation instead of a wall of JSON, with subagent and workflow runs grouped by the folder they ran in. It also follows transcripts that are still being written, so you can watch a run grow in place.

The whole thing is server.py, which only uses the Python standard library, and index.html. There's nothing to install beyond Python 3.7 or newer.

Why it's harder than it looks

Reading a finished log is easy. Following a live one cheaply, across hundreds of transcripts, is where the work went. A background thread rescans the watched folders every second and pushes changes to the page over server-sent events on /api/stream. When a transcript you have open changes, the page asks for everything after the last byte it already has instead of reloading the file, so a run that's still writing grows in place.

The sidebar has to stay quick too. The server only parses the first 200 KB of each file as JSON, because that's where the prompt, model and working directory are, and it caches each agent until its file's size or modified time changes. By default it keeps the newest 300 transcripts.

Then there's the folder problem. When you drag a folder onto the page, the browser only gives the server its name, not its full path. So the server looks for a folder with that name under ~/.claude/projects (or whatever roots you pass on the command line) and picks the most recently changed match. If the name isn't found, the page reads the .jsonl files itself and opens them read-only without watching anything.

Reading a transcript

Every row in the sidebar is one agent. The server guesses a title from what the run did, usually the file it wrote to most. If it didn't write anything, it falls back to a task line in the prompt, a file the prompt mentions, or the prompt's first sentence. When the guess is bad you can double-click the row to rename it, and the name is saved in the browser's local storage.

Above the transcript are toggles for each kind of event: thinking, tools, results, answer and prompt. Thinking, answer and prompt start on. Tools and results start off because they're a lot of noise unless you're tracking down a specific call. The filter box hides events that don't contain what you type and highlights the matches in the rest.

Under the toggles is a strip with one bar per event, scaled by length, so you can see the shape of the whole run at once and click a bar to jump to it. With follow on, which is the default, the view stays at the bottom unless you've scrolled up, and a dot next to an agent means its file changed in the last 90 seconds.

Staying local

The server listens on 127.0.0.1 only and the page doesn't load anything from the internet. The one file it ever writes is containers.json, the list of folders you told it to watch, so they're still there the next time you start it. It never writes to a transcript. The port, the transcript cap and request logging are set through three environment variables.

Stack

Python standard library on the server, with threading for the rescan and server-sent events for the push. The client is a single HTML page with its own JavaScript and no outside requests.

See the code on GitHub, or go back to mahfujmustafa.dev and the rest of my projects.