review-council 1.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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/config/defaults.d.ts +17 -0
  4. package/dist/config/defaults.js +27 -0
  5. package/dist/config/defaults.js.map +1 -0
  6. package/dist/config/loader.d.ts +2 -0
  7. package/dist/config/loader.js +66 -0
  8. package/dist/config/loader.js.map +1 -0
  9. package/dist/config/schema.d.ts +79 -0
  10. package/dist/config/schema.js +49 -0
  11. package/dist/config/schema.js.map +1 -0
  12. package/dist/consensus/deduper.d.ts +6 -0
  13. package/dist/consensus/deduper.js +127 -0
  14. package/dist/consensus/deduper.js.map +1 -0
  15. package/dist/consensus/parser.d.ts +6 -0
  16. package/dist/consensus/parser.js +90 -0
  17. package/dist/consensus/parser.js.map +1 -0
  18. package/dist/consensus/types.d.ts +57 -0
  19. package/dist/consensus/types.js +2 -0
  20. package/dist/consensus/types.js.map +1 -0
  21. package/dist/consensus/voter.d.ts +3 -0
  22. package/dist/consensus/voter.js +198 -0
  23. package/dist/consensus/voter.js.map +1 -0
  24. package/dist/dispatch/adapter.d.ts +15 -0
  25. package/dist/dispatch/adapter.js +24 -0
  26. package/dist/dispatch/adapter.js.map +1 -0
  27. package/dist/dispatch/anthropic.d.ts +9 -0
  28. package/dist/dispatch/anthropic.js +132 -0
  29. package/dist/dispatch/anthropic.js.map +1 -0
  30. package/dist/dispatch/google.d.ts +9 -0
  31. package/dist/dispatch/google.js +87 -0
  32. package/dist/dispatch/google.js.map +1 -0
  33. package/dist/dispatch/openai-compat.d.ts +18 -0
  34. package/dist/dispatch/openai-compat.js +90 -0
  35. package/dist/dispatch/openai-compat.js.map +1 -0
  36. package/dist/dispatch/openai.d.ts +9 -0
  37. package/dist/dispatch/openai.js +90 -0
  38. package/dist/dispatch/openai.js.map +1 -0
  39. package/dist/dispatch/runner.d.ts +11 -0
  40. package/dist/dispatch/runner.js +67 -0
  41. package/dist/dispatch/runner.js.map +1 -0
  42. package/dist/dispatch/utils.d.ts +6 -0
  43. package/dist/dispatch/utils.js +15 -0
  44. package/dist/dispatch/utils.js.map +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +262 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/output/github.d.ts +3 -0
  49. package/dist/output/github.js +95 -0
  50. package/dist/output/github.js.map +1 -0
  51. package/dist/output/json.d.ts +3 -0
  52. package/dist/output/json.js +8 -0
  53. package/dist/output/json.js.map +1 -0
  54. package/dist/output/markdown.d.ts +3 -0
  55. package/dist/output/markdown.js +81 -0
  56. package/dist/output/markdown.js.map +1 -0
  57. package/dist/output/terminal.d.ts +2 -0
  58. package/dist/output/terminal.js +141 -0
  59. package/dist/output/terminal.js.map +1 -0
  60. package/dist/prepare/chunker.d.ts +9 -0
  61. package/dist/prepare/chunker.js +62 -0
  62. package/dist/prepare/chunker.js.map +1 -0
  63. package/dist/prepare/language.d.ts +1 -0
  64. package/dist/prepare/language.js +99 -0
  65. package/dist/prepare/language.js.map +1 -0
  66. package/dist/prepare/prompt-builder.d.ts +11 -0
  67. package/dist/prepare/prompt-builder.js +65 -0
  68. package/dist/prepare/prompt-builder.js.map +1 -0
  69. package/dist/prompts/base.d.ts +3 -0
  70. package/dist/prompts/base.js +53 -0
  71. package/dist/prompts/base.js.map +1 -0
  72. package/dist/prompts/hardening.d.ts +7 -0
  73. package/dist/prompts/hardening.js +30 -0
  74. package/dist/prompts/hardening.js.map +1 -0
  75. package/dist/prompts/languages/elixir.d.ts +1 -0
  76. package/dist/prompts/languages/elixir.js +13 -0
  77. package/dist/prompts/languages/elixir.js.map +1 -0
  78. package/dist/prompts/languages/generic.d.ts +1 -0
  79. package/dist/prompts/languages/generic.js +11 -0
  80. package/dist/prompts/languages/generic.js.map +1 -0
  81. package/dist/prompts/languages/python.d.ts +1 -0
  82. package/dist/prompts/languages/python.js +14 -0
  83. package/dist/prompts/languages/python.js.map +1 -0
  84. package/dist/prompts/languages/typescript.d.ts +1 -0
  85. package/dist/prompts/languages/typescript.js +13 -0
  86. package/dist/prompts/languages/typescript.js.map +1 -0
  87. package/dist/resolver/github.d.ts +8 -0
  88. package/dist/resolver/github.js +70 -0
  89. package/dist/resolver/github.js.map +1 -0
  90. package/dist/resolver/local.d.ts +3 -0
  91. package/dist/resolver/local.js +75 -0
  92. package/dist/resolver/local.js.map +1 -0
  93. package/dist/resolver/types.d.ts +28 -0
  94. package/dist/resolver/types.js +2 -0
  95. package/dist/resolver/types.js.map +1 -0
  96. package/dist/roles/builtin.d.ts +4 -0
  97. package/dist/roles/builtin.js +234 -0
  98. package/dist/roles/builtin.js.map +1 -0
  99. package/dist/roles/dispatcher.d.ts +23 -0
  100. package/dist/roles/dispatcher.js +87 -0
  101. package/dist/roles/dispatcher.js.map +1 -0
  102. package/dist/roles/loader.d.ts +11 -0
  103. package/dist/roles/loader.js +107 -0
  104. package/dist/roles/loader.js.map +1 -0
  105. package/dist/roles/types.d.ts +13 -0
  106. package/dist/roles/types.js +2 -0
  107. package/dist/roles/types.js.map +1 -0
  108. package/package.json +67 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michael Ströck
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # review-council
2
+
3
+ > Multi-model AI code review in your terminal — many models, many roles, one consensus.
4
+
5
+ ![npm](https://img.shields.io/npm/v/review-council) ![license](https://img.shields.io/npm/l/review-council) ![node](https://img.shields.io/node/v/review-council)
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g review-council
13
+ ```
14
+
15
+ Requires Node.js >= 18.
16
+
17
+ ---
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Review a GitHub PR with default models and roles
23
+ rcl review owner/repo#42
24
+
25
+ # Review with specific roles and post findings as a PR comment
26
+ rcl review owner/repo#42 --roles security-auditor,bug-hunter --post
27
+
28
+ # Review a local patch file; fail CI if critical/important findings exist
29
+ rcl review changes.patch --ci --markdown report.md
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Built-in Roles
35
+
36
+ | Role | Description |
37
+ |------|-------------|
38
+ | 🔍 `general` | Comprehensive review covering all dimensions |
39
+ | 🔒 `security-auditor` | Auth, injection, XSS, CSRF, IDOR, and sensitive data exposure |
40
+ | ⚡ `performance-engineer` | N+1 queries, caching, algorithmic complexity, and memory efficiency |
41
+ | 📐 `api-design` | API contracts, breaking changes, REST/gRPC conventions |
42
+ | 🧪 `test-coverage` | Missing tests, edge cases, flawed test logic |
43
+ | ✏️ `dx-critic` | Readability, naming, documentation, and developer ergonomics |
44
+ | 🏗️ `architecture` | Module boundaries, coupling, and architectural patterns |
45
+ | 🐛 `bug-hunter` | Logic errors, null paths, race conditions, off-by-one |
46
+ | ♿ `accessibility-auditor` | WCAG compliance, ARIA roles, keyboard navigation |
47
+ | 📋 `project-rules` | Enforces repo conventions from `AGENTS.md`, `CLAUDE.md`, etc. |
48
+ | 📄 `spec-compliance` | Checks implementation against a spec or plan file |
49
+
50
+ List roles in the terminal:
51
+
52
+ ```bash
53
+ rcl roles list
54
+ rcl roles show security-auditor
55
+ ```
56
+
57
+ ---
58
+
59
+ ## CLI Reference
60
+
61
+ ### `rcl review <target>`
62
+
63
+ Review a PR or local diff.
64
+
65
+ **Target formats:**
66
+ - `owner/repo#N` — GitHub PR number
67
+ - GitHub PR URL
68
+ - Path to a `.patch` or `.diff` file
69
+
70
+ **Options:**
71
+
72
+ | Flag | Description |
73
+ |------|-------------|
74
+ | `--role <name>` | Use a single named role |
75
+ | `--roles <names>` | Comma-separated list of roles |
76
+ | `--reviewer <model:role>` | Explicit model:role pair (repeatable) |
77
+ | `--models <models>` | Comma-separated list of models to use |
78
+ | `--context <path>` | Context file or directory (repeatable) |
79
+ | `--spec <path>` | Specification file for `spec-compliance` role |
80
+ | `--focus <areas>` | Comma-separated focus areas |
81
+ | `--post` | Post review as a GitHub PR comment |
82
+ | `--json` | Print JSON output to stdout |
83
+ | `--json-file <path>` | Write JSON output to a file |
84
+ | `--markdown <path>` | Write Markdown report to a file |
85
+ | `--ci` | Exit non-zero if critical/important findings exist |
86
+ | `--config <path>` | Path to a config file |
87
+
88
+ `--role`, `--roles`, and `--reviewer` are mutually exclusive.
89
+
90
+ **Examples:**
91
+
92
+ ```bash
93
+ # Use explicit model:role pairs
94
+ rcl review owner/repo#7 \
95
+ --reviewer claude-opus-4-6:security-auditor \
96
+ --reviewer gpt-5.4:bug-hunter
97
+
98
+ # Spec compliance review with context
99
+ rcl review ./feature.patch --role spec-compliance --spec SPEC.md --context src/
100
+
101
+ # Output JSON for downstream processing
102
+ rcl review owner/repo#99 --json > findings.json
103
+ ```
104
+
105
+ ---
106
+
107
+ ### `rcl roles`
108
+
109
+ ```bash
110
+ rcl roles list # List all built-in roles
111
+ rcl roles show <name> # Show system prompt and details for a role
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Config File
117
+
118
+ Place `.review-council.yml` in your project root (or any parent directory). All fields are optional.
119
+
120
+ ```yaml
121
+ # Models to use (provider-prefixed names)
122
+ models:
123
+ - anthropic/claude-opus-4-6
124
+ - openai/gpt-5.4
125
+ - google/gemini-2.5-pro
126
+
127
+ # Default roles to run
128
+ roles:
129
+ - security-auditor
130
+ - bug-hunter
131
+ - test-coverage
132
+
133
+ # Or pin explicit model:role pairs
134
+ reviewers:
135
+ - model: anthropic/claude-opus-4-6
136
+ role: security-auditor
137
+ - model: openai/gpt-5.4
138
+ role: bug-hunter
139
+
140
+ # Custom role overrides (extends a built-in or creates new)
141
+ customRoles:
142
+ - name: my-style-guide
143
+ focus: [best-practices]
144
+ systemPrompt: |
145
+ Enforce our team style guide. Flag any deviation from snake_case
146
+ variable names and require docstrings on all public functions.
147
+
148
+ # Consensus and deduplication thresholds
149
+ thresholds:
150
+ minConsensusScore: 0.4 # 0–1; findings below this are dropped
151
+ minConfidence: 0.2
152
+ dedupeLineWindow: 5 # lines within which findings are merged
153
+ jaccardThreshold: 0.3 # token-overlap threshold for deduplication
154
+
155
+ # Output defaults
156
+ output:
157
+ markdown: true
158
+ markdownPath: review-report.md
159
+
160
+ # Concurrency and reliability
161
+ concurrency: 6
162
+ timeout: 120000 # ms per model call
163
+ maxRetries: 3
164
+
165
+ # Context files to attach to every review
166
+ context:
167
+ - ARCHITECTURE.md
168
+ - docs/api.md
169
+
170
+ # Spec file for spec-compliance role
171
+ spec: SPEC.md
172
+
173
+ # GitHub token (prefer GITHUB_TOKEN env var instead)
174
+ # githubToken: ghp_...
175
+ ```
176
+
177
+ Supported config file names: `.review-council.yml`, `.review-council.yaml`, `.review-council.json`, `review-council.config.js`.
178
+
179
+ ---
180
+
181
+ ## How Consensus Works
182
+
183
+ When multiple models and roles review the same diff, their findings are:
184
+
185
+ 1. **Deduplicated** — findings on the same file, overlapping line range, and similar category are grouped together using Jaccard token overlap.
186
+ 2. **Scored** — each group receives a consensus score based on three dimensions: reviewer diversity (how many distinct models and roles flagged it), role relevance (whether the reporting role specialises in that finding type), and isolation (what fraction of relevant reviewers flagged it).
187
+ 3. **Classified** — groups are assigned a confidence band (Very High → Minimal) and a final severity, with upward elevation when multiple high-relevance roles agree.
188
+ 4. **Filtered** — groups below `minConsensusScore` or `minConfidence` are dropped.
189
+
190
+ The result is a ranked list of findings that rewards agreement across independent reviewers and penalises noise from a single model.
191
+
192
+ For the full algorithm, see [CONSENSUS_V2_SPEC.md](./CONSENSUS_V2_SPEC.md).
193
+
194
+ ---
195
+
196
+ ## Environment Variables
197
+
198
+ | Variable | Description |
199
+ |----------|-------------|
200
+ | `ANTHROPIC_API_KEY` | API key for Claude models |
201
+ | `OPENAI_API_KEY` | API key for OpenAI models |
202
+ | `GEMINI_API_KEY` | API key for Google Gemini models |
203
+ | `GITHUB_TOKEN` | GitHub personal access token (PR fetch and post) |
204
+ | `RCL_DEBUG` | Set to any value to print full error stack traces |
205
+
206
+ ---
207
+
208
+ ## License
209
+
210
+ MIT © 2026 Michael Ströck
@@ -0,0 +1,17 @@
1
+ export declare const DEFAULT_MODELS: readonly ["anthropic/claude-opus-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro"];
2
+ export declare const DEFAULT_THRESHOLDS: {
3
+ readonly minConsensusScore: 0.4;
4
+ readonly minConfidence: 0.2;
5
+ readonly dedupeLineWindow: 5;
6
+ readonly jaccardThreshold: 0.3;
7
+ };
8
+ export declare const DEFAULT_TIMEOUT_MS = 120000;
9
+ export declare const DEFAULT_MAX_RETRIES = 3;
10
+ export declare const DEFAULT_CONCURRENCY = 6;
11
+ export declare const DEFAULT_SEVERITY_ORDER: readonly ["critical", "important", "minor", "nitpick"];
12
+ export declare const CONFIDENCE_THRESHOLDS: {
13
+ readonly veryHigh: 0.8;
14
+ readonly high: 0.6;
15
+ readonly medium: 0.4;
16
+ readonly low: 0.2;
17
+ };
@@ -0,0 +1,27 @@
1
+ export const DEFAULT_MODELS = [
2
+ 'anthropic/claude-opus-4-6',
3
+ 'openai/gpt-5.4',
4
+ 'google/gemini-2.5-pro',
5
+ ];
6
+ export const DEFAULT_THRESHOLDS = {
7
+ minConsensusScore: 0.4,
8
+ minConfidence: 0.2,
9
+ dedupeLineWindow: 5,
10
+ jaccardThreshold: 0.3,
11
+ };
12
+ export const DEFAULT_TIMEOUT_MS = 120_000;
13
+ export const DEFAULT_MAX_RETRIES = 3;
14
+ export const DEFAULT_CONCURRENCY = 6;
15
+ export const DEFAULT_SEVERITY_ORDER = [
16
+ 'critical',
17
+ 'important',
18
+ 'minor',
19
+ 'nitpick',
20
+ ];
21
+ export const CONFIDENCE_THRESHOLDS = {
22
+ veryHigh: 0.8,
23
+ high: 0.6,
24
+ medium: 0.4,
25
+ low: 0.2,
26
+ };
27
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,2BAA2B;IAC3B,gBAAgB;IAChB,uBAAuB;CACf,CAAC;AAEX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,iBAAiB,EAAE,GAAG;IACtB,aAAa,EAAE,GAAG;IAClB,gBAAgB,EAAE,CAAC;IACnB,gBAAgB,EAAE,GAAG;CACb,CAAC;AAEX,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,UAAU;IACV,WAAW;IACX,OAAO;IACP,SAAS;CACD,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,GAAG;IACb,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,GAAG;IACX,GAAG,EAAE,GAAG;CACA,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type Config } from './schema.js';
2
+ export declare function loadConfig(configPath?: string): Promise<Config>;
@@ -0,0 +1,66 @@
1
+ import { cosmiconfig } from 'cosmiconfig';
2
+ import { ConfigSchema } from './schema.js';
3
+ import { DEFAULT_MODELS, DEFAULT_THRESHOLDS, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_CONCURRENCY, } from './defaults.js';
4
+ const explorer = cosmiconfig('review-council', {
5
+ searchPlaces: [
6
+ '.review-council.yml',
7
+ '.review-council.yaml',
8
+ '.review-council.json',
9
+ '.review-council.js',
10
+ '.review-council.cjs',
11
+ 'review-council.config.js',
12
+ 'review-council.config.cjs',
13
+ ],
14
+ });
15
+ export async function loadConfig(configPath) {
16
+ try {
17
+ const result = configPath
18
+ ? await explorer.load(configPath)
19
+ : await explorer.search();
20
+ if (!result || result.isEmpty) {
21
+ return buildDefaultConfig();
22
+ }
23
+ const parsed = ConfigSchema.safeParse(result.config);
24
+ if (!parsed.success) {
25
+ console.warn('Warning: config validation errors:', parsed.error.format());
26
+ return buildDefaultConfig();
27
+ }
28
+ return mergeWithDefaults(parsed.data);
29
+ }
30
+ catch (err) {
31
+ if (configPath) {
32
+ throw new Error(`Failed to load config from ${configPath}: ${String(err)}`);
33
+ }
34
+ return buildDefaultConfig();
35
+ }
36
+ }
37
+ function buildDefaultConfig() {
38
+ return {
39
+ models: [...DEFAULT_MODELS],
40
+ thresholds: { ...DEFAULT_THRESHOLDS },
41
+ timeout: DEFAULT_TIMEOUT_MS,
42
+ maxRetries: DEFAULT_MAX_RETRIES,
43
+ concurrency: DEFAULT_CONCURRENCY,
44
+ };
45
+ }
46
+ function mergeWithDefaults(config) {
47
+ return {
48
+ models: config.models ?? [...DEFAULT_MODELS],
49
+ roles: config.roles,
50
+ reviewers: config.reviewers,
51
+ customRoles: config.customRoles,
52
+ thresholds: {
53
+ ...DEFAULT_THRESHOLDS,
54
+ ...config.thresholds,
55
+ },
56
+ output: config.output,
57
+ timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
58
+ maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
59
+ concurrency: config.concurrency ?? DEFAULT_CONCURRENCY,
60
+ githubToken: config.githubToken ?? process.env['GITHUB_TOKEN'],
61
+ context: config.context,
62
+ spec: config.spec,
63
+ focus: config.focus,
64
+ };
65
+ }
66
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,MAAM,QAAQ,GAAG,WAAW,CAAC,gBAAgB,EAAE;IAC7C,YAAY,EAAE;QACZ,qBAAqB;QACrB,sBAAsB;QACtB,sBAAsB;QACtB,oBAAoB;QACpB,qBAAqB;QACrB,0BAA0B;QAC1B,2BAA2B;KAC5B;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAmB;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU;YACvB,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;YACjC,CAAC,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;QAE5B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,OAAO,kBAAkB,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,oCAAoC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1E,OAAO,kBAAkB,EAAE,CAAC;QAC9B,CAAC;QAED,OAAO,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,UAAU,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO,kBAAkB,EAAE,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO;QACL,MAAM,EAAE,CAAC,GAAG,cAAc,CAAC;QAC3B,UAAU,EAAE,EAAE,GAAG,kBAAkB,EAAE;QACrC,OAAO,EAAE,kBAAkB;QAC3B,UAAU,EAAE,mBAAmB;QAC/B,WAAW,EAAE,mBAAmB;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAc;IACvC,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,cAAc,CAAC;QAC5C,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE;YACV,GAAG,kBAAkB;YACrB,GAAG,MAAM,CAAC,UAAU;SACrB;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,kBAAkB;QAC7C,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,mBAAmB;QACpD,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,mBAAmB;QACtD,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAC9D,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { z } from 'zod';
2
+ export declare const SeveritySchema: z.ZodEnum<{
3
+ critical: "critical";
4
+ important: "important";
5
+ minor: "minor";
6
+ nitpick: "nitpick";
7
+ }>;
8
+ export declare const CategorySchema: z.ZodEnum<{
9
+ security: "security";
10
+ correctness: "correctness";
11
+ "best-practices": "best-practices";
12
+ tests: "tests";
13
+ "api-design": "api-design";
14
+ }>;
15
+ export declare const ReviewerPairSchema: z.ZodObject<{
16
+ model: z.ZodString;
17
+ role: z.ZodString;
18
+ }, z.core.$strip>;
19
+ export declare const RoleConfigSchema: z.ZodObject<{
20
+ name: z.ZodString;
21
+ systemPrompt: z.ZodOptional<z.ZodString>;
22
+ focus: z.ZodOptional<z.ZodArray<z.ZodString>>;
23
+ severityBias: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
24
+ }, z.core.$strip>;
25
+ export declare const ThresholdsSchema: z.ZodObject<{
26
+ minConsensusScore: z.ZodOptional<z.ZodNumber>;
27
+ minConfidence: z.ZodOptional<z.ZodNumber>;
28
+ dedupeLineWindow: z.ZodOptional<z.ZodNumber>;
29
+ jaccardThreshold: z.ZodOptional<z.ZodNumber>;
30
+ }, z.core.$strip>;
31
+ export declare const OutputSchema: z.ZodObject<{
32
+ terminal: z.ZodOptional<z.ZodBoolean>;
33
+ json: z.ZodOptional<z.ZodBoolean>;
34
+ markdown: z.ZodOptional<z.ZodBoolean>;
35
+ github: z.ZodOptional<z.ZodBoolean>;
36
+ jsonPath: z.ZodOptional<z.ZodString>;
37
+ markdownPath: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strip>;
39
+ export declare const ConfigSchema: z.ZodObject<{
40
+ models: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
+ roles: z.ZodOptional<z.ZodArray<z.ZodString>>;
42
+ reviewers: z.ZodOptional<z.ZodArray<z.ZodObject<{
43
+ model: z.ZodString;
44
+ role: z.ZodString;
45
+ }, z.core.$strip>>>;
46
+ customRoles: z.ZodOptional<z.ZodArray<z.ZodObject<{
47
+ name: z.ZodString;
48
+ systemPrompt: z.ZodOptional<z.ZodString>;
49
+ focus: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
+ severityBias: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
51
+ }, z.core.$strip>>>;
52
+ thresholds: z.ZodOptional<z.ZodObject<{
53
+ minConsensusScore: z.ZodOptional<z.ZodNumber>;
54
+ minConfidence: z.ZodOptional<z.ZodNumber>;
55
+ dedupeLineWindow: z.ZodOptional<z.ZodNumber>;
56
+ jaccardThreshold: z.ZodOptional<z.ZodNumber>;
57
+ }, z.core.$strip>>;
58
+ output: z.ZodOptional<z.ZodObject<{
59
+ terminal: z.ZodOptional<z.ZodBoolean>;
60
+ json: z.ZodOptional<z.ZodBoolean>;
61
+ markdown: z.ZodOptional<z.ZodBoolean>;
62
+ github: z.ZodOptional<z.ZodBoolean>;
63
+ jsonPath: z.ZodOptional<z.ZodString>;
64
+ markdownPath: z.ZodOptional<z.ZodString>;
65
+ }, z.core.$strip>>;
66
+ timeout: z.ZodOptional<z.ZodNumber>;
67
+ maxRetries: z.ZodOptional<z.ZodNumber>;
68
+ concurrency: z.ZodOptional<z.ZodNumber>;
69
+ githubToken: z.ZodOptional<z.ZodString>;
70
+ context: z.ZodOptional<z.ZodArray<z.ZodString>>;
71
+ spec: z.ZodOptional<z.ZodString>;
72
+ focus: z.ZodOptional<z.ZodArray<z.ZodString>>;
73
+ }, z.core.$strip>;
74
+ export type Config = z.infer<typeof ConfigSchema>;
75
+ export type ReviewerPair = z.infer<typeof ReviewerPairSchema>;
76
+ export type RoleConfig = z.infer<typeof RoleConfigSchema>;
77
+ export type Thresholds = z.infer<typeof ThresholdsSchema>;
78
+ export type Severity = z.infer<typeof SeveritySchema>;
79
+ export type Category = z.infer<typeof CategorySchema>;
@@ -0,0 +1,49 @@
1
+ import { z } from 'zod';
2
+ export const SeveritySchema = z.enum(['critical', 'important', 'minor', 'nitpick']);
3
+ export const CategorySchema = z.enum([
4
+ 'security',
5
+ 'correctness',
6
+ 'best-practices',
7
+ 'tests',
8
+ 'api-design',
9
+ ]);
10
+ export const ReviewerPairSchema = z.object({
11
+ model: z.string(),
12
+ role: z.string(),
13
+ });
14
+ export const RoleConfigSchema = z.object({
15
+ name: z.string(),
16
+ systemPrompt: z.string().optional(),
17
+ focus: z.array(z.string()).optional(),
18
+ severityBias: z.record(z.string(), z.number()).optional(),
19
+ });
20
+ export const ThresholdsSchema = z.object({
21
+ minConsensusScore: z.number().min(0).max(1).optional(),
22
+ minConfidence: z.number().min(0).max(1).optional(),
23
+ dedupeLineWindow: z.number().int().positive().optional(),
24
+ jaccardThreshold: z.number().min(0).max(1).optional(),
25
+ });
26
+ export const OutputSchema = z.object({
27
+ terminal: z.boolean().optional(),
28
+ json: z.boolean().optional(),
29
+ markdown: z.boolean().optional(),
30
+ github: z.boolean().optional(),
31
+ jsonPath: z.string().optional(),
32
+ markdownPath: z.string().optional(),
33
+ });
34
+ export const ConfigSchema = z.object({
35
+ models: z.array(z.string()).optional(),
36
+ roles: z.array(z.string()).optional(),
37
+ reviewers: z.array(ReviewerPairSchema).optional(),
38
+ customRoles: z.array(RoleConfigSchema).optional(),
39
+ thresholds: ThresholdsSchema.optional(),
40
+ output: OutputSchema.optional(),
41
+ timeout: z.number().positive().optional(),
42
+ maxRetries: z.number().int().nonnegative().optional(),
43
+ concurrency: z.number().int().positive().optional(),
44
+ githubToken: z.string().optional(),
45
+ context: z.array(z.string()).optional(),
46
+ spec: z.string().optional(),
47
+ focus: z.array(z.string()).optional(),
48
+ });
49
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ModelReview, DeduplicatedGroup } from './types.js';
2
+ /**
3
+ * Compute Jaccard similarity between two strings (word-level tokenization)
4
+ */
5
+ export declare function jaccardSimilarity(a: string, b: string): number;
6
+ export declare function deduplicateFindings(reviews: ModelReview[], jaccardThreshold?: number, lineWindow?: number): DeduplicatedGroup[];
@@ -0,0 +1,127 @@
1
+ const LINE_WINDOW = 5;
2
+ /**
3
+ * Compute Jaccard similarity between two strings (word-level tokenization)
4
+ */
5
+ export function jaccardSimilarity(a, b) {
6
+ const tokenize = (s) => {
7
+ return new Set(s
8
+ .toLowerCase()
9
+ .replace(/[^a-z0-9\s]/g, ' ')
10
+ .split(/\s+/)
11
+ .filter((t) => t.length > 2));
12
+ };
13
+ const setA = tokenize(a);
14
+ const setB = tokenize(b);
15
+ if (setA.size === 0 && setB.size === 0)
16
+ return 1.0;
17
+ if (setA.size === 0 || setB.size === 0)
18
+ return 0.0;
19
+ let intersection = 0;
20
+ for (const token of setA) {
21
+ if (setB.has(token))
22
+ intersection++;
23
+ }
24
+ const union = setA.size + setB.size - intersection;
25
+ return intersection / union;
26
+ }
27
+ function linesOverlap(a, b, window) {
28
+ const aStart = Math.max(0, a.startLine - window);
29
+ const aEnd = a.endLine + window;
30
+ const bStart = Math.max(0, b.startLine - window);
31
+ const bEnd = b.endLine + window;
32
+ return aStart <= bEnd && bStart <= aEnd;
33
+ }
34
+ function areSameFile(a, b) {
35
+ return a.file === b.file;
36
+ }
37
+ function sameCategory(a, b) {
38
+ return a.category === b.category;
39
+ }
40
+ function areDuplicates(a, b, jaccardThreshold, lineWindow) {
41
+ if (!areSameFile(a, b))
42
+ return false;
43
+ if (!sameCategory(a, b))
44
+ return false;
45
+ if (!linesOverlap(a, b, lineWindow))
46
+ return false;
47
+ const titleSim = jaccardSimilarity(a.title, b.title);
48
+ const descSim = jaccardSimilarity(a.description, b.description);
49
+ const maxSim = Math.max(titleSim, descSim);
50
+ return maxSim >= jaccardThreshold;
51
+ }
52
+ /**
53
+ * Choose the representative finding from a group:
54
+ * - Prefer higher severity
55
+ * - Break ties by description length (more detail wins)
56
+ */
57
+ function chooseRepresentative(members) {
58
+ const severityOrder = { critical: 0, important: 1, minor: 2, nitpick: 3 };
59
+ return members.reduce((best, curr) => {
60
+ const bestScore = severityOrder[best.finding.severity];
61
+ const currScore = severityOrder[curr.finding.severity];
62
+ if (currScore < bestScore)
63
+ return curr;
64
+ if (currScore === bestScore && curr.finding.description.length > best.finding.description.length)
65
+ return curr;
66
+ return best;
67
+ }).finding;
68
+ }
69
+ export function deduplicateFindings(reviews, jaccardThreshold = 0.3, lineWindow = LINE_WINDOW) {
70
+ // Flatten all findings with attribution
71
+ const all = [];
72
+ for (const review of reviews) {
73
+ if (review.status !== 'success')
74
+ continue;
75
+ for (const finding of review.findings) {
76
+ all.push({ finding, model: review.model, role: review.role });
77
+ }
78
+ }
79
+ if (all.length === 0)
80
+ return [];
81
+ // Union-Find style grouping
82
+ const groupOf = new Array(all.length).fill(-1);
83
+ for (let i = 0; i < all.length; i++) {
84
+ for (let j = i + 1; j < all.length; j++) {
85
+ if (areDuplicates(all[i].finding, all[j].finding, jaccardThreshold, lineWindow)) {
86
+ // Merge groups: find root of i
87
+ let rootI = i;
88
+ while (groupOf[rootI] !== -1)
89
+ rootI = groupOf[rootI];
90
+ let rootJ = j;
91
+ while (groupOf[rootJ] !== -1)
92
+ rootJ = groupOf[rootJ];
93
+ if (rootI !== rootJ) {
94
+ groupOf[rootJ] = rootI;
95
+ }
96
+ }
97
+ }
98
+ }
99
+ // Collect groups
100
+ const groups = new Map();
101
+ for (let i = 0; i < all.length; i++) {
102
+ let root = i;
103
+ while (groupOf[root] !== -1)
104
+ root = groupOf[root];
105
+ const existing = groups.get(root) ?? [];
106
+ existing.push(all[i]);
107
+ groups.set(root, existing);
108
+ }
109
+ const result = [];
110
+ for (const members of groups.values()) {
111
+ result.push({
112
+ representative: chooseRepresentative(members),
113
+ members,
114
+ });
115
+ }
116
+ // Sort by severity then file
117
+ const severityOrder = { critical: 0, important: 1, minor: 2, nitpick: 3 };
118
+ result.sort((a, b) => {
119
+ const sevDiff = severityOrder[a.representative.severity] -
120
+ severityOrder[b.representative.severity];
121
+ if (sevDiff !== 0)
122
+ return sevDiff;
123
+ return a.representative.file.localeCompare(b.representative.file);
124
+ });
125
+ return result;
126
+ }
127
+ //# sourceMappingURL=deduper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deduper.js","sourceRoot":"","sources":["../../src/consensus/deduper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Finding } from './types.js';
2
+ export interface ParseResult {
3
+ findings: Finding[];
4
+ warnings: string[];
5
+ }
6
+ export declare function parseReviewOutput(rawOutput: string, model: string, role: string): ParseResult;