This script allows you to authenticate with a GitHub personal access token and open repos from your user account or any organizations you belong to. It also displays private repos and handles pagination if the repo count exceeds 100 (just scroll to the bottom of the list and you'll see a "Load more..." option, when applicable).
// Menu: Open a GitHub Repo// Description: Launch a GitHub repo in your browser// Author: Mandi Wise// Twitter: @mandiwise// Learn how to create a personal access token for GitHub here:// https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-tokenlet { Octokit } = await npm("octokit");const GH_PERSONAL_ACCESS_TOKEN = await env("GH_PERSONAL_ACCESS_TOKEN");const octokit = new Octokit({ auth: GH_PERSONAL_ACCESS_TOKEN });const ORGS_PER_PAGE = 20;const REPOS_PER_PAGE = 100;// GraphQL operationsconst GetAccounts = `query GetAccounts($first: Int) {viewer {organizations(first: $first) {edges {node {loginnameurl}}}loginnameurl}}`;const ReposPage = `fragment ReposPage on RepositoryConnection {edges {node {namedescriptionurl}}pageInfo {endCursorhasNextPage}}`;const GetOrgRepos = `query GetOrgRepos($first: Int, $after: String, $login: String!) {viewer {organization(login: $login) {repositories(first: $firstafter: $afterorderBy: { field: UPDATED_AT, direction: DESC }) {...ReposPage}}}}${ReposPage}`;const GetUserRepos = `query GetUserRepos($first: Int, $after: String) {viewer {repositories(first: $firstafter: $afterorderBy: {field: UPDATED_AT, direction: DESC}affiliations: OWNER) {...ReposPage}}}${ReposPage}`;// Get user and their organizations in a listlet dots = 0;const accountsPlaceholderIntervalId = setInterval(() => {setPlaceholder(`Loading GitHub accounts`.padEnd(++dots, "."));}, 100);const { viewer } = await octokit.graphql(GetAccounts, { first: ORGS_PER_PAGE });if (!viewer) {exit(1);}const { login, name, url, organizations } = viewer;const accounts = [{ name, value: login, description: url, type: "user" },...organizations.edges.map(({ node: { login, name, url } }) => ({name,value: login,description: url,type: "org"}))].sort((a, b) => (a.name > b.name ? 1 : -1));clearInterval(accountsPlaceholderIntervalId);dots = 0;const accountChoice = await arg("Which account?", accounts);const { type: accountType } = accounts.find(account => accountChoice === account.value);// Get repo list for the user or organizationlet repositoriesAndLoadMore = [];async function fetchRepositories(variables) {const reposPlaceholderIntervalId = setInterval(() => {setPlaceholder(`Loading repositories`.padEnd(++dots, "."));}, 100);let edges, endCursor, hasNextPage;const oldLoadMore = repositoriesAndLoadMore.find(({ value }) =>value.startsWith("load-more-after-"));if (oldLoadMore) {repositoriesAndLoadMore.pop();}if (accountType === "org") {({viewer: {organization: {repositories: {edges,pageInfo: { endCursor, hasNextPage }}}}} = await octokit.graphql(GetOrgRepos, {login: accountChoice,...variables}));} else {({viewer: {repositories: {edges,pageInfo: { endCursor, hasNextPage }}}} = await octokit.graphql(GetUserRepos, variables));}repositoriesAndLoadMore = [...repositoriesAndLoadMore,...edges.map(({ node: { description, name, url } }) => ({name,description,value: url}))];if (hasNextPage) {repositoriesAndLoadMore.push({name: "Load more...",value: `load-more-after-${endCursor}`});}clearInterval(reposPlaceholderIntervalId);dots = 0;if (!repositoriesAndLoadMore.length) {exit(1);}let repoChoice = await arg("Which project?", repositoriesAndLoadMore);if (repoChoice.startsWith("load-more-after-")) {await fetchRepositories({first: REPOS_PER_PAGE,after: repoChoice.split("-").pop()});} else {exec(`open ${repoChoice}`);}}await fetchRepositories({ first: REPOS_PER_PAGE });