Хуки автоматизированного ревью кода

Обзор

CodeGraph предоставляет автоматизированное ревью кода через два механизма:

  1. Хуки Claude Code — самостоятельные Python-скрипты в .claude/hooks/, выполняемые автоматически в точках жизненного цикла (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop). Взаимодействуют через JSON stdin/stdout.
  2. CLI-конвейер ревью — команда python -m src.cli review с собственным агрегатором, scope-фильтрацией и SARIF-выводом.

Оба механизма разделяют конфигурацию review_pipeline и scope-фильтрацию, но реализуют её по-разному.

Хуки

Хук Точка срабатывания Таймаут Назначение
session_context.py SessionStart 10с Определение проекта, проверка актуальности CPG, загрузка scope
enrich_prompt.py UserPromptSubmit 15с Внедрение CPG-контекста в промпты (макс. 3 сущности)
pre_tool_use.py PreToolUse Проверка сложности (CC > 15), fan-out, полнота регистрации
commit_analysis.py PostToolUse (Bash) 60с Анализ качества после git commit с обновлением CPG
post_analysis.py Stop 10с Мёртвый код, отсутствие тестов, скачки сложности, пробелы интерфейсов
cli_error_monitor.py PostToolUse (Bash) Обнаружение 6 паттернов ошибок CLI

Паттерны cli_error_monitor

Монитор обнаруживает следующие паттерны ошибок в выводе Bash:

  1. Database not found
  2. DatabaseNotConfiguredError
  3. DuckDB __spec__ import bug
  4. ImportError / ModuleNotFoundError
  5. Python traceback (общий)
  6. RuntimeWarning (импорт модулей)

Бюджеты таймаутов

commit_analysis.py имеет внутренний бюджет 58с (2с запас от таймаута 60с):

Фаза Бюджет Описание
Проверка актуальности CPG Сравнение HEAD-коммита с БД
Обновление CPG (при устаревании) 40с Перепарсинг через gocpg
Анализ качества 16с CC, TODO/FIXME, радиус изменений

Пороговые значения

Хук Порог Значение
pre_tool_use.py Цикломатическая сложность 15
pre_tool_use.py Fan-Out 30
commit_analysis.py Цикломатическая сложность 10
commit_analysis.py Fan-Out 30
commit_analysis.py Максимум файлов 20
post_analysis.py Коэффициент скачка CC 2.0x
post_analysis.py Максимум файлов 5
enrich_prompt.py Максимум сущностей 3

Архитектура

stdin (JSON) → Скрипт хука → stdout (JSON)
                    ↓
              _utils.py      (общие хелперы: запросы gocpg, загрузка scope)
              _feedback.py   (датаклассы ReviewFeedback/ReviewFinding)
              _metrics.py    (логирование производительности в JSONL)
              _session_cache.py  (файловый кеш контекста проекта, TTL=3600с)
              _project_detector.py  (автоопределение проекта по CWD/source_path)

Модули поддержки

Модуль Ключевые экспорты
_utils.py read_stdin_json(), safe_json_output(), run_gocpg_query(), extract_entities(), load_parse_scope(), is_partial_scope(), tests_in_scope()
_feedback.py ReviewFinding (8 полей), ReviewFeedback (9 полей), FINDING_TYPES, SEVERITY_ORDER
_metrics.py timed_hook() — контекстный менеджер, log_hook_metric()
_session_cache.py get_project_with_cache(), set_cached_project(), invalidate_cache() — TTL 3600с, хранится в .claude/.cache/session_project.json
_project_detector.py detect_project(cwd) — сопоставление CWD с projects.registry[*].source_path, поддержка ${VAR:-default}

Протокол структурированной обратной связи

Все хуки выводят JSON с необязательным полем additionalContext (markdown, отображается пользователю):

{
  "additionalContext": "## Отчёт ревью\n..."
}

Класс ReviewFeedback в _feedback.py генерирует структурированный markdown с: - Таблицей находок, отсортированных по severity - Дисклеймером scope (когда scope парсинга ограничен) - Счётчиком подавленных находок (отфильтрованных из-за scope) - Длительностью и метаданными

Типы находок

Шесть типов находок определены в FINDING_TYPES:

Тип Описание Используется в
complexity Высокая цикломатическая сложность или fan-out pre_tool_use, commit_analysis, post_analysis
dead_code Недостижимый или невызываемый код commit_analysis
missing_test Отсутствует тестовый файл для модуля post_analysis
blast_radius Много вызывающих функций затронуто изменением commit_analysis
security Уязвимость безопасности CLI-конвейер ревью
interface_gap Публичные функции не экспонированы через интерфейс pre_tool_use, post_analysis

Scope-aware фильтрация

Когда CPG построен с исключениями (например, только бэкенд без тестов/фронтенда), применяется фильтрация с учётом scope. Поведение различается между хуками и CLI-конвейером:

Поведение хуков

Тип находки Условие Хук Действие
dead_code Scope ограничен commit_analysis.py Добавить disclaimer к отчёту
blast_radius Scope ограничен commit_analysis.py Добавить disclaimer к отчёту
missing_test include_tests=false post_analysis.py Полностью подавить, увеличить suppressed_count
complexity все Не затронут (метрика per-method)
security все Не затронут (реальная уязвимость)
interface_gap все Не затронут

Поведение CLI-конвейера ревью

Команда review использует ReviewAggregator (src/review/aggregator.py) с иной логикой:

Тип находки Условие Действие
dead_code Scope ограничен + demote_dead_code_partial_scope=true Понизить до info, установить scope_limited=True
blast_radius Scope ограничен + demote_dead_code_partial_scope=true Понизить до info, установить scope_limited=True
missing_test suppress_tests_outside_scope=true + тесты вне scope Подавить (вернуть None)

При ограниченном scope в отчёт добавляется блок-дисклеймер.

CLI-команда ревью

# Ревью изменений относительно базового рефа
python -m src.cli review --base-ref HEAD~3

# Ревью с указанием базы данных
python -m src.cli review --db data/projects/myproject.duckdb --base-ref HEAD~3

# Ревью проиндексированных изменений
python -m src.cli review --staged

# Ревью конкретных файлов
python -m src.cli review --files src/api/main.py src/auth.py

# Форматы вывода
python -m src.cli review --format json --output-file report.json
python -m src.cli review --format sarif --output-file report.sarif
python -m src.cli review --base-ref HEAD~5 --sarif-file out.sarif

# Без анализа безопасности
python -m src.cli review --no-security
Аргумент Описание
--db PATH Путь к базе данных DuckDB CPG (по умолчанию: активный проект)
--base-ref REF Git-реф для diff-ревью (напр. HEAD~3, origin/main)
--staged Ревью только проиндексированных изменений
--files FILE... Ревью конкретных файлов
--no-security Пропустить анализ безопасности
--format {markdown,json,sarif} Формат вывода (по умолчанию: markdown)
--output-file PATH Записать вывод в файл
--sarif-file PATH Записать SARIF-вывод отдельно

Коды возврата: 0 = чисто или только medium/low, 1 = обнаружены critical или high.

Метрики и мониторинг

Хуки логируют метрики выполнения в data/hook_metrics.jsonl:

{"timestamp": "2026-03-06T12:00:00Z", "hook": "commit_analysis", "duration_ms": 1234.5, "findings": 3, "project": "codegraph", "status": "ok"}

Просмотр статистики:

python -m src.cli dogfood hooks-status
python -m src.cli dogfood hooks-status --last 50
python -m src.cli dogfood hooks-status --hook commit_analysis

Устойчивость к сбоям

Все хуки следуют принципу fail-open: - Отсутствует бинарник gocpg: CPG-запросы пропускаются, выводится пустой контекст - Повреждённая или отсутствующая БД: анализ пропускается, предупреждение в метрики - Таймаут: частичные результаты возвращаются в пределах бюджета - Ошибки импорта: переключение на subprocess-путь (commit_analysis: DuckDB direct → gocpg subprocess) - Любое необработанное исключение: перехватывается на верхнем уровне, пустой JSON

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

# config.yaml
review_pipeline:
  # Определение проекта
  auto_detect_project: true             # Автоопределение проекта по CWD
  detect_by_source_path: true           # Сопоставление CWD с projects.registry[*].source_path
  # Актуальность CPG
  auto_reparse: prompt                  # Поведение при устаревшем CPG: prompt | auto | off
  freshness_check_on_session: true      # Проверка актуальности CPG при SessionStart
  max_reparse_timeout_seconds: 60       # Таймаут перепарсинга
  stale_threshold_commits: 0            # Коммитов до устаревания (0 = любой новый коммит)
  # Вывод
  max_findings_in_summary: 10           # Максимум находок в итоговом отчёте
  # Scope-фильтрация
  scope_aware_filtering: true           # Включить scope-фильтрацию
  suppress_tests_outside_scope: true    # Подавлять missing_test когда тесты вне scope
  demote_dead_code_partial_scope: true  # Понижать dead_code/blast_radius в CLI review
  show_scope_disclaimer: true           # Показывать disclaimer при ограниченном scope
  # Метрики
  metrics_enabled: true                 # Включить JSONL-логирование метрик
  metrics_file: data/hook_metrics.jsonl # Файл вывода метрик
  fail_open: true                       # Продолжать при ошибках (fail-open)

# Dogfooding (используется commit_analysis.py)
dogfooding:
  enabled: true
  auto_update_cpg: true
  cpg_update_timeout: 40                # Таймаут обновления CPG в commit_analysis
  analysis_timeout: 16                  # Таймаут фазы анализа качества

Добавление нового хука

  1. Создайте Python-скрипт в .claude/hooks/
  2. Прочитайте JSON из stdin через read_stdin_json() из _utils.py
  3. Оберните логику в with timed_hook("hook_name") из _metrics.py
  4. Выведите JSON в stdout через safe_json_output() из _utils.py
  5. Зарегистрируйте в .claude/settings.json

Формат settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "python .claude/hooks/my_hook.py",
            "timeout": 8000
          }
        ]
      }
    ]
  }
}
Поле Описание
Ключ верхнего уровня Событие жизненного цикла: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop
matcher Фильтр по имени инструмента (напр. "Bash"). Пустая строка = все инструменты
type Всегда "command"
command Полный путь к Python-скрипту
timeout Таймаут в миллисекундах

Директория: .claude/hooks/ Последнее обновление: март 2026