-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhook_claude.go
More file actions
158 lines (137 loc) · 3.72 KB
/
Copy pathhook_claude.go
File metadata and controls
158 lines (137 loc) · 3.72 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
148
149
150
151
152
153
154
155
156
157
158
package main
import (
"encoding/json"
"os"
"path/filepath"
)
// Claude Code specific hook installation.
// This file contains only Claude-specific code. When another agent
// supports hooks, create a similar hook_<agent>.go file.
// claudeSettingsPath returns the path to Claude Code's settings.json
func claudeSettingsPath() string {
home, err := os.UserHomeDir()
if err != nil {
return ""
}
return filepath.Join(home, ".claude", "settings.json")
}
// hookCommand is the command that Claude Code will execute for each hook event
const hookCommand = "aipilot-cli --agent-event"
// claudeHookEvents lists the Claude Code events we want to hook into
var claudeHookEvents = []string{
"UserPromptSubmit",
"PreToolUse",
"Stop",
"StopFailure",
"Notification",
"PermissionRequest",
"TaskCompleted",
}
// ensureClaudeHooksInstalled reads ~/.claude/settings.json and adds
// aipilot hook entries for agent status detection if not already present.
func ensureClaudeHooksInstalled() {
settingsPath := claudeSettingsPath()
if settingsPath == "" {
return
}
// Read existing settings
settings, err := readClaudeSettings(settingsPath)
if err != nil {
// Cannot read settings — skip hook installation
return
}
// Ensure hooks section exists
hooks, ok := settings["hooks"].(map[string]interface{})
if !ok {
hooks = make(map[string]interface{})
settings["hooks"] = hooks
}
modified := false
for _, eventName := range claudeHookEvents {
if addClaudeHookIfMissing(hooks, eventName) {
modified = true
}
}
if !modified {
return
}
// Write back
if err := writeClaudeSettings(settingsPath, settings); err != nil {
// Cannot write settings — skip
return
}
// Hooks installed successfully
}
// addClaudeHookIfMissing adds an aipilot hook entry to the given event array
// if one doesn't already exist. Returns true if the settings were modified.
func addClaudeHookIfMissing(hooks map[string]interface{}, eventName string) bool {
// Get or create the event array
var eventEntries []interface{}
if existing, ok := hooks[eventName]; ok {
if arr, ok := existing.([]interface{}); ok {
eventEntries = arr
}
}
// Check if aipilot hook is already installed
for _, entry := range eventEntries {
entryMap, ok := entry.(map[string]interface{})
if !ok {
continue
}
hooksList, ok := entryMap["hooks"].([]interface{})
if !ok {
continue
}
for _, h := range hooksList {
hookMap, ok := h.(map[string]interface{})
if !ok {
continue
}
if cmd, ok := hookMap["command"].(string); ok && cmd == hookCommand {
return false // Already installed
}
}
}
// Add new entry
newEntry := map[string]interface{}{
"matcher": "",
"hooks": []interface{}{
map[string]interface{}{
"type": "command",
"command": hookCommand,
"timeout": 5,
"async": true,
},
},
}
hooks[eventName] = append(eventEntries, newEntry)
return true
}
// readClaudeSettings reads and parses Claude Code's settings.json
func readClaudeSettings(path string) (map[string]interface{}, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
// No settings file yet — start fresh
return make(map[string]interface{}), nil
}
return nil, err
}
var settings map[string]interface{}
if err := json.Unmarshal(data, &settings); err != nil {
return nil, err
}
return settings, nil
}
// writeClaudeSettings writes settings back to Claude Code's settings.json
func writeClaudeSettings(path string, settings map[string]interface{}) error {
// Ensure directory exists
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
data, err := json.MarshalIndent(settings, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, append(data, '\n'), 0644)
}