@7n/rules 1.27.0 → 1.28.1

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 (268) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -5
  3. package/schemas/v8r-catalog.json +10 -1
  4. package/scripts/auto-rules.mjs +2 -3
  5. package/scripts/docs/auto-rules.md +1 -1
  6. package/scripts/lib/docs/glob-to-regex.md +31 -0
  7. package/scripts/lib/docs/index.md +2 -0
  8. package/scripts/lib/docs/js-source-signals.md +32 -0
  9. package/scripts/lib/glob-to-regex.mjs +79 -0
  10. package/scripts/lib/js-source-signals.mjs +50 -0
  11. package/scripts/utils/ast-scan-utils.mjs +2 -2
  12. package/scripts/utils/docs/ast-scan-utils.md +1 -1
  13. package/rules/bun/bunfig/bunfig.mdc +0 -17
  14. package/rules/bun/bunfig/bunfig.rego +0 -29
  15. package/rules/bun/bunfig/concern.json +0 -9
  16. package/rules/bun/bunfig/template/bunfig.toml.snippet.toml +0 -2
  17. package/rules/bun/docs/index.md +0 -11
  18. package/rules/bun/layout/concern.json +0 -16
  19. package/rules/bun/layout/docs/fix-layout.md +0 -29
  20. package/rules/bun/layout/docs/main.md +0 -34
  21. package/rules/bun/layout/fix-layout.mjs +0 -63
  22. package/rules/bun/layout/layout.mdc +0 -60
  23. package/rules/bun/layout/main.mjs +0 -53
  24. package/rules/bun/licensee/concern.json +0 -7
  25. package/rules/bun/licensee/docs/fix-licensee.md +0 -27
  26. package/rules/bun/licensee/docs/index.md +0 -12
  27. package/rules/bun/licensee/docs/main.md +0 -30
  28. package/rules/bun/licensee/fix-licensee.mjs +0 -31
  29. package/rules/bun/licensee/main.mjs +0 -68
  30. package/rules/bun/lint-surface/concern.json +0 -3
  31. package/rules/bun/lint-surface/lint-surface.mdc +0 -13
  32. package/rules/bun/main.json +0 -1
  33. package/rules/bun/main.mdc +0 -11
  34. package/rules/bun/package_json/concern.json +0 -9
  35. package/rules/bun/package_json/docs/fix-package_json.md +0 -28
  36. package/rules/bun/package_json/docs/index.md +0 -9
  37. package/rules/bun/package_json/fix-package_json.mjs +0 -311
  38. package/rules/bun/package_json/package_json.mdc +0 -14
  39. package/rules/bun/package_json/package_json.rego +0 -64
  40. package/rules/bun/package_json/template/package.json.deny.json +0 -4
  41. package/rules/js/check/check.mdc +0 -26
  42. package/rules/js/check/concern.json +0 -19
  43. package/rules/js/check/docs/eslint-config.md +0 -56
  44. package/rules/js/check/docs/fix-check.md +0 -48
  45. package/rules/js/check/docs/index.md +0 -13
  46. package/rules/js/check/docs/main.md +0 -48
  47. package/rules/js/check/eslint-config.mjs +0 -262
  48. package/rules/js/check/fix-check.mjs +0 -82
  49. package/rules/js/check/main.mjs +0 -310
  50. package/rules/js/dep-policy/concern.json +0 -4
  51. package/rules/js/dep-policy/dep-policy.mdc +0 -36
  52. package/rules/js/dep-policy/docs/main.md +0 -35
  53. package/rules/js/dep-policy/main.mjs +0 -99
  54. package/rules/js/docs/index.md +0 -11
  55. package/rules/js/eslint/concern.json +0 -8
  56. package/rules/js/eslint/docs/fix-eslint.md +0 -50
  57. package/rules/js/eslint/docs/fix-worker.md +0 -31
  58. package/rules/js/eslint/docs/index.md +0 -11
  59. package/rules/js/eslint/docs/main.md +0 -35
  60. package/rules/js/eslint/fix-eslint.mjs +0 -160
  61. package/rules/js/eslint/fix-worker.mjs +0 -139
  62. package/rules/js/eslint/main.mjs +0 -115
  63. package/rules/js/file-extensions/concern.json +0 -3
  64. package/rules/js/file-extensions/file-extensions.mdc +0 -12
  65. package/rules/js/jscpd_config/concern.json +0 -11
  66. package/rules/js/jscpd_config/docs/fix-jscpd_config.md +0 -26
  67. package/rules/js/jscpd_config/docs/index.md +0 -9
  68. package/rules/js/jscpd_config/fix-jscpd_config.mjs +0 -3
  69. package/rules/js/jscpd_config/jscpd_config.mdc +0 -42
  70. package/rules/js/jscpd_config/jscpd_config.rego +0 -44
  71. package/rules/js/jscpd_config/template/.jscpd.json.snippet.json +0 -7
  72. package/rules/js/jscpd_duplicates/concern.json +0 -7
  73. package/rules/js/jscpd_duplicates/docs/main.md +0 -29
  74. package/rules/js/jscpd_duplicates/main.mjs +0 -69
  75. package/rules/js/knip/concern.json +0 -7
  76. package/rules/js/knip/docs/main.md +0 -32
  77. package/rules/js/knip/knip.mdc +0 -15
  78. package/rules/js/knip/main.mjs +0 -68
  79. package/rules/js/lint-findings/concern.json +0 -3
  80. package/rules/js/lint-findings/docs/main.md +0 -41
  81. package/rules/js/lint-findings/main.mjs +0 -125
  82. package/rules/js/main.json +0 -1
  83. package/rules/js/main.mdc +0 -18
  84. package/rules/js/package_json/concern.json +0 -9
  85. package/rules/js/package_json/docs/fix-package_json.md +0 -26
  86. package/rules/js/package_json/docs/index.md +0 -9
  87. package/rules/js/package_json/fix-package_json.mjs +0 -3
  88. package/rules/js/package_json/package_json.mdc +0 -15
  89. package/rules/js/package_json/package_json.rego +0 -142
  90. package/rules/js/package_json/template/package.json.snippet.json +0 -6
  91. package/rules/js/tooling/concern.json +0 -3
  92. package/rules/js/tooling/data/tooling/knip-canonical.json +0 -30
  93. package/rules/js/tooling/data/tooling/oxlint-canonical.json +0 -400
  94. package/rules/js/tooling/docs/main.md +0 -53
  95. package/rules/js/tooling/main.mjs +0 -183
  96. package/rules/js/utils_imports/concern.json +0 -4
  97. package/rules/js/utils_imports/docs/main.md +0 -50
  98. package/rules/js/utils_imports/main.mjs +0 -185
  99. package/rules/js/utils_imports/utils_imports.mdc +0 -15
  100. package/rules/js/vscode_extensions/concern.json +0 -11
  101. package/rules/js/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
  102. package/rules/js/vscode_extensions/docs/index.md +0 -11
  103. package/rules/js/vscode_extensions/fix-vscode_extensions.mjs +0 -1
  104. package/rules/js/vscode_extensions/template/extensions.json.snippet.json +0 -6
  105. package/rules/js/vscode_extensions/vscode_extensions.mdc +0 -11
  106. package/rules/js/vscode_extensions/vscode_extensions.rego +0 -12
  107. package/rules/js-bun-db/connection/concern.json +0 -3
  108. package/rules/js-bun-db/connection/connection.mdc +0 -42
  109. package/rules/js-bun-db/docs/index.md +0 -11
  110. package/rules/js-bun-db/lib/bun-sql-scan.mjs +0 -1055
  111. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +0 -63
  112. package/rules/js-bun-db/lib/docs/index.md +0 -11
  113. package/rules/js-bun-db/main.json +0 -1
  114. package/rules/js-bun-db/main.mdc +0 -8
  115. package/rules/js-bun-db/package_json/concern.json +0 -9
  116. package/rules/js-bun-db/package_json/package_json.mdc +0 -31
  117. package/rules/js-bun-db/package_json/package_json.rego +0 -15
  118. package/rules/js-bun-db/package_json/template/package.json.deny.json +0 -6
  119. package/rules/js-bun-db/pg_format_identifiers/concern.json +0 -3
  120. package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +0 -104
  121. package/rules/js-bun-db/safety/concern.json +0 -4
  122. package/rules/js-bun-db/safety/docs/main.md +0 -34
  123. package/rules/js-bun-db/safety/main.mjs +0 -430
  124. package/rules/js-bun-db/safety/safety.mdc +0 -458
  125. package/rules/js-bun-redis/docs/index.md +0 -11
  126. package/rules/js-bun-redis/imports/concern.json +0 -4
  127. package/rules/js-bun-redis/imports/docs/main.md +0 -36
  128. package/rules/js-bun-redis/imports/imports.mdc +0 -47
  129. package/rules/js-bun-redis/imports/main.mjs +0 -88
  130. package/rules/js-bun-redis/lib/docs/index.md +0 -11
  131. package/rules/js-bun-redis/lib/docs/redis-imports.md +0 -227
  132. package/rules/js-bun-redis/lib/redis-imports.mjs +0 -130
  133. package/rules/js-bun-redis/main.json +0 -1
  134. package/rules/js-bun-redis/main.mdc +0 -8
  135. package/rules/js-bun-redis/package_json/concern.json +0 -9
  136. package/rules/js-bun-redis/package_json/package_json.mdc +0 -11
  137. package/rules/js-bun-redis/package_json/package_json.rego +0 -15
  138. package/rules/js-bun-redis/package_json/template/package.json.deny.json +0 -12
  139. package/rules/js-mssql/deps/concern.json +0 -4
  140. package/rules/js-mssql/deps/docs/main.md +0 -33
  141. package/rules/js-mssql/deps/main.mjs +0 -297
  142. package/rules/js-mssql/docs/index.md +0 -11
  143. package/rules/js-mssql/lib/docs/index.md +0 -11
  144. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +0 -380
  145. package/rules/js-mssql/lib/mssql-pool-scan.mjs +0 -610
  146. package/rules/js-mssql/main.json +0 -1
  147. package/rules/js-mssql/main.mdc +0 -144
  148. package/rules/js-mssql/mssql-tvp/concern.json +0 -3
  149. package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +0 -77
  150. package/rules/js-mssql/package_json/concern.json +0 -9
  151. package/rules/js-mssql/package_json/package_json.mdc +0 -9
  152. package/rules/js-mssql/package_json/package_json.rego +0 -57
  153. package/rules/js-run/configmap/concern.json +0 -9
  154. package/rules/js-run/configmap/configmap.mdc +0 -37
  155. package/rules/js-run/configmap/configmap.rego +0 -21
  156. package/rules/js-run/configmap/template/configmap.yaml.contains.yml +0 -4
  157. package/rules/js-run/docs/index.md +0 -11
  158. package/rules/js-run/jsconfig/concern.json +0 -9
  159. package/rules/js-run/jsconfig/docs/fix-jsconfig.md +0 -29
  160. package/rules/js-run/jsconfig/docs/index.md +0 -9
  161. package/rules/js-run/jsconfig/fix-jsconfig.mjs +0 -119
  162. package/rules/js-run/jsconfig/jsconfig.mdc +0 -48
  163. package/rules/js-run/jsconfig/jsconfig.rego +0 -59
  164. package/rules/js-run/jsconfig/template/jsconfig.json.snippet.json +0 -10
  165. package/rules/js-run/lib/bunyan-imports.mjs +0 -98
  166. package/rules/js-run/lib/check-env-scan.mjs +0 -338
  167. package/rules/js-run/lib/conn-file-rules.mjs +0 -214
  168. package/rules/js-run/lib/conn-imports-scan.mjs +0 -154
  169. package/rules/js-run/lib/docs/bunyan-imports.md +0 -121
  170. package/rules/js-run/lib/docs/check-env-scan.md +0 -438
  171. package/rules/js-run/lib/docs/conn-file-rules.md +0 -304
  172. package/rules/js-run/lib/docs/conn-imports-scan.md +0 -208
  173. package/rules/js-run/lib/docs/index.md +0 -16
  174. package/rules/js-run/lib/docs/promise-settimeout-scan.md +0 -334
  175. package/rules/js-run/lib/docs/temporal-scan.md +0 -29
  176. package/rules/js-run/lib/promise-settimeout-scan.mjs +0 -128
  177. package/rules/js-run/lib/temporal-scan.mjs +0 -52
  178. package/rules/js-run/main.json +0 -1
  179. package/rules/js-run/main.mdc +0 -16
  180. package/rules/js-run/package_json/concern.json +0 -9
  181. package/rules/js-run/package_json/package_json.mdc +0 -44
  182. package/rules/js-run/package_json/package_json.rego +0 -37
  183. package/rules/js-run/package_json/template/package.json.deny.json +0 -22
  184. package/rules/js-run/project-structure/concern.json +0 -3
  185. package/rules/js-run/project-structure/project-structure.mdc +0 -11
  186. package/rules/js-run/runtime/concern.json +0 -12
  187. package/rules/js-run/runtime/docs/fix-runtime.md +0 -27
  188. package/rules/js-run/runtime/docs/main.md +0 -35
  189. package/rules/js-run/runtime/fix-runtime.mjs +0 -46
  190. package/rules/js-run/runtime/main.mjs +0 -494
  191. package/rules/js-run/runtime/runtime.mdc +0 -184
  192. package/rules/js-run/scope/concern.json +0 -3
  193. package/rules/js-run/scope/scope.mdc +0 -11
  194. package/rules/npm-module/docs/index.md +0 -11
  195. package/rules/npm-module/emit_types_config/concern.json +0 -9
  196. package/rules/npm-module/emit_types_config/docs/fix-emit_types_config.md +0 -25
  197. package/rules/npm-module/emit_types_config/docs/index.md +0 -9
  198. package/rules/npm-module/emit_types_config/emit_types_config.mdc +0 -43
  199. package/rules/npm-module/emit_types_config/emit_types_config.rego +0 -28
  200. package/rules/npm-module/emit_types_config/fix-emit_types_config.mjs +0 -5
  201. package/rules/npm-module/emit_types_config/template/tsconfig.emit-types.json.snippet.json +0 -9
  202. package/rules/npm-module/header_doc_pointer/concern.json +0 -5
  203. package/rules/npm-module/header_doc_pointer/docs/main.md +0 -40
  204. package/rules/npm-module/header_doc_pointer/header_doc_pointer.mdc +0 -18
  205. package/rules/npm-module/header_doc_pointer/main.mjs +0 -131
  206. package/rules/npm-module/main.json +0 -1
  207. package/rules/npm-module/main.mdc +0 -36
  208. package/rules/npm-module/npm_package_json/concern.json +0 -9
  209. package/rules/npm-module/npm_package_json/docs/fix-npm_package_json.md +0 -27
  210. package/rules/npm-module/npm_package_json/docs/index.md +0 -9
  211. package/rules/npm-module/npm_package_json/fix-npm_package_json.mjs +0 -5
  212. package/rules/npm-module/npm_package_json/npm_package_json.mdc +0 -57
  213. package/rules/npm-module/npm_package_json/npm_package_json.rego +0 -73
  214. package/rules/npm-module/npm_package_json/template/package.json.snippet.json +0 -1
  215. package/rules/npm-module/package_structure/concern.json +0 -5
  216. package/rules/npm-module/package_structure/docs/main.md +0 -37
  217. package/rules/npm-module/package_structure/main.mjs +0 -515
  218. package/rules/npm-module/package_structure/package_structure.mdc +0 -63
  219. package/rules/npm-module/root_package_json/concern.json +0 -9
  220. package/rules/npm-module/root_package_json/docs/fix-root_package_json.md +0 -26
  221. package/rules/npm-module/root_package_json/docs/index.md +0 -9
  222. package/rules/npm-module/root_package_json/fix-root_package_json.mjs +0 -5
  223. package/rules/npm-module/root_package_json/root_package_json.mdc +0 -41
  224. package/rules/npm-module/root_package_json/root_package_json.rego +0 -28
  225. package/rules/npm-module/root_package_json/template/package.json.snippet.json +0 -1
  226. package/rules/npm-module/rule_meta/concern.json +0 -8
  227. package/rules/npm-module/rule_meta/docs/main.md +0 -35
  228. package/rules/npm-module/rule_meta/main.mjs +0 -119
  229. package/rules/npm-module/rule_meta/rule_meta.mdc +0 -11
  230. package/rules/npm-module/skill_meta/concern.json +0 -5
  231. package/rules/npm-module/skill_meta/docs/main.md +0 -149
  232. package/rules/npm-module/skill_meta/main.mjs +0 -91
  233. package/rules/npm-module/skill_meta/skill_meta.mdc +0 -11
  234. package/rules/tool-surface/docs/index.md +0 -11
  235. package/rules/tool-surface/main.json +0 -6
  236. package/rules/tool-surface/main.mdc +0 -72
  237. package/rules/vue/composition-api/composition-api.mdc +0 -82
  238. package/rules/vue/composition-api/concern.json +0 -3
  239. package/rules/vue/docs/index.md +0 -11
  240. package/rules/vue/lib/docs/index.md +0 -11
  241. package/rules/vue/lib/docs/vue-forbidden-imports.md +0 -265
  242. package/rules/vue/lib/vue-forbidden-imports.mjs +0 -264
  243. package/rules/vue/main.json +0 -1
  244. package/rules/vue/main.mdc +0 -18
  245. package/rules/vue/nheader-layout/concern.json +0 -3
  246. package/rules/vue/nheader-layout/nheader-layout.mdc +0 -171
  247. package/rules/vue/package_json/concern.json +0 -9
  248. package/rules/vue/package_json/package_json.mdc +0 -30
  249. package/rules/vue/package_json/package_json.rego +0 -140
  250. package/rules/vue/packages/concern.json +0 -6
  251. package/rules/vue/packages/docs/index.md +0 -11
  252. package/rules/vue/packages/docs/main.md +0 -35
  253. package/rules/vue/packages/main.mjs +0 -575
  254. package/rules/vue/packages/packages.mdc +0 -56
  255. package/rules/vue/quasar-ui/concern.json +0 -3
  256. package/rules/vue/quasar-ui/quasar-ui.mdc +0 -32
  257. package/rules/vue/structure/concern.json +0 -3
  258. package/rules/vue/structure/structure.mdc +0 -101
  259. package/rules/vue/testing/concern.json +0 -3
  260. package/rules/vue/testing/testing.mdc +0 -40
  261. package/rules/vue/tfm-translations/concern.json +0 -7
  262. package/rules/vue/tfm-translations/docs/main.md +0 -29
  263. package/rules/vue/tfm-translations/main.mjs +0 -55
  264. package/rules/vue/tfm-translations/tfm-translations.mdc +0 -32
  265. package/rules/vue/vite-config/concern.json +0 -3
  266. package/rules/vue/vite-config/vite-config.mdc +0 -153
  267. package/rules/vue/vite-env/concern.json +0 -3
  268. package/rules/vue/vite-env/vite-env.mdc +0 -61
@@ -1,160 +0,0 @@
1
- /** @see ./docs/fix-eslint.md */
2
-
3
- /**
4
- * T0-autofix для `js/eslint` — детермінований прогін лінтерів у fix-режимі
5
- * (`oxlint --fix` + `eslint --fix`) на файлах, де детектор знайшов порушення.
6
- * Виправляє лише авто-fixable правила; решта лишається детектору на re-check
7
- * (далі — LLM-ладдер). Запис permanent (поза rollback).
8
- *
9
- * Межа CI: цей модуль — частина fix-фази (`lint` без `--no-fix`); сам по собі
10
- * detector-шлях його не вантажить. У CI (`--no-fix`) fix не запускається —
11
- * узгоджено із забороною `oxlint --fix`/`eslint --fix` у CI (js.mdc / lint_js_yml).
12
- */
13
- import { spawnSync } from 'node:child_process'
14
- import { readFileSync, writeFileSync } from 'node:fs'
15
- import { resolve } from 'node:path'
16
-
17
- import { ESLint } from 'eslint'
18
-
19
- import { filterJsFiles } from './main.mjs'
20
-
21
- /**
22
- * Вміст файлу або null, якщо не читається (видалений/недоступний).
23
- * @param {string} abs абсолютний шлях
24
- * @returns {string|null} вміст або null
25
- */
26
- function readOrNull(abs) {
27
- try {
28
- return readFileSync(abs, 'utf8')
29
- } catch {
30
- return null
31
- }
32
- }
33
-
34
- /**
35
- * Прогін `oxlint --fix` + `eslint --fix` по списку js-файлів. Повертає абсолютні
36
- * шляхи фактично змінених файлів (порівняння до/після).
37
- * @param {string[]} jsFiles posix-relative js-файли
38
- * @param {string} cwd корінь проєкту
39
- * @returns {Promise<string[]>} абсолютні шляхи змінених файлів
40
- */
41
- async function runLinterFix(jsFiles, cwd) {
42
- const abs = jsFiles.map(f => resolve(cwd, f))
43
- const before = new Map(abs.map(a => [a, readOrNull(a)]))
44
-
45
- // oxlint --fix (CLI): авто-fixable oxc-правила.
46
- spawnSync('bunx', ['oxlint', '--fix', ...jsFiles], { cwd, encoding: 'utf8' })
47
-
48
- // eslint --fix (API): outputFixes пише виправлені файли на диск.
49
- const eslint = new ESLint({ cwd, fix: true })
50
- const results = await eslint.lintFiles(abs)
51
- await ESLint.outputFixes(results)
52
-
53
- /** @type {string[]} */
54
- const touched = []
55
- for (const a of abs) {
56
- if (readOrNull(a) !== before.get(a)) touched.push(a)
57
- }
58
- return touched
59
- }
60
-
61
- /**
62
- * Реєстр "механічних" правил `js/eslint`: `oxlint --fix`/`eslint --fix` (T0
63
- * `js-eslint-autofix` вище) їх НЕ покриває (suggestion-only у власній реалізації
64
- * інструментів — перевірено емпірично: лишаються порушеними після `--fix` на
65
- * реальних прогонах), але текстуальна заміна на позначеному рядку однозначна й
66
- * безпечна без повного AST-парсингу — проста заміна ідентифікатора/API-виклику.
67
- * `reasons` — обидва формати `reason` з `main.mjs` (`f.rule`): eslint (`ruleId`,
68
- * `"plugin/rule"`) і oxlint (`d.code`, `"plugin(rule)"`) — те саме правило різні
69
- * тули віддають по-різному.
70
- * @type {Array<{ reasons: string[], replace: (line: string) => string|null }>}
71
- */
72
- const MECHANICAL_TEXT_FIXES = [
73
- {
74
- // Number.isInteger(x) не ловить x поза Number.MIN/MAX_SAFE_INTEGER — сама заміна
75
- // імені методу семантично точна для рядка, що ЛИШЕ так і викликає isInteger.
76
- reasons: ['unicorn/prefer-number-is-safe-integer', 'unicorn(prefer-number-is-safe-integer)'],
77
- replace: line =>
78
- line.includes('Number.isInteger') ? line.replaceAll('Number.isInteger', 'Number.isSafeInteger') : null
79
- }
80
- ]
81
-
82
- /**
83
- * Знаходить механічний фікс для reason-у порушення (обидва формати tool-у).
84
- * @param {string} reason `violation.reason`
85
- * @returns {((line: string) => string|null)|null} replace-функція або null
86
- */
87
- function mechanicalFixFor(reason) {
88
- return MECHANICAL_TEXT_FIXES.find(f => f.reasons.includes(reason))?.replace ?? null
89
- }
90
-
91
- /**
92
- * Застосовує механічні текстові заміни по конкретних рядках (`data.line`, 1-indexed)
93
- * файлу. Рядок без збігу очікуваного шаблону (файл змінився з моменту detect-у) —
94
- * пропускається, не гадаємо.
95
- * @param {string} content вміст файлу
96
- * @param {Array<{ line: number, replace: (line: string) => string|null }>} targets рядки й replace-функції
97
- * @returns {string|null} новий вміст або null, якщо жоден рядок не змінився
98
- */
99
- function applyMechanicalLineFixes(content, targets) {
100
- const lines = content.split('\n')
101
- let changed = false
102
- for (const { line, replace } of targets) {
103
- const idx = line - 1
104
- if (idx < 0 || idx >= lines.length) continue
105
- const next = replace(lines[idx])
106
- if (next !== null && next !== lines[idx]) {
107
- lines[idx] = next
108
- changed = true
109
- }
110
- }
111
- return changed ? lines.join('\n') : null
112
- }
113
-
114
- /** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
115
- export const patterns = [
116
- {
117
- id: 'js-eslint-autofix',
118
- test: violations => violations.some(v => v.file),
119
- apply: async (violations, ctx) => {
120
- const files = [...new Set(violations.map(v => v.file).filter(Boolean))]
121
- const jsFiles = filterJsFiles(files)
122
- if (jsFiles.length === 0) return { touchedFiles: [] }
123
- const touchedFiles = await runLinterFix(jsFiles, ctx.cwd)
124
- for (const a of touchedFiles) ctx.recordWrite?.(a)
125
- return touchedFiles.length > 0
126
- ? { touchedFiles, message: `oxlint/eslint --fix: ${touchedFiles.length} файл(ів)` }
127
- : { touchedFiles: [] }
128
- }
129
- },
130
- {
131
- id: 'js-eslint-mechanical-text-fix',
132
- test: violations => violations.some(v => v.file && v.data?.line && mechanicalFixFor(v.reason)),
133
- apply: (violations, ctx) => {
134
- /** @type {Map<string, Array<{ line: number, replace: (line: string) => string|null }>>} */
135
- const byFile = new Map()
136
- for (const v of violations) {
137
- const replace = v.file && v.data?.line ? mechanicalFixFor(v.reason) : null
138
- if (!replace) continue
139
- const arr = byFile.get(v.file)
140
- const target = { line: v.data.line, replace }
141
- if (arr) arr.push(target)
142
- else byFile.set(v.file, [target])
143
- }
144
- const touchedFiles = []
145
- for (const [rel, targets] of byFile) {
146
- const abs = resolve(ctx.cwd, rel)
147
- const content = readOrNull(abs)
148
- if (content === null) continue
149
- const next = applyMechanicalLineFixes(content, targets)
150
- if (next === null) continue
151
- ctx.recordWrite?.(abs)
152
- writeFileSync(abs, next)
153
- touchedFiles.push(abs)
154
- }
155
- return touchedFiles.length > 0
156
- ? { touchedFiles, message: `механічні текстові заміни: ${touchedFiles.length} файл(ів)` }
157
- : { touchedFiles: [] }
158
- }
159
- }
160
- ]
@@ -1,139 +0,0 @@
1
- /**
2
- * fix-worker для `js/eslint`: окрема агентна сесія НА ФАЙЛ замість одного великого промпту
3
- * на всі файли concern-а одразу. Дефолтний worker (`default-worker.mjs`) шле ВСІ порушення
4
- * з УСІХ файлів в один `runAgentFix`-виклик — на реальних lint-прогонах (tauri-components)
5
- * це давало 100% timeout на всіх 4 rung-ах драбини (local-min→cloud-avg), навіть коли
6
- * порушень було лише 20-90 у 1-3 файлах: одна сесія жонглює кількома файлами одразу і не
7
- * встигає в rung-таймаут.
8
- *
9
- * Файли обробляються ПАРАЛЕЛЬНО (обмежений пул, `MAX_PARALLEL_FILES`), не послідовно —
10
- * ADR docs/adr/260718-0754-js-eslint-fix-worker-per-session-overhead.md. Профайлінг
11
- * реального прогону (2026-07-18, trace `runAgentFix`) спростував гіпотезу про фіксовані
12
- * bootstrap-витрати сесії: `dispatch.test.js` (2 порушення) — 9 раундів моделі, 29.4с;
13
- * тривіальний фікс так само вимагав багато tool-раундів, а не одноразової плати за bootstrap.
14
- * Оскільки домінує саме кількість/довжина turn-раундів, а не старт сесії, паралелізм
15
- * (кілька файлів одночасно замість черги) дає пряме пришвидшення незалежно від причини —
16
- * секвенційна версія витрачала весь бюджет rung-а на 1 файл, лишаючи іншим 0 шансів
17
- * навіть стартувати.
18
- *
19
- * Rollback-контракт незмінний: `recordWrite` (не durable) на кожен файл — правки наявного
20
- * стороннього коду. Якщо після worker-а `runRung`-ів canonical re-detect (whole-concern)
21
- * знайде хоч одне порушення будь-де, увесь rung однаково відкотиться (S1) — той самий
22
- * контракт, що й у дефолтного worker-а; паралелізм підвищує ймовірність, що ЦЕЙ rung
23
- * дійде до 0 порушень, а не замінює rollback-семантику. `snapshot.record`/`recordWrite` —
24
- * синхронні Map-операції без `await` усередині, тож конкурентні виклики безпечні (Node
25
- * event loop не перериває синхронний блок).
26
- *
27
- * Дедлайн (`DEADLINE_FRACTION` від `ctx.timeoutMs`, той самий підхід, що й
28
- * `doc-files/check/fix-worker.mjs`) гейтить лише СТАРТ нового файлу з черги — не
29
- * скасовує вже запущені. При files.length ≤ MAX_PARALLEL_FILES усі стартують майже
30
- * одночасно й отримують практично весь бюджет незалежно один від одного (справжній
31
- * паралелізм); при більшій кількості — черга природно звужує бюджет пізніших хвиль
32
- * (`callTimeoutMs` рахується в момент старту, не наперед), не даючи перевищити
33
- * backstop ×1.25 runner-а.
34
- * @typedef {import('../../../scripts/lib/lint-surface/types.mjs').FixWorkerFn} FixWorkerFn
35
- */
36
- import { resolve } from 'node:path'
37
-
38
- import { anchoredEnabled } from '../../../scripts/lib/lint-surface/default-worker.mjs'
39
- import { renderViolations } from '../../../scripts/lib/lint-surface/render.mjs'
40
- import { lint } from './main.mjs'
41
-
42
- /** Частка ctx.timeoutMs, після якої черга не стартує новий файл (запас до backstop ×1.25). */
43
- const DEADLINE_FRACTION = 0.8
44
-
45
- /** Максимум файлів, що обробляються одночасно (без необмеженого burst на великих concern-ах). */
46
- const MAX_PARALLEL_FILES = 4
47
-
48
- /**
49
- * Item-scoped (один файл) canonical re-detect для evidence-гейта `runAgentFix` —
50
- * НЕ те саме, що whole-concern `ctx.verify` з `runRung` (той перевірив би ВСІ файли
51
- * concern-а й дав би хибний "не готово" навіть коли саме ЦЕЙ файл уже чистий).
52
- * @param {string} cwd абсолютний корінь проєкту
53
- * @param {string} ruleId id правила
54
- * @param {string} concernId id concern-а
55
- * @param {string} file posix-relative шлях файлу, що перевіряється
56
- * @returns {Promise<{ ok: boolean, output: string }>} вердикт verify-петлі
57
- */
58
- async function verifyFile(cwd, ruleId, concernId, file) {
59
- const { violations: after } = await lint({ cwd, ruleId, concernId, files: [file] })
60
- const stamped = after.map(v => ({ ...v, ruleId, concernId }))
61
- return { ok: stamped.length === 0, output: renderViolations(stamped) }
62
- }
63
-
64
- /**
65
- * Обробляє `items` обмеженим пулом воркерів (не більше `MAX_PARALLEL_FILES` одночасно) —
66
- * власна черга замість `Promise.all(items.map(...))`, щоб великий concern не відкрив
67
- * необмежену кількість конкурентних агентних сесій одразу.
68
- * @param {string[]} items елементи черги (шляхи файлів)
69
- * @param {(item: string) => Promise<void>} worker обробник одного елемента; винятки ловить сам
70
- * @returns {Promise<void>}
71
- */
72
- async function runPooled(items, worker) {
73
- const queue = [...items]
74
- const runnerCount = Math.min(MAX_PARALLEL_FILES, items.length)
75
- await Promise.all(
76
- Array.from({ length: runnerCount }, async () => {
77
- let item
78
- while ((item = queue.shift())) {
79
- await worker(item)
80
- }
81
- })
82
- )
83
- }
84
-
85
- /** @type {FixWorkerFn} */
86
- export async function fixWorker(violations, ctx) {
87
- // lazy import — тримає detect-шлях вільним від pi/oxc (read-only --no-fix не вантажить їх).
88
- const [{ runAgentFix }, { isLocalModel }, { extractContext }] = await Promise.all([
89
- import('@7n/llm-lib/agent-fix'),
90
- import('@7n/llm-lib/model-tiers'),
91
- import('../../../scripts/utils/ast-extract.mjs')
92
- ])
93
-
94
- /** @type {Map<string, import('../../../scripts/lib/lint-surface/types.mjs').LintViolation[]>} */
95
- const byFile = new Map()
96
- for (const v of violations) {
97
- if (!v.file) continue
98
- const arr = byFile.get(v.file)
99
- if (arr) arr.push(v)
100
- else byFile.set(v.file, [v])
101
- }
102
- const files = byFile.keys().toArray()
103
- if (files.length === 0) return { touchedFiles: [] }
104
-
105
- const deadlineAt = ctx.timeoutMs ? Date.now() + Math.round(ctx.timeoutMs * DEADLINE_FRACTION) : null
106
- const anchoredEdits = anchoredEnabled(ctx.model, isLocalModel)
107
-
108
- const touchedFiles = []
109
- await runPooled(files, async file => {
110
- if (deadlineAt && Date.now() >= deadlineAt) return
111
- const callTimeoutMs = deadlineAt ? Math.max(1000, deadlineAt - Date.now()) : ctx.timeoutMs
112
-
113
- let res
114
- try {
115
- res = await runAgentFix(ctx.ruleId, renderViolations(byFile.get(file)), ctx.cwd, {
116
- model: ctx.model,
117
- tier: ctx.tier,
118
- timeoutMs: callTimeoutMs,
119
- feedback: ctx.feedback ?? null,
120
- caller: `fix:${ctx.ruleId}/${ctx.concernId}:${ctx.tier}:${file}`,
121
- recordWrite: ctx.recordWrite,
122
- chain: ctx.chain ?? null,
123
- targetFiles: [file],
124
- verify: () => verifyFile(ctx.cwd, ctx.ruleId, ctx.concernId, file),
125
- verifyMax: ctx.verifyMax,
126
- anchoredEdits,
127
- deps: { astContext: p => extractContext(resolve(ctx.cwd, p)) }
128
- })
129
- } catch {
130
- // Один файл впав винятком — не валимо решту паралельних воркерів у пулі; whole-concern
131
- // canonical re-detect runner-а (не цей worker) визначить, чи rung закрито.
132
- return
133
- }
134
-
135
- if (!res.error) touchedFiles.push(...(res.touchedFiles ?? []))
136
- })
137
-
138
- return { touchedFiles }
139
- }
@@ -1,115 +0,0 @@
1
- /**
2
- * lint-поверхня js/eslint: read-only detector (oxlint + eslint). Fix (oxlint --fix /
3
- * eslint --fix) — окремий T0 `fix-eslint.mjs` (детермінований), не в detector-і.
4
- */
5
- import { resolve, relative } from 'node:path'
6
-
7
- import { ESLint } from 'eslint'
8
-
9
- import { addedLinesByFile } from '../../../scripts/lib/diff-added-lines.mjs'
10
- import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
11
- import { WORKTREE_CHECKOUT_GLOBS } from '../../../scripts/utils/walkDir.mjs'
12
- import { classifyFindings, eslintResultsToFindings, parseOxlint } from '../lint-findings/main.mjs'
13
-
14
- const JS_EXT_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx|vue)$/u
15
-
16
- /**
17
- * @param {string[]} files список шляхів
18
- * @returns {string[]} лише JS-подібні файли
19
- */
20
- export function filterJsFiles(files) {
21
- return files.filter(f => JS_EXT_RE.test(f))
22
- }
23
-
24
- /**
25
- * Async (не блокує event loop) — детектор може виконуватись у parallel lane `detectAll()`
26
- * (ADR 260716-1354).
27
- * @param {string[]} args аргументи запуску oxlint
28
- * @param {string} cwd робочий каталог
29
- * @returns {Promise<{ status: number, stdout: string, stderr: string }>} код завершення, stdout і stderr процесу
30
- */
31
- async function runOxlintJson(args, cwd) {
32
- const r = await spawnAsync('bunx', args, { cwd })
33
- return { status: typeof r.exitCode === 'number' ? r.exitCode : 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' }
34
- }
35
-
36
- /**
37
- * Finding → LintViolation.
38
- * @param {{ file: string, line: number, rule: string, message: string, tool: string }} f знахідка лінтера
39
- * @param {string} cwd робочий каталог
40
- * @param {'error'|'warn'} severity рівень порушення
41
- * @returns {import('../../../scripts/lib/lint-surface/types.mjs').LintViolation} нормалізоване порушення
42
- */
43
- function toViolation(f, cwd, severity) {
44
- return /** @type {import('../../../scripts/lib/lint-surface/types.mjs').LintViolation} */ ({
45
- reason: f.rule || `${f.tool}-error`,
46
- message: `${f.message} (${f.tool})`,
47
- file: relative(cwd, f.file).split('\\').join('/'),
48
- severity,
49
- data: { line: f.line, tool: f.tool }
50
- })
51
- }
52
-
53
- /**
54
- * Збирає findings (oxlint json + eslint read-only) по заданих файлах або всьому проєкту.
55
- * @param {string[]|null} js null → весь проєкт
56
- * @param {string} cwd робочий каталог
57
- * @returns {Promise<Array<{ file: string, line: number, rule: string, message: string, tool: string }>>} зібрані знахідки oxlint+eslint
58
- */
59
- async function collectFindings(js, cwd) {
60
- // warnIgnored:false — файли з delta, що матчать ignore-патерни конфігу, не є порушеннями.
61
- // overrideConfig лише з ignores → global ignores: worktree-чекаути (.worktrees/,
62
- // .claude/worktrees/) — копії репо, споживацький eslint-конфіг їх не виключає.
63
- const eslint = new ESLint({ cwd, warnIgnored: false, overrideConfig: { ignores: WORKTREE_CHECKOUT_GLOBS } })
64
- const esResults = await eslint.lintFiles(js === null ? [cwd] : js.map(f => resolve(cwd, f)))
65
- const es = eslintResultsToFindings(esResults)
66
-
67
- const worktreeIgnoreArgs = WORKTREE_CHECKOUT_GLOBS.map(g => `--ignore-pattern=${g}`)
68
- const oxArgs =
69
- js === null
70
- ? ['oxlint', '--format=json', ...worktreeIgnoreArgs]
71
- : ['oxlint', '--format=json', ...worktreeIgnoreArgs, ...js]
72
- const oxRes = await runOxlintJson(oxArgs, cwd)
73
- const ox = parseOxlint(oxRes.stdout)
74
- if (ox === null && oxRes.status !== 0) {
75
- // Хвости stdout/stderr — інакше на CI причина крашу (OOM, конфіг, версія) невидима.
76
- const tail = [oxRes.stderr, oxRes.stdout]
77
- .map(t => t.trim().slice(-500))
78
- .filter(t => t !== '')
79
- .join('\n--- stdout: ')
80
- const suffix = tail === '' ? '' : `\n${tail}`
81
- throw new Error(
82
- `oxlint завершився з помилкою (exit ${oxRes.status}, не lint-порушення) — json не розпарсено${suffix}`
83
- )
84
- }
85
- return [...(ox ?? []), ...es]
86
- }
87
-
88
- /**
89
- * Detector js/eslint: per-file (classify introduced/pre-existing) або full-project.
90
- * @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
91
- * @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} перелік порушень
92
- */
93
- export async function lint(ctx) {
94
- const { cwd, files } = ctx
95
-
96
- if (files === undefined) {
97
- const findings = await collectFindings(null, cwd)
98
- return { violations: findings.map(f => toViolation(f, cwd, 'error')) }
99
- }
100
-
101
- const js = filterJsFiles(files)
102
- if (js.length === 0) return { violations: [] }
103
-
104
- const findings = await collectFindings(js, cwd)
105
- if (findings.length === 0) return { violations: [] }
106
-
107
- // introduced (на доданих рядках) → error; pre-existing → warn.
108
- const classified = classifyFindings(findings, addedLinesByFile(js, cwd), cwd)
109
- return {
110
- violations: [
111
- ...classified.introduced.map(f => toViolation(f, cwd, 'error')),
112
- ...classified.preExisting.map(f => toViolation(f, cwd, 'warn'))
113
- ]
114
- }
115
- }
@@ -1,3 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"
3
- }
@@ -1,12 +0,0 @@
1
- ## Розширення нових файлів — `.mjs` / `.cjs`, не `.js`
2
-
3
- **Нові** JS-файли створюй з явним розширенням модуля:
4
-
5
- - **`.mjs`** — для ESM (типовий випадок);
6
- - **`.cjs`** — для CommonJS, де він справді потрібен.
7
-
8
- Голий **`.js`** для нового файлу **заборонено**. Розширення `.js` інтерпретується як ESM чи CJS лише за полем `package.json#type`, тож той самий файл читається по-різному залежно від пакета. Явне `.mjs`/`.cjs` робить тип модуля однозначним **без читання `package.json`** — навіть якщо `type` зміниться або файл перемістять в інший пакет. Це доповнює вимогу `"type": "module"` у [package_json.mdc](../package_json/package_json.mdc): `type` лишається каноном для всього дерева, а розширення нового файлу прибирає залежність від нього.
9
-
10
- Стосується **backend і frontend** — будь-який новий вихідний файл: `src/`, тести `*.test.*`, `scripts/`, `src/conn/` тощо.
11
-
12
- **Існуючі `.js` лишаються як є** — масово перейменовувати не треба; це конвенція для нового коду. Автоматичної перевірки тут немає: stateless-скан не відрізнить новий файл від існуючого, тож `.js` нікого не фейлить.
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".jscpd.json",
7
- "required": true
8
- },
9
- "missingMessage": ".jscpd.json не існує — створи з полями згідно js.mdc"
10
- }
11
- }
@@ -1,26 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-jscpd_config.mjs
4
- resource: npm/rules/js/jscpd_config/fix-jscpd_config.mjs
5
- docgen:
6
- crc: 93258f26
7
- model: openai-codex/gpt-5.4-mini
8
- tier: cloud-min
9
- score: 100
10
- judgeModel: openai-codex/gpt-5.4-mini
11
- ---
12
-
13
- ## Огляд
14
-
15
- `patterns` визначає узгоджені з правилами проєкту зміни для `.jscpd.json` і слугує централізованим орієнтиром для того, що саме має бути виправлено в цьому конфігу.
16
-
17
- ## Поведінка
18
-
19
- 1. `patterns` формує перелік правил для уніфікації шаблонного конфіга проєкту.
20
- 2. Кожне правило орієнтується на конфіг `.jscpd.json`, щоб тримати його в узгодженому, очікуваному форматі.
21
- 3. `patterns` потрібен для централізованого опису того, що саме має бути виправлено в `.jscpd.json`, без ручного втручання в інші файли чи артефакти.
22
- 4. `patterns` не змінює файлову систему або базу даних і працює лише як опис набору правок для цього конфіга.
23
-
24
- ## Гарантії поведінки
25
-
26
- - Read-only: не виконує операцій запису (ФС/БД).
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/js/jscpd_config
4
- resource: npm/rules/js/jscpd_config/
5
- ---
6
-
7
- | Файл | Тип |
8
- | ------------------------------------------- | --------- |
9
- | [fix-jscpd_config.mjs](fix-jscpd_config.md) | JS Module |
@@ -1,3 +0,0 @@
1
- import { createTemplateFixPattern } from '../../../scripts/lib/fix/template-deep-merge.mjs'
2
-
3
- export const patterns = [createTemplateFixPattern({ id: 'js-jscpd_config-template', targetPath: '.jscpd.json' })]
@@ -1,42 +0,0 @@
1
- ## Rego-gate `.jscpd.json`
2
-
3
- Rego-пакет: `js_lint.jscpd`
4
-
5
- Цільовий файл: `.jscpd.json` у корені проєкту.
6
-
7
- Що перевіряється:
8
-
9
- - `gitignore` — точна відповідність (`true`)
10
- - `exitCode` — точна відповідність (`1`)
11
- - `reporters` — subset-of: масив має містити всі елементи з канону (`["console"]`)
12
- - `minLines` — число, значення >= канонічного порогу (`25`); більше дозволено
13
-
14
- Канон-snippet: [.jscpd.json.snippet.json](./template/.jscpd.json.snippet.json)
15
-
16
- ### Ігнорування `.claude/worktrees/`
17
-
18
- Каталог `.claude/worktrees/` (робочі копії, які Claude Code створює через **superpowers:using-git-worktrees**) має ігноруватися:
19
-
20
- - додай `.claude/worktrees/` у кореневий `.gitignore` (штатне місце для не-комітних робочих копій);
21
- - у `.jscpd.json` додай `.claude/worktrees/**` у `ignore` як страховку на випадок запуску без `gitignore: true`.
22
-
23
- Без цього jscpd сканує паралельну копію репо в worktree і фіксує самозбіги між дзеркальними файлами.
24
-
25
- ```text title=".gitignore (фрагмент)"
26
- .claude/worktrees/
27
- ```
28
-
29
- ### Ігнорування `**/CHANGELOG.md`
30
-
31
- `**/CHANGELOG.md` у каноні `ignore`: release-журнали різних пакетів структурно повторюються (заголовки `## [x.y.z] - YYYY-MM-DD`, секції `### Added / Changed / Fixed` за Keep a Changelog), і `jscpd` за `minLines: 25` фіксує їх як клон — це false positive. Без цього в монорепо легко зловити критичний `bun run lint` на парі CHANGELOG-ів довжиною від ~25 рядків.
32
-
33
- ### Рефакторинг vs конфіг
34
-
35
- Коли **jscpd** знаходить клони, спочатку зменшуй дублювання кодом, а не конфігом.
36
-
37
- - **Рефакторинг:** винеси спільні фрагменти в функції, модулі, утиліти, composables/hooks, спільні компоненти або базові типи — залежно від контексту.
38
- - **Структура:** якщо одна й та сама логіка розмазана між файлами чи пакетами, запропонуй зміну структури (наприклад, спільний модуль, `shared/`, внутрішній пакет у monorepo), щоб була **одна канонічна реалізація** і повторні місця лише викликали її.
39
-
40
- Розширення `ignore` чи завищення `minLines` лише щоб прибрати звіт — не заміна рефакторингу для справжніх клонів. Якщо збіг **семантично випадковий** (генерований код, формальні шаблони без спільної логіки), після оцінки допустимо точковий `ignore` або зміна порогу — з коротким обґрунтуванням.
41
-
42
- Перед рефакторингом перевір чи є тести на блоки які підлягають зміні (Bun.test для js, playwright для vue). Якщо тестів немає або вони не покривають — спочатку створи їх, перевір що вони відпрацьовують коректно, потім роби рефакторинг і ще раз запускай тести.
@@ -1,44 +0,0 @@
1
- # Перевірка `.jscpd.json` (js.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "snippet": ... } }
4
- # Структура --data сформована з template/.jscpd.json.snippet.json.
5
- # Особливості:
6
- # - `reporters` — subset-of (масив string)
7
- # - `minLines` — >= expected (semantic: дозволяється більше, не менше)
8
- # - `gitignore`/`exitCode` — exact match
9
- package js.jscpd_config
10
-
11
- import rego.v1
12
-
13
- # Top-level scalar leafs (exact match) для gitignore + exitCode.
14
- deny contains msg if {
15
- some key, expected_value in data.template.snippet
16
- key != "minLines"
17
- not is_array(expected_value)
18
- actual := object.get(input, key, null)
19
- actual != expected_value
20
- msg := sprintf(".jscpd.json має містити \"%s\": %v (js.mdc)", [key, expected_value])
21
- }
22
-
23
- # Array subset-of для reporters.
24
- deny contains msg if {
25
- some field, expected_values in data.template.snippet
26
- is_array(expected_values)
27
- actual_set := {v | some v in object.get(input, field, [])}
28
- some required in expected_values
29
- not required in actual_set
30
- msg := sprintf(".jscpd.json має містити \"%s\": [\"%s\"] (js.mdc)", [field, required])
31
- }
32
-
33
- # minLines: must be number and >= expected.
34
- deny contains msg if {
35
- expected := data.template.snippet.minLines
36
- actual := object.get(input, "minLines", null)
37
- not is_valid_min_lines(actual, expected)
38
- msg := sprintf(".jscpd.json має містити \"minLines\" як число >= %d (js.mdc)", [expected])
39
- }
40
-
41
- is_valid_min_lines(actual, expected) if {
42
- is_number(actual)
43
- actual >= expected
44
- }
@@ -1,7 +0,0 @@
1
- {
2
- "gitignore": true,
3
- "exitCode": 1,
4
- "reporters": ["console"],
5
- "minLines": 25,
6
- "ignore": [".claude/worktrees/**", "**/dist/**", "**/CHANGELOG.md"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "structural",
4
- "lint": {
5
- "scope": "full"
6
- }
7
- }
@@ -1,29 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: main.mjs
4
- resource: npm/rules/js/jscpd_duplicates/main.mjs
5
- docgen:
6
- crc: 8839c5e9
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 100
9
- issues: judge:inaccurate:0.99
10
- judgeModel: openai-codex/gpt-5.4-mini
11
- ---
12
-
13
- ## Огляд
14
-
15
- Публічна функція `lint` аналізує вихідний код у вказаному кореневому каталозі, використовуючи `jscpd`, щоб виявляти дублікати та інші порушення стилю і логіки коду. Функція повертає код виходу, який інформує про успішне завершення аналізу чи виявлення проблем.
16
-
17
- ## Поведінка
18
-
19
- 1. Викликається функція `lint`, яка ініціює перевірку коду у вказаному кореневому каталозі з метою виявлення дублікатів.
20
- 2. Процес аналізу виконується за допомогою `jscpd` у середовищі `bunx`.
21
- 3. Функція повертає код завершення процесу: 0 у разі успішного виконання (без знайдених проблем) або інше число, що вказує на помилки.
22
-
23
- ## Публічний API
24
-
25
- lint — виявляє дублікати коду за допомогою `bunx jscpd` у файлах JavaScript/JSCPD.
26
-
27
- ## Гарантії поведінки
28
-
29
- - Read-only: не виконує операцій запису (ФС/БД).