Перейти к содержанию

Руководство по проверке AI-готовности (ai-lint)

chutils dev ai-lint — это специализированный инструмент статического анализа (линтер), разработанный для оценки * AI-готовности* вашей кодовой базы.

Современные AI-ассистенты и автономные кодинг-агенты (такие как Antigravity, Gemini CLI, Claude, GitHub Copilot) работают значительно эффективнее, если кодовая база хорошо структурирована, снабжена подробными манифестами, строгой типизацией и стандартизированными docstrings. Инструмент ai-lint помогает автоматически проверять кодовую базу на соответствие этим стандартам.


Зачем нужен ai-lint?

При работе искусственного интеллекта с вашей кодовой базой возникают следующие проблемы:

  1. Отсутствие контекста: ИИ не знает архитектурных ограничений проекта, принятых соглашений или используемых технологий, если они нигде не описаны.
  2. Плохая типизация: Без аннотаций типов (type hints) ИИ часто делает ошибочные предположения о структурах данных, что ведет к багам.
  3. Неполная документация: Если публичные методы не документированы по стандарту (например, Google Style), ИИ сложнее понять их контракты, параметры и возвращаемые типы.
  4. Утечка секретов: AI-ассистенты отправляют части кода на внешние сервера. Наличие захардкоженных токенов и паролей в коде создает серьезную угрозу безопасности.
  5. Изобретение велосипедов: Если в проекте уже есть готовые утилиты (например, логгер или менеджер секретов из chutils), ИИ может этого не знать и начать писать собственные аналоги.

ai-lint решает эти проблемы, выполняя статический аудит проекта по ряду специализированных правил.


Использование существующих правил

Базовый запуск

Для запуска проверки в текущей директории выполните:

poetry run python -m chutils dev ai-lint

или после установки библиотеки:

chutils dev ai-lint

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

Вы можете тонко настраивать поведение линтера с помощью флагов:

  • --staged — аудит только файлов, подготовленных к коммиту (staged diff) в Git. В этом режиме линтер проверяет только измененные файлы, а правила отслеживания зависимостей и покрытия фокусируются на подготовленных изменениях.
  • --strict — строгий режим. Любые предупреждения (warn) будут трактоваться как ошибки (error), и линтер вернет ненулевой код выхода (1).
  • --soft-mode — мягкий режим. Линтер выведет список всех найденных проблем, но всегда будет завершаться с успешным кодом выхода (0). Это полезно при первой интеграции в CI.
  • --ignore "<path1>,<path2>" — дополнительные шаблоны путей для игнорирования (разделяются запятой). Эти шаблоны дополняют настройки из файлов конфигурации.
  • --rules "<Rule1>,<Rule2>" — запуск только указанных правил через запятую (по умолчанию запускаются все правила).
  • --exclude-rules "<Rule1>,<Rule2>" — исключение указанных правил из аудита.
  • --output-format {default,table} — формат вывода результатов проверки (по умолчанию: table).
  • --group-by {file,rule} — группировка вывода отчета (по файлам или по правилам, по умолчанию: file).
  • --custom-rules-path "<path_to_file>" — путь к файлу с вашими собственными правилами.

Пример расширенного запуска:

chutils dev ai-lint --staged --strict --exclude-rules UpgradeCheckRule

Встроенные правила

В ai-lint встроен ряд правил:

1. ManifestRule (severity: warn)

Проверяет наличие файлов манифестов ИИ в корневом каталоге проекта и в основных пакетах (подкаталогах src/).

  • Используемые имена файлов: antigravity.md, agents.md, GEMINI.md (в любом регистре), .cursorrules, .windsurfrules.
  • Зачем: Файлы манифестов служат инструкцией для ИИ-агентов. В них описывается структура проекта, используемые библиотеки и глобальные правила кодирования.

2. DocstringQualityRule (severity: error)

Выполняет AST-анализ всех публичных классов, функций и методов (исключая тесты).

  • Что проверяет:
    • Наличие docstring у публичных классов и функций.
    • Соответствие структуры docstring формату Google Style (наличие обязательных разделов Args: и Returns: при наличии параметров и возвращаемого значения).
    • Документированность каждого аргумента функции в разделе Args:.
    • Наличие аннотаций типов (type hints) у всех аргументов функции (кроме self и cls).
    • Наличие аннотации возвращаемого значения (кроме метода __init__).
  • Зачем: Строгая типизация и структурированные docstrings критичны для генерации точного кода ИИ.

3. SecurityHardcodeRule (severity: error)

Сканирует текстовое содержимое файлов и строит AST-дерево для поиска секретов.

  • Что проверяет:
    • Приватные ключи (заголовки -----BEGIN PRIVATE KEY-----).
    • Токены облачных провайдеров (AWS Access Key, Slack Token и др.).
    • Жестко заданные присвоения строк переменным, содержащим в имени key, secret, password, token, pwd ( длиной более 8 символов, если они не похожи на плейсхолдеры).
  • Зачем: Защита от случайной утечки учетных данных в контекст больших языковых моделей (LLM).

4. ChutilsIntegrationRule (severity: warn)

Проверяет интеграцию с экосистемой chutils в проекте.

  • Что проверяет:
    • Использование стандартного модуля logging (рекомендует перейти на chutils.setup_logger).
    • Использование библиотеки keyring напрямую (рекомендует chutils.SecretManager).
    • Прямые обращения к os.getenv или os.environ (рекомендует использовать встроенные инструменты управления конфигурацией chutils).
    • Ручные вызовы метода .mkdir(parents=True, exist_ok=True) (рекомендует использовать chutils.fs.ensure_dir).
    • Ручные вызовы .write_text() / .write_bytes(), прямой вызов сериализаторов json.dump / yaml.dump или использование временных файлов tempfile с последующим перемещением/переименованием для записи файлов ( рекомендует использовать безопасную атомарную запись chutils.fs.atomic_write).
    • Ручные вызовы получения текущего UTC-времени с использованием datetime.utcnow() или datetime.now(timezone.utc) (рекомендует использовать chutils.time.utc_now()).
  • Зачем: Обеспечивает единообразие архитектуры проекта и направляет ИИ на переиспользование уже готовых библиотечных решений.

5. APIMapRule (severity: error)

Проверяет актуальность карты публичного API (api_map.md).

  • Что проверяет:
    • Наличие файла api_map.md в корне.
    • Полное соответствие содержимого api_map.md реально экспортируемым публичным функциям, классам и константам библиотеки.
  • Зачем: Карта API позволяет ИИ быстро ориентироваться в возможностях библиотеки без необходимости сканирования всех исходных файлов.

6. EnvSyncRule (severity: warn)

Проверяет соответствие состава ключей переменных окружения в файлах .env и .env.example.

  • Что проверяет:
    • Одновременное существование файлов .env и .env.example (если один есть, а другого нет — выдает предупреждение).
    • Совпадение наборов ключей в обоих файлах.
  • Зачем: Предотвращает ошибки запуска приложения из-за отсутствующих локальных переменных или забытых обновлений в файле-шаблоне.

7. CodeDecompositionRule (severity: warn)

Контролирует архитектурную декомпозицию и размер исходных файлов.

  • Что проверяет:
    • Количество строк в одном файле (по умолчанию не более 700 строк, настраивается параметром max_file_lines).
    • Количество классов в одном модуле (по умолчанию не более 5 классов, настраивается параметром max_file_classes).
  • Зачем: Предотвращает разрастание «божественных» модулей, снижает когнитивную нагрузку и улучшает качество анализа кода большими языковыми моделями.

8. APIMapHashRule (severity: warn)

Вычисляет целостный хэш публичного API кодовой базы и сопоставляет его с сохраненным в api_map.md.

  • Что проверяет:
    • Сверяет актуальный SHA-256 хэш публичного интерфейса проекта со значением хэша в метаданных api_map.md.
    • При расхождении сообщает о необходимости перегенерировать карту с помощью команды chutils dev generate-context -o api_map.md.
    • Данное предупреждение носит информационный характер и не блокирует сборку даже в режиме --strict.

9. FileDependencySyncRule (severity: warn)

Проверяет синхронность обновления связанных файлов и документации в соответствии с картой зависимостей. Данное правило гарантирует актуальность руководств при модификации исходного кода.

  • Что проверяет:
    • Наличие измененных файлов по глоб-шаблонам источников (например, "src/chutils/**/*.py").
    • Если ключ-источник начинается с префикса new: (например, "new:src/chutils/dev/rules/*.py"), проверка срабатывает только при создании новых (добавленных или неотслеживаемых) файлов в указанном каталоге, полностью игнорируя редактирование уже существующих файлов.
    • Если файлы-источники изменены (или созданы новые в режиме new:), проверяет, что хотя бы один из связанных зависимых файлов (например, "docs/api_map.md") также был обновлен в том же наборе изменений.
    • Поддержка --staged: В режиме chutils dev ai-lint --staged правило анализирует строго файлы, подготовленные к коммиту в индекс Git (git diff --cached), исключая незастейдженные изменения рабочей копии.
    • Обход глобальных списков игнорирования: Данное правило проверяет Git-изменения напрямую, не подавляясь записями из .gitignore, .chutilsignore или секцией ignore в pyproject.toml. Это гарантирует, что даже если файлы документации (например, docs/*.md) или индекса находятся в глобальном списке игнорирования линтера, правило все равно заставит их обновить. Подавить проверку можно только точечной инлайн-директивой в файле: # chutils: ignore[FileDependencySyncRule].

10. UpgradeCheckRule (severity: warn)

Обнаруживает изменение версии пакета в pyproject.toml по сравнению с Git HEAD или зафиксированной версией в .chutils/last_known_version.json.

  • Что проверяет:
    • Наличие увеличения версии при коммите или проверке.
    • При обнаружении обновления автоматически скачивает описания изменений релизов из GitHub API (с кэшированием запросов во избежание лимитов).
    • Генерирует файл AI-контекста миграции .chutils/migration_context.md с группировкой по категориям: Breaking Changes, New API и Deprecations.
  • Зачем: Позволяет AI-ассистентам мгновенно получать информацию об изменениях API при повышении версии библиотеки, предотвращая использование устаревших методов.

Настройка и отключение UpgradeCheckRule

Вы можете гибко управлять работой правила и генерацией чейнджлога:

  1. Через переменные окружения (ENV):

    • CHUTILS_DISABLE_UPGRADE_CHECK=1 — полностью отключает правило UpgradeCheckRule и сетевые запросы к GitHub.
    • CHUTILS_GENERATE_CHANGELOG=0 — отключает запись файла .chutils/migration_context.md, но сохраняет информационное предупреждение линтера.
  2. Через файл конфигурации (ai-lint.toml или pyproject.toml): ```toml # Отключение правила целиком [ai-lint] exclude_rules = ["UpgradeCheckRule"]

# Или точечная настройка поведения [ai-lint.upgrade_check] enabled = false # Полное отключение правила generate_changelog = false # Отключение только создания файла migration_context.md ```

  1. Через параметры CLI: bash chutils dev ai-lint --exclude-rules UpgradeCheckRule

11. LinterCoverageRule (severity: warn)

Проверяет степень покрытия исходного кода правилами отслеживания зависимостей в ai-lint.toml.

  • Что проверяет:
    • Проверяет, что каждый .py файл в пакете src/chutils/ охвачен хотя бы одним глоб-шаблоном в секции [dependencies] файла ai-lint.toml.
    • Поддержка --staged: В режиме chutils dev ai-lint --staged проверка покрытия выполняется только для измененных и добавленных файлов, подготовленных к коммиту.
    • Правило автоматически отключается, если секция [dependencies] отсутствует или пуста, либо если само правило FileDependencySyncRule отключено в конфигурации линтера (в rules или через exclude_rules).
  • Зачем: Гарантирует, что при добавлении новых модулей разработчики или ИИ-агенты не забудут прописать для них правила отслеживания документации, сохраняя её актуальность.

Исключение файлов из проверки покрытия

Если вам необходимо исключить определённые исходные файлы или папки из проверки покрытия правил зависимостей, вы можете использовать три способа:

  1. Файл .chutilsignore в корне проекта (Рекомендуется для постоянных исключений): Добавьте глоб-шаблоны файлов в .chutilsignore. Правило LinterCoverageRule автоматически пропустит их. text # .chutilsignore **/testing/**/*.py **/dev/scaffold.py

  2. Секция ignore в ai-lint.toml: Укажите пути в глобальном списке игнорирования линтера: toml [ai-lint] ignore = ["**/temp_*.py", "src/chutils/debug_helpers.py"]

  3. Инлайн-директивы в коде (Рекомендуется для точечного скрытия файлов): Добавьте специальный комментарий в начале (первые 10 строк) исходного файла: python # chutils: ignore[LinterCoverageRule] # или универсальный игнор всех правил: # chutils: ignore[all]

[!NOTE]
Обратите внимание, что файл .gitignore не используется правилом LinterCoverageRule для исключения файлов. Это сделано для того, чтобы исходные файлы кодовой базы, находящиеся во временном или локальном Git-игноре на этапе разработки, всё равно были должным образом задокументированы.


Настройка конфигурации

Вы можете настроить параметры ai-lint через стандартные файлы конфигурации проекта. Настройки объединяются в следующем приоритете (от высшего к низшему):

  1. Флаги CLI
  2. Переменные окружения (CH_DEV_AILINT_STRICT, CH_DEV_AILINT_IGNORE, CH_DEV_AILINT_RULES, CH_DEV_AILINT_CUSTOM_RULES_PATH, CH_DEV_AILINT_SOFT_MODE)
  3. Файл config.yml (секция Dev.AI-Lint)
  4. Файл pyproject.toml (секция [tool.chutils.ai-lint])
  5. Значения по умолчанию

Настройка в pyproject.toml

Рекомендуемый способ настройки проекта — добавление секции в pyproject.toml:

[tool.chutils.ai-lint]
strict = false
soft_mode = false
ignore = [
    ".git",
    ".venv",
    "__pycache__",
    "build",
    "dist",
    "tests",
    "docs",
    "src/chutils/testing"
]
rules = ["ManifestRule", "DocstringQualityRule", "SecurityHardcodeRule"]
custom_rules_path = ".chutils/custom_rules.py"
env_path = ".env"
example_path = ".env.example"

Настройка зависимостей файлов (File Dependency Sync)

Для правила FileDependencySyncRule вы можете задать сопоставление вложенной секцией [tool.chutils.ai-lint.dependencies] в pyproject.toml или в корневой секции [dependencies] во внешних файлах ai-lint.toml/ai-lint.json:

[tool.chutils.ai-lint.dependencies]
"src/chutils/**/*.py" = ["README.md", "docs/api_map.md"]
"schema.json" = ["docs/schema_guide.md"]
# Проверка сработает только при добавлении новых правил (файлов) в указанную директорию
"new:src/chutils/dev/rules/*.py" = ["docs/ai_lint.md"]

Механизм фильтрации и файл .chutilsignore

При выполнении аудита ai-lint объединяет правила скрытия путей из нескольких источников в следующем порядке приоритета:

  1. Флаги CLI (--ignore "<pattern>"): высший приоритет, перекрывают любые глобальные настройки.
  2. Секция ignore в pyproject.toml ([tool.chutils.ai-lint]) / ai-lint.toml.
  3. Файл .chutilsignore в корне проекта: специализированный маскировочный файл формата gitignore, позволяющий отсечь файлы от ИИ без их скрытия от Git.
  4. Файл .gitignore в корне проекта: базовые правила игнорирования Git.

[!NOTE]
Особенность правила LinterCoverageRule: Данное правило намерено игнорирует .gitignore, но учитывает .chutilsignore и секцию ignore в pyproject.toml. Это гарантирует, что локально неотслеживаемые исходные .py файлы проекта всё равно будут проверены на наличие документации и правил связей.

Пример .chutilsignore:

# Игнорировать автогенерированные файлы и временные отчерки для AI
src/chutils/dev/ast_indexer.py
*.tmp
temp/
tests/fixtures/*

Создание собственных правил (Custom Rules)

Если вам необходимо внедрить специфичные для вашего проекта архитектурные правила, вы можете написать свои собственные правила на Python.

Базовые классы

Пользовательское правило должно быть классом, унаследованным от chutils.dev.ai_lint.Rule. Интерфейс правила выглядит следующим образом:

from typing import Optional


class LintResult:
    rule_name: str  # Имя правила, создавшего результат
    message: str  # Сообщение об ошибке/предупреждении
    severity: str  # Уровень критичности: "error" или "warn"
    file_path: Optional[str]  # Абсолютный путь к файлу с проблемой (опционально)
    line_number: Optional[
        int
    ]  # Номер строки с проблемой (1-индексированный, опционально)
    fix_suggestion: Optional[str]  # Совет по исправлению проблемы (опционально)


class Rule:
    name: str = ""  # Уникальное имя правила
    description: str = ""  # Краткое описание правила
    severity: str = "error"  # Уровень критичности по умолчанию ("error" или "warn")

    def check(self, base_dir: str, files: list[str]) -> list[LintResult]:
        """
        Выполняет проверку. Должен возвращать список LintResult.

        Args:
            base_dir: Абсолютный путь к корню проекта.
            files: Список абсолютных путей ко всем неигнорируемым файлам проекта.
        """
        raise NotImplementedError

Шаг 1. Написание правила

Создайте файл .chutils/custom_rules.py в вашем проекте. Например, напишем правило NoAnyTypeRule, которое запрещает использовать Any в аннотациях типов (согласно Zero-Any Strategy):

import ast
from pathlib import Path
from chutils.dev.ai_lint import Rule, LintResult


class AnyTypeVisitor(ast.NodeVisitor):
    def __init__(self, file_path: str, rule_name: str) -> None:
        self.file_path = file_path
        self.rule_name = rule_name
        self.issues: list[LintResult] = []

    def visit_Name(self, node: ast.Name) -> None:
        # Проверяем использование имени Any
        if node.id == "Any":
            self.issues.append(
                LintResult(
                    rule_name=self.rule_name,
                    message="Обнаружено использование типа 'Any' в аннотации.",
                    severity="error",
                    file_path=self.file_path,
                    line_number=node.lineno,
                    fix_suggestion="Используйте более конкретный тип или Union/Generic вместо Any.",
                )
            )
        self.generic_visit(node)


class NoAnyTypeRule(Rule):
    name = "NoAnyTypeRule"
    description = "Запрещает использование типа 'Any' в кодовой базе для соблюдения строгой типизации."
    severity = "error"

    def check(self, base_dir: str, files: list[str]) -> list[LintResult]:
        results: list[LintResult] = []
        for file_path in files:
            # Проверяем только Python-файлы и исключаем тесты
            if not file_path.endswith(".py"):
                continue
            if "tests" in Path(file_path).parts:
                continue

            try:
                with open(file_path, "r", encoding="utf-8") as f:
                    content = f.read()

                # Парсим файл в AST и обходим его
                tree = ast.parse(content)
                visitor = AnyTypeVisitor(file_path, self.name)
                visitor.visit(tree)
                results.extend(visitor.issues)
            except Exception as e:
                # В случае синтаксических ошибок пропускаем файл
                pass
        return results

Шаг 2. Подключение правила

Чтобы подключить созданное правило, укажите путь к нему в вашем pyproject.toml:

[tool.chutils.ai-lint]
custom_rules_path = ".chutils/custom_rules.py"

Или передайте путь при запуске команды:

chutils dev ai-lint --custom-rules-path ".chutils/custom_rules.py"

При запуске линтер динамически импортирует ваш файл, найдет все классы, унаследованные от Rule, создаст их экземпляры и выполнит проверку наряду со встроенными правилами.


Интеграция в CI/CD

Автоматическая проверка AI-готовности кодовой базы при каждом коммите или Pull Request помогает поддерживать проект в идеальном состоянии для работы AI-агентов.

GitHub Actions

Создайте файл .github/workflows/ai-lint.yml:

name: AI Readiness Lint

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.13'

      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install poetry
          poetry install

      - name: Run AI Linter
        run: |
          poetry run python -m chutils dev ai-lint --strict