@nitra/cursor 12.11.0 → 12.11.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 +12 -0
- package/bin/n-cursor.js +9 -27
- package/package.json +1 -1
- package/rules/adr/js/docs/hooks.md +0 -2
- package/rules/bun/js/docs/fix-layout.md +25 -0
- package/rules/bun/js/fix-layout.mjs +55 -0
- package/rules/changelog/js/docs/consistency.md +11 -13
- package/rules/changelog/js/docs/fix-consistency.md +27 -0
- package/rules/changelog/js/docs/index.md +2 -2
- package/rules/changelog/js/fix-consistency.mjs +50 -0
- package/rules/ci4/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/ci4/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/ga/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/ga/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/ga/policy/workflow_common/workflow_common.rego +15 -0
- package/rules/graphql/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/graphql/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/js/js/docs/dep-policy.md +12 -10
- package/rules/js/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/js/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/js-run/js/docs/fix-runtime.md +25 -0
- package/rules/js-run/js/fix-runtime.mjs +41 -0
- package/rules/k8s/policy/lint_k8s_yml/lint_k8s_yml.rego +57 -0
- package/rules/k8s/policy/lint_k8s_yml/target.json +4 -0
- package/rules/k8s/policy/lint_k8s_yml/template/lint-k8s.yml.snippet.yml +43 -0
- package/rules/nginx-default-tpl/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/nginx-default-tpl/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/rego/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/rego/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/rust/policy/vscode_extensions/docs/fix-vscode_extensions.md +22 -0
- package/rules/rust/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/style/js/docs/fix-tooling.md +29 -0
- package/rules/style/js/fix-tooling.mjs +46 -0
- package/rules/style/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/style/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/tauri/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/tauri/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/text/policy/vscode_extensions/docs/fix-vscode_extensions.md +21 -0
- package/rules/text/policy/vscode_extensions/fix-vscode_extensions.mjs +1 -0
- package/rules/vue/js/docs/packages.md +0 -2
- package/scripts/docs/index.md +0 -2
- package/scripts/lib/discover-checkable-rules.mjs +1 -0
- package/scripts/lib/docs/discover-checkable-rules.md +13 -155
- package/scripts/lib/fix/discover-t0-patterns.mjs +83 -0
- package/scripts/lib/fix/docs/discover-t0-patterns.md +37 -0
- package/scripts/lib/fix/docs/llm-fix-apply.md +12 -10
- package/scripts/lib/fix/docs/llm-worker.md +6 -14
- package/scripts/lib/fix/docs/orchestrator.md +0 -2
- package/scripts/lib/fix/docs/t0.md +11 -10
- package/scripts/lib/fix/docs/vscode-ext-add.md +29 -0
- package/scripts/lib/fix/llm-fix-apply.mjs +34 -3
- package/scripts/lib/fix/llm-worker.mjs +24 -15
- package/scripts/lib/fix/t0.mjs +8 -119
- package/scripts/lib/fix/vscode-ext-add.mjs +45 -0
- package/rules/test/coverage/coverage.mjs +0 -317
- package/scripts/coverage-classify/apply.mjs +0 -67
- package/scripts/coverage-classify/cache.mjs +0 -77
- package/scripts/coverage-classify/docs/apply.md +0 -206
- package/scripts/coverage-classify/docs/cache.md +0 -207
- package/scripts/coverage-classify/docs/index.md +0 -14
- package/scripts/coverage-classify/docs/prompt.md +0 -136
- package/scripts/coverage-classify/docs/verdict-schema.md +0 -28
- package/scripts/coverage-classify/index.mjs +0 -114
- package/scripts/coverage-classify/prompt.mjs +0 -126
- package/scripts/coverage-classify/verdict-schema.mjs +0 -35
- package/scripts/coverage-fix-extract.mjs +0 -122
- package/scripts/coverage-fix.mjs +0 -119
- package/scripts/docs/coverage-fix-extract.md +0 -36
- package/scripts/docs/coverage-fix.md +0 -181
- package/skills/coverage-fix/SKILL.md +0 -131
- package/skills/coverage-fix/main.json +0 -1
|
@@ -3,27 +3,29 @@ type: JS Module
|
|
|
3
3
|
title: llm-fix-apply.mjs
|
|
4
4
|
resource: npm/scripts/lib/fix/llm-fix-apply.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 49f989ca
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Це спільне ядро LLM-фіксу, призначене для оркестрації процесу застосування змін, згенерованих моделлю. Модуль використовує `llm-worker.mjs` та `llm-lint-fix.mjs` для забезпечення конформності та виконання лінтер-фіксів. Основний функціонал включає: парсинг відповіді LLM за схемою `{changes:[{path,content}]}` (через `parseChangesResponse`), зчитування необхідних файлів (`readFilesForFix`), та безпечне застосування змін (`applyChanges`). Система реалізує механізм fail-safe, перехоплюючи помилки та повертаючи `null` замість винятків для певних сценаріїв. При цьому шляхи `.git` та `node_modules` свідомо ігноруються.
|
|
12
14
|
|
|
13
15
|
## Поведінка
|
|
14
16
|
|
|
15
|
-
parseChangesResponse парсить сирий текст відповіді моделі,
|
|
16
|
-
readFilesForFix
|
|
17
|
-
applyChanges записує
|
|
17
|
+
parseChangesResponse парсить сирий текст відповіді моделі, витягуючи структуру змін у форматі патчу, або повертає null, якщо парсинг неможливий.
|
|
18
|
+
readFilesForFix читає вміст файлів, вказаних у списку, відносно кореня проєкту. Якщо прямий шлях не знайдено, шукає файл за його базовим ім'ям у проєкті, ігноруючи каталоги `.git` та `node_modules`.
|
|
19
|
+
applyChanges записує вміст, наданий у змінних, у відповідні файли проєкту, створюючи необхідні каталоги, якщо вони відсутні.
|
|
18
20
|
|
|
19
21
|
## Публічний API
|
|
20
22
|
|
|
21
|
-
parseChangesResponse —
|
|
22
|
-
readFilesForFix — зчитує вміст файлів за заданими
|
|
23
|
-
applyChanges — замінює вміст файлів на
|
|
23
|
+
parseChangesResponse — розбирає JSON-відповідь моделі, витягуючи вміст першого об'єкта.
|
|
24
|
+
readFilesForFix — зчитує вміст файлів за заданими шляхами, шукаючи їх у системі, якщо прямий шлях не знайдено.
|
|
25
|
+
applyChanges — замінює вміст файлів на новий, наданий у змінній `changes`.
|
|
24
26
|
|
|
25
27
|
## Гарантії поведінки
|
|
26
28
|
|
|
27
29
|
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
28
|
-
- За
|
|
29
|
-
-
|
|
30
|
+
- За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
|
|
31
|
+
- Свідомо пропускає шляхи: `.git`, `node_modules`.
|
|
@@ -3,32 +3,24 @@ 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: 55419474
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Огляд
|
|
12
12
|
|
|
13
|
-
Модуль
|
|
13
|
+
Модуль виділяє унікальні відносні шляхи файлів, пов'язаних із порушеннями. Він також виконує роботу з великою мовною моделлю для аналізу даних про порушення, зчитуючи правила та обробляючи відповідні файли.
|
|
14
14
|
|
|
15
15
|
## Поведінка
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
3. Витягує шляхи до файлів, які порушені, з вихідних даних про порушення.
|
|
20
|
-
4. Зчитує вміст цих файлів.
|
|
21
|
-
5. Формує повний текстовий запит для моделі, включаючи правило, вихідні дані про порушення, вміст файлів та, за наявності, контекст попередньої спроби.
|
|
22
|
-
6. Відправляє цей запит моделі.
|
|
23
|
-
7. У разі помилки при виклику моделі, повертає статус невдачі з деталями помилки.
|
|
24
|
-
8. Якщо модель відповідає, парсить її відповідь, щоб отримати діагноз та список змін.
|
|
25
|
-
9. У разі неможливості розібрати відповідь або відсутності змін, повертає статус невдачі.
|
|
26
|
-
10. Якщо зміни успішно отримані, застосовує ці зміни до файлової системи проєкту.
|
|
27
|
-
11. Повертає результат, що містить статус успіху, список застосованих змін, діагноз та метадані про запит.
|
|
17
|
+
extractFilePaths витягує унікальні відносні шляхи файлів із вихідних даних про порушення, розпізнаючи як явні файли, що потребують виправлення, так і файли, що надають контекст.
|
|
18
|
+
runLlmWorker викликає LLM для виправлення одного порушення, зчитує відповідне правило, аналізує файли, формує промпт, отримує відповідь від моделі та застосовує виправлення.
|
|
28
19
|
|
|
29
20
|
## Публічний API
|
|
30
21
|
|
|
31
|
-
|
|
22
|
+
extractFilePaths — виділяє відносні шляхи файлів з виводу помилок, обробляючи префікс робочого простору та пріоритетно парсячи рядки, що вказують на необхідність виправлення.
|
|
23
|
+
runLlmWorker — виправляє одне порушення правила за допомогою LLM, повертаючи результати змін чи діагнозу, що слугує інформацією для наступних етапів процесу.
|
|
32
24
|
|
|
33
25
|
## Гарантії поведінки
|
|
34
26
|
|
|
@@ -8,8 +8,6 @@ docgen:
|
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Огляд
|
|
12
|
-
|
|
13
11
|
Модуль відповідає за управління процесом вирішення порушень. Він будує послідовність тирів ескалації за допомогою `buildLadder`. Функція `parseOrchestratorArgs` визначає бюджет LLM та фільтр правил. Далі, `runOrchestratorCli` виконує процес виправлення правил, послідовно застосовуючи `escalateRule` по тирах до досягнення першого успішного вирішення.
|
|
14
12
|
|
|
15
13
|
## Поведінка
|
|
@@ -3,26 +3,27 @@ type: JS Module
|
|
|
3
3
|
title: t0.mjs
|
|
4
4
|
resource: npm/scripts/lib/fix/t0.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 92c9348d
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Модуль забезпечує механізм автоматичного застосування паттернів T0-auto. Він дозволяє відфільтровувати правила, до яких застосовуються ці паттерни, та запускає повний цикл перевірки конформності з автоматичним виправленням через `runT0AutoCli`.
|
|
12
14
|
|
|
13
15
|
## Поведінка
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
runT0AutoCli запускає T0-auto для всіх провальних правил, повторно перевіряє конформність та виводить підсумок щодо закритого чи незакритого порушень.
|
|
17
|
+
applyT0Auto застосовує всі T0-auto паттерни до одного виявленого порушення, повертаючи результат застосування та список виконаних дій.
|
|
18
|
+
filterT0AutoRules повертає список ID правил, для яких існують відповідні T0-auto паттерни, виходячи з виявлених порушень.
|
|
19
|
+
runT0AutoCli запускає процес T0-auto: виконує перевірку конформності, застосовує T0-auto до порушень, повторно перевіряє змінені правила та виводить підсумок.
|
|
19
20
|
|
|
20
21
|
## Публічний API
|
|
21
22
|
|
|
22
|
-
applyT0Auto —
|
|
23
|
-
filterT0AutoRules —
|
|
24
|
-
runT0AutoCli — виконує команду `n-cursor fix-t0 [rule...]`,
|
|
23
|
+
applyT0Auto — застосовує всі T0-auto шаблони до вихідних даних про порушення.
|
|
24
|
+
filterT0AutoRules — визначає і повертає ідентифікатори правил, які мають принаймні один T0-auto шаблон, виходячи з результату `fix --json`.
|
|
25
|
+
runT0AutoCli — виконує команду `n-cursor fix-t0 [rule...]`, яка запускає `fix --json`, застосовує T0-auto до кожного порушення, повторно перевіряє check-gate та виводить звіт.
|
|
25
26
|
|
|
26
27
|
## Гарантії поведінки
|
|
27
28
|
|
|
28
|
-
-
|
|
29
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: vscode-ext-add.mjs
|
|
4
|
+
resource: npm/scripts/lib/fix/vscode-ext-add.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 950e9098
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Огляд
|
|
12
|
+
|
|
13
|
+
Реалізує Shared T0-autofix паттерн для правил із `vscode_extensions.rego`. Механізм витягує назви розширень із повідомлень про порушення та додає їх до секції `recommendations` у конфігурації `.vscode/extensions.json`, якщо вони відсутні. Паттерн універсальний для правил, що емітують вимогу «recommendations має містити "…"». Функціональність надається через публічні функції `patterns` та забезпечує перехоплення помилок для запобігання виняткам.
|
|
14
|
+
|
|
15
|
+
## Поведінка
|
|
16
|
+
|
|
17
|
+
1. Перевіряє, чи повідомлення про порушення містить шаблон, що вказує на необхідність додати рекомендації.
|
|
18
|
+
2. Витягує назви розширень з повідомлення про порушення.
|
|
19
|
+
3. Перевіряє наявність файлу `.vscode/extensions.json` у поточній робочій директорії.
|
|
20
|
+
4. Зчитує вміст `.vscode/extensions.json` та парсить його як JSON.
|
|
21
|
+
5. Ініціалізує список існуючих рекомендацій.
|
|
22
|
+
6. Визначає назви розширень, які потрібно додати, відфільтровувавши ті, що вже присутні.
|
|
23
|
+
7. Якщо є розширення для додавання, оновлює список рекомендацій у парсереному об'єкті.
|
|
24
|
+
8. Записує оновлений JSON-об'єкт назад у `.vscode/extensions.json`.
|
|
25
|
+
9. Повертає результат виконання операції.
|
|
26
|
+
|
|
27
|
+
## Гарантії поведінки
|
|
28
|
+
|
|
29
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* під фікс і застосування змін. Використовують і `llm-worker.mjs` (конформність), і
|
|
4
4
|
* `llm-lint-fix.mjs` (per-tool лінтер-фіксери) — щоб не дублювати парс/apply (knip/jscpd).
|
|
5
5
|
*/
|
|
6
|
+
import { execSync } from 'node:child_process'
|
|
6
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
7
|
-
import { dirname, join } from 'node:path'
|
|
8
|
+
import { basename, dirname, join } from 'node:path'
|
|
8
9
|
|
|
9
10
|
const JSON_CODE_BLOCK_RE = /```(?:json)?[ \t]{0,8}\n?([\s\S]*?)```/
|
|
10
11
|
|
|
@@ -39,8 +40,30 @@ export function parseChangesResponse(text) {
|
|
|
39
40
|
return null
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Шукає файл за basename у дереві проєкту (fallback коли прямий шлях не існує).
|
|
45
|
+
* Повертає відносний шлях якщо знайдено рівно один матч, інакше `null` (ambiguous/not found).
|
|
46
|
+
* @param {string} name basename файлу
|
|
47
|
+
* @param {string} projectRoot абсолютний корінь
|
|
48
|
+
* @returns {string|null} відносний шлях або null
|
|
49
|
+
*/
|
|
50
|
+
function findByBasename(name, projectRoot) {
|
|
51
|
+
try {
|
|
52
|
+
const raw = execSync(
|
|
53
|
+
`find . -maxdepth 7 -name '${name.replace(/'/g, "'\\''")}' -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/.worktrees/*'`,
|
|
54
|
+
{ cwd: projectRoot, encoding: 'utf8', timeout: 3000 }
|
|
55
|
+
).trim()
|
|
56
|
+
const hits = raw.split('\n').filter(Boolean)
|
|
57
|
+
return hits.length === 1 ? hits[0].replace(/^\.\//, '') : null
|
|
58
|
+
} catch {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
/**
|
|
43
64
|
* Читає існуючі файли за відносними шляхами у форму `{path, content}` (для prompt).
|
|
65
|
+
* Якщо файл не знайдений за прямим шляхом — намагається знайти за basename через `find`.
|
|
66
|
+
* Повертає resolved path (може відрізнятись від вхідного коли `find` знайшов реальне місце).
|
|
44
67
|
* @param {string[]} filePaths відносні шляхи від кореня
|
|
45
68
|
* @param {string} projectRoot абсолютний корінь
|
|
46
69
|
* @returns {Array<{path:string, content:string}>} наявні файли з вмістом
|
|
@@ -48,10 +71,18 @@ export function parseChangesResponse(text) {
|
|
|
48
71
|
export function readFilesForFix(filePaths, projectRoot) {
|
|
49
72
|
return filePaths
|
|
50
73
|
.map(p => {
|
|
51
|
-
|
|
74
|
+
let abs = join(projectRoot, p)
|
|
75
|
+
let resolvedPath = p
|
|
76
|
+
if (!existsSync(abs)) {
|
|
77
|
+
const found = findByBasename(basename(p), projectRoot)
|
|
78
|
+
if (found) {
|
|
79
|
+
resolvedPath = found
|
|
80
|
+
abs = join(projectRoot, found)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
52
83
|
if (!existsSync(abs)) return null
|
|
53
84
|
try {
|
|
54
|
-
return { path:
|
|
85
|
+
return { path: resolvedPath, content: readFileSync(abs, 'utf8') }
|
|
55
86
|
} catch {
|
|
56
87
|
return null
|
|
57
88
|
}
|
|
@@ -18,35 +18,44 @@ const DEFAULT_THINKING_BUDGET = Number(env.N_CURSOR_OMLX_THINKING_BUDGET ?? 4096
|
|
|
18
18
|
|
|
19
19
|
const API_KEY_RE = /api key/i
|
|
20
20
|
|
|
21
|
+
const FILE_EXTS = 'json|js|mjs|ts|vue|yml|yaml|toml|mdc|md|sh|py'
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* Витягує відносні шляхи файлів із violation output.
|
|
23
25
|
* Розуміє workspace-prefix: `[npm] skills/foo.mjs` → `npm/skills/foo.mjs`.
|
|
26
|
+
* Спочатку явно парсить рядки ❌ (найвищий сигнал — файл потребує фіксу),
|
|
27
|
+
* потім підхоплює решту файлів generic-regex (контекст для читання).
|
|
24
28
|
* @param {string} output violation output з fix check
|
|
25
29
|
* @returns {string[]} унікальні відносні шляхи (від кореня проєкту)
|
|
26
30
|
*/
|
|
27
|
-
function extractFilePaths(output) {
|
|
31
|
+
export function extractFilePaths(output) {
|
|
28
32
|
const seen = new Set()
|
|
29
33
|
const results = []
|
|
30
|
-
|
|
31
|
-
// Патерн з workspace: [npm] skills/foo.mjs або [demo] src/bar.ts
|
|
32
|
-
const wsRe = /\[([\w-]+)\]\s+([\w./][\w./-]*\.(?:json|js|mjs|ts|vue|yml|yaml|toml|mdc|md|sh|py))(?::\d+)?/gm
|
|
33
|
-
for (const m of output.matchAll(wsRe)) {
|
|
34
|
-
const p = `${m[1]}/${m[2]}`
|
|
34
|
+
const add = p => {
|
|
35
35
|
if (!seen.has(p)) {
|
|
36
36
|
seen.add(p)
|
|
37
37
|
results.push(p)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
// 1. Явні рядки ❌ — найвищий сигнал: саме ці файли потребують фіксу.
|
|
42
|
+
// Формати: `❌ [ws] path/file.ext:line — msg` та `❌ path/file.ext: msg`
|
|
43
|
+
// Роздільник після шляху: `:` (з пробілом або цифрою), `—` (em-dash), або кінець рядка.
|
|
44
|
+
const failSep = `(?::\\d+)?(?::\\s|[\\s—]|$)`
|
|
45
|
+
const failWsRe = new RegExp(`^\\s*❌\\s+\\[([\\w-]+)\\]\\s+([\\w./][\\w./-]*\\.(?:${FILE_EXTS}))${failSep}`, 'gm')
|
|
46
|
+
for (const m of output.matchAll(failWsRe)) add(`${m[1]}/${m[2]}`)
|
|
47
|
+
|
|
48
|
+
const failRe = new RegExp(`^\\s*❌\\s+(\\.?[\\w][\\w./-]*\\.(?:${FILE_EXTS}))${failSep}`, 'gm')
|
|
49
|
+
for (const m of output.matchAll(failRe)) add(m[1])
|
|
50
|
+
|
|
51
|
+
// 2. Generic-regex: підхоплює файли з ✅-рядків та описів (контекст для читання).
|
|
52
|
+
// Workspace: [npm] skills/foo.mjs
|
|
53
|
+
const wsRe = new RegExp(`\\[([\\w-]+)\\]\\s+([\\w./][\\w./-]*\\.(?:${FILE_EXTS}))(?::\\d+)?`, 'gm')
|
|
54
|
+
for (const m of output.matchAll(wsRe)) add(`${m[1]}/${m[2]}`)
|
|
55
|
+
|
|
56
|
+
// Без workspace: path/to/file.ext або ./file.ext
|
|
57
|
+
const re = new RegExp(`(?:^|\\s)(\\.?\\w[\\w./-]*\\.(?:${FILE_EXTS}))(?::\\d+)?`, 'gm')
|
|
58
|
+
for (const m of output.matchAll(re)) add(m[1])
|
|
50
59
|
|
|
51
60
|
return results
|
|
52
61
|
}
|
package/scripts/lib/fix/t0.mjs
CHANGED
|
@@ -1,123 +1,14 @@
|
|
|
1
1
|
/** @see ./docs/t0.md */
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { join } from 'node:path'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
5
4
|
|
|
5
|
+
import { discoverT0Patterns } from './discover-t0-patterns.mjs'
|
|
6
6
|
import { runConformanceCheck } from './run-conformance-check.mjs'
|
|
7
|
-
import { writeChange } from '../../../rules/release/change.mjs'
|
|
8
|
-
|
|
9
|
-
const REC_REQUIRE_RE = /recommendations має містити "[^"]+"/
|
|
10
|
-
const REC_MATCH_ALL_RE = /recommendations має містити "([^"]+)"/g
|
|
11
|
-
const FORBIDDEN_FILE_RE = /Знайдено заборонений файл: \S+/
|
|
12
|
-
const FORBIDDEN_FILE_MATCH_ALL_RE = /Знайдено заборонений файл: (\S+)/g
|
|
13
|
-
// Конформність changelog: «<ws>: є релевантні зміни, але немає change-файлу».
|
|
14
|
-
const MISSING_CHANGE_RE = /є релевантні зміни, але немає change-файлу/
|
|
15
|
-
const MISSING_CHANGE_MATCH_ALL_RE = /(?:^|\s)([\w./@-]+): є релевантні зміни, але немає change-файлу/gm
|
|
16
|
-
/** Дефолти autofix-створеного change-файлу (узгоджено з n-changelog.mdc / consistency.mjs). */
|
|
17
|
-
const CHANGE_BUMP = 'patch'
|
|
18
|
-
const CHANGE_SECTION = 'Changed'
|
|
19
|
-
const CHANGE_FALLBACK_MESSAGE = 'оновлення'
|
|
20
7
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*/
|
|
26
|
-
function autoChangeMessage(cwd) {
|
|
27
|
-
const r = spawnSync('git', ['log', '-1', '--format=%s'], { cwd, encoding: 'utf8' })
|
|
28
|
-
const subject = r.status === 0 ? (r.stdout ?? '').trim() : ''
|
|
29
|
-
return subject || CHANGE_FALLBACK_MESSAGE
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Патерни T0-auto.
|
|
34
|
-
* Кожен паттерн: {
|
|
35
|
-
* id: string — унікальна назва паттерну (для логу)
|
|
36
|
-
* test: (output)=>bool — чи підходить цей output до паттерну
|
|
37
|
-
* apply: (match, cwd)=>{ ok: bool, action: string } — застосувати фікс
|
|
38
|
-
* }
|
|
39
|
-
*/
|
|
40
|
-
const PATTERNS = [
|
|
41
|
-
// ── vscode-ext-add ──────────────────────────────────────────────────────────
|
|
42
|
-
// Violation: «recommendations має містити "tsandall.opa"»
|
|
43
|
-
// Fix: додати рядок у .vscode/extensions.json#recommendations
|
|
44
|
-
{
|
|
45
|
-
id: 'vscode-ext-add',
|
|
46
|
-
test: out => REC_REQUIRE_RE.test(out),
|
|
47
|
-
apply: (out, cwd) => {
|
|
48
|
-
const matches = [...out.matchAll(REC_MATCH_ALL_RE)]
|
|
49
|
-
if (matches.length === 0) return { ok: false, action: 'no match' }
|
|
50
|
-
|
|
51
|
-
const extPath = join(cwd, '.vscode/extensions.json')
|
|
52
|
-
if (!existsSync(extPath)) {
|
|
53
|
-
return { ok: false, action: '.vscode/extensions.json не знайдено' }
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let parsed
|
|
57
|
-
try {
|
|
58
|
-
parsed = JSON.parse(readFileSync(extPath, 'utf8'))
|
|
59
|
-
} catch {
|
|
60
|
-
return { ok: false, action: '.vscode/extensions.json: невалідний JSON' }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const recs = Array.isArray(parsed.recommendations) ? parsed.recommendations : []
|
|
64
|
-
const toAdd = matches.map(m => m[1]).filter(e => !recs.includes(e))
|
|
65
|
-
if (toAdd.length === 0) return { ok: false, action: 'вже є' }
|
|
66
|
-
|
|
67
|
-
parsed.recommendations = [...recs, ...toAdd]
|
|
68
|
-
writeFileSync(extPath, JSON.stringify(parsed, null, 2) + '\n', 'utf8')
|
|
69
|
-
return { ok: true, action: `додано до extensions.json: ${toAdd.join(', ')}` }
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
// ── rm-forbidden-file ────────────────────────────────────────────────────────
|
|
74
|
-
// Violation: «Знайдено заборонений файл: package-lock.json»
|
|
75
|
-
// Fix: видалити файл
|
|
76
|
-
{
|
|
77
|
-
id: 'rm-forbidden-file',
|
|
78
|
-
test: out => FORBIDDEN_FILE_RE.test(out),
|
|
79
|
-
apply: (out, cwd) => {
|
|
80
|
-
const matches = [...out.matchAll(FORBIDDEN_FILE_MATCH_ALL_RE)]
|
|
81
|
-
if (matches.length === 0) return { ok: false, action: 'no match' }
|
|
82
|
-
|
|
83
|
-
const removed = []
|
|
84
|
-
for (const m of matches) {
|
|
85
|
-
const filePath = join(cwd, m[1])
|
|
86
|
-
if (existsSync(filePath)) {
|
|
87
|
-
rmSync(filePath, { force: true })
|
|
88
|
-
removed.push(m[1])
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (removed.length === 0) return { ok: false, action: 'файлів не знайдено' }
|
|
92
|
-
return { ok: true, action: `видалено: ${removed.join(', ')}` }
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
// ── changelog-create-change-file ─────────────────────────────────────────────
|
|
97
|
-
// Violation: «<ws>: є релевантні зміни, але немає change-файлу»
|
|
98
|
-
// Fix: створити change-файл через канонічну `writeChange` (без LLM) — той самий
|
|
99
|
-
// механізм, що autofix changelog-конформності. Прибирає ескалацію в хмару на цьому кейсі.
|
|
100
|
-
{
|
|
101
|
-
id: 'changelog-create-change-file',
|
|
102
|
-
test: out => MISSING_CHANGE_RE.test(out),
|
|
103
|
-
apply: async (out, cwd) => {
|
|
104
|
-
const workspaces = Array.from(out.matchAll(MISSING_CHANGE_MATCH_ALL_RE), m => m[1])
|
|
105
|
-
if (workspaces.length === 0) return { ok: false, action: 'no match' }
|
|
106
|
-
|
|
107
|
-
const message = autoChangeMessage(cwd)
|
|
108
|
-
const created = []
|
|
109
|
-
for (const ws of workspaces) {
|
|
110
|
-
try {
|
|
111
|
-
const rel = await writeChange({ bump: CHANGE_BUMP, section: CHANGE_SECTION, message, ws, cwd })
|
|
112
|
-
created.push(ws === '.' ? rel : join(ws, rel))
|
|
113
|
-
} catch (error) {
|
|
114
|
-
return { ok: false, action: `writeChange ${ws}: ${error.message}` }
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return { ok: true, action: `створено change-файл (${CHANGE_BUMP}/${CHANGE_SECTION}): ${created.join(', ')}` }
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
]
|
|
8
|
+
// Паттерни живуть у rule-level fix-*.mjs файлах; агрегуємо тут через discovery.
|
|
9
|
+
// Top-level await: ініціалізація один раз при завантаженні модуля.
|
|
10
|
+
const RULES_DIR = join(dirname(fileURLToPath(import.meta.url)), '../../../rules')
|
|
11
|
+
const PATTERNS = await discoverT0Patterns(RULES_DIR)
|
|
121
12
|
|
|
122
13
|
/**
|
|
123
14
|
* Застосовує всі T0-auto паттерни до одного violation-output.
|
|
@@ -135,9 +26,7 @@ export async function applyT0Auto(ruleId, violationOutput, cwd) {
|
|
|
135
26
|
// Патерн може бути sync ({ok,action}) або async (Promise) — await нормалізує обидва.
|
|
136
27
|
const result = await p.apply(violationOutput, cwd)
|
|
137
28
|
actions.push(`[${p.id}] ${result.action}`)
|
|
138
|
-
if (result.ok)
|
|
139
|
-
applied = true
|
|
140
|
-
}
|
|
29
|
+
if (result.ok) applied = true
|
|
141
30
|
}
|
|
142
31
|
|
|
143
32
|
return { applied, actions }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared T0-autofix паттерн для правил із `vscode_extensions.rego`.
|
|
3
|
+
* Читає назву розширення з violation-message і додає його до
|
|
4
|
+
* `.vscode/extensions.json#recommendations`.
|
|
5
|
+
*
|
|
6
|
+
* Не прив'язаний до конкретного правила — один механізм для всіх правил,
|
|
7
|
+
* що емітують «recommendations має містити "…"».
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
|
|
12
|
+
const REC_REQUIRE_RE = /recommendations має містити "[^"]+"/
|
|
13
|
+
const REC_MATCH_ALL_RE = /recommendations має містити "([^"]+)"/g
|
|
14
|
+
|
|
15
|
+
/** @type {import('./discover-t0-patterns.mjs').T0Pattern[]} */
|
|
16
|
+
export const patterns = [
|
|
17
|
+
{
|
|
18
|
+
id: 'vscode-ext-add',
|
|
19
|
+
test: out => REC_REQUIRE_RE.test(out),
|
|
20
|
+
apply: (out, cwd) => {
|
|
21
|
+
const matches = [...out.matchAll(REC_MATCH_ALL_RE)]
|
|
22
|
+
if (matches.length === 0) return { ok: false, action: 'no match' }
|
|
23
|
+
|
|
24
|
+
const extPath = join(cwd, '.vscode/extensions.json')
|
|
25
|
+
if (!existsSync(extPath)) {
|
|
26
|
+
return { ok: false, action: '.vscode/extensions.json не знайдено' }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let parsed
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(readFileSync(extPath, 'utf8'))
|
|
32
|
+
} catch {
|
|
33
|
+
return { ok: false, action: '.vscode/extensions.json: невалідний JSON' }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const recs = Array.isArray(parsed.recommendations) ? parsed.recommendations : []
|
|
37
|
+
const toAdd = matches.map(m => m[1]).filter(e => !recs.includes(e))
|
|
38
|
+
if (toAdd.length === 0) return { ok: false, action: 'вже є' }
|
|
39
|
+
|
|
40
|
+
parsed.recommendations = [...recs, ...toAdd]
|
|
41
|
+
writeFileSync(extPath, JSON.stringify(parsed, null, 2) + '\n', 'utf8')
|
|
42
|
+
return { ok: true, action: `додано до extensions.json: ${toAdd.join(', ')}` }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|