skills/repo-audit/SKILL.md
Audit GitHub repo security settings, dependabot config, workflow hygiene, stale PRs/branches, and action pinning. Auto-fix drift with --fix. Use when you want to check repo health across an org or a single repo.
npx skillsauth add skinnyandbald/fish-skills repo-auditInstall 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.
Audit GitHub repo security settings and hygiene. Detect drift, report findings, optionally auto-fix.
/repo-audit # audit all active repos (default org: skinnyandbald)
/repo-audit <repo> # audit one repo (e.g. skinnyandbald/cos-bot)
/repo-audit --fix # audit + auto-fix what's fixable
/repo-audit <org> # audit all active repos in a specific org
/repo-audit <repo> --fix # audit one repo + fix
Before running any checks, verify the token has sufficient permissions:
# Check token is valid
gh api /user
# Check repo scope by testing vulnerability alerts on first repo
gh api repos/{first-repo}/vulnerability-alerts
If either call fails, warn the user and skip security settings checks (dependabot-alerts, dependabot-autofixes). Report clearly:
WARNING: Token permission check failed. Ensure your token has `repo` scope.
Security settings checks will be skipped.
Continue with remaining checks (dependabot-config, semgrep-syntax, stale-prs, stale-branches, actions-pinned) that only require read access.
Single-repo mode: If a specific repo was provided (contains /), use that repo directly. Skip to Step 2.
Org mode: List all active repos for the org (default: skinnyandbald).
Active repos = non-archived repos with a push in the last 180 days.
CUTOFF=$(date -v-180d +%Y-%m-%dT%H:%M:%SZ) # macOS; use `date -d '-180 days'` on Linux
gh repo list {org} --limit 200 --json nameWithOwner,isArchived,pushedAt --no-archived \
--jq --arg cutoff "$CUTOFF" '.[] | select(.pushedAt > $cutoff) | .nameWithOwner'
Report the repo count before proceeding: "Found N active repos in {org}."
For each repo, run every check below. Collect all results into three buckets: Critical, Warning, Clean.
Rate limit awareness: If auditing 50+ repos, add a 1-second pause between repos (sleep 1).
dependabot-alerts (Critical, Auto-fixable)Verify vulnerability alerts are enabled at the repo level.
gh api repos/{repo}/vulnerability-alerts
# 204 = enabled, 404 = disabled
A 404 response means alerts are disabled.
dependabot-autofixes (High, Auto-fixable)Verify automated security fixes are enabled.
gh api repos/{repo}/automated-security-fixes
# Check the response JSON -- `enabled` field
dependabot-config (High, Auto-fixable)Verify .github/dependabot.yml exists in the repo.
gh api repos/{repo}/contents/.github/dependabot.yml
# 404 = missing
semgrep-syntax (High, Auto-fixable)Scan all *.yml files under .github/workflows/ for any file that contains both semgrep ci and --config auto on the same line or in the same step.
# List workflow files
workflows=$(gh api repos/{repo}/contents/.github/workflows --jq '.[].name' 2>/dev/null)
# For each workflow file, fetch content and check
for wf in $workflows; do
content=$(gh api repos/{repo}/contents/.github/workflows/$wf --jq '.content' | base64 -D)
if echo "$content" | grep -q 'semgrep ci' && echo "$content" | grep -q '\-\-config auto'; then
# Flag this file
echo "FAIL: $wf contains semgrep ci with --config auto"
fi
done
stale-prs (Low, Report only)Find open PRs older than 30 days.
gh pr list --repo {repo} --state open --json number,title,createdAt,author \
--jq '.[] | select(.createdAt < "'$(date -v-30d +%Y-%m-%dT%H:%M:%SZ)'")'
Report count and PR numbers/titles.
stale-branches (Low, Report only)Find branches with tip commit older than 60 days, excluding:
dependabot/*, renovate/*, release/* prefixes# Get all open PR source and target branches (to exclude them)
open_pr_branches=$(gh api repos/{repo}/pulls --jq '.[].head.ref, .[].base.ref' | sort -u)
default_branch=$(gh api repos/{repo} --jq '.default_branch')
gh api repos/{repo}/branches --paginate | jq -r '.[].name' | while read branch; do
# Skip default branch
[[ "$branch" == "$default_branch" ]] && continue
# Skip dependabot/renovate/release branches
[[ "$branch" =~ ^(dependabot|renovate|release)/ ]] && continue
# Skip branches with open PRs
echo "$open_pr_branches" | grep -qx "$branch" && continue
# URL-encode branch name to handle slashes and special chars
encoded_branch=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$branch")
# Check tip commit date
committed_at=$(gh api "repos/{repo}/git/refs/heads/${encoded_branch}" --jq '.object.url' \
| xargs gh api --jq '.commit.committer.date')
# Flag if older than 60 days
cutoff_60d=$(date -v-60d +%Y-%m-%dT%H:%M:%SZ)
if [[ "$committed_at" < "$cutoff_60d" ]]; then
echo "STALE: $branch (last commit: $committed_at)"
fi
done
actions-pinned (High, Report only)Scan workflow files for third-party GitHub Actions that are NOT pinned to a SHA.
A uses: line is flagged if ALL of these are true:
actions/* or github/* (first-party exclusions)@ is NOT a 40-character hexadecimal SHAThis catches @main, @master, @v1, @v1.2.3, and any other non-SHA ref.
# For each workflow file, fetch and scan
for wf in $workflows; do
content=$(gh api repos/{repo}/contents/.github/workflows/$wf --jq '.content' | base64 -D)
# Find uses: lines with non-SHA refs for third-party actions
echo "$content" | grep -E '^\s*uses:\s*' | while read line; do
# Extract action ref (owner/repo@ref)
action=$(echo "$line" | sed 's/.*uses:\s*//' | tr -d '"' | tr -d "'")
owner=$(echo "$action" | cut -d'/' -f1)
ref=$(echo "$action" | sed 's/.*@//' | cut -d' ' -f1)
# Skip first-party actions
[[ "$owner" == "actions" || "$owner" == "github" ]] && continue
# Check if ref is a 40-char hex SHA
if ! echo "$ref" | grep -qE '^[0-9a-f]{40}$'; then
echo "UNPINNED: $wf uses $action"
fi
done
done
Format results as a markdown report with three sections:
## Repo Audit Results
### Critical Issues
| Repo | Check | Details |
|------|-------|---------|
| cos-bot | dependabot-alerts | Disabled |
### Warnings
| Repo | Check | Details |
|------|-------|---------|
| prompto | stale-prs | 3 PRs older than 30 days |
| dear-ben | actions-pinned | 2 unpinned actions in ci.yml |
### All Clear
dear-ben, SecondBrain, distil (12 repos clean)
**Summary:** 2 critical, 3 warnings, 12 clean
Map check severities to report sections:
dependabot-alerts (Critical severity)dependabot-autofixes, dependabot-config, semgrep-syntax, actions-pinned (High severity), stale-prs, stale-branches (Low severity)If --fix was NOT passed, remind the user: "Run /repo-audit --fix to auto-fix fixable issues."
--fix flag)If --fix was not passed, STOP here. Do not modify anything.
Auto-fixable checks fall into two categories:
dependabot-alerts:
gh api repos/{repo}/vulnerability-alerts --method PUT
dependabot-autofixes:
gh api repos/{repo}/automated-security-fixes --method PUT
dependabot-config -- create .github/dependabot.yml with the default template (see below).
Only push if the repo has a package.json (npm ecosystem) or .github/workflows/ (actions ecosystem). Skip repos with neither.
For new file creation, omit the sha field:
# Check for ecosystem markers before fixing
if ! gh api repos/{repo}/contents/package.json &>/dev/null && \
! gh api repos/{repo}/contents/.github/workflows &>/dev/null; then
echo "Skipping {repo}: no npm or actions ecosystem found."
continue
fi
# Write template to temp file
cat > /tmp/dependabot.yml << 'DEPBOT'
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
cooldown:
default-days: 7
semver-major-days: 14
semver-minor-days: 7
semver-patch-days: 3
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
cooldown:
default-days: 3
DEPBOT
gh api repos/{repo}/contents/.github/dependabot.yml \
--method PUT \
-f message="chore: add dependabot config" \
-f content="$(base64 < /tmp/dependabot.yml)"
semgrep-syntax -- update each offending workflow file to remove --config auto.
Scan ALL *.yml files under .github/workflows/ (not just security.yml).
# Get current file content and SHA
sha=$(gh api repos/{repo}/contents/.github/workflows/{file} --jq '.sha')
content=$(gh api repos/{repo}/contents/.github/workflows/{file} --jq '.content' | base64 -D)
# Remove --config auto from the content
fixed_content=$(echo "$content" | sed '/semgrep ci/s/--config auto//g')
# Write fixed content to temp file
echo "$fixed_content" > /tmp/fixed-workflow.yml
# Push fix via Contents API
gh api repos/{repo}/contents/.github/workflows/{file} \
--method PUT \
-f message="fix: remove --config auto from semgrep ci" \
-f content="$(base64 < /tmp/fixed-workflow.yml)" \
-f sha="$sha"
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
cooldown:
default-days: 7
semver-major-days: 14
semver-minor-days: 7
semver-patch-days: 3
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
cooldown:
default-days: 3
After applying fixes, report what was changed:
## Fixes Applied
| Repo | Check | Action |
|------|-------|--------|
| cos-bot | dependabot-alerts | Enabled via API |
| cos-bot | dependabot-config | Created .github/dependabot.yml via Contents API |
| prompto | semgrep-syntax | Removed --config auto from security.yml |
.github/.--fix flag is passed.skinnyandbald if none specified.sleep 1 between repos to avoid hitting GitHub API rate limits.date -v-Nd syntax (macOS). On Linux, use date -d '-N days' instead.git clone needed.--fix is passed, show the audit report FIRST, then confirm with the user before applying fixes. List exactly what will be changed.development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
tools
Verify worktree plugin patches are intact after plugin updates. Checks compound-engineering and superpowers skills for Claude Code launch instructions.
development
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
development
Reviews the feature you just built and adds missing test coverage. Focuses on behavior that matters — not coverage metrics. Use after completing a feature to identify untested code paths, edge cases, and risk areas.