From finops-plugin
Analyzes GitHub Actions billing, workflow efficiency, and waste patterns in orgs and repos. Identifies wasted runs, trigger issues, and CI/CD costs using gh CLI.
npx claudepluginhub laurigates/claude-plugins --plugin finops-pluginThis skill is limited to using the following tools:
Analyze GitHub Actions usage, costs, and efficiency across organizations and repositories.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Analyze GitHub Actions usage, costs, and efficiency across organizations and repositories.
| Use this skill when... | Use X instead when... |
|---|---|
| Analyzing CI/CD costs and billing | Debugging a specific failed workflow -- use gh-workflow-monitoring |
| Identifying wasted workflow runs | Setting up new workflows -- use github-actions-workflows |
| Investigating workflow trigger patterns | Managing cache keys -- use github-actions-cache-optimization |
| Comparing efficiency across repos | Monitoring a single run -- use gh-workflow-monitoring |
git remote get-url originfind .github/workflows -maxdepth 1 -name '*.yml' -o -name '*.yaml'gh workflow list --json id,name,stateExecute this GitHub Actions FinOps analysis:
Read the Context values above. Parse $OWNER and $REPO from the current repo URL (e.g., https://github.com/OWNER/REPO.git). Run gh api repos/$OWNER/$REPO --jq '.owner.type' to determine if the owner is an "Organization" or "User". If Organization, set $GITHUB_ORG to the repo owner for org-level billing queries.
If no repo context is available, ask the user for the target organization or repository.
Query the Actions billing API:
gh api /orgs/$GITHUB_ORG/settings/billing/actions \
--jq '{included_minutes, total_minutes_used, total_paid_minutes_used}'
If this returns a permissions error, note that admin access is required and skip to Step 3.
Optionally also check packages and storage billing:
gh api /orgs/$GITHUB_ORG/settings/billing/packages \
--jq '{included_gigabytes_bandwidth, total_gigabytes_bandwidth_used}'
gh api /orgs/$GITHUB_ORG/settings/billing/shared-storage \
--jq '{days_left_in_billing_cycle, estimated_paid_storage_for_month}'
Fetch recent runs and group by workflow:
gh api "/repos/$OWNER/$REPO/actions/runs?per_page=100" \
--jq '.workflow_runs | group_by(.name) |
map({workflow: .[0].name, runs: length,
conclusions: (group_by(.conclusion) | map({(.[0].conclusion // "unknown"): length}) | add)}) |
sort_by(-.runs)'
Calculate run durations:
gh api "/repos/$OWNER/$REPO/actions/runs?per_page=20&status=completed" \
--jq '.workflow_runs | group_by(.name) |
map({name: .[0].name, count: length,
total_seconds: (map(.run_started_at as $start | .updated_at as $end |
(($end | fromdateiso8601) - ($start | fromdateiso8601))) | add)}) |
sort_by(-.count) | .[] | "\(.name): \(.count) runs, ~\(.total_seconds/60|floor)min total"'
Check each waste indicator:
Skipped runs:
gh api "/repos/$OWNER/$REPO/actions/runs?per_page=100" \
--jq '[.workflow_runs[] | select(.conclusion == "skipped")] |
group_by(.name) | map({workflow: .[0].name, skipped: length}) |
sort_by(-.skipped)'
Bot-triggered runs:
gh api "/repos/$OWNER/$REPO/actions/runs?per_page=100" \
--jq '[.workflow_runs[] | select(.triggering_actor.type == "Bot")] | length'
High-frequency workflows (candidates for path filters):
gh api "/repos/$OWNER/$REPO/actions/runs?per_page=100" \
--jq '.workflow_runs | group_by(.name) | map(select(length > 50)) |
map({workflow: .[0].name, runs: length})'
Read each workflow file from .github/workflows/ and check for:
concurrency: groupspaths: filters on push/PR triggersif: github.event.sender.type != 'Bot')Print a summary with these sections:
Use these thresholds for flagging:
| Metric | Red Flag Threshold |
|---|---|
| Skipped runs | >10% of total runs |
| Bot triggers | Bot-to-bot chains detected |
| Long durations | >10min average |
| High frequency | >50 runs/month without path filters |
| Duplicate runs | Same commit triggers multiple runs |
For each waste pattern found, recommend the specific fix:
| Pattern | Fix |
|---|---|
| Bot-triggered waste | Add if: github.event.sender.type != 'Bot' to jobs |
| Missing concurrency | Add concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true } |
| No path filters | Add paths: with relevant source directories |
| Duplicate runs | Add concurrency groups with cancel-in-progress: true |
| Endpoint | Purpose | Admin Required |
|---|---|---|
/orgs/{org}/settings/billing/actions | Minutes usage | Yes |
/orgs/{org}/settings/billing/packages | Package bandwidth | Yes |
/orgs/{org}/settings/billing/shared-storage | Storage billing | Yes |
/orgs/{org}/actions/cache/usage | Org cache stats | No |
/repos/{owner}/{repo}/actions/runs | Workflow runs | No |
/repos/{owner}/{repo}/actions/workflows | Workflow definitions | No |
| Context | Command |
|---|---|
| Org billing | gh api /orgs/$ORG/settings/billing/actions --jq '{included_minutes, total_minutes_used}' |
| List repos | gh repo list $ORG --json nameWithOwner --limit 100 |
| Workflow runs | gh api "/repos/$O/$R/actions/runs?per_page=100" --jq '.workflow_runs' --jq 'length' |
| Skipped count | `gh api "..." --jq '[.workflow_runs[] |
| Bot triggers | `gh api "..." --jq '[.workflow_runs[] |