---
docgen:
  source: npm/scripts/lib/discover-check-rules-from-cursor.mjs
  crc: 5e312e3e
---

# discover-check-rules-from-cursor.mjs

## Огляд

Модуль `discover-check-rules-from-cursor.mjs` визначає, які саме id правил повинні бути запущені командою `npx @nitra/cursor fix`, коли користувач викликає її **без аргументів**. Він узгоджує два джерела:

1. **Файлова система проєкту** — базові імена `*.mdc` у директорії `.cursor/rules/` (тобто правила, які реально присутні у конкретному репо).
2. **Реєстр пакета** — список id правил, для яких у пакеті `@nitra/cursor` існує programmatic перевірка (JS-концерн або policy з `target.json`); цей список зазвичай приходить із функції `discoverCheckableRules`.

Результат — впорядкований унікальний масив id, у якому:

- порядок диктується порядком файлів `*.mdc` у директорії правил (як їх передали ззовні, зазвичай відсортованими);
- залишаються лише ті id, для яких у пакеті є реальна перевірка;
- видалено префікс `n-`, оскільки внутрішня команда `check <id>` оперує «короткими» id (наприклад, `bun`, а не `n-bun`).

Файл містить чисті pure-функції без побічних ефектів — без `fs`, без `process`, без I/O. Все читання директорії правил і реєстру здійснюється викликачем; цей модуль лише поєднує два готових списки.

## Експорти / API

Модуль має три публічних експорти у форматі ES modules (`.mjs`):

| Експорт                             | Тип        | Призначення                                                                                      |
| ----------------------------------- | ---------- | ------------------------------------------------------------------------------------------------ |
| `MANAGED_RULE_FILE_PREFIX`          | `string`   | Константа `'n-'` — префікс імен файлів `.mdc`, які вважаються «керованими» пакетом.              |
| `mdcBasenameToCheckId`              | `function` | Перетворює базове ім'я `*.mdc` (із розширенням або без) у id для CLI-команди `check`.            |
| `discoverCheckRulesFromCursorRules` | `function` | Будує впорядкований список id перевірок як перетин «доступних у пакеті» та «присутніх на диску». |

Усі експорти — іменовані (`export const` / `export function`); default-export відсутній.

## Функції

### `mdcBasenameToCheckId(mdcBasename)`

**Сигнатура:**

```js
export function mdcBasenameToCheckId(mdcBasename: string): string
```

**Параметри:**

- `mdcBasename` — рядок, який може бути:
  - чистим базовим іменем файлу, наприклад `'n-bun.mdc'` або `'my-rule.mdc'`;
  - відносним/абсолютним шляхом, наприклад `'.cursor/rules/n-bun.mdc'` (функція сама візьме сегмент після останнього `/`).

**Поведінка:**

1. Якщо у вхідному рядку є символ `/`, береться підрядок після останнього `/` — таким чином приймаються як «чисті» імена, так і шляхи.
2. Якщо отриманий рядок закінчується на `.mdc`, розширення відсікається.
3. Якщо те, що залишилось, починається з `MANAGED_RULE_FILE_PREFIX` (`'n-'`), цей префікс також знімається.
4. Повертається отриманий «короткий» id.

**Повертає:**

- `string` — id, придатний для команди `check <id>`. Приклади:
  - `'n-bun.mdc'` → `'bun'`
  - `'.cursor/rules/n-vue.mdc'` → `'vue'`
  - `'my-rule.mdc'` → `'my-rule'`
  - `'n-text'` (без розширення) → `'text'`

**Side effects:** немає. Функція суто детермінована, не звертається до файлової системи, не мутує аргументи.

**Крайні випадки:**

- Backslashes (`\\`) як роздільник шляху **не** розпізнаються — функція оперує лише `/`. Це безпечно для шляхів, які прийшли від утиліт на кшталт `fs.readdir`, що повертають базові імена, або з `path.posix`-стилю.
- Якщо вхід уже без `.mdc`, він обробляється тим самим алгоритмом (просто пропускається крок зняття розширення).
- Якщо файл, прибравши `.mdc`, дорівнює саме `'n-'`, повернеться порожній рядок — це не очікувана реальна ситуація, бо керованих правил із порожнім id не буває.

### `discoverCheckRulesFromCursorRules(available, mdcBasenames)`

**Сигнатура:**

```js
export function discoverCheckRulesFromCursorRules(
  available: string[],
  mdcBasenames: string[],
): string[]
```

**Параметри:**

- `available` — масив id, які пакет уміє перевіряти (програмні перевірки JS-концерну або policy з `target.json`). Зазвичай — результат `discoverCheckableRules` із сусіднього модуля `discover-checkable-rules.mjs`. Очікується алфавітний порядок, але алгоритм не залежить від нього.
- `mdcBasenames` — масив базових імен файлів `*.mdc`, які реально присутні у `.cursor/rules/` поточного проєкту. Очікується, що масив уже відсортований викликачем (порядок цього масиву визначає порядок результату).

**Поведінка:**

1. Створюється `Set` `seen` для дедуплікації результату.
2. Створюється масив `ordered` для збереження порядку входу `mdcBasenames`.
3. Для кожного `basename`:
   - обчислюється id через `mdcBasenameToCheckId(basename)`;
   - якщо id присутній у `available` **і** ще не доданий — додається в `ordered` та позначається у `seen`.
4. Повертається `ordered`.

**Повертає:**

- `string[]` — впорядкований масив унікальних id перевірок:
  - **тільки** ті, що одночасно (а) мають programmatic-перевірку в пакеті і (б) представлені файлом `*.mdc` у `.cursor/rules/`;
  - порядок диктується `mdcBasenames` (тобто реальним порядком файлів у директорії, як його передав викликач);
  - кожен id зустрічається не більше одного разу.

**Side effects:** немає. Функція pure, не читає диск, не мутує аргументи (`Set`/`Array` створюються локально).

**Крайні випадки:**

- Якщо `mdcBasenames` порожній — повертається `[]`.
- Якщо жоден `id`, отриманий з імен `.mdc`, не присутній у `available` — повертається `[]` (типово для проєктів, де `.cursor/rules/` містить лише сторонні правила без programmatic-перевірок у пакеті).
- Дублікати у `mdcBasenames` (наприклад, два файли, що дають один і той самий id після відсікання префікса) включаються лише один раз.

## Залежності

- **Зовнішні (npm/Node):** немає. Файл не використовує жодних імпортів — ні з Node core (`fs`, `path` тощо), ні з npm-пакетів.
- **Внутрішні (проєкт):** немає прямих імпортів. Очікується, що:
  - аргумент `available` формується сусіднім модулем `discover-checkable-rules.mjs` (експорт `discoverCheckableRules`);
  - аргумент `mdcBasenames` формується викликачем читанням `.cursor/rules/` (через `fs.readdirSync`/`fs.promises.readdir` із подальшим фільтром `.endsWith('.mdc')` та сортуванням).

Така архітектура свідомо відокремлює I/O від чистої логіки: цей модуль легко тестувати без файлової системи.

## Потік виконання / Використання

Типовий сценарій — `npx @nitra/cursor fix` без аргументів. CLI-входу потрібно вирішити, які id перевірок запустити за замовчуванням; для цього він:

1. Отримує список програмно перевірюваних правил пакета (`available = discoverCheckableRules(...)`).
2. Читає директорію `.cursor/rules/` поточного проєкту й відбирає файли з розширенням `.mdc` (`mdcBasenames`), сортує їх.
3. Викликає `discoverCheckRulesFromCursorRules(available, mdcBasenames)` і отримує **підсумковий** список id.
4. Для кожного id послідовно запускає команду `check <id>` (наприклад, через `run-rule-cli.mjs`).

Приклад інлайн-використання (псевдокод викликача):

```js
import { readdirSync } from 'node:fs'
import { discoverCheckableRules } from './discover-checkable-rules.mjs'
import { discoverCheckRulesFromCursorRules } from './discover-check-rules-from-cursor.mjs'

const available = discoverCheckableRules(/* … */)
const mdcBasenames = readdirSync('.cursor/rules')
  .filter(f => f.endsWith('.mdc'))
  .sort()
const idsToRun = discoverCheckRulesFromCursorRules(available, mdcBasenames)
// idsToRun: наприклад, ['adr', 'bun', 'ci4', 'text', 'vue']
```

Приклад трансформації імен:

| Файл у `.cursor/rules/`    | id після `mdcBasenameToCheckId` | Потрапляє у результат?                             |
| -------------------------- | ------------------------------- | -------------------------------------------------- |
| `n-bun.mdc`                | `bun`                           | Так, якщо `available.includes('bun')`              |
| `n-vue.mdc`                | `vue`                           | Так, якщо `available.includes('vue')`              |
| `my-rule.mdc`              | `my-rule`                       | Лише якщо пакет реєструє `my-rule` як перевірюване |
| `.cursor/rules/n-text.mdc` | `text`                          | Так, якщо `available.includes('text')`             |
| `n-bun.mdc` (двічі)        | `bun`                           | У результаті — один раз                            |

Таким чином модуль є тонким, але критичним «клейовим» шаром між:

- **диском проєкту** (що користувач реально хоче перевіряти, судячи з вмісту `.cursor/rules/`)
- **реєстром пакета** (що пакет фізично вміє перевіряти автоматично).

Файл свідомо тримається без I/O і без зовнішніх залежностей, щоб залишатись повністю детермінованим і легко покритим юніт-тестами (див. сусідню директорію `tests/`).
