2025-08-24

How We Automated GitHub Issues with Free AI

We Gave Our GitHub Issues an AI Assistant

Building on the foundation: This artist is an extention to teaching Claude Code to be consistent and turning prompts into reusable commands. If you’re new to CLAUDE.md files, start there.

We’re a small team that runs everything through GitHub Issues. I’d already invested in Claude Code Pro and documented our patterns with CLAUDE.md files. That setup was working great for complex development.

But I kept seeing routing simple tasks pile up - the kind that take 5 minutes to fix but 30 minutes of context switching. I wanted to delegate these without adding another subscription.

So I setup a two-tier system: Claude handles the complex work, and free Gemini takes the simple stuff. Both read from the same CLAUDE.md files, so the code stays consistent.

Now when someone reports a small change or bug, we comment @gemini-cli fix this and get back to work. Gemini creates the branch, fixes the issue, runs tests, and opens a PR.

Why This Made Sense

Simple Tasks Are Perfect for Automation

Simple tasks usually have a well-defined scope, and if not, then they can be easier to define. “Change button color to …” or “Update docs for endpoint” - these have clear boundaries. They’re low risk, easy to review, easy to revert if needed. Clear success criteria too - tests pass, issue closed.

These tasks add up to real time over weeks. And since we already documented our patterns in CLAUDE.md files, they apply directly.

Building on What We Had

Claude Code Pro was already handling complex development. CLAUDE.md files had our patterns documented and proven. GitHub Issues provided a natural queue for work items. The next logical step was to automate the automatable.

How We Split the Work

Claude Code handles complex features, pair programming, architecture work. Gemini (free) takes simple fixes, test updates, documentation, routine changes. Both use the same patterns from CLAUDE.md, so we get consistent code regardless of which AI writes it.

Cost structure stays at $199/month total - no additional subscriptions. Althought you might have need to pay for some requests depending on how frequently you invoke the action.

The Solution: Gemini CLI + Domain Context

Why Gemini?

The free tier covers a lot of ground - we haven’t hit limits yet. There’s a GitHub Action for Gemini CLI that Google maintains. It handles structured tasks well and can execute shell commands reliably.

The Architecture

# Two-tier AI system:
Claude Code Pro → Complex development, architecture decisions
Gemini CLI → PR automation, routine fixes

Initializing Gemini for the Project

Ran gemini /init to create a root GEMINI.md, then curated it to fit the project. After that, I added companion files alongside each CLAUDE.md:

# app/models/GEMINI.md

# Model Patterns

Read this file for rules and patterns.

@./CLAUDE.md

Simple reference - no duplication. Now the project structure looks like:

CLAUDE.md
GEMINI.md
app/business_logic/CLAUDE.md
app/business_logic/GEMINI.md
app/controllers/CLAUDE.md
app/controllers/GEMINI.md
app/models/CLAUDE.md
app/models/GEMINI.md
test/CLAUDE.md
test/GEMINI.md
# ... and so on

Both AIs read the same patterns. Future refactor might extract to AGENT.md, but this works now.

GitHub Actions Setup

Ran gemini /setup-github to generate the workflows in .github/:

  • Created issue comment handler
  • Created PR comment handler
  • Set up Gemini CLI integration

Custom Gemini Commands

Created .gemini/commands/implement.toml for TDD workflow (similar to how I create custom Claude commands):

name = "implement"
description = "Implements a feature or fix according to project patterns"
prompt = """
You are an expert Rails developer implementing a change.

Follow strict TDD process while adhering to patterns in CLAUDE.md files.

Your Process:
1. Analyze the request
2. Consult CLAUDE.md files for patterns
3. Write a failing test
4. Implement minimal code to pass
5. Run tests and linter (bin/rubocop -A)
"""

Now we can trigger from issues:

  • @gemini-cli /implement - uses issue description
  • @gemini-cli /implement change tab order - adds extra context
  • @gemini-cli default the active scope to nil - direct instruction

The Implementation Journey

Phase 1: Initial Setup

# Basic integration
- name: Run Gemini
  uses: google-github-actions/run-gemini-cli@v0

This worked for basic commands, but failed when Gemini tried to run Rails commands. It couldn’t find Ruby or any of the gems - the environment wasn’t persisting between commands.

Phase 2: Solving the Environment Issue

The discovery: Gemini runs commands via bash -c which creates isolated shells. GitHub Actions environment variables don’t persist between commands. But Gemini reads .env files in the working directory.

Added composite action to gemini-cli.yml after checkout:

# .github/gemini-cli.yml
# ...
# I put this after the branch checkout.
# This is a github composite action that contains all the rails app setup steps.
# This composite is used by ci.yml too.
- name: Setup Rails environment
  uses: ./.github/actions/setup-rails-env
  with:
    setup-database: "true"

# For injecting ENV variables into Gemini CLI - is there a better way?
- name: Setup environment for Gemini CLI
  run: |
    touch $GITHUB_WORKSPACE/.env
    cat $GITHUB_WORKSPACE/.github/actions-env/.env >> $GITHUB_WORKSPACE/.env
# ...

This ensures Gemini has everything it needs (Ruby, gems, database) before executing commands.

The static environment file:

# .github/actions-env/.env
# Gemini CLI Ruby/Rails Environment
# These paths must be hardcoded because Gemini runs commands via bash -c
# which creates isolated shells that don't inherit the GitHub Actions environment
PATH=/opt/hostedtoolcache/Ruby/3.3.8/x64/bin:/home/runner/work/commerce_app/commerce_app/vendor/bundle/ruby/3.3.0/bin:$PATH
GEM_HOME=/opt/hostedtoolcache/Ruby/3.3.8/x64/lib/ruby/gems/3.3.0
GEM_PATH=/opt/hostedtoolcache/Ruby/3.3.8/x64/lib/ruby/gems/3.3.0
BUNDLE_PATH=/home/runner/work/commerce_app/commerce_app/vendor/bundle
BUNDLE_APP_CONFIG=/home/runner/work/commerce_app/commerce_app/.bundle
BUNDLE_GEMFILE=/home/runner/work/commerce_app/commerce_app/Gemfile
BUNDLE_DEPLOYMENT=false
BUNDLE_WITHOUT=production

# TODO: Make Ruby version dynamic
# Is there a better solution? Let me know if you find one.

Getting Started

Look at your last 10 closed issues:

  • Which were trivial but took an hour?
  • Which cost you focus on bigger problems?
  • Which were literally just “change this text” or “fix this color”?

Those are your automation targets.

The Minimum Viable Automation

  1. Set up Gemini CLI in ONE workflow
  2. Handle ONE type of issue (e.g., “failing tests”)
  3. Use for one week
  4. Measure: Did it save more time than it took to set up?
  5. If yes, add another issue type
  6. Iterate
    • Be mindful of context files. They need curation.
    • Experiment with prompting, but don’t get too complex
    • Experiment with other models

Why This Works for Small Teams

You can move fast. You can be pragmatic. You already have context. Small products are easier for AI to understand. Your patterns are probably consistent if you have a small footprint, and if not, make them consistent. Your issues are well-defined.

Everyone knows the frustration of losing an hour to a five-minute task because of context switching.

This isn’t about being clever with AI. It’s about being practical with time.

It’s not revolutionary. It’s not magical. It’s just a way to work more efficiently, so you can deliver value in less time.

See It in Action

Triggering Gemini from an Issue

Kickoff Gemini from GitHub Issue

Just comment @gemini-cli on any issue. That’s it.

The Initial Response

Gemini's Response

Gemini acknowledges the request and starts working.

After the Environment Fix

Working Response After Environment Setup

Once the environment is configured correctly, Gemini executes commands, runs tests, and creates the PR.