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

> Source: <https://dev.to/bokuwalily/your-claude-code-setup-gets-bloated-if-you-ignore-it-weekly-auto-slimming-by-watching-injected-3jki>
> Published: 2026-07-22 05:00:07+00:00

This is the next installment in my "Claude Code environment" series. Combined with the [idempotent marker implementation](https://zenn.dev/bokuwalily/articles/substep-idempotent-marker) 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.

```
# ~/.claude/scripts/cc-self-audit.sh（collect関数より）

# --- 静的 ---
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 ' ')

# --- 動的（前回実行以降のtranscriptのみ・最大200ファイル）---
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.

``` php
<!-- ~/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を呼ばない）
~/.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 slow`head -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 uses`set -uo pipefail`

, each individual measurement swallows errors with`2>/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](https://bokuwalily.com) · [X](https://x.com/bokuwalily) · [GitHub](https://github.com/bokuwalily)*
