---
name: n-lint
description: >-
  Запустити дельта-лінт (npx @nitra/cursor lint) по змінених файлах vs origin, виправити порушення й підтвердити чистий вихід
version: '1.0'
---

# n-lint — лінт проєкту по змінених файлах

## Мета

Прогнати **`npx @nitra/cursor lint`** (**дельта-режим**: лише файли, змінені vs `origin`), усунути порушення (авто- та вручну) і переконатися, що команда завершується з кодом **`0`**.

> **Чому дельта, не `--full`?** `--full` — CI-режим: сканує весь репо незалежно від змін. Під час задачі це зайво — перевіряємо лише те, що змінили. `lint --full` запускати **не треба**.

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

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

## Workflow

1. **Запуск** — дельта-лінт по змінених файлах:

```bash
npx @nitra/cursor lint
```

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

3. **Цикл** — повторюй кроки 1–2, доки **`npx @nitra/cursor lint`** не завершиться успішно.

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

```bash
npx @nitra/cursor 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**, **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/потоків.
- Якщо тестів **немає** або вони **не покривають** блок, який змінюєш — **спочатку** додай/розшир тести, переконайся, що вони стабільно проходять, **потім** роби рефакторинг, **потім** знову прогони тести й **`npx @nitra/cursor lint`**, щоб підтвердити, що функціональність коректна й лінт чистий.
- Якщо після рефакторингу тести або лінт падають — **не** залишай «половинчастий» рефакторинг: відкотись або доведи зміни до зеленого стану.

## Паралелізм і навантаження на macOS

**Дельта-лінт — без черги.** `npx @nitra/cursor lint` (дельта, типовий задачний прогін) не бере лока: паралельні запуски по різних файлах дозволені й не конфліктують.

**`lint --full` — вбудована глобальна черга** (spec 2026-07-03): у кожен момент виконується один full-прогін на машину. Запуск у черзі сам показує свою позицію, решту черги і живий прогрес-бар активного прогону (`⏳ lint --full у черзі #2/3 · працює pid … [██████░░] 5/12 концернів · знайдено … · виправлено … · js/eslint`). Ідентичний повтор --full на незміненому дереві дедуплікується (`♻️ … пропускаю`). Агенту нічого координувати вручну.

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

1. **Дельта-лінт** (`npx @nitra/cursor lint`) — можна запускати повторно; паралелізм із субагентами по різних файлах — OK.
2. **`lint --full`** у цьому скілі **не запускати** — це CI-команда, не задачна.
3. Якщо `--full`-запуск показує `⏳ lint --full у черзі…` — інший full-прогін ще працює; це штатна черга, не зависання. Fail-closed таймаут черги — 45 хв.

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

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

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

## Примітка

Окремих команд **`check`** / **`fix`** немає — **`npx @nitra/cursor lint`** це unified lint surface: detect + fix-by-default (авто-fix лінтерів/формату і програмних правил пакета **`@nitra/cursor`** в одному проході). **`--no-fix`** — лише detect, без записів.

Для CI або явного повного сканування всього репо незалежно від змін — `npx @nitra/cursor lint --full`. Але в рамках задачі це **зайво**.
