@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,163 @@
1
+ # fix.mjs — entry-point правила `python`
2
+
3
+ ## Огляд
4
+
5
+ Файл `npm/rules/python/fix.mjs` — це **точка входу** для правила з ідентифікатором `python` пакету `@nitra/cursor`. Він виконує дві ролі одночасно:
6
+
7
+ 1. **Library mode** — експортує функцію `run(ctx)`, яку CLI-оркестратор `npx @nitra/cursor fix` або інші правила можуть викликати програмно через `import { run } from './fix.mjs'`.
8
+ 2. **Standalone mode** — коли файл запускається безпосередньо (`bun npm/rules/python/fix.mjs`), він самостійно ініціалізує CLI-шар (завантаження конфіга, whitelist цілей, summary-звіт) і завершує процес із коректним exit-code для CI/IDE.
9
+
10
+ Сам файл нічого не перевіряє і не править — він лише делегує всю роботу стандартному раннеру `runStandardRule`, який за угодою обходить підпапки правила (`applies`, `js`, `policy`, `mdc-refs` тощо) у фіксованому порядку. Конкретні перевірки правила `python` живуть у сусідніх теках (`./js/`, `./lint/`, `./policy/`) і у файлі-описі `./python.mdc`.
11
+
12
+ Файл є мінімальним shim-ом і свідомо тримається коротким — уся логіка винесена в спільні бібліотеки `scripts/lib/`, щоб усі правила пакету мали однаковий контракт запуску.
13
+
14
+ ## Експорти / API
15
+
16
+ | Експорт | Тип | Призначення |
17
+ | ------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
18
+ | `run` | named export, `function` | Library API правила `python`. Викликається CLI-оркестратором або іншим кодом для прогону правила в межах вже існуючого контексту. |
19
+
20
+ Default-експорту немає. Експорт `run` має сталу сигнатуру через JSDoc-тип `RuleContext`, імпортований із `../../scripts/lib/run-standard-rule.mjs`.
21
+
22
+ Окрім експорту, файл містить **side-effect блок** для standalone-режиму — він не експортується, але виконується при безпосередньому запуску модуля.
23
+
24
+ ## Функції
25
+
26
+ ### `run(ctx)`
27
+
28
+ Public-функція правила, делегатор до `runStandardRule`.
29
+
30
+ **Сигнатура**
31
+
32
+ ```js
33
+ export function run(ctx) {
34
+ return runStandardRule(import.meta.dirname, ctx)
35
+ }
36
+ ```
37
+
38
+ **Параметри**
39
+
40
+ - `ctx` _(optional)_ — об'єкт типу `RuleContext` (визначений у `../../scripts/lib/run-standard-rule.mjs`). Несе спільний для одного прогону стан: зокрема `walkCache` (закешований обхід дерева файлів, щоб не пере-сканувати робочу копію між правилами) та інші поля, які додає оркестратор. Якщо параметр опущений — `runStandardRule` створить дефолтний контекст самостійно.
41
+
42
+ **Повертає**
43
+
44
+ - `Promise<number>` — exit-code прогону:
45
+ - `0` — порушень не знайдено (правило пройшло),
46
+ - `1` — знайдено хоча б одне порушення.
47
+
48
+ Хоча тіло функції `return runStandardRule(...)` синхронне, сам `runStandardRule` повертає `Promise`, тому викликач має `await`-ити результат.
49
+
50
+ **Side effects**
51
+
52
+ Безпосередньо у `run` побічних ефектів немає — усі вони виконуються всередині `runStandardRule`:
53
+
54
+ - читання файлів проекту (через `walkCache`/файлову систему),
55
+ - запуск підправил (applies/JS-concerns/policy/mdc-refs),
56
+ - запис у `stdout`/`stderr` діагностики (summary вмикається лише в standalone-режимі через `runRuleCli`).
57
+
58
+ Сам `run` **не викликає** `process.exit` — це обов'язок зовнішнього оркестратора. Це принципово, бо `run` має бути безпечним для виклику з іншого правила або тесту.
59
+
60
+ ### Standalone-блок (anonymous side-effect)
61
+
62
+ ```js
63
+ if (isRunAsCli(import.meta.url)) {
64
+ // eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit
65
+ process.exit(await runRuleCli(import.meta.dirname))
66
+ }
67
+ ```
68
+
69
+ Не функція, а top-level async-блок (використовує `await` на рівні модуля, що валідно для ES-модулів).
70
+
71
+ **Як працює**
72
+
73
+ 1. `isRunAsCli(import.meta.url)` — повертає `true`, тільки якщо модуль є entry-point процесу (`bun rules/python/fix.mjs`, а не імпорт). При імпорті з іншого модуля гілка не виконується.
74
+ 2. `runRuleCli(import.meta.dirname)` — повний CLI-цикл одного правила: завантажує конфіг проекту, формує whitelist цілей, прогоняє `run` (через `runStandardRule`), друкує summary.
75
+ 3. `process.exit(<exit-code>)` — терміново завершує процес кодом, який повернув `runRuleCli`. Дві ESLint-директиви (`n/no-process-exit`, `unicorn/no-process-exit`) явно дозволяють виклик `process.exit` саме тут, бо standalone entry-point повинен повернути код процесу для CI/IDE-інтеграції.
76
+
77
+ **Side effects**
78
+
79
+ - Завершує Node/Bun-процес (`process.exit`).
80
+ - Усе, що робить `runRuleCli` (читання конфіга, файлові обходи, лог summary).
81
+
82
+ ## Залежності
83
+
84
+ Усі залежності — внутрішні модулі того ж пакету (`@nitra/cursor`):
85
+
86
+ | Модуль | Імпортовані символи | Роль |
87
+ | ----------------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
88
+ | `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` | Детектор standalone-запуску й повний CLI-цикл одного правила (config + whitelist + summary). |
89
+ | `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule` | Стандартний раннер правила: обходить підпапки `applies → js → policy → mdc-refs` у фіксованому порядку й агрегує exit-code. Також експортує тип `RuleContext` (використовується в JSDoc). |
90
+
91
+ Зовнішніх npm-залежностей у файлі немає. Файл покладається лише на ES-модульні runtime-API:
92
+
93
+ - `import.meta.dirname` — абсолютний шлях до теки модуля (`.../npm/rules/python/`). Передається як «корінь правила» в обидва раннери, щоб вони знали, де шукати підпапки.
94
+ - `import.meta.url` — URL модуля (`file://...`), потрібен `isRunAsCli`, щоб порівняти з `process.argv[1]`.
95
+ - `process.exit` — глобальний Node/Bun API.
96
+ - Top-level `await` — стандарт ESM.
97
+
98
+ ## Потік виконання / Використання
99
+
100
+ ### Сценарій 1. Виклик з оркестратора (library mode)
101
+
102
+ Коли користувач запускає `npx @nitra/cursor fix python` (або просто `npx @nitra/cursor fix`, де `python` потрапляє у whitelist), CLI-оркестратор пакету:
103
+
104
+ 1. Імпортує `run` із цього файлу: `const { run } = await import('.../npm/rules/python/fix.mjs')`.
105
+ 2. Створює спільний `RuleContext` (зокрема `walkCache`).
106
+ 3. Викликає `await run(ctx)` і збирає exit-code.
107
+
108
+ У цьому сценарії `if (isRunAsCli(...))` повертає `false`, бо модуль імпортовано — standalone-блок не виконується, `process.exit` не викликається.
109
+
110
+ ### Сценарій 2. Прямий запуск (standalone mode)
111
+
112
+ ```bash
113
+ bun npm/rules/python/fix.mjs
114
+ # або еквівалент:
115
+ npx @nitra/cursor fix python
116
+ ```
117
+
118
+ 1. Модуль виконується як entry-point — `isRunAsCli(import.meta.url)` → `true`.
119
+ 2. Викликається `runRuleCli(import.meta.dirname)`:
120
+ - завантажує конфіг проекту,
121
+ - формує whitelist цілей,
122
+ - усередині сам викликає `run(ctx)` (через `runStandardRule`),
123
+ - друкує summary-звіт.
124
+ 3. `process.exit` із отриманим exit-code (`0` або `1`) — придатно для CI (`bun npm/rules/python/fix.mjs && echo OK`).
125
+
126
+ ### Внутрішній порядок підправил
127
+
128
+ Згідно з JSDoc до `run`, `runStandardRule` запускає послідовність:
129
+
130
+ 1. **applies** — фільтр «чи правило взагалі застосовне до проекту» (читає метадані `meta.json` / контекст).
131
+ 2. **JS-concerns** — JS-перевірки (у цьому правилі живуть у `./js/`).
132
+ 3. **policy** — політики (у цьому правилі — `./policy/`).
133
+ 4. **mdc-refs** — перевірка посилань усередині `python.mdc`.
134
+
135
+ Будь-яке порушення на будь-якому етапі підвищує exit-code до `1`, але всі етапи прогоняються — щоб користувач бачив повний список порушень за один прохід, а не виправляв їх по одному.
136
+
137
+ ### Чому дві ролі в одному файлі
138
+
139
+ Це угода всього пакету `@nitra/cursor`: кожне правило має один `fix.mjs`, який:
140
+
141
+ - легко імпортувати з іншого правила/коду (named export `run`),
142
+ - легко запустити вручну для діагностики конкретного правила (`bun rules/<id>/fix.mjs`),
143
+ - однаково поводиться під CLI-оркестратором і в standalone.
144
+
145
+ Сусідні правила (наприклад, `npm/rules/n-bun/fix.mjs`, `npm/rules/n-adr/fix.mjs`) мають той самий шаблон — це дає змогу мати один універсальний loader і не дублювати CLI-код у кожному правилі.
146
+
147
+ ## Rebuild Test
148
+
149
+ Контрольний перелік, за яким можна відтворити файл «з нуля», маючи лише цю документацію:
150
+
151
+ 1. Файл — ES-модуль (`.mjs`), без default-експорту, із одним named export `run`.
152
+ 2. Імпорти (у такому порядку):
153
+ - `isRunAsCli` та `runRuleCli` з `../../scripts/lib/run-rule-cli.mjs`,
154
+ - `runStandardRule` з `../../scripts/lib/run-standard-rule.mjs`.
155
+ 3. Функція `run(ctx)`:
156
+ - `export function run(ctx)`,
157
+ - тіло: `return runStandardRule(import.meta.dirname, ctx)`,
158
+ - JSDoc із описом «applies → JS-concerns → policy → mdc-refs», згадкою про library mode, `@param {RuleContext} [ctx]` і `@returns {Promise<number>}` (0 — OK, 1 — порушення).
159
+ 4. Standalone-блок:
160
+ - `if (isRunAsCli(import.meta.url)) { ... }`,
161
+ - усередині: `process.exit(await runRuleCli(import.meta.dirname))`,
162
+ - над `process.exit` — коментар-пояснення дволикої ролі fix.mjs та `eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit` із причиною.
163
+ 5. Файл не містить жодної додаткової логіки — усі перевірки правила живуть у сусідніх теках (`./js/`, `./lint/`, `./policy/`) та `./python.mdc`.
@@ -0,0 +1,108 @@
1
+ # `applies.mjs` — applies-гейт правила `python`
2
+
3
+ ## Огляд
4
+
5
+ Модуль `applies.mjs` реалізує **applies-гейт** для правила `python` (`python.mdc`) у системі правил `n-cursor`. Його єдина роль — відповісти на питання: «чи варто застосовувати правило `python.mdc` до поточного репозиторію?».
6
+
7
+ Критерій застосовності — наявність файлу `pyproject.toml` у корені репозиторію. Якщо файл відсутній, CLI `n-cursor` пропускає всі concerns цього правила (як JS-перевірки, так і policy-перевірки Rego). Це усуває хибне спрацювання rego-правила `python/package_json`, яке вимагало б наявність npm-скрипта `scripts.lint-python` навіть у репозиторіях, що не пов'язані з Python.
8
+
9
+ Сам JS-файл не виконує жодних інших FS-перевірок: усі подальші перевірки (`uv.lock`, `package.json`, `lint-python.yml`, заборона Poetry тощо) розміщені окремо у `tooling.mjs`. Окрім гейта `applies()`, файл містить тонкий стаб `check()`, який друкує context-pass через стандартний reporter.
10
+
11
+ ## Експорти / API
12
+
13
+ Модуль експортує дві named-функції (ESM):
14
+
15
+ | Експорт | Тип / сигнатура | Призначення |
16
+ | --------- | ------------------------------------ | --------------------------------------------------------------------- |
17
+ | `applies` | `(cwd?: string) => Promise<boolean>` | Гейт застосовності правила: `true`, якщо в корені є `pyproject.toml`. |
18
+ | `check` | `() => number` | Друкує context-pass через `check-reporter`, повертає exit-code (`0`). |
19
+
20
+ Default-export відсутній.
21
+
22
+ ## Функції
23
+
24
+ ### `applies(cwd = process.cwd())`
25
+
26
+ Гейт застосовності правила `python.mdc` для конкретного репозиторію.
27
+
28
+ - **Сигнатура:** `export function applies(cwd?: string): Promise<boolean>`
29
+ - **Параметри:**
30
+ - `cwd` _(string, опційний)_ — абсолютний (або відносний) шлях до кореня репозиторію, у якому виконується перевірка. За замовчуванням — результат `process.cwd()`. У звичайному прогоні CLI це робочий каталог, з якого запущено команду.
31
+ - **Повертає:** `Promise<boolean>`
32
+ - `true` — у `cwd` існує файл `pyproject.toml`, правило `python.mdc` слід застосовувати (CLI запустить решту concerns).
33
+ - `false` — `pyproject.toml` відсутній, правило треба повністю пропустити.
34
+ - **Алгоритм:**
35
+ 1. Будує абсолютний шлях `join(cwd, 'pyproject.toml')`.
36
+ 2. Викликає синхронну `existsSync()` з `node:fs`.
37
+ 3. Загортає синхронний boolean у `Promise.resolve(...)`, щоб відповідати очікуваному CLI-контракту `Promise<boolean>`.
38
+ - **Side effects:** немає — лише читання метаданих файлової системи (`stat`-like виклик). Файл не відкривається й не читається; запис не виконується; глобальний стан не змінюється.
39
+ - **Помилки:** функція не очікує винятків; `existsSync` сам по собі не кидає для відсутніх шляхів — повертає `false`. Промісована обгортка зберігає таку ж поведінку.
40
+
41
+ ### `check()`
42
+
43
+ Тонкий стаб, що друкує єдиний context-pass і завершується успіхом. Фактичні порушення (на кшталт відсутності `lint-python`-job у CI, заборони Poetry тощо) перевіряють інші concerns правила, тож тут жодних реальних перевірок немає.
44
+
45
+ - **Сигнатура:** `export function check(): number`
46
+ - **Параметри:** немає.
47
+ - **Повертає:** `number` — exit-code, отриманий з `reporter.getExitCode()`. Оскільки викликається лише `reporter.pass(...)`, код завжди `0` (немає фейлів).
48
+ - **Алгоритм:**
49
+ 1. Створює інстанс reporter-а через `createCheckReporter()`.
50
+ 2. Викликає `reporter.pass('pyproject.toml знайдено в корені — застосовую python.mdc')`, що друкує позитивний рядок у стандартному форматі n-cursor reporter-а.
51
+ 3. Повертає `reporter.getExitCode()`.
52
+ - **Side effects:** пише в `stdout` (через reporter). Не виконує FS-операцій, не змінює стану процесу, не кидає винятків.
53
+ - **Передумова:** функцію має сенс викликати лише після того, як `applies()` повернула `true` (інакше повідомлення про «знайдено» було б оманливим). CLI `n-cursor` забезпечує цей контракт.
54
+
55
+ ## Залежності
56
+
57
+ ### Зовнішні (стандартна бібліотека Node.js)
58
+
59
+ - `node:fs`
60
+ - `existsSync(path)` — синхронна перевірка існування шляху. Використовується для виявлення `pyproject.toml`.
61
+ - `node:path`
62
+ - `join(...segments)` — нормалізована побудова шляху, незалежна від OS-роздільника.
63
+
64
+ ### Внутрішні (репозиторій)
65
+
66
+ - `../../../scripts/lib/check-reporter.mjs`
67
+ - `createCheckReporter()` — фабрика стандартного reporter-а для `check.mjs`-скриптів правил. Очікувані методи: `pass(message)` (друк позитивного рядка) та `getExitCode()` (повертає накопичений exit-код, `0` для успіху).
68
+ - Відносний шлях вказує на спільну бібліотеку скриптів правил у тому ж `npm/`-пакеті (`npm/scripts/lib/check-reporter.mjs` відносно файлу `npm/rules/python/js/applies.mjs`).
69
+
70
+ Інших залежностей (зокрема — TOML-парсера, інших правил, `tooling.mjs`) у цьому файлі немає.
71
+
72
+ ## Потік виконання / Використання
73
+
74
+ ### Місце в архітектурі правил
75
+
76
+ У системі правил `n-cursor` кожне правило (наприклад, `python.mdc`) описується трьома шарами:
77
+
78
+ 1. **`*.mdc`** — людинозрозумілий опис правила, без алгоритму перевірки.
79
+ 2. **`js/applies.mjs`** — гейт застосовності правила до конкретного репозиторію (поточний файл).
80
+ 3. **`js/<concern>.mjs`** і Rego-policy — фактичні перевірки (`tooling.mjs`, `package_json.rego` тощо).
81
+
82
+ CLI `n-cursor` під час прогону правил викликає `applies()` для кожного правила перед тим, як запускати решту concerns. Якщо `applies()` повертає `false`, **усі** перевірки правила (JS і policy) пропускаються. Це й розв'язує проблему хибних спрацювань rego-правила `python/package_json` у не-Python репозиторіях.
83
+
84
+ ### Послідовність викликів (happy path)
85
+
86
+ 1. Користувач запускає `n-cursor check` (або еквівалент) у корені репо.
87
+ 2. CLI обходить набір правил, серед яких `python`.
88
+ 3. CLI імпортує `npm/rules/python/js/applies.mjs` і викликає `applies(process.cwd())`.
89
+ 4. Якщо `pyproject.toml` присутній — CLI продовжує: запускає `check()` (друк context-pass) та інші concerns правила (`tooling.mjs`, Rego-policy).
90
+ 5. Якщо `pyproject.toml` відсутній — CLI повністю пропускає `python` для цього прогону. `check()` у такому разі не викликається.
91
+
92
+ ### Приклад прямого використання (без CLI)
93
+
94
+ ```js
95
+ import { applies, check } from './applies.mjs'
96
+
97
+ if (await applies(process.cwd())) {
98
+ const code = check()
99
+ process.exitCode = code
100
+ }
101
+ ```
102
+
103
+ ### Інваріанти й контракти
104
+
105
+ - `applies(cwd)` ніколи не модифікує файлову систему.
106
+ - `applies(cwd)` повертає Promise навіть попри синхронну реалізацію — для уніфікованого CLI-контракту з іншими applies-гейтами.
107
+ - `check()` не дублює перевірку `pyproject.toml`: вона вже відбулась у `applies()`. Якщо `check()` викликається без попереднього `applies()`, повідомлення «pyproject.toml знайдено в корені» може ввести в оману, але exit-code від цього не залежить.
108
+ - Файл свідомо не імпортує `tooling.mjs` чи інші concerns — кожен concern завантажується CLI окремо.
@@ -0,0 +1,153 @@
1
+ # tooling.mjs — FS-перевірка вимог правила `python.mdc`
2
+
3
+ ## Огляд
4
+
5
+ Модуль `npm/rules/python/js/tooling.mjs` — це **check-частина** правила `python.mdc` для Python-проєктів, які перейшли на пакет-менеджер [uv](https://github.com/astral-sh/uv). Його єдина відповідальність — перевірити **наявність/відсутність файлів** у корені репозиторію, які не може покрити декларативний шар (`conftest`, `Rego`-policies для `fix`-команди).
6
+
7
+ Модуль експортує функцію `check(cwd)`, що повертає exit-code (0/1) і друкує діагностику через спільний `check-reporter`. Викликається CLI `@nitra/cursor` після того, як `applies.mjs` вирішив, що правило застосовується до поточного workspace.
8
+
9
+ Розподіл відповідальностей між шарами правила `python`:
10
+
11
+ | Шар | Що перевіряє |
12
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
13
+ | `applies.mjs` | Чи це Python-проєкт узагалі (наявність `pyproject.toml` як вхідний гейт). |
14
+ | `tooling.mjs` (цей файл) | FS-existence: `uv.lock`, `package.json`, `.github/workflows/lint-python.yml`, відсутність `poetry.lock` / `poetry.toml`. |
15
+ | `python/pyproject_toml/` (Rego, `fix`) | Заборона `[tool.poetry]`, вимога `PEP 621` `[project].name` / `[project].version`. |
16
+ | `python/package_json/` (Rego, `fix`) | Наявність скрипта `lint-python` у кореневому `package.json`. |
17
+ | `python/lint_python_yml/` (Rego, `fix`) | Канонічна структура `uses`/`run`-кроків у `.github/workflows/lint-python.yml`. |
18
+
19
+ Свідомо **не перевіряється** наявність `.venv/`: uv теж створює `.venv/`, тож сам по собі цей каталог не є ознакою Poetry й давав би хибнопозитивні спрацювання.
20
+
21
+ ## Експорти / API
22
+
23
+ | Символ | Тип | Призначення |
24
+ | ------- | -------------- | ----------------------------------------------------------------------------------- |
25
+ | `check` | named function | Головна (і єдина) точка входу — FS-перевірка проєкту на відповідність `python.mdc`. |
26
+
27
+ Default-експорт відсутній. Усі імпорти модуля повинні використовувати іменований імпорт:
28
+
29
+ ```js
30
+ import { check } from './tooling.mjs'
31
+ ```
32
+
33
+ ## Функції
34
+
35
+ ### `check(cwd = process.cwd())`
36
+
37
+ **Сигнатура**
38
+
39
+ ```js
40
+ export function check(cwd = process.cwd()): number
41
+ ```
42
+
43
+ **Параметри**
44
+
45
+ | Імʼя | Тип | Значення за замовч. | Опис |
46
+ | ----- | -------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `cwd` | `string` | `process.cwd()` | Абсолютний шлях до кореня репозиторію, який перевіряється. У звичайному CLI-прогоні передається з runtime; у тестах та standalone-сценаріях — явним аргументом для ізоляції від реального CWD. |
48
+
49
+ **Повертає**
50
+
51
+ `number` — exit-code, отриманий від `reporter.getExitCode()`:
52
+
53
+ - `0` — усі очікувані файли на місці, заборонених артефактів немає.
54
+ - `1` — щонайменше один `fail(...)` був викликаний (відсутня обовʼязкова сутність або присутній заборонений артефакт).
55
+
56
+ **Side effects**
57
+
58
+ - Виклик `createCheckReporter()` створює репортер, який пише форматовану діагностику в `stdout` / `stderr` через зареєстровані `pass(message)` та `fail(message)`. Кожен виклик `pass` / `fail` усередині `check()` — потенційний рядок у виведенні CLI.
59
+ - Виконує синхронні `existsSync(...)` для серії шляхів (читання FS — read-only).
60
+ - **Жодних мутацій FS, мережі чи процесу** функція не робить (не пише файли, не виходить через `process.exit`).
61
+
62
+ **Алгоритм (покроково)**
63
+
64
+ 1. Створює репортер: `const reporter = createCheckReporter()` і деструктурує `{ pass, fail }`.
65
+ 2. **Захисний гейт.** Якщо у `cwd` немає `pyproject.toml` — повертає поточний exit-code репортера (на цей момент `0`, оскільки жодних `fail` ще не було). Це дублює гейт `applies.mjs` і потрібне лише для прямого виклику `check()` поза CLI (тести/standalone).
66
+ 3. **Перевірка `uv.lock`** (uv-проєкт коммітить lock-файл):
67
+ - є → `pass('uv.lock є')`,
68
+ - немає → ``fail('uv.lock не знайдено — згенеруй `uv lock` (python.mdc, без Poetry)')``.
69
+ 4. **Заборона Poetry-артефактів.** Для кожного імені з масиву `['poetry.lock', 'poetry.toml']`:
70
+ - файл існує → `fail('<file> знайдено — прибери Poetry, мігруй на uv (python.mdc)')`,
71
+ - файла немає → `pass('<file> відсутній')`.
72
+ 5. **Наявність кореневого `package.json`** (для запуску `bun run lint-python`):
73
+ - є → `pass('package.json є (наявність lint-python перевіряє fix → python.package_json)')`,
74
+ - немає → ``fail('package.json не знайдено в корені — додай для `bun run lint-python` (python.mdc)')``.
75
+ 6. **Наявність `.github/workflows/lint-python.yml`** (CI workflow для lint):
76
+ - є → `pass('<wfPath> є (структуру перевіряє fix → python.lint_python_yml)')`,
77
+ - немає → `fail('<wfPath> не існує — створи згідно python.mdc')`.
78
+ 7. Повертає `reporter.getExitCode()`.
79
+
80
+ **Інваріанти**
81
+
82
+ - Послідовність перевірок фіксована й важить для читабельності виводу. Гейт `pyproject.toml` — обовʼязково першим (інакше для не-Python репо посипалися б `fail`-и).
83
+ - Жодна перевірка не «короткозамикає» наступні: навіть якщо `uv.lock` відсутній, далі будуть зроблені перевірки Poetry, `package.json` і workflow — це дозволяє за один прогін побачити повний список проблем.
84
+
85
+ ## Залежності
86
+
87
+ ### Зовнішні (Node.js standard library)
88
+
89
+ | Імпорт | Звідки | Використання |
90
+ | ------------ | ----------- | ---------------------------------------------------------------------------- |
91
+ | `existsSync` | `node:fs` | Синхронна перевірка існування файлу за абсолютним шляхом. |
92
+ | `join` | `node:path` | Кросплатформне склеювання `cwd` із відносним шляхом файла, що перевіряється. |
93
+
94
+ ### Внутрішні (проєкт)
95
+
96
+ | Імпорт | Звідки | Використання |
97
+ | --------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
98
+ | `createCheckReporter` | `../../../scripts/lib/check-reporter.mjs` | Фабрика репортера для check-скриптів. Очікувані поля результату: `pass(msg: string)`, `fail(msg: string)`, `getExitCode(): number` (0 поки не було `fail`, 1 після першого `fail`). |
99
+
100
+ ### Зовнішні сутності (контекст правила)
101
+
102
+ Файл логічно повʼязаний (але **не імпортує** їх) із:
103
+
104
+ - `npm/rules/python/python.mdc` — текстове формулювання правила.
105
+ - `npm/rules/python/applies.mjs` — гейт, який вирішує, чи запускати `check()`.
106
+ - Rego-полісі `python/pyproject_toml/`, `python/package_json/`, `python/lint_python_yml/` — покривають структурні (а не FS-existence) аспекти, які цей файл свідомо **не** перевіряє.
107
+
108
+ ## Потік виконання / Використання
109
+
110
+ ### Типовий запуск через CLI
111
+
112
+ ```bash
113
+ npx @nitra/cursor check python
114
+ ```
115
+
116
+ CLI послідовно:
117
+
118
+ 1. Резолвить правило `python`.
119
+ 2. Викликає `applies(cwd)` — якщо `false`, правило пропускається повністю.
120
+ 3. Якщо `true`, викликає `check(cwd)` із цього файла.
121
+ 4. Друкує зібрану діагностику й завершується з отриманим exit-code.
122
+
123
+ ### Прямий виклик (тест / standalone)
124
+
125
+ ```js
126
+ import { check } from '@nitra/cursor/rules/python/js/tooling.mjs'
127
+
128
+ const code = check('/abs/path/to/repo')
129
+ // code === 0 → проєкт відповідає python.mdc
130
+ // code === 1 → у виводі репортера є помилки, треба полагодити
131
+ process.exitCode = code
132
+ ```
133
+
134
+ ### Виправлення виявлених проблем
135
+
136
+ Файл лише **діагностує** — нічого не змінює. Виправлення:
137
+
138
+ | Проблема (`fail`-message) | Що зробити |
139
+ | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | `uv.lock не знайдено` | Запустити `uv lock` у корені проєкту й закомітити результат. |
141
+ | `poetry.lock знайдено` / `poetry.toml знайдено` | Завершити міграцію з Poetry: видалити файл, перенести залежності в `pyproject.toml` під `[project]` (PEP 621) і згенерувати `uv.lock`. |
142
+ | `package.json не знайдено в корені` | Створити `package.json` з полем `scripts.lint-python`. Структуру скрипта перевіряє Rego `python.package_json`. |
143
+ | `.github/workflows/lint-python.yml не існує` | Створити workflow за шаблоном з `python.mdc`. Канонічну структуру кроків перевіряє Rego `python.lint_python_yml`. |
144
+
145
+ ### Граничні випадки
146
+
147
+ - **Не-Python репо** (немає `pyproject.toml`) — `check()` повертає `0` без виводу. Безпечно викликати в монорепо корені.
148
+ - **Рівно один `fail`** — exit-code усе одно `1`. Репортер не накопичує «вагу» помилок, лише факт.
149
+ - **Усі `pass`** — exit-code `0`, але всі повідомлення `pass` усе одно потрапляють у вивід (для прозорості та CI-логів).
150
+
151
+ ## Rebuild Test
152
+
153
+ Документ описує модуль розміром 70 рядків з єдиним експортом `check`, кореневим гейтом по `pyproject.toml`, чотирма блоками перевірок (`uv.lock`, заборона Poetry, `package.json`, workflow `lint-python.yml`), залежністю від `node:fs`, `node:path` та `../../../scripts/lib/check-reporter.mjs`. На основі цього опису можна відтворити функціональний еквівалент: створити фабрику репортера, повернути `getExitCode()` без перевірок при відсутності `pyproject.toml`, інакше — викликати `pass`/`fail` для кожного шляху в наведеному порядку зі вказаними повідомленнями (українська локалізація, посилання на `python.mdc`). Сигнатура `check(cwd = process.cwd()): number`, side effects обмежені виводом репортера й читанням FS.