@nitra/cursor 1.8.210 → 1.8.212

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 CHANGED
@@ -4,6 +4,42 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.8.212] - 2026-05-08
8
+
9
+ ### Changed
10
+
11
+ - `npm/skills/taze/SKILL.md`: повний workflow замість шаблону-заглушки. Тепер
12
+ скіл бекапить `package.json`/`bun.lock`, виконує `bunx taze -w -r latest` +
13
+ `bun install`, виявляє major-стрибки порівнянням з бекапом, тягне breaking
14
+ changes з CHANGELOG модуля або git-діфу `node_modules` (з фолбеком на
15
+ встановлення старої версії в `/tmp`), шукає використання зачепленого API в
16
+ коді через `rg`, рефакторить несумісні місця (нетривіальні міграції — TODO),
17
+ прибирає тимчасові файли і віддає структурований звіт користувачу.
18
+
19
+ ## [1.8.211] - 2026-05-08
20
+
21
+ ### Added
22
+
23
+ - Окремий шлях автодетекту для скілів — `npm/bin/auto-skills.md` +
24
+ `npm/scripts/auto-skills.mjs` (`detectAutoSkills`). Скіли отримують свій
25
+ словник умов (`skill - [rules]`), залежний від уже виявлених правил, тож не
26
+ дублюють файлові ознаки з `auto-rules.md`.
27
+ - Нові авто-скіли: `publish-telegram` (завжди) і `taze` (за правилом `bun`).
28
+ - `npm/tests/auto-skills.test.mjs` — окремі тести `detectAutoSkills`
29
+ (завжди-додавані, залежності від rule-id, `disable-skills`, фільтр за
30
+ `availableSkills`).
31
+
32
+ ### Changed
33
+
34
+ - `npm/scripts/auto-rules.mjs`: `detectAutoRulesAndSkills` → `detectAutoRules`
35
+ (повертає лише `{ rules }`); прибрано `AUTO_SKILL_ORDER` і скіл-логіку.
36
+ `mergeConfigWithAutoDetected` лишається спільним і приймає вже виявлені
37
+ rules+skills, тож публічний контракт `.n-cursor.json` не змінився.
38
+ - `npm/bin/n-cursor.js` тепер послідовно викликає `detectAutoRules` і
39
+ `detectAutoSkills` (скіли отримують `detectedRules` як вхід).
40
+ - `npm/bin/auto-rules.md` залишає тільки правила; секція скілів винесена в
41
+ `auto-skills.md` з посиланням з `auto-rules.md`.
42
+
7
43
  ## [1.8.210] - 2026-05-08
8
44
 
9
45
  ### Added
package/bin/auto-rules.md CHANGED
@@ -1,6 +1,6 @@
1
- # Авто вмикання правил та скілів
1
+ # Авто вмикання правил
2
2
 
3
- В цьому файлі описані умови, по яким повинні скіли та правила автододаватись в конфіг
3
+ В цьому файлі описані умови, по яким повинні правила автододаватись в конфіг. Умови автододавання скілів — у `auto-skills.md` (виносить логіку зі словника правил у пару «правило → скіл»).
4
4
 
5
5
  ## Правила, які автоматично додається до .n-cursor.json
6
6
 
@@ -50,16 +50,6 @@ text - завжди
50
50
 
51
51
  vue - якщо присутній хоч один vue файл
52
52
 
53
- ## Скіли, які автоматично додається до .n-cursor.json
54
-
55
- abie-kustomize - якщо в кореневому package.json в секції "repository" присутній текст "<https://github.com/abinbevefes/**/>"
56
-
57
- fix - завжди
58
-
59
- lint - завжди
60
-
61
53
  ## Виключення
62
54
 
63
55
  Якщо в .n-cursor.json задано в секції disable-rules правило, то воно автоматично додаватись не повинно.
64
-
65
- Якщо в .n-cursor.json задано в секції disable-skills скіл, то він автоматично додаватись не повинен.
@@ -0,0 +1,21 @@
1
+ # Авто вмикання скілів
2
+
3
+ В цьому файлі описані умови, по яким повинні скіли автододаватись в конфіг.
4
+
5
+ ## Скіли, які автоматично додається до .n-cursor.json
6
+
7
+ Синтаксис `skill - [rules]` означає: скіл `skill` варто автододати лише якщо всі правила у списку `[rules]` уже додані до конфігу автодетектом (з `auto-rules.md`). Так залежність не дублює вихідну умову правила.
8
+
9
+ abie-kustomize - [abie]
10
+
11
+ fix - завжди
12
+
13
+ lint - завжди
14
+
15
+ publish-telegram - завжди
16
+
17
+ taze - [bun]
18
+
19
+ ## Виключення
20
+
21
+ Якщо в .n-cursor.json задано в секції disable-skills скіл, то він автоматично додаватись не повинен.
package/bin/n-cursor.js CHANGED
@@ -58,12 +58,13 @@ import { fileURLToPath } from 'node:url'
58
58
 
59
59
  import { buildAgentsCommandBulletItems } from '../scripts/build-agents-commands.mjs'
60
60
  import {
61
- detectAutoRulesAndSkills,
61
+ detectAutoRules,
62
62
  detectLegacyRuleIds,
63
63
  mergeConfigWithAutoDetected,
64
64
  normalizeIdList,
65
65
  RULE_MIGRATIONS
66
66
  } from '../scripts/auto-rules.mjs'
67
+ import { detectAutoSkills } from '../scripts/auto-skills.mjs'
67
68
  import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
68
69
  import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
69
70
  import { runLintGaCli } from '../scripts/lint-ga.mjs'
@@ -250,19 +251,22 @@ async function readConfig(paths = {}) {
250
251
  const rootPkg = await readRootPackageJsonSafe()
251
252
  const disableRules = normalizeIdList(parsedConfig['disable-rules'])
252
253
  const disableSkills = normalizeIdList(parsedConfig['disable-skills'])
253
- const autoDetected = await detectAutoRulesAndSkills({
254
+ const autoDetectedRules = await detectAutoRules({
254
255
  root: cwd(),
255
256
  availableRules,
256
- availableSkills,
257
257
  packageJsonParsed: rootPkg,
258
- disableRules,
258
+ disableRules
259
+ })
260
+ const autoDetectedSkills = detectAutoSkills({
261
+ availableSkills,
262
+ detectedRules: autoDetectedRules.rules,
259
263
  disableSkills
260
264
  })
261
265
 
262
266
  const merged = mergeConfigWithAutoDetected({
263
267
  config: parsedConfig,
264
- detectedRules: autoDetected.rules,
265
- detectedSkills: autoDetected.skills
268
+ detectedRules: autoDetectedRules.rules,
269
+ detectedSkills: autoDetectedSkills.skills
266
270
  })
267
271
 
268
272
  const rest = Object.fromEntries(Object.entries(parsedConfig).filter(([k]) => k !== '$schema'))
@@ -283,16 +287,19 @@ async function readConfig(paths = {}) {
283
287
 
284
288
  if (!existsSync(configPath)) {
285
289
  const rootPkg = await readRootPackageJsonSafe()
286
- const autoDetected = await detectAutoRulesAndSkills({
290
+ const autoDetectedRules = await detectAutoRules({
287
291
  root: cwd(),
288
292
  availableRules,
289
- availableSkills,
290
293
  packageJsonParsed: rootPkg
291
294
  })
295
+ const autoDetectedSkills = detectAutoSkills({
296
+ availableSkills,
297
+ detectedRules: autoDetectedRules.rules
298
+ })
292
299
  const defaultConfig = sortConfigIdArrays({
293
300
  $schema: CONFIG_SCHEMA_URL,
294
- rules: autoDetected.rules,
295
- skills: autoDetected.skills
301
+ rules: autoDetectedRules.rules,
302
+ skills: autoDetectedSkills.skills
296
303
  })
297
304
  await writeFile(configPath, `${JSON.stringify(defaultConfig, null, 2)}\n`, 'utf8')
298
305
  console.log(
@@ -1127,7 +1134,7 @@ async function readBundledVersionAt(packageRoot) {
1127
1134
  * Якщо `upgradeNitraCursorToLatestAndBunInstall` встановив у `node_modules/@nitra/cursor` версію,
1128
1135
  * відмінну від тієї, з якої стартував поточний процес (наприклад, з npx-кешу), запускає бінар нової
1129
1136
  * версії через `spawnSync` і завершує поточний процес із успадкованим exit-кодом. Re-exec потрібен,
1130
- * бо ES-модулі вже завантажені у V8 (RULE_MIGRATIONS, detectAutoRulesAndSkills тощо) і нова логіка
1137
+ * бо ES-модулі вже завантажені у V8 (RULE_MIGRATIONS, detectAutoRules тощо) і нова логіка
1131
1138
  * без повної заміни процесу не підхопиться. Захист від нескінченного циклу — env `NITRA_CURSOR_REEXEC=1`.
1132
1139
  * @param {string} effectivePackageRoot шлях, повернутий `upgradeNitraCursorToLatestAndBunInstall`
1133
1140
  * @returns {Promise<void>} повертається лише якщо re-exec не потрібен (інакше викликає `process.exit`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.210",
3
+ "version": "1.8.212",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,13 +1,16 @@
1
1
  /**
2
- * Автовизначення правил і skills для `.n-cursor.json` за умовами з `npm/bin/auto-rules.md`.
2
+ * Автовизначення правил для `.n-cursor.json` за умовами з `npm/bin/auto-rules.md`.
3
3
  *
4
4
  * Модуль аналізує дерево проєкту (наявність файлів/директорій, `gql\`...\`` у source,
5
5
  * залежності `mssql` / `pg` / `pg-format` / `mysql2` у `package.json`, імпорт `sql`/`SQL` з `bun`, кореневий
6
6
  * `package.json`, `config.yaml` з рядком `metadata_directory: metadata` для hasura)
7
- * та повертає ідентифікатори правил і skills, які потрібно автододати.
7
+ * та повертає ідентифікатори правил, які потрібно автододати.
8
8
  *
9
- * Також враховує винятки `disable-rules` і `disable-skills`: елементи з цих списків не
10
- * додаються автоматично.
9
+ * Враховує винятки `disable-rules`: елементи зі списку не додаються автоматично.
10
+ *
11
+ * Автодетект скілів — у `./auto-skills.mjs` (умови — у `npm/bin/auto-skills.md`).
12
+ * `mergeConfigWithAutoDetected` нижче приймає вже виявлені rules і skills і вливає
13
+ * їх у конфіг із поправкою на legacy-id (`migrateRuleIds`).
11
14
  */
12
15
  import { existsSync } from 'node:fs'
13
16
  import { readdir, readFile } from 'node:fs/promises'
@@ -47,9 +50,6 @@ export const AUTO_RULE_ORDER = Object.freeze([
47
50
  'vue'
48
51
  ])
49
52
 
50
- /** Порядок автододавання skills відповідно до `auto-rules.md`. */
51
- export const AUTO_SKILL_ORDER = Object.freeze(['abie-kustomize', 'fix', 'lint'])
52
-
53
53
  /**
54
54
  * Карта міграції застарілих rule-id у `.n-cursor.json` на актуальні.
55
55
  * Застосовується автоматично при читанні конфігу (як для `rules`, так і для `disable-rules`).
@@ -575,29 +575,23 @@ function resolveRuleDependencies(detectedRules, addRule) {
575
575
  }
576
576
 
577
577
  /**
578
- * Визначає авто-правила та skills згідно з `auto-rules.md`.
578
+ * Визначає авто-правила згідно з `auto-rules.md`.
579
579
  * @param {object} params параметри аналізу
580
580
  * @param {string} params.root абсолютний шлях до кореня репозиторію
581
581
  * @param {string[]} params.availableRules перелік доступних правил з пакету
582
- * @param {string[]} params.availableSkills перелік доступних skills з пакету
583
582
  * @param {unknown} params.packageJsonParsed кореневий package.json (розпарсений) або null
584
583
  * @param {string[]} [params.disableRules] список `disable-rules` з конфігу
585
- * @param {string[]} [params.disableSkills] список `disable-skills` з конфігу
586
- * @returns {Promise<{ rules: string[], skills: string[] }>} списки id у стабільному порядку
584
+ * @returns {Promise<{ rules: string[] }>} список id у стабільному порядку (за `AUTO_RULE_ORDER`)
587
585
  */
588
- export async function detectAutoRulesAndSkills({
586
+ export async function detectAutoRules({
589
587
  root,
590
588
  availableRules,
591
- availableSkills,
592
589
  packageJsonParsed,
593
- disableRules = DEFAULT_DISABLED_LIST,
594
- disableSkills = DEFAULT_DISABLED_LIST
590
+ disableRules = DEFAULT_DISABLED_LIST
595
591
  }) {
596
592
  const facts = await collectAutoRuleFacts(root)
597
593
  const normalizedRules = new Set(availableRules.map(r => r.trim().toLowerCase()))
598
- const normalizedSkills = new Set(availableSkills.map(s => s.trim().toLowerCase()))
599
594
  const disableRulesSet = new Set(disableRules)
600
- const disableSkillsSet = new Set(disableSkills)
601
595
 
602
596
  const packageJsonExists = existsSync(join(root, 'package.json'))
603
597
  const npmDirExists = existsSync(join(root, 'npm'))
@@ -616,8 +610,6 @@ export async function detectAutoRulesAndSkills({
616
610
 
617
611
  /** @type {string[]} */
618
612
  const detectedRules = []
619
- /** @type {string[]} */
620
- const detectedSkills = []
621
613
 
622
614
  /**
623
615
  * Додає правило до результату, якщо воно доступне і не в disable-списку.
@@ -631,18 +623,6 @@ export async function detectAutoRulesAndSkills({
631
623
  detectedRules.push(ruleId)
632
624
  }
633
625
 
634
- /**
635
- * Додає skill до результату, якщо він доступний і не в disable-списку.
636
- * @param {string} skillId id skill
637
- * @returns {void}
638
- */
639
- function addSkill(skillId) {
640
- if (!normalizedSkills.has(skillId) || disableSkillsSet.has(skillId) || detectedSkills.includes(skillId)) {
641
- return
642
- }
643
- detectedSkills.push(skillId)
644
- }
645
-
646
626
  const autoRuleChecks = [
647
627
  { enabled: isAbie, id: 'abie' },
648
628
  { enabled: packageJsonExists, id: 'bun' },
@@ -673,20 +653,8 @@ export async function detectAutoRulesAndSkills({
673
653
  }
674
654
  resolveRuleDependencies(detectedRules, addRule)
675
655
 
676
- const autoSkillChecks = [
677
- { enabled: isAbie, id: 'abie-kustomize' },
678
- { enabled: true, id: 'fix' },
679
- { enabled: true, id: 'lint' }
680
- ]
681
- for (const item of autoSkillChecks) {
682
- if (item.enabled) {
683
- addSkill(item.id)
684
- }
685
- }
686
-
687
656
  const rules = AUTO_RULE_ORDER.filter(ruleId => detectedRules.includes(ruleId))
688
- const skills = AUTO_SKILL_ORDER.filter(skillId => detectedSkills.includes(skillId))
689
- return { rules, skills }
657
+ return { rules }
690
658
  }
691
659
 
692
660
  /**
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Автовизначення skills для `.n-cursor.json` за умовами з `npm/bin/auto-skills.md`.
3
+ *
4
+ * Скіли автододаються залежно від уже виявлених правил (auto-rules) — щоб не дублювати
5
+ * умови, які вже формалізовані для відповідного правила. Наприклад:
6
+ *
7
+ * - `abie-kustomize - [abie]` — додається разом з правилом `abie`
8
+ * - `taze - [bun]` — додається разом з правилом `bun`
9
+ *
10
+ * Скіли без секції `[rules]` у `auto-skills.md` (`fix`, `lint`, `publish-telegram`)
11
+ * додаються завжди, якщо доступні в пакеті й не у `disable-skills`.
12
+ */
13
+
14
+ /** Порядок автододавання skills відповідно до `auto-skills.md`. */
15
+ export const AUTO_SKILL_ORDER = Object.freeze([
16
+ 'abie-kustomize',
17
+ 'fix',
18
+ 'lint',
19
+ 'publish-telegram',
20
+ 'taze'
21
+ ])
22
+
23
+ /**
24
+ * Залежність скілів від правил (`auto-skills.md` синтаксис `skill - [rules]`).
25
+ * Ключ варто автододати, коли всі правила-залежності вже додані до конфігу автодетектом.
26
+ */
27
+ export const AUTO_SKILL_RULE_DEPENDENCIES = Object.freeze(
28
+ /** @type {Record<string, readonly string[]>} */ ({
29
+ 'abie-kustomize': Object.freeze(['abie']),
30
+ taze: Object.freeze(['bun'])
31
+ })
32
+ )
33
+
34
+ /** Скіли без залежностей — додаються завжди (рядок «завжди» в `auto-skills.md`). */
35
+ const ALWAYS_ON_SKILLS = Object.freeze(['fix', 'lint', 'publish-telegram'])
36
+
37
+ const DEFAULT_DISABLED_LIST = Object.freeze([])
38
+
39
+ /**
40
+ * Визначає авто-skills згідно з `auto-skills.md`.
41
+ * @param {object} params параметри
42
+ * @param {string[]} params.availableSkills перелік доступних skills із пакету (id без префікса n-)
43
+ * @param {string[]} params.detectedRules id правил, виявлених auto-rules (вхідні залежності)
44
+ * @param {string[]} [params.disableSkills] список `disable-skills` з конфігу
45
+ * @returns {{ skills: string[] }} список id у стабільному порядку (за `AUTO_SKILL_ORDER`)
46
+ */
47
+ export function detectAutoSkills({ availableSkills, detectedRules, disableSkills = DEFAULT_DISABLED_LIST }) {
48
+ const normalizedSkills = new Set(availableSkills.map(s => s.trim().toLowerCase()))
49
+ const disableSkillsSet = new Set(disableSkills)
50
+ const detectedRulesSet = new Set(detectedRules)
51
+
52
+ /** @type {string[]} */
53
+ const detected = []
54
+
55
+ /**
56
+ * Додає skill до результату, якщо він доступний і не в disable-списку.
57
+ * @param {string} skillId id skill
58
+ * @returns {void}
59
+ */
60
+ function addSkill(skillId) {
61
+ if (!normalizedSkills.has(skillId) || disableSkillsSet.has(skillId) || detected.includes(skillId)) {
62
+ return
63
+ }
64
+ detected.push(skillId)
65
+ }
66
+
67
+ for (const skillId of ALWAYS_ON_SKILLS) {
68
+ addSkill(skillId)
69
+ }
70
+
71
+ for (const [skillId, deps] of Object.entries(AUTO_SKILL_RULE_DEPENDENCIES)) {
72
+ if (deps.every(d => detectedRulesSet.has(d))) {
73
+ addSkill(skillId)
74
+ }
75
+ }
76
+
77
+ return { skills: AUTO_SKILL_ORDER.filter(id => detected.includes(id)) }
78
+ }
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: n-taze
3
+ description: >-
4
+ Оновлення версій модулів проекту з аналізом major-змін і автоматичним
5
+ рефакторингом несумісного коду
6
+ ---
7
+
8
+ # n-taze — Оновлення версій проекту
9
+
10
+ ## Мета
11
+
12
+ Оновити всі модулі проекту до останніх версій, виявити major-оновлення, перевірити сумісність змін з кодом проекту і за потреби зрефакторити несумісні місця.
13
+
14
+ ## Передумови
15
+
16
+ - Чисте робоче дерево (`git status` без незакоммічених змін у `package.json` / `bun.lock` / `node_modules`) — інакше різницю не відрізнити від оновлення.
17
+ - Встановлений `bun` і доступний `bunx`.
18
+ - Запуск з кореня проекту (де лежить `package.json` / `bun.lock`).
19
+
20
+ ## Workflow
21
+
22
+ ### 1. Зафіксувати стартовий стан
23
+
24
+ Перед оновленням зберегти список поточних версій, щоб потім обчислити, які модулі стрибнули через major:
25
+
26
+ ```bash
27
+ cp package.json package.json.taze-bak
28
+ cp bun.lock bun.lock.taze-bak
29
+ ```
30
+
31
+ (У monorepo — також усі `*/package.json` воркспейсів. Файли тимчасові, видалити в кінці.)
32
+
33
+ ### 2. Запустити оновлення
34
+
35
+ ```bash
36
+ bunx taze -w -r latest
37
+ bun install
38
+ ```
39
+
40
+ - `-w` — записати нові версії у `package.json`.
41
+ - `-r` — рекурсивно по всіх воркспейсах.
42
+ - `latest` — піднімати навіть major.
43
+
44
+ ### 3. Виявити major-оновлення
45
+
46
+ Порівняти `package.json.taze-bak` з оновленим `package.json` (а також `bun.lock.taze-bak` ↔ `bun.lock` для транзитивних). Для кожної залежності, у якої змінилась перша значуща цифра semver (`1.x.x → 2.x.x`, `0.4.x → 0.5.x`, `0.0.x → 0.1.x`) — додати в список «потребує перевірки».
47
+
48
+ Minor/patch — пропускати, їх вважаємо сумісними.
49
+
50
+ ### 4. Зібрати breaking changes по кожному major-оновленню
51
+
52
+ Для кожного модуля зі списку зібрати фактичні відмінності одним з джерел (у порядку пріоритету):
53
+
54
+ 1. **CHANGELOG / Releases репозиторію модуля** — найшвидше і найточніше. Адресу репозиторію взяти з `package.json` модуля у `node_modules/<name>/package.json` (поле `repository`). Дістати релізи між старою і новою версією.
55
+ 2. **Git-різниця в `node_modules/<name>`** — якщо CHANGELOG відсутній або неінформативний, порівняти попередню версію (з кешу bun: `~/.bun/install/cache/<name>@<old-version>/`) з новою (`node_modules/<name>/`) через `diff -r` по `dist/` / `*.d.ts` / публічних entry-points з `exports`.
56
+ 3. **Якщо немає кешованої старої версії** — встановити її окремо в тимчасову теку (`mkdir -p /tmp/taze-old && cd /tmp/taze-old && bun add <name>@<old-version>`) і порівняти.
57
+
58
+ Цікавлять: видалені/перейменовані експорти, змінені сигнатури функцій, змінені типи, змінена поведінка за замовчуванням, видалені CLI-прапорці.
59
+
60
+ ### 5. Перевірити сумісність з кодом проекту
61
+
62
+ Для кожного breaking change знайти його використання в коді проекту:
63
+
64
+ ```bash
65
+ rg -n "<імпорт|функція|опція>" --type ts --type js --type vue
66
+ ```
67
+
68
+ Класифікувати:
69
+ - **сумісно** — проект не використовує зачеплене API → нічого не робити.
70
+ - **несумісно** — використання знайдено → перейти до п. 6.
71
+
72
+ ### 6. Рефакторинг несумісних місць
73
+
74
+ Для кожного несумісного місця — застосувати міграцію згідно з changelog модуля (перейменувати імпорт, оновити сигнатуру виклику, замінити видалену опцію еквівалентом тощо). Після правок:
75
+
76
+ ```bash
77
+ bun run lint
78
+ bun run typecheck # якщо є
79
+ bun test # якщо є
80
+ ```
81
+
82
+ Якщо міграція нетривіальна або неоднозначна — **не вгадувати**, залишити TODO у коді з посиланням на CHANGELOG і винести в підсумковий звіт як ручну дію.
83
+
84
+ ### 7. Прибрати тимчасові файли
85
+
86
+ ```bash
87
+ rm package.json.taze-bak bun.lock.taze-bak
88
+ ```
89
+
90
+ (І решту бекапів воркспейсів, якщо створювались.)
91
+
92
+ ### 8. Звіт користувачу
93
+
94
+ Коротко в одному повідомленні:
95
+
96
+ - **Оновлено (minor/patch):** кількість пакетів, без деталей.
97
+ - **Major-оновлення:** список `<name>: <old> → <new>` з посиланням на release notes.
98
+ - **Зрефакторено автоматично:** список файлів і коротко що саме змінено.
99
+ - **Потребує ручного втручання:** список TODO з причиною (нетривіальна міграція / неоднозначність / падіння тестів).
100
+ - **Стан перевірок:** `lint` / `typecheck` / `test` — pass/fail з номером рядка, де впало.
101
+
102
+ ## Примітка
103
+
104
+ - Не запускати `bun run lint` паралельно з іншими ESLint-задачами — діє правило з кореневого `CLAUDE.md`.
105
+ - Якщо проект — `npm/` пакет цього репо, після змін у `package.json` / коді треба підняти `version` і додати запис у `CHANGELOG.md` згідно з `npm/CLAUDE.md`.
106
+ - При великій кількості major-оновлень розбити PR по одному модулю на коміт — щоб `git bisect` залишався корисним.