@nitra/cursor 12.8.8 → 12.9.0
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 +14 -1
- package/bin/n-cursor.js +18 -10
- package/package.json +5 -5
- package/rules/abie/docs/index.md +0 -1
- package/rules/abie/lib/docs/http-route.md +11 -12
- package/rules/abie/lib/http-route.mjs +3 -0
- package/rules/abie/policy/health_check_policy/health_check_policy.mdc +3 -1
- package/rules/abie/policy/health_check_policy/health_check_policy.rego +27 -0
- package/rules/adr/docs/index.md +0 -1
- package/rules/adr/js/madr_format.mdc +13 -1
- package/rules/bun/docs/index.md +0 -1
- package/rules/bun/policy/package_json/package_json.rego +12 -0
- package/rules/capacitor/docs/index.md +0 -1
- package/rules/changelog/docs/index.md +0 -1
- package/rules/ci4/docs/index.md +0 -1
- package/rules/doc-files/docs/index.md +0 -1
- package/rules/doc-files/docs/main.md +7 -9
- package/rules/doc-files/main.mjs +2 -3
- package/rules/docker/docs/index.md +0 -1
- package/rules/efes/docs/index.md +0 -1
- package/rules/feedback/docs/index.md +0 -1
- package/rules/ga/docs/index.md +0 -1
- package/rules/graphql/docs/index.md +0 -1
- package/rules/hasura/docs/index.md +0 -1
- package/rules/hasura/js/docs/index.md +3 -2
- package/rules/hasura/js/docs/migrations.md +30 -0
- package/rules/hasura/js/migrations.mjs +47 -0
- package/rules/image-avif/docs/index.md +0 -1
- package/rules/image-compress/docs/index.md +0 -1
- package/rules/js/docs/index.md +0 -1
- package/rules/js/js/dep-policy.mjs +87 -0
- package/rules/js/js/docs/dep-policy.md +36 -0
- package/rules/js/js/docs/index.md +1 -0
- package/rules/js/policy/package_json/package_json.rego +16 -0
- package/rules/js-bun-db/docs/index.md +0 -1
- package/rules/js-bun-redis/docs/index.md +0 -1
- package/rules/js-mssql/docs/index.md +0 -1
- package/rules/js-run/docs/index.md +0 -1
- package/rules/k8s/docs/index.md +0 -1
- package/rules/nginx-default-tpl/docs/index.md +0 -1
- package/rules/npm-module/docs/index.md +0 -1
- package/rules/php/docs/index.md +0 -1
- package/rules/python/docs/index.md +0 -1
- package/rules/rego/docs/index.md +0 -1
- package/rules/rego/js/docs/index.md +3 -3
- package/rules/rego/js/docs/tooling.md +28 -0
- package/rules/rego/js/tooling.mjs +24 -0
- package/rules/rego/policy/package_json/package_json.rego +21 -0
- package/rules/rego/policy/package_json/target.json +4 -0
- package/rules/release/docs/index.md +0 -1
- package/rules/rust/docs/index.md +0 -1
- package/rules/rust/policy/lint_rust_yml/lint_rust_yml.rego +24 -0
- package/rules/rust/policy/package_json/package_json.rego +20 -0
- package/rules/rust/policy/package_json/target.json +4 -0
- package/rules/security/docs/index.md +0 -1
- package/rules/style/docs/index.md +0 -1
- package/rules/style/js/docs/index.md +2 -3
- package/rules/style/js/docs/tooling.md +14 -10
- package/rules/style/js/tooling.mjs +8 -2
- package/rules/style/policy/lint_style_yml/lint_style_yml.rego +5 -0
- package/rules/tauri/docs/index.md +0 -1
- package/rules/test/docs/index.md +0 -1
- package/rules/test/js/docs/index.md +2 -0
- package/rules/test/js/docs/no-console-store-restore.md +32 -0
- package/rules/test/js/docs/sandbox-aware-test.md +32 -0
- package/rules/test/js/no-console-store-restore.mjs +88 -0
- package/rules/test/js/sandbox-aware-test.mjs +89 -0
- package/rules/text/docs/index.md +0 -1
- package/rules/tool-surface/docs/index.md +0 -1
- package/rules/vue/docs/index.md +0 -1
- package/rules/worktree/docs/index.md +0 -1
- package/scripts/docs/hook.md +29 -0
- package/scripts/docs/index.md +1 -2
- package/scripts/hook.mjs +72 -0
- package/scripts/lib/docs/index.md +35 -36
- package/scripts/lib/docs/rule-meta.md +10 -9
- package/scripts/lib/docs/run-lint.md +9 -8
- package/scripts/lib/docs/run-rule.md +7 -7
- package/scripts/lib/fix/docs/index.md +0 -1
- package/scripts/lib/rule-meta.mjs +2 -1
- package/scripts/lib/run-lint.mjs +54 -3
- package/scripts/lib/run-rule.mjs +1 -2
- package/skills/adr-normalize/SKILL.md +1 -0
- package/skills/coverage-fix/SKILL.md +1 -0
- package/skills/doc-aggregate/SKILL.md +1 -0
- package/skills/doc-files/SKILL.md +1 -0
- package/skills/lint/SKILL.md +24 -19
- package/skills/llm-patch/SKILL.md +1 -0
- package/skills/publish-telegram/SKILL.md +1 -0
- package/skills/start-check/SKILL.md +1 -0
- package/skills/taze/SKILL.md +3 -2
- package/types/bin/n-cursor.d.ts +1 -1
- package/rules/abie/docs/fix.md +0 -37
- package/rules/adr/docs/fix.md +0 -37
- package/rules/bun/docs/fix.md +0 -30
- package/rules/capacitor/docs/fix.md +0 -36
- package/rules/changelog/docs/fix.md +0 -37
- package/rules/ci4/docs/fix.md +0 -32
- package/rules/doc-files/docs/fix.md +0 -29
- package/rules/docker/docs/fix.md +0 -35
- package/rules/efes/docs/fix.md +0 -37
- package/rules/feedback/docs/fix.md +0 -30
- package/rules/ga/docs/fix.md +0 -30
- package/rules/graphql/docs/fix.md +0 -37
- package/rules/hasura/docs/fix.md +0 -39
- package/rules/image-avif/docs/fix.md +0 -28
- package/rules/image-compress/docs/fix.md +0 -27
- package/rules/js/docs/fix.md +0 -37
- package/rules/js-bun-db/docs/fix.md +0 -30
- package/rules/js-bun-redis/docs/fix.md +0 -32
- package/rules/js-mssql/docs/fix.md +0 -30
- package/rules/js-run/docs/fix.md +0 -36
- package/rules/k8s/docs/fix.md +0 -31
- package/rules/nginx-default-tpl/docs/fix.md +0 -35
- package/rules/npm-module/docs/fix.md +0 -34
- package/rules/php/docs/fix.md +0 -35
- package/rules/python/docs/fix.md +0 -38
- package/rules/rego/docs/fix.md +0 -31
- package/rules/release/docs/fix.md +0 -28
- package/rules/rust/docs/fix.md +0 -32
- package/rules/security/docs/fix.md +0 -33
- package/rules/style/docs/fix.md +0 -28
- package/rules/tauri/docs/fix.md +0 -39
- package/rules/test/docs/fix.md +0 -31
- package/rules/text/docs/fix.md +0 -37
- package/rules/tool-surface/docs/fix.md +0 -32
- package/rules/vue/docs/fix.md +0 -32
- package/rules/worktree/docs/fix.md +0 -40
- package/scripts/docs/post-tool-use-fix.md +0 -32
- package/scripts/docs/worktree-cli.md +0 -27
- package/scripts/lib/docs/worktree.md +0 -42
- package/scripts/lib/fix/docs/run-fix-check.md +0 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [12.9.0] - 2026-06-24
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Додано підтримку hook-режиму для linting через `n-cursor
|
|
8
|
+
- Додано перевірку порту 8080 для HTTPRoute та політики HealthCheckPolicy
|
|
9
|
+
|
|
10
|
+
## [12.8.9] - 2026-06-22
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- ♻️ refactor(npm): Вдосконалено логіку `full`-scope правил у delta-режимі
|
|
15
|
+
|
|
3
16
|
## [12.8.8] - 2026-06-22
|
|
4
17
|
|
|
5
18
|
### Changed
|
|
6
19
|
|
|
7
|
-
- ✨ feat(rules): policy mdc для всіх npm/rules
|
|
20
|
+
- ✨ feat(rules): policy mdc для всіх npm/rules/\*/policy/<concern>/
|
|
8
21
|
|
|
9
22
|
## [12.8.7] - 2026-06-22
|
|
10
23
|
|
package/bin/n-cursor.js
CHANGED
|
@@ -142,7 +142,7 @@ function sortConfigIdArrays(config) {
|
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* Імена правил з каталогу `rules/` поточної інсталяції пакету. Кожне правило — окремий
|
|
145
|
-
* підкаталог `rules/<id>/`, у якому має бути
|
|
145
|
+
* підкаталог `rules/<id>/`, у якому має бути `main.mdc`.
|
|
146
146
|
* @param {string} [bundledRulesDir] каталог `rules/` у корені пакету
|
|
147
147
|
* @returns {Promise<string[]>} відсортовані id правил (імена підкаталогів)
|
|
148
148
|
*/
|
|
@@ -157,11 +157,11 @@ async function discoverBundledRuleNames(bundledRulesDir = BUNDLED_RULES_DIR) {
|
|
|
157
157
|
const entries = await readdir(bundledRulesDir, { withFileTypes: true })
|
|
158
158
|
const rules = entries
|
|
159
159
|
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
|
160
|
-
.filter(e => existsSync(join(bundledRulesDir, e.name,
|
|
160
|
+
.filter(e => existsSync(join(bundledRulesDir, e.name, 'main.mdc')))
|
|
161
161
|
.map(e => e.name)
|
|
162
162
|
.toSorted((a, b) => a.localeCompare(b))
|
|
163
163
|
if (rules.length === 0) {
|
|
164
|
-
throw new Error(`У каталозі rules/ пакету немає підкаталогів з
|
|
164
|
+
throw new Error(`У каталозі rules/ пакету немає підкаталогів з main.mdc. Створіть ${CONFIG_FILE} вручну.`)
|
|
165
165
|
}
|
|
166
166
|
return rules
|
|
167
167
|
}
|
|
@@ -407,18 +407,18 @@ function normalizeRuleName(ruleName) {
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
/**
|
|
410
|
-
* Читає вміст правила з каталогу `rules/<id
|
|
411
|
-
* (наприклад `node_modules/@nitra/cursor/rules/<id
|
|
410
|
+
* Читає вміст правила з каталогу `rules/<id>/main.mdc` установленого пакету
|
|
411
|
+
* (наприклад `node_modules/@nitra/cursor/rules/<id>/main.mdc` або кеш npx).
|
|
412
412
|
* @param {string} rule елемент масиву rules з `.n-cursor.json`
|
|
413
413
|
* @param {string} [bundledRulesDir] каталог `rules/` у корені пакету-джерела
|
|
414
414
|
* @returns {Promise<string>} текст правила для запису в `.cursor/rules/n-*.mdc`
|
|
415
415
|
*/
|
|
416
416
|
async function readBundledRuleContent(rule, bundledRulesDir = BUNDLED_RULES_DIR) {
|
|
417
417
|
const id = normalizeRuleName(rule)
|
|
418
|
-
const bundledPath = join(bundledRulesDir, id,
|
|
418
|
+
const bundledPath = join(bundledRulesDir, id, 'main.mdc')
|
|
419
419
|
if (!existsSync(bundledPath)) {
|
|
420
420
|
throw new Error(
|
|
421
|
-
`Немає файлу ${id}
|
|
421
|
+
`Немає файлу ${id}/main.mdc у ${bundledRulesDir}. Оновіть ${PACKAGE_NAME} або приберіть "${rule}" з rules у ${CONFIG_FILE}.`
|
|
422
422
|
)
|
|
423
423
|
}
|
|
424
424
|
const text = await readFile(bundledPath, 'utf8')
|
|
@@ -1515,9 +1515,17 @@ try {
|
|
|
1515
1515
|
|
|
1516
1516
|
break
|
|
1517
1517
|
}
|
|
1518
|
+
case 'hook': {
|
|
1519
|
+
// Thin hook entrypoint для Claude Code hooks. Делегує в runLint, перекодовує exit 1→2.
|
|
1520
|
+
// --post-tool-use PostToolUse: file_path зі stdin JSON
|
|
1521
|
+
// --stop Stop: робоче дерево vs HEAD
|
|
1522
|
+
const { runHookCli } = await import('../scripts/hook.mjs')
|
|
1523
|
+
process.exitCode = await runHookCli(args)
|
|
1524
|
+
|
|
1525
|
+
break
|
|
1526
|
+
}
|
|
1518
1527
|
case 'post-tool-use-check': {
|
|
1519
|
-
//
|
|
1520
|
-
// Маршрутизує змінений файл у релевантні правила і прокидає `fix` лише з ними.
|
|
1528
|
+
// @deprecated: використовуй `hook --post-tool-use`
|
|
1521
1529
|
const code = await runPostToolUseCheckCli()
|
|
1522
1530
|
process.exitCode = code
|
|
1523
1531
|
|
|
@@ -1661,7 +1669,7 @@ try {
|
|
|
1661
1669
|
default: {
|
|
1662
1670
|
console.error(`❌ Невідома команда: ${command}`)
|
|
1663
1671
|
console.error(
|
|
1664
|
-
` Очікується: (без аргументів) синхронізація правил, rename-yaml-extensions,
|
|
1672
|
+
` Очікується: (без аргументів) синхронізація правил, rename-yaml-extensions, hook, adr-normalize-local, lint (включно зі scope: lint ga|rego|k8s|docker|text), fix-doc-files, coverage, coverage-fix, analyze-escalation, taze, start-check, change, release, skill, trace, doc-aggregate`
|
|
1665
1673
|
)
|
|
1666
1674
|
process.exitCode = 1
|
|
1667
1675
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitra/cursor",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.9.0",
|
|
4
4
|
"description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
"rename-yaml-extensions": "bun ./bin/n-cursor.js rename-yaml-extensions"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@7n/mt": "^0.5.
|
|
57
|
-
"oxc-parser": "^0.
|
|
56
|
+
"@7n/mt": "^0.5.1",
|
|
57
|
+
"oxc-parser": "^0.137.0",
|
|
58
58
|
"picomatch": "^4.0.4",
|
|
59
|
-
"smol-toml": "^1.
|
|
60
|
-
"yaml": "^2.
|
|
59
|
+
"smol-toml": "^1.7.0",
|
|
60
|
+
"yaml": "^2.9.0",
|
|
61
61
|
"zod": "^4.4.3"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
package/rules/abie/docs/index.md
CHANGED
|
@@ -3,26 +3,25 @@ type: JS Module
|
|
|
3
3
|
title: http-route.mjs
|
|
4
4
|
resource: npm/rules/abie/lib/http-route.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 1ffd9c0b
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
7
9
|
---
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Огляд
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Виконує крос-документну аналітику для підрахунку `backendRefs` до спільних сервісів (`auth-run-hl`, `file-link-hl`) у base-маніфестах пакета, що знаходяться поза overlay `ua`. Використовується фіксований список спільних сервісів, визначений через `ABIE_SHARED_CROSS_NS_BACKEND_NAMES`. Функція `analyzeAbieSharedBackendRefsInPackageK8s` підраховує ці посилання. Це забезпечує синхронізацію числа патчів namespace в overlay із кількістю base-reference, використовуючи `ua_http_route-концерном` для забезпечення узгодженості (abie.mdc).
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
Визначає список спільних сервісів, які підлягають аналітиці.
|
|
15
|
+
## Поведінка
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
ABIE_SHARED_CROSS_NS_BACKEND_NAMES надає фіксований список назв спільних сервісів (`auth-run-hl`, `file-link-hl`), які підлягають аналізу.
|
|
18
|
+
analyzeAbieSharedBackendRefsInPackageK8s збирає кількість посилань на спільні сервіси (`backendRefs`) у base-маніфестах пакета (виключаючи overlay `ua`) та виявляє порушення вимог до цих посилань (наприклад, відсутність `namespace: dev` або `port: 8080` (abie.mdc)).
|
|
18
19
|
|
|
19
20
|
## Публічний API
|
|
20
21
|
|
|
21
|
-
ABIE_SHARED_CROSS_NS_BACKEND_NAMES —
|
|
22
|
-
|
|
23
|
-
analyzeAbieSharedBackendRefsInPackageK8s — Збирає кількість спільних посилань `backendRefs` та базові помилки з YAML-файлів пакета, ігноруючи неймспейс `dev`. (abie.mdc)
|
|
22
|
+
ABIE_SHARED_CROSS_NS_BACKEND_NAMES — Визначає імена бекендів, що використовуються між різними неймспейсами.
|
|
23
|
+
analyzeAbieSharedBackendRefsInPackageK8s — Підраховує кількість посилань на спільні бекенди у YAML-файлах пакета (крім overlay ua) та фіксує базові помилки (без неймспейсу dev). (abie.mdc)
|
|
24
24
|
|
|
25
25
|
## Гарантії поведінки
|
|
26
26
|
|
|
27
|
-
- Read-only:
|
|
28
|
-
- Не звертається до мережі.
|
|
27
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -27,6 +27,9 @@ function checkSharedBackendRef(br, rel, errors) {
|
|
|
27
27
|
if (typeof brRec.namespace !== 'string' || brRec.namespace !== 'dev') {
|
|
28
28
|
errors.push(`${rel}: HTTPRoute backendRefs до ${name} має містити namespace: dev (abie.mdc)`)
|
|
29
29
|
}
|
|
30
|
+
if (brRec.port !== 8080) {
|
|
31
|
+
errors.push(`${rel}: HTTPRoute backendRefs до ${name} має містити port: 8080 (abie.mdc)`)
|
|
32
|
+
}
|
|
30
33
|
return 1
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -8,10 +8,12 @@ Rego-пакет: `abie.health_check_policy`
|
|
|
8
8
|
|
|
9
9
|
- `apiVersion` — точно `networking.gke.io/v1`;
|
|
10
10
|
- `metadata.name` — непорожній рядок;
|
|
11
|
+
- `metadata.namespace` — `dev`;
|
|
11
12
|
- `spec.default.config.type` — `HTTP`;
|
|
12
13
|
- `spec.default.config.httpHealthCheck.requestPath` — непорожній, починається з `/`;
|
|
13
|
-
- `spec.default.config.httpHealthCheck.port` — `8080
|
|
14
|
+
- `spec.default.config.httpHealthCheck.port` — `8080` (обов'язковий, відсутність теж помилка);
|
|
14
15
|
- `spec.targetRef.kind` — `Service`;
|
|
16
|
+
- `spec.targetRef.group` — порожній рядок `''` (якщо поле присутнє);
|
|
15
17
|
- `spec.targetRef.name` — `<metadata.name>-hl` (якщо `metadata.name` вже закінчується на `-hl` — використовується як є).
|
|
16
18
|
|
|
17
19
|
FS-парність HCP↔Deployment та modeline `$schema` у `hc.yaml` — поза Rego, у `js/hc_pairing.mjs`. JS-опис структури та приклад — у `js/hc_pairing.mdc`.
|
|
@@ -61,6 +61,15 @@ deny contains "HealthCheckPolicy: metadata.name має бути непорожн
|
|
|
61
61
|
trim_space(name) == ""
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
# ── deny: metadata.namespace ──────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
deny contains msg if {
|
|
67
|
+
is_health_check_policy
|
|
68
|
+
ns := object.get(object.get(input, "metadata", {}), "namespace", "")
|
|
69
|
+
ns != "dev"
|
|
70
|
+
msg := sprintf("HealthCheckPolicy: metadata.namespace має бути dev (зараз %q) (abie.mdc)", [ns])
|
|
71
|
+
}
|
|
72
|
+
|
|
64
73
|
# ── deny: spec.default.config.type ────────────────────────────────────────
|
|
65
74
|
|
|
66
75
|
deny contains "HealthCheckPolicy: spec.default.config.type має бути HTTP (abie.mdc)" if {
|
|
@@ -87,6 +96,12 @@ deny contains msg if {
|
|
|
87
96
|
|
|
88
97
|
# ── deny: port == 8080 ────────────────────────────────────────────────────
|
|
89
98
|
|
|
99
|
+
deny contains "HealthCheckPolicy: spec.default.config.httpHealthCheck.port має бути 8080 (abie.mdc)" if {
|
|
100
|
+
is_health_check_policy
|
|
101
|
+
is_object(http_health_check)
|
|
102
|
+
object.get(http_health_check, "port", null) == null
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
deny contains msg if {
|
|
91
106
|
is_health_check_policy
|
|
92
107
|
is_object(http_health_check)
|
|
@@ -108,6 +123,18 @@ deny contains msg if {
|
|
|
108
123
|
msg := sprintf("HealthCheckPolicy: targetRef.kind має бути Service (зараз %q) (abie.mdc)", [kind])
|
|
109
124
|
}
|
|
110
125
|
|
|
126
|
+
# ── deny: targetRef.group == '' ───────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
deny contains msg if {
|
|
129
|
+
is_health_check_policy
|
|
130
|
+
target_ref := object.get(object.get(input, "spec", {}), "targetRef", {})
|
|
131
|
+
is_object(target_ref)
|
|
132
|
+
group := object.get(target_ref, "group", null)
|
|
133
|
+
group != null
|
|
134
|
+
group != ""
|
|
135
|
+
msg := sprintf("HealthCheckPolicy: targetRef.group має бути порожнім рядком '' (зараз %q) (abie.mdc)", [group])
|
|
136
|
+
}
|
|
137
|
+
|
|
111
138
|
# ── deny: targetRef.name = `<hcp.metadata.name>-hl` (exact, з нормалізацією)
|
|
112
139
|
|
|
113
140
|
deny contains msg if {
|
package/rules/adr/docs/index.md
CHANGED
|
@@ -93,4 +93,16 @@ docs/adr/
|
|
|
93
93
|
└── hooks.json # Cursor Agent stop-hooks для тих самих скриптів
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
`.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @nitra/cursor`, коли правило `adr` увімкнене):
|
|
96
|
+
`.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @nitra/cursor`, коли правило `adr` увімкнене):
|
|
97
|
+
|
|
98
|
+
```gitignore
|
|
99
|
+
node_modules/
|
|
100
|
+
dist/
|
|
101
|
+
*.secret
|
|
102
|
+
|
|
103
|
+
# @nitra/cursor (adr) — локальні артефакти Stop-hook, не коміти
|
|
104
|
+
.claude/hooks/*.log
|
|
105
|
+
.claude/hooks/.normalize-state
|
|
106
|
+
.claude/hooks/.normalize.lock
|
|
107
|
+
.claude/scheduled_tasks.lock
|
|
108
|
+
```
|
package/rules/bun/docs/index.md
CHANGED
|
@@ -24,6 +24,18 @@ deny contains msg if {
|
|
|
24
24
|
msg := sprintf("package.json: поле %s — %s", [field, reason])
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
# ── deny: scripts.lint / scripts.lint-* заборонені (bun.mdc lint) ────────
|
|
28
|
+
|
|
29
|
+
deny contains msg if {
|
|
30
|
+
is_object(input.scripts)
|
|
31
|
+
some script_name, _ in input.scripts
|
|
32
|
+
regex.match(`^lint(-.*)?$`, script_name)
|
|
33
|
+
msg := sprintf(
|
|
34
|
+
"package.json: scripts.%s заборонений — лінт запускається через n-cursor lint, не через package.json-скрипти (bun.mdc)",
|
|
35
|
+
[script_name]
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
# ── deny: devDependencies — лише `@nitra/*` + root-only тестові peer/tools ─
|
|
28
40
|
|
|
29
41
|
deny contains msg if {
|
package/rules/ci4/docs/index.md
CHANGED
|
@@ -3,26 +3,24 @@ type: JS Module
|
|
|
3
3
|
title: main.mjs
|
|
4
4
|
resource: npm/rules/doc-files/main.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 82caefcd
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
score:
|
|
8
|
+
score: 90
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль перевіряє відповідність
|
|
13
|
+
Модуль перевіряє відповідність конфігурацій політики джерелам коду, спираючись на `meta.json`. Він також аналізує документацію на відповідність джерелам коду, виявляючи застарілі або нерелевантні описи.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
run виконує перевірку
|
|
18
|
-
|
|
19
|
-
lint перевіряє, чи не застаріли описи документації, і може автоматично їх оновлювати, якщо це дозволено конфігурацією `meta.json`.
|
|
17
|
+
run виконує перевірку відповідності до політики, порівнюючи конфігурації з джерелами коду.
|
|
18
|
+
lint перевіряє, чи відповідає документація джерелам коду, виявляючи застарілі описи та сирітські доки, а також може автоматично їх виправляти за умови наявності конфігурації `meta.json: llmFix:true`.
|
|
20
19
|
|
|
21
20
|
## Публічний API
|
|
22
21
|
|
|
23
|
-
run —
|
|
24
|
-
lint —
|
|
25
|
-
llmFix — виконує добровільну генерацію виправлень за допомогою LLM, якщо це дозволено в конфігурації.
|
|
22
|
+
run — основна точка входу, яка виконує перевірку (JS-залежності $\rightarrow$ політики $\rightarrow$ посилання MDC) та запускає перевірку на застарілі файли.
|
|
23
|
+
lint — збирає дані для перевірки документаційних файлів, з можливістю автоматичного виправлення за умови ввімкнення LLM-функціоналу.
|
|
26
24
|
|
|
27
25
|
## Гарантії поведінки
|
|
28
26
|
|
package/rules/doc-files/main.mjs
CHANGED
|
@@ -101,8 +101,7 @@ function collectStale(files, cwd) {
|
|
|
101
101
|
*/
|
|
102
102
|
export async function lint(files, cwd = process.cwd(), { readOnly = false, llmFix = false } = {}) {
|
|
103
103
|
const stale = collectStale(files, cwd)
|
|
104
|
-
|
|
105
|
-
const orphans = files === undefined ? scanOrphanedDocs(cwd) : []
|
|
104
|
+
const orphans = scanOrphanedDocs(cwd)
|
|
106
105
|
|
|
107
106
|
if (stale.length === 0 && orphans.length === 0) return 0
|
|
108
107
|
if (readOnly || !llmFix) {
|
|
@@ -132,7 +131,7 @@ export async function lint(files, cwd = process.cwd(), { readOnly = false, llmFi
|
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
const stillStale = collectStale(files, cwd)
|
|
135
|
-
const stillOrphans =
|
|
134
|
+
const stillOrphans = scanOrphanedDocs(cwd)
|
|
136
135
|
if (stillStale.length === 0 && stillOrphans.length === 0) return 0
|
|
137
136
|
if (stillStale.length > 0) reportStale(stillStale)
|
|
138
137
|
if (stillOrphans.length > 0) {
|
package/rules/efes/docs/index.md
CHANGED
package/rules/ga/docs/index.md
CHANGED
|
@@ -6,6 +6,7 @@ resource: npm/rules/hasura/js/
|
|
|
6
6
|
|
|
7
7
|
# npm/rules/hasura/js
|
|
8
8
|
|
|
9
|
-
| Файл
|
|
10
|
-
|
|
9
|
+
| Файл | Тип |
|
|
10
|
+
|---|---|
|
|
11
11
|
| [internal_urls.mjs](internal_urls.md) | JS Module |
|
|
12
|
+
| [migrations.mjs](migrations.md) | JS Module |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: migrations.mjs
|
|
4
|
+
resource: npm/rules/hasura/js/migrations.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: a8e2c4c4
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Функція перевіряє вміст директорії міграцій Hasura. Вона гарантує, що в директорії `hasura/migrations` відсутні файли з назвою `down.sql`, оскільки там мають бути лише файли `up.sql` (hasura.mdc).
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
1. Перевіряє наявність директорії `hasura/migrations` відносно кореня репозиторію.
|
|
18
|
+
2. Якщо директорія `hasura/migrations` відсутня, повідомляє, що перевірка `down.sql` не потрібна (hasura.mdc) і завершує роботу.
|
|
19
|
+
3. Якщо директорія `hasura/migrations` існує, сканує її вміст.
|
|
20
|
+
4. Збирає список усіх знайдених файлів з назвою `down.sql` у цій директорії.
|
|
21
|
+
5. Якщо жоден файл `down.sql` не знайдено, повідомляє, що жоден `down.sql` не знайдено у `hasura/migrations/` (hasura.mdc) і завершує роботу.
|
|
22
|
+
6. Якщо знайдено один або більше файлів `down.sql`, повідомляє про кожен знайдений файл, що `down.sql` заборонений у `hasura/migrations/` — у директорії міграції має бути лише `up.sql` (hasura.mdc) і завершує роботу.
|
|
23
|
+
|
|
24
|
+
## Публічний API
|
|
25
|
+
|
|
26
|
+
check — перевіряє відсутність файлів `down.sql` у директорії міграцій, оскільки вони не використовуються.
|
|
27
|
+
|
|
28
|
+
## Гарантії поведінки
|
|
29
|
+
|
|
30
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** @see ./docs/migrations.md */
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { basename, join, relative } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
6
|
+
import { walkDir } from '../../../scripts/utils/walkDir.mjs'
|
|
7
|
+
|
|
8
|
+
/** Відносний шлях до директорії міграцій від кореня проєкту. */
|
|
9
|
+
const MIGRATIONS_REL = 'hasura/migrations'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Перевіряє, що у `hasura/migrations/` відсутні файли `down.sql`.
|
|
13
|
+
* Директорія міграції має містити лише `up.sql` — `down.sql` у проєкті не використовується.
|
|
14
|
+
* @param {string} [cwdParam] корінь репозиторію
|
|
15
|
+
* @returns {Promise<number>} 0 — чисто, 1 — знайдено `down.sql`
|
|
16
|
+
*/
|
|
17
|
+
export async function check(cwdParam = process.cwd()) {
|
|
18
|
+
const reporter = createCheckReporter()
|
|
19
|
+
const { pass, fail } = reporter
|
|
20
|
+
|
|
21
|
+
const cwd = cwdParam
|
|
22
|
+
const migrationsDir = join(cwd, MIGRATIONS_REL)
|
|
23
|
+
|
|
24
|
+
if (!existsSync(migrationsDir)) {
|
|
25
|
+
pass(`${MIGRATIONS_REL}/ відсутній — перевірка down.sql не потрібна (hasura.mdc)`)
|
|
26
|
+
return reporter.getExitCode()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** @type {string[]} */
|
|
30
|
+
const offenders = []
|
|
31
|
+
await walkDir(migrationsDir, absPath => {
|
|
32
|
+
if (basename(absPath) === 'down.sql') {
|
|
33
|
+
offenders.push(relative(cwd, absPath))
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
if (offenders.length === 0) {
|
|
38
|
+
pass(`Жоден down.sql не знайдено у ${MIGRATIONS_REL}/ (hasura.mdc)`)
|
|
39
|
+
return reporter.getExitCode()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const file of offenders) {
|
|
43
|
+
fail(`${file}: down.sql заборонений у ${MIGRATIONS_REL}/ — у директорії міграції має бути лише up.sql (hasura.mdc)`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return reporter.getExitCode()
|
|
47
|
+
}
|
package/rules/js/docs/index.md
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** @see ./docs/dep-policy.md */
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { join, relative, sep } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { parseSync } from 'oxc-parser'
|
|
6
|
+
|
|
7
|
+
import { createCheckReporter } from '../../../scripts/lib/check-reporter.mjs'
|
|
8
|
+
import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.mjs'
|
|
9
|
+
import { walkDir } from '../../../scripts/utils/walkDir.mjs'
|
|
10
|
+
import {
|
|
11
|
+
dynamicImportModule,
|
|
12
|
+
langFromPath,
|
|
13
|
+
requireCallModule,
|
|
14
|
+
walkAstWithAncestors,
|
|
15
|
+
} from '../../../scripts/utils/ast-scan-utils.mjs'
|
|
16
|
+
|
|
17
|
+
const JS_SOURCE_RE = /\.(?:[cm]?[jt]sx?)$/u
|
|
18
|
+
|
|
19
|
+
/** Пакети, заборонені як import-specifier у будь-якому JS/TS-файлі. */
|
|
20
|
+
const BANNED_SPECIFIERS = new Set(['@nitra/as-integrations-fastify'])
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Витягає з джерела всі import-specifier'и (static + dynamic + require).
|
|
24
|
+
* @param {string} source текст файлу
|
|
25
|
+
* @param {string} filePath шлях до файлу (для вибору мови OXC-парсера)
|
|
26
|
+
* @returns {string[]} список specifier'ів
|
|
27
|
+
*/
|
|
28
|
+
function extractImportSpecifiers(source, filePath) {
|
|
29
|
+
/** @type {string[]} */
|
|
30
|
+
const result = []
|
|
31
|
+
let parsed
|
|
32
|
+
try {
|
|
33
|
+
parsed = parseSync(filePath, source, { lang: langFromPath(filePath) })
|
|
34
|
+
} catch {
|
|
35
|
+
return result
|
|
36
|
+
}
|
|
37
|
+
for (const imp of parsed?.module?.staticImports ?? []) {
|
|
38
|
+
if (typeof imp?.moduleRequest?.value === 'string') result.push(imp.moduleRequest.value)
|
|
39
|
+
}
|
|
40
|
+
const program = parsed?.program
|
|
41
|
+
if (program && typeof program === 'object') {
|
|
42
|
+
walkAstWithAncestors(program, [], node => {
|
|
43
|
+
const dyn = dynamicImportModule(node)
|
|
44
|
+
if (dyn !== null) result.push(dyn)
|
|
45
|
+
const req = requireCallModule(node)
|
|
46
|
+
if (req !== null) result.push(req)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Сканує всі JS/TS-файли проєкту на заборонені import-specifier'и (dep-policy.mdc).
|
|
54
|
+
* @param {string} [cwdParam] корінь репозиторію
|
|
55
|
+
* @returns {Promise<number>} 0 — чисто, 1 — знайдено заборонені specifier'и
|
|
56
|
+
*/
|
|
57
|
+
export async function check(cwdParam = process.cwd()) {
|
|
58
|
+
const reporter = createCheckReporter()
|
|
59
|
+
const cwd = cwdParam
|
|
60
|
+
const ignorePaths = await loadCursorIgnorePaths(cwd)
|
|
61
|
+
|
|
62
|
+
const files = []
|
|
63
|
+
await walkDir(cwd, p => {
|
|
64
|
+
if (JS_SOURCE_RE.test(p)) files.push(p)
|
|
65
|
+
}, ignorePaths)
|
|
66
|
+
|
|
67
|
+
let violations = 0
|
|
68
|
+
for (const absPath of files) {
|
|
69
|
+
const source = await readFile(absPath, 'utf8')
|
|
70
|
+
const specifiers = extractImportSpecifiers(source, absPath)
|
|
71
|
+
for (const spec of specifiers) {
|
|
72
|
+
if (BANNED_SPECIFIERS.has(spec)) {
|
|
73
|
+
const rel = relative(cwd, absPath)
|
|
74
|
+
reporter.fail(
|
|
75
|
+
`${rel}: заборонений import '${spec}' — використовуй @as-integrations/fastify (js.mdc dep-policy)`
|
|
76
|
+
)
|
|
77
|
+
violations += 1
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (violations === 0) {
|
|
83
|
+
reporter.pass(`dep-policy: перевірено ${files.length} файлів — заборонених import-specifier'ів немає (js.mdc)`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return reporter.getExitCode()
|
|
87
|
+
}
|