@nitra/cursor 1.29.1 → 1.29.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.29.3] - 2026-05-29
8
+
9
+ ### Changed
10
+
11
+ - **`rules/test/js/stryker_config.mjs`** — концерн `stryker_config` додає у кореневий `.gitignore` ще й `**/coverage/` (весь ефемерний output vitest v8 coverage: `lcov.info` + HTML `lcov-report/`) поряд із `**/reports/stryker/`. Coverage регенерується кожним прогоном, фінальні метрики живуть у `COVERAGE.md`; gitignore не заважає `n-cursor coverage` читати `lcov.info` у тому ж прогоні. Секцію .gitignore перейменовано на `# Test artifacts: Stryker + coverage`. Документація — `test.mdc`.
12
+ - **`rules/test/js/stryker_config.mjs`**, **`rules/test/js/cargo_mutants_config.mjs`**, **`rules/tauri/js/cargo_mutants_config.mjs`** — `check()` → `check(cwd = process.cwd())` за test.mdc canon (production functions приймають перший параметр `cwd`). До цього усі три концерни читали лише `process.cwd()`, через що тести змушені були викликати `process.chdir(dir)` (з обходом regex-сканера `no-process-chdir` через `import { chdir } from 'node:process'`). Stryker крутить vitest у threads-pool (`@stryker-mutator/vitest-runner` форсує `pool: 'threads'`, перетираючи `pool: 'forks'` у `vitest.config.js`), де `process.chdir` не підтримується → dry-run обривався з `process.chdir() is not supported in workers`. Тести у `tests/stryker_config.test.mjs`, `tests/cargo_mutants_config.test.mjs` (обидва правила) переписано на `runCheckIn(dir) → check(dir)` без chdir.
13
+
14
+ ### Fixed
15
+
16
+ - **`tests/integration-repo-checks.test.mjs`** — `test.skipIf(env.STRYKER_MUTATOR_WORKER)` для test `узгоджені з поточним деревом cursor` (env-import із `node:process` за `js-run.mdc`). Stryker копіює репо у `reports/stryker/.tmp/sandbox-XXX/` і запускає тести звідти; `REPO_ROOT = join(import.meta.dirname, '..', '..')` резолвиться у sandbox-копію, де `checkNpmModule` валідує CHANGELOG vs HEAD-version без живого `.git/` і повертає 1 → Stryker dry-run обривається (`ConfigError: There were failed tests in the initial test run.`). Для mutation-analysis інтеграція проти живого дерева не дає додаткової сигналу понад per-rule unit-тести.
17
+ - **`rules/js-lint/coverage/coverage.mjs:236`** `runStryker` — `bunx` → `npx` для запуску `@stryker-mutator/core`. Bunx **завжди** інсталює пакет у `T/bunx-<uid>-<pkg>@latest/node_modules/` і запускає Stryker звідти, навіть коли локальний install уже існує у hoisted node_modules. Stryker plugin-discovery (`@stryker-mutator/*`) globится відносно core-install-каталогу (`core/dist/src/di/plugin-loader.js:79` → `../../../../../@stryker-mutator/*`) — у bunx-temp бачить лише `core/api/instrumenter/util` (всі чотири у `IGNORED_PACKAGES`), а локально встановлений `@stryker-mutator/vitest-runner` залишається невидимим. Worker-процеси падають з `Cannot find TestRunner plugin "vitest". In fact, no TestRunner plugins were loaded.`. До зняття `mutate`-обмеження (1.29.2) баг маскувався: при єдиному файлі у scope Stryker incremental-кеш мав усі 141 мутант → fresh-workers не стартували → плагін не потрібен. Після розширення `mutate` 25052 мутантів вимагали свіжих workers — баг проявився. `npx` шукає у локальному `node_modules/.bin/` (walking up), запускає Stryker з cursor-репо install, де поряд лежить vitest-runner.
18
+
19
+ ## [1.29.2] - 2026-05-29
20
+
21
+ ### Changed
22
+
23
+ - **`npm/stryker.config.mjs`** — знято тимчасове обмеження `mutate: ['rules/test/coverage/coverage.mjs']` (yet single orchestrator-file). Тепер Stryker мутує весь production-код cursor-репо: `scripts/**/*.mjs` (lib/utils/CLI helpers), `rules/**/*.mjs` (`<r>/{js,lib,coverage,fix.mjs}`), `bin/**/*.{js,mjs}`. Виключаємо `**/tests/**`, `**/__fixtures__/**`, `**/fixtures/**`, `**/data/**`, `**/template/**`, `**/templates/**` — це або тести/фікстури (Stryker і так пропускає за іменем, але дублюємо явно для прозорості), або baseline-шаблони/JSON-канон, що копіюються консьюмерам як-є й не мають логіки для мутації (інфляція survived-рейтингу без сенсу). Explicit include для `rules/test/js/data/stryker_config/stryker-vue-macros-ignorer.mjs` — єдиний `data/`-файл із власними юніт-тестами (`tests/stryker-vue-macros-ignorer.test.mjs`). Мотивація — `lib/`-модулі правил (`abie/lib/env-dns.mjs`, `abie/lib/http-route.mjs` тощо) мають юніт-тести у `<rule>/lib/tests/`, але до зняття обмеження не потрапляли у mutation-score — JS-row у `COVERAGE.md` показував лише orchestrator.
24
+
7
25
  ## [1.29.1] - 2026-05-28
8
26
 
9
27
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.29.1",
3
+ "version": "1.29.3",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -233,7 +233,14 @@ const defaultRunner = {
233
233
  return r.status ?? 1
234
234
  },
235
235
  runStryker({ cwd }) {
236
- const r = spawnSync('bunx', ['@stryker-mutator/core', 'run'], { cwd, stdio: 'inherit', env: process.env })
236
+ // `npx`, не `bunx`: bunx завжди ставить пакет у `T/bunx-<uid>-<pkg>@latest` і запускає
237
+ // Stryker звідти. Плагін-discovery у Stryker (`@stryker-mutator/*`) globится відносно
238
+ // CORE-install-каталогу (`core/dist/src/di/plugin-loader.js` → `../../../../../@stryker-mutator/*`),
239
+ // тож у bunx-temp бачить лише `core/api/instrumenter/util` (усі в IGNORED_PACKAGES) — а локально
240
+ // встановлений `@stryker-mutator/vitest-runner` залишається невидимим, і workers падають з
241
+ // `Cannot find TestRunner plugin "vitest"`. `npx` ходить угору по `node_modules/.bin/` і
242
+ // запускає Stryker з локального hoisted-install, де поряд лежить vitest-runner.
243
+ const r = spawnSync('npx', ['@stryker-mutator/core', 'run'], { cwd, stdio: 'inherit', env: process.env })
237
244
  return r.status ?? 1
238
245
  }
239
246
  }
@@ -135,11 +135,11 @@ async function processOneSrcTauri(srcTauriDir, cwd, reporter) {
135
135
  }
136
136
 
137
137
  /**
138
+ * @param {string} [cwd] корінь проєкту (default: `process.cwd()` — CLI-сумісність)
138
139
  * @returns {Promise<number>} 0 — OK або silently skipped, 1 — порушення
139
140
  */
140
- export async function check() {
141
+ export async function check(cwd = process.cwd()) {
141
142
  const reporter = createCheckReporter()
142
- const cwd = process.cwd()
143
143
  const srcTauriDirs = await findSrcTauriDirs(cwd)
144
144
  if (srcTauriDirs.length === 0) {
145
145
  return reporter.getExitCode()
@@ -23,11 +23,11 @@ const HERE = dirname(fileURLToPath(import.meta.url))
23
23
  const BASELINE_PATH = join(HERE, 'data', 'cargo_mutants_config', 'mutants.toml.baseline')
24
24
 
25
25
  /**
26
+ * @param {string} [cwd] корінь проєкту (default: `process.cwd()` — CLI-сумісність)
26
27
  * @returns {Promise<number>} 0 — OK або silently skipped, 1 — порушення
27
28
  */
28
- export async function check() {
29
+ export async function check(cwd = process.cwd()) {
29
30
  const reporter = createCheckReporter()
30
- const cwd = process.cwd()
31
31
  const config = await readNCursorConfigLite(cwd)
32
32
 
33
33
  // Self-gate: rust має бути enabled
@@ -33,11 +33,14 @@ const STRYKER_VUE_PLUGIN_PATH = join(HERE, 'data', 'stryker_config', 'stryker-vu
33
33
  const STRYKER_VUE_PLUGIN_FILENAME = 'stryker-vue-macros-ignorer.mjs'
34
34
  const VITEST_BASELINE_PATH = join(HERE, 'data', 'vitest_config', 'vitest.config.baseline.js')
35
35
 
36
- // Stryker-output патерн для .gitignore: увесь каталог reports/stryker/ це
37
- // build-артефакти (`tempDirName` backup'и, mutation.json, HTML/dashboard-репорти
38
- // якщо користувач додасть інші reporter-и). Покриваємо одним патерном замість
39
- // перелічування під-патернів. Подвійний-зірочка-префікс для monorepo workspaces.
40
- const STRYKER_GITIGNORE_ENTRIES = ['**/reports/stryker/']
36
+ // Тест-артефакти для .gitignore (подвійний-зірочка-префікс для monorepo workspaces):
37
+ // - `**/reports/stryker/` — увесь каталог Stryker-output-у (`tempDirName` backup'и,
38
+ // mutation.json, HTML/dashboard-репорти якщо користувач додасть інші reporter-и).
39
+ // - `**/coverage/`весь output vitest v8 coverage (`lcov.info` + HTML `lcov-report/`).
40
+ // Ефемерний: регенерується кожним прогоном; фінальні метрики живуть у `COVERAGE.md`.
41
+ // Gitignore не заважає `n-cursor coverage` читати `lcov.info` у тому ж прогоні.
42
+ // Покриваємо каталогами замість перелічування під-патернів.
43
+ const TEST_GITIGNORE_ENTRIES = ['**/reports/stryker/', '**/coverage/']
41
44
 
42
45
  // .vue detection: scope — `<jsRoot>/src/**/*.vue` (як і Stryker mutate defaults для src/);
43
46
  // skip build-артефактів і чужих node_modules, щоб не вмикати vue-варіант через transitive deps.
@@ -75,11 +78,11 @@ async function ensureBaselineFile(reporter, cwd, baselinePath, target, label) {
75
78
  }
76
79
 
77
80
  /**
81
+ * @param {string} [cwd] корінь проєкту (default: `process.cwd()` — CLI-сумісність)
78
82
  * @returns {Promise<number>} 0 — OK або silently skipped, 1 — порушення
79
83
  */
80
- export async function check() {
84
+ export async function check(cwd = process.cwd()) {
81
85
  const reporter = createCheckReporter()
82
- const cwd = process.cwd()
83
86
  const config = await readNCursorConfigLite(cwd)
84
87
 
85
88
  // Self-gate: js-lint має бути enabled
@@ -127,11 +130,12 @@ export async function check() {
127
130
  await ensureBaselineFile(reporter, cwd, VITEST_BASELINE_PATH, join(jsRoot, 'vitest.config.js'), 'vitest.config.js')
128
131
  }
129
132
 
130
- // Гарантуємо що Stryker temp/output ніколи не потрапляють у commit. Patterns
131
- // покривають усі workspaces через `**/`-префікс (єдиний root .gitignore).
132
- const { added } = await ensureGitignoreEntries(cwd, STRYKER_GITIGNORE_ENTRIES, 'Stryker mutation testing (test.mdc)')
133
+ // Гарантуємо що тест-артефакти (Stryker output, lcov HTML-звіт) ніколи не
134
+ // потрапляють у commit. Patterns покривають усі workspaces через `**/`-префікс
135
+ // (єдиний root .gitignore).
136
+ const { added } = await ensureGitignoreEntries(cwd, TEST_GITIGNORE_ENTRIES, 'Test artifacts: Stryker + coverage (test.mdc)')
133
137
  if (added.length > 0) {
134
- reporter.pass(`.gitignore: додано Stryker-патерни (${added.join(', ')}) (test.mdc)`)
138
+ reporter.pass(`.gitignore: додано тест-патерни (${added.join(', ')}) (test.mdc)`)
135
139
  }
136
140
  return reporter.getExitCode()
137
141
  }
@@ -153,4 +153,9 @@ Customization (mutate patterns, exclude rules, timeout) — відповідал
153
153
 
154
154
  Якщо інше правило спеціалізує mutation-behavior — воно зобов'язане **доповнювати** існуючий `.cargo/mutants.toml` без дублювання (додавати лише відсутні ключі) і **не перетирати** ручні налаштування. Послідовний запуск `npx @nitra/cursor fix test` після `fix tauri` не має скидати tauri-tuning, і навпаки — повторний `fix tauri` не дублює секції.
155
155
 
156
- Додатково: коли `js-lint` enabled, концерн `stryker_config` без дублювання додає у кореневий `.gitignore` патерн `**/reports/stryker/` — увесь каталог Stryker-output-у (backup'и `tempDirName`, `mutation.json`, HTML/dashboard-репорти якщо додасте інші reporter-и). Це запобігає випадковому коміту build-артефактів.
156
+ Додатково: коли `js-lint` enabled, концерн `stryker_config` без дублювання додає у кореневий `.gitignore` тест-патерни:
157
+
158
+ - `**/reports/stryker/` — увесь каталог Stryker-output-у (backup'и `tempDirName`, `mutation.json`, HTML/dashboard-репорти якщо додасте інші reporter-и).
159
+ - `**/coverage/` — весь output vitest v8 coverage (`lcov.info` + HTML `lcov-report/`). Ефемерний: регенерується кожним прогоном, фінальні метрики живуть у `COVERAGE.md`. Gitignore не заважає `n-cursor coverage` читати `lcov.info` у тому ж прогоні.
160
+
161
+ Це запобігає випадковому коміту build-артефактів.