@nitra/cursor 5.3.4 → 6.0.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 (173) hide show
  1. package/.claude-template/settings.template.json +2 -2
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +13 -24
  3. package/CHANGELOG.md +21 -0
  4. package/bin/n-cursor.js +47 -24
  5. package/lib/docs/models.md +29 -18
  6. package/lib/docs/omlx-trace.md +51 -0
  7. package/lib/docs/omlx.md +31 -15
  8. package/lib/omlx.mjs +2 -5
  9. package/package.json +1 -1
  10. package/rules/abie/docs/fix.md +17 -11
  11. package/rules/adr/docs/fix.md +25 -140
  12. package/rules/bun/docs/fix.md +18 -151
  13. package/rules/capacitor/docs/fix.md +16 -13
  14. package/rules/capacitor/js/docs/platforms.md +31 -43
  15. package/rules/changelog/docs/fix.md +25 -169
  16. package/rules/ci4/docs/fix.md +11 -14
  17. package/rules/doc-files/doc-files.mdc +60 -0
  18. package/rules/doc-files/docs/fix.md +31 -0
  19. package/rules/doc-files/fix.mjs +19 -0
  20. package/{skills → rules}/doc-files/js/docgen-extract.mjs +42 -19
  21. package/{skills → rules}/doc-files/js/docgen-files-batch.mjs +18 -5
  22. package/{skills → rules}/doc-files/js/docgen-gen.mjs +46 -5
  23. package/{skills → rules}/doc-files/js/docgen-ignore.mjs +2 -1
  24. package/{skills → rules}/doc-files/js/docgen-scan.mjs +11 -3
  25. package/{skills → rules}/doc-files/js/docs/docgen-crc.md +1 -1
  26. package/{skills → rules}/doc-files/js/docs/docgen-extract-anchors.md +1 -1
  27. package/{skills → rules}/doc-files/js/docs/docgen-extract.md +2 -2
  28. package/{skills → rules}/doc-files/js/docs/docgen-files-batch.md +2 -2
  29. package/{skills → rules}/doc-files/js/docs/docgen-gen.md +2 -2
  30. package/{skills → rules}/doc-files/js/docs/docgen-ignore.md +4 -4
  31. package/rules/doc-files/js/docs/docgen-prompts.md +39 -0
  32. package/rules/doc-files/js/docs/docgen-scan.md +54 -0
  33. package/rules/doc-files/js/docs/lint.md +36 -0
  34. package/rules/doc-files/js/docs/units-js.md +31 -0
  35. package/rules/doc-files/js/docs/units-rs.md +35 -0
  36. package/rules/doc-files/js/docs/units.md +30 -0
  37. package/rules/doc-files/js/lint.mjs +96 -0
  38. package/{skills → rules}/doc-files/js/units-rs.mjs +37 -17
  39. package/rules/doc-files/lint/docs/lint.md +37 -0
  40. package/rules/doc-files/lint/lint.mjs +105 -0
  41. package/rules/doc-files/meta.json +1 -0
  42. package/rules/docker/docs/fix.md +21 -161
  43. package/rules/efes/docs/fix.md +23 -194
  44. package/rules/feedback/docs/fix.md +10 -8
  45. package/rules/ga/docs/fix.md +10 -5
  46. package/rules/ga/meta.json +1 -1
  47. package/rules/graphql/docs/fix.md +23 -119
  48. package/rules/hasura/docs/fix.md +19 -5
  49. package/rules/hasura/js/docs/internal_urls.md +34 -307
  50. package/rules/image-avif/docs/fix.md +16 -127
  51. package/rules/image-compress/docs/fix.md +20 -141
  52. package/rules/image-compress/js/docs/package_setup.md +22 -182
  53. package/rules/js-bun-db/docs/fix.md +23 -139
  54. package/rules/js-bun-db/js/docs/safety.md +33 -221
  55. package/rules/js-bun-redis/docs/fix.md +25 -114
  56. package/rules/js-bun-redis/js/docs/imports.md +18 -166
  57. package/rules/js-lint/docs/fix.md +30 -108
  58. package/rules/js-lint/js/docs/lint-findings.md +37 -17
  59. package/rules/js-lint/js/docs/lint.md +22 -238
  60. package/rules/js-lint/js/docs/tooling.md +34 -331
  61. package/rules/js-lint/js/lint.mjs +19 -12
  62. package/rules/js-lint/js-lint.mdc +1 -1
  63. package/rules/js-lint/meta.json +1 -1
  64. package/rules/js-lint-ci/docs/fix.md +16 -149
  65. package/rules/js-lint-ci/js/docs/lint.md +16 -136
  66. package/rules/js-lint-ci/js-lint-ci.mdc +1 -1
  67. package/rules/js-lint-ci/meta.json +1 -1
  68. package/rules/js-mssql/docs/fix.md +18 -123
  69. package/rules/js-mssql/js/docs/deps.md +28 -251
  70. package/rules/js-run/docs/fix.md +23 -138
  71. package/rules/js-run/js/docs/runtime.md +24 -378
  72. package/rules/k8s/docs/fix.md +18 -123
  73. package/rules/nginx-default-tpl/docs/fix.md +22 -118
  74. package/rules/nginx-default-tpl/js/docs/template.md +38 -360
  75. package/rules/npm-module/docs/fix.md +27 -89
  76. package/rules/npm-module/js/docs/header_doc_pointer.md +15 -15
  77. package/rules/npm-module/js/docs/package_structure.md +36 -258
  78. package/rules/npm-module/js/docs/rule_meta.md +25 -127
  79. package/rules/npm-module/js/docs/skill_meta.md +18 -180
  80. package/rules/npm-module/js/rule_meta.mjs +3 -3
  81. package/rules/php/docs/fix.md +21 -98
  82. package/rules/php/js/docs/tooling.md +20 -143
  83. package/rules/python/docs/fix.md +25 -157
  84. package/rules/python/js/docs/applies.md +20 -98
  85. package/rules/python/js/docs/tooling.md +27 -144
  86. package/rules/rego/docs/fix.md +24 -112
  87. package/rules/rego/js/docs/applies.md +20 -164
  88. package/rules/rego/js/docs/lint.md +15 -110
  89. package/rules/rego/meta.json +1 -1
  90. package/rules/release/docs/fix.md +16 -114
  91. package/rules/rust/docs/fix.md +24 -119
  92. package/rules/rust/js/docs/applies.md +20 -129
  93. package/rules/security/docs/fix.md +21 -78
  94. package/rules/security/js/docs/sample_secret.md +23 -182
  95. package/rules/security/js/docs/trufflehog.md +19 -128
  96. package/rules/security/meta.json +1 -1
  97. package/rules/style-lint/docs/fix.md +16 -150
  98. package/rules/style-lint/js/docs/lint.md +21 -172
  99. package/rules/style-lint/js/docs/tooling.md +19 -184
  100. package/rules/style-lint/js/lint.mjs +4 -3
  101. package/rules/style-lint/meta.json +1 -1
  102. package/rules/tauri/docs/fix.md +26 -152
  103. package/rules/tauri/js/docs/cargo_mutants_config.md +21 -159
  104. package/rules/tauri/js/docs/tooling.md +20 -217
  105. package/rules/test/docs/fix.md +19 -127
  106. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +15 -127
  107. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +17 -153
  108. package/rules/test/js/docs/cargo_mutants_config.md +24 -164
  109. package/rules/test/js/docs/location.md +24 -126
  110. package/rules/test/js/docs/no-process-chdir.md +20 -151
  111. package/rules/test/js/docs/no-relative-fs-path.md +24 -261
  112. package/rules/test/js/docs/stryker_config.md +48 -148
  113. package/rules/test/js/docs/vitest-config-pool-forks.md +21 -164
  114. package/rules/text/docs/fix.md +25 -113
  115. package/rules/text/js/docs/forbidden-prettier.md +21 -132
  116. package/rules/text/js/docs/formatting.md +60 -251
  117. package/rules/text/js/docs/lint.md +17 -114
  118. package/rules/text/js/lint.mjs +5 -3
  119. package/rules/text/lint/docs/lint.md +1 -1
  120. package/rules/text/lint/docs/run-dotenv-linter.md +1 -1
  121. package/rules/text/lint/docs/run-shellcheck.md +1 -1
  122. package/rules/text/lint/lint.mjs +13 -9
  123. package/rules/text/lint/run-dotenv-linter.mjs +13 -10
  124. package/rules/text/lint/run-shellcheck.mjs +10 -6
  125. package/rules/text/meta.json +1 -1
  126. package/rules/vue/docs/fix.md +25 -118
  127. package/rules/vue/js/docs/packages.md +25 -323
  128. package/rules/worktree/docs/fix.md +31 -150
  129. package/scripts/coverage-classify/docs/index.md +23 -209
  130. package/scripts/coverage-classify/docs/verdict-schema.md +14 -159
  131. package/scripts/dispatcher/docs/trace.md +35 -0
  132. package/scripts/docs/auto-rules.md +37 -361
  133. package/scripts/docs/lint-cli.md +12 -13
  134. package/scripts/docs/post-tool-use-fix.md +16 -15
  135. package/scripts/docs/skills-cli.md +26 -23
  136. package/scripts/docs/sync-claude-config.md +94 -34
  137. package/scripts/docs/worktree-cli.md +11 -34
  138. package/scripts/lib/docs/assert-project-root.md +14 -16
  139. package/scripts/lib/docs/changed-files.md +24 -139
  140. package/scripts/lib/docs/discover-check-rules-from-cursor.md +14 -146
  141. package/scripts/lib/docs/rule-meta.md +1 -1
  142. package/scripts/lib/docs/rule-predicates.md +20 -17
  143. package/scripts/lib/docs/run-rule-cli.md +14 -18
  144. package/scripts/lib/docs/run-rule.md +13 -20
  145. package/scripts/lib/docs/run-standard-rule.md +12 -15
  146. package/scripts/lib/docs/sync-gitignore-worktree.md +15 -18
  147. package/scripts/lib/rule-meta.mjs +10 -6
  148. package/scripts/lib/rule-predicates.mjs +1 -1
  149. package/scripts/lint-cli.mjs +28 -20
  150. package/scripts/sync-claude-config.mjs +4 -1
  151. package/scripts/utils/docs/with-lock.md +19 -12
  152. package/scripts/utils/with-lock.mjs +4 -2
  153. package/skills/doc-aggregate/SKILL.md +2 -2
  154. package/skills/doc-aggregate/js/docgen-ignore.mjs +6 -6
  155. package/skills/doc-aggregate/js/docs/docgen-ignore.md +1 -1
  156. package/skills/doc-aggregate/js/docs/docgen-scan.md +78 -0
  157. package/skills/doc-files/.changes/260612-0031.md +5 -0
  158. package/skills/doc-files/.changes/260612-0036.md +5 -0
  159. package/skills/doc-files/.changes/260612-0114.md +5 -0
  160. package/skills/doc-files/SKILL.md +6 -6
  161. package/skills/fix/js/docs/llm-worker.md +17 -15
  162. package/skills/fix/js/docs/orchestrator.md +30 -23
  163. package/skills/fix/js/docs/t0.md +26 -16
  164. package/skills/start-check/js/docs/check.md +26 -22
  165. package/skills/taze/js/docs/diff.md +44 -20
  166. package/skills/doc-files/js/docs/docgen-prompts.md +0 -32
  167. package/skills/doc-files/js/docs/docgen-scan.md +0 -25
  168. package/skills/doc-files/js/docs/units-rs.md +0 -35
  169. /package/{skills → rules}/doc-files/js/docgen-crc.mjs +0 -0
  170. /package/{skills → rules}/doc-files/js/docgen-extract-anchors.mjs +0 -0
  171. /package/{skills → rules}/doc-files/js/docgen-prompts.mjs +0 -0
  172. /package/{skills → rules}/doc-files/js/units-js.mjs +0 -0
  173. /package/{skills → rules}/doc-files/js/units.mjs +0 -0
@@ -18,7 +18,8 @@ export const DOCGEN_IGNORE_GLOBS = Object.freeze([
18
18
  '**/demo/**',
19
19
  '**/docs/**',
20
20
  'npm/reports/**',
21
- 'npm/bin/**'
21
+ 'npm/bin/**',
22
+ 'npm/rules/k8s/js/manifests.mjs'
22
23
  ])
23
24
 
24
25
  const IGNORE_MATCHERS = DOCGEN_IGNORE_GLOBS.map(glob => picomatch(glob, { dot: true }))
@@ -229,7 +229,7 @@ function runDegradedReport(root) {
229
229
  })
230
230
  .join('\n')
231
231
  console.log(
232
- `⚠ doc-files: degraded-док ${degraded.length} (score < ${QUALITY_THRESHOLD}):\n${list}\n→ перегенеруй: npx @nitra/cursor doc-files gen --retry-degraded`
232
+ `⚠ doc-files: degraded-док ${degraded.length} (score < ${QUALITY_THRESHOLD}):\n${list}\n→ перегенеруй: npx @nitra/cursor fix-doc-files --retry-degraded`
233
233
  )
234
234
  return 0
235
235
  }
@@ -265,8 +265,16 @@ export async function runDocFilesCheckCli(argv) {
265
265
  } else if (gitMode) {
266
266
  sources = gitChangedSources(root)
267
267
  } else {
268
+ // Значення прапорців виключаємо за індексом: порівняння за значенням при
269
+ // відсутньому `--max` (maxIdx = -1) викидало argv[0] — перший шлях губився
270
+ const flagValueIdxs = new Set(
271
+ ['--max', '--root']
272
+ .map(f => argv.indexOf(f))
273
+ .filter(i => i !== -1)
274
+ .map(i => i + 1)
275
+ )
268
276
  sources = argv
269
- .filter(a => !a.startsWith('--') && a !== argv[maxIdx + 1])
277
+ .filter((a, i) => !a.startsWith('--') && !flagValueIdxs.has(i))
270
278
  .map(a => toRelSource(root, a))
271
279
  .filter(rel => rel && isDocCandidate(root, rel) && existsSync(join(root, rel)))
272
280
  }
@@ -277,7 +285,7 @@ export async function runDocFilesCheckCli(argv) {
277
285
  // Великий прогін: Stop-гейт не блокує, лише попереджає (захист від нескінченного блоку).
278
286
  if (gitMode && stale.length > gateMax) {
279
287
  console.error(
280
- `⚠ doc-files: застарілих док ${stale.length} (> ${gateMax}) — гейт не блокує. Запусти масовий прогін:\n npx @nitra/cursor doc-files gen`
288
+ `⚠ doc-files: застарілих док ${stale.length} (> ${gateMax}) — гейт не блокує. Запусти масовий прогін:\n npx @nitra/cursor fix-doc-files`
281
289
  )
282
290
  return 0
283
291
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  docgen:
3
- source: npm/skills/doc-files/js/docgen-crc.mjs
3
+ source: npm/rules/doc-files/js/docgen-crc.mjs
4
4
  crc: 54e8e12b
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  docgen:
3
- source: npm/skills/doc-files/js/docgen-extract-anchors.mjs
3
+ source: npm/rules/doc-files/js/docgen-extract-anchors.mjs
4
4
  crc: 49749e9c
5
5
  score: 80
6
6
  ---
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  docgen:
3
- source: npm/skills/doc-files/js/docgen-extract.mjs
4
- crc: 1592076b
3
+ source: npm/rules/doc-files/js/docgen-extract.mjs
4
+ crc: e790ff64
5
5
  score: 100
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  docgen:
3
- source: npm/skills/doc-files/js/docgen-files-batch.mjs
4
- crc: 5c9b8d72
3
+ source: npm/rules/doc-files/js/docgen-files-batch.mjs
4
+ crc: 6f01f8b9
5
5
  score: 95
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  docgen:
3
- source: npm/skills/doc-files/js/docgen-gen.mjs
4
- crc: e2af04d6
3
+ source: npm/rules/doc-files/js/docgen-gen.mjs
4
+ crc: 70215974
5
5
  score: 100
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  docgen:
3
- source: npm/skills/doc-files/js/docgen-ignore.mjs
4
- crc: f9206fe0
3
+ source: npm/rules/doc-files/js/docgen-ignore.mjs
4
+ crc: c17cd785
5
5
  score: 100
6
6
  ---
7
7
 
@@ -13,7 +13,7 @@ DOCGEN_IGNORE_GLOBS
13
13
  Список шляхів, які docgen повинен ігнорувати.
14
14
 
15
15
  isDocgenIgnored
16
- Перевіряє, чи вказаний шлях знаходиться у списку ігнорованих шляхів. Повертає булеве значення.
16
+ Перевіряє, чи шлях знаходиться у списку ігнорованих шляхів.
17
17
 
18
18
  ## Поведінка
19
19
 
@@ -25,7 +25,7 @@ isDocgenIgnored
25
25
 
26
26
  ## Публічний API
27
27
 
28
- DOCGEN_IGNORE_GLOBS — Базовий список glob-ів для ігнорування у `docgen`.
28
+ DOCGEN_IGNORE_GLOBS — Список glob-ів для ігнорування у `docgen`.
29
29
  isDocgenIgnored — Визначає, чи шлях повинен бути пропущений `docgen`. Для `kind = 'dir'` працює і на підкаталоги, наприклад, `**\\/demo/**` спрацьовує на `demo/x` під час рекурсивного обходу.
30
30
 
31
31
  ## Гарантії поведінки
@@ -0,0 +1,39 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/js/docgen-prompts.mjs
4
+ crc: 72ac304f
5
+ score: 100
6
+ ---
7
+
8
+ # docgen-prompts.mjs
9
+
10
+ ## Огляд
11
+
12
+ Файл надає інструкції для створення лаконічної поведінкової документації українською мовою. Він містить інструменти для формування, перевірки та виправлення тексту документації, а також для визначення гарантій щодо роботи файлу
13
+
14
+ ## Поведінка
15
+
16
+ Поведінка:
17
+ - STYLE: Надає інструкцію для генерації лаконічної поведінкової документації українською мовою.
18
+ - sectionMessages: Генерує секційні промпти для різних частин документації.
19
+ - overviewMessages: Формує текст для узагальнення ролі та призначення файлу.
20
+ - criticMessages: Створює запити для перевірки чорновиків документації на наявність дефектів.
21
+ - refineMessages: Переписує та виправляє чорновки документації відповідно до критики.
22
+ - guaranteesFromMarkers: Формує список гарантій щодо поведінки файлу на основі маркерів.
23
+ - oneShotMessages: Створює універсальний запит для генерації повної документації.
24
+
25
+ ## Публічний API
26
+
27
+ STYLE — Формулює стиль файлу.
28
+ sectionMessages — Збирає повідомлення з мінімальним контекстом для кожної секції.
29
+ overviewMessages — Надає узагальнення поведінки файлу.
30
+ criticMessages — Виявляє дефекти в чорнетці секції.
31
+ refineMessages — Виправляє чорнетку, усуваючи виявлені дефекти.
32
+ guaranteesFromMarkers — Створює детермінований шаблон гарантій поведінки з фактів.
33
+
34
+ ## Гарантії поведінки
35
+
36
+ - Read-only: файл не виконує операцій запису у файлову систему.
37
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
38
+ - Кешує результати в межах одного прогону.
39
+ - Не звертається до мережі.
@@ -0,0 +1,54 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/js/docgen-scan.mjs
4
+ crc: dcc90d44
5
+ score: 100
6
+ ---
7
+
8
+ # docgen-scan.mjs
9
+
10
+ ## Огляд
11
+
12
+ isSourceFile перевіряє, чи є файл кодовим джерелом.
13
+ docPathForSource обчислює шлях md-документа для кодового файлу.
14
+ isDocCandidate перевіряє, чи підлягає файл документуванню.
15
+ describeFile описує кодовий файл, включаючи шлях доки та стан застарілості.
16
+ scanForDocFiles рекурсивно обходить дерево, повертаючи кандидати з інформацією про старілість.
17
+ resolveRoot парсить аргументи, щоб визначити абсолютний корінь.
18
+ runDocFilesScanCli сканує дерево і друкує JSON-масив усіх кодових файлів зі станом застарілості.
19
+ runDocFilesCheckCli детектує застарілість для хук'ів, гейтів або інших режимів, повертаючи код виходу.
20
+
21
+ ## Поведінка
22
+
23
+ isSourceFile Обчислює, чи є файл кодовим джерелом.
24
+ docPathForSource Обчислює шлях md-документа для кодового файлу.
25
+ isDocCandidate Перевіряє, чи підлягає файл документуванню.
26
+ describeFile Описує кодовий файл, включаючи шлях доки та стан застарілості.
27
+ scanForDocFiles Рекурсивно обходить дерево, повертаючи кандидати з інформацією про старілість.
28
+ resolveRoot Парсить аргументи, щоб визначити абсолютний корінь.
29
+ runDocFilesScanCli Сканує дерево і друкує JSON-масив усіх кодових файлів зі станом застарілості.
30
+ runDocFilesCheckCli Детектує застарілість для хук'ів, гейтів або інших режимів, повертаючи код виходу.
31
+
32
+ ## Публічний API
33
+
34
+ isSourceFile — перевіряє, чи є файл коду для документування.
35
+ docPathForSource — обчислює шлях до документа для кодового файлу, розміщуючи його в теці `docs/` поруч із джерелом. Якщо шлях відносний, документ також відносний; якщо абсолютний, документ залишається абсолютним.
36
+ isDocCandidate — визначає, чи підлягає кодовий файл документуванню: має правильне розширення, не є тестом, не знаходиться в ігнорованому списку, і не є кореневим системним документуванням.
37
+ describeFile — надає опис кодового файлу: шлях до джерела, шлях до документа та стан застарілості за CRC.
38
+ scanForDocFiles — рекурсивно переглядає дерево від заданого кореня і повертає кодові файли разом зі станом застарілості.
39
+ resolveRoot — парсить аргумент `--root <dir>` з командного рядка; за замовчуванням використовує поточну робочу директорію.
40
+ runDocFilesScanCli — сканує дерево і виводить JSON-масив усіх кодових файлів із зазначенням їхнього стану застарілості.
41
+ runDocFilesCheckCli — виконує перевірку застарілості для хуків та командного рядка через інструмент `doc-files check`.
42
+ Режими — це способи виконання:
43
+ --hook — бере шлях до файлу з вводу JSON і перевіряє один файл.
44
+ --git — перевіряє різницю в Git (`git diff --name-only HEAD`) з урахуванням порогу `--max` (за замовчуванням 50); якщо застарілості більше, не блокує (виходить з кодом 0 з попередженням).
45
+ --degraded — генерує інформаційний звіт про документи, які мають оцінку нижче встановленого порогу (виходить з кодом 0).
46
+ <paths…> — використовується для визначення явних шляхів до джерел.
47
+ Exit 2 — повертається, якщо знайдено застарілі дані; повертається 0, якщо дані свіжі або пройдено перевищення порогу.
48
+
49
+ ## Гарантії поведінки
50
+
51
+ - Read-only: файл не виконує операцій запису у файлову систему.
52
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
53
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
54
+ - Не звертається до мережі.
@@ -0,0 +1,36 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/js/lint.mjs
4
+ crc: f25a3bbe
5
+ score: 100
6
+ ---
7
+
8
+ # lint.mjs
9
+
10
+ ## Огляд
11
+
12
+ Адаптер правила doc-files до агрегатора `n-cursor lint`. Дає агрегатору відповідь на одне
13
+ питання: чи має кожен дотичний кодовий файл актуальну файлову документацію поряд із собою.
14
+
15
+ ## Поведінка
16
+
17
+ У quick-фазі отримує список змінених файлів і зводить його до набору джерел для перевірки в
18
+ **обидва** боки: змінене джерело перевіряється проти своєї доки, а змінена або видалена дока
19
+ (`<dir>/docs/<stem>.md`) повертається до відповідного джерела з тим самим іменем у каталозі над
20
+ `docs/`. У ci-фазі (списку немає) обходить усе дерево.
21
+
22
+ Порушенням вважається відсутня дока (`missing`) або розбіжність контрольної суми джерела з тією,
23
+ що записана у frontmatter доки (`crc-mismatch`). Документ із низькою якістю, але свіжою сумою —
24
+ не порушення.
25
+
26
+ ## Публічний API
27
+
28
+ `lint` — перевіряє документацію для переданого набору змінених файлів (або всього репозиторію,
29
+ якщо набір не задано); повертає код виходу `1`, якщо є застарілі чи відсутні доки, інакше `0`.
30
+ Список проблемних файлів друкується у stderr із підказкою регенерувати.
31
+
32
+ ## Гарантії поведінки
33
+
34
+ - Детермінованість: жодного виклику мовної моделі, рішення лише за контрольною сумою.
35
+ - Реверс-мапінг доки до джерела перебирає лише кодові розширення і повертає наявний файл-кандидат.
36
+ - Порожній набір змінених файлів дає код `0` без обходу дерева.
@@ -0,0 +1,31 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/js/units-js.mjs
4
+ crc: 58b898cc
5
+ score: 100
6
+ ---
7
+
8
+ # units-js.mjs
9
+
10
+ ## Огляд
11
+
12
+ Файл парсить програму, ітеруючи по її елементах для збору юнітів. Для кожної декларації визначається опис. Для функцій та класів збираються виклики інших юнітів через ребра викликів.
13
+
14
+ ## Поведінка
15
+
16
+ 1. Парсинг програми.
17
+ 2. Ітерація по тілу програми.
18
+ 3. Збір юнітів.
19
+ 4. Для кожної декларації визначається опис.
20
+ 5. Для функцій та класів збираються виклики інших юнітів.
21
+ 6. Вибираються виклики інших юнітів з ребрами викликів.
22
+
23
+ ## Публічний API
24
+
25
+ extractUnitsJs — Юніт-шар для js/mjs/ts: top-level функції/класи/const-функції з тілом, JSDoc, прапором експорту і ребрами call-graph (виклики ІНШИХ юнітів у тілі).
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: файл не виконує операцій запису у файлову систему.
30
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
31
+ - Не звертається до мережі.
@@ -0,0 +1,35 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/js/units-rs.mjs
4
+ crc: 114e9a0b
5
+ score: 100
6
+ ---
7
+
8
+ # units-rs.mjs
9
+
10
+ ## Огляд
11
+
12
+ Файл аналізує структуру коду для вилучення інформації про юніти. Процес включає сканування рядків, ітерацію для визначення глибини блоків та позиції закриваючих лапок. Обробляються рядкові літерали для визначення межі блоків та підраховуються глибини блоків для визначення видимості. Обробляються декларації для визначення публічності. Витягується тіло функцій, структур, перерахувань або трейтів. Визначається експонування функцій через атрибути. Збирається документація перед декларацією. Формується об'єкт юніту з назвою, типом, публічністю, ім'я impl та діапазон. Визначається область видимості тіла за допомогою пошуку закриваючої фігу. Збираються виклики інших юнітів у тілі функції.
13
+
14
+ ## Поведінка
15
+
16
+ 1. Скан файлу по рядках.
17
+ 2. Ітерація по рядках для визначення глибини блоків.
18
+ 3. Обробка рядкових літералів для визначення позиції закриваючого лапки.
19
+ 4. Підрахунок глибини блоків для визначення видимості.
20
+ 5. Обробка декларацій для визначення публічності.
21
+ 6. Витягнення тіла функції, структури, перерахування або трейта.
22
+ 7. Визначення експонування функції через атрибути.
23
+ 8. Збір документації перед декларацією.
24
+ 9. Формування об'єкта юніту з назвою, типом, публічністю, ім'ям impl та діапазоном.
25
+ 10. Визначення області видимості тіла за допомогою пошуку закриваючої фігу.
26
+ 11. Збір викликів інших юнітів у тілі функції.
27
+
28
+ ## Публічний API
29
+
30
+ extractUnitsRs — Визначає top-level і impl-методи через підрахунок дужок по рядках. Обмеження: рядкові літерали з `{`/`}` всередині `{}` можуть дати хибну глибину.
31
+
32
+ ## Гарантії поведінки
33
+
34
+ - Read-only: файл не виконує операцій запису у файлову систему.
35
+ - Не звертається до мережі.
@@ -0,0 +1,30 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/js/units.mjs
4
+ crc: af91bd0d
5
+ score: 100
6
+ ---
7
+
8
+ # units.mjs
9
+
10
+ ## Огляд
11
+
12
+ Файл витягує одиниці з вмісту файлу залежно від розширення шляху. Якщо розширення належить до набору JS_EXT, використовується extractUnitsJs. Якщо розширення дорівнює 'rs', використовується extractUnitsRs. В інших випадках функція повертає null.
13
+
14
+ ## Поведінка
15
+
16
+ 1. extractUnits приймає вміст файлу та шлях файлу.
17
+ 2. Перевіряє розширення шляху.
18
+ 3. Якщо розширення знаходиться у наборі JS_EXT, викликає extractUnitsJs.
19
+ 4. Якщо розширення дорівнює 'rs', викликає extractUnitsRs.
20
+ 5. У інших випадках повертає null.
21
+
22
+ ## Публічний API
23
+
24
+ extractUnits — Визначає тип парсингу залежно від розширення файлу: js/mjs/ts використовує oxc AST, rs використовує regex+brace-counting, vue/py повертає null (повний шлях).
25
+
26
+ ## Гарантії поведінки
27
+
28
+ - Read-only: файл не виконує операцій запису у файлову систему.
29
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
30
+ - Не звертається до мережі.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Адаптер агрегатора `n-cursor lint` для правила doc-files.
3
+ *
4
+ * Quick-фаза отримує список змінених файлів і мапить їх у пари в **обидва** боки:
5
+ * - змінене **джерело** (`.js/.mjs/.ts/.vue/.py/.rs`) → перевірка його доки `<dir>/docs/<stem>.md`;
6
+ * - змінена/видалена **дока** (`<dir>/docs/<stem>.md`) → перевірка відповідного джерела
7
+ * (той самий stem у каталозі над текою `docs`).
8
+ * Ci-фаза (files === undefined) проганяє повний скан дерева.
9
+ *
10
+ * Порушення — `missing` ∪ `crc-mismatch` (детермінований CRC-детект, 0 LLM-токенів);
11
+ * degraded не блокує. Exit 1 — є stale; 0 — все свіже (конвенція агрегатора).
12
+ */
13
+ import { join, dirname, basename, extname } from 'node:path'
14
+ import { existsSync, readdirSync } from 'node:fs'
15
+
16
+ import { describeFile, isDocCandidate, isSourceFile, scanForDocFiles } from './docgen-scan.mjs'
17
+
18
+ /** Дока живе у `<dir>/docs/<stem>.md`; повертає `<dir>/<stem>` для реверс-мапінгу. */
19
+ const DOC_MD_RE = /(?:^|\/)docs\/[^/]+\.md$/u
20
+
21
+ /**
22
+ * Реверс-мапінг доки → джерело: для `<dir>/docs/<stem>.md` шукає у `<dir>` файл
23
+ * `<stem>.<ext>` із кодовим розширенням, що існує і є кандидатом на доку.
24
+ * @param {string} cwd корінь репо
25
+ * @param {string} docRel posix-шлях доки від кореня
26
+ * @returns {string|null} posix-шлях джерела або null
27
+ */
28
+ function sourceForDoc(cwd, docRel) {
29
+ const docsDir = dirname(docRel) // `<dir>/docs`
30
+ const srcDir = dirname(docsDir) // `<dir>`
31
+ const stem = basename(docRel, '.md')
32
+ let entries
33
+ try {
34
+ entries = readdirSync(join(cwd, srcDir), { withFileTypes: true })
35
+ } catch {
36
+ return null
37
+ }
38
+ for (const e of entries) {
39
+ if (!e.isFile() || !isSourceFile(e.name)) continue
40
+ if (basename(e.name, extname(e.name)) !== stem) continue
41
+ const rel = srcDir === '.' ? e.name : `${srcDir}/${e.name}`
42
+ if (isDocCandidate(cwd, rel)) return rel
43
+ }
44
+ return null
45
+ }
46
+
47
+ /**
48
+ * Зводить список змінених файлів у множину джерел-кандидатів для перевірки доки.
49
+ * @param {string[]} files змінені шляхи (posix або нативні)
50
+ * @param {string} cwd корінь репо
51
+ * @returns {string[]} унікальні posix-шляхи джерел
52
+ */
53
+ function sourcesFromChanged(files, cwd) {
54
+ const out = new Set()
55
+ for (const raw of files) {
56
+ const rel = raw.split('\\').join('/')
57
+ if (DOC_MD_RE.test(rel)) {
58
+ const src = sourceForDoc(cwd, rel)
59
+ if (src) out.add(src)
60
+ } else if (isDocCandidate(cwd, rel) && existsSync(join(cwd, rel))) {
61
+ out.add(rel)
62
+ }
63
+ }
64
+ return [...out]
65
+ }
66
+
67
+ /**
68
+ * Друкує список stale і повертає exit-код.
69
+ * @param {Array<{sourcePath:string, reason:string|null}>} stale застарілі описи
70
+ * @returns {number} 1 — є stale; 0 — немає
71
+ */
72
+ function reportStale(stale) {
73
+ if (stale.length === 0) return 0
74
+ const list = stale.map(f => ` - ${f.sourcePath} (${f.reason})`).join('\n')
75
+ process.stderr.write(
76
+ `✗ doc-files: документація застаріла/відсутня для ${stale.length} файл(ів):\n${list}\n→ перегенеруй: npx @nitra/cursor fix-doc-files\n`
77
+ )
78
+ return 1
79
+ }
80
+
81
+ /**
82
+ * Крок агрегатора lint для doc-files.
83
+ * @param {string[] | undefined} files quick: лише ці файли; undefined: весь репозиторій
84
+ * @param {string} [cwd] корінь репо
85
+ * @returns {Promise<number>} 0 — OK, 1 — є застарілі доки
86
+ */
87
+ export function lint(files, cwd = process.cwd()) {
88
+ if (files === undefined) {
89
+ const stale = scanForDocFiles(cwd).filter(f => f.stale)
90
+ return Promise.resolve(reportStale(stale))
91
+ }
92
+ const sources = sourcesFromChanged(files, cwd)
93
+ if (sources.length === 0) return Promise.resolve(0)
94
+ const stale = sources.map(src => describeFile(cwd, src)).filter(f => f.stale)
95
+ return Promise.resolve(reportStale(stale))
96
+ }
@@ -9,7 +9,10 @@
9
9
  function skipString(src, i) {
10
10
  i++ // відкриваючий "
11
11
  while (i < src.length) {
12
- if (src[i] === '\\') { i += 2; continue }
12
+ if (src[i] === '\\') {
13
+ i += 2
14
+ continue
15
+ }
13
16
  if (src[i] === '"') return i + 1
14
17
  i++
15
18
  }
@@ -38,8 +41,15 @@ function findClosingBrace(src, start) {
38
41
  i = end === -1 ? src.length : end + 2
39
42
  continue
40
43
  }
41
- if (ch === '"') { i = skipString(src, i); continue }
42
- if (ch === '{') { depth++; i++; continue }
44
+ if (ch === '"') {
45
+ i = skipString(src, i)
46
+ continue
47
+ }
48
+ if (ch === '{') {
49
+ depth++
50
+ i++
51
+ continue
52
+ }
43
53
  if (ch === '}') {
44
54
  depth--
45
55
  if (depth === 0) return i
@@ -73,12 +83,17 @@ function docBefore(lines, lineIdx) {
73
83
  return doc.join(' ').trim()
74
84
  }
75
85
 
76
- // Pub-items: pub fn / pub struct / pub enum / pub trait / pub type
86
+ // Pub-items матчаться у два кроки по trim-нутому рядку (прості регекспи без
87
+ // бектрекінгу): спершу опційний pub(...)-префікс, потім сама декларація.
77
88
  // Також ловить fn без pub (для localSymbols і impl-методів)
78
- const ITEM_RE = /^[ \t]*(pub(?:\([^)]*\))?\s+)?(?:async\s+)?(?:unsafe\s+)?(fn|struct|enum|trait|type)\s+(\w+)/
89
+ const PUB_PREFIX_RE = /^pub(?:\([^)]*\))?\s+/
90
+ const ITEM_DECL_RE = /^(?:async\s+)?(?:unsafe\s+)?(fn|struct|enum|trait|type)\s+(\w+)/
79
91
 
80
- // impl Type { або impl<T> Trait for Type {
81
- const IMPL_RE = /^[ \t]*(?:pub\s+)?impl(?:<[^>]*>)?\s+(?:(?:\w[\w:<>, ]*\s+for\s+))?(\w+)/
92
+ // impl Type { або impl<T> Trait for Type { — теж двокроково: голова `impl<...>`,
93
+ // далі тип після `for` (trait-impl) або перше слово (inherent impl)
94
+ const IMPL_HEAD_RE = /^impl(?:<[^>]*>)?\s+/
95
+ const IMPL_FOR_TYPE_RE = /\bfor\s+(\w+)/
96
+ const TYPE_NAME_RE = /^(\w+)/
82
97
 
83
98
  // Підозрілі exposure-атрибути, що роблять непуб-fn фактично публічними
84
99
  const EXPOSURE_ATTR_RE = /#\[(?:tauri::command|wasm_bindgen|uniffi::export|pyo3::pyfunction|napi)/
@@ -93,7 +108,7 @@ const CALL_RE = /\b([a-z_]\w*)\s*\(/g
93
108
  * хибну глибину (рідкісно в реальному Rust-коді з rustfmt).
94
109
  * @param {string} src вміст файлу
95
110
  * @param {string} [_relPath] резервний (не використовується)
96
- * @returns {Array<{name:string, kind:string, exported:boolean, implName:string|null, span:{start:number,end:number}, body:string, calls:string[], doc:string}>|null}
111
+ * @returns {Array<{name:string, kind:string, exported:boolean, implName:string|null, span:{start:number,end:number}, body:string, calls:string[], doc:string}>|null} юніти файлу (fn та impl-методи) або `null`, якщо юнітів не знайдено
97
112
  */
98
113
  export function extractUnitsRs(src, _relPath) {
99
114
  const lines = src.split('\n')
@@ -129,7 +144,7 @@ export function extractUnitsRs(src, _relPath) {
129
144
  }
130
145
 
131
146
  // Закриті impl-блоки прибираємо зі стека
132
- while (implStack.length > 0 && implStack[implStack.length - 1].openDepth > depth) {
147
+ while (implStack.length > 0 && implStack.at(-1).openDepth > depth) {
133
148
  implStack.pop()
134
149
  }
135
150
 
@@ -140,22 +155,27 @@ export function extractUnitsRs(src, _relPath) {
140
155
  nextFnExposed = true
141
156
  }
142
157
 
158
+ const trimmed = line.trimStart()
159
+
143
160
  // Impl-декларація (зазвичай глибина 0, але може бути в mod)
144
161
  if (depthAtStart <= 1) {
145
- const implM = line.match(IMPL_RE)
146
- if (implM && line.includes('{')) {
147
- implStack.push({ typeName: implM[1], openDepth: depth })
162
+ const headM = trimmed.match(IMPL_HEAD_RE)
163
+ if (headM && line.includes('{')) {
164
+ const rest = trimmed.slice(headM[0].length)
165
+ const typeM = rest.match(IMPL_FOR_TYPE_RE) ?? rest.match(TYPE_NAME_RE)
166
+ if (typeM) implStack.push({ typeName: typeM[1], openDepth: depth })
148
167
  }
149
168
  }
150
169
 
151
170
  // Елементи на глибині 0 (top-level) і 1 (всередині impl)
152
171
  if (depthAtStart <= 1) {
153
- const m = line.match(ITEM_RE)
172
+ const pubM = trimmed.match(PUB_PREFIX_RE)
173
+ const m = (pubM ? trimmed.slice(pubM[0].length) : trimmed).match(ITEM_DECL_RE)
154
174
  if (m) {
155
- const isPub = Boolean(m[1]) || (m[2] === 'fn' && nextFnExposed)
156
- if (m[2] === 'fn') nextFnExposed = false
157
- const kind = m[2]
158
- const name = m[3]
175
+ const isPub = Boolean(pubM) || (m[1] === 'fn' && nextFnExposed)
176
+ if (m[1] === 'fn') nextFnExposed = false
177
+ const kind = m[1]
178
+ const name = m[2]
159
179
  const doc = docBefore(lines, li)
160
180
 
161
181
  // Витягуємо тіло через findClosingBrace для fn/struct/enum/trait
@@ -0,0 +1,37 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/doc-files/lint/lint.mjs
4
+ crc: a30dd81f
5
+ score: 100
6
+ ---
7
+
8
+ # lint.mjs
9
+
10
+ ## Огляд
11
+
12
+ CLI-команда `lint-doc-files` — детермінований детектор застарілості файлових док. Працює без
13
+ мовної моделі скрізь: локально, в hook'ах і в CI.
14
+
15
+ ## Поведінка
16
+
17
+ Без прапорців або з переліком шляхів робить повний чи точковий детект і повертає код `1`, якщо є
18
+ застарілі/відсутні доки. `--missing-only` звужує до самих відсутніх. `--json` друкує машинний
19
+ лістинг усіх кандидатів зі станом і завершується кодом `0`. Режими `--hook`, `--git` і
20
+ `--degraded` делегуються детектору hook-протоколу (PostToolUse за одним файлом зі stdin, Stop-гейт
21
+ за зміненими у задачі джерелами з порогом, інформаційний звіт про доки нижчої якості).
22
+
23
+ Повний прогін серіалізується спільним локом під ключем, виведеним зі шляху каталогу, тож паралельні
24
+ запуски не накладаються; hook-форми навмисно виконуються без локу заради завжди-свіжого вердикту.
25
+
26
+ ## Публічний API
27
+
28
+ `runLintDocFilesCli` — точка входу команди: маршрутизує між делегатом hook-протоколу, JSON-лістингом
29
+ і повним/точковим детектом; повертає код виходу.
30
+ `runLintDocFilesSteps` — сама робота повного чи точкового детекту, придатна для прямого виклику з
31
+ тестів без локу; повертає `1` за наявності застарілих док, інакше `0`.
32
+
33
+ ## Гарантії поведінки
34
+
35
+ - Неіснуючий корінь дає зрозумілу помилку і код `1`.
36
+ - Повний прогін — код `1` (конвенція `lint-*`); hook/git — код `2` (blocking feedback для Claude Code).
37
+ - Жодних викликів мовної моделі.