A desktop GUI for Claude Code. Run many claude sessions in parallel — grouped by task, not by repo — with the visual density of a CLI and the interaction model of a native app.
Download for Windows / macOS / Linux →
Claude Code is a powerful CLI, but the moment you're juggling 5+ live agents across different repos, terminal tabs stop scaling. CCSM keeps every session attached and addressable from one window, organised the way you actually think about work — by task.
Sessions live inside user-defined groups. A group is a goal ("Refactor auth middleware", "v0.3 release prep"); sessions inside it can run in any cwd. Switch groups to context-switch your whole task, not one tab at a time.
The PTY lives in the main process. Flipping between sessions is a buffer swap, not a respawn — no lost scrollback, no reconnect, no waiting for the welcome banner.
Block-based message rendering (> user, ● assistant, ⏺ tool), collapsed tool calls, monospace everywhere. No chat bubbles, no avatars, no wasted vertical space. But drag-to-reorder, right-click menus, keyboard shortcuts, inline rename and hover/focus states behave the way a native app should.
When the agent needs Allow / Deny on a tool call, you see the tool name and structured input inline — not a wall of JSON. Answer in place, the session keeps streaming.
Two derived states: idle and waiting. The sidebar breathes amber when an agent needs you. No manual "in progress / done / blocked" flags to keep current.
CCSM makes zero HTTP calls to Anthropic. Every API request goes through your local claude binary using credentials the CLI already manages. The database is a local SQLite file; conversation history is read directly from ~/.claude/projects/.
- Claude Code CLI installed and authenticated. CCSM delegates 100% of agent execution to the CLI:
- Install:
npm i -g @anthropic-ai/claude-code(or your platform package manager) - First-time login: run
claudeonce to complete OAuth (or setANTHROPIC_API_KEYin your environment) - If
claudeworks in your terminal, CCSM will work. If it doesn't, CCSM won't either.
- Install:
- OS: Windows 10+, macOS 11+ (Big Sur), or Linux (glibc 2.17+)
- Disk: ~200 MB
CCSM does not make any HTTP calls to Anthropic itself. All API traffic goes through your local claude binary, with your existing credentials.
- Download the latest
.exe(Windows) /.dmg(macOS) /.AppImage/.deb/.rpm(Linux) from Releases. - Run the installer.
- Launch CCSM. If the Claude CLI isn't found, you'll see an actionable error showing every path CCSM searched.
- Click + New group in the sidebar to create a task bucket (or skip — CCSM auto-creates a default group on the first session).
- Click + New session inside a group. Pick a working directory (any repo). The session spawns
claudein that cwd. - Type in the composer at the bottom. Enter to send, Shift+Enter for newline, Esc to cancel inline edits.
- When the agent requests a tool, a permission block appears at the tail of the conversation — Allow / Deny.
- Switch between sessions with the sidebar. Background sessions keep streaming and surface a breathing amber dot when they need you.
Cmd/Ctrl+F— Search / Command PaletteCmd/Ctrl+,— SettingsCmd/Ctrl+N— New sessionCmd/Ctrl+Shift+N— New group
Release builds can control live CCSM terminals from one phone browser through the end-to-end encrypted Cloudflare relay. Open Settings → Mobile Remote, then scan the QR code. The phone can list terminal sessions, view their current buffer, type commands, and use terminal shortcut keys; it does not control the rest of CCSM or the operating-system desktop.
The initial scope is one user, one CCSM desktop, and one phone. Pause remote control disconnects the phone without discarding the pairing. Refresh QR rotates the pairing identity and rejects the old credential. The relay depends on the Cloudflare Workers and Durable Objects free tiers, so an unavailable or exhausted relay temporarily disables phone access without affecting local CCSM sessions.
Local SQLite database (groups, sessions, user-defined order, sidebar width, theme):
- Windows:
%APPDATA%\CCSM\ - macOS:
~/Library/Application Support/CCSM/ - Linux:
~/.config/CCSM/
Conversation history is not duplicated — CCSM reads it directly from the Claude CLI's ~/.claude/projects/ jsonl files. Anthropic credentials are stored by the CLI itself; CCSM never touches them.
CCSM can send crash reports and unhandled errors to Sentry to help fix bugs. Reports include error stack traces and the app version; they do NOT include the contents of your conversations, file paths inside your projects, or environment variables.
Crash reporting is off by default in the open-source build: there is no hardcoded DSN. To enable it (e.g. for your own fork), set SENTRY_DSN=https://<key>@<org>.ingest.sentry.io/<project> in the process environment before launching the app. If SENTRY_DSN is unset, Sentry.init() is skipped entirely and a single informational line is logged at startup.
To disable after opting in: open Settings → Notifications and uncheck "Send crash reports to developer".
Requires Node 22+ and npm. The native module node-pty is rebuilt for Electron's ABI on npm install via scripts/postinstall.mjs. (The local DB uses Node's built-in node:sqlite, which needs no native rebuild.)
npm install
npm run dev # webpack-dev-server + electron, concurrently
npm test # vitest
npm run typecheck # tsc --noEmit (renderer + electron)
npm run lint # eslint
npm run make:win # build Windows installer (NSIS)
npm run make:mac # build macOS .dmg + .zip
npm run make:linux # build AppImage / .deb / .rpmThe architecture has a hard rule: frontend code under src/ may not import from electron. The only backend entry point is window.ccsm (declared in src/global.d.ts), exposed via electron/preload.ts. This keeps the door open for a future remote daemon. See docs/mvp-design.md §15.
node-pty's native rebuild uses node-gyp, which on Windows often
picks up the WindowsApps\python.exe Microsoft Store launcher stub instead
of a real interpreter and fails. If you see gyp ERR! find Python, point
node-gyp at a real Python 3.x in your user-level ~/.npmrc (not the
repo's .npmrc — the path is machine-specific):
python=C:/Users/<you>/AppData/Local/Programs/Python/Python312/python.exeAny installed Python 3.8+ works. Re-run npm install and the rebuild
will pick up the override automatically.
CCSM raises desktop notifications when a background session needs your attention or a long-running turn finishes:
permission— the agent is waiting on an Allow / Allow always / Reject decision (e.g.Bash,Edit,Write).question— the agent invokedAskUserQuestionand needs you to pick an option.turn_done— a turn finished, and either took longer than 15 seconds, errored, or completed in a session that wasn't focused.
CCSM uses Electron's built-in Notification API, which delivers OS-native
toasts on Windows (via ToastNotificationManager + the AUMID stamped on the
Start Menu shortcut), macOS, and Linux. Click a toast to bring CCSM forward
and focus the originating session — no inline action buttons; permission
approval flows through the in-app dialog where the command + cwd context is
visible.
To disable: open Settings → Notifications and toggle the master
Enable notifications switch (or any of the per-event sub-toggles for
permission / question / turn done). Per-session muting is also
available from the session's context menu.
Focus suppression: when CCSM has the OS focus, no toast fires — you'll
see the in-app prompt or sidebar pulse instead. This is enforced both in
the renderer (per-session focus check) and in the main process
(BrowserWindow.isFocused()), so devtools / debuggers / playwright
sessions can't bypass it.
Adaptive Toast notifications require a registered AppUserModelID (AUMID)
plus a Start Menu shortcut that points at the same AUMID. NSIS installs of
CCSM register both automatically. For npm run dev (no installer is run),
register them once per machine:
pwsh scripts/setup-aumid.ps1Without this, the Adaptive Toast pipeline silently no-ops in dev mode and CCSM falls back to plain Electron notifications.
- Electron (main + renderer) with TypeScript throughout
- React 18 for the renderer UI, bundled by webpack 5
- xterm.js +
node-ptyfor the embedded terminal that hosts eachclaudesession - node:sqlite (Node built-in) for the local database (groups, sessions, ordering, preferences)
- electron-builder for packaging (NSIS / dmg / AppImage / deb / rpm)
- vitest for unit + integration tests, playwright for e2e
See package.json for exact versions.
Public releases are cut from main via tagged builds (see Release flow below). The author uses CCSM daily as a personal driver; expect rough edges and breaking-ish updates between minor versions.
For maintainers:
- Merge PRs into the
mainbranch (the default branch). CI on every PR runs lint + typecheck + test + e2e on all three OSes. - Bump
versioninpackage.jsonand land that bump onmain. - Tag the commit
vX.Y.Zand push the tag..github/workflows/release.ymlpicks it up and runs the cross-platform build matrix (Windows NSIS installer, macOS dmg + zip for x64 and arm64, Linux AppImage / deb / rpm). - The workflow uploads the artifacts to a draft GitHub Release with auto-generated notes. Review the draft, edit notes if needed, then publish it manually.
The workflow_dispatch trigger exists for dry-run builds; it stops after uploading workflow artifacts and does not touch the Releases tab. Signing secrets (CSC_LINK / CSC_KEY_PASSWORD for Windows + macOS, plus Apple notarization creds for mac) are optional — builds proceed unsigned with a CI warning if they're absent.
Mobile Remote releases additionally require repository secrets
CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID, plus the repository variable
CLOUDFLARE_WORKERS_SUBDOMAIN. The release workflow verifies the application,
deploys the phone PWA and relay first, stamps that deployed URL into
package.json, and only then packages desktop installers. Missing configuration
or a failed relay deployment stops packaging rather than publishing an
installer with a dead QR code. For local relay development, build the phone PWA,
run Wrangler, and launch CCSM with
CCSM_MOBILE_REMOTE_RELAY_URL=http://127.0.0.1:<port>; this override is not an
installed-user setting.
The relay deliberately has no WebRTC runtime dependency. An earlier werift
implementation was reverted after electron-builder pruning caused startup
failure. Keep the remote controller behind its guarded post-ready lazy import:
relay loading or configuration failures must never prevent the main window from
opening.
MIT — see LICENSE.
