@nitra/cursor 1.5.1 → 1.5.3

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/mdc/ga.mdc CHANGED
@@ -41,6 +41,9 @@ on:
41
41
  jobs:
42
42
  cleanup_old_workflows:
43
43
  runs-on: ubuntu-latest
44
+ permissions:
45
+ actions: write
46
+ contents: read
44
47
  steps:
45
48
  - name: Delete workflow runs
46
49
  uses: dmvict/clean-workflow-runs@v1.0.0
@@ -67,6 +70,8 @@ on:
67
70
  jobs:
68
71
  cleanup_old_branches:
69
72
  runs-on: ubuntu-latest
73
+ permissions:
74
+ contents: write
70
75
  steps:
71
76
  - id: delete_stuff
72
77
  name: Delete those pesky dead branches
@@ -78,11 +83,68 @@ jobs:
78
83
  dry_run: no
79
84
 
80
85
  - name: Get output
81
- run: "echo 'Deleted branches: ${{ steps.delete_stuff.outputs.deleted_branches }}'"
86
+ env:
87
+ DELETED_BRANCHES: ${{ steps.delete_stuff.outputs.deleted_branches }}
88
+ run: |
89
+ echo "Deleted branches: ${DELETED_BRANCHES}"
82
90
  ```
83
91
 
84
92
  якщо в ignore_branches задані інші бранчі, то це допустимо.
85
93
 
94
+ Повинен бути файл .github/workflows/lint-ga.yml, зі змістом:
95
+
96
+ ```yaml
97
+ name: Lint GA
98
+
99
+ on:
100
+ push:
101
+ branches:
102
+ - dev
103
+ paths:
104
+ - '.github/workflows/**'
105
+ - '.github/zizmor.yml'
106
+ - 'package.json'
107
+ - 'bun.lock'
108
+
109
+ pull_request:
110
+ branches:
111
+ - dev
112
+
113
+ concurrency:
114
+ group: ${{ github.ref }}-${{ github.workflow }}
115
+ cancel-in-progress: true
116
+
117
+ jobs:
118
+ lint-ga:
119
+ runs-on: ubuntu-latest
120
+ permissions:
121
+ contents: read
122
+ steps:
123
+ - uses: actions/checkout@v4
124
+ with:
125
+ persist-credentials: false
126
+
127
+ - uses: oven-sh/setup-bun@v2
128
+
129
+ - name: Cache Bun dependencies
130
+ uses: actions/cache@v4
131
+ with:
132
+ path: |
133
+ ~/.bun/install/cache
134
+ node_modules
135
+ key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
136
+ restore-keys: |
137
+ ${{ runner.os }}-bun-
138
+
139
+ - name: Install dependencies
140
+ run: bun install --frozen-lockfile
141
+
142
+ - uses: astral-sh/setup-uv@v6
143
+
144
+ - name: Lint GA
145
+ run: bun run lint-ga
146
+ ```
147
+
86
148
  в файлі .vscode/extensions.json є налаштування для GitHub Actions:
87
149
 
88
150
  ```json title=".vscode/extensions.json"
@@ -91,6 +153,29 @@ jobs:
91
153
  }
92
154
  ```
93
155
 
156
+ ## actionlint
157
+
158
+ Статична перевірка синтаксису та виразів GitHub Actions: [actionlint](https://github.com/rhysd/actionlint).
159
+
160
+ У кореневому `package.json` у скрипті `lint-ga` викликай **actionlint** через **`bunx node-actionlint`** (пакет [node-actionlint](https://www.npmjs.com/package/node-actionlint) постає бінарник actionlint для Node-екосистеми).
161
+
162
+ ## zizmor
163
+
164
+ Статичний аналіз безпеки для GitHub Actions: [zizmor documentation](https://docs.zizmor.sh).
165
+
166
+ У кореневому `package.json` має бути скрипт:
167
+
168
+ ```json title="package.json"
169
+ "scripts": {
170
+ "lint-ga": "bunx node-actionlint && uvx zizmor --offline --collect=workflows ."
171
+ }
172
+ ```
173
+
174
+ Параметр `--offline` обмежує аналіз офлайн-аудитами (без GitHub API);
175
+
176
+ За замовчуванням audit [unpinned-uses](https://docs.zizmor.sh/audits/#unpinned-uses) вимагає повний commit SHA для кожного `uses:`. У всих проєктах прийняті **семантичні теги** (`@v4`, `@v2` тощо), додай `.github/zizmor.yml` з політикою `ref-pin` (приклад у цьому репозиторії).
177
+
94
178
  ## Перевірка
95
179
 
96
- `npx @nitra/cursor check ga`
180
+ - `bun run lint-ga` — actionlint (node-actionlint) і zizmor
181
+ - `npx @nitra/cursor check ga`
package/mdc/js-lint.mdc CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  description: Перевірка JavaScript коду
3
3
  alwaysApply: true
4
- version: '1.2'
4
+ version: '1.4'
5
5
  ---
6
6
 
7
- Перевірка виконується за допомогою **oxlint** та **ESLint**.
7
+ Перевірка виконується за допомогою **oxlint**, **ESLint** та **jscpd** (дублікати коду).
8
8
 
9
9
  У файлі `.vscode/extensions.json` мають бути рекомендації розширень:
10
10
 
@@ -18,18 +18,30 @@ version: '1.2'
18
18
  }
19
19
  ```
20
20
 
21
- У кореневому `package.json` додай скрипт і залежність:
21
+ У кореневому `package.json` додай скрипт і залежності:
22
22
 
23
23
  ```json title="package.json"
24
24
  "scripts": {
25
- "lint-js": "oxlint --fix && bunx eslint --fix ."
25
+ "lint-js": "oxlint --fix && bunx eslint --fix . && bunx jscpd ."
26
26
  },
27
27
  "devDependencies": {
28
- "@nitra/eslint-config": "^3.3.0"
28
+ "@nitra/eslint-config": "^3.4.0"
29
29
  }
30
30
  ```
31
31
 
32
- У `devDependencies` достатньо `@nitra/eslint-config` (залежності ESLint підтягуються разом із ним). **oxlint** і **eslint** у скриптах викликай через `bunx`; окремо не додавай пакети `oxlint` / `eslint`, якщо цього не вимагає ваш монорепо.
32
+ У `devDependencies` достатньо `@nitra/eslint-config` (залежності ESLint підтягуються разом із ним). **oxlint**, **eslint** та **jscpd** у скриптах викликай через `bunx`; окремо не додавай пакети `oxlint` / `eslint` / `jscpd`, якщо цього не вимагає ваш монорепо.
33
+
34
+ У корені проєкту має бути `.jscpd.json`. Мінімум: увімкнути облік `.gitignore`, ненульовий код виходу при знаходженні клонів, консольний звіт. За потреби додай `ignore` (дзеркальні каталоги, шаблони) та `minLines`, щоб відсікти дрібні збіги:
35
+
36
+ ```json title=".jscpd.json"
37
+ {
38
+ "gitignore": true,
39
+ "exitCode": 1,
40
+ "reporters": ["console"],
41
+ "minLines": 25,
42
+ "ignore": []
43
+ }
44
+ ```
33
45
 
34
46
  Додай workflow `.github/workflows/lint-js.yml`:
35
47
 
@@ -49,6 +61,7 @@ on:
49
61
  - '**/*.tsx'
50
62
  - '**/*.vue'
51
63
  - '**/eslint.config.*'
64
+ - '.jscpd.json'
52
65
 
53
66
  pull_request:
54
67
  branches:
@@ -61,8 +74,12 @@ concurrency:
61
74
  jobs:
62
75
  eslint:
63
76
  runs-on: ubuntu-latest
77
+ permissions:
78
+ contents: read
64
79
  steps:
65
80
  - uses: actions/checkout@v4
81
+ with:
82
+ persist-credentials: false
66
83
 
67
84
  - uses: oven-sh/setup-bun@v2
68
85
 
@@ -83,6 +100,7 @@ jobs:
83
100
  run: |
84
101
  bunx oxlint
85
102
  bunx eslint .
103
+ bunx jscpd .
86
104
  ```
87
105
 
88
106
  **Без дублювання CI:** якщо в `.github/workflows` уже є `lint.yml` з тими самими кроками `oxlint` або `eslint`, видали зайвий workflow, а саме `lint.yml` — лінт JS має виконуватися в одному місці.
package/mdc/text.mdc CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Обробка та перевірка текстових файлів (cspell, markdownlint-cli2, v8r, CI)
3
3
  alwaysApply: true
4
- version: '1.10'
4
+ version: '1.12'
5
5
  ---
6
6
 
7
7
  Правило описує роботу з **текстовими файлами** в репозиторії: перевірка правопису через **cspell**, стиль **Markdown** через **markdownlint-cli2**, валідація **JSON/YAML/TOML** через **[v8r](https://chris48s.github.io/v8r/)** ([Schema Store](https://www.schemastore.org/)), розширення **DavidAnson.vscode-markdownlint** у Cursor/VS Code та інтеграція в CI.
@@ -42,7 +42,9 @@ version: '1.10'
42
42
 
43
43
  **Код 98:** якщо для glob **немає жодного файлу**, v8r дає **98**. Це стосується не лише TOML: репозиторій без `.yml`/`.yaml` зламає **один** спільний виклик `v8r` уже на `**/*.yml`. Окремі виклики з дозволом лише **98** покривають і такі репозиторії.
44
44
 
45
- За замовчуванням v8r враховує **`.gitignore`** і **`.v8rignore`**. Якщо для файлу немає схеми в каталозі, запуск завершиться з помилкою додай такі шляхи в **`.v8rignore`**.
45
+ За замовчуванням v8r враховує **`.gitignore`**. **`.v8rignore` не створюй завчасно** файл потрібен **лише коли** `bunx v8r` падає на JSON/YAML без схеми в каталозі (тоді додай у ignore відповідні glob’и).
46
+
47
+ Пакет **`v8r` у `devDependencies` не додавай** — у скрипті достатньо **`bunx v8r`**.
46
48
 
47
49
  ## Конфігурація markdownlint-cli2
48
50
 
@@ -84,7 +86,6 @@ on:
84
86
  - '.cspell.json'
85
87
  - '.gitignore'
86
88
  - '.markdownlint-cli2.jsonc'
87
- - '.v8rignore'
88
89
  - '**/*.js'
89
90
  - '**/*.ts'
90
91
  - '**/*.vue'
@@ -117,8 +118,12 @@ concurrency:
117
118
  jobs:
118
119
  text:
119
120
  runs-on: ubuntu-latest
121
+ permissions:
122
+ contents: read
120
123
  steps:
121
124
  - uses: actions/checkout@v4
125
+ with:
126
+ persist-credentials: false
122
127
 
123
128
  - uses: oven-sh/setup-bun@v2
124
129
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -25,6 +25,7 @@
25
25
  "files": [
26
26
  "mdc",
27
27
  "bin",
28
+ "schemas",
28
29
  "scripts",
29
30
  "skills",
30
31
  "AGENTS.template.md"
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://unpkg.com/@nitra/cursor/schemas/n-cursor.json",
4
+ "title": "n-cursor project config",
5
+ "description": "Конфігурація правил і skills для CLI @nitra/cursor (файл .n-cursor.json у корені репозиторію).",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "properties": {
9
+ "rules": {
10
+ "type": "array",
11
+ "description": "Ідентифікатори правил без префікса n- (відповідають файлам n-<id>.mdc).",
12
+ "items": {
13
+ "type": "string",
14
+ "minLength": 1
15
+ }
16
+ },
17
+ "skills": {
18
+ "type": "array",
19
+ "description": "Ідентифікатори skills без префікса n- (каталог .cursor/skills).",
20
+ "items": {
21
+ "type": "string",
22
+ "minLength": 1
23
+ }
24
+ }
25
+ },
26
+ "required": ["rules", "skills"]
27
+ }
@@ -30,7 +30,7 @@ export async function check() {
30
30
  pass('Всі workflows мають розширення .yml')
31
31
  }
32
32
 
33
- for (const f of ['clean-ga-workflows.yml', 'clean-merged-branch.yml']) {
33
+ for (const f of ['clean-ga-workflows.yml', 'clean-merged-branch.yml', 'lint-ga.yml']) {
34
34
  if (files.includes(f)) {
35
35
  pass(`${f} існує`)
36
36
  } else {
@@ -27,8 +27,13 @@ export async function check() {
27
27
 
28
28
  if (pkg.scripts?.['lint-js']) {
29
29
  pass('package.json містить скрипт lint-js')
30
+ if (String(pkg.scripts['lint-js']).includes('jscpd')) {
31
+ pass('lint-js містить jscpd')
32
+ } else {
33
+ fail('lint-js має викликати jscpd — додай "&& bunx jscpd ." у кінець скрипта')
34
+ }
30
35
  } else {
31
- fail('package.json не містить скрипт "lint-js" — додай: "oxlint --fix && bunx eslint --fix ."')
36
+ fail('package.json не містить скрипт "lint-js" — додай: "oxlint --fix && bunx eslint --fix . && bunx jscpd ."')
32
37
  }
33
38
 
34
39
  if (pkg.devDependencies?.['@nitra/eslint-config']) {
@@ -63,10 +68,40 @@ export async function check() {
63
68
  } else {
64
69
  fail('lint-js.yml не містить eslint')
65
70
  }
71
+ if (content.includes('jscpd')) {
72
+ pass('lint-js.yml містить jscpd')
73
+ } else {
74
+ fail('lint-js.yml не містить jscpd — додай крок bunx jscpd .')
75
+ }
66
76
  } else {
67
77
  fail('.github/workflows/lint-js.yml не існує — створи його')
68
78
  }
69
79
 
80
+ if (existsSync('.jscpd.json')) {
81
+ let jscpdCfg
82
+ try {
83
+ jscpdCfg = JSON.parse(await readFile('.jscpd.json', 'utf8'))
84
+ } catch {
85
+ fail('.jscpd.json не є валідним JSON')
86
+ jscpdCfg = null
87
+ }
88
+ if (jscpdCfg) {
89
+ pass('.jscpd.json існує')
90
+ if (jscpdCfg.gitignore === true) {
91
+ pass('.jscpd.json: gitignore увімкнено')
92
+ } else {
93
+ fail('.jscpd.json має містити "gitignore": true')
94
+ }
95
+ if (jscpdCfg.exitCode === 1) {
96
+ pass('.jscpd.json: exitCode 1 при дублікатах')
97
+ } else {
98
+ fail('.jscpd.json має містити "exitCode": 1 (інакше CI не впаде на клонах)')
99
+ }
100
+ }
101
+ } else {
102
+ fail('.jscpd.json не існує — створи з gitignore, exitCode та reporters згідно js-lint.mdc')
103
+ }
104
+
70
105
  for (const dup of ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml']) {
71
106
  if (existsSync(dup)) fail(`Знайдено застарілий конфіг ESLint: ${dup} — видали, використовуй eslint.config.js`)
72
107
  }