mirror of
https://github.com/imjasonh/cnotes
synced 2026-07-13 11:19:43 +00:00
Since cnotes only handles PostToolUse events (and ignores all others), update the installation to only register for that specific event rather than all available hook events. This reduces unnecessary hook calls. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
177 lines
3.7 KiB
Go
177 lines
3.7 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
type Settings struct {
|
|
Hooks map[string][]HookDefinition `json:"hooks"`
|
|
}
|
|
|
|
type HookDefinition struct {
|
|
Matcher string `json:"matcher"`
|
|
Hooks []HookAction `json:"hooks"`
|
|
}
|
|
|
|
type HookAction struct {
|
|
Type string `json:"type"`
|
|
Command string `json:"command"`
|
|
Timeout int `json:"timeout,omitempty"`
|
|
}
|
|
|
|
func LoadSettings(path string) (*Settings, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return &Settings{}, nil
|
|
}
|
|
return nil, fmt.Errorf("failed to read settings: %w", err)
|
|
}
|
|
|
|
var settings Settings
|
|
if err := json.Unmarshal(data, &settings); err != nil {
|
|
return nil, fmt.Errorf("failed to parse settings: %w", err)
|
|
}
|
|
|
|
return &settings, nil
|
|
}
|
|
|
|
func SaveSettings(path string, settings *Settings) error {
|
|
dir := filepath.Dir(path)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create settings directory: %w", err)
|
|
}
|
|
|
|
data, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal settings: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(path, data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write settings: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetSettingsPath() string {
|
|
return GetGlobalSettingsPath()
|
|
}
|
|
|
|
func GetGlobalSettingsPath() string {
|
|
if path := os.Getenv("CLAUDE_SETTINGS_PATH"); path != "" {
|
|
return path
|
|
}
|
|
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return filepath.Join(home, ".claude", "settings.json")
|
|
}
|
|
|
|
func GetLocalSettingsPath() string {
|
|
return filepath.Join(".", ".claude", "settings.local.json")
|
|
}
|
|
|
|
func GetProjectSettingsPath() string {
|
|
return filepath.Join(".", ".claude", "settings.json")
|
|
}
|
|
|
|
func InstallHooks(binaryPath string) error {
|
|
return InstallHooksToPath(binaryPath, GetSettingsPath())
|
|
}
|
|
|
|
func InstallHooksToPath(binaryPath, settingsPath string) error {
|
|
settings, err := LoadSettings(settingsPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if settings.Hooks == nil {
|
|
settings.Hooks = make(map[string][]HookDefinition)
|
|
}
|
|
|
|
hookAction := HookAction{
|
|
Type: "command",
|
|
Command: binaryPath,
|
|
}
|
|
|
|
hookDef := HookDefinition{
|
|
Matcher: ".*",
|
|
Hooks: []HookAction{hookAction},
|
|
}
|
|
|
|
// Only register for PostToolUse events since that's all we handle
|
|
claudeEvent := "PostToolUse"
|
|
|
|
// Check if our hook is already installed
|
|
found := false
|
|
for i, def := range settings.Hooks[claudeEvent] {
|
|
for j, action := range def.Hooks {
|
|
if action.Command == binaryPath {
|
|
// Update existing hook
|
|
settings.Hooks[claudeEvent][i].Hooks[j] = hookAction
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
// Add new hook
|
|
settings.Hooks[claudeEvent] = append(settings.Hooks[claudeEvent], hookDef)
|
|
}
|
|
|
|
return SaveSettings(settingsPath, settings)
|
|
}
|
|
|
|
func UninstallHooks(binaryPath string) error {
|
|
return UninstallHooksFromPath(binaryPath, GetSettingsPath())
|
|
}
|
|
|
|
func UninstallHooksFromPath(binaryPath, settingsPath string) error {
|
|
settings, err := LoadSettings(settingsPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if settings.Hooks == nil {
|
|
return nil
|
|
}
|
|
|
|
// Remove our hook from all events
|
|
for eventName, hookDefs := range settings.Hooks {
|
|
newDefs := make([]HookDefinition, 0)
|
|
|
|
for _, def := range hookDefs {
|
|
newActions := make([]HookAction, 0)
|
|
for _, action := range def.Hooks {
|
|
if action.Command != binaryPath {
|
|
newActions = append(newActions, action)
|
|
}
|
|
}
|
|
|
|
// Only keep the definition if it still has actions
|
|
if len(newActions) > 0 {
|
|
def.Hooks = newActions
|
|
newDefs = append(newDefs, def)
|
|
}
|
|
}
|
|
|
|
if len(newDefs) > 0 {
|
|
settings.Hooks[eventName] = newDefs
|
|
} else {
|
|
delete(settings.Hooks, eventName)
|
|
}
|
|
}
|
|
|
|
return SaveSettings(settingsPath, settings)
|
|
}
|