// Package claude provides Claude Code configuration management. package claude import ( "embed" "fmt" "os" "path/filepath" ) //go:embed config/*.json var configFS embed.FS // RoleType indicates whether a role is autonomous or interactive. type RoleType string const ( // Autonomous roles (polecat, witness, refinery) need mail in SessionStart // because they may be triggered externally without user input. Autonomous RoleType = "autonomous" // Interactive roles (mayor, crew) wait for user input, so UserPromptSubmit // handles mail injection. Interactive RoleType = "interactive" ) // RoleTypeFor returns the RoleType for a given role name. func RoleTypeFor(role string) RoleType { switch role { case "polecat", "witness", "refinery", "deacon": return Autonomous default: return Interactive } } // EnsureSettings ensures .claude/settings.json exists in the given directory. // For worktrees, we use sparse checkout to exclude source repo's .claude/ directory, // so our settings.json is the only one Claude Code sees. func EnsureSettings(workDir string, roleType RoleType) error { return EnsureSettingsAt(workDir, roleType, ".claude", "settings.json") } // EnsureSettingsAt ensures a settings file exists at a custom directory/file. // If the file doesn't exist, it copies the appropriate template based on role type. // If the file already exists, it's left unchanged. func EnsureSettingsAt(workDir string, roleType RoleType, settingsDir, settingsFile string) error { claudeDir := filepath.Join(workDir, settingsDir) settingsPath := filepath.Join(claudeDir, settingsFile) // If settings already exist, don't overwrite if _, err := os.Stat(settingsPath); err == nil { return nil } // Create settings directory if needed if err := os.MkdirAll(claudeDir, 0755); err != nil { return fmt.Errorf("creating settings directory: %w", err) } // Select template based on role type var templateName string switch roleType { case Autonomous: templateName = "config/settings-autonomous.json" default: templateName = "config/settings-interactive.json" } // Read template content, err := configFS.ReadFile(templateName) if err != nil { return fmt.Errorf("reading template %s: %w", templateName, err) } // Write settings file if err := os.WriteFile(settingsPath, content, 0600); err != nil { return fmt.Errorf("writing settings: %w", err) } return nil } // EnsureSettingsForRole is a convenience function that combines RoleTypeFor and EnsureSettings. func EnsureSettingsForRole(workDir, role string) error { return EnsureSettings(workDir, RoleTypeFor(role)) } // EnsureSettingsForRoleAt is a convenience function that combines RoleTypeFor and EnsureSettingsAt. func EnsureSettingsForRoleAt(workDir, role, settingsDir, settingsFile string) error { return EnsureSettingsAt(workDir, RoleTypeFor(role), settingsDir, settingsFile) }