feat: Auto-create patrol hooks for witness/refinery/deacon (gt-qpoxz)

gt rig add now creates .claude/settings.json with patrol hooks for:
- witness/ directory (SessionStart, PreCompact, UserPromptSubmit hooks)
- refinery/ directory (same hooks)

gt deacon start also creates hooks if not present (idempotent).

These hooks run `gt prime && gt mail check --inject` on session start,
enabling autonomous patrol execution when daemon sends heartbeats.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-28 09:33:02 -08:00
parent ec870b92d8
commit f90b7d9817
2 changed files with 123 additions and 0 deletions

View File

@@ -256,6 +256,11 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
if err := m.createRoleCLAUDEmd(refineryRigPath, "refinery", opts.Name, ""); err != nil {
return nil, fmt.Errorf("creating refinery CLAUDE.md: %w", err)
}
// Create refinery hooks for patrol triggering (at refinery/ level, not rig/)
refineryPath := filepath.Dir(refineryRigPath)
if err := m.createPatrolHooks(refineryPath); err != nil {
fmt.Printf(" Warning: Could not create refinery hooks: %v\n", err)
}
// Clone repository for default crew workspace
crewPath := filepath.Join(rigPath, "crew", opts.CrewName)
@@ -275,6 +280,10 @@ func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
if err := os.MkdirAll(witnessPath, 0755); err != nil {
return nil, fmt.Errorf("creating witness dir: %w", err)
}
// Create witness hooks for patrol triggering
if err := m.createPatrolHooks(witnessPath); err != nil {
fmt.Printf(" Warning: Could not create witness hooks: %v\n", err)
}
// Create polecats directory (empty)
polecatsPath := filepath.Join(rigPath, "polecats")
@@ -573,6 +582,58 @@ func (m *Manager) createRoleCLAUDEmd(workspacePath string, role string, rigName
return os.WriteFile(claudePath, []byte(content), 0644)
}
// createPatrolHooks creates .claude/settings.json with hooks for patrol roles.
// These hooks trigger gt prime on session start and inject mail, enabling
// autonomous patrol execution for Witness and Refinery roles.
func (m *Manager) createPatrolHooks(workspacePath string) error {
claudeDir := filepath.Join(workspacePath, ".claude")
if err := os.MkdirAll(claudeDir, 0755); err != nil {
return fmt.Errorf("creating .claude dir: %w", err)
}
// Standard patrol hooks - same as deacon
hooksJSON := `{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "gt prime && gt mail check --inject"
}
]
}
],
"PreCompact": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "gt prime"
}
]
}
],
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "gt mail check --inject"
}
]
}
]
}
}
`
settingsPath := filepath.Join(claudeDir, "settings.json")
return os.WriteFile(settingsPath, []byte(hooksJSON), 0600)
}
// seedPatrolMolecules creates patrol molecule prototypes in the rig's beads database.
// These molecules define the work loops for Deacon, Witness, and Refinery roles.
func (m *Manager) seedPatrolMolecules(rigPath string) error {