| name | maintain-claude-config |
| description | Guide for maintaining and optimizing Claude Code configuration โ CLAUDE.md, rules files, hooks, and skills. Use when auditing instruction bloat, deciding where a new rule belongs, or pruning stale content.
|
Maintaining Claude Code Configuration
Core Principle: Progressive Disclosure
Configuration that loads unconditionally on every session costs tokens every
time, whether relevant or not. The goal is to keep always-loaded content minimal
and push everything else to mechanisms that load only when needed.
| Mechanism | Loads When | Token Cost | Use For |
|---|
CLAUDE.md / rules/*.md (no frontmatter) | Every session | Always | Universally-applicable standing facts only |
rules/*.md with paths: frontmatter | Matching file opened | Conditional | Language/directory-specific conventions |
| Skills | Explicitly invoked | Description only (body on demand) | Reference content, procedures, domain knowledge |
Hooks (settings.json) | Tool event fires | None | Hard behavioral enforcement |
The 200-Line Rule
Anthropic's official guidance: keep every always-loaded file under 200 lines.
Longer files cause Claude to ignore rules โ important instructions get lost in
the noise. This is qualitative degradation, not just a capacity problem; it
begins before the context window fills.
@path imports do NOT help. @file references in CLAUDE.md still load the
imported content unconditionally at session start. Only paths: frontmatter and
skills actually reduce token spend.
Where a New Rule Belongs
Ask these questions in order:
-
Must this always run, regardless of whether Claude agrees? โ Hook
(PreToolUse to block, PostToolUse to enforce after). Rules in prose have
no compliance guarantee; hooks are deterministic.
-
Does this only apply when working in a specific language or directory? โ
rules/*.md file with paths: frontmatter. Example:
---
paths:
- "**/*.py"
- "**/pyproject.toml"
---
-
Is this reference content, a procedure, or domain knowledge? โ Skill.
Skill descriptions stay in context; the full body loads only when invoked.
-
Does this genuinely apply to every session in every project? โ Keep it in
CLAUDE.md or an unconditional rules/*.md file. Be ruthless โ if Claude
already does it correctly without the instruction, delete it.
The Iteration Loop (Boris Cherny / Anthropic official)
- Run
/init on a new project to generate a starter CLAUDE.md.
- Treat CLAUDE.md like code: review when things go wrong, prune regularly.
- Test each rule by removing it and observing โ if behavior doesn't change,
delete it.
- If Claude keeps violating a rule despite it being written, the file is
probably too long and the rule is getting lost. Move it to a hook.
- Reserve
IMPORTANT / YOU MUST emphasis for genuinely critical rules;
overuse dilutes signal.
Audit Checklist
When reviewing a rules file, apply each instruction to this filter:
Hook Patterns for Hard Behavioral Rules
Hooks live in settings.json under "hooks". They fire deterministically
regardless of what Claude decides.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "bash ~/.claude/hooks/guard-installs.sh"
}]
}
]
}
}
A blocking hook should exit with code 2 and write a JSON decision:
echo '{"decision": "block", "reason": "Install commands are not allowed. Surface the command and let the user run it."}' >&2
exit 2
Auditing Token Usage with koopa app claude audit-tokens
The command reports approximate token cost for always-loaded Claude config.
Token estimate: len(text) // 4 (chars, not bytes).
Flags:
--scope {all,global,project} โ default all; scan global ~/.claude/, the
current project's .claude/, or both. Project root is auto-discovered by walking
up from CWD looking for a .claude/ subdir or .git/.
--project-dir PATH โ explicit project root, skips CWD discovery.
--max-tokens N โ exit 1 if combined always-loaded tokens exceed N.
What "always-loaded" means: CLAUDE.md + all rules/**/*.md files without
paths: frontmatter. Path-scoped files are reported separately but excluded from
the gated total.
The combined all output is the true per-session token cost before the first prompt.
Use --scope global to see only the global ~/.claude/ tree (pre-2026-07 behavior).
Slimming a Bloated lessons.md (migration pattern)
When a project lessons.md exceeds ~200 lines, apply this triage to each lesson:
- Subsystem gotcha / how-to reference โ fold the full content verbatim into
the matching skill under a
## Lessons (Migrated from rules/lessons.md) section.
Replace the lessons.md entry with a 1-line pointer:
- **Title** โ see \skill-name` skill.`
- Universal behavioral rule (short, fires without a specific file open) โ
keep in lessons.md, trimmed to 1โ2 sentences.
- Never delete institutional knowledge โ only move it to the skill that owns the
subsystem. If no matching skill exists, keep it (trimmed) in lessons.md.
This pattern routinely achieves 70โ80% token reduction on a bloated lessons.md
while preserving all knowledge in skill files that load on-demand.
Priority Moves for This Config
Token targets:
- Global always-loaded: ~2,161 tokens (stable)
- Any project
lessons.md: โค200 lines / โค3,000 tokens
- Combined (global + project): target โค8,000 tokens
Completed moves
-
Hook: never-install enforcement โ ~/.claude/hooks/guard-installs.sh. Done.
-
Path-scope python.md โ paths: ["**/*.py", "**/pyproject.toml"]. Done.
-
Path-scope environment-specific rules โ GHA, IaC, cloud-platform rules
path-scoped to their relevant file patterns. Done.
-
Migrate bulky lessons.md entries to skills โ apply the triage pattern above
whenever a project lessons.md exceeds 200 lines.
Remaining items
-
Migrate workflow.md (107 lines) to a skill โ entirely procedure/reference,
never a hard behavioral constraint. Keep as a ~10-line stub + skill pointer.
-
Audit and prune coding.md, thinking.md, security.md โ apply the test:
"would removing this instruction change Claude's behavior?" Delete where no.
Bottom line
- Hooks โ behavioral enforcement (never-install, git denies, env-file guard)
- Skills โ procedures, reference, domain knowledge
paths:-scoped rules โ language/framework/project-type conventions
- Unconditional rules โ short, universal, things Claude would get wrong without them
Cross-tree Ownership (chezmoiignore Pattern)
This config spans two chezmoi trees. The public koopa tree cedes ownership of
settings.json (and .npmrc, pip.conf) to the work tree when the generic
~/.config/koopa/dotfiles-work symlink exists:
{{- if stat (joinPath .chezmoi.homeDir ".config" "koopa" "dotfiles-work") }}
.claude/settings.json
.config/pip/pip.conf
.npmrc
{{- end }}
Rules for extending this:
- Detection key is always
dotfiles-work (generic symlink name) โ never the actual
private repo name. This keeps the public repo free of private identifiers.
- General scripts (like
guard-installs.sh) live in koopa and are deployed everywhere;
both settings files merely reference them by path.
- Work-specific rules, hooks, and settings stay in the work tree. Nothing work-specific
ever enters koopa.
See also
claude-permissions โ protected paths, permission modes, allow/ask/deny
precedence, PreToolUse permissionDecision contract, and the carve-out hook
for .claude/ writes. Use when debugging unexpected permission prompts.
What Belongs in This Project's Setup
This user's configuration lives in chezmoi at:
~/.local/share/koopa/opt/dotfiles/chezmoi/dot_claude/
(or equivalently ~/.config/koopa/dotfiles/chezmoi/dot_claude/).
The deployed targets are ~/.claude/. Always edit the chezmoi source, not
the deployed copy โ it will be overwritten on the next chezmoi apply.
After editing, deploy with a targeted apply:
chezmoi apply \
--source=~/.local/share/koopa/opt/dotfiles/chezmoi \
~/.claude/rules/lessons.md
Do NOT run koopa configure user dotfiles from inside a long-running agent
session โ the session's KOOPA_COLOR_MODE may be stale and will clobber theme
files.