Two changes to how the ruleset instructs, with the behavior it asks for
unchanged.
Replace the shouted persistence clause ("ACTIVE EVERY RESPONSE. No revert after
many turns.") with a plain statement of scope: this is the default style for the
session until the user turns it off. Same instruction, stated once, in the
register the rest of the file uses.
Replace the "No self-reference. Never name or announce the style." prohibition
with the positive form — answer directly in this style, skip the mode tags and
the "Caveman:" recap, because they are redundant with the reply itself. A rule
that says what to do is followed more reliably than one that only forbids.
Also drops the em-dashes from the ruleset prose so the file does not model the
punctuation its own output should avoid. The SessionStart hook body carries the
same two rewrites, with its checksum updated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
read_source took one CRLF anywhere in the file as a mandate to write every
line CRLF; a mixed-ending document now takes the terminator the majority of
its lines use. write_text_atomic normalises before rendering, so model
output that already carries CRLF no longer becomes "\r\r\n". The backup is
written from the raw source bytes instead of re-rendered text, so it is a
byte-for-byte copy of what was there.
evals/llm_run.py resolves `claude` through shutil.which — npm installs it as
claude.CMD on Windows and CreateProcess does not apply PATHEXT, so the bare
name raised FileNotFoundError — and decodes subprocess output as UTF-8 with
errors="replace" rather than inheriting a cp1252 console codec.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmbqWDtNexzDfH5V3BMRz
Findings from a whole-repo cross-platform audit.
Data loss:
- evals/llm_run.py wrote the snapshot with write_text(ensure_ascii=False) and
no encoding: on Windows that truncates results.json to 0 bytes, then raises
at the first non-cp1252 char. Destroyed the committed snapshot and the run.
- compress.py read the user's target with errors="ignore", silently dropping
undecodable bytes, backing up the mangled text, passing its own readback
check, then overwriting the original. Now decodes strictly and refuses.
Broken on Windows:
- benchmarks/ and evals/ read SKILL.md, README.md and results.json without an
encoding; all three raise on a cp1252 decode.
- cavemem's Python client used text=True with no encoding, so a non-ASCII
memory failed to store and any recall came back mojibake.
- native-hook-fast spawned a second full Node process from inside a hook with
no timeout at all.
Also fixed:
- Backported the pnpm cross-drive shim fix to the five copies that never got
it; hoisted two inline parsers so all of them are unit-testable.
- portable-command now resolves through PATH/PATHEXT instead of silently
no-op'ing on a bare command name.
- uninstall.ps1 writes a temp .js instead of node -e (matches install.ps1).
- Real backoff on the flag rename retry; EPERM/EBUSY retry when replacing a
running binary.
- Hooks act on the first complete JSON payload instead of blocking on EOF;
the stats child timeout drops below the host's hook budget.
- Statusline tests run the .ps1 on Windows instead of skipping.
- Added the macOS CI job the repo never had.
Left unfixed and annotated in place: the win32 hookCommand emits PowerShell
call-operator syntax while #835 describes the runner as git-bash /bin/sh.
Needs a real Windows host to settle; guessing would break whichever works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NveRZs9mV97iX7HSgq3BYF
A user reported caveman producing "User see field marked mapped when it not"
where "User sees field marked mapped when not" is both correct English and one
token shorter. Dropping a verb ending saves nothing — "sees" and "see" are one
token each — and the inserted pronoun costs one. The style was growing output
to sound more like a caveman, which is the opposite of the point.
Adds a rule that compression is the only justification for a phrasing: never
add a word to sound caveman, keep the correct verb form when it costs the same,
and fall back to plain phrasing when caveman phrasing is not shorter. Same
reasoning the file already applies to invented abbreviations and causal arrows.
The same report had caveman writing a defect body in caveman speak. Boundaries
already covered "issue/PR/MR text" but a user saying "open a defect" or "file a
bug" was not obviously the same thing, so those words are named explicitly.
Fixes#837
Single-commit port of the Caveman 2 tree onto the original repo history
(candidate caveman-2@88878a4 applied over main@3098342). No unrelated-history
merge; original history untouched.
Preserved v1 surfaces:
- skills/caveman/SKILL.md byte-for-byte, plus plugin mirror and caveman.skill zip
- legacy CI (Node 18/20/22 installer + Python 3.11 suites) alongside engine-ci
- cli/install.js compatibility shim forwarding to bin/install.js
- .codex/config.toml ships both hooks and codex_hooks for older Codex (#617)
- README Trendshift + Skills.sh badges and local star-history asset
- v1.10.0-pinned curl installs; benchmarks/tmp-starcharts ignore rules
Fixed for public same-repo release:
- SECURITY.md vulnerability reporting points at this repository
- release-binaries.yml publishes to this repo via job-scoped GITHUB_TOKEN
(cross-repo PAT removed); binary-release environment still gates signing
- packages/cli/PUBLISHING.md rewritten for packages/ layout and same-repo flow
- profile-contract and provider-catalog lanes skip lane-scope gates on
initial import so the launch PR can gate on their real validation steps
- ANNOUNCEMENT.md network claim corrected: compression is local; requests
still go to the provider you configured
Caveman 2 stays opt-in: bin-v1.0.0 binaries and @caveman-ai/cli publish
separately after this lands and soaks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BLvb5QniwYjmcawyssQNw9
Every read/write resolved to the locale codec (cp1252/cp949 on Windows):
non-ASCII files were silently mojibake'd, and because Path.write_text
truncates before encoding, a UnicodeEncodeError left the target at 0 bytes.
The backup readback check couldn't catch it — it read back with the same
wrong codec.
- encoding=utf-8 pinned on every I/O call site (compress, validate,
detect, benchmark); validate now decodes strict — it's the fidelity gate
- write_text_atomic: encode first, temp file in same dir, fsync, preserve
permissions, os.replace; temp unlinked on any failure
- fix-retry pass gains the same empty-output guard as the first pass
- fix-retry preamble leak (#588): output must start at the original's
structural anchor (frontmatter/heading) or the attempt is rejected
- primary-write failure now prints the backup path — users hitting the
crash had no idea a backup existed
- extract_inline_codes: strip fences via the CommonMark-aware extractor;
the old column-0 regex leaked indented fences into inline-code pairing,
causing false validation failures (extracted from PR #619's diagnosis)
- SKILL.md/README/SECURITY corrected: backups live in the out-of-tree data
dir (#420), not beside the source file
Supersedes PRs #683#678#626#534 and the fence fix from #619 with a
local implementation. 58 python tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
/caveman-stats only reported gross output savings, hiding the regime where
the ~1,250-token/turn rule injection costs more than compression saves —
the exact case docs/HONEST-NUMBERS.md warns about.
- Est. rule overhead: per-turn injected-rule input cost x turns
(CAVEMAN_RULE_OVERHEAD_TOKENS overrides the default 1250)
- Est. net: saved minus overhead; when negative, says plainly that caveman
cost more than it saved for this workload
- shown only for attributed uniform spans (follows #601 attribution);
history rows without turn counts are excluded from net, never guessed
- statusline suffix stays gross savings, unchanged semantics
Reimplements the idea from PR #718 locally. 46 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
Verified against filed reports and applied minimal rewordings:
- never-drop-negations rule — dropping not/never inverts instructions (#670)
- tool-call silence defined: no preamble/progress notes around calls (#713, #714)
- language rule de-named: removes Portuguese/Spanish attractor tokens that
drove drift under compaction; every emitted line in session language
(#665, #654, #701, #539)
- particles/postpositions exempt from article-dropping — Korean/Japanese
case markers are grammar, not filler (#680)
- classical chars scoped to wenyan modes only; Auto-Clarity example marked
format-only so warnings render in session language (#679, #680)
- switch line covers all six levels + off (#549, #670)
- persisted-text boundary: docs/issues/PR text/memory files write normal
(#483, #562, #582, #670)
- wenyan-full row: character reduction labeled chars-not-tokens
Supersedes PRs #715#702#684#685#667#654#658#549#670#483#562#582
with local implementations. Eval snapshots need regeneration (API-gated).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
README headline now states the measured range with method (65% avg vs
verbose default from benchmarks/, ~50% median vs 'Answer concisely.'
control from evals/) plus explicit caveat: input tokens untouched,
session-level savings smaller. Stats box shows output vs input split,
drops the unmeasured ~3x speed row. Same ~75% claim corrected in
SKILL.md description (+ synced plugin copy and dist/caveman.skill),
plugin.json, marketplace.json, gemini-extension.json, caveman-init
rule frontmatter, skill README, and CLAUDE.md.
Note for maintainer: the GitHub repo description still says 'cuts 65%
of tokens' — should read 'cuts ~50-65% of output tokens (measured)'.
Not changeable from a local commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
detect.py listed .dockerfile/.makefile in SKIP_EXTENSIONS, but real
files are named Dockerfile/Makefile with no extension, so they fell
through to the content heuristic and came back compressible —
/caveman-compress Dockerfile overwrote a Dockerfile with caveman prose.
Add a basename guard (Dockerfile, Makefile, Jenkinsfile, Vagrantfile,
CMakeLists.txt, ...) checked before any extension rule — CMakeLists.txt
would otherwise ride the compressible .txt rule — and a shebang check
in the extensionless branch so executable scripts are always code.
Mirror synced to plugins/caveman (sync workflow only triggers on
SKILL.md changes, so scripts/ must ride along).
Fixes#600
Verbatim copies of edited sources into the CI-managed plugins/caveman mirror and rebuilt dist/caveman.skill, so local main is self-consistent without a push. CI's sync-skill.yml regenerates these on push; this just keeps verify_repo green locally.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brings the long-stashed Node installer onto main. install.sh and install.ps1
shrink to thin shims (~50 lines each) that delegate to bin/install.js, fixing
the cross-platform drift that caused #249-class quoting bugs.
- bin/install.js (850 lines) — unified PROVIDERS-driven installer
- bin/lib/settings.js (221 lines) — JSONC parser + hook validator
(validateHookFields prevents single bad hook from poisoning settings.json)
- tests/installer/{unit.argv,unit.settings,e2e.dryrun}.test.mjs — npm test
now actually runs four real tests (was silently passing 0)
- .agents/skills/cavecrew, .junie/, .kiro/, .roo/ — per-agent skill mirrors
- skills-lock.json — vercel-labs/skills slug pinning
- install.{sh,ps1}.legacy escape hatch dropped (git history is the fallback)
- Minor cavecrew agent description refinements
Closes the gap between docs (already merged) describing bin/install.js and
the actual implementation.
- Move caveman-compress/ source (SKILL.md, scripts/, README.md, SECURITY.md) to skills/caveman-compress/.
- Delete skills/compress/ — the CI-generated rename mirror that caused dual-source confusion.
- Move plugins/caveman/skills/compress/ to plugins/caveman/skills/caveman-compress/. Plugin keeps the consolidated name; CI no longer rewrites the frontmatter.
- Replace the two sed-heavy CI sync steps with one verbatim cp -r from source to plugin.
- Update verify_repo.py, test_compress_safety.py, test_validate_inline.py, GEMINI.md, AGENTS.md, CONTRIBUTING.md, README.md, and the workflow paths to reference the new location.
- Use Path.resolve().parents[N] for the benchmark.py repo-root walk now that the directory depth changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
caveman-shrink@0.1.0 is now live on npm (pre-1.0). Restore the original
default-on behavior for --with-mcp-shrink / -WithMcpShrink. Keep the
`npm view caveman-shrink` probe — a transient registry outage now degrades
to a clean manual-config skip instead of registering a `npx -y caveman-shrink`
entry that would have spawned-failed.
Also: sync `model: haiku` onto cavecrew investigator/reviewer canonicals
so the top-level agents/ matches the synced plugins/caveman/agents/.
README + CLAUDE.md flipped back to "On by default."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
install.sh:
- detect_match: replace `awk -v RS='||'` (rejected by macOS BSD awk:
"illegal primary in regular expression") with bash parameter expansion.
Compound detection specs were silently failing, so cursor / windsurf /
continue / and 28 other compound-spec providers were never detected.
- --with-mcp-shrink: flip default OFF + probe `npm view caveman-shrink`
before registering. Was registering a config that 404s on first spawn.
install.ps1: mirror the MCP-shrink default flip + npm probe.
tests: update statusline tests for default-on suffix behavior. Add a
regression for fresh installs where the suffix file is absent.
Add npm-pkg-fix formatting to package.json.
cavecrew: promote agents/cavecrew-*.md to top-level canonical, refine
subagent contracts (sharper output formats, terminal refusal
lines, model: haiku for read-only roles). CI workflow syncs
agents/ + skills/cavecrew/ into plugins/caveman/.
docs: README & CLAUDE.md align with new opt-in MCP-shrink policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- caveman-stats: dollar math via per-million model pricing, --share
tweetable line, lifetime log via symlink-safe appendFlag, --all and
--since N[d|h] aggregation, opt-in statusline savings suffix, and
detection of *.original.md compress backups for input-side savings.
- install.sh / install.ps1 at repo root: smart multi-agent installer
that detects Claude Code, Gemini, Codex, Cursor, Windsurf, Cline,
Copilot and runs each one's native install. Idempotent, --dry-run,
--only, --force.
- cavecrew: skills/cavecrew + three Claude Code subagents
(investigator / builder / reviewer) for caveman-style machine-to-
machine handoffs.
- cavepack: tools/caveman-init.js drops the always-on caveman rule
into Cursor / Windsurf / Cline / Copilot / AGENTS.md in one shot,
idempotent, with a sentinel check so re-runs never duplicate.
- caveman-shrink: MCP middleware proxy + pure-Node prose compressor
that strips articles/filler/hedging from tools/list descriptions
while preserving code, URLs, paths, and identifiers byte-for-byte.
59 tests passing (27 stats, 8 init, 12 mcp-shrink, 12 symlink-flag).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New slash command that reads the active Claude Code session JSONL
(.claude/projects/**/*.jsonl), sums output_tokens and
cache_read_input_tokens from assistant turns, and shows an estimated
savings figure when the active mode is 'full'.
Real numbers, not the model's guess:
Caveman Stats
──────────────────────────────────
Session: ...projects/my-app/abc123.jsonl
Turns: 47
──────────────────────────────────
Output tokens: 3,210
Cache-read tokens: 128,400
──────────────────────────────────
Est. without caveman: 9,171
Est. tokens saved: 5,961 (~65%)
Savings est. from benchmarks/ (mean per-task). Actual varies by task.
Implementation:
* hooks/caveman-stats.js — script. Run directly with
`node hooks/caveman-stats.js`, or via `--session-file <path>`.
Falls back to most-recent JSONL under .claude/projects/ when no
session file is passed.
* hooks/caveman-mode-tracker.js — `/caveman-stats` triggers an
execFileSync call to caveman-stats.js with the hook's transcript_path,
and the output is returned via `decision: "block"` so the user sees
the stats inline without a model round-trip.
* install.sh / install.ps1 / uninstall.{sh,ps1} include
caveman-stats.js in HOOK_FILES.
* skills/caveman-stats/SKILL.md (+ plugin mirror) for skill listing.
* README install matrix and Caveman Skills section updated.
Compression ratio (0.65) is the mean per-task figure from
benchmarks/results/*.json (avg_savings: 65 across 10 tasks). Only 'full'
mode has measured data — lite/ultra/wenyan show no estimate.
Tests: 6 passing in tests/test_caveman_stats.js covering direct
invocation, full-mode estimate math, non-full skip, no-session error,
mode-tracker block behavior, and flag preservation.
Closes#305 (re-implementation; takes the design from
@DeeptimaanB but rewritten against current main).
Co-Authored-By: Deeptimaan Banerjee <DeeptimaanB@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small SKILL.md edits batched:
* Ultra mode: spell out that abbreviation applies to prose only, never to
code symbols, function names, API names, or error strings (#238 by
@AnthoHansen). Stops models from rendering literal `fn` in code where
the user wrote `function`.
* Auto-clarity (#239 by @AnthoHansen): bullet list replaces run-on
sentence; adds explicit trigger for compression-induced ambiguity in
technical sequences (e.g. "migrate table drop column backup first").
* Typst + LaTeX (#243 by @wildwestrom): add .typ, .typst, .tex to
COMPRESSIBLE_EXTENSIONS in caveman-compress/scripts/detect.py and to
the boundaries lists in SKILL.md / README.md.
Synced SKILL.md to caveman/, plugins/caveman/skills/caveman/,
.cursor/skills/caveman/, .windsurf/skills/caveman/, and rebuilt
caveman.skill ZIP.
Co-Authored-By: Anthony Domínguez <AnthoHansen@users.noreply.github.com>
Co-Authored-By: West <wildwestrom@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five user-contributed fixes consolidated:
* UTF-8 stdout (#289 by @ousamabenyounes) — reconfigure stdout/stderr at the
top of cli.py so Windows cp1252 consoles don't crash on the ❌ glyph in
error/validation branches and mask the real error.
* Empty / unchanged compression guards (#292 by @ousamabenyounes, closes#237)
— refuse empty input, refuse empty/whitespace/identical Claude output,
read back the backup before touching the input. Five regression tests in
tests/test_compress_safety.py.
* Inline backtick validation (#309 by @hireblackout) — validate_inline_codes
closes the silent-overwrite gap where `npm install` → `yarn install`
passed validation. Wired into validate(); 11 unit tests in
tests/test_validate_inline.py.
* Frontmatter angle-bracket fix (#268 by @Bortlesboat, closes#266) —
caveman-compress/SKILL.md description now uses FILEPATH instead of
<filepath>, plus verify_repo gains a new
verify_skill_frontmatter_upload_compatibility check, UTF-8 hardening for
Windows, and the activation-banner regex no longer requires a trailing
period.
* Two test fixtures (claude-md-project.md, mixed-with-code.md) updated so
the new inline-backtick validator passes — they were silently dropping
`server/src/`, `type(scope): description`, and `status` references in
compression. The fixture is documentation of "good" compression, so the
fix is to preserve those references.
Co-Authored-By: Ben Younes <ousama.benyounes@oratelecom.net>
Co-Authored-By: hireblackout <hireblackout@users.noreply.github.com>
Co-Authored-By: Andrew Barnes <Bortlesboat@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Writes were hardened via safeWriteFlag (PRs #70/#71) but readers still
trusted whatever the flag contained. A local attacker with write access
to ~/.claude/ could symlink the flag at a secret file and have the
per-turn reinforcement inject its bytes into model context, or the
statuslines echo ANSI escapes to the terminal on every keystroke.
- caveman-config.js: new readFlag() — lstat symlink refuse, 64-byte cap,
O_NOFOLLOW, VALID_MODES whitelist. Returns null on any anomaly.
- caveman-mode-tracker.js: per-turn reinforcement routes through
readFlag() instead of fs.readFileSync.
- caveman-statusline.sh / .ps1: symlink + size refuse, strip to
[a-z0-9-], whitelist-validate before rendering.
- compress.py (3 synced copies): is_sensitive_path() denylist refuses
.env*, .netrc, keys/certs, ~/.ssh|.aws|.gnupg|.kube|.docker, and any
basename containing secret/credential/password/apikey/token/privatekey
(separator-insensitive). Fails loudly before read — no silent exfil
of credentials to the Anthropic API.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Symlinks in plugins/caveman/skills/compress/ were checked out as plain
text path stubs on Windows (core.symlinks=false), causing Codex to reject
SKILL.md as missing YAML frontmatter. Replace with verbatim copies of
source files and add CI sync step to keep them in sync.
Closes#92
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Separate language translations are unnecessary — Claude already responds
in the user's language. These added maintenance surface without real value.
Wenyan mode is kept as it's a genuinely different compression technique
built into the core skill.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update caveman-compress/SKILL.md name to 'compress' and trigger to '/caveman:compress'
- Create symlinks from compress/ (Claude Code) and plugins/caveman/skills/compress/ (Codex/npx) to caveman-compress/
- Single source of truth: caveman-compress/ holds all scripts and SKILL.md
- Update README to reflect compress as built-in skill
Fixes#33
- Add intensity levels section to README so feature is discoverable
- Document Codex trigger syntax ($caveman lite|full|ultra) in SKILL.md
- Sync plugin copy and rebuild .skill archive
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds three compression levels so users can dial caveman up or down:
- Lite: drops filler/pleasantries, keeps normal grammar
- Full: existing default behavior, unchanged
- Ultra: maximum telegraphic compression with abbreviations
Closes#10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>