Add persistent theme config and fix crew session theming
- Fix crew sessions missing theme application (ConfigureGasTownSession) - Add theme persistence to .gastown/config.json - gt theme <name> now saves to config - gt theme apply reads from config, falls back to hash-based default - Improve rig detection using GT_RIG env var and path parsing - gt theme shows whether theme is configured or default 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
{"id":"gt-01u","title":"Design: Collapse mail into beads","description":"## Proposal\n\nReplace the separate mail system (JSONL inboxes) with beads issues using a naming convention.\n\n## Rationale\n\nIf all state should be in beads (work items, swarm state, dependencies), why have a separate system for messages? Mail is just:\n- Handoffs (agent → self)\n- Commands (Mayor → Refinery)\n- Escalations (Witness → Mayor)\n\nThese can all be beads issues with a special prefix.\n\n## Design\n\n### Convention\n- Messages use `@-` prefix: `@-witness-1734012345`\n- Assignee = recipient\n- Status: open = unread, closed = read/acknowledged\n- Priority 0 = urgent\n\n### Commands (thin wrappers)\n```bash\ngt mail send witness -s \"Subject\" -m \"Body\"\n → bd create --prefix=@ --title=\"Subject\" --assignee=witness --description=\"Body\"\n\ngt mail inbox\n → bd list --prefix=@ --assignee=$(gt whoami) --status=open\n\ngt mail read @-abc\n → bd show @-abc \u0026\u0026 bd close @-abc\n```\n\n### Notification\nDaemon watches for new `@-` issues and pokes relevant sessions.\nOr: agents poll on heartbeat (simpler).\n\n## What We Remove\n- `mail/inbox.jsonl` files\n- Mail JSONL read/write code\n- Separate delivery mechanism\n\n## What We Keep\n- `gt mail` CLI (as wrapper)\n- Handoff semantics\n- Notification (via daemon or polling)\n\n## Benefits\n- One system, one sync, one query interface\n- All communication in git history\n- Simpler architecture\n\n## Risks\n- Beads prefix filtering must be efficient\n- Namespace collision with user prefixes\n- Performance for high-frequency messages (probably fine for handoffs)\n\n## Decision Point\nDo we need first-class mail support in beads (`bd mail` commands) or is convention sufficient?","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-16T02:10:18.32879-08:00","updated_at":"2025-12-16T13:12:16.46526-08:00","closed_at":"2025-12-16T13:12:16.46526-08:00"}
|
{"id":"gt-01u","title":"Design: Collapse mail into beads","description":"## Proposal\n\nReplace the separate mail system (JSONL inboxes) with beads issues using a naming convention.\n\n## Rationale\n\nIf all state should be in beads (work items, swarm state, dependencies), why have a separate system for messages? Mail is just:\n- Handoffs (agent → self)\n- Commands (Mayor → Refinery)\n- Escalations (Witness → Mayor)\n\nThese can all be beads issues with a special prefix.\n\n## Design\n\n### Convention\n- Messages use `@-` prefix: `@-witness-1734012345`\n- Assignee = recipient\n- Status: open = unread, closed = read/acknowledged\n- Priority 0 = urgent\n\n### Commands (thin wrappers)\n```bash\ngt mail send witness -s \"Subject\" -m \"Body\"\n → bd create --prefix=@ --title=\"Subject\" --assignee=witness --description=\"Body\"\n\ngt mail inbox\n → bd list --prefix=@ --assignee=$(gt whoami) --status=open\n\ngt mail read @-abc\n → bd show @-abc \u0026\u0026 bd close @-abc\n```\n\n### Notification\nDaemon watches for new `@-` issues and pokes relevant sessions.\nOr: agents poll on heartbeat (simpler).\n\n## What We Remove\n- `mail/inbox.jsonl` files\n- Mail JSONL read/write code\n- Separate delivery mechanism\n\n## What We Keep\n- `gt mail` CLI (as wrapper)\n- Handoff semantics\n- Notification (via daemon or polling)\n\n## Benefits\n- One system, one sync, one query interface\n- All communication in git history\n- Simpler architecture\n\n## Risks\n- Beads prefix filtering must be efficient\n- Namespace collision with user prefixes\n- Performance for high-frequency messages (probably fine for handoffs)\n\n## Decision Point\nDo we need first-class mail support in beads (`bd mail` commands) or is convention sufficient?","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-16T02:10:18.32879-08:00","updated_at":"2025-12-16T13:12:16.46526-08:00","closed_at":"2025-12-16T13:12:16.46526-08:00"}
|
||||||
{"id":"gt-082","title":"Worker cleanup: Beads sync on shutdown","description":"Add beads sync verification to worker cleanup checklist and Witness verification.\n\n## Update to Decommission Checklist (gt-sd6)\n\nAdd to pre-done verification:\n- bd sync --status must show 'Up to date'\n- git status .beads/ must show no changes\n\n## Beads Edge Cases\n\nUncommitted beads changes:\n bd sync\n git add .beads/\n git commit -m 'beads: final sync'\n\nBeads sync conflict (rare):\n git fetch origin main\n git checkout main -- .beads/\n bd sync --force\n git add .beads/\n git commit -m 'beads: resolve sync conflict'\n\n## Update to Witness Verification (gt-f8v)\n\nWhen capturing worker state:\n town capture \u003cpolecat\u003e \"bd sync --status \u0026\u0026 git status .beads/\"\n\nCheck for:\n- bd sync --status shows 'Up to date'\n- git status .beads/ shows no changes\n\nIf beads not synced, nudge:\n WITNESS CHECK: Beads not synced. Run 'bd sync' then commit .beads/. Signal done when complete.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T19:47:21.757756-08:00","updated_at":"2025-12-15T20:48:37.663168-08:00","dependencies":[{"issue_id":"gt-082","depends_on_id":"gt-l3c","type":"blocks","created_at":"2025-12-15T19:47:35.977804-08:00","created_by":"daemon"}]}
|
{"id":"gt-082","title":"Worker cleanup: Beads sync on shutdown","description":"Add beads sync verification to worker cleanup checklist and Witness verification.\n\n## Update to Decommission Checklist (gt-sd6)\n\nAdd to pre-done verification:\n- bd sync --status must show 'Up to date'\n- git status .beads/ must show no changes\n\n## Beads Edge Cases\n\nUncommitted beads changes:\n bd sync\n git add .beads/\n git commit -m 'beads: final sync'\n\nBeads sync conflict (rare):\n git fetch origin main\n git checkout main -- .beads/\n bd sync --force\n git add .beads/\n git commit -m 'beads: resolve sync conflict'\n\n## Update to Witness Verification (gt-f8v)\n\nWhen capturing worker state:\n town capture \u003cpolecat\u003e \"bd sync --status \u0026\u0026 git status .beads/\"\n\nCheck for:\n- bd sync --status shows 'Up to date'\n- git status .beads/ shows no changes\n\nIf beads not synced, nudge:\n WITNESS CHECK: Beads not synced. Run 'bd sync' then commit .beads/. Signal done when complete.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-15T19:47:21.757756-08:00","updated_at":"2025-12-15T20:48:37.663168-08:00","dependencies":[{"issue_id":"gt-082","depends_on_id":"gt-l3c","type":"blocks","created_at":"2025-12-15T19:47:35.977804-08:00","created_by":"daemon"}]}
|
||||||
{"id":"gt-0asj","title":"Merge: gt-5af.5","description":"branch: polecat/Scabrous\ntarget: main\nsource_issue: gt-5af.5\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T17:50:25.227909-08:00","updated_at":"2025-12-19T17:52:57.683445-08:00","closed_at":"2025-12-19T17:52:57.683445-08:00","close_reason":"Superseded - source issue gt-5af.5 already closed, branch has extensive conflicts with main"}
|
{"id":"gt-0asj","title":"Merge: gt-5af.5","description":"branch: polecat/Scabrous\ntarget: main\nsource_issue: gt-5af.5\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T17:50:25.227909-08:00","updated_at":"2025-12-19T17:52:57.683445-08:00","closed_at":"2025-12-19T17:52:57.683445-08:00","close_reason":"Superseded - source issue gt-5af.5 already closed, branch has extensive conflicts with main"}
|
||||||
|
{"id":"gt-0ei3","title":"Add molecules.jsonl as separate catalog file for template molecules","description":"","design":"Template molecules should live in a separate molecules.jsonl file, distinct from work items in issues.jsonl.\n\n## Rationale\n- Templates are read-only, work items are read-write\n- Clean separation makes bd list show only work, not templates\n- Enables sharing/versioning molecule catalogs independently\n- Organizations can publish/share molecule catalogs\n- Built-in molecules ship with bd, not seeded per-project\n\n## File Location\n.beads/\n├── beads.db # Runtime database\n├── issues.jsonl # Work items (synced)\n├── molecules.jsonl # Template catalog (read-only)\n└── config.yaml\n\n## Hierarchical Loading\n1. Built-in molecules (shipped with bd binary)\n2. Town-level: ~/gt/.beads/molecules.jsonl\n3. Rig-level: ~/gt/\u003crig\u003e/.beads/molecules.jsonl\n4. Project-level: committed in repo\n\n## Instantiation\nbd molecule instantiate mol-polecat-work --parent gt-123\nCreates child issues in issues.jsonl with instantiated_from reference.\n\n## Key Properties\n- Templates marked is_template: true in graph\n- Templates are read-only (mutations rejected)\n- bd list excludes templates by default\n- bd molecule list shows catalog","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-19T20:16:10.763471-08:00","updated_at":"2025-12-19T20:16:19.682508-08:00"}
|
||||||
{"id":"gt-0iy3","title":"Merge: gt-3x1.3","description":"branch: polecat/Doof\ntarget: main\nsource_issue: gt-3x1.3\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T14:53:52.741123-08:00","updated_at":"2025-12-19T19:13:27.737052-08:00","closed_at":"2025-12-19T17:47:03.618858-08:00"}
|
{"id":"gt-0iy3","title":"Merge: gt-3x1.3","description":"branch: polecat/Doof\ntarget: main\nsource_issue: gt-3x1.3\nrig: gastown","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-19T14:53:52.741123-08:00","updated_at":"2025-12-19T19:13:27.737052-08:00","closed_at":"2025-12-19T17:47:03.618858-08:00"}
|
||||||
{"id":"gt-0ol","title":"Update prompts.md: Engineer role and templates","description":"Update docs/prompts.md with Engineer role:\n\n1. Role Prompts table: Change Refinery to Engineer\n2. Add Engineer-specific prompts:\n - Session restart request template\n - Subtask filing template\n - Handoff mail template\n3. Update refinery.md template name to engineer.md\n4. Ensure consistency with architecture.md","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T23:12:05.279233-08:00","updated_at":"2025-12-16T23:12:05.279233-08:00","dependencies":[{"issue_id":"gt-0ol","depends_on_id":"gt-h5n","type":"blocks","created_at":"2025-12-16T23:12:15.013747-08:00","created_by":"daemon"}]}
|
{"id":"gt-0ol","title":"Update prompts.md: Engineer role and templates","description":"Update docs/prompts.md with Engineer role:\n\n1. Role Prompts table: Change Refinery to Engineer\n2. Add Engineer-specific prompts:\n - Session restart request template\n - Subtask filing template\n - Handoff mail template\n3. Update refinery.md template name to engineer.md\n4. Ensure consistency with architecture.md","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T23:12:05.279233-08:00","updated_at":"2025-12-16T23:12:05.279233-08:00","dependencies":[{"issue_id":"gt-0ol","depends_on_id":"gt-h5n","type":"blocks","created_at":"2025-12-16T23:12:15.013747-08:00","created_by":"daemon"}]}
|
||||||
{"id":"gt-0pc","title":"Document Overseer role (human operator)","description":"Document the Overseer role in Gas Town architecture.\n\n## The Overseer\n\nThe **Overseer** is the human operator of Gas Town. Not an agent - a person.\n\n## Responsibilities\n\n| Area | Overseer Does | Mayor/Agents Do |\n|------|---------------|-----------------|\n| Strategy | Define project goals | Execute toward goals |\n| Priorities | Set priority order | Work in priority order |\n| Escalations | Final decision on stuck work | Escalate to Overseer |\n| Resources | Provision machines | Use allocated resources |\n| Quality | Review \u0026 approve swarm output | Produce output |\n| Operations | Run gt commands, monitor dashboards | Do the work |\n\n## Key Interactions\n\n### Overseer → Mayor\n- Start/stop Mayor sessions\n- Direct Mayor via conversation\n- Review Mayor recommendations\n- Approve cross-rig decisions\n\n### Mayor → Overseer (Escalations)\n- Stuck workers after retries\n- Resource decisions (add machines, polecats)\n- Ambiguous requirements\n- Architecture decisions\n\n## Operating Cadence\n\nTypical Overseer workflow:\n1. Morning: Check status, review overnight work\n2. During day: Monitor, respond to escalations, adjust priorities\n3. End of day: Review progress, plan next batch\n\n## Commands for Overseers\n\n```bash\ngt status # Quick health check\ngt doctor # Detailed diagnostics \ngt doctor --fix # Auto-repair issues\ngt inbox # Messages from agents\ngt stop --all # Emergency halt\n```\n\n## Documentation Updates\n\nAdd to docs/architecture.md:\n- Overseer section under Agent Roles\n- Clarify Mayor reports to Overseer\n- Add Overseer to workflow diagrams","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-15T23:18:03.177633-08:00","updated_at":"2025-12-15T23:22:51.477786-08:00","closed_at":"2025-12-15T23:22:51.477786-08:00"}
|
{"id":"gt-0pc","title":"Document Overseer role (human operator)","description":"Document the Overseer role in Gas Town architecture.\n\n## The Overseer\n\nThe **Overseer** is the human operator of Gas Town. Not an agent - a person.\n\n## Responsibilities\n\n| Area | Overseer Does | Mayor/Agents Do |\n|------|---------------|-----------------|\n| Strategy | Define project goals | Execute toward goals |\n| Priorities | Set priority order | Work in priority order |\n| Escalations | Final decision on stuck work | Escalate to Overseer |\n| Resources | Provision machines | Use allocated resources |\n| Quality | Review \u0026 approve swarm output | Produce output |\n| Operations | Run gt commands, monitor dashboards | Do the work |\n\n## Key Interactions\n\n### Overseer → Mayor\n- Start/stop Mayor sessions\n- Direct Mayor via conversation\n- Review Mayor recommendations\n- Approve cross-rig decisions\n\n### Mayor → Overseer (Escalations)\n- Stuck workers after retries\n- Resource decisions (add machines, polecats)\n- Ambiguous requirements\n- Architecture decisions\n\n## Operating Cadence\n\nTypical Overseer workflow:\n1. Morning: Check status, review overnight work\n2. During day: Monitor, respond to escalations, adjust priorities\n3. End of day: Review progress, plan next batch\n\n## Commands for Overseers\n\n```bash\ngt status # Quick health check\ngt doctor # Detailed diagnostics \ngt doctor --fix # Auto-repair issues\ngt inbox # Messages from agents\ngt stop --all # Emergency halt\n```\n\n## Documentation Updates\n\nAdd to docs/architecture.md:\n- Overseer section under Agent Roles\n- Clarify Mayor reports to Overseer\n- Add Overseer to workflow diagrams","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-15T23:18:03.177633-08:00","updated_at":"2025-12-15T23:22:51.477786-08:00","closed_at":"2025-12-15T23:22:51.477786-08:00"}
|
||||||
|
|||||||
@@ -506,6 +506,10 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
|||||||
_ = t.SetEnvironment(sessionID, "GT_RIG", r.Name)
|
_ = t.SetEnvironment(sessionID, "GT_RIG", r.Name)
|
||||||
_ = t.SetEnvironment(sessionID, "GT_CREW", name)
|
_ = t.SetEnvironment(sessionID, "GT_CREW", name)
|
||||||
|
|
||||||
|
// Apply rig-based theming (uses config if set, falls back to hash)
|
||||||
|
theme := getThemeForRig(r.Name)
|
||||||
|
_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")
|
||||||
|
|
||||||
// Wait for shell to be ready after session creation
|
// Wait for shell to be ready after session creation
|
||||||
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
||||||
return fmt.Errorf("waiting for shell: %w", err)
|
return fmt.Errorf("waiting for shell: %w", err)
|
||||||
@@ -847,6 +851,10 @@ func runCrewRestart(cmd *cobra.Command, args []string) error {
|
|||||||
t.SetEnvironment(sessionID, "GT_RIG", r.Name)
|
t.SetEnvironment(sessionID, "GT_RIG", r.Name)
|
||||||
t.SetEnvironment(sessionID, "GT_CREW", name)
|
t.SetEnvironment(sessionID, "GT_CREW", name)
|
||||||
|
|
||||||
|
// Apply rig-based theming (uses config if set, falls back to hash)
|
||||||
|
theme := getThemeForRig(r.Name)
|
||||||
|
_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")
|
||||||
|
|
||||||
// Wait for shell to be ready
|
// Wait for shell to be ready
|
||||||
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
||||||
return fmt.Errorf("waiting for shell: %w", err)
|
return fmt.Errorf("waiting for shell: %w", err)
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
"github.com/steveyegge/gastown/internal/tmux"
|
"github.com/steveyegge/gastown/internal/tmux"
|
||||||
|
"github.com/steveyegge/gastown/internal/workspace"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -63,9 +67,15 @@ func runTheme(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// Show current theme assignment
|
// Show current theme assignment
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
theme := tmux.AssignTheme(rigName)
|
theme := getThemeForRig(rigName)
|
||||||
fmt.Printf("Rig: %s\n", rigName)
|
fmt.Printf("Rig: %s\n", rigName)
|
||||||
fmt.Printf("Theme: %s (%s)\n", theme.Name, theme.Style())
|
fmt.Printf("Theme: %s (%s)\n", theme.Name, theme.Style())
|
||||||
|
// Show if it's configured vs default
|
||||||
|
if configured := loadRigTheme(rigName); configured != "" {
|
||||||
|
fmt.Printf("(configured in .gastown/config.json)\n")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("(default, based on rig name hash)\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +86,13 @@ func runTheme(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("unknown theme: %s (use --list to see available themes)", themeName)
|
return fmt.Errorf("unknown theme: %s (use --list to see available themes)", themeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Save to rig config.json
|
// Save to rig config
|
||||||
fmt.Printf("Theme '%s' selected for rig '%s'\n", themeName, rigName)
|
if err := saveRigTheme(rigName, themeName); err != nil {
|
||||||
fmt.Println("Note: Run 'gt theme apply' to apply to running sessions")
|
return fmt.Errorf("saving theme config: %w", err)
|
||||||
fmt.Println("(Persistent config not yet implemented)")
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Theme '%s' saved for rig '%s'\n", themeName, rigName)
|
||||||
|
fmt.Println("Run 'gt theme apply' to apply to running sessions")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -133,7 +146,8 @@ func runThemeApply(cmd *cobra.Command, args []string) error {
|
|||||||
role = "polecat"
|
role = "polecat"
|
||||||
}
|
}
|
||||||
|
|
||||||
theme = tmux.AssignTheme(rig)
|
// Use configured theme, fall back to hash-based assignment
|
||||||
|
theme = getThemeForRig(rig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply theme and status format
|
// Apply theme and status format
|
||||||
@@ -165,29 +179,115 @@ func runThemeApply(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// detectCurrentRig determines the rig from environment or cwd.
|
// detectCurrentRig determines the rig from environment or cwd.
|
||||||
func detectCurrentRig() string {
|
func detectCurrentRig() string {
|
||||||
// Try environment first
|
// Try environment first (GT_RIG is set in tmux sessions)
|
||||||
if rig := detectCurrentSession(); rig != "" {
|
if rig := os.Getenv("GT_RIG"); rig != "" {
|
||||||
// Extract rig from session name
|
return rig
|
||||||
parts := strings.SplitN(rig, "-", 3)
|
}
|
||||||
if len(parts) >= 2 && parts[0] == "gt" {
|
|
||||||
|
// Try to extract from tmux session name
|
||||||
|
if session := detectCurrentSession(); session != "" {
|
||||||
|
// Extract rig from session name: gt-<rig>-...
|
||||||
|
parts := strings.SplitN(session, "-", 3)
|
||||||
|
if len(parts) >= 2 && parts[0] == "gt" && parts[1] != "mayor" && parts[1] != "deacon" {
|
||||||
return parts[1]
|
return parts[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to detect from cwd
|
// Try to detect from actual cwd path
|
||||||
cwd, err := findBeadsWorkDir()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract rig name from path
|
// Find town root to extract rig name
|
||||||
// Typical paths: /Users/stevey/gt/<rig>/...
|
townRoot, err := workspace.FindFromCwd()
|
||||||
parts := strings.Split(cwd, "/")
|
if err != nil || townRoot == "" {
|
||||||
for i, p := range parts {
|
return ""
|
||||||
if p == "gt" && i+1 < len(parts) {
|
}
|
||||||
return parts[i+1]
|
|
||||||
}
|
// Get path relative to town root
|
||||||
|
rel, err := filepath.Rel(townRoot, cwd)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract first path component (rig name)
|
||||||
|
// Patterns: <rig>/..., mayor/..., deacon/...
|
||||||
|
parts := strings.Split(rel, string(filepath.Separator))
|
||||||
|
if len(parts) > 0 && parts[0] != "." && parts[0] != "mayor" && parts[0] != "deacon" {
|
||||||
|
return parts[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getThemeForRig returns the theme for a rig, checking config first.
|
||||||
|
func getThemeForRig(rigName string) tmux.Theme {
|
||||||
|
// Try to load configured theme
|
||||||
|
if themeName := loadRigTheme(rigName); themeName != "" {
|
||||||
|
if theme := tmux.GetThemeByName(themeName); theme != nil {
|
||||||
|
return *theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to hash-based assignment
|
||||||
|
return tmux.AssignTheme(rigName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadRigTheme loads the theme name from rig config.
|
||||||
|
func loadRigTheme(rigName string) string {
|
||||||
|
townRoot, err := workspace.FindFromCwd()
|
||||||
|
if err != nil || townRoot == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := filepath.Join(townRoot, rigName, ".gastown", "config.json")
|
||||||
|
cfg, err := config.LoadRigConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Theme != nil && cfg.Theme.Name != "" {
|
||||||
|
return cfg.Theme.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveRigTheme saves the theme name to rig config.
|
||||||
|
func saveRigTheme(rigName, themeName string) error {
|
||||||
|
townRoot, err := workspace.FindFromCwd()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("finding workspace: %w", err)
|
||||||
|
}
|
||||||
|
if townRoot == "" {
|
||||||
|
return fmt.Errorf("not in a Gas Town workspace")
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath := filepath.Join(townRoot, rigName, ".gastown", "config.json")
|
||||||
|
|
||||||
|
// Load existing config or create new
|
||||||
|
var cfg *config.RigConfig
|
||||||
|
cfg, err = config.LoadRigConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
// Create new config if not found
|
||||||
|
if os.IsNotExist(err) || strings.Contains(err.Error(), "not found") {
|
||||||
|
cfg = &config.RigConfig{
|
||||||
|
Type: "rig",
|
||||||
|
Version: config.CurrentRigConfigVersion,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("loading config: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set theme
|
||||||
|
cfg.Theme = &config.ThemeConfig{
|
||||||
|
Name: themeName,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save
|
||||||
|
if err := config.SaveRigConfig(configPath, cfg); err != nil {
|
||||||
|
return fmt.Errorf("saving config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user