@nitra/cursor 12.11.3 → 12.13.0
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 +13 -0
- package/bin/n-cursor.js +5 -22
- package/package.json +2 -1
- package/rules/bun/docs/index.md +2 -2
- package/rules/bun/docs/main.md +7 -8
- package/rules/bun/js/docs/index.md +3 -3
- package/rules/bun/main.json +1 -1
- package/rules/bun/main.mdc +3 -1
- package/rules/bun/main.mjs +24 -12
- package/rules/changelog/js/docs/index.md +3 -3
- package/rules/ci4/main.mdc +1 -0
- package/rules/ga/main.mdc +9 -1
- package/rules/js/js/docs/index.md +5 -5
- package/rules/js/main.mdc +4 -0
- package/rules/js-run/js/docs/index.md +3 -3
- package/rules/js-run/main.mdc +3 -0
- package/rules/npm-module/main.mdc +4 -0
- package/rules/python/docs/index.md +2 -2
- package/rules/python/docs/main.md +11 -10
- package/rules/python/main.mjs +49 -1
- package/rules/rego/main.mdc +2 -0
- package/rules/rust/docs/index.md +2 -2
- package/rules/rust/docs/main.md +8 -7
- package/rules/rust/main.json +1 -1
- package/rules/rust/main.mjs +16 -3
- package/rules/security/main.mdc +2 -0
- package/rules/style/js/docs/index.md +3 -3
- package/rules/style/main.mdc +5 -1
- package/rules/test/main.mdc +2 -1
- package/rules/text/main.mdc +9 -0
- package/scripts/docs/index.md +1 -0
- package/scripts/docs/update-blue-oak.md +28 -0
- package/scripts/lib/blue-oak.mjs +45 -0
- package/scripts/lib/discover-checkable-rules.mjs +7 -14
- package/scripts/lib/docs/blue-oak.md +29 -0
- package/scripts/lib/docs/index.md +35 -35
- package/scripts/lib/docs/run-rule.md +8 -6
- package/scripts/lib/fix/discover-t0-patterns.mjs +15 -48
- package/scripts/lib/fix/docs/index.md +12 -12
- package/scripts/lib/fix/docs/llm-worker.md +13 -8
- package/scripts/lib/fix/docs/t0.md +7 -7
- package/scripts/lib/fix/llm-worker.mjs +83 -3
- package/scripts/lib/fix/t0.mjs +1 -2
- package/scripts/lib/inline-template-links.mjs +9 -17
- package/scripts/lib/list-project-rules-mdc.mjs +3 -3
- package/scripts/lib/run-rule.mjs +0 -11
- package/scripts/update-blue-oak.mjs +51 -0
- package/scripts/utils/walkDir.mjs +32 -72
- package/skills/doc-aggregate/SKILL.md +1 -0
- package/skills/publish-telegram/SKILL.md +26 -28
- package/scripts/lib/check-mdc-template-refs.mjs +0 -57
- package/scripts/lib/docs/check-mdc-template-refs.md +0 -22
package/scripts/docs/index.md
CHANGED
|
@@ -19,4 +19,5 @@ resource: npm/scripts/
|
|
|
19
19
|
| [skills-cli.mjs](skills-cli.md) | JS Module |
|
|
20
20
|
| [sync-claude-config.mjs](sync-claude-config.md) | JS Module |
|
|
21
21
|
| [sync-setup-bun-deps-action.mjs](sync-setup-bun-deps-action.md) | JS Module |
|
|
22
|
+
| [update-blue-oak.mjs](update-blue-oak.md) | JS Module |
|
|
22
23
|
| [upgrade-nitra-cursor-and-install.mjs](upgrade-nitra-cursor-and-install.md) | JS Module |
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: update-blue-oak.mjs
|
|
4
|
+
resource: npm/scripts/update-blue-oak.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: f818f73b
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 95
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Оновлює вбудований список ліцензій Blue Oak Council, отримуючи дані з https://blueoakcouncil.org/list.json. Витягує SPDX-ідентифікатори ліцензій рівнів Model, Gold, Silver та Bronze і зберігає їх у npm/data/blue-oak.json. Цей процес запускається вручну у середовищі @nitra/cursor командою bun npm/scripts/update-blue-oak.mjs. Оновлення відбувається рідко, але нові permissive ліцензії з'являються раз на кілька місяців. Lead-рівень (найгірший, GPL-compatible) навмисно виключений.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
1. Завантажує дані з https://blueoakcouncil.org/list.json.
|
|
18
|
+
2. Перевіряє успішність отримання даних. У разі невдачі зупиняє виконання.
|
|
19
|
+
3. Парсить отриманий JSON-відповідь.
|
|
20
|
+
4. Ітерує по рейтингах у даних.
|
|
21
|
+
5. Для кожного рейтингу перевіряє, чи належить він до рівнів Model, Gold, Silver або Bronze.
|
|
22
|
+
6. Якщо рейтинг відповідає критеріям, витягує всі SPDX-ідентифікатори ліцензій, пов'язані з цим рейтингом, та додає їх до списку.
|
|
23
|
+
7. Формує об'єкт, що містить версію даних та зібраний список SPDX-ідентифікаторів.
|
|
24
|
+
8. Зберігає цей об'єкт у файл blue-oak.json у каталозі data.
|
|
25
|
+
|
|
26
|
+
## Гарантії поведінки
|
|
27
|
+
|
|
28
|
+
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Читає вбудований Blue Oak Council snapshot (`npm/data/blue-oak.json`).
|
|
3
|
+
* Повертає множину SPDX-ідентифікаторів рівнів Model+Gold+Silver+Bronze.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs'
|
|
6
|
+
import { dirname, join } from 'node:path'
|
|
7
|
+
import { fileURLToPath } from 'node:url'
|
|
8
|
+
|
|
9
|
+
const DATA_PATH = join(dirname(dirname(dirname(fileURLToPath(import.meta.url)))), 'data', 'blue-oak.json')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Множина SPDX-ідентифікаторів Blue Oak Bronze і вище (Model+Gold+Silver+Bronze).
|
|
13
|
+
* Ліцензії з цього списку вважаються permissive-safe для комерційного проєкту.
|
|
14
|
+
* @returns {Set<string>}
|
|
15
|
+
*/
|
|
16
|
+
export function getBronzeAndAbove() {
|
|
17
|
+
const { bronzeAndAbove } = JSON.parse(readFileSync(DATA_PATH, 'utf8'))
|
|
18
|
+
return new Set(bronzeAndAbove)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Генерує TOML-рядок `[licenses]` для `deny.toml` (cargo-deny) на основі Blue Oak Bronze+.
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
export function generateDenyTomlLicenses() {
|
|
26
|
+
const ids = [...getBronzeAndAbove()].toSorted()
|
|
27
|
+
const lines = ids.map(id => ` "${id}",`).join('\n')
|
|
28
|
+
return `[licenses]\nallow = [\n${lines}\n]\n`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Перевіряє SPDX-вираз проти Blue Oak Bronze+ allowlist.
|
|
33
|
+
* Підтримує: одиночний ID, `A OR B` (будь-який дозволений = OK), `A AND B` (усі мають бути дозволені).
|
|
34
|
+
* `NOASSERTION` і `NONE` завжди → false.
|
|
35
|
+
* @param {string} expression SPDX-вираз з pip-licenses або іншого інструмента
|
|
36
|
+
* @param {Set<string>} allowed множина дозволених SPDX-ідентифікаторів
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
export function isSpdxAllowed(expression, allowed) {
|
|
40
|
+
if (!expression || expression === 'NOASSERTION' || expression === 'NONE') return false
|
|
41
|
+
const clean = s => s.trim().replace(/^\(|\)$/g, '')
|
|
42
|
+
if (expression.includes(' AND ')) return expression.split(' AND ').every(p => allowed.has(clean(p)))
|
|
43
|
+
if (expression.includes(' OR ')) return expression.split(' OR ').some(p => allowed.has(clean(p)))
|
|
44
|
+
return allowed.has(clean(expression))
|
|
45
|
+
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { existsSync } from 'node:fs'
|
|
18
18
|
import { readdir } from 'node:fs/promises'
|
|
19
19
|
import { join } from 'node:path'
|
|
20
|
+
import { globby } from 'globby'
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @typedef {object} JsConcern
|
|
@@ -44,20 +45,12 @@ import { join } from 'node:path'
|
|
|
44
45
|
*/
|
|
45
46
|
async function listJsConcerns(jsDir) {
|
|
46
47
|
if (!existsSync(jsDir)) return []
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (entry.name.endsWith('.test.mjs')) continue
|
|
54
|
-
if (entry.name.startsWith('fix-')) continue
|
|
55
|
-
if (entry.name.startsWith('_')) continue
|
|
56
|
-
if (entry.name.startsWith('.')) continue
|
|
57
|
-
const name = entry.name.slice(0, -'.mjs'.length)
|
|
58
|
-
concerns.push({ name })
|
|
59
|
-
}
|
|
60
|
-
return concerns.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
48
|
+
const files = await globby(['*.mjs', '!*.test.mjs', '!fix-*.mjs', '!_*'], {
|
|
49
|
+
cwd: jsDir,
|
|
50
|
+
onlyFiles: true,
|
|
51
|
+
gitignore: false
|
|
52
|
+
})
|
|
53
|
+
return files.map(f => ({ name: f.slice(0, -4) })).toSorted((a, b) => a.name.localeCompare(b.name))
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: blue-oak.mjs
|
|
4
|
+
resource: npm/scripts/lib/blue-oak.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 9039e57f
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Модуль читає конфігурацію Blue Oak Council (`blue-oak.json`) для визначення дозволених рівнів SPDX. Він повертає множину SPDX-ідентифікаторів рівнів Model, Gold, Silver та Bronze. Модуль також генерує правила заборони ліцензій у форматі TOML та перевіряє відповідність будь-якого SPDX-ідентифікатора цьому списку.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
getBronzeAndAbove повертає множину SPDX-ідентифікаторів рівнів Model+Gold+Silver+Bronze, які беруться з конфігурації blue-oak.json.
|
|
18
|
+
generateDenyTomlLicenses генерує TOML-рядок для `deny.toml` (cargo-deny), дозволяючи лише SPDX-ідентифікатори, що знаходяться у множині Blue Oak Bronze+.
|
|
19
|
+
isSpdxAllowed перевіряє, чи відповідає наданий SPDX-вираз дозволеним ідентифікаторам, враховуючи логічні оператори `AND` та `OR`.
|
|
20
|
+
|
|
21
|
+
## Публічний API
|
|
22
|
+
|
|
23
|
+
getBronzeAndAbove — Визначає набір SPDX-ідентифікаторів (Model, Gold, Silver, Bronze), які вважаються безпечними для комерційного використання.
|
|
24
|
+
generateDenyTomlLicenses — Створює конфігураційний рядок TOML для файлу `deny.toml`, що забороняє використання ліцензій нижче рівня Blue Oak Bronze.
|
|
25
|
+
isSpdxAllowed — Визначає, чи відповідає заданий SPDX-ідентифікатор дозволеному списку Blue Oak Bronze+ згідно з логікою (одиночний ID, `A OR B`, `A AND B`).
|
|
26
|
+
|
|
27
|
+
## Гарантії поведінки
|
|
28
|
+
|
|
29
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -6,39 +6,39 @@ resource: npm/scripts/lib/
|
|
|
6
6
|
|
|
7
7
|
# npm/scripts/lib
|
|
8
8
|
|
|
9
|
-
| Файл
|
|
10
|
-
|
|
11
|
-
| [assert-project-root.mjs](assert-project-root.md)
|
|
12
|
-
| [
|
|
13
|
-
| [
|
|
14
|
-
| [check-reporter.mjs](check-reporter.md)
|
|
15
|
-
| [diff-added-lines.mjs](diff-added-lines.md)
|
|
9
|
+
| Файл | Тип |
|
|
10
|
+
| --------------------------------------------------------------------------- | --------- |
|
|
11
|
+
| [assert-project-root.mjs](assert-project-root.md) | JS Module |
|
|
12
|
+
| [blue-oak.mjs](blue-oak.md) | JS Module |
|
|
13
|
+
| [changed-files.mjs](changed-files.md) | JS Module |
|
|
14
|
+
| [check-reporter.mjs](check-reporter.md) | JS Module |
|
|
15
|
+
| [diff-added-lines.mjs](diff-added-lines.md) | JS Module |
|
|
16
16
|
| [discover-check-rules-from-cursor.mjs](discover-check-rules-from-cursor.md) | JS Module |
|
|
17
|
-
| [discover-checkable-rules.mjs](discover-checkable-rules.md)
|
|
18
|
-
| [ensure-tool.mjs](ensure-tool.md)
|
|
19
|
-
| [generated-markdown.mjs](generated-markdown.md)
|
|
20
|
-
| [gha-workflow.mjs](gha-workflow.md)
|
|
21
|
-
| [inline-template-links.mjs](inline-template-links.md)
|
|
22
|
-
| [list-project-rules-mdc.mjs](list-project-rules-mdc.md)
|
|
23
|
-
| [list-rule-ids.mjs](list-rule-ids.md)
|
|
24
|
-
| [load-cursor-config.mjs](load-cursor-config.md)
|
|
25
|
-
| [mirror-parity.mjs](mirror-parity.md)
|
|
26
|
-
| [read-n-cursor-config-lite.mjs](read-n-cursor-config-lite.md)
|
|
27
|
-
| [resolve-target-files.mjs](resolve-target-files.md)
|
|
28
|
-
| [root-notice.mjs](root-notice.md)
|
|
29
|
-
| [rule-meta-helpers.mjs](rule-meta-helpers.md)
|
|
30
|
-
| [rule-meta.mjs](rule-meta.md)
|
|
31
|
-
| [rule-predicates.mjs](rule-predicates.md)
|
|
32
|
-
| [run-conftest-batch.mjs](run-conftest-batch.md)
|
|
33
|
-
| [run-lint-step.mjs](run-lint-step.md)
|
|
34
|
-
| [run-lint.mjs](run-lint.md)
|
|
35
|
-
| [run-rule-cli.mjs](run-rule-cli.md)
|
|
36
|
-
| [run-rule.mjs](run-rule.md)
|
|
37
|
-
| [run-standard-lint.mjs](run-standard-lint.md)
|
|
38
|
-
| [run-standard-rule.mjs](run-standard-rule.md)
|
|
39
|
-
| [skill-meta.mjs](skill-meta.md)
|
|
40
|
-
| [sync-gitignore-worktree.mjs](sync-gitignore-worktree.md)
|
|
41
|
-
| [template.mjs](template.md)
|
|
42
|
-
| [timing-summary.mjs](timing-summary.md)
|
|
43
|
-
| [workspaces.mjs](workspaces.md)
|
|
44
|
-
| [worktree-notice.mjs](worktree-notice.md)
|
|
17
|
+
| [discover-checkable-rules.mjs](discover-checkable-rules.md) | JS Module |
|
|
18
|
+
| [ensure-tool.mjs](ensure-tool.md) | JS Module |
|
|
19
|
+
| [generated-markdown.mjs](generated-markdown.md) | JS Module |
|
|
20
|
+
| [gha-workflow.mjs](gha-workflow.md) | JS Module |
|
|
21
|
+
| [inline-template-links.mjs](inline-template-links.md) | JS Module |
|
|
22
|
+
| [list-project-rules-mdc.mjs](list-project-rules-mdc.md) | JS Module |
|
|
23
|
+
| [list-rule-ids.mjs](list-rule-ids.md) | JS Module |
|
|
24
|
+
| [load-cursor-config.mjs](load-cursor-config.md) | JS Module |
|
|
25
|
+
| [mirror-parity.mjs](mirror-parity.md) | JS Module |
|
|
26
|
+
| [read-n-cursor-config-lite.mjs](read-n-cursor-config-lite.md) | JS Module |
|
|
27
|
+
| [resolve-target-files.mjs](resolve-target-files.md) | JS Module |
|
|
28
|
+
| [root-notice.mjs](root-notice.md) | JS Module |
|
|
29
|
+
| [rule-meta-helpers.mjs](rule-meta-helpers.md) | JS Module |
|
|
30
|
+
| [rule-meta.mjs](rule-meta.md) | JS Module |
|
|
31
|
+
| [rule-predicates.mjs](rule-predicates.md) | JS Module |
|
|
32
|
+
| [run-conftest-batch.mjs](run-conftest-batch.md) | JS Module |
|
|
33
|
+
| [run-lint-step.mjs](run-lint-step.md) | JS Module |
|
|
34
|
+
| [run-lint.mjs](run-lint.md) | JS Module |
|
|
35
|
+
| [run-rule-cli.mjs](run-rule-cli.md) | JS Module |
|
|
36
|
+
| [run-rule.mjs](run-rule.md) | JS Module |
|
|
37
|
+
| [run-standard-lint.mjs](run-standard-lint.md) | JS Module |
|
|
38
|
+
| [run-standard-rule.mjs](run-standard-rule.md) | JS Module |
|
|
39
|
+
| [skill-meta.mjs](skill-meta.md) | JS Module |
|
|
40
|
+
| [sync-gitignore-worktree.mjs](sync-gitignore-worktree.md) | JS Module |
|
|
41
|
+
| [template.mjs](template.md) | JS Module |
|
|
42
|
+
| [timing-summary.mjs](timing-summary.md) | JS Module |
|
|
43
|
+
| [workspaces.mjs](workspaces.md) | JS Module |
|
|
44
|
+
| [worktree-notice.mjs](worktree-notice.md) | JS Module |
|
|
@@ -3,23 +3,25 @@ type: JS Module
|
|
|
3
3
|
title: run-rule.mjs
|
|
4
4
|
resource: npm/scripts/lib/run-rule.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 500c35f6
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Оркестратор виконує логіку одного правила під CLI `fix`. Він послідовно застосовує фільтр застосовності з `js/applies.mjs`. Далі виконуються JS-концерни для перевірки, а потім запускаються Policy-концерни, що зчитують конфігурації з `target.json` та `.n-cursor.json`. Резолвер ділить кеш між концернами, а їхні коди виходу об'єднуються в єдиний результат правила.
|
|
12
14
|
|
|
13
15
|
## Поведінка
|
|
14
16
|
|
|
15
|
-
runTemplateSubsetConcern виконує перевірку концерну, де канон
|
|
17
|
+
runTemplateSubsetConcern виконує перевірку концерну, де канон (сніпет) береться з `template.json` та звіряється з вмістом актуальних файлів-таргетів, використовуючи логіку subset-of.
|
|
16
18
|
|
|
17
|
-
runRule оркеструє виконання одного правила, послідовно застосовуючи
|
|
19
|
+
runRule оркеструє виконання одного правила, послідовно застосовуючи applies-гейт, а потім виконуючи JS-концерни та policy-концерни, збираючи загальний exit-код.
|
|
18
20
|
|
|
19
21
|
## Публічний API
|
|
20
22
|
|
|
21
|
-
runTemplateSubsetConcern — Порівнює фактичний файл з
|
|
22
|
-
runRule — Виконує окреме правило,
|
|
23
|
+
runTemplateSubsetConcern — Порівнює фактичний файл з шаблоном, визначеним у `target.json`, перевіряючи, чи всі обов'язкові елементи з шаблону присутні у файлі.
|
|
24
|
+
runRule — Виконує окреме правило, яке проходить через етапи застосування, перевірки на JS-концерни та політики.
|
|
23
25
|
|
|
24
26
|
## Гарантії поведінки
|
|
25
27
|
|
|
@@ -6,42 +6,13 @@
|
|
|
6
6
|
* `t0.mjs` ініціалізує результат через top-level await (один раз при завантаженні модуля).
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync } from 'node:fs'
|
|
9
|
-
import { readdir } from 'node:fs/promises'
|
|
10
9
|
import { join } from 'node:path'
|
|
10
|
+
import { globby } from 'globby'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @typedef {{ id: string, test: (output: string) => boolean, apply: (output: string, cwd: string) => Promise<{ok: boolean, action: string}> | {ok: boolean, action: string} }} T0Pattern
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* Повертає абсолютні шляхи до `fix-*.mjs` файлів у директорії (плоско, без рекурсії).
|
|
18
|
-
* @param {string} dir абсолютний шлях до директорії
|
|
19
|
-
* @returns {Promise<string[]>}
|
|
20
|
-
*/
|
|
21
|
-
async function findFixFiles(dir) {
|
|
22
|
-
if (!existsSync(dir)) return []
|
|
23
|
-
const entries = await readdir(dir, { withFileTypes: true })
|
|
24
|
-
return entries
|
|
25
|
-
.filter(e => e.isFile() && e.name.startsWith('fix-') && e.name.endsWith('.mjs'))
|
|
26
|
-
.map(e => join(dir, e.name))
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Повертає абсолютні шляхи до `policy/{concern}/fix-*.mjs` у правилі.
|
|
31
|
-
* @param {string} policyDir абсолютний шлях `rules/{rule}/policy/`
|
|
32
|
-
* @returns {Promise<string[]>}
|
|
33
|
-
*/
|
|
34
|
-
async function findPolicyFixFiles(policyDir) {
|
|
35
|
-
if (!existsSync(policyDir)) return []
|
|
36
|
-
const entries = await readdir(policyDir, { withFileTypes: true })
|
|
37
|
-
const paths = []
|
|
38
|
-
for (const entry of entries) {
|
|
39
|
-
if (!entry.isDirectory()) continue
|
|
40
|
-
paths.push(...(await findFixFiles(join(policyDir, entry.name))))
|
|
41
|
-
}
|
|
42
|
-
return paths
|
|
43
|
-
}
|
|
44
|
-
|
|
45
16
|
/**
|
|
46
17
|
* Збирає всі T0-паттерни з `fix-*.mjs` файлів усіх правил у `rulesDir`.
|
|
47
18
|
* @param {string} rulesDir абсолютний шлях до `npm/rules/`
|
|
@@ -49,26 +20,22 @@ async function findPolicyFixFiles(policyDir) {
|
|
|
49
20
|
*/
|
|
50
21
|
export async function discoverT0Patterns(rulesDir) {
|
|
51
22
|
if (!existsSync(rulesDir)) return []
|
|
52
|
-
const ruleEntries = await readdir(rulesDir, { withFileTypes: true })
|
|
53
|
-
/** @type {T0Pattern[]} */
|
|
54
|
-
const allPatterns = []
|
|
55
|
-
|
|
56
|
-
for (const ruleEntry of ruleEntries) {
|
|
57
|
-
if (!ruleEntry.isDirectory() || ruleEntry.name.startsWith('.')) continue
|
|
58
|
-
const ruleDir = join(rulesDir, ruleEntry.name)
|
|
59
23
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
24
|
+
const relPaths = await globby(['*/js/fix-*.mjs', '*/policy/*/fix-*.mjs'], {
|
|
25
|
+
cwd: rulesDir,
|
|
26
|
+
onlyFiles: true,
|
|
27
|
+
gitignore: false
|
|
28
|
+
})
|
|
64
29
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
30
|
+
/** @type {T0Pattern[]} */
|
|
31
|
+
const allPatterns = []
|
|
32
|
+
for (const rel of relPaths) {
|
|
33
|
+
const fixPath = join(rulesDir, rel)
|
|
34
|
+
try {
|
|
35
|
+
const mod = await import(fixPath)
|
|
36
|
+
if (Array.isArray(mod.patterns)) allPatterns.push(...mod.patterns)
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error(`[discover-t0-patterns] не вдалося імпортувати ${fixPath}: ${err.message}`)
|
|
72
39
|
}
|
|
73
40
|
}
|
|
74
41
|
|
|
@@ -6,16 +6,16 @@ resource: npm/scripts/lib/fix/
|
|
|
6
6
|
|
|
7
7
|
# npm/scripts/lib/fix
|
|
8
8
|
|
|
9
|
-
| Файл
|
|
10
|
-
|
|
11
|
-
| [analyze-escalation.mjs](analyze-escalation.md)
|
|
12
|
-
| [discover-t0-patterns.mjs](discover-t0-patterns.md)
|
|
13
|
-
| [escalation-log.mjs](escalation-log.md)
|
|
14
|
-
| [llm-fix-apply.mjs](llm-fix-apply.md)
|
|
15
|
-
| [llm-lint-fix.mjs](llm-lint-fix.md)
|
|
16
|
-
| [llm-worker.mjs](llm-worker.md)
|
|
17
|
-
| [orchestrator.mjs](orchestrator.md)
|
|
9
|
+
| Файл | Тип |
|
|
10
|
+
| ----------------------------------------------------- | --------- |
|
|
11
|
+
| [analyze-escalation.mjs](analyze-escalation.md) | JS Module |
|
|
12
|
+
| [discover-t0-patterns.mjs](discover-t0-patterns.md) | JS Module |
|
|
13
|
+
| [escalation-log.mjs](escalation-log.md) | JS Module |
|
|
14
|
+
| [llm-fix-apply.mjs](llm-fix-apply.md) | JS Module |
|
|
15
|
+
| [llm-lint-fix.mjs](llm-lint-fix.md) | JS Module |
|
|
16
|
+
| [llm-worker.mjs](llm-worker.md) | JS Module |
|
|
17
|
+
| [orchestrator.mjs](orchestrator.md) | JS Module |
|
|
18
18
|
| [run-conformance-check.mjs](run-conformance-check.md) | JS Module |
|
|
19
|
-
| [t0.mjs](t0.md)
|
|
20
|
-
| [verbose-block.mjs](verbose-block.md)
|
|
21
|
-
| [vscode-ext-add.mjs](vscode-ext-add.md)
|
|
19
|
+
| [t0.mjs](t0.md) | JS Module |
|
|
20
|
+
| [verbose-block.mjs](verbose-block.md) | JS Module |
|
|
21
|
+
| [vscode-ext-add.mjs](vscode-ext-add.md) | JS Module |
|
|
@@ -3,26 +3,31 @@ type: JS Module
|
|
|
3
3
|
title: llm-worker.mjs
|
|
4
4
|
resource: npm/scripts/lib/fix/llm-worker.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 5d019a98
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль виділяє унікальні відносні шляхи файлів, пов'язаних із
|
|
13
|
+
Модуль виділяє унікальні відносні шляхи файлів, пов'язаних із порушеннями, та виконує LLM-виклик для виправлення одного rule-порушення. Підтримує вибір sub-check `.mdc` за конкретним target-файлом — замість повного `n-{id}.mdc` передає моделі лише релевантну секцію правила (1–2 KB замість 20+ KB).
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
extractFilePaths витягує унікальні відносні шляхи файлів із
|
|
18
|
-
|
|
17
|
+
`extractFilePaths` витягує унікальні відносні шляхи файлів із violation output, пріоритетно розбираючи рядки ❌ (файли що потребують фіксу), потім generic-regex для контексту. Розуміє workspace-prefix `[npm] path/file.ext → npm/path/file.ext`.
|
|
18
|
+
|
|
19
|
+
`selectSubCheckMdc` читає `policy/<concern>/target.json` з каталогу `npm/rules/{ruleId}/` пакету, знаходить concerns із `files.single`, що відповідають failing-файлам із violation output, і повертає конкатенацію відповідних `.mdc`. Повертає `null` якщо правило не має policy-підкаталогу або жоден concern не збігається з ❌-файлами.
|
|
20
|
+
|
|
21
|
+
`runLlmWorker` спочатку викликає `selectSubCheckMdc` — і якщо отримує match, передає моделі лише той sub-check `.mdc`. Якщо match немає — fallback на повний `n-{id}.mdc`. Далі читає файли з violation output, будує prompt, викликає LLM через `callLlmRich` і застосовує зміни.
|
|
19
22
|
|
|
20
23
|
## Публічний API
|
|
21
24
|
|
|
22
|
-
extractFilePaths —
|
|
23
|
-
|
|
25
|
+
`extractFilePaths(output)` — повертає унікальні відносні шляхи файлів з violation output (❌-рядки першими).
|
|
26
|
+
|
|
27
|
+
`runLlmWorker(ruleId, violationOutput, projectRoot, opts)` — виправляє одне порушення правила через LLM. Повертає `{ ok, error?, changes, diagnosis, reasoning, reasoningSource, promptSummary }`. `promptSummary.subCheckMdc: boolean` вказує чи використано точковий sub-check замість повного mdc.
|
|
24
28
|
|
|
25
29
|
## Гарантії поведінки
|
|
26
30
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
31
|
+
- Читає `policy/` безпосередньо з пакету (`import.meta.dirname/../../../rules/`), без pre-generation файлів.
|
|
32
|
+
- Fallback на повний `n-{id}.mdc` при: відсутності policy-підкаталогу, `walkGlob`-таргетах, відсутності ❌-файлів у violation, нульовому match.
|
|
33
|
+
- Перехоплює всі помилки LLM і повертає структурований `{ ok: false, error }`.
|
|
@@ -3,25 +3,25 @@ type: JS Module
|
|
|
3
3
|
title: t0.mjs
|
|
4
4
|
resource: npm/scripts/lib/fix/t0.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 4cee4f45
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль забезпечує
|
|
13
|
+
Модуль забезпечує автоматичне застосування паттернів T0-auto до виявлених порушень. Він фільтрує доступні правила за допомогою `filterT0AutoRules`, застосовує паттерни до порушень за допомогою `applyT0Auto` та запускає повний процес перевірки та виведення результатів через `runT0AutoCli`.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
applyT0Auto застосовує всі T0-auto паттерни до одного виявленого порушення, повертаючи результат застосування та список
|
|
18
|
-
filterT0AutoRules повертає список ID правил, для яких
|
|
19
|
-
runT0AutoCli
|
|
17
|
+
applyT0Auto застосовує всі T0-auto паттерни до одного виявленого порушення, повертаючи результат застосування та список дій.
|
|
18
|
+
filterT0AutoRules повертає список ID правил, для яких існує хоча б один T0-auto паттерн, виходячи з виявлених порушень.
|
|
19
|
+
runT0AutoCli виконує повний цикл T0-auto: запускає перевірку конформності, застосовує T0-auto до провальних правил, повторно перевіряє змінені правила та виводить підсумок.
|
|
20
20
|
|
|
21
21
|
## Публічний API
|
|
22
22
|
|
|
23
|
-
applyT0Auto — застосовує всі T0-auto шаблони до
|
|
24
|
-
filterT0AutoRules — визначає і повертає ідентифікатори правил, які мають
|
|
23
|
+
applyT0Auto — застосовує всі T0-auto шаблони до результату виявлення порушень.
|
|
24
|
+
filterT0AutoRules — визначає і повертає ідентифікатори правил, які мають відповідні T0-auto шаблони у результаті `fix --json`.
|
|
25
25
|
runT0AutoCli — виконує команду `n-cursor fix-t0 [rule...]`, яка запускає `fix --json`, застосовує T0-auto до кожного порушення, повторно перевіряє check-gate та виводить звіт.
|
|
26
26
|
|
|
27
27
|
## Гарантії поведінки
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @see ./docs/llm-worker.md */
|
|
2
2
|
|
|
3
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
import { env } from 'node:process'
|
|
6
6
|
import { resolveModel } from '../../../lib/models.mjs'
|
|
@@ -20,6 +20,83 @@ const API_KEY_RE = /api key/i
|
|
|
20
20
|
|
|
21
21
|
const FILE_EXTS = 'json|js|mjs|ts|vue|yml|yaml|toml|mdc|md|sh|py'
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Каталог `npm/rules/` у пакеті — для вибору sub-check .mdc.
|
|
25
|
+
* Шлях: <package>/npm/scripts/lib/fix/ → ../../.. → npm/ → rules/.
|
|
26
|
+
*/
|
|
27
|
+
const PACKAGE_RULES_DIR = join(import.meta.dirname, '..', '..', '..', 'rules')
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Витягує шляхи файлів лише з рядків ❌ у violation output.
|
|
31
|
+
* Без workspace-розгортання — повертає bare path для звірки з target.json.
|
|
32
|
+
* @param {string} output violation output
|
|
33
|
+
* @returns {string[]} унікальні шляхи з ❌-рядків
|
|
34
|
+
*/
|
|
35
|
+
function extractFailPaths(output) {
|
|
36
|
+
const seen = new Set()
|
|
37
|
+
const add = p => {
|
|
38
|
+
seen.add(p)
|
|
39
|
+
}
|
|
40
|
+
const failSep = `(?::\\d+)?(?::\\s|[\\s—]|$)`
|
|
41
|
+
// ❌ [ws] path/file.ext → strip workspace, зберігаємо bare file
|
|
42
|
+
const failWsRe = new RegExp(`^\\s*❌\\s+\\[[\\w-]+\\]\\s+([\\w./][\\w./-]*\\.(?:${FILE_EXTS}))${failSep}`, 'gm')
|
|
43
|
+
for (const m of output.matchAll(failWsRe)) add(m[1])
|
|
44
|
+
const failRe = new RegExp(`^\\s*❌\\s+(\\.?[\\w][\\w./-]*\\.(?:${FILE_EXTS}))${failSep}`, 'gm')
|
|
45
|
+
for (const m of output.matchAll(failRe)) add(m[1])
|
|
46
|
+
return [...seen]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Для правил з `policy/<concern>/` підбирає лише ті concern-.mdc, що відповідають
|
|
51
|
+
* файлам з ❌-рядків violation output. Читає безпосередньо з пакету (`PACKAGE_RULES_DIR`),
|
|
52
|
+
* тому не потребує pre-generation. Fallback — null (→ повний n-{id}.mdc у caller).
|
|
53
|
+
* @param {string} ruleId ID правила
|
|
54
|
+
* @param {string} violationOutput violation output
|
|
55
|
+
* @returns {string|null} конкатенація релевантних .mdc або null
|
|
56
|
+
*/
|
|
57
|
+
function selectSubCheckMdc(ruleId, violationOutput) {
|
|
58
|
+
const policyDir = join(PACKAGE_RULES_DIR, ruleId, 'policy')
|
|
59
|
+
if (!existsSync(policyDir)) return null
|
|
60
|
+
|
|
61
|
+
const failPaths = extractFailPaths(violationOutput)
|
|
62
|
+
if (failPaths.length === 0) return null
|
|
63
|
+
|
|
64
|
+
const matched = []
|
|
65
|
+
let concerns
|
|
66
|
+
try {
|
|
67
|
+
concerns = readdirSync(policyDir, { withFileTypes: true })
|
|
68
|
+
} catch {
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const entry of concerns) {
|
|
73
|
+
if (!entry.isDirectory()) continue
|
|
74
|
+
const concernDir = join(policyDir, entry.name)
|
|
75
|
+
const targetPath = join(concernDir, 'target.json')
|
|
76
|
+
if (!existsSync(targetPath)) continue
|
|
77
|
+
|
|
78
|
+
let target
|
|
79
|
+
try {
|
|
80
|
+
target = JSON.parse(readFileSync(targetPath, 'utf8'))
|
|
81
|
+
} catch {
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const targetFile = target?.files?.single
|
|
86
|
+
if (!targetFile) continue // walkGlob та інші типи → skip, fallback на main.mdc
|
|
87
|
+
|
|
88
|
+
// Перевіряємо чи хоч один failing path закінчується на targetFile
|
|
89
|
+
const hit = failPaths.some(p => p === targetFile || p.endsWith(`/${targetFile}`))
|
|
90
|
+
if (!hit) continue
|
|
91
|
+
|
|
92
|
+
const mdcEntry = readdirSync(concernDir).find(f => f.endsWith('.mdc'))
|
|
93
|
+
if (!mdcEntry) continue
|
|
94
|
+
matched.push(readFileSync(join(concernDir, mdcEntry), 'utf8').trim())
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return matched.length > 0 ? matched.join('\n\n') : null
|
|
98
|
+
}
|
|
99
|
+
|
|
23
100
|
/**
|
|
24
101
|
* Витягує відносні шляхи файлів із violation output.
|
|
25
102
|
* Розуміє workspace-prefix: `[npm] skills/foo.mjs` → `npm/skills/foo.mjs`.
|
|
@@ -185,9 +262,11 @@ export function runLlmWorker(ruleId, violationOutput, projectRoot, opts = {}) {
|
|
|
185
262
|
const timeoutMs = opts.timeoutMs
|
|
186
263
|
const thinkingBudget = opts.thinkingBudget ?? DEFAULT_THINKING_BUDGET
|
|
187
264
|
|
|
188
|
-
// 1. Читаємо rule .mdc
|
|
265
|
+
// 1. Читаємо rule .mdc: спробуємо sub-check mdc для конкретної перевірки,
|
|
266
|
+
// якщо не вдалося — fallback на повний n-{id}.mdc.
|
|
267
|
+
const subMdc = selectSubCheckMdc(ruleId, violationOutput)
|
|
189
268
|
const mdcPath = join(projectRoot, '.cursor', 'rules', `n-${ruleId}.mdc`)
|
|
190
|
-
const ruleMdc = existsSync(mdcPath) ? readFileSync(mdcPath, 'utf8') : '(rule file not found)'
|
|
269
|
+
const ruleMdc = subMdc ?? (existsSync(mdcPath) ? readFileSync(mdcPath, 'utf8') : '(rule file not found)')
|
|
191
270
|
|
|
192
271
|
// 2. Витягуємо файли з violation output і читаємо їх
|
|
193
272
|
const files = readFilesForFix(extractFilePaths(violationOutput), projectRoot)
|
|
@@ -195,6 +274,7 @@ export function runLlmWorker(ruleId, violationOutput, projectRoot, opts = {}) {
|
|
|
195
274
|
// 3. Будуємо summary промпту (для verbose-блоку) до виклику моделі
|
|
196
275
|
const promptSummary = {
|
|
197
276
|
ruleMdcLen: ruleMdc.length,
|
|
277
|
+
subCheckMdc: !!subMdc,
|
|
198
278
|
violationLen: violationOutput.length,
|
|
199
279
|
filesCount: files.length,
|
|
200
280
|
filesTotalBytes: files.reduce((s, f) => s + f.content.length, 0),
|
package/scripts/lib/fix/t0.mjs
CHANGED
|
@@ -5,7 +5,6 @@ import { fileURLToPath } from 'node:url'
|
|
|
5
5
|
import { discoverT0Patterns } from './discover-t0-patterns.mjs'
|
|
6
6
|
import { runConformanceCheck } from './run-conformance-check.mjs'
|
|
7
7
|
|
|
8
|
-
// Паттерни живуть у rule-level fix-*.mjs файлах; агрегуємо тут через discovery.
|
|
9
8
|
// Top-level await: ініціалізація один раз при завантаженні модуля.
|
|
10
9
|
const RULES_DIR = join(dirname(fileURLToPath(import.meta.url)), '../../../rules')
|
|
11
10
|
const PATTERNS = await discoverT0Patterns(RULES_DIR)
|
|
@@ -24,7 +23,7 @@ export async function applyT0Auto(ruleId, violationOutput, cwd) {
|
|
|
24
23
|
for (const p of PATTERNS) {
|
|
25
24
|
if (!p.test(violationOutput)) continue
|
|
26
25
|
// Патерн може бути sync ({ok,action}) або async (Promise) — await нормалізує обидва.
|
|
27
|
-
const result = await p.apply(violationOutput, cwd)
|
|
26
|
+
const result = await p.apply(violationOutput, cwd, { ruleId, rulesDir: RULES_DIR })
|
|
28
27
|
actions.push(`[${p.id}] ${result.action}`)
|
|
29
28
|
if (result.ok) applied = true
|
|
30
29
|
}
|