@nitra/cursor 3.22.0 → 3.23.0

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 (228) hide show
  1. package/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
  2. package/CHANGELOG.md +31 -3
  3. package/bin/docs/n-cursor.md +636 -0
  4. package/bin/docs/rename-yaml-extensions.md +207 -0
  5. package/bin/n-cursor.js +30 -3
  6. package/package.json +1 -1
  7. package/rules/abie/docs/fix.md +18 -0
  8. package/rules/abie/js/docs/applies.md +26 -0
  9. package/rules/abie/js/docs/env_dns.md +32 -0
  10. package/rules/abie/js/docs/firebase_hosting.md +23 -0
  11. package/rules/abie/js/docs/hc_pairing.md +35 -0
  12. package/rules/abie/js/docs/ua_http_route.md +28 -0
  13. package/rules/abie/js/docs/ua_node_selector.md +28 -0
  14. package/rules/abie/lib/docs/enabled.md +29 -0
  15. package/rules/abie/lib/docs/env-dns.md +35 -0
  16. package/rules/abie/lib/docs/hc-yaml.md +33 -0
  17. package/rules/abie/lib/docs/http-route.md +44 -0
  18. package/rules/abie/lib/docs/k8s-tree.md +40 -0
  19. package/rules/abie/lib/docs/kustomization-patches.md +47 -0
  20. package/rules/abie/lib/docs/overlay-paths.md +38 -0
  21. package/rules/abie/lib/docs/yaml.md +29 -0
  22. package/rules/adr/docs/fix.md +148 -0
  23. package/rules/adr/js/docs/hooks.md +259 -0
  24. package/rules/bun/docs/fix.md +156 -0
  25. package/rules/bun/js/docs/layout.md +393 -0
  26. package/rules/capacitor/docs/fix.md +121 -0
  27. package/rules/capacitor/js/docs/platforms.md +295 -0
  28. package/rules/changelog/changelog.mdc +2 -2
  29. package/rules/changelog/docs/fix.md +174 -0
  30. package/rules/changelog/js/consistency.mjs +114 -13
  31. package/rules/changelog/js/docs/consistency.md +387 -0
  32. package/rules/changelog/lib/docs/package-manifest.md +210 -0
  33. package/rules/ci4/docs/fix.md +179 -0
  34. package/rules/ci4/js/docs/marksman_config.md +128 -0
  35. package/rules/docker/docker.mdc +8 -3
  36. package/rules/docker/docs/fix.md +171 -0
  37. package/rules/docker/js/docs/lint.md +258 -0
  38. package/rules/docker/lib/docs/docker-hadolint.md +184 -0
  39. package/rules/docker/lib/docs/docker-mirror.md +247 -0
  40. package/rules/docker/lib/docs/docker-native-addon.md +170 -0
  41. package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
  42. package/rules/docker/lint/docs/lint.md +193 -0
  43. package/rules/efes/docs/fix.md +203 -0
  44. package/rules/feedback/docs/fix.md +140 -0
  45. package/rules/flow/docs/fix.md +152 -0
  46. package/rules/ga/docs/fix.md +158 -0
  47. package/rules/ga/js/docs/lint.md +100 -0
  48. package/rules/ga/js/docs/workflows.md +217 -0
  49. package/rules/ga/lint/docs/lint.md +209 -0
  50. package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
  51. package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
  52. package/rules/graphql/docs/fix.md +126 -0
  53. package/rules/graphql/js/docs/tooling.md +264 -0
  54. package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
  55. package/rules/hasura/docs/fix.md +120 -0
  56. package/rules/hasura/hasura.mdc +14 -0
  57. package/rules/hasura/js/docs/internal_urls.md +326 -0
  58. package/rules/image-avif/docs/fix.md +132 -0
  59. package/rules/image-avif/js/docs/avif_generation.md +241 -0
  60. package/rules/image-compress/docs/fix.md +150 -0
  61. package/rules/image-compress/js/docs/package_setup.md +191 -0
  62. package/rules/js-bun-db/docs/fix.md +148 -0
  63. package/rules/js-bun-db/js/docs/safety.md +231 -0
  64. package/rules/js-bun-db/js-bun-db.mdc +42 -13
  65. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
  66. package/rules/js-bun-redis/docs/fix.md +123 -0
  67. package/rules/js-bun-redis/js/docs/imports.md +176 -0
  68. package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
  69. package/rules/js-lint/docs/fix.md +117 -0
  70. package/rules/js-lint/js/docs/lint.md +250 -0
  71. package/rules/js-lint/js/docs/tooling.md +348 -0
  72. package/rules/js-lint/js/docs/utils_imports.md +207 -0
  73. package/rules/js-lint-ci/docs/fix.md +154 -0
  74. package/rules/js-lint-ci/js/docs/lint.md +144 -0
  75. package/rules/js-mssql/docs/fix.md +128 -0
  76. package/rules/js-mssql/js/docs/deps.md +263 -0
  77. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
  78. package/rules/js-run/docs/fix.md +144 -0
  79. package/rules/js-run/js/docs/runtime.md +388 -0
  80. package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
  81. package/rules/js-run/lib/docs/check-env-scan.md +433 -0
  82. package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
  83. package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
  84. package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
  85. package/rules/k8s/docs/fix.md +129 -0
  86. package/rules/k8s/js/docs/manifests.md +344 -0
  87. package/rules/k8s/js/manifests.mjs +6 -2
  88. package/rules/k8s/k8s.mdc +4 -2
  89. package/rules/k8s/lint/docs/lint.md +411 -0
  90. package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
  91. package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
  92. package/rules/nginx-default-tpl/docs/fix.md +124 -0
  93. package/rules/nginx-default-tpl/js/docs/template.md +378 -0
  94. package/rules/npm-module/docs/fix.md +98 -0
  95. package/rules/npm-module/js/docs/package_structure.md +274 -0
  96. package/rules/npm-module/js/docs/rule_meta.md +137 -0
  97. package/rules/npm-module/js/docs/skill_meta.md +190 -0
  98. package/rules/php/docs/fix.md +107 -0
  99. package/rules/php/js/docs/tooling.md +152 -0
  100. package/rules/php/lint/docs/lint.md +215 -0
  101. package/rules/python/docs/fix.md +163 -0
  102. package/rules/python/js/docs/applies.md +108 -0
  103. package/rules/python/js/docs/tooling.md +153 -0
  104. package/rules/python/lint/docs/lint.md +322 -0
  105. package/rules/rego/docs/fix.md +121 -0
  106. package/rules/rego/js/docs/applies.md +174 -0
  107. package/rules/rego/js/docs/lint.md +118 -0
  108. package/rules/rego/lint/docs/lint.md +204 -0
  109. package/rules/release/docs/change.md +185 -0
  110. package/rules/release/docs/fix.md +119 -0
  111. package/rules/release/docs/release.md +222 -0
  112. package/rules/release/lib/docs/aggregate.md +246 -0
  113. package/rules/release/lib/docs/change-file.md +200 -0
  114. package/rules/release/lib/docs/fallback.md +203 -0
  115. package/rules/rust/docs/fix.md +129 -0
  116. package/rules/rust/js/docs/applies.md +140 -0
  117. package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
  118. package/rules/security/docs/fix.md +86 -0
  119. package/rules/security/js/docs/lint.md +171 -0
  120. package/rules/security/js/docs/sample_secret.md +190 -0
  121. package/rules/security/js/docs/trufflehog.md +137 -0
  122. package/rules/security/js/lint.mjs +9 -1
  123. package/rules/style-lint/docs/fix.md +155 -0
  124. package/rules/style-lint/js/docs/lint.md +184 -0
  125. package/rules/style-lint/js/docs/tooling.md +194 -0
  126. package/rules/tauri/docs/fix.md +158 -0
  127. package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
  128. package/rules/tauri/js/docs/tooling.md +228 -0
  129. package/rules/test/coverage/coverage.mjs +15 -3
  130. package/rules/test/docs/fix.md +132 -0
  131. package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
  132. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
  133. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
  134. package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
  135. package/rules/test/js/docs/cargo_mutants_config.md +173 -0
  136. package/rules/test/js/docs/location.md +136 -0
  137. package/rules/test/js/docs/no-process-chdir.md +160 -0
  138. package/rules/test/js/docs/no-relative-fs-path.md +271 -0
  139. package/rules/test/js/docs/stryker_config.md +152 -0
  140. package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
  141. package/rules/text/docs/fix.md +118 -0
  142. package/rules/text/js/docs/forbidden-prettier.md +143 -0
  143. package/rules/text/js/docs/formatting.md +256 -0
  144. package/rules/text/js/docs/lint.md +122 -0
  145. package/rules/text/lint/docs/lint.md +220 -0
  146. package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
  147. package/rules/text/lint/docs/run-shellcheck.md +212 -0
  148. package/rules/text/lint/docs/run-v8r.md +197 -0
  149. package/rules/vue/docs/fix.md +127 -0
  150. package/rules/vue/js/docs/packages.md +335 -0
  151. package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
  152. package/rules/worktree/docs/fix.md +161 -0
  153. package/schemas/rule-meta.json +5 -1
  154. package/scripts/auto-rules.mjs +7 -4
  155. package/scripts/coverage-classify/docs/apply.md +202 -0
  156. package/scripts/coverage-classify/docs/cache.md +203 -0
  157. package/scripts/coverage-classify/docs/index.md +218 -0
  158. package/scripts/coverage-classify/docs/prompt.md +132 -0
  159. package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
  160. package/scripts/coverage-fix-extract.mjs +122 -0
  161. package/scripts/coverage-fix.mjs +1 -1
  162. package/scripts/dispatcher/docs/graph.md +346 -0
  163. package/scripts/dispatcher/docs/index.md +236 -0
  164. package/scripts/dispatcher/docs/trace.md +296 -0
  165. package/scripts/dispatcher/index.mjs +1 -1
  166. package/scripts/dispatcher/lib/active.mjs +4 -8
  167. package/scripts/dispatcher/lib/commands.mjs +7 -11
  168. package/scripts/dispatcher/lib/docs/active.md +348 -0
  169. package/scripts/dispatcher/lib/docs/artifact.md +232 -0
  170. package/scripts/dispatcher/lib/docs/budget.md +167 -0
  171. package/scripts/dispatcher/lib/docs/capability.md +196 -0
  172. package/scripts/dispatcher/lib/docs/commands.md +210 -0
  173. package/scripts/dispatcher/lib/docs/events.md +182 -0
  174. package/scripts/dispatcher/lib/docs/executor.md +190 -0
  175. package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
  176. package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
  177. package/scripts/dispatcher/lib/docs/gate.md +231 -0
  178. package/scripts/dispatcher/lib/docs/level.md +335 -0
  179. package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
  180. package/scripts/dispatcher/lib/docs/plan.md +200 -0
  181. package/scripts/dispatcher/lib/docs/planner.md +269 -0
  182. package/scripts/dispatcher/lib/docs/review.md +255 -0
  183. package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
  184. package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
  185. package/scripts/dispatcher/lib/docs/spec.md +203 -0
  186. package/scripts/dispatcher/lib/docs/state-store.md +303 -0
  187. package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
  188. package/scripts/dispatcher/lib/executor.mjs +6 -1
  189. package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
  190. package/scripts/dispatcher/lib/level.mjs +29 -3
  191. package/scripts/dispatcher/lib/review.mjs +1 -1
  192. package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
  193. package/scripts/docs/auto-rules.md +376 -0
  194. package/scripts/docs/auto-skills.md +173 -0
  195. package/scripts/docs/build-agents-commands.md +183 -0
  196. package/scripts/docs/cli-entry.md +153 -0
  197. package/scripts/docs/coverage-fix.md +177 -0
  198. package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
  199. package/scripts/lib/changed-files.mjs +4 -1
  200. package/scripts/lib/docs/changed-files.md +149 -0
  201. package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
  202. package/scripts/lib/docs/check-reporter.md +175 -0
  203. package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
  204. package/scripts/lib/docs/discover-checkable-rules.md +165 -0
  205. package/scripts/lib/docs/ensure-tool.md +254 -0
  206. package/scripts/lib/docs/generated-markdown.md +275 -0
  207. package/scripts/lib/docs/gha-workflow.md +326 -0
  208. package/scripts/lib/docs/inline-template-links.md +303 -0
  209. package/scripts/lib/docs/list-rule-ids.md +156 -0
  210. package/scripts/lib/docs/load-cursor-config.md +147 -0
  211. package/scripts/lib/docs/mirror-parity.md +167 -0
  212. package/scripts/lib/worktree.mjs +26 -0
  213. package/scripts/worktree-cli.mjs +12 -2
  214. package/skills/coverage-fix/SKILL.md +34 -45
  215. package/skills/docgen/SKILL.md +44 -23
  216. package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
  217. package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
  218. package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
  219. package/skills/docgen/js/docgen-ignore.mjs +54 -0
  220. package/skills/docgen/js/docgen-scan.mjs +37 -21
  221. package/skills/llm-patch/SKILL.md +23 -2
  222. package/skills/start-check/SKILL.md +26 -53
  223. package/skills/start-check/js/check.mjs +211 -0
  224. package/skills/taze/SKILL.md +9 -3
  225. package/skills/taze/js/diff.mjs +154 -0
  226. package/types/bin/n-cursor.d.ts +1 -1
  227. package/skills/fix-tests/SKILL.md +0 -119
  228. package/skills/fix-tests/meta.json +0 -1
@@ -0,0 +1,203 @@
1
+ # `fix.mjs` — entry-point правила `efes`
2
+
3
+ ## Огляд
4
+
5
+ Файл `npm/rules/efes/fix.mjs` — це **уніфікований entry-point** одного з правил пакету `@nitra/cursor` (правило `efes`, директорія `npm/rules/efes/`). Він виконує дві ролі одночасно:
6
+
7
+ 1. **Library mode** — експортує функцію `run(ctx)`, яку викликає CLI-оркестратор `@nitra/cursor` (через динамічний `import` модуля та виклик `run()` із підготовленим контекстом).
8
+ 2. **Standalone mode** — якщо файл запущено напряму (наприклад, `bun npm/rules/efes/fix.mjs`), він виконує повний CLI-цикл правила (завантаження конфігу, whitelist, summary), еквівалентний `npx @nitra/cursor fix efes`, і завершує процес із відповідним exit-кодом для CI/IDE.
9
+
10
+ Сам файл навмисно мінімальний — уся реальна логіка (виконання `applies → JS-concerns → policy → mdc-refs`) делегована у спільну бібліотечну функцію `runStandardRule`, що живе у `npm/scripts/lib/run-standard-rule.mjs`. Тому цей файл слугує лише **тонкою обгорткою (shim)** для одного конкретного правила `efes`, прив’язуючись до власної директорії через `import.meta.dirname`.
11
+
12
+ Така архітектура — стандартна для всіх правил із сімейства `npm/rules/<id>/fix.mjs`: одне правило = одна директорія = один однаковий `fix.mjs`, який відрізняється від сусідів лише розміщенням (`import.meta.dirname` бере шлях до **цієї** директорії правила).
13
+
14
+ ## Експорти / API
15
+
16
+ | Експорт | Тип | Призначення |
17
+ | ------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
18
+ | `run` | `(ctx?: RuleContext) => Promise<number>` | Library API — викликається CLI-оркестратором `@nitra/cursor` для запуску правила в межах одного спільного прогону над монорепо. |
19
+
20
+ Інших іменованих або дефолтних експортів файл не має. Окрім експорту, файл містить **side-effect блок** (top-level `if (isRunAsCli(...))`), що активується тільки коли файл запущено напряму.
21
+
22
+ ### Сигнатура `run`
23
+
24
+ ```js
25
+ export function run(ctx)
26
+ ```
27
+
28
+ - **Параметри**:
29
+ - `ctx` _(optional)_ — об’єкт типу `RuleContext`, імпортований із `npm/scripts/lib/run-standard-rule.mjs`. Містить розділяємий між правилами стан прогону: зокрема `walkCache` (кеш обходу файлів монорепо), а також іншу контекстну інформацію, передану CLI-оркестратором. Якщо `ctx` не передано (наприклад, при ad-hoc виклику), `runStandardRule` створює власний внутрішній контекст.
30
+ - **Повертає**: `Promise<number>` —
31
+ - `0` — правило не знайшло порушень (OK);
32
+ - `1` — є щонайменше одне порушення (fail).
33
+ - **Сторонні ефекти**:
34
+ - Сам `run` нічого не пише в `stdout`/`stderr` напряму, проте делегована функція `runStandardRule` веде логування прогресу, виводить summary порушень і — за потреби — модифікує файли проєкту (`autofix`).
35
+ - Може читати файлову систему монорепо (через walk), читати конфіг `@nitra/cursor` та `meta.json` правила.
36
+ - Не викликає `process.exit` у library mode — exit-код доручений викликаючій стороні.
37
+
38
+ ## Функції
39
+
40
+ ### `run(ctx)`
41
+
42
+ #### Сигнатура
43
+
44
+ ```js
45
+ /**
46
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
47
+ * Library mode: викликається CLI orchestration через `import + run(ctx)`.
48
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
49
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
50
+ */
51
+ export function run(ctx) {
52
+ return runStandardRule(import.meta.dirname, ctx)
53
+ }
54
+ ```
55
+
56
+ #### Параметри
57
+
58
+ | Ім’я | Тип | Обов’язковий | Опис |
59
+ | ----- | -------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
60
+ | `ctx` | `RuleContext` (див. `run-standard-rule.mjs`) | ні | Спільний контекст прогону, який оркестратор `@nitra/cursor` створює один раз для всієї пачки правил. Дозволяє ділити дорогий стан (наприклад, `walkCache` — список відсканованих файлів) між правилами без повторного обходу ФС. |
61
+
62
+ #### Повертає
63
+
64
+ `Promise<number>` — `0` або `1`. Семантика — звичайна для UNIX exit codes:
65
+
66
+ - `0` — все добре, правило `efes` не зафіксувало порушень.
67
+ - `1` — правило знайшло хоча б одне порушення (CI має впасти).
68
+
69
+ #### Логіка
70
+
71
+ Функція складається з єдиного рядка — `return runStandardRule(import.meta.dirname, ctx)`. Тобто:
72
+
73
+ 1. Беремо абсолютний шлях директорії, у якій лежить **цей** `fix.mjs` (`import.meta.dirname` дає `.../npm/rules/efes`).
74
+ 2. Передаємо цей шлях як перший аргумент у `runStandardRule` — щоб бібліотечна функція знала, **яке саме правило** запускає (звідки читати `meta.json`, `efes.mdc`, `policy/*` тощо).
75
+ 3. Передаємо отриманий `ctx` (або `undefined`).
76
+ 4. Повертаємо отриману `Promise<number>` — без обгортки, без додаткової обробки.
77
+
78
+ #### Сторонні ефекти
79
+
80
+ Усі побічні ефекти зосереджені всередині `runStandardRule` (читання файлів, walk, виконання checks, можливі autofix-записи). Сам `run` лише проксує виклик.
81
+
82
+ ### Top-level CLI-блок (без імені, не функція)
83
+
84
+ ```js
85
+ if (isRunAsCli(import.meta.url)) {
86
+ // eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
87
+ process.exit(await runRuleCli(import.meta.dirname))
88
+ }
89
+ ```
90
+
91
+ #### Поведінка
92
+
93
+ - Виконується **тільки** коли модуль запущено як CLI-точку входу (`bun npm/rules/efes/fix.mjs` або `node ...`). Перевірка `isRunAsCli(import.meta.url)` порівнює URL модуля з `process.argv[1]` (стандартна ідіома для ESM).
94
+ - Якщо умова виконалась — асинхронно (через `await` на top-level, що дозволяє ESM) викликає `runRuleCli(import.meta.dirname)`, який реалізує **повний CLI-цикл**:
95
+ - завантаження конфігу `@nitra/cursor`;
96
+ - застосування whitelist/ignore;
97
+ - запуск самого правила (через ту саму `runStandardRule` або еквівалентний механізм);
98
+ - друк summary;
99
+ - повернення exit-коду.
100
+ - Завершує процес через `process.exit(...)` із отриманим кодом (0 або 1).
101
+
102
+ #### Чому є коментар `eslint-disable-next-line`
103
+
104
+ У проєкті заборонено `process.exit` (`n/no-process-exit`, `unicorn/no-process-exit`) — але standalone entry-point **повинен** повертати exit-код, інакше CI не зрозуміє результат. Виключення оформлене явним коментарем із поясненням («standalone entry-point має повертати exit-code для CI/IDE»), щоб лінт не падав.
105
+
106
+ #### Чому є подвійний кінець рядка / два правила в одному disable
107
+
108
+ Бо обидва правила (`n/no-process-exit` від `eslint-plugin-n` і `unicorn/no-process-exit` від `eslint-plugin-unicorn`) забороняють те саме незалежно — disable має покривати обидва.
109
+
110
+ ## Залежності
111
+
112
+ ### Внутрішні (з того ж монорепо)
113
+
114
+ | Шлях | Що імпортується | Призначення |
115
+ | ----------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
116
+ | `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` | `isRunAsCli` — детектор «модуль виконано напряму». `runRuleCli` — повний CLI-обгортка для standalone-режиму (config loading, whitelist, summary). |
117
+ | `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule`, тип `RuleContext` (через JSDoc) | Бібліотечна реалізація стандартного потоку правила: `applies → JS-concerns → policy → mdc-refs`. |
118
+
119
+ Шляхи з `../../` ведуть на `npm/scripts/lib/` (бо файл лежить у `npm/rules/efes/`).
120
+
121
+ ### Зовнішні
122
+
123
+ Жодних `npm`-залежностей файл не імпортує напряму — усі вони транзитивно підвантажуються через `runStandardRule` / `runRuleCli`.
124
+
125
+ ### Файли поруч (через `import.meta.dirname`)
126
+
127
+ Бібліотечні функції очікують у директорії правила (`npm/rules/efes/`) певні файли — їх імпортує не `fix.mjs` напряму, а саме `runStandardRule`/`runRuleCli`:
128
+
129
+ - `meta.json` — метадані правила (id, applies, policy hints, тощо);
130
+ - `efes.mdc` — людиночитаний опис правила (Markdown + frontmatter);
131
+ - `policy/` — директорія з policy-чеками правила.
132
+
133
+ ## Потік виконання / Використання
134
+
135
+ ### Сценарій A — виклик з CLI `@nitra/cursor` (library mode)
136
+
137
+ ```text
138
+ $ npx @nitra/cursor fix
139
+ └─ CLI оркестратор будує список правил і прогонить кожне:
140
+ └─ import('npm/rules/efes/fix.mjs')
141
+ └─ run(ctx)
142
+ └─ runStandardRule('.../npm/rules/efes', ctx)
143
+ ├─ читає meta.json + efes.mdc
144
+ ├─ застосовує applies-фільтри
145
+ ├─ запускає JS-concerns (eslint-подібні чеки)
146
+ ├─ запускає policy/* (специфічні чеки правила)
147
+ └─ перевіряє mdc-refs (узгодженість документації)
148
+ └─ повертає 0 / 1
149
+ └─ оркестратор агрегує exit-коди всіх правил
150
+ ```
151
+
152
+ У цьому сценарії **жодних виходів через `process.exit`** із цього файлу — exit-код повертає оркестратор уже наприкінці всього прогону.
153
+
154
+ ### Сценарій B — прямий запуск файлу (standalone mode)
155
+
156
+ ```text
157
+ $ bun npm/rules/efes/fix.mjs
158
+
159
+ 1. ESM завантажує модуль.
160
+ 2. На етапі імпорту виконуються top-level statements.
161
+ 3. `isRunAsCli(import.meta.url)` -> true.
162
+ 4. `await runRuleCli(import.meta.dirname)`:
163
+ - повністю емулює `npx @nitra/cursor fix efes`;
164
+ - завантажує конфіг проєкту, whitelist;
165
+ - запускає правило;
166
+ - виводить summary в stdout/stderr.
167
+ 5. `process.exit(<code>)` — повертає exit-код shell-у / CI / IDE.
168
+ ```
169
+
170
+ Це зручно для:
171
+
172
+ - швидкого ad-hoc запуску однієї конкретної перевірки під час дебагу правила;
173
+ - інтеграції в IDE-таски (наприклад, окремі run configurations VSCode/JetBrains);
174
+ - ізольованого прогону в pre-commit hook на одне правило.
175
+
176
+ ### Сценарій C — імпорт із тестів
177
+
178
+ ```js
179
+ import { run } from 'npm/rules/efes/fix.mjs'
180
+ const code = await run() // 0 або 1
181
+ ```
182
+
183
+ CLI-блок **не запускається** (бо файл не є entry-point процесу), а `run` доступний як звичайна функція. Це дає змогу писати unit/e2e-тести правила без spawn-у дочірнього процесу.
184
+
185
+ ### Інваріанти / нюанси
186
+
187
+ - `import.meta.dirname` доступний у Node ≥ 20.11 та Bun. Якщо середовище старіше — потрібен fallback через `path.dirname(fileURLToPath(import.meta.url))`. У монорепо `@nitra/cursor` офіційно підтримується Bun-середовище, тому це не проблема.
188
+ - Top-level `await` працює тільки в ESM (`.mjs`) — саме тому файл має розширення `.mjs`, а не `.js`.
189
+ - Дві ролі файлу (library + standalone) — це усвідомлений патерн «dual entry-point»: ESM-import не запускає CLI-блок, бо `isRunAsCli` повертає `false`.
190
+ - Файл навмисно ідентичний по структурі з усіма іншими `npm/rules/<id>/fix.mjs`. Уся специфіка правила `efes` живе в сусідніх файлах (`meta.json`, `efes.mdc`, `policy/`) — у самому `fix.mjs` нічого правило-специфічного немає, окрім розташування.
191
+
192
+ ## Rebuild Test
193
+
194
+ Уявно «перебудувавши» файл лише за цією документацією, маємо отримати:
195
+
196
+ - ESM-модуль `.mjs` із двома імпортами зі спільної бібліотеки `npm/scripts/lib/`:
197
+ - `{ isRunAsCli, runRuleCli }` із `../../scripts/lib/run-rule-cli.mjs`;
198
+ - `{ runStandardRule }` із `../../scripts/lib/run-standard-rule.mjs`.
199
+ - Один іменований експорт `run(ctx)` із JSDoc, що описує параметр `ctx` як `RuleContext` (тип взято через `import('...')` JSDoc-нотацію) і повертає `Promise<number>` (`0` — OK, `1` — порушення). Тіло — один рядок `return runStandardRule(import.meta.dirname, ctx)`.
200
+ - Top-level guard `if (isRunAsCli(import.meta.url)) { process.exit(await runRuleCli(import.meta.dirname)) }` із коментарем-disable для двох ESLint-правил (`n/no-process-exit`, `unicorn/no-process-exit`) і поясненням «standalone entry-point має повертати exit-code для CI/IDE».
201
+ - Жодних інших declarations, експортів чи побічних ефектів.
202
+
203
+ Такий «реконструйований» файл функціонально й текстово збігається з оригіналом `npm/rules/efes/fix.mjs`.
@@ -0,0 +1,140 @@
1
+ # `npm/rules/feedback/fix.mjs`
2
+
3
+ ## Огляд
4
+
5
+ Файл є точкою входу (entry-point) для правила `feedback` у CLI-наборі `@nitra/cursor`. Він реалізує дві паралельні ролі того ж самого модуля:
6
+
7
+ 1. **Library mode** — експортує функцію `run(ctx)`, яку викликають інші частини оркестратора (наприклад, агрегований `fix`/`lint`-прогін, де всі правила запускаються послідовно з шарінгом кешу обходу файлів `walkCache`).
8
+ 2. **Standalone mode** — якщо файл стартує безпосередньо як CLI-скрипт (`bun rules/feedback/fix.mjs`), він виконується як повний аналог команди `npx @nitra/cursor fix feedback` із завантаженням конфігу, застосуванням whitelist та підсумком.
9
+
10
+ Сам файл логіки правила не містить — він є тонким адаптером, що делегує роботу до спільного раннера `runStandardRule`. Стандартний потік правила, який запускається через цей адаптер: `applies → JS-concerns → policy → mdc-refs` (тобто перевіряється застосовність до проєкту, далі — JS-специфічні перевірки, далі — policy-шар, наприкінці — синхронізація посилань у відповідному `.mdc`-файлі).
11
+
12
+ Модуль використовує `import.meta.dirname`, тому він повністю прив'язаний до місця свого розташування: каталог правила (`npm/rules/feedback/`) автоматично стає коренем, у якому раннер шукає `meta.json`, `feedback.mdc` та інші артефакти.
13
+
14
+ ## Експорти / API
15
+
16
+ | Експорт | Тип | Призначення |
17
+ | ------- | --------------------------------- | ---------------------------------------------------------------------------------------------------- |
18
+ | `run` | `function(ctx?): Promise<number>` | Library-точка входу правила. Викликається оркестратором; повертає exit-code (0 — OK, 1 — порушення). |
19
+
20
+ Side-експортів немає. Default-експорту немає.
21
+
22
+ Поведінка при прямому запуску файлу як CLI (через `process.exit(await runRuleCli(...))`) — це не експорт, а top-level side-effect, активний лише коли `isRunAsCli(import.meta.url)` повертає `true`.
23
+
24
+ ## Функції
25
+
26
+ ### `run(ctx)`
27
+
28
+ ```js
29
+ export function run(ctx)
30
+ ```
31
+
32
+ - **Призначення:** запустити правило `feedback` у library-режимі. Делегує всю роботу до `runStandardRule`, передаючи каталог поточного файлу як ідентифікатор правила.
33
+ - **Параметри:**
34
+ - `ctx` — `RuleContext | undefined`. Опціональний контекст прогону. Тип імпортується через JSDoc-посилання `import('../../scripts/lib/run-standard-rule.mjs').RuleContext`. Зокрема, контекст несе спільні структури між правилами (наприклад, `walkCache` — кеш обходу файлової системи, щоб не сканувати дерево кілька разів у разі прогону кількох правил поспіль). Якщо `ctx` не переданий, `runStandardRule` створює власний внутрішній контекст.
35
+ - **Повертає:** `Promise<number>` — exit-code:
36
+ - `0` — правило застосовне і порушень не знайдено, або правило не застосовне до поточного проєкту (`applies` повернув `false`);
37
+ - `1` — знайдені порушення (policy / JS-concerns / mdc-refs).
38
+ - **Side effects:** жодних прямих у цій функції. Опосередковано через `runStandardRule` можливі: читання файлів проєкту, читання `meta.json` і `feedback.mdc`, лог у stdout/stderr, мутація переданого `walkCache`.
39
+ - **Винятки:** функція сама не кидає; будь-яка помилка приходить як rejected promise від `runStandardRule`.
40
+
41
+ ### Top-level standalone-блок
42
+
43
+ ```js
44
+ if (isRunAsCli(import.meta.url)) {
45
+ process.exit(await runRuleCli(import.meta.dirname))
46
+ }
47
+ ```
48
+
49
+ - **Призначення:** перетворити цей же модуль на CLI-скрипт. Якщо файл був запущений напряму (а не імпортований), виконується повний CLI-цикл правила — еквівалент `npx @nitra/cursor fix feedback`.
50
+ - **Виклик:** `runRuleCli(import.meta.dirname)` отримує абсолютний шлях до каталогу правила; всередині раннера це задає, який саме `meta.json`/`*.mdc` і `applies/policy/...`-функції підтягуються.
51
+ - **Повертає / завершення:** результат `runRuleCli` (число) йде в `process.exit(...)`, тобто процес одразу завершується з відповідним exit-code. Це навмисно: standalone entry-point має повернути код виходу для CI/IDE-інтеграцій.
52
+ - **Спеціальні маркери:**
53
+ - `// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit` — свідома відмова від загальної заборони `process.exit` саме тут, бо це standalone-точка входу.
54
+ - **Side effects:** завершує процес Node/Bun.
55
+
56
+ ## Залежності
57
+
58
+ Файл імпортує два внутрішні модулі (relative-шляхи, без зовнішніх npm-пакетів):
59
+
60
+ | Імпорт | З файлу | Що використовується | Роль |
61
+ | ----------------- | ----------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
62
+ | `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Функція-предикат | Визначає, чи цей `.mjs`-файл був запущений напряму як CLI (через `import.meta.url`), а не імпортований як модуль. |
63
+ | `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Async-функція | Виконує повний CLI-цикл правила: завантаження конфігу, whitelist-фільтрацію, прогін стандартного раннера, форматування підсумку, повернення exit-code. |
64
+ | `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Async-функція + JSDoc-тип `RuleContext` | Стандартний раннер для правил: `applies → JS-concerns → policy → mdc-refs`. Шукає артефакти у переданому каталозі. |
65
+
66
+ Глобали з рантайму:
67
+
68
+ - `import.meta.dirname` — абсолютний шлях каталогу поточного `.mjs`-файлу (Bun / Node ≥ 20.11). Використовується як «корінь правила».
69
+ - `import.meta.url` — file-URL модуля; передається в `isRunAsCli` для надійного порівняння з `process.argv[1]`.
70
+ - `process.exit` — викликається лише у standalone-гілці.
71
+
72
+ Зовнішні пакети не імпортуються.
73
+
74
+ ## Потік виконання / Використання
75
+
76
+ ### Сценарій A — імпорт як library
77
+
78
+ ```js
79
+ import { run } from './npm/rules/feedback/fix.mjs'
80
+
81
+ const code = await run(/* ctx */)
82
+ if (code !== 0) {
83
+ // правило знайшло порушення
84
+ }
85
+ ```
86
+
87
+ Послідовність всередині:
88
+
89
+ 1. Викликається `run(ctx)`.
90
+ 2. `run` повертає `runStandardRule(import.meta.dirname, ctx)`.
91
+ 3. Усередині `runStandardRule`:
92
+ - перевіряє `applies` (чи правило застосовне до проєкту);
93
+ - проганяє JS-concerns (lint/structure);
94
+ - проганяє policy-перевірки;
95
+ - звіряє `mdc-refs` (узгодженість посилань між кодом і `feedback.mdc`).
96
+ 4. Повертається `0` або `1`.
97
+
98
+ Top-level `if (isRunAsCli(...))` у цьому випадку не спрацьовує — `isRunAsCli(import.meta.url)` повертає `false`, бо стартова точка процесу — інший файл.
99
+
100
+ ### Сценарій B — прямий запуск як CLI
101
+
102
+ ```bash
103
+ bun npm/rules/feedback/fix.mjs
104
+ # або еквівалент:
105
+ npx @nitra/cursor fix feedback
106
+ ```
107
+
108
+ Послідовність:
109
+
110
+ 1. Node/Bun завантажує модуль; виконуються `import`-и.
111
+ 2. Перевіряється `isRunAsCli(import.meta.url)` → `true`.
112
+ 3. Викликається `await runRuleCli(import.meta.dirname)`. Цей раннер:
113
+ - завантажує загальний конфіг проєкту;
114
+ - застосовує whitelist (якщо в конфізі обмежений набір правил);
115
+ - всередині все одно проганяє ту ж саму `runStandardRule`-логіку;
116
+ - друкує форматований summary;
117
+ - повертає exit-code.
118
+ 4. `process.exit(code)` миттєво завершує процес із отриманим кодом, який потім читає CI/IDE.
119
+
120
+ ### Чому дві ролі в одному файлі
121
+
122
+ Конвенція в `npm/rules/<id>/fix.mjs` дозволяє:
123
+
124
+ - оркестратору проганяти всі правила одним процесом, шарячи `walkCache` (швидко, для batch-режимів);
125
+ - розробнику запустити **одне** правило точково з шелла без обгорток і отримати CI-сумісний exit-code.
126
+
127
+ Жодного дублювання логіки немає — обидві гілки в кінцевому рахунку викликають один і той самий `runStandardRule`, просто standalone-гілка додає шар CLI-обгортки (`runRuleCli`).
128
+
129
+ ## Rebuild Test
130
+
131
+ Чи можна за цим документом без доступу до файлу відтворити модуль еквівалентної поведінки? Так:
132
+
133
+ - ім'я експорту: `run`, сигнатура `(ctx) => Promise<number>`;
134
+ - тіло: `return runStandardRule(import.meta.dirname, ctx)`;
135
+ - top-level `if`: `isRunAsCli(import.meta.url)` → `process.exit(await runRuleCli(import.meta.dirname))`;
136
+ - два імпорти з точно вказаних шляхів (`../../scripts/lib/run-rule-cli.mjs` для `isRunAsCli`/`runRuleCli`, `../../scripts/lib/run-standard-rule.mjs` для `runStandardRule`);
137
+ - ESLint-disable коментар над `process.exit` для `n/no-process-exit` і `unicorn/no-process-exit`;
138
+ - JSDoc-блок над `run` із посиланням на тип `RuleContext` через `import('...')`-нотацію.
139
+
140
+ Усі ці елементи описані вище — реконструкція еквівалентного файлу можлива.
@@ -0,0 +1,152 @@
1
+ # `npm/rules/flow/fix.mjs`
2
+
3
+ ## Огляд
4
+
5
+ Файл є точкою входу (entry point) правила `flow` у каталозі правил пакета `@nitra/cursor`. Правило `flow` належить до категорії **pure-doc contract-правил**: воно не має програмних concern-ів і фактично валідує лише супровідний документ `.mdc` (Markdown-Contract) у тій самій теці.
6
+
7
+ Модуль виконує дві основні задачі:
8
+
9
+ 1. Експортує функцію `run(ctx)`, яка делегує всю роботу стандартному пайплайну виконання правил (`runStandardRule`). Цей експорт використовується диспетчером CLI `@nitra/cursor` (наприклад, командою `npx @nitra/cursor fix flow`) та внутрішніми оркестраторами правил.
10
+ 2. Підтримує **standalone-режим** — якщо файл запущено напряму як CLI-скрипт (через `bun rules/flow/fix.mjs`), він викликає універсальний CLI-раннер `runRuleCli` і завершує процес коректним exit-кодом, придатним для CI/IDE-інтеграцій.
11
+
12
+ Стандартний пайплайн правила, який запускає `runStandardRule`, виглядає так (порядок фіксований):
13
+ `applies` → `JS-concerns` → `policy` → `mdc-refs`. Оскільки в цьому правилі програмних concern-ів немає, реальна перевірка зводиться до етапів `applies`, `policy` та валідації посилань у `.mdc`-файлі.
14
+
15
+ ## Експорти / API
16
+
17
+ Модуль є **ES-модулем** (`.mjs`), має такі експорти:
18
+
19
+ | Експорт | Тип | Призначення |
20
+ | ------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
21
+ | `run` | `function(ctx?: RuleContext): Promise<number>` | Іменований експорт; основна точка входу для виконання правила. Викликається диспетчером правил `@nitra/cursor`. |
22
+
23
+ Side-effect верхнього рівня (не експорт, але частина публічної поведінки модуля):
24
+
25
+ - Якщо `import.meta.url` відповідає стартовому файлу процесу (`isRunAsCli`), модуль запускає `runRuleCli(import.meta.dirname)` та викликає `process.exit(...)` із кодом, який повернув раннер.
26
+
27
+ Default-експорту **немає**.
28
+
29
+ ## Функції
30
+
31
+ ### `run(ctx)`
32
+
33
+ ```js
34
+ export function run(ctx)
35
+ ```
36
+
37
+ - **Призначення.** Запустити стандартизований пайплайн правила `flow` для поточної теки (`import.meta.dirname` — каталог, де лежить `fix.mjs`, тобто `npm/rules/flow/`).
38
+ - **Параметри:**
39
+ - `ctx` _(необов'язковий, `RuleContext`)_ — контекст прогону правил. Тип `RuleContext` імпортується з `../../scripts/lib/run-standard-rule.mjs`. У контексті, зокрема, передаються спільні структури між правилами — наприклад, `walkCache` (кеш обходу файлової системи), щоб не повторювати дорогі сканування при послідовному запуску багатьох правил.
40
+ - **Повертає:** `Promise<number>`.
41
+ - `0` — правило виконано успішно, порушень не виявлено.
42
+ - `1` — знайдено порушення (наприклад, проблеми у відповідному `.mdc`-файлі).
43
+ - **Side effects:** жодних безпосередньо у функції; усі побічні ефекти (читання FS, виведення повідомлень, агрегація знахідок) інкапсульовані всередині `runStandardRule`.
44
+ - **Контракт пайплайну, який вмикає `runStandardRule`:**
45
+ - `applies` — визначає, чи правило застосовне до поточного проєкту/контексту.
46
+ - `JS-concerns` — програмні перевірки JS/MJS-коду (у цьому правилі — фактично відсутні, бо правило pure-doc).
47
+ - `policy` — політики/обмеження, спільні для всіх правил.
48
+ - `mdc-refs` — валідація посилань і структури супровідного `.mdc`-файлу.
49
+
50
+ ### IIFE-блок CLI-режиму (top-level, не функція)
51
+
52
+ ```js
53
+ if (isRunAsCli(import.meta.url)) {
54
+ process.exit(await runRuleCli(import.meta.dirname))
55
+ }
56
+ ```
57
+
58
+ - **Призначення.** Дозволити запуск файлу як самостійного скрипта (`bun rules/flow/fix.mjs`), повністю еквівалентний `npx @nitra/cursor fix flow`.
59
+ - **Семантика умови.** `isRunAsCli(import.meta.url)` повертає `true`, якщо цей файл є стартовим модулем процесу (а не імпортованим іншим модулем). Це стандартний у пакеті паттерн dual-mode (бібліотека + CLI).
60
+ - **Параметри `runRuleCli`:** передається `import.meta.dirname` — абсолютний шлях до каталогу `npm/rules/flow/`. Раннер сам читає `meta.json`/`fix.mjs`/`.mdc` із цього каталогу.
61
+ - **Завершення.** `process.exit(...)` із цілочисельним exit-кодом, який повернув `runRuleCli`. Це важливо для CI/IDE: ненульовий код → провалена перевірка.
62
+ - **ESLint-винятки в коді:** рядок із `process.exit` навмисно вимикає правила `n/no-process-exit` та `unicorn/no-process-exit` через коментар `// eslint-disable-next-line ... -- standalone entry-point має повертати exit-code для CI/IDE`. Це задокументоване відхилення: для standalone CLI вихід кодом потрібен.
63
+
64
+ ## Залежності
65
+
66
+ Усі залежності — **внутрішні**, з підкаталогу `scripts/lib/` пакета:
67
+
68
+ | Імпорт | Шлях | Що дає |
69
+ | ----------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
70
+ | `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Предикат: чи `import.meta.url` модуля збігається зі стартовим файлом процесу (визначає standalone-запуск). |
71
+ | `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Універсальний CLI-раннер для одного правила: парсить аргументи, готує контекст, викликає `run`, форматує вивід, повертає exit-код. |
72
+ | `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Стандартний пайплайн правил `applies → JS-concerns → policy → mdc-refs`; читає сусідній `.mdc`, виконує перевірки, агрегує результат у код повернення. |
73
+
74
+ Зовнішніх (npm-пакетних) залежностей файл не імпортує.
75
+
76
+ Опосередковано (через `runStandardRule`) використовуються:
77
+
78
+ - Сусідні файли у каталозі `npm/rules/flow/` — насамперед `.mdc` (Markdown-Contract правила) та `meta.json` (метадані правила), якщо такі присутні.
79
+ - Спільна інфраструктура з `npm/scripts/lib/` (логування, форматування результатів, утиліти FS-обходу).
80
+
81
+ ## Потік виконання / Використання
82
+
83
+ ### Сценарій 1. Виклик з диспетчера правил (типовий)
84
+
85
+ 1. CLI `@nitra/cursor` (або інший оркестратор) визначає, що потрібно запустити правило `flow` у режимі `fix`.
86
+ 2. Диспетчер імпортує `fix.mjs` як модуль і викликає `run(ctx)`, передаючи спільний `ctx` (наприклад, із заповненим `walkCache`).
87
+ 3. `run` делегує виконання `runStandardRule(import.meta.dirname, ctx)`.
88
+ 4. `runStandardRule` послідовно виконує етапи `applies → JS-concerns → policy → mdc-refs`.
89
+ 5. Повертається `Promise<number>` — `0` або `1`.
90
+ 6. Оркестратор агрегує коди повернення з усіх правил.
91
+
92
+ ### Сценарій 2. Standalone CLI-запуск
93
+
94
+ Команда:
95
+
96
+ ```bash
97
+ bun npm/rules/flow/fix.mjs
98
+ ```
99
+
100
+ (еквівалентно `npx @nitra/cursor fix flow`).
101
+
102
+ 1. Інтерпретатор виконує модуль як стартовий файл процесу.
103
+ 2. Рядок `if (isRunAsCli(import.meta.url))` повертає `true`.
104
+ 3. Викликається `await runRuleCli(import.meta.dirname)`:
105
+ - Раннер парсить CLI-аргументи (флаги, шляхи).
106
+ - Готує `ctx` для одиничного запуску.
107
+ - Викликає `run` цього ж модуля (або еквівалентний внутрішній механізм через `runStandardRule`).
108
+ - Форматує вивід (звіт про знахідки, кольори, summary).
109
+ 4. `process.exit(<код>)` — процес завершується з кодом раннера: `0` за успіху, `1` за порушень.
110
+
111
+ ### Сценарій 3. Імпорт у тестах / скриптах
112
+
113
+ Можливий імпорт `run` для unit-/інтеграційного тестування або для написання кастомних оркестраторів:
114
+
115
+ ```js
116
+ import { run } from '@nitra/cursor/rules/flow/fix.mjs'
117
+
118
+ const code = await run() // або await run(customCtx)
119
+ if (code !== 0) {
120
+ // обробити порушення
121
+ }
122
+ ```
123
+
124
+ У цьому випадку top-level блок `if (isRunAsCli(...))` **не спрацьовує** (файл імпортовано, а не запущено напряму), отже `process.exit` не викликається — побічних ефектів на процес немає.
125
+
126
+ ### Контракт повернення
127
+
128
+ | Код | Значення | Дія викликача |
129
+ | --- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
130
+ | `0` | Правило застосоване й порушень немає; або правило не застосовне (etap `applies` повернув false). | Продовжувати конвеєр. |
131
+ | `1` | Знайдено порушення (зазвичай — невідповідність `.mdc`-файлу контракту). | CI має зафейлити збірку; IDE — показати помилку. |
132
+
133
+ ## Особливості реалізації / Нотатки
134
+
135
+ - **Pure-doc rule.** За коментарем у JSDoc функції `run`, правило `flow` не має програмних concern-ів. Це означає, що каталог `npm/rules/flow/` навмисно не містить (або містить лише мінімум) перевірок поверх JS-коду — основний контракт описано в `.mdc`-файлі, який і валідується через підетап `mdc-refs`.
136
+ - **Стандартизований шаблон.** Файл написаний за єдиним шаблоном для всіх правил пакета: тонкий wrapper над `runStandardRule` + опційний standalone CLI-блок. Це спрощує генерацію/підтримку правил і робить їх взаємозамінними для оркестратора.
137
+ - **`import.meta.dirname`** використовується замість `__dirname` (який недоступний в ES-модулях) і передається у `runStandardRule`/`runRuleCli` як локалізатор каталогу правила.
138
+ - **Top-level `await`** у блоці `process.exit(await runRuleCli(...))` дозволений, тому що модуль є ES-модулем (`.mjs`).
139
+ - **ESLint-disable з обґрунтуванням.** Вимкнення `n/no-process-exit` та `unicorn/no-process-exit` зроблено з явним коментарем-обґрунтуванням після `--`, що відповідає внутрішнім js-lint правилам пакета (вимога описувати причину disable).
140
+
141
+ ## Rebuild Test (контрольна реконструкція)
142
+
143
+ Спираючись виключно на цю документацію, файл `fix.mjs` можна відновити так:
144
+
145
+ 1. Створити ES-модуль `npm/rules/flow/fix.mjs`.
146
+ 2. Імпортувати з `../../scripts/lib/run-rule-cli.mjs` іменовані експорти `isRunAsCli` та `runRuleCli`.
147
+ 3. Імпортувати з `../../scripts/lib/run-standard-rule.mjs` іменований експорт `runStandardRule`.
148
+ 4. Експортувати функцію `run(ctx)`, яка повертає результат виклику `runStandardRule(import.meta.dirname, ctx)`. Контракт: `Promise<number>` (`0` — OK, `1` — порушення). JSDoc описує пайплайн `applies → JS-concerns → policy → mdc-refs` та зазначає, що правило pure-doc.
149
+ 5. Додати top-level блок: `if (isRunAsCli(import.meta.url)) process.exit(await runRuleCli(import.meta.dirname))`.
150
+ 6. Поряд із `process.exit` поставити `eslint-disable-next-line` для `n/no-process-exit` та `unicorn/no-process-exit` з обґрунтуванням, що standalone entry-point має повертати exit-code для CI/IDE.
151
+
152
+ Такий файл буде функціонально еквівалентним оригіналу.