@nitra/cursor 3.22.0 → 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 +31 -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-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/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,259 @@
|
|
|
1
|
+
# hooks.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `npm/rules/adr/js/hooks.mjs` — це скрипт-перевірник (check) правила `adr.mdc` пакета `@nitra/cursor`. Його завдання — переконатися, що в репозиторії-споживачі правильно встановлені та підтримуються ADR (Architecture Decision Records) Stop-hook'и для двох LLM-середовищ: Claude Code та Cursor Agent.
|
|
6
|
+
|
|
7
|
+
Skрипт перевіряє, що:
|
|
8
|
+
|
|
9
|
+
- Канонічні bash-скрипти `capture-decisions.sh` і `normalize-decisions.sh` присутні у `.claude/hooks/` цільового репо й байт-у-байт збігаються з версіями, які поставляє npm-пакет у `.claude-template/hooks/` (sync керує файлами повністю — будь-яке локальне редагування фіксується як `fail`).
|
|
10
|
+
- Файл `.claude/settings.json` (project-shared конфіг Claude Code) існує — глибша валідація `hooks.Stop[]` робиться окремими policy-правилами (`npm/policy/adr/settings_json/`, `npm/policy/adr/settings_local_json/`).
|
|
11
|
+
- Файл `.cursor/hooks.json` валідний JSON і містить у `hooks.stop[]` entries з `command`, що посилаються на обидва managed-скрипти (це дозволяє Cursor Agent також запускати ADR capture/normalize по `stop`-події).
|
|
12
|
+
- `.gitignore` у корені покриває лог-файли `.claude/hooks/capture-decisions.log` і `.claude/hooks/normalize-decisions.log`, інакше runtime-логи потраплятимуть у git.
|
|
13
|
+
- Наявність LLM CLI (`claude` або `cursor-agent`) у `PATH` — це **інформативна** перевірка (warning через `pass`-меседж), бо хук без CLI просто мовчки no-op'ає, а не фейлиться.
|
|
14
|
+
|
|
15
|
+
Модуль експортує єдину асинхронну функцію `check(cwd)`, яка повертає numeric exit-code (0 = OK, 1 = є проблеми). Усе зведення результатів і форматування проходить через `createCheckReporter()` з `npm/scripts/lib/check-reporter.mjs`.
|
|
16
|
+
|
|
17
|
+
## Експорти / API
|
|
18
|
+
|
|
19
|
+
| Назва | Тип | Призначення |
|
|
20
|
+
| ----- | --- | ----------- |
|
|
21
|
+
| `check` | `async (cwd?: string) => Promise<number>` | Єдиний публічний експорт. Запускає весь набір перевірок правила `adr.mdc` для дерева репозиторію за шляхом `cwd` (default — `process.cwd()`). Повертає 0 при повному success або 1, якщо принаймні одна перевірка зафейлилась. |
|
|
22
|
+
|
|
23
|
+
Усі інші ідентифікатори файлу (`HOOK_ARTIFACTS`, `PROJECT_SETTINGS_REL`, `CURSOR_HOOKS_REL`, `EOL_RE`, `BUNDLED_HOOKS_DIR`, `projectHookPath`, `projectLogPath`, `gitignoreLineCoversHookLog`, `checkHookScript`, `checkProjectSettings`, `readJsonSafe`, `cursorConfigHasStopHook`, `checkCursorHooks`, `checkGitignoreForLog`, `checkGitignore`, `isBinaryInPath`, `checkLlmCliAvailable`) — внутрішні (module-private).
|
|
24
|
+
|
|
25
|
+
## Функції
|
|
26
|
+
|
|
27
|
+
### `projectHookPath(scriptName)`
|
|
28
|
+
|
|
29
|
+
- **Сигнатура:** `(scriptName: string) => string`
|
|
30
|
+
- **Параметри:**
|
|
31
|
+
- `scriptName` — базове ім'я hook-скрипта (наприклад `capture-decisions.sh`).
|
|
32
|
+
- **Повертає:** відносний шлях вигляду `.claude/hooks/<scriptName>`.
|
|
33
|
+
- **Side effects:** немає (чиста функція конкатенації шляху).
|
|
34
|
+
|
|
35
|
+
### `projectLogPath(logName)`
|
|
36
|
+
|
|
37
|
+
- **Сигнатура:** `(logName: string) => string`
|
|
38
|
+
- **Параметри:**
|
|
39
|
+
- `logName` — базове ім'я лог-файлу (наприклад `capture-decisions.log`).
|
|
40
|
+
- **Повертає:** відносний шлях вигляду `.claude/hooks/<logName>`.
|
|
41
|
+
- **Side effects:** немає.
|
|
42
|
+
|
|
43
|
+
### `gitignoreLineCoversHookLog(line, logPath)`
|
|
44
|
+
|
|
45
|
+
- **Сигнатура:** `(line: string, logPath: string) => boolean`
|
|
46
|
+
- **Параметри:**
|
|
47
|
+
- `line` — одна нормалізована (trim) лінія з `.gitignore`.
|
|
48
|
+
- `logPath` — шлях до конкретного лог-файлу, який треба покрити.
|
|
49
|
+
- **Повертає:** `true`, якщо рядок покриває цей лог. Підтримує матчі:
|
|
50
|
+
- точний шлях (`logPath`);
|
|
51
|
+
- glob `.claude/hooks/*.log` або `.claude/hooks/**/*.log`;
|
|
52
|
+
- широкий glob `*.log` або `**/*.log`.
|
|
53
|
+
- Порожні рядки та коментарі (`#…`) ігноруються.
|
|
54
|
+
- **Side effects:** немає.
|
|
55
|
+
|
|
56
|
+
### `checkHookScript(reporter, cwd, scriptName)`
|
|
57
|
+
|
|
58
|
+
- **Сигнатура:** `async (reporter: CheckReporter, cwd: string, scriptName: string) => Promise<void>`
|
|
59
|
+
- **Параметри:**
|
|
60
|
+
- `reporter` — інстанс, створений `createCheckReporter()`; з нього використовуються `pass` і `fail`.
|
|
61
|
+
- `cwd` — корінь репозиторію-споживача.
|
|
62
|
+
- `scriptName` — базове ім'я hook-скрипта.
|
|
63
|
+
- **Повертає:** нічого (звітує результат через reporter).
|
|
64
|
+
- **Поведінка:**
|
|
65
|
+
1. Якщо `<cwd>/.claude/hooks/<scriptName>` не існує → `fail` з підказкою запустити `npx @nitra/cursor`.
|
|
66
|
+
2. Якщо канонічний скрипт у пакеті (`<пакет>/.claude-template/hooks/<scriptName>`, обчислюється від `import.meta.url`) не існує → `fail` про перевстановлення `@nitra/cursor`.
|
|
67
|
+
3. Інакше читає обидва файли паралельно через `Promise.all`+`readFile` і порівнює як рядки UTF-8. При повному збігу — `pass`, інакше — `fail` із підказкою про повторний sync.
|
|
68
|
+
- **Side effects:** файлова система — `existsSync`, `readFile`. Мутує внутрішній стан репортера.
|
|
69
|
+
|
|
70
|
+
### `checkProjectSettings(reporter, cwd)`
|
|
71
|
+
|
|
72
|
+
- **Сигнатура:** `(reporter: CheckReporter, cwd: string) => void`
|
|
73
|
+
- **Параметри:**
|
|
74
|
+
- `reporter` — репортер для збору результату.
|
|
75
|
+
- `cwd` — корінь репозиторію.
|
|
76
|
+
- **Повертає:** нічого.
|
|
77
|
+
- **Поведінка:** перевіряє лише факт наявності `.claude/settings.json` (`existsSync`). `pass`, якщо файл є, інакше — `fail` з підказкою про `npx @nitra/cursor`. Глибша валідація структури `hooks.Stop[]` навмисно винесена в policy-правила `adr.settings_json` і `adr.settings_local_json`.
|
|
78
|
+
- **Side effects:** одне `existsSync`.
|
|
79
|
+
|
|
80
|
+
### `readJsonSafe(path)`
|
|
81
|
+
|
|
82
|
+
- **Сигнатура:** `async (path: string) => Promise<unknown | null>`
|
|
83
|
+
- **Параметри:**
|
|
84
|
+
- `path` — шлях до JSON-файлу.
|
|
85
|
+
- **Повертає:** результат `JSON.parse(await readFile(path, 'utf8'))` або `null`, якщо читання чи парсинг кинули виняток.
|
|
86
|
+
- **Side effects:** читання файлу; виняток придушується try/catch і конвертується у `null`.
|
|
87
|
+
|
|
88
|
+
### `cursorConfigHasStopHook(config, marker)`
|
|
89
|
+
|
|
90
|
+
- **Сигнатура:** `(config: unknown, marker: string) => boolean`
|
|
91
|
+
- **Параметри:**
|
|
92
|
+
- `config` — попередньо розпарсений вміст `.cursor/hooks.json`.
|
|
93
|
+
- `marker` — підрядок, який має зустрічатися в `command` шуканого entry (зазвичай — шлях до managed hook-скрипта, наприклад `.claude/hooks/capture-decisions.sh`).
|
|
94
|
+
- **Повертає:** `true`, якщо у `config.hooks.stop` є щонайменше один елемент-об'єкт з рядковим `command`, який містить `marker`. Кожен крок обходу (`config` — об'єкт-не-масив, `hooks` — об'єкт-не-масив, `stop` — масив) явно валідовано, тож не-канонічна структура повертає `false` без винятків.
|
|
95
|
+
- **Side effects:** немає.
|
|
96
|
+
|
|
97
|
+
### `checkCursorHooks(reporter, cwd)`
|
|
98
|
+
|
|
99
|
+
- **Сигнатура:** `async (reporter: CheckReporter, cwd: string) => Promise<void>`
|
|
100
|
+
- **Параметри:**
|
|
101
|
+
- `reporter` — репортер.
|
|
102
|
+
- `cwd` — корінь репо.
|
|
103
|
+
- **Повертає:** нічого.
|
|
104
|
+
- **Поведінка:**
|
|
105
|
+
1. Якщо `.cursor/hooks.json` не існує — `fail` (запропонувати `npx @nitra/cursor`).
|
|
106
|
+
2. Інакше парсить через `readJsonSafe`. Якщо повернуло `null` — `fail` ("не парситься як JSON").
|
|
107
|
+
3. Для кожного елемента `HOOK_ARTIFACTS` обчислює marker через `projectHookPath(scriptName)` і шукає stop-entry за допомогою `cursorConfigHasStopHook`. На кожен скрипт — окремий `pass` або `fail`.
|
|
108
|
+
- **Side effects:** читання файлу, мутація стану репортера.
|
|
109
|
+
|
|
110
|
+
### `checkGitignoreForLog(reporter, logName, gitignoreContent)`
|
|
111
|
+
|
|
112
|
+
- **Сигнатура:** `(reporter: CheckReporter, logName: string, gitignoreContent: string) => void`
|
|
113
|
+
- **Параметри:**
|
|
114
|
+
- `reporter` — репортер.
|
|
115
|
+
- `logName` — базове ім'я лог-файлу.
|
|
116
|
+
- `gitignoreContent` — наперед прочитаний вміст `.gitignore` (передається ззовні, щоб не читати файл двічі).
|
|
117
|
+
- **Повертає:** нічого.
|
|
118
|
+
- **Поведінка:** розбиває контент по `EOL_RE` (`\r?\n`), тримує кожну лінію та перевіряє через `gitignoreLineCoversHookLog`. Якщо хоч одна лінія покриває шлях — `pass`, інакше — `fail` з підказкою додати рядок.
|
|
119
|
+
- **Side effects:** мутація стану репортера.
|
|
120
|
+
|
|
121
|
+
### `checkGitignore(reporter, cwd)`
|
|
122
|
+
|
|
123
|
+
- **Сигнатура:** `async (reporter: CheckReporter, cwd: string) => Promise<void>`
|
|
124
|
+
- **Параметри:**
|
|
125
|
+
- `reporter` — репортер.
|
|
126
|
+
- `cwd` — корінь репо.
|
|
127
|
+
- **Повертає:** нічого.
|
|
128
|
+
- **Поведінка:**
|
|
129
|
+
1. Якщо `.gitignore` у корені відсутній — викидає по `fail` на кожен `HOOK_ARTIFACTS.logName` (один fail для кожного логу окремо, а не один загальний).
|
|
130
|
+
2. Інакше один раз читає файл і прокручує `HOOK_ARTIFACTS`, делегуючи кожен лог у `checkGitignoreForLog`.
|
|
131
|
+
- **Side effects:** одне `readFile`, кілька викликів `fail`/`pass` через репортер.
|
|
132
|
+
|
|
133
|
+
### `isBinaryInPath(name)`
|
|
134
|
+
|
|
135
|
+
- **Сигнатура:** `(name: string) => boolean`
|
|
136
|
+
- **Параметри:**
|
|
137
|
+
- `name` — ім'я бінарника без розширення.
|
|
138
|
+
- **Повертає:** `true`, якщо у каталогах `process.env.PATH` (розділених `path.delimiter`) знайдено файл з таким іменем. Чек робиться через `existsSync(join(dir, name))` — без виклику `spawn`/`child_process`, тому не залежить від executable-біта і працює як легкий `which`.
|
|
139
|
+
- **Side effects:** читання env через `node:process` + `existsSync` на кожен каталог `PATH`.
|
|
140
|
+
|
|
141
|
+
### `checkLlmCliAvailable(reporter)`
|
|
142
|
+
|
|
143
|
+
- **Сигнатура:** `(reporter: CheckReporter) => void`
|
|
144
|
+
- **Параметри:**
|
|
145
|
+
- `reporter` — репортер.
|
|
146
|
+
- **Повертає:** нічого.
|
|
147
|
+
- **Поведінка:** перевіряє `isBinaryInPath('claude')` та `isBinaryInPath('cursor-agent')`. Виводить **завжди `pass`** з одним із чотирьох повідомлень (обидва знайдено / лише `claude` / лише `cursor-agent` / жодного). Це навмисний дизайн — відсутність CLI означає, що hook просто no-op'ає й не валить білд.
|
|
148
|
+
- **Side effects:** мутація стану репортера (тільки `pass`-меседжі).
|
|
149
|
+
|
|
150
|
+
### `check(cwd?)` *(експорт)*
|
|
151
|
+
|
|
152
|
+
- **Сигнатура:** `async (cwd?: string) => Promise<number>`
|
|
153
|
+
- **Параметри:**
|
|
154
|
+
- `cwd` — корінь репо. За замовчуванням — `process.cwd()`.
|
|
155
|
+
- **Повертає:** exit-code зі `reporter.getExitCode()` (0 або 1).
|
|
156
|
+
- **Поведінка (порядок виконання):**
|
|
157
|
+
1. Створює свіжий репортер через `createCheckReporter()`.
|
|
158
|
+
2. Послідовно (через `for…of` + `await`) для кожного `HOOK_ARTIFACTS` запускає `checkHookScript`.
|
|
159
|
+
3. Викликає `checkProjectSettings` (синхронно).
|
|
160
|
+
4. Викликає `await checkCursorHooks`.
|
|
161
|
+
5. Викликає `await checkGitignore`.
|
|
162
|
+
6. Викликає `checkLlmCliAvailable` (синхронно).
|
|
163
|
+
7. Повертає `reporter.getExitCode()`.
|
|
164
|
+
- **Side effects:** усі ті, що зведено у внутрішніх перевірках (FS read, env read).
|
|
165
|
+
|
|
166
|
+
## Константи модуля
|
|
167
|
+
|
|
168
|
+
| Ім'я | Тип/значення | Призначення |
|
|
169
|
+
| ---- | ------------ | ----------- |
|
|
170
|
+
| `HOOK_ARTIFACTS` | `readonly [{ scriptName, logName }, …]` | Перелік hook-артефактів (`capture-decisions.sh`+`.log`, `normalize-decisions.sh`+`.log`), які перевіряються однотипно. `as const` через JSDoc-каст `/** @type {const} */`. |
|
|
171
|
+
| `PROJECT_SETTINGS_REL` | `'.claude/settings.json'` | Project-shared конфіг Claude Code. |
|
|
172
|
+
| `CURSOR_HOOKS_REL` | `'.cursor/hooks.json'` | Конфіг Cursor Agent. |
|
|
173
|
+
| `EOL_RE` | `/\r?\n/u` | Регекс для split рядків `.gitignore` (підтримує LF і CRLF). |
|
|
174
|
+
| `here` | `string` | Каталог цього модуля, обчислений через `fileURLToPath(import.meta.url)` + `dirname`. |
|
|
175
|
+
| `BUNDLED_HOOKS_DIR` | `string` | Абсолютний шлях до `.claude-template/hooks/` усередині пакета `@nitra/cursor`: `<here>/../../../.claude-template/hooks`. Використовується як джерело канонічних скриптів. |
|
|
176
|
+
|
|
177
|
+
## Залежності
|
|
178
|
+
|
|
179
|
+
### Node.js builtins
|
|
180
|
+
|
|
181
|
+
- `node:fs` → `existsSync` — синхронна перевірка наявності файлу.
|
|
182
|
+
- `node:fs/promises` → `readFile` — асинхронне читання UTF-8 контенту.
|
|
183
|
+
- `node:path` → `delimiter`, `dirname`, `join` — крос-платформне склеювання шляхів і split `PATH`.
|
|
184
|
+
- `node:process` → `env` — доступ до `PATH` для пошуку LLM CLI.
|
|
185
|
+
- `node:url` → `fileURLToPath` — конверсія `import.meta.url` у локальний шлях для обчислення `BUNDLED_HOOKS_DIR`.
|
|
186
|
+
|
|
187
|
+
### Внутрішні модулі пакета
|
|
188
|
+
|
|
189
|
+
- `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — фабрика репортера з API `{ pass(msg), fail(msg), getExitCode() }`. Усі результати модуля проходять через нього.
|
|
190
|
+
|
|
191
|
+
### Зовнішні файли репо-споживача (читаються, не модифікуються)
|
|
192
|
+
|
|
193
|
+
- `<cwd>/.claude/hooks/capture-decisions.sh`, `<cwd>/.claude/hooks/normalize-decisions.sh` — managed bash-скрипти.
|
|
194
|
+
- `<cwd>/.claude/settings.json` — project-shared Claude config.
|
|
195
|
+
- `<cwd>/.cursor/hooks.json` — Cursor Agent hooks config.
|
|
196
|
+
- `<cwd>/.gitignore` — для перевірки покриття лог-файлів.
|
|
197
|
+
|
|
198
|
+
### Файли пакета (джерело істини для diff)
|
|
199
|
+
|
|
200
|
+
- `<package-root>/.claude-template/hooks/capture-decisions.sh`
|
|
201
|
+
- `<package-root>/.claude-template/hooks/normalize-decisions.sh`
|
|
202
|
+
|
|
203
|
+
Шлях обчислюється статично від `import.meta.url` — модуль не залежить від `process.cwd()` для пошуку bundled-файлів.
|
|
204
|
+
|
|
205
|
+
## Потік виконання / Використання
|
|
206
|
+
|
|
207
|
+
### Як модуль викликається
|
|
208
|
+
|
|
209
|
+
Скрипт — частина уніфікованої check-системи `@nitra/cursor`. Зовнішній рантайм (`npx @nitra/cursor check` або еквівалент у тестах) імпортує функцію `check` і викликає її:
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
import { check } from '@nitra/cursor/rules/adr/js/hooks.mjs'
|
|
213
|
+
|
|
214
|
+
const exitCode = await check(process.cwd())
|
|
215
|
+
process.exit(exitCode)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Порядок перевірок усередині `check`
|
|
219
|
+
|
|
220
|
+
1. **Канонічність bash-скриптів** — для кожного з `HOOK_ARTIFACTS`:
|
|
221
|
+
- FS-existence cцільового файлу;
|
|
222
|
+
- FS-existence канонічного файлу в пакеті;
|
|
223
|
+
- byte-exact diff обох контентів.
|
|
224
|
+
2. **`.claude/settings.json` existence** — sanity-check, що project-shared конфіг є.
|
|
225
|
+
3. **`.cursor/hooks.json`** — JSON parsing + наявність `hooks.stop[].command` markers для обох scriptName.
|
|
226
|
+
4. **`.gitignore` log-coverage** — для кожного логу шукається покривна лінія (точна, scoped-glob або wide-glob).
|
|
227
|
+
5. **LLM CLI availability** — інформативна перевірка `claude`/`cursor-agent` у `PATH`; **не валить exit code**.
|
|
228
|
+
|
|
229
|
+
### Як інтерпретувати результат
|
|
230
|
+
|
|
231
|
+
- **Return `0`** — усі обов'язкові перевірки пройшли; LLM-CLI-секція могла видати warning, але це нормально.
|
|
232
|
+
- **Return `1`** — хоч одна `fail`-перевірка. Усі повідомлення `fail` містять actionable-підказку (зазвичай `npx @nitra/cursor` або конкретний рядок для `.gitignore`).
|
|
233
|
+
|
|
234
|
+
### Типові сценарії регресії
|
|
235
|
+
|
|
236
|
+
- Локальний редактор bash-скрипта → `checkHookScript` зафейлить byte-diff.
|
|
237
|
+
- Видалення `.cursor/hooks.json` чи поламана структура → `checkCursorHooks` повідомить через JSON-error або відсутність marker.
|
|
238
|
+
- Перенесення hook entry в `settings.local.json` → деталі ловить policy-правило, але `checkProjectSettings` гарантує, що project-shared файл хоча б присутній.
|
|
239
|
+
- Відсутність `.gitignore`-покриття для логів → багато `fail`-меседжів з конкретним рядком, що треба додати.
|
|
240
|
+
- Відсутність LLM CLI → `pass` із warning-меседжем, але `exit 0` (hook буде no-op'ати).
|
|
241
|
+
|
|
242
|
+
### Контекстні нюанси
|
|
243
|
+
|
|
244
|
+
- Усі шляхи в `fail`/`pass`-меседжах — відносні до `cwd` (не абсолютні), щоб output був стабільний у CI та однаковий між машинами.
|
|
245
|
+
- `readJsonSafe` свідомо приглушує помилки парсингу — рішення про "JSON broken" приймається в `checkCursorHooks` через `config === null`.
|
|
246
|
+
- `cursorConfigHasStopHook` робить точкову, але повну валідацію типів, бо `.cursor/hooks.json` керується одночасно автоматичним sync і людьми — не можна вважати, що структура завжди канонічна.
|
|
247
|
+
- `BUNDLED_HOOKS_DIR` обчислений `dirname(fileURLToPath(import.meta.url))` + три `..` — цей розрахунок чутливий до **переміщень файлу всередині пакета**: якщо змінити structure `npm/rules/adr/js/`, треба синхронно правити кількість `..` сегментів.
|
|
248
|
+
|
|
249
|
+
## Rebuild Test
|
|
250
|
+
|
|
251
|
+
Якщо файл `hooks.mjs` загубити, його можна відновити з цієї документації, дотримуючись таких інваріантів:
|
|
252
|
+
|
|
253
|
+
- Один експорт `async function check(cwd = process.cwd()): Promise<number>`.
|
|
254
|
+
- Внутрішні константи `HOOK_ARTIFACTS`, `PROJECT_SETTINGS_REL`, `CURSOR_HOOKS_REL`, `EOL_RE`, `BUNDLED_HOOKS_DIR` мають описані значення.
|
|
255
|
+
- Усі перевірки звітують через `createCheckReporter()` з `npm/scripts/lib/check-reporter.mjs`.
|
|
256
|
+
- Канонічні bash-скрипти беруться з `<тут>/../../../.claude-template/hooks/` (три `..` від каталогу модуля).
|
|
257
|
+
- `.gitignore`-матч підтримує точний шлях + `.claude/hooks/*.log` + `.claude/hooks/**/*.log` + `*.log` + `**/*.log`, ігнорує порожні й коментарі.
|
|
258
|
+
- LLM-CLI-перевірка завжди завершується `pass` — exit-code від неї не залежить.
|
|
259
|
+
- Порядок викликів усередині `check` має значення для читабельного output: hook-скрипти → settings → cursor hooks → gitignore → llm cli.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# `npm/rules/bun/fix.mjs`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл є точкою входу для правила `bun` у системі правил `@nitra/cursor`. Він виконує дві ролі одночасно:
|
|
6
|
+
|
|
7
|
+
1. **Library mode** — експортує функцію `run(ctx)`, яку CLI-оркестрація викликає через динамічний `import(...)`, щоб виконати правило в межах загального батч-прогону всіх правил.
|
|
8
|
+
2. **Standalone mode** — якщо файл запущено напряму через `bun rules/bun/fix.mjs`, він повністю емулює виклик `npx @nitra/cursor fix bun` (з конфіг-завантаженням, whitelist-фільтрацією та фінальним summary) і завершує процес кодом виходу, придатним для CI/IDE.
|
|
9
|
+
|
|
10
|
+
Власної логіки перевірки/виправлення файл не містить — він делегує роботу стандартному пайплайну правил `runStandardRule`, який послідовно прогонює фази `applies → JS-concerns → policy → mdc-refs` на основі сусідніх файлів правила (`meta.json`, `bun.mdc`, тек `js/`, `policy/`).
|
|
11
|
+
|
|
12
|
+
Цей патерн ідентичний у всіх однотипних правил `npm/rules/<id>/fix.mjs` — фактично це тонкий «диспатчер», який прив’язує конкретне правило (визначене теками-сусідами через `import.meta.dirname`) до загальної інфраструктури запуску.
|
|
13
|
+
|
|
14
|
+
## Експорти / API
|
|
15
|
+
|
|
16
|
+
| Експорт | Тип | Опис |
|
|
17
|
+
| ------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `run` | `function(ctx?: RuleContext): Promise<number>` | Іменований експорт. Точка входу в library-режимі: викликається оркестратором CLI через ESM `import` після динамічного резолву шляху до файлу правила. |
|
|
19
|
+
|
|
20
|
+
Default-експорту немає. Окрім `run`, файл має **top-level side-effect**: при виконанні в standalone-режимі (див. нижче) виконує `process.exit(...)` ще до того, як модуль завершить ініціалізацію.
|
|
21
|
+
|
|
22
|
+
## Функції
|
|
23
|
+
|
|
24
|
+
### `run(ctx)`
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
export function run(ctx) {
|
|
28
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **Сигнатура:** `run(ctx?: RuleContext): Promise<number>`
|
|
33
|
+
- **Параметри:**
|
|
34
|
+
- `ctx` — необов’язковий об’єкт контексту прогону, тип `RuleContext` із модуля `../../scripts/lib/run-standard-rule.mjs`. Передається оркестратором, коли кілька правил виконуються в одному батчі та потребують спільного стану (наприклад, `walkCache` — кеш обходу файлової системи, щоб не повторювати `fs.readdir` для тих самих директорій). Якщо `undefined`, `runStandardRule` створює власний контекст за замовчуванням.
|
|
35
|
+
- **Повертає:** `Promise<number>` з кодом виходу:
|
|
36
|
+
- `0` — правило не знайшло порушень (OK);
|
|
37
|
+
- `1` — знайдено порушення (правило завершилося з помилками).
|
|
38
|
+
- **Алгоритм:** єдине виконуване твердження — `return runStandardRule(import.meta.dirname, ctx)`. Тут `import.meta.dirname` — абсолютний шлях до теки правила (`npm/rules/bun/`); саме на основі цього шляху `runStandardRule` визначає ідентифікатор правила (остання сегментна назва — `bun`) та підвантажує сусідні артефакти (`meta.json`, `bun.mdc`, скрипти з `js/` та `policy/`).
|
|
39
|
+
- **Side effects:**
|
|
40
|
+
- Делеговані до `runStandardRule`: читання конфіг-файлу, обхід дерева проєкту згідно з applies-фільтрами, можливі модифікації цільових файлів у фазі JS-concerns / policy, запис лог-повідомлень у stdout.
|
|
41
|
+
- Сам `run` не звертається до `process.exit`, не змінює `process.env` і не змінює глобальний стан — повертає чистий `Promise`, який повністю керується викликачем.
|
|
42
|
+
|
|
43
|
+
### Standalone-блок (top-level)
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
if (isRunAsCli(import.meta.url)) {
|
|
47
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **Що це:** не функція, а IIFE-подібний top-level guard. Виконується один раз на завантаженні модуля.
|
|
52
|
+
- **Умова:** `isRunAsCli(import.meta.url)` повертає `true`, лише коли цей файл стартовано напряму як entry-point (а не імпортовано як модуль із оркестратора). Перевірка зазвичай зводиться до порівняння `import.meta.url` з `pathToFileURL(process.argv[1])`.
|
|
53
|
+
- **Дія:** викликає `runRuleCli(import.meta.dirname)`, дочікується резолву та передає результат у `process.exit(...)`. На відміну від `run`, тут запускається повний CLI-pipeline (а не лише стандартне правило): завантаження користувацького конфігу, фільтр whitelist, фінальний summary та форматування виводу.
|
|
54
|
+
- **Side effects:**
|
|
55
|
+
- Завершує процес Node/Bun із кодом виходу `0` або `1`.
|
|
56
|
+
- У файлі присутні директиви `eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit` — це свідомий виняток для standalone-точок входу, які повинні повертати exit-code для CI та IDE.
|
|
57
|
+
- Використано top-level `await`, тому модуль працює лише в ESM-середовищі з підтримкою TLA (Bun, Node.js 14.8+ у ESM-режимі).
|
|
58
|
+
|
|
59
|
+
## Залежності
|
|
60
|
+
|
|
61
|
+
### Внутрішні (relative imports)
|
|
62
|
+
|
|
63
|
+
| Модуль | Імпорти | Призначення |
|
|
64
|
+
| ----------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
65
|
+
| `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` | Утиліти для standalone-режиму: визначити, чи файл запущено як CLI, та виконати повноцінний CLI-pipeline для одного правила. |
|
|
66
|
+
| `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule` | Стандартний рантайм правила: послідовність фаз `applies → JS-concerns → policy → mdc-refs`. Також експортує тип `RuleContext`, на який посилається JSDoc-анотація `run`. |
|
|
67
|
+
|
|
68
|
+
### Зовнішні (npm-пакети, runtime)
|
|
69
|
+
|
|
70
|
+
- Зовнішніх npm-залежностей файл не імпортує напряму.
|
|
71
|
+
- Опосередковано через `runStandardRule` / `runRuleCli` залучається інфраструктура `@nitra/cursor` (logger, конфіг-парсер, walker файлової системи тощо).
|
|
72
|
+
|
|
73
|
+
### Платформенні залежності
|
|
74
|
+
|
|
75
|
+
- ESM (`import`, `import.meta.url`, `import.meta.dirname`).
|
|
76
|
+
- Top-level `await` — потрібна Node.js ≥ 14.8 в ESM або Bun.
|
|
77
|
+
- `process.exit` — Node/Bun-середовище.
|
|
78
|
+
|
|
79
|
+
### Сусідні артефакти правила (читаються через `import.meta.dirname`)
|
|
80
|
+
|
|
81
|
+
Файл сам не імпортує їх явно, але `runStandardRule` / `runRuleCli` підхоплюють з тієї ж теки:
|
|
82
|
+
|
|
83
|
+
- `meta.json` — метадані правила (id, applies-патерни, опис тощо).
|
|
84
|
+
- `bun.mdc` — людинозрозумілий опис правила у форматі MDC.
|
|
85
|
+
- `js/` — JS-concerns (check-/fix-скрипти, що працюють із JS/AST).
|
|
86
|
+
- `policy/` — policy-фаза (rego/інші policy-чеки).
|
|
87
|
+
|
|
88
|
+
## Потік виконання / Використання
|
|
89
|
+
|
|
90
|
+
### Сценарій 1: виклик з оркестратора (library mode)
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
import { run } from '@nitra/cursor/rules/bun/fix.mjs'
|
|
94
|
+
|
|
95
|
+
const exitCode = await run({ walkCache })
|
|
96
|
+
if (exitCode !== 0) {
|
|
97
|
+
// правило знайшло порушення
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Послідовність:
|
|
102
|
+
|
|
103
|
+
1. Оркестратор резолвить шлях до `fix.mjs` (наприклад, у циклі по `npm/rules/*/fix.mjs`).
|
|
104
|
+
2. Виконує `import(...)` — модуль завантажується, `isRunAsCli(...)` повертає `false`, тому standalone-блок не спрацьовує, `process.exit` не викликається.
|
|
105
|
+
3. Оркестратор отримує іменований експорт `run` і викликає його з підготованим `ctx`.
|
|
106
|
+
4. `run` делегує до `runStandardRule(import.meta.dirname, ctx)`, який:
|
|
107
|
+
- читає `meta.json` сусідньої теки;
|
|
108
|
+
- проходить фази `applies → JS-concerns → policy → mdc-refs`;
|
|
109
|
+
- повертає `0` або `1`.
|
|
110
|
+
5. Оркестратор агрегує коди повернення всіх правил та формує підсумковий exit-code.
|
|
111
|
+
|
|
112
|
+
### Сценарій 2: прямий запуск файлу (standalone mode)
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
bun npm/rules/bun/fix.mjs
|
|
116
|
+
# або
|
|
117
|
+
node npm/rules/bun/fix.mjs
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Послідовність:
|
|
121
|
+
|
|
122
|
+
1. Bun/Node стартує модуль як entry-point.
|
|
123
|
+
2. Модуль виконує імпорти `run-rule-cli.mjs` та `run-standard-rule.mjs`.
|
|
124
|
+
3. Створюється експорт `run` (просто зв’язується з функцією).
|
|
125
|
+
4. Перевірка `isRunAsCli(import.meta.url)` повертає `true`.
|
|
126
|
+
5. Виконується `await runRuleCli(import.meta.dirname)` — це **повний** еквівалент `npx @nitra/cursor fix bun`: завантаження конфігу, whitelist-фільтрація, прогон правила, фінальний summary.
|
|
127
|
+
6. Результат (число — exit-code) передається у `process.exit(...)`, процес завершується одразу.
|
|
128
|
+
|
|
129
|
+
### Еквівалентність CLI-команд
|
|
130
|
+
|
|
131
|
+
| Спосіб запуску | Внутрішній шлях | Поведінка |
|
|
132
|
+
| --------------------------- | --------------------------------------------------------------- | -------------------------------------------------------- |
|
|
133
|
+
| `npx @nitra/cursor fix bun` | CLI знаходить правило `bun`, імпортує `run` → `runStandardRule` | Library mode |
|
|
134
|
+
| `bun npm/rules/bun/fix.mjs` | Standalone-блок → `runRuleCli` | Standalone mode (повний CLI-pipeline для одного правила) |
|
|
135
|
+
|
|
136
|
+
### Чому дві ролі в одному файлі
|
|
137
|
+
|
|
138
|
+
- **Library mode** потрібен для батч-прогону: оркестратор не повинен форкати процес під кожне правило і не повинен дублювати конфіг-завантаження.
|
|
139
|
+
- **Standalone mode** потрібен для зручного дебагу одного правила в IDE/CI: достатньо вказати шлях до файлу як entry-point, не запускаючи всю CLI-обгортку.
|
|
140
|
+
|
|
141
|
+
Обидві ролі узгоджуються через стандартний `import.meta.dirname` — точку прив’язки до сусідніх артефактів правила.
|
|
142
|
+
|
|
143
|
+
## Rebuild Test
|
|
144
|
+
|
|
145
|
+
Якщо файл видалити і відтворити з нуля за цією документацією, мають виконуватись усі наступні твердження:
|
|
146
|
+
|
|
147
|
+
1. Файл містить рівно два імпорти з відносних шляхів: `../../scripts/lib/run-rule-cli.mjs` (іменовані `isRunAsCli`, `runRuleCli`) та `../../scripts/lib/run-standard-rule.mjs` (іменований `runStandardRule`).
|
|
148
|
+
2. Експортується іменована функція `run(ctx)`, яка повертає результат виклику `runStandardRule(import.meta.dirname, ctx)`. `ctx` — необов’язковий параметр.
|
|
149
|
+
3. JSDoc над `run` описує: фази правила (`applies → JS-concerns → policy → mdc-refs`), library-режим (`import + run(ctx)`), тип параметра `RuleContext`, повертає `Promise<number>` з кодами `0`/`1`.
|
|
150
|
+
4. Після експорту функції є top-level guard `if (isRunAsCli(import.meta.url)) { ... }`.
|
|
151
|
+
5. Усередині guard — `process.exit(await runRuleCli(import.meta.dirname))`.
|
|
152
|
+
6. Рядок із `process.exit` має eslint-disable-коментар, що вимикає правила `n/no-process-exit` та `unicorn/no-process-exit` з поясненням, що це standalone entry-point для CI/IDE.
|
|
153
|
+
7. Стандартний коментар поряд із guard пояснює подвійну роль файлу (`library (run) + standalone (main)`) та еквівалентність до `npx @nitra/cursor fix <id>`.
|
|
154
|
+
8. У файлі немає інших експортів, default-експорту, побічних викликів `process.exit` поза guard-блоком та жодних звернень до `process.env`, файлової системи чи мережі — все делеговано в `runStandardRule` / `runRuleCli`.
|
|
155
|
+
9. Файл коректно виконується в Bun і Node.js ≥ 14.8 у ESM-режимі завдяки top-level `await`.
|
|
156
|
+
10. При імпорті як модуль (`import { run } from '.../fix.mjs'`) `process.exit` не викликається — guard повертає `false`.
|