@nitra/cursor 11.3.0 → 11.4.0

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [11.4.0] - 2026-06-15
4
+
5
+ ### Changed
6
+
7
+ - Додано логіку відсіювання неактуальних правил/скілів за `availableRules`/`availableSkills
8
+
3
9
  ## [11.3.0] - 2026-06-15
4
10
 
5
11
  ### Changed
@@ -173,7 +173,7 @@
173
173
  #### Вкладена normalizeConfigWithAutoRules(parsedConfig)
174
174
 
175
175
  - **Сигнатура:** `async function normalizeConfigWithAutoRules(parsedConfig: Record<string, unknown>): Promise<Record<string, unknown>>`
176
- - **Призначення:** перевіряє типи полів, обчислює `auto-detected rules` (`detectAutoRules`), будує ефективний список правил (поточні + auto, мінус `disable-rules`), за яким `detectAutoSkills` визначає скіли. Далі `mergeConfigWithAutoDetected` зливає дані, після чого `$schema` вирівнюється до `CONFIG_SCHEMA_URL`, додаються `disable-rules`/`disable-skills` (якщо непорожні), результат проходить через `sortConfigIdArrays`.
176
+ - **Призначення:** перевіряє типи полів, обчислює `auto-detected rules` (`detectAutoRules`), будує ефективний список правил (поточні + auto, мінус `disable-rules`), за яким `detectAutoSkills` визначає скіли. Далі `mergeConfigWithAutoDetected` зливає дані (передаючи `availableRules`/`availableSkills` із каталогів пакета, щоб відсіяти з `rules`/`skills` неактуальні id, яких уже немає у пакеті — прибрані логуються через `🧹`), після чого `$schema` вирівнюється до `CONFIG_SCHEMA_URL`, додаються `disable-rules`/`disable-skills` (якщо непорожні), результат проходить через `sortConfigIdArrays`.
177
177
 
178
178
  ### logRuleMigrationsIfAny(parsedConfig)
179
179
 
package/bin/n-cursor.js CHANGED
@@ -25,7 +25,7 @@
25
25
  * `npx \@nitra/cursor lint-text` — канонічний lint-text (text.mdc): `cspell` → `shellcheck` (з auto-fix) →
26
26
  * `markdownlint-cli2 --fix` → `v8r` (json/json5/yaml/yml/toml)
27
27
  * `npx \@nitra/cursor lint-doc-files` — детермінований детектор застарілості файлових док (`stale`: `missing`|`crc-mismatch`); правило doc-files (ignore-glob у `npm/rules/doc-files/js/docgen-ignore.mjs`; тека `docs/` поряд із джерелом). Режими: повний (exit 1), `--json` (exit 0), `--missing-only`, `--hook`/`--git` (hook-протокол, exit 2), `--degraded`
28
- * `npx \@nitra/cursor fix-doc-files` — JS-оркестрована генерація файлових док (роутинг local/cloud) зі штампом CRC (`--limit`/`--from`/`--overwrite`/`--retry-degraded`); `--stamp` — детерміноване перештампування CRC без LLM
28
+ * `npx \@nitra/cursor fix-doc-files` — JS-оркестрована генерація файлових док (роутинг local/cloud) зі штампом CRC (`--limit`/`--from`/`--overwrite`); `--stamp` — детерміноване перештампування CRC без LLM
29
29
  * `npx \@nitra/cursor doc-aggregate modules` — JSON-лістинг логічних модулів (межі за `package.json`) для Tier 2 скілу doc-aggregate
30
30
  * `npx \@nitra/cursor skill list` — скіли пакета без синку в проєкт
31
31
  * `npx \@nitra/cursor skill taze` — промпт на stdout
@@ -315,9 +315,18 @@ async function readConfig(paths = {}) {
315
315
  const merged = mergeConfigWithAutoDetected({
316
316
  config: parsedConfig,
317
317
  detectedRules: autoDetectedRules.rules,
318
- detectedSkills: autoDetectedSkills.skills
318
+ detectedSkills: autoDetectedSkills.skills,
319
+ availableRules,
320
+ availableSkills
319
321
  })
320
322
 
323
+ if (merged.pruned) {
324
+ const parts = []
325
+ if (merged.pruned.rules.length > 0) parts.push(`rules: ${merged.pruned.rules.join(', ')}`)
326
+ if (merged.pruned.skills.length > 0) parts.push(`skills: ${merged.pruned.skills.join(', ')}`)
327
+ console.log(`🧹 Прибрано з ${CONFIG_FILE} неактуальні (немає у пакеті) — ${parts.join('; ')}\n`)
328
+ }
329
+
321
330
  const rest = Object.fromEntries(Object.entries(parsedConfig).filter(([k]) => k !== '$schema'))
322
331
  const normalized = {
323
332
  $schema: CONFIG_SCHEMA_URL,
@@ -1650,7 +1659,7 @@ try {
1650
1659
  }
1651
1660
  case 'fix-doc-files': {
1652
1661
  // n-cursor fix-doc-files — local-only генерація файлових док (omlx) + CRC-штамп
1653
- // (--limit/--from/--overwrite/--retry-degraded); --stamp — детерміноване
1662
+ // (--limit/--from/--overwrite); --stamp — детерміноване
1654
1663
  // перештампування source+crc без LLM. У CI не запускається (потрібна локальна модель).
1655
1664
  if (args.includes('--stamp')) {
1656
1665
  const { runDocFilesStampCli } = await import('../rules/doc-files/js/docgen-files-batch.mjs')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "11.3.0",
3
+ "version": "11.4.0",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -392,14 +392,45 @@ export async function detectAutoRules({
392
392
  }
393
393
 
394
394
  /**
395
- * Доповнює конфіг автодетектом (лише додає; існуючі вручну задані елементи не прибирає).
395
+ * Розділяє список id на доступні в пакеті й застарілі (відсутні).
396
+ * Без `available` нічого не прибирає — усе вважається доступним.
397
+ * @param {string[]} ids перелік id (rules або skills)
398
+ * @param {string[] | undefined} available id, що реально є у каталозі пакета
399
+ * @returns {{ kept: string[], pruned: string[] }} відфільтровані й прибрані id
400
+ */
401
+ function partitionByAvailability(ids, available) {
402
+ if (!available) return { kept: ids, pruned: [] }
403
+ const availableSet = new Set(available)
404
+ const kept = []
405
+ const pruned = []
406
+ for (const id of ids) {
407
+ if (availableSet.has(id)) kept.push(id)
408
+ else pruned.push(id)
409
+ }
410
+ return { kept, pruned }
411
+ }
412
+
413
+ /**
414
+ * Доповнює конфіг автодетектом (лише додає; існуючі вручну задані елементи не прибирає),
415
+ * а за наявності `availableRules`/`availableSkills` ще й прибирає з `rules`/`skills`
416
+ * неактуальні id, яких уже немає у пакеті (наприклад, правило чи скіл видалено з нової
417
+ * версії \@nitra/cursor) — інакше sync щоразу падав би на завантаженні відсутнього
418
+ * `rules/<id>.mdc` чи `skills/<id>/`. Прибрані id повертаються у полі `pruned` (для логу).
396
419
  * @param {object} params параметри оновлення
397
420
  * @param {{ rules: unknown, skills?: unknown, ['disable-rules']?: unknown, ['disable-skills']?: unknown }} params.config розпарсений `.n-cursor.json`
398
421
  * @param {string[]} params.detectedRules правила, визначені автодетектом
399
422
  * @param {string[]} params.detectedSkills skills, визначені автодетектом
400
- * @returns {{ rules: string[], skills: string[] } & Record<string, unknown>} новий нормалізований конфіг
423
+ * @param {string[]} [params.availableRules] id правил, наявних у каталозі `rules/` пакета (для відсіву неактуальних)
424
+ * @param {string[]} [params.availableSkills] id skills, наявних у каталозі `skills/` пакета (для відсіву неактуальних)
425
+ * @returns {{ rules: string[], skills: string[], pruned?: { rules: string[], skills: string[] } } & Record<string, unknown>} новий нормалізований конфіг
401
426
  */
402
- export function mergeConfigWithAutoDetected({ config, detectedRules, detectedSkills }) {
427
+ export function mergeConfigWithAutoDetected({
428
+ config,
429
+ detectedRules,
430
+ detectedSkills,
431
+ availableRules,
432
+ availableSkills
433
+ }) {
403
434
  const existingRules = migrateRuleIds(normalizeIdList(config.rules))
404
435
  const existingSkills = normalizeIdList(config.skills)
405
436
  const disableRules = migrateRuleIds(normalizeIdList(config['disable-rules']))
@@ -419,13 +450,19 @@ export function mergeConfigWithAutoDetected({ config, detectedRules, detectedSki
419
450
  }
420
451
  }
421
452
 
422
- /** @type {{ rules: string[], skills: string[] } & Record<string, unknown>} */
423
- const normalized = { rules, skills }
453
+ const { kept: keptRules, pruned: prunedRules } = partitionByAvailability(rules, availableRules)
454
+ const { kept: keptSkills, pruned: prunedSkills } = partitionByAvailability(skills, availableSkills)
455
+
456
+ /** @type {{ rules: string[], skills: string[], pruned?: { rules: string[], skills: string[] } } & Record<string, unknown>} */
457
+ const normalized = { rules: keptRules, skills: keptSkills }
424
458
  if (disableRules.length > 0) {
425
459
  normalized['disable-rules'] = disableRules
426
460
  }
427
461
  if (disableSkills.length > 0) {
428
462
  normalized['disable-skills'] = disableSkills
429
463
  }
464
+ if (prunedRules.length > 0 || prunedSkills.length > 0) {
465
+ normalized.pruned = { rules: prunedRules, skills: prunedSkills }
466
+ }
430
467
  return normalized
431
468
  }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  docgen:
3
3
  source: npm/scripts/auto-rules.mjs
4
- crc: 972b56fc
4
+ crc: d50b922f
5
5
  score: 90
6
6
  ---
7
7
 
@@ -29,7 +29,7 @@ detectAutoRules
29
29
  Визначає активні правила на основі spec, перевіряючи їх проти згенерованих фактів.
30
30
 
31
31
  mergeConfigWithAutoDetected
32
- Доповнює конфігурацію, додаючи визначені автоправила та налаштування, з урахуванням legacy-ID.
32
+ Доповнює конфігурацію, додаючи визначені автоправила та налаштування, з урахуванням legacy-ID; за наявності `availableRules`/`availableSkills` ще й відсіює з `rules`/`skills` неактуальні id, яких немає у пакеті (повертає їх у полі `pruned`).
33
33
 
34
34
  ## Публічний API
35
35