How to Connect Claude Code to GitHub and Automate Your Dev Workflow

AI coding agents are most powerful when they’re embedded directly into your development workflow — not just answering questions, but taking actions: reading issues, writing code, committing changes, and opening pull requests. This tutorial walks through exactly how to connect Claude Code to GitHub and automate real development tasks end to end.

By the end, you’ll have a working pipeline where Claude Code reads a GitHub Issue, implements the solution, commits the code, and opens a Pull Request — automatically.


Prerequisites

  • Node.js 18+ installed
  • An Anthropic API key (console.anthropic.com)
  • A GitHub account and a repository to test with
  • Git configured locally
  • Basic familiarity with the terminal

Part 1: Installing and Configuring Claude Code

Step 1: Install Claude Code

npm install -g @anthropic-ai/claude-code

Verify the installation:

claude --version

Step 2: Authenticate with Anthropic

export ANTHROPIC_API_KEY="sk-ant-your-key-here"

For persistence, add it to your shell profile:

# ~/.bashrc or ~/.zshrc
echo 'export ANTHROPIC_API_KEY="sk-ant-your-key-here"' >> ~/.zshrc
source ~/.zshrc

Step 3: Test the installation

Navigate to any project directory and run:

cd your-project
claude

Claude Code will start an interactive session. Type /help to see available commands. Type exit to quit.


Part 2: Connecting Claude Code to GitHub

Step 4: Install and configure the GitHub CLI

Claude Code integrates with GitHub through the gh CLI. Install it:

# macOS
brew install gh

# Ubuntu/Debian
sudo apt install gh

# Windows
winget install --id GitHub.cli

Authenticate:

gh auth login

Follow the prompts to authenticate via browser. Select HTTPS as the protocol.

Verify:

gh auth status

Step 5: Configure your repository for Claude Code

Claude Code uses a CLAUDE.md file at the root of your repository to understand project-specific context — conventions, architecture decisions, commands to run tests, and anything else the agent should know.

Create it:

touch CLAUDE.md

Example CLAUDE.md for a Spring Boot project:

# Project Context for Claude Code

## Stack
- Java 17, Spring Boot 3.x, Maven
- PostgreSQL (local: localhost:5432/mydb)
- JUnit 5 + Mockito for tests

## Key Commands
- Build: `mvn clean compile`
- Test: `mvn test`
- Run: `mvn spring-boot:run`
- Lint: `mvn checkstyle:check`

## Conventions
- Controllers: `src/main/java/com/myapp/controller/`
- Services: `src/main/java/com/myapp/service/`
- Tests must be in `src/test/java/` mirroring main structure
- All public methods require Javadoc
- Branch naming: `feature/issue-{number}-short-description`
- Commit format: `feat: short description (#issue-number)`

## Important
- Never commit directly to `main` or `develop`
- Always run tests before committing
- Database migrations use Flyway — add files to `src/main/resources/db/migration/`

Commit this file:

git add CLAUDE.md
git commit -m "chore: add Claude Code project context"
git push

Part 3: Automating a Complete Task — Issue to Pull Request

This is where it gets powerful. We’ll walk through a real example: a GitHub Issue is opened, and Claude Code handles everything — implementation, tests, commit, and PR.

The Scenario

Imagine this issue exists in your repository:

Issue #42: Add endpoint to return user statistics

We need a new GET endpoint `/api/users/{id}/stats` that returns:
- total_posts: number of posts by the user
- total_comments: number of comments
- account_age_days: days since account creation
- last_active: ISO 8601 timestamp of last activity

Response should be JSON. Add appropriate unit tests.

Step 6: Run Claude Code against the issue

# Start Claude Code in your project directory
cd your-project

# Give Claude Code the task
claude "Read GitHub issue #42 using gh cli, implement the required endpoint following the conventions in CLAUDE.md, write unit tests, run the tests to verify they pass, then create a feature branch, commit the changes, and open a pull request referencing issue #42"

Claude Code will:

  1. Run gh issue view 42 to read the full issue
  2. Explore the existing codebase structure
  3. Identify the relevant files (UserController, UserService, UserRepository, etc.)
  4. Implement the new endpoint
  5. Write unit tests
  6. Run mvn test to verify
  7. Create a branch and commit
  8. Open a PR with gh pr create

What Claude Code actually produces

Here’s an example of the code Claude Code generates for this scenario:

UserStatsDTO.java

package com.myapp.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;

public record UserStatsDTO(
    @JsonProperty("total_posts") long totalPosts,
    @JsonProperty("total_comments") long totalComments,
    @JsonProperty("account_age_days") long accountAgeDays,
    @JsonProperty("last_active") Instant lastActive
) {}

UserController.java (new endpoint)

@GetMapping("/{id}/stats")
public ResponseEntity<UserStatsDTO> getUserStats(@PathVariable Long id) {
    return userService.getUserStats(id)
        .map(ResponseEntity::ok)
        .orElse(ResponseEntity.notFound().build());
}

UserService.java (new method)

public Optional<UserStatsDTO> getUserStats(Long userId) {
    return userRepository.findById(userId).map(user -> {
        long totalPosts = postRepository.countByUserId(userId);
        long totalComments = commentRepository.countByUserId(userId);
        long accountAgeDays = ChronoUnit.DAYS.between(
            user.getCreatedAt(), Instant.now()
        );
        return new UserStatsDTO(
            totalPosts,
            totalComments,
            accountAgeDays,
            user.getLastActiveAt()
        );
    });
}

UserControllerTest.java

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired MockMvc mockMvc;
    @MockBean UserService userService;

    @Test
    @DisplayName("GET /api/users/{id}/stats returns stats for existing user")
    void getUserStats_existingUser_returnsStats() throws Exception {
        var stats = new UserStatsDTO(42L, 17L, 365L, Instant.parse("2024-11-01T10:00:00Z"));
        when(userService.getUserStats(1L)).thenReturn(Optional.of(stats));

        mockMvc.perform(get("/api/users/1/stats"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.total_posts").value(42))
            .andExpect(jsonPath("$.total_comments").value(17))
            .andExpect(jsonPath("$.account_age_days").value(365));
    }

    @Test
    @DisplayName("GET /api/users/{id}/stats returns 404 for unknown user")
    void getUserStats_unknownUser_returns404() throws Exception {
        when(userService.getUserStats(99L)).thenReturn(Optional.empty());

        mockMvc.perform(get("/api/users/99/stats"))
            .andExpect(status().isNotFound());
    }
}

The resulting commit and PR

Claude Code runs these commands automatically:

# Branch creation
git checkout -b feature/issue-42-user-stats-endpoint

# Stage changes
git add src/main/java/com/myapp/dto/UserStatsDTO.java
git add src/main/java/com/myapp/controller/UserController.java
git add src/main/java/com/myapp/service/UserService.java
git add src/test/java/com/myapp/controller/UserControllerTest.java

# Commit
git commit -m "feat: add GET /api/users/{id}/stats endpoint (#42)"

# Push
git push origin feature/issue-42-user-stats-endpoint

# Open PR via GitHub CLI
gh pr create \
  --title "feat: add user statistics endpoint" \
  --body "## Summary
Implements GET /api/users/{id}/stats as requested in #42.

## Changes
- Added UserStatsDTO record
- Added getUserStats() to UserService
- Added controller endpoint with 404 handling
- Added unit tests for both success and not-found cases

## Tests
All tests passing: \`mvn test\` ✅

Closes #42" \
  --base develop \
  --assignee @me

The PR is opened on GitHub referencing the original issue, with a structured description and test confirmation.


Part 4: GitHub Actions Integration

To run Claude Code automatically on every new issue labeled claude, add this GitHub Actions workflow:

.github/workflows/claude-autofix.yml

name: Claude Code Auto-implement

on:
  issues:
    types: [labeled]

jobs:
  claude-implement:
    if: github.event.label.name == 'claude'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code

      - name: Setup GitHub CLI
        run: |
          gh auth status
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Configure Git
        run: |
          git config user.name "Claude Code Bot"
          git config user.email "claude-bot@yourorg.com"

      - name: Run Claude Code on Issue
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
        run: |
          claude --non-interactive "
            Read GitHub issue #$ISSUE_NUMBER using the gh cli.
            Implement the requested changes following CLAUDE.md conventions.
            Run the test suite and ensure all tests pass.
            Create a feature branch named feature/issue-$ISSUE_NUMBER.
            Commit the changes with a conventional commit message referencing #$ISSUE_NUMBER.
            Push the branch and open a pull request referencing issue #$ISSUE_NUMBER.
            If tests fail, do not push — add a comment to the issue explaining what blocked you.
          "

Setting up the required secrets

In your GitHub repository, go to Settings → Secrets and variables → Actions and add:

  • ANTHROPIC_API_KEY — your Anthropic API key from console.anthropic.com

The GITHUB_TOKEN is provided automatically by GitHub Actions.

Triggering the workflow

Label any issue with claude and the workflow runs automatically. The bot will read the issue, implement the solution, and open a PR — you just review and merge.


Part 5: Non-Interactive Mode for Scripts

For use in pipelines, Claude Code supports fully non-interactive execution:

# Run a specific task without prompting
claude --non-interactive "refactor the UserService class to use constructor injection instead of field injection, run tests to verify, and commit the changes"

# Print output only (no interactive UI)
claude --print "explain what the PaymentProcessor class does"

# Allow all operations without confirmation (use carefully)
claude --dangerously-allow-all --non-interactive "update all dependencies to latest versions and fix any breaking changes"

Summary

CapabilityCommand / Config
Installnpm install -g @anthropic-ai/claude-code
Project contextCLAUDE.md in repo root
GitHub integrationgh CLI authenticated
Interactive sessionclaude
Automated taskclaude --non-interactive "task description"
CI/CD pipelineGitHub Actions + ANTHROPIC_API_KEY secret
Issue to PRclaude "read issue #N, implement, test, commit, open PR"

Claude Code turns your GitHub Issues into a task queue. The developer’s job shifts from writing every line to reviewing what the agent produces — and that shift, done right, is a significant productivity multiplier.


References


Jorge David | Dev AI Tools — Practical AI insights for developers since 2004.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *