2 May 2024

Identifying Trends in Your Repos’ Issues…with AI!

By Bashbunni

It’s a lot of work trying to keep up with what’s happening in your code repos, let alone deciding what to focus on, but thankfully here’s where AI can come in handy. I don’t think AI can actually do your job for you, but it provides excellent base content to work from, hence why AI is the perfect partner for this type of problem.

So you can access your GitHub issues with the command line and you have an AI chatbot that also uses a CLI. Why not write a little script that can summarize key trends in issues, both for bug reports and enhancement requests? That can help us identify what to focus on. Better yet, let’s automatically get those summaries via email to help us stay on top of things!

An example of the kind of email you will receive from the script. Includes a summary of bug reports and enhancements for the repo
We’re gonna generate something like this and save you from meetings, one email at a time.

Required doodads

You’ll need to have Pop and Mods installed to use the provided script. Both of these programs also require environment variables to be set. You can define these variables in the script, as you’ll see in the example below.

Alternatively, you can set your environment variables in your .bashrc/.zshrc, then source the file with a good ‘ol source ~/.zshrc in your existing terminal session - note that any new sessions will use the updated rc file anyway, so don’t worry about sourcing that file for new sessions.

Because my dotfiles are public, I like to use Skate to protect my API keys, so I don’t accidentally leak any secrets. Here’s how the two options compare:

export OPENAI_API_KEY="<private key>" vs export OPENAI_API_KEY=$(skate get openai-key)

Under the hood


Mods mascot


  • Built ItselfYep

AI on the command line.

Mods is a command line interface for interacting with AI chatbots, meaning, it’s entirely script-friendly. It works with OpenAI-compatible endpoints and LocalAI models. Mods uses GPT-4 by default and supports LocalAI models running on port 8080. It falls back to using GPT-3.5 Turbo if GPT-4 isn’t available. If you wanna learn more, you can read all about it.

For this script to work, you’ll need an OPENAI_API_KEY environment variable set. Get your key.


Pop mascot


  • EmailSent

Send emails from your terminal

Pop is a terminal-based program, offered as both a TUI and CLI, that allows you to send emails from your terminal. All emails send through Resend APIs or you can use a custom SMTP setup.

To use Pop with Resend you’ll need to export a RESEND_API_KEY, you can also set RESEND_FROM to avoid having to type in your sending email. If you run into any difficulties using your own domain with resend, you can always have it send from onboarding@resend.dev.

export RESEND_FROM=pop@charm.sh

Alternatively, you can use a custom SMTP configuration with Pop.

export POP_SMTP_HOST=smtp.gmail.com
export POP_SMTP_PORT=587
export POP_SMTP_USERNAME=pop@charm.sh
export POP_SMTP_PASSWORD=hunter2

For this script to work, you’ll need the RESEND_API_KEY environment variable or custom SMTP server configurations set. Get your key.

Putting it all together

Writing the script

Alright, now that you know how the whole thing works, let’s do something useful.

  1. Set up the OPENAI_API_KEY environment variable.
  2. Set up the RESEND_API_KEY environment variable (or configure your SMTP setup).
  3. Run the following script and provide the path to a local git repository as an argument

export RESEND_API_KEY=$(skate get resend)
export OPENAI_API_KEY=$(skate get open-ai)

# fail if no repo provided
if [ $# -ne 1 ]
    printf "Please provide an absolute path to a local code repository with a GitHub remote upstream."
    exit 1
    cd $1
    ( gh issue list |
    mods "what are recurring themes in bug reports given this output" && gh issue list |
    mods "what are recurring themes in enhancements given this output" ) |
    pop \
      --from onboarding@resend.dev \
      --to youremail@resend.dev \
      --subject $(basename $PWD)

(PSSSST! basename $PWD provides the name of the project whose issues are being summarized)

If you wanted these summaries to recur on a weekly basis (or whatever other regular interval you’d like) you can choose to activate this script in a cron job.

Scheduling the script


Cron jobs are available on Unix-based systems and on Windows through a Unix-based subsystem like WSL (Windows Subsystem for Linux). If you’re on a Windows machine, you can also use Windows Task Scheduler to automate tasks, but that is beyond the scope of this post.

Cron is a daemon to execute scheduled commands. By default, it runs in the user’s home directory.

Crontabs are files that are used to schedule the execution of programs. These files have all the instructions that you might need for the cron job to run. Another, very popular alternative, is Systemd timer that also allows you to schedule tasks. Cron is the more popular option, but can be hard to troubleshoot by comparison to Systemd timers. If you’d like to learn more, you can do so here

Configuring cron

Run crontab -e to create a new crontab (use man crontab or tldr crontab). The first iteration might look something like this:

*/1 * * * * /home/bashbunni/scripts/repo-summary /home/bashbunni/charm/bubbletea >> /home/bashbunni/scripts/repo-summary.log 2>&1

Alas, there’s still room to improve. Try to spot the differences!

*/1 * * * * repo-summary $HOME/charm/bubbletea >> /var/log/repo-summary/bubbletea.log 2>&1

To clean up the script, you have to run chmod +x repo-summary, and move the executable to /usr/bin which allows the script to be executable from anywhere. You’ll also want to move logging to /var/log which is typically where programs post logs on Linux. If you’re not using Linux, you should change this to whatever is idiomatic with your operating system. You need to change the permissions on the /var/log/repo-summary directory with sudo chmod ugo+w /var/log/repo-summary so that the cron job has write access to the file.

That’s a fair amount of steps, so here’s that breakdown as a list:

  1. chmod +x repo-summary
  2. sudo mv repo-summary /usr/bin/repo-summary
  3. sudo mkdir /var/log/repo-summary
  4. sudo chmod ugo+w /var/log/repo-summary

You need sudo for some of these commands given that they’re in protected directories.

Overengineering with containers?!

After spending a bunch of time getting this working in Docker, it turns out it’s not worth containerizing given that it depends on a git repo to work. I included this section anyway because I’m sure at least one developer will look at this and say “I wonder if I could containerize it”… Just like I did.

Pros of a container:

  • User of the script gets a “plug-and-play” experience.
  • Export env variables from host to container.
  • Centralize dependency management.

How to containerize it

The deciding factor is that you need a local git repo to use with the script, due to depending on the GitHub CLI results for mods to work. This gets complicated when you containerize it. If you are curious to see what the Dockerfile might look like, I’ve included it to indulge your curiosities.

FROM golang:1.21-alpine

# TODO Copy your own version of the script to run
# Don't forget, the file has to be in the same directory as the Dockerfile
COPY repo-summary /usr/bin/repo-summary
RUN chmod +x /usr/bin/repo-summary

ENV PATH="/usr/local/go/bin:${PATH}"

# Go apps
RUN go install github.com/charmbracelet/pop@latest
RUN go install github.com/charmbracelet/mods@latest

# gh cli
# If mods and pop were available through apk, we could have just used an alpine
# container instead of using the larger, go-alpine container.
RUN apk add github-cli
RUN rm /var/cache/apk/*

WORKDIR /home/bubbletea

CMD repo-summary

Then you can run it with

docker run -it -v $(pwd):/home/bubbletea -e
GH_TOKEN=$GH_TOKEN -e RESEND_API_KEY=$(skate get resend) -e
OPENAI_API_KEY=$(skate get open-ai) <image-id> repo-summary ./

Okay, okay, fine I’ll break down what that command does. You have to run docker build --tag 'bubbletea-summary' . in the same directory as your Dockerfile to create your custom image. Run the image as a container and start an interactive terminal session in that container with -it. From there, mount the desired repo as a volume (matches the WORKDIR specified in the Dockerfile) with -v. Then pass along your environment variables from the host machine to the container with -e. It’s important to provide the image ID, which you can see with docker images. It will run the repo-summary script in the WORKDIR of the container.

Notice that you need to define the environment variables needed by the container as arguments to the docker run command. If you were to specify these environment variables in the Dockerfile, then they would only be accessible at build time, not during the lifespan of the container. (Thank you, Ayman!)

If you want to challenge yourself, you can try to set up a cron job in the docker container given the information provided. Let us know how you do!

Whatcha think?

Have some feedback on this post? We’d love to hear. Let us know in Discord or via email at vt100@charm.sh.


Read this post in your terminal with Glow:

glow -p https://charm.sh/blog/gh-mods-pop.md Copied!

By Bashbunni

2 May 2024

Bashbunni is a coder and developer advocate at Charm. She supports the community by hacking on cool stuff in public and creating educational content.

Lets chat!

Have a question about a command line thing you’re building? Got an idea for a new feature? Just wanna hang out? You’re always welcome in the Charm Discord.