From 794a9ee8a87188bbbd61e37fa7294be9cdd7c554 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 22 Dec 2025 12:31:38 -0800 Subject: [PATCH] feat: Add gt peek command and session communication docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds gt peek as ergonomic alias for gt session capture: gt peek gastown/furiosa # Last 100 lines gt peek gastown/furiosa 50 # Last 50 lines Creates nudge/peek pair as canonical session interface: gt nudge - send TO session (reliable delivery) gt peek - read FROM session (capture-pane wrapper) Adds docs/session-communication.md explaining: - Why raw tmux send-keys is unreliable for Claude - The reliable NudgeSession pattern (literal + delay + Enter) - Command reference and common patterns - Guidance for template authors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/session-communication.md | 126 ++++++++++++++++++++++++++++++++++ internal/cmd/peek.go | 68 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 docs/session-communication.md create mode 100644 internal/cmd/peek.go diff --git a/docs/session-communication.md b/docs/session-communication.md new file mode 100644 index 00000000..430666a4 --- /dev/null +++ b/docs/session-communication.md @@ -0,0 +1,126 @@ +# Session Communication: Nudge and Peek + +Gas Town agents communicate with Claude Code sessions through **two canonical commands**: + +| Command | Direction | Purpose | +|---------|-----------|---------| +| `gt nudge` | You → Agent | Send a message reliably | +| `gt peek` | Agent → You | Read recent output | + +## Why Not Raw tmux? + +**tmux send-keys is unreliable for Claude Code sessions.** + +The problem: When you send text followed by Enter using `tmux send-keys "message" Enter`, +the Enter key often arrives before the paste completes. Claude receives a truncated +message or the Enter appends to the previous line. + +This is a race condition in tmux's input handling. It's not a bug - tmux wasn't +designed for pasting multi-line content to interactive AI sessions. + +### The Reliable Pattern + +`gt nudge` uses a tested, reliable pattern: + +```go +// 1. Send text in literal mode (handles special characters) +tmux send-keys -t session -l "message" + +// 2. Wait 500ms for paste to complete +time.Sleep(500ms) + +// 3. Send Enter as separate command +tmux send-keys -t session Enter +``` + +**Never use raw tmux send-keys for agent sessions.** Always use `gt nudge`. + +## Command Reference + +### gt nudge + +Send a message to a polecat's Claude session: + +```bash +gt nudge + +# Examples +gt nudge gastown/furiosa "Check your mail and start working" +gt nudge gastown/alpha "What's your status?" +gt nudge gastown/beta "Stop what you're doing and read gt-xyz" +``` + +### gt peek + +View recent output from a polecat's session: + +```bash +gt peek [lines] + +# Examples +gt peek gastown/furiosa # Last 100 lines (default) +gt peek gastown/furiosa 50 # Last 50 lines +gt peek gastown/furiosa -n 200 # Last 200 lines +``` + +## Common Patterns + +### Check on a polecat + +```bash +# See what they're doing +gt peek gastown/furiosa + +# Ask for status +gt nudge gastown/furiosa "What's your current status?" +``` + +### Redirect a polecat + +```bash +# Stop current work and pivot +gt nudge gastown/furiosa "Stop current task. New priority: read and act on gt-xyz" +``` + +### Wake up a stuck polecat + +```bash +# Sometimes agents get stuck waiting for input +gt nudge gastown/furiosa "Continue working" +``` + +### Batch check all polecats + +```bash +# Quick status check +for p in furiosa nux slit; do + echo "=== $p ===" + gt peek gastown/$p 20 +done +``` + +## For Template Authors + +When writing agent templates (deacon.md.tmpl, polecat.md.tmpl, etc.), include this guidance: + +```markdown +## Session Communication + +To send messages to other agents, use gt commands: +- `gt nudge ` - Send reliably +- `gt peek ` - Read output + +⚠️ NEVER use raw tmux send-keys - it's unreliable for Claude sessions. +``` + +## Implementation Details + +The nudge/peek commands wrap these underlying functions: + +| Command | Wrapper | Underlying | +|---------|---------|------------| +| `gt nudge` | `tmux.NudgeSession()` | literal send-keys + delay + Enter | +| `gt peek` | `session.Capture()` | tmux capture-pane | + +The 500ms delay in NudgeSession was determined empirically. Shorter delays fail +intermittently; longer delays work but slow down communication unnecessarily. diff --git a/internal/cmd/peek.go b/internal/cmd/peek.go new file mode 100644 index 00000000..5140f496 --- /dev/null +++ b/internal/cmd/peek.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "fmt" + "strconv" + + "github.com/spf13/cobra" +) + +// Peek command flags +var peekLines int + +func init() { + rootCmd.AddCommand(peekCmd) + peekCmd.Flags().IntVarP(&peekLines, "lines", "n", 100, "Number of lines to capture") +} + +var peekCmd = &cobra.Command{ + Use: "peek [count]", + Short: "View recent output from a polecat session", + Long: `Capture and display recent terminal output from a polecat session. + +This is the ergonomic alias for 'gt session capture'. Use it to check +what an agent is currently doing or has recently output. + +The nudge/peek pair provides the canonical interface for agent sessions: + gt nudge - send messages TO a session (reliable delivery) + gt peek - read output FROM a session (capture-pane wrapper) + +Examples: + gt peek gastown/furiosa # Last 100 lines (default) + gt peek gastown/furiosa 50 # Last 50 lines + gt peek gastown/furiosa -n 200 # Last 200 lines`, + Args: cobra.RangeArgs(1, 2), + RunE: runPeek, +} + +func runPeek(cmd *cobra.Command, args []string) error { + address := args[0] + + // Handle optional positional count argument + lines := peekLines + if len(args) > 1 { + n, err := strconv.Atoi(args[1]) + if err != nil { + return fmt.Errorf("invalid line count: %s", args[1]) + } + lines = n + } + + rigName, polecatName, err := parseAddress(address) + if err != nil { + return err + } + + mgr, _, err := getSessionManager(rigName) + if err != nil { + return err + } + + output, err := mgr.Capture(polecatName, lines) + if err != nil { + return fmt.Errorf("capturing output: %w", err) + } + + fmt.Print(output) + return nil +}