@rigour-labs/mcp 4.3.3 → 4.3.5
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/dist/index.js
CHANGED
|
@@ -102,7 +102,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
102
102
|
break;
|
|
103
103
|
case "rigour_run_supervised": {
|
|
104
104
|
const { command, maxRetries = 3, dryRun = false } = args;
|
|
105
|
-
result = await handleRunSupervised(runner, cwd, command, maxRetries, dryRun, requestId);
|
|
105
|
+
result = await handleRunSupervised(runner, cwd, command, maxRetries, dryRun, requestId, config);
|
|
106
106
|
break;
|
|
107
107
|
}
|
|
108
108
|
// Multi-agent governance
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GateRunner } from "@rigour-labs/core";
|
|
2
|
+
import type { Config } from "@rigour-labs/core";
|
|
2
3
|
type ToolResult = {
|
|
3
4
|
content: {
|
|
4
5
|
type: string;
|
|
@@ -7,5 +8,5 @@ type ToolResult = {
|
|
|
7
8
|
isError?: boolean;
|
|
8
9
|
};
|
|
9
10
|
export declare function handleRun(cwd: string, command: string, requestId: string): Promise<ToolResult>;
|
|
10
|
-
export declare function handleRunSupervised(runner: GateRunner, cwd: string, command: string, maxRetries: number, dryRun: boolean, requestId: string): Promise<ToolResult>;
|
|
11
|
+
export declare function handleRunSupervised(runner: GateRunner, cwd: string, command: string, maxRetries: number, dryRun: boolean, requestId: string, config?: Config): Promise<ToolResult>;
|
|
11
12
|
export {};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from "fs-extra";
|
|
9
9
|
import path from "path";
|
|
10
|
+
import { FixPacketService } from "@rigour-labs/core";
|
|
10
11
|
import { logStudioEvent } from '../utils/config.js';
|
|
11
12
|
export async function handleRun(cwd, command, requestId) {
|
|
12
13
|
// 1. Log Interceptable Event
|
|
@@ -40,7 +41,7 @@ export async function handleRun(cwd, command, requestId) {
|
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
export async function handleRunSupervised(runner, cwd, command, maxRetries, dryRun, requestId) {
|
|
44
|
+
export async function handleRunSupervised(runner, cwd, command, maxRetries, dryRun, requestId, config) {
|
|
44
45
|
const { execa } = await import("execa");
|
|
45
46
|
let iteration = 0;
|
|
46
47
|
let lastReport = null;
|
|
@@ -67,39 +68,50 @@ export async function handleRunSupervised(runner, cwd, command, maxRetries, dryR
|
|
|
67
68
|
console.error(`[RIGOUR] Iteration ${iteration} (DRY RUN - skipping command execution)`);
|
|
68
69
|
}
|
|
69
70
|
lastReport = await runner.run(cwd);
|
|
70
|
-
|
|
71
|
+
const failedGateIds = [...new Set(lastReport.failures.map(f => f.id))];
|
|
72
|
+
iterations.push({
|
|
73
|
+
iteration,
|
|
74
|
+
status: lastReport.status,
|
|
75
|
+
failures: lastReport.failures.length,
|
|
76
|
+
failedGates: failedGateIds,
|
|
77
|
+
});
|
|
71
78
|
await logStudioEvent(cwd, {
|
|
72
79
|
type: "supervisor_iteration",
|
|
73
80
|
requestId,
|
|
74
81
|
iteration,
|
|
75
82
|
status: lastReport.status,
|
|
76
83
|
failures: lastReport.failures.length,
|
|
84
|
+
failedGates: failedGateIds,
|
|
77
85
|
});
|
|
78
86
|
if (lastReport.status === "PASS") {
|
|
87
|
+
const score = lastReport.stats.score !== undefined ? ` | Score: ${lastReport.stats.score}/100` : '';
|
|
79
88
|
result = {
|
|
80
89
|
content: [{
|
|
81
90
|
type: "text",
|
|
82
|
-
text: `✅ SUPERVISOR MODE: PASSED on iteration ${iteration}/${maxRetries}\n\nIterations:\n${iterations
|
|
91
|
+
text: `✅ SUPERVISOR MODE: PASSED on iteration ${iteration}/${maxRetries}${score}\n\nIterations:\n${formatIterationHistory(iterations)}\n\nAll quality gates have been satisfied.`,
|
|
83
92
|
}],
|
|
84
93
|
};
|
|
85
94
|
break;
|
|
86
95
|
}
|
|
87
96
|
if (iteration >= maxRetries) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
// Use FixPacketService for structured output instead of ad-hoc formatting
|
|
98
|
+
let fixPacketText;
|
|
99
|
+
if (config) {
|
|
100
|
+
const fixPacketService = new FixPacketService();
|
|
101
|
+
const fixPacket = fixPacketService.generate(lastReport, config);
|
|
102
|
+
fixPacketText = formatFixPacketForSupervisor(fixPacket);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Fallback if config not available
|
|
106
|
+
fixPacketText = lastReport.failures.map((f, i) => {
|
|
107
|
+
const sevTag = `[${(f.severity || 'medium').toUpperCase()}]`;
|
|
108
|
+
return `${i + 1}. ${sevTag} [${f.id}] ${f.title}: ${f.details}${f.files?.length ? ` (${f.files.join(', ')})` : ''}`;
|
|
109
|
+
}).join('\n');
|
|
110
|
+
}
|
|
99
111
|
result = {
|
|
100
112
|
content: [{
|
|
101
113
|
type: "text",
|
|
102
|
-
text: `❌ SUPERVISOR MODE: FAILED after ${iteration} iterations\n\nIterations:\n${iterations
|
|
114
|
+
text: `❌ SUPERVISOR MODE: FAILED after ${iteration} iterations\n\nIterations:\n${formatIterationHistory(iterations)}\n\n${fixPacketText}`,
|
|
103
115
|
}],
|
|
104
116
|
isError: true,
|
|
105
117
|
};
|
|
@@ -113,6 +125,44 @@ export async function handleRunSupervised(runner, cwd, command, maxRetries, dryR
|
|
|
113
125
|
});
|
|
114
126
|
return result;
|
|
115
127
|
}
|
|
128
|
+
function formatIterationHistory(iterations) {
|
|
129
|
+
return iterations.map(i => {
|
|
130
|
+
const gates = i.failedGates.length > 0 ? ` [${i.failedGates.join(', ')}]` : '';
|
|
131
|
+
return ` ${i.iteration}. ${i.status} (${i.failures} failures)${gates}`;
|
|
132
|
+
}).join('\n');
|
|
133
|
+
}
|
|
134
|
+
function formatFixPacketForSupervisor(fixPacket) {
|
|
135
|
+
const lines = [];
|
|
136
|
+
lines.push(`FINAL FIX PACKET (${fixPacket.violations.length} violations across gates: ${fixPacket.failed_gates.join(', ')})`);
|
|
137
|
+
lines.push('');
|
|
138
|
+
fixPacket.violations.forEach((v, i) => {
|
|
139
|
+
const sevTag = `[${(v.severity || 'medium').toUpperCase()}]`;
|
|
140
|
+
lines.push(`${i + 1}. ${sevTag} [${v.id}] ${v.title}`);
|
|
141
|
+
lines.push(` PROBLEM: ${v.details}`);
|
|
142
|
+
if (v.locations?.length > 0) {
|
|
143
|
+
const locs = v.locations.map((l) => l.line ? `${l.file}:${l.line}` : l.file);
|
|
144
|
+
lines.push(` WHERE: ${locs.join(', ')}`);
|
|
145
|
+
}
|
|
146
|
+
else if (v.files?.length > 0) {
|
|
147
|
+
lines.push(` FILES: ${v.files.join(', ')}`);
|
|
148
|
+
}
|
|
149
|
+
if (v.instructions?.length > 0) {
|
|
150
|
+
lines.push(` FIX: ${v.instructions[0]}`);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (fixPacket.verification?.commands?.length > 0) {
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('VERIFICATION COMMANDS (run after fixing):');
|
|
156
|
+
fixPacket.verification.commands.forEach((c) => {
|
|
157
|
+
lines.push(` $ ${c.cmd} — ${c.purpose}`);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (fixPacket.constraints?.allowed_scope?.length > 0) {
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push(`ALLOWED SCOPE: ${fixPacket.constraints.allowed_scope.join(', ')}`);
|
|
163
|
+
}
|
|
164
|
+
return lines.join('\n');
|
|
165
|
+
}
|
|
116
166
|
// ─── Private Helpers ──────────────────────────────────────────────
|
|
117
167
|
async function pollArbitration(cwd, rid, timeout) {
|
|
118
168
|
const start = Date.now();
|
|
@@ -108,27 +108,92 @@ export async function handleGetFixPacket(runner, cwd, config) {
|
|
|
108
108
|
const { FixPacketService } = await import("@rigour-labs/core");
|
|
109
109
|
const fixPacketService = new FixPacketService();
|
|
110
110
|
const fixPacket = fixPacketService.generate(report, config);
|
|
111
|
-
const packet = fixPacket.violations.map((v, i) => {
|
|
112
|
-
const sevTag = `[${(v.severity || 'medium').toUpperCase()}]`;
|
|
113
|
-
const catTag = v.category ? `(${v.category})` : '';
|
|
114
|
-
let text = `FIX TASK ${i + 1}: ${sevTag} ${catTag} [${v.id.toUpperCase()}] ${v.title}\n`;
|
|
115
|
-
text += ` - CONTEXT: ${v.details}\n`;
|
|
116
|
-
if (v.files?.length > 0)
|
|
117
|
-
text += ` - TARGET FILES: ${v.files.join(", ")}\n`;
|
|
118
|
-
if (v.hint)
|
|
119
|
-
text += ` - REFACTORING GUIDANCE: ${v.hint}\n`;
|
|
120
|
-
return text;
|
|
121
|
-
}).join("\n---\n");
|
|
122
|
-
let scoreHeader = formatScoreText(report.stats).trim();
|
|
123
|
-
if (scoreHeader)
|
|
124
|
-
scoreHeader += '\n';
|
|
125
111
|
return {
|
|
126
|
-
content: [{
|
|
127
|
-
type: "text",
|
|
128
|
-
text: `ENGINEERING REFINEMENT REQUIRED:\n${scoreHeader}\nThe project state violated ${report.failures.length} quality gates. You MUST address these failures before declaring the task complete (critical issues first):\n\n${packet}`,
|
|
129
|
-
}],
|
|
112
|
+
content: [{ type: "text", text: formatFixPacketText(fixPacket, report) }],
|
|
130
113
|
};
|
|
131
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Formats a FixPacketV2 into a structured, agent-readable text block.
|
|
117
|
+
* Every violation includes: who failed, which files, what rule, where (line numbers),
|
|
118
|
+
* and what commands must pass after fixing.
|
|
119
|
+
*/
|
|
120
|
+
function formatFixPacketText(fixPacket, report) {
|
|
121
|
+
const lines = [];
|
|
122
|
+
// Header
|
|
123
|
+
let scoreHeader = formatScoreText(report.stats).trim();
|
|
124
|
+
lines.push('ENGINEERING REFINEMENT REQUIRED');
|
|
125
|
+
if (scoreHeader)
|
|
126
|
+
lines.push(scoreHeader);
|
|
127
|
+
lines.push(`Violations: ${report.failures.length} | Failed gates: ${fixPacket.failed_gates.join(', ')}`);
|
|
128
|
+
lines.push('');
|
|
129
|
+
// Violations
|
|
130
|
+
fixPacket.violations.forEach((v, i) => {
|
|
131
|
+
const sevTag = `[${(v.severity || 'medium').toUpperCase()}]`;
|
|
132
|
+
const catTag = v.category ? ` (${v.category})` : '';
|
|
133
|
+
lines.push(`━━━ FIX ${i + 1}/${fixPacket.violations.length}: ${sevTag}${catTag} ${v.title} ━━━`);
|
|
134
|
+
lines.push(`GATE: ${v.id}`);
|
|
135
|
+
lines.push(`PROBLEM: ${v.details}`);
|
|
136
|
+
// Locations with line numbers (precise targeting)
|
|
137
|
+
if (v.locations && v.locations.length > 0) {
|
|
138
|
+
const locStrs = v.locations.map((loc) => {
|
|
139
|
+
let s = loc.file;
|
|
140
|
+
if (loc.line)
|
|
141
|
+
s += `:${loc.line}`;
|
|
142
|
+
if (loc.endLine && loc.endLine !== loc.line)
|
|
143
|
+
s += `-${loc.endLine}`;
|
|
144
|
+
return s;
|
|
145
|
+
});
|
|
146
|
+
lines.push(`WHERE: ${locStrs.join(', ')}`);
|
|
147
|
+
}
|
|
148
|
+
else if (v.files && v.files.length > 0) {
|
|
149
|
+
lines.push(`FILES: ${v.files.join(', ')}`);
|
|
150
|
+
}
|
|
151
|
+
// Metrics (thresholds vs actuals)
|
|
152
|
+
if (v.metrics && Object.keys(v.metrics).length > 0) {
|
|
153
|
+
const metricStrs = Object.entries(v.metrics).map(([k, val]) => `${k}=${val}`);
|
|
154
|
+
lines.push(`METRICS: ${metricStrs.join(', ')}`);
|
|
155
|
+
}
|
|
156
|
+
// Instructions
|
|
157
|
+
if (v.instructions && v.instructions.length > 0) {
|
|
158
|
+
lines.push(`FIX:`);
|
|
159
|
+
v.instructions.forEach((inst, j) => {
|
|
160
|
+
lines.push(` ${j + 1}. ${inst}`);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else if (v.hint) {
|
|
164
|
+
lines.push(`HINT: ${v.hint}`);
|
|
165
|
+
}
|
|
166
|
+
lines.push('');
|
|
167
|
+
});
|
|
168
|
+
// Verification commands
|
|
169
|
+
if (fixPacket.verification?.commands?.length > 0) {
|
|
170
|
+
lines.push('━━━ VERIFICATION (run these after fixing) ━━━');
|
|
171
|
+
fixPacket.verification.commands.forEach((c) => {
|
|
172
|
+
lines.push(` $ ${c.cmd} — ${c.purpose}`);
|
|
173
|
+
});
|
|
174
|
+
lines.push('');
|
|
175
|
+
}
|
|
176
|
+
// Constraints
|
|
177
|
+
const c = fixPacket.constraints;
|
|
178
|
+
if (c) {
|
|
179
|
+
const constraintParts = [];
|
|
180
|
+
if (c.allowed_scope?.length > 0)
|
|
181
|
+
constraintParts.push(`ALLOWED SCOPE: ${c.allowed_scope.join(', ')}`);
|
|
182
|
+
if (c.do_not_touch?.length > 0)
|
|
183
|
+
constraintParts.push(`DO NOT TOUCH: ${c.do_not_touch.join(', ')}`);
|
|
184
|
+
if (c.max_files_changed)
|
|
185
|
+
constraintParts.push(`MAX FILES CHANGED: ${c.max_files_changed}`);
|
|
186
|
+
if (c.no_new_deps)
|
|
187
|
+
constraintParts.push('NO NEW DEPENDENCIES');
|
|
188
|
+
if (c.paradigm)
|
|
189
|
+
constraintParts.push(`PARADIGM: ${c.paradigm}`);
|
|
190
|
+
if (constraintParts.length > 0) {
|
|
191
|
+
lines.push('━━━ CONSTRAINTS ━━━');
|
|
192
|
+
constraintParts.forEach(p => lines.push(` ${p}`));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return lines.join('\n');
|
|
196
|
+
}
|
|
132
197
|
export function handleListGates(config) {
|
|
133
198
|
return {
|
|
134
199
|
content: [{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/mcp",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.5",
|
|
4
4
|
"description": "MCP server for AI code governance — OWASP LLM Top 10 (10/10), real-time hooks, 25+ security patterns, hallucinated import detection, multi-agent governance. Works with Claude, Cursor, Cline, Windsurf, Gemini. Industry presets for HIPAA, SOC2, FedRAMP.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://rigour.run",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"execa": "^8.0.1",
|
|
49
49
|
"fs-extra": "^11.2.0",
|
|
50
50
|
"yaml": "^2.8.2",
|
|
51
|
-
"@rigour-labs/core": "4.3.
|
|
51
|
+
"@rigour-labs/core": "4.3.5"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.0.3",
|