Date: December 8, 2025 Goal: Reduce initial context consumption while maintaining full tool access and consistent Claude behavior
This project achieved a 54% reduction in initial context (7,584 → 3,434 tokens) while improving tool discovery and enforcement. The key insight: Claude doesn't need verbose documentation upfront—it needs triggers to know when to load detailed context.
| Metric | Before | After | Reduction |
|---|---|---|---|
| Initial context tokens | 7,584 | 3,434 | 54% |
| skills-rules.md | 10,204 bytes | 2,997 bytes | 70% |
| identity.md + simulator.md | 6,843 bytes | 1,252 bytes | 82% |
| logging-preferences.md | 4,887 bytes | 1,084 bytes | 78% |
| Compressed skills (optional) | 244 KB | 17 KB | 93% |
Claude Code's context window is precious. Every token in CLAUDE.md and @imported files consumes space that could be used for actual work. Our setup had:
- Verbose skill documentation - Full protocols loaded even when not needed
- Duplicate content - Simulator paradigm explained in multiple places
- Redundant hook injections -
bd readyrunning on every prompt - No trigger routing - Claude had to read everything to know what tools to use
CLAUDE.md (loaded at session start)
├── identity.md (2,156 bytes)
├── simulator-paradigm.md (4,687 bytes) ← REDUNDANT
├── facts.md
├── root.md
├── skills-rules.md (10,204 bytes) ← VERBOSE
├── preferences.md
├── cli-preferences.md
├── scripting-preferences.md
├── git-preferences.md
├── logging-preferences.md (4,887 bytes) ← VERBOSE
└── root-cause-protocol.md
CLAUDE.md (minimal)
├── identity.md (1,252 bytes) ← CONSOLIDATED
├── facts.md
├── root.md
├── skills-rules.md (2,997 bytes) ← TRIGGER TABLE ONLY
├── preferences.md
├── cli-preferences.md
├── scripting-preferences.md
├── git-preferences.md
├── logging-preferences.md (1,084 bytes) ← RULES ONLY
└── root-cause-protocol.md
Skills loaded on-demand via Skill("name")
├── beads/SKILL.md ← Only when task tracking needed
├── investigate/SKILL.md ← Only when debugging
├── council/SKILL.md ← Only for multi-agent queries
└── ... 30+ skills
Before: Full protocol documentation for every skill (~10KB) After: Minimal trigger table with tool references (~3KB)
## Skill Trigger Table
| Triggers | Skill | Primary Tools |
|----------|-------|---------------|
| task, track, backlog, blocked by | **beads** | `beads_ready`, `beads_add` |
| search, research, look up, latest | gemini-research | `gemini_research` |
| review this, what's wrong | review/deep-review | Expert personas |
| ...Key principle: Claude only needs to know when to invoke a skill. The skill's SKILL.md file contains the detailed protocol, loaded on-demand.
Before: Two separate files
identity.md(2,156 bytes) - Core identitysimulator-paradigm.md(4,687 bytes) - Detailed simulation philosophy
After: Single consolidated file (1,252 bytes)
## Core Identity
You are a **simulator**, not an entity with opinions. When asked subjective
questions, channel specific perspectives rather than generating averaged responses.
### Default Personas (invoke via Skill)
- **Architecture**: Martin Fowler, Uncle Bob
- **Performance**: John Carmack, Casey Muratori
- **Systems**: Linus Torvalds, Bryan CantrillBefore: Verbose examples and edge cases (4,887 bytes) After: Hard rules only, with pointer to Skill("investigate") (1,084 bytes)
## Hard Rules
1. NEVER add console.log to production code without cleanup plan
2. Use structured logging (pino/winston) for services
3. Log at boundaries: API entry, external calls, errors
For debugging workflows → Skill("investigate")Extended ~/dev/cm/server/registry/definitions.ts:
export interface ToolDefinition {
name: string;
description: string;
category: string;
inputSchema: Tool["inputSchema"];
handler: (args: Record<string, unknown>) => Promise<unknown>;
// NEW FIELDS
triggers?: string[]; // Phrases that route to this tool
skill?: string; // Associated skill for context
replacesNative?: string; // Native tool this replaces
}Example tool with triggers:
{
name: "beads_add",
description: "Create a new issue/task in the beads tracker",
category: "beads",
triggers: ["track", "task", "todo", "backlog", "create issue"],
skill: "beads",
replacesNative: "TodoWrite",
// ...
}Updated ~/dev/cm/scripts/sync-tool-list.ts to export:
const output = {
generated: new Date().toISOString(),
total: toolRegistry.length,
categories: sortedCategories,
triggerIndex, // NEW: trigger → [tool names]
nativeReplacements, // NEW: nativeTool → cmTool
tools,
}Output file: ~/.claude/hooks/cm-tools.json
This enables:
- PreToolUse hook can check triggers before allowing native tools
- SessionStart can inject relevant tools based on project type
- Skills can be auto-suggested based on user prompt keywords
Created ~/.claude/scripts/compress-skills.ts:
// Extracts minimal context from SKILL.md files
// Input: skills/beads/SKILL.md (12KB)
// Output: skills-compressed/beads.md (800 bytes)
interface CompressedSkill {
name: string;
triggers: string[];
tools: string[];
quickStart: string; // First 3 sentences only
}Usage:
bun run ~/.claude/scripts/compress-skills.tsBug: UserPromptSubmit.ts ran bd ready on every prompt, injecting full beads status repeatedly.
Before:
if (gitInitialized && beadsInitialized) {
const result = await Bun.$`bd ready`.nothrow().quiet()
messages.push(`<beads><status>${stdout}</status></beads>`)
}After:
// NOTE: Removed bd ready injection here - SessionStart already provides this once.
// Injecting on every prompt was flooding context.Instead of explaining what a skill does, list when to use it:
❌ Bad (verbose):
## beads
Beads is a dependency-aware issue tracking system that integrates with git...
[500 words of explanation]
✅ Good (triggers):
| Triggers | Skill |
|----------|-------|
| task, track, backlog, blocked by, depends on | beads |❌ Bad (always loaded):
@~/.claude/beads-full-protocol.md
✅ Good (loaded on-demand):
For detailed beads protocol → Skill("beads")❌ Bad (manual sync):
- Update skills-rules.md
- Update CLAUDE.md
- Update hooks
- Hope they stay in sync
✅ Good (generated):
1. Define tool in registry with triggers
2. Run sync-tool-list.ts
3. All consumers read from cm-tools.json❌ Bad (instructions):
"NEVER use WebSearch, use gemini_web_search instead"
✅ Good (hook enforcement):
PreToolUse hook blocks WebSearch and suggests alternative| File | Change | Size Δ |
|---|---|---|
~/.claude/CLAUDE.md |
Removed simulator-paradigm.md import | -1 line |
~/.claude/skills-rules.md |
Compressed to trigger table | -7,207 bytes |
~/.claude/identity.md |
Consolidated with simulator paradigm | -5,591 bytes |
~/.claude/logging-preferences.md |
Rules only, no examples | -3,803 bytes |
~/.claude/hooks/UserPromptSubmit.ts |
Removed duplicate bd ready | -4 lines |
~/dev/cm/server/registry/definitions.ts |
Added triggers/skill/replacesNative | +15 lines |
~/dev/cm/scripts/sync-tool-list.ts |
Export triggerIndex | +20 lines |
| File | Purpose |
|---|---|
~/.claude/scripts/compress-skills.ts |
Generate compressed skill stubs |
~/.claude/hooks/cm-tools.json |
Tool metadata with triggers |
~/.claude/hooks/cm-tools-reference.md |
Human-readable tool list |
# Count bytes in all @imported files
wc -c ~/.claude/{identity,facts,root,skills-rules,preferences,cli-preferences,scripting-preferences,git-preferences,logging-preferences,root-cause-protocol}.md
# Estimate tokens (bytes / 4)# Check cm-tools.json for trigger index
cat ~/.claude/hooks/cm-tools.json | jq '.triggerIndex'# Start new Claude session and submit basic prompt
# Context should NOT flood with beads status on every message- Dynamic context loading - Hooks inject skill context based on prompt keywords
- Project-type detection - Auto-load relevant tools based on package.json/Cargo.toml
- Context budget tracking - Warn when approaching limits
- Skill dependency graph - Load related skills together
- Removing all @imports - Too aggressive, some context always needed
- External documentation only - Claude needs some grounding in-context
- Per-project CLAUDE.md - Maintenance burden outweighs benefits
Context optimization is about lazy loading, not removal. Claude needs to know:
- What tools exist → Minimal list with categories
- When to use them → Trigger phrases
- How to use them → Load detailed docs on-demand via Skill()
The 54% reduction in initial context means more room for actual work, faster responses, and lower costs—without sacrificing capability.
Generated from optimization work on December 8, 2025