@nitra/cursor 12.6.1 → 12.8.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 (372) hide show
  1. package/.claude-template/settings.template.json +1 -1
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +2 -2
  3. package/CHANGELOG.md +25 -5
  4. package/bin/docs/n-cursor.md +4 -20
  5. package/bin/n-cursor.js +8 -54
  6. package/docs/index.md +3 -3
  7. package/docs/stryker.config.md +20 -28
  8. package/lib/docs/index.md +5 -5
  9. package/lib/docs/llm.md +4 -4
  10. package/package.json +2 -2
  11. package/rules/abie/docs/fix.md +8 -8
  12. package/rules/abie/docs/index.md +4 -3
  13. package/rules/abie/docs/main.md +29 -0
  14. package/rules/abie/js/docs/index.md +6 -6
  15. package/rules/abie/lib/docs/index.md +9 -9
  16. package/rules/abie/{fix.mjs → main.mjs} +5 -3
  17. package/rules/adr/docs/index.md +1 -0
  18. package/rules/adr/docs/main.md +29 -0
  19. package/rules/adr/{fix.mjs → main.mjs} +5 -3
  20. package/rules/bun/docs/fix.md +5 -5
  21. package/rules/bun/docs/index.md +4 -3
  22. package/rules/bun/docs/main.md +30 -0
  23. package/rules/bun/js/docs/index.md +2 -2
  24. package/rules/bun/js/docs/layout.md +11 -36
  25. package/rules/bun/{fix.mjs → main.mjs} +5 -3
  26. package/rules/capacitor/docs/fix.md +10 -10
  27. package/rules/capacitor/docs/index.md +4 -3
  28. package/rules/capacitor/docs/main.md +29 -0
  29. package/rules/capacitor/js/docs/index.md +2 -2
  30. package/rules/capacitor/{fix.mjs → main.mjs} +5 -3
  31. package/rules/changelog/docs/fix.md +11 -11
  32. package/rules/changelog/docs/index.md +4 -3
  33. package/rules/changelog/docs/main.md +27 -0
  34. package/rules/changelog/js/docs/consistency.md +12 -12
  35. package/rules/changelog/js/docs/index.md +2 -2
  36. package/rules/changelog/lib/docs/index.md +2 -2
  37. package/rules/changelog/main.mjs +20 -0
  38. package/rules/ci4/docs/fix.md +4 -4
  39. package/rules/ci4/docs/index.md +4 -3
  40. package/rules/ci4/docs/main.md +30 -0
  41. package/rules/ci4/js/docs/index.md +2 -2
  42. package/rules/ci4/main.mjs +20 -0
  43. package/rules/doc-files/docs/index.md +4 -3
  44. package/rules/doc-files/docs/main.md +31 -0
  45. package/rules/doc-files/js/docgen-crc.mjs +2 -8
  46. package/rules/doc-files/js/docgen-extract.mjs +5 -3
  47. package/rules/doc-files/js/docgen-files-batch.mjs +63 -4
  48. package/rules/doc-files/js/docgen-gen.mjs +11 -3
  49. package/rules/doc-files/js/docgen-judge-measure.mjs +67 -18
  50. package/rules/doc-files/js/docgen-judge.mjs +8 -1
  51. package/rules/doc-files/js/docgen-scan.mjs +99 -11
  52. package/rules/doc-files/js/docs/docgen-crc.md +25 -14
  53. package/rules/doc-files/js/docs/docgen-extract.md +15 -13
  54. package/rules/doc-files/js/docs/docgen-files-batch.md +15 -15
  55. package/rules/doc-files/js/docs/docgen-gen.md +15 -26
  56. package/rules/doc-files/js/docs/docgen-judge-measure.md +14 -12
  57. package/rules/doc-files/js/docs/docgen-scan.md +34 -34
  58. package/rules/doc-files/js/docs/index.md +16 -15
  59. package/rules/doc-files/js/docs/run-lint.md +27 -0
  60. package/rules/doc-files/{lint/lint.mjs → js/run-lint.mjs} +23 -9
  61. package/rules/doc-files/{js/lint.mjs → main.mjs} +60 -10
  62. package/rules/docker/docs/fix.md +6 -6
  63. package/rules/docker/docs/index.md +4 -3
  64. package/rules/docker/docs/main.md +28 -0
  65. package/rules/docker/js/docs/index.md +2 -2
  66. package/rules/docker/js/docs/lint.md +26 -54
  67. package/rules/docker/js/lint.mjs +11 -0
  68. package/rules/docker/lib/docker-hadolint.mjs +1 -1
  69. package/rules/docker/lib/docs/docker-hadolint.md +16 -173
  70. package/rules/docker/lib/docs/index.md +5 -5
  71. package/rules/docker/main.mjs +20 -0
  72. package/rules/efes/docs/fix.md +8 -8
  73. package/rules/efes/docs/index.md +4 -3
  74. package/rules/efes/docs/main.md +29 -0
  75. package/rules/efes/main.mjs +20 -0
  76. package/rules/feedback/docs/fix.md +5 -5
  77. package/rules/feedback/docs/index.md +4 -3
  78. package/rules/feedback/docs/main.md +30 -0
  79. package/rules/feedback/main.mjs +20 -0
  80. package/rules/ga/docs/fix.md +5 -5
  81. package/rules/ga/docs/index.md +4 -3
  82. package/rules/ga/docs/main.md +29 -0
  83. package/rules/ga/js/docs/index.md +3 -3
  84. package/rules/ga/{lint/lint.mjs → main.mjs} +36 -10
  85. package/rules/graphql/docs/fix.md +8 -8
  86. package/rules/graphql/docs/index.md +4 -3
  87. package/rules/graphql/docs/main.md +36 -0
  88. package/rules/graphql/js/docs/index.md +2 -2
  89. package/rules/graphql/lib/docs/index.md +2 -2
  90. package/rules/graphql/main.mjs +20 -0
  91. package/rules/hasura/docs/fix.md +11 -11
  92. package/rules/hasura/docs/index.md +4 -3
  93. package/rules/hasura/docs/main.md +30 -0
  94. package/rules/hasura/js/docs/index.md +2 -2
  95. package/rules/hasura/main.mjs +20 -0
  96. package/rules/image-avif/docs/fix.md +3 -3
  97. package/rules/image-avif/docs/index.md +4 -3
  98. package/rules/image-avif/docs/main.md +30 -0
  99. package/rules/image-avif/js/docs/avif_generation.md +20 -233
  100. package/rules/image-avif/js/docs/index.md +2 -2
  101. package/rules/image-avif/main.mjs +20 -0
  102. package/rules/image-compress/docs/fix.md +2 -2
  103. package/rules/image-compress/docs/index.md +4 -3
  104. package/rules/image-compress/docs/main.md +29 -0
  105. package/rules/image-compress/js/docs/index.md +3 -3
  106. package/rules/image-compress/js/docs/package_setup.md +12 -11
  107. package/rules/image-compress/{js/lint.mjs → main.mjs} +21 -5
  108. package/rules/js-bun-db/docs/fix.md +5 -5
  109. package/rules/js-bun-db/docs/index.md +4 -3
  110. package/rules/js-bun-db/docs/main.md +30 -0
  111. package/rules/js-bun-db/js/docs/index.md +2 -2
  112. package/rules/js-bun-db/lib/docs/index.md +2 -2
  113. package/rules/js-bun-db/main.mjs +20 -0
  114. package/rules/js-bun-redis/docs/fix.md +6 -6
  115. package/rules/js-bun-redis/docs/index.md +4 -3
  116. package/rules/js-bun-redis/docs/main.md +29 -0
  117. package/rules/js-bun-redis/js/docs/index.md +2 -2
  118. package/rules/js-bun-redis/lib/docs/index.md +2 -2
  119. package/rules/js-bun-redis/main.mjs +20 -0
  120. package/rules/js-lint/docs/fix.md +9 -9
  121. package/rules/js-lint/docs/index.md +4 -3
  122. package/rules/js-lint/docs/main.md +29 -0
  123. package/rules/js-lint/js/check.mjs +268 -0
  124. package/rules/js-lint/js/docs/check.md +39 -0
  125. package/rules/js-lint/js/docs/index.md +4 -4
  126. package/rules/js-lint/js/docs/tooling.md +12 -32
  127. package/rules/js-lint/js/tooling.mjs +1 -265
  128. package/rules/js-lint/{js/lint.mjs → main.mjs} +19 -2
  129. package/rules/js-lint-ci/docs/fix.md +3 -3
  130. package/rules/js-lint-ci/docs/index.md +4 -3
  131. package/rules/js-lint-ci/docs/main.md +27 -0
  132. package/rules/js-lint-ci/js/docs/index.md +2 -2
  133. package/rules/js-lint-ci/main.mjs +33 -0
  134. package/rules/js-mssql/docs/fix.md +5 -5
  135. package/rules/js-mssql/docs/index.md +4 -3
  136. package/rules/js-mssql/docs/main.md +30 -0
  137. package/rules/js-mssql/js/docs/index.md +2 -2
  138. package/rules/js-mssql/lib/docs/index.md +2 -2
  139. package/rules/js-mssql/main.mjs +20 -0
  140. package/rules/js-run/docs/fix.md +8 -8
  141. package/rules/js-run/docs/index.md +4 -3
  142. package/rules/js-run/docs/main.md +30 -0
  143. package/rules/js-run/js/docs/index.md +2 -2
  144. package/rules/js-run/lib/docs/index.md +7 -7
  145. package/rules/js-run/main.mjs +20 -0
  146. package/rules/k8s/docs/fix.md +4 -4
  147. package/rules/k8s/docs/index.md +4 -3
  148. package/rules/k8s/docs/main.md +40 -0
  149. package/rules/k8s/js/docs/index.md +12 -0
  150. package/rules/k8s/{lint/lint.mjs → main.mjs} +32 -10
  151. package/rules/nginx-default-tpl/docs/fix.md +7 -7
  152. package/rules/nginx-default-tpl/docs/index.md +4 -3
  153. package/rules/nginx-default-tpl/docs/main.md +30 -0
  154. package/rules/nginx-default-tpl/js/docs/index.md +2 -2
  155. package/rules/nginx-default-tpl/js/docs/template.md +2 -2
  156. package/rules/nginx-default-tpl/main.mjs +20 -0
  157. package/rules/npm-module/docs/fix.md +8 -8
  158. package/rules/npm-module/docs/index.md +4 -3
  159. package/rules/npm-module/docs/main.md +29 -0
  160. package/rules/npm-module/js/docs/index.md +5 -5
  161. package/rules/npm-module/js/docs/rule_meta.md +17 -16
  162. package/rules/npm-module/js/header_doc_pointer.mjs +1 -3
  163. package/rules/npm-module/js/rule_meta.mjs +13 -3
  164. package/rules/npm-module/main.mjs +20 -0
  165. package/rules/php/docs/fix.md +6 -6
  166. package/rules/php/docs/index.md +4 -3
  167. package/rules/php/docs/main.md +33 -0
  168. package/rules/php/js/docs/index.md +3 -3
  169. package/rules/php/js/docs/tooling.md +10 -10
  170. package/rules/php/{lint/lint.mjs → main.mjs} +32 -6
  171. package/rules/python/docs/fix.md +11 -11
  172. package/rules/python/docs/index.md +4 -3
  173. package/rules/python/docs/main.md +31 -0
  174. package/rules/python/js/docs/index.md +3 -3
  175. package/rules/python/js/docs/tooling.md +17 -17
  176. package/rules/python/{lint/lint.mjs → main.mjs} +31 -6
  177. package/rules/rego/docs/fix.md +5 -5
  178. package/rules/rego/docs/index.md +4 -3
  179. package/rules/rego/docs/main.md +37 -0
  180. package/rules/rego/js/docs/index.md +3 -3
  181. package/rules/rego/{lint/lint.mjs → main.mjs} +27 -5
  182. package/rules/release/docs/index.md +5 -4
  183. package/rules/release/docs/main.md +29 -0
  184. package/rules/release/docs/release.md +0 -3
  185. package/rules/release/lib/docs/index.md +4 -4
  186. package/rules/release/release.mdc +10 -0
  187. package/rules/rust/docs/fix.md +4 -4
  188. package/rules/rust/docs/index.md +4 -3
  189. package/rules/rust/docs/main.md +27 -0
  190. package/rules/rust/js/docs/index.md +3 -3
  191. package/rules/rust/lib/docs/index.md +2 -2
  192. package/rules/rust/{js/lint.mjs → main.mjs} +27 -4
  193. package/rules/security/docs/fix.md +6 -6
  194. package/rules/security/docs/index.md +4 -3
  195. package/rules/security/docs/main.md +28 -0
  196. package/rules/security/js/docs/index.md +4 -4
  197. package/rules/security/main.mjs +45 -0
  198. package/rules/style-lint/docs/fix.md +3 -3
  199. package/rules/style-lint/docs/index.md +4 -3
  200. package/rules/style-lint/docs/main.md +29 -0
  201. package/rules/style-lint/js/docs/index.md +3 -3
  202. package/rules/style-lint/{js/lint.mjs → main.mjs} +19 -1
  203. package/rules/tauri/docs/fix.md +11 -11
  204. package/rules/tauri/docs/index.md +4 -3
  205. package/rules/tauri/docs/main.md +29 -0
  206. package/rules/tauri/js/docs/index.md +3 -3
  207. package/rules/tauri/main.mjs +20 -0
  208. package/rules/test/docs/fix.md +5 -5
  209. package/rules/test/docs/index.md +4 -3
  210. package/rules/test/docs/main.md +30 -0
  211. package/rules/test/js/data/stryker_config/docs/index.md +4 -4
  212. package/rules/test/js/data/vitest_config/docs/index.md +2 -2
  213. package/rules/test/js/docs/index.md +7 -7
  214. package/rules/test/main.mjs +20 -0
  215. package/rules/text/docs/fix.md +11 -11
  216. package/rules/text/docs/index.md +4 -3
  217. package/rules/text/docs/main.md +29 -0
  218. package/rules/text/{lint → js}/cspell-fix.mjs +7 -2
  219. package/rules/text/js/docs/cspell-fix.md +30 -0
  220. package/rules/text/js/docs/formatting.md +12 -45
  221. package/rules/text/js/docs/index.md +8 -4
  222. package/rules/text/js/docs/run-dotenv-linter.md +31 -0
  223. package/rules/text/js/docs/run-shellcheck.md +28 -0
  224. package/rules/text/js/docs/run-v8r.md +29 -0
  225. package/rules/text/{lint/lint.mjs → main.mjs} +41 -10
  226. package/rules/tool-surface/docs/index.md +4 -3
  227. package/rules/tool-surface/docs/main.md +29 -0
  228. package/rules/tool-surface/main.mjs +20 -0
  229. package/rules/tool-surface/meta.json +6 -1
  230. package/rules/vue/docs/fix.md +6 -6
  231. package/rules/vue/docs/index.md +4 -3
  232. package/rules/vue/docs/main.md +29 -0
  233. package/rules/vue/js/docs/index.md +2 -2
  234. package/rules/vue/lib/docs/index.md +2 -2
  235. package/rules/vue/main.mjs +20 -0
  236. package/rules/worktree/docs/fix.md +11 -11
  237. package/rules/worktree/docs/index.md +4 -3
  238. package/rules/worktree/docs/main.md +28 -0
  239. package/rules/worktree/main.mjs +20 -0
  240. package/scripts/coverage-classify/docs/index.md +6 -6
  241. package/scripts/dispatcher/docs/index.md +2 -2
  242. package/scripts/docs/index.md +16 -15
  243. package/scripts/docs/post-tool-use-check.md +29 -0
  244. package/scripts/docs/sync-claude-config.md +64 -92
  245. package/scripts/lib/adr/docs/normalize-cli.md +0 -3
  246. package/scripts/lib/adr/docs/normalize-pipeline.md +0 -3
  247. package/scripts/lib/docs/gha-workflow.md +25 -317
  248. package/scripts/lib/docs/index.md +36 -35
  249. package/scripts/lib/docs/list-project-rules-mdc.md +5 -4
  250. package/scripts/lib/docs/list-rule-ids.md +15 -148
  251. package/scripts/lib/docs/read-n-cursor-config-lite.md +12 -16
  252. package/scripts/lib/docs/run-lint-step.md +13 -13
  253. package/scripts/lib/docs/run-lint.md +30 -0
  254. package/scripts/lib/docs/run-rule-cli.md +14 -10
  255. package/scripts/lib/docs/run-standard-lint.md +29 -10
  256. package/scripts/lib/docs/run-standard-rule.md +12 -11
  257. package/scripts/lib/docs/timing-summary.md +11 -12
  258. package/scripts/lib/docs/worktree-notice.md +0 -3
  259. package/scripts/lib/fix/analyze-escalation.mjs +4 -1
  260. package/scripts/lib/fix/docs/index.md +11 -10
  261. package/scripts/lib/fix/docs/orchestrator.md +23 -18
  262. package/scripts/lib/fix/docs/run-conformance-check.md +33 -0
  263. package/scripts/lib/fix/docs/run-fix-check.md +3 -3
  264. package/scripts/lib/fix/docs/t0.md +10 -9
  265. package/scripts/lib/fix/orchestrator.mjs +31 -8
  266. package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
  267. package/scripts/lib/fix/t0.mjs +6 -3
  268. package/scripts/lib/list-project-rules-mdc.mjs +1 -1
  269. package/scripts/lib/list-rule-ids.mjs +12 -3
  270. package/scripts/lib/read-n-cursor-config-lite.mjs +2 -2
  271. package/{rules/lint/js/orchestrate.mjs → scripts/lib/run-lint.mjs} +42 -22
  272. package/scripts/lib/run-rule-cli.mjs +4 -4
  273. package/scripts/lib/run-standard-lint.mjs +19 -6
  274. package/scripts/lib/run-standard-rule.mjs +4 -4
  275. package/scripts/lib/timing-summary.mjs +1 -1
  276. package/scripts/{post-tool-use-fix.mjs → post-tool-use-check.mjs} +9 -9
  277. package/scripts/sync-claude-config.mjs +2 -2
  278. package/scripts/utils/docs/index.md +14 -14
  279. package/skills/doc-aggregate/js/docs/index.md +3 -3
  280. package/skills/doc-files/.changes/260612-0002.md +1 -0
  281. package/skills/doc-files/.changes/260612-0006.md +1 -0
  282. package/skills/doc-files/.changes/260612-0008.md +1 -0
  283. package/skills/doc-files/.changes/260612-0012.md +1 -0
  284. package/skills/doc-files/.changes/260612-0031.md +1 -0
  285. package/skills/doc-files/.changes/260612-0036.md +1 -0
  286. package/skills/doc-files/.changes/260612-0114.md +1 -0
  287. package/skills/start-check/js/docs/index.md +2 -2
  288. package/skills/taze/js/docs/index.md +2 -2
  289. package/types/bin/n-cursor.d.ts +1 -1
  290. package/rules/changelog/fix.mjs +0 -18
  291. package/rules/ci4/fix.mjs +0 -18
  292. package/rules/doc-files/fix.mjs +0 -19
  293. package/rules/doc-files/js/docs/lint.md +0 -34
  294. package/rules/doc-files/lint/docs/index.md +0 -11
  295. package/rules/doc-files/lint/docs/lint.md +0 -35
  296. package/rules/docker/fix.mjs +0 -18
  297. package/rules/docker/lint/docs/index.md +0 -11
  298. package/rules/docker/lint/docs/lint.md +0 -200
  299. package/rules/docker/lint/lint.mjs +0 -95
  300. package/rules/efes/fix.mjs +0 -18
  301. package/rules/feedback/fix.mjs +0 -18
  302. package/rules/ga/fix.mjs +0 -18
  303. package/rules/ga/js/docs/lint.md +0 -20
  304. package/rules/ga/js/lint.mjs +0 -12
  305. package/rules/ga/lint/docs/index.md +0 -11
  306. package/rules/ga/lint/docs/lint.md +0 -31
  307. package/rules/graphql/fix.mjs +0 -18
  308. package/rules/hasura/fix.mjs +0 -18
  309. package/rules/image-avif/fix.mjs +0 -18
  310. package/rules/image-compress/fix.mjs +0 -18
  311. package/rules/image-compress/js/docs/lint.md +0 -24
  312. package/rules/js-bun-db/fix.mjs +0 -18
  313. package/rules/js-bun-redis/fix.mjs +0 -18
  314. package/rules/js-lint/fix.mjs +0 -18
  315. package/rules/js-lint/js/docs/lint.md +0 -32
  316. package/rules/js-lint-ci/fix.mjs +0 -18
  317. package/rules/js-lint-ci/js/docs/lint.md +0 -22
  318. package/rules/js-lint-ci/js/lint.mjs +0 -15
  319. package/rules/js-mssql/fix.mjs +0 -18
  320. package/rules/js-run/fix.mjs +0 -18
  321. package/rules/k8s/fix.mjs +0 -18
  322. package/rules/k8s/js/lint.mjs +0 -14
  323. package/rules/k8s/lint/docs/index.md +0 -11
  324. package/rules/k8s/lint/docs/lint.md +0 -413
  325. package/rules/lint/docs/fix.md +0 -25
  326. package/rules/lint/docs/index.md +0 -11
  327. package/rules/lint/fix.mjs +0 -18
  328. package/rules/lint/js/docs/index.md +0 -11
  329. package/rules/lint/js/docs/orchestrate.md +0 -31
  330. package/rules/lint/meta.json +0 -1
  331. package/rules/nginx-default-tpl/fix.mjs +0 -18
  332. package/rules/npm-module/fix.mjs +0 -18
  333. package/rules/php/fix.mjs +0 -18
  334. package/rules/php/js/docs/lint.md +0 -20
  335. package/rules/php/js/lint.mjs +0 -15
  336. package/rules/php/lint/docs/index.md +0 -11
  337. package/rules/php/lint/docs/lint.md +0 -219
  338. package/rules/python/fix.mjs +0 -18
  339. package/rules/python/js/docs/lint.md +0 -21
  340. package/rules/python/js/lint.mjs +0 -14
  341. package/rules/python/lint/docs/index.md +0 -11
  342. package/rules/python/lint/docs/lint.md +0 -29
  343. package/rules/rego/fix.mjs +0 -18
  344. package/rules/rego/js/docs/lint.md +0 -21
  345. package/rules/rego/js/lint.mjs +0 -12
  346. package/rules/rego/lint/docs/index.md +0 -11
  347. package/rules/rego/lint/docs/lint.md +0 -208
  348. package/rules/rust/fix.mjs +0 -18
  349. package/rules/rust/js/docs/lint.md +0 -21
  350. package/rules/security/fix.mjs +0 -18
  351. package/rules/security/js/docs/lint.md +0 -175
  352. package/rules/security/js/lint.mjs +0 -26
  353. package/rules/style-lint/fix.mjs +0 -18
  354. package/rules/style-lint/js/docs/lint.md +0 -31
  355. package/rules/tauri/fix.mjs +0 -18
  356. package/rules/test/fix.mjs +0 -18
  357. package/rules/text/fix.mjs +0 -18
  358. package/rules/text/js/docs/lint.md +0 -23
  359. package/rules/text/js/lint.mjs +0 -15
  360. package/rules/text/lint/docs/cspell-fix.md +0 -32
  361. package/rules/text/lint/docs/index.md +0 -15
  362. package/rules/text/lint/docs/lint.md +0 -36
  363. package/rules/text/lint/docs/run-dotenv-linter.md +0 -161
  364. package/rules/text/lint/docs/run-shellcheck.md +0 -216
  365. package/rules/text/lint/docs/run-v8r.md +0 -201
  366. package/rules/tool-surface/fix.mjs +0 -18
  367. package/rules/vue/fix.mjs +0 -18
  368. package/rules/worktree/fix.mjs +0 -18
  369. /package/rules/release/{fix.mjs → main.mjs} +0 -0
  370. /package/rules/text/{lint → js}/run-dotenv-linter.mjs +0 -0
  371. /package/rules/text/{lint → js}/run-shellcheck.mjs +0 -0
  372. /package/rules/text/{lint → js}/run-v8r.mjs +0 -0
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: JS Module
3
+ title: run-conformance-check.mjs
4
+ resource: npm/scripts/lib/fix/run-conformance-check.mjs
5
+ docgen:
6
+ crc: 7a026c26
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 90
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль реалізує прямий механізм виконання конформності, викликаючи конформність-фазу `lint`, движок (`orchestrator.mjs`, `t0.mjs`) та PostToolUse-хук. Селекція активних правил здійснюється на основі конфігурації `.n-cursor.json`. Для забезпечення ізоляції, кожен обраний `entrypoint rules/<id>/main.mjs` запускається окремим процесом `bun`.
14
+
15
+ ## Поведінка
16
+
17
+ resolveCheckRuleIds визначає набір ID правил для прогону, використовуючи `.n-cursor.json` як єдине джерело правди для селекції. Якщо запитані правила надані, вони фільтруються за активністю; інакше, він вибирає активні правила з конфігурації, або, за відсутності конфігурації, використовує зматеріалізовані файли `.cursor/rules/*.mdc` для режиму відладки.
18
+
19
+ runConformanceCheck виконує перевірку конформності для кожного правила, яке було визначено як активне. Він запускає кожен `check.mjs` окремим процесом `bun` для ізоляції та повертає загальну кількість виконаних, успішних та невдалих перевірок.
20
+
21
+ ## Публічний API
22
+
23
+ resolveCheckRuleIds — Визначає, які правила будуть застосовані для перевірки, використовуючи `.n-cursor.json` як основне джерело:
24
+
25
+ - Якщо вказані правила, вони фільтруються до активних у конфігурації.
26
+ - Якщо правила не вказані, беруться всі активні правила з конфігу.
27
+ - Якщо правила не вказані і конфіг відсутній, використовується список правил, що були збережені локально.
28
+
29
+ runConformanceCheck — Виконує перевірку відповідності для кожного окремого правила без внесення змін.
30
+
31
+ ## Гарантії поведінки
32
+
33
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -15,9 +15,9 @@ docgen:
15
15
  1. Визначається наявність інструменту `conftest`.
16
16
  2. Отримується список усіх доступних ідентифікаторів правил з каталогу правил.
17
17
  3. Визначається список ідентифікаторів правил для прогону (`resolveCheckRuleIds`), де `.n-cursor.json` — єдине джерело правди:
18
- а. Якщо надано явний список правил — він валідується проти доступних і звужується до активних (вимкнене правило не вмикається навіть на явний запит).
19
- б. Якщо явного списку нема й конфіг є — беруться активні правила конфіга (`available ∩ enabled`); `.cursor/rules/*.mdc` ігнорується (фікс дрейфу «enabled, але .mdc нема»).
20
- в. Якщо конфіга нема (open-by-default debug) — fallback на скан `.cursor/rules/*.mdc`.
18
+ а. Якщо надано явний список правил — він валідується проти доступних і звужується до активних (вимкнене правило не вмикається навіть на явний запит).
19
+ б. Якщо явного списку нема й конфіг є — беруться активні правила конфіга (`available ∩ enabled`); `.cursor/rules/*.mdc` ігнорується (фікс дрейфу «enabled, але .mdc нема»).
20
+ в. Якщо конфіга нема (open-by-default debug) — fallback на скан `.cursor/rules/*.mdc`.
21
21
  4. Якщо визначено ідентифікатори правил для прогону, для кожного ідентифікатора запускається окремий процес `bun` з файлом `fix.mjs` відповідного правила.
22
22
  5. Захоплюється вивід кожного процесу.
23
23
  6. Підраховується загальна кількість правил, що не пройшли перевірку.
@@ -3,26 +3,27 @@ type: JS Module
3
3
  title: t0.mjs
4
4
  resource: npm/scripts/lib/fix/t0.mjs
5
5
  docgen:
6
- crc: 524d91e2
6
+ crc: 49c0669b
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Модуль керує детермінованим (без LLM) застосуванням паттернів до виводів порушень конформності. Паттерни: `vscode-ext-add` (дописати розширення), `rm-forbidden-file` (видалити заборонений файл), `changelog-create-change-file` (створити change-файл через канонічну `writeChange` для воркспейсів із «немає change-файлу» — прибирає ескалацію в LLM на цьому кейсі). `filterT0AutoRules` визначає, які правила мають придатний паттерн; `applyT0Auto`/`runT0AutoCli` застосовують. Паттерн може бути sync або async (await нормалізує). Fail-safe: помилки перехоплюються.
11
+ ## Огляд
12
+
13
+ Модуль керує автоматичним застосуванням T0-auto паттернів до виводів порушень. Він визначає, які правила можуть бути оброблені автоматично, використовуючи конфігурації з `extensions.json` та `package-lock.json`. Функціонал реалізовано через публічні функції: `filterT0AutoRules` для визначення правил, `applyT0Auto` для автоматичного застосування паттернів до виводів, та `runT0AutoCli` для запуску процесу. Модуль працює у режимі fail-safe, перехоплюючи помилки та не кидаючи винятків назовні, надаючи звіт про застосовані автоматичні виправлення.
12
14
 
13
15
  ## Поведінка
14
16
 
15
- applyT0Auto застосовує придатні паттерни до одного виводу порушення (await на apply — паттерн може бути async, як `changelog-create-change-file`).
16
- filterT0AutoRules повертає id правил, для яких існує придатний паттерн.
17
- runT0AutoCli запускає T0-auto для всіх провальних правил, повторно перевіряє check-gate і виводить підсумок.
17
+ applyT0Auto застосовує всі визначені T0-auto паттерни до одного виводу порушення, повертаючи результат застосування.
18
+ filterT0AutoRules повертає список ID правил, для яких існує хоча б один T0-auto паттерн, виходячи з виводів порушень.
19
+ runT0AutoCli запускає T0-auto для кожного провального правила, повторно перевіряє конформність та виводить підсумок.
18
20
 
19
21
  ## Публічний API
20
22
 
21
- applyT0Auto — Впроваджує всі шаблони T0-auto до одного результату виявлених проблем.
22
- filterT0AutoRules — Визначає, які правила мають хоча б один шаблон T0-auto, виходячи з результату `fix --json`.
23
- runT0AutoCli — Запускає команду `n-cursor fix-t0 [rule...]`, виконує `fix --json`, застосовує T0-auto до кожної проблеми, повторно перевіряє check-gate та надає звіт.
23
+ applyT0Auto — вносить зміни до виводу порушень, використовуючи всі визначені T0-auto шаблони.
24
+ filterT0AutoRules — визначає, які правила мають принаймні один T0-auto шаблон, ґрунтуючись на виводі порушень у форматі JSON.
25
+ runT0AutoCli — виконує команду `n-cursor fix-t0 [rule...]`, яка застосовує T0-auto до кожного порушення, повторно перевіряє check-gate та надає звіт.
24
26
 
25
27
  ## Гарантії поведінки
26
28
 
27
29
  - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
28
- - Не звертається до мережі.
@@ -1,7 +1,7 @@
1
1
  /** @see ./docs/orchestrator.md */
2
2
 
3
3
  import { env } from 'node:process'
4
- import { runFixCheck } from './run-fix-check.mjs'
4
+ import { runConformanceCheck } from './run-conformance-check.mjs'
5
5
  import { runT0AutoCli } from './t0.mjs'
6
6
  import { logEscalation } from './escalation-log.mjs'
7
7
  import { runLlmWorker } from './llm-worker.mjs'
@@ -45,7 +45,14 @@ const CLOUD_TRANSPORT_RE = /etimedout|timed out|pi error/i
45
45
  export function buildLadder({ localMin, cloudMin, cloudAvg }) {
46
46
  return [
47
47
  { tier: 'local-min', model: localMin, feedback: false, local: true, isAvg: false, timeoutMs: LOCAL_TIMEOUT_MS },
48
- { tier: 'local-min-retry', model: localMin, feedback: true, local: true, isAvg: false, timeoutMs: LOCAL_TIMEOUT_MS },
48
+ {
49
+ tier: 'local-min-retry',
50
+ model: localMin,
51
+ feedback: true,
52
+ local: true,
53
+ isAvg: false,
54
+ timeoutMs: LOCAL_TIMEOUT_MS
55
+ },
49
56
  { tier: 'cloud-min', model: cloudMin, feedback: true, local: false, isAvg: false, timeoutMs: CLOUD_TIMEOUT_MS },
50
57
  { tier: 'cloud-avg', model: cloudAvg, feedback: true, local: false, isAvg: true, timeoutMs: CLOUD_TIMEOUT_MS }
51
58
  ].filter(r => r.model)
@@ -103,7 +110,15 @@ export async function escalateRule(rule, cwd, deps) {
103
110
 
104
111
  const common = { rung: idx, tier: rung.tier, model: rung.model, withFeedback: rung.feedback }
105
112
  if (rung.isAvg && avgBudget - avgUsed <= 0) {
106
- record({ ...common, callOk: false, callError: 'cloud-avg cap reached', recheckOk: false, remainingViolation: currentViolation, diagnosis: null, ms: 0 })
113
+ record({
114
+ ...common,
115
+ callOk: false,
116
+ callError: 'cloud-avg cap reached',
117
+ recheckOk: false,
118
+ remainingViolation: currentViolation,
119
+ diagnosis: null,
120
+ ms: 0
121
+ })
107
122
  log(` ⏭️ ${rule.ruleId}: ${rung.tier} пропущено (avg-кеп вичерпано)`)
108
123
  continue
109
124
  }
@@ -120,7 +135,15 @@ export async function escalateRule(rule, cwd, deps) {
120
135
  const recheck = await check([rule.ruleId], cwd)
121
136
  const recheckOk = recheck.rules.every(r => r.ok)
122
137
  const remaining = recheckOk ? '' : (recheck.rules.find(r => !r.ok)?.output ?? '')
123
- record({ ...common, callOk: res.ok, callError: res.error ?? null, recheckOk, remainingViolation: remaining, diagnosis: res.diagnosis ?? null, ms: clock() - startedAt })
138
+ record({
139
+ ...common,
140
+ callOk: res.ok,
141
+ callError: res.error ?? null,
142
+ recheckOk,
143
+ remainingViolation: remaining,
144
+ diagnosis: res.diagnosis ?? null,
145
+ ms: clock() - startedAt
146
+ })
124
147
 
125
148
  if (recheckOk) {
126
149
  log(` ✅ ${rung.tier} (${rung.model || 'pi'}): ${rule.ruleId}`)
@@ -165,7 +188,7 @@ export function parseOrchestratorArgs(args) {
165
188
  async function runT0Step(cwd, ruleFilter, failed) {
166
189
  await runT0AutoCli([...ruleFilter], cwd)
167
190
 
168
- const afterT0 = await runFixCheck(ruleFilter, cwd)
191
+ const afterT0 = await runConformanceCheck(ruleFilter, cwd)
169
192
  const failedAfterT0 = afterT0.rules.filter(r => !r.ok)
170
193
  const t0Fixed = failed.filter(r => !failedAfterT0.some(f => f.ruleId === r.ruleId))
171
194
 
@@ -186,7 +209,7 @@ export async function runOrchestratorCli(args, cwd) {
186
209
  const ladder = buildLadder({ localMin: LOCAL_MIN, cloudMin: CLOUD_MIN, cloudAvg: CLOUD_AVG })
187
210
 
188
211
  // ── Перша перевірка (тихо) ──
189
- const initial = await runFixCheck(ruleFilter, cwd)
212
+ const initial = await runConformanceCheck(ruleFilter, cwd)
190
213
  let failed = initial.rules.filter(r => !r.ok)
191
214
  const total = initial.total
192
215
 
@@ -220,14 +243,14 @@ export async function runOrchestratorCli(args, cwd) {
220
243
  const { avgUsed } = await escalateRule(rule, cwd, {
221
244
  ladder,
222
245
  worker,
223
- check: runFixCheck,
246
+ check: runConformanceCheck,
224
247
  avgBudget
225
248
  })
226
249
  avgBudget -= avgUsed
227
250
  }
228
251
 
229
252
  // ── Фінальна перевірка ──
230
- const finalCheck = await runFixCheck(ruleFilter, cwd)
253
+ const finalCheck = await runConformanceCheck(ruleFilter, cwd)
231
254
  const stillFailed = finalCheck.rules.filter(r => !r.ok)
232
255
  if (stillFailed.length === 0) {
233
256
  console.log(`✅ fix: ${total} правил — все чисто`)
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Конформність-детект (колишній subcommand `_fix-check`) як ПРЯМА функція — без subprocess-обгортки
3
- * `bun n-cursor.js _fix-check`. Викликають конформність-фаза `lint` (read-only), движок
4
- * (`orchestrator.mjs`, `t0.mjs`) і PostToolUse-хук.
2
+ * Конформність-детект як ПРЯМА функція — без subprocess-обгортки.
3
+ * Викликають конформність-фаза `lint` (read-only), движок (`orchestrator.mjs`, `t0.mjs`)
4
+ * і PostToolUse-хук.
5
5
  *
6
- * Per-rule ізоляція зберігається: кожне `rules/<id>/fix.mjs` усе ще запускається окремим
7
- * процесом `bun` (crash-isolation). Прибрано лише зовнішній wrapper-subprocess, що його
8
- * раніше шелили оркестратор/хук.
6
+ * Per-rule ізоляція зберігається: entrypoint `rules/<id>/main.mjs` кожного правила
7
+ * запускається окремим процесом `bun` (crash-isolation). Канон єдиний `main.mjs`
8
+ * (ADR 2026-06-21); його CLI-блок кличе `runRuleCli(import.meta.dirname)`.
9
9
  *
10
10
  * Селекція активних правил — виключно тут (`resolveCheckRuleIds` за `.n-cursor.json`);
11
11
  * per-rule whitelist у спавнених процесах прибрано як дубль (див. `runRuleCli`).
@@ -21,7 +21,7 @@ import { discoverCheckRulesFromCursorRules } from '../discover-check-rules-from-
21
21
  import { listProjectRulesMdcFiles } from '../list-project-rules-mdc.mjs'
22
22
  import { isRuleEnabled, readNCursorConfigLite } from '../read-n-cursor-config-lite.mjs'
23
23
 
24
- // Цей файл: npm/scripts/lib/fix/run-fix-check.mjs → npm/rules (чотири dirname угору + rules).
24
+ // Цей файл: npm/scripts/lib/fix/run-conformance-check.mjs → npm/rules (чотири dirname угору + rules).
25
25
  const BUNDLED_RULES_DIR = join(dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url))))), 'rules')
26
26
 
27
27
  /**
@@ -61,16 +61,16 @@ export async function resolveCheckRuleIds(requestedRules, available, cwd) {
61
61
  }
62
62
 
63
63
  /**
64
- * Прогоняє `fix.mjs` кожного правила окремим процесом, захоплюючи output.
64
+ * Прогоняє check-entrypoint кожного правила окремим процесом, захоплюючи output.
65
65
  * @param {string[]} idsToRun правила
66
66
  * @param {string} cwd корінь
67
67
  * @returns {{ totalFailed:number, rules:Array<{ruleId:string, ok:boolean, output:string}> }} результат
68
68
  */
69
- function runRuleFixProcesses(idsToRun, cwd) {
69
+ function runRuleCheckProcesses(idsToRun, cwd) {
70
70
  let totalFailed = 0
71
71
  const rules = []
72
72
  for (const id of idsToRun) {
73
- const r = spawnSync('bun', [join(BUNDLED_RULES_DIR, id, 'fix.mjs')], { cwd, encoding: 'utf8' })
73
+ const r = spawnSync('bun', [join(BUNDLED_RULES_DIR, id, 'main.mjs')], { cwd, encoding: 'utf8' })
74
74
  const ok = r.status === 0
75
75
  rules.push({ ruleId: id, ok, output: `${r.stdout ?? ''}${r.stderr ?? ''}`.trim() })
76
76
  if (!ok) totalFailed++
@@ -79,12 +79,12 @@ function runRuleFixProcesses(idsToRun, cwd) {
79
79
  }
80
80
 
81
81
  /**
82
- * Конформність-детект: per-rule `fix.mjs run()` (= перевірка, без мутацій).
82
+ * Конформність-детект: per-rule `check.mjs run()` (= перевірка, без мутацій).
83
83
  * @param {string[]} [requestedRules] фільтр (порожній → discovery з `.cursor/rules/`)
84
84
  * @param {string} [cwd] корінь
85
85
  * @returns {Promise<{ total:number, failed:number, rules:Array<{ruleId:string, ok:boolean, output:string}> }>} результат
86
86
  */
87
- export async function runFixCheck(requestedRules = [], cwd = processCwd()) {
87
+ export async function runConformanceCheck(requestedRules = [], cwd = processCwd()) {
88
88
  ensureTool('conftest')
89
89
  const available = await listRuleIds(BUNDLED_RULES_DIR)
90
90
  if (available.length === 0) return { total: 0, failed: 0, rules: [] }
@@ -92,6 +92,6 @@ export async function runFixCheck(requestedRules = [], cwd = processCwd()) {
92
92
  const idsToRun = await resolveCheckRuleIds(requestedRules, available, cwd)
93
93
  if (idsToRun.length === 0) return { total: 0, failed: 0, rules: [] }
94
94
 
95
- const { totalFailed, rules } = runRuleFixProcesses(idsToRun, cwd)
95
+ const { totalFailed, rules } = runRuleCheckProcesses(idsToRun, cwd)
96
96
  return { total: idsToRun.length, failed: totalFailed, rules }
97
97
  }
@@ -3,7 +3,7 @@ import { spawnSync } from 'node:child_process'
3
3
  import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
4
4
  import { join } from 'node:path'
5
5
 
6
- import { runFixCheck } from './run-fix-check.mjs'
6
+ import { runConformanceCheck } from './run-conformance-check.mjs'
7
7
  import { writeChange } from '../../../rules/release/change.mjs'
8
8
 
9
9
  const REC_REQUIRE_RE = /recommendations має містити "[^"]+"/
@@ -188,7 +188,7 @@ export async function runT0AutoCli(args, cwd) {
188
188
  const verbose = args.includes('--verbose') || args.includes('-v')
189
189
 
190
190
  // 1. Конформність-детект (пряма функція, без subprocess)
191
- const fixJson = await runFixCheck(ruleFilter, cwd)
191
+ const fixJson = await runConformanceCheck(ruleFilter, cwd)
192
192
  const failed = fixJson.rules.filter(r => !r.ok)
193
193
  if (failed.length === 0) {
194
194
  console.log(`✅ fix-t0: всі правила чисті — T0 не потрібен`)
@@ -210,7 +210,10 @@ export async function runT0AutoCli(args, cwd) {
210
210
  }
211
211
 
212
212
  // 4. Check-gate: перевірити лише ті правила, що ми чіпали
213
- const recheckJson = await runFixCheck(applied.map(a => a.ruleId), cwd)
213
+ const recheckJson = await runConformanceCheck(
214
+ applied.map(a => a.ruleId),
215
+ cwd
216
+ )
214
217
  const stillFailed = recheckJson.rules.filter(r => !r.ok)
215
218
 
216
219
  if (verbose) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Список `.mdc`-файлів правил у `.cursor/rules/` проєкту-споживача (відсортований).
3
- * Винесено зі `bin/n-cursor.js`, щоб ділити між CLI-dispatch і `run-fix-check` (конформність-детект).
3
+ * Винесено зі `bin/n-cursor.js`, щоб ділити між CLI-dispatch і `run-conformance-check` (конформність-детект).
4
4
  */
5
5
  import { existsSync } from 'node:fs'
6
6
  import { readdir } from 'node:fs/promises'
@@ -1,12 +1,21 @@
1
1
  /**
2
- * Перебір `rules/<id>/` директорій з фільтром на наявність `fix.mjs`.
3
- * Після атомарної міграції `fix.mjs` обов'язковий у кожному правилі каталог без нього
2
+ * Перебір `rules/<id>/` директорій з фільтром на наявність entrypoint-а.
3
+ * Канон (ADR 2026-06-21): єдиний entrypoint `rules/<id>/main.mjs`. Каталог без нього
4
4
  * пропускається (це not-a-rule або заглушка).
5
5
  */
6
6
  import { existsSync } from 'node:fs'
7
7
  import { readdir } from 'node:fs/promises'
8
8
  import { join } from 'node:path'
9
9
 
10
+ /**
11
+ * Чи має каталог правила entrypoint `main.mjs`.
12
+ * @param {string} ruleDir абсолютний шлях `rules/<id>/`
13
+ * @returns {boolean} true, якщо `main.mjs` існує
14
+ */
15
+ function hasEntrypoint(ruleDir) {
16
+ return existsSync(join(ruleDir, 'main.mjs'))
17
+ }
18
+
10
19
  /**
11
20
  * @param {string} bundledRulesDir абсолютний шлях до `npm/rules/`
12
21
  * @param {string} [filter] id одного правила (через `--rule abie`)
@@ -17,7 +26,7 @@ export async function listRuleIds(bundledRulesDir, filter) {
17
26
  const ids = entries
18
27
  .filter(e => e.isDirectory() && !e.name.startsWith('.'))
19
28
  .map(e => e.name)
20
- .filter(id => existsSync(join(bundledRulesDir, id, 'fix.mjs')))
29
+ .filter(id => hasEntrypoint(join(bundledRulesDir, id)))
21
30
  .filter(id => filter === undefined || id === filter)
22
31
  return ids.toSorted((a, b) => a.localeCompare(b))
23
32
  }
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Light read-only `.n-cursor.json` reader для standalone `fix.mjs` invocation.
2
+ * Light read-only `.n-cursor.json` reader для standalone `check.mjs` invocation.
3
3
  *
4
4
  * НЕ робить auto-rules detection, merge, schema sync — це справа повного `readConfig` у CLI.
5
5
  * Тут лише: прочитати файл (якщо є), повернути `{ rules: string[], disableRules: string[] }`.
6
6
  *
7
7
  * Спостереження whitelist:
8
8
  * - якщо `.n-cursor.json` НЕМАЄ → правило вважається enabled (поведінка "open by default"),
9
- * щоб `bun rules/<id>/fix.mjs` з будь-якої тимчасової директорії працювало для debug.
9
+ * щоб `bun rules/<id>/check.mjs` з будь-якої тимчасової директорії працювало для debug.
10
10
  * - якщо файл є з `rules:[…]`, але правила там немає → правило не enabled.
11
11
  * - якщо правило в `disable-rules` → не enabled, навіть якщо у `rules:[…]`.
12
12
  */
@@ -1,19 +1,40 @@
1
- /** @see ./docs/orchestrate.md */
1
+ /** @see ./docs/run-lint.md */
2
2
  import { existsSync, readdirSync } from 'node:fs'
3
3
  import { dirname, join } from 'node:path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
  import { cwd as processCwd } from 'node:process'
6
6
  import { spawnSync } from 'node:child_process'
7
7
 
8
- import { parseRuleLintSpec, readRuleMetaRaw } from '../../../scripts/lib/rule-meta.mjs'
9
- import { collectChangedFilesSince, resolveChangedBase } from '../../../scripts/lib/changed-files.mjs'
10
- import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
11
- import { isRuleEnabled, readNCursorConfigLite } from '../../../scripts/lib/read-n-cursor-config-lite.mjs'
8
+ import { parseRuleLintSpec, readRuleMetaRaw } from './rule-meta.mjs'
9
+ import { collectChangedFilesSince, resolveChangedBase } from './changed-files.mjs'
10
+ import { resolveCmd } from '../utils/resolve-cmd.mjs'
11
+ import { isRuleEnabled, readNCursorConfigLite } from './read-n-cursor-config-lite.mjs'
12
12
 
13
- // Цей файл: npm/rules/lint/js/orchestrate.mjs → PACKAGE_ROOT = npm (чотири dirname угору).
14
- const PACKAGE_ROOT = dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url)))))
13
+ // Цей файл: npm/scripts/lib/run-lint.mjs → PACKAGE_ROOT = npm (два dirname угору).
14
+ const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)))
15
15
  const RULES_DIR = join(PACKAGE_ROOT, 'rules')
16
16
 
17
+ /**
18
+ * Чи має правило лінт-поверхню — `meta.json#lint` задано (`per-file`/`full`).
19
+ * Канонічний сигнал (ADR 2026-06-21): gate за meta, не за наявністю файлу.
20
+ * @param {Record<string, unknown> | undefined} raw meta-обʼєкт правила
21
+ * @returns {boolean} true — правило лінтить
22
+ */
23
+ function hasLintSurface(raw) {
24
+ return parseRuleLintSpec(raw?.lint) !== null
25
+ }
26
+
27
+ /**
28
+ * Резолвить лінт-entrypoint правила: `main.mjs` з експортом `lint` (канон, ADR 2026-06-21).
29
+ * @param {string} rulesDir каталог rules
30
+ * @param {string} id rule-id
31
+ * @returns {string | null} шлях до `main.mjs`, або null якщо файлу нема
32
+ */
33
+ function resolveLintEntrypoint(rulesDir, id) {
34
+ const main = join(rulesDir, id, 'main.mjs')
35
+ return existsSync(main) ? main : null
36
+ }
37
+
17
38
  /**
18
39
  * Конформність-фаза lint (whole-repo: config/file/workflow conformance — те, що раніше робив `fix`).
19
40
  * Per-file декомпозиції немає, тож виконується лише у `--full`.
@@ -27,11 +48,11 @@ const RULES_DIR = join(PACKAGE_ROOT, 'rules')
27
48
  */
28
49
  async function runConformance(cwd, readOnly, log, filter = []) {
29
50
  if (!readOnly) {
30
- const { runOrchestratorCli } = await import('../../../scripts/lib/fix/orchestrator.mjs')
51
+ const { runOrchestratorCli } = await import('./fix/orchestrator.mjs')
31
52
  return runOrchestratorCli(filter, cwd)
32
53
  }
33
- const { runFixCheck } = await import('../../../scripts/lib/fix/run-fix-check.mjs')
34
- const { rules } = await runFixCheck(filter, cwd)
54
+ const { runConformanceCheck } = await import('./fix/run-conformance-check.mjs')
55
+ const { rules } = await runConformanceCheck(filter, cwd)
35
56
  const failed = rules.filter(x => !x.ok)
36
57
  if (failed.length === 0) return 0
37
58
  log(`❌ lint: конформність — ${failed.length} порушень: ${failed.map(x => x.ruleId).join(', ')}\n`)
@@ -97,9 +118,9 @@ async function runPerFileRules(ids, ctx) {
97
118
  const { rulesDir, changed, cwd, readOnly, metaById, log } = ctx
98
119
  let worst = 0
99
120
  for (const id of ids) {
100
- const lintPath = join(rulesDir, id, 'js', 'lint.mjs')
101
- if (!existsSync(lintPath)) {
102
- log(`⚠️ lint: правило ${id} має lint-фазу, але немає js/lint.mjs — пропускаю.\n`)
121
+ const lintPath = resolveLintEntrypoint(rulesDir, id)
122
+ if (!lintPath) {
123
+ log(`⚠️ lint: правило ${id} має lint-фазу (meta.lint), але немає main.mjs — пропускаю.\n`)
103
124
  continue
104
125
  }
105
126
  // lintPath = join(rulesDir, id, …) — суто package-internal (rulesDir пакета + id зі
@@ -127,9 +148,7 @@ async function runPerFileRules(ids, ctx) {
127
148
  * @returns {Promise<number>} код конформності
128
149
  */
129
150
  async function runFullConformancePhase(cwd, readOnly, log) {
130
- const { escalationLogSize, maybeAnalyzeEscalation, reportRunStats } = await import(
131
- '../../../scripts/lib/fix/analyze-escalation.mjs'
132
- )
151
+ const { escalationLogSize, maybeAnalyzeEscalation, reportRunStats } = await import('./fix/analyze-escalation.mjs')
133
152
  const escOffset = readOnly ? 0 : escalationLogSize()
134
153
  const conformanceCode = await runConformance(cwd, readOnly, log)
135
154
  if (!readOnly) {
@@ -160,11 +179,12 @@ function runFormat(cwd, log) {
160
179
  }
161
180
 
162
181
  /**
163
- * Scoped-режим (`lint <rule…>`): повний прогін НАЗВАНИХ правил — їх лінтер (`js/lint.mjs`,
164
- * whole-repo) для тих, що його мають, + конформність для всіх названих. Дзеркалить `--full`,
165
- * але звужено до правил, тож `lint ga` standalone `lint-ga`. Конформність-only правила
166
- * (напр. `changelog` із hk) не мають `js/lint.mjs` проганяється лише їх конформність
167
- * (зворотна сумісність із колишнім `fix <rule>`). oxfmt у scoped НЕ запускається — це
182
+ * Scoped-режим (`lint <rule…>`): повний прогін НАЗВАНИХ правил — їх лінтер (entrypoint
183
+ * `main.mjs::lint`, whole-repo) для тих, що мають лінт-поверхню
184
+ * (`meta.json#lint`), + конформність для всіх названих. Дзеркалить `--full`, але звужено
185
+ * до правил, тож `lint ga` standalone `lint-ga`. Конформність-only правила (напр.
186
+ * `changelog` із hk) без `meta.lint` проганяється лише їх конформність (зворотна
187
+ * сумісність із колишнім `fix <rule>`). oxfmt у scoped НЕ запускається — це
168
188
  * таргетований прогін правил, а не глобальне форматування.
169
189
  * @param {string[]} rules id названих правил
170
190
  * @param {{ cwd: string, readOnly: boolean, rulesDir: string, conformance: boolean, log: (s: string) => void }} ctx контекст (`conformance` — чи запускати конформність; false для юніт-тестів із кастомним rulesDir, де реальний пакет недоступний)
@@ -173,7 +193,7 @@ function runFormat(cwd, log) {
173
193
  async function runScopedRules(rules, ctx) {
174
194
  const { cwd, readOnly, rulesDir, conformance, log } = ctx
175
195
  const metaById = readAllMeta(rulesDir)
176
- const linterIds = rules.filter(id => existsSync(join(rulesDir, id, 'js', 'lint.mjs')))
196
+ const linterIds = rules.filter(id => hasLintSurface(metaById[id]))
177
197
  let worst = 0
178
198
  if (linterIds.length > 0) {
179
199
  const perFile = await runPerFileRules(linterIds, { rulesDir, changed: undefined, cwd, readOnly, metaById, log })
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Standalone CLI runner для одного правила. Викликається з `rules/<id>/fix.mjs`
3
- * у блоці `if (import.meta.main)` — це робить `bun rules/<id>/fix.mjs` повним
2
+ * Standalone CLI runner для одного правила. Викликається з `rules/<id>/check.mjs`
3
+ * у блоці `if (import.meta.main)` — це робить `bun rules/<id>/check.mjs` повним
4
4
  * еквівалентом `npx \@nitra/cursor fix <id>`: друкує summary, повертає aggregated exit-code.
5
5
  *
6
6
  * **Без whitelist-гейту.** Гейтинг активних правил — єдине джерело: `resolveCheckRuleIds`
7
- * (`scripts/lib/fix/run-fix-check.mjs`) за `.n-cursor.json`. Прямий `bun rules/<id>/fix.mjs` —
7
+ * (`scripts/lib/fix/run-conformance-check.mjs`) за `.n-cursor.json`. Прямий `bun rules/<id>/check.mjs` —
8
8
  * свідомий запуск саме цього правила (debug / override), тож виконується беззастережно;
9
9
  * усі автоматичні шляхи (lint-конформність, orchestrator, t0, hook) уже спавнять лише активні.
10
10
  *
@@ -15,7 +15,7 @@ import { basename } from 'node:path'
15
15
  import { runStandardRule } from './run-standard-rule.mjs'
16
16
  import { getOrCreateWalkCache } from '../utils/walk-cache.mjs'
17
17
 
18
- // Re-export для зворотної сумісності: уся `rules/<id>/fix.mjs` уже імпортує `isRunAsCli`
18
+ // Re-export для зворотної сумісності: уся `rules/<id>/check.mjs` уже імпортує `isRunAsCli`
19
19
  // саме звідси. Канонічна реалізація — у `scripts/cli-entry.mjs`. Caller передає
20
20
  // `import.meta.url`: `if (isRunAsCli(import.meta.url)) …`.
21
21
  export { isRunAsCli } from '../cli-entry.mjs'
@@ -7,29 +7,42 @@
7
7
  * патчилися в кожному `rules/<rule>/lint/lint.mjs`.
8
8
  *
9
9
  * Зараз робить рівно одне: серіалізує + дедуплікує запуски через `withLock('lint-<ruleId>')`.
10
- * `ruleId` виводиться зі шляху: `import.meta.dirname` у `rules/<id>/lint/lint.mjs` → `<id>`.
10
+ * `ruleId` виводиться зі шляху незалежно від глибини виклику: `rules/<id>` (з `main.mjs`),
11
+ * `rules/<id>/js` або `rules/<id>/lint` → `<id>` (сегмент одразу після `rules/`).
11
12
  *
12
13
  * Інтеграція з боку правила:
13
14
  *
14
15
  * ```js
15
- * import { runStandardLint } from '../../../scripts/lib/run-standard-lint.mjs'
16
+ * import { runStandardLint } from '../../scripts/lib/run-standard-lint.mjs'
16
17
  *
17
18
  * async function runLintFooSteps() { ... }
18
19
  *
19
- * export const runLintFooCli = () => runStandardLint(import.meta.dirname, runLintFooSteps)
20
+ * export function lint(_files) { return runStandardLint(import.meta.dirname, runLintFooSteps) }
20
21
  * ```
21
22
  */
22
- import { basename, dirname } from 'node:path'
23
+ import { basename } from 'node:path'
23
24
 
24
25
  import { withLock } from '../utils/with-lock.mjs'
25
26
 
26
27
  /**
27
- * @param {string} lintDir абсолютний шлях до `rules/<id>/lint/` (передавай `import.meta.dirname`)
28
+ * Виводить `<id>` зі шляху каталогу правила незалежно від глибини: сегмент одразу після
29
+ * останнього `rules/`. Fallback — `basename(dir)`, якщо `rules/` у шляху немає.
30
+ * @param {string} dir абсолютний шлях каталогу (`rules/<id>`, `rules/<id>/js`, …)
31
+ * @returns {string} rule-id
32
+ */
33
+ function ruleIdFromDir(dir) {
34
+ const parts = dir.split(/[/\\]/u)
35
+ const i = parts.lastIndexOf('rules')
36
+ return i !== -1 && parts[i + 1] ? parts[i + 1] : basename(dir)
37
+ }
38
+
39
+ /**
40
+ * @param {string} lintDir абсолютний шлях до каталогу правила (передавай `import.meta.dirname`)
28
41
  * @param {() => number | Promise<number>} stepsFn реальна робота лінту; повертає код виходу
29
42
  * @param {{ttl?:number, staleThreshold?:number, waitTimeout?:number, pollInterval?:number, cacheDir?:string, getFingerprint?:() => string | null}} [opts] прокидаються у `withLock`
30
43
  * @returns {Promise<number>} код виходу
31
44
  */
32
45
  export function runStandardLint(lintDir, stepsFn, opts) {
33
- const ruleId = basename(dirname(lintDir))
46
+ const ruleId = ruleIdFromDir(lintDir)
34
47
  return withLock(`lint-${ruleId}`, stepsFn, opts)
35
48
  }
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Public API per-rule orchestration. Викликається з `rules/<id>/fix.mjs`.
2
+ * Public API per-rule orchestration. Викликається з `rules/<id>/check.mjs`.
3
3
  *
4
4
  * Інкапсулює: `discoverOneRule` → `runRule(applies → JS → policy → mdc-refs)`.
5
5
  * Локальна логіка в правилах заборонена; розширення поведінки — через `ctx`-опції.
6
6
  *
7
7
  * Серіалізація: загортає виконання у `withLock('fix-<ruleId>')` — паралельні запуски
8
- * того самого правила (через `npx \@nitra/cursor fix`, прямий `bun rules/<id>/fix.mjs`
8
+ * того самого правила (через `npx \@nitra/cursor fix`, прямий `bun rules/<id>/check.mjs`
9
9
  * чи `run(ctx)`-композицію) дедупляться за станом git-дерева; різні правила можуть
10
10
  * виконуватись паралельно. Точка інтеграції — тут, щоб не дублювати лок у кожному
11
- * `fix.mjs`.
11
+ * `check.mjs`.
12
12
  */
13
13
  import { basename, dirname } from 'node:path'
14
14
 
@@ -24,7 +24,7 @@ import { withLock } from '../utils/with-lock.mjs'
24
24
  * Зарезервовано на майбутнє (поки не реалізовано — додається, коли з'явиться потреба):
25
25
  * - `skipMdcRefs`, `skipApplies`, `onlyConcerns`.
26
26
  * Розширення поведінки правила робиться лише через нові поля тут, не через локальну
27
- * логіку в `rules/<id>/fix.mjs`.
27
+ * логіку в `rules/<id>/check.mjs`.
28
28
  */
29
29
 
30
30
  /**
@@ -2,7 +2,7 @@
2
2
  * Формат таблиці-резюме часу виконання для orchestrator `fix` / `lint`.
3
3
  *
4
4
  * Дві спільні точки використання:
5
- * - `runFixCommand` у `bin/n-cursor.js` — після прогону всіх `rules/<id>/fix.mjs`.
5
+ * - `runFixCommand` у `bin/n-cursor.js` — після прогону всіх `rules/<id>/check.mjs`.
6
6
  * - `runLintCli` у `scripts/lib/run-lint-cli.mjs` — після прогону `lint-*` скриптів з кореневого `package.json`.
7
7
  *
8
8
  * Чиста функція без I/O — повертає готовий рядок (з фінальним `\n`), друк — на стороні виклику.
@@ -4,17 +4,17 @@
4
4
  *
5
5
  * Раніше хук маршрутизував змінений файл у релевантні правила й ганяв повний `fix` (автофікс
6
6
  * + LLM) — дорого, тож звужували. Тепер хук — **детект** (нуль мутацій, нуль LLM), тож роутинг
7
- * зайвий: один виклик `_fix-check` (per-rule `fix.mjs run()` = перевірка) по всіх правилах.
7
+ * зайвий: один виклик `_fix-check` (per-rule `check.mjs run()` = перевірка) по всіх правилах.
8
8
  *
9
9
  * Контракт:
10
10
  * - stdin Claude Code: JSON із `tool_input.file_path`; якщо файлу немає (напр. Bash) — exit 0 (skip);
11
- * - інакше пряма `runFixCheck` (детект усіх правил, без subprocess-обгортки), exit-код прозоро:
11
+ * - інакше пряма `runConformanceCheck` (детект усіх правил, без subprocess-обгортки), exit-код прозоро:
12
12
  * 1 — є порушення конформності (PostToolUse не блокує turn, але код лишаємо інформативним).
13
13
  */
14
14
  import { once } from 'node:events'
15
15
  import { cwd as processCwd } from 'node:process'
16
16
 
17
- import { runFixCheck } from './lib/fix/run-fix-check.mjs'
17
+ import { runConformanceCheck } from './lib/fix/run-conformance-check.mjs'
18
18
 
19
19
  /**
20
20
  * Зчитує stdin до EOF як utf8 рядок. На TTY — повертає `''` одразу.
@@ -57,20 +57,20 @@ export function extractFilePath(stdinJson) {
57
57
  }
58
58
 
59
59
  /**
60
- * Точка входу. Викликається з `bin/n-cursor.js` коли argv[0] === `post-tool-use-fix`.
60
+ * Точка входу. Викликається з `bin/n-cursor.js` коли argv[0] === `post-tool-use-check`.
61
61
  * Параметри доступні для інʼєкції для тестів: `stdinJson` обходить read від `process.stdin`,
62
- * `runFixCheckFn` — заміна `runFixCheck`.
63
- * @param {{ stdinJson?: string, runFixCheckFn?: typeof runFixCheck }} [options] параметри для тестів
62
+ * `runConformanceCheckFn` — заміна `runConformanceCheck`.
63
+ * @param {{ stdinJson?: string, runConformanceCheckFn?: typeof runConformanceCheck }} [options] параметри для тестів
64
64
  * @returns {Promise<number>} exit code (0 — пропущено / конформність ОК; 1 — є порушення)
65
65
  */
66
- export async function runPostToolUseFixCli(options = {}) {
66
+ export async function runPostToolUseCheckCli(options = {}) {
67
67
  const stdinJson = options.stdinJson ?? (await readStdin())
68
68
  const filePath = extractFilePath(stdinJson)
69
69
  // Тільки після редагування файлу (Edit/Write/MultiEdit мають file_path); Bash тощо — skip.
70
70
  if (filePath === null) {
71
71
  return 0
72
72
  }
73
- const check = options.runFixCheckFn ?? runFixCheck
73
+ const check = options.runConformanceCheckFn ?? runConformanceCheck
74
74
  // Один read-only детект конформності всіх активованих правил (пряма функція, без subprocess).
75
75
  try {
76
76
  const { failed, rules } = await check([], processCwd())
@@ -80,7 +80,7 @@ export async function runPostToolUseFixCli(options = {}) {
80
80
  }
81
81
  return 1
82
82
  } catch (error) {
83
- process.stderr.write(`post-tool-use-fix: не вдалося запустити детект конформності — ${error.message}\n`)
83
+ process.stderr.write(`post-tool-use-check: не вдалося запустити детект конформності — ${error.message}\n`)
84
84
  return 1
85
85
  }
86
86
  }