perf: Optimize gt status from 13s to <1s (gt-zdtpw)
Key optimizations: - Use --no-daemon for bd commands to avoid daemon IPC overhead - Pre-fetch all agent beads in single query (ListAgentBeads) - Pre-fetch all hook beads in single query (ShowMultiple) - Pre-fetch all tmux sessions for O(1) lookup - Parallel rig processing with goroutines - Add --fast flag to skip mail lookups The main bottleneck was bd daemon communication timing out on stale daemon processes, causing 5+ second delays per rig. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
6d1d1469b2
commit
22557e6917
@@ -218,7 +218,10 @@ func New(workDir string) *Beads {
|
|||||||
|
|
||||||
// run executes a bd command and returns stdout.
|
// run executes a bd command and returns stdout.
|
||||||
func (b *Beads) run(args ...string) ([]byte, error) {
|
func (b *Beads) run(args ...string) ([]byte, error) {
|
||||||
cmd := exec.Command("bd", args...)
|
// Use --no-daemon for faster read operations (avoids daemon IPC overhead)
|
||||||
|
// The daemon is primarily useful for write coalescing, not reads
|
||||||
|
fullArgs := append([]string{"--no-daemon"}, args...)
|
||||||
|
cmd := exec.Command("bd", fullArgs...)
|
||||||
cmd.Dir = b.workDir
|
cmd.Dir = b.workDir
|
||||||
|
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
@@ -389,6 +392,55 @@ func (b *Beads) Show(id string) (*Issue, error) {
|
|||||||
return issues[0], nil
|
return issues[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowMultiple fetches multiple issues by ID in a single bd call.
|
||||||
|
// Returns a map of ID to Issue. Missing IDs are not included in the map.
|
||||||
|
func (b *Beads) ShowMultiple(ids []string) (map[string]*Issue, error) {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return make(map[string]*Issue), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bd show supports multiple IDs
|
||||||
|
args := append([]string{"show", "--json"}, ids...)
|
||||||
|
out, err := b.run(args...)
|
||||||
|
if err != nil {
|
||||||
|
// If bd fails, return empty map (some IDs might not exist)
|
||||||
|
return make(map[string]*Issue), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var issues []*Issue
|
||||||
|
if err := json.Unmarshal(out, &issues); err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing bd show output: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]*Issue, len(issues))
|
||||||
|
for _, issue := range issues {
|
||||||
|
result[issue.ID] = issue
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAgentBeads returns all agent beads in a single query.
|
||||||
|
// Returns a map of agent bead ID to Issue.
|
||||||
|
func (b *Beads) ListAgentBeads() (map[string]*Issue, error) {
|
||||||
|
out, err := b.run("list", "--type=agent", "--json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var issues []*Issue
|
||||||
|
if err := json.Unmarshal(out, &issues); err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing bd list output: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]*Issue, len(issues))
|
||||||
|
for _, issue := range issues {
|
||||||
|
result[issue.ID] = issue
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Blocked returns issues that are blocked by dependencies.
|
// Blocked returns issues that are blocked by dependencies.
|
||||||
func (b *Beads) Blocked() ([]*Issue, error) {
|
func (b *Beads) Blocked() ([]*Issue, error) {
|
||||||
out, err := b.run("blocked", "--json")
|
out, err := b.run("blocked", "--json")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
@@ -21,6 +22,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var statusJSON bool
|
var statusJSON bool
|
||||||
|
var statusFast bool
|
||||||
|
|
||||||
var statusCmd = &cobra.Command{
|
var statusCmd = &cobra.Command{
|
||||||
Use: "status",
|
Use: "status",
|
||||||
@@ -29,12 +31,15 @@ var statusCmd = &cobra.Command{
|
|||||||
Short: "Show overall town status",
|
Short: "Show overall town status",
|
||||||
Long: `Display the current status of the Gas Town workspace.
|
Long: `Display the current status of the Gas Town workspace.
|
||||||
|
|
||||||
Shows town name, registered rigs, active polecats, and witness status.`,
|
Shows town name, registered rigs, active polecats, and witness status.
|
||||||
|
|
||||||
|
Use --fast to skip mail lookups for faster execution.`,
|
||||||
RunE: runStatus,
|
RunE: runStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
statusCmd.Flags().BoolVar(&statusJSON, "json", false, "Output as JSON")
|
statusCmd.Flags().BoolVar(&statusJSON, "json", false, "Output as JSON")
|
||||||
|
statusCmd.Flags().BoolVar(&statusFast, "fast", false, "Skip mail lookups for faster execution")
|
||||||
rootCmd.AddCommand(statusCmd)
|
rootCmd.AddCommand(statusCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +149,14 @@ func runStatus(cmd *cobra.Command, args []string) error {
|
|||||||
// Create tmux instance for runtime checks
|
// Create tmux instance for runtime checks
|
||||||
t := tmux.NewTmux()
|
t := tmux.NewTmux()
|
||||||
|
|
||||||
|
// Pre-fetch all tmux sessions for O(1) lookup
|
||||||
|
allSessions := make(map[string]bool)
|
||||||
|
if sessions, err := t.ListSessions(); err == nil {
|
||||||
|
for _, s := range sessions {
|
||||||
|
allSessions[s] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Discover rigs
|
// Discover rigs
|
||||||
rigs, err := mgr.DiscoverRigs()
|
rigs, err := mgr.DiscoverRigs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -154,6 +167,25 @@ func runStatus(cmd *cobra.Command, args []string) error {
|
|||||||
gastownBeadsPath := filepath.Join(townRoot, "gastown", "mayor", "rig")
|
gastownBeadsPath := filepath.Join(townRoot, "gastown", "mayor", "rig")
|
||||||
agentBeads := beads.New(gastownBeadsPath)
|
agentBeads := beads.New(gastownBeadsPath)
|
||||||
|
|
||||||
|
// Pre-fetch all agent beads in a single query for performance
|
||||||
|
allAgentBeads, _ := agentBeads.ListAgentBeads()
|
||||||
|
if allAgentBeads == nil {
|
||||||
|
allAgentBeads = make(map[string]*beads.Issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-fetch all hook beads (referenced in agent beads) in a single query
|
||||||
|
var allHookIDs []string
|
||||||
|
for _, issue := range allAgentBeads {
|
||||||
|
fields := beads.ParseAgentFields(issue.Description)
|
||||||
|
if fields != nil && fields.HookBead != "" {
|
||||||
|
allHookIDs = append(allHookIDs, fields.HookBead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allHookBeads, _ := agentBeads.ShowMultiple(allHookIDs)
|
||||||
|
if allHookBeads == nil {
|
||||||
|
allHookBeads = make(map[string]*beads.Issue)
|
||||||
|
}
|
||||||
|
|
||||||
// Create mail router for inbox lookups
|
// Create mail router for inbox lookups
|
||||||
mailRouter := mail.NewRouter(townRoot)
|
mailRouter := mail.NewRouter(townRoot)
|
||||||
|
|
||||||
@@ -173,57 +205,79 @@ func runStatus(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build status
|
// Build status - parallel fetch global agents and rigs
|
||||||
status := TownStatus{
|
status := TownStatus{
|
||||||
Name: townConfig.Name,
|
Name: townConfig.Name,
|
||||||
Location: townRoot,
|
Location: townRoot,
|
||||||
Overseer: overseerInfo,
|
Overseer: overseerInfo,
|
||||||
Agents: discoverGlobalAgents(t, agentBeads, mailRouter),
|
Rigs: make([]RigStatus, len(rigs)),
|
||||||
Rigs: make([]RigStatus, 0, len(rigs)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range rigs {
|
var wg sync.WaitGroup
|
||||||
rs := RigStatus{
|
|
||||||
Name: r.Name,
|
|
||||||
Polecats: r.Polecats,
|
|
||||||
PolecatCount: len(r.Polecats),
|
|
||||||
HasWitness: r.HasWitness,
|
|
||||||
HasRefinery: r.HasRefinery,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count crew workers
|
// Fetch global agents in parallel with rig discovery
|
||||||
crewGit := git.NewGit(r.Path)
|
wg.Add(1)
|
||||||
crewMgr := crew.NewManager(r, crewGit)
|
go func() {
|
||||||
if workers, err := crewMgr.List(); err == nil {
|
defer wg.Done()
|
||||||
for _, w := range workers {
|
status.Agents = discoverGlobalAgents(allSessions, allAgentBeads, allHookBeads, mailRouter, statusFast)
|
||||||
rs.Crews = append(rs.Crews, w.Name)
|
}()
|
||||||
|
|
||||||
|
// Process all rigs in parallel
|
||||||
|
rigActiveHooks := make([]int, len(rigs)) // Track hooks per rig for thread safety
|
||||||
|
for i, r := range rigs {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(idx int, r *rig.Rig) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
rs := RigStatus{
|
||||||
|
Name: r.Name,
|
||||||
|
Polecats: r.Polecats,
|
||||||
|
PolecatCount: len(r.Polecats),
|
||||||
|
HasWitness: r.HasWitness,
|
||||||
|
HasRefinery: r.HasRefinery,
|
||||||
}
|
}
|
||||||
rs.CrewCount = len(workers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discover hooks for all agents in this rig
|
// Count crew workers
|
||||||
rs.Hooks = discoverRigHooks(r, rs.Crews)
|
crewGit := git.NewGit(r.Path)
|
||||||
for _, hook := range rs.Hooks {
|
crewMgr := crew.NewManager(r, crewGit)
|
||||||
if hook.HasWork {
|
if workers, err := crewMgr.List(); err == nil {
|
||||||
status.Summary.ActiveHooks++
|
for _, w := range workers {
|
||||||
|
rs.Crews = append(rs.Crews, w.Name)
|
||||||
|
}
|
||||||
|
rs.CrewCount = len(workers)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Discover runtime state for all agents in this rig
|
// Discover hooks for all agents in this rig
|
||||||
rs.Agents = discoverRigAgents(t, r, rs.Crews, agentBeads, mailRouter)
|
rs.Hooks = discoverRigHooks(r, rs.Crews)
|
||||||
|
activeHooks := 0
|
||||||
|
for _, hook := range rs.Hooks {
|
||||||
|
if hook.HasWork {
|
||||||
|
activeHooks++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rigActiveHooks[idx] = activeHooks
|
||||||
|
|
||||||
// Get MQ summary if rig has a refinery
|
// Discover runtime state for all agents in this rig
|
||||||
rs.MQ = getMQSummary(r)
|
rs.Agents = discoverRigAgents(allSessions, r, rs.Crews, allAgentBeads, allHookBeads, mailRouter, statusFast)
|
||||||
|
|
||||||
status.Rigs = append(status.Rigs, rs)
|
// Get MQ summary if rig has a refinery
|
||||||
|
rs.MQ = getMQSummary(r)
|
||||||
|
|
||||||
// Update summary
|
status.Rigs[idx] = rs
|
||||||
status.Summary.PolecatCount += len(r.Polecats)
|
}(i, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Aggregate summary (after parallel work completes)
|
||||||
|
for i, rs := range status.Rigs {
|
||||||
|
status.Summary.PolecatCount += rs.PolecatCount
|
||||||
status.Summary.CrewCount += rs.CrewCount
|
status.Summary.CrewCount += rs.CrewCount
|
||||||
if r.HasWitness {
|
status.Summary.ActiveHooks += rigActiveHooks[i]
|
||||||
|
if rs.HasWitness {
|
||||||
status.Summary.WitnessCount++
|
status.Summary.WitnessCount++
|
||||||
}
|
}
|
||||||
if r.HasRefinery {
|
if rs.HasRefinery {
|
||||||
status.Summary.RefineryCount++
|
status.Summary.RefineryCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -529,58 +583,73 @@ func discoverRigHooks(r *rig.Rig, crews []string) []AgentHookInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// discoverGlobalAgents checks runtime state for town-level agents (Mayor, Deacon).
|
// discoverGlobalAgents checks runtime state for town-level agents (Mayor, Deacon).
|
||||||
func discoverGlobalAgents(t *tmux.Tmux, agentBeads *beads.Beads, mailRouter *mail.Router) []AgentRuntime {
|
// Uses parallel fetching for performance. If skipMail is true, mail lookups are skipped.
|
||||||
var agents []AgentRuntime
|
// allSessions is a preloaded map of tmux sessions for O(1) lookup.
|
||||||
|
// allAgentBeads is a preloaded map of agent beads for O(1) lookup.
|
||||||
// Check Mayor
|
// allHookBeads is a preloaded map of hook beads for O(1) lookup.
|
||||||
mayorRunning, _ := t.HasSession(MayorSessionName)
|
func discoverGlobalAgents(allSessions map[string]bool, allAgentBeads map[string]*beads.Issue, allHookBeads map[string]*beads.Issue, mailRouter *mail.Router, skipMail bool) []AgentRuntime {
|
||||||
mayor := AgentRuntime{
|
// Define agents to discover
|
||||||
Name: "mayor",
|
agentDefs := []struct {
|
||||||
Address: "mayor/",
|
name string
|
||||||
Session: MayorSessionName,
|
address string
|
||||||
Role: "coordinator",
|
session string
|
||||||
Running: mayorRunning,
|
role string
|
||||||
|
beadID string
|
||||||
|
}{
|
||||||
|
{"mayor", "mayor/", MayorSessionName, "coordinator", "gt-mayor"},
|
||||||
|
{"deacon", "deacon/", DeaconSessionName, "health-check", "gt-deacon"},
|
||||||
}
|
}
|
||||||
// Look up agent bead for hook/state
|
|
||||||
if issue, fields, err := agentBeads.GetAgentBead("gt-mayor"); err == nil && issue != nil {
|
agents := make([]AgentRuntime, len(agentDefs))
|
||||||
mayor.HookBead = fields.HookBead
|
var wg sync.WaitGroup
|
||||||
mayor.State = fields.AgentState
|
|
||||||
if fields.HookBead != "" {
|
for i, def := range agentDefs {
|
||||||
mayor.HasWork = true
|
wg.Add(1)
|
||||||
// Try to get the title of the pinned bead
|
go func(idx int, d struct {
|
||||||
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
|
name string
|
||||||
mayor.WorkTitle = pinnedIssue.Title
|
address string
|
||||||
|
session string
|
||||||
|
role string
|
||||||
|
beadID string
|
||||||
|
}) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
agent := AgentRuntime{
|
||||||
|
Name: d.name,
|
||||||
|
Address: d.address,
|
||||||
|
Session: d.session,
|
||||||
|
Role: d.role,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get mail info
|
|
||||||
populateMailInfo(&mayor, mailRouter)
|
|
||||||
agents = append(agents, mayor)
|
|
||||||
|
|
||||||
// Check Deacon
|
// Check tmux session from preloaded map (O(1))
|
||||||
deaconRunning, _ := t.HasSession(DeaconSessionName)
|
agent.Running = allSessions[d.session]
|
||||||
deacon := AgentRuntime{
|
|
||||||
Name: "deacon",
|
// Look up agent bead from preloaded map (O(1))
|
||||||
Address: "deacon/",
|
if issue, ok := allAgentBeads[d.beadID]; ok {
|
||||||
Session: DeaconSessionName,
|
fields := beads.ParseAgentFields(issue.Description)
|
||||||
Role: "health-check",
|
if fields != nil {
|
||||||
Running: deaconRunning,
|
agent.HookBead = fields.HookBead
|
||||||
}
|
agent.State = fields.AgentState
|
||||||
// Look up agent bead for hook/state
|
if fields.HookBead != "" {
|
||||||
if issue, fields, err := agentBeads.GetAgentBead("gt-deacon"); err == nil && issue != nil {
|
agent.HasWork = true
|
||||||
deacon.HookBead = fields.HookBead
|
// Get hook title from preloaded map
|
||||||
deacon.State = fields.AgentState
|
if pinnedIssue, ok := allHookBeads[fields.HookBead]; ok {
|
||||||
if fields.HookBead != "" {
|
agent.WorkTitle = pinnedIssue.Title
|
||||||
deacon.HasWork = true
|
}
|
||||||
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
|
}
|
||||||
deacon.WorkTitle = pinnedIssue.Title
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get mail info
|
|
||||||
populateMailInfo(&deacon, mailRouter)
|
|
||||||
agents = append(agents, deacon)
|
|
||||||
|
|
||||||
|
// Get mail info (skip if --fast)
|
||||||
|
if !skipMail {
|
||||||
|
populateMailInfo(&agent, mailRouter)
|
||||||
|
}
|
||||||
|
|
||||||
|
agents[idx] = agent
|
||||||
|
}(i, def)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
return agents
|
return agents
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,118 +671,117 @@ func populateMailInfo(agent *AgentRuntime, router *mail.Router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// agentDef defines an agent to discover
|
||||||
|
type agentDef struct {
|
||||||
|
name string
|
||||||
|
address string
|
||||||
|
session string
|
||||||
|
role string
|
||||||
|
beadID string
|
||||||
|
}
|
||||||
|
|
||||||
// discoverRigAgents checks runtime state for all agents in a rig.
|
// discoverRigAgents checks runtime state for all agents in a rig.
|
||||||
func discoverRigAgents(t *tmux.Tmux, r *rig.Rig, crews []string, agentBeads *beads.Beads, mailRouter *mail.Router) []AgentRuntime {
|
// Uses parallel fetching for performance. If skipMail is true, mail lookups are skipped.
|
||||||
var agents []AgentRuntime
|
// allSessions is a preloaded map of tmux sessions for O(1) lookup.
|
||||||
|
// allAgentBeads is a preloaded map of agent beads for O(1) lookup.
|
||||||
|
// allHookBeads is a preloaded map of hook beads for O(1) lookup.
|
||||||
|
func discoverRigAgents(allSessions map[string]bool, r *rig.Rig, crews []string, allAgentBeads map[string]*beads.Issue, allHookBeads map[string]*beads.Issue, mailRouter *mail.Router, skipMail bool) []AgentRuntime {
|
||||||
|
// Build list of all agents to discover
|
||||||
|
var defs []agentDef
|
||||||
|
|
||||||
// Check Witness
|
// Witness
|
||||||
if r.HasWitness {
|
if r.HasWitness {
|
||||||
sessionName := witnessSessionName(r.Name)
|
defs = append(defs, agentDef{
|
||||||
running, _ := t.HasSession(sessionName)
|
name: "witness",
|
||||||
witness := AgentRuntime{
|
address: r.Name + "/witness",
|
||||||
Name: "witness",
|
session: witnessSessionName(r.Name),
|
||||||
Address: r.Name + "/witness",
|
role: "witness",
|
||||||
Session: sessionName,
|
beadID: beads.WitnessBeadID(r.Name),
|
||||||
Role: "witness",
|
})
|
||||||
Running: running,
|
|
||||||
}
|
|
||||||
// Look up agent bead
|
|
||||||
agentID := beads.WitnessBeadID(r.Name)
|
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
|
||||||
witness.HookBead = fields.HookBead
|
|
||||||
witness.State = fields.AgentState
|
|
||||||
if fields.HookBead != "" {
|
|
||||||
witness.HasWork = true
|
|
||||||
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
|
|
||||||
witness.WorkTitle = pinnedIssue.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
populateMailInfo(&witness, mailRouter)
|
|
||||||
agents = append(agents, witness)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Refinery
|
// Refinery
|
||||||
if r.HasRefinery {
|
if r.HasRefinery {
|
||||||
sessionName := fmt.Sprintf("gt-%s-refinery", r.Name)
|
defs = append(defs, agentDef{
|
||||||
running, _ := t.HasSession(sessionName)
|
name: "refinery",
|
||||||
refinery := AgentRuntime{
|
address: r.Name + "/refinery",
|
||||||
Name: "refinery",
|
session: fmt.Sprintf("gt-%s-refinery", r.Name),
|
||||||
Address: r.Name + "/refinery",
|
role: "refinery",
|
||||||
Session: sessionName,
|
beadID: beads.RefineryBeadID(r.Name),
|
||||||
Role: "refinery",
|
})
|
||||||
Running: running,
|
|
||||||
}
|
|
||||||
// Look up agent bead
|
|
||||||
agentID := beads.RefineryBeadID(r.Name)
|
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
|
||||||
refinery.HookBead = fields.HookBead
|
|
||||||
refinery.State = fields.AgentState
|
|
||||||
if fields.HookBead != "" {
|
|
||||||
refinery.HasWork = true
|
|
||||||
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
|
|
||||||
refinery.WorkTitle = pinnedIssue.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
populateMailInfo(&refinery, mailRouter)
|
|
||||||
agents = append(agents, refinery)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Polecats
|
// Polecats
|
||||||
for _, name := range r.Polecats {
|
for _, name := range r.Polecats {
|
||||||
sessionName := fmt.Sprintf("gt-%s-%s", r.Name, name)
|
defs = append(defs, agentDef{
|
||||||
running, _ := t.HasSession(sessionName)
|
name: name,
|
||||||
polecat := AgentRuntime{
|
address: r.Name + "/" + name,
|
||||||
Name: name,
|
session: fmt.Sprintf("gt-%s-%s", r.Name, name),
|
||||||
Address: r.Name + "/" + name,
|
role: "polecat",
|
||||||
Session: sessionName,
|
beadID: beads.PolecatBeadID(r.Name, name),
|
||||||
Role: "polecat",
|
})
|
||||||
Running: running,
|
|
||||||
}
|
|
||||||
// Look up agent bead
|
|
||||||
agentID := beads.PolecatBeadID(r.Name, name)
|
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
|
||||||
polecat.HookBead = fields.HookBead
|
|
||||||
polecat.State = fields.AgentState
|
|
||||||
if fields.HookBead != "" {
|
|
||||||
polecat.HasWork = true
|
|
||||||
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
|
|
||||||
polecat.WorkTitle = pinnedIssue.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
populateMailInfo(&polecat, mailRouter)
|
|
||||||
agents = append(agents, polecat)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Crew
|
// Crew
|
||||||
for _, name := range crews {
|
for _, name := range crews {
|
||||||
sessionName := crewSessionName(r.Name, name)
|
defs = append(defs, agentDef{
|
||||||
running, _ := t.HasSession(sessionName)
|
name: name,
|
||||||
crewAgent := AgentRuntime{
|
address: r.Name + "/crew/" + name,
|
||||||
Name: name,
|
session: crewSessionName(r.Name, name),
|
||||||
Address: r.Name + "/crew/" + name,
|
role: "crew",
|
||||||
Session: sessionName,
|
beadID: beads.CrewBeadID(r.Name, name),
|
||||||
Role: "crew",
|
})
|
||||||
Running: running,
|
|
||||||
}
|
|
||||||
// Look up agent bead
|
|
||||||
agentID := beads.CrewBeadID(r.Name, name)
|
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
|
||||||
crewAgent.HookBead = fields.HookBead
|
|
||||||
crewAgent.State = fields.AgentState
|
|
||||||
if fields.HookBead != "" {
|
|
||||||
crewAgent.HasWork = true
|
|
||||||
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
|
|
||||||
crewAgent.WorkTitle = pinnedIssue.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
populateMailInfo(&crewAgent, mailRouter)
|
|
||||||
agents = append(agents, crewAgent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(defs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all agents in parallel
|
||||||
|
agents := make([]AgentRuntime, len(defs))
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for i, def := range defs {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(idx int, d agentDef) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
agent := AgentRuntime{
|
||||||
|
Name: d.name,
|
||||||
|
Address: d.address,
|
||||||
|
Session: d.session,
|
||||||
|
Role: d.role,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tmux session from preloaded map (O(1))
|
||||||
|
agent.Running = allSessions[d.session]
|
||||||
|
|
||||||
|
// Look up agent bead from preloaded map (O(1))
|
||||||
|
if issue, ok := allAgentBeads[d.beadID]; ok {
|
||||||
|
fields := beads.ParseAgentFields(issue.Description)
|
||||||
|
if fields != nil {
|
||||||
|
agent.HookBead = fields.HookBead
|
||||||
|
agent.State = fields.AgentState
|
||||||
|
if fields.HookBead != "" {
|
||||||
|
agent.HasWork = true
|
||||||
|
// Get hook title from preloaded map
|
||||||
|
if pinnedIssue, ok := allHookBeads[fields.HookBead]; ok {
|
||||||
|
agent.WorkTitle = pinnedIssue.Title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mail info (skip if --fast)
|
||||||
|
if !skipMail {
|
||||||
|
populateMailInfo(&agent, mailRouter)
|
||||||
|
}
|
||||||
|
|
||||||
|
agents[idx] = agent
|
||||||
|
}(i, def)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
return agents
|
return agents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user