@nitra/cursor 1.13.26 → 1.13.28

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,22 @@
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.28] - 2026-05-18
8
+
9
+ ### Fixed
10
+
11
+ - `scripts/utils/template.mjs` (`stripJsonComments`): враховує контекст рядкових літералів. Раніше regex `\/\*[\s\S]*?\*\/` без розрізнення string-літералів агресивно вирізав блоки між `/*` і `*/`, які зустрічаються в glob-патернах JSON-значень (напр. `**/node_modules/**`, `**/k8s/**/*.yaml`), і канонічний `.cspell.json.snippet.json` чи `.oxfmtrc.json.snippet.json` після стрипу стягувався в один склеєний рядок замість 7-елементного масиву. Новий стриппер пропускає вміст `"..."` (з підтримкою backslash-escape) без змін і вирізає лише реальні JSONC-коментарі.
12
+
13
+ ### Changed
14
+
15
+ - `hasura` rule (`hasura.svc_hl`): іменування Service узгоджено з `k8s.svc_hl_yaml` — headless (`spec.clusterIP: None`) має суфікс `-h-hl` (напр. `db-h` → `db-h-hl`), clusterIP у `svc.yaml` — `-h`. Target розширено на `hasura/k8s/base/svc.yaml` і `svc-hl.yaml`; додано `svc_hl_test.rego`. `hasura.mdc` і `fix/internal_urls` оновлено під headless DNS (`contract-h-hl`). Bump `hasura.mdc` `1.1` → `1.2`.
16
+
17
+ ## [1.13.27] - 2026-05-18
18
+
19
+ ### Fixed
20
+
21
+ - `text`, `js-lint`, `js-run` rules: додано markdown-посилання на template-файли у канонічні `<id>.mdc` — `findMissingMdcRefs` (викликається з `run-rule.mjs`) раніше падав, бо канонічні `.mdc` не містили `[name](./policy/<concern>/template/<file>)` для власних шаблонів. Bump rule versions: `text.mdc` `1.27` → `1.28`, `js-lint.mdc` `1.22` → `1.23`, `js-run.mdc` `1.8` → `1.9`.
22
+
7
23
  ## [1.13.26] - 2026-05-17
8
24
 
9
25
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.13.26",
3
+ "version": "1.13.28",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -9,10 +9,11 @@
9
9
  *
10
10
  * Очікуваний формат URL — кластерний DNS-суфікс `<cluster>.internal`:
11
11
  * - GKE / GCP: `http://<service>.<namespace>.svc.<cluster>.internal:<port>`
12
- * приклад: `http://contract-h.ua-contract.svc.abie-ua.internal:8080`
12
+ * приклад: `http://contract-h-hl.ua-contract.svc.abie-ua.internal:8080`
13
13
  *
14
14
  * Сегменти беруться з `hasura/k8s/base/svc-hl.yaml` (`metadata.name` —
15
- * має закінчуватись на `-h`, headless-сервіс) і `hasura/k8s/base/namespace.yaml`
15
+ * headless, має закінчуватись на `-h-hl`; див. `hasura.svc_hl` / k8s.svc_hl_yaml) і
16
+ * `hasura/k8s/base/namespace.yaml`
16
17
  * (`metadata.name` — namespace). Якщо ці YAML є в репозиторії, у URL додатково
17
18
  * звіряються конкретні `<service>` і `<namespace>` з ними.
18
19
  *
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Правила для директорії з hasura graphql-engine
3
- version: '1.1'
3
+ version: '1.2'
4
4
  globs: "**/hasura/**,**/*.env"
5
5
  alwaysApply: false
6
6
  ---
@@ -18,10 +18,10 @@ HASURA_GRAPHQL_ENDPOINT=https://vybeerai.com.ua/contract/ql
18
18
  Правильне значення:
19
19
 
20
20
  ```env
21
- HASURA_GRAPHQL_ENDPOINT=http://contract-h.ua-contract.svc.abie-ua.internal:8080
21
+ HASURA_GRAPHQL_ENDPOINT=http://contract-h-hl.ua-contract.svc.abie-ua.internal:8080
22
22
  ```
23
23
 
24
- де `contract-h` — це `metadata.name` сервісу з `hasura/k8s/base/svc-hl.yaml`, а `ua-contract` — `metadata.name` namespace з `hasura/k8s/base/namespace.yaml`.
24
+ де `contract-h-hl` — це `metadata.name` headless Service з `hasura/k8s/base/svc-hl.yaml` (пара з clusterIP `contract-h` у `svc.yaml`, якщо є; узгоджено з k8s: суфікс `-hl` на базі `-h`), а `ua-contract` — `metadata.name` namespace з `hasura/k8s/base/namespace.yaml`.
25
25
 
26
26
  Правило застосовується для проєктів **nitra** (у кореневому `package.json` `"repository": "https://github.com/nitra/*"`) і **abie** (`"repository": "https://github.com/abinbevefes/*"`); для інших репозиторіїв перевірка пропускається.
27
27
 
@@ -1,28 +1,50 @@
1
- # Порт мінімальної структурної перевірки `hasura/k8s/base/svc-hl.yaml` з
2
- # `npm/scripts/check-hasura.mjs` (hasura.mdc): для кожного Service у файлі
3
- # `metadata.name` має закінчуватись на `-h` (headless-сервіс Hasura).
1
+ # Іменування Service у `hasura/k8s/base/svc.yaml` та `svc-hl.yaml`, узгоджене з
2
+ # `k8s.svc_hl_yaml` / `k8s.svc_yaml` (пара clusterIP + headless під `k8s/**/`).
3
+ #
4
+ # Hasura-конвенція: базовий сегмент закінчується на `-h`; headless додає `-hl`
5
+ # → повне ім'я `*-h-hl` (також задовольняє k8s-вимогу суфікса `-hl`).
4
6
  #
5
7
  # Запуск (локально):
6
- # conftest test hasura/k8s/base/svc-hl.yaml -p npm/policy/hasura \
8
+ # conftest test hasura/k8s/base/svc-hl.yaml -p npm/rules/hasura/policy/svc_hl \
7
9
  # --namespace hasura.svc_hl
8
10
  #
9
- # Решта логіки `check-hasura.mjs` (звірення `HASURA_GRAPHQL_ENDPOINT` в `.env`-файлах
10
- # з `<service>.<namespace>.svc.<cluster>` через regex по всьому дереву репо, gating
11
- # на `repository` у кореневому `package.json`) — у JS: вона потребує текстового
12
- # парсингу `.env`-файлів, обходу дерева й cross-file resolution. JS authoritative;
13
- # ця Rego — додатковий gate (JS неявно перевіряє суфікс через звірку URL).
14
- #
15
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
16
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
17
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
11
+ # Cross-file (`HASURA_GRAPHQL_ENDPOINT` YAML) — `fix/internal_urls/check.mjs`.
18
12
  package hasura.svc_hl
19
13
 
20
14
  import rego.v1
21
15
 
16
+ # Суфікс clusterIP Service у hasura/k8s/base (і база для пари з svc-hl.yaml).
17
+ hasura_cluster_suffix := "-h"
18
+
19
+ # Headless: `<cluster-name>-hl`, напр. `db-h` → `db-h-hl`.
20
+ hasura_headless_suffix := "-h-hl"
21
+
22
+ service_is_headless if {
23
+ input.kind == "Service"
24
+ spec := object.get(input, "spec", {})
25
+ is_object(spec)
26
+ spec.clusterIP == "None"
27
+ }
28
+
29
+ deny contains msg if {
30
+ service_is_headless
31
+ name := object.get(object.get(input, "metadata", {}), "name", "")
32
+ name != ""
33
+ not endswith(name, hasura_headless_suffix)
34
+ msg := sprintf(
35
+ "hasura svc-hl.yaml: headless Service %q має закінчуватись на `%s` (узгоджено з k8s.svc_hl_yaml `-hl`; hasura.mdc)",
36
+ [name, hasura_headless_suffix],
37
+ )
38
+ }
39
+
22
40
  deny contains msg if {
23
41
  input.kind == "Service"
42
+ not service_is_headless
24
43
  name := object.get(object.get(input, "metadata", {}), "name", "")
25
44
  name != ""
26
- not endswith(name, "-h")
27
- msg := sprintf("hasura svc-hl.yaml: Service %q має закінчуватись на `-h` (hasura.mdc / k8s.mdc)", [name])
45
+ not endswith(name, hasura_cluster_suffix)
46
+ msg := sprintf(
47
+ "hasura svc.yaml: clusterIP Service %q має закінчуватись на `%s` (hasura.mdc / k8s.mdc)",
48
+ [name, hasura_cluster_suffix],
49
+ )
28
50
  }
@@ -1,4 +1,6 @@
1
1
  {
2
2
  "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
- "files": { "single": "hasura/k8s/base/svc-hl.yaml" }
3
+ "files": {
4
+ "walkGlob": ["hasura/k8s/base/svc.yaml", "hasura/k8s/base/svc-hl.yaml"]
5
+ }
4
6
  }
@@ -2,7 +2,7 @@
2
2
  description: Перевірка JavaScript коду
3
3
  globs: "**/{.oxlintrc.json,eslint.config.js,.jscpd.json,knip.json,package.json},**/*.{js,mjs,cjs,jsx,ts,tsx}"
4
4
  alwaysApply: false
5
- version: '1.22'
5
+ version: '1.23'
6
6
  ---
7
7
 
8
8
  **oxlint**, **ESLint**, **jscpd**, **knip**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`**, **`bunx knip`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**. У **`devDependencies`** має бути **`@nitra/eslint-config` мінімум `^3.9.2`** (з **3.8.0** правило `no-restricted-syntax` забороняє `for...in`; з **3.9.2** у `getConfig` вбудовано ignore для **`**/adr/**`** — ADR-документи не валідуються ESLint, локально цей glob додавати не потрібно; також транзитивно йде **`@e18e/eslint-plugin`** для oxlint); пакет **`@e18e/eslint-plugin`** окремо не додавай. Пакети oxlint/eslint/jscpd/knip не додавай без потреби монорепо.
@@ -21,6 +21,10 @@ version: '1.22'
21
21
  }
22
22
  ```
23
23
 
24
+ Канон `type` + `scripts.lint-js` (substring requirement): [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
25
+
26
+ У `.vscode/extensions.json` `recommendations` мають містити `dbaeumer.vscode-eslint`, `github.vscode-github-actions`, `oxc.oxc-vscode`: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
27
+
24
28
  У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/scripts/utils/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**). Поле **`ignorePatterns`** працює як **`rules`**: канонічні патерни з **`oxlint-canonical.json`** (наразі **`**/schema.graphql`**, **`**/auto-imports.d.ts`**) мають бути присутні, додаткові локальні glob-и дозволені. Оновити канон можна з репозиторію пакета або скопіювавши файл після **`bun ./scripts/utils/rebuild-oxlint-canonical.mjs`** (джерело правил — **`oxlint-rules.tsv`** + скелет **`oxlint-canonical-skeleton.json`**). Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.8.0**), oxlint підвантажує його з **`node_modules`**.
25
29
 
26
30
  Мінімум для розуміння структури (реальний корінь конфігу має збігатися з каноном повністю):
@@ -48,6 +52,8 @@ version: '1.22'
48
52
  }
49
53
  ```
50
54
 
55
+ Канон базових ключів `.jscpd.json` (`gitignore`, `exitCode`, `reporters`, `minLines`): [.jscpd.json.snippet.json](./policy/jscpd/template/.jscpd.json.snippet.json)
56
+
51
57
  ```text title=".gitignore (фрагмент)"
52
58
  .claude/worktrees/
53
59
  ```
@@ -122,6 +128,8 @@ jobs:
122
128
  bunx knip --no-config-hints
123
129
  ```
124
130
 
131
+ Канон workflow `.github/workflows/lint-js.yml`: [lint-js.yml.snippet.yml](./policy/lint_js_yml/template/lint-js.yml.snippet.yml)
132
+
125
133
  Перед **`./.github/actions/setup-bun-deps`** — **`actions/checkout@v6`** (див. **ga.mdc**). Composite: Node 24, Bun, кеш, `bun install --frozen-lockfile`.
126
134
 
127
135
  Один workflow на лінт JS; зайвий `lint.yml` з тими самими кроками — прибери.
@@ -2,7 +2,7 @@
2
2
  description: Це правила для backend проектів на JavaScript/Node.js, сюди входять і job і WEB сервери.
3
3
  globs: "**/package.json,**/jsconfig.json,**/src/**/*.{js,mjs,cjs,ts,tsx}"
4
4
  alwaysApply: false
5
- version: '1.8'
5
+ version: '1.9'
6
6
  ---
7
7
 
8
8
  ## Область застосування
@@ -46,16 +46,20 @@ readme.md
46
46
  }
47
47
  ```
48
48
 
49
+ Канон: [jsconfig.json.snippet.json](./policy/jsconfig/template/jsconfig.json.snippet.json)
50
+
49
51
  Якщо пакет не слідує структурі з `src/` (наприклад, лише `scripts/` у корені) — ця вимога не застосовується; для типових сервісів із `src/` файл обов’язковий і має збігатися з каноном.
50
52
 
51
53
  ## Використання @nitra/pino
52
54
 
53
55
  Проект використовує @nitra/pino для логування.
54
- Якщо в проекті присутній @nitra/bunyan, то він повинен бути замінений на @nitra/pino — як у `package.json`, так і в коді: усі `import` / `require` / динамічні `import()` з `@nitra/bunyan` (і застарілого `bunyan`) треба замінити на `@nitra/pino` і за потреби адаптувати виклики під його API.
56
+ Якщо в проекті присутній @nitra/bunyan, то він повинен бути замінений на @nitra/pino — як у `package.json`, так і в коді: усі `import` / `require` / динамічні `import()` з `@nitra/bunyan` (і застарілого `bunyan`) треба замінити на `@nitra/pino` і за потреби адаптувати виклики під його API. Канон заборонених `dependencies` / `devDependencies` (`bunyan`, `@nitra/bunyan`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
55
57
 
56
58
  В **/k8s/base/configmap.yaml повинен бути заданий OTEL_RESOURCE_ATTRIBUTES: 'service.name=<project_name>,service.namespace=<project_namespace>'
57
59
  а в директоріях з kustomize повинні бути перевизначені значення OTEL_RESOURCE_ATTRIBUTES і в них service.namespace повинен відповідати namespace, в якому знаходиться дана директорія.
58
60
 
61
+ Канон обовʼязкових substring у `data.OTEL_RESOURCE_ATTRIBUTES` (`service.name=`, `service.namespace=`): [configmap.yaml.contains.yml](./policy/configmap/template/configmap.yaml.contains.yml)
62
+
59
63
  ## Внутрішні аліаси
60
64
 
61
65
  Якщо в проекті є підключення до баз даних, зовнішніх graphql на кшталт:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Обробка та перевірка текстових файлів, oxfmt, cspell, shellcheck (sh), dotenv-linter (.env*), markdownlint-cli2, v8r, CI
3
3
  alwaysApply: true
4
- version: '1.27'
4
+ version: '1.28'
5
5
  ---
6
6
 
7
7
  **oxfmt** (`.oxfmtrc.json`, редактор), **cspell**, **shellcheck** (tracked `*.sh` у `lint-text`), **[dotenv-linter](https://dotenv-linter.github.io/)** (`.env*` у `lint-text`), **markdownlint-cli2**, **[v8r](https://chris48s.github.io/v8r/)** ([Schema Store](https://www.schemastore.org/)), розширення **DavidAnson.vscode-markdownlint** / **timonwong.shellcheck**, workflow **`lint-text`**.
@@ -20,6 +20,8 @@ version: '1.27'
20
20
  }
21
21
  ```
22
22
 
23
+ Канон `recommendations` (substring requirement): [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
24
+
23
25
  ```json title=".vscode/settings.json"
24
26
  {
25
27
  "files.associations": {
@@ -79,6 +81,8 @@ version: '1.27'
79
81
  }
80
82
  ```
81
83
 
84
+ Канон `editor.formatOnSave` + `editor.defaultFormatter` для основних мов: [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
85
+
82
86
  У корені проєкту має бути файл з правилами форматування для **oxfmt**:
83
87
 
84
88
  ```json title=".oxfmtrc.json"
@@ -105,9 +109,11 @@ version: '1.27'
105
109
  }
106
110
  ```
107
111
 
112
+ Канон мінімального набору ключів і `ignorePatterns`: [.oxfmtrc.json.snippet.json](./policy/oxfmtrc/template/.oxfmtrc.json.snippet.json)
113
+
108
114
  Поле **`ignorePatterns`** обовʼязкове: у масиві мають бути **`**/hasura/metadata/**`**, **`**/schema.graphql`** і **`**/auto-imports.d.ts`**; інші glob-и додавай за потреби (згенеровані каталоги тощо) — канон задає мінімум, локальні розширення дозволені.
109
115
 
110
- Також потрібно прибрати, якщо є в проєкті, модуль **`@nitra/prettier-config`**, **prettier** та всі виклики prettier і налаштування для нього.
116
+ Також потрібно прибрати, якщо є в проєкті, модуль **`@nitra/prettier-config`**, **prettier** та всі виклики prettier і налаштування для нього. Канон заборонених top-level/`dependencies`/`devDependencies` (prettier, `@nitra/prettier-config`, `markdownlint-cli2`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
111
117
 
112
118
  Завжди пиши **JSDoc** до функцій та методів.
113
119
 
@@ -156,6 +162,8 @@ version: '1.27'
156
162
  }
157
163
  ```
158
164
 
165
+ Канон: [.markdownlint-cli2.jsonc.snippet.jsonc](./policy/markdownlint/template/.markdownlint-cli2.jsonc.snippet.jsonc)
166
+
159
167
  **MD041** off навмисно (`.mdc` з frontmatter). Деталі — [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2).
160
168
 
161
169
  ## Cspell
@@ -238,6 +246,8 @@ jobs:
238
246
  }
239
247
  ```
240
248
 
249
+ Канон базових ключів `.cspell.json` (`version`, `ignorePaths`): [.cspell.json.snippet.json](./policy/cspell/template/.cspell.json.snippet.json). Обовʼязковий запис у `import`: [.cspell.json.contains.json](./policy/cspell/template/.cspell.json.contains.json). Заборонено імпортувати окремі `@cspell/dict-*` у `.cspell.json`: [.cspell.json.deny.json](./policy/cspell/template/.cspell.json.deny.json).
250
+
241
251
  ```json title="package.json"
242
252
  {
243
253
  "scripts": {
@@ -28,8 +28,9 @@ async function parseByExt(path) {
28
28
  }
29
29
 
30
30
  function stripJsonComments(s) {
31
- // Minimal: strip // line comments and /* */ block comments. JSON-with-comments format.
32
- return s.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '')
31
+ // Match string literals OR comments. Strings are returned unchanged so we never
32
+ // strip `/*` / `//` / `*/` that appear inside values (e.g. glob `**/node_modules/**`).
33
+ return s.replace(/"(?:\\.|[^"\\])*"|\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, m => (m.startsWith('"') ? m : ''))
33
34
  }
34
35
 
35
36
  async function walk(dir, base = dir) {