Automating Daily Dev Chores with Claude Code and GitHub Actions: 3 Workflows I Use A developer has automated three daily chores using Claude Code and GitHub Actions: issue triage, changelog generation, and lint fix patching. The workflows run headlessly via `claude -p` in non-interactive mode, reclaiming 6.2 hours per week at roughly $9/month in API costs. The triage workflow uses Claude Haiku to classify new issues from the repo's actual label set, reducing per-issue cost from $0.03 to $0.004. After this article you'll have a GitHub Actions workflow that triages every new issue with Claude, a nightly job that rewrites your stale changelog from real commits, and a claude CLI step that auto-fixes failing lint on a PR branch and pushes the patch back. All three are copy-paste runnable today, and I'll show you the exact line that cost me $14 in wasted API calls before I caught it. The blunt conclusion first: running Claude Code interactively in your terminal is great for building, but terrible for chores . Chores happen when you're asleep, in a meeting, or context-switched onto something else. I was spending roughly 70 minutes a day on three things — labeling issues, writing release notes, and fixing the same trivial lint failures — and none of it needed my brain. The unlock is that @anthropic-ai/claude-code ships a non-interactive mode. claude -p "prompt" runs headlessly, prints to stdout, and exits with a status code. That's the entire bridge between "AI assistant" and "cron job". Once it runs headlessly, GitHub Actions becomes a free scheduler with secrets management, a checkout of your repo, and write access to your PRs already wired up. My real numbers after three weeks: 6.2 hours/week reclaimed, about $9/month in Claude API spend I'm on a metered key for CI, separate from my Max plan , and one embarrassing incident I'll get to in the pitfalls section. The most boring chore is the highest-value to automate, because it happens on someone else's schedule. Every new issue used to sit unlabeled until I got to it. Now Claude reads the issue body, picks from my actual label set, and posts a one-paragraph triage comment. The key design choice: I pass Claude the real label list from the repo via gh , so it can't hallucinate priority: P0 when my labels are prio/high . Grounding the model in real data is the difference between a useful bot and a noisy one. .github/workflows/triage.yml name: Claude issue triage on: issues: types: opened permissions: issues: write contents: read jobs: triage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Claude Code run: npm install -g @anthropic-ai/claude-code - name: Triage with Claude env: ANTHROPIC API KEY: ${{ secrets.ANTHROPIC API KEY }} GH TOKEN: ${{ secrets.GITHUB TOKEN }} BODY: ${{ github.event.issue.body }} NUM: ${{ github.event.issue.number }} run: | LABELS=$ gh label list --limit 100 --json name -q '. .name' | paste -sd ',' SUGGESTION=$ claude -p "You are triaging a GitHub issue. \ Available labels pick 1-3 ONLY from this list : $LABELS. \ Issue text: $BODY. \ Reply with a JSON object: {\"labels\": ... , \"comment\": \"one short paragraph\"}" \ --model claude-haiku-4-5-20251001 --output-format json | jq -r '.result' echo "$SUGGESTION" | jq -r '.labels ' | while read -r L; do gh issue edit "$NUM" --add-label "$L" || true done echo "$SUGGESTION" | jq -r '.comment' | gh issue comment "$NUM" --body-file - Note I use claude-haiku-4-5-20251001 here, not Opus. Triage is a classification task — Haiku does it for roughly a tenth of the cost and finishes in ~4 seconds. Reserve the expensive models for jobs that actually write code. This single decision dropped my triage cost from ~$0.03 to ~$0.004 per issue. Everyone's changelog rots. Mine had a 5-week gap. Instead of a git hook nobody runs, I schedule a 2 a.m. job that diffs the last 24 hours of commits and asks Claude to summarize them into human-readable release notes — then opens a PR so I review before anything merges. The interesting bit is grounding again: I don't ask Claude "what changed lately?" I hand it the commits and forbid invention. Here's the Python that builds the prompt and calls the SDK directly cleaner than shelling out when you need to parse structured input : python scripts/changelog.py import subprocess, datetime, os from anthropic import Anthropic def commits since hours=24 : since = datetime.datetime.now datetime.timezone.utc - datetime.timedelta hours=hours .isoformat out = subprocess.run "git", "log", f"--since={since}", "--no-merges", "--pretty=format:%h %s" , capture output=True, text=True, check=True, return out.stdout.strip def summarize commits: str - str: client = Anthropic api key=os.environ "ANTHROPIC API KEY" msg = client.messages.create model="claude-sonnet-4-6", max tokens=800, messages= { "role": "user", "content": "Turn these git commits into changelog bullets grouped " "under Added / Fixed / Changed. " "Use ONLY information present in the commit messages " "— do not invent features. Commits:\n\n" + commits , } , return msg.content 0 .text if name == " main ": commits = commits since 24 if not commits: print "No commits in window; skipping." raise SystemExit 0 notes = summarize commits today = datetime.date.today .isoformat with open "CHANGELOG.md", "r+", encoding="utf-8" as f: old = f.read f.seek 0 f.write f" {today}\n\n{notes}\n\n{old}" And the workflow that runs it and opens a PR — peter-evans/create-pull-request handles the branch and commit so you keep a human review gate: .github/workflows/changelog.yml name: Nightly changelog on: schedule: - cron: "0 17 " 02:00 JST workflow dispatch: jobs: changelog: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } full history or --since lies to you - uses: actions/setup-python@v5 with: { python-version: "3.12" } - run: pip install anthropic - run: python scripts/changelog.py env: ANTHROPIC API KEY: ${{ secrets.ANTHROPIC API KEY }} - uses: peter-evans/create-pull-request@v6 with: commit-message: "docs: nightly changelog" branch: auto/changelog title: "Nightly changelog update" fetch-depth: 0 is not optional. The default shallow checkout grabs one commit, so git log --since returns almost nothing and your changelog silently stays empty. I lost two nights to a "working" job that produced blank PRs because of this. claude auto-fix loop billed me on every push Here's the failure I promised. My third workflow auto-fixes lint on a PR: it runs ruff , and if it fails, hands the diff to Claude to fix and pushes the result. The naive version triggered on: push . Claude pushes a fix → that push triggers the workflow again → ruff still flags one stylistic rule Claude and the linter disagree on → Claude "fixes" it again → push → loop. It ran 9 times in 4 minutes before I killed it, billing an Opus call each round. $14, gone, on a missing-newline argument between two robots. Two guards fixed it permanently. First, skip the job when the actor is the bot itself. Second, only push if the working tree actually changed: .github/workflows/autofix.yml name: Claude lint autofix on: pull request: types: opened, synchronize jobs: autofix: if: github.actor = 'github-actions bot ' break the loop runs-on: ubuntu-latest permissions: { contents: write, pull-requests: write } steps: - uses: actions/checkout@v4 with: { ref: ${{ github.head ref }} } - run: npm install -g @anthropic-ai/claude-code - run: pip install ruff - name: Fix lint with Claude env: ANTHROPIC API KEY: ${{ secrets.ANTHROPIC API KEY }} run: | if ruff check . ; then claude -p "ruff check failed. Read the ruff output above, \ edit the offending files to fix ONLY the lint errors, \ and make no behavioral changes." \ --model claude-sonnet-4-6 --permission-mode acceptEdits fi - name: Commit only if changed run: | git config user.name "github-actions bot " git config user.email "github-actions bot @users.noreply.github.com" if git diff --quiet ; then git commit -am "style: claude lint autofix" git push else echo "No changes — not pushing." fi The --permission-mode acceptEdits flag is what lets Claude Code edit files unattended without an interactive approval prompt; without it the CLI hangs forever in CI waiting for a keystroke. The git diff --quiet check is your circuit breaker — if Claude decided the code was already fine, you push nothing and the cycle dies. After three weeks the pattern that generalizes: anything where the input is text the model can be grounded in an issue body, a commit range, a linter's stderr and the output is reviewable a comment, a PR, a labeled item is a great candidate. Anything that writes to production without a human gate is not — every one of my workflows ends in a PR or a comment, never a direct merge or deploy. Pick the model per job: Haiku for classification, Sonnet for code edits, Opus only when you genuinely need deep reasoning. Cap your CI key with a monthly budget in the Anthropic console so a runaway loop costs you $14 of annoyance instead of a $400 invoice. Start with the issue triage workflow above — it's the lowest risk, ships value on day one, and proves out your secrets setup before you let the model touch code.