GitLab
В этом руководстве описана настройка канала Qwen Code для мониторинга todos в GitLab и ответа на упоминания в issues и merge requests.
Предварительные требования
- Аккаунт GitLab (или выделенный аккаунт бота)
- Персональный токен доступа GitLab (Personal Access Token) со скоупами
read_apiиapi
Создание токена
- Перейдите в Preferences → Access Tokens
- Создайте токен со следующими скоупами:
- read_api — чтение todos и данных проекта
- api — публикация заметок (комментариев) к issues/MRs
- Сохраните токен в переменную окружения
Конфигурация
Добавьте канал в ~/.qwen/settings.json:
{
"channels": {
"my-gitlab": {
"type": "gitlab",
"token": "$GITLAB_TOKEN",
"pollInterval": 60000,
"senderPolicy": "open",
"sessionScope": "chat_thread",
"cwd": "/path/to/your/project",
"groupPolicy": "open",
"action_prompt_template": {
"mentioned": "Project: %project% | URL: %project_url% | Author: %author% | Type: %target_type% | IID: %iid% | Title: %title% | Description: %description% | TodoID: %todo_id%"
}
}
}
}Установите токен как переменную окружения:
export GITLAB_TOKEN="glpat-your_token_here"Self-hosted GitLab
Для self-hosted инстансов укажите baseUrl:
{
"baseUrl": "https://gitlab.example.com"
}Параметры конфигурации
| Параметр | Значение по умолчанию | Описание |
|---|---|---|
token | (обязательно) | PAT со скоупами read_api + api |
pollInterval | 60000 | Интервал опроса в мс |
baseUrl | https://gitlab.com | URL инстанса GitLab |
action_prompt_template | (обязательно для обработки) | Сопоставляет имена действий GitLab с шаблонами метаданных |
groupPolicy | "disabled" | Должен быть "open" или "allowlist" с указанием проекта |
senderPolicy | "allowlist" | Кто может запускать бота |
action_prompt_template
Это поле определяет, какие действия todo обрабатываются и как формируются метаданные. Обрабатываются только те действия, для которых задан шаблон; остальные пропускаются и помечаются как выполненные.
{
"action_prompt_template": {
"mentioned": "Project: %project% | Author: %author% | Title: %title%"
}
}Действие directly_addressed (комментарий, начинающийся с @bot) автоматически использует фолбэк на шаблон mentioned, если не настроено явно.
Доступные ключи действий
| Ключ | Триггер |
|---|---|
mentioned | Кто-то @упоминает бота в комментарии или описании (не в начале) |
directly_addressed | Комментарий начинается с @bot (фолбэк на шаблон mentioned) |
assigned | Кто-то назначает бота на issue/MR |
review_requested | Кто-то запрашивает бота как ревьюера на MR |
approval_required | MR требует одобрения бота (правила одобрения) |
marked | Кто-то помечает комментарий/issue/MR бота (звёздочка) |
build_failed | CI/CD пайплайн завершился ошибкой на ветке/MR бота |
unmergeable | MR, в котором участвует бот, становится неслияемым (конфликты) |
merge_train_removed | MR удалён из merge train |
Обрабатываются только те ключи, которые присутствуют в action_prompt_template. Необработанные действия пропускаются и молча помечаются как выполненные.
Переменные шаблона
| Переменная | Значение |
|---|---|
%project% | Путь проекта (например, owner/repo) |
%project_url% | Полный URL проекта |
%author% | Имя пользователя автора todo |
%target_type% | Issue или MergeRequest |
%iid% | Внутренний ID issue/MR |
%title% | Заголовок issue/MR |
%description% | Тело описания issue/MR |
%todo_id% | ID todo в GitLab |
%% | Литеральный символ % (экранирование) |
Неизвестные переменные сохраняются в выходных данных как есть.
Сборка промпта
Шаблон рендерится в envelope.metadata (структурированный контекст). Текст триггера (todo.body или описание) попадает в envelope.text (основной промпт). Базовый класс собирает итоговый промпт, отправляемый агенту:
[alice] please fix this bug
Project: owner/repo | URL: https://gitlab.com/owner/repo | Author: alice | Type: Issue | IID: 42 | Title: Test Issue | Description: ... | TodoID: 100- Строка 1: префикс
[sender]+envelope.text(с удалённым@bot) - Строка 3:
envelope.metadata(отрендеренный шаблон, очищенный)
Переменная %body% не нужна — текст комментария/описания всегда является основным содержимым промпта, а шаблон предоставляет дополнительный контекст ниже.
⚠️ Безопасность
В публичном проекте при senderPolicy: "open" любой пользователь GitLab, упомянувший бота через @, может отправлять промпты, управляющие агентом в вашем cwd.
Всегда используйте senderPolicy: "allowlist" с явным указанием allowedUsers в публичных проектах.
Обнаружение упоминаний
Адаптер всегда устанавливает isMentioned = true для отправляемых envelope-объектов, потому что GitLab уже определил упоминание при создании todo. Конфигурация action_prompt_template является реальным фильтром событий — обрабатываются только действия с настроенным шаблоном. Упоминание @bot удаляется из текста сообщения перед отправкой через stripBotMention.
⚠️ groupPolicy должен быть “open” или “allowlist”
Для обработки todos параметр groupPolicy должен быть установлен в "open" или "allowlist" с явным указанием проекта. Значение по умолчанию "disabled" отклоняет все упоминания: todos помечаются как выполненные, курсор продвигается, но отправка не происходит. В лог записывается отклонение (preflight rejected reason=group_disabled), но todo всё равно считается обработанным. Если бот не отвечает на упоминания, проверьте, что groupPolicy не установлен в "disabled".
Как это работает
Адаптер использует GitLab Todos API как источник сообщений:
- Опрос
GET /todos?state=pendingдля получения новых todos - Первичная очистка: если курсор никогда не инициализировался (
initialized: false), все ожидающие todos помечаются как выполненные без отправки, и курсор продвигается до максимального ID todo. Это предотвращает завал отложенных задач при первом запуске. - Очистка устаревших todos: todos с
id <= cursorпомечаются как выполненные (best-effort), чтобы предотвратить их повторную выборку при каждом опросе - Фильтрация по
id > cursorи настроенномуaction_prompt_template - Определение типа упоминания по якорю
target_url:#note_123присутствует → упоминание в комментарии → текст:todo.body(комментарий)- Нет якоря → упоминание в описании → текст: описание issue/MR
- Отправка envelope через
handleInbound(требуетсяgroupPolicy: "open"или"allowlist"с указанием проекта) - Продвижение курсора и пометка todo как выполненного (best-effort)
Курсор (lastProcessedId) продвигается независимо от успеха или неудачи отправки. При неудачной отправке в issue/MR добавляется комментарий с ⚠️ об ошибке; повторных попыток не делается — пользователь может повторно упомянуть бота, чтобы создать новый todo.
Обратная связь через реакции
При принятом упоминании в комментарии (заметка с якорем #note_) канал добавляет award emoji 👀 к заметке, пока агент работает, а затем удаляет его при завершении, ошибке или отмене. Обе операции выполняются в режиме best-effort: ошибка API award emoji или нехватка прав логируется и никогда не блокирует финальный ответ.
Упоминания в описании (без якоря #note_) не получают award emoji, потому что нет конкретной заметки для реакции.
Известные ограничения
- При первом запуске существующие ожидающие todos пропускаются. Курсор инициализируется как
{ lastProcessedId: 0, initialized: false }при первом запуске. При первом цикле опроса все ранее существовавшие ожидающие todos помечаются как выполненные без отправки (флагinitializedобеспечивает эту одноразовую очистку), предотвращая завал отложенных задач. - Бот не читает историю предыдущих обсуждений — обрабатывается только содержимое триггера.
- Конфиденциальные (внутренние) заметки: если кто-то @упоминает бота в конфиденциальной заметке, тело todo содержит этот внутренний текст, и агент его обработает. Ответ бота всегда публикуется как публичная заметка, что может раскрыть внутреннее обсуждение. Todo API GitLab не предоставляет информацию о видимости заметок, поэтому адаптер не может это отфильтровать. Не упоминайте бота через @ в конфиденциальных заметках.
- Требуются скоупы PAT
read_api+api. Токены на уровне группы или проекта работают, если имеют эти скоупы. - Todos для Epics, Designs и Alerts пропускаются (обрабатываются только Issues и MRs).
Запуск канала
qwen channel start my-gitlab