fix(prime): use startup beacon instead of bare "gt prime" prompt
Agents were confused when receiving "gt prime" as their first prompt, interpreting it as a command to investigate rather than understanding they were starting a Gas Town session. Changed crew_at.go, start.go, and handoff.go to use FormatStartupNudge() which produces a proper beacon like: [GAS TOWN] george/crew/george <- human • 2026-01-09T10:30 • start The SessionStart hook (gt prime --hook) still injects context - the prompt just needs to be something agents recognize as a greeting. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
ff3d1b2e23
commit
86739556c2
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/steveyegge/gastown/internal/constants"
|
||||
"github.com/steveyegge/gastown/internal/crew"
|
||||
"github.com/steveyegge/gastown/internal/runtime"
|
||||
"github.com/steveyegge/gastown/internal/session"
|
||||
"github.com/steveyegge/gastown/internal/style"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
"github.com/steveyegge/gastown/internal/workspace"
|
||||
@@ -162,11 +163,20 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("getting pane ID: %w", err)
|
||||
}
|
||||
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
|
||||
// The SessionStart hook handles context injection (gt prime --hook)
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "start",
|
||||
})
|
||||
|
||||
// Use respawn-pane to replace shell with runtime directly
|
||||
// This gives cleaner lifecycle: runtime exits → session ends (no intermediate shell)
|
||||
// Pass "gt prime" as initial prompt if supported
|
||||
// Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes
|
||||
startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, "gt prime", crewAgentOverride)
|
||||
startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, beacon, crewAgentOverride)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building startup command: %w", err)
|
||||
}
|
||||
@@ -198,10 +208,18 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("getting pane ID: %w", err)
|
||||
}
|
||||
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "restart",
|
||||
})
|
||||
|
||||
// Use respawn-pane to replace shell with runtime directly
|
||||
// Pass "gt prime" as initial prompt if supported
|
||||
// Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes
|
||||
startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, "gt prime", crewAgentOverride)
|
||||
startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, beacon, crewAgentOverride)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building startup command: %w", err)
|
||||
}
|
||||
@@ -218,13 +236,19 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
// Check if we're already in the target session
|
||||
if isInTmuxSession(sessionID) {
|
||||
// We're in the session at a shell prompt - just start the agent directly
|
||||
// Pass "gt prime" as initial prompt so it loads context immediately
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "start",
|
||||
})
|
||||
agentCfg, _, err := config.ResolveAgentConfigWithOverride(townRoot, r.Path, crewAgentOverride)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolving agent: %w", err)
|
||||
}
|
||||
fmt.Printf("Starting %s in current session...\n", agentCfg.Command)
|
||||
return execAgent(agentCfg, "gt prime")
|
||||
return execAgent(agentCfg, beacon)
|
||||
}
|
||||
|
||||
// If inside tmux (but different session), don't switch - just inform user
|
||||
|
||||
@@ -340,19 +340,29 @@ func buildRestartCommand(sessionName string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Determine GT_ROLE and BD_ACTOR values for this session
|
||||
gtRole := sessionToGTRole(sessionName)
|
||||
// Parse the session name to get the identity (used for GT_ROLE and beacon)
|
||||
identity, err := session.ParseSessionName(sessionName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot parse session name %q: %w", sessionName, err)
|
||||
}
|
||||
gtRole := identity.GTRole()
|
||||
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
|
||||
// The SessionStart hook handles context injection (gt prime --hook)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
Recipient: identity.Address(),
|
||||
Sender: "self",
|
||||
Topic: "handoff",
|
||||
})
|
||||
|
||||
// For respawn-pane, we:
|
||||
// 1. cd to the right directory (role's canonical home)
|
||||
// 2. export GT_ROLE and BD_ACTOR so role detection works correctly
|
||||
// 3. export Claude-related env vars (not inherited by fresh shell)
|
||||
// 4. run claude with "gt prime" as initial prompt (triggers GUPP)
|
||||
// 4. run claude with the startup beacon (triggers immediate context loading)
|
||||
// Use exec to ensure clean process replacement.
|
||||
// IMPORTANT: Passing "gt prime" as argument injects it as the first prompt,
|
||||
// which triggers the agent to execute immediately. Without this, agents
|
||||
// wait for user input despite all GUPP prompting in hooks.
|
||||
runtimeCmd := config.GetRuntimeCommandWithPrompt("", "gt prime")
|
||||
runtimeCmd := config.GetRuntimeCommandWithPrompt("", beacon)
|
||||
|
||||
// Build environment exports - role vars first, then Claude vars
|
||||
var exports []string
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/steveyegge/gastown/internal/polecat"
|
||||
"github.com/steveyegge/gastown/internal/refinery"
|
||||
"github.com/steveyegge/gastown/internal/rig"
|
||||
"github.com/steveyegge/gastown/internal/session"
|
||||
"github.com/steveyegge/gastown/internal/style"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
"github.com/steveyegge/gastown/internal/witness"
|
||||
@@ -277,7 +278,14 @@ func startConfiguredCrew(t *tmux.Tmux, townRoot string) {
|
||||
if !t.IsAgentRunning(sessionID, config.ExpectedPaneCommands(agentCfg)...) {
|
||||
// Claude has exited, restart it
|
||||
fmt.Printf(" %s %s/%s session exists, restarting Claude...\n", style.Dim.Render("○"), r.Name, crewName)
|
||||
claudeCmd := config.BuildCrewStartupCommand(r.Name, crewName, r.Path, "gt prime")
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, crewName)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "restart",
|
||||
})
|
||||
claudeCmd := config.BuildCrewStartupCommand(r.Name, crewName, r.Path, beacon)
|
||||
if err := t.SendKeys(sessionID, claudeCmd); err != nil {
|
||||
fmt.Printf(" %s %s/%s restart failed: %v\n", style.Dim.Render("○"), r.Name, crewName, err)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user