Skip to content

Instantly share code, notes, and snippets.

@jricardo27
Last active February 12, 2026 08:05
Show Gist options
  • Select an option

  • Save jricardo27/ceaf163312d4e663efdb07eb6dbeeeb9 to your computer and use it in GitHub Desktop.

Select an option

Save jricardo27/ceaf163312d4e663efdb07eb6dbeeeb9 to your computer and use it in GitHub Desktop.
Extract Gemini Code Assist comments - Tampermonkey
// ==UserScript==
// @name Gemini Comment Extractor (Agent Optimized)
// @namespace http://tampermonkey.net/
// @version 2.4
// @description Extract Gemini findings with strict AI Assistant instructions and plain-text optimization
// @author Antigravity
// @match https://github.com/*/*/pull/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const UI_CONFIG = {
primaryColor: '#1a73e8',
backgroundColor: '#ffffff',
textColor: '#24292f',
border: '1px solid #d0d7de',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif'
};
const INSTRUCTIONS = `
⚠️ INSTRUCTIONS FOR AI ASSISTANT:
1. Solve the following issues SEQUENTIALLY.
2. ADD corresponding unit or integration tests for every bug fix or feature change.
3. COMMIT your changes after each individual issue is solved.
4. Do not batch multiple fixes into a single commit.
5. CRITICAL: Respect all git hooks and pre-commit checks. NEVER use '--no-verify' or skip any verification steps.
`.trim();
/**
* Parsing Logic
*/
const Extractor = {
getFileName(threadContainer) {
const fileLink = threadContainer.querySelector('summary a.wb-break-all');
return fileLink ? fileLink.innerText.trim() : 'Unknown File';
},
getLines(threadContainer) {
const start = threadContainer.querySelector('.js-multi-line-preview-start');
const end = threadContainer.querySelector('.js-multi-line-preview-end');
if (start && end) {
return `${start.innerText.trim()} - ${end.innerText.trim()}`;
}
const lineNums = threadContainer.querySelectorAll('td[data-line-number]');
if (lineNums.length > 0) {
return lineNums[lineNums.length - 1].getAttribute('data-line-number');
}
return 'N/A';
},
getPriority(commentBody) {
if (!commentBody) return 'Medium';
const img = commentBody.querySelector('img[src*="priority"], img[data-canonical-src*="priority"]');
if (img) {
return img.getAttribute('alt') || img.getAttribute('data-canonical-src').split('/').pop().replace('.svg', '');
}
return 'Medium';
},
formatCommentWithDiffs(commentBody) {
if (!commentBody) return '';
const bodyClone = commentBody.cloneNode(true);
const suggestedChanges = bodyClone.querySelectorAll('.js-suggested-changes-blob');
suggestedChanges.forEach(blob => {
const rows = blob.querySelectorAll('tr');
let diffText = '\n```diff\n';
rows.forEach(row => {
const codeCell = row.querySelector('.blob-code-inner');
if (!codeCell) return;
let prefix = ' ';
if (codeCell.classList.contains('blob-code-marker-deletion')) prefix = '-';
if (codeCell.classList.contains('blob-code-marker-addition')) prefix = '+';
diffText += `${prefix}${codeCell.innerText}\n`;
});
diffText += '```\n';
blob.outerHTML = diffText;
});
return bodyClone.innerText.trim();
},
isGeminiBot(commentGroup) {
const author = commentGroup.querySelector('.author');
return author && author.innerText.toLowerCase().includes('gemini-code-assist');
},
process() {
const threads = document.querySelectorAll('.review-thread-component');
const results = [];
threads.forEach(thread => {
const isOutdated = thread.querySelector('.Label--warning') && thread.querySelector('.Label--warning').innerText.includes('Outdated');
if (isOutdated) return;
const allCommentGroups = Array.from(thread.querySelectorAll('.timeline-comment-group'));
const botComments = allCommentGroups.filter(group => this.isGeminiBot(group));
if (botComments.length === 0) return;
const targetCommentGroup = botComments[botComments.length - 1];
const commentBody = targetCommentGroup.querySelector('.comment-body');
const rawBodyText = commentBody ? commentBody.innerText.trim() : '';
if (rawBodyText.toLowerCase().includes('summary of changes')) return;
results.push({
fileName: this.getFileName(thread),
lines: this.getLines(thread),
priority: this.getPriority(commentBody),
comment: this.formatCommentWithDiffs(commentBody)
});
});
return results;
}
};
/**
* UI Implementation
*/
function createResultDisplay(data) {
let existing = document.getElementById('gemini-results-container');
if (existing) existing.remove();
const container = document.createElement('div');
container.id = 'gemini-results-container';
Object.assign(container.style, {
margin: '20px', padding: '20px', backgroundColor: UI_CONFIG.backgroundColor,
border: UI_CONFIG.border, borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
fontFamily: UI_CONFIG.fontFamily, color: UI_CONFIG.textColor, clear: 'both'
});
// Header with "Copy for AI" Button
const header = document.createElement('div');
header.style.display = 'flex';
header.style.justifyContent = 'space-between';
header.style.alignItems = 'center';
header.style.marginBottom = '15px';
header.style.borderBottom = `2px solid ${UI_CONFIG.primaryColor}`;
header.style.paddingBottom = '8px';
header.innerHTML = `
<h2 style="margin:0; font-size: 1.5em;">Gemini Findings (${data.length})</h2>
<div style="display: flex; gap: 10px;">
<button id="copy-ai-gemini" style="padding: 6px 12px; background: ${UI_CONFIG.primaryColor}; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Copy for AI</button>
<button id="close-gemini-results" style="border:none; background:none; cursor:pointer; font-size:24px; line-height: 1;">&times;</button>
</div>
`;
container.appendChild(header);
if (data.length === 0) {
container.innerHTML += '<p style="color: #666;">No active Gemini findings found.</p>';
} else {
const list = document.createElement('div');
data.forEach((item, index) => {
const block = document.createElement('div');
Object.assign(block.style, {
marginBottom: '15px', padding: '15px', backgroundColor: '#f6f8fa',
border: '1px solid #d0d7de', borderRadius: '6px'
});
// Structured specifically for clean copy-paste formatting
block.innerHTML = `
<div style="font-weight: bold; margin-bottom: 8px;">ISSUE #${index + 1}</div>
<div><strong>FILE:</strong> ${item.fileName}</div>
<div><strong>LINES:</strong> ${item.lines}</div>
<div><strong>PRIORITY:</strong> ${item.priority.toUpperCase()}</div>
<div style="margin-top: 10px; white-space: pre-wrap; font-family: monospace; font-size: 12px; background: #fff; padding: 12px; border: 1px solid #eee;">${item.comment}</div>
`;
list.appendChild(block);
});
container.appendChild(list);
const footerNote = document.createElement('div');
Object.assign(footerNote.style, {
marginTop: '25px', padding: '15px', backgroundColor: '#fff8c5',
border: '1px solid #d4a72c', borderRadius: '6px', whiteSpace: 'pre-wrap'
});
footerNote.innerText = INSTRUCTIONS;
container.appendChild(footerNote);
}
document.body.appendChild(container);
// Click Logic
document.getElementById('close-gemini-results').onclick = () => container.remove();
document.getElementById('copy-ai-gemini').onclick = function() {
const textToCopy = data.map((item, idx) =>
`ISSUE #${idx + 1}\nFILE: ${item.fileName}\nLINES: ${item.lines}\nPRIORITY: ${item.priority.toUpperCase()}\nCOMMENT:\n${item.comment}\n-----------------------------------------`
).join('\n\n') + `\n\n${INSTRUCTIONS}`;
navigator.clipboard.writeText(textToCopy).then(() => {
this.innerText = 'Copied!';
setTimeout(() => this.innerText = 'Copy for AI', 2000);
});
};
container.scrollIntoView({ behavior: 'smooth' });
}
function injectFloatingButton() {
if (document.getElementById('gemini-floating-btn')) return;
const btn = document.createElement('button');
btn.id = 'gemini-floating-btn';
btn.innerText = 'Extract Gemini';
Object.assign(btn.style, {
position: 'fixed', bottom: '20px', right: '20px', zIndex: '9999',
padding: '12px 24px', backgroundColor: UI_CONFIG.primaryColor, color: 'white',
border: 'none', borderRadius: '50px', cursor: 'pointer', fontWeight: 'bold',
boxShadow: '0 4px 15px rgba(26, 115, 232, 0.4)', transition: 'transform 0.2s'
});
btn.onmouseover = () => btn.style.transform = 'scale(1.05)';
btn.onmouseout = () => btn.style.transform = 'scale(1)';
btn.onclick = (e) => { e.preventDefault(); createResultDisplay(Extractor.process()); };
document.body.appendChild(btn);
}
const observer = new MutationObserver(() => injectFloatingButton());
observer.observe(document.body, { childList: true, subtree: true });
injectFloatingButton();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment