Documentation
Everything you need to install, configure, and extend Claude Control Center.
Quick Start
Download the DMG
Grab the latest ClaudeControlCenter.dmg from the GitHub Releases page.
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.
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.
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
| Requirement | Version | Notes |
|---|---|---|
| macOS | 14 Sonoma or later | Universal binary — Apple Silicon & Intel |
| Claude Code CLI | Any version with hook support | Install with npm i -g @anthropic-ai/claude-code |
| Python 3 | 3.8+ | Required by hook scripts for JSON parsing |
| curl | Any | Bundled 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
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
| Path | Description |
|---|---|
/Applications/ClaudeControlCenter.app | The app bundle — menu bar UI, local HTTP server, hook scripts bundled inside |
~/.claude/ccc_token | 32-byte random auth token (mode 0600, readable only by you). Required by all hook scripts to authenticate with the local server. |
~/.claude/settings.json | 8 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:
| Event | Script | Purpose |
|---|---|---|
PreToolUse | pre-tool-use.sh | Risk-checks every tool call; long-polls CCC for allow/deny/session decision if needed |
PermissionRequest | permission-request.sh | Claude Code's native "Allow this tool?" prompt — always shows the approval dialog, bypasses allow rules |
PostToolUse | post-tool-use.sh | Streams completed tool calls to the activity feed |
Stop | on-stop.sh | Signals end-of-turn; clears the active-status indicator |
SessionStart | session-start.sh | Notifies CCC a new Claude Code session has started |
SessionEnd | session-end.sh | Notifies CCC the session ended; clears session-scoped approvals |
UserPromptSubmit | user-prompt.sh | Streams user prompt events to the activity feed |
Notification | notification.sh | Forwards Claude Code notifications to the CCC activity feed |
Display Modes
| Mode | Appearance | When 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 Island | Floating 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:
| Decision | Effect |
|---|---|
| Allow Once | Allows this single invocation. Next time the same command runs, you'll be asked again. |
| Allow for Session | Auto-approves this exact command for the rest of the current session. Approving echo hello does not approve rm -rf /tmp. |
| Always Allow | Creates a persistent allow rule for this exact input pattern. Visible and removable in the Rules tab. |
| Deny | Blocks the command. The hook returns exit code 2; Claude Code reports the denial to the user. |
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:
Risk Levels
| Level | Examples | Default |
|---|---|---|
| NONE | Read, Grep, Glob, LS, read-only git commands | Auto-allow |
| LOW | Safe Bash, echo, cat, ls, pwd | Auto-allow |
| MEDIUM | rm <file>, pip install, kill -9, npm install | Requires approval |
| HIGH | git push --force, git reset --hard, chmod 777, npm publish | Requires approval |
| CRITICAL | rm -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.
| Field | Example | Meaning |
|---|---|---|
| Tool | Bash | Match only Bash tool calls. Use * for any tool. |
| Pattern | git status* | Glob match against the primary input. Use * to match anything. |
| Note | Read-only git | Optional 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 script | Claude Code event | Exit code meaning |
|---|---|---|
pre-tool-use.sh | PreToolUse | 0 = allow, 1 = deny (blocks the tool call) |
permission-request.sh | PermissionRequest | Returns JSON {"decision":"allow"} or {"decision":"deny"} |
post-tool-use.sh | PostToolUse | 0 always (informational only) |
on-stop.sh | Stop | 0 always; signals end of turn |
session-start.sh | SessionStart | 0 always; starts session tracking |
session-end.sh | SessionEnd | 0 always; closes session |
user-prompt.sh | UserPromptSubmit | 0 always; records prompt events |
notification.sh | Notification | 0 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.
| Endpoint | Method | Description |
|---|---|---|
/health | GET | Returns 200 if CCC is running |
/events/tool-request | POST | Submit a pre-tool-use event. Long-polls until decision or 30s timeout. |
/events/permission-request | POST | Submit a PermissionRequest event. Always surfaces the approval dialog. |
/events/post-tool-use | POST | Submit a completed tool call for the activity feed. |
/events/stop | POST | Signal end of a Claude turn. |
/events/session-start | POST | Open a new session with project path and session ID. |
/events/session-end | POST | Close the active session. |
/approvals/:id/allow | POST | Programmatically allow a pending request. |
/approvals/:id/deny | POST | Programmatically deny a pending request. |
Architecture
Key implementation notes:
- HTTPServer —
NWListener-based; no third-party networking dependencies - ApprovalQueue — Swift actor;
withCheckedContinuationsuspends the hook's curl connection until you decide or 30s elapses - RiskClassifier —
NSRegularExpressionpattern set; runs on the full bash argument string, not just the command name - AllowRulesStore —
@Observable; glob-pattern matching; persisted toUserDefaults - DynamicIslandController — Atoll invariant:
panel.frame.maxY == screen.frame.maxYalways. Three size states driven bywithObservationTracking - GIFAnimator —
CGImageSourceframe-by-frame decoding; drives the menu bar animated icons
Build from Source
macOS 14+ Xcode 15+ (or Swift 5.9 command-line tools) Python 3.8+ create-dmg (optional, for styled DMG — falls back to hdiutil)
git clone https://github.com/sparshalc/ClaudeControlCenter cd ClaudeControlCenter bash scripts/build_dmg.sh 0.3.1 # Output: ClaudeControlCenter-0.3.1.dmg
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.jsonand verify the CCC hook entries are present. If missing, relaunch CCC — it re-installs hooks on startup. - Run
curl http://localhost:40440/healthin 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:
- Quit Claude Control Center from the menu bar icon.
- Delete
/Applications/ClaudeControlCenter.app. - 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
- Optionally delete allow rules from
UserDefaults:defaults delete app.sparshalc.claude-control-center