🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI Standup Bot for DevOps Teams with Claude API

Build a Slack bot that generates an accurate daily standup summary for a DevOps team by pulling real signal from GitHub commits, deploy events, and open incidents with Claude API — instead of relying on everyone remembering to type an update.

Shubham4 min read
Share:Tweet

DevOps team standups have a specific failure mode: half the "work" is deploys, incident response, and infra changes that already left a trail in GitHub, your CI system, and PagerDuty — but everyone still manually recaps it from memory. This bot pulls the actual trail and drafts the summary, so standup becomes "confirm and add context" instead of "reconstruct yesterday from memory."

Setup

bash
pip install anthropic PyGithub slack-sdk requests

Data Collectors

python
import anthropic
from github import Github
from datetime import datetime, timedelta
 
client = anthropic.Anthropic()
 
 
def get_yesterdays_commits(repo_names: list[str], github_token: str) -> dict:
    gh = Github(github_token)
    since = datetime.utcnow() - timedelta(hours=24)
    activity = {}
 
    for repo_name in repo_names:
        repo = gh.get_repo(repo_name)
        commits = repo.get_commits(since=since)
        prs = [pr for pr in repo.get_pulls(state="all") if pr.updated_at > since]
 
        activity[repo_name] = {
            "commits": [{"message": c.commit.message, "author": c.commit.author.name} for c in commits],
            "prs_opened": [pr.title for pr in prs if pr.created_at > since],
            "prs_merged": [pr.title for pr in prs if pr.merged and pr.merged_at and pr.merged_at > since],
        }
 
    return activity
 
 
def get_yesterdays_deploys(deploy_webhook_log: str) -> list[dict]:
    """Pull from wherever your deploy events are logged — this example
    reads a simple JSON log written by the CD pipeline."""
    import json
    with open(deploy_webhook_log) as f:
        events = [json.loads(line) for line in f]
    since = datetime.utcnow() - timedelta(hours=24)
    return [e for e in events if datetime.fromisoformat(e["timestamp"]) > since]
 
 
def get_open_incidents(pagerduty_token: str) -> list[dict]:
    import requests
    resp = requests.get(
        "https://api.pagerduty.com/incidents",
        headers={"Authorization": f"Token token={pagerduty_token}"},
        params={"statuses[]": ["triggered", "acknowledged"]}
    )
    return [{"title": i["title"], "urgency": i["urgency"], "created_at": i["created_at"]}
            for i in resp.json()["incidents"]]

Summary Generation

python
SUMMARY_PROMPT = """Generate a concise daily standup summary for a DevOps team
based on this activity data from the last 24 hours.
 
GitHub activity: {github_activity}
Deployments: {deploys}
Open incidents: {incidents}
 
Format as:
**Shipped:** (merged PRs and deploys, grouped by repo/service, one line each)
**In progress:** (open PRs, inferred from activity but not yet merged)
**Incidents:** (open incidents needing attention, or "none" if empty)
 
Keep it factual and terse — this is a status summary, not a narrative.
Skip any section with nothing to report rather than writing "nothing to report"."""
 
 
def generate_standup(github_activity: dict, deploys: list, incidents: list) -> str:
    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=600,
        messages=[{
            "role": "user",
            "content": SUMMARY_PROMPT.format(
                github_activity=github_activity,
                deploys=deploys,
                incidents=incidents,
            )
        }]
    )
    return response.content[0].text

Post to Slack Before Standup

python
from slack_sdk import WebClient
 
def post_standup_draft(channel: str, summary: str, slack_token: str):
    client = WebClient(token=slack_token)
    client.chat_postMessage(
        channel=channel,
        text=f"*Daily Standup Draft — {datetime.utcnow().strftime('%b %d')}*\n\n{summary}\n\n"
             f"_Auto-generated from GitHub + deploy + incident activity. React with 👍 to confirm, "
             f"or reply to add anything manual (PTO, planning work, etc.)_"
    )
 
 
def run_daily_standup_bot():
    repos = ["myorg/api", "myorg/infra", "myorg/frontend"]
    github_activity = get_yesterdays_commits(repos, GITHUB_TOKEN)
    deploys = get_yesterdays_deploys("deploy-log.jsonl")
    incidents = get_open_incidents(PAGERDUTY_TOKEN)
 
    summary = generate_standup(github_activity, deploys, incidents)
    post_standup_draft("#team-standup", summary, SLACK_TOKEN)

Schedule It Before Standup Time

yaml
# .github/workflows/standup-bot.yml
name: Daily Standup Draft
on:
  schedule:
    - cron: "30 8 * * 1-5"    # 8:30am weekdays, 30 min before a 9am standup
  workflow_dispatch:
 
jobs:
  standup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install anthropic PyGithub slack-sdk requests
      - run: python standup_bot.py
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
          PAGERDUTY_TOKEN: ${{ secrets.PAGERDUTY_TOKEN }}

What This Doesn't Replace

This drafts the parts of standup that are already recorded somewhere — deploys, merged PRs, open incidents. It deliberately does not try to infer planning work, PTO, or "what I'm blocked on" — those need a human to say out loud, and pretending an LLM can guess them from GitHub activity alone produces confident-sounding nonsense. The prompt explicitly asks people to add that context on top of the draft, not to trust the draft as complete.


More AI DevOps automation? Read our Build AI ChatOps incident bot for Slack with Claude API and Build AI PR description generator with Claude API.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Build an AI Load Test Scenario Generator with Claude API

Writing realistic k6 or Locust load test scenarios means understanding actual traffic patterns, not just hammering one endpoint. Build a tool that reads your API spec and real traffic logs, then generates realistic load test scripts with Claude API.

S
3 min readRead

Comments