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.
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/config/defaults.d.ts +17 -0
- package/dist/config/defaults.js +27 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/loader.js +66 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +79 -0
- package/dist/config/schema.js +49 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/consensus/deduper.d.ts +6 -0
- package/dist/consensus/deduper.js +127 -0
- package/dist/consensus/deduper.js.map +1 -0
- package/dist/consensus/parser.d.ts +6 -0
- package/dist/consensus/parser.js +90 -0
- package/dist/consensus/parser.js.map +1 -0
- package/dist/consensus/types.d.ts +57 -0
- package/dist/consensus/types.js +2 -0
- package/dist/consensus/types.js.map +1 -0
- package/dist/consensus/voter.d.ts +3 -0
- package/dist/consensus/voter.js +198 -0
- package/dist/consensus/voter.js.map +1 -0
- package/dist/dispatch/adapter.d.ts +15 -0
- package/dist/dispatch/adapter.js +24 -0
- package/dist/dispatch/adapter.js.map +1 -0
- package/dist/dispatch/anthropic.d.ts +9 -0
- package/dist/dispatch/anthropic.js +132 -0
- package/dist/dispatch/anthropic.js.map +1 -0
- package/dist/dispatch/google.d.ts +9 -0
- package/dist/dispatch/google.js +87 -0
- package/dist/dispatch/google.js.map +1 -0
- package/dist/dispatch/openai-compat.d.ts +18 -0
- package/dist/dispatch/openai-compat.js +90 -0
- package/dist/dispatch/openai-compat.js.map +1 -0
- package/dist/dispatch/openai.d.ts +9 -0
- package/dist/dispatch/openai.js +90 -0
- package/dist/dispatch/openai.js.map +1 -0
- package/dist/dispatch/runner.d.ts +11 -0
- package/dist/dispatch/runner.js +67 -0
- package/dist/dispatch/runner.js.map +1 -0
- package/dist/dispatch/utils.d.ts +6 -0
- package/dist/dispatch/utils.js +15 -0
- package/dist/dispatch/utils.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +262 -0
- package/dist/index.js.map +1 -0
- package/dist/output/github.d.ts +3 -0
- package/dist/output/github.js +95 -0
- package/dist/output/github.js.map +1 -0
- package/dist/output/json.d.ts +3 -0
- package/dist/output/json.js +8 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/markdown.d.ts +3 -0
- package/dist/output/markdown.js +81 -0
- package/dist/output/markdown.js.map +1 -0
- package/dist/output/terminal.d.ts +2 -0
- package/dist/output/terminal.js +141 -0
- package/dist/output/terminal.js.map +1 -0
- package/dist/prepare/chunker.d.ts +9 -0
- package/dist/prepare/chunker.js +62 -0
- package/dist/prepare/chunker.js.map +1 -0
- package/dist/prepare/language.d.ts +1 -0
- package/dist/prepare/language.js +99 -0
- package/dist/prepare/language.js.map +1 -0
- package/dist/prepare/prompt-builder.d.ts +11 -0
- package/dist/prepare/prompt-builder.js +65 -0
- package/dist/prepare/prompt-builder.js.map +1 -0
- package/dist/prompts/base.d.ts +3 -0
- package/dist/prompts/base.js +53 -0
- package/dist/prompts/base.js.map +1 -0
- package/dist/prompts/hardening.d.ts +7 -0
- package/dist/prompts/hardening.js +30 -0
- package/dist/prompts/hardening.js.map +1 -0
- package/dist/prompts/languages/elixir.d.ts +1 -0
- package/dist/prompts/languages/elixir.js +13 -0
- package/dist/prompts/languages/elixir.js.map +1 -0
- package/dist/prompts/languages/generic.d.ts +1 -0
- package/dist/prompts/languages/generic.js +11 -0
- package/dist/prompts/languages/generic.js.map +1 -0
- package/dist/prompts/languages/python.d.ts +1 -0
- package/dist/prompts/languages/python.js +14 -0
- package/dist/prompts/languages/python.js.map +1 -0
- package/dist/prompts/languages/typescript.d.ts +1 -0
- package/dist/prompts/languages/typescript.js +13 -0
- package/dist/prompts/languages/typescript.js.map +1 -0
- package/dist/resolver/github.d.ts +8 -0
- package/dist/resolver/github.js +70 -0
- package/dist/resolver/github.js.map +1 -0
- package/dist/resolver/local.d.ts +3 -0
- package/dist/resolver/local.js +75 -0
- package/dist/resolver/local.js.map +1 -0
- package/dist/resolver/types.d.ts +28 -0
- package/dist/resolver/types.js +2 -0
- package/dist/resolver/types.js.map +1 -0
- package/dist/roles/builtin.d.ts +4 -0
- package/dist/roles/builtin.js +234 -0
- package/dist/roles/builtin.js.map +1 -0
- package/dist/roles/dispatcher.d.ts +23 -0
- package/dist/roles/dispatcher.js +87 -0
- package/dist/roles/dispatcher.js.map +1 -0
- package/dist/roles/loader.d.ts +11 -0
- package/dist/roles/loader.js +107 -0
- package/dist/roles/loader.js.map +1 -0
- package/dist/roles/types.d.ts +13 -0
- package/dist/roles/types.js +2 -0
- package/dist/roles/types.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { detectLanguage } from '../prepare/language.js';
|
|
3
|
+
function parseDiffText(diffText) {
|
|
4
|
+
const hunks = [];
|
|
5
|
+
const fileBlocks = diffText.split(/^diff --git /m).filter(Boolean);
|
|
6
|
+
for (const block of fileBlocks) {
|
|
7
|
+
const lines = block.split('\n');
|
|
8
|
+
const headerLine = lines[0] ?? '';
|
|
9
|
+
// Extract filenames from "a/path b/path"
|
|
10
|
+
const headerMatch = headerLine.match(/^a\/(.+) b\/(.+)$/);
|
|
11
|
+
if (!headerMatch)
|
|
12
|
+
continue;
|
|
13
|
+
const aPath = headerMatch[1];
|
|
14
|
+
const bPath = headerMatch[2];
|
|
15
|
+
let status = 'modified';
|
|
16
|
+
let previousFilename;
|
|
17
|
+
const deletedMatch = block.match(/^deleted file mode/m);
|
|
18
|
+
const newFileMatch = block.match(/^new file mode/m);
|
|
19
|
+
const renameFromMatch = block.match(/^rename from (.+)$/m);
|
|
20
|
+
const renameToMatch = block.match(/^rename to (.+)$/m);
|
|
21
|
+
if (deletedMatch) {
|
|
22
|
+
status = 'deleted';
|
|
23
|
+
}
|
|
24
|
+
else if (newFileMatch) {
|
|
25
|
+
status = 'added';
|
|
26
|
+
}
|
|
27
|
+
else if (renameFromMatch && renameToMatch) {
|
|
28
|
+
status = 'renamed';
|
|
29
|
+
previousFilename = renameFromMatch[1].trim();
|
|
30
|
+
}
|
|
31
|
+
const filename = status === 'deleted' ? aPath : bPath;
|
|
32
|
+
// Extract the actual patch lines (@@...)
|
|
33
|
+
const patchStart = block.indexOf('\n@@');
|
|
34
|
+
const patch = patchStart >= 0 ? block.slice(patchStart + 1) : '';
|
|
35
|
+
let additions = 0;
|
|
36
|
+
let deletions = 0;
|
|
37
|
+
for (const line of patch.split('\n')) {
|
|
38
|
+
if (line.startsWith('+') && !line.startsWith('+++'))
|
|
39
|
+
additions++;
|
|
40
|
+
if (line.startsWith('-') && !line.startsWith('---'))
|
|
41
|
+
deletions++;
|
|
42
|
+
}
|
|
43
|
+
hunks.push({
|
|
44
|
+
filename,
|
|
45
|
+
status,
|
|
46
|
+
previousFilename,
|
|
47
|
+
patch,
|
|
48
|
+
additions,
|
|
49
|
+
deletions,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return hunks;
|
|
53
|
+
}
|
|
54
|
+
export async function loadLocalDiff(filePath) {
|
|
55
|
+
const content = await readFile(filePath, 'utf-8');
|
|
56
|
+
return parseDiffFromString(content);
|
|
57
|
+
}
|
|
58
|
+
export function parseDiffFromString(diffText) {
|
|
59
|
+
const hunks = parseDiffText(diffText);
|
|
60
|
+
const files = hunks.map((h) => ({
|
|
61
|
+
filename: h.filename,
|
|
62
|
+
status: h.status,
|
|
63
|
+
additions: h.additions,
|
|
64
|
+
deletions: h.deletions,
|
|
65
|
+
patch: h.patch,
|
|
66
|
+
language: detectLanguage(h.filename),
|
|
67
|
+
previousFilename: h.previousFilename,
|
|
68
|
+
}));
|
|
69
|
+
return {
|
|
70
|
+
files,
|
|
71
|
+
source: 'local',
|
|
72
|
+
rawDiff: diffText,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/resolver/local.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface FileChange {
|
|
2
|
+
filename: string;
|
|
3
|
+
status: 'added' | 'modified' | 'deleted' | 'renamed';
|
|
4
|
+
additions: number;
|
|
5
|
+
deletions: number;
|
|
6
|
+
patch: string;
|
|
7
|
+
language: string;
|
|
8
|
+
previousFilename?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PRMetadata {
|
|
11
|
+
owner: string;
|
|
12
|
+
repo: string;
|
|
13
|
+
number: number;
|
|
14
|
+
title: string;
|
|
15
|
+
body: string;
|
|
16
|
+
author: string;
|
|
17
|
+
base: string;
|
|
18
|
+
head: string;
|
|
19
|
+
url: string;
|
|
20
|
+
labels: string[];
|
|
21
|
+
draft: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface Diff {
|
|
24
|
+
files: FileChange[];
|
|
25
|
+
metadata?: PRMetadata;
|
|
26
|
+
source: 'github' | 'local';
|
|
27
|
+
rawDiff?: string;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/resolver/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
export const BUILTIN_ROLES = [
|
|
2
|
+
{
|
|
3
|
+
name: 'general',
|
|
4
|
+
description: 'Comprehensive code review covering all dimensions',
|
|
5
|
+
isSpecialized: false,
|
|
6
|
+
focus: ['security', 'correctness', 'best-practices', 'tests', 'api-design'],
|
|
7
|
+
systemPrompt: `You are an expert code reviewer performing a comprehensive review. Your role is to identify all types of issues: security vulnerabilities, logic errors, performance problems, missing tests, API design flaws, and code quality issues.
|
|
8
|
+
|
|
9
|
+
Be thorough but focused — only report real issues visible in the diff. Prioritize severity accurately. Consider the full context of each change.`,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'security-auditor',
|
|
13
|
+
description: 'Focused on security vulnerabilities: auth, injection, XSS, CSRF, IDOR',
|
|
14
|
+
isSpecialized: true,
|
|
15
|
+
focus: ['security'],
|
|
16
|
+
severityBias: { security: 1.2 },
|
|
17
|
+
systemPrompt: `You are a security-focused code reviewer with deep expertise in application security. Your primary mission is to identify security vulnerabilities and weaknesses.
|
|
18
|
+
|
|
19
|
+
Focus areas:
|
|
20
|
+
- Authentication and authorization flaws (broken auth, privilege escalation, IDOR)
|
|
21
|
+
- Injection vulnerabilities (SQL, NoSQL, command, LDAP, XPath injection)
|
|
22
|
+
- Cross-site scripting (XSS) — reflected, stored, and DOM-based
|
|
23
|
+
- Cross-site request forgery (CSRF)
|
|
24
|
+
- Sensitive data exposure (hardcoded secrets, improper encryption, insecure transmission)
|
|
25
|
+
- Security misconfigurations (debug mode in prod, overly permissive CORS, insecure defaults)
|
|
26
|
+
- Insecure deserialization
|
|
27
|
+
- Input validation failures
|
|
28
|
+
- Race conditions with security implications
|
|
29
|
+
- Dependency vulnerabilities (if visible)
|
|
30
|
+
|
|
31
|
+
Be specific about attack vectors and potential impact. Prefer security findings as "critical" or "important" when there is a real exploitable vulnerability.`,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'performance-engineer',
|
|
35
|
+
description: 'Focus on performance: N+1 queries, caching, algorithmic complexity',
|
|
36
|
+
isSpecialized: true,
|
|
37
|
+
focus: ['correctness', 'best-practices'],
|
|
38
|
+
systemPrompt: `You are a performance-focused code reviewer. Your mission is to identify performance bottlenecks, inefficiencies, and scalability concerns.
|
|
39
|
+
|
|
40
|
+
Focus areas:
|
|
41
|
+
- N+1 query problems (database queries in loops)
|
|
42
|
+
- Missing database indexes (new query patterns without indexes)
|
|
43
|
+
- Inefficient algorithms (O(n²) when O(n log n) is available)
|
|
44
|
+
- Missing caching opportunities (repeated expensive computations)
|
|
45
|
+
- Memory inefficiencies (large object retention, missing streaming)
|
|
46
|
+
- Unnecessary computation (recalculating constants, redundant processing)
|
|
47
|
+
- Synchronous blocking in async contexts
|
|
48
|
+
- Large payload sizes (over-fetching, missing pagination)
|
|
49
|
+
- Connection pool exhaustion
|
|
50
|
+
- Missing batch operations
|
|
51
|
+
|
|
52
|
+
Estimate impact where possible (e.g., "for 10k rows, this will issue 10k queries").`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'api-design',
|
|
56
|
+
description: 'API contracts, backwards compatibility, interface design',
|
|
57
|
+
isSpecialized: true,
|
|
58
|
+
focus: ['api-design'],
|
|
59
|
+
systemPrompt: `You are an API design expert. Your mission is to evaluate the quality and correctness of API design, with particular attention to backwards compatibility and consumer experience.
|
|
60
|
+
|
|
61
|
+
Focus areas:
|
|
62
|
+
- Breaking changes to public APIs (changed signatures, removed fields, altered semantics)
|
|
63
|
+
- REST conventions (correct HTTP methods, status codes, resource naming)
|
|
64
|
+
- Backwards compatibility (new required fields, changed field types, deprecated but not removed)
|
|
65
|
+
- Versioning strategy
|
|
66
|
+
- Error response consistency
|
|
67
|
+
- Missing or inadequate API documentation
|
|
68
|
+
- Inconsistent naming conventions
|
|
69
|
+
- Overly coupled interfaces
|
|
70
|
+
- Missing pagination, filtering, or sorting for collection endpoints
|
|
71
|
+
- GraphQL: N+1 in resolvers, missing rate limiting, overly broad queries allowed
|
|
72
|
+
- gRPC/Protobuf: field numbering, backward compatibility of message changes
|
|
73
|
+
|
|
74
|
+
Flag breaking changes as "critical" severity.`,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'test-coverage',
|
|
78
|
+
description: 'Missing tests, edge cases, flawed test logic',
|
|
79
|
+
isSpecialized: true,
|
|
80
|
+
focus: ['tests'],
|
|
81
|
+
systemPrompt: `You are a testing expert. Your mission is to evaluate test coverage quality and identify missing or inadequate tests.
|
|
82
|
+
|
|
83
|
+
Focus areas:
|
|
84
|
+
- Missing tests for new functionality added in the diff
|
|
85
|
+
- Edge cases not covered (empty inputs, null/undefined, boundary values, max values)
|
|
86
|
+
- Error path testing (what happens when things fail)
|
|
87
|
+
- Flawed test logic (tests that always pass, testing implementation not behavior)
|
|
88
|
+
- Test isolation issues (shared state, ordering dependencies, missing cleanup)
|
|
89
|
+
- Missing integration tests for critical paths
|
|
90
|
+
- Mocking anti-patterns (over-mocking, mocking what you own)
|
|
91
|
+
- Missing negative test cases
|
|
92
|
+
- Tests without assertions
|
|
93
|
+
- Brittle tests (relying on specific timing, order, or environment)
|
|
94
|
+
|
|
95
|
+
Suggest specific test cases and scenarios that should be covered.`,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'dx-critic',
|
|
99
|
+
description: 'Developer experience: readability, naming, documentation',
|
|
100
|
+
isSpecialized: true,
|
|
101
|
+
focus: ['best-practices'],
|
|
102
|
+
systemPrompt: `You are a developer experience (DX) expert. Your mission is to evaluate code readability, maintainability, and developer ergonomics.
|
|
103
|
+
|
|
104
|
+
Focus areas:
|
|
105
|
+
- Poor naming (unclear variable/function/class names that don't convey intent)
|
|
106
|
+
- Excessive complexity (functions doing too many things, deep nesting)
|
|
107
|
+
- Missing or inadequate documentation (complex algorithms without explanation)
|
|
108
|
+
- Magic numbers and strings without constants
|
|
109
|
+
- Inconsistent patterns within the codebase
|
|
110
|
+
- Long functions that should be decomposed
|
|
111
|
+
- Unclear error messages (from the developer/user perspective)
|
|
112
|
+
- Missing or unhelpful logging
|
|
113
|
+
- Over-engineering (unnecessary abstraction layers)
|
|
114
|
+
- Code duplication that should be extracted
|
|
115
|
+
- Confusing control flow
|
|
116
|
+
|
|
117
|
+
Be constructive — suggest better names and approaches. Rate issues as "minor" or "nitpick" unless they significantly impact maintainability.`,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'architecture',
|
|
121
|
+
description: 'Module boundaries, coupling, architectural patterns',
|
|
122
|
+
isSpecialized: true,
|
|
123
|
+
focus: ['best-practices', 'api-design'],
|
|
124
|
+
systemPrompt: `You are a software architect. Your mission is to evaluate architectural decisions, module boundaries, and coupling.
|
|
125
|
+
|
|
126
|
+
Focus areas:
|
|
127
|
+
- Violation of module/layer boundaries (e.g., UI code accessing DB directly)
|
|
128
|
+
- Tight coupling between components that should be independent
|
|
129
|
+
- Missing dependency inversion (high-level modules depending on low-level details)
|
|
130
|
+
- Circular dependencies
|
|
131
|
+
- God objects or modules that know too much
|
|
132
|
+
- Missing abstractions (concrete implementations where interfaces are needed)
|
|
133
|
+
- Architectural inconsistencies (mixing patterns in the same layer)
|
|
134
|
+
- Side effects in unexpected places
|
|
135
|
+
- State management issues (global state, inappropriate mutation)
|
|
136
|
+
- Missing or incorrect use of design patterns
|
|
137
|
+
- Scalability concerns at the architectural level
|
|
138
|
+
|
|
139
|
+
Think in terms of long-term maintainability and the ability to change components independently.`,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'bug-hunter',
|
|
143
|
+
description: 'Logic errors, null paths, race conditions, off-by-one',
|
|
144
|
+
isSpecialized: true,
|
|
145
|
+
focus: ['correctness'],
|
|
146
|
+
systemPrompt: `You are a bug hunter specializing in finding logic errors and correctness issues. Your mission is to find bugs in the code.
|
|
147
|
+
|
|
148
|
+
Focus areas:
|
|
149
|
+
- Logic errors (conditions that are always true/false, incorrect operator precedence)
|
|
150
|
+
- Off-by-one errors (loop bounds, array indices, pagination)
|
|
151
|
+
- Null/undefined dereference without proper checks
|
|
152
|
+
- Race conditions and concurrency bugs (shared mutable state, TOCTOU)
|
|
153
|
+
- Integer overflow/underflow
|
|
154
|
+
- Floating point precision issues
|
|
155
|
+
- Incorrect comparison (== vs ===, reference vs value equality)
|
|
156
|
+
- Missing or incorrect error propagation
|
|
157
|
+
- Incorrect state machine transitions
|
|
158
|
+
- Resource cleanup in error paths
|
|
159
|
+
- Incorrect handling of empty collections
|
|
160
|
+
- Assumption violations (assuming sorted input, assuming specific locale)
|
|
161
|
+
|
|
162
|
+
Be precise about when and how the bug manifests. Prefer "critical" or "important" severity for bugs that can cause incorrect behavior in production.`,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'accessibility-auditor',
|
|
166
|
+
description: 'WCAG compliance, ARIA, keyboard navigation',
|
|
167
|
+
isSpecialized: true,
|
|
168
|
+
focus: ['best-practices'],
|
|
169
|
+
systemPrompt: `You are an accessibility expert. Your mission is to evaluate code for accessibility issues per WCAG 2.1 AA standards.
|
|
170
|
+
|
|
171
|
+
Focus areas:
|
|
172
|
+
- Missing or incorrect ARIA labels and roles
|
|
173
|
+
- Images without alt text (or with poor alt text)
|
|
174
|
+
- Color contrast issues (where identifiable from code)
|
|
175
|
+
- Keyboard navigation (elements not reachable by keyboard, missing focus management)
|
|
176
|
+
- Screen reader compatibility (missing live regions, incorrect reading order)
|
|
177
|
+
- Form accessibility (labels not associated with inputs, missing error announcements)
|
|
178
|
+
- Interactive elements not usable without mouse
|
|
179
|
+
- Missing skip navigation links
|
|
180
|
+
- Time-based content without controls
|
|
181
|
+
- Missing captions or transcripts references
|
|
182
|
+
- Focus traps (modal dialogs without proper focus management)
|
|
183
|
+
- Semantic HTML violations (using divs where buttons/links are appropriate)
|
|
184
|
+
|
|
185
|
+
Only flag issues visible in the code diff. Rate WCAG Level A violations as "critical", Level AA as "important".`,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'project-rules',
|
|
189
|
+
description: 'Enforces repo conventions from AGENTS.md, CLAUDE.md, etc.',
|
|
190
|
+
isSpecialized: true,
|
|
191
|
+
focus: ['best-practices'],
|
|
192
|
+
systemPrompt: `You are a project conventions enforcer. Your mission is to ensure the code follows the repository's established rules, conventions, and patterns as defined in the project's rules files (AGENTS.md, CLAUDE.md, CONTRIBUTING.md, etc.).
|
|
193
|
+
|
|
194
|
+
The project rules file content will be provided in the context section above. Review the diff against those rules specifically.
|
|
195
|
+
|
|
196
|
+
Focus areas:
|
|
197
|
+
- Naming conventions defined in the rules
|
|
198
|
+
- File organization and structure requirements
|
|
199
|
+
- Required patterns or anti-patterns to avoid
|
|
200
|
+
- Commit message or PR conventions (if visible)
|
|
201
|
+
- Testing requirements
|
|
202
|
+
- Documentation requirements
|
|
203
|
+
- Any project-specific architectural decisions
|
|
204
|
+
|
|
205
|
+
Reference specific rule violations by quoting the rule that is being violated.`,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'spec-compliance',
|
|
209
|
+
description: 'Checks implementation against a spec or plan file',
|
|
210
|
+
isSpecialized: true,
|
|
211
|
+
focus: ['correctness', 'api-design'],
|
|
212
|
+
systemPrompt: `You are a specification compliance reviewer. Your mission is to verify that the implementation matches the provided specification or design document.
|
|
213
|
+
|
|
214
|
+
The specification content will be provided in the context section above. Review the diff against that specification.
|
|
215
|
+
|
|
216
|
+
Focus areas:
|
|
217
|
+
- Deviations from specified behavior
|
|
218
|
+
- Missing required features or endpoints
|
|
219
|
+
- Incorrect data types or field names vs spec
|
|
220
|
+
- Missing validation described in spec
|
|
221
|
+
- Incorrect error handling vs spec
|
|
222
|
+
- Unspecified behavior that contradicts the spirit of the spec
|
|
223
|
+
- Implementation gaps (things in spec not yet implemented)
|
|
224
|
+
|
|
225
|
+
Be precise about which part of the spec is being violated or not met.`,
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
export function getRoleByName(name) {
|
|
229
|
+
return BUILTIN_ROLES.find((r) => r.name === name);
|
|
230
|
+
}
|
|
231
|
+
export function getBuiltinRoleNames() {
|
|
232
|
+
return BUILTIN_ROLES.map((r) => r.name);
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=builtin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../src/roles/builtin.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAW;IACnC;QACE,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,mDAAmD;QAChE,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,OAAO,EAAE,YAAY,CAAC;QAC3E,YAAY,EAAE;;iJAE+H;KAC9I;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,uEAAuE;QACpF,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,UAAU,CAAC;QACnB,YAAY,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;QAC/B,YAAY,EAAE;;;;;;;;;;;;;;6JAc2I;KAC1J;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,oEAAoE;QACjF,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;QACxC,YAAY,EAAE;;;;;;;;;;;;;;oFAckE;KACjF;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,0DAA0D;QACvE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,YAAY,CAAC;QACrB,YAAY,EAAE;;;;;;;;;;;;;;;8CAe4B;KAC3C;IACD;QACE,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,8CAA8C;QAC3D,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,OAAO,CAAC;QAChB,YAAY,EAAE;;;;;;;;;;;;;;kEAcgD;KAC/D;IACD;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,0DAA0D;QACvE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,gBAAgB,CAAC;QACzB,YAAY,EAAE;;;;;;;;;;;;;;;6IAe2H;KAC1I;IACD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,qDAAqD;QAClE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,gBAAgB,EAAE,YAAY,CAAC;QACvC,YAAY,EAAE;;;;;;;;;;;;;;;gGAe8E;KAC7F;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,uDAAuD;QACpE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,aAAa,CAAC;QACtB,YAAY,EAAE;;;;;;;;;;;;;;;;qJAgBmI;KAClJ;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,4CAA4C;QACzD,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,gBAAgB,CAAC;QACzB,YAAY,EAAE;;;;;;;;;;;;;;;;gHAgB8F;KAC7G;IACD;QACE,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,2DAA2D;QACxE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,gBAAgB,CAAC;QACzB,YAAY,EAAE;;;;;;;;;;;;;+EAa6D;KAC5E;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,mDAAmD;QAChE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC,aAAa,EAAE,YAAY,CAAC;QACpC,YAAY,EAAE;;;;;;;;;;;;;sEAaoD;KACnE;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Role, ReviewAssignment } from './types.js';
|
|
2
|
+
import type { ReviewerPair } from '../config/schema.js';
|
|
3
|
+
export type ModelProvider = 'anthropic' | 'openai' | 'google' | 'openai-compat';
|
|
4
|
+
export declare function detectProvider(model: string): ModelProvider;
|
|
5
|
+
/**
|
|
6
|
+
* Build assignments from explicit reviewer pairs (--reviewer model:role)
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildExplicitAssignments(reviewerPairs: ReviewerPair[], roleMap: Map<string, Role>): ReviewAssignment[];
|
|
9
|
+
/**
|
|
10
|
+
* Build assignments using the dispatch algorithm:
|
|
11
|
+
* - general role: runs on ALL models
|
|
12
|
+
* - specialized roles: spread across models via shuffled round-robin
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildRoleAssignments(models: string[], roles: Role[]): ReviewAssignment[];
|
|
15
|
+
/**
|
|
16
|
+
* Main dispatch builder — handles explicit pairs or role-based assignment
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildAssignments(opts: {
|
|
19
|
+
models: string[];
|
|
20
|
+
roles: Role[];
|
|
21
|
+
explicitReviewers?: ReviewerPair[];
|
|
22
|
+
roleMap: Map<string, Role>;
|
|
23
|
+
}): ReviewAssignment[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export function detectProvider(model) {
|
|
2
|
+
// Handle explicit provider prefix (e.g. "anthropic/claude-sonnet-4-5")
|
|
3
|
+
if (model.startsWith('anthropic/'))
|
|
4
|
+
return 'anthropic';
|
|
5
|
+
if (model.startsWith('openai/'))
|
|
6
|
+
return 'openai';
|
|
7
|
+
if (model.startsWith('google/'))
|
|
8
|
+
return 'google';
|
|
9
|
+
if (model.startsWith('openai-compat/'))
|
|
10
|
+
return 'openai-compat';
|
|
11
|
+
// Detect by model name prefix
|
|
12
|
+
if (model.startsWith('claude'))
|
|
13
|
+
return 'anthropic';
|
|
14
|
+
if (model.startsWith('gpt') ||
|
|
15
|
+
model.startsWith('o1') ||
|
|
16
|
+
model.startsWith('o3') ||
|
|
17
|
+
model.startsWith('o4'))
|
|
18
|
+
return 'openai';
|
|
19
|
+
if (model.startsWith('gemini'))
|
|
20
|
+
return 'google';
|
|
21
|
+
// OpenAI-compatible for anything else (local models, etc.)
|
|
22
|
+
return 'openai-compat';
|
|
23
|
+
}
|
|
24
|
+
function shuffle(arr) {
|
|
25
|
+
const out = [...arr];
|
|
26
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
27
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
28
|
+
[out[i], out[j]] = [out[j], out[i]];
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build assignments from explicit reviewer pairs (--reviewer model:role)
|
|
34
|
+
*/
|
|
35
|
+
export function buildExplicitAssignments(reviewerPairs, roleMap) {
|
|
36
|
+
return reviewerPairs.flatMap(({ model, role: roleName }) => {
|
|
37
|
+
const role = roleMap.get(roleName);
|
|
38
|
+
if (!role) {
|
|
39
|
+
console.warn(`Warning: unknown role "${roleName}" in reviewer pair, skipping`);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return [{ model, role, provider: detectProvider(model) }];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build assignments using the dispatch algorithm:
|
|
47
|
+
* - general role: runs on ALL models
|
|
48
|
+
* - specialized roles: spread across models via shuffled round-robin
|
|
49
|
+
*/
|
|
50
|
+
export function buildRoleAssignments(models, roles) {
|
|
51
|
+
const assignments = [];
|
|
52
|
+
const generalRoles = roles.filter((r) => !r.isSpecialized);
|
|
53
|
+
const specializedRoles = roles.filter((r) => r.isSpecialized);
|
|
54
|
+
// General roles: every model runs every general role
|
|
55
|
+
for (const model of models) {
|
|
56
|
+
for (const role of generalRoles) {
|
|
57
|
+
assignments.push({
|
|
58
|
+
model,
|
|
59
|
+
role,
|
|
60
|
+
provider: detectProvider(model),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Specialized roles: shuffled round-robin across models
|
|
65
|
+
if (specializedRoles.length > 0 && models.length > 0) {
|
|
66
|
+
const shuffledModels = shuffle(models);
|
|
67
|
+
specializedRoles.forEach((role, index) => {
|
|
68
|
+
const model = shuffledModels[index % shuffledModels.length];
|
|
69
|
+
assignments.push({
|
|
70
|
+
model,
|
|
71
|
+
role,
|
|
72
|
+
provider: detectProvider(model),
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return assignments;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Main dispatch builder — handles explicit pairs or role-based assignment
|
|
80
|
+
*/
|
|
81
|
+
export function buildAssignments(opts) {
|
|
82
|
+
if (opts.explicitReviewers && opts.explicitReviewers.length > 0) {
|
|
83
|
+
return buildExplicitAssignments(opts.explicitReviewers, opts.roleMap);
|
|
84
|
+
}
|
|
85
|
+
return buildRoleAssignments(opts.models, opts.roles);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/roles/dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Role } from './types.js';
|
|
2
|
+
import type { Config } from '../config/schema.js';
|
|
3
|
+
export declare function findProjectRulesFile(cwd?: string): Promise<string | null>;
|
|
4
|
+
export declare function loadProjectRulesContent(cwd?: string): Promise<string | null>;
|
|
5
|
+
export declare function buildCustomRole(config: {
|
|
6
|
+
name: string;
|
|
7
|
+
systemPrompt?: string;
|
|
8
|
+
focus?: string[];
|
|
9
|
+
severityBias?: Record<string, number>;
|
|
10
|
+
}): Role;
|
|
11
|
+
export declare function resolveRoles(config: Config, requestedRoles?: string[], projectRulesContent?: string, specContent?: string): Promise<Role[]>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { readFile, access } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { BUILTIN_ROLES, getRoleByName } from './builtin.js';
|
|
4
|
+
const PROJECT_RULES_FILES = [
|
|
5
|
+
'AGENTS.md',
|
|
6
|
+
'CLAUDE.md',
|
|
7
|
+
'CONTRIBUTING.md',
|
|
8
|
+
'.github/CONTRIBUTING.md',
|
|
9
|
+
'DEVELOPMENT.md',
|
|
10
|
+
'docs/CONTRIBUTING.md',
|
|
11
|
+
];
|
|
12
|
+
export async function findProjectRulesFile(cwd = process.cwd()) {
|
|
13
|
+
for (const filename of PROJECT_RULES_FILES) {
|
|
14
|
+
const fullPath = join(cwd, filename);
|
|
15
|
+
try {
|
|
16
|
+
await access(fullPath);
|
|
17
|
+
return fullPath;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// not found, try next
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export async function loadProjectRulesContent(cwd) {
|
|
26
|
+
const filePath = await findProjectRulesFile(cwd);
|
|
27
|
+
if (!filePath)
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
return await readFile(filePath, 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function buildCustomRole(config) {
|
|
37
|
+
// If it extends a builtin, start from that base
|
|
38
|
+
const base = getRoleByName(config.name);
|
|
39
|
+
return {
|
|
40
|
+
name: config.name,
|
|
41
|
+
description: `Custom role: ${config.name}`,
|
|
42
|
+
isSpecialized: true,
|
|
43
|
+
focus: config.focus ?? base?.focus ?? ['best-practices'],
|
|
44
|
+
severityBias: config.severityBias ?? base?.severityBias,
|
|
45
|
+
systemPrompt: config.systemPrompt ??
|
|
46
|
+
base?.systemPrompt ??
|
|
47
|
+
`You are a code reviewer with focus on: ${config.focus?.join(', ') ?? 'general quality'}.`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export async function resolveRoles(config, requestedRoles, projectRulesContent, specContent) {
|
|
51
|
+
const roles = new Map();
|
|
52
|
+
// Add all builtin roles
|
|
53
|
+
for (const role of BUILTIN_ROLES) {
|
|
54
|
+
roles.set(role.name, role);
|
|
55
|
+
}
|
|
56
|
+
// Add custom roles from config (override builtins if same name)
|
|
57
|
+
if (config.customRoles) {
|
|
58
|
+
for (const customConfig of config.customRoles) {
|
|
59
|
+
roles.set(customConfig.name, buildCustomRole(customConfig));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Augment project-rules role with actual content
|
|
63
|
+
if (projectRulesContent) {
|
|
64
|
+
const projectRulesRole = roles.get('project-rules');
|
|
65
|
+
roles.set('project-rules', {
|
|
66
|
+
...projectRulesRole,
|
|
67
|
+
systemPrompt: projectRulesRole.systemPrompt +
|
|
68
|
+
'\n\n## Project Rules File Content\n\n' +
|
|
69
|
+
projectRulesContent,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Augment spec-compliance role with actual spec content
|
|
73
|
+
if (specContent) {
|
|
74
|
+
const specRole = roles.get('spec-compliance');
|
|
75
|
+
roles.set('spec-compliance', {
|
|
76
|
+
...specRole,
|
|
77
|
+
systemPrompt: specRole.systemPrompt +
|
|
78
|
+
'\n\n## Specification Content\n\n' +
|
|
79
|
+
specContent,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Determine which roles to use
|
|
83
|
+
const requested = requestedRoles ?? config.roles;
|
|
84
|
+
if (requested && requested.length > 0) {
|
|
85
|
+
const hasAll = requested.some((r) => r.toLowerCase() === 'all');
|
|
86
|
+
if (hasAll) {
|
|
87
|
+
if (requested.length > 1) {
|
|
88
|
+
throw new Error(`'all' cannot be combined with other roles. Got: ${requested.join(', ')}`);
|
|
89
|
+
}
|
|
90
|
+
return Array.from(roles.values());
|
|
91
|
+
}
|
|
92
|
+
const result = [];
|
|
93
|
+
for (const name of requested) {
|
|
94
|
+
const role = roles.get(name);
|
|
95
|
+
if (role) {
|
|
96
|
+
result.push(role);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.warn(`Warning: unknown role "${name}", skipping`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
// Default: general + all specialized
|
|
105
|
+
return Array.from(roles.values());
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/roles/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Role {
|
|
2
|
+
name: string;
|
|
3
|
+
systemPrompt: string;
|
|
4
|
+
focus: string[];
|
|
5
|
+
severityBias?: Record<string, number>;
|
|
6
|
+
description: string;
|
|
7
|
+
isSpecialized: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ReviewAssignment {
|
|
10
|
+
model: string;
|
|
11
|
+
role: Role;
|
|
12
|
+
provider: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/roles/types.ts"],"names":[],"mappings":""}
|