@nitra/cursor 12.6.0 → 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 (269) hide show
  1. package/.claude-template/settings.template.json +1 -1
  2. package/CHANGELOG.md +16 -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/text/text.mdc +5 -14
  141. package/rules/tool-surface/docs/index.md +1 -0
  142. package/rules/tool-surface/docs/main.md +29 -0
  143. package/rules/tool-surface/main.mjs +20 -0
  144. package/rules/vue/docs/index.md +1 -0
  145. package/rules/vue/docs/main.md +29 -0
  146. package/rules/vue/main.mjs +20 -0
  147. package/rules/worktree/docs/index.md +1 -0
  148. package/rules/worktree/docs/main.md +28 -0
  149. package/rules/worktree/main.mjs +20 -0
  150. package/scripts/docs/index.md +1 -0
  151. package/scripts/docs/post-tool-use-check.md +29 -0
  152. package/scripts/docs/sync-claude-config.md +64 -92
  153. package/scripts/lib/adr/docs/normalize-cli.md +0 -3
  154. package/scripts/lib/adr/docs/normalize-pipeline.md +0 -3
  155. package/scripts/lib/docs/gha-workflow.md +25 -317
  156. package/scripts/lib/docs/index.md +1 -0
  157. package/scripts/lib/docs/list-project-rules-mdc.md +5 -4
  158. package/scripts/lib/docs/list-rule-ids.md +15 -148
  159. package/scripts/lib/docs/read-n-cursor-config-lite.md +12 -16
  160. package/scripts/lib/docs/run-lint-step.md +13 -13
  161. package/scripts/lib/docs/run-lint.md +30 -0
  162. package/scripts/lib/docs/run-rule-cli.md +14 -10
  163. package/scripts/lib/docs/run-standard-lint.md +27 -10
  164. package/scripts/lib/docs/run-standard-rule.md +12 -11
  165. package/scripts/lib/docs/timing-summary.md +11 -12
  166. package/scripts/lib/docs/worktree-notice.md +0 -3
  167. package/scripts/lib/fix/docs/index.md +1 -0
  168. package/scripts/lib/fix/docs/orchestrator.md +23 -18
  169. package/scripts/lib/fix/docs/run-conformance-check.md +32 -0
  170. package/scripts/lib/fix/docs/t0.md +10 -9
  171. package/scripts/lib/fix/orchestrator.mjs +5 -5
  172. package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
  173. package/scripts/lib/fix/t0.mjs +3 -3
  174. package/scripts/lib/gha-workflow.mjs +1 -1
  175. package/scripts/lib/list-project-rules-mdc.mjs +1 -1
  176. package/scripts/lib/list-rule-ids.mjs +12 -3
  177. package/scripts/lib/read-n-cursor-config-lite.mjs +2 -2
  178. package/scripts/lib/run-lint-step.mjs +1 -1
  179. package/{rules/lint/js/orchestrate.mjs → scripts/lib/run-lint.mjs} +42 -20
  180. package/scripts/lib/run-rule-cli.mjs +4 -4
  181. package/scripts/lib/run-standard-lint.mjs +19 -6
  182. package/scripts/lib/run-standard-rule.mjs +4 -4
  183. package/scripts/lib/timing-summary.mjs +1 -1
  184. package/scripts/{post-tool-use-fix.mjs → post-tool-use-check.mjs} +9 -9
  185. package/scripts/sync-claude-config.mjs +2 -2
  186. package/rules/changelog/fix.mjs +0 -18
  187. package/rules/ci4/fix.mjs +0 -18
  188. package/rules/doc-files/fix.mjs +0 -19
  189. package/rules/doc-files/js/docs/lint.md +0 -34
  190. package/rules/doc-files/lint/docs/index.md +0 -11
  191. package/rules/doc-files/lint/docs/lint.md +0 -35
  192. package/rules/docker/fix.mjs +0 -18
  193. package/rules/docker/lint/docs/index.md +0 -11
  194. package/rules/docker/lint/docs/lint.md +0 -200
  195. package/rules/docker/lint/lint.mjs +0 -95
  196. package/rules/efes/fix.mjs +0 -18
  197. package/rules/feedback/fix.mjs +0 -18
  198. package/rules/ga/fix.mjs +0 -18
  199. package/rules/ga/js/docs/lint.md +0 -20
  200. package/rules/ga/js/lint.mjs +0 -12
  201. package/rules/ga/lint/docs/index.md +0 -11
  202. package/rules/ga/lint/docs/lint.md +0 -31
  203. package/rules/graphql/fix.mjs +0 -18
  204. package/rules/hasura/fix.mjs +0 -18
  205. package/rules/image-avif/fix.mjs +0 -18
  206. package/rules/image-compress/fix.mjs +0 -18
  207. package/rules/image-compress/js/docs/lint.md +0 -24
  208. package/rules/js-bun-db/fix.mjs +0 -18
  209. package/rules/js-bun-redis/fix.mjs +0 -18
  210. package/rules/js-lint/fix.mjs +0 -18
  211. package/rules/js-lint/js/docs/lint.md +0 -32
  212. package/rules/js-lint-ci/fix.mjs +0 -18
  213. package/rules/js-lint-ci/js/docs/lint.md +0 -22
  214. package/rules/js-lint-ci/js/lint.mjs +0 -15
  215. package/rules/js-mssql/fix.mjs +0 -18
  216. package/rules/js-run/fix.mjs +0 -18
  217. package/rules/k8s/fix.mjs +0 -18
  218. package/rules/k8s/js/lint.mjs +0 -14
  219. package/rules/k8s/lint/docs/index.md +0 -11
  220. package/rules/k8s/lint/docs/lint.md +0 -413
  221. package/rules/lint/docs/fix.md +0 -25
  222. package/rules/lint/docs/index.md +0 -11
  223. package/rules/lint/fix.mjs +0 -18
  224. package/rules/lint/js/docs/index.md +0 -11
  225. package/rules/lint/js/docs/orchestrate.md +0 -31
  226. package/rules/lint/meta.json +0 -1
  227. package/rules/nginx-default-tpl/fix.mjs +0 -18
  228. package/rules/npm-module/fix.mjs +0 -18
  229. package/rules/php/fix.mjs +0 -18
  230. package/rules/php/js/docs/lint.md +0 -20
  231. package/rules/php/js/lint.mjs +0 -15
  232. package/rules/php/lint/docs/index.md +0 -11
  233. package/rules/php/lint/docs/lint.md +0 -219
  234. package/rules/python/fix.mjs +0 -18
  235. package/rules/python/js/docs/lint.md +0 -21
  236. package/rules/python/js/lint.mjs +0 -14
  237. package/rules/python/lint/docs/index.md +0 -11
  238. package/rules/python/lint/docs/lint.md +0 -29
  239. package/rules/rego/fix.mjs +0 -18
  240. package/rules/rego/js/docs/lint.md +0 -21
  241. package/rules/rego/js/lint.mjs +0 -12
  242. package/rules/rego/lint/docs/index.md +0 -11
  243. package/rules/rego/lint/docs/lint.md +0 -208
  244. package/rules/rust/fix.mjs +0 -18
  245. package/rules/rust/js/docs/lint.md +0 -21
  246. package/rules/security/fix.mjs +0 -18
  247. package/rules/security/js/docs/lint.md +0 -175
  248. package/rules/security/js/lint.mjs +0 -26
  249. package/rules/style-lint/fix.mjs +0 -18
  250. package/rules/style-lint/js/docs/lint.md +0 -31
  251. package/rules/tauri/fix.mjs +0 -18
  252. package/rules/test/fix.mjs +0 -18
  253. package/rules/text/fix.mjs +0 -18
  254. package/rules/text/js/docs/lint.md +0 -23
  255. package/rules/text/js/lint.mjs +0 -15
  256. package/rules/text/lint/docs/cspell-fix.md +0 -32
  257. package/rules/text/lint/docs/index.md +0 -15
  258. package/rules/text/lint/docs/lint.md +0 -36
  259. package/rules/text/lint/docs/run-dotenv-linter.md +0 -161
  260. package/rules/text/lint/docs/run-shellcheck.md +0 -216
  261. package/rules/text/lint/docs/run-v8r.md +0 -201
  262. package/rules/tool-surface/fix.mjs +0 -18
  263. package/rules/vue/fix.mjs +0 -18
  264. package/rules/worktree/fix.mjs +0 -18
  265. /package/rules/release/{fix.mjs → main.mjs} +0 -0
  266. /package/rules/text/{lint → js}/cspell-fix.mjs +0 -0
  267. /package/rules/text/{lint → js}/run-dotenv-linter.mjs +0 -0
  268. /package/rules/text/{lint → js}/run-shellcheck.mjs +0 -0
  269. /package/rules/text/{lint → js}/run-v8r.mjs +0 -0
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: npm/rules/changelog/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 ініціює процес виконання правила, включаючи кешування даних у межах одного прогону.
14
+
15
+ ## Поведінка
16
+
17
+ 1. Викликається для перевірки. Виконує застосування політики, перевірку JS-зацікавленостей та посилання на MDC.
18
+ 2. Якщо код виконується як окрема програма (CLI), запускається повний процес виконання правила, включаючи завантаження конфігурацій та підсумок.
19
+
20
+ ## Публічний API
21
+
22
+ run — виконує основну логіку правила: застосовує перевірки до коду, аналізує відповідність політикам та посиланням на метадані.
23
+
24
+ ## Гарантії поведінки
25
+
26
+ - Read-only: не виконує операцій запису (ФС/БД).
27
+ - Кешує результати в межах одного прогону.
@@ -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/ci4/
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/ci4/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, тобто не здійснює записів у файлову систему чи бази даних. Кешування даних відбувається у межах одного прогону.
14
+
15
+ ## Поведінка
16
+
17
+ 1. Викликається функція `run` для виконання перевірки.
18
+ 2. Виконання `run` застосовує логіку правила, включаючи обробку JS-запитань, політику та посилання на MDC.
19
+ 3. Якщо код виконується як окрема програма (CLI), ініціюється повний запуск правила.
20
+ 4. Повний запуск правила зчитує конфігурацію з `meta.json`, застосовує білий список та надає зведену інформацію.
21
+ 5. Результат виконання визначає код виходу процесу: 0 для успіху або 1 для виявлення порушень.
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/doc-files/
9
9
  | Файл | Тип |
10
10
  |---|---|
11
11
  | [fix.mjs](fix.md) | JS Module |
12
+ | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,31 @@
1
+ ---
2
+ type: JS Module
3
+ title: main.mjs
4
+ resource: npm/rules/doc-files/main.mjs
5
+ docgen:
6
+ crc: 64f215a5
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Модуль перевіряє відповідність коду та документації, спираючись на конфігурацію `meta.json`. Функція `run` перевіряє консистентність між кодом та документацією. Функція `lint` перевіряє застарілі описи документації.
14
+
15
+ ## Поведінка
16
+
17
+ run виконує перевірку консистентності між кодом та документацією.
18
+
19
+ lint перевіряє, чи не застаріли описи документації, і може автоматично їх оновлювати, якщо це дозволено конфігурацією `meta.json`.
20
+
21
+ ## Публічний API
22
+
23
+ run — виконує основну перевірку, яка включає аналіз JS-задач, застосування політик та посилання на MDC.
24
+ lint — сканує файли документації на наявність застарілих елементів, з можливістю автоматичного виправлення за умови активації LLM.
25
+ llmFix — виконує добровільну генерацію виправлень за допомогою LLM, якщо це дозволено в конфігурації.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
30
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
31
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
@@ -1,12 +1,12 @@
1
1
  /** @see ./docs/docgen-files-batch.md */
2
- import { readFileSync, readdirSync, mkdirSync, writeFileSync, existsSync, statSync } from 'node:fs'
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs'
3
3
  import { basename, dirname, join, relative } from 'node:path'
4
4
 
5
5
  import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
6
6
  import { classifyOmlxError, preflightLocalModel } from '../../../lib/llm.mjs'
7
7
  import { generateDoc, DEFAULT_LOCAL_MODEL } from './docgen-gen.mjs'
8
8
  import { crc32, stampDoc, readDocQuality, readDocModel, QUALITY_THRESHOLD } from './docgen-crc.mjs'
9
- import { resolveRoot, scanForDocFiles } from './docgen-scan.mjs'
9
+ import { resolveRoot, scanForDocFiles, scanOrphanedDocs } from './docgen-scan.mjs'
10
10
 
11
11
  /**
12
12
  * Парсить `--limit N` / `--from N` / прапори режимів для дозапуску великого прогону.
@@ -215,6 +215,42 @@ function reportStats(stats) {
215
215
  }
216
216
  }
217
217
 
218
+ /**
219
+ * Видаляє сирітські доки (source-файл не існує) і оновлює/прибирає index.md.
220
+ * Якщо після видалення в docs/-директорії лишились тільки index.md або нічого — очищує її.
221
+ * @param {string} root абсолютний корінь
222
+ * @returns {number} кількість видалених doc-файлів
223
+ */
224
+ export function purgeOrphanedDocs(root) {
225
+ const orphans = scanOrphanedDocs(root)
226
+ if (orphans.length === 0) return 0
227
+ let deleted = 0
228
+ const docsDirs = new Set()
229
+ for (const docRel of orphans) {
230
+ try {
231
+ unlinkSync(join(root, docRel))
232
+ docsDirs.add(dirname(join(root, docRel)))
233
+ deleted++
234
+ } catch {
235
+ // race condition або вже видалено — ігноруємо
236
+ }
237
+ }
238
+ for (const docsAbsDir of docsDirs) {
239
+ if (!existsSync(docsAbsDir)) continue
240
+ const remaining = readdirSync(docsAbsDir)
241
+ const docFiles = remaining.filter(f => f.endsWith('.md') && f !== 'index.md')
242
+ if (docFiles.length === 0) {
243
+ // Лише index.md або порожня директорія — прибираємо повністю
244
+ const indexPath = join(docsAbsDir, 'index.md')
245
+ if (existsSync(indexPath)) unlinkSync(indexPath)
246
+ try { rmdirSync(docsAbsDir) } catch { /* не порожня — пропускаємо */ }
247
+ } else {
248
+ generateDirIndex(docsAbsDir, root)
249
+ }
250
+ }
251
+ return deleted
252
+ }
253
+
218
254
  /**
219
255
  * `doc-files gen` — згенерувати документацію для застарілих/відсутніх док.
220
256
  * @param {string[]} argv аргументи після назви субкоманди
@@ -224,11 +260,19 @@ export async function runDocFilesGenCli(argv) {
224
260
  const root = resolveRoot(argv)
225
261
  const { from, limit, overwrite } = parseGenArgs(argv)
226
262
 
263
+ // Видаляємо orphan-доки до генерації (незалежно від наявності stale)
264
+ const deleted = purgeOrphanedDocs(root)
265
+ if (deleted > 0) {
266
+ console.log(`🗑 doc-files: видалено ${deleted} сирітських доки(ів)`)
267
+ }
268
+
227
269
  const all = scanForDocFiles(root)
228
270
  const targets = selectTargets(root, all, { overwrite }).slice(from, from + limit)
229
271
 
230
272
  if (targets.length === 0) {
231
- console.log('✓ doc-files: усі файлові доки свіжі й не-degraded. Нічого генерувати.')
273
+ if (deleted === 0) {
274
+ console.log('✓ doc-files: усі файлові доки свіжі й не-degraded. Нічого генерувати.')
275
+ }
232
276
  return 0
233
277
  }
234
278
 
@@ -1,13 +1,13 @@
1
1
  /** @see ./docs/docgen-scan.md */
2
2
  import { join, dirname, basename, extname, relative, resolve, sep, isAbsolute, posix } from 'node:path'
3
- import { existsSync, readdirSync, statSync } from 'node:fs'
3
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
4
4
  import { execFileSync } from 'node:child_process'
5
5
  import { once } from 'node:events'
6
6
  import { env } from 'node:process'
7
7
 
8
8
  import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
9
9
  import { isDocgenIgnored } from './docgen-ignore.mjs'
10
- import { QUALITY_THRESHOLD, readDocQuality, staleness } from './docgen-crc.mjs'
10
+ import { QUALITY_THRESHOLD, parseDocFrontmatter, readDocQuality, staleness } from './docgen-crc.mjs'
11
11
 
12
12
  /** Кодові розширення, для яких генеруємо документацію. */
13
13
  const SOURCE_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.vue', '.py', '.rs'])
@@ -78,6 +78,70 @@ export function describeFile(root, sourcePath) {
78
78
  return { sourcePath, docPath, stale, reason }
79
79
  }
80
80
 
81
+ /**
82
+ * Знаходить "сирітські" доки: `docs/<stem>.md` із `resource:` + `docgen.crc` у frontmatter,
83
+ * у яких відповідний source-файл (resource:) вже не існує. Перевіряє лише файли,
84
+ * згенеровані `fix-doc-files` (наявність `docgen.crc` у frontmatter). Directory Index
85
+ * (resource із `/` на кінці) та ручні доки без `resource:` або без CRC — ігноруються.
86
+ * @param {string} root абсолютний корінь обходу
87
+ * @returns {string[]} posix-шляхи сирітських doc-файлів від кореня
88
+ */
89
+ export function scanOrphanedDocs(root) {
90
+ const orphans = []
91
+
92
+ /** @param {string} docsAbsDir абсолютний шлях docs/-директорії */
93
+ function scanDocsDir(docsAbsDir) {
94
+ let entries
95
+ try {
96
+ entries = readdirSync(docsAbsDir, { withFileTypes: true })
97
+ } catch {
98
+ return
99
+ }
100
+ for (const entry of entries) {
101
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue
102
+ const fullPath = join(docsAbsDir, entry.name)
103
+ let content
104
+ try {
105
+ content = readFileSync(fullPath, 'utf8')
106
+ } catch {
107
+ continue
108
+ }
109
+ const { data } = parseDocFrontmatter(content)
110
+ // Пропускаємо: Directory Index (resource з `/`), ручні доки (немає resource або CRC)
111
+ if (!data?.source || data.source.endsWith('/') || !data.crc) continue
112
+ if (!existsSync(join(root, data.source))) {
113
+ orphans.push(relative(root, fullPath).split(sep).join('/'))
114
+ }
115
+ }
116
+ }
117
+
118
+ /** Обходить дерево, шукаючи docs/-директорії для orphan-перевірки.
119
+ * docs/ — входимо завжди (батьківська пройшла ignore-перевірку);
120
+ * інші — перевіряємо через isDocgenIgnored. */
121
+ function walk(dir) {
122
+ let entries
123
+ try {
124
+ entries = readdirSync(dir, { withFileTypes: true })
125
+ } catch {
126
+ return
127
+ }
128
+ for (const entry of entries) {
129
+ if (!entry.isDirectory()) continue
130
+ const fullPath = join(dir, entry.name)
131
+ if (entry.name === 'docs') {
132
+ scanDocsDir(fullPath)
133
+ } else {
134
+ const relPath = relative(root, fullPath).split(sep).join('/')
135
+ if (isDocgenIgnored(relPath, 'dir')) continue
136
+ walk(fullPath)
137
+ }
138
+ }
139
+ }
140
+
141
+ walk(root)
142
+ return orphans
143
+ }
144
+
81
145
  /**
82
146
  * Підмножина шляхів, які git вважає ігнорованими (`.gitignore` + global excludes).
83
147
  * Один батч-виклик `git check-ignore --stdin`. Tracked-файли git не репортить як
@@ -307,20 +371,36 @@ export async function runDocFilesCheckCli(argv) {
307
371
  }
308
372
 
309
373
  const stale = sources.map(src => describeFile(root, src)).filter(f => f.stale)
310
- if (stale.length === 0) return 0
374
+ // В git-режимі (Stop-гейт) додатково шукаємо сирітські доки без source-файлу
375
+ const orphans = gitMode ? scanOrphanedDocs(root) : []
311
376
 
312
- // Великий прогін: Stop-гейт не блокує, лише попереджає (захист від нескінченного блоку).
377
+ if (stale.length === 0 && orphans.length === 0) return 0
378
+
379
+ // Великий прогін stale: Stop-гейт не блокує, але orphan-check продовжуємо
313
380
  if (gitMode && stale.length > gateMax) {
314
381
  console.error(
315
382
  `⚠ doc-files: застарілих док ${stale.length} (> ${gateMax}) — гейт не блокує. Запусти масовий прогін:\n npx @nitra/cursor fix-doc-files`
316
383
  )
317
- return 0
384
+ if (orphans.length === 0) return 0
385
+ const oList = orphans.map(f => ` - ${f}`).join('\n')
386
+ console.error(
387
+ `✗ doc-files: сирітських доків (source видалено) ${orphans.length}:\n${oList}\n→ очисти: npx @nitra/cursor fix-doc-files`
388
+ )
389
+ return 2
318
390
  }
319
391
 
320
- const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
321
- console.error(
322
- `✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: /doc-files`
323
- )
392
+ if (stale.length > 0) {
393
+ const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
394
+ console.error(
395
+ `✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: /doc-files`
396
+ )
397
+ }
398
+ if (orphans.length > 0) {
399
+ const oList = orphans.map(f => ` - ${f}`).join('\n')
400
+ console.error(
401
+ `✗ doc-files: сирітських доків (source видалено) ${orphans.length}:\n${oList}\n→ очисти: npx @nitra/cursor fix-doc-files`
402
+ )
403
+ }
324
404
  return 2
325
405
  }
326
406
 
@@ -3,31 +3,31 @@ type: JS Module
3
3
  title: docgen-files-batch.mjs
4
4
  resource: npm/rules/doc-files/js/docgen-files-batch.mjs
5
5
  docgen:
6
- crc: 18c96a58
7
- score: 95
6
+ crc: f68c0832
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
8
9
  ---
9
10
 
10
- runDocFilesGenCli
11
- Запускає генерацію документації для застарілих або відсутніх файлів.
11
+ ## Огляд
12
12
 
13
- runDocFilesStampCli
14
- Перештампує frontmatter джерело та CRC у наявних документах без виклику LLM.
13
+ Модуль керує життєвим циклом документації. Він вибирає цільові документи для оновлення, видаляє непов'язані документи та виконує пакетну генерацію. Модуль забезпечує стійкість роботи, перехоплюючи помилки та не кидаючи винятків назовні. Він також детерміновано оновлює метадані існуючих документів.
15
14
 
16
15
  ## Поведінка
17
16
 
18
- runDocFilesGenCli
19
- Запускає генерацію документації для застарілих/відсутніх док.
20
-
21
- runDocFilesStampCli
22
- Перештампує frontmatter source+crc у наявних доках без виклику LLM.
17
+ selectTargets фільтрує знайдені файли, вибираючи ті, що застарілі або мають низьку якість і не були спробовані раніше.
18
+ purgeOrphanedDocs видаляє доки, для яких не існує відповідного джерела, і оновлює індекси директорій.
19
+ runDocFilesGenCli ініціює генерацію документації, видаляючи сирітських доків, вибираючи цілі та запускаючи пакетну генерацію.
20
+ runGenerationBatch виконує послідовний прогін цільових файлів, генеруючи документацію та керуючи цим процесом через механізм захисту від збоїв.
21
+ runDocFilesStampCli детерміновано оновлює метадані (frontmatter) існуючих доків, додаючи CRC та зберігаючи якість, без виклику LLM.
23
22
 
24
23
  ## Публічний API
25
24
 
26
- - runDocFilesGenCli згенерувати документацію для застарілих/відсутніх док.
27
- - runDocFilesStampCli детерміновано (пере)штампувати frontmatter `source`+`crc` у наявних доках без виклику LLM. Для міграції док, які ще не мають CRC. Поля якості (`score`/`issues`) зберігаються з наявного frontmatter.
25
+ selectTargetsВизначає цілі для генерації: застарілі або погіршені документи, які ще не пройшли перевірку CRC, або всі документи при використанні прапора перезапису.
26
+ purgeOrphanedDocsВидаляє документи, для яких відсутній вихідний файл, та оновлює/очищає файл `index.md` у відповідній директорії.
27
+ runDocFilesGenCli — Генерує документацію для застарілих або відсутніх документів.
28
+ runGenerationBatch — Виконує послідовну генерацію документів: перевіряє локальний бекенд, обробляє цілі генерації з механізмом захисту від збоїв, і надає фінальний звіт.
29
+ runDocFilesStampCli — Додає або оновлює метадані `source` та `crc` до існуючих документів без залучення великих мовних моделей, використовуючи наявні дані про модель та якість.
28
30
 
29
31
  ## Гарантії поведінки
30
32
 
31
33
  - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
32
- - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
33
- - Не звертається до мережі.
@@ -3,50 +3,50 @@ type: JS Module
3
3
  title: docgen-scan.mjs
4
4
  resource: npm/rules/doc-files/js/docgen-scan.mjs
5
5
  docgen:
6
- crc: 4c17838f
6
+ crc: f01465d8
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
7
8
  score: 100
8
9
  ---
9
10
 
10
- isSourceFile перевіряє, чи є файл кодовим джерелом.
11
- docPathForSource обчислює шлях md-документа для кодового файлу.
12
- isDocCandidate перевіряє, чи підлягає файл документуванню.
13
- describeFile описує кодовий файл, включаючи шлях доки та стан застарілості.
14
- scanForDocFiles рекурсивно обходить дерево, повертаючи кандидати з інформацією про старілість.
15
- resolveRoot парсить аргументи, щоб визначити абсолютний корінь.
16
- runDocFilesScanCli сканує дерево і друкує JSON-масив усіх кодових файлів зі станом застарілості.
17
- runDocFilesCheckCli детектує застарілість для хук'ів, гейтів або інших режимів, повертаючи код виходу.
11
+ ## Огляд
12
+
13
+ Модуль аналізує кодову базу та пов'язану з нею документацію. Він визначає, які файли є джерелами за допомогою `isSourceFile` та знаходить відповідні шляхи документації за допомогою `docPathForSource`. Модуль перевіряє, чи не залишилися без зв'язку (сиротами) документи, використовуючи `scanOrphanedDocs`, та сканує файли документації за допомогою `scanForDocFiles`. Для роботи з кореневими каталогами використовується `resolveRoot`. Усі операції виконуються з механізмом перехоплення помилок (fail-safe), при цьому при певних збоях повертається `null` замість викидання винятків.
18
14
 
19
15
  ## Поведінка
20
16
 
21
- isSourceFile Обчислює, чи є файл кодовим джерелом.
22
- docPathForSource Обчислює шлях md-документа для кодового файлу.
23
- isDocCandidate Перевіряє, чи підлягає файл документуванню.
24
- describeFile Описує кодовий файл, включаючи шлях доки та стан застарілості.
25
- scanForDocFiles Рекурсивно обходить дерево, повертаючи кандидати з інформацією про старілість.
26
- resolveRoot Парсить аргументи, щоб визначити абсолютний корінь.
27
- runDocFilesScanCli Сканує дерево і друкує JSON-масив усіх кодових файлів зі станом застарілості.
28
- runDocFilesCheckCli Детектує застарілість для хук'ів, гейтів або інших режимів, повертаючи код виходу.
17
+ isSourceFile визначає, чи є ім'я файлу кодовим джерелом для документування.
18
+ docPathForSource обчислює шлях до відповідного MD-документа для заданого кодового файлу.
19
+ isDocCandidate визначає, чи підлягає певний файл документуванню, враховуючи розширення, статус тесту та ігнорування.
20
+ describeFile описує кодовий файл, надаючи його шлях, шлях до документації та стан застарілості.
21
+ scanOrphanedDocs знаходить MD-документи, які посилаються на кодові файли, що більше не існують.
22
+ scanForDocFiles рекурсивно обходить дерево і повертає список кодових файлів із їхнім станом застарілості, відфільтрований за ігноруванням Git.
23
+ resolveRoot парсить аргументи для визначення абсолютного кореня обходу, використовуючи поточну робочу директорію за замовчуванням.
24
+ runDocFilesScanCli сканує дерево і друкує JSON-масив усіх кодових файлів зі станом застарілості.
25
+ runDocFilesCheckCli виконує перевірку застарілості, підтримуючи режими для перевірки одного файлу, Git-гейту або звітів про знижену якість.
29
26
 
30
27
  ## Публічний API
31
28
 
32
- isSourceFile — перевіряє, чи є файл коду для документування.
33
- docPathForSource — обчислює шлях до документа для кодового файлу, розміщуючи його в теці `docs/` поруч із джерелом. Якщо шлях відносний, документ також відносний; якщо абсолютний, документ залишається абсолютним.
34
- isDocCandidate — визначає, чи підлягає кодовий файл документуванню: має правильне розширення, не є тестом, не знаходиться в ігнорованому списку, і не є кореневим системним документуванням.
35
- describeFile — надає опис кодового файлу: шлях до джерела, шлях до документа та стан застарілості за CRC.
36
- scanForDocFilesрекурсивно переглядає дерево від заданого кореня і повертає кодові файли разом зі станом застарілості.
37
- resolveRootпарсить аргумент `--root <dir>` з командного рядка; за замовчуванням використовує поточну робочу директорію.
38
- runDocFilesScanCliсканує дерево і виводить JSON-масив усіх кодових файлів із зазначенням їхнього стану застарілості.
39
- runDocFilesCheckCliвиконує перевірку застарілості для хуків та командного рядка через інструмент `doc-files check`.
40
- Режимице способи виконання:
41
- --hook — бере шлях до файлу з вводу JSON і перевіряє один файл.
42
- --git — перевіряє різницю в Git (`git diff --name-only HEAD`) з урахуванням порогу `--max` (за замовчуванням 50); якщо застарілості більше, не блокує (виходить з кодом 0 з попередженням).
43
- --degradedгенерує інформаційний звіт про документи, які мають оцінку нижче встановленого порогу (виходить з кодом 0).
44
- <paths…>використовується для визначення явних шляхів до джерел.
45
- Exit 2 повертається, якщо знайдено застарілі дані; повертається 0, якщо дані свіжі або пройдено перевищення порогу.
29
+ isSourceFile — визначає, чи є файл кодовим джерелом для створення документації.
30
+ docPathForSource — визначає шлях до відповідного MD-документа, розміщений у теці `docs/` відносно коду.
31
+ isDocCandidate — вирішує, чи повинен кодовий файл бути документований: має правильне розширення, не є тестом, не ігнорується та не є системним документом.
32
+ describeFile — надає опис кодового файлу, включаючи його шлях, шлях до документації та статус застарілості.
33
+ scanOrphanedDocsшукає документи, які посилаються на кодові файли, що більше не існують, перевіряючи лише згенеровані файли.
34
+ scanForDocFilesрекурсивно збирає всі кодові файли з дерева, визначаючи їхній статус застарілості, і відсіюючи ті, що ігноруються через `.gitignore`.
35
+ resolveRootвстановлює кореневу директорію для сканування, використовуючи аргумент командного рядка або поточну робочу директорію.
36
+ runDocFilesScanCliсканує дерево та виводить JSON-масив усіх кодових файлів зі статусом застарілості, дозволяючи фільтрувати лише застарілі або всі файли.
37
+ runDocFilesCheckCliвиконує перевірку застарілості для використання у хуках та командному інтерфейсі.
38
+
39
+ Режими:
40
+ --hookперевіряє один файл, отриманий з JSON через стандартний ввід.
41
+ --gitпорівнює файли у `git diff` та блокує процес, якщо знайдено більше за встановлений поріг застарілих файлів.
42
+ --degradedвиводить звіт про документи з низьким показником якості.
43
+ <paths…> — обробляє лише вказані шляхи-джерела.
44
+
45
+ Вихідний код 2 — вказує на знаходження застарілих файлів (для блокування хука).
46
+ Вихідний код 0 — вказує на відсутність застарілих файлів або проходження перевірки згідно з порогом.
46
47
 
47
48
  ## Гарантії поведінки
48
49
 
49
- - Read-only: файл не виконує операцій запису у файлову систему.
50
+ - Read-only: не виконує операцій запису (ФС/БД).
50
51
  - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
51
- - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
52
- - Не звертається до мережі.
52
+ - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
@@ -19,6 +19,7 @@ resource: npm/rules/doc-files/js/
19
19
  | [docgen-prompts.mjs](docgen-prompts.md) | JS Module |
20
20
  | [docgen-scan.mjs](docgen-scan.md) | JS Module |
21
21
  | [lint.mjs](lint.md) | JS Module |
22
+ | [run-lint.mjs](run-lint.md) | JS Module |
22
23
  | [units-js.mjs](units-js.md) | JS Module |
23
24
  | [units-rs.mjs](units-rs.md) | JS Module |
24
25
  | [units.mjs](units.md) | JS Module |
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: JS Module
3
+ title: run-lint.mjs
4
+ resource: npm/rules/doc-files/js/run-lint.mjs
5
+ docgen:
6
+ crc: ba329b37
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 80
9
+ ---
10
+
11
+ ## Огляд
12
+
13
+ Цей модуль надає CLI-обгортку для канонічного `lint-doc-files` (doc-files.mdc): детермінованого детектора застарілості файлових док (`<dir>/docs/<stem>.md`). При відсутності прапорців або при вказанні шляхів (`[paths…]`) виконується повний або точковий детекст застарілості. Якщо знайдено застарілі файли, повертається код виходу 1. Можливі режими: `--missing-only` звужує перевірку до відсутніх файлів. Прапорець `--json` виводить JSON-лістинг усіх кандидатів. Режими `--hook`, `--git` та `--degraded` делегують перевірку через `runDocFilesCheckCli` (hook-протокол: exit 2/0). Повний прогін здійснюється через `runStandardLint` (ключ `lint-doc-files`), тоді як хук/git/degraded форми використовують канон scripts.mdc, не генеруючи логів. Поведінка описується у (doc-files.mdc).
14
+
15
+ ## Поведінка
16
+
17
+ runLintDocFilesSteps виконує повний або точковий детект застарілості файлової документації (doc-files.mdc), повертаючи код виходу 1, якщо знайдено застарілі файли, або 0, якщо всі актуальні.
18
+ runLintDocFilesCli керує викликом лінтера doc-files.mdc: виконує повний/точковий детект, якщо не вказано `--json`, або повертає JSON-лістинг кандидатів, якщо вказано `--json`, або делегує перевірку через протоколи `--hook`, `--git`, `--degraded`.
19
+
20
+ ## Публічний API
21
+
22
+ runLintDocFilesSteps — Виконує повний або точковий пошук неактуальних (stale) файлів документації. Повертає код виходу 1, якщо знайдено неактуальні файли, і 0, якщо всі файли актуальні.
23
+ runLintDocFilesCli — Виконує перевірку файлів документації через інтерфейс командного рядка. У режимах hook/git/degraded виконує делегацію без логування. З опцією `--json` виконує сканування. В інших режимах виконує повний або точковий пошук, використовуючи логіку `runStandardLint` для ключа `lint-doc-files`.
24
+
25
+ ## Гарантії поведінки
26
+
27
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -23,8 +23,9 @@ import {
23
23
  resolveRoot,
24
24
  runDocFilesCheckCli,
25
25
  runDocFilesScanCli,
26
- scanForDocFiles
27
- } from '../js/docgen-scan.mjs'
26
+ scanForDocFiles,
27
+ scanOrphanedDocs
28
+ } from './docgen-scan.mjs'
28
29
 
29
30
  /**
30
31
  * Нормалізує шлях-кандидат до posix-шляху від кореня (null поза деревом).
@@ -73,15 +74,28 @@ export function runLintDocFilesSteps(argv) {
73
74
  let stale = described.filter(f => f.stale)
74
75
  if (missingOnly) stale = stale.filter(f => f.reason === 'missing')
75
76
 
76
- if (stale.length === 0) {
77
+ // Orphan-детект лише при повному скані (без явних шляхів); точковий — не релевантно
78
+ const orphans = paths.length === 0 ? scanOrphanedDocs(root) : []
79
+
80
+ let exitCode = 0
81
+ if (stale.length > 0) {
82
+ const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
83
+ console.error(
84
+ `✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: npx @nitra/cursor fix-doc-files`
85
+ )
86
+ exitCode = 1
87
+ }
88
+ if (orphans.length > 0) {
89
+ const list = orphans.map(f => ` - ${f}`).join('\n')
90
+ console.error(
91
+ `✗ doc-files: сирітських доків (source видалено) ${orphans.length}:\n${list}\n→ очисти: npx @nitra/cursor fix-doc-files`
92
+ )
93
+ exitCode = 1
94
+ }
95
+ if (exitCode === 0) {
77
96
  console.log('✓ doc-files: усі файлові доки актуальні.')
78
- return 0
79
97
  }
80
- const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
81
- console.error(
82
- `✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: npx @nitra/cursor fix-doc-files`
83
- )
84
- return 1
98
+ return exitCode
85
99
  }
86
100
 
87
101
  /**