feat: Create crew agent beads in doctor --fix and crew add
- doctor/agent_beads_check.go: Check and create agent beads for all crew workers - New listCrewWorkers() helper finds crew directories in each rig - Run() checks for missing crew agent beads - Fix() creates missing crew agent beads with proper fields - cmd/crew_add.go: Create agent bead when adding a crew worker - Creates gt-crew-<rig>-<name> agent bead after workspace creation - Non-fatal if bead creation fails (warns but continues) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,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/crew"
|
||||
"github.com/steveyegge/gastown/internal/git"
|
||||
@@ -77,6 +78,27 @@ func runCrewAdd(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf(" Branch: %s\n", worker.Branch)
|
||||
fmt.Printf(" Mail: %s/mail/\n", worker.ClonePath)
|
||||
|
||||
// Create agent bead for the crew worker
|
||||
rigBeadsPath := filepath.Join(r.Path, "mayor", "rig")
|
||||
bd := beads.New(rigBeadsPath)
|
||||
crewID := fmt.Sprintf("gt-crew-%s-%s", rigName, name)
|
||||
if _, err := bd.Show(crewID); err != nil {
|
||||
// Agent bead doesn't exist, create it
|
||||
fields := &beads.AgentFields{
|
||||
RoleType: "crew",
|
||||
Rig: rigName,
|
||||
AgentState: "idle",
|
||||
RoleBead: crewID + "-role",
|
||||
}
|
||||
desc := fmt.Sprintf("Crew worker %s in %s - human-managed persistent workspace.", name, rigName)
|
||||
if _, err := bd.CreateAgentBead(crewID, desc, fields); err != nil {
|
||||
// Non-fatal: warn but don't fail the add
|
||||
fmt.Printf(" %s\n", style.Dim.Render(fmt.Sprintf("Warning: could not create agent bead: %v", err)))
|
||||
} else {
|
||||
fmt.Printf(" Agent bead: %s\n", crewID)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n", style.Dim.Render("Start working with: cd "+worker.ClonePath))
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package doctor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -12,8 +13,9 @@ import (
|
||||
// This includes:
|
||||
// - Global agents (deacon, mayor) - stored in first rig's beads
|
||||
// - Per-rig agents (witness, refinery) - stored in each rig's beads
|
||||
// - Crew workers - stored in each rig's beads
|
||||
//
|
||||
// Agent beads are created by gt rig add (see gt-h3hak, gt-pinkq).
|
||||
// Agent beads are created by gt rig add (see gt-h3hak, gt-pinkq) and gt crew add.
|
||||
//
|
||||
// NOTE: Currently, the beads library validates that agent IDs must start
|
||||
// with 'gt-'. Rigs with different prefixes (like 'bd-') cannot have agent
|
||||
@@ -113,6 +115,16 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
|
||||
}
|
||||
checked++
|
||||
|
||||
// Check crew worker agents
|
||||
crewWorkers := listCrewWorkers(ctx.TownRoot, rigName)
|
||||
for _, workerName := range crewWorkers {
|
||||
crewID := fmt.Sprintf("%s-crew-%s-%s", prefix, rigName, workerName)
|
||||
if _, err := bd.Show(crewID); err != nil {
|
||||
missing = append(missing, crewID)
|
||||
}
|
||||
checked++
|
||||
}
|
||||
|
||||
// Check global agents in first rig
|
||||
if rigName == firstRigName {
|
||||
deaconID := firstPrefix + "-deacon"
|
||||
@@ -236,6 +248,24 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Create crew worker agents if missing
|
||||
crewWorkers := listCrewWorkers(ctx.TownRoot, rigName)
|
||||
for _, workerName := range crewWorkers {
|
||||
crewID := fmt.Sprintf("%s-crew-%s-%s", prefix, rigName, workerName)
|
||||
if _, err := bd.Show(crewID); err != nil {
|
||||
fields := &beads.AgentFields{
|
||||
RoleType: "crew",
|
||||
Rig: rigName,
|
||||
AgentState: "idle",
|
||||
RoleBead: crewID + "-role",
|
||||
}
|
||||
desc := fmt.Sprintf("Crew worker %s in %s - human-managed persistent workspace.", workerName, rigName)
|
||||
if _, err := bd.CreateAgentBead(crewID, desc, fields); err != nil {
|
||||
return fmt.Errorf("creating %s: %w", crewID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create global agents in first rig if missing
|
||||
if rigName == firstRigName {
|
||||
deaconID := firstPrefix + "-deacon"
|
||||
@@ -270,3 +300,20 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listCrewWorkers returns the names of all crew workers in a rig.
|
||||
func listCrewWorkers(townRoot, rigName string) []string {
|
||||
crewDir := filepath.Join(townRoot, rigName, "crew")
|
||||
entries, err := os.ReadDir(crewDir)
|
||||
if err != nil {
|
||||
return nil // No crew directory or can't read it
|
||||
}
|
||||
|
||||
var workers []string
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && !strings.HasPrefix(entry.Name(), ".") {
|
||||
workers = append(workers, entry.Name())
|
||||
}
|
||||
}
|
||||
return workers
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user