C очередью web UI, Telegram-ботом и продолжением диалога

Я собрал инструмент, который решает несколько моих конкретных болей при работе с AI CLI — Claude Code, Codex, Qwen. Не абстрактных, а тех, на которые реально натыкаешься каждый день.

PromptPilot — очередь задач для AI CLI. Отправляешь промпт, воркер его выполняет. Запланировать задачу можно из командной строки, в веб-интерфейсе либо через Telegram-бота. Поддерживает планирование, приоритеты, retry при rate limit и продолжение диалога прямо из бота.

Содержание

Реальные кейсы

Вот конкретные ситуации, под которые писался проект. Возможно, что-то узнаешь:

Кейс 0. Упущенная прибыль и душевные терзания от непотраченных ресурсов

«Токены в конце месяца похожи на снег в апреле: они напоминают о зиме, но уже совершенно бесполезны» — Луций Сенека.

Накупили подписок, но в грандиозные планы вмешалась суровая реальность? Завтра сгорят токены — не буду спать, пока не потрачу их на всё своё TODO.

Теперь можно спать, распланировав пул задач, чтобы максимально потратить свои квоты.

Напишите в комментах: это чисто мой кейс и у меня расстройство AI-поведения, или у вас так же.

Кейс 1. «Разогреть» лимит до начала рабочего дня

Есть статья, где предлагается хак для удвоения лимита Claude: запускать сессию за 3–4 часа до начала кодирования. Это работает, но требует быть за компом в нужный момент (а ведь кто-то кодит сразу, как только проснулся).

У меня другой подход. Я планирую задачу на 5 утра — любой лёгкий промпт вроде "hi" или просьбу сгенерировать документацию:

pp add "Напиши подробную документацию к модулю auth.py" \
  --dir C:\Projects\MyApp \
  -a "2026-04-01T05:00"

или в UI

Воркер сам стартует сессию в 5:00. В 9:00 я начинаю работать — и использую лимит примерно за час, а в 10 он сбросится и будет свежим.

В Telegram-боте то же самое через кнопки: ➕ Добавить задачу → промпт → директория → выбрать расписание +8ч.

Кейс 2. Лимит кончился поздно вечером — поставить задачу на сброс

Сидишь поздно, Claude говорит, что лимит исчерпан.

Планируем продолжение банкета на время после сброса лимита и идём спать.

pp add "Продолжи рефакторинг, осталось разобрать worker.py" \
  --dir C:\Projects\PromptPilot \
  -a "2026-03-29T02:20"

Утром задача выполнена, а лимиты свежие.

Кейс 3. Ты не дома, но хочешь подготовить лимит к кодингу

Пришла гениальная идея, чувствуешь, что всю ночь будешь кодить. Отправляешь задачу, чтобы по возвращении максимально эффективно воспользоваться лимитами.

➕ Добавить задачу
→ "Проведи аудит кода в папке src/, найди потенциальные проблемы"
→ провайдер: claude
→ приоритет: 5
→ директория: MyProject
→ расписание: +1ч

Воркер запустит задачу через час — к тому времени, когда ты дойдёшь до компа, лимит уже будет частично использован и скоро обновится. Плюс задача сделана.

Кейс 4. Появились мысли — доработать проект удалённо

Едешь в такси, пришли идеи. Открываешь Telegram-бот, создаёшь задачу прямо с телефона:

"В модуле payments.py добавь валидацию суммы: должна быть > 0 и не превышать 1 000 000.
 Напиши тесты. Используй существующий стиль в тестах."

Выбираешь проект из списка (если задана PP_PROJECTS_ROOT), провайдера, жмёшь «Отправить». К приезду домой — готовый коммит. Или не готовый — и тогда Claude задаст вопрос, и ты ответишь прямо из бота (об этом дальше).

Кейс 5. Скилы Claude Code через Telegram

У меня есть скил для формирования приглашений на демо — demo-invite.md в .claude/skills/ проекта. Раньше нужно было открывать терминал, помнить синтаксис. Теперь:

В боте: /skills → выбираешь demo-invite → вводишь аргументы → задача создана.

Скилы работают и в Web UI — при выборе Claude-провайдера появляется кнопка ⚡ Skills.

Архитектура

Схема: интерфейсы → SQLite → воркер → CLI
┌─────────────────────────────────────────┐
│            Интерфейсы                   │
│  CLI (pp add)  │  Web UI  │  TG Bot     │
└────────────────┴──────────┴─────────────┘
                     │ создают задачи
                     ▼
            ┌─────────────────┐
            │   SQLite (WAL)  │
            │  ~/.promptpilot │
            └────────┬────────┘
                     │ читает очередь
                     ▼
            ┌─────────────────┐
            │     Worker      │  ← опрос каждые 5с
            └────────┬────────┘
                     │ subprocess
          ┌──────────┼──────────┐
          ▼          ▼          ▼
     claude.exe    codex      qwen       

Три независимых процесса через одну SQLite-базу в WAL-режиме:

  • Worker — опрашивает очередь, запускает задачу через subprocess, сохраняет результат. Намеренно однопоточный: одна задача в момент времени, никаких гонок за API-лимит.

  • Server — FastAPI + Web UI на чистом JS. Принимает задачи через REST.

  • Bot — Telegram-бот, полный менеджмент задач через чат.

Все три можно запускать независимо или вместе через tray.

Установка

git clone https://github.com/your/promptpilot
cd PromptPilot
pip install -e .

Требования: Python 3.10+, хотя бы один AI CLI в PATH.

Создай .env в папке проекта (или рядом с pp.exe):

# Telegram бот (опционально)
PP_TG_TOKEN=7123456789:AAF...
PP_TG_ALLOWED_PHONES=+79001234567  Для авторизации бота
PP_TASK_PASSWORD=12345  Вдруг другие вайбкодеры украдут телефон уже авторизованный   

# Claude
PP_CLAUDE_EXE=C:\Users\username\.local\bin\claude.exe
PP_DEFAULT_CLI=claude

# Папка с проектами — для выбора директории кнопками в боте/веб
PP_PROJECTS_ROOT=C:\Projects

# Таймаут и retry
PP_TASK_TIMEOUT=300
PP_BASE_DELAY=60
PP_MAX_DELAY=3600

Запуск: CLI, tray и .exe

Вариант 1 — CLI (для разработки и серверов)

# Три процесса в трёх терминалах
pp worker       # выполняет задачи
pp server       # веб-интерфейс http://127.0.0.1:8420
pp bot          # Telegram-бот

# Или воркер + сервер одной командой
pp start

Вариант 2 — PowerShell-скрипт

.\start.ps1     # воркер + сервер; если PP_TG_TOKEN задан в .env — бот тоже запустится автоматически
.\start.ps1 -Bot  # явно запустить бот, даже если PP_TG_TOKEN не в .env (нужен в переменных окружения)

.\stop.ps1      # остановить все запущенные сервисы

Вариант 3 — Tray (.exe, рекомендуется на Windows)

Двойной клик на pp.exe — иконка в трее. Воркер и сервер стартуют автоматически. Иконка меняет цвет: 🟢 всё работает / 🟠 частично / ⚫ остановлено.

Чтобы собрать .exe самому:

.\build.ps1
# результат: dist\pp.exe (~40 МБ, Python внутри, зависимостей не нужно)

PyInstaller упаковывает Python, все зависимости и статику в один файл. На целевой машине ничего устанавливать не нужно — только .env рядом с pp.exe.

Нюанс с .env: load_dotenv() читает файл один раз при запуске. Порядок поиска: папка рядом с pp.exe → рабочая директория → ~/.promptpilot/.env. Переменные окружения системы имеют приоритет над .env.

Интерфейсы: CLI, Web UI, Telegram-бот

CLI

Самый быстрый способ добавить задачу:

# Простая задача
pp add "Отрефактори модуль db.py, избавься от дублирования"

# С директорией и приоритетом
pp add "Напиши тесты для api.py" \
  --dir C:\Projects\PromptPilot \
  -p 1

# Запланировать на конкретное время
pp add "Сгенерируй отчёт за месяц" \
  -a "2026-04-01T09:00"

# Использовать другой провайдер
pp add "Explain this codebase" --provider codex

# Пакетная загрузка из файла (каждая строка — задача)
pp add --file prompts.txt

# Список задач
pp list
pp list --status pending
pp list --status completed --limit 5

Web UI

Минималистичный интерфейс на http://127.0.0.1:8420:

  • Выбор провайдера → для Claude совместимых автоматически появляется кнопка ⚡ Skills

  • Промпт (Ctrl+Enter для отправки)

  • Чекбокс --dangerously-skip-permissions

  • Пресеты расписания (+1ч / +3ч / +8ч / +24ч) или ручной ввод ISO-даты

  • Фильтры по статусу с бейджами-счётчиками

  • Раскрытие задачи — полный результат, метаданные (модель, стоимость, токены)

  • Автообновление каждые 5 секунд

Весь UI — один HTML-файл, ванильный JS, никаких npm-зависимостей.

Telegram-бот

Авторизация — без паролей в чате. Пользователь жмёт «Поделиться контактом», Telegram передаёт номер, бот сверяет с PP_TG_ALLOWED_PHONES. Авторизованные пользователи сохраняются в JSON — при перезапуске авторизация не слетает.

Опционально: PP_TASK_PASSWORD — пользователь вводит пароль один раз за сессию перед созданием задачи.

Создание задачи в боте — пошаговый сценарий
Промпт
  → Провайдер [claude | claude-z | codex | qwen | ...]
    → Приоритет [1..10]
      → skip-permissions [Да / Нет]
        → Директория [список из PP_PROJECTS_ROOT + "Ввести вручную"]
          → Расписание [+1ч / +3ч / +8ч / +24ч / Ввести / Без расписания]
            ✅ Задача #N добавлена!

Список задач — пагинация по 5, кнопки перехода, клик на задачу — детали.

Пример списка задач
Пример экрана деталей задачи в боте
📋 Задача #42
Статус: ✅ completed
Провайдер: claude
Модель: claude-opus-4

Промпт:
Добавь валидацию суммы в payments.py...

Результат:
Добавил проверку в строке 47: if amount <= 0 or amount > 1_000_000...

Model: claude-opus-4
Cost: $0.0063
Time: 18.4s
Tokens: 312 in / 891 out
Rate limit resets: 2026-03-29 02:17

[❌ Отменить] [💬 Ответить] [🗑 Удалить]
Пример экрана со статистикой и списком провайдеров

Rate limits и exponential backoff

Воркер определяет rate limit по stderr или по событиям stream-json:

RATE_LIMIT_PATTERNS = [
    "rate limit", "rate_limit", "ratelimit",
    "overloaded", "too many requests", "429",
    "quota exceeded", "capacity", "try again later",
]

При срабатывании задача переходит в rate_limited и уходит в exponential backoff:

def compute_next_run(retry_count: int) -> datetime:
    delay = min(BASE_DELAY * (2 ** retry_count), MAX_DELAY)
    jitter = delay * 0.1 * (random.random() * 2 - 1)
    return datetime.now(timezone.utc) + timedelta(seconds=delay + jitter)

Задержки: 60с → 120с → 240с → 480с → … → 3600с. Джиттер ±10% — чтобы не молотить синхронно, если несколько задач rate-limited одновременно. После max_retries попыток — failed.

Воркер при запуске вызывает db.recover_running() — все задачи со статусом running (зависшие при прошлом краше) откатываются в pending. Crash recovery из коробки.

Продолжение диалога — reply в боте

Claude Code может задать вопрос в середине задачи:

«Нашёл два файла: auth.py и auth_v2.py. Какой актуальный?»

Задача завершается — Claude ждёт ответа. Но терминала нет, сессия закрыта.

Теперь в деталях завершённой задачи есть кнопка 💬 Ответить:

Нажимаешь → вводишь «auth.py, auth_v2.py — устаревший черновик» → новая задача создаётся с флагом --resume <session_id>. Claude продолжает разговор в том же контексте, помнит всё, что было.

Как это работает под капотом
  1. Claude CLI возвращает session_id в stream-json-выводе (событие result)

  2. Worker сохраняет его в колонку tasks.session_id

  3. Кнопка 💬 появляется у завершённых задач с session_id

  4. Ответ создаёт задачу с session_id и parent_task_id от родительской

  5. Worker добавляет --resume session_id в команду

# config.py — build_cmd
extras = []
if session_id:
    extras += ["--resume", session_id]
if skip_permissions:
    extras.append("--dangerously-skip-permissions")
if extras:
    prompt_idx = cmd.index(prompt)
    cmd[prompt_idx:prompt_idx] = extras

Цепочка не ограничена — каждый ответ тоже сохраняет session_id и показывает 💬. Новая задача наследует провайдера, рабочую директорию и флаги от родительской.

[СКРИНШОТ: цепочка задач — #1 → #2 (ответ) → #3 (ответ на ответ)]

Скилы Claude Code

Скилы — это файлы .md в ~/.claude/commands/ или ~/.claude/skills/, в плагинах (~/.claude/plugins//commands/), а также в локальных папках проекта (.claude/commands/ или .claude/skills/). Claude Code вызывает их через /skill-name [args].

PromptPilot умеет читать эти файлы, показывать список и подставлять /skill-name в промпт.

Web UI: при выборе Claude-провайдера появляется кнопка ⚡ Skills. Клик → список с описаниями. Выбор скила подставляет /skill-name в поле промпта — дополняешь аргументами и отправляешь.

Telegram-бот: команда /skills или кнопка в меню:

/skills

⚡ Скилы (claude)

[commit] Create a git commit
[review-pr] Review a pull request
[demo-invite] Формирует приглашение на демо для клиента
[feature-dev] Guided feature development

→ [demo-invite]
Введите аргументы: ООО Ромашка, среда 14:00

✅ Задача #47 добавлена!

Скилы из рабочей директории проекта (workdir/.claude/commands/ или workdir/.claude/skills/) добавляются к глобальным — то есть у каждого проекта могут быть свои скилы, и они будут видны в боте при выборе этой директории.

При нажатии на кнопку "Скилы проекта" отображаются только те проекты, у которых есть скилы

Работает только с провайдерами claude и claude-z — для Codex и Qwen скилы не отображаются.

Провайдеры и кастомные команды

Встроенные:

Имя

Описание

Скилы

claude

Claude Code (Anthropic)

claude-z

Claude Code через GLM/z.ai

codex

OpenAI Codex (codex -q)

qwen

Qwen Code (qwen -p)

Кастомный провайдер:

pp provider add myai \
  --cmd "myai run {prompt}" \
  --desc "My AI Tool" \
  --env "MYAI_API_KEY=sk-..."

Провайдер — шаблон команды с {prompt}. Воркер подставляет промпт, добавляет флаги и запускает через subprocess.run с нужными переменными окружения.

Пример: кастомный claude-z через GLM (PowerShell)
pp provider add claude-z `
  --cmd "C:\Users\<username>\.local\bin\claude.exe -p --verbose --output-format stream-json {prompt}" `
  --desc "Claude Code (GLM via z.ai)" `
  --env "ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic" `
  --env "ANTHROPIC_AUTH_TOKEN=your-token-here" `
  --env "ANTHROPIC_DEFAULT_SONNET_MODEL=glm-4.7" `
  --env "ANTHROPIC_DEFAULT_OPUS_MODEL=glm-4.7"

Нюанс для Windows: если claude настроен как PowerShell-функция или алиас, subprocess его не найдёт — нужен полный путь к claude.exe.

Конфигурация

Таблица переменных окружения

Переменная

По умолчанию

Описание

PP_TG_TOKEN

Токен Telegram бота

PP_TG_ALLOWED_PHONES

Разрешённые номера через запятую

PP_TASK_PASSWORD

Пароль для создания задач в боте

PP_CLAUDE_EXE

~/.local/bin/claude.exe

Путь к claude

PP_DEFAULT_CLI

claude

Провайдер по умолчанию

PP_PROJECTS_ROOT

Корень проектов для выбора директории

PP_DATA_DIR

~/.promptpilot

Папка с БД и конфигами

PP_TASK_TIMEOUT

300

Таймаут задачи, секунд

PP_POLL_INTERVAL

5

Интервал опроса очереди, секунд

PP_BASE_DELAY

60

Начальная задержка retry

PP_MAX_DELAY

3600

Максимальная задержка retry

PP_MAX_RETRIES

5

Максимум попыток перед failed

PP_HOST

127.0.0.1

Хост веб-сервера

PP_PORT

8420

Порт веб-сервера

Структура репозитория (дерево файлов)
promptpilot/
├── config.py    — настройки, провайдеры, скилы, build_cmd
├── models.py    — Pydantic-модели (TaskCreate, TaskInDB)
├── db.py        — SQLite CRUD, миграции, очередь
├── worker.py    — subprocess, парсинг stream-json, retry
├── cli.py       — Click CLI (pp add, pp list, pp worker...)
├── api.py       — FastAPI REST
├── bot.py       — Telegram-бот (ConversationHandler, reply flow)
├── tg_auth.py   — авторизация по номеру телефона
├── tray.py      — системный трей (pystray)
└── static/
    └── index.html   — Web UI (ванильный JS, один файл)

Намеренно без тяжёлых зависимостей: SQLite вместо Postgres, один HTML-файл вместо React, subprocess вместо SDK. pip install + pp worker — и работает.

Что дальше

  • Уведомления в Telegram — сообщение, когда задача завершилась (или упала)

  • История диалогов — показывать цепочку задача → ответ → ответ в одном view

  • Параллельные воркеры — отдельный воркер для каждого провайдера

Итог

Если коротко: отправляешь промпт откуда угодно — с компа, телефона, по расписанию — воркер сам разберётся с rate limits, дождётся своей очереди, выполнит и отдаст результат. Если модель задала вопрос — отвечаешь прямо из Telegram, не открывая терминал.

Код на GitHub

Буду рад вопросам и фидбеку — особенно если используете что-то похожее или есть идеи, как улучшить.

Первая статья на ХАБРе, прошу не судить строго.