@nitra/cursor 3.14.1 → 3.14.2

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.14.2] - 2026-06-02
4
+
5
+ ### Fixed
6
+
7
+ - Додано pull-requests: read до clean-merged-branch.yml, щоб cleanup action міг перевіряти PR-и комітів без 403.
8
+ - detectLevel: ASCII L0-дієслова (fix/typo/bump/rename/hotfix) матчаться цілим словом, а не підрядком — опис на кшталт 'add prefix validation' більше не дає хибний L0 (раніше 'fix' ловився в 'prefix'/'fixture'/'suffix'). Кириличні L0-ключі лишаються підрядком (стемінг).
9
+
3
10
  ## [3.14.1] - 2026-06-02
4
11
 
5
12
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "3.14.1",
3
+ "version": "3.14.2",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -49,8 +49,9 @@ deny contains msg if {
49
49
  }
50
50
 
51
51
  deny contains msg if {
52
- input.jobs.cleanup_old_branches.permissions.contents != expected_perms.contents
53
- msg := sprintf("clean-merged-branch.yml: permissions.contents має бути %s (ga.mdc)", [expected_perms.contents])
52
+ some permission, expected in expected_perms
53
+ object.get(input.jobs.cleanup_old_branches.permissions, permission, null) != expected
54
+ msg := sprintf("clean-merged-branch.yml: permissions.%s має бути %s (ga.mdc)", [permission, expected])
54
55
  }
55
56
 
56
57
  deny contains msg if {
@@ -17,6 +17,7 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  permissions:
19
19
  contents: write
20
+ pull-requests: read
20
21
  steps:
21
22
  - id: delete_stuff
22
23
  name: Delete those pesky dead branches
@@ -10,11 +10,38 @@
10
10
 
11
11
  /** L3 — велике/архітектурне. */
12
12
  const L3_KEYS = ['platform', 'migration', 'rewrite', 'architecture', 'enterprise', 'редизайн', 'міграц', 'переписат']
13
- /** L0 — тривіальне. */
14
- const L0_KEYS = ['fix', 'typo', 'bump', 'rename', 'hotfix', 'опечат', 'перейменув']
13
+ /** L0 — тривіальне. ASCII-дієслова: матч цілим словом (щоб `fix` не ловило `prefix`/`fixture`). */
14
+ const L0_WORD_KEYS = ['fix', 'typo', 'bump', 'rename', 'hotfix']
15
+ /** L0 — кириличні ключі: підрядком (стемінг: `перейменув` ловить `перейменування`). */
16
+ const L0_SUBSTR_KEYS = ['опечат', 'перейменув']
15
17
  /** L2 — багатофайлова фіча/рефактор. */
16
18
  const L2_KEYS = ['feature', 'epic', 'refactor', 'рефактор', 'фіча']
17
19
 
20
+ /**
21
+ * Чи символ — ASCII-літера/цифра (межа слова). `undefined` (край рядка) — не alnum.
22
+ * @param {string | undefined} ch символ
23
+ * @returns {boolean} результат
24
+ */
25
+ function isAsciiAlnum(ch) {
26
+ return ch !== undefined && ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
27
+ }
28
+
29
+ /**
30
+ * Чи містить `text` слово `word` із межами, що не є ASCII-alnum (без regex —
31
+ * конвенція файлу). Для ASCII L0-дієслів: `fix` у `prefix`/`fixture` не рахується.
32
+ * @param {string} text текст (lowercase)
33
+ * @param {string} word шукане ASCII-слово (lowercase)
34
+ * @returns {boolean} результат
35
+ */
36
+ function hasWord(text, word) {
37
+ let i = text.indexOf(word)
38
+ while (i !== -1) {
39
+ if (!isAsciiAlnum(text[i - 1]) && !isAsciiAlnum(text[i + word.length])) return true
40
+ i = text.indexOf(word, i + 1)
41
+ }
42
+ return false
43
+ }
44
+
18
45
  /**
19
46
  * Рівень складності задачі за описом: 0 (тривіальне) … 3 (архітектурне).
20
47
  * Пріоритет: L3 > L0 > L2 > дефолт L1.
@@ -24,8 +51,9 @@ const L2_KEYS = ['feature', 'epic', 'refactor', 'рефактор', 'фіча']
24
51
  export function detectLevel(desc) {
25
52
  const d = String(desc ?? '').toLowerCase()
26
53
  const has = keys => keys.some(k => d.includes(k))
54
+ const isL0 = L0_WORD_KEYS.some(k => hasWord(d, k)) || L0_SUBSTR_KEYS.some(k => d.includes(k))
27
55
  if (has(L3_KEYS)) return 3
28
- if (has(L0_KEYS)) return 0
56
+ if (isL0) return 0
29
57
  if (has(L2_KEYS)) return 2
30
58
  return 1
31
59
  }