@nitra/cursor 1.8.155 → 1.8.157

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 ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ Усі помітні зміни цього модуля документуються тут.
4
+
5
+ Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
+
7
+ ## [1.8.157] - 2026-04-30
8
+
9
+ ### Added
10
+
11
+ - Правило `npm-module.mdc`: секція **CHANGELOG** — разом із bump build-версії в `npm/package.json` обовʼязково оновлювати `npm/CHANGELOG.md` (Keep a Changelog).
12
+ - `check-npm-module.mjs`: перевірка наявності `npm/CHANGELOG.md`, наявності в `files` у `npm/package.json` і запису для поточної версії.
13
+ - `check-hasura.mjs`: перевірка `HASURA_GRAPHQL_ENDPOINT` у `*.env` для проєктів **nitra** і **abie** — має бути внутрішнім кластерним URL виду `http://<service>.<namespace>.svc.<cluster>.internal:<port>`; за наявності `hasura/k8s/base/svc-hl.yaml` та `hasura/k8s/base/namespace.yaml` додатково звіряється `<service>` і `<namespace>`.
14
+
15
+ ### Changed
16
+
17
+ - `npm/package.json`: `CHANGELOG.md` додано в масив `files`, щоб публікувався разом із пакетом.
18
+ - `hasura.mdc`: текст правила переформульовано як людинозрозумілий з прикладом і посиланням на `check-hasura.mjs`.
package/bin/auto-rules.md CHANGED
@@ -16,6 +16,8 @@ ga - якщо присутня директорія .github/workflows
16
16
 
17
17
  graphql - якщо хоч в одному js або vue файлі присутній gql` темплейт літерал
18
18
 
19
+ hasura - якщо в директорії присутній config.yaml, який містить рядок `metadata_directory: metadata`
20
+
19
21
  js-lint - якщо присутній хоч один js файл
20
22
 
21
23
  js-run - якщо це вкладена директорія з package.json (не в корені) та в devDependencies немає vite
package/mdc/hasura.mdc ADDED
@@ -0,0 +1,31 @@
1
+ ---
2
+ description: Правила для директорії з hasura graphql-engine
3
+ alwaysApply: true
4
+ version: '1.0'
5
+ ---
6
+
7
+ ## Підключення для оновлення метаданих у CI (Nitra та Abinbevefes)
8
+
9
+ У `*.env` для атрибута `HASURA_GRAPHQL_ENDPOINT` підключення має бути **усередині кластера**, а не через публічний домен — інакше CI кладе метадані через зовнішній бекенд і ламає перевипуск/міграції.
10
+
11
+ Приклад **неправильного** значення:
12
+
13
+ ```env
14
+ HASURA_GRAPHQL_ENDPOINT=https://napitkivmeste.tech/contract/ql
15
+ ```
16
+
17
+ Правильне значення:
18
+
19
+ ```env
20
+ HASURA_GRAPHQL_ENDPOINT=http://contract-h.ua-contract.svc.abie-ua.internal:8080
21
+ ```
22
+
23
+ де `contract-h` — це `metadata.name` сервісу з `hasura/k8s/base/svc-hl.yaml`, а `ua-contract` — `metadata.name` namespace з `hasura/k8s/base/namespace.yaml`.
24
+
25
+ Правило застосовується для проєктів **nitra** (у кореневому `package.json` `"repository": "https://github.com/nitra/*"`) і **abie** (`"repository": "https://github.com/abinbevefes/*"`); для інших репозиторіїв перевірка пропускається.
26
+
27
+ ## Перевірка
28
+
29
+ `npx @nitra/cursor check hasura`
30
+
31
+ Деталі алгоритму — у `check-hasura.mjs`.
package/mdc/js-lint.mdc CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  description: Перевірка JavaScript коду
3
3
  alwaysApply: true
4
- version: '1.15'
4
+ version: '1.16'
5
5
  ---
6
6
 
7
- **oxlint**, **ESLint**, **jscpd**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**. У **`devDependencies`** має бути **`@nitra/eslint-config` мінімум `^3.6.12`** (з ним транзитивно йде **`@e18e/eslint-plugin`** для oxlint); пакет **`@e18e/eslint-plugin`** окремо не додавай. Пакети oxlint/eslint/jscpd не додавай без потреби монорепо.
7
+ **oxlint**, **ESLint**, **jscpd**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**. У **`devDependencies`** має бути **`@nitra/eslint-config` мінімум `^3.8.0`** (з цієї версії правило `no-restricted-syntax` забороняє `for...in`; також транзитивно йде **`@e18e/eslint-plugin`** для oxlint); пакет **`@e18e/eslint-plugin`** окремо не додавай. Пакети oxlint/eslint/jscpd не додавай без потреби монорепо.
8
8
 
9
9
  ```json title=".vscode/extensions.json"
10
10
  {
@@ -25,12 +25,12 @@ version: '1.15'
25
25
  "lint-js": "bunx oxlint --fix && bunx eslint --fix . && bunx jscpd ."
26
26
  },
27
27
  "devDependencies": {
28
- "@nitra/eslint-config": "^3.6.12"
28
+ "@nitra/eslint-config": "^3.8.0"
29
29
  }
30
30
  }
31
31
  ```
32
32
 
33
- У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/scripts/utils/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**, **`ignorePatterns`**). Оновити можна з репозиторію пакета або скопіювавши файл після **`bun ./scripts/utils/rebuild-oxlint-canonical.mjs`** (джерело правил — **`oxlint-rules.tsv`** + скелет **`oxlint-canonical-skeleton.json`**). Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.6.12**), oxlint підвантажує його з **`node_modules`**.
33
+ У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/scripts/utils/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**, **`ignorePatterns`**). Оновити можна з репозиторію пакета або скопіювавши файл після **`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`**.
34
34
 
35
35
  Мінімум для розуміння структури (реальний корінь конфігу має збігатися з каноном повністю):
36
36
 
@@ -144,6 +144,31 @@ export default [
144
144
  }
145
145
  ```
146
146
 
147
+ ## `for...in` заборонено — рефакторити на `for...of`
148
+
149
+ Конструкція `for (const k in obj)` обходить успадковані ключі прототипу, тому майже завжди тягне `Object.hasOwn(obj, k)`-guard. Заборонена у `@nitra/eslint-config` через `no-restricted-syntax` для `ForInStatement` (з версії **3.8.0**). У каноні oxlint лишається `guard-for-in` як часткова страховка (oxlint не підтримує `no-restricted-syntax`). Замість цього обходь масив напряму, а обʼєкт — через `Object.entries` / `Object.keys` / `Object.values`. У такому коді guard за `Object.hasOwn` стає непотрібним і має зникнути разом із `for...in`.
150
+
151
+ ```javascript title="❌ до"
152
+ for (const k in obj) {
153
+ if (!Object.hasOwn(obj, k)) continue
154
+ use(k, obj[k])
155
+ }
156
+
157
+ for (const i in arr) {
158
+ use(arr[i])
159
+ }
160
+ ```
161
+
162
+ ```javascript title="✅ після"
163
+ for (const [k, v] of Object.entries(obj)) {
164
+ use(k, v)
165
+ }
166
+
167
+ for (const item of arr) {
168
+ use(item)
169
+ }
170
+ ```
171
+
147
172
  ## Тести
148
173
 
149
174
  Проєкт має бути покритий unit-тестами (**Bun test**). Код: синтаксис Node **24+**, **top level await** (узгоджено з `engines.node` у `package.json`).
package/mdc/js-mssql.mdc CHANGED
@@ -26,7 +26,7 @@ let poolPromise;
26
26
 
27
27
  export function getPool() {
28
28
  if (!poolPromise) {
29
- const pool = new sql.ConnectionPool(config);
29
+ const db = new SQL.ConnectionPool(config);
30
30
  poolPromise = pool.connect().catch(err => {
31
31
  poolPromise = undefined; // дозволити повторну спробу
32
32
  throw err;
package/mdc/js-run.mdc CHANGED
@@ -16,7 +16,6 @@ package.json
16
16
  readme.md
17
17
  ```
18
18
 
19
-
20
19
  ## Використання @nitra/pino
21
20
 
22
21
  Проект використовує @nitra/pino для логування.
@@ -53,34 +52,34 @@ import { GraphQLClient } from '@nitra/graphql-request'
53
52
  ```
54
53
 
55
54
  так виглядатиме підключення до PostgreSQL в коді:
55
+
56
56
  ```javascript title="Приклад підключення до PostgreSQL в /src/conn/pg.js"
57
57
  import { checkEnv, env } from '@nitra/check-env'
58
58
  import { SQL } from 'bun'
59
59
 
60
60
  checkEnv(['PG_CONN'])
61
61
 
62
- export const pool = new SQL({ url: env.PG_CONN })
62
+ export const db = new SQL({ url: env.PG_CONN })
63
63
 
64
64
  ```
65
65
 
66
66
  а так до GraphQL:
67
67
 
68
68
  ```js
69
- import { checkEnv } from '@nitra/check-env'
69
+ import { checkEnv, env } from '@nitra/check-env'
70
70
  import { GraphQLClient } from '@nitra/graphql-request'
71
71
 
72
72
  checkEnv(['QL', 'X_HASURA_ADMIN_SECRET'])
73
73
 
74
74
  export { gql } from '@nitra/graphql-request'
75
75
 
76
- export const graphQLClientSmart = new GraphQLClient(process.env.QL, {
76
+ export const graphQLClientSmart = new GraphQLClient(env.QL, {
77
77
  headers: {
78
- 'X-Hasura-Admin-Secret': process.env.X_HASURA_ADMIN_SECRET
78
+ 'X-Hasura-Admin-Secret': env.X_HASURA_ADMIN_SECRET
79
79
  }
80
80
  })
81
81
  ```
82
82
 
83
-
84
83
  а в коді повинно бути використано:
85
84
 
86
85
  ```js
@@ -91,12 +90,9 @@ import { pool } from '#conn/pg.js'
91
90
  import { gql, graphQLClient } from '@nitra/graphql-request'
92
91
  ```
93
92
 
94
-
95
-
96
93
  ## CheckEnv
97
94
 
98
- Усі змінні оточення, які використовуються в коді, повинні бути перевірені за допомогою `checkEnv` з пакету `@nitra/check-env`. Це гарантує, що всі необхідні змінні оточення встановлені перед запуском програми. (Виключенням можуть бути задані коментарем)
99
-
95
+ Усі змінні оточення, які використовуються в коді, повинні бути перевірені за допомогою `checkEnv` з пакету `@nitra/check-env`. Це гарантує, що всі необхідні змінні оточення встановлені перед запуском програми.
100
96
 
101
97
  ```javascript title="Приклад підключення до PostgreSQL в /src/conn/pg.js"
102
98
  import { checkEnv, env } from '@nitra/check-env'
@@ -104,12 +100,28 @@ import { SQL } from 'bun'
104
100
 
105
101
  checkEnv(['PG_CONN'])
106
102
 
107
- export const pool = new SQL({ url: env.PG_CONN })
103
+ export const db = new SQL({ url: env.PG_CONN })
108
104
 
109
- // @nitra/cursor ignore-next-line checkEnv
110
- console.log(process.env.OPTIONAL_ENV_VAR)
111
105
  ```
112
106
 
107
+ ## process.env
108
+
109
+ Прямий доступ до `process.env.X` у коді заборонений — його треба замінити на `env`:
110
+
111
+ - **обов'язкова змінна** — `import { checkEnv, env } from '@nitra/check-env'` плюс `checkEnv(['X'])`
112
+ у тому ж файлі (приклад див. вище в розділі **CheckEnv**);
113
+ - **опційна змінна** — `import { env } from 'node:process'`:
114
+
115
+ ```javascript title="Опційна змінна — env з node:process"
116
+ import { env } from 'node:process'
117
+
118
+ console.log(env.OPTIONAL_ENV_VAR)
119
+ ```
120
+
121
+ Тимчасово приглушити перевірку для конкретного рядка можна коментарем
122
+ `// @nitra/cursor ignore-next-line checkEnv` безпосередньо перед використанням
123
+ (escape-hatch для legacy-коду, не для нових файлів).
124
+
113
125
  ## Перевірка
114
126
 
115
127
  `npx @nitra/cursor check js-run`
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Оформлення репозиторію для npm модуля
3
3
  alwaysApply: true
4
- version: '1.7'
4
+ version: '1.8'
5
5
  ---
6
6
 
7
7
  Bun monorepo: workspace **`npm/`**, кореневий **`package.json`**, **`.github/workflows/`**; опційно **`demo/`**.
@@ -49,6 +49,14 @@ bunx -p typescript tsc src/**/*.js --declaration --allowJs --emitDeclarationOnly
49
49
 
50
50
  **Підказка:** щоб не дублювати bump і бачити різницю зі збереженим деревом, перевір `git status npm/package.json` або `git diff HEAD -- npm/package.json` перед другим підвищенням у тій самій гілці / наборі змін.
51
51
 
52
+ ## CHANGELOG
53
+
54
+ Разом із підвищенням **build**-версії в **`npm/package.json`** оновлюй **`npm/CHANGELOG.md`** — додавай новий запис із номером версії та коротким описом змін у модулі. Без оновлення `CHANGELOG.md` зміни в `npm/` зливати в `main` не можна.
55
+
56
+ Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/) (новіші версії зверху, мова — українська). Кожен запис починається з рядка `## [версія] - YYYY-MM-DD` і має одну або кілька секцій: `### Added`, `### Changed`, `### Fixed`, `### Removed`.
57
+
58
+ Файл **`CHANGELOG.md`** має бути в масиві **`files`** у **`npm/package.json`**, щоб публікувався разом із пакетом.
59
+
52
60
  ## npm publish
53
61
 
54
62
  **`npm-publish.yml`:** push у **`main`**, **`on.push.paths`** з **`npm/**`**, **`JS-DevTools/npm-publish@v4.1.5`**, **`with.package: npm/package.json`**, **`permissions.id-token: write`** (OIDC на npm).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.155",
3
+ "version": "1.8.157",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -30,7 +30,8 @@
30
30
  "schemas",
31
31
  "scripts",
32
32
  "skills",
33
- "AGENTS.template.md"
33
+ "AGENTS.template.md",
34
+ "CHANGELOG.md"
34
35
  ],
35
36
  "type": "module",
36
37
  "types": "./types/bin/n-cursor.d.ts",
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Модуль аналізує дерево проєкту (наявність файлів/директорій, `gql\`...\`` у source,
5
5
  * залежності `mssql` / `pg` / `pg-format` / `mysql2` у `package.json`, імпорт `sql`/`SQL` з `bun`, кореневий
6
- * `package.json`) та повертає ідентифікатори правил і skills, які потрібно автододати.
6
+ * `package.json`, `config.yaml` з рядком `metadata_directory: metadata` для hasura)
7
+ * та повертає ідентифікатори правил і skills, які потрібно автододати.
7
8
  *
8
9
  * Також враховує винятки `disable-rules` і `disable-skills`: елементи з цих списків не
9
10
  * додаються автоматично.
@@ -28,6 +29,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
28
29
  'docker',
29
30
  'ga',
30
31
  'graphql',
32
+ 'hasura',
31
33
  'js-lint',
32
34
  'js-mssql',
33
35
  'js-bun-db',
@@ -45,6 +47,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
45
47
  export const AUTO_SKILL_ORDER = Object.freeze(['abie-kustomize', 'fix', 'lint'])
46
48
 
47
49
  const ABIE_REPOSITORY_URL_MARKER = 'https://github.com/abinbevefes/'
50
+ const HASURA_CONFIG_MARKER = 'metadata_directory: metadata'
48
51
  const JS_LIKE_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx)$/iu
49
52
  const STYLE_RE = /\.(?:css|vue)$/iu
50
53
  const VUE_RE = /\.vue$/iu
@@ -138,6 +141,24 @@ async function collectDependencyKeysPresentInPackageJsonTree(root, dependencyKey
138
141
  return found
139
142
  }
140
143
 
144
+ /**
145
+ * Перевіряє один package.json: повертає true, якщо в `devDependencies` немає `vite`.
146
+ * @param {string} absPath абсолютний шлях до package.json
147
+ * @returns {Promise<boolean>} true, якщо vite відсутній у devDependencies
148
+ */
149
+ async function packageJsonLacksViteDevDependency(absPath) {
150
+ try {
151
+ const parsed = JSON.parse(await readFile(absPath, 'utf8'))
152
+ const devDeps = parsed?.devDependencies
153
+ if (!devDeps || typeof devDeps !== 'object' || Array.isArray(devDeps)) {
154
+ return true
155
+ }
156
+ return !Object.hasOwn(devDeps, 'vite')
157
+ } catch {
158
+ return false
159
+ }
160
+ }
161
+
141
162
  /**
142
163
  * Перевіряє, чи існує хоча б один вкладений `package.json` (не кореневий),
143
164
  * у якому в `devDependencies` відсутня залежність `vite`.
@@ -147,28 +168,10 @@ async function collectDependencyKeysPresentInPackageJsonTree(root, dependencyKey
147
168
  async function hasNestedPackageJsonWithoutViteDevDependency(root) {
148
169
  let result = false
149
170
 
150
- /**
151
- * Перевіряє один package.json: повертає true, якщо в `devDependencies` немає `vite`.
152
- * @param {string} absPath абсолютний шлях до package.json
153
- * @returns {Promise<boolean>} true, якщо vite відсутній у devDependencies
154
- */
155
- async function packageJsonLacksViteDevDependency(absPath) {
156
- try {
157
- const parsed = JSON.parse(await readFile(absPath, 'utf8'))
158
- const devDeps = parsed?.devDependencies
159
- if (!devDeps || typeof devDeps !== 'object' || Array.isArray(devDeps)) {
160
- return true
161
- }
162
- return !Object.hasOwn(devDeps, 'vite')
163
- } catch {
164
- return false
165
- }
166
- }
167
-
168
171
  /**
169
172
  * Рекурсивний обхід каталогу з пропуском службових директорій.
170
173
  * @param {string} dir абсолютний шлях каталогу
171
- * @returns {Promise<void>}
174
+ * @returns {Promise<void>} завершується після обходу всього піддерева або встановлення `result`
172
175
  */
173
176
  async function walk(dir) {
174
177
  if (result) return
@@ -187,11 +190,14 @@ async function hasNestedPackageJsonWithoutViteDevDependency(root) {
187
190
  }
188
191
  continue
189
192
  }
190
- if (entry.isFile() && entry.name === 'package.json' && absPath !== join(root, 'package.json')) {
191
- if (await packageJsonLacksViteDevDependency(absPath)) {
192
- result = true
193
- return
194
- }
193
+ if (
194
+ entry.isFile() &&
195
+ entry.name === 'package.json' &&
196
+ absPath !== join(root, 'package.json') &&
197
+ (await packageJsonLacksViteDevDependency(absPath))
198
+ ) {
199
+ result = true
200
+ return
195
201
  }
196
202
  }
197
203
  }
@@ -313,6 +319,26 @@ async function updateBunSqlFactFromFile(absPath, relPath, facts) {
313
319
  }
314
320
  }
315
321
 
322
+ /**
323
+ * Оновлює ознаку `hasHasuraConfig`, якщо файл — `config.yaml` із рядком
324
+ * `metadata_directory: metadata` (маркер hasura graphql-engine).
325
+ * @param {string} absPath абсолютний шлях до файлу
326
+ * @param {string} fileName базове імʼя файлу
327
+ * @param {{ hasHasuraConfig: boolean }} facts агреговані факти
328
+ * @returns {Promise<void>}
329
+ */
330
+ async function updateHasuraFactFromFile(absPath, fileName, facts) {
331
+ if (facts.hasHasuraConfig || fileName !== 'config.yaml') return
332
+ try {
333
+ const content = await readFile(absPath, 'utf8')
334
+ if (content.includes(HASURA_CONFIG_MARKER)) {
335
+ facts.hasHasuraConfig = true
336
+ }
337
+ } catch {
338
+ /* ігноруємо пошкоджені/недоступні файли */
339
+ }
340
+ }
341
+
316
342
  /**
317
343
  * Обробляє файл під час обходу дерева.
318
344
  * @param {string} absPath абсолютний шлях до файлу
@@ -322,6 +348,7 @@ async function updateBunSqlFactFromFile(absPath, relPath, facts) {
322
348
  * hasCapacitorConfig: boolean,
323
349
  * hasDockerfile: boolean,
324
350
  * hasGqlTaggedTemplates: boolean,
351
+ * hasHasuraConfig: boolean,
325
352
  * hasJsLikeSource: boolean,
326
353
  * hasNginxDefaultTplFile: boolean,
327
354
  * hasVueOrCssSource: boolean,
@@ -339,6 +366,7 @@ async function processFileEntry(absPath, root, facts) {
339
366
  if (shouldScanFileForBunSql(rel, facts)) {
340
367
  await updateBunSqlFactFromFile(absPath, rel, facts)
341
368
  }
369
+ await updateHasuraFactFromFile(absPath, fileName, facts)
342
370
  }
343
371
 
344
372
  /**
@@ -407,6 +435,7 @@ export function isMonorepoPackage(packageJson) {
407
435
  * hasGaWorkflowsDir: boolean,
408
436
  * hasBunSqlImport: boolean,
409
437
  * hasGqlTaggedTemplates: boolean,
438
+ * hasHasuraConfig: boolean,
410
439
  * hasJsLikeSource: boolean,
411
440
  * hasK8sDir: boolean,
412
441
  * hasNginxDefaultTplFile: boolean,
@@ -423,6 +452,7 @@ export async function collectAutoRuleFacts(root) {
423
452
  hasDockerfile: false,
424
453
  hasGaWorkflowsDir: existsSync(join(root, '.github', 'workflows')),
425
454
  hasGqlTaggedTemplates: false,
455
+ hasHasuraConfig: false,
426
456
  hasJsLikeSource: false,
427
457
  hasK8sDir: false,
428
458
  hasNginxDefaultTplFile: false,
@@ -496,10 +526,10 @@ export async function detectAutoRulesAndSkills({
496
526
  : null
497
527
  )
498
528
  const isAbie = typeof repositoryUrl === 'string' && repositoryUrl.toLowerCase().includes(ABIE_REPOSITORY_URL_MARKER)
499
- const isMonorepo = isMonorepoPackage(packageJsonParsed)
500
529
  const depHits = await collectDependencyKeysPresentInPackageJsonTree(root, ['mssql', 'pg', 'pg-format', 'mysql2'])
501
530
  const hasMssqlDependency = depHits.has('mssql')
502
- const hasJsBunDbSignal = depHits.has('pg') || depHits.has('pg-format') || depHits.has('mysql2') || facts.hasBunSqlImport
531
+ const hasJsBunDbSignal =
532
+ depHits.has('pg') || depHits.has('pg-format') || depHits.has('mysql2') || facts.hasBunSqlImport
503
533
  const hasNestedNodePackage = await hasNestedPackageJsonWithoutViteDevDependency(root)
504
534
 
505
535
  /** @type {string[]} */
@@ -538,6 +568,7 @@ export async function detectAutoRulesAndSkills({
538
568
  { enabled: facts.hasDockerfile, id: 'docker' },
539
569
  { enabled: facts.hasGaWorkflowsDir, id: 'ga' },
540
570
  { enabled: facts.hasGqlTaggedTemplates, id: 'graphql' },
571
+ { enabled: facts.hasHasuraConfig, id: 'hasura' },
541
572
  { enabled: facts.hasJsLikeSource, id: 'js-lint' },
542
573
  { enabled: hasMssqlDependency, id: 'js-mssql' },
543
574
  { enabled: hasJsBunDbSignal, id: 'js-bun-db' },
@@ -57,9 +57,7 @@ async function readPackageScripts(projectRoot) {
57
57
  */
58
58
  export async function buildAgentsCommandBulletItems(projectRoot) {
59
59
  const scripts = await readPackageScripts(projectRoot)
60
- const items = /** @type {{ name: string }[]} */ ([])
61
-
62
- items.push({ name: `- **Залежності**: \`bun i\`` })
60
+ const items = /** @type {{ name: string }[]} */ ([{ name: `- **Залежності**: \`bun i\`` }])
63
61
 
64
62
  const added = new Set()
65
63
 
@@ -79,10 +77,12 @@ export async function buildAgentsCommandBulletItems(projectRoot) {
79
77
  added.add(key)
80
78
  }
81
79
 
82
- items.push({
83
- name: `- **Оновити правила та ${AGENTS_MD}** (після змін у правилах/шаблоні CLI): \`npx ${PACKAGE_NAME}\``
84
- })
85
- items.push({ name: `- **Перевірки правил (programmatic)**: \`npx ${PACKAGE_NAME} check\`` })
80
+ items.push(
81
+ {
82
+ name: `- **Оновити правила та ${AGENTS_MD}** (після змін у правилах/шаблоні CLI): \`npx ${PACKAGE_NAME}\``
83
+ },
84
+ { name: `- **Перевірки правил (programmatic)**: \`npx ${PACKAGE_NAME} check\`` }
85
+ )
86
86
 
87
87
  return items
88
88
  }