@nitra/cursor 1.8.164 → 1.8.167

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,29 @@
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.167] - 2026-05-03
8
+
9
+ ### Added
10
+
11
+ - `lint-ga.mjs` / `bin/n-cursor.js`: нова CLI-підкоманда `n-cursor lint-ga` (експорт `runLintGaCli`). Робить preflight на `shellcheck` (exit 1 + brew/apt/pacman підказки, коли його немає в `PATH`), тоді послідовно запускає `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .` через `spawnSync` з `stdio: 'inherit'`. Тепер і `bun lint-ga` сигналізує про відсутність shellcheck — раніше це робила лише `check ga`.
12
+ - `ga.mdc` (v1.5): канонічний скрипт `lint-ga` у `package.json` тепер `n-cursor lint-ga` (а не `bunx github-actionlint && uvx zizmor …`); `check-ga.mjs` валідує саме цю форму. Виклик через bin-ім’я `n-cursor`, бо `bun run` транслює `npx` у `bun x`, а `bun x @nitra/cursor` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання.
13
+
14
+ ## [1.8.166] - 2026-05-03
15
+
16
+ ### Added
17
+
18
+ - `ga.mdc` (v1.4) / `check-ga.mjs`: нова перевірка локального [`shellcheck`](https://www.shellcheck.net/) у `PATH`. Без нього `actionlint` (`bunx github-actionlint`) мовчки пропускає shell-перевірки в `run:` блоках, тож локальний `bun lint-ga` дає зелений результат, який падає в CI на `ubuntu-latest` (де shellcheck передвстановлений). `npx @nitra/cursor check ga` тепер `fail` з підказкою встановлення (`brew install shellcheck` / `apt-get install -y shellcheck` / `pacman -S shellcheck`).
19
+
20
+ ### Changed
21
+
22
+ - `utils/resolve-cmd.mjs`: явно передаємо `process.env` у `spawnSync('which'/'where', ...)`, щоб у Bun зміни `PATH` у runtime (наприклад, підстановка стабів у тестах) бачилися дочірнім процесом. Без цього Bun використовував би snapshot оточення на старті.
23
+
24
+ ## [1.8.165] - 2026-05-01
25
+
26
+ ### Changed
27
+
28
+ - `ga.mdc` / `check-ga.mjs`: лінт workflow-ів через [`github-actionlint`](https://www.npmjs.com/package/github-actionlint) замість `node-actionlint`. Канонічний скрипт `lint-ga` тепер `bunx github-actionlint && uvx zizmor --offline --collect=workflows .`; `check-ga` вимагає у `package.json` саме `github-actionlint`.
29
+
7
30
  ## [1.8.164] - 2026-05-01
8
31
 
9
32
  ### Added
package/bin/n-cursor.js CHANGED
@@ -11,6 +11,8 @@
11
11
  * `npx \@nitra/cursor rename-yaml-extensions` — k8s `*.yml` → `*.yaml`, `.github` `*.yaml` → `*.yml` (опції: `--dry-run`, `--root=…`; див. bin/rename-yaml-extensions.mjs)
12
12
  * `npx \@nitra/cursor stop-hook` — точка входу Stop hook Claude Code (читає stdin, виходить 0 при `stop_hook_active`,
13
13
  * інакше викликає `check`); прописується автоматично в `.claude/settings.json`
14
+ * `npx \@nitra/cursor lint-ga` — канонічний lint-ga (ga.mdc): preflight на `shellcheck` →
15
+ * `bunx github-actionlint` → `uvx zizmor --offline --collect=workflows .`
14
16
  *
15
17
  * Claude Code інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
16
18
  * синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
@@ -57,6 +59,7 @@ import { buildAgentsCommandBulletItems } from '../scripts/build-agents-commands.
57
59
  import { detectAutoRulesAndSkills, mergeConfigWithAutoDetected, normalizeIdList } from '../scripts/auto-rules.mjs'
58
60
  import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
59
61
  import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
62
+ import { runLintGaCli } from '../scripts/lint-ga.mjs'
60
63
  import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
61
64
  import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
62
65
  import { runRenameYamlExtensionsCli } from './rename-yaml-extensions.mjs'
@@ -1222,6 +1225,12 @@ try {
1222
1225
 
1223
1226
  break
1224
1227
  }
1228
+ case 'lint-ga': {
1229
+ // Канонічний lint-ga з preflight на shellcheck → actionlint → zizmor (ga.mdc).
1230
+ process.exitCode = runLintGaCli()
1231
+
1232
+ break
1233
+ }
1225
1234
  case undefined:
1226
1235
  case '': {
1227
1236
  await runSync()
@@ -1231,7 +1240,7 @@ try {
1231
1240
  default: {
1232
1241
  console.error(`❌ Невідома команда: ${command}`)
1233
1242
  console.error(
1234
- ` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook`
1243
+ ` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook, lint-ga`
1235
1244
  )
1236
1245
  process.exitCode = 1
1237
1246
  }
package/mdc/ga.mdc CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Правила форматів для .github/workflows
3
3
  alwaysApply: true
4
- version: '1.3'
4
+ version: '1.5'
5
5
  ---
6
6
 
7
7
  У `.github/workflows/` лише **`.yml`**. Мають бути **`clean-ga-workflows.yml`**, **`clean-merged-branch.yml`**, **`lint-ga.yml`**, **`git-ai.yml`**. Якщо є **`apply-k8s.yml`** / **`apply-nats-consumer.yml`** — paths у тригері як у фрагментах.
@@ -231,14 +231,22 @@ jobs:
231
231
  - uses: ./.github/actions/setup-bun-deps
232
232
  ```
233
233
 
234
- **Лінт:** [actionlint](https://github.com/rhysd/actionlint) через [node-actionlint](https://www.npmjs.com/package/node-actionlint); [zizmor](https://docs.zizmor.sh) — `uvx`, офлайн. Скрипт у корені:
234
+ **Лінт:** [actionlint](https://github.com/rhysd/actionlint) через [github-actionlint](https://www.npmjs.com/package/github-actionlint); [zizmor](https://docs.zizmor.sh) — `uvx`, офлайн. Канонічний скрипт у корені делегує виконання CLI `n-cursor lint-ga` (бінарка з `node_modules/.bin/` пакету `@nitra/cursor`), який робить preflight на `shellcheck` і послідовно запускає `actionlint` та `zizmor`:
235
235
 
236
236
  ```json title="package.json"
237
237
  "scripts": {
238
- "lint-ga": "bunx node-actionlint && uvx zizmor --offline --collect=workflows ."
238
+ "lint-ga": "n-cursor lint-ga"
239
239
  }
240
240
  ```
241
241
 
242
+ > Не використовуй `npx --no @nitra/cursor lint-ga` — `bun run` автоматично транслює `npx` у `bun x`, а `bun x` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання. Виклик через bin-ім’я `n-cursor` працює і у `bun run`, і у `npm run`.
243
+
244
+ CLI виконує:
245
+
246
+ 1. Перевірку наявності [`shellcheck`](https://www.shellcheck.net/) у `PATH` — без нього `actionlint` мовчки пропускає shell-перевірки в `run:` блоках, тож локальний прогін зеленіє, а CI на `ubuntu-latest` (де shellcheck передвстановлений) падає на тих самих workflow. Якщо бінарника немає — `bun lint-ga` падає одразу, до запуску `actionlint`. Встановлення: `brew install shellcheck` (macOS), `sudo apt-get install -y shellcheck` (Debian/Ubuntu), `sudo pacman -S shellcheck` (Arch).
247
+ 2. `bunx github-actionlint`.
248
+ 3. `uvx zizmor --offline --collect=workflows .`.
249
+
242
250
  **`.github/zizmor.yml`:** для [unpinned-uses](https://docs.zizmor.sh/audits/#unpinned-uses) — політика **`ref-pin`**, якщо в `uses:` семантичні теги. За потреби вимкни [template-injection](https://docs.zizmor.sh/audits/#template-injection):
243
251
 
244
252
  ```yaml title=".github/zizmor.yml"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.164",
3
+ "version": "1.8.167",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -49,6 +49,6 @@
49
49
  "node": ">=25"
50
50
  },
51
51
  "devDependencies": {
52
- "@nitra/cursor": "^1.8.164"
52
+ "@nitra/cursor": "^1.8.167"
53
53
  }
54
54
  }
@@ -35,6 +35,7 @@ import {
35
35
  getStepUses,
36
36
  parseWorkflowYaml
37
37
  } from './utils/gha-workflow.mjs'
38
+ import { resolveCmd } from './utils/resolve-cmd.mjs'
38
39
 
39
40
  /** Шаблони наявності MegaLinter у вмісті workflow */
40
41
  const MEGALINTER_USE_PATTERNS = [/oxsecurity\/megalinter-action/i, /megalinter\/megalinter/i]
@@ -738,16 +739,43 @@ async function checkLintGaScript(passFn, failFn) {
738
739
  return
739
740
  }
740
741
  passFn('package.json містить lint-ga')
741
- if (lg.includes('node-actionlint')) {
742
- passFn('lint-ga викликає node-actionlint')
742
+ // Канонічний скрипт делегує виконання CLI `n-cursor lint-ga` (bin з `@nitra/cursor`) — там preflight
743
+ // на shellcheck + послідовно `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .`.
744
+ // Виклик через bin-ім’я `n-cursor`, а не `npx --no @nitra/cursor`, бо `bun run` транслює `npx` у `bun x`,
745
+ // а `bun x @nitra/cursor` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання.
746
+ if (/\bn-cursor\s+lint-ga\b/.test(lg)) {
747
+ passFn('lint-ga делегує CLI n-cursor lint-ga (preflight shellcheck + actionlint + zizmor)')
743
748
  } else {
744
- failFn('lint-ga має містити bunx node-actionlint (ga.mdc)')
749
+ failFn(
750
+ 'lint-ga має бути "n-cursor lint-ga" — CLI робить preflight shellcheck перед actionlint/zizmor (ga.mdc)'
751
+ )
745
752
  }
746
- if (lg.includes('zizmor') && lg.includes('--offline')) {
747
- passFn('lint-ga викликає zizmor з --offline')
748
- } else {
749
- failFn('lint-ga має містити zizmor і --offline (ga.mdc)')
753
+ }
754
+
755
+ /**
756
+ * Перевіряє наявність локального `shellcheck` у PATH. `actionlint` (`bunx github-actionlint`)
757
+ * запускає shell-перевірки в кроках `run:` workflow тільки коли `shellcheck` доступний; інакше
758
+ * мовчки пропускає SC-правила, через що локальний `bun lint-ga` зелений, а CI на ubuntu-latest
759
+ * (де shellcheck передвстановлений) падає. Тому відсутність бінарника локально — `fail`.
760
+ *
761
+ * Кросплатформно: `resolveCmd` використовує `which`/`where` і однаково знаходить `shellcheck`
762
+ * та `shellcheck.exe` на Windows.
763
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
764
+ * @param {(msg: string) => void} failFn callback при помилці
765
+ */
766
+ function checkShellcheckInstalled(passFn, failFn) {
767
+ if (resolveCmd('shellcheck')) {
768
+ passFn('shellcheck встановлений локально, actionlint виконуватиме SC-правила, як у CI')
769
+ return
750
770
  }
771
+ failFn(
772
+ [
773
+ 'shellcheck не знайдено в PATH — actionlint без нього мовчки пропускає shell-перевірки в run: блоках,',
774
+ 'тому локальний `bun lint-ga` буде зелений, а CI на ubuntu-latest (де shellcheck передвстановлений) падатиме.',
775
+ 'Встанови: macOS — `brew install shellcheck`; Debian/Ubuntu — `sudo apt-get install -y shellcheck`;',
776
+ 'Arch — `sudo pacman -S shellcheck` (ga.mdc)'
777
+ ].join(' ')
778
+ )
751
779
  }
752
780
 
753
781
  /**
@@ -978,6 +1006,7 @@ export async function check() {
978
1006
  await checkLintGaScript(pass, fail)
979
1007
  await checkLintGaWorkflow(wfDir, pass, fail)
980
1008
  await checkGitAiWorkflow(wfDir, pass, fail)
1009
+ checkShellcheckInstalled(pass, fail)
981
1010
 
982
1011
  return reporter.getExitCode()
983
1012
  }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * CLI-обгортка над канонічним `lint-ga` (ga.mdc): додає preflight на наявність `shellcheck`,
3
+ * тоді послідовно виконує `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .`.
4
+ *
5
+ * Без preflight `actionlint` (через `bunx github-actionlint`) мовчки пропускає shell-перевірки в
6
+ * `run:` блоках, коли `shellcheck` відсутній у PATH; локально `bun lint-ga` лишається зеленим, а CI
7
+ * на ubuntu-latest (де shellcheck передвстановлений) падає. Preflight робить цю різницю явною.
8
+ *
9
+ * Експортовано окремо `runLintGaCli` — використовується з `bin/n-cursor.js` як підкоманда `lint-ga`.
10
+ */
11
+ import { spawnSync } from 'node:child_process'
12
+ import { platform } from 'node:process'
13
+
14
+ import { resolveCmd } from './utils/resolve-cmd.mjs'
15
+
16
+ /** Підказки встановлення shellcheck на типових платформах. */
17
+ const SHELLCHECK_INSTALL_HINTS = [
18
+ 'macOS: brew install shellcheck',
19
+ 'Debian/Ubuntu: sudo apt-get install -y shellcheck',
20
+ 'Arch: sudo pacman -S shellcheck'
21
+ ]
22
+
23
+ /**
24
+ * Друкує блок з причиною fail і командами встановлення `shellcheck`.
25
+ * @returns {void}
26
+ */
27
+ function printShellcheckMissingMessage() {
28
+ console.error('❌ shellcheck не знайдено в PATH.')
29
+ console.error(' Без нього `actionlint` пропускає shell-перевірки в run: блоках,')
30
+ console.error(' тож локальний прогін зеленіє, а CI на ubuntu-latest (де shellcheck')
31
+ console.error(' передвстановлений) падає на тих самих workflow. Встанови:')
32
+ for (const line of SHELLCHECK_INSTALL_HINTS) {
33
+ console.error(` ${line}`)
34
+ }
35
+ console.error(' Деталі: ga.mdc → секція про lint-ga.')
36
+ }
37
+
38
+ /**
39
+ * Запускає крок lint-ga з відображенням команди користувачу. Stdout/stderr дочірнього процесу
40
+ * передається користувачу як є (`stdio: 'inherit'`), щоб виглядало як прямий виклик у shell.
41
+ * @param {string} title заголовок для логу (наприклад `actionlint`)
42
+ * @param {string} cmd ім'я команди (`bunx`, `uvx`)
43
+ * @param {string[]} args аргументи команди
44
+ * @returns {number} код виходу дочірнього процесу (0 — OK, інше — помилка)
45
+ */
46
+ function runStep(title, cmd, args) {
47
+ console.log(`\n▶ ${title}: ${cmd} ${args.join(' ')}`)
48
+ const resolved = resolveCmd(cmd)
49
+ if (!resolved) {
50
+ console.error(`❌ ${cmd} не знайдено в PATH (${title}).`)
51
+ return 127
52
+ }
53
+ const r = spawnSync(resolved, args, { stdio: 'inherit', env: process.env })
54
+ if (r.error) {
55
+ console.error(`❌ Не вдалося запустити ${cmd}: ${r.error.message}`)
56
+ return 1
57
+ }
58
+ return r.status ?? 1
59
+ }
60
+
61
+ /**
62
+ * Виконує канонічний `lint-ga` з preflight на `shellcheck`.
63
+ *
64
+ * Послідовність:
65
+ * 1) перевірка наявності `shellcheck` у PATH (на Windows — `shellcheck.exe`); відсутній → exit 1;
66
+ * 2) `bunx github-actionlint`;
67
+ * 3) `uvx zizmor --offline --collect=workflows .`.
68
+ *
69
+ * Першу помилку повертаємо як код виходу; наступні кроки не запускаються (відповідає `&&` у package.json).
70
+ * @returns {number} 0 — все OK, інакше — код першого кроку, що впав
71
+ */
72
+ export function runLintGaCli() {
73
+ const shellcheckBin = platform === 'win32' ? 'shellcheck.exe' : 'shellcheck'
74
+ if (!resolveCmd(shellcheckBin)) {
75
+ printShellcheckMissingMessage()
76
+ return 1
77
+ }
78
+ console.log('✅ shellcheck знайдено в PATH — actionlint виконуватиме SC-правила, як у CI')
79
+
80
+ const actionlintCode = runStep('actionlint', 'bunx', ['github-actionlint'])
81
+ if (actionlintCode !== 0) return actionlintCode
82
+
83
+ const zizmorCode = runStep('zizmor', 'uvx', ['zizmor', '--offline', '--collect=workflows', '.'])
84
+ return zizmorCode
85
+ }
@@ -14,7 +14,9 @@ import { platform } from 'node:process'
14
14
  */
15
15
  export function resolveCmd(cmd) {
16
16
  const whichCmd = platform === 'win32' ? 'where' : 'which'
17
- const result = spawnSync(whichCmd, [cmd], { encoding: 'utf8' })
17
+ // Явно передаємо `process.env`, інакше Bun вмикає snapshot оточення на старті процесу
18
+ // і не бачить змін `process.env.PATH` у runtime (типова ситуація — підстановка стабів у тестах).
19
+ const result = spawnSync(whichCmd, [cmd], { encoding: 'utf8', env: process.env })
18
20
  if (result.status !== 0 || result.error) {
19
21
  return null
20
22
  }