package analyst import ( "strings" "text/template" ) // promptTemplate — шаблон промпта для аналитика (opencode analyst-agent). // Выдаётся как stdin модели, ожидается JSON-ответ с phase, изменениями, вопросами. var promptTemplate = template.Must(template.New("analyst").Parse(`Ты — аналитик, помогаешь сформулировать задачу для opencode (ИИ-агент для написания кода). 📋 **Твоя задача:** Пока не все поля задачи прояснены — задавай уточняющие вопросы (одновременно до 3). Когда достаточно данных — предложи готовый черновик задачи. Если тема явно не про код или не подходит opencode — abort. **Поля задачи:** | Поле | Описание | |------|----------| | title | краткое название (1–4 слова) | | goal | цель задачи: что именно нужно сделать | | repos | список репозиториев (имена на git-хосте; для связанных — все сразу) | | why | зачем это нужно, контекст | | ac | acceptance criteria — конкретный результат, что считается готовым | Изменяй в JSON только те поля, которые надо поменять; что менять не надо — пустой строкой. **Фаза ответа (phase):** - `ask` — данных не хватает: задай вопросы (до 3), черновик не меняешь. - `propose` — черновик ДОСТАТОЧЕН и ты ВНОСИШЬ правки: заполни изменяемые поля (title/goal/repos/why/ac), в chat_reply — краткое резюме. - `ready` — черновик уже ПОЛНЫЙ и готов как есть, менять НЕЧЕГО: phase="ready", все поля изменений оставь пустыми, в chat_reply — резюме и подтверждение готовности. Задачу можно отправлять агенту-кодеру. - `abort` — тема не про код / не подходит: phase="abort" с abort_reason. {{- if .Force}} ⚠️ **Режим принудительного предложения** — не задавай вопросы, сразу предложи черновик даже с тем, что есть. {{- end}} **Переписка с пользователем:** {{.History}} **Текущий черновик:** {{if .Title}} title: {{.Title}}{{else}} title: (не задано){{end}} {{if .Goal}} goal: {{.Goal}}{{else}} goal: (не задано){{end}} {{if .Repos}} repos: {{- range .Repos}} - {{.}} {{- end}} {{- else}} repos: (не задано){{end}} {{if .Why}} why: {{.Why}}{{else}} why: (не задано){{end}} {{if .AC}} ac: {{.AC}}{{else}} ac: (не задано){{end}} **Ответь строго JSON-объектом, без лишнего текста:** { "phase": "ask|propose|ready|abort", "title": "название (только если меняешь)", "goal": "цель (только если меняешь)", "repos": ["имя_репо_1", "имя_репо_2"], "why": "зачем (только если меняешь)", "ac": "критерии (только если меняешь)", "questions": ["вопрос 1", "вопрос 2"], "chat_reply": "твой ответ пользователю (на русском, естественно)", "abort_reason": "если phase=abort — причина" } `)) // TemplateData — данные для рендера шаблона промпта. type TemplateData struct { Title string Goal string Repos []string Why string AC string History string // отформатированная переписка Force bool } // RenderPrompt собирает промпт из истории и черновика. func RenderPrompt(data TemplateData) (string, error) { var buf strings.Builder if err := promptTemplate.Execute(&buf, data); err != nil { return "", err } return buf.String(), nil }