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.
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
pip install anthropic PyGithub slack-sdk requestsData Collectors
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
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].textPost to Slack Before Standup
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
# .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
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 ChatOps Incident Bot for Slack with Claude API
Build a Slack bot that turns '/incident api is down' into a full incident channel with a Claude-generated initial assessment, relevant runbook links, and the right people paged automatically.
Build an AI Incident Timeline Reconstructor with Claude API
Writing an incident timeline after the fact means manually cross-referencing Slack messages, deploy logs, alert timestamps, and PagerDuty events. Build a tool that pulls all of it together and generates an accurate, chronological timeline with Claude API.
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.