
Bloat Detector
- 137 installs
- 325 repo stars
- Updated August 2, 2026
- athola/claude-night-market
Scan an AI-assisted codebase for vibe-coding slop—duplicate blocks, oversized commits, and agent-style repetition—before merge or refactor sprints.
About
Bloat Detector is a Claude Night Market conserve module aimed at solo builders who ship fast with agents and need an objective pass for AI-generated debt. It targets patterns that differ from classic legacy bloat: repeated logic blocks instead of shared utilities, enormous insertion commits without matching tests, and other vibe-coding tells tied to agent-assisted workflows. The module documents runnable checks—duplicate detection scripts, git log heuristics, and grep-based signature clustering—with recommended actions such as refactor versus investigate. It frames why the problem matters using published trends on copy-paste growth and collapsing refactor rates so you can justify cleanup to yourself or stakeholders. Run it when a feature branch balloons overnight or before a release candidate merge; pair results with human judgment because confidence scores are heuristics, not verdicts. It complements generic linters by focusing on how models tend to duplicate and over-insert rather than syntax alone.
- Detects tab-completion bloat via duplicate blocks (5+ lines) with detect_duplicates.py
- Flags massive single commits (>500 insertions) as vibe-coding signatures
- Documents AI-specific slop patterns with stated confidence levels (e.g. 85% on repetitive logic)
- Supports JSON output for CI integration on duplicate detection
- Cites industry signals: 8x duplication growth and refactoring share falling below 10%
Bloat Detector by the numbers
- 137 all-time installs (skills.sh)
- Ranked #391 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/athola/claude-night-market --skill bloat-detectorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 137 |
|---|---|
| repo stars | ★ 325 |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 2, 2026 |
| Repository | athola/claude-night-market ↗ |
What it does
Scan an AI-assisted codebase for vibe-coding slop—duplicate blocks, oversized commits, and agent-style repetition—before merge or refactor sprints.
Files
Bloat Detector
Systematically detect and eliminate codebase bloat through progressive analysis tiers.
Bloat Categories
| Category | Examples |
|---|---|
| Code | Dead code, God classes, Lava flow, duplication |
| AI-Generated | Tab-completion bloat, vibe coding, hallucinated deps |
| Documentation | Redundancy, verbosity, stale content, slop |
| Dependencies | Unused imports, dependency bloat, phantom packages |
| Git History | Stale files, low-churn code, massive single commits |
Quick Start
Tier 1: Quick Scan (2-5 min, no tools)
/bloat-scanDetects: Large files, stale code, old TODOs, commented blocks, basic duplication
Tier 2: Targeted Analysis (10-20 min, optional tools)
/bloat-scan --level 2 --focus code # or docs, depsAdds: Static analysis (Vulture/Knip), git churn hotspots, doc similarity
Tier 3: Deep Audit (30-60 min, full tooling)
/bloat-scan --level 3 --report audit.mdAdds: Cross-file redundancy, dependency graphs, readability metrics
When To Use
| Do | Don't |
|---|---|
| Context usage > 30% | Active feature development |
| Quarterly maintenance | Time-sensitive bugs |
| Pre-release cleanup | Codebase < 1000 lines |
| Before major refactoring | Tools unavailable (Tier 2/3) |
When NOT To Use
- Active feature development
- Time-sensitive bugs
- Codebase < 1000 lines
Confidence Levels
| Level | Confidence | Action |
|---|---|---|
| HIGH | 90-100% | Safe to remove |
| MEDIUM | 70-89% | Review first |
| LOW | 50-69% | Investigate |
Prioritization
Priority = (Token_Savings × 0.4) + (Maintenance × 0.3) + (Confidence × 0.2) + (Ease × 0.1)Module Architecture
Tier 1 (always available):
- See
modules/quick-scan.md- Heuristics, no tools - See
modules/git-history-analysis.md- Staleness, churn, vibe coding signatures - See
modules/growth-analysis.md- Growth velocity, forecasts, threshold alerts
Tier 2 (optional tools):
- See
modules/code-bloat-patterns.md- Anti-patterns (God class, Lava flow) - See
modules/ai-generated-bloat.md- AI-specific patterns (Tab bloat, hallucinations) - See
modules/documentation-bloat.md- Redundancy, readability, slop detection - See
modules/static-analysis-integration.md- Vulture, Knip
Shared:
- See
modules/remediation-types.md- DELETE, REFACTOR, CONSOLIDATE, ARCHIVE
Ecosystem-Level Detection
Patterns that span plugin boundaries or manifest configuration, discovered through ecosystem-wide audits.
alwaysApply Accumulation
Flag plugins with 3+ skills where alwaysApply: true. Each always-on skill injects its full text into every session, creating a baseline token floor before the user types anything. Sum the estimated_tokens fields to report total per-session cost.
Hook Registration Gaps
Compare hooks declared in plugin.json or openpackage.yml against entries in hooks.json. A hook present in hooks.json but absent from the manifest is invisible to the plugin loader and cannot be audited, versioned, or disabled through normal plugin management.
Boilerplate Footer Detection
Scan skill files for identical multi-line text blocks repeated across 10+ files (e.g., generic troubleshooting sections like "Command not found / Permission errors / Unexpected behavior"). These are copy-paste artifacts that inflate token cost without adding skill-specific value.
ToC Bloat in Skills
Skills loaded into model context gain nothing from HTML-style Tables of Contents. Detect ## Table of Contents followed by bulleted anchor-link lists. These waste tokens since the model reads sequentially, not via hyperlinks.
Unregistered Module Subdirectories
Compare files on disk in skills/*/modules/ against the modules: list in each skill's SKILL.md frontmatter. Files that exist on disk but are not listed in the manifest are invisible to progressive loading and may be dead weight or missing from the load path.
Auto-Exclusions
Always excludes: .venv, __pycache__, .git, node_modules, dist, build, vendor
Also respects: .gitignore, .bloat-ignore
Safety
- Never auto-delete - all changes require approval
- Dry-run support -
--dry-runfor previews - Backup branches - created before bulk changes
Related
bloat-auditoragent - Executes scansunbloat-remediatoragent - Safe remediationcontext-optimizationskill - MECW principles
AI-Generated Bloat Detection Module
Detect bloat patterns specific to AI-assisted coding: vibe coding artifacts, slop patterns, and agent psychosis indicators.
Why This Module Exists
AI coding has created qualitatively different bloat than traditional development:
- 2024: First year copy/pasted lines exceeded refactored lines (GitClear)
- Refactoring: Dropped from 25% (2021) to <10% (2024), predicted 3% (2025)
- Duplication: 8x increase in 5+ line code blocks
AI Bloat Patterns
1. Tab-Completion Bloat (Repetitive Logic)
Definition: Same pattern repeated 3+ times instead of abstracted into shared function.
# Detect similar code blocks (built-in, no external deps)
python3 plugins/conserve/scripts/detect_duplicates.py . --min-lines 5
# JSON output for CI integration
python3 plugins/conserve/scripts/detect_duplicates.py . --format json --threshold 15
# Heuristic: functions with near-identical signatures
grep -rn "^def " --include="*.py" . | cut -d: -f2 | sort | uniq -c | sort -rn | head -10Confidence: HIGH (85%) Action: REFACTOR - extract to shared utility Rationale: AI suggests new implementations rather than reusing existing code
2. Massive Single Commits (Vibe Coding Signature)
Definition: Commits with >500 insertions, especially without proportional tests.
# Find vibe coding commits
git log --oneline --shortstat | grep -E "[0-9]{3,} insertion" | head -20
# Commits with high insertion:deletion ratio (adding without cleanup)
git log --shortstat --pretty=format:"%h %s" | awk '/insertion|deletion/ {
ins=$4; del=$6;
if (ins > 200 && (del == "" || ins/del > 10)) print prev, ins, del
} {prev=$0}'Confidence: MEDIUM (70%) Action: INVESTIGATE - review for understanding gaps Rationale: Large additions without refactoring indicate Tab-driven development
3. Hallucinated Dependencies
Definition: Imports referencing non-existent packages (AI hallucination).
# Python: Check for uninstallable packages
pip freeze > /tmp/installed.txt
grep -rh "^import \|^from " --include="*.py" . | \
sed 's/^import //;s/^from //;s/ import.*//' | \
sort -u | while read pkg; do
root=$(echo $pkg | cut -d. -f1)
grep -q "^$root" /tmp/installed.txt || echo "HALLUCINATED?: $pkg"
done
# JavaScript: Check for phantom packages
jq -r '.dependencies // {} | keys[]' package.json | while read pkg; do
npm view $pkg version 2>/dev/null || echo "HALLUCINATED?: $pkg"
doneConfidence: HIGH (95%) Action: DELETE or REPLACE Rationale: AI invents plausible-sounding packages (slopsquatting risk)
4. Happy Path Only (Test Coverage Gap)
Definition: Code >200 lines with no corresponding tests, or tests without error assertions.
# Files without test coverage
find . -name "*.py" ! -path "*/test*" \
-not -path "*/.venv/*" -not -path "*/__pycache__/*" \
-not -path "*/node_modules/*" -not -path "*/.git/*" \
-exec sh -c '
lines=$(wc -l < "$1")
if [ $lines -gt 200 ]; then
base=$(basename "$1" .py)
test_exists=$(find . -name "test_${base}.py" -o -name "${base}_test.py" \
-not -path "*/.venv/*" -not -path "*/__pycache__/*" \
-not -path "*/node_modules/*" -not -path "*/.git/*" | head -1)
[ -z "$test_exists" ] && echo "UNTESTED ($lines lines): $1"
fi
' _ {} \;
# Tests without error/exception assertions
grep -rL "assert.*Error\|assert.*Exception\|pytest.raises\|with self.assertRaises" \
--include="test_*.py" .Confidence: HIGH (90%) Action: AUGMENT_TESTS before adding more code Rationale: AI generates happy path; errors require human insight
5. Premature Abstraction
Definition: Base classes/interfaces with only 1-2 implementations.
# Python: Abstract classes with single inheritor
grep -rn "class.*ABC\|@abstractmethod" --include="*.py" . | cut -d: -f1 | sort -u | while read f; do
class=$(grep -oP "class \K\w+" "$f" | head -1)
inheritors=$(grep -rn "($class)" --include="*.py" . | wc -l)
[ $inheritors -lt 2 ] && echo "PREMATURE: $class in $f (${inheritors} inheritors)"
doneConfidence: HIGH (85%) Action: INLINE - remove abstraction until 3rd use case Rationale: AI suggests "scalable" patterns for simple problems
6. Enterprise Cosplay
Definition: Microservices, Kubernetes, complex architecture for simple applications.
# Docker complexity for simple apps
if [ -f docker-compose.yml ]; then
services=$(grep -c "^ [a-z].*:$" docker-compose.yml)
code_lines=$(find . \( -name "*.py" -o -name "*.js" \) \
-not -path "*/.venv/*" -not -path "*/__pycache__/*" \
-not -path "*/node_modules/*" -not -path "*/.git/*" | xargs wc -l 2>/dev/null | tail -1 | awk '{print $1}')
ratio=$((code_lines / services))
[ $ratio -lt 500 ] && echo "ENTERPRISE_COSPLAY: $services services for $code_lines lines"
fi
# Kubernetes for CRUD
[ -d k8s ] && [ $(find . -name "*.py" -not -path "*/.venv/*" -not -path "*/__pycache__/*" -not -path "*/node_modules/*" -not -path "*/.git/*" | xargs wc -l | tail -1 | awk '{print $1}') -lt 5000 ] && \
echo "ENTERPRISE_COSPLAY: Kubernetes for <5000 lines"Confidence: MEDIUM (70%) Action: SIMPLIFY - evaluate if complexity is justified Rationale: AI defaults to "production-ready" patterns without context
7. Documentation Slop
Definition: AI-generated docs with excessive hedging, formulaic structure, surface insights.
# Hedge word density (AI slop indicators)
hedge_words="worth noting|arguably|to some extent|it's important|consider that|generally speaking"
for f in $(find . -name "*.md" -not -path "*/.venv/*" -not -path "*/node_modules/*" -not -path "*/.git/*"); do
total=$(wc -w < "$f")
hedges=$(grep -oiE "$hedge_words" "$f" | wc -l)
if [ $total -gt 100 ]; then
density=$((hedges * 1000 / total))
[ $density -gt 20 ] && echo "DOC_SLOP ($density/1000): $f"
fi
doneConfidence: MEDIUM (65%) Action: REWRITE with concrete specifics Rationale: AI safety training creates artificial hedging
Scoring
AI_BLOAT_SCORES = {
'tab_completion_bloat': 25,
'massive_single_commit': 15,
'hallucinated_dependency': 35,
'happy_path_only': 30,
'premature_abstraction': 20,
'enterprise_cosplay': 25,
'documentation_slop': 10,
}
def ai_bloat_score(detected_patterns):
return min(100, sum(AI_BLOAT_SCORES.get(p, 0) for p in detected_patterns))Integration with Existing Tiers
Tier 1 (Quick Scan): Massive single commits, hedge word density Tier 2 (Targeted): Duplication ratio, test coverage gaps, premature abstraction Tier 3 (Deep Audit): Hallucinated dependencies, enterprise cosplay analysis
Output Format
file: src/services/user_manager.py
ai_bloat_patterns:
- tab_completion_bloat
- happy_path_only
ai_bloat_score: 55/100
indicators:
similar_blocks: 4
test_coverage: 0%
commit_size: 847 lines
confidence: HIGH
action: REFACTOR + ADD_TESTS
rationale: "Vibe coding signature - large addition without tests or abstraction"Prevention Recommendations
When AI bloat is detected, recommend:
1. Refactoring Budget: Add 25 lines of refactoring for every 100 lines added 2. Test Requirement: No merge without proportional test coverage 3. Understanding Gate: Require explanation of non-trivial changes 4. 24-Hour Rule: Sleep before adopting new AI-suggested patterns
Related
code-bloat-patterns- Traditional anti-patterns (God class, Lava flow)documentation-bloat- Readability metricsimbue:anti-cargo-cult- Understanding verification protocol- Knowledge corpus:
agent-psychosis-codebase-hygiene.md
Code Bloat Patterns Module
Detect anti-patterns using pattern recognition and heuristics. Works without external tools.
Tool Preference (Claude Code 2.1.31+): The bash snippets in this module are reference implementations for external script execution or CI pipelines. When performing these analyses directly within Claude Code, prefer native tools: use Grep instead ofgrep, Glob instead offind, and Read instead ofcat/sed.
Anti-Patterns
1. God Class
Definition: Single class with > 500 lines, > 10 methods, multiple responsibilities.
# Quick detection
find . -name "*.py" \
-not -path "*/.venv/*" -not -path "*/__pycache__/*" \
-not -path "*/node_modules/*" -not -path "*/.git/*" \
-exec sh -c 'lines=$(wc -l < "$1"); [ $lines -gt 500 ] && echo "GOD_CLASS: $1 - $lines lines"' _ {} \;Confidence: HIGH (85%) | Action: REFACTOR into focused modules
2. Lava Flow
Definition: Ancient untouched code - commented blocks, old TODOs.
# Find files with >20% commented code
grep -rn "^#\|^//" --include="*.py" . | cut -d: -f1 | sort | uniq -c | sort -rn | head -10Confidence: HIGH (90%) | Action: DELETE commented code
3. Dead Code
Detection: Use static analysis (Vulture/Knip) or fallback heuristic:
# Heuristic: find functions with 0 calls
grep -rn "^def " --include="*.py" . | while read line; do
func=$(echo $line | awk '{print $2}' | cut -d'(' -f1)
[ $(git grep -c "$func(" 2>/dev/null || echo 0) -eq 1 ] && echo "DEAD: $func"
doneConfidence: MEDIUM (70%) heuristic, HIGH (90%) with tools | Action: DELETE
4. Import Bloat
# Star imports (block tree-shaking)
grep -rn "^from .* import \*" --include="*.py" .
# Unused imports (requires autoflake)
autoflake --check --remove-all-unused-imports -r .Confidence: HIGH (95%) | Action: Fix imports
5. Duplication
Intra-file: Hash-based block detection (5+ line matches) Cross-file: Function signature matching Semantic: AST comparison (80%+ similarity)
Confidence: HIGH (85%) | Action: EXTRACT to shared utility
Language-Specific
Python
- Circular imports: Files with 20+ imports
- Deep nesting: > 4 indentation levels
JavaScript/TypeScript
- Barrel files:
export * frombreaks tree-shaking - CommonJS in ESM:
module.exports/require()blocks bundler optimization
AI-Amplified Patterns
These traditional patterns are amplified by AI coding tools:
6. Tab-Completion Duplication
Definition: AI suggests similar code blocks instead of reusing existing functions. 2024 Data: 8x increase in 5+ line duplicated blocks (GitClear)
# Quick detection: near-identical function signatures
grep -rn "^def " --include="*.py" . | awk -F'def ' '{print $2}' | \
cut -d'(' -f1 | sort | uniq -c | sort -rn | awk '$1 > 1'Confidence: HIGH (85%) | Action: EXTRACT shared utility
7. Dead Wrapper / Facade Bloat
Definition: Modules that wrap existing functionality without adding meaningful logic: thin facades, unused service interfaces, or re-export layers with no consumers.
Signals:
- File imports from another internal module and re-exports similar API
- No external imports of the wrapper (0 refs from outside itself)
- Not a proper package (missing
__init__.pyfor Python) - Docstring examples show imports but no actual code uses them
- Functionality already exists in the wrapped module or in
examples/
# Find Python files that only re-export from other internal modules
for f in $(find . -name "*.py" -not -path "*/test*" -not -path "*/__pycache__/*" -not -path "*/.venv/*" -not -path "*/node_modules/*" -not -path "*/.git/*"); do
# Check if file mostly imports and re-calls another module's functions
imports=$(grep -c "^from \.\." "$f" 2>/dev/null || echo 0)
total=$(wc -l < "$f" 2>/dev/null || echo 0)
refs=$(git grep -l "$(basename "$f" .py)" -- "*.py" 2>/dev/null | grep -v "$f" | wc -l)
if [ "$imports" -gt 2 ] && [ "$refs" -eq 0 ] && [ "$total" -gt 50 ]; then
echo "DEAD_WRAPPER: $f ($total lines, $imports internal imports, 0 external refs)"
fi
doneAlso check for intra-file dead wrappers:
# Find classes/functions that only delegate to another method with no transformation
grep -rn "def .*self" --include="*.py" . | while read line; do
file=$(echo "$line" | cut -d: -f1)
lineno=$(echo "$line" | cut -d: -f2)
# Check if function body is just "return self.other_thing(...)"
body=$(sed -n "$((lineno+1)),$((lineno+3))p" "$file" 2>/dev/null)
if echo "$body" | grep -qP '^\s+return self\.\w+\(' && [ $(echo "$body" | wc -l) -le 2 ]; then
echo "PASSTHROUGH: $file:$lineno - trivial delegation"
fi
doneConfidence: HIGH (85%) for whole-file wrappers, MEDIUM (70%) for intra-file passthrough Action: DELETE (whole-file) or INLINE (intra-file passthrough)
8. Premature Abstraction
Definition: Base classes/interfaces with <3 implementations (YAGNI violation). AI Cause: AI defaults to "scalable" patterns without context.
# Find abstract classes with few inheritors
grep -rln "ABC\|abstractmethod" --include="*.py" . | while read f; do
class=$(grep -oP "class \K\w+" "$f" | head -1)
[ $(grep -rc "($class)" --include="*.py" . 2>/dev/null) -lt 3 ] && echo "PREMATURE: $class"
doneConfidence: HIGH (80%) | Action: INLINE until 3rd use case
9. Happy Path Bias
Definition: Tests verify success paths only; no error handling tested. AI Cause: AI optimizes for "works" demonstrations.
# Tests without error assertions
grep -rL "Error\|Exception\|raises\|fail\|invalid" --include="test_*.py" .Confidence: MEDIUM (70%) | Action: ADD error path tests
For the full set of AI-specific patterns, see: @module:ai-generated-bloat
Scoring
PATTERN_SCORES = {
'god_class': 30, 'lava_flow': 25, 'dead_code': 35,
'import_bloat': 15, 'duplication': 20, 'dead_wrapper': 30
}
score = min(100, sum(PATTERN_SCORES[p] for p in detected))Output Format
file: src/legacy/manager.py
patterns: [god_class, lava_flow, import_bloat]
bloat_score: 85/100
confidence: HIGH
token_estimate: ~3,400
action: REFACTORAll actions require user approval.
Documentation Bloat Module
Detect documentation redundancy, verbosity, and poor readability.
Detection Categories
1. Duplicate Documentation
Cross-File (Jaccard Similarity)
# Quick similarity check between two files
words1=$(tr '[:space:]' '\n' < file1.md | sort -u)
words2=$(tr '[:space:]' '\n' < file2.md | sort -u)
# > 70% overlap = potential duplication| Similarity | Confidence | Action |
|---|---|---|
| > 90% | HIGH (95%) | DELETE one, keep recent |
| 70-90% | MEDIUM (80%) | MERGE, preserve unique |
| 50-70% | LOW (60%) | CROSS-LINK |
Intra-File (Section Hashing)
Hash each ## section's normalized content. Duplicates = repeated sections.
Confidence: HIGH (85%)
2. Excessive Verbosity
| Metric | Threshold | Action |
|---|---|---|
| Word count | > 500 words/section | Condense |
| Sentence length | > 25 words avg | Simplify |
| Passive voice | > 30% | Rewrite active |
| Readability | Flesch < 40 | Simplify |
# Quick verbosity check
wc -w file.md # Total words
rg -c '\.' file.md # Approximate sentences (or grep -c)3. Stale Documentation
| Signal | Confidence | Action |
|---|---|---|
| Unchanged 12+ months | HIGH (85%) | Review/Archive |
| References deleted code | HIGH (90%) | Update/Delete |
| No git activity | MEDIUM (75%) | Investigate |
# Find stale docs
git log -1 --format="%ar" -- docs/*.md | rg -E "year|months"
# fallback: grep -E "year|months"4. Missing/Outdated References
- Broken internal links:
rg -oP '\[.*?\]\((?!http).*?\)' *.md(orgrep -oP) - References to deleted files
- Outdated API examples
Confidence: HIGH (90%) for broken links
Scoring
def doc_bloat_score(metrics):
score = 0
if metrics['duplicate_ratio'] > 0.3: score += 30
if metrics['avg_words_per_section'] > 500: score += 20
if metrics['readability'] < 40: score += 15
if metrics['stale_months'] > 12: score += 25
return min(100, score)Output Format
file: docs/old-guide.md
bloat_type: [duplicate, verbose, stale]
bloat_score: 72/100
confidence: HIGH
token_estimate: ~1,200
similar_to: docs/guide.md (87%)
action: MERGERelated
quick-scan- Tier 1 stale detectiongit-history-analysis- Activity signals
Git History Analysis Module
Detect bloat using git history: staleness, churn metrics, and reference counting.
Core Techniques
1. Staleness Detection
Command:
# Files not modified in last 6 months
git log --since="6 months ago" --name-only --pretty=format: | sort -u > recent.txt
comm -13 recent.txt <(git ls-files | sort) > stale_files.txtStaleness Scoring:
def staleness_score(months_since_change):
if months_since_change > 24:
return 95 # Almost certainly abandoned
elif months_since_change > 12:
return 85 # Likely abandoned
elif months_since_change > 6:
return 65 # Possibly stale
else:
return 20 # ActiveConfidence Modifiers:
- File type: Config files -20%, code files +0%
- Last author: If single author who left project +15%
- Dependencies: If no imports found +25%
2. Reference Counting
Detect unused files:
# For each file, count references in codebase
git ls-files | while read file; do
filename=$(basename "$file")
refs=$(git grep -l "$filename" | wc -l)
if [ $refs -eq 1 ]; then # Only self-reference
echo "0 $file"
else
echo "$((refs - 1)) $file" # Subtract self
fi
done | grep "^0 "Confidence: HIGH (90%) if zero refs and stale
False Positives:
- Entry points (main.py, index.js)
- Configuration files
- Documentation
3. Code Churn Metrics
Churn formula:
# Lines added + deleted per file
git log --numstat --pretty="%H" -- $file | \
awk '{added+=$1; deleted+=$2} END {print added+deleted}'Churn Categories:
- High churn (>1000 changes/year): Active development
- Low churn (<50 changes/year): Stable or abandoned
- Zero churn + old: Strong bloat signal
Filter out cleanup-churn (release sweeps, frontmatter-only edits):
A naive commit count over-flags files swept by repo-wide release operations (version bumps, frontmatter additions). Filter to commits that made substantive changes to the file under analysis.
# Count only commits with >5 line net change in the file
git log --numstat --pretty=tformat:%H -- "$file" | \
awk '/^[0-9]/ && ($1 + $2) > 5 { count++ } END { print count }'Compare against the unfiltered count: if substantive_count < total_count / 3, the file is cleanup-churn not design churn. Downgrade the thrashing/hotspot signal in that case.
Worked example: rigorous-reasoning/SKILL.md showed 12 commits in 30 days. After filtering for substantive body changes (>5 line net), only 1 commit remained. The rest were repo-wide frontmatter sweeps (version bumps, tag adds, description tweaks). This file is NOT a thrashing hotspot; the signal was a false positive from cleanup-churn.
Hotspot Detection:
def is_hotspot(churn, complexity):
"""
Hotspot = High churn × High complexity
Indicates technical debt accumulation
"""
churn_score = normalize_churn(churn)
complexity_score = cyclomatic_complexity(file)
return churn_score * complexity_score > threshold4. Ownership Analysis
Detect abandoned code:
# Find files where primary author has no recent commits
git log --format="%an" --since="6 months ago" | sort -u > active_authors.txt
git ls-files | while read file; do
primary_author=$(git log --format="%an" -- "$file" | sort | uniq -c | sort -rn | head -1 | awk '{$1=""; print $0}' | sed 's/^ //')
if ! grep -qF "$primary_author" active_authors.txt; then
echo "$file - Primary author inactive: $primary_author"
fi
doneConfidence: MEDIUM (70%) - Ownership transfer is possible
5. Branch Analysis
Detect orphaned feature branches:
# Branches not merged in 6+ months
git for-each-ref --sort=-committerdate refs/heads/ --format='%(committerdate:short) %(refname:short)' | \
while read date branch; do
age_days=$(( ($(date +%s) - $(date -d "$date" +%s)) / 86400 ))
if [ $age_days -gt 180 ]; then
echo "$branch - ${age_days} days old"
fi
doneAction: Suggest cleanup or archival
Integrated Analysis
Multi-Signal Validation
Combine signals for higher confidence:
def calculate_bloat_confidence(file):
signals = []
# Staleness
months = months_since_last_change(file)
if months > 12:
signals.append(('stale', 85, months))
# No references
refs = count_references(file)
if refs == 0:
signals.append(('unused', 90, refs))
# Low churn
churn = calculate_churn(file)
if churn < 50: # < 50 changes/year
signals.append(('low_churn', 70, churn))
# Inactive owner
if is_owner_inactive(file):
signals.append(('inactive_owner', 65, None))
# Combined confidence
if len(signals) >= 3:
return 'HIGH', signals
elif len(signals) == 2:
return 'MEDIUM', signals
else:
return 'LOW', signalsExample Output
file: src/deprecated/old_api.py
confidence: HIGH
signals:
- type: stale
score: 85
detail: 18 months since last change
- type: unused
score: 90
detail: Zero references found
- type: low_churn
score: 70
detail: 12 changes in last year
combined_score: 82
recommendation: DELETE
rationale: |
Multiple strong signals indicate abandonment:
- No changes in 18 months
- No code references
- Minimal historical activity
Safe to remove with archival backup.AskGit Integration (Optional)
If AskGit is available, use SQL for advanced queries:
-- Find files with high churn but low recent activity
SELECT
file_path,
SUM(additions + deletions) as total_churn,
MAX(author_when) as last_change
FROM commits
WHERE author_when < date('now', '-6 months')
GROUP BY file_path
HAVING total_churn > 1000
ORDER BY total_churn DESC;Performance Optimization
Caching Strategy:
# Cache git log results for reuse
git log --all --numstat --pretty=format:'%H|%an|%ai' > /tmp/git_cache.txt
# Query cache instead of running git log repeatedly
grep "path/to/file" /tmp/git_cache.txtIncremental Updates:
- Store previous scan results
- Only analyze changed files
- Delta reporting
Safety Checks
Before flagging for deletion:
1. Test Files: Exclude test_*.py, *.spec.js 2. Migrations: Database migrations must never auto-delete 3. CI/CD: Files in .github/, .gitlab-ci.yml 4. Documentation: User-facing docs need manual review
Whitelist Patterns:
safe_paths:
- tests/
- migrations/
- .github/
- docs/api/ # API docs are references, not code
excluded_from_bloat_analysis:
# Cache directories (always exclude from counts)
- .venv/
- venv/
- __pycache__/
- .pytest_cache/
- .mypy_cache/
- .ruff_cache/
- .tox/
- .git/
# Dependencies and build artifacts
- node_modules/
- vendor/
- dist/
- build/Integration with Quick Scan
Git analysis validates quick scan findings:
def validate_quick_scan_finding(finding):
# Quick scan says file is bloated
# Git analysis confirms or refutes
git_score = analyze_git_history(finding.file)
if quick_scan.score > 80 and git_score > 80:
return 'HIGH_CONFIDENCE'
elif quick_scan.score > 60 and git_score > 60:
return 'MEDIUM_CONFIDENCE'
else:
return 'LOW_CONFIDENCE' # Conflicting signalsNext Steps
Based on git analysis:
- HIGH confidence: Create cleanup PR
- MEDIUM confidence: Run static analysis (Tier 2)
- LOW confidence: Manual code review
Growth Analysis Module
Track codebase growth velocity using git history. Forecast future size, predict threshold crossings, and rank directories by urgency.
This module replaces the former standalone /analyze-growth command (removed in v1.6.0). It runs as part of /bloat-scan --growth.
When to Load
Load this module when:
- Running
/bloat-scan --growth - Investigating rapid file or line count increases
- Planning capacity for skill files approaching token limits
- Preparing quarterly growth reports
Core Metrics
1. File Count Velocity
Track how fast new files appear in a directory tree.
# File count per week for the last 8 weeks
for i in $(seq 0 7); do
date=$(date -d "$((i * 7)) days ago" +%Y-%m-%d)
count=$(git log --until="$date" --diff-filter=A \
--name-only --pretty=format: -- "$TARGET_DIR" | \
sort -u | wc -l)
echo "$date $count"
done | sortOutput columns: date, cumulative file count
2. Line Count Velocity
Measure net line growth over recent commits.
# Net lines added per week for last 8 weeks
for i in $(seq 0 7); do
start=$(date -d "$(( (i+1) * 7 )) days ago" +%Y-%m-%d)
end=$(date -d "$((i * 7)) days ago" +%Y-%m-%d)
git log --since="$start" --until="$end" \
--numstat --pretty=format: -- "$TARGET_DIR" | \
awk '{added+=$1; deleted+=$2}
END {print added - deleted}'
doneNegative values indicate shrinkage (good after cleanup).
3. Commit Frequency
Count commits touching a path over rolling windows.
# Commits per week for last 8 weeks
for i in $(seq 0 7); do
start=$(date -d "$(( (i+1) * 7 )) days ago" +%Y-%m-%d)
end=$(date -d "$((i * 7)) days ago" +%Y-%m-%d)
count=$(git log --since="$start" --until="$end" \
--oneline -- "$TARGET_DIR" | wc -l)
echo "week-$i: $count commits"
done4. Size Snapshot
Current state measurement for the target path.
# Total lines in tracked files (exclude cache dirs)
git ls-files -- "$TARGET_DIR" | \
grep -v -E '(\.venv|__pycache__|node_modules|\.git)' | \
xargs wc -l 2>/dev/null | tail -130-Day Forecast
Use simple linear regression on the last 8 weekly data points to project 30 days forward.
Algorithm
def forecast_30d(weekly_counts):
"""
Linear least-squares fit on weekly data.
Returns projected value 4.3 weeks from now.
"""
n = len(weekly_counts)
if n < 3:
return None # Not enough data
xs = list(range(n))
x_mean = sum(xs) / n
y_mean = sum(weekly_counts) / n
numerator = sum(
(x - x_mean) * (y - y_mean)
for x, y in zip(xs, weekly_counts)
)
denominator = sum((x - x_mean) ** 2 for x in xs)
if denominator == 0:
return y_mean # Flat line
slope = numerator / denominator
intercept = y_mean - slope * x_mean
# 30 days = ~4.3 weeks beyond last data point
future_x = (n - 1) + 4.3
return slope * future_x + interceptInterpreting Forecasts
- Slope > 0: Growing. Report weekly rate.
- Slope ~ 0: Stable. No action needed.
- Slope < 0: Shrinking. Recent cleanup likely working.
Report the R-squared value when possible. Low R-squared (< 0.5) means the trend is noisy and the forecast is unreliable.
Threshold Crossing Predictions
Given a target limit (e.g., 500-line skill file limit), calculate when the current growth rate will cross it.
Algorithm
def weeks_until_threshold(current_size, weekly_rate, limit):
"""
Returns weeks until current_size reaches limit
at the given weekly_rate.
Returns None if rate <= 0 (will never cross).
"""
if weekly_rate <= 0:
return None
remaining = limit - current_size
if remaining <= 0:
return 0 # Already exceeded
return remaining / weekly_rateDefault Thresholds
| Target | Limit | Rationale |
|---|---|---|
| Skill file | 500 lines | Progressive loading boundary |
| Module file | 300 lines | Single-responsibility cap |
| Python source | 500 lines | God class indicator |
| Markdown doc | 300 lines | Reader attention limit |
Override thresholds with --threshold <lines>.
Urgency Rankings
Rank directories or files by how soon they will need attention.
Scoring Formula
urgency = growth_rate * (current_size / threshold) * recency_weightWhere:
growth_rate: Lines per week (normalized 0-1)current_size / threshold: How close to the limit (0-1+)recency_weight: 1.5 if accelerating, 1.0 if steady,
0.5 if decelerating
Urgency Categories
| Category | Score Range | Action |
|---|---|---|
| Critical | > 0.8 | Modularize or split now |
| High | 0.5 - 0.8 | Plan optimization this sprint |
| Medium | 0.2 - 0.5 | Add to backlog |
| Low | < 0.2 | No action needed |
Acceleration Detection
Compare the growth rate of the last 4 weeks against the preceding 4 weeks.
def detect_acceleration(weekly_rates):
if len(weekly_rates) < 8:
return "insufficient_data"
recent = sum(weekly_rates[-4:]) / 4
earlier = sum(weekly_rates[-8:-4]) / 4
if earlier == 0:
return "new_growth" if recent > 0 else "stable"
ratio = recent / earlier
if ratio > 1.5:
return "accelerating"
elif ratio < 0.5:
return "decelerating"
return "steady"Output Format
Terminal Report
=== Growth Analysis: plugins/conserve/skills/ ===
Current State:
Files: 47
Lines: 8,234
Avg: 175 lines/file
30-Day Forecast:
Files: +5 (52 projected)
Lines: +820 (9,054 projected)
Rate: ~205 lines/week
Threshold Alerts:
bloat-detector/SKILL.md 412/500 lines ~4 weeks to limit
context-optimization.md 289/300 lines ~1 week to limit [!]
Urgency Rankings:
[CRITICAL] context-optimization.md 0.92
[HIGH] bloat-detector/SKILL.md 0.67
[MEDIUM] token-conservation.md 0.34
[LOW] performance-monitoring.md 0.11Machine-Readable Output
growth_analysis:
target: plugins/conserve/skills/
snapshot:
files: 47
lines: 8234
date: "2026-03-10"
forecast_30d:
files: 52
lines: 9054
confidence: 0.78
weekly_rate:
files: 1.2
lines: 205
threshold_alerts:
- path: context-optimization.md
current: 289
limit: 300
weeks_remaining: 1
urgency: critical
- path: bloat-detector/SKILL.md
current: 412
limit: 500
weeks_remaining: 4
urgency: high
rankings:
- path: context-optimization.md
urgency: 0.92
category: critical
acceleration: accelerating
- path: bloat-detector/SKILL.md
urgency: 0.67
category: high
acceleration: steadyIntegration with Bloat Scan
Growth analysis feeds into the bloat detection pipeline:
- Fast-growing files get flagged for proactive review
before they become bloated
- Threshold alerts trigger modularization suggestions
from the remediation-types module
- Urgency rankings prioritize the bloat scan report's
findings list
Coordination with Other Modules
quick-scan: Growth data adds time dimension to
size-based findings
git-history-analysis: Shares git log data;
growth-analysis focuses on trends while git-history focuses on staleness and churn
remediation-types: Growth-triggered items map to
REFACTOR (split) or ARCHIVE (stabilize) actions
Limitations
- Requires at least 3 weeks of git history for
meaningful forecasts
- Linear projection does not capture seasonal patterns
or burst development cycles
- Merge commits can skew line counts; use
--no-merges
when possible
- Renamed files appear as delete + add, inflating
apparent growth
Quick Scan Module
Fast heuristic-based bloat detection without external tools. Completes in < 5 minutes.
Detection Patterns
1. Large Files (God Class Candidates)
# Find files > 500 lines (excluding cache and dependency directories)
find . -type f \( -name "*.py" -o -name "*.js" -o -name "*.ts" \) \
-not -path "*/.venv/*" \
-not -path "*/venv/*" \
-not -path "*/__pycache__/*" \
-not -path "*/.pytest_cache/*" \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" \
-not -path "*/dist/*" \
-not -path "*/build/*" \
-not -path "*/.tox/*" \
-not -path "*/.mypy_cache/*" \
-not -path "*/.ruff_cache/*" | \
while read f; do
lines=$(wc -l < "$f")
if [ $lines -gt 500 ]; then
echo "$lines $f"
fi
done | sort -rnThresholds:
- Python: > 500 lines (God class likely)
- JavaScript/TypeScript: > 400 lines
- Markdown: > 300 lines (bloated docs)
Confidence: MEDIUM (70%) - Large size suggests but doesn't confirm bloat
2. Stale Files (Lava Flow)
# Files unchanged in 6+ months
git log --since="6 months ago" --name-only --pretty=format: | \
sort -u > recent_files.txt
git ls-files | while read f; do
if ! grep -qxF "$f" recent_files.txt; then
last_modified=$(git log -1 --format="%ai" -- "$f")
echo "$last_modified $f"
fi
done | sort
rm recent_files.txtThresholds:
- > 12 months: HIGH confidence (95%)
- 6-12 months: MEDIUM confidence (75%)
- 3-6 months: LOW confidence (50%)
False Positives: Stable libraries, configuration files (check .bloat-ignore)
3. Commented Code Blocks
# Find large commented code blocks (Python)
grep -rn "^#.*def \|^#.*class \|^#.*import " --include="*.py" . | \
awk '{print $1}' | uniq -c | sort -rn
# JavaScript/TypeScript
grep -rn "^//.*function \|^//.*class \|^//.*import " --include="*.js" --include="*.ts" . | \
awk '{print $1}' | uniq -c | sort -rnConfidence: HIGH (90%) - Commented code is rarely needed
4. Old TODOs/FIXMEs
# Find TODOs with dates > 3 months old
grep -rn "TODO\|FIXME\|HACK" --include="*.py" --include="*.js" --include="*.ts" --include="*.md" . | \
grep -E "[0-9]{4}-[0-9]{2}" | \
while read line; do
# Extract date and compare (simplified - actual implementation would parse dates)
echo "$line"
doneThresholds:
- > 12 months: Remove or convert to issue
- 6-12 months: Review for relevance
- 3-6 months: Monitor
Confidence: MEDIUM (70%) - Context-dependent
5. Duplicate Patterns
# Find potential duplicate files by name similarity (excluding cache directories)
find . -type f \( -name "*.py" -o -name "*.js" -o -name "*.ts" \) \
-not -path "*/.venv/*" \
-not -path "*/venv/*" \
-not -path "*/__pycache__/*" \
-not -path "*/.pytest_cache/*" \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" | \
sed 's/.*\///' | sort | uniq -d
# Find duplicate files by content hash (excluding cache directories)
find . -type f -name "*.py" \
-not -path "*/.venv/*" \
-not -path "*/venv/*" \
-not -path "*/__pycache__/*" \
-not -path "*/.pytest_cache/*" \
-not -path "*/.git/*" \
-exec md5sum {} \; | \
sort | uniq -w32 -D | cut -d' ' -f3-Confidence: LOW (60%) - Needs manual review, may be intentional
Scoring Algorithm
def calculate_quick_scan_score(file_path, metrics):
score = 0
# Size penalty
if metrics['lines'] > 500:
score += (metrics['lines'] - 500) / 100 * 10
# Staleness penalty
months_unchanged = metrics['months_since_change']
if months_unchanged > 12:
score += 30 # High penalty
elif months_unchanged > 6:
score += 15 # Medium penalty
# Commented code penalty
commented_lines = metrics['commented_code_lines']
score += commented_lines * 0.5
# Old TODOs
old_todos = metrics['todos_older_than_6mo']
score += old_todos * 2
# Normalize to 0-100
return min(score, 100)Output Format
file: path/to/bloated_file.py
bloat_score: 85
confidence: MEDIUM
signals:
- large_file: 847 lines (threshold: 500)
- stale: 18 months unchanged
- commented_code: 23 lines
- old_todos: 3 (oldest: 14 months)
token_estimate: ~3,200 tokens
recommendations:
- action: DELETE
rationale: No recent usage, high bloat score
safety: Check for external references first
- action: ARCHIVE
rationale: Preserve history without active maintenance
location: archive/legacy/Integration with Git Analysis
Quick scan coordinates with git-history-analysis module:
- Quick scan identifies candidates
- Git analysis validates with reference counting
- Combined confidence: HIGHER than either alone
Performance
- Target: < 5 minutes for 10,000 files
- Method: Parallel grep, minimal disk I/O
- Optimization: Cache git log results, reuse across scans
False Positive Handling
Respect .bloat-ignore patterns:
# .bloat-ignore - Patterns to exclude from bloat detection
# Cache directories (should always be excluded)
.venv/
venv/
__pycache__/
.pytest_cache/
.mypy_cache/
.ruff_cache/
.tox/
.git/
# Build and distribution
dist/
build/
*.egg-info/
# Dependencies
node_modules/
vendor/
# IDE and editor
.vscode/
.idea/
# Test fixtures and templates
tests/fixtures/*
config/*.template
# Auto-generated code
generated/*
*_pb2.pyDefault Exclusions: The scan tools should automatically exclude common cache directories even without a .bloat-ignore file.
Next Steps After Quick Scan
Based on findings:
- High-confidence: Proceed with cleanup
- Medium-confidence: Run Tier 2 for validation
- Low-confidence: Manual review required
Remediation Types
Shared definitions for bloat remediation actions used by unbloat command and unbloat-remediator agent.
DELETE (Dead Code Removal)
Remove files with high confidence they're unused:
- 0 references (git grep, static analysis)
- Stale (> 6 months unchanged)
- High confidence (> 90%)
- Non-core files
Risk Assessment:
| Risk | Criteria |
|---|---|
| LOW | deprecated/, test files, archive/, 0 refs, 95%+ confidence |
| MEDIUM | 1-2 refs, 85-94% confidence |
| HIGH | >2 refs, <85% confidence, core infrastructure |
REFACTOR (Split God Classes)
Break large, low-cohesion files into focused modules:
- Large files (> 500 lines)
- Multiple responsibilities (low cohesion)
- High cyclomatic complexity
- Active usage (recent changes)
Risk Assessment:
| Risk | Criteria |
|---|---|
| LOW | Utilities, helpers, pure functions, < 3 import sites |
| MEDIUM | Services, handlers, 3-10 import sites |
| HIGH | Core modules, frameworks, > 10 import sites |
CONSOLIDATE (Merge Duplicates)
Merge duplicate or redundant content:
- Documentation with > 85% similarity
- Duplicate code patterns
- Multiple versions of same concept
Risk Assessment:
| Risk | Criteria |
|---|---|
| LOW | Docs, examples, pure duplication |
| MEDIUM | Utilities with slight variations |
| HIGH | Business logic, different contexts |
ARCHIVE (Move to Archive)
Move stale but historically valuable content:
- Old tutorials, examples
- Deprecated but referenced
- Historical documentation
Risk Assessment:
| Risk | Criteria |
|---|---|
| LOW | Examples, tutorials, < 5 refs |
| MEDIUM | Guides, how-tos, 5-10 refs |
| HIGH | Core docs, > 10 refs |
INLINE (Remove Dead Wrappers)
Replace thin facades and passthrough functions with direct usage of the underlying implementation:
- Whole-file wrappers with 0 external consumers → DELETE
- Intra-file passthrough methods that only delegate → INLINE callers to use wrapped method directly
- Re-export layers where examples/ already demonstrates the same API
Risk Assessment:
| Risk | Criteria |
|---|---|
| LOW | 0 external refs, wrapper adds no logic, underlying module is stable |
| MEDIUM | 1-2 refs, wrapper adds minor convenience (default args, error handling) |
| HIGH | >2 refs, wrapper provides meaningful abstraction or cross-cutting concerns |
Detection Signals:
- File imports internal module and re-exports similar API surface
- No
__init__.py(not a proper package) - Function bodies are single
return self.other_method(...)calls - Duplicate capability exists in
examples/orskills/directories
Auto-Approval Levels
| Level | Criteria |
|---|---|
low | Confidence >= 90%, Risk = LOW, 0 refs, deprecated/test/archive files only |
medium | Confidence >= 80%, Risk <= MEDIUM, <= 2 refs, non-core |
none | Prompts for every change (default, safest) |
Note: All levels still show preview before execution.
Static Analysis Integration Module
Bridge Tier 1 heuristics with Tier 2 programmatic analysis. Auto-detects tools and falls back gracefully.
Tool Detection
# Auto-detect available tools
TOOLS=()
command -v vulture &>/dev/null && TOOLS+=("vulture")
command -v deadcode &>/dev/null && TOOLS+=("deadcode")
command -v autoflake &>/dev/null && TOOLS+=("autoflake")
command -v knip &>/dev/null && TOOLS+=("knip")
command -v sonar-scanner &>/dev/null && TOOLS+=("sonarqube")
[ ${#TOOLS[@]} -gt 0 ] && echo "Tier 2 capable" || echo "Tier 1 only"Python Tools
| Tool | Strength | Confidence | Command |
|---|---|---|---|
| vulture | Dead code detection | 80-95% | vulture . --min-confidence 80 |
| deadcode | Fast, auto-fix | 85% | deadcode --dry |
| autoflake | Import cleanup | 95% | autoflake --check -r . |
Vulture (Recommended)
vulture . --min-confidence 80 --exclude=.venv,__pycache__,.git,node_modules- 90-100%: Safe to remove
- 80-89%: Review first
- <80%: Investigate
autoflake (Imports)
autoflake --check --remove-all-unused-imports --expand-star-imports -r .
# Fix: add --in-placeImpact: 40-70% startup time reduction
JavaScript/TypeScript
| Tool | Strength | Confidence | Command |
|---|---|---|---|
| knip | Files, exports, deps | 95% | knip --include files,exports |
knip --include files,exports,dependencies --reporter json > knip-report.jsonTree-shaking prereqs:
"type": "module"in package.json- Avoid
export * frombarrel patterns
Multi-Language
SonarQube (enterprise): Duplication, complexity, code smells
sonar-scanner -Dsonar.sources=. -Dsonar.exclusions="**/node_modules/**"Tool Selection
PRIORITY = {'python': ['vulture', 'deadcode'], 'javascript': ['knip']}
tool = next((t for t in PRIORITY.get(lang, []) if t in available), 'heuristic')Confidence Boosting
When heuristic and tool agree: boost confidence by 15% (max 95%)
# Output format
file: src/utils/helpers.py
type: function
name: calculate_legacy
confidence: 95%
sources: [heuristic, vulture]
action: DELETEGraceful Degradation
No tools? Fall back to @module:code-bloat-patterns heuristics.
Related
code-bloat-patterns- Heuristic fallbacksbloat-auditor- Orchestrates tool execution
Related skills
FAQ
Is Bloat Detector safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.