Copilot Continue

Auto-clicks the "Continue" button when GitHub Copilot gets stuck

当前为 2025-06-28 提交的版本,查看 最新版本

// ==UserScript==
// @name        Copilot Continue
// @namespace   https://snomiao.com
// @match       *://*/*
// @grant       none
// @version     1.2.5
// @author      snomiao
// @description Auto-clicks the "Continue" button when GitHub Copilot gets stuck
// @homepage    https://github.com/snomiao/copilot-continue.user.js
// @supportURL  https://github.com/snomiao/copilot-continue.user.js/issues
// @license     MIT
// @compatible  chrome
// @compatible  firefox
// @compatible  edge
// @compatible  opera
// ==/UserScript==

/*
 * Copilot Continue - A userscript to automatically continue GitHub Copilot
 *
 * This script automatically clicks the "Continue" button when GitHub Copilot
 * shows interruption messages like:
 * - "Copilot has been working on this problem for a while"
 * - "Run command in the terminal"
 * - "Allow task run?"
 *
 * The script runs in VS Code web environments and checks for interruptions every second.
 */

const actionMatchers = {
  cilckContinue: [
    /^Copilot has been working on this problem for a while/,
    /^Run command in terminal/,
    /^Continue to iterate\?/,
    /^Allow task run\?/,
    /^Allow test run\?/,
  ],
  clickGrant: [
    /^To get more relevant Copilot Chat results, we need permission to read the contents of your repository on GitHub./,
  ],
  clickTryAgain: [
    /^The model unexpectedly did not return a response, which may indicate a service issue. Please report a bug./,
    /^Sorry, your request failed. Please try again./,

  ],
  clickRetryIcon: [/^Language model unavailable./],
};

const actions = {
  default: () => {
    console.warn("No action matched. Please check the action matchers.");
  },
  refresh: () => {
    location.href = location.href;
  },
  cilckContinue: () => {
    const btn = $$("a.monaco-button").findLast(
      (e) => e.textContent === "Continue"
    );
    if (!btn) return;
    btn.click();
  },
  clickGrant: () => {
    const btn = $$("a.monaco-button").findLast(
      (e) => e.textContent === "Grant"
    );
    if (!btn) return;
    btn.click();
  },
  clickTryAgain: (
    (tryAgainCount = 0) =>
      () => {
        if (tryAgainCount >= 3) {
          // Refresh the page if we've tried more than 3 times
          location.href = location.href;
          return;
        }
        const btn = $$("a.monaco-button").findLast(
          (e) => e.textContent === "Try Again"
        );
        if (!btn) return;
        btn.click();
        tryAgainCount++;
      }
  )(),
};

const enable = !!document.querySelector("meta#vscode-workbench-auth-session");

// Prevent double execution if loaded both as userscript and extension
if (enable && !window.copilotContinueLoaded) {
  window.copilotContinueLoaded = true;
  main();
}

function main() {
  const clear = useInterval(() => loop(), 1e3);
  return () => clear();
}
function loop() {
  const text = $$("div.rendered-markdown")
    .map((e) => e.innerText)
    .flatMap((e) => (e ? [e] : [])) // empty filter
    .map((e) => e.replace(/\s+/g, " "));

  for (const [action, matchers] of Object.entries(actionMatchers)) {
    if (text.some((s) => matchers.some((m) => s.match(m)))) {
      (actions[action] || actions.default)?.();
      return;
    }
  }
}

function $$(sel) {
  return [...document.querySelectorAll(sel)];
}

function useInterval(...args) {
  const id = setInterval(...args);
  return () => clearInterval(id);
}
d