@nitra/cursor 1.8.201 → 1.8.202

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,12 @@
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.202] - 2026-05-07
8
+
9
+ ### Added
10
+
11
+ - `bin/n-cursor.js`: новий хелпер `reexecIfPackageVersionChanged(effectivePackageRoot)` і його виклик у `runSync` одразу після `upgradeNitraCursorToLatestAndBunInstall`. Якщо self-upgrade встановив у `node_modules/@nitra/cursor` версію, відмінну від тієї, з якої стартував поточний процес (типово — npx-кеш), CLI спавнить `process.execPath <newBin> <args…>` через `spawnSync` (`stdio: 'inherit'`), додає в env `NITRA_CURSOR_REEXEC=1` і завершується з exit-кодом дочірнього процесу. Обґрунтування: ES-модулі (`RULE_MIGRATIONS`, `detectAutoRulesAndSkills`, списки правил) уже завантажені у V8 і нова логіка з-під свіжо встановленого пакета без re-exec невидима для поточного запуску — `import()` не вирішує цього, бо процес виконується з `bin/` у npx-кеші, а не з `node_modules/`. Захист від нескінченного циклу — раннє повернення при `process.env.NITRA_CURSOR_REEXEC === '1'`; додатково нічого не робить, якщо `effectivePackageRoot === BUNDLED_PACKAGE_ROOT` (реального апгрейду не сталося), якщо `version` не вдалося прочитати з обох `package.json`, або якщо у новому корені відсутній `bin/n-cursor.js`. `runChecks` свідомо не патчиться — він не виконує self-upgrade, тож версія процесу і пакета там завжди узгоджені. Імпорт `spawnSync` із `node:child_process` — єдина нова зовнішня залежність.
12
+
7
13
  ## [1.8.201] - 2026-05-07
8
14
 
9
15
  ### Changed
package/bin/n-cursor.js CHANGED
@@ -49,6 +49,7 @@
49
49
  * `node_modules/@nitra/cursor`, якщо пакет з’явився після встановлення.
50
50
  */
51
51
 
52
+ import { spawnSync } from 'node:child_process'
52
53
  import { existsSync } from 'node:fs'
53
54
  import { mkdir, readdir, readFile, rename, rm, unlink, writeFile } from 'node:fs/promises'
54
55
  import { basename, dirname, join } from 'node:path'
@@ -1122,6 +1123,46 @@ async function readBundledVersionAt(packageRoot) {
1122
1123
  }
1123
1124
  }
1124
1125
 
1126
+ /**
1127
+ * Якщо `upgradeNitraCursorToLatestAndBunInstall` встановив у `node_modules/@nitra/cursor` версію,
1128
+ * відмінну від тієї, з якої стартував поточний процес (наприклад, з npx-кешу), запускає бінар нової
1129
+ * версії через `spawnSync` і завершує поточний процес із успадкованим exit-кодом. Re-exec потрібен,
1130
+ * бо ES-модулі вже завантажені у V8 (RULE_MIGRATIONS, detectAutoRulesAndSkills тощо) і нова логіка
1131
+ * без повної заміни процесу не підхопиться. Захист від нескінченного циклу — env `NITRA_CURSOR_REEXEC=1`.
1132
+ * @param {string} effectivePackageRoot шлях, повернутий `upgradeNitraCursorToLatestAndBunInstall`
1133
+ * @returns {Promise<void>} повертається лише якщо re-exec не потрібен (інакше викликає `process.exit`)
1134
+ */
1135
+ async function reexecIfPackageVersionChanged(effectivePackageRoot) {
1136
+ if (process.env.NITRA_CURSOR_REEXEC === '1') {
1137
+ return
1138
+ }
1139
+ if (effectivePackageRoot === BUNDLED_PACKAGE_ROOT) {
1140
+ return
1141
+ }
1142
+ const currentVersion = await readBundledVersionAt(BUNDLED_PACKAGE_ROOT)
1143
+ const installedVersion = await readBundledVersionAt(effectivePackageRoot)
1144
+ if (!currentVersion || !installedVersion || currentVersion === installedVersion) {
1145
+ return
1146
+ }
1147
+ const newBinPath = join(effectivePackageRoot, 'bin', 'n-cursor.js')
1148
+ if (!existsSync(newBinPath)) {
1149
+ return
1150
+ }
1151
+ console.log(
1152
+ `🔁 Перезапуск ${PACKAGE_NAME}: процес стартував на ${currentVersion}, ` +
1153
+ `після self-upgrade встановлено ${installedVersion}.\n` +
1154
+ ` Re-exec свіжого бінаря, щоб підхопити нову логіку (RULE_MIGRATIONS, auto-detect тощо).\n`
1155
+ )
1156
+ const result = spawnSync(process.execPath, [newBinPath, ...process.argv.slice(2)], {
1157
+ stdio: 'inherit',
1158
+ env: { ...process.env, NITRA_CURSOR_REEXEC: '1' }
1159
+ })
1160
+ if (result.error) {
1161
+ throw result.error
1162
+ }
1163
+ process.exit(typeof result.status === 'number' ? result.status : 1)
1164
+ }
1165
+
1125
1166
  /**
1126
1167
  * Копіює правила з каталогу `mdc/` установленого пакету та синхронізує `.cursor/rules`
1127
1168
  * @returns {Promise<void>}
@@ -1134,6 +1175,8 @@ async function runSync() {
1134
1175
  upgradeNitraCursorToLatestAndBunInstall(projectRoot, BUNDLED_PACKAGE_ROOT)
1135
1176
  )
1136
1177
 
1178
+ await reexecIfPackageVersionChanged(effectivePackageRoot)
1179
+
1137
1180
  const bundledMdcDir = join(effectivePackageRoot, 'mdc')
1138
1181
  const bundledSkillsDir = join(effectivePackageRoot, 'skills')
1139
1182
  const bundledAgentsTemplatePath = join(effectivePackageRoot, AGENTS_TEMPLATE_FILE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.201",
3
+ "version": "1.8.202",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",