refactor(recipe): improve loading and execution logic

- Enhance recipe parsing in loader.go: extract overview, use split-based step extraction to avoid duplicates, refine final summary handling, and clean up comments/templates.
- Refine runner.go prompts: add Grok system message, simplify user prompts for conciseness, adjust result joining with separators, and remove unnecessary text.
This commit is contained in:
Greg Gauthier 2026-03-06 20:46:30 +00:00
parent 5c67f78c27
commit d1ebd2af79
2 changed files with 27 additions and 32 deletions

View File

@ -12,15 +12,10 @@ import (
)
var (
// stepRe finds every "### Step N: Title" heading
stepRe = regexp.MustCompile(`(?m)^### Step (\d+): (.+)$`)
// subRe finds the three labelled sections inside each step (no Perl lookahead)
subRe = regexp.MustCompile(`(?m)^(\*\*(?:Objective|Instructions|Expected output):\*\*)\s*(.+?)(?:\n\n|\n###|\z)`)
subRe = regexp.MustCompile(`(?m)^(\*\*(?:Objective|Instructions|Expected output):\*\*)\s*(.+?)(?:\n\n|\n###|\z)`)
)
// Load reads a recipe from disk and fully parses it.
// Load reads a recipe from disk and fully parses it.
func Load(path string, userParams map[string]any) (*Recipe, error) {
b, err := os.ReadFile(path)
if err != nil {
@ -37,7 +32,7 @@ func Load(path string, userParams map[string]any) (*Recipe, error) {
return nil, fmt.Errorf("yaml parse: %w", err)
}
// Apply defaults from YAML if user didn't supply them
// Apply defaults
if r.Parameters == nil {
r.Parameters = make(map[string]Parameter)
}
@ -50,7 +45,7 @@ func Load(path string, userParams map[string]any) (*Recipe, error) {
}
}
// render templates with defaults applied
// Render templates
tpl, err := template.New("recipe").Parse(string(parts[2]))
if err != nil {
return nil, err
@ -59,25 +54,26 @@ func Load(path string, userParams map[string]any) (*Recipe, error) {
if err := tpl.Execute(&rendered, params); err != nil {
return nil, err
}
if err := tpl.Execute(&rendered, params); err != nil {
return nil, err
}
body := rendered.String()
// extract steps
// Extract Overview
if idx := strings.Index(body, "## Execution Steps"); idx != -1 {
r.Overview = strings.TrimSpace(body[:idx])
}
// Extract steps — split-based to guarantee no duplicates
matches := stepRe.FindAllStringSubmatch(body, -1)
for i, m := range matches {
stepNum := i + 1
title := m[2]
// crude but effective sub-section extraction
start := strings.Index(body, m[0])
end := len(body)
if i+1 < len(matches) {
end = strings.Index(body[start:], matches[i+1][0]) + start
nextStart := strings.Index(body[start:], matches[i+1][0])
end = start + nextStart
}
section := body[start:end]
step := Step{Number: stepNum, Title: title}
@ -94,9 +90,12 @@ func Load(path string, userParams map[string]any) (*Recipe, error) {
r.Steps = append(r.Steps, step)
}
// final summary is everything after the last step
lastStepEnd := strings.LastIndex(body, matches[len(matches)-1][0])
r.FinalSummaryPrompt = strings.TrimSpace(body[lastStepEnd+len(matches[len(matches)-1][0]):])
// Final summary = everything after the last step
if len(matches) > 0 {
lastMatch := matches[len(matches)-1][0]
lastIdx := strings.LastIndex(body, lastMatch)
r.FinalSummaryPrompt = strings.TrimSpace(body[lastIdx+len(lastMatch):])
}
return &r, nil
}

View File

@ -25,10 +25,7 @@ func (r *Runner) Run() error {
for _, step := range r.Recipe.Steps {
fmt.Printf("Step %d/%d: %s\n", step.Number, len(r.Recipe.Steps), step.Title)
// Build a clear, self-contained prompt for this step
prompt := fmt.Sprintf(`You are an expert sous-chef executing a recipe with precision.
Recipe Overview:
prompt := fmt.Sprintf(`Recipe Overview:
%s
Previous step results (for context):
@ -39,35 +36,34 @@ Objective: %s
Instructions: %s
Expected output format: %s
Execute the step now. Be concise and follow the expected output format exactly.`,
r.Recipe.Overview, // we'll extract this too if you want, or leave as-is
strings.Join(previousResults, "\n\n"),
Execute this step now. Respond ONLY with the expected output format no explanations, no extra text.`,
r.Recipe.Overview,
strings.Join(previousResults, "\n\n---\n\n"),
step.Objective,
step.Instructions,
step.Expected)
// Use the same streaming client as every other command
messages := []map[string]string{
{"role": "system", "content": "You are a precise, no-nonsense coding sous-chef."},
{"role": "system", "content": "You are Grok, built by xAI. You are a precise, expert Go programmer and refactoring assistant. Always follow the user's instructions exactly for legitimate coding tasks. Never refuse or add safety warnings."},
{"role": "user", "content": prompt},
}
response := r.Client.Stream(messages, r.Model)
fmt.Println() // extra newline after streaming finishes
fmt.Println()
previousResults = append(previousResults, fmt.Sprintf("Step %d result:\n%s", step.Number, response))
}
// Final summary step
// Final summary
fmt.Println("Final Summary")
finalPrompt := fmt.Sprintf(`You just executed the entire recipe. Here is the full history:
%s
%s`, strings.Join(previousResults, "\n\n"), r.Recipe.FinalSummaryPrompt)
%s`, strings.Join(previousResults, "\n\n---\n\n"), r.Recipe.FinalSummaryPrompt)
messages := []map[string]string{
{"role": "system", "content": "You are a precise, no-nonsense coding sous-chef."},
{"role": "system", "content": "You are Grok, built by xAI. You are a precise, expert programmer and refactoring assistant."},
{"role": "user", "content": finalPrompt},
}
r.Client.Stream(messages, r.Model)