@nitra/cursor 1.38.0 → 1.39.1

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/package.json +1 -1
  3. package/rules/abie/meta.json +1 -0
  4. package/rules/adr/meta.json +1 -0
  5. package/rules/bun/meta.json +1 -0
  6. package/rules/capacitor/meta.json +1 -0
  7. package/rules/changelog/meta.json +1 -0
  8. package/rules/ci4/meta.json +1 -0
  9. package/rules/docker/meta.json +1 -0
  10. package/rules/efes/meta.json +1 -0
  11. package/rules/feedback/meta.json +1 -0
  12. package/rules/ga/meta.json +1 -0
  13. package/rules/graphql/meta.json +1 -0
  14. package/rules/hasura/meta.json +1 -0
  15. package/rules/image-avif/meta.json +1 -0
  16. package/rules/image-compress/meta.json +1 -0
  17. package/rules/js-bun-db/meta.json +1 -0
  18. package/rules/js-bun-redis/meta.json +1 -0
  19. package/rules/js-lint/meta.json +1 -0
  20. package/rules/js-mssql/meta.json +1 -0
  21. package/rules/js-run/meta.json +1 -0
  22. package/rules/k8s/meta.json +1 -0
  23. package/rules/nginx-default-tpl/meta.json +1 -0
  24. package/rules/npm-module/js/rule_meta.mjs +63 -0
  25. package/rules/npm-module/meta.json +1 -0
  26. package/rules/php/meta.json +1 -0
  27. package/rules/rego/meta.json +1 -0
  28. package/rules/release/meta.json +1 -0
  29. package/rules/rust/meta.json +1 -0
  30. package/rules/security/meta.json +1 -0
  31. package/rules/style-lint/meta.json +1 -0
  32. package/rules/tauri/meta.json +1 -0
  33. package/rules/test/meta.json +1 -0
  34. package/rules/text/meta.json +1 -0
  35. package/rules/vue/meta.json +1 -0
  36. package/rules/worktree/fix.mjs +19 -0
  37. package/rules/worktree/meta.json +1 -0
  38. package/rules/worktree/worktree.mdc +14 -0
  39. package/schemas/rule-meta.json +39 -0
  40. package/schemas/v8r-catalog.json +5 -0
  41. package/scripts/auto-rules.mjs +151 -449
  42. package/scripts/lib/rule-meta-helpers.mjs +103 -0
  43. package/scripts/lib/rule-meta.mjs +66 -0
  44. package/scripts/lib/rule-predicates.mjs +147 -0
  45. package/skills/worktree/meta.json +1 -1
  46. package/rules/abie/auto.md +0 -1
  47. package/rules/adr/auto.md +0 -1
  48. package/rules/bun/auto.md +0 -1
  49. package/rules/capacitor/auto.md +0 -1
  50. package/rules/changelog/auto.md +0 -1
  51. package/rules/docker/auto.md +0 -1
  52. package/rules/efes/auto.md +0 -1
  53. package/rules/ga/auto.md +0 -1
  54. package/rules/graphql/auto.md +0 -1
  55. package/rules/hasura/auto.md +0 -1
  56. package/rules/image-avif/auto.md +0 -1
  57. package/rules/image-compress/auto.md +0 -1
  58. package/rules/js-bun-db/auto.md +0 -1
  59. package/rules/js-bun-redis/auto.md +0 -1
  60. package/rules/js-lint/auto.md +0 -1
  61. package/rules/js-mssql/auto.md +0 -1
  62. package/rules/js-run/auto.md +0 -1
  63. package/rules/k8s/auto.md +0 -1
  64. package/rules/nginx-default-tpl/auto.md +0 -1
  65. package/rules/npm-module/auto.md +0 -1
  66. package/rules/php/auto.md +0 -1
  67. package/rules/rego/auto.md +0 -1
  68. package/rules/rust/auto.md +0 -1
  69. package/rules/security/auto.md +0 -1
  70. package/rules/style-lint/auto.md +0 -1
  71. package/rules/tauri/auto.md +0 -1
  72. package/rules/test/auto.md +0 -1
  73. package/rules/text/auto.md +0 -1
  74. package/rules/vue/auto.md +0 -1
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Чисті хелпери конфігу/репо для автодетекту правил: id-міграції, нормалізація
3
+ * списків, repository URL, monorepo-детект.
4
+ *
5
+ * Винесені з `auto-rules.mjs`, щоб `rule-predicates.mjs` міг використати
6
+ * `getRepositoryUrl` без циклу імпортів. `auto-rules.mjs` пізніше ре-експортує їх звідси.
7
+ */
8
+
9
+ /**
10
+ * Карта міграції застарілих rule-id у `.n-cursor.json` на актуальні.
11
+ * Застосовується автоматично при читанні конфігу (як для `rules`, так і для `disable-rules`).
12
+ * Приклад: `image` → `image-compress` + `image-avif` (правило розщеплене у 1.8.197).
13
+ */
14
+ export const RULE_MIGRATIONS = Object.freeze(
15
+ /** @type {Record<string, readonly string[]>} */ ({
16
+ image: Object.freeze(['image-compress', 'image-avif'])
17
+ })
18
+ )
19
+
20
+ /**
21
+ * Розгортає застарілі rule-id у списку згідно з `RULE_MIGRATIONS`. Зберігає порядок,
22
+ * дедуплікує. Чистий хелпер: не мутує вхід, не логує.
23
+ * @param {string[]} ids нормалізований список id (як з `normalizeIdList`)
24
+ * @returns {string[]} список з legacy-id, заміненими на нові; решта без змін
25
+ */
26
+ export function migrateRuleIds(ids) {
27
+ /** @type {string[]} */
28
+ const out = []
29
+ for (const id of ids) {
30
+ const replacement = Object.hasOwn(RULE_MIGRATIONS, id) ? RULE_MIGRATIONS[id] : [id]
31
+ for (const newId of replacement) {
32
+ if (!out.includes(newId)) out.push(newId)
33
+ }
34
+ }
35
+ return out
36
+ }
37
+
38
+ /**
39
+ * Повертає лише ті legacy rule-id зі списку, для яких є запис у `RULE_MIGRATIONS`.
40
+ * Використовується для людинозрозумілого логування міграції при синхронізації CLI.
41
+ * @param {string[]} ids нормалізований список id
42
+ * @returns {string[]} legacy id, які потребуватимуть заміни у `migrateRuleIds`
43
+ */
44
+ export function detectLegacyRuleIds(ids) {
45
+ return ids.filter(id => Object.hasOwn(RULE_MIGRATIONS, id))
46
+ }
47
+
48
+ /**
49
+ * Нормалізує список ідентифікаторів (trim + lowercase + унікальність збереженням порядку).
50
+ * @param {unknown} value вихідне значення з `.n-cursor.json`
51
+ * @returns {string[]} масив id у нормалізованому вигляді
52
+ */
53
+ export function normalizeIdList(value) {
54
+ if (!Array.isArray(value)) {
55
+ return []
56
+ }
57
+ const out = []
58
+ for (const item of value) {
59
+ const normalized = String(item).trim().toLowerCase()
60
+ if (normalized && !out.includes(normalized)) {
61
+ out.push(normalized)
62
+ }
63
+ }
64
+ return out
65
+ }
66
+
67
+ /**
68
+ * Повертає URL репозиторію з package.json (`repository` може бути рядком або обʼєктом).
69
+ * @param {unknown} repository значення `packageJson.repository`
70
+ * @returns {string | null} URL або null
71
+ */
72
+ export function getRepositoryUrl(repository) {
73
+ if (typeof repository === 'string') {
74
+ return repository
75
+ }
76
+ if (repository && typeof repository === 'object' && !Array.isArray(repository)) {
77
+ const url = /** @type {Record<string, unknown>} */ (repository).url
78
+ if (typeof url === 'string') {
79
+ return url
80
+ }
81
+ }
82
+ return null
83
+ }
84
+
85
+ /**
86
+ * Чи package.json виглядає як монорепо (поле `workspaces`).
87
+ * @param {unknown} packageJson кореневий package.json як JS-обʼєкт
88
+ * @returns {boolean} true, якщо оголошено workspaces
89
+ */
90
+ export function isMonorepoPackage(packageJson) {
91
+ if (packageJson === null || typeof packageJson !== 'object' || Array.isArray(packageJson)) {
92
+ return false
93
+ }
94
+ const workspaces = /** @type {Record<string, unknown>} */ (packageJson).workspaces
95
+ if (Array.isArray(workspaces)) {
96
+ return workspaces.length > 0
97
+ }
98
+ if (workspaces && typeof workspaces === 'object' && !Array.isArray(workspaces)) {
99
+ const packages = /** @type {Record<string, unknown>} */ (workspaces).packages
100
+ return Array.isArray(packages) && packages.length > 0
101
+ }
102
+ return false
103
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Парсер метаданих правила з `npm/rules/<id>/meta.json` (data-driven автодетект).
3
+ *
4
+ * `meta.json.auto` має один із чотирьох видів:
5
+ * - `"завжди"` → always-on;
6
+ * - `["rule", …]` → активується, коли всі правила-залежності виявлені;
7
+ * - `{ "glob": "<pat>" | [<pat>] }` → наявність файлів/каталогів за glob (OR);
8
+ * - `{ "predicate": "<name>", "arg"? }` → незводимий предикат із реєстру `rule-predicates.mjs`.
9
+ *
10
+ * Поля `worktree` правило НЕ має (це скілова вісь). Дзеркало `skill-meta.mjs`.
11
+ */
12
+ import { existsSync, readFileSync } from 'node:fs'
13
+ import { join } from 'node:path'
14
+
15
+ /** Літерал безумовної активації (українською, як у скілах). */
16
+ export const RULE_ALWAYS = 'завжди'
17
+
18
+ /**
19
+ * @typedef {{ always: true } | { rules: string[] } | { glob: string[] } | { predicate: string, arg: unknown }} RuleAutoSpec
20
+ */
21
+
22
+ /**
23
+ * Нормалізує значення `meta.json.auto` у дискриміновану форму.
24
+ * @param {unknown} value значення поля `auto`
25
+ * @returns {RuleAutoSpec | null} `null` — формат не розпізнано (= opt-in)
26
+ */
27
+ export function parseRuleAutoSpec(value) {
28
+ if (value === RULE_ALWAYS) return { always: true }
29
+
30
+ if (Array.isArray(value)) {
31
+ const rules = value.map(s => String(s).trim()).filter(s => s.length > 0)
32
+ return rules.length > 0 ? { rules } : null
33
+ }
34
+
35
+ if (value !== null && typeof value === 'object') {
36
+ const obj = /** @type {Record<string, unknown>} */ (value)
37
+ if ('glob' in obj) {
38
+ const raw = obj.glob
39
+ const globs = (Array.isArray(raw) ? raw : [raw]).filter(g => typeof g === 'string' && g.length > 0)
40
+ return globs.length > 0 ? { glob: /** @type {string[]} */ (globs) } : null
41
+ }
42
+ if ('predicate' in obj) {
43
+ return typeof obj.predicate === 'string' && obj.predicate.length > 0
44
+ ? { predicate: obj.predicate, arg: obj.arg }
45
+ : null
46
+ }
47
+ }
48
+ return null
49
+ }
50
+
51
+ /**
52
+ * Читає й парсить `meta.json` одного правила.
53
+ * @param {string} ruleDir абсолютний шлях до каталогу правила
54
+ * @returns {Record<string, unknown> | null} обʼєкт або `null` (немає файлу / невалідний JSON / не-обʼєкт)
55
+ */
56
+ export function readRuleMetaRaw(ruleDir) {
57
+ const metaPath = join(ruleDir, 'meta.json')
58
+ if (!existsSync(metaPath)) return null
59
+ try {
60
+ const parsed = JSON.parse(readFileSync(metaPath, 'utf8'))
61
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return null
62
+ return /** @type {Record<string, unknown>} */ (parsed)
63
+ } catch {
64
+ return null
65
+ }
66
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Реєстр незводимих до даних предикатів автодетекту правил.
3
+ *
4
+ * Прості умови (наявність файлів) живуть як `glob` у `meta.json`; ці предикати —
5
+ * для умов, що вимагають парсингу залежностей, сканування вмісту source чи URL repo.
6
+ * Декларація «який предикат + аргумент» — у `meta.json.auto.predicate`; тут — реалізація.
7
+ *
8
+ * Сигнатури неоднорідні (одні беруть `facts`, інші — `cwd`/`packageJson`), бо предикати
9
+ * читають різні джерела; виклик диспетчиться в `auto-rules.mjs` за іменем предиката.
10
+ */
11
+ import { readdir, readFile } from 'node:fs/promises'
12
+ import { join } from 'node:path'
13
+
14
+ import { getRepositoryUrl } from './rule-meta-helpers.mjs'
15
+
16
+ const IGNORED_DIR_NAMES = new Set(['node_modules', '.git', '.next', '.turbo'])
17
+
18
+ /**
19
+ * Чи package.json дерева містить будь-який із зазначених пакетів у dependencies.
20
+ * @param {string} root корінь репо
21
+ * @param {string[]} keys імена пакетів
22
+ * @returns {Promise<boolean>} true, якщо знайдено хоч один
23
+ */
24
+ async function anyDepInTree(root, keys) {
25
+ const wanted = new Set(keys)
26
+ let found = false
27
+ /** @param {string} dir каталог обходу @returns {Promise<void>} */
28
+ async function walk(dir) {
29
+ if (found) return
30
+ let entries
31
+ try {
32
+ entries = await readdir(dir, { withFileTypes: true })
33
+ } catch {
34
+ return
35
+ }
36
+ for (const entry of entries) {
37
+ if (found) return
38
+ const abs = join(dir, entry.name)
39
+ if (entry.isDirectory()) {
40
+ if (!IGNORED_DIR_NAMES.has(entry.name)) await walk(abs)
41
+ } else if (entry.isFile() && entry.name === 'package.json') {
42
+ try {
43
+ const deps = JSON.parse(await readFile(abs, 'utf8'))?.dependencies
44
+ if (deps && typeof deps === 'object' && !Array.isArray(deps)) {
45
+ for (const k of wanted) if (Object.hasOwn(deps, k)) found = true
46
+ }
47
+ } catch {
48
+ /* ігноруємо пошкоджені package.json */
49
+ }
50
+ }
51
+ }
52
+ }
53
+ await walk(root)
54
+ return found
55
+ }
56
+
57
+ /**
58
+ * Чи існує вкладений (не кореневий) package.json без `vite` у devDependencies.
59
+ * @param {string} root корінь репо
60
+ * @returns {Promise<boolean>} true, якщо знайдено
61
+ */
62
+ async function nestedWithoutVite(root) {
63
+ const rootPkg = join(root, 'package.json')
64
+ let result = false
65
+ /** @param {string} dir каталог @returns {Promise<void>} */
66
+ async function walk(dir) {
67
+ if (result) return
68
+ let entries
69
+ try {
70
+ entries = await readdir(dir, { withFileTypes: true })
71
+ } catch {
72
+ return
73
+ }
74
+ for (const entry of entries) {
75
+ if (result) return
76
+ const abs = join(dir, entry.name)
77
+ if (entry.isDirectory()) {
78
+ if (!IGNORED_DIR_NAMES.has(entry.name)) await walk(abs)
79
+ } else if (entry.isFile() && entry.name === 'package.json' && abs !== rootPkg) {
80
+ try {
81
+ const dev = JSON.parse(await readFile(abs, 'utf8'))?.devDependencies
82
+ const hasVite = dev && typeof dev === 'object' && !Array.isArray(dev) && Object.hasOwn(dev, 'vite')
83
+ if (!hasVite) result = true
84
+ } catch {
85
+ /* пошкоджений package.json не вважаємо vite-проєктом */
86
+ }
87
+ }
88
+ }
89
+ }
90
+ await walk(root)
91
+ return result
92
+ }
93
+
94
+ /** Реєстр предикатів: імʼя → реалізація. Виклик за `meta.json.auto.predicate`. */
95
+ export const RULE_PREDICATES = {
96
+ /**
97
+ * @param {unknown} packageJson кореневий package.json
98
+ * @param {string} arg підрядок-маркер URL
99
+ * @returns {boolean} true, якщо repository.url містить маркер
100
+ */
101
+ repoUrlMarker(packageJson, arg) {
102
+ const url = getRepositoryUrl(
103
+ packageJson && typeof packageJson === 'object' && !Array.isArray(packageJson)
104
+ ? /** @type {Record<string, unknown>} */ (packageJson).repository
105
+ : null
106
+ )
107
+ return typeof url === 'string' && url.toLowerCase().includes(String(arg).toLowerCase())
108
+ },
109
+ /**
110
+ * @param {string} cwd корінь репо
111
+ * @param {string[]} arg імена пакетів
112
+ * @returns {Promise<boolean>} true, якщо будь-який пакет у dependencies дерева
113
+ */
114
+ depInAnyPackageJson(cwd, arg) {
115
+ return anyDepInTree(cwd, Array.isArray(arg) ? arg : [])
116
+ },
117
+ /**
118
+ * @param {{ hasGqlTaggedTemplates: boolean }} facts факти
119
+ * @returns {boolean} true, якщо є gql-літерал
120
+ */
121
+ gqlTaggedTemplate(facts) {
122
+ return facts.hasGqlTaggedTemplates === true
123
+ },
124
+ /**
125
+ * @param {{ hasHasuraConfig: boolean }} facts факти
126
+ * @returns {boolean} true, якщо config.yaml із маркером
127
+ */
128
+ hasuraConfigMarker(facts) {
129
+ return facts.hasHasuraConfig === true
130
+ },
131
+ /**
132
+ * @param {string} cwd корінь репо
133
+ * @param {{ hasBunSqlImport: boolean }} facts факти
134
+ * @returns {Promise<boolean>} true, якщо deps pg/pg-format/mysql2 або import sql з bun
135
+ */
136
+ async jsBunDbSignal(cwd, facts) {
137
+ if (facts.hasBunSqlImport === true) return true
138
+ return anyDepInTree(cwd, ['pg', 'pg-format', 'mysql2'])
139
+ },
140
+ /**
141
+ * @param {string} cwd корінь репо
142
+ * @returns {Promise<boolean>} true, якщо вкладений package.json без vite
143
+ */
144
+ nestedPackageWithoutVite(cwd) {
145
+ return nestedWithoutVite(cwd)
146
+ }
147
+ }
@@ -1 +1 @@
1
- { "worktree": false }
1
+ { "auto": "завжди", "worktree": false }
@@ -1 +0,0 @@
1
- якщо в кореневому package.json в секції "repository" присутній текст "<https://github.com/abinbevefes/**/>"
package/rules/adr/auto.md DELETED
@@ -1 +0,0 @@
1
- завжди
package/rules/bun/auto.md DELETED
@@ -1 +0,0 @@
1
- якщо в корені проекту є package.json
@@ -1 +0,0 @@
1
- якщо в проекті є хоч один файл capacitor.config.json
@@ -1 +0,0 @@
1
- [bun]
@@ -1 +0,0 @@
1
- якщо в проекті є хоч один Dockerfile
@@ -1 +0,0 @@
1
- якщо в кореневому package.json в секції "repository" присутній текст "<https://github.com/efes-cloud/**/>"
package/rules/ga/auto.md DELETED
@@ -1 +0,0 @@
1
- якщо присутня директорія .github/workflows
@@ -1 +0,0 @@
1
- якщо хоч в одному js або vue файлі присутній gql` темплейт літерал
@@ -1 +0,0 @@
1
- якщо в директорії присутній config.yaml, який містить рядок `metadata_directory: metadata`
@@ -1 +0,0 @@
1
- [vue, image-compress]
@@ -1 +0,0 @@
1
- [bun]
@@ -1 +0,0 @@
1
- якщо в хоч одному package.json в секції dependencies присутній пакет pg, pg-format або mysql2 або є імпорт sql/SQL з Bun (приклад: import { sql } from "bun")
@@ -1 +0,0 @@
1
- якщо в хоч одному package.json в секції dependencies присутній пакет ioredis або node-redis
@@ -1 +0,0 @@
1
- якщо присутній хоч один js файл
@@ -1 +0,0 @@
1
- якщо в хоч одному package.json в секції dependencies присутній пакет mssql
@@ -1 +0,0 @@
1
- якщо це вкладена директорія з package.json (не в корені) та в devDependencies немає vite
package/rules/k8s/auto.md DELETED
@@ -1 +0,0 @@
1
- якщо присутня хоч одна директорія k8s
@@ -1 +0,0 @@
1
- якщо присутній хоч один файл з переліку - default.conf.template, default.conf, nginx.conf
@@ -1 +0,0 @@
1
- якщо в корені присутня директорія npm
package/rules/php/auto.md DELETED
@@ -1 +0,0 @@
1
- якщо в корені є composer.json
@@ -1 +0,0 @@
1
- якщо в проекті є хоч один rego
@@ -1 +0,0 @@
1
- якщо в проекті є хоч один Cargo.toml
@@ -1 +0,0 @@
1
- завжди
@@ -1 +0,0 @@
1
- якщо присутній хоч один vue або css файл
@@ -1 +0,0 @@
1
- якщо в хоч в package.json в секції dependencies присутній пакет @tauri-apps/api
@@ -1 +0,0 @@
1
- завжди
@@ -1 +0,0 @@
1
- завжди
package/rules/vue/auto.md DELETED
@@ -1 +0,0 @@
1
- якщо присутній хоч один vue файл