@7n/rules 1.20.1 → 1.22.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 (62) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +1 -1
  3. package/scripts/lib/docs/resolve-plugins.md +1 -1
  4. package/skills/taze/js/docs/orchestrate.md +1 -1
  5. package/skills/taze/js/orchestrate.mjs +175 -64
  6. package/rules/python/applies/concern.json +0 -4
  7. package/rules/python/applies/docs/main.md +0 -29
  8. package/rules/python/applies/main.mjs +0 -24
  9. package/rules/python/docs/index.md +0 -11
  10. package/rules/python/lib/docs/index.md +0 -9
  11. package/rules/python/lib/docs/uv-run.md +0 -31
  12. package/rules/python/lib/uv-run.mjs +0 -52
  13. package/rules/python/main.json +0 -1
  14. package/rules/python/main.mdc +0 -8
  15. package/rules/python/mypy/concern.json +0 -7
  16. package/rules/python/mypy/docs/main.md +0 -32
  17. package/rules/python/mypy/main.mjs +0 -37
  18. package/rules/python/project/concern.json +0 -6
  19. package/rules/python/project/docs/main.md +0 -42
  20. package/rules/python/project/main.mjs +0 -125
  21. package/rules/python/pyproject_toml/concern.json +0 -9
  22. package/rules/python/pyproject_toml/pyproject_toml.mdc +0 -29
  23. package/rules/python/pyproject_toml/pyproject_toml.rego +0 -53
  24. package/rules/python/pyproject_toml/template/pyproject.toml.deny.toml +0 -2
  25. package/rules/python/pyproject_toml/template/pyproject.toml.snippet.toml +0 -12
  26. package/rules/python/ruff/concern.json +0 -7
  27. package/rules/python/ruff/docs/fix-ruff.md +0 -33
  28. package/rules/python/ruff/docs/main.md +0 -35
  29. package/rules/python/ruff/fix-ruff.mjs +0 -65
  30. package/rules/python/ruff/main.mjs +0 -53
  31. package/rules/python/tooling/concern.json +0 -15
  32. package/rules/python/tooling/docs/index.md +0 -11
  33. package/rules/python/tooling/docs/main.md +0 -35
  34. package/rules/python/tooling/main.mjs +0 -45
  35. package/rules/python/tooling/tooling.mdc +0 -22
  36. package/rules/rust/applies/concern.json +0 -4
  37. package/rules/rust/applies/docs/index.md +0 -11
  38. package/rules/rust/applies/docs/main.md +0 -30
  39. package/rules/rust/applies/main.mjs +0 -28
  40. package/rules/rust/check/check.mdc +0 -16
  41. package/rules/rust/check/concern.json +0 -7
  42. package/rules/rust/check/docs/fix-check.md +0 -30
  43. package/rules/rust/check/docs/index.md +0 -11
  44. package/rules/rust/check/docs/main.md +0 -37
  45. package/rules/rust/check/fix-check.mjs +0 -80
  46. package/rules/rust/check/main.mjs +0 -87
  47. package/rules/rust/docs/index.md +0 -11
  48. package/rules/rust/lib/docs/has-cargo-toml.md +0 -134
  49. package/rules/rust/lib/docs/index.md +0 -11
  50. package/rules/rust/lib/has-cargo-toml.mjs +0 -37
  51. package/rules/rust/main.json +0 -1
  52. package/rules/rust/main.mdc +0 -8
  53. package/rules/rust/package_json/concern.json +0 -10
  54. package/rules/rust/package_json/package_json.mdc +0 -12
  55. package/rules/rust/package_json/package_json.rego +0 -20
  56. package/rules/rust/vscode_extensions/concern.json +0 -11
  57. package/rules/rust/vscode_extensions/docs/fix-vscode_extensions.md +0 -25
  58. package/rules/rust/vscode_extensions/docs/index.md +0 -11
  59. package/rules/rust/vscode_extensions/fix-vscode_extensions.mjs +0 -1
  60. package/rules/rust/vscode_extensions/template/extensions.json.snippet.json +0 -1
  61. package/rules/rust/vscode_extensions/vscode_extensions.mdc +0 -9
  62. package/rules/rust/vscode_extensions/vscode_extensions.rego +0 -15
@@ -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
- }
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/rust
4
- resource: npm/rules/rust/
5
- ---
6
-
7
- # npm/rules/rust
8
-
9
- | Файл | Тип |
10
- | ------------------- | --------- |
11
- | [main.mjs](main.md) | JS Module |
@@ -1,134 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: has-cargo-toml.mjs
4
- resource: npm/rules/rust/lib/has-cargo-toml.mjs
5
- docgen:
6
- crc: f55fc57e
7
- ---
8
-
9
- Модуль `has-cargo-toml.mjs` — rule-local утиліта для правил Rust, що визначає наявність маніфесту Cargo (`Cargo.toml`) у дереві файлової системи, починаючи з заданого кореневого каталогу. Використовується як applies-walker на рівні правила: дозволяє правилу Rust динамічно вирішувати, чи варто застосовуватися до конкретного workspace/cwd, шукаючи `Cargo.toml` у поточному каталозі або в будь-якому з підкаталогів.
10
-
11
- Реалізація — синхронний рекурсивний обхід каталогів з раннім поверненням (`early return`) при першому ж знайденому `Cargo.toml`, без читання вмісту файлів. Імена директорій, у які заходити не треба (наприклад, `node_modules`, `.git`, `.next`, `.turbo`), передаються ззовні через `Set<string>` — модуль свідомо не містить власного списку, щоб список ігнорувань був єдиним з тим, що в `npm/scripts/auto-rules.mjs` (на рівні виклику).
12
-
13
- Утиліта навмисно тримається rule-local (у каталозі правила `rust/lib/`), оскільки на момент написання її єдиний споживач — правило `rust`. У документуючому коментарі модуля зазначено: якщо з'явиться другий споживач — утиліту слід підняти у спільний `npm/scripts/lib/`.
14
-
15
- ## Експорти / API
16
-
17
- Модуль експортує одну іменовану функцію:
18
-
19
- - `hasCargoTomlInTree(root, ignoredDirNames)` — `function`, named export.
20
-
21
- Інших експортів (default, типів, констант) у модулі немає.
22
-
23
- ## Функції
24
-
25
- ### `hasCargoTomlInTree(root, ignoredDirNames)`
26
-
27
- **Сигнатура:**
28
-
29
- ```js
30
- export function hasCargoTomlInTree(root: string, ignoredDirNames: Set<string>): boolean
31
- ```
32
-
33
- **Параметри:**
34
-
35
- - `root` — `string`. Абсолютний шлях до кореневого каталогу, з якого починається пошук. Передається у внутрішню функцію `walk` як стартова точка обходу.
36
- - `ignoredDirNames` — `Set<string>`. Множина імен директорій (саме базових імен — не шляхів), у які НЕ слід заходити під час обходу. Перевірка виконується через `ignoredDirNames.has(entry.name)`. Типові значення, які формує викликова сторона: `'node_modules'`, `'.git'`, `'.next'`, `'.turbo'`.
37
-
38
- **Повертає:**
39
-
40
- - `boolean` — `true`, якщо в піддереві з коренем `root` (включно з самим `root`) знайдено хоча б один файл з ім'ям `Cargo.toml`. `false` — якщо такого файла немає, або якщо корінь недоступний для читання.
41
-
42
- **Алгоритм:**
43
-
44
- 1. Викликається внутрішня функція `walk(root)`.
45
- 2. Усередині `walk(dir)` робиться спроба отримати вміст каталогу через `readdirSync(dir, { withFileTypes: true })`, що повертає масив об'єктів `Dirent`.
46
- 3. Якщо `readdirSync` кидає виключення (наприклад, `EACCES`, `ENOENT`, `ENOTDIR`) — воно мовчки ловиться `catch {}` і функція повертає `false` для цього каталогу (обхід продовжується для інших гілок, якщо вони є).
47
- 4. Виконується ітерація по `entries`:
48
- - Якщо запис — це файл і його ім'я дорівнює рівно `'Cargo.toml'`, негайно повертається `true` (early return).
49
- - Якщо запис — це каталог і його ім'я не входить у `ignoredDirNames`, рекурсивно викликається `walk(join(dir, entry.name))`; при `true` від рекурсії — негайно повертається `true`.
50
- 5. Якщо жоден запис не дав `true`, повертається `false`.
51
-
52
- Порядок обходу — детерміністичний у межах одного запуску і визначається порядком, який повертає `readdirSync` (як правило, залежить від ФС/ОС). Це означає, що логічний результат (boolean) детерміністичний, але швидкість виявлення `Cargo.toml` залежить від порядку записів.
53
-
54
- Алгоритм має властивість «depth-first з раннім return»: знайшовши `Cargo.toml` у першій же підгілці, функція не обходить решту дерева.
55
-
56
- **Side effects:**
57
-
58
- - Виключно синхронні I/O-операції читання вмісту директорій через `readdirSync` із Node.js `fs`. Не читає вміст файлів, не пише на диск, не звертається до мережі, не змінює глобальний стан.
59
- - При помилках доступу до окремого каталогу не пробрасує виключення назовні — глушить їх через `try/catch` і повертає `false` лише для проблемної гілки.
60
- - Блокує event loop на час обходу (синхронне API). Для дуже великих дерев це може бути помітним; ризик частково мітигує ранній return і список `ignoredDirNames`, що відсікає типові важкі директорії на кшталт `node_modules`.
61
-
62
- **Внутрішня функція `walk(dir)`:**
63
-
64
- - `dir` — `string`, абсолютний шлях каталогу для обходу.
65
- - Повертає `boolean` — `true`, якщо в піддереві `dir` є `Cargo.toml`.
66
- - Замикається на параметр `ignoredDirNames` зовнішньої функції (тобто множина передається один раз і використовується на всіх рівнях рекурсії без повторної параметризації).
67
-
68
- ## Залежності
69
-
70
- Зовнішні npm-залежності — відсутні. Модуль працює виключно на стандартній бібліотеці Node.js:
71
-
72
- - `node:fs` — імпортується `readdirSync` для синхронного читання вмісту каталогу з опцією `withFileTypes: true`, що повертає `Dirent[]` (для уникнення додаткових `statSync` на кожен запис).
73
- - `node:path` — імпортується `join` для безпечного склеювання шляхів `dir + entry.name` із врахуванням розділювача ОС.
74
-
75
- Імпорти використовують префікс `node:` (`node:fs`, `node:path`) — це канонічна форма посилання на вбудовані модулі Node.js, що відрізняє їх від npm-пакунків.
76
-
77
- Модуль написаний як ES Module (`.mjs`): використовуються `import` та `export`.
78
-
79
- Внутрішніх залежностей від інших файлів проєкту немає — модуль повністю самодостатній.
80
-
81
- ## Потік виконання / Використання
82
-
83
- ### Контекст застосування
84
-
85
- Функція проектована як applies-walker для правил Rust: правило викликає її, щоб дізнатися, чи має воно сенс у поточному репозиторії/workspace. Якщо `Cargo.toml` ніде не знайдено — правило застосовувати не треба.
86
-
87
- ### Типовий виклик
88
-
89
- ```js
90
- import { hasCargoTomlInTree } from './lib/has-cargo-toml.mjs'
91
-
92
- const IGNORED = new Set(['node_modules', '.git', '.next', '.turbo'])
93
-
94
- if (hasCargoTomlInTree(process.cwd(), IGNORED)) {
95
- // правило Rust застосовується: десь у дереві є Cargo.toml
96
- } else {
97
- // Rust-проєкту не виявлено — правило пропускається
98
- }
99
- ```
100
-
101
- Список ігнорованих директорій формується викликовою стороною й має співпадати зі списком, який використовує `npm/scripts/auto-rules.mjs` (це вимога з заголовного коментаря модуля — щоб поведінка обходу була узгоджена між скриптом авто-правил і rule-local утилітою).
102
-
103
- ### Розгортання потоку виконання
104
-
105
- 1. Споживач (правило Rust) формує `Set<string>` ігнорованих імен директорій.
106
- 2. Споживач викликає `hasCargoTomlInTree(root, ignoredDirNames)` із кореневим шляхом (зазвичай — поточний workspace або репозиторій).
107
- 3. Усередині функції стартує `walk(root)`:
108
- - Якщо `root` сам містить `Cargo.toml` як файл першого рівня — повертається `true` без рекурсії далі по цьому ж рівню (інші записи не перевіряються після першого ж `Cargo.toml`).
109
- - Якщо `Cargo.toml` на першому рівні немає — обхід заглиблюється у кожен підкаталог, якого немає в `ignoredDirNames`.
110
- - Перший же `true` із рекурсії припиняє обхід і пробрасує `true` нагору.
111
- 4. Якщо все дерево пройдене без знахідки — повертається `false`.
112
- 5. Споживач використовує boolean-результат для прийняття рішення про застосування правила.
113
-
114
- ### Граничні випадки
115
-
116
- - **Порожній `ignoredDirNames`** (`new Set()`): функція обходить геть усе дерево, включно з `node_modules` тощо — це коректно, але повільно.
117
- - **Неіснуючий `root`**: `readdirSync` кидає `ENOENT`, помилка ловиться, функція повертає `false`.
118
- - **`root` не є директорією** (наприклад, шлях до файлу): `readdirSync` кидає `ENOTDIR`, ловиться `catch`, повертається `false`.
119
- - **Cargo.toml як директорія**: пропускається, бо перевірка йде через `entry.isFile() && entry.name === 'Cargo.toml'` — потрібен саме файл.
120
- - **Симлінки**: `withFileTypes: true` повертає `Dirent` з власними методами; `entry.isFile()` для симлінка поверне `false`, `entry.isDirectory()` — теж `false` (метод `isSymbolicLink()` тут не перевіряється), тож симлінки фактично не переходяться. Це свідомий вибір — захист від циклів через симлінки.
121
- - **Cargo.toml у самому корені**: знаходиться на першому ж рівні без рекурсії.
122
- - **Cargo.toml виключно всередині `node_modules`** (за умови, що `node_modules` у `ignoredDirNames`): не знаходиться, функція повертає `false`. Це навмисна поведінка: ні правила, ні авто-обхід не повинні «бачити» вендоровані Rust-маніфести з npm-залежностей.
123
-
124
- ### Контекст у проєкті
125
-
126
- Згідно з заголовним коментарем файлу:
127
-
128
- - Утиліта живе у `npm/rules/rust/lib/` як rule-local — лише `rust`-правило її використовує.
129
- - Список ігнорованих директорій має узгоджуватися зі списком у `npm/scripts/auto-rules.mjs`.
130
- - Інваріант масштабування: при появі другого споживача — підняти модуль у спільний `npm/scripts/lib/`.
131
-
132
- ## Rebuild Test
133
-
134
- За цією документацією модуль можна відтворити: один ES-модуль з імпортами `readdirSync` із `node:fs` та `join` із `node:path`, що експортує іменовану функцію `hasCargoTomlInTree(root, ignoredDirNames)`. Усередині — внутрішня рекурсивна функція `walk(dir)` із синхронним `readdirSync(dir, { withFileTypes: true })`, обгорнутим у `try/catch`, ітерацією по `Dirent`-записах, перевіркою `isFile() && name === 'Cargo.toml'` для негайного `true`, та рекурсією у підкаталоги через `join(dir, entry.name)` за умови `isDirectory() && !ignoredDirNames.has(name)`. Поведінкові інваріанти: ранній return при знахідці; повернення `false` при помилці читання каталогу; пропуск ігнорованих директорій; обхід не йде у симлінки (бо `isDirectory()` для симлінка повертає `false`); жодних побічних ефектів окрім читання структури директорій. Сигнатура: `(string, Set<string>) => boolean`.
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/rust/lib
4
- resource: npm/rules/rust/lib/
5
- ---
6
-
7
- # npm/rules/rust/lib
8
-
9
- | Файл | Тип |
10
- | --------------------------------------- | --------- |
11
- | [has-cargo-toml.mjs](has-cargo-toml.md) | JS Module |
@@ -1,37 +0,0 @@
1
- /**
2
- * Rule-level applies-walker правила rust: рекурсивно шукає Cargo.toml у
3
- * cwd або будь-якому workspace-підкаталозі. Пропускає `node_modules`, `.git`,
4
- * `.next`, `.turbo` за тим самим списком, що `npm/scripts/auto-rules.mjs`.
5
- *
6
- * Утиліта rule-local, бо лише `rust` потребує "знайти Cargo.toml у дереві";
7
- * якщо з'явиться другий споживач — підняти у `npm/scripts/lib/`.
8
- */
9
- import { readdirSync } from 'node:fs'
10
- import { join } from 'node:path'
11
-
12
- /**
13
- * Чи присутній хоч один Cargo.toml у дереві `root` (синхронно, з раннім return).
14
- * @param {string} root абсолютний шлях кореня
15
- * @param {Set<string>} ignoredDirNames імена директорій, в які НЕ заходимо
16
- * @returns {boolean} true, якщо знайдено Cargo.toml
17
- */
18
- export function hasCargoTomlInTree(root, ignoredDirNames) {
19
- /**
20
- * @param {string} dir абсолютний шлях каталогу для обходу
21
- * @returns {boolean} true якщо в піддереві є Cargo.toml
22
- */
23
- function walk(dir) {
24
- let entries
25
- try {
26
- entries = readdirSync(dir, { withFileTypes: true })
27
- } catch {
28
- return false
29
- }
30
- for (const entry of entries) {
31
- if (entry.isFile() && entry.name === 'Cargo.toml') return true
32
- if (entry.isDirectory() && !ignoredDirNames.has(entry.name) && walk(join(dir, entry.name))) return true
33
- }
34
- return false
35
- }
36
- return walk(root)
37
- }
@@ -1 +0,0 @@
1
- { "auto": { "glob": ["**/Cargo.toml", "deny.toml"] } }
@@ -1,8 +0,0 @@
1
- ---
2
- description: Перевірка Rust коду
3
- globs: "**/{Cargo.toml,Cargo.lock,rustfmt.toml,clippy.toml,.vscode/extensions.json,package.json},**/*.rs"
4
- alwaysApply: false
5
- version: '1.4'
6
- ---
7
-
8
- Правило забезпечує форматування (rustfmt), лінт (clippy), CI workflow та покриття для Rust-проєктів.
@@ -1,10 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": "package.json",
7
- "required": false
8
- }
9
- }
10
- }
@@ -1,12 +0,0 @@
1
- ## Rego-gate: `cargo`, `rustfmt`, `clippy` заборонені у `package.json`
2
-
3
- Rego-пакет: `rust.package_json`
4
-
5
- Цільовий файл: `package.json`
6
-
7
- `cargo`, `rustfmt` і `clippy` — це частина **Rust toolchain**, а не npm-пакети. Вони **не додаються** у `dependencies`, `devDependencies` або `peerDependencies`. Встановлення:
8
-
9
- - **локально** — через `rustup` (`rustup component add rustfmt clippy`);
10
- - **у CI** — через крок `dtolnay/rust-toolchain@stable` із `with.components: rustfmt, clippy`.
11
-
12
- Gate видає deny, якщо `cargo`, `rustfmt` або `clippy` з'являється у будь-якій секції залежностей `package.json`.
@@ -1,20 +0,0 @@
1
- # Перевірка `package.json` для Rust-проєктів (rust.mdc / lint.mdc).
2
- #
3
- # `cargo`, `rustfmt`, `clippy` — частина Rust toolchain (rustup), не npm-залежностей.
4
- # Вони мають бути у PATH через `rustup` локально або `dtolnay/rust-toolchain@stable` у CI.
5
- package rust.package_json
6
-
7
- import rego.v1
8
-
9
- banned_rust_tools := {"cargo", "rustfmt", "clippy"}
10
-
11
- deny contains msg if {
12
- some field in {"dependencies", "devDependencies", "peerDependencies"}
13
- deps := object.get(input, field, {})
14
- some name, _ in deps
15
- name in banned_rust_tools
16
- msg := sprintf(
17
- "package.json: %s.%s заборонений — Rust toolchain встановлюється через rustup / dtolnay/rust-toolchain@stable, не через npm (rust.mdc)",
18
- [field, name],
19
- )
20
- }
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".vscode/extensions.json",
7
- "required": true
8
- },
9
- "missingMessage": ".vscode/extensions.json не існує — створи з recommendations \"rust-lang.rust-analyzer\" і \"tamasfe.even-better-toml\" (rust.mdc)"
10
- }
11
- }
@@ -1,25 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-vscode_extensions.mjs
4
- resource: npm/rules/rust/vscode_extensions/fix-vscode_extensions.mjs
5
- docgen:
6
- crc: 3353fe17
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- tier: local-min
9
- score: 100
10
- issues: judge:inaccurate:0.98
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- Надайте мені чорнетку секції "overview", яку потрібно переписати, щоб я міг застосувати ваші інструкції.
17
-
18
- ## Поведінка
19
-
20
- 1. Ініціалізує логіку для виправлення VS Code Extensions.
21
- 2. Імпортує шаблони логіки для додавання до VS Code Extensions з файлу `../../../scripts/lib/fix/vscode-ext-add.mjs`.
22
-
23
- ## Гарантії поведінки
24
-
25
- - Read-only: не виконує операцій запису (ФС/БД).