@nitra/cursor 1.9.2 → 1.9.4

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,11 +4,23 @@
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.4] - 2026-05-11
8
+
9
+ ### Removed
10
+
11
+ - **graphql — вимога `scripts.dump-schema` у `package.json` прибрана:** правило `graphql.mdc` більше не вимагає канонічний скрипт `dump-schema` (раніше — `bunx graphqurl http://localhost:4040/v1/graphql -H 'X-Hasura-Admin-Secret: secret' --introspect > schema.graphql`) у корені проєкту за наявності gql tagged template literals. У `.mdc` відповідну буліт-точку та JSON-фрагмент видалено; фраза про «стандартний спосіб оновлення локальної `schema.graphql`» теж прибрана з підсумкового речення. Каталог `npm/policy/graphql/` (єдиний файл `package_json/package_json.rego` з deny-правилами на відсутність/неканонічний `scripts.dump-schema`) видалено повністю. Запис реєстру `graphql.package_json` (policyDir `graphql`, rule `graphql`, single `package.json`) прибрано з `npm/scripts/lint-conftest.mjs` (заголовок секції перейменовано — `graphql` вилучено). JSDoc-преамбулу `npm/scripts/check-graphql.mjs` оновлено: видалено абзац про rego-порт перевірки `dump-schema` і згадку `scripts.dump-schema` з докстрингу `check()`. Сам JS-чек і так не торкався `package.json` — після видалення rego-полісі ніяких runtime-перевірок `dump-schema` не лишається. У кореневому `package.json` репо cursor скрипт `dump-schema` теж видалено, оскільки тримати його як shim без правила немає сенсу.
12
+
13
+ ## [1.9.3] - 2026-05-11
14
+
15
+ ### Fixed
16
+
17
+ - **k8s — `pathHasK8sSegment` тепер відносно кореня репо; `.github/` явно поза скоупом:** функція `pathHasK8sSegment(filePath)` у `npm/scripts/check-k8s.mjs` та `npm/scripts/run-k8s.mjs` розбивала **абсолютний** шлях і шукала компонент `k8s`. У проєктах, де сам корінь репо називається `k8s/` (напр. `/Users/.../abie/k8s/`), сегмент `k8s` присутній в абсолютному шляху **усіх** файлів — і весь репозиторій, включно з `.github/workflows/*.yml`, потрапляв у `findK8sYamlFiles` як k8s-маніфести, після чого `checkK8sYamlFile` падав на «розширення .yml — перейменуй на .yaml» (територія `ga.mdc`, де канон протилежний). Виправлено: (1) сигнатура тепер `pathHasK8sSegment(filePath, root?)` — коли `root` передано, шлях спершу нормалізується через `node:path` `relative(root, filePath)`, і компоненти беруться **відносно кореня** (порожній relative — це сам root, повертає false); (2) `findK8sYamlFiles` у `check-k8s.mjs` і `check-abie.mjs`, а також `findK8sRoots` у `run-k8s.mjs` тепер передають `root` і додатково мають defense-in-depth ранній `return` для шляхів, що починаються з `.github/`; (3) `k8s.mdc` явно фіксує: правило стосується каталогів `k8s` відносно кореня; `.github/workflows/` і `.github/actions/` — поза скоупом (їх веде `ga.mdc`). Без `root` (юніт-тести з відносним шляхом) функція веде себе як раніше. Додано тести у `tests/check-k8s-schema.test.mjs` (worst-case з префіксом `/home/test/some/k8s/`) і `tests/run-k8s-roots.test.mjs` (інтеграційний — `findK8sRoots` у репі, корінь якого називається `k8s/`).
18
+
7
19
  ## [1.9.2] - 2026-05-11
8
20
 
9
21
  ### Changed
10
22
 
11
- - **k8s — modeline `$schema` тепер опційний; `file:…` заборонено як заглушку:** правило `k8s.mdc` уточнено — рядок `# yaml-language-server: $schema=…` обов'язковий **лише** коли для поєднання `apiVersion`/`kind` існує надійна публічна схема (kustomization / yannh / datree CRDs-catalog). Якщо публічної схеми немає, modeline **не додається зовсім** (раніше п. 5 розділу «Визначення схеми YAML» допускав `file:` за узгодженням — це створювало фальшиву видимість валідації, а автовиправлення n-fix залишало заглушковий `# yaml-language-server: $schema=file:.`). У `check-k8s.mjs`: (1) файли без modeline більше не падають як «перший рядок має бути коментарем», натомість `pass` із позначкою «без modeline — перевірка $schema пропущена»; (2) `$schema=file:…` тепер реєструється як помилка з підказкою прибрати modeline; (3) modeline нижче першого рядка все ще порушення; (4) `HttpBackendGroup` (Yandex ALB) як виняток без modeline залишається без змін. `lint-k8s` (kubeconform з прапорцем ignore-missing-schemas) продовжує покривати валідацію і для файлів без modeline. JSDoc на початку `check-k8s.mjs` оновлено.
23
+ - **k8s — modeline `$schema` тепер опційний; `file:…` заборонено як плейсхолдер:** правило `k8s.mdc` уточнено — рядок `# yaml-language-server: $schema=…` обов'язковий **лише** коли для поєднання `apiVersion`/`kind` існує надійна публічна схема (kustomization / yannh / datree CRDs-catalog). Якщо публічної схеми немає, modeline **не додається зовсім** (раніше п. 5 розділу «Визначення схеми YAML» допускав `file:` за узгодженням — це створювало фальшиву видимість валідації, а автовиправлення n-fix залишало плейсхолдер `# yaml-language-server: $schema=file:.`). У `check-k8s.mjs`: (1) файли без modeline більше не падають як «перший рядок має бути коментарем», натомість `pass` із позначкою «без modeline — перевірка $schema пропущена»; (2) `$schema=file:…`тепер реєструється як помилка з підказкою прибрати modeline; (3) modeline нижче першого рядка все ще порушення; (4)`HttpBackendGroup`(Yandex ALB) як виняток без modeline залишається без змін.`lint-k8s`(kubeconform з прапорцем ignore-missing-schemas) продовжує покривати валідацію і для файлів без modeline. JSDoc на початку`check-k8s.mjs` оновлено.
12
24
 
13
25
  ## [1.9.1] - 2026-05-11
14
26
 
package/mdc/graphql.mdc CHANGED
@@ -8,18 +8,9 @@ alwaysApply: false
8
8
  Якщо в **`.vue`** або в **JavaScript / TypeScript** джерелах (`.js`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.jsx` тощо) зустрічається **tagged template literal** з тегом **`gql`** (типово `gql\`query …\`` для GraphQL-запиту), у **корені репозиторію** мають бути:
9
9
 
10
10
  - файл **`.graphqlrc.yml`** ([GraphQL Config](https://the-guild.dev/graphql/config/docs));
11
- - у **`.vscode/extensions.json`** в масиві **`recommendations`** — запис **`graphql.vscode-graphql`**;
12
- - у кореневому **`package.json`** скрипт:
11
+ - у **`.vscode/extensions.json`** в масиві **`recommendations`** — запис **`graphql.vscode-graphql`**.
13
12
 
14
- ```json title="package.json (фрагмент)"
15
- {
16
- "scripts": {
17
- "dump-schema": "bunx graphqurl http://localhost:4040/v1/graphql -H 'X-Hasura-Admin-Secret: secret' --introspect > schema.graphql"
18
- }
19
- }
20
- ```
21
-
22
- Це забезпечує підсвітку та діагностику GraphQL в редакторі, а також стандартний спосіб оновлення локальної `schema.graphql`.
13
+ Це забезпечує підсвітку та діагностику GraphQL в редакторі.
23
14
 
24
15
  Деталі виявлення `gql` у скриптах (у т.ч. лише `<script>` у SFC) — **`npm/scripts/check-graphql.mjs`** / **`npm/scripts/utils/graphql-gql-scan.mjs`**.
25
16
 
@@ -28,7 +19,7 @@ alwaysApply: false
28
19
  Підстав свої шляхи до схеми та до файлів з операціями; приклад орієнтиру:
29
20
 
30
21
  ```yaml title=".graphqlrc.yml"
31
- schema: schema.graphql
22
+ schema: node_modules/@nitra/efes-docs/schema/maya.graphql
32
23
  documents:
33
24
  - '**/*.{vue,js,ts,tsx}'
34
25
  ```
package/mdc/k8s.mdc CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: K8s YAML — $schema (yaml-language-server); lint-k8s (kubeconform, kubescape); check-k8s
3
- version: '1.28'
3
+ version: '1.29'
4
4
  globs: "**/k8s/**/*.yaml"
5
5
  alwaysApply: false
6
6
  ---
@@ -21,6 +21,8 @@ alwaysApply: false
21
21
 
22
22
  **Розширення:** усі маніфести під **`k8s`**, включно з **`kustomization.yaml`**, — лише **`.yaml`** (розширення **`.yml`** не використовуй).
23
23
 
24
+ **Скоп — поза `.github/`:** правило стосується YAML-маніфестів у каталогах **`k8s`** (визначаються відносно кореня репо, не за абсолютним шляхом). Файли під **`.github/workflows/`** та **`.github/actions/`** ця перевірка **не** зачіпає — їхній скоп визначає **`ga.mdc`** (там канон — **`.yml`**). Це робить два правила несуперечливими навіть у проєктах, де сам корінь репо випадково має ім'я `k8s/` (тоді сегмент `k8s` присутній у абсолютному шляху всіх файлів, але **відносно кореня** його там немає).
25
+
24
26
  **Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor check docker`**.
25
27
 
26
28
  ## lint-k8s: kubeconform і kubescape
package/mdc/tauri.mdc CHANGED
@@ -14,7 +14,6 @@ version: '1.0'
14
14
  }
15
15
  ```
16
16
 
17
-
18
17
  ## Перевірка
19
18
 
20
- `npx @nitra/cursor check tauri`
19
+ `npx @nitra/cursor check tauri`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -323,7 +323,12 @@ async function findK8sYamlFiles(root, ignorePaths = []) {
323
323
  await walkDir(
324
324
  root,
325
325
  p => {
326
- if (!pathHasK8sSegment(p)) {
326
+ const rel = relative(root, p).replaceAll('\\', '/')
327
+ // `.github/` належить `ga.mdc`; check-abie не зачіпає workflow-файли.
328
+ if (rel.startsWith('.github/')) {
329
+ return
330
+ }
331
+ if (!pathHasK8sSegment(p, root)) {
327
332
  return
328
333
  }
329
334
  if (!YAML_EXTENSION_RE.test(p)) {
@@ -760,9 +765,9 @@ async function collectDeploymentDirs(root, yamlAbs, fail) {
760
765
  * @param {string[]} yamlFilesAbs yaml під k8s
761
766
  * @param {(msg: string) => void} fail callback
762
767
  * @param {(msg: string) => void} passFn успішне повідомлення
763
- * @returns {Promise<void>}
768
+ * @returns {void}
764
769
  */
765
- async function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fail, passFn) {
770
+ function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fail, passFn) {
766
771
  const baseFiles = yamlFilesAbs.filter(abs => isAbieK8sBaseYamlPath(relative(root, abs).replaceAll('\\', '/') || abs))
767
772
  if (baseFiles.length === 0) {
768
773
  passFn('Немає файлів у шляхах …/base/… — перевірку preem у base пропущено')
@@ -1425,9 +1430,9 @@ async function checkHttpRouteKustomization(abs, rel, mode, root, yamlFilesAbs, c
1425
1430
  * @param {string[]} yamlFilesAbs yaml під k8s
1426
1431
  * @param {(msg: string) => void} fail callback при помилці
1427
1432
  * @param {(msg: string) => void} passFn callback при успішній перевірці
1428
- * @returns {Promise<void>}
1433
+ * @returns {void}
1429
1434
  */
1430
- async function ensureAbieBaseHttpRouteHostnames(root, yamlFilesAbs, fail, passFn) {
1435
+ function ensureAbieBaseHttpRouteHostnames(root, yamlFilesAbs, fail, passFn) {
1431
1436
  const baseFiles = yamlFilesAbs.filter(abs => isAbieK8sBaseYamlPath(relative(root, abs).replaceAll('\\', '/') || abs))
1432
1437
  if (baseFiles.length === 0) {
1433
1438
  passFn('Немає файлів у шляхах …/k8s/base/… — перевірку HTTPRoute hostnames пропущено')
@@ -1531,8 +1536,9 @@ async function ensureNoFirebaseHostingArtifacts(root, passFn, failFn) {
1531
1536
  * @param {string} root корінь репозиторію
1532
1537
  * @param {(msg: string) => void} pass callback при успішній перевірці
1533
1538
  * @param {(msg: string) => void} fail callback при помилці
1539
+ * @returns {void}
1534
1540
  */
1535
- async function checkCleanMergedBranch(root, pass, fail) {
1541
+ function checkCleanMergedBranch(root, pass, fail) {
1536
1542
  const cleanMergedPath = join(root, '.github/workflows/clean-merged-branch.yml')
1537
1543
  if (!existsSync(cleanMergedPath)) {
1538
1544
  fail(`Відсутній ${cleanMergedPath} — потрібен для ignore_branches (abie.mdc)`)
@@ -365,9 +365,9 @@ const GA_PER_WORKFLOW_REGO_TARGETS = [
365
365
  * @param {string[]} ymlWorkflows відносні (від `wfDir`) імена файлів `*.yml`
366
366
  * @param {(msg: string) => void} pass callback при успішній перевірці
367
367
  * @param {(msg: string) => void} fail callback при помилці
368
- * @returns {Promise<void>}
368
+ * @returns {void}
369
369
  */
370
- async function runAllGaRego(wfDir, ymlWorkflows, pass, fail) {
370
+ function runAllGaRego(wfDir, ymlWorkflows, pass, fail) {
371
371
  for (const target of GA_PER_WORKFLOW_REGO_TARGETS) {
372
372
  if (!existsSync(target.workflow)) continue
373
373
  const violations = runConftestBatch({
@@ -5,10 +5,6 @@
5
5
  * Обхід репозиторію — **`walkDir`** від **`process.cwd()`** (пропуски як у інших check). Кандидати — **`.vue`** та **`.js`/`.ts`/`.jsx`/`.tsx`** тощо; пропуск **`.d.ts`**, **auto-imports.d.ts** тощо — **`shouldSkipFileForGqlScan`**.
6
6
  *
7
7
  * Виявлення **`gql`** — **oxc-parser** після витягування `<script>` з SFC (**`graphql-gql-scan.mjs`**). Якщо збігів немає — перевірка завершується успішно без вимог до конфігів.
8
- *
9
- * Перевірку `scripts.dump-schema == REQUIRED_DUMP_SCHEMA_SCRIPT` у `package.json`
10
- * перенесено в Rego (`npm/policy/graphql/package_json/`); `bun run lint-conftest`
11
- * запускає її окремо.
12
8
  */
13
9
  import { existsSync } from 'node:fs'
14
10
  import { readFile } from 'node:fs/promises'
@@ -107,8 +103,8 @@ async function checkExtensionsRecommendation(pass, fail) {
107
103
  }
108
104
 
109
105
  /**
110
- * Перевіряє graphql.mdc: умовна вимога .graphqlrc.yml, graphql.vscode-graphql
111
- * і scripts.dump-schema за наявності gql tagged templates.
106
+ * Перевіряє graphql.mdc: умовна вимога .graphqlrc.yml і graphql.vscode-graphql
107
+ * за наявності gql tagged templates.
112
108
  * @returns {Promise<number>} 0 — OK, 1 — порушення
113
109
  */
114
110
  export async function check() {
@@ -349,11 +349,23 @@ const BATCH_V1BETA1_API_VERSION_LINE_RE = /^(\s*apiVersion:\s*)["']?batch\/v1bet
349
349
 
350
350
  /**
351
351
  * Чи містить шлях сегмент директорії `k8s` (рівно ця назва компонента).
352
- * @param {string} filePath шлях до файлу
353
- * @returns {boolean} true, якщо серед компонентів шляху є каталог `k8s`
354
- */
355
- export function pathHasK8sSegment(filePath) {
356
- const parts = filePath.split(PATH_SPLIT_RE)
352
+ *
353
+ * Якщо передано `root`, перевірка ведеться **відносно** кореня репо інакше випадає
354
+ * false-positive, коли сам корінь репо вже містить компонент `k8s` (напр.
355
+ * `/Users/.../abie/k8s/`): без relativize функція б повертала true для **усіх** файлів
356
+ * у проєкті, включно з `.github/workflows/*.yml`, які належать іншому правилу (`ga.mdc`).
357
+ *
358
+ * Без `root` (як у юніт-тестах або коли шлях уже відносний) спрацьовує старий шлях:
359
+ * розбиття за `/`/`\` і пошук компонента `k8s`.
360
+ * @param {string} filePath абсолютний або відносний шлях до файлу
361
+ * @param {string} [root] корінь репо для relativize (типово — без relativize)
362
+ * @returns {boolean} true, якщо серед компонентів шляху **відносно root** є каталог `k8s`
363
+ */
364
+ export function pathHasK8sSegment(filePath, root) {
365
+ const target = root ? relative(root, filePath).replaceAll('\\', '/') : filePath
366
+ // Порожній relative означає сам root — у ньому компонента `k8s` відносно себе немає.
367
+ if (target === '') return false
368
+ const parts = target.split(PATH_SPLIT_RE)
357
369
  return parts.includes('k8s')
358
370
  }
359
371
 
@@ -1723,7 +1735,11 @@ async function findK8sYamlFiles(root, ignorePaths = []) {
1723
1735
  await walkDir(
1724
1736
  root,
1725
1737
  p => {
1726
- if (!pathHasK8sSegment(p)) return
1738
+ const rel = relative(root, p).replaceAll('\\', '/')
1739
+ // `.github/` належить правилу `ga.mdc` (там канон — `.yml`); не зачіпай тут навіть
1740
+ // якщо `pathHasK8sSegment` колись зіб'ється на крайовому кейсі.
1741
+ if (rel.startsWith('.github/')) return
1742
+ if (!pathHasK8sSegment(p, root)) return
1727
1743
  if (!YAML_EXTENSION_RE.test(p)) return
1728
1744
  out.push(p)
1729
1745
  },
@@ -2890,18 +2906,11 @@ export function collectGatewayApiRouteBackendRefsWithRedundantNamespace(spec, ro
2890
2906
  return out
2891
2907
  }
2892
2908
 
2893
- /**
2894
- * Один документ: маршрут Gateway API має посилатися на **Service** з суфіксом **`-hl`**;
2895
- * у **`backendRef`** не має дублюватися **`namespace`**, що збігається з **`metadata.namespace`** маршруту.
2896
- * @param {string} rel відносний шлях до файлу
2897
- * @param {number} docIndex 1-based індекс документа
2898
- * @param {Record<string, unknown>} rec корінь маніфесту
2899
- * @param {(msg: string) => void} fail callback помилки
2900
- * @returns {void}
2901
- */
2902
2909
  // Plan B: Gateway API маршрут backendRef з суфіксом `-hl` і redundant namespace —
2903
2910
  // у rego-пакеті `k8s.gateway`, виклик через `runAllK8sRego`. JS-функції
2904
- // failIfGatewayRouteUsesNonHeadlessService, scanGatewayApiRouteBackendRefsInYamlBody видалено.
2911
+ // failIfGatewayRouteUsesNonHeadlessService, scanGatewayApiRouteBackendRefsInYamlBody видалено;
2912
+ // JSDoc-блок для них прибрано (eslint-plugin-jsdoc trip-ив на «orphan» JSDoc, який
2913
+ // прилипав до asPlainRecord як другий @returns).
2905
2914
 
2906
2915
  /**
2907
2916
  * Звузити `unknown` до `Record<string, unknown>` (`null`, масиви, примітиви → null).
@@ -3552,13 +3561,12 @@ function countSchemaModelines(lines) {
3552
3561
  return lines.filter(l => OXLINT_SCHEMA_MODELINE_RE.test(l.trim())).length
3553
3562
  }
3554
3563
 
3555
-
3556
3564
  /**
3557
3565
  * Файл з першим документом **HttpBackendGroup** (ALB Yandex): без modeline **$schema**.
3558
3566
  * @param {string} rel відносний шлях
3559
- * @param {string} baseLower basename
3560
- * @param {string[]} lines рядки файлу
3561
- * @param {(msg: string) => void} fail реєстрація помилки
3567
+ * @param {string} _baseLower basename (лишений для уніфікованої сигнатури `checkK8sYamlFile*`)
3568
+ * @param {string[]} _lines рядки файлу (лишені з тієї ж причини)
3569
+ * @param {(msg: string) => void} _fail реєстрація помилки (rego гейтує per-document)
3562
3570
  * @param {(msg: string) => void} pass реєстрація успіху
3563
3571
  * @returns {void}
3564
3572
  */
@@ -3679,9 +3687,7 @@ async function checkK8sYamlFile(abs, root, fail, pass) {
3679
3687
  // Але `# yaml-language-server: $schema=…` дозволено **лише** у першому рядку — якщо він
3680
3688
  // зустрічається нижче, це порушення (yaml-language-server чекає на нього у заголовку файлу).
3681
3689
  if (countSchemaModelines(lines) > 0) {
3682
- fail(
3683
- `${rel}: рядок # yaml-language-server: $schema=… має бути першим у файлі (без префіксів перед #; k8s.mdc)`
3684
- )
3690
+ fail(`${rel}: рядок # yaml-language-server: $schema=… має бути першим у файлі (без префіксів перед #; k8s.mdc)`)
3685
3691
  return
3686
3692
  }
3687
3693
  pass(`${rel}: без modeline — перевірка $schema пропущена (немає публічної схеми; k8s.mdc)`)
@@ -5965,6 +5971,10 @@ function runAllK8sRego(root, yamlFiles, fail) {
5965
5971
  }
5966
5972
  }
5967
5973
 
5974
+ /**
5975
+ * Точка входу `check k8s`: повний набір перевірок маніфестів і структури `…/k8s` (див. JSDoc на початку файлу).
5976
+ * @returns {Promise<number>} `process.exitCode`: 0 при успіху, 1 при будь-якому `fail(...)`
5977
+ */
5968
5978
  export async function check() {
5969
5979
  const reporter = createCheckReporter()
5970
5980
  const { pass, fail } = reporter
@@ -157,8 +157,7 @@ const TARGETS = [
157
157
  single: '.github/workflows/lint-js.yml'
158
158
  },
159
159
 
160
- // ── graphql / image-compress / capacitor ────────────────────────────────
161
- { namespace: 'graphql.package_json', policyDir: 'graphql', rule: 'graphql', single: 'package.json' },
160
+ // ── image-compress / capacitor ──────────────────────────────────────────
162
161
  {
163
162
  namespace: 'image_compress.package_json',
164
163
  policyDir: 'image_compress',
@@ -13,7 +13,7 @@
13
13
  * Kubescape не має аналога цього прапорця; орієнтир цільового кластера — та сама лінія релізу (див. k8s.mdc).
14
14
  */
15
15
  import { spawnSync } from 'node:child_process'
16
- import { basename, dirname } from 'node:path'
16
+ import { basename, dirname, relative } from 'node:path'
17
17
 
18
18
  import { isRunAsCli } from './cli-entry.mjs'
19
19
  import { loadCursorIgnorePaths } from './utils/load-cursor-config.mjs'
@@ -31,12 +31,19 @@ const DATREE_CRD_SCHEMA_LOCATION =
31
31
  'https://datreeio.github.io/CRDs-catalog/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'
32
32
 
33
33
  /**
34
- * Чи містить шлях сегмент директорії `k8s`.
35
- * @param {string} filePath шлях до файлу
36
- * @returns {boolean} true, якщо серед компонентів шляху є каталог `k8s`
34
+ * Чи містить шлях сегмент директорії `k8s` (відносно `root`, якщо передано).
35
+ *
36
+ * Якщо корінь репо сам має компонент `k8s` (напр. `/Users/.../abie/k8s/`), без relativize
37
+ * функція повертала б true для **усіх** файлів проєкту — включно з `.github/workflows/*.yml`,
38
+ * які належать `ga.mdc`. Передавай `root` у викликах з walkDir щоб уникнути false-positive.
39
+ * @param {string} filePath абсолютний або відносний шлях до файлу
40
+ * @param {string} [root] корінь репо для relativize (типово — без relativize)
41
+ * @returns {boolean} true, якщо серед компонентів шляху **відносно root** є каталог `k8s`
37
42
  */
38
- export function pathHasK8sSegment(filePath) {
39
- const parts = filePath.split(PATH_SEPARATOR_RE)
43
+ export function pathHasK8sSegment(filePath, root) {
44
+ const target = root ? relative(root, filePath).replaceAll('\\', '/') : filePath
45
+ if (target === '') return false
46
+ const parts = target.split(PATH_SEPARATOR_RE)
40
47
  return parts.includes('k8s')
41
48
  }
42
49
 
@@ -68,7 +75,10 @@ export async function findK8sRoots(root, ignorePaths = []) {
68
75
  await walkDir(
69
76
  root,
70
77
  p => {
71
- if (!pathHasK8sSegment(p)) return
78
+ const rel = relative(root, p).replaceAll('\\', '/')
79
+ // `.github/` належить `ga.mdc`; kubeconform/kubescape там не запускаємо.
80
+ if (rel.startsWith('.github/')) return
81
+ if (!pathHasK8sSegment(p, root)) return
72
82
  if (!YAML_EXT_RE.test(p)) return
73
83
  const k8sRoot = k8sRootFromFile(p)
74
84
  if (k8sRoot) roots.add(k8sRoot)
@@ -30,7 +30,7 @@ const POLICY_ROOT = join(PACKAGE_ROOT, 'policy')
30
30
  /**
31
31
  * Друкує install-hint для conftest і кидає виняток, щоб викликана `check-*`
32
32
  * команда ясно завершилась з кодом 1.
33
- * @returns {never}
33
+ * @returns {never} завжди кидає; для точки виклику — non-returning
34
34
  */
35
35
  function failConftestMissing() {
36
36
  throw new Error(
@@ -94,9 +94,7 @@ export function runConftestBatch(opts) {
94
94
  }
95
95
  // conftest exit 1 = є failures (це валідно для нас); >1 = справжня помилка.
96
96
  if (result.status !== 0 && result.status !== 1) {
97
- throw new Error(
98
- `conftest exit ${result.status}: ${(result.stderr || result.stdout || '').slice(0, 500)}`
99
- )
97
+ throw new Error(`conftest exit ${result.status}: ${(result.stderr || result.stdout || '').slice(0, 500)}`)
100
98
  }
101
99
  /** @type {Array<{ filename: string, namespace: string, failures?: Array<{ msg: string }> }>} */
102
100
  let parsed
@@ -1,35 +0,0 @@
1
- # Порт перевірки `package.json` з `npm/scripts/check-graphql.mjs` (graphql.mdc).
2
- #
3
- # Запуск (локально, ЯКЩО проект містить `gql\`…\`` теги — gating робить JS-частина
4
- # через oxc-parser-скан):
5
- # conftest test package.json -p npm/policy/graphql --namespace graphql.package_json
6
- #
7
- # Перевіряє: `scripts.dump-schema` точно відповідає канону graphql.mdc.
8
- #
9
- # AST-скан коду на `gql\`…\`` template literals і FS-перевірки (наявність
10
- # `.graphqlrc.yml`, `.vscode/extensions.json` з `graphql.vscode-graphql`) — у JS.
11
- #
12
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
13
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
14
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
15
- package graphql.package_json
16
-
17
- import rego.v1
18
-
19
- required_dump_schema := concat("", [
20
- "bunx graphqurl http://localhost:4040/v1/graphql ",
21
- "-H 'X-Hasura-Admin-Secret: secret' --introspect > schema.graphql",
22
- ])
23
-
24
- deny contains msg if {
25
- scripts := object.get(input, "scripts", {})
26
- not "dump-schema" in object.keys(scripts)
27
- msg := "package.json: відсутній scripts.dump-schema (graphql.mdc)"
28
- }
29
-
30
- deny contains msg if {
31
- dump := object.get(object.get(input, "scripts", {}), "dump-schema", "")
32
- dump != ""
33
- dump != required_dump_schema
34
- msg := sprintf("package.json: scripts.dump-schema має бути канонічним з graphql.mdc (зараз %q)", [dump])
35
- }