feat: add gt rig add command with container-based structure
- Implement AddRig with container-based approach (rig root is NOT a clone) - Create internal/cmd/rig.go with add/list/remove subcommands - Clone repo into refinery/rig/, mayor/rig/, crew/main/ - Initialize rig-level .beads/ with derived prefix - Update docs/architecture.md to match implementation - File gt-jpt epic for town-level beads redesign 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
245
internal/cmd/rig.go
Normal file
245
internal/cmd/rig.go
Normal file
@@ -0,0 +1,245 @@
|
||||
// Package cmd provides CLI commands for the gt tool.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/git"
|
||||
"github.com/steveyegge/gastown/internal/rig"
|
||||
"github.com/steveyegge/gastown/internal/style"
|
||||
"github.com/steveyegge/gastown/internal/workspace"
|
||||
)
|
||||
|
||||
var rigCmd = &cobra.Command{
|
||||
Use: "rig",
|
||||
Short: "Manage rigs in the workspace",
|
||||
Long: `Manage rigs (project containers) in the Gas Town workspace.
|
||||
|
||||
A rig is a container for managing a project and its agents:
|
||||
- refinery/rig/ Canonical main clone (Refinery's working copy)
|
||||
- mayor/rig/ Mayor's working clone for this rig
|
||||
- crew/<name>/ Human workspace(s)
|
||||
- witness/ Witness agent (no clone)
|
||||
- polecats/ Worker directories
|
||||
- .beads/ Rig-level issue tracking`,
|
||||
}
|
||||
|
||||
var rigAddCmd = &cobra.Command{
|
||||
Use: "add <name> <git-url>",
|
||||
Short: "Add a new rig to the workspace",
|
||||
Long: `Add a new rig by cloning a repository.
|
||||
|
||||
This creates a rig container with:
|
||||
- config.json Rig configuration
|
||||
- .beads/ Rig-level issue tracking (initialized)
|
||||
- refinery/rig/ Canonical main clone
|
||||
- mayor/rig/ Mayor's working clone
|
||||
- crew/main/ Default human workspace
|
||||
- witness/ Witness agent directory
|
||||
- polecats/ Worker directory (empty)
|
||||
|
||||
Example:
|
||||
gt rig add gastown https://github.com/steveyegge/gastown
|
||||
gt rig add my-project git@github.com:user/repo.git --prefix mp`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runRigAdd,
|
||||
}
|
||||
|
||||
var rigListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all rigs in the workspace",
|
||||
RunE: runRigList,
|
||||
}
|
||||
|
||||
var rigRemoveCmd = &cobra.Command{
|
||||
Use: "remove <name>",
|
||||
Short: "Remove a rig from the registry (does not delete files)",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRigRemove,
|
||||
}
|
||||
|
||||
// Flags
|
||||
var (
|
||||
rigAddPrefix string
|
||||
rigAddCrew string
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(rigCmd)
|
||||
rigCmd.AddCommand(rigAddCmd)
|
||||
rigCmd.AddCommand(rigListCmd)
|
||||
rigCmd.AddCommand(rigRemoveCmd)
|
||||
|
||||
rigAddCmd.Flags().StringVar(&rigAddPrefix, "prefix", "", "Beads issue prefix (default: derived from name)")
|
||||
rigAddCmd.Flags().StringVar(&rigAddCrew, "crew", "main", "Default crew workspace name")
|
||||
}
|
||||
|
||||
func runRigAdd(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
gitURL := args[1]
|
||||
|
||||
// Find workspace
|
||||
townRoot, err := workspace.FindFromCwdOrError()
|
||||
if err != nil {
|
||||
return fmt.Errorf("not in a Gas Town workspace: %w", err)
|
||||
}
|
||||
|
||||
// Load rigs config
|
||||
rigsPath := filepath.Join(townRoot, "mayor", "rigs.json")
|
||||
rigsConfig, err := config.LoadRigsConfig(rigsPath)
|
||||
if err != nil {
|
||||
// Create new if doesn't exist
|
||||
rigsConfig = &config.RigsConfig{
|
||||
Version: 1,
|
||||
Rigs: make(map[string]config.RigEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// Create rig manager
|
||||
g := git.NewGit(townRoot)
|
||||
mgr := rig.NewManager(townRoot, rigsConfig, g)
|
||||
|
||||
fmt.Printf("Creating rig %s...\n", style.Bold.Render(name))
|
||||
fmt.Printf(" Repository: %s\n", gitURL)
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
// Add the rig
|
||||
newRig, err := mgr.AddRig(rig.AddRigOptions{
|
||||
Name: name,
|
||||
GitURL: gitURL,
|
||||
BeadsPrefix: rigAddPrefix,
|
||||
CrewName: rigAddCrew,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("adding rig: %w", err)
|
||||
}
|
||||
|
||||
// Save updated rigs config
|
||||
if err := config.SaveRigsConfig(rigsPath, rigsConfig); err != nil {
|
||||
return fmt.Errorf("saving rigs config: %w", err)
|
||||
}
|
||||
|
||||
elapsed := time.Since(startTime)
|
||||
|
||||
fmt.Printf("\n%s Rig created in %.1fs\n", style.Success.Render("✓"), elapsed.Seconds())
|
||||
fmt.Printf("\nStructure:\n")
|
||||
fmt.Printf(" %s/\n", name)
|
||||
fmt.Printf(" ├── config.json\n")
|
||||
fmt.Printf(" ├── .beads/ (prefix: %s)\n", newRig.Config.Prefix)
|
||||
fmt.Printf(" ├── refinery/rig/ (canonical main)\n")
|
||||
fmt.Printf(" ├── mayor/rig/ (mayor's clone)\n")
|
||||
fmt.Printf(" ├── crew/%s/ (your workspace)\n", rigAddCrew)
|
||||
fmt.Printf(" ├── witness/\n")
|
||||
fmt.Printf(" └── polecats/\n")
|
||||
|
||||
fmt.Printf("\nNext steps:\n")
|
||||
fmt.Printf(" cd %s/crew/%s # Work in your clone\n", filepath.Join(townRoot, name), rigAddCrew)
|
||||
fmt.Printf(" bd ready # See available work\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runRigList(cmd *cobra.Command, args []string) error {
|
||||
// Find workspace
|
||||
townRoot, err := workspace.FindFromCwdOrError()
|
||||
if err != nil {
|
||||
return fmt.Errorf("not in a Gas Town workspace: %w", err)
|
||||
}
|
||||
|
||||
// Load rigs config
|
||||
rigsPath := filepath.Join(townRoot, "mayor", "rigs.json")
|
||||
rigsConfig, err := config.LoadRigsConfig(rigsPath)
|
||||
if err != nil {
|
||||
fmt.Println("No rigs configured.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(rigsConfig.Rigs) == 0 {
|
||||
fmt.Println("No rigs configured.")
|
||||
fmt.Printf("\nAdd one with: %s\n", style.Dim.Render("gt rig add <name> <git-url>"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create rig manager to get details
|
||||
g := git.NewGit(townRoot)
|
||||
mgr := rig.NewManager(townRoot, rigsConfig, g)
|
||||
|
||||
fmt.Printf("Rigs in %s:\n\n", townRoot)
|
||||
|
||||
for name := range rigsConfig.Rigs {
|
||||
r, err := mgr.GetRig(name)
|
||||
if err != nil {
|
||||
fmt.Printf(" %s %s\n", style.Warning.Render("!"), name)
|
||||
continue
|
||||
}
|
||||
|
||||
summary := r.Summary()
|
||||
fmt.Printf(" %s\n", style.Bold.Render(name))
|
||||
fmt.Printf(" Polecats: %d Crew: %d\n", summary.PolecatCount, summary.CrewCount)
|
||||
|
||||
agents := []string{}
|
||||
if summary.HasRefinery {
|
||||
agents = append(agents, "refinery")
|
||||
}
|
||||
if summary.HasWitness {
|
||||
agents = append(agents, "witness")
|
||||
}
|
||||
if r.HasMayor {
|
||||
agents = append(agents, "mayor")
|
||||
}
|
||||
if len(agents) > 0 {
|
||||
fmt.Printf(" Agents: %v\n", agents)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runRigRemove(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
// Find workspace
|
||||
townRoot, err := workspace.FindFromCwdOrError()
|
||||
if err != nil {
|
||||
return fmt.Errorf("not in a Gas Town workspace: %w", err)
|
||||
}
|
||||
|
||||
// Load rigs config
|
||||
rigsPath := filepath.Join(townRoot, "mayor", "rigs.json")
|
||||
rigsConfig, err := config.LoadRigsConfig(rigsPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading rigs config: %w", err)
|
||||
}
|
||||
|
||||
// Create rig manager
|
||||
g := git.NewGit(townRoot)
|
||||
mgr := rig.NewManager(townRoot, rigsConfig, g)
|
||||
|
||||
if err := mgr.RemoveRig(name); err != nil {
|
||||
return fmt.Errorf("removing rig: %w", err)
|
||||
}
|
||||
|
||||
// Save updated config
|
||||
if err := config.SaveRigsConfig(rigsPath, rigsConfig); err != nil {
|
||||
return fmt.Errorf("saving rigs config: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s Rig %s removed from registry\n", style.Success.Render("✓"), name)
|
||||
fmt.Printf("\nNote: Files at %s were NOT deleted.\n", filepath.Join(townRoot, name))
|
||||
fmt.Printf("To delete: %s\n", style.Dim.Render(fmt.Sprintf("rm -rf %s", filepath.Join(townRoot, name))))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper to check if path exists
|
||||
func pathExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package rig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/git"
|
||||
@@ -16,6 +20,25 @@ var (
|
||||
ErrRigExists = errors.New("rig already exists")
|
||||
)
|
||||
|
||||
// RigConfig represents the rig-level configuration (config.json at rig root).
|
||||
type RigConfig struct {
|
||||
Type string `json:"type"` // "rig"
|
||||
Version int `json:"version"` // schema version
|
||||
Name string `json:"name"` // rig name
|
||||
GitURL string `json:"git_url"` // repository URL
|
||||
CreatedAt time.Time `json:"created_at"` // when rig was created
|
||||
Beads *BeadsConfig `json:"beads,omitempty"`
|
||||
}
|
||||
|
||||
// BeadsConfig represents beads configuration for the rig.
|
||||
type BeadsConfig struct {
|
||||
Prefix string `json:"prefix"` // issue prefix (e.g., "gt")
|
||||
SyncRemote string `json:"sync_remote,omitempty"` // git remote for bd sync
|
||||
}
|
||||
|
||||
// CurrentRigConfigVersion is the current schema version.
|
||||
const CurrentRigConfigVersion = 1
|
||||
|
||||
// Manager handles rig discovery, loading, and creation.
|
||||
type Manager struct {
|
||||
townRoot string
|
||||
@@ -125,42 +148,222 @@ func (m *Manager) loadRig(name string, entry config.RigEntry) (*Rig, error) {
|
||||
return rig, nil
|
||||
}
|
||||
|
||||
// AddRig clones a repository and registers it as a rig.
|
||||
func (m *Manager) AddRig(name, gitURL string) (*Rig, error) {
|
||||
if m.RigExists(name) {
|
||||
// AddRigOptions configures rig creation.
|
||||
type AddRigOptions struct {
|
||||
Name string // Rig name (directory name)
|
||||
GitURL string // Repository URL
|
||||
BeadsPrefix string // Beads issue prefix (defaults to derived from name)
|
||||
CrewName string // Default crew workspace name (defaults to "main")
|
||||
}
|
||||
|
||||
// AddRig creates a new rig as a container with clones for each agent.
|
||||
// The rig structure is:
|
||||
//
|
||||
// <name>/ # Container (NOT a git clone)
|
||||
// ├── config.json # Rig configuration
|
||||
// ├── .beads/ # Rig-level issue tracking
|
||||
// ├── refinery/rig/ # Canonical main clone
|
||||
// ├── mayor/rig/ # Mayor's working clone
|
||||
// ├── witness/ # Witness agent (no clone)
|
||||
// ├── polecats/ # Worker directories (empty)
|
||||
// └── crew/<crew>/ # Default human workspace
|
||||
func (m *Manager) AddRig(opts AddRigOptions) (*Rig, error) {
|
||||
if m.RigExists(opts.Name) {
|
||||
return nil, ErrRigExists
|
||||
}
|
||||
|
||||
rigPath := filepath.Join(m.townRoot, name)
|
||||
rigPath := filepath.Join(m.townRoot, opts.Name)
|
||||
|
||||
// Check if directory already exists
|
||||
if _, err := os.Stat(rigPath); err == nil {
|
||||
return nil, fmt.Errorf("directory already exists: %s", rigPath)
|
||||
}
|
||||
|
||||
// Clone repository
|
||||
if err := m.git.Clone(gitURL, rigPath); err != nil {
|
||||
return nil, fmt.Errorf("cloning repository: %w", err)
|
||||
// Derive defaults
|
||||
if opts.BeadsPrefix == "" {
|
||||
opts.BeadsPrefix = deriveBeadsPrefix(opts.Name)
|
||||
}
|
||||
if opts.CrewName == "" {
|
||||
opts.CrewName = "main"
|
||||
}
|
||||
|
||||
// Create agent directories
|
||||
if err := m.createAgentDirs(rigPath); err != nil {
|
||||
// Cleanup on failure
|
||||
os.RemoveAll(rigPath)
|
||||
return nil, fmt.Errorf("creating agent directories: %w", err)
|
||||
// Create container directory
|
||||
if err := os.MkdirAll(rigPath, 0755); err != nil {
|
||||
return nil, fmt.Errorf("creating rig directory: %w", err)
|
||||
}
|
||||
|
||||
// Update git exclude
|
||||
if err := m.updateGitExclude(rigPath); err != nil {
|
||||
// Non-fatal, continue
|
||||
// Track cleanup on failure
|
||||
cleanup := func() { os.RemoveAll(rigPath) }
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
cleanup()
|
||||
}
|
||||
}()
|
||||
|
||||
// Create rig config
|
||||
rigConfig := &RigConfig{
|
||||
Type: "rig",
|
||||
Version: CurrentRigConfigVersion,
|
||||
Name: opts.Name,
|
||||
GitURL: opts.GitURL,
|
||||
CreatedAt: time.Now(),
|
||||
Beads: &BeadsConfig{
|
||||
Prefix: opts.BeadsPrefix,
|
||||
},
|
||||
}
|
||||
if err := m.saveRigConfig(rigPath, rigConfig); err != nil {
|
||||
return nil, fmt.Errorf("saving rig config: %w", err)
|
||||
}
|
||||
|
||||
// Register in config
|
||||
m.config.Rigs[name] = config.RigEntry{
|
||||
GitURL: gitURL,
|
||||
// Clone repository for refinery (canonical main)
|
||||
refineryRigPath := filepath.Join(rigPath, "refinery", "rig")
|
||||
if err := os.MkdirAll(filepath.Dir(refineryRigPath), 0755); err != nil {
|
||||
return nil, fmt.Errorf("creating refinery dir: %w", err)
|
||||
}
|
||||
if err := m.git.Clone(opts.GitURL, refineryRigPath); err != nil {
|
||||
return nil, fmt.Errorf("cloning for refinery: %w", err)
|
||||
}
|
||||
|
||||
return m.loadRig(name, m.config.Rigs[name])
|
||||
// Clone repository for mayor
|
||||
mayorRigPath := filepath.Join(rigPath, "mayor", "rig")
|
||||
if err := os.MkdirAll(filepath.Dir(mayorRigPath), 0755); err != nil {
|
||||
return nil, fmt.Errorf("creating mayor dir: %w", err)
|
||||
}
|
||||
if err := m.git.Clone(opts.GitURL, mayorRigPath); err != nil {
|
||||
return nil, fmt.Errorf("cloning for mayor: %w", err)
|
||||
}
|
||||
|
||||
// Clone repository for default crew workspace
|
||||
crewPath := filepath.Join(rigPath, "crew", opts.CrewName)
|
||||
if err := os.MkdirAll(filepath.Dir(crewPath), 0755); err != nil {
|
||||
return nil, fmt.Errorf("creating crew dir: %w", err)
|
||||
}
|
||||
if err := m.git.Clone(opts.GitURL, crewPath); err != nil {
|
||||
return nil, fmt.Errorf("cloning for crew: %w", err)
|
||||
}
|
||||
|
||||
// Create witness directory (no clone needed)
|
||||
witnessPath := filepath.Join(rigPath, "witness")
|
||||
if err := os.MkdirAll(witnessPath, 0755); err != nil {
|
||||
return nil, fmt.Errorf("creating witness dir: %w", err)
|
||||
}
|
||||
|
||||
// Create polecats directory (empty)
|
||||
polecatsPath := filepath.Join(rigPath, "polecats")
|
||||
if err := os.MkdirAll(polecatsPath, 0755); err != nil {
|
||||
return nil, fmt.Errorf("creating polecats dir: %w", err)
|
||||
}
|
||||
|
||||
// Initialize agent state files
|
||||
if err := m.initAgentStates(rigPath); err != nil {
|
||||
return nil, fmt.Errorf("initializing agent states: %w", err)
|
||||
}
|
||||
|
||||
// Initialize beads at rig level
|
||||
if err := m.initBeads(rigPath, opts.BeadsPrefix); err != nil {
|
||||
return nil, fmt.Errorf("initializing beads: %w", err)
|
||||
}
|
||||
|
||||
// Register in town config
|
||||
m.config.Rigs[opts.Name] = config.RigEntry{
|
||||
GitURL: opts.GitURL,
|
||||
AddedAt: time.Now(),
|
||||
BeadsConfig: &config.BeadsConfig{
|
||||
Prefix: opts.BeadsPrefix,
|
||||
},
|
||||
}
|
||||
|
||||
success = true
|
||||
return m.loadRig(opts.Name, m.config.Rigs[opts.Name])
|
||||
}
|
||||
|
||||
// saveRigConfig writes the rig configuration to config.json.
|
||||
func (m *Manager) saveRigConfig(rigPath string, cfg *RigConfig) error {
|
||||
configPath := filepath.Join(rigPath, "config.json")
|
||||
data, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(configPath, data, 0644)
|
||||
}
|
||||
|
||||
// initAgentStates creates initial state.json files for agents.
|
||||
func (m *Manager) initAgentStates(rigPath string) error {
|
||||
agents := []struct {
|
||||
path string
|
||||
role string
|
||||
}{
|
||||
{filepath.Join(rigPath, "refinery", "state.json"), "refinery"},
|
||||
{filepath.Join(rigPath, "witness", "state.json"), "witness"},
|
||||
{filepath.Join(rigPath, "mayor", "state.json"), "mayor"},
|
||||
}
|
||||
|
||||
for _, agent := range agents {
|
||||
state := &config.AgentState{
|
||||
Role: agent.role,
|
||||
LastActive: time.Now(),
|
||||
}
|
||||
data, err := json.MarshalIndent(state, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(agent.path, data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// initBeads initializes the beads database at rig level.
|
||||
func (m *Manager) initBeads(rigPath, prefix string) error {
|
||||
beadsDir := filepath.Join(rigPath, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run bd init if available, with --no-agents to skip AGENTS.md creation
|
||||
cmd := exec.Command("bd", "init", "--prefix", prefix, "--no-agents")
|
||||
cmd.Dir = rigPath
|
||||
if err := cmd.Run(); err != nil {
|
||||
// bd might not be installed or --no-agents not supported, create minimal structure
|
||||
configPath := filepath.Join(beadsDir, "config.yaml")
|
||||
configContent := fmt.Sprintf("prefix: %s\n", prefix)
|
||||
if writeErr := os.WriteFile(configPath, []byte(configContent), 0644); writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deriveBeadsPrefix generates a beads prefix from a rig name.
|
||||
// Examples: "gastown" -> "gt", "my-project" -> "mp", "foo" -> "foo"
|
||||
func deriveBeadsPrefix(name string) string {
|
||||
// Remove common suffixes
|
||||
name = strings.TrimSuffix(name, "-py")
|
||||
name = strings.TrimSuffix(name, "-go")
|
||||
|
||||
// Split on hyphens/underscores
|
||||
parts := strings.FieldsFunc(name, func(r rune) bool {
|
||||
return r == '-' || r == '_'
|
||||
})
|
||||
|
||||
if len(parts) >= 2 {
|
||||
// Take first letter of each part: "gas-town" -> "gt"
|
||||
prefix := ""
|
||||
for _, p := range parts {
|
||||
if len(p) > 0 {
|
||||
prefix += string(p[0])
|
||||
}
|
||||
}
|
||||
return strings.ToLower(prefix)
|
||||
}
|
||||
|
||||
// Single word: use first 2-3 chars
|
||||
if len(name) <= 3 {
|
||||
return strings.ToLower(name)
|
||||
}
|
||||
return strings.ToLower(name[:2])
|
||||
}
|
||||
|
||||
// RemoveRig unregisters a rig (does not delete files).
|
||||
@@ -173,37 +376,6 @@ func (m *Manager) RemoveRig(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// createAgentDirs creates the standard agent directory structure.
|
||||
func (m *Manager) createAgentDirs(rigPath string) error {
|
||||
for _, dir := range AgentDirs {
|
||||
dirPath := filepath.Join(rigPath, dir)
|
||||
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
||||
return fmt.Errorf("creating %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateGitExclude adds agent directories to .git/info/exclude.
|
||||
func (m *Manager) updateGitExclude(rigPath string) error {
|
||||
excludePath := filepath.Join(rigPath, ".git", "info", "exclude")
|
||||
|
||||
// Read existing content
|
||||
content, err := os.ReadFile(excludePath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Append agent dirs
|
||||
additions := "\n# Gas Town agent directories\n"
|
||||
for _, dir := range AgentDirs {
|
||||
additions += dir + "/\n"
|
||||
}
|
||||
|
||||
// Write back
|
||||
return os.WriteFile(excludePath, append(content, []byte(additions)...), 0644)
|
||||
}
|
||||
|
||||
// ListRigNames returns the names of all registered rigs.
|
||||
func (m *Manager) ListRigNames() []string {
|
||||
names := make([]string, 0, len(m.config.Rigs))
|
||||
|
||||
Reference in New Issue
Block a user