Built-in Checks
Built-in checks are registered under devManager.quality.builtin.*. Most run in pure Node.js (filesystem + regex) with no extra install. A few invoke npx or external CLIs when present — those are also documented on CLI Tool Checks. All are enabled by default; each returns skip when it does not apply to the project.
Code Size & Complexity
| Check | What it detects | Default thresholds |
|---|---|---|
| Line Count | Source files that are too long | warn ≥ 300, fail ≥ 500 lines |
| Function Length | Functions / methods that are too long | warn ≥ 40, fail ≥ 80 lines |
| Cyclomatic Complexity | Functions with too many branches | warn ≥ 10, fail ≥ 20 |
| File Size | Files exceeding size limits | warn ≥ 50 KB, fail ≥ 200 KB |
| Long Lines | Lines exceeding column width | warn if >5% of lines over 120 chars |
| Import Depth | Deep ../../../ import chains | warn ≥ 4, fail ≥ 6 levels |
| Directory Depth | Folder nesting too deep | warn ≥ 7, fail ≥ 10 levels |
| Dependency Count | Too many direct dependencies | warn ≥ 50, fail ≥ 100 |
Code Quality & Maintainability
| Check | What it detects |
|---|---|
| TODO Count | TODO, FIXME, HACK, BUG, XXX, NOSONAR markers in comments |
| Commented Code | Blocks of source code that have been commented out |
| Duplicate Files | Exact binary-identical files with different names |
| Test Ratio | Ratio of test files to source files — warns when tests are sparse |
| Mixed Indent | Files mixing tabs and spaces inconsistently |
| Encoding | Non-UTF-8 files that cause cross-platform issues |
| Debug Leaks | console.log, print(), debugger, var_dump(), dd() in production code |
| Secret Leaks | Hardcoded API keys, tokens, passwords, private keys |
| Gitignore Check | Files tracked by git that match .gitignore patterns |
| Magic Numbers | Hardcoded numeric literals that should be named constants |
| Parameter Count | Functions / methods with too many parameters |
| Barrel Files | Oversized index.ts/js re-export files that hurt tree-shaking and build times |
Architecture & Design
| Check | What it detects |
|---|---|
| Coupling Metrics | High afferent/efferent coupling; flags “god files” and architecturally unstable modules |
| Cross-Layer Imports | Forbidden import directions (e.g. UI components importing Prisma; API routes importing React) |
| Component Inventory | Similar UI components/hooks grouped by pattern — suggests consolidation opportunities |
| Heavy Imports | Full-package imports of large libraries (lodash, moment, @mui/material) that block tree-shaking |
Type Safety & Documentation
| Check | What it detects |
|---|---|
| Any Coverage | TypeScript any usage — counts explicit annotations, casts (as any), generics |
| JSDoc Coverage | Percentage of exported functions, classes, and interfaces with JSDoc comments |
Test Quality
| Check | What it detects |
|---|---|
| Test Ratio | Test-to-source file ratio |
| Empty Tests | it() / test() / def test_ / #[test] blocks with no assertions |
| LCOV Coverage | Reads existing lcov.info or coverage-summary.json — reports line, branch, function % without running tests |
CI/CD Security
| Check | What it detects |
|---|---|
| GitHub Actions Audit | Unpinned actions, pull_request_target misuse, ${{ }} injection, hardcoded secrets in workflow files |
Project health
| Check | What it detects |
|---|---|
| .env Consistency | Keys in .env missing from .env.example (fail); keys in .env.example missing from .env (warn); missing .env.example (warn) |
| Lockfile Sync | Missing lockfile when a manifest exists (fail); lockfile older than manifest by mtime (warn) — npm/yarn/pnpm, Cargo, Go, Composer, Poetry |
| Node Version Consistency | Mismatched Node major/minor across .nvmrc, .node-version, .tool-versions, package.json engines, Dockerfile FROM node: |
| README Check | Missing README.md, too few lines, or missing Installation / Usage / Contributing / License sections |
| Changelog Check | Missing or non–Keep a Changelog CHANGELOG.md (## [Unreleased], version headings, standard subsections) |
| CODEOWNERS Check | Missing .github/CODEOWNERS, invalid paths, or missing catch-all * rule (git repos only) |
TypeScript & frontend heuristics
| Check | What it detects |
|---|---|
| Async/Await Misuse | async callbacks in .forEach(), .then() without .catch(), discarded Promise.all / race results (JS/TS) |
| Error Handling | Empty or swallowed catch blocks; Python except: pass; Ruby rescue with no handling |
| Return Types | TypeScript functions / methods without explicit return type annotations (warnAt / failAt thresholds) |
| React Hook Rules | useEffect / useCallback / useMemo / useLayoutEffect / useInsertionEffect without a dependency array — only if react is in package.json |
| Accessibility Check | Missing img alt, empty decorative alt, unlabeled input / button / a in JSX, TSX, HTML, Vue, Svelte |
Linters, tsconfig, and framework hints
| Check | What it detects |
|---|---|
| ESLint | Project ESLint rules when ESLint is configured |
| Biome | Biome diagnostics when biome.json (or related config) is present |
| Oxlint | Fast Oxc linter when configured for the repo |
| Type Safety | Type escape hatches: as any, @ts-ignore, and similar patterns |
| Non-null Assertions | Overuse of postfix ! non-null assertions |
| TS Config Audit | tsconfig.json strictness and important compiler flags |
| Enum Usage | Questionable enum patterns vs const objects / unions |
| Broad Types | Over-broad types such as Record<K, any>, Array<any>, loose object / Function |
| Zod Consistency | Duplication or drift between Zod schemas and manual TypeScript types |
| ESLint Disable | eslint-disable / file-level suppressions worth reviewing |
| Zod Any | z.any() fields that effectively disable validation |
| N+1 | ORM / query patterns that suggest N+1 round-trips in loops |
| Edge Compatibility | Node-only APIs that are incompatible with edge / worker runtimes |
| Use Client Boundary | Next.js 'use client' modules importing server-only resources |
AI Agent Context
Project-scope checks for every file an AI coding agent reads before acting
(AGENTS.md, CLAUDE.md, GEMINI.md, .github/copilot-instructions.md, Cursor /
Antigravity / Windsurf / Zed rules, slash commands, prompt files, chatmodes,
Anthropic Skills, and @import graphs). Limits are sourced from Anthropic
Claude Code docs, OpenAI Codex docs, Cursor docs, and ACL 2023 tokenization
research — not made up.
| Check | What it detects |
|---|---|
| Context Budget | Combined token budget across all agent-visible files |
| Line Count / File Size | Per-file caps for AGENTS.md, CLAUDE.md, copilot-instructions.md, rules |
| Recommended Sections | Enforces the SnakeFlow 10-section AGENTS.md template (Overview, Tech Stack, Commands, Code Style, Architecture, Testing, Security, PR/Commits, Common Tasks, Verify) |
| Structure Block Drift | Warns when the managed <!-- SNAKEFLOW:STRUCTURE:BEGIN/END --> block in AGENTS.md drifted from projectStructure settings |
| Import Graph | Rejects @import chains deeper than 5 levels or with cycles |
| Cursor Rules Frontmatter | Validates YAML frontmatter for .cursor/rules/*.mdc (required name, description, apply-mode) |
| CLAUDE.md ↔ AGENTS.md Sync | Flags significant content drift between the two files |
| Copilot / AGENTS Conflict | Warns when both .github/copilot-instructions.md and AGENTS.md exist with overlapping instructions |
| Absolute Paths | Machine-specific paths (C:\Users\…, /home/…) that break on other machines |
| Secrets in Context | Leaked API keys / tokens inside any agent context file |
| Cyrillic Outside Code | Cyrillic text in instructions (~2.75× token cost vs. English per ACL 2023) |
| Orphaned / Duplicate Rules | Unreferenced or duplicated rule files across platforms |
AGENTS.md Structure Sync — the command SnakeFlow: Sync AGENTS.md Structure Block regenerates a managed region inside AGENTS.md from
projectStructure.fileRules / folderRules and quality.skipFiles /
skipDirs. The block only injects non-inferable architectural hints
(layer boundaries, file placement, required exports, don’t-touch zones) —
numeric limits are deliberately excluded because auto-generated context
files have been shown to reduce agent success rates by 0.5–3% (ETH Zurich,
Feb 2026). Auto-sync triggers on settings change when
devManager.agentContext.structureSync.autoSync is true.
Project Structure — Layer Visibility
The projectStructure.layers setting declares architectural layers with an
explicit import allow-list (inspired by Bazel visibility = [...] / Buck
package_boundary). Each file belongs to the first layer whose path glob
matches; its relative imports must resolve to a layer listed in canImport.
Per-file exceptions — layers can declare exceptions: [{file, canImport}]
for friend-module patterns (e.g. one file granted access to an otherwise
private layer). The first exception whose file glob matches wins; if none
match, the layer’s default canImport applies.
{ "name": "auth", "path": "src/auth/**", "canImport": ["utils"], "exceptions": [ { "file": "src/auth/service.ts", "canImport": ["utils", "crypto"] } ]}Config validation — before scanning imports, SnakeFlow validates the
layer graph: duplicate names, unknown layer references in canImport /
exceptions, and cyclic canImport edges (A -> B -> A) produce a single
fail result with the exact cycle path. This turns misconfiguration into one
actionable error instead of thousands of noisy per-import warnings chasing a
broken contract.
A matching User Agent Context provider (agentContextUser.*) validates
your global, cross-project files (~/.claude/**, ~/.codex/AGENTS.md,
~/.cursor/rules/**, ~/.cursor/skills/**, etc.) with the same rules.
Project Structure ↔ dependency-cruiser
Project Structure and dependency-cruiser
are two independent checks with different jobs. SnakeFlow does not
translate projectStructure.layers into depcruise forbidden rules — the
two configs live side by side, and the relationship between them is
enforced by a third check (Architecture Guidance) plus your AGENTS.md.
| Dimension | Project Structure (projectStructure.layers) | dependency-cruiser (.dependency-cruiser.cjs) |
|---|---|---|
| Runs where | In-process inside the extension (fast, live in editor) | External CLI via npx depcruise (slower, CI-friendly) |
| Config source | VS Code settings (devManager.quality.builtin.projectStructure.layers) | Repo file: .dependency-cruiser.{cjs,js,mjs,json} or devManager.quality.builtin.dependencyCruiser.rules |
| Primary purpose | Layer visibility / import allow-list (who-may-import-whom) | Circular imports + arbitrary forbidden rule graph |
| Scope | Your own source only; respects skipDirs | Whole JS/TS import graph incl. resolved node_modules refs |
| Per-file exceptions | Yes — exceptions: [{file, canImport}] (friend modules) | Yes — via depcruise forbidden[*].from.path regexes |
| Failure mode | Per-import warn/fail in Quality Hub | Single aggregated report (count of cycles + violations) |
| Auto-translation | No — rules are not converted into depcruise config | No — reads only its own config |
| What the skill sees | Settings JSON + the injected Project Structure block in AGENTS.md | .dependency-cruiser.cjs + cruise:err script + AGENTS.md line |
Why both?
Project Structuregives sub-second feedback while typing: it understands globs, friend-module exceptions, and emits one diagnostic per offending import. Great for the inner loop.dependency-cruiseris the CI source of truth: it detects cycles the layer check intentionally doesn’t model, integrates withdepcruise-reporter, produces SVG graphs, and is what external contributors’ PRs are gated on.- Running both means an agent that violates visibility gets caught twice: once live in the editor, once in CI. Different wording, same contract.
The bridge: Architecture Guidance (part of the agentContext
provider). When SnakeFlow sees a .dependency-cruiser.{cjs,js,mjs,json}
file (or .madgerc, or eslint-plugin-boundaries) but no mention of
the tool in AGENTS.md / CLAUDE.md / GEMINI.md / copilot-instructions.md,
it emits a warn:
Agent context doesn’t mention: dependency-cruiser — agents may write code that violates layer rules and fail CI
with the suggested fix:
## Verify Your Change- Layer rules live in .dependency-cruiser.cjs; run `npm run cruise:err` before PR.This is the mechanism that makes the skill actually see depcruise:
it’s a plain-text line under ## Architecture / ## Verify Your Change
that the agent reads the same way a human does, not a generated block.
Recommended setup when you use layer architecture:
- Declare layers once in
projectStructure.layers— this is your source of truth for the inner loop and for the auto-syncedProject Structureblock in AGENTS.md (non-inferable hints only: layer boundaries, file placement, don’t-touch zones). - Mirror the same boundaries in
.dependency-cruiser.cjsasforbiddenrules + ano-circularrule. Wire acruise:errnpm script:"scripts": { "cruise:err": "depcruise --config .dependency-cruiser.cjs src" } - Add one line under
## Architecturein AGENTS.md:Layer rules live in .dependency-cruiser.cjs; run \npm run cruise:err` before PR.That line is whatArchitecture Guidance` looks for, and what the agent skill picks up when planning changes that touch imports. - Leave
devManager.quality.builtin.dependencyCruiser.enabled: true(default) so Quality Hub runs depcruise on every sweep — it reuses your repo’s.dependency-cruiser.cjsautomatically.
Config resolution for dependency-cruiser (priority order, settings-wins):
devManager.quality.builtin.dependencyCruiser.rules(written to a temp JSON file per run, passed via--config, then deleted).- Local
.dependency-cruiser.{cjs,js,mjs,json}in the manifest dir. --no-config(depcruise defaults — cycles only).
Database & migrations
| Check | What it detects |
|---|---|
| Prisma Validate | prisma validate when the Prisma CLI is available (supports single-file schema.prisma and multi-file prisma/schema/*.prisma — pass the folder to --schema) |
| Prisma Migrate Status | Pending / drift Prisma migrations (needs DATABASE_URL when configured). When realtime is on, re-runs on changes under prisma/migrations/** and .env* (debounced, cached) — disable with devManager.quality.builtin.prismaMigrateStatus.realtime |
| Drizzle Check | Drizzle schema vs migration consistency when Drizzle tooling is present |
| No Manual Migrations | Hand-written or hand-edited migration SQL (Drizzle, Prisma, or Atlas); optional git history scan for deleted/renamed migrations — also runs in realtime on save and file watch when enabled |
| Migrations CI Gate | Pure Node: fails when a Prisma/Drizzle/Atlas project has no GitHub Actions workflow that runs migration safety commands (prisma migrate diff, drizzle-kit check, atlas migrate validate, SnakeFlow / cruise:err, etc.), or when realtime or No Manual Migrations is disabled locally |
No Manual Migrations {#no-manual-migrations}
builtin-noManualMigrations discourages editing migration files by hand. It runs in the full Quality Hub sweep and, when realtime quality is on, re-checks on save and when watched migration/schema files change. Diagnostics attach to the offending file.
| Stack | How it works |
|---|---|
| Drizzle | Preferred: a temporary wrapper drizzle.config.mjs re-exports your config with out: pointing to a temp dir, then drizzle-kit generate runs there and the extension diffs the result against your real migrations folder — this catches edits to existing .sql files, not only stray files. Fallback when regen is unavailable (old drizzle-kit, TS-only config without tsx, etc.): meta/_journal.json must list every .sql in out, plus drizzle-kit check / generate --check for drift. The migrations out path is resolved by importing the config via Node ESM (with npx tsx for .ts configs), cached by file mtime, with a regex fallback. |
| Prisma | prisma migrate diff compares the schema to applied migrations and checks that concatenated migration.sql matches what Prisma would regenerate. Multi-file schema: if prisma/schema/*.prisma exists, --schema is passed the folder (same as Prisma CLI). |
| Atlas | atlas migrate validate — atlas.sum checksums must match migration files. |
| All adapters | When checkRemoved is on (default), recent git history is scanned for deleted or renamed files under the migrations directory — applied migrations should be append-only. Requires git in PATH and a git repo. |
Settings:
"devManager.quality.builtin.noManualMigrations.enabled": true,"devManager.quality.builtin.noManualMigrations.severity": "error","devManager.quality.builtin.noManualMigrations.adapters": ["drizzle", "prisma", "atlas"],"devManager.quality.builtin.noManualMigrations.checkRemoved": true,"devManager.quality.builtin.noManualMigrations.gitLookback": 50adapters limits which toolchains to probe; each adapter skips when that stack is not detected. gitLookback is the number of recent commits scanned for delete/rename events (1–5000). Other ORMs (Rails, Django, TypeORM, etc.) are not covered in v1. Requires the relevant CLI in PATH or resolvable via npx where applicable.
See CLI tool checks — No Manual Migrations for install notes.
Squawk extras (data migrations & rollbacks)
The Squawk provider still runs the squawk-cli linter on .sql files. In addition, a pure Node post-pass can flag risky data migrations and missing down paths (configurable):
unbatched-data-migration—UPDATE/DELETE FROMwithoutLIMITor a batchedWHERE … IN (…)pattern (heuristic; tune or disable if noisy).missing-lock-timeout—BEGIN/COMMITpresent withoutSET LOCAL lock_timeoutorSET LOCAL statement_timeout.missing-down-migration— for non-Prisma / non-Drizzle SQL or TS migrations: require a sibling*.down.sqlnext to*.up.sql, or adown()export in.ts/.jsmigrations (requireDownMigration).
"devManager.quality.builtin.squawk.dataRisk": "warn","devManager.quality.builtin.squawk.requireDownMigration": falsedataRisk: off | warn | error (default warn).
Configuring Thresholds
Every built-in check supports enabled, and most support warnAt/failAt thresholds:
"devManager.quality.builtin.lineCount.enabled": true,"devManager.quality.builtin.lineCount.warnLines": 300,"devManager.quality.builtin.lineCount.failLines": 500,
"devManager.quality.builtin.functionLength.warnLines": 40,"devManager.quality.builtin.functionLength.failLines": 80,
"devManager.quality.builtin.complexity.warnScore": 10,"devManager.quality.builtin.complexity.failScore": 20,
"devManager.quality.builtin.parameterCount.warnAt": 4,"devManager.quality.builtin.parameterCount.failAt": 7,
"devManager.quality.builtin.dependencyCount.warnAt": 50,"devManager.quality.builtin.dependencyCount.failAt": 100,
"devManager.quality.builtin.returnTypes.warnAt": 10,"devManager.quality.builtin.returnTypes.failAt": 50To disable a check entirely:
"devManager.quality.builtin.magicNumbers.enabled": falseThe TypeScript Check (tscCheck) runs npx tsc --noEmit when tsconfig.json exists — see CLI Tool Checks.