feat: add hook status to gt status command (gt-h6eq.6)
Add HookInfo struct and hook discovery to show which agents have work attached to their hooks. Updates both JSON and text output: - New HookInfo type with agent, role, has_work, molecule, and title fields - RigStatus now includes Hooks slice - StatusSum includes ActiveHooks count - discoverRigHooks() scans polecats, crew, witness, and refinery - getAgentHook() retrieves hook status from handoff beads - Text output shows active hooks per rig with agent and molecule info 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/beads"
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/constants"
|
||||
"github.com/steveyegge/gastown/internal/crew"
|
||||
@@ -43,13 +44,23 @@ type TownStatus struct {
|
||||
|
||||
// RigStatus represents status of a single rig.
|
||||
type RigStatus struct {
|
||||
Name string `json:"name"`
|
||||
Polecats []string `json:"polecats"`
|
||||
PolecatCount int `json:"polecat_count"`
|
||||
Crews []string `json:"crews"`
|
||||
CrewCount int `json:"crew_count"`
|
||||
HasWitness bool `json:"has_witness"`
|
||||
HasRefinery bool `json:"has_refinery"`
|
||||
Name string `json:"name"`
|
||||
Polecats []string `json:"polecats"`
|
||||
PolecatCount int `json:"polecat_count"`
|
||||
Crews []string `json:"crews"`
|
||||
CrewCount int `json:"crew_count"`
|
||||
HasWitness bool `json:"has_witness"`
|
||||
HasRefinery bool `json:"has_refinery"`
|
||||
Hooks []AgentHookInfo `json:"hooks,omitempty"`
|
||||
}
|
||||
|
||||
// AgentHookInfo represents an agent's hook (pinned work) status.
|
||||
type AgentHookInfo struct {
|
||||
Agent string `json:"agent"` // Agent address (e.g., "gastown/toast", "gastown/witness")
|
||||
Role string `json:"role"` // Role type (polecat, crew, witness, refinery)
|
||||
HasWork bool `json:"has_work"` // Whether agent has pinned work
|
||||
Molecule string `json:"molecule,omitempty"` // Attached molecule ID
|
||||
Title string `json:"title,omitempty"` // Pinned bead title
|
||||
}
|
||||
|
||||
// StatusSum provides summary counts.
|
||||
@@ -59,6 +70,7 @@ type StatusSum struct {
|
||||
CrewCount int `json:"crew_count"`
|
||||
WitnessCount int `json:"witness_count"`
|
||||
RefineryCount int `json:"refinery_count"`
|
||||
ActiveHooks int `json:"active_hooks"`
|
||||
}
|
||||
|
||||
func runStatus(cmd *cobra.Command, args []string) error {
|
||||
@@ -120,6 +132,14 @@ func runStatus(cmd *cobra.Command, args []string) error {
|
||||
rs.CrewCount = len(workers)
|
||||
}
|
||||
|
||||
// Discover hooks for all agents in this rig
|
||||
rs.Hooks = discoverRigHooks(r, rs.Crews)
|
||||
for _, hook := range rs.Hooks {
|
||||
if hook.HasWork {
|
||||
status.Summary.ActiveHooks++
|
||||
}
|
||||
}
|
||||
|
||||
status.Rigs = append(status.Rigs, rs)
|
||||
|
||||
// Update summary
|
||||
@@ -159,6 +179,7 @@ func outputStatusText(status TownStatus) error {
|
||||
fmt.Printf(" Crews: %d\n", status.Summary.CrewCount)
|
||||
fmt.Printf(" Witnesses: %d\n", status.Summary.WitnessCount)
|
||||
fmt.Printf(" Refineries: %d\n", status.Summary.RefineryCount)
|
||||
fmt.Printf(" Active Hooks: %d\n", status.Summary.ActiveHooks)
|
||||
|
||||
if len(status.Rigs) == 0 {
|
||||
fmt.Printf("\n%s\n", style.Dim.Render("No rigs registered. Use 'gt rig add' to add one."))
|
||||
@@ -191,7 +212,90 @@ func outputStatusText(status TownStatus) error {
|
||||
if len(r.Crews) > 0 {
|
||||
fmt.Printf(" Crews: %v\n", r.Crews)
|
||||
}
|
||||
|
||||
// Show active hooks
|
||||
activeHooks := []AgentHookInfo{}
|
||||
for _, h := range r.Hooks {
|
||||
if h.HasWork {
|
||||
activeHooks = append(activeHooks, h)
|
||||
}
|
||||
}
|
||||
if len(activeHooks) > 0 {
|
||||
fmt.Printf(" %s\n", style.Bold.Render("Hooks:"))
|
||||
for _, h := range activeHooks {
|
||||
if h.Molecule != "" {
|
||||
fmt.Printf(" %s %s → %s\n", AgentTypeIcons[AgentPolecat], h.Agent, h.Molecule)
|
||||
} else if h.Title != "" {
|
||||
fmt.Printf(" %s %s → %s\n", AgentTypeIcons[AgentPolecat], h.Agent, h.Title)
|
||||
} else {
|
||||
fmt.Printf(" %s %s → (work attached)\n", AgentTypeIcons[AgentPolecat], h.Agent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// discoverRigHooks finds all hook attachments for agents in a rig.
|
||||
// It scans polecats, crew workers, witness, and refinery for handoff beads.
|
||||
func discoverRigHooks(r *rig.Rig, crews []string) []AgentHookInfo {
|
||||
var hooks []AgentHookInfo
|
||||
|
||||
// Create beads instance for the rig
|
||||
b := beads.New(r.Path)
|
||||
|
||||
// Check polecats
|
||||
for _, name := range r.Polecats {
|
||||
hook := getAgentHook(b, name, r.Name+"/"+name, "polecat")
|
||||
hooks = append(hooks, hook)
|
||||
}
|
||||
|
||||
// Check crew workers
|
||||
for _, name := range crews {
|
||||
hook := getAgentHook(b, name, r.Name+"/crew/"+name, "crew")
|
||||
hooks = append(hooks, hook)
|
||||
}
|
||||
|
||||
// Check witness
|
||||
if r.HasWitness {
|
||||
hook := getAgentHook(b, "witness", r.Name+"/witness", "witness")
|
||||
hooks = append(hooks, hook)
|
||||
}
|
||||
|
||||
// Check refinery
|
||||
if r.HasRefinery {
|
||||
hook := getAgentHook(b, "refinery", r.Name+"/refinery", "refinery")
|
||||
hooks = append(hooks, hook)
|
||||
}
|
||||
|
||||
return hooks
|
||||
}
|
||||
|
||||
// getAgentHook retrieves hook status for a specific agent.
|
||||
func getAgentHook(b *beads.Beads, role, agentAddress, roleType string) AgentHookInfo {
|
||||
hook := AgentHookInfo{
|
||||
Agent: agentAddress,
|
||||
Role: roleType,
|
||||
}
|
||||
|
||||
// Find handoff bead for this role
|
||||
handoff, err := b.FindHandoffBead(role)
|
||||
if err != nil || handoff == nil {
|
||||
return hook
|
||||
}
|
||||
|
||||
// Check for attachment
|
||||
attachment := beads.ParseAttachmentFields(handoff)
|
||||
if attachment != nil && attachment.AttachedMolecule != "" {
|
||||
hook.HasWork = true
|
||||
hook.Molecule = attachment.AttachedMolecule
|
||||
hook.Title = handoff.Title
|
||||
} else if handoff.Description != "" {
|
||||
// Has content but no molecule - still has work
|
||||
hook.HasWork = true
|
||||
hook.Title = handoff.Title
|
||||
}
|
||||
|
||||
return hook
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user