Sessions and the graph

Claude Code writes one JSONL transcript per session to ~/.claude/projects/<project>/<session-id>.jsonl. Smalti reads these files. It never writes to them.

Indexing

At launch Smalti loads its cache and then scans the projects folder. It rescans every four seconds. A transcript is parsed only if it is new or its size or modification time changed since the last scan. For each session it records:

  • the working directory, whose last path component is used as the project name;
  • the title: Claude Code's generated title if there is one, otherwise the first prompt, otherwise Untitled session;
  • the number of user and assistant messages;
  • files edited, from Edit, Write, MultiEdit and NotebookEdit tool calls;
  • files read, from Read tool calls.

Only lines that can contain these fields are decoded as JSON, so large transcripts are cheap to index. The results are cached in ~/Library/Application Support/Smalti/sessions-v3.json.

Sessions are grouped by project. Projects are ordered by their most recent session, and sessions within a project are newest first. Each row shows the title, the time since the transcript last changed, the message count and the number of files edited. The header shows the total number of sessions and how many are live. The filter field matches titles and project names.

A session is live (green dot) if a terminal panel is running it or its transcript changed in the last 90 seconds.

Clicking a session focuses the terminal panel it is running in. If none is, Smalti opens a terminal in the session's working directory and runs claude --resume <id>. The context menu also has Highlight in Graph, Copy Session ID and Reveal Transcript in Finder.

Each project has a colour, chosen from a ten-colour palette by a hash of the project name, so it stays the same between launches. The same colour is used in the sidebar and the graph.

Attention: what needs you

Once a second Smalti works out a state for every terminal panel running Claude, from the process and its transcript:

StateMeaningRing
WaitingClaude is running, its transcript has not changed for 20 seconds, and it changed after you last looked at the panel. Usually a question or a permission prompt.Amber
DoneClaude exited since you last looked at the panel.Green
CI failedA CI panel's latest run failed.Red
WorkingThe transcript changed in the last 20 seconds.None
IdleAnything else.None

Waiting, Done and CI failed count as needing you. They are listed under Needs you at the top of the sidebar, and a chip at the bottom of the canvas shows how many there are. ⌘J jumps to the next one, most urgent first, then oldest first. Looking at a panel clears it until something changes. The state is inferred from the transcript, so Smalti cannot yet tell a permission prompt from a question; the planned smaltid daemon will get this from Claude Code hooks instead.

The graph

The graph has three kinds of node:

  • Projects, one per working directory, drawn in the project colour.
  • Sessions, linked to their project. A session's radius grows with the square root of its message count. Live sessions have a green ring.
  • Files, linked to every session that edited them. A file touched by more sessions is drawn larger, and files touched by three or more are drawn lighter.

The chips in the top-left corner show or hide each kind of node. Reads adds files that sessions only read, which makes the graph much larger.

The layout is a force simulation: nodes repel nearby nodes, links pull towards a fixed length (60 points from a project, 40 otherwise), and a weak force pulls everything towards the centre. The simulation runs to rest before the first frame is drawn, so the graph appears already laid out. New sessions are added near their project and the layout warms up just enough to fit them in.

Hovering a node highlights its links in the accent colour and fades everything else. Labels are shown for projects at all times, for sessions when zoomed in, and for heavily shared files. The status line at the bottom shows counts, or details of the hovered node.