@nitra/cursor 1.13.41 → 1.13.43

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,19 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.13.43] - 2026-05-18
8
+
9
+ ### Removed
10
+
11
+ - `npm/CLAUDE.md` як path-scoped нагадування для роботи в `npm/` повністю прибрано — фінальне завершення міграції з `1.13.42` (де вже прибрали `syncNpmClaudeMd` + Rego-first STOP перенесли у `scripts.mdc`): видалено сам `npm/CLAUDE.md`, темплейт `npm/.claude-template/npm-CLAUDE.md`, останні згадки в `bin/n-cursor.js` (повідомлення про `npm/CLAUDE.md` після sync; JSDoc) і опис у `schemas/n-cursor.json` `claude-config`. Реліз-правила (PR-bump + CHANGELOG) і так живуть у `n-changelog.mdc`/`n-npm-module.mdc` (alwaysApply).
12
+
13
+ ## [1.13.42] - 2026-05-18
14
+
15
+ ### Added
16
+
17
+ - `efes` rule: новий (поки що порожній) пакет правил для проєктів **github.com/efes-cloud/***. Автодетект у `auto-rules.mjs` через `EFES_REPOSITORY_URL_MARKER` (`https://github.com/efes-cloud/`) — аналогічно до `abie`. Додано `npm/rules/efes/efes.mdc` + `auto.md`, прописано порядок в `AUTO_RULE_ORDER` і покрито тестами в `auto-rules.test.mjs`.
18
+ - `efes-create-env` skill: повʼязано з правилом `efes` через `skills/efes-create-env/auto.md` (`[efes]`) — активується автоматично, коли репозиторій відповідає efes-маркеру. Тести в `auto-skills.test.mjs` фіксують позитивний і негативний випадки.
19
+
7
20
  ## [1.13.41] - 2026-05-18
8
21
 
9
22
  ### Fixed
package/bin/n-cursor.js CHANGED
@@ -23,7 +23,7 @@
23
23
  * Agent інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
24
24
  * синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
25
25
  * `.cursor/hooks.json` (Cursor Agent hooks; merge — користувацькі hooks зберігаються),
26
- * `npm/CLAUDE.md` (path-scoped нагадування для роботи в `npm/`) і slash-команди checks (`/n-check`).
26
+ * і slash-команди checks (`/n-check`).
27
27
  * Опт-аут — поле `claude-config: false` у `.n-cursor.json`.
28
28
  *
29
29
  * Якщо у корені репозиторію немає .n-cursor.json, спочатку перейменовується за наявності nitra-cursor.json;
@@ -1315,7 +1315,6 @@ async function runSync() {
1315
1315
  const parts = []
1316
1316
  if (result.settings) parts.push('.claude/settings.json')
1317
1317
  if (result.cursorHooks) parts.push('.cursor/hooks.json')
1318
- if (result.npmClaudeMd) parts.push('npm/CLAUDE.md')
1319
1318
  if (result.commands.length > 0) parts.push(`${result.commands.length} slash-commands`)
1320
1319
  if (result.adrHook) parts.push('.claude/hooks/capture-decisions.sh')
1321
1320
  if (result.adrNormalizeHook) parts.push('.claude/hooks/normalize-decisions.sh')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.13.41",
3
+ "version": "1.13.43",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -0,0 +1 @@
1
+ якщо в кореневому package.json в секції "repository" присутній текст "<https://github.com/efes-cloud/**/>"
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: Правила для проєктів Efes (репозиторії під github.com/efes-cloud)
3
+ alwaysApply: true
4
+ version: '1.0'
5
+ ---
6
+
7
+ Правило **efes** для споживачів **@nitra/cursor** у репозиторіях під **`github.com/efes-cloud/*`**. Поки що зміст правила порожній — додавайте сюди вимоги, специфічні для efes-проєктів, у міру їх появи.
@@ -130,7 +130,7 @@ egress_has_internet_http_https(spec) if {
130
130
  is_object(peer)
131
131
  ipb := object.get(peer, "ipBlock", null)
132
132
  is_object(ipb)
133
- object.get(ipb, "cidr", "") == "0.0.0.0/0"
133
+ ipb.cidr == "0.0.0.0/0"
134
134
  ports := object.get(rule, "ports", null)
135
135
  is_array(ports)
136
136
  egress_ports_include(ports, 80)
@@ -140,7 +140,7 @@ egress_has_internet_http_https(spec) if {
140
140
  egress_ports_include(ports, want) if {
141
141
  some p in ports
142
142
  is_object(p)
143
- object.get(p, "port", null) == want
143
+ p.port == want
144
144
  }
145
145
 
146
146
  egress_has_cluster_namespace_selector(spec) if {
@@ -78,8 +78,9 @@ const DOTENV_LINTER_PREFLIGHT = {
78
78
  }
79
79
 
80
80
  /**
81
- * @param {PreflightDep} dep
82
- * @returns {string | null}
81
+ * Шукає шлях до бінарника `dep.bin` у `PATH`; на Windows додатково перебирає `dep.winBins`.
82
+ * @param {PreflightDep} dep опис залежності з canon-списку preflight-перевірок
83
+ * @returns {string | null} абсолютний шлях до знайденого бінарника або `null`, якщо не знайдено
83
84
  */
84
85
  function resolvePreflightBin(dep) {
85
86
  if (platform === 'win32' && dep.winBins) {
@@ -92,8 +93,9 @@ function resolvePreflightBin(dep) {
92
93
  }
93
94
 
94
95
  /**
95
- * @param {PreflightDep} dep
96
- * @returns {void}
96
+ * Друкує stderr-повідомлення про відсутній бінарник з install-hint'ами і посиланням на правило.
97
+ * @param {PreflightDep} dep опис залежності — джерело пояснення й install-команд
98
+ * @returns {void} нічого не повертає; виводить рядки в `console.error`
97
99
  */
98
100
  function printPreflightMissingMessage(dep) {
99
101
  console.error(`❌ ${dep.bin} не знайдено в PATH.`)
@@ -106,8 +108,10 @@ function printPreflightMissingMessage(dep) {
106
108
  }
107
109
 
108
110
  /**
109
- * @param {PreflightDep} dep
110
- * @returns {boolean}
111
+ * Виконує preflight-перевірку: повертає `true` і друкує `successMsg`, якщо бінарник знайдено,
112
+ * інакше друкує install-hint у stderr і повертає `false`.
113
+ * @param {PreflightDep} dep опис залежності для перевірки наявності в `PATH`
114
+ * @returns {boolean} `true` — бінарник знайдено, `false` — відсутній
111
115
  */
112
116
  function preflight(dep) {
113
117
  if (resolvePreflightBin(dep)) {
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "claude-config": {
59
59
  "type": "boolean",
60
- "description": "Чи синхронізувати `.claude/settings.json` (hooks + permissions, merge зі збереженням користувацьких полів), `npm/CLAUDE.md` (path-scoped нагадування для роботи в `npm/`) і slash-команди checks. За замовчуванням true.",
60
+ "description": "Чи синхронізувати `.claude/settings.json` (hooks + permissions, merge зі збереженням користувацьких полів) і slash-команди checks. За замовчуванням true.",
61
61
  "default": true
62
62
  }
63
63
  },
@@ -32,6 +32,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
32
32
  'capacitor',
33
33
  'changelog',
34
34
  'docker',
35
+ 'efes',
35
36
  'ga',
36
37
  'graphql',
37
38
  'hasura',
@@ -106,6 +107,7 @@ export const AUTO_RULE_DEPENDENCIES = Object.freeze(
106
107
  )
107
108
 
108
109
  const ABIE_REPOSITORY_URL_MARKER = 'https://github.com/abinbevefes/'
110
+ const EFES_REPOSITORY_URL_MARKER = 'https://github.com/efes-cloud/'
109
111
  const HASURA_CONFIG_MARKER = 'metadata_directory: metadata'
110
112
  const JS_LIKE_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx)$/iu
111
113
  const REGO_RE = /\.rego$/iu
@@ -605,6 +607,7 @@ export async function detectAutoRules({
605
607
  : null
606
608
  )
607
609
  const isAbie = typeof repositoryUrl === 'string' && repositoryUrl.toLowerCase().includes(ABIE_REPOSITORY_URL_MARKER)
610
+ const isEfes = typeof repositoryUrl === 'string' && repositoryUrl.toLowerCase().includes(EFES_REPOSITORY_URL_MARKER)
608
611
  const depHits = await collectDependencyKeysPresentInPackageJsonTree(root, [
609
612
  'mssql',
610
613
  'pg',
@@ -639,6 +642,7 @@ export async function detectAutoRules({
639
642
  { enabled: packageJsonExists, id: 'bun' },
640
643
  { enabled: facts.hasCapacitorConfig, id: 'capacitor' },
641
644
  { enabled: facts.hasDockerfile, id: 'docker' },
645
+ { enabled: isEfes, id: 'efes' },
642
646
  { enabled: facts.hasGaWorkflowsDir, id: 'ga' },
643
647
  { enabled: facts.hasGqlTaggedTemplates, id: 'graphql' },
644
648
  { enabled: facts.hasHasuraConfig, id: 'hasura' },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Синхронізує конфігурацію Claude Code (`.claude/settings.json`, `npm/CLAUDE.md`,
2
+ * Синхронізує конфігурацію Claude Code (`.claude/settings.json`,
3
3
  * slash-команди для checks, ADR Stop-hook) і Cursor hooks (`.cursor/hooks.json`)
4
4
  * у поточний проєкт із темплейтів пакету
5
5
  * `npm/.claude-template/`.
@@ -8,8 +8,6 @@
8
8
  * - `settings.json` — **merge**: користувацькі поля зберігаються; наші hooks
9
9
  * ідентифікуються командою-маркером (`MANAGED_HOOK_COMMAND_MARKERS`) і
10
10
  * перезаписуються; permissions.allow зливається через union (із дедублікацією).
11
- * - `npm/CLAUDE.md` — **fully owned**: завжди перезаписується; пропускається,
12
- * якщо в проєкті немає каталогу `npm/`.
13
11
  * - `.claude/commands/n-check.md` — fully owned slash-команда.
14
12
  * - `.claude/hooks/capture-decisions.sh` — fully owned bash-скрипт ADR capture Stop-hook;
15
13
  * копіюється з `.claude-template/hooks/`, лише коли в `.n-cursor.json` `rules`
@@ -52,7 +50,6 @@ const CURSOR_DIR = '.cursor'
52
50
  const CURSOR_HOOKS_FILE = `${CURSOR_DIR}/hooks.json`
53
51
  const ADR_HOOK_SCRIPT_NAME = 'capture-decisions.sh'
54
52
  const ADR_NORMALIZE_HOOK_SCRIPT_NAME = 'normalize-decisions.sh'
55
- const NPM_CLAUDE_MD_FILE = 'npm/CLAUDE.md'
56
53
  const TEMPLATE_DIR_NAME = '.claude-template'
57
54
 
58
55
  /** Канонічна група hooks для ADR capture Stop-hook'а — додається в settings, коли `adr` у `rules`. */
@@ -390,25 +387,6 @@ export function syncAdrNormalizeHookScript(projectRoot, templateDir) {
390
387
  return syncHookScript(projectRoot, templateDir, ADR_NORMALIZE_HOOK_SCRIPT_NAME)
391
388
  }
392
389
 
393
- /**
394
- * Копіює `npm/CLAUDE.md` з темплейту, якщо в проєкті є каталог `npm/`.
395
- * @param {string} projectRoot корінь проєкту, куди писати
396
- * @param {string} templateDir каталог `.claude-template/` усередині пакету `@nitra/cursor`
397
- * @returns {Promise<{ written: boolean, path: string }>} результат: чи писали файл, та його відносний шлях
398
- */
399
- export async function syncNpmClaudeMd(projectRoot, templateDir) {
400
- if (!existsSync(join(projectRoot, 'npm'))) {
401
- return { written: false, path: '' }
402
- }
403
- const templatePath = join(templateDir, 'npm-CLAUDE.md')
404
- if (!existsSync(templatePath)) {
405
- return { written: false, path: '' }
406
- }
407
- const content = await readFile(templatePath, 'utf8')
408
- await writeFile(join(projectRoot, NPM_CLAUDE_MD_FILE), content, 'utf8')
409
- return { written: true, path: NPM_CLAUDE_MD_FILE }
410
- }
411
-
412
390
  /**
413
391
  * Копіює всі slash-команди з `templateDir/commands/` у `.claude/commands/`.
414
392
  * Команди ідентифікуються тим, що вони лежать у темплейті — не перетинаються
@@ -444,14 +422,13 @@ export async function syncClaudeCommands(projectRoot, templateDir) {
444
422
  * @param {string} options.bundledPackageRoot корінь установленого `@nitra/cursor`
445
423
  * @param {boolean} options.enabled чи увімкнено sync (з `.n-cursor.json` `claude-config`)
446
424
  * @param {string[]} [options.rules] список увімкнених правил із `.n-cursor.json` — впливає на ADR Stop-hook (`adr`)
447
- * @returns {Promise<{ settings: boolean, cursorHooks: boolean, npmClaudeMd: boolean, commands: string[], adrHook: boolean, adrNormalizeHook: boolean }>} прапорці записів settings/CLAUDE.md/Cursor hooks/ADR-hook(s) та список записаних slash-команд
425
+ * @returns {Promise<{ settings: boolean, cursorHooks: boolean, commands: string[], adrHook: boolean, adrNormalizeHook: boolean }>} прапорці записів settings/Cursor hooks/ADR-hook(s) та список записаних slash-команд
448
426
  */
449
427
  export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enabled, rules = [] }) {
450
428
  if (!enabled) {
451
429
  return {
452
430
  settings: false,
453
431
  cursorHooks: false,
454
- npmClaudeMd: false,
455
432
  commands: [],
456
433
  adrHook: false,
457
434
  adrNormalizeHook: false
@@ -462,7 +439,6 @@ export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enable
462
439
  return {
463
440
  settings: false,
464
441
  cursorHooks: false,
465
- npmClaudeMd: false,
466
442
  commands: [],
467
443
  adrHook: false,
468
444
  adrNormalizeHook: false
@@ -475,12 +451,10 @@ export async function syncClaudeConfig({ projectRoot, bundledPackageRoot, enable
475
451
  : { written: false, path: '' }
476
452
  const settings = await syncClaudeSettings(projectRoot, templateDir, { includeAdrHook })
477
453
  const cursorHooks = await syncCursorHooksConfig(projectRoot, { includeAdrHook })
478
- const npmClaudeMd = await syncNpmClaudeMd(projectRoot, templateDir)
479
454
  const commands = await syncClaudeCommands(projectRoot, templateDir)
480
455
  return {
481
456
  settings: settings.written,
482
457
  cursorHooks: cursorHooks.written,
483
- npmClaudeMd: npmClaudeMd.written,
484
458
  commands,
485
459
  adrHook: adrHook.written,
486
460
  adrNormalizeHook: adrNormalizeHook.written
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: n-efes-create-env
3
+ description: >-
4
+ Створення нового середовища (env) в репозиторії efes/manager — приймає назву
5
+ середовища як аргумент (наприклад `kz`, `kz-qa`, `tr-stage`, `ua-dev`).
6
+ Генерує .env.prod-<env>, .env.remote-<env>, оверлей site/k8s/<env>/,
7
+ додає скрипт start-remote-<env> у site/package.json і реєструє <env> у
8
+ списках branches GitHub Actions та Azure Pipelines. Якщо назва закінчується
9
+ на dev або -qa — non-production (база: md-qa); інакше — production (база: md).
10
+ Тригери: «створи середовище», «новий env», «add environment», «n-efes-create-env».
11
+ ---
12
+
13
+ # n-efes-create-env — створення нового середовища
14
+
15
+ Скіл генерує всі конфіги для нового середовища `manager`, спираючись на існуюче як шаблон. Запуск: `/n-efes-create-env <env>` або «створи середовище `<env>`».
16
+
17
+ ## Існуючі середовища (станом на створення скіла)
18
+
19
+ `dev` · `md` · `md-qa` · `tr` · `tr-qa`
20
+
21
+ ## Аргумент
22
+
23
+ Назва середовища — `$ARGUMENTS` (kebab-case, без пробілів). Якщо аргументу немає — спитай користувача.
24
+
25
+ ## Класифікація та джерело
26
+
27
+ | Закінчення | Тип | Базове середовище | Базовий домен |
28
+ | --------------- | -------------- | ----------------- | --------------------------- |
29
+ | `dev` або `-qa` | non-production | `md-qa` | `mayamd-qa.anadoluefes.com` |
30
+ | інше | production | `md` | `mayamd.anadoluefes.com` |
31
+
32
+ Перед роботою зафіксуй у себе:
33
+
34
+ - `ENV` — `$ARGUMENTS`
35
+ - `BASE` — `md-qa` або `md` згідно з таблицею вище
36
+ - `BASE_DOMAIN` — домен бази
37
+ - `DOMAIN` — `maya${ENV}.anadoluefes.com` (шаблон: префікс `maya` + env). Якщо ENV вже містить країну з дефісом (наприклад `kz-qa`) — домен буде `mayakz-qa.anadoluefes.com`. Якщо користувач хоче інший домен — підтверди.
38
+ - `COUNTRY` — країна без суфіксу `-qa`/`-dev`/`-stage` (наприклад `kz-qa` → `kz`). Потрібно для ACR-шляхів.
39
+
40
+ ## Кроки
41
+
42
+ ### 0. Перевірити, що середовище ще не існує
43
+
44
+ ```bash
45
+ test ! -f site/.env.prod-$ENV \
46
+ && test ! -f site/.env.remote-$ENV \
47
+ && test ! -d site/k8s/$ENV \
48
+ || { echo "Середовище $ENV вже існує — припини"; exit 1; }
49
+ ```
50
+
51
+ ### 1. Інтерактивне опитування — зібрати country-specific дані
52
+
53
+ **ОБОВ'ЯЗКОВО** перед будь-якими `cp`/`sed` запитай користувача через `AskUserQuestion` (у Cursor — звичайним повідомленням з варіантами). Не виконуй кроки 2–5, поки не отримав усі відповіді.
54
+
55
+ Запитай **одним викликом** `AskUserQuestion` із кількома запитаннями:
56
+
57
+ 1. **Тип середовища** — заголовок «Тип env»:
58
+ - «Нова країна» — env вводить нову країну (новий VITE_COUNTRY, своя SAP-tenant, свій бакет)
59
+ - «Новий етап існуючої країни» — env є додатковим етапом md/tr (інший суфікс, але та сама країна та tenant)
60
+
61
+ 2. **Домен** — заголовок «Domain»:
62
+ - `maya${ENV}.anadoluefes.com` (default, рекомендований)
63
+ - «Інший» — користувач введе власний
64
+
65
+ 3. **SAP-credentials** — заголовок «SAP auth»:
66
+ - «Залишити як у бази (`$BASE`)» — для нових етапів існуючої країни
67
+ - «Заглушки `TODO_SAP_*`» — для нової країни, реальні значення додасть devops/security окремим commit
68
+ - «Введу зараз» — користувач передасть значення в `Other` (формат: `ENDPOINT=…; CLIENT_ID=…; CLIENT_SECRET=…`)
69
+
70
+ 4. **Storage account (`VITE_BUCKET`)** — заголовок «Bucket»:
71
+ - «Залишити як у бази» — той самий storage account (`stmdmayasfaprod001`/`stmdmayasfadev001`)
72
+ - «Власний для нової країни» — користувач введе ім'я storage account в `Other` (наприклад `stkzmayasfaprod001`)
73
+
74
+ Якщо «Нова країна» — окремо (у тому ж або наступному виклику) запитай:
75
+
76
+ 5. **VITE_COUNTRY** — заголовок «Country»: `md`, `tr`, інше (через `Other`)
77
+ 6. **VITE_TFM_DEFAULT** — заголовок «TFM default»: `ru`, `en`, `tr`, `ro`, інше
78
+ 7. **VITE_TFM_LIST** — заголовок «TFM list»: набір популярних варіантів (`ru,en,ro,tr`, `en,tr`, `ru,en`) + `Other`
79
+
80
+ Збережи відповіді у змінні `DOMAIN`, `COUNTRY`, `TFM_DEFAULT`, `TFM_LIST`, `BUCKET_ACCOUNT`, `SAP_MODE` (`keep`/`stub`/`custom`), `SAP_VALUES` (якщо `custom`).
81
+
82
+ ### 2. Створити `.env.prod-$ENV` та `.env.remote-$ENV`
83
+
84
+ Скопіюй файли бази:
85
+
86
+ ```bash
87
+ cp site/.env.prod-$BASE site/.env.prod-$ENV
88
+ cp site/.env.remote-$BASE site/.env.remote-$ENV
89
+
90
+ # домен
91
+ sed -i '' "s|$BASE_DOMAIN|$DOMAIN|g" site/.env.prod-$ENV site/.env.remote-$ENV
92
+
93
+ # bucket path (maya-files-<base> → maya-files-$ENV)
94
+ sed -i '' "s|maya-files-$BASE|maya-files-$ENV|g" site/.env.prod-$ENV site/.env.remote-$ENV
95
+ ```
96
+
97
+ Далі **застосуй відповіді з кроку 1** (через `Edit`):
98
+
99
+ - Якщо `BUCKET_ACCOUNT` != base — заміни storage account host у `VITE_BUCKET`.
100
+ - Якщо тип = «Нова країна»:
101
+ - `VITE_COUNTRY=$COUNTRY`
102
+ - `VITE_TFM_DEFAULT=$TFM_DEFAULT`
103
+ - `VITE_TFM_LIST=$TFM_LIST`
104
+ - Якщо `SAP_MODE=stub` — заміни значення на:
105
+ - `VITE_SAP_AUTH_ENDPOINT=TODO_SAP_ENDPOINT`
106
+ - `VITE_SAP_AUTH_CLIENT_ID='TODO_SAP_CLIENT_ID'`
107
+ - `VITE_SAP_AUTH_CLIENT_SECRET=TODO_SAP_CLIENT_SECRET`
108
+ - Якщо `SAP_MODE=custom` — підстав `SAP_VALUES`.
109
+
110
+ Інваріанти, які мають лишитися після всіх правок:
111
+
112
+ - У `.env.remote-$ENV` — `VITE_DOMAIN=localhost`, `VITE_UPLOADER=/file-link/`, `VITE_EXPORT_EXCEL=/export-table/`.
113
+ - У `.env.prod-$ENV` — `VITE_DOMAIN=$DOMAIN`, `VITE_UPLOADER=https://$DOMAIN/file-link/`, `VITE_EXPORT_EXCEL=https://$DOMAIN/export-table/`.
114
+
115
+ ### 2. Створити `site/k8s/$ENV/kustomization.yaml`
116
+
117
+ ```bash
118
+ mkdir -p site/k8s/$ENV
119
+ cp site/k8s/$BASE/kustomization.yaml site/k8s/$ENV/kustomization.yaml
120
+ ```
121
+
122
+ Відредагуй новий файл:
123
+
124
+ - `namespace: $ENV`
125
+ - `images[].newName`:
126
+ - **production** → `aefes.azurecr.io/mayasfa-$ENV/$ENV/manager-site`
127
+ - **non-production** → `aefes.azurecr.io/mayasfa-$COUNTRY-dev/$ENV/manager-site`
128
+ - `HTTPRoute` патч → `hostnames: ["$DOMAIN"]`
129
+ - Для **production** залиш блок `components: [../components]` (HPA/PDB).
130
+ - Для **non-production** видали `components` блок і HPA/PDB-патчі (як у `md-qa`).
131
+
132
+ Перевір: запусти `kubectl kustomize site/k8s/$ENV --output /dev/null` (якщо `kubectl` доступний) — має пройти без помилок.
133
+
134
+ ### 3. Додати `start-remote-$ENV` у `site/package.json`
135
+
136
+ У секцію `"scripts"` додай рядок (зберігай порядок поряд із сусідніми `start-remote-*`):
137
+
138
+ ```json
139
+ { "scripts": { "start-remote-$ENV": "vite dev --mode remote-$ENV" } }
140
+ ```
141
+
142
+ ### 4. Зареєструвати `$ENV` у CI/CD branch-списках
143
+
144
+ Додай `$ENV` у такі місця (зберігай існуючий порядок: dev → md-qa → md → tr → tr-qa → нові):
145
+
146
+ | Файл | Поле |
147
+ | ------------------------------------------- | ------------------------------ |
148
+ | `.github/workflows/sync-to-azure.yml` | `on.push.branches` |
149
+ | `.github/workflows/clean-merged-branch.yml` | `ignore_branches` (через кому) |
150
+ | `.azurepipelines/apply-k8s.yml` | `trigger.branches.include` |
151
+ | `.azurepipelines/site.yml` | `trigger.branches.include` |
152
+
153
+ Якщо існують інші `**/k8s/`-проекти у репозиторії — переконайся, що `$ENV` потрібен і там (на момент створення скіла є тільки `site/k8s/`).
154
+
155
+ ### 5. Попередження користувачу про зовнішні залежності
156
+
157
+ Скіл **не** створює ці артефакти — повідом про них користувача:
158
+
159
+ - У repo `MayaSFA/k8s` має існувати шаблон `.azurepipelines/azure-pipeline-templates/$ENV.yml` (його використовує `apply-k8s.yml` і `site.yml`).
160
+ - Гілка `$ENV` має бути створена в обох remote (GitHub + Azure DevOps).
161
+ - DNS-запис для `$DOMAIN` та health-check у gateway мають бути налаштовані відповідною командою.
162
+ - Якщо ENV — нова країна: ACR-репозиторій `mayasfa-$ENV` (для prod) або `mayasfa-$COUNTRY-dev` (для non-prod) має бути створений.
163
+
164
+ ### 6. Перевірка результату
165
+
166
+ ```bash
167
+ ls site/.env.prod-$ENV site/.env.remote-$ENV site/k8s/$ENV/kustomization.yaml
168
+ grep -n "$ENV" \
169
+ .github/workflows/sync-to-azure.yml \
170
+ .github/workflows/clean-merged-branch.yml \
171
+ .azurepipelines/apply-k8s.yml \
172
+ .azurepipelines/site.yml \
173
+ site/package.json
174
+ git status
175
+ ```
176
+
177
+ Виведи користувачу короткий звіт: що створено, що змінено, які зовнішні дії потрібні (з кроку 5). **Не комітити автоматично** — користувач сам перегляне через `git diff` і закомітить.
178
+
179
+ ## Нюанси та пастки
180
+
181
+ - **Доменний шаблон `maya${ENV}.anadoluefes.com`** працює для існуючих країн (md/tr). Якщо нова країна має інший публічний URL — підтверди в користувача _до_ запуску `sed`.
182
+ - **ACR-шлях суворо за шаблоном**: prod → `mayasfa-$ENV/$ENV/manager-site`, non-prod → `mayasfa-$COUNTRY-dev/$ENV/manager-site`. Жодних додаткових сегментів між ACR-namespace та `$ENV` бути не повинно.
183
+ - **`ENV=dev`**: гілка `dev` — спеціальний випадок (вживає `base/`, без оверлею). Скіл не призначений для перестворення `dev`.
184
+ - **`.env.development`** — лише для локального запуску без бекенду; скіл його не чіпає.
185
+ - **Не запускай `bun run lint` / `lint-ga`** в один потік з іншими — дивись CLAUDE.md «Лінт і ESLint». Перевір лише потрібний файл (`bunx eslint <path>` або точково).
@@ -0,0 +1 @@
1
+ [efes]
@@ -1,40 +0,0 @@
1
- <!-- Файл генерується автоматично через `npx @nitra/cursor`. Не редагуй вручну. -->
2
-
3
- # Робота в `npm/`
4
-
5
- Path-scoped нагадування для агента: підвантажується автоматично, коли редагуємо файли під `npm/`.
6
-
7
- ## Перед PR з коміт-релевантними змінами в `npm/`
8
-
9
- 1. Підвищ `version` у `npm/package.json` (build-bump, не більше одного кроку відносно `HEAD`).
10
- 2. Додай запис у `npm/CHANGELOG.md` форматом Keep a Changelog: `## [версія] - YYYY-MM-DD` + секції `### Added/Changed/Fixed/Removed`.
11
- 3. Переконайся, що `"CHANGELOG.md"` є в масиві `files` у `npm/package.json` (правило `changelog`).
12
-
13
- Логіка PR-scoped: bump і запис достатньо зробити **один раз — як суму по всьому PR** (порівняння йде з гілкою `dev`), а не на кожен коміт.
14
-
15
- Без оновленого CHANGELOG `npx @nitra/cursor check changelog` падає, а `Stop` hook блокує завершення ходу.
16
-
17
- ## Перевірка локально
18
-
19
- ```bash
20
- npx @nitra/cursor check changelog
21
- npx @nitra/cursor check npm-module
22
- ```
23
-
24
- ## Перш ніж писати / розширювати `check-*.mjs`
25
-
26
- **STOP — спершу пройди алгоритм Rego-first** (`.cursor/rules/conftest.mdc`, alwaysApply). Це стосується **і нової** перевірки, **і додавання нового deny у вже існуючий** `check-<rule>.mjs`: подивись `npm/policy/<rule>/`, чи задача не лягає у вже існуючий rego-пакет як ще одне `deny contains`.
27
-
28
- Швидкий self-check для нової перевірки (порядок важливий):
29
-
30
- 1. **Це пер-документна перевірка одного JSON/YAML?** (наявність / форма поля, regex по значенню, перелік дозволених літералів). → **Rego, без JS-коду.** Пиши у `npm/policy/<rule>/<name>/<name>.rego` + `<name>_test.rego`.
31
- 2. Потрібен `readdir`, `stat`, парність файлів, AST-парсинг JS/TS, autofix, modeline до YAML-body? → **JS** у `check-<rule>.mjs`. Per-document частина (якщо є) усе одно лишається у rego — JS викликає її через `runConftestBatch`.
32
- 3. Не впевнений? Подивись референс **`npm/policy/k8s/*`** ↔ **`npm/scripts/check-k8s.mjs`** (Plan B: Rego-authoritative + JS-orchestrator) і список «що Rego об'єктивно не вміє» у `conftest.mdc`.
33
-
34
- **Червоний прапор:** дописуєш `if (pkg.<field>) fail(…)` у JS — майже завжди це варто було робити як `deny contains msg if { … }` у відповідному rego-пакеті. Перевір `npm/policy/<rule>/` **перед** редагуванням `check-<rule>.mjs`.
35
-
36
- ## Джерело правил
37
-
38
- - `.cursor/rules/n-changelog.mdc` — правило про CHANGELOG (PR-scoped, для всіх воркспейсів)
39
- - `.cursor/rules/n-npm-module.mdc` — правило публікації пакета (типи, hk, npm-publish workflow)
40
- - `npm/scripts/check-changelog.mjs`, `npm/scripts/check-npm-module.mjs` — алгоритми перевірки