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/dist/index.js ADDED
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env node
2
+ import { Command, InvalidArgumentError } from 'commander';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { readFile } from 'fs/promises';
6
+ import { loadConfig } from './config/loader.js';
7
+ import { parseGitHubTarget, fetchPRDiff } from './resolver/github.js';
8
+ import { loadLocalDiff } from './resolver/local.js';
9
+ import { chunkDiff } from './prepare/chunker.js';
10
+ import { buildPrompt } from './prepare/prompt-builder.js';
11
+ import { BUILTIN_ROLES, getRoleByName } from './roles/builtin.js';
12
+ import { resolveRoles, loadProjectRulesContent } from './roles/loader.js';
13
+ import { buildAssignments } from './roles/dispatcher.js';
14
+ import { runReviews } from './dispatch/runner.js';
15
+ import { deduplicateFindings } from './consensus/deduper.js';
16
+ import { computeConsensus } from './consensus/voter.js';
17
+ import { printReviewSummary } from './output/terminal.js';
18
+ import { postGitHubReview } from './output/github.js';
19
+ import { toJson, writeJsonOutput } from './output/json.js';
20
+ import { writeMarkdownOutput } from './output/markdown.js';
21
+ const program = new Command();
22
+ program
23
+ .name('rcl')
24
+ .description('Review Council — multi-model AI code review')
25
+ .version('1.0.0');
26
+ // review command
27
+ program
28
+ .command('review <target>')
29
+ .description('Review a PR or local diff. Target: owner/repo#N, GitHub PR URL, or path to .patch file')
30
+ .option('--role <name>', 'Use a single named role')
31
+ .option('--roles <names>', 'Comma-separated list of roles')
32
+ .option('--reviewer <pair>', 'Explicit model:role pair (repeatable)', (val, prev) => {
33
+ prev.push(val);
34
+ return prev;
35
+ }, [])
36
+ .option('--context <path>', 'Context file or directory to include (repeatable)', (val, prev) => {
37
+ prev.push(val);
38
+ return prev;
39
+ }, [])
40
+ .option('--spec <path>', 'Specification file for spec-compliance role')
41
+ .option('--models <models>', 'Comma-separated list of models to use')
42
+ .option('--focus <areas>', 'Comma-separated focus areas')
43
+ .option('--post', 'Post review as GitHub PR comment')
44
+ .option('--json', 'Output JSON to stdout')
45
+ .option('--json-file <path>', 'Write JSON output to file')
46
+ .option('--markdown <path>', 'Write Markdown report to file')
47
+ .option('--ci', 'CI mode: exit non-zero if critical/important findings')
48
+ .option('--config <path>', 'Path to config file')
49
+ .action(async (target, opts) => {
50
+ await runReview(target, opts);
51
+ });
52
+ // roles subcommand
53
+ const rolesCmd = program.command('roles').description('Manage and inspect roles');
54
+ rolesCmd
55
+ .command('list')
56
+ .description('List all built-in roles')
57
+ .action(() => {
58
+ console.log('\n' + chalk.bold('Built-in Roles:') + '\n');
59
+ for (const role of BUILTIN_ROLES) {
60
+ const tag = role.isSpecialized ? chalk.dim('[specialized]') : chalk.blue('[general]');
61
+ console.log(` ${chalk.cyan(role.name.padEnd(22))} ${tag} ${chalk.dim(role.description)}`);
62
+ }
63
+ console.log('');
64
+ });
65
+ rolesCmd
66
+ .command('show <name>')
67
+ .description('Show details for a specific role')
68
+ .action((name) => {
69
+ const role = getRoleByName(name);
70
+ if (!role) {
71
+ console.error(chalk.red(`Role "${name}" not found.`));
72
+ console.log('Run `rcl roles list` to see available roles.');
73
+ process.exit(1);
74
+ }
75
+ console.log('\n' + chalk.bold(`Role: ${role.name}`) + '\n');
76
+ console.log(chalk.dim('Description:'), role.description);
77
+ console.log(chalk.dim('Type:'), role.isSpecialized ? 'specialized' : 'general');
78
+ console.log(chalk.dim('Focus:'), role.focus.join(', '));
79
+ if (role.severityBias) {
80
+ console.log(chalk.dim('Severity bias:'), JSON.stringify(role.severityBias));
81
+ }
82
+ console.log('\n' + chalk.dim('System Prompt:'));
83
+ console.log(role.systemPrompt);
84
+ console.log('');
85
+ });
86
+ async function runReview(target, opts) {
87
+ const spinner = ora('Loading configuration...').start();
88
+ try {
89
+ // Load config
90
+ const config = await loadConfig(opts.config);
91
+ // Validate mutually exclusive role options
92
+ const roleOptionCount = [opts.role, opts.roles, opts.reviewer?.length].filter(Boolean).length;
93
+ if (roleOptionCount > 1) {
94
+ spinner.fail('--role, --roles, and --reviewer are mutually exclusive');
95
+ process.exit(1);
96
+ }
97
+ // Override models from CLI
98
+ if (opts.models) {
99
+ config.models = opts.models.split(',').map((s) => s.trim());
100
+ }
101
+ // Determine roles to use
102
+ let requestedRoles;
103
+ let explicitReviewers;
104
+ if (opts.role) {
105
+ requestedRoles = [opts.role];
106
+ }
107
+ else if (opts.roles) {
108
+ requestedRoles = opts.roles.split(',').map((s) => s.trim());
109
+ }
110
+ else if (opts.reviewer && opts.reviewer.length > 0) {
111
+ explicitReviewers = opts.reviewer.map((pair) => {
112
+ const colonIdx = pair.indexOf(':');
113
+ if (colonIdx < 0) {
114
+ throw new InvalidArgumentError(`Invalid reviewer pair "${pair}". Use model:role format.`);
115
+ }
116
+ return {
117
+ model: pair.slice(0, colonIdx),
118
+ role: pair.slice(colonIdx + 1),
119
+ };
120
+ });
121
+ }
122
+ // Load spec file
123
+ let specContent;
124
+ const specPath = opts.spec ?? config.spec;
125
+ if (specPath) {
126
+ try {
127
+ specContent = await readFile(specPath, 'utf-8');
128
+ }
129
+ catch {
130
+ spinner.warn(`Could not read spec file: ${specPath}`);
131
+ }
132
+ }
133
+ // Load project rules
134
+ const projectRulesContent = await loadProjectRulesContent();
135
+ // Resolve roles
136
+ const roles = await resolveRoles(config, requestedRoles, projectRulesContent ?? undefined, specContent);
137
+ if (roles.length === 0) {
138
+ spinner.fail('No roles resolved. Check your --role/--roles flags.');
139
+ process.exit(1);
140
+ }
141
+ // Build role map for voter
142
+ const roleMap = new Map();
143
+ for (const role of roles) {
144
+ roleMap.set(role.name, role);
145
+ }
146
+ // Build assignments
147
+ const models = config.models ?? ['claude-opus-4-6'];
148
+ const assignments = buildAssignments({
149
+ models,
150
+ roles,
151
+ explicitReviewers,
152
+ roleMap,
153
+ });
154
+ spinner.text = `Resolving diff for: ${target}`;
155
+ // Resolve diff
156
+ let diff;
157
+ const isLocalFile = target.endsWith('.patch') ||
158
+ target.endsWith('.diff') ||
159
+ target.startsWith('./') ||
160
+ target.startsWith('/');
161
+ if (isLocalFile) {
162
+ diff = await loadLocalDiff(target);
163
+ }
164
+ else {
165
+ const prTarget = parseGitHubTarget(target);
166
+ diff = await fetchPRDiff(prTarget, config.githubToken);
167
+ }
168
+ if (diff.files.length === 0) {
169
+ spinner.warn('No files found in diff. Nothing to review.');
170
+ process.exit(0);
171
+ }
172
+ // Chunk the diff
173
+ const chunks = chunkDiff(diff.files);
174
+ spinner.text = `Building prompts (${chunks.length} chunk(s), ${assignments.length} reviewer(s))...`;
175
+ // Build context options
176
+ const contextFiles = [
177
+ ...(opts.context ?? []),
178
+ ...(config.context ?? []),
179
+ ];
180
+ // Build prompts for each assignment Ɨ chunk (using first chunk for simplicity, multi-chunk support TBD)
181
+ const primaryChunk = chunks[0];
182
+ const prompts = await Promise.all(assignments.map((assignment) => buildPrompt(primaryChunk, assignment.role, {
183
+ contextFiles: contextFiles.length > 0 ? contextFiles : undefined,
184
+ specFile: specPath,
185
+ })));
186
+ spinner.text = `Running ${assignments.length} reviews...`;
187
+ spinner.start();
188
+ const startTime = Date.now();
189
+ const completedReviews = [];
190
+ const reviews = await runReviews(assignments, prompts, {
191
+ timeoutMs: config.timeout ?? 120_000,
192
+ maxRetries: config.maxRetries ?? 3,
193
+ concurrency: config.concurrency ?? 6,
194
+ onReviewComplete: (review) => {
195
+ completedReviews.push(review);
196
+ const done = completedReviews.length;
197
+ const total = assignments.length;
198
+ const icon = review.status === 'success' ? 'āœ“' : review.status === 'timeout' ? 'ā±' : 'āœ—';
199
+ spinner.text = `Reviews: ${done}/${total} [${icon} ${review.model}/${review.role}]`;
200
+ },
201
+ });
202
+ spinner.text = 'Computing consensus...';
203
+ // Deduplicate and compute consensus
204
+ const groups = deduplicateFindings(reviews, config.thresholds?.jaccardThreshold ?? 0.3, config.thresholds?.dedupeLineWindow ?? 5);
205
+ const consensusFindings = computeConsensus(groups, reviews, roleMap);
206
+ const totalRawFindings = reviews.reduce((sum, r) => sum + r.findings.length, 0);
207
+ const result = {
208
+ reviews,
209
+ findings: consensusFindings,
210
+ stats: {
211
+ totalReviews: reviews.length,
212
+ successfulReviews: reviews.filter((r) => r.status === 'success').length,
213
+ totalRawFindings,
214
+ totalDeduped: consensusFindings.length,
215
+ durationMs: Date.now() - startTime,
216
+ },
217
+ };
218
+ spinner.succeed('Review complete');
219
+ // Output
220
+ if (opts.json) {
221
+ console.log(toJson(result));
222
+ }
223
+ else {
224
+ printReviewSummary(result);
225
+ }
226
+ if (opts.jsonFile) {
227
+ await writeJsonOutput(result, opts.jsonFile);
228
+ console.log(chalk.dim(`JSON written to: ${opts.jsonFile}`));
229
+ }
230
+ if (opts.markdown) {
231
+ await writeMarkdownOutput(result, opts.markdown);
232
+ console.log(chalk.dim(`Markdown written to: ${opts.markdown}`));
233
+ }
234
+ if (opts.post && diff.metadata) {
235
+ const postSpinner = ora('Posting review to GitHub...').start();
236
+ try {
237
+ await postGitHubReview(result, diff.metadata, config.githubToken);
238
+ postSpinner.succeed('Review posted to GitHub');
239
+ }
240
+ catch (err) {
241
+ postSpinner.fail(`Failed to post to GitHub: ${String(err)}`);
242
+ }
243
+ }
244
+ // CI mode: exit non-zero if critical/important findings
245
+ if (opts.ci) {
246
+ const blocking = result.findings.filter((f) => f.severity === 'critical' || f.severity === 'important');
247
+ if (blocking.length > 0) {
248
+ console.error(chalk.red(`\nCI: ${blocking.length} blocking finding(s) found. Exiting with code 1.`));
249
+ process.exit(1);
250
+ }
251
+ }
252
+ }
253
+ catch (err) {
254
+ spinner.fail(String(err));
255
+ if (process.env['RCL_DEBUG']) {
256
+ console.error(err);
257
+ }
258
+ process.exit(1);
259
+ }
260
+ }
261
+ program.parse();
262
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ReviewResult } from '../consensus/types.js';
2
+ import type { PRMetadata } from '../resolver/types.js';
3
+ export declare function postGitHubReview(result: ReviewResult, metadata: PRMetadata, token?: string): Promise<void>;
@@ -0,0 +1,95 @@
1
+ import { Octokit } from '@octokit/rest';
2
+ function buildCommentBody(finding) {
3
+ const { consensus } = finding;
4
+ const severityEmoji = {
5
+ critical: 'šŸ”“',
6
+ important: '🟔',
7
+ minor: 'šŸ”µ',
8
+ nitpick: '⚪',
9
+ }[finding.severity];
10
+ const lines = [
11
+ `### ${severityEmoji} ${finding.severity.toUpperCase()}: ${finding.title}`,
12
+ '',
13
+ finding.description,
14
+ ];
15
+ if (finding.suggestedFix) {
16
+ lines.push('', '**Suggested Fix:**', finding.suggestedFix);
17
+ }
18
+ lines.push('', '---', `*Confidence: **${consensus.confidenceLabel}** (${(consensus.confidence * 100).toFixed(0)}%) Ā· ` +
19
+ `Flagged by: ${consensus.roles.join(', ')}` +
20
+ (consensus.elevated
21
+ ? ` Ā· Elevated from \`${consensus.original_severity}\` [${consensus.elevation}]`
22
+ : '') +
23
+ '*');
24
+ return lines.join('\n');
25
+ }
26
+ function buildSummaryComment(result) {
27
+ const { stats } = result;
28
+ const lines = [
29
+ '## šŸ” Review Council Report',
30
+ '',
31
+ `**${stats.successfulReviews}/${stats.totalReviews}** reviewers completed Ā· ` +
32
+ `**${stats.totalDeduped}** unique findings (from ${stats.totalRawFindings} raw)`,
33
+ '',
34
+ ];
35
+ // Summary table
36
+ const bySeverity = {
37
+ critical: 0,
38
+ important: 0,
39
+ minor: 0,
40
+ nitpick: 0,
41
+ };
42
+ for (const f of result.findings) {
43
+ bySeverity[f.severity] = (bySeverity[f.severity] ?? 0) + 1;
44
+ }
45
+ lines.push('| Severity | Count |');
46
+ lines.push('|----------|-------|');
47
+ for (const [sev, count] of Object.entries(bySeverity)) {
48
+ if (count > 0) {
49
+ lines.push(`| ${sev} | ${count} |`);
50
+ }
51
+ }
52
+ lines.push('', '### Reviewers');
53
+ for (const review of result.reviews) {
54
+ const icon = review.status === 'success' ? 'āœ…' : review.status === 'timeout' ? 'ā±ļø' : 'āŒ';
55
+ lines.push(`- ${icon} **${review.model}** / ${review.role}: ` +
56
+ (review.status === 'success'
57
+ ? `${review.findings.length} findings`
58
+ : review.error ?? review.status));
59
+ }
60
+ return lines.join('\n');
61
+ }
62
+ export async function postGitHubReview(result, metadata, token) {
63
+ const octokit = new Octokit({
64
+ auth: token ?? process.env['GITHUB_TOKEN'],
65
+ });
66
+ // Get the latest commit SHA for the PR
67
+ const prResponse = await octokit.pulls.get({
68
+ owner: metadata.owner,
69
+ repo: metadata.repo,
70
+ pull_number: metadata.number,
71
+ });
72
+ const commitSha = prResponse.data.head.sha;
73
+ // Build inline review comments for findings with location info
74
+ const inlineComments = [];
75
+ for (const finding of result.findings) {
76
+ if (finding.file && finding.startLine > 0) {
77
+ inlineComments.push({
78
+ path: finding.file,
79
+ line: finding.startLine,
80
+ body: buildCommentBody(finding),
81
+ });
82
+ }
83
+ }
84
+ // Post review
85
+ await octokit.pulls.createReview({
86
+ owner: metadata.owner,
87
+ repo: metadata.repo,
88
+ pull_number: metadata.number,
89
+ commit_id: commitSha,
90
+ body: buildSummaryComment(result),
91
+ event: 'COMMENT',
92
+ comments: inlineComments,
93
+ });
94
+ }
95
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/output/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ReviewResult } from '../consensus/types.js';
2
+ export declare function toJson(result: ReviewResult, pretty?: boolean): string;
3
+ export declare function writeJsonOutput(result: ReviewResult, path: string): Promise<void>;
@@ -0,0 +1,8 @@
1
+ import { writeFile } from 'fs/promises';
2
+ export function toJson(result, pretty = true) {
3
+ return JSON.stringify(result, null, pretty ? 2 : 0);
4
+ }
5
+ export async function writeJsonOutput(result, path) {
6
+ await writeFile(path, toJson(result), 'utf-8');
7
+ }
8
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,UAAU,MAAM,CAAC,MAAoB,EAAE,MAAM,GAAG,IAAI;IACxD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAoB,EAAE,IAAY;IACtE,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ReviewResult } from '../consensus/types.js';
2
+ export declare function toMarkdown(result: ReviewResult): string;
3
+ export declare function writeMarkdownOutput(result: ReviewResult, path: string): Promise<void>;
@@ -0,0 +1,81 @@
1
+ import { writeFile } from 'fs/promises';
2
+ function severityEmoji(severity) {
3
+ return { critical: 'šŸ”“', important: '🟔', minor: 'šŸ”µ', nitpick: '⚪' }[severity];
4
+ }
5
+ function buildFindingSection(finding, index) {
6
+ const { consensus } = finding;
7
+ const lines = [
8
+ `### ${index + 1}. ${severityEmoji(finding.severity)} [${finding.severity.toUpperCase()}] ${finding.title}`,
9
+ '',
10
+ `**File:** \`${finding.file}\` (lines ${finding.startLine}–${finding.endLine})`,
11
+ `**Category:** ${finding.category}`,
12
+ `**Confidence:** ${consensus.confidenceLabel} (${(consensus.confidence * 100).toFixed(0)}%)`,
13
+ '',
14
+ finding.description,
15
+ ];
16
+ if (finding.suggestedFix) {
17
+ lines.push('', '**Suggested Fix:**', '', '```', finding.suggestedFix, '```');
18
+ }
19
+ lines.push('', `> Flagged by: ${consensus.roles.join(', ')} on ${consensus.models.join(', ')}` +
20
+ (consensus.elevated
21
+ ? ` | Elevated from \`${consensus.original_severity}\` (${consensus.elevation})`
22
+ : '') +
23
+ (consensus.disputed ? ' | ⚠ Disputed' : ''));
24
+ return lines.join('\n');
25
+ }
26
+ export function toMarkdown(result) {
27
+ const { stats } = result;
28
+ const sections = [
29
+ '# Review Council Report',
30
+ '',
31
+ `**Completed:** ${stats.successfulReviews}/${stats.totalReviews} reviewers Ā· ` +
32
+ `**${stats.totalDeduped}** unique findings (${stats.totalRawFindings} raw) Ā· ` +
33
+ `${(stats.durationMs / 1000).toFixed(1)}s`,
34
+ '',
35
+ ];
36
+ // Summary counts
37
+ const bySeverity = {
38
+ critical: [],
39
+ important: [],
40
+ minor: [],
41
+ nitpick: [],
42
+ };
43
+ for (const f of result.findings) {
44
+ bySeverity[f.severity]?.push(f);
45
+ }
46
+ sections.push('## Summary', '');
47
+ sections.push('| Severity | Count |');
48
+ sections.push('|:---------|------:|');
49
+ for (const [sev, findings] of Object.entries(bySeverity)) {
50
+ sections.push(`| ${severityEmoji(sev)} ${sev} | ${findings.length} |`);
51
+ }
52
+ sections.push('');
53
+ // Reviewers table
54
+ sections.push('## Reviewers', '');
55
+ sections.push('| Model | Role | Status | Findings | Duration |');
56
+ sections.push('|-------|------|--------|----------|----------|');
57
+ for (const review of result.reviews) {
58
+ const status = review.status === 'success' ? 'āœ…' : review.status === 'timeout' ? 'ā±ļø' : 'āŒ';
59
+ sections.push(`| ${review.model} | ${review.role} | ${status} | ${review.findings.length} | ${(review.durationMs / 1000).toFixed(1)}s |`);
60
+ }
61
+ sections.push('');
62
+ // Findings by severity
63
+ for (const severity of ['critical', 'important', 'minor', 'nitpick']) {
64
+ const findings = bySeverity[severity];
65
+ if (findings.length === 0)
66
+ continue;
67
+ sections.push(`## ${severityEmoji(severity)} ${severity.charAt(0).toUpperCase() + severity.slice(1)} (${findings.length})`, '');
68
+ findings.forEach((f, i) => {
69
+ sections.push(buildFindingSection(f, i));
70
+ sections.push('');
71
+ });
72
+ }
73
+ if (result.findings.length === 0) {
74
+ sections.push('## āœ… No Issues Found', '', 'All reviewers returned clean results.');
75
+ }
76
+ return sections.join('\n');
77
+ }
78
+ export async function writeMarkdownOutput(result, path) {
79
+ await writeFile(path, toMarkdown(result), 'utf-8');
80
+ }
81
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/output/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ReviewResult } from '../consensus/types.js';
2
+ export declare function printReviewSummary(result: ReviewResult): void;
@@ -0,0 +1,141 @@
1
+ import chalk from 'chalk';
2
+ const SEVERITY_COLORS = {
3
+ critical: chalk.bgRed.white.bold,
4
+ important: chalk.yellow.bold,
5
+ minor: chalk.cyan,
6
+ nitpick: chalk.gray,
7
+ };
8
+ const SEVERITY_ICONS = {
9
+ critical: 'šŸ”“',
10
+ important: '🟔',
11
+ minor: 'šŸ”µ',
12
+ nitpick: '⚪',
13
+ };
14
+ const CONFIDENCE_COLORS = {
15
+ 'Very High': chalk.green.bold,
16
+ High: chalk.green,
17
+ Medium: chalk.yellow,
18
+ Low: chalk.gray,
19
+ Minimal: chalk.dim,
20
+ };
21
+ function confidenceBar(confidence) {
22
+ const bars = 10;
23
+ const filled = Math.round(confidence * bars);
24
+ const empty = bars - filled;
25
+ return chalk.green('ā–ˆ'.repeat(filled)) + chalk.gray('ā–‘'.repeat(empty));
26
+ }
27
+ function severityBadge(severity) {
28
+ const color = SEVERITY_COLORS[severity];
29
+ return color(` ${severity.toUpperCase()} `);
30
+ }
31
+ function printDivider(width = 80) {
32
+ console.log(chalk.dim('─'.repeat(width)));
33
+ }
34
+ function printHeader(text, width = 80) {
35
+ const padded = ` ${text} `;
36
+ const leftPad = Math.floor((width - padded.length) / 2);
37
+ const rightPad = width - padded.length - leftPad;
38
+ console.log(chalk.bold('═'.repeat(leftPad) + padded + '═'.repeat(rightPad)));
39
+ }
40
+ export function printReviewSummary(result) {
41
+ console.log('');
42
+ printHeader('REVIEW COUNCIL RESULTS');
43
+ console.log('');
44
+ const { stats } = result;
45
+ console.log(chalk.bold('Reviews completed: ') +
46
+ `${stats.successfulReviews}/${stats.totalReviews} ` +
47
+ chalk.dim(`(${(stats.durationMs / 1000).toFixed(1)}s)`));
48
+ console.log(chalk.bold('Raw findings: ') +
49
+ stats.totalRawFindings +
50
+ chalk.dim(` → deduped to ${stats.totalDeduped}`));
51
+ console.log('');
52
+ // Group by severity
53
+ const bySeverity = {
54
+ critical: [],
55
+ important: [],
56
+ minor: [],
57
+ nitpick: [],
58
+ };
59
+ for (const f of result.findings) {
60
+ bySeverity[f.severity]?.push(f);
61
+ }
62
+ for (const severity of ['critical', 'important', 'minor', 'nitpick']) {
63
+ const findings = bySeverity[severity];
64
+ if (findings.length === 0)
65
+ continue;
66
+ printDivider();
67
+ console.log(SEVERITY_ICONS[severity] + ' ' +
68
+ severityBadge(severity) +
69
+ chalk.bold(` Ɨ ${findings.length}`));
70
+ console.log('');
71
+ for (const finding of findings) {
72
+ printFinding(finding);
73
+ }
74
+ }
75
+ if (result.findings.length === 0) {
76
+ printDivider();
77
+ console.log(chalk.green.bold('\nāœ“ No issues found across all reviewers.\n'));
78
+ }
79
+ printDivider();
80
+ printReviewerSummary(result);
81
+ console.log('');
82
+ }
83
+ function printFinding(finding) {
84
+ const { consensus } = finding;
85
+ // Title line
86
+ console.log(chalk.bold(` ${finding.file}`) +
87
+ chalk.dim(`:${finding.startLine}`) +
88
+ (finding.endLine !== finding.startLine ? chalk.dim(`-${finding.endLine}`) : ''));
89
+ console.log(` ${chalk.bold(finding.title)}`);
90
+ console.log('');
91
+ // Description
92
+ const descLines = finding.description.split('\n');
93
+ for (const line of descLines) {
94
+ console.log(` ${chalk.white(line)}`);
95
+ }
96
+ console.log('');
97
+ // Suggested fix
98
+ if (finding.suggestedFix) {
99
+ console.log(` ${chalk.dim('Suggested fix:')}`);
100
+ const fixLines = finding.suggestedFix.split('\n');
101
+ for (const line of fixLines) {
102
+ console.log(` ${chalk.dim(line)}`);
103
+ }
104
+ console.log('');
105
+ }
106
+ // Consensus metadata
107
+ const confColor = CONFIDENCE_COLORS[consensus.confidenceLabel];
108
+ console.log(` ${chalk.dim('Confidence:')} ${confColor(consensus.confidenceLabel)} ` +
109
+ confidenceBar(consensus.confidence) +
110
+ chalk.dim(` (${(consensus.confidence * 100).toFixed(0)}%)`));
111
+ if (consensus.elevated && consensus.original_severity) {
112
+ console.log(` ${chalk.dim('Elevated from:')} ${chalk.yellow(consensus.original_severity)} ` +
113
+ chalk.dim(`[${consensus.elevation}]`));
114
+ }
115
+ console.log(` ${chalk.dim('Flagged by:')} ${consensus.roles.map((r) => chalk.cyan(r)).join(', ')}`);
116
+ if (consensus.crossModel) {
117
+ console.log(` ${chalk.dim('Cross-model agreement:')} ${consensus.models.join(', ')}`);
118
+ }
119
+ if (consensus.disputed) {
120
+ console.log(` ${chalk.red('⚠ Disputed:')} ${chalk.dim(consensus.disputeDetails ?? '')}`);
121
+ }
122
+ console.log('');
123
+ }
124
+ function printReviewerSummary(result) {
125
+ console.log(chalk.bold('\nReviewer Summary:'));
126
+ console.log('');
127
+ for (const review of result.reviews) {
128
+ const statusIcon = review.status === 'success'
129
+ ? chalk.green('āœ“')
130
+ : review.status === 'timeout'
131
+ ? chalk.yellow('ā±')
132
+ : chalk.red('āœ—');
133
+ const findingsStr = review.status === 'success'
134
+ ? chalk.dim(`${review.findings.length} findings`)
135
+ : chalk.red(review.error ?? review.status);
136
+ console.log(` ${statusIcon} ${chalk.bold(review.model)} ${chalk.dim('/')} ${chalk.cyan(review.role)} ` +
137
+ chalk.dim(`(${(review.durationMs / 1000).toFixed(1)}s)`) +
138
+ ` — ${findingsStr}`);
139
+ }
140
+ }
141
+ //# sourceMappingURL=terminal.js.map