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"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/config"
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
"github.com/steveyegge/gastown/internal/crew"
|
"github.com/steveyegge/gastown/internal/crew"
|
||||||
"github.com/steveyegge/gastown/internal/git"
|
"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(" Branch: %s\n", worker.Branch)
|
||||||
fmt.Printf(" Mail: %s/mail/\n", worker.ClonePath)
|
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))
|
fmt.Printf("\n%s\n", style.Dim.Render("Start working with: cd "+worker.ClonePath))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package doctor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -12,8 +13,9 @@ import (
|
|||||||
// This includes:
|
// This includes:
|
||||||
// - Global agents (deacon, mayor) - stored in first rig's beads
|
// - Global agents (deacon, mayor) - stored in first rig's beads
|
||||||
// - Per-rig agents (witness, refinery) - stored in each 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
|
// NOTE: Currently, the beads library validates that agent IDs must start
|
||||||
// with 'gt-'. Rigs with different prefixes (like 'bd-') cannot have agent
|
// with 'gt-'. Rigs with different prefixes (like 'bd-') cannot have agent
|
||||||
@@ -113,6 +115,16 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
|
|||||||
}
|
}
|
||||||
checked++
|
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
|
// Check global agents in first rig
|
||||||
if rigName == firstRigName {
|
if rigName == firstRigName {
|
||||||
deaconID := firstPrefix + "-deacon"
|
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
|
// Create global agents in first rig if missing
|
||||||
if rigName == firstRigName {
|
if rigName == firstRigName {
|
||||||
deaconID := firstPrefix + "-deacon"
|
deaconID := firstPrefix + "-deacon"
|
||||||
@@ -270,3 +300,20 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
|||||||
|
|
||||||
return nil
|
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