vibecheck-mcp-server 2.0.1
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/README.md +191 -0
- package/agent-checkpoint.js +364 -0
- package/architect-tools.js +707 -0
- package/audit-mcp.js +206 -0
- package/codebase-architect-tools.js +838 -0
- package/guardrail-2.0-tools.js +748 -0
- package/guardrail-tools.js +1075 -0
- package/hygiene-tools.js +428 -0
- package/index-v1.js +698 -0
- package/index.js +1409 -0
- package/index.old.js +4137 -0
- package/intelligence-tools.js +664 -0
- package/intent-drift-tools.js +873 -0
- package/mdc-generator.js +298 -0
- package/package.json +47 -0
- package/premium-tools.js +1275 -0
- package/test-mcp.js +108 -0
- package/test-tools.js +36 -0
- package/tier-auth.js +147 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail 2.0 MCP Tools - Consolidated to 6 Tools
|
|
3
|
+
*
|
|
4
|
+
* Tools:
|
|
5
|
+
* checkpoint() - Pre/post write enforcement, block AI until fixed
|
|
6
|
+
* check() - Verify code is real, wired, honest
|
|
7
|
+
* ship() - Go/No-Go decision (GO / WARN / NO-GO)
|
|
8
|
+
* fix() - Fix blocking issues safely
|
|
9
|
+
* status() - Health + version info
|
|
10
|
+
* set_strictness() - Set checkpoint strictness level
|
|
11
|
+
*
|
|
12
|
+
* Server-side enforcement:
|
|
13
|
+
* - Fix-Only Mode: Rejects patches outside allowedFiles when blocked
|
|
14
|
+
* - Intent Lock: Prevents scope creep when enabled
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { execSync } from "child_process";
|
|
18
|
+
import fs from "fs/promises";
|
|
19
|
+
import path from "path";
|
|
20
|
+
|
|
21
|
+
// Strictness levels for checkpoint
|
|
22
|
+
const STRICTNESS_LEVELS = ["chill", "standard", "strict", "paranoid"];
|
|
23
|
+
|
|
24
|
+
// Current strictness (in-memory, should be persisted per-project)
|
|
25
|
+
let currentStrictness = "standard";
|
|
26
|
+
|
|
27
|
+
// Blocked state tracking (in-memory, should be persisted)
|
|
28
|
+
const blockedState = new Map();
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// TOOL DEFINITIONS - 6 TOOLS ONLY
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
const GUARDRAIL_2_TOOLS = [
|
|
35
|
+
// 1. CHECKPOINT - Block AI agents until issues are fixed
|
|
36
|
+
{
|
|
37
|
+
name: "checkpoint",
|
|
38
|
+
description: `🛡️ Block AI agents until issues are fixed. Call BEFORE and AFTER every code write.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
- PASS: Continue writing code
|
|
42
|
+
- BLOCKED: Stop and fix issues first. Response includes:
|
|
43
|
+
- blockedFiles: Files with violations
|
|
44
|
+
- violations: List of issues with ruleId, message, file, line
|
|
45
|
+
- fixOnly: { allowedFiles, forbiddenActions } - Fix-Only Mode restrictions
|
|
46
|
+
|
|
47
|
+
If BLOCKED, you MUST:
|
|
48
|
+
1. Enter Fix-Only Mode
|
|
49
|
+
2. Only modify files in allowedFiles
|
|
50
|
+
3. Do NOT add features, refactors, or new files
|
|
51
|
+
4. Call checkpoint again after fixes`,
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
file_path: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Path to file being written",
|
|
58
|
+
},
|
|
59
|
+
content: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: "Content to validate (for pre-write check)",
|
|
62
|
+
},
|
|
63
|
+
action: {
|
|
64
|
+
type: "string",
|
|
65
|
+
enum: ["pre_write", "post_write", "status"],
|
|
66
|
+
description: "Checkpoint action type",
|
|
67
|
+
default: "pre_write",
|
|
68
|
+
},
|
|
69
|
+
projectPath: {
|
|
70
|
+
type: "string",
|
|
71
|
+
default: ".",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
required: ["file_path"],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// 2. CHECK - Verify code is real, wired, honest
|
|
79
|
+
{
|
|
80
|
+
name: "check",
|
|
81
|
+
description: `🔍 Verify code is real, wired, and honest.
|
|
82
|
+
|
|
83
|
+
Consolidates: scan, validate, mockproof
|
|
84
|
+
Returns: PASS | WARN | FAIL with findings list
|
|
85
|
+
|
|
86
|
+
Use cases:
|
|
87
|
+
- Quick code verification
|
|
88
|
+
- Mock/placeholder detection (--mocks)
|
|
89
|
+
- Security scanning (--only=security)
|
|
90
|
+
- AI code validation (--validate)`,
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
projectPath: {
|
|
95
|
+
type: "string",
|
|
96
|
+
default: ".",
|
|
97
|
+
},
|
|
98
|
+
mocks: {
|
|
99
|
+
type: "boolean",
|
|
100
|
+
description: "Focus on mock/placeholder detection",
|
|
101
|
+
default: false,
|
|
102
|
+
},
|
|
103
|
+
validate: {
|
|
104
|
+
type: "boolean",
|
|
105
|
+
description: "Include AI code validation",
|
|
106
|
+
default: false,
|
|
107
|
+
},
|
|
108
|
+
only: {
|
|
109
|
+
type: "array",
|
|
110
|
+
items: { type: "string" },
|
|
111
|
+
description:
|
|
112
|
+
"Run specific checks: secrets, auth, routes, mocks, hygiene, security",
|
|
113
|
+
},
|
|
114
|
+
json: {
|
|
115
|
+
type: "boolean",
|
|
116
|
+
description: "Return machine-readable JSON",
|
|
117
|
+
default: true,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// 3. SHIP - Go/No-Go decision
|
|
124
|
+
{
|
|
125
|
+
name: "ship",
|
|
126
|
+
description: `🚀 Decide if this can ship. Returns GO | WARN | NO-GO.
|
|
127
|
+
|
|
128
|
+
Consolidates: ship, gate, realityproof, ai-test, badge, evidence
|
|
129
|
+
Flags:
|
|
130
|
+
- --ci: CI/CD hard fail mode
|
|
131
|
+
- --runtime: Runtime verification (Playwright)
|
|
132
|
+
- --badge: Generate ship badge
|
|
133
|
+
- --report: Generate full report
|
|
134
|
+
- --evidence: Enterprise audit evidence pack`,
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
projectPath: {
|
|
139
|
+
type: "string",
|
|
140
|
+
default: ".",
|
|
141
|
+
},
|
|
142
|
+
ci: {
|
|
143
|
+
type: "boolean",
|
|
144
|
+
description: "CI mode - hard fail on issues",
|
|
145
|
+
default: false,
|
|
146
|
+
},
|
|
147
|
+
runtime: {
|
|
148
|
+
type: "boolean",
|
|
149
|
+
description: "Run runtime verification",
|
|
150
|
+
default: false,
|
|
151
|
+
},
|
|
152
|
+
url: {
|
|
153
|
+
type: "string",
|
|
154
|
+
description: "URL for runtime testing (required if runtime=true)",
|
|
155
|
+
},
|
|
156
|
+
badge: {
|
|
157
|
+
type: "boolean",
|
|
158
|
+
description: "Generate ship badge",
|
|
159
|
+
default: false,
|
|
160
|
+
},
|
|
161
|
+
report: {
|
|
162
|
+
type: "boolean",
|
|
163
|
+
description: "Generate detailed report",
|
|
164
|
+
default: true,
|
|
165
|
+
},
|
|
166
|
+
evidence: {
|
|
167
|
+
type: "boolean",
|
|
168
|
+
description: "Generate audit evidence pack (Enterprise)",
|
|
169
|
+
default: false,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// 4. FIX - Fix blocking issues safely
|
|
176
|
+
{
|
|
177
|
+
name: "fix",
|
|
178
|
+
description: `🔧 Fix blocking issues safely.
|
|
179
|
+
|
|
180
|
+
Options:
|
|
181
|
+
- --plan: Show fix plan without applying (default)
|
|
182
|
+
- --apply: Apply the fixes
|
|
183
|
+
- --pr: Open a pull request with fixes
|
|
184
|
+
|
|
185
|
+
Respects Fix-Only Mode restrictions when checkpoint is BLOCKED.`,
|
|
186
|
+
inputSchema: {
|
|
187
|
+
type: "object",
|
|
188
|
+
properties: {
|
|
189
|
+
projectPath: {
|
|
190
|
+
type: "string",
|
|
191
|
+
default: ".",
|
|
192
|
+
},
|
|
193
|
+
plan: {
|
|
194
|
+
type: "boolean",
|
|
195
|
+
description: "Show fix plan without applying",
|
|
196
|
+
default: true,
|
|
197
|
+
},
|
|
198
|
+
apply: {
|
|
199
|
+
type: "boolean",
|
|
200
|
+
description: "Apply fixes",
|
|
201
|
+
default: false,
|
|
202
|
+
},
|
|
203
|
+
pr: {
|
|
204
|
+
type: "boolean",
|
|
205
|
+
description: "Open PR with fixes",
|
|
206
|
+
default: false,
|
|
207
|
+
},
|
|
208
|
+
scope: {
|
|
209
|
+
type: "string",
|
|
210
|
+
enum: ["all", "secrets", "auth", "mocks", "routes"],
|
|
211
|
+
default: "all",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
// 5. STATUS - Health + version info
|
|
218
|
+
{
|
|
219
|
+
name: "status",
|
|
220
|
+
description: `📊 Get Guardrail status - health, version, config, checkpoint state.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
- healthy: boolean
|
|
224
|
+
- version: string
|
|
225
|
+
- checkpoint: { strictness, blockedFiles, fixOnlyMode }
|
|
226
|
+
- lastRun: timestamp and results`,
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
projectPath: {
|
|
231
|
+
type: "string",
|
|
232
|
+
default: ".",
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
// 6. SET_STRICTNESS - Set checkpoint strictness level
|
|
239
|
+
{
|
|
240
|
+
name: "set_strictness",
|
|
241
|
+
description: `⚙️ Set checkpoint strictness level.
|
|
242
|
+
|
|
243
|
+
Levels:
|
|
244
|
+
- chill: TODOs, FIXMEs, mock data, placeholders
|
|
245
|
+
- standard: + console.log, debugger, localhost URLs
|
|
246
|
+
- strict: + any types, @ts-ignore, eslint-disable
|
|
247
|
+
- paranoid: + nested ternaries, inline styles, JSDoc requirements`,
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
level: {
|
|
252
|
+
type: "string",
|
|
253
|
+
enum: ["chill", "standard", "strict", "paranoid"],
|
|
254
|
+
description: "Strictness level",
|
|
255
|
+
},
|
|
256
|
+
projectPath: {
|
|
257
|
+
type: "string",
|
|
258
|
+
default: ".",
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
required: ["level"],
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
// ============================================================================
|
|
267
|
+
// CHECKPOINT RULES BY STRICTNESS
|
|
268
|
+
// ============================================================================
|
|
269
|
+
|
|
270
|
+
const CHECKPOINT_RULES = {
|
|
271
|
+
chill: [
|
|
272
|
+
{
|
|
273
|
+
id: "no-todo",
|
|
274
|
+
pattern: /\/\/\s*TODO[:\s].*$/gim,
|
|
275
|
+
message: "TODO comment found",
|
|
276
|
+
block: true,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
id: "no-fixme",
|
|
280
|
+
pattern: /\/\/\s*FIXME[:\s].*$/gim,
|
|
281
|
+
message: "FIXME comment found",
|
|
282
|
+
block: true,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
id: "no-mock-data",
|
|
286
|
+
pattern: /['"]mock['"]/gi,
|
|
287
|
+
message: "Mock data reference",
|
|
288
|
+
block: true,
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: "no-placeholder",
|
|
292
|
+
pattern: /placeholder|lorem ipsum/gi,
|
|
293
|
+
message: "Placeholder content",
|
|
294
|
+
block: true,
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
standard: [
|
|
298
|
+
{
|
|
299
|
+
id: "no-console-log",
|
|
300
|
+
pattern: /console\.log\s*\(/g,
|
|
301
|
+
message: "console.log found",
|
|
302
|
+
block: true,
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: "no-debugger",
|
|
306
|
+
pattern: /\bdebugger\b/g,
|
|
307
|
+
message: "debugger statement",
|
|
308
|
+
block: true,
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "no-localhost",
|
|
312
|
+
pattern: /localhost:\d+/g,
|
|
313
|
+
message: "localhost URL",
|
|
314
|
+
block: true,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
id: "no-empty-catch",
|
|
318
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\}/g,
|
|
319
|
+
message: "Empty catch block",
|
|
320
|
+
block: true,
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
strict: [
|
|
324
|
+
{
|
|
325
|
+
id: "no-any",
|
|
326
|
+
pattern: /:\s*any\b/g,
|
|
327
|
+
message: "any type used",
|
|
328
|
+
block: true,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: "no-ts-ignore",
|
|
332
|
+
pattern: /@ts-ignore/g,
|
|
333
|
+
message: "@ts-ignore directive",
|
|
334
|
+
block: true,
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
id: "no-ts-nocheck",
|
|
338
|
+
pattern: /@ts-nocheck/g,
|
|
339
|
+
message: "@ts-nocheck directive",
|
|
340
|
+
block: true,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: "no-eslint-disable",
|
|
344
|
+
pattern: /eslint-disable/g,
|
|
345
|
+
message: "eslint-disable directive",
|
|
346
|
+
block: true,
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
paranoid: [
|
|
350
|
+
{
|
|
351
|
+
id: "no-nested-ternary",
|
|
352
|
+
pattern: /\?[^:]+\?/g,
|
|
353
|
+
message: "Nested ternary",
|
|
354
|
+
block: true,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
id: "no-inline-style",
|
|
358
|
+
pattern: /style\s*=\s*\{/g,
|
|
359
|
+
message: "Inline style object",
|
|
360
|
+
block: true,
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// TOOL HANDLERS
|
|
367
|
+
// ============================================================================
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Handle checkpoint tool - server-side enforcement
|
|
371
|
+
*/
|
|
372
|
+
async function handleCheckpoint(args, dirname) {
|
|
373
|
+
const { file_path, content, action = "pre_write", projectPath = "." } = args;
|
|
374
|
+
const fullPath = path.resolve(projectPath, file_path);
|
|
375
|
+
|
|
376
|
+
// Get current blocked state for this project
|
|
377
|
+
const projectKey = path.resolve(projectPath);
|
|
378
|
+
const blocked = blockedState.get(projectKey) || { files: [], violations: [] };
|
|
379
|
+
|
|
380
|
+
if (action === "status") {
|
|
381
|
+
return {
|
|
382
|
+
content: [
|
|
383
|
+
{
|
|
384
|
+
type: "text",
|
|
385
|
+
text: JSON.stringify(
|
|
386
|
+
{
|
|
387
|
+
status: blocked.files.length > 0 ? "BLOCKED" : "PASS",
|
|
388
|
+
strictness: currentStrictness,
|
|
389
|
+
blockedFiles: blocked.files,
|
|
390
|
+
violations: blocked.violations,
|
|
391
|
+
fixOnly:
|
|
392
|
+
blocked.files.length > 0
|
|
393
|
+
? {
|
|
394
|
+
allowedFiles: blocked.files,
|
|
395
|
+
forbiddenActions: [
|
|
396
|
+
"add_file",
|
|
397
|
+
"add_feature",
|
|
398
|
+
"refactor",
|
|
399
|
+
"change_scope",
|
|
400
|
+
],
|
|
401
|
+
}
|
|
402
|
+
: null,
|
|
403
|
+
},
|
|
404
|
+
null,
|
|
405
|
+
2,
|
|
406
|
+
),
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Validate content against checkpoint rules
|
|
413
|
+
const violations = [];
|
|
414
|
+
const contentToCheck = content || "";
|
|
415
|
+
|
|
416
|
+
// Get rules for current strictness and below
|
|
417
|
+
const strictnessIndex = STRICTNESS_LEVELS.indexOf(currentStrictness);
|
|
418
|
+
const applicableLevels = STRICTNESS_LEVELS.slice(0, strictnessIndex + 1);
|
|
419
|
+
|
|
420
|
+
for (const level of applicableLevels) {
|
|
421
|
+
const rules = CHECKPOINT_RULES[level] || [];
|
|
422
|
+
for (const rule of rules) {
|
|
423
|
+
const matches = contentToCheck.match(rule.pattern);
|
|
424
|
+
if (matches) {
|
|
425
|
+
for (const match of matches) {
|
|
426
|
+
const lineNum = contentToCheck
|
|
427
|
+
.substring(0, contentToCheck.indexOf(match))
|
|
428
|
+
.split("\n").length;
|
|
429
|
+
violations.push({
|
|
430
|
+
ruleId: rule.id,
|
|
431
|
+
message: rule.message,
|
|
432
|
+
file: file_path,
|
|
433
|
+
line: lineNum,
|
|
434
|
+
match: match.substring(0, 50),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (violations.length > 0) {
|
|
442
|
+
// Update blocked state
|
|
443
|
+
if (!blocked.files.includes(file_path)) {
|
|
444
|
+
blocked.files.push(file_path);
|
|
445
|
+
}
|
|
446
|
+
blocked.violations = [...blocked.violations, ...violations];
|
|
447
|
+
blockedState.set(projectKey, blocked);
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
isError: true,
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
type: "text",
|
|
454
|
+
text: JSON.stringify(
|
|
455
|
+
{
|
|
456
|
+
status: "BLOCKED",
|
|
457
|
+
message: `🛑 CHECKPOINT BLOCKED: ${violations.length} violation(s) found`,
|
|
458
|
+
blockedFiles: blocked.files,
|
|
459
|
+
violations: violations,
|
|
460
|
+
fixOnly: {
|
|
461
|
+
allowedFiles: blocked.files,
|
|
462
|
+
forbiddenActions: [
|
|
463
|
+
"add_file",
|
|
464
|
+
"add_feature",
|
|
465
|
+
"refactor",
|
|
466
|
+
"change_scope",
|
|
467
|
+
],
|
|
468
|
+
},
|
|
469
|
+
instruction:
|
|
470
|
+
"Enter Fix-Only Mode. Only modify files in allowedFiles. Do NOT add features or new files.",
|
|
471
|
+
},
|
|
472
|
+
null,
|
|
473
|
+
2,
|
|
474
|
+
),
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Clear blocked state for this file if it was blocked
|
|
481
|
+
if (blocked.files.includes(file_path)) {
|
|
482
|
+
blocked.files = blocked.files.filter((f) => f !== file_path);
|
|
483
|
+
blocked.violations = blocked.violations.filter((v) => v.file !== file_path);
|
|
484
|
+
blockedState.set(projectKey, blocked);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
content: [
|
|
489
|
+
{
|
|
490
|
+
type: "text",
|
|
491
|
+
text: JSON.stringify(
|
|
492
|
+
{
|
|
493
|
+
status: "PASS",
|
|
494
|
+
message: "✅ Checkpoint passed",
|
|
495
|
+
blockedFiles: blocked.files,
|
|
496
|
+
},
|
|
497
|
+
null,
|
|
498
|
+
2,
|
|
499
|
+
),
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Handle check tool - unified code verification
|
|
507
|
+
*/
|
|
508
|
+
async function handleCheck(args, dirname) {
|
|
509
|
+
const { projectPath = ".", mocks, validate, only, json = true } = args;
|
|
510
|
+
|
|
511
|
+
let output = "# 🔍 Guardrail Check\n\n";
|
|
512
|
+
|
|
513
|
+
try {
|
|
514
|
+
let cmd = `node "${path.join(dirname, "..", "bin", "guardrail.js")}" check`;
|
|
515
|
+
if (mocks) cmd += " --mocks";
|
|
516
|
+
if (validate) cmd += " --validate";
|
|
517
|
+
if (only?.length) cmd += ` --only=${only.join(",")}`;
|
|
518
|
+
if (json) cmd += " --json";
|
|
519
|
+
|
|
520
|
+
const result = execSync(cmd, {
|
|
521
|
+
cwd: path.resolve(projectPath),
|
|
522
|
+
encoding: "utf8",
|
|
523
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
if (json) {
|
|
527
|
+
return { content: [{ type: "text", text: result }] };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
output += result;
|
|
531
|
+
} catch (err) {
|
|
532
|
+
output += `⚠️ Check error: ${err.message}\n`;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return { content: [{ type: "text", text: output }] };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Handle ship tool - Go/No-Go decision
|
|
540
|
+
*/
|
|
541
|
+
async function handleShip(args, dirname) {
|
|
542
|
+
const { projectPath = ".", ci, runtime, url, badge, report, evidence } = args;
|
|
543
|
+
|
|
544
|
+
let output = "# 🚀 Guardrail Ship\n\n";
|
|
545
|
+
|
|
546
|
+
try {
|
|
547
|
+
let cmd = `node "${path.join(dirname, "..", "bin", "guardrail.js")}" ship`;
|
|
548
|
+
if (ci) cmd += " --ci";
|
|
549
|
+
if (runtime && url) cmd += ` --runtime --url="${url}"`;
|
|
550
|
+
if (badge) cmd += " --badge";
|
|
551
|
+
if (evidence) cmd += " --evidence";
|
|
552
|
+
cmd += " --json";
|
|
553
|
+
|
|
554
|
+
const result = execSync(cmd, {
|
|
555
|
+
cwd: path.resolve(projectPath),
|
|
556
|
+
encoding: "utf8",
|
|
557
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
return { content: [{ type: "text", text: result }] };
|
|
561
|
+
} catch (err) {
|
|
562
|
+
// Ship command exits non-zero for NO-GO
|
|
563
|
+
output += `## 🚫 NO-GO\n\n`;
|
|
564
|
+
output += `${err.message}\n`;
|
|
565
|
+
output += `\nRun \`guardrail fix --plan\` to see fixes.\n`;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return { content: [{ type: "text", text: output }] };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Handle fix tool
|
|
573
|
+
*/
|
|
574
|
+
async function handleFix(args, dirname) {
|
|
575
|
+
const { projectPath = ".", plan = true, apply, pr, scope = "all" } = args;
|
|
576
|
+
|
|
577
|
+
// Check Fix-Only Mode restrictions
|
|
578
|
+
const projectKey = path.resolve(projectPath);
|
|
579
|
+
const blocked = blockedState.get(projectKey);
|
|
580
|
+
|
|
581
|
+
if (blocked?.files.length > 0) {
|
|
582
|
+
return {
|
|
583
|
+
content: [
|
|
584
|
+
{
|
|
585
|
+
type: "text",
|
|
586
|
+
text: JSON.stringify(
|
|
587
|
+
{
|
|
588
|
+
fixOnlyMode: true,
|
|
589
|
+
message:
|
|
590
|
+
"⚠️ Fix-Only Mode active. Fixes restricted to blocked files.",
|
|
591
|
+
allowedFiles: blocked.files,
|
|
592
|
+
scope: scope,
|
|
593
|
+
},
|
|
594
|
+
null,
|
|
595
|
+
2,
|
|
596
|
+
),
|
|
597
|
+
},
|
|
598
|
+
],
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
let output = "# 🔧 Guardrail Fix\n\n";
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
let cmd = `node "${path.join(dirname, "..", "bin", "guardrail.js")}" fix`;
|
|
606
|
+
if (plan && !apply) cmd += " --plan";
|
|
607
|
+
if (apply) cmd += " --apply";
|
|
608
|
+
if (pr) cmd += " --pr";
|
|
609
|
+
cmd += ` --scope=${scope}`;
|
|
610
|
+
|
|
611
|
+
const result = execSync(cmd, {
|
|
612
|
+
cwd: path.resolve(projectPath),
|
|
613
|
+
encoding: "utf8",
|
|
614
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
output += result;
|
|
618
|
+
} catch (err) {
|
|
619
|
+
output += `⚠️ Fix error: ${err.message}\n`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return { content: [{ type: "text", text: output }] };
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Handle status tool
|
|
627
|
+
*/
|
|
628
|
+
async function handleStatus(args, dirname) {
|
|
629
|
+
const { projectPath = "." } = args;
|
|
630
|
+
const projectKey = path.resolve(projectPath);
|
|
631
|
+
const blocked = blockedState.get(projectKey) || { files: [], violations: [] };
|
|
632
|
+
|
|
633
|
+
const status = {
|
|
634
|
+
healthy: true,
|
|
635
|
+
version: "2.0.0",
|
|
636
|
+
checkpoint: {
|
|
637
|
+
strictness: currentStrictness,
|
|
638
|
+
blockedFiles: blocked.files,
|
|
639
|
+
fixOnlyMode: blocked.files.length > 0,
|
|
640
|
+
violations: blocked.violations.length,
|
|
641
|
+
},
|
|
642
|
+
lastRun: null,
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// Try to read last run info
|
|
646
|
+
try {
|
|
647
|
+
const summaryPath = path.join(projectPath, ".guardrail", "summary.json");
|
|
648
|
+
const summary = JSON.parse(await fs.readFile(summaryPath, "utf-8"));
|
|
649
|
+
status.lastRun = {
|
|
650
|
+
timestamp: summary.timestamp,
|
|
651
|
+
score: summary.score,
|
|
652
|
+
canShip: summary.canShip,
|
|
653
|
+
};
|
|
654
|
+
} catch {
|
|
655
|
+
// No last run
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
content: [
|
|
660
|
+
{
|
|
661
|
+
type: "text",
|
|
662
|
+
text: JSON.stringify(status, null, 2),
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Handle set_strictness tool
|
|
670
|
+
*/
|
|
671
|
+
function handleSetStrictness(args) {
|
|
672
|
+
const { level, projectPath = "." } = args;
|
|
673
|
+
|
|
674
|
+
if (!STRICTNESS_LEVELS.includes(level)) {
|
|
675
|
+
return {
|
|
676
|
+
isError: true,
|
|
677
|
+
content: [
|
|
678
|
+
{
|
|
679
|
+
type: "text",
|
|
680
|
+
text: `Invalid strictness level: ${level}. Valid: ${STRICTNESS_LEVELS.join(", ")}`,
|
|
681
|
+
},
|
|
682
|
+
],
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const previousLevel = currentStrictness;
|
|
687
|
+
currentStrictness = level;
|
|
688
|
+
|
|
689
|
+
// Clear blocked state when changing strictness (they'll need to re-check)
|
|
690
|
+
const projectKey = path.resolve(projectPath);
|
|
691
|
+
blockedState.delete(projectKey);
|
|
692
|
+
|
|
693
|
+
return {
|
|
694
|
+
content: [
|
|
695
|
+
{
|
|
696
|
+
type: "text",
|
|
697
|
+
text: JSON.stringify(
|
|
698
|
+
{
|
|
699
|
+
success: true,
|
|
700
|
+
previousLevel,
|
|
701
|
+
currentLevel: currentStrictness,
|
|
702
|
+
message: `Strictness changed from ${previousLevel} to ${currentStrictness}. Blocked state cleared.`,
|
|
703
|
+
},
|
|
704
|
+
null,
|
|
705
|
+
2,
|
|
706
|
+
),
|
|
707
|
+
},
|
|
708
|
+
],
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Route tool calls to handlers
|
|
714
|
+
*/
|
|
715
|
+
async function handleGuardrail2Tool(name, args, dirname) {
|
|
716
|
+
switch (name) {
|
|
717
|
+
case "checkpoint":
|
|
718
|
+
return await handleCheckpoint(args, dirname);
|
|
719
|
+
case "check":
|
|
720
|
+
return await handleCheck(args, dirname);
|
|
721
|
+
case "ship":
|
|
722
|
+
return await handleShip(args, dirname);
|
|
723
|
+
case "fix":
|
|
724
|
+
return await handleFix(args, dirname);
|
|
725
|
+
case "status":
|
|
726
|
+
return await handleStatus(args, dirname);
|
|
727
|
+
case "set_strictness":
|
|
728
|
+
return handleSetStrictness(args);
|
|
729
|
+
default:
|
|
730
|
+
return {
|
|
731
|
+
isError: true,
|
|
732
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export {
|
|
738
|
+
GUARDRAIL_2_TOOLS,
|
|
739
|
+
handleGuardrail2Tool,
|
|
740
|
+
handleCheckpoint,
|
|
741
|
+
handleCheck,
|
|
742
|
+
handleShip,
|
|
743
|
+
handleFix,
|
|
744
|
+
handleStatus,
|
|
745
|
+
handleSetStrictness,
|
|
746
|
+
CHECKPOINT_RULES,
|
|
747
|
+
STRICTNESS_LEVELS,
|
|
748
|
+
};
|