@nitra/cursor 1.14.0 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.15.1] - 2026-05-24
8
+
9
+ ### Fixed
10
+
11
+ - `adr.mdc`: виправлено stale template-лінк `./js/hooks/template/.gitignore.snippet` → `./js/templates/hooks/.gitignore.snippet` (після flat-layout міграції `js/<concern>.mjs` у комміті `6ecd84c` шлях не оновили, через що `inlineTemplateLinks` падав під час `bun start` із `file not found`).
12
+
13
+ ## [1.15.0] - 2026-05-24
14
+
15
+ ### Added
16
+
17
+ - **Нове правило `rust`** (`npm/rules/rust/`): канонічний скрипт `lint-rust` у `package.json` (`cargo fmt` → `cargo clippy --fix` → `cargo clippy ... -D warnings`), CI workflow `.github/workflows/lint-rust.yml` з `dtolnay/rust-toolchain@stable` (`components: rustfmt, clippy`) + `Swatinem/rust-cache@v2`, VSCode-розширення `rust-lang.rust-analyzer` + `tamasfe.even-better-toml`. Auto-trigger — наявність `Cargo.toml` (`hasCargoToml` fact у `auto-rules.mjs`). Три rego policy-пакети (`package_json`, `vscode_extensions`, `lint_rust_yml`) читають канон через `data.template.*` з drift-тестами.
18
+
19
+ ### Changed
20
+
21
+ - **Правило `tauri` (1.1 → 1.2) звужено:** `rust-lang.rust-analyzer` більше не вимагається у `tauri.vscode_extensions` — перенесено в нове правило `rust`. Tauri-проєкт автоматично активує `rust` через `src-tauri/Cargo.toml`. Канон `tauri.mdc` оновлено: лишається лише `tauri-apps.tauri-vscode`.
22
+
7
23
  ## [1.14.0] - 2026-05-24
8
24
 
9
25
  ### Changed (BREAKING)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.14.0",
3
+ "version": "1.15.1",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
package/rules/adr/adr.mdc CHANGED
@@ -95,7 +95,7 @@ docs/adr/
95
95
  └── hooks.json # Cursor Agent stop-hooks для тих самих скриптів
96
96
  ```
97
97
 
98
- `.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @nitra/cursor`, коли правило `adr` увімкнене): [.gitignore.snippet](./js/hooks/template/.gitignore.snippet).
98
+ `.gitignore` у корені проєкту повинен містити базові рядки (`node_modules/`, `dist/`, `*.secret`) і патерни для ADR Stop-hook (**`.claude/hooks/*.log`**, `.claude/hooks/.normalize-state`, `.claude/hooks/.normalize.lock`). Канонічний фрагмент (дописується `npx @nitra/cursor`, коли правило `adr` увімкнене): [.gitignore.snippet](./js/templates/hooks/.gitignore.snippet).
99
99
 
100
100
  ## Stop-hook у `.claude/settings.json`
101
101
 
@@ -0,0 +1 @@
1
+ якщо в проекті є хоч один Cargo.toml
@@ -0,0 +1,19 @@
1
+ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
2
+
3
+ /**
4
+ * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
5
+ * Library mode: викликається CLI orchestration через `import + run(ctx)`.
6
+ * @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
7
+ * @returns {Promise<number>} 0 — OK, 1 — порушення
8
+ */
9
+ export function run(ctx) {
10
+ return runStandardRule(import.meta.dirname, ctx)
11
+ }
12
+
13
+ if (import.meta.main) {
14
+ // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
15
+ // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
16
+ const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
17
+ // eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
18
+ process.exit(await runRuleCli(import.meta.dirname))
19
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Applies-гейт правила rust: маркер — наявність `Cargo.toml` у `cwd` або
3
+ * в будь-якому workspace-підкаталозі (рекурсивний пошук з пропуском
4
+ * `node_modules`, `.git`, `.next`, `.turbo`). Якщо повертає `false` —
5
+ * `runStandardRule` пропускає всі концерни (JS і policy) цього правила.
6
+ * `check()` друкує тільки context-pass; реальна робота — у policy-концернах.
7
+ */
8
+ import { existsSync } from 'node:fs'
9
+
10
+ import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
11
+
12
+ import { hasCargoTomlInTree } from '../utils/has-cargo-toml.mjs'
13
+
14
+ const IGNORED_DIR_NAMES = new Set(['node_modules', '.git', '.next', '.turbo'])
15
+
16
+ /**
17
+ * @returns {Promise<boolean>} `true` — правило застосовне; `false` — пропустити
18
+ */
19
+ export function applies() {
20
+ if (existsSync('Cargo.toml')) return Promise.resolve(true)
21
+ return Promise.resolve(hasCargoTomlInTree(process.cwd(), IGNORED_DIR_NAMES))
22
+ }
23
+
24
+ /**
25
+ * @returns {number} exit-код (0 — OK, 1 — порушення)
26
+ */
27
+ export function check() {
28
+ const reporter = createCheckReporter()
29
+ reporter.pass('Знайдено Cargo.toml — застосовуємо правила rust.mdc')
30
+ return reporter.getExitCode()
31
+ }
@@ -0,0 +1,55 @@
1
+ # Перевірка `.github/workflows/lint-rust.yml` для правила rust (rust.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/lint-rust.yml.snippet.yml.
5
+ # Перевіряємо:
6
+ # - кожен `uses` з template (підмножина): actions/checkout@v6,
7
+ # dtolnay/rust-toolchain@stable, Swatinem/rust-cache@v2;
8
+ # - кожен `run` з template має бути присутнім (як substring) серед
9
+ # run-кроків input'а — drift-safe: зміна template одразу рухає перевірку.
10
+ # Універсальні workflow-перевірки (name, concurrency, branches) — у `ga.workflow_common`.
11
+ package rust.lint_rust_yml
12
+
13
+ import rego.v1
14
+
15
+ # Усі `uses` з канону workflow.
16
+ expected_uses contains u if {
17
+ some step in data.template.snippet.jobs.lint.steps
18
+ u := object.get(step, "uses", "")
19
+ u != ""
20
+ }
21
+
22
+ # Усі `uses` з input workflow.
23
+ actual_uses contains u if {
24
+ some job in object.get(input, "jobs", {})
25
+ some step in object.get(job, "steps", [])
26
+ u := object.get(step, "uses", "")
27
+ u != ""
28
+ }
29
+
30
+ # Конкатенація всіх `run`-кроків з input workflow.
31
+ all_run_text := concat("\n", [run_text |
32
+ some job in object.get(input, "jobs", {})
33
+ some step in object.get(job, "steps", [])
34
+ run_text := step_run_to_text(step)
35
+ ])
36
+
37
+ deny contains msg if {
38
+ some required_use in expected_uses
39
+ not required_use in actual_uses
40
+ msg := sprintf("lint-rust.yml: відсутній step з `uses: %s` (rust.mdc)", [required_use])
41
+ }
42
+
43
+ deny contains msg if {
44
+ some step in data.template.snippet.jobs.lint.steps
45
+ expected_run := object.get(step, "run", "")
46
+ expected_run != ""
47
+ not contains(all_run_text, expected_run)
48
+ msg := sprintf("lint-rust.yml: жоден крок run не містить %q (rust.mdc)", [expected_run])
49
+ }
50
+
51
+ step_run_to_text(step) := step.run if is_string(step.run)
52
+
53
+ else := concat("\n", [s | some s in step.run]) if is_array(step.run)
54
+
55
+ else := ""
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
+ "files": { "single": ".github/workflows/lint-rust.yml", "required": true },
4
+ "missingMessage": ".github/workflows/lint-rust.yml не існує — створи з канонічним вмістом (rust.mdc)"
5
+ }
@@ -0,0 +1,44 @@
1
+ name: Lint Rust
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - dev
7
+ - main
8
+ paths:
9
+ - '**/*.rs'
10
+ - '**/Cargo.toml'
11
+ - '**/Cargo.lock'
12
+ - '**/rustfmt.toml'
13
+ - '**/clippy.toml'
14
+
15
+ pull_request:
16
+ branches:
17
+ - dev
18
+ - main
19
+
20
+ concurrency:
21
+ group: ${{ github.ref }}-${{ github.workflow }}
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ lint:
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ contents: read
29
+ steps:
30
+ - uses: actions/checkout@v6
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - uses: dtolnay/rust-toolchain@stable
35
+ with:
36
+ components: rustfmt, clippy
37
+
38
+ - uses: Swatinem/rust-cache@v2
39
+
40
+ - name: Rustfmt
41
+ run: cargo fmt --all -- --check
42
+
43
+ - name: Clippy
44
+ run: cargo clippy --all-targets --all-features -- -D warnings
@@ -0,0 +1,18 @@
1
+ # Перевірка `package.json` для правила rust (rust.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "contains": ... } }
4
+ # Структура --data сформована з template/package.json.contains.json.
5
+ # Перевіряємо substring-вимоги до scripts.lint-rust: усі три кроки
6
+ # (`cargo fmt`, `cargo clippy --fix`, фінальний `cargo clippy ... -D warnings`)
7
+ # мають бути присутніми у значенні скрипта.
8
+ package rust.package_json
9
+
10
+ import rego.v1
11
+
12
+ deny contains msg if {
13
+ some script_name, needles in data.template.contains.scripts
14
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
15
+ some needle in needles
16
+ not contains(actual, needle)
17
+ msg := sprintf("package.json: scripts.%s має містити %q (rust.mdc)", [script_name, needle])
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
+ "files": { "single": "package.json", "required": true },
4
+ "missingMessage": "package.json не існує — створи зі scripts.lint-rust (rust.mdc)"
5
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "scripts": {
3
+ "lint-rust": [
4
+ "cargo fmt --all",
5
+ "cargo clippy --fix --allow-staged --allow-dirty",
6
+ "cargo clippy --all-targets --all-features -- -D warnings"
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
+ "files": { "single": ".vscode/extensions.json", "required": true },
4
+ "missingMessage": ".vscode/extensions.json не існує — створи з recommendations \"rust-lang.rust-analyzer\" і \"tamasfe.even-better-toml\" (rust.mdc)"
5
+ }
@@ -0,0 +1 @@
1
+ { "recommendations": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"] }
@@ -0,0 +1,15 @@
1
+ # Перевірка `.vscode/extensions.json` для правила rust (rust.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/extensions.json.snippet.json.
5
+ # Semantics: subset-of для recommendations — кожен з канону має бути присутнім,
6
+ # інші екстеншени дозволені.
7
+ package rust.vscode_extensions
8
+
9
+ import rego.v1
10
+
11
+ deny contains msg if {
12
+ some rec in data.template.snippet.recommendations
13
+ not rec in {r | some r in object.get(input, "recommendations", [])}
14
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (rust.mdc)", [rec])
15
+ }
@@ -0,0 +1,27 @@
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.0'
6
+ ---
7
+
8
+ **rustfmt** ([rust-lang/rustfmt](https://github.com/rust-lang/rustfmt)) — форматер; **clippy** ([rust-lang/rust-clippy](https://github.com/rust-lang/rust-clippy)) — лінтер. У скрипті **`lint-rust`** локально йдуть три кроки в одному рядку: `cargo fmt --all` → `cargo clippy --fix --allow-staged --allow-dirty --all-targets --all-features` → фінальний `cargo clippy --all-targets --all-features -- -D warnings`. У CI — без `--fix`: `cargo fmt --all -- --check` і `cargo clippy ... -- -D warnings` (див. `lint-rust.yml`).
9
+
10
+ `cargo`, `rustfmt`, `clippy` не додавай у `devDependencies` — це Rust toolchain, ставиться через `rustup` локально або через `dtolnay/rust-toolchain@stable` у CI.
11
+
12
+ Канон `scripts.lint-rust` (substring requirement): [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
13
+
14
+ У `.vscode/extensions.json` `recommendations` мають містити `rust-lang.rust-analyzer` і `tamasfe.even-better-toml`: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
15
+
16
+ Канон workflow `.github/workflows/lint-rust.yml`: [lint-rust.yml.snippet.yml](./policy/lint_rust_yml/template/lint-rust.yml.snippet.yml)
17
+
18
+ Перед **`./.github/actions/setup-bun-deps`** в інших workflows йде **`actions/checkout@v6`** (див. **ga.mdc**). У **lint-rust.yml** після checkout — `dtolnay/rust-toolchain@stable` (з `components: rustfmt, clippy`) + `Swatinem/rust-cache@v2` для кешу `~/.cargo` і `target/`. Bun composite дії тут не потрібен — toolchain ставиться напряму.
19
+
20
+ ## Композиція з Tauri
21
+
22
+ Tauri-проєкт завжди має `src-tauri/Cargo.toml`, тому правило `rust` активується автоматично разом з `tauri`. Поділ обов'язків:
23
+
24
+ - `rust` — `lint-rust` скрипт, `rust-analyzer`, `even-better-toml`, CI workflow.
25
+ - `tauri` — `tauri-apps.tauri-vscode` (див. **tauri.mdc**).
26
+
27
+ Обидва правила перевіряють `.vscode/extensions.json` за `contains`-семантикою; конкурентного запису немає.
@@ -0,0 +1,39 @@
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/utils/`.
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)) {
33
+ if (walk(join(dir, entry.name))) return true
34
+ }
35
+ }
36
+ return false
37
+ }
38
+ return walk(root)
39
+ }
@@ -70,9 +70,7 @@ export async function check() {
70
70
 
71
71
  const extPath = '.vscode/extensions.json'
72
72
  if (!existsSync(extPath)) {
73
- fail(
74
- `${extPath} не існує — створи з recommendations "tauri-apps.tauri-vscode" і "rust-lang.rust-analyzer" (tauri.mdc)`
75
- )
73
+ fail(`${extPath} не існує — створи з recommendations "tauri-apps.tauri-vscode" (tauri.mdc)`)
76
74
  return reporter.getExitCode()
77
75
  }
78
76
  const violations = runConftestBatch({
@@ -1,24 +1,22 @@
1
1
  # Перевірка `.vscode/extensions.json` для tauri (tauri.mdc).
2
2
  #
3
- # Викликається з `rules/tauri/fix.mjs` через `runConftestBatch` лише ПІСЛЯ того,
4
- # як JS виявив маркер Tauri-проєкту (`src-tauri/` каталог, `tauri.conf.json`
5
- # у будь-якому пакеті, або залежність `@tauri-apps/*`). Без `target.json` поруч
6
- # (не auto-discoverable через `n-cursor fix`) — інакше false-positive порушення на не-Tauri проєктах.
3
+ # Викликається з `rules/tauri/js/tooling.mjs` через `runConftestBatch` лише
4
+ # ПІСЛЯ того, як JS виявив маркер Tauri-проєкту (`src-tauri/` каталог,
5
+ # `tauri.conf.json` у будь-якому пакеті, або залежність `@tauri-apps/*`).
6
+ # Без `target.json` поруч (не auto-discoverable через `n-cursor fix`) — це
7
+ # conditional правило.
7
8
  #
8
- # Canonical (tauri.mdc): `recommendations` має містити обидва записи —
9
- # - tauri-apps.tauri-vscode
10
- # - rust-lang.rust-analyzer
11
- #
12
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
9
+ # Canonical (tauri.mdc): `recommendations` має містити `tauri-apps.tauri-vscode`.
10
+ # `rust-lang.rust-analyzer` і `tamasfe.even-better-toml` — вимагаються правилом
11
+ # `rust` (rust.mdc), бо Tauri-проєкт завжди має `src-tauri/Cargo.toml`.
13
12
  package tauri.vscode_extensions
14
13
 
15
14
  import rego.v1
16
15
 
17
- required_extensions := {"tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"}
16
+ required_extensions := {"tauri-apps.tauri-vscode"}
18
17
 
19
18
  missing_extension_template := ".vscode/extensions.json: recommendations має містити %q (tauri.mdc)"
20
19
 
21
- # Множина усіх записів `recommendations` (поза deny — performance/non-loop-expression).
22
20
  recommendations_set := {r | some r in object.get(input, "recommendations", [])}
23
21
 
24
22
  deny contains msg if {
@@ -2,15 +2,15 @@
2
2
  description: Tauri
3
3
  globs: "**/src-tauri/**,**/tauri.conf.json"
4
4
  alwaysApply: false
5
- version: '1.1'
5
+ version: '1.2'
6
6
  ---
7
7
 
8
-
9
- в файлі .vscode/extensions.json є налаштування для Vue:
8
+ У `.vscode/extensions.json` `recommendations` має містити `tauri-apps.tauri-vscode`:
10
9
 
11
10
  ```json title=".vscode/extensions.json"
12
11
  {
13
- "recommendations": ["tauri-apps.tauri-vscode",
14
- "rust-lang.rust-analyzer"]
12
+ "recommendations": ["tauri-apps.tauri-vscode"]
15
13
  }
16
14
  ```
15
+
16
+ Розширені Rust-вимоги (`rust-lang.rust-analyzer`, `tamasfe.even-better-toml`, скрипт `lint-rust`, CI `lint-rust.yml`) — у правилі **`rust`** (`n-rust.mdc`). Tauri-проєкт завжди має `src-tauri/Cargo.toml`, тому `rust` активується автоматично разом з `tauri`.
@@ -48,6 +48,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
48
48
  'npm-module',
49
49
  'php',
50
50
  'rego',
51
+ 'rust',
51
52
  'security',
52
53
  'style-lint',
53
54
  'text',
@@ -291,6 +292,7 @@ function updateDirFacts(dirName, facts) {
291
292
  * @param {string} relPath шлях відносно кореня
292
293
  * @param {{
293
294
  * hasCapacitorConfig: boolean,
295
+ * hasCargoToml: boolean,
294
296
  * hasDockerfile: boolean,
295
297
  * hasJsLikeSource: boolean,
296
298
  * hasNginxDefaultTplFile: boolean,
@@ -304,6 +306,9 @@ function updateFileFacts(fileName, relPath, facts) {
304
306
  if (fileName === 'capacitor.config.json') {
305
307
  facts.hasCapacitorConfig = true
306
308
  }
309
+ if (fileName === 'Cargo.toml') {
310
+ facts.hasCargoToml = true
311
+ }
307
312
  if (fileName === 'Dockerfile' || fileName.startsWith('Dockerfile.')) {
308
313
  facts.hasDockerfile = true
309
314
  }
@@ -407,6 +412,7 @@ async function updateHasuraFactFromFile(absPath, fileName, facts) {
407
412
  * @param {{
408
413
  * hasBunSqlImport: boolean,
409
414
  * hasCapacitorConfig: boolean,
415
+ * hasCargoToml: boolean,
410
416
  * hasDockerfile: boolean,
411
417
  * hasGqlTaggedTemplates: boolean,
412
418
  * hasHasuraConfig: boolean,
@@ -493,6 +499,7 @@ export function isMonorepoPackage(packageJson) {
493
499
  * @param {string} root абсолютний шлях кореня репозиторію
494
500
  * @returns {Promise<{
495
501
  * hasCapacitorConfig: boolean,
502
+ * hasCargoToml: boolean,
496
503
  * hasDockerfile: boolean,
497
504
  * hasGaWorkflowsDir: boolean,
498
505
  * hasBunSqlImport: boolean,
@@ -511,6 +518,7 @@ export async function collectAutoRuleFacts(root) {
511
518
  const facts = {
512
519
  hasBunSqlImport: false,
513
520
  hasCapacitorConfig: false,
521
+ hasCargoToml: false,
514
522
  hasDockerfile: false,
515
523
  hasGaWorkflowsDir: existsSync(join(root, '.github', 'workflows')),
516
524
  hasGqlTaggedTemplates: false,
@@ -656,6 +664,7 @@ export async function detectAutoRules({
656
664
  { enabled: npmDirExists, id: 'npm-module' },
657
665
  { enabled: composerJsonExists, id: 'php' },
658
666
  { enabled: facts.hasRegoFile, id: 'rego' },
667
+ { enabled: facts.hasCargoToml, id: 'rust' },
659
668
  { enabled: facts.hasVueOrCssSource, id: 'style-lint' }
660
669
  ]
661
670
  for (const item of autoRuleChecks) {