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:
- Run
gh issue view 42to read the full issue - Explore the existing codebase structure
- Identify the relevant files (UserController, UserService, UserRepository, etc.)
- Implement the new endpoint
- Write unit tests
- Run
mvn testto verify - Create a branch and commit
- 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
| Capability | Command / Config |
|---|---|
| Install | npm install -g @anthropic-ai/claude-code |
| Project context | CLAUDE.md in repo root |
| GitHub integration | gh CLI authenticated |
| Interactive session | claude |
| Automated task | claude --non-interactive "task description" |
| CI/CD pipeline | GitHub Actions + ANTHROPIC_API_KEY secret |
| Issue to PR | claude "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
- Claude Code Official Documentation — docs.anthropic.com/en/docs/claude-code/overview
- Claude Code npm Package — npmjs.com/package/@anthropic-ai/claude-code
- GitHub CLI Documentation — cli.github.com/manual
- GitHub Actions Documentation — docs.github.com/en/actions
- Model Context Protocol (MCP) — modelcontextprotocol.io
- Anthropic API Console — console.anthropic.com
- Claude Code GitHub Actions (official) — github.com/anthropics/claude-code-action
Jorge David | Dev AI Tools — Practical AI insights for developers since 2004.