@nitra/cursor 1.11.0 → 1.11.3
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 +20 -3
- package/bin/n-cursor.js +10 -3
- package/package.json +1 -4
- package/rules/abie/utils/k8s-tree.mjs +1 -1
- package/rules/adr/js/{check.mjs → hooks/check.mjs} +2 -2
- package/rules/bun/js/{check.mjs → layout/check.mjs} +1 -1
- package/rules/capacitor/js/{check.mjs → platforms/check.mjs} +1 -1
- package/rules/changelog/js/{check.mjs → consistency/check.mjs} +2 -2
- package/rules/docker/js/{check.mjs → lint/check.mjs} +5 -5
- package/rules/ga/js/lint.mjs +1 -1
- package/rules/ga/js/{check.mjs → workflows/check.mjs} +4 -4
- package/rules/graphql/js/{check.mjs → tooling/check.mjs} +5 -5
- package/rules/hasura/js/{check.mjs → internal_urls/check.mjs} +4 -4
- package/rules/image-avif/js/{check.mjs → avif_generation/check.mjs} +5 -5
- package/rules/image-compress/js/{check.mjs → package_setup/check.mjs} +1 -1
- package/rules/js-bun-db/js/{check.mjs → safety/check.mjs} +5 -5
- package/rules/js-bun-redis/js/{check.mjs → imports/check.mjs} +4 -4
- package/rules/js-lint/js/{check.mjs → tooling/check.mjs} +16 -2
- package/rules/js-mssql/js/{check.mjs → deps/check.mjs} +5 -5
- package/rules/js-run/js/{check.mjs → runtime/check.mjs} +10 -10
- package/rules/k8s/js/{check.mjs → manifests/check.mjs} +4 -4
- package/rules/nginx-default-tpl/js/{check.mjs → template/check.mjs} +5 -5
- package/rules/npm-module/js/{check.mjs → package_structure/check.mjs} +4 -4
- package/rules/php/js/{check.mjs → tooling/check.mjs} +1 -1
- package/rules/style-lint/js/{check.mjs → tooling/check.mjs} +1 -1
- package/rules/tauri/js/{check.mjs → tooling/check.mjs} +2 -2
- package/rules/text/js/{check.mjs → formatting/check.mjs} +2 -2
- package/rules/vue/js/{check.mjs → packages/check.mjs} +5 -5
- package/scripts/auto-skills.mjs +92 -51
- package/scripts/utils/discover-checkable-rules.mjs +5 -22
- package/scripts/utils/run-rule.mjs +1 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@
|
|
|
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.3] - 2026-05-15
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`npm/bin/n-cursor.js`** — `detectAutoSkills` тепер отримує **ефективний** список правил (опт-ін вручну з `.n-cursor.json:rules` ∪ auto-detected, мінус `disable-rules`), а не лише auto-detected. Без цього скіли із залежністю на правило, додане вручну (наприклад, `adr` без `auto.md`-умови), не активувалися — у репо з `"rules": ["adr", …]` скіл `adr-normalize` залишався відсутнім, попри `[adr]` у його `skills/adr-normalize/auto.md`. Тепер `adr-normalize`, `abie-clean`, `abie-kustomize`, `taze` авто-додаються коректно як при auto-detected, так і при manual-opt-in відповідних правил.
|
|
12
|
+
|
|
13
|
+
## [1.11.2] - 2026-05-15
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **`npm/scripts/auto-skills.mjs`** — джерело правди для автоактивації скілів тепер `skills/<skill>/auto.md`, а не hardcoded мапа в JS. Парсер розпізнає три формати: `завжди` (always-on), `[rule, rule, …]` (умова на правила), відсутній/нерозпізнаний файл (opt-in). Експортовані константи `AUTO_SKILL_ORDER` та `AUTO_SKILL_RULE_DEPENDENCIES` тепер похідні від сканування `npm/skills/` під час завантаження модуля (зберігаються для зворотної сумісності). Побічно виправлено пропуск `abie-clean` у hardcoded мапі попри `[abie]` у його `auto.md` — тепер скіл коректно автоактивується разом з правилом `abie`.
|
|
18
|
+
|
|
19
|
+
## [1.11.1] - 2026-05-15
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **`npm/bin/n-cursor.js`** — `runSync()` (entry для `npx @nitra/cursor` без аргументів) шукав
|
|
24
|
+
`<packageRoot>/mdc` після того, як phase 1-4 перейменував каталог у `rules/`. Виправлено: тепер
|
|
25
|
+
вказує на коректний шлях `<packageRoot>/rules` — більше не кидає «Не знайдено каталог правил пакету».
|
|
26
|
+
|
|
7
27
|
## [1.11.0] - 2026-05-15
|
|
8
28
|
|
|
9
29
|
### Added
|
|
@@ -37,9 +57,6 @@
|
|
|
37
57
|
- **Walk-glob правила** (6): `js-mssql`, `js-bun-db`, `js-bun-redis`, `js-run` (package_json + configmap), `vue`, `image-avif` — `walkGlob: "**/package.json"` або відповідний патерн.
|
|
38
58
|
- **k8s.* концерни** (8): `manifest`, `gateway`, `hpa_pdb`, `kustomization`, `svc_yaml`, `svc_hl_yaml`, `base_kustomization`, `base_manifest` — `walkGlob` по YAML під сегментом `k8s/`; `base_manifest` використовує негативний glob для виключення `kustomization.yaml`.
|
|
39
59
|
- **abie концерни** (4): `clean_merged_ignore_branches` (single), `health_check_policy` (walkGlob `**/k8s/**/hc.yaml`), `http_route_base` (walkGlob `**/k8s/**/base/**/hr.yaml`), `base_deployment_preem` (walkGlob `**/k8s/**/base/**/*.{yaml,yml}` з виключенням `kustomization.yaml`).
|
|
40
|
-
|
|
41
|
-
### Changed
|
|
42
|
-
|
|
43
60
|
- **`capture-decisions.sh` тепер пише чернетки напряму в `docs/adr/<timestamp>-<sid>.md`** (раніше — у `docs/adr/_inbox/`). Сам каталог `_inbox/` більше не створюється, але `normalize-decisions.sh` бачить його рекурсивно — старі чернетки з `_inbox/` поступово розчищаються нормалізацією. Можна також одноразово `git mv docs/adr/_inbox/*.md docs/adr/` і прибрати порожній каталог.
|
|
44
61
|
- **Правило `adr` (`npm/rules/adr/adr.mdc`)**: повне переписування під дві фази (capture + normalize). Видалено згадки `_inbox/`. Версія `version: '2.0'`.
|
|
45
62
|
- **`npm/rules/adr/js/check.mjs`**: перевірка обох hook-скриптів (canonicity), обох log-файлів у `.gitignore`.
|
package/bin/n-cursor.js
CHANGED
|
@@ -261,9 +261,16 @@ async function readConfig(paths = {}) {
|
|
|
261
261
|
packageJsonParsed: rootPkg,
|
|
262
262
|
disableRules
|
|
263
263
|
})
|
|
264
|
+
// Skills залежать від ефективного списку правил, який буде у конфізі після merge:
|
|
265
|
+
// вже існуючі (опт-ін вручну) + auto-detected, мінус `disable-rules`. Без цього
|
|
266
|
+
// правило, додане вручну (напр. `adr` без auto.md-умови), не активувало б залежні
|
|
267
|
+
// скіли (`adr-normalize`).
|
|
268
|
+
const disableRulesSet = new Set(disableRules)
|
|
269
|
+
const effectiveRulesForSkills = [...new Set([...normalizeIdList(parsedConfig.rules), ...autoDetectedRules.rules])]
|
|
270
|
+
.filter(id => !disableRulesSet.has(id))
|
|
264
271
|
const autoDetectedSkills = detectAutoSkills({
|
|
265
272
|
availableSkills,
|
|
266
|
-
detectedRules:
|
|
273
|
+
detectedRules: effectiveRulesForSkills,
|
|
267
274
|
disableSkills
|
|
268
275
|
})
|
|
269
276
|
|
|
@@ -945,7 +952,7 @@ async function runSyncStep(prefix, action) {
|
|
|
945
952
|
/**
|
|
946
953
|
* Копіює керовані `.mdc` файли з пакету до `.cursor/rules`.
|
|
947
954
|
* @param {string[]} rules список rules з конфігу
|
|
948
|
-
* @param {string} bundledRulesDir каталог `
|
|
955
|
+
* @param {string} bundledRulesDir каталог `rules` пакету-джерела
|
|
949
956
|
* @param {string} rulesDir абсолютний шлях до `.cursor/rules`
|
|
950
957
|
* @returns {Promise<{ successCount: number, failCount: number }>} статистика копіювання
|
|
951
958
|
*/
|
|
@@ -1207,7 +1214,7 @@ async function runSync() {
|
|
|
1207
1214
|
|
|
1208
1215
|
await reexecIfPackageVersionChanged(effectivePackageRoot)
|
|
1209
1216
|
|
|
1210
|
-
const bundledRulesDir = join(effectivePackageRoot, '
|
|
1217
|
+
const bundledRulesDir = join(effectivePackageRoot, 'rules')
|
|
1211
1218
|
const bundledSkillsDir = join(effectivePackageRoot, 'skills')
|
|
1212
1219
|
const bundledAgentsTemplatePath = join(effectivePackageRoot, AGENTS_TEMPLATE_FILE)
|
|
1213
1220
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitra/cursor",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.3",
|
|
4
4
|
"description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -51,8 +51,5 @@
|
|
|
51
51
|
"engines": {
|
|
52
52
|
"bun": ">=1.3",
|
|
53
53
|
"node": ">=25"
|
|
54
|
-
},
|
|
55
|
-
"devDependencies": {
|
|
56
|
-
"@nitra/cursor": "^1.9.22"
|
|
57
54
|
}
|
|
58
55
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { dirname, relative } from 'node:path'
|
|
11
11
|
|
|
12
|
-
import { pathHasK8sSegment } from '../../k8s/js/check.mjs'
|
|
12
|
+
import { pathHasK8sSegment } from '../../k8s/js/manifests/check.mjs'
|
|
13
13
|
import { walkDir } from '../../../scripts/utils/walkDir.mjs'
|
|
14
14
|
import { isDeploymentDoc, readAndParseYamlDocs } from './yaml.mjs'
|
|
15
15
|
|
|
@@ -22,7 +22,7 @@ import { delimiter, dirname, join } from 'node:path'
|
|
|
22
22
|
import { env } from 'node:process'
|
|
23
23
|
import { fileURLToPath } from 'node:url'
|
|
24
24
|
|
|
25
|
-
import { createCheckReporter } from '
|
|
25
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
26
26
|
|
|
27
27
|
/** Один hook-артефакт: bash-скрипт + його лог-файл, які перевіряємо однотипно. */
|
|
28
28
|
const HOOK_ARTIFACTS = /** @type {const} */ ([
|
|
@@ -34,7 +34,7 @@ const PROJECT_SETTINGS_PATH = '.claude/settings.json'
|
|
|
34
34
|
const EOL_RE = /\r?\n/u
|
|
35
35
|
|
|
36
36
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
37
|
-
const BUNDLED_HOOKS_DIR = join(here, '..', '..', '..', '.claude-template', 'hooks')
|
|
37
|
+
const BUNDLED_HOOKS_DIR = join(here, '..', '..', '..', '..', '.claude-template', 'hooks')
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Відносний шлях до managed hook-скрипта у проєкті.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { existsSync } from 'node:fs'
|
|
19
19
|
import { readFile } from 'node:fs/promises'
|
|
20
20
|
|
|
21
|
-
import { createCheckReporter } from '
|
|
21
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
22
22
|
|
|
23
23
|
// Перевірка `devDependencies` кореневого `package.json` (дозволено лише `@nitra/*`)
|
|
24
24
|
// — у rego (`npm/policy/bun/package_json/`). JS-копії `isAllowedRootDevDependency`
|
|
@@ -25,7 +25,7 @@ import { existsSync } from 'node:fs'
|
|
|
25
25
|
import { readdir, readFile } from 'node:fs/promises'
|
|
26
26
|
import { join, relative } from 'node:path'
|
|
27
27
|
|
|
28
|
-
import { createCheckReporter } from '
|
|
28
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
29
29
|
|
|
30
30
|
/** Мінімальна допустима мажорна версія Capacitor (capacitor.mdc) */
|
|
31
31
|
const MIN_CAPACITOR_MAJOR = 8
|
|
@@ -27,8 +27,8 @@ import { readFile } from 'node:fs/promises'
|
|
|
27
27
|
import { join } from 'node:path'
|
|
28
28
|
import { promisify } from 'node:util'
|
|
29
29
|
|
|
30
|
-
import { createCheckReporter } from '
|
|
31
|
-
import { getMonorepoPackageRootDirs } from '
|
|
30
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
31
|
+
import { getMonorepoPackageRootDirs } from '../../../../scripts/utils/workspaces.mjs'
|
|
32
32
|
|
|
33
33
|
const execFileAsync = promisify(execFile)
|
|
34
34
|
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
import { readFile } from 'node:fs/promises'
|
|
31
31
|
import { basename } from 'node:path'
|
|
32
32
|
|
|
33
|
-
import { getMirrorGcrHint, getFromImageToken } from '
|
|
34
|
-
import { lintDockerfileWithHadolint, posixRel } from '
|
|
35
|
-
import { createCheckReporter } from '
|
|
36
|
-
import { loadCursorIgnorePaths } from '
|
|
37
|
-
import { walkDir } from '
|
|
33
|
+
import { getMirrorGcrHint, getFromImageToken } from '../../../../scripts/utils/docker-mirror.mjs'
|
|
34
|
+
import { lintDockerfileWithHadolint, posixRel } from '../../../../scripts/utils/docker-hadolint.mjs'
|
|
35
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
36
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
37
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
38
38
|
|
|
39
39
|
const NEWLINE_RE = /\r?\n/
|
|
40
40
|
const BUN_INSTALL_RE = /\bbun\s+(?:install|i)\b/iu
|
package/rules/ga/js/lint.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { spawnSync } from 'node:child_process'
|
|
23
23
|
import { platform } from 'node:process'
|
|
24
24
|
|
|
25
|
-
import { check as checkGa } from './check.mjs'
|
|
25
|
+
import { check as checkGa } from './workflows/check.mjs'
|
|
26
26
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -19,10 +19,10 @@ import { readdir, readFile } from 'node:fs/promises'
|
|
|
19
19
|
import { execFileSync } from 'node:child_process'
|
|
20
20
|
import { join } from 'node:path'
|
|
21
21
|
|
|
22
|
-
import { createCheckReporter } from '
|
|
23
|
-
import { eventPathsIncludeExact, parseWorkflowYaml } from '
|
|
24
|
-
import { resolveCmd } from '
|
|
25
|
-
import { runConftestBatch } from '
|
|
22
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
23
|
+
import { eventPathsIncludeExact, parseWorkflowYaml } from '../../../../scripts/utils/gha-workflow.mjs'
|
|
24
|
+
import { resolveCmd } from '../../../../scripts/utils/resolve-cmd.mjs'
|
|
25
|
+
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
26
26
|
|
|
27
27
|
/** Шаблони наявності MegaLinter у вмісті workflow */
|
|
28
28
|
const MEGALINTER_USE_PATTERNS = [/oxsecurity\/megalinter-action/i, /megalinter\/megalinter/i]
|
|
@@ -10,15 +10,15 @@ import { existsSync } from 'node:fs'
|
|
|
10
10
|
import { readFile } from 'node:fs/promises'
|
|
11
11
|
import { relative } from 'node:path'
|
|
12
12
|
|
|
13
|
-
import { createCheckReporter } from '
|
|
13
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
14
14
|
import {
|
|
15
15
|
isGqlScanSourceFile,
|
|
16
16
|
shouldSkipFileForGqlScan,
|
|
17
17
|
sourceFileHasGqlTaggedTemplate
|
|
18
|
-
} from '
|
|
19
|
-
import { loadCursorIgnorePaths } from '
|
|
20
|
-
import { runConftestBatch } from '
|
|
21
|
-
import { walkDir } from '
|
|
18
|
+
} from '../../../../scripts/utils/graphql-gql-scan.mjs'
|
|
19
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
20
|
+
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
21
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
22
22
|
|
|
23
23
|
/** Очікуваний файл GraphQL Config у корені (graphql.mdc). */
|
|
24
24
|
export const GRAPHQL_RC_FILENAME = '.graphqlrc.yml'
|
|
@@ -27,10 +27,10 @@ import { basename, join, relative } from 'node:path'
|
|
|
27
27
|
|
|
28
28
|
import { parseAllDocuments } from 'yaml'
|
|
29
29
|
|
|
30
|
-
import { getRepositoryUrl } from '
|
|
31
|
-
import { createCheckReporter } from '
|
|
32
|
-
import { loadCursorIgnorePaths } from '
|
|
33
|
-
import { walkDir } from '
|
|
30
|
+
import { getRepositoryUrl } from '../../../../scripts/auto-rules.mjs'
|
|
31
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
32
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
33
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
34
34
|
|
|
35
35
|
const NITRA_REPOSITORY_URL_MARKER = 'https://github.com/nitra/'
|
|
36
36
|
const ABIE_REPOSITORY_URL_MARKER = 'https://github.com/abinbevefes/'
|
|
@@ -25,11 +25,11 @@ import { join, relative } from 'node:path'
|
|
|
25
25
|
import { spawnSync } from 'node:child_process'
|
|
26
26
|
import { env } from 'node:process'
|
|
27
27
|
|
|
28
|
-
import { createCheckReporter } from '
|
|
29
|
-
import { loadCursorIgnorePaths } from '
|
|
30
|
-
import { resolveCmd } from '
|
|
31
|
-
import { walkDir } from '
|
|
32
|
-
import { getMonorepoPackageRootDirs } from '
|
|
28
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
29
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
30
|
+
import { resolveCmd } from '../../../../scripts/utils/resolve-cmd.mjs'
|
|
31
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
32
|
+
import { getMonorepoPackageRootDirs } from '../../../../scripts/utils/workspaces.mjs'
|
|
33
33
|
|
|
34
34
|
/** Імʼя CLI-пакета, який генерує AVIF. */
|
|
35
35
|
const MINIFY_PACKAGE_NAME = '@nitra/minify-image'
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { existsSync } from 'node:fs'
|
|
20
20
|
import { readFile } from 'node:fs/promises'
|
|
21
21
|
|
|
22
|
-
import { createCheckReporter } from '
|
|
22
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
23
23
|
|
|
24
24
|
/** Імʼя committed-кешу (sha1 + originalSize + size) у `@nitra/minify-image` ≥ 3.2.0. */
|
|
25
25
|
const HASH_CACHE_FILENAME = '.n-minify-image.tsv'
|
|
@@ -25,7 +25,7 @@ import { existsSync } from 'node:fs'
|
|
|
25
25
|
import { readFile } from 'node:fs/promises'
|
|
26
26
|
import { join, relative } from 'node:path'
|
|
27
27
|
|
|
28
|
-
import { createCheckReporter } from '
|
|
28
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
29
29
|
import {
|
|
30
30
|
findBunSqlPerRequestConnectionInText,
|
|
31
31
|
findBunSqlPgLeftoverCallInText,
|
|
@@ -36,10 +36,10 @@ import {
|
|
|
36
36
|
findUnsafeBunSqlInListMissingEmptyGuardInText,
|
|
37
37
|
isBunSqlScanSourceFile,
|
|
38
38
|
textHasBunSqlImport
|
|
39
|
-
} from '
|
|
40
|
-
import { findAllPackageJsonPaths } from '
|
|
41
|
-
import { loadCursorIgnorePaths } from '
|
|
42
|
-
import { walkDir } from '
|
|
39
|
+
} from '../../../../scripts/utils/bun-sql-scan.mjs'
|
|
40
|
+
import { findAllPackageJsonPaths } from '../../../../scripts/utils/find-package-json-paths.mjs'
|
|
41
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
42
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Збирає абсолютні шляхи JS/TS джерел у репозиторії для скану Bun SQL патернів.
|
|
@@ -14,10 +14,10 @@ import { existsSync } from 'node:fs'
|
|
|
14
14
|
import { readFile } from 'node:fs/promises'
|
|
15
15
|
import { join, relative } from 'node:path'
|
|
16
16
|
|
|
17
|
-
import { createCheckReporter } from '
|
|
18
|
-
import { loadCursorIgnorePaths } from '
|
|
19
|
-
import { findRedisImportsInText, isRedisScanSourceFile, shouldSkipFileForRedisScan } from '
|
|
20
|
-
import { walkDir } from '
|
|
17
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
18
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
19
|
+
import { findRedisImportsInText, isRedisScanSourceFile, shouldSkipFileForRedisScan } from '../../../../scripts/utils/redis-imports.mjs'
|
|
20
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Збирає абсолютні шляхи JS/TS джерел у репозиторії для скану заборонених redis-імпортів.
|
|
@@ -16,17 +16,31 @@ import { copyFile, readFile } from 'node:fs/promises'
|
|
|
16
16
|
import { dirname, join } from 'node:path'
|
|
17
17
|
import { fileURLToPath } from 'node:url'
|
|
18
18
|
|
|
19
|
-
import { createCheckReporter } from '
|
|
19
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
20
20
|
|
|
21
21
|
/** Шлях до канонічного oxlint JSON у цьому пакеті (для перевірки та тестів). */
|
|
22
22
|
export const OXLINT_CANONICAL_JSON_PATH = join(
|
|
23
23
|
dirname(fileURLToPath(import.meta.url)),
|
|
24
|
+
'..',
|
|
25
|
+
'..',
|
|
26
|
+
'..',
|
|
27
|
+
'..',
|
|
28
|
+
'scripts',
|
|
24
29
|
'utils',
|
|
25
30
|
'oxlint-canonical.json'
|
|
26
31
|
)
|
|
27
32
|
|
|
28
33
|
/** Шлях до канонічного knip JSON у цьому пакеті — копіюється у корінь проєкту-споживача, якщо відсутній. */
|
|
29
|
-
export const KNIP_CANONICAL_JSON_PATH = join(
|
|
34
|
+
export const KNIP_CANONICAL_JSON_PATH = join(
|
|
35
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
36
|
+
'..',
|
|
37
|
+
'..',
|
|
38
|
+
'..',
|
|
39
|
+
'..',
|
|
40
|
+
'scripts',
|
|
41
|
+
'utils',
|
|
42
|
+
'knip-canonical.json'
|
|
43
|
+
)
|
|
30
44
|
|
|
31
45
|
/** Мінімальні рекомендації розширень редактора з js-lint.mdc (eslint, oxlint, GA). */
|
|
32
46
|
export const REQUIRED_VSCODE_EXTENSIONS = ['dbaeumer.vscode-eslint', 'github.vscode-github-actions', 'oxc.oxc-vscode']
|
|
@@ -12,8 +12,8 @@ import { existsSync } from 'node:fs'
|
|
|
12
12
|
import { readFile } from 'node:fs/promises'
|
|
13
13
|
import { join, relative } from 'node:path'
|
|
14
14
|
|
|
15
|
-
import { createCheckReporter } from '
|
|
16
|
-
import { findAllPackageJsonPaths } from '
|
|
15
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
16
|
+
import { findAllPackageJsonPaths } from '../../../../scripts/utils/find-package-json-paths.mjs'
|
|
17
17
|
import {
|
|
18
18
|
findMssqlPerRequestConnectionInText,
|
|
19
19
|
findSharedMssqlRequestInText,
|
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
findUnsafeMssqlInListUnparsedInText,
|
|
23
23
|
findUnsafeMssqlInListMissingEmptyGuardInText,
|
|
24
24
|
isMssqlScanSourceFile
|
|
25
|
-
} from '
|
|
26
|
-
import { loadCursorIgnorePaths } from '
|
|
27
|
-
import { walkDir } from '
|
|
25
|
+
} from '../../../../scripts/utils/mssql-pool-scan.mjs'
|
|
26
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
27
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
28
28
|
|
|
29
29
|
const VERSION_PREFIX_RE = /^[\^~>=<]+\s*/u
|
|
30
30
|
const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/u
|
|
@@ -37,21 +37,21 @@ import {
|
|
|
37
37
|
findBunyanImportsInText,
|
|
38
38
|
isBunyanScanSourceFile,
|
|
39
39
|
shouldSkipFileForBunyanScan
|
|
40
|
-
} from '
|
|
41
|
-
import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from '
|
|
42
|
-
import { createCheckReporter } from '
|
|
43
|
-
import { runConftestBatch } from '
|
|
44
|
-
import { findConnFileRuleViolations, isConnFileRulesSourceFile } from '
|
|
40
|
+
} from '../../../../scripts/utils/bunyan-imports.mjs'
|
|
41
|
+
import { findUncheckedProcessEnvInText, isCheckEnvScanSourceFile } from '../../../../scripts/utils/check-env-scan.mjs'
|
|
42
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
43
|
+
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
44
|
+
import { findConnFileRuleViolations, isConnFileRulesSourceFile } from '../../../../scripts/utils/conn-file-rules.mjs'
|
|
45
45
|
import {
|
|
46
46
|
findConnFactoryImportsInText,
|
|
47
47
|
isConnImportsScanSourceFile,
|
|
48
48
|
isInsideConnDir,
|
|
49
49
|
resolveConnDirFromPackageJson
|
|
50
|
-
} from '
|
|
51
|
-
import { loadCursorIgnorePaths } from '
|
|
52
|
-
import { findPromiseSetTimeoutInText, isPromiseSetTimeoutScanSourceFile } from '
|
|
53
|
-
import { walkDir } from '
|
|
54
|
-
import { getMonorepoPackageRootDirs } from '
|
|
50
|
+
} from '../../../../scripts/utils/conn-imports-scan.mjs'
|
|
51
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
52
|
+
import { findPromiseSetTimeoutInText, isPromiseSetTimeoutScanSourceFile } from '../../../../scripts/utils/promise-settimeout-scan.mjs'
|
|
53
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
54
|
+
import { getMonorepoPackageRootDirs } from '../../../../scripts/utils/workspaces.mjs'
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Чи існує непорожній за змістом маркер каталогу `src/` (рекомендована структура js-run).
|
|
@@ -134,10 +134,10 @@ import { basename, dirname, join, relative, resolve } from 'node:path'
|
|
|
134
134
|
|
|
135
135
|
import { isSeq, parseAllDocuments, parseDocument } from 'yaml'
|
|
136
136
|
|
|
137
|
-
import { createCheckReporter } from '
|
|
138
|
-
import { loadCursorIgnorePaths } from '
|
|
139
|
-
import { runConftestBatch } from '
|
|
140
|
-
import { walkDir } from '
|
|
137
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
138
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
139
|
+
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
140
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
141
141
|
|
|
142
142
|
/** Версія набору схем yannh — узгоджено з k8s.mdc */
|
|
143
143
|
const YANNH_PIN = 'v1.33.9-standalone-strict'
|
|
@@ -17,11 +17,11 @@ import { existsSync } from 'node:fs'
|
|
|
17
17
|
import { readdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'
|
|
18
18
|
import { basename, dirname, join, relative } from 'node:path'
|
|
19
19
|
|
|
20
|
-
import { findDockerfilePaths } from '
|
|
21
|
-
import { createCheckReporter } from '
|
|
22
|
-
import { loadCursorIgnorePaths } from '
|
|
23
|
-
import { runConftestBatch } from '
|
|
24
|
-
import { walkDir } from '
|
|
20
|
+
import { findDockerfilePaths } from '../../../docker/js/lint/check.mjs'
|
|
21
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
22
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
23
|
+
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
24
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
25
25
|
|
|
26
26
|
const LINE_SPLIT_RE = /\r?\n/u
|
|
27
27
|
const INI_KEY_RE = /^([A-Za-z_]\w*)\s*=/u
|
|
@@ -32,10 +32,10 @@ import { promisify } from 'node:util'
|
|
|
32
32
|
|
|
33
33
|
import { parseSync } from 'oxc-parser'
|
|
34
34
|
|
|
35
|
-
import { dynamicImportModule, langFromPath, requireCallModule, walkAstWithAncestors } from '
|
|
36
|
-
import { createCheckReporter } from '
|
|
37
|
-
import { loadCursorIgnorePaths } from '
|
|
38
|
-
import { walkDir } from '
|
|
35
|
+
import { dynamicImportModule, langFromPath, requireCallModule, walkAstWithAncestors } from '../../../../scripts/utils/ast-scan-utils.mjs'
|
|
36
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
37
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
38
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
39
39
|
|
|
40
40
|
const execFileAsync = promisify(execFile)
|
|
41
41
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { existsSync } from 'node:fs'
|
|
13
13
|
|
|
14
|
-
import { createCheckReporter } from '
|
|
14
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Перевіряє відповідність проєкту правилам php.mdc.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { existsSync } from 'node:fs'
|
|
20
20
|
import { readFile } from 'node:fs/promises'
|
|
21
21
|
|
|
22
|
-
import { createCheckReporter } from '
|
|
22
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Альтернатива полю `stylelint` у `package.json` — зовнішній файл конфігу. Якщо
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
import { existsSync, statSync } from 'node:fs'
|
|
19
19
|
import { readFile } from 'node:fs/promises'
|
|
20
20
|
|
|
21
|
-
import { createCheckReporter } from '
|
|
22
|
-
import { runConftestBatch } from '
|
|
21
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
22
|
+
import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Чи є префікс `@tauri-apps/` у ключах `dependencies` або `devDependencies`.
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
import { existsSync } from 'node:fs'
|
|
33
33
|
import { readFile } from 'node:fs/promises'
|
|
34
34
|
|
|
35
|
-
import { createCheckReporter } from '
|
|
36
|
-
import { anyRunStepIncludes, parseWorkflowYaml } from '
|
|
35
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
36
|
+
import { anyRunStepIncludes, parseWorkflowYaml } from '../../../../scripts/utils/gha-workflow.mjs'
|
|
37
37
|
|
|
38
38
|
/** Заголовок абзацу про апостроф у text.mdc / n-text.mdc. */
|
|
39
39
|
const UK_APOSTROPHE_HEADING = '**Український апостроф:**'
|
|
@@ -21,16 +21,16 @@ import { existsSync } from 'node:fs'
|
|
|
21
21
|
import { readFile } from 'node:fs/promises'
|
|
22
22
|
import { join, relative } from 'node:path'
|
|
23
23
|
|
|
24
|
-
import { createCheckReporter } from '
|
|
24
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
25
25
|
import {
|
|
26
26
|
findForbiddenNodeImportsInVueFile,
|
|
27
27
|
findForbiddenVueImportsInSourceFile,
|
|
28
28
|
isVueImportScanSourceFile,
|
|
29
29
|
shouldSkipFileForVueImportScan
|
|
30
|
-
} from '
|
|
31
|
-
import { loadCursorIgnorePaths } from '
|
|
32
|
-
import { walkDir } from '
|
|
33
|
-
import { getMonorepoPackageRootDirs } from '
|
|
30
|
+
} from '../../../../scripts/utils/vue-forbidden-imports.mjs'
|
|
31
|
+
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
32
|
+
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
33
|
+
import { getMonorepoPackageRootDirs } from '../../../../scripts/utils/workspaces.mjs'
|
|
34
34
|
|
|
35
35
|
const ESBUILD_RE = /\besbuild\b/
|
|
36
36
|
|
package/scripts/auto-skills.mjs
CHANGED
|
@@ -1,81 +1,122 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Автовизначення skills для `.n-cursor.json` за умовами
|
|
2
|
+
* Автовизначення skills для `.n-cursor.json` за умовами зі `npm/skills/<skill>/auto.md`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* умови, які вже формалізовані для відповідного правила. Наприклад:
|
|
4
|
+
* `auto.md` — джерело правди (а не hardcoded мапа). Підтримуються три варіанти:
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* - `завжди` — скіл активується незалежно від інших правил
|
|
7
|
+
* (приклади: `fix`, `lint`, `llm-patch`, `publish-telegram`).
|
|
8
|
+
* - `[rule, rule, …]` — скіл активується, якщо ВСІ перелічені правила вже виявлені
|
|
9
|
+
* auto-rules (приклади: `abie-clean - [abie]`, `taze - [bun]`).
|
|
10
|
+
* - файл відсутній або формат не розпізнано — скіл opt-in лише через `.n-cursor.json:skills`.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
+
* Сканування `npm/skills/` — sync під час завантаження модуля (детермінізм + sync API
|
|
13
|
+
* `auto-rules.mjs`-сусіда). Кеш на час процесу.
|
|
12
14
|
*/
|
|
15
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
16
|
+
import { dirname, join } from 'node:path'
|
|
17
|
+
import { fileURLToPath } from 'node:url'
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'lint',
|
|
20
|
-
'llm-patch',
|
|
21
|
-
'publish-telegram',
|
|
22
|
-
'taze'
|
|
23
|
-
])
|
|
19
|
+
const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
20
|
+
const SKILLS_DIR = join(PACKAGE_ROOT, 'skills')
|
|
21
|
+
|
|
22
|
+
const ALWAYS_LITERAL = 'завжди'
|
|
23
|
+
const BRACKET_LIST_RE = /^\[([^\]]+)\]$/u
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
* Ключ варто автододати, коли всі правила-залежності вже додані до конфігу автодетектом.
|
|
26
|
+
* @typedef {{ always: true } | { rules: readonly string[] }} SkillAutoSpec
|
|
28
27
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Парсить тіло `auto.md` одного скіла.
|
|
31
|
+
* @param {string} text вміст файла (без `trim`)
|
|
32
|
+
* @returns {SkillAutoSpec | null} `null` — формат не розпізнано (= opt-in)
|
|
33
|
+
*/
|
|
34
|
+
function parseSkillAutoSpec(text) {
|
|
35
|
+
const trimmed = text.trim()
|
|
36
|
+
if (trimmed === ALWAYS_LITERAL) {
|
|
37
|
+
return { always: true }
|
|
38
|
+
}
|
|
39
|
+
const m = trimmed.match(BRACKET_LIST_RE)
|
|
40
|
+
if (m) {
|
|
41
|
+
const rules = m[1]
|
|
42
|
+
.split(',')
|
|
43
|
+
.map(s => s.trim())
|
|
44
|
+
.filter(s => s.length > 0)
|
|
45
|
+
if (rules.length === 0) return null
|
|
46
|
+
return { rules: Object.freeze(rules) }
|
|
47
|
+
}
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Сканує `npm/skills/<id>/auto.md`. Скіли без `auto.md` або з нерозпізнаним
|
|
53
|
+
* вмістом не потрапляють у результат — їх можна вмикати лише вручну в конфізі.
|
|
54
|
+
* @param {string} [skillsDir] override для тестів
|
|
55
|
+
* @returns {Record<string, SkillAutoSpec>}
|
|
56
|
+
*/
|
|
57
|
+
export function discoverSkillAutoActivation(skillsDir = SKILLS_DIR) {
|
|
58
|
+
if (!existsSync(skillsDir)) return {}
|
|
59
|
+
/** @type {Record<string, SkillAutoSpec>} */
|
|
60
|
+
const out = {}
|
|
61
|
+
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
62
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) continue
|
|
63
|
+
const autoMdPath = join(skillsDir, entry.name, 'auto.md')
|
|
64
|
+
if (!existsSync(autoMdPath)) continue
|
|
65
|
+
const spec = parseSkillAutoSpec(readFileSync(autoMdPath, 'utf8'))
|
|
66
|
+
if (spec) out[entry.name] = spec
|
|
67
|
+
}
|
|
68
|
+
return out
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Cache на час процесу: один скан `npm/skills/` дає всю автоактивацію. */
|
|
72
|
+
const SKILL_AUTO_ACTIVATION = discoverSkillAutoActivation()
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Стабільний алфавітний порядок скілів з автоактивацією. Експортовано для зворотної
|
|
76
|
+
* сумісності (попередня версія мала жорстко прописаний `AUTO_SKILL_ORDER`).
|
|
77
|
+
*/
|
|
78
|
+
export const AUTO_SKILL_ORDER = Object.freeze(
|
|
79
|
+
Object.keys(SKILL_AUTO_ACTIVATION).toSorted((a, b) => a.localeCompare(b))
|
|
35
80
|
)
|
|
36
81
|
|
|
37
|
-
/**
|
|
38
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Похідна view на `SKILL_AUTO_ACTIVATION`: лише скіли з rule-залежностями.
|
|
84
|
+
* Експортовано для зворотної сумісності та автодоку.
|
|
85
|
+
*/
|
|
86
|
+
export const AUTO_SKILL_RULE_DEPENDENCIES = Object.freeze(
|
|
87
|
+
Object.fromEntries(
|
|
88
|
+
Object.entries(SKILL_AUTO_ACTIVATION)
|
|
89
|
+
.filter(([, spec]) => 'rules' in spec)
|
|
90
|
+
.map(([id, spec]) => [id, /** @type {{ rules: readonly string[] }} */ (spec).rules])
|
|
91
|
+
)
|
|
92
|
+
)
|
|
39
93
|
|
|
40
94
|
const DEFAULT_DISABLED_LIST = Object.freeze([])
|
|
41
95
|
|
|
42
96
|
/**
|
|
43
|
-
* Визначає авто-skills згідно з `skills/<skill>/auto.md`.
|
|
97
|
+
* Визначає авто-skills згідно з вмістом `skills/<skill>/auto.md`.
|
|
44
98
|
* @param {object} params параметри
|
|
45
99
|
* @param {string[]} params.availableSkills перелік доступних skills із пакету (id без префікса n-)
|
|
46
100
|
* @param {string[]} params.detectedRules id правил, виявлених auto-rules (вхідні залежності)
|
|
47
101
|
* @param {string[]} [params.disableSkills] список `disable-skills` з конфігу
|
|
48
|
-
* @returns {{ skills: string[] }} список id у стабільному порядку
|
|
102
|
+
* @returns {{ skills: string[] }} список id у стабільному алфавітному порядку
|
|
49
103
|
*/
|
|
50
104
|
export function detectAutoSkills({ availableSkills, detectedRules, disableSkills = DEFAULT_DISABLED_LIST }) {
|
|
51
105
|
const normalizedSkills = new Set(availableSkills.map(s => s.trim().toLowerCase()))
|
|
52
106
|
const disableSkillsSet = new Set(disableSkills)
|
|
53
107
|
const detectedRulesSet = new Set(detectedRules)
|
|
54
108
|
|
|
55
|
-
/** @type {string
|
|
56
|
-
const detected =
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Додає skill до результату, якщо він доступний і не в disable-списку.
|
|
60
|
-
* @param {string} skillId id skill
|
|
61
|
-
* @returns {void}
|
|
62
|
-
*/
|
|
63
|
-
function addSkill(skillId) {
|
|
64
|
-
if (!normalizedSkills.has(skillId) || disableSkillsSet.has(skillId) || detected.includes(skillId)) {
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
detected.push(skillId)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
for (const skillId of ALWAYS_ON_SKILLS) {
|
|
71
|
-
addSkill(skillId)
|
|
72
|
-
}
|
|
109
|
+
/** @type {Set<string>} */
|
|
110
|
+
const detected = new Set()
|
|
73
111
|
|
|
74
|
-
for (const [skillId,
|
|
75
|
-
if (
|
|
76
|
-
|
|
112
|
+
for (const [skillId, spec] of Object.entries(SKILL_AUTO_ACTIVATION)) {
|
|
113
|
+
if (!normalizedSkills.has(skillId) || disableSkillsSet.has(skillId)) continue
|
|
114
|
+
if ('always' in spec) {
|
|
115
|
+
detected.add(skillId)
|
|
116
|
+
} else if (spec.rules.every(d => detectedRulesSet.has(d))) {
|
|
117
|
+
detected.add(skillId)
|
|
77
118
|
}
|
|
78
119
|
}
|
|
79
120
|
|
|
80
|
-
return { skills: AUTO_SKILL_ORDER.filter(id => detected.
|
|
121
|
+
return { skills: AUTO_SKILL_ORDER.filter(id => detected.has(id)) }
|
|
81
122
|
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Discovery rules для CLI `check`. Шукає правила, для яких є щось «прогонне»:
|
|
3
3
|
* - JS concerns: `rules/<id>/js/<concern>/<check.mjs | check-*.mjs>` — кожен concern окремий вузол.
|
|
4
4
|
* - Policy concerns: `rules/<id>/policy/<concern>/target.json` — пара з `<concern>.rego`.
|
|
5
|
-
* - Legacy JS (на час міграції): `rules/<id>/js/check.mjs` (плаский) — мапиться у concern `legacy`,
|
|
6
|
-
* щоб не ламати ще не мігровані правила.
|
|
7
5
|
*
|
|
8
6
|
* Каталог `utils/` всередині `js/` свідомо пропускається — це хелпери, не концерни.
|
|
9
7
|
* Файли `*.test.mjs` фільтруються regex (`^check(?:-.+)?\.mjs$`).
|
|
8
|
+
* Top-level плаский `js/check.mjs` (legacy) більше не підтримується — усі вшиті правила
|
|
9
|
+
* у пакеті розпиляні на concern-структуру.
|
|
10
10
|
*
|
|
11
11
|
* Намеренно НЕ парсимо `target.json` тут (це робить runner). Discovery — швидкий скан структури:
|
|
12
12
|
* шляхи + назви, без I/O вмісту.
|
|
@@ -20,9 +20,8 @@ const TEST_SUFFIX = '.test.mjs'
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @typedef {object} JsConcern
|
|
23
|
-
* @property {string} name імʼя концерну (`<name>` у `js/<name>/`)
|
|
23
|
+
* @property {string} name імʼя концерну (`<name>` у `js/<name>/`)
|
|
24
24
|
* @property {string[]} files імена `check*.mjs` у концерні (відсортовані алфавітно)
|
|
25
|
-
* @property {boolean} legacy чи це fallback на плаский `js/check.mjs`
|
|
26
25
|
*/
|
|
27
26
|
|
|
28
27
|
/**
|
|
@@ -38,8 +37,7 @@ const TEST_SUFFIX = '.test.mjs'
|
|
|
38
37
|
*/
|
|
39
38
|
|
|
40
39
|
/**
|
|
41
|
-
* Перелічує JS-концерни одного правила: підкаталоги `js/<name>/` з принаймні одним `check*.mjs
|
|
42
|
-
* плюс legacy-fallback на плаский `js/check.mjs` (без підкаталогу).
|
|
40
|
+
* Перелічує JS-концерни одного правила: підкаталоги `js/<name>/` з принаймні одним `check*.mjs`.
|
|
43
41
|
*
|
|
44
42
|
* `js/utils/` свідомо пропускається — це хелпери, а не концерни.
|
|
45
43
|
* @param {string} jsDir абсолютний шлях `rules/<id>/js/`
|
|
@@ -49,7 +47,6 @@ async function listJsConcerns(jsDir) {
|
|
|
49
47
|
if (!existsSync(jsDir)) return []
|
|
50
48
|
const topLevel = await readdir(jsDir, { withFileTypes: true })
|
|
51
49
|
|
|
52
|
-
// Перевага — нова concern-структура (`js/<concern>/check*.mjs`).
|
|
53
50
|
/** @type {JsConcern[]} */
|
|
54
51
|
const concerns = []
|
|
55
52
|
for (const entry of topLevel) {
|
|
@@ -59,21 +56,7 @@ async function listJsConcerns(jsDir) {
|
|
|
59
56
|
.filter(n => CHECK_FILENAME_RE.test(n) && !n.endsWith(TEST_SUFFIX))
|
|
60
57
|
.toSorted((a, b) => a.localeCompare(b))
|
|
61
58
|
if (files.length > 0) {
|
|
62
|
-
concerns.push({ name: entry.name, files
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Legacy fallback — лише якщо subdir-концернів немає взагалі. Гібридні правила
|
|
67
|
-
// (одночасно legacy check.mjs + нові концерни) трактуються як уже мігровані:
|
|
68
|
-
// CLI запускає тільки субдиректорні концерни, flat-файл лишається для backward-compat
|
|
69
|
-
// тестів, які імпортують `check` напряму.
|
|
70
|
-
if (concerns.length === 0) {
|
|
71
|
-
const flatChecks = topLevel
|
|
72
|
-
.filter(e => e.isFile() && CHECK_FILENAME_RE.test(e.name) && !e.name.endsWith(TEST_SUFFIX))
|
|
73
|
-
.map(e => e.name)
|
|
74
|
-
.toSorted((a, b) => a.localeCompare(b))
|
|
75
|
-
if (flatChecks.length > 0) {
|
|
76
|
-
concerns.push({ name: 'legacy', files: flatChecks, legacy: true })
|
|
59
|
+
concerns.push({ name: entry.name, files })
|
|
77
60
|
}
|
|
78
61
|
}
|
|
79
62
|
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* false — друкуємо `✅ правило не застосовне` і завершуємо без подальших викликів.
|
|
7
7
|
* 2. **JS-концерни** — кожен `check*.mjs` у `js/<concern>/`. Concern `applies` теж може мати
|
|
8
8
|
* `check()` для друку контексту (його `applies()` уже відпрацював на кроці 1, він не повторюється).
|
|
9
|
-
* Legacy-fallback: плаский `js/check.mjs` лежить як concern `legacy` — імпортується з кореня `js/`,
|
|
10
|
-
* а не з підкаталога.
|
|
11
9
|
* 3. **Policy-концерни** — кожен `policy/<concern>/target.json` через `runConftestBatch`.
|
|
12
10
|
* Реcолвер `resolveTargetFiles` ділить cache (`walkCache`) між концернами.
|
|
13
11
|
*
|
|
@@ -32,9 +30,7 @@ const APPLIES_CONCERN_NAME = 'applies'
|
|
|
32
30
|
* @returns {string} абсолютний шлях
|
|
33
31
|
*/
|
|
34
32
|
function resolveJsCheckPath(bundledRulesDir, ruleId, concern, fileName) {
|
|
35
|
-
return concern.
|
|
36
|
-
? join(bundledRulesDir, ruleId, 'js', fileName)
|
|
37
|
-
: join(bundledRulesDir, ruleId, 'js', concern.name, fileName)
|
|
33
|
+
return join(bundledRulesDir, ruleId, 'js', concern.name, fileName)
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
/**
|