@nitra/cursor 1.9.23 → 1.11.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/.claude-template/hooks/capture-decisions.sh +3 -3
- package/.claude-template/hooks/normalize-decisions.sh +370 -0
- package/CHANGELOG.md +52 -0
- package/bin/n-cursor.js +30 -29
- package/package.json +2 -1
- package/rules/abie/js/applies/check.mjs +24 -0
- package/rules/abie/js/env_dns/check.mjs +53 -0
- package/rules/abie/js/firebase_hosting/check.mjs +49 -0
- package/rules/abie/js/hc_pairing/check.mjs +58 -0
- package/rules/abie/js/ua_http_route/check.mjs +86 -0
- package/rules/abie/js/ua_node_selector/check.mjs +65 -0
- package/rules/abie/policy/base_deployment_preem/target.json +10 -0
- package/rules/abie/policy/clean_merged_ignore_branches/target.json +4 -0
- package/rules/abie/policy/health_check_policy/target.json +4 -0
- package/rules/abie/policy/http_route_base/target.json +4 -0
- package/rules/abie/utils/enabled.mjs +35 -0
- package/rules/abie/utils/env-dns.mjs +81 -0
- package/rules/abie/utils/hc-yaml.mjs +27 -0
- package/rules/abie/utils/http-route.mjs +93 -0
- package/rules/abie/utils/k8s-tree.mjs +102 -0
- package/rules/abie/utils/kustomization-patches.mjs +224 -0
- package/rules/abie/utils/overlay-paths.mjs +97 -0
- package/rules/abie/utils/yaml.mjs +72 -0
- package/rules/adr/adr.mdc +82 -18
- package/rules/adr/js/check.mjs +84 -40
- package/rules/adr/policy/settings_json/settings_json.rego +17 -11
- package/rules/adr/policy/settings_json/target.json +4 -0
- package/rules/adr/policy/settings_local_json/settings_local_json.rego +24 -12
- package/rules/adr/policy/settings_local_json/target.json +4 -0
- package/rules/bun/policy/bunfig/target.json +4 -0
- package/rules/bun/policy/package_json/target.json +4 -0
- package/rules/capacitor/policy/package_json/target.json +4 -0
- package/rules/docker/policy/lint_docker_yml/target.json +4 -0
- package/rules/docker/policy/package_json/target.json +4 -0
- package/rules/hasura/policy/svc_hl/target.json +4 -0
- package/rules/image-avif/policy/package_json/target.json +4 -0
- package/rules/image-compress/policy/package_json/target.json +4 -0
- package/rules/js-bun-db/policy/package_json/target.json +4 -0
- package/rules/js-bun-redis/policy/package_json/target.json +4 -0
- package/rules/js-lint/policy/lint_js_yml/target.json +4 -0
- package/rules/js-lint/policy/package_json/target.json +4 -0
- package/rules/js-mssql/policy/package_json/target.json +4 -0
- package/rules/js-run/policy/configmap/target.json +4 -0
- package/rules/js-run/policy/package_json/target.json +4 -0
- package/rules/k8s/policy/base_kustomization/target.json +4 -0
- package/rules/k8s/policy/base_manifest/target.json +10 -0
- package/rules/k8s/policy/gateway/target.json +4 -0
- package/rules/k8s/policy/hpa_pdb/target.json +4 -0
- package/rules/k8s/policy/kustomization/target.json +4 -0
- package/rules/k8s/policy/manifest/target.json +4 -0
- package/rules/k8s/policy/svc_hl_yaml/target.json +4 -0
- package/rules/k8s/policy/svc_yaml/target.json +4 -0
- package/rules/npm-module/policy/emit_types_config/target.json +4 -0
- package/rules/npm-module/policy/npm_package_json/target.json +4 -0
- package/rules/npm-module/policy/npm_publish_yml/target.json +4 -0
- package/rules/npm-module/policy/root_package_json/target.json +4 -0
- package/rules/php/policy/lint_php_yml/target.json +4 -0
- package/rules/php/policy/package_json/target.json +4 -0
- package/rules/rego/js/applies/check.mjs +54 -0
- package/rules/rego/policy/package_json/target.json +5 -0
- package/rules/rego/policy/vscode_extensions/target.json +5 -0
- package/rules/rego/policy/vscode_settings/target.json +5 -0
- package/rules/style-lint/policy/lint_style_yml/target.json +4 -0
- package/rules/style-lint/policy/package_json/target.json +4 -0
- package/rules/style-lint/policy/vscode_extensions/target.json +4 -0
- package/rules/style-lint/policy/vscode_settings/target.json +4 -0
- package/rules/text/policy/cspell/target.json +4 -0
- package/rules/text/policy/markdownlint/target.json +4 -0
- package/rules/text/policy/oxfmtrc/target.json +4 -0
- package/rules/text/policy/package_json/target.json +4 -0
- package/rules/text/policy/vscode_extensions/target.json +4 -0
- package/rules/text/policy/vscode_settings/target.json +4 -0
- package/rules/vue/policy/package_json/target.json +4 -0
- package/schemas/target.json +58 -0
- package/scripts/auto-skills.mjs +2 -0
- package/scripts/lint-conftest.mjs +65 -414
- package/scripts/sync-claude-config.mjs +70 -14
- package/scripts/utils/discover-checkable-rules.mjs +123 -0
- package/scripts/utils/resolve-target-files.mjs +109 -0
- package/scripts/utils/run-rule.mjs +131 -0
- package/skills/adr-normalize/SKILL.md +71 -0
- package/skills/adr-normalize/auto.md +1 -0
- package/rules/abie/js/check.mjs +0 -1152
- package/rules/rego/js/check.mjs +0 -106
package/rules/adr/js/check.mjs
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Перевіряє вимоги правила adr.mdc: ADR Stop-hook capture-decisions.sh
|
|
2
|
+
* Перевіряє вимоги правила adr.mdc: ADR Stop-hook'и `capture-decisions.sh` і
|
|
3
|
+
* `normalize-decisions.sh` у Claude Code.
|
|
3
4
|
*
|
|
4
5
|
* Очікування:
|
|
5
|
-
* - `.claude/hooks/capture-decisions.sh`
|
|
6
|
-
* `.claude-template/hooks
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* -
|
|
6
|
+
* - `.claude/hooks/capture-decisions.sh` та `.claude/hooks/normalize-decisions.sh`
|
|
7
|
+
* існують і байт-у-байт збігаються з канонічними `.claude-template/hooks/*`
|
|
8
|
+
* пакета (sync керує файлами повністю).
|
|
9
|
+
* - `.claude/settings.json` (project-shared) має managed-групи у `hooks.Stop` для
|
|
10
|
+
* обох скриптів (маркери у `command` — самі шляхи до скриптів).
|
|
11
|
+
* - `.claude/settings.local.json` (якщо існує) НЕ має дублів цих managed-груп —
|
|
12
|
+
* після переходу на project-shared такі записи створили б два запуски на одну подію.
|
|
13
|
+
* - `.gitignore` у корені містить шаблон, який покриває
|
|
14
|
+
* `.claude/hooks/capture-decisions.log` і `.claude/hooks/normalize-decisions.log`.
|
|
12
15
|
*
|
|
13
16
|
* LLM CLI (`claude` або `cursor-agent`) у `PATH` — інформативна перевірка: якщо жодного
|
|
14
17
|
* немає, скрипт працює, але мовчки виходить, тому це warning, а не fail.
|
|
@@ -21,26 +24,48 @@ import { fileURLToPath } from 'node:url'
|
|
|
21
24
|
|
|
22
25
|
import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
/** Один hook-артефакт: bash-скрипт + його лог-файл, які перевіряємо однотипно. */
|
|
28
|
+
const HOOK_ARTIFACTS = /** @type {const} */ ([
|
|
29
|
+
{ scriptName: 'capture-decisions.sh', logName: 'capture-decisions.log' },
|
|
30
|
+
{ scriptName: 'normalize-decisions.sh', logName: 'normalize-decisions.log' }
|
|
31
|
+
])
|
|
32
|
+
|
|
25
33
|
const PROJECT_SETTINGS_PATH = '.claude/settings.json'
|
|
26
|
-
const PROJECT_LOG_PATH = '.claude/hooks/capture-decisions.log'
|
|
27
34
|
const EOL_RE = /\r?\n/u
|
|
28
35
|
|
|
29
36
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
const BUNDLED_HOOKS_DIR = join(here, '..', '..', '..', '.claude-template', 'hooks')
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Відносний шлях до managed hook-скрипта у проєкті.
|
|
41
|
+
* @param {string} scriptName базове ім'я скрипта (наприклад `capture-decisions.sh`)
|
|
42
|
+
* @returns {string} `.claude/hooks/<scriptName>`
|
|
43
|
+
*/
|
|
44
|
+
function projectHookPath(scriptName) {
|
|
45
|
+
return `.claude/hooks/${scriptName}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Відносний шлях до лог-файлу managed hook'а у проєкті.
|
|
50
|
+
* @param {string} logName базове ім'я лог-файлу (наприклад `capture-decisions.log`)
|
|
51
|
+
* @returns {string} `.claude/hooks/<logName>`
|
|
52
|
+
*/
|
|
53
|
+
function projectLogPath(logName) {
|
|
54
|
+
return `.claude/hooks/${logName}`
|
|
55
|
+
}
|
|
32
56
|
|
|
33
57
|
/**
|
|
34
|
-
* Чи містить рядок `.gitignore` шаблон, який покриває
|
|
58
|
+
* Чи містить рядок `.gitignore` шаблон, який покриває цей конкретний лог-файл хука.
|
|
35
59
|
* Враховує точний шлях, glob `.claude/hooks/*.log` та широкий glob `**\/*.log`.
|
|
36
60
|
* @param {string} line одна нормалізована (trim) лінія `.gitignore`
|
|
37
|
-
* @
|
|
61
|
+
* @param {string} logPath шлях `.claude/hooks/<name>.log`, який треба покрити
|
|
62
|
+
* @returns {boolean} `true`, якщо лінія матчить цей лог-файл
|
|
38
63
|
*/
|
|
39
|
-
function gitignoreLineCoversHookLog(line) {
|
|
64
|
+
function gitignoreLineCoversHookLog(line, logPath) {
|
|
40
65
|
if (!line || line.startsWith('#')) {
|
|
41
66
|
return false
|
|
42
67
|
}
|
|
43
|
-
if (line ===
|
|
68
|
+
if (line === logPath) {
|
|
44
69
|
return true
|
|
45
70
|
}
|
|
46
71
|
if (line === '.claude/hooks/*.log' || line === '.claude/hooks/**/*.log') {
|
|
@@ -53,28 +78,28 @@ function gitignoreLineCoversHookLog(line) {
|
|
|
53
78
|
}
|
|
54
79
|
|
|
55
80
|
/**
|
|
56
|
-
* Перевіряє наявність і канонічність
|
|
81
|
+
* Перевіряє наявність і канонічність одного hook-скрипта.
|
|
57
82
|
* @param {import('./utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
83
|
+
* @param {string} scriptName базове ім'я скрипта (наприклад `capture-decisions.sh`)
|
|
58
84
|
* @returns {Promise<void>}
|
|
59
85
|
*/
|
|
60
|
-
async function checkHookScript(reporter) {
|
|
86
|
+
async function checkHookScript(reporter, scriptName) {
|
|
61
87
|
const { pass, fail } = reporter
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
const projectPath = projectHookPath(scriptName)
|
|
89
|
+
const bundledPath = join(BUNDLED_HOOKS_DIR, scriptName)
|
|
90
|
+
if (!existsSync(projectPath)) {
|
|
91
|
+
fail(`${projectPath} не існує — запусти \`npx @nitra/cursor\` (правило adr копіює канонічний скрипт)`)
|
|
64
92
|
return
|
|
65
93
|
}
|
|
66
|
-
if (!existsSync(
|
|
67
|
-
fail(`канонічний скрипт у пакеті не знайдено: ${
|
|
94
|
+
if (!existsSync(bundledPath)) {
|
|
95
|
+
fail(`канонічний скрипт у пакеті не знайдено: ${bundledPath} — перевстанови @nitra/cursor`)
|
|
68
96
|
return
|
|
69
97
|
}
|
|
70
|
-
const [project, bundled] = await Promise.all([
|
|
71
|
-
readFile(PROJECT_HOOK_PATH, 'utf8'),
|
|
72
|
-
readFile(BUNDLED_HOOK_PATH, 'utf8')
|
|
73
|
-
])
|
|
98
|
+
const [project, bundled] = await Promise.all([readFile(projectPath, 'utf8'), readFile(bundledPath, 'utf8')])
|
|
74
99
|
if (project === bundled) {
|
|
75
|
-
pass(`${
|
|
100
|
+
pass(`${projectPath} збігається з канонічним`)
|
|
76
101
|
} else {
|
|
77
|
-
fail(`${
|
|
102
|
+
fail(`${projectPath} відрізняється від канонічного — запусти \`npx @nitra/cursor\` для повторного синку`)
|
|
78
103
|
}
|
|
79
104
|
}
|
|
80
105
|
|
|
@@ -95,25 +120,42 @@ function checkProjectSettings(reporter) {
|
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
/**
|
|
98
|
-
* Перевіряє `.gitignore` на ігнорування лог-файлу хука.
|
|
123
|
+
* Перевіряє `.gitignore` на ігнорування лог-файлу одного хука.
|
|
124
|
+
* @param {import('./utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
125
|
+
* @param {string} logName базове ім'я лог-файлу (наприклад `capture-decisions.log`)
|
|
126
|
+
* @param {string} gitignoreContent попередньо прочитаний вміст `.gitignore`
|
|
127
|
+
* @returns {void}
|
|
128
|
+
*/
|
|
129
|
+
function checkGitignoreForLog(reporter, logName, gitignoreContent) {
|
|
130
|
+
const { pass, fail } = reporter
|
|
131
|
+
const logPath = projectLogPath(logName)
|
|
132
|
+
const covers = gitignoreContent
|
|
133
|
+
.split(EOL_RE)
|
|
134
|
+
.map(l => l.trim())
|
|
135
|
+
.some(line => gitignoreLineCoversHookLog(line, logPath))
|
|
136
|
+
if (covers) {
|
|
137
|
+
pass(`.gitignore покриває ${logPath}`)
|
|
138
|
+
} else {
|
|
139
|
+
fail(`.gitignore не ігнорує \`${logPath}\` — додай цей рядок`)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Перевіряє `.gitignore` для всіх hook-логів одним проходом.
|
|
99
145
|
* @param {import('./utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
100
146
|
* @returns {Promise<void>}
|
|
101
147
|
*/
|
|
102
148
|
async function checkGitignore(reporter) {
|
|
103
|
-
const {
|
|
149
|
+
const { fail } = reporter
|
|
104
150
|
if (!existsSync('.gitignore')) {
|
|
105
|
-
|
|
151
|
+
for (const { logName } of HOOK_ARTIFACTS) {
|
|
152
|
+
fail(`.gitignore не існує — додай рядок \`${projectLogPath(logName)}\``)
|
|
153
|
+
}
|
|
106
154
|
return
|
|
107
155
|
}
|
|
108
156
|
const content = await readFile('.gitignore', 'utf8')
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
.map(l => l.trim())
|
|
112
|
-
.some(line => gitignoreLineCoversHookLog(line))
|
|
113
|
-
if (covers) {
|
|
114
|
-
pass(`.gitignore покриває ${PROJECT_LOG_PATH}`)
|
|
115
|
-
} else {
|
|
116
|
-
fail(`.gitignore не ігнорує \`${PROJECT_LOG_PATH}\` — додай цей рядок`)
|
|
157
|
+
for (const { logName } of HOOK_ARTIFACTS) {
|
|
158
|
+
checkGitignoreForLog(reporter, logName, content)
|
|
117
159
|
}
|
|
118
160
|
}
|
|
119
161
|
|
|
@@ -166,7 +208,9 @@ function checkLlmCliAvailable(reporter) {
|
|
|
166
208
|
*/
|
|
167
209
|
export async function check() {
|
|
168
210
|
const reporter = createCheckReporter()
|
|
169
|
-
|
|
211
|
+
for (const { scriptName } of HOOK_ARTIFACTS) {
|
|
212
|
+
await checkHookScript(reporter, scriptName)
|
|
213
|
+
}
|
|
170
214
|
checkProjectSettings(reporter)
|
|
171
215
|
await checkGitignore(reporter)
|
|
172
216
|
checkLlmCliAvailable(reporter)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#
|
|
2
|
-
# `hooks.Stop[*]` має містити
|
|
3
|
-
#
|
|
1
|
+
# Перевірка `.claude/settings.json` для правила adr.mdc:
|
|
2
|
+
# `hooks.Stop[*]` має містити дві managed-групи — capture і normalize — у
|
|
3
|
+
# кожній хоча б один елемент `hooks[]` з відповідним маркером у `command`.
|
|
4
4
|
#
|
|
5
5
|
# Запуск (локально):
|
|
6
|
-
# conftest test .claude/settings.json -p npm/
|
|
6
|
+
# conftest test .claude/settings.json -p npm/rules/adr/policy \
|
|
7
7
|
# --namespace adr.settings_json
|
|
8
8
|
#
|
|
9
|
-
# Hash-порівняння bash
|
|
10
|
-
# — у JS (`check
|
|
9
|
+
# Hash-порівняння bash-скриптів з канонічними bundled-варіантами і `.gitignore`-перевірки
|
|
10
|
+
# — у JS (`js/check.mjs`).
|
|
11
11
|
#
|
|
12
12
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
13
13
|
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
@@ -16,16 +16,22 @@ package adr.settings_json
|
|
|
16
16
|
|
|
17
17
|
import rego.v1
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
capture_marker := ".claude/hooks/capture-decisions.sh"
|
|
20
|
+
normalize_marker := ".claude/hooks/normalize-decisions.sh"
|
|
20
21
|
|
|
21
22
|
deny contains msg if {
|
|
22
|
-
not
|
|
23
|
+
not has_stop_hook_with_marker(capture_marker)
|
|
23
24
|
msg := ".claude/settings.json: відсутній Stop-hook для `capture-decisions.sh` у hooks.Stop (adr.mdc)"
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
deny contains msg if {
|
|
28
|
+
not has_stop_hook_with_marker(normalize_marker)
|
|
29
|
+
msg := ".claude/settings.json: відсутній Stop-hook для `normalize-decisions.sh` у hooks.Stop (adr.mdc)"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Чи є в `hooks.Stop[*].hooks[*].command` рядок з заданим маркером.
|
|
33
|
+
has_stop_hook_with_marker(marker) if {
|
|
28
34
|
some group in object.get(object.get(input, "hooks", {}), "Stop", [])
|
|
29
35
|
some hook in object.get(group, "hooks", [])
|
|
30
|
-
contains(object.get(hook, "command", ""),
|
|
36
|
+
contains(object.get(hook, "command", ""), marker)
|
|
31
37
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# інакше
|
|
1
|
+
# Перевірка `.claude/settings.local.json` для правила adr.mdc: після переходу на
|
|
2
|
+
# project-shared `settings.json` цей файл (якщо є) НЕ повинен мати дубля жодного
|
|
3
|
+
# з керованих Stop-хуків (`capture-decisions.sh` або `normalize-decisions.sh`),
|
|
4
|
+
# інакше відповідний скрипт виконається двічі на одну подію.
|
|
5
5
|
#
|
|
6
6
|
# Запуск (локально):
|
|
7
|
-
# conftest test .claude/settings.local.json -p npm/
|
|
7
|
+
# conftest test .claude/settings.local.json -p npm/rules/adr/policy \
|
|
8
8
|
# --namespace adr.settings_local_json
|
|
9
9
|
#
|
|
10
10
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
@@ -14,15 +14,27 @@ package adr.settings_local_json
|
|
|
14
14
|
|
|
15
15
|
import rego.v1
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
capture_marker := ".claude/hooks/capture-decisions.sh"
|
|
18
|
+
normalize_marker := ".claude/hooks/normalize-decisions.sh"
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
deny contains msg if {
|
|
21
|
+
has_stop_hook_with_marker(capture_marker)
|
|
22
|
+
msg := concat(" ", [
|
|
23
|
+
".claude/settings.local.json: видали дубль Stop-хука для",
|
|
24
|
+
"`capture-decisions.sh` — він уже у project-shared settings.json (adr.mdc)",
|
|
25
|
+
])
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
deny contains msg if {
|
|
29
|
+
has_stop_hook_with_marker(normalize_marker)
|
|
30
|
+
msg := concat(" ", [
|
|
31
|
+
".claude/settings.local.json: видали дубль Stop-хука для",
|
|
32
|
+
"`normalize-decisions.sh` — він уже у project-shared settings.json (adr.mdc)",
|
|
33
|
+
])
|
|
34
|
+
}
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
has_stop_hook_with_marker(marker) if {
|
|
25
37
|
some group in object.get(object.get(input, "hooks", {}), "Stop", [])
|
|
26
38
|
some hook in object.get(group, "hooks", [])
|
|
27
|
-
contains(object.get(hook, "command", ""),
|
|
39
|
+
contains(object.get(hook, "command", ""), marker)
|
|
28
40
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies-гейт правила `rego` (rego.mdc): правило застосовне, лише якщо в репозиторії є
|
|
3
|
+
* хоча б один `.rego`-файл (під типовими skip-ами і `.n-cursor.json:ignore`).
|
|
4
|
+
*
|
|
5
|
+
* Якщо `.rego` нема — CLI пропускає правило цілком (включно з polices `package_json`,
|
|
6
|
+
* `vscode_extensions`, `vscode_settings`), бо вимоги rego-tooling неактуальні. Якщо є — CLI
|
|
7
|
+
* прогонить policy-концерни через `target.json`-маніфести у `rules/rego/policy/<name>/`.
|
|
8
|
+
*
|
|
9
|
+
* JS тут лишається лише як cross-file гейт: walkDir не виразити декларативно через `target.json`.
|
|
10
|
+
* Друк короткого pass-повідомлення з контекстом робить `check()` (необовʼязковий).
|
|
11
|
+
*/
|
|
12
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
13
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
14
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Чи є хоча б один `.rego`-файл у дереві від `cwd`. Зупиняється на першому матчі.
|
|
18
|
+
* @param {string} root абсолютний шлях кореня
|
|
19
|
+
* @param {string[]} ignorePaths шляхи каталогів, повністю виключених з обходу
|
|
20
|
+
* @returns {Promise<boolean>} `true`, якщо знайдено хоч один `.rego`
|
|
21
|
+
*/
|
|
22
|
+
async function projectHasRegoFiles(root, ignorePaths) {
|
|
23
|
+
let found = false
|
|
24
|
+
await walkDir(
|
|
25
|
+
root,
|
|
26
|
+
p => {
|
|
27
|
+
if (p.endsWith('.rego')) {
|
|
28
|
+
found = true
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
ignorePaths
|
|
32
|
+
)
|
|
33
|
+
return found
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Rule-level applies-гейт: CLI пропускає правило, якщо в репо немає `.rego` файлів.
|
|
38
|
+
* @returns {Promise<boolean>} `true`, якщо правило застосовне
|
|
39
|
+
*/
|
|
40
|
+
export async function applies() {
|
|
41
|
+
const root = process.cwd()
|
|
42
|
+
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
43
|
+
return projectHasRegoFiles(root, ignorePaths)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Друкує короткий context-pass — самі полісі прогонить CLI через `policy/<name>/target.json`.
|
|
48
|
+
* @returns {Promise<number>} 0 — все ок (фактичні порушення повертають policy-концерни)
|
|
49
|
+
*/
|
|
50
|
+
export async function check() {
|
|
51
|
+
const reporter = createCheckReporter()
|
|
52
|
+
reporter.pass('Знайдено *.rego у дереві — перевіряємо канонічні конфіги rego.mdc')
|
|
53
|
+
return reporter.getExitCode()
|
|
54
|
+
}
|