@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 +18 -0
- package/bin/auto-rules.md +2 -0
- package/mdc/hasura.mdc +31 -0
- package/mdc/js-lint.mdc +29 -4
- package/mdc/js-mssql.mdc +1 -1
- package/mdc/js-run.mdc +25 -13
- package/mdc/npm-module.mdc +9 -1
- package/package.json +3 -2
- package/scripts/auto-rules.mjs +58 -27
- package/scripts/build-agents-commands.mjs +7 -7
- package/scripts/check-hasura.mjs +219 -0
- package/scripts/check-js-bun-db.mjs +64 -45
- package/scripts/check-js-lint.mjs +10 -8
- package/scripts/check-js-run.mjs +62 -36
- package/scripts/check-k8s.mjs +455 -197
- package/scripts/check-npm-module.mjs +55 -0
- package/scripts/utils/bun-sql-scan.mjs +5 -2
- package/scripts/utils/check-env-scan.mjs +222 -64
- package/scripts/utils/conn-imports-scan.mjs +13 -3
- package/scripts/utils/mssql-pool-scan.mjs +57 -38
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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(
|
|
76
|
+
export const graphQLClientSmart = new GraphQLClient(env.QL, {
|
|
77
77
|
headers: {
|
|
78
|
-
'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
|
|
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`
|
package/mdc/npm-module.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Оформлення репозиторію для npm модуля
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
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.
|
|
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",
|
package/scripts/auto-rules.mjs
CHANGED
|
@@ -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`
|
|
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 (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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 =
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
}
|