@nitra/cursor 1.8.204 → 1.8.207
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 +52 -1
- package/bin/auto-rules.md +2 -0
- package/mdc/rego.mdc +77 -0
- package/package.json +1 -1
- package/policy/abie/health_check_policy/health_check_policy.rego +73 -0
- package/policy/abie/http_route_base/http_route_base.rego +45 -0
- package/policy/adr/settings_json/settings_json.rego +31 -0
- package/policy/adr/settings_local_json/settings_local_json.rego +28 -0
- package/policy/bun/bunfig/bunfig.rego +33 -0
- package/policy/bun/package_json/package_json.rego +94 -0
- package/policy/capacitor/package_json/package_json.rego +45 -0
- package/policy/ga/clean_ga_workflows/clean_ga_workflows.rego +0 -26
- package/policy/ga/clean_merged_branch/clean_merged_branch.rego +0 -25
- package/policy/ga/git_ai/git_ai.rego +83 -0
- package/policy/ga/lint_ga/lint_ga.rego +118 -0
- package/policy/ga/workflow_common/workflow_common.rego +161 -0
- package/policy/graphql/package_json/package_json.rego +35 -0
- package/policy/hasura/svc_hl/svc_hl.rego +27 -0
- package/policy/image_compress/package_json/package_json.rego +94 -0
- package/policy/js_bun_db/package_json/package_json.rego +28 -0
- package/policy/js_lint/lint_js_yml/lint_js_yml.rego +98 -0
- package/policy/js_lint/package_json/package_json.rego +137 -0
- package/policy/js_mssql/package_json/package_json.rego +57 -0
- package/policy/js_run/configmap/configmap.rego +45 -0
- package/policy/js_run/jsconfig/jsconfig.rego +66 -0
- package/policy/js_run/package_json/package_json.rego +31 -0
- package/policy/k8s/manifest/manifest.rego +130 -0
- package/policy/npm_module/emit_types_config/emit_types_config.rego +37 -0
- package/policy/npm_module/npm_package_json/npm_package_json.rego +55 -0
- package/policy/npm_module/npm_publish_yml/npm_publish_yml.rego +79 -0
- package/policy/npm_module/root_package_json/root_package_json.rego +28 -0
- package/policy/php/lint_php_yml/lint_php_yml.rego +32 -0
- package/policy/php/package_json/package_json.rego +19 -0
- package/policy/style_lint/lint_style_yml/lint_style_yml.rego +35 -0
- package/policy/style_lint/package_json/package_json.rego +49 -0
- package/policy/text/cspell/cspell.rego +91 -0
- package/policy/text/markdownlint/markdownlint.rego +21 -0
- package/policy/text/oxfmtrc/oxfmtrc.rego +90 -0
- package/policy/text/package_json/package_json.rego +88 -0
- package/policy/vue/package_json/package_json.rego +54 -0
- package/scripts/auto-rules.mjs +10 -0
- package/scripts/check-adr.mjs +7 -3
- package/scripts/check-bun.mjs +21 -117
- package/scripts/check-ga.mjs +0 -284
- package/scripts/check-graphql.mjs +6 -45
- package/scripts/check-hasura.mjs +4 -5
- package/scripts/check-image-avif.mjs +3 -3
- package/scripts/check-image-compress.mjs +25 -132
- package/scripts/check-js-bun-db.mjs +3 -50
- package/scripts/check-js-run.mjs +9 -12
- package/scripts/check-k8s.mjs +6 -5
- package/scripts/check-npm-module.mjs +17 -8
- package/scripts/check-php.mjs +16 -51
- package/scripts/check-style-lint.mjs +28 -52
- package/scripts/check-text.mjs +47 -219
- package/scripts/check-vue.mjs +3 -16
- package/scripts/lint-conftest.mjs +351 -0
- package/scripts/lint-ga.mjs +49 -2
- package/scripts/lint-rego.mjs +67 -21
- package/scripts/run-shellcheck-text.mjs +3 -6
- package/scripts/utils/depcheck-workflow.mjs +2 -6
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Порт перевірки `lint-php.yml` з `npm/scripts/check-php.mjs` (php.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .github/workflows/lint-php.yml -p npm/policy/php \
|
|
5
|
+
# --namespace php.lint_php_yml
|
|
6
|
+
#
|
|
7
|
+
# Перевіряє: хоча б один крок `run` містить `bun run lint-php`. Універсальні
|
|
8
|
+
# workflow-перевірки — у `ga.workflow_common`.
|
|
9
|
+
#
|
|
10
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
13
|
+
package php.lint_php_yml
|
|
14
|
+
|
|
15
|
+
import rego.v1
|
|
16
|
+
|
|
17
|
+
all_run_text := concat("\n", [run_text |
|
|
18
|
+
some job in object.get(input, "jobs", {})
|
|
19
|
+
some step in object.get(job, "steps", [])
|
|
20
|
+
run_text := step_run_to_text(step)
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
deny contains msg if {
|
|
24
|
+
not contains(all_run_text, "bun run lint-php")
|
|
25
|
+
msg := "lint-php.yml: жоден крок run не містить `bun run lint-php` (php.mdc)"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
step_run_to_text(step) := step.run if is_string(step.run)
|
|
29
|
+
|
|
30
|
+
else := concat("\n", [s | some s in step.run]) if is_array(step.run)
|
|
31
|
+
|
|
32
|
+
else := ""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Порт перевірки `package.json` з `npm/scripts/check-php.mjs` (php.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test package.json -p npm/policy/php --namespace php.package_json
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: наявність скрипта `lint-php`. FS-перевірки (`composer.json`, наявність
|
|
7
|
+
# `package.json` як такого) — у JS.
|
|
8
|
+
#
|
|
9
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
10
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
11
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
12
|
+
package php.package_json
|
|
13
|
+
|
|
14
|
+
import rego.v1
|
|
15
|
+
|
|
16
|
+
deny contains msg if {
|
|
17
|
+
not object.get(object.get(input, "scripts", {}), "lint-php", false)
|
|
18
|
+
msg := "package.json: додай скрипт \"lint-php\" (php.mdc)"
|
|
19
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Порт перевірки `lint-style.yml` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .github/workflows/lint-style.yml -p npm/policy/style_lint \
|
|
5
|
+
# --namespace style_lint.lint_style_yml
|
|
6
|
+
#
|
|
7
|
+
# Перевіряє: хоча б один крок `run` містить `npx stylelint` (саме через npx, не
|
|
8
|
+
# `bun run lint-style`). Універсальні workflow-перевірки (concurrency, заборонені
|
|
9
|
+
# setup-bun/cache/install) — у `ga.workflow_common`.
|
|
10
|
+
#
|
|
11
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
12
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
13
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
14
|
+
package style_lint.lint_style_yml
|
|
15
|
+
|
|
16
|
+
import rego.v1
|
|
17
|
+
|
|
18
|
+
# Усі тексти `run:` зі steps усіх jobs, склеєні в один blob — для substring-перевірки.
|
|
19
|
+
all_run_text := concat("\n", [run_text |
|
|
20
|
+
some job in object.get(input, "jobs", {})
|
|
21
|
+
some step in object.get(job, "steps", [])
|
|
22
|
+
run_text := step_run_to_text(step)
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
deny contains msg if {
|
|
26
|
+
not contains(all_run_text, "npx stylelint")
|
|
27
|
+
msg := "lint-style.yml: жоден крок run не містить `npx stylelint` (style-lint.mdc)"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Текст `run:` як один рядок: підтримує string і array форми (YAML).
|
|
31
|
+
step_run_to_text(step) := step.run if is_string(step.run)
|
|
32
|
+
|
|
33
|
+
else := concat("\n", [s | some s in step.run]) if is_array(step.run)
|
|
34
|
+
|
|
35
|
+
else := ""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Порт перевірок `package.json` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test package.json -p npm/policy/style_lint --namespace style_lint.package_json
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: наявність скрипта `lint-style` з `npx stylelint`, `@nitra/stylelint-config`
|
|
7
|
+
# у `devDependencies`, поле `stylelint.extends == "@nitra/stylelint-config"`. FS-частина
|
|
8
|
+
# (зовнішні `.stylelintrc.*` як альтернатива полю; `.stylelintignore`) лишається у JS.
|
|
9
|
+
#
|
|
10
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
13
|
+
package style_lint.package_json
|
|
14
|
+
|
|
15
|
+
import rego.v1
|
|
16
|
+
|
|
17
|
+
# ── deny: lint-style скрипт ───────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
deny contains msg if {
|
|
20
|
+
not object.get(object.get(input, "scripts", {}), "lint-style", false)
|
|
21
|
+
msg := "package.json не містить скрипт \"lint-style\" (style-lint.mdc)"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
deny contains msg if {
|
|
25
|
+
lint_style := object.get(object.get(input, "scripts", {}), "lint-style", "")
|
|
26
|
+
lint_style != ""
|
|
27
|
+
not contains(lint_style, "npx stylelint")
|
|
28
|
+
msg := sprintf("lint-style має викликати stylelint через npx (зараз: %q) (style-lint.mdc)", [lint_style])
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# ── deny: @nitra/stylelint-config у devDependencies ───────────────────────
|
|
32
|
+
|
|
33
|
+
deny contains msg if {
|
|
34
|
+
dev := object.get(input, "devDependencies", {})
|
|
35
|
+
not "@nitra/stylelint-config" in object.keys(dev)
|
|
36
|
+
msg := "@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config (style-lint.mdc)"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ── deny: поле stylelint.extends = "@nitra/stylelint-config" ──────────────
|
|
40
|
+
#
|
|
41
|
+
# JS-перевірка дозволяє альтернативу: окремий файл `.stylelintrc.*`. Цю частину
|
|
42
|
+
# перевіряємо в JS (FS-вибірка); тут — лише структурна валідація поля, якщо воно є.
|
|
43
|
+
|
|
44
|
+
deny contains msg if {
|
|
45
|
+
cfg := object.get(input, "stylelint", null)
|
|
46
|
+
is_object(cfg)
|
|
47
|
+
object.get(cfg, "extends", null) != "@nitra/stylelint-config"
|
|
48
|
+
msg := "package.json: stylelint.extends має бути \"@nitra/stylelint-config\" (style-lint.mdc)"
|
|
49
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Порт перевірок `.cspell.json` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .cspell.json -p npm/policy/text --namespace text.cspell
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: `version: "0.2"`, наявність `language`, імпорт `@nitra/cspell-dict`,
|
|
7
|
+
# відсутність прямих імпортів `@cspell/dict-*`, обовʼязкові glob-и в `ignorePaths`
|
|
8
|
+
# (text.mdc).
|
|
9
|
+
#
|
|
10
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
13
|
+
package text.cspell
|
|
14
|
+
|
|
15
|
+
import rego.v1
|
|
16
|
+
|
|
17
|
+
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
# Канонічні `ignorePaths` з text.mdc — кожен має бути присутнім.
|
|
20
|
+
required_ignore_paths := {
|
|
21
|
+
"**/node_modules/**",
|
|
22
|
+
"**/vscode-extension/**",
|
|
23
|
+
"**/.git/**",
|
|
24
|
+
".vscode",
|
|
25
|
+
"report",
|
|
26
|
+
"*.svg",
|
|
27
|
+
"**/k8s/**/*.yaml",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
nitra_cspell_dict_marker := "@nitra/cspell-dict"
|
|
31
|
+
|
|
32
|
+
legacy_dict_marker := "@cspell/dict-"
|
|
33
|
+
|
|
34
|
+
# Шаблон повідомлення про заборонений імпорт `@cspell/dict-*` — через `concat`
|
|
35
|
+
# для regal style/line-length.
|
|
36
|
+
legacy_dict_import_template := concat(" ", [
|
|
37
|
+
".cspell.json не має імпортувати @cspell/dict-* —",
|
|
38
|
+
"використовуй лише @nitra/cspell-dict (знайдено: %s) (text.mdc)",
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
# ── deny: version / language ──────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
deny contains msg if {
|
|
44
|
+
object.get(input, "version", null) != "0.2"
|
|
45
|
+
msg := ".cspell.json: version має бути \"0.2\" (text.mdc)"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
deny contains msg if {
|
|
49
|
+
not object.get(input, "language", false)
|
|
50
|
+
msg := ".cspell.json: відсутнє поле language (text.mdc)"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ── deny: imports ─────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
deny contains msg if {
|
|
56
|
+
imports := object.get(input, "import", [])
|
|
57
|
+
is_array(imports)
|
|
58
|
+
not has_nitra_dict_import(imports)
|
|
59
|
+
msg := ".cspell.json не імпортує @nitra/cspell-dict/cspell-ext.json (text.mdc)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
deny contains msg if {
|
|
63
|
+
imports := object.get(input, "import", [])
|
|
64
|
+
is_array(imports)
|
|
65
|
+
some imp in imports
|
|
66
|
+
is_string(imp)
|
|
67
|
+
contains(imp, legacy_dict_marker)
|
|
68
|
+
msg := sprintf(legacy_dict_import_template, [imp])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# ── deny: ignorePaths ─────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
deny contains msg if {
|
|
74
|
+
not is_array(object.get(input, "ignorePaths", null))
|
|
75
|
+
msg := ".cspell.json: додай масив ignorePaths з канонічними glob-ами (text.mdc)"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
deny contains msg if {
|
|
79
|
+
is_array(input.ignorePaths)
|
|
80
|
+
some path in required_ignore_paths
|
|
81
|
+
not path in {p | some p in input.ignorePaths}
|
|
82
|
+
msg := sprintf(".cspell.json ignorePaths: додай %q (text.mdc)", [path])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
has_nitra_dict_import(imports) if {
|
|
88
|
+
some imp in imports
|
|
89
|
+
is_string(imp)
|
|
90
|
+
contains(imp, nitra_cspell_dict_marker)
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Порт перевірки `.markdownlint-cli2.jsonc` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .markdownlint-cli2.jsonc -p npm/policy/text \
|
|
5
|
+
# --namespace text.markdownlint --parser json
|
|
6
|
+
#
|
|
7
|
+
# Конфтест парсить `.jsonc` як JSON лише якщо файл — валідний JSON (без коментарів).
|
|
8
|
+
# У випадку справжнього JSONC з `//` коментарями цей крок мовчки ігноруватиметься
|
|
9
|
+
# (conftest skip). FS-перевірка (наявність файлу) живе у JS.
|
|
10
|
+
#
|
|
11
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
12
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
13
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
14
|
+
package text.markdownlint
|
|
15
|
+
|
|
16
|
+
import rego.v1
|
|
17
|
+
|
|
18
|
+
deny contains msg if {
|
|
19
|
+
object.get(input, "gitignore", null) != true
|
|
20
|
+
msg := ".markdownlint-cli2.jsonc: додай на верхньому рівні \"gitignore\": true (text.mdc)"
|
|
21
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Порт перевірок `.oxfmtrc.json` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test .oxfmtrc.json -p npm/policy/text --namespace text.oxfmtrc
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: обовʼязкові ключі, канонічні значення (`semi=false`, `singleQuote=true`,
|
|
7
|
+
# `tabWidth=2`, `useTabs=false`, `printWidth=120`), масив `ignorePatterns` з
|
|
8
|
+
# канонічними glob-ами (hasura/metadata, schema.graphql, auto-imports.d.ts).
|
|
9
|
+
#
|
|
10
|
+
# FS-перевірки (наявність самого `.oxfmtrc.json`, `.prettierrc.*` файлів) живуть
|
|
11
|
+
# у `check-text.mjs`. Тут — лише про вже завантажений input.
|
|
12
|
+
#
|
|
13
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
14
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
15
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
16
|
+
package text.oxfmtrc
|
|
17
|
+
|
|
18
|
+
import rego.v1
|
|
19
|
+
|
|
20
|
+
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
required_keys := [
|
|
23
|
+
"arrowParens",
|
|
24
|
+
"printWidth",
|
|
25
|
+
"bracketSpacing",
|
|
26
|
+
"bracketSameLine",
|
|
27
|
+
"semi",
|
|
28
|
+
"singleQuote",
|
|
29
|
+
"tabWidth",
|
|
30
|
+
"trailingComma",
|
|
31
|
+
"useTabs",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
required_ignore_patterns := {
|
|
35
|
+
"**/hasura/metadata/**",
|
|
36
|
+
"**/schema.graphql",
|
|
37
|
+
"**/auto-imports.d.ts",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# ── deny: обовʼязкові ключі ────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
deny contains msg if {
|
|
43
|
+
some key in required_keys
|
|
44
|
+
not key in object.keys(input)
|
|
45
|
+
msg := sprintf(".oxfmtrc.json: відсутній обовʼязковий ключ %q (text.mdc)", [key])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ── deny: канонічні значення ───────────────────────────────────────────────
|
|
49
|
+
#
|
|
50
|
+
# `object.get(…, sentinel)` робить значення визначеним — інакше при відсутньому
|
|
51
|
+
# ключі порівняння дало б `undefined`, не `true`, і правило мовчки не спрацювало б.
|
|
52
|
+
|
|
53
|
+
deny contains msg if {
|
|
54
|
+
object.get(input, "semi", null) != false
|
|
55
|
+
msg := ".oxfmtrc.json: semi має бути false (text.mdc)"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
deny contains msg if {
|
|
59
|
+
object.get(input, "singleQuote", null) != true
|
|
60
|
+
msg := ".oxfmtrc.json: singleQuote має бути true (text.mdc)"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
deny contains msg if {
|
|
64
|
+
object.get(input, "tabWidth", null) != 2
|
|
65
|
+
msg := ".oxfmtrc.json: tabWidth має бути 2 (text.mdc)"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
deny contains msg if {
|
|
69
|
+
object.get(input, "useTabs", null) != false
|
|
70
|
+
msg := ".oxfmtrc.json: useTabs має бути false (text.mdc)"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
deny contains msg if {
|
|
74
|
+
object.get(input, "printWidth", null) != 120
|
|
75
|
+
msg := ".oxfmtrc.json: printWidth має бути 120 (text.mdc)"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# ── deny: ignorePatterns ───────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
deny contains msg if {
|
|
81
|
+
not is_array(object.get(input, "ignorePatterns", null))
|
|
82
|
+
msg := ".oxfmtrc.json: додай масив ignorePatterns з канонічними glob-ами (text.mdc)"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
deny contains msg if {
|
|
86
|
+
is_array(input.ignorePatterns)
|
|
87
|
+
some pattern in required_ignore_patterns
|
|
88
|
+
not pattern in {p | some p in input.ignorePatterns}
|
|
89
|
+
msg := sprintf(".oxfmtrc.json ignorePatterns: додай %q (text.mdc)", [pattern])
|
|
90
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Порт текст-специфічних перевірок `package.json` з `npm/scripts/check-text.mjs` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test package.json -p npm/policy/text --namespace text.package_json
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: відсутність Prettier (поле + конфіги в deps), `@nitra/cspell-dict ^2.0.0+`
|
|
7
|
+
# у `devDependencies`, заборона `markdownlint-cli2` у dependencies/devDependencies.
|
|
8
|
+
#
|
|
9
|
+
# Перевірка скрипта `lint-text` (cspell, run-shellcheck-text.mjs, markdownlint, v8r,
|
|
10
|
+
# обовʼязкові glob-и для v8r) — у JS-частині (`check-text.mjs`): занадто варіативна
|
|
11
|
+
# для декларативної політики (3 режими v8r з різними вимогами до глобів).
|
|
12
|
+
#
|
|
13
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
14
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
15
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
16
|
+
package text.package_json
|
|
17
|
+
|
|
18
|
+
import rego.v1
|
|
19
|
+
|
|
20
|
+
# ── Заборонені пакети у dependencies/devDependencies ──────────────────────
|
|
21
|
+
|
|
22
|
+
forbidden_packages := {
|
|
23
|
+
"prettier": "Prettier заборонено — використовуй oxfmt (text.mdc)",
|
|
24
|
+
"@nitra/prettier-config": "Prettier-конфіг заборонено — використовуй oxfmt (text.mdc)",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ── deny: заборонене поле `prettier` у package.json ───────────────────────
|
|
28
|
+
|
|
29
|
+
deny contains msg if {
|
|
30
|
+
object.get(input, "prettier", null) != null
|
|
31
|
+
msg := "package.json містить поле \"prettier\" — видали його (text.mdc)"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# ── deny: prettier у dependencies/devDependencies ─────────────────────────
|
|
35
|
+
|
|
36
|
+
deny contains msg if {
|
|
37
|
+
some pkg, hint in forbidden_packages
|
|
38
|
+
pkg in object.keys(object.get(input, "dependencies", {}))
|
|
39
|
+
msg := sprintf("package.json: dependencies містить %q — %s", [pkg, hint])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
deny contains msg if {
|
|
43
|
+
some pkg, hint in forbidden_packages
|
|
44
|
+
pkg in object.keys(object.get(input, "devDependencies", {}))
|
|
45
|
+
msg := sprintf("package.json: devDependencies містить %q — %s", [pkg, hint])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ── deny: markdownlint-cli2 не повинен бути у залежностях ─────────────────
|
|
49
|
+
#
|
|
50
|
+
# Канонічний виклик — `bunx markdownlint-cli2` у `lint-text`, без оголошення пакета.
|
|
51
|
+
|
|
52
|
+
deny contains msg if {
|
|
53
|
+
"markdownlint-cli2" in object.keys(object.get(input, "dependencies", {}))
|
|
54
|
+
msg := "package.json: dependencies містить markdownlint-cli2 — використовуй bunx у lint-text (text.mdc)"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
deny contains msg if {
|
|
58
|
+
"markdownlint-cli2" in object.keys(object.get(input, "devDependencies", {}))
|
|
59
|
+
msg := "package.json: devDependencies містить markdownlint-cli2 — використовуй bunx у lint-text (text.mdc)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# ── deny: @nitra/cspell-dict ^2.0.0+ обовʼязковий ─────────────────────────
|
|
63
|
+
|
|
64
|
+
deny contains msg if {
|
|
65
|
+
dev := object.get(input, "devDependencies", {})
|
|
66
|
+
not "@nitra/cspell-dict" in object.keys(dev)
|
|
67
|
+
msg := "@nitra/cspell-dict у devDependencies обовʼязковий — bun add -d @nitra/cspell-dict@^2.0.0 (text.mdc)"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
deny contains msg if {
|
|
71
|
+
range := object.get(object.get(input, "devDependencies", {}), "@nitra/cspell-dict", "")
|
|
72
|
+
range != ""
|
|
73
|
+
not cspell_dict_major_at_least_2(range)
|
|
74
|
+
msg := sprintf("@nitra/cspell-dict має бути ^2.0.0 або новіший (зараз %q) (text.mdc)", [range])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
# Чи мажорна версія cspell-dict ≥ 2. Підтримує `^2.0.0`, `~2.x`, `2.5.0`,
|
|
80
|
+
# `>=2.0.0`, `workspace:*` (тоді fallback false), із префіксом і без.
|
|
81
|
+
# Regex `^[\^~>=<]*\s*(\d+)` дістає першу цифру після опціональних range-операторів.
|
|
82
|
+
cspell_dict_major_at_least_2(range) if {
|
|
83
|
+
# `regex.find_n` повертає масив збігів; беремо перший і дивимось на перше число.
|
|
84
|
+
match := regex.find_n(`^[\^~>=<]*\s*(\d+)`, range, 1)
|
|
85
|
+
count(match) > 0
|
|
86
|
+
major := to_number(regex.replace(match[0], `^[\^~>=<]*\s*`, ""))
|
|
87
|
+
major >= 2
|
|
88
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Порт перевірки версій з `package.json` для Vue+Vite пакетів з
|
|
2
|
+
# `npm/scripts/check-vue.mjs` (vue.mdc).
|
|
3
|
+
#
|
|
4
|
+
# Запуск (локально, у Vue+Vite-пакеті):
|
|
5
|
+
# conftest test path/to/package.json -p npm/policy/vue \
|
|
6
|
+
# --namespace vue.package_json
|
|
7
|
+
#
|
|
8
|
+
# Перевіряє: якщо в `dependencies` є `vue`, то у `devDependencies.vite` має бути
|
|
9
|
+
# мажорна версія ≥ 8.
|
|
10
|
+
#
|
|
11
|
+
# AST-сканування коду (заборона явних value-імпортів `from 'vue'`, заборона
|
|
12
|
+
# Node-нативних модулів у `.vue` SFC, перевірка `vite.config` на
|
|
13
|
+
# `process.env.npm_lifecycle_event`, vue-macros, auto-import тощо), а також
|
|
14
|
+
# FS-перевірки (`src/vite-env.d.ts`, `jsconfig.json` у корені пакета) — у JS.
|
|
15
|
+
#
|
|
16
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
17
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
18
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
19
|
+
package vue.package_json
|
|
20
|
+
|
|
21
|
+
import rego.v1
|
|
22
|
+
|
|
23
|
+
deny contains msg if {
|
|
24
|
+
uses_vue
|
|
25
|
+
not vite_in_dev_dependencies
|
|
26
|
+
msg := "Vue-пакет: відсутня залежність `vite` у devDependencies (vue.mdc)"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
deny contains msg if {
|
|
30
|
+
uses_vue
|
|
31
|
+
vite_in_dev_dependencies
|
|
32
|
+
not vite_major_at_least_8
|
|
33
|
+
vite_range := input.devDependencies.vite
|
|
34
|
+
msg := sprintf("Vue-пакет: vite має бути >= 8 (зараз %q) (vue.mdc)", [vite_range])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
uses_vue if {
|
|
40
|
+
"vue" in object.keys(object.get(input, "dependencies", {}))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
vite_in_dev_dependencies if {
|
|
44
|
+
"vite" in object.keys(object.get(input, "devDependencies", {}))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
vite_major_at_least_8 if {
|
|
48
|
+
range := input.devDependencies.vite
|
|
49
|
+
|
|
50
|
+
# Перша мажорна цифра з рядка: `^8`, `>=8.0.0`, `8.x` → 8.
|
|
51
|
+
match := regex.find_n(`\d+`, range, 1)
|
|
52
|
+
count(match) > 0
|
|
53
|
+
to_number(match[0]) >= 8
|
|
54
|
+
}
|
package/scripts/auto-rules.mjs
CHANGED
|
@@ -41,6 +41,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
|
|
|
41
41
|
'nginx-default-tpl',
|
|
42
42
|
'npm-module',
|
|
43
43
|
'php',
|
|
44
|
+
'rego',
|
|
44
45
|
'style-lint',
|
|
45
46
|
'text',
|
|
46
47
|
'vue'
|
|
@@ -104,6 +105,7 @@ export const AUTO_RULE_DEPENDENCIES = Object.freeze(
|
|
|
104
105
|
const ABIE_REPOSITORY_URL_MARKER = 'https://github.com/abinbevefes/'
|
|
105
106
|
const HASURA_CONFIG_MARKER = 'metadata_directory: metadata'
|
|
106
107
|
const JS_LIKE_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx)$/iu
|
|
108
|
+
const REGO_RE = /\.rego$/iu
|
|
107
109
|
const STYLE_RE = /\.(?:css|vue)$/iu
|
|
108
110
|
const VUE_RE = /\.vue$/iu
|
|
109
111
|
const NGINX_DEFAULT_FILES = new Set(['default.conf.template', 'default.conf', 'nginx.conf'])
|
|
@@ -287,6 +289,7 @@ function updateDirFacts(dirName, facts) {
|
|
|
287
289
|
* hasDockerfile: boolean,
|
|
288
290
|
* hasJsLikeSource: boolean,
|
|
289
291
|
* hasNginxDefaultTplFile: boolean,
|
|
292
|
+
* hasRegoFile: boolean,
|
|
290
293
|
* hasVueOrCssSource: boolean,
|
|
291
294
|
* hasVueSource: boolean
|
|
292
295
|
* }} facts агреговані факти
|
|
@@ -311,6 +314,9 @@ function updateFileFacts(fileName, relPath, facts) {
|
|
|
311
314
|
if (STYLE_RE.test(relPath)) {
|
|
312
315
|
facts.hasVueOrCssSource = true
|
|
313
316
|
}
|
|
317
|
+
if (REGO_RE.test(relPath)) {
|
|
318
|
+
facts.hasRegoFile = true
|
|
319
|
+
}
|
|
314
320
|
}
|
|
315
321
|
|
|
316
322
|
/**
|
|
@@ -401,6 +407,7 @@ async function updateHasuraFactFromFile(absPath, fileName, facts) {
|
|
|
401
407
|
* hasHasuraConfig: boolean,
|
|
402
408
|
* hasJsLikeSource: boolean,
|
|
403
409
|
* hasNginxDefaultTplFile: boolean,
|
|
410
|
+
* hasRegoFile: boolean,
|
|
404
411
|
* hasVueOrCssSource: boolean,
|
|
405
412
|
* hasVueSource: boolean
|
|
406
413
|
* }} facts агреговані факти
|
|
@@ -489,6 +496,7 @@ export function isMonorepoPackage(packageJson) {
|
|
|
489
496
|
* hasJsLikeSource: boolean,
|
|
490
497
|
* hasK8sDir: boolean,
|
|
491
498
|
* hasNginxDefaultTplFile: boolean,
|
|
499
|
+
* hasRegoFile: boolean,
|
|
492
500
|
* hasTempoDir: boolean,
|
|
493
501
|
* hasVueSource: boolean,
|
|
494
502
|
* hasVueOrCssSource: boolean
|
|
@@ -505,6 +513,7 @@ export async function collectAutoRuleFacts(root) {
|
|
|
505
513
|
hasJsLikeSource: false,
|
|
506
514
|
hasK8sDir: false,
|
|
507
515
|
hasNginxDefaultTplFile: false,
|
|
516
|
+
hasRegoFile: false,
|
|
508
517
|
hasTempoDir: false,
|
|
509
518
|
hasVueSource: false,
|
|
510
519
|
hasVueOrCssSource: false
|
|
@@ -650,6 +659,7 @@ export async function detectAutoRulesAndSkills({
|
|
|
650
659
|
{ enabled: facts.hasNginxDefaultTplFile, id: 'nginx-default-tpl' },
|
|
651
660
|
{ enabled: npmDirExists, id: 'npm-module' },
|
|
652
661
|
{ enabled: composerJsonExists, id: 'php' },
|
|
662
|
+
{ enabled: facts.hasRegoFile, id: 'rego' },
|
|
653
663
|
{ enabled: facts.hasVueOrCssSource, id: 'style-lint' }
|
|
654
664
|
]
|
|
655
665
|
for (const item of autoRuleChecks) {
|
package/scripts/check-adr.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const PROJECT_SETTINGS_PATH = '.claude/settings.json'
|
|
|
26
26
|
const PROJECT_LOCAL_SETTINGS_PATH = '.claude/settings.local.json'
|
|
27
27
|
const PROJECT_LOG_PATH = '.claude/hooks/capture-decisions.log'
|
|
28
28
|
const HOOK_COMMAND_MARKER = '.claude/hooks/capture-decisions.sh'
|
|
29
|
+
const EOL_RE = /\r?\n/u
|
|
29
30
|
|
|
30
31
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
31
32
|
/** Канонічний bundled-скрипт у пакеті — джерело правди для звірки з проєктним. */
|
|
@@ -68,7 +69,10 @@ async function checkHookScript(reporter) {
|
|
|
68
69
|
fail(`канонічний скрипт у пакеті не знайдено: ${BUNDLED_HOOK_PATH} — перевстанови @nitra/cursor`)
|
|
69
70
|
return
|
|
70
71
|
}
|
|
71
|
-
const [project, bundled] = await Promise.all([
|
|
72
|
+
const [project, bundled] = await Promise.all([
|
|
73
|
+
readFile(PROJECT_HOOK_PATH, 'utf8'),
|
|
74
|
+
readFile(BUNDLED_HOOK_PATH, 'utf8')
|
|
75
|
+
])
|
|
72
76
|
if (project === bundled) {
|
|
73
77
|
pass(`${PROJECT_HOOK_PATH} збігається з канонічним`)
|
|
74
78
|
} else {
|
|
@@ -180,9 +184,9 @@ async function checkGitignore(reporter) {
|
|
|
180
184
|
}
|
|
181
185
|
const content = await readFile('.gitignore', 'utf8')
|
|
182
186
|
const covers = content
|
|
183
|
-
.split(
|
|
187
|
+
.split(EOL_RE)
|
|
184
188
|
.map(l => l.trim())
|
|
185
|
-
.some(gitignoreLineCoversHookLog)
|
|
189
|
+
.some(line => gitignoreLineCoversHookLog(line))
|
|
186
190
|
if (covers) {
|
|
187
191
|
pass(`.gitignore покриває ${PROJECT_LOG_PATH}`)
|
|
188
192
|
} else {
|