I usually have three to five Claude Code sessions running at once. One is fixing a bug in the backend. One is babysitting a build. One is reviewing a PR. One is writing a migration. Until a few weeks ago I was tracking all of them in VS Code's terminal tabs — alt-tabbing back and forth, hunting for the one that was actually waiting on me, and missing the moment one of them finished and went idle.

So I built Glance.

Glance hero — multi-session Claude Code agent panel for VS Code

Glance is a VS Code extension that runs your Claude Code sessions side-by-side in a sidebar panel. Each session gets a card. The card shows what the agent is doing, how far along it is, and whether it needs you. When something is waiting on you, the activity bar lights up. When a background turn finishes, you get a toast.

It's free, MIT-licensed, and on the Marketplace as hamzawaleed.glance-claude-code.

The problem I actually had

The real pain wasn't running multiple sessions — that part Claude Code handles fine. The pain was knowing which one to look at. Terminal tabs are silent. They don't tell you "this one finished" or "this one is asking for permission." So I'd get pulled into one session, lose context on the other four, and come back to find two of them had been idle for ten minutes.

I tried scripting around it — checking transcript files, scraping shell output, parsing emoji markers I'd had Claude print into the terminal. Everything I built was brittle. Terminals lie. Buffers wrap. ANSI escapes get in the way.

The version that actually works does something different.

What a card shows

Anatomy of an agent card — title, TL;DR, progress bar, status stripe, model chip, kill

Every card carries five fields:

No log scraping. No regex over emoji. The agent itself reports its state — and that's the entire trick.

The trick: Claude updates its own card

Glance ships a tiny MCP server that exposes one tool: update_state. The extension launches Claude with --mcp-config pointing at this server, plus a system prompt that says, in roughly these words:

After EVERY response — short, long, trivial, planning, mid-tool-chain — your LAST action MUST be a call to update_state, with ALL FIVE fields populated.

Claude follows the instruction. The tool writes a small JSON payload to a per-agent state file on disk. A chokidar watcher in the extension picks up the file change, diffs against the last known state, and pushes a partial update to the React webview. The card re-renders.

That's the whole pipeline:

Claude tool call
  → MCP server writes JSON to state/<agentId>.json
  → chokidar watcher fires
  → extension diffs and posts agentUpdate to the webview
  → card re-renders

I tried the obvious alternative first — --append-system-prompt — but the shell echoed the prompt into the visible terminal on launch, which was awful. Returning the instruction in the MCP server's initialize response keeps it invisible.

Three runtimes per agent

Behind every card, three things are running:

  1. The extension host. Owns the agent list, the global hook watcher, and the on-disk persistence. Spawns each Claude session.
  2. The Claude process. Runs in a node-pty child shell, wrapped in a vscode.Pseudoterminal so VS Code owns the scrollback. From the user's perspective, it's a normal terminal — you type, Claude responds, history scrolls.
  3. The MCP server. A small Node script bundled with the extension, the only path Claude uses to mutate the card.

Hook events flow on a separate channel. Stop, UserPromptSubmit, Notification, and SessionStart are wired to a short Node script that drops JSON files into a watched directory. The extension reads those to know when a turn started, when it finished, when you /clear'd the session, when you /compact'd it.

The activity-bar badge — the little count that says "two of your agents need you" — is a derived count of agents.where(needsInput || error). It recomputes on every update, never tracked incrementally, so it can't drift.

Activity-bar count badge appears when an agent is waiting on you

Sessions stick around

Close VS Code. Reopen it. Every card is still there. Reload-the-window doesn't kill them either.

The trick is that cards render from the last-known state immediately (it's just a JSON file on disk), but the actual Claude process isn't restarted. When you click a dormant card to focus it, Glance shells out to claude --resume <sessionId> to revive that specific session. Until you click, it costs nothing.

Agents you spawned but never prompted are filtered out — they don't have a transcript on disk yet, so claude --resume would fail with "no conversation found." Only agents that actually got a first prompt are persisted.

Toasts you don't mind

VS Code information toast showing an agent's name and the reason it needs you, with a Show button

When a background turn finishes — meaning you're not actively looking at that agent's terminal — Glance fires a native VS Code toast. The toast carries the agent's name and the latest TL;DR or needs-input reason. A Show button on the toast jumps you straight to that terminal.

The toast is suppressed when you are already looking at the agent's terminal. No "your work is done" ping for work you're staring at.

There's a short audio tone too, configurable.

What I learned building it

A few unobvious things:

Try it

If you spend any real time in Claude Code and have ever lost track of which terminal was doing what:

Source is on GitHub at hamzawaleed0102/glancer-vscode. Issues and PRs welcome.