@nitra/cursor 12.6.1 → 12.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/.claude-template/settings.template.json +1 -1
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +2 -2
  3. package/CHANGELOG.md +25 -5
  4. package/bin/docs/n-cursor.md +4 -20
  5. package/bin/n-cursor.js +8 -54
  6. package/docs/index.md +3 -3
  7. package/docs/stryker.config.md +20 -28
  8. package/lib/docs/index.md +5 -5
  9. package/lib/docs/llm.md +4 -4
  10. package/package.json +2 -2
  11. package/rules/abie/docs/fix.md +8 -8
  12. package/rules/abie/docs/index.md +4 -3
  13. package/rules/abie/docs/main.md +29 -0
  14. package/rules/abie/js/docs/index.md +6 -6
  15. package/rules/abie/lib/docs/index.md +9 -9
  16. package/rules/abie/{fix.mjs → main.mjs} +5 -3
  17. package/rules/adr/docs/index.md +1 -0
  18. package/rules/adr/docs/main.md +29 -0
  19. package/rules/adr/{fix.mjs → main.mjs} +5 -3
  20. package/rules/bun/docs/fix.md +5 -5
  21. package/rules/bun/docs/index.md +4 -3
  22. package/rules/bun/docs/main.md +30 -0
  23. package/rules/bun/js/docs/index.md +2 -2
  24. package/rules/bun/js/docs/layout.md +11 -36
  25. package/rules/bun/{fix.mjs → main.mjs} +5 -3
  26. package/rules/capacitor/docs/fix.md +10 -10
  27. package/rules/capacitor/docs/index.md +4 -3
  28. package/rules/capacitor/docs/main.md +29 -0
  29. package/rules/capacitor/js/docs/index.md +2 -2
  30. package/rules/capacitor/{fix.mjs → main.mjs} +5 -3
  31. package/rules/changelog/docs/fix.md +11 -11
  32. package/rules/changelog/docs/index.md +4 -3
  33. package/rules/changelog/docs/main.md +27 -0
  34. package/rules/changelog/js/docs/consistency.md +12 -12
  35. package/rules/changelog/js/docs/index.md +2 -2
  36. package/rules/changelog/lib/docs/index.md +2 -2
  37. package/rules/changelog/main.mjs +20 -0
  38. package/rules/ci4/docs/fix.md +4 -4
  39. package/rules/ci4/docs/index.md +4 -3
  40. package/rules/ci4/docs/main.md +30 -0
  41. package/rules/ci4/js/docs/index.md +2 -2
  42. package/rules/ci4/main.mjs +20 -0
  43. package/rules/doc-files/docs/index.md +4 -3
  44. package/rules/doc-files/docs/main.md +31 -0
  45. package/rules/doc-files/js/docgen-crc.mjs +2 -8
  46. package/rules/doc-files/js/docgen-extract.mjs +5 -3
  47. package/rules/doc-files/js/docgen-files-batch.mjs +63 -4
  48. package/rules/doc-files/js/docgen-gen.mjs +11 -3
  49. package/rules/doc-files/js/docgen-judge-measure.mjs +67 -18
  50. package/rules/doc-files/js/docgen-judge.mjs +8 -1
  51. package/rules/doc-files/js/docgen-scan.mjs +99 -11
  52. package/rules/doc-files/js/docs/docgen-crc.md +25 -14
  53. package/rules/doc-files/js/docs/docgen-extract.md +15 -13
  54. package/rules/doc-files/js/docs/docgen-files-batch.md +15 -15
  55. package/rules/doc-files/js/docs/docgen-gen.md +15 -26
  56. package/rules/doc-files/js/docs/docgen-judge-measure.md +14 -12
  57. package/rules/doc-files/js/docs/docgen-scan.md +34 -34
  58. package/rules/doc-files/js/docs/index.md +16 -15
  59. package/rules/doc-files/js/docs/run-lint.md +27 -0
  60. package/rules/doc-files/{lint/lint.mjs → js/run-lint.mjs} +23 -9
  61. package/rules/doc-files/{js/lint.mjs → main.mjs} +60 -10
  62. package/rules/docker/docs/fix.md +6 -6
  63. package/rules/docker/docs/index.md +4 -3
  64. package/rules/docker/docs/main.md +28 -0
  65. package/rules/docker/js/docs/index.md +2 -2
  66. package/rules/docker/js/docs/lint.md +26 -54
  67. package/rules/docker/js/lint.mjs +11 -0
  68. package/rules/docker/lib/docker-hadolint.mjs +1 -1
  69. package/rules/docker/lib/docs/docker-hadolint.md +16 -173
  70. package/rules/docker/lib/docs/index.md +5 -5
  71. package/rules/docker/main.mjs +20 -0
  72. package/rules/efes/docs/fix.md +8 -8
  73. package/rules/efes/docs/index.md +4 -3
  74. package/rules/efes/docs/main.md +29 -0
  75. package/rules/efes/main.mjs +20 -0
  76. package/rules/feedback/docs/fix.md +5 -5
  77. package/rules/feedback/docs/index.md +4 -3
  78. package/rules/feedback/docs/main.md +30 -0
  79. package/rules/feedback/main.mjs +20 -0
  80. package/rules/ga/docs/fix.md +5 -5
  81. package/rules/ga/docs/index.md +4 -3
  82. package/rules/ga/docs/main.md +29 -0
  83. package/rules/ga/js/docs/index.md +3 -3
  84. package/rules/ga/{lint/lint.mjs → main.mjs} +36 -10
  85. package/rules/graphql/docs/fix.md +8 -8
  86. package/rules/graphql/docs/index.md +4 -3
  87. package/rules/graphql/docs/main.md +36 -0
  88. package/rules/graphql/js/docs/index.md +2 -2
  89. package/rules/graphql/lib/docs/index.md +2 -2
  90. package/rules/graphql/main.mjs +20 -0
  91. package/rules/hasura/docs/fix.md +11 -11
  92. package/rules/hasura/docs/index.md +4 -3
  93. package/rules/hasura/docs/main.md +30 -0
  94. package/rules/hasura/js/docs/index.md +2 -2
  95. package/rules/hasura/main.mjs +20 -0
  96. package/rules/image-avif/docs/fix.md +3 -3
  97. package/rules/image-avif/docs/index.md +4 -3
  98. package/rules/image-avif/docs/main.md +30 -0
  99. package/rules/image-avif/js/docs/avif_generation.md +20 -233
  100. package/rules/image-avif/js/docs/index.md +2 -2
  101. package/rules/image-avif/main.mjs +20 -0
  102. package/rules/image-compress/docs/fix.md +2 -2
  103. package/rules/image-compress/docs/index.md +4 -3
  104. package/rules/image-compress/docs/main.md +29 -0
  105. package/rules/image-compress/js/docs/index.md +3 -3
  106. package/rules/image-compress/js/docs/package_setup.md +12 -11
  107. package/rules/image-compress/{js/lint.mjs → main.mjs} +21 -5
  108. package/rules/js-bun-db/docs/fix.md +5 -5
  109. package/rules/js-bun-db/docs/index.md +4 -3
  110. package/rules/js-bun-db/docs/main.md +30 -0
  111. package/rules/js-bun-db/js/docs/index.md +2 -2
  112. package/rules/js-bun-db/lib/docs/index.md +2 -2
  113. package/rules/js-bun-db/main.mjs +20 -0
  114. package/rules/js-bun-redis/docs/fix.md +6 -6
  115. package/rules/js-bun-redis/docs/index.md +4 -3
  116. package/rules/js-bun-redis/docs/main.md +29 -0
  117. package/rules/js-bun-redis/js/docs/index.md +2 -2
  118. package/rules/js-bun-redis/lib/docs/index.md +2 -2
  119. package/rules/js-bun-redis/main.mjs +20 -0
  120. package/rules/js-lint/docs/fix.md +9 -9
  121. package/rules/js-lint/docs/index.md +4 -3
  122. package/rules/js-lint/docs/main.md +29 -0
  123. package/rules/js-lint/js/check.mjs +268 -0
  124. package/rules/js-lint/js/docs/check.md +39 -0
  125. package/rules/js-lint/js/docs/index.md +4 -4
  126. package/rules/js-lint/js/docs/tooling.md +12 -32
  127. package/rules/js-lint/js/tooling.mjs +1 -265
  128. package/rules/js-lint/{js/lint.mjs → main.mjs} +19 -2
  129. package/rules/js-lint-ci/docs/fix.md +3 -3
  130. package/rules/js-lint-ci/docs/index.md +4 -3
  131. package/rules/js-lint-ci/docs/main.md +27 -0
  132. package/rules/js-lint-ci/js/docs/index.md +2 -2
  133. package/rules/js-lint-ci/main.mjs +33 -0
  134. package/rules/js-mssql/docs/fix.md +5 -5
  135. package/rules/js-mssql/docs/index.md +4 -3
  136. package/rules/js-mssql/docs/main.md +30 -0
  137. package/rules/js-mssql/js/docs/index.md +2 -2
  138. package/rules/js-mssql/lib/docs/index.md +2 -2
  139. package/rules/js-mssql/main.mjs +20 -0
  140. package/rules/js-run/docs/fix.md +8 -8
  141. package/rules/js-run/docs/index.md +4 -3
  142. package/rules/js-run/docs/main.md +30 -0
  143. package/rules/js-run/js/docs/index.md +2 -2
  144. package/rules/js-run/lib/docs/index.md +7 -7
  145. package/rules/js-run/main.mjs +20 -0
  146. package/rules/k8s/docs/fix.md +4 -4
  147. package/rules/k8s/docs/index.md +4 -3
  148. package/rules/k8s/docs/main.md +40 -0
  149. package/rules/k8s/js/docs/index.md +12 -0
  150. package/rules/k8s/{lint/lint.mjs → main.mjs} +32 -10
  151. package/rules/nginx-default-tpl/docs/fix.md +7 -7
  152. package/rules/nginx-default-tpl/docs/index.md +4 -3
  153. package/rules/nginx-default-tpl/docs/main.md +30 -0
  154. package/rules/nginx-default-tpl/js/docs/index.md +2 -2
  155. package/rules/nginx-default-tpl/js/docs/template.md +2 -2
  156. package/rules/nginx-default-tpl/main.mjs +20 -0
  157. package/rules/npm-module/docs/fix.md +8 -8
  158. package/rules/npm-module/docs/index.md +4 -3
  159. package/rules/npm-module/docs/main.md +29 -0
  160. package/rules/npm-module/js/docs/index.md +5 -5
  161. package/rules/npm-module/js/docs/rule_meta.md +17 -16
  162. package/rules/npm-module/js/header_doc_pointer.mjs +1 -3
  163. package/rules/npm-module/js/rule_meta.mjs +13 -3
  164. package/rules/npm-module/main.mjs +20 -0
  165. package/rules/php/docs/fix.md +6 -6
  166. package/rules/php/docs/index.md +4 -3
  167. package/rules/php/docs/main.md +33 -0
  168. package/rules/php/js/docs/index.md +3 -3
  169. package/rules/php/js/docs/tooling.md +10 -10
  170. package/rules/php/{lint/lint.mjs → main.mjs} +32 -6
  171. package/rules/python/docs/fix.md +11 -11
  172. package/rules/python/docs/index.md +4 -3
  173. package/rules/python/docs/main.md +31 -0
  174. package/rules/python/js/docs/index.md +3 -3
  175. package/rules/python/js/docs/tooling.md +17 -17
  176. package/rules/python/{lint/lint.mjs → main.mjs} +31 -6
  177. package/rules/rego/docs/fix.md +5 -5
  178. package/rules/rego/docs/index.md +4 -3
  179. package/rules/rego/docs/main.md +37 -0
  180. package/rules/rego/js/docs/index.md +3 -3
  181. package/rules/rego/{lint/lint.mjs → main.mjs} +27 -5
  182. package/rules/release/docs/index.md +5 -4
  183. package/rules/release/docs/main.md +29 -0
  184. package/rules/release/docs/release.md +0 -3
  185. package/rules/release/lib/docs/index.md +4 -4
  186. package/rules/release/release.mdc +10 -0
  187. package/rules/rust/docs/fix.md +4 -4
  188. package/rules/rust/docs/index.md +4 -3
  189. package/rules/rust/docs/main.md +27 -0
  190. package/rules/rust/js/docs/index.md +3 -3
  191. package/rules/rust/lib/docs/index.md +2 -2
  192. package/rules/rust/{js/lint.mjs → main.mjs} +27 -4
  193. package/rules/security/docs/fix.md +6 -6
  194. package/rules/security/docs/index.md +4 -3
  195. package/rules/security/docs/main.md +28 -0
  196. package/rules/security/js/docs/index.md +4 -4
  197. package/rules/security/main.mjs +45 -0
  198. package/rules/style-lint/docs/fix.md +3 -3
  199. package/rules/style-lint/docs/index.md +4 -3
  200. package/rules/style-lint/docs/main.md +29 -0
  201. package/rules/style-lint/js/docs/index.md +3 -3
  202. package/rules/style-lint/{js/lint.mjs → main.mjs} +19 -1
  203. package/rules/tauri/docs/fix.md +11 -11
  204. package/rules/tauri/docs/index.md +4 -3
  205. package/rules/tauri/docs/main.md +29 -0
  206. package/rules/tauri/js/docs/index.md +3 -3
  207. package/rules/tauri/main.mjs +20 -0
  208. package/rules/test/docs/fix.md +5 -5
  209. package/rules/test/docs/index.md +4 -3
  210. package/rules/test/docs/main.md +30 -0
  211. package/rules/test/js/data/stryker_config/docs/index.md +4 -4
  212. package/rules/test/js/data/vitest_config/docs/index.md +2 -2
  213. package/rules/test/js/docs/index.md +7 -7
  214. package/rules/test/main.mjs +20 -0
  215. package/rules/text/docs/fix.md +11 -11
  216. package/rules/text/docs/index.md +4 -3
  217. package/rules/text/docs/main.md +29 -0
  218. package/rules/text/{lint → js}/cspell-fix.mjs +7 -2
  219. package/rules/text/js/docs/cspell-fix.md +30 -0
  220. package/rules/text/js/docs/formatting.md +12 -45
  221. package/rules/text/js/docs/index.md +8 -4
  222. package/rules/text/js/docs/run-dotenv-linter.md +31 -0
  223. package/rules/text/js/docs/run-shellcheck.md +28 -0
  224. package/rules/text/js/docs/run-v8r.md +29 -0
  225. package/rules/text/{lint/lint.mjs → main.mjs} +41 -10
  226. package/rules/tool-surface/docs/index.md +4 -3
  227. package/rules/tool-surface/docs/main.md +29 -0
  228. package/rules/tool-surface/main.mjs +20 -0
  229. package/rules/tool-surface/meta.json +6 -1
  230. package/rules/vue/docs/fix.md +6 -6
  231. package/rules/vue/docs/index.md +4 -3
  232. package/rules/vue/docs/main.md +29 -0
  233. package/rules/vue/js/docs/index.md +2 -2
  234. package/rules/vue/lib/docs/index.md +2 -2
  235. package/rules/vue/main.mjs +20 -0
  236. package/rules/worktree/docs/fix.md +11 -11
  237. package/rules/worktree/docs/index.md +4 -3
  238. package/rules/worktree/docs/main.md +28 -0
  239. package/rules/worktree/main.mjs +20 -0
  240. package/scripts/coverage-classify/docs/index.md +6 -6
  241. package/scripts/dispatcher/docs/index.md +2 -2
  242. package/scripts/docs/index.md +16 -15
  243. package/scripts/docs/post-tool-use-check.md +29 -0
  244. package/scripts/docs/sync-claude-config.md +64 -92
  245. package/scripts/lib/adr/docs/normalize-cli.md +0 -3
  246. package/scripts/lib/adr/docs/normalize-pipeline.md +0 -3
  247. package/scripts/lib/docs/gha-workflow.md +25 -317
  248. package/scripts/lib/docs/index.md +36 -35
  249. package/scripts/lib/docs/list-project-rules-mdc.md +5 -4
  250. package/scripts/lib/docs/list-rule-ids.md +15 -148
  251. package/scripts/lib/docs/read-n-cursor-config-lite.md +12 -16
  252. package/scripts/lib/docs/run-lint-step.md +13 -13
  253. package/scripts/lib/docs/run-lint.md +30 -0
  254. package/scripts/lib/docs/run-rule-cli.md +14 -10
  255. package/scripts/lib/docs/run-standard-lint.md +29 -10
  256. package/scripts/lib/docs/run-standard-rule.md +12 -11
  257. package/scripts/lib/docs/timing-summary.md +11 -12
  258. package/scripts/lib/docs/worktree-notice.md +0 -3
  259. package/scripts/lib/fix/analyze-escalation.mjs +4 -1
  260. package/scripts/lib/fix/docs/index.md +11 -10
  261. package/scripts/lib/fix/docs/orchestrator.md +23 -18
  262. package/scripts/lib/fix/docs/run-conformance-check.md +33 -0
  263. package/scripts/lib/fix/docs/run-fix-check.md +3 -3
  264. package/scripts/lib/fix/docs/t0.md +10 -9
  265. package/scripts/lib/fix/orchestrator.mjs +31 -8
  266. package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
  267. package/scripts/lib/fix/t0.mjs +6 -3
  268. package/scripts/lib/list-project-rules-mdc.mjs +1 -1
  269. package/scripts/lib/list-rule-ids.mjs +12 -3
  270. package/scripts/lib/read-n-cursor-config-lite.mjs +2 -2
  271. package/{rules/lint/js/orchestrate.mjs → scripts/lib/run-lint.mjs} +42 -22
  272. package/scripts/lib/run-rule-cli.mjs +4 -4
  273. package/scripts/lib/run-standard-lint.mjs +19 -6
  274. package/scripts/lib/run-standard-rule.mjs +4 -4
  275. package/scripts/lib/timing-summary.mjs +1 -1
  276. package/scripts/{post-tool-use-fix.mjs → post-tool-use-check.mjs} +9 -9
  277. package/scripts/sync-claude-config.mjs +2 -2
  278. package/scripts/utils/docs/index.md +14 -14
  279. package/skills/doc-aggregate/js/docs/index.md +3 -3
  280. package/skills/doc-files/.changes/260612-0002.md +1 -0
  281. package/skills/doc-files/.changes/260612-0006.md +1 -0
  282. package/skills/doc-files/.changes/260612-0008.md +1 -0
  283. package/skills/doc-files/.changes/260612-0012.md +1 -0
  284. package/skills/doc-files/.changes/260612-0031.md +1 -0
  285. package/skills/doc-files/.changes/260612-0036.md +1 -0
  286. package/skills/doc-files/.changes/260612-0114.md +1 -0
  287. package/skills/start-check/js/docs/index.md +2 -2
  288. package/skills/taze/js/docs/index.md +2 -2
  289. package/types/bin/n-cursor.d.ts +1 -1
  290. package/rules/changelog/fix.mjs +0 -18
  291. package/rules/ci4/fix.mjs +0 -18
  292. package/rules/doc-files/fix.mjs +0 -19
  293. package/rules/doc-files/js/docs/lint.md +0 -34
  294. package/rules/doc-files/lint/docs/index.md +0 -11
  295. package/rules/doc-files/lint/docs/lint.md +0 -35
  296. package/rules/docker/fix.mjs +0 -18
  297. package/rules/docker/lint/docs/index.md +0 -11
  298. package/rules/docker/lint/docs/lint.md +0 -200
  299. package/rules/docker/lint/lint.mjs +0 -95
  300. package/rules/efes/fix.mjs +0 -18
  301. package/rules/feedback/fix.mjs +0 -18
  302. package/rules/ga/fix.mjs +0 -18
  303. package/rules/ga/js/docs/lint.md +0 -20
  304. package/rules/ga/js/lint.mjs +0 -12
  305. package/rules/ga/lint/docs/index.md +0 -11
  306. package/rules/ga/lint/docs/lint.md +0 -31
  307. package/rules/graphql/fix.mjs +0 -18
  308. package/rules/hasura/fix.mjs +0 -18
  309. package/rules/image-avif/fix.mjs +0 -18
  310. package/rules/image-compress/fix.mjs +0 -18
  311. package/rules/image-compress/js/docs/lint.md +0 -24
  312. package/rules/js-bun-db/fix.mjs +0 -18
  313. package/rules/js-bun-redis/fix.mjs +0 -18
  314. package/rules/js-lint/fix.mjs +0 -18
  315. package/rules/js-lint/js/docs/lint.md +0 -32
  316. package/rules/js-lint-ci/fix.mjs +0 -18
  317. package/rules/js-lint-ci/js/docs/lint.md +0 -22
  318. package/rules/js-lint-ci/js/lint.mjs +0 -15
  319. package/rules/js-mssql/fix.mjs +0 -18
  320. package/rules/js-run/fix.mjs +0 -18
  321. package/rules/k8s/fix.mjs +0 -18
  322. package/rules/k8s/js/lint.mjs +0 -14
  323. package/rules/k8s/lint/docs/index.md +0 -11
  324. package/rules/k8s/lint/docs/lint.md +0 -413
  325. package/rules/lint/docs/fix.md +0 -25
  326. package/rules/lint/docs/index.md +0 -11
  327. package/rules/lint/fix.mjs +0 -18
  328. package/rules/lint/js/docs/index.md +0 -11
  329. package/rules/lint/js/docs/orchestrate.md +0 -31
  330. package/rules/lint/meta.json +0 -1
  331. package/rules/nginx-default-tpl/fix.mjs +0 -18
  332. package/rules/npm-module/fix.mjs +0 -18
  333. package/rules/php/fix.mjs +0 -18
  334. package/rules/php/js/docs/lint.md +0 -20
  335. package/rules/php/js/lint.mjs +0 -15
  336. package/rules/php/lint/docs/index.md +0 -11
  337. package/rules/php/lint/docs/lint.md +0 -219
  338. package/rules/python/fix.mjs +0 -18
  339. package/rules/python/js/docs/lint.md +0 -21
  340. package/rules/python/js/lint.mjs +0 -14
  341. package/rules/python/lint/docs/index.md +0 -11
  342. package/rules/python/lint/docs/lint.md +0 -29
  343. package/rules/rego/fix.mjs +0 -18
  344. package/rules/rego/js/docs/lint.md +0 -21
  345. package/rules/rego/js/lint.mjs +0 -12
  346. package/rules/rego/lint/docs/index.md +0 -11
  347. package/rules/rego/lint/docs/lint.md +0 -208
  348. package/rules/rust/fix.mjs +0 -18
  349. package/rules/rust/js/docs/lint.md +0 -21
  350. package/rules/security/fix.mjs +0 -18
  351. package/rules/security/js/docs/lint.md +0 -175
  352. package/rules/security/js/lint.mjs +0 -26
  353. package/rules/style-lint/fix.mjs +0 -18
  354. package/rules/style-lint/js/docs/lint.md +0 -31
  355. package/rules/tauri/fix.mjs +0 -18
  356. package/rules/test/fix.mjs +0 -18
  357. package/rules/text/fix.mjs +0 -18
  358. package/rules/text/js/docs/lint.md +0 -23
  359. package/rules/text/js/lint.mjs +0 -15
  360. package/rules/text/lint/docs/cspell-fix.md +0 -32
  361. package/rules/text/lint/docs/index.md +0 -15
  362. package/rules/text/lint/docs/lint.md +0 -36
  363. package/rules/text/lint/docs/run-dotenv-linter.md +0 -161
  364. package/rules/text/lint/docs/run-shellcheck.md +0 -216
  365. package/rules/text/lint/docs/run-v8r.md +0 -201
  366. package/rules/tool-surface/fix.mjs +0 -18
  367. package/rules/vue/fix.mjs +0 -18
  368. package/rules/worktree/fix.mjs +0 -18
  369. /package/rules/release/{fix.mjs → main.mjs} +0 -0
  370. /package/rules/text/{lint → js}/run-dotenv-linter.mjs +0 -0
  371. /package/rules/text/{lint → js}/run-shellcheck.mjs +0 -0
  372. /package/rules/text/{lint → js}/run-v8r.mjs +0 -0
@@ -1,12 +1,21 @@
1
1
  /** @see ./docs/docgen-files-batch.md */
2
- import { readFileSync, readdirSync, mkdirSync, writeFileSync, existsSync, statSync } from 'node:fs'
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readdirSync,
6
+ readFileSync,
7
+ rmdirSync,
8
+ statSync,
9
+ unlinkSync,
10
+ writeFileSync
11
+ } from 'node:fs'
3
12
  import { basename, dirname, join, relative } from 'node:path'
4
13
 
5
14
  import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
6
15
  import { classifyOmlxError, preflightLocalModel } from '../../../lib/llm.mjs'
7
16
  import { generateDoc, DEFAULT_LOCAL_MODEL } from './docgen-gen.mjs'
8
17
  import { crc32, stampDoc, readDocQuality, readDocModel, QUALITY_THRESHOLD } from './docgen-crc.mjs'
9
- import { resolveRoot, scanForDocFiles } from './docgen-scan.mjs'
18
+ import { resolveRoot, scanForDocFiles, scanOrphanedDocs } from './docgen-scan.mjs'
10
19
 
11
20
  /**
12
21
  * Парсить `--limit N` / `--from N` / прапори режимів для дозапуску великого прогону.
@@ -153,7 +162,9 @@ const OKF_RESOURCE_RE = /^resource:[ \t]+(.+)$/mu
153
162
  * @returns {void}
154
163
  */
155
164
  function generateDirIndex(docsAbsDir, root) {
156
- const allMd = readdirSync(docsAbsDir).filter(f => f.endsWith('.md')).sort()
165
+ const allMd = readdirSync(docsAbsDir)
166
+ .filter(f => f.endsWith('.md'))
167
+ .sort()
157
168
 
158
169
  // Якщо index.md вже є дока для source-файлу (має docgen.source → index.*) — не чіпаємо
159
170
  if (allMd.includes('index.md')) {
@@ -215,6 +226,46 @@ function reportStats(stats) {
215
226
  }
216
227
  }
217
228
 
229
+ /**
230
+ * Видаляє сирітські доки (source-файл не існує) і оновлює/прибирає index.md.
231
+ * Якщо після видалення в docs/-директорії лишились тільки index.md або нічого — очищує її.
232
+ * @param {string} root абсолютний корінь
233
+ * @returns {number} кількість видалених doc-файлів
234
+ */
235
+ export function purgeOrphanedDocs(root) {
236
+ const orphans = scanOrphanedDocs(root)
237
+ if (orphans.length === 0) return 0
238
+ let deleted = 0
239
+ const docsDirs = new Set()
240
+ for (const docRel of orphans) {
241
+ try {
242
+ unlinkSync(join(root, docRel))
243
+ docsDirs.add(dirname(join(root, docRel)))
244
+ deleted++
245
+ } catch {
246
+ // race condition або вже видалено — ігноруємо
247
+ }
248
+ }
249
+ for (const docsAbsDir of docsDirs) {
250
+ if (!existsSync(docsAbsDir)) continue
251
+ const remaining = readdirSync(docsAbsDir)
252
+ const docFiles = remaining.filter(f => f.endsWith('.md') && f !== 'index.md')
253
+ if (docFiles.length === 0) {
254
+ // Лише index.md або порожня директорія — прибираємо повністю
255
+ const indexPath = join(docsAbsDir, 'index.md')
256
+ if (existsSync(indexPath)) unlinkSync(indexPath)
257
+ try {
258
+ rmdirSync(docsAbsDir)
259
+ } catch {
260
+ /* не порожня — пропускаємо */
261
+ }
262
+ } else {
263
+ generateDirIndex(docsAbsDir, root)
264
+ }
265
+ }
266
+ return deleted
267
+ }
268
+
218
269
  /**
219
270
  * `doc-files gen` — згенерувати документацію для застарілих/відсутніх док.
220
271
  * @param {string[]} argv аргументи після назви субкоманди
@@ -224,11 +275,19 @@ export async function runDocFilesGenCli(argv) {
224
275
  const root = resolveRoot(argv)
225
276
  const { from, limit, overwrite } = parseGenArgs(argv)
226
277
 
278
+ // Видаляємо orphan-доки до генерації (незалежно від наявності stale)
279
+ const deleted = purgeOrphanedDocs(root)
280
+ if (deleted > 0) {
281
+ console.log(`🗑 doc-files: видалено ${deleted} сирітських доки(ів)`)
282
+ }
283
+
227
284
  const all = scanForDocFiles(root)
228
285
  const targets = selectTargets(root, all, { overwrite }).slice(from, from + limit)
229
286
 
230
287
  if (targets.length === 0) {
231
- console.log('✓ doc-files: усі файлові доки свіжі й не-degraded. Нічого генерувати.')
288
+ if (deleted === 0) {
289
+ console.log('✓ doc-files: усі файлові доки свіжі й не-degraded. Нічого генерувати.')
290
+ }
232
291
  return 0
233
292
  }
234
293
 
@@ -133,7 +133,10 @@ export function splitProtected(md) {
133
133
  break
134
134
  }
135
135
  }
136
- const body = lines.slice(start + 1, end).join('\n').trim()
136
+ const body = lines
137
+ .slice(start + 1, end)
138
+ .join('\n')
139
+ .trim()
137
140
  const without = [...lines.slice(0, start), ...lines.slice(end)].join('\n')
138
141
  return { body: body || null, without }
139
142
  }
@@ -395,7 +398,10 @@ export const DEFAULT_LOCAL_MODEL = env.N_CURSOR_DOCGEN_MODEL ?? resolveModel('mi
395
398
  * @param {{ model?: string, threshold?: number, existingMd?: string|null }} [opts] model-id, поріг degraded, наявна дока (для збереження захищеної секції)
396
399
  * @returns {{ md: string, ms: number, llmMs: number, llmCalls: number, score: number|null, issues: string[], degraded: boolean, model: string }} документ і метадані генерації (ms — увесь файл; llmMs/llmCalls — лише LLM; решта ms — оркестрація)
397
400
  */
398
- export function generateDoc(file, { model = DEFAULT_LOCAL_MODEL, threshold = QUALITY_THRESHOLD, existingMd = null } = {}) {
401
+ export function generateDoc(
402
+ file,
403
+ { model = DEFAULT_LOCAL_MODEL, threshold = QUALITY_THRESHOLD, existingMd = null } = {}
404
+ ) {
399
405
  const src = readFileSync(file, 'utf8')
400
406
  // Pre-send guard: весь src вшивається у промпт як є (екстракт фактів його НЕ
401
407
  // замінює). Для гігантів (vendored/генерат) це переповнює контекст → інстант-skip
@@ -403,7 +409,9 @@ export function generateDoc(file, { model = DEFAULT_LOCAL_MODEL, threshold = QUA
403
409
  const estTokens = Math.round(Buffer.byteLength(src, 'utf8') / 4)
404
410
  const budget = srcTokenBudget()
405
411
  if (estTokens > budget) {
406
- throw new Error(`docgen pre-send guard: джерело ~${estTokens} токенів > бюджет ${budget} (0.5× контексту) — Prompt too long, skip`)
412
+ throw new Error(
413
+ `docgen pre-send guard: джерело ~${estTokens} токенів > бюджет ${budget} (0.5× контексту) — Prompt too long, skip`
414
+ )
407
415
  }
408
416
  const facts = extractFacts(src, file)
409
417
  const t0 = Date.now()
@@ -64,8 +64,16 @@ function judgeCached(src, doc) {
64
64
  const hit = cacheGet(key)
65
65
  if (hit) return { ...hit, cached: true }
66
66
  const user = `SOURCE FILE:\n\`\`\`\n${src.slice(0, 12000)}\n\`\`\`\n\nGENERATED DOC:\n\`\`\`md\n${doc.slice(0, 8000)}\n\`\`\`\n\nReturn the JSON verdict.`
67
- const raw = callLlm([{ role: 'system', content: SYSTEM }, { role: 'user', content: user }], JUDGE_MODEL, { timeoutMs: JUDGE_TIMEOUT, temperature: 0 })
68
- const a = raw.indexOf('{'), b = raw.lastIndexOf('}')
67
+ const raw = callLlm(
68
+ [
69
+ { role: 'system', content: SYSTEM },
70
+ { role: 'user', content: user }
71
+ ],
72
+ JUDGE_MODEL,
73
+ { timeoutMs: JUDGE_TIMEOUT, temperature: 0 }
74
+ )
75
+ const a = raw.indexOf('{'),
76
+ b = raw.lastIndexOf('}')
69
77
  if (a === -1 || b === -1) throw new Error('no JSON in judge reply: ' + raw.slice(0, 160))
70
78
  const v = JSON.parse(raw.slice(a, b + 1))
71
79
  cacheSet(key, v)
@@ -81,17 +89,34 @@ function main() {
81
89
  console.error('Usage: node docgen-judge-measure.mjs <file1> <file2> ...')
82
90
  process.exit(2)
83
91
  }
84
- console.error(`[measure] gen=${GEN_MODEL} judge=${JUDGE_MODEL} threshold=${THRESHOLD} files=${files.length} cache=${CACHE_DIR}`)
92
+ console.error(
93
+ `[measure] gen=${GEN_MODEL} judge=${JUDGE_MODEL} threshold=${THRESHOLD} files=${files.length} cache=${CACHE_DIR}`
94
+ )
85
95
 
86
96
  const rows = []
87
97
  for (const [i, file] of files.entries()) {
88
98
  const tag = `(${i + 1}/${files.length}) ${file}`
89
99
  let src
90
- try { src = readFileSync(file, 'utf8') } catch (error) { console.error(`[skip] ${tag}: read ${error.message}`); continue }
100
+ try {
101
+ src = readFileSync(file, 'utf8')
102
+ } catch (error) {
103
+ console.error(`[skip] ${tag}: read ${error.message}`)
104
+ continue
105
+ }
91
106
 
92
107
  let gen
93
- try { gen = genCached(file, src) } catch (error) { console.error(`[gen-err] ${tag}: ${error.message.slice(0, 120)}`); rows.push({ file, error: 'gen', detail: error.message.slice(0, 200) }); continue }
94
- if (gen.score === null) { console.error(`[unsupported] ${tag}`); rows.push({ file, score: null, unsupported: true }); continue }
108
+ try {
109
+ gen = genCached(file, src)
110
+ } catch (error) {
111
+ console.error(`[gen-err] ${tag}: ${error.message.slice(0, 120)}`)
112
+ rows.push({ file, error: 'gen', detail: error.message.slice(0, 200) })
113
+ continue
114
+ }
115
+ if (gen.score === null) {
116
+ console.error(`[unsupported] ${tag}`)
117
+ rows.push({ file, score: null, unsupported: true })
118
+ continue
119
+ }
95
120
 
96
121
  const passed = gen.score >= THRESHOLD
97
122
  const row = { file, score: gen.score, degraded: gen.degraded, passed, genCached: gen.cached }
@@ -100,9 +125,18 @@ function main() {
100
125
  if (passed) {
101
126
  try {
102
127
  const v = judgeCached(src, gen.md)
103
- row.verdict = v.verdict; row.confidence = v.confidence; row.reason = v.reason; row.offending = v.offending; row.judgeCached = v.cached
104
- console.error(` [judge${v.cached ? '*' : ''}] ${v.verdict} (${v.confidence}) — ${(v.reason || '').slice(0, 90)}`)
105
- } catch (error) { row.judgeError = error.message.slice(0, 200); console.error(` [judge-err] ${error.message.slice(0, 120)}`) }
128
+ row.verdict = v.verdict
129
+ row.confidence = v.confidence
130
+ row.reason = v.reason
131
+ row.offending = v.offending
132
+ row.judgeCached = v.cached
133
+ console.error(
134
+ ` [judge${v.cached ? '*' : ''}] ${v.verdict} (${v.confidence}) — ${(v.reason || '').slice(0, 90)}`
135
+ )
136
+ } catch (error) {
137
+ row.judgeError = error.message.slice(0, 200)
138
+ console.error(` [judge-err] ${error.message.slice(0, 120)}`)
139
+ }
106
140
  }
107
141
  rows.push(row)
108
142
  }
@@ -119,17 +153,26 @@ function main() {
119
153
  const report = {
120
154
  config: { genModel: GEN_MODEL, judgeModel: JUDGE_MODEL, threshold: THRESHOLD },
121
155
  counts: {
122
- files: files.length, generated: scored.length,
156
+ files: files.length,
157
+ generated: scored.length,
123
158
  unsupported: rows.filter(r => r.unsupported).length,
124
159
  genErrors: rows.filter(r => r.error === 'gen').length,
125
160
  passedDetScorer: scored.filter(r => r.passed).length,
126
- judged: M, judgeErrors: rows.filter(r => r.judgeError).length
161
+ judged: M,
162
+ judgeErrors: rows.filter(r => r.judgeError).length
127
163
  },
128
- falsePositiveRate: { // серед PASSED+judged
129
- accurate: byVerdict.accurate, generic: byVerdict.generic, inaccurate: byVerdict.inaccurate,
130
- badPct: pct(bad), inaccuratePct: pct(byVerdict.inaccurate), genericPct: pct(byVerdict.generic)
164
+ falsePositiveRate: {
165
+ // серед PASSED+judged
166
+ accurate: byVerdict.accurate,
167
+ generic: byVerdict.generic,
168
+ inaccurate: byVerdict.inaccurate,
169
+ badPct: pct(bad),
170
+ inaccuratePct: pct(byVerdict.inaccurate),
171
+ genericPct: pct(byVerdict.generic)
131
172
  },
132
- offenders: passedRows.filter(r => r.verdict !== 'accurate').map(r => ({ file: r.file, score: r.score, verdict: r.verdict, confidence: r.confidence, reason: r.reason })),
173
+ offenders: passedRows
174
+ .filter(r => r.verdict !== 'accurate')
175
+ .map(r => ({ file: r.file, score: r.score, verdict: r.verdict, confidence: r.confidence, reason: r.reason })),
133
176
  rows
134
177
  }
135
178
 
@@ -138,10 +181,16 @@ function main() {
138
181
  writeFileSync(out, JSON.stringify(report, null, 2))
139
182
 
140
183
  console.log('\n===== Q4 MEASUREMENT =====')
141
- console.log(`generated: ${report.counts.generated}/${files.length} (unsupported=${report.counts.unsupported}, gen-errors=${report.counts.genErrors})`)
184
+ console.log(
185
+ `generated: ${report.counts.generated}/${files.length} (unsupported=${report.counts.unsupported}, gen-errors=${report.counts.genErrors})`
186
+ )
142
187
  console.log(`passed det-scorer (score≥${THRESHOLD}): ${report.counts.passedDetScorer} judged: ${M}`)
143
- console.log(`among PASSED+judged → accurate=${byVerdict.accurate} generic=${byVerdict.generic} inaccurate=${byVerdict.inaccurate}`)
144
- console.log(`>>> det-scorer FALSE-POSITIVE rate: ${pct(bad)}% (inaccurate=${pct(byVerdict.inaccurate)}%, generic=${pct(byVerdict.generic)}%)`)
188
+ console.log(
189
+ `among PASSED+judged accurate=${byVerdict.accurate} generic=${byVerdict.generic} inaccurate=${byVerdict.inaccurate}`
190
+ )
191
+ console.log(
192
+ `>>> det-scorer FALSE-POSITIVE rate: ${pct(bad)}% (inaccurate=${pct(byVerdict.inaccurate)}%, generic=${pct(byVerdict.generic)}%)`
193
+ )
145
194
  console.log(`decision guide: <~5% → don't build gate; >~15% → build (inaccurate-only)`)
146
195
  console.log(`report: ${out}`)
147
196
  }
@@ -58,7 +58,14 @@ export function parseDocVerdict(rawText) {
58
58
  */
59
59
  export function judgeDoc(src, doc, { model = JUDGE_MODEL, timeoutMs = 120_000 } = {}) {
60
60
  const user = `SOURCE FILE:\n\`\`\`\n${src.slice(0, 12_000)}\n\`\`\`\n\nGENERATED DOC:\n\`\`\`md\n${doc.slice(0, 8000)}\n\`\`\`\n\nReturn the JSON verdict.`
61
- const raw = callLlm([{ role: 'system', content: JUDGE_SYSTEM }, { role: 'user', content: user }], model, { timeoutMs, temperature: 0 })
61
+ const raw = callLlm(
62
+ [
63
+ { role: 'system', content: JUDGE_SYSTEM },
64
+ { role: 'user', content: user }
65
+ ],
66
+ model,
67
+ { timeoutMs, temperature: 0 }
68
+ )
62
69
  return parseDocVerdict(raw)
63
70
  }
64
71
 
@@ -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 не репортить як
@@ -96,7 +160,12 @@ function gitIgnoredPaths(root, relPaths) {
96
160
  encoding: 'utf8',
97
161
  stdio: ['pipe', 'pipe', 'ignore'] // git пише «not a git repository» у stderr — глушимо
98
162
  })
99
- return new Set(out.split('\n').map(s => s.trim()).filter(Boolean))
163
+ return new Set(
164
+ out
165
+ .split('\n')
166
+ .map(s => s.trim())
167
+ .filter(Boolean)
168
+ )
100
169
  } catch {
101
170
  // exit 1 (жоден не ігнорується) і 128 (не git-репо) → execFileSync кидає; обидва = «не фільтруємо».
102
171
  return new Set()
@@ -137,7 +206,10 @@ export function scanForDocFiles(root) {
137
206
  }
138
207
 
139
208
  walk(root)
140
- const ignored = gitIgnoredPaths(root, results.map(r => r.sourcePath))
209
+ const ignored = gitIgnoredPaths(
210
+ root,
211
+ results.map(r => r.sourcePath)
212
+ )
141
213
  return ignored.size ? results.filter(r => !ignored.has(r.sourcePath)) : results
142
214
  }
143
215
 
@@ -307,20 +379,36 @@ export async function runDocFilesCheckCli(argv) {
307
379
  }
308
380
 
309
381
  const stale = sources.map(src => describeFile(root, src)).filter(f => f.stale)
310
- if (stale.length === 0) return 0
382
+ // В git-режимі (Stop-гейт) додатково шукаємо сирітські доки без source-файлу
383
+ const orphans = gitMode ? scanOrphanedDocs(root) : []
311
384
 
312
- // Великий прогін: Stop-гейт не блокує, лише попереджає (захист від нескінченного блоку).
385
+ if (stale.length === 0 && orphans.length === 0) return 0
386
+
387
+ // Великий прогін stale: Stop-гейт не блокує, але orphan-check продовжуємо
313
388
  if (gitMode && stale.length > gateMax) {
314
389
  console.error(
315
390
  `⚠ doc-files: застарілих док ${stale.length} (> ${gateMax}) — гейт не блокує. Запусти масовий прогін:\n npx @nitra/cursor fix-doc-files`
316
391
  )
317
- return 0
392
+ if (orphans.length === 0) return 0
393
+ const oList = orphans.map(f => ` - ${f}`).join('\n')
394
+ console.error(
395
+ `✗ doc-files: сирітських доків (source видалено) ${orphans.length}:\n${oList}\n→ очисти: npx @nitra/cursor fix-doc-files`
396
+ )
397
+ return 2
318
398
  }
319
399
 
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
- )
400
+ if (stale.length > 0) {
401
+ const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
402
+ console.error(
403
+ `✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: /doc-files`
404
+ )
405
+ }
406
+ if (orphans.length > 0) {
407
+ const oList = orphans.map(f => ` - ${f}`).join('\n')
408
+ console.error(
409
+ `✗ doc-files: сирітських доків (source видалено) ${orphans.length}:\n${oList}\n→ очисти: npx @nitra/cursor fix-doc-files`
410
+ )
411
+ }
324
412
  return 2
325
413
  }
326
414
 
@@ -3,28 +3,39 @@ type: JS Module
3
3
  title: docgen-crc.mjs
4
4
  resource: npm/rules/doc-files/js/docgen-crc.mjs
5
5
  docgen:
6
- crc: cca0a79f
6
+ crc: 0c277bb7
7
+ model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
+ score: 100
7
9
  ---
8
10
 
9
- Детермінований маркер актуальності файлових док: контрольна сума джерела у frontmatter плюс опційний degraded-маркер якості. Єдине джерело правди про «дока свіжа/застаріла/неякісна» для генерації, перевірок і хуків.
11
+ ## Огляд
12
+
13
+ Модуль забезпечує роботу з метаданими та оцінкою якості документів. Він дозволяє парсити метадані з Markdown-файлів за допомогою `parseDocFrontmatter`, створювати та заповнювати блоки метаданих за допомогою `buildDocFrontmatter`. Документи можуть бути промарковані за допомогою `stampDoc`. Якість документа оцінюється за допомогою `readDocQuality`, а його актуальність визначається функцією `staleness`, яка використовує `crc32` для перевірки хеш-суми. Модель документа зчитується через `readDocModel`.
10
14
 
11
15
  ## Поведінка
12
16
 
13
- 1. Контрольна сума обчислюється з байтів джерела і записується у машинний frontmatter доки разом зі шляхом джерела; розбіжність суми з поточним джерелом (або відсутність доки) означає застарілість.
14
- 2. Якщо генерація не дотягнула до порогу якості, frontmatter додатково несе оцінку (`score`) і коди проблем (`issues`); коди нормалізуються до YAML-безпечних (без пробілів, обмежена кількість), а старі доки без цих полів лишаються валідними.
15
- 3. Поріг degraded `70`, override через `N_CURSOR_DOC_FILES_THRESHOLD`.
16
- 4. Перештампування знімає наявний frontmatter і ставить свіжий, не торкаючись тіла документа; якість при цьому передається явно — без неї поля якості зникають.
17
+ QUALITY_THRESHOLD визначає поріг якості, нижче якого дока вважається неякісним.
18
+ crc32 обчислює CRC32 вмісту в hex-форматі.
19
+ parseDocFrontmatter парсить YAML-блок у вмісті md-файлу, виділяючи метадані та тіло.
20
+ buildDocFrontmatter створює OKF-сумісний YAML-блок для доки, включаючи метадані.
21
+ stampDoc замінює або додає свіжий YAML-блок у тіло md-доку, використовуючи дані джерела та оцінку.
22
+ readDocCrc зчитує CRC32 з YAML-блоку md-доки за заданим абсолютним шляхом.
23
+ readDocQuality зчитує оцінку якості (score, issues, retried, judgeModel) з YAML-блоку md-доки за абсолютним шляхом.
24
+ readDocModel зчитує повний ID моделі-генератора з YAML-блоку md-доки за абсолютним шляхом.
25
+ staleness визначає, чи є дока застарілою, порівнюючи CRC джерела з CRC у доці за абсолютними шляхами.
17
26
 
18
27
  ## Публічний API
19
28
 
20
- - `crc32` контрольна сума вмісту в hex.
21
- - `staleness` стан доки відносно джерела: `missing` / `crc-mismatch` / свіжа.
22
- - `parseDocFrontmatter` / `buildDocFrontmatter` / `stampDoc` читання і (пере)штампування машинного блока.
23
- - `readDocCrc` / `readDocQuality` точкове читання суми та якості з доки.
24
- - `QUALITY_THRESHOLD` чинний поріг degraded.
29
+ QUALITY_THRESHOLDВизначає рівень якості документа: дока з низьким показником вважається неякісним.
30
+ crc32Генерує 32-бітний контрольний сума вмісту у шістнадцятковому форматі.
31
+ parseDocFrontmatter Витягує метадані з початку документа. Якщо метаданих немає, повертає порожні дані.
32
+ buildDocFrontmatter Створює блок метаданих, сумісний з OKF, включаючи дані про генерацію документа (CRC, модель, якість).
33
+ stampDocДодає метадані до документа, що фіксують його стан.
34
+ readDocCrc — Зчитує контрольний сума з метаданих документа.
35
+ readDocQuality — Зчитує рівень якості, збережений у метаданих документа.
36
+ readDocModel — Зчитує назву моделі, використаної для генерації документа.
37
+ staleness — Визначає, чи є документ актуальним, порівнюючи його з джерелом.
25
38
 
26
39
  ## Гарантії поведінки
27
40
 
28
- - Сума не залежить від git-стану: rebase, гілки й незакомічені зміни на неї не впливають.
29
- - Frontmatter — єдиний машинний виняток із правила «чистий Markdown»; тіло доки модуль не редагує.
30
- - Відсутні поля якості читаються як «не оцінено» (`score: null`), а не як нуль.
41
+ - Read-only: не виконує операцій запису (ФС/БД).
@@ -3,31 +3,33 @@ type: JS Module
3
3
  title: docgen-extract.mjs
4
4
  resource: npm/rules/doc-files/js/docgen-extract.mjs
5
5
  docgen:
6
- crc: a0680e77
6
+ crc: 369662fe
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Витягує структурований факт-лист з вмісту файлів, аналізуючи їх залежно від мови. Для Rust витягує модульний опис, публічні експорти, локальні символи та класифікує імпорти й поведінкові маркери. Для JavaScript/TypeScript/MJS витягує опис, експортовані елементи з JSDoc, класифікує імпорти та визначає поведінкові маркери. При аналізі ігноруються директорії: .github, .git, node_modules, base/, ua/, .firebase. Звертається до мережі та кешує дані протягом одного прогону.
11
+ ## Огляд
12
+
13
+ Модуль витягує структурований факт-лист з вмісту файлів, звертаючись до мережі. Функція `extractFacts` аналізує вміст файлів, визначаючи публічні експорти, локальні символи та поведінкові маркери для Rust та JavaScript/TypeScript файлів. При цьому ігноруються системні та залежності, включаючи каталоги `.github`, `.git`, `node_modules`, `base/`, `ua/` та `.firebase`. Результати аналізу кешуються протягом одного прогону.
12
14
 
13
15
  ## Поведінка
14
16
 
15
17
  1. Витягує факт-лист з вмісту файлу.
16
18
  2. Визначає мову файлу за розширенням.
17
19
  3. Якщо мова — Rust, виконує аналіз Rust-коду:
18
- а. Витягує модульний опис з `//!`.
19
- б. Визначає публічні експорти (структури, функції, енуми) на основі `pub` префікса або експозиційних атрибутів.
20
+ а. Витягує модульний заголовок (`//!`).
21
+ б. Визначає публічні експорти (структури, функції, класи), виходячи з префіксів `pub` та атрибутів експозиції.
20
22
  в. Визначає локальні (приватні) символи, які не є публічними.
21
- г. Класифікує імпорти (`std`, зовнішні, внутрішні).
22
- д. Визначає поведінкові маркери (readOnly, network, caches тощо) на основі специфічних Rust-конструкцій.
23
+ г. Класифікує імпорти (`use`) як стандартні, зовнішні чи внутрішні.
24
+ д. Визначає поведінкові маркери (наприклад, чи є код лише для читання, чи обробляє помилки).
23
25
  4. Якщо мова — JavaScript/TypeScript/MJS, виконує аналіз JS-коду:
24
- а. Витягує загальний опис файлу з верхнього блоку коментарів.
25
- б. Витягує експортовані функції та класи разом із їхніми JSDoc-описами.
26
- в. Класифікує імпорти на стандартні бібліотеки, NPM-пакети та внутрішні модулі.
27
- г. Визначає локальні (неекспортовані) функції та класи.
28
- д. Визначає поведінкові маркери (readOnly, network, caches тощо) на основі евристик.
29
- 5. При аналізі JS-коду, свідомо ігнорує шляхи: .github, .git, node_modules, base/, ua/, .firebase.
30
- 6. Повертає структуру фактів, що містить метадані про файл.
26
+ а. Витягує заголовок файлу.
27
+ б. Визначає публічні експорти з їхнім JSDoc.
28
+ в. Класифікує імпорти як стандартні, npm або внутрішні.
29
+ г. Визначає локальні символи (службові функції).
30
+ д. Визначає поведінкові маркери (наприклад, чи є код лише для читання, чи звертається до мережі).
31
+ 5. Усі аналізи свідомо ігнорують шляхи: `.github`, `.git`, `node_modules`, `base/`, `ua/`, `.firebase`.
32
+ 6. Повертає структуру фактів, що містить інформацію про файл.
31
33
 
32
34
  ## Публічний API
33
35
 
@@ -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: 6316eecb
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
+ Модуль керує життєвим циклом документації. Він вибирає цілі для оновлення за допомогою `selectTargets`, очищає від неіснуючих джерел за допомогою `purgeOrphanedDocs`, запускає генерацію файлів через `runDocFilesGenCli` та `runDocFilesStampCli`, а також виконує пакетну генерацію за допомогою `runGenerationBatch`. Усі операції виконуються з механізмом перехоплення помилок, що запобігає виникненню винятків назовні.
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 та зберігаючи дані про якість.
23
22
 
24
23
  ## Публічний API
25
24
 
26
- - runDocFilesGenCli згенерувати документацію для застарілих/відсутніх док.
27
- - runDocFilesStampCli детерміновано (пере)штампувати frontmatter `source`+`crc` у наявних доках без виклику LLM. Для міграції док, які ще не мають CRC. Поля якості (`score`/`issues`) зберігаються з наявного frontmatter.
25
+ selectTargetsВизначає цілі для генерації документації: застарілі або деградовані документи, або всі документи при використанні прапора перезапису.
26
+ purgeOrphanedDocsВидаляє документи, для яких відсутній відповідний вихідний файл, та оновлює файл індексу.
27
+ runDocFilesGenCli — Генерує документацію для застарілих або відсутніх документів.
28
+ runGenerationBatch — Виконує повний цикл генерації: перевіряє стан локального бекенду, послідовно генерує документи з обробкою збоїв, та створює фінальний звіт.
29
+ runDocFilesStampCli — Додає або оновлює метадані джерела та контрольної суми до існуючих документів без використання великих мовних моделей.
28
30
 
29
31
  ## Гарантії поведінки
30
32
 
31
33
  - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
32
- - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
33
- - Не звертається до мережі.