@nitra/cursor 3.21.1 → 3.23.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 (231) hide show
  1. package/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
  2. package/CHANGELOG.md +37 -3
  3. package/bin/docs/n-cursor.md +636 -0
  4. package/bin/docs/rename-yaml-extensions.md +207 -0
  5. package/bin/n-cursor.js +30 -3
  6. package/package.json +1 -1
  7. package/rules/abie/docs/fix.md +18 -0
  8. package/rules/abie/js/docs/applies.md +26 -0
  9. package/rules/abie/js/docs/env_dns.md +32 -0
  10. package/rules/abie/js/docs/firebase_hosting.md +23 -0
  11. package/rules/abie/js/docs/hc_pairing.md +35 -0
  12. package/rules/abie/js/docs/ua_http_route.md +28 -0
  13. package/rules/abie/js/docs/ua_node_selector.md +28 -0
  14. package/rules/abie/lib/docs/enabled.md +29 -0
  15. package/rules/abie/lib/docs/env-dns.md +35 -0
  16. package/rules/abie/lib/docs/hc-yaml.md +33 -0
  17. package/rules/abie/lib/docs/http-route.md +44 -0
  18. package/rules/abie/lib/docs/k8s-tree.md +40 -0
  19. package/rules/abie/lib/docs/kustomization-patches.md +47 -0
  20. package/rules/abie/lib/docs/overlay-paths.md +38 -0
  21. package/rules/abie/lib/docs/yaml.md +29 -0
  22. package/rules/adr/docs/fix.md +148 -0
  23. package/rules/adr/js/docs/hooks.md +259 -0
  24. package/rules/bun/docs/fix.md +156 -0
  25. package/rules/bun/js/docs/layout.md +393 -0
  26. package/rules/capacitor/docs/fix.md +121 -0
  27. package/rules/capacitor/js/docs/platforms.md +295 -0
  28. package/rules/changelog/changelog.mdc +2 -2
  29. package/rules/changelog/docs/fix.md +174 -0
  30. package/rules/changelog/js/consistency.mjs +114 -13
  31. package/rules/changelog/js/docs/consistency.md +387 -0
  32. package/rules/changelog/lib/docs/package-manifest.md +210 -0
  33. package/rules/ci4/docs/fix.md +179 -0
  34. package/rules/ci4/js/docs/marksman_config.md +128 -0
  35. package/rules/docker/docker.mdc +8 -3
  36. package/rules/docker/docs/fix.md +171 -0
  37. package/rules/docker/js/docs/lint.md +258 -0
  38. package/rules/docker/lib/docs/docker-hadolint.md +184 -0
  39. package/rules/docker/lib/docs/docker-mirror.md +247 -0
  40. package/rules/docker/lib/docs/docker-native-addon.md +170 -0
  41. package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
  42. package/rules/docker/lint/docs/lint.md +193 -0
  43. package/rules/efes/docs/fix.md +203 -0
  44. package/rules/feedback/docs/fix.md +140 -0
  45. package/rules/flow/docs/fix.md +152 -0
  46. package/rules/ga/docs/fix.md +158 -0
  47. package/rules/ga/js/docs/lint.md +100 -0
  48. package/rules/ga/js/docs/workflows.md +217 -0
  49. package/rules/ga/lint/docs/lint.md +209 -0
  50. package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
  51. package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
  52. package/rules/graphql/docs/fix.md +126 -0
  53. package/rules/graphql/js/docs/tooling.md +264 -0
  54. package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
  55. package/rules/hasura/docs/fix.md +120 -0
  56. package/rules/hasura/hasura.mdc +14 -0
  57. package/rules/hasura/js/docs/internal_urls.md +326 -0
  58. package/rules/image-avif/docs/fix.md +132 -0
  59. package/rules/image-avif/js/docs/avif_generation.md +241 -0
  60. package/rules/image-compress/docs/fix.md +150 -0
  61. package/rules/image-compress/js/docs/package_setup.md +191 -0
  62. package/rules/js-bun-db/docs/fix.md +148 -0
  63. package/rules/js-bun-db/js/docs/safety.md +231 -0
  64. package/rules/js-bun-db/js-bun-db.mdc +42 -13
  65. package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
  66. package/rules/js-bun-redis/docs/fix.md +123 -0
  67. package/rules/js-bun-redis/js/docs/imports.md +176 -0
  68. package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
  69. package/rules/js-lint/docs/fix.md +117 -0
  70. package/rules/js-lint/js/docs/lint.md +250 -0
  71. package/rules/js-lint/js/docs/tooling.md +348 -0
  72. package/rules/js-lint/js/docs/utils_imports.md +207 -0
  73. package/rules/js-lint/js/lint-findings.mjs +110 -0
  74. package/rules/js-lint/js/lint.mjs +86 -15
  75. package/rules/js-lint-ci/docs/fix.md +154 -0
  76. package/rules/js-lint-ci/js/docs/lint.md +144 -0
  77. package/rules/js-mssql/docs/fix.md +128 -0
  78. package/rules/js-mssql/js/docs/deps.md +263 -0
  79. package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
  80. package/rules/js-run/docs/fix.md +144 -0
  81. package/rules/js-run/js/docs/runtime.md +388 -0
  82. package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
  83. package/rules/js-run/lib/docs/check-env-scan.md +433 -0
  84. package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
  85. package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
  86. package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
  87. package/rules/k8s/docs/fix.md +129 -0
  88. package/rules/k8s/js/docs/manifests.md +344 -0
  89. package/rules/k8s/js/manifests.mjs +6 -2
  90. package/rules/k8s/k8s.mdc +4 -2
  91. package/rules/k8s/lint/docs/lint.md +411 -0
  92. package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
  93. package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
  94. package/rules/nginx-default-tpl/docs/fix.md +124 -0
  95. package/rules/nginx-default-tpl/js/docs/template.md +378 -0
  96. package/rules/npm-module/docs/fix.md +98 -0
  97. package/rules/npm-module/js/docs/package_structure.md +274 -0
  98. package/rules/npm-module/js/docs/rule_meta.md +137 -0
  99. package/rules/npm-module/js/docs/skill_meta.md +190 -0
  100. package/rules/php/docs/fix.md +107 -0
  101. package/rules/php/js/docs/tooling.md +152 -0
  102. package/rules/php/lint/docs/lint.md +215 -0
  103. package/rules/python/docs/fix.md +163 -0
  104. package/rules/python/js/docs/applies.md +108 -0
  105. package/rules/python/js/docs/tooling.md +153 -0
  106. package/rules/python/lint/docs/lint.md +322 -0
  107. package/rules/rego/docs/fix.md +121 -0
  108. package/rules/rego/js/docs/applies.md +174 -0
  109. package/rules/rego/js/docs/lint.md +118 -0
  110. package/rules/rego/lint/docs/lint.md +204 -0
  111. package/rules/release/docs/change.md +185 -0
  112. package/rules/release/docs/fix.md +119 -0
  113. package/rules/release/docs/release.md +222 -0
  114. package/rules/release/lib/docs/aggregate.md +246 -0
  115. package/rules/release/lib/docs/change-file.md +200 -0
  116. package/rules/release/lib/docs/fallback.md +203 -0
  117. package/rules/rust/docs/fix.md +129 -0
  118. package/rules/rust/js/docs/applies.md +140 -0
  119. package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
  120. package/rules/security/docs/fix.md +86 -0
  121. package/rules/security/js/docs/lint.md +171 -0
  122. package/rules/security/js/docs/sample_secret.md +190 -0
  123. package/rules/security/js/docs/trufflehog.md +137 -0
  124. package/rules/security/js/lint.mjs +9 -1
  125. package/rules/style-lint/docs/fix.md +155 -0
  126. package/rules/style-lint/js/docs/lint.md +184 -0
  127. package/rules/style-lint/js/docs/tooling.md +194 -0
  128. package/rules/tauri/docs/fix.md +158 -0
  129. package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
  130. package/rules/tauri/js/docs/tooling.md +228 -0
  131. package/rules/test/coverage/coverage.mjs +15 -3
  132. package/rules/test/docs/fix.md +132 -0
  133. package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
  134. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
  135. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
  136. package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
  137. package/rules/test/js/docs/cargo_mutants_config.md +173 -0
  138. package/rules/test/js/docs/location.md +136 -0
  139. package/rules/test/js/docs/no-process-chdir.md +160 -0
  140. package/rules/test/js/docs/no-relative-fs-path.md +271 -0
  141. package/rules/test/js/docs/stryker_config.md +152 -0
  142. package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
  143. package/rules/text/docs/fix.md +118 -0
  144. package/rules/text/js/docs/forbidden-prettier.md +143 -0
  145. package/rules/text/js/docs/formatting.md +256 -0
  146. package/rules/text/js/docs/lint.md +122 -0
  147. package/rules/text/lint/docs/lint.md +220 -0
  148. package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
  149. package/rules/text/lint/docs/run-shellcheck.md +212 -0
  150. package/rules/text/lint/docs/run-v8r.md +197 -0
  151. package/rules/vue/docs/fix.md +127 -0
  152. package/rules/vue/js/docs/packages.md +335 -0
  153. package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
  154. package/rules/worktree/docs/fix.md +161 -0
  155. package/schemas/rule-meta.json +5 -1
  156. package/scripts/auto-rules.mjs +7 -4
  157. package/scripts/coverage-classify/docs/apply.md +202 -0
  158. package/scripts/coverage-classify/docs/cache.md +203 -0
  159. package/scripts/coverage-classify/docs/index.md +218 -0
  160. package/scripts/coverage-classify/docs/prompt.md +132 -0
  161. package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
  162. package/scripts/coverage-fix-extract.mjs +122 -0
  163. package/scripts/coverage-fix.mjs +1 -1
  164. package/scripts/dispatcher/docs/graph.md +346 -0
  165. package/scripts/dispatcher/docs/index.md +236 -0
  166. package/scripts/dispatcher/docs/trace.md +296 -0
  167. package/scripts/dispatcher/index.mjs +1 -1
  168. package/scripts/dispatcher/lib/active.mjs +4 -8
  169. package/scripts/dispatcher/lib/commands.mjs +7 -11
  170. package/scripts/dispatcher/lib/docs/active.md +348 -0
  171. package/scripts/dispatcher/lib/docs/artifact.md +232 -0
  172. package/scripts/dispatcher/lib/docs/budget.md +167 -0
  173. package/scripts/dispatcher/lib/docs/capability.md +196 -0
  174. package/scripts/dispatcher/lib/docs/commands.md +210 -0
  175. package/scripts/dispatcher/lib/docs/events.md +182 -0
  176. package/scripts/dispatcher/lib/docs/executor.md +190 -0
  177. package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
  178. package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
  179. package/scripts/dispatcher/lib/docs/gate.md +231 -0
  180. package/scripts/dispatcher/lib/docs/level.md +335 -0
  181. package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
  182. package/scripts/dispatcher/lib/docs/plan.md +200 -0
  183. package/scripts/dispatcher/lib/docs/planner.md +269 -0
  184. package/scripts/dispatcher/lib/docs/review.md +255 -0
  185. package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
  186. package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
  187. package/scripts/dispatcher/lib/docs/spec.md +203 -0
  188. package/scripts/dispatcher/lib/docs/state-store.md +303 -0
  189. package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
  190. package/scripts/dispatcher/lib/executor.mjs +6 -1
  191. package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
  192. package/scripts/dispatcher/lib/level.mjs +29 -3
  193. package/scripts/dispatcher/lib/review.mjs +1 -1
  194. package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
  195. package/scripts/docs/auto-rules.md +376 -0
  196. package/scripts/docs/auto-skills.md +173 -0
  197. package/scripts/docs/build-agents-commands.md +183 -0
  198. package/scripts/docs/cli-entry.md +153 -0
  199. package/scripts/docs/coverage-fix.md +177 -0
  200. package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
  201. package/scripts/lib/changed-files.mjs +4 -1
  202. package/scripts/lib/diff-added-lines.mjs +85 -0
  203. package/scripts/lib/docs/changed-files.md +149 -0
  204. package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
  205. package/scripts/lib/docs/check-reporter.md +175 -0
  206. package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
  207. package/scripts/lib/docs/discover-checkable-rules.md +165 -0
  208. package/scripts/lib/docs/ensure-tool.md +254 -0
  209. package/scripts/lib/docs/generated-markdown.md +275 -0
  210. package/scripts/lib/docs/gha-workflow.md +326 -0
  211. package/scripts/lib/docs/inline-template-links.md +303 -0
  212. package/scripts/lib/docs/list-rule-ids.md +156 -0
  213. package/scripts/lib/docs/load-cursor-config.md +147 -0
  214. package/scripts/lib/docs/mirror-parity.md +167 -0
  215. package/scripts/lib/worktree.mjs +26 -0
  216. package/scripts/worktree-cli.mjs +12 -2
  217. package/skills/coverage-fix/SKILL.md +34 -45
  218. package/skills/docgen/SKILL.md +44 -23
  219. package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
  220. package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
  221. package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
  222. package/skills/docgen/js/docgen-ignore.mjs +54 -0
  223. package/skills/docgen/js/docgen-scan.mjs +37 -21
  224. package/skills/llm-patch/SKILL.md +23 -2
  225. package/skills/start-check/SKILL.md +26 -53
  226. package/skills/start-check/js/check.mjs +211 -0
  227. package/skills/taze/SKILL.md +9 -3
  228. package/skills/taze/js/diff.mjs +154 -0
  229. package/types/bin/n-cursor.d.ts +1 -1
  230. package/skills/fix-tests/SKILL.md +0 -119
  231. package/skills/fix-tests/meta.json +0 -1
@@ -0,0 +1,167 @@
1
+ ## Огляд
2
+
3
+ Модуль `mirror-parity.mjs` забезпечує перевірку парності (parity) між «дзеркальними» файлами правил Cursor у `.cursor/rules/n-<id>.mdc` та їх канонічними джерелами у `npm/rules/<id>/<id>.mdc`. Ідея така: репозиторій містить два представлення одного й того ж правила:
4
+
5
+ - канонічне джерело правила лежить у `npm/rules/<id>/<id>.mdc` (з можливими посиланнями на шаблони у тій самій теці);
6
+ - дзеркало лежить у `.cursor/rules/n-<id>.mdc` і повинно бути результатом застосування трансформу `inlineTemplateLinks` до канону (тобто посилання на шаблони мають бути вставлені «інлайном»).
7
+
8
+ Коли канонічний `.mdc` змінюють і забувають регенерувати дзеркало, виникає «дрейф» (drift). Цей модуль:
9
+
10
+ - перелічує керовані дзеркала (ті, для яких знайдено канон);
11
+ - обчислює очікуваний вміст дзеркала через ту саму трансформацію, що і синхронізатор;
12
+ - виявляє ідентифікатори дрейфних правил (де `actual ≠ expected`).
13
+
14
+ Модуль використовується одночасно і як гард у тестах (очікувано `drift === []`), і для разової регенерації дзеркал. Контекст беклогу — адаптація flow #10.
15
+
16
+ ## Експорти / API
17
+
18
+ Модуль експортує три іменовані функції:
19
+
20
+ - `listManagedMirrors(repoRoot)` — синхронна; повертає опис керованих дзеркал.
21
+ - `expectedMirrorContent(canonicalPath)` — асинхронна (повертає `Promise<string>`); обчислює очікуваний вміст дзеркала.
22
+ - `findMirrorDrift(repoRoot)` — асинхронна; повертає масив id правил із дрейфом, відсортований за зростанням.
23
+
24
+ Жодних дефолтних експортів, побічних ефектів на рівні модуля немає (лише імпорти стандартних модулів і одного локального).
25
+
26
+ ## Функції
27
+
28
+ ### `listManagedMirrors(repoRoot)`
29
+
30
+ Сигнатура: `function listManagedMirrors(repoRoot: string): { id: string, mirrorPath: string, canonicalPath: string }[]`
31
+
32
+ Параметри:
33
+
34
+ - `repoRoot` — абсолютний шлях до кореня репозиторію (директорія, у якій лежать `.cursor/` та `npm/`).
35
+
36
+ Повертає масив об'єктів-описів дзеркал, де:
37
+
38
+ - `id` — ідентифікатор правила без префіксу `n-` і без розширення `.mdc` (наприклад, для файлу `n-bun.mdc` → `bun`);
39
+ - `mirrorPath` — абсолютний шлях до файлу дзеркала всередині `.cursor/rules/`;
40
+ - `canonicalPath` — абсолютний шлях до канонічного `.mdc` всередині `npm/rules/<id>/`.
41
+
42
+ Алгоритм:
43
+
44
+ 1. Будує шлях до `.cursor/rules`.
45
+ 2. Якщо такої директорії не існує (`existsSync` false) — повертає порожній масив (раннє повернення, безпечне для свіжих репо).
46
+ 3. Зчитує перелік файлів директорії синхронно (`readdirSync`).
47
+ 4. Фільтрує файли: повинні починатися з префіксу `'n-'` (константа `MIRROR_PREFIX`) і завершуватися розширенням `'.mdc'` (константа `MDC_EXT`).
48
+ 5. Мапить кожен файл у об'єкт із обчисленими шляхами: `id` отримується вирізанням префіксу і розширення (`f.slice(MIRROR_PREFIX.length, -MDC_EXT.length)`).
49
+ 6. Залишає лише ті записи, для яких реально існує канонічне джерело `canonicalPath` (друга фільтрація через `existsSync`). Це дозволяє пропускати «зовнішні» дзеркала, які не мають канону в монорепо.
50
+
51
+ Side effects: лише читання директорії з диску (синхронні `existsSync`, `readdirSync`). Жодних записів, мережевих викликів, мутацій глобального стану.
52
+
53
+ Помилки: якщо файлова система кине помилку під час `readdirSync` (наприклад, права доступу) — вона прокинеться вгору; для типового сценарію відсутності директорії `.cursor/rules` функція не падає (гард через `existsSync`).
54
+
55
+ ### `expectedMirrorContent(canonicalPath)`
56
+
57
+ Сигнатура: `function expectedMirrorContent(canonicalPath: string): Promise<string>`
58
+
59
+ Параметри:
60
+
61
+ - `canonicalPath` — абсолютний шлях до канонічного `.mdc`-файлу (`npm/rules/<id>/<id>.mdc`).
62
+
63
+ Повертає `Promise<string>` — очікуваний (нормалізований) текст дзеркала після інлайнінгу шаблонів. Хоча сама функція не використовує `async`/`await`, її результат типізовано як `Promise`, оскільки делегує виклик до `inlineTemplateLinks`, яка є асинхронною (її результат може бути thenable). Це означає, що споживач має `await`-нути повернене значення.
64
+
65
+ Алгоритм:
66
+
67
+ 1. Синхронно зчитує вміст канонічного файлу як UTF-8 (`readFileSync(canonicalPath, 'utf8')`).
68
+ 2. Викликає `inlineTemplateLinks(content, dirname(canonicalPath))`, передаючи прочитаний текст та теку канону як base для розв'язання шляхів до шаблонів.
69
+ 3. Повертає результат трансформації.
70
+
71
+ Side effects: одне читання з диску. Подальша поведінка щодо шаблонів інкапсульована в `inlineTemplateLinks` (див. її документацію). Контракт: цей же самий трансформ застосовує синк, тому очікуваний вміст детермінований по канону.
72
+
73
+ Помилки: якщо файл за `canonicalPath` не існує або недоступний — `readFileSync` кине помилку. Викличні сторони мають гарантувати існування (`listManagedMirrors` уже фільтрує неіснуючі канони).
74
+
75
+ ### `findMirrorDrift(repoRoot)`
76
+
77
+ Сигнатура: `async function findMirrorDrift(repoRoot: string): Promise<string[]>`
78
+
79
+ Параметри:
80
+
81
+ - `repoRoot` — абсолютний шлях до кореня репозиторію.
82
+
83
+ Повертає `Promise<string[]>` — відсортований за зростанням масив `id` правил, де дзеркало розійшлося з очікуваним вмістом.
84
+
85
+ Алгоритм:
86
+
87
+ 1. Заводить порожній масив `drift`.
88
+ 2. Послідовно (через `for...of`) обходить усі керовані дзеркала, отримані з `listManagedMirrors(repoRoot)`.
89
+ 3. Для кожного дзеркала `m`:
90
+ - `await`-ить очікуваний вміст через `expectedMirrorContent(m.canonicalPath)`;
91
+ - синхронно зчитує фактичний вміст файлу дзеркала `readFileSync(m.mirrorPath, 'utf8')`;
92
+ - якщо стрічки не дорівнюють — додає `m.id` до `drift`.
93
+ 4. Повертає `drift.sort()` (лексикографічне сортування).
94
+
95
+ Side effects: тільки читання з диску. Не записує жодних файлів, не пише в лог.
96
+
97
+ Зауваження щодо порівняння: рівність стрічок є точною (строге `!==` по UTF-8 тексту). Будь-яка різниця в пробілах, символах кінця рядка чи порядку інлайнених шаблонів буде розцінена як дрейф.
98
+
99
+ ## Залежності
100
+
101
+ Стандартна бібліотека Node.js:
102
+
103
+ - `node:fs` — `existsSync`, `readdirSync`, `readFileSync`. Усі виклики синхронні. Імпорт зроблено через `node:`-префікс для явної прив'язки до built-in модуля.
104
+ - `node:path` — `dirname`, `join`. Використовуються для безпечного складання абсолютних шляхів і отримання батьківської теки канону.
105
+
106
+ Локальні модулі:
107
+
108
+ - `./inline-template-links.mjs` — імпорт іменованої функції `inlineTemplateLinks(content, baseDir)`. Це той самий трансформ, що його використовує синк дзеркал; саме завдяки спільному використанню досягається консистентність між «гардом дрейфу» і «генератором дзеркал».
109
+
110
+ Зовнішніх npm-залежностей немає.
111
+
112
+ Константи на рівні модуля:
113
+
114
+ - `MIRROR_PREFIX = 'n-'` — префікс імені файлів дзеркал.
115
+ - `MDC_EXT = '.mdc'` — розширення файлів правил Cursor.
116
+
117
+ ## Потік виконання / Використання
118
+
119
+ Типові сценарії використання модуля:
120
+
121
+ 1. Тест-гард парності (CI). Споживач передає корінь репо й перевіряє, що дрейф порожній:
122
+
123
+ ```js
124
+ import { findMirrorDrift } from './mirror-parity.mjs'
125
+
126
+ const drift = await findMirrorDrift(repoRoot)
127
+ if (drift.length) {
128
+ throw new Error(`Mirror drift detected for: ${drift.join(', ')}`)
129
+ }
130
+ ```
131
+
132
+ 2. Разова регенерація дзеркал. Споживач отримує список керованих дзеркал і для кожного перезаписує файл дзеркала очікуваним вмістом:
133
+
134
+ ```js
135
+ import { writeFileSync } from 'node:fs'
136
+ import { listManagedMirrors, expectedMirrorContent } from './mirror-parity.mjs'
137
+
138
+ for (const m of listManagedMirrors(repoRoot)) {
139
+ const expected = await expectedMirrorContent(m.canonicalPath)
140
+ writeFileSync(m.mirrorPath, expected)
141
+ }
142
+ ```
143
+
144
+ (Сам модуль `mirror-parity.mjs` записів не виконує — це задача викличного скрипта.)
145
+
146
+ Внутрішній потік виконання `findMirrorDrift`:
147
+
148
+ 1. Виклик `listManagedMirrors(repoRoot)`:
149
+ - перевірка існування `.cursor/rules`;
150
+ - читання списку файлів;
151
+ - фільтрація за префіксом `n-` і розширенням `.mdc`;
152
+ - обчислення `id`, `mirrorPath`, `canonicalPath`;
153
+ - відкидання дзеркал без канону.
154
+ 2. Послідовний обхід отриманих описів:
155
+ - читання канону → `inlineTemplateLinks` → очікуваний текст;
156
+ - читання дзеркала → порівняння;
157
+ - накопичення id з дрейфом.
158
+ 3. Сортування результату та повернення.
159
+
160
+ Контрактна гарантія: якщо синк використовує ту ж саму функцію `inlineTemplateLinks` для генерації дзеркал, то регенерація закриває будь-який дрейф, виявлений `findMirrorDrift`. Якщо дрейф виявлено — він завжди вказує на людську помилку (зміна канону без регенерації) або на розбіжність версій трансформу.
161
+
162
+ Обмеження:
163
+
164
+ - Обхід послідовний (без `Promise.all`) — простіший контракт по помилках, але повільніший на великих наборах правил. Зважаючи на типову кількість правил у `.cursor/rules`, це не критично.
165
+ - Усі читання файлів синхронні, попри асинхронну сигнатуру `findMirrorDrift` — асинхронність зумовлена тільки інтерфейсом `inlineTemplateLinks`.
166
+ - Префікс `n-` зашитий константою — додавання інших префіксів дзеркал вимагатиме зміни модуля.
167
+ - Зовнішні дзеркала (без канону в `npm/rules/<id>/`) автоматично пропускаються; модуль не повідомляє про них окремо.
@@ -87,6 +87,32 @@ export function buildDescription({ branch, task, baseCommit, date }) {
87
87
  ].join('\n')
88
88
  }
89
89
 
90
+ /** Поріг переліку файлів у нагадуванні: понад нього показуємо лише кількість. */
91
+ const DIRTY_LIST_LIMIT = 10
92
+
93
+ /**
94
+ * Нагадування про незакомічені зміни основного дерева, які **не** потрапляють
95
+ * у новий worktree (він створюється від HEAD, без брудного стану). До
96
+ * `limit` файлів — перелік шляхів; більше — лише підсумкова кількість, щоб не
97
+ * залити екран. Призначене для виводу одразу після `worktree add`.
98
+ * @param {string} porcelain вивід `git status --porcelain` основного дерева
99
+ * @param {number} [limit] поріг переліку (понад нього — лише кількість)
100
+ * @returns {string | null} текст нагадування або `null`, якщо дерево чисте
101
+ */
102
+ export function buildDirtyNotice(porcelain, limit = DIRTY_LIST_LIMIT) {
103
+ // Порядок: XY + пробіл (3 символи) + шлях; для перейменування — `orig -> dest`.
104
+ const files = String(porcelain ?? '')
105
+ .split('\n')
106
+ .map(line => line.slice(3).trim())
107
+ .filter(Boolean)
108
+ if (files.length === 0) return null
109
+ const head = `⚠️ Основне дерево має ${files.length} незакомічених змін — вони НЕ потрапили в цей worktree (створено від HEAD).`
110
+ const tail = ' Закоміть потрібні файли, якщо worktree-скіл має їх бачити.'
111
+ if (files.length > limit) return `${head}\n${tail}`
112
+ const list = files.map(f => ` - ${f}`).join('\n')
113
+ return `${head}\n${list}\n${tail}`
114
+ }
115
+
90
116
  /**
91
117
  * `.md`-описи без відповідного зареєстрованого worktree-checkout.
92
118
  * @param {string[]} descFiles абсолютні шляхи `.worktrees/*.md`
@@ -2,7 +2,7 @@
2
2
  * CLI-оркестратор worktree-tool `n-cursor worktree` (виконавець конвенції `.worktrees/`).
3
3
  *
4
4
  * Підкоманди:
5
- * add <branch> "<опис>" — git worktree add .worktrees/<sanit> -b <branch> (від HEAD) + .md-опис
5
+ * add <branch> "<опис>" — git worktree add .worktrees/<sanit> -b <branch> (від HEAD) + .md-опис + нагадування про незакомічені зміни
6
6
  * remove <branch> [--force] — прибрати checkout + .md (гілку лишає)
7
7
  * list — git worktree list + вміст .md-описів
8
8
  * prune — git worktree prune + видалити осиротілі .md
@@ -16,7 +16,13 @@ import { join } from 'node:path'
16
16
  import { cwd as processCwd } from 'node:process'
17
17
 
18
18
  import { cleanupFlowSiblings } from './dispatcher/lib/state-store.mjs'
19
- import { buildDescription, findOrphanDescFiles, firstFreeBranch, worktreePaths } from './lib/worktree.mjs'
19
+ import {
20
+ buildDescription,
21
+ buildDirtyNotice,
22
+ findOrphanDescFiles,
23
+ firstFreeBranch,
24
+ worktreePaths
25
+ } from './lib/worktree.mjs'
20
26
 
21
27
  const USAGE = [
22
28
  'Usage:',
@@ -110,6 +116,9 @@ function cmdAdd(rest, ctx) {
110
116
  if (chosen !== branch) {
111
117
  ctx.log(`ℹ️ гілка/worktree "${branch}" уже існує — обрано вільну назву "${chosen}"`)
112
118
  }
119
+ // Знімаємо статус ДО створення worktree: інакше щойно створений checkout/опис у `.worktrees/`
120
+ // потрапили б у `git status` (коли `.worktrees/` не в .gitignore) і дали б хибне нагадування.
121
+ const dirty = buildDirtyNotice(git(['status', '--porcelain'], ctx.cwd).stdout)
113
122
  const added = git(['worktree', 'add', paths.checkout, '-b', chosen], ctx.cwd)
114
123
  if (added.status !== 0) {
115
124
  ctx.logError(`worktree add не вдався: ${added.stderr.trim()}`)
@@ -120,6 +129,7 @@ function cmdAdd(rest, ctx) {
120
129
  writeFileSync(paths.descFile, md, 'utf8')
121
130
  ctx.log(`✅ worktree: ${paths.checkout}`)
122
131
  ctx.log(` опис: ${paths.descFile}`)
132
+ if (dirty) ctx.log(dirty) // нагадування про незакомічені зміни основного дерева (зняте ДО add)
123
133
  return 0
124
134
  }
125
135
 
@@ -24,35 +24,46 @@ description: >-
24
24
 
25
25
  ## Workflow
26
26
 
27
- ### Крок 1: Запусти coverage
27
+ ### Крок 0: Визнач команди (з `package.json#scripts`)
28
28
 
29
- ```bash
30
- n-cursor coverage
31
- ```
29
+ Прочитай кореневий `package.json` і зафіксуй дві команди (перша що існує):
30
+
31
+ - **coverage-команда**: `scripts["coverage"]` → виклик `bun run coverage`; fallback `n-cursor coverage`
32
+ - **test-команда**: `scripts["test"]` → виклик `bun run test` (або те, що в скрипті); fallback `bun test`
32
33
 
33
- Або якщо є у `package.json#scripts`:
34
+ Далі по тексту «coverage-команда» / «test-команда» = знайдені тут значення.
35
+
36
+ ### Крок 1: Згенеруй або переви́користай `COVERAGE.md`
37
+
38
+ **Early-skip.** Якщо `COVERAGE.md` уже існує, свіжий (новіший за останню зміну source/тестів) і має секцію `## Вцілілі мутанти` — можеш одразу перейти до Кроку 2 без повторної генерації. Інакше — згенеруй звіт:
34
39
 
35
40
  ```bash
36
- bun run coverage
41
+ n-cursor coverage # або coverage-команда з Кроку 0
37
42
  ```
38
43
 
39
44
  Ця команда генерує `COVERAGE.md`. Якщо є survived mutants — COVERAGE.md матиме секцію `## Вцілілі мутанти` з JSON-блоком.
40
45
 
41
- ### Крок 2: Перевір вцілілих
46
+ ### Крок 2: Прочитай компактний index вцілілих
42
47
 
43
- Прочитай `COVERAGE.md`. Знайди секцію `## Вцілілі мутанти`. Знайди огороджений блок ` ```json ` і розбери JSON-масив.
48
+ > **Не читай `COVERAGE.md` сам.** Файл може важити мегабайти (секція `## Вцілілі
49
+ мутанти` на сотні файлів) — його читання спалило б сотні тисяч токенів. Важкий
50
+ > парсинг несе CLI; ти отримуєш лише крихітний index.
44
51
 
45
- Якщо секція відсутня або масив порожній — зупинись:
52
+ ```bash
53
+ n-cursor coverage-fix index
54
+ ```
55
+
56
+ Друкує компактний JSON-масив `[{ "file": "<path>", "mutants": <N> }]` (кілобайти, не мегабайти). Якщо `[]` — зупинись:
46
57
 
47
58
  ```
48
59
  ✓ Жодних вцілілих мутантів — mutation score повний. Coverage завершено.
49
60
  ```
50
61
 
51
- Запам'ятай `prevCount = масив.length`.
62
+ Запам'ятай `prevCount = сума всіх mutants` (загальна кількість вцілілих мутантів).
52
63
 
53
- ### Крок 3: Для кожного файлу — запускає Agent
64
+ ### Крок 3: Для кожного файлу — slice + Agent
54
65
 
55
- Згрупуй мутанти по полю `file`. Для кожної групи:
66
+ Для кожного запису `{ file, mutants }` з index:
56
67
 
57
68
  **3a. Визнач test файл (завжди у `tests/` директорії):**
58
69
 
@@ -65,42 +76,20 @@ bun run coverage
65
76
  - Оновити відносні imports (тепер `../` рівень вгору до source)
66
77
  3. Жоден не знайдено → буде створено `<dir>/tests/<basename>.test.mjs`
67
78
 
68
- **3b. Сформуй промпт для Agent:**
79
+ **3b. Отримай готовий зріз-промпт лише для цього файлу:**
69
80
 
81
+ ```bash
82
+ n-cursor coverage-fix slice --file <file>
70
83
  ```
71
- Тобі дані вцілілі мутанти зі Stryker для файлу `<file>`.
72
- Ці мутанти вціліли, бо наявні тести НЕ вловили конкретні зміни коду.
73
-
74
- **Вихідний код** (`<file>`):
75
- \`\`\`
76
- <зміст source-файлу>
77
- \`\`\`
78
-
79
- **Наявні тести** (`<test-file>`):
80
- \`\`\`
81
- <зміст test-файлу або "файл ще не існує">
82
- \`\`\`
83
-
84
- **Вцілілі мутанти** (кожен — зміна коду що НЕ вловлена):
85
- <для кожного мутанта:>
86
- - Рядок <line>, колонка <col>: `<original>` → `<replacement>` (тип: <mutantType>)
87
-
88
- **Завдання:**
89
- Допиши мінімальні test-cases у файл `<test-file>` які вловлять кожен мутант.
90
- Правила:
91
- - НЕ видаляй і НЕ змінюй наявні тести
92
- - Стиль тестів — відповідно до наявного файлу (той самий фреймворк, describe/test)
93
- - Якщо файл ще не існує — створи `<dir>/tests/<basename>.test.mjs` з правильними імпортами.
94
- Приклад: source `src/services/auth-store.js` → import `import { ... } from '../auth-store.js'`
95
- - Після написання запусти: `bun test <test-file>` і переконайся що тести проходять (виправ якщо падають, 1-2 спроби)
96
- ```
97
84
 
98
- **3c. Запусти Agent** з цим промптом. Дочекайся завершення.
85
+ CLI друкує самодостатній промпт **рівно для одного файлу**: список вцілілих мутантів цього файлу (рядок/колонка/`original → replacement`/тип) з контекстом ±3 рядки навколо кожного та фіксовані правила. Це і є «порція під когнітивне навантаження» одного субагента — нічого зайвого.
86
+
87
+ **3c. Запусти Agent** з цим зрізом як промптом, дописавши один рядок про цільовий test-файл із 3a (`<dir>/tests/<basename>.test.mjs`, з правильними відносними imports). Дочекайся завершення.
99
88
 
100
89
  ### Крок 4: Перевір що всі тести проходять
101
90
 
102
91
  ```bash
103
- bun test
92
+ bun test # або test-команда з Кроку 0
104
93
  ```
105
94
 
106
95
  Якщо падають — поверни відповідний Agent з помилкою і попроси виправити.
@@ -108,15 +97,15 @@ bun test
108
97
  ### Крок 5: Запусти coverage і порівняй
109
98
 
110
99
  ```bash
111
- n-cursor coverage
100
+ n-cursor coverage # або coverage-команда з Кроку 0
101
+ n-cursor coverage-fix index
112
102
  ```
113
103
 
114
- Прочитай новий `COVERAGE.md`. Розбери JSON-масив вцілілих.
115
- `newCount = новий масив.length`
104
+ `newCount = сума mutants` зі свіжого index (знову — без читання `COVERAGE.md` вручну).
116
105
 
117
106
  **Рішення:**
118
107
 
119
- - `newCount < prevCount` AND iterations < 3 → повтор з Кроку 2 з оновленим масивом
108
+ - `newCount < prevCount` AND iterations < 3 → повтор з Кроку 2 з оновленим index
120
109
  - `newCount >= prevCount` → конвергенція:
121
110
 
122
111
  ```
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: docgen
3
3
  description: >-
4
- Обходить проєкт і для кожного кодового файлу (js/mjs/ts/vue/py) пише вичерпну українську md-документацію у теку docs/ поряд із кодом — диспатчить окремого субагента на кожен файл, за правилами adr/ci4
4
+ Обходить проєкт і для кожного кодового файлу (js/mjs/ts/vue/py) пише лаконічну поведінкову українську md-документацію у теку docs/ поряд із кодом — диспатчить окремого субагента на кожен файл, за правилами adr/ci4
5
5
  ---
6
6
 
7
7
  # docgen — генерація документації по файлах
8
8
 
9
9
  ## Мета
10
10
 
11
- Для кожного кодового файлу проєкту створити вичерпну `.md`-документацію у теці `docs/`
11
+ Для кожного кодового файлу проєкту створити лаконічну поведінкову `.md`-документацію у теці `docs/`
12
12
  **поряд із самим файлом** (`<dir>/docs/<stem>.md`). Документацію пише **окремий субагент**
13
13
  на кожен файл — не один прохід, а батч-диспатч. Джерело правди стилю — правила `adr` і
14
14
  `ci4` (`docs/explanation`/`docs/adr`-каталоги з тих правил **не застосовуємо** — доку
@@ -46,13 +46,10 @@ Tier 3 — лише після завершення всього Tier 2.
46
46
  npx @nitra/cursor docgen scan
47
47
  ```
48
48
 
49
- Команда друкує JSON-масив об'єктів:
49
+ Команда друкує JSON-масив об'єктів. Усі шляхи в ньому — відносні до кореня проєкту:
50
50
 
51
51
  ```json
52
- [
53
- { "sourcePath": "/abs/src/lib/foo.js", "relSource": "src/lib/foo.js",
54
- "docPath": "/abs/src/lib/docs/foo.md", "exists": false }
55
- ]
52
+ [{ "sourcePath": "src/lib/foo.js", "docPath": "src/lib/docs/foo.md", "exists": false }]
56
53
  ```
57
54
 
58
55
  Розпарси JSON.
@@ -80,7 +77,7 @@ npx @nitra/cursor docgen scan
80
77
  Промпт кожного субагента (підстав `sourcePath` і `docPath`):
81
78
 
82
79
  ```
83
- Напиши вичерпну технічну документацію для одного файлу коду.
80
+ Напиши лаконічну технічну документацію для одного файлу коду — орієнтовану на поведінку, не реалізацію.
84
81
 
85
82
  ФАЙЛ-ДЖЕРЕЛО: <sourcePath>
86
83
  ЗАПИСАТИ В: <docPath>
@@ -94,18 +91,32 @@ npx @nitra/cursor docgen scan
94
91
  - Мова — УКРАЇНСЬКА для всього тексту (заголовки, абзаци, таблиці). Code identifiers,
95
92
  шляхи, імена API, команди — лишай як у коді (зазвичай ASCII).
96
93
  - ЧИСТИЙ Markdown. Жодних HTML-обгорток (<div>/<span>/класів) — токен-ефективність.
97
- - Контекстна незалежність: кожна секція самодостатня. Явно повторюй імена сутностей
98
- («функція `calculateTotal()`», «модуль `src/lib/foo.js`»). Заборонено «як вище»,
99
- «ця функція», «той сервіс».
100
- - ВИЧЕРПНІСТЬ опиши всю логіку файлу. Секції:
101
- ## Оглядпризначення файлу, його роль, що дає.
102
- ## Експорти / APIкожен експорт (функція/клас/компонент/константа).
103
- ## Функції для кожної: сигнатура, параметри (типи), що повертає, side effects.
104
- ## Залежності імпорти й зовнішні залежності та навіщо вони.
105
- ## Потік виконання / Використання як цей файл використовують; ключові гілки логіки.
106
- - Для .vue додай опис props, emits, slots, реактивного стану.
94
+ - ФОКУС НА ПОВЕДІНЦІ, не реалізації. Пиши ЩО і НАВІЩО, а не як саме це зроблено.
95
+ - НЕ перелічуй модулі стандартної бібліотеки (node:fs, node:path, node:crypto, python stdlib
96
+ тощо) вони не несуть бізнес-значення. Зовнішні залежності (npm-пакети, внутрішні модулі)
97
+ згадуй лише якщо їхня роль не очевидна з контексту.
98
+ - НЕ перелічуй внутрішні назви допоміжних функцій/змінних описуй їхню роль і поведінку.
99
+ Імена публічних exports згадуй лише коли export справжня точка інтеграції, яку кличуть
100
+ ззовні. Для дрібних/листкових модулів з однією відповідальністю опиши роль поведінково,
101
+ БЕЗ сигнатур, таблиць типів і переліку параметрів це деталі реалізації.
102
+ - Контекстна незалежність: кожна секція самодостатня. Уникай «як вище», «ця функція», «той сервіс».
103
+ - Секції (включай лише доречні порожніх не вигадуй):
104
+ ## Огляд — 1-3 речення: що файл робить і навіщо він існує (роль у системі). Згадай
105
+ ключову семантику, якщо вона визначає сенс файлу (opt-in/gate, кеш, ідемпотентність тощо).
106
+ ## Поведінка — покроковий алгоритм у бізнес-термінах (не деталі реалізації).
107
+ Нумерований список: що відбувається, умови, гілки логіки. Якщо файл керується
108
+ конфігом чи форматом даних — наведи короткий приклад (тільки реальний з коду).
109
+ ## Публічний API — ЛИШЕ якщо модуль має нетривіальну зовнішню поверхню, яку називають
110
+ споживачі. Для кожного export: назва + що робить. Без сигнатур і таблиць типів.
111
+ Не дублюй ## Поведінка. Для модуля з однією функцією-предикатом цю секцію пропусти.
112
+ ## Де використовується — де в системі цей файл вживається (якщо відомо з коду).
113
+ ## Гарантії поведінки — інваріанти й крайові випадки: що гарантовано (read-only,
114
+ не кидає винятків, fail-safe-значення за замовчуванням, безпечна обробка поганих даних)
115
+ і що стається при відсутніх ресурсах чи некоректному вводі. Пропусти, якщо таких гарантій немає.
116
+ - Для .vue додай ## Інтерфейс компонента — props (типи, defaults), emits, slots, реактивний стан.
107
117
  - НЕ вигадуй деталей, яких немає в коді.
108
- - Мета — Rebuild Test: за цим документом можна відтворити логіку файлу.
118
+ - Мета — Behavior Test: читач розуміє, що робить файл і як він вписується в систему,
119
+ без потреби читати реалізацію.
109
120
 
110
121
  Поверни лише підтвердження, що файл <docPath> записано.
111
122
  ```
@@ -122,9 +133,14 @@ npx @nitra/cursor docgen modules
122
133
 
123
134
  ```json
124
135
  [
125
- { "moduleRoot": "/abs/npm/rules/adr", "relRoot": "npm/rules/adr",
126
- "slug": "npm-rules-adr", "docPath": "/abs/npm/rules/adr/docs/ARCHITECTURE.md",
127
- "members": ["npm/rules/adr/index.mjs"], "exists": false }
136
+ {
137
+ "moduleRoot": "/abs/npm/rules/adr",
138
+ "relRoot": "npm/rules/adr",
139
+ "slug": "npm-rules-adr",
140
+ "docPath": "/abs/npm/rules/adr/docs/ARCHITECTURE.md",
141
+ "members": ["npm/rules/adr/index.mjs"],
142
+ "exists": false
143
+ }
128
144
  ]
129
145
  ```
130
146
 
@@ -138,7 +154,7 @@ module-summary **завжди регенерується** (це агрегат
138
154
  ФАЙЛИ МОДУЛЯ (members): <members>
139
155
 
140
156
  Кроки:
141
- 1. Прочитай файлові доки членів модуля. <member> — relSource відносно кореня проєкту
157
+ 1. Прочитай файлові доки членів модуля. <member> — sourcePath відносно кореня проєкту
142
158
  (= поточний CWD); його файлова дока — <CWD>/<dir>/docs/<stem>.md. За потреби зазирни
143
159
  в самі файли.
144
160
  2. Створи теку для <docPath>, якщо її немає.
@@ -201,3 +217,8 @@ Tier 3 (домени): <D> доменних доків у docs/.
201
217
  - Не комітити автоматично — користувач вирішує, коли комітити згенеровану доку.
202
218
  - Scanner ігнорує `node_modules`, `dist`, `.git`, `__pycache__`, `coverage`, `.cursor`,
203
219
  `.claude`, усі теки `docs/`, а також `*.test.*` / `*.spec.*` / `*.d.ts`.
220
+ Кореневий repo `docs/` — system-wide only: file-level docs туди не пишуться, і Tier 1
221
+ має трактувати цей корінь як повністю нецільовий.
222
+ - Список glob-ів для ignore живе в окремому snippet-модулі
223
+ `npm/skills/docgen/js/docgen-ignore.mjs` (`DOCGEN_IGNORE_GLOBS`).
224
+ Scanner лише читає цей список.
@@ -0,0 +1,19 @@
1
+ # firebase_hosting.mjs
2
+
3
+ ## Огляд
4
+
5
+ Перевірка-концерн правила abie: у підкаталогах першого рівня репозиторію не повинно бути артефактів Firebase Hosting (`.firebaserc`, `firebase.json`, `.firebase/`), бо `abie.mdc` забороняє Firebase Hosting. Сам корінь репозиторію не перевіряється — там ці імена можуть належати суміжним проєктам.
6
+
7
+ ## Поведінка
8
+
9
+ 1. Прочитати список елементів кореня репозиторію. Якщо каталог не читається — зафіксувати помилку (fail) і завершитися.
10
+ 2. Відібрати підкаталоги першого рівня, пропустивши `.git` і `node_modules`.
11
+ 3. У кожному такому підкаталозі перевірити наявність заборонених імен: файлів `.firebaserc`, `firebase.json` і каталогу `.firebase/`. Кожна знахідка — окремий fail.
12
+ 4. Якщо жодного порушення не знайдено — зафіксувати pass.
13
+ 5. Повернути підсумковий exit-код.
14
+
15
+ ## Гарантії поведінки
16
+
17
+ - Read-only: лише перелічує й перевіряє існування шляхів.
18
+ - Помилка читання кореня не валить процес винятком, а стає fail.
19
+ - Перевіряється лише перший рівень; корінь і глибші рівні поза охопленням.
@@ -0,0 +1,24 @@
1
+ # k8s-tree.mjs
2
+
3
+ ## Огляд
4
+
5
+ Обхід Kubernetes-дерева для перевірок abie з кешуванням на час одного прогону. Знаходить YAML під сегментом `k8s/` і визначає каталоги з `Deployment`. Перший виклик платить за обхід; наступні концерни прогону беруть із кешу.
6
+
7
+ ## Поведінка
8
+
9
+ 1. Пошук маніфестів: рекурсивно обійти дерево, відібравши `.yaml`/`.yml` під сегментом `k8s/`; `.github/` свідомо пропускається. Результат відсортований.
10
+ 2. Каталоги з Deployment: розпарсити передані YAML, відібрати `kind: Deployment`, зібрати унікальні каталоги.
11
+ 3. Кешування: обидві операції кешуються module-level singleton-ом за ключем із входів; повтор без I/O.
12
+ 4. Пошкоджені YAML за замовчуванням мовчки пропускаються; репортер передає викликач.
13
+
14
+ ## Публічний API
15
+
16
+ - `findK8sYamlFiles` — відсортований список YAML під `k8s/` (з кешем, пропуск `.github/`).
17
+ - `collectDeploymentDirs` — множина каталогів із Deployment (кеш, опц. репортер помилок).
18
+
19
+ ## Гарантії поведінки
20
+
21
+ - Read-only щодо проєкту.
22
+ - Стійкість до пошкоджених YAML: помилкові документи пропускаються, обхід не переривається.
23
+ - Детермінований вивід (стабільне сортування).
24
+ - Кеш у межах прогону; повторні виклики безкоштовні.
@@ -0,0 +1,24 @@
1
+ # overlay-paths.mjs
2
+
3
+ ## Огляд
4
+
5
+ Набір чистих path-хелперів для overlay-перевірок правила abie: класифікація шляхів (ua-overlay проти base-шару), виведення каталогу пакета з overlay-шляху, умовні питання правила (чи потрібен HTTPRoute, чи є Deployment). Уся логіка — над рядками/шляхами та перевіркою існування файлів; YAML не парситься.
6
+
7
+ ## Поведінка
8
+
9
+ - ua-overlay: шлях закінчується на `ua/kustomization.yaml`; base-шар — за сегментом `base/`.
10
+ - Каталог пакета: з `…/k8s/ua/kustomization.yaml` виділяється батько `k8s/`; без збігу — немає результату.
11
+ - HTTPRoute-gate: вимога лише для Vite-пакетів (є `vite.config.{js,mjs,ts}`).
12
+ - Deployment: чи хоч один каталог із Deployment лежить у `k8s/` цього пакета.
13
+ - base-шар: yaml під `<пакет>/k8s/` і не в `ua/`.
14
+ - Шляхи нормалізуються до posix (`\`→`/`).
15
+
16
+ ## Публічний API
17
+
18
+ - `isUaKustomizationPath`, `abiePackageDirFromK8sOverlay`, `abieOverlayRequiresHttpRouteByVite`, `abieOverlayK8sTreeHasDeployment`, `isAbieK8sBaseYamlPath`, `isK8sYamlInAbiePackageExcludingUaOverlay`.
19
+
20
+ ## Гарантії поведінки
21
+
22
+ - Read-only, без побічних ефектів.
23
+ - Невідповідність шаблону → негативний/порожній результат, не виняток.
24
+ - Незалежність від ОС (розділювачі зводяться до `/`).
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Глоби, які `docgen` завжди ігнорує.
3
+ *
4
+ * Це окремий snippet-модуль: список правиться тут, scanner лише читає його
5
+ * через predicate. Патерни пишуться в posix-формі відносно кореня проєкту.
6
+ */
7
+ import picomatch from 'picomatch'
8
+
9
+ /** Базовий список glob-ів для `docgen` ignore. */
10
+ export const DOCGEN_IGNORE_GLOBS = Object.freeze([
11
+ '**/node_modules/**',
12
+ '**/dist/**',
13
+ '.git/**',
14
+ '**/__pycache__/**',
15
+ '**/coverage/**',
16
+ '.cursor/**',
17
+ '.claude/**',
18
+ '.pi/**',
19
+ '.pi-template/**',
20
+ '.worktrees/**',
21
+ '**/benchmarks/**',
22
+ '**/demo/**',
23
+ '**/docs/**'
24
+ ])
25
+
26
+ const IGNORE_MATCHERS = DOCGEN_IGNORE_GLOBS.map(glob => picomatch(glob, { dot: true }))
27
+
28
+ /**
29
+ * Нормалізує відносний шлях до posix-формату для glob-matching.
30
+ * @param {string} relPath відносний шлях із path.relative(...)
31
+ * @returns {string} posix-вигляд шляху
32
+ */
33
+ function toPosixRelPath(relPath) {
34
+ return relPath.split('\\').join('/')
35
+ }
36
+
37
+ /**
38
+ * Перевіряє, чи шлях має бути пропущений `docgen`.
39
+ * Для `kind = 'dir'` це працює і на піддерево каталогу, тож glob на кшталт
40
+ * `**\\/demo/**` спрацьовує на `demo/x` під час рекурсивного обходу.
41
+ * @param {string} relPath відносний шлях від кореня проєкту
42
+ * @param {'path'|'dir'} [kind='path'] тип перевірки
43
+ * @returns {boolean} `true`, якщо шлях ігнорується
44
+ */
45
+ export function isDocgenIgnored(relPath, kind = 'path') {
46
+ if (typeof relPath !== 'string' || relPath.length === 0) {
47
+ return false
48
+ }
49
+ const posixRelPath = toPosixRelPath(relPath)
50
+ if (kind === 'dir') {
51
+ return IGNORE_MATCHERS.some(match => match(posixRelPath) || match(`${posixRelPath}/__docgen__`))
52
+ }
53
+ return IGNORE_MATCHERS.some(match => match(posixRelPath))
54
+ }