Раздел: AI & Agents
Идентификатор:prompts· Экран:ui/prompts/(PromptsScreen, PromptsViewModel, PromptFormScreen)
Снимок: 2026-10-11
1. Зачем и что это
- Done when — критерий готовности, обязательные поля;
- Контекст — что именно нужно сделать.
Заготовка — не шаблон копипасты, а структурированный объект с валидацией: пока обязательное поле не заполнено, вставка в чат заблокирована.
Библиотека — файл на backend ~/.y83-ws/prompts.json. Локальной копии в приложении нет намеренно: экран показывает ошибку и кнопку «Обновить», если бэкенд недоступен. Тот же файл правят MCP-инструменты prompt_list / prompt_get / prompt_save.
Смысловая граница проекта: всегда применимое правило живёт в AGENTS.md, процедура — в скилле, формулировка задачи — в заготовке (docs/tech/prompts-library.md).
2. Как попасть
Хаб инструментов → секция AI & Agents → карточка «Prompts».
| Откуда ещё | Путь |
|---|---|
| Меню треда Codex | меню композера → «Prompts…» (сразу после «Skills…») |
| Чат | та же точка входа композера |
| Хаб (второй путь) | AI & Agents → Prompts — из хаба кнопка копирует текст в буфер |
Разница поведения зафиксирована в AGENTS.md: из чата кнопка вставляет текст в поле ввода (экран закрывается), из хаба — копирует в буфер обмена.
| Откуда ещё | Путь |
|---|---|
| Мост | app_navigate с screen: "prompts" |
3. Экран: элементы и действия
Список (PromptsScreen.kt, 400+ строк)
| Элемент | Что делает | Где в коде |
|---|---|---|
| Иконка поиска | Показывает поле «Поиск заготовок» | searchOpen |
Иконка Add | Новая заготовка | PromptFormScreen(editing = false) |
Иконка MoreVert | Меню: обновить, копировать JSON, импорт из буфера | ActionMenu |
| Поле поиска | Фильтр по названию/тегам/телу | OutlinedTextField |
| Ряд чипов тегов | Фильтр по тегу, первый чип — «все» | LazyRow + SelectChip |
| Секция «Встроенные» | Заготовки с builtin = true | prompts_built_in |
| Строка заготовки | Звезда ★ у закреплённых, название, строка-превью, счётчик N×, стрелка | PromptRow |
| Полоса статуса | Сообщения сохранения/ошибки, закреплена под шапкой | ToggleStatusStrip |
| Карточка ошибки | Текст ошибки бэкенда + «Обновить» | PromptsErrorCard |
Форма (PromptFormScreen.kt)
Два режима — «Заполнить» и «Правка».
| Элемент | Что делает |
|---|---|
| Поля формы | По одному на каждое fields; обязательные помечены required |
| Предпросмотр | Живой рендер renderPrompt с числом символов в заголовке секции |
| Строка «Заполни: …» | Список незаполненных обязательных полей |
| Кнопка «Вставить в чат» / «Копировать» | Переключается в зависимости от готовности |
| Поле «Название» | Только в режиме правки |
| Поле «Теги через запятую» | Только в режиме правки |
| Поле «Тело (цель, контекст, ограничения, done when)» | Только в режиме правки |
| Секция «Поля» + «Добавить поле» | Список редакторов полей |
| Кнопки «Создать» / «Сохранить», «Дублировать», «Удалить» | Только в режиме правки |
| Карточка «Не сохранено» | Список проблем валидации (ProblemsCard) |
| Диалог удаления | Подтверждение с предупреждением, что заготовку можно вернуть |
Редактор поля (FieldEditor): ключ, подпись, значение по умолчанию, чекбоксы required и multiline, кнопка удаления поля.
4. Возможности подробно
Структура заготовки
PromptItemDto (data/api/PromptDtos.kt:21):
| Поле | Смысл |
|---|---|
id | стабильный идентификатор (латиница) |
title | название |
tags | список тегов, по ним фильтр |
icon | имя иконочной строки |
pinned | закрепление, в списке ★ |
order | порядок в списке |
uses | счётчик использований, в списке N× |
lastUsedAt | время последнего использования |
fields | поля подстановки |
body | текст с плейсхолдерами {{ключ}} |
builtin | встроенная заготовка |
Плейсхолдеры и зарезервированные ключи
Плейсхолдер — {{ключ}}, регексп \{\{([a-z0-9_]+)\}\}. Зарезервированные ключи PromptsRules.RESERVED = ["project", "thread", "model"] подставляет приложение из контекста треда — объявлять их как поля нельзя, но использовать в теле можно.
Рендер и пропуск строк
renderPrompt(item, args) собирает готовый текст:
- для каждого поля берётся значение из аргументов, при пустом —
default; - обязательное поле с пустым значением попадает в
missing; - зарезервированные ключи берутся из аргументов напрямую;
- построчная подстановка, и сверх того убираются «дыры»:
- строка с плейсхолдером, которая после подстановки пуста, выбрасывается;
- строка, где после подстановки осталось только «Метка:» (подстановка с пустыми значениями совпала с результатом и кончается двоеточием), тоже выбрасывается — незаполненное необязательное поле не должно оставлять хвост;
- лишние пустые строки не дублируются;
- результат —
PromptRender(text, missing),ready = missing.isEmpty().
Именно ready решает судьбу главной кнопки: пока есть незаполненные обязательные поля, доступна «Копировать», а не «Вставить в чат».
Валидация (зеркало бэкенда)
promptProblems(doc) возвращает список проблем до отправки — чтобы не ждать 400 от сервера. Формулировки совпадают с бэкендом (backend/internal/prompts). Проверяется:
| Правило | Значение |
|---|---|
MAX_ITEMS | 200 заготовок на документ |
MAX_FIELDS | 12 полей на заготовку |
MAX_BODY_BYTES | 8000 байт тела |
MAX_TITLE_CHARS | 80 символов названия |
| id | ^[a-z0-9][a-z0-9_-]{0,47}$, уникальный, без пробелов |
| ключ поля | ^[a-z][a-z0-9_]{0,31}$, уникальный, не зарезервирован |
| плейсхолдер в теле | объявлен как поле или зарезервирован |
| обязательное поле | встречается в теле |
| подпись поля | не пустая |
Счётчик использований
markUsed(id) сначала увеличивает uses локально (мгновенно), потом шлёт markUsed(id) в фон. Это сделано, чтобы вставка текста не ждала сеть — закоммитить вставку важнее, чем обновить счётчик.
Дубликат
duplicateItem берёт id из заголовка с суффиксом «(копия)» через promptSlug(title, taken) (латиница из заголовка, максимум 40 символов, при занятости — -2, -3…), обнуляет uses, снимает закрепление.
Встроенные заготовки
restoreBuiltins() вызывает POST /api/v1/prompts/seed — возвращает в файл встроенные заготовки, которых там нет. Ключевая деталь: правки существующих заготовок не затираются, добавляются только отсутствующие. Если все уже на месте, приходит сообщение «все встроенные уже на месте».
Импорт и экспорт
Через меню «Ещё»:
- Копировать JSON — весь документ в буфер обмена;
- Импорт из буфера — читает буфер, парсит как
PromptDocDtoи заменяет библиотеку целиком (не дополняет). Пустой буфер — предупреждение, не JSON — ошибка, валидный документ с проблемами — показывается первая проблема.
5. Данные: БД, файлы, настройки
| Что | Где лежит | Как меняется | Кто читает |
|---|---|---|---|
| Библиотека | ~/.y83-ws/prompts.json (backend) | PUT /api/v1/prompts | экран, prompt_get, композер чата |
| Встроенные заготовки | Код backend/internal/prompts/builtin.go | POST /api/v1/prompts/seed | Материализуются в файл один раз |
Локального кэша и Room-таблицы нет. Backend при старте, если файла нет, заводит встроенные заготовки сам (NewService: «при отсутствии — заводит встроенные»).
6. Backend: REST/роуты
| Метод и путь | Назначение | Обработчик |
|---|---|---|
GET /api/v1/prompts | весь документ | prmH.Doc |
PUT /api/v1/prompts | сохранить документ (с валидацией) | prmH.Doc |
POST /api/v1/prompts/seed | добавить встроенные заготовки | prmH.Seed |
POST /api/v1/prompts/{id}/used | увеличить счётчик использования | prmH.Used |
Роуты: backend/internal/api/v1/routes.go:162–165. Логика и валидация — backend/internal/prompts/prompts.go (DefaultPath → ~/.y83-ws/prompts.json).
7. Мост и MCP
| Инструмент | Что делает |
|---|---|
prompt_list {q, tag} | список заготовок с id, заголовком, тегами, полями |
prompt_get {id, args} | подставить значения в текст; возвращает missing для незаполненных обязательных полей |
prompt_save {title, body, fields, id, tags, icon, order, pinned} | создать или изменить; валидация на сервере |
Метод prompt_get сознательно не генерирует id: незаполненные обязательные поля возвращаются как missing, заполненные — уже в text. Агент может дописать значения сам.
8. Настройки, конфиг, квоты и лимиты
| Параметр | Значение | Где задаётся |
|---|---|---|
| Максимум заготовок | 200 | PromptsRules.MAX_ITEMS |
| Максимум полей | 12 | PromptsRules.MAX_FIELDS |
| Максимум тела | 8000 байт | PromptsRules.MAX_BODY_BYTES |
| Максимум названия | 80 символов | PromptsRules.MAX_TITLE_CHARS |
| Зарезервированные ключи | project, thread, model | PromptsRules.RESERVED |
| Длина id | до 48 символов | PromptsRules.ID |
9. Ловушки и ограничения
- Импорт заменяет библиотеку целиком. Это не слияние: если в буфере лежит документ из трёх заготовок, после импорта в библиотеке останутся только они.
- Валидация дублируется на клиенте и сервере. Правила держатся синхронно, но при правке одной стороны вторая об этом не узнает — появится расхождение в формулировке ошибки, а не в её сути.
- Экран бесполезен без бэкенда. Локальной копии нет намеренно, поэтому при недоступном backend вы увидите только карточку ошибки с кнопкой «Обновить».
- Счётчик использований обновляется в фоне. Если сеть отвалилась сразу после вставки,
usesв файле не вырастет — но в UI уже показан. prompt_getне генерирует значения. Обязательные поля остаются незаполненными, это ожидаемое поведение, а не ошибка.- Зарезервированные ключи нельзя объявлять полями, но можно использовать в теле — они подставятся из контекста треда.
- Название важнее тела для id.
promptSlugстроит id из латиницы заголовка; русское название без латиницы дастprompt-N. - Правки встроенных заготовок стираются? Нет:
seedдобавляет только отсутствующие, существующие не трогает. Но это не значит, что изменения переживут обновление приложения, если backend их не переживёт — файл один.
10. Источники правды
app/src/main/java/com/y83/ws/ui/prompts/PromptsScreen.kt— список, поиск, теги, меню.app/src/main/java/com/y83/ws/ui/prompts/PromptFormScreen.kt— режимы правки и заполнения.app/src/main/java/com/y83/ws/ui/prompts/PromptsViewModel.kt— сохранение, seed, импорт, счётчик.app/src/main/java/com/y83/ws/data/prompts/PromptsRender.kt— рендер, валидация, slug.app/src/main/java/com/y83/ws/data/api/PromptDtos.kt— DTO.app/src/main/java/com/y83/ws/data/repo/PromptsRepository.kt— клиент REST.backend/internal/prompts/prompts.go,builtin.go— файл, валидация, встроенные.backend/internal/api/v1/routes.go:162–165— роуты.docs/tech/prompts-library.md— границы «правило / процедура / формулировка».