| name | tech-debt-audit |
| description | Generate a comprehensive technical debt audit for a codebase. Analyzes security vulnerabilities, dependency freshness, code complexity, test coverage, and produces a single self-contained HTML report with visuals and actionable recommendations. |
| argument-hint | ["directory"] |
| allowed-tools | ["Bash(bundle*)","Bash(gem install*)","Bash(npm*)","Bash(yarn*)","Bash(npx*)","Bash(ruby*)","Bash(rake*)","Bash(rails-stats*)","Bash(rails_stats*)","Bash(grep*)","Bash(git*)","Bash(ls*)","Bash(cat*)","Bash(head*)","Bash(wc*)","Bash(find*)","Bash(mkdir*)","Bash(cp*)","Bash(date*)","Bash(base64*)","Bash(brakeman*)","Bash(bundle-audit*)","Bash(bundle-leak*)","Bash(bundler-audit*)","Bash(bundler-leak*)","Bash(rubycritic*)","Bash(skunk*)","Bash(libyear-bundler*)","Bash(bundle_report*)","Bash(cloc*)","Bash(trivy*)","Bash(upjs-plato*)","Bash(brew*)","Bash(COVERAGE=true*)","Bash(/Applications/Google Chrome.app/Contents/MacOS/Google Chrome*)","Bash(google-chrome*)","Bash(chromium*)","Read","Glob","Grep"] |
Tech Debt Audit Skill
Generate a comprehensive technical debt audit for the codebase. This skill runs multiple
code-quality and security tools, captures visuals, and compiles everything into a single,
self-contained HTML page with an executive summary, one section per tool, embedded
screenshots, and the top 3 recommended actions.
Target Directory
If $ARGUMENTS is provided, audit that directory. Otherwise, audit the current working
directory. All commands below run from inside the target directory.
Pre-Audit Check
Before running tools, check if the client is using SonarQube. If so, some metrics may already
be tracked and running certain tools (like Skunk) may be duplicate effort. Note this in the report.
Tool Installation Policy
IMPORTANT: Always install required tools before running them. Do NOT skip any tool
because it's missing from the Gemfile. Install Ruby tools globally with gem install and
run them directly (not via bundle exec) to avoid version conflicts with the project's
Gemfile.
Step 0: Create the Timestamped Output Directory
All results from this run — raw tool output, the RubyCritic HTML report, screenshots, and
the final report — go into one single directory named with the timestamp of the run.
TS=$(date +%Y%m%d-%H%M%S)
OUT="tech-debt-audit-$TS"
mkdir -p "$OUT/raw" "$OUT/rubycritic" "$OUT/screenshots"
echo "$OUT"
Directory layout produced by this skill:
tech-debt-audit-YYYYMMDD-HHMMSS/
├── index.html <- the single self-contained report (the deliverable)
├── raw/ <- raw text/JSON output from every tool
├── rubycritic/ <- RubyCritic's generated HTML report
└── screenshots/ <- PNG screenshots embedded into index.html
Save every tool's raw output into $OUT/raw/ (redirect with > "$OUT/raw/<tool>.txt" 2>&1)
so the final report can quote exact numbers and the run is reproducible.
Step 1: Detect Project Type
- Ruby/Rails:
Gemfile, Gemfile.lock, *.rb files
- JavaScript/Node.js:
package.json, package-lock.json, yarn.lock
- Both: many Rails apps have both
Only run the checks that apply to what you detect.
Step 2: Security Vulnerabilities
Ruby (if Gemfile exists)
bundler-audit — gem versions with known CVEs
gem install bundler-audit --no-document
bundle-audit check --update > "$OUT/raw/bundler-audit.txt" 2>&1
Brakeman — static security analysis for Rails
gem install brakeman --no-document
brakeman --no-pager -q > "$OUT/raw/brakeman.txt" 2>&1
bundler-leak — memory-leaking gems
gem install bundler-leak --no-document
bundle-leak check --update > "$OUT/raw/bundler-leak.txt" 2>&1
Trivy (all projects)
Trivy — filesystem scan for vulnerable dependencies (Ruby, JS, OS packages),
leaked secrets, and misconfigurations. Install if missing.
command -v trivy >/dev/null 2>&1 || brew install trivy
trivy fs --scanners vuln,secret,misconfig --format table . > "$OUT/raw/trivy.txt" 2>&1
trivy fs --scanners vuln,secret,misconfig --format json . > "$OUT/raw/trivy.json" 2>&1
Goal: zero HIGH/CRITICAL findings and no leaked secrets. Summarize counts by severity.
JavaScript (if package.json exists)
[ -f package-lock.json ] || npm i --package-lock-only
npm audit > "$OUT/raw/npm-audit.txt" 2>&1
Step 3: Dependency Freshness
Ruby (if Gemfile exists)
gem install next_rails --no-document
bundle_report outdated --without-bundler > "$OUT/raw/outdated.txt" 2>&1
gem install libyear-bundler --no-document
libyear-bundler --all > "$OUT/raw/libyear.txt" 2>&1
Track outdated percentage and total libyears behind.
JavaScript (if package.json exists)
npm outdated > "$OUT/raw/npm-outdated.txt" 2>&1
Step 4: Code Coverage
IMPORTANT: Run the test suite with coverage enabled BEFORE running Skunk so it reads
fresh SimpleCov data.
ls -d spec/ test/ 2>/dev/null
COVERAGE=true bundle exec rspec > "$OUT/raw/rspec.txt" 2>&1
cp coverage/.last_run.json "$OUT/raw/coverage-last-run.json" 2>/dev/null
cat coverage/.last_run.json
If the test suite cannot run (missing DB, etc.), fall back to the existing
coverage/.last_run.json and note in the report that coverage may be stale.
If SimpleCov is not configured, note it and recommend adding it.
Step 5: Code Complexity — RubyCritic (with visuals)
RubyCritic's default format is HTML, which includes the churn-vs-complexity overview
chart we screenshot for the report.
gem install rubycritic --no-document
rubycritic app lib --no-browser -p "$OUT/rubycritic" > "$OUT/raw/rubycritic.txt" 2>&1
This produces $OUT/rubycritic/overview.html (the scatter plot) plus code_index.html,
churn_index.html, and complexity_index.html.
Capture screenshots
Render the RubyCritic HTML with headless Chrome and save PNGs into $OUT/screenshots/.
Use whichever Chrome/Chromium binary exists:
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
ABS="$(cd "$OUT/rubycritic" && pwd)"
"$CHROME" --headless=new --disable-gpu --hide-scrollbars \
--window-size=1280,1400 --screenshot="$(pwd)/$OUT/screenshots/rubycritic-overview.png" \
"file://$ABS/overview.html"
"$CHROME" --headless=new --disable-gpu --hide-scrollbars \
--window-size=1280,1600 --screenshot="$(pwd)/$OUT/screenshots/rubycritic-code.png" \
"file://$ABS/code_index.html"
If the chrome-devtools MCP tools are available, you may use navigate_page +
take_screenshot on the same file:// URLs instead — that also works and can capture
full-page shots. If no headless browser is available at all, skip screenshots and note it
in the report; the report still renders without them.
Step 6: Combined Metric — Skunk
PREREQUISITE: Step 4 (coverage) must have run first.
gem install skunk --no-document
skunk app lib > "$OUT/raw/skunk.txt" 2>&1
SkunkScore = (Code Smells + Complexity) * Coverage Penalty. Focus on files with high
complexity + low coverage + high churn.
Step 7: Framework Health & Stats (Rails)
Always use rails_stats instead of Rails' native rake stats — it reports richer metrics
(polymorphic associations, schema stats, a cleaner code-to-test ratio). It depends on
bundle-stats (the bundler-stats gem) and emits its dependency-weight table too, so a
single rails-stats call gives you BOTH application code metrics AND dependency metrics. Do
NOT install or run bundle-stats separately — that would be redundant.
bundle show rails > "$OUT/raw/rails-version.txt" 2>&1
ruby -v > "$OUT/raw/ruby-version.txt" 2>&1
gem install rails_stats --no-document
rails-stats > "$OUT/raw/rails-stats.txt" 2>&1
cloc --exclude-dir=node_modules,vendor,coverage,tmp,.git . > "$OUT/raw/cloc.txt" 2>&1
The rails-stats output has two tables: a dependency table (Name | Total Deps | 1st Level Deps — gems with large transitive trees are prime removal/replacement candidates) and the
code-stats table (Name | Files | Lines | LOC | Classes | Methods …). Check Rails version
support/EOL, code-to-test ratio, and the heaviest dependencies.
Step 8: Runtime Observability
A production Rails app should be able to answer "did something break?" and "is it slow?".
Check the Gemfile/Gemfile.lock for the presence of tooling in each category. If a category
has no gem, flag it in the report as an issue and a departure from best practices.
Exception Tracking
grep -iE "sentry|rollbar|honeybadger|bugsnag|airbrake|exception_notification|appsignal" Gemfile.lock \
> "$OUT/raw/exception-tracking.txt" 2>&1
Look for: sentry-ruby/sentry-rails, rollbar, honeybadger, bugsnag, airbrake,
exception_notification, or appsignal. If none is present, the app has no way to know about
production exceptions — flag it as a high-priority gap.
Performance Monitoring
grep -iE "newrelic|scout_apm|skylight|ddtrace|datadog|appsignal|rack-mini-profiler|prosopite|bullet" Gemfile.lock \
> "$OUT/raw/performance-monitoring.txt" 2>&1
Look for an APM (newrelic_rpm, scout_apm, skylight, ddtrace/datadog, appsignal) and/or
in-process tools (rack-mini-profiler, bullet, prosopite). If nothing is present, the team is
flying blind on performance regressions — flag it as an issue.
Step 9: Development Environment
Check for bin/setup, docker-compose.yml, README setup instructions, CI config
(.github/workflows/, .circleci/), and .env.example.
Step 10: Assemble the Single HTML Report
This is the primary deliverable. Read the template and produce ONE self-contained HTML file
at $OUT/index.html.
-
Read the template: report-template.html (next to this SKILL.md). It uses the
FastRuby.io styleguide (https://fastruby.github.io/styleguide) palette and the Oxygen
font, is mobile/tablet friendly (tables scroll horizontally on small screens), links each
section to the open source tool it used, and contains all CSS inline plus
{{PLACEHOLDER}} markers. Do NOT change the styling or the tool links — only fill the
{{PLACEHOLDER}} markers.
-
Score each category using the Scoring Guidelines below, then fill the executive
summary and health-score table. For each {{*_PCT}} marker, use score / 20 * 100
(e.g. a 15/20 → 75) so the progress bars render correctly. For each {{*_BADGE}},
emit <span class="badge pass">Pass</span>, warn/Warning, or fail/Fail.
Set {{SCORE_CLASS}} on the big total score by severity so the number is NOT misleadingly
green when the score is poor: low for a total under 50 (red), mid for 50-74 (yellow),
high for 75-100 (green).
-
Fill each tool section ({{BUNDLER_AUDIT_CONTENT}}, {{TRIVY_CONTENT}},
{{RUBYCRITIC_CONTENT}}, {{SKUNK_CONTENT}}, {{RAILS_STATS_CONTENT}},
{{BUNDLE_STATS_CONTENT}}, etc.) with real numbers pulled from the files in $OUT/raw/.
Wrap every <table> in <div class="table-wrap">…</div> so it scrolls on mobile. Use
<pre> blocks for short raw excerpts. When a check found nothing, write a positive note
like <p class="empty">No vulnerabilities found.</p>. Never leave a {{PLACEHOLDER}} in
the final file.
{{RAILS_STATS_CONTENT}}: the code-stats table from rails-stats.txt (code LOC, test
LOC, code-to-test ratio, polymorphic associations, schema create_table count).
{{BUNDLE_STATS_CONTENT}}: the dependency table from the SAME rails-stats.txt (top gems
by total transitive dependency count), with a note on removal/replacement candidates.
There is no separate bundle-stats.txt — rails_stats emits this table itself.
{{EXCEPTIONS_CONTENT}}: if an exception-tracking gem was found (see Step 8), name it and
mark it <p class="empty">…</p>. If none, use <p class="flag">No exception tracking gem detected — the app cannot report production errors. This falls short of best practices; add Sentry, Honeybadger, Rollbar, or similar.</p>.
{{PERFORMANCE_CONTENT}}: same pattern for performance/APM gems. If none, flag it:
<p class="flag">No performance monitoring detected — regressions will go unnoticed. Add an APM (New Relic, Scout, Skylight, AppSignal, Datadog) and/or rack-mini-profiler.</p>.
-
Embed screenshots as base64 data URIs so the report is truly a single file. For each
screenshot:
echo "data:image/png;base64,$(base64 -i "$OUT/screenshots/rubycritic-overview.png")"
Put the result in the src="{{RUBYCRITIC_OVERVIEW_IMG}}" attribute. Add any extra shots
to {{RUBYCRITIC_EXTRA_SHOTS}} as additional <div class="shot">…</div> blocks (embed
those base64 too). If a screenshot is missing, remove that <div class="shot"> block.
-
Executive summary ({{EXECUTIVE_SUMMARY}}): 3-5 sentences synthesizing findings
across ALL reports — the health score, the most serious risks, and the standout
strengths.
-
Top 3 recommended actions ({{REC1_*}}–{{REC3_*}}): the three highest-impact
actions to address the highest-priority issues found. Be specific — name the exact gem,
CVE, or file, and say what to do. Order by impact/urgency.
-
Appendix ({{TOOLS_TABLE}}, wrapped in <div class="table-wrap">): a table of every
tool run with its purpose. Link each tool name to its open source project page, e.g.
<a href="https://github.com/fastruby/skunk">skunk</a>,
<a href="https://github.com/rubysec/bundler-audit">bundler-audit</a>,
<a href="https://github.com/presidentbeef/brakeman">brakeman</a>,
<a href="https://github.com/rubymem/bundler-leak">bundler-leak</a>,
<a href="https://github.com/aquasecurity/trivy">trivy</a>,
<a href="https://github.com/fastruby/next_rails">next_rails</a>,
<a href="https://github.com/jaredbeck/libyear-bundler">libyear-bundler</a>,
<a href="https://github.com/simplecov-ruby/simplecov">simplecov</a>,
<a href="https://github.com/whitesmith/rubycritic">rubycritic</a>,
<a href="https://rubygems.org/gems/rails_stats">rails_stats</a>,
<a href="https://rubygems.org/gems/bundler-stats">bundle-stats</a>.
Use {{APPENDIX_NOTES}} for caveats, exclusions, false positives, or a note that SonarQube
already covers some metrics.
-
Write the filled HTML to $OUT/index.html.
-
Report back to the user with the path to $OUT/index.html and a one-paragraph
summary of the health score and the top recommendation.
Scoring Guidelines (0-100 total, 20 per category)
Security (20)
- 20: No vulnerabilities (bundler-audit, Brakeman, bundler-leak, Trivy all clean)
- 15: Low severity only
- 10: Some medium severity
- 5: High severity issues
- 0: Critical vulnerabilities or leaked secrets
Dependencies (20)
- 20: <10% outdated, <5 libyears
- 15: <20% outdated, <15 libyears
- 10: <40% outdated, <30 libyears
- 5: <60% outdated, <50 libyears
- 0: >60% outdated or >50 libyears
Complexity (20)
- 20: No files with complexity >10
- 15: Few files with complexity 11-20
- 10: Some files with complexity 21-50
- 5: Many files with high complexity
- 0: Files with complexity >50
Coverage (20)
- 20: >90% · 15: 70-90% · 10: 50-70% · 5: 30-50% · 0: <30%
Maintainability (20)
- 20: Excellent setup, CI, documentation
- 15: Good setup with minor gaps
- 10: Adequate but needs improvement
- 5: Significant maintainability issues
- 0: Major maintainability problems
Important Notes
- Don't duplicate effort: if SonarQube is in use, note which metrics it already tracks.
- Focus on business logic: complexity tools target
app/ and lib/, not tests.
- Context matters: a high SkunkScore in a rarely-changed file is less urgent than a
moderate score in a frequently-modified file.
- Prioritize by impact: files with high churn + high complexity + low coverage first.
- Track over time: the timestamped directory is the baseline — keep it to compare
future runs.