@kevinrabun/judges 3.4.0 → 3.5.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 (45) hide show
  1. package/README.md +189 -4
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +280 -12
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/baseline.d.ts +2 -0
  6. package/dist/commands/baseline.d.ts.map +1 -0
  7. package/dist/commands/baseline.js +126 -0
  8. package/dist/commands/baseline.js.map +1 -0
  9. package/dist/commands/completions.d.ts +2 -0
  10. package/dist/commands/completions.d.ts.map +1 -0
  11. package/dist/commands/completions.js +226 -0
  12. package/dist/commands/completions.js.map +1 -0
  13. package/dist/commands/deps.d.ts +6 -0
  14. package/dist/commands/deps.d.ts.map +1 -0
  15. package/dist/commands/deps.js +123 -0
  16. package/dist/commands/deps.js.map +1 -0
  17. package/dist/commands/diff.d.ts +7 -0
  18. package/dist/commands/diff.d.ts.map +1 -0
  19. package/dist/commands/diff.js +209 -0
  20. package/dist/commands/diff.js.map +1 -0
  21. package/dist/commands/docs.d.ts +2 -0
  22. package/dist/commands/docs.d.ts.map +1 -0
  23. package/dist/commands/docs.js +157 -0
  24. package/dist/commands/docs.js.map +1 -0
  25. package/dist/commands/watch.js.map +1 -1
  26. package/dist/formatters/badge.d.ts +17 -0
  27. package/dist/formatters/badge.d.ts.map +1 -0
  28. package/dist/formatters/badge.js +79 -0
  29. package/dist/formatters/badge.js.map +1 -0
  30. package/dist/formatters/codeclimate.d.ts +25 -0
  31. package/dist/formatters/codeclimate.d.ts.map +1 -0
  32. package/dist/formatters/codeclimate.js +81 -0
  33. package/dist/formatters/codeclimate.js.map +1 -0
  34. package/dist/formatters/junit.d.ts +7 -0
  35. package/dist/formatters/junit.d.ts.map +1 -0
  36. package/dist/formatters/junit.js +69 -0
  37. package/dist/formatters/junit.js.map +1 -0
  38. package/dist/index.js +17 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/presets.d.ts +22 -0
  41. package/dist/presets.d.ts.map +1 -0
  42. package/dist/presets.js +115 -0
  43. package/dist/presets.js.map +1 -0
  44. package/judgesrc.schema.json +74 -0
  45. package/package.json +14 -1
package/README.md CHANGED
@@ -11,7 +11,7 @@ An MCP (Model Context Protocol) server that provides a panel of **35 specialized
11
11
  [![npm](https://img.shields.io/npm/v/@kevinrabun/judges)](https://www.npmjs.com/package/@kevinrabun/judges)
12
12
  [![npm downloads](https://img.shields.io/npm/dw/@kevinrabun/judges)](https://www.npmjs.com/package/@kevinrabun/judges)
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
- [![Tests](https://img.shields.io/badge/tests-730-brightgreen)](https://github.com/KevinRabun/judges/actions)
14
+ [![Tests](https://img.shields.io/badge/tests-777-brightgreen)](https://github.com/KevinRabun/judges/actions)
15
15
 
16
16
  ---
17
17
 
@@ -63,6 +63,15 @@ judges eval --fail-on-findings src/api.ts
63
63
  # Suppress known findings via baseline
64
64
  judges eval --baseline baseline.json src/api.ts
65
65
 
66
+ # Use a named preset
67
+ judges eval --preset security-only src/api.ts
68
+
69
+ # Use a config file
70
+ judges eval --config .judgesrc.json src/api.ts
71
+
72
+ # Set a minimum score threshold (exit 1 if below)
73
+ judges eval --min-score 80 src/api.ts
74
+
66
75
  # One-line summary for scripts
67
76
  judges eval --summary src/api.ts
68
77
 
@@ -88,6 +97,32 @@ judges watch src/
88
97
  # Project-level report (local directory)
89
98
  judges report . --format html --output report.html
90
99
 
100
+ # Evaluate a unified diff (pipe from git diff)
101
+ git diff HEAD~1 | judges diff
102
+
103
+ # Analyze dependencies for supply-chain risks
104
+ judges deps --path . --format json
105
+
106
+ # Create a baseline file to suppress known findings
107
+ judges baseline create --file src/api.ts -o baseline.json
108
+
109
+ # Generate CI template files
110
+ judges ci-templates --provider github
111
+ judges ci-templates --provider gitlab
112
+ judges ci-templates --provider azure
113
+ judges ci-templates --provider bitbucket
114
+
115
+ # Generate per-judge rule documentation
116
+ judges docs
117
+ judges docs --judge cybersecurity
118
+ judges docs --output docs/
119
+
120
+ # Install shell completions
121
+ judges completions bash # eval "$(judges completions bash)"
122
+ judges completions zsh
123
+ judges completions fish
124
+ judges completions powershell
125
+
91
126
  # Install pre-commit hook
92
127
  judges hook install
93
128
 
@@ -371,11 +406,17 @@ Evaluate a file with all 35 judges or a single judge.
371
406
  | `--file <path>` / positional | File to evaluate |
372
407
  | `--judge <id>` / `-j <id>` | Single judge mode |
373
408
  | `--language <lang>` / `-l <lang>` | Language hint (auto-detected from extension) |
374
- | `--format <fmt>` / `-f <fmt>` | Output format: `text`, `json`, `sarif`, `markdown`, `html` |
409
+ | `--format <fmt>` / `-f <fmt>` | Output format: `text`, `json`, `sarif`, `markdown`, `html`, `junit`, `codeclimate` |
375
410
  | `--output <path>` / `-o <path>` | Write output to file |
376
411
  | `--fail-on-findings` | Exit with code 1 if verdict is FAIL |
377
412
  | `--baseline <path>` / `-b <path>` | JSON baseline file — suppress known findings |
378
413
  | `--summary` | Print a single summary line (ideal for scripts) |
414
+ | `--config <path>` | Load a `.judgesrc` / `.judgesrc.json` config file |
415
+ | `--preset <name>` | Use a named preset: `strict`, `lenient`, `security-only`, `startup`, `compliance`, `performance` |
416
+ | `--min-score <n>` | Exit with code 1 if overall score is below this threshold |
417
+ | `--verbose` | Print timing and debug information |
418
+ | `--quiet` | Suppress non-essential output |
419
+ | `--no-color` | Disable ANSI colors |
379
420
 
380
421
  ### `judges init`
381
422
 
@@ -428,6 +469,131 @@ judges hook uninstall # remove pre-commit hook
428
469
 
429
470
  Detects Husky (`.husky/pre-commit`) and falls back to `.git/hooks/pre-commit`. Uses marker-based injection so it won't clobber existing hooks.
430
471
 
472
+ ### `judges diff`
473
+
474
+ Evaluate only the changed lines from a unified diff (e.g., `git diff` output).
475
+
476
+ | Flag | Description |
477
+ |------|-------------|
478
+ | `--file <path>` | Read diff from file instead of stdin |
479
+ | `--format <fmt>` | Output format: `text`, `json`, `sarif`, `junit`, `codeclimate` |
480
+ | `--output <path>` | Write output to file |
481
+
482
+ ```bash
483
+ git diff HEAD~1 | judges diff
484
+ judges diff --file changes.patch --format sarif
485
+ ```
486
+
487
+ ### `judges deps`
488
+
489
+ Analyze project dependencies for supply-chain risks.
490
+
491
+ | Flag | Description |
492
+ |------|-------------|
493
+ | `--path <dir>` | Project root to scan (default: `.`) |
494
+ | `--format <fmt>` | Output format: `text`, `json` |
495
+
496
+ ```bash
497
+ judges deps --path .
498
+ judges deps --path ./backend --format json
499
+ ```
500
+
501
+ ### `judges baseline`
502
+
503
+ Create a baseline file to suppress known findings in future evaluations.
504
+
505
+ ```bash
506
+ judges baseline create --file src/api.ts
507
+ judges baseline create --file src/api.ts -o .judges-baseline.json
508
+ ```
509
+
510
+ ### `judges ci-templates`
511
+
512
+ Generate CI/CD configuration templates for popular providers.
513
+
514
+ ```bash
515
+ judges ci-templates --provider github # .github/workflows/judges.yml
516
+ judges ci-templates --provider gitlab # .gitlab-ci.judges.yml
517
+ judges ci-templates --provider azure # azure-pipelines.judges.yml
518
+ judges ci-templates --provider bitbucket # bitbucket-pipelines.yml (snippet)
519
+ ```
520
+
521
+ ### `judges docs`
522
+
523
+ Generate per-judge rule documentation in Markdown.
524
+
525
+ | Flag | Description |
526
+ |------|-------------|
527
+ | `--judge <id>` | Generate docs for a single judge |
528
+ | `--output <dir>` | Write individual `.md` files per judge |
529
+
530
+ ```bash
531
+ judges docs # all judges to stdout
532
+ judges docs --judge cybersecurity # single judge
533
+ judges docs --output docs/judges/ # write files to directory
534
+ ```
535
+
536
+ ### `judges completions`
537
+
538
+ Generate shell completion scripts.
539
+
540
+ ```bash
541
+ eval "$(judges completions bash)" # Bash
542
+ eval "$(judges completions zsh)" # Zsh
543
+ judges completions fish | source # Fish
544
+ judges completions powershell # PowerShell (Register-ArgumentCompleter)
545
+ ```
546
+
547
+ ### Named Presets
548
+
549
+ Use `--preset` to apply pre-configured evaluation settings:
550
+
551
+ | Preset | Description |
552
+ |--------|-------------|
553
+ | `strict` | All severities, all judges — maximum thoroughness |
554
+ | `lenient` | Only high and critical findings — fast and focused |
555
+ | `security-only` | Security judges only — cybersecurity, data-security, authentication, logging-privacy |
556
+ | `startup` | Skip compliance, sovereignty, i18n judges — move fast |
557
+ | `compliance` | Only compliance, data-sovereignty, authentication — regulatory focus |
558
+ | `performance` | Only performance, scalability, caching, cost-effectiveness |
559
+
560
+ ```bash
561
+ judges eval --preset security-only src/api.ts
562
+ judges eval --preset strict --format sarif src/app.ts > results.sarif
563
+ ```
564
+
565
+ ### CI Output Formats
566
+
567
+ #### JUnit XML
568
+
569
+ Generate JUnit XML for Jenkins, Azure DevOps, GitHub Actions, or GitLab test result viewers:
570
+
571
+ ```bash
572
+ judges eval --format junit src/api.ts > results.xml
573
+ ```
574
+
575
+ Each judge maps to a `<testsuite>`, each finding becomes a `<testcase>` with `<failure>` for critical/high severity.
576
+
577
+ #### CodeClimate / GitLab Code Quality
578
+
579
+ Generate CodeClimate JSON for GitLab Code Quality or similar tools:
580
+
581
+ ```bash
582
+ judges eval --format codeclimate src/api.ts > codequality.json
583
+ ```
584
+
585
+ #### Score Badges
586
+
587
+ Generate SVG or text badges for your README:
588
+
589
+ ```typescript
590
+ import { generateBadgeSvg, generateBadgeText } from "@kevinrabun/judges/badge";
591
+
592
+ const svg = generateBadgeSvg(85); // shields.io-style SVG
593
+ const text = generateBadgeText(85); // "✓ judges 85/100"
594
+ const svg2 = generateBadgeSvg(75, "quality"); // custom label
595
+ ```
596
+
431
597
  ---
432
598
 
433
599
  ## The Judge Panel
@@ -926,14 +1092,23 @@ judges/
926
1092
  │ │ └── *.ts # One analyzer per judge (35 files)
927
1093
  │ ├── formatters/ # Output formatters
928
1094
  │ │ ├── sarif.ts # SARIF 2.1.0 output
929
- │ │ └── html.ts # Self-contained HTML report (dark/light theme, filters)
1095
+ │ │ ├── html.ts # Self-contained HTML report (dark/light theme, filters)
1096
+ │ │ ├── junit.ts # JUnit XML output (Jenkins, Azure DevOps, GitHub Actions)
1097
+ │ │ ├── codeclimate.ts # CodeClimate/GitLab Code Quality JSON
1098
+ │ │ └── badge.ts # SVG and text badge generator
930
1099
  │ ├── commands/ # CLI subcommands
931
1100
  │ │ ├── init.ts # Interactive project setup wizard
932
1101
  │ │ ├── fix.ts # Auto-fix patch preview and application
933
1102
  │ │ ├── watch.ts # Watch mode — re-evaluate on save
934
1103
  │ │ ├── report.ts # Project-level local report
935
1104
  │ │ ├── hook.ts # Pre-commit hook install/uninstall
936
- │ │ └── ci-templates.ts # GitLab, Azure, Bitbucket CI templates
1105
+ │ │ ├── ci-templates.ts # GitLab, Azure, Bitbucket CI templates
1106
+ │ │ ├── diff.ts # Evaluate unified diff (git diff)
1107
+ │ │ ├── deps.ts # Dependency supply-chain analysis
1108
+ │ │ ├── baseline.ts # Create baseline for finding suppression
1109
+ │ │ ├── completions.ts # Shell completions (bash/zsh/fish/PowerShell)
1110
+ │ │ └── docs.ts # Per-judge rule documentation generator
1111
+ │ ├── presets.ts # Named evaluation presets (strict, lenient, security-only, …)
937
1112
  │ ├── reports/
938
1113
  │ │ └── public-repo-report.ts # Public repo clone + full tribunal report generation
939
1114
  │ └── judges/ # Judge definitions (id, name, domain, system prompt)
@@ -955,6 +1130,7 @@ judges/
955
1130
  │ ├── tree-sitter-rust.wasm
956
1131
  │ ├── tree-sitter-java.wasm
957
1132
  │ └── tree-sitter-c_sharp.wasm
1133
+ ├── judgesrc.schema.json # JSON Schema for .judgesrc config files
958
1134
  ├── server.json # MCP Registry manifest
959
1135
  ├── package.json
960
1136
  ├── tsconfig.json
@@ -981,6 +1157,12 @@ judges/
981
1157
  | `judges watch <dir>` | Watch mode — re-evaluate on file save |
982
1158
  | `judges report <dir>` | Full tribunal report on a local directory |
983
1159
  | `judges hook install` | Install a Git pre-commit hook |
1160
+ | `judges diff` | Evaluate changed lines from unified diff |
1161
+ | `judges deps` | Analyze dependencies for supply-chain risks |
1162
+ | `judges baseline create` | Create baseline for finding suppression |
1163
+ | `judges ci-templates` | Generate CI pipeline templates |
1164
+ | `judges docs` | Generate per-judge rule documentation |
1165
+ | `judges completions <shell>` | Shell completion scripts |
984
1166
 
985
1167
  ---
986
1168
 
@@ -1051,6 +1233,9 @@ const sarif = findingsToSarif(verdict.evaluations.flatMap(e => e.findings));
1051
1233
  | `@kevinrabun/judges/api` | Programmatic API (default) |
1052
1234
  | `@kevinrabun/judges/server` | MCP server entry point |
1053
1235
  | `@kevinrabun/judges/sarif` | SARIF 2.1.0 formatter |
1236
+ | `@kevinrabun/judges/junit` | JUnit XML formatter |
1237
+ | `@kevinrabun/judges/codeclimate` | CodeClimate/GitLab Code Quality JSON |
1238
+ | `@kevinrabun/judges/badge` | SVG and text badge generator |
1054
1239
 
1055
1240
  ### SARIF Output
1056
1241
 
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAyVH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2I1D"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAibH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoN1D"}
package/dist/cli.js CHANGED
@@ -24,6 +24,19 @@ import { resolve, extname } from "path";
24
24
  import { evaluateWithTribunal, evaluateWithJudge, formatVerdictAsMarkdown, formatEvaluationAsMarkdown, } from "./evaluators/index.js";
25
25
  import { getJudge, getJudgeSummaries } from "./judges/index.js";
26
26
  import { verdictToSarif } from "./formatters/sarif.js";
27
+ import { verdictToHtml } from "./formatters/html.js";
28
+ import { verdictToJUnit } from "./formatters/junit.js";
29
+ import { verdictToCodeClimate } from "./formatters/codeclimate.js";
30
+ import { runReport } from "./commands/report.js";
31
+ import { runHook } from "./commands/hook.js";
32
+ import { runDiff } from "./commands/diff.js";
33
+ import { runDeps } from "./commands/deps.js";
34
+ import { runBaseline } from "./commands/baseline.js";
35
+ import { runCompletions } from "./commands/completions.js";
36
+ import { runDocs } from "./commands/docs.js";
37
+ import { generateGitLabCi, generateAzurePipelines, generateBitbucketPipelines } from "./commands/ci-templates.js";
38
+ import { getPreset, listPresets } from "./presets.js";
39
+ import { parseConfig } from "./config.js";
27
40
  // ─── Language Detection from Extension ──────────────────────────────────────
28
41
  const EXT_TO_LANG = {
29
42
  ".ts": "typescript",
@@ -75,6 +88,12 @@ function parseCliArgs(argv) {
75
88
  failOnFindings: false,
76
89
  baseline: undefined,
77
90
  summary: false,
91
+ config: undefined,
92
+ preset: undefined,
93
+ minScore: undefined,
94
+ noColor: false,
95
+ verbose: false,
96
+ quiet: false,
78
97
  };
79
98
  // First non-flag arg is the command
80
99
  let i = 2; // skip node + script
@@ -115,6 +134,26 @@ function parseCliArgs(argv) {
115
134
  case "--summary":
116
135
  args.summary = true;
117
136
  break;
137
+ case "--config":
138
+ case "-c":
139
+ args.config = argv[++i];
140
+ break;
141
+ case "--preset":
142
+ case "-p":
143
+ args.preset = argv[++i];
144
+ break;
145
+ case "--min-score":
146
+ args.minScore = parseInt(argv[++i], 10);
147
+ break;
148
+ case "--no-color":
149
+ args.noColor = true;
150
+ break;
151
+ case "--verbose":
152
+ args.verbose = true;
153
+ break;
154
+ case "--quiet":
155
+ args.quiet = true;
156
+ break;
118
157
  default:
119
158
  // If it looks like a file path (not a flag), treat as --file
120
159
  if (!arg.startsWith("-") && !args.file) {
@@ -138,17 +177,29 @@ USAGE:
138
177
  judges watch <path> Watch files and re-evaluate on save
139
178
  judges report <dir> Generate project-level report
140
179
  judges hook install Install pre-commit git hook
180
+ judges diff Evaluate only changed lines from a diff
181
+ judges deps [dir] Analyze dependencies for supply-chain risks
182
+ judges baseline create <file> Create a findings baseline
183
+ judges ci-templates <provider> Generate CI pipeline template
184
+ judges completions <shell> Generate shell completions
185
+ judges docs Generate rule documentation
141
186
  judges list List all available judges
142
187
  judges --help Show this help
143
188
 
144
189
  EVAL OPTIONS:
145
190
  --file, -f <path> File to evaluate (or pass as positional arg)
146
191
  --language, -l <lang> Language override (auto-detected from extension)
147
- --format, -o <fmt> Output: text (default), json, sarif, markdown, html
192
+ --format, -o <fmt> Output: text, json, sarif, markdown, html, junit, codeclimate
148
193
  --judge, -j <id> Run a single judge instead of the full tribunal
149
194
  --fail-on-findings Exit with code 1 when verdict is fail
150
195
  --baseline, -b <path> Suppress findings already in baseline file
151
196
  --summary Show one-line summary instead of full output
197
+ --config, -c <path> Path to .judgesrc config file
198
+ --preset, -p <name> Use a named preset (strict, lenient, security-only, startup, compliance, performance)
199
+ --min-score <n> Fail if score drops below threshold (0-100)
200
+ --no-color Disable colored output
201
+ --verbose Show detailed evaluation information
202
+ --quiet Suppress non-essential output
152
203
  --help, -h Show this help
153
204
 
154
205
  FIX OPTIONS:
@@ -159,20 +210,50 @@ WATCH OPTIONS:
159
210
  --judge, -j <id> Only evaluate with a specific judge
160
211
  --fail-on-findings Exit on first failure
161
212
 
213
+ DIFF OPTIONS:
214
+ --file, -f <path> Read diff from file (or pipe via stdin)
215
+ --language, -l <lang> Language override for all files in diff
216
+
217
+ DEPS OPTIONS:
218
+ --file, -f <path> Specific manifest to analyze
219
+ --format, -o <fmt> Output: text, json
220
+
221
+ CI-TEMPLATES:
222
+ judges ci-templates github GitHub Actions workflow
223
+ judges ci-templates gitlab GitLab CI pipeline
224
+ judges ci-templates azure Azure Pipelines
225
+ judges ci-templates bitbucket Bitbucket Pipelines
226
+
227
+ COMPLETIONS:
228
+ judges completions bash Bash completions
229
+ judges completions zsh Zsh completions
230
+ judges completions fish Fish completions
231
+ judges completions powershell PowerShell completions
232
+
162
233
  STDIN:
163
234
  cat file.ts | judges eval --language typescript
235
+ git diff | judges diff --language typescript
164
236
 
165
237
  EXAMPLES:
166
238
  judges eval src/app.ts
167
239
  judges eval --file api.py --format sarif
168
240
  judges eval --judge cybersecurity server.ts
169
- judges eval --format html --fail-on-findings src/
241
+ judges eval --format junit --fail-on-findings src/
170
242
  judges eval --baseline .judges-baseline.json src/app.ts
243
+ judges eval --preset security-only src/app.ts
244
+ judges eval --config .judgesrc src/app.ts
245
+ judges eval --min-score 80 src/app.ts
171
246
  judges init
172
247
  judges fix src/app.ts --apply
173
248
  judges watch src/
174
249
  judges report .
175
250
  judges hook install
251
+ judges diff --file changes.patch
252
+ judges deps .
253
+ judges baseline create --file src/app.ts
254
+ judges ci-templates github
255
+ judges docs --output docs/rules/
256
+ judges completions bash >> ~/.bashrc
176
257
  judges list
177
258
 
178
259
  SUPPORTED LANGUAGES:
@@ -340,16 +421,44 @@ export async function runCli(argv) {
340
421
  }
341
422
  // ─── Report Command ───────────────────────────────────────────────────
342
423
  if (args.command === "report") {
343
- const { runReport } = await import("./commands/report.js");
344
424
  runReport(argv);
345
425
  return;
346
426
  }
347
427
  // ─── Hook Command ────────────────────────────────────────────────────
348
428
  if (args.command === "hook") {
349
- const { runHook } = await import("./commands/hook.js");
350
429
  runHook(argv);
351
430
  return;
352
431
  }
432
+ // ─── Diff Command ────────────────────────────────────────────────────
433
+ if (args.command === "diff") {
434
+ runDiff(argv);
435
+ return;
436
+ }
437
+ // ─── Deps Command ────────────────────────────────────────────────────
438
+ if (args.command === "deps") {
439
+ runDeps(argv);
440
+ return;
441
+ }
442
+ // ─── Baseline Command ────────────────────────────────────────────────
443
+ if (args.command === "baseline") {
444
+ runBaseline(argv);
445
+ return;
446
+ }
447
+ // ─── CI Templates Command ────────────────────────────────────────────
448
+ if (args.command === "ci-templates") {
449
+ runCiTemplates(argv);
450
+ return;
451
+ }
452
+ // ─── Completions Command ─────────────────────────────────────────────
453
+ if (args.command === "completions") {
454
+ runCompletions(argv);
455
+ return;
456
+ }
457
+ // ─── Docs Command ────────────────────────────────────────────────────
458
+ if (args.command === "docs") {
459
+ runDocs(argv);
460
+ return;
461
+ }
353
462
  // ─── List Command ────────────────────────────────────────────────────
354
463
  if (args.command === "list") {
355
464
  listJudges();
@@ -357,13 +466,18 @@ export async function runCli(argv) {
357
466
  }
358
467
  // ─── Eval Command ────────────────────────────────────────────────────
359
468
  if (args.command === "eval" || args.file) {
469
+ const startTime = Date.now();
360
470
  const { code, resolvedPath } = readCode(args.file);
361
471
  const language = args.language || detectLanguage(args.file || resolvedPath) || "typescript";
362
- // Load baseline if specified
472
+ // Load config from file or preset
473
+ const evalConfig = loadEvalConfig(args);
474
+ // Load baseline if specified (from CLI flag — config doesn't carry baseline)
363
475
  let baselineFindings;
364
476
  if (args.baseline) {
365
477
  baselineFindings = loadBaseline(args.baseline);
366
478
  }
479
+ // Build evaluation options from config
480
+ const evalOptions = evalConfig ? { config: evalConfig } : undefined;
367
481
  if (args.judge) {
368
482
  // Single judge mode
369
483
  const judge = getJudge(args.judge);
@@ -377,6 +491,11 @@ export async function runCli(argv) {
377
491
  if (baselineFindings) {
378
492
  evaluation.findings = evaluation.findings.filter((f) => !baselineFindings.has(baselineKey(f)));
379
493
  }
494
+ // Apply min-severity filter from config
495
+ if (evalConfig?.minSeverity) {
496
+ evaluation.findings = filterBySeverity(evaluation.findings, evalConfig.minSeverity);
497
+ }
498
+ const elapsed = Date.now() - startTime;
380
499
  if (args.summary) {
381
500
  printSummaryLine(evaluation.verdict, evaluation.score, evaluation.findings.length);
382
501
  }
@@ -387,7 +506,6 @@ export async function runCli(argv) {
387
506
  console.log(formatEvaluationAsMarkdown(evaluation));
388
507
  }
389
508
  else if (args.format === "html") {
390
- const { verdictToHtml } = await import("./formatters/html.js");
391
509
  // Wrap single evaluation as a tribunal-like verdict for HTML
392
510
  const wrappedVerdict = {
393
511
  overallVerdict: evaluation.verdict,
@@ -404,14 +522,20 @@ export async function runCli(argv) {
404
522
  else {
405
523
  console.log(formatSingleJudgeTextOutput(evaluation));
406
524
  }
407
- // Exit code
408
- if (args.failOnFindings && evaluation.verdict === "fail") {
525
+ if (args.verbose) {
526
+ console.log(` ⏱ Evaluated in ${elapsed}ms`);
527
+ }
528
+ // Exit code — fail-on-findings or min-score
529
+ if (args.failOnFindings && evaluation.verdict === "fail")
530
+ process.exit(1);
531
+ if (args.minScore !== undefined && evaluation.score < args.minScore) {
532
+ console.error(`Score ${evaluation.score} is below minimum threshold ${args.minScore}`);
409
533
  process.exit(1);
410
534
  }
411
535
  }
412
536
  else {
413
537
  // Full tribunal mode
414
- const verdict = evaluateWithTribunal(code, language);
538
+ const verdict = evaluateWithTribunal(code, language, undefined, evalOptions);
415
539
  // Apply baseline suppression
416
540
  if (baselineFindings) {
417
541
  for (const evaluation of verdict.evaluations) {
@@ -419,19 +543,39 @@ export async function runCli(argv) {
419
543
  }
420
544
  verdict.findings = verdict.findings.filter((f) => !baselineFindings.has(baselineKey(f)));
421
545
  }
546
+ // Apply min-severity filter from config
547
+ if (evalConfig?.minSeverity) {
548
+ for (const evaluation of verdict.evaluations) {
549
+ evaluation.findings = filterBySeverity(evaluation.findings, evalConfig.minSeverity);
550
+ }
551
+ verdict.findings = filterBySeverity(verdict.findings, evalConfig.minSeverity);
552
+ }
553
+ const elapsed = Date.now() - startTime;
422
554
  if (args.summary) {
423
555
  const totalFindings = verdict.evaluations.reduce((s, e) => s + e.findings.length, 0);
424
556
  printSummaryLine(verdict.overallVerdict, verdict.overallScore, totalFindings);
425
557
  }
426
558
  else if (args.format === "html") {
427
- const { verdictToHtml } = await import("./formatters/html.js");
428
559
  console.log(verdictToHtml(verdict, resolvedPath || args.file));
429
560
  }
561
+ else if (args.format === "junit") {
562
+ console.log(verdictToJUnit(verdict, resolvedPath || args.file));
563
+ }
564
+ else if (args.format === "codeclimate") {
565
+ console.log(JSON.stringify(verdictToCodeClimate(verdict, resolvedPath || args.file), null, 2));
566
+ }
430
567
  else {
431
568
  console.log(formatTribunalOutput(verdict, args.format, resolvedPath || args.file));
432
569
  }
433
- // Exit code
434
- if (args.failOnFindings && verdict.overallVerdict === "fail") {
570
+ if (args.verbose) {
571
+ console.log(` ⏱ Evaluated in ${elapsed}ms`);
572
+ console.log(` 📊 ${verdict.evaluations.length} judges, ${verdict.findings.length} total findings`);
573
+ }
574
+ // Exit code — fail-on-findings or min-score
575
+ if (args.failOnFindings && verdict.overallVerdict === "fail")
576
+ process.exit(1);
577
+ if (args.minScore !== undefined && verdict.overallScore < args.minScore) {
578
+ console.error(`Score ${verdict.overallScore} is below minimum threshold ${args.minScore}`);
435
579
  process.exit(1);
436
580
  }
437
581
  }
@@ -472,4 +616,128 @@ function printSummaryLine(verdict, score, findings) {
472
616
  const icon = verdict === "pass" ? "✅" : verdict === "warning" ? "⚠️" : "❌";
473
617
  console.log(`${icon} ${verdict.toUpperCase()} ${score}/100 (${findings} findings)`);
474
618
  }
619
+ // ─── Config / Preset Loader ────────────────────────────────────────────────
620
+ function loadEvalConfig(args) {
621
+ let config;
622
+ // 1. Load from preset
623
+ if (args.preset) {
624
+ const preset = getPreset(args.preset);
625
+ if (!preset) {
626
+ console.error(`Unknown preset: ${args.preset}`);
627
+ console.error(`Available: ${listPresets()
628
+ .map((p) => p.name)
629
+ .join(", ")}`);
630
+ process.exit(1);
631
+ }
632
+ config = { ...preset.config };
633
+ }
634
+ // 2. Load from --config file (overrides preset)
635
+ if (args.config) {
636
+ const configPath = resolve(args.config);
637
+ if (!existsSync(configPath)) {
638
+ console.error(`Config file not found: ${configPath}`);
639
+ process.exit(1);
640
+ }
641
+ const fileConfig = parseConfig(readFileSync(configPath, "utf-8"));
642
+ config = config ? { ...config, ...fileConfig } : fileConfig;
643
+ }
644
+ // 3. Auto-discover .judgesrc or .judgesrc.json if no explicit config
645
+ if (!config && !args.config) {
646
+ for (const name of [".judgesrc", ".judgesrc.json"]) {
647
+ const p = resolve(name);
648
+ if (existsSync(p)) {
649
+ try {
650
+ config = parseConfig(readFileSync(p, "utf-8"));
651
+ }
652
+ catch {
653
+ // Silently skip invalid auto-discovered configs
654
+ }
655
+ break;
656
+ }
657
+ }
658
+ }
659
+ return config;
660
+ }
661
+ // ─── Severity Filter ────────────────────────────────────────────────────────
662
+ const SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"];
663
+ function filterBySeverity(findings, minSeverity) {
664
+ const minIndex = SEVERITY_ORDER.indexOf(minSeverity);
665
+ if (minIndex < 0)
666
+ return findings;
667
+ return findings.filter((f) => {
668
+ const idx = SEVERITY_ORDER.indexOf(f.severity);
669
+ return idx >= 0 && idx <= minIndex;
670
+ });
671
+ }
672
+ // ─── CI Templates CLI ──────────────────────────────────────────────────────
673
+ function runCiTemplates(argv) {
674
+ const provider = argv[3];
675
+ if (!provider || provider === "--help" || provider === "-h") {
676
+ console.log(`
677
+ Judges Panel — CI Template Generator
678
+
679
+ USAGE:
680
+ judges ci-templates github GitHub Actions workflow
681
+ judges ci-templates gitlab GitLab CI pipeline
682
+ judges ci-templates azure Azure Pipelines
683
+ judges ci-templates bitbucket Bitbucket Pipelines
684
+ `);
685
+ process.exit(0);
686
+ }
687
+ switch (provider) {
688
+ case "github":
689
+ console.log(generateGitHubActions());
690
+ break;
691
+ case "gitlab":
692
+ console.log(generateGitLabCi());
693
+ break;
694
+ case "azure":
695
+ console.log(generateAzurePipelines());
696
+ break;
697
+ case "bitbucket":
698
+ console.log(generateBitbucketPipelines());
699
+ break;
700
+ default:
701
+ console.error(`Unknown provider: ${provider}`);
702
+ console.error("Supported: github, gitlab, azure, bitbucket");
703
+ process.exit(1);
704
+ }
705
+ process.exit(0);
706
+ }
707
+ function generateGitHubActions() {
708
+ return `# .github/workflows/judges.yml
709
+ name: Judges Panel Code Review
710
+
711
+ on:
712
+ pull_request:
713
+ branches: [main]
714
+ push:
715
+ branches: [main]
716
+
717
+ jobs:
718
+ judges:
719
+ runs-on: ubuntu-latest
720
+ steps:
721
+ - uses: actions/checkout@v4
722
+
723
+ - uses: actions/setup-node@v4
724
+ with:
725
+ node-version: '22'
726
+
727
+ - name: Install Judges
728
+ run: npm install -g @kevinrabun/judges
729
+
730
+ - name: Run Judges Evaluation
731
+ run: |
732
+ for file in $(git diff --name-only HEAD~1 -- '*.ts' '*.js' '*.py' '*.go' '*.rs' '*.java' '*.cs'); do
733
+ judges eval --file "$file" --format sarif --fail-on-findings >> results.sarif || true
734
+ done
735
+
736
+ - name: Upload SARIF
737
+ if: always()
738
+ uses: github/codeql-action/upload-sarif@v3
739
+ with:
740
+ sarif_file: results.sarif
741
+ `;
742
+ }
475
743
  //# sourceMappingURL=cli.js.map