secufusion-mcp 1.0.8 → 1.0.9
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/index.js +62 -18
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -49,6 +49,36 @@ function getCurrentBranch(cwd) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
// ─────────────────────────────────────────────
|
|
52
|
+
// Telemetry & Compression helpers
|
|
53
|
+
// ─────────────────────────────────────────────
|
|
54
|
+
function compressCode(code) {
|
|
55
|
+
return code
|
|
56
|
+
.replace(/\/\*[\s\S]*?\*\//g, "") // Remove multi-line comments
|
|
57
|
+
.replace(/\/\/.*$/gm, "") // Remove single-line comments
|
|
58
|
+
.replace(/^\s*[\r\n]/gm, "") // Remove empty lines
|
|
59
|
+
.replace(/[ \t]+$/gm, ""); // Remove trailing whitespace
|
|
60
|
+
}
|
|
61
|
+
function appendTelemetry(response, inputChars) {
|
|
62
|
+
const inputTokens = Math.ceil(inputChars / 4);
|
|
63
|
+
let outputChars = 0;
|
|
64
|
+
if (response.content && Array.isArray(response.content)) {
|
|
65
|
+
for (const c of response.content) {
|
|
66
|
+
if (c.type === "text" && c.text) {
|
|
67
|
+
outputChars += c.text.length;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const outputTokens = Math.ceil(outputChars / 4);
|
|
72
|
+
const inputCost = (inputTokens / 1000000) * 3.0;
|
|
73
|
+
const outputCost = (outputTokens / 1000000) * 15.0;
|
|
74
|
+
const totalCost = (inputCost + outputCost).toFixed(4);
|
|
75
|
+
const telemetry = `\n\n> 📊 Telemetry\n> Input Tokens: ~${inputTokens}\n> Output Tokens: ~${outputTokens}\n> Estimated Cost: ${totalCost}`;
|
|
76
|
+
if (response.content && response.content.length > 0 && response.content[0].type === "text") {
|
|
77
|
+
response.content[0].text += telemetry;
|
|
78
|
+
}
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
// ─────────────────────────────────────────────
|
|
52
82
|
// run_pre_pr_checks helpers
|
|
53
83
|
// ─────────────────────────────────────────────
|
|
54
84
|
const CONSOLE_LOG_REGEX = [
|
|
@@ -282,6 +312,7 @@ server.tool("manage_branch_state", "Manage the structured JSON state for the cur
|
|
|
282
312
|
.optional()
|
|
283
313
|
.describe("Explicit actionable instruction for resuming. Required for 'update'."),
|
|
284
314
|
}, async ({ action, task_description, reference_file_path, pending_acs, completed_acs, next_step }) => {
|
|
315
|
+
const inputChars = JSON.stringify({ action, task_description, reference_file_path, pending_acs, completed_acs, next_step }).length;
|
|
285
316
|
const cwd = process.cwd();
|
|
286
317
|
const branch = getCurrentBranch(cwd);
|
|
287
318
|
const statePath = path.resolve(STATE_FILE);
|
|
@@ -297,18 +328,18 @@ server.tool("manage_branch_state", "Manage the structured JSON state for the cur
|
|
|
297
328
|
if (action === "read") {
|
|
298
329
|
const branchState = state[branch];
|
|
299
330
|
if (!branchState) {
|
|
300
|
-
return {
|
|
331
|
+
return appendTelemetry({
|
|
301
332
|
content: [
|
|
302
333
|
{
|
|
303
334
|
type: "text",
|
|
304
335
|
text: `No state found for branch '${branch}'. Please initialize first.`,
|
|
305
336
|
},
|
|
306
337
|
],
|
|
307
|
-
};
|
|
338
|
+
}, inputChars);
|
|
308
339
|
}
|
|
309
|
-
return {
|
|
340
|
+
return appendTelemetry({
|
|
310
341
|
content: [{ type: "text", text: JSON.stringify(branchState, null, 2) }],
|
|
311
|
-
};
|
|
342
|
+
}, inputChars);
|
|
312
343
|
}
|
|
313
344
|
if (action === "initialize") {
|
|
314
345
|
state[branch] = {
|
|
@@ -320,31 +351,42 @@ server.tool("manage_branch_state", "Manage the structured JSON state for the cur
|
|
|
320
351
|
last_updated: new Date().toISOString()
|
|
321
352
|
};
|
|
322
353
|
writeFile(statePath, JSON.stringify(state, null, 2));
|
|
323
|
-
|
|
354
|
+
let refText = "";
|
|
355
|
+
if (reference_file_path) {
|
|
356
|
+
const refPath = resolve(reference_file_path);
|
|
357
|
+
const content = readFileSafe(refPath);
|
|
358
|
+
if (content) {
|
|
359
|
+
refText = `\n\n**Reference Code (Compressed):**\n\`\`\`\n${compressCode(content)}\n\`\`\``;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
refText = `\n\n*(Warning: reference file '${reference_file_path}' not found)*`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return appendTelemetry({
|
|
324
366
|
content: [
|
|
325
367
|
{
|
|
326
368
|
type: "text",
|
|
327
|
-
text: `✅ Branch state initialized for '${branch}'.\n\nState:\n${JSON.stringify(state[branch], null, 2)}`,
|
|
369
|
+
text: `✅ Branch state initialized for '${branch}'.\n\nState:\n${JSON.stringify(state[branch], null, 2)}${refText}`,
|
|
328
370
|
},
|
|
329
371
|
],
|
|
330
|
-
};
|
|
372
|
+
}, inputChars);
|
|
331
373
|
}
|
|
332
374
|
if (action === "update") {
|
|
333
375
|
if (!state[branch]) {
|
|
334
|
-
return {
|
|
376
|
+
return appendTelemetry({
|
|
335
377
|
isError: true,
|
|
336
378
|
content: [
|
|
337
379
|
{ type: "text", text: `No state found for branch '${branch}'. Initialize first.` },
|
|
338
380
|
],
|
|
339
|
-
};
|
|
381
|
+
}, inputChars);
|
|
340
382
|
}
|
|
341
383
|
if (!next_step) {
|
|
342
|
-
return {
|
|
384
|
+
return appendTelemetry({
|
|
343
385
|
isError: true,
|
|
344
386
|
content: [
|
|
345
387
|
{ type: "text", text: "ERROR: 'next_step' is required when updating." },
|
|
346
388
|
],
|
|
347
|
-
};
|
|
389
|
+
}, inputChars);
|
|
348
390
|
}
|
|
349
391
|
if (pending_acs)
|
|
350
392
|
state[branch].pending_acs = pending_acs;
|
|
@@ -353,16 +395,16 @@ server.tool("manage_branch_state", "Manage the structured JSON state for the cur
|
|
|
353
395
|
state[branch].next_step = next_step;
|
|
354
396
|
state[branch].last_updated = new Date().toISOString();
|
|
355
397
|
writeFile(statePath, JSON.stringify(state, null, 2));
|
|
356
|
-
return {
|
|
398
|
+
return appendTelemetry({
|
|
357
399
|
content: [
|
|
358
400
|
{
|
|
359
401
|
type: "text",
|
|
360
402
|
text: `✅ Branch state updated for '${branch}'. Next step recorded:\n> ${next_step}`,
|
|
361
403
|
},
|
|
362
404
|
],
|
|
363
|
-
};
|
|
405
|
+
}, inputChars);
|
|
364
406
|
}
|
|
365
|
-
return { content: [{ type: "text", text: "Invalid action." }] };
|
|
407
|
+
return appendTelemetry({ content: [{ type: "text", text: "Invalid action." }] }, inputChars);
|
|
366
408
|
});
|
|
367
409
|
// ─── Tool 2: log_rejected_pattern ────────────────────────────────────────────
|
|
368
410
|
server.tool("log_rejected_pattern", "Record a coding pattern that was rejected by the team so it is never repeated. " +
|
|
@@ -390,6 +432,7 @@ server.tool("log_rejected_pattern", "Record a coding pattern that was rejected b
|
|
|
390
432
|
.optional()
|
|
391
433
|
.describe("Optional: the file or code area where this was observed."),
|
|
392
434
|
}, async ({ pattern, reason, category, file_context }) => {
|
|
435
|
+
const inputChars = JSON.stringify({ pattern, reason, category, file_context }).length;
|
|
393
436
|
const rejectedPath = resolve(REJECTED_FILE);
|
|
394
437
|
let records = [];
|
|
395
438
|
const existing = readFileSafe(rejectedPath);
|
|
@@ -411,7 +454,7 @@ server.tool("log_rejected_pattern", "Record a coding pattern that was rejected b
|
|
|
411
454
|
};
|
|
412
455
|
records.push(newEntry);
|
|
413
456
|
writeFile(rejectedPath, JSON.stringify(records, null, 2));
|
|
414
|
-
return {
|
|
457
|
+
return appendTelemetry({
|
|
415
458
|
content: [
|
|
416
459
|
{
|
|
417
460
|
type: "text",
|
|
@@ -422,7 +465,7 @@ server.tool("log_rejected_pattern", "Record a coding pattern that was rejected b
|
|
|
422
465
|
`This will be checked automatically in all future architectural suggestions.`,
|
|
423
466
|
},
|
|
424
467
|
],
|
|
425
|
-
};
|
|
468
|
+
}, inputChars);
|
|
426
469
|
});
|
|
427
470
|
// ─── Tool 3: run_pre_pr_checks ───────────────────────────────────────────────
|
|
428
471
|
server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks across the workspace. " +
|
|
@@ -448,6 +491,7 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks acr
|
|
|
448
491
|
.optional()
|
|
449
492
|
.describe("Explicitly skip specific checks. Use sparingly — document the reason in your PR description."),
|
|
450
493
|
}, async ({ work_item_id, root_dir, skip_checks = [] }) => {
|
|
494
|
+
const inputChars = JSON.stringify({ work_item_id, root_dir, skip_checks }).length;
|
|
451
495
|
const scanRoot = root_dir ? path.resolve(root_dir) : process.cwd();
|
|
452
496
|
const errors = [];
|
|
453
497
|
const warnings = [];
|
|
@@ -613,10 +657,10 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks acr
|
|
|
613
657
|
`- **Ready to raise PR** 🚀\n` +
|
|
614
658
|
rejectedReminder;
|
|
615
659
|
}
|
|
616
|
-
return {
|
|
660
|
+
return appendTelemetry({
|
|
617
661
|
content: [{ type: "text", text: report }],
|
|
618
662
|
isError: hasErrors,
|
|
619
|
-
};
|
|
663
|
+
}, inputChars);
|
|
620
664
|
});
|
|
621
665
|
// ─────────────────────────────────────────────
|
|
622
666
|
// Start transport
|