@nitra/cursor 3.21.0 → 3.21.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.21.1] - 2026-06-04
4
+
5
+ ### Changed
6
+
7
+ - CLI sync: ховати блоки правил / 🧩 Skills / ⌨️ Commands / 🥧 Pi skills (рядки ⬇ і підсумок) за успішного прогону — друкувати лише коли fail > 0 (helper captureOutput)
8
+ - CLI sync: ховати post-sync рядки (📝 setup-bun-deps action / 📝 AGENTS.md / 📝 CLAUDE.md / 🤖 Claude-конфіг) за успішного прогону — через captureOutput, друк лише при помилці
9
+
3
10
  ## [3.21.0] - 2026-06-04
4
11
 
5
12
  ### Added
package/bin/n-cursor.js CHANGED
@@ -1132,6 +1132,48 @@ async function runSyncStep(prefix, action) {
1132
1132
  }
1133
1133
  }
1134
1134
 
1135
+ /**
1136
+ * Виконує `action`, буферизуючи весь його stdout/console-вивід.
1137
+ *
1138
+ * Мотивація: за успішного прогону sync-блоку рядки `⬇ … ✅` і підсумок
1139
+ * (`🧩 Skills: N скопійовано, 0 з помилками`) не несуть користі й лише
1140
+ * захаращують термінал. Тому буфер скидається в реальний stdout **лише**
1141
+ * коли крок повернув `fail > 0` (або кинув виняток); за `fail === 0` —
1142
+ * відкидається мовчки.
1143
+ *
1144
+ * @template T
1145
+ * @param {() => Promise<T>} action крок синку, що повертає обʼєкт із лічильником помилок `fail`
1146
+ * @returns {Promise<T>} результат `action` без змін
1147
+ */
1148
+ async function captureOutput(action) {
1149
+ const buffer = []
1150
+ const realStdoutWrite = process.stdout.write
1151
+ const realLog = console.log
1152
+ const realError = console.error
1153
+ const flush = () => realStdoutWrite.call(process.stdout, buffer.join(''))
1154
+ process.stdout.write = (...args) => {
1155
+ const [chunk] = args
1156
+ buffer.push(typeof chunk === 'string' ? chunk : chunk.toString())
1157
+ const cb = args.find((arg) => typeof arg === 'function')
1158
+ if (cb) cb()
1159
+ return true
1160
+ }
1161
+ console.log = (...args) => buffer.push(`${args.map(String).join(' ')}\n`)
1162
+ console.error = (...args) => buffer.push(`${args.map(String).join(' ')}\n`)
1163
+ try {
1164
+ const result = await action()
1165
+ if (result?.fail > 0) flush()
1166
+ return result
1167
+ } catch (error) {
1168
+ flush()
1169
+ throw error
1170
+ } finally {
1171
+ process.stdout.write = realStdoutWrite
1172
+ console.log = realLog
1173
+ console.error = realError
1174
+ }
1175
+ }
1176
+
1135
1177
  /**
1136
1178
  * Копіює керовані `.mdc` файли з пакету до `.cursor/rules`.
1137
1179
  * @param {string[]} rules список rules з конфігу
@@ -1357,14 +1399,19 @@ async function runSync() {
1357
1399
  console.log(`📋 Правил до завантаження: ${rules.length}`)
1358
1400
  console.log(`📋 Skills до синхронізації: ${skills.length}`)
1359
1401
 
1360
- await runSyncStep('❌ Не вдалося записати setup-bun-deps action: ', async () => {
1361
- const { destPath } = await syncSetupBunDepsAction(cwd(), effectivePackageRoot)
1362
- console.log(`📝 Оновлено ${destPath} (composite setup-bun-deps з пакету)\n`)
1363
- })
1402
+ await runSyncStep('❌ Не вдалося записати setup-bun-deps action: ', () =>
1403
+ captureOutput(async () => {
1404
+ const { destPath } = await syncSetupBunDepsAction(cwd(), effectivePackageRoot)
1405
+ console.log(`📝 Оновлено ${destPath} (composite setup-bun-deps з пакету)\n`)
1406
+ })
1407
+ )
1364
1408
 
1365
1409
  const rulesDir = join(cwd(), RULES_DIR)
1366
1410
  await mkdir(rulesDir, { recursive: true })
1367
- const { successCount, failCount } = await syncManagedRuleFiles(rules, bundledRulesDir, rulesDir)
1411
+ const { successCount, failCount } = await captureOutput(async () => {
1412
+ const stats = await syncManagedRuleFiles(rules, bundledRulesDir, rulesDir)
1413
+ return { ...stats, fail: stats.failCount }
1414
+ })
1368
1415
 
1369
1416
  await runSyncStep(`❌ Не вдалося прибрати зайві файли в ${RULES_DIR}: `, async () => {
1370
1417
  const removed = await removeOrphanManagedRuleFiles(rulesDir, rules)
@@ -1372,10 +1419,13 @@ async function runSync() {
1372
1419
  })
1373
1420
 
1374
1421
  await runSyncStep('❌ Skills: ', async () => {
1375
- const { success: skillOk, fail: skillFail } = await syncSkills(skills, bundledSkillsDir)
1376
- if (skills.length > 0) {
1377
- console.log(`\n🧩 Skills: ${skillOk} скопійовано, ${skillFail} з помилками`)
1378
- }
1422
+ const { fail: skillFail } = await captureOutput(async () => {
1423
+ const { success: skillOk, fail } = await syncSkills(skills, bundledSkillsDir)
1424
+ if (skills.length > 0) {
1425
+ console.log(`\n🧩 Skills: ${skillOk} скопійовано, ${fail} з помилками`)
1426
+ }
1427
+ return { fail }
1428
+ })
1379
1429
  const removedSkills = await removeOrphanManagedSkillDirs(join(cwd(), SKILLS_DIR), skills)
1380
1430
  logRemovedManagedItems('skills', SKILLS_DIR, removedSkills)
1381
1431
  if (skillFail > 0) {
@@ -1384,13 +1434,16 @@ async function runSync() {
1384
1434
  })
1385
1435
 
1386
1436
  await runSyncStep('❌ Commands: ', async () => {
1387
- const { success: cmdOk, fail: cmdFail } = await syncCommands(skills, bundledSkillsDir)
1388
- const { success: localOk, fail: localFail } = await syncLocalOnlySkillCommands(skills)
1389
- const totalOk = cmdOk + localOk
1390
- const totalFail = cmdFail + localFail
1391
- if (totalOk + totalFail > 0) {
1392
- console.log(`\n⌨️ Commands: ${totalOk} скопійовано, ${totalFail} з помилками`)
1393
- }
1437
+ const { fail: totalFail } = await captureOutput(async () => {
1438
+ const { success: cmdOk, fail: cmdFail } = await syncCommands(skills, bundledSkillsDir)
1439
+ const { success: localOk, fail: localFail } = await syncLocalOnlySkillCommands(skills)
1440
+ const totalOk = cmdOk + localOk
1441
+ const fail = cmdFail + localFail
1442
+ if (totalOk + fail > 0) {
1443
+ console.log(`\n⌨️ Commands: ${totalOk} скопійовано, ${fail} з помилками`)
1444
+ }
1445
+ return { fail }
1446
+ })
1394
1447
  const commandsDir = join(cwd(), COMMANDS_DIR)
1395
1448
  const removedCmds = await removeOrphanManagedCommandFiles(commandsDir, skills)
1396
1449
  logRemovedManagedItems('commands', COMMANDS_DIR, removedCmds)
@@ -1402,13 +1455,16 @@ async function runSync() {
1402
1455
  })
1403
1456
 
1404
1457
  await runSyncStep('❌ Pi skills: ', async () => {
1405
- const { success: piOk, fail: piFail } = await syncPiSkills(skills, bundledSkillsDir)
1406
- const { success: piLocalOk, fail: piLocalFail } = await syncLocalOnlyPiSkills(skills)
1407
- const totalOk = piOk + piLocalOk
1408
- const totalFail = piFail + piLocalFail
1409
- if (totalOk + totalFail > 0) {
1410
- console.log(`\n🥧 Pi skills: ${totalOk} скопійовано, ${totalFail} з помилками`)
1411
- }
1458
+ const { fail: totalFail } = await captureOutput(async () => {
1459
+ const { success: piOk, fail: piFail } = await syncPiSkills(skills, bundledSkillsDir)
1460
+ const { success: piLocalOk, fail: piLocalFail } = await syncLocalOnlyPiSkills(skills)
1461
+ const totalOk = piOk + piLocalOk
1462
+ const fail = piFail + piLocalFail
1463
+ if (totalOk + fail > 0) {
1464
+ console.log(`\n🥧 Pi skills: ${totalOk} скопійовано, ${fail} з помилками`)
1465
+ }
1466
+ return { fail }
1467
+ })
1412
1468
  const piSkillsDir = join(cwd(), PI_SKILLS_DIR)
1413
1469
  const removedPi = await removeOrphanManagedPiSkillDirs(piSkillsDir, skills)
1414
1470
  logRemovedManagedItems('pi skills', PI_SKILLS_DIR, removedPi)
@@ -1419,39 +1475,43 @@ async function runSync() {
1419
1475
  }
1420
1476
  })
1421
1477
 
1422
- await runSyncStep(`❌ Не вдалося оновити ${AGENTS_FILE}: `, () => syncAgentsMd(bundledAgentsTemplatePath))
1478
+ await runSyncStep(`❌ Не вдалося оновити ${AGENTS_FILE}: `, () =>
1479
+ captureOutput(() => syncAgentsMd(bundledAgentsTemplatePath))
1480
+ )
1423
1481
  await runSyncStep('❌ Не вдалося оновити CLAUDE.md: ', () =>
1424
- syncClaudeMd(/** @type {string[] | undefined} */ (ignore))
1482
+ captureOutput(() => syncClaudeMd(/** @type {string[] | undefined} */ (ignore)))
1425
1483
  )
1426
1484
 
1427
- await runSyncStep('❌ Не вдалося синхронізувати Claude-конфіг: ', async () => {
1428
- const result = await syncClaudeConfig({
1429
- projectRoot: cwd(),
1430
- bundledPackageRoot: effectivePackageRoot,
1431
- enabled: claudeConfigEnabled,
1432
- rules
1433
- })
1434
- if (!claudeConfigEnabled) {
1435
- console.log('🤖 Claude-конфіг: пропущено (claude-config: false у .n-cursor.json)')
1436
- return
1437
- }
1438
- const parts = []
1439
- if (result.settings) parts.push('.claude/settings.json')
1440
- if (result.cursorHooks) parts.push('.cursor/hooks.json')
1441
- if (result.commands.length > 0) parts.push(`${result.commands.length} slash-commands`)
1442
- if (result.adrHook) parts.push('.claude/hooks/capture-decisions.sh')
1443
- if (result.adrNormalizeHook) parts.push('.claude/hooks/normalize-decisions.sh')
1444
- if (result.adrHookLib?.length > 0) {
1445
- for (const libPath of result.adrHookLib) {
1446
- parts.push(libPath)
1485
+ await runSyncStep('❌ Не вдалося синхронізувати Claude-конфіг: ', () =>
1486
+ captureOutput(async () => {
1487
+ const result = await syncClaudeConfig({
1488
+ projectRoot: cwd(),
1489
+ bundledPackageRoot: effectivePackageRoot,
1490
+ enabled: claudeConfigEnabled,
1491
+ rules
1492
+ })
1493
+ if (!claudeConfigEnabled) {
1494
+ console.log('🤖 Claude-конфіг: пропущено (claude-config: false у .n-cursor.json)')
1495
+ return
1447
1496
  }
1448
- }
1449
- if (result.gitignoreAdr) parts.push('.gitignore (adr fragment)')
1450
- if (result.piExtension) parts.push('.pi/extensions/n-cursor-adr/')
1451
- if (parts.length > 0) {
1452
- console.log(`🤖 Claude-конфіг: ${parts.join(', ')}`)
1453
- }
1454
- })
1497
+ const parts = []
1498
+ if (result.settings) parts.push('.claude/settings.json')
1499
+ if (result.cursorHooks) parts.push('.cursor/hooks.json')
1500
+ if (result.commands.length > 0) parts.push(`${result.commands.length} slash-commands`)
1501
+ if (result.adrHook) parts.push('.claude/hooks/capture-decisions.sh')
1502
+ if (result.adrNormalizeHook) parts.push('.claude/hooks/normalize-decisions.sh')
1503
+ if (result.adrHookLib?.length > 0) {
1504
+ for (const libPath of result.adrHookLib) {
1505
+ parts.push(libPath)
1506
+ }
1507
+ }
1508
+ if (result.gitignoreAdr) parts.push('.gitignore (adr fragment)')
1509
+ if (result.piExtension) parts.push('.pi/extensions/n-cursor-adr/')
1510
+ if (parts.length > 0) {
1511
+ console.log(`🤖 Claude-конфіг: ${parts.join(', ')}`)
1512
+ }
1513
+ })
1514
+ )
1455
1515
 
1456
1516
  await runSyncStep('❌ Не вдалося оновити .gitignore (worktree): ', async () => {
1457
1517
  const { written } = await syncGitignoreWorktree(cwd())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "3.21.0",
3
+ "version": "3.21.1",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",