An operating system for the agents you already run.
A local console that reads two things you already have — your Claude Code transcripts and your markdown vault — and turns them into the dashboard you keep wishing existed: what you actually spent, what you actually worked on, and a short list of things worth doing about it. Then it lets you act on all of it: read and follow your notes, search every word you have written, and run the agent in any project without leaving the page.
Nothing is mocked. Every number on the screen is computed from a file on your disk, and the ones that can't be computed say so.
git clone https://github.com/openwarehq/atlas
cd atlas
npm install
npm run dev # http://localhost:4338That's the whole setup. It finds ~/.claude/projects and ~/ObsidianVault on
its own; point it somewhere else with ATLAS_VAULT and ATLAS_TRANSCRIPTS.
Never run Claude Code? Five empty panels look broken rather than empty, so there is a seeder:
node seed/demo.mjs
ATLAS_TRANSCRIPTS=./demo/transcripts ATLAS_VAULT=./vault-example npm run devThat is what the screenshot above is — a synthetic month across six invented projects. It is also how the screenshot avoids being a list of everything the author is building and what they paid for it, which is what this dashboard is when you point it at yourself.
| ⌘K | search every note — titles rank above bodies, matches are highlighted |
| click any note | read it rendered, with backlinks and outgoing links |
| click a wikilink | follow it, with a back stack, like a browser |
| ⌘2 | write a prompt, pick a project, run the agent, watch it stream |
| ⌘1 ⌘3 ⌘4 | command centre, vault, notices |
| a suggestion | expand it to see exactly which notes it means, and open them |
The Run tab spawns claude -p in a project and streams stdout back live. Three
things about that are deliberate:
- No shell.
spawngets an argv array, so a prompt containing;or backticks is one argument and not a second command. There is no string in that file that ever reachessh. - The working directory is an allowlist, not a parameter — it has to be a directory atlas already saw in your own transcripts, or the vault.
ATLAS_RUN=offturns the whole thing off. atlas binds to localhost, but behind a tunnel this route is remote code execution, and that switch is how you close it.
Stop actually stops: aborting the request kills the process, and closing the tab does too.
A dashboard sitting still reads as a screenshot, and a screenshot does not hold anyone for ten seconds. So there is a reel: the console builds itself, the headline figures count up to their real values, the charts draw in, and the suggestions arrive one at a time. Ten seconds, then a beat and a half of stillness — a reel that cuts on its final motion feels clipped.
2.9s in: the counters are still climbing — $165 on its way to $177, 89% on its way to 96% — and the charts are half drawn.
http://localhost:4338/?reel=1 play once
http://localhost:4338/?reel=loop loop, for recording
http://localhost:4338/?reel=portrait loop, framed for a 9:16 crop
⌘⌥⇧A starts or stops the reel without a reload, so triggering it never
appears in the take. Esc cancels.
⌘⇧A fires it too, but Chrome binds that to Search Tabs at the browser level
where a page cannot intercept it — so Option is the one to use while recording.
The browser has no claim on that chord.
Portrait mode is the one worth knowing about. A wide console cropped to 9:16
is a wall of six-pixel type, so ?reel=portrait drops the side rails, stacks the
paired panels into one column, and scales the figures that carry the shot up to
44px. Record the browser at 1080×1920 and it needs no cropping.
Nothing in the reel is fabricated. Every number it counts towards is the number that was already on screen — the motion is the only thing being added.
Your transcripts. Claude Code writes one JSONL record per API request, and
every one carries a usage block. atlas parses all of them — 35,000 turns in
about four seconds cold, 40ms warm — and gets you token spend by day and by
model, cache hit rate, hour-of-day working pattern, longest sitting, and what
the whole thing would have cost at list price.
Your vault. Any folder of markdown. Wikilinks resolved by basename the way Obsidian does it, so the note count and link count agree with what Obsidian shows you. Out of that: folders, backlinks, orphans, broken links, and a local graph.
Both are read-only. atlas writes exactly one file — atlas.db — and the only
things in it are which suggestions you dismissed.
Real pixel art, generated — and then made real arithmetically, because asking a model for pixel art gets you a smooth illustration of pixel art. The render is box-averaged down onto a 200-cell grid, every cell quantised to a fixed 25-colour palette taken from this console's own CSS variables, then scaled back up with nearest-neighbour so each cell is a hard square. 21 colours survive.
Averaging on the way down matters: nearest-neighbour point-samples one arbitrary pixel per cell, which on a dithered source picks the checkerboard at random and turns the whole image into noise.
The result is 18KB instead of 1.1MB, and image-rendering: pixelated keeps the
browser from smoothing the edges back off. Swap it by replacing
public/banner.png — 5:1 fits the hero exactly.
Sittings, not sessions. A Claude Code session id survives --resume, so one
id can span three weeks. Reporting that gives you "longest session: 58h 29m",
which is the age of an identifier, not a fact about your day. atlas cuts turns
into sittings at any gap over thirty minutes. The same fix makes the session
list useful instead of showing four rows with a thousand turns each.
An unpriced model is never priced at zero. Models come out faster than public price lists get updated. A model with no rate is excluded from the cost figure and counted, and the console tells you how many turns that was. A model matched only by family prefix is flagged as inferred. The headline is either right or visibly incomplete — it is never quietly short.
Cache is priced at three different rates. A read is a tenth of a fresh input token; a five-minute write is 1.25x; a one-hour write is 2x. The transcript breaks the two writes out separately, so atlas prices them separately. This is most of why the number is what it is.
Confidence is computed, not assigned. Every suggestion shows the numbers it
came from. 0.95 ESCALATE · 2 blocked notes · oldest 43d is a rule reading your
frontmatter, not a model's opinion. And when no rule fires, the column is empty
— an empty suggestions panel is a real answer, and padding it with filler is how
you train someone to stop reading it.
- It doesn't schedule anything. Runs happen when you press Run. There is no cron, no queue, no background agent.
- It doesn't write to your vault. The reader is read-only; the one file
atlas writes is
atlas.db. - Context window is inferred, not read — the transcript doesn't record it. atlas picks the smallest standard window your week's peak actually fits in.
- Prices are list prices as of writing and live in
src/lib/pricing.ts. Verify them against the published rates before you quote anyone. - "List price" is not your bill. On a subscription the marginal cost of a turn is zero. It answers "what would this have cost on the API", which is the only version of that question with an answer.
- One machine. No sync, no accounts, no telemetry, no network calls at all.
| variable | default |
|---|---|
ATLAS_VAULT |
~/ObsidianVault, then ~/Documents/Lattice, then vault-example/ |
ATLAS_TRANSCRIPTS |
~/.claude/projects |
ATLAS_DB |
./atlas.db |
PORT |
4338 |
ATLAS_RUN |
on — set to off to disable running agents entirely |
ATLAS_AGENT_BIN |
claude — the binary the Run tab spawns |
Docker, with both sources mounted read-only:
ATLAS_VAULT=~/ObsidianVault docker compose up --buildnpm test # 164 tests
npm run typecheck
npm run buildThe tests are on the parsing and the arithmetic, because that is where being wrong is invisible — a mis-bucketed day or a double-counted resumed session looks exactly like a correct dashboard.
MIT.


