@nitra/cursor 3.26.0 → 3.28.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 (128) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/bin/n-cursor.js +29 -9
  3. package/package.json +1 -1
  4. package/rules/abie/js/applies.mjs +1 -5
  5. package/rules/abie/js/env_dns.mjs +1 -9
  6. package/rules/abie/js/firebase_hosting.mjs +1 -5
  7. package/rules/abie/js/hc_pairing.mjs +1 -8
  8. package/rules/abie/js/ua_http_route.mjs +1 -10
  9. package/rules/abie/js/ua_node_selector.mjs +1 -8
  10. package/rules/adr/js/hooks.mjs +1 -20
  11. package/rules/bun/js/layout.mjs +1 -19
  12. package/rules/capacitor/js/platforms.mjs +1 -23
  13. package/rules/changelog/js/consistency.mjs +1 -29
  14. package/rules/ci4/js/marksman_config.mjs +1 -19
  15. package/rules/docker/js/lint.mjs +1 -34
  16. package/rules/ga/docs/fix.md +16 -149
  17. package/rules/ga/js/docs/lint.md +12 -93
  18. package/rules/ga/js/docs/workflows.md +28 -213
  19. package/rules/ga/js/workflows.mjs +1 -16
  20. package/rules/ga/lint/docs/lint.md +24 -206
  21. package/rules/graphql/js/tooling.mjs +1 -9
  22. package/rules/hasura/js/internal_urls.mjs +1 -24
  23. package/rules/image-avif/js/avif_generation.mjs +1 -27
  24. package/rules/image-compress/js/package_setup.mjs +1 -18
  25. package/rules/js-bun-db/js/safety.mjs +1 -31
  26. package/rules/js-bun-redis/js/imports.mjs +1 -12
  27. package/rules/js-lint/js/docs/lint-findings.md +30 -0
  28. package/rules/js-lint/js/lint-findings.mjs +1 -7
  29. package/rules/js-lint/js/lint.mjs +1 -10
  30. package/rules/js-lint/js/tooling.mjs +1 -13
  31. package/rules/js-lint/js/utils_imports.mjs +1 -18
  32. package/rules/js-lint-ci/js/lint.mjs +1 -6
  33. package/rules/js-mssql/js/deps.mjs +1 -10
  34. package/rules/js-run/js/runtime.mjs +1 -37
  35. package/rules/js-run/lib/docs/temporal-scan.md +25 -0
  36. package/rules/k8s/js/manifests.mjs +1 -137
  37. package/rules/nginx-default-tpl/js/template.mjs +1 -18
  38. package/rules/npm-module/js/docs/header_doc_pointer.md +25 -0
  39. package/rules/npm-module/js/header_doc_pointer.mjs +82 -0
  40. package/rules/npm-module/js/package_structure.mjs +1 -28
  41. package/rules/npm-module/js/rule_meta.mjs +1 -10
  42. package/rules/npm-module/js/skill_meta.mjs +1 -13
  43. package/rules/php/js/tooling.mjs +1 -11
  44. package/rules/python/js/applies.mjs +1 -8
  45. package/rules/python/js/tooling.mjs +1 -21
  46. package/rules/rego/js/applies.mjs +1 -11
  47. package/rules/rust/js/applies.mjs +1 -7
  48. package/rules/security/js/sample_secret.mjs +1 -28
  49. package/rules/security/js/trufflehog.mjs +1 -8
  50. package/rules/style-lint/js/lint.mjs +1 -5
  51. package/rules/style-lint/js/tooling.mjs +1 -19
  52. package/rules/tauri/js/cargo_mutants_config.mjs +1 -20
  53. package/rules/tauri/js/tooling.mjs +1 -21
  54. package/rules/test/js/cargo_mutants_config.mjs +1 -12
  55. package/rules/test/js/location.mjs +1 -9
  56. package/rules/test/js/no-process-chdir.mjs +1 -21
  57. package/rules/test/js/no-relative-fs-path.mjs +1 -23
  58. package/rules/test/js/stryker_config.mjs +4 -25
  59. package/rules/test/js/vitest-config-pool-forks.mjs +1 -17
  60. package/rules/text/js/forbidden-prettier.mjs +1 -10
  61. package/rules/text/js/formatting.mjs +1 -31
  62. package/rules/vue/js/packages.mjs +1 -24
  63. package/scripts/docs/coverage-fix-extract.md +32 -0
  64. package/scripts/docs/lint-cli.md +25 -0
  65. package/scripts/docs/post-tool-use-fix.md +27 -0
  66. package/scripts/docs/rename-yaml-extensions.md +36 -0
  67. package/scripts/docs/skills-cli.md +35 -0
  68. package/scripts/docs/sync-claude-config.md +52 -0
  69. package/scripts/docs/sync-setup-bun-deps-action.md +26 -0
  70. package/scripts/docs/upgrade-nitra-cursor-and-install.md +29 -0
  71. package/scripts/docs/worktree-cli.md +46 -0
  72. package/scripts/lib/docs/assert-project-root.md +28 -0
  73. package/scripts/lib/docs/diff-added-lines.md +34 -0
  74. package/scripts/lib/docs/read-n-cursor-config-lite.md +28 -0
  75. package/scripts/lib/docs/resolve-target-files.md +34 -0
  76. package/scripts/lib/docs/root-notice.md +28 -0
  77. package/scripts/lib/docs/rule-meta-helpers.md +34 -0
  78. package/scripts/lib/docs/rule-meta.md +34 -0
  79. package/scripts/lib/docs/rule-predicates.md +30 -0
  80. package/scripts/lib/docs/run-conftest-batch.md +26 -0
  81. package/scripts/lib/docs/run-lint-step.md +25 -0
  82. package/scripts/lib/docs/run-rule-cli.md +27 -0
  83. package/scripts/lib/docs/run-rule.md +32 -0
  84. package/scripts/lib/docs/run-standard-lint.md +22 -0
  85. package/scripts/lib/docs/run-standard-rule.md +24 -0
  86. package/scripts/lib/docs/skill-meta.md +31 -0
  87. package/scripts/lib/docs/sync-gitignore-worktree.md +31 -0
  88. package/scripts/lib/docs/template.md +40 -0
  89. package/scripts/lib/docs/timing-summary.md +24 -0
  90. package/scripts/lib/docs/workspaces.md +30 -0
  91. package/scripts/lib/docs/worktree-notice.md +27 -0
  92. package/scripts/lib/docs/worktree.md +38 -0
  93. package/scripts/utils/docs/ast-scan-utils.md +50 -0
  94. package/scripts/utils/docs/ensure-gitignore-entries.md +28 -0
  95. package/scripts/utils/docs/find-package-json-paths.md +26 -0
  96. package/scripts/utils/docs/lock-cache-dir.md +25 -0
  97. package/scripts/utils/docs/pass.md +25 -0
  98. package/scripts/utils/docs/resolve-cargo-manifest.md +23 -0
  99. package/scripts/utils/docs/resolve-cmd.md +29 -0
  100. package/scripts/utils/docs/resolve-js-root.md +25 -0
  101. package/scripts/utils/docs/test-helpers.md +36 -0
  102. package/scripts/utils/docs/walk-cache.md +27 -0
  103. package/scripts/utils/docs/walkDir.md +32 -0
  104. package/scripts/utils/docs/with-lock.md +25 -0
  105. package/scripts/utils/docs/worktree-fingerprint.md +27 -0
  106. package/skills/docgen/js/docgen-batch.mjs +95 -0
  107. package/skills/docgen/js/docgen-extract.mjs +33 -18
  108. package/skills/docgen/js/docgen-gen.mjs +258 -30
  109. package/skills/docgen/js/docgen-ignore.mjs +4 -7
  110. package/skills/docgen/js/docgen-prompts.mjs +40 -23
  111. package/skills/docgen/js/docgen-scan.mjs +1 -8
  112. package/skills/docgen/js/docs/docgen-extract.md +28 -0
  113. package/skills/docgen/js/docs/docgen-gen.md +41 -0
  114. package/skills/docgen/js/docs/docgen-ignore.md +24 -0
  115. package/skills/docgen/js/docs/docgen-prompts.md +24 -0
  116. package/skills/docgen/js/docs/docgen-scan.md +48 -0
  117. package/skills/fix/SKILL.md +5 -31
  118. package/skills/fix/js/docs/llm-worker.md +27 -0
  119. package/skills/fix/js/docs/orchestrator.md +32 -0
  120. package/skills/fix/js/docs/t0.md +29 -0
  121. package/skills/fix/js/llm-worker.mjs +216 -0
  122. package/skills/fix/js/orchestrator.mjs +119 -0
  123. package/skills/fix/js/t0.mjs +213 -0
  124. package/skills/fix/meta.json +1 -1
  125. package/skills/start-check/js/check.mjs +1 -16
  126. package/skills/start-check/js/docs/check.md +34 -0
  127. package/skills/taze/js/diff.mjs +1 -15
  128. package/skills/taze/js/docs/diff.md +33 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.28.0] - 2026-06-06
4
+
5
+ ### Added
6
+
7
+ - lib/models.mjs: global model tier classification (LOCAL_MIN/AVG/MAX, CLOUD_MIN/AVG/MAX) via N_*_MODEL env vars; fix llm-worker uses CLOUD_MIN/AVG by default
8
+
9
+ ### Changed
10
+
11
+ - fix orchestrator: concise output — single line when clean, details only when issues occur
12
+ - llm-worker: rename MODEL_HAIKU/SONNET → MODEL/MODEL_HEAVY, helpful error when pi has no API key for provider
13
+
14
+ ## [3.27.0] - 2026-06-06
15
+
16
+ ### Added
17
+
18
+ - add fix-t0 CLI command: T0-auto pattern library for n-fix orchestrator (deterministic vscode-ext-add and rm-forbidden-file fixes, 0 LLM tokens)
19
+ - add fix-run: autonomous n-fix orchestrator (convergence-loop fix-t0+LLM, haiku→sonnet escalation, no agent needed)
20
+
3
21
  ## [3.26.0] - 2026-06-06
4
22
 
5
23
  ### Added
package/bin/n-cursor.js CHANGED
@@ -5,14 +5,15 @@
5
5
  *
6
6
  * Використання:
7
7
  * `npx \@nitra/cursor` — завантажити cursor-правила
8
- * `npx \@nitra/cursor fix` — перевірити правила з `.cursor/rules/*.mdc`, для яких у пакеті є `fix.mjs`/policy;
8
+ * `npx \@nitra/cursor fix` — автономний оркестратор: T0-auto + LLM (haiku→sonnet); convergence-loop до чистого стану [--max-iter N] [rules]
9
9
  * якщо в корені вже є `.n-cursor.json`, спочатку зчитується конфіг і за потреби дописується `$schema`
10
- * `npx \@nitra/cursor fix bun` — перевірити лише вказані правила (ігнорує `.cursor/rules/`)
10
+ * `npx \@nitra/cursor fix bun` — оркестратор лише для вказаних правил; `--json` = check-only (structured output для CI)
11
11
  * `npx \@nitra/cursor rename-yaml-extensions` — k8s `*.yml` → `*.yaml`, `.github` `*.yaml` → `*.yml` (опції: `--dry-run`, `--root=…`; див. bin/rename-yaml-extensions.mjs)
12
12
  * `npx \@nitra/cursor post-tool-use-fix` — точка входу PostToolUse hook Claude Code: читає stdin JSON,
13
13
  * дістає `tool_input.file_path`, маршрутизує його у відповідні правила
14
14
  * (`*.mjs` → `js-lint`, `*.vue` → `js-lint style-lint vue` тощо) і викликає
15
15
  * `fix` лише з ними. Прописується автоматично в `.claude/settings.json`.
16
+ * `npx \@nitra/cursor fix` — автономний оркестратор (meta.json: orchestrator:true): T0-auto → LLM via pi (haiku→sonnet) → check-gate → loop; [--max-iter N] [rules]
16
17
  * `npx \@nitra/cursor lint` — оркестратор lint-ланцюжка з кореневого `package.json` з вимірюванням часу
17
18
  * кожного `lint-*` / `oxfmt` скрипта (fail-fast); канонічна заміна
18
19
  * раніше ручного `lint-ga && lint-js && …` агрегатора.
@@ -1596,12 +1597,14 @@ try {
1596
1597
  await ensureNitraCursorInRootDevDependencies(cwd())
1597
1598
  switch (command) {
1598
1599
  case 'fix': {
1599
- // --json: компактний {total, failed, rules:[{ruleId, ok, output}]} у stdout для скілу n-fix.
1600
- await runFixCommand(
1601
- args.filter(a => a !== '--json'),
1602
- { json: args.includes('--json') }
1603
- )
1604
-
1600
+ const { runOrchestratorCli } = await import('../skills/fix/js/orchestrator.mjs')
1601
+ process.exitCode = await runOrchestratorCli(args, cwd())
1602
+ break
1603
+ }
1604
+ case '_fix-check': {
1605
+ // Внутрішня команда оркестратора — не є публічним API.
1606
+ // Повертає JSON {total, failed, rules:[{ruleId, ok, output}]} у stdout.
1607
+ await runFixCommand(args, { json: true })
1605
1608
  break
1606
1609
  }
1607
1610
  case 'check': {
@@ -1709,6 +1712,23 @@ try {
1709
1712
 
1710
1713
  break
1711
1714
  }
1715
+ case 'fix-run': {
1716
+ // Backward-compatibility alias → перенаправляємо на `fix`.
1717
+ console.warn(`⚠️ \`fix-run\` deprecated — використовуйте \`fix\``)
1718
+ const { runOrchestratorCli } = await import('../skills/fix/js/orchestrator.mjs')
1719
+ process.exitCode = await runOrchestratorCli(args, cwd())
1720
+ break
1721
+ }
1722
+ case 'fix-t0': {
1723
+ // n-cursor fix-t0 [rule...] — T0-auto рівень n-fix оркестратора.
1724
+ // Запускає fix --json, знаходить violation-output із детермінованим паттерном
1725
+ // (vscode-ext-add, rm-forbidden-file тощо), застосовує програмний фікс (0 LLM),
1726
+ // перевіряє check-gate. Exit 0 = усі T0-паттерни закриті; 1 = є решта для LLM.
1727
+ const { runT0AutoCli } = await import('../skills/fix/js/t0.mjs')
1728
+ process.exitCode = await runT0AutoCli(args, cwd())
1729
+
1730
+ break
1731
+ }
1712
1732
  case 'change': {
1713
1733
  const { runChangeCli } = await import('../rules/release/change.mjs')
1714
1734
  process.exitCode = await runChangeCli(args)
@@ -1780,7 +1800,7 @@ try {
1780
1800
  default: {
1781
1801
  console.error(`❌ Невідома команда: ${command}`)
1782
1802
  console.error(
1783
- ` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, post-tool-use-fix, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, coverage, coverage-fix, taze, start-check, change, release, skill, worktree, lint-ci, flow, trace, graph, docgen`
1803
+ ` Очікується: (без аргументів) синхронізація правил, fix, check, rename-yaml-extensions, post-tool-use-fix, lint, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text, coverage, coverage-fix, taze, start-check, fix-t0, change, release, skill, worktree, lint-ci, flow, trace, graph, docgen`
1784
1804
  )
1785
1805
  process.exitCode = 1
1786
1806
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "3.26.0",
3
+ "version": "3.28.0",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,8 +1,4 @@
1
- /**
2
- * Applies-гейт правила abie: rule-level через `isAbieRuleEnabled` (поле `rules` у `.n-cursor.json`).
3
- * Якщо повертає `false` — CLI пропускає всі концерни (JS і policy) цього правила.
4
- * `check()` друкує тільки context-pass; решта концернів роблять справжню роботу.
5
- */
1
+ /** @see ./docs/applies.md */
6
2
  import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
7
3
 
8
4
  import { isAbieRuleEnabled } from '../lib/enabled.mjs'
@@ -1,12 +1,4 @@
1
- /**
2
- * Скан env-файлів abie (`*.dev.env`, `*.ua.env`): кожен внутрішньокластерний URL
3
- * `http://<svc>.<ns>.svc.<dns>` має відповідати кластеру за іменем файла:
4
- * - `dev.env` → `abie-dev.internal` + `dev-*` namespace
5
- * - `ua.env` → `abie-ua.internal` + `ua-*` namespace
6
- *
7
- * Файл `.env` без імені (локальний для розробника) — виключено.
8
- * @param {string} [cwd] корінь репозиторію
9
- */
1
+ /** @see ./docs/env_dns.md */
10
2
  import { readFile } from 'node:fs/promises'
11
3
  import { basename, relative } from 'node:path'
12
4
 
@@ -1,8 +1,4 @@
1
- /**
2
- * Перевірка abie: у **підкаталогах першого рівня** (без `.git`/`node_modules`) не має бути
3
- * `.firebaserc`, `firebase.json`, `.firebase/` (abie.mdc — Firebase Hosting заборонено).
4
- * У самому корені репозиторію ці імена не перевіряються (можуть бути від суміжних проєктів).
5
- */
1
+ /** @see ./docs/firebase_hosting.md */
6
2
  import { existsSync } from 'node:fs'
7
3
  import { readdir } from 'node:fs/promises'
8
4
  import { join } from 'node:path'
@@ -1,11 +1,4 @@
1
- /**
2
- * Перевірка abie: для кожного каталогу з `kind: Deployment` під `k8s/` поруч має бути `hc.yaml`
3
- * з коректним modeline (yaml-language-server $schema).
4
- *
5
- * Це JS-частина (FS-парність + modeline). Структурну валідацію `HealthCheckPolicy`
6
- * (apiVersion, requestPath, port, targetRef з суфіксом `-hl`) робить CLI через
7
- * `policy/health_check_policy/target.json` (walkGlob по hc.yaml у k8s-дереві).
8
- */
1
+ /** @see ./docs/hc_pairing.md */
9
2
  import { existsSync } from 'node:fs'
10
3
  import { readFile } from 'node:fs/promises'
11
4
  import { relative } from 'node:path'
@@ -1,13 +1,4 @@
1
- /**
2
- * Якщо в каталозі пакета (батько `k8s/`) є `vite.config.{js,mjs,ts}`, у `ua/kustomization.yaml`
3
- * має бути inline-patch HTTPRoute (непорожній `target.name`): `/spec/hostnames` (домени abie),
4
- * `/spec/parentRefs/0/namespace` (`ua` або `ua-*`).
5
- *
6
- * Для спільних сервісів (`auth-run-hl`, `file-link-hl`) у base-HTTPRoute пакета — кожен `backendRef`
7
- * має `namespace: dev`; в overlay patch — JSON6902 на `/spec/rules/…/backendRefs/…/namespace` зі
8
- * `value: ua`. Кількість patch-ів = кількість таких посилань у base.
9
- * @param {string} [cwd] корінь репозиторію
10
- */
1
+ /** @see ./docs/ua_http_route.md */
11
2
  import { readFile } from 'node:fs/promises'
12
3
  import { relative } from 'node:path'
13
4
 
@@ -1,11 +1,4 @@
1
- /**
2
- * Якщо в дереві `k8s/` пакета є `Deployment`, у `ua/kustomization.yaml` має бути inline-patch
3
- * на `Deployment` з `path /spec/template/spec/nodeSelector` і `preem: false` (abie.mdc).
4
- *
5
- * Структурні обмеження JSON6902 (заборона `remove + add` на той самий path) перевіряє k8s.mdc /
6
- * `k8s.kustomization` rego — тут лише abie-специфічне.
7
- * @param {string} [cwd] корінь репозиторію
8
- */
1
+ /** @see ./docs/ua_node_selector.md */
9
2
  import { readFile } from 'node:fs/promises'
10
3
  import { relative } from 'node:path'
11
4
 
@@ -1,23 +1,4 @@
1
- /**
2
- * Перевіряє вимоги правила adr.mdc: ADR Stop-hook'и `capture-decisions.sh` і
3
- * `normalize-decisions.sh` у Claude Code.
4
- *
5
- * Очікування:
6
- * - `.claude/hooks/capture-decisions.sh` та `.claude/hooks/normalize-decisions.sh`
7
- * існують і байт-у-байт збігаються з канонічними `.claude-template/hooks/*`
8
- * пакета (sync керує файлами повністю).
9
- * - `.claude/settings.json` (project-shared) має managed-групи у `hooks.Stop` для
10
- * обох скриптів (маркери у `command` — самі шляхи до скриптів).
11
- * - `.cursor/hooks.json` має managed entries у `hooks.stop` для обох скриптів, щоб
12
- * Cursor Agent теж запускав ADR capture/normalize після завершення відповіді.
13
- * - `.claude/settings.local.json` (якщо існує) НЕ має дублів цих managed-груп —
14
- * після переходу на project-shared такі записи створили б два запуски на одну подію.
15
- * - `.gitignore` у корені містить шаблон, який покриває
16
- * `.claude/hooks/capture-decisions.log` і `.claude/hooks/normalize-decisions.log`.
17
- *
18
- * LLM CLI (`claude` або `cursor-agent`) у `PATH` — інформативна перевірка: якщо жодного
19
- * немає, скрипт працює, але мовчки виходить, тому це warning, а не fail.
20
- */
1
+ /** @see ./docs/hooks.md */
21
2
  import { existsSync } from 'node:fs'
22
3
  import { readFile } from 'node:fs/promises'
23
4
  import { delimiter, dirname, join } from 'node:path'
@@ -1,22 +1,4 @@
1
- /**
2
- * Перевіряє відповідність репозиторію правилам Bun (bun.mdc).
3
- *
4
- * **Що тут лишилося** (FS / cross-file — не покривається conftest):
5
- * - наявність `bun.lock`, `bunfig.toml`, `package.json` у корені (FS-existence);
6
- * - заборонені lockfile та артефакти yarn/pnpm (`package-lock.json`, `yarn.lock`,
7
- * `pnpm-lock.yaml`, `.yarnrc.yml`, директорія `.yarn/`);
8
- * - двосторонній зв'язок `.n-cursor.json:rules` ↔ `package.json:scripts` для правил із
9
- * `lint-<id>` (`docker`, `k8s`): rule увімкнено → скрипт мусить існувати; rule
10
- * відсутнє (або в `disable-rules`) → скрипту та згадки `bun run lint-<id>` у
11
- * агрегованому `scripts.lint` бути **не може** (інакше `bun run lint` падатиме
12
- * на правилі, яке у конфізі вимкнено).
13
- *
14
- * **Що покрила Rego** (`npx \@nitra/cursor check`):
15
- * - `npm/policy/bun/bunfig/` — `[install].linker == "hoisted"` у `bunfig.toml`;
16
- * - `npm/policy/bun/package_json/` — відсутність `packageManager` / `dependencies`
17
- * у кореневому `package.json`, у `devDependencies` лише `@nitra/*`, агрегований
18
- * `lint`-скрипт покриває всі `lint-*` через `bun run` і завершується `&& oxfmt .`.
19
- */
1
+ /** @see ./docs/layout.md */
20
2
  import { existsSync } from 'node:fs'
21
3
  import { readFile } from 'node:fs/promises'
22
4
  import { join } from 'node:path'
@@ -1,26 +1,4 @@
1
- /**
2
- * Перевіряє відповідність проєкту правилам capacitor.mdc для застосунків **Capacitor**.
3
- *
4
- * Якщо у репозиторії **немає** ознак Capacitor (див. наведене) — вихід **0**, перевірка не застосовується.
5
- *
6
- * **Ознака Capacitor:** наявні **`capacitor.config.json`**, **`capacitor.config.ts`**, **`capacitor.config.mjs`**
7
- * (у корені) **або** у будь-якому `package.json` (рекурсивно, з пропуском типових каталогів) оголошено
8
- * хоча б одну залежність **`@capacitor/…`** (у **`dependencies`**, **`devDependencies`**, опційно
9
- * **`optionalDependencies`**, **`peerDependencies`**).
10
- *
11
- * **Версія мінімум 8:** у кожному `package.json`, де вказано **`@capacitor/core`**, діапазон версії
12
- * мусить допускати лише **Capacitor 8+** (оцінка мінімального **major** з рядка діапазону npm, зокрема
13
- * `||` і діапазонів через `-` у спрощеному вигляді). **`*`**, **latest** та нерозпізнані випадки — **порушення**:
14
- * варто задати явний діапазон, наприклад **`^8.0.0`**. Якщо оголошено `capacitor.config.*` без жодного
15
- * **`@capacitor/core`** у дереві `package.json` — також помилка.
16
- *
17
- * **iOS:** зазвичай **без** **Podfile** поза **Pods** (тільки **SPM**). **@nitra/**-плагіни за політикою **SPM** —
18
- * їх **не** перелічуємо й **не** перевіряємо. Якщо **Podfile** є, його можна зареєструвати як **виняток**
19
- * (див. **capacitor.mdc**): у кореневому **`package.json`** або
20
- * **`capacitor.config.json` / `capacitor.config.ts` / `capacitor.config.mjs`**, об’єкт **nitra** з
21
- * **`iosCocoaPodsBecausePluginsLackSpm: true`** (або **`iosCocoaPodsAllowed: true`**); тоді **Podfile** **не** fail.
22
- * Якщо **`ios/`** **немає** — iOS-умови не застосовуються.
23
- */
1
+ /** @see ./docs/platforms.md */
24
2
  import { existsSync } from 'node:fs'
25
3
  import { readdir, readFile } from 'node:fs/promises'
26
4
  import { join, relative } from 'node:path'
@@ -1,32 +1,4 @@
1
- /**
2
- * Перевіряє, що кожен workspace із релізно-релевантними змінами зафіксував їх через
3
- * change-файл `<ws>/.changes/*.md` — єдиний дозволений артефакт зміни. Bump `version`
4
- * і генерацію `CHANGELOG.md` робить виключно `n-cursor release` у CI на `main`.
5
- *
6
- * Інваріант (на будь-якій гілці): `version` у дереві не має **випереджати** базу. Лише
7
- * drift **уперед** (`version` > опублікованої в реєстрі / > git-бази) — ручний bump поза
8
- * CI — завалює перевірку, навіть із change-файлом. Version **позаду** бази (локаль відстала
9
- * від уже опублікованого CI-релізу, ще не зроблено `git pull`) — НЕ порушення: це не ручний
10
- * bump, а git і так не дасть запушити non-fast-forward. Pass лише коли є change-файл, а
11
- * version не випереджає базу; зміни без change-файлу — fail.
12
- *
13
- * Дві моделі бази — на рівні воркспейсу (див. n-changelog.mdc):
14
- *
15
- * 1) **registry-published** (npm: `name` + `files`, не `private`; Python: `project.name` +
16
- * статична `project.version`): база = опублікована версія в npm / PyPI.
17
- * 2) **local-only** (приватні npm без `files`, Python без імені/версії для реєстру):
18
- * feature-гілка — `merge-base` з `dev`, інакше з `main`; на `main` — diff від
19
- * `origin/main` (або `HEAD~1` без remote).
20
- *
21
- * Усі `git` і зовнішні виклики — через `execFile` / `fetch`, без shell-інтерполяції.
22
- *
23
- * **Autofix-режим** (env `N_CURSOR_CHANGELOG_AUTOFIX=1`, виставляється лише кроком
24
- * `npm-changelog` у `hk.pkl` для pre-commit): замість `fail` на відсутній change-файл
25
- * правило саме створює його через `writeChange()` з дефолтами (`bump=patch`,
26
- * `section=Changed`, `message` = subject останнього коміту) і ставить у git-індекс, щоб
27
- * коміт не падав. Поза хуком (CI, ручний `fix`/`check`, read-only review) режим вимкнено —
28
- * поведінка лишається fail-on-missing, тож CI не плодить артефактів.
29
- */
1
+ /** @see ./docs/consistency.md */
30
2
  import { execFile } from 'node:child_process'
31
3
  import { join } from 'node:path'
32
4
  import { promisify } from 'node:util'
@@ -1,22 +1,4 @@
1
- /**
2
- * Концерн `marksman_config` правила ci4 (ci4.mdc): копіює canonical
3
- * `.marksman.toml` baseline у корінь cwd, якщо файлу ще немає.
4
- *
5
- * Marksman LSP читає `.marksman.toml` для визначення workspace-роота,
6
- * GLFM-флага (GitHub-Flavored Markdown), стилю wiki-links і code actions.
7
- * Дефолти marksman не вмикають GLFM і використовують `title-slug-ref` —
8
- * але portable subset з ci4.mdc вимагає GLFM (alerts/таблиці/todo) +
9
- * `file-stem` (ADR slug == ім'я файла). Без явного конфіга частина
10
- * marksman-функцій працює інакше, ніж задокументовано у правилі.
11
- *
12
- * Idempotent: якщо `.marksman.toml` вже існує (навіть з кастомним вмістом)
13
- * — не перетирається, тільки рапортується факт існування. Ручні правки
14
- * користувача зберігаються між прогонами.
15
- *
16
- * Файл скопійовано в `cwd`, бо marksman визначає workspace-root за
17
- * розташуванням свого `.marksman.toml`. У корені репо марксман бачить
18
- * і docs/, і README.md усіх workspaces одним workspace-ом.
19
- */
1
+ /** @see ./docs/marksman_config.md */
20
2
  import { existsSync } from 'node:fs'
21
3
  import { copyFile } from 'node:fs/promises'
22
4
  import { dirname, join, relative } from 'node:path'
@@ -1,37 +1,4 @@
1
- /**
2
- * Запускає hadolint для Dockerfile / Containerfile у всьому репозиторії (див. docker.mdc).
3
- *
4
- * Додатково переконуються, що образи `oven/bun`, `alpine`, `nginx`, `node` з Docker Hub
5
- * вказуються через `mirror.gcr.io` (див. `./docker-mirror.mjs`).
6
- *
7
- * Також перевіряє, що Dockerfile/Containerfile має **multistage build** і що фінальний stage
8
- * використовує дозволений runtime-образ (див. docker.mdc):
9
- * - backend: `mirror.gcr.io/library/alpine:*`, `scratch`, `mirror.gcr.io/library/debian:` з тегом, що
10
- * містить `slim` (не повний `debian:bookworm`), за винятком PHP/Python — `mirror.gcr.io/library/php:*` або
11
- * `mirror.gcr.io/library/python:*`
12
- * - frontend: `mirror.gcr.io/nginxinc/nginx-unprivileged:*`, `mirror.gcr.io/openresty/openresty:*`
13
- *
14
- * Якщо в Dockerfile є крок `bun install` і це не frontend-образ (фінальний stage — alpine),
15
- * то очікується компіляція в один бінарник через `bun build --compile` у build stage, а у
16
- * фінальному stage не повинно залишатися build tooling (Bun/Node).
17
- *
18
- * Виняток — проєкти з нативним `.node`-аддоном (sharp/@img/argon2), який вантажиться через
19
- * динамічний `require`: їх НЕ можна компілювати (`bun build --compile` не вшиває нативний
20
- * біндинг → краш у рантаймі). Для них канон — node_modules + `bun <entry>` на базі
21
- * `mirror.gcr.io/oven/bun:alpine` (див. `../lib/docker-native-addon.mjs`).
22
- *
23
- * Мета — щоб у фінальному образі не було build tooling (Bun/Node та залежностей), а лише
24
- * дозволений runtime (alpine, scratch, debian slim, за потреби php/python, nginx або openresty).
25
- *
26
- * Для nginx-образів (`mirror.gcr.io/nginxinc/nginx-unprivileged`) у будь-якому `FROM` очікується
27
- * тег `alpine-slim` (docker.mdc: мінімальні образи), не `latest` /
28
- * `alpine` / інші. `nginx-unprivileged` запускається від не-root користувача (uid=101) без явного
29
- * `USER` у Dockerfile — перевірка non-root для нього пропускається.
30
- *
31
- * Знаходить Dockerfile, Dockerfile.*, Containerfile, Containerfile.*; пропускає node_modules, .git
32
- * тощо. hadolint — нативний бінарник через `ensureTool` (PATH/кеш/авто-install; без docker run).
33
- * Кореневий .hadolint.yaml підхоплюється hadolint автоматично.
34
- */
1
+ /** @see ./docs/lint.md */
35
2
  import { readFile } from 'node:fs/promises'
36
3
  import { basename, dirname, join } from 'node:path'
37
4
 
@@ -1,158 +1,25 @@
1
- # fix.mjs — точка входу правила `ga`
1
+ # fix.mjs
2
2
 
3
3
  ## Огляд
4
4
 
5
- Файл `npm/rules/ga/fix.mjs` це **подвійна точка входу** (dual-mode entry-point) для правила з ідентифікатором `ga` (Google Analytics) у проєкті `@nitra/cursor`. Файл реалізує дві ролі одночасно:
5
+ Цей файл запускає процес перевірки наявності та стану необхідних системних ресурсів. Він забезпечує швидку оцінку готовності системи до подальшої роботи, використовуючи кешовані дані для прискорення процесу. Результати перевірки використовуються для прийняття рішень щодо подальших дій.
6
6
 
7
- 1. **Library mode** — експортує функцію `run(ctx)`, яку імпортує оркестратор CLI (`bin/cursor.mjs` або інші правила) для запуску прогону у складі сукупної команди (наприклад, `npx @nitra/cursor fix`).
8
- 2. **Standalone mode** — якщо файл запущено напряму через `bun npm/rules/ga/fix.mjs` (або `node ...`), він самостійно ініціалізує CLI-обгортку (`runRuleCli`) і завершує процес із кодом виходу, придатним для CI/IDE.
7
+ ## Поведінка
9
8
 
10
- Логіка самого правила інкапсульована в утиліті `runStandardRule`, яка послідовно виконує стандартний пайплайн усіх «standard»-правил репозиторію:
9
+ 1. Ініціалізує контекст прогону.
10
+ 2. Викликає стандартне правило, використовуючи контекст.
11
+ 3. Якщо код виконується як окремий CLI-скрипт, то:
12
+ 1. Викликає оркестрацію правил.
13
+ 2. Повертає код завершення процесу.
11
14
 
12
- - **applies** — перевірка, що правило застосовне до поточного workspace/файлів (за патернами з `meta.json`);
13
- - **JS-concerns** — запуск перевірок (`check-*.mjs`) і авто-фіксерів (`fix-*.mjs`) із підтек `js/`, `lint/`;
14
- - **policy** — застосування policy-перевірок із підтеки `policy/`;
15
- - **mdc-refs** — валідація посилань всередині `ga.mdc` (cross-link integrity).
15
+ ## Публічний API
16
16
 
17
- Файл свідомо мінімалістичний: він **не містить власної бізнес-логіки**, лише делегує виконання у спільні бібліотеки `scripts/lib/*`. Це гарантує однакову поведінку для всіх правил пакету.
17
+ - run Запускає стандартне правило, враховуючи контекст та перетворюючи його на JS-коду, політику та посилання на MDC.
18
+ - Library mode — Викликається через CLI-оркестрацію за допомогою імпорту та функції `run`.
18
19
 
19
- ## Експорти / API
20
+ ## Гарантії поведінки
20
21
 
21
- | Експорт | Тип | Опис |
22
- | ------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
23
- | `run` | `function(ctx?): Promise<number>` | Бібліотечний вхід; запускає стандартний пайплайн правила в контексті переданого `ctx`. Повертає exit-code (0 OK, 1 — порушення). |
24
-
25
- Default-експорту немає. Файл не реекспортує жодних інших символів.
26
-
27
- ## Функції
28
-
29
- ### `run(ctx)`
30
-
31
- Бібліотечний інтерфейс правила. Дозволяє оркестратору CLI або іншим правилам запустити перевірку `ga` без створення нового процесу.
32
-
33
- **Сигнатура:**
34
-
35
- ```js
36
- export function run(ctx)
37
- ```
38
-
39
- **Параметри:**
40
-
41
- - `ctx` (`RuleContext`, опційний) — контекст прогону, типізований через JSDoc-імпорт з `../../scripts/lib/run-standard-rule.mjs`. Типово містить:
42
- - `walkCache` — кеш обходу файлів (щоб уникнути повторного `readdir` між правилами в одній сесії);
43
- - інші поля, передбачені реалізацією `runStandardRule` (репорти, прапорці dry-run тощо).
44
-
45
- **Повертає:**
46
-
47
- - `Promise<number>` — числовий код виходу:
48
- - `0` — правило не знайшло порушень або всі знайдені були автоматично виправлені;
49
- - `1` — залишилися порушення, які потребують ручного втручання.
50
-
51
- **Side effects:**
52
-
53
- - Може читати файли проєкту (через walkCache і внутрішні `check-*.mjs`/`fix-*.mjs`);
54
- - Може **писати** файли (auto-fix у режимі за замовчуванням; `fix-*.mjs` із підтек `js/`, `lint/`, `policy/`);
55
- - Може писати в `stdout`/`stderr` (репорти, попередження, summary), залежно від конфігурації `runStandardRule`;
56
- - Не викликає `process.exit` напряму — повертає код виходу як значення Promise.
57
-
58
- **Внутрішня реалізація:**
59
-
60
- ```js
61
- return runStandardRule(import.meta.dirname, ctx)
62
- ```
63
-
64
- Передає **абсолютний шлях до директорії правила** (`import.meta.dirname` — Node/Bun-нативний спосіб отримати `__dirname` в ESM) і контекст. `runStandardRule` сам визначить ID правила за назвою теки (`ga`), завантажить `meta.json`, `ga.mdc` і виконає пайплайн.
65
-
66
- ### Standalone-блок (top-level `if`)
67
-
68
- Не є експортованою функцією, але є важливою частиною API файлу.
69
-
70
- **Логіка:**
71
-
72
- ```js
73
- if (isRunAsCli(import.meta.url)) {
74
- process.exit(await runRuleCli(import.meta.dirname))
75
- }
76
- ```
77
-
78
- **Поведінка:**
79
-
80
- - `isRunAsCli(import.meta.url)` повертає `true`, якщо файл був запущений як основний модуль (а не імпортований). Це Bun/Node-сумісна заміна звичайного для CommonJS `require.main === module`.
81
- - У такому разі викликається `runRuleCli(import.meta.dirname)` — обгортка, що додає до простого `run(ctx)`:
82
- - парсинг аргументів командного рядка (наприклад, `--dry-run`, `--scope`);
83
- - завантаження користувацького конфігу (`.cursorrc`, whitelist-файлів);
84
- - друк фінального summary з кодом виходу.
85
- - Результат (`number`) передається у `process.exit(...)`, тож виклик `bun npm/rules/ga/fix.mjs` повертає shell-у точний exit-code для CI/IDE-інтеграції.
86
-
87
- **ESLint-винятки** (інлайн-коментар):
88
-
89
- - `n/no-process-exit` (плагін `eslint-plugin-n`) і `unicorn/no-process-exit` — обидва зазвичай забороняють `process.exit`, оскільки він обриває async-операції без cleanup. Тут виняток виправданий: це **єдиний standalone entry-point**, який мусить повернути exit-code саме через `process.exit`, інакше CI не зможе розрізнити успіх/провал.
90
-
91
- **Top-level `await`:**
92
-
93
- - Використовується `await runRuleCli(...)` на верхньому рівні модуля — це валідно для ESM (`.mjs`) у сучасних Node.js (≥14.8) і Bun. Без `await` `process.exit` отримав би `Promise` замість числа.
94
-
95
- ## Залежності
96
-
97
- ### Внутрішні (workspace)
98
-
99
- | Модуль | Шлях | Експорти, що використовуються |
100
- | ------------------- | ----------------------------------------- | ------------------------------------------------------------------- |
101
- | `run-rule-cli` | `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` |
102
- | `run-standard-rule` | `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule`; також імпортується тип `RuleContext` через JSDoc |
103
-
104
- Шляхи відносні до `npm/rules/ga/fix.mjs`, тобто `../../scripts/lib/` → `npm/scripts/lib/`.
105
-
106
- ### Зовнішні / runtime
107
-
108
- - **Node.js / Bun ESM API**: `import.meta.url`, `import.meta.dirname` (вимагає Node ≥20.11 або Bun, де `dirname` доступний нативно).
109
- - **`process`**: глобальний об'єкт (`process.exit`).
110
-
111
- Жодних npm-залежностей файл напряму не імпортує — всі стороні пакети підтягуються транзитивно через `scripts/lib/*`.
112
-
113
- ## Потік виконання / Використання
114
-
115
- ### Сценарій 1: запуск через `@nitra/cursor` (library mode)
116
-
117
- 1. Користувач виконує `npx @nitra/cursor fix ga` або `npx @nitra/cursor fix` (усі правила).
118
- 2. CLI-бінарник пакету (`bin/cursor.mjs` або аналог) динамічно імпортує `npm/rules/ga/fix.mjs`.
119
- 3. Оскільки модуль імпортовано (а не запущено), `isRunAsCli(import.meta.url)` → `false`, standalone-блок не виконується.
120
- 4. CLI викликає експортовану `run(ctx)` із заздалегідь підготованим контекстом (`walkCache`, прапорці).
121
- 5. `run` повертає `Promise<number>` — CLI агрегує його з результатами інших правил у фінальний exit-code.
122
-
123
- ### Сценарій 2: прямий запуск файлу (standalone)
124
-
125
- 1. Розробник або CI виконує `bun npm/rules/ga/fix.mjs` (зручно для дебагу одного правила).
126
- 2. Модуль завантажується як основний; `isRunAsCli(import.meta.url)` → `true`.
127
- 3. Виконується `await runRuleCli(import.meta.dirname)`:
128
- - парсяться CLI-аргументи;
129
- - завантажується конфіг/whitelist;
130
- - всередині також викликається `runStandardRule` (так само як у library-режимі), але з повним «end-to-end» обрамленням;
131
- - виводиться summary.
132
- 4. `process.exit(<code>)` завершує процес із отриманим exit-code (0/1).
133
-
134
- ### Стандартний пайплайн `runStandardRule` (у обох сценаріях)
135
-
136
- Послідовність (детальніше — в документації `runStandardRule`):
137
-
138
- 1. **applies** — `meta.json` визначає, чи треба запускати правило в поточному workspace; якщо ні — ранній вихід з кодом 0.
139
- 2. **JS-concerns** — обходяться підтеки правила (`js/`, `lint/`), виконуються пари `check-*.mjs` + `fix-*.mjs`. Якщо `check` повернув порушення, відповідний `fix` пробує їх прибрати.
140
- 3. **policy** — з підтеки `policy/` запускаються policy-чекери (зазвичай це декларативні правила, без auto-fix або з обмеженим fix).
141
- 4. **mdc-refs** — валідація, що в `ga.mdc` (markdown-документ із описом правила) посилання на `js/`, `lint/`, `policy/` коректні (немає «битих» референсів).
142
- 5. Агрегується фінальний exit-code: `0`, якщо всі стадії чисті; `1` — інакше.
143
-
144
- ### Як додати/змінити поведінку правила `ga`
145
-
146
- Файл `fix.mjs` змінювати **не треба** — він універсальний. Щоб модифікувати поведінку правила:
147
-
148
- - додайте/змініть `check-*.mjs` і `fix-*.mjs` у підтеках `js/`, `lint/` теки `ga/`;
149
- - оновіть `policy/`-чекери для декларативних обмежень;
150
- - актуалізуйте `ga.mdc` (опис для людини/LLM) і `meta.json` (applies, scope, прапорці);
151
- - запустіть `bun npm/rules/ga/fix.mjs` для локальної перевірки.
152
-
153
- ### Обмеження та інваріанти
154
-
155
- - Файл **повинен** залишатися мінімалістичним — будь-яка бізнес-логіка в `fix.mjs` правила вважається порушенням архітектури «standard rule» (див. `n-ci4.mdc`).
156
- - Не можна вилучати `if (isRunAsCli(...))`-блок: інакше прямий запуск файлу не поверне CI exit-code.
157
- - Не можна замінювати `process.exit` на `return` у standalone-блоці: top-level `return` у ESM-модулі недопустимий, а без `process.exit` процес не отримає правильний код.
158
- - ESLint-винятки (`n/no-process-exit`, `unicorn/no-process-exit`) застосовуються **лише** до рядка з `process.exit` — їх не слід розширювати на інші місця.
22
+ - Запускає процес.
23
+ - Кеш зберігає результати попередніх прогонів.
24
+ - Результат залежить від попередніх прогонів.
25
+ - Немає взаємодії з мережею.