package main import ( "context" "encoding/json" "fmt" "strings" ) const managerSystemPrompt = `You are the Manager agent. You coordinate between the user, a Programmer agent, and a QA agent. You have two orchestration tools: - assign_task(instructions): delegate coding work to the Programmer. Returns the Programmer's report. - request_qa_review(focus): ask QA to validate specific files/changes. Returns QA's report. Workflow: 1. Read the user's request. If it is purely conversational or meta (a greeting, a question about the agent itself), answer directly with no tool calls. 2. Otherwise, plan the work, then call assign_task with clear, self-contained instructions for the Programmer. 3. When the Programmer reports back, call request_qa_review naming the files touched and what QA should verify. 4. If QA reports issues, call assign_task again with specific fix instructions that reference QA's findings. 5. Iterate until QA approves, then reply to the user with a concise summary of what changed and any caveats. Important: - You have NO file tools. Do not try to read or edit files yourself. - Every call to assign_task/request_qa_review spawns a fresh sub-agent with no memory of prior calls — put all needed context into the brief. - Keep the final user-facing reply brief: what was done, where, and anything flagged by QA.` func managerTools() []Tool { return []Tool{ { Type: "function", Function: ToolFunction{ Name: "assign_task", Description: "Delegate a coding task to the Programmer agent. The Programmer has read/create/edit/delete/list file tools scoped to the project root. Returns the Programmer's written report.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "instructions": map[string]any{ "type": "string", "description": "Clear, self-contained instructions for the Programmer. Include all context needed (files, goals, constraints).", }, }, "required": []string{"instructions"}, }, }, }, { Type: "function", Function: ToolFunction{ Name: "request_qa_review", Description: "Ask the QA agent to review code. QA has read-only file tools (read_file, list_directory) plus a shell command runner (run_command) that can execute any test suite, linter, type-checker, or build command. Returns QA's written report with a verdict.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "focus": map[string]any{ "type": "string", "description": "What QA should review: specific files, what aspect of the change, what to look for. Be explicit.", }, }, "required": []string{"focus"}, }, }, }, } } type Orchestrator struct { client *Client root string manager *Agent history []Message } func NewOrchestrator(client *Client, root string) *Orchestrator { o := &Orchestrator{client: client, root: root} o.manager = &Agent{ Name: "manager", Client: client, SystemPrompt: managerSystemPrompt, Tools: managerTools(), ToolExec: o.execManagerTool, OnToolCall: logToolCall(""), } o.history = []Message{{Role: "system", Content: managerSystemPrompt}} return o } // Handle advances the Manager conversation with one user turn. func (o *Orchestrator) Handle(ctx context.Context, userInput string) (string, error) { o.history = append(o.history, Message{Role: "user", Content: userInput}) reply, updated, err := o.manager.Run(ctx, o.history) o.history = updated return reply, err } func (o *Orchestrator) execManagerTool(ctx context.Context, name, argsJSON string) string { switch name { case "assign_task": var a struct { Instructions string `json:"instructions"` } if err := json.Unmarshal([]byte(argsJSON), &a); err != nil { return "error: invalid arguments: " + err.Error() } if strings.TrimSpace(a.Instructions) == "" { return "error: instructions is required" } prog := newProgrammerAgent(o.client, o.root, logToolCall(" ")) report, err := prog.Do(ctx, a.Instructions) if err != nil { return "error: programmer failed: " + err.Error() } printReport("programmer", report) return report case "request_qa_review": var a struct { Focus string `json:"focus"` } if err := json.Unmarshal([]byte(argsJSON), &a); err != nil { return "error: invalid arguments: " + err.Error() } if strings.TrimSpace(a.Focus) == "" { return "error: focus is required" } qa := newQAAgent(o.client, o.root, logToolCall(" ")) report, err := qa.Do(ctx, a.Focus) if err != nil { return "error: qa failed: " + err.Error() } printReport("qa", report) return report default: return "error: unknown tool " + name } } func logToolCall(indent string) func(string, ToolCall, string) { return func(agent string, tc ToolCall, _ string) { fmt.Printf("%s[%s] %s %s\n", indent, agent, tc.Function.Name, tc.Function.Arguments) } } func printReport(from, report string) { fmt.Printf(" [%s → manager]\n", from) for _, line := range strings.Split(strings.TrimRight(report, "\n"), "\n") { fmt.Printf(" %s\n", line) } }