feat(rig): add park/unpark commands for Level 1 rig control

Implement gt rig park <rig> and gt rig unpark <rig> commands:
- park: stops witness/refinery, sets status=parked in wisp layer
- unpark: clears parked status, allows daemon to restart agents

This is Level 1 (local/ephemeral) control - affects only this town
and disappears on wisp cleanup. Exports IsRigParked() for daemon use.

(gt-vxv0u)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
furiosa
2026-01-06 19:27:27 -08:00
committed by Steve Yegge
parent 29aed4b42f
commit 6e84489ca3

149
internal/cmd/rig_park.go Normal file
View File

@@ -0,0 +1,149 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/refinery"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/tmux"
"github.com/steveyegge/gastown/internal/wisp"
"github.com/steveyegge/gastown/internal/witness"
)
// RigStatusKey is the wisp config key for rig operational status.
const RigStatusKey = "status"
// RigStatusParked is the value indicating a rig is parked.
const RigStatusParked = "parked"
var rigParkCmd = &cobra.Command{
Use: "park <rig>",
Short: "Park a rig (stops agents, daemon won't auto-restart)",
Long: `Park a rig to temporarily disable it.
Parking a rig:
- Stops the witness if running
- Stops the refinery if running
- Sets status=parked in the wisp layer (local/ephemeral)
- The daemon respects this status and won't auto-restart agents
This is a Level 1 (local/ephemeral) operation:
- Only affects this town
- Disappears on wisp cleanup
- Use 'gt rig unpark' to resume normal operation
Examples:
gt rig park gastown
gt rig park beads`,
Args: cobra.ExactArgs(1),
RunE: runRigPark,
}
var rigUnparkCmd = &cobra.Command{
Use: "unpark <rig>",
Short: "Unpark a rig (allow daemon to auto-restart agents)",
Long: `Unpark a rig to resume normal operation.
Unparking a rig:
- Removes the parked status from the wisp layer
- Allows the daemon to auto-restart agents
- Does NOT automatically start agents (use 'gt rig start' for that)
Examples:
gt rig unpark gastown
gt rig unpark beads`,
Args: cobra.ExactArgs(1),
RunE: runRigUnpark,
}
func init() {
rigCmd.AddCommand(rigParkCmd)
rigCmd.AddCommand(rigUnparkCmd)
}
func runRigPark(cmd *cobra.Command, args []string) error {
rigName := args[0]
// Get rig and town root
townRoot, r, err := getRig(rigName)
if err != nil {
return err
}
fmt.Printf("Parking rig %s...\n", style.Bold.Render(rigName))
var stoppedAgents []string
t := tmux.NewTmux()
// Stop witness if running
witnessSession := fmt.Sprintf("gt-%s-witness", rigName)
witnessRunning, _ := t.HasSession(witnessSession)
if witnessRunning {
fmt.Printf(" Stopping witness...\n")
witMgr := witness.NewManager(r)
if err := witMgr.Stop(); err != nil {
fmt.Printf(" %s Failed to stop witness: %v\n", style.Warning.Render("!"), err)
} else {
stoppedAgents = append(stoppedAgents, "Witness stopped")
}
}
// Stop refinery if running
refinerySession := fmt.Sprintf("gt-%s-refinery", rigName)
refineryRunning, _ := t.HasSession(refinerySession)
if refineryRunning {
fmt.Printf(" Stopping refinery...\n")
refMgr := refinery.NewManager(r)
if err := refMgr.Stop(); err != nil {
fmt.Printf(" %s Failed to stop refinery: %v\n", style.Warning.Render("!"), err)
} else {
stoppedAgents = append(stoppedAgents, "Refinery stopped")
}
}
// Set parked status in wisp layer
wispCfg := wisp.NewConfig(townRoot, rigName)
if err := wispCfg.Set(RigStatusKey, RigStatusParked); err != nil {
return fmt.Errorf("setting parked status: %w", err)
}
// Output
fmt.Printf("%s Rig %s parked (local only)\n", style.Success.Render("✓"), rigName)
for _, msg := range stoppedAgents {
fmt.Printf(" %s\n", msg)
}
fmt.Printf(" Daemon will not auto-restart\n")
return nil
}
func runRigUnpark(cmd *cobra.Command, args []string) error {
rigName := args[0]
// Get rig and town root
townRoot, _, err := getRig(rigName)
if err != nil {
return err
}
// Remove parked status from wisp layer
wispCfg := wisp.NewConfig(townRoot, rigName)
if err := wispCfg.Unset(RigStatusKey); err != nil {
return fmt.Errorf("clearing parked status: %w", err)
}
fmt.Printf("%s Rig %s unparked\n", style.Success.Render("✓"), rigName)
fmt.Printf(" Daemon can now auto-restart agents\n")
fmt.Printf(" Use '%s' to start agents immediately\n", style.Dim.Render("gt rig start "+rigName))
return nil
}
// IsRigParked checks if a rig is parked in the wisp layer.
// This function is exported for use by the daemon.
func IsRigParked(townRoot, rigName string) bool {
wispCfg := wisp.NewConfig(townRoot, rigName)
return wispCfg.GetString(RigStatusKey) == RigStatusParked
}