@nitra/cursor 1.11.4 → 1.11.6
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 +24 -1
- package/bin/n-cursor.js +37 -4
- package/package.json +2 -1
- package/rules/abie/utils/http-route.mjs +1 -1
- package/rules/abie/utils/k8s-tree.mjs +9 -10
- package/rules/abie/utils/overlay-paths.mjs +1 -1
- package/rules/adr/adr.mdc +2 -2
- package/rules/adr/js/hooks/check.mjs +5 -5
- package/rules/docker/docker.mdc +2 -2
- package/rules/docker/js/run.mjs +3 -2
- package/rules/docker/policy/package_json/package_json.rego +1 -1
- package/rules/ga/js/lint.mjs +3 -26
- package/rules/k8s/js/run.mjs +3 -2
- package/rules/k8s/k8s.mdc +2 -4
- package/rules/npm-module/js/package_structure/check.mjs +2 -2
- package/rules/npm-module/npm-module.mdc +3 -3
- package/rules/rego/js/lint.mjs +4 -1
- package/rules/rego/policy/package_json/package_json.rego +5 -3
- package/rules/rego/rego.mdc +3 -3
- package/rules/style-lint/js/tooling/check.mjs +1 -1
- package/rules/style-lint/style-lint.mdc +1 -1
- package/rules/text/js/formatting/check.mjs +8 -24
- package/rules/text/js/lint.mjs +34 -0
- package/rules/text/js/run-shellcheck.mjs +2 -2
- package/rules/text/js/run-v8r.mjs +2 -2
- package/rules/text/text.mdc +5 -5
- package/schemas/v8r-catalog.json +6 -0
- package/scripts/auto-skills.mjs +1 -3
- package/scripts/utils/resolve-target-files.mjs +1 -1
- package/scripts/utils/run-lint-step.mjs +33 -0
- package/scripts/utils/run-rule.mjs +2 -1
- package/skills/abie-clean/SKILL.md +9 -5
- package/skills/fix/SKILL.md +3 -7
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem_test.rego +0 -60
- package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches_test.rego +0 -48
- package/rules/abie/policy/health_check_policy/health_check_policy_test.rego +0 -99
- package/rules/abie/policy/http_route_base/http_route_base_test.rego +0 -64
- package/rules/bun/policy/package_json/package_json_test.rego +0 -109
- package/rules/docker/policy/lint_docker_yml/lint_docker_yml_test.rego +0 -104
- package/rules/docker/policy/package_json/package_json_test.rego +0 -42
- package/rules/graphql/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
- package/rules/image-avif/policy/package_json/package_json_test.rego +0 -69
- package/rules/js-lint/policy/package_json/package_json_test.rego +0 -130
- package/rules/js-run/policy/jsconfig/jsconfig_test.rego +0 -88
- package/rules/k8s/policy/base_kustomization/base_kustomization_test.rego +0 -73
- package/rules/k8s/policy/base_manifest/base_manifest_test.rego +0 -94
- package/rules/k8s/policy/gateway/gateway_test.rego +0 -122
- package/rules/k8s/policy/hasura_configmap/hasura_configmap_test.rego +0 -49
- package/rules/k8s/policy/hasura_httproute/hasura_httproute_test.rego +0 -148
- package/rules/k8s/policy/hpa_pdb/hpa_pdb_test.rego +0 -101
- package/rules/k8s/policy/kustomization/kustomization_test.rego +0 -128
- package/rules/k8s/policy/manifest/manifest_test.rego +0 -309
- package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml_test.rego +0 -42
- package/rules/k8s/policy/svc_yaml/svc_yaml_test.rego +0 -41
- package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions_test.rego +0 -30
- package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings_test.rego +0 -53
- package/rules/npm-module/policy/npm_package_json/npm_package_json_test.rego +0 -81
- package/rules/rego/policy/package_json/package_json_test.rego +0 -42
- package/rules/rego/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
- package/rules/rego/policy/vscode_settings/vscode_settings_test.rego +0 -55
- package/rules/style-lint/policy/vscode_extensions/vscode_extensions_test.rego +0 -39
- package/rules/style-lint/policy/vscode_settings/vscode_settings_test.rego +0 -49
- package/rules/tauri/policy/vscode_extensions/vscode_extensions_test.rego +0 -44
- package/rules/text/policy/markdownlint/markdownlint_test.rego +0 -98
- package/rules/text/policy/vscode_extensions/vscode_extensions_test.rego +0 -51
- package/rules/text/policy/vscode_settings/vscode_settings_test.rego +0 -85
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.11.6] - 2026-05-15
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **`npm/rules/npm-module/npm-module.mdc`** — переформульовано вимогу про тести й фікстури. Раніше правило вимагало тримати їх **поза** будь-яким шляхом з `"files"` (канонічно — у `npm/tests/`). Тепер тести/фікстури можуть лежати **поруч з кодом** усередині `"files"`-шляхів, але `"files"` обовʼязково має містити **негативні glob-патерни**, що виключають їх із tarball (`!**/*.test.*`, `!**/*.spec.*`, `!**/test-helpers.*`, `!**/fixtures/**`, `!**/__tests__/**`, опційно `!**/*_test.rego`). Це краще відповідає реальному layout пакета (co-located test-файли у `rules/<id>/js/<concern>/`) і прибирає роз'їзд правила з фактичним `npm/package.json`. Версію `.mdc` піднято до `1.12`.
|
|
12
|
+
- **`npm/rules/npm-module/js/package_structure/check.mjs::checkNoTestsInPublishedFiles`** — текст fail-повідомлення тепер однозначно радить додати негативний glob у `"files"`, без альтернативи «винеси за межі шляхів з "files"». Логіка перевірки (walk positive ∖ negative + класифікація test-style) не змінилась — пере-кваліфіковано лише підказку для агента й людини.
|
|
13
|
+
|
|
14
|
+
## [1.11.5] - 2026-05-15
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`npm/bin/n-cursor.js`** — підкоманди `lint-rego`, `lint-k8s`, `lint-docker`, `lint-text` (раніше був лише `lint-ga`). Споживчі `package.json` тепер можуть використовувати уніфіковану форму `n-cursor lint-X` замість прямих посилань на файли `bun ./npm/scripts/*.mjs`, які після phase 2 концерн-сплету переїхали у `npm/rules/<id>/js/`. `lint-text` — композитний: послідовно `cspell .` → `runShellcheckText()` → `bunx markdownlint-cli2 --fix "**/*.md" "**/*.mdc"` → `runV8rWithGlobs()`.
|
|
19
|
+
- **`npm/rules/text/js/lint.mjs`** — новий ентрі-модуль для канонічного `lint-text`.
|
|
20
|
+
- **`npm/scripts/utils/run-lint-step.mjs`** — спільний хелпер `runLintStep(title, cmd, args)` для CLI-обгорток `lint-<rule>` (раніше дублювалося у `rules/ga/js/lint.mjs` і новому `rules/text/js/lint.mjs` — jscpd-clone).
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **`npm/rules/k8s/js/run.mjs`**, **`npm/rules/docker/js/run.mjs`** — `main()` перейменовано і експортовано як `runLintK8s` / `runLintDocker` для виклику з CLI-маршрутизатора. `isRunAsCli()`-гілка прямого запуску збережена для зворотної сумісності.
|
|
25
|
+
- **`npm/rules/rego/js/lint.mjs`** — авто-виклик `runLintRego()` тепер обгорнуто `if (isRunAsCli())`, інакше імпорт модуля з CLI запускав би лінт як side-effect.
|
|
26
|
+
- **`npm/rules/rego/policy/package_json/`** — канонічне значення `scripts.lint-rego` змінено на `"n-cursor lint-rego"` (раніше `"bun ./npm/scripts/lint-rego.mjs"`). Аналогічно `npm/rules/docker/policy/package_json/` — на `"n-cursor lint-docker"`.
|
|
27
|
+
- **`npm/rules/text/js/formatting/check.mjs`** — `checkLintTextScript()` тепер вимагає рівно `"n-cursor lint-text"` замість попередньої складної валідації багатоступеневого ланцюжка (`cspell` → `run-shellcheck-text.mjs` → `markdownlint-cli2` → `run-v8r.mjs`). Канонічна форма — одне посилання на CLI, а зміст ланцюжка живе у `npm/rules/text/js/lint.mjs`.
|
|
28
|
+
- **`npm/package.json#files`** — додано негативний glob `"!**/*_test.rego"` (раніше були лише `*.test.mjs`, `test-helpers.mjs`, `fixtures/**`). 33 rego-юніт-тестових файли (`<policy>_test.rego`) більше не потрапляють у tarball — їх виконує лише `conftest verify` у dev-репо.
|
|
29
|
+
|
|
7
30
|
## [1.11.4] - 2026-05-15
|
|
8
31
|
|
|
9
32
|
### Fixed
|
|
@@ -83,7 +106,7 @@
|
|
|
83
106
|
|
|
84
107
|
### Fixed
|
|
85
108
|
|
|
86
|
-
- `npm/package.json#files`: додано негативні glob-патерни `!**/*.test.mjs`, `!**/test-helpers.mjs`, `!**/fixtures/**`, щоб після переїзду тестів у `rules/<rule>/js/`, `scripts/`, `scripts/utils/` вони не потрапляли в опубліковану npm
|
|
109
|
+
- `npm/package.json#files`: додано негативні glob-патерни `!**/*.test.mjs`, `!**/test-helpers.mjs`, `!**/fixtures/**`, щоб після переїзду тестів у `rules/<rule>/js/`, `scripts/`, `scripts/utils/` вони не потрапляли в опубліковану npm-tarball (вимагає правило `npm-module`).
|
|
87
110
|
- `npm/package.json#devDependencies`: додано `@nitra/cursor: ^1.9.22` (auto-fill від `ensure-nitra-cursor-dev-dependencies.mjs`).
|
|
88
111
|
|
|
89
112
|
## [1.9.22] - 2026-05-14
|
package/bin/n-cursor.js
CHANGED
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
* інакше викликає `check`); прописується автоматично в `.claude/settings.json`
|
|
14
14
|
* `npx \@nitra/cursor lint-ga` — канонічний lint-ga (ga.mdc): preflight на `shellcheck` →
|
|
15
15
|
* `bunx github-actionlint` → `uvx zizmor --offline --collect=workflows .`
|
|
16
|
+
* `npx \@nitra/cursor lint-rego` — канонічний lint-rego (conftest.mdc + rego.mdc):
|
|
17
|
+
* preflight на `opa`/`regal` → `opa check --strict` → `regal lint` → опц. `conftest verify`
|
|
18
|
+
* `npx \@nitra/cursor lint-k8s` — канонічний lint-k8s (k8s.mdc): `kubeconform` + `kubescape` по `…/k8s/*.yaml`
|
|
19
|
+
* `npx \@nitra/cursor lint-docker` — канонічний lint-docker (docker.mdc): `hadolint` по `Dockerfile`/`*.Dockerfile`
|
|
20
|
+
* `npx \@nitra/cursor lint-text` — канонічний lint-text (text.mdc): `cspell` → `shellcheck` (з auto-fix) →
|
|
21
|
+
* `markdownlint-cli2 --fix` → `v8r` (json/json5/yaml/yml/toml)
|
|
16
22
|
*
|
|
17
23
|
* Claude Code інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
|
|
18
24
|
* синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
|
|
@@ -68,7 +74,11 @@ import { detectAutoSkills } from '../scripts/auto-skills.mjs'
|
|
|
68
74
|
import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
|
|
69
75
|
import { discoverCheckableRules } from '../scripts/utils/discover-checkable-rules.mjs'
|
|
70
76
|
import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
|
|
77
|
+
import { runLintDocker } from '../rules/docker/js/run.mjs'
|
|
71
78
|
import { runLintGaCli } from '../rules/ga/js/lint.mjs'
|
|
79
|
+
import { runLintK8s } from '../rules/k8s/js/run.mjs'
|
|
80
|
+
import { runLintRego } from '../rules/rego/js/lint.mjs'
|
|
81
|
+
import { runLintTextCli } from '../rules/text/js/lint.mjs'
|
|
72
82
|
import { runRule } from '../scripts/utils/run-rule.mjs'
|
|
73
83
|
import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
|
|
74
84
|
import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
|
|
@@ -88,7 +98,6 @@ const RULE_PREFIX = 'n-'
|
|
|
88
98
|
|
|
89
99
|
const binDir = dirname(fileURLToPath(import.meta.url))
|
|
90
100
|
const BUNDLED_RULES_DIR = join(binDir, '..', 'rules')
|
|
91
|
-
const BUNDLED_SCRIPTS_DIR = join(binDir, '..', 'scripts')
|
|
92
101
|
const BUNDLED_SKILLS_DIR = join(binDir, '..', 'skills')
|
|
93
102
|
const BUNDLED_AGENTS_TEMPLATE_PATH = join(binDir, '..', AGENTS_TEMPLATE_FILE)
|
|
94
103
|
/** Корінь установленого пакету (каталог з `rules/`, `github-actions/`, …) */
|
|
@@ -1000,9 +1009,9 @@ function logRemovedManagedItems(title, basePath, names) {
|
|
|
1000
1009
|
* (плюс legacy `rules/<id>/js/check.mjs` як концерн `legacy`) або policy-концерн у
|
|
1001
1010
|
* `rules/<id>/policy/<concern>/target.json`. Делегує у `discoverCheckableRules` —
|
|
1002
1011
|
* див. `scripts/utils/discover-checkable-rules.mjs`.
|
|
1003
|
-
* @returns {
|
|
1012
|
+
* @returns {import('../scripts/utils/discover-checkable-rules.mjs').CheckableRule[]} опис правил у алфавітному порядку
|
|
1004
1013
|
*/
|
|
1005
|
-
|
|
1014
|
+
function discoverCheckScripts() {
|
|
1006
1015
|
return discoverCheckableRules(BUNDLED_RULES_DIR)
|
|
1007
1016
|
}
|
|
1008
1017
|
|
|
@@ -1348,6 +1357,30 @@ try {
|
|
|
1348
1357
|
|
|
1349
1358
|
break
|
|
1350
1359
|
}
|
|
1360
|
+
case 'lint-rego': {
|
|
1361
|
+
// Канонічний lint-rego: preflight opa/regal → opa check --strict → regal lint → conftest verify (опц.).
|
|
1362
|
+
process.exitCode = runLintRego()
|
|
1363
|
+
|
|
1364
|
+
break
|
|
1365
|
+
}
|
|
1366
|
+
case 'lint-k8s': {
|
|
1367
|
+
// Канонічний lint-k8s: kubeconform + kubescape по знайдених деревах `…/k8s/*.yaml`.
|
|
1368
|
+
process.exitCode = await runLintK8s()
|
|
1369
|
+
|
|
1370
|
+
break
|
|
1371
|
+
}
|
|
1372
|
+
case 'lint-docker': {
|
|
1373
|
+
// Канонічний lint-docker: hadolint по Dockerfile та *.Dockerfile (docker.mdc).
|
|
1374
|
+
process.exitCode = await runLintDocker()
|
|
1375
|
+
|
|
1376
|
+
break
|
|
1377
|
+
}
|
|
1378
|
+
case 'lint-text': {
|
|
1379
|
+
// Канонічний lint-text: cspell → run-shellcheck → markdownlint-cli2 --fix → run-v8r (text.mdc).
|
|
1380
|
+
process.exitCode = runLintTextCli()
|
|
1381
|
+
|
|
1382
|
+
break
|
|
1383
|
+
}
|
|
1351
1384
|
case undefined:
|
|
1352
1385
|
case '': {
|
|
1353
1386
|
await runSync()
|
|
@@ -1357,7 +1390,7 @@ try {
|
|
|
1357
1390
|
default: {
|
|
1358
1391
|
console.error(`❌ Невідома команда: ${command}`)
|
|
1359
1392
|
console.error(
|
|
1360
|
-
` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook, lint-ga`
|
|
1393
|
+
` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text`
|
|
1361
1394
|
)
|
|
1362
1395
|
process.exitCode = 1
|
|
1363
1396
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitra/cursor",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.6",
|
|
4
4
|
"description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"AGENTS.template.md",
|
|
35
35
|
"CHANGELOG.md",
|
|
36
36
|
"!**/*.test.mjs",
|
|
37
|
+
"!**/*_test.rego",
|
|
37
38
|
"!**/test-helpers.mjs",
|
|
38
39
|
"!**/fixtures/**"
|
|
39
40
|
],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Cross-документна аналітика abie HTTPRoute: підрахунок `backendRefs` до спільних
|
|
3
3
|
* сервісів (`auth-run-hl`, `file-link-hl`) у base-маніфестах пакета (поза overlay `ua`).
|
|
4
4
|
* Використовується ua_http_route-концерном для синхронізації числа patch-ів namespace
|
|
5
|
-
* у overlay із кількістю base-
|
|
5
|
+
* у overlay із кількістю base-reference.
|
|
6
6
|
*/
|
|
7
7
|
import { relative } from 'node:path'
|
|
8
8
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Кеш — module-level singleton, ключований за `(root, ignorePaths)`. Перший виклик
|
|
7
7
|
* платить за обхід; наступні концерни в межах того ж прогону отримують готове.
|
|
8
|
-
* Для тестів — `resetAbieK8sTreeCache()` (інакше withTmpCwd-фікстури злипатимуться).
|
|
9
8
|
*/
|
|
10
9
|
import { dirname, relative } from 'node:path'
|
|
11
10
|
|
|
@@ -20,15 +19,6 @@ const yamlCache = new Map()
|
|
|
20
19
|
/** @type {Map<string, Promise<Set<string>>>} */
|
|
21
20
|
const deploymentCache = new Map()
|
|
22
21
|
|
|
23
|
-
/**
|
|
24
|
-
* Скидає кеш — тести мусять викликати між фікстурами.
|
|
25
|
-
* @returns {void} результат
|
|
26
|
-
*/
|
|
27
|
-
export function resetAbieK8sTreeCache() {
|
|
28
|
-
yamlCache.clear()
|
|
29
|
-
deploymentCache.clear()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
22
|
/**
|
|
33
23
|
* Стабільний ключ кешу за (root, ignorePaths).
|
|
34
24
|
* @param {string} root опис.
|
|
@@ -86,6 +76,15 @@ const silentFail = _msg => {
|
|
|
86
76
|
// noop
|
|
87
77
|
}
|
|
88
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Знаходить унікальні каталоги, що містять Deployment-маніфести серед переданих YAML-файлів.
|
|
81
|
+
* Парсить документи через `readAndParseYamlDocs` і фільтрує лише ті, що є Deployment.
|
|
82
|
+
* Кешує результат за ключем `root|<sorted yamlAbs>`, щоб повторні виклики не робили I/O.
|
|
83
|
+
* @param {string} root абсолютний корінь репо для побудови relative-шляхів у повідомленнях
|
|
84
|
+
* @param {string[]} yamlAbs абсолютні шляхи до YAML-файлів для перевірки
|
|
85
|
+
* @param {(msg: string) => void} [fail] callback на помилку парсингу (за замовчуванням noop)
|
|
86
|
+
* @returns {Promise<Set<string>>} проміс із сетом абсолютних каталогів, де знайдено Deployment
|
|
87
|
+
*/
|
|
89
88
|
export function collectDeploymentDirs(root, yamlAbs, fail = silentFail) {
|
|
90
89
|
const key = `${root}|${[...yamlAbs].toSorted((a, b) => a.localeCompare(b)).join(':')}`
|
|
91
90
|
const cached = deploymentCache.get(key)
|
|
@@ -16,7 +16,7 @@ const TRAILING_SLASH_RE = /\/$/u
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Чи `rel` — це `…/ua/kustomization.yaml` (abie overlay).
|
|
19
|
-
* @param {string} rel
|
|
19
|
+
* @param {string} rel posix-шлях від кореня репозиторію
|
|
20
20
|
* @returns {boolean} результат
|
|
21
21
|
*/
|
|
22
22
|
export function isUaKustomizationPath(rel) {
|
package/rules/adr/adr.mdc
CHANGED
|
@@ -39,7 +39,7 @@ Stop-hook `normalize-decisions.sh` спрацьовує на тому самом
|
|
|
39
39
|
LLM повертає масив операцій:
|
|
40
40
|
|
|
41
41
|
| `op` | Семантика | Поля |
|
|
42
|
-
|
|
42
|
+
| --- | --- | --- |
|
|
43
43
|
| `delete` | Чернетка тривіальна / повністю покрита іншим clean-ADR-ом. | `file`, `reason` |
|
|
44
44
|
| `rewrite` | Чернетка стає окремим clean-файлом: frontmatter знімається, ім'я → `<slug>.md`, додаються `**Status: Accepted**` і `**Date:**` з `captured`. | `file`, `slug`, `content` |
|
|
45
45
|
| `merge-into` | Чернетка повторює тему вже існуючого clean-файлу; дописуємо `## Update YYYY-MM-DD` у кінець `target`. | `file`, `target`, `additions` |
|
|
@@ -55,7 +55,7 @@ LLM повертає масив операцій:
|
|
|
55
55
|
Інший LLM CLI, який запустить normalize, успадковує `ADR_NORMALIZE_RUNNING=1` — внутрішній Stop-hook вийде відразу. Доступні ENV:
|
|
56
56
|
|
|
57
57
|
| Змінна | Default | Призначення |
|
|
58
|
-
|
|
58
|
+
| --- | --- | --- |
|
|
59
59
|
| `ADR_NORMALIZE_THRESHOLD` | `30` | Поріг чернеток для запуску фази. |
|
|
60
60
|
| `ADR_NORMALIZE_BATCH` | `30` | Максимум чернеток у одному виклику LLM. |
|
|
61
61
|
| `ADR_NORMALIZE_MIN_INTERVAL_HOURS` | `6` | Мінімум між спробами (навіть якщо поріг). |
|
|
@@ -79,7 +79,7 @@ function gitignoreLineCoversHookLog(line, logPath) {
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Перевіряє наявність і канонічність одного hook-скрипта.
|
|
82
|
-
* @param {import('
|
|
82
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
83
83
|
* @param {string} scriptName базове ім'я скрипта (наприклад `capture-decisions.sh`)
|
|
84
84
|
* @returns {Promise<void>}
|
|
85
85
|
*/
|
|
@@ -108,7 +108,7 @@ async function checkHookScript(reporter, scriptName) {
|
|
|
108
108
|
* `.claude/settings.local.json`. Структуру (`hooks.Stop[]` містить групу з
|
|
109
109
|
* `capture-decisions.sh`; `settings.local.json` не дублює) валідують
|
|
110
110
|
* `npm/policy/adr/settings_json/` і `npm/policy/adr/settings_local_json/`.
|
|
111
|
-
* @param {import('
|
|
111
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер
|
|
112
112
|
*/
|
|
113
113
|
function checkProjectSettings(reporter) {
|
|
114
114
|
const { pass, fail } = reporter
|
|
@@ -121,7 +121,7 @@ function checkProjectSettings(reporter) {
|
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Перевіряє `.gitignore` на ігнорування лог-файлу одного хука.
|
|
124
|
-
* @param {import('
|
|
124
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
125
125
|
* @param {string} logName базове ім'я лог-файлу (наприклад `capture-decisions.log`)
|
|
126
126
|
* @param {string} gitignoreContent попередньо прочитаний вміст `.gitignore`
|
|
127
127
|
* @returns {void}
|
|
@@ -142,7 +142,7 @@ function checkGitignoreForLog(reporter, logName, gitignoreContent) {
|
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* Перевіряє `.gitignore` для всіх hook-логів одним проходом.
|
|
145
|
-
* @param {import('
|
|
145
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
146
146
|
* @returns {Promise<void>}
|
|
147
147
|
*/
|
|
148
148
|
async function checkGitignore(reporter) {
|
|
@@ -182,7 +182,7 @@ function isBinaryInPath(name) {
|
|
|
182
182
|
/**
|
|
183
183
|
* Інформативна перевірка: чи доступний бодай один LLM CLI (`claude` або `cursor-agent`).
|
|
184
184
|
* Якщо жодного немає — це warning (`pass` з підказкою), бо хук просто мовчки no-op'ає.
|
|
185
|
-
* @param {import('
|
|
185
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер для збору результатів
|
|
186
186
|
* @returns {void}
|
|
187
187
|
*/
|
|
188
188
|
function checkLlmCliAvailable(reporter) {
|
package/rules/docker/docker.mdc
CHANGED
|
@@ -97,7 +97,7 @@ CMD ["./app"]
|
|
|
97
97
|
|
|
98
98
|
## lint-docker
|
|
99
99
|
|
|
100
|
-
CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE...]` у **`hadolint --help`**); обхід репозиторію робить
|
|
100
|
+
CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE...]` у **`hadolint --help`**); обхід репозиторію робить CLI **`n-cursor lint-docker`** (реалізація — **`npm/rules/docker/js/run.mjs`**).
|
|
101
101
|
|
|
102
102
|
**Область lint-docker (вужча, ніж `check docker`):** лише файли з іменем **`Dockerfile`** та **`*.Dockerfile`** (суфікс **`.dockerfile`** без урахування регістру, наприклад **`api.Dockerfile`**). Файли **`Dockerfile.prod`**, **`Containerfile`** тощо **не** входять у **`lint-docker`**; їх ловить **`check docker`** (`check-docker.mjs`).
|
|
103
103
|
|
|
@@ -106,7 +106,7 @@ CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE
|
|
|
106
106
|
```json title="package.json"
|
|
107
107
|
{
|
|
108
108
|
"scripts": {
|
|
109
|
-
"lint-docker": "
|
|
109
|
+
"lint-docker": "n-cursor lint-docker"
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
```
|
package/rules/docker/js/run.mjs
CHANGED
|
@@ -47,9 +47,10 @@ export async function findLintDockerfilePaths(root, ignorePaths = []) {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Запуск hadolint по Dockerfile та *.Dockerfile.
|
|
50
|
+
* Експортовано як `runLintDocker` — використовується з `bin/n-cursor.js` як підкоманда `lint-docker`.
|
|
50
51
|
* @returns {Promise<number>} 0 — OK, 1 — зауваження або помилка
|
|
51
52
|
*/
|
|
52
|
-
async function
|
|
53
|
+
export async function runLintDocker() {
|
|
53
54
|
const reporter = createCheckReporter()
|
|
54
55
|
const { pass, fail } = reporter
|
|
55
56
|
|
|
@@ -80,5 +81,5 @@ async function main() {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
if (isRunAsCli()) {
|
|
83
|
-
process.exitCode = await
|
|
84
|
+
process.exitCode = await runLintDocker()
|
|
84
85
|
}
|
|
@@ -19,7 +19,7 @@ package docker.package_json
|
|
|
19
19
|
|
|
20
20
|
import rego.v1
|
|
21
21
|
|
|
22
|
-
canonical_lint_docker := "
|
|
22
|
+
canonical_lint_docker := "n-cursor lint-docker"
|
|
23
23
|
|
|
24
24
|
lint_docker_template := concat(" ", [
|
|
25
25
|
"package.json: scripts.lint-docker має бути %q",
|
package/rules/ga/js/lint.mjs
CHANGED
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Експортовано окремо `runLintGaCli` — використовується з `bin/n-cursor.js` як підкоманда `lint-ga`.
|
|
21
21
|
*/
|
|
22
|
-
import { spawnSync } from 'node:child_process'
|
|
23
22
|
import { platform } from 'node:process'
|
|
24
23
|
|
|
25
24
|
import { check as checkGa } from './workflows/check.mjs'
|
|
26
25
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
26
|
+
import { runLintStep } from '../../../scripts/utils/run-lint-step.mjs'
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Опис залежності preflight-ом: бінарник, для чого потрібен, і команди встановлення.
|
|
@@ -112,29 +112,6 @@ function preflight(dep) {
|
|
|
112
112
|
return false
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/**
|
|
116
|
-
* Запускає крок lint-ga з відображенням команди користувачу. Stdout/stderr дочірнього процесу
|
|
117
|
-
* передається користувачу як є (`stdio: 'inherit'`), щоб виглядало як прямий виклик у shell.
|
|
118
|
-
* @param {string} title заголовок для логу (наприклад `actionlint`)
|
|
119
|
-
* @param {string} cmd ім'я команди (`bunx`, `uvx`)
|
|
120
|
-
* @param {string[]} args аргументи команди
|
|
121
|
-
* @returns {number} код виходу дочірнього процесу (0 — OK, інше — помилка)
|
|
122
|
-
*/
|
|
123
|
-
function runStep(title, cmd, args) {
|
|
124
|
-
console.log(`\n▶ ${title}: ${cmd} ${args.join(' ')}`)
|
|
125
|
-
const resolved = resolveCmd(cmd)
|
|
126
|
-
if (!resolved) {
|
|
127
|
-
console.error(`❌ ${cmd} не знайдено в PATH (${title}).`)
|
|
128
|
-
return 127
|
|
129
|
-
}
|
|
130
|
-
const r = spawnSync(resolved, args, { stdio: 'inherit', env: process.env })
|
|
131
|
-
if (r.error) {
|
|
132
|
-
console.error(`❌ Не вдалося запустити ${cmd}: ${r.error.message}`)
|
|
133
|
-
return 1
|
|
134
|
-
}
|
|
135
|
-
return r.status ?? 1
|
|
136
|
-
}
|
|
137
|
-
|
|
138
115
|
/**
|
|
139
116
|
* Виконує канонічний `lint-ga` з preflight-перевірками і делегує до `check-ga.check()`.
|
|
140
117
|
*
|
|
@@ -160,10 +137,10 @@ export async function runLintGaCli() {
|
|
|
160
137
|
}
|
|
161
138
|
if (!preflightOk) return 1
|
|
162
139
|
|
|
163
|
-
const actionlintCode =
|
|
140
|
+
const actionlintCode = runLintStep('actionlint', 'bunx', ['github-actionlint'])
|
|
164
141
|
if (actionlintCode !== 0) return actionlintCode
|
|
165
142
|
|
|
166
|
-
const zizmorCode =
|
|
143
|
+
const zizmorCode = runLintStep('zizmor', 'uvx', ['zizmor', '--offline', '--collect=workflows', '.'])
|
|
167
144
|
if (zizmorCode !== 0) return zizmorCode
|
|
168
145
|
|
|
169
146
|
console.log('\n▶ check-ga (rego-полісі npm/policy/ga/ + JS cross-file перевірки)')
|
package/rules/k8s/js/run.mjs
CHANGED
|
@@ -146,9 +146,10 @@ function runKubescape(dirs) {
|
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Головна точка входу: kubeconform + kubescape для усіх знайдених дерев `k8s`.
|
|
149
|
+
* Експортовано як `runLintK8s` — використовується з `bin/n-cursor.js` як підкоманда `lint-k8s`.
|
|
149
150
|
* @returns {Promise<number>} код виходу для `process.exitCode` (0 — успіх або пропуск)
|
|
150
151
|
*/
|
|
151
|
-
async function
|
|
152
|
+
export async function runLintK8s() {
|
|
152
153
|
const root = process.cwd()
|
|
153
154
|
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
154
155
|
const dirs = await findK8sRoots(root, ignorePaths)
|
|
@@ -169,5 +170,5 @@ async function main() {
|
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
if (isRunAsCli()) {
|
|
172
|
-
process.exitCode = await
|
|
173
|
+
process.exitCode = await runLintK8s()
|
|
173
174
|
}
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -38,20 +38,18 @@ alwaysApply: false
|
|
|
38
38
|
|
|
39
39
|
**kubescape:** типово **`kubescape scan <каталог-k8s>`**; поріг серйозності підлаштуй під проєкт (наприклад **`--severity-threshold high`**). Перший запуск може завантажувати артефакти — у CI потрібна мережа або [offline](https://github.com/kubescape/kubescape#readme). На відміну від kubeconform, у **kubescape scan** немає прапорця **`-kubernetes-version`**: перевірка йде за **framework/control** (NSA, MITRE, CIS тощо), а не проти OpenAPI-схеми конкретного релізу Kubernetes. **Орієнтир** для репозиторію той самий, що й для kubeconform — кластер **v1.33.9** (див. **`-kubernetes-version 1.33.9`** вище); для CIS і подібних наближень обирай актуальний framework під політику команди (**`kubescape list frameworks`**, див. [CLI reference](https://github.com/kubescape/kubescape/blob/master/docs/cli-reference.md)).
|
|
40
40
|
|
|
41
|
-
У репозиторії пакета **`@nitra/cursor`** скрипт **`lint-k8s`** делегує
|
|
41
|
+
У репозиторії пакета **`@nitra/cursor`** скрипт **`lint-k8s`** делегує до CLI **`n-cursor lint-k8s`** (реалізація — **`npm/rules/k8s/js/run.mjs`**). У інших проєктах достатньо встановити **`@nitra/cursor`** у `devDependencies` — бінарка **`n-cursor`** буде у **`node_modules/.bin/`**.
|
|
42
42
|
|
|
43
43
|
```json title="package.json"
|
|
44
44
|
{
|
|
45
45
|
"scripts": {
|
|
46
|
-
"lint-k8s": "
|
|
46
|
+
"lint-k8s": "n-cursor lint-k8s"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Якщо правило **`k8s`** підключено в **`.n-cursor.json`** (масив **`rules`**), у **кореневому** `package.json` **мають** бути скрипт **`lint-k8s`** і виклик **`bun run lint-k8s`** у агрегованому **`lint`** (див. **`bun.mdc`**). Це перевіряє **`npx @nitra/cursor check bun`**.
|
|
52
52
|
|
|
53
|
-
Шлях до скрипта підстав свій (`./scripts/…` після копіювання, `node_modules/@nitra/cursor/scripts/…` якщо пакет у залежностях).
|
|
54
|
-
|
|
55
53
|
Додай workflow **`.github/workflows/lint-k8s.yml`** (гілки **`dev`** і **`main`**, лише **`.yml`**, узгоджено з **`ga.mdc`**). **Не** дублюй **`setup-node`**, **`oven-sh/setup-bun`**, **`actions/cache`** і **`bun install`** у job — після **`checkout`** використовуй локальний composite **`setup-bun-deps`** (шлях **`./.github/actions/setup-bun-deps`** або **`./npm/github-actions/setup-bun-deps`**, як у репозиторії). Встановлення **kubeconform** / **kubescape** лишаються окремими кроками.
|
|
56
54
|
|
|
57
55
|
```yaml title=".github/workflows/lint-k8s.yml"
|
|
@@ -496,8 +496,8 @@ async function checkNoTestsInPublishedFiles(pass, fail) {
|
|
|
496
496
|
}
|
|
497
497
|
for (const v of violations) {
|
|
498
498
|
fail(
|
|
499
|
-
`npm/${v.file}: ${v.reason} —
|
|
500
|
-
'(наприклад "!**/*_test.rego")
|
|
499
|
+
`npm/${v.file}: ${v.reason} — додай у "files" у npm/package.json негативний glob, ` +
|
|
500
|
+
'що виключає цей файл з tarball (наприклад "!**/*.test.mjs", "!**/fixtures/**", "!**/*_test.rego") (npm-module.mdc)'
|
|
501
501
|
)
|
|
502
502
|
}
|
|
503
503
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Оформлення репозиторію для npm модуля
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.12'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
Bun monorepo: workspace **`npm/`**, кореневий **`package.json`**, **`.github/workflows/`**; опційно **`demo/`**.
|
|
@@ -11,13 +11,13 @@ Bun monorepo: workspace **`npm/`**, кореневий **`package.json`**, **`.g
|
|
|
11
11
|
Мета — **максимально компактний** опублікований пакет: у npm потрапляє тільки те, що потрібно під час `require`/`import` користувачем.
|
|
12
12
|
|
|
13
13
|
- **`"files"` обовʼязковий** у `npm/package.json` як **whitelist** того, що публікується (без `"files"` npm пакує майже все — це антипатерн для цього правила).
|
|
14
|
-
- **Тести й фікстури —
|
|
14
|
+
- **Тести й фікстури не публікуються — через негативні glob-патерни у `"files"`.** Тести можна (і зазвичай зручніше) тримати **поруч з кодом** усередині шляхів, перелічених у `"files"` (наприклад `*.test.mjs` поруч з модулем чи `fixtures/` під `rules/<id>/js/`). Щоб вони не потрапляли у tarball, `"files"` **обовʼязково має містити негативні glob-патерни**, що їх виключають. Покрий усі форми тестового: фреймворк-тести (`bun:test`, `node:test`, `vitest`, `@jest/globals`, `mocha`, …), test-style каталоги (`tests/`, `__tests__/`, `fixtures/`, `__fixtures__/`, `spec/`, `test/`), файли за патернами `*.test.*` / `*.spec.*`. Орієнтовний набір: `"!**/*.test.*"`, `"!**/*.spec.*"`, `"!**/test-helpers.*"`, `"!**/fixtures/**"`, `"!**/__tests__/**"`. **Rego (`*_test.rego`):** за конвенцією conftest юніт-тест лежить поруч з полісі у тому самому `package` — `*_test.rego` усередині опублікованого `policy/` дозволені; якщо потрібна максимальна компактність, додай `"!**/*_test.rego"` явно (як у самому `@nitra/cursor`).
|
|
15
15
|
- **Лише runtime-залежності у `npm/package.json`.** `devDependencies` тримай у **кореневому** `package.json` монорепо — тоді `npm install @nitra/<pkg>` не тягне інструментарій, потрібний лише для розробки самого пакета.
|
|
16
16
|
|
|
17
17
|
Деталі алгоритму перевірки:
|
|
18
18
|
|
|
19
19
|
- Пер-документні правила для `npm/package.json` (whitelist `files`, заборона `devDependencies`, форма `types`) — у rego-пакеті `npm_module.npm_package_json` (`npm/policy/npm_module/npm_package_json/`).
|
|
20
|
-
-
|
|
20
|
+
- Walk шляхів з `"files"` з застосуванням негативних patterns і скан залишку на тест-патерни (walking + AST JS/TS) — у `check.mjs::checkNoTestsInPublishedFiles` (FS / AST не лягають у rego). Якщо після застосування негативних patterns у tarball лишається test-style файл — `check` падає з вказівкою, який саме негативний glob треба додати у `"files"`.
|
|
21
21
|
|
|
22
22
|
## TypeScript declaration (`npm/types`)
|
|
23
23
|
|
package/rules/rego/js/lint.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
26
26
|
import { existsSync } from 'node:fs'
|
|
27
27
|
import { resolve } from 'node:path'
|
|
28
28
|
|
|
29
|
+
import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
29
30
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
30
31
|
|
|
31
32
|
/** Шляхи з Rego-полісі (відносно cwd). Існують не всі на ранніх стадіях — фільтруємо нижче. */
|
|
@@ -128,4 +129,6 @@ export function runLintRego(cwd = process.cwd()) {
|
|
|
128
129
|
return runStep(conftest, ['verify', ...targets.flatMap(t => ['-p', t])], root)
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
|
|
132
|
+
if (isRunAsCli()) {
|
|
133
|
+
process.exitCode = runLintRego()
|
|
134
|
+
}
|
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
# Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ виявлення
|
|
4
4
|
# `.rego` файлів у дереві. Глобально у `lint-conftest` НЕ реєструється.
|
|
5
5
|
#
|
|
6
|
-
# Canonical (rego.mdc): scripts.lint-rego має бути "
|
|
6
|
+
# Canonical (rego.mdc): scripts.lint-rego має бути "n-cursor lint-rego" — CLI пакета `@nitra/cursor`
|
|
7
|
+
# (бінарка з `node_modules/.bin/`) робить preflight `opa`/`regal` і прогонить
|
|
8
|
+
# `opa check --strict` → `regal lint` → опц. `conftest verify` (`@nitra/cursor lint-rego`).
|
|
7
9
|
#
|
|
8
10
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
9
11
|
package rego.package_json
|
|
10
12
|
|
|
11
13
|
import rego.v1
|
|
12
14
|
|
|
13
|
-
canonical_lint_rego := "
|
|
15
|
+
canonical_lint_rego := "n-cursor lint-rego"
|
|
14
16
|
|
|
15
17
|
lint_rego_template := concat(" ", [
|
|
16
18
|
"package.json: scripts.lint-rego має бути %q",
|
|
@@ -22,7 +24,7 @@ deny contains msg if {
|
|
|
22
24
|
not "lint-rego" in object.keys(scripts)
|
|
23
25
|
msg := concat(" ", [
|
|
24
26
|
"package.json: відсутній scripts.lint-rego — додай",
|
|
25
|
-
"\"lint-rego\": \"
|
|
27
|
+
"\"lint-rego\": \"n-cursor lint-rego\" (rego.mdc)",
|
|
26
28
|
])
|
|
27
29
|
}
|
|
28
30
|
|
package/rules/rego/rego.mdc
CHANGED
|
@@ -36,13 +36,13 @@ alwaysApply: false
|
|
|
36
36
|
bun run lint-rego
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
Скрипт делегує до `
|
|
39
|
+
Скрипт делегує до CLI `n-cursor lint-rego` (бінарка з `node_modules/.bin/` пакету `@nitra/cursor`). Послідовність:
|
|
40
40
|
|
|
41
41
|
1. **preflight** — наявність `opa` і `regal` у `PATH`; якщо хоча б одного нема — exit 1 з підказкою встановлення;
|
|
42
42
|
2. `opa check --strict <targets>` — компіляція з типами та `--strict` (мертвий код, неоднозначні правила, незадекларовані змінні);
|
|
43
43
|
3. `regal lint <targets>` — статичний лінтер Rego ([Styra Regal](https://docs.styra.com/regal)): ловить v0-синтаксис, неявні set-rules і відхилення від `rego.v1`, плюс bugs/idiomatic/style-правила.
|
|
44
44
|
|
|
45
|
-
Цілі — `npm/policy/` (де живуть Rego-полісі пакета). Інші *.rego поза деревом додай у `LINT_TARGETS` у `lint
|
|
45
|
+
Цілі — `npm/policy/` (де живуть Rego-полісі пакета). Інші *.rego поза деревом додай у `LINT_TARGETS` у `npm/rules/rego/js/lint.mjs`.
|
|
46
46
|
|
|
47
47
|
### Встановлення інструментів
|
|
48
48
|
|
|
@@ -58,7 +58,7 @@ bun run lint-rego
|
|
|
58
58
|
```json title="package.json"
|
|
59
59
|
{
|
|
60
60
|
"scripts": {
|
|
61
|
-
"lint-rego": "
|
|
61
|
+
"lint-rego": "n-cursor lint-rego"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
```
|
|
@@ -25,7 +25,7 @@ import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mj
|
|
|
25
25
|
* Альтернатива полю `stylelint` у `package.json` — зовнішній файл конфігу. Якщо
|
|
26
26
|
* поля немає і файлу немає, фейлимося; якщо є хоч щось — пропускаємо. Поле
|
|
27
27
|
* `stylelint.extends == "@nitra/stylelint-config"` сам формат — у Rego.
|
|
28
|
-
* @param {import('
|
|
28
|
+
* @param {import('../../../../scripts/utils/check-reporter.mjs').CheckReporter} reporter репортер
|
|
29
29
|
*/
|
|
30
30
|
async function checkStylelintConfigPresence(reporter) {
|
|
31
31
|
const { pass, fail } = reporter
|
|
@@ -9,7 +9,7 @@ alwaysApply: false
|
|
|
9
9
|
|
|
10
10
|
- **Джерело правил:** перед тим як писати або суттєво змінювати **`.css`**, **`.scss`** або стилі в **`.vue`**, переглянь у корені проєкту (і в релевантних пакетах монорепо, якщо є) поле **`stylelint`** у **`package.json`** (зокрема `extends`), наявні **`.stylelintrc.*`**, **`stylelint.config.*`** та **`.stylelintignore`**. Не покладайся на «типові» правила stylelint з пам’яті — дотримуйся **проєктного** **`@nitra/stylelint-config`** і будь-яких локальних доповнень у репозиторії.
|
|
11
11
|
- **Форматування** узгоджуй з **`n-text.mdc`** (oxfmt / `.oxfmtrc.json` для css, scss тощо), щоб форматер і stylelint не суперечили один одному.
|
|
12
|
-
- **Запуск stylelint:** лише **`npx stylelint`**. Локально — через скрипт **`lint-style`** (`bun run lint-style`); у **GitHub Actions** у кроці **`run`** викликай `npx stylelint '**/*.{css,scss,vue}' --fix` напряму (не через **`bun run lint-style`**). Не використовуй **`bunx stylelint`**. Після змін запускай **`bun run lint-style`** і виправляй усе, що лишилось після auto-fix; за потреби — повний
|
|
12
|
+
- **Запуск stylelint:** лише **`npx stylelint`**. Локально — через скрипт **`lint-style`** (`bun run lint-style`); у **GitHub Actions** у кроці **`run`** викликай `npx stylelint '**/*.{css,scss,vue}' --fix` напряму (не через **`bun run lint-style`**). Не використовуй **`bunx stylelint`**. Після змін запускай **`bun run lint-style`** і виправляй усе, що лишилось після auto-fix; за потреби — повний `bun run lint` (навичка **`/n-lint`**).
|
|
13
13
|
- **Не розширюй винятки:** не додавай зайві **`stylelint-disable`** без потреби; краще підлаштувати стилі під правила проєкту.
|
|
14
14
|
|
|
15
15
|
**VSCode:** у **`.vscode/extensions.json`** рекомендуй **`stylelint.vscode-stylelint`**. У **`.vscode/settings.json`** вимкни вбудовану валідацію CSS/SCSS/Less і увімкни явні code actions:
|
|
@@ -147,36 +147,20 @@ async function checkPackageJsonText(passFn, failFn) {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
|
-
* Перевіряє скрипт lint-text
|
|
151
|
-
*
|
|
150
|
+
* Перевіряє скрипт lint-text: канонічний — `n-cursor lint-text` (CLI пакета `@nitra/cursor` робить
|
|
151
|
+
* `cspell` → `runShellcheckText()` → `bunx markdownlint-cli2 --fix` → `runV8rWithGlobs()`).
|
|
152
|
+
* Дозволено whitespace навколо команди.
|
|
153
|
+
* @param {unknown} lintText значення `scripts.lint-text` з package.json
|
|
152
154
|
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
153
155
|
* @param {(msg: string) => void} failFn callback при помилці
|
|
154
156
|
*/
|
|
155
157
|
function checkLintTextScript(lintText, passFn, failFn) {
|
|
156
|
-
const lt = typeof lintText === 'string' ? lintText : ''
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const eq98Hints = (lt.match(/eq 98/g) || []).length
|
|
160
|
-
const legacyV8r = v8rCalls >= 4 && eq98Hints >= 4
|
|
161
|
-
const quietBundled = quietCalls === 1
|
|
162
|
-
const quietLegacy4x = quietCalls >= 4
|
|
163
|
-
const v8rTextOk = legacyV8r || quietBundled || quietLegacy4x
|
|
164
|
-
const globsRequired = legacyV8r || quietLegacy4x
|
|
165
|
-
const globsOk =
|
|
166
|
-
lt.includes('**/*.json') && lt.includes('**/*.yml') && lt.includes('**/*.yaml') && lt.includes('**/*.toml')
|
|
167
|
-
const ok =
|
|
168
|
-
lt &&
|
|
169
|
-
lt.includes('cspell') &&
|
|
170
|
-
lt.includes('run-shellcheck-text.mjs') &&
|
|
171
|
-
lt.includes('bunx markdownlint-cli2') &&
|
|
172
|
-
lt.includes('**/*.mdc') &&
|
|
173
|
-
v8rTextOk &&
|
|
174
|
-
(!globsRequired || globsOk)
|
|
175
|
-
if (ok) {
|
|
176
|
-
passFn('package.json: lint-text — shellcheck (run-shellcheck-text.mjs), v8r: run-v8r.mjs або чотири bunx v8r')
|
|
158
|
+
const lt = typeof lintText === 'string' ? lintText.trim() : ''
|
|
159
|
+
if (lt === 'n-cursor lint-text') {
|
|
160
|
+
passFn('lint-text делегує CLI n-cursor lint-text (cspell + shellcheck + markdownlint + v8r)')
|
|
177
161
|
} else {
|
|
178
162
|
failFn(
|
|
179
|
-
'package.json: lint-text
|
|
163
|
+
'package.json: lint-text має бути "n-cursor lint-text" — CLI пакета @nitra/cursor виконує cspell → shellcheck → markdownlint-cli2 → v8r (text.mdc)'
|
|
180
164
|
)
|
|
181
165
|
}
|
|
182
166
|
}
|