vipr.config.json Schema
Vipr works without a config file. When you add one, it gives you a persistent place to set output defaults, enforce quality thresholds, tune per-analyzer behavior, and express environment-specific overrides for CI. This page documents every supported field.
Location and discovery
Vipr walks up from the current working directory looking for vipr.config.json. Place it at your
project root alongside package.json.
# Point at a specific file
vipr analyze src/ --config ./config/vipr.config.json
# Skip config loading entirely
vipr analyze src/ --no-config
CLI flags always override config file values. Config file values override built-in defaults.
Minimal example
{
"$schema": "https://vipr.dev/schemas/vipr-config.schema.json",
"output": {
"failThreshold": 70,
"failOnCritical": true
}
}
Full structure
{
"$schema": "https://vipr.dev/schemas/vipr-config.schema.json",
"extends": "./base.config.json",
"global": { ... },
"output": { ... },
"plugins": { ... },
"analyses": { ... },
"env": { ... }
}
global
Cross-cutting settings that apply before any analyzer runs.
| Key | Type | Default | Description |
|---|---|---|---|
global.debug |
boolean |
false |
Print debug-level logs during analysis |
global.cache |
boolean |
true |
Enable in-memory result caching |
global.cacheTTL |
number |
3600000 |
Cache time-to-live in milliseconds |
global.ignorePatterns |
string[] |
Built-in list | Additional paths and glob patterns to exclude |
global.gradeBoundaries |
object |
See below | Complexity score cutoffs for grade letters |
Default ignore patterns
The built-in ignore list covers: node_modules, .git, dist, build, .next, coverage,
.turbo, out, .vite, __mocks__, mock, mocks, __tests__, fixtures, docs, public,
generated. Files matching *.test.*, *.spec.*, *.stories.*, *.d.ts, and *.json are
also excluded automatically.
Use ignorePatterns to extend — not replace — that list:
{
"global": {
"ignorePatterns": ["scripts/**", "e2e/**", "**/*.generated.ts"]
}
}
gradeBoundaries
Maps the 0–100 quality score to letter grades. Lower raw score = worse quality. The defaults:
{
"global": {
"gradeBoundaries": {
"A": 25,
"B": 45,
"C": 65,
"D": 80
}
}
}
A score below the A boundary receives an A grade; between A and B boundaries receives B; and
so on. A score above D receives an F. The strict preset tightens these boundaries.
output
Controls how results are formatted and when the command exits non-zero.
| Key | Type | Default | Description |
|---|---|---|---|
output.format |
cli | json | json-full | markdown |
cli |
Default output format |
output.minSeverity |
info | warning | critical |
info |
Lowest severity level shown |
output.failThreshold |
number |
0 |
Exit non-zero when any file scores below this (0–100) |
output.failOnCritical |
boolean |
false |
Exit non-zero when any critical-severity finding is present |
output.compact |
boolean |
false |
Minify JSON output (no whitespace) |
Setting failThreshold to 0 disables the score gate. Setting failOnCritical to true is
the most common CI configuration — it fails only when Vipr finds something it classifies as
immediately actionable.
plugins
Per-analyzer configuration keyed by plugin ID. Each plugin owns its own threshold and weight schema. Unrecognized keys are silently ignored.
{
"plugins": {
"core": { ... },
"react": { ... }
}
}
plugins.core
The core plugin scores every JS/TS file on cyclomatic complexity, cognitive complexity, Halstead metrics, maintainability index, and lines of code.
{
"plugins": {
"core": {
"id": "core",
"thresholds": {
"cyclomaticComplexity": 10,
"cognitiveComplexity": 15,
"halsteadVolume": 1000,
"halsteadDifficulty": 30,
"linesOfCode": 250
},
"weights": {
"complexity": {
"cyclomatic": 0.3,
"cognitive": 0.3,
"halstead": 0.2,
"linesOfCode": 0.2
}
}
}
}
}
Core thresholds — all are single numbers; files that exceed the threshold receive a warning:
| Key | Default | What it measures |
|---|---|---|
cyclomaticComplexity |
10 |
Number of independent paths through a function |
cognitiveComplexity |
15 |
How hard a function is to understand (nested conditionals cost more) |
halsteadVolume |
1000 |
Estimated program size in bits based on operators and operands |
halsteadDifficulty |
30 |
Effort required to write or understand the code |
linesOfCode |
250 |
Raw line count (physical lines, not logical statements) |
Core complexity weights — must sum to 1.0. Changing these shifts how the composite score is
calculated across the four complexity dimensions:
| Key | Default | Role |
|---|---|---|
cyclomatic |
0.3 |
Weight for cyclomatic complexity dimension |
cognitive |
0.3 |
Weight for cognitive complexity dimension |
halstead |
0.2 |
Weight for Halstead dimension |
linesOfCode |
0.2 |
Weight for lines-of-code dimension |
plugins.react
The React plugin analyzes .tsx and .jsx files for structural complexity, hook usage, temporal
coupling (effects), prop coupling, component identity, dataflow, anti-patterns, security,
accessibility, performance, reliability, and technical debt.
The React plugin config surface that is stable for user overrides:
{
"plugins": {
"react": {
"id": "react",
"thresholds": {
"structural": {
"warning": 15
},
"hooks": {
"total": 10,
"customHookSuggestion": 8
},
"coupling": {
"propsWarning": 10,
"contextWarning": 3
}
}
}
}
}
React structural thresholds:
| Key | Default | Description |
|---|---|---|
structural.warning |
15 |
Structural complexity score at which a warning is raised |
React hook thresholds:
| Key | Default | Description |
|---|---|---|
hooks.total |
10 |
Total hook calls before a warning |
hooks.customHookSuggestion |
8 |
Hook count above which extracting a custom hook is suggested |
hooks.stateCountWarning |
3 |
useState calls per component before warning |
hooks.effectCountWarning |
2 |
useEffect calls per component before warning |
React coupling thresholds:
| Key | Default | Description |
|---|---|---|
coupling.propsWarning |
10 |
Prop count before a coupling warning |
coupling.contextWarning |
3 |
Context consumers before a warning |
coupling.propsDrillingThreshold |
7 |
Prop count before prop-drilling detection activates |
Note: The
weightsobject on the React plugin config is large and covers many internal scoring dimensions (structural, hooks, temporal, coupling, identity, dataflow, reliability, accessibility, security, anti-pattern). These fields are read at runtime but are not recommended for routine user override — changing them without understanding the scoring model will produce unexpected results. Stick tothresholdsfor day-to-day tuning.
analyses
Enable or disable individual named analyses without touching plugin-level thresholds.
{
"analyses": {
"core-dead-code": { "enabled": false },
"react-migration": { "enabled": false }
}
}
Analysis IDs follow the pattern <plugin>-<name>. The full list of built-in analysis IDs:
| Plugin | Analysis IDs |
|---|---|
| core | core-cyclomatic, core-cognitive, core-halstead, core-maintainability, core-functions, core-dead-code |
| react | react-structural, react-hooks, react-temporal, react-coupling, react-identity, react-dataflow, react-anti-patterns, react-security, react-accessibility, react-performance, react-reliability, react-technical-debt, react-migration |
extends
Layer one config on top of another. Relative paths are resolved from the file that declares them.
{
"extends": "./base.config.json",
"output": {
"format": "json"
}
}
Arrays replace rather than merge when extending. Nested objects merge deeply. You can pass an array to extend multiple configs in order:
{
"extends": ["./base.config.json", "./ci.config.json"]
}
env
Override any config section for a named environment. The public CLI does not expose an --env
flag today. The env block is still part of the shared config contract for callers that supply an
environment name before CLI overrides are applied.
{
"output": { "format": "cli" },
"env": {
"ci": {
"output": {
"format": "json",
"failThreshold": 75,
"failOnCritical": true
}
}
}
}
Presets
Run vipr init to scaffold a starter config interactively. Use --preset to skip the wizard:
vipr init --preset strict --quiet
| Preset | failThreshold |
failOnCritical |
Grade boundaries | Use when |
|---|---|---|---|---|
default |
60 |
false |
Standard (A=25, B=45, C=65, D=80) | New projects, legacy codebases |
strict |
80 |
true |
Tighter (A=15, B=30, C=50, D=70) | Greenfield projects, high-quality targets |
lenient |
40 |
false |
Looser (A=35, B=55, C=75, D=90) | Bootstrapping gates on existing debt |
The strict preset also tightens React and core thresholds:
{
"plugins": {
"core": {
"id": "core",
"thresholds": {
"cyclomaticComplexity": { "warning": 8, "critical": 15 }
}
},
"react": {
"id": "react",
"thresholds": {
"structural": { "warning": 12 },
"hooks": { "total": 8, "customHookSuggestion": 6 },
"coupling": { "propsWarning": 6 }
}
}
}
}
Overriding for test files
The env block does not yet support per-glob overrides in the public CLI. To exclude test files
from analysis entirely, use global.ignorePatterns:
{
"global": {
"ignorePatterns": ["**/*.test.ts", "**/*.spec.tsx", "**/__tests__/**"]
}
}
Test files matching .test.* and .spec.* are already excluded by the built-in ignore list.
Note: Per-glob threshold overrides (e.g., applying lenient thresholds to
*.test.ts) are not yet supported invipr.config.json. If this is blocking for you, open an issue — the schema has a placeholder for it.