cd /news/developer-tools/your-claude-code-setup-gets-bloated-… · home topics developer-tools article
[ARTICLE · art-68082] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Your Claude Code Setup Gets Bloated If You Ignore It — Weekly Auto-Slimming by Watching Injected Bytes, Agent Count, and Frustration Words

A developer created a self-audit script for Claude Code environments that automatically detects and fixes 'environment decay' — the gradual bloat of rules, agents, and hooks that degrades performance. The script, cc-self-audit.sh, measures five metrics weekly including injected bytes, agent count, and frustration words in transcripts, then uses 'claude -p' to remediate any threshold breaches and re-measures to confirm improvement.

read6 min views1 publishedJul 22, 2026

This is the next installment in my "Claude Code environment" series. Combined with the idempotent marker implementation from the previous article, I've come to realize that more than half of the reasons an autonomous loop won't stop come down to one thing: environment decay.

A Claude Code setup gets fat if you leave it alone. The rules balloon, agents you thought you'd retired come back to life, and hooks keep misfiring. The only thing you actually notice is a vague "this feels sluggish" or "it keeps making the same mistake," and then you burn time hunting down the cause. This article is about a system that measures that decay every week, lets claude -p fix it, and then confirms the improvement with an independent re-measurement after the fix.

This is a real incident I hit during a performance audit on 2026-07-11.

I'd tidied up ~/.claude/agents/

several times. I'd manually moved agent definitions I no longer used into agents-archive/

and figured I was done. But when I actually measured, agents_loaded

was still stuck at 99. The culprit was dot-directories (.deprecated/

, .backup/

, and the like). find

's default behavior recurses into dot-directories too. They looked deleted, but every single one was still being loaded through the hidden directories.

This real incident is what prompted me to write cc-self-audit.sh

.

The script's collect()

function grabs five metrics every run.


inject_bytes=$(( \
  $(find "$HOME/.claude/rules" -name '*.md' -print0 2>/dev/null | xargs -0 cat 2>/dev/null | wc -c) + \
  $(cat "$HOME/.claude/CLAUDE.md" 2>/dev/null | wc -c) + \
  $(cat "$HOME/CLAUDE.md" 2>/dev/null | wc -c) + \
  $(cat "$HOME/.claude/projects/-Users-matsubara/memory/MEMORY.md" 2>/dev/null | wc -c) ))
agents_loaded=$(find "$HOME/.claude/agents" -name '*.md' 2>/dev/null | wc -l | tr -d ' ')

stopspam=$(echo "$files" | xargs /usr/bin/grep -h -c 'セルフ監査未実施。実装' 2>/dev/null | awk '{s+=$1} END{print s+0}')
frustration=$(echo "$files" | xargs /usr/bin/grep -h -c -E '何回も言|いい加減にし|嘘つ|舐めんな|なんで治らん|最悪やろ|頭悪い' 2>/dev/null | awk '{s+=$1} END{print s+0}')
toolerr=$(echo "$files" | xargs /usr/bin/grep -h -o -c '"is_error":true' 2>/dev/null | awk '{s+=$1} END{print s+0}')
Metric Meaning Threshold
inject_bytes
Total bytes of rules + CLAUDE.md + MEMORY.md 40,000
agents_loaded
Total .md count under ~/.claude/agents/ (recursion including dot-dirs)
60
stopspam
Number of times the audit hook fired / week 15
frustration
Occurrences of frustration words in transcripts / week 8
toolerr
Count of "is_error":true in transcripts / week
400

The two static metrics are proxy variables for injection cost, and the three dynamic metrics are proxy variables for experience quality. The frustration-word detection may look out of place, but if I'm scolding it repeatedly, that means it's repeating the same failure — and those failures often stem from environment problems (misfiring hooks, rotted memory, broken tools).

The thresholds can be overridden with env variables.

TH_INJECT_BYTES="${SELF_AUDIT_TH_INJECT:-40000}"
TH_AGENTS="${SELF_AUDIT_TH_AGENTS:-60}"
TH_STOPSPAM="${SELF_AUDIT_TH_STOPSPAM:-15}"
TH_FRUSTRATION="${SELF_AUDIT_TH_FRUST:-8}"
TH_TOOLERR="${SELF_AUDIT_TH_TOOLERR:-400}"

claude -p

fixes itself If every metric is under its threshold, it just posts a one-line ✅ GREEN

to Discord and exits. If any is over, it calls claude -p

to fix it.

BREACH=$(echo "$METRICS" | breaches)

if [ -z "$BREACH" ]; then
  log "GREEN"
  touch "$LASTRUN"
  notify "✅ CC自己監査: 正常 ($METRICS)"
  exit 0
fi

log "RED: $BREACH"
if [ "$DRY" = "1" ]; then log "DRY=1: 修正スキップ"; touch "$LASTRUN"; exit 0; fi

The prompt passed to claude -p

has a remediation policy embedded for each decay pattern.

PROMPT="...
## 指示
1. ~/.claude 内だけを調査・修正する(プロジェクトコード・secret・plist削除は禁止)
2. 既知の劣化パターンと正典:
   - agents_loaded超過 → ~/.claude/agents/ 配下の退避漏れ(ドットdirも読み込まれる)を ~/.claude/agents-archive/ へ移動
   - inject_bytes超過 → 肥大したrules/MEMORY.mdを圧縮(フル版は ~/.claude/rules-archive/ へ。リンクは全維持し索引整合を検証)
   - stopspam超過 → ~/.claude/hooks/self_audit_stop.sh の抑制ロジックを点検
   - frustration/toolerr超過 → 該当transcriptをgrepして繰り返し失敗の真因を特定し、hook/スキル/メモリで再発防止を仕込む
3. 変更は1件ずつ ${CHANGELOG} に「日時/対象/理由/戻し方」を追記
4. 修正後に必ず 'bash ~/.claude/scripts/cc-self-audit.sh --collect-only' を実行し改善を数値で確認
..."

I restrict it to filesystem operations only with --allowedTools "Read,Write,Edit,Bash,Grep,Glob"

, and cap it at max-turns 50 and a 20-minute wall clock (FIX_TIMEOUT=1200

).

Note

Having claude run--collect-only

itself inside the fix loop is strictly "a confirmation as part of the remediation procedure." The final verdict of the audit is made by the outer, independent re-measurement. The key to the design is not taking the inner confirmation at face value.

Even when claude -p

says "fixed it," I don't believe it right away. The outer harness calls collect() again to re-measure the numbers.

AFTER=$(collect)
log "after: $AFTER"
echo "$AFTER" >> "$HISTORY"
STILL=$(echo "$AFTER" | breaches)
touch "$LASTRUN"

if [ -z "$STILL" ]; then
  notify "🔧 CC自己監査: 劣化検知→自己修正済み。前:[$BREACH] 後:全緑。詳細=$CHANGELOG"
else
  notify "🚨 CC自己監査: 自己修正後も残存 [$STILL]。要確認: $LOG / $CHANGELOG"
fi

If a violation still remains after the fix, it flies off to Discord with a 🚨

and hands it over to a human. Trust only the OS's measured values, not self-reporting — that's the core of fail-closed.

All measured values are stacked one record per line in ~/.claude/self-audit/history.jsonl

, and the prompt handed to claude also includes the most recent 5 entries as a trend. By looking at the magnitude of change — "it was 30KB last week but 51KB this week" — I can distinguish a one-off outlier from a continuous increase.

<!-- ~/Library/LaunchAgents/com.lily.cc-self-audit.plist -->
<key>StartCalendarInterval</key><dict>
  <key>Weekday</key><integer>0</integer>
  <key>Hour</key><integer>8</integer>
  <key>Minute</key><integer>30</integer>
</dict>
<key>RunAtLoad</key><false/>

Weekday=0 is Sunday. I set RunAtLoad

to false to prevent it from running immediately right after registering with launchd. I include ~/.local/bin

in the environment variable PATH

and place a claude

shim there.

There are two modes for manual runs.

~/.claude/scripts/cc-self-audit.sh --collect-only

SELF_AUDIT_DRY=1 ~/.claude/scripts/cc-self-audit.sh

Before putting it into production, I always eyeballed the metrics with DRY=1

first, then enabled it.

find

~/.claude/agents/.deprecated/

and the like became recursion targets, so agents I thought I'd retired kept getting counted. The root cause of the real incident.find

is slowhead -200

and exclude empty files with -size +100k

, the first run of the weekend takes over 3 minutes.LANG=en_US.UTF-8

at the top, grep -E

misbehaves on multibyte characters.claude -p

isn't on the PATH~/.local/bin

. Solved by explicitly adding it to the plist's EnvironmentVariables.collect()

call is mandatory. The inner confirmation stays limited to "part of the remediation procedure."

Warning

As the comment says —bash 3.2 compatible, fail-open

— the audit script itself is designed so that nothing breaks even if it crashes. It usesset -uo pipefail

, each individual measurement swallows errors with2>/dev/null || true

, and in the worst case the measured value becomes 0 and gets treated as "GREEN." Having the main system die because of an audit failure would be putting the cart before the horse.

claude -p

fixes only what's inside ~/.claude

, and records it in the change log (self-audit-changes.log

) with a "how to revert" note--collect-only

and DRY=1

Next time, I'll write about how I trimmed down the inject_bytes overage that this audit loop detected — a pipeline that compresses the rules and auto-slims them every week.

*Written by Lily — I ship iOS apps and automate my content stack with Claude Code.

Follow along: Portfolio · X · GitHub*

── more in #developer-tools 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/your-claude-code-set…] indexed:0 read:6min 2026-07-22 ·