@bakapiano/ccsm
v0.17.7
Published
Claude Code Session Manager — Windows web UI to manage many concurrent claude sessions: live list, snapshot/restore, focus existing window, new session in an isolated workspace with repo clones
Downloads
2,666
Maintainers
Readme
ccsm — Claude Code Session Manager
A single pane over every Claude / Codex / Copilot CLI session on your machine. Each session runs inside the page (xterm.js + a PTY pool in the local backend), gets recorded, and re-attaches to the exact upstream conversation when you click it again.
┌── browser ─────────────────────────┐
│ https://bakapiano.github.io/ccsm/ ← version router
│ ↓
│ /ccsm/X.Y.Z/ ← per-version frontend (pinned to your backend)
└────────────┬───────────────────────┘
│ fetch /api/* (CORS)
│ ws://localhost:7777/ws/*
▼
┌── local backend ───────────────────┐
│ ccsm (npm bin) │
│ ├── /api/sessions /api/sessions/new │
│ ├── /api/sessions/:id/resume │
│ ├── /api/sessions/adopt │
│ ├── /api/version /api/upgrade │
│ ├── /ws/terminal/:id (PTY) │
│ └── /api/health /api/heartbeat │
└────────────────────────────────────┘What it does
- Runs every CLI session in the page.
claude,codex,copilotor any custom command, in an xterm.js panel. Switch sessions in the sidebar; the PTY keeps running in the backend. --resume <uuid>precision. ccsm watches the upstream CLI's transcript dir after spawn and captures its session UUID. Click a stopped session later → re-spawns with--resume <uuid>(or whateverresumeIdArgstemplate you set per-CLI) so the exact conversation comes back.- Import existing sessions. Scans
~/.claude/~/.codex/~/.copilotand lets you adopt any session ccsm didn't start. - Workspaces + clones. "New session" picks an unused workspace
under your work-dir, clones selected repos with live
git clone --progressstreamed to per-repo progress bars, opens a fresh CLI there. Or pick any existing folder via the file browser. - Folders. Drag sessions into named folders for organisation.
- In-app upgrade. About page checks npm for newer versions of ccsm and offers a one-click upgrade button. Backend self-restarts.
Install
npm i -g @bakapiano/ccsmThis:
- puts
ccsmon your PATH - registers a
ccsm://URL protocol so the hosted frontend can wake the backend with one click
npx @bakapiano/ccsm works too for a one-shot trial — the protocol
still gets registered.
Use
ccsm # starts the backend, opens the frontendOr just visit https://bakapiano.github.io/ccsm/ in any browser.
If the backend isn't running, the router shows a "Backend not running"
banner with a Start ccsm button — click it, Windows asks once
whether to open the ccsm:// handler (check "Always allow"), and the
backend spawns silently behind the page. The router auto-reconnects in
1-2s and redirects to the frontend matching your installed backend
version.
Install as PWA
In Chrome / Edge, click the install icon in the address bar (or use the "Install ccsm" button on the About tab inside the app). The PWA gets its own window, its own icon, and Window Controls Overlay so the title bar blends into the page.
After installing, clicking the PWA icon is the new entry point — no terminal needed.
Defaults
| | |
|---|---|
| Port | 7777 (auto-bumps if taken) |
| Work dir | ~/ccsm-workspaces (each subdirectory holds one or more repo clones) |
| Built-in CLIs | claude, codex, copilot — add your own via the Configure tab |
| Data dir | ~/.ccsm/ (override with CCSM_HOME=<path>) — survives upgrades and npx cache wipes |
All of the above are editable through the Configure tab.
Layout
ccsm/
├── server.js # Express + WebSocket; API only in prod
├── bin/ccsm.js # launcher · detaches server, opens browser
├── scripts/
│ ├── install.js # postinstall · registers ccsm:// (Windows)
│ └── uninstall.js # preuninstall · cleanup
├── lib/
│ ├── persistedSessions.js # ~/.ccsm/sessions.json — the source of truth
│ ├── folders.js # sidebar tree
│ ├── localCliSessions.js # scan ~/.claude · ~/.codex · ~/.copilot
│ ├── workspace.js # ws-N allocation + repo clones
│ ├── webTerminal.js # node-pty pool · WebSocket bridge
│ ├── jsonStore.js · config.js
├── pages-root/ # → GH Pages / (version router)
└── public/ # → GH Pages /<pkg.version>/ (per-version frontend)
~/.ccsm/ # or $CCSM_HOME
├── config.json
├── sessions.json # persisted sessions
├── folders.json
├── server.log
└── browser-profile/ # Edge/Chrome --user-data-dirHow "wake on click" works
The hosted frontend lives entirely in the browser sandbox — it cannot
spawn processes. So when the backend is down, the OfflineBanner's
Start ccsm is a plain <a href="ccsm://start">. The OS hands that
off to a per-user URL protocol handler registered at install time:
HKCU\Software\Classes\ccsm\shell\open\command
→ wscript.exe "<LOCALAPPDATA>\ccsm\launcher.vbs" "%1"The .vbs calls ccsm.cmd "ccsm://start" with WindowStyle = 0. That
gets to bin/ccsm.js, which parses the protocol URL, spawns
server.js detached, and exits. Zero windows ever flash.
First click triggers a one-time Windows dialog ("Open ccsm.cmd?"). Tick Always allow and future clicks are silent.
Lifecycle (when does the backend die)
| trigger | reaction |
|---|---|
| The auto-opened browser window closes | wait 12s · if any other client heartbeats during that window, stay alive; otherwise gracefulShutdown |
| No heartbeat for 90s | gracefulShutdown |
| POST /api/shutdown | gracefulShutdown |
| POST /api/upgrade after install | self-respawn + gracefulShutdown |
| SIGINT / SIGTERM | gracefulShutdown |
Dev
git clone https://github.com/bakapiano/ccsm
cd ccsm
npm install
CCSM_NO_BROWSER=1 CCSM_KEEP_ALIVE=1 node server.js
# opens http://localhost:7777 with hot-reload (public/ is served locally
# and SSE pushes a reload event on every file save)Dev mode is detected via __dirname.includes('node_modules') — when
running from a checkout, the backend also serves public/. In an
npm-installed copy it's API-only, and you use the hosted frontend.
Versioning (frontend ↔ backend)
The hosted root (/ccsm/) is a tiny static version router: it
probes localhost:7777/api/health, then redirects you to
/ccsm/<backend.version>/. Each release publishes a fresh
per-version subdir; old ones stay forever. No semver-compat logic — a
frontend is always 1:1 with the backend it was built against.
If your backend gets upgraded under a still-loaded page, the per-version frontend detects the mismatch on its next probe and bounces you back through the router automatically.
Status
- Backend: Windows-first. macOS / Linux backend ports planned (URL protocol registration is the only platform-specific install piece).
- Frontend: cross-platform (pure web).
See CLAUDE.md for design decisions and the non-obvious gotchas baked into the launcher / session-watcher / lifecycle code.
