Skip to Content
Руководство для пользователейВозможностиХуки

Хуки Qwen Code

Обзор

Хуки Qwen Code предоставляют мощный механизм для расширения и настройки поведения приложения Qwen Code. Хуки позволяют пользователям выполнять пользовательские скрипты или программы в определенные моменты жизненного цикла приложения, например, до выполнения инструмента, после выполнения инструмента, при начале/завершении сессии и во время других ключевых событий.

Хуки включены по умолчанию. Вы можете временно отключить все хуки, установив для параметра disableAllHooks значение true в файле настроек (на верхнем уровне, рядом с hooks):

{ "disableAllHooks": true, "hooks": { "PreToolUse": [...] } }

Это отключает все хуки без удаления их конфигураций.

Что такое хуки?

Хуки — это пользовательские скрипты или программы, которые автоматически выполняются Qwen Code в заранее определенных точках потока приложения. Они позволяют пользователям:

  • Отслеживать и аудировать использование инструментов
  • Обеспечивать соблюдение политик безопасности
  • Добавлять дополнительный контекст в диалоги
  • Настраивать поведение приложения в зависимости от событий
  • Интегрироваться с внешними системами и сервисами
  • Программно изменять входные данные или ответы инструментов

Типы хуков

Qwen Code поддерживает четыре типа исполнителей хуков:

ТипОписание
commandВыполняет команду оболочки. Получает JSON через stdin, возвращает результаты через stdout.
httpОтправляет JSON в теле POST-запроса на указанный URL. Возвращает результаты через тело HTTP-ответа.
functionНапрямую вызывает зарегистрированную JavaScript-функцию (только для хуков уровня сессии).
promptИспользует LLM для оценки входных данных хука и возврата решения.

Хуки command

Хуки command выполняют команды через дочерние процессы. Входной JSON передается через stdin, а вывод возвращается через stdout.

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

ПолеТипОбязательноОписание
type"command"ДаТип хука
commandstringДаКоманда для выполнения
namestringНетИмя хука (для логирования)
descriptionstringНетОписание хука
timeoutnumberНетТаймаут в миллисекундах, по умолчанию 60000
asyncbooleanНетЗапускать ли асинхронно в фоновом режиме
envRecord<string, string>НетПеременные окружения
shell"bash" | "powershell"НетИспользуемая оболочка
statusMessagestringНетСообщение о статусе, отображаемое во время выполнения

Пример:

{ "hooks": { "PreToolUse": [ { "matcher": "WriteFile", "hooks": [ { "type": "command", "command": "$QWEN_PROJECT_DIR/.qwen/hooks/security-check.sh", "name": "security-check", "timeout": 10000 } ] } ] } }

Хуки http

Хуки http отправляют входные данные хука в виде POST-запросов на указанные URL. Они поддерживают белые списки URL, защиту от SSRF на уровне DNS, интерполяцию переменных окружения и другие функции безопасности.

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

ПолеТипОбязательноОписание
type"http"ДаТип хука
urlstringДаЦелевой URL
headersRecord<string, string>НетЗаголовки запроса (поддерживают интерполяцию переменных окружения)
allowedEnvVarsstring[]НетБелый список переменных окружения, разрешенных в URL/заголовках
timeoutnumberНетТаймаут в секундах, по умолчанию 600
namestringНетИмя хука (для логирования)
statusMessagestringНетСообщение о статусе, отображаемое во время выполнения
oncebooleanНетВыполнять только один раз для каждого события в сессии (только для хуков http)

Функции безопасности:

  • Белый список URL: Настройка разрешенных шаблонов URL через allowedUrls
  • Защита от SSRF: Блокирует приватные IP-адреса (10.x.x.x, 172.16-31.x.x, 192.168.x.x и т.д.), но разрешает адреса loopback (127.0.0.1, ::1)
  • Валидация DNS: Проверяет разрешение доменов перед запросами для предотвращения атак DNS rebinding
  • Интерполяция переменных окружения: Синтаксис ${VAR}, разрешает использование только переменных из белого списка allowedEnvVars

Пример:

{ "hooks": { "PreToolUse": [ { "matcher": "*", "hooks": [ { "type": "http", "url": "http://127.0.0.1:8080/hooks/pre-tool-use", "headers": { "Authorization": "Bearer ${HOOK_API_KEY}" }, "allowedEnvVars": ["HOOK_API_KEY"], "timeout": 10, "name": "remote-security-check" } ] } ] } }

Хуки function

Хуки function напрямую вызывают зарегистрированные JavaScript/TypeScript-функции. Они используются внутри системы Skill и в настоящее время не доступны как публичный API для конечных пользователей.

Примечание: Для большинства сценариев использования вместо них применяйте хуки command или хуки http, которые можно настраивать в файлах настроек.

Хуки prompt

Хуки prompt используют LLM для оценки входных данных хука и возврата решения. Это полезно для принятия интеллектуальных решений на основе контекста, например, для определения того, разрешить или заблокировать операцию.

Как это работает:

  1. Входной JSON хука внедряется в ваш промпт с помощью плейсхолдера $ARGUMENTS
  2. Промпт отправляется в LLM (по умолчанию: ваша текущая модель)
  3. LLM возвращает JSON-ответ с решением
  4. Qwen Code обрабатывает решение и соответственно продолжает или блокирует выполнение

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

ПолеТипОбязательноОписание
type"prompt"ДаТип хука
promptstringДаПромпт, отправляемый в LLM. Используйте $ARGUMENTS для входных данных хука
modelstringНетИспользуемая модель (по умолчанию ваша текущая модель)
timeoutnumberНетТаймаут в секундах, по умолчанию 30
namestringНетИмя хука (для логирования)
descriptionstringНетОписание хука
statusMessagestringНетСообщение о статусе, отображаемое во время выполнения

Формат ответа:

LLM должна возвращать JSON со следующей структурой:

{ "ok": true, "reason": "Explanation of the decision", "additionalContext": "Optional context to inject into the conversation" }
ПолеОписание
oktrue для разрешения/продолжения, false для блокировки/остановки
reasonОбязательно, если ok равно false. Показывается модели для объяснения блокировки
additionalContextОпционально. Дополнительный контекст для внедрения в диалог при разрешении

Поддерживаемые события:

Хуки prompt можно использовать с большинством событий хуков, включая:

  • PreToolUse — Оценка того, разрешить ли вызов инструмента
  • PostToolUse — Оценка результатов инструмента и потенциальное внедрение контекста
  • Stop — Определение того, продолжать ли работу или остановиться
  • SubagentStop — Оценка результатов подагента
  • UserPromptSubmit — Оценка или обогащение промптов пользователя

Пример: хук Stop

{ "hooks": { "Stop": [ { "hooks": [ { "type": "prompt", "prompt": "You are evaluating whether Qwen Code should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.", "timeout": 30 } ] } ] } }

Если ok равно false, Qwen Code продолжит работу и использует reason в качестве контекста для следующего ответа.

Пример: хук PreToolUse

{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "prompt", "prompt": "Evaluate this tool call for security concerns. Tool input: $ARGUMENTS\n\nCheck for:\n- Dangerous commands (rm -rf, curl | sh, etc.)\n- Unauthorized access attempts\n- Data exfiltration patterns\n\nRespond with {\"ok\": true} if safe, or {\"ok\": false, \"reason\": \"concern\"} if blocked.", "model": "sonnet", "timeout": 30, "name": "security-evaluator" } ] } ] } }

События хуков

Хуки срабатывают в определенные моменты во время сессии Qwen Code. Разные события поддерживают разные матчеры для фильтрации условий срабатывания.

СобытиеКогда срабатываетЦель матчера
PreToolUseДо выполнения инструментаИмя инструмента (WriteFile, ReadFile, Bash и т.д.)
PostToolUseПосле успешного выполнения инструментаИмя инструмента
PostToolUseFailureПосле сбоя при выполнении инструментаИмя инструмента
UserPromptSubmitПосле отправки промпта пользователемНет (срабатывает всегда)
SessionStartПри запуске или возобновлении сессииИсточник (startup, resume, clear, compact)
SessionEndПри завершении сессииПричина (clear, logout, prompt_input_exit и т.д.)
StopКогда Claude готовится завершить ответНет (срабатывает всегда)
SubagentStartПри запуске подагентаТип агента (Bash, Explorer, Plan и т.д.)
SubagentStopПри остановке подагентаТип агента
PreCompactПеред сжатием диалогаТриггер (manual, auto)
NotificationПри отправке уведомленийТип (permission_prompt, idle_prompt, auth_success)
PermissionRequestПри отображении диалога запроса разрешенийИмя инструмента
TodoCreatedПри создании нового элемента todoНет (срабатывает всегда)
TodoCompletedКогда элемент todo помечен как выполненныйНет (срабатывает всегда)

Паттерны матчера

matcher — это регулярное выражение, используемое для фильтрации условий срабатывания.

Тип событияСобытияПоддержка матчераЦель матчера
События инструментовPreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest✅ RegexИмя инструмента: WriteFile, ReadFile, Bash и т.д.
События подагентовSubagentStart, SubagentStop✅ RegexТип агента: Bash, Explorer и т.д.
События сессииSessionStart✅ RegexИсточник: startup, resume, clear, compact
События сессииSessionEnd✅ RegexПричина: clear, logout, prompt_input_exit и т.д.
События уведомленийNotification✅ Точное совпадениеТип: permission_prompt, idle_prompt, auth_success
События сжатияPreCompact✅ Точное совпадениеТриггер: manual, auto
События TodoTodoCreated, TodoCompleted❌ НетН/Д
События промптаUserPromptSubmit❌ НетН/Д
События остановкиStop❌ НетН/Д

Синтаксис матчера:

  • Пустая строка "" или "*" соответствует всем событиям данного типа
  • Поддерживается стандартный синтаксис регулярных выражений (например, ^Bash$, Read.*, (WriteFile|Edit))

Примеры:

{ "hooks": { "PreToolUse": [ { "matcher": "^Bash$", "hooks": [ { "type": "command", "command": "echo 'bash check' >> /tmp/hooks.log" } ] }, { "matcher": "Write.*", "hooks": [ { "type": "command", "command": "echo 'write check' >> /tmp/hooks.log" } ] }, { "matcher": "*", "hooks": [ { "type": "command", "command": "echo 'all tools' >> /tmp/hooks.log" } ] } ], "SubagentStart": [ { "matcher": "^(Bash|Explorer)$", "hooks": [ { "type": "command", "command": "echo 'subagent check' >> /tmp/hooks.log" } ] } ] } }

Правила ввода/вывода

Структура входных данных хука

Все хуки получают стандартизированные входные данные в формате JSON через stdin (command) или тело POST-запроса (http).

Общие поля:

{ "session_id": "string", "transcript_path": "string", "cwd": "string", "hook_event_name": "string", "timestamp": "string" }

Специфичные для события поля добавляются в зависимости от типа хука. При запуске в подагенте дополнительно включаются agent_id и agent_type.

Структура выходных данных хука

Выходные данные хука возвращаются через stdout (command) или тело HTTP-ответа (http) в формате JSON.

Поведение кодов завершения (хуки command):

Код завершенияПоведение
0Успех. Парсит JSON в stdout для управления поведением.
2Блокирующая ошибка. Игнорирует stdout, передает stderr как обратную связь об ошибке модели.
ДругойНеблокирующая ошибка. stderr отображается только в режиме отладки, выполнение продолжается.

Структура выходных данных:

Выходные данные хука поддерживают три категории полей:

  1. Общие поля: continue, stopReason, suppressOutput, systemMessage
  2. Решение верхнего уровня: decision, reason (используются некоторыми событиями)
  3. Специфичное для события управление: hookSpecificOutput (должно включать hookEventName)
{ "continue": true, "decision": "allow", "reason": "Operation approved", "hookSpecificOutput": { "hookEventName": "PreToolUse", "additionalContext": "Additional context information" } }

Детали отдельных событий хуков

PreToolUse

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

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_name": "name of the tool being executed", "tool_input": "object containing the tool's input parameters", "tool_use_id": "unique identifier for this tool use instance (internal format, e.g., toolu_xxx)", "tool_call_id": "original API call ID from the LLM provider (e.g., call_xxx for OpenAI/Qwen) (optional)" }

Варианты вывода:

  • hookSpecificOutput.permissionDecision: “allow”, “deny” или “ask” (ОБЯЗАТЕЛЬНО)
  • hookSpecificOutput.permissionDecisionReason: объяснение решения (ОБЯЗАТЕЛЬНО)
  • hookSpecificOutput.updatedInput: измененные входные параметры инструмента для использования вместо исходных
  • hookSpecificOutput.additionalContext: дополнительная контекстная информация

Значение permissionDecision управляет запуском инструмента:

  • "allow" — запустить инструмент без обычного запроса на подтверждение.
  • "deny" — заблокировать инструмент; он не выполняется, и модели возвращается ошибка.
  • "ask" — приостановить выполнение и запросить у пользователя подтверждение вызова инструмента в TUI перед его запуском. Подтверждение запускает инструмент один раз; отмена отменяет его. В контекстах, где невозможно запросить подтверждение — headless-запуски (--prompt) и фоновые подагенты — "ask" переключается на "deny".

Примечание: Хотя стандартные поля вывода хука, такие как decision и reason, технически поддерживаются базовым классом, официальный интерфейс ожидает hookSpecificOutput с permissionDecision и permissionDecisionReason.

Пример вывода:

{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Security policy blocks database writes", "additionalContext": "Current environment: production. Proceed with caution." } }

PostToolUse

Назначение: Выполняется после успешного завершения работы инструмента для обработки результатов, логирования или внедрения дополнительного контекста.

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_name": "name of the tool that was executed", "tool_input": "object containing the tool's input parameters", "tool_response": "object containing the tool's response", "tool_use_id": "unique identifier for this tool use instance (internal format, e.g., toolu_xxx)", "tool_call_id": "original API call ID from the LLM provider (e.g., call_xxx for OpenAI/Qwen) (optional)" }

Варианты вывода:

  • decision: “allow”, “deny”, “block” (по умолчанию “allow”, если не указано)
  • reason: причина решения
  • hookSpecificOutput.additionalContext: дополнительная информация для включения

Пример вывода:

{ "decision": "allow", "reason": "Tool executed successfully", "hookSpecificOutput": { "additionalContext": "File modification recorded in audit log" } }

PostToolUseFailure

Назначение: Выполняется при сбое выполнения инструмента для обработки ошибок, отправки оповещений или регистрации сбоев.

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_use_id": "unique identifier for the tool use (internal format, e.g., toolu_xxx)", "tool_call_id": "original API call ID from the LLM provider (e.g., call_xxx for OpenAI/Qwen) (optional)", "tool_name": "name of the tool that failed", "tool_input": "object containing the tool's input parameters", "error": "error message describing the failure", "is_interrupt": "boolean indicating if failure was due to user interruption (optional)" }

Варианты вывода:

  • hookSpecificOutput.additionalContext: информация об обработке ошибки
  • Стандартные поля вывода хука

Пример вывода:

{ "hookSpecificOutput": { "additionalContext": "Error: File not found. Failure logged in monitoring system." } }

UserPromptSubmit

Назначение: Выполняется, когда пользователь отправляет промпт, для изменения, валидации или обогащения входных данных.

Специфичные для события поля:

{ "prompt": "the user's submitted prompt text" }

Варианты вывода:

  • decision: “allow”, “deny”, “block” или “ask”
  • reason: понятное человеку объяснение решения
  • hookSpecificOutput.additionalContext: дополнительный контекст для добавления к промпту (опционально)

Примечание: Поскольку UserPromptSubmitOutput расширяет HookOutput, доступны все стандартные поля, но только additionalContext в hookSpecificOutput специально определен для этого события.

Пример вывода:

{ "decision": "allow", "reason": "Prompt reviewed and approved", "hookSpecificOutput": { "additionalContext": "Remember to follow company coding standards." } }

SessionStart

Назначение: Выполняется при запуске новой сессии для выполнения задач инициализации.

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "source": "startup | resume | clear | compact", "model": "the model being used", "agent_type": "the type of agent if applicable (optional)" }

Варианты вывода:

  • hookSpecificOutput.additionalContext: контекст, доступный в сессии
  • Стандартные поля вывода хука

Пример вывода:

{ "hookSpecificOutput": { "additionalContext": "Session started with security policies enabled." } }

SessionEnd

Назначение: Выполняется при завершении сессии для выполнения задач очистки.

Специфичные для события поля:

{ "reason": "clear | logout | prompt_input_exit | bypass_permissions_disabled | other" }

Варианты вывода:

  • Стандартные поля вывода хука (обычно не используются для блокировки)

Stop

Назначение: Выполняется перед тем, как Qwen завершит свой ответ, для предоставления итоговой обратной связи или резюме.

Специфичные для события поля:

{ "stop_hook_active": "boolean indicating if stop hook is active", "last_assistant_message": "the last message from the assistant", "context_usage": "ratio of context window used (may exceed 1 when tokens exceed window; optional)", "context_limit": "context window size in tokens (optional)", "input_tokens": "prompt token count (may include output tokens depending on provider; optional)" }

Поля context_usage, context_limit и input_tokens позволяют скриптам хуков отслеживать использование контекста и реализовывать пользовательские стратегии сжатия — например, скрипт, который выводит напоминание о запуске /compact, когда использование превышает пользовательский порог.

Варианты вывода:

  • decision: “allow”, “deny”, “block” или “ask”
  • reason: понятное человеку объяснение решения
  • stopReason: обратная связь для включения в ответ об остановке
  • continue: установите в false, чтобы остановить выполнение
  • hookSpecificOutput.additionalContext: дополнительная контекстная информация

Примечание: Поскольку StopOutput расширяет HookOutput, доступны все стандартные поля, но поле stopReason особенно актуально для этого события.

Пример вывода:

{ "decision": "block", "reason": "Must be provided when Qwen Code is blocked from stopping" }

StopFailure

Назначение: Выполняется, когда ход завершается из-за ошибки API (вместо Stop). Это событие по принципу fire-and-forget — выходные данные хука и коды завершения игнорируются.

Специфичные для события поля:

{ "error": "rate_limit | authentication_failed | billing_error | invalid_request | server_error | max_output_tokens | unknown", "error_details": "detailed error message (optional)", "last_assistant_message": "the last message from the assistant before the error (optional)" }

Matcher: Сопоставляется с полем error. Например, "matcher": "rate_limit" будет срабатывать только для ошибок rate limit.

Варианты вывода:

  • None - StopFailure работает в режиме fire-and-forget. Весь вывод хуков и коды завершения игнорируются.

Обработка кодов завершения:

Код завершенияПоведение
ЛюбойИгнорируется (fire-and-forget)

Пример конфигурации:

{ "hooks": { "StopFailure": [ { "matcher": "rate_limit", "hooks": [ { "type": "command", "command": "/path/to/rate-limit-alert.sh", "name": "rate-limit-alerter" } ] } ] } }

Варианты использования:

  • Мониторинг и алертинг rate limit
  • Логирование ошибок аутентификации
  • Уведомления об ошибках биллинга
  • Сбор статистики ошибок

SubagentStart

Назначение: Выполняется при запуске подагента (например, инструмента Task) для настройки контекста или прав доступа.

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "agent_id": "identifier for the subagent", "agent_type": "type of agent (Bash, Explorer, Plan, Custom, etc.)" }

Варианты вывода:

  • hookSpecificOutput.additionalContext: начальный контекст для подагента
  • Стандартные поля вывода хука

Пример вывода:

{ "hookSpecificOutput": { "additionalContext": "Subagent initialized with restricted permissions." } }

SubagentStop

Назначение: Выполняется при завершении работы подагента для выполнения финальных задач.

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "stop_hook_active": "boolean indicating if stop hook is active", "agent_id": "identifier for the subagent", "agent_type": "type of agent", "agent_transcript_path": "path to the subagent's transcript", "last_assistant_message": "the last message from the subagent" }

Варианты вывода:

  • decision: “allow”, “deny”, “block” или “ask”
  • reason: понятное человеку объяснение принятого решения

Пример вывода:

{ "decision": "block", "reason": "Must be provided when Qwen Code is blocked from stopping" }

PreCompact

Назначение: Выполняется перед сжатием диалога (compaction) для подготовки или логирования сжатия.

Специфичные для события поля:

{ "trigger": "manual | auto", "custom_instructions": "custom instructions currently set" }

Варианты вывода:

  • hookSpecificOutput.additionalContext: контекст для включения перед сжатием
  • Стандартные поля вывода хука

Пример вывода:

{ "hookSpecificOutput": { "additionalContext": "Compacting conversation to maintain optimal context window." } }

PostCompact

Назначение: Выполняется после завершения сжатия диалога для архивации резюме или отслеживания использования.

Специфичные для события поля:

{ "trigger": "manual | auto", "compact_summary": "the summary generated by the compaction process" }

Matcher: Сопоставляется с полем trigger. Например, "matcher": "manual" будет срабатывать только для ручного сжатия через команду /compact.

Варианты вывода:

  • hookSpecificOutput.additionalContext: дополнительный контекст (только для логирования)
  • Стандартные поля вывода хука (только для логирования)

Примечание: PostCompact не входит в официальный список событий, поддерживающих decision mode. Поле decision и другие управляющие поля не оказывают никакого управляющего эффекта — они используются только для логирования.

Обработка кодов завершения:

Код завершенияПоведение
0Успех - stdout показывается пользователю в verbose-режиме
ДругойНеблокирующая ошибка - stderr показывается пользователю в verbose-режиме

Пример конфигурации:

{ "hooks": { "PostCompact": [ { "matcher": "manual", "hooks": [ { "type": "command", "command": "/path/to/save-compact-summary.sh", "name": "save-summary" } ] } ] } }

Варианты использования:

  • Архивация резюме в файлы или базы данных
  • Отслеживание статистики использования
  • Мониторинг изменений контекста
  • Аудит-логирование операций сжатия

Notification

Назначение: Выполняется при отправке уведомлений для их кастомизации или перехвата.

Специфичные для события поля:

{ "message": "notification message content", "title": "notification title (optional)", "notification_type": "permission_prompt | idle_prompt | auth_success" }

Примечание: тип elicitation_dialog определен, но в настоящее время не реализован.

Варианты вывода:

  • hookSpecificOutput.additionalContext: дополнительная информация для включения
  • Стандартные поля вывода хука

Пример вывода:

{ "hookSpecificOutput": { "additionalContext": "Notification processed by monitoring system." } }

PermissionRequest

Назначение: Выполняется при отображении диалогов запроса прав доступа для автоматизации принятия решений или обновления прав.

Специфичные для события поля:

{ "permission_mode": "default | plan | auto_edit | yolo", "tool_name": "name of the tool requesting permission", "tool_input": "object containing the tool's input parameters", "permission_suggestions": "array of suggested permissions (optional)" }

Варианты вывода:

  • hookSpecificOutput.decision: структурированный объект с деталями решения о правах доступа:
    • behavior: “allow” или “deny”
    • updatedInput: измененные входные данные инструмента (опционально)
    • updatedPermissions: измененные права доступа (опционально)
    • message: сообщение для показа пользователю (опционально)
    • interrupt: прерывать ли рабочий процесс (опционально)

Пример вывода:

{ "hookSpecificOutput": { "decision": { "behavior": "allow", "message": "Permission granted based on security policy", "interrupt": false } } }

TodoCreated

Назначение: Выполняется при создании нового элемента todo с помощью инструмента todo_write. Позволяет валидировать, логировать или блокировать создание todo.

Хуки todo выполняются в две фазы:

  • validation: выполняется до сохранения. Используйте эту фазу только для валидации; возврат block или deny предотвращает запись.
  • postWrite: выполняется после сохранения. Используйте эту фазу для побочных эффектов, таких как логирование или синхронизация; block или deny игнорируются в этой фазе.

Специфичные для события поля:

{ "todo_id": "unique identifier for the todo item", "todo_content": "content/description of the todo item", "todo_status": "pending | in_progress | completed", "all_todos": "array of all todo items in the current list", "phase": "validation | postWrite" }

Варианты вывода:

  • decision: “allow”, “block” или “deny”
  • reason: понятное человеку объяснение принятого решения (обязательно при блокировке)

Поведение при блокировке:

В фазе validation, если decision равно block или deny (код завершения 2), создание todo предотвращается. Список todo остается неизменным, а причина предоставляется модели в качестве обратной связи.

В фазе postWrite todo уже сохранен. Хуки могут возвращать вывод, но block / deny не отменяет запись и не должно использоваться для валидации.

Пример вывода (Allow):

{ "decision": "allow", "reason": "Todo content validated successfully" }

Пример вывода (Block):

{ "decision": "block", "reason": "Todo content too short. Minimum 5 characters required." }

Пример скрипта хука:

#!/bin/bash # ~/.qwen/hooks/todo-validator.sh # Validates todo content before creation INPUT=$(cat) CONTENT=$(echo "$INPUT" | jq -r '.todo_content') # Check minimum length if [ ${#CONTENT} -lt 5 ]; then echo '{"decision": "block", "reason": "Todo content must be at least 5 characters"}' exit 2 fi # Block test-related todos if [[ "$CONTENT" =~ "test" ]]; then echo '{"decision": "block", "reason": "Test todos are not allowed in production"}' exit 2 fi echo '{"decision": "allow"}' exit 0

Пример конфигурации:

{ "hooks": { "TodoCreated": [ { "hooks": [ { "type": "command", "command": "$HOME/.qwen/hooks/todo-validator.sh", "name": "todo-validator", "timeout": 5000 } ] } ] } }

TodoCompleted

Назначение: Выполняется, когда элемент todo помечается как выполненный. Позволяет валидировать, логировать или блокировать завершение todo.

Хуки todo выполняются в две фазы:

  • validation: выполняется до сохранения. Используйте эту фазу только для валидации; возврат block или deny предотвращает запись.
  • postWrite: выполняется после сохранения. Используйте эту фазу для побочных эффектов, таких как логирование или синхронизация; block или deny игнорируются в этой фазе.

Специфичные для события поля:

{ "todo_id": "unique identifier for the todo item", "todo_content": "content/description of the todo item", "previous_status": "pending | in_progress (status before completion)", "all_todos": "array of all todo items in the current list", "phase": "validation | postWrite" }

Варианты вывода:

  • decision: “allow”, “block” или “deny”
  • reason: понятное человеку объяснение принятого решения (обязательно при блокировке)

Поведение при блокировке:

В фазе validation, если decision равно block или deny (код завершения 2), завершение todo предотвращается. Элемент todo остается в предыдущем статусе, а причина предоставляется модели в качестве обратной связи.

В фазе postWrite todo уже сохранен. Хуки могут возвращать вывод, но block / deny не отменяет запись и не должно использоваться для валидации.

Пример вывода (Allow):

{ "decision": "allow", "reason": "Todo completion approved" }

Пример вывода (Block):

{ "decision": "block", "reason": "Cannot complete this todo until dependent tasks are finished." }

Пример скрипта хука:

#!/bin/bash # ~/.qwen/hooks/todo-completion-validator.sh # Validates todo completion conditions INPUT=$(cat) TODO_ID=$(echo "$INPUT" | jq -r '.todo_id') ALL_TODOS=$(echo "$INPUT" | jq -r '.all_todos') # Check if there are incomplete dependent todos (example logic) INCOMPLETE_COUNT=$(echo "$ALL_TODOS" | jq '[.[] | select(.status != "completed")] | length') if [ "$INCOMPLETE_COUNT" -gt 5 ]; then echo '{"decision": "block", "reason": "Too many incomplete todos. Complete other tasks first."}' exit 2 fi echo '{"decision": "allow"}' exit 0

Пример конфигурации:

{ "hooks": { "TodoCompleted": [ { "hooks": [ { "type": "command", "command": "$HOME/.qwen/hooks/todo-completion-validator.sh", "name": "completion-validator", "timeout": 5000 } ] } ] } }

Варианты использования:

  • Логирование: Отслеживание создания и завершения todo для аудита или аналитики
  • Валидация: Обеспечение стандартов качества контента (минимальная длина, обязательные ключевые слова)
  • Управление рабочим процессом: Блокировка завершения до выполнения предварительных условий
  • Интеграция: Синхронизация todo с внешними системами управления задачами (Jira, Trello и т. д.)

Конфигурация хуков

Хуки настраиваются в настройках Qwen Code, обычно в файле .qwen/settings.json или пользовательских конфигурационных файлах:

{ "hooks": { "PreToolUse": [ { "matcher": "^Bash$", "sequential": false, "hooks": [ { "type": "command", "command": "/path/to/security-check.sh", "name": "security-check", "description": "Run security checks before tool execution", "timeout": 30000 } ] } ], "SessionStart": [ { "hooks": [ { "type": "command", "command": "echo 'Session started'", "name": "session-init" } ] } ] } }

Выполнение хуков

Параллельное и последовательное выполнение

  • По умолчанию хуки выполняются параллельно для повышения производительности
  • Используйте sequential: true в определении хука, чтобы принудительно задать порядок выполнения
  • Последовательные хуки могут изменять входные данные для последующих хуков в цепочке

Асинхронные хуки

Только тип command поддерживает асинхронное выполнение. Установка "async": true запускает хук в фоновом режиме, не блокируя основной поток.

Особенности:

  • Нельзя вернуть управление решением (операция уже произошла)
  • Результаты внедряются в следующий ход диалога через systemMessage или additionalContext
  • Подходит для аудита, логирования, фонового тестирования и т.д.

Пример:

{ "hooks": { "PostToolUse": [ { "matcher": "WriteFile|Edit", "hooks": [ { "type": "command", "command": "$QWEN_PROJECT_DIR/.qwen/hooks/run-tests-async.sh", "async": true, "timeout": 300000 } ] } ] } }
#!/bin/bash INPUT=$(cat) FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0; fi RESULT=$(npm test 2>&1) if [ $? -eq 0 ]; then echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}" else echo "{\"systemMessage\": \"Tests failed: $RESULT\"}" fi

Модель безопасности

  • Хуки выполняются в окружении пользователя с его правами доступа
  • Хуки на уровне проекта требуют статуса доверенной папки
  • Таймауты предотвращают зависание хуков (по умолчанию: 60 секунд)

Лучшие практики

Пример 1: Хук проверки безопасности

Хук PreToolUse, который логирует и при необходимости блокирует опасные команды:

security_check.sh

#!/bin/bash # Read input from stdin INPUT=$(cat) # Parse the input to extract tool info TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name') TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input') # Check for potentially dangerous operations if echo "$TOOL_INPUT" | grep -qiE "(rm.*-rf|mv.*\/|chmod.*777)"; then echo '{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Security policy blocks dangerous command" } }' exit 2 # Blocking error fi # Log the operation echo "INFO: Tool $TOOL_NAME executed safely at $(date)" >> /var/log/qwen-security.log # Allow with additional context echo '{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "allow", "permissionDecisionReason": "Security check passed", "additionalContext": "Command approved by security policy" } }' exit 0

Настройка в .qwen/settings.json:

{ "hooks": { "PreToolUse": [ { "hooks": [ { "type": "command", "command": "${SECURITY_CHECK_SCRIPT}", "name": "security-checker", "description": "Security validation for bash commands", "timeout": 10000 } ] } ] } }

Пример 2: HTTP-хук для аудита

HTTP-хук PostToolUse, который отправляет все записи о выполнении инструментов в удаленную службу аудита:

{ "hooks": { "PostToolUse": [ { "matcher": "*", "hooks": [ { "type": "http", "url": "https://audit.example.com/api/tool-execution", "headers": { "Authorization": "Bearer ${AUDIT_API_TOKEN}", "Content-Type": "application/json" }, "allowedEnvVars": ["AUDIT_API_TOKEN"], "timeout": 10, "name": "audit-logger" } ] } ] } }

Пример 3: Хук валидации пользовательского промпта

Хук UserPromptSubmit, который проверяет пользовательские промпты на наличие конфиденциальной информации и добавляет контекст для длинных промптов:

prompt_validator.py

import json import sys import re # Load input from stdin try: input_data = json.load(sys.stdin) except json.JSONDecodeError as e: print(f"Error: Invalid JSON input: {e}", file=sys.stderr) exit(1) user_prompt = input_data.get("prompt", "") # Sensitive words list sensitive_words = ["password", "secret", "token", "api_key"] # Check for sensitive information for word in sensitive_words: if re.search(rf"\b{word}\b", user_prompt.lower()): # Block prompts containing sensitive information output = { "decision": "block", "reason": f"Prompt contains sensitive information '{word}'. Please remove sensitive content and resubmit.", "hookSpecificOutput": { "hookEventName": "UserPromptSubmit" } } print(json.dumps(output)) exit(0) # Check prompt length and add warning context if too long if len(user_prompt) > 1000: output = { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": "Note: User submitted a long prompt. Please read carefully and ensure all requirements are understood." } } print(json.dumps(output)) exit(0) # No processing needed for normal cases exit(0)

Устранение неполадок

  • Проверьте логи приложения для получения подробностей о выполнении хуков
  • Убедитесь в наличии прав доступа и исполняемости скриптов хуков
  • Убедитесь в правильности форматирования JSON в выходных данных хуков
  • Используйте конкретные паттерны matcher, чтобы избежать непреднамеренного выполнения хуков
  • Используйте режим --debug для просмотра подробной информации о сопоставлении и выполнении хуков
  • Временно отключите все хуки: добавьте "disableAllHooks": true в настройки
Last updated on