fix: Address golangci-lint errors (errcheck, gosec) (#76)
Apply PR #76 from dannomayernotabot: - Add golangci exclusions for internal package false positives - Tighten file permissions (0644 -> 0600) for sensitive files - Add ReadHeaderTimeout to HTTP server (slowloris prevention) - Explicit error ignoring with _ = for intentional cases - Add //nolint comments with justifications - Spelling: cancelled -> canceled (US locale) Co-Authored-By: dannomayernotabot <noreply@github.com> 🤖 Generated with Claude Code
This commit is contained in:
@@ -159,7 +159,7 @@ func parseDuration(s string) (time.Duration, error) {
|
||||
}
|
||||
|
||||
// collectGitCommits queries git log for commits by the actor.
|
||||
func collectGitCommits(townRoot, actor string, since time.Time) ([]AuditEntry, error) {
|
||||
func collectGitCommits(townRoot, actor string, since time.Time) ([]AuditEntry, error) { //nolint:unparam // error return kept for future use
|
||||
var entries []AuditEntry
|
||||
|
||||
// Build git log command
|
||||
|
||||
@@ -277,10 +277,7 @@ func runDegradedTriage(b *boot.Boot) (action, target string, err error) {
|
||||
tm := b.Tmux()
|
||||
|
||||
// Check if Deacon session exists
|
||||
deaconSession, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return "error", "deacon", fmt.Errorf("getting deacon session name: %w", err)
|
||||
}
|
||||
deaconSession := getDeaconSessionName()
|
||||
hasDeacon, err := tm.HasSession(deaconSession)
|
||||
if err != nil {
|
||||
return "error", "deacon", fmt.Errorf("checking deacon session: %w", err)
|
||||
|
||||
@@ -274,7 +274,7 @@ func classifyCallback(subject string) CallbackType {
|
||||
|
||||
// handlePolecatDone processes a POLECAT_DONE callback.
|
||||
// These come from Witnesses forwarding polecat completion notices.
|
||||
func handlePolecatDone(townRoot string, msg *mail.Message, dryRun bool) (string, error) {
|
||||
func handlePolecatDone(townRoot string, msg *mail.Message, dryRun bool) (string, error) { //nolint:unparam // error return kept for consistency with callback interface
|
||||
matches := patternPolecatDone.FindStringSubmatch(msg.Subject)
|
||||
polecatName := ""
|
||||
if len(matches) > 1 {
|
||||
@@ -306,7 +306,7 @@ func handlePolecatDone(townRoot string, msg *mail.Message, dryRun bool) (string,
|
||||
}
|
||||
|
||||
// handleMergeCompleted processes a merge completion callback from Refinery.
|
||||
func handleMergeCompleted(townRoot string, msg *mail.Message, dryRun bool) (string, error) {
|
||||
func handleMergeCompleted(townRoot string, msg *mail.Message, dryRun bool) (string, error) { //nolint:unparam // error return kept for consistency with callback interface
|
||||
matches := patternMergeCompleted.FindStringSubmatch(msg.Subject)
|
||||
branch := ""
|
||||
if len(matches) > 1 {
|
||||
@@ -353,7 +353,7 @@ func handleMergeCompleted(townRoot string, msg *mail.Message, dryRun bool) (stri
|
||||
}
|
||||
|
||||
// handleMergeRejected processes a merge rejection callback from Refinery.
|
||||
func handleMergeRejected(townRoot string, msg *mail.Message, dryRun bool) (string, error) {
|
||||
func handleMergeRejected(townRoot string, msg *mail.Message, dryRun bool) (string, error) { //nolint:unparam // error return kept for consistency with callback interface
|
||||
matches := patternMergeRejected.FindStringSubmatch(msg.Subject)
|
||||
branch := ""
|
||||
if len(matches) > 1 {
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
// generateShortID generates a short random ID (5 lowercase chars).
|
||||
func generateShortID() string {
|
||||
b := make([]byte, 3)
|
||||
rand.Read(b)
|
||||
_, _ = rand.Read(b)
|
||||
return strings.ToLower(base32.StdEncoding.EncodeToString(b)[:5])
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ func attachToTmuxSession(sessionID string) error {
|
||||
// ensureMainBranch checks if a git directory is on main branch.
|
||||
// If not, warns the user and offers to switch.
|
||||
// Returns true if on main (or switched to main), false if user declined.
|
||||
func ensureMainBranch(dir, roleName string) bool {
|
||||
func ensureMainBranch(dir, roleName string) bool { //nolint:unparam // bool return kept for future callers to check
|
||||
g := git.NewGit(dir)
|
||||
|
||||
branch, err := g.CurrentBranch()
|
||||
@@ -271,7 +271,7 @@ func parseCrewSessionName(sessionName string) (rigName, crewName string, ok bool
|
||||
|
||||
// findRigCrewSessions returns all crew sessions for a given rig, sorted alphabetically.
|
||||
// Uses tmux list-sessions to find sessions matching gt-<rig>-crew-* pattern.
|
||||
func findRigCrewSessions(rigName string) ([]string, error) {
|
||||
func findRigCrewSessions(rigName string) ([]string, error) { //nolint:unparam // error return kept for future use
|
||||
cmd := exec.Command("tmux", "list-sessions", "-F", "#{session_name}")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
|
||||
@@ -337,7 +337,7 @@ func runCrewRestart(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Set environment
|
||||
t.SetEnvironment(sessionID, "GT_ROLE", "crew")
|
||||
_ = t.SetEnvironment(sessionID, "GT_ROLE", "crew")
|
||||
// Apply rig-based theming (non-fatal: theming failure doesn't affect operation)
|
||||
theme := getThemeForRig(r.Name)
|
||||
_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")
|
||||
@@ -596,7 +596,7 @@ func runCrewStop(cmd *cobra.Command, args []string) error {
|
||||
if townRoot != "" {
|
||||
agent := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
logger := townlog.NewLogger(townRoot)
|
||||
logger.Log(townlog.EventKill, agent, "gt crew stop")
|
||||
_ = logger.Log(townlog.EventKill, agent, "gt crew stop")
|
||||
}
|
||||
|
||||
// Log captured output (truncated)
|
||||
@@ -682,7 +682,7 @@ func runCrewStopAll() error {
|
||||
townRoot, _ := workspace.FindFromCwd()
|
||||
if townRoot != "" {
|
||||
logger := townlog.NewLogger(townRoot)
|
||||
logger.Log(townlog.EventKill, agentName, "gt crew stop --all")
|
||||
_ = logger.Log(townlog.EventKill, agentName, "gt crew stop --all")
|
||||
}
|
||||
|
||||
// Log captured output (truncated)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/web"
|
||||
@@ -67,11 +68,16 @@ func runDashboard(cmd *cobra.Command, args []string) error {
|
||||
go openBrowser(url)
|
||||
}
|
||||
|
||||
// Start the server
|
||||
// Start the server with timeouts
|
||||
fmt.Printf("🚚 Gas Town Dashboard starting at %s\n", url)
|
||||
fmt.Printf(" Press Ctrl+C to stop\n")
|
||||
|
||||
return http.ListenAndServe(fmt.Sprintf(":%d", dashboardPort), handler)
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", dashboardPort),
|
||||
Handler: handler,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
// openBrowser opens the specified URL in the default browser.
|
||||
|
||||
+9
-24
@@ -22,8 +22,8 @@ import (
|
||||
)
|
||||
|
||||
// getDeaconSessionName returns the Deacon session name.
|
||||
func getDeaconSessionName() (string, error) {
|
||||
return session.DeaconSessionName(), nil
|
||||
func getDeaconSessionName() string {
|
||||
return session.DeaconSessionName()
|
||||
}
|
||||
|
||||
var deaconCmd = &cobra.Command{
|
||||
@@ -276,10 +276,7 @@ func init() {
|
||||
func runDeaconStart(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getDeaconSessionName()
|
||||
|
||||
// Check if session already exists
|
||||
running, err := t.HasSession(sessionName)
|
||||
@@ -370,10 +367,7 @@ func startDeaconSession(t *tmux.Tmux, sessionName string) error {
|
||||
func runDeaconStop(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getDeaconSessionName()
|
||||
|
||||
// Check if session exists
|
||||
running, err := t.HasSession(sessionName)
|
||||
@@ -402,10 +396,7 @@ func runDeaconStop(cmd *cobra.Command, args []string) error {
|
||||
func runDeaconAttach(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getDeaconSessionName()
|
||||
|
||||
// Check if session exists
|
||||
running, err := t.HasSession(sessionName)
|
||||
@@ -428,10 +419,7 @@ func runDeaconAttach(cmd *cobra.Command, args []string) error {
|
||||
func runDeaconStatus(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getDeaconSessionName()
|
||||
|
||||
running, err := t.HasSession(sessionName)
|
||||
if err != nil {
|
||||
@@ -470,10 +458,7 @@ func runDeaconStatus(cmd *cobra.Command, args []string) error {
|
||||
func runDeaconRestart(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getDeaconSessionName()
|
||||
|
||||
running, err := t.HasSession(sessionName)
|
||||
if err != nil {
|
||||
@@ -1092,7 +1077,7 @@ func getPolecatStaleness(polecatPath string) time.Duration {
|
||||
}
|
||||
|
||||
// nukeZombie cleans up a zombie polecat.
|
||||
func nukeZombie(townRoot string, z zombieInfo, t *tmux.Tmux) error {
|
||||
func nukeZombie(townRoot string, z zombieInfo, t *tmux.Tmux) error { //nolint:unparam // error return kept for future use
|
||||
// Step 1: Kill tmux session if somehow still exists
|
||||
if exists, _ := t.HasSession(z.sessionName); exists {
|
||||
_ = t.KillSession(z.sessionName)
|
||||
@@ -1204,7 +1189,7 @@ func sendMail(townRoot, to, subject, body string) {
|
||||
}
|
||||
|
||||
// updateAgentBeadState updates an agent bead's state.
|
||||
func updateAgentBeadState(townRoot, agent, state, reason string) {
|
||||
func updateAgentBeadState(townRoot, agent, state, _ string) { // reason unused but kept for API consistency
|
||||
beadID, _, err := agentAddressToIDs(agent)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -326,7 +326,7 @@ func runDone(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Log done event (townlog and activity feed)
|
||||
LogDone(townRoot, sender, issueID)
|
||||
_ = LogDone(townRoot, sender, issueID)
|
||||
_ = events.LogFeed(events.TypeDone, sender, events.DonePayload(issueID, branch))
|
||||
|
||||
// Update agent bead state (ZFC: self-report completion)
|
||||
@@ -352,7 +352,7 @@ func runDone(cmd *cobra.Command, args []string) error {
|
||||
// - PHASE_COMPLETE → "awaiting-gate"
|
||||
//
|
||||
// Also self-reports cleanup_status for ZFC compliance (#10).
|
||||
func updateAgentStateOnDone(cwd, townRoot, exitType, issueID string) {
|
||||
func updateAgentStateOnDone(cwd, townRoot, exitType, _ string) { // issueID unused but kept for future audit logging
|
||||
// Get role context
|
||||
roleInfo, err := GetRoleWithContext(cwd, townRoot)
|
||||
if err != nil {
|
||||
|
||||
@@ -74,8 +74,8 @@ func runDown(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Get session names
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
|
||||
// 2. Stop Mayor
|
||||
if err := stopSession(t, mayorSession); err != nil {
|
||||
|
||||
@@ -226,7 +226,7 @@ func runFeedTUI(workDir string) error {
|
||||
|
||||
// Combine all sources
|
||||
multiSource := feed.NewMultiSource(sources...)
|
||||
defer multiSource.Close()
|
||||
defer func() { _ = multiSource.Close() }()
|
||||
|
||||
// Create model and connect event source
|
||||
m := feed.NewModel()
|
||||
@@ -305,7 +305,7 @@ func runFeedInWindow(workDir string, bdArgs []string) error {
|
||||
|
||||
// windowExists checks if a window with the given name exists in the session.
|
||||
// Note: getCurrentTmuxSession is defined in handoff.go
|
||||
func windowExists(t *tmux.Tmux, session, windowName string) (bool, error) {
|
||||
func windowExists(_ *tmux.Tmux, session, windowName string) (bool, error) { // t unused: direct exec for simplicity
|
||||
cmd := exec.Command("tmux", "list-windows", "-t", session, "-F", "#{window_name}")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -321,14 +321,14 @@ func windowExists(t *tmux.Tmux, session, windowName string) (bool, error) {
|
||||
}
|
||||
|
||||
// createWindow creates a new tmux window with the given name and command.
|
||||
func createWindow(t *tmux.Tmux, session, windowName, workDir, command string) error {
|
||||
func createWindow(_ *tmux.Tmux, session, windowName, workDir, command string) error { // t unused: direct exec for simplicity
|
||||
args := []string{"new-window", "-t", session, "-n", windowName, "-c", workDir, command}
|
||||
cmd := exec.Command("tmux", args...)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// selectWindow switches to the specified window.
|
||||
func selectWindow(t *tmux.Tmux, target string) error {
|
||||
func selectWindow(_ *tmux.Tmux, target string) error { // t unused: direct exec for simplicity
|
||||
cmd := exec.Command("tmux", "select-window", "-t", target)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
@@ -683,7 +683,7 @@ func extractPrompts(content string) map[string]string {
|
||||
// generateFormulaShortID generates a short random ID (5 lowercase chars)
|
||||
func generateFormulaShortID() string {
|
||||
b := make([]byte, 3)
|
||||
rand.Read(b)
|
||||
_, _ = rand.Read(b)
|
||||
return strings.ToLower(base32.StdEncoding.EncodeToString(b)[:5])
|
||||
}
|
||||
|
||||
|
||||
+5
-13
@@ -155,7 +155,7 @@ func runHandoff(cmd *cobra.Command, args []string) error {
|
||||
if agent == "" {
|
||||
agent = currentSession
|
||||
}
|
||||
LogHandoff(townRoot, agent, handoffSubject)
|
||||
_ = LogHandoff(townRoot, agent, handoffSubject)
|
||||
// Also log to activity feed
|
||||
_ = events.LogFeed(events.TypeHandoff, agent, events.HandoffPayload(handoffSubject, true))
|
||||
}
|
||||
@@ -230,18 +230,10 @@ func resolveRoleToSession(role string) (string, error) {
|
||||
|
||||
switch strings.ToLower(role) {
|
||||
case "mayor", "may":
|
||||
mayorSession, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot determine mayor session name: %w", err)
|
||||
}
|
||||
return mayorSession, nil
|
||||
return getMayorSessionName(), nil
|
||||
|
||||
case "deacon", "dea":
|
||||
deaconSession, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot determine deacon session name: %w", err)
|
||||
}
|
||||
return deaconSession, nil
|
||||
return getDeaconSessionName(), nil
|
||||
|
||||
case "crew":
|
||||
// Try to get rig and crew name from environment or cwd
|
||||
@@ -369,8 +361,8 @@ func buildRestartCommand(sessionName string) (string, error) {
|
||||
// This is the canonical home for each role type.
|
||||
func sessionWorkDir(sessionName, townRoot string) (string, error) {
|
||||
// Get session names for comparison
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
|
||||
switch {
|
||||
case sessionName == mayorSession:
|
||||
|
||||
@@ -114,7 +114,7 @@ func runHookOrStatus(cmd *cobra.Command, args []string) error {
|
||||
return runHook(cmd, args)
|
||||
}
|
||||
|
||||
func runHook(cmd *cobra.Command, args []string) error {
|
||||
func runHook(_ *cobra.Command, args []string) error {
|
||||
beadID := args[0]
|
||||
|
||||
// Polecats cannot hook - they use gt done for lifecycle
|
||||
|
||||
@@ -122,10 +122,7 @@ func detectCurrentSession() string {
|
||||
|
||||
// Check if we're mayor
|
||||
if os.Getenv("GT_ROLE") == "mayor" {
|
||||
mayorSession, err := getMayorSessionName()
|
||||
if err == nil {
|
||||
return mayorSession
|
||||
}
|
||||
return getMayorSessionName()
|
||||
}
|
||||
|
||||
return ""
|
||||
|
||||
@@ -433,7 +433,7 @@ func init() {
|
||||
// Reply flags
|
||||
mailReplyCmd.Flags().StringVarP(&mailReplySubject, "subject", "s", "", "Override reply subject (default: Re: <original>)")
|
||||
mailReplyCmd.Flags().StringVarP(&mailReplyMessage, "message", "m", "", "Reply message body (required)")
|
||||
mailReplyCmd.MarkFlagRequired("message")
|
||||
_ = mailReplyCmd.MarkFlagRequired("message")
|
||||
|
||||
// Search flags
|
||||
mailSearchCmd.Flags().StringVar(&mailSearchFrom, "from", "", "Filter by sender address")
|
||||
|
||||
+7
-22
@@ -15,8 +15,8 @@ import (
|
||||
)
|
||||
|
||||
// getMayorSessionName returns the Mayor session name.
|
||||
func getMayorSessionName() (string, error) {
|
||||
return session.MayorSessionName(), nil
|
||||
func getMayorSessionName() string {
|
||||
return session.MayorSessionName()
|
||||
}
|
||||
|
||||
var mayorCmd = &cobra.Command{
|
||||
@@ -90,10 +90,7 @@ func init() {
|
||||
func runMayorStart(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getMayorSessionName()
|
||||
|
||||
// Check if session already exists
|
||||
running, err := t.HasSession(sessionName)
|
||||
@@ -172,10 +169,7 @@ func startMayorSession(t *tmux.Tmux, sessionName string) error {
|
||||
func runMayorStop(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getMayorSessionName()
|
||||
|
||||
// Check if session exists
|
||||
running, err := t.HasSession(sessionName)
|
||||
@@ -204,10 +198,7 @@ func runMayorStop(cmd *cobra.Command, args []string) error {
|
||||
func runMayorAttach(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getMayorSessionName()
|
||||
|
||||
// Check if session exists
|
||||
running, err := t.HasSession(sessionName)
|
||||
@@ -229,10 +220,7 @@ func runMayorAttach(cmd *cobra.Command, args []string) error {
|
||||
func runMayorStatus(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getMayorSessionName()
|
||||
|
||||
running, err := t.HasSession(sessionName)
|
||||
if err != nil {
|
||||
@@ -271,10 +259,7 @@ func runMayorStatus(cmd *cobra.Command, args []string) error {
|
||||
func runMayorRestart(cmd *cobra.Command, args []string) error {
|
||||
t := tmux.NewTmux()
|
||||
|
||||
sessionName, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sessionName := getMayorSessionName()
|
||||
|
||||
running, err := t.HasSession(sessionName)
|
||||
if err != nil {
|
||||
|
||||
@@ -235,7 +235,7 @@ func calculateEffectiveTimeout(idleCycles int) (time.Duration, error) {
|
||||
}
|
||||
|
||||
// waitForActivitySignal starts bd activity --follow and waits for any output.
|
||||
// Returns immediately when a line is received, or when context is cancelled.
|
||||
// Returns immediately when a line is received, or when context is canceled.
|
||||
func waitForActivitySignal(ctx context.Context, workDir string) (*AwaitSignalResult, error) {
|
||||
// Start bd activity --follow
|
||||
cmd := exec.CommandContext(ctx, "bd", "activity", "--follow")
|
||||
|
||||
@@ -250,7 +250,7 @@ func findNextReadyStep(b *beads.Beads, moleculeID string) (*beads.Issue, bool, e
|
||||
}
|
||||
|
||||
// handleStepContinue handles continuing to the next step.
|
||||
func handleStepContinue(cwd, townRoot, workDir string, nextStep *beads.Issue, dryRun bool) error {
|
||||
func handleStepContinue(cwd, townRoot, _ string, nextStep *beads.Issue, dryRun bool) error { // workDir unused but kept for signature consistency
|
||||
fmt.Printf("\n%s Next step: %s\n", style.Bold.Render("→"), nextStep.ID)
|
||||
fmt.Printf(" %s\n", nextStep.Title)
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ func runNudge(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Log nudge event
|
||||
if townRoot, err := workspace.FindFromCwd(); err == nil && townRoot != "" {
|
||||
LogNudge(townRoot, "deacon", message)
|
||||
_ = LogNudge(townRoot, "deacon", message)
|
||||
}
|
||||
_ = events.LogFeed(events.TypeNudge, sender, events.NudgePayload("", "deacon", message))
|
||||
return nil
|
||||
@@ -202,7 +202,7 @@ func runNudge(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Log nudge event
|
||||
if townRoot, err := workspace.FindFromCwd(); err == nil && townRoot != "" {
|
||||
LogNudge(townRoot, target, message)
|
||||
_ = LogNudge(townRoot, target, message)
|
||||
}
|
||||
_ = events.LogFeed(events.TypeNudge, sender, events.NudgePayload(rigName, target, message))
|
||||
} else {
|
||||
@@ -223,7 +223,7 @@ func runNudge(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Log nudge event
|
||||
if townRoot, err := workspace.FindFromCwd(); err == nil && townRoot != "" {
|
||||
LogNudge(townRoot, target, message)
|
||||
_ = LogNudge(townRoot, target, message)
|
||||
}
|
||||
_ = events.LogFeed(events.TypeNudge, sender, events.NudgePayload("", target, message))
|
||||
}
|
||||
@@ -424,7 +424,7 @@ func resolveNudgePattern(pattern string, agents []*AgentSession) []string {
|
||||
// Returns (shouldSend bool, level string, err error).
|
||||
// If force is true, always returns true.
|
||||
// If the agent bead cannot be found, returns true (fail-open for backward compatibility).
|
||||
func shouldNudgeTarget(townRoot, targetAddress string, force bool) (bool, string, error) {
|
||||
func shouldNudgeTarget(townRoot, targetAddress string, force bool) (bool, string, error) { //nolint:unparam // error return kept for future use
|
||||
if force {
|
||||
return true, "", nil
|
||||
}
|
||||
|
||||
@@ -82,15 +82,15 @@ func cyclePolecatSession(direction int, sessionOverride string) error {
|
||||
// parsePolecatSessionName extracts rig and polecat name from a tmux session name.
|
||||
// Format: gt-<rig>-<name> where name is NOT crew-*, witness, or refinery.
|
||||
// Returns empty strings and false if the format doesn't match.
|
||||
func parsePolecatSessionName(sessionName string) (rigName, polecatName string, ok bool) {
|
||||
func parsePolecatSessionName(sessionName string) (rigName, polecatName string, ok bool) { //nolint:unparam // polecatName kept for API consistency
|
||||
// Must start with "gt-"
|
||||
if !strings.HasPrefix(sessionName, "gt-") {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
// Exclude town-level sessions by exact match
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
if sessionName == mayorSession || sessionName == deaconSession {
|
||||
return "", "", false
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func parsePolecatSessionName(sessionName string) (rigName, polecatName string, o
|
||||
// findRigPolecatSessions returns all polecat sessions for a given rig.
|
||||
// Uses tmux list-sessions to find sessions matching gt-<rig>-<name> pattern,
|
||||
// excluding crew, witness, and refinery sessions.
|
||||
func findRigPolecatSessions(rigName string) ([]string, error) {
|
||||
func findRigPolecatSessions(rigName string) ([]string, error) { //nolint:unparam // error return kept for future use
|
||||
cmd := exec.Command("tmux", "list-sessions", "-F", "#{session_name}")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
|
||||
@@ -101,8 +101,8 @@ func runPrime(cmd *cobra.Command, args []string) error {
|
||||
persistSessionID(cwd, sessionID)
|
||||
}
|
||||
// Set environment for this process (affects event emission below)
|
||||
os.Setenv("GT_SESSION_ID", sessionID)
|
||||
os.Setenv("CLAUDE_SESSION_ID", sessionID) // Legacy compatibility
|
||||
_ = os.Setenv("GT_SESSION_ID", sessionID)
|
||||
_ = os.Setenv("CLAUDE_SESSION_ID", sessionID) // Legacy compatibility
|
||||
// Output session beacon
|
||||
fmt.Printf("[session:%s]\n", sessionID)
|
||||
if source != "" {
|
||||
@@ -1564,7 +1564,7 @@ func emitSessionEvent(ctx RoleContext) {
|
||||
|
||||
// Emit the event
|
||||
payload := events.SessionPayload(sessionID, actor, topic, ctx.WorkDir)
|
||||
events.LogFeed(events.TypeSessionStart, actor, payload)
|
||||
_ = events.LogFeed(events.TypeSessionStart, actor, payload)
|
||||
}
|
||||
|
||||
// outputSessionMetadata prints a structured metadata line for seance discovery.
|
||||
|
||||
@@ -278,7 +278,7 @@ func runSessionStart(cmd *cobra.Command, args []string) error {
|
||||
if townRoot, err := workspace.FindFromCwd(); err == nil && townRoot != "" {
|
||||
agent := fmt.Sprintf("%s/%s", rigName, polecatName)
|
||||
logger := townlog.NewLogger(townRoot)
|
||||
logger.Log(townlog.EventWake, agent, sessionIssue)
|
||||
_ = logger.Log(townlog.EventWake, agent, sessionIssue)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -314,7 +314,7 @@ func runSessionStop(cmd *cobra.Command, args []string) error {
|
||||
reason = "gt session stop --force"
|
||||
}
|
||||
logger := townlog.NewLogger(townRoot)
|
||||
logger.Log(townlog.EventKill, agent, reason)
|
||||
_ = logger.Log(townlog.EventKill, agent, reason)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -904,7 +904,7 @@ func runSlingFormula(args []string) error {
|
||||
// requires cross-database access (agent in rig db, hook bead in town db), but
|
||||
// bd slot set has a bug where it doesn't support this. See BD_BUG_AGENT_STATE_ROUTING.md.
|
||||
// The work is still correctly attached via `bd update <bead> --assignee=<agent>`.
|
||||
func updateAgentHookBead(agentID, beadID, workDir, townBeadsDir string) {
|
||||
func updateAgentHookBead(agentID, _, workDir, townBeadsDir string) { // beadID unused due to BD_BUG_AGENT_STATE_ROUTING
|
||||
_ = townBeadsDir // Not used - BEADS_DIR breaks redirect mechanism
|
||||
|
||||
// Convert agent ID to agent bead ID
|
||||
@@ -1166,7 +1166,7 @@ func generateDogName(mgr *dog.Manager) string {
|
||||
// slingGenerateShortID generates a short random ID (5 lowercase chars).
|
||||
func slingGenerateShortID() string {
|
||||
b := make([]byte, 3)
|
||||
rand.Read(b)
|
||||
_, _ = rand.Read(b)
|
||||
return strings.ToLower(base32.StdEncoding.EncodeToString(b)[:5])
|
||||
}
|
||||
|
||||
|
||||
+15
-21
@@ -179,14 +179,8 @@ func runStart(cmd *cobra.Command, args []string) error {
|
||||
// startCoreAgents starts Mayor and Deacon sessions.
|
||||
func startCoreAgents(t *tmux.Tmux) error {
|
||||
// Get session names
|
||||
mayorSession, err := getMayorSessionName()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting Mayor session name: %w", err)
|
||||
}
|
||||
deaconSession, err := getDeaconSessionName()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting Deacon session name: %w", err)
|
||||
}
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
|
||||
// Start Mayor first (so Deacon sees it as up)
|
||||
mayorRunning, _ := t.HasSession(mayorSession)
|
||||
@@ -335,15 +329,15 @@ func ensureRefinerySession(rigName string, r *rig.Rig) (bool, error) {
|
||||
|
||||
// Set environment
|
||||
bdActor := fmt.Sprintf("%s/refinery", rigName)
|
||||
t.SetEnvironment(sessionName, "GT_ROLE", "refinery")
|
||||
t.SetEnvironment(sessionName, "GT_RIG", rigName)
|
||||
t.SetEnvironment(sessionName, "BD_ACTOR", bdActor)
|
||||
_ = t.SetEnvironment(sessionName, "GT_ROLE", "refinery")
|
||||
_ = t.SetEnvironment(sessionName, "GT_RIG", rigName)
|
||||
_ = t.SetEnvironment(sessionName, "BD_ACTOR", bdActor)
|
||||
|
||||
// Set beads environment
|
||||
beadsDir := filepath.Join(r.Path, "mayor", "rig", ".beads")
|
||||
t.SetEnvironment(sessionName, "BEADS_DIR", beadsDir)
|
||||
t.SetEnvironment(sessionName, "BEADS_NO_DAEMON", "1")
|
||||
t.SetEnvironment(sessionName, "BEADS_AGENT_NAME", fmt.Sprintf("%s/refinery", rigName))
|
||||
_ = t.SetEnvironment(sessionName, "BEADS_DIR", beadsDir)
|
||||
_ = t.SetEnvironment(sessionName, "BEADS_NO_DAEMON", "1")
|
||||
_ = t.SetEnvironment(sessionName, "BEADS_AGENT_NAME", fmt.Sprintf("%s/refinery", rigName))
|
||||
|
||||
// Apply Gas Town theming (non-fatal: theming failure doesn't affect operation)
|
||||
theme := tmux.AssignTheme(rigName)
|
||||
@@ -391,8 +385,8 @@ func runShutdown(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Get session names for categorization
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
toStop, preserved := categorizeSessions(sessions, mayorSession, deaconSession)
|
||||
|
||||
if len(toStop) == 0 {
|
||||
@@ -421,7 +415,7 @@ func runShutdown(cmd *cobra.Command, args []string) error {
|
||||
response, _ := reader.ReadString('\n')
|
||||
response = strings.TrimSpace(strings.ToLower(response))
|
||||
if response != "y" && response != "yes" {
|
||||
fmt.Println("Shutdown cancelled.")
|
||||
fmt.Println("Shutdown canceled.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -515,8 +509,8 @@ func runGracefulShutdown(t *tmux.Tmux, gtSessions []string, townRoot string) err
|
||||
|
||||
// Phase 4: Kill sessions in correct order
|
||||
fmt.Printf("\nPhase 4: Terminating sessions...\n")
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
stopped := killSessionsInOrder(t, gtSessions, mayorSession, deaconSession)
|
||||
|
||||
// Phase 5: Cleanup polecat worktrees and branches
|
||||
@@ -533,8 +527,8 @@ func runGracefulShutdown(t *tmux.Tmux, gtSessions []string, townRoot string) err
|
||||
func runImmediateShutdown(t *tmux.Tmux, gtSessions []string, townRoot string) error {
|
||||
fmt.Println("Shutting down Gas Town...")
|
||||
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
stopped := killSessionsInOrder(t, gtSessions, mayorSession, deaconSession)
|
||||
|
||||
// Cleanup polecat worktrees and branches
|
||||
|
||||
@@ -469,7 +469,7 @@ func outputStatusText(status TownStatus) error {
|
||||
}
|
||||
|
||||
// renderAgentDetails renders full agent bead details
|
||||
func renderAgentDetails(agent AgentRuntime, indent string, hooks []AgentHookInfo, townRoot string) {
|
||||
func renderAgentDetails(agent AgentRuntime, indent string, hooks []AgentHookInfo, townRoot string) { //nolint:unparam // indent kept for future customization
|
||||
// Line 1: Agent bead ID + status
|
||||
// Reconcile bead state with tmux session state to surface mismatches
|
||||
// States: "running" (active), "idle" (waiting), "stopped", "dead", etc.
|
||||
@@ -646,8 +646,8 @@ func discoverRigHooks(r *rig.Rig, crews []string) []AgentHookInfo {
|
||||
// allHookBeads is a preloaded map of hook beads for O(1) lookup.
|
||||
func discoverGlobalAgents(allSessions map[string]bool, allAgentBeads map[string]*beads.Issue, allHookBeads map[string]*beads.Issue, mailRouter *mail.Router, skipMail bool) []AgentRuntime {
|
||||
// Get session names dynamically
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
|
||||
// Define agents to discover
|
||||
agentDefs := []struct {
|
||||
|
||||
@@ -53,8 +53,8 @@ func runStatusLine(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Get session names for comparison
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
|
||||
// Determine identity and output based on role
|
||||
if role == "mayor" || statusLineSession == mayorSession {
|
||||
@@ -164,7 +164,7 @@ func runMayorStatusLine(t *tmux.Tmux) error {
|
||||
|
||||
// Get town root from mayor pane's working directory
|
||||
var townRoot string
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
paneDir, err := t.GetPaneWorkDir(mayorSession)
|
||||
if err == nil && paneDir != "" {
|
||||
townRoot, _ = workspace.Find(paneDir)
|
||||
@@ -241,7 +241,7 @@ func runDeaconStatusLine(t *tmux.Tmux) error {
|
||||
|
||||
// Get town root from deacon pane's working directory
|
||||
var townRoot string
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
paneDir, err := t.GetPaneWorkDir(deaconSession)
|
||||
if err == nil && paneDir != "" {
|
||||
townRoot, _ = workspace.Find(paneDir)
|
||||
|
||||
@@ -146,7 +146,7 @@ func runStop(cmd *cobra.Command, args []string) error {
|
||||
// Log kill event
|
||||
agent := fmt.Sprintf("%s/%s", r.Name, info.Polecat)
|
||||
logger := townlog.NewLogger(townRoot)
|
||||
logger.Log(townlog.EventKill, agent, "gt stop")
|
||||
_ = logger.Log(townlog.EventKill, agent, "gt stop")
|
||||
|
||||
// Log kill event to activity feed
|
||||
_ = events.LogFeed(events.TypeKill, "gt", events.KillPayload(r.Name, info.Polecat, "gt stop"))
|
||||
|
||||
@@ -114,7 +114,7 @@ var swarmCancelCmd = &cobra.Command{
|
||||
Short: "Cancel a swarm",
|
||||
Long: `Cancel an active swarm.
|
||||
|
||||
Marks the swarm as cancelled and optionally cleans up branches.`,
|
||||
Marks the swarm as canceled and optionally cleans up branches.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runSwarmCancel,
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func init() {
|
||||
swarmStatusCmd.Flags().BoolVar(&swarmStatusJSON, "json", false, "Output as JSON")
|
||||
|
||||
// List flags
|
||||
swarmListCmd.Flags().StringVar(&swarmListStatus, "status", "", "Filter by status (active, landed, cancelled, failed)")
|
||||
swarmListCmd.Flags().StringVar(&swarmListStatus, "status", "", "Filter by status (active, landed, canceled, failed)")
|
||||
swarmListCmd.Flags().BoolVar(&swarmListJSON, "json", false, "Output as JSON")
|
||||
|
||||
// Dispatch flags
|
||||
@@ -526,7 +526,7 @@ func runSwarmDispatch(cmd *cobra.Command, args []string) error {
|
||||
func spawnSwarmWorkersFromBeads(r *rig.Rig, townRoot string, swarmID string, workers []string, tasks []struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
}) error {
|
||||
}) error { //nolint:unparam // error return kept for future use
|
||||
t := tmux.NewTmux()
|
||||
sessMgr := session.NewManager(t, r)
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
@@ -866,8 +866,8 @@ func runSwarmCancel(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Close the swarm epic in beads with cancelled reason
|
||||
closeArgs := []string{"close", swarmID, "--reason", "Swarm cancelled"}
|
||||
// Close the swarm epic in beads with canceled reason
|
||||
closeArgs := []string{"close", swarmID, "--reason", "Swarm canceled"}
|
||||
if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" {
|
||||
closeArgs = append(closeArgs, "--session="+sessionID)
|
||||
}
|
||||
@@ -877,7 +877,7 @@ func runSwarmCancel(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("closing swarm: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s Swarm %s cancelled\n", style.Bold.Render("✓"), swarmID)
|
||||
fmt.Printf("%s Swarm %s canceled\n", style.Bold.Render("✓"), swarmID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -408,7 +408,7 @@ func getConvoyMeta(convoyID string) (*ConvoyMeta, error) {
|
||||
}
|
||||
|
||||
// collectLegOutputs gathers outputs from all convoy legs.
|
||||
func collectLegOutputs(meta *ConvoyMeta, f *formula.Formula) ([]LegOutput, bool, error) {
|
||||
func collectLegOutputs(meta *ConvoyMeta, f *formula.Formula) ([]LegOutput, bool, error) { //nolint:unparam // error return kept for future use
|
||||
var outputs []LegOutput
|
||||
allComplete := true
|
||||
|
||||
|
||||
@@ -15,13 +15,9 @@ import (
|
||||
var townCycleSession string
|
||||
|
||||
// getTownLevelSessions returns the town-level session names for the current workspace.
|
||||
// Returns empty slice if workspace cannot be determined.
|
||||
func getTownLevelSessions() []string {
|
||||
mayorSession, errMayor := getMayorSessionName()
|
||||
deaconSession, errDeacon := getDeaconSessionName()
|
||||
if errMayor != nil || errDeacon != nil {
|
||||
return nil
|
||||
}
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
return []string{mayorSession, deaconSession}
|
||||
}
|
||||
|
||||
@@ -35,8 +31,8 @@ func isTownLevelSession(sessionName string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
_ = townName // used for session name generation
|
||||
return sessionName == mayorSession || sessionName == deaconSession
|
||||
}
|
||||
|
||||
+2
-2
@@ -80,8 +80,8 @@ func runUp(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Get session names
|
||||
deaconSession, _ := getDeaconSessionName()
|
||||
mayorSession, _ := getMayorSessionName()
|
||||
deaconSession := getDeaconSessionName()
|
||||
mayorSession := getMayorSessionName()
|
||||
|
||||
// 2. Deacon (Claude agent)
|
||||
if err := ensureSession(t, deaconSession, townRoot, "deacon"); err != nil {
|
||||
|
||||
@@ -333,9 +333,9 @@ func ensureWitnessSession(rigName string, r *rig.Rig) (bool, error) {
|
||||
|
||||
// Set environment
|
||||
bdActor := fmt.Sprintf("%s/witness", rigName)
|
||||
t.SetEnvironment(sessionName, "GT_ROLE", "witness")
|
||||
t.SetEnvironment(sessionName, "GT_RIG", rigName)
|
||||
t.SetEnvironment(sessionName, "BD_ACTOR", bdActor)
|
||||
_ = t.SetEnvironment(sessionName, "GT_ROLE", "witness")
|
||||
_ = t.SetEnvironment(sessionName, "GT_RIG", rigName)
|
||||
_ = t.SetEnvironment(sessionName, "BD_ACTOR", bdActor)
|
||||
|
||||
// Apply Gas Town theming (non-fatal: theming failure doesn't affect operation)
|
||||
theme := tmux.AssignTheme(rigName)
|
||||
|
||||
Reference in New Issue
Block a user