@nitra/cursor 1.13.8 → 1.13.11

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,49 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.13.11] - 2026-05-17
8
+
9
+ ### Added
10
+
11
+ - `rego` rule template/ міграція (Phase 3): 3 концерни — `package_json` (snippet із збереженням `trim_space` tolerance), `vscode_extensions` (snippet-array), `vscode_settings` (snippet-object 2-level + окремий deny на non-object block).
12
+ - Drift-тести у кожному `*_test.rego`.
13
+
14
+ ### Changed
15
+
16
+ - `rego.package_json.rego` — замість двох inline-deny (missing + wrong-value через `regex/trim_space`) тепер один snippet-walker через `data.template.snippet`.
17
+ - `rego.vscode_extensions.rego` — замість inline `"tsandall.opa"` тепер subset-of через `data.template.snippet.recommendations`.
18
+ - `rego.vscode_settings.rego` — 2-рівневий snippet-walker з гардом `is_object(inner)` для випадку, коли block існує, але не обʼєкт.
19
+ - `rego.mdc` — inline `package.json` snippet замінено на template-link; додано посилання на `.vscode/{extensions,settings}.json` template-файли. Виправлено застаріле `Цілі — npm/policy/` → `npm/rules/`.
20
+ - `docs/adr/template-dir-concern-inventory.md` — позначено 3 `rego.*` концерни як ✓; додано Phase 3 у прогрес-секцію.
21
+
22
+ ## [1.13.10] - 2026-05-17
23
+
24
+ ### Fixed
25
+
26
+ - `runLintRego` (`npm/rules/rego/lint/lint.mjs`) — `LINT_TARGETS` вказував на застарілий шлях `npm/policy` (не існує після Phase 1 реструктуризації), тож `bun run lint-rego` мовчки exit 0 без реальної перевірки. Тепер `LINT_TARGETS = ['npm/rules']` — `opa check --strict`, `regal lint`, `conftest verify` реально проходять по всіх 111 `.rego`-файлах. TDD-регресія у `lint.test.mjs` (broken-syntax + well-formed fixtures).
27
+
28
+ ### Changed
29
+
30
+ - `.regal/config.yaml` — додано `idiomatic.directory-package-mismatch` і `imports.unresolved-reference` у `ignore` (інтенціональні конвенції проєкту: package = `<rule>.<concern>` у `<rule>/policy/<concern>/`; `data.template.*` ін'єктиться runtime через `--data`). `style.line-length.max-line-length: 220` — узгоджено з `opa fmt` (тримає малі обʼєкти single-line).
31
+ - `*_test.rego` з порушенням `test-outside-test-package` (4 файли: `js-lint.jscpd`, `js-lint.vscode_extensions`, `security.gitleaks`, `vue.package_json`) — перейменовано в `<package>_test` із явним `import data.<package>`.
32
+ - `opa fmt -w npm/rules` — auto-fix форматування.
33
+ - `docs/adr/template-dir-concern-inventory.md` — додано 4 `ga.*` концерни з відміткою `✓` (мігровано); оновлено summary-числа (85 концернів, 39 з template — 46%); додано секцію прогресу міграції.
34
+
35
+ ## [1.13.9] - 2026-05-17
36
+
37
+ ### Added
38
+
39
+ - `ga` rule template/ міграція (Phase 2): 4 концерни — `package_json` (contains-style), `vscode_extensions` (snippet-array), `vscode_settings` (snippet-object), `zizmor_yml` (snippet з канонічним path `rules.unpinned-uses.config.policies."*"`).
40
+ - Drift-тести (`test_data_template_drives_*`) у кожному `*_test.rego` ловлять регресію, якщо rego перестане читати з `data.template`.
41
+
42
+ ### Changed
43
+
44
+ - `ga.package_json.rego` — замість двох inline-deny з `is_string` + `regex.match` тепер один generic contains-walker через `data.template.contains`.
45
+ - `ga.vscode_extensions.rego` — замість inline `"github.vscode-github-actions"` тепер subset-of через `data.template.snippet.recommendations`.
46
+ - `ga.vscode_settings.rego` — 2-рівневий snippet-walker через `data.template.snippet` (літеральні keys `[github-actions-workflow]`, `editor.defaultFormatter`).
47
+ - `ga.zizmor_yml.rego` — замість substring `json.marshal` хака тепер структурний чек `rules.unpinned-uses.config.policies."*"` із expected value з `data.template.snippet`.
48
+ - `ga.mdc` — inline `package.json` snippet і `zizmor.yml` snippet блоки замінено на markdown-посилання на template-файли; додано посилання на нові template/ для `.vscode/{extensions,settings}.json`.
49
+
7
50
  ## [1.13.8] - 2026-05-17
8
51
 
9
52
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.13.8",
3
+ "version": "1.13.11",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
package/rules/ga/ga.mdc CHANGED
@@ -242,11 +242,7 @@ jobs:
242
242
 
243
243
  **Лінт:** [actionlint](https://github.com/rhysd/actionlint) через [github-actionlint](https://www.npmjs.com/package/github-actionlint); [zizmor](https://docs.zizmor.sh) — `uvx`, офлайн. Канонічний скрипт у корені делегує виконання CLI `n-cursor lint-ga` (бінарка з `node_modules/.bin/` пакету `@nitra/cursor`), який робить preflight на `shellcheck` і послідовно запускає `actionlint` та `zizmor`:
244
244
 
245
- ```json title="package.json"
246
- "scripts": {
247
- "lint-ga": "n-cursor lint-ga"
248
- }
249
- ```
245
+ - `package.json` — `scripts.lint-ga` має містити `n-cursor lint-ga`: [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
250
246
 
251
247
  > Не використовуй `npx --no @nitra/cursor lint-ga` — `bun run` автоматично транслює `npx` у `bun x`, а `bun x` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання. Виклик через bin-ім’я `n-cursor` працює і у `bun run`, і у `npm run`.
252
248
 
@@ -254,16 +250,15 @@ CLI робить preflight на `shellcheck` і `uv` (`uvx`) у `PATH`, поті
254
250
 
255
251
  **`.github/zizmor.yml`:** для [unpinned-uses](https://docs.zizmor.sh/audits/#unpinned-uses) — політика **`ref-pin`**, якщо в `uses:` семантичні теги. За потреби вимкни [template-injection](https://docs.zizmor.sh/audits/#template-injection):
256
252
 
257
- ```yaml title=".github/zizmor.yml"
258
- # https://docs.zizmor.sh/configuration/
259
- rules:
260
- unpinned-uses:
261
- config:
262
- policies:
263
- '*': ref-pin
264
- template-injection:
265
- disable: true
266
- ```
253
+ - Канон `.github/zizmor.yml`: [zizmor.yml.snippet.yml](./policy/zizmor_yml/template/zizmor.yml.snippet.yml)
254
+
255
+ **`.vscode/extensions.json`** має рекомендувати `github.vscode-github-actions`:
256
+
257
+ - Канон: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
258
+
259
+ **`.vscode/settings.json`** для мови `github-actions-workflow` має `editor.defaultFormatter = "oxc.oxc-vscode"`:
260
+
261
+ - Канон: [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
267
262
 
268
263
  **MegaLinter:** не використовувати; прибрати workflow, конфіги (`.mega-linter.yml`, `.megalinter.yaml`, `.mega-linter.yaml`), залежності та згадки в CI / pre-commit / документації.
269
264
 
@@ -1,24 +1,20 @@
1
1
  # Перевірка кореневого `package.json` для GitHub Actions tooling (ga.mdc).
2
2
  #
3
- # Структурні workflow-перевірки живуть у `ga.workflow_common` і per-workflow
4
- # policy-пакетах. JS лишається для PATH-preflight (`shellcheck`) і git-залежної
5
- # перевірки `on.*.paths` через `git ls-files`.
3
+ # Канон надходить через --data: { "template": { "contains": ... } }
4
+ # Структура --data сформована з template/package.json.contains.json.
6
5
  #
7
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
8
6
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
9
7
  # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
10
8
  package ga.package_json
11
9
 
12
10
  import rego.v1
13
11
 
12
+ # Кожне рядкове поле з contains має містити кожен substring.
13
+ # Відсутність ключа → `""` → contains() = false → deny.
14
14
  deny contains msg if {
15
- not is_string(object.get(object.get(input, "scripts", {}), "lint-ga", null))
16
- msg := "package.json: додай скрипт \"lint-ga\" (ga.mdc)"
17
- }
18
-
19
- deny contains msg if {
20
- lint_ga := object.get(object.get(input, "scripts", {}), "lint-ga", "")
21
- is_string(lint_ga)
22
- not regex.match(`\bn-cursor\s+lint-ga\b`, lint_ga)
23
- msg := "lint-ga має делегувати CLI `n-cursor lint-ga` (ga.mdc)"
15
+ some script_name, needles in data.template.contains.scripts
16
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
17
+ some needle in needles
18
+ not contains(actual, needle)
19
+ msg := sprintf("package.json: scripts.%s має містити %q (ga.mdc)", [script_name, needle])
24
20
  }
@@ -0,0 +1 @@
1
+ { "scripts": { "lint-ga": ["n-cursor lint-ga"] } }
@@ -0,0 +1 @@
1
+ { "recommendations": ["github.vscode-github-actions"] }
@@ -1,9 +1,10 @@
1
1
  # Перевірка `.vscode/extensions.json` для GitHub Actions (ga.mdc).
2
2
  #
3
- # Canonical: у `recommendations` має бути `github.vscode-github-actions`.
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/extensions.json.snippet.json.
5
+ # `recommendations` — subset-of: кожна рекомендація з template має бути у input.
4
6
  # Додаткові рекомендації від інших правил дозволені.
5
7
  #
6
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
7
8
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
8
9
  # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
9
10
  package ga.vscode_extensions
@@ -11,6 +12,7 @@ package ga.vscode_extensions
11
12
  import rego.v1
12
13
 
13
14
  deny contains msg if {
14
- not "github.vscode-github-actions" in {r | some r in object.get(input, "recommendations", [])}
15
- msg := ".vscode/extensions.json: recommendations має містити \"github.vscode-github-actions\" (ga.mdc)"
15
+ some rec in data.template.snippet.recommendations
16
+ not rec in {r | some r in object.get(input, "recommendations", [])}
17
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (ga.mdc)", [rec])
16
18
  }
@@ -0,0 +1 @@
1
+ { "[github-actions-workflow]": { "editor.defaultFormatter": "oxc.oxc-vscode" } }
@@ -1,9 +1,11 @@
1
1
  # Перевірка `.vscode/settings.json` для GitHub Actions workflow (ga.mdc).
2
2
  #
3
- # Мова `github-actions-workflow` має форматуватись через `oxc.oxc-vscode`,
4
- # узгоджено з oxc для YAML/workflow.
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/settings.json.snippet.json.
5
+ # Snippet — 2-рівнева мапа: <language-block-key>.<setting-key> = <expected>
6
+ # (VS Code-конвенція: ключі типу `[github-actions-workflow]` і `editor.defaultFormatter`
7
+ # — це літеральні string-keys із дужками/крапкою, не вкладені обʼєкти).
5
8
  #
6
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
7
9
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
8
10
  # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
9
11
  package ga.vscode_settings
@@ -11,14 +13,10 @@ package ga.vscode_settings
11
13
  import rego.v1
12
14
 
13
15
  deny contains msg if {
14
- block := object.get(input, "[github-actions-workflow]", null)
15
- not is_object(block)
16
- msg := ".vscode/settings.json: додай \"[github-actions-workflow]\": { \"editor.defaultFormatter\": \"oxc.oxc-vscode\" } (ga.mdc)"
17
- }
18
-
19
- deny contains msg if {
20
- block := object.get(input, "[github-actions-workflow]", null)
21
- is_object(block)
22
- object.get(block, "editor.defaultFormatter", null) != "oxc.oxc-vscode"
23
- msg := ".vscode/settings.json: [github-actions-workflow].editor.defaultFormatter має бути \"oxc.oxc-vscode\" (ga.mdc)"
16
+ some block_key, expected_inner in data.template.snippet
17
+ inner := object.get(input, block_key, {})
18
+ some leaf_key, expected_value in expected_inner
19
+ actual := object.get(inner, leaf_key, null)
20
+ actual != expected_value
21
+ msg := sprintf(".vscode/settings.json: %s.%s має бути %q (ga.mdc)", [block_key, leaf_key, expected_value])
24
22
  }
@@ -0,0 +1,5 @@
1
+ rules:
2
+ unpinned-uses:
3
+ config:
4
+ policies:
5
+ '*': ref-pin
@@ -1,10 +1,10 @@
1
1
  # Перевірка `.github/zizmor.yml` для GitHub Actions (ga.mdc).
2
2
  #
3
- # JS раніше перевіряв сирий текст на `ref-pin`; у policy це робиться по
4
- # JSON-представленню розпарсеного YAML-документа. Коментарі не враховуються,
5
- # тож збіг має бути у фактичній конфігурації.
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/zizmor.yml.snippet.yml.
5
+ # Канонічний шлях `rules.unpinned-uses.config.policies."*"`; expected value
6
+ # (наприклад `"ref-pin"`) приходить із template, path лишається тут.
6
7
  #
7
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
8
8
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
9
9
  # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
10
10
  package ga.zizmor_yml
@@ -12,6 +12,16 @@ package ga.zizmor_yml
12
12
  import rego.v1
13
13
 
14
14
  deny contains msg if {
15
- not contains(json.marshal(input), "ref-pin")
16
- msg := ".github/zizmor.yml: додай policies ref-pin для unpinned-uses (ga.mdc)"
15
+ expected := data.template.snippet.rules["unpinned-uses"].config.policies["*"]
16
+ policies := object.get(
17
+ object.get(
18
+ object.get(object.get(input, "rules", {}), "unpinned-uses", {}),
19
+ "config",
20
+ {},
21
+ ),
22
+ "policies",
23
+ {},
24
+ )
25
+ object.get(policies, "*", null) != expected
26
+ msg := sprintf(".github/zizmor.yml: rules.unpinned-uses.config.policies[%q] має бути %q (ga.mdc)", ["*", expected])
17
27
  }
@@ -18,9 +18,10 @@
18
18
  * потрібен VS Code-розширенню `tsandall.opa` (LSP, format-on-save через `opa fmt`) — деталі в
19
19
  * `mdc/rego.mdc`.
20
20
  *
21
- * Цілі лінту: `npm/policy/` (місце, де поки що живуть Rego-полісі пакета `@nitra/cursor`).
22
- * Якщо в репозиторії з’являться інші *.rego поза цим деревом, додай шлях у `LINT_TARGETS` —
23
- * усі три інструменти приймають кілька шляхів і самі рекурсивно обходять директорії.
21
+ * Цілі лінту: `npm/rules/` (де живуть Rego-полісі пакета `@nitra/cursor` — у
22
+ * `npm/rules/<id>/policy/<concern>/`). Усі три інструменти приймають один шлях
23
+ * і самі рекурсивно знаходять `.rego` (ігноруючи інші розширення на кшталт
24
+ * `target.json` чи template-фіх).
24
25
  */
25
26
  import { spawnSync } from 'node:child_process'
26
27
  import { existsSync } from 'node:fs'
@@ -30,7 +31,7 @@ import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
30
31
  import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
31
32
 
32
33
  /** Шляхи з Rego-полісі (відносно cwd). Існують не всі на ранніх стадіях — фільтруємо нижче. */
33
- const LINT_TARGETS = ['npm/policy']
34
+ const LINT_TARGETS = ['npm/rules']
34
35
 
35
36
  /**
36
37
  * Друкує підказку зі встановлення `opa` (потрібен для `opa check --strict` і VS Code LSP).
@@ -1,37 +1,16 @@
1
1
  # Перевірка `package.json` для rego (rego.mdc).
2
2
  #
3
- # Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ виявлення
4
- # `.rego` файлів у дереві. Глобально без `target.json` поруч (не auto-discoverable через `n-cursor check`).
5
- #
6
- # Canonical (rego.mdc): scripts.lint-rego має бути "n-cursor lint-rego" — CLI пакета `@nitra/cursor`
7
- # (бінарка з `node_modules/.bin/`) робить preflight `opa`/`regal` і прогонить
8
- # `opa check --strict` → `regal lint` → опц. `conftest verify` (`@nitra/cursor lint-rego`).
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/package.json.snippet.json.
5
+ # Дозволяємо whitespace навколо значення (trim_space) — допуск, який мав
6
+ # попередній inline-варіант.
11
7
  package rego.package_json
12
8
 
13
9
  import rego.v1
14
10
 
15
- canonical_lint_rego := "n-cursor lint-rego"
16
-
17
- lint_rego_template := concat(" ", [
18
- "package.json: scripts.lint-rego має бути %q",
19
- "(зараз: %q) (rego.mdc)",
20
- ])
21
-
22
- deny contains msg if {
23
- scripts := object.get(input, "scripts", {})
24
- not "lint-rego" in object.keys(scripts)
25
- msg := concat(" ", [
26
- "package.json: відсутній scripts.lint-rego — додай",
27
- "\"lint-rego\": \"n-cursor lint-rego\" (rego.mdc)",
28
- ])
29
- }
30
-
31
11
  deny contains msg if {
32
- scripts := object.get(input, "scripts", {})
33
- lint_rego := object.get(scripts, "lint-rego", "")
34
- lint_rego != ""
35
- trim_space(lint_rego) != canonical_lint_rego
36
- msg := sprintf(lint_rego_template, [canonical_lint_rego, lint_rego])
12
+ some script_name, expected in data.template.snippet.scripts
13
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
14
+ trim_space(actual) != expected
15
+ msg := sprintf("package.json: scripts.%s має бути %q (rego.mdc)", [script_name, expected])
37
16
  }
@@ -0,0 +1 @@
1
+ { "scripts": { "lint-rego": "n-cursor lint-rego" } }
@@ -0,0 +1 @@
1
+ { "recommendations": ["tsandall.opa"] }
@@ -1,19 +1,15 @@
1
1
  # Перевірка `.vscode/extensions.json` для rego (rego.mdc).
2
2
  #
3
- # Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ того, як
4
- # JS виявив `.rego` файли у дереві (умовне правило — проєкти без rego не
5
- # зобовʼязані ставити tsandall.opa). Без `target.json` поруч НЕ
6
- # реєструється.
7
- #
8
- # Canonical (rego.mdc): `recommendations` має містити `tsandall.opa`.
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/extensions.json.snippet.json.
5
+ # `recommendations` subset-of: кожна рекомендація з template має бути у input.
6
+ # Додаткові рекомендації від інших правил дозволені.
11
7
  package rego.vscode_extensions
12
8
 
13
9
  import rego.v1
14
10
 
15
11
  deny contains msg if {
16
- recs := object.get(input, "recommendations", [])
17
- not "tsandall.opa" in {r | some r in recs}
18
- msg := ".vscode/extensions.json: recommendations має містити \"tsandall.opa\" (rego.mdc)"
12
+ some rec in data.template.snippet.recommendations
13
+ not rec in {r | some r in object.get(input, "recommendations", [])}
14
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (rego.mdc)", [rec])
19
15
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "[rego]": {
3
+ "editor.defaultFormatter": "tsandall.opa",
4
+ "editor.formatOnSave": true
5
+ }
6
+ }
@@ -1,38 +1,30 @@
1
1
  # Перевірка `.vscode/settings.json` для rego (rego.mdc).
2
2
  #
3
- # Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ виявлення
4
- # `.rego` файлів у дереві. Глобально без `target.json` поруч (не auto-discoverable через `n-cursor check`).
5
- #
6
- # Canonical (rego.mdc):
7
- # { "[rego]": { "editor.defaultFormatter": "tsandall.opa",
8
- # "editor.formatOnSave": true } }
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/settings.json.snippet.json.
5
+ # Snippet — 2-рівнева мапа: <language-block-key>.<setting-key> = <expected>
6
+ # (VS Code-конвенція: ключі `[rego]` і `editor.defaultFormatter`/`editor.formatOnSave`
7
+ # це літеральні string-keys із дужками/крапкою, не вкладені обʼєкти).
11
8
  package rego.vscode_settings
12
9
 
13
10
  import rego.v1
14
11
 
15
- # ── deny: [rego] block ──────────────────────────────────────────────────
16
-
17
- deny contains msg if {
18
- not is_object(object.get(input, "[rego]", null))
19
- msg := concat(" ", [
20
- ".vscode/settings.json: \"[rego]\" має бути обʼєктом з",
21
- "\"editor.defaultFormatter\": \"tsandall.opa\" і",
22
- "\"editor.formatOnSave\": true (rego.mdc)",
23
- ])
24
- }
25
-
12
+ # Leaf-by-leaf: працює коли block присутній і є обʼєктом.
26
13
  deny contains msg if {
27
- rego_block := object.get(input, "[rego]", {})
28
- is_object(rego_block)
29
- object.get(rego_block, "editor.defaultFormatter", null) != "tsandall.opa"
30
- msg := ".vscode/settings.json: \"[rego].editor.defaultFormatter\" має бути \"tsandall.opa\" (rego.mdc)"
14
+ some block_key, expected_inner in data.template.snippet
15
+ inner := object.get(input, block_key, {})
16
+ is_object(inner)
17
+ some leaf_key, expected_value in expected_inner
18
+ actual := object.get(inner, leaf_key, null)
19
+ actual != expected_value
20
+ msg := sprintf(".vscode/settings.json: %s.%s має бути %v (rego.mdc)", [block_key, leaf_key, expected_value])
31
21
  }
32
22
 
23
+ # Block існує, але не обʼєкт (напр. рядок) — окрема помилка типу.
33
24
  deny contains msg if {
34
- rego_block := object.get(input, "[rego]", {})
35
- is_object(rego_block)
36
- object.get(rego_block, "editor.formatOnSave", null) != true
37
- msg := ".vscode/settings.json: \"[rego].editor.formatOnSave\" має бути true (rego.mdc)"
25
+ some block_key in object.keys(data.template.snippet)
26
+ raw := object.get(input, block_key, null)
27
+ raw != null
28
+ not is_object(raw)
29
+ msg := sprintf(".vscode/settings.json: %s має бути обʼєктом (rego.mdc)", [block_key])
38
30
  }
@@ -15,19 +15,21 @@ alwaysApply: false
15
15
  bun run lint-rego
16
16
  ```
17
17
 
18
- Цілі — `npm/policy/`. Інші *.rego поза деревом додай у `LINT_TARGETS` у `npm/rules/rego/js/lint.mjs`.
18
+ Цілі — `npm/rules/` (рекурсивно знаходить `.rego` у `<rule>/policy/<concern>/`). Інші *.rego поза деревом додай у `LINT_TARGETS` у `npm/rules/rego/lint/lint.mjs`.
19
19
 
20
20
  `opa` і `regal` — лише у `PATH`, **не** додавай у `dependencies` / `devDependencies`.
21
21
 
22
22
  ### `package.json`
23
23
 
24
- ```json title="package.json"
25
- {
26
- "scripts": {
27
- "lint-rego": "n-cursor lint-rego"
28
- }
29
- }
30
- ```
24
+ - Канон `scripts.lint-rego`: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
25
+
26
+ ### `.vscode/extensions.json`
27
+
28
+ - Канон `recommendations` має містити `tsandall.opa` (LSP, format-on-save через `opa fmt`): [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
29
+
30
+ ### `.vscode/settings.json`
31
+
32
+ - Канон `[rego]`-block (`editor.defaultFormatter` + `editor.formatOnSave`): [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
31
33
 
32
34
  ## Конфіг regal
33
35
 
@@ -0,0 +1,27 @@
1
+ # TODO:
2
+
3
+ ## semgrep --config p/kubernetes
4
+
5
+ p/security-audit
6
+
7
+ p/secrets
8
+
9
+ p/nodejs
10
+
11
+ p/javascript
12
+
13
+ p/typescript
14
+
15
+ p/docker
16
+
17
+ p/kubernetes
18
+
19
+ ## Trivy
20
+ Найцінніша частина Trivy
21
+
22
+ НЕ image scanning. А:
23
+
24
+
25
+ config scanning
26
+ +
27
+ Kubernetes scanning