cmux/docs-site/content/docs/claude-code-hooks.mdx
Lawrence Chen 9817d131f8
Release v1.23.0 (#31)
* Rename cmuxterm to cmux across entire codebase

- Rename GitHub repos: manaflow-ai/cmuxterm -> manaflow-ai/cmux,
  manaflow-ai/homebrew-cmuxterm -> manaflow-ai/homebrew-cmux
- Rename bundle IDs: com.cmuxterm.app -> com.cmux.app
- Rename CLI: CLI/cmuxterm.swift -> CLI/cmux.swift
- Rename homebrew submodule: homebrew-cmuxterm -> homebrew-cmux
- Update all socket paths: /tmp/cmuxterm*.sock -> /tmp/cmux*.sock
- Update all GitHub URLs, DMG names, Sparkle URLs
- Update all source files, scripts, tests, docs, CI workflows

* Bump version to 1.23.0
2026-02-09 15:30:43 -08:00

266 lines
5.3 KiB
Text

---
title: Claude Code Hooks
description: Set up notifications for Claude Code using cmux hooks
---
# Claude Code Hooks
cmux integrates with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) via hooks to notify you when tasks complete or when Claude needs attention.
## Detecting cmux
Before sending notifications, you should detect if you're running inside cmux to avoid conflicts with other terminals.
### Detection Methods
**1. Check for the socket:**
```bash
[ -S /tmp/cmux.sock ] && echo "In cmux"
```
**2. Check for the CLI:**
```bash
command -v cmux &>/dev/null && echo "cmux available"
```
**3. Check the TERM_PROGRAM environment variable:**
```bash
[ "$TERM_PROGRAM" = "ghostty" ] && [ -S /tmp/cmux.sock ] && echo "In cmux"
```
<Callout type="info">
cmux sets `TERM_PROGRAM=ghostty` since it's built on Ghostty. Use the socket check to distinguish from regular Ghostty.
</Callout>
## Setting Up Hooks
Claude Code supports hooks that run on specific events. Add these to your `~/.claude/settings.json`:
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Task",
"hooks": [
"/path/to/cmux-notify.sh"
]
}
],
"Stop": [
"/path/to/cmux-notify.sh"
]
}
}
```
## Notification Hook Script
Create a script that checks for cmux and sends notifications:
```bash
#!/bin/bash
# ~/.claude/hooks/cmux-notify.sh
# Only proceed if we're in cmux
if [ ! -S /tmp/cmux.sock ]; then
exit 0
fi
# Parse the hook event from stdin (Claude Code passes JSON)
EVENT=$(cat)
# Extract event type and details
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
TOOL_NAME=$(echo "$EVENT" | jq -r '.tool_name // ""')
SESSION=$(echo "$EVENT" | jq -r '.session_id // ""' | cut -c1-8)
case "$EVENT_TYPE" in
"Stop")
cmux notify \
--title "Claude Code" \
--subtitle "Task Complete" \
--body "Session $SESSION finished"
;;
"PostToolUse")
if [ "$TOOL_NAME" = "Task" ]; then
cmux notify \
--title "Claude Code" \
--subtitle "Agent Finished" \
--body "Task agent completed in session $SESSION"
fi
;;
esac
```
Make it executable:
```bash
chmod +x ~/.claude/hooks/cmux-notify.sh
```
## Example Configurations
### Notify on All Completions
Get notified whenever Claude Code finishes a task:
```json
{
"hooks": {
"Stop": [
"~/.claude/hooks/cmux-notify.sh"
]
}
}
```
### Notify on Long-Running Tasks
Only notify for Task tool completions (agent subprocesses):
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Task",
"hooks": ["~/.claude/hooks/cmux-notify.sh"]
}
]
}
}
```
### Notify on Errors
Add error notifications:
```bash
#!/bin/bash
# cmux-notify.sh with error handling
if [ ! -S /tmp/cmux.sock ]; then
exit 0
fi
EVENT=$(cat)
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
ERROR=$(echo "$EVENT" | jq -r '.error // ""')
if [ -n "$ERROR" ] && [ "$ERROR" != "null" ]; then
cmux notify \
--title "Claude Code Error" \
--body "$ERROR"
elif [ "$EVENT_TYPE" = "Stop" ]; then
cmux notify \
--title "Claude Code" \
--body "Task complete"
fi
```
## Advanced: Conditional Notifications
Only notify if the terminal is not focused:
```bash
#!/bin/bash
# cmux-notify.sh with focus detection
if [ ! -S /tmp/cmux.sock ]; then
exit 0
fi
# cmux automatically suppresses notifications for focused tabs,
# so we can always send - it will handle suppression for us
EVENT=$(cat)
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
if [ "$EVENT_TYPE" = "Stop" ]; then
cmux notify \
--title "Claude Code" \
--subtitle "Ready" \
--body "Waiting for your input"
fi
```
## Using OSC Sequences Directly
If you prefer not to use the CLI, you can emit OSC sequences directly:
```bash
#!/bin/bash
# Direct OSC notification (no CLI needed)
if [ ! -S /tmp/cmux.sock ]; then
exit 0
fi
EVENT=$(cat)
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
if [ "$EVENT_TYPE" = "Stop" ]; then
# OSC 777 notification
printf '\e]777;notify;Claude Code;Task complete\a'
fi
```
## Full Example Setup
1. Create the hooks directory:
```bash
mkdir -p ~/.claude/hooks
```
2. Create the notification script:
```bash
cat > ~/.claude/hooks/cmux-notify.sh << 'EOF'
#!/bin/bash
# cmux notification hook for Claude Code
# Skip if not in cmux
[ -S /tmp/cmux.sock ] || exit 0
EVENT=$(cat)
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
TOOL=$(echo "$EVENT" | jq -r '.tool_name // ""')
case "$EVENT_TYPE" in
"Stop")
cmux notify --title "Claude Code" --body "Session complete"
;;
"PostToolUse")
[ "$TOOL" = "Task" ] && cmux notify --title "Claude Code" --body "Agent finished"
;;
esac
EOF
chmod +x ~/.claude/hooks/cmux-notify.sh
```
3. Configure Claude Code:
```bash
cat > ~/.claude/settings.json << 'EOF'
{
"hooks": {
"Stop": ["~/.claude/hooks/cmux-notify.sh"],
"PostToolUse": [
{
"matcher": "Task",
"hooks": ["~/.claude/hooks/cmux-notify.sh"]
}
]
}
}
EOF
```
4. Restart Claude Code to apply the hooks.
Now you'll receive desktop notifications in cmux whenever Claude Code finishes a task or needs attention.