GitHub Agentic Workflows (gh-aw)#
GitHub Agentic Workflows (gh-aw) is a framework for running AI coding agents—Claude, GitHub Copilot CLI, Codex, Gemini, Crush, or OpenCode—inside GitHub Actions from declarative Markdown configuration. Unlike fixed if-then automation, agents read natural language instructions and make context-aware decisions: reading threads end-to-end, ranking issues by engagement, and drafting tailored comments rather than applying a uniform template.
In better-stale-bot, the two files that matter are better-stale-bot.md (the editable source) and its compiled counterpart better-stale-bot.lock.yml (machine-generated, do not hand-edit).
Official documentation:
Installing better-stale-bot#
Recommended: Use the interactive add-wizard (simplest approach):
# 1. Authenticate with GitHub
gh auth login
# 2. Install the gh-aw CLI extension
gh extension install github/gh-aw
# 3. Add the workflow (interactive — sets secret, adds workflow, opens a PR; may need to manually merge the PR)
gh aw add-wizard dosu-ai/better-stale-bot/better-stale-bot
# Optional: skip secret configuration
# gh aw add-wizard dosu-ai/better-stale-bot/better-stale-bot --skip-secret
# 4. Pull locally
git pull
The add-wizard command walks you through setup step-by-step: checks requirements, prompts for missing secrets, downloads the workflow file from the workflows/ directory (not .github/workflows/—the latter is for live/testing in template repositories), compiles it to YAML, and opens a pull request. You may need to manually merge the PR it creates.
Alternative: Manual installation:
# 1. Install gh-aw
gh auth login
gh extension install github/gh-aw
# 2. Add your engine secret in repo Settings → Secrets and variables → Actions
# (ANTHROPIC_API_KEY for Claude, COPILOT_GITHUB_TOKEN for Copilot, OPENAI_API_KEY for Codex)
# 3. Download, compile, and push
mkdir -p .github/workflows
curl -o .github/workflows/better-stale-bot.md \
https://raw.githubusercontent.com/dosu-ai/better-stale-bot/main/workflows/better-stale-bot.md
gh aw compile better-stale-bot
git add .github/ .gitattributes
git commit -m "Add better-stale-bot workflow"
git push
# 4. Run
gh aw run better-stale-bot
Key Features#
- Natural Language Workflows — Markdown + YAML frontmatter
- AI Engine Support — Copilot, Claude, Codex, Gemini, Crush, OpenCode, or custom
- MCP Server Integration — Tool-based execution with Model Context Protocol
- Safe Outputs — Declarative, constrained write operations with hard limits
- Strict Mode — Security-first validation and sandboxing
- Shared Components — Reusable workflow building blocks
- Repo Memory — Persistent git-backed storage for agents via
cache-memory
Creating Workflows#
There are four ways to author gh-aw workflows:
-
GitHub Web Interface — Use Copilot on github.com with prompts like "Create a workflow for GitHub Agentic Workflows using https://raw.githubusercontent.com/github/gh-aw/main/create.md" (requires
gh aw initfirst to set up the dispatcher agent). -
VSCode/Claude/Codex/Copilot — Interactive coding agent conversation in your editor.
-
Manual Editing — Create a
.mdfile manually in.github/workflows/, compile withgh aw compile, commit both.mdand.lock.ymlfiles. -
Adding existing workflows — Use
gh aw add <owner/repo/workflow-name>to import workflows from other repositories.
Using the GitHub Web Interface: After running gh aw init, the command creates a dispatcher agent at .github/agents/agentic-workflows.agent.md, sets up MCP integration, and updates .gitattributes. You can then create workflows via /agent agentic-workflows in Copilot Chat on github.com.
create.md action router: The create.md prompt routes to different specialized prompt files:
- Create new workflow →
.github/aw/create-agentic-workflow.md - Update existing workflow →
.github/aw/update-agentic-workflow.md - Debug workflow →
.github/aw/debug-agentic-workflow.md - Upgrade workflows →
.github/aw/upgrade-agentic-workflows.md - Create shared workflow →
.github/aw/create-shared-agentic-workflow.md
Security Architecture: Five Layers of Defense#
gh-aw implements defense-in-depth with five security layers:
- Read-only tokens — The AI agent receives a GitHub token scoped to read-only permissions. It can observe the repository but cannot change it.
- Zero secrets in the agent — The agent process never receives write tokens, API keys, or other sensitive credentials. Secrets exist only in separate, isolated jobs that run after the agent finishes.
- Containerized with network firewall — The agent runs in an isolated container with an Agent Workflow Firewall (AWF) routing all outbound traffic through a Squid proxy with an explicit domain allowlist.
- Safe outputs with strong guardrails — The agent cannot write to GitHub directly. It produces a structured artifact describing intended actions. A separate gated job with scoped write permissions applies only what the workflow permits (hard limits per operation, required title prefixes, label constraints).
- Agentic threat detection — Before any output is applied, a dedicated threat detection job runs an AI-powered scan checking for prompt injection attacks, leaked credentials, and malicious code patterns.
Workflow Structure: Declarative Markdown + YAML Frontmatter#
Each workflow is a single Markdown file with two parts:
- YAML frontmatter (between
---delimiters) — configures triggers, engine, permissions, tools, and safe-output caps. - Markdown body — natural language instructions the AI agent receives at runtime.
The frontmatter block in better-stale-bot.md illustrates all major keys:
| Key | Purpose |
|---|---|
description | Human-readable summary embedded in the compiled workflow |
on: schedule: daily | Trigger; compiled to a scattered cron (e.g., 52 10 * * *) |
engine: { id: claude, model: haiku } | AI model selection with explicit model; determines which API secret is required; note: an invalid model slug may be resolved to the provider's default model—verify the actual model used via GitHub Actions → Agent Summary → Token Usage. Specifying engine: claude defaults to Sonnet; better-stale-bot uses engine: { id: claude, model: haiku } |
permissions: contents: read, issues: read | Minimal read-only GitHub permissions for the agent job |
tools.github.toolsets: [issues] | Scopes the GitHub MCP server to the issues toolset only |
cache-memory: true | Enables cross-run file persistence in /tmp/gh-aw/cache-memory/ |
safe-outputs | Declares allowed write operations with per-type caps and allowlists |
The markdown body is injected into the agent prompt at runtime via {{#runtime-import .github/workflows/better-stale-bot.md}} , so instruction-only edits (changes to the markdown body) take effect on the next run without recompilation—only frontmatter edits (engine, permissions, safe-outputs, schedule, tools) require gh aw compile. Policy settings like days-before-stale, days-before-close, exempt labels, and instruction logic are in the markdown body and can be changed without recompilation.
The Compile Step#
gh aw compile better-stale-bot
gh aw compile transforms the .md source into a hardened .lock.yml GitHub Actions workflow. The lock file header records the schema version, a hash of the frontmatter, the compiler version (v0.68.1), and the resolved agent model. All referenced GitHub Actions are pinned to exact commit SHAs .
When to recompile: Any edit to the YAML frontmatter—engine, permissions, safe-output caps, schedule, tools—requires recompiling. Edits to the markdown body (instructions, configuration table) take effect on the next run without recompiling.
Compile options: Use gh aw compile --strict for strict mode validation (recommended for production) or gh aw compile --purge to remove stale lock files.
After compilation, commit both files:
git add .github/ .gitattributes
Important: When setting up any gh-aw workflow, .gitattributes must contain:
.github/workflows/*.lock.yml linguist-generated=true merge=ours
This marks lock files as generated (hides them in diffs) and uses ours merge strategy to avoid merge conflicts.
Note: If there is branch protection on the default branch, create a pull request instead of pushing directly when setting up the workflow.
Safe Outputs: Constrained Write Operations#
The AI agent runs with read-only GitHub permissions. All writes are declared upfront as safe-outputs in the frontmatter and enforced by the framework. The agent never writes directly to GitHub; it emits structured JSONL which a separate job applies only after threat detection passes.
The better-stale-bot safe-outputs config:
safe-outputs:
add-comment:
max: 30
add-labels:
max: 30
allowed: ["Stale"]
remove-labels:
max: 30
allowed: ["Stale"]
close-issue:
max: 30
noop:
report-as-issue: false
Key constraints enforced at runtime :
max:— per-run cap per output type, enforced independently; exceeding it is rejected, not silently dropped.allowed:on label operations — the agent can only add/remove labels in the allowlist; attempts to use other labels are blocked.noop— a special output that records "nothing to do"; by default opens a tracking issue titled "[aw] No-Op Runs" to report when the agent found no work. Setreport-as-issue: falseto suppress this issue.
After changing max: values, rerun gh aw compile.
See the Safe Outputs Reference for the full list of constraints (text sanitization, HTTPS-only URLs, domain allowlisting, bot-mention redaction, etc.).
Threat Detection#
Before safe outputs are applied, a dedicated detection job runs a second AI pass against the agent's proposed outputs. It has no MCP connections—only read access to the prompt and output files—and checks for prompt injection, data exfiltration, or policy violations.
The safe_outputs job has an explicit dependency on detection success :
safe_outputs:
needs: [activation, agent, detection]
if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
The detection agent runs with a restricted tool set—Bash, Grep, Read, LS, Glob only; no MCP or write tools—inside the same sandboxed awf container with network firewall enabled.
Cache Memory#
Enabling cache-memory: true in the frontmatter gives the agent a persistent file share at /tmp/gh-aw/cache-memory/ that survives across runs via GitHub Actions Cache. This step stores a short run summary between runs, allowing instructions to tell the agent to read it at the start of later runs to avoid blind reprocessing.
How it works:
- The
agentjob restores the cache keyed bymemory-none-nopolicy-{workflowId}-{run_id}, using a prefix fallback to pick up the most recent prior run. - The agent reads and writes files in
/tmp/gh-aw/cache-memory/using standard filesystem tools (Read,Write,Editrestricted to that path). - After the run, the
update_cache_memoryjob saves the updated directory back to the cache.
In better-stale-bot: Step 5 instructs the agent to write a JSON run summary to /tmp/gh-aw/cache-memory/stale-bot-state.json (date, issues labeled/closed/un-staled, using filesystem-safe timestamps). On the next run, Step 1 tells the agent to read this file to avoid reprocessing recently handled issues.
Execution Pipeline#
The compiled lock file defines five jobs with an explicit dependency order :
activation → agent → detection ──────┐
└─ update_cache_memory
↓
safe_outputs → conclusion
| Job | Runner | Key responsibilities |
|---|---|---|
activation | ubuntu-slim | Validates secrets, checks lock-file integrity, builds the prompt (injects frontmatter context + runtime-imports the .md body), uploads activation artifact |
agent | ubuntu-latest | Installs the AI CLI (e.g., Claude Code @2.1.98), starts MCP Gateway + Safe Outputs MCP Server, runs the agent inside awf sandbox with network firewall; emits safe-output JSONL |
detection | ubuntu-latest | Re-runs AI in read-only mode against agent outputs to detect prompt injection or policy violations |
update_cache_memory | ubuntu-slim | Saves /tmp/gh-aw/cache-memory/ back to GitHub Actions Cache |
safe_outputs | ubuntu-slim | Applies validated writes to GitHub (comments, labels, issue closures) only if detection succeeded |
conclusion | ubuntu-slim | Handles noop tracking issues, missing-tool reports, and agent failure notifications |
The agent runs inside the awf container with a strict network firewall (squid proxy, domain allowlist) and reads GitHub exclusively through the GitHub MCP Server (ghcr.io/github/github-mcp-server:v0.32.0) with GITHUB_READ_ONLY=1 enforced .
Observability: Run logs, token usage, safe-outputs JSONL, and firewall audit logs are uploaded as GitHub Actions artifacts on every run. Use gh aw logs and gh aw audit from a local clone for CLI access.
Billing and Cost: LLM usage is charged by the configured engine. Cost scales with model choice, context size, and the amount of work performed. To inspect usage, navigate to GitHub Actions → Agent Summary → Agentic Conversation and Token Usage. Use the gh aw logs and gh aw audit commands for deeper analysis.
Engine & Authentication Setup#
gh-aw supports six AI engines. Each requires a specific secret:
| Engine | Engine ID | Required Secret | Notes |
|---|---|---|---|
| GitHub Copilot CLI (default) | copilot | COPILOT_GITHUB_TOKEN | Fine-grained PAT with Copilot Requests permission. Resource owner must be personal account, not org. |
| Claude (Claude Code) | claude | ANTHROPIC_API_KEY | API key from Anthropic Console (https://platform.claude.com/docs/en/get-started). Use engine: claude to default to Sonnet, or engine: { id: claude, model: haiku } for explicit model selection. |
| OpenAI Codex | codex | OPENAI_API_KEY | Runtime uses CODEX_API_KEY if present, falls back to OPENAI_API_KEY. Use engine: codex for default model. |
| Google Gemini CLI | gemini | GEMINI_API_KEY | API key from Google AI Studio. Use engine: gemini for default model. |
| Crush (experimental) | crush | COPILOT_GITHUB_TOKEN | Same token as Copilot |
| OpenCode (experimental) | opencode | COPILOT_GITHUB_TOKEN | Same token as Copilot |
Choosing an engine: Copilot is the default choice for most users because it supports the broadest gh-aw feature set. Choose Claude when you want stronger control over turn limits (max-turns) for long reasoning sessions. Choose Gemini or Codex when those models are already part of existing tooling or budget decisions.
Setting secrets: Use the CLI: gh aw secrets set SECRET_NAME --value "YOUR_KEY" or add them via the GitHub UI. Check existing secrets with gh aw secrets bootstrap.
Extended engine configuration: Engines support version pinning, custom model override, custom executable path, custom CLI arguments, custom agent file (Copilot only), custom API endpoint (api-target for GHEC/GHES), custom driver script (Copilot only), and custom token weights.
Timeout configuration:
timeout-minutes: job-level wall clock (default 20 min, all engines)tools.timeout: per tool-call limit in seconds (Claude default 60s, Codex default 120s)max-turns: iteration budget (Claude only)max-continuations: autopilot run budget (Copilot only)
Custom API endpoints: All engines support custom API endpoints via engine.env configuration (e.g., Azure OpenAI, corporate proxies).
GitHub App authentication: Also supported for enhanced security with short-lived tokens (does not apply to COPILOT_GITHUB_TOKEN which must be a PAT).
Common authentication errors:
- 403 for Copilot: missing Copilot Requests permission
- 401/403 for Claude: invalid
ANTHROPIC_API_KEY - 401/403 for Codex: invalid
OPENAI_API_KEY
CLI Commands Reference#
Installation:
gh extension install github/gh-aw— Standard installationgh extension install github/gh-aw@v0.1.0— Version pinned installation- Standalone installer:
curl -sL https://raw.githubusercontent.com/github/gh-aw/main/install-gh-aw.sh | bash - GitHub Actions:
uses: github/gh-aw/actions/setup-cli@main - GHES supported with
GH_HOSTenvironment variable gh extension upgrade aw— Upgrade to latest versiongh aw version— Check installed version
Getting Workflows:
gh aw init— Initialize repository (creates.gitattributes, dispatcher agent file, MCP integration). Options:--no-mcp,--codespaces,--completions,--create-pull-requestgh aw add-wizard <ref>— Interactive guided setup. Acceptsowner/repo/workflow-nameformat. Checks requirements, prompts for missing secretsgh aw add <ref>— Non-interactive add. Supports@versionpinninggh aw new [name]— Create workflow template.--forceto overwrite,--engineto inject enginegh aw secrets set <name>— Create/update repository secret (from stdin,--valueflag, or--value-from-env)gh aw secrets bootstrap— Analyze workflows, detect required secrets, interactively prompt for missing ones
Building:
gh aw fix— Auto-fix deprecated fields. Dry-run by default,--writeto apply.--list-codemodsto see available fixesgh aw compile [workflow-name]— Compile workflows. Key flags:--watch,--validate,--strict,--zizmor(security scan),--purge,--dependabot,--approve(for new secrets/actions)gh aw validate— Validate without generating lock files (equivalent tocompile --validate --no-emit --zizmor --actionlint --poutine)
Testing:
gh aw trial <ref>— Test in temporary private repos or specific repos with--host-repo
Monitoring:
gh aw list— Quick listing without GitHub API queries. Supports--json,--labelfiltergh aw status— Detailed status with enabled/disabled state, schedules, run info (with--ref)gh aw logs [workflow-name]— Download and analyze logs with tool usage, network patterns, errors. Cached for speedup. Accepts workflow IDs or display names (case-insensitive). Has--trainflag for anomaly detection weightsgh aw audit— Rich multi-section reports. 3 modes: single-run audit, cross-run diff, cross-run security report. Accepts run IDs, workflow URLs, job URLs, step URLsgh aw health— Success rates, trends (↑ improving, → stable, ↓ degrading), costs, alertsgh aw checks <pr-number>— Classify CI check state for a PR
Management:
gh aw enable/disable— Enable/disable workflows (disablealso cancels in-progress runs)gh aw remove— Remove workflows (both.mdand.lock.yml). Accepts prefix patternsgh aw update— Update workflows from source field. 3-way merge preserves local changes.--no-mergeto override with upstreamgh aw upgrade— Upgrade repository agent files and apply codemods.--auditfor dependency health audit
Advanced:
gh aw mcp— Manage MCP servers (list, list-tools, inspect, add)gh aw mcp inspect— Inspect configured MCP serversgh aw pr transfer— Transfer PR to another repositorygh aw mcp-server— Run MCP server exposing gh-aw commands as toolsgh aw domains— List network domains configured in workflows
Utility:
gh aw completion— Shell completions (bash, zsh, fish, powershell)gh aw hash-frontmatter— Compute deterministic SHA-256 hash of workflow frontmatter
Debugging tip: Use DEBUG=* gh aw compile for all logs or DEBUG=cli:* gh aw compile for CLI-only logs.
Note: Workflow name matching is fuzzy—the CLI auto-suggests similar names on typos.
Check token usage at: GitHub Actions → Agent Summary → Token Usage