@nitra/cursor 12.6.1 → 12.7.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 (266) hide show
  1. package/.claude-template/settings.template.json +1 -1
  2. package/CHANGELOG.md +10 -0
  3. package/bin/docs/n-cursor.md +4 -20
  4. package/bin/n-cursor.js +7 -53
  5. package/docs/stryker.config.md +20 -28
  6. package/package.json +1 -1
  7. package/rules/abie/docs/index.md +1 -0
  8. package/rules/abie/docs/main.md +29 -0
  9. package/rules/abie/{fix.mjs → main.mjs} +5 -3
  10. package/rules/adr/docs/index.md +1 -0
  11. package/rules/adr/docs/main.md +29 -0
  12. package/rules/adr/{fix.mjs → main.mjs} +5 -3
  13. package/rules/bun/docs/index.md +1 -0
  14. package/rules/bun/docs/main.md +30 -0
  15. package/rules/bun/js/docs/layout.md +11 -36
  16. package/rules/bun/{fix.mjs → main.mjs} +5 -3
  17. package/rules/capacitor/docs/index.md +1 -0
  18. package/rules/capacitor/docs/main.md +29 -0
  19. package/rules/capacitor/{fix.mjs → main.mjs} +5 -3
  20. package/rules/changelog/docs/index.md +1 -0
  21. package/rules/changelog/docs/main.md +27 -0
  22. package/rules/changelog/main.mjs +20 -0
  23. package/rules/ci4/docs/index.md +1 -0
  24. package/rules/ci4/docs/main.md +30 -0
  25. package/rules/ci4/main.mjs +20 -0
  26. package/rules/doc-files/docs/index.md +1 -0
  27. package/rules/doc-files/docs/main.md +31 -0
  28. package/rules/doc-files/js/docgen-files-batch.mjs +47 -3
  29. package/rules/doc-files/js/docgen-scan.mjs +89 -9
  30. package/rules/doc-files/js/docs/docgen-files-batch.md +15 -15
  31. package/rules/doc-files/js/docs/docgen-scan.md +34 -34
  32. package/rules/doc-files/js/docs/index.md +1 -0
  33. package/rules/doc-files/js/docs/run-lint.md +27 -0
  34. package/rules/doc-files/{lint/lint.mjs → js/run-lint.mjs} +23 -9
  35. package/rules/doc-files/{js/lint.mjs → main.mjs} +60 -10
  36. package/rules/docker/docs/index.md +1 -0
  37. package/rules/docker/docs/main.md +28 -0
  38. package/rules/docker/js/docs/lint.md +26 -54
  39. package/rules/docker/js/lint.mjs +11 -0
  40. package/rules/docker/lib/docker-hadolint.mjs +1 -1
  41. package/rules/docker/lib/docs/docker-hadolint.md +16 -173
  42. package/rules/docker/main.mjs +20 -0
  43. package/rules/efes/docs/index.md +1 -0
  44. package/rules/efes/docs/main.md +29 -0
  45. package/rules/efes/main.mjs +20 -0
  46. package/rules/feedback/docs/index.md +1 -0
  47. package/rules/feedback/docs/main.md +30 -0
  48. package/rules/feedback/main.mjs +20 -0
  49. package/rules/ga/docs/index.md +1 -0
  50. package/rules/ga/docs/main.md +29 -0
  51. package/rules/ga/{lint/lint.mjs → main.mjs} +36 -10
  52. package/rules/graphql/docs/index.md +1 -0
  53. package/rules/graphql/docs/main.md +36 -0
  54. package/rules/graphql/main.mjs +20 -0
  55. package/rules/hasura/docs/index.md +1 -0
  56. package/rules/hasura/docs/main.md +30 -0
  57. package/rules/hasura/main.mjs +20 -0
  58. package/rules/image-avif/docs/index.md +1 -0
  59. package/rules/image-avif/docs/main.md +30 -0
  60. package/rules/image-avif/js/docs/avif_generation.md +20 -233
  61. package/rules/image-avif/main.mjs +20 -0
  62. package/rules/image-compress/docs/index.md +1 -0
  63. package/rules/image-compress/docs/main.md +29 -0
  64. package/rules/image-compress/js/docs/package_setup.md +12 -11
  65. package/rules/image-compress/{js/lint.mjs → main.mjs} +21 -5
  66. package/rules/js-bun-db/docs/index.md +1 -0
  67. package/rules/js-bun-db/docs/main.md +30 -0
  68. package/rules/js-bun-db/main.mjs +20 -0
  69. package/rules/js-bun-redis/docs/index.md +1 -0
  70. package/rules/js-bun-redis/docs/main.md +29 -0
  71. package/rules/js-bun-redis/main.mjs +20 -0
  72. package/rules/js-lint/docs/index.md +1 -0
  73. package/rules/js-lint/docs/main.md +29 -0
  74. package/rules/js-lint/js/check.mjs +268 -0
  75. package/rules/js-lint/js/docs/check.md +39 -0
  76. package/rules/js-lint/js/docs/index.md +1 -1
  77. package/rules/js-lint/js/docs/tooling.md +12 -32
  78. package/rules/js-lint/js/tooling.mjs +1 -265
  79. package/rules/js-lint/{js/lint.mjs → main.mjs} +19 -2
  80. package/rules/js-lint-ci/docs/index.md +1 -0
  81. package/rules/js-lint-ci/docs/main.md +27 -0
  82. package/rules/js-lint-ci/main.mjs +33 -0
  83. package/rules/js-mssql/docs/index.md +1 -0
  84. package/rules/js-mssql/docs/main.md +30 -0
  85. package/rules/js-mssql/main.mjs +20 -0
  86. package/rules/js-run/docs/index.md +1 -0
  87. package/rules/js-run/docs/main.md +30 -0
  88. package/rules/js-run/main.mjs +20 -0
  89. package/rules/k8s/docs/index.md +1 -0
  90. package/rules/k8s/docs/main.md +40 -0
  91. package/rules/k8s/js/docs/index.md +12 -0
  92. package/rules/k8s/{lint/lint.mjs → main.mjs} +32 -10
  93. package/rules/nginx-default-tpl/docs/index.md +1 -0
  94. package/rules/nginx-default-tpl/docs/main.md +30 -0
  95. package/rules/nginx-default-tpl/main.mjs +20 -0
  96. package/rules/npm-module/docs/index.md +1 -0
  97. package/rules/npm-module/docs/main.md +29 -0
  98. package/rules/npm-module/js/docs/rule_meta.md +17 -16
  99. package/rules/npm-module/js/rule_meta.mjs +13 -3
  100. package/rules/npm-module/main.mjs +20 -0
  101. package/rules/php/docs/index.md +1 -0
  102. package/rules/php/docs/main.md +33 -0
  103. package/rules/php/js/docs/tooling.md +10 -10
  104. package/rules/php/{lint/lint.mjs → main.mjs} +32 -6
  105. package/rules/python/docs/index.md +1 -0
  106. package/rules/python/docs/main.md +31 -0
  107. package/rules/python/js/docs/tooling.md +17 -17
  108. package/rules/python/{lint/lint.mjs → main.mjs} +29 -5
  109. package/rules/rego/docs/index.md +1 -0
  110. package/rules/rego/docs/main.md +37 -0
  111. package/rules/rego/{lint/lint.mjs → main.mjs} +27 -5
  112. package/rules/release/docs/index.md +1 -0
  113. package/rules/release/docs/main.md +29 -0
  114. package/rules/release/docs/release.md +0 -3
  115. package/rules/release/release.mdc +10 -0
  116. package/rules/rust/docs/index.md +1 -0
  117. package/rules/rust/docs/main.md +27 -0
  118. package/rules/rust/{js/lint.mjs → main.mjs} +20 -3
  119. package/rules/security/docs/index.md +1 -0
  120. package/rules/security/docs/main.md +28 -0
  121. package/rules/security/main.mjs +45 -0
  122. package/rules/style-lint/docs/index.md +1 -0
  123. package/rules/style-lint/docs/main.md +29 -0
  124. package/rules/style-lint/{js/lint.mjs → main.mjs} +19 -1
  125. package/rules/tauri/docs/index.md +1 -0
  126. package/rules/tauri/docs/main.md +29 -0
  127. package/rules/tauri/main.mjs +20 -0
  128. package/rules/test/docs/index.md +1 -0
  129. package/rules/test/docs/main.md +30 -0
  130. package/rules/test/main.mjs +20 -0
  131. package/rules/text/docs/index.md +1 -0
  132. package/rules/text/docs/main.md +29 -0
  133. package/rules/text/js/docs/cspell-fix.md +30 -0
  134. package/rules/text/js/docs/formatting.md +12 -45
  135. package/rules/text/js/docs/index.md +4 -0
  136. package/rules/text/js/docs/run-dotenv-linter.md +31 -0
  137. package/rules/text/js/docs/run-shellcheck.md +28 -0
  138. package/rules/text/js/docs/run-v8r.md +29 -0
  139. package/rules/text/{lint/lint.mjs → main.mjs} +38 -9
  140. package/rules/tool-surface/docs/index.md +1 -0
  141. package/rules/tool-surface/docs/main.md +29 -0
  142. package/rules/tool-surface/main.mjs +20 -0
  143. package/rules/vue/docs/index.md +1 -0
  144. package/rules/vue/docs/main.md +29 -0
  145. package/rules/vue/main.mjs +20 -0
  146. package/rules/worktree/docs/index.md +1 -0
  147. package/rules/worktree/docs/main.md +28 -0
  148. package/rules/worktree/main.mjs +20 -0
  149. package/scripts/docs/index.md +1 -0
  150. package/scripts/docs/post-tool-use-check.md +29 -0
  151. package/scripts/docs/sync-claude-config.md +64 -92
  152. package/scripts/lib/adr/docs/normalize-cli.md +0 -3
  153. package/scripts/lib/adr/docs/normalize-pipeline.md +0 -3
  154. package/scripts/lib/docs/gha-workflow.md +25 -317
  155. package/scripts/lib/docs/index.md +1 -0
  156. package/scripts/lib/docs/list-project-rules-mdc.md +5 -4
  157. package/scripts/lib/docs/list-rule-ids.md +15 -148
  158. package/scripts/lib/docs/read-n-cursor-config-lite.md +12 -16
  159. package/scripts/lib/docs/run-lint-step.md +13 -13
  160. package/scripts/lib/docs/run-lint.md +30 -0
  161. package/scripts/lib/docs/run-rule-cli.md +14 -10
  162. package/scripts/lib/docs/run-standard-lint.md +27 -10
  163. package/scripts/lib/docs/run-standard-rule.md +12 -11
  164. package/scripts/lib/docs/timing-summary.md +11 -12
  165. package/scripts/lib/docs/worktree-notice.md +0 -3
  166. package/scripts/lib/fix/docs/index.md +1 -0
  167. package/scripts/lib/fix/docs/orchestrator.md +23 -18
  168. package/scripts/lib/fix/docs/run-conformance-check.md +32 -0
  169. package/scripts/lib/fix/docs/t0.md +10 -9
  170. package/scripts/lib/fix/orchestrator.mjs +5 -5
  171. package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
  172. package/scripts/lib/fix/t0.mjs +3 -3
  173. package/scripts/lib/list-project-rules-mdc.mjs +1 -1
  174. package/scripts/lib/list-rule-ids.mjs +12 -3
  175. package/scripts/lib/read-n-cursor-config-lite.mjs +2 -2
  176. package/{rules/lint/js/orchestrate.mjs → scripts/lib/run-lint.mjs} +42 -20
  177. package/scripts/lib/run-rule-cli.mjs +4 -4
  178. package/scripts/lib/run-standard-lint.mjs +19 -6
  179. package/scripts/lib/run-standard-rule.mjs +4 -4
  180. package/scripts/lib/timing-summary.mjs +1 -1
  181. package/scripts/{post-tool-use-fix.mjs → post-tool-use-check.mjs} +9 -9
  182. package/scripts/sync-claude-config.mjs +2 -2
  183. package/rules/changelog/fix.mjs +0 -18
  184. package/rules/ci4/fix.mjs +0 -18
  185. package/rules/doc-files/fix.mjs +0 -19
  186. package/rules/doc-files/js/docs/lint.md +0 -34
  187. package/rules/doc-files/lint/docs/index.md +0 -11
  188. package/rules/doc-files/lint/docs/lint.md +0 -35
  189. package/rules/docker/fix.mjs +0 -18
  190. package/rules/docker/lint/docs/index.md +0 -11
  191. package/rules/docker/lint/docs/lint.md +0 -200
  192. package/rules/docker/lint/lint.mjs +0 -95
  193. package/rules/efes/fix.mjs +0 -18
  194. package/rules/feedback/fix.mjs +0 -18
  195. package/rules/ga/fix.mjs +0 -18
  196. package/rules/ga/js/docs/lint.md +0 -20
  197. package/rules/ga/js/lint.mjs +0 -12
  198. package/rules/ga/lint/docs/index.md +0 -11
  199. package/rules/ga/lint/docs/lint.md +0 -31
  200. package/rules/graphql/fix.mjs +0 -18
  201. package/rules/hasura/fix.mjs +0 -18
  202. package/rules/image-avif/fix.mjs +0 -18
  203. package/rules/image-compress/fix.mjs +0 -18
  204. package/rules/image-compress/js/docs/lint.md +0 -24
  205. package/rules/js-bun-db/fix.mjs +0 -18
  206. package/rules/js-bun-redis/fix.mjs +0 -18
  207. package/rules/js-lint/fix.mjs +0 -18
  208. package/rules/js-lint/js/docs/lint.md +0 -32
  209. package/rules/js-lint-ci/fix.mjs +0 -18
  210. package/rules/js-lint-ci/js/docs/lint.md +0 -22
  211. package/rules/js-lint-ci/js/lint.mjs +0 -15
  212. package/rules/js-mssql/fix.mjs +0 -18
  213. package/rules/js-run/fix.mjs +0 -18
  214. package/rules/k8s/fix.mjs +0 -18
  215. package/rules/k8s/js/lint.mjs +0 -14
  216. package/rules/k8s/lint/docs/index.md +0 -11
  217. package/rules/k8s/lint/docs/lint.md +0 -413
  218. package/rules/lint/docs/fix.md +0 -25
  219. package/rules/lint/docs/index.md +0 -11
  220. package/rules/lint/fix.mjs +0 -18
  221. package/rules/lint/js/docs/index.md +0 -11
  222. package/rules/lint/js/docs/orchestrate.md +0 -31
  223. package/rules/lint/meta.json +0 -1
  224. package/rules/nginx-default-tpl/fix.mjs +0 -18
  225. package/rules/npm-module/fix.mjs +0 -18
  226. package/rules/php/fix.mjs +0 -18
  227. package/rules/php/js/docs/lint.md +0 -20
  228. package/rules/php/js/lint.mjs +0 -15
  229. package/rules/php/lint/docs/index.md +0 -11
  230. package/rules/php/lint/docs/lint.md +0 -219
  231. package/rules/python/fix.mjs +0 -18
  232. package/rules/python/js/docs/lint.md +0 -21
  233. package/rules/python/js/lint.mjs +0 -14
  234. package/rules/python/lint/docs/index.md +0 -11
  235. package/rules/python/lint/docs/lint.md +0 -29
  236. package/rules/rego/fix.mjs +0 -18
  237. package/rules/rego/js/docs/lint.md +0 -21
  238. package/rules/rego/js/lint.mjs +0 -12
  239. package/rules/rego/lint/docs/index.md +0 -11
  240. package/rules/rego/lint/docs/lint.md +0 -208
  241. package/rules/rust/fix.mjs +0 -18
  242. package/rules/rust/js/docs/lint.md +0 -21
  243. package/rules/security/fix.mjs +0 -18
  244. package/rules/security/js/docs/lint.md +0 -175
  245. package/rules/security/js/lint.mjs +0 -26
  246. package/rules/style-lint/fix.mjs +0 -18
  247. package/rules/style-lint/js/docs/lint.md +0 -31
  248. package/rules/tauri/fix.mjs +0 -18
  249. package/rules/test/fix.mjs +0 -18
  250. package/rules/text/fix.mjs +0 -18
  251. package/rules/text/js/docs/lint.md +0 -23
  252. package/rules/text/js/lint.mjs +0 -15
  253. package/rules/text/lint/docs/cspell-fix.md +0 -32
  254. package/rules/text/lint/docs/index.md +0 -15
  255. package/rules/text/lint/docs/lint.md +0 -36
  256. package/rules/text/lint/docs/run-dotenv-linter.md +0 -161
  257. package/rules/text/lint/docs/run-shellcheck.md +0 -216
  258. package/rules/text/lint/docs/run-v8r.md +0 -201
  259. package/rules/tool-surface/fix.mjs +0 -18
  260. package/rules/vue/fix.mjs +0 -18
  261. package/rules/worktree/fix.mjs +0 -18
  262. /package/rules/release/{fix.mjs → main.mjs} +0 -0
  263. /package/rules/text/{lint → js}/cspell-fix.mjs +0 -0
  264. /package/rules/text/{lint → js}/run-dotenv-linter.mjs +0 -0
  265. /package/rules/text/{lint → js}/run-shellcheck.mjs +0 -0
  266. /package/rules/text/{lint → js}/run-v8r.mjs +0 -0
@@ -1,11 +1,7 @@
1
1
  /** @see ./docs/tooling.md */
2
- import { existsSync } from 'node:fs'
3
- import { copyFile, readFile } from 'node:fs/promises'
4
2
  import { dirname, join } from 'node:path'
5
3
  import { fileURLToPath } from 'node:url'
6
4
 
7
- import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
8
-
9
5
  /** Шлях до канонічного oxlint JSON у цьому пакеті (для перевірки та тестів). */
10
6
  export const OXLINT_CANONICAL_JSON_PATH = join(
11
7
  dirname(fileURLToPath(import.meta.url)),
@@ -22,15 +18,13 @@ export const KNIP_CANONICAL_JSON_PATH = join(
22
18
  'knip-canonical.json'
23
19
  )
24
20
 
25
- const NON_DIGITS_RE = /\D+/u
26
-
27
21
  // Канонічний рядок `lint-js`-скрипта і мінімальна версія `@nitra/eslint-config` —
28
22
  // у rego (`npm/policy/js_lint/package_json/`). JS-копії (`CANONICAL_LINT_JS`,
29
23
  // `isCanonicalLintJs`, `nitraEslintConfigMeetsMinVersion`) видалено, щоб не
30
24
  // було двох джерел істини й ризику дрифту.
31
25
 
32
26
  /**
33
- * Рекурсивне порівняння фрагментів канону oxlint (масиви — порядок як у каноні; об’єкти — той самий набір ключів і вкладеність).
27
+ * Рекурсивне порівняння фрагментів канону oxlint (масиви — порядок як у каноні; об'єкти — той самий набір ключів і вкладеність).
34
28
  * @param {unknown} actual значення з `.oxlintrc.json`
35
29
  * @param {unknown} expected значення з канону
36
30
  * @returns {boolean} true, якщо значення збігаються за правилами канону
@@ -154,261 +148,3 @@ export function verifyOxlintRcAgainstCanonical(cfg, canonical) {
154
148
 
155
149
  return { ok: failures.length === 0, failures }
156
150
  }
157
-
158
- /**
159
- * Перевіряє ESLint flat config файл.
160
- * @param {(msg: string) => void} passFn callback при успішній перевірці
161
- * @param {(msg: string) => void} failFn callback при помилці
162
- * @param {string} cwd корінь репозиторію
163
- */
164
- async function checkEslintConfig(passFn, failFn, cwd) {
165
- let eslintPath
166
- if (existsSync(join(cwd, 'eslint.config.js'))) {
167
- eslintPath = 'eslint.config.js'
168
- passFn('eslint.config.js існує')
169
- } else if (existsSync(join(cwd, 'eslint.config.mjs'))) {
170
- eslintPath = 'eslint.config.mjs'
171
- passFn('eslint.config.mjs існує')
172
- } else {
173
- failFn('Відсутній eslint.config.js або eslint.config.mjs — flat config з getConfig (js-lint.mdc)')
174
- return
175
- }
176
- const eslintRaw = await readFile(join(cwd, eslintPath), 'utf8')
177
- const checks = [
178
- {
179
- needle: 'getConfig',
180
- ok: `${eslintPath}: містить getConfig`,
181
- err: `${eslintPath}: потрібен виклик getConfig (js-lint.mdc)`
182
- },
183
- {
184
- needle: '@nitra/eslint-config',
185
- ok: `${eslintPath}: імпорт @nitra/eslint-config`,
186
- err: `${eslintPath}: імпортуй getConfig з @nitra/eslint-config`
187
- },
188
- {
189
- needle: '**/auto-imports.d.ts',
190
- ok: `${eslintPath}: ignores містить **/auto-imports.d.ts`,
191
- err: `${eslintPath}: додай у ignores запис **/auto-imports.d.ts (js-lint.mdc)`
192
- }
193
- ]
194
- for (const { needle, ok, err } of checks) {
195
- if (eslintRaw.includes(needle)) {
196
- passFn(ok)
197
- } else {
198
- failFn(err)
199
- }
200
- }
201
- }
202
-
203
- // Перевірки `prettier` / `@nitra/prettier-config` у залежностях (text.mdc) і
204
- // `@nitra/eslint-config ≥ 3.10.0` тепер у Rego: відповідно
205
- // `npm/policy/text/package_json/` і `npm/policy/js_lint/package_json/`. Тут
206
- // лишилася лише workspace-ітерація для `type: "module"` і engines, бо js_lint
207
- // Rego запускається лише на кореневому `package.json`.
208
-
209
- /**
210
- * Перевіряє, що package.json має `"type": "module"`.
211
- * @param {string} label шлях або назва пакета для повідомлень
212
- * @param {{ type?: string }} pkg parsed package.json
213
- * @param {(msg: string) => void} passFn callback при успішній перевірці
214
- * @param {(msg: string) => void} failFn callback при помилці
215
- */
216
- function checkPackageJsonTypeModule(label, pkg, passFn, failFn) {
217
- if (pkg.type === 'module') {
218
- passFn(`${label}: "type": "module"`)
219
- } else {
220
- failFn(`${label}: має містити "type": "module" (js-lint.mdc)`)
221
- }
222
- }
223
-
224
- /**
225
- * `"type": "module"`, `engines.node >= 24` і `engines.bun >= 1.3` у кожному workspace `package.json`.
226
- * @param {unknown[]} workspaces поле workspaces з package.json
227
- * @param {(msg: string) => void} passFn callback при успішній перевірці
228
- * @param {(msg: string) => void} failFn callback при помилці
229
- * @param {string} cwd корінь репозиторію
230
- */
231
- async function checkWorkspacePackages(workspaces, passFn, failFn, cwd) {
232
- for (const ws of workspaces) {
233
- const wsPkgRel = `${ws}/package.json`
234
- const wsPkgAbs = join(cwd, wsPkgRel)
235
- if (existsSync(wsPkgAbs)) {
236
- const wsPkg = JSON.parse(await readFile(wsPkgAbs, 'utf8'))
237
- checkPackageJsonTypeModule(wsPkgRel, wsPkg, passFn, failFn)
238
- checkEnginesNode(wsPkgRel, wsPkg, passFn, failFn)
239
- checkEnginesBun(wsPkgRel, wsPkg, passFn, failFn)
240
- }
241
- }
242
- }
243
-
244
- /**
245
- * engines.node >= 24.
246
- * @param {string} label шлях або назва пакета для повідомлень
247
- * @param {{ engines?: { node?: string } }} pkg розпарсений package.json
248
- * @param {(msg: string) => void} passFn callback при успішній перевірці
249
- * @param {(msg: string) => void} failFn callback при помилці
250
- */
251
- function checkEnginesNode(label, pkg, passFn, failFn) {
252
- const nodeEngine = pkg.engines?.node
253
- if (nodeEngine) {
254
- const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
255
- if (firstNumeric && Number(firstNumeric) >= 24) {
256
- passFn(`${label}: engines.node "${nodeEngine}"`)
257
- } else {
258
- failFn(`${label}: engines.node "${nodeEngine}" — має бути >=24`)
259
- }
260
- } else {
261
- failFn(`${label} не містить engines.node — додай: "engines": { "node": ">=24" }`)
262
- }
263
- }
264
-
265
- /**
266
- * engines.bun >= 1.3.
267
- * @param {string} label шлях або назва пакета для повідомлень
268
- * @param {{ engines?: { bun?: string } }} pkg розпарсений package.json
269
- * @param {(msg: string) => void} passFn callback при успішній перевірці
270
- * @param {(msg: string) => void} failFn callback при помилці
271
- */
272
- function checkEnginesBun(label, pkg, passFn, failFn) {
273
- const bunEngine = pkg.engines?.bun
274
- if (bunEngine) {
275
- const [major, minor] = String(bunEngine).split(NON_DIGITS_RE).filter(Boolean).map(Number)
276
- if (Number.isFinite(major) && Number.isFinite(minor) && (major > 1 || (major === 1 && minor >= 3))) {
277
- passFn(`${label}: engines.bun "${bunEngine}"`)
278
- } else {
279
- failFn(`${label}: engines.bun "${bunEngine}" — має бути >=1.3`)
280
- }
281
- } else {
282
- failFn(`${label} не містить engines.bun — додай: "engines": { "bun": ">=1.3" }`)
283
- }
284
- }
285
-
286
- /**
287
- * Workspace-ітерація: для кожного workspace `package.json` перевіряємо
288
- * `type: "module"` і `engines.{node,bun}`. Кореневий `package.json` ці поля
289
- * валідує `npm/policy/js_lint/package_json/`; lint-js скрипт і `@nitra/eslint-config`
290
- * — теж у Rego.
291
- * @param {(msg: string) => void} passFn callback при успішній перевірці
292
- * @param {(msg: string) => void} failFn callback при помилці
293
- * @param {string} cwd корінь репозиторію
294
- */
295
- async function checkPackageJsonJsLint(passFn, failFn, cwd) {
296
- const pkgPath = join(cwd, 'package.json')
297
- if (!existsSync(pkgPath)) return
298
- const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
299
- const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : []
300
- await checkWorkspacePackages(workspaces, passFn, failFn, cwd)
301
- }
302
-
303
- /**
304
- * Перевіряє .oxlintrc.json.
305
- * @param {(msg: string) => void} passFn callback при успішній перевірці
306
- * @param {(msg: string) => void} failFn callback при помилці
307
- * @param {string} cwd корінь репозиторію
308
- */
309
- async function checkOxlintRc(passFn, failFn, cwd) {
310
- const oxPath = join(cwd, '.oxlintrc.json')
311
- if (!existsSync(oxPath)) {
312
- failFn('.oxlintrc.json не існує — додай конфіг oxlint (js-lint.mdc)')
313
- return
314
- }
315
- let oxCfg
316
- try {
317
- oxCfg = JSON.parse(await readFile(oxPath, 'utf8'))
318
- } catch {
319
- failFn('.oxlintrc.json не є валідним JSON')
320
- return
321
- }
322
- passFn('.oxlintrc.json існує')
323
- let canonical
324
- try {
325
- canonical = JSON.parse(await readFile(OXLINT_CANONICAL_JSON_PATH, 'utf8'))
326
- } catch {
327
- failFn('внутрішня помилка: не вдалося прочитати канон oxlint з пакета @nitra/cursor')
328
- return
329
- }
330
- const oxV = verifyOxlintRcAgainstCanonical(oxCfg, canonical)
331
- if (oxV.ok) {
332
- passFn('.oxlintrc.json збігається з каноном oxlint (@nitra/cursor)')
333
- } else {
334
- for (const msg of oxV.failures) {
335
- failFn(msg)
336
- }
337
- }
338
- }
339
-
340
- /**
341
- * FS-existence для `lint-js.yml` + cross-file перевірка, що `lint.yml` (якщо існує)
342
- * не дублює лінт JS-кроки. Структуру `lint-js.yml` (`actions/checkout@v6`,
343
- * `persist-credentials: false`, `setup-bun-deps`, `bunx oxlint/eslint/jscpd .`,
344
- * заборона `--fix` у CI) валідує `npm/policy/js_lint/lint_js_yml/`.
345
- * @param {(msg: string) => void} passFn callback при успішній перевірці
346
- * @param {(msg: string) => void} failFn callback при помилці
347
- * @param {string} cwd корінь репозиторію
348
- */
349
- async function checkLintJsWorkflows(passFn, failFn, cwd) {
350
- if (existsSync(join(cwd, '.github/workflows/lint-js.yml'))) {
351
- passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor fix → js_lint.lint_js_yml)')
352
- } else {
353
- failFn('.github/workflows/lint-js.yml не існує — створи його (див. rules/js-lint/fix.mjs / js-lint.mdc)')
354
- }
355
-
356
- const lintYmlPath = join(cwd, '.github/workflows/lint.yml')
357
- if (existsSync(lintYmlPath)) {
358
- const lintYml = await readFile(lintYmlPath, 'utf8')
359
- if (lintYml.includes('bunx oxlint') && lintYml.includes('bunx eslint') && lintYml.includes('jscpd')) {
360
- failFn('.github/workflows/lint.yml дублює кроки lint-js.yml — залиш один workflow на лінт JS (js-lint.mdc)')
361
- } else {
362
- passFn('.github/workflows/lint.yml не дублює oxlint/eslint/jscpd з lint-js.yml')
363
- }
364
- }
365
- }
366
-
367
- /**
368
- * Перевіряє наявність `knip.json` у корені проєкту. Якщо файл відсутній —
369
- * копіює канонічний `knip-canonical.json` з пакета `@nitra/cursor` як стартовий
370
- * baseline; зміст подальших модифікацій локально не валідується (`entry` /
371
- * `project` / `ignore` / `ignoreDependencies` / `ignoreBinaries` дозволені
372
- * будь-які; це side effect — описано у js-lint.mdc).
373
- * @param {(msg: string) => void} passFn callback при успішній перевірці
374
- * @param {(msg: string) => void} failFn callback при помилці
375
- * @param {string} cwd корінь репозиторію
376
- */
377
- async function checkKnipConfig(passFn, failFn, cwd) {
378
- const knipPath = join(cwd, 'knip.json')
379
- if (existsSync(knipPath)) {
380
- passFn('knip.json існує')
381
- return
382
- }
383
- if (!existsSync(KNIP_CANONICAL_JSON_PATH)) {
384
- failFn(
385
- `knip.json відсутній, і канонічний шаблон у пакеті не знайдено (${KNIP_CANONICAL_JSON_PATH}) — ` +
386
- 'перевстанови @nitra/cursor'
387
- )
388
- return
389
- }
390
- await copyFile(KNIP_CANONICAL_JSON_PATH, knipPath)
391
- passFn('knip.json створено з канонічного npm/rules/js-lint/js/data/tooling/knip-canonical.json (js-lint.mdc)')
392
- }
393
-
394
- /**
395
- * Перевіряє відповідність проєкту правилам js-lint.mdc
396
- * @param {string} [cwd] корінь репозиторію
397
- * @returns {Promise<number>} 0 — все OK, 1 — є проблеми
398
- */
399
- export async function check(cwd = process.cwd()) {
400
- const reporter = createCheckReporter()
401
- const { pass, fail } = reporter
402
-
403
- await checkEslintConfig(pass, fail, cwd)
404
- await checkPackageJsonJsLint(pass, fail, cwd)
405
- await checkOxlintRc(pass, fail, cwd)
406
- await checkLintJsWorkflows(pass, fail, cwd)
407
- await checkKnipConfig(pass, fail, cwd)
408
-
409
- for (const dup of ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml']) {
410
- if (existsSync(join(cwd, dup))) fail(`Знайдено застарілий конфіг ESLint: ${dup} — видали, використовуй flat config`)
411
- }
412
-
413
- return reporter.getExitCode()
414
- }
@@ -1,8 +1,20 @@
1
1
  /** @see ./docs/lint.md */
2
2
  import { spawnSync } from 'node:child_process'
3
3
 
4
- import { addedLinesByFile } from '../../../scripts/lib/diff-added-lines.mjs'
5
- import { classifyFindings, parseEslint, parseOxlint, renderFindings } from './lint-findings.mjs'
4
+ import { addedLinesByFile } from '../../scripts/lib/diff-added-lines.mjs'
5
+ import { classifyFindings, parseEslint, parseOxlint, renderFindings } from './js/lint-findings.mjs'
6
+ import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
7
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
8
+
9
+ /**
10
+ * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
11
+ * → policy → mdc-refs); `lint()` нижче — lint-поверхня (oxlint+eslint), імпл інлайн тут.
12
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
13
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
14
+ */
15
+ export function run(ctx) {
16
+ return runStandardRule(import.meta.dirname, ctx)
17
+ }
6
18
 
7
19
  const JS_EXT_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx|vue)$/u
8
20
 
@@ -119,3 +131,8 @@ export function lint(files, cwd = process.cwd(), opts = {}) {
119
131
  if (js.length === 0) return Promise.resolve(0)
120
132
  return Promise.resolve(lintChangedClassified(js, cwd, readOnly))
121
133
  }
134
+
135
+ if (isRunAsCli(import.meta.url)) {
136
+ // Standalone: bun rules/js-lint/main.mjs — повний еквівалент `npx @nitra/cursor check js-lint`.
137
+ process.exitCode = await runRuleCli(import.meta.dirname)
138
+ }
@@ -9,3 +9,4 @@ resource: npm/rules/js-lint-ci/
9
9
  | Файл | Тип |
10
10
  |---|---|
11
11
  | [fix.mjs](fix.md) | JS Module |
12
+ | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: npm/rules/js-lint-ci/main.mjs
5
+ docgen:
6
+ crc: 59cd2fd3
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль надає інструменти для глибокого аналізу коду. Функція `run` виконує перевірку коду відповідно до заданого контексту прогону. Функція `lint` порівнює файли репозиторію для виявлення дублікатів та неіспользумого коду.
14
+
15
+ ## Поведінка
16
+
17
+ run виконує перевірку на основі контексту прогону.
18
+ lint виконує крос-файловий аналіз репозиторію на дублікати та мертвий код.
19
+
20
+ ## Публічний API
21
+
22
+ run — точка входу для виконання правила, що включає перевірку логіки застосування (JS-занепокложення $\to$ політика $\to$ посилання MDC) та аналіз коду (jscpd + knip) по всьому репозиторію.
23
+ lint — аналіз коду по всьому репозиторію на наявність дублікатів (jscpd) та мертвого коду (knip).
24
+
25
+ ## Гарантії поведінки
26
+
27
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -0,0 +1,33 @@
1
+ import { spawnSync } from 'node:child_process'
2
+
3
+ import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
4
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
5
+
6
+ /**
7
+ * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
8
+ * → policy → mdc-refs); `lint()` — lint-поверхня (jscpd + knip, крос-файловий аналіз).
9
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
10
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
11
+ */
12
+ export function run(ctx) {
13
+ return runStandardRule(import.meta.dirname, ctx)
14
+ }
15
+
16
+ /**
17
+ * lint-поверхня: jscpd (дублікати) + knip (мертвий код) по всьому репо.
18
+ * @param {string[] | undefined} _files ігнорується (крос-файловий аналіз)
19
+ * @param {string} [cwd] корінь репо
20
+ * @returns {Promise<number>} 0 — OK, ≠0 — порушення
21
+ */
22
+ export function lint(_files, cwd = process.cwd()) {
23
+ const jscpd = spawnSync('bunx', ['jscpd', '.'], { cwd, stdio: 'inherit' })
24
+ const jc = typeof jscpd.status === 'number' ? jscpd.status : 1
25
+ if (jc !== 0) return Promise.resolve(jc)
26
+ const knip = spawnSync('bunx', ['knip', '--no-config-hints'], { cwd, stdio: 'inherit' })
27
+ return Promise.resolve(typeof knip.status === 'number' ? knip.status : 1)
28
+ }
29
+
30
+ if (isRunAsCli(import.meta.url)) {
31
+ // Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`.
32
+ process.exitCode = await runRuleCli(import.meta.dirname)
33
+ }
@@ -9,3 +9,4 @@ resource: npm/rules/js-mssql/
9
9
  | Файл | Тип |
10
10
  |---|---|
11
11
  | [fix.mjs](fix.md) | JS Module |
12
+ | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: npm/rules/js-mssql/main.mjs
5
+ docgen:
6
+ crc: 762b6875
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль валідує відповідність даних, використовуючи логіку, визначену у meta.json. Він перевіряє відповідність конфігурацій та білих списків. Публічна функція run ініціює процес. Модуль є Read-only, тобто не виконує запис у файлову систему чи базу даних.
14
+
15
+ ## Поведінка
16
+
17
+ 1. Викликається функція `run` для виконання перевірки.
18
+ 2. Виконання `run` застосовує логіку, визначену в `meta.json`, включаючи конфігурації.
19
+ 3. Виконання `run` виконує перевірку, яка включає логіку, пов'язану з JS-зацікавленостями та політиками.
20
+ 4. Якщо скрипт запускається як окрема програма (CLI), виконується повний цикл виконання правила.
21
+ 5. Цей цикл включає завантаження конфігурацій, застосування білих списків та підбиття підсумків.
22
+
23
+ ## Публічний API
24
+
25
+ run — виконує основну логіку правила: застосовує перевірки щодо JS-зацікавленостей, політики та посилань MDC.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
30
+ - Кешує результати в межах одного прогону.
@@ -0,0 +1,20 @@
1
+ import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
+
4
+ /**
5
+ * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня: applies →
6
+ * JS-concerns → policy → mdc-refs (через runStandardRule). Lint-поверхні правило не має
7
+ * (`meta.json` без `lint`), тож експорту `lint` тут немає.
8
+ * Library mode: викликається CLI orchestration через `import + run(ctx)`.
9
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
10
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
11
+ */
12
+ export function run(ctx) {
13
+ return runStandardRule(import.meta.dirname, ctx)
14
+ }
15
+
16
+ if (isRunAsCli(import.meta.url)) {
17
+ // Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`
18
+ // (config-loading + whitelist + summary): library-роль (run) + standalone-роль (CLI-блок).
19
+ process.exitCode = await runRuleCli(import.meta.dirname)
20
+ }
@@ -9,3 +9,4 @@ resource: npm/rules/js-run/
9
9
  | Файл | Тип |
10
10
  |---|---|
11
11
  | [fix.mjs](fix.md) | JS Module |
12
+ | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: npm/rules/js-run/main.mjs
5
+ docgen:
6
+ crc: 762b6875
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль виконує визначене правило. Він ініціює стандартний запуск правила, використовуючи директорію правила та контекст прогону. При запуску як окрема програма, він завантажує конфігурації, на які спирається код, зокрема `meta.json`. Функціональність є read-only, і дані кешуються у межах прогону. Фінальний результат визначається викликом публічної функції `run`.
14
+
15
+ ## Поведінка
16
+
17
+ 1. Викликається функція `run` для виконання перевірки.
18
+ 2. Функція `run` ініціює стандартне правило, використовуючи директорію цього правила та наданий контекст прогону.
19
+ 3. Якщо код виконується як окрема програма (CLI), ініціюється повний запуск правила.
20
+ 4. Повний запуск включає завантаження конфігурацій (наприклад, `meta.json`), застосування білих списків та підсумок.
21
+ 5. Результат виконання визначає код виходу процесу.
22
+
23
+ ## Публічний API
24
+
25
+ run — виконує основну перевірку правила, застосовуючи вимоги JS-контексту, політики та посилання MDC.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
30
+ - Кешує результати в межах одного прогону.
@@ -0,0 +1,20 @@
1
+ import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
+
4
+ /**
5
+ * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня: applies →
6
+ * JS-concerns → policy → mdc-refs (через runStandardRule). Lint-поверхні правило не має
7
+ * (`meta.json` без `lint`), тож експорту `lint` тут немає.
8
+ * Library mode: викликається CLI orchestration через `import + run(ctx)`.
9
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
10
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
11
+ */
12
+ export function run(ctx) {
13
+ return runStandardRule(import.meta.dirname, ctx)
14
+ }
15
+
16
+ if (isRunAsCli(import.meta.url)) {
17
+ // Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`
18
+ // (config-loading + whitelist + summary): library-роль (run) + standalone-роль (CLI-блок).
19
+ process.exitCode = await runRuleCli(import.meta.dirname)
20
+ }
@@ -9,3 +9,4 @@ resource: npm/rules/k8s/
9
9
  | Файл | Тип |
10
10
  |---|---|
11
11
  | [fix.mjs](fix.md) | JS Module |
12
+ | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,40 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: npm/rules/k8s/main.mjs
5
+ docgen:
6
+ crc: 1caca447
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 85
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль знаходить унікальні корені каталогів із іменем `k8s` за шляхами файлів `*.yaml` у репозиторії. Якщо таких файлів немає, виконання завершується з кодом 0 без виклику зовнішніх CLI. Для знайдених коренів виконується перевірка YAML-маніфестів. `kubeconform` перевіряє маніфести проти OpenAPI-схем Kubernetes (https://github.com/yannh/kubeconform#readme), використовуючи версію, узгоджену з лінією релізу. `kubescape` сканує маніфести на misconfiguration та відповідність стандартам (NSA, MITRE, CIS), використовуючи конфігураційний файл `.kubescape-exceptions.json`. Орієнтир цільового кластера для `kubescape` визначається за тією ж лінією релізу, що й для `kubeconform`. Обидві утиліти (`kubeconform` та `kubescape`) повинні бути доступні в системному PATH.
14
+
15
+ ## Поведінка
16
+
17
+ run виконує стандартну перевірку правила.
18
+ pathHasK8sSegment визначає, чи містить шлях сегмент директорії `k8s` відносно кореня репозиторію.
19
+ k8sRootFromFile знаходить абсолютний шлях до каталогу `…/k8s`, що містить маніфест, виходячи з абсолютного шляху до YAML-файлу.
20
+ findK8sRoots знаходить унікальні абсолютні шляхи до каталогів `k8s` за наявності файлів `*.yaml`, ігноруючи шляхи, що починаються з `.github/`.
21
+ buildKubescapeExceptionsArgs будує аргументи для `kubescape`, якщо існує файл `.kubescape-exceptions.json` у корені репозиторію.
22
+ findKustomizationDirs знаходить абсолютні шляхи до каталогів, що містять білдабельний `kustomization.yaml` у межах каталогу `…/k8s`, виключаючи компоненти (`kind: Component`).
23
+ runLintK8s виконує повний цикл перевірки Kubernetes-маніфестів за допомогою `kubeconform` та `kubescape`.
24
+ lint делегує виконання повного циклу перевірки Kubernetes-маніфестів функції `runLintK8s`.
25
+
26
+ ## Публічний API
27
+
28
+ run — виконує перевірку конфігурацій (applies → JS-concerns → policy → mdc-refs) та лінтинг (kubeconform/kubescape).
29
+ pathHasK8sSegment — визначає, чи містить шлях сегмент директорії `k8s`.
30
+ k8sRootFromFile — знаходить каталог `…/k8s`, що містить маніфест, рухаючись від файлу вгору.
31
+ findK8sRoots — збирає список унікальних коренів `k8s`, якщо в поточному каталозі є файли `*.yaml`.
32
+ buildKubescapeExceptionsArgs — створює аргументи `--exceptions <file>` для kubescape, якщо існує `.kubescape-exceptions.json` у корені проєкту.
33
+ findKustomizationDirs — знаходить каталоги, які є точками входу Kustomize (містять `kustomization.yaml` з `kind: Kustomization`).
34
+ runLintK8s — виконує публічну CLI-операцію лінтингу конфігурацій, використовуючи механізм блокування та дедуплікації за станом git-дерева.
35
+ lint — слугує оркестратором, який делегує виконання лінтингу конфігурацій (`n-cursor lint k8s`) функції `runLintK8s`.
36
+
37
+ ## Гарантії поведінки
38
+
39
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
40
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,12 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/k8s/js
4
+ resource: npm/rules/k8s/js/
5
+ ---
6
+
7
+ # npm/rules/k8s/js
8
+
9
+ | Файл | Тип |
10
+ |---|---|
11
+ | [lint.mjs](lint.md) | JS Module |
12
+ | [manifests](manifests.md) | Source File |
@@ -2,14 +2,14 @@
2
2
  * Запуск kubeconform та kubescape для каталогів `…/k8s`, де є YAML-маніфести (див. k8s.mdc).
3
3
  *
4
4
  * Знаходить унікальні корені каталогів із іменем `k8s` за шляхами файлів **`*.yaml`**
5
- * (той самий принцип сегмента `k8s`, що й у rules/k8s/fix.mjs; розширення **`.yml`** під `k8s` не використовується). Якщо таких файлів немає — вихід 0
5
+ * (той самий принцип сегмента `k8s`, що й у rules/k8s/check.mjs; розширення **`.yml`** під `k8s` не використовується). Якщо таких файлів немає — вихід 0
6
6
  * без виклику зовнішніх CLI.
7
7
  *
8
8
  * kubeconform перевіряє маніфести проти OpenAPI-схем Kubernetes; kubescape — сканування на
9
9
  * misconfiguration / compliance (NSA, MITRE, CIS тощо). Обидві утиліти очікуються в PATH
10
10
  * (локально: Homebrew, релізи GitHub; у CI — крок установки з k8s.mdc).
11
11
  *
12
- * Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у rules/k8s/fix.mjs / k8s.mdc.
12
+ * Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у rules/k8s/check.mjs / k8s.mdc.
13
13
  * Kubescape не має аналога цього прапорця; орієнтир цільового кластера — та сама лінія релізу (див. k8s.mdc).
14
14
  *
15
15
  * Канон патерну `lint-*` (серіалізація через `runStandardLint`, без прямого `withLock`) —
@@ -23,12 +23,24 @@ import { basename, dirname, join, relative } from 'node:path'
23
23
 
24
24
  import { parse } from 'yaml'
25
25
 
26
- import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
27
- import { ensureTool } from '../../../scripts/lib/ensure-tool.mjs'
28
- import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
29
- import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
30
- import { walkDir } from '../../../scripts/utils/walkDir.mjs'
31
- import { runStandardLint } from '../../../scripts/lib/run-standard-lint.mjs'
26
+ import { isRunAsCli } from '../../scripts/cli-entry.mjs'
27
+ import { ensureTool } from '../../scripts/lib/ensure-tool.mjs'
28
+ import { loadCursorIgnorePaths } from '../../scripts/lib/load-cursor-config.mjs'
29
+ import { resolveCmd } from '../../scripts/utils/resolve-cmd.mjs'
30
+ import { walkDir } from '../../scripts/utils/walkDir.mjs'
31
+ import { runStandardLint } from '../../scripts/lib/run-standard-lint.mjs'
32
+ import { runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
33
+ import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
34
+
35
+ /**
36
+ * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
37
+ * → policy → mdc-refs); `lint()` нижче — lint-поверхня (kubeconform/kubescape), імпл інлайн тут.
38
+ * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
39
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
40
+ */
41
+ export function run(ctx) {
42
+ return runStandardRule(import.meta.dirname, ctx)
43
+ }
32
44
 
33
45
  /** Per-project kubescape exceptions file; підмішується через --exceptions, якщо існує в корені. */
34
46
  const KUBESCAPE_EXCEPTIONS_FILE = '.kubescape-exceptions.json'
@@ -342,11 +354,21 @@ async function runLintK8sSteps() {
342
354
 
343
355
  /**
344
356
  * Публічна CLI-форма: серіалізує через `withLock('lint-k8s')` + дедуп за станом git-дерева.
345
- * Експортовано як `runLintK8s` — використовується з `bin/n-cursor.js` як підкоманда `lint-k8s`.
357
+ * Експортовано як `runLintK8s` — викликається через `n-cursor lint k8s` (оркестраторний адаптер `lint()` делегує сюди); окремої bin-підкоманди `lint-k8s` немає.
346
358
  * @returns {Promise<number>} код виходу
347
359
  */
348
360
  export const runLintK8s = () => runStandardLint(import.meta.dirname, runLintK8sSteps)
349
361
 
362
+ /**
363
+ * Оркестраторний адаптер `n-cursor lint k8s`: делегує у `runLintK8s`.
364
+ * @param {string[] | undefined} _files ігнорується (whole-repo обхід `.../k8s`)
365
+ * @returns {Promise<number>} exit code
366
+ */
367
+ export function lint(_files) {
368
+ return runLintK8s()
369
+ }
370
+
350
371
  if (isRunAsCli(import.meta.url)) {
351
- process.exitCode = await runLintK8s()
372
+ // Standalone: bun rules/k8s/main.mjs повний еквівалент `npx @nitra/cursor check k8s`.
373
+ process.exitCode = await runRuleCli(import.meta.dirname)
352
374
  }
@@ -9,3 +9,4 @@ resource: npm/rules/nginx-default-tpl/
9
9
  | Файл | Тип |
10
10
  |---|---|
11
11
  | [fix.mjs](fix.md) | JS Module |
12
+ | [main.mjs](main.md) | JS Module |