@rigour-labs/mcp 2.10.0 → 2.11.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/dist/index.js +180 -116
- package/dist/smoke.test.js +4 -6
- package/package.json +3 -2
- package/src/index.ts +157 -91
package/dist/index.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const yaml_1 = __importDefault(require("yaml"));
|
|
13
|
-
const core_1 = require("@rigour-labs/core");
|
|
14
|
-
const pattern_index_1 = require("@rigour-labs/core/pattern-index");
|
|
15
|
-
const server = new index_js_1.Server({
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import fs from "fs-extra";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import yaml from "yaml";
|
|
8
|
+
import { randomUUID } from "crypto";
|
|
9
|
+
import { GateRunner, ConfigSchema, } from "@rigour-labs/core";
|
|
10
|
+
import { PatternMatcher, loadPatternIndex, getDefaultIndexPath, StalenessDetector, SecurityDetector } from "@rigour-labs/core/pattern-index";
|
|
11
|
+
const server = new Server({
|
|
16
12
|
name: "rigour-mcp",
|
|
17
13
|
version: "1.0.0",
|
|
18
14
|
}, {
|
|
@@ -21,31 +17,48 @@ const server = new index_js_1.Server({
|
|
|
21
17
|
},
|
|
22
18
|
});
|
|
23
19
|
async function loadConfig(cwd) {
|
|
24
|
-
const configPath =
|
|
25
|
-
if (!(await
|
|
20
|
+
const configPath = path.join(cwd, "rigour.yml");
|
|
21
|
+
if (!(await fs.pathExists(configPath))) {
|
|
26
22
|
throw new Error("Rigour configuration (rigour.yml) not found. The agent must run `rigour init` first to establish engineering standards.");
|
|
27
23
|
}
|
|
28
|
-
const configContent = await
|
|
29
|
-
return
|
|
24
|
+
const configContent = await fs.readFile(configPath, "utf-8");
|
|
25
|
+
return ConfigSchema.parse(yaml.parse(configContent));
|
|
30
26
|
}
|
|
31
27
|
async function getMemoryPath(cwd) {
|
|
32
|
-
const rigourDir =
|
|
33
|
-
await
|
|
34
|
-
return
|
|
28
|
+
const rigourDir = path.join(cwd, ".rigour");
|
|
29
|
+
await fs.ensureDir(rigourDir);
|
|
30
|
+
return path.join(rigourDir, "memory.json");
|
|
35
31
|
}
|
|
36
32
|
async function loadMemory(cwd) {
|
|
37
33
|
const memPath = await getMemoryPath(cwd);
|
|
38
|
-
if (await
|
|
39
|
-
const content = await
|
|
34
|
+
if (await fs.pathExists(memPath)) {
|
|
35
|
+
const content = await fs.readFile(memPath, "utf-8");
|
|
40
36
|
return JSON.parse(content);
|
|
41
37
|
}
|
|
42
38
|
return { memories: {} };
|
|
43
39
|
}
|
|
44
40
|
async function saveMemory(cwd, store) {
|
|
45
41
|
const memPath = await getMemoryPath(cwd);
|
|
46
|
-
await
|
|
42
|
+
await fs.writeFile(memPath, JSON.stringify(store, null, 2));
|
|
47
43
|
}
|
|
48
|
-
|
|
44
|
+
// Helper to log events for Rigour Studio
|
|
45
|
+
async function logStudioEvent(cwd, event) {
|
|
46
|
+
try {
|
|
47
|
+
const rigourDir = path.join(cwd, ".rigour");
|
|
48
|
+
await fs.ensureDir(rigourDir);
|
|
49
|
+
const eventsPath = path.join(rigourDir, "events.jsonl");
|
|
50
|
+
const logEntry = JSON.stringify({
|
|
51
|
+
id: randomUUID(),
|
|
52
|
+
timestamp: new Date().toISOString(),
|
|
53
|
+
...event
|
|
54
|
+
}) + "\n";
|
|
55
|
+
await fs.appendFile(eventsPath, logEntry);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Silent fail - Studio logging is non-blocking and zero-telemetry
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
49
62
|
return {
|
|
50
63
|
tools: [
|
|
51
64
|
{
|
|
@@ -233,16 +246,24 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
|
233
246
|
],
|
|
234
247
|
};
|
|
235
248
|
});
|
|
236
|
-
server.setRequestHandler(
|
|
249
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
237
250
|
const { name, arguments: args } = request.params;
|
|
238
251
|
const cwd = args?.cwd || process.cwd();
|
|
252
|
+
const requestId = randomUUID();
|
|
239
253
|
try {
|
|
254
|
+
await logStudioEvent(cwd, {
|
|
255
|
+
type: "tool_call",
|
|
256
|
+
requestId,
|
|
257
|
+
tool: name,
|
|
258
|
+
arguments: args
|
|
259
|
+
});
|
|
240
260
|
const config = await loadConfig(cwd);
|
|
241
|
-
const runner = new
|
|
261
|
+
const runner = new GateRunner(config);
|
|
262
|
+
let result;
|
|
242
263
|
switch (name) {
|
|
243
264
|
case "rigour_check": {
|
|
244
265
|
const report = await runner.run(cwd);
|
|
245
|
-
|
|
266
|
+
result = {
|
|
246
267
|
content: [
|
|
247
268
|
{
|
|
248
269
|
type: "text",
|
|
@@ -250,11 +271,14 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
250
271
|
},
|
|
251
272
|
],
|
|
252
273
|
};
|
|
274
|
+
// Add the report to the tool_response log for high-fidelity Studio visualization
|
|
275
|
+
result._rigour_report = report;
|
|
276
|
+
break;
|
|
253
277
|
}
|
|
254
278
|
case "rigour_explain": {
|
|
255
279
|
const report = await runner.run(cwd);
|
|
256
280
|
if (report.status === "PASS") {
|
|
257
|
-
|
|
281
|
+
result = {
|
|
258
282
|
content: [
|
|
259
283
|
{
|
|
260
284
|
type: "text",
|
|
@@ -263,21 +287,24 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
263
287
|
],
|
|
264
288
|
};
|
|
265
289
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
290
|
+
else {
|
|
291
|
+
const bullets = report.failures.map((f, i) => {
|
|
292
|
+
return `${i + 1}. [${f.id.toUpperCase()}] ${f.title}: ${f.details}${f.hint ? ` (Hint: ${f.hint})` : ''}`;
|
|
293
|
+
}).join("\n");
|
|
294
|
+
result = {
|
|
295
|
+
content: [
|
|
296
|
+
{
|
|
297
|
+
type: "text",
|
|
298
|
+
text: `RIGOUR EXPLAIN:\n\n${bullets}`,
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
277
304
|
}
|
|
278
305
|
case "rigour_status": {
|
|
279
306
|
const report = await runner.run(cwd);
|
|
280
|
-
|
|
307
|
+
result = {
|
|
281
308
|
content: [
|
|
282
309
|
{
|
|
283
310
|
type: "text",
|
|
@@ -290,11 +317,12 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
290
317
|
},
|
|
291
318
|
],
|
|
292
319
|
};
|
|
320
|
+
break;
|
|
293
321
|
}
|
|
294
322
|
case "rigour_get_fix_packet": {
|
|
295
323
|
const report = await runner.run(cwd);
|
|
296
324
|
if (report.status === "PASS") {
|
|
297
|
-
|
|
325
|
+
result = {
|
|
298
326
|
content: [
|
|
299
327
|
{
|
|
300
328
|
type: "text",
|
|
@@ -303,28 +331,31 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
303
331
|
],
|
|
304
332
|
};
|
|
305
333
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
334
|
+
else {
|
|
335
|
+
const packet = report.failures.map((f, i) => {
|
|
336
|
+
let text = `FIX TASK ${i + 1}: [${f.id.toUpperCase()}] ${f.title}\n`;
|
|
337
|
+
text += ` - CONTEXT: ${f.details}\n`;
|
|
338
|
+
if (f.files && f.files.length > 0) {
|
|
339
|
+
text += ` - TARGET FILES: ${f.files.join(", ")}\n`;
|
|
340
|
+
}
|
|
341
|
+
if (f.hint) {
|
|
342
|
+
text += ` - REFACTORING GUIDANCE: ${f.hint}\n`;
|
|
343
|
+
}
|
|
344
|
+
return text;
|
|
345
|
+
}).join("\n---\n");
|
|
346
|
+
result = {
|
|
347
|
+
content: [
|
|
348
|
+
{
|
|
349
|
+
type: "text",
|
|
350
|
+
text: `ENGINEERING REFINEMENT REQUIRED:\n\nThe project state violated ${report.failures.length} quality gates. You MUST address these failures before declaring the task complete:\n\n${packet}`,
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
325
356
|
}
|
|
326
357
|
case "rigour_list_gates":
|
|
327
|
-
|
|
358
|
+
result = {
|
|
328
359
|
content: [
|
|
329
360
|
{
|
|
330
361
|
type: "text",
|
|
@@ -337,8 +368,9 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
337
368
|
},
|
|
338
369
|
],
|
|
339
370
|
};
|
|
371
|
+
break;
|
|
340
372
|
case "rigour_get_config":
|
|
341
|
-
|
|
373
|
+
result = {
|
|
342
374
|
content: [
|
|
343
375
|
{
|
|
344
376
|
type: "text",
|
|
@@ -346,6 +378,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
346
378
|
},
|
|
347
379
|
],
|
|
348
380
|
};
|
|
381
|
+
break;
|
|
349
382
|
case "rigour_remember": {
|
|
350
383
|
const { key, value } = args;
|
|
351
384
|
const store = await loadMemory(cwd);
|
|
@@ -354,7 +387,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
354
387
|
timestamp: new Date().toISOString(),
|
|
355
388
|
};
|
|
356
389
|
await saveMemory(cwd, store);
|
|
357
|
-
|
|
390
|
+
result = {
|
|
358
391
|
content: [
|
|
359
392
|
{
|
|
360
393
|
type: "text",
|
|
@@ -362,6 +395,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
362
395
|
},
|
|
363
396
|
],
|
|
364
397
|
};
|
|
398
|
+
break;
|
|
365
399
|
}
|
|
366
400
|
case "rigour_recall": {
|
|
367
401
|
const { key } = args;
|
|
@@ -369,7 +403,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
369
403
|
if (key) {
|
|
370
404
|
const memory = store.memories[key];
|
|
371
405
|
if (!memory) {
|
|
372
|
-
|
|
406
|
+
result = {
|
|
373
407
|
content: [
|
|
374
408
|
{
|
|
375
409
|
type: "text",
|
|
@@ -378,44 +412,51 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
378
412
|
],
|
|
379
413
|
};
|
|
380
414
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
415
|
+
else {
|
|
416
|
+
result = {
|
|
417
|
+
content: [
|
|
418
|
+
{
|
|
419
|
+
type: "text",
|
|
420
|
+
text: `RECALLED MEMORY [${key}]:\n${memory.value}\n\n(Stored: ${memory.timestamp})`,
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
389
425
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
426
|
+
else {
|
|
427
|
+
const keys = Object.keys(store.memories);
|
|
428
|
+
if (keys.length === 0) {
|
|
429
|
+
result = {
|
|
430
|
+
content: [
|
|
431
|
+
{
|
|
432
|
+
type: "text",
|
|
433
|
+
text: "NO MEMORIES STORED. Use rigour_remember to persist important instructions.",
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
const allMemories = keys.map(k => {
|
|
440
|
+
const mem = store.memories[k];
|
|
441
|
+
return `## ${k}\n${mem.value}\n(Stored: ${mem.timestamp})`;
|
|
442
|
+
}).join("\n\n---\n\n");
|
|
443
|
+
result = {
|
|
444
|
+
content: [
|
|
445
|
+
{
|
|
446
|
+
type: "text",
|
|
447
|
+
text: `RECALLED ALL MEMORIES (${keys.length} items):\n\n${allMemories}\n\n---\nIMPORTANT: Follow these stored instructions throughout this session.`,
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
400
452
|
}
|
|
401
|
-
|
|
402
|
-
const mem = store.memories[k];
|
|
403
|
-
return `## ${k}\n${mem.value}\n(Stored: ${mem.timestamp})`;
|
|
404
|
-
}).join("\n\n---\n\n");
|
|
405
|
-
return {
|
|
406
|
-
content: [
|
|
407
|
-
{
|
|
408
|
-
type: "text",
|
|
409
|
-
text: `RECALLED ALL MEMORIES (${keys.length} items):\n\n${allMemories}\n\n---\nIMPORTANT: Follow these stored instructions throughout this session.`,
|
|
410
|
-
},
|
|
411
|
-
],
|
|
412
|
-
};
|
|
453
|
+
break;
|
|
413
454
|
}
|
|
414
455
|
case "rigour_forget": {
|
|
415
456
|
const { key } = args;
|
|
416
457
|
const store = await loadMemory(cwd);
|
|
417
458
|
if (!store.memories[key]) {
|
|
418
|
-
|
|
459
|
+
result = {
|
|
419
460
|
content: [
|
|
420
461
|
{
|
|
421
462
|
type: "text",
|
|
@@ -424,25 +465,28 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
424
465
|
],
|
|
425
466
|
};
|
|
426
467
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
468
|
+
else {
|
|
469
|
+
delete store.memories[key];
|
|
470
|
+
await saveMemory(cwd, store);
|
|
471
|
+
result = {
|
|
472
|
+
content: [
|
|
473
|
+
{
|
|
474
|
+
type: "text",
|
|
475
|
+
text: `MEMORY DELETED: "${key}" has been removed.`,
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
437
481
|
}
|
|
438
482
|
case "rigour_check_pattern": {
|
|
439
483
|
const { name: patternName, type, intent } = args;
|
|
440
|
-
const indexPath =
|
|
441
|
-
const index = await
|
|
484
|
+
const indexPath = getDefaultIndexPath(cwd);
|
|
485
|
+
const index = await loadPatternIndex(indexPath);
|
|
442
486
|
let resultText = "";
|
|
443
487
|
// 1. Check for Reinvention
|
|
444
488
|
if (index) {
|
|
445
|
-
const matcher = new
|
|
489
|
+
const matcher = new PatternMatcher(index);
|
|
446
490
|
const matchResult = await matcher.match({ name: patternName, type, intent });
|
|
447
491
|
if (matchResult.status === "FOUND_SIMILAR") {
|
|
448
492
|
resultText += `🚨 PATTERN REINVENTION DETECTED\n`;
|
|
@@ -454,7 +498,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
454
498
|
resultText += `⚠️ Pattern index not found. Run 'rigour index' to enable reinvention detection.\n\n`;
|
|
455
499
|
}
|
|
456
500
|
// 2. Check for Staleness/Best Practices
|
|
457
|
-
const detector = new
|
|
501
|
+
const detector = new StalenessDetector(cwd);
|
|
458
502
|
const staleness = await detector.checkStaleness(`${type || 'function'} ${patternName} {}`);
|
|
459
503
|
if (staleness.status !== "FRESH") {
|
|
460
504
|
resultText += `⚠️ STALENESS/ANTI-PATTERN WARNING\n`;
|
|
@@ -465,7 +509,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
465
509
|
}
|
|
466
510
|
// 3. Check Security for this library (if it's an import)
|
|
467
511
|
if (intent && intent.includes('import')) {
|
|
468
|
-
const security = new
|
|
512
|
+
const security = new SecurityDetector(cwd);
|
|
469
513
|
const audit = await security.runAudit();
|
|
470
514
|
const relatedVulns = audit.vulnerabilities.filter(v => patternName.toLowerCase().includes(v.packageName.toLowerCase()) ||
|
|
471
515
|
intent.toLowerCase().includes(v.packageName.toLowerCase()));
|
|
@@ -493,7 +537,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
493
537
|
}
|
|
494
538
|
resultText += `\nRECOMMENDED ACTION: ${recommendation}`;
|
|
495
539
|
}
|
|
496
|
-
|
|
540
|
+
result = {
|
|
497
541
|
content: [
|
|
498
542
|
{
|
|
499
543
|
type: "text",
|
|
@@ -501,11 +545,12 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
501
545
|
},
|
|
502
546
|
],
|
|
503
547
|
};
|
|
548
|
+
break;
|
|
504
549
|
}
|
|
505
550
|
case "rigour_security_audit": {
|
|
506
|
-
const security = new
|
|
551
|
+
const security = new SecurityDetector(cwd);
|
|
507
552
|
const summary = await security.getSecuritySummary();
|
|
508
|
-
|
|
553
|
+
result = {
|
|
509
554
|
content: [
|
|
510
555
|
{
|
|
511
556
|
type: "text",
|
|
@@ -513,13 +558,23 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
513
558
|
},
|
|
514
559
|
],
|
|
515
560
|
};
|
|
561
|
+
break;
|
|
516
562
|
}
|
|
517
563
|
default:
|
|
518
564
|
throw new Error(`Unknown tool: ${name}`);
|
|
519
565
|
}
|
|
566
|
+
await logStudioEvent(cwd, {
|
|
567
|
+
type: "tool_response",
|
|
568
|
+
requestId,
|
|
569
|
+
tool: name,
|
|
570
|
+
status: "success",
|
|
571
|
+
content: result.content,
|
|
572
|
+
_rigour_report: result._rigour_report
|
|
573
|
+
});
|
|
574
|
+
return result;
|
|
520
575
|
}
|
|
521
576
|
catch (error) {
|
|
522
|
-
|
|
577
|
+
const errorResponse = {
|
|
523
578
|
content: [
|
|
524
579
|
{
|
|
525
580
|
type: "text",
|
|
@@ -528,10 +583,19 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
528
583
|
],
|
|
529
584
|
isError: true,
|
|
530
585
|
};
|
|
586
|
+
await logStudioEvent(cwd, {
|
|
587
|
+
type: "tool_response",
|
|
588
|
+
requestId,
|
|
589
|
+
tool: name,
|
|
590
|
+
status: "error",
|
|
591
|
+
error: error.message,
|
|
592
|
+
content: errorResponse.content
|
|
593
|
+
});
|
|
594
|
+
return errorResponse;
|
|
531
595
|
}
|
|
532
596
|
});
|
|
533
597
|
async function main() {
|
|
534
|
-
const transport = new
|
|
598
|
+
const transport = new StdioServerTransport();
|
|
535
599
|
await server.connect(transport);
|
|
536
600
|
console.error("Rigour MCP server v1.0.0 running on stdio");
|
|
537
601
|
}
|
package/dist/smoke.test.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
(
|
|
5
|
-
(0, vitest_1.it)('should pass', async () => {
|
|
6
|
-
(0, vitest_1.expect)(true).toBe(true);
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
describe('MCP Smoke Test', () => {
|
|
3
|
+
it('should pass', async () => {
|
|
4
|
+
expect(true).toBe(true);
|
|
7
5
|
});
|
|
8
6
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"mcpName": "io.github.rigour-labs/rigour",
|
|
5
6
|
"description": "Quality gates for AI-generated code. Forces AI agents to meet strict engineering standards with PASS/FAIL enforcement.",
|
|
6
7
|
"bin": {
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
19
20
|
"fs-extra": "^11.2.0",
|
|
20
21
|
"yaml": "^2.8.2",
|
|
21
|
-
"@rigour-labs/core": "2.
|
|
22
|
+
"@rigour-labs/core": "2.11.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^25.0.3"
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import fs from "fs-extra";
|
|
9
9
|
import path from "path";
|
|
10
10
|
import yaml from "yaml";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
11
12
|
import {
|
|
12
13
|
GateRunner,
|
|
13
14
|
ConfigSchema,
|
|
@@ -67,6 +68,23 @@ async function saveMemory(cwd: string, store: MemoryStore): Promise<void> {
|
|
|
67
68
|
await fs.writeFile(memPath, JSON.stringify(store, null, 2));
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
// Helper to log events for Rigour Studio
|
|
72
|
+
async function logStudioEvent(cwd: string, event: any) {
|
|
73
|
+
try {
|
|
74
|
+
const rigourDir = path.join(cwd, ".rigour");
|
|
75
|
+
await fs.ensureDir(rigourDir);
|
|
76
|
+
const eventsPath = path.join(rigourDir, "events.jsonl");
|
|
77
|
+
const logEntry = JSON.stringify({
|
|
78
|
+
id: randomUUID(),
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
...event
|
|
81
|
+
}) + "\n";
|
|
82
|
+
await fs.appendFile(eventsPath, logEntry);
|
|
83
|
+
} catch {
|
|
84
|
+
// Silent fail - Studio logging is non-blocking and zero-telemetry
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
71
89
|
return {
|
|
72
90
|
tools: [
|
|
@@ -259,15 +277,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
259
277
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
260
278
|
const { name, arguments: args } = request.params;
|
|
261
279
|
const cwd = (args as any)?.cwd || process.cwd();
|
|
280
|
+
const requestId = randomUUID();
|
|
262
281
|
|
|
263
282
|
try {
|
|
283
|
+
await logStudioEvent(cwd, {
|
|
284
|
+
type: "tool_call",
|
|
285
|
+
requestId,
|
|
286
|
+
tool: name,
|
|
287
|
+
arguments: args
|
|
288
|
+
});
|
|
289
|
+
|
|
264
290
|
const config = await loadConfig(cwd);
|
|
265
291
|
const runner = new GateRunner(config);
|
|
266
292
|
|
|
293
|
+
let result: any;
|
|
294
|
+
|
|
267
295
|
switch (name) {
|
|
268
296
|
case "rigour_check": {
|
|
269
297
|
const report = await runner.run(cwd);
|
|
270
|
-
|
|
298
|
+
result = {
|
|
271
299
|
content: [
|
|
272
300
|
{
|
|
273
301
|
type: "text",
|
|
@@ -275,12 +303,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
275
303
|
},
|
|
276
304
|
],
|
|
277
305
|
};
|
|
306
|
+
|
|
307
|
+
// Add the report to the tool_response log for high-fidelity Studio visualization
|
|
308
|
+
(result as any)._rigour_report = report;
|
|
309
|
+
break;
|
|
278
310
|
}
|
|
279
311
|
|
|
280
312
|
case "rigour_explain": {
|
|
281
313
|
const report = await runner.run(cwd);
|
|
282
314
|
if (report.status === "PASS") {
|
|
283
|
-
|
|
315
|
+
result = {
|
|
284
316
|
content: [
|
|
285
317
|
{
|
|
286
318
|
type: "text",
|
|
@@ -288,25 +320,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
288
320
|
},
|
|
289
321
|
],
|
|
290
322
|
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}).join("\n");
|
|
323
|
+
} else {
|
|
324
|
+
const bullets = report.failures.map((f, i) => {
|
|
325
|
+
return `${i + 1}. [${f.id.toUpperCase()}] ${f.title}: ${f.details}${f.hint ? ` (Hint: ${f.hint})` : ''}`;
|
|
326
|
+
}).join("\n");
|
|
296
327
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
result = {
|
|
329
|
+
content: [
|
|
330
|
+
{
|
|
331
|
+
type: "text",
|
|
332
|
+
text: `RIGOUR EXPLAIN:\n\n${bullets}`,
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
305
338
|
}
|
|
306
339
|
|
|
307
340
|
case "rigour_status": {
|
|
308
341
|
const report = await runner.run(cwd);
|
|
309
|
-
|
|
342
|
+
result = {
|
|
310
343
|
content: [
|
|
311
344
|
{
|
|
312
345
|
type: "text",
|
|
@@ -319,12 +352,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
319
352
|
},
|
|
320
353
|
],
|
|
321
354
|
};
|
|
355
|
+
break;
|
|
322
356
|
}
|
|
323
357
|
|
|
324
358
|
case "rigour_get_fix_packet": {
|
|
325
359
|
const report = await runner.run(cwd);
|
|
326
360
|
if (report.status === "PASS") {
|
|
327
|
-
|
|
361
|
+
result = {
|
|
328
362
|
content: [
|
|
329
363
|
{
|
|
330
364
|
type: "text",
|
|
@@ -332,32 +366,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
332
366
|
},
|
|
333
367
|
],
|
|
334
368
|
};
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}).join("\n---\n");
|
|
369
|
+
} else {
|
|
370
|
+
const packet = report.failures.map((f, i) => {
|
|
371
|
+
let text = `FIX TASK ${i + 1}: [${f.id.toUpperCase()}] ${f.title}\n`;
|
|
372
|
+
text += ` - CONTEXT: ${f.details}\n`;
|
|
373
|
+
if (f.files && f.files.length > 0) {
|
|
374
|
+
text += ` - TARGET FILES: ${f.files.join(", ")}\n`;
|
|
375
|
+
}
|
|
376
|
+
if (f.hint) {
|
|
377
|
+
text += ` - REFACTORING GUIDANCE: ${f.hint}\n`;
|
|
378
|
+
}
|
|
379
|
+
return text;
|
|
380
|
+
}).join("\n---\n");
|
|
348
381
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
382
|
+
result = {
|
|
383
|
+
content: [
|
|
384
|
+
{
|
|
385
|
+
type: "text",
|
|
386
|
+
text: `ENGINEERING REFINEMENT REQUIRED:\n\nThe project state violated ${report.failures.length} quality gates. You MUST address these failures before declaring the task complete:\n\n${packet}`,
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
break;
|
|
357
392
|
}
|
|
358
393
|
|
|
359
394
|
case "rigour_list_gates":
|
|
360
|
-
|
|
395
|
+
result = {
|
|
361
396
|
content: [
|
|
362
397
|
{
|
|
363
398
|
type: "text",
|
|
@@ -370,9 +405,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
370
405
|
},
|
|
371
406
|
],
|
|
372
407
|
};
|
|
408
|
+
break;
|
|
373
409
|
|
|
374
410
|
case "rigour_get_config":
|
|
375
|
-
|
|
411
|
+
result = {
|
|
376
412
|
content: [
|
|
377
413
|
{
|
|
378
414
|
type: "text",
|
|
@@ -380,6 +416,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
380
416
|
},
|
|
381
417
|
],
|
|
382
418
|
};
|
|
419
|
+
break;
|
|
383
420
|
|
|
384
421
|
case "rigour_remember": {
|
|
385
422
|
const { key, value } = args as any;
|
|
@@ -389,7 +426,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
389
426
|
timestamp: new Date().toISOString(),
|
|
390
427
|
};
|
|
391
428
|
await saveMemory(cwd, store);
|
|
392
|
-
|
|
429
|
+
result = {
|
|
393
430
|
content: [
|
|
394
431
|
{
|
|
395
432
|
type: "text",
|
|
@@ -397,6 +434,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
397
434
|
},
|
|
398
435
|
],
|
|
399
436
|
};
|
|
437
|
+
break;
|
|
400
438
|
}
|
|
401
439
|
|
|
402
440
|
case "rigour_recall": {
|
|
@@ -406,7 +444,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
406
444
|
if (key) {
|
|
407
445
|
const memory = store.memories[key];
|
|
408
446
|
if (!memory) {
|
|
409
|
-
|
|
447
|
+
result = {
|
|
410
448
|
content: [
|
|
411
449
|
{
|
|
412
450
|
type: "text",
|
|
@@ -414,42 +452,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
414
452
|
},
|
|
415
453
|
],
|
|
416
454
|
};
|
|
455
|
+
} else {
|
|
456
|
+
result = {
|
|
457
|
+
content: [
|
|
458
|
+
{
|
|
459
|
+
type: "text",
|
|
460
|
+
text: `RECALLED MEMORY [${key}]:\n${memory.value}\n\n(Stored: ${memory.timestamp})`,
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
417
464
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
465
|
+
} else {
|
|
466
|
+
const keys = Object.keys(store.memories);
|
|
467
|
+
if (keys.length === 0) {
|
|
468
|
+
result = {
|
|
469
|
+
content: [
|
|
470
|
+
{
|
|
471
|
+
type: "text",
|
|
472
|
+
text: "NO MEMORIES STORED. Use rigour_remember to persist important instructions.",
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
};
|
|
476
|
+
} else {
|
|
477
|
+
const allMemories = keys.map(k => {
|
|
478
|
+
const mem = store.memories[k];
|
|
479
|
+
return `## ${k}\n${mem.value}\n(Stored: ${mem.timestamp})`;
|
|
480
|
+
}).join("\n\n---\n\n");
|
|
427
481
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
};
|
|
482
|
+
result = {
|
|
483
|
+
content: [
|
|
484
|
+
{
|
|
485
|
+
type: "text",
|
|
486
|
+
text: `RECALLED ALL MEMORIES (${keys.length} items):\n\n${allMemories}\n\n---\nIMPORTANT: Follow these stored instructions throughout this session.`,
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
};
|
|
490
|
+
}
|
|
438
491
|
}
|
|
439
|
-
|
|
440
|
-
const allMemories = keys.map(k => {
|
|
441
|
-
const mem = store.memories[k];
|
|
442
|
-
return `## ${k}\n${mem.value}\n(Stored: ${mem.timestamp})`;
|
|
443
|
-
}).join("\n\n---\n\n");
|
|
444
|
-
|
|
445
|
-
return {
|
|
446
|
-
content: [
|
|
447
|
-
{
|
|
448
|
-
type: "text",
|
|
449
|
-
text: `RECALLED ALL MEMORIES (${keys.length} items):\n\n${allMemories}\n\n---\nIMPORTANT: Follow these stored instructions throughout this session.`,
|
|
450
|
-
},
|
|
451
|
-
],
|
|
452
|
-
};
|
|
492
|
+
break;
|
|
453
493
|
}
|
|
454
494
|
|
|
455
495
|
case "rigour_forget": {
|
|
@@ -457,7 +497,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
457
497
|
const store = await loadMemory(cwd);
|
|
458
498
|
|
|
459
499
|
if (!store.memories[key]) {
|
|
460
|
-
|
|
500
|
+
result = {
|
|
461
501
|
content: [
|
|
462
502
|
{
|
|
463
503
|
type: "text",
|
|
@@ -465,19 +505,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
465
505
|
},
|
|
466
506
|
],
|
|
467
507
|
};
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
await saveMemory(cwd, store);
|
|
508
|
+
} else {
|
|
509
|
+
delete store.memories[key];
|
|
510
|
+
await saveMemory(cwd, store);
|
|
472
511
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
512
|
+
result = {
|
|
513
|
+
content: [
|
|
514
|
+
{
|
|
515
|
+
type: "text",
|
|
516
|
+
text: `MEMORY DELETED: "${key}" has been removed.`,
|
|
517
|
+
},
|
|
518
|
+
],
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
break;
|
|
481
522
|
}
|
|
482
523
|
|
|
483
524
|
case "rigour_check_pattern": {
|
|
@@ -546,7 +587,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
546
587
|
resultText += `\nRECOMMENDED ACTION: ${recommendation}`;
|
|
547
588
|
}
|
|
548
589
|
|
|
549
|
-
|
|
590
|
+
result = {
|
|
550
591
|
content: [
|
|
551
592
|
{
|
|
552
593
|
type: "text",
|
|
@@ -554,12 +595,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
554
595
|
},
|
|
555
596
|
],
|
|
556
597
|
};
|
|
598
|
+
break;
|
|
557
599
|
}
|
|
558
600
|
|
|
559
601
|
case "rigour_security_audit": {
|
|
560
602
|
const security = new SecurityDetector(cwd);
|
|
561
603
|
const summary = await security.getSecuritySummary();
|
|
562
|
-
|
|
604
|
+
result = {
|
|
563
605
|
content: [
|
|
564
606
|
{
|
|
565
607
|
type: "text",
|
|
@@ -567,13 +609,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
567
609
|
},
|
|
568
610
|
],
|
|
569
611
|
};
|
|
612
|
+
break;
|
|
570
613
|
}
|
|
571
614
|
|
|
572
615
|
default:
|
|
573
616
|
throw new Error(`Unknown tool: ${name}`);
|
|
574
617
|
}
|
|
618
|
+
|
|
619
|
+
await logStudioEvent(cwd, {
|
|
620
|
+
type: "tool_response",
|
|
621
|
+
requestId,
|
|
622
|
+
tool: name,
|
|
623
|
+
status: "success",
|
|
624
|
+
content: result.content,
|
|
625
|
+
_rigour_report: (result as any)._rigour_report
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
return result;
|
|
629
|
+
|
|
575
630
|
} catch (error: any) {
|
|
576
|
-
|
|
631
|
+
const errorResponse = {
|
|
577
632
|
content: [
|
|
578
633
|
{
|
|
579
634
|
type: "text",
|
|
@@ -582,6 +637,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
582
637
|
],
|
|
583
638
|
isError: true,
|
|
584
639
|
};
|
|
640
|
+
|
|
641
|
+
await logStudioEvent(cwd, {
|
|
642
|
+
type: "tool_response",
|
|
643
|
+
requestId,
|
|
644
|
+
tool: name,
|
|
645
|
+
status: "error",
|
|
646
|
+
error: error.message,
|
|
647
|
+
content: errorResponse.content
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
return errorResponse;
|
|
585
651
|
}
|
|
586
652
|
});
|
|
587
653
|
|