@simonren/quorum 0.7.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/commands/multi-consult.md +109 -0
  4. package/commands/multi-review.md +139 -0
  5. package/dist/adapters/base.d.ts +120 -0
  6. package/dist/adapters/base.js +98 -0
  7. package/dist/adapters/claude.d.ts +25 -0
  8. package/dist/adapters/claude.js +217 -0
  9. package/dist/adapters/codex.d.ts +20 -0
  10. package/dist/adapters/codex.js +227 -0
  11. package/dist/adapters/gemini.d.ts +20 -0
  12. package/dist/adapters/gemini.js +197 -0
  13. package/dist/adapters/index.d.ts +12 -0
  14. package/dist/adapters/index.js +15 -0
  15. package/dist/cli/check.d.ts +20 -0
  16. package/dist/cli/check.js +78 -0
  17. package/dist/cli/codex.d.ts +11 -0
  18. package/dist/cli/codex.js +255 -0
  19. package/dist/cli/gemini.d.ts +12 -0
  20. package/dist/cli/gemini.js +253 -0
  21. package/dist/commands.d.ts +28 -0
  22. package/dist/commands.js +105 -0
  23. package/dist/config.d.ts +244 -0
  24. package/dist/config.js +179 -0
  25. package/dist/consult-prompt.d.ts +10 -0
  26. package/dist/consult-prompt.js +72 -0
  27. package/dist/context.d.ts +1538 -0
  28. package/dist/context.js +383 -0
  29. package/dist/decoders/claude.d.ts +53 -0
  30. package/dist/decoders/claude.js +106 -0
  31. package/dist/decoders/codex.d.ts +71 -0
  32. package/dist/decoders/codex.js +145 -0
  33. package/dist/decoders/gemini.d.ts +33 -0
  34. package/dist/decoders/gemini.js +58 -0
  35. package/dist/decoders/index.d.ts +6 -0
  36. package/dist/decoders/index.js +3 -0
  37. package/dist/errors.d.ts +46 -0
  38. package/dist/errors.js +192 -0
  39. package/dist/executor.d.ts +103 -0
  40. package/dist/executor.js +244 -0
  41. package/dist/handoff.d.ts +270 -0
  42. package/dist/handoff.js +599 -0
  43. package/dist/index.d.ts +18 -0
  44. package/dist/index.js +134 -0
  45. package/dist/pipeline.d.ts +135 -0
  46. package/dist/pipeline.js +462 -0
  47. package/dist/prompt-v2.d.ts +38 -0
  48. package/dist/prompt-v2.js +391 -0
  49. package/dist/prompt.d.ts +71 -0
  50. package/dist/prompt.js +309 -0
  51. package/dist/schema.d.ts +660 -0
  52. package/dist/schema.js +536 -0
  53. package/dist/tools/consult.d.ts +104 -0
  54. package/dist/tools/consult.js +220 -0
  55. package/dist/tools/feedback.d.ts +91 -0
  56. package/dist/tools/feedback.js +117 -0
  57. package/dist/types.d.ts +105 -0
  58. package/dist/types.js +31 -0
  59. package/package.json +54 -0
package/dist/index.js ADDED
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Quorum MCP Server
4
+ *
5
+ * Provides tools for getting second-opinion reviews from external AI CLIs
6
+ * (Codex and Gemini) on Claude Code's work.
7
+ *
8
+ * Features:
9
+ * - Single model review (codex_review, gemini_review)
10
+ * - Multi-model parallel review (multi_review)
11
+ * - Structured JSON output with confidence scores
12
+ * - Expert role specialization per focus area
13
+ *
14
+ * Usage:
15
+ * - npx -y @simonren/quorum # Run MCP server (normal usage)
16
+ * - npx -y @simonren/quorum update # Install/update slash commands
17
+ */
18
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
19
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
21
+ import { handleMultiReview, ReviewInputSchema, TOOL_DEFINITIONS, } from './tools/feedback.js';
22
+ import { handleMultiConsult, ConsultInputSchema, MULTI_CONSULT_TOOL_DEFINITION, } from './tools/consult.js';
23
+ import { logCliStatus } from './cli/check.js';
24
+ import { installCommands } from './commands.js';
25
+ import { initConfig } from './config.js';
26
+ // Read version from package.json
27
+ import { readFileSync } from 'fs';
28
+ import { join, dirname } from 'path';
29
+ import { fileURLToPath } from 'url';
30
+ const __pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
31
+ const __pkg = JSON.parse(readFileSync(__pkgPath, 'utf-8'));
32
+ const VERSION = __pkg.version;
33
+ // Handle subcommands
34
+ const subcommand = process.argv[2];
35
+ if (subcommand === 'update' || subcommand === '--setup' || subcommand === '--commands') {
36
+ const result = installCommands();
37
+ if (result.success) {
38
+ console.log(`quorum v${VERSION}`);
39
+ if (result.removed.length > 0) {
40
+ console.log(`✓ Removed ${result.removed.length} deprecated commands: ${result.removed.map(c => `/${c}`).join(', ')}`);
41
+ }
42
+ console.log(`✓ Installed ${result.installed.length} slash commands: ${result.installed.map(c => `/${c}`).join(', ')}`);
43
+ process.exit(0);
44
+ }
45
+ else {
46
+ console.error(`✗ Failed to install commands: ${result.error}`);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ // Import adapters to register them
51
+ import './adapters/index.js';
52
+ // Create the MCP server
53
+ const server = new Server({
54
+ name: 'quorum',
55
+ version: VERSION,
56
+ }, {
57
+ capabilities: {
58
+ tools: {},
59
+ },
60
+ });
61
+ // Handle tool listing
62
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
63
+ return {
64
+ tools: [
65
+ TOOL_DEFINITIONS.multi_review,
66
+ MULTI_CONSULT_TOOL_DEFINITION,
67
+ ],
68
+ };
69
+ });
70
+ // Handle tool calls
71
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
72
+ const { name, arguments: args } = request.params;
73
+ try {
74
+ switch (name) {
75
+ case 'multi_review': {
76
+ const input = ReviewInputSchema.parse(args);
77
+ return await handleMultiReview(input);
78
+ }
79
+ case 'multi_consult': {
80
+ const input = ConsultInputSchema.parse(args);
81
+ return await handleMultiConsult(input);
82
+ }
83
+ default:
84
+ return {
85
+ content: [{
86
+ type: 'text',
87
+ text: `Unknown tool: ${name}`
88
+ }],
89
+ isError: true
90
+ };
91
+ }
92
+ }
93
+ catch (error) {
94
+ const errorMessage = error instanceof Error ? error.message : String(error);
95
+ return {
96
+ content: [{
97
+ type: 'text',
98
+ text: `Error: ${errorMessage}`
99
+ }],
100
+ isError: true
101
+ };
102
+ }
103
+ });
104
+ // Start the server
105
+ async function main() {
106
+ // Initialize config (writes defaults to ~/.config/quorum/config.json on first run)
107
+ try {
108
+ const cfg = initConfig();
109
+ console.error(cfg.created
110
+ ? `[quorum] Initialized config at ${cfg.path}`
111
+ : `[quorum] Loaded config from ${cfg.path}`);
112
+ }
113
+ catch (error) {
114
+ const msg = error instanceof Error ? error.message : String(error);
115
+ console.error(`[quorum] Warning: Could not initialize config: ${msg}`);
116
+ }
117
+ // Auto-install slash commands
118
+ const result = installCommands();
119
+ if (result.success) {
120
+ console.error(`[quorum] Installed ${result.installed.length} slash commands`);
121
+ }
122
+ else {
123
+ console.error(`[quorum] Warning: Could not install commands: ${result.error}`);
124
+ }
125
+ // Log CLI availability status on startup
126
+ await logCliStatus();
127
+ const transport = new StdioServerTransport();
128
+ await server.connect(transport);
129
+ console.error('Quorum MCP server running on stdio');
130
+ }
131
+ main().catch((error) => {
132
+ console.error('Fatal error:', error);
133
+ process.exit(1);
134
+ });
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Review Response Processing Pipeline
3
+ *
4
+ * Processes reviewer output through multiple stages:
5
+ * 1. Parse - Extract structured data
6
+ * 2. Verify - Check file/line references exist
7
+ * 3. Cross-check - Compare with CC's knowledge
8
+ * 4. Prioritize - Rank by impact and confidence
9
+ * 5. Plan - Generate actionable next steps
10
+ */
11
+ import { ReviewOutput, ReviewFinding } from './schema.js';
12
+ import { ReviewContext, VerificationData } from './context.js';
13
+ export interface PipelineStage<TInput, TOutput> {
14
+ name: string;
15
+ process(input: TInput, context: PipelineContext): Promise<TOutput>;
16
+ }
17
+ export interface PipelineContext {
18
+ workingDir: string;
19
+ reviewContext: ReviewContext;
20
+ verificationData?: VerificationData;
21
+ }
22
+ export interface VerifiedFinding extends ReviewFinding {
23
+ verification: {
24
+ fileExists: boolean;
25
+ lineValid: boolean;
26
+ codeSnippetMatches?: boolean;
27
+ verificationNotes?: string;
28
+ };
29
+ crossCheck: {
30
+ alreadyAddressedByCC: boolean;
31
+ conflictsWithCC: boolean;
32
+ ccMentioned: boolean;
33
+ };
34
+ adjustedConfidence: number;
35
+ }
36
+ export interface ActionItem {
37
+ finding: VerifiedFinding;
38
+ action: 'fix_now' | 'investigate' | 'defer' | 'reject';
39
+ priority: number;
40
+ suggestedFix?: string;
41
+ reason: string;
42
+ }
43
+ export interface ProcessedReview {
44
+ original: ReviewOutput;
45
+ verified: VerifiedFinding[];
46
+ rejected: {
47
+ finding: ReviewFinding;
48
+ reason: string;
49
+ }[];
50
+ actionPlan: ActionItem[];
51
+ summary: {
52
+ totalFindings: number;
53
+ verifiedCount: number;
54
+ rejectedCount: number;
55
+ actionableCount: number;
56
+ topPriority: ActionItem[];
57
+ };
58
+ }
59
+ /**
60
+ * Simple file cache to avoid re-reading files for each finding
61
+ */
62
+ export declare class FileCache {
63
+ private workingDir;
64
+ private contentCache;
65
+ private lineCountCache;
66
+ private linesCache;
67
+ constructor(workingDir: string);
68
+ /**
69
+ * Check if file exists (cached)
70
+ */
71
+ exists(relativePath: string): boolean;
72
+ /**
73
+ * Get file content (cached, lazy-loaded)
74
+ */
75
+ getContent(relativePath: string): string | null;
76
+ /**
77
+ * Get lines array (cached)
78
+ */
79
+ getLines(relativePath: string): string[] | null;
80
+ /**
81
+ * Get line count (cached)
82
+ */
83
+ getLineCount(relativePath: string): number | null;
84
+ /**
85
+ * Get stats about cache usage
86
+ */
87
+ getStats(): {
88
+ filesChecked: number;
89
+ filesLoaded: number;
90
+ };
91
+ }
92
+ /**
93
+ * Build verification data by scanning the filesystem
94
+ */
95
+ export declare function buildVerificationData(workingDir: string): Promise<VerificationData>;
96
+ /**
97
+ * Verify a single finding's references
98
+ * @param finding The finding to verify
99
+ * @param workingDir Working directory for path resolution
100
+ * @param cache Optional file cache for performance (recommended for multiple findings)
101
+ */
102
+ export declare function verifyFinding(finding: ReviewFinding, workingDir: string, cache?: FileCache): Promise<VerifiedFinding>;
103
+ /**
104
+ * Cross-check findings against CC's analysis
105
+ */
106
+ export declare function crossCheckWithCC(finding: VerifiedFinding, ccAnalysis: ReviewContext['analysis']): VerifiedFinding;
107
+ /**
108
+ * Calculate priority score for a finding
109
+ */
110
+ export declare function calculatePriority(finding: VerifiedFinding): number;
111
+ /**
112
+ * Determine action for a finding
113
+ */
114
+ export declare function determineAction(finding: VerifiedFinding, priority: number): {
115
+ action: ActionItem['action'];
116
+ reason: string;
117
+ };
118
+ /**
119
+ * Process a review output through the full verification pipeline
120
+ */
121
+ export declare function processReviewOutput(output: ReviewOutput, context: ReviewContext): Promise<ProcessedReview>;
122
+ /**
123
+ * Format processed review for display
124
+ */
125
+ export declare function formatProcessedReview(processed: ProcessedReview): string;
126
+ export interface FollowUpQuestion {
127
+ topic: string;
128
+ question: string;
129
+ relatedFindings: string[];
130
+ context: string;
131
+ }
132
+ /**
133
+ * Generate follow-up questions for uncertain findings
134
+ */
135
+ export declare function generateFollowUpQuestions(processed: ProcessedReview): FollowUpQuestion[];