Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Analyze a git repo's history to surface high-churn files, ownership risks, bug hotspots, momentum trends, and firefighting patterns. Use this skill whenever the user wants to understand a codebase, assess repo health, or orient themselves before reading code — even if they don't explicitly say "audit".
.claude/skills/fredrikaverpil-codebase-audit/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 307% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 140% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 28% | 0% |
Analyze the current git repository's history to produce a structured health report covering churn, ownership, bugs, momentum, and firefighting patterns.
Produce a structured markdown report with one section per dimension, including raw data (top files/contributors) and observations. The report should help the reader orient themselves in an unfamiliar codebase and identify risk areas before reading code.
Steps 1a-1e are independent — run them in parallel, then synthesize in step 2.
Before starting the analysis, check whether the repo is GitHub-hosted and gh is authenticated:
bashgh repo view --json nameWithOwner 2>/dev/null
If this succeeds, set a mental flag that GitHub enrichments are available. If it fails (not a GitHub repo, or gh not authenticated), skip all gh-prefixed sub-steps below and rely on git-only analysis. Do not warn or apologize — just use what's available.
bashgit log --format=format: --name-only --since="1 year ago" | grep . | sort | uniq -c | sort -nr | head -20
Report the top 20 most-modified files in the past year. Flag any file that appears disproportionately often -- this is the clearest signal of codebase drag.
Lockfiles, generated output and vendored trees dominate raw churn without telling you anything about the code. If 5 or more of the top 10 are noise of this kind, run a filtered second pass:
bashNOISE='(^|/)([^/]*-lock\.(json|yaml)|[^/]*\.lock|package\.json|go\.sum|CHANGELOG\.md|[^/]*\.snap)$|(^|/)(__snapshots__|dist|build|coverage|generated|vendor|node_modules|\.terraform)/' git log --format=format: --name-only --since="1 year ago" | grep . | grep -vE "$NOISE" | sort | uniq -c | sort -nr | head -20
Extend $NOISE with repo-specific paths when the filtered pass is still dominated by bulk content (docs/, locales/, i18n/). Label that as a repo-specific pass, separate from the generic de-noising above.
Report both passes and name what was excluded — a lockfile churning 80 times is itself a finding about dependency pressure, it just isn't a finding about the code.
Keep the blank-line strip as its own grep . rather than folding ^$ into $NOISE. BSD grep (macOS) drops the ^$ branch when the alternation contains a leading .*, silently leaving every blank line in the count.
When file-level hotspots are too noisy to read — large repos, monorepos:
bashgit log --format=format: --name-only --since="1 year ago" | grep . | cut -d/ -f1-2 | sort | uniq -c | sort -nr | head -20
Commit count alone is a poor proxy for ownership. Someone reformatting config files 100 times looks more "important" than someone who architected a core subsystem in 5 commits. Gather multiple signals to build a nuanced picture.
bashgit shortlog -sn --no-merges
bashgit shortlog -sn --no-merges --since="6 months ago"
Under a squash-merge workflow these counts reflect who merged, not who wrote the code — which can invert the ranking entirely. Check before trusting it:
bashgh pr list --state merged --limit 20 --json mergeCommit,author
If GitHub is unavailable, a history of single-commit merges with no branch topology is the tell.
For the top 5 contributors by commit count, measure insertions and deletions:
bashgit log --author="<name>" --numstat --no-merges --format='' | awk '{ add += $1; del += $2 } END { print "+" add, "-" del }'
This distinguishes high-volume contributors from high-frequency ones.
Identify the top-level directories in the repo, then for each one show the top 3 contributors:
bashgit shortlog -sn --no-merges -- <directory>
This reveals domain expertise — one person may own 80% of infra/ while another owns src/auth/. Concentrated subsystem ownership is a bus-factor risk even when overall commit counts look balanced.
For each of the top 3 contributors, sample their 5 most recent commits:
bashgit log --author="<name>" --no-merges --oneline -5
Use the commit messages to characterize the nature of their work: features, bug fixes, refactoring, formatting, dependency updates, etc. This adds qualitative context that numbers alone cannot provide.
Only if GitHub is available. Query merged PRs to understand who reviews whose code:
bashgh pr list --state merged --limit 100 --json author,reviews
Look for review silos — if only one person reviews a particular author's PRs, that's a knowledge concentration risk. Also note if anyone is a "review bottleneck" (appears as reviewer on most PRs).
Only if GitHub is available. GitHub's own contributor statistics include weekly additions/deletions per author, which is more efficient than looping git log --numstat per author:
bashgh api repos/{owner}/{repo}/stats/contributors
Use this to cross-validate the git-based lines-changed data from 1b-ii. If available, prefer this data as it's pre-aggregated.
Combine all signals into an ownership assessment. A contributor with few commits but large line changes in critical subsystems is more important than commit count suggests. Conversely, someone with many commits that are mostly formatting or config changes carries less bus-factor risk. PR review patterns (if available) reveal knowledge sharing — or the lack of it.
bashgit log -i -E --grep="fix|bug|broken" --name-only --format='' | grep . | sort | uniq -c | sort -nr | head -20
Show the top 20 files most frequently touched in bug-related commits. If step 1a-ii needed de-noising, apply the same $NOISE filter here.
Commit-message discipline sets the ceiling on this signal — a repo where fixes are titled "update" produces a thin list, which says nothing about its actual defect rate.
Only if GitHub is available. Query actual bug reports for a richer picture than commit message grep alone:
bashgh issue list --label bug --state all --limit 50 --json title,assignees,url,closedAt
Cross-reference with the git-based hotspot data. Issues give you the user-facing bugs that were reported, while commit grep gives you the files that were patched. Together they paint a fuller picture.
Overlay bug hotspots against churn data from step 1a to identify highest-risk code — files that both change frequently and attract bug fixes.
bashgit log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c
Display monthly commit counts over the repo's entire history. Note the overall trend: steady rhythm, growth, or decline. Flag sudden drops.
Only if GitHub is available. PR merge rate is often a better velocity signal than raw commits, especially in squash-merge workflows:
bashgh pr list --state merged --limit 200 --json mergedAt
Group by month and compare against commit cadence.
Only if GitHub is available:
bashgh release list --limit 20
Regular releases indicate a healthy delivery rhythm. Long gaps between releases may signal stalled work or big-bang deployments.
bashgit log --oneline --since="1 year ago" | grep -iE 'revert|hotfix|emergency|rollback'
Count and list reverts, hotfixes, and emergency commits from the past year.
grep exits 1 on no match. That is a real result — a quiet year, or vague commit messages — not a failed command, so report it as a finding rather than retrying with a looser pattern.
Only if GitHub is available. Squash-merged reverts don't always show up in git log --oneline, so also search merged PRs:
bashgh pr list --state merged --search "revert OR hotfix OR emergency" --limit 50 --json title,mergedAt,url
Frequent reverts indicate deploy instability and test reliability issues.
After all parallel steps complete, combine findings into a structured markdown report with the following sections:
If GitHub data was available, note this at the top of the report. If not, mention that the analysis is git-only and could be enriched by running against a GitHub-hosted repo with gh authenticated.
Each section is its table plus 2-4 observation bullets. An observation says something the table doesn't already show — a cross-reference, an outlier, a caveat that changes how the numbers read. Prose that restates the table earns nothing and costs the reader a paragraph.
Spend the budget where the findings are. A repo with one real risk gets one substantial section and four thin ones, not five padded to match.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 20,131 | 7,136 | -65% | 1 | 1 | 0% | 3,382 | 2,749 | -19% | 0 | 0 | — |
case-02 | fail→fail | 22,778 | 5,660 | -75% | 1 | 1 | 0% | 3,958 | 2,857 | -28% | 0 | 0 | — |
case-03 | fail→fail | 19,663 | 5,107 | -74% | 1 | 1 | 0% | 3,374 | 2,748 | -19% | 0 | 0 | — |
case-04 | pass→pass | 6,924 | 4,342 | -37% | 1 | 1 | 0% | 1,129 | 3,146 | +179% | 0 | 0 | — |
case-05 | pass→pass | 4,626 | 6,503 | +41% | 1 | 1 | 0% | 888 | 3,574 | +302% | 0 | 0 | — |
case-06 | fail→pass | 4,101 | 3,986 | -3% | 1 | 1 | 0% | 773 | 3,149 | +307% | 0 | 0 | — |
case-07 | fail→pass | 7,765 | 4,876 | -37% | 1 | 1 | 0% | 1,431 | 3,428 | +140% | 0 | 0 | — |
case-08 | fail→pass | 16,559 | 9,309 | -44% | 1 | 1 | 0% | 2,636 | 3,809 | +44% | 0 | 0 | — |
case-09 | fail→pass | 11,614 | 3,680 | -68% | 1 | 1 | 0% | 2,231 | 3,134 | +40% | 0 | 0 | — |
case-10 | pass→pass | 8,604 | 4,380 | -49% | 1 | 1 | 0% | 1,434 | 3,164 | +121% | 0 | 0 | — |
case-11 | pass→pass | 10,901 | 7,133 | -35% | 1 | 1 | 0% | 1,874 | 3,747 | +100% | 0 | 0 | — |
case-12 | pass→pass | 8,065 | 4,630 | -43% | 1 | 1 | 0% | 1,375 | 3,253 | +137% | 0 | 0 | — |
case-13 | fail→pass | 13,878 | 2,883 | -79% | 1 | 1 | 0% | 2,301 | 2,935 | +28% | 0 | 0 | — |
case-14 | fail→pass | 10,902 | 4,916 | -55% | 1 | 1 | 0% | 1,845 | 3,387 | +84% | 0 | 0 | — |
case-15 | pass→pass | 4,081 | 2,719 | -33% | 1 | 1 | 0% | 704 | 2,894 | +311% | 0 | 0 | — |
case-16 | pass→pass | 10,722 | 4,835 | -55% | 1 | 1 | 0% | 1,737 | 3,280 | +89% | 0 | 0 | — |
case-17 | pass→pass | 11,175 | 3,697 | -67% | 1 | 1 | 0% | 1,840 | 3,046 | +66% | 0 | 0 | — |
case-18 | fail→pass | 11,096 | 2,443 | -78% | 1 | 1 | 0% | 1,716 | 2,827 | +65% | 0 | 0 | — |
case-19 | fail→pass | 28,041 | 2,537 | -91% | 1 | 1 | 0% | 1,232 | 2,888 | +134% | 0 | 0 | — |
case-20 | pass→pass | 9,736 | 2,991 | -69% | 1 | 1 | 0% | 1,776 | 2,907 | +64% | 0 | 0 | — |
case-21 | pass→pass | 9,213 | 4,405 | -52% | 1 | 1 | 0% | 1,486 | 3,167 | +113% | 0 | 0 | — |
case-22 | pass→pass | 3,972 | 2,719 | -32% | 1 | 1 | 0% | 667 | 2,896 | +334% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 18 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 18 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.