Monitor GitHub Actions runs with blocking watch commands instead of polling loops. Use when waiting for CI after push, following a workflow to completion, or diagnosing failed runs with --log-failed.
Monitor GitHub Actions runs with blocking watch commands instead of polling loops. Use when waiting for CI after push, following a workflow to completion, or diagnosing failed runs with --log-failed.
user-invocable
false
allowed-tools
Bash(gh run *), Bash(gh workflow *), Bash(gh pr *), Read
created
"2025-01-16T00:00:00.000Z"
modified
"2026-07-15T00:00:00.000Z"
reviewed
"2026-04-25T00:00:00.000Z"
GitHub Workflow Monitoring
When to Use This Skill
Use this skill when...
Use the alternative when...
Watching a workflow run until it completes via blocking
gh run watch
Use gh-cli-agentic for one-shot JSON queries of run/PR check state
Waiting for CI after a push, or triggering a workflow and following progress
Use git-fix-pr to diagnose AND auto-correct failing checks on a PR
Diagnosing a failed run with gh run view --log-failed
Use git-pr-feedback to address reviewer comments rather than CI failures
Finding the latest in-progress run for a workflow
Use gh-cli-agentic to list completed runs by status filter
Watch and monitor GitHub Actions workflow runs using gh run watch - a blocking command that follows runs until completion without needing timeouts or polling.
Core Commands
Watch a Run Until Completion
# Watch most recent run (interactive selection if multiple)
gh run watch
# Watch specific run ID
gh run watch $RUN_ID# Compact mode - show only relevant/failed steps (recommended for agents)
gh run watch $RUN_ID --compact
# Exit with non-zero if run fails (useful for chaining)
gh run watch $RUN_ID --exit-status
# Combined: compact output, fail on error
gh run watch $RUN_ID --compact --exit-status
Key Flags:
Flag
Description
--compact
Show only relevant/failed steps (less output)
--exit-status
Exit non-zero if run fails
-i, --interval
Refresh interval in seconds (default: 3)
Find Runs to Monitor
# List in-progress runs
gh run list --status in_progress --json databaseId,name,status,createdAt
# List runs for specific workflow
gh run list -w "CI" --json databaseId,name,status,conclusion -L 5
# List runs for current branch
gh run list --branch $(git branch --show-current) --json databaseId,name,status
# List runs triggered by specific event
gh run list --event push --json databaseId,name,status -L 10
# List failed runs
gh run list --status failure --json databaseId,name,conclusion,createdAt -L 5
Status Values: queued, in_progress, completed, waiting, pending, requested
# Get run status with jobs
gh run view $RUN_ID --json status,conclusion,jobs,name,createdAt
# View with step details
gh run view $RUN_ID --verbose
# Get failed logs only (most useful for debugging)
gh run view $RUN_ID --log-failed
# Get full logs
gh run view $RUN_ID --log# View specific job
gh run view --job $JOB_ID# Open in browser
gh run view $RUN_ID --web
Extract One Step's Bare Output from --log
gh run view --log is tab-delimited: every line is
<job>\t<step>\t<timestamp> <log line>. To scrape one step's stdout back to
its bare form — dropping the job and step columns and the per-line
timestamp — filter to the step name (field-2 substring) and strip the
timestamp, which is the first space-delimited token of field 3:
# All lines from the step whose name contains "clean warm run", de-columned
gh run view $RUN_ID --log \
| awk -F'\t''/clean warm run/ { sub(/^[^ ]* /, "", $3); print $3 }'
This is the durable way to read a succeeded step's output for structured
markers — RESULT …, a PASS/FAIL line, a JSON blob a job printed — when
--log-failed doesn't apply (nothing failed; you just want the output). The
whole job's log is one stream, so narrow to the step first, then grep the
markers:
Notes: match the step name as it appears in the workflow's name: (the awk
/pattern/ is a plain regex over the whole tab-joined line — anchor with a
distinctive substring to avoid matching the same text inside a log line);
[^ ]* matches the timestamp because it never contains a space, so a
mangled first token can't over-strip the line.
Workflow Patterns
Trigger and Watch
# Trigger workflow and immediately watch it
gh workflow run "CI" && sleep 2 && gh run watch --compact --exit-status
# Trigger with inputs
gh workflow run "Deploy" -f environment=staging -f version=1.2.3
Wait for PR Checks
# Get the latest run for a PR's head commit
RUN_ID=$(gh run list --branch $(gh pr view $PR --json headRefName --jq '.headRefName') -L 1 --json databaseId --jq '.[0].databaseId')
gh run watch $RUN_ID --compact --exit-status
Monitor Multiple Runs
# List all in-progress runs and watch the first one
gh run list --status in_progress --json databaseId,name --jq '.[0]'# Get all active run IDs
gh run list --status in_progress --json databaseId --jq '.[].databaseId'
Agentic Patterns
Find and Watch Latest Run
# 1. Find the run
RUN_ID=$(gh run list -L 1 --json databaseId --jq '.[0].databaseId')
# 2. Watch it (blocking - waits until complete)
gh run watch $RUN_ID --compact --exit-status
Diagnose Failures
# 1. Find failed run
gh run list --status failure -L 1 --json databaseId,name,conclusion
# 2. Get failed logs
gh run view $RUN_ID --log-failed
CI Integration Flow
# After pushing, find and watch the triggered run
git push origin HEAD
sleep 5 # Wait for GitHub to register the run
RUN_ID=$(gh run list --branch $(git branch --show-current) -L 1 --json databaseId --jq '.[0].databaseId')
gh run watch $RUN_ID --compact --exit-status
Agentic Optimizations
Context
Command
Watch until done
gh run watch $ID --compact --exit-status
Find in-progress
gh run list --status in_progress --json databaseId,name
Latest run ID
gh run list -L 1 --json databaseId --jq '.[0].databaseId'
gh workflow run "$NAME" && sleep 2 && gh run watch --compact
PR run status
gh pr checks $PR --json name,state,conclusion
Why gh run watch Over Polling
Approach
Problem
sleep + poll
Wastes time, may miss completion, timeout complexity
Webhook
Requires infrastructure, not CLI-friendly
gh run watch
Blocks until complete, shows progress, returns exit code
Benefits of gh run watch:
Blocking: Waits until run completes - no timeout management needed
Live updates: Shows progress during execution
Exit codes: Returns 0 on success, non-zero on failure
Compact mode: --compact reduces output to relevant steps only
Chain-friendly: Use with && for conditional next steps
Error Handling
# Watch with error handling
gh run watch $RUN_ID --compact --exit-status && echo"Success" || echo"Failed"# Check if run exists before watching
gh run view $RUN_ID --json status 2>/dev/null && gh run watch $RUN_ID --compact
Context Expressions
Use in command frontmatter:
- In-progress runs: !`gh run list --status in_progress --json databaseId,name --jq '.[0]'`- Latest run: !`gh run list -L 1 --json databaseId,name,status,conclusion`