summaryrefslogtreecommitdiff
path: root/prompts/tmux-edit-integration-tests.md
diff options
context:
space:
mode:
Diffstat (limited to 'prompts/tmux-edit-integration-tests.md')
-rw-r--r--prompts/tmux-edit-integration-tests.md445
1 files changed, 0 insertions, 445 deletions
diff --git a/prompts/tmux-edit-integration-tests.md b/prompts/tmux-edit-integration-tests.md
deleted file mode 100644
index a0525b0..0000000
--- a/prompts/tmux-edit-integration-tests.md
+++ /dev/null
@@ -1,445 +0,0 @@
-# hexai-tmux-edit Integration Test Runbook
-
-Real-life integration tests against actual tmux panes running AI agent CLIs.
-These tests verify prompt capture, agent detection, multi-line extraction,
-text clearing, and the full edit-and-replace flow.
-
-**Test Status**:
-- ✅ **cursor-agent**: All tests passed (Feb 8, 2026)
-- ✅ **amp**: All tests passed (Feb 8, 2026) - Note: amp uses TUI mode with Emacs/readline keybindings
-- ⏳ **claude**: Needs testing
-- ⏳ **aider**: Needs testing
-
-**Important**: Agent detection and prompt extraction rely on regex patterns
-matched against each agent's TUI output (box-drawing characters, prompt
-symbols, status text). When agents update their TUI, these patterns may
-break. If tests fail after an agent update, check the built-in patterns in
-`internal/tmuxedit/agent.go` `builtinAgents()` and adjust the
-`DetectPattern`, `PromptPattern`, and `StripPatterns` fields accordingly.
-Users can also override patterns via `[[tmux_edit.agents]]` in config
-without code changes.
-
-## Prerequisites
-
-- Must be running inside a tmux session
-- `hexai-tmux-edit` binary must be installed (`go build -o ~/go/bin/hexai-tmux-edit ./cmd/hexai-tmux-edit/`)
-- At least one tmux pane running `cursor-agent` with an empty prompt
-- All unit tests must pass first: `go test ./internal/tmuxedit/`
-
-## Finding a test pane
-
-List all panes and pick one running cursor-agent with an idle prompt:
-
-```sh
-tmux list-panes -a -F '#{pane_id} #{window_name} #{pane_current_command}'
-```
-
-Look for a pane running `cursor-agent`. Use its `%NN` pane ID throughout.
-Verify it has an empty input prompt:
-
-```sh
-tmux capture-pane -p -t '%NN' | tail -10
-```
-
-You should see the box-drawing prompt:
-```
- ┌─────────────────────┐
- │ → Add a follow-up │
- └─────────────────────┘
-```
-
-If the prompt has text in it, clear it first:
-
-```sh
-tmux send-keys -t '%NN' End && sleep 0.1 && tmux send-keys -t '%NN' -N 200 BSpace
-```
-
-## Test 1: Single-line prompt capture
-
-**Goal**: Verify that text typed into cursor-agent's prompt is correctly
-detected and extracted.
-
-```sh
-# 1. Send test text to the prompt
-tmux send-keys -t '%NN' 'hello world test'
-
-# 2. Wait for the TUI to render
-sleep 1
-
-# 3. Verify text appeared in prompt
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → hello world test │
-
-# 4. Write a Go test script to verify extraction logic
-cat > /tmp/test_capture.go << 'GOEOF'
-//go:build ignore
-
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "regexp"
- "strings"
-)
-
-func main() {
- pane := os.Args[1]
- out, err := exec.Command("tmux", "capture-pane", "-p", "-t", pane).Output()
- if err != nil {
- fmt.Printf("FAIL: capture error: %v\n", err)
- os.Exit(1)
- }
- content := string(out)
-
- // Verify cursor agent detected
- detectRe := regexp.MustCompile(`(│\s*→|/ commands · @ files)`)
- if !detectRe.MatchString(content) {
- fmt.Println("FAIL: cursor agent not detected")
- os.Exit(1)
- }
- fmt.Println("PASS: cursor agent detected")
-
- // Verify prompt extracted
- promptRe := regexp.MustCompile(`(?m)│\s*→?\s*(.+?)\s*│\s*$`)
- m := promptRe.FindStringSubmatch(content)
- if len(m) < 2 {
- fmt.Println("FAIL: no prompt match")
- os.Exit(1)
- }
- text := m[1]
- for _, s := range []string{"INSERT", "Add a follow-up", "ctrl+c to stop"} {
- text = strings.ReplaceAll(text, s, "")
- }
- text = strings.TrimSpace(text)
- fmt.Printf("PASS: extracted %q\n", text)
-}
-GOEOF
-
-go run /tmp/test_capture.go '%NN'
-# Expected: PASS: cursor agent detected
-# PASS: extracted "hello world test"
-
-# 5. Clean up
-tmux send-keys -t '%NN' End && sleep 0.1 && tmux send-keys -t '%NN' -N 200 BSpace
-rm /tmp/test_capture.go
-```
-
-## Test 2: Multi-box disambiguation
-
-**Goal**: Verify that when cursor-agent shows multiple box-drawing sections
-(e.g. follow-ups box + input prompt), only the last box (the input prompt)
-is captured.
-
-This test requires a pane that has a follow-ups section above the input
-prompt. You can create this state by sending text with Shift+Enter (which
-cursor interprets as submit-and-queue-follow-up).
-
-```sh
-# 1. Verify the pane has multiple boxes visible
-tmux capture-pane -p -t '%NN' | grep '│' | head -20
-# Look for multiple │ lines from different boxes
-
-# 2. Type text into the (bottom) input prompt
-tmux send-keys -t '%NN' 'only this should be captured'
-sleep 1
-
-# 3. Run extraction and verify only the last box is picked
-cat > /tmp/test_multibox.go << 'GOEOF'
-//go:build ignore
-
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "regexp"
- "strings"
-)
-
-type promptMatch struct {
- lineNum int
- text string
-}
-
-func main() {
- pane := os.Args[1]
- out, err := exec.Command("tmux", "capture-pane", "-p", "-t", pane).Output()
- if err != nil {
- fmt.Printf("FAIL: capture error: %v\n", err)
- os.Exit(1)
- }
- content := string(out)
- re := regexp.MustCompile(`(?m)│\s*→?\s*(.+?)\s*│\s*$`)
- strips := []string{"INSERT", "Add a follow-up", "ctrl+c to stop"}
-
- // Find all matches with line numbers
- paneLines := strings.Split(content, "\n")
- var matches []promptMatch
- for i, line := range paneLines {
- m := re.FindStringSubmatch(line)
- if len(m) >= 2 {
- matches = append(matches, promptMatch{i, m[1]})
- }
- }
- fmt.Printf("Total matches across all boxes: %d\n", len(matches))
-
- // Take last contiguous block
- if len(matches) == 0 {
- fmt.Println("FAIL: no matches found")
- os.Exit(1)
- }
- last := len(matches) - 1
- start := last
- for start > 0 && matches[start].lineNum-matches[start-1].lineNum == 1 {
- start--
- }
-
- fmt.Printf("Last contiguous block: lines %d-%d (%d matches)\n",
- matches[start].lineNum, matches[last].lineNum, last-start+1)
-
- var lines []string
- for i := start; i <= last; i++ {
- text := matches[i].text
- for _, s := range strips {
- text = strings.ReplaceAll(text, s, "")
- }
- text = strings.TrimSpace(text)
- if text != "" {
- lines = append(lines, text)
- }
- }
- result := strings.Join(lines, "\n")
- fmt.Printf("EXTRACTED: %q\n", result)
-
- if strings.Contains(result, "only this should be captured") {
- fmt.Println("PASS: last box correctly isolated")
- } else {
- fmt.Println("FAIL: wrong box content captured")
- os.Exit(1)
- }
-}
-GOEOF
-
-go run /tmp/test_multibox.go '%NN'
-# Expected: Total matches > 1 (from multiple boxes)
-# PASS: last box correctly isolated
-
-# 3. Clean up
-tmux send-keys -t '%NN' End && sleep 0.1 && tmux send-keys -t '%NN' -N 200 BSpace
-rm /tmp/test_multibox.go
-```
-
-## Test 3: Clear and retype flow
-
-**Goal**: Verify that `End + BSpace*200` clears the existing prompt text
-and new text can be inserted afterwards.
-
-```sh
-# 1. Type original text
-tmux send-keys -t '%NN' 'original text here'
-sleep 0.5
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → original text here │
-
-# 2. Clear using the same method hexai-tmux-edit uses
-tmux send-keys -t '%NN' End
-sleep 0.1
-tmux send-keys -t '%NN' -N 200 BSpace
-sleep 0.5
-
-# 3. Verify prompt is empty
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → Add a follow-up │ (placeholder = empty)
-
-# 4. Type replacement text
-tmux send-keys -t '%NN' 'replacement text here'
-sleep 0.5
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → replacement text here │
-
-# 5. Clean up
-tmux send-keys -t '%NN' End && sleep 0.1 && tmux send-keys -t '%NN' -N 200 BSpace
-```
-
-## Test 4: Full end-to-end with mock editor
-
-**Goal**: Run the actual `hexai-tmux-edit` binary against a real pane,
-using a mock editor to automate the popup interaction. Verifies the
-complete flow: capture -> detect agent -> extract prompt -> edit -> clear
--> send back.
-
-**Note**: Mock editors should include a small delay before exiting to ensure
-tmux popups close cleanly. Without the delay, the popup might not register
-the editor exit properly and could hang.
-
-```sh
-# 1. Type text into the prompt
-tmux send-keys -t '%NN' 'hello world'
-sleep 0.5
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → hello world │
-
-# 2. Create a mock editor that replaces content (with delay for clean popup close)
-cat > /tmp/mock-editor.sh << 'SH'
-#!/bin/sh
-# Write new content
-echo "hello universe" > "$1"
-# Small delay ensures tmux popup closes cleanly
-sleep 0.1
-SH
-chmod +x /tmp/mock-editor.sh
-
-# 3. Run hexai-tmux-edit with the mock editor
-HEXAI_EDITOR=/tmp/mock-editor.sh hexai-tmux-edit --pane '%NN'
-# Wait for popup to close
-sleep 0.5
-
-# 4. Verify the prompt was updated
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → hello universe │
-
-# 5. Check debug log for the full trace
-cat /tmp/hexai-tmux-edit.log
-# Expected log should show:
-# - agent detected as "cursor"
-# - extractPrompt result: "hello world"
-# - editor returned: "hello universe"
-# - deduplicateText result: "hello universe"
-# - sending to pane: "hello universe"
-# - === done ===
-
-# 6. Clean up
-rm /tmp/mock-editor.sh
-tmux send-keys -t '%NN' End && sleep 0.1 && tmux send-keys -t '%NN' -N 200 BSpace
-```
-
-## Test 5: Agent detection does not false-positive on model names
-
-**Goal**: Verify that a cursor-agent pane showing "Claude 4.5 Sonnet" as
-the model name is detected as cursor (not claude).
-
-```sh
-# 1. Capture pane content and check for model name
-tmux capture-pane -p -t '%NN' | grep -i 'claude\|sonnet'
-# This may show "Claude 4.5 Sonnet (Thinking)" in the status line
-
-# 2. Verify detection is still "cursor"
-cat > /tmp/test_detect.go << 'GOEOF'
-//go:build ignore
-
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "regexp"
-)
-
-func main() {
- pane := os.Args[1]
- out, _ := exec.Command("tmux", "capture-pane", "-p", "-t", pane).Output()
- content := string(out)
-
- agents := []struct {
- name string
- detect string
- }{
- {"cursor", `(│\s*→|/ commands · @ files)`},
- {"claude", `(❯|claude code|anthropic)`},
- }
- for _, a := range agents {
- re := regexp.MustCompile(a.detect)
- if re.MatchString(content) {
- fmt.Printf("Detected: %s\n", a.name)
- if a.name == "cursor" {
- fmt.Println("PASS: correctly detected as cursor")
- } else {
- fmt.Println("FAIL: should be cursor, not " + a.name)
- os.Exit(1)
- }
- return
- }
- }
- fmt.Println("FAIL: no agent detected")
- os.Exit(1)
-}
-GOEOF
-
-go run /tmp/test_detect.go '%NN'
-# Expected: PASS: correctly detected as cursor
-
-rm /tmp/test_detect.go
-```
-
-## Test 6: Unchanged text results in no-op
-
-**Goal**: If the user opens the editor and saves without changes,
-nothing should be sent to the pane.
-
-```sh
-# 1. Type text into the prompt
-tmux send-keys -t '%NN' 'do not change me'
-sleep 0.5
-
-# 2. Create a mock editor that keeps the content unchanged (with delay)
-cat > /tmp/mock-noop-editor.sh << 'SH'
-#!/bin/sh
-# Do nothing -- file already has the pre-filled content
-# Small delay for clean popup close
-sleep 0.1
-SH
-chmod +x /tmp/mock-noop-editor.sh
-
-# 3. Run hexai-tmux-edit
-HEXAI_EDITOR=/tmp/mock-noop-editor.sh hexai-tmux-edit --pane '%NN'
-sleep 0.5
-
-# 4. Check debug log -- should show "nothing to send"
-grep 'nothing to send' /tmp/hexai-tmux-edit.log
-# Expected: "nothing to send, exiting"
-
-# 5. Verify prompt text is unchanged (still has original text)
-tmux capture-pane -p -t '%NN' | grep '│.*→'
-# Expected: │ → do not change me │
-
-# 6. Clean up
-rm /tmp/mock-noop-editor.sh
-tmux send-keys -t '%NN' End && sleep 0.1 && tmux send-keys -t '%NN' -N 200 BSpace
-```
-
-## Cleanup
-
-After all tests, remove any leftover temp files and check for hung popups:
-
-```sh
-# Remove test files
-rm -f /tmp/test_capture.go /tmp/test_multibox.go /tmp/test_detect.go
-rm -f /tmp/mock-editor.sh /tmp/mock-noop-editor.sh
-
-# Check for any hung tmux popups (should show nothing)
-tmux list-panes -a | grep popup || echo "✓ No hung popups"
-
-# If popups are hung, kill them
-# pkill -f 'tmux.*popup' || true
-```
-
-## Troubleshooting
-
-**Popup doesn't close**: Mock editors that exit instantly (< 50ms) might not
-give tmux enough time to register the exit. Add `sleep 0.1` before the editor
-script exits.
-
-**Popup hangs**: If a popup is stuck open:
-```sh
-# Find popup panes
-tmux list-panes -a | grep popup
-
-# Kill popup processes
-pkill -f 'tmux.*popup'
-
-# Or press Escape in the tmux session to close active popup
-```