@7n/rules 1.20.1 → 1.21.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -1
  3. package/scripts/lib/docs/resolve-plugins.md +1 -1
  4. package/rules/python/applies/concern.json +0 -4
  5. package/rules/python/applies/docs/main.md +0 -29
  6. package/rules/python/applies/main.mjs +0 -24
  7. package/rules/python/docs/index.md +0 -11
  8. package/rules/python/lib/docs/index.md +0 -9
  9. package/rules/python/lib/docs/uv-run.md +0 -31
  10. package/rules/python/lib/uv-run.mjs +0 -52
  11. package/rules/python/main.json +0 -1
  12. package/rules/python/main.mdc +0 -8
  13. package/rules/python/mypy/concern.json +0 -7
  14. package/rules/python/mypy/docs/main.md +0 -32
  15. package/rules/python/mypy/main.mjs +0 -37
  16. package/rules/python/project/concern.json +0 -6
  17. package/rules/python/project/docs/main.md +0 -42
  18. package/rules/python/project/main.mjs +0 -125
  19. package/rules/python/pyproject_toml/concern.json +0 -9
  20. package/rules/python/pyproject_toml/pyproject_toml.mdc +0 -29
  21. package/rules/python/pyproject_toml/pyproject_toml.rego +0 -53
  22. package/rules/python/pyproject_toml/template/pyproject.toml.deny.toml +0 -2
  23. package/rules/python/pyproject_toml/template/pyproject.toml.snippet.toml +0 -12
  24. package/rules/python/ruff/concern.json +0 -7
  25. package/rules/python/ruff/docs/fix-ruff.md +0 -33
  26. package/rules/python/ruff/docs/main.md +0 -35
  27. package/rules/python/ruff/fix-ruff.mjs +0 -65
  28. package/rules/python/ruff/main.mjs +0 -53
  29. package/rules/python/tooling/concern.json +0 -15
  30. package/rules/python/tooling/docs/index.md +0 -11
  31. package/rules/python/tooling/docs/main.md +0 -35
  32. package/rules/python/tooling/main.mjs +0 -45
  33. package/rules/python/tooling/tooling.mdc +0 -22
  34. package/rules/rust/applies/concern.json +0 -4
  35. package/rules/rust/applies/docs/index.md +0 -11
  36. package/rules/rust/applies/docs/main.md +0 -30
  37. package/rules/rust/applies/main.mjs +0 -28
  38. package/rules/rust/check/check.mdc +0 -16
  39. package/rules/rust/check/concern.json +0 -7
  40. package/rules/rust/check/docs/fix-check.md +0 -30
  41. package/rules/rust/check/docs/index.md +0 -11
  42. package/rules/rust/check/docs/main.md +0 -37
  43. package/rules/rust/check/fix-check.mjs +0 -80
  44. package/rules/rust/check/main.mjs +0 -87
  45. package/rules/rust/docs/index.md +0 -11
  46. package/rules/rust/lib/docs/has-cargo-toml.md +0 -134
  47. package/rules/rust/lib/docs/index.md +0 -11
  48. package/rules/rust/lib/has-cargo-toml.mjs +0 -37
  49. package/rules/rust/main.json +0 -1
  50. package/rules/rust/main.mdc +0 -8
  51. package/rules/rust/package_json/concern.json +0 -10
  52. package/rules/rust/package_json/package_json.mdc +0 -12
  53. package/rules/rust/package_json/package_json.rego +0 -20
  54. package/rules/rust/vscode_extensions/concern.json +0 -11
  55. package/rules/rust/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
  56. package/rules/rust/vscode_extensions/docs/index.md +0 -11
  57. package/rules/rust/vscode_extensions/fix-vscode_extensions.mjs +0 -1
  58. package/rules/rust/vscode_extensions/template/extensions.json.snippet.json +0 -1
  59. package/rules/rust/vscode_extensions/vscode_extensions.mdc +0 -9
  60. package/rules/rust/vscode_extensions/vscode_extensions.rego +0 -15
@@ -1,35 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: main.mjs
4
- resource: npm/rules/python/ruff/main.mjs
5
- docgen:
6
- crc: aa3519c3
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 100
9
- issues: judge:inaccurate:0.98
10
- judgeModel: openai-codex/gpt-5.4-mini
11
- ---
12
-
13
- ## Огляд
14
-
15
- Цей детектор працює у **read-only** режимі та здійснює перевірку форматування та синтаксису Python-коду в заданому робочому каталозі. Він використовує `ruff` (зокрема, `ruff check` та `ruff format --check`) через `uv run --frozen` для аналізу цільових файлів, гарантуючи відповідність коду правилам, визначеним у `pyproject.toml`. Детектор аналізує наданий набір файлів (`ctx.files`) або весь проєкт (`.`), використовуючи транзитивне підвантаження імпортованих модулів для коректної перевірки. Результат містить зареєстровані порушення, виявлені під час перевірки.
16
-
17
- ## Поведінка
18
-
19
- 1. Якщо у робочому каталозі відсутній файл `pyproject.toml`, обробка припиняється.
20
- 2. Визначаються цільові файли для аналізу: або наданий список файлів, або всі файли у робочому каталозі з розширенням `.py`.
21
- 3. Якщо цільових файлів немає, обробка припиняється.
22
- 4. Перевіряється доступність бінарника `uv` у системному шляху. Якщо його немає, реєстрація порушення про помилку з `uv` і обробка припиняється.
23
- 5. Перевіряється доступність інструменту `ruff` у середовищі `uv`. Якщо він недоступний, обробка припиняється.
24
- 6. Викликається `lint` для виконання перевірки синтаксису та логіки за допомогою `ruff check` для всіх цільових файлів.
25
- 7. Якщо `ruff check` завершується з помилкою, це фіксується як порушення.
26
- 8. Викликається `lint` для перевірки форматування коду за допомогою `ruff format --check` для всіх цільових файлів.
27
- 9. Результат аналізу повертається з зареєстрованими порушеннями.
28
-
29
- ## Публічний API
30
-
31
- lint — виявляє проблеми у коді за допомогою парсера python/ruff (только читання).
32
-
33
- ## Гарантії поведінки
34
-
35
- - Read-only: не виконує операцій запису (ФС/БД).
@@ -1,65 +0,0 @@
1
- /**
2
- * T0-autofix для `python/ruff` — детерміновані `ruff check --fix` + `ruff format` (через
3
- * `uv run --frozen`), перенесено з колишнього `python/check/fix-check.mjs`
4
- * (spec docs/specs/2026-07-02-text-check-per-file-split-design.md "Рішення python/php/rego").
5
- * Відсутній `uv` → no-op.
6
- */
7
- import { readFileSync } from 'node:fs'
8
- import { resolve } from 'node:path'
9
-
10
- import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
11
- import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
12
-
13
- /**
14
- * Вміст файлу або null, якщо не читається.
15
- * @param {string} abs абсолютний шлях
16
- * @returns {string|null} вміст або null
17
- */
18
- function readOrNull(abs) {
19
- try {
20
- return readFileSync(abs, 'utf8')
21
- } catch {
22
- return null
23
- }
24
- }
25
-
26
- /**
27
- * Tracked *.py файли проєкту (через git).
28
- * Async (не блокує event loop) — може виконуватись у parallel lane `detectAll()`
29
- * (ADR 260716-1354).
30
- * @param {string} cwd корінь
31
- * @returns {Promise<string[]>} posix-relative шляхи
32
- */
33
- async function listPyFiles(cwd) {
34
- const r = await spawnAsync('git', ['ls-files', '-z', '--', '*.py'], { cwd })
35
- if (r.exitCode !== 0) return []
36
- return (r.stdout ?? '').split('\0').filter(Boolean)
37
- }
38
-
39
- /** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
40
- export const patterns = [
41
- {
42
- id: 'python-ruff-fix',
43
- standalone: true, // §8 Phase 2: ruff check --fix + format самі ре-аналізують, test() не потрібен
44
- test: violations =>
45
- violations.some(v => v.reason === 'ruff-check-violation' || v.reason === 'ruff-format-violation'),
46
- apply: async (violations, ctx) => {
47
- const uv = resolveCmd('uv')
48
- if (!uv) return { touchedFiles: [] }
49
- const files = await listPyFiles(ctx.cwd)
50
- if (files.length === 0) return { touchedFiles: [] }
51
-
52
- const abs = files.map(f => resolve(ctx.cwd, f))
53
- const before = new Map(abs.map(a => [a, readOrNull(a)]))
54
- const opts = { cwd: ctx.cwd }
55
- await spawnAsync(uv, ['run', '--frozen', 'ruff', 'check', '--fix', '.'], opts)
56
- await spawnAsync(uv, ['run', '--frozen', 'ruff', 'format', '.'], opts)
57
-
58
- const touchedFiles = abs.filter(a => readOrNull(a) !== before.get(a))
59
- for (const a of touchedFiles) ctx.recordWrite?.(a)
60
- return touchedFiles.length > 0
61
- ? { touchedFiles, message: `ruff check --fix + format: ${touchedFiles.length} файл(ів)` }
62
- : { touchedFiles: [] }
63
- }
64
- }
65
- ]
@@ -1,53 +0,0 @@
1
- /**
2
- * lint-поверхня python/ruff: read-only detector (`ruff check` + `ruff format --check`, через
3
- * `uv run --frozen`). Per-file: приймає `ctx.files`, інакше `.` (весь проєкт). Виділено з
4
- * колишнього bundled `python/check` (spec docs/specs/2026-07-02-text-check-per-file-split-design.md
5
- * "Рішення python/php/rego") — ruff сам транзитивно підвантажує імпортовані модулі
6
- * (follow-imports), тож коректний і на підмножині переданих файлів.
7
- */
8
- import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
9
- import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
10
- import { preparePythonRun } from '../lib/uv-run.mjs'
11
-
12
- /**
13
- * Async (не блокує event loop) — детектор може виконуватись у parallel lane `detectAll()`
14
- * (ADR 260716-1354).
15
- * @param {string} label назва кроку.
16
- * @param {string} uv шлях до бінарника uv.
17
- * @param {string[]} args аргументи ruff (без `uv run --frozen ruff`-префіксу).
18
- * @param {string} cwd робочий каталог.
19
- * @param {(msg: string, opts: object) => void} fail колбек реєстрації порушення.
20
- * @param {string} reason машиночитна причина порушення.
21
- * @returns {Promise<boolean>} true якщо OK
22
- */
23
- async function runRuffStep(label, uv, args, cwd, fail, reason) {
24
- const r = await spawnAsync(uv, ['run', '--frozen', 'ruff', ...args], { cwd })
25
- if (r.exitCode === 0) return true
26
- const code = typeof r.exitCode === 'number' ? r.exitCode : 1
27
- const out = `${r.stdout ?? ''}${r.stderr ?? ''}`.trim().slice(0, 2000)
28
- const outSuffix = out ? `\n${out}` : ''
29
- fail(`lint-python: ${label} — помилка (код ${code}, python.mdc)${outSuffix}`, reason)
30
- return false
31
- }
32
-
33
- /**
34
- * Detector python/ruff (read-only).
35
- * @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту.
36
- * @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат із порушеннями
37
- */
38
- export async function lint(ctx) {
39
- const reporter = createViolationReporter(ctx)
40
- const { fail } = reporter
41
- const cwd = ctx.cwd
42
-
43
- const prepared = await preparePythonRun(ctx, fail, 'ruff')
44
- if (!prepared) return reporter.result()
45
- const { uv, targets } = prepared
46
-
47
- if (!(await runRuffStep('ruff check', uv, ['check', ...targets], cwd, fail, 'ruff-check-violation'))) {
48
- return reporter.result()
49
- }
50
- await runRuffStep('ruff format --check', uv, ['format', '--check', ...targets], cwd, fail, 'ruff-format-violation')
51
-
52
- return reporter.result()
53
- }
@@ -1,15 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "lint": {
5
- "scope": "full",
6
- "glob": [
7
- "pyproject.toml",
8
- "uv.lock",
9
- "poetry.lock",
10
- "poetry.toml",
11
- "package.json",
12
- ".github/workflows/lint-python.yml"
13
- ]
14
- }
15
- }
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/python/tooling
4
- resource: npm/rules/python/tooling/
5
- ---
6
-
7
- # npm/rules/python/tooling
8
-
9
- | Файл | Тип |
10
- | ------------------- | --------- |
11
- | [main.mjs](main.md) | JS Module |
@@ -1,35 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: main.mjs
4
- resource: npm/rules/python/tooling/main.mjs
5
- docgen:
6
- crc: 98ec5b8e
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 90
9
- issues: surzhik,judge:inaccurate:0.98
10
- judgeModel: openai-codex/gpt-5.4-mini
11
- ---
12
-
13
- ## Огляд
14
-
15
- Цей файл відповідає за перевірку відповідності проєкту встановленим правилам, визначеним у `python.mdc`. Він підтверджує наявність ключових конфігураційних файлів, зокрема `package.json`, що визначає залежності проекту. Дозволена функціональність обмежена лише читанням файлової системи та бази даних; жодних операцій запису в ці ресурси не виконується. Основна логіка роботи реалізована у публічній функції `main`.
16
-
17
- ## Поведінка
18
-
19
- main виявляє відповідність проєкту правилам (python.mdc) за наявності необхідних файлів та конфігурацій.
20
-
21
- 1. Перевіряє наявність `pyproject.toml` у корені.
22
- 2. Перевіряє наявність `uv.lock`, повідомляючи про необхідність його генерації, якщо він відсутній.
23
- 3. Перевіряє відсутність артефактів Poetry (`poetry.lock`, `poetry.toml`), оскільки підтримується лише `uv` (python.mdc).
24
- 4. Перевіряє наявність `package.json` у корені.
25
- 5. Перевіряє наявність файлу робочого процесу `.github/workflows/lint-python.yml`, необхідного для відповідності (python.mdc).
26
- Не перевіряє шляхи, що містять `.github` або `.git`.
27
-
28
- ## Публічний API
29
-
30
- main — перевіряє, чи відповідає проєкт вимогам, визначеним у правилах `python.mdc`.
31
-
32
- ## Гарантії поведінки
33
-
34
- - Read-only: не виконує операцій запису (ФС/БД).
35
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,45 +0,0 @@
1
- /** @see ./docs/tooling.md */
2
- import { existsSync } from 'node:fs'
3
- import { join } from 'node:path'
4
-
5
- import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
6
-
7
- /**
8
- * Перевіряє відповідність проєкту правилам python.mdc.
9
- * @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту.
10
- * @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат із порушеннями
11
- */
12
- export function lint(ctx) {
13
- const cwd = ctx.cwd
14
- const reporter = createViolationReporter(ctx)
15
- const { pass, fail } = reporter
16
-
17
- if (!existsSync(join(cwd, 'pyproject.toml'))) {
18
- return Promise.resolve(reporter.result())
19
- }
20
-
21
- if (existsSync(join(cwd, 'uv.lock'))) {
22
- pass('uv.lock є')
23
- } else {
24
- fail('uv.lock не знайдено — згенеруй `uv lock` (python.mdc, без Poetry)')
25
- }
26
-
27
- // Poetry-артефакти заборонені: uv є єдиним пакет-менеджером (python.mdc).
28
- for (const poetryFile of ['poetry.lock', 'poetry.toml']) {
29
- if (existsSync(join(cwd, poetryFile))) {
30
- fail(`${poetryFile} знайдено — прибери Poetry, мігруй на uv (python.mdc)`)
31
- } else {
32
- pass(`${poetryFile} відсутній`)
33
- }
34
- }
35
-
36
- if (existsSync(join(cwd, 'package.json'))) {
37
- pass('package.json є')
38
- } else {
39
- fail('package.json не знайдено в корені — додай (python.mdc)')
40
- }
41
-
42
- // Existence/структуру lint-python.yml вимагає провайдер-плагін @7n/rules-ci-github
43
- // (mixin python/lint_python_yml) — ядро провайдер-агностичне.
44
- return Promise.resolve(reporter.result())
45
- }
@@ -1,22 +0,0 @@
1
- ## Перевірка наявності uv-toolchain і відсутності Poetry
2
-
3
- JS-перевірка (`npm/rules/python/tooling/main.mjs`) запускається, якщо в корені репо є `pyproject.toml`. Перевіряє:
4
-
5
- - `uv.lock` — має існувати (зафіксувати командою `uv lock`);
6
- - `poetry.lock` / `poetry.toml` — мають **бути відсутніми** (Poetry заборонено);
7
- - `package.json` — має бути у корені (js-обгортка lint є частиною монорепо);
8
- - `.github/workflows/lint-python.yml` — має існувати (структуру перевіряє Rego-полісі `python.lint_python_yml`).
9
-
10
- Якщо `pyproject.toml` відсутній — перевірка пропускається з кодом 0 (проєкт не Python).
11
-
12
- ## lint-python: JS-детектори за concern
13
-
14
- Інструменти uv-екосистеми не мають єдиного CLI-обходу репо, тому python-лінт делегується у JS-детектори. Запуск — через **`n-rules lint python`** (CI — `--no-fix`); окремого `package.json`-скрипта немає.
15
-
16
- Три концерни (кожен — окремий per-file чи project-wide детектор):
17
-
18
- - `npm/rules/python/project/main.mjs` — `uv lock --check` + `uv sync --frozen` + ліцензійний аудит (`pip-licenses`); project-wide, поза delta-планом (лише `n-rules lint --full` або scoped `n-rules lint python`);
19
- - `npm/rules/python/ruff/main.mjs` (+ `fix-ruff.mjs`) — `ruff check` + `ruff format --check` (детект), `ruff check --fix` + `ruff format` (autofix); per-file;
20
- - `npm/rules/python/mypy/main.mjs` — `mypy` (лише детект, немає autofix); per-file.
21
-
22
- Якщо `pyproject.toml` у корені відсутній — усі концерни виходять без порушень (проєкт не Python). Якщо `pyproject.toml` є, але `uv` не знайдено в PATH — це помилка. `ruff`/`mypy`-кроки запускаються лише якщо інструмент доступний у середовищі (інакше крок пропускається з повідомленням).
@@ -1,4 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "lint": { "scope": "full", "glob": ["**/Cargo.toml"] }
4
- }
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/rust/applies
4
- resource: npm/rules/rust/applies/
5
- ---
6
-
7
- # npm/rules/rust/applies
8
-
9
- | Файл | Тип |
10
- | ------------------- | --------- |
11
- | [main.mjs](main.md) | JS Module |
@@ -1,30 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: main.mjs
4
- resource: npm/rules/rust/applies/main.mjs
5
- docgen:
6
- crc: 2d2dcbac
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 100
9
- issues: judge:inaccurate:0.97
10
- judgeModel: openai-codex/gpt-5.4-mini
11
- ---
12
-
13
- ## Огляд
14
-
15
- Функціональність визначає, чи підходить даний каталог для застосування правил. Публічна функція `applies` здійснює цей аналіз, тоді як функція `main` ініціює процес повної перевірки, звітуючи про результати застосування правил до каталогу. Сканування каталогу ігнорує системні та залежностні каталоги, такі як `.git` та `node_modules`, а також конфігураційний файл `Cargo.toml`.
16
-
17
- ## Поведінка
18
-
19
- Поведінка:
20
- applies визначає, чи є правило застосовним до поточного каталогу, перевіряючи наявність файлу Cargo.toml або його присутності у підкаталогах, і свідомо ігнорує каталоги .git, node_modules та .next.
21
- main виконує логіку перевірки, ініціалізує репортер та інформує про застосування правил Rust, повертаючи код завершення.
22
-
23
- ## Публічний API
24
-
25
- Будь ласка, надайте код або список елементів коду, які потрібно задокументувати.
26
-
27
- ## Гарантії поведінки
28
-
29
- - Read-only: не виконує операцій запису (ФС/БД).
30
- - Свідомо пропускає шляхи: `.git`, `node_modules`.
@@ -1,28 +0,0 @@
1
- /** @see ./docs/applies.md */
2
- import { existsSync } from 'node:fs'
3
- import { join } from 'node:path'
4
-
5
- import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
6
-
7
- import { hasCargoTomlInTree } from '../lib/has-cargo-toml.mjs'
8
-
9
- const IGNORED_DIR_NAMES = new Set(['node_modules', '.git', '.next', '.turbo'])
10
-
11
- /**
12
- * @param {string} [cwd] корінь репозиторію
13
- * @returns {Promise<boolean>} `true` — правило застосовне; `false` — пропустити
14
- */
15
- export function applies(cwd = process.cwd()) {
16
- if (existsSync(join(cwd, 'Cargo.toml'))) return Promise.resolve(true)
17
- return Promise.resolve(hasCargoTomlInTree(cwd, IGNORED_DIR_NAMES))
18
- }
19
-
20
- /**
21
- * @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту.
22
- * @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат context-pass
23
- */
24
- export function lint(ctx) {
25
- const reporter = createViolationReporter(ctx)
26
- reporter.pass('Знайдено Cargo.toml — застосовуємо правила rust.mdc')
27
- return Promise.resolve(reporter.result())
28
- }
@@ -1,16 +0,0 @@
1
- ## Лінт Rust: rustfmt + clippy + cargo-deny
2
-
3
- **rustfmt** ([rust-lang/rustfmt](https://github.com/rust-lang/rustfmt)) — форматер; **clippy** ([rust-lang/rust-clippy](https://github.com/rust-lang/rust-clippy)) — лінтер; **cargo-deny** — перевірка ліцензій залежностей. Запуск — через **`n-rules lint rust`** (detector `npm/rules/rust/check/main.mjs`, autofix `npm/rules/rust/check/fix-check.mjs`):
4
-
5
- - **read-only (детект, CI):** `cargo fmt --all -- --check` → `cargo clippy --all-targets --all-features -- -D warnings` → (за наявності `deny.toml` і встановленого `cargo-deny`) `cargo deny check licenses`.
6
- - **`fix` (локально):** `cargo fmt --all` (реальне форматування) і, якщо `deny.toml` відсутній та `cargo-deny` встановлено, `cargo deny init` для генерації канонічного файлу.
7
-
8
- `clippy` **не** автофіксимо (`--fix` потенційно небезпечний і може прибрати навмисний код) — clippy-порушення йдуть у LLM-ladder, а не в детермінований autofix.
9
-
10
- Без `Cargo.toml` у cwd — no-op (exit 0). Окремого `package.json`-скрипта немає.
11
-
12
- `cargo`, `rustfmt`, `clippy`, `cargo-deny` **не додавай** у `devDependencies` — це Rust toolchain, ставиться через `rustup`/`cargo install` локально або через `dtolnay/rust-toolchain@stable` у CI.
13
-
14
- ### Канон CI workflow `.github/workflows/lint-rust.yml`
15
-
16
- Канонічний workflow, drift-safe перевірка (`rust.lint_rust_yml`) і вимога `Swatinem/rust-cache@v2` — `npm/rules/rust/lint_rust_yml/lint_rust_yml.mdc`. Тут лише сам лінт-скрипт; CI-канон workflow **не дублюється**.
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "lint": {
4
- "scope": "full",
5
- "glob": ["**/*.rs", "Cargo.toml", "Cargo.lock"]
6
- }
7
- }
@@ -1,30 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-check.mjs
4
- resource: npm/rules/rust/check/fix-check.mjs
5
- docgen:
6
- crc: 3bd2ccd0
7
- ---
8
-
9
- ## Огляд
10
-
11
- T0-autofix для `rust/check`: детермінований `cargo fmt --all` (детектор ганяє його лише з
12
- `--check`) і канонічна генерація `deny.toml` через `cargo deny init`. Форматує Rust-код перед
13
- LLM-ладдером. clippy не автофіксимо (його `--fix` потенційно небезпечний) — ці порушення й
14
- далі йдуть у LLM-ladder. Запис незворотний. Відсутній `cargo`/`cargo-deny` → no-op.
15
-
16
- ## Поведінка
17
-
18
- - Перелічує tracked \*.rs через git, застосовує `cargo fmt --all`. До списку змінених — лише
19
- файли з фактичною зміною.
20
- - За наявності порушення `deny-config-missing` і встановленого `cargo-deny` викликає
21
- `cargo deny init`, що створює канонічний `deny.toml` у корені проєкту.
22
-
23
- ## Публічний API
24
-
25
- - `patterns` — `rust-cargo-fmt` спрацьовує на reason `cargo-fmt-violation`;
26
- `rust-cargo-deny-init` спрацьовує на reason `deny-config-missing`.
27
-
28
- ## Гарантії поведінки
29
-
30
- - Записуються лише фактично змінені/створені файли; кожен реєструється через `recordWrite`.
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/rust/check
4
- resource: npm/rules/rust/check/
5
- ---
6
-
7
- # npm/rules/rust/check
8
-
9
- | Файл | Тип |
10
- | ------------------- | --------- |
11
- | [main.mjs](main.md) | JS Module |
@@ -1,37 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: main.mjs
4
- resource: npm/rules/rust/check/main.mjs
5
- docgen:
6
- crc: 71821b75
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 100
9
- judgeModel: openai-codex/gpt-5.4-mini
10
- ---
11
-
12
- ## Огляд
13
-
14
- Файл реалізує lint-поверхню для Rust, використовуючи команди `cargo fmt`, `clippy` та `deny` для автоматизованої перевірки та форматування коду в робочому каталозі. Поведінка супроводжується повідомленнями, описаними в `rust.mdc`.
15
-
16
- ## Поведінка
17
-
18
- Поведінка:
19
-
20
- 1. Якщо в робочому каталозі відсутній файл `Cargo.toml`, операція лінту для Rust пропускається.
21
- 2. Якщо інструмент `cargo` не знайдено в системному PATH, операція лінту завершується з помилкою.
22
- 3. Виконується форматування коду за допомогою `cargo fmt`. Якщо режим `readOnly` активовано, перевіряється лише відповідність формату; інакше, застосовується форматування.
23
- 4. Якщо режим `readOnly` не активовано, запускається автоматичне виправлення стильових проблем за допомогою `cargo clippy --fix`.
24
- 5. Після виправлення стилів, виконується перевірка на наявність попереджень за допомогою `cargo clippy -D warnings`.
25
- 6. Перевіряється наявність файлу `deny.toml`.
26
- 7. Якщо `deny.toml` відсутній і режим `readOnly` активовано, фіксується помилка, вказуючи на необхідність ручного запуску для його генерації (rust.mdc).
27
- 8. Якщо `deny.toml` відсутній і режим `readOnly` не активовано, він створюється з дефолтним списком дозволених ліцензій.
28
- 9. Якщо `deny.toml` присутній і інструмент `cargo deny` встановлений, виконується перевірка ліцензій за допомогою `cargo deny check licenses`.
29
- 10. Якщо інструмент `cargo deny` не встановлений, перевірка ліцензій пропускається.
30
-
31
- ## Публічний API
32
-
33
- lint — забезпечує відповідність коду специфічним стандартам поверхні Rust.
34
-
35
- ## Гарантії поведінки
36
-
37
- - (специфічних машинно-виведених гарантій немає)
@@ -1,80 +0,0 @@
1
- /** @see ./docs/fix-check.md */
2
-
3
- /**
4
- * T0-autofix для `rust/check` — детермінований `cargo fmt --all`, що його read-only детектор
5
- * виконує лише з `--check`, плюс канонічна генерація `deny.toml` через `cargo deny init`
6
- * (`deny-config-missing`). clippy не автофіксимо (його `--fix` потенційно небезпечний) — ці
7
- * порушення й далі йдуть у LLM-ladder. Запис permanent. Відсутній `cargo`/`cargo-deny` → no-op.
8
- */
9
- import { existsSync, readFileSync } from 'node:fs'
10
- import { join, resolve } from 'node:path'
11
-
12
- import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
13
- import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
14
-
15
- /**
16
- * Вміст файлу або null, якщо не читається.
17
- * @param {string} abs абсолютний шлях
18
- * @returns {string|null} вміст або null
19
- */
20
- function readOrNull(abs) {
21
- try {
22
- return readFileSync(abs, 'utf8')
23
- } catch {
24
- return null
25
- }
26
- }
27
-
28
- /**
29
- * Tracked *.rs файли проєкту (через git). Async (не блокує event loop, ADR 260716-1354).
30
- * @param {string} cwd корінь
31
- * @returns {Promise<string[]>} posix-relative шляхи
32
- */
33
- async function listRsFiles(cwd) {
34
- const r = await spawnAsync('git', ['ls-files', '-z', '--', '*.rs'], { cwd })
35
- if (r.exitCode !== 0) return []
36
- return (r.stdout ?? '').split('\0').filter(Boolean)
37
- }
38
-
39
- /** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
40
- export const patterns = [
41
- {
42
- id: 'rust-cargo-fmt',
43
- standalone: true, // §8 Phase 2: apply самостійно перелічує *.rs (git ls-files), cargo fmt сам ре-аналізує
44
- test: violations => violations.some(v => v.reason === 'cargo-fmt-violation'),
45
- apply: async (violations, ctx) => {
46
- const cargo = resolveCmd('cargo')
47
- if (!cargo) return { touchedFiles: [] }
48
- const files = await listRsFiles(ctx.cwd)
49
- if (files.length === 0) return { touchedFiles: [] }
50
-
51
- const abs = files.map(f => resolve(ctx.cwd, f))
52
- const before = new Map(abs.map(a => [a, readOrNull(a)]))
53
- await spawnAsync(cargo, ['fmt', '--all'], { cwd: ctx.cwd })
54
-
55
- const touchedFiles = abs.filter(a => readOrNull(a) !== before.get(a))
56
- for (const a of touchedFiles) ctx.recordWrite?.(a)
57
- return touchedFiles.length > 0
58
- ? { touchedFiles, message: `cargo fmt: ${touchedFiles.length} файл(ів)` }
59
- : { touchedFiles: [] }
60
- }
61
- },
62
- {
63
- id: 'rust-cargo-deny-init',
64
- test: violations => violations.some(v => v.reason === 'deny-config-missing'),
65
- apply: async (violations, ctx) => {
66
- const cargo = resolveCmd('cargo')
67
- if (!cargo) return { touchedFiles: [] }
68
- const denyVersionResult = await spawnAsync(cargo, ['deny', '--version'])
69
- const hasDeny = denyVersionResult.exitCode === 0
70
- if (!hasDeny) return { touchedFiles: [] }
71
-
72
- const denyConfigPath = join(ctx.cwd, 'deny.toml')
73
- await spawnAsync(cargo, ['deny', 'init'], { cwd: ctx.cwd })
74
- if (!existsSync(denyConfigPath)) return { touchedFiles: [] }
75
-
76
- ctx.recordWrite?.(denyConfigPath)
77
- return { touchedFiles: [denyConfigPath], message: 'cargo deny init: deny.toml' }
78
- }
79
- }
80
- ]
@@ -1,87 +0,0 @@
1
- /**
2
- * lint-поверхня rust: read-only detector (cargo fmt --check + clippy -D warnings +
3
- * cargo deny check licenses). Генерація deny.toml — окремий T0-fix, не в detector-і.
4
- */
5
- import { existsSync } from 'node:fs'
6
- import { join } from 'node:path'
7
-
8
- import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
9
- import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
10
- import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
11
-
12
- /**
13
- * Async (не блокує event loop) — детектор може виконуватись у parallel lane `detectAll()`
14
- * (ADR 260716-1354).
15
- * @param {string} label назва кроку.
16
- * @param {string} cargo шлях до бінарника cargo.
17
- * @param {string[]} args аргументи cargo.
18
- * @param {string} cwd робочий каталог.
19
- * @param {(msg: string, reason: string) => void} fail колбек реєстрації порушення.
20
- * @param {string} reason машиночитна причина порушення.
21
- * @returns {Promise<boolean>} true якщо OK
22
- */
23
- async function runCargo(label, cargo, args, cwd, fail, reason) {
24
- const r = await spawnAsync(cargo, args, { cwd })
25
- if (r.exitCode === 0) return true
26
- const code = typeof r.exitCode === 'number' ? r.exitCode : 1
27
- const out = `${r.stdout ?? ''}${r.stderr ?? ''}`.trim().slice(0, 2000)
28
- const outTail = out ? `\n${out}` : ''
29
- fail(`lint-rust: ${label} — помилка (код ${code}, rust.mdc)${outTail}`, reason)
30
- return false
31
- }
32
-
33
- /**
34
- * Detector rust/check (read-only). Async (не блокує event loop) — детектор може виконуватись
35
- * у parallel lane `detectAll()` (ADR 260716-1354).
36
- * @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту.
37
- * @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат із порушеннями
38
- */
39
- export async function lint(ctx) {
40
- const reporter = createViolationReporter(ctx)
41
- const { fail } = reporter
42
- const cwd = ctx.cwd
43
-
44
- if (!existsSync(join(cwd, 'Cargo.toml'))) {
45
- // немає Cargo.toml → кроки Rust пропущено
46
- return reporter.result()
47
- }
48
-
49
- const cargo = resolveCmd('cargo')
50
- if (!cargo) {
51
- fail('lint-rust: `cargo` не знайдено в PATH (Rust toolchain через rustup, rust.mdc)', 'cargo-missing')
52
- return reporter.result()
53
- }
54
-
55
- if (
56
- !(await runCargo('cargo fmt --check', cargo, ['fmt', '--all', '--', '--check'], cwd, fail, 'cargo-fmt-violation'))
57
- ) {
58
- return reporter.result()
59
- }
60
-
61
- await runCargo(
62
- 'cargo clippy -D warnings',
63
- cargo,
64
- ['clippy', '--all-targets', '--all-features', '--', '-D', 'warnings'],
65
- cwd,
66
- fail,
67
- 'cargo-clippy-violation'
68
- )
69
-
70
- const denyConfigPath = join(cwd, 'deny.toml')
71
- if (!existsSync(denyConfigPath)) {
72
- fail(
73
- 'lint-rust: cargo deny — немає deny.toml; запустіть `npx @7n/rules fix rust` локально для генерації (rust.mdc)',
74
- 'deny-config-missing'
75
- )
76
- return reporter.result()
77
- }
78
-
79
- const denyVersionResult = await spawnAsync(cargo, ['deny', '--version'])
80
- const hasDeny = denyVersionResult.exitCode === 0
81
- if (hasDeny) {
82
- await runCargo('cargo deny check licenses', cargo, ['deny', 'check', 'licenses'], cwd, fail, 'cargo-deny-violation')
83
- }
84
- // cargo-deny не встановлено → перевірку ліцензій пропущено (старий код — pass)
85
-
86
- return reporter.result()
87
- }