Files
awesome-copilot/cookbook/copilot-sdk/go/recipe/ralph-loop.go
Anthony Shaw 92df16da5a Remove git commands from Ralph loop recipes
Git operations (commit, push) belong in the prompt instructions, not
hardcoded in the loop orchestrator. The SDK recipes should focus purely
on the SDK API: create client, create session, send prompt, destroy.
2026-02-11 11:32:42 -08:00

95 lines
2.4 KiB
Go

package main
import (
"context"
"fmt"
"log"
"os"
"strconv"
"strings"
copilot "github.com/github/copilot-sdk/go"
)
// Ralph loop: autonomous AI task loop with fresh context per iteration.
//
// Two modes:
// - "plan": reads PROMPT_plan.md, generates/updates IMPLEMENTATION_PLAN.md
// - "build": reads PROMPT_build.md, implements tasks, runs tests, commits
//
// Each iteration creates a fresh session so the agent always operates in
// the "smart zone" of its context window. State is shared between
// iterations via files on disk (IMPLEMENTATION_PLAN.md, AGENTS.md, specs/*).
//
// Usage:
// go run ralph-loop.go # build mode, 50 iterations
// go run ralph-loop.go plan # planning mode
// go run ralph-loop.go 20 # build mode, 20 iterations
// go run ralph-loop.go plan 5 # planning mode, 5 iterations
func ralphLoop(ctx context.Context, mode string, maxIterations int) error {
promptFile := "PROMPT_build.md"
if mode == "plan" {
promptFile = "PROMPT_plan.md"
}
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
return fmt.Errorf("failed to start client: %w", err)
}
defer client.Stop()
fmt.Println(strings.Repeat("━", 40))
fmt.Printf("Mode: %s\n", mode)
fmt.Printf("Prompt: %s\n", promptFile)
fmt.Printf("Max: %d iterations\n", maxIterations)
fmt.Println(strings.Repeat("━", 40))
prompt, err := os.ReadFile(promptFile)
if err != nil {
return fmt.Errorf("failed to read %s: %w", promptFile, err)
}
for i := 1; i <= maxIterations; i++ {
fmt.Printf("\n=== Iteration %d/%d ===\n", i, maxIterations)
// Fresh session — each task gets full context budget
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "claude-sonnet-4.5",
})
if err != nil {
return fmt.Errorf("failed to create session: %w", err)
}
_, err = session.SendAndWait(ctx, copilot.MessageOptions{
Prompt: string(prompt),
})
session.Destroy()
if err != nil {
return fmt.Errorf("send failed on iteration %d: %w", i, err)
}
fmt.Printf("\nIteration %d complete.\n", i)
}
fmt.Printf("\nReached max iterations: %d\n", maxIterations)
return nil
}
func main() {
mode := "build"
maxIterations := 50
for _, arg := range os.Args[1:] {
if arg == "plan" {
mode = "plan"
} else if n, err := strconv.Atoi(arg); err == nil {
maxIterations = n
}
}
if err := ralphLoop(context.Background(), mode, maxIterations); err != nil {
log.Fatal(err)
}
}