@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.
- package/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
- package/CHANGELOG.md +37 -3
- package/bin/docs/n-cursor.md +636 -0
- package/bin/docs/rename-yaml-extensions.md +207 -0
- package/bin/n-cursor.js +30 -3
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +18 -0
- package/rules/abie/js/docs/applies.md +26 -0
- package/rules/abie/js/docs/env_dns.md +32 -0
- package/rules/abie/js/docs/firebase_hosting.md +23 -0
- package/rules/abie/js/docs/hc_pairing.md +35 -0
- package/rules/abie/js/docs/ua_http_route.md +28 -0
- package/rules/abie/js/docs/ua_node_selector.md +28 -0
- package/rules/abie/lib/docs/enabled.md +29 -0
- package/rules/abie/lib/docs/env-dns.md +35 -0
- package/rules/abie/lib/docs/hc-yaml.md +33 -0
- package/rules/abie/lib/docs/http-route.md +44 -0
- package/rules/abie/lib/docs/k8s-tree.md +40 -0
- package/rules/abie/lib/docs/kustomization-patches.md +47 -0
- package/rules/abie/lib/docs/overlay-paths.md +38 -0
- package/rules/abie/lib/docs/yaml.md +29 -0
- package/rules/adr/docs/fix.md +148 -0
- package/rules/adr/js/docs/hooks.md +259 -0
- package/rules/bun/docs/fix.md +156 -0
- package/rules/bun/js/docs/layout.md +393 -0
- package/rules/capacitor/docs/fix.md +121 -0
- package/rules/capacitor/js/docs/platforms.md +295 -0
- package/rules/changelog/changelog.mdc +2 -2
- package/rules/changelog/docs/fix.md +174 -0
- package/rules/changelog/js/consistency.mjs +114 -13
- package/rules/changelog/js/docs/consistency.md +387 -0
- package/rules/changelog/lib/docs/package-manifest.md +210 -0
- package/rules/ci4/docs/fix.md +179 -0
- package/rules/ci4/js/docs/marksman_config.md +128 -0
- package/rules/docker/docker.mdc +8 -3
- package/rules/docker/docs/fix.md +171 -0
- package/rules/docker/js/docs/lint.md +258 -0
- package/rules/docker/lib/docs/docker-hadolint.md +184 -0
- package/rules/docker/lib/docs/docker-mirror.md +247 -0
- package/rules/docker/lib/docs/docker-native-addon.md +170 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
- package/rules/docker/lint/docs/lint.md +193 -0
- package/rules/efes/docs/fix.md +203 -0
- package/rules/feedback/docs/fix.md +140 -0
- package/rules/flow/docs/fix.md +152 -0
- package/rules/ga/docs/fix.md +158 -0
- package/rules/ga/js/docs/lint.md +100 -0
- package/rules/ga/js/docs/workflows.md +217 -0
- package/rules/ga/lint/docs/lint.md +209 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
- package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
- package/rules/graphql/docs/fix.md +126 -0
- package/rules/graphql/js/docs/tooling.md +264 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
- package/rules/hasura/docs/fix.md +120 -0
- package/rules/hasura/hasura.mdc +14 -0
- package/rules/hasura/js/docs/internal_urls.md +326 -0
- package/rules/image-avif/docs/fix.md +132 -0
- package/rules/image-avif/js/docs/avif_generation.md +241 -0
- package/rules/image-compress/docs/fix.md +150 -0
- package/rules/image-compress/js/docs/package_setup.md +191 -0
- package/rules/js-bun-db/docs/fix.md +148 -0
- package/rules/js-bun-db/js/docs/safety.md +231 -0
- package/rules/js-bun-db/js-bun-db.mdc +42 -13
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
- package/rules/js-bun-redis/docs/fix.md +123 -0
- package/rules/js-bun-redis/js/docs/imports.md +176 -0
- package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
- package/rules/js-lint/docs/fix.md +117 -0
- package/rules/js-lint/js/docs/lint.md +250 -0
- package/rules/js-lint/js/docs/tooling.md +348 -0
- package/rules/js-lint/js/docs/utils_imports.md +207 -0
- package/rules/js-lint/js/lint-findings.mjs +110 -0
- package/rules/js-lint/js/lint.mjs +86 -15
- package/rules/js-lint-ci/docs/fix.md +154 -0
- package/rules/js-lint-ci/js/docs/lint.md +144 -0
- package/rules/js-mssql/docs/fix.md +128 -0
- package/rules/js-mssql/js/docs/deps.md +263 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
- package/rules/js-run/docs/fix.md +144 -0
- package/rules/js-run/js/docs/runtime.md +388 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
- package/rules/js-run/lib/docs/check-env-scan.md +433 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
- package/rules/k8s/docs/fix.md +129 -0
- package/rules/k8s/js/docs/manifests.md +344 -0
- package/rules/k8s/js/manifests.mjs +6 -2
- package/rules/k8s/k8s.mdc +4 -2
- package/rules/k8s/lint/docs/lint.md +411 -0
- package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
- package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
- package/rules/nginx-default-tpl/docs/fix.md +124 -0
- package/rules/nginx-default-tpl/js/docs/template.md +378 -0
- package/rules/npm-module/docs/fix.md +98 -0
- package/rules/npm-module/js/docs/package_structure.md +274 -0
- package/rules/npm-module/js/docs/rule_meta.md +137 -0
- package/rules/npm-module/js/docs/skill_meta.md +190 -0
- package/rules/php/docs/fix.md +107 -0
- package/rules/php/js/docs/tooling.md +152 -0
- package/rules/php/lint/docs/lint.md +215 -0
- package/rules/python/docs/fix.md +163 -0
- package/rules/python/js/docs/applies.md +108 -0
- package/rules/python/js/docs/tooling.md +153 -0
- package/rules/python/lint/docs/lint.md +322 -0
- package/rules/rego/docs/fix.md +121 -0
- package/rules/rego/js/docs/applies.md +174 -0
- package/rules/rego/js/docs/lint.md +118 -0
- package/rules/rego/lint/docs/lint.md +204 -0
- package/rules/release/docs/change.md +185 -0
- package/rules/release/docs/fix.md +119 -0
- package/rules/release/docs/release.md +222 -0
- package/rules/release/lib/docs/aggregate.md +246 -0
- package/rules/release/lib/docs/change-file.md +200 -0
- package/rules/release/lib/docs/fallback.md +203 -0
- package/rules/rust/docs/fix.md +129 -0
- package/rules/rust/js/docs/applies.md +140 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
- package/rules/security/docs/fix.md +86 -0
- package/rules/security/js/docs/lint.md +171 -0
- package/rules/security/js/docs/sample_secret.md +190 -0
- package/rules/security/js/docs/trufflehog.md +137 -0
- package/rules/security/js/lint.mjs +9 -1
- package/rules/style-lint/docs/fix.md +155 -0
- package/rules/style-lint/js/docs/lint.md +184 -0
- package/rules/style-lint/js/docs/tooling.md +194 -0
- package/rules/tauri/docs/fix.md +158 -0
- package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
- package/rules/tauri/js/docs/tooling.md +228 -0
- package/rules/test/coverage/coverage.mjs +15 -3
- package/rules/test/docs/fix.md +132 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
- package/rules/test/js/docs/cargo_mutants_config.md +173 -0
- package/rules/test/js/docs/location.md +136 -0
- package/rules/test/js/docs/no-process-chdir.md +160 -0
- package/rules/test/js/docs/no-relative-fs-path.md +271 -0
- package/rules/test/js/docs/stryker_config.md +152 -0
- package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
- package/rules/text/docs/fix.md +118 -0
- package/rules/text/js/docs/forbidden-prettier.md +143 -0
- package/rules/text/js/docs/formatting.md +256 -0
- package/rules/text/js/docs/lint.md +122 -0
- package/rules/text/lint/docs/lint.md +220 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
- package/rules/text/lint/docs/run-shellcheck.md +212 -0
- package/rules/text/lint/docs/run-v8r.md +197 -0
- package/rules/vue/docs/fix.md +127 -0
- package/rules/vue/js/docs/packages.md +335 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
- package/rules/worktree/docs/fix.md +161 -0
- package/schemas/rule-meta.json +5 -1
- package/scripts/auto-rules.mjs +7 -4
- package/scripts/coverage-classify/docs/apply.md +202 -0
- package/scripts/coverage-classify/docs/cache.md +203 -0
- package/scripts/coverage-classify/docs/index.md +218 -0
- package/scripts/coverage-classify/docs/prompt.md +132 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
- package/scripts/coverage-fix-extract.mjs +122 -0
- package/scripts/coverage-fix.mjs +1 -1
- package/scripts/dispatcher/docs/graph.md +346 -0
- package/scripts/dispatcher/docs/index.md +236 -0
- package/scripts/dispatcher/docs/trace.md +296 -0
- package/scripts/dispatcher/index.mjs +1 -1
- package/scripts/dispatcher/lib/active.mjs +4 -8
- package/scripts/dispatcher/lib/commands.mjs +7 -11
- package/scripts/dispatcher/lib/docs/active.md +348 -0
- package/scripts/dispatcher/lib/docs/artifact.md +232 -0
- package/scripts/dispatcher/lib/docs/budget.md +167 -0
- package/scripts/dispatcher/lib/docs/capability.md +196 -0
- package/scripts/dispatcher/lib/docs/commands.md +210 -0
- package/scripts/dispatcher/lib/docs/events.md +182 -0
- package/scripts/dispatcher/lib/docs/executor.md +190 -0
- package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
- package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
- package/scripts/dispatcher/lib/docs/gate.md +231 -0
- package/scripts/dispatcher/lib/docs/level.md +335 -0
- package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
- package/scripts/dispatcher/lib/docs/plan.md +200 -0
- package/scripts/dispatcher/lib/docs/planner.md +269 -0
- package/scripts/dispatcher/lib/docs/review.md +255 -0
- package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
- package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
- package/scripts/dispatcher/lib/docs/spec.md +203 -0
- package/scripts/dispatcher/lib/docs/state-store.md +303 -0
- package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
- package/scripts/dispatcher/lib/executor.mjs +6 -1
- package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
- package/scripts/dispatcher/lib/level.mjs +29 -3
- package/scripts/dispatcher/lib/review.mjs +1 -1
- package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
- package/scripts/docs/auto-rules.md +376 -0
- package/scripts/docs/auto-skills.md +173 -0
- package/scripts/docs/build-agents-commands.md +183 -0
- package/scripts/docs/cli-entry.md +153 -0
- package/scripts/docs/coverage-fix.md +177 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
- package/scripts/lib/changed-files.mjs +4 -1
- package/scripts/lib/diff-added-lines.mjs +85 -0
- package/scripts/lib/docs/changed-files.md +149 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
- package/scripts/lib/docs/check-reporter.md +175 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
- package/scripts/lib/docs/discover-checkable-rules.md +165 -0
- package/scripts/lib/docs/ensure-tool.md +254 -0
- package/scripts/lib/docs/generated-markdown.md +275 -0
- package/scripts/lib/docs/gha-workflow.md +326 -0
- package/scripts/lib/docs/inline-template-links.md +303 -0
- package/scripts/lib/docs/list-rule-ids.md +156 -0
- package/scripts/lib/docs/load-cursor-config.md +147 -0
- package/scripts/lib/docs/mirror-parity.md +167 -0
- package/scripts/lib/worktree.mjs +26 -0
- package/scripts/worktree-cli.mjs +12 -2
- package/skills/coverage-fix/SKILL.md +34 -45
- package/skills/docgen/SKILL.md +44 -23
- package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
- package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
- package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
- package/skills/docgen/js/docgen-ignore.mjs +54 -0
- package/skills/docgen/js/docgen-scan.mjs +37 -21
- package/skills/llm-patch/SKILL.md +23 -2
- package/skills/start-check/SKILL.md +26 -53
- package/skills/start-check/js/check.mjs +211 -0
- package/skills/taze/SKILL.md +9 -3
- package/skills/taze/js/diff.mjs +154 -0
- package/types/bin/n-cursor.d.ts +1 -1
- package/skills/fix-tests/SKILL.md +0 -119
- package/skills/fix-tests/meta.json +0 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# check-mdc-template-refs.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `check-mdc-template-refs.mjs` — це невелика утиліта, призначена для перевірки цілісності посилань між файлом правила `<id>.mdc` та шаблонами, що лежать у підкаталогах `template/` цього самого правила. Він обходить структуру каталогів правила (`fix/<concern>/template/`, `policy/<concern>/template/`), збирає всі знайдені файли й порівнює їхні відносні шляхи з вмістом основного файлу правила `<id>.mdc`. Результатом є перелік шаблонних файлів, на які у `.mdc` немає жодного markdown-посилання, тобто «осиротілі» (orphaned) шаблони.
|
|
6
|
+
|
|
7
|
+
Типовий контекст застосування: модуль використовується в утилітах перевірки/лінтингу правил репозиторію (`npm/rules/<id>/`), де `.mdc`-файл описує правило людською мовою й має посилатися на свої шаблони. Якщо шаблон існує, але не згаданий у `.mdc`, цей модуль повідомить про нього, щоб супровідник правила або додав посилання, або видалив зайвий шаблон.
|
|
8
|
+
|
|
9
|
+
Модуль повністю асинхронний (використовує `node:fs/promises`), стандартної залежності від сторонніх бібліотек не має, працює у середовищі Node.js (а також сумісне з Bun) як ES-модуль (`.mjs`).
|
|
10
|
+
|
|
11
|
+
## Експорти / API
|
|
12
|
+
|
|
13
|
+
Модуль експортує єдину функцію:
|
|
14
|
+
|
|
15
|
+
- `findMissingMdcRefs(ruleDir, ruleId)` — публічний експорт (`export async function`).
|
|
16
|
+
|
|
17
|
+
Дві внутрішні (не експортовані) допоміжні функції:
|
|
18
|
+
|
|
19
|
+
- `walkTemplateDirs(ruleDir)` — обходить підкаталоги `fix/*/template/` і `policy/*/template/`.
|
|
20
|
+
- `collectFiles(dir)` — рекурсивно збирає всі файли в заданому каталозі.
|
|
21
|
+
|
|
22
|
+
Імпорти з ядра Node:
|
|
23
|
+
|
|
24
|
+
- `existsSync` з `node:fs` — синхронна перевірка існування шляху.
|
|
25
|
+
- `readdir`, `readFile`, `stat` з `node:fs/promises` — асинхронні операції з файловою системою.
|
|
26
|
+
- `join`, `relative` з `node:path` — нормалізація та побудова шляхів.
|
|
27
|
+
|
|
28
|
+
## Функції
|
|
29
|
+
|
|
30
|
+
### findMissingMdcRefs
|
|
31
|
+
|
|
32
|
+
Сигнатура:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
export async function findMissingMdcRefs(ruleDir, ruleId): Promise<string[]>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Параметри:
|
|
39
|
+
|
|
40
|
+
- `ruleDir` — `string`, абсолютний шлях до каталогу правила (за конвенцією `npm/rules/<id>/`).
|
|
41
|
+
- `ruleId` — `string`, basename правила (наприклад, `"security"`). Використовується для побудови імені `.mdc`-файлу: `${ruleId}.mdc`.
|
|
42
|
+
|
|
43
|
+
Що робить:
|
|
44
|
+
|
|
45
|
+
1. Будує очікуваний шлях до файлу правила: `mdcPath = join(ruleDir, `${ruleId}.mdc`)`.
|
|
46
|
+
2. Якщо файл `.mdc` не існує — повертає порожній масив `[]` (нічого перевіряти).
|
|
47
|
+
3. Читає текст `.mdc` у пам'ять як UTF-8 рядок.
|
|
48
|
+
4. Викликає `walkTemplateDirs(ruleDir)`, щоб отримати масив усіх файлів із `template/`-каталогів правила (як шляхи, відносні до `ruleDir`).
|
|
49
|
+
5. Фільтрує отриманий масив, залишаючи лише ті файли, посилань на які НЕ виявлено в тексті `.mdc`. Перевірка наявності посилання — підрядкова: файл вважається «посиланим», якщо в тексті `.mdc` зустрічається будь-яке з двох вкраплень:
|
|
50
|
+
- `./<rel>` — типова форма markdown-посилання `[label](./path)`.
|
|
51
|
+
- `(<rel>)` — також зловить голу форму `(path)` у круглих дужках.
|
|
52
|
+
|
|
53
|
+
Повертає:
|
|
54
|
+
|
|
55
|
+
- `Promise<string[]>` — масив відносних шляхів (відносно `ruleDir`) файлів шаблонів, на які в `.mdc` немає посилання.
|
|
56
|
+
|
|
57
|
+
Side effects:
|
|
58
|
+
|
|
59
|
+
- Виключно read-only доступ до файлової системи: `existsSync`, `readFile`, `readdir`, `stat`. Файли або каталоги не створюються, не змінюються, не видаляються.
|
|
60
|
+
- Не виводить нічого в `stdout`/`stderr`. Не має побічних ефектів на process state.
|
|
61
|
+
|
|
62
|
+
Помилки:
|
|
63
|
+
|
|
64
|
+
- Може кинути помилку, якщо `readFile`/`readdir`/`stat` зазнають невдачі з причин, відмінних від «файл не існує» (наприклад, права доступу). Перед читанням існування `mdcPath` перевіряється явно, тому відсутність `.mdc` помилки не дає.
|
|
65
|
+
|
|
66
|
+
### walkTemplateDirs (внутрішня)
|
|
67
|
+
|
|
68
|
+
Сигнатура:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
async function walkTemplateDirs(ruleDir): Promise<string[]>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Параметри:
|
|
75
|
+
|
|
76
|
+
- `ruleDir` — `string`, абсолютний шлях до каталогу правила.
|
|
77
|
+
|
|
78
|
+
Що робить:
|
|
79
|
+
|
|
80
|
+
1. Ітерується по двох жорстко закодованих «kind»-категоріях: `'fix'` та `'policy'`.
|
|
81
|
+
2. Для кожного `kind` будує шлях `kindDir = join(ruleDir, kind)`. Якщо такого каталогу немає — пропускає.
|
|
82
|
+
3. Для кожного `concern` (підкаталога або файлу всередині `kindDir`, який повертає `readdir`) будує шлях `tpl = join(kindDir, concern, 'template')`.
|
|
83
|
+
4. Якщо `tpl` не існує — пропускає. Якщо існує, але не є каталогом (`stat(tpl).isDirectory() === false`) — також пропускає.
|
|
84
|
+
5. Викликає `collectFiles(tpl)` для рекурсивного збору всіх файлів і додає їх до результуючого масиву.
|
|
85
|
+
6. На виході перетворює абсолютні шляхи у шляхи, відносні до `ruleDir` (`relative(ruleDir, p)`).
|
|
86
|
+
|
|
87
|
+
Повертає:
|
|
88
|
+
|
|
89
|
+
- `Promise<string[]>` — відносні (від `ruleDir`) шляхи всіх файлів у `fix/*/template/` і `policy/*/template/`.
|
|
90
|
+
|
|
91
|
+
Side effects:
|
|
92
|
+
|
|
93
|
+
- Лише read-only ФС-операції.
|
|
94
|
+
|
|
95
|
+
Примітка: значення `readdir(kindDir)` (без `withFileTypes`) повертає масив імен і файлів, і каталогів. Перевірка `existsSync(tpl)` та `stat(tpl).isDirectory()` коректно відсіює випадки, коли `concern` — це файл, а не каталог.
|
|
96
|
+
|
|
97
|
+
### collectFiles (внутрішня)
|
|
98
|
+
|
|
99
|
+
Сигнатура:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
async function collectFiles(dir): Promise<string[]>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Параметри:
|
|
106
|
+
|
|
107
|
+
- `dir` — `string`, абсолютний шлях каталогу, який треба обійти.
|
|
108
|
+
|
|
109
|
+
Що робить:
|
|
110
|
+
|
|
111
|
+
1. Викликає `readdir(dir, { withFileTypes: true })` і отримує `Dirent[]`.
|
|
112
|
+
2. Для кожного запису будує повний шлях `full = join(dir, entry.name)`.
|
|
113
|
+
3. Якщо запис — каталог (`entry.isDirectory()`), рекурсивно занурюється в нього й приєднує знайдене до результату.
|
|
114
|
+
4. Якщо запис — файл (або symlink/інше, що НЕ каталог), додає `full` як є.
|
|
115
|
+
|
|
116
|
+
Повертає:
|
|
117
|
+
|
|
118
|
+
- `Promise<string[]>` — абсолютні шляхи всіх файлів усередині `dir` (з усіх рівнів вкладеності).
|
|
119
|
+
|
|
120
|
+
Side effects:
|
|
121
|
+
|
|
122
|
+
- Read-only обхід ФС.
|
|
123
|
+
|
|
124
|
+
Зауваження щодо поведінки: символьні посилання й сокети не каталогізуються як директорії (`isDirectory()` поверне `false`), отже потрапляють у список як «файли». Гранична глибина рекурсії не обмежена — для зацикленої структури symlinks теоретично можливе нескінченне занурення; на практиці шаблонні каталоги правил такого не мають.
|
|
125
|
+
|
|
126
|
+
## Залежності
|
|
127
|
+
|
|
128
|
+
Стандартна бібліотека Node.js (ESM):
|
|
129
|
+
|
|
130
|
+
- `node:fs` — `existsSync` (синхронна перевірка існування).
|
|
131
|
+
- `node:fs/promises` — `readdir`, `readFile`, `stat` (асинхронні ФС-операції).
|
|
132
|
+
- `node:path` — `join`, `relative` (нормалізація шляхів).
|
|
133
|
+
|
|
134
|
+
Зовнішніх npm-залежностей немає. Модуль не використовує жодних глобальних змінних, конфігів, env-vars чи CLI-аргументів. Не звертається до мережі.
|
|
135
|
+
|
|
136
|
+
Передумови оточення:
|
|
137
|
+
|
|
138
|
+
- Node.js версія, що підтримує ES Modules (`.mjs`) і `node:`-protocol-imports (Node 14+; на практиці Node 18+/Bun).
|
|
139
|
+
- Доступ на читання до каталогу `ruleDir` та його підкаталогів.
|
|
140
|
+
|
|
141
|
+
## Потік виконання / Використання
|
|
142
|
+
|
|
143
|
+
### Очікувана структура каталогу правила
|
|
144
|
+
|
|
145
|
+
Модуль розрахований на конвенцію розкладки правил:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
<ruleDir>/
|
|
149
|
+
<ruleId>.mdc ← текст правила з markdown-посиланнями
|
|
150
|
+
fix/
|
|
151
|
+
<concern-A>/
|
|
152
|
+
template/
|
|
153
|
+
file1.ext
|
|
154
|
+
nested/file2.ext
|
|
155
|
+
<concern-B>/
|
|
156
|
+
template/
|
|
157
|
+
...
|
|
158
|
+
policy/
|
|
159
|
+
<concern-C>/
|
|
160
|
+
template/
|
|
161
|
+
...
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Перелічуються лише файли всередині `fix/*/template/` і `policy/*/template/`. Будь-який інший контент (`check-*.mjs`, `README`, інші підкаталоги) ігнорується.
|
|
165
|
+
|
|
166
|
+
### Алгоритм у двох словах
|
|
167
|
+
|
|
168
|
+
1. Зібрати всі файли з `fix/*/template/` і `policy/*/template/`, перетворити у відносні шляхи від `ruleDir`.
|
|
169
|
+
2. Прочитати `<ruleId>.mdc`.
|
|
170
|
+
3. Для кожного відносного шляху перевірити, чи зустрічається в тексті `.mdc` хоча б одна з форм `./<rel>` або `(<rel>)`. Якщо НЕ зустрічається — додати у вихідний список.
|
|
171
|
+
|
|
172
|
+
### Типове використання
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
import { findMissingMdcRefs } from './check-mdc-template-refs.mjs'
|
|
176
|
+
|
|
177
|
+
const missing = await findMissingMdcRefs('/abs/path/to/npm/rules/security', 'security')
|
|
178
|
+
if (missing.length > 0) {
|
|
179
|
+
console.error('Orphaned template files (no markdown link in .mdc):')
|
|
180
|
+
for (const rel of missing) console.error(` - ${rel}`)
|
|
181
|
+
process.exit(1)
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Цей виклик часто є частиною check-скрипта правила (`check-<id>.mjs`) або агрегованого валідатора, який обходить усі правила репозиторію.
|
|
186
|
+
|
|
187
|
+
### Граничні випадки
|
|
188
|
+
|
|
189
|
+
- `.mdc` відсутній — повертається `[]`. Це навмисно: правило без `.mdc` не валідуємо «на покинуті шаблони».
|
|
190
|
+
- Каталоги `fix/`, `policy/`, `template/` відсутні — обходяться silent, результат для них порожній.
|
|
191
|
+
- Шаблонів немає взагалі — повертається `[]`.
|
|
192
|
+
- `.mdc` посилається на шаблон, якого фізично немає, — цей сценарій модуль НЕ перевіряє (зворотний напрям перевірки тут не реалізовано).
|
|
193
|
+
- Перевірка посилань — підрядкова, без парсингу markdown. Якщо файл містить `./foo/bar.md` як випадковий збіг у документації, шаблон `foo/bar.md` буде вважатися посиланим, навіть якщо це не власне markdown-link. На практиці це не проблема, бо шляхи всередині `template/` достатньо унікальні.
|
|
194
|
+
- Сепаратор шляхів — платформозалежний (`path.join`/`path.relative` використовують `\` на Windows). Усередині `.mdc` посилання, як правило, пишуть зі слешем `/` — на Windows це може давати хибно-позитивні результати (тобто файл буде вважатися «без посилання»). У цільовому оточенні (macOS/Linux/Docker) проблема не виникає.
|
|
195
|
+
|
|
196
|
+
### Складність
|
|
197
|
+
|
|
198
|
+
- Час: O(N + M), де N — сумарна кількість файлів у `template/`-каталогах, а M — довжина тексту `.mdc` (через `String.prototype.includes`, що виконується для кожного з N файлів двічі).
|
|
199
|
+
- Пам'ять: лінійна щодо розміру `.mdc` плюс масиву шляхів.
|
|
200
|
+
|
|
201
|
+
## Rebuild Test
|
|
202
|
+
|
|
203
|
+
Файл `check-mdc-template-refs.mjs` можна повністю відновити, дотримуючись опису вище, якщо забезпечити такі властивості:
|
|
204
|
+
|
|
205
|
+
1. ES-модуль (`.mjs`), без `default export`. Єдиний `export` — `async function findMissingMdcRefs(ruleDir, ruleId)`.
|
|
206
|
+
2. Імпорти лише з `node:fs` (`existsSync`), `node:fs/promises` (`readdir`, `readFile`, `stat`) і `node:path` (`join`, `relative`).
|
|
207
|
+
3. Дві внутрішні async-функції: `walkTemplateDirs(ruleDir)` та `collectFiles(dir)`.
|
|
208
|
+
4. `walkTemplateDirs` ітерується по двох категоріях у фіксованому порядку: спочатку `'fix'`, потім `'policy'`. Пропускає неіснуючі `kindDir`. Для кожного `concern` перевіряє існування й те, що `template` — каталог; у разі успіху делегує до `collectFiles`. На виході конвертує абсолютні шляхи у відносні від `ruleDir`.
|
|
209
|
+
5. `collectFiles` використовує `readdir(dir, { withFileTypes: true })`, рекурсивно занурюється тільки в каталоги, інші записи додає в результат як абсолютні шляхи.
|
|
210
|
+
6. `findMissingMdcRefs` спочатку перевіряє існування `<ruleId>.mdc`, читає його у UTF-8, отримує всі файли через `walkTemplateDirs`, потім фільтрує, залишаючи лише ті `rel`, для яких у тексті `.mdc` ОДНОЧАСНО немає `./<rel>` і немає `(<rel>)` (логічне AND через подвійне заперечення з `||` під `!`).
|
|
211
|
+
7. Поведінка строго read-only: жодних запитів у мережу, жодного запису на диск, жодного консольного виводу.
|
|
212
|
+
8. Повертає `Promise<string[]>`, відносні шляхи (з POSIX/платформозалежним сепаратором у міру `path.relative`).
|
|
213
|
+
|
|
214
|
+
Тестові сценарії для smoke-перевірки:
|
|
215
|
+
|
|
216
|
+
- Каталог без `.mdc` → `[]`.
|
|
217
|
+
- `.mdc` є, але `fix/`, `policy/` відсутні → `[]`.
|
|
218
|
+
- `fix/x/template/a.txt` існує, у `.mdc` згадано `./fix/x/template/a.txt` → `[]`.
|
|
219
|
+
- Той самий шаблон, але `.mdc` посилається як `(fix/x/template/a.txt)` (без `./`) → `[]`.
|
|
220
|
+
- Той самий шаблон, але в `.mdc` посилання немає → `['fix/x/template/a.txt']`.
|
|
221
|
+
- Вкладений `fix/x/template/sub/b.txt`, посилання немає → містить `'fix/x/template/sub/b.txt'`.
|
|
222
|
+
- Запис `concern`, що насправді є файлом, а не каталогом → пропускається без помилки.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# check-reporter.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `check-reporter.mjs` — це невелика спільна (shared) бібліотека-фабрика для check-скриптів (`check-*.mjs`) і для `lint-docker`. Вона надає уніфікований спосіб репортити результати перевірок: успіхи (`pass`) та помилки (`fail`), а також акумулювати фінальний код виходу процесу (`exit code`).
|
|
6
|
+
|
|
7
|
+
Основна ідея: викликаючи фабрику `createCheckReporter()`, споживач отримує об’єкт з трьома методами — `pass`, `fail` і `getExitCode`. Будь-який виклик `fail` переводить внутрішній лічильник у стан помилки (`exitCode = 1`), і подальший `getExitCode()` повертатиме `1`. Якщо `fail` жодного разу не викликали — `getExitCode()` поверне `0`.
|
|
8
|
+
|
|
9
|
+
Модуль навмисно тримає API мінімальним і не нав’язує конкретний формат виводу: рядок успіху форматується делегованою функцією `pass` з `../utils/pass.mjs`, а помилка виводиться у форматі ` ❌ <msg>` через `console.log`.
|
|
10
|
+
|
|
11
|
+
Важлива поведінкова деталь: `getExitCode` — це **метод-геттер у вигляді функції**, а не геттер-властивість через `get`. Тому слід викликати саме `reporter.getExitCode()` у момент завершення, а не деструктурувати `const { exitCode } = reporter` — інакше отримаєш undefined або застаріле значення, оскільки `exitCode` як поле в об’єкті не експонується назовні (це замикання).
|
|
12
|
+
|
|
13
|
+
## Експорти / API
|
|
14
|
+
|
|
15
|
+
Модуль експортує одну іменовану (named) функцію:
|
|
16
|
+
|
|
17
|
+
- `createCheckReporter()` — фабрика, що повертає об’єкт-репортер.
|
|
18
|
+
|
|
19
|
+
Структура об’єкта, який повертає `createCheckReporter()`:
|
|
20
|
+
|
|
21
|
+
| Поле | Тип | Опис |
|
|
22
|
+
| ------------- | ----------------------- | -------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| `pass` | `typeof pass` | Реекспорт функції `pass` з `../utils/pass.mjs`. Друкує рядок успіху у форматі, визначеному `pass`. |
|
|
24
|
+
| `fail` | `(msg: string) => void` | Друкує рядок помилки з префіксом ` ❌` і виставляє внутрішній `exitCode = 1`. |
|
|
25
|
+
| `getExitCode` | `() => number` | Повертає `0`, якщо `fail` жодного разу не викликали, інакше `1`. |
|
|
26
|
+
|
|
27
|
+
JSDoc-сигнатура у файлі:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
/**
|
|
31
|
+
* @returns {{
|
|
32
|
+
* pass: typeof pass,
|
|
33
|
+
* fail: (msg: string) => void,
|
|
34
|
+
* getExitCode: () => number
|
|
35
|
+
* }}
|
|
36
|
+
*/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Функції
|
|
40
|
+
|
|
41
|
+
### `createCheckReporter()`
|
|
42
|
+
|
|
43
|
+
**Сигнатура:**
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
export function createCheckReporter(): {
|
|
47
|
+
pass: typeof pass,
|
|
48
|
+
fail: (msg: string) => void,
|
|
49
|
+
getExitCode: () => number,
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Параметри:** немає.
|
|
54
|
+
|
|
55
|
+
**Повертає:** новий об’єкт-репортер з трьома методами. Кожен виклик `createCheckReporter()` створює **окреме замикання** з власним внутрішнім `exitCode`, тобто різні репортери незалежні один від одного.
|
|
56
|
+
|
|
57
|
+
**Side effects:** при створенні — жодних. Side effects виникають лише при виклику методів повернутого об’єкта (`fail` пише в stdout через `console.log`; `pass` робить те, що визначено в `../utils/pass.mjs`).
|
|
58
|
+
|
|
59
|
+
**Внутрішня логіка:**
|
|
60
|
+
|
|
61
|
+
1. Створюється локальна змінна `let exitCode = 0`.
|
|
62
|
+
2. Повертається об’єктний літерал з трьома методами:
|
|
63
|
+
- `pass` — пряма посилання на імпортовану функцію `pass`.
|
|
64
|
+
- `fail(msg)` — виконує `console.log(\` ❌ ${msg}\`)`і присвоює`exitCode = 1`.
|
|
65
|
+
- `getExitCode()` — повертає поточне значення `exitCode`.
|
|
66
|
+
|
|
67
|
+
### Метод `pass` (повертається з фабрики)
|
|
68
|
+
|
|
69
|
+
**Сигнатура:** успадковує сигнатуру функції `pass` з `../utils/pass.mjs` (`typeof pass`).
|
|
70
|
+
|
|
71
|
+
**Поведінка:** повний контракт — за `../utils/pass.mjs`. У межах цього модуля `pass` просто реекспортується без обгортки і не впливає на `exitCode`.
|
|
72
|
+
|
|
73
|
+
### Метод `fail(msg)` (повертається з фабрики)
|
|
74
|
+
|
|
75
|
+
**Сигнатура:**
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
fail(msg: string): void
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Параметри:**
|
|
82
|
+
|
|
83
|
+
- `msg: string` — текст повідомлення про помилку.
|
|
84
|
+
|
|
85
|
+
**Повертає:** `undefined`.
|
|
86
|
+
|
|
87
|
+
**Side effects:**
|
|
88
|
+
|
|
89
|
+
- Пише в stdout рядок виду ` ❌ <msg>` через `console.log` (два пробіли індентації + хрестик-емодзі + пробіл + повідомлення).
|
|
90
|
+
- Виставляє внутрішній `exitCode = 1` у замиканні фабрики.
|
|
91
|
+
|
|
92
|
+
**Семантика:** після першого виклику `fail` репортер «фіксує» загальний стан як помилковий — це безповоротна операція в межах одного інстансу.
|
|
93
|
+
|
|
94
|
+
### Метод `getExitCode()` (повертається з фабрики)
|
|
95
|
+
|
|
96
|
+
**Сигнатура:**
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
getExitCode(): number
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Параметри:** немає.
|
|
103
|
+
|
|
104
|
+
**Повертає:** `0` (якщо `fail` не викликали жодного разу) або `1` (якщо викликали хоча б один раз).
|
|
105
|
+
|
|
106
|
+
**Side effects:** немає.
|
|
107
|
+
|
|
108
|
+
**Чому функція, а не властивість:** значення `exitCode` зберігається в замиканні. Звичайне поле об’єкта закарбувало б початкове `0` і не відображало б подальших змін. Геттер-функція забезпечує «живе» зчитування актуального стану.
|
|
109
|
+
|
|
110
|
+
## Залежності
|
|
111
|
+
|
|
112
|
+
### Внутрішні (з репо)
|
|
113
|
+
|
|
114
|
+
- `../utils/pass.mjs` — імпортується named-імпорт `pass`. Це функція, що друкує рядок успіху в уніфікованому форматі. Для деталей формату див. документацію `pass.mjs`.
|
|
115
|
+
|
|
116
|
+
### Зовнішні (Node/runtime)
|
|
117
|
+
|
|
118
|
+
- Глобальний `console.log` — для виводу помилок у `fail()`.
|
|
119
|
+
|
|
120
|
+
### Без побічних залежностей
|
|
121
|
+
|
|
122
|
+
Модуль чистий від npm-пакетів, не звертається до файлової системи, мережі чи `process.exit` — повернений `getExitCode()` лише **повідомляє** код, але **не завершує** процес самостійно. Завершення процесу — відповідальність викликаючого скрипта.
|
|
123
|
+
|
|
124
|
+
## Потік виконання / Використання
|
|
125
|
+
|
|
126
|
+
### Типовий сценарій у check-скрипті
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
import { createCheckReporter } from '../lib/check-reporter.mjs'
|
|
130
|
+
|
|
131
|
+
const reporter = createCheckReporter()
|
|
132
|
+
|
|
133
|
+
if (somethingWrong) {
|
|
134
|
+
reporter.fail('Опис проблеми')
|
|
135
|
+
} else {
|
|
136
|
+
reporter.pass('Усе ок')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Важливо: викликати getExitCode() саме як функцію
|
|
140
|
+
process.exit(reporter.getExitCode())
|
|
141
|
+
// або: return reporter.getExitCode()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Як використовується в `lint-docker` та інших споживачах
|
|
145
|
+
|
|
146
|
+
Один інстанс `reporter` створюється на запуск перевірки. У циклі по ресурсах (файлах, правилах, конфігах тощо) виконуються виклики `pass` / `fail` залежно від результату. По завершенні — одноразовий `getExitCode()`, значення якого передається в `process.exit(...)` або повертається з `async`-функції-точки входу.
|
|
147
|
+
|
|
148
|
+
### Антипатерн (нерекомендовано)
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// ❌ НЕ робити так — exitCode не «знімається» цим способом,
|
|
152
|
+
// бо це не геттер-властивість, а замикання поверх локальної змінної.
|
|
153
|
+
const { exitCode } = reporter
|
|
154
|
+
process.exit(exitCode) // буде undefined
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Правильно:
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
process.exit(reporter.getExitCode())
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Семантика exit code
|
|
164
|
+
|
|
165
|
+
- `0` — усі перевірки пройшли (або жодного `fail` не було).
|
|
166
|
+
- `1` — щонайменше один `fail`. Подальші виклики `fail` не «накопичують» вище 1 — це бінарний прапорець «була помилка / не було».
|
|
167
|
+
|
|
168
|
+
### Формат виводу
|
|
169
|
+
|
|
170
|
+
- Успіх: формат повністю делегований `pass` з `../utils/pass.mjs`.
|
|
171
|
+
- Помилка: ` ❌ <msg>` (два пробіли + ❌ + пробіл + повідомлення), завжди через `console.log` (stdout, з переведенням рядка).
|
|
172
|
+
|
|
173
|
+
### Ізольованість інстансів
|
|
174
|
+
|
|
175
|
+
Кожен виклик `createCheckReporter()` повертає новий незалежний об’єкт із власним `exitCode`. Це означає, що в одному процесі можна тримати кілька паралельних/незалежних репортерів — наприклад, по одному на правило/секцію — і агрегувати їхні фінальні коди вручну (через `Math.max` чи логічне OR), якщо потрібно.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# discover-check-rules-from-cursor.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `discover-check-rules-from-cursor.mjs` визначає, які саме id правил повинні бути запущені командою `npx @nitra/cursor fix`, коли користувач викликає її **без аргументів**. Він узгоджує два джерела:
|
|
6
|
+
|
|
7
|
+
1. **Файлова система проєкту** — базові імена `*.mdc` у директорії `.cursor/rules/` (тобто правила, які реально присутні у конкретному репо).
|
|
8
|
+
2. **Реєстр пакета** — список id правил, для яких у пакеті `@nitra/cursor` існує programmatic перевірка (JS-концерн або policy з `target.json`); цей список зазвичай приходить із функції `discoverCheckableRules`.
|
|
9
|
+
|
|
10
|
+
Результат — впорядкований унікальний масив id, у якому:
|
|
11
|
+
|
|
12
|
+
- порядок диктується порядком файлів `*.mdc` у директорії правил (як їх передали ззовні, зазвичай відсортованими);
|
|
13
|
+
- залишаються лише ті id, для яких у пакеті є реальна перевірка;
|
|
14
|
+
- видалено префікс `n-`, оскільки внутрішня команда `check <id>` оперує «короткими» id (наприклад, `bun`, а не `n-bun`).
|
|
15
|
+
|
|
16
|
+
Файл містить чисті pure-функції без побічних ефектів — без `fs`, без `process`, без I/O. Все читання директорії правил і реєстру здійснюється викликачем; цей модуль лише поєднує два готових списки.
|
|
17
|
+
|
|
18
|
+
## Експорти / API
|
|
19
|
+
|
|
20
|
+
Модуль має три публічних експорти у форматі ES modules (`.mjs`):
|
|
21
|
+
|
|
22
|
+
| Експорт | Тип | Призначення |
|
|
23
|
+
| ----------------------------------- | ---------- | ------------------------------------------------------------------------------------------------ |
|
|
24
|
+
| `MANAGED_RULE_FILE_PREFIX` | `string` | Константа `'n-'` — префікс імен файлів `.mdc`, які вважаються «керованими» пакетом. |
|
|
25
|
+
| `mdcBasenameToCheckId` | `function` | Перетворює базове ім'я `*.mdc` (із розширенням або без) у id для CLI-команди `check`. |
|
|
26
|
+
| `discoverCheckRulesFromCursorRules` | `function` | Будує впорядкований список id перевірок як перетин «доступних у пакеті» та «присутніх на диску». |
|
|
27
|
+
|
|
28
|
+
Усі експорти — іменовані (`export const` / `export function`); default-export відсутній.
|
|
29
|
+
|
|
30
|
+
## Функції
|
|
31
|
+
|
|
32
|
+
### `mdcBasenameToCheckId(mdcBasename)`
|
|
33
|
+
|
|
34
|
+
**Сигнатура:**
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
export function mdcBasenameToCheckId(mdcBasename: string): string
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Параметри:**
|
|
41
|
+
|
|
42
|
+
- `mdcBasename` — рядок, який може бути:
|
|
43
|
+
- чистим базовим іменем файлу, наприклад `'n-bun.mdc'` або `'my-rule.mdc'`;
|
|
44
|
+
- відносним/абсолютним шляхом, наприклад `'.cursor/rules/n-bun.mdc'` (функція сама візьме сегмент після останнього `/`).
|
|
45
|
+
|
|
46
|
+
**Поведінка:**
|
|
47
|
+
|
|
48
|
+
1. Якщо у вхідному рядку є символ `/`, береться підрядок після останнього `/` — таким чином приймаються як «чисті» імена, так і шляхи.
|
|
49
|
+
2. Якщо отриманий рядок закінчується на `.mdc`, розширення відсікається.
|
|
50
|
+
3. Якщо те, що залишилось, починається з `MANAGED_RULE_FILE_PREFIX` (`'n-'`), цей префікс також знімається.
|
|
51
|
+
4. Повертається отриманий «короткий» id.
|
|
52
|
+
|
|
53
|
+
**Повертає:**
|
|
54
|
+
|
|
55
|
+
- `string` — id, придатний для команди `check <id>`. Приклади:
|
|
56
|
+
- `'n-bun.mdc'` → `'bun'`
|
|
57
|
+
- `'.cursor/rules/n-vue.mdc'` → `'vue'`
|
|
58
|
+
- `'my-rule.mdc'` → `'my-rule'`
|
|
59
|
+
- `'n-text'` (без розширення) → `'text'`
|
|
60
|
+
|
|
61
|
+
**Side effects:** немає. Функція суто детермінована, не звертається до файлової системи, не мутує аргументи.
|
|
62
|
+
|
|
63
|
+
**Крайні випадки:**
|
|
64
|
+
|
|
65
|
+
- Backslashes (`\\`) як роздільник шляху **не** розпізнаються — функція оперує лише `/`. Це безпечно для шляхів, які прийшли від утиліт на кшталт `fs.readdir`, що повертають базові імена, або з `path.posix`-стилю.
|
|
66
|
+
- Якщо вхід уже без `.mdc`, він обробляється тим самим алгоритмом (просто пропускається крок зняття розширення).
|
|
67
|
+
- Якщо файл, прибравши `.mdc`, дорівнює саме `'n-'`, повернеться порожній рядок — це не очікувана реальна ситуація, бо керованих правил із порожнім id не буває.
|
|
68
|
+
|
|
69
|
+
### `discoverCheckRulesFromCursorRules(available, mdcBasenames)`
|
|
70
|
+
|
|
71
|
+
**Сигнатура:**
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
export function discoverCheckRulesFromCursorRules(
|
|
75
|
+
available: string[],
|
|
76
|
+
mdcBasenames: string[],
|
|
77
|
+
): string[]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Параметри:**
|
|
81
|
+
|
|
82
|
+
- `available` — масив id, які пакет уміє перевіряти (програмні перевірки JS-концерну або policy з `target.json`). Зазвичай — результат `discoverCheckableRules` із сусіднього модуля `discover-checkable-rules.mjs`. Очікується алфавітний порядок, але алгоритм не залежить від нього.
|
|
83
|
+
- `mdcBasenames` — масив базових імен файлів `*.mdc`, які реально присутні у `.cursor/rules/` поточного проєкту. Очікується, що масив уже відсортований викликачем (порядок цього масиву визначає порядок результату).
|
|
84
|
+
|
|
85
|
+
**Поведінка:**
|
|
86
|
+
|
|
87
|
+
1. Створюється `Set` `seen` для дедуплікації результату.
|
|
88
|
+
2. Створюється масив `ordered` для збереження порядку входу `mdcBasenames`.
|
|
89
|
+
3. Для кожного `basename`:
|
|
90
|
+
- обчислюється id через `mdcBasenameToCheckId(basename)`;
|
|
91
|
+
- якщо id присутній у `available` **і** ще не доданий — додається в `ordered` та позначається у `seen`.
|
|
92
|
+
4. Повертається `ordered`.
|
|
93
|
+
|
|
94
|
+
**Повертає:**
|
|
95
|
+
|
|
96
|
+
- `string[]` — впорядкований масив унікальних id перевірок:
|
|
97
|
+
- **тільки** ті, що одночасно (а) мають programmatic-перевірку в пакеті і (б) представлені файлом `*.mdc` у `.cursor/rules/`;
|
|
98
|
+
- порядок диктується `mdcBasenames` (тобто реальним порядком файлів у директорії, як його передав викликач);
|
|
99
|
+
- кожен id зустрічається не більше одного разу.
|
|
100
|
+
|
|
101
|
+
**Side effects:** немає. Функція pure, не читає диск, не мутує аргументи (`Set`/`Array` створюються локально).
|
|
102
|
+
|
|
103
|
+
**Крайні випадки:**
|
|
104
|
+
|
|
105
|
+
- Якщо `mdcBasenames` порожній — повертається `[]`.
|
|
106
|
+
- Якщо жоден `id`, отриманий з імен `.mdc`, не присутній у `available` — повертається `[]` (типово для проєктів, де `.cursor/rules/` містить лише сторонні правила без programmatic-перевірок у пакеті).
|
|
107
|
+
- Дублікати у `mdcBasenames` (наприклад, два файли, що дають один і той самий id після відсікання префікса) включаються лише один раз.
|
|
108
|
+
|
|
109
|
+
## Залежності
|
|
110
|
+
|
|
111
|
+
- **Зовнішні (npm/Node):** немає. Файл не використовує жодних імпортів — ні з Node core (`fs`, `path` тощо), ні з npm-пакетів.
|
|
112
|
+
- **Внутрішні (проєкт):** немає прямих імпортів. Очікується, що:
|
|
113
|
+
- аргумент `available` формується сусіднім модулем `discover-checkable-rules.mjs` (експорт `discoverCheckableRules`);
|
|
114
|
+
- аргумент `mdcBasenames` формується викликачем читанням `.cursor/rules/` (через `fs.readdirSync`/`fs.promises.readdir` із подальшим фільтром `.endsWith('.mdc')` та сортуванням).
|
|
115
|
+
|
|
116
|
+
Така архітектура свідомо відокремлює I/O від чистої логіки: цей модуль легко тестувати без файлової системи.
|
|
117
|
+
|
|
118
|
+
## Потік виконання / Використання
|
|
119
|
+
|
|
120
|
+
Типовий сценарій — `npx @nitra/cursor fix` без аргументів. CLI-входу потрібно вирішити, які id перевірок запустити за замовчуванням; для цього він:
|
|
121
|
+
|
|
122
|
+
1. Отримує список програмно перевірюваних правил пакета (`available = discoverCheckableRules(...)`).
|
|
123
|
+
2. Читає директорію `.cursor/rules/` поточного проєкту й відбирає файли з розширенням `.mdc` (`mdcBasenames`), сортує їх.
|
|
124
|
+
3. Викликає `discoverCheckRulesFromCursorRules(available, mdcBasenames)` і отримує **підсумковий** список id.
|
|
125
|
+
4. Для кожного id послідовно запускає команду `check <id>` (наприклад, через `run-rule-cli.mjs`).
|
|
126
|
+
|
|
127
|
+
Приклад інлайн-використання (псевдокод викликача):
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
import { readdirSync } from 'node:fs'
|
|
131
|
+
import { discoverCheckableRules } from './discover-checkable-rules.mjs'
|
|
132
|
+
import { discoverCheckRulesFromCursorRules } from './discover-check-rules-from-cursor.mjs'
|
|
133
|
+
|
|
134
|
+
const available = discoverCheckableRules(/* … */)
|
|
135
|
+
const mdcBasenames = readdirSync('.cursor/rules')
|
|
136
|
+
.filter(f => f.endsWith('.mdc'))
|
|
137
|
+
.sort()
|
|
138
|
+
const idsToRun = discoverCheckRulesFromCursorRules(available, mdcBasenames)
|
|
139
|
+
// idsToRun: наприклад, ['adr', 'bun', 'ci4', 'text', 'vue']
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Приклад трансформації імен:
|
|
143
|
+
|
|
144
|
+
| Файл у `.cursor/rules/` | id після `mdcBasenameToCheckId` | Потрапляє у результат? |
|
|
145
|
+
| -------------------------- | ------------------------------- | -------------------------------------------------- |
|
|
146
|
+
| `n-bun.mdc` | `bun` | Так, якщо `available.includes('bun')` |
|
|
147
|
+
| `n-vue.mdc` | `vue` | Так, якщо `available.includes('vue')` |
|
|
148
|
+
| `my-rule.mdc` | `my-rule` | Лише якщо пакет реєструє `my-rule` як перевірюване |
|
|
149
|
+
| `.cursor/rules/n-text.mdc` | `text` | Так, якщо `available.includes('text')` |
|
|
150
|
+
| `n-bun.mdc` (двічі) | `bun` | У результаті — один раз |
|
|
151
|
+
|
|
152
|
+
Таким чином модуль є тонким, але критичним «клейовим» шаром між:
|
|
153
|
+
|
|
154
|
+
- **диском проєкту** (що користувач реально хоче перевіряти, судячи з вмісту `.cursor/rules/`)
|
|
155
|
+
- **реєстром пакета** (що пакет фізично вміє перевіряти автоматично).
|
|
156
|
+
|
|
157
|
+
Файл свідомо тримається без I/O і без зовнішніх залежностей, щоб залишатись повністю детермінованим і легко покритим юніт-тестами (див. сусідню директорію `tests/`).
|