feat(bot): Коммит — создание, замена и удаление файлов, черновик

- новый файл: имя → содержимое текстом (```-обёртка снимается) или документом
- новая папка (виртуальная, пока в ней нет файлов), замена с diff +N/−M, удаление
- валидация: имя, ≤ 100 КБ, UTF-8, без NUL; .py — ast.parse с выбором «исправить / всё равно»;
  .sourcecraft/, .tgit/, ruff.toml из бота не меняются
- DraftService: черновик в БД, 24 ч, ≤ 50 изменений; удаление нового файла убирает его из черновика
- дерево показывает черновик поверх репозитория: 🆕 / ✏️ / 🗑, размер — как будет после пуша
- экран черновика (A/M/D) с «↩️ убрать»

Refs #26

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 19:18:49 +03:00
co-authored by Claude Opus 5.5
parent dbb17bd774
commit 614f8eafd5
11 changed files with 951 additions and 91 deletions
+2 -4
View File
@@ -87,13 +87,11 @@ async def test_pagination(tg: TgClient, texts: Texts) -> None:
assert labels(second.reply_markup)[0].startswith("📄 f20.txt") and texts.commit.prev in labels(second.reply_markup)
async def test_unfinished_actions_and_stale_buttons(tg: TgClient, texts: Texts) -> None:
async def test_stale_button_is_ignored(tg: TgClient, texts: Texts) -> None:
await tg.send(ADMIN, texts.menu.commit)
await tg.press(ADMIN, cb(Act.NEW_FILE))
await tg.press(ADMIN, cb(Act.OPEN, 99))
soon, stale = tg.session.sent(AnswerCallbackQuery)
assert soon.text == texts.commit.soon and soon.show_alert
[stale] = tg.session.sent(AnswerCallbackQuery)
assert stale.text is None
assert tg.session.sent(EditMessageText) == []