Update flake inputs, deduplicate beads/gastown package definitions
- Update beads and gastown flake inputs and vendorHashes - Remove merged-upstream patches for gastown and beads - Deduplicate beads/gastown build definitions in home role by using callPackage to reference shared packages/ definitions - Pin dolt to v1.82.4 (gastown requires >= 1.82.4) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
12
flake.lock
generated
12
flake.lock
generated
@@ -7,11 +7,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772587450,
|
"lastModified": 1773070962,
|
||||||
"narHash": "sha256-yJ38xi6nRnEZIV5CEgV8xZK1dky0dVMavfFg4J7gkqw=",
|
"narHash": "sha256-kHZXx+kygpVholOBsuQocCtksHo5ZWYskP64qK2Kjh0=",
|
||||||
"owner": "steveyegge",
|
"owner": "steveyegge",
|
||||||
"repo": "beads",
|
"repo": "beads",
|
||||||
"rev": "7f41edd9c4350e5c68ebec6f9571e27833f27133",
|
"rev": "9604d30b7c746f9f04f6dea5f82996f71bb66073",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -88,11 +88,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772724442,
|
"lastModified": 1773088127,
|
||||||
"narHash": "sha256-+H/T745sVDJAztpf3k6zz89YNrpCYsGghbO7O3O/IHw=",
|
"narHash": "sha256-gJFayiBYrF0Q99AOQH29uq0Mli8KRfwReYeAh5H5evY=",
|
||||||
"owner": "steveyegge",
|
"owner": "steveyegge",
|
||||||
"repo": "gastown",
|
"repo": "gastown",
|
||||||
"rev": "112ff2c4bd145de796d20f35d35e583832a72ce1",
|
"rev": "8da798be0663af74be7960844b90038e51769203",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
13
flake.nix
13
flake.nix
@@ -113,6 +113,19 @@
|
|||||||
(ufinal: uprev: {
|
(ufinal: uprev: {
|
||||||
claude-code = uprev.callPackage ./packages/claude-code {};
|
claude-code = uprev.callPackage ./packages/claude-code {};
|
||||||
})
|
})
|
||||||
|
# Pin dolt to v1.82.4 (gastown requires >= 1.82.4)
|
||||||
|
(ufinal: uprev: {
|
||||||
|
dolt = uprev.dolt.overrideAttrs (old: rec {
|
||||||
|
version = "1.82.4";
|
||||||
|
src = uprev.fetchFromGitHub {
|
||||||
|
owner = "dolthub";
|
||||||
|
repo = "dolt";
|
||||||
|
tag = "v${version}";
|
||||||
|
hash = "sha256-mavL3y+Kv25hzFlDFXk7W/jeKVKlCBjlc67GkL3Jcwk=";
|
||||||
|
};
|
||||||
|
vendorHash = "sha256-K1KzsqptZxO5OraWKIXeqKuVSzb6E/Mjy3c5PQ7Rs9k=";
|
||||||
|
});
|
||||||
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
nixosModules = [
|
nixosModules = [
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
diff --git a/internal/storage/dolt/queries.go b/internal/storage/dolt/queries.go
|
|
||||||
index 7d8214ee..8acdaae2 100644
|
|
||||||
--- a/internal/storage/dolt/queries.go
|
|
||||||
+++ b/internal/storage/dolt/queries.go
|
|
||||||
@@ -212,8 +212,21 @@ func (s *DoltStore) SearchIssues(ctx context.Context, query string, filter types
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:gosec // G201: whereSQL contains column comparisons with ?, limitSQL is a safe integer
|
|
||||||
+ // Performance fix: SELECT all columns directly instead of id-only + WHERE IN (all_ids)
|
|
||||||
+ // See: hq-ihwsj - bd list uses inefficient WHERE IN (all_ids) query pattern
|
|
||||||
querySQL := fmt.Sprintf(`
|
|
||||||
- SELECT id FROM issues
|
|
||||||
+ SELECT id, content_hash, title, description, design, acceptance_criteria, notes,
|
|
||||||
+ status, priority, issue_type, assignee, estimated_minutes,
|
|
||||||
+ created_at, created_by, owner, updated_at, closed_at, external_ref,
|
|
||||||
+ compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason,
|
|
||||||
+ deleted_at, deleted_by, delete_reason, original_type,
|
|
||||||
+ sender, ephemeral, pinned, is_template, crystallizes,
|
|
||||||
+ await_type, await_id, timeout_ns, waiters,
|
|
||||||
+ hook_bead, role_bead, agent_state, last_activity, role_type, rig, mol_type,
|
|
||||||
+ event_kind, actor, target, payload,
|
|
||||||
+ due_at, defer_until,
|
|
||||||
+ quality_score, work_type, source_system
|
|
||||||
+ FROM issues
|
|
||||||
%s
|
|
||||||
ORDER BY priority ASC, created_at DESC
|
|
||||||
%s
|
|
||||||
@@ -225,7 +238,15 @@ func (s *DoltStore) SearchIssues(ctx context.Context, query string, filter types
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
- return s.scanIssueIDs(ctx, rows)
|
|
||||||
+ var issues []*types.Issue
|
|
||||||
+ for rows.Next() {
|
|
||||||
+ issue, err := scanIssueRow(rows)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return nil, err
|
|
||||||
+ }
|
|
||||||
+ issues = append(issues, issue)
|
|
||||||
+ }
|
|
||||||
+ return issues, rows.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetReadyWork returns issues that are ready to work on (not blocked)
|
|
||||||
@@ -5,65 +5,17 @@ with lib;
|
|||||||
let
|
let
|
||||||
cfg = config.home.roles.development;
|
cfg = config.home.roles.development;
|
||||||
|
|
||||||
# Build beads from flake input
|
# Build beads and gastown from flake inputs using shared package definitions
|
||||||
beadsRev = builtins.substring 0 8 (globalInputs.beads.rev or "unknown");
|
beadsRev = builtins.substring 0 8 (globalInputs.beads.rev or "unknown");
|
||||||
|
beadsPackage = pkgs.callPackage ../../../packages/beads {
|
||||||
beadsPackage = pkgs.unstable.buildGoModule {
|
|
||||||
pname = "beads";
|
|
||||||
version = "0.52.0-${beadsRev}";
|
|
||||||
src = globalInputs.beads;
|
src = globalInputs.beads;
|
||||||
subPackages = [ "cmd/bd" ];
|
version = "0.52.0-${beadsRev}";
|
||||||
doCheck = false;
|
|
||||||
vendorHash = "sha256-OL6QGf4xSMpEbmU+41pFdO0Rrs3H162T3pdiW9UfWR0=";
|
|
||||||
nativeBuildInputs = [ pkgs.git pkgs.pkg-config ];
|
|
||||||
buildInputs = [ pkgs.icu ];
|
|
||||||
meta = with lib; {
|
|
||||||
description = "beads (bd) - An issue tracker designed for AI-supervised coding workflows";
|
|
||||||
homepage = "https://github.com/steveyegge/beads";
|
|
||||||
license = licenses.mit;
|
|
||||||
mainProgram = "bd";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Gastown - multi-agent workspace manager
|
|
||||||
# Source is tracked via flake input for renovate updates
|
|
||||||
gastownRev = builtins.substring 0 8 (globalInputs.gastown.rev or "unknown");
|
gastownRev = builtins.substring 0 8 (globalInputs.gastown.rev or "unknown");
|
||||||
gastownPackage = pkgs.buildGoModule {
|
gastownPackage = pkgs.callPackage ../../../packages/gastown {
|
||||||
pname = "gastown";
|
|
||||||
version = "unstable-${gastownRev}";
|
|
||||||
src = globalInputs.gastown;
|
src = globalInputs.gastown;
|
||||||
vendorHash = "sha256-/+ODyndArUF0nJY9r8G5JKhzQckBHFb48A7EBZmoIr0=";
|
version = "unstable-${gastownRev}";
|
||||||
subPackages = [ "cmd/gt" ];
|
|
||||||
doCheck = false;
|
|
||||||
|
|
||||||
# Must match ldflags from gastown Makefile - BuiltProperly=1 is required
|
|
||||||
# or gt will error with "This binary was built with 'go build' directly"
|
|
||||||
ldflags = [
|
|
||||||
"-X github.com/steveyegge/gastown/internal/cmd.Version=${gastownRev}"
|
|
||||||
"-X github.com/steveyegge/gastown/internal/cmd.Commit=${gastownRev}"
|
|
||||||
"-X github.com/steveyegge/gastown/internal/cmd.BuildTime=nix-build"
|
|
||||||
"-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Bug fixes not yet merged upstream
|
|
||||||
# Each patch is stored in a separate file for clarity and maintainability
|
|
||||||
patches = [
|
|
||||||
# Fix validateRecipient bug: normalize addresses before comparison
|
|
||||||
./gastown-fix-validate-recipient.patch
|
|
||||||
# Fix agentBeadToAddress to use title field for hq- prefixed beads
|
|
||||||
./gastown-fix-agent-bead-address-title.patch
|
|
||||||
# town-root-detection fix merged upstream (detectRole removed)
|
|
||||||
# Statusline optimization: skip expensive beads queries for detached sessions
|
|
||||||
# Reduces Dolt CPU from ~70% to ~20% by caching and early-exit
|
|
||||||
./gastown-statusline-optimization.patch
|
|
||||||
];
|
|
||||||
|
|
||||||
meta = with lib; {
|
|
||||||
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
|
|
||||||
homepage = "https://github.com/steveyegge/gastown";
|
|
||||||
license = licenses.mit;
|
|
||||||
mainProgram = "gt";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Perles - TUI for beads issue tracking (no upstream flake.nix yet)
|
# Perles - TUI for beads issue tracking (no upstream flake.nix yet)
|
||||||
@@ -132,7 +84,7 @@ in
|
|||||||
pkgs.unstable.claude-code
|
pkgs.unstable.claude-code
|
||||||
pkgs.unstable.claude-code-router
|
pkgs.unstable.claude-code-router
|
||||||
pkgs.unstable.codex
|
pkgs.unstable.codex
|
||||||
pkgs.dolt
|
pkgs.unstable.dolt
|
||||||
pkgs.sqlite
|
pkgs.sqlite
|
||||||
|
|
||||||
# Custom packages
|
# Custom packages
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
|
||||||
--- a/internal/mail/router.go
|
|
||||||
+++ b/internal/mail/router.go
|
|
||||||
@@ -315,7 +315,10 @@ func agentBeadToAddress(bead *agentBead) string {
|
|
||||||
}
|
|
||||||
|
|
||||||
// For other hq- agents, fall back to description parsing
|
|
||||||
- return parseAgentAddressFromDescription(bead.Description)
|
|
||||||
+ if bead.Title != "" && strings.Contains(bead.Title, "/") {
|
|
||||||
+ return bead.Title
|
|
||||||
+ }
|
|
||||||
+ return parseAgentAddressFromDescription(bead.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle gt- prefixed IDs (legacy format)
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
|
||||||
--- a/internal/mail/router.go
|
|
||||||
+++ b/internal/mail/router.go
|
|
||||||
@@ -330,8 +330,29 @@ func agentBeadToAddress(bead *agentBead) string {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle gt- prefixed IDs (legacy format)
|
|
||||||
- if !strings.HasPrefix(id, "gt-") {
|
|
||||||
- return "" // Not a valid agent bead ID
|
|
||||||
+ // Handle rig-specific prefixes: <prefix>-<rig>-<role>-<name>
|
|
||||||
+ // Examples: j-java-crew-americano -> java/crew/americano
|
|
||||||
+ idParts := strings.Split(id, "-")
|
|
||||||
+ if len(idParts) >= 3 {
|
|
||||||
+ for i, part := range idParts {
|
|
||||||
+ if part == "crew" || part == "polecat" || part == "polecats" {
|
|
||||||
+ if i >= 1 && i < len(idParts)-1 {
|
|
||||||
+ rig := idParts[i-1]
|
|
||||||
+ name := strings.Join(idParts[i+1:], "-")
|
|
||||||
+ return rig + "/" + part + "/" + name
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ if part == "witness" || part == "refinery" {
|
|
||||||
+ if i >= 1 {
|
|
||||||
+ return idParts[i-1] + "/" + part
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Handle gt- prefixed IDs (legacy format)
|
|
||||||
+ if !strings.HasPrefix(id, "gt-") {
|
|
||||||
+ return "" // Not a valid agent bead ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip prefix
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
diff --git a/internal/git/git.go b/internal/git/git.go
|
|
||||||
--- a/internal/git/git.go
|
|
||||||
+++ b/internal/git/git.go
|
|
||||||
@@ -73,7 +73,19 @@ func copyDir(src, dest string) error {
|
|
||||||
srcPath := filepath.Join(src, entry.Name())
|
|
||||||
destPath := filepath.Join(dest, entry.Name())
|
|
||||||
|
|
||||||
- if entry.IsDir() {
|
|
||||||
+ // Handle symlinks (recreate them, do not follow)
|
|
||||||
+ if entry.Type()&os.ModeSymlink != 0 {
|
|
||||||
+ linkTarget, err := os.Readlink(srcPath)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return err
|
|
||||||
+ }
|
|
||||||
+ if err := os.Symlink(linkTarget, destPath); err != nil {
|
|
||||||
+ return err
|
|
||||||
+ }
|
|
||||||
+ continue
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if entry.IsDir() {
|
|
||||||
if err := copyDir(srcPath, destPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
diff --git a/internal/cmd/role.go b/internal/cmd/role.go
|
|
||||||
--- a/internal/cmd/role.go
|
|
||||||
+++ b/internal/cmd/role.go
|
|
||||||
@@ -326,11 +326,11 @@ func getRoleHome(role Role, rig, polecat, townRoot string) string {
|
|
||||||
if rig == "" || polecat == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
- return filepath.Join(townRoot, rig, "polecats", polecat, "rig")
|
|
||||||
+ return filepath.Join(townRoot, rig, "polecats", polecat)
|
|
||||||
case RoleCrew:
|
|
||||||
if rig == "" || polecat == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
- return filepath.Join(townRoot, rig, "crew", polecat, "rig")
|
|
||||||
+ return filepath.Join(townRoot, rig, "crew", polecat)
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
diff --git a/internal/cmd/prime.go b/internal/cmd/prime.go
|
|
||||||
--- a/internal/cmd/prime.go
|
|
||||||
+++ b/internal/cmd/prime.go
|
|
||||||
@@ -276,11 +276,12 @@ func detectRole(cwd, townRoot string) RoleInfo {
|
|
||||||
|
|
||||||
// Check for mayor role
|
|
||||||
// At town root, or in mayor/ or mayor/rig/
|
|
||||||
if relPath == "." || relPath == "" {
|
|
||||||
- ctx.Role = RoleMayor
|
|
||||||
- return ctx
|
|
||||||
+ return ctx // RoleUnknown - town root is shared space
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ // Check for mayor role: mayor/ or mayor/rig/
|
|
||||||
if len(parts) >= 1 && parts[0] == "mayor" {
|
|
||||||
ctx.Role = RoleMayor
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
|
||||||
index b864c069..4b6a045b 100644
|
|
||||||
--- a/internal/mail/router.go
|
|
||||||
+++ b/internal/mail/router.go
|
|
||||||
@@ -646,7 +646,7 @@ func (r *Router) validateRecipient(identity string) error {
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, agent := range agents {
|
|
||||||
- if agentBeadToAddress(agent) == identity {
|
|
||||||
+ if AddressToIdentity(agentBeadToAddress(agent)) == AddressToIdentity(identity) {
|
|
||||||
return nil // Found matching agent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
diff --git a/internal/cmd/statusline.go b/internal/cmd/statusline.go
|
|
||||||
index 2edf1be8..00253eea 100644
|
|
||||||
--- a/internal/cmd/statusline.go
|
|
||||||
+++ b/internal/cmd/statusline.go
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
+ "time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
|
||||||
@@ -14,6 +15,37 @@ import (
|
|
||||||
"github.com/steveyegge/gastown/internal/tmux"
|
|
||||||
"github.com/steveyegge/gastown/internal/workspace"
|
|
||||||
)
|
|
||||||
+// statusLineCacheTTL is how long cached status output remains valid.
|
|
||||||
+const statusLineCacheTTL = 10 * time.Second
|
|
||||||
+
|
|
||||||
+// statusLineCachePath returns the cache file path for a session.
|
|
||||||
+func statusLineCachePath(session string) string {
|
|
||||||
+ return filepath.Join(os.TempDir(), fmt.Sprintf("gt-status-%s", session))
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// getStatusLineCache returns cached status if fresh, empty string otherwise.
|
|
||||||
+func getStatusLineCache(session string) string {
|
|
||||||
+ path := statusLineCachePath(session)
|
|
||||||
+ info, err := os.Stat(path)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return ""
|
|
||||||
+ }
|
|
||||||
+ if time.Since(info.ModTime()) > statusLineCacheTTL {
|
|
||||||
+ return ""
|
|
||||||
+ }
|
|
||||||
+ data, err := os.ReadFile(path)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return ""
|
|
||||||
+ }
|
|
||||||
+ return string(data)
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// setStatusLineCache writes status to cache file.
|
|
||||||
+func setStatusLineCache(session, status string) {
|
|
||||||
+ path := statusLineCachePath(session)
|
|
||||||
+ _ = os.WriteFile(path, []byte(status), 0644)
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
|
|
||||||
var (
|
|
||||||
statusLineSession string
|
|
||||||
@@ -34,6 +66,19 @@ func init() {
|
|
||||||
func runStatusLine(cmd *cobra.Command, args []string) error {
|
|
||||||
t := tmux.NewTmux()
|
|
||||||
|
|
||||||
+ // Optimization: skip expensive beads queries for detached sessions
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ if !t.IsSessionAttached(statusLineSession) {
|
|
||||||
+ fmt.Print("○ |")
|
|
||||||
+ return nil
|
|
||||||
+ }
|
|
||||||
+ // Check cache for attached sessions too
|
|
||||||
+ if cached := getStatusLineCache(statusLineSession); cached != "" {
|
|
||||||
+ fmt.Print(cached)
|
|
||||||
+ return nil
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
// Get session environment
|
|
||||||
var rigName, polecat, crew, issue, role string
|
|
||||||
|
|
||||||
@@ -150,7 +195,11 @@ func runWorkerStatusLine(t *tmux.Tmux, session, rigName, polecat, crew, issue st
|
|
||||||
|
|
||||||
// Output
|
|
||||||
if len(parts) > 0 {
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
@@ -389,7 +438,11 @@ func runMayorStatusLine(t *tmux.Tmux) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -458,7 +511,11 @@ func runDeaconStatusLine(t *tmux.Tmux) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -526,7 +583,11 @@ func runWitnessStatusLine(t *tmux.Tmux, rigName string) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -617,7 +678,11 @@ func runRefineryStatusLine(t *tmux.Tmux, rigName string) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ buildGoModule {
|
|||||||
inherit version src;
|
inherit version src;
|
||||||
subPackages = [ "cmd/bd" ];
|
subPackages = [ "cmd/bd" ];
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
vendorHash = "sha256-OL6QGf4xSMpEbmU+41pFdO0Rrs3H162T3pdiW9UfWR0=";
|
vendorHash = "sha256-XAhe4yuLzP9vQ3IFhWAO5fN/3OOfokcRxfeGKaRYEws=";
|
||||||
nativeBuildInputs = [ git pkg-config ];
|
nativeBuildInputs = [ git pkg-config ];
|
||||||
buildInputs = [ icu ];
|
buildInputs = [ icu ];
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
buildGoModule {
|
buildGoModule {
|
||||||
pname = "gastown";
|
pname = "gastown";
|
||||||
inherit version src;
|
inherit version src;
|
||||||
vendorHash = "sha256-/+ODyndArUF0nJY9r8G5JKhzQckBHFb48A7EBZmoIr0=";
|
vendorHash = "sha256-8SdvSASP+bJjMooqEQvkCzG+J6CbsK+HCQulrPnJZ1Y=";
|
||||||
subPackages = [ "cmd/gt" ];
|
subPackages = [ "cmd/gt" ];
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
||||||
@@ -22,13 +22,6 @@ buildGoModule {
|
|||||||
"-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
|
"-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
|
||||||
];
|
];
|
||||||
|
|
||||||
# Bug fixes not yet merged upstream
|
|
||||||
patches = [
|
|
||||||
./gastown-fix-validate-recipient.patch
|
|
||||||
./gastown-fix-agent-bead-address-title.patch
|
|
||||||
./gastown-statusline-optimization.patch
|
|
||||||
];
|
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
|
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
|
||||||
homepage = "https://github.com/steveyegge/gastown";
|
homepage = "https://github.com/steveyegge/gastown";
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
|
||||||
--- a/internal/mail/router.go
|
|
||||||
+++ b/internal/mail/router.go
|
|
||||||
@@ -315,7 +315,10 @@ func agentBeadToAddress(bead *agentBead) string {
|
|
||||||
}
|
|
||||||
|
|
||||||
// For other hq- agents, fall back to description parsing
|
|
||||||
- return parseAgentAddressFromDescription(bead.Description)
|
|
||||||
+ if bead.Title != "" && strings.Contains(bead.Title, "/") {
|
|
||||||
+ return bead.Title
|
|
||||||
+ }
|
|
||||||
+ return parseAgentAddressFromDescription(bead.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle gt- prefixed IDs (legacy format)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
|
||||||
index b864c069..4b6a045b 100644
|
|
||||||
--- a/internal/mail/router.go
|
|
||||||
+++ b/internal/mail/router.go
|
|
||||||
@@ -646,7 +646,7 @@ func (r *Router) validateRecipient(identity string) error {
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, agent := range agents {
|
|
||||||
- if agentBeadToAddress(agent) == identity {
|
|
||||||
+ if AddressToIdentity(agentBeadToAddress(agent)) == AddressToIdentity(identity) {
|
|
||||||
return nil // Found matching agent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
diff --git a/internal/cmd/statusline.go b/internal/cmd/statusline.go
|
|
||||||
index 2edf1be8..00253eea 100644
|
|
||||||
--- a/internal/cmd/statusline.go
|
|
||||||
+++ b/internal/cmd/statusline.go
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
+ "time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
|
||||||
@@ -14,6 +15,37 @@ import (
|
|
||||||
"github.com/steveyegge/gastown/internal/tmux"
|
|
||||||
"github.com/steveyegge/gastown/internal/workspace"
|
|
||||||
)
|
|
||||||
+// statusLineCacheTTL is how long cached status output remains valid.
|
|
||||||
+const statusLineCacheTTL = 10 * time.Second
|
|
||||||
+
|
|
||||||
+// statusLineCachePath returns the cache file path for a session.
|
|
||||||
+func statusLineCachePath(session string) string {
|
|
||||||
+ return filepath.Join(os.TempDir(), fmt.Sprintf("gt-status-%s", session))
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// getStatusLineCache returns cached status if fresh, empty string otherwise.
|
|
||||||
+func getStatusLineCache(session string) string {
|
|
||||||
+ path := statusLineCachePath(session)
|
|
||||||
+ info, err := os.Stat(path)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return ""
|
|
||||||
+ }
|
|
||||||
+ if time.Since(info.ModTime()) > statusLineCacheTTL {
|
|
||||||
+ return ""
|
|
||||||
+ }
|
|
||||||
+ data, err := os.ReadFile(path)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return ""
|
|
||||||
+ }
|
|
||||||
+ return string(data)
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// setStatusLineCache writes status to cache file.
|
|
||||||
+func setStatusLineCache(session, status string) {
|
|
||||||
+ path := statusLineCachePath(session)
|
|
||||||
+ _ = os.WriteFile(path, []byte(status), 0644)
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
|
|
||||||
var (
|
|
||||||
statusLineSession string
|
|
||||||
@@ -34,6 +66,19 @@ func init() {
|
|
||||||
func runStatusLine(cmd *cobra.Command, args []string) error {
|
|
||||||
t := tmux.NewTmux()
|
|
||||||
|
|
||||||
+ // Optimization: skip expensive beads queries for detached sessions
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ if !t.IsSessionAttached(statusLineSession) {
|
|
||||||
+ fmt.Print("○ |")
|
|
||||||
+ return nil
|
|
||||||
+ }
|
|
||||||
+ // Check cache for attached sessions too
|
|
||||||
+ if cached := getStatusLineCache(statusLineSession); cached != "" {
|
|
||||||
+ fmt.Print(cached)
|
|
||||||
+ return nil
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
// Get session environment
|
|
||||||
var rigName, polecat, crew, issue, role string
|
|
||||||
|
|
||||||
@@ -150,7 +195,11 @@ func runWorkerStatusLine(t *tmux.Tmux, session, rigName, polecat, crew, issue st
|
|
||||||
|
|
||||||
// Output
|
|
||||||
if len(parts) > 0 {
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
@@ -389,7 +438,11 @@ func runMayorStatusLine(t *tmux.Tmux) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -458,7 +511,11 @@ func runDeaconStatusLine(t *tmux.Tmux) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -526,7 +583,11 @@ func runWitnessStatusLine(t *tmux.Tmux, rigName string) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -617,7 +678,11 @@ func runRefineryStatusLine(t *tmux.Tmux, rigName string) error {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- fmt.Print(strings.Join(parts, " | ") + " |")
|
|
||||||
+ output := strings.Join(parts, " | ") + " |"
|
|
||||||
+ if statusLineSession != "" {
|
|
||||||
+ setStatusLineCache(statusLineSession, output)
|
|
||||||
+ }
|
|
||||||
+ fmt.Print(output)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user