Skip to content

Instantly share code, notes, and snippets.

@James-Lam
Last active April 17, 2025 03:41
Show Gist options
  • Save James-Lam/590aebd241a61d96254ef9b4bd1d9a02 to your computer and use it in GitHub Desktop.
Save James-Lam/590aebd241a61d96254ef9b4bd1d9a02 to your computer and use it in GitHub Desktop.
Automatically remove members from Cursor team settings page whose email isn't in the allowed list.
// ==UserScript==
// @name Auto Remove Member (Cursor.com)
// @namespace http://tampermonkey.net/
// @version 0.6
// @description Automatically remove members from Cursor team settings page whose email isn't in the allowed list.
// @author James Lam
// @match https://www.cursor.com/settings
// @grant none
// ==/UserScript==
(function () {
"use strict";
// Only keep members with emails in this list
const ALLOWED_EMAILS = [
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
// Add more approved emails here
];
const AUTO_REFRESH_INTERVAL = 30 * 1000; // 30 seconds
// Helper function to pause execution
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Wait for an element to appear in the DOM
async function waitForElement(selectorFn, timeout = 10000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const el = selectorFn();
if (el) return el;
await sleep(200);
}
return null;
}
async function removeMember() {
// Select all member rows
const rows = document.querySelectorAll("div.cursor-pointer.flex-row.gap-4");
for (const row of rows) {
// Find the email element - email is in a span inside the w-[240px] element
const emailContainer = row.querySelector("span.w-\\[240px\\]");
const email = emailContainer?.querySelector("span")?.textContent?.trim();
// Get name for logging purposes
const nameEl = row.querySelector("span.truncate[title]");
const name = nameEl?.getAttribute("title")?.trim() || "Unknown";
// Check if email is NOT in the allowed list
if (email && !ALLOWED_EMAILS.includes(email)) {
console.log(
`[Cursor Auto Remove] Found unauthorized email: "${email}" for user: "${name}"`
);
const moreBtn = row.querySelector("button");
if (!moreBtn) {
console.warn('No "..." button found for', email);
continue;
}
moreBtn.click();
console.log('[Cursor Auto Remove] Clicked "..." button');
const removeBtn = await waitForElement(() => {
return Array.from(document.querySelectorAll("#dropdown button")).find(
(btn) =>
btn.textContent.trim() === "Remove" &&
btn.classList.contains("text-red-600")
);
});
if (!removeBtn) {
console.warn('[Cursor Auto Remove] Dropdown "Remove" not found');
continue;
}
removeBtn.click();
console.log('[Cursor Auto Remove] Clicked dropdown "Remove"');
const confirmRemoveBtn = await waitForElement(() => {
return Array.from(
document.querySelectorAll(
"span.cursor-pointer.text-red-600.underline"
)
).find((el) => el.textContent.trim().toLowerCase() === "remove");
});
if (!confirmRemoveBtn) {
console.warn('[Cursor Auto Remove] Confirmation "Remove" not found');
continue;
}
confirmRemoveBtn.click();
// Get current date/time for logging
const now = new Date().toLocaleString();
console.log(
`[Cursor Auto Remove] ✅ Successfully removed "${name}" with email "${email}" at ${now}`
);
// Wait a bit before continuing to the next member
await sleep(1000);
}
}
}
// Initialize and run the script when member rows are loaded
const init = setInterval(() => {
const rows = document.querySelectorAll("div.cursor-pointer.flex-row.gap-4");
if (rows.length > 0) {
clearInterval(init);
removeMember();
}
}, 1000);
// 🔄 Auto-refresh periodically
setTimeout(() => {
console.log(
`[Cursor Auto Remove] Refreshing page to check for new members...`
);
location.reload();
}, AUTO_REFRESH_INTERVAL);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment