@nitra/cursor 1.8.38 → 1.8.41
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/README.md +2 -1
- package/bin/n-cursor.js +23 -37
- package/package.json +1 -1
- package/schemas/n-cursor.json +1 -1
- package/skills/abie-kustomize/SKILL.md +26 -0
- package/skills/n-mdc-check/SKILL.md +0 -86
- /package/skills/{n-fix → fix}/SKILL.md +0 -0
- /package/skills/{n-publish-telegram → publish-telegram}/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -85,10 +85,11 @@ CLI автоматично (команда завантаження правил
|
|
|
85
85
|
```
|
|
86
86
|
npm/
|
|
87
87
|
├── AGENTS.template.md # шаблон AGENTS.md для цільових репозиторіїв (потрапляє в npm-архів)
|
|
88
|
-
├── mdc/ # cursor-правила
|
|
88
|
+
├── mdc/ # cursor-правила (без префікса n-; після синку — .cursor/rules/n-<id>.mdc)
|
|
89
89
|
│ ├── js-format.mdc
|
|
90
90
|
│ ├── npm-module.mdc
|
|
91
91
|
│ └── text.mdc
|
|
92
|
+
├── skills/ # skills (каталоги <id>/; після синку — .cursor/skills/n-<id>/)
|
|
92
93
|
└── bin/
|
|
93
94
|
└── n-cursor.js # CLI-скрипт
|
|
94
95
|
```
|
package/bin/n-cursor.js
CHANGED
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
* `github-actions/` пакету при кожному успішному синку (workflows з правил ga / js-lint / text).
|
|
25
25
|
*
|
|
26
26
|
* Skills копіюються з npm/skills пакету лише для id з масиву «skills» у .n-cursor.json
|
|
27
|
-
* (у JSON — без префікса,
|
|
28
|
-
*
|
|
27
|
+
* (у JSON — без префікса, як імена файлів у mdc/ без n-). У пакеті джерело — каталоги
|
|
28
|
+
* skills/<id>/ (без префікса); у проєкті — .cursor/skills/n-<id>/ (префікс n-, як n-*.mdc).
|
|
29
|
+
* Якщо ключа skills немає, за замовчуванням підтягуються всі підкаталоги skills/ (лише імена без префікса n-).
|
|
29
30
|
* Зайві каталоги n-* у .cursor/skills, яких немає у списку, видаляються.
|
|
30
31
|
*
|
|
31
32
|
* Якщо в корені є package.json і в ньому ще немає \@nitra/cursor у devDependencies (і не оголошено
|
|
@@ -87,7 +88,7 @@ async function discoverBundledRuleNames() {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
|
-
* Імена skills (без префікса n-) з каталогу skills пакету — лише
|
|
91
|
+
* Імена skills (id без префікса n-) з каталогу skills пакету — лише підкаталоги `<id>/` без префікса n-
|
|
91
92
|
* @returns {Promise<string[]>} відсортовані id
|
|
92
93
|
*/
|
|
93
94
|
async function discoverBundledSkillNames() {
|
|
@@ -96,8 +97,8 @@ async function discoverBundledSkillNames() {
|
|
|
96
97
|
}
|
|
97
98
|
const entries = await readdir(BUNDLED_SKILLS_DIR, { withFileTypes: true })
|
|
98
99
|
return entries
|
|
99
|
-
.filter(e => e.isDirectory() && e.name.startsWith(RULE_PREFIX))
|
|
100
|
-
.map(e => e.name
|
|
100
|
+
.filter(e => e.isDirectory() && !e.name.startsWith('.') && !e.name.startsWith(RULE_PREFIX))
|
|
101
|
+
.map(e => e.name)
|
|
101
102
|
.toSorted((a, b) => a.localeCompare(b))
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -233,28 +234,13 @@ function normalizeSkillId(skillName) {
|
|
|
233
234
|
return s
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
/** Legacy id у `.n-cursor.json` → поточний bundled id (каталог `n-<id>` у пакеті) */
|
|
237
|
-
const LEGACY_SKILL_ID_MAP = {
|
|
238
|
-
'fix-cursor': 'fix'
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Поточний id skill для шляхів у пакеті та `.cursor/skills`
|
|
243
|
-
* @param {string} skillName елемент масиву skills або ім'я каталогу
|
|
244
|
-
* @returns {string} canonical id без префікса n-
|
|
245
|
-
*/
|
|
246
|
-
function canonicalSkillId(skillName) {
|
|
247
|
-
const id = normalizeSkillId(skillName)
|
|
248
|
-
return LEGACY_SKILL_ID_MAP[id] ?? id
|
|
249
|
-
}
|
|
250
|
-
|
|
251
237
|
/**
|
|
252
238
|
* Ім'я керованого каталогу skill у .cursor/skills (префікс n-)
|
|
253
|
-
* @param {string} skillId id без префікса
|
|
239
|
+
* @param {string} skillId id без префікса (або з префіксом n- у конфігу — нормалізується)
|
|
254
240
|
* @returns {string} наприклад n-fix
|
|
255
241
|
*/
|
|
256
242
|
function managedSkillDirName(skillId) {
|
|
257
|
-
return `${RULE_PREFIX}${
|
|
243
|
+
return `${RULE_PREFIX}${normalizeSkillId(skillId)}`
|
|
258
244
|
}
|
|
259
245
|
|
|
260
246
|
/**
|
|
@@ -445,7 +431,7 @@ async function syncClaudeMd(configRules, configSkills) {
|
|
|
445
431
|
lines.push('', '## Skills', '')
|
|
446
432
|
const skillsRoot = join(cwd(), SKILLS_DIR)
|
|
447
433
|
for (const skillId of configSkills) {
|
|
448
|
-
const id =
|
|
434
|
+
const id = normalizeSkillId(skillId)
|
|
449
435
|
const dirName = managedSkillDirName(skillId)
|
|
450
436
|
const skillMdPath = join(skillsRoot, dirName, 'SKILL.md')
|
|
451
437
|
let desc = ''
|
|
@@ -495,7 +481,7 @@ async function syncAgentsMd(configSkills) {
|
|
|
495
481
|
}
|
|
496
482
|
|
|
497
483
|
/**
|
|
498
|
-
* Копіює лише skills зі списку configSkills (
|
|
484
|
+
* Копіює лише skills зі списку configSkills (джерело: skills/<id>/ у пакеті)
|
|
499
485
|
* @param {string[]} configSkills id без префікса n-
|
|
500
486
|
* @returns {Promise<{ success: number, fail: number }>} лічильники успішних і невдалих копіювань
|
|
501
487
|
*/
|
|
@@ -511,13 +497,13 @@ async function syncSkills(configSkills) {
|
|
|
511
497
|
let fail = 0
|
|
512
498
|
|
|
513
499
|
for (const skillId of configSkills) {
|
|
514
|
-
const id =
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
const destDir = join(skillsRoot,
|
|
500
|
+
const id = normalizeSkillId(skillId)
|
|
501
|
+
const srcDir = join(BUNDLED_SKILLS_DIR, id)
|
|
502
|
+
const destDirName = managedSkillDirName(skillId)
|
|
503
|
+
const destDir = join(skillsRoot, destDirName)
|
|
518
504
|
|
|
519
505
|
if (existsSync(srcDir)) {
|
|
520
|
-
process.stdout.write(` ⬇ ${id} → ${SKILLS_DIR}/${
|
|
506
|
+
process.stdout.write(` ⬇ ${id} → ${SKILLS_DIR}/${destDirName} ... `)
|
|
521
507
|
try {
|
|
522
508
|
await mkdir(destDir, { recursive: true })
|
|
523
509
|
const files = await readdir(srcDir)
|
|
@@ -533,9 +519,9 @@ async function syncSkills(configSkills) {
|
|
|
533
519
|
fail++
|
|
534
520
|
}
|
|
535
521
|
} else {
|
|
536
|
-
process.stdout.write(` ⬇ ${id} → ${SKILLS_DIR}/${
|
|
522
|
+
process.stdout.write(` ⬇ ${id} → ${SKILLS_DIR}/${destDirName} ... `)
|
|
537
523
|
console.log(`❌`)
|
|
538
|
-
console.error(` Немає каталогу в пакеті:
|
|
524
|
+
console.error(` Немає каталогу в пакеті: skills/${id}`)
|
|
539
525
|
fail++
|
|
540
526
|
}
|
|
541
527
|
}
|
|
@@ -560,9 +546,9 @@ async function syncCommands(configSkills) {
|
|
|
560
546
|
let fail = 0
|
|
561
547
|
|
|
562
548
|
for (const skillId of configSkills) {
|
|
563
|
-
const id =
|
|
564
|
-
const
|
|
565
|
-
const
|
|
549
|
+
const id = normalizeSkillId(skillId)
|
|
550
|
+
const srcSkillMd = join(BUNDLED_SKILLS_DIR, id, 'SKILL.md')
|
|
551
|
+
const destDirName = managedSkillDirName(skillId)
|
|
566
552
|
const destFile = join(commandsDir, `${RULE_PREFIX}${id}.md`)
|
|
567
553
|
|
|
568
554
|
process.stdout.write(` ⬇ ${id} → ${COMMANDS_DIR}/${RULE_PREFIX}${id}.md ... `)
|
|
@@ -572,7 +558,7 @@ async function syncCommands(configSkills) {
|
|
|
572
558
|
const descRaw = extractSkillDescription(raw)
|
|
573
559
|
const desc = descRaw ? skillDescriptionSafeForMarkdownInline(descRaw) : ''
|
|
574
560
|
const header = desc ? `# ${RULE_PREFIX}${id} — ${desc}\n\n` : ''
|
|
575
|
-
const body = `${header}Виконай інструкції зі скілу \`.cursor/skills/${
|
|
561
|
+
const body = `${header}Виконай інструкції зі скілу \`.cursor/skills/${destDirName}/SKILL.md\`.\n`
|
|
576
562
|
await writeFile(destFile, body, 'utf8')
|
|
577
563
|
console.log(`✅`)
|
|
578
564
|
success++
|
|
@@ -583,7 +569,7 @@ async function syncCommands(configSkills) {
|
|
|
583
569
|
}
|
|
584
570
|
} else {
|
|
585
571
|
console.log(`❌`)
|
|
586
|
-
console.error(` Немає SKILL.md у пакеті:
|
|
572
|
+
console.error(` Немає SKILL.md у пакеті: skills/${id}`)
|
|
587
573
|
fail++
|
|
588
574
|
}
|
|
589
575
|
}
|
|
@@ -600,7 +586,7 @@ async function removeOrphanManagedCommandFiles(commandsDir, configSkills) {
|
|
|
600
586
|
if (!existsSync(commandsDir)) {
|
|
601
587
|
return []
|
|
602
588
|
}
|
|
603
|
-
const expected = new Set(configSkills.map(s => `${RULE_PREFIX}${
|
|
589
|
+
const expected = new Set(configSkills.map(s => `${RULE_PREFIX}${normalizeSkillId(s)}.md`))
|
|
604
590
|
const names = await readdir(commandsDir)
|
|
605
591
|
const removed = []
|
|
606
592
|
for (const name of names) {
|
package/package.json
CHANGED
package/schemas/n-cursor.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"skills": {
|
|
23
23
|
"type": "array",
|
|
24
|
-
"description": "Ідентифікатори skills без префікса n- (
|
|
24
|
+
"description": "Ідентифікатори skills без префікса n- (у пакеті — підкаталог skills/<id>/, у проєкті після синку — .cursor/skills/n-<id>/). Якщо відсутній, CLI доповнить списком skills з пакету.",
|
|
25
25
|
"items": {
|
|
26
26
|
"type": "string",
|
|
27
27
|
"minLength": 1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: n-abie-kustomize
|
|
3
|
+
description: >-
|
|
4
|
+
Трансформація дерев k8s у структуру Kustomize (base + overlays): dev → base, без окремої dev/
|
|
5
|
+
version: '1.0'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Трансформуй директорії, щоб виділити спільне за допомогою kustomize. За основу беремо все, що в середовищі dev, і саме в такому вигляді з dev воно має стати **base**; якщо вже є base і немає dev — це нормально, рухайся далі.
|
|
9
|
+
|
|
10
|
+
У інших середовищах має бути лише `kustomization.yaml` і зміни через оверрайди.
|
|
11
|
+
|
|
12
|
+
У **base** у всіх ресурсів (окрім `base/kustomization.yaml`) має бути namespace **dev**.
|
|
13
|
+
|
|
14
|
+
Окремої директорії **dev** не має бути — за середовище dev відповідає **base**.
|
|
15
|
+
|
|
16
|
+
README має бути в директорії **k8s**.
|
|
17
|
+
|
|
18
|
+
Рядки в маніфестах у **base**, які змінюватимуться в інших середовищах, позначай коментарем на тому самому рядку: `# буде замінено через kustomize`.
|
|
19
|
+
|
|
20
|
+
Патчів лише на namespace не роби — namespace задається в `kustomization.yaml`.
|
|
21
|
+
|
|
22
|
+
Застарілі файли прибирай.
|
|
23
|
+
|
|
24
|
+
У всіх Deployment має бути `imagePullPolicy: Always`.
|
|
25
|
+
|
|
26
|
+
Для overlays **ru** та **ua** `namespace` задавай у `kustomization.yaml` (без окремих patch лише на зміну namespace). Деталі — **n-k8s** / **abie** у `.cursor/rules/`, якщо ці правила увімкнені в проєкті.
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: n-mdc-check
|
|
3
|
-
description: >-
|
|
4
|
-
Проаналізувати правило в npm/mdc і перенести максимум перевірюваної логіки в check-{id}.mjs;
|
|
5
|
-
залишити в .mdc лише те, що не автоматизується або служить контекстом для агента
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# n-mdc-check — від правила до скрипта
|
|
9
|
-
|
|
10
|
-
Навичка для роботи з правилами пакета `@nitra/cursor`: **перевірювані критерії** мають жити в **`npm/scripts/check-<id>.mjs`**, а **`npm/mdc/<id>.mdc`** (і дзеркало **`.cursor/rules/n-<id>.mdc`**) — бути **коротким орієнтиром**: навіщо правило, як виправляти порушення, яка команда підтверджує відповідність.
|
|
11
|
-
|
|
12
|
-
Канонічні вимоги до скриптів, тестів і AST — **`.cursor/rules/scripts.mdc`**.
|
|
13
|
-
|
|
14
|
-
## Коли застосовувати
|
|
15
|
-
|
|
16
|
-
- Додали або змінили вимоги в **`npm/mdc/<id>.mdc`** (або локальному **`n-<id>.mdc`**).
|
|
17
|
-
- У правилі багато «має містити», «заборонено», точних фрагментів файлів — і агенти повторюють перевірку вручну.
|
|
18
|
-
- Потрібно зменшити обсяг `.mdc` і уникнути роз’їзду тексту й фактичної перевірки.
|
|
19
|
-
|
|
20
|
-
## Ідентифікатор правила
|
|
21
|
-
|
|
22
|
-
- Файл правила в пакеті: **`npm/mdc/<id>.mdc`** → скрипт: **`npm/scripts/check-<id>.mjs`**, команда CLI: **`npx @nitra/cursor check <id>`**.
|
|
23
|
-
- CLI підхоплює всі **`check-*.mjs`** з каталогу `scripts` пакета; окремої реєстрації не потрібно.
|
|
24
|
-
- Якщо правило згадане в **`AGENTS.md`**, для нього доречна programmatic перевірка — має існувати відповідний **`check-<id>.mjs`** (коли вимоги формалізовані).
|
|
25
|
-
|
|
26
|
-
## Workflow
|
|
27
|
-
|
|
28
|
-
### 1. Базова лінія
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npx @nitra/cursor check <id>
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Зафіксуй поточний вивід (якщо скрипт уже є).
|
|
35
|
-
|
|
36
|
-
### 2. Розбір тексту правила
|
|
37
|
-
|
|
38
|
-
Прочитай **`npm/mdc/<id>.mdc`** повністю. Для кожної вимоги виріши:
|
|
39
|
-
|
|
40
|
-
| Тип | Куди |
|
|
41
|
-
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
42
|
-
| Перевірювана структура (файли, поля JSON/YAML, наявність ключів, заборонені шляхи) | **`check-<id>.mjs`** |
|
|
43
|
-
| Семантика JS/TS у файлах | **`check-<id>.mjs`** через **Oxc AST** (`oxc-parser`), не «сліпі» regex по всьому файлу |
|
|
44
|
-
| Вміст у Vue SFC / MDX / Astro | Спочатку **виділи** валідний JS/TS-фрагмент, потім AST; regex — лише для ізоляції блоків |
|
|
45
|
-
| Не-JS конфіги, сирі workflow | Текстовий/структурний парсинг або обґрунтований regex; у JSDoc коротко **чому** не AST |
|
|
46
|
-
| Смак, архітектура, «навіщо так», поради без однозначного критерію | Залиш у **`.mdc`** |
|
|
47
|
-
|
|
48
|
-
### 3. Скрипт
|
|
49
|
-
|
|
50
|
-
- **Новий** `check-<id>.mjs`: орієнтуйся на наявні файли в **`npm/scripts/`** (наприклад **`check-bun.mjs`**) — стиль **`fail` / `pass`**, **`export async function check()`**, exit code **0 / 1**.
|
|
51
|
-
- **На початку файлу** (перед `import`) — **багаторядковий** верхній JSDoc **українською**: що перевіряє файл; між смисловими блоками — рядок `*`; перші рядки без службових префіксів — одразу зрозуміло призначення.
|
|
52
|
-
- **JSDoc** до експортованих функцій і нетривіальних внутрішніх функцій.
|
|
53
|
-
- Повідомлення **`fail`**: конкретно, що не так і як виправити (як у інших check-скриптах).
|
|
54
|
-
- Залежність **`oxc-parser`** не додавай у корінь споживача — вона в **`dependencies`** пакета **`@nitra/cursor`** (див. **dev-dep** / **scripts.mdc**).
|
|
55
|
-
|
|
56
|
-
### 4. Тести
|
|
57
|
-
|
|
58
|
-
Якщо логіка нетривіальна або її легко зламати — додай **`npm/tests/check-<id>.test.mjs`** (або розшир наявні тести). Після змін:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
cd npm && bun test
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### 5. Спрощення `.mdc`
|
|
65
|
-
|
|
66
|
-
- Прибери з правила **дубль** детальних умов, які тепер у скрипті; залиш **короткий зміст**, мотивацію, посилання на файли/конвенції.
|
|
67
|
-
- Потрібно зберегти (або додати) секцію **«Перевірка»** з **`npx @nitra/cursor check <id>`** (або повним ім’ям правила з `AGENTS.md`).
|
|
68
|
-
- Якщо оновлюєш пакетне правило, **синхронно** онови дзеркало **`.cursor/rules/n-<id>.mdc`**, щоб текст не розходився (як прийнято в цьому репозиторії).
|
|
69
|
-
|
|
70
|
-
### 6. Верифікація та версія пакета
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
npx @nitra/cursor check <id>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Після змін у **`npm/`** (у тому числі `skills/`, `scripts/`, `mdc/`) підвищ **patch**-версію в **`npm/package.json`** на **один** крок відносно вже запланованого релізу (див. **n-npm-module**).
|
|
77
|
-
|
|
78
|
-
## Антипатерни
|
|
79
|
-
|
|
80
|
-
- Залишати в `.mdc` великі «еталонні» YAML/JSON, якщо перевірка може читати очікувану структуру з репозиторію або константи в скрипті.
|
|
81
|
-
- Оновлювати лише текст правила без оновлення **`check-<id>.mjs`**, коли для правила вже є скрипт або з’явилась нова перевірювана умова.
|
|
82
|
-
- Дублювати однакову логіку в кількох check-скриптах замість спільної утиліти в **`npm/scripts/utils/`**.
|
|
83
|
-
|
|
84
|
-
## Команда в чаті
|
|
85
|
-
|
|
86
|
-
`/n-mdc-check` — застосуй цей workflow до правила з поточного контексту або до `<id>`, який вкаже користувач.
|
|
File without changes
|
|
File without changes
|