docs: comprehensive hook/sling/handoff/nudge audit
- Update propulsion-principle.md: implementation status now accurate - Update beads-data-plane.md: correct command syntax - Fix hook.go: clarify durability semantics, add related commands - Fix sling.go: use reliable NudgePane instead of raw tmux send-keys - Add tmux.NudgePane: pane-targeted reliable message delivery The command menagerie: gt hook = assign (durability) gt nudge = communicate (generic messaging) gt sling = hook + nudge "start working" gt handoff = hook + restart (GUPP kicks in) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,12 +16,13 @@ var hookCmd = &cobra.Command{
|
||||
Short: "Attach work to your hook (durable across restarts)",
|
||||
Long: `Attach a bead (issue) to your hook for durable work tracking.
|
||||
|
||||
The hook is a lightweight, ephemeral attachment point. When you restart
|
||||
(via gt handoff), your SessionStart hook finds the slung work and you
|
||||
continue from where you left off.
|
||||
The hook is the "durability primitive" - work on your hook survives session
|
||||
restarts, context compaction, and handoffs. When you restart (via gt handoff),
|
||||
your SessionStart hook finds the attached work and you continue from where
|
||||
you left off.
|
||||
|
||||
This is the "durability primitive" - work on your hook survives session
|
||||
restarts. For the full restart-and-resume flow, use: gt handoff <bead>
|
||||
This is "assign without action" - use gt sling to also start immediately,
|
||||
or gt handoff to hook and restart with fresh context.
|
||||
|
||||
Examples:
|
||||
gt hook gt-abc # Attach issue gt-abc to your hook
|
||||
@@ -30,8 +31,9 @@ Examples:
|
||||
|
||||
Related commands:
|
||||
gt mol status # See what's on your hook
|
||||
gt handoff <bead> # Hook + restart in one step
|
||||
gt handoff # Restart (uses existing hook if present)`,
|
||||
gt sling <bead> # Hook + start now (keep context)
|
||||
gt handoff <bead> # Hook + restart (fresh context)
|
||||
gt nudge <agent> # Send message to trigger execution`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runHook,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/style"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
"github.com/steveyegge/gastown/internal/wisp"
|
||||
)
|
||||
|
||||
@@ -134,6 +135,7 @@ func runSling(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// injectStartPrompt sends a prompt to the target pane to start working.
|
||||
// Uses the reliable nudge pattern: literal mode + 500ms debounce + separate Enter.
|
||||
func injectStartPrompt(pane, beadID, subject string) error {
|
||||
if pane == "" {
|
||||
return fmt.Errorf("no target pane")
|
||||
@@ -147,9 +149,9 @@ func injectStartPrompt(pane, beadID, subject string) error {
|
||||
prompt = fmt.Sprintf("Work slung: %s. Start working on it now - run `gt mol status` to see the hook, then begin.", beadID)
|
||||
}
|
||||
|
||||
// Use tmux send-keys to inject the prompt
|
||||
// Add Enter to submit it
|
||||
return exec.Command("tmux", "send-keys", "-t", pane, prompt, "Enter").Run()
|
||||
// Use the reliable nudge pattern (same as gt nudge / tmux.NudgeSession)
|
||||
t := tmux.NewTmux()
|
||||
return t.NudgePane(pane, prompt)
|
||||
}
|
||||
|
||||
// resolveTargetAgent converts a target spec to agent ID and pane.
|
||||
|
||||
@@ -207,6 +207,25 @@ func (t *Tmux) NudgeSession(session, message string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NudgePane sends a message to a specific pane reliably.
|
||||
// Same pattern as NudgeSession but targets a pane ID (e.g., "%9") instead of session name.
|
||||
func (t *Tmux) NudgePane(pane, message string) error {
|
||||
// 1. Send text in literal mode (handles special characters)
|
||||
if _, err := t.run("send-keys", "-t", pane, "-l", message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Wait 500ms for paste to complete (tested, required)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// 3. Send Enter as separate command (key to reliability)
|
||||
if _, err := t.run("send-keys", "-t", pane, "Enter"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPaneCommand returns the current command running in a pane.
|
||||
// Returns "bash", "zsh", "claude", "node", etc.
|
||||
func (t *Tmux) GetPaneCommand(session string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user