@nitra/cursor 1.9.12 → 1.9.14

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/CHANGELOG.md CHANGED
@@ -4,6 +4,28 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.9.14] - 2026-05-13
8
+
9
+ ### Added
10
+
11
+ - **`text.markdownlint` rego — повний канон `.markdownlint-cli2.jsonc` тепер виноситься як deny:** раніше rego-полісі мала **рівно один** deny (`gitignore == true`), а решта канонічного блока з [text.mdc](mdc/text.mdc) (`config.default == true`, `MD013 == false`, `MD024.siblings_only == true`, `MD029 == false`, `MD040 == false`, `MD041 == false`) була показана як приклад, але не перевірялась. Додано 6 нових deny-правил, що покривають кожне поле канону; додаткові поля верхнього рівня (`ignores`) і додаткові MD-rules (`MD033` тощо) дозволені — канон задає мінімум. Шаблон повідомлень — через `concat` для regal style/line-length.
12
+ - **`npm/policy/text/markdownlint/markdownlint_test.rego` — 14 нових тестів:** happy path (канонічний `.markdownlint-cli2.jsonc`), дозволені розширення (`ignores`, `MD033`), порушення для `gitignore` (відсутній / `false`), `config.default` (відсутній / `false`), `MD013/029/040/041` (`true` або відсутній), `MD024` (не object / `siblings_only: false` / відсутній). `conftest verify` — **183/183 pass** (+14). `lint-conftest` на реальному `.markdownlint-cli2.jsonc` репо: 5/5 (раніше було 1/1 — додано 4 нові тестові кейси проти реального файлу).
13
+
14
+ ## [1.9.13] - 2026-05-13
15
+
16
+ ### Removed
17
+
18
+ - **`check-bun.mjs::isAllowedRootDevDependency` — видалено JS-копію, дубль rego:** функція експортувалася лише для тестів, у `check()` не викликалась; логіка «дозволено лише `@nitra/*` у кореневих `devDependencies`» давно живе у `npm/policy/bun/package_json/package_json.rego` (`not startswith(name, "@nitra/")`). Docstring помилково посилався на `check-text.mjs`, який цю функцію не імпортує. Тепер єдине джерело — rego; у `check-bun.mjs` додано коментар з посиланням на полісі.
19
+
20
+ ### Added
21
+
22
+ - **`npm/policy/bun/package_json/package_json_test.rego` — rego-тести для bun.package_json:** 12 нових `test_*`-кейсів через `json.patch`-фікстури — happy path (без `devDependencies`, з кількома `@nitra/*`), 4 негативні `devDependencies` (`@cspell/dict-uk-ua`, `@cspell/cspell-lib`, `lodash`, `@types/node`), mixed-devDeps з конкретним повідомленням про `lodash`, заборона `packageManager`, заборона кореневих `dependencies` (порожній обʼєкт теж), агрегований `lint`-скрипт (відсутній / не покриває `bun run` / без `&& oxfmt .`). Покривається `bun run lint-rego` (169/169 pass).
23
+
24
+ ### Changed
25
+
26
+ - **`npm/tests/check-bun.test.mjs` — прибрано `describe('isAllowedRootDevDependency')`:** імпорт і блок тестів видалено; залишено інтеграційні `check-bun`-тести у тимчасових каталогах (FS / cross-file частина).
27
+ - **Аудит інших `check-*.mjs`**: пройдено всі 22 скрипти на наявність дубля з rego (export не викликається внутрішньо + наявне `npm/policy/<rule>/<name>/<name>.rego`). Знайдено лише цей кейс; `httpRouteMatchesNginxDefaultTpl` у `check-nginx-default-tpl.mjs` залишається — не має rego-counterpart і свідомо лишений для майбутнього використання згідно docstring. Інші експорти або викликаються у відповідному `check()` / приватних helper-ах, або не мають rego-копії.
28
+
7
29
  ## [1.9.12] - 2026-05-13
8
30
 
9
31
  ### Removed
package/mdc/text.mdc CHANGED
@@ -144,6 +144,9 @@ version: '1.26'
144
144
  "config": {
145
145
  "default": true,
146
146
  "MD013": false,
147
+ "MD024": {
148
+ "siblings_only": true
149
+ },
147
150
  "MD029": false,
148
151
  "MD040": false,
149
152
  "MD041": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.9.12",
3
+ "version": "1.9.14",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -0,0 +1,109 @@
1
+ # Тести для `bun.package_json`. Запуск:
2
+ # conftest verify -p npm/policy/bun/package_json
3
+ package bun.package_json_test
4
+
5
+ import rego.v1
6
+
7
+ import data.bun.package_json
8
+
9
+ valid_pkg := {
10
+ "name": "n-cursor",
11
+ "devDependencies": {"@nitra/eslint-config": "^3.9.2"},
12
+ }
13
+
14
+ # ── happy path ────────────────────────────────────────────────────────────
15
+
16
+ test_allow_minimal if {
17
+ count(package_json.deny) == 0 with input as valid_pkg
18
+ }
19
+
20
+ test_allow_multiple_nitra_deps if {
21
+ pkg := json.patch(valid_pkg, [{
22
+ "op": "replace",
23
+ "path": "/devDependencies",
24
+ "value": {"@nitra/eslint-config": "^3.9.2", "@nitra/cspell-dict": "^2.0.0", "@nitra/stylelint-config": "^1.0.0"},
25
+ }])
26
+ count(package_json.deny) == 0 with input as pkg
27
+ }
28
+
29
+ test_allow_no_dev_dependencies if {
30
+ pkg := json.patch(valid_pkg, [{"op": "remove", "path": "/devDependencies"}])
31
+ count(package_json.deny) == 0 with input as pkg
32
+ }
33
+
34
+ # ── deny: devDependencies лише @nitra/* ──────────────────────────────────
35
+
36
+ test_deny_non_nitra_devdep if {
37
+ cases := [
38
+ {"@cspell/dict-uk-ua": "^2.0.0"},
39
+ {"@cspell/cspell-lib": "^9.0.0"},
40
+ {"lodash": "*"},
41
+ {"@types/node": "^24.0.0"},
42
+ ]
43
+ some bad in cases
44
+ pkg := json.patch(valid_pkg, [{"op": "replace", "path": "/devDependencies", "value": bad}])
45
+ count(package_json.deny) > 0 with input as pkg
46
+ }
47
+
48
+ test_deny_mixed_dev_deps_only_flags_non_nitra if {
49
+ pkg := json.patch(valid_pkg, [{
50
+ "op": "replace",
51
+ "path": "/devDependencies",
52
+ "value": {"@nitra/eslint-config": "^3.9.2", "lodash": "*"},
53
+ }])
54
+ some msg in package_json.deny with input as pkg
55
+ contains(msg, "lodash")
56
+ }
57
+
58
+ # ── deny: packageManager ─────────────────────────────────────────────────
59
+
60
+ test_deny_package_manager_field if {
61
+ pkg := json.patch(valid_pkg, [{"op": "add", "path": "/packageManager", "value": "pnpm@9.0.0"}])
62
+ count(package_json.deny) > 0 with input as pkg
63
+ }
64
+
65
+ # ── deny: dependencies у кореневому ──────────────────────────────────────
66
+
67
+ test_deny_root_dependencies_present if {
68
+ pkg := json.patch(valid_pkg, [{"op": "add", "path": "/dependencies", "value": {"lodash": "*"}}])
69
+ count(package_json.deny) > 0 with input as pkg
70
+ }
71
+
72
+ test_deny_empty_dependencies_object if {
73
+ pkg := json.patch(valid_pkg, [{"op": "add", "path": "/dependencies", "value": {}}])
74
+ count(package_json.deny) > 0 with input as pkg
75
+ }
76
+
77
+ # ── deny: агрегований lint ───────────────────────────────────────────────
78
+
79
+ test_deny_lint_prefixed_without_aggregate if {
80
+ pkg := json.patch(valid_pkg, [{"op": "add", "path": "/scripts", "value": {"lint-js": "echo"}}])
81
+ count(package_json.deny) > 0 with input as pkg
82
+ }
83
+
84
+ test_allow_lint_aggregate_calls_subscript_and_oxfmt if {
85
+ pkg := json.patch(valid_pkg, [{
86
+ "op": "add",
87
+ "path": "/scripts",
88
+ "value": {"lint-js": "echo", "lint": "bun run lint-js && oxfmt ."},
89
+ }])
90
+ count(package_json.deny) == 0 with input as pkg
91
+ }
92
+
93
+ test_deny_lint_aggregate_missing_oxfmt if {
94
+ pkg := json.patch(valid_pkg, [{
95
+ "op": "add",
96
+ "path": "/scripts",
97
+ "value": {"lint-js": "echo", "lint": "bun run lint-js"},
98
+ }])
99
+ count(package_json.deny) > 0 with input as pkg
100
+ }
101
+
102
+ test_deny_lint_aggregate_missing_subscript_via_bun_run if {
103
+ pkg := json.patch(valid_pkg, [{
104
+ "op": "add",
105
+ "path": "/scripts",
106
+ "value": {"lint-js": "echo", "lint-text": "echo", "lint": "bun run lint-js && oxfmt ."},
107
+ }])
108
+ count(package_json.deny) > 0 with input as pkg
109
+ }
@@ -8,6 +8,12 @@
8
8
  # У випадку справжнього JSONC з `//` коментарями цей крок мовчки ігноруватиметься
9
9
  # (conftest skip). FS-перевірка (наявність файлу) живе у JS.
10
10
  #
11
+ # Перевіряє канонічний baseline з text.mdc (мінімум — додаткові ключі дозволені):
12
+ # { "gitignore": true,
13
+ # "config": { "default": true, "MD013": false, "MD024": {"siblings_only": true},
14
+ # "MD029": false, "MD040": false, "MD041": false } }
15
+ # MD041 off навмисно — `.mdc` з frontmatter (див. text.mdc).
16
+ #
11
17
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
12
18
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
13
19
  # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
@@ -15,7 +21,50 @@ package text.markdownlint
15
21
 
16
22
  import rego.v1
17
23
 
24
+ # ── Шаблони повідомлень ────────────────────────────────────────────────────
25
+
26
+ config_rule_template := concat(" ", [
27
+ ".markdownlint-cli2.jsonc: config.%s має бути %v",
28
+ "(зараз: %v) (text.mdc)",
29
+ ])
30
+
31
+ # ── deny: gitignore ───────────────────────────────────────────────────────
32
+
18
33
  deny contains msg if {
19
34
  object.get(input, "gitignore", null) != true
20
35
  msg := ".markdownlint-cli2.jsonc: додай на верхньому рівні \"gitignore\": true (text.mdc)"
21
36
  }
37
+
38
+ # ── deny: config.default ──────────────────────────────────────────────────
39
+
40
+ deny contains msg if {
41
+ config := object.get(input, "config", {})
42
+ object.get(config, "default", null) != true
43
+ msg := sprintf(config_rule_template, ["default", true, object.get(config, "default", null)])
44
+ }
45
+
46
+ # ── deny: MD013 / MD029 / MD040 / MD041 повинні бути `false` ──────────────
47
+
48
+ deny contains msg if {
49
+ config := object.get(input, "config", {})
50
+ some rule in {"MD013", "MD029", "MD040", "MD041"}
51
+ object.get(config, rule, null) != false
52
+ msg := sprintf(config_rule_template, [rule, false, object.get(config, rule, null)])
53
+ }
54
+
55
+ # ── deny: MD024.siblings_only == true ─────────────────────────────────────
56
+
57
+ deny contains msg if {
58
+ config := object.get(input, "config", {})
59
+ md024 := object.get(config, "MD024", null)
60
+ not is_object(md024)
61
+ msg := sprintf(config_rule_template, ["MD024", "{\"siblings_only\": true}", md024])
62
+ }
63
+
64
+ deny contains msg if {
65
+ config := object.get(input, "config", {})
66
+ md024 := object.get(config, "MD024", {})
67
+ is_object(md024)
68
+ object.get(md024, "siblings_only", null) != true
69
+ msg := sprintf(config_rule_template, ["MD024.siblings_only", true, object.get(md024, "siblings_only", null)])
70
+ }
@@ -0,0 +1,98 @@
1
+ # Тести для `text.markdownlint`. Запуск:
2
+ # conftest verify -p npm/policy/text/markdownlint
3
+ package text.markdownlint_test
4
+
5
+ import rego.v1
6
+
7
+ import data.text.markdownlint
8
+
9
+ valid_cfg := {
10
+ "gitignore": true,
11
+ "config": {
12
+ "default": true,
13
+ "MD013": false,
14
+ "MD024": {"siblings_only": true},
15
+ "MD029": false,
16
+ "MD040": false,
17
+ "MD041": false,
18
+ },
19
+ }
20
+
21
+ # ── happy path ────────────────────────────────────────────────────────────
22
+
23
+ test_allow_canonical if {
24
+ count(markdownlint.deny) == 0 with input as valid_cfg
25
+ }
26
+
27
+ test_allow_with_additional_top_level_keys if {
28
+ cfg := json.patch(valid_cfg, [{"op": "add", "path": "/ignores", "value": ["**/adr/**"]}])
29
+ count(markdownlint.deny) == 0 with input as cfg
30
+ }
31
+
32
+ test_allow_with_additional_md_rules if {
33
+ cfg := json.patch(valid_cfg, [{"op": "add", "path": "/config/MD033", "value": {"allowed_elements": ["a"]}}])
34
+ count(markdownlint.deny) == 0 with input as cfg
35
+ }
36
+
37
+ # ── gitignore ─────────────────────────────────────────────────────────────
38
+
39
+ test_deny_missing_gitignore if {
40
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/gitignore"}])
41
+ count(markdownlint.deny) > 0 with input as cfg
42
+ }
43
+
44
+ test_deny_gitignore_false if {
45
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/gitignore", "value": false}])
46
+ count(markdownlint.deny) > 0 with input as cfg
47
+ }
48
+
49
+ # ── config.default ────────────────────────────────────────────────────────
50
+
51
+ test_deny_default_false if {
52
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/config/default", "value": false}])
53
+ count(markdownlint.deny) > 0 with input as cfg
54
+ }
55
+
56
+ test_deny_default_missing if {
57
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/config/default"}])
58
+ count(markdownlint.deny) > 0 with input as cfg
59
+ }
60
+
61
+ # ── MD013 / MD029 / MD040 / MD041 — повинні бути false ────────────────────
62
+
63
+ test_deny_md013_true if {
64
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/config/MD013", "value": true}])
65
+ count(markdownlint.deny) > 0 with input as cfg
66
+ }
67
+
68
+ test_deny_md029_missing if {
69
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/config/MD029"}])
70
+ count(markdownlint.deny) > 0 with input as cfg
71
+ }
72
+
73
+ test_deny_md040_true if {
74
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/config/MD040", "value": true}])
75
+ count(markdownlint.deny) > 0 with input as cfg
76
+ }
77
+
78
+ test_deny_md041_true if {
79
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/config/MD041", "value": true}])
80
+ count(markdownlint.deny) > 0 with input as cfg
81
+ }
82
+
83
+ # ── MD024.siblings_only ──────────────────────────────────────────────────
84
+
85
+ test_deny_md024_not_object if {
86
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/config/MD024", "value": false}])
87
+ count(markdownlint.deny) > 0 with input as cfg
88
+ }
89
+
90
+ test_deny_md024_siblings_only_false if {
91
+ cfg := json.patch(valid_cfg, [{"op": "replace", "path": "/config/MD024/siblings_only", "value": false}])
92
+ count(markdownlint.deny) > 0 with input as cfg
93
+ }
94
+
95
+ test_deny_md024_missing if {
96
+ cfg := json.patch(valid_cfg, [{"op": "remove", "path": "/config/MD024"}])
97
+ count(markdownlint.deny) > 0 with input as cfg
98
+ }
@@ -20,17 +20,9 @@ import { readFile } from 'node:fs/promises'
20
20
 
21
21
  import { createCheckReporter } from './utils/check-reporter.mjs'
22
22
 
23
- /**
24
- * Чи ім'я пакета дозволене в кореневих `devDependencies` за bun.mdc (лише **`@nitra/*`**).
25
- *
26
- * Залишилася як експорт для `check-text.mjs` і тестів — `bun.package_json` Rego
27
- * робить ту саму перевірку для check-runner-а.
28
- * @param {string} name ключ з поля `devDependencies`
29
- * @returns {boolean} true, якщо префікс дозволений
30
- */
31
- export function isAllowedRootDevDependency(name) {
32
- return name.startsWith('@nitra/')
33
- }
23
+ // Перевірка `devDependencies` кореневого `package.json` (дозволено лише `@nitra/*`)
24
+ // у rego (`npm/policy/bun/package_json/`). JS-копії `isAllowedRootDevDependency`
25
+ // видалено, щоб не було двох джерел істини.
34
26
 
35
27
  /**
36
28
  * Зчитує ідентифікатори правил з `.n-cursor.json` (поле `rules`).