A live dashboard over your tmux sessions. Every window becomes a card; click
into a card to open a full live terminal (xterm.js + tmux pipe-pane over
WebSocket), send keystrokes, focus the window in tmux, or rename it. Parses
Claude Code pane status — branch, PR, CI state, recap, spinner — and surfaces
what's pending input.
Built for the "I have 30 tmux windows across 5 sessions with various Claude Code agents running, and I want a single pane of glass" workflow.
tmux(any reasonably modern version)uvfor running the single-file script- A modern browser (uses WebSockets + vanilla JS)
- Optional:
ANTHROPIC_API_KEYfor the ✨ auto-rename feature - Optional: Node 20+ if you want HMR while iterating on the frontend
uv run server.pyOpen http://127.0.0.1:8765/. Polls every 3s; the modal opens a live WebSocket bridge to the selected pane.
For the always-on / launchd-managed setup and the prod/dev port split,
see CLAUDE.md → Development workflow
and bin/periscope.
For hot-reload while editing static/app.js or static/styles.css:
npm install # one-time
npm run dev # then visit http://127.0.0.1:5174/npm run dev runs the FastAPI server and Vite together via
concurrently; ctrl+c stops both. Vite proxies /api/* and /ws/* to
FastAPI, so only one URL matters in the browser. There's no build step —
production still loads static/ as-is from FastAPI on :8765.
Periscope can run as a native .app — its own Dock icon and Cmd-Tab
entry instead of a browser tab. The app is a thin Tauri
shell that loads the dashboard from http://127.0.0.1:8765, so the
server still has to be running (uv run server.py, or the launchd
service via bin/periscope install).
Download the latest .dmg from
Releases, or build it
from source:
cd src-tauri
cargo tauri build # needs the Rust toolchain + `cargo tauri`
open target/release/bundle/macos/Periscope.appRelease .dmgs are unsigned, so Gatekeeper blocks them on first
launch. After dragging Periscope.app into /Applications:
xattr -dr com.apple.quarantine /Applications/Periscope.appThe ✨ button on each session header asks Haiku 4.5 to suggest fresh, descriptive names for every window in the session based on current pane content. Requires an Anthropic API key:
cp .env.example .env
# then edit .env and paste your keyPeriscope can push messages into the Claude Code sessions it spawns and surface Claude's replies in its UI. This uses Claude Code's channels feature, currently in research preview.
Claude Code keeps user-level MCP servers under the mcpServers key in
~/.claude.json. Merge the periscope entry in (don't overwrite the
file — it holds lots of other Claude Code state):
jq '.mcpServers.periscope = {
"command": "uv",
"args": ["run", "--script", "/ABSOLUTE/PATH/TO/periscope/channel_shim.py"]
}' ~/.claude.json > ~/.claude.json.tmp && mv ~/.claude.json.tmp ~/.claude.jsonReplace /ABSOLUTE/PATH/TO/periscope/ with your local checkout path.
After this, restart any running claude sessions you want channels in
(it's read at invocation time) or spawn fresh ones via periscope's
+ claude button.
When you click + claude in periscope, the spawned command is
claude --dangerously-load-development-channels server:periscope. Claude
launches channel_shim.py as a stdio child. The shim proxies MCP
messages over a unix socket (/tmp/periscope-mcp.sock) to periscope's
in-process MCP server, and reconnects transparently across periscope
restarts.
The --dangerously-load-development-channels flag is required because
channels are in research preview — bare --channels only resolves
allowlisted entries.
- Push to Claude: open a pane's modal, type a message in the
composer in the Messages section, and submit. Claude sees it on its
next turn as a
<channel source="periscope">block. - Replies from Claude: Claude can call the
replytool withkind="need_human",kind="done", orkind="info"(the default). Messages show in the modal's Messages section;need_humantriggers a pulsing red border on the pane card and fades the rest of the grid.
If a session was started outside periscope (no dev-channels flag), the push composer is disabled with a tooltip.
MIT — see LICENSE.