
Caveman
- 366 installs
- 23.8k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
Caveman is a Claude Code skill that puts the agent into an ultra-compressed communication mode to cut token usage by about 75 percent.
About
Caveman is a communication-mode skill that makes the agent respond in a terse, compressed style by dropping articles, filler words, and pleasantries. A developer uses it to cut token usage by roughly 75 percent while keeping full technical accuracy. It stays active across turns once triggered and temporarily reverts to normal prose for security warnings and irreversible-action confirmations.
- Compresses agent replies ~75% while keeping technical substance
- Auto-suspends for security warnings and destructive-action confirmations
- Triggered by phrases like caveman mode, be brief, or /caveman
Caveman by the numbers
- 366 all-time installs (skills.sh)
- Ranked #827 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
caveman capabilities & compatibility
- Capabilities
- token optimization
- Use cases
- token optimization
- Pricing
- Free
What caveman says it does
Ultra-compressed communication mode. Cuts token usage ~75% by dropping filler, articles, and pleasantries while keeping full technical accuracy.
Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread
npx skills add https://github.com/alirezarezvani/claude-skills --skill cavemanAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 366 |
|---|---|
| repo stars | ★ 23.8k |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
What it does
Cut Claude's token usage by ~75% by switching it into a terse, filler-free communication mode.
Who is it for?
Long agent sessions where you want shorter, cheaper responses without losing technical detail.
Skip if: Situations needing careful multi-step explanations, or security and destructive-action confirmations where clarity matters more than brevity.
When should I use this skill?
The user says caveman mode, talk like caveman, less tokens, be brief, or invokes /caveman.
What you get
Agent replies drop roughly 75 percent in token cost while keeping all technical substance.
- Compressed agent responses
- Token savings estimates
By the numbers
- ~75% token reduction
Files
Caveman Mode
Derived from Matt Pocock's caveman (MIT). Matt's voice preserved verbatim. Additions: compression tools + references + cs-* wrapper (see references/companion_tooling.md).
Respond terse like smart caveman. All technical substance stay. Only fluff die.
Persistence
ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
Rules
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Abbreviate common terms (DB/auth/config/req/res/fn/impl). Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough.
Technical terms stay exact. Code blocks unchanged. Errors quoted exact.
Pattern: [thing] [action] [reason]. [next step].
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..." Yes: "Bug in auth middleware. Token expiry check use < not <=. Fix:"
Examples
"Why React component re-render?"
Inline obj prop -> new ref -> re-render. useMemo."Explain database connection pooling."
Pool = reuse DB conn. Skip handshake -> fast under load.
Auto-Clarity Exception
Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done.
Example -- destructive op:
Warning: This will permanently delete all rows in the users table and cannot be undone.>
```sql
DROP TABLE users;
```
>
Caveman resume. Verify backup exist first.
Tooling
See references/companion_tooling.md. Tools: compressor + estimator + lint. Agent: cs-caveman-mode. Command: /cs:caveman.
---
Version: 1.0.0 Derived: Matt Pocock (MIT) + this repo's wrapper
Companion Tooling
Compression tools + cs-* wrapper layered on top of Matt's caveman skill.
Validation Tools (stdlib Python)
| Tool | Purpose | Run when |
|---|---|---|
scripts/caveman_compressor.py | Apply Matt's rules deterministically (drop articles/filler/pleasantries/hedging, abbreviate technical terms, use causality arrows) | Want a starting compressed version of any text |
scripts/token_savings_estimator.py | Estimate token + cost savings using 4 chars/token (prose) or 3.5 chars/token (technical) heuristic | Want to quantify the value of caveman mode |
scripts/caveman_lint.py | Detect banned vocabulary in a response (pleasantries, filler, hedging, metatalk, verbose phrases). Whitelist: code blocks, inline code, exception zones | Verify a response complies with caveman rules |
All three tools:
- Stdlib-only (no external dependencies)
- Run with embedded sample if no input provided
- Output text or JSON (
--output json) - Code blocks + inline code preserved (compression skips them)
Token-Savings Heuristic
The estimator uses character-per-token approximations:
- 4.0 chars/token for English prose
- 3.5 chars/token for technical text (detected by presence of
{,},(),->,==,//, etc.)
This is within 10-15% of cl100k_base / o200k_base tokenizers for English. For exact token counts use the model's actual tokenizer (e.g., tiktoken).
cs-caveman-mode Persona Agent
Lives at ../agents/cs-caveman-mode.md. Voice: terse, fragments-OK, no filler. Persistence is the hard rule — once activated stays active until "stop caveman" / "normal mode".
/cs:caveman Slash Command
Lives at ../commands/cs-caveman.md. Single-trigger activation. Equivalent to typing "caveman mode" but more explicit.
When Caveman Backfires (See main SKILL.md "Auto-Clarity Exception")
The compressor + lint tool both whitelist these zones — Matt's rule is explicit:
- Security warnings
- Irreversible action confirmations
- Multi-step sequences where fragment order risks misread
- User asks to clarify or repeats question
The lint tool detects **Warning:**, destructive, irreversible, cannot be undone markers and softens its verdict accordingly.
Why Wrap Matt's Original
Matt's caveman skill is tight + complete. The wrapper adds: 1. Deterministic compression — apply rules consistently across responses (not just in spirit) 2. Quantification — show ROI of caveman mode in tokens/dollars 3. Compliance checking — verify a response actually follows rules (vs claiming to)
Attribution
Original: matt-pocock/skills/skills/productivity/caveman (MIT).
---
Source authorities (non-exhaustive):
- Matt Pocock — caveman (https://github.com/mattpocock/skills/, MIT) — the upstream source
- Anthropic — Token usage best practices (https://docs.claude.com/en/docs/build-with-claude/prompt-engineering) — token-conscious prompting
- OpenAI tokenizer docs —
tiktokenlibrary + cl100k_base / o200k_base heuristics - Strunk & White — "The Elements of Style" (1918) — "omit needless words"; foundational text on prose compression
- Plain Language Movement / Plain Writing Act of 2010 — federal mandate for concise government writing
- Norman, D. — "Living with Complexity" (2010) — when simplicity helps vs hurts cognition
- Pareto principle in communication — 20% of words carry 80% of information density
Compression Principles for LLM Output
This reference answers exactly one decision: what should be cut and what must stay when compressing LLM output for token efficiency?
Pair with scripts/caveman_compressor.py for deterministic application.
Matt Pocock's Foundational Insight
"Respond terse like smart caveman. All technical substance stay. Only fluff die."
>
— Matt Pocock, caveman SKILL.md
The crucial distinction: substance vs fluff. Caveman mode is aggressive about fluff and conservative about substance. Confusion between the two creates either bloated responses (under-cutting) or hallucinated answers (over-cutting).
What Counts as Fluff (Safe to Drop)
| Category | Examples | Why safe to drop |
|---|---|---|
| Articles | a, an, the | Grammatical scaffolding; meaning preserved without them |
| Filler | just, really, basically, actually, simply, obviously | Add no information; speakers use as verbal pauses |
| Pleasantries | sure!, certainly, of course, happy to help | Social lubrication; cost tokens with zero info gain |
| Hedging | might, maybe, perhaps, likely, possibly | Either qualify with data or remove; vague hedging is fake precision |
| Metatalk | as you can see, worth noting, that said | Self-referential commentary about the response itself |
| Verbose phrases | "implementation of a solution for" → "fix"; "in order to" → "to" | Phrase-level redundancy |
What Counts as Substance (Must Stay)
| Category | Examples | Why preserve |
|---|---|---|
| Technical terms | useMemo, NULL, HTTP/2, OAuth2 | Exact names matter; abbreviation breaks identifiers |
| Code blocks | All ``...`` regions | Syntactically meaningful; whitespace + characters matter |
| Inline code | useState, auth_token | Same as code blocks |
| Quoted strings | "expected value", 'string literal' | Exact text matters |
| Error messages | "TypeError: cannot read property X" | Diagnostic precision required |
| Numbers + units | 200ms, 4kb, 99.9% | Exactness matters for engineering decisions |
| Causal claims | "X causes Y" — can be compressed to "X -> Y" | The relationship is the substance |
The Abbreviation Cost-Benefit
Abbreviating common technical terms saves tokens but only when: 1. The abbreviation is universally understood (DB, auth, config, fn — yes; ETL, ORM — maybe; "imp" for implementation — no) 2. The reader has full context (caveman responses are usually mid-conversation) 3. The exact term isn't being introduced (don't abbreviate the FIRST use of a term)
Matt's abbreviation list is conservative + universal:
- DB, auth, config, req, res, fn, impl, env, deps, repo, docs, app
Causality Arrows: The Compression Win
Replacing verbose causality with arrows is high-leverage:
| Verbose | Caveman | Savings |
|---|---|---|
| "X leads to Y" (3 words) | "X -> Y" (1 unit) | 67% |
| "which causes Y to happen" (5 words) | "-> Y" (2 units) | 60% |
| "because of X, Y happens" (5 words) | "Y <- X" (2 units) | 60% |
Arrows are unambiguous + compact + preserve causality (not just adjacency).
Compression Anti-Patterns
1. Dropping subject pronouns at all costs — "Bug in auth" is fine. "Auth bug, fix soon" loses clarity. Keep enough syntax to disambiguate. 2. Over-abbreviating — "MWMV" instead of "memory write/memory verify" forces reader to expand mentally; net cognitive cost goes up. 3. Dropping units — "Response takes 200" — 200 what? ms? bytes? Keep units always. 4. Compressing security warnings — Matt's explicit exception. A truncated security warning is worse than no caveman mode. 5. Dropping examples — "Bug in auth. Fix." — what bug? what fix? Caveman keeps the substance, just removes the wrapping.
Compression vs Clarity Tradeoff
Compression is a tax on the reader. The trade-off is worth it when:
- The reader has the context to fill in the gaps (mid-conversation, technical peer)
- The information density is high enough to justify cognitive load
- The savings are meaningful (>20% token reduction)
Not worth it when:
- New context being established (introductions, first turns)
- Multi-step sequences where order matters
- Multi-stakeholder communication (caveman style confuses non-technical readers)
- Audio interfaces (caveman text reads badly when read aloud)
How Much Compression Is Realistic?
Matt's claim is ~75% — this is the upper bound on extremely verbose responses (with multiple pleasantries + filler + hedging). Realistic ranges:
| Response type | Realistic compression |
|---|---|
| ChatGPT-style verbose response | 50-75% |
| Already-concise technical answer | 10-25% |
| Code-heavy response (most text is code) | 5-15% |
| Single-sentence answer | 0-30% |
The compressor in this skill targets 20-50% on typical mid-conversation responses, which is meaningful at scale.
When This Reference Doesn't Help
- Code minification — different concern; this is about prose around code, not code itself
- Prompt compression for inputs — different mode; input compression has different rules
- Speech synthesis — caveman text reads poorly aloud
- Marketing copy — different goal; conversion > brevity
---
Source authorities (non-exhaustive):
- Matt Pocock — caveman (https://github.com/mattpocock/skills/, MIT) — the upstream source + rule set
- Strunk & White — "The Elements of Style" (1918) — Rule 17: "Omit needless words"
- Plain Language Movement / Plain Writing Act of 2010 (https://www.plainlanguage.gov/) — government mandate for concise English; well-researched compression rules
- Pinker, S. — "The Sense of Style" (2014) — cognitive science of clear writing
- Williams, J. — "Style: Toward Clarity and Grace" (1995) — academic compression patterns
- Anthropic — Prompt engineering for tokens (https://docs.claude.com/en/docs/build-with-claude/prompt-engineering) — token-conscious patterns
- OpenAI tokenizer documentation — character-per-token ratios across cl100k_base / o200k_base
- Pareto principle in writing — 20% of words carry 80% of meaning
When Caveman Backfires
This reference answers exactly one decision: when should caveman mode NOT be used, and what are the failure modes?
Pair with scripts/caveman_lint.py — the linter detects exception-zone markers and softens its verdict accordingly.
Matt Pocock's Auto-Clarity Exception (Verbatim)
"Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done."
>
— Matt Pocock, caveman SKILL.md
This is the non-negotiable exception list. Compressing in these zones can cause user harm — not just token cost confusion.
The Five Failure Modes
1. Compressed Security Warnings
Failure: Warning: drop users table. Cannot undo. Why bad: Two compound failures: 1. Reader skims caveman text; warning blends into surrounding terse responses 2. Critical verb ("drop", "cannot undo") loses emphasis without sentence structure
Correct: Drop caveman entirely for warnings:
Warning: This will permanently delete all rows in the users table and cannot be undone.The full sentence + bold marker + explicit consequences make it unmissable.
2. Compressed Multi-Step Sequences
Failure: npm install. npm test. npm build. deploy. Why bad: Fragment order = execution order. If reader misreads order, deploy can run before tests pass.
Correct: Number steps + use full sentences when order matters:
1. Install dependencies: npm install2. Run tests: npm test — must pass before continuing3. Build: npm build4. Deploy: only after step 3 succeeds
3. Compressed Clarification Responses
Failure: User asks "Wait, what?" → Response: Token expiry. Less-than. Fix. Why bad: User asked for clarification because original was unclear. Compressing further compounds the problem.
Correct: Drop caveman temporarily, explain fully, then resume:
The original issue: the token expiry check uses<(strict less-than) instead of<=(less-than-or-equal). This means tokens at the exact expiry timestamp are rejected even though they should still be valid. The fix changes the operator from<to<=.
>
[Resume caveman.]
4. Compressed Irreversible Action Confirmations
Failure: rm -rf node_modules. Confirm. Why bad: "Confirm" loses weight without context. User may type "yes" reflexively.
Correct: Drop caveman, state explicitly:
About to run rm -rf node_modules which permanently deletes the directory.>
Reply with the exact string "DELETE" to proceed, or "cancel" to abort.
The exact-string requirement breaks reflex confirmation.
5. Compressed First-Turn Responses
Failure: User's first message → Response in caveman. Why bad: No shared context yet. Reader can't fill in caveman's gaps.
Correct: First turn establishes context fully. Activate caveman ONLY after user explicitly triggers it (per Matt's activation triggers: "caveman mode", "talk like caveman", /caveman, etc.).
Less-Obvious Backfire Cases
Caveman in Code Review
Caveman compression on code-review feedback can lose nuance:
Failure: Bug L42. Var name bad. Refactor. Why bad: Three findings, no specificity. Engineer can't tell what to fix.
Better: L42: var name "x" → "userIndex". L67: off-by-one in loop bound.
The fix: caveman compresses sentence STRUCTURE, not technical SPECIFICITY.
Caveman in Estimates / Forecasts
Hedging is fluff per Matt's rules. But hedging carries information in estimates:
Failure: Done by Friday. (when uncertain) Why bad: Reads as commitment, but actual confidence was 60%.
Correct: Caveman exception for probability claims. State confidence explicitly:
Friday delivery — 60% confidence. Risks: API spec churn.
Caveman in Multi-Stakeholder Threads
Caveman is for technical peer-to-peer (or peer-to-self) communication. When non-technical stakeholders are reading:
Failure: Auth bug. Fix shipping. Why bad: PM/CEO/non-engineer reader can't decode "Fix shipping" — is shipping affected?
Correct: Drop caveman in stakeholder communication. Save it for technical conversations.
Detection Patterns (How caveman_lint.py Helps)
The lint tool detects these markers as exception-zone signals:
**Warning:**markdown bold + worddestructiveirreversiblecannot be undone
When present, the linter softens FAIL → WARN. This isn't perfect — manual review still required for stakeholder mismatches + first-turn responses.
Resuming Caveman After Exception
Matt's rule: "Resume caveman after clear part done."
Pattern:
Warning: [full sentence warning].
>
[empty line]
>
Caveman resume. [terse fragment continues].
The explicit "Caveman resume." marker signals the reader that compression resumes. This is critical when the response is long enough that the reader might lose track of which mode they're in.
Tooling Recommendation
When in doubt: 1. Run caveman_lint.py on the proposed response 2. If FAIL → consider rewriting (banned vocab present) 3. If WARN with exception context → check whether the exception is genuine 4. If CLEAN → ship
When This Reference Doesn't Help
- Brevity in writing generally — different concern; see editing references
- Code minification — different mode; this is about prose around code
- API response compression — gzip/brotli, not prose compression
---
Source authorities (non-exhaustive):
- Matt Pocock — caveman (https://github.com/mattpocock/skills/, MIT) — the auto-clarity exception list
- Nielsen Norman Group — Error message design — when verbosity in errors helps vs hurts
- FAA Human Factors research on cockpit warnings — emphasis + redundancy in safety-critical communications
- Krug, S. — "Don't Make Me Think" (2000) — when brevity becomes ambiguity
- Schneier, B. — Communication on security warnings — why brevity in security messages is dangerous
- Larson, W. — "An Elegant Puzzle" (2019) — engineering manager communication patterns
- Rommetveit, R. — Linguistic shared context — when compression depends on shared frame
#!/usr/bin/env python3
"""caveman_compressor.py — Apply Matt Pocock's caveman compression rules to text.
Stdlib-only. Deterministic regex-based compression matching the rules in
Matt Pocock's caveman skill SKILL.md:
1. Drop articles (a/an/the)
2. Drop filler (just/really/basically/actually/simply)
3. Drop pleasantries (sure/certainly/of course/happy to)
4. Drop hedging (might/maybe/perhaps/likely/possibly)
5. Abbreviate common technical terms (database -> DB, configuration -> config, etc.)
6. Strip conjunctions where safe (and/but at sentence start)
7. Use arrows for "leads to" / "causes" phrases (-> )
8. Strip "as you can see / it should be noted / it's worth mentioning"
PRESERVES:
- Code blocks (```...```) unchanged
- Inline code (`...`) unchanged
- Technical terms named verbatim
- Quoted strings unchanged
NO LLM CALLS. Stdlib only.
Usage:
python caveman_compressor.py # uses embedded sample
python caveman_compressor.py "your text here"
python caveman_compressor.py --file path/to/input.txt
python caveman_compressor.py "text" --output json
"""
import argparse
import json
import re
import sys
from typing import Any, Dict, List, Tuple
# Filler/pleasantry/hedging vocabularies (per Matt's rules)
ARTICLES = {"a", "an", "the"}
FILLER = {"just", "really", "basically", "actually", "simply", "obviously", "literally"}
PLEASANTRIES_PHRASES = [
"sure!", "sure,", "certainly!", "certainly,",
"of course!", "of course,",
"happy to help", "i'd be happy to", "i would be happy to",
"great question", "good question",
"absolutely!", "absolutely,",
"no problem!", "no problem,",
]
HEDGING = {"might", "maybe", "perhaps", "likely", "possibly", "probably"}
METATALK_PHRASES = [
"as you can see",
"it should be noted",
"it's worth mentioning",
"it is worth mentioning",
"needless to say",
"to be clear",
"in other words",
"that said",
"having said that",
]
# Technical term abbreviations
ABBREVIATIONS = [
(r"\bdatabase\b", "DB"),
(r"\bdatabases\b", "DBs"),
(r"\bauthentication\b", "auth"),
(r"\bauthorization\b", "authz"),
(r"\bconfiguration\b", "config"),
(r"\bconfigurations\b", "configs"),
(r"\brequest\b", "req"),
(r"\brequests\b", "reqs"),
(r"\bresponse\b", "res"),
(r"\bresponses\b", "ress"),
(r"\bfunction\b", "fn"),
(r"\bfunctions\b", "fns"),
(r"\bimplementation\b", "impl"),
(r"\bimplementations\b", "impls"),
(r"\benvironment\b", "env"),
(r"\bdependencies\b", "deps"),
(r"\bdependency\b", "dep"),
(r"\brepository\b", "repo"),
(r"\brepositories\b", "repos"),
(r"\bdocumentation\b", "docs"),
(r"\bapplication\b", "app"),
(r"\bapplications\b", "apps"),
]
# Causality phrase -> arrow
CAUSALITY_PATTERNS = [
(re.compile(r"\b(which\s+)?(leads?|causes?|results?\s+in|gives?\s+you|produces?)\s+", re.IGNORECASE), "-> "),
(re.compile(r"\bbecause\s+of\b", re.IGNORECASE), "<- "),
]
# Embedded sample
SAMPLE_INPUT = (
"Sure! I'd be happy to help you with that. The issue you're experiencing is "
"likely caused by a misconfiguration in the authentication middleware, where "
"the token expiry check is actually using a strict less-than comparison "
"instead of less-than-or-equal. This basically means tokens at the exact "
"expiry timestamp will get rejected. To fix this, you should simply update "
"the configuration of the auth function to use `<=` instead of `<`."
)
def _protect_code(text: str) -> Tuple[str, List[str]]:
"""Replace code blocks + inline code with placeholders, return text + protected list."""
protected: List[str] = []
def replace_block(m: re.Match) -> str:
protected.append(m.group(0))
return f"\x00CODE{len(protected) - 1}\x00"
text = re.sub(r"```.*?```", replace_block, text, flags=re.DOTALL)
text = re.sub(r"`[^`]+`", replace_block, text)
return text, protected
def _restore_code(text: str, protected: List[str]) -> str:
for i, code in enumerate(protected):
text = text.replace(f"\x00CODE{i}\x00", code)
return text
def _drop_articles(text: str) -> str:
pattern = re.compile(r"\b(" + "|".join(ARTICLES) + r")\s+", re.IGNORECASE)
return pattern.sub("", text)
def _drop_word_set(text: str, words: set) -> str:
pattern = re.compile(r"\b(" + "|".join(words) + r")\b\s*", re.IGNORECASE)
return pattern.sub("", text)
def _drop_phrases(text: str, phrases: List[str]) -> str:
for phrase in phrases:
text = re.sub(re.escape(phrase) + r"\s*", "", text, flags=re.IGNORECASE)
text = re.sub(re.escape(phrase.rstrip(",!")) + r"\s*", "", text, flags=re.IGNORECASE)
return text
def _apply_abbreviations(text: str) -> str:
for pattern, replacement in ABBREVIATIONS:
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
return text
def _apply_causality_arrows(text: str) -> str:
for pattern, replacement in CAUSALITY_PATTERNS:
text = pattern.sub(replacement, text)
return text
def _strip_leading_conjunctions(text: str) -> str:
return re.sub(r"(^|\.\s+)(and|but|so)\s+", r"\1", text, flags=re.IGNORECASE)
def _collapse_whitespace(text: str) -> str:
text = re.sub(r"\s+", " ", text)
text = re.sub(r"\s+([.,;:!?])", r"\1", text)
return text.strip()
def compress(text: str) -> str:
"""Apply Matt Pocock's caveman rules. Returns compressed text."""
text, protected = _protect_code(text)
text = _drop_phrases(text, PLEASANTRIES_PHRASES)
text = _drop_phrases(text, METATALK_PHRASES)
text = _drop_word_set(text, FILLER)
text = _drop_word_set(text, HEDGING)
text = _drop_articles(text)
text = _apply_abbreviations(text)
text = _apply_causality_arrows(text)
text = _strip_leading_conjunctions(text)
text = _collapse_whitespace(text)
text = _restore_code(text, protected)
return text
def analyze(original: str, compressed: str) -> Dict[str, Any]:
orig_words = len(original.split())
new_words = len(compressed.split())
saved = orig_words - new_words
pct = round(100.0 * saved / max(orig_words, 1), 1)
return {
"original_chars": len(original),
"compressed_chars": len(compressed),
"original_words": orig_words,
"compressed_words": new_words,
"words_saved": saved,
"percent_savings": pct,
"compressed_text": compressed,
}
def render_text(original: str, result: Dict[str, Any]) -> str:
lines = []
lines.append("=" * 72)
lines.append("CAVEMAN COMPRESSOR")
lines.append("=" * 72)
lines.append("")
lines.append("ORIGINAL:")
lines.append(f" {original}")
lines.append("")
lines.append("COMPRESSED:")
lines.append(f" {result['compressed_text']}")
lines.append("")
lines.append("-" * 72)
lines.append(f"Chars: {result['original_chars']} -> {result['compressed_chars']}")
lines.append(f"Words: {result['original_words']} -> {result['compressed_words']}")
lines.append(f"Savings: {result['words_saved']} words ({result['percent_savings']}%)")
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(
description="Compress text per Matt Pocock's caveman rules.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument("text", nargs="?", help="Input text (uses embedded sample if omitted)")
parser.add_argument("--file", help="Read input from file")
parser.add_argument("--output", choices=("text", "json"), default="text", help="Output format")
args = parser.parse_args()
if args.file:
try:
with open(args.file, "r", encoding="utf-8") as f:
original = f.read()
except (IOError, OSError) as e:
print(f"error: {e}", file=sys.stderr)
return 1
elif args.text:
original = args.text
else:
original = SAMPLE_INPUT
compressed = compress(original)
result = analyze(original, compressed)
if args.output == "json":
print(json.dumps(result, indent=2))
else:
print(render_text(original, result))
return 0
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""caveman_lint.py — Lint a response for caveman-mode compliance.
Stdlib-only. Detects banned vocabulary in a response that's supposed to be in
caveman mode. Returns specific findings + verdict.
Banned categories per Matt Pocock's caveman rules:
- Pleasantries (sure, certainly, of course, happy to)
- Filler (just, really, basically, actually, simply)
- Hedging (might, maybe, perhaps, likely)
- Metatalk (as you can see, worth noting)
- Verbose phrases ("the implementation of a solution for")
Whitelist (NOT banned even in caveman mode):
- Words inside code blocks
- Words inside inline code
- Words inside quoted strings
- Caveman exception zones (security warnings, destructive op confirmations)
Usage:
python caveman_lint.py # uses embedded samples
python caveman_lint.py "response text"
python caveman_lint.py --file path/to/response.txt
python caveman_lint.py "text" --output json
"""
import argparse
import json
import re
import sys
from typing import Any, Dict, List
BANNED_PHRASES = {
"pleasantry": [
"sure!", "sure,", "certainly", "of course", "happy to help",
"i'd be happy", "i would be happy", "great question", "good question",
"absolutely", "no problem!",
],
"filler": ["just", "really", "basically", "actually", "simply", "obviously", "literally"],
"hedging": ["might", "maybe", "perhaps", "likely", "possibly", "probably"],
"metatalk": [
"as you can see", "it should be noted", "worth mentioning",
"needless to say", "to be clear", "in other words",
"that said", "having said that",
],
"verbose": [
"implement a solution for", "the implementation of",
"in order to", "for the purpose of", "with respect to",
"due to the fact that",
],
}
# Patterns that DROP caveman temporarily (whitelisted zones)
EXCEPTION_MARKERS = [
re.compile(r"\*\*warning:\*\*", re.IGNORECASE),
re.compile(r"\bdestructive\b", re.IGNORECASE),
re.compile(r"\birreversible\b", re.IGNORECASE),
re.compile(r"\bcannot be undone\b", re.IGNORECASE),
]
SAMPLE_BAD = (
"Sure! I'd be happy to help. The issue is actually quite simple — basically, "
"you just need to update the configuration. It's worth mentioning that this might "
"cause a slight performance hit, but probably not noticeable."
)
SAMPLE_GOOD = "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix: change to `<=`."
def _protect_code(text: str) -> str:
"""Mask code blocks + inline code so banned-word matching skips them."""
text = re.sub(r"```.*?```", lambda m: "\x00" * len(m.group(0)), text, flags=re.DOTALL)
text = re.sub(r"`[^`]+`", lambda m: "\x00" * len(m.group(0)), text)
return text
def _has_exception_context(text: str) -> bool:
return any(p.search(text) for p in EXCEPTION_MARKERS)
def _count_phrase(phrase: str, masked: str) -> int:
return len(re.findall(r"\b" + re.escape(phrase) + r"\b", masked, re.IGNORECASE))
def _violation_record(category: str, phrase: str, count: int) -> Dict[str, Any]:
return {"category": category, "phrase": phrase, "count": count}
def find_violations(text: str) -> List[Dict[str, Any]]:
"""Find banned phrases. Returns list of {category, phrase, count}."""
masked = _protect_code(text)
violations: List[Dict[str, Any]] = []
for category, phrases in BANNED_PHRASES.items():
for phrase in phrases:
count = _count_phrase(phrase, masked)
if count > 0:
violations.append(_violation_record(category, phrase, count))
return violations
def analyze(text: str) -> Dict[str, Any]:
violations = find_violations(text)
total_violations = sum(v["count"] for v in violations)
has_exception = _has_exception_context(text)
# Verdict logic:
# 0 violations + reasonable length -> CLEAN
# <= 2 violations OR exception context -> WARN
# > 2 violations -> FAIL
if total_violations == 0:
verdict = "CLEAN"
elif has_exception:
verdict = "WARN"
# When there's a security warning, some normal language is allowed
elif total_violations <= 2:
verdict = "WARN"
else:
verdict = "FAIL"
return {
"char_count": len(text),
"word_count": len(text.split()),
"violation_categories": sorted(set(v["category"] for v in violations)),
"total_violations": total_violations,
"has_exception_context": has_exception,
"violations": violations,
"verdict": verdict,
}
def render_text(text: str, r: Dict[str, Any]) -> str:
lines = []
lines.append("=" * 72)
lines.append("CAVEMAN LINT")
lines.append("=" * 72)
lines.append("")
preview = text[:200] + ("..." if len(text) > 200 else "")
lines.append(f"Text ({r['char_count']} chars, {r['word_count']} words):")
lines.append(f" {preview}")
lines.append("")
lines.append("-" * 72)
lines.append(f"Violations: {r['total_violations']}")
lines.append(f"Categories hit: {r['violation_categories']}")
if r["has_exception_context"]:
lines.append("Exception context detected (warning/destructive zone — some prose allowed)")
lines.append("")
if r["violations"]:
for v in r["violations"]:
lines.append(f" [{v['category']:11s}] x{v['count']:2d} '{v['phrase']}'")
else:
lines.append(" No banned phrases found.")
lines.append("")
lines.append("-" * 72)
lines.append(f"Verdict: {r['verdict']}")
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(
description="Lint a response for caveman-mode compliance.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument("text", nargs="?", help="Input text (uses embedded sample if omitted)")
parser.add_argument("--file", help="Read input from file")
parser.add_argument("--output", choices=("text", "json"), default="text", help="Output format")
args = parser.parse_args()
if args.file:
try:
with open(args.file, "r", encoding="utf-8") as f:
text = f.read()
except (IOError, OSError) as e:
print(f"error: {e}", file=sys.stderr)
return 1
elif args.text:
text = args.text
else:
text = SAMPLE_BAD
result = analyze(text)
if args.output == "json":
print(json.dumps({"text": text, **result}, indent=2))
else:
print(render_text(text, result))
return 0 if result["verdict"] == "CLEAN" else 1
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""token_savings_estimator.py — Estimate token-cost savings from caveman compression.
Stdlib-only. Uses a chars-per-token heuristic (4 chars/token average for English
prose; 3.5 for technical text) to estimate output tokens before vs after caveman
compression.
Why heuristic and not real tokenizer:
- No external dependencies (stdlib only)
- Tokenizer accuracy varies by model (cl100k_base vs o200k_base vs others)
- Heuristic is within 10-15% of real tokenizer output for English prose
- Reports both heuristic + character count so user can apply their own multiplier
Usage:
python token_savings_estimator.py # uses embedded sample
python token_savings_estimator.py "your text"
python token_savings_estimator.py --file path/to/input.txt
python token_savings_estimator.py "text" --output json
python token_savings_estimator.py "text" --price-per-mtok 3.00
"""
import argparse
import json
import sys
from typing import Any, Dict
# Import the compressor as a module
import os
_HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, _HERE)
from caveman_compressor import compress, SAMPLE_INPUT # noqa: E402
# Heuristic: average chars per token
CHARS_PER_TOKEN_PROSE = 4.0
CHARS_PER_TOKEN_TECHNICAL = 3.5
TECHNICAL_TOKEN_INDICATORS = ("```", "{", "}", "()", "->", "==", "//", "/*", "import ", "function ")
def _estimate_chars_per_token(text: str) -> float:
"""Heuristic: technical text has more tokens per char than prose."""
hit_count = sum(1 for sig in TECHNICAL_TOKEN_INDICATORS if sig in text)
if hit_count >= 3:
return CHARS_PER_TOKEN_TECHNICAL
return CHARS_PER_TOKEN_PROSE
def estimate_tokens(text: str) -> int:
return int(round(len(text) / _estimate_chars_per_token(text)))
def analyze(original: str, price_per_mtok: float = 0.0) -> Dict[str, Any]:
compressed = compress(original)
orig_tokens = estimate_tokens(original)
new_tokens = estimate_tokens(compressed)
saved = orig_tokens - new_tokens
pct = round(100.0 * saved / max(orig_tokens, 1), 1)
out: Dict[str, Any] = {
"original_chars": len(original),
"compressed_chars": len(compressed),
"chars_per_token_used": _estimate_chars_per_token(original),
"estimated_original_tokens": orig_tokens,
"estimated_compressed_tokens": new_tokens,
"tokens_saved": saved,
"percent_token_savings": pct,
"compressed_preview": compressed[:200] + ("..." if len(compressed) > 200 else ""),
}
if price_per_mtok > 0:
cost_per_token = price_per_mtok / 1_000_000.0
out["price_per_million_tokens"] = price_per_mtok
out["cost_saved_per_response_usd"] = round(saved * cost_per_token, 6)
out["cost_saved_per_1k_responses_usd"] = round(saved * cost_per_token * 1000, 4)
return out
def render_text(r: Dict[str, Any]) -> str:
lines = []
lines.append("=" * 72)
lines.append("TOKEN SAVINGS ESTIMATOR (caveman compression)")
lines.append("=" * 72)
lines.append("")
lines.append(f"Chars/token heuristic: {r['chars_per_token_used']:.1f} (prose=4.0; technical=3.5)")
lines.append("")
lines.append(f"Original: {r['original_chars']} chars ~ {r['estimated_original_tokens']} tokens")
lines.append(f"Compressed: {r['compressed_chars']} chars ~ {r['estimated_compressed_tokens']} tokens")
lines.append("")
lines.append(f"Savings: {r['tokens_saved']} tokens ({r['percent_token_savings']}%)")
if "price_per_million_tokens" in r:
lines.append("")
lines.append(f"At ${r['price_per_million_tokens']}/Mtok:")
lines.append(f" Cost saved per response: ${r['cost_saved_per_response_usd']:.6f}")
lines.append(f" Cost saved per 1k responses: ${r['cost_saved_per_1k_responses_usd']:.4f}")
lines.append("")
lines.append("-" * 72)
lines.append("Compressed preview:")
lines.append(f" {r['compressed_preview']}")
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(
description="Estimate token + cost savings from caveman compression.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
price_help = "Per-million-token price (USD) to estimate cost savings"
parser.add_argument("text", nargs="?", help="Input text (uses embedded sample if omitted)")
parser.add_argument("--file", help="Read input from file")
parser.add_argument("--output", choices=("text", "json"), default="text", help="Output format")
parser.add_argument("--price-per-mtok", type=float, default=0.0, help=price_help)
args = parser.parse_args()
if args.file:
try:
with open(args.file, "r", encoding="utf-8") as f:
original = f.read()
except (IOError, OSError) as e:
print(f"error: {e}", file=sys.stderr)
return 1
elif args.text:
original = args.text
else:
original = SAMPLE_INPUT
result = analyze(original, args.price_per_mtok)
if args.output == "json":
print(json.dumps(result, indent=2))
else:
print(render_text(result))
return 0
if __name__ == "__main__":
sys.exit(main())
Related skills
How it compares
Pick caveman for live session token savings; use separate summarization skills when you need to compress existing documents rather than change agent response style.
FAQ
How much does caveman mode reduce token usage?
It cuts token usage by roughly 75 percent by dropping filler, articles, and pleasantries while keeping technical accuracy.
How do I turn caveman mode off?
Say stop caveman or normal mode; it also temporarily suspends itself for security warnings and irreversible-action confirmations.