Features Docs Changelog Download GitHub

Documentation

Everything you need to install, configure, and extend Claude Control Center.

Quick Start

1

Download the DMG

Grab the latest ClaudeControlCenter.dmg from the GitHub Releases page.

2

Install & Launch

Open the DMG, drag the app to /Applications, and launch it. First launch only: right-click → Open to pass Gatekeeper (the app is ad-hoc signed, not yet notarized). The icon appears in your menu bar.

3

Hooks install automatically

On first launch, CCC appends 8 hook entries to ~/.claude/settings.json and generates a private auth token at ~/.claude/ccc_token. No terminal commands needed. Existing hooks are preserved.

4

Run Claude Code normally

Start any Claude Code session. Events stream to CCC automatically — you'll see the menu bar icon animate as Claude works.

Requirements

RequirementVersionNotes
macOS14 Sonoma or laterUniversal binary — Apple Silicon & Intel
Claude Code CLIAny version with hook supportInstall with npm i -g @anthropic-ai/claude-code
Python 33.8+Required by hook scripts for JSON parsing
curlAnyBundled with macOS; used by hook scripts

Installation

Option 1 — DMG (recommended)

Download, drag, launch. CCC installs its own hooks automatically on the first run.

⚠

Gatekeeper prompt on first launch: CCC is ad-hoc signed but not yet Apple-notarized. On first open, right-click the app in Finder and choose Open, then click Open in the dialog. You only need to do this once.

✓

If you already have custom hooks in settings.json, CCC appends to them — it never overwrites existing entries. The installer is idempotent; running it multiple times is safe.

Option 2 — Build from source

bash
git clone https://github.com/sparshalc/ClaudeControlCenter
cd ClaudeControlCenter
bash scripts/build_dmg.sh
open ClaudeControlCenter-*.dmg

Requires: macOS 14+, Xcode 15+ or the Swift 5.9 command-line tools.

What the installer creates on your Mac

PathDescription
/Applications/ClaudeControlCenter.appThe app bundle — menu bar UI, local HTTP server, hook scripts bundled inside
~/.claude/ccc_token32-byte random auth token (mode 0600, readable only by you). Required by all hook scripts to authenticate with the local server.
~/.claude/settings.json8 hook entries appended. Your existing hooks are preserved. No other file is modified.
ℹ

App sandbox is disabled so CCC can read ~/.claude/, generate the token file, and execute hook shell scripts. The app communicates only over 127.0.0.1:40440 — never externally.

What the hook installer registers

The installer appends 8 entries to the hooks section of ~/.claude/settings.json:

EventScriptPurpose
PreToolUsepre-tool-use.shRisk-checks every tool call; long-polls CCC for allow/deny/session decision if needed
PermissionRequestpermission-request.shClaude Code's native "Allow this tool?" prompt — always shows the approval dialog, bypasses allow rules
PostToolUsepost-tool-use.shStreams completed tool calls to the activity feed
Stopon-stop.shSignals end-of-turn; clears the active-status indicator
SessionStartsession-start.shNotifies CCC a new Claude Code session has started
SessionEndsession-end.shNotifies CCC the session ended; clears session-scoped approvals
UserPromptSubmituser-prompt.shStreams user prompt events to the activity feed
Notificationnotification.shForwards Claude Code notifications to the CCC activity feed

Display Modes

ModeAppearanceWhen to use
Menu Bar (default)Animated icon + status text in the macOS menu bar. Click to open the full panel.Classic macOS workflow. Works on any Mac including those without a notch.
Dynamic IslandFloating black pill at the top of the screen. 3 states: hidden (idle), slim strip (active), expanded card (hover/hotkey).Minimal distraction. Keeps the menu bar clean. Best on MacBooks with the notch.

Switch between modes in the Settings tab — no restart needed. In Dynamic Island mode, press ⌘⇧I to toggle the expanded card.

Approval Flow

When Claude runs a medium/high/critical-risk command, the hook pauses execution and posts to CCC. CCC shows the approval panel and holds the HTTP connection open for up to 30 seconds. Your decision is returned to the hook, which exits accordingly:

DecisionEffect
Allow OnceAllows this single invocation. Next time the same command runs, you'll be asked again.
Allow for SessionAuto-approves this exact command for the rest of the current session. Approving echo hello does not approve rm -rf /tmp.
Always AllowCreates a persistent allow rule for this exact input pattern. Visible and removable in the Rules tab.
DenyBlocks the command. The hook returns exit code 2; Claude Code reports the denial to the user.
Claude wants to run: rm /tmp/old.log → pre-tool-use.sh fires → POST localhost:40440/events/tool-request → CCC: risk = medium → show approval panel → hook long-polls (up to 30s) → you tap Allow Once in the panel → hook exits 0 → Claude proceeds
⚠

Auto-allow on timeout: if you don't respond within 30 seconds, CCC defaults to allow. This prevents Claude from hanging indefinitely if you walk away. You can change this default in Settings.

PermissionRequest flow (Claude Code's native prompt) always bypasses allow rules and shows the approval dialog regardless of configuration:

Claude Code asks: "Allow Bash tool?" → permission-request.sh fires → POST localhost:40440/events/permission-request → CCC: always show dialog (allow rules don't apply) → hook long-polls (up to 30s) → you tap Allow / Deny → hook returns {"decision":"allow"} or {"decision":"deny"}

Risk Levels

LevelExamplesDefault
NONERead, Grep, Glob, LS, read-only git commandsAuto-allow
LOWSafe Bash, echo, cat, ls, pwdAuto-allow
MEDIUMrm <file>, pip install, kill -9, npm installRequires approval
HIGHgit push --force, git reset --hard, chmod 777, npm publishRequires approval
CRITICALrm -rf, sudo, curl … | bash, dd if=Requires approval

Classification uses regex pattern matching on the full bash argument string. The risk engine runs inside CCC — not inside the hook script — so it has no shell-injection surface.

Allow Rules

Allow rules let you permanently whitelist specific tool + input combinations so they never trigger the approval dialog.

FieldExampleMeaning
ToolBashMatch only Bash tool calls. Use * for any tool.
Patterngit status*Glob match against the primary input. Use * to match anything.
NoteRead-only gitOptional reminder. Not used for matching.

Rules are evaluated in order. The first matching rule wins. Deleting or toggling a rule takes effect immediately — no restart needed.

ℹ

Allow rules do not apply to PermissionRequest events — those always show the dialog. Allow rules only skip the dialog for tool calls that CCC itself decides to surface.

Keyboard Shortcut

In Dynamic Island mode, press ⌘⇧I (Command + Shift + I) from any app to toggle the expanded pill. The expanded view shows the last few events and an inline approve/deny card when a permission is pending.

Hooks Reference

Hook scriptClaude Code eventExit code meaning
pre-tool-use.shPreToolUse0 = allow, 1 = deny (blocks the tool call)
permission-request.shPermissionRequestReturns JSON {"decision":"allow"} or {"decision":"deny"}
post-tool-use.shPostToolUse0 always (informational only)
on-stop.shStop0 always; signals end of turn
session-start.shSessionStart0 always; starts session tracking
session-end.shSessionEnd0 always; closes session
user-prompt.shUserPromptSubmit0 always; records prompt events
notification.shNotification0 always; triggers macOS notification

All hook scripts read a JSON payload from stdin and post to localhost:40440. If CCC is not running (/health check fails), every hook exits 0 silently so Claude Code proceeds normally.

HTTP API

CCC runs a local HTTP/1.1 server on localhost:40440. Hook scripts are the only intended clients, but the endpoints are documented for contributors and advanced users.

EndpointMethodDescription
/healthGETReturns 200 if CCC is running
/events/tool-requestPOSTSubmit a pre-tool-use event. Long-polls until decision or 30s timeout.
/events/permission-requestPOSTSubmit a PermissionRequest event. Always surfaces the approval dialog.
/events/post-tool-usePOSTSubmit a completed tool call for the activity feed.
/events/stopPOSTSignal end of a Claude turn.
/events/session-startPOSTOpen a new session with project path and session ID.
/events/session-endPOSTClose the active session.
/approvals/:id/allowPOSTProgrammatically allow a pending request.
/approvals/:id/denyPOSTProgrammatically deny a pending request.

Architecture

Claude Code ──hook scripts──▶ localhost:40440 (NWListener) │ ┌────────▼────────┐ │ AppState │ @Observable, @MainActor │ RiskClassifier │ NSRegularExpression patterns │ ApprovalQueue │ Swift actor, CheckedContinuation │ AllowRulesStore│ Glob match, UserDefaults │ EventStore │ Circular buffer, recentEvents() └────────┬────────┘ │ ┌────────────────┴──────────────────┐ │ │ ┌────────▼────────┐ ┌────────────▼──────────┐ │ NSStatusItem │ │ DynamicIslandPanel │ │ + NSPopover │ │ (floating NSPanel) │ │ │ │ │ │ Activity Feed │ │ compact strip │ │ Approvals │ │ wide active strip │ │ Files │ │ expanded card │ │ Rules │ │ inline approval │ │ Settings │ └───────────────────────┘ └─────────────────┘

Key implementation notes:

  • HTTPServer — NWListener-based; no third-party networking dependencies
  • ApprovalQueue — Swift actor; withCheckedContinuation suspends the hook's curl connection until you decide or 30s elapses
  • RiskClassifier — NSRegularExpression pattern set; runs on the full bash argument string, not just the command name
  • AllowRulesStore — @Observable; glob-pattern matching; persisted to UserDefaults
  • DynamicIslandController — Atoll invariant: panel.frame.maxY == screen.frame.maxY always. Three size states driven by withObservationTracking
  • GIFAnimator — CGImageSource frame-by-frame decoding; drives the menu bar animated icons

Build from Source

Requirements
macOS 14+
Xcode 15+  (or Swift 5.9 command-line tools)
Python 3.8+
create-dmg  (optional, for styled DMG — falls back to hdiutil)
bash — build a DMG
git clone https://github.com/sparshalc/ClaudeControlCenter
cd ClaudeControlCenter
bash scripts/build_dmg.sh 0.3.1
# Output: ClaudeControlCenter-0.3.1.dmg
bash — run in Xcode
open Package.swift
# Product → Run  (or ⌘R)

The build script compiles a universal binary (arm64 + x86_64), assembles the .app bundle with all hook scripts, signs ad-hoc, and packages into a DMG.

Troubleshooting

CCC isn't receiving events from Claude Code

  • Check that the menu bar icon is visible — if CCC isn't running, hooks exit silently.
  • Open ~/.claude/settings.json and verify the CCC hook entries are present. If missing, relaunch CCC — it re-installs hooks on startup.
  • Run curl http://localhost:40440/health in a terminal. Should return 200.

The approval dialog doesn't appear for a command I expected

  • Check the Rules tab — you may have an allow rule that matches the command.
  • Verify the risk level. Commands classified as NONE or LOW auto-allow by default.
  • Check the approval timeout in Settings — if 30s has already passed, CCC defaulted to allow.

Hooks are installed but the pill/icon never appears

  • Ensure CCC is running (look for the icon in your menu bar or System Settings → Login Items).
  • Make sure Claude Code's session is actually starting — CCC shows nothing until a session event arrives.

Dynamic Island pill appears in the wrong position

  • CCC anchors the pill to NSScreen.main. On multi-monitor setups, that's whichever screen has the menu bar. Move your menu bar screen to the display with the notch.

Uninstalling

To fully remove CCC:

  1. Quit Claude Control Center from the menu bar icon.
  2. Delete /Applications/ClaudeControlCenter.app.
  3. Remove the CCC hook entries from ~/.claude/settings.json. You can run the bundled uninstaller:
    bash /Applications/ClaudeControlCenter.app/Contents/Resources/installer/install.sh --uninstall
  4. Optionally delete allow rules from UserDefaults: defaults delete app.sparshalc.claude-control-center