@nitra/cursor 12.11.2 → 12.12.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 (59) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/n-cursor.js +5 -37
  3. package/package.json +1 -1
  4. package/rules/bun/docs/index.md +2 -2
  5. package/rules/bun/docs/main.md +8 -9
  6. package/rules/bun/js/docs/index.md +3 -2
  7. package/rules/bun/main.json +1 -1
  8. package/rules/bun/main.mdc +3 -1
  9. package/rules/bun/main.mjs +63 -3
  10. package/rules/changelog/js/docs/index.md +3 -2
  11. package/rules/ci4/main.mdc +1 -0
  12. package/rules/ci4/policy/vscode_extensions/docs/index.md +11 -0
  13. package/rules/ga/main.mdc +9 -1
  14. package/rules/ga/policy/vscode_extensions/docs/index.md +11 -0
  15. package/rules/graphql/policy/vscode_extensions/docs/index.md +11 -0
  16. package/rules/js/js/dep-policy.mdc +19 -0
  17. package/rules/js/js/dep-policy.mjs +14 -6
  18. package/rules/js/js/docs/index.md +5 -5
  19. package/rules/js/main.mdc +4 -0
  20. package/rules/js/policy/vscode_extensions/docs/index.md +11 -0
  21. package/rules/js-run/js/docs/index.md +3 -2
  22. package/rules/js-run/main.mdc +3 -0
  23. package/rules/nginx-default-tpl/policy/vscode_extensions/docs/index.md +11 -0
  24. package/rules/npm-module/main.mdc +4 -0
  25. package/rules/python/docs/index.md +2 -2
  26. package/rules/python/docs/main.md +13 -10
  27. package/rules/python/main.mjs +43 -0
  28. package/rules/rego/main.mdc +2 -0
  29. package/rules/rego/policy/vscode_extensions/docs/index.md +11 -0
  30. package/rules/rust/docs/index.md +2 -2
  31. package/rules/rust/docs/main.md +10 -7
  32. package/rules/rust/main.json +1 -1
  33. package/rules/rust/main.mjs +23 -1
  34. package/rules/rust/policy/vscode_extensions/docs/index.md +11 -0
  35. package/rules/security/main.mdc +2 -0
  36. package/rules/style/js/docs/index.md +3 -2
  37. package/rules/style/main.mdc +5 -1
  38. package/rules/style/policy/vscode_extensions/docs/index.md +11 -0
  39. package/rules/tauri/policy/vscode_extensions/docs/index.md +11 -0
  40. package/rules/test/main.mdc +2 -1
  41. package/rules/text/main.mdc +9 -0
  42. package/rules/text/policy/vscode_extensions/docs/index.md +11 -0
  43. package/scripts/docs/index.md +14 -13
  44. package/scripts/docs/update-blue-oak.md +28 -0
  45. package/scripts/lib/blue-oak.mjs +45 -0
  46. package/scripts/lib/docs/blue-oak.md +29 -0
  47. package/scripts/lib/docs/index.md +35 -35
  48. package/scripts/lib/docs/run-rule.md +8 -6
  49. package/scripts/lib/fix/docs/index.md +12 -10
  50. package/scripts/lib/fix/docs/t0.md +7 -7
  51. package/scripts/lib/fix/t0.mjs +1 -2
  52. package/scripts/lib/run-rule.mjs +0 -11
  53. package/scripts/update-blue-oak.mjs +51 -0
  54. package/skills/doc-aggregate/SKILL.md +8 -18
  55. package/skills/doc-aggregate/js/docs/index.md +0 -1
  56. package/scripts/lib/check-mdc-template-refs.mjs +0 -57
  57. package/scripts/lib/docs/check-mdc-template-refs.md +0 -22
  58. package/skills/doc-aggregate/js/docgen-scan.mjs +0 -195
  59. package/skills/doc-aggregate/js/docs/docgen-scan.md +0 -76
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [12.12.0] - 2026-06-25
4
+
5
+ ### Changed
6
+
7
+ - Додано автоматичне додавання посилань на template-файли у main.mdc
8
+ - Додано підтримку Blue Oak Bronze+ для перевірки ліцензій у `bun`, `python` та `rust
9
+
10
+ ## [12.11.3] - 2026-06-25
11
+
12
+ ### Changed
13
+
14
+ - rm doc-aggregate; dep policy
15
+
3
16
  ## [12.11.2] - 2026-06-25
4
17
 
5
18
  ### Changed
package/bin/n-cursor.js CHANGED
@@ -14,7 +14,6 @@
14
14
  * весь репо (`per-file` ∪ `full`); `--read-only` = без мутацій/LLM (CI); позиційні
15
15
  * (не-флаг) аргументи — фільтр правил конформності (мапить колишній `fix <rule>`).
16
16
  * CI = `lint --read-only --full` (весь репо, нуль мутацій/LLM).
17
- * `npx \@nitra/cursor doc-aggregate modules` — JSON-лістинг логічних модулів (межі за `package.json`) для Tier 2 скілу doc-aggregate
18
17
  * `npx \@nitra/cursor skill list` — скіли пакета без синку в проєкт
19
18
  * `npx \@nitra/cursor skill taze` — промпт на stdout
20
19
  * `npx \@nitra/cursor skill cursor taze ["task"]` — Cursor CLI (`cursor-agent -p`)
@@ -1453,11 +1452,11 @@ async function runSync() {
1453
1452
 
1454
1453
  /**
1455
1454
  * Команди, що мутують проєкт у CWD і вимагають кореня репо. `undefined`/`''` —
1456
- * дефолтний sync; `check` — deprecated-alias `fix`. Решта (read-only `trace`,
1455
+ * дефолтний sync; `check` — deprecated-alias `fix`. Решта (read-only,
1457
1456
  * `--root`-команди `doc-aggregate`/`rename-yaml-extensions`,
1458
1457
  * sub-лінтери) гард не зачіпає.
1459
1458
  */
1460
- const ROOT_GUARDED_COMMANDS = new Set([undefined, '', 'lint', 'change', 'release'])
1459
+ const ROOT_GUARDED_COMMANDS = new Set([undefined, '', 'lint', 'release'])
1461
1460
 
1462
1461
  /**
1463
1462
  * Короткий опис дії для тексту root-guard помилки за іменем команди.
@@ -1473,9 +1472,6 @@ function describeRootGuardedAction(cmd) {
1473
1472
  case 'lint': {
1474
1473
  return '`lint` за замовчуванням авто-fix лінтерів (oxfmt/eslint --fix/stylelint --fix) і конформності (--full) у поточному каталозі'
1475
1474
  }
1476
- case 'change': {
1477
- return '`change` пише change-файл у .changes/ поточного каталогу'
1478
- }
1479
1475
  case 'release': {
1480
1476
  return '`release` бампає version і переписує CHANGELOG у поточному каталозі'
1481
1477
  }
@@ -1490,10 +1486,10 @@ const [command, ...args] = process.argv.slice(2)
1490
1486
 
1491
1487
  try {
1492
1488
  // Root-guard до перших мутацій: дефолтний sync скаффолдить .cursor/.claude/CLAUDE.md/
1493
- // .n-cursor.json + bun install, а lint/change/release переписують файли в CWD —
1489
+ // .n-cursor.json + bun install, а lint/release переписують файли в CWD —
1494
1490
  // усе це ключиться на cwd(). Запуск із піддиректорії git-репо (типово прямий
1495
1491
  // `bun npm/bin/n-cursor.js` не з кореня) зачепив би не той каталог → STOP. Read-only та
1496
- // `--root`-команди (trace, graph, doc-aggregate, rename-yaml-extensions) не зачіпаємо.
1492
+ // `--root`-команди (doc-aggregate, rename-yaml-extensions) не зачіпаємо.
1497
1493
  if (ROOT_GUARDED_COMMANDS.has(command)) {
1498
1494
  assertCwdIsProjectRoot(cwd(), describeRootGuardedAction(command))
1499
1495
  }
@@ -1559,12 +1555,6 @@ try {
1559
1555
 
1560
1556
  break
1561
1557
  }
1562
- case 'change': {
1563
- const { runChangeCli } = await import('../rules/release/change.mjs')
1564
- process.exitCode = await runChangeCli(args)
1565
-
1566
- break
1567
- }
1568
1558
  case 'release': {
1569
1559
  const { runReleaseCli } = await import('../rules/release/release.mjs')
1570
1560
  process.exitCode = await runReleaseCli(args)
@@ -1576,14 +1566,6 @@ try {
1576
1566
 
1577
1567
  break
1578
1568
  }
1579
- case 'trace': {
1580
- // n-cursor trace — наскрізна простежуваність (spec §5.4/§7): граф
1581
- // ADR↔spec↔plan↔change за front-matter + флаг розривів. exit 1 на розрив.
1582
- const { runTraceCli } = await import('../scripts/dispatcher/trace.mjs')
1583
- process.exitCode = runTraceCli(args)
1584
-
1585
- break
1586
- }
1587
1569
  case 'adr-normalize-local': {
1588
1570
  // Local-backend ADR-нормалізації: викликається з .claude/hooks/normalize-decisions.sh
1589
1571
  // як заміна single-shot LLM-виклику. Проганяє конвеєр (retrieval→edge-judge→
@@ -1593,20 +1575,6 @@ try {
1593
1575
 
1594
1576
  break
1595
1577
  }
1596
- case 'doc-aggregate': {
1597
- // n-cursor doc-aggregate modules — детермінований лістинг логічних модулів
1598
- // (межі за package.json) для Tier 2 module-summary скілу doc-aggregate.
1599
- // Друкує JSON; module-summary і доменні доки пише скіл, диспатчачи субагентів.
1600
- const { runDocAggregateModulesCli } = await import('../skills/doc-aggregate/js/docgen-scan.mjs')
1601
- if (args[0] === 'modules') {
1602
- process.exitCode = await runDocAggregateModulesCli(args.slice(1))
1603
- } else {
1604
- console.error('Usage: npx @nitra/cursor doc-aggregate <modules> [--root <dir>]')
1605
- process.exitCode = 1
1606
- }
1607
-
1608
- break
1609
- }
1610
1578
  case undefined:
1611
1579
  case '': {
1612
1580
  await runSync()
@@ -1616,7 +1584,7 @@ try {
1616
1584
  default: {
1617
1585
  console.error(`❌ Невідома команда: ${command}`)
1618
1586
  console.error(
1619
- ` Очікується: (без аргументів) синхронізація правил, rename-yaml-extensions, hook, adr-normalize-local, lint (включно зі scope: lint ga|rego|k8s|docker|text), analyze-escalation, taze, start-check, release, skill, trace, doc-aggregate`
1587
+ ` Очікується: (без аргументів) синхронізація правил, rename-yaml-extensions, hook, adr-normalize-local, lint (включно зі scope: lint ga|rego|k8s|docker|text), analyze-escalation, taze, start-check, release, skill, doc-aggregate`
1620
1588
  )
1621
1589
  process.exitCode = 1
1622
1590
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "12.11.2",
3
+ "version": "12.12.0",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -6,6 +6,6 @@ resource: npm/rules/bun/
6
6
 
7
7
  # npm/rules/bun
8
8
 
9
- | Файл | Тип |
10
- | ------------------- | --------- |
9
+ | Файл | Тип |
10
+ |---|---|
11
11
  | [main.mjs](main.md) | JS Module |
@@ -3,26 +3,25 @@ type: JS Module
3
3
  title: main.mjs
4
4
  resource: npm/rules/bun/main.mjs
5
5
  docgen:
6
- crc: 762b6875
6
+ crc: 18950415
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 100
9
9
  ---
10
10
 
11
- Модуль валідує дані, застосовуючи правила, визначені у конфігурації meta.json, та збирає посилання на MDC. При виклику публічної функції run він здійснює повний запуск правила, включаючи завантаження конфігурацій та застосування білих списків. Результат виконання повертається як код виходу процесу.
11
+ ## Огляд
12
+
13
+ Модуль застосовує політики до коду та перевіряє ліцензії залежностей (bun.mdc). Функція `run` застосовує політику до коду, використовуючи кешування у межах прогону для прискорення. Функція `lint` перевіряє ліцензії npm-залежностей, спираючись на конфігурацію в .licensee.json. У режимі `fix` вона створює .licensee.json, а в режимі `readOnly` блокує виконання.
12
14
 
13
15
  ## Поведінка
14
16
 
15
- 1. Викликається функція `run` для виконання перевірки.
16
- 2. Виконання перевірки включає застосування правил, обробку логіки, визначену в конфігурації `meta.json`, та збір посилань на MDC.
17
- 3. Якщо код виконується як окремий інструмент (standalone), ініціюється повний запуск правила.
18
- 4. Повний запуск правила включає завантаження конфігурацій, застосування білих списків та підбиття підсумків.
19
- 5. Результат виконання повертається як код виходу процесу.
17
+ run виконує перевірку, застосовуючи політику до коду, використовуючи кешування.
18
+ lint виконує перевірку ліцензій npm-залежностей, генеруючи `.licensee.json` у fix-режимі або відмовляючись у readOnly режимі.
20
19
 
21
20
  ## Публічний API
22
21
 
23
- run — виконує основну логіку правила: застосовує перевірки до коду, аналізує відповідність політиці та перевіряє посилання на метадані.
22
+ run — Точка входу правила, що виконує перевірку: застосовує логіку, перевіряє відповідність політиці та посилання на маркери повідомлень (bun.mdc).
23
+ lint — Інструмент для перевірки ліцензій npm-залежностей у всьому репозиторії. У режимі `--full` він працює повноцінно; у режимі виправлення автоматично створює файл .licensee.json, якщо його немає.
24
24
 
25
25
  ## Гарантії поведінки
26
26
 
27
- - Read-only: не виконує операцій запису (ФС/БД).
28
27
  - Кешує результати в межах одного прогону.
@@ -6,6 +6,7 @@ resource: npm/rules/bun/js/
6
6
 
7
7
  # npm/rules/bun/js
8
8
 
9
- | Файл | Тип |
10
- | ----------------------- | --------- |
9
+ | Файл | Тип |
10
+ |---|---|
11
+ | [fix-layout.mjs](fix-layout.md) | JS Module |
11
12
  | [layout.mjs](layout.md) | JS Module |
@@ -1 +1 @@
1
- { "auto": { "glob": "package.json" } }
1
+ { "auto": { "glob": ["package.json", ".licensee.json"] }, "lint": "full" }
@@ -5,4 +5,6 @@ alwaysApply: false
5
5
  version: '2.1'
6
6
  ---
7
7
 
8
- Проект використовує тільки Bun для керування залежностями та запуску скриптів.
8
+ Проект використовує тільки Bun для керування залежностями та запуску скриптів.
9
+ - [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
10
+ - [bunfig.toml.snippet.toml](./policy/bunfig/template/bunfig.toml.snippet.toml)
@@ -1,11 +1,17 @@
1
+ import { existsSync, writeFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { spawnSync } from 'node:child_process'
4
+
5
+ import { createCheckReporter } from '../../scripts/lib/check-reporter.mjs'
6
+ import { runStandardLint } from '../../scripts/lib/run-standard-lint.mjs'
7
+ import { resolveCmd } from '../../scripts/utils/resolve-cmd.mjs'
1
8
  import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
9
  import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
10
 
4
11
  /**
5
12
  * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня: applies →
6
- * JS-concerns → policy → mdc-refs (через runStandardRule). Lint-поверхні правило не має
7
- * (`meta.json` без `lint`), тож експорту `lint` тут немає.
8
- * Library mode: викликається CLI orchestration через `import + run(ctx)`.
13
+ * JS-concerns → policy → mdc-refs (через runStandardRule). `lint()` lint-поверхня
14
+ * (licensee перевірка ліцензій npm-залежностей, лише у `--full`).
9
15
  * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
10
16
  * @returns {Promise<number>} 0 — OK, 1 — порушення
11
17
  */
@@ -13,6 +19,60 @@ export function run(ctx) {
13
19
  return runStandardRule(import.meta.dirname, ctx)
14
20
  }
15
21
 
22
+ /** Дефолтний allowlist: Blue Oak bronze — дозволяє MIT/Apache/BSD/ISC, блокує GPL/AGPL/LGPL. */
23
+ const DEFAULT_LICENSEE_CONFIG = JSON.stringify({ licenses: { blueOak: 'bronze' }, corrections: true }, null, 2) + '\n'
24
+
25
+ /**
26
+ * Перевірка ліцензій npm-залежностей через `licensee`.
27
+ * У fix-режимі: якщо `.licensee.json` відсутній — генерує його з дефолтним allowlist
28
+ * (blueOak: bronze) і запускає перевірку. У readOnly (CI): відсутність файлу → fail.
29
+ * @param {string} [cwd] корінь проєкту
30
+ * @param {{ readOnly?: boolean }} [opts]
31
+ * @returns {number} 0 — OK, 1 — порушення
32
+ */
33
+ function runLicenseeSteps(cwd = process.cwd(), opts = {}) {
34
+ const readOnly = opts.readOnly === true
35
+ const reporter = createCheckReporter()
36
+ const { pass, fail } = reporter
37
+
38
+ const configPath = join(cwd, '.licensee.json')
39
+ if (!existsSync(configPath)) {
40
+ if (readOnly) {
41
+ fail('lint-bun: licensee — немає .licensee.json; запустіть `npx @nitra/cursor fix bun` локально для генерації (bun.mdc)')
42
+ return reporter.getExitCode()
43
+ }
44
+ writeFileSync(configPath, DEFAULT_LICENSEE_CONFIG, 'utf8')
45
+ pass('lint-bun: licensee — створено .licensee.json з дефолтним allowlist (blueOak: bronze)')
46
+ }
47
+
48
+ const bun = resolveCmd('bun')
49
+ if (!bun) {
50
+ fail('lint-bun: `bun` не знайдено в PATH (bun.mdc)')
51
+ return reporter.getExitCode()
52
+ }
53
+
54
+ const r = spawnSync(bun, ['x', 'licensee', '--production', '--quiet'], { cwd, stdio: 'inherit', shell: false })
55
+ if (r.status === 0) {
56
+ pass('lint-bun: licensee — ліцензії OK')
57
+ } else {
58
+ const code = typeof r.status === 'number' ? r.status : 1
59
+ fail(`lint-bun: licensee — порушення ліцензій (код ${code}, bun.mdc)`)
60
+ }
61
+ return reporter.getExitCode()
62
+ }
63
+
64
+ /**
65
+ * Оркестраторний адаптер `n-cursor lint bun`: licensee-перевірка ліцензій npm-залежностей.
66
+ * Whole-repo (ігнорує `_files`). Fix-режим: auto-генерує `.licensee.json` якщо відсутній.
67
+ * @param {string[] | undefined} _files ігнорується
68
+ * @param {string} [cwd] корінь
69
+ * @param {{ readOnly?: boolean }} [opts] readOnly → не мутує ФС; відсутність конфігу → fail
70
+ * @returns {Promise<number>} exit code
71
+ */
72
+ export function lint(_files, cwd = process.cwd(), opts = {}) {
73
+ return runStandardLint(import.meta.dirname, () => runLicenseeSteps(cwd, opts))
74
+ }
75
+
16
76
  if (isRunAsCli(import.meta.url)) {
17
77
  // Standalone: bun rules/<id>/main.mjs — повний еквівалент `npx @nitra/cursor check <id>`
18
78
  // (config-loading + whitelist + summary): library-роль (run) + standalone-роль (CLI-блок).
@@ -6,6 +6,7 @@ resource: npm/rules/changelog/js/
6
6
 
7
7
  # npm/rules/changelog/js
8
8
 
9
- | Файл | Тип |
10
- | --------------------------------- | --------- |
9
+ | Файл | Тип |
10
+ |---|---|
11
11
  | [consistency.mjs](consistency.md) | JS Module |
12
+ | [fix-consistency.mjs](fix-consistency.md) | JS Module |
@@ -372,3 +372,4 @@ Rego-перевірки, що запускаються через `conftest` у
372
372
  | --------------------------- | -------------------------------------------------------------------- |
373
373
  | `ci4.vscode_extensions` | `.vscode/extensions.json` містить `arr.marksman` у `recommendations` |
374
374
 
375
+ - [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/ci4/policy/vscode_extensions
4
+ resource: npm/rules/ci4/policy/vscode_extensions/
5
+ ---
6
+
7
+ # npm/rules/ci4/policy/vscode_extensions
8
+
9
+ | Файл | Тип |
10
+ | ----------------------------------------------------- | --------- |
11
+ | [fix-vscode_extensions.mjs](fix-vscode_extensions.md) | JS Module |
package/rules/ga/main.mdc CHANGED
@@ -5,4 +5,12 @@ globs: ".github/workflows/*.yml"
5
5
  alwaysApply: false
6
6
  ---
7
7
 
8
- Правило **ga** перевіряє структуру `.github/workflows/`, наявність обов'язкових workflow-файлів і їх відповідність канонам, а також налаштування VS Code та zizmor для роботи з GitHub Actions.
8
+ Правило **ga** перевіряє структуру `.github/workflows/`, наявність обов'язкових workflow-файлів і їх відповідність канонам, а також налаштування VS Code та zizmor для роботи з GitHub Actions.
9
+ - [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
10
+ - [git-ai.yml.snippet.yml](./policy/git_ai/template/git-ai.yml.snippet.yml)
11
+ - [clean-ga-workflows.yml.snippet.yml](./policy/clean_ga_workflows/template/clean-ga-workflows.yml.snippet.yml)
12
+ - [lint-ga.yml.snippet.yml](./policy/lint_ga/template/lint-ga.yml.snippet.yml)
13
+ - [zizmor.yml.snippet.yml](./policy/zizmor_yml/template/zizmor.yml.snippet.yml)
14
+ - [clean-merged-branch.yml.snippet.yml](./policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml)
15
+ - [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
16
+ - [uses-min-versions.snippet.json](./policy/workflow_common/template/uses-min-versions.snippet.json)
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/ga/policy/vscode_extensions
4
+ resource: npm/rules/ga/policy/vscode_extensions/
5
+ ---
6
+
7
+ # npm/rules/ga/policy/vscode_extensions
8
+
9
+ | Файл | Тип |
10
+ | ----------------------------------------------------- | --------- |
11
+ | [fix-vscode_extensions.mjs](fix-vscode_extensions.md) | JS Module |
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/graphql/policy/vscode_extensions
4
+ resource: npm/rules/graphql/policy/vscode_extensions/
5
+ ---
6
+
7
+ # npm/rules/graphql/policy/vscode_extensions
8
+
9
+ | Файл | Тип |
10
+ | ----------------------------------------------------- | --------- |
11
+ | [fix-vscode_extensions.mjs](fix-vscode_extensions.md) | JS Module |
@@ -2,6 +2,25 @@
2
2
 
3
3
  `@e18e/eslint-plugin` окремо не додавай — він уже в залежностях `@nitra/eslint-config` (з **3.8.0**), oxlint підвантажує його з `node_modules`. Пакети `oxlint`/`eslint`/`jscpd`/`knip` теж не додавай у `devDependencies` без потреби монорепо — `bunx` тягне їх ad-hoc.
4
4
 
5
+ ### Заборона `ua-parser-js`
6
+
7
+ Пакет **`ua-parser-js`** заборонений починаючи з версії **2.x**, де ліцензія змінилась з MIT на **AGPL-3.0** — несумісну з комерційним використанням у закритому SaaS. Замінити на **`bowser`** (MIT, ~6 KB gzip):
8
+
9
+ ```javascript title="❌ до"
10
+ import { UAParser } from 'ua-parser-js'
11
+ const parser = new UAParser(userAgent)
12
+ const result = parser.getResult()
13
+ const browserName = result.browser.name
14
+ ```
15
+
16
+ ```javascript title="✅ після"
17
+ import Bowser from 'bowser'
18
+ const browser = Bowser.getParser(userAgent)
19
+ const browserName = browser.getBrowserName()
20
+ ```
21
+
22
+ Bowser надає ті самі поля: `getBrowserName()`, `getOSName()`, `getPlatform().vendor`. Міграція займає кілька рядків; логіка не змінюється.
23
+
5
24
  ### Заборона `@nitra/as-integrations-fastify`
6
25
 
7
26
  Пакет **`@nitra/as-integrations-fastify`** заборонений у **`dependencies`**, **`peerDependencies`** та в import-specifier-ах. Це чистий републіш upstream, застряглий на peer `@apollo/server: "^4.0.0"`, тож на Apollo 5 `bun install` дає `warn: incorrect peer dependency`. Заміна — upstream **`@as-integrations/fastify`** (**`^3.1.0`**): peer `@apollo/server: "^4.0.0 || ^5.0.0"` + `fastify: "^5.3.0"`.
@@ -16,8 +16,17 @@ import {
16
16
 
17
17
  const JS_SOURCE_RE = /\.(?:[cm]?[jt]sx?)$/u
18
18
 
19
- /** Пакети, заборонені як import-specifier у будь-якому JS/TS-файлі. */
20
- const BANNED_SPECIFIERS = new Set(['@nitra/as-integrations-fastify'])
19
+ /**
20
+ * Пакети, заборонені як import-specifier у будь-якому JS/TS-файлі.
21
+ * Ключ — specifier, значення — підказка про заміну.
22
+ */
23
+ const BANNED_SPECIFIERS = new Map([
24
+ ['@nitra/as-integrations-fastify', 'використовуй @as-integrations/fastify'],
25
+ [
26
+ 'ua-parser-js',
27
+ 'замінити на bowser (MIT, ~6 KB) — npm i bowser. ua-parser-js v2 змінив ліцензію на AGPL-3.0, несумісну з комерційним використанням'
28
+ ]
29
+ ])
21
30
 
22
31
  /**
23
32
  * Витягає з джерела всі import-specifier'и (static + dynamic + require).
@@ -73,11 +82,10 @@ export async function check(cwdParam = process.cwd()) {
73
82
  const source = await readFile(absPath, 'utf8')
74
83
  const specifiers = extractImportSpecifiers(source, absPath)
75
84
  for (const spec of specifiers) {
76
- if (BANNED_SPECIFIERS.has(spec)) {
85
+ const hint = BANNED_SPECIFIERS.get(spec)
86
+ if (hint !== undefined) {
77
87
  const rel = relative(cwd, absPath)
78
- reporter.fail(
79
- `${rel}: заборонений import '${spec}' — використовуй @as-integrations/fastify (js.mdc dep-policy)`
80
- )
88
+ reporter.fail(`${rel}: заборонений import '${spec}' — ${hint} (js.mdc dep-policy)`)
81
89
  violations += 1
82
90
  }
83
91
  }
@@ -6,10 +6,10 @@ resource: npm/rules/js/js/
6
6
 
7
7
  # npm/rules/js/js
8
8
 
9
- | Файл | Тип |
10
- | ------------------------------------- | --------- |
11
- | [check.mjs](check.md) | JS Module |
12
- | [dep-policy.mjs](dep-policy.md) | JS Module |
9
+ | Файл | Тип |
10
+ |---|---|
11
+ | [check.mjs](check.md) | JS Module |
12
+ | [dep-policy.mjs](dep-policy.md) | JS Module |
13
13
  | [lint-findings.mjs](lint-findings.md) | JS Module |
14
- | [tooling.mjs](tooling.md) | JS Module |
14
+ | [tooling.mjs](tooling.md) | JS Module |
15
15
  | [utils_imports.mjs](utils_imports.md) | JS Module |
package/rules/js/main.mdc CHANGED
@@ -11,3 +11,7 @@ version: '1.30'
11
11
 
12
12
  Rego-пакети у `policy/` — запускаються `npx @nitra/cursor fix js` або `conftest`:
13
13
 
14
+ - [lint-js.yml.snippet.yml](./policy/lint_js_yml/template/lint-js.yml.snippet.yml)
15
+ - [.jscpd.json.snippet.json](./policy/jscpd/template/.jscpd.json.snippet.json)
16
+ - [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
17
+ - [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/js/policy/vscode_extensions
4
+ resource: npm/rules/js/policy/vscode_extensions/
5
+ ---
6
+
7
+ # npm/rules/js/policy/vscode_extensions
8
+
9
+ | Файл | Тип |
10
+ | ----------------------------------------------------- | --------- |
11
+ | [fix-vscode_extensions.mjs](fix-vscode_extensions.md) | JS Module |
@@ -6,6 +6,7 @@ resource: npm/rules/js-run/js/
6
6
 
7
7
  # npm/rules/js-run/js
8
8
 
9
- | Файл | Тип |
10
- | ------------------------- | --------- |
9
+ | Файл | Тип |
10
+ |---|---|
11
+ | [fix-runtime.mjs](fix-runtime.md) | JS Module |
11
12
  | [runtime.mjs](runtime.md) | JS Module |
@@ -11,3 +11,6 @@ version: '1.12'
11
11
 
12
12
  Rego-пакети, які запускає `npx @nitra/cursor fix js-run` / `npx @nitra/cursor check`:
13
13
 
14
+ - [jsconfig.json.snippet.json](./policy/jsconfig/template/jsconfig.json.snippet.json)
15
+ - [configmap.yaml.contains.yml](./policy/configmap/template/configmap.yaml.contains.yml)
16
+ - [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/nginx-default-tpl/policy/vscode_extensions
4
+ resource: npm/rules/nginx-default-tpl/policy/vscode_extensions/
5
+ ---
6
+
7
+ # npm/rules/nginx-default-tpl/policy/vscode_extensions
8
+
9
+ | Файл | Тип |
10
+ | ----------------------------------------------------- | --------- |
11
+ | [fix-vscode_extensions.mjs](fix-vscode_extensions.md) | JS Module |
@@ -18,3 +18,7 @@ Bun monorepo: workspace **`npm/`**, кореневий **`package.json`**, **`.g
18
18
  Rego-пакети (запускаються через `npx @nitra/cursor fix`):
19
19
 
20
20
  - `npm_module.npm_publish_yml` — template-driven перевірка `.github/workflows/npm-publish.yml` (deep-subset: усі обовʼязкові поля й кроки з канонічного сніпету).
21
+ - [package.json.snippet.json](./policy/npm_package_json/template/package.json.snippet.json)
22
+ - [npm-publish.yml.snippet.yml](./policy/npm_publish_yml/template/npm-publish.yml.snippet.yml)
23
+ - [package.json.snippet.json](./policy/root_package_json/template/package.json.snippet.json)
24
+ - [tsconfig.emit-types.json.snippet.json](./policy/emit_types_config/template/tsconfig.emit-types.json.snippet.json)
@@ -6,6 +6,6 @@ resource: npm/rules/python/
6
6
 
7
7
  # npm/rules/python
8
8
 
9
- | Файл | Тип |
10
- | ------------------- | --------- |
9
+ | Файл | Тип |
10
+ |---|---|
11
11
  | [main.mjs](main.md) | JS Module |
@@ -3,27 +3,30 @@ type: JS Module
3
3
  title: main.mjs
4
4
  resource: npm/rules/python/main.mjs
5
5
  docgen:
6
- crc: d8a3aa1f
6
+ crc: b072766d
7
7
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
8
  score: 90
9
9
  ---
10
10
 
11
- Реалізує логіку запуску `lint-python` за правилом `python.mdc` на базі [uv](https://docs.astral.sh/uv/). Якщо `pyproject.toml` у корені відсутній, процес завершується з кодом виходу 0. Якщо файл присутній, але інструмент `uv` не знайдено в PATH, це розглядається як помилка. Обов'язкові кроки включають перевірку актуальності lock-файлу (`uv lock --check`) та синхронізацію середовища (`uv sync --frozen`). Опційні лінтери (`ruff`, `mypy`) запускаються лише за умови їх доступності через `uv run`. `ruff` виконується у режимі автоматичного виправлення (`--fix`) для мутації робочого дерева та для форматування. Цей підхід відповідає канону патерну `lint-*` (серіалізація через `runStandardLint`, без прямого `withLock`).
11
+ ## Огляд
12
+
13
+ Виконує послідовність кроків для валідації коду Python відповідно до правил, визначених у (python.mdc). Перед запуском інструментів перевіряє наявність `pyproject.toml` у корені; якщо він відсутній, завершує роботу з кодом 0. Якщо файл присутній, перевіряє наявність `uv` у PATH, оскільки він є єдиним пакет-менеджером. Обов'язково виконує `uv lock --check` для підтвердження актуальності lock-файлу та `uv sync --frozen` для синхронізації середовища з `uv.lock` (див. https://docs.astral.sh/uv/). Опційні лінтери запускаються лише за умови їх доступності через `uv run`. При використанні `ruff` застосовується автоматичне виправлення (`auto-fix`), що модифікує робоче дерево. Усі операції виконуються з механізмом перехоплення помилок (fail-safe), запобігаючи викиданню винятків назовні.
12
14
 
13
15
  ## Поведінка
14
16
 
15
- run виконує стандартну перевірку для правила, використовуючи контекст прогону.
16
- runLintPythonSteps виконує послідовність кроків лінтування Python, перевіряючи наявність `pyproject.toml` та виконуючи команди `uv lock --check` та `uv sync --frozen`, а також запускаючи опціональні лінтери (`ruff`, `mypy`) через `uv run` на базі https://docs.astral.sh/uv/.
17
- runLintPython запускає послідовність кроків лінтування Python, серіалізуючи її через стандартний механізм перевірки.
18
- lint делегує виклик до `runLintPython`, забезпечуючи можливість запуску в режимі, що не мутує робоче дерево.
17
+ run виконує стандартну перевірку на основі контексту.
18
+ runLintPythonSteps виконує повний цикл лінтування Python, включаючи перевірку `uv lock --check`, `uv sync --frozen` та запуск лінтерів, якщо `pyproject.toml` присутній.
19
+ runLintPython запускає кроки лінтування Python, використовуючи механізм серіалізації через `runStandardLint`.
20
+ lint оркеструє запуск `runLintPython` з опцією `readOnly` для детектних перевірок.
19
21
 
20
22
  ## Публічний API
21
23
 
22
- run — Основна точка входу для виконання правил, яка перевіряє логіку застосування (JS-занепокложення $\rightarrow$ політика $\rightarrow$ mdc-посилання) та запускає перевірку коду (uv/ruff/mypy).
23
- runLintPythonSteps — Виконує внутрішні етапи перевірки Python-коду без збереження логу.
24
- runLintPython — Публічний інтерфейс для запуску перевірки Python-коду, який гарантує унікальність виконання через блокування та аналіз стану Git-дерева.
25
- lint — Адаптер, що керує запуском перевірки Python-коду, делегуючи цю роботу `runLintPython`.
24
+ run — Точка входу для виконання правил, яка перевіряє аспекти застосування (JS-занепокложення $\rightarrow$ політика $\rightarrow$ mdc-посилання).
25
+ runLintPythonSteps — Виконує внутрішні етапи перевірки коду Python без збереження результатів.
26
+ runLintPython — Публічний інтерфейс командного рядка для запуску перевірки коду Python, що забезпечує унікальність завдяки блоку блокування та аналізу стану Git-дерева.
27
+ lint — Адаптер, що керує запуском перевірки коду Python, делегуючи це `runLintPython`.
26
28
 
27
29
  ## Гарантії поведінки
28
30
 
29
31
  - Read-only: не виконує операцій запису (ФС/БД).
32
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
@@ -30,6 +30,7 @@ import { resolveCmd } from '../../scripts/utils/resolve-cmd.mjs'
30
30
  import { runStandardLint } from '../../scripts/lib/run-standard-lint.mjs'
31
31
  import { runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
32
32
  import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
33
+ import { getBronzeAndAbove, isSpdxAllowed } from '../../scripts/lib/blue-oak.mjs'
33
34
 
34
35
  /**
35
36
  * Єдиний entrypoint правила (ADR 2026-06-21). `run()` — check-поверхня (applies → JS-concerns
@@ -112,12 +113,54 @@ export function runLintPythonSteps(cwd = process.cwd(), opts = {}) {
112
113
  return runTool(label, uv, ['run', '--frozen', tool, ...args], pass, fail)
113
114
  }
114
115
 
116
+ /**
117
+ * Перевірка ліцензій Python-залежностей через pip-licenses + Blue Oak Bronze+.
118
+ * Opt-in: пропускається якщо pip-licenses не встановлений у uv-середовищі.
119
+ * @param {string} uvPath абсолютний шлях до uv
120
+ * @param {string} cwdPath корінь проєкту
121
+ * @param {(msg: string) => void} passF
122
+ * @param {(msg: string) => void} failF
123
+ * @returns {boolean} true якщо OK або пропущено; false якщо порушення
124
+ */
125
+ function checkPipLicenses(uvPath, cwdPath, passF, failF) {
126
+ if (!uvToolAvailable(uvPath, 'pip-licenses')) {
127
+ passF('lint-python: pip-licenses недоступний у uv-середовищі — перевірку ліцензій пропущено')
128
+ return true
129
+ }
130
+ const r = spawnSync(uvPath, ['run', '--frozen', 'pip-licenses', '--from=mixed', '--format=spdx-json'], {
131
+ cwd: cwdPath, stdio: ['ignore', 'pipe', 'inherit'], shell: false,
132
+ })
133
+ if (r.status !== 0) {
134
+ failF('lint-python: pip-licenses — помилка виконання')
135
+ return false
136
+ }
137
+ const allowed = getBronzeAndAbove()
138
+ let doc
139
+ try { doc = JSON.parse(r.stdout.toString('utf8')) } catch { doc = null }
140
+ const packages = doc?.packages ?? []
141
+ const violations = packages.filter(pkg => {
142
+ const lic = pkg.licenseDeclared ?? pkg.licenseConcluded ?? 'NOASSERTION'
143
+ return !isSpdxAllowed(lic, allowed)
144
+ })
145
+ if (violations.length > 0) {
146
+ for (const pkg of violations) {
147
+ const lic = pkg.licenseDeclared ?? pkg.licenseConcluded ?? 'NOASSERTION'
148
+ process.stdout.write(` ✗ ${pkg.name}@${pkg.versionInfo ?? '?'}: ${lic}\n`)
149
+ }
150
+ failF(`lint-python: pip-licenses — ${violations.length} пакет(ів) поза Blue Oak Bronze+ (python.mdc)`)
151
+ return false
152
+ }
153
+ passF(`lint-python: pip-licenses — ліцензії OK (Blue Oak Bronze+, ${packages.length} пакетів)`)
154
+ return true
155
+ }
156
+
115
157
  const ruffCheck = readOnly ? ['check', '.'] : ['check', '--fix', '.']
116
158
  const ruffFormat = readOnly ? ['format', '--check', '.'] : ['format', '.']
117
159
  if (!runOptionalUvTool('ruff', readOnly ? 'ruff check' : 'ruff check --fix', ruffCheck)) return reporter.getExitCode()
118
160
  if (!runOptionalUvTool('ruff', readOnly ? 'ruff format --check' : 'ruff format', ruffFormat))
119
161
  return reporter.getExitCode()
120
162
  if (!runOptionalUvTool('mypy', 'mypy', ['.'])) return reporter.getExitCode()
163
+ if (!checkPipLicenses(uv, cwd, pass, fail)) return reporter.getExitCode()
121
164
 
122
165
  return reporter.getExitCode()
123
166
  }
@@ -16,3 +16,5 @@ alwaysApply: false
16
16
  | `rego.vscode_extensions` | `.vscode/extensions.json` | `recommendations` містить `tsandall.opa` |
17
17
  | `rego.vscode_settings` | `.vscode/settings.json` | `[rego]`-блок з `defaultFormatter` + `formatOnSave` |
18
18
 
19
+ - [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
20
+ - [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)