| name | gh-workflow-monitoring |
| description | 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
gh run watch
gh run watch $RUN_ID
gh run watch $RUN_ID --compact
gh run watch $RUN_ID --exit-status
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
gh run list --status in_progress --json databaseId,name,status,createdAt
gh run list -w "CI" --json databaseId,name,status,conclusion -L 5
gh run list --branch $(git branch --show-current) --json databaseId,name,status
gh run list --event push --json databaseId,name,status -L 10
gh run list --status failure --json databaseId,name,conclusion,createdAt -L 5
Status Values: queued, in_progress, completed, waiting, pending, requested
Conclusion Values (when completed): success, failure, cancelled, skipped, neutral, timed_out
View Run Details
gh run view $RUN_ID --json status,conclusion,jobs,name,createdAt
gh run view $RUN_ID --verbose
gh run view $RUN_ID --log-failed
gh run view $RUN_ID --log
gh run view --job $JOB_ID
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:
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:
gh run view $RUN_ID --log \
| awk -F'\t' '/bench \(clean warm/ { sub(/^[^ ]* /, "", $3); print $3 }' \
| grep -E 'RESULT|SANITY'
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
gh workflow run "CI" && sleep 2 && gh run watch --compact --exit-status
gh workflow run "Deploy" -f environment=staging -f version=1.2.3
Wait for PR Checks
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
gh run list --status in_progress --json databaseId,name --jq '.[0]'
gh run list --status in_progress --json databaseId --jq '.[].databaseId'
Agentic Patterns
Find and Watch Latest Run
RUN_ID=$(gh run list -L 1 --json databaseId --jq '.[0].databaseId')
gh run watch $RUN_ID --compact --exit-status
Diagnose Failures
gh run list --status failure -L 1 --json databaseId,name,conclusion
gh run view $RUN_ID --log-failed
CI Integration Flow
git push origin HEAD
sleep 5
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' |
| Failed logs | gh run view $ID --log-failed |
| Scrape a succeeded step's stdout | gh run view $ID --log | awk -F'\t' '/STEP/{sub(/^[^ ]* /,"",$3);print $3}' |
| Trigger + watch | 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
gh run watch $RUN_ID --compact --exit-status && echo "Success" || echo "Failed"
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`
See Also
- gh-cli-agentic - General GitHub CLI patterns
- git-branch-pr-workflow - PR and branch workflows