/** * Prompt builder — deterministic prompt construction for all workflow agents. * * All prompts are concise, include metadata inline, and instruct agents * to read source files by path (not inlined). */ import type { TaskExecState } from "./orchestrator-state.js"; import type { ReviewFindings } from "../review-parser.js"; import { formatFindings } from "../review-parser.js"; // --- Scout --- export function buildScoutPrompt(cwd: string): string { return [ `Read .pi/context.md if present in ${cwd}.`, `Summarize: tech stack, directory layout (3 levels), key entry points, test conventions.`, `Max 500 words.`, ].join("\n"); } // --- Planner --- export function buildPlannerPrompt(scoutOutput: string, userDescription: string, planPath: string): string { return [ `Write a plan file to ${planPath}.`, ``, `## User request`, userDescription, ``, `## Project context`, scoutOutput, ``, `## Instructions`, `The plan must contain a \`\`\`superteam-tasks block with YAML task list.`, `Each task needs: title, description, files.`, `Keep tasks small: 1-3 files, 2-5 min each.`, `Use TDD — each task should mention writing tests first.`, `Include a Goal, Architecture, and Tech Stack header before the tasks.`, ].join("\n"); } // --- Plan revision --- export function buildPlanRevisionPrompt(planContent: string, findings: string): string { return [ `Revise this plan based on review findings. Write the updated plan to the same file.`, `Keep the \`\`\`superteam-tasks block.`, ``, `## Current plan`, planContent, ``, `## Review findings`, findings, ].join("\n"); } // --- Plan review --- export function buildPlanReviewPrompt(planContent: string, reviewType: "architect" | "spec", designContent?: string): string { const instructions = reviewType === "architect" ? `Check design, modularity, and task ordering. Are dependencies correct? Is the architecture sound?` : `Check completeness, task independence, and file coverage. Can each task be done standalone? Are all files covered?`; const parts = [ `Review this implementation plan (${reviewType} review).`, ``, ``, planContent, ``, ``, ]; if (designContent) { parts.push(``, designContent, ``, ``); parts.push(`Validate the plan against the approved design above.`, ``); } parts.push(instructions); return parts.join("\n"); } // --- Implementation --- export function buildImplPrompt( task: TaskExecState, planContext: string, previousTaskSummary?: { title: string; status: string; changedFiles: string[] }, ): string { const parts = [ `## Task: ${task.title}`, ``, task.description, ``, `## Files`, task.files.map((f) => `- ${f}`).join("\n"), ``, `## Plan context`, planContext, ``, ]; if (previousTaskSummary) { parts.push( `## Previous task`, `**${previousTaskSummary.title}** — ${previousTaskSummary.status}`, `Changed files:`, previousTaskSummary.changedFiles.map((f) => `- ${f}`).join("\n"), ``, ); } parts.push( `## Process`, `Use strict TDD: write a failing test first, implement minimally, refactor.`, `Commit after each green cycle.`, `Self-review your changes before reporting done.`, ); return parts.join("\n"); } // --- Fix --- export function buildFixPrompt(task: TaskExecState, reviewType: string, findings: ReviewFindings, changedFiles: string[]): string { return [ `Fix these ${reviewType} review findings for task "${task.title}".`, ``, formatFindings(findings, reviewType), ``, `## Changed files`, changedFiles.map((f) => `- ${f}`).join("\n"), ``, `Update tests if needed. Use TDD — fix failing tests first if any.`, ].join("\n"); } // --- Spec review --- export function buildSpecReviewPrompt(task: TaskExecState, changedFiles: string[]): string { return [ `## Spec review for: ${task.title}`, ``, `### Task spec`, task.description, ``, `### Files to read`, changedFiles.map((f) => `- ${f}`).join("\n"), ``, `Only review files listed below — do not review test files unless the task description explicitly targets test code.`, `Read these files. Compare implementation against spec.`, `Do NOT trust the implementer's self-report — verify independently.`, ].join("\n"); } // --- Quality review --- export function buildQualityReviewPrompt(task: TaskExecState, changedFiles: string[]): string { return [ `## Quality review for: ${task.title}`, ``, `Review code quality in these files:`, changedFiles.map((f) => `- ${f}`).join("\n"), ``, `Check: naming, DRY, error handling, test quality.`, ].join("\n"); } // --- Final review --- export function buildFinalReviewPrompt(completedTasks: TaskExecState[], changedFiles: string[]): string { const taskSummary = completedTasks.map((t) => `- ${t.title}: ${t.description.split("\n")[0]}`).join("\n"); return [ `## Final review`, ``, `### Completed tasks`, taskSummary, ``, `### Changed files`, changedFiles.map((f) => `- ${f}`).join("\n"), ``, `Review the full implementation across these files.`, `Check cross-task integration, consistency, and completeness.`, ].join("\n"); } // --- Brainstorm --- import type { BrainstormQuestion, BrainstormApproach, DesignSection } from "./orchestrator-state.js"; export function buildBrainstormQuestionsPrompt(scoutOutput: string, userDescription: string): string { return [ `## Task: Generate clarifying questions`, ``, `The user wants: ${userDescription}`, ``, `## Project context (from scout)`, scoutOutput, ``, `## Instructions`, `Generate 3-7 focused questions to clarify requirements, constraints, and preferences.`, `Use "choice" type with options for questions where common choices exist.`, `Use "input" type for open-ended questions.`, ``, `Return a \`\`\`superteam-brainstorm block with type "questions".`, ``, `IMPORTANT: In your JSON output, never use literal newlines inside string values. Use \\n escape sequences instead.`, ].join("\n"); } export function buildBrainstormApproachesPrompt( scoutOutput: string, userDescription: string, questionsAndAnswers: BrainstormQuestion[], ): string { const qaLines = questionsAndAnswers.map((q) => `- **${q.text}** → ${q.answer || "(no answer)"}`).join("\n"); return [ `## Task: Propose implementation approaches`, ``, `The user wants: ${userDescription}`, ``, `## Requirements (from Q&A)`, qaLines, ``, `## Project context (from scout)`, scoutOutput, ``, `## Instructions`, `Propose 2-3 distinct implementation approaches.`, `For each: title, summary, tradeoffs, estimated task count.`, `Provide a recommendation with reasoning.`, ``, `Return a \`\`\`superteam-brainstorm block with type "approaches".`, ``, `IMPORTANT: In your JSON output, never use literal newlines inside string values. Use \\n escape sequences instead.`, ].join("\n"); } export function buildBrainstormDesignPrompt( scoutOutput: string, userDescription: string, questionsAndAnswers: BrainstormQuestion[], chosenApproach: BrainstormApproach, ): string { const qaLines = questionsAndAnswers.map((q) => `- **${q.text}** → ${q.answer || "(no answer)"}`).join("\n"); return [ `## Task: Write detailed design sections`, ``, `The user wants: ${userDescription}`, `Chosen approach: **${chosenApproach.title}** — ${chosenApproach.summary}`, ``, `## Requirements (from Q&A)`, qaLines, ``, `## Project context (from scout)`, scoutOutput, ``, `## Instructions`, `Write 3-6 design sections covering: architecture, components, data flow, error handling, testing approach.`, `Each section: 200-300 words, specific file paths and function names.`, ``, `Return a \`\`\`superteam-brainstorm block with type "design".`, ``, `IMPORTANT: In your JSON output, never use literal newlines inside string values. Use \\n escape sequences instead.`, ].join("\n"); } export function buildBrainstormSectionRevisionPrompt( section: DesignSection, feedback: string, context: string, ): string { return [ `## Task: Revise design section`, ``, `## Current section: ${section.title}`, section.content, ``, `## User feedback`, feedback, ``, `## Context`, context, ``, `## Instructions`, `Revise this section based on the feedback. Keep the same id and title.`, `Return a \`\`\`superteam-brainstorm block with type "design" containing the revised section.`, ``, `IMPORTANT: In your JSON output, never use literal newlines inside string values. Use \\n escape sequences instead.`, ].join("\n"); } // --- Plan-write --- export function buildPlannerPromptFromDesign( designContent: string, scoutOutput: string, userDescription: string, planFilePath: string, ): string { return [ `Write a detailed TDD implementation plan to ${planFilePath}.`, ``, `## User request`, userDescription, ``, `## Approved design`, designContent, ``, `## Project context (from scout)`, scoutOutput, ``, `## Instructions`, `The plan must contain a \`\`\`superteam-tasks block with YAML task list.`, `Each task needs: title, description, files.`, `Keep tasks small: 1-3 files, 2-5 min each.`, `Use TDD — each task should mention writing tests first.`, `Include complete test code inline in task descriptions.`, `Include exact file paths and verification commands.`, ].join("\n"); } // --- Plan revision from findings --- export function buildPlanRevisionPromptFromFindings( planContent: string, designContent: string, findingsText: string, ): string { return [ `Revise this plan based on review findings. Write the updated plan to the same file.`, `Keep the \`\`\`superteam-tasks block.`, ``, `## Current plan`, planContent, ``, `## Approved design`, designContent, ``, `## Review findings`, findingsText, ].join("\n"); } // --- Targeted plan revision --- export function buildTargetedPlanRevisionPrompt( planContent: string, findings: string, designContent: string, ): string { return [ `Revise this plan based on review findings. Write the updated plan to the same file.`, ``, `## IMPORTANT: Targeted edits only`, `- Edit ONLY the tasks mentioned or referenced in the findings below.`, `- Do NOT rewrite other tasks — leave them exactly as they are.`, `- Preserve existing task IDs and ordering unless findings specifically require changes.`, `- Keep the \`\`\`superteam-tasks block intact with all tasks.`, ``, `## Current plan`, planContent, ``, `## Approved design`, designContent, ``, `## Review findings`, findings, ].join("\n"); } // --- Utilities --- export function extractPlanContext(planContent: string): string { const marker = "```superteam-tasks"; const idx = planContent.indexOf(marker); if (idx === -1) return planContent; return planContent.slice(0, idx).trim(); }