@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/prompt.js ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Prompt Builder for AI Review
3
+ *
4
+ * Builds structured prompts that request JSON output from external CLIs.
5
+ * Supports expert roles for specialized reviews.
6
+ */
7
+ import { FOCUS_AREA_DESCRIPTIONS } from './types.js';
8
+ import { getReviewOutputJsonSchema } from './schema.js';
9
+ import { selectExpertRole, EXPERT_ROLES } from './adapters/base.js';
10
+ // =============================================================================
11
+ // JSON SCHEMA PROMPT SECTION
12
+ // =============================================================================
13
+ /**
14
+ * Generate the JSON schema section for the prompt
15
+ */
16
+ function buildJsonSchemaSection() {
17
+ const schema = getReviewOutputJsonSchema();
18
+ return `OUTPUT FORMAT (JSON):
19
+ You MUST respond with valid JSON matching this schema:
20
+ \`\`\`json
21
+ ${JSON.stringify(schema, null, 2)}
22
+ \`\`\`
23
+
24
+ IMPORTANT:
25
+ - Output ONLY the JSON object, no markdown wrapping, no explanation before/after
26
+ - All fields marked "required" MUST be present
27
+ - Use empty arrays [] for sections with no findings
28
+ - Confidence scores are 0-1 (e.g., 0.85 for 85% confident)
29
+ - Severity levels: critical > high > medium > low > info
30
+ - Include file:line in location when you reference specific code`;
31
+ }
32
+ // =============================================================================
33
+ // EXPERT ROLE PROMPT
34
+ // =============================================================================
35
+ /**
36
+ * Build the expert role section of the prompt
37
+ */
38
+ function buildExpertSection(role) {
39
+ return `ROLE: ${role.name}
40
+
41
+ ${role.systemPrompt}
42
+
43
+ EVALUATION CRITERIA:
44
+ ${role.evaluationCriteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}`;
45
+ }
46
+ /**
47
+ * Build the main review prompt
48
+ */
49
+ export function buildReviewPrompt(options) {
50
+ const { request, expertRole, reviewerName, useJsonOutput = true, retryContext, } = options;
51
+ const role = expertRole || selectExpertRole(request.focusAreas);
52
+ const focusDescription = request.focusAreas && request.focusAreas.length > 0
53
+ ? request.focusAreas.map(f => `${f} (${FOCUS_AREA_DESCRIPTIONS[f]})`).join(', ')
54
+ : 'General review';
55
+ const filesAnalyzed = request.analyzedFiles && request.analyzedFiles.length > 0
56
+ ? request.analyzedFiles.join(', ')
57
+ : 'Not specified';
58
+ const sections = [];
59
+ // Section 1: Expert Role
60
+ sections.push(buildExpertSection(role));
61
+ // Section 2: Task Description
62
+ sections.push(`
63
+ ---
64
+
65
+ TASK: Review Claude Code's ${request.outputType} and provide structured feedback.
66
+
67
+ You are reviewing another AI assistant's (Claude Code) analysis of a codebase.
68
+ Your job is to:
69
+ 1. VALIDATE correct findings (agreements)
70
+ 2. CHALLENGE incorrect assessments (disagreements)
71
+ 3. ADD issues they missed (new findings)
72
+ 4. SUGGEST alternatives where applicable
73
+ 5. ASSESS overall risk`);
74
+ // Section 3: Context
75
+ sections.push(`
76
+ ---
77
+
78
+ CONTEXT:
79
+ Working Directory: ${request.workingDir}
80
+ Output Type: ${request.outputType}
81
+ Focus Areas: ${focusDescription}
82
+ Files Analyzed: ${filesAnalyzed}
83
+ Reviewer ID: ${reviewerName}
84
+
85
+ CLAUDE CODE'S OUTPUT TO REVIEW:
86
+ \`\`\`
87
+ ${request.ccOutput}
88
+ \`\`\``);
89
+ // Section 4: Custom Instructions
90
+ if (request.customPrompt) {
91
+ sections.push(`
92
+ ---
93
+
94
+ ADDITIONAL INSTRUCTIONS:
95
+ ${request.customPrompt}`);
96
+ }
97
+ // Section 5: Constraints
98
+ sections.push(`
99
+ ---
100
+
101
+ CONSTRAINTS:
102
+ • You have filesystem access - READ files to verify claims
103
+ • Do NOT modify any files (advisory mode only)
104
+ • Do NOT assume a git repository exists - do NOT run git commands
105
+ • Reference specific file:line when making claims
106
+ • Do NOT hallucinate file paths - verify they exist
107
+ • Be skeptical - verify before agreeing with CC's findings`);
108
+ // Section 6: Output Format
109
+ if (useJsonOutput) {
110
+ sections.push(`
111
+ ---
112
+
113
+ ${buildJsonSchemaSection()}`);
114
+ }
115
+ else {
116
+ // Legacy markdown format for backwards compatibility
117
+ sections.push(`
118
+ ---
119
+
120
+ OUTPUT FORMAT (Markdown):
121
+ ## Agreements
122
+ - [Finding]: [Why correct]
123
+
124
+ ## Disagreements
125
+ - [Finding]: [Why wrong] - [Correct assessment]
126
+
127
+ ## Additions
128
+ - [New finding]: [File:line] - [Impact]
129
+
130
+ ## Alternatives
131
+ - [Topic]: [Alternative] - [Tradeoffs]
132
+
133
+ ## Risk Assessment
134
+ [Low/Medium/High] - [Reason]`);
135
+ }
136
+ // Section 7: Retry Context (if applicable)
137
+ if (retryContext) {
138
+ sections.push(`
139
+ ---
140
+
141
+ ⚠️ RETRY ATTEMPT ${retryContext.attemptNumber}/3
142
+
143
+ Previous attempt failed with: ${retryContext.previousError}
144
+
145
+ Previous output (truncated):
146
+ ${retryContext.previousOutput.slice(0, 500)}...
147
+
148
+ Please ensure your response is valid JSON matching the schema exactly.`);
149
+ }
150
+ return sections.join('\n');
151
+ }
152
+ // =============================================================================
153
+ // PEER REVIEW PROMPT
154
+ // =============================================================================
155
+ /**
156
+ * Build a prompt for peer review (one model reviewing another's output)
157
+ */
158
+ export function buildPeerReviewPrompt(reviewerName, anonymizedReviewerId, reviewToScore, originalCcOutput) {
159
+ return `ROLE: Peer Reviewer
160
+
161
+ You are evaluating another AI reviewer's analysis for accuracy and quality.
162
+
163
+ ---
164
+
165
+ ORIGINAL TASK:
166
+ Claude Code produced this output that was being reviewed:
167
+ \`\`\`
168
+ ${originalCcOutput}
169
+ \`\`\`
170
+
171
+ ---
172
+
173
+ REVIEW TO EVALUATE (from ${anonymizedReviewerId}):
174
+ \`\`\`
175
+ ${reviewToScore}
176
+ \`\`\`
177
+
178
+ ---
179
+
180
+ TASK: Score each finding in the review above.
181
+
182
+ For each finding, assess:
183
+ 1. Is the finding valid? (Does the issue actually exist in the code?)
184
+ 2. Is it correctly categorized and described?
185
+ 3. Is the evidence accurate?
186
+
187
+ OUTPUT FORMAT (JSON):
188
+ {
189
+ "reviewer": "${reviewerName}",
190
+ "reviewed_model": "${anonymizedReviewerId}",
191
+ "scores": [
192
+ {
193
+ "finding_id": "<id from the review>",
194
+ "validity": "valid" | "questionable" | "invalid" | "cannot_assess",
195
+ "confidence": 0.0-1.0,
196
+ "notes": "<optional explanation>"
197
+ }
198
+ ],
199
+ "overall_quality": 0.0-1.0,
200
+ "summary": "<brief assessment of the review quality>"
201
+ }
202
+
203
+ Output ONLY the JSON, no other text.`;
204
+ }
205
+ /**
206
+ * Legacy function - builds old-style 7-section prompt
207
+ * @deprecated Use buildReviewPrompt instead
208
+ */
209
+ export function build7SectionPrompt(request) {
210
+ return buildReviewPrompt({
211
+ request: {
212
+ workingDir: request.workingDir,
213
+ ccOutput: request.ccOutput,
214
+ outputType: request.outputType,
215
+ analyzedFiles: request.analyzedFiles,
216
+ focusAreas: request.focusAreas,
217
+ customPrompt: request.customPrompt,
218
+ },
219
+ reviewerName: 'external',
220
+ useJsonOutput: false, // Legacy uses markdown
221
+ });
222
+ }
223
+ /**
224
+ * Legacy function - builds developer instructions
225
+ * @deprecated Use buildReviewPrompt with expertRole instead
226
+ */
227
+ export function buildDeveloperInstructions(cli) {
228
+ const roleMap = {
229
+ codex: EXPERT_ROLES.correctness_analyst,
230
+ gemini: EXPERT_ROLES.architect,
231
+ };
232
+ return buildExpertSection(roleMap[cli]);
233
+ }
234
+ /**
235
+ * Legacy function - builds retry prompt
236
+ * @deprecated Use buildReviewPrompt with retryContext instead
237
+ */
238
+ export function buildRetryPrompt(request, attemptNumber, previousError, previousOutput) {
239
+ return buildReviewPrompt({
240
+ request: {
241
+ workingDir: request.workingDir,
242
+ ccOutput: request.ccOutput,
243
+ outputType: request.outputType,
244
+ analyzedFiles: request.analyzedFiles,
245
+ focusAreas: request.focusAreas,
246
+ customPrompt: request.customPrompt,
247
+ },
248
+ reviewerName: 'external',
249
+ useJsonOutput: false,
250
+ retryContext: {
251
+ attemptNumber,
252
+ previousError,
253
+ previousOutput,
254
+ },
255
+ });
256
+ }
257
+ /**
258
+ * Validate feedback output structure
259
+ * Now supports both JSON and legacy markdown formats
260
+ */
261
+ export function isValidFeedbackOutput(output) {
262
+ // Try JSON first
263
+ try {
264
+ const jsonMatch = output.match(/```(?:json)?\s*([\s\S]*?)```/) || [null, output];
265
+ let jsonStr = jsonMatch[1] || output;
266
+ const jsonStart = jsonStr.indexOf('{');
267
+ const jsonEnd = jsonStr.lastIndexOf('}');
268
+ if (jsonStart !== -1 && jsonEnd !== -1) {
269
+ jsonStr = jsonStr.slice(jsonStart, jsonEnd + 1);
270
+ const parsed = JSON.parse(jsonStr);
271
+ // Check for required fields
272
+ if (parsed.findings !== undefined &&
273
+ parsed.agreements !== undefined &&
274
+ parsed.disagreements !== undefined &&
275
+ parsed.risk_assessment !== undefined) {
276
+ return true;
277
+ }
278
+ }
279
+ }
280
+ catch {
281
+ // Not valid JSON, try markdown
282
+ }
283
+ // Legacy markdown validation
284
+ const requiredSections = [
285
+ /^## Agreements$/m,
286
+ /^## Disagreements$/m,
287
+ /^## Additions$/m,
288
+ /^## Alternatives$/m,
289
+ /^## Risk Assessment$/m
290
+ ];
291
+ const hasRequiredSections = requiredSections.every(regex => regex.test(output));
292
+ const hasContent = output.length > 100;
293
+ return hasRequiredSections && hasContent;
294
+ }
295
+ /**
296
+ * Detect output type from CC's output content
297
+ */
298
+ export function detectOutputType(ccOutput) {
299
+ if (/\b(vulnerab|injection|XSS|CSRF|SSRF|auth.*issue|security)/i.test(ccOutput)) {
300
+ return 'findings';
301
+ }
302
+ if (/\b(fix|bug|issue|error|proposed fix|solution|patch)/i.test(ccOutput)) {
303
+ return 'proposal';
304
+ }
305
+ if (/\b(architect|design|plan|implement|phase|step \d|structure)/i.test(ccOutput)) {
306
+ return 'plan';
307
+ }
308
+ return 'analysis';
309
+ }