@nitra/cursor 1.13.76 → 1.13.83

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.
Files changed (116) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +5 -5
  3. package/bin/n-cursor.js +16 -21
  4. package/package.json +1 -1
  5. package/rules/abie/abie.mdc +12 -4
  6. package/rules/abie/fix.mjs +15 -0
  7. package/rules/abie/policy/base_deployment_preem/base_deployment_preem.rego +9 -11
  8. package/rules/abie/policy/health_check_policy/health_check_policy.rego +9 -10
  9. package/rules/abie/policy/http_route_base/http_route_base.rego +7 -8
  10. package/rules/abie/utils/k8s-tree.mjs +1 -1
  11. package/rules/adr/adr.mdc +1 -1
  12. package/rules/adr/fix.mjs +15 -0
  13. package/rules/bun/fix.mjs +15 -0
  14. package/rules/capacitor/fix.mjs +15 -0
  15. package/rules/changelog/changelog.mdc +1 -1
  16. package/rules/changelog/fix.mjs +15 -0
  17. package/rules/changelog/{fix → js}/consistency/check.mjs +16 -16
  18. package/{scripts/utils → rules/changelog/js/consistency}/package-manifest.mjs +1 -1
  19. package/rules/ci4/fix.mjs +15 -0
  20. package/rules/docker/docker.mdc +3 -3
  21. package/rules/docker/fix.mjs +15 -0
  22. package/rules/docker/{fix → js}/lint/check.mjs +3 -3
  23. package/{scripts/utils → rules/docker/js/lint}/docker-hadolint.mjs +3 -3
  24. package/rules/docker/lint/lint.mjs +2 -2
  25. package/rules/efes/fix.mjs +15 -0
  26. package/rules/feedback/fix.mjs +15 -0
  27. package/rules/ga/fix.mjs +15 -0
  28. package/rules/ga/lint/lint.mjs +5 -2
  29. package/rules/graphql/fix.mjs +15 -0
  30. package/rules/graphql/{fix → js}/tooling/check.mjs +1 -1
  31. package/{scripts/utils → rules/graphql/js/tooling}/graphql-gql-scan.mjs +47 -12
  32. package/rules/hasura/fix.mjs +15 -0
  33. package/rules/hasura/{fix → js}/internal_urls/check.mjs +1 -1
  34. package/rules/hasura/policy/svc_hl/svc_hl.rego +1 -1
  35. package/rules/image-avif/fix.mjs +15 -0
  36. package/rules/image-compress/fix.mjs +15 -0
  37. package/rules/js-bun-db/fix.mjs +15 -0
  38. package/{scripts/utils → rules/js-bun-db/js/safety}/bun-sql-scan.mjs +1 -1
  39. package/rules/js-bun-db/{fix → js}/safety/check.mjs +1 -1
  40. package/rules/js-bun-redis/fix.mjs +15 -0
  41. package/rules/js-bun-redis/policy/package_json/package_json.rego +1 -1
  42. package/rules/js-lint/fix.mjs +15 -0
  43. package/rules/js-lint/{fix → js}/tooling/check.mjs +3 -15
  44. package/{scripts/utils → rules/js-lint/js/tooling}/rebuild-oxlint-canonical.mjs +1 -1
  45. package/rules/js-lint/js-lint.mdc +2 -2
  46. package/rules/js-mssql/fix.mjs +15 -0
  47. package/rules/js-mssql/{fix → js}/deps/check.mjs +1 -1
  48. package/{scripts/utils → rules/js-mssql/js/deps}/mssql-pool-scan.mjs +1 -1
  49. package/rules/js-run/fix.mjs +15 -0
  50. package/{scripts/utils → rules/js-run/js/runtime}/bunyan-imports.mjs +1 -1
  51. package/{scripts/utils → rules/js-run/js/runtime}/check-env-scan.mjs +1 -1
  52. package/rules/js-run/{fix → js}/runtime/check.mjs +5 -5
  53. package/{scripts/utils → rules/js-run/js/runtime}/conn-file-rules.mjs +1 -1
  54. package/{scripts/utils → rules/js-run/js/runtime}/conn-imports-scan.mjs +1 -1
  55. package/{scripts/utils → rules/js-run/js/runtime}/promise-settimeout-scan.mjs +1 -1
  56. package/rules/k8s/fix.mjs +15 -0
  57. package/rules/k8s/k8s.mdc +2 -2
  58. package/rules/nginx-default-tpl/fix.mjs +15 -0
  59. package/rules/nginx-default-tpl/{fix → js}/template/check.mjs +1 -1
  60. package/rules/npm-module/fix.mjs +15 -0
  61. package/rules/npm-module/{fix → js}/package_structure/check.mjs +11 -1
  62. package/rules/php/fix.mjs +15 -0
  63. package/rules/rego/fix.mjs +15 -0
  64. package/rules/security/fix.mjs +15 -0
  65. package/rules/security/security.mdc +2 -2
  66. package/rules/style-lint/fix.mjs +15 -0
  67. package/rules/tauri/fix.mjs +15 -0
  68. package/rules/test/auto.md +1 -0
  69. package/rules/test/fix.mjs +15 -0
  70. package/rules/test/js/location/check.mjs +77 -0
  71. package/rules/test/test.mdc +60 -0
  72. package/rules/text/fix.mjs +15 -0
  73. package/rules/vue/fix.mjs +15 -0
  74. package/rules/vue/{fix → js}/packages/check.mjs +2 -2
  75. package/rules/vue/vue.mdc +1 -1
  76. package/scripts/auto-rules.mjs +3 -3
  77. package/scripts/sync-claude-config.mjs +2 -2
  78. package/scripts/utils/ast-scan-utils.mjs +3 -2
  79. package/scripts/utils/discover-checkable-rules.mjs +30 -18
  80. package/scripts/utils/list-rule-ids.mjs +23 -0
  81. package/scripts/utils/run-rule.mjs +7 -7
  82. package/scripts/utils/run-standard-rule.mjs +34 -0
  83. package/scripts/utils/walk-cache.mjs +24 -0
  84. package/scripts/utils/with-lock.mjs +120 -0
  85. package/scripts/utils/workspaces.mjs +1 -1
  86. package/scripts/utils/worktree-fingerprint.mjs +30 -0
  87. /package/rules/abie/{fix → js}/applies/check.mjs +0 -0
  88. /package/rules/abie/{fix → js}/env_dns/check.mjs +0 -0
  89. /package/rules/abie/{fix → js}/firebase_hosting/check.mjs +0 -0
  90. /package/rules/abie/{fix → js}/hc_pairing/check.mjs +0 -0
  91. /package/rules/abie/{fix → js}/ua_http_route/check.mjs +0 -0
  92. /package/rules/abie/{fix → js}/ua_node_selector/check.mjs +0 -0
  93. /package/rules/adr/{fix → js}/hooks/check.mjs +0 -0
  94. /package/rules/adr/{fix → js}/hooks/template/.gitignore.snippet +0 -0
  95. /package/rules/bun/{fix → js}/layout/check.mjs +0 -0
  96. /package/rules/capacitor/{fix → js}/platforms/check.mjs +0 -0
  97. /package/{scripts/utils → rules/docker/js/lint}/docker-mirror.mjs +0 -0
  98. /package/rules/ga/{fix → js}/workflows/check.mjs +0 -0
  99. /package/rules/image-avif/{fix → js}/avif_generation/check.mjs +0 -0
  100. /package/rules/image-compress/{fix → js}/package_setup/check.mjs +0 -0
  101. /package/rules/js-bun-redis/{fix → js}/imports/check.mjs +0 -0
  102. /package/{scripts/utils → rules/js-lint/js/tooling}/knip-canonical.json +0 -0
  103. /package/{scripts/utils → rules/js-lint/js/tooling}/oxlint-canonical-skeleton.json +0 -0
  104. /package/{scripts/utils → rules/js-lint/js/tooling}/oxlint-canonical.json +0 -0
  105. /package/{scripts/utils → rules/js-lint/js/tooling}/oxlint-rules.tsv +0 -0
  106. /package/rules/k8s/{fix → js}/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json +0 -0
  107. /package/rules/k8s/{fix → js}/manifests/check.mjs +0 -0
  108. /package/rules/php/{fix → js}/tooling/check.mjs +0 -0
  109. /package/rules/rego/{fix → js}/applies/check.mjs +0 -0
  110. /package/rules/security/{fix → js}/sample_secret/check.mjs +0 -0
  111. /package/rules/security/{fix → js}/trufflehog/check.mjs +0 -0
  112. /package/rules/security/{fix → js}/trufflehog/template/.trufflehog-exclude.snippet.txt +0 -0
  113. /package/rules/style-lint/{fix → js}/tooling/check.mjs +0 -0
  114. /package/rules/tauri/{fix → js}/tooling/check.mjs +0 -0
  115. /package/rules/text/{fix → js}/formatting/check.mjs +0 -0
  116. /package/{scripts/utils → rules/vue/js/packages}/vue-forbidden-imports.mjs +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,95 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.13.83] - 2026-05-23
8
+
9
+ ### Changed
10
+
11
+ - **Per-rule `fix.mjs` entry-point + rename `fix/` → `js/`:** кожне з 30 правил тепер має `rules/<id>/fix.mjs` — 11-рядковий wrapper над новим `runStandardRule`. CLI більше не робить convention-based discovery на верхньому рівні — перебирає правила через `listRuleIds` і викликає `await import(rules/<id>/fix.mjs).run({ walkCache })`. Каталог `fix/<concern>/` перейменовано на `js/<concern>/` для усунення колізії з кореневим `fix.mjs` та узгодження з `policy/` (за технологією, не функцією).
12
+ - **Локальна логіка в `fix.mjs` заборонена** — розширення поведінки правил тільки через опції в `RuleContext` (зараз: `walkCache`; зарезервовано на майбутнє: `skipMdcRefs`, `skipApplies`, `onlyConcerns`). Простір варіацій повністю описано в `RuleContext` JSDoc; convention-drift виключений на рівні дизайну.
13
+ - **Shared `walkCache`** як module-level singleton у `scripts/utils/walk-cache.mjs` (`getOrCreateWalkCache` + `resetWalkCache` для тестів). CLI створює один cache на прогон і прокидає через ctx до всіх concerns.
14
+ - **Нові utils:** `scripts/utils/run-standard-rule.mjs`, `scripts/utils/list-rule-ids.mjs`, `scripts/utils/walk-cache.mjs`. Експорт `discoverOneRule(ruleDir, ruleId)` з `discover-checkable-rules.mjs` (виокремлено з існуючого `discoverCheckableRules` — DRY).
15
+ - **Нові тести:** `tests/fix-mjs-contract.test.mjs` (91 кейс — smoke на всі 30 правил), `tests/run-standard-rule.test.mjs`, `tests/list-rule-ids.test.mjs`, `tests/walk-cache.test.mjs`, `tests/discover-one-rule.test.mjs`. Існуючі тести оновлено в частині import-шляхів `/fix/<concern>` → `/js/<concern>` (логіка не змінювалась); видалено застарілий `discoverCheckableRules > legacy js/-структура ігнорується` — `js/` тепер canonical convention.
16
+
17
+ ### Breaking
18
+
19
+ - **Для зовнішніх інтеграторів, що пишуть власні правила:** каталог `rules/<id>/fix/<concern>/check.mjs` перейменовано на `rules/<id>/js/<concern>/check.mjs`; додатково потрібен файл `rules/<id>/fix.mjs` з канонічним вмістом (див. будь-яке вбудоване правило для шаблону). CLI більше не запустить правило без `fix.mjs`.
20
+
21
+ ### Notes
22
+
23
+ - Зворотна сумісність CLI: `npx @nitra/cursor check` та `npx @nitra/cursor check abie` працюють як раніше.
24
+ - Use-cases: `bun npm/rules/abie/fix.mjs` (debug); `bun npm/rules/${{ matrix.rule }}/fix.mjs` (CI per-rule jobs); IDE Run-button на `fix.mjs`.
25
+
26
+ ## [1.13.82] - 2026-05-23
27
+
28
+ ### Changed
29
+
30
+ - **`rules/test`: виняток для `*_test.rego` файлів — лишаються поряд із полісі (OPA/Conftest community-конвенція)**:
31
+ - **Rego unit-тести (`*_test.rego`) лежать у тому самому каталозі, що й `<name>.rego`** — за загальноприйнятим патерном OPA/Conftest. `package <name>` (полісі) ↔ `package <name>_test` (тест) семантично зв'язані через `package`-декларації, а не локацію файлу; `conftest verify -p <dir>` рекурсивний, тож знаходить тест незалежно від місця, але спільнота тримає їх поруч (бачимо у OPA examples, Gatekeeper library, Datree, Styra DAS bundles). Це **легітимне відхилення** від внутрішньої JS-конвенції «`tests/` всюди» на користь OPA-ідіоми.
32
+ - `rules/test/test.mdc` v1.1 — додано **окрему секцію про виняток** для Rego: «`*_test.rego` лишаються поряд із полісі, бо це загальноприйнятий OPA/Conftest community-патерн» (з прикладом структури `policy/<concern>/{<name>.rego, <name>_test.rego, target.json}`).
33
+ - `rules/test/fix/location/check.mjs` — перевіряє **лише `*.test.mjs`**, `*_test.rego` свідомо виключено з область перевірки (зафіксовано у docstring).
34
+ - Додано test-case у `rules/test/fix/location/tests/check.test.mjs`: `*_test.rego` поряд із полісі НЕ є порушенням.
35
+ - **Відкат переміщення `*_test.rego`**: 69 файлів, які раніше було помилково перенесено у `policy/<concern>/tests/<name>_test.rego`, повернуто у `policy/<concern>/<name>_test.rego` через `git mv`. Порожні `tests/` піддиректорії під `policy/` видалено.
36
+ - **`npx @nitra/cursor check test`** охоплює лише JS-тести: «✅ Всі 77 файлів *.test.mjs у каталозі tests/». Rego-тести продовжують перевірятись через `conftest verify` у правилі `rego`.
37
+
38
+ ## [1.13.81] - 2026-05-23
39
+
40
+ ### Fixed
41
+
42
+ - **`npm-module.package_structure`: carve-out для rule-name сегмента** у `classifyPublishedFileAsTest`. Раніше для шляху `rules/<X>/...` сегмент `<X>` піддавався TEST_DIR_NAMES-перевірці, що давало false positive на правилах із id, що збігається з test-style ім'ям (`test`, `tests`, `fixtures` тощо). Тепер сегмент індекс 1 (ім'я правила, коли індекс 0 — `rules`) пропускається; глибші сегменти (`rules/<r>/fix/<c>/tests/`) продовжують перевірятись.
43
+
44
+ ### Added
45
+
46
+ - **Нове правило `test` (`npm/rules/test/`)** — програмний канон розміщення тестів (ADR `docs/adr/20260523-154806-...`):
47
+ - `test.mdc` — конвенція «`*.test.mjs` живуть у `tests/` поряд із джерелом», з описом спецвипадків (root `tests/`, fixtures у `tests/__fixtures__/` і `tests/fixtures/`, test-helpers як shared-infra).
48
+ - `fix/location/check.mjs` — обхід дерева `walkDir`'ом (зі стандартним skip-листом + `.n-cursor.json:ignore`); для кожного `*.test.mjs` басенейм батьківської директорії має бути `tests`, інакше fail з вказівкою куди перенести.
49
+ - `fix/location/tests/check.test.mjs` — 6 тестів самого правила (eats own dogfood).
50
+ - `auto.md` — auto-enable умова: «якщо у проекті є хоча б один файл `*.test.mjs`».
51
+ - Додано `"test"` у `.n-cursor.json:rules` репо `@nitra/cursor`.
52
+ - Додано `"ignore": [".claude/worktrees"]` у `.n-cursor.json` — щоб правило не звітувало про знімки в git worktrees.
53
+
54
+ ### Changed
55
+
56
+ - **Тести переміщено з-поряд-із-файлом у `dir/tests/` піддиректорію** (ADR `docs/adr/20260523-154806-...`):
57
+ - **73 sibling-тести** у `rules/...` і `scripts/...`: для кожного `dir/X.test.mjs` → `dir/tests/X.test.mjs` із автоматичним оновленням relative imports.
58
+ - **3 integration-тести у `npm/tests/`** — без змін (вже відповідали конвенції).
59
+ - **`npm/scripts/utils/__fixtures__/`** → `npm/scripts/utils/tests/__fixtures__/`.
60
+ - **`npm/rules/nginx-default-tpl/fix/template/fixtures/`** → `.../tests/fixtures/`; посилання в `npm/tests/check-rule-fixtures.test.mjs` оновлено.
61
+ - Ручні фіксапи 4 тестів із HERE/`..` path patterns (sync-setup-bun-deps-action, inline-template-links, rules/adr/fix/hooks, rules/abie/utils/enabled) — додано додатковий `..`, бо тести стали на рівень глибше.
62
+ - `package.json#files` негативні globs (`!**/*.test.mjs`, `!**/__fixtures__/**`, `!**/fixtures/**`) працюють рекурсивно — без змін.
63
+ - **77 тестів** проходять у новому layout (76 існуючих + 1 нового правила): `bun test` 843 pass / 2 fail (обидва — pre-existing `with-lock` issues, не пов'язані).
64
+ - `npx @nitra/cursor check test` → `✅ Всі 77 файлів *.test.mjs у каталозі tests/ (test.mdc)`.
65
+
66
+ ## [1.13.79] - 2026-05-23
67
+
68
+ ### Changed
69
+
70
+ - **Перенесення single-rule сканерів і canonical-конфігів з `npm/scripts/utils/` у `npm/rules/<rule>/fix/<sub>/`** (узгоджено з конвенцією `rules/ga/fix/workflows/`, `rules/nginx-default-tpl/fix/template/` тощо; ADR `docs/adr/20260523-114913-...`, який supersede `20260523-112217-...`):
71
+ - **js-lint** (`rules/js-lint/fix/tooling/`): `knip-canonical.json`, `oxlint-canonical.json`, `oxlint-canonical-skeleton.json`, `oxlint-rules.tsv`, `rebuild-oxlint-canonical.mjs`. Константи `OXLINT_CANONICAL_JSON_PATH` / `KNIP_CANONICAL_JSON_PATH` у `check.mjs` стали локальними (без 4-річневих `..`).
72
+ - **js-run** (`rules/js-run/fix/runtime/`): `bunyan-imports.mjs` (+test), `check-env-scan.mjs`, `conn-file-rules.mjs` (+test), `conn-imports-scan.mjs` (+test), `promise-settimeout-scan.mjs` (+test).
73
+ - **docker** (`rules/docker/fix/lint/`): `docker-hadolint.mjs` (+test), `docker-mirror.mjs`. `rules/docker/lint/lint.mjs` тепер імпортує з `../fix/lint/docker-hadolint.mjs`.
74
+ - **js-bun-db** (`rules/js-bun-db/fix/safety/`): `bun-sql-scan.mjs`.
75
+ - **js-mssql** (`rules/js-mssql/fix/deps/`): `mssql-pool-scan.mjs`.
76
+ - **changelog** (`rules/changelog/fix/consistency/`): `package-manifest.mjs` (+test).
77
+ - **vue** (`rules/vue/fix/packages/`): `vue-forbidden-imports.mjs` (+test).
78
+ - **graphql** (`rules/graphql/fix/tooling/`): `graphql-gql-scan.mjs`. Cross-rule імпорту немає: `extractVueScriptBlocks`, локалізована `contentForGqlScan` і власні `isGqlScanSourceFile` / `shouldSkipFileForGqlScan` (з власною source-regex і skip-list `.d.ts` / `auto-imports.d.ts` / `components.d.ts`) дубльовані всередині `graphql-gql-scan.mjs` — правила залишаються самодостатніми.
79
+ - **`scripts/auto-rules.mjs`** оновлено: імпорти переадресовано на нові локації трьох сканерів (`bun-sql-scan`, `graphql-gql-scan`, `vue-forbidden-imports`).
80
+ - **`.mdc`-документація** оновлена: `rules/js-lint/js-lint.mdc`, `rules/docker/docker.mdc`, `rules/vue/vue.mdc`, `.cursor/rules/n-js-lint.mdc`, `.cursor/rules/n-vue.mdc` — посилання на нові шляхи canonical-файлів і сканерів.
81
+
82
+ ## [1.13.78] - 2026-05-23
83
+
84
+ ### Changed
85
+
86
+ - **abie / k8s / hasura — актуалізація посилань на неіснуючий `check-abie.mjs`:** після реструктуризації `rules/abie/` на `fix/<concern>/check.mjs` (+ Rego-пакети у `policy/`) монолітного `check-abie.mjs` більше немає; застарілі посилання в активних `.mdc`/`.rego`/`.mjs` (поза історичним `CHANGELOG.md`) оновлено:
87
+ - `npm/rules/abie/abie.mdc` — три згадки замінено: пер-документна перевірка HTTPRoute base hostnames → Rego `abie.http_route_base`; env-DNS-скан → `fix/env_dns/check.mjs`; cross-file/FS-логіку розбито за концернами (`hc_pairing/`, `ua_http_route/`, `ua_node_selector/`, `env_dns/`, `firebase_hosting/`) з поясненням, що `targetRef.name -hl` cross-check обчислюється з `hcp.metadata.name` у Rego.
88
+ - `npm/rules/abie/policy/{http_route_base,base_deployment_preem,health_check_policy}/*.rego` — у шапках замінено `npm/scripts/check-abie.mjs` на актуальні джерела: cross-file gating через `policy/<pkg>/target.json` (glob), rule-level applies-гейт у `fix/applies/check.mjs`, FS-парність HCP↔Deployment у `fix/hc_pairing/check.mjs`. Прибрано згадки видалених JS-функцій (`validateAbieHcPolicy`, `deploymentDocumentHasAbieBasePreemNodeSelector`).
89
+ - `npm/rules/k8s/k8s.mdc` — рядок про `targetRef -hl` для abie-проєктів вказує на Rego-пакет `abie.health_check_policy` + `abie.mdc` (замість `check-abie.mjs`).
90
+ - `npm/rules/hasura/fix/internal_urls/check.mjs` — у JSDoc згадку `check-abie` замінено на нейтральне «abie-перевірки».
91
+
92
+ ### Added
93
+
94
+ - **`with-lock`:** атомарний `mkdirSync`-лок + SHA-256 fingerprint-дедуп для важких команд; пілот — `lint-ga` автоматично серіалізує паралельні запуски та пропускає дублікати при незміненому робочому дереві (TTL 10 хв). Нові модулі: `scripts/utils/worktree-fingerprint.mjs`, `scripts/utils/with-lock.mjs`.
95
+
7
96
  ## [1.13.76] - 2026-05-22
8
97
 
9
98
  ### Added
package/README.md CHANGED
@@ -56,7 +56,7 @@
56
56
  - Рядки в **base**, які змінюються в overlays, позначайте коментарем на рядку (узгоджено в команді), наприклад: `# буде замінено через kustomize`.
57
57
  - Після перенесення в **`base`** / overlays **видаляйте** застарілі маніфести та каталоги, які більше не потрібні.
58
58
 
59
- Повний текст правил — у **`k8s.mdc`**; programmatic перевірки — у **`npm/rules/k8s/`**: JS-checks у `fix/<concern>/check.mjs`, rego-policies у `policy/<concern>/<name>.rego` (обидва запускаються через `npx @nitra/cursor check k8s`).
59
+ Повний текст правил — у **`k8s.mdc`**; programmatic перевірки — у **`npm/rules/k8s/`**: JS-checks у `js/<concern>/check.mjs`, rego-policies у `policy/<concern>/<name>.rego` (обидва запускаються через `npx @nitra/cursor check k8s`).
60
60
 
61
61
  ### v8r і власний каталог схем
62
62
 
@@ -109,13 +109,13 @@ npm/
109
109
 
110
110
  ### Структура одного правила
111
111
 
112
- Кожне правило `npm/rules/<id>/` ділиться за **технологією реалізації** на три сиблінги — `fix/`, `lint/`, `policy/`:
112
+ Кожне правило `npm/rules/<id>/` ділиться за **технологією реалізації** на три сиблінги — `js/`, `lint/`, `policy/`:
113
113
 
114
114
  ```
115
115
  npm/rules/<id>/
116
116
  ├── <id>.mdc # текст правила (після синку — .cursor/rules/n-<id>.mdc)
117
117
  ├── auto.md # умова автоактивації скілу (опційно)
118
- ├── fix/ # JS для `npx @nitra/cursor check`
118
+ ├── js/ # JS для `npx @nitra/cursor check`
119
119
  │ └── <concern>/
120
120
  │ ├── check.mjs # діагностика — повертає список violations
121
121
  │ ├── check.test.mjs
@@ -134,11 +134,11 @@ npm/rules/<id>/
134
134
 
135
135
  | Що реалізує | Канал виклику | Куди |
136
136
  | ------------------------- | ---------------------------------------------- | ------------------- |
137
- | JS-діагностика + автофікс | `npx @nitra/cursor check` (fix-канал) | `fix/<concern>/` |
137
+ | JS-діагностика + автофікс | `npx @nitra/cursor check` (fix-канал) | `js/<concern>/` |
138
138
  | JS-orchestrator лінту | `bun run lint-<id>` через `n-cursor lint-<id>` | `lint/` |
139
139
  | Rego-діагностика | `npx @nitra/cursor check` (fix-канал) | `policy/<concern>/` |
140
140
 
141
- `fix/` і `policy/` обидва живлять fix-канал (`npx @nitra/cursor check` запускає і JS-checks, і rego-policies), але **розділені за технологією**: JS у `fix/`, rego у `policy/`. `lint/` тримає лише JS, що оркеструє `bun run lint-<id>`.
141
+ `js/` і `policy/` обидва живлять fix-канал (`npx @nitra/cursor check` запускає і JS-checks, і rego-policies), але **розділені за технологією**: JS у `js/`, rego у `policy/`. `lint/` тримає лише JS, що оркеструє `bun run lint-<id>`.
142
142
 
143
143
  ## AGENTS.md у проєкті користувача
144
144
 
package/bin/n-cursor.js CHANGED
@@ -83,14 +83,14 @@ import {
83
83
  import { detectAutoSkills } from '../scripts/auto-skills.mjs'
84
84
  import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
85
85
  import { discoverCheckRulesFromCursorRules } from '../scripts/utils/discover-check-rules-from-cursor.mjs'
86
- import { discoverCheckableRules } from '../scripts/utils/discover-checkable-rules.mjs'
86
+ import { listRuleIds } from '../scripts/utils/list-rule-ids.mjs'
87
87
  import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
88
88
  import { runLintDocker } from '../rules/docker/lint/lint.mjs'
89
89
  import { runLintGaCli } from '../rules/ga/lint/lint.mjs'
90
90
  import { runLintK8s } from '../rules/k8s/lint/lint.mjs'
91
91
  import { runLintRego } from '../rules/rego/lint/lint.mjs'
92
92
  import { runLintTextCli } from '../rules/text/lint/lint.mjs'
93
- import { runRule } from '../scripts/utils/run-rule.mjs'
93
+ import { getOrCreateWalkCache } from '../scripts/utils/walk-cache.mjs'
94
94
  import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
95
95
  import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
96
96
  import { runRenameYamlExtensionsCli } from './rename-yaml-extensions.mjs'
@@ -973,26 +973,17 @@ function logRemovedManagedItems(title, basePath, names) {
973
973
  }
974
974
  }
975
975
 
976
- /**
977
- * Знаходить правила, для яких є хоча б щось прогонне: JS-концерн у `rules/<id>/fix/<concern>/check*.mjs`
978
- * або policy-концерн у `rules/<id>/policy/<concern>/target.json`. Делегує у `discoverCheckableRules`
979
- * — див. `scripts/utils/discover-checkable-rules.mjs`.
980
- * @returns {import('../scripts/utils/discover-checkable-rules.mjs').CheckableRule[]} опис правил у алфавітному порядку
981
- */
982
- function discoverCheckScripts() {
983
- return discoverCheckableRules(BUNDLED_RULES_DIR)
984
- }
985
-
986
976
  /**
987
977
  * Запускає перевірки: без аргументів — за `*.mdc` у `.cursor/rules/`; з аргументами — лише вказані правила.
988
- * Делегує оркестрацію concern-ів (JS-checks + policy через `runConftestBatch`) у `runRule`;
989
- * сам `runChecks` відповідає лише за фільтр id, агрегацію exit-кодів і shared walk-cache на прогон.
978
+ * Перебирає правила через `listRuleIds`, для кожного робить dynamic `import('<rules>/<id>/fix.mjs')`
979
+ * і викликає `mod.run({ walkCache })`. Сам `runChecks` відповідає лише за фільтр id, агрегацію
980
+ * exit-кодів і shared walk-cache на прогон.
990
981
  * @param {string[]} requestedRules імена правил; порожній масив — брати з `.cursor/rules/*.mdc`
991
982
  * @returns {Promise<void>}
992
983
  */
993
984
  async function runChecks(requestedRules) {
994
- const allRules = await discoverCheckScripts()
995
- if (allRules.length === 0) {
985
+ const available = await listRuleIds(BUNDLED_RULES_DIR)
986
+ if (available.length === 0) {
996
987
  console.error('❌ Не знайдено жодного check-скрипта у пакеті')
997
988
  throw new Error('No check scripts found')
998
989
  }
@@ -1008,7 +999,6 @@ async function runChecks(requestedRules) {
1008
999
  }
1009
1000
  }
1010
1001
 
1011
- const available = allRules.map(r => r.id)
1012
1002
  let idsToCheck
1013
1003
  if (requestedRules.length > 0) {
1014
1004
  idsToCheck = requestedRules
@@ -1023,7 +1013,7 @@ async function runChecks(requestedRules) {
1023
1013
  if (idsToCheck.length === 0) {
1024
1014
  console.log(
1025
1015
  `\n🔍 ${PACKAGE_NAME} check — у ${RULES_DIR}/ немає правил з programmatic перевіркою ` +
1026
- `(відповідного check-*.mjs або policy/<name>/target.json у пакеті). Нічого не запущено.\n`
1016
+ `(відповідного fix.mjs у пакеті). Нічого не запущено.\n`
1027
1017
  )
1028
1018
  return
1029
1019
  }
@@ -1038,14 +1028,19 @@ async function runChecks(requestedRules) {
1038
1028
 
1039
1029
  console.log(`\n🔍 ${PACKAGE_NAME} check — перевірка правил (${idsToCheck.length})\n`)
1040
1030
 
1041
- const ruleMap = new Map(allRules.map(r => [r.id, r]))
1042
1031
  /** @type {Map<string, Promise<string[]>>} shared walk-cache (cross-concern, cross-rule у межах одного прогону) */
1043
- const walkCache = new Map()
1032
+ const walkCache = getOrCreateWalkCache()
1044
1033
  let totalFailed = 0
1045
1034
 
1046
1035
  for (const id of idsToCheck) {
1047
1036
  try {
1048
- const code = await runRule(ruleMap.get(id), BUNDLED_RULES_DIR, walkCache)
1037
+ const fixPath = join(BUNDLED_RULES_DIR, id, 'fix.mjs')
1038
+ // eslint-disable-next-line no-unsanitized/method -- id з whitelist'у listRuleIds (readdir + existsSync), fixPath не з зовнішнього input
1039
+ const mod = await import(fixPath)
1040
+ if (typeof mod.run !== 'function') {
1041
+ throw new TypeError(`${id}: rules/${id}/fix.mjs не експортує run()`)
1042
+ }
1043
+ const code = await mod.run({ walkCache })
1049
1044
  if (code !== 0) totalFailed++
1050
1045
  } catch (error) {
1051
1046
  console.log(` ❌ Помилка виконання: ${error.message}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.13.76",
3
+ "version": "1.13.83",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -36,7 +36,7 @@ spec:
36
36
 
37
37
  ### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
38
38
 
39
- У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка в **`check-abie.mjs`**).
39
+ У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка Rego-пакет **`abie.http_route_base`**, див. розділ нижче).
40
40
 
41
41
  Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
42
42
 
@@ -133,7 +133,7 @@ KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
133
133
 
134
134
  `<namespace>` (наприклад `dev-apruv` / `ua-apruv`) — `metadata.name` цільового namespace після kustomize-overlay для відповідного середовища; `<service>` — `metadata.name` headless Service (`-hl`) того сервісу, до якого йде URL.
135
135
 
136
- **Перевірка `check-abie.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
136
+ **Перевірка `js/env_dns/check.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
137
137
 
138
138
  - DNS-суфікс відповідав env: `abie-dev.internal` / `abie-ua.internal`;
139
139
  - namespace починався з `dev-` / `ua-` відповідно.
@@ -160,7 +160,7 @@ bun add -d @nitra/abie-docs
160
160
 
161
161
  ## Швидкий gate через conftest (Rego)
162
162
 
163
- Підмножину пер-документних правил продубльовано як rego-полісі у **`npm/rules/abie/policy/`** (запускається через **`bun run lint-rego`** для `*_test.rego` юніт-тестів і через **`npx @nitra/cursor check abie`** для прогону по реальних YAML — деталі в **conftest.mdc** / **n-rego.mdc**). JS у **`fix/<concern>/check.mjs`** authoritative — rego тільки швидкий gate для одиничного маніфеста (зокрема через IDE-розширення `tsandall.opa`).
163
+ Підмножину пер-документних правил продубльовано як rego-полісі у **`npm/rules/abie/policy/`** (запускається через **`bun run lint-rego`** для `*_test.rego` юніт-тестів і через **`npx @nitra/cursor check abie`** для прогону по реальних YAML — деталі в **conftest.mdc** / **n-rego.mdc**). JS у **`js/<concern>/check.mjs`** authoritative — rego тільки швидкий gate для одиничного маніфеста (зокрема через IDE-розширення `tsandall.opa`).
164
164
 
165
165
  Пакети (директорія в **`npm/policy/abie/`** → namespace → що перевіряє):
166
166
 
@@ -170,4 +170,12 @@ bun add -d @nitra/abie-docs
170
170
  - **`clean_merged_ignore_branches/`** → `abie.clean_merged_ignore_branches` — у workflow `.github/workflows/clean-merged-branch.yml` крок з `uses: phpdocker-io/github-actions-delete-abandoned-branches` має `with.ignore_branches`, що містить токени `dev,ua` (case-insensitive). **Цільові файли:** `.github/workflows/clean-merged-branch.yml`.
171
171
  - **`package_json_docs/`** → `abie.package_json_docs` — у кореневому `package.json` `devDependencies` має містити `@nitra/abie-docs` (presence-only, версію не фіксуємо). **Цільові файли:** `package.json`.
172
172
 
173
- Cross-file логіка (парність HCP↔Deployment у каталозі, обчислений `<deployment.name>-hl` для `targetRef.name`, валідація ua-overlay JSON6902 patches на HTTPRoute, env→cluster DNS, аналіз cross-namespace backendRefs у пакетах) лишається у **`check-abie.mjs`** — Rego не читає файлову систему й не робить cross-document резолюцію.
173
+ Cross-file / FS-логіка лишається у JS-частинах (`js/<concern>/check.mjs`) — Rego не читає файлову систему й не робить cross-document резолюцію:
174
+
175
+ - парність HCP↔Deployment у каталозі та modeline `hc.yaml` — `js/hc_pairing/check.mjs`;
176
+ - валідація ua-overlay JSON6902 patches на HTTPRoute + аналіз cross-namespace `backendRefs` у пакетах — `js/ua_http_route/check.mjs`;
177
+ - ua-overlay JSON6902 patch на `Deployment.nodeSelector` (`preem: false`) — `js/ua_node_selector/check.mjs`;
178
+ - env→cluster DNS (`*.dev.env` / `*.ua.env`) — `js/env_dns/check.mjs`;
179
+ - скан артефактів Firebase Hosting у підкаталогах першого рівня — `js/firebase_hosting/check.mjs`.
180
+
181
+ Точна звірка `targetRef.name` HealthCheckPolicy з суфіксом `-hl` обчислюється з `hcp.metadata.name` і живе у Rego (`abie.health_check_policy`); за конвенцією `hcp.metadata.name` дорівнює `<deployment.name>`, тому окремий cross-file lookup до маніфесту Deployment не потрібен.
@@ -0,0 +1,15 @@
1
+ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
2
+
3
+ /**
4
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
+ * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
7
+ */
8
+ export function run(ctx) {
9
+ return runStandardRule(import.meta.dirname, ctx)
10
+ }
11
+
12
+ if (import.meta.main) {
13
+ // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
14
+ process.exit(await run())
15
+ }
@@ -1,19 +1,17 @@
1
- # Порт перевірки `deploymentDocumentHasAbieBasePreemNodeSelector` з
2
- # `npm/scripts/check-abie.mjs` (abie.mdc): кожен `Deployment` у файлах під
3
- # `…/k8s/.../base/…` має `spec.template.spec.nodeSelector.preem` зі
4
- # значенням, що вважається істинним (boolean `true` або рядок `"true"`
5
- # без урахування регістру). Overlay ua далі підміняє селектор
6
- # JSON6902-патчем на `preem: false`.
1
+ # Перевірка (abie.mdc): кожен `Deployment` у файлах під `…/k8s/.../base/…` має
2
+ # `spec.template.spec.nodeSelector.preem` зі значенням, що вважається істинним
3
+ # (boolean `true` або рядок `"true"` без урахування регістру). Overlay ua далі
4
+ # підміняє селектор JSON6902-патчем на `preem: false`
5
+ # (див. `js/ua_node_selector/check.mjs`).
7
6
  #
8
7
  # Запуск (локально, лише для одного base-YAML з Deployment):
9
8
  # conftest test path/to/k8s/base/deployment.yaml \
10
- # -p npm/policy/abie/base_deployment_preem \
9
+ # -p npm/rules/abie/policy/base_deployment_preem \
11
10
  # --namespace abie.base_deployment_preem
12
11
  #
13
- # JS відбирає файли під `…/k8s/.../base/…` (через `isAbieK8sBaseYamlPath`) і
14
- # викликає conftest з цією намеспейс. JS authoritative (`check-abie.mjs`:
15
- # `deploymentDocumentHasAbieBasePreemNodeSelector` + `ensureAbieBaseDeploymentPreemNodeSelector`).
16
- # Cross-file gating (правило `abie` у `.n-cursor.json`, шлях файла) — у JS.
12
+ # Cross-file gating: шлях `…/k8s/.../base/…` фільтрується через
13
+ # `policy/base_deployment_preem/target.json` (glob). Rule-level applies-гейт
14
+ # `js/applies/check.mjs` (поле `rules` у `.n-cursor.json`).
17
15
  #
18
16
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
19
17
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
@@ -1,9 +1,8 @@
1
- # Порт структурної перевірки `HealthCheckPolicy` з
2
- # `npm/scripts/check-abie.mjs` (abie.mdc).
1
+ # Структурна перевірка `HealthCheckPolicy` (abie.mdc).
3
2
  #
4
3
  # Запуск (локально):
5
4
  # conftest test path/to/k8s/.../hc.yaml \
6
- # -p npm/policy/abie/health_check_policy \
5
+ # -p npm/rules/abie/policy/health_check_policy \
7
6
  # --namespace abie.health_check_policy
8
7
  #
9
8
  # Перевіряє для `kind: HealthCheckPolicy`:
@@ -14,12 +13,13 @@
14
13
  # починається з `/`;
15
14
  # - `spec.default.config.httpHealthCheck.port: 8080`;
16
15
  # - `spec.targetRef.kind: Service`;
17
- # - `spec.targetRef.name` має суфікс `-hl` (headless backend).
16
+ # - `spec.targetRef.name` `<hcp.metadata.name>-hl` (exact, з нормалізацією
17
+ # суфікса).
18
18
  #
19
- # Cross-file gating (правило `abie` у `.n-cursor.json`, парність з Deployment-каталогу,
20
- # точна звірка `targetRef.name` з обчисленим `<deployment.name>-hl`) — у JS
21
- # (`check-abie.mjs`: `validateAbieHcPolicy`, `checkHcYamlFiles`). JS authoritative;
22
- # ця Rego — швидкий gate для одиничного YAML (наприклад через IDE).
19
+ # Cross-file gating: glob по `hc.yaml` у k8s-дереві у
20
+ # `policy/health_check_policy/target.json`. FS-парність HCP↔Deployment та
21
+ # modeline `hc.yaml` `js/hc_pairing/check.mjs`. Rule-level applies-гейт —
22
+ # `js/applies/check.mjs`.
23
23
  #
24
24
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
25
25
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
@@ -122,8 +122,7 @@ deny contains msg if {
122
122
  }
123
123
 
124
124
  # Нормалізація: якщо `metadata.name` уже закінчується на `-hl` — використовуємо
125
- # як є; інакше додаємо суфікс. Узгоджено з `validateAbieHcPolicy`
126
- # у `check-abie.mjs`.
125
+ # як є; інакше додаємо суфікс.
127
126
  expected_target_ref_name(name) := name if {
128
127
  endswith(name, "-hl")
129
128
  } else := concat("", [name, "-hl"])
@@ -1,15 +1,14 @@
1
- # Порт перевірки `HTTPRoute` у шарі `…/k8s/.../base/...` з
2
- # `npm/scripts/check-abie.mjs` (abie.mdc): дозволені лише hostnames з домену
3
- # `aiml.live` (включно з піддоменами та `*.aiml.live`).
1
+ # Перевірка `HTTPRoute` у шарі `…/k8s/.../base/...` (abie.mdc): дозволені лише
2
+ # hostnames з домену `aiml.live` (включно з піддоменами та `*.aiml.live`).
4
3
  #
5
4
  # Запуск (локально):
6
- # conftest test path/to/k8s/base/hr.yaml -p npm/policy/abie \
5
+ # conftest test path/to/k8s/base/hr.yaml \
6
+ # -p npm/rules/abie/policy/http_route_base \
7
7
  # --namespace abie.http_route_base
8
8
  #
9
- # Cross-file gating (саме шлях `…/base/…` визначає, чи застосовувати правило)
10
- # у JS: conftest викликаємо лише на YAML-ах з base/. Тут — лише валідація вмісту
11
- # `spec.hostnames`. JS authoritative (`check-abie.mjs`) — ця Rego гейт для
12
- # одиничного YAML.
9
+ # Cross-file gating (саме шлях `…/k8s/.../base/...` визначає, чи застосовувати
10
+ # правило) задає glob у `policy/http_route_base/target.json`. Тут — лише
11
+ # валідація вмісту `spec.hostnames`. Rule-level applies-гейт `js/applies/check.mjs`.
13
12
  #
14
13
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
15
14
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { dirname, relative } from 'node:path'
10
10
 
11
- import { pathHasK8sSegment } from '../../k8s/fix/manifests/check.mjs'
11
+ import { pathHasK8sSegment } from '../../k8s/js/manifests/check.mjs'
12
12
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
13
13
  import { isDeploymentDoc, readAndParseYamlDocs } from './yaml.mjs'
14
14
 
package/rules/adr/adr.mdc CHANGED
@@ -95,7 +95,7 @@ docs/adr/
95
95
  └── hooks.json # Cursor Agent stop-hooks для тих самих скриптів
96
96
  ```
97
97
 
98
- `.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @nitra/cursor`, коли правило `adr` увімкнене): [.gitignore.snippet](./fix/hooks/template/.gitignore.snippet).
98
+ `.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @nitra/cursor`, коли правило `adr` увімкнене): [.gitignore.snippet](./js/hooks/template/.gitignore.snippet).
99
99
 
100
100
  ## Stop-hook у `.claude/settings.json`
101
101
 
@@ -0,0 +1,15 @@
1
+ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
2
+
3
+ /**
4
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
+ * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
7
+ */
8
+ export function run(ctx) {
9
+ return runStandardRule(import.meta.dirname, ctx)
10
+ }
11
+
12
+ if (import.meta.main) {
13
+ // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
14
+ process.exit(await run())
15
+ }
@@ -0,0 +1,15 @@
1
+ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
2
+
3
+ /**
4
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
+ * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
7
+ */
8
+ export function run(ctx) {
9
+ return runStandardRule(import.meta.dirname, ctx)
10
+ }
11
+
12
+ if (import.meta.main) {
13
+ // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
14
+ process.exit(await run())
15
+ }
@@ -0,0 +1,15 @@
1
+ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
2
+
3
+ /**
4
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
+ * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
7
+ */
8
+ export function run(ctx) {
9
+ return runStandardRule(import.meta.dirname, ctx)
10
+ }
11
+
12
+ if (import.meta.main) {
13
+ // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
14
+ process.exit(await run())
15
+ }
@@ -43,7 +43,7 @@ alwaysApply: true
43
43
 
44
44
  **Вимагають bump + нову секцію CHANGELOG** — усі інші зміни в каталозі workspace (код, rego, правила, скіли, конфіги, тести тощо). Виняток `.cursor/` / `.claude/` **не** поширюється на джерело правил у репо `@nitra/cursor` — воно лежить під `npm/`, тож зміни в ньому далі вимагають bump.
45
45
 
46
- Перевірка програмна (`changelog/fix/consistency/check.mjs`).
46
+ Перевірка програмна (`changelog/js/consistency/check.mjs`).
47
47
 
48
48
  ## Дві моделі бази порівняння
49
49
 
@@ -0,0 +1,15 @@
1
+ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
2
+
3
+ /**
4
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
+ * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
6
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
7
+ */
8
+ export function run(ctx) {
9
+ return runStandardRule(import.meta.dirname, ctx)
10
+ }
11
+
12
+ if (import.meta.main) {
13
+ // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
14
+ process.exit(await run())
15
+ }