Draft for a new blog post on lianghuiyi.com, written to anchor the github-agent-runner launch and cross-post to dev.to + HN (regular story, not Show HN).
Status
Title options
- The sed patch that makes your Claude subscription work inside GitHub Actions
- Running scheduled Claude agents on your GitHub repos, using the subscription you already pay for
- Putting idle Claude quota to work: scheduled agents on GitHub Actions
Draft body
Most of us pay for a Claude subscription and actively use it a few hours a day. The quota sits idle the rest of the time.
GitHub has an extension called `gh-aw` (agentic workflows) that turns any repo into a host for scheduled AI agents. The agents triage issues, review PRs nitpick-style, summarize repo activity daily, open draft PRs when docs drift from code. They run on a cron inside GitHub Actions.
The natural pitch is: use the Claude subscription I'm already paying for to run these agents, so the idle capacity does something useful overnight.
For a few months, this was straightforward. Then it stopped being.
What changed
gh-aw originally supported a secret called `CLAUDE_CODE_OAUTH_TOKEN` natively, via an internal `AlternativeSecrets` mechanism. You'd set the secret on your repo, gh-aw's compiler would wire it up, and your subscription-authenticated Claude Code CLI would run inside Actions.
PR #16757 removed that mechanism on 2026-02-19. The reason, from the PR discussion, was refactoring around a cleaner `AuthDefinition` scaffolding (PR #20473, merged 2026-03-11) that would eventually allow richer auth modeling including OIDC. Issue #16498 tracks the re-introduction. Until that ships, compiled gh-aw workflows only know how to wire up `ANTHROPIC_API_KEY`.
Which means: out of the box, every workflow run bills the Anthropic API per token. Your subscription sits unused.
The obvious fix, and why it silently fails
The obvious fix is to edit the compiled `.lock.yml` file gh-aw produces and swap `ANTHROPIC_API_KEY` for `CLAUDE_CODE_OAUTH_TOKEN` everywhere. One sed command:
```bash
sed -i 's/ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN/g' \
.github/workflows/.lock.yml
```
I tried this. The workflow ran. It didn't error out in Actions. But when I inspected the agent logs, every run ended with:
```
Not logged in · Please run /login
```
The agent never authenticated, so it didn't do anything useful. But nothing in the outer workflow noticed. The job was green.
I spent an evening figuring out why. The answer is in how gh-aw sandboxes its agent process.
The sandbox carve-out
gh-aw runs its Claude calls inside a lightweight sandbox (`awf`) that strips certain environment variables from the inner process before executing the CLI. The stripping is controlled by `--exclude-env ` flags. The generated `.lock.yml` has, by design, a line like:
```yaml
awf --exclude-env ANTHROPIC_API_KEY ...
```
The rationale is a security default. When gh-aw is using the Anthropic API proxy pattern, authentication happens outside the inner sandbox. The inner CLI doesn't need to see the key. Stripping it prevents accidental leaks if the inner agent writes env dumps to logs.
My sed pass had replaced `ANTHROPIC_API_KEY` everywhere, including inside that flag. So the sandbox was now stripping `CLAUDE_CODE_OAUTH_TOKEN` from the inner process. The CLI ran with no auth. It reported "Not logged in." Actions reported success because the outer job completed.
The fix is a two-pass sed. First, replace everywhere. Then, put the name back inside the `--exclude-env` clause:
```bash
Pass 1: swap everywhere
sed -i 's/ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN/g' \
.github/workflows/.lock.yml
Pass 2: restore the sandbox carve-out
sed -i 's/--exclude-env CLAUDE_CODE_OAUTH_TOKEN/--exclude-env ANTHROPIC_API_KEY/g' \
.github/workflows/.lock.yml
```
The second pass is the critical one. It looks like a no-op because `ANTHROPIC_API_KEY` doesn't exist on the host anymore. That's fine. `--exclude-env NAME` where `NAME` doesn't exist is harmless. What matters is that your actual OAuth token is no longer being stripped.
Verification is two grep counts:
```bash
$ grep -c ANTHROPIC_API_KEY .lock.yml
2 # only inside --exclude-env
$ grep -c CLAUDE_CODE_OAUTH_TOKEN .lock.yml
9 # everywhere else
```
If the first count is zero, you over-patched. If it's over two, you missed occurrences. The second count varies a bit by workflow but nine is typical.
The fragility
Every time you run `gh aw compile`, the `.lock.yml` gets regenerated from the source `.md` and the patch is reverted. Same for `gh aw upgrade`, `gh aw fix`, or editing the source. The patch is not a config option. It's a post-hoc edit you re-apply on every recompile.
This is annoying but tractable. I automated it.
Why this is fine, ToS-wise
gh-aw doesn't call Anthropic's Messages API directly. Its compiled `.lock.yml` shells out to the official `@anthropic-ai/claude-code` npm CLI (the version is pinned in the lock). The CLI is on Anthropic's OAuth-eligible product allow-list alongside Claude.ai, Claude Desktop, and Claude Cowork.
The relevant clause in Anthropic's Consumer Terms says subscription access applies "except when you are accessing our Services via an Anthropic API Key or where we otherwise explicitly permit it." Running the official CLI with a subscription OAuth token inside a GitHub Actions runner is the same pattern documented at code.claude.com/docs/en/github-actions.
What is not allowed: using the OAuth token (`sk-ant-oat01-...`) to call the Messages API directly. That path is explicitly rejected (anthropics/claude-code#37205). The official CLI is the required intermediary. In the gh-aw flow, that intermediary is already in place, which is why this works.
What I built
After applying this fix manually to seven workflows across two repos, I packaged it into a Claude Code plugin: github-agent-runner.
Two commands:
- `/discover-workflows` inspects your repo (language, CI setup, recent activity) using only local `git` and filesystem tools. Fetches the current upstream catalog from githubnext/agentics at runtime, reads frontmatter, and recommends up to three workflows that fit your repo with one-sentence reasons.
- `/install-workflow` walks you through installation: prerequisite checks, auth path (existing secret detected and reused if present), `gh aw add`, the engine default quirk (agentics workflows without explicit `engine: claude` fall back to Copilot auth), the two-pass sed patch, grep verification, and a reminder about `gh aw compile` reverting the patch.
First install with no arguments pitches `daily-repo-status` as a zero-risk starter. It opens a daily issue summarizing repo activity. Low stakes, visible output, no write permissions beyond issue creation. Good first taste before committing to anything heavier.
The repo dogfoods eight workflows on itself so the wiring is visible end to end.
The cost math
If your repo is public, GitHub Actions minutes are unlimited. Your Claude subscription is already a fixed monthly cost. Running scheduled AI agents on your own code, across any number of repos you own, has zero marginal cost in this configuration.
It won't stay this way. When gh-aw finishes re-introducing native OAuth support through the new `AuthDefinition` scaffolding, the sed patch goes away and this all becomes a configuration flag. I've opened an RFC at github/gh-aw#27259 arguing for a specific surface for this. Until then, the plugin is the path.
Links
Source context: drafted in conversation 2026-04-20. Full history including HN/LinkedIn/X variants in github-agent-runner session.
Draft for a new blog post on lianghuiyi.com, written to anchor the github-agent-runner launch and cross-post to dev.to + HN (regular story, not Show HN).
Status
Title options
Draft body
Most of us pay for a Claude subscription and actively use it a few hours a day. The quota sits idle the rest of the time.
GitHub has an extension called `gh-aw` (agentic workflows) that turns any repo into a host for scheduled AI agents. The agents triage issues, review PRs nitpick-style, summarize repo activity daily, open draft PRs when docs drift from code. They run on a cron inside GitHub Actions.
The natural pitch is: use the Claude subscription I'm already paying for to run these agents, so the idle capacity does something useful overnight.
For a few months, this was straightforward. Then it stopped being.
What changed
gh-aw originally supported a secret called `CLAUDE_CODE_OAUTH_TOKEN` natively, via an internal `AlternativeSecrets` mechanism. You'd set the secret on your repo, gh-aw's compiler would wire it up, and your subscription-authenticated Claude Code CLI would run inside Actions.
PR #16757 removed that mechanism on 2026-02-19. The reason, from the PR discussion, was refactoring around a cleaner `AuthDefinition` scaffolding (PR #20473, merged 2026-03-11) that would eventually allow richer auth modeling including OIDC. Issue #16498 tracks the re-introduction. Until that ships, compiled gh-aw workflows only know how to wire up `ANTHROPIC_API_KEY`.
Which means: out of the box, every workflow run bills the Anthropic API per token. Your subscription sits unused.
The obvious fix, and why it silently fails
The obvious fix is to edit the compiled `.lock.yml` file gh-aw produces and swap `ANTHROPIC_API_KEY` for `CLAUDE_CODE_OAUTH_TOKEN` everywhere. One sed command:
```bash
sed -i 's/ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN/g' \
.github/workflows/.lock.yml
```
I tried this. The workflow ran. It didn't error out in Actions. But when I inspected the agent logs, every run ended with:
```
Not logged in · Please run /login
```
The agent never authenticated, so it didn't do anything useful. But nothing in the outer workflow noticed. The job was green.
I spent an evening figuring out why. The answer is in how gh-aw sandboxes its agent process.
The sandbox carve-out
gh-aw runs its Claude calls inside a lightweight sandbox (`awf`) that strips certain environment variables from the inner process before executing the CLI. The stripping is controlled by `--exclude-env ` flags. The generated `.lock.yml` has, by design, a line like:
```yaml
awf --exclude-env ANTHROPIC_API_KEY ...
```
The rationale is a security default. When gh-aw is using the Anthropic API proxy pattern, authentication happens outside the inner sandbox. The inner CLI doesn't need to see the key. Stripping it prevents accidental leaks if the inner agent writes env dumps to logs.
My sed pass had replaced `ANTHROPIC_API_KEY` everywhere, including inside that flag. So the sandbox was now stripping `CLAUDE_CODE_OAUTH_TOKEN` from the inner process. The CLI ran with no auth. It reported "Not logged in." Actions reported success because the outer job completed.
The fix is a two-pass sed. First, replace everywhere. Then, put the name back inside the `--exclude-env` clause:
```bash
Pass 1: swap everywhere
sed -i 's/ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN/g' \
.github/workflows/.lock.yml
Pass 2: restore the sandbox carve-out
sed -i 's/--exclude-env CLAUDE_CODE_OAUTH_TOKEN/--exclude-env ANTHROPIC_API_KEY/g' \
.github/workflows/.lock.yml
```
The second pass is the critical one. It looks like a no-op because `ANTHROPIC_API_KEY` doesn't exist on the host anymore. That's fine. `--exclude-env NAME` where `NAME` doesn't exist is harmless. What matters is that your actual OAuth token is no longer being stripped.
Verification is two grep counts:
```bash
$ grep -c ANTHROPIC_API_KEY .lock.yml
2 # only inside --exclude-env
$ grep -c CLAUDE_CODE_OAUTH_TOKEN .lock.yml
9 # everywhere else
```
If the first count is zero, you over-patched. If it's over two, you missed occurrences. The second count varies a bit by workflow but nine is typical.
The fragility
Every time you run `gh aw compile`, the `.lock.yml` gets regenerated from the source `.md` and the patch is reverted. Same for `gh aw upgrade`, `gh aw fix`, or editing the source. The patch is not a config option. It's a post-hoc edit you re-apply on every recompile.
This is annoying but tractable. I automated it.
Why this is fine, ToS-wise
gh-aw doesn't call Anthropic's Messages API directly. Its compiled `.lock.yml` shells out to the official `@anthropic-ai/claude-code` npm CLI (the version is pinned in the lock). The CLI is on Anthropic's OAuth-eligible product allow-list alongside Claude.ai, Claude Desktop, and Claude Cowork.
The relevant clause in Anthropic's Consumer Terms says subscription access applies "except when you are accessing our Services via an Anthropic API Key or where we otherwise explicitly permit it." Running the official CLI with a subscription OAuth token inside a GitHub Actions runner is the same pattern documented at code.claude.com/docs/en/github-actions.
What is not allowed: using the OAuth token (`sk-ant-oat01-...`) to call the Messages API directly. That path is explicitly rejected (anthropics/claude-code#37205). The official CLI is the required intermediary. In the gh-aw flow, that intermediary is already in place, which is why this works.
What I built
After applying this fix manually to seven workflows across two repos, I packaged it into a Claude Code plugin: github-agent-runner.
Two commands:
First install with no arguments pitches `daily-repo-status` as a zero-risk starter. It opens a daily issue summarizing repo activity. Low stakes, visible output, no write permissions beyond issue creation. Good first taste before committing to anything heavier.
The repo dogfoods eight workflows on itself so the wiring is visible end to end.
The cost math
If your repo is public, GitHub Actions minutes are unlimited. Your Claude subscription is already a fixed monthly cost. Running scheduled AI agents on your own code, across any number of repos you own, has zero marginal cost in this configuration.
It won't stay this way. When gh-aw finishes re-introducing native OAuth support through the new `AuthDefinition` scaffolding, the sed patch goes away and this all becomes a configuration flag. I've opened an RFC at github/gh-aw#27259 arguing for a specific surface for this. Until then, the plugin is the path.
Links
Source context: drafted in conversation 2026-04-20. Full history including HN/LinkedIn/X variants in github-agent-runner session.