Skip to content

Repository files navigation

Claude Code Barber

💈

"Just take a little off the top." — Claude Code, probably

CI Rust 2021 MIT License

Your AI's context, styled.

ccb is a composable token management layer for Claude Code. It compresses noisy command output, lazy-loads skills on demand, monitors your context window, and logs token savings — built as a single Rust binary with optional feature flags so you only ship what you need.


Table of Contents


About

Claude Code dumps a lot of noise into its context window — Compiling lines, npm warnings, pytest headers, git hints. Every token of noise is a token not spent on reasoning.

ccb sits between your shell and Claude Code. It filters, compresses, and monitors so the model sees signal instead of static.

Built With

  • Rust 2021 — single binary, no runtime
  • clap — CLI
  • rusqlite — code graph and knowledge graph (optional features)
  • tree-sitter — AST-based symbol extraction (Rust, Python, TS, JS)
  • SQLite — code graph, knowledge graph, and infra metadata (via rusqlite)

Getting Started

Prerequisites

Installation

git clone https://github.com/thrtysxty/claude-code-barber
cd claude-code-barber
cargo build --release
cp target/release/ccb target/release/ccb-route ~/.local/bin/

# macOS only: ad-hoc codesign required for Cargo-compiled binaries
codesign --sign - ~/.local/bin/ccb ~/.local/bin/ccb-route

# Wire hooks into Claude Code (context monitor + skill loader + expert)
ccb install

Build options

# Default — trim + fade + route enabled
cargo build --release

# All features
cargo build --release --features full

# Without model router
cargo build --release --no-default-features --features trim,fade

Commands

Command What it does
ccb trim <cmd> Compress noisy output (git, pytest, tsc) before it hits the context window
ccb fade [skill] Lazy-load a skill from INDEX.md — or list the index
ccb context show Display current context window usage with progress bar
ccb context clear [threshold] Warn when context exceeds threshold (default 80%)
ccb context compact [threshold] Warn when context exceeds threshold (default 60%)
ccb lineup Show what is loaded in the context budget with token estimates
ccb cut Run context check + lineup in one shot
ccb buzz Nuclear: strip INDEX.md overhead, prune log to last 500 events
ccb gain Token savings analytics from ~/.claude/ccb_log.jsonl
ccb style index-build Scan ~/.claude/skills/ and regenerate INDEX.md
ccb style show Print current config (~/.claude/ccb.toml)
ccb install Wire context monitor + skill loader hooks into ~/.claude/settings.json
ccb install --auto Same, no interactive prompt
ccb install --dry-run Show what would be installed without writing anything
ccb-route Start the model router on :9001 — routes to all configured providers
ccb route start [port] Start the router as a background process
ccb route stop Stop the running router
ccb route status Show router status, PID, and route table
ccb route tiers Show tier routing table (opus/sonnet/haiku → provider mappings)
ccb models List all available models from the running router
ccb expert build <name> --dataset <file> Build a knowledge graph from a YAML dataset
ccb expert activate <name> Set the active expert persona
ccb expert list List registered experts and active status
ccb expert walk "<task>" Traverse graph for matched knowledge nodes
ccb graph index [path] Index a directory into the code symbol graph
ccb graph search <pattern> Search symbols by name
ccb graph show <file> Show all symbols in a file
ccb graph stats Print aggregate symbol counts by language
ccb status Render the statusline (reads session JSON from stdin)
ccb status demo [scenario] Render with mock data — no live session needed
ccb status mon [directory] Multi-session monitor TUI (watches ~/.claude/)

Usage

Compress command output

Pipe any command through ccb trim before its output reaches Claude's context:

ccb trim git status
ccb trim npm test
ccb trim cargo build

Strips boilerplate lines (hints, "Compiling…", "Finished"), deduplicates consecutive identical lines, and logs before/after token counts to ~/.claude/ccb_log.jsonl.

Real compression examples

cargo build with a type error — 50% reduction

# Before (90 tokens)
   Compiling serde v1.0.197
   Compiling serde_derive v1.0.197
   Compiling anyhow v1.0.86
   Compiling ccb v0.1.0 (/home/user/ccb)
error[E0308]: mismatched types
 --> src/main.rs:42:18
  |
42|     let x: u32 = "hello";
  |            ---   ^^^^^^^ expected `u32`, found `&str`
error: aborting due to 1 previous error
   Finished dev [unoptimized + debuginfo] target(s) in 3.14s

# After (45 tokens)
error[E0308]: mismatched types
 --> src/main.rs:42:18
  |
42|     let x: u32 = "hello";
  |            ---   ^^^^^^^ expected `u32`, found `&str`
error: aborting due to 1 previous error

npm install clean — 94% reduction

# Before (92 tokens)
npm warn deprecated inflight@1.0.6: This module is not supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
added 312 packages, audited 313 packages in 8s
3 packages are looking for funding
  run `npm fund` for details
found 0 vulnerabilities

# After (6 tokens)
found 0 vulnerabilities

pytest with failures — 54% reduction

# Before (122 tokens)
============================= test session starts ==============================
platform darwin -- Python 3.11.8, pytest-8.1.1, pluggy-1.4.0
rootdir: /Users/user/project
configfile: pyproject.toml
plugins: anyio-4.3.0, cov-5.0.0
collecting ...
collected 47 items

FAILED tests/test_api.py::test_create_story - AssertionError: 404
FAILED tests/test_api.py::test_update_story - AssertionError: 500

============================== 2 failed, 45 passed in 1.23s ==============================

# After (56 tokens)
FAILED tests/test_api.py::test_create_story - AssertionError: 404
FAILED tests/test_api.py::test_update_story - AssertionError: 500

============================== 2 failed, 45 passed in 1.23s ==============================
command tokens before tokens after saved reduction
cargo build (type error) 90 45 45 50%
npm install (clean) 92 6 86 94%
pytest (2 failures) 122 56 66 54%

Lazy-load skills

Instead of injecting all skill files into every session:

# List available skills (reads INDEX.md)
ccb fade

# Load a specific skill on demand
ccb fade read-then-write
ccb fade hookify

Pair with the PreToolUse hook (below) so skills load automatically when invoked.

Monitor context window

ccb context show
# context: 73% [██████████████░░░░░░] 🟡

ccb context clear 80
# ⚠️  ccb context: 85% used (threshold 80%) — consider /clear

ccb context compact 60
# ⚠️  ccb context: 73% used (threshold 60%) — consider /compact

Reads CCB_CONTEXT_PCT env var when wired as a hook, or CCB_CTX_TOKENS / CCB_CTX_MAX.

Budget inspector

CCB_CONTEXT_PCT=62 ccb lineup

window: [██████░░░░] 62%

resource tokens path
INDEX (32 skills) 820 ~/.claude/skills/INDEX
CLAUDE.md 240 ~/.claude/CLAUDE.md
rules (5 files) 1,480 ~/.claude/rules/
ESTIMATED TOTAL 2,540

Token savings

ccb gain
feature tokens in tokens out saved %
trim 18,420 3,210 15,210 82%
buzz 640 88 552 86%
TOTAL 19,060 3,298 15,762 82%

47 operations logged


Hook Integration

Wire ccb into Claude Code via ~/.claude/settings.json. A reference config is in config/hooks.json.

Lazy skill loading (PreToolUse)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Skill",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/skill_loader.sh" }
        ]
      }
    ]
  }
}

Copy hooks/skill_loader.sh to ~/.claude/hooks/skill_loader.sh. The hook reads the skill name from TOOL_INPUT, calls ccb fade <name>, and returns the SKILL.md content as feedback — skills load on demand instead of being pre-injected.

Context monitoring (PostToolUse)

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/context_monitor.sh" }
        ]
      }
    ]
  }
}

Copy hooks/context_monitor.sh to ~/.claude/hooks/. Warns after every tool call when compact (70%) or clear (85%) thresholds are breached. Thresholds are env-configurable: CCB_COMPACT_THRESHOLD, CCB_CLEAR_THRESHOLD.

Build the skills index

ccb style index-build
# INDEX.md written to /Users/you/.claude/skills/INDEX.md

Re-run whenever you add or update a skill.


Optional Features

cargo build --release --features graph     # code symbol graph (SQLite + tree-sitter)
cargo build --release --features expert    # unified knowledge graph (Layer 3)
cargo build --release --features classify  # two-tier safety classifier hook
cargo build --release --features route     # model router proxy binary
cargo build --release --features full      # everything

Code Graph

Builds a SQLite-backed symbol index across Rust, Python, TypeScript, and JavaScript files. When paired with the classify hook, automatically injects symbol maps on stderr for every Read tool call — giving the LLM a table of contents with line numbers so it can use targeted offset/limit reads instead of loading entire files.

The graph also stores infrastructure metadata — the domains and patterns tables hold operational knowledge (e.g., GitHub account routing, deployment configs) that agents can query at tool time.

ccb graph index ./src          # index a directory (default: .)
ccb graph search "compress"    # find symbols by name
ccb graph show src/main.rs     # show all symbols in a file
ccb graph stats                # aggregate counts by language

Graph-aware Read context (automatic via classify hook):

[ccb:graph] 28 symbols in src/features/classify.rs
  const `TRANSCRIPT_CHAR_LIMIT` line 16
  enum `Decision` line 21
  fn `tier1_classify` line 112
  fn `tier2_classify` line 251
  fn `run` line 658

Classify

Two-tier safety classifier for Claude Code PreToolUse hooks. Tier 1 is instant local pattern matching (no API call). Tier 2 sends ambiguous actions to an LLM for evaluation (via OpenRouter). Integrates expert context and graph-aware Read hints.

# Wire as a PreToolUse hook in ~/.claude/settings.json
ccb classify   # reads hook JSON from stdin, exits silently to allow, prints deny JSON to block

Expert Personas & Knowledge Graph

Define domain experts in a YAML dataset — security rules, coding patterns, architecture principles, or any knowledge you want surfaced at tool time. Experts activate on demand and inject context without pre-loading files.

Quick start

# 1. Build with the expert feature
cargo build --release --features expert
cp target/release/ccb ~/.local/bin/

# 2. Ingest a dataset (bundled sentinel dataset or your own)
ccb expert ingest --dataset datasets/sentinel.yaml

# 3. Activate a persona
ccb expert activate sentinel

# 4. Traverse the graph — see what it surfaces for a given task
ccb expert walk "validate user input before SQL query"

# 5. Wire to Claude Code (done automatically by ccb install)
ccb install --features expert

Commands

ccb expert ingest --dataset <file.yaml>   # load a YAML dataset into the graph
ccb expert build <name> --dataset <file>  # same as ingest, names the expert explicitly
ccb expert list                           # list registered experts + active status
ccb expert activate <name>                # set active persona (persists across sessions)
ccb expert deactivate                     # clear active persona
ccb expert walk "<task description>"      # traverse graph, print matched nodes
ccb expert query [--tool <name>]          # hook-facing: emit context for active persona

Dataset format

A dataset is a YAML file with one or more personas. Each persona has domains, and each domain has patterns — the atomic knowledge nodes.

personas:
  - name: my-expert              # used in: ccb expert activate my-expert
    description: One line — what this expert knows about
    domains:
      - name: auth               # logical grouping
        category: security       # free-form tag (security | architecture | style | ...)
        patterns:
          - id: AUTH-001         # unique ID — any string
            name: Session token storage
            mitigations:
              - Store session tokens in httpOnly cookies, never localStorage
              - Rotate tokens on privilege escalation
              - Set Secure + SameSite=Strict on all auth cookies
          - id: AUTH-002
            name: Password hashing
            mitigations:
              - Use bcrypt (cost ≥12), Argon2id, or scrypt — never MD5/SHA1
              - Hash on the server; never accept pre-hashed passwords from clients
      - name: input-validation
        category: security
        patterns:
          - id: VALID-001
            name: SQL injection prevention
            mitigations:
              - Use parameterised queries — never string concatenation
              - Apply allowlist validation on all user inputs

Required fields per pattern: id, name, mitigations (array of strings). category and domain name are free-form — use whatever taxonomy makes sense for your knowledge.

Bundled dataset: sentinel

datasets/sentinel.yaml ships with the repo — a security expert covering OWASP Top 10 patterns (SQLi, XSS, path traversal, SSRF, and more):

ccb expert ingest --dataset datasets/sentinel.yaml
ccb expert activate sentinel
ccb expert walk "user uploads a file to the server"
# → surfaces: path traversal, file type validation, upload size limits

Writing your own persona

Good expert datasets are narrow and opinionated:

Good Avoid
8–15 patterns per domain 100-pattern dumps
Concrete mitigations ("use X", "never Y") Abstract advice ("be careful")
Specific to your stack Generic best-practices lists

A 40-pattern dataset with precise mitigations beats a 400-pattern dataset with vague ones. The traversal surfaces the top matches — depth beats breadth.

See docs/ARCHITECTURE.md for the graph traversal design.

Model Router

Routes Claude Code API calls across multiple backends through a single endpoint. One claude command, every model from every provider available in the /model picker. Binary: ccb-route, default feature.

cargo build --release --features route
cp target/release/ccb-route ~/.local/bin/
ccb-route
# listens on :9001 by default

How it works

The router sits between Claude Code and your backends. Claude Code thinks it's talking to Anthropic. The router intercepts every request, resolves the model to the correct provider, and forwards it — rewriting auth headers, endpoints, and model IDs as needed.

Claude Code ──► localhost:9001 (ccb-route) ──┬──► api.anthropic.com  (Claude)
                                              ├──► localhost:11434    (Ollama)
                                              ├──► api.minimax.io    (MiniMax)
                                              └──► aibox:8080        (local GPU)

All models share the same Claude Code session — same tools, same context, same hooks, same permissions. Switch models mid-conversation with /model and the new model picks up the full transcript.

Gateway discovery

Claude Code's gateway model discovery fetches /v1/models from the router at startup. The router aggregates models from all providers into one list. Every model appears in the /model picker alongside the built-in Claude models.

The claude-prefix trick: Claude Code filters gateway models by /^(claude|anthropic)/i. The router prefixes all model IDs with claude- (e.g., claude-glm-5.1, claude-deepseek-v4-flash). When a request comes in, the router strips the prefix before forwarding to the backend.

Auto-discovery: Providers with discover = true (like Ollama) auto-populate from the backend. Pull a new model in Ollama and it appears in the /model picker on the next claude launch — no config changes needed.

Shell setup

Add to ~/.zshenv (not .zprofile or .zshrc.zshenv is the only file sourced for all shell types including non-login interactive shells, which is how terminals and VS Code launch):

# Source secrets first (API keys for MiniMax, etc.)
[ -f ~/.secrets ] && source ~/.secrets

# CCB router — all providers through one endpoint
export ANTHROPIC_BASE_URL=http://localhost:9001
unset ANTHROPIC_API_KEY
export ANTHROPIC_AUTH_TOKEN=$(your-oauth-token-helper)
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1

Key points:

  • ANTHROPIC_AUTH_TOKEN (not ANTHROPIC_API_KEY) keeps Claude Code in OAuth/subscription mode with the full model picker. Setting ANTHROPIC_API_KEY locks the picker to 4 built-in models.
  • CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 tells Claude Code to fetch /v1/models from ANTHROPIC_BASE_URL and add the results to the picker.
  • The router forwards the OAuth token to Anthropic as Authorization: Bearer for real Claude models.

Provider configuration

All providers are defined in config/providers.toml:

[providers.anthropic]
url = "https://api.anthropic.com"
auth_method = "oauth_passthrough"
models = [
    { id = "claude-opus-4-7", display = "Claude Opus 4.7", tier = "opus" },
    # ...
]

[providers.ollama]
url = "http://localhost:11434"
auth_method = "none"
discover = true    # auto-populate from Ollama's model list
models = [
    # Static entries override display name and tier for known models
    { id = "qwen3.5", backend_id = "qwen3.5:cloud", display = "Qwen 3.5", tier = "sonnet" },
    # ...
]

[providers.minimax]
url = "https://api.minimax.io/anthropic"
auth_method = "api_key"
auth_value_env = "MINIMAX_API_KEY"
models = [
    { id = "MiniMax-M2.7", display = "MiniMax M2.7", tier = "opus" },
    # ...
]

See config/providers.toml for the full configuration with all supported auth methods and a commented example for adding new providers (Together, Groq, etc.).

Model tiers

Models are organized into tiers that map to Claude Code's model slots:

Tier Role Examples
Opus Frontier reasoning Claude Opus 4.7, DeepSeek V4 Pro, MiniMax M2.7
Sonnet Strong all-rounders Claude Sonnet 4.6, Qwen 3.5, GLM 5.1, Kimi K2.6
Haiku Fast / small Claude Haiku 4.5, Gemma4 31B, Qwopus 3.5 9B
Local Offline / low-latency Devstral Small 24B, Ministral 3 14B

For discover = true providers, unknown models get auto-assigned tiers based on parameter count: 200B+ → Opus, 30B+ → Sonnet, 10B+ → Haiku, <10B → Local.

Explicit prefix overrides

Bypass the routing table by prefixing the model with a provider name:

claude --model anthropic:haiku  # → real Anthropic, always
claude --model ollama:gemma4    # → Ollama, always
claude --model minimax:opus     # → MiniMax, always

Startup banner

When the router starts, it prints the full model list grouped by tier and provider status:

ccb-route  :9001
  ── Opus ──
    Claude Opus 4.7 (claude-opus-4-7 via anthropic [oauth])
    DeepSeek V4 Pro (deepseek-v4-pro via ollama [none])
    MiniMax M2.7 (MiniMax-M2.7 via minimax [api-key])
  ── Sonnet ──
    Claude Sonnet 4.6 (claude-sonnet-4-6 via anthropic [oauth])
    Qwen 3.5 (qwen3.5 via ollama [none])
    ...

Diagnostics

  • Router log: stderr (or redirect to /tmp/ccb-route.log)
  • Gateway cache: ~/.claude/cache/gateway-models.json — delete to force Claude Code to re-fetch on next launch
  • Verify env vars: zsh -i -c 'echo $ANTHROPIC_BASE_URL' should print http://localhost:9001
  • Test model list: curl -s http://localhost:9001/v1/models | python3 -c "import json,sys; print(len(json.load(sys.stdin)['data']), 'models')"

Benchmarks

Criterion benchmarks run against real fixture files (git status, pytest output, tsc output):

cargo bench
# Opens HTML report at target/criterion/compression/report/index.html

Every operation is also logged to ~/.claude/ccb_log.jsonl:

{"timestamp":"2026-05-21T14:23:01Z","feature":"trim","command":"git status","tokens_in":840,"tokens_out":142,"bytes_in":3360,"bytes_out":568}

Why not just RTK?

RTK is great at one thing: compressing command output. ccb is the full barber shop:

Capability RTK ccb
Command output compression ✓ (trim)
Token savings analytics ✓ (gain)
Lazy skill/context loading ✓ (fade)
Context window monitoring ✓ (context)
Budget inspector ✓ (lineup)
Knowledge graph (Layer 3) ✓ (expert)
Multi-provider model router ✓ (route)
Code symbol graph ✓ (graph)
Hook scripts included
Build without unused features ✓ (feature flags)

Roadmap

  • Layer 1 — Token management (trim, fade, context, buzz, gain)
  • Layer 2 — Code symbol graph (graph index, graph search)
  • Layer 3 — Unified knowledge graph + expert personas
  • Classify — Two-tier safety classifier with graph-aware Read hints
  • tree-sitter AST-based symbol extraction (Rust, Python, TypeScript, JavaScript)
  • Model router — multi-provider routing with gateway discovery, /model picker integration, auto-discovery
  • Infra metadata in graph — operational knowledge (GitHub auth routing, deployment configs)

Contributing

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/your-feature)
  3. Make your changes — cargo test must pass, cargo clippy -- -D warnings must be clean
  4. Commit (git commit -m 'feat: your feature')
  5. Push and open a PR against main

CI runs on every PR: cargo test, cargo clippy, cargo fmt --check.


Project Structure

src/
├── main.rs            # entry point, command dispatch
├── cli.rs             # clap definitions
├── config.rs          # ccb.toml loading
├── log.rs             # token estimation, CompressionEvent, JSONL logging
├── analytics.rs       # ccb gain — aggregate savings from log
├── utils.rs           # shared utilities (progress bar)
├── bin/
│   └── ccb-route.rs   # model router binary (--features route)
└── features/
    ├── trim.rs        # command output compression + tests
    ├── fade.rs        # lazy skill loading + index lookup
    ├── context.rs     # context window monitoring
    ├── lineup.rs      # context budget report
    ├── buzz.rs        # nuclear mode cleanup + tests
    ├── cut.rs         # all-in-one compression
    ├── index.rs       # skills index generator
    ├── install.rs     # hook wiring into ~/.claude/settings.json
    ├── route.rs       # model router logic (--features route)
    ├── classify.rs    # two-tier safety classifier (--features classify)
    ├── expert.rs      # unified knowledge graph (--features expert)
    └── graph.rs       # code symbol graph + infra metadata (--features graph)
hooks/
├── skill_loader.sh        # PreToolUse hook for /skill
├── context_monitor.sh     # PostToolUse hook for context checks
└── expert_pretooluse.sh   # PreToolUse hook for knowledge graph traversal
docs/
├── ARCHITECTURE.md        # three-layer design + knowledge graph spec
├── TEST_DATA.md           # real trim fixture inputs/outputs
└── TEST_DATA_LAYER3.md    # real expert graph fixture inputs/outputs
datasets/
└── sentinel.yaml          # bundled security expert (OWASP Top 10)

Configuration

~/.claude/ccb.toml (auto-created with defaults on first use):

terse = false
conversation_style = false
nerd_font = false    # true = Nerd Font icons in statusline (falls back to ASCII)

[features]
trim = true
fade = true
sandbox = false
terse = false
graph = false
expert = false

License

MIT — see LICENSE for details.

About

Composable token management layer for Claude Code

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages