mcp-feedback-enhanced 0.1.64 â 0.1.65
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 +309 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,7 +16,9 @@ import * as os from 'os';
|
|
|
16
16
|
import * as fs from 'fs';
|
|
17
17
|
import * as path from 'path';
|
|
18
18
|
import * as net from 'net';
|
|
19
|
+
import * as http from 'http';
|
|
19
20
|
import { createRequire } from 'module';
|
|
21
|
+
import { exec } from 'child_process';
|
|
20
22
|
// Read version from package.json (ES module compatible)
|
|
21
23
|
const require = createRequire(import.meta.url);
|
|
22
24
|
const packageJson = require('../package.json');
|
|
@@ -414,9 +416,18 @@ function handleMessage(message) {
|
|
|
414
416
|
}
|
|
415
417
|
/**
|
|
416
418
|
* Request feedback from user via Extension
|
|
419
|
+
* Falls back to browser if extension is not available
|
|
417
420
|
*/
|
|
418
421
|
async function requestFeedback(projectDirectory, summary, timeout, agentName) {
|
|
419
|
-
|
|
422
|
+
// Try to connect to extension first
|
|
423
|
+
try {
|
|
424
|
+
await ensureConnectedForProject(projectDirectory);
|
|
425
|
+
}
|
|
426
|
+
catch (e) {
|
|
427
|
+
// Extension not available, fall back to browser
|
|
428
|
+
debug(`Extension not available, falling back to browser: ${e.message}`);
|
|
429
|
+
return requestFeedbackViaBrowser(projectDirectory, summary, timeout);
|
|
430
|
+
}
|
|
420
431
|
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
421
432
|
return new Promise((resolve, reject) => {
|
|
422
433
|
// Set timeout
|
|
@@ -443,6 +454,303 @@ async function requestFeedback(projectDirectory, summary, timeout, agentName) {
|
|
|
443
454
|
});
|
|
444
455
|
}
|
|
445
456
|
// ============================================================================
|
|
457
|
+
// Browser Fallback
|
|
458
|
+
// ============================================================================
|
|
459
|
+
/**
|
|
460
|
+
* Open system default browser
|
|
461
|
+
*/
|
|
462
|
+
function openBrowser(url) {
|
|
463
|
+
const platform = os.platform();
|
|
464
|
+
let cmd;
|
|
465
|
+
if (platform === 'darwin') {
|
|
466
|
+
cmd = `open "${url}"`;
|
|
467
|
+
}
|
|
468
|
+
else if (platform === 'win32') {
|
|
469
|
+
cmd = `start "" "${url}"`;
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
cmd = `xdg-open "${url}"`;
|
|
473
|
+
}
|
|
474
|
+
exec(cmd, (error) => {
|
|
475
|
+
if (error) {
|
|
476
|
+
debug(`Failed to open browser: ${error.message}`);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Generate HTML page for browser feedback
|
|
482
|
+
*/
|
|
483
|
+
function generateBrowserHtml(summary, port) {
|
|
484
|
+
const escapedSummary = summary
|
|
485
|
+
.replace(/&/g, '&')
|
|
486
|
+
.replace(/</g, '<')
|
|
487
|
+
.replace(/>/g, '>')
|
|
488
|
+
.replace(/"/g, '"')
|
|
489
|
+
.replace(/\n/g, '<br>');
|
|
490
|
+
return `<!DOCTYPE html>
|
|
491
|
+
<html lang="en">
|
|
492
|
+
<head>
|
|
493
|
+
<meta charset="UTF-8">
|
|
494
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
495
|
+
<title>MCP Feedback</title>
|
|
496
|
+
<style>
|
|
497
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
498
|
+
body {
|
|
499
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
500
|
+
background: #1e1e1e;
|
|
501
|
+
color: #d4d4d4;
|
|
502
|
+
min-height: 100vh;
|
|
503
|
+
display: flex;
|
|
504
|
+
align-items: center;
|
|
505
|
+
justify-content: center;
|
|
506
|
+
padding: 20px;
|
|
507
|
+
}
|
|
508
|
+
.container {
|
|
509
|
+
max-width: 800px;
|
|
510
|
+
width: 100%;
|
|
511
|
+
background: #252526;
|
|
512
|
+
border-radius: 12px;
|
|
513
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
|
514
|
+
overflow: hidden;
|
|
515
|
+
}
|
|
516
|
+
.header {
|
|
517
|
+
background: #007acc;
|
|
518
|
+
padding: 16px 24px;
|
|
519
|
+
display: flex;
|
|
520
|
+
align-items: center;
|
|
521
|
+
gap: 12px;
|
|
522
|
+
}
|
|
523
|
+
.header h1 {
|
|
524
|
+
font-size: 18px;
|
|
525
|
+
font-weight: 600;
|
|
526
|
+
color: white;
|
|
527
|
+
}
|
|
528
|
+
.header .icon { font-size: 24px; }
|
|
529
|
+
.content { padding: 24px; }
|
|
530
|
+
.ai-message {
|
|
531
|
+
background: #2d2d2d;
|
|
532
|
+
border: 1px solid #3c3c3c;
|
|
533
|
+
border-radius: 8px;
|
|
534
|
+
padding: 16px;
|
|
535
|
+
margin-bottom: 20px;
|
|
536
|
+
max-height: 400px;
|
|
537
|
+
overflow-y: auto;
|
|
538
|
+
line-height: 1.6;
|
|
539
|
+
}
|
|
540
|
+
.label {
|
|
541
|
+
font-size: 12px;
|
|
542
|
+
color: #888;
|
|
543
|
+
margin-bottom: 8px;
|
|
544
|
+
display: flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
gap: 6px;
|
|
547
|
+
}
|
|
548
|
+
.quick-btns {
|
|
549
|
+
display: flex;
|
|
550
|
+
gap: 8px;
|
|
551
|
+
margin-bottom: 16px;
|
|
552
|
+
flex-wrap: wrap;
|
|
553
|
+
}
|
|
554
|
+
.quick-btn {
|
|
555
|
+
padding: 8px 16px;
|
|
556
|
+
background: transparent;
|
|
557
|
+
border: 1px solid #3c3c3c;
|
|
558
|
+
color: #d4d4d4;
|
|
559
|
+
border-radius: 20px;
|
|
560
|
+
cursor: pointer;
|
|
561
|
+
font-size: 13px;
|
|
562
|
+
transition: all 0.2s;
|
|
563
|
+
}
|
|
564
|
+
.quick-btn:hover {
|
|
565
|
+
background: #3c3c3c;
|
|
566
|
+
border-color: #569cd6;
|
|
567
|
+
}
|
|
568
|
+
textarea {
|
|
569
|
+
width: 100%;
|
|
570
|
+
min-height: 120px;
|
|
571
|
+
padding: 12px;
|
|
572
|
+
background: #2d2d2d;
|
|
573
|
+
border: 1px solid #3c3c3c;
|
|
574
|
+
border-radius: 8px;
|
|
575
|
+
color: #d4d4d4;
|
|
576
|
+
font-size: 14px;
|
|
577
|
+
font-family: inherit;
|
|
578
|
+
resize: vertical;
|
|
579
|
+
margin-bottom: 16px;
|
|
580
|
+
}
|
|
581
|
+
textarea:focus {
|
|
582
|
+
outline: none;
|
|
583
|
+
border-color: #007acc;
|
|
584
|
+
}
|
|
585
|
+
.submit-btn {
|
|
586
|
+
width: 100%;
|
|
587
|
+
padding: 14px;
|
|
588
|
+
background: #007acc;
|
|
589
|
+
border: none;
|
|
590
|
+
border-radius: 8px;
|
|
591
|
+
color: white;
|
|
592
|
+
font-size: 15px;
|
|
593
|
+
font-weight: 600;
|
|
594
|
+
cursor: pointer;
|
|
595
|
+
transition: background 0.2s;
|
|
596
|
+
}
|
|
597
|
+
.submit-btn:hover { background: #0098ff; }
|
|
598
|
+
.submit-btn:disabled {
|
|
599
|
+
background: #3c3c3c;
|
|
600
|
+
cursor: not-allowed;
|
|
601
|
+
}
|
|
602
|
+
.success {
|
|
603
|
+
text-align: center;
|
|
604
|
+
padding: 40px;
|
|
605
|
+
}
|
|
606
|
+
.success .icon { font-size: 48px; margin-bottom: 16px; }
|
|
607
|
+
.success h2 { color: #4ec9b0; margin-bottom: 8px; }
|
|
608
|
+
.success p { color: #888; }
|
|
609
|
+
</style>
|
|
610
|
+
</head>
|
|
611
|
+
<body>
|
|
612
|
+
<div class="container" id="feedbackForm">
|
|
613
|
+
<div class="header">
|
|
614
|
+
<span class="icon">đŦ</span>
|
|
615
|
+
<h1>MCP Feedback</h1>
|
|
616
|
+
</div>
|
|
617
|
+
<div class="content">
|
|
618
|
+
<div class="label">đ¤ AI Summary</div>
|
|
619
|
+
<div class="ai-message">${escapedSummary}</div>
|
|
620
|
+
|
|
621
|
+
<div class="label">đŦ Your Feedback</div>
|
|
622
|
+
<div class="quick-btns">
|
|
623
|
+
<button class="quick-btn" onclick="setFeedback('Continue')">âļī¸ Continue</button>
|
|
624
|
+
<button class="quick-btn" onclick="setFeedback('Looks good')">đ Good</button>
|
|
625
|
+
<button class="quick-btn" onclick="setFeedback('Please fix it')">đ§ Fix</button>
|
|
626
|
+
<button class="quick-btn" onclick="setFeedback('Stop')">âšī¸ Stop</button>
|
|
627
|
+
</div>
|
|
628
|
+
<textarea id="feedback" placeholder="Type your feedback here..."></textarea>
|
|
629
|
+
<button class="submit-btn" id="submitBtn" onclick="submitFeedback()">Send Feedback</button>
|
|
630
|
+
</div>
|
|
631
|
+
</div>
|
|
632
|
+
|
|
633
|
+
<div class="container" id="successMsg" style="display: none;">
|
|
634
|
+
<div class="success">
|
|
635
|
+
<div class="icon">â
</div>
|
|
636
|
+
<h2>Feedback Sent!</h2>
|
|
637
|
+
<p>You can close this window now.</p>
|
|
638
|
+
</div>
|
|
639
|
+
</div>
|
|
640
|
+
|
|
641
|
+
<script>
|
|
642
|
+
function setFeedback(text) {
|
|
643
|
+
document.getElementById('feedback').value = text;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
async function submitFeedback() {
|
|
647
|
+
const feedback = document.getElementById('feedback').value.trim();
|
|
648
|
+
if (!feedback) {
|
|
649
|
+
alert('Please enter your feedback');
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const btn = document.getElementById('submitBtn');
|
|
654
|
+
btn.disabled = true;
|
|
655
|
+
btn.textContent = 'Sending...';
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
const response = await fetch('/submit', {
|
|
659
|
+
method: 'POST',
|
|
660
|
+
headers: { 'Content-Type': 'application/json' },
|
|
661
|
+
body: JSON.stringify({ feedback })
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
if (response.ok) {
|
|
665
|
+
document.getElementById('feedbackForm').style.display = 'none';
|
|
666
|
+
document.getElementById('successMsg').style.display = 'block';
|
|
667
|
+
} else {
|
|
668
|
+
throw new Error('Submit failed');
|
|
669
|
+
}
|
|
670
|
+
} catch (e) {
|
|
671
|
+
btn.disabled = false;
|
|
672
|
+
btn.textContent = 'Send Feedback';
|
|
673
|
+
alert('Failed to send feedback. Please try again.');
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
</script>
|
|
677
|
+
</body>
|
|
678
|
+
</html>`;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Request feedback via browser (fallback when extension not available)
|
|
682
|
+
*/
|
|
683
|
+
async function requestFeedbackViaBrowser(projectDirectory, summary, timeout) {
|
|
684
|
+
return new Promise((resolve, reject) => {
|
|
685
|
+
// Find available port
|
|
686
|
+
const server = http.createServer();
|
|
687
|
+
server.listen(0, '127.0.0.1', () => {
|
|
688
|
+
const address = server.address();
|
|
689
|
+
if (!address || typeof address === 'string') {
|
|
690
|
+
reject(new Error('Failed to start HTTP server'));
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const port = address.port;
|
|
694
|
+
debug(`Browser feedback server started on port ${port}`);
|
|
695
|
+
let feedbackReceived = false;
|
|
696
|
+
// Handle requests
|
|
697
|
+
server.on('request', (req, res) => {
|
|
698
|
+
if (req.method === 'GET' && req.url === '/') {
|
|
699
|
+
// Serve HTML page
|
|
700
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
701
|
+
res.end(generateBrowserHtml(summary, port));
|
|
702
|
+
}
|
|
703
|
+
else if (req.method === 'POST' && req.url === '/submit') {
|
|
704
|
+
// Handle feedback submission
|
|
705
|
+
let body = '';
|
|
706
|
+
req.on('data', chunk => body += chunk);
|
|
707
|
+
req.on('end', () => {
|
|
708
|
+
try {
|
|
709
|
+
const data = JSON.parse(body);
|
|
710
|
+
feedbackReceived = true;
|
|
711
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
712
|
+
res.end(JSON.stringify({ success: true }));
|
|
713
|
+
// Close server and resolve
|
|
714
|
+
setTimeout(() => {
|
|
715
|
+
server.close();
|
|
716
|
+
resolve({
|
|
717
|
+
feedback: data.feedback || '',
|
|
718
|
+
images: []
|
|
719
|
+
});
|
|
720
|
+
}, 1000);
|
|
721
|
+
}
|
|
722
|
+
catch (e) {
|
|
723
|
+
res.writeHead(400);
|
|
724
|
+
res.end('Invalid request');
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
res.writeHead(404);
|
|
730
|
+
res.end('Not found');
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
// Open browser
|
|
734
|
+
const url = `http://127.0.0.1:${port}`;
|
|
735
|
+
debug(`Opening browser: ${url}`);
|
|
736
|
+
openBrowser(url);
|
|
737
|
+
// Timeout
|
|
738
|
+
const timeoutHandle = setTimeout(() => {
|
|
739
|
+
if (!feedbackReceived) {
|
|
740
|
+
server.close();
|
|
741
|
+
reject(new Error(`Browser feedback timeout after ${timeout} seconds`));
|
|
742
|
+
}
|
|
743
|
+
}, timeout * 1000);
|
|
744
|
+
server.on('close', () => {
|
|
745
|
+
clearTimeout(timeoutHandle);
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
server.on('error', (err) => {
|
|
749
|
+
reject(new Error(`Failed to start browser feedback server: ${err.message}`));
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
// ============================================================================
|
|
446
754
|
// MCP Server Setup
|
|
447
755
|
// ============================================================================
|
|
448
756
|
const server = new McpServer({
|