@nitra/cursor 5.3.4 → 5.4.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 (151) 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 +11 -0
  4. package/bin/n-cursor.js +43 -22
  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-ignore.mjs +2 -1
  22. package/{skills → rules}/doc-files/js/docgen-scan.mjs +9 -1
  23. package/{skills → rules}/doc-files/js/docs/docgen-crc.md +1 -1
  24. package/{skills → rules}/doc-files/js/docs/docgen-extract-anchors.md +1 -1
  25. package/{skills → rules}/doc-files/js/docs/docgen-extract.md +2 -2
  26. package/{skills → rules}/doc-files/js/docs/docgen-files-batch.md +1 -1
  27. package/{skills → rules}/doc-files/js/docs/docgen-gen.md +1 -1
  28. package/{skills → rules}/doc-files/js/docs/docgen-ignore.md +4 -4
  29. package/rules/doc-files/js/docs/docgen-prompts.md +39 -0
  30. package/rules/doc-files/js/docs/docgen-scan.md +54 -0
  31. package/rules/doc-files/js/docs/lint.md +36 -0
  32. package/rules/doc-files/js/docs/units-js.md +31 -0
  33. package/rules/doc-files/js/docs/units-rs.md +35 -0
  34. package/rules/doc-files/js/docs/units.md +30 -0
  35. package/rules/doc-files/js/lint.mjs +96 -0
  36. package/{skills → rules}/doc-files/js/units-rs.mjs +37 -17
  37. package/rules/doc-files/lint/docs/lint.md +37 -0
  38. package/rules/doc-files/lint/lint.mjs +105 -0
  39. package/rules/doc-files/meta.json +1 -0
  40. package/rules/docker/docs/fix.md +21 -161
  41. package/rules/efes/docs/fix.md +23 -194
  42. package/rules/feedback/docs/fix.md +10 -8
  43. package/rules/ga/docs/fix.md +10 -5
  44. package/rules/graphql/docs/fix.md +23 -119
  45. package/rules/hasura/docs/fix.md +19 -5
  46. package/rules/hasura/js/docs/internal_urls.md +34 -307
  47. package/rules/image-avif/docs/fix.md +16 -127
  48. package/rules/image-compress/docs/fix.md +20 -141
  49. package/rules/image-compress/js/docs/package_setup.md +22 -182
  50. package/rules/js-bun-db/docs/fix.md +23 -139
  51. package/rules/js-bun-db/js/docs/safety.md +33 -221
  52. package/rules/js-bun-redis/docs/fix.md +25 -114
  53. package/rules/js-bun-redis/js/docs/imports.md +18 -166
  54. package/rules/js-lint/docs/fix.md +30 -108
  55. package/rules/js-lint/js/docs/lint-findings.md +37 -17
  56. package/rules/js-lint/js/docs/lint.md +22 -238
  57. package/rules/js-lint/js/docs/tooling.md +34 -331
  58. package/rules/js-lint-ci/docs/fix.md +16 -149
  59. package/rules/js-lint-ci/js/docs/lint.md +16 -136
  60. package/rules/js-mssql/docs/fix.md +18 -123
  61. package/rules/js-mssql/js/docs/deps.md +28 -251
  62. package/rules/js-run/docs/fix.md +23 -138
  63. package/rules/js-run/js/docs/runtime.md +24 -378
  64. package/rules/k8s/docs/fix.md +18 -123
  65. package/rules/nginx-default-tpl/docs/fix.md +22 -118
  66. package/rules/nginx-default-tpl/js/docs/template.md +38 -360
  67. package/rules/npm-module/docs/fix.md +27 -89
  68. package/rules/npm-module/js/docs/header_doc_pointer.md +15 -15
  69. package/rules/npm-module/js/docs/package_structure.md +36 -258
  70. package/rules/npm-module/js/docs/rule_meta.md +25 -127
  71. package/rules/npm-module/js/docs/skill_meta.md +18 -180
  72. package/rules/php/docs/fix.md +21 -98
  73. package/rules/php/js/docs/tooling.md +20 -143
  74. package/rules/python/docs/fix.md +25 -157
  75. package/rules/python/js/docs/applies.md +20 -98
  76. package/rules/python/js/docs/tooling.md +27 -144
  77. package/rules/rego/docs/fix.md +24 -112
  78. package/rules/rego/js/docs/applies.md +20 -164
  79. package/rules/rego/js/docs/lint.md +15 -110
  80. package/rules/release/docs/fix.md +16 -114
  81. package/rules/rust/docs/fix.md +24 -119
  82. package/rules/rust/js/docs/applies.md +20 -129
  83. package/rules/security/docs/fix.md +21 -78
  84. package/rules/security/js/docs/sample_secret.md +23 -182
  85. package/rules/security/js/docs/trufflehog.md +19 -128
  86. package/rules/style-lint/docs/fix.md +16 -150
  87. package/rules/style-lint/js/docs/lint.md +21 -172
  88. package/rules/style-lint/js/docs/tooling.md +19 -184
  89. package/rules/tauri/docs/fix.md +26 -152
  90. package/rules/tauri/js/docs/cargo_mutants_config.md +21 -159
  91. package/rules/tauri/js/docs/tooling.md +20 -217
  92. package/rules/test/docs/fix.md +19 -127
  93. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +15 -127
  94. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +17 -153
  95. package/rules/test/js/docs/cargo_mutants_config.md +24 -164
  96. package/rules/test/js/docs/location.md +24 -126
  97. package/rules/test/js/docs/no-process-chdir.md +20 -151
  98. package/rules/test/js/docs/no-relative-fs-path.md +24 -261
  99. package/rules/test/js/docs/stryker_config.md +48 -148
  100. package/rules/test/js/docs/vitest-config-pool-forks.md +21 -164
  101. package/rules/text/docs/fix.md +25 -113
  102. package/rules/text/js/docs/forbidden-prettier.md +21 -132
  103. package/rules/text/js/docs/formatting.md +60 -251
  104. package/rules/text/js/docs/lint.md +17 -114
  105. package/rules/vue/docs/fix.md +25 -118
  106. package/rules/vue/js/docs/packages.md +25 -323
  107. package/rules/worktree/docs/fix.md +31 -150
  108. package/scripts/coverage-classify/docs/index.md +23 -209
  109. package/scripts/coverage-classify/docs/verdict-schema.md +14 -159
  110. package/scripts/dispatcher/docs/trace.md +35 -0
  111. package/scripts/docs/auto-rules.md +37 -361
  112. package/scripts/docs/lint-cli.md +12 -13
  113. package/scripts/docs/post-tool-use-fix.md +16 -15
  114. package/scripts/docs/skills-cli.md +26 -23
  115. package/scripts/docs/sync-claude-config.md +94 -34
  116. package/scripts/docs/worktree-cli.md +11 -34
  117. package/scripts/lib/docs/assert-project-root.md +14 -16
  118. package/scripts/lib/docs/changed-files.md +24 -139
  119. package/scripts/lib/docs/discover-check-rules-from-cursor.md +14 -146
  120. package/scripts/lib/docs/rule-predicates.md +20 -17
  121. package/scripts/lib/docs/run-rule-cli.md +14 -18
  122. package/scripts/lib/docs/run-rule.md +13 -20
  123. package/scripts/lib/docs/run-standard-rule.md +12 -15
  124. package/scripts/lib/docs/sync-gitignore-worktree.md +15 -18
  125. package/scripts/lib/rule-predicates.mjs +1 -1
  126. package/scripts/sync-claude-config.mjs +4 -1
  127. package/scripts/utils/docs/with-lock.md +19 -12
  128. package/scripts/utils/with-lock.mjs +4 -2
  129. package/skills/doc-aggregate/SKILL.md +2 -2
  130. package/skills/doc-aggregate/js/docgen-ignore.mjs +6 -6
  131. package/skills/doc-aggregate/js/docs/docgen-ignore.md +1 -1
  132. package/skills/doc-aggregate/js/docs/docgen-scan.md +78 -0
  133. package/skills/doc-files/.changes/260612-0031.md +5 -0
  134. package/skills/doc-files/.changes/260612-0036.md +5 -0
  135. package/skills/doc-files/.changes/260612-0114.md +5 -0
  136. package/skills/doc-files/SKILL.md +6 -6
  137. package/skills/fix/js/docs/llm-worker.md +17 -15
  138. package/skills/fix/js/docs/orchestrator.md +30 -23
  139. package/skills/fix/js/docs/t0.md +26 -16
  140. package/skills/start-check/js/docs/check.md +26 -22
  141. package/skills/taze/js/docs/diff.md +44 -20
  142. package/skills/doc-files/js/docs/docgen-prompts.md +0 -32
  143. package/skills/doc-files/js/docs/docgen-scan.md +0 -25
  144. package/skills/doc-files/js/docs/units-rs.md +0 -35
  145. /package/{skills → rules}/doc-files/js/docgen-crc.mjs +0 -0
  146. /package/{skills → rules}/doc-files/js/docgen-extract-anchors.mjs +0 -0
  147. /package/{skills → rules}/doc-files/js/docgen-files-batch.mjs +0 -0
  148. /package/{skills → rules}/doc-files/js/docgen-gen.mjs +0 -0
  149. /package/{skills → rules}/doc-files/js/docgen-prompts.mjs +0 -0
  150. /package/{skills → rules}/doc-files/js/units-js.mjs +0 -0
  151. /package/{skills → rules}/doc-files/js/units.mjs +0 -0
@@ -1,335 +1,37 @@
1
+ ---
2
+ docgen:
3
+ source: npm/rules/vue/js/packages.mjs
4
+ crc: 6119ae9c
5
+ score: 85
6
+ ---
7
+
1
8
  # packages.mjs
2
9
 
3
10
  ## Огляд
4
11
 
5
- Модуль `packages.mjs` реалізує перевірку відповідності Vue-пакетів monorepo правилам `vue.mdc`. Він обходить усі workspace-пакети, знаходить серед них ті, що мають `vue` у `dependencies`, і виконує над кожним набір контрольних кроків:
6
-
7
- - наявність та коректність `src/vite-env.d.ts` із triple-slash-посиланням на `vite/client`;
8
- - наявність `jsconfig.json` у корені пакета;
9
- - наявність та коректне конфігурування `vite.config.{js,ts,mjs}` (зокрема `VueMacros`, `AutoImport` і Bun-сумісність);
10
- - наявність `'vue'` у списку `imports` плагіна `unplugin-auto-import` (`AutoImport`);
11
- - відсутність заборонених явних value-імпортів з `'vue'` у джерелах пакета (скан через oxc-parser у сусідньому модулі);
12
- - відсутність імпортів Node-нативних модулів (`node:*` або bare-ім’я кшталту `fs`, `path`) у `.vue` SFC;
13
- - відсутність згадок `esbuild` у джерелах пакета (заохочується перехід на `rolldown`);
14
- - наявність рекомендації розширення `Vue.volar` у `.vscode/extensions.json` на рівні всього репозиторію.
15
-
16
- Перевірка залежностей `package.json` (`vite >= 8`, `@vitejs/plugin-vue`, `vue-macros`, `unplugin-auto-import`, `vite-plugin-vue-layouts-next`, заборона `esbuild`) виноситься у policy `vue.package_json` і викликається через CLI `npx @nitra/cursor fix`; цей модуль лише друкує підказку про це.
17
-
18
- Результати збираються через `createCheckReporter()` і повертаються у вигляді exit-коду: `0` — все OK, `1` — є проблеми. Основна публічна точка входу — функція `check(cwd)`.
19
-
20
- ## Експорти / API
21
-
22
- Модуль є ES-модулем (`*.mjs`) і має такі іменовані експорти:
23
-
24
- - `isVueComponentLibraryPkg(pkg)` — predicate, що визначає, чи є пакет бібліотекою компонентів Vue (за наявністю `vue` у `peerDependencies`).
25
- - `check(cwd?)` — головна функція перевірки усього репозиторію; повертає `Promise<number>` із exit-кодом.
26
-
27
- Решта функцій модуля — приватні (file-local) helpers, які використовуються лише всередині `packages.mjs`.
28
-
29
- ## Функції
30
-
31
- ### `isEsbuildScanFile(relPosix)`
32
-
33
- - **Сигнатура:** `function isEsbuildScanFile(relPosix: string): boolean`
34
- - **Параметри:**
35
- - `relPosix` — відносний шлях у POSIX-форматі (з прямими слешами), відраховуваний від кореня пакета.
36
- - **Повертає:** `boolean` — `true`, якщо файл варто перевіряти на текстові згадки `esbuild`, інакше `false`.
37
- - **Логіка:** виключає типові службові каталоги (`node_modules/`, `dist/`, `build/`, `coverage/`, `.git/`), типові lock-файли (`bun.lock`, `bun.lockb`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`), і допускає лише розширення з білого списку: `.js`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.vue`, `.json`, `.jsonc`, `.yaml`, `.yml`, `.md`, `.mdc`.
38
- - **Side effects:** немає.
39
-
40
- ### `appendEsbuildLineMatches(rel, content, matches, maxMatches)`
41
-
42
- - **Сигнатура:** `function appendEsbuildLineMatches(rel: string, content: string, matches: { rel: string; line: number; snippet: string }[], maxMatches: number): void`
43
- - **Параметри:**
44
- - `rel` — відносний шлях до файлу (для повідомлень).
45
- - `content` — повний вміст файлу як рядок.
46
- - `matches` — мутабельний буфер, у який дописуються знайдені збіги.
47
- - `maxMatches` — верхня межа кількості елементів у `matches`; при досягненні функція припиняє обробку.
48
- - **Повертає:** `void`. Результат повертається через мутацію аргументу `matches`.
49
- - **Логіка:** розбиває `content` на рядки, перевіряє регулярний вираз `\besbuild\b` і додає об’єкт `{ rel, line: i + 1, snippet: line.trim() }` для кожного збігу.
50
- - **Side effects:** мутує переданий масив `matches`.
51
-
52
- ### `collectEsbuildMatchesInFiles(absPackageRoot, files, maxMatches)`
53
-
54
- - **Сигнатура:** `async function collectEsbuildMatchesInFiles(absPackageRoot: string, files: { rel: string }[], maxMatches: number): Promise<{ rel: string; line: number; snippet: string }[]>`
55
- - **Параметри:**
56
- - `absPackageRoot` — абсолютний шлях до кореня пакета.
57
- - `files` — перелік відносних шляхів-кандидатів, відфільтрованих через `isEsbuildScanFile`.
58
- - `maxMatches` — максимальна кількість збігів, які треба зібрати.
59
- - **Повертає:** `Promise` зі списком знайдених збігів `{ rel, line, snippet }`.
60
- - **Логіка:** для кожного файлу читає вміст через `fs/promises.readFile(..., 'utf8')`; якщо у файлі немає згадки `esbuild`, переходить далі. Інакше викликає `appendEsbuildLineMatches`. Зупиняється, щойно `matches.length` досягає `maxMatches`.
61
- - **Side effects:** виконує читання файлів з диска.
62
-
63
- ### `checkEsbuildMentions(rootDir, absPackageRoot, ignorePaths, prefix, passFn, fail)`
64
-
65
- - **Сигнатура:** `async function checkEsbuildMentions(rootDir: string, absPackageRoot: string, ignorePaths: string[], prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void): Promise<void>`
66
- - **Параметри:**
67
- - `rootDir` — відносний шлях до пакета (для повідомлень).
68
- - `absPackageRoot` — абсолютний шлях до кореня пакета (для обходу).
69
- - `ignorePaths` — масив абсолютних шляхів каталогів, які повністю виключені з обходу.
70
- - `prefix` — текстовий префікс для повідомлень (`[<label>] `).
71
- - `passFn` — callback на успішне повідомлення (як у check-reporter).
72
- - `fail` — callback на помилку перевірки.
73
- - **Повертає:** `Promise<void>`.
74
- - **Логіка:** обходить дерево через `walkDir(absPackageRoot, visitor, ignorePaths)`; кожен файл прогоняється через `isEsbuildScanFile`; кандидати збираються у локальний масив. Далі викликає `collectEsbuildMatchesInFiles` з лімітом `maxMatches = 30`. Якщо збігів немає — друкує pass із підказкою «очікується `rolldown`». Якщо є — реєструє `fail` на кожен збіг з підказкою замінити на `rolldown` та (якщо ліміт вичерпано) додає ще один `fail`, що показано лише перші 30.
75
- - **Side effects:** виконує читання файлів з диска; викликає `passFn`/`fail`.
76
-
77
- ### `packageLabel(rootDir)`
78
-
79
- - **Сигнатура:** `function packageLabel(rootDir: string): string`
80
- - **Параметри:**
81
- - `rootDir` — відносний шлях до пакета (`'.'` для кореня monorepo або, наприклад, `site`).
82
- - **Повертає:** `string` — підпис для логів: `'корінь'` якщо `rootDir === '.'`, інакше сам `rootDir`.
83
- - **Side effects:** немає.
84
-
85
- ### `ukFilesCountPhrase(n)`
86
-
87
- - **Сигнатура:** `function ukFilesCountPhrase(n: number): string`
88
- - **Параметри:**
89
- - `n` — невід’ємна кількість файлів.
90
- - **Повертає:** фразу українською мовою з відмінком «файл» / «файли» / «файлів» відповідно до правил pluralization:
91
- - залишок від 100 у діапазоні 11..14 → `«N файлів»`;
92
- - залишок від 10 рівний 1 → `«N файл»`;
93
- - залишок від 10 у 2..4 → `«N файли»`;
94
- - решта → `«N файлів»`.
95
- - **Side effects:** немає.
96
-
97
- ### `checkViteClientEnvAndEditorConfig(rootDir, prefix, passFn, fail, cwd)`
98
-
99
- - **Сигнатура:** `async function checkViteClientEnvAndEditorConfig(rootDir: string, prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void, cwd: string): Promise<void>`
100
- - **Параметри:**
101
- - `rootDir` — відносний шлях до кореня пакета.
102
- - `prefix` — префікс повідомлень.
103
- - `passFn` / `fail` — callbacks check-reporter-а.
104
- - `cwd` — корінь репозиторію.
105
- - **Повертає:** `Promise<void>`.
106
- - **Логіка:**
107
- 1. Перевіряє, що `<cwd>/<rootDir>/src/vite-env.d.ts` існує; якщо ні — `fail` з підказкою про `/// <reference types="vite/client" />`.
108
- 2. Якщо файл існує, читає його вміст і перевіряє регулярним виразом `VITE_CLIENT_REFERENCE_RE = /\/\/\/\s*<reference\s+types\s*=\s*["']vite\/client["']\s*\/>/`. За відсутності збігу — `fail`.
109
- 3. Якщо обидва кроки успішні — `passFn` про коректний `vite-env.d.ts`.
110
- 4. Перевіряє наявність `<cwd>/<rootDir>/jsconfig.json`; за відсутності — `fail`, інакше `passFn`.
111
- - **Side effects:** читання файлів з диска; виклики `passFn`/`fail`.
112
-
113
- ### `isVueComponentLibraryPkg(pkg)` _(експорт)_
114
-
115
- - **Сигнатура:** `function isVueComponentLibraryPkg(pkg: { peerDependencies?: Record<string, string> }): boolean`
116
- - **Параметри:**
117
- - `pkg` — розпарсений `package.json` пакета.
118
- - **Повертає:** `boolean` — `true`, якщо `vue` присутній у `peerDependencies`.
119
- - **Семантика:** такі пакети — бібліотеки компонентів Vue. Їхні джерела не проходять через `unplugin-auto-import` споживача (auto-import резолвиться лише в коді додатка, не в `node_modules`), тому правило «без явних value-імпортів з `'vue'`» до них не застосовується.
120
- - **Side effects:** немає.
121
-
122
- ### `extractAutoImportCallArgs(content)`
123
-
124
- - **Сигнатура:** `function extractAutoImportCallArgs(content: string): string | null`
125
- - **Параметри:**
126
- - `content` — повний текст `vite.config.*`.
127
- - **Повертає:** текст усередині найближчого виклику `AutoImport(...)` без зовнішніх дужок, або `null`, якщо виклик не знайдено або дужки не збалансовані.
128
- - **Логіка:** шукає marker `AutoImport(`, після нього просувається посимвольно з лічильником глибини дужок (`(` → `+1`, `)` → `-1`) і повертає підрядок від першого символу після `(` до символу, на якому `depth` стає `0`.
129
- - **Side effects:** немає.
130
-
131
- ### `viteConfigHasVueInAutoImports(content)`
132
-
133
- - **Сигнатура:** `function viteConfigHasVueInAutoImports(content: string): boolean`
134
- - **Параметри:**
135
- - `content` — повний текст `vite.config.*`.
136
- - **Повертає:** `boolean` — `true`, якщо у `AutoImport(...)` як рядковий елемент `imports` фігурує `'vue'` або `"vue"`.
137
- - **Логіка:** делегує до `extractAutoImportCallArgs`; якщо `null` — `false`; інакше повертає `args.includes("'vue'") || args.includes('"vue"')`. Зауважте: перевірка є текстовим contains, без парсингу JS — точкою обʼєктивізації пошуку є вже відокремлений виклик `AutoImport(...)`.
138
- - **Side effects:** немає.
139
-
140
- ### `checkViteConfig(rootDir, isComponentLibrary, prefix, passFn, fail, cwd)`
141
-
142
- - **Сигнатура:** `async function checkViteConfig(rootDir: string, isComponentLibrary: boolean, prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void, cwd: string): Promise<{ hasVueAutoImport: boolean }>`
143
- - **Параметри:**
144
- - `rootDir` — відносний шлях до пакета.
145
- - `isComponentLibrary` — чи це бібліотека компонентів Vue.
146
- - `prefix`, `passFn`, `fail` — як вище.
147
- - `cwd` — корінь репозиторію.
148
- - **Повертає:** `Promise<{ hasVueAutoImport: boolean }>` — ознака, чи AutoImport сконфігуровано на `'vue'`. Її використовує `checkVueImportViolations`.
149
- - **Логіка:**
150
- 1. Шукає перший наявний з `vite.config.js`, `vite.config.ts`, `vite.config.mjs`; якщо жоден не існує — `fail` і повертає `{ hasVueAutoImport: false }`.
151
- 2. Читає вміст vite.config.
152
- 3. Якщо вміст містить `esbuild` (за регуляркою `ESBUILD_RE`) — `fail` з підказкою замінити на `rolldown`.
153
- 4. Викликає `viteConfigHasVueInAutoImports(content)` → `hasVueAutoImport`.
154
- 5. Якщо це бібліотека компонентів — `passFn`, що `VueMacros`/`AutoImport` не вимагаються.
155
- 6. Інакше — пробігає список перевірок `[ VueMacros, AutoImport ]` і реєструє `passFn`/`fail` за наявністю токенів у вмісті. Якщо у файлі є виклик `AutoImport(`, додатково перевіряє `hasVueAutoImport`: `pass` — якщо `'vue'` у `imports`, інакше `fail` з підказкою додати `'vue'` (бо інакше прибирати явні value-імпорти `from 'vue'` небезпечно).
156
- 7. Незалежно від типу пакета, якщо у vite.config фігурує `process.env.npm_lifecycle_event` — `fail` з підказкою перейти на `mode` з `defineConfig(({ mode }) => ...)` (Bun не підставляє `npm_lifecycle_event` так, як npm).
157
- - **Side effects:** читання `vite.config.*` з диска; виклики `passFn`/`fail`.
158
-
159
- ### `checkVueNodeImportViolations(rootDir, absPackageRoot, ignorePaths, prefix, passFn, fail)`
160
-
161
- - **Сигнатура:** `async function checkVueNodeImportViolations(rootDir: string, absPackageRoot: string, ignorePaths: string[], prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void): Promise<void>`
162
- - **Параметри:** як в інших чек-функцій; `absPackageRoot` — абсолютний шлях до пакета.
163
- - **Повертає:** `Promise<void>`.
164
- - **Логіка:**
165
- 1. Обходить `absPackageRoot` через `walkDir(absPackageRoot, visitor, ignorePaths)`, збирає абсолютні шляхи всіх `.vue`-файлів, що не пропускаються через `shouldSkipFileForVueImportScan(rel)`.
166
- 2. Для кожного `.vue` читає вміст і викликає `findForbiddenNodeImportsInVueFile(content, rel)`; на кожне порушення — `fail` з підказкою винести логіку у server-side утіліту (SFC виконується у браузері, Node API недоступне).
167
- 3. Якщо порушень немає — `passFn` з фразою «`немає імпортів Node-нативних модулів у .vue (проскановано N файлів)`», де `N` форматується через `ukFilesCountPhrase`.
168
- - **Side effects:** обхід та читання файлів з диска; виклики `passFn`/`fail`.
169
-
170
- ### `checkVueImportViolations(rootDir, absPackageRoot, ignorePaths, isComponentLibrary, hasVueAutoImport, prefix, passFn, fail)`
171
-
172
- - **Сигнатура:** `async function checkVueImportViolations(rootDir: string, absPackageRoot: string, ignorePaths: string[], isComponentLibrary: boolean, hasVueAutoImport: boolean, prefix: string, passFn: (msg: string) => void, fail: (msg: string) => void): Promise<void>`
173
- - **Параметри:**
174
- - `rootDir` — відносний шлях до пакета.
175
- - `absPackageRoot` — абсолютний шлях до кореня пакета.
176
- - `ignorePaths` — каталоги, повністю виключені з обходу.
177
- - `isComponentLibrary` — чи це бібліотека компонентів (`vue` у `peerDependencies`).
178
- - `hasVueAutoImport` — чи AutoImport сконфігуровано на `'vue'` (з `checkViteConfig`).
179
- - `prefix`, `passFn`, `fail` — як вище.
180
- - **Повертає:** `Promise<void>`.
181
- - **Логіка:**
182
- 1. Якщо `isComponentLibrary === true` — `passFn` із поясненням, що для бібліотек компонентів явні value-імпорти з `'vue'` дозволені, і повертається. Джерела бібліотеки не проходять через `unplugin-auto-import` споживача.
183
- 2. Інакше якщо `hasVueAutoImport === false` — `passFn` з підказкою спершу додати `'vue'` до `AutoImport.imports` (fail про це вже зареєстровано в `checkViteConfig`), і повертається.
184
- 3. Інакше обходить пакет через `walkDir`, збирає абсолютні шляхи всіх source-файлів, для яких `isVueImportScanSourceFile(rel) === true` і `shouldSkipFileForVueImportScan(rel) === false`.
185
- 4. Для кожного такого файлу читає вміст і викликає `findForbiddenVueImportsInSourceFile(content, rel)`; на кожне порушення — `fail` з підказкою прибрати явний value-імпорт з `'vue'`.
186
- 5. Якщо порушень не знайдено — `passFn` з фразою «немає заборонених value-імпортів з `'vue'` у джерелах (проскановано N файлів)».
187
- - **Side effects:** обхід та читання файлів з диска; виклики `passFn`/`fail`.
188
-
189
- ### `checkVuePackage(rootDir, isComponentLibrary, ignorePaths, fail, passFn, cwd)`
190
-
191
- - **Сигнатура:** `async function checkVuePackage(rootDir: string, isComponentLibrary: boolean, ignorePaths: string[], fail: (msg: string) => void, passFn: (msg: string) => void, cwd: string): Promise<void>`
192
- - **Параметри:**
193
- - `rootDir` — відносний шлях до пакета.
194
- - `isComponentLibrary` — чи це бібліотека компонентів Vue.
195
- - `ignorePaths` — каталоги, повністю виключені з обходу.
196
- - `fail`, `passFn` — callbacks check-reporter-а (порядок `fail` перед `passFn` тут зворотний до інших функцій).
197
- - `cwd` — корінь репозиторію.
198
- - **Повертає:** `Promise<void>` — завершується після усього набору перевірок пакета.
199
- - **Логіка послідовно виконує:**
200
- 1. Формує `prefix = "[" + packageLabel(rootDir) + "] "`.
201
- 2. Реєструє інформаційний `passFn`: «`package.json` залежності перевіряє `npx @nitra/cursor fix → vue.package_json`».
202
- 3. `checkViteClientEnvAndEditorConfig(rootDir, prefix, passFn, fail, cwd)`.
203
- 4. `checkViteConfig(rootDir, isComponentLibrary, prefix, passFn, fail, cwd)` → отримує `hasVueAutoImport`.
204
- 5. `checkVueImportViolations(rootDir, join(cwd, rootDir), ignorePaths, isComponentLibrary, hasVueAutoImport, prefix, passFn, fail)`.
205
- 6. `checkVueNodeImportViolations(rootDir, join(cwd, rootDir), ignorePaths, prefix, passFn, fail)`.
206
- 7. `checkEsbuildMentions(rootDir, join(cwd, rootDir), ignorePaths, prefix, passFn, fail)`.
207
- - **Side effects:** виконує всі вкладені перевірки (читання файлів, виклики `passFn`/`fail`).
208
-
209
- ### `collectVueRoots(roots, cwd)`
210
-
211
- - **Сигнатура:** `async function collectVueRoots(roots: string[], cwd: string): Promise<Array<{ rootDir: string, isComponentLibrary: boolean }>>`
212
- - **Параметри:**
213
- - `roots` — усі корені пакетів monorepo (відносні шляхи), отримані від `getMonorepoPackageRootDirs`.
214
- - `cwd` — корінь репозиторію.
215
- - **Повертає:** масив описів пакетів, у яких `vue` зазначений у `dependencies`, з ознакою `isComponentLibrary`.
216
- - **Логіка:**
217
- 1. Для кожного `r` будує абсолютний шлях `<cwd>/<r>/package.json` і пропускає його, якщо файл відсутній.
218
- 2. Парсить JSON-вміст `package.json`.
219
- 3. Якщо `pkg.dependencies?.vue` істина — додає до результату об’єкт `{ rootDir: r, isComponentLibrary: isVueComponentLibraryPkg(pkg) }`.
220
- 4. Пакети, у яких `vue` лише в `peerDependencies` (без `dependencies`), не додаються — це самостійні бібліотеки компонентів, до них app-перевірки не застосовуються.
221
- - **Side effects:** читання `package.json` файлів з диска.
222
-
223
- ### `checkVueVolarRecommendation(pass, fail, cwd)`
224
-
225
- - **Сигнатура:** `async function checkVueVolarRecommendation(pass: (msg: string) => void, fail: (msg: string) => void, cwd: string): Promise<void>`
226
- - **Параметри:**
227
- - `pass`, `fail` — callbacks check-reporter-а.
228
- - `cwd` — корінь репозиторію.
229
- - **Повертає:** `Promise<void>`.
230
- - **Логіка:**
231
- 1. Перевіряє наявність `<cwd>/.vscode/extensions.json`; за відсутності — `fail` з поясненням, що для Vue-проєкту потрібна рекомендація `Vue.volar`.
232
- 2. Парсить JSON; якщо `recommendations` містить `'Vue.volar'` — `pass`, інакше — `fail` з підказкою додати рекомендацію.
233
- - **Side effects:** читання `extensions.json` з диска; виклики `pass`/`fail`.
234
-
235
- ### `check(cwd)` _(експорт, точка входу)_
236
-
237
- - **Сигнатура:** `async function check(cwd: string = process.cwd()): Promise<number>`
238
- - **Параметри:**
239
- - `cwd` — корінь репозиторію; за замовчуванням `process.cwd()`.
240
- - **Повертає:** `Promise<number>` — `0`, якщо всі перевірки успішні; `1`, якщо є зареєстровані `fail`.
241
- - **Логіка:**
242
- 1. Створює `reporter = createCheckReporter()` і деструктурує `{ pass, fail }`.
243
- 2. Отримує корені всіх workspace-пакетів через `getMonorepoPackageRootDirs(cwd)`.
244
- 3. Викликає `collectVueRoots(roots, cwd)` → список Vue-пакетів.
245
- 4. Якщо список порожній — друкує два `pass`-повідомлення (про пропуск Volar і про відсутність `vue` у будь-яких `dependencies`) і одразу повертає `reporter.getExitCode()`.
246
- 5. Інакше викликає `checkVueVolarRecommendation(pass, fail, cwd)`.
247
- 6. Завантажує абсолютні `ignorePaths` через `loadCursorIgnorePaths(cwd)` (читає `.cursorignore`-подібну конфігурацію).
248
- 7. Для кожного `{ rootDir, isComponentLibrary }` із `vueRoots` викликає `checkVuePackage(rootDir, isComponentLibrary, ignorePaths, fail, pass, cwd)`.
249
- 8. Повертає `reporter.getExitCode()`.
250
- - **Side effects:** усе разом — повний обхід monorepo, читання багатьох файлів з диска, накопичення pass/fail у check-reporter-і.
251
-
252
- ## Залежності
253
-
254
- ### Імпорти зі стандартної бібліотеки Node.js
255
-
256
- - `node:fs` — `existsSync` (синхронна перевірка існування файлу).
257
- - `node:fs/promises` — `readFile` (асинхронне читання файлу як `utf8`).
258
- - `node:path` — `join`, `relative` (компонування і обчислення відносних шляхів).
259
-
260
- ### Внутрішні модулі проєкту
261
-
262
- - `../../../scripts/lib/check-reporter.mjs` — `createCheckReporter()`: створює reporter з API `{ pass, fail, getExitCode }`, який накопичує повідомлення перевірки та підраховує підсумковий exit-код.
263
- - `../lib/vue-forbidden-imports.mjs` — функції сканування заборонених імпортів:
264
- - `findForbiddenNodeImportsInVueFile(content, rel)` — повертає список порушень із Node-імпортами у `.vue` SFC.
265
- - `findForbiddenVueImportsInSourceFile(content, rel)` — повертає список явних value-імпортів з `'vue'` у звичайних source-файлах.
266
- - `isVueImportScanSourceFile(rel)` — чи варто файл взагалі сканувати на value-імпорти з `'vue'` (за розширенням/типом).
267
- - `shouldSkipFileForVueImportScan(rel)` — predicate-фільтр (`node_modules/`, `dist/`, lock-файли тощо).
268
- - `../../../scripts/lib/load-cursor-config.mjs` — `loadCursorIgnorePaths(cwd)`: повертає масив абсолютних шляхів каталогів, які повністю виключаються з обходу.
269
- - `../../../scripts/utils/walkDir.mjs` — `walkDir(absRoot, visitor, ignorePaths)`: рекурсивний обхід дерева; visitor викликається з абсолютним шляхом кожного файлу.
270
- - `../../../scripts/lib/workspaces.mjs` — `getMonorepoPackageRootDirs(cwd)`: повертає відносні шляхи всіх workspace-пакетів monorepo (включно з коренем `'.'`, якщо він є пакетом).
271
-
272
- ### Зовнішні (вказані у документації, не у коді цього модуля)
273
-
274
- - `oxc-parser` — використовується транзитивно через `../lib/vue-forbidden-imports.mjs` для парсингу і аналізу `module.staticImports`. У самому `packages.mjs` прямого виклику парсера немає.
275
- - Vue/Vite-стек, для якого писано перевірки: `vite >= 8`, `@vitejs/plugin-vue`, `vue-macros`, `unplugin-auto-import`, `vite-plugin-vue-layouts-next`. Заборонено: `esbuild` (треба використовувати `rolldown`).
276
-
277
- ### Константи
278
-
279
- - `ESBUILD_RE = /\besbuild\b/` — регулярний вираз для пошуку слова `esbuild` як цілого ідентифікатора.
280
- - `VITE_CLIENT_REFERENCE_RE = /\/\/\/\s*<reference\s+types\s*=\s*["']vite\/client["']\s*\/>/` — triple-slash-директива для `vite/client` у `src/vite-env.d.ts`.
281
-
282
- ## Потік виконання / Використання
283
-
284
- ### Виклик
285
-
286
- Модуль експортує `check(cwd)`. Зовнішній runner (CLI `@nitra/cursor` або pipeline-перевірка) викликає його приблизно так:
287
-
288
- ```js
289
- import { check } from './npm/rules/vue/js/packages.mjs'
290
-
291
- const code = await check(process.cwd())
292
- process.exit(code)
293
- ```
294
-
295
- ### Послідовність кроків `check()`
12
+ isVueComponentLibraryPkg
13
+ Перевіряє, чи є пакет бібліотекою компонентів Vue шляхом перевірки `peerDependencies`.
296
14
 
297
- 1. Інстанціювання check-reporter-а.
298
- 2. Отримання списку workspace-пакетів через `getMonorepoPackageRootDirs(cwd)`.
299
- 3. Фільтрація пакетів через `collectVueRoots(roots, cwd)`: залишаються лише ті, що мають `vue` у `dependencies`. Кожному обчислюється `isComponentLibrary` через `isVueComponentLibraryPkg(pkg)` (за наявністю `vue` у `peerDependencies`).
300
- 4. Якщо Vue-пакетів немає — друкує два інформаційних `pass`-повідомлення і повертає exit-код reporter-а (`0`).
301
- 5. Перевіряє `.vscode/extensions.json` на рекомендацію `Vue.volar` (`checkVueVolarRecommendation`).
302
- 6. Завантажує `ignorePaths` через `loadCursorIgnorePaths(cwd)`.
303
- 7. Для кожного Vue-пакета послідовно виконує `checkVuePackage`:
304
- 1. Інформаційний `pass` про `package.json`-перевірку через `npx @nitra/cursor fix → vue.package_json`.
305
- 2. `checkViteClientEnvAndEditorConfig` — `src/vite-env.d.ts` + `jsconfig.json`.
306
- 3. `checkViteConfig` — наявність `vite.config.*`, `VueMacros`, `AutoImport`, `'vue'` у `AutoImport.imports`, заборона `process.env.npm_lifecycle_event`, заборона `esbuild` у vite.config.
307
- 4. `checkVueImportViolations` — скан source-файлів пакета на заборонені value-імпорти з `'vue'` (тільки якщо це не бібліотека компонентів і AutoImport налаштовано на `'vue'`).
308
- 5. `checkVueNodeImportViolations` — скан `.vue` SFC на заборонені імпорти Node-нативних модулів.
309
- 6. `checkEsbuildMentions` — текстовий скан усіх scan-сумісних файлів пакета на згадки `esbuild` із підказкою переходу на `rolldown`.
310
- 8. Повертає `reporter.getExitCode()` — `0` за умови відсутності `fail`, `1` за наявності.
15
+ check
16
+ Перевіряє залежності та конфігурацію vite.config одного Vue-пакета.
311
17
 
312
- ### Особливості/інваріанти
18
+ ## Поведінка
313
19
 
314
- - Перевірки vite.config — текстові (`String.prototype.includes` / regex). Винятком є `extractAutoImportCallArgs`, яка точково виокремлює аргументи виклику `AutoImport(...)` за збалансованими дужками, щоб уникнути false-positive поза цим викликом.
315
- - Перевірка `'vue'` у `AutoImport.imports` свідомо не використовує AST-парсер: вона працює лише після того, як аргументи `AutoImport(...)` уже виокремлено, і обмежується пошуком підрядків `'vue'` або `"vue"`.
316
- - Скан value-імпортів `'vue'` пропускається, якщо `'vue'` ще не доданий до `AutoImport.imports` — інакше пропозиція прибрати імпорти зламала б код (нікому буде надати `ref`/`createApp` тощо). Користувача спершу штовхають полагодити `vite.config`, і лише наступний прогін викриє самі заборонені імпорти.
317
- - Бібліотеки компонентів (`vue` у `peerDependencies`) звільнюються від вимог `VueMacros`/`AutoImport` і правила «без явних value-імпортів з `'vue'`».
318
- - Сканування `.vue` SFC на Node-імпорти виконується завжди, незалежно від типу пакета.
319
- - Скан `esbuild`-згадок має жорсткий ліміт у 30 збігів; при перевищенні додатково реєструється fail-маркер «показано перші 30 збігів».
320
- - Обхід `walkDir` керується `ignorePaths` із `.cursor`-конфігу; усередині кожної чек-функції додатково діють фільтри `isEsbuildScanFile` / `isVueImportScanSourceFile` / `shouldSkipFileForVueImportScan`.
20
+ isVueComponentLibraryPkg
21
+ Визначає, чи є пакет бібліотекою компонентів Vue через peerDependencies
321
22
 
322
- ### Корисність окремих експортів
23
+ check
24
+ Перевіряє залежності та vite.config одного Vue-пакета
323
25
 
324
- - `isVueComponentLibraryPkg(pkg)` — корисна як reusable predicate в інших правилах/інструментах, які мають відрізняти бібліотеки компонентів Vue від додатків.
325
- - `check(cwd)` — основна публічна точка для запуску перевірки. Повертає exit-код, який інтегрується у будь-який runner перевірок.
26
+ ## Публічний API
326
27
 
327
- ## Rebuild Test
28
+ isVueComponentLibraryPkg забезпечує, що `+` використовується для підхоплення `vite-env.d.ts` та `.vue`.
29
+ passFn — перевіряє наявність `prefixjsconfig.json`.
30
+ check — перевіряє, чи є `vue` у `peerDependencies` пакету бібліотеки. Якщо `vue` є залежністю, то правило авто-імпорту (заборона value-імпортів з `'vue'`) не застосовується до цієї бібліотеки, оскільки імпорти з `'vue'` повинні бути явними.
328
31
 
329
- За цим документом має бути можливо однозначно відтворити поведінку `packages.mjs`:
32
+ ## Гарантії поведінки
330
33
 
331
- - Імпорти, експорти (`isVueComponentLibraryPkg`, `check`) і їхні сигнатури документовані.
332
- - Усі file-local helper-функції перераховані з параметрами, поверненнями, логікою та side effects.
333
- - Описано константи (`ESBUILD_RE`, `VITE_CLIENT_REFERENCE_RE`), залежні модулі і їхні ролі.
334
- - Описано послідовність викликів у `check(cwd)` і `checkVuePackage(...)`.
335
- - Зафіксовано інваріанти: ліміт у 30 esbuild-збігів, виключення для бібліотек компонентів, пропуск value-імпорт-скану до додавання `'vue'` у `AutoImport.imports`, скан Node-імпортів у `.vue` незалежно від типу пакета, заборона `process.env.npm_lifecycle_event` у vite.config (Bun-сумісність), вимога `mode` з `defineConfig(({ mode }) => ...)`.
34
+ - Read-only: файл не виконує операцій запису у файлову систему.
35
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
36
+ - Свідомо пропускає шляхи: `.git`, `node_modules`.
37
+ - Не звертається до мережі.
@@ -1,161 +1,42 @@
1
- # fix.mjs — точка входу правила `worktree` (fix)
1
+ ---
2
+ docgen:
3
+ source: npm/rules/worktree/fix.mjs
4
+ crc: 38cf876b
5
+ score: 100
6
+ ---
2
7
 
3
- ## Огляд
4
-
5
- Файл `npm/rules/worktree/fix.mjs` — це **точка входу правила `worktree`** для пакета `@nitra/cursor`. Він реалізує **дві ролі одночасно** (dual role):
6
-
7
- 1. **Library mode** — експортує функцію `run(ctx)`, яку CLI-оркестратор `@nitra/cursor` (або інший runner) викликає через `import { run } from '.../fix.mjs'` для запуску правила в межах батч-прогону всіх правил.
8
- 2. **Standalone mode** — якщо файл запускається напряму через `bun npm/rules/worktree/fix.mjs`, він самостійно ініціалізує CLI-обгортку (config-loading, whitelist, summary) і завершує процес `exit-code`-ом, придатним для CI/IDE.
9
-
10
- Сам файл **не містить власної логіки перевірки** — він лише делегує виконання стандартному раннеру `runStandardRule`, який послідовно виконує підкроки правила в наступному порядку:
11
-
12
- - **applies** — детектор, чи застосовне правило до конкретного файлу/директорії;
13
- - **JS-concerns** — JS-перевірки (зокрема `check-*.mjs` у директорії правила);
14
- - **policy** — політики/декларативні перевірки;
15
- - **mdc-refs** — перевірка посилань у відповідному `.mdc`-документі правила.
16
-
17
- Каталог `worktree/` стосується доменного правила про **git worktree** (див. `.cursor/rules/n-worktree.mdc`) — конвенцій ізольованих робочих дерев у `.worktrees/<branch>/` та інвентарних описів. Сам `fix.mjs` не реалізує цих перевірок безпосередньо — він лише диспетчеризує їх через стандартний пайплайн.
18
-
19
- ## Експорти / API
20
-
21
- | Експорт | Тип | Призначення |
22
- | ------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
- | `run` | `function (ctx?: RuleContext): Promise<number>` | Library-entry. Запускає стандартний пайплайн правила для директорії, в якій знаходиться `fix.mjs`. Повертає **exit-code**: `0` — порушень немає, `1` — є порушення. |
24
-
25
- Файл також містить **side-effect блок** (виконується тільки при прямому запуску як CLI), який не є експортом, але є частиною контракту:
26
-
27
- - При `isRunAsCli(import.meta.url) === true` модуль викликає `runRuleCli(import.meta.dirname)` і завершує процес `process.exit(<code>)`.
28
-
29
- ## Функції
30
-
31
- ### `run(ctx)`
32
-
33
- **Сигнатура:**
34
-
35
- ```js
36
- export function run(ctx)
37
- ```
38
-
39
- **Параметри:**
40
-
41
- | Параметр | Тип | Обов'язковий | Опис |
42
- | -------- | ----------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
- | `ctx` | `RuleContext` (з `../../scripts/lib/run-standard-rule.mjs`) | Ні | Контекст прогону правила. Передається оркестратором із `@nitra/cursor` і містить, зокрема, спільні структури на кшталт `walkCache` (кеш обходу файлової системи між кількома правилами в одному прогоні). Якщо не передано — раннер створить дефолтний контекст. |
44
-
45
- **Повертає:** `Promise<number>` — exit-code:
46
-
47
- - `0` — правило виконалося без порушень;
48
- - `1` — знайдено порушення (інтерпретація залежить від `runStandardRule`).
49
-
50
- **Side effects:**
51
-
52
- - Сама `run` не пише в `stdout` напряму та не змінює FS — усі побічні ефекти інкапсульовані в `runStandardRule` (форматований вивід summary, потенційне читання `.mdc`/`check-*.mjs`-файлів сусідньої директорії, обхід проєктних файлів через `walkCache`).
53
- - `run` **не** викликає `process.exit` — це відповідальність standalone-блоку нижче.
54
-
55
- **Поведінкова ідіома:**
56
-
57
- `run` — це тонкий **прокладочний шар**: він фіксує **директорію** правила (`import.meta.dirname`), бо саме за нею раннер визначає `id` правила (ім'я каталогу `worktree`), знаходить сусідні файли (`policy.mjs`, `applies.mjs`, `check-*.mjs`, `<id>.mdc`) та збирає пайплайн.
8
+ # fix.mjs
58
9
 
59
- ### Standalone-блок (top-level)
60
-
61
- Анонімний side-effect блок, що виконується лише при прямому запуску:
62
-
63
- ```js
64
- if (isRunAsCli(import.meta.url)) {
65
- process.exit(await runRuleCli(import.meta.dirname))
66
- }
67
- ```
68
-
69
- **Поведінка:**
70
-
71
- - `isRunAsCli(import.meta.url)` повертає `true`, якщо модуль є **головним** entry-point процесу (а не імпортований). Це типова заміна паттерну `require.main === module` для ESM.
72
- - `runRuleCli(import.meta.dirname)` — повна CLI-обгортка над `run`: завантажує проєктний config, застосовує whitelist (наприклад, виключення з `.cursorignore`/конфігу), друкує summary після виконання та повертає підсумковий exit-code.
73
- - `process.exit(code)` — закриває процес з кодом, придатним для CI/IDE. Лінт-винятки `n/no-process-exit` та `unicorn/no-process-exit` свідомо вимкнені коментарем, бо standalone entry-point має лагідно виходити з конкретним кодом, інакше CI не отримає сигналу про fail.
74
-
75
- ## Залежності
76
-
77
- Внутрішні залежності (відносні до `npm/scripts/lib/`):
78
-
79
- | Імпорт | З файлу | Призначення |
80
- | ----------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
81
- | `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Перевіряє, чи поточний модуль є головним процесом (заміна `require.main === module` для ESM). |
82
- | `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Standalone-обгортка: config-loading + whitelist + summary + exit-code. Еквівалент `npx @nitra/cursor fix <id>` для одного правила. |
83
- | `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Стандартний пайплайн правила: applies → JS-concerns → policy → mdc-refs. Приймає директорію правила та опційний `RuleContext`. |
84
-
85
- Зовнішні залежності та глобальні API:
86
-
87
- - `import.meta.dirname` — ESM-аналог `__dirname`; використовується для передачі шляху до каталогу правила раннерам.
88
- - `import.meta.url` — використовується `isRunAsCli` для визначення головного модуля.
89
- - `process.exit(code)` — Node.js/Bun runtime API для встановлення exit-code.
90
-
91
- Файл **не залежить** напряму від конкретних `check-*.mjs`, `policy.mjs`, `<id>.mdc` сусідньої директорії — їх відкриває та інтерпретує `runStandardRule`.
92
-
93
- ## Потік виконання / Використання
94
-
95
- ### Сценарій A — Library mode (виклик з оркестратора)
96
-
97
- ```js
98
- import { run } from '@nitra/cursor/rules/worktree/fix.mjs'
99
-
100
- const ctx = { walkCache: new Map(/* ... */) }
101
- const exitCode = await run(ctx)
102
- // exitCode === 0 — OK, 1 — порушення
103
- ```
104
-
105
- Послідовність:
106
-
107
- 1. Оркестратор (`@nitra/cursor fix` без аргументів або з переліком правил) проходить по списку правил і для кожного робить `import('.../fix.mjs')`.
108
- 2. Викликає `run(sharedCtx)`, де `sharedCtx.walkCache` спільний для всіх правил у прогоні (економить FS-обходи).
109
- 3. `run` делегує в `runStandardRule(import.meta.dirname, ctx)`.
110
- 4. `runStandardRule` послідовно виконує під-кроки правила `worktree`: `applies` → JS-перевірки → `policy` → `mdc-refs`.
111
- 5. Повертається `number` (0/1), оркестратор агрегує результати всіх правил у фінальний summary.
112
-
113
- Жодних `process.exit` у цьому сценарії — control flow залишається в оркестратора.
114
-
115
- ### Сценарій B — Standalone mode (прямий запуск)
116
-
117
- ```bash
118
- bun npm/rules/worktree/fix.mjs
119
- # або (еквівалент)
120
- npx @nitra/cursor fix worktree
121
- ```
122
-
123
- Послідовність:
124
-
125
- 1. Bun завантажує файл; `import.meta.url` вказує на сам файл як головний entry-point.
126
- 2. `isRunAsCli(import.meta.url)` повертає `true`.
127
- 3. Виконується `await runRuleCli(import.meta.dirname)`:
128
- - читає проєктний config (`.cursor`/`package.json`);
129
- - застосовує whitelist (включення/виключення файлів);
130
- - формує `RuleContext` та викликає `run(ctx)` (опосередковано, через стандартний пайплайн);
131
- - друкує summary в stdout (кількість порушень, перелік фейлів тощо).
132
- 4. `process.exit(<exitCode>)` — процес закривається з кодом для CI/IDE.
10
+ ## Огляд
133
11
 
134
- ### Чому така архітектура (dual role)
12
+ Виконує застосування політики JS-занепокоєних та конфігурації на наданому контексті прогону, генеруючи посилання MDC та повертаючи результат.
135
13
 
136
- - Library mode дає **спільний батч**: один обхід FS, одна summary, паралельний прогін правил.
137
- - Standalone mode зручний для **локальної налагодки** одного правила і для **IDE-інтеграцій** (Cursor запускає окремий `fix.mjs` й отримує exit-code).
138
- - Логіка не дублюється — `runRuleCli` всередині все одно використовує той самий `runStandardRule`.
14
+ Виконується при запуску через командний рядок, виконуючи повний еквівалент команди `npx @nitra/cursor fix <id>`, завантажуючи конфігурацію, перевіряючи дозволені елементи та генеруючи зведену інформацію з виходом з процесу залежно від результату.
139
15
 
140
- ### Конвенція директорії правила
16
+ ## Поведінка
141
17
 
142
- Для коректної роботи цього `fix.mjs` у сусідній директорії `npm/rules/worktree/` мають бути (опційно — будь-які з):
18
+ 1. Запуск правила.
19
+ * Приймає контекст прогону.
20
+ * Виконує застосування JS-занепокоєних.
21
+ * Застосовує політику.
22
+ * Генерує посилання MDC.
23
+ * Повертає результат прогону.
143
24
 
144
- - `worktree.mdc` (або `<id>.mdc`) людинозрозумілий опис правила;
145
- - `applies.mjs` детектор застосовності;
146
- - `policy.mjs` декларативні політики;
147
- - `check-*.mjs` — JS-перевірки (детальна логіка);
148
- - `meta.json` метадані (у т.ч. `worktree: true` для skill-ів).
25
+ 2. Запуск у режимі CLI.
26
+ * Виконується при запуску через CLI.
27
+ * Виконує повний еквівалент команди `npx @nitra/cursor fix <id>`.
28
+ * Виконує завантаження конфігурації.
29
+ * Виконує перевірку дозволених елементів.
30
+ * Генерує зведену інформацію.
31
+ * Виконує вихід з процесу залежно від результату.
149
32
 
150
- Сам `fix.mjs` нічого з цього не імпортує напряму — все шукає `runStandardRule` за `import.meta.dirname`.
33
+ ## Публічний API
151
34
 
152
- ## Rebuild Test
35
+ run запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
36
+ Library mode — викликається CLI orchestration через `import + run`.
153
37
 
154
- Перевірка, що файл можна відновити з цієї документації:
38
+ ## Гарантії поведінки
155
39
 
156
- 1. **Імпорти:** два named-імпорти `isRunAsCli`, `runRuleCli` з `../../scripts/lib/run-rule-cli.mjs`; `runStandardRule` з `../../scripts/lib/run-standard-rule.mjs`.
157
- 2. **Export `run(ctx)`:** JSDoc з `@param ctx` (опційний, тип `RuleContext` із `run-standard-rule.mjs`) та `@returns {Promise<number>}` (0 OK, 1 — порушення); тіло — `return runStandardRule(import.meta.dirname, ctx)`.
158
- 3. **Top-level `if`:** `if (isRunAsCli(import.meta.url)) { process.exit(await runRuleCli(import.meta.dirname)) }`.
159
- 4. **Eslint-disable коментар:** перед `process.exit` — `// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE`.
160
- 5. **Коментарі:** перед `if`-блоком — пояснення про standalone-режим та еквівалентність `npx @nitra/cursor fix <id>`; над `run` — JSDoc про послідовність applies → JS-concerns → policy → mdc-refs та library mode.
161
- 6. **Стиль:** ESM, без `;` у кінці рядків, single quotes, без default-експортів.
40
+ - Read-only: файл не виконує операцій запису у файлову систему.
41
+ - Кешує результати в межах одного прогону.
42
+ - Не звертається до мережі.