---
name: n-lint
description: >-
  Запустити кореневий bun run lint, виправити порушення й підтвердити чистий вихід
---

# n-lint — лінт проєкту через кореневий скрипт

## Мета

Один раз узгоджено прогнати весь ланцюжок **`lint`** з кореневого **`package.json`**, усунути помилки (авто- та вручну) і переконатися, що **`bun run lint`** завершується з кодом **`0`**.

## Передумови

- Поточна робоча директорія — **корінь репозиторію**, де є **`package.json`** зі скриптом **`lint`**.
- Залежності встановлені (**`bun i`**) — якщо після правок змінювався **`package.json`** / lockfile, знову виконай **`bun i`** перед наступним запуском лінту.

## Workflow

1. **Запуск** — виконай повний лінт:

```bash
bun run lint
```

2. **Якщо exit code не 0** — проаналізуй вивід (останній упавший крок у ланцюжку **`lint`** часто видно з stderr / логів):
   - Де скрипт уже робить **auto-fix** (**`--fix`**, **`markdownlint-cli2 --fix`**, **`oxfmt`** тощо) — перезапусти **`bun run lint`** після змін файлів.
   - Де auto-fix **немає** (наприклад, **jscpd**, **cspell**, **zizmor**, перевірки без прапорця fix) — **за замовчуванням рефактори код проєкту**, щоб усунути порушення: перейменуй ідентифікатори, перепиши логіку, видали дублікати тощо. **Не** розширюй конфіги з винятками «мовчки» — див. блок **«Винятки в конфігурації»** нижче.
   - Якщо спрацьовує **`sonarjs/cognitive-complexity`** — див. окремий блок нижче.

3. **Цикл** — повторюй кроки 1–2, доки **`bun run lint`** не завершиться успішно. Після суттєвих правок за потреби ще раз **`bun run lint`**, щоб переконатися, що не зламав наступний крок у скрипті **`lint`**.

4. **Верифікація** — фінальна перевірка (обов’язково з кодом **0**):

```bash
bun run lint
```

5. **Результат** — коротко опиши, що саме виправлено; якщо щось блокує нульовий 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-lint**, **n-text**): виняток допустимий лише з **обґрунтованою** причиною, не як заміна рефакторингу для справжніх клонів / дублікатів.

### Коли обовʼязково питати користувача

Перед **будь-якою** правкою рядків із таблиці вище (або коментарем-винятком у коді) для **конкретного** порушення з поточного виводу лінту:

1. **Зупини** автоматичні правки конфігів.
2. **Один** виклик **`AskQuestion`** (або еквівалентне повідомлення з варіантами, якщо інструмент недоступний) — **одне питання на одне порушення** (або на одну логічну групу однакових jscpd-клонів у тому ж файлі).
3. У тексті питання коротко дай **контекст**: інструмент, файл:рядок, суть порушення (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/потоків.
- Якщо тестів **немає** або вони **не покривають** блок, який змінюєш — **спочатку** додай/розшир тести, переконайся, що вони стабільно проходять, **потім** роби рефакторинг, **потім** знову прогони тести й **`bun run lint`**, щоб підтвердити, що функціональність коректна й лінт чистий.
- Якщо після рефакторингу тести або лінт падають — **не** залишай «половинчастий» рефакторинг: відкотись або доведи зміни до зеленого стану.

## Навантаження на macOS (багато процесів `eslint` / лаги)

Часто це **не** «один ESLint розпаралелив себе всередині», а **кілька паралельних запусків** одного й того ж ланцюжка (**`bun run lint`**, **`bun run lint-js`**) у різних Bash-задачах агента (кілька shells). Кожен запуск = окремий процес **`eslint`** плюс **`oxlint`**, **`jscpd`** тощо — диск і CPU перегружаються.

**Чому взагалі кілька `eslint`:** оркестратор (Claude Code, Cursor тощо) може **розпаралелити** роботу: кілька **субагентів** / **паралельних Bash-задач** / **фонових shell**, і кожен **сам** виконує **`/n-lint`** або запускає **`eslint`**. Тоді навантаження **множиться**: не один прогон лінту, а **N прогонів** одночасно.

### Що робити агенту під час виконання цього скілу (обов’язково)

1. **Один** запуск **`bun run lint`** (або всі кроки **`lint`**, як у **`package.json`**) — у **одному** foreground shell, **без** `run_in_background` / фонових копій тієї ж команди.
2. **Не** викликати **паралельні субагенти** (subagent, Task, «розбий на N паралельних завдань») лише заради лінту в одному репозиторії. Лінт не потребує шардінгу: один процес, послідовно.
3. Якщо ти **батьок-сесія** й уже делегував дочірнім задачам **інші** кроки — **не** доручай дочірнім і **`bun run lint`**, залиш **лише** собі один **послідовний** прогон **після** змін у файлах, або **один** виклик цілого скілу на сесію.
4. Якщо сесія/користувач уже запускає лінт — **не** дублювати; зачекати завершення або **не** стартувати лінт повторно в іншому shell.

**Що можна змінити у проєкті (локально або в `package.json`)**

- **ESLint** (CLI): за потреби явно **`--concurrency off`** (див. **`eslint --help`**).
- **oxlint**: **`--threads=1`**, якщо потрібно зменшити навантаження на CPU.
- **ESLint cache**: **`--cache`** / **`--cache-location .eslintcache`** — менше повторного читання з диска.

Канонічний рядок **`lint-js`** у репозиторіях з **`check js-lint`** фіксований; додаткові прапорці — з узгодженням канону або в споживацькому проєкті окремо.

## Примітка

Цей скіл **не** замінює **`npx @nitra/cursor fix`**: **`lint`** перевіряє лінтери/формат у **`package.json`**, а **`check`** — програмні правила пакета **`@nitra/cursor`**. За потреби запускай обидва.
