pr-checkmate 1.23.23 → 2.0.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 (101) hide show
  1. package/README.md +68 -15
  2. package/cspell.json +3 -8
  3. package/dist/cli.js +37 -62
  4. package/dist/config/constants.d.ts +21 -3
  5. package/dist/config/constants.js +33 -6
  6. package/dist/config/pr-checkmate-workflow.yml +33 -327
  7. package/dist/core/checks/dependencies/banned-imports.js +23 -24
  8. package/dist/core/checks/dependencies/circular-deps.js +1 -0
  9. package/dist/core/checks/dependencies/dependency-check.js +1 -0
  10. package/dist/core/checks/dependencies/grype-scan.js +1 -0
  11. package/dist/core/checks/dependencies/npm-audit.js +6 -1
  12. package/dist/core/checks/dependencies/vuln-scan.js +1 -0
  13. package/dist/core/checks/findings.d.ts +198 -19
  14. package/dist/core/checks/findings.js +474 -22
  15. package/dist/core/checks/git/custom-rules.js +21 -16
  16. package/dist/core/checks/git/diff-smell.d.ts +17 -0
  17. package/dist/core/checks/git/diff-smell.js +43 -24
  18. package/dist/core/checks/git/large-files.js +14 -2
  19. package/dist/core/checks/git/leftover-debug.js +23 -14
  20. package/dist/core/checks/git/lockfile-drift.js +2 -1
  21. package/dist/core/checks/git/merge-conflict.js +19 -8
  22. package/dist/core/checks/git/missing-tests.js +22 -6
  23. package/dist/core/checks/git/todo-fixme.js +10 -9
  24. package/dist/core/checks/languages/shellcheck.d.ts +8 -0
  25. package/dist/core/checks/languages/shellcheck.js +55 -0
  26. package/dist/core/checks/languages/typecheck.js +1 -0
  27. package/dist/core/checks/pr/pr-size-check.js +2 -1
  28. package/dist/core/checks/quality/config-validation.d.ts +14 -4
  29. package/dist/core/checks/quality/config-validation.js +119 -17
  30. package/dist/core/checks/quality/dead-code.js +1 -0
  31. package/dist/core/checks/quality/duplicate-check.js +1 -0
  32. package/dist/core/checks/quality/license-header.js +12 -2
  33. package/dist/core/checks/quality/spellcheck.d.ts +50 -0
  34. package/dist/core/checks/quality/spellcheck.js +141 -13
  35. package/dist/core/checks/quality/symlinks.d.ts +16 -0
  36. package/dist/core/checks/quality/symlinks.js +62 -0
  37. package/dist/core/checks/quality/yaml-lint.js +22 -9
  38. package/dist/core/checks/security/diff-security.js +111 -28
  39. package/dist/core/checks/security/dockerfile-security.js +23 -20
  40. package/dist/core/checks/security/migration-safety.js +19 -17
  41. package/dist/core/checks/security/sensitive-file-guard.js +37 -5
  42. package/dist/core/checks/security/workflow-security.d.ts +10 -0
  43. package/dist/core/checks/security/workflow-security.js +48 -26
  44. package/dist/core/commands.d.ts +27 -0
  45. package/dist/core/commands.js +91 -0
  46. package/dist/core/context.d.ts +12 -3
  47. package/dist/core/context.js +118 -16
  48. package/dist/core/install/cache.d.ts +39 -0
  49. package/dist/core/install/cache.js +99 -0
  50. package/dist/core/install/doctor.d.ts +35 -0
  51. package/dist/core/install/doctor.js +145 -0
  52. package/dist/core/install/ensure.d.ts +60 -0
  53. package/dist/core/install/ensure.js +169 -0
  54. package/dist/core/install/lock.d.ts +33 -0
  55. package/dist/core/install/lock.js +99 -0
  56. package/dist/core/install/profiles.d.ts +64 -0
  57. package/dist/core/install/profiles.js +170 -0
  58. package/dist/core/registry.js +4 -0
  59. package/dist/core/reporters.d.ts +13 -2
  60. package/dist/core/reporters.js +145 -104
  61. package/dist/core/run-checks.js +91 -14
  62. package/dist/core/targets.js +26 -4
  63. package/dist/core/types.d.ts +49 -1
  64. package/dist/init.d.ts +29 -14
  65. package/dist/init.js +204 -262
  66. package/dist/interfaces/index.d.ts +130 -0
  67. package/dist/scripts/generate-install-manifests.d.ts +39 -0
  68. package/dist/scripts/generate-install-manifests.js +121 -0
  69. package/dist/scripts/publish-github.js +18 -4
  70. package/dist/utils/detect-language.d.ts +37 -4
  71. package/dist/utils/detect-language.js +113 -19
  72. package/dist/utils/http.d.ts +59 -0
  73. package/dist/utils/http.js +119 -0
  74. package/dist/utils/logger.d.ts +52 -6
  75. package/dist/utils/logger.js +125 -33
  76. package/dist/utils/pool.d.ts +49 -0
  77. package/dist/utils/pool.js +124 -0
  78. package/dist/utils/update-notifier.d.ts +9 -0
  79. package/dist/utils/update-notifier.js +57 -3
  80. package/eslint.config.mjs +14 -2
  81. package/install-manifests/commitlint/package-lock.json +1028 -0
  82. package/install-manifests/commitlint/package.json +9 -0
  83. package/install-manifests/core/package-lock.json +1335 -0
  84. package/install-manifests/core/package.json +11 -0
  85. package/install-manifests/cpp/package-lock.json +29 -0
  86. package/install-manifests/cpp/package.json +8 -0
  87. package/install-manifests/duplicate/package-lock.json +113 -0
  88. package/install-manifests/duplicate/package.json +8 -0
  89. package/install-manifests/js/package-lock.json +5099 -0
  90. package/install-manifests/js/package.json +26 -0
  91. package/install-manifests/markdown/package-lock.json +1281 -0
  92. package/install-manifests/markdown/package.json +8 -0
  93. package/install-manifests/python/package-lock.json +21 -0
  94. package/install-manifests/python/package.json +8 -0
  95. package/install-manifests/spellcheck/package-lock.json +1047 -0
  96. package/install-manifests/spellcheck/package.json +8 -0
  97. package/package.json +13 -28
  98. package/pr-checkmate.schema.json +1070 -96
  99. package/src/config/cspell.json +3 -8
  100. package/src/config/pr-checkmate-workflow.yml +33 -327
  101. package/src/config/pr-checkmate.schema.json +1070 -96
@@ -5,20 +5,45 @@
5
5
  "type": "object",
6
6
  "additionalProperties": true,
7
7
  "properties": {
8
- "$schema": { "type": "string" },
8
+ "$schema": {
9
+ "type": "string"
10
+ },
9
11
  "sourcePath": {
10
12
  "description": "Directory (or directories) checks are scoped to. Defaults to '.'.",
11
- "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }]
13
+ "oneOf": [
14
+ {
15
+ "type": "string"
16
+ },
17
+ {
18
+ "type": "array",
19
+ "items": {
20
+ "type": "string"
21
+ }
22
+ }
23
+ ],
24
+ "default": "."
25
+ },
26
+ "excludePaths": {
27
+ "description": "Files and folders no check ever looks at, as globs (\"src/generated/**\", \"vendor/legacy.ts\", \"**/*.min.js\"). Unlike ignoreDirs — which matches a directory NAME on any segment — this addresses one path or one file.",
28
+ "type": "array",
29
+ "items": {
30
+ "type": "string"
31
+ },
32
+ "default": []
12
33
  },
13
34
  "ignoreDirs": {
14
35
  "description": "Directory names excluded from every check.",
15
36
  "type": "array",
16
- "items": { "type": "string" }
37
+ "items": {
38
+ "type": "string"
39
+ }
17
40
  },
18
41
  "severity": {
19
42
  "description": "Universal per-check severity override, keyed by the check's display name.",
20
43
  "type": "object",
21
- "additionalProperties": { "enum": ["error", "warn", "off"] }
44
+ "additionalProperties": {
45
+ "enum": ["error", "warn", "off"]
46
+ }
22
47
  },
23
48
  "customRules": {
24
49
  "description": "User-defined regex rules run against the diff.",
@@ -27,13 +52,33 @@
27
52
  "type": "object",
28
53
  "required": ["name", "pattern"],
29
54
  "properties": {
30
- "name": { "type": "string" },
31
- "pattern": { "type": "string" },
32
- "message": { "type": "string" },
33
- "severity": { "enum": ["error", "warn"] },
34
- "include": { "type": "array", "items": { "type": "string" } },
35
- "exclude": { "type": "array", "items": { "type": "string" } },
36
- "flags": { "type": "string" }
55
+ "name": {
56
+ "type": "string"
57
+ },
58
+ "pattern": {
59
+ "type": "string"
60
+ },
61
+ "message": {
62
+ "type": "string"
63
+ },
64
+ "severity": {
65
+ "enum": ["error", "warn"]
66
+ },
67
+ "include": {
68
+ "type": "array",
69
+ "items": {
70
+ "type": "string"
71
+ }
72
+ },
73
+ "exclude": {
74
+ "type": "array",
75
+ "items": {
76
+ "type": "string"
77
+ }
78
+ },
79
+ "flags": {
80
+ "type": "string"
81
+ }
37
82
  }
38
83
  }
39
84
  },
@@ -44,62 +89,141 @@
44
89
  "type": "object",
45
90
  "required": ["name"],
46
91
  "properties": {
47
- "name": { "type": "string" },
48
- "message": { "type": "string" },
49
- "severity": { "enum": ["error", "warn"] }
92
+ "name": {
93
+ "type": "string"
94
+ },
95
+ "message": {
96
+ "type": "string"
97
+ },
98
+ "severity": {
99
+ "enum": ["error", "warn"]
100
+ }
50
101
  }
51
102
  }
52
103
  },
53
104
  "prSize": {
54
105
  "type": "object",
55
106
  "properties": {
56
- "enabled": { "type": "boolean" },
57
- "maxFiles": { "type": "number" },
58
- "maxLines": { "type": "number" }
59
- }
107
+ "enabled": {
108
+ "type": "boolean",
109
+ "default": true
110
+ },
111
+ "maxFiles": {
112
+ "type": "number",
113
+ "default": 50
114
+ },
115
+ "maxLines": {
116
+ "type": "number",
117
+ "default": 1000
118
+ }
119
+ },
120
+ "description": "PR size advisory: warns when a pull request changes more lines or files than the thresholds."
60
121
  },
61
122
  "largeFiles": {
62
123
  "type": "object",
63
124
  "properties": {
64
- "enabled": { "type": "boolean" },
65
- "severity": { "enum": ["error", "warn"] },
66
- "maxSizeKb": { "type": "number" },
67
- "allow": { "type": "array", "items": { "type": "string" } }
68
- }
125
+ "enabled": {
126
+ "type": "boolean",
127
+ "default": true
128
+ },
129
+ "severity": {
130
+ "enum": ["error", "warn"],
131
+ "default": "warn"
132
+ },
133
+ "maxSizeKb": {
134
+ "type": "number",
135
+ "default": 500
136
+ },
137
+ "allow": {
138
+ "type": "array",
139
+ "items": {
140
+ "type": "string"
141
+ }
142
+ }
143
+ },
144
+ "description": "Flags large or binary files added to the diff."
69
145
  },
70
146
  "licenseHeader": {
71
147
  "type": "object",
72
148
  "properties": {
73
- "enabled": { "type": "boolean" },
74
- "pattern": { "type": "string" },
75
- "lines": { "type": "number" }
76
- }
149
+ "enabled": {
150
+ "type": "boolean",
151
+ "default": false
152
+ },
153
+ "pattern": {
154
+ "type": "string"
155
+ },
156
+ "lines": {
157
+ "type": "number",
158
+ "default": 10
159
+ }
160
+ },
161
+ "description": "Requires a license header on added source files. Opt-in."
77
162
  },
78
163
  "coverage": {
79
164
  "type": "object",
80
165
  "properties": {
81
- "enabled": { "type": "boolean" },
82
- "severity": { "enum": ["error", "warn"] },
166
+ "enabled": {
167
+ "type": "boolean",
168
+ "default": false
169
+ },
170
+ "severity": {
171
+ "enum": ["error", "warn"]
172
+ },
83
173
  "reportPath": {
84
- "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }]
174
+ "oneOf": [
175
+ {
176
+ "type": "string"
177
+ },
178
+ {
179
+ "type": "array",
180
+ "items": {
181
+ "type": "string"
182
+ }
183
+ }
184
+ ]
85
185
  }
86
- }
186
+ },
187
+ "description": "Warns when the test step produced no coverage report. Opt-in; never parses percentages."
87
188
  },
88
189
  "vulnScan": {
89
190
  "type": "object",
90
- "properties": { "enabled": { "type": "boolean" }, "timeoutMs": { "type": "number" } }
191
+ "properties": {
192
+ "enabled": {
193
+ "type": "boolean",
194
+ "default": false
195
+ },
196
+ "timeoutMs": {
197
+ "type": "number",
198
+ "default": 120000
199
+ }
200
+ },
201
+ "description": "Cross-language vulnerability scan via osv-scanner. Opt-in."
91
202
  },
92
203
  "grypeScan": {
93
204
  "type": "object",
94
205
  "properties": {
95
- "enabled": { "type": "boolean" },
206
+ "enabled": {
207
+ "type": "boolean",
208
+ "default": true
209
+ },
96
210
  "minSeverity": {
97
211
  "type": "string",
98
- "enum": ["negligible", "low", "medium", "high", "critical"]
212
+ "enum": ["negligible", "low", "medium", "high", "critical"],
213
+ "default": "medium"
99
214
  },
100
- "ignore": { "type": "array", "items": { "type": "string" } },
101
- "timeoutMs": { "type": "number" }
102
- }
215
+ "ignore": {
216
+ "type": "array",
217
+ "items": {
218
+ "type": "string"
219
+ }
220
+ },
221
+ "timeoutMs": {
222
+ "type": "number",
223
+ "default": 120000
224
+ }
225
+ },
226
+ "description": "Direct-dependency vulnerability scan via grype."
103
227
  },
104
228
  "security": {
105
229
  "type": "object",
@@ -107,8 +231,13 @@
107
231
  "gitleaks": {
108
232
  "type": "object",
109
233
  "properties": {
110
- "enabled": { "type": "boolean" },
111
- "version": { "type": "string" },
234
+ "enabled": {
235
+ "type": "boolean",
236
+ "default": true
237
+ },
238
+ "version": {
239
+ "type": "string"
240
+ },
112
241
  "depth": {
113
242
  "description": "Caps the whole-repository history scan used when there is no diff range (a local run or --full) to the last N commits. Ignored in a PR run, which scans the diff range.",
114
243
  "type": "integer",
@@ -119,120 +248,965 @@
119
248
  "npm-audit": {
120
249
  "type": "object",
121
250
  "properties": {
122
- "enabled": { "type": "boolean" },
251
+ "enabled": {
252
+ "type": "boolean",
253
+ "default": true
254
+ },
123
255
  "auditLevel": {
124
256
  "type": "string",
125
- "enum": ["info", "low", "moderate", "high", "critical"]
257
+ "enum": ["info", "low", "moderate", "high", "critical"],
258
+ "default": "high"
126
259
  },
127
260
  "ignore": {
128
261
  "type": "array",
129
- "items": { "type": "string" },
262
+ "items": {
263
+ "type": "string"
264
+ },
130
265
  "description": "Package names whose vulnerabilities never block, even when direct and at/above auditLevel. Still reported as warnings."
131
266
  }
132
267
  }
133
268
  }
269
+ },
270
+ "description": "Secret scanning (gitleaks) and npm audit."
271
+ },
272
+ "mergeConflict": {
273
+ "description": "Conflict markers left in a file. Blocking by default — this is breakage, not an opinion.",
274
+ "type": "object",
275
+ "properties": {
276
+ "enabled": {
277
+ "type": "boolean",
278
+ "default": true
279
+ },
280
+ "severity": {
281
+ "enum": ["error", "warn"],
282
+ "default": "error"
283
+ }
134
284
  }
135
285
  },
136
- "mergeConflict": { "$ref": "#/$defs/toggleSeverity" },
137
- "leftoverDebug": { "$ref": "#/$defs/toggleSeverity" },
138
- "lockfileDrift": { "$ref": "#/$defs/toggleSeverity" },
139
- "missingTests": { "$ref": "#/$defs/toggleSeverity" },
140
- "diffSecurity": { "$ref": "#/$defs/toggleIgnore" },
141
- "workflowSecurity": { "$ref": "#/$defs/toggleIgnore" },
142
- "dockerfileSecurity": { "$ref": "#/$defs/toggleIgnore" },
143
- "migrationSafety": { "$ref": "#/$defs/toggleSeverity" },
286
+ "leftoverDebug": {
287
+ "$ref": "#/$defs/toggleSeverity"
288
+ },
289
+ "lockfileDrift": {
290
+ "$ref": "#/$defs/toggleSeverity"
291
+ },
292
+ "missingTests": {
293
+ "$ref": "#/$defs/toggleSeverity"
294
+ },
295
+ "diffSecurity": {
296
+ "$ref": "#/$defs/patternCheck"
297
+ },
298
+ "workflowSecurity": {
299
+ "$ref": "#/$defs/toggleIgnorePaths"
300
+ },
301
+ "dockerfileSecurity": {
302
+ "$ref": "#/$defs/toggleIgnorePaths"
303
+ },
304
+ "migrationSafety": {
305
+ "$ref": "#/$defs/toggleSeverity"
306
+ },
144
307
  "todoFixme": {
145
308
  "type": "object",
146
309
  "properties": {
147
- "enabled": { "type": "boolean" },
148
- "severity": { "enum": ["error", "warn"] },
149
- "requireTicket": { "type": "boolean" },
150
- "keywords": { "type": "array", "items": { "type": "string" } }
310
+ "enabled": {
311
+ "type": "boolean"
312
+ },
313
+ "severity": {
314
+ "enum": ["error", "warn"]
315
+ },
316
+ "requireTicket": {
317
+ "type": "boolean"
318
+ },
319
+ "keywords": {
320
+ "type": "array",
321
+ "items": {
322
+ "type": "string"
323
+ }
324
+ }
325
+ },
326
+ "description": "Flags TODO/FIXME markers added in the diff."
327
+ },
328
+ "diffSmell": {
329
+ "$ref": "#/$defs/togglePaths"
330
+ },
331
+ "caseCollision": {
332
+ "$ref": "#/$defs/toggle"
333
+ },
334
+ "sensitiveFileGuard": {
335
+ "$ref": "#/$defs/toggleIgnore",
336
+ "type": "object",
337
+ "properties": {
338
+ "ignorePaths": {
339
+ "description": "Globs for files this check never looks at — the exclusion for a single false positive (a .pem fixture). Unlike \"ignore\", it does not silence a whole category.",
340
+ "type": "array",
341
+ "items": {
342
+ "type": "string"
343
+ }
344
+ }
345
+ }
346
+ },
347
+ "configValidation": {
348
+ "description": "Validation of pr-checkmate.json itself: unknown keys, invalid levels, and severity keys naming no check.",
349
+ "type": "object",
350
+ "properties": {
351
+ "enabled": {
352
+ "description": "Run the check.",
353
+ "type": "boolean"
354
+ },
355
+ "severity": {
356
+ "description": "Findings severity. \"warn\" (default) advises; \"error\" fails the run.",
357
+ "enum": ["error", "warn"]
358
+ },
359
+ "validateCheckNames": {
360
+ "description": "Verify that keys in \"severity\" name a real check. On by default.",
361
+ "type": "boolean"
362
+ },
363
+ "allowUnknownKeys": {
364
+ "description": "Top-level keys to accept without complaint.",
365
+ "type": "array",
366
+ "items": {
367
+ "type": "string"
368
+ }
369
+ }
151
370
  }
152
371
  },
153
- "diffSmell": { "$ref": "#/$defs/toggle" },
154
- "caseCollision": { "$ref": "#/$defs/toggle" },
155
- "sensitiveFileGuard": { "$ref": "#/$defs/toggle" },
156
- "configValidation": { "$ref": "#/$defs/toggle" },
157
372
  "cspell": {
158
373
  "description": "Spellcheck control. 'enabled: false' turns the check off. The other fields configure the cspell config pr-checkmate generates, so they apply only when the repository has no cspell.json of its own: 'words', 'ignorePaths', 'ignoreRegExpList' and 'dictionaries' are appended to the bundled defaults, 'language' replaces it. Ignore globs are anchored to the repository root, so '**/dist/**' never matches a directory above the checkout.",
159
374
  "type": "object",
160
375
  "properties": {
161
- "enabled": { "type": "boolean" },
162
- "language": { "type": "string" },
163
- "words": { "type": "array", "items": { "type": "string" } },
164
- "dictionaries": { "type": "array", "items": { "type": "string" } },
165
- "ignorePaths": { "type": "array", "items": { "type": "string" } },
166
- "ignoreRegExpList": { "type": "array", "items": { "type": "string" } }
376
+ "enabled": {
377
+ "type": "boolean"
378
+ },
379
+ "language": {
380
+ "type": "string"
381
+ },
382
+ "words": {
383
+ "type": "array",
384
+ "items": {
385
+ "type": "string"
386
+ }
387
+ },
388
+ "dictionaries": {
389
+ "type": "array",
390
+ "items": {
391
+ "type": "string"
392
+ }
393
+ },
394
+ "ignorePaths": {
395
+ "type": "array",
396
+ "items": {
397
+ "type": "string"
398
+ }
399
+ },
400
+ "ignoreRegExpList": {
401
+ "type": "array",
402
+ "items": {
403
+ "type": "string"
404
+ }
405
+ }
167
406
  }
168
407
  },
169
408
  "yamlLint": {
170
409
  "type": "object",
171
410
  "properties": {
172
- "enabled": { "type": "boolean" },
173
- "severity": { "enum": ["error", "warn"] },
174
- "ignore": { "type": "array", "items": { "type": "string" } }
175
- }
411
+ "enabled": {
412
+ "type": "boolean",
413
+ "default": true
414
+ },
415
+ "severity": {
416
+ "enum": ["error", "warn"]
417
+ },
418
+ "ignore": {
419
+ "type": "array",
420
+ "items": {
421
+ "type": "string"
422
+ }
423
+ }
424
+ },
425
+ "description": "YAML syntax and style validation."
426
+ },
427
+ "symlinks": {
428
+ "type": "object",
429
+ "properties": {
430
+ "enabled": {
431
+ "type": "boolean"
432
+ },
433
+ "severity": {
434
+ "enum": ["error", "warn"]
435
+ },
436
+ "ignore": {
437
+ "type": "array",
438
+ "items": {
439
+ "type": "string"
440
+ }
441
+ }
442
+ },
443
+ "description": "Flags tracked symlinks whose target does not exist. Whole-repo scan, not delta."
444
+ },
445
+ "shellcheck": {
446
+ "type": "object",
447
+ "properties": {
448
+ "enabled": {
449
+ "type": "boolean"
450
+ },
451
+ "minSeverity": {
452
+ "enum": ["error", "warning", "info", "style"]
453
+ },
454
+ "ignore": {
455
+ "type": "array",
456
+ "items": {
457
+ "type": "string"
458
+ }
459
+ }
460
+ },
461
+ "description": "ShellCheck over changed shell scripts. Needs the shellcheck binary on the runner; skips when absent."
176
462
  },
177
463
  "circularDeps": {
178
464
  "type": "object",
179
465
  "properties": {
180
- "enabled": { "type": "boolean" },
181
- "severity": { "enum": ["error", "warn"] },
182
- "ignore": { "type": "array", "items": { "type": "string" } },
183
- "timeoutMs": { "type": "number" }
184
- }
466
+ "enabled": {
467
+ "type": "boolean",
468
+ "default": true
469
+ },
470
+ "severity": {
471
+ "enum": ["error", "warn"],
472
+ "default": "warn"
473
+ },
474
+ "ignore": {
475
+ "type": "array",
476
+ "items": {
477
+ "type": "string"
478
+ }
479
+ },
480
+ "timeoutMs": {
481
+ "type": "number",
482
+ "default": 120000
483
+ }
484
+ },
485
+ "description": "Circular import detection across the whole module graph."
486
+ },
487
+ "typecheck": {
488
+ "$ref": "#/$defs/toggle"
185
489
  },
186
- "typecheck": { "$ref": "#/$defs/toggle" },
187
490
  "prBody": {
188
491
  "type": "object",
189
492
  "properties": {
190
- "enabled": { "type": "boolean" },
191
- "minLength": { "type": "number" },
192
- "requireTicket": { "type": "boolean" }
193
- }
493
+ "enabled": {
494
+ "type": "boolean"
495
+ },
496
+ "minLength": {
497
+ "type": "number",
498
+ "default": 20
499
+ },
500
+ "requireTicket": {
501
+ "type": "boolean",
502
+ "default": true
503
+ }
504
+ },
505
+ "description": "Validates the pull request description — minimum length, ticket reference."
506
+ },
507
+ "cpp": {
508
+ "$ref": "#/$defs/toggle"
194
509
  },
195
- "cpp": { "$ref": "#/$defs/toggle" },
196
510
  "python": {
197
511
  "type": "object",
198
512
  "properties": {
199
- "enabled": { "type": "boolean" },
200
- "typecheck": { "type": "boolean" },
201
- "typeChecker": { "enum": ["mypy", "pyright"] },
513
+ "enabled": {
514
+ "type": "boolean"
515
+ },
516
+ "typecheck": {
517
+ "type": "boolean"
518
+ },
519
+ "typeChecker": {
520
+ "enum": ["mypy", "pyright"]
521
+ },
202
522
  "ruff": {
203
523
  "type": "object",
204
524
  "properties": {
205
- "lint": { "type": "boolean" },
206
- "format": { "type": "boolean" }
525
+ "lint": {
526
+ "type": "boolean"
527
+ },
528
+ "format": {
529
+ "type": "boolean"
530
+ }
207
531
  }
208
532
  }
533
+ },
534
+ "description": "Python lint and format via Ruff."
535
+ },
536
+ "swift": {
537
+ "$ref": "#/$defs/toggle"
538
+ },
539
+ "kotlin": {
540
+ "$ref": "#/$defs/toggle"
541
+ },
542
+ "go": {
543
+ "$ref": "#/$defs/toggle"
544
+ },
545
+ "rust": {
546
+ "$ref": "#/$defs/toggle"
547
+ },
548
+ "csharp": {
549
+ "$ref": "#/$defs/toggle"
550
+ },
551
+ "ruby": {
552
+ "$ref": "#/$defs/toggle"
553
+ },
554
+ "php": {
555
+ "$ref": "#/$defs/toggle"
556
+ },
557
+ "severityDefaults": {
558
+ "description": "Repo-wide fallback for checks that set no failOn of their own. The single lever for staged adoption.",
559
+ "type": "object",
560
+ "properties": {
561
+ "failOn": {
562
+ "description": "What a finding rated \"error\" does to the run. Default \"error\" (blocks the merge).",
563
+ "enum": ["error", "warn", "never"]
564
+ }
209
565
  }
210
566
  },
211
- "swift": { "$ref": "#/$defs/toggle" },
212
- "kotlin": { "$ref": "#/$defs/toggle" },
213
- "go": { "$ref": "#/$defs/toggle" },
214
- "rust": { "$ref": "#/$defs/toggle" },
215
- "csharp": { "$ref": "#/$defs/toggle" },
216
- "ruby": { "$ref": "#/$defs/toggle" },
217
- "php": { "$ref": "#/$defs/toggle" }
567
+ "run": {
568
+ "description": "How the run itself behaves, as opposed to what any single check looks for.",
569
+ "type": "object",
570
+ "properties": {
571
+ "reportSkipped": {
572
+ "description": "Whether checks that need a diff range and did not get one appear in the report as skips.",
573
+ "enum": ["auto", "always", "never"]
574
+ },
575
+ "onDegradedScope": {
576
+ "description": "What to do when a CI run wanted a diff range, could not build one, and fell back to a full scan — which silently drops every diff-only check. Never triggers outside CI.",
577
+ "enum": ["warn", "error", "off"]
578
+ },
579
+ "concurrency": {
580
+ "description": "How many checks run at once: \"auto\" (default, leaves a core free, caps at 4), a number, or \"unlimited\".",
581
+ "oneOf": [
582
+ {
583
+ "type": "number"
584
+ },
585
+ {
586
+ "enum": ["auto", "unlimited"]
587
+ }
588
+ ]
589
+ },
590
+ "heavyConcurrency": {
591
+ "description": "Separate cap for whole-project analysers (knip, jscpd, dpdm, tsc, grype, cspell). Default 1.",
592
+ "type": "number"
593
+ },
594
+ "checkTimeoutMs": {
595
+ "description": "Per-check timeout in ms (default 300000). On expiry the check warns and the run continues; 0 disables.",
596
+ "type": "number"
597
+ },
598
+ "timings": {
599
+ "description": "Print how long each check took.",
600
+ "type": "boolean"
601
+ },
602
+ "fullScanBehaviour": {
603
+ "description": "What checks that can work either way do with no diff range (--full, or a local run on the default branch). \"scan\" (default) reviews every tracked file; \"skip\" reports a skip.",
604
+ "enum": ["scan", "skip"]
605
+ }
606
+ }
607
+ },
608
+ "reporter": {
609
+ "description": "Where the run summary goes and what happens if it cannot get there.",
610
+ "type": "object",
611
+ "properties": {
612
+ "onPostFailure": {
613
+ "description": "What a failed post means for the run. Default \"warn\".",
614
+ "enum": ["warn", "error", "off"]
615
+ },
616
+ "mode": {
617
+ "description": "\"update\" (default) edits the previous summary; \"append\" leaves one comment per run.",
618
+ "enum": ["update", "append"]
619
+ },
620
+ "timeoutMs": {
621
+ "description": "Per-request timeout in ms (default 10000).",
622
+ "type": "number"
623
+ },
624
+ "retries": {
625
+ "description": "Attempts per request for retryable failures — 5xx, 429, network (default 3).",
626
+ "type": "number"
627
+ },
628
+ "maxPages": {
629
+ "description": "Pages of existing comments searched for the summary marker (default 10, 100 per page).",
630
+ "type": "number"
631
+ },
632
+ "skipWhenClean": {
633
+ "description": "Post nothing when the run produced no warnings and no failures.",
634
+ "type": "boolean"
635
+ },
636
+ "include": {
637
+ "description": "Trim the comment body.",
638
+ "type": "object",
639
+ "properties": {
640
+ "passed": {
641
+ "type": "boolean"
642
+ },
643
+ "skipped": {
644
+ "type": "boolean"
645
+ },
646
+ "details": {
647
+ "type": "boolean"
648
+ },
649
+ "docsLink": {
650
+ "type": "boolean"
651
+ }
652
+ }
653
+ }
654
+ }
655
+ },
656
+ "commands": {
657
+ "description": "Quick CLI reference written by init. Informational only — ignored at runtime.",
658
+ "type": "object",
659
+ "additionalProperties": true
660
+ },
661
+ "documentation": {
662
+ "description": "Documentation links written by init. Informational only — ignored at runtime.",
663
+ "type": "object",
664
+ "additionalProperties": true
665
+ },
666
+ "languages": {
667
+ "description": "Languages detected at init time; drives which language-specific checks run.",
668
+ "type": "array",
669
+ "items": {
670
+ "type": "string"
671
+ }
672
+ },
673
+ "detect": {
674
+ "description": "How languages are inferred when \"languages\" is absent.",
675
+ "type": "object",
676
+ "properties": {
677
+ "minFiles": {
678
+ "description": "Source files a language needs before it counts (default 1). A marker file such as pyproject.toml includes the language regardless.",
679
+ "type": "integer",
680
+ "minimum": 1,
681
+ "default": 1
682
+ },
683
+ "excludePaths": {
684
+ "description": "Globs excluded from the language count, added to the built-in list (**/*UnitTest*/**, **/get-pip.py, **/vendor/**, **/third_party/**).",
685
+ "type": "array",
686
+ "items": {
687
+ "type": "string"
688
+ }
689
+ },
690
+ "disable": {
691
+ "description": "Languages never detected, whatever the tree looks like.",
692
+ "type": "array",
693
+ "items": {
694
+ "type": "string"
695
+ }
696
+ }
697
+ }
698
+ },
699
+ "install": {
700
+ "description": "Which tooling is fetched, when, and what happens when it cannot be.",
701
+ "type": "object",
702
+ "properties": {
703
+ "strategy": {
704
+ "description": "\"adaptive\" fetches what this repository needs; \"eager\" fetches everything up front (closed networks, image warm-up); \"manual\" fetches nothing.",
705
+ "type": "string",
706
+ "enum": ["adaptive", "eager", "manual"],
707
+ "default": "adaptive"
708
+ },
709
+ "profiles": {
710
+ "description": "Profiles installed on top of what detection concluded.",
711
+ "type": "array",
712
+ "items": {
713
+ "type": "string"
714
+ }
715
+ },
716
+ "excludeProfiles": {
717
+ "description": "Profiles never installed, even when the language is detected. Wins over \"profiles\".",
718
+ "type": "array",
719
+ "items": {
720
+ "type": "string"
721
+ }
722
+ },
723
+ "minFilesPerLanguage": {
724
+ "description": "Source files a language needs before its toolchain is worth downloading (default 1).",
725
+ "type": "integer",
726
+ "minimum": 1,
727
+ "default": 1
728
+ },
729
+ "reuseLocal": {
730
+ "description": "Reuse a tool already in the client's node_modules: \"compatible\" (default), \"always\", or \"off\".",
731
+ "type": "string",
732
+ "enum": ["compatible", "always", "off"],
733
+ "default": "compatible"
734
+ },
735
+ "onMissingTool": {
736
+ "description": "\"auto\" tries and skips with a reason on failure (default); \"skip\" never tries; \"error\" fails the run.",
737
+ "type": "string",
738
+ "enum": ["auto", "skip", "error"],
739
+ "default": "auto"
740
+ },
741
+ "offline": {
742
+ "description": "Forbid every network install at run time.",
743
+ "type": "boolean",
744
+ "default": false
745
+ },
746
+ "waitTimeoutMs": {
747
+ "description": "How long to wait for another process installing the same profile.",
748
+ "type": "integer",
749
+ "minimum": 0,
750
+ "default": 120000
751
+ },
752
+ "installTimeoutMs": {
753
+ "description": "Cap on a single install.",
754
+ "type": "integer",
755
+ "minimum": 0,
756
+ "default": 300000
757
+ },
758
+ "cacheDir": {
759
+ "description": "Where fetched tooling is kept. The path includes the pr-checkmate version.",
760
+ "type": "string",
761
+ "default": "~/.cache/pr-checkmate"
762
+ },
763
+ "prewarm": {
764
+ "description": "Warm the cache in postinstall. false leaves everything to the lazy path.",
765
+ "type": "boolean",
766
+ "default": true
767
+ },
768
+ "deny": {
769
+ "description": "Packages never fetched, whatever profile asks for them.",
770
+ "type": "array",
771
+ "items": {
772
+ "type": "string"
773
+ }
774
+ }
775
+ }
776
+ },
777
+ "lint": {
778
+ "description": "ESLint settings (interface name; init writes \"eslint\").",
779
+ "type": "object",
780
+ "properties": {
781
+ "rules": {
782
+ "description": "Rule overrides merged over the bundled defaults.",
783
+ "type": "object",
784
+ "additionalProperties": true
785
+ },
786
+ "ignorePatterns": {
787
+ "description": "Extra ignore globs.",
788
+ "type": "array",
789
+ "items": {
790
+ "type": "string"
791
+ }
792
+ }
793
+ }
794
+ },
795
+ "eslint": {
796
+ "description": "ESLint settings as written by init.",
797
+ "type": "object",
798
+ "properties": {
799
+ "rules": {
800
+ "description": "Rule overrides merged over the bundled defaults.",
801
+ "type": "object",
802
+ "additionalProperties": true
803
+ },
804
+ "ignorePatterns": {
805
+ "description": "Extra ignore globs.",
806
+ "type": "array",
807
+ "items": {
808
+ "type": "string"
809
+ }
810
+ }
811
+ }
812
+ },
813
+ "prettier": {
814
+ "description": "Prettier settings.",
815
+ "type": "object",
816
+ "properties": {
817
+ "config": {
818
+ "description": "Prettier options.",
819
+ "type": "object",
820
+ "additionalProperties": true
821
+ },
822
+ "rules": {
823
+ "description": "Prettier options as written by init.",
824
+ "type": "object",
825
+ "additionalProperties": true
826
+ }
827
+ }
828
+ },
829
+ "markdownlint": {
830
+ "description": "markdownlint settings.",
831
+ "type": "object",
832
+ "properties": {
833
+ "enabled": {
834
+ "description": "Run the check.",
835
+ "type": "boolean"
836
+ },
837
+ "rules": {
838
+ "description": "Rule overrides.",
839
+ "type": "object",
840
+ "additionalProperties": true
841
+ },
842
+ "ignores": {
843
+ "description": "Ignore globs.",
844
+ "type": "array",
845
+ "items": {
846
+ "type": "string"
847
+ }
848
+ }
849
+ }
850
+ },
851
+ "spellcheck": {
852
+ "description": "Spellcheck settings (interface name; init writes \"cspell\").",
853
+ "type": "object",
854
+ "properties": {
855
+ "enabled": {
856
+ "description": "Run the check.",
857
+ "type": "boolean"
858
+ },
859
+ "words": {
860
+ "description": "Project words to accept.",
861
+ "type": "array",
862
+ "items": {
863
+ "type": "string"
864
+ }
865
+ },
866
+ "ignoreWords": {
867
+ "description": "Words to ignore.",
868
+ "type": "array",
869
+ "items": {
870
+ "type": "string"
871
+ }
872
+ },
873
+ "ignorePaths": {
874
+ "description": "Paths excluded from spellchecking.",
875
+ "type": "array",
876
+ "items": {
877
+ "type": "string"
878
+ }
879
+ }
880
+ }
881
+ },
882
+ "dependency": {
883
+ "description": "Unused/missing dependency check.",
884
+ "type": "object",
885
+ "properties": {
886
+ "enabled": {
887
+ "description": "Run the check.",
888
+ "type": "boolean"
889
+ },
890
+ "ignore": {
891
+ "description": "Package names never reported.",
892
+ "type": "array",
893
+ "items": {
894
+ "type": "string"
895
+ }
896
+ }
897
+ }
898
+ },
899
+ "outdatedDeps": {
900
+ "description": "Outdated dependency check.",
901
+ "type": "object",
902
+ "properties": {
903
+ "enabled": {
904
+ "description": "Run the check.",
905
+ "type": "boolean"
906
+ },
907
+ "timeoutMs": {
908
+ "description": "Registry lookup timeout.",
909
+ "type": "number"
910
+ },
911
+ "ignore": {
912
+ "description": "Package names never reported.",
913
+ "type": "array",
914
+ "items": {
915
+ "type": "string"
916
+ }
917
+ }
918
+ }
919
+ },
920
+ "licenseCheck": {
921
+ "description": "License compliance check.",
922
+ "type": "object",
923
+ "properties": {
924
+ "allowPackages": {
925
+ "description": "Package names exempt from the forbidden-license check.",
926
+ "type": "array",
927
+ "items": {
928
+ "type": "string"
929
+ }
930
+ },
931
+ "allowLicenses": {
932
+ "description": "Extra SPDX ids treated as allowed.",
933
+ "type": "array",
934
+ "items": {
935
+ "type": "string"
936
+ }
937
+ }
938
+ }
939
+ },
940
+ "duplicate": {
941
+ "description": "Duplicate-code detection (jscpd).",
942
+ "type": "object",
943
+ "properties": {
944
+ "enabled": {
945
+ "description": "Run the check.",
946
+ "type": "boolean"
947
+ },
948
+ "minLines": {
949
+ "description": "Smallest clone reported, in lines.",
950
+ "type": "number"
951
+ },
952
+ "minTokens": {
953
+ "description": "Smallest clone reported, in tokens.",
954
+ "type": "number"
955
+ },
956
+ "ignore": {
957
+ "description": "Ignore globs.",
958
+ "type": "array",
959
+ "items": {
960
+ "type": "string"
961
+ }
962
+ },
963
+ "formats": {
964
+ "description": "Languages scanned.",
965
+ "type": "array",
966
+ "items": {
967
+ "type": "string"
968
+ }
969
+ }
970
+ }
971
+ },
972
+ "deadCode": {
973
+ "description": "Unused-exports report.",
974
+ "type": "object",
975
+ "properties": {
976
+ "enabled": {
977
+ "description": "Run the check.",
978
+ "type": "boolean"
979
+ },
980
+ "ignoreFiles": {
981
+ "description": "Regex sources (not globs) matched against reported file paths.",
982
+ "type": "array",
983
+ "items": {
984
+ "type": "string"
985
+ }
986
+ }
987
+ }
988
+ },
989
+ "commitlint": {
990
+ "description": "Conventional-commit validation of the PR title.",
991
+ "type": "object",
992
+ "properties": {
993
+ "enabled": {
994
+ "description": "Run the check.",
995
+ "type": "boolean"
996
+ },
997
+ "types": {
998
+ "description": "Allowed commit types.",
999
+ "type": "array",
1000
+ "items": {
1001
+ "type": "string"
1002
+ }
1003
+ },
1004
+ "ignores": {
1005
+ "description": "Regex sources for titles to skip, e.g. release PRs.",
1006
+ "type": "array",
1007
+ "items": {
1008
+ "type": "string"
1009
+ }
1010
+ }
1011
+ }
1012
+ },
1013
+ "log": {
1014
+ "description": "Diagnostic output — the running commentary around the run, as opposed to the summary it produces.",
1015
+ "type": "object",
1016
+ "properties": {
1017
+ "level": {
1018
+ "description": "Threshold. \"debug\" adds per-check detail.",
1019
+ "enum": ["error", "warn", "info", "log", "debug"]
1020
+ },
1021
+ "format": {
1022
+ "description": "\"pretty\" (default outside CI), \"ci\" (timestamp + level, default in CI), or \"json\" (JSON Lines).",
1023
+ "enum": ["pretty", "ci", "json"]
1024
+ },
1025
+ "color": {
1026
+ "description": "\"auto\" (default) honours TTY plus NO_COLOR / FORCE_COLOR.",
1027
+ "enum": ["auto", "always", "never"]
1028
+ },
1029
+ "timestamps": {
1030
+ "description": "Prefix each line with an ISO timestamp. Defaults to on only in \"ci\" format.",
1031
+ "type": "boolean"
1032
+ },
1033
+ "stderrLevels": {
1034
+ "description": "Levels routed to stderr. Default [\"error\", \"warn\"].",
1035
+ "type": "array",
1036
+ "items": {
1037
+ "enum": ["error", "warn", "info", "log", "debug"]
1038
+ }
1039
+ },
1040
+ "sampleLimit": {
1041
+ "description": "Sample lines printed per finding (default 3).",
1042
+ "type": "number"
1043
+ }
1044
+ }
1045
+ }
218
1046
  },
219
1047
  "$defs": {
220
1048
  "toggle": {
221
1049
  "type": "object",
222
- "properties": { "enabled": { "type": "boolean" } }
1050
+ "properties": {
1051
+ "enabled": {
1052
+ "type": "boolean"
1053
+ }
1054
+ }
1055
+ },
1056
+ "togglePaths": {
1057
+ "type": "object",
1058
+ "properties": {
1059
+ "enabled": {
1060
+ "type": "boolean"
1061
+ },
1062
+ "ignorePaths": {
1063
+ "description": "Globs excluded from scanning entirely.",
1064
+ "type": "array",
1065
+ "items": {
1066
+ "type": "string"
1067
+ }
1068
+ }
1069
+ }
223
1070
  },
224
1071
  "toggleSeverity": {
225
1072
  "type": "object",
226
1073
  "properties": {
227
- "enabled": { "type": "boolean" },
228
- "severity": { "enum": ["error", "warn"] }
1074
+ "enabled": {
1075
+ "type": "boolean",
1076
+ "default": true
1077
+ },
1078
+ "severity": {
1079
+ "enum": ["error", "warn"],
1080
+ "default": "warn"
1081
+ }
229
1082
  }
230
1083
  },
231
1084
  "toggleIgnore": {
232
1085
  "type": "object",
233
1086
  "properties": {
234
- "enabled": { "type": "boolean" },
235
- "ignore": { "type": "array", "items": { "type": "string" } }
1087
+ "enabled": {
1088
+ "type": "boolean"
1089
+ },
1090
+ "ignore": {
1091
+ "type": "array",
1092
+ "items": {
1093
+ "type": "string"
1094
+ }
1095
+ },
1096
+ "failOn": {
1097
+ "description": "What a finding rated \"error\" does to the run. Default \"error\" (blocks the merge).",
1098
+ "enum": ["error", "warn", "never"]
1099
+ },
1100
+ "severityOverrides": {
1101
+ "description": "Re-level individual findings by case-insensitive label substring; \"off\" drops one entirely.",
1102
+ "type": "object",
1103
+ "additionalProperties": {
1104
+ "enum": ["error", "warn", "off"]
1105
+ }
1106
+ }
1107
+ }
1108
+ },
1109
+ "patternCheck": {
1110
+ "type": "object",
1111
+ "properties": {
1112
+ "enabled": {
1113
+ "type": "boolean"
1114
+ },
1115
+ "ignore": {
1116
+ "type": "array",
1117
+ "items": {
1118
+ "type": "string"
1119
+ }
1120
+ },
1121
+ "failOn": {
1122
+ "description": "What a finding rated \"error\" does to the run. Default \"error\" (blocks the merge).",
1123
+ "enum": ["error", "warn", "never"]
1124
+ },
1125
+ "severityOverrides": {
1126
+ "description": "Re-level individual findings by case-insensitive label substring; \"off\" drops one entirely.",
1127
+ "type": "object",
1128
+ "additionalProperties": {
1129
+ "enum": ["error", "warn", "off"]
1130
+ }
1131
+ },
1132
+ "ignorePaths": {
1133
+ "description": "Globs excluded from scanning entirely.",
1134
+ "type": "array",
1135
+ "items": {
1136
+ "type": "string"
1137
+ }
1138
+ },
1139
+ "disablePatterns": {
1140
+ "description": "Built-in rules to switch off by label substring — the rule never runs.",
1141
+ "type": "array",
1142
+ "items": {
1143
+ "type": "string"
1144
+ }
1145
+ },
1146
+ "commentHandling": {
1147
+ "description": "\"language\" (default) strips comments per file extension; \"legacy\" strips # and // everywhere; \"off\" scans commented-out code too.",
1148
+ "enum": ["language", "legacy", "off"]
1149
+ },
1150
+ "extraPatterns": {
1151
+ "description": "Project rules evaluated alongside the built-ins, with the same comment handling and grouping.",
1152
+ "type": "array",
1153
+ "items": {
1154
+ "type": "object",
1155
+ "required": ["label", "pattern"],
1156
+ "properties": {
1157
+ "label": {
1158
+ "type": "string"
1159
+ },
1160
+ "pattern": {
1161
+ "description": "Regex source matched against an added diff line.",
1162
+ "type": "string"
1163
+ },
1164
+ "severity": {
1165
+ "enum": ["error", "warn"]
1166
+ },
1167
+ "view": {
1168
+ "description": "\"code\" matches with string literals blanked out — use it to find a call, not text inside a string.",
1169
+ "enum": ["raw", "code"]
1170
+ },
1171
+ "flags": {
1172
+ "description": "Regex flags; g and y are stripped.",
1173
+ "type": "string"
1174
+ }
1175
+ }
1176
+ }
1177
+ }
1178
+ }
1179
+ },
1180
+ "toggleIgnorePaths": {
1181
+ "type": "object",
1182
+ "properties": {
1183
+ "enabled": {
1184
+ "type": "boolean"
1185
+ },
1186
+ "ignore": {
1187
+ "type": "array",
1188
+ "items": {
1189
+ "type": "string"
1190
+ }
1191
+ },
1192
+ "failOn": {
1193
+ "description": "What a finding rated \"error\" does to the run. Default \"error\" (blocks the merge).",
1194
+ "enum": ["error", "warn", "never"]
1195
+ },
1196
+ "severityOverrides": {
1197
+ "description": "Re-level individual findings by case-insensitive label substring; \"off\" drops one entirely.",
1198
+ "type": "object",
1199
+ "additionalProperties": {
1200
+ "enum": ["error", "warn", "off"]
1201
+ }
1202
+ },
1203
+ "ignorePaths": {
1204
+ "description": "Globs excluded from scanning entirely.",
1205
+ "type": "array",
1206
+ "items": {
1207
+ "type": "string"
1208
+ }
1209
+ }
236
1210
  }
237
1211
  }
238
1212
  }