From d0019d2415020f84c21fe0766c4780f4e435953e Mon Sep 17 00:00:00 2001 From: Kalle Bylin Date: Wed, 18 Mar 2026 20:52:42 +0100 Subject: [PATCH] fix: update built-in prompt work commands --- cmd/gc/builtin_prompts_test.go | 90 +++++++++++++++++++++++++++++++++ cmd/gc/cmd_hook.go | 2 +- cmd/gc/cmd_hook_test.go | 6 +++ cmd/gc/prompts/loop-mail.md | 6 +-- cmd/gc/prompts/loop.md | 12 ++--- cmd/gc/prompts/one-shot.md | 8 +-- cmd/gc/prompts/scoped-worker.md | 8 +-- cmd/gc/prompts/worker.md | 8 +-- 8 files changed, 118 insertions(+), 22 deletions(-) diff --git a/cmd/gc/builtin_prompts_test.go b/cmd/gc/builtin_prompts_test.go index 8787f0f4..2f2d6892 100644 --- a/cmd/gc/builtin_prompts_test.go +++ b/cmd/gc/builtin_prompts_test.go @@ -1,13 +1,66 @@ package main import ( + "io" "os" "path/filepath" + "strings" "testing" "github.com/gastownhall/gascity/internal/citylayout" + "github.com/gastownhall/gascity/internal/fsys" ) +func materializeBuiltinPromptsForTest(t *testing.T) string { + t.Helper() + + dir := t.TempDir() + if err := materializeBuiltinPrompts(dir); err != nil { + t.Fatalf("materializeBuiltinPrompts: %v", err) + } + return dir +} + +func renderBuiltinPromptForTest(t *testing.T, dir, name string, ctx PromptContext) string { + t.Helper() + + got := renderPrompt(fsys.OSFS{}, dir, "gastown", filepath.Join(citylayout.PromptsRoot, name), ctx, "", io.Discard, nil, nil, nil) + if got == "" { + t.Fatalf("renderPrompt(%s) returned empty output", name) + } + return got +} + +func assertRenderedPromptContains(t *testing.T, rendered, name string, want []string) { + t.Helper() + + for _, needle := range want { + if strings.Contains(rendered, needle) { + continue + } + t.Errorf("prompt %s missing %q", name, needle) + } +} + +func assertRenderedPromptDoesNotContain(t *testing.T, rendered, name string, blocked []string) { + t.Helper() + + for _, needle := range blocked { + if !strings.Contains(rendered, needle) { + continue + } + t.Errorf("prompt %s unexpectedly contains %q", name, needle) + } +} + +func currentHookCommand(target string) string { + return "gc " + newHookCmd(io.Discard, io.Discard).Name() + " " + target +} + +func currentSlingCommand(target, bead string) string { + return "gc " + newSlingCmd(io.Discard, io.Discard).Name() + " " + target + " " + bead +} + func TestMaterializeBuiltinPrompts(t *testing.T) { dir := t.TempDir() if err := materializeBuiltinPrompts(dir); err != nil { @@ -59,6 +112,43 @@ func TestMaterializeBuiltinPromptsOverwrites(t *testing.T) { } } +func TestMaterializeBuiltinFixedWorkerPromptsUseCurrentHookCommand(t *testing.T) { + dir := materializeBuiltinPromptsForTest(t) + + tests := map[string]PromptContext{ + "worker.md": {AgentName: "mayor", TemplateName: "mayor"}, + "one-shot.md": {AgentName: "mayor", TemplateName: "mayor"}, + "scoped-worker.md": {AgentName: "hello-world/worker", TemplateName: "worker", RigName: "hello-world", WorkDir: "/city/hello-world"}, + } + for name, ctx := range tests { + rendered := renderBuiltinPromptForTest(t, dir, name, ctx) + assertRenderedPromptContains(t, rendered, name, []string{currentHookCommand("$GC_AGENT")}) + assertRenderedPromptDoesNotContain(t, rendered, name, []string{"gc agent claimed"}) + } +} + +func TestMaterializeBuiltinLoopPromptsUseCurrentHookAndSlingCommands(t *testing.T) { + dir := materializeBuiltinPromptsForTest(t) + + tests := map[string]PromptContext{ + "loop.md": {AgentName: "worker", TemplateName: "worker"}, + "loop-mail.md": {AgentName: "worker", TemplateName: "worker"}, + } + want := []string{ + currentHookCommand("$GC_AGENT"), + currentSlingCommand("$GC_AGENT", ""), + } + blocked := []string{ + "gc agent claimed", + "gc agent claim", + } + for name, ctx := range tests { + rendered := renderBuiltinPromptForTest(t, dir, name, ctx) + assertRenderedPromptContains(t, rendered, name, want) + assertRenderedPromptDoesNotContain(t, rendered, name, blocked) + } +} + func TestMaterializeBuiltinFormulas(t *testing.T) { dir := t.TempDir() if err := materializeBuiltinFormulas(dir); err != nil { diff --git a/cmd/gc/cmd_hook.go b/cmd/gc/cmd_hook.go index 8d428fe7..d9435b88 100644 --- a/cmd/gc/cmd_hook.go +++ b/cmd/gc/cmd_hook.go @@ -137,7 +137,7 @@ func doHook(workQuery, dir string, inject bool, runner WorkQueryRunner, stdout, if inject { if hasWork { - fmt.Fprintf(stdout, "\nYou have pending work. Pick up the next item:\n\n\n%s\n\n\nClaim it and start working. Run 'gc hook' to see the full queue.\n\n", trimmed) //nolint:errcheck // best-effort stdout + fmt.Fprintf(stdout, "\nYou have pending work. Pick up the next item:\n\n\n%s\n\n\nStart working on it. Run 'gc hook' to see the full queue.\n\n", trimmed) //nolint:errcheck // best-effort stdout } return 0 // --inject always exits 0 } diff --git a/cmd/gc/cmd_hook_test.go b/cmd/gc/cmd_hook_test.go index a1dff13f..433ab384 100644 --- a/cmd/gc/cmd_hook_test.go +++ b/cmd/gc/cmd_hook_test.go @@ -108,6 +108,12 @@ func TestHookInjectFormatsOutput(t *testing.T) { if !strings.Contains(out, "gc hook") { t.Errorf("stdout missing 'gc hook' hint: %q", out) } + if !strings.Contains(out, "Start working on it.") { + t.Errorf("stdout missing updated work wording: %q", out) + } + if strings.Contains(out, "Claim it and start working.") { + t.Errorf("stdout still contains stale claim wording: %q", out) + } } func TestHookInjectAlwaysExitsZero(t *testing.T) { diff --git a/cmd/gc/prompts/loop-mail.md b/cmd/gc/prompts/loop-mail.md index ce393b85..71bc4e52 100644 --- a/cmd/gc/prompts/loop-mail.md +++ b/cmd/gc/prompts/loop-mail.md @@ -8,10 +8,10 @@ You are a coding agent that runs in a loop, checking for work and messages. 2. If you have unread messages, read each one: `gc mail read ` - If the message asks a question, reply: `gc mail send ""` - If the message gives you information, incorporate it into your work -3. Check your claim: `gc agent claimed $GC_AGENT` -4. If a bead is already claimed by you, execute it and go to step 7 +3. Check your claim: `gc hook $GC_AGENT` +4. If a bead is already assigned to you, execute it and go to step 7 5. If your hook is empty, check for available work: `bd ready` -6. If a bead is available, claim it: `gc agent claim $GC_AGENT ` +6. If a bead is available, route it to yourself: `gc sling $GC_AGENT ` 7. Execute the work described in the bead's title 8. When done, close it: `bd close ` 9. Go to step 1 diff --git a/cmd/gc/prompts/loop.md b/cmd/gc/prompts/loop.md index 625408ca..e619f232 100644 --- a/cmd/gc/prompts/loop.md +++ b/cmd/gc/prompts/loop.md @@ -3,24 +3,24 @@ You are a worker agent in a Gas City workspace. You drain the backlog — executing tasks one at a time, each with a clean focus. -## GUPP — If you find work claimed by you, YOU RUN IT. +## GUPP — If you find work assigned to you, YOU RUN IT. No confirmation, no waiting. The hook having work IS the assignment. ## Your tools -- `gc agent claimed $GC_AGENT` — check what's claimed by you +- `gc hook $GC_AGENT` — check what's assigned to you - `bd ready` — see available work items -- `gc agent claim $GC_AGENT ` — claim a work item +- `gc sling $GC_AGENT ` — route a work item to yourself - `bd show ` — see details of a work item - `bd close ` — mark work as done ## How to work -1. Check your claim: `gc agent claimed $GC_AGENT` -2. If a bead is already claimed by you, execute it and go to step 5 +1. Check your claim: `gc hook $GC_AGENT` +2. If a bead is already assigned to you, execute it and go to step 5 3. If your hook is empty, check for available work: `bd ready` -4. If a bead is available, claim it: `gc agent claim $GC_AGENT ` +4. If a bead is available, route it to yourself: `gc sling $GC_AGENT ` 5. Execute the work described in the bead's title 6. When done, close it: `bd close ` 7. Go to step 1 diff --git a/cmd/gc/prompts/one-shot.md b/cmd/gc/prompts/one-shot.md index 5e609c0e..03bee545 100644 --- a/cmd/gc/prompts/one-shot.md +++ b/cmd/gc/prompts/one-shot.md @@ -3,20 +3,20 @@ You are a worker agent in a Gas City workspace. You execute a single task and stop. -## GUPP — If you find work claimed by you, YOU RUN IT. +## GUPP — If you find work assigned to you, YOU RUN IT. No confirmation, no waiting. The hook having work IS the assignment. ## Your tools -- `gc agent claimed $GC_AGENT` — check what's claimed by you +- `gc hook $GC_AGENT` — check what's assigned to you - `bd show ` — see details of a work item - `bd close ` — mark work as done ## How to work -1. Check your claim: `gc agent claimed $GC_AGENT` -2. If a bead is claimed by you, execute the work described in its title +1. Check your claim: `gc hook $GC_AGENT` +2. If a bead is assigned to you, execute the work described in its title 3. When done, close it: `bd close ` 4. You're done. Wait for further instructions. diff --git a/cmd/gc/prompts/scoped-worker.md b/cmd/gc/prompts/scoped-worker.md index f95872b0..d74ec936 100644 --- a/cmd/gc/prompts/scoped-worker.md +++ b/cmd/gc/prompts/scoped-worker.md @@ -3,20 +3,20 @@ You are a worker agent in a Gas City workspace. Your working directory is $GC_DIR — all your work happens there. -## GUPP — If you find work claimed by you, YOU RUN IT. +## GUPP — If you find work assigned to you, YOU RUN IT. No confirmation, no waiting. The hook having work IS the assignment. ## Your tools -- `gc agent claimed $GC_AGENT` — check what's claimed by you +- `gc hook $GC_AGENT` — check what's assigned to you - `bd show ` — see details of a work item - `bd close ` — mark work as done ## How to work -1. Check your claim: `gc agent claimed $GC_AGENT` -2. If a bead is claimed by you, execute the work described in its title +1. Check your claim: `gc hook $GC_AGENT` +2. If a bead is assigned to you, execute the work described in its title 3. All file operations happen in your directory: $GC_DIR 4. When done, close it: `bd close ` 5. Check your claim again for more work diff --git a/cmd/gc/prompts/worker.md b/cmd/gc/prompts/worker.md index fba88a07..78b46865 100644 --- a/cmd/gc/prompts/worker.md +++ b/cmd/gc/prompts/worker.md @@ -2,20 +2,20 @@ You are a worker agent in a Gas City workspace. -## GUPP — If you find work claimed by you, YOU RUN IT. +## GUPP — If you find work assigned to you, YOU RUN IT. No confirmation, no waiting. The hook having work IS the assignment. ## Your tools -- `gc agent claimed $GC_AGENT` — check what's claimed by you +- `gc hook $GC_AGENT` — check what's assigned to you - `bd show ` — see details of a work item - `bd close ` — mark work as done ## How to work -1. Check your claim: `gc agent claimed $GC_AGENT` -2. If a bead is claimed by you, execute the work described in its title +1. Check your claim: `gc hook $GC_AGENT` +2. If a bead is assigned to you, execute the work described in its title 3. When done, close it: `bd close ` 4. Check your claim again for more work