Imported from nitra/telegram (
.cursor/skills/n-lint/SKILL.md). Install upstream withnpx skills add nitra/telegram --skill n-lint. Copyright stays with the author.
[!IMPORTANT] Worktree-only skill. Виконується виключно в окремому git-worktree (
.worktrees/<current-branch>-lint/) і не паралелиться — один інстанс за раз.
Крок 0 — preflight (обовʼязковий, перед будь-якими іншими діями). Якщо перевірка падає — STOP: не питай користувача про назву гілки, а сам створи worktree від поточної гілки за конвенцією <current-branch>-lint. Суфікс lint — коротка (до 10 символів) транслітерація задачі. Не виконуй жоден наступний крок скіла, поки preflight не завершився успіхом.
pwd
git rev-parse --show-toplevel
git branch --show-current
Root-assert. Якщо pwd не збігається з виводом git rev-parse --show-toplevel — ти в піддиректорії робочого дерева (worktree-шляхи нижче відносні до кореня репо). Спершу перейди в корінь: cd <toplevel> (literal-шлях із виводу), і лише тоді продовжуй preflight. Не створюй worktree з піддиректорії — cd .worktrees/<…> звідти впаде.
Якщо git rev-parse --show-toplevel показав, що ти не в .worktrees/, візьми вивід git branch --show-current як <current-branch> і виконай literal-команди без shell expansion (без command substitution, variable expansion чи backticks). Наприклад, якщо поточна гілка feature/x:
npx @7n/mt worktree create "feature/x-lint" "n-lint: worktree-only skill"
cd ".worktrees/feature-x-lint"
Тобто branch-argument лишає slash як у git-гілці, а шлях для cd бере sanitized форму: slash → -.
Крок 0.1 — bootstrap у новому дереві (після cd). Дерево щойно створене й без node_modules. Постав залежності локально — тоді npx @nitra/cursor <cmd> бере локальну копію без походу в реєстр:
bun install
n-lint — лінт проєкту по змінених файлах
Мета
Прогнати npx @nitra/cursor lint (дельта-режим: лише файли, змінені vs origin), усунути порушення (авто- та вручну) і переконатися, що команда завершується з кодом 0.
Чому дельта, не
--full?--full— CI-режим: сканує весь репо незалежно від змін. Під час задачі це зайво — перевіряємо лише те, що змінили.lint --fullзапускати не треба.
Передумови
- Поточна робоча директорія — корінь репозиторію.
- Залежності встановлені (
bun i) — якщо після правок змінювавсяpackage.json/ lockfile, знову виконайbun iперед наступним запуском лінту.
Workflow
- Запуск — дельта-лінт по змінених файлах:
npx @nitra/cursor lint
-
Якщо exit code не 0 — проаналізуй вивід:
- Де лінт уже робить auto-fix (
--fix,oxfmtтощо) — перезапустиnpx @nitra/cursor lintпісля змін файлів. - Де auto-fix немає (наприклад, jscpd, cspell, zizmor) — за замовчуванням рефактори код проєкту, щоб усунути порушення. Не розширюй конфіги з винятками «мовчки» — див. блок «Винятки в конфігурації» нижче.
- Якщо спрацьовує
sonarjs/cognitive-complexity— див. окремий блок нижче.
- Де лінт уже робить auto-fix (
-
Цикл — повторюй кроки 1–2, доки
npx @nitra/cursor lintне завершиться успішно. -
Верифікація — фінальна перевірка (обов'язково з кодом 0):
npx @nitra/cursor lint
- Результат — коротко опиши, що саме виправлено; якщо щось блокує нульовий exit code — залиш чітке пояснення й наступні кроки для людини.
Винятки в конфігурації — інтерактивне рішення
Коли порушення не зникає auto-fix і перша думка — «додати в ignore / words / minLines» — STOP. Заборонено мовчки редагувати конфіги лише щоб зеленіти лінт без згоди користувача.
Конфіги і коментарі, які потребують зупинки (неповний список — будь-який аналог):
| Інструмент | Типові файли / зміни |
|---|---|
| jscpd | .jscpd.json → ignore, minLines |
| cspell | .cspell.json → words, ignorePaths; .cspellignore |
| knip | knip.json → ignore, ignoreDependencies, ignoreBinaries, entry |
| oxlint / ESLint | .oxlintrc.json → ignorePatterns; eslint.config.js → ignores; eslint-disable / oxlint-disable у коді |
| інше | .v8rignore, .stylelintignore, .trufflehog-exclude, розширення ignores у workflow-конфігах |
Політика узгоджена з .cursor/rules/ (зокрема n-js, n-text): виняток допустимий лише з обґрунтованою причиною, не як заміна рефакторингу для справжніх клонів / дублікатів.
Коли обовʼязково питати користувача
Перед будь-якою правкою рядків із таблиці вище (або коментарем-винятком у коді) для конкретного порушення з поточного виводу лінту:
- Зупини автоматичні правки конфігів.
- Один виклик
AskQuestion(або еквівалентне повідомлення з варіантами, якщо інструмент недоступний) — одне питання на одне порушення (або на одну логічну групу однакових jscpd-клонів у тому ж файлі). - У тексті питання коротко дай контекст: інструмент, файл:рядок, суть порушення (1–2 речення), що саме пропонується додати в конфіг (точний glob / слово / ключ).
Варіанти відповіді (мінімум такі; allow_multiple: false):
| id | label (українською) | Дія агента |
|---|---|---|
refactor |
Рефакторинг коду — усунути дублікат / помилку в коді (рекомендовано за замовчуванням) | Рефакторинг; конфіг не чіпати |
ignore-once |
Точковий виняток у конфігу — додати ignore/words/minLines з обґрунтуванням у коментарі PR/відповіді | Після вибору — мінімальна зміна конфігу + 1 речення чому це не рефакторинг |
skip |
Залишити як є — не чіпати ні код, ні конфіг зараз | Не змінювати; у фінальному резюме — що лишилось червоним |
explain |
Потрібні деталі — поясни варіанти глибше | Розгорнути порівняння refactor vs ignore; знову запитати той самий набір варіантів |
Якщо користувач обрав ignore-once — у відповіді після зміни зафіксуй: який ключ конфігу змінено, який glob/слово додано, чому рефакторинг був недоречний (генерований код, формальний шаблон, легітимний термін без перекладу тощо).
Якщо користувач не відповів (сесія без інтерактиву) — не додавай винятки в конфіг; роби рефакторинг або залиш порушення з поясненням у кроці 5 workflow.
Приклад формулювання (jscpd)
jscpd: клон 42 рядки в
src/foo.ts↔src/bar.ts(однакова логіка валідації).
Варіанти: (A) винести спільну функцію; (B) додатиsrc/bar.tsу.jscpd.json→ignore; (C) пропустити зараз.
Не виконуй (B), поки користувач явно не обрав ignore-once.
sonarjs/cognitive-complexity
- Не додавай
eslint-disable(у т.ч. наsonarjs/cognitive-complexity) чи інші коментарі-винятки лише щоб приховати порушення — потрібен саме рефакторинг коду, щоб зменшити cognitive complexity. - Перед будь-яким рефакторингом перевір, чи є тести, які покривають змінювану поведінку:
- unit —
bun test(або скрипт тестів у відповідному пакеті репозиторію); - e2e — Playwright, якщо в проєкті він використовується для UI/потоків.
- unit —
- Якщо тестів немає або вони не покривають блок, який змінюєш — спочатку додай/розшир тести, переконайся, що вони стабільно проходять, потім роби рефакторинг, потім знову прогони тести й
npx @nitra/cursor lint, щоб підтвердити, що функціональність коректна й лінт чистий. - Якщо після рефакторингу тести або лінт падають — не залишай «половинчастий» рефакторинг: відкотись або доведи зміни до зеленого стану.
Паралелізм і навантаження на macOS
Паралельно по різних файлах — дозволено. Дельта-прогони на диз'юнктних наборах файлів не конфліктують.
Серіалізувати треба лише n-cursor lint --full (whole-tree CI-прогін) — запускати не більше одного за раз. Але цей скіл --full не використовує: npx @nitra/cursor lint (дельта) сам по собі не перевантажує репо.
Що робити агенту під час виконання цього скілу
- Дельта-лінт (
npx @nitra/cursor lint) — можна запускати повторно; паралелізм із субагентами по різних файлах — OK. lint --fullу цьому скілі не запускати — це CI-команда, не задачна.- Якщо сесія/користувач уже запускає
lint --full— не дублювати; зачекати завершення.
Що можна змінити у проєкті (локально або в package.json)
- ESLint (CLI): за потреби явно
--concurrency off(див.eslint --help). - oxlint:
--threads=1, якщо потрібно зменшити навантаження на CPU. - ESLint cache:
--cache/--cache-location .eslintcache— менше повторного читання з диска.
Канонічний рядок lint-js у репозиторіях з check js фіксований; додаткові прапорці — з узгодженням канону або в споживацькому проєкті окремо.
Примітка
Цей скіл не замінює npx @nitra/cursor fix: lint перевіряє лінтери/формат (eslint, oxlint, rego тощо), а check / fix — програмні правила пакета @nitra/cursor. За потреби запускай обидва.
Для CI або явного повного сканування всього репо незалежно від змін — npx @nitra/cursor lint --full. Але в рамках задачі це зайво.