@nitra/cursor 3.21.1 → 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 (231) hide show
  1. package/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
  2. package/CHANGELOG.md +37 -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/js/lint-findings.mjs +110 -0
  74. package/rules/js-lint/js/lint.mjs +86 -15
  75. package/rules/js-lint-ci/docs/fix.md +154 -0
  76. package/rules/js-lint-ci/js/docs/lint.md +144 -0
  77. package/rules/js-mssql/docs/fix.md +128 -0
  78. package/rules/js-mssql/js/docs/deps.md +263 -0
  79. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
  80. package/rules/js-run/docs/fix.md +144 -0
  81. package/rules/js-run/js/docs/runtime.md +388 -0
  82. package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
  83. package/rules/js-run/lib/docs/check-env-scan.md +433 -0
  84. package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
  85. package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
  86. package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
  87. package/rules/k8s/docs/fix.md +129 -0
  88. package/rules/k8s/js/docs/manifests.md +344 -0
  89. package/rules/k8s/js/manifests.mjs +6 -2
  90. package/rules/k8s/k8s.mdc +4 -2
  91. package/rules/k8s/lint/docs/lint.md +411 -0
  92. package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
  93. package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
  94. package/rules/nginx-default-tpl/docs/fix.md +124 -0
  95. package/rules/nginx-default-tpl/js/docs/template.md +378 -0
  96. package/rules/npm-module/docs/fix.md +98 -0
  97. package/rules/npm-module/js/docs/package_structure.md +274 -0
  98. package/rules/npm-module/js/docs/rule_meta.md +137 -0
  99. package/rules/npm-module/js/docs/skill_meta.md +190 -0
  100. package/rules/php/docs/fix.md +107 -0
  101. package/rules/php/js/docs/tooling.md +152 -0
  102. package/rules/php/lint/docs/lint.md +215 -0
  103. package/rules/python/docs/fix.md +163 -0
  104. package/rules/python/js/docs/applies.md +108 -0
  105. package/rules/python/js/docs/tooling.md +153 -0
  106. package/rules/python/lint/docs/lint.md +322 -0
  107. package/rules/rego/docs/fix.md +121 -0
  108. package/rules/rego/js/docs/applies.md +174 -0
  109. package/rules/rego/js/docs/lint.md +118 -0
  110. package/rules/rego/lint/docs/lint.md +204 -0
  111. package/rules/release/docs/change.md +185 -0
  112. package/rules/release/docs/fix.md +119 -0
  113. package/rules/release/docs/release.md +222 -0
  114. package/rules/release/lib/docs/aggregate.md +246 -0
  115. package/rules/release/lib/docs/change-file.md +200 -0
  116. package/rules/release/lib/docs/fallback.md +203 -0
  117. package/rules/rust/docs/fix.md +129 -0
  118. package/rules/rust/js/docs/applies.md +140 -0
  119. package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
  120. package/rules/security/docs/fix.md +86 -0
  121. package/rules/security/js/docs/lint.md +171 -0
  122. package/rules/security/js/docs/sample_secret.md +190 -0
  123. package/rules/security/js/docs/trufflehog.md +137 -0
  124. package/rules/security/js/lint.mjs +9 -1
  125. package/rules/style-lint/docs/fix.md +155 -0
  126. package/rules/style-lint/js/docs/lint.md +184 -0
  127. package/rules/style-lint/js/docs/tooling.md +194 -0
  128. package/rules/tauri/docs/fix.md +158 -0
  129. package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
  130. package/rules/tauri/js/docs/tooling.md +228 -0
  131. package/rules/test/coverage/coverage.mjs +15 -3
  132. package/rules/test/docs/fix.md +132 -0
  133. package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
  134. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
  135. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
  136. package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
  137. package/rules/test/js/docs/cargo_mutants_config.md +173 -0
  138. package/rules/test/js/docs/location.md +136 -0
  139. package/rules/test/js/docs/no-process-chdir.md +160 -0
  140. package/rules/test/js/docs/no-relative-fs-path.md +271 -0
  141. package/rules/test/js/docs/stryker_config.md +152 -0
  142. package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
  143. package/rules/text/docs/fix.md +118 -0
  144. package/rules/text/js/docs/forbidden-prettier.md +143 -0
  145. package/rules/text/js/docs/formatting.md +256 -0
  146. package/rules/text/js/docs/lint.md +122 -0
  147. package/rules/text/lint/docs/lint.md +220 -0
  148. package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
  149. package/rules/text/lint/docs/run-shellcheck.md +212 -0
  150. package/rules/text/lint/docs/run-v8r.md +197 -0
  151. package/rules/vue/docs/fix.md +127 -0
  152. package/rules/vue/js/docs/packages.md +335 -0
  153. package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
  154. package/rules/worktree/docs/fix.md +161 -0
  155. package/schemas/rule-meta.json +5 -1
  156. package/scripts/auto-rules.mjs +7 -4
  157. package/scripts/coverage-classify/docs/apply.md +202 -0
  158. package/scripts/coverage-classify/docs/cache.md +203 -0
  159. package/scripts/coverage-classify/docs/index.md +218 -0
  160. package/scripts/coverage-classify/docs/prompt.md +132 -0
  161. package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
  162. package/scripts/coverage-fix-extract.mjs +122 -0
  163. package/scripts/coverage-fix.mjs +1 -1
  164. package/scripts/dispatcher/docs/graph.md +346 -0
  165. package/scripts/dispatcher/docs/index.md +236 -0
  166. package/scripts/dispatcher/docs/trace.md +296 -0
  167. package/scripts/dispatcher/index.mjs +1 -1
  168. package/scripts/dispatcher/lib/active.mjs +4 -8
  169. package/scripts/dispatcher/lib/commands.mjs +7 -11
  170. package/scripts/dispatcher/lib/docs/active.md +348 -0
  171. package/scripts/dispatcher/lib/docs/artifact.md +232 -0
  172. package/scripts/dispatcher/lib/docs/budget.md +167 -0
  173. package/scripts/dispatcher/lib/docs/capability.md +196 -0
  174. package/scripts/dispatcher/lib/docs/commands.md +210 -0
  175. package/scripts/dispatcher/lib/docs/events.md +182 -0
  176. package/scripts/dispatcher/lib/docs/executor.md +190 -0
  177. package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
  178. package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
  179. package/scripts/dispatcher/lib/docs/gate.md +231 -0
  180. package/scripts/dispatcher/lib/docs/level.md +335 -0
  181. package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
  182. package/scripts/dispatcher/lib/docs/plan.md +200 -0
  183. package/scripts/dispatcher/lib/docs/planner.md +269 -0
  184. package/scripts/dispatcher/lib/docs/review.md +255 -0
  185. package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
  186. package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
  187. package/scripts/dispatcher/lib/docs/spec.md +203 -0
  188. package/scripts/dispatcher/lib/docs/state-store.md +303 -0
  189. package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
  190. package/scripts/dispatcher/lib/executor.mjs +6 -1
  191. package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
  192. package/scripts/dispatcher/lib/level.mjs +29 -3
  193. package/scripts/dispatcher/lib/review.mjs +1 -1
  194. package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
  195. package/scripts/docs/auto-rules.md +376 -0
  196. package/scripts/docs/auto-skills.md +173 -0
  197. package/scripts/docs/build-agents-commands.md +183 -0
  198. package/scripts/docs/cli-entry.md +153 -0
  199. package/scripts/docs/coverage-fix.md +177 -0
  200. package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
  201. package/scripts/lib/changed-files.mjs +4 -1
  202. package/scripts/lib/diff-added-lines.mjs +85 -0
  203. package/scripts/lib/docs/changed-files.md +149 -0
  204. package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
  205. package/scripts/lib/docs/check-reporter.md +175 -0
  206. package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
  207. package/scripts/lib/docs/discover-checkable-rules.md +165 -0
  208. package/scripts/lib/docs/ensure-tool.md +254 -0
  209. package/scripts/lib/docs/generated-markdown.md +275 -0
  210. package/scripts/lib/docs/gha-workflow.md +326 -0
  211. package/scripts/lib/docs/inline-template-links.md +303 -0
  212. package/scripts/lib/docs/list-rule-ids.md +156 -0
  213. package/scripts/lib/docs/load-cursor-config.md +147 -0
  214. package/scripts/lib/docs/mirror-parity.md +167 -0
  215. package/scripts/lib/worktree.mjs +26 -0
  216. package/scripts/worktree-cli.mjs +12 -2
  217. package/skills/coverage-fix/SKILL.md +34 -45
  218. package/skills/docgen/SKILL.md +44 -23
  219. package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
  220. package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
  221. package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
  222. package/skills/docgen/js/docgen-ignore.mjs +54 -0
  223. package/skills/docgen/js/docgen-scan.mjs +37 -21
  224. package/skills/llm-patch/SKILL.md +23 -2
  225. package/skills/start-check/SKILL.md +26 -53
  226. package/skills/start-check/js/check.mjs +211 -0
  227. package/skills/taze/SKILL.md +9 -3
  228. package/skills/taze/js/diff.mjs +154 -0
  229. package/types/bin/n-cursor.d.ts +1 -1
  230. package/skills/fix-tests/SKILL.md +0 -119
  231. package/skills/fix-tests/meta.json +0 -1
@@ -0,0 +1,179 @@
1
+ # fix.mjs — точка входу правила `ci4` (library + standalone CLI)
2
+
3
+ ## Огляд
4
+
5
+ Файл `npm/rules/ci4/fix.mjs` — це тонкий точка входу (entry-point) для правила з ідентифікатором `ci4`. Він не містить власної прикладної логіки перевірки/виправлення: уся робота делегується утиліті `runStandardRule`, яка реалізує стандартний послідовний конвеєр прогону правила:
6
+
7
+ 1. `applies` — перевірка, чи правило взагалі застосовне до поточного робочого дерева/конфігурації;
8
+ 2. `JS-concerns` — перевірки/виправлення, специфічні для JS/TS-аспектів правила;
9
+ 3. `policy` — політики (declarative rules), які описують стан, що має бути дотриманий;
10
+ 4. `mdc-refs` — перевірка відповідності правила супровідній `.mdc`-документації (посилання та узгодженість).
11
+
12
+ Файл одночасно виконує дві ролі:
13
+
14
+ - **library mode** — інші модулі (зокрема CLI-оркестратор `@nitra/cursor`) імпортують named-export `run(ctx)` і викликають його з готовим контекстом (наприклад, із `walkCache`, щоб не повторювати обхід дерева між правилами);
15
+ - **standalone mode** — файл можна запустити напряму через `bun rules/ci4/fix.mjs`. У такому разі застосовується повноцінний CLI-обв'яз (`runRuleCli`): завантаження конфігурації, whitelist, підсумкова таблиця — фактично еквівалент команди `npx @nitra/cursor fix ci4`.
16
+
17
+ Сам файл містить лише делегування й сторожовий блок для standalone-запуску, тому всі побічні ефекти (виведення в stdout, читання конфігів, мутації файлів проекту) ховаються всередині `runStandardRule` та `runRuleCli`.
18
+
19
+ ## Експорти / API
20
+
21
+ | Експорт | Тип | Призначення |
22
+ | ------- | ------------------------------------------------- | -------------------------------------------------------------------------------- |
23
+ | `run` | named export, функція `(ctx?) => Promise<number>` | Library-entry для виклику правила `ci4` з оркестратора. Повертає exit-код (0/1). |
24
+
25
+ Default export відсутній. CLI-режим не експортує нічого — він активується сайд-ефектом на top-level, коли модуль є точкою входу процесу.
26
+
27
+ ## Функції
28
+
29
+ ### `run(ctx)`
30
+
31
+ **Сигнатура.**
32
+
33
+ ```js
34
+ export function run(ctx)
35
+ ```
36
+
37
+ **Параметри.**
38
+
39
+ - `ctx` — `RuleContext` (опціональний). Тип посилається на `import('../../scripts/lib/run-standard-rule.mjs').RuleContext`. Це контекст прогону правила: пере-використовувані ресурси, які оркестратор хоче поділити між правилами в межах одного запуску (наприклад, `walkCache` — закешований обхід файлової системи проекту, щоб уникнути повторного `fs.readdir`).
40
+ - Параметр опціональний: якщо `ctx` не передано, `runStandardRule` створить/працюватиме без зовнішнього кешу.
41
+
42
+ **Повертає.**
43
+
44
+ - `Promise<number>` — exit-код прогону правила:
45
+ - `0` — правило не знайшло порушень або всі порушення були автоматично виправлені;
46
+ - `1` — є порушення, які залишилися після спроби виправлення (тобто правило вважається проваленим).
47
+
48
+ **Що робить.**
49
+
50
+ - Викликає `runStandardRule(import.meta.dirname, ctx)`. Перший аргумент `import.meta.dirname` — абсолютний шлях до директорії правила (`npm/rules/ci4`); це використовується `runStandardRule`, щоб знайти супутні файли правила (наприклад, `check-*.mjs`, `mdc`-документацію, ID правила тощо).
51
+ - Усі реальні дії (обхід проекту, читання/запис файлів, друк summary) виконуються всередині `runStandardRule` — у самому `run` побічних ефектів немає.
52
+
53
+ **Side effects.**
54
+
55
+ - Безпосередньо в коді функції — немає. Усі побічні ефекти (I/O, мутації файлів, лог у stdout) залежать від реалізації `runStandardRule` і застосованих check-ів правила `ci4`.
56
+
57
+ ### Top-level standalone-блок (без імені)
58
+
59
+ **Сигнатура (умовно).**
60
+
61
+ ```js
62
+ if (isRunAsCli(import.meta.url)) {
63
+ process.exit(await runRuleCli(import.meta.dirname))
64
+ }
65
+ ```
66
+
67
+ **Що робить.**
68
+
69
+ - `isRunAsCli(import.meta.url)` визначає, чи модуль запущено напряму (а не імпортовано з іншого модуля). Реалізація — у `npm/scripts/lib/run-rule-cli.mjs`.
70
+ - Якщо так — викликає `runRuleCli(import.meta.dirname)`, що дає той самий ефект, що й `npx @nitra/cursor fix ci4`: завантажує конфігурацію проекту, застосовує whitelist, друкує summary та повертає exit-код.
71
+ - Результат передається в `process.exit(...)` — щоб CI/IDE отримали коректний код завершення.
72
+
73
+ **Параметри / повертає.**
74
+
75
+ - Не функція як така — це топ-рівневий `await` із умовою. Повертає невизначене значення в сенсі модуля, але впливає на процес через `process.exit`.
76
+
77
+ **Side effects.**
78
+
79
+ - Завершує Node/Bun-процес із кодом, який повернув `runRuleCli`.
80
+ - Випуск `console.log`/`console.error` усередині `runRuleCli` (за реалізацією).
81
+ - Можливі мутації файлів проекту, якщо правило підтримує auto-fix.
82
+
83
+ **Лінт-винятки в коді.**
84
+
85
+ - Рядок із `process.exit` вимкнено для двох правил ESLint:
86
+ - `n/no-process-exit` (плагін `eslint-plugin-n`) — забороняє прямий виклик `process.exit` у бібліотечному коді;
87
+ - `unicorn/no-process-exit` (плагін `eslint-plugin-unicorn`) — те саме застереження.
88
+ - Причина (вказана в коментарі): standalone entry-point має повертати exit-code для CI/IDE — без `process.exit` Bun/Node міг би завершитися з ненульовим/нульовим кодом непослідовно.
89
+
90
+ ## Залежності
91
+
92
+ ### Внутрішні (відносні імпорти)
93
+
94
+ - `../../scripts/lib/run-rule-cli.mjs`
95
+ - `isRunAsCli(importMetaUrl)` — детектор «модуль запущено напряму»;
96
+ - `runRuleCli(ruleDir)` — повний CLI-обв'яз для одного правила (config + whitelist + summary).
97
+ - `../../scripts/lib/run-standard-rule.mjs`
98
+ - `runStandardRule(ruleDir, ctx?)` — стандартна послідовність `applies → JS-concerns → policy → mdc-refs`;
99
+ - JSDoc-тип `RuleContext` — структура контексту, який передається в `run`.
100
+
101
+ ### Зовнішні
102
+
103
+ - Прямих зовнішніх npm-залежностей немає. Усі залежності — внутрішні модулі пакета `@nitra/cursor` (`npm/scripts/lib/...`).
104
+
105
+ ### Платформа
106
+
107
+ - Очікується середовище Node.js/Bun з підтримкою:
108
+ - ESM-модулів (`import`/`export`, `import.meta.dirname`, `import.meta.url`);
109
+ - top-level `await` (для рядка `await runRuleCli(...)`);
110
+ - `process.exit` (Node/Bun).
111
+ - `import.meta.dirname` потребує сучасних версій Node.js (≥ 20.11 / 21.2) або Bun, де ця властивість підтримується.
112
+
113
+ ## Потік виконання / Використання
114
+
115
+ ### Library mode (виклик з оркестратора)
116
+
117
+ 1. Інший модуль (CLI-агрегатор правил, наприклад, диспетчер `@nitra/cursor fix`) імпортує named-export:
118
+ ```js
119
+ import { run } from '@nitra/cursor/rules/ci4/fix.mjs'
120
+ ```
121
+ 2. Підготовляє спільний контекст `ctx` (наприклад, спільний `walkCache`).
122
+ 3. Викликає:
123
+ ```js
124
+ const exitCode = await run(ctx)
125
+ ```
126
+ 4. `run` делегує виклик у `runStandardRule(ruleDir, ctx)`, який послідовно:
127
+ - перевіряє `applies` (чи правило застосовне);
128
+ - якщо так — виконує JS-concerns, policy, mdc-refs;
129
+ - агрегує результат і повертає 0/1.
130
+ 5. Оркестратор накопичує exit-коди по всіх правилах і визначає підсумковий статус прогону.
131
+
132
+ ### Standalone mode (CLI)
133
+
134
+ 1. Користувач/CI запускає файл напряму:
135
+ ```bash
136
+ bun npm/rules/ci4/fix.mjs
137
+ ```
138
+ 2. `isRunAsCli(import.meta.url)` повертає `true`, оскільки модуль є entry-point процесу.
139
+ 3. Викликається `runRuleCli(import.meta.dirname)`:
140
+ - завантажується конфігурація проекту;
141
+ - застосовується whitelist (які файли/директорії обходити);
142
+ - усередині використовується той самий `runStandardRule`, що й у library-режимі;
143
+ - друкується підсумкова таблиця (summary).
144
+ 4. Результат (0/1) передається в `process.exit(...)` — процес завершується відповідним кодом.
145
+
146
+ Цей режим є повним функціональним еквівалентом команди:
147
+
148
+ ```bash
149
+ npx @nitra/cursor fix ci4
150
+ ```
151
+
152
+ ### Інваріанти й типові помилки
153
+
154
+ - Файл свідомо порожній від прикладної логіки: будь-яка зміна поведінки правила `ci4` має робитися в супутніх файлах (`check-*.mjs`, `applies.mjs`, тощо) — а не тут.
155
+ - Дві ролі (library + standalone) реалізовано в одному файлі навмисно — щоб уникнути дублікатів і щоб CLI-режим завжди узгоджувався з library-режимом.
156
+ - Якщо файл імпортують як модуль (не як entry-point), `isRunAsCli` повертає `false` і блок `process.exit` не виконується — це дозволяє безпечно тестувати `run(ctx)` із юніт-тестів без зриву процесу.
157
+
158
+ ## Rebuild Test
159
+
160
+ Якщо файл відновлювати «з нуля» за цією документацією, мінімальна реалізація має містити:
161
+
162
+ 1. Імпорт `isRunAsCli, runRuleCli` з `../../scripts/lib/run-rule-cli.mjs`.
163
+ 2. Імпорт `runStandardRule` з `../../scripts/lib/run-standard-rule.mjs`.
164
+ 3. Named-export `run(ctx)`, який повертає `runStandardRule(import.meta.dirname, ctx)`.
165
+ 4. JSDoc для `run` з типом `ctx` (`RuleContext` із `run-standard-rule.mjs`) і `@returns {Promise<number>}`.
166
+ 5. Сторожовий блок:
167
+ ```js
168
+ if (isRunAsCli(import.meta.url)) {
169
+ process.exit(await runRuleCli(import.meta.dirname))
170
+ }
171
+ ```
172
+ 6. ESLint-disable коментар над `process.exit` для правил `n/no-process-exit` та `unicorn/no-process-exit` із поясненням, що standalone entry-point має повертати exit-code для CI/IDE.
173
+
174
+ Поведінкові інваріанти, які мають бути збережені:
175
+
176
+ - Імпорт модуля без запуску не повинен викликати `process.exit`.
177
+ - `run()` без аргументів має працювати (опціональний `ctx`).
178
+ - `run(ctx)` повертає саме те значення, яке повернув `runStandardRule`, без додаткової обробки.
179
+ - Standalone-запуск має повертати той самий exit-код, що й `runRuleCli(...)`.
@@ -0,0 +1,128 @@
1
+ # marksman_config.mjs
2
+
3
+ ## Огляд
4
+
5
+ Модуль `marksman_config.mjs` — це концерн правила `ci4` (`ci4.mdc`), що відповідає за матеріалізацію canonical-конфіга для Marksman LSP у корені проєкту. Модуль експортує єдину функцію `check`, яка перевіряє наявність файлу `.marksman.toml` у заданому робочому каталозі (`cwd`) і, якщо файлу немає, копіює туди baseline-варіант, що поставляється з пакета `@nitra/cursor`.
6
+
7
+ Призначення модуля — забезпечити стабільне, передбачуване середовище для Marksman LSP, який використовується для роботи з Markdown-документами в репо (ADR, документація, README). Без явного `.marksman.toml` Marksman використовує свої вбудовані дефолти, які НЕ збігаються з вимогами правила `ci4.mdc`:
8
+
9
+ - `ci4.mdc` вимагає увімкненого GLFM (GitHub-Flavored Markdown) — для підтримки `> [!NOTE]`-alerts, таблиць, `- [ ]` task-lists.
10
+ - `ci4.mdc` вимагає стилю wiki-link-посилань `file-stem` (ADR-slug дорівнює імені файлу без розширення), а дефолт Marksman — `title-slug-ref`.
11
+
12
+ Модуль ідемпотентний: якщо `.marksman.toml` уже існує у `cwd` (навіть із ручними кастомними правками користувача), він НЕ перетирається. Користувач отримає тільки звіт про факт існування, а ручні модифікації зберігаються між прогонами.
13
+
14
+ Файл цілеспрямовано копіюється у `cwd` (а не глибше у дерево), бо Marksman визначає workspace-root за розташуванням свого `.marksman.toml`. Розташування файлу в корені репозиторію дає Marksman можливість бачити одночасно директорії `docs/` та `README.md` усіх workspaces як один загальний workspace.
15
+
16
+ ## Експорти / API
17
+
18
+ Модуль експортує одну named-функцію:
19
+
20
+ - `check(cwd?: string): Promise<number>` — асинхронна перевірка/матеріалізація `.marksman.toml`.
21
+
22
+ Інших публічних експортів немає. Внутрішні константи (`HERE`, `MARKSMAN_BASELINE_PATH`, `MARKSMAN_TARGET_FILENAME`) — module-private і не призначені для прямого використання ззовні.
23
+
24
+ ## Функції
25
+
26
+ ### `check(cwd?)`
27
+
28
+ **Сигнатура:**
29
+
30
+ ```js
31
+ export async function check(cwd = process.cwd()): Promise<number>
32
+ ```
33
+
34
+ **Параметри:**
35
+
36
+ - `cwd` (`string`, опціональний) — абсолютний шлях до кореня проєкту, де має бути матеріалізований `.marksman.toml`. Значення за замовчуванням — `process.cwd()`. Дефолт зроблено для сумісності з прямим викликом із CLI (без передачі аргументів).
37
+
38
+ **Повертає:**
39
+
40
+ - `Promise<number>` — exit-код перевірки, отриманий через `reporter.getExitCode()`:
41
+ - `0` — успіх. Можливі підстани:
42
+ - Файл `.marksman.toml` уже існував у `cwd` (репортується через `reporter.pass(...)`, нічого не змінюється).
43
+ - Файл був успішно створений із canonical baseline (репортується через `reporter.pass(...)` з префіксом «створено з canonical baseline»).
44
+ - `1` — фейл. Підстава: canonical baseline (`data/marksman_config/marksman.baseline.toml`) відсутній всередині самого пакета `@nitra/cursor` — означає, що пакет встановлений некоректно. Користувачу пропонується перевстановити `@nitra/cursor`.
45
+
46
+ **Алгоритм:**
47
+
48
+ 1. Створити instance `reporter` через `createCheckReporter()` із `scripts/lib/check-reporter.mjs`.
49
+ 2. Перевірити, що baseline-файл усередині пакета існує (`existsSync(MARKSMAN_BASELINE_PATH)`).
50
+ - Якщо НЕ існує — викликати `reporter.fail(...)` з повідомленням про зламану установку та повернути exit-код (це буде `1`).
51
+ 3. Сформувати цільовий шлях `target = join(cwd, '.marksman.toml')`.
52
+ 4. Перевірити, чи `target` уже існує (`existsSync(target)`).
53
+ - Якщо існує — викликати `reporter.pass(...)` із повідомленням «`.marksman.toml` існує (<relative-шлях>)» і повернути exit-код (`0`). Файл НЕ перезаписується.
54
+ 5. Якщо файл НЕ існує — скопіювати baseline у `target` через `await copyFile(MARKSMAN_BASELINE_PATH, target)`.
55
+ 6. Викликати `reporter.pass(...)` з повідомленням «`.marksman.toml` створено з canonical baseline (<relative-шлях>) (ci4.mdc)» і повернути exit-код (`0`).
56
+
57
+ **Side effects:**
58
+
59
+ - Читання файлової системи: два виклики `existsSync` (синхронно).
60
+ - Запис у файлову систему: один виклик `copyFile` — створює файл `<cwd>/.marksman.toml`, копіюючи вміст із baseline-файлу пакета. Існуючі файли НЕ перезаписуються.
61
+ - Виклики `reporter.pass(...)` / `reporter.fail(...)` мають свої побічні ефекти (typovo — вивід у stdout/stderr, акумуляція стану звіту в `reporter`). Деталі залежать від реалізації `createCheckReporter`.
62
+
63
+ **Помилки:**
64
+
65
+ - Якщо `copyFile` кине помилку (наприклад, через відсутність прав запису в `cwd` чи фізично заповнений диск) — вона НЕ перехоплюється всередині `check` і прокинеться вище, в caller. Caller відповідає за відлов і репорт.
66
+ - Якщо `cwd` не існує — `copyFile` теж кине ENOENT.
67
+
68
+ ## Залежності
69
+
70
+ ### Імпорти з Node.js стандартної бібліотеки
71
+
72
+ - `existsSync` із `node:fs` — синхронна перевірка існування файлів. Використовується двічі: для baseline і для target.
73
+ - `copyFile` із `node:fs/promises` — асинхронне копіювання файлу.
74
+ - `dirname`, `join`, `relative` із `node:path` — конструювання та форматування шляхів:
75
+ - `dirname` — отримати директорію поточного модуля.
76
+ - `join` — сконструювати абсолютний шлях до baseline та цільового файлу.
77
+ - `relative` — обчислити шлях відносно `cwd` для людиночитного логування.
78
+ - `fileURLToPath` із `node:url` — конвертувати `import.meta.url` у файловий шлях (ESM-патерн отримання `__dirname`).
79
+
80
+ ### Внутрішні залежності проєкту
81
+
82
+ - `../../../scripts/lib/check-reporter.mjs` — модуль, що експортує `createCheckReporter`. Реалізує уніфікований інтерфейс репорту для concern-функцій правил: методи `pass(message)`, `fail(message)` і `getExitCode()`. Усі concern-модулі правил `ci4` (та інші правила) використовують цей репортер для єдиного UX.
83
+
84
+ ### Файлові залежності (data assets)
85
+
86
+ - `./data/marksman_config/marksman.baseline.toml` — canonical-baseline TOML-конфіг Marksman LSP, що поставляється разом із пакетом `@nitra/cursor`. Цей файл — джерело правди для дефолтного `.marksman.toml`. Якщо файл відсутній — `check` поверне `1`.
87
+
88
+ ## Потік виконання / Використання
89
+
90
+ ### Типовий контекст виклику
91
+
92
+ Модуль викликається фреймворком правил `ci4.mdc` як один із concern-чеків (поряд із іншими файлами у `npm/rules/ci4/js/`). Кожен concern перевіряє один аспект конфігурації проєкту відповідно до правила `ci4.mdc`. `marksman_config` відповідає за аспект «Marksman LSP має консистентну конфігурацію».
93
+
94
+ ### Інтеграція
95
+
96
+ 1. Зовнішній runner (зазвичай CLI `n-cursor` або агрегатор правил) імпортує `check` і викликає його:
97
+ ```js
98
+ import { check } from '@nitra/cursor/npm/rules/ci4/js/marksman_config.mjs'
99
+ const code = await check(process.cwd())
100
+ process.exit(code)
101
+ ```
102
+ 2. `check` повертає exit-код:
103
+ - `0` — concern пройдений, можна продовжувати.
104
+ - `1` — fatal, треба зупинитися або репортувати юзеру.
105
+
106
+ ### Сценарії
107
+
108
+ - **Свіжий клон репо без `.marksman.toml`**: на першому прогоні правила `ci4` файл буде створений із baseline. Подальші прогони побачать існуючий файл і не змінюватимуть його.
109
+ - **Користувач уже має кастомний `.marksman.toml`**: модуль НЕ перетирає його. Користувач може налаштувати Marksman під свої потреби (наприклад, додати specific completion-теги), і ця конфігурація переживе всі майбутні запуски `ci4`.
110
+ - **Зламана установка `@nitra/cursor` (baseline відсутній)**: модуль миттєво повертає `1` із пояснювальним повідомленням про необхідність перевстановити пакет. Жодних змін у `cwd` не вноситься.
111
+
112
+ ### Ідемпотентність і безпека
113
+
114
+ - Функція ідемпотентна: повторні виклики на тому самому `cwd` дають однаковий результат — або `0` із пояснювальним «існує», або `0` із «створено» лише на першому прогоні.
115
+ - Безпека для користувацьких правок: модуль НЕ змінює існуючий `.marksman.toml`, навіть якщо його вміст застарів чи розходиться з baseline. Це свідома trade-off на користь юзерських кастомізацій. Якщо потрібно примусове оновлення baseline — користувач має видалити файл вручну.
116
+
117
+ ## Rebuild Test
118
+
119
+ Перевірка повноти документації — чи можна за цим описом відтворити поведінку модуля з нуля без перегляду коду:
120
+
121
+ - Public API: `check(cwd?)` — задокументовано (сигнатура, дефолт, повернення, exit-коди).
122
+ - Алгоритм: 6 кроків (reporter → перевірка baseline → формування target → перевірка target → copy → repоrt) — задокументовано.
123
+ - Залежності: всі імпорти (`node:fs`, `node:fs/promises`, `node:path`, `node:url`, `check-reporter.mjs`, baseline TOML) — перелічено з призначенням.
124
+ - Side effects: I/O (read+write), вивід у репортер — описано.
125
+ - Граничні випадки: відсутній baseline (exit 1), існуючий target (no-op, exit 0), створення нового файлу (exit 0), помилки copyFile (прокидаються нагору) — описано.
126
+ - Контекст і мотивація: чому файл у `cwd`, чому НЕ перезаписується, чому потрібен явний конфіг (GLFM, file-stem) — описано.
127
+
128
+ Реконструкція коду на основі цього документа повинна давати функціонально еквівалентний модуль.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Dockerfile — lint-docker / hadolint; перевірка check-docker
3
- version: '1.11'
3
+ version: '1.12'
4
4
  globs: "**/Dockerfile*"
5
5
  alwaysApply: false
6
6
  ---
@@ -13,7 +13,7 @@ alwaysApply: false
13
13
 
14
14
  - **backend (типово)**: `mirror.gcr.io/library/alpine:*`
15
15
  - **ультра-легкі (glibc / одна статична збірка)**: `scratch` — тільки як `FROM scratch` (офіційний порожній базовий шар), коли весь **runtime** уже в `COPY --from=…`
16
- - **glibc, Debian (slim)**: `mirror.gcr.io/library/debian:*` **лише** з тегом, у якому є `slim` (наприклад `bookworm-slim`, `trixie-slim`), а не `bookworm` без `slim`
16
+ - **glibc, Debian (slim)**: `mirror.gcr.io/library/debian:*` **лише** з тегом, у якому є `slim` (наприклад `bookworm-slim`, `trixie-slim`), а не `bookworm` без `slim`. Debian-slim виправданий **лише** коли потрібен саме glibc-рантайм (нативні glibc-залежності, prebuilds без musl-варіанта). **Non-root сам по собі не є підставою** переходити сюди: `mirror.gcr.io/oven/bun:alpine` уже має користувача `bun` (uid/gid 1000), тож `USER bun` дає non-root **без зміни бази**. Перехід Alpine→Debian заради лише non-root — **антипатерн** (більший образ + зайва musl→glibc міграція bun-бінарника)
17
17
  - **виняток (інтерпретовані стеки)**: `mirror.gcr.io/library/php:*` або `mirror.gcr.io/library/python:*` — якщо сервіс має крутитися в офіційному runtime PHP чи Python, а не як один бінарник на Alpine; інакше лишай **alpine** у фінальному stage
18
18
  - **frontend**: `mirror.gcr.io/nginxinc/nginx-unprivileged:*` або `mirror.gcr.io/openresty/openresty:*`
19
19
 
@@ -102,7 +102,12 @@ CMD ["bun", "src/index.js"]
102
102
 
103
103
  ## не превілейований образ
104
104
 
105
- Для всих образів потрібно щоб використовувся non root принцип, наприклад:
105
+ Для всіх образів потрібно щоб використовувся non-root принцип. **Спосіб** досягнення non-root залежить від **бази**, а не від зміни ОС — змінювати дистрибутив (Alpine→Debian) заради лише non-root **не треба**. Два шляхи:
106
+
107
+ - **standalone-бінарник на `alpine:latest`** (секція «компіляція») — у `alpine` немає готового non-root користувача, тож його створюємо явно: `addgroup -g 1000 app && adduser -D -u 1000 -G app app` + `COPY --chown=app:app` + `USER app` (приклад нижче);
108
+ - **ship `node_modules` на `mirror.gcr.io/oven/bun:alpine`** (секція «виняток: нативний `.node`-аддон») — користувач `bun` (uid/gid 1000) **уже в базі**, тож достатньо `COPY --chown=bun:bun …` + `USER bun`; базу на Debian-slim міняти **не треба** — це той самий антипатерн, що й у переліку фінальних образів вище.
109
+
110
+ Приклад для standalone-бінарника на alpine:
106
111
 
107
112
  ```dockerfile
108
113
  # Stage 1
@@ -0,0 +1,171 @@
1
+ # fix.mjs — entry-point правила `docker` (fix-режим)
2
+
3
+ ## Огляд
4
+
5
+ Файл `npm/rules/docker/fix.mjs` — це тонкий **entry-point** правила `docker` у пакеті `@nitra/cursor` для режиму `fix` (автоматичне виправлення порушень). Він не містить власної логіки перевірки чи виправлень: уся реальна робота делегується у спільні бібліотечні функції з `npm/scripts/lib/`.
6
+
7
+ Файл виконує **дві ролі** одночасно (патерн dual-role module, прийнятий в усіх `rules/<id>/fix.mjs` цього репозиторію):
8
+
9
+ 1. **Library role** — експортує іменовану функцію `run(ctx)`, яку викликає CLI-оркестратор `@nitra/cursor` (`bin/cursor.mjs` → `cmd-fix.mjs` / `cmd-fix-all.mjs`). У цьому режимі правило вбудовується в загальний прогін «всіх правил» зі спільним кешем обходу файлів (`walkCache`), агрегованим summary та глобальним whitelist.
10
+ 2. **Standalone role** — якщо файл запущений напряму (`bun npm/rules/docker/fix.mjs` або через `import.meta.url` як головний модуль), він виконується як автономний CLI-ентрі: завантажує конфіг, застосовує whitelist, друкує summary та повертає exit-code, повністю еквівалентний `npx @nitra/cursor fix docker`.
11
+
12
+ Сам алгоритм виправлень розкладений у сусідніх теках цього правила (`js/`, `lint/`, `policy/`) і запускається уніфіковано через `runStandardRule`, яке прокручує чотири стандартні фази:
13
+
14
+ 1. **applies** — перевірка, чи правило взагалі релевантне для поточного workspace.
15
+ 2. **JS-concerns** — JS-специфічні fix-перевірки (з теки `js/`).
16
+ 3. **policy** — політики/policy-перевірки правила.
17
+ 4. **mdc-refs** — узгодження посилань у файлі `docker.mdc` (опис правила для Cursor).
18
+
19
+ ID правила (`docker`) визначається автоматично з імені директорії-носія через `import.meta.dirname` — у `fix.mjs` немає жодного хардкоду ID.
20
+
21
+ ## Експорти / API
22
+
23
+ | Експорт | Тип | Призначення |
24
+ | ----------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
25
+ | `run(ctx?)` | named export, `function` | Library-API: запустити стандартний fix-pipeline правила `docker`. Повертає `Promise<number>` з exit-кодом (`0` — OK, `1` — порушення/помилка). |
26
+
27
+ Інших іменованих або default-експортів файл не має.
28
+
29
+ Окрім експорту, модуль містить **top-level side-effect-блок** (поза `run`), який спрацьовує лише коли файл є точкою входу процесу — див. секцію _Потік виконання_.
30
+
31
+ ### Сигнатура `run`
32
+
33
+ ```js
34
+ export function run(ctx)
35
+ ```
36
+
37
+ - **Параметри:**
38
+ - `ctx` _(optional)_ — об’єкт типу `RuleContext` (з `../../scripts/lib/run-standard-rule.mjs`). Передається оркестратором, коли правило запускають у складі загального прогону «всіх правил». Може містити, зокрема, спільний `walkCache` (для уникнення повторного `walk`/`glob` файлів кожним правилом), накопичений summary, прапори дебагу тощо.
39
+ - **Повертає:** `Promise<number>` — exit-код прогону: `0` коли правило не знайшло порушень або успішно їх виправило, `1` коли лишились незавершені порушення чи виникла помилка.
40
+ - **Side effects:** делегуються у `runStandardRule` — це може бути запис у файли проєкту (auto-fix), читання конфіга/файлів, запис у stdout/stderr (summary, прогрес-логи), а також модифікація переданого `ctx` (наприклад, заповнення `walkCache`).
41
+
42
+ ## Функції
43
+
44
+ ### `run(ctx)`
45
+
46
+ **Сигнатура:** `export function run(ctx) → Promise<number>`
47
+
48
+ **Призначення:** library-API правила `docker` для CLI-оркестратора `@nitra/cursor`. Виконує повний fix-pipeline правила: `applies → JS-concerns → policy → mdc-refs`.
49
+
50
+ **Параметри:**
51
+
52
+ - `ctx` — _(optional, об’єкт)_ контекст прогону, який передає зовнішній оркестратор. Тип описаний у JSDoc-import-посиланні `import('../../scripts/lib/run-standard-rule.mjs').RuleContext`. Може містити:
53
+ - `walkCache` — спільний кеш обходу/glob файлів, щоб правила не дублювали I/O;
54
+ - інші службові поля (summary-агрегатор, прапори, конфіг), залежно від реалізації `run-standard-rule.mjs`.
55
+ - Якщо викликати без аргументу, `runStandardRule` створить свій локальний контекст за замовчуванням.
56
+
57
+ **Повертає:** `Promise<number>` — exit-код. За конвенцією репозиторію: `0` — порушень не знайдено / усе виправлено, `1` — лишились порушення або сталася помилка.
58
+
59
+ **Side effects:** не виконує жодних побічних ефектів безпосередньо — лише повертає результат виклику `runStandardRule(import.meta.dirname, ctx)`. Усе I/O й auto-fix належить `runStandardRule` (читання файлів проєкту, потенційні модифікації файлів, запис у stdout, мутація `ctx.walkCache`).
60
+
61
+ **Ключова деталь:** першим аргументом у `runStandardRule` передається `import.meta.dirname` — абсолютний шлях до теки, в якій лежить цей `fix.mjs`. Звідти `runStandardRule` витягує ID правила (`docker` — остання частина шляху) і завантажує підкомпоненти з `js/`, `lint/`, `policy/`. Завдяки цьому `fix.mjs` цілком універсальний і не містить літерала `"docker"`.
62
+
63
+ ## Залежності
64
+
65
+ ### Внутрішні (з самого пакета `@nitra/cursor`)
66
+
67
+ | Імпорт | Звідки | Призначення |
68
+ | ----------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
69
+ | `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Детектор «чи запущено цей файл як головний модуль процесу (CLI)». Зазвичай порівнює `import.meta.url` з `pathToFileURL(process.argv[1])`. |
70
+ | `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Standalone-обгортка: завантаження конфігу, застосування whitelist, друк summary, повернення exit-коду. Еквівалент `npx @nitra/cursor fix <id>`. |
71
+ | `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Універсальний fix-pipeline правила: послідовно прогоняє фази `applies → JS-concerns → policy → mdc-refs`, читаючи реалізації з підтек правила. |
72
+
73
+ Розв’язання шляхів:
74
+
75
+ - `../../scripts/lib/...` від `npm/rules/docker/fix.mjs` → `npm/scripts/lib/run-rule-cli.mjs` і `npm/scripts/lib/run-standard-rule.mjs`.
76
+
77
+ ### Зовнішні
78
+
79
+ Власних `node:`/npm-залежностей файл не імпортує. Опосередковано (через `runStandardRule` і `runRuleCli`) використовуються стандартні модулі для роботи з FS/процесом, але до цього файлу це не належить.
80
+
81
+ ### Структурні залежності (runtime-конвенції)
82
+
83
+ Хоч `fix.mjs` нічого з них не імпортує статично, для коректної роботи правила в директорії `npm/rules/docker/` мають існувати:
84
+
85
+ - `meta.json` — метадані правила (ID, опис, applies-умови);
86
+ - `docker.mdc` — Cursor rule-документ, на який звіряються mdc-refs;
87
+ - `js/` — JS-специфічні fix-чеки правила (фаза _JS-concerns_);
88
+ - `lint/` — lint-перевірки (читаються відповідним check-режимом);
89
+ - `policy/` — policy-перевірки (фаза _policy_).
90
+
91
+ `runStandardRule` сам шукає й підвантажує ці компоненти за `import.meta.dirname`.
92
+
93
+ ## Потік виконання / Використання
94
+
95
+ ### Сценарій 1 — Library mode (виклик з оркестратора)
96
+
97
+ Так правило запускає `@nitra/cursor` під час команд типу `npx @nitra/cursor fix` (усі правила) або `npx @nitra/cursor fix docker` (одне правило, але через диспетчер):
98
+
99
+ 1. Оркестратор робить динамічний `import('npm/rules/docker/fix.mjs')`.
100
+ 2. Отримує функцію `run` з експортів.
101
+ 3. Підготовлює спільний `ctx` (наприклад, з `walkCache`) і викликає `await run(ctx)`.
102
+ 4. `run` повертає управління у `runStandardRule(import.meta.dirname, ctx)`, який:
103
+ - визначає ID правила як `basename(import.meta.dirname)` = `docker`;
104
+ - читає `meta.json` правила;
105
+ - виконує фазу `applies` — пропускає правило, якщо воно нерелевантне;
106
+ - запускає JS-concerns → policy → mdc-refs;
107
+ - повертає exit-код (`0`/`1`).
108
+ 5. Оркестратор додає результат до загального summary й переходить до наступного правила.
109
+
110
+ Гілка `if (isRunAsCli(...))` у цьому сценарії **не виконується**, бо файл імпортовано як модуль, а не запущено напряму.
111
+
112
+ ### Сценарій 2 — Standalone mode (прямий запуск)
113
+
114
+ Використовується розробником або IDE/CI-кроком напряму, без проходу через головний `bin/cursor.mjs`:
115
+
116
+ ```bash
117
+ bun npm/rules/docker/fix.mjs
118
+ # або
119
+ node npm/rules/docker/fix.mjs
120
+ ```
121
+
122
+ 1. Node/Bun виконує файл як модуль-точку входу.
123
+ 2. Імпорти на верхньому рівні підтягують `isRunAsCli`, `runRuleCli`, `runStandardRule`.
124
+ 3. Експорт `run` реєструється (на випадок, якщо хтось одночасно імпортує цей файл).
125
+ 4. Виконується top-level блок:
126
+
127
+ ```js
128
+ if (isRunAsCli(import.meta.url)) {
129
+ process.exit(await runRuleCli(import.meta.dirname))
130
+ }
131
+ ```
132
+
133
+ - `isRunAsCli(import.meta.url)` повертає `true`, бо файл є головним модулем процесу.
134
+ - `await runRuleCli(import.meta.dirname)` виконує повноцінний standalone-pipeline: завантаження конфіга, whitelist, виклик внутрішнього аналогу `run` (тобто `runStandardRule`), друк summary.
135
+ - `process.exit(<code>)` завершує процес із отриманим exit-кодом, щоб CI / IDE могли правильно інтерпретувати успіх/помилку.
136
+
137
+ 5. Коментарі в коді явно фіксують виняток ESLint: `n/no-process-exit` і `unicorn/no-process-exit` свідомо вимкнено саме для цього рядка, бо standalone-ентрі **зобов’язаний** повертати exit-код процесу.
138
+
139
+ ### Інваріанти / контракти
140
+
141
+ - ID правила завжди дорівнює імені теки, де лежить `fix.mjs` (тут — `docker`). Хардкод відсутній — переміщення в іншу теку автоматично змінить ID, тому файл не можна копіювати без перейменування теки.
142
+ - `run` має лишатися **named export** `run` (не default) — оркестратор шукає саме це ім’я.
143
+ - Top-level `await runRuleCli(...)` працює завдяки тому, що файл є ES-модулем (`.mjs`) із підтримкою top-level await.
144
+ - Гілка `isRunAsCli` повинна лишатися **єдиним** side-effect блоком на верхньому рівні; будь-яка інша робота на модульному рівні зламає library mode.
145
+
146
+ ## Rebuild Test
147
+
148
+ З цієї документації можна реконструювати файл-еквівалент:
149
+
150
+ ```js
151
+ import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
152
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
153
+
154
+ /**
155
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs.
156
+ * Library mode: викликається CLI orchestration через `import + run(ctx)`.
157
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx]
158
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
159
+ */
160
+ export function run(ctx) {
161
+ return runStandardRule(import.meta.dirname, ctx)
162
+ }
163
+
164
+ if (isRunAsCli(import.meta.url)) {
165
+ // Standalone-ентрі: повний еквівалент `npx @nitra/cursor fix <id>`.
166
+ // eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit
167
+ process.exit(await runRuleCli(import.meta.dirname))
168
+ }
169
+ ```
170
+
171
+ Семантично та функціонально такий код тотожний оригіналу: дві ролі модуля (library `run` + standalone main), делегування у `runStandardRule`/`runRuleCli`, ID правила визначається через `import.meta.dirname`, exit-код процесу повертається лише у standalone-режимі.