git-plugin/skills/gh-workflow-monitoring/SKILL.md
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.
npx skillsauth add laurigates/claude-plugins gh-workflow-monitoringInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| 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.
# 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) |
# 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
Conclusion Values (when completed): success, failure, cancelled, skipped, neutral, timed_out
# 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
--loggh 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:
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.
# 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
# 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
# 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'
# 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
# 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
# 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
| 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 |
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:
--compact reduces output to relevant steps only&& for conditional next steps# 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
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`
development
Debug HTTP APIs: trace requests, inspect headers. Use when a request fails: check status first.
documentation
Render architecture diagrams from text sources. Use when documenting system topology.
tools
Inspect JSON payloads and extract nested fields. Use when parsing API responses.
tools
--- name: no-description allowed-tools: Read --- # No Description This skill has no description and must be dropped with a warning.