[1] major Файл: ai-review.js Строка: 133-176 Тип: error-handling Описание: При падении `downloadFile` (например, HTTP 404) поток всё равно создаёт `deepseek_review_prompt.txt` и `fs.existsSync` возвращает `true`, поэтому функция читаёт частично записанный/пустой файл и возвращает его вместо перехода на файл из репозитория; fallback никогда не срабатывает. Нужно удалять файл при ошибке загрузки или проверять, что скачанный файл непуст, прежде чем использовать его, чтобы гарантировать повторное чтение из репозитория и логировать исходную ошибку. [2] critical Файл: ai-review.js Строка: 1172-1178 Тип: security Описание: Команда `sshpass -p "${CONFIG.sshPassword}" ssh … "mkdir -p …"` передаёт пароль SSH в аргументах командной строки, что моментально раскрывает его через `ps` или журналы и делает автоматизацию уязвимой. Надёжнее перейти на авторизацию по ключу (или использовать `ssh-agent`/`sshpass` через stdin), а пароль хранить в защищённом хранилище; если ключи недоступны — хотя бы считывать пароль из защищённого файла и не включать его в команду. [3] critical Файл: ai-review.js Строка: 1180-1199 Тип: security Описание: Аналогичная проблема с `sshpass -p "${CONFIG.sshPassword}" scp …` при копировании `.htaccess` и отчёта — пароль снова видно в аргументах, а команда запускается в unsandboxed среде. Решение: перейти на SSH-ключи, либо, если пароль неизбежен, передавать его через stdin (например, через `sshpass -f`) и сократить количество команд, в которых он упоминается (внутри `scp`/`ssh`). [4] info Файл: ai-review.js Строка: 795-827 Тип: error-handling Описание: Ложно-положительное срабатывание — `getOpenAIApiKey` уже проверяет, что `CONFIG.codexAuthJson` доступен, парсит JSON из файла или строки, убеждается, что это объект, и требует непустой `tokens.access_token`. Добавочные проверки не нужны, текущая логика либо возвращает токен, либо громко завершает процесс. [5] major Файл: ai-review.js Строка: 358-389 Тип: performance Описание: `buildFilesContentPrompt` собирает содержимое всех изменённых файлов с суммарным размером без ограничений. В больших репозиториях это легко превысит лимиты Codex/DeepSeek (или вызовет timeouts) и приведёт к отказу API. Нужно ограничить накопление (например, по байтам при сумме `totalSize`, пропускать слишком большие файлы, дробить вывод на несколько промптов или сокращать содержимое до первых N строк). [6] major Файл: ai-review.js Строка: 420-520 Тип: code-smell Описание: `parseTextReport` выполняет неограниченное число правил (определяет маркеры, разбивает на строки, тримит, парсит разные паттерны, собирает контекст, обрабатывает fallback и т.д.) в одном монструозном цикле; поддерживать и тестировать такую функцию тяжело, а мелкие изменения легко сломают новый формат. Разбейте парсер на шаги (фильтрация маркеров, нормализация, парсинг полей, накопление описаний), заведите юнит-тесты на каждый блок и сократите количество вложенных условий. [7] major Файл: ai-review/clients/base.js Строка: 1-7 Тип: bug Описание: Абстрактный класс `AIClient` содержит метод `analyze`, который всегда выбрасывает `Error('not implemented')`; поскольку нигде нет реализации, любая попытка использовать `AIClient` или его наследников вызовет исключение. Нужно реализовать хотя бы базовый контракт (проверять вход, подставить запрос на нужный AI) или убрать класс/подклассы, чтобы они не попадали в prod. [8] major Файл: ai-review/clients/codex.js Строка: 1-8 Тип: bug Описание: `CodexClient.analyze` пока просто выбрасывает `Error('CodexClient.analyze is not implemented yet')`, то есть в текущем состоянии невозможно вызвать Codex-аналитик — скрипт будет падать, когда потребуется анализ серьёзных проблем. Необходимо реализовать метод, который подготавливает промпт, вызывает Codex CLI/API и собирает результат. [9] major Файл: ai-review/clients/deepseek.js Строка: 1-8 Тип: bug Описание: `DeepSeekClient.analyze` аналогично неизвёрнут — при вызове сразу выбрасывается `Error`. Без реального клиента нельзя проводить первичный анализ DeepSeek, а весь гибридный процесс недоступен. Надо реализовать этот метод или удалить модуль. [10] major Файл: ai-review/integrations/pachka.js Строка: 1-8 Тип: bug Описание: Экспорт `sendToPachka` бросает `Error('sendToPachka is not implemented yet')`, поэтому интеграция Пачки не работает, а код, который импортирует модуль, мгновенно завершится с исключением. Для устойчивости нужно либо убрать модуль из пайплайна, либо реализовать отправку в Пачку и обработку ошибок. [11] info Файл: ai-review/integrations/server-deploy.js Строка: 1-8 Тип: bug Описание: Модуль содержит заглушку `saveReportToServer`, но он нигде не импортируется (скрипт использует свою локальную реализацию в ai-review.js), поэтому на текущий момент это ложное срабатывание — никакой функциональности не ломается. Можно удалить файл или реализовать его, если он планируется к использованию. [12] major Файл: ai-review/parsers/issues.js Строка: 1-14 Тип: bug Описание: Функция `createIssue` просто выбрасывает `Error('issues.createIssue is not implemented yet')`, поэтому любая логика, которая ожидает преобразованную структуру issue (например, формирование отчётов или отправка в GitLab) не сможет работать. Нужно реализовать генерацию объекта (описание, severity, location, fingerprint) или удалить парсер. [13] major Файл: ai-review/parsers/text-report.js Строка: 1-9 Тип: bug Описание: `parseTextReport` тоже — просто ошибка. Это единственный парсер, который должен принимать вывод DeepSeek или Codex и превращать его в массив структур; без реализации весь процесс анализа отчётов мёртв. Требуется написать реальный парсер (по мотивам parseTextReport внутри ai-review.js или иного формата). [14] major Файл: ai-review/utils/file-utils.js Строка: 1-14 Тип: bug Описание: `readFileContents` и `buildFilesContentPrompt` экспортируются, но обе функции бросают `Error('… not implemented yet')`. Если кто-то попытается импортировать этот модуль (или будет рефакторинг), выполнение прекратится. Нужно либо реализовать функциональность или удалить экспорт/заменить на рабочие версии (в ai-review.js уже есть аналогичные реализации). [15] major Файл: ai-review/utils/git.js Строка: 1-14 Тип: bug Описание: `getChangedFiles` и `isLocalFile` тоже делают `throw new Error('… not implemented yet')`. Асинхронные/CI-скрипты, которые захотят пользоваться этими утилитами, упадут, а кодbase содержит реальную реализацию прямо в ai-review.js. Рекомендуется реализовать рабочую обёртку (или убрать файл, чтобы не вводить в заблуждение). [16] info Файл: src/app/api/projects/[id]/figma-links/route.ts Строка: 46-195 Тип: error-handling Описание: Ложно-положительное замечание: функция `refreshPreviewInBackground` обёрнута в `try/catch`, ошибки логируются, и обработка Figma API (fetch + json) обрабатывает неуспех `metadataResponse.ok`. Нет явного пропуска обработки ошибок, она уже есть. [17] info Файл: src/app/api/projects/[id]/figma-links/route.ts Строка: 120-159 Тип: performance Описание: Ложно-положительное замечание: `getPreviewNodeCandidates` возвращает максимум несколько ID (URL, первый рендеримый, корневой), поэтому единственный `fetch` не отправляет «бесконечное количество» nodeId и не превышает лимиты Figma. Ограничений по chunking не требуется до появления реальных сценариев с десятками узлов. [18] critical Файл: src/lib/figma-token.ts Строка: 10-29 Тип: security Описание: Если `FIGMA_TOKEN_ENCRYPTION_KEY` не задан, код падает обратно на `JWT_SECRET || 'default-secret'`, то есть используется уже известный секрет или даже статическая строка. Любая компрометация JWT-секрета (или знание `default-secret`) сразу позволяет расшифровать все Figma-токены. Нужно требовать обязательной настройки `FIGMA_TOKEN_ENCRYPTION_KEY`, не генерировать ключ из JWT, а при отсутствии — отклонять запуск. [19] info Файл: src/lib/figma-token.ts Строка: 33-80 Тип: validation Описание: Ложно-положительное замечание — `encryptToken` уже проверяет `if (!token || token.trim() === '') { throw new Error('Token cannot be empty'); }`, так что пустые токены отсекаются до шифрования. [20] major Файл: src/modules/notifications/hooks/usePushSubscription.ts Строка: 12-59 Тип: security Описание: `waitForServiceWorker` делает `Promise.race` между `navigator.serviceWorker.ready` и собственным таймаутом: если `ready` разрешится первым, таймаут всё равно исполнится и вызовет `reject`, порождая необработанное исключение/ошибочные сообщения и потенциально сбрасывая уже зарегистрированный sw. Надо сохранять таймаут (`const timeout = setTimeout…`) и очищать его (`clearTimeout(timeout)`) внутри `.then`/`.finally` после `navigator.serviceWorker.ready`, либо переписать логику без race: например, подписываться на `statechange` и при любом удачном завершении вручную `resolve` уже существующего `Promise`, не создавая дополнительного reject. [21] major Файл: src/modules/projects/components/FigmaLinksSection.tsx Строка: 87-200 Тип: performance Описание: Поллинг `setInterval` запускается при создании ссылки и очищается только когда превью найдено/лимит попыток. Если пользователь уйдёт со страницы или событие завершится иначе, интервал продолжит выполнять `getProjectFigmaLinks` каждые 2 секунды и удерживать закрытие. Нужно хранить идентификатор `pollingInterval` (например, через `useRef`) и очищать его в `useEffect` cleanup/в `finally` даже при уходе, чтобы избежать утечек и лишних API-запросов.