https://user-images.githubusercontent.com/2759340/190688251-8a7a0033-9cce-4dc2-bafe-9c02d06e9f00.mov
Lets you browse your GitHub repos and perform actions on them such as coping/opening the URL or listing the issues/pulls/branches. This script could be improved way more so feel free to leave a comment below with some suggestions.
// Name: GitHub Repos// Description: List GitHub repositories and perform actions on them// Author: Vogelino// Twitter: @soyvogelinoimport "@johnlindquist/kit";const { Octokit } = await npm("octokit");const shodwon = await npm("showdown");const converter = new shodwon.Converter({ghMentions: true,emoji: true,});converter.setFlavor("github");interface RawRepositoryType {id: string;name: string;html_url: string;full_name: string;visibility: string[];description: string;homepage?: string;owner: {login: string;};open_issues_count: number;}interface RawIssueType {id: string;title: string;body: string;labels: {name: string;}[];number: string;user: {login: string;};html_url: string;}interface RawBranchName {id: string;name: string;protected: boolean;}interface OptionType<ValueType = unknown> {name: string;descritpion?: string;preview?: string;value: ValueType;}const auth = await env(`GITHUB_ACCESS_TOKEN`, "Enter your GitHub access token");const octokit = new Octokit({ auth });const {data: { login },} = await octokit.rest.users.getAuthenticated();const universalOptions = [{name: "Browse",description: "Open the url in your default browser",value: "browse",},{name: "Copy URL",description: "Copy the URL to the clipboard",value: "copy",},];const mapRawRepo = (repo: RawRepositoryType) => ({name: repo.full_name,description: [repo.visibility[0].toUpperCase() + repo.visibility.slice(1),repo.description,repo.homepage,].filter(Boolean).join(" ยท "),value: repo,});const mapReposResponse = (response: { data: RawRepositoryType[] }) =>(response.data || []).map(mapRawRepo);async function fetchAllRepos() {return await octokit.paginate(octokit.rest.repos.listForAuthenticatedUser,{ sort: "updated", per_page: 100 },mapReposResponse);}async function fetchRecentRepos() {const res = await octokit.request("GET /user/repos", {sort: "updated",per_page: 50,});return res.data;}async function fetchOwnerRepos() {const res = await octokit.request("GET /user/repos", {sort: "updated",per_page: 50,affiliation: "owner",});return res.data;}function createIssuePullHandler(key: "pull" | "issue") {return async (repo: RawRepositoryType) => {const res = await octokit.request(`GET /repos/{owner}/{repo}/${key}s`, {owner: repo.owner.login,repo: repo.name,pulls: key === "pull",});let { data: items } = res as { data: RawIssueType[] };if (key === "issue") {const pullsRes = (await octokit.request(`GET /repos/{owner}/{repo}/pulls`,{owner: repo.owner.login,repo: repo.name,})) as { data: RawIssueType[] };items = items.filter(({ number }) => !pullsRes.data.find((p) => p.number === number));}if (items.length === 0) {await div(`<div class="p-4 bg-white">No ${key}s</div>`);return;}const itemSelected = await arg(`Search for ${key}s`,items.map((i) => ({name: i.title,description: [`#${i.number}`,i.user.login,i.labels.map((l) => l.name).join(", "),].filter(Boolean).join(" ยท "),preview: `<style>p,h1,h2,h3 { margin-bottom: 8px; }</style><div class="p-4 bg-white"><h1>${i.title}</h1><small>By @${i.user.login} ยท ${i.labels.map((l) => l.name).join(", ")}</small><hr/><br />${i?.body ? converter.makeHtml(i.body) : "โ"}</div>`,value: i,})));const action = await arg("Select an action to perform", [...universalOptions,{name: `Copy ${key} number`,description: `Copy ${key} number to clipboard for reference elswhere (eg. branch-name)`,value: "number",},{name: `Close ${key}`,description: `Close ${key}`,value: "close",},]);switch (action) {case "browse":await browse(itemSelected.html_url);exit();case "copy":await copy(itemSelected.html_url);exit();case "number":await copy(`${itemSelected.number}`);exit();case "close":await await octokit.request(`PATCH /repos/{owner}/{repo}/${key}s/{${key}_number}`,{owner: repo.owner.login,repo: repo.name,[`${key}_number`]: itemSelected.number,state: "closed",});break;}notify(`${action} successful`);const nextHanlder = createIssuePullHandler(key);await nextHanlder(repo);};}async function getAllBranches(repo: RawRepositoryType) {const { data } = await octokit.request(`GET /repos/{owner}/{repo}/branches`, {owner: repo.owner.login,repo: repo.name,});const items = data as RawBranchName[];if (items.length === 0) {await div(`<div class="p-4 bg-white">No branches</div>`);return;}const branchSelected = await arg(`Search for branches`,items.map((b) => ({name: b.name,description: b.protected ? "Protected" : undefined,value: b,})));const action = await arg("Select an action to perform", [{name: `Copy name`,description: `Copy name to clipboard for reference elswhere (eg. in issue)`,value: "name",},{name: `Rename`,value: "rename",},]);switch (action) {case "name":await copy(branchSelected.name);exit();case "rename":const newName = await arg(`What should the new name be? (was ${branchSelected.name})`);await octokit.request(`POST /repos/{owner}/{repo}/branches/{branch}/rename`,{owner: repo.owner.login,repo: repo.name,branch: branchSelected.name,new_name: newName,});break;}notify(`${action} successful`);await getAllBranches(repo);}const issueHandler = createIssuePullHandler(`issue`);const pullHandler = createIssuePullHandler(`pull`);function getTabHandler(getter: () => Promise<OptionType<RawRepositoryType>[]>) {return async function handler() {const repos = await getter();const repoSelected = await arg(`Hello ${login}. Search for a repo`, repos);if (repos.length === 0) {await div(`<div class="p-4 bg-white">No repos</div>`);await handler();}const action = await arg("Select an action to perform",[...universalOptions,repoSelected.open_issues_count > 0 && {name: "Recent issues",description: "List top 10 most recent open issues",value: "issues",},{name: "Pull Requests",description: "List top 10 most recent PRs",value: "prs",},{name: "Branches",description: "List branches",value: "branches",},].filter(Boolean));switch (action) {case "browse":await browse(repoSelected.html_url);exit();case "copy":await copy(repoSelected.html_url);exit();case "issues":await issueHandler(repoSelected);break;case "prs":await pullHandler(repoSelected);break;case "branches":await getAllBranches(repoSelected);break;}notify(`${action} successful`);await handler();};}const recentTab = getTabHandler(fetchRecentRepos);onTab("Recent", recentTab);onTab("Owner", getTabHandler(fetchOwnerRepos));onTab("All", getTabHandler(fetchAllRepos));await recentTab();