feat(nudge): Add gt nudge command for reliable Claude session messaging
Encapsulates tmux send-keys with: literal mode, 500ms debounce, separate Enter. Tested and reliable. Updates spawn.go to use NudgeSession. Related: gt-1hf, gt-lz2 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
52
internal/cmd/nudge.go
Normal file
52
internal/cmd/nudge.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(nudgeCmd)
|
||||
}
|
||||
|
||||
var nudgeCmd = &cobra.Command{
|
||||
Use: "nudge <session> <message>",
|
||||
Short: "Send a message to a Claude session reliably",
|
||||
Long: `Sends a message to a tmux session running Claude Code.
|
||||
|
||||
Uses a reliable delivery pattern:
|
||||
1. Sends text in literal mode (-l flag)
|
||||
2. Waits 500ms for paste to complete
|
||||
3. Sends Enter as a separate command
|
||||
|
||||
This is the ONLY way to send messages to Claude sessions.
|
||||
Do not use raw tmux send-keys elsewhere.`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runNudge,
|
||||
}
|
||||
|
||||
func runNudge(cmd *cobra.Command, args []string) error {
|
||||
session := args[0]
|
||||
message := args[1]
|
||||
|
||||
t := tmux.NewTmux()
|
||||
|
||||
// Verify session exists
|
||||
exists, err := t.HasSession(session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking session: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("session %q not found", session)
|
||||
}
|
||||
|
||||
// Send message with reliable pattern
|
||||
if err := t.NudgeSession(session, message); err != nil {
|
||||
return fmt.Errorf("nudging session: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Nudged %s\n", session)
|
||||
return nil
|
||||
}
|
||||
@@ -338,11 +338,11 @@ func runSpawn(cmd *cobra.Command, args []string) error {
|
||||
style.Bold.Render("✓"),
|
||||
style.Dim.Render(fmt.Sprintf("gt session at %s/%s", rigName, polecatName)))
|
||||
|
||||
// Send direct nudge to start working - don't rely on hooks or witness coordination
|
||||
// Send direct nudge to start working using reliable NudgeSession
|
||||
// The polecat has a work assignment in its inbox; just tell it to check
|
||||
sessionName := sessMgr.SessionName(polecatName)
|
||||
nudgeMsg := fmt.Sprintf("You have a work assignment. Run 'gt mail inbox' to see it, then start working on issue %s.", assignmentID)
|
||||
if err := t.SendKeysDebounced(sessionName, nudgeMsg, 500); err != nil {
|
||||
if err := t.NudgeSession(sessionName, nudgeMsg); err != nil {
|
||||
fmt.Printf(" %s\n", style.Dim.Render(fmt.Sprintf("Warning: could not nudge polecat: %v", err)))
|
||||
} else {
|
||||
fmt.Printf(" %s\n", style.Dim.Render("Polecat nudged to start working"))
|
||||
|
||||
Reference in New Issue
Block a user