Opening GitHub repos often means browser hassle. While gh is great, finding and opening still takes steps.
Imagine fuzzy-finding and instantly opening any GitHub repo from your terminal. This Bun script does just that, using gh, fzf, and Bun’s speed.
Let’s see how it works and how to set it up.
Table of contents
Open Table of contents
Prerequisites
Before we start, make sure you have the following tools installed:
- Bun: A fast JavaScript runtime. Installation instructions are on the official Bun website.
- GitHub CLI (
gh
): GitHub’s official command-line interface. Follow the installation guide. Make sure you’ve authenticated withgh auth login
. - fzf: A general-purpose command-line fuzzy finder. Check the fzf repository for installation steps (available via most package managers like Homebrew, apt, etc.).
1. Building the CLI
First, we need two key pieces: Bun’s built-in shell functionality ($
) for running commands, and Node.js’s parseArgs
utility for handling command-line options and arguments.
Secondly, we need to parse the command-line options and arguments, and then use them to construct a command to run.
const { values, positionals } = parseArgs({ args: Bun.argv, // Feed arguments passed to the bun script options: { // Define the --user or -u option user: { short: "u", // Alias -u type: "string", // Expects a value (the username) }, // Define the --help or -h option help: { short: "h", // Alias -h type: "boolean", // It's a flag, either present (true) or not (false) }, }, strict: true, // Disallow any options not defined above allowPositionals: true, // Allow arguments without a preceding flag (our query)});
Adding the help by identifying the --help
or -h
option.
if (values.help) { console.log(`Usage: repos [user] [query]`); console.log(`Parameters: query Search query [optional]`); console.log(`Options: -u, --user GitHub username [optional] -h, --help Show this help message`); process.exit(0);}
2. The Implementation
The basic logic is simple we can use gh repo list
to list all the repos of a user and then use fzf
to select the one we want to open.
The output of this selection is then passed to gh repo view
to open the repo in the browser.
// --- Extracting User and Query ---const user = values.user ?? "";const query = positionals[2] ?? "";
// --- Configuring fzf ---const FZF_ARGS = ["--ansi", '--pointer "⏵"', '--prompt "❯ "'];
// --- Building the Core Command ---let command = `gh repo list ${user} -L 100 | fzf ${FZF_ARGS.join(" ")}`;
// --- Adding Optional fzf Query ---if (query) { command += ` --query ${query}`;}
// --- Executing the Command and Selecting the Repo ---const repo = await $`${{ raw: command }}` .text() .then((text) => text?.split("\t")[0]);
// --- Handling No Selection ---if (!repo) { console.warn("No repo selected."); process.exit(1);}
// --- Opening the Repository ---await $`gh repo view --web ${repo}`.quiet().nothrow();
console.log(`Opening ${repo}...`);
3. Linking the Script
You can run the script using the following command:
bun run repos.ts --user tanishqmanuja
But for convenience, you can also link it so that you can run it directly from your terminal. To do this, you need to setup a project using bun.
mkdir reposcd reposbun init -y
Copy the contents of the repos.ts
file into the index.ts
file and save it. Then add bin property to package.json
{ "name": "@cli/repos", "module": "index.ts", "type": "module", "bin": { "repos": "index.ts" }, "devDependencies": { "@types/bun": "^1.2.10" }, "peerDependencies": { "typescript": "^5.8.3" }}
Then finally, link the script using the following command:
bun link
4. Using the Script
repos # Fuzzy find your reposrepos my-project # Start fzf with "my-project"repos -u octocat # Find repos for 'octocat'repos -h # Show help
Final Code
import { parseArgs } from "node:util";
import { $ } from "bun";
const { values, positionals } = parseArgs({ args: Bun.argv, options: { user: { short: "u", type: "string", }, help: { short: "h", type: "boolean", }, }, strict: true, allowPositionals: true,});
if (values.help) { console.log(`Usage: repos [user] [query]`); console.log(`Parameters: query Search query [optional]`); console.log(`Options: -u, --user GitHub username [optional] -h, --help Show this help`); process.exit(0);}
const user = values.user ?? "";const query = positionals[2] ?? "";
const FZF_ARGS = ["--ansi", '--pointer "⏵"', '--prompt "❯ "'];let command = `gh repo list ${user} -L 100 | fzf ${FZF_ARGS.join(" ")}`;
if (query) { command += ` --query ${query}`;}
const repo = await $`${{ raw: command }}` .text() .then((text) => text?.split("\t")[0]);
if (!repo) { console.warn("No repo selected"); process.exit(1);}
await $`gh repo view --web ${repo}`.quiet().nothrow();