-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
147 lines (131 loc) · 5.24 KB
/
install.sh
File metadata and controls
147 lines (131 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env bash
# claude-code-ding installer
# Adds audio notification hooks to your Claude Code settings
# https://github.com/whoisjaso/claude-code-ding
set -euo pipefail
SETTINGS_FILE="$HOME/.claude/settings.json"
echo ""
echo " claude-code-ding"
echo " Audio notifications for Claude Code"
echo " ─────────────────────────────────────"
echo ""
# Detect OS
if [[ "$OSTYPE" == "darwin"* ]]; then
PLATFORM="macos"
echo " Platform: macOS"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
PLATFORM="linux"
echo " Platform: Linux"
# Check for paplay
if ! command -v paplay &>/dev/null; then
echo ""
echo " Warning: paplay not found. Install pulseaudio-utils:"
echo " sudo apt install pulseaudio-utils"
echo ""
echo " Falling back to terminal bell."
fi
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
PLATFORM="windows"
echo " Platform: Windows"
else
echo " Platform: Unknown ($OSTYPE), using Linux profile"
PLATFORM="linux"
fi
echo ""
# Ensure .claude directory exists
mkdir -p "$HOME/.claude"
# Define hooks per platform
case "$PLATFORM" in
windows)
QUESTION_CMD='powershell -NoProfile -Command \"[console]::beep(900,150); [console]::beep(1100,150); [console]::beep(900,150)\"'
STOP_CMD='powershell -NoProfile -Command \"[console]::beep(700,300); Start-Sleep -Milliseconds 100; [console]::beep(700,300)\"'
TASK_CMD='powershell -NoProfile -Command \"[console]::beep(600,150); [console]::beep(800,150); [console]::beep(1000,300)\"'
;;
macos)
QUESTION_CMD='afplay /System/Library/Sounds/Tink.aiff; afplay /System/Library/Sounds/Tink.aiff; afplay /System/Library/Sounds/Tink.aiff'
STOP_CMD='afplay /System/Library/Sounds/Pop.aiff; sleep 0.1; afplay /System/Library/Sounds/Pop.aiff'
TASK_CMD='afplay /System/Library/Sounds/Glass.aiff'
;;
linux)
QUESTION_CMD="paplay /usr/share/sounds/freedesktop/stereo/dialog-information.oga 2>/dev/null || echo -e '\\\\a'"
STOP_CMD="paplay /usr/share/sounds/freedesktop/stereo/message-new-instant.oga 2>/dev/null || echo -e '\\\\a'"
TASK_CMD="paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null || echo -e '\\\\a'"
;;
esac
# Build the hooks JSON using node (available on all Claude Code systems)
if ! command -v node &>/dev/null; then
echo " Error: Node.js is required (Claude Code depends on it)."
exit 1
fi
node -e "
const fs = require('fs');
const path = '$SETTINGS_FILE';
let settings = {};
if (fs.existsSync(path)) {
try {
settings = JSON.parse(fs.readFileSync(path, 'utf8'));
} catch (e) {
console.error(' Error: Invalid JSON in ' + path);
console.error(' Please fix your settings file and try again.');
process.exit(1);
}
}
if (!settings.hooks) settings.hooks = {};
// Add PreToolUse for AskUserQuestion (merge with existing)
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
const hasAskHook = settings.hooks.PreToolUse.some(h => h.matcher === 'AskUserQuestion');
if (!hasAskHook) {
settings.hooks.PreToolUse.push({
matcher: 'AskUserQuestion',
hooks: [{
type: 'command',
command: $(printf '%s' "$QUESTION_CMD" | node -e "process.stdout.write(JSON.stringify(require('fs').readFileSync(0,'utf8')))"),
timeout: 5,
async: true
}]
});
}
// Add Stop hook (merge with existing)
if (!settings.hooks.Stop) settings.hooks.Stop = [];
const hasStopDing = settings.hooks.Stop.some(h =>
h.hooks && h.hooks.some(hh => hh.command && hh.command.includes('beep') || hh.command && hh.command.includes('afplay') || hh.command && hh.command.includes('paplay'))
);
if (!hasStopDing) {
settings.hooks.Stop.push({
hooks: [{
type: 'command',
command: $(printf '%s' "$STOP_CMD" | node -e "process.stdout.write(JSON.stringify(require('fs').readFileSync(0,'utf8')))"),
timeout: 5,
async: true
}]
});
}
// Add TaskCompleted hook (merge with existing)
if (!settings.hooks.TaskCompleted) settings.hooks.TaskCompleted = [];
const hasTaskDing = settings.hooks.TaskCompleted.some(h =>
h.hooks && h.hooks.some(hh => hh.command && hh.command.includes('beep') || hh.command && hh.command.includes('afplay') || hh.command && hh.command.includes('paplay'))
);
if (!hasTaskDing) {
settings.hooks.TaskCompleted.push({
hooks: [{
type: 'command',
command: $(printf '%s' "$TASK_CMD" | node -e "process.stdout.write(JSON.stringify(require('fs').readFileSync(0,'utf8')))"),
timeout: 5,
async: true
}]
});
}
fs.writeFileSync(path, JSON.stringify(settings, null, 2) + '\n');
console.log(' Done! Hooks added to ' + path);
"
echo ""
echo " Three notification hooks installed:"
echo " - Question asked → attention beeps"
echo " - Stopped / input → ready signal"
echo " - Task completed → completion chime"
echo ""
echo " Restart Claude Code or open /hooks to activate."
echo ""
echo " Customize sounds: edit hooks in $SETTINGS_FILE"
echo " Uninstall: remove the hook entries from settings.json"
echo ""