package worker import ( "strings" "text/template" "github.com/kamelion/ratatoskr-go/internal/storage" ) // devPromptTemplate — промпт для dev-агента при запуске задачи. var devPromptTemplate = template.Must(template.New("dev").Parse(`Ты — dev-агент, реализуешь задачу в репозитории(ях). **Задача:** {{if .Title}}Название: {{.Title}}{{end}} {{if .Goal}}Цель: {{.Goal}}{{end}} {{if .Repos}} Репозитории: {{- range .Repos}} - {{.}} {{- end}} {{end}} {{if .Primary}} Основной репозиторий (рабочий каталог этой сессии): {{.Primary}} {{end}} {{if .Why}}Зачем: {{.Why}}{{end}} {{if .AC}}Критерии готовности: {{.AC}}{{end}} {{if .Steps}} **Этапы (выполняй по порядку, у каждого свой критерий готовности):** {{- range .Steps}} {{.Title}}{{if .AC}} — готово, когда: {{.AC}}{{end}} {{- end}} {{end}} **Инструкции:** 1. Рабочий каталог — сам основной репозиторий ({{.Primary}}): правь файлы в нём прямо, относительными путями (./...). {{if .Others}} Связанные репозитории задачи лежат рядом, в ../имя_репо/ (здесь cwd — основной репозиторий): меняй их согласованно, если этого требует задача.{{else}} Работа ведётся только в нём — связанных репозиториев у задачи нет.{{end}} 2. Напиши код, реализующий задачу. 3. Убедись, что все acceptance criteria выполнены. 4. Пользуйся встроенными инструментами opencode (чтение файлов, поиск, редактирование). 5. Работай в feature-ветке {{.Branch}} (уже создана, checkout выполнен). Коммить изменения в эту ветку. 6. По окончании верни краткий отчёт о том, что сделано. {{if .ReviewFeedback}}{{.ReviewFeedback}}{{end}}`)) // DevPromptData — данные для рендера dev-промпта. type DevPromptData struct { Title string Goal string Repos []string // Primary — основной (первый) репозиторий задачи: рабочий каталог сессии // dev — это именно его папка (сервер opencode стартует из неё). Primary string // Others — остальные репозитории задачи (доступны рядом как ../имя_репо/). Others []string Why string AC string Steps []storage.Step // ReviewFeedback — замечания ревьюера при повторном прогоне dev // (не пусто → dev должен исправить именно это). ReviewFeedback []string // Branch — имя feature-ветки, в которой ведётся работа (для отчёта). Branch string } // RenderDevPrompt собирает промпт для dev-агента. func RenderDevPrompt(data DevPromptData) (string, error) { var buf strings.Builder if err := devPromptTemplate.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil } // reviewPromptTemplate — промпт для reviewer-агента. var reviewPromptTemplate = template.Must(template.New("review").Parse(`Ты — ревьюер в конвейере Ratatoskr. Проверь работу dev-агента в feature-ветке **{{.Branch}}** строго и придирчиво. **Критерии готовности (AC):** {{.AC}} **Полный diff ветки (base...HEAD):** {{.Diff}} ПРАВИЛА: 1. Изучи весь diff, а не заголовки. Смотри контекст изменений. 2. Проверяй: безопасность; логические ошибки и баги; соответствие AC; качество кода. 3. SOLID — СТРОГО. Приоритет — минимальная связанность компонентов: зависимость от абстракций, без циклических/лишних зависимостей, single responsibility. 4. Любой критический изъян (security/логика) или нарушение SOLID → passed=false. Это блокирующие. 5. critical_issues и solid_violations клади в свои списки; comments — конкретные, где и как править. **Верни строго один JSON-объект без markdown-обрамления:** { "passed": true|false, "critical_issues": [], "solid_violations": [], "comments": [] }`)) // ReviewPromptData — данные для рендера review-промпта. type ReviewPromptData struct { Branch string AC string Diff string } // RenderReviewPrompt собирает промпт для reviewer-агента. func RenderReviewPrompt(data ReviewPromptData) (string, error) { var buf strings.Builder if err := reviewPromptTemplate.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil } // reviewFeedbackText — строка (список), которую dev видит при повторном прогоне // после не-прохода ревью. Пустые comments → пустая строка. func reviewFeedbackText(branch string, comments []string) string { if len(comments) == 0 { return "" } var b strings.Builder b.WriteString("\n\n⚠ РЕВЬЮ НЕ ПРОЙДЕНО по ветке ") b.WriteString(branch) b.WriteString(". Прочитай и исправь каждый пункт, затем закоммить повторно:\n") for _, c := range comments { if c == "" { continue } b.WriteString("- ") b.WriteString(c) b.WriteString("\n") } return b.String() } // reviewFeedbackList — то же, но возвращает []string (для DevPromptData.ReviewFeedback). func reviewFeedbackList(branch string, comments []string) []string { s := reviewFeedbackText(branch, comments) if s == "" { return nil } return []string{s} }