Fix: /handoff regression - use respawn-pane with direct claude command
Three related fixes: 1. lifecycle.go: Use gt mail delete instead of gt mail read to prevent lifecycle requests from accumulating. 2. handoff.go: Return exec claude command for respawn-pane instead of gt crew at which tries to attach to existing session. 3. handoff.md skill: Work without --cycle and -m flags, send mail separately. 🤖 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,6 @@
|
||||
---
|
||||
description: Hand off to fresh session, work continues from hook
|
||||
allowed-tools: Bash(gt handoff:*)
|
||||
allowed-tools: Bash(gt mail send:*),Bash(gt handoff:*)
|
||||
argument-hint: [message]
|
||||
---
|
||||
|
||||
@@ -8,8 +8,14 @@ Hand off to a fresh session.
|
||||
|
||||
User's handoff message (if any): $ARGUMENTS
|
||||
|
||||
Execute the appropriate command:
|
||||
- If user provided a message: `gt handoff --cycle -m "USER_MESSAGE_HERE"`
|
||||
- If no message provided: `gt handoff --cycle`
|
||||
Execute these steps in order:
|
||||
|
||||
1. If user provided a message, send handoff mail to yourself first.
|
||||
Construct your mail address from your identity (e.g., gastown/crew/max for crew, mayor/ for mayor).
|
||||
Example: `gt mail send gastown/crew/max -s "🤝 HANDOFF: Session cycling" -m "USER_MESSAGE_HERE"`
|
||||
|
||||
2. Run the handoff command (this will respawn your session with a fresh Claude):
|
||||
`gt handoff`
|
||||
|
||||
Note: The new session will auto-prime via the SessionStart hook and find your handoff mail.
|
||||
End watch. A new session takes over, picking up any molecule on the hook.
|
||||
|
||||
@@ -152,27 +152,28 @@ func resolveRoleToSession(role string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// buildRestartCommand creates the gt command to restart a session.
|
||||
// buildRestartCommand creates the command to run when respawning a session's pane.
|
||||
// This needs to be the actual command to execute (e.g., claude), not a session attach command.
|
||||
func buildRestartCommand(sessionName string) (string, error) {
|
||||
// For respawn-pane, we run claude directly. The SessionStart hook will run gt prime.
|
||||
// Use exec to ensure clean process replacement.
|
||||
claudeCmd := "exec claude --dangerously-skip-permissions"
|
||||
|
||||
switch {
|
||||
case sessionName == "gt-mayor":
|
||||
return "gt may at", nil
|
||||
return claudeCmd, nil
|
||||
|
||||
case sessionName == "gt-deacon":
|
||||
return "gt dea at", nil
|
||||
return claudeCmd, nil
|
||||
|
||||
case strings.Contains(sessionName, "-crew-"):
|
||||
// gt-<rig>-crew-<name>
|
||||
// The attach command can auto-detect from cwd, so just use `gt crew at`
|
||||
return "gt crew at", nil
|
||||
return claudeCmd, nil
|
||||
|
||||
case strings.HasSuffix(sessionName, "-witness"):
|
||||
// gt-<rig>-witness
|
||||
return "gt wit at", nil
|
||||
return claudeCmd, nil
|
||||
|
||||
case strings.HasSuffix(sessionName, "-refinery"):
|
||||
// gt-<rig>-refinery
|
||||
return "gt ref at", nil
|
||||
return claudeCmd, nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unknown session type: %s (try specifying role explicitly)", sessionName)
|
||||
|
||||
@@ -300,10 +300,12 @@ func (d *Daemon) syncWorkspace(workDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// closeMessage marks a mail message as read by closing the beads issue.
|
||||
// closeMessage removes a lifecycle mail message after processing.
|
||||
// We use delete instead of read because gt mail read intentionally
|
||||
// doesn't mark messages as read (to preserve handoff messages).
|
||||
func (d *Daemon) closeMessage(id string) error {
|
||||
// Use gt mail commands for town-level beads
|
||||
cmd := exec.Command("gt", "mail", "read", id)
|
||||
// Use gt mail delete to actually remove the message
|
||||
cmd := exec.Command("gt", "mail", "delete", id)
|
||||
cmd.Dir = d.config.TownRoot
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user