@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.
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/commands/multi-consult.md +109 -0
- package/commands/multi-review.md +139 -0
- package/dist/adapters/base.d.ts +120 -0
- package/dist/adapters/base.js +98 -0
- package/dist/adapters/claude.d.ts +25 -0
- package/dist/adapters/claude.js +217 -0
- package/dist/adapters/codex.d.ts +20 -0
- package/dist/adapters/codex.js +227 -0
- package/dist/adapters/gemini.d.ts +20 -0
- package/dist/adapters/gemini.js +197 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.js +15 -0
- package/dist/cli/check.d.ts +20 -0
- package/dist/cli/check.js +78 -0
- package/dist/cli/codex.d.ts +11 -0
- package/dist/cli/codex.js +255 -0
- package/dist/cli/gemini.d.ts +12 -0
- package/dist/cli/gemini.js +253 -0
- package/dist/commands.d.ts +28 -0
- package/dist/commands.js +105 -0
- package/dist/config.d.ts +244 -0
- package/dist/config.js +179 -0
- package/dist/consult-prompt.d.ts +10 -0
- package/dist/consult-prompt.js +72 -0
- package/dist/context.d.ts +1538 -0
- package/dist/context.js +383 -0
- package/dist/decoders/claude.d.ts +53 -0
- package/dist/decoders/claude.js +106 -0
- package/dist/decoders/codex.d.ts +71 -0
- package/dist/decoders/codex.js +145 -0
- package/dist/decoders/gemini.d.ts +33 -0
- package/dist/decoders/gemini.js +58 -0
- package/dist/decoders/index.d.ts +6 -0
- package/dist/decoders/index.js +3 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.js +192 -0
- package/dist/executor.d.ts +103 -0
- package/dist/executor.js +244 -0
- package/dist/handoff.d.ts +270 -0
- package/dist/handoff.js +599 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +134 -0
- package/dist/pipeline.d.ts +135 -0
- package/dist/pipeline.js +462 -0
- package/dist/prompt-v2.d.ts +38 -0
- package/dist/prompt-v2.js +391 -0
- package/dist/prompt.d.ts +71 -0
- package/dist/prompt.js +309 -0
- package/dist/schema.d.ts +660 -0
- package/dist/schema.js +536 -0
- package/dist/tools/consult.d.ts +104 -0
- package/dist/tools/consult.js +220 -0
- package/dist/tools/feedback.d.ts +91 -0
- package/dist/tools/feedback.js +117 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.js +31 -0
- 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[];
|