Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

270 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

agentglass

agentglass

A loupe for your agents β€” a real-time Mission-Control dashboard and workspace for AI coding agents, across every provider and every project on your machine.

β–Ά Live demo

stack ui workspace desktop themes license

agentglass in action β€” the live cockpit, then the workspace: source control, diff review, Docker and chat, one keystroke away

the cockpit β€” every session, spend, throughput, tool mix and what needs you

Point any AI coding agent at agentglass β€” via Claude Code hooks or any OpenTelemetry GenAI exporter (OpenAI Codex, Gemini CLI, Bedrock, LangChain, LiteLLM…) β€” and watch every agent, tool call, token, and dollar move in real time. Cost tracking, tool-latency percentiles, error timelines, session lifecycles, a filter-the-whole-cockpit-by-provider switch, and 22 themes. It persists across reloads (unlike a pure in-browser stream).

And it's not just a viewer. agentglass carries a full workspace in the same cockpit β€” the idea is simple: browser, terminal, IDE panels, agent telemetry… all in one place. A syntax-highlighted diff viewer for everything the fleet changed, a lazygit-style source-control panel (stage, commit, push), a lazydocker-style Docker panel (containers, logs, stats), a real terminal (an actual PTY shell on your machine, not an emulation), and a chat panel that drives local Claude Code sessions β€” all behind one keystroke, under a notch that mirrors your desktop notifications so nothing is lost while you are fullscreen. Ships as a native desktop app, server included.

The full cockpit running on fabricated sample data β€” a simulated live event stream, populated radar, spend charts, and even the control-plane approve/deny gate. No install, no server. (Everything there is fake; it's a showcase.)


Contents


Every project, one cockpit

agentglass watches every Claude Code session on your machine β€” you don't launch it per-repo. Alongside the live hook stream, a transcript scanner reads ~/.claude/projects directly, so history from every project is there the moment you open the dashboard, and new sessions tail in live (deduped against the hooks, so nothing is double-counted).

Want to focus? Scope the whole cockpit to a single project β€” only that repo (and its worktrees) show up, and its git / terminal / chat panels, diffs, and spend are all you see. The natural way is the in-app project picker: on first open (a desktop app has no "current folder", so it asks) you choose what this cockpit is about, and the βŒ‚ name in the header switches it any time:

  • pick a project β†’ that repo and its worktrees, nothing else;
  • pick a folder your projects live in (e.g. ~/code) β†’ every repo from that folder inward;
  • pick Whole machine β†’ no scope at all.

The choice is applied live and persisted (root in ~/.config/agentglass/config.json), so the next launch opens straight into it. It can also be set from outside:

AGENTGLASS_ROOT=~/code/my-project bun run dev
# desktop:  make desktop-open DIR=~/code/my-project

Leave everything unset and it covers the whole machine. You can also pin the repo sweep to specific directories via AGENTGLASS_REPO_DIRS, or the same config file:

{ "root": "~/code/my-project", "repoDirs": ["~/code", "/mnt/hdd/code"] }

More than a dashboard β€” a workspace

Watching is only half of it. agentglass grew a set of lazygit / lazydocker-style panels β€” plus a real terminal and a Claude chat β€” that live right in the cockpit, so you can go from seeing what the fleet did to acting on it without leaving the tab. Keyboard-driven, and they wear the same 22 themes.

They live in one workspace: Ctrl+\ (⌘\) opens it over the dashboard, a rail down the left switches between the six views, and Esc puts you back. Every view has the same fixed-height title bar and the same list width, so switching changes the panel and nothing else moves.

Two kinds of shortcut, because they answer different questions. On the dashboard, bare letters jump straight in β€” g d p o t c. Inside the workspace every keystroke belongs to whatever has focus, usually a shell, so navigation there carries a modifier: Ctrl+1–Ctrl+6 for the rail in order, Ctrl+[ / Ctrl+] to cycle. Both sets are rebindable in Settings β–Έ Shortcuts, and the modified one takes any combination you like β€” Ctrl+Alt+J is recorded exactly as you hold it.

settings β€” every shortcut, rebindable, with the key that works anywhere beside the one that works on the dashboard

Drag the rail to reorder it. Put the terminal at the bottom if that is where your thumb goes; Ctrl+1–6 follow your arrangement, so the tooltips never start lying. Drag the seam beside any list to resize it β€” every view shares that width, and it is remembered.

πŸ”” The notch β€” what is happening, above everything

A strip across the top of the workspace, always visible, never themed: true black so it disappears into the bezel of an OLED display.

It carries what you would otherwise go looking for β€” commits to push and to pull, live shells, sessions waiting on you, CPU and memory, a seven-segment clock β€” and it mirrors desktop notifications, so the Slack message that arrives while you are fullscreen is not lost. Click it and it opens downward into an inbox with the full text; click again and it closes. Capability-probed on the server, so a platform without a notification bus gets a notch with no inbox rather than a broken one.

Notifications are off by default and opt-in per level in Settings β–Έ Preferences β€” titles only, or the full body.

πŸ”¬ File changes β€” a syntax-highlighted diff & review workspace Β d

Every Edit/Write the fleet makes, gathered into one reviewable, chaptered list. Shiki syntax highlighting composed with a word-level intra-line diff, split or unified, ligatures and a per-diff theme, "reviewed" check-offs β€” plus one-click ✦ Explain (a local-Claude walkthrough of the whole change set) and Commit… to turn a review straight into a commit.

diff viewer

🌿 Source control β€” lazygit, in the dashboard Β g

A live view of any repo's working tree (repos are discovered from the fleet's own file paths). Stage / unstage / discard, interactive hunk staging, a commit composer, branches (checkout / create / delete), log, reflog, remotes, tags, worktrees and stashes β€” plus push / pull / fetch. Keyboard-driven (j/k move Β· s/u stage Β· x discard Β· 1–8 jump to a tab) and write-gated, so it's read-only until you opt in.

It also does the three things you would otherwise drop to a terminal for:

Sync from base. Pull main into the branch you are on, from the header, with the count of what is waiting. Disabled while the tree is dirty β€” merging over uncommitted work is how you lose it.

Resolve conflicts. Conflicted files are listed as what they are β€” files git has stopped in the middle of, not ordinary edits β€” so you cannot commit one with <<<<<<< still in it. Take a whole file's ours/theirs for the lockfile case, or open it one by one and choose a side per conflict block, with both versions side by side and the common ancestor when git recorded one. Nothing is written until every block has an answer, because defaulting the ones you did not read is exactly how a merge quietly eats somebody's work.

Undo the merge, while that is still exactly reversible β€” only when nothing is committed on top and nothing is pushed. If either is true the button explains why instead of offering you a lie.

source control panel

πŸ”€ Pull requests β€” review one without opening a browser Β p

Every open pull request across the repos the fleet touches, filtered by mine / waiting on your review / all, each row carrying its checks rolled into one dot (hover for passed Β· failed Β· skipped Β· running) and a here chip when this checkout is on that branch.

Open one and it has overview Β· conversation Β· commits Β· files Β· checks Β· review. The diff is the app's own viewer β€” the same SplitDiff / UnifiedDiff the file-changes panel uses, keybindings and all, rather than a second implementation that drifts β€” and it reads per file or per commit, with merge commits marked as the trunk catch-ups they are so you do not review them as work.

The conversation is mostly machines, so it is not one list. On a real review, four issue comments were all from CI, one coverage table alone was 46,551 characters, and the single human review that actually blocked the merge sat last, under all of it. It reads in three lanes instead β€” humans, line threads, automation β€” and the machine lane collapses to a digest. Everything a person wrote renders as real markdown at a reading measure, because prose set to the full width of a 2000px window is unreadable however correctly it is formatted.

Reviews work the way GitHub's do. Line comments queue as drafts (a pending chip counts them) and go up together as one review β€” approve, request changes or comment β€” so a half-finished review never lands in someone's inbox a line at a time. Threads belong to the review that opened them, are anchored to the code they are about, link out when you do want the browser, and the app declines to let you approve your own pull request.

Nothing blocks on the network: gh costs a second or more per call and the server has one thread, so every read is a cached answer that shows its own age.

🐳 Docker β€” lazydocker, in the dashboard Β o

Containers, images, volumes and networks in one stacked column whose headers never leave β€” so "is anything dangling?" is answerable without navigating away from the container you are watching. Containers group by compose project with live CPU / memory in aligned columns, and a dense toggle drops the image line when you would rather fit more on screen.

Select one and the pane beside it carries logs Β· info Β· env Β· config Β· top, with the logs coloured by level and pinned to the bottom while they stream. exec drops you into a shell inside that container β€” in the console already docked below, so your history and any running job survive it. Start / stop / restart / rm per container, or across a whole compose project at once, with each bulk action hidden when it would do nothing. Same keyboard-first feel, same write-gate.

docker panel

β–Ά Terminal β€” a real shell, in the dashboard Β t

Not a command-runner imitation: the server opens your login shell inside a real PTY (xterm.js in front, a pseudo-terminal behind a WebSocket), in any repo/worktree the fleet has touched. Job control, Ctrl+C / Ctrl+R, tab-completion, colors, vim / htop / lazygit β€” everything a local terminal does. Sessions are per-repo and persistent: close the panel mid-build, reopen later, the job is still running with scrollback intact.

The βš™ commands menu makes every project command self-explanatory and one click away β€” and it covers the whole selected project, not just its root: Makefile targets with their descriptions (from ## comment annotations or the # comment above each target) plus package.json scripts, discovered in the repo root and its subfolders. A monorepo's nested commands come out ready to run (make -C api test, bun run --cwd web dev), grouped by folder in the menu, each with the right runner (bun / npm / pnpm / yarn) detected from that folder's lockfile. agentglass's own Makefile is annotated this way β€” make help prints the same list in the shell.

Run tmux in it and the panel adopts its windows as its own tabs. The list comes from tmux, the pixels come from agentglass: click to switch, + for a new window, double-click to rename, and tmux's own status line steps aside (one click brings it back, and it is restored when the panel closes). Nothing about the keyboard changes β€” ^b c, ^b n, ^b 2 and everything else still go straight to tmux, and the tabs follow. The point is that the window list stops being the one strip of the workspace themed by whichever .tmux.conf the machine happens to carry.

terminal panel

πŸ’¬ Chat β€” drive Claude sessions from the browser Β c

Multi-chat against your local claude CLI: pick a repo/worktree, a model, and a permission mode (plan β†’ default / acceptEdits β†’ bypass), then converse β€” replies stream in, tool calls appear as chips, and follow-ups resume the same session. Sessions you start here show up in the fleet like any other agent.

↩ resume picks up a session that already exists β€” including one you started in a terminal β€” with its full context intact. Sessions that are still running are listed but can't be picked: a claude session has a single owner, and a second writer on the same transcript corrupts its history.

What a session shows: the conversation is a timeline, not only a chat log. Tool runs interleave with messages, each tool card carries the head of its output (so a failing test is distinguishable from a passing one without leaving the panel), and subagents report the parent's session id β€” click one to filter the thread to it. Images are sent to the model as image blocks; other files are quoted into the message. Type / in the composer to list and insert skills/commands (slash commands are enabled in -p, they just weren't discoverable).


chat panel

Why

agentglass is a visibility layer, not a harness: it doesn't run your agents or impose a workflow on them β€” it shows you what they're actually doing, and puts the controls (diff, commit, terminal, docker) next to what it shows. Everyone's harness is their own; the missing piece is seeing through it.

Most agent dashboards show a live event feed and forget everything on refresh. agentglass adds the layer that actually answers "what did this cost, what's slow, what's breaking, and how much of my plan is left?" β€” across every provider and every project, wrapped in a fast, animated cockpit.

Feature What you get
πŸ›° Mission-Control cockpit Mission clock, live throughput, tool-mix, a sweeping agent radar (distance from centre = context window used β€” a blip at the edge is about to compact), plain-English event stream, and a "what needs you" alert center.
πŸ—‚ Every project, machine-wide A transcript scanner reads every Claude Code session on the machine β€” history is there on open, new sessions tail in live. Or scope the whole cockpit to one project (or one folder of projects) with the in-app picker.
πŸ–₯ Native desktop app Own window + icon and a self-contained bundled server β€” nothing to run in a terminal. Launch-at-login toggle, attaches to a running server instead of duplicating it (Linux).
πŸ”¬ Diff & review A real diff viewer for everything the fleet changed β€” Shiki highlighting + word-level diff, split/unified, AI Explain, and commit-straight-from-review.
🌿 Source control lazygit in the cockpit: stage, hunk-stage, commit, branch, stash, push/pull β€” live on any repo the fleet touched (write-gated).
🐳 Docker lazydocker in the cockpit: containers by compose project, live stats, a log viewer, start/stop/restart (write-gated).
β–Ά Real terminal A true PTY shell (your login shell) per repo/worktree over a WebSocket β€” persistent sessions, plus a described, ready-to-run list of every Makefile target & package script across the whole project, grouped by folder.
πŸ’¬ Claude chat Drive local Claude Code sessions from the browser β€” model + permission-mode picker, streamed replies, resumable sessions that appear in the fleet.
πŸ’° Cost & tokens Per-event, per-session, per-model USD from a tunable pricing table (input / output / cache-write / cache-read).
⏱️ Tool latency PreToolUseβ†’PostToolUse pairing β†’ real p50 / p95 / max per tool.
πŸ“Š Persistent analytics SQLite-backed. /stats over any time window survives reloads and restarts.
🌐 Any provider Claude Code hooks plus an OpenTelemetry OTLP receiver (gen_ai.* spans). Provider is auto-detected from the model β€” then filter the entire cockpit (cost, tools, latency, sessions, radar…) by provider.
πŸ€– Per-model breakdown Cost & token split across every model β€” Claude, GPT, Gemini, and more β€” from a tunable pricing table.
🧡 Session lifecycle Timeline of every session: startβ†’end, duration, tokens, cost.
πŸ“ˆ Anthropic plan usage 5-hour + weekly plan-limit meters β€” shown only when you're viewing Anthropic (the one provider with a usage API), on wide screens.
⌨ Command palette + shortcuts Ctrl-K to filter, switch theme, change window, export; d diffs · g git · o Docker · t terminal · c chat · k skills · s stats · / search; click any event for full details; click an agent to filter to it.
🎨 22 themes 11 dark palettes (Midnight Purple, Forest, Ember, Nord, …), each with a light twin β€” instant switch, remembered.
πŸ”” Push alerts Webhook (Slack/Discord) + desktop notify + optional in-app chime on approvals and errors.
πŸ“€ Export One-click CSV / JSON of all events.

Themes

22 palettes β€” 11 dark, each with a light twin. A few:

Forest Ember Deep Sea
forest ember deep sea

Every dark palette has a matching light twin β€” e.g. Midnight Purple Light:

light theme


Quickstart

agentglass is a desktop app. Grab the installer for your platform from Releases, launch it, and the cockpit opens with its own server bundled inside. Nothing to run in a terminal, no port to open in a browser.

Linux macOS
.AppImage (chmod +x, run it) or .deb .dmg, Apple Silicon and Intel

That alone already shows you everything: the built-in transcript scanner reads ~/.claude/projects, so every Claude Code session on the machine is there on first open, with no wiring at all.

To also get live streaming and PreToolUse gating, wire the hooks once (opt-in, details below). The forwarder lives in the repo, so this step wants a clone and Python 3; it needs nothing else:

git clone https://github.com/SirAllap/agentglass.git && cd agentglass
python3 hooks/install_hooks.py        # global Claude Code hooks
python3 hooks/seed_demo.py            # optional: streams demo agents for ~30s

Want to look before installing? The live demo is this exact UI, built from the same source on every push, running on fabricated data.

Running from source

For hacking on agentglass, or for a headless box. Requires Bun β‰₯ 1.1 and Python 3 (for the hook forwarder).

bun install
bun run dev          # server :4000  +  UI :6180  (vite dev server)
make desktop         # or: build the UI and launch the real shell

bun run dev gives you the same UI in a browser tab at http://localhost:6180, minus what only the shell can do (fullscreen, zoom, launch-at-login, and the self-update route, which refuses a browser by design). It is the development path, not the way to run the app. If something else on your machine already owns :4000, the tab will talk to that server, so check the port before believing an empty dashboard.

Single-port deploys (a headless box, a systemd unit): build the UI once and the server serves it itself, one process, one port, API and dashboard on the same origin:

bun run build                         # web/dist
cd server && bun run src/index.ts     # dashboard AND API on :4000

When web/dist doesn't exist (plain bun run dev, or the packaged app's bundled server), nothing is served over HTTP: the server is API-only and no dashboard is reachable on that port at all.

Prefer make? Every entry point is a described Makefile target, and make help lists them all (make dev, make setup, make demo-feed, make desktop, …). The in-app terminal (t β†’ βš™ commands) surfaces the same list, ready to click-run.

Wire the hooks globally β€” one command, opt-in

bun run setup appends the agentglass forwarder to your global ~/.claude/settings.json, so every Claude Code session β€” in any project β€” starts streaming to the dashboard. No hand-copying, no per-project setup. (It's deliberately not run automatically on bun install: touching ~/.claude is a decision, so postinstall only prints a reminder.) Safe to re-run:

  • Idempotent & non-destructive β€” your existing hooks are preserved; re-running only re-points agentglass's own entries (e.g. after moving the clone).
  • Backed up β€” the settings file is copied to *.bak.agentglass.<timestamp> before any change.
  • Auto-labeled β€” --source-app is omitted so each session shows up under its own project's folder name in the dashboard.
  • Takes effect on the next session β€” Claude Code loads hooks at startup, so open a new session after wiring.
bun run setup            # wire the global hooks (also: make setup)
bun run setup:undo       # remove the agentglass hooks again

Even without any hooks, the built-in transcript scanner already surfaces every Claude Code session on the machine β€” the hooks add live PreToolUse gating and lower-latency streaming on top.

Prefer to scope it to one project instead of globally? Point the installer at a project directory (writes <project>/.claude/settings.json):

python3 hooks/install_hooks.py --project ~/code/my-project

Both use a dependency-free Python forwarder that POSTs to the server; Stop / SubagentStop / SessionEnd pass --add-chat so token usage can be read from the transcript. The raw hook blocks also live in hooks/settings.example.json for manual setups.


Desktop app

agentglass ships as a desktop app β€” its own window and icon, plus a self-contained server (the Bun backend compiled to a standalone binary and shipped as an Electron sidecar), so there's nothing to run in a terminal. Launch it from your app menu and the cockpit opens; close it and the server goes with it. The UI is the same web app, run in Chromium so it composites on the GPU.

Installers for every release are published on Releases: .AppImage and .deb for Linux, .dmg for macOS (Apple Silicon and Intel). That is the way to install it.

Building it yourself, from a clone:

make desktop            # build the UI and launch Electron + the sidecar
make desktop-dist       # package installable binaries (electron-builder)
make desktop-install    # install for this user (no root)

Then launch agentglass from your desktop menu, or agentglass from a shell.

The packaged app's bundled server serves the API and nothing else: the UI is loaded from the shell's own agentglass:// origin, which a browser cannot reach, so an install never exposes a dashboard on a port.

  • Attaches, never duplicates β€” if a server is already listening on :4000 (e.g. a bun run dev you left running), the app attaches to it instead of racing a second one against the same database.
  • Clean lifecycle β€” the bundled server is a child process, killed when the app exits (and reaped by the kernel via PR_SET_PDEATHSIG if the app dies hard).
  • Launch at login β€” an in-app toggle, no file editing.
  • Keeps full history β€” the desktop app defaults AGENTGLASS_RETENTION_DAYS=0.

A desktop app launched from its icon has no "current folder" β€” so on first open the cockpit asks which folder it's about: pick a project, a folder of projects, or the whole machine, and switch any time from the βŒ‚ header. The choice persists across launches. Prefer to decide at launch time? Pass the directory instead:

make desktop-open DIR=~/code/my-project   # or: agentglass ~/code/my-project

Works on Linux and macOS.


Updating

Settings β–Έ About shows the version you are running, the commit it was built from, and whether a newer release is published. One click builds it and restarts.

settings β€” preferences, shortcuts, and the About pane that offers a newer release

It tracks tags, never a branch tip. A tip is wherever development happened to stop β€” half a feature, a debugging commit β€” and tagging is the act of saying this one is tested. So nothing pushed after the last tag reaches an installed app until you tag it:

git tag v0.3.0 && git push --tags     # now every install is offered it

The build happens in agentglass's own clone under ~/.cache/agentglass/source, never in your checkout β€” so a convenience button can never move your HEAD or touch work in progress. It works out what it already has from git describe rather than a version field, so a package.json nobody remembered to bump cannot make an older tag look like an upgrade.

The route that runs it is the strictest in the server: reachable from the desktop shell's own origin and nothing else β€” not from a browser, not from another machine on your network. It is the one endpoint that executes arbitrary code, so the ordinary "local network is fine" rule is not enough for it.

Updating this way compiles on your machine, which is only reasonable because your machine already has the toolchain. It is not a substitute for a signed release feed, and it is deliberately not automatic β€” nothing is downloaded or run until you press the button.

Security model β€” read this before installing

agentglass is a workspace, not just a viewer: it can open a real shell, write to your repos and control Docker. It ships safe for its intended home β€” your own single-user machine β€” and you should know exactly where the lines are:

  • It only listens on your own machine. The server binds 127.0.0.1 β€” nothing on your network can reach it. By default there is no authentication, because on a single-user machine "can reach localhost" already means "is you".
  • Optional shared-secret token. Set AGENTGLASS_TOKEN and every route but the append-only telemetry sinks (/ingest, the OTLP receivers) requires it β€” Authorization: Bearer <token> for the API, ?token=<token> on the dashboard URL (it's stored and stripped from the address bar). This is what makes a shared machine or a network bind safe, and it stops other local processes from opening the shell. Binding a non-loopback address without a token refuses to run unauthenticated: it mints one, prints it, and saves it 0600 under your config dir.
  • Websites you visit can't touch it. Every request is origin-checked, the shell and the live stream require a verified local origin, and a Host-header guard blocks DNS-rebinding tricks (browsers can't forge Host). Running it behind a reverse proxy? Allow its name via AGENTGLASS_ALLOWED_HOSTS.
  • ⚠️ Shared / multi-user machines are NOT the default home. localhost belongs to the machine, not to your account β€” on a box where other people also have accounts, any of them could reach the server and its shell as your user. Set AGENTGLASS_TOKEN to lock it to you, and/or disable the capability surfaces: AGENTGLASS_TERMINAL_DISABLED=1, AGENTGLASS_FS_BROWSE_DISABLED=1, AGENTGLASS_CHAT_DISABLED=1, AGENTGLASS_GIT_WRITE_DISABLED=1, AGENTGLASS_DOCKER_WRITE_DISABLED=1.
  • ⚠️ Exposing it to a network is a three-part deliberate act. AGENTGLASS_BIND=0.0.0.0 hands the shell, git write and Docker control to that network. Do it only with a token set and AGENTGLASS_TRUST_LAN=1 (off by default, LAN browsers are refused as cross-origin without it), and only on a network you fully trust.
  • ⚠️ Browser-driven autonomy is opt-in. The Chat panel's bypassPermissions mode (claude --dangerously-skip-permissions) is honored only when AGENTGLASS_CHAT_BYPASS=1; otherwise it's downgraded to a prompting default.
  • Your data stays local. Events live in a local SQLite file (owner-only permissions). The only outbound call is the optional Anthropic plan-usage meter (api.anthropic.com, using your own credentials) and anything you configure (webhook alerts).

Control plane β€” approve / deny tool calls remotely (opt-in)

agentglass can do more than watch: a PreToolUse hook can hold a tool call until you approve or deny it from the dashboard. Wire hooks/gate_event.py into a project's PreToolUse and risky tool calls show up under "What needs you" with Approve / Deny buttons β€” decide from any device and the agent unblocks.

"PreToolUse": [
  { "matcher": "Bash", "hooks": [{ "type": "command",
    "command": "python3 ~/code/agentglass/hooks/gate_event.py --source-app my-project" }] }
]

On Windows, use py (or python) instead of python3 in hand-written hook commands; hooks/install_hooks.py picks this automatically.

Safe by design β€” it never blocks your agents by accident:

  • unreachable server or an error β†’ allow (the hook exits 0, no decision)
  • no one decides within AGENTGLASS_GATE_TIMEOUT (default 60s) β†’ auto-allow
  • only sessions wired to the gate are gated; everything else is untouched

It also survives a restart. Pending requests are persisted, so restarting or crashing the server brings the queue back instead of quietly auto-allowing everything that was waiting on you β€” the hook re-attaches to the request it was already holding. A request whose window ran out while the server was down is resolved by your configured policy and says so in "What needs you", because an outcome nobody chose is the one worth showing.

Scope it with the matcher (e.g. Bash only, or a specific tool) so you're not gating every call. Denying returns a PreToolUse deny with your reason.

Want the opposite trade-off? Set AGENTGLASS_GATE_FAILCLOSED=1 and a timeout or an unreachable control plane denies instead of allows β€” the fleet stops until you decide. Off by default; turn it on only when blocking is safer than proceeding, and remember agentglass being down then blocks every gated call.


Any provider β€” via OpenTelemetry (OpenAI, Gemini, Bedrock, …)

agentglass isn't Claude-only. It exposes an OTLP/HTTP trace receiver that maps OpenTelemetry GenAI spans (the gen_ai.* semantic conventions) into the same events the dashboard already understands β€” so anything emitting GenAI telemetry streams in: the OpenAI / Google / Bedrock SDK instrumentations, LangChain, LiteLLM, OpenLLMetry, and Claude Code's own OTel export.

Auto-connect installed CLIs β€” one command, opt-in

Like the Claude Code hooks, one command detects and wires any installed agent CLI that speaks OpenTelemetry β€” backed up first, idempotent, and never run behind your back on bun install:

bun run connect          # detect + wire installed agent CLIs (also: make connect)
bun run connect:undo     # unwire them again
  • Gemini CLI β†’ ~/.gemini/settings.json (OTLP traces β†’ /v1/traces)
  • OpenAI Codex CLI β†’ ~/.codex/config.toml (OTLP logs β†’ /v1/logs)

Start a new gemini / codex session after connecting and it streams straight in.

Anything else β€” point its OTLP exporter here

The receiver accepts OTLP/HTTP in both protobuf (the SDK default) and JSON, so no Collector is needed β€” just aim any exporter's endpoint at the server:

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4000
# spans POST to /v1/traces automatically (protobuf or http/json both accepted)

The provider and model are auto-detected from the spans (gen_ai.system, gen_ai.request.model) β€” no config, no dropdown. Mapping:

  • LLM spans (chat / completion / …) β†’ a costed "turn" event carrying the span's token usage. Cost uses the same pricing table (OpenAI, Gemini, Mistral, … included; override with AGENTGLASS_PRICING).
  • Tool spans (execute_tool, or any span with gen_ai.tool.name) β†’ a paired PreToolUse + PostToolUse, so tool latency (p50/p95) and the tool-mix populate.

Some agents (OpenAI Codex CLI) export OpenTelemetry logs rather than traces β€” those go to /v1/logs, which maps each GenAI log record (tool decision/result, inference, prompt) to an event the same way.

Non-GenAI spans/records are ignored β€” this is an agent-observability lens, not a general trace or log store.


Configuration (env)

Var Default Meaning
AGENTGLASS_PORT 4000 Server HTTP/WS port.
AGENTGLASS_BIND 127.0.0.1 Address the server binds to. Loopback-only by default. Exposing (0.0.0.0) requires AGENTGLASS_TOKEN and AGENTGLASS_TRUST_LAN=1, and only on a trusted network. See Security model.
AGENTGLASS_TOKEN β€” Shared secret required on every route but the telemetry intake sinks. Pass as Authorization: Bearer <t> or ?token=<t>. Locks the server to you on a shared machine and makes a network bind safe. Exposing without one auto-mints + prints a token (saved 0600 in the config dir).
AGENTGLASS_TRUST_LAN β€” 1 β†’ also trust RFC1918 (private-LAN) addresses as origins/hosts, not just loopback. Required for LAN browsers to reach an exposed instance. Off by default: a shell-granting server trusts only localhost unless told otherwise.
AGENTGLASS_ALLOWED_HOSTS β€” Comma-separated extra hostnames accepted by the DNS-rebinding guard (requests must arrive under a localhost/private Host). Only needed behind a reverse proxy.
AGENTGLASS_DB agentglass.db SQLite file path.
AGENTGLASS_ROOT β€” Scope the whole cockpit to one project (repo + worktrees) or a folder of projects. Unset = every project on the machine. Also set by passing a directory to the desktop app; the in-app project picker sets/clears the same scope at runtime and persists it as root in the config file (note: the env var, when set, wins again on the next launch).
AGENTGLASS_REPO_DIRS β€” Colon-separated dirs to sweep for git repos (git / terminal / chat panels). Also settable as repoDirs in the config file.
AGENTGLASS_PROJECTS_DIR ~/.claude/projects Root the transcript scanner reads Claude Code session logs from. Several roots can be listed, separated by the platform's PATH delimiter (: on Linux/macOS, ; on Windows).
AGENTGLASS_SCAN_INTERVAL_MS 3000 Transcript scan poll interval (min 500).
AGENTGLASS_SCAN_DISABLED β€” 1 β†’ turn off the machine-wide transcript scanner (rely on hooks / OTel only).
AGENTGLASS_RETENTION_DAYS 8 Days of history to keep (pruned hourly). Covers the full 7d stats window; 0 = keep forever (the desktop app's default).
AGENTGLASS_PRICING β€” Path to a JSON pricing override (see server/src/pricing.ts).
AGENTGLASS_WEBHOOK β€” POST {text} alerts here (Slack/Discord compatible).
AGENTGLASS_NOTIFY β€” 1 β†’ fire notify-send desktop alerts.
AGENTGLASS_SERVER http://localhost:4000 Used by the hook/seed scripts.
VITE_CW_SERVER http://<host>:4000 UI β†’ server URL (build/dev time). Unset, the UI resolves same-origin when the server itself served it (single-port mode), :4000 otherwise.
AGENTGLASS_GIT_WRITE_DISABLED β€” 1 β†’ make the Source control panel read-only (no stage / commit / push).
AGENTGLASS_DOCKER_WRITE_DISABLED β€” 1 β†’ make the Docker panel read-only (no start / stop / restart / rm).
AGENTGLASS_NOTIFY_DISABLED β€” 1 β†’ never watch the desktop notification bus, whatever the UI asks for. The notch keeps its counters and loses its inbox.
Scope is a boundary, not just a filter. With a project open, git writes, the
terminal and chat are all refused outside it β€” the same rule that decides what the
dashboard shows. This is a behaviour boundary, not cosmetic: with a project
open, opening the app on ~/code and then jumping to /tmp in the terminal is
refused, and git writes outside the root are blocked β€” on their own, by the
scope boundary. (The AGENTGLASS_GIT_WRITE_DISABLED/AGENTGLASS_TERMINAL_DISABLED
knobs are a separate, global off-switch, not what enforces the root.)
For genuinely multi-repo work, scope to the parent folder (~/code) rather
than one repo: every repo beneath it is then in scope. An unscoped (whole-machine)
instance is unaffected.

| AGENTGLASS_TERMINAL_DISABLED | β€” | 1 β†’ disable the in-browser Terminal entirely (no PTY shells are spawned). | | AGENTGLASS_FS_BROWSE_DISABLED | β€” | 1 β†’ disable directory completion in the project picker (/fs/complete). Separate from the terminal switch on purpose: disabling the shell should not leave the directory tree readable. | | AGENTGLASS_CHAT_DISABLED | β€” | 1 β†’ disable the Chat panel (no claude sessions can be started from the browser). | | AGENTGLASS_CHAT_BYPASS | β€” | 1 β†’ allow the Chat panel's bypassPermissions mode (claude --dangerously-skip-permissions). Off by default: the mode is downgraded to a prompting default unless you opt in. | | AGENTGLASS_COMMIT_DISABLED | β€” | 1 β†’ disable the diff viewer's Commit… composer. | | AGENTGLASS_GATE_TIMEOUT | 60 | Seconds the PreToolUse gate hook waits for an approve/deny before auto-allowing. | | AGENTGLASS_GATE_FAILCLOSED | β€” | 1 β†’ a gate timeout (server) or an unreachable control plane (hook) denies the tool call instead of allowing it. Off by default (fail-open β€” never block agents by accident). | | AGENTGLASS_RATE_MAX | 300 | Max intake requests (/ingest, OTLP receivers) per source-address+route within the window, before 429. | | AGENTGLASS_RATE_WINDOW_MS | 10000 | Rate-limit window in ms for the intake sinks. | | AGENTGLASS_CODE_DIR | ~/code | Where the skills explorer scans for per-project .claude skills/commands. | | AGENTGLASS_WALKTHROUGH_MODEL | claude-haiku-4-5 | Model for the AI Explain walkthrough (uses a local claude CLI, else ANTHROPIC_API_KEY). | | CLAUDE_CREDENTIALS | ~/.claude/.credentials.json | OAuth token for the Anthropic plan-usage meters (never leaves your machine except to api.anthropic.com). |

Prefer a file over env vars? Drop a ~/.config/agentglass/config.json (or $XDG_CONFIG_HOME/agentglass/config.json) with root and/or repoDirs; env vars override it.

Pricing is a user-editable default. Numbers in pricing.ts are per 1M tokens and matched against model_name by substring. Anthropic (Claude) rates are verified; other providers are marked approx β€” verify current rates and tune, or point AGENTGLASS_PRICING at your own JSON.


API

Route Description
POST /ingest Ingest an event {source_app, session_id, hook_event_type, payload?, chat?, model_name?}.
POST /v1/traces OTLP/HTTP (JSON + protobuf) β€” maps OpenTelemetry gen_ai.* spans to events (any provider).
POST /v1/logs OTLP/HTTP (JSON + protobuf) β€” maps OpenTelemetry GenAI log records to events (e.g. Codex CLI).
GET /events/recent?limit= Latest events.
GET /events/filter-options Distinct apps / event types / models.
GET /projects Known projects (filtered to the active scope) + the current workspace.
POST /workspace Scope the cockpit to a project / folder at runtime ({root}; null β†’ whole machine). Applied live, persisted to the config file β€” this is what the in-app project picker calls.
GET /sessions?limit= Session rollups.
GET /stats?window=<ms> Full analytics summary (totals, by-model, tool latency, timeline).
GET /skills Skill/command catalog scanned from ~/.claude + $AGENTGLASS_CODE_DIR/*/.claude, joined with recorded usage.
GET /changes?limit= Recent file changes (Edit/Write) as diff hunks β€” feeds the File changes diff viewer.
POST /walkthrough AI Explain β€” a local-Claude walkthrough of a set of diffs (per-file summary + review focus).
GET /git/tree Β· /repos Β· /branches Β· /log Β· /graph Β· /worktrees Β· /stashes Β· /commit-diff Β· POST /git/status Live working-tree, branches, log/graph, worktrees & stashes for a repo (read). /repos honours the active scope; ?all=1 lists the whole machine (what the project picker uses).
POST /git/{stage,unstage,discard,commit-staged,push,pull,fetch,checkout,branch-*,stash-*,apply-hunk,merge,rebase,reset,worktree-*} Mutating git ops β€” gated by AGENTGLASS_GIT_WRITE_DISABLED.
GET /docker/overview Β· /stats Β· /logs Β· /inspect Β· /top Containers / images / volumes / networks, live CPU-mem stats, container logs, environment & config, running processes.
POST /docker/{start,stop,restart,rm} Container actions β€” gated by AGENTGLASS_DOCKER_WRITE_DISABLED.
GET /git/conflicts Β· /conflict-blocks Β· POST /git/resolve Β· /resolve-blocks Β· /sync-base Β· /merge-abort Β· /merge-continue Β· /undo-merge Mid-merge state: which files are conflicted, the <<<<<<< blocks inside one, and taking a side per file or per block. Sync a branch from its base, and undo the last merge while that is still exactly reversible. Write-gated.
GET /update/status Β· /update/log Β· POST /update/run The running version, the newest published release tag, and building it. Desktop-shell origin only β€” refused (403) for a browser, another machine, or a caller with no Origin at all, because it is the one route that executes arbitrary code.
WS /terminal/pty?root=&cols=&rows= A real PTY shell in a repo/worktree β€” raw bytes out, {t:"in"|"resize"} frames in. Gated by AGENTGLASS_TERMINAL_DISABLED.
GET /terminal/commands?root= Ready-to-run project commands: Makefile targets with descriptions + package.json scripts (runner-aware), from the repo root and its subfolders (make -C …), grouped by folder.
GET /chat/enabled Β· POST /chat/send Drive a local claude session in a repo (streamed JSONL) β€” gated by AGENTGLASS_CHAT_DISABLED.
GET /usage Anthropic plan-limit windows (5-hour / weekly) for the usage meters.
GET /session?id= Full detail for one session (events, files, totals).
GET /insights Derived warnings β€” loops, fast burn, high failure rate, spend velocity.
GET /search?q= Full-text search across all captured prompts/commands/outputs.
POST /gate Β· GET /gate/pending Β· POST /gate/decide Control-plane approve/deny for the opt-in PreToolUse gate.
GET /export?format=csv|json Download all events.
WS /stream Live {type: initial|event|session} frames.

Architecture

 Claude Code hooks ───────────▢ hooks/send_event.py ──┐
 OpenTelemetry (any provider) ─▢ /v1/traces, /v1/logs ─
 ~/.claude/projects (every session) ─▢ scan + tail ──────▢  server (Bun)
                                                       β”‚      β”œβ”€ ingest.ts       normalize + token/cost delta
                                                       β”‚      β”œβ”€ transcripts.ts  machine-wide scan + live tail
                                                       β”‚      β”œβ”€ otlp.ts         map gen_ai.* spans/logs β†’ events
                                                       β”‚      β”œβ”€ config.ts       project scoping (root / repoDirs)
                                                       β”‚      β”œβ”€ db.ts           SQLite: events + sessions, latency pairing
                                                       β”‚      β”œβ”€ pricing.ts      model β†’ USD (any provider)
                                                       β”‚      β”œβ”€ alerts.ts       webhook / desktop push
                                                       β”‚      β”œβ”€ gitwork.ts      live working tree (lazygit)
                                                       β”‚      β”œβ”€ docker.ts       live containers (lazydocker)
                                                       β”‚      β”œβ”€ terminal.ts     real PTY shells over WS (+ make/script catalog)
                                                       β”‚      β”œβ”€ chat.ts         drive local `claude` sessions (stream-json)
                                                       β”‚      β”œβ”€ gate.ts         approve/deny control plane
                                                       β”‚      β”œβ”€ walkthrough.ts  local-Claude "Explain" of a diff set
                                                       β”‚      └─ WS /stream ─┐
                                                       β”‚                      β–Ό
              web (React + Vite + Motion + Recharts + Shiki + xterm.js, :6180)
              └─ also packaged as an Electron desktop app with a bundled Bun sidecar

How cost stays correct: transcripts report cumulative session tokens. On ingest the server diffs each cumulative report against the session's prior total, storing a per-event delta β€” so timeline sums and session totals agree and nothing is double-counted. Hook events and the scanner dedupe against each other by session, so the same turn is never counted twice.


Roadmap

Where this is going β€” themes, not dates. The living version is the issue tracker; the help wanted and good first issue labels mark the best places to start.

Now

  • Lead with a verdict: what's running, what's stuck, what needs you now β€” #42
  • Windows: a usable terminal panel, or an honest disable β€” #98

Next

  • Per-agent changes scoped to each session's worktree/branch β€” #117
  • Warn when parallel agents collide on shared runtime the diff can't see β€” #118
  • A gate that can hold by rule (spend, allowlist), not only by hand β€” #109
  • Per-project gate policies and hook profiles β€” #14
  • Keep model prices fresh without hand-editing the table β€” #9

Later / exploring

  • An API panel to exercise the endpoints the fleet is building β€” #170
  • Tasks per project, and a decision log mined from transcripts β€” #12, #13
  • A phone-friendly view for monitoring and answering gate approvals β€” #7
  • Voice input in chat β€” #92

Recently shipped β€” see the releases for the full record.

  • unreleased β€” a slow tool call is told from a hung one by evidence rather than by a timer, per tool class, with "can't tell" as a real answer (#134); the UI says so when something other than agentglass owns :4000; a fresh install has somewhere to look for repos
  • v0.5.0 β€” pull request review inside the cockpit, and the freeze is gone: the event loop is watched, and every expensive git, docker and database read left the thread that carries the terminal
  • v0.4.0 β€” evidence-of-life signal for open tool calls; the shell no longer adopts a stranger's server on :4000
  • v0.3.0 β€” in-app merge-conflict resolution, whole-project docker controls, a rearrangeable workspace, and an in-app updater
  • v0.2.x β€” downloadable installers for Linux / macOS / Windows, the Electron desktop shell, and a real chat panel

Contributing

PRs welcome. If you want to adapt agentglass to another agent or harness without forking, start with docs/EXTENDING.md β€” OTLP ingest, the gate primitive, themes, and config surfaces are already there.

Issues and PRs welcome β€” see CONTRIBUTING.md. Small, fast, and dependency-light on purpose: a Bun/SQLite server, a React/Vite UI, an Electron desktop shell, and a stdlib-only Python hook forwarder.

About

Built by @SirAllap (David Pallares). Original work β€” not a fork. Not affiliated with or endorsed by Anthropic; "Claude" and "Claude Code" are trademarks of Anthropic.

License

MIT Β© 2026 David Pallares β€” see LICENSE.

About

πŸ›° A loupe for your agents β€” a real-time Mission-Control dashboard and workspace for AI coding agents, across every provider and every project on your machine

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Sponsor this project

Packages

Contributors

Languages