🔥 Remove caveman skills
Changes
29 files changed, +0 -1734
DELETE
.agents/skills/cavecrew/README.md
+0 -46
@@ -1,46 +0,0 @@
1
-# cavecrew
2
-
3
-Decision guide. When to delegate to caveman subagents instead of doing the work inline.
4
-
5
-## What it does
6
-
7
-Tells the main thread when to spawn a caveman-style subagent versus the vanilla equivalent. The win: subagent
8
-tool-results inject back into main context verbatim, and caveman output is roughly 1/3 the size of vanilla prose. Across
9
-20 delegations in one session, that is the difference between context exhaustion and finishing the task.
10
-
11
-Three subagents:
12
-
13
-| Subagent | Job | Use when |
14
-|-------------------------|--------------------------|------------------------------------------------------|
15
-| `cavecrew-investigator` | Locate code (read-only) | "Where is X defined / what calls Y / list uses of Z" |
16
-| `cavecrew-builder` | Surgical edit, 1-2 files | Scope is obvious, ≤2 files. Refuses 3+ file scope. |
17
-| `cavecrew-reviewer` | Diff/file review | One-line findings with severity emoji |
18
-
19
-Use vanilla `Explore` or `Code Reviewer` when you want prose, architecture commentary, or rationale. Use main thread
20
-directly for one-line answers and 3+ file refactors.
21
-
22
-This skill is a decision guide, not a slash command. It activates when the conversation mentions delegation.
23
-
24
-## How to invoke
25
-
26
-Triggers on phrases like "delegate to subagent", "use cavecrew", "spawn investigator", "save context", "compressed agent
27
-output".
28
-
29
-## Example chaining
30
-
31
-Locate → fix → verify (most common):
32
-
33
-1. `cavecrew-investigator` returns site list (`path:line — symbol — note`)
34
-2. Main thread picks 1-2 sites, hands paths to `cavecrew-builder`
35
-3. `cavecrew-reviewer` audits the resulting diff
36
-
37
-Parallel scout: spawn 2-3 `cavecrew-investigator` calls in one message with different angles (defs, callers, tests).
38
-Aggregate in main.
39
-
40
-## See also
41
-
42
-- [`SKILL.md`](./SKILL.md) — full decision matrix and output contracts
43
-- [`agents/cavecrew-investigator.md`](../../agents/cavecrew-investigator.md)
44
-- [`agents/cavecrew-builder.md`](../../agents/cavecrew-builder.md)
45
-- [`agents/cavecrew-reviewer.md`](../../agents/cavecrew-reviewer.md)
46
-- [Caveman README](../../README.md) — repo overview
DELETE
.agents/skills/cavecrew/SKILL.md
+0 -97
@@ -1,99 +0,0 @@
1
-name: cavecrew
2
-description: >
3
- Decision guide for delegating to caveman-style subagents. Tells the main
4
- thread WHEN to spawn `cavecrew-investigator` (locate code), `cavecrew-builder`
5
- (1-2 file edit), or `cavecrew-reviewer` (diff review) instead of doing the
6
- work inline or using vanilla `Explore`. Subagent output is caveman-compressed
7
- so the tool-result injected back into main context is ~60% smaller — main
8
- context lasts longer across long sessions.
9
- Trigger: "delegate to subagent", "use cavecrew", "spawn investigator/builder/reviewer",
10
- "save context", "compressed agent output".
11
-
12
-Cavecrew = three subagent presets that emit caveman output. Same job as Anthropic defaults (`Explore`, edit-style
13
-agents, reviewer); difference is the tool-result they return is compressed, so main context shrinks per delegation.
14
-
15
-## When to use cavecrew vs alternatives
16
-
17
-| Task | Use |
18
-|------------------------------------------------------------|---------------------------------------------|
19
-| "Where is X defined / what calls Y / list uses of Z" | `cavecrew-investigator` |
20
-| Same but you also want suggestions/architecture commentary | `Explore` (vanilla) |
21
-| Surgical edit, ≤2 files, scope obvious | `cavecrew-builder` |
22
-| New feature / 3+ files / cross-cutting refactor | Main thread or `feature-dev:code-architect` |
23
-| Review diff, branch, or file for bugs | `cavecrew-reviewer` |
24
-| Deep code review with rationale + alternatives | `Code Reviewer` (vanilla) |
25
-| One-line answer you already know | Main thread, no subagent |
26
-
27
-Rule of thumb: **if you'd want the subagent's output in 1/3 the tokens, pick cavecrew. If you'd want prose, pick
28
-vanilla.**
29
-
30
-## Why this exists (the real win)
31
-
32
-Subagent tool results get injected into main context verbatim. A vanilla `Explore` that returns 2k tokens of prose costs
33
-2k tokens of main-context budget every time. The same finding from `cavecrew-investigator` returns ~700 tokens. Across
34
-20 delegations in one session that's the difference between context exhaustion and finishing the task.
35
-
36
-## Output contracts
37
-
38
-What main thread can rely on per agent:
39
-
40
-**`cavecrew-investigator`**
41
-
42
-```
43
-<Header>:
44
-- path:line — `symbol` — short note
45
-totals: <counts>.
46
-```
47
-
48
-Or `No match.` Always file-path-first, line-number-attached, backticked symbols. Safe to grep with `path:\d+`.
49
-
50
-**`cavecrew-builder`**
51
-
52
-```
53
-<path:line-range> — <change ≤10 words>.
54
-verified: <re-read OK | mismatch @ path:line>.
55
-```
56
-
57
-Or one of: `too-big.` / `needs-confirm.` / `ambiguous.` / `regressed.` (terminal first token).
58
-
59
-**`cavecrew-reviewer`**
60
-
61
-```
62
-path:line: <emoji> <severity>: <problem>. <fix>.
63
-totals: N🔴 N🟡 N🔵 N❓
64
-```
65
-
66
-Or `No issues.` Findings sorted file → line ascending.
67
-
68
-## Chaining patterns
69
-
70
-**Locate → fix → verify** (most common):
71
-
72
-1. `cavecrew-investigator` returns site list.
73
-2. Main thread picks 1-2 sites, hands paths to `cavecrew-builder`.
74
-3. `cavecrew-reviewer` audits the diff.
75
-
76
-**Parallel scout** (when investigation is broad):
77
-Spawn 2-3 `cavecrew-investigator` calls in one message (different angles: defs vs callers vs tests). Aggregate in main
78
-thread.
79
-
80
-**Single-shot edit** (when site is already known):
81
-Skip investigator. Hand exact path:line to `cavecrew-builder` directly.
82
-
83
-## What NOT to do
84
-
85
-- Don't use `cavecrew-builder` when you don't already know the file. Spawn investigator first or main thread will eat
86
- tokens passing context.
87
-- Don't chain `cavecrew-investigator → cavecrew-builder` for a 5-file refactor. Builder will return `too-big.` and
88
- you'll have wasted a turn.
89
-- Don't ask `cavecrew-reviewer` for "general feedback" — it returns findings only, no architecture opinions. Use
90
- `Code Reviewer` for that.
91
-- Don't expect prose. Cavecrew output is structured, sometimes terse to the point of cryptic. If a human will read it
92
- directly, paraphrase.
93
-
94
-## Auto-clarity (inherited)
95
-
96
-Subagents drop caveman → normal English for security warnings, irreversible-action confirmations, and any output where
97
-fragment ambiguity could be misread. Resume caveman after.
DELETE
.agents/skills/caveman-commit/README.md
+0 -47
@@ -1,47 +0,0 @@
1
-# caveman-commit
2
-
3
-Terse Conventional Commits. Why over what.
4
-
5
-## What it does
6
-
7
-Generates commit messages in Conventional Commits format. Subject ≤50 chars, hard cap 72. Imperative mood. Body only
8
-when the *why* is non-obvious or there are breaking changes. No AI attribution, no "this commit does X", no emoji unless
9
-the project uses them. Body always required for breaking changes, security fixes, data migrations, and reverts — future
10
-debuggers need the context.
11
-
12
-Outputs only the message. Does not stage, commit, or amend.
13
-
14
-## How to invoke
15
-
16
-```
17
-/caveman-commit
18
-```
19
-
20
-Also triggers on phrases like "write a commit", "commit message", "generate commit".
21
-
22
-## Example output
23
-
24
-Diff: new endpoint for user profile.
25
-
26
-```
27
-feat(api): add GET /users/:id/profile
28
-
29
-Mobile client needs profile data without the full user payload
30
-to reduce LTE bandwidth on cold-launch screens.
31
-
32
-Closes #128
33
-```
34
-
35
-Diff: breaking API rename.
36
-
37
-```
38
-feat(api)!: rename /v1/orders to /v1/checkout
39
-
40
-BREAKING CHANGE: clients on /v1/orders must migrate to /v1/checkout
41
-before 2026-06-01. Old route returns 410 after that date.
42
-```
43
-
44
-## See also
45
-
46
-- [`SKILL.md`](./SKILL.md) — full LLM-facing instructions
47
-- [Caveman README](../../README.md) — repo overview
DELETE
.agents/skills/caveman-commit/SKILL.md
+0 -70
@@ -1,72 +0,0 @@
1
-name: caveman-commit
2
-description: >
3
- Ultra-compressed commit message generator. Cuts noise from commit messages while preserving
4
- intent and reasoning. Conventional Commits format. Subject ≤50 chars, body only when "why"
5
- isn't obvious. Use when user says "write a commit", "commit message", "generate commit",
6
- "/commit", or invokes /caveman-commit. Auto-triggers when staging changes.
7
-
8
-Write commit messages terse and exact. Conventional Commits format. No fluff. Why over what.
9
-
10
-## Rules
11
-
12
-**Subject line:**
13
-
14
-- `<type>(<scope>): <imperative summary>` — `<scope>` optional
15
-- Types: `feat`, `fix`, `refactor`, `perf`, `docs`, `test`, `chore`, `build`, `ci`, `style`, `revert`
16
-- Imperative mood: "add", "fix", "remove" — not "added", "adds", "adding"
17
-- ≤50 chars when possible, hard cap 72
18
-- No trailing period
19
-- Match project convention for capitalization after the colon
20
-
21
-**Body (only if needed):**
22
-
23
-- Skip entirely when subject is self-explanatory
24
-- Add body only for: non-obvious *why*, breaking changes, migration notes, linked issues
25
-- Wrap at 72 chars
26
-- Bullets `-` not `*`
27
-- Reference issues/PRs at end: `Closes #42`, `Refs #17`
28
-
29
-**What NEVER goes in:**
30
-
31
-- "This commit does X", "I", "we", "now", "currently" — the diff says what
32
-- "As requested by..." — use Co-authored-by trailer
33
-- "Generated with Claude Code" or any AI attribution
34
-- Emoji (unless project convention requires)
35
-- Restating the file name when scope already says it
36
-
37
-## Examples
38
-
39
-Diff: new endpoint for user profile with body explaining the why
40
-
41
-- ❌ "feat: add a new endpoint to get user profile information from the database"
42
-- ✅
43
- ```
44
- feat(api): add GET /users/:id/profile
45
-
46
- Mobile client needs profile data without the full user payload
47
- to reduce LTE bandwidth on cold-launch screens.
48
-
49
- Closes #128
50
- ```
51
-
52
-Diff: breaking API change
53
-
54
-- ✅
55
- ```
56
- feat(api)!: rename /v1/orders to /v1/checkout
57
-
58
- BREAKING CHANGE: clients on /v1/orders must migrate to /v1/checkout
59
- before 2026-06-01. Old route returns 410 after that date.
60
- ```
61
-
62
-## Auto-Clarity
63
-
64
-Always include body for: breaking changes, security fixes, data migrations, anything reverting a prior commit. Never
65
-compress these into subject-only — future debuggers need the context.
66
-
67
-## Boundaries
68
-
69
-Only generates the commit message. Does not run `git commit`, does not stage files, does not amend. Output the message
70
-as a code block ready to paste. "stop caveman-commit" or "normal mode": revert to verbose commit style.
DELETE
.agents/skills/caveman-compress/README.md
+0 -171
@@ -1,172 +0,0 @@
1
-<p align="center">
2
- <img src="https://em-content.zobj.net/source/apple/391/rock_1faa8.png" width="80" />
3
-</p>
4
-
5
-<h1 align="center">caveman-compress</h1>
6
-
7
-<p align="center">
8
- <strong>shrink memory file. save token every session.</strong>
9
-</p>
10
-
11
-
12
-A Claude Code skill that compresses your project memory files (`CLAUDE.md`, todos, preferences) into caveman format — so
13
-every session loads fewer tokens automatically.
14
-
15
-Claude read `CLAUDE.md` on every session start. If file big, cost big. Caveman make file small. Cost go down forever.
16
-
17
-## What It Do
18
-
19
-```
20
-/caveman-compress CLAUDE.md
21
-```
22
-
23
-```
24
-CLAUDE.md ← compressed (Claude reads this — fewer tokens every session)
25
-CLAUDE.original.md ← human-readable backup (you edit this)
26
-```
27
-
28
-Original never lost. You can read and edit `.original.md`. Run skill again to re-compress after edits.
29
-
30
-## Benchmarks
31
-
32
-Real results on real project files:
33
-
34
-| File | Original | Compressed | Saved |
35
-|----------------------------|---------:|-----------:|----------:|
36
-| `claude-md-preferences.md` | 706 | 285 | **59.6%** |
37
-| `project-notes.md` | 1145 | 535 | **53.3%** |
38
-| `claude-md-project.md` | 1122 | 636 | **43.3%** |
39
-| `todo-list.md` | 627 | 388 | **38.1%** |
40
-| `mixed-with-code.md` | 888 | 560 | **36.9%** |
41
-| **Average** | **898** | **481** | **46%** |
42
-
43
-All validations passed ✅ — headings, code blocks, URLs, file paths preserved exactly.
44
-
45
-## Before / After
46
-
47
-<table>
48
-<tr>
49
-<td width="50%">
50
-
51
-### 📄 Original (706 tokens)
52
-
53
-> "I strongly prefer TypeScript with strict mode enabled for all new code. Please don't use `any` type unless there's
54
-> genuinely no way around it, and if you do, leave a comment explaining the reasoning. I find that taking the time to
55
-> properly type things catches a lot of bugs before they ever make it to runtime."
56
-
57
-</td>
58
-<td width="50%">
59
-
60
-### <img src="../../docs/assets/dancing-rock.svg" width="20" height="20" alt="rock"/> Caveman (285 tokens)
61
-
62
-> "Prefer TypeScript strict mode always. No `any` unless unavoidable — comment why if used. Proper types catch bugs
63
-> early."
64
-
65
-</td>
66
-</tr>
67
-</table>
68
-
69
-**Same instructions. 60% fewer tokens. Every. Single. Session.**
70
-
71
-## Security
72
-
73
-`caveman-compress` is flagged as Snyk High Risk due to subprocess and file I/O patterns detected by static analysis.
74
-This is a false positive — see [SECURITY.md](./SECURITY.md) for a full explanation of what the skill does and does not
75
-do.
76
-
77
-## Install
78
-
79
-Compress is built in with the `caveman` plugin. Install `caveman` once, then use `/caveman-compress`.
80
-
81
-If you need local files, the compress skill lives at:
82
-
83
-```bash
84
-caveman-compress/
85
-```
86
-
87
-**Requires:** Python 3.10+
88
-
89
-## Usage
90
-
91
-```
92
-/caveman-compress <filepath>
93
-```
94
-
95
-Examples:
96
-
97
-```
98
-/caveman-compress CLAUDE.md
99
-/caveman-compress docs/preferences.md
100
-/caveman-compress todos.md
101
-```
102
-
103
-### What files work
104
-
105
-| Type | Compress? |
106
-|-------------------------------------------------|-----------------------|
107
-| `.md`, `.txt`, `.rst`, `.typ`, `.typst`, `.tex` | ✅ Yes |
108
-| Extensionless natural language | ✅ Yes |
109
-| `.py`, `.js`, `.ts`, `.json`, `.yaml` | ❌ Skip (code/config) |
110
-| `*.original.md` | ❌ Skip (backup files) |
111
-
112
-## How It Work
113
-
114
-```
115
-/caveman-compress CLAUDE.md
116
- ↓
117
-detect file type (no tokens)
118
- ↓
119
-Claude compresses (tokens — one call)
120
- ↓
121
-validate output (no tokens)
122
- checks: headings, code blocks, URLs, file paths, bullets
123
- ↓
124
-if errors: Claude fixes cherry-picked issues only (tokens — targeted fix)
125
- does NOT recompress — only patches broken parts
126
- ↓
127
-retry up to 2 times
128
- ↓
129
-write compressed → CLAUDE.md
130
-write original → CLAUDE.original.md
131
-```
132
-
133
-Only two things use tokens: initial compression + targeted fix if validation fails. Everything else is local Python.
134
-
135
-## What Is Preserved
136
-
137
-Caveman compress natural language. It never touch:
138
-
139
-- Code blocks (` ``` ` fenced or indented)
140
-- Inline code (`` `backtick content` ``)
141
-- URLs and links
142
-- File paths (`/src/components/...`)
143
-- Commands (`npm install`, `git commit`)
144
-- Technical terms, library names, API names
145
-- Headings (exact text preserved)
146
-- Tables (structure preserved, cell text compressed)
147
-- Dates, version numbers, numeric values
148
-
149
-## Why This Matter
150
-
151
-`CLAUDE.md` loads on **every session start**. A 1000-token project memory file costs tokens every single time you open a
152
-project. Over 100 sessions that's 100,000 tokens of overhead — just for context you already wrote.
153
-
154
-Caveman cut that by ~46% on average. Same instructions. Same accuracy. Less waste.
155
-
156
-```
157
-┌────────────────────────────────────────────┐
158
-│ TOKEN SAVINGS PER FILE █████ 46% │
159
-│ SESSIONS THAT BENEFIT ██████████ 100% │
160
-│ INFORMATION PRESERVED ██████████ 100% │
161
-│ SETUP TIME █ 1x │
162
-└────────────────────────────────────────────┘
163
-```
164
-
165
-## Part of Caveman
166
-
167
-This skill is part of the [caveman](https://github.com/JuliusBrussee/caveman) toolkit — making Claude use fewer tokens
168
-without losing accuracy.
169
-
170
-- **caveman** — make Claude *speak* like caveman (cuts response tokens ~65%)
171
-- **caveman-compress** — make Claude *read* less (cuts context tokens ~46%)
DELETE
.agents/skills/caveman-compress/SECURITY.md
+0 -37
@@ -1,37 +0,0 @@
1
-# Security
2
-
3
-## Snyk High Risk Rating
4
-
5
-`caveman-compress` receives a Snyk High Risk rating due to static analysis heuristics. This document explains what the
6
-skill does and does not do.
7
-
8
-### What triggers the rating
9
-
10
-1. **subprocess usage**: The skill calls the `claude` CLI via `subprocess.run()` as a fallback when `ANTHROPIC_API_KEY`
11
- is not set. The subprocess call uses a fixed argument list — no shell interpolation occurs. User file content is
12
- passed via stdin, not as a shell argument.
13
-
14
-2. **File read/write**: The skill reads the file the user explicitly points it at, compresses it, and writes the result
15
- back to the same path. A `.original.md` backup is saved alongside it. No files outside the user-specified path are
16
- read or written.
17
-
18
-### What the skill does NOT do
19
-
20
-- Does not execute user file content as code
21
-- Does not make network requests except to Anthropic's API (via SDK or CLI)
22
-- Does not access files outside the path the user provides
23
-- Does not use shell=True or string interpolation in subprocess calls
24
-- Does not collect or transmit any data beyond the file being compressed
25
-
26
-### Auth behavior
27
-
28
-If `ANTHROPIC_API_KEY` is set, the skill uses the Anthropic Python SDK directly (no subprocess). If not set, it falls
29
-back to the `claude` CLI, which uses the user's existing Claude desktop authentication.
30
-
31
-### File size limit
32
-
33
-Files larger than 500KB are rejected before any API call is made.
34
-
35
-### Reporting a vulnerability
36
-
37
-If you believe you've found a genuine security issue, please open a GitHub issue with the label `security`.
DELETE
.agents/skills/caveman-compress/SKILL.md
+0 -122
@@ -1,124 +0,0 @@
1
-name: caveman-compress
2
-description: >
3
- Compress natural language memory files (CLAUDE.md, todos, preferences) into caveman format
4
- to save input tokens. Preserves all technical substance, code, URLs, and structure.
5
- Compressed version overwrites the original file. Human-readable backup saved as FILE.original.md.
6
- Trigger: /caveman-compress FILEPATH or "compress memory file"
7
-
8
-# Caveman Compress
9
-
10
-## Purpose
11
-
12
-Compress natural language files (CLAUDE.md, todos, preferences) into caveman-speak to reduce input tokens. Compressed
13
-version overwrites original. Human-readable backup saved as `<filename>.original.md`.
14
-
15
-## Trigger
16
-
17
-`/caveman-compress <filepath>` or when user asks to compress a memory file.
18
-
19
-## Process
20
-
21
-1. The compression scripts live in `scripts/` (adjacent to this SKILL.md). If the path is not immediately available,
22
- search for `scripts/__main__.py` next to this SKILL.md.
23
-
24
-2. From the directory containing this SKILL.md, run:
25
-
26
-python3 -m scripts <absolute_filepath>
27
-
28
-3. The CLI will:
29
-
30
-- detect file type (no tokens)
31
-- call Claude to compress
32
-- validate output (no tokens)
33
-- if errors: cherry-pick fix with Claude (targeted fixes only, no recompression)
34
-- retry up to 2 times
35
-- if still failing after 2 retries: report error to user, leave original file untouched
36
-
37
-4. Return result to user
38
-
39
-## Compression Rules
40
-
41
-### Remove
42
-
43
-- Articles: a, an, the
44
-- Filler: just, really, basically, actually, simply, essentially, generally
45
-- Pleasantries: "sure", "certainly", "of course", "happy to", "I'd recommend"
46
-- Hedging: "it might be worth", "you could consider", "it would be good to"
47
-- Redundant phrasing: "in order to" → "to", "make sure to" → "ensure", "the reason is because" → "because"
48
-- Connective fluff: "however", "furthermore", "additionally", "in addition"
49
-
50
-### Preserve EXACTLY (never modify)
51
-
52
-- Code blocks (fenced ``` and indented)
53
-- Inline code (`backtick content`)
54
-- URLs and links (full URLs, markdown links)
55
-- File paths (`/src/components/...`, `./config.yaml`)
56
-- Commands (`npm install`, `git commit`, `docker build`)
57
-- Technical terms (library names, API names, protocols, algorithms)
58
-- Proper nouns (project names, people, companies)
59
-- Dates, version numbers, numeric values
60
-- Environment variables (`$HOME`, `NODE_ENV`)
61
-
62
-### Preserve Structure
63
-
64
-- All markdown headings (keep exact heading text, compress body below)
65
-- Bullet point hierarchy (keep nesting level)
66
-- Numbered lists (keep numbering)
67
-- Tables (compress cell text, keep structure)
68
-- Frontmatter/YAML headers in markdown files
69
-
70
-### Compress
71
-
72
-- Use short synonyms: "big" not "extensive", "fix" not "implement a solution for", "use" not "utilize"
73
-- Fragments OK: "Run tests before commit" not "You should always run tests before committing"
74
-- Drop "you should", "make sure to", "remember to" — just state the action
75
-- Merge redundant bullets that say the same thing differently
76
-- Keep one example where multiple examples show the same pattern
77
-
78
-CRITICAL RULE:
79
-Anything inside ``` ... ``` must be copied EXACTLY.
80
-Do not:
81
-
82
-- remove comments
83
-- remove spacing
84
-- reorder lines
85
-- shorten commands
86
-- simplify anything
87
-
88
-Inline code (`...`) must be preserved EXACTLY.
89
-Do not modify anything inside backticks.
90
-
91
-If file contains code blocks:
92
-
93
-- Treat code blocks as read-only regions
94
-- Only compress text outside them
95
-- Do not merge sections around code
96
-
97
-## Pattern
98
-
99
-Original:
100
-> You should always make sure to run the test suite before pushing any changes to the main branch. This is important
101
-> because it helps catch bugs early and prevents broken builds from being deployed to production.
102
-
103
-Compressed:
104
-> Run tests before push to main. Catch bugs early, prevent broken prod deploys.
105
-
106
-Original:
107
-> The application uses a microservices architecture with the following components. The API gateway handles all incoming
108
-> requests and routes them to the appropriate service. The authentication service is responsible for managing user
109
-> sessions and JWT tokens.
110
-
111
-Compressed:
112
-> Microservices architecture. API gateway route all requests to services. Auth service manage user sessions + JWT
113
-> tokens.
114
-
115
-## Boundaries
116
-
117
-- ONLY compress natural language files (.md, .txt, .typ, .typst, .tex, extensionless)
118
-- NEVER modify: .py, .js, .ts, .json, .yaml, .yml, .toml, .env, .lock, .css, .html, .xml, .sql, .sh
119
-- If file has mixed content (prose + code), compress ONLY the prose sections
120
-- If unsure whether something is code or prose, leave it unchanged
121
-- Original file is backed up as FILE.original.md before overwriting
122
-- Never compress FILE.original.md (skip it)
DELETE
.agents/skills/caveman-compress/scripts/__init__.py
+0 -9
@@ -1,9 +0,0 @@
1
-"""Caveman compress scripts.
2
-
3
-This package provides tools to compress natural language markdown files
4
-into caveman format to save input tokens.
5
-"""
6
-
7
-__all__ = ["cli", "compress", "detect", "validate"]
8
-
9
-__version__ = "1.0.0"
DELETE
.agents/skills/caveman-compress/scripts/__main__.py
+0 -3
@@ -1,3 +0,0 @@
1
-from .cli import main
2
-
3
-main()
DELETE
.agents/skills/caveman-compress/scripts/benchmark.py
+0 -80
@@ -1,80 +0,0 @@
1
-#!/usr/bin/env python3
2
-from pathlib import Path
3
-import sys
4
-
5
-# Support both direct execution and module import
6
-try:
7
- from .validate import validate
8
-except ImportError:
9
- sys.path.insert(0, str(Path(__file__).parent))
10
- from validate import validate
11
-
12
-try:
13
- import tiktoken
14
- _enc = tiktoken.get_encoding("o200k_base")
15
-except ImportError:
16
- _enc = None
17
-
18
-
19
-def count_tokens(text):
20
- if _enc is None:
21
- return len(text.split()) # fallback: word count
22
- return len(_enc.encode(text))
23
-
24
-
25
-def benchmark_pair(orig_path: Path, comp_path: Path):
26
- orig_text = orig_path.read_text()
27
- comp_text = comp_path.read_text()
28
-
29
- orig_tokens = count_tokens(orig_text)
30
- comp_tokens = count_tokens(comp_text)
31
- saved = 100 * (orig_tokens - comp_tokens) / orig_tokens if orig_tokens > 0 else 0.0
32
- result = validate(orig_path, comp_path)
33
-
34
- return (comp_path.name, orig_tokens, comp_tokens, saved, result.is_valid)
35
-
36
-
37
-def print_table(rows):
38
- print("\n| File | Original | Compressed | Saved % | Valid |")
39
- print("|------|----------|------------|---------|-------|")
40
- for r in rows:
41
- print(f"| {r[0]} | {r[1]} | {r[2]} | {r[3]:.1f}% | {'✅' if r[4] else '❌'} |")
42
-
43
-
44
-def main():
45
- # Direct file pair: python3 benchmark.py original.md compressed.md
46
- if len(sys.argv) == 3:
47
- orig = Path(sys.argv[1]).resolve()
48
- comp = Path(sys.argv[2]).resolve()
49
- if not orig.exists():
50
- print(f"❌ Not found: {orig}")
51
- sys.exit(1)
52
- if not comp.exists():
53
- print(f"❌ Not found: {comp}")
54
- sys.exit(1)
55
- print_table([benchmark_pair(orig, comp)])
56
- return
57
-
58
- # Glob mode: repo_root/tests/caveman-compress/
59
- # __file__ lives at <repo_root>/skills/caveman-compress/scripts/benchmark.py
60
- # Walk up four dirs: scripts → caveman-compress → skills → repo_root.
61
- tests_dir = Path(__file__).resolve().parents[3] / "tests" / "caveman-compress"
62
- if not tests_dir.exists():
63
- print(f"❌ Tests dir not found: {tests_dir}")
64
- sys.exit(1)
65
-
66
- rows = []
67
- for orig in sorted(tests_dir.glob("*.original.md")):
68
- comp = orig.with_name(orig.stem.removesuffix(".original") + ".md")
69
- if comp.exists():
70
- rows.append(benchmark_pair(orig, comp))
71
-
72
- if not rows:
73
- print("No compressed file pairs found.")
74
- return
75
-
76
- print_table(rows)
77
-
78
-
79
-if __name__ == "__main__":
80
- main()
DELETE
.agents/skills/caveman-compress/scripts/cli.py
+0 -85
@@ -1,85 +0,0 @@
1
-#!/usr/bin/env python3
2
-"""
3
-Caveman Compress CLI
4
-
5
-Usage:
6
- caveman <filepath>
7
-"""
8
-
9
-import sys
10
-
11
-# Force UTF-8 on stdout/stderr before any code can print. Windows consoles
12
-# default to cp1252 and crash on the ❌ glyphs in error/validation branches,
13
-# masking the real error and leaving the user with a half-compressed file.
14
-for _stream in (sys.stdout, sys.stderr):
15
- reconfigure = getattr(_stream, "reconfigure", None)
16
- if callable(reconfigure):
17
- try:
18
- reconfigure(encoding="utf-8", errors="replace")
19
- except Exception:
20
- pass
21
-
22
-from pathlib import Path
23
-
24
-from .compress import compress_file
25
-from .detect import detect_file_type, should_compress
26
-
27
-
28
-def print_usage():
29
- print("Usage: caveman <filepath>")
30
-
31
-
32
-def main():
33
- if len(sys.argv) != 2:
34
- print_usage()
35
- sys.exit(1)
36
-
37
- filepath = Path(sys.argv[1])
38
-
39
- # Check file exists
40
- if not filepath.exists():
41
- print(f"❌ File not found: {filepath}")
42
- sys.exit(1)
43
-
44
- if not filepath.is_file():
45
- print(f"❌ Not a file: {filepath}")
46
- sys.exit(1)
47
-
48
- filepath = filepath.resolve()
49
-
50
- # Detect file type
51
- file_type = detect_file_type(filepath)
52
-
53
- print(f"Detected: {file_type}")
54
-
55
- # Check if compressible
56
- if not should_compress(filepath):
57
- print("Skipping: file is not natural language (code/config)")
58
- sys.exit(0)
59
-
60
- print("Starting caveman compression...\n")
61
-
62
- try:
63
- success = compress_file(filepath)
64
-
65
- if success:
66
- print("\nCompression completed successfully")
67
- backup_path = filepath.with_name(filepath.stem + ".original.md")
68
- print(f"Compressed: {filepath}")
69
- print(f"Original: {backup_path}")
70
- sys.exit(0)
71
- else:
72
- print("\n❌ Compression failed after retries")
73
- sys.exit(2)
74
-
75
- except KeyboardInterrupt:
76
- print("\nInterrupted by user")
77
- sys.exit(130)
78
-
79
- except Exception as e:
80
- print(f"\n❌ Error: {e}")
81
- sys.exit(1)
82
-
83
-
84
-if __name__ == "__main__":
85
- main()
DELETE
.agents/skills/caveman-compress/scripts/compress.py
+0 -254
@@ -1,254 +0,0 @@
1
-#!/usr/bin/env python3
2
-"""
3
-Caveman Memory Compression Orchestrator
4
-
5
-Usage:
6
- python scripts/compress.py <filepath>
7
-"""
8
-
9
-import os
10
-import re
11
-import subprocess
12
-from pathlib import Path
13
-from typing import List
14
-
15
-OUTER_FENCE_REGEX = re.compile(
16
- r"\A\s*(`{3,}|~{3,})[^\n]*\n(.*)\n\1\s*\Z", re.DOTALL
17
-)
18
-
19
-# Filenames and paths that almost certainly hold secrets or PII. Compressing
20
-# them ships raw bytes to the Anthropic API — a third-party data boundary that
21
-# developers on sensitive codebases cannot cross. detect.py already skips .env
22
-# by extension, but credentials.md / secrets.txt / ~/.aws/credentials would
23
-# slip through the natural-language filter. This is a hard refuse before read.
24
-SENSITIVE_BASENAME_REGEX = re.compile(
25
- r"(?ix)^("
26
- r"\.env(\..+)?"
27
- r"|\.netrc"
28
- r"|credentials(\..+)?"
29
- r"|secrets?(\..+)?"
30
- r"|passwords?(\..+)?"
31
- r"|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?"
32
- r"|authorized_keys"
33
- r"|known_hosts"
34
- r"|.*\.(pem|key|p12|pfx|crt|cer|jks|keystore|asc|gpg)"
35
- r")$"
36
-)
37
-
38
-SENSITIVE_PATH_COMPONENTS = frozenset({".ssh", ".aws", ".gnupg", ".kube", ".docker"})
39
-
40
-SENSITIVE_NAME_TOKENS = (
41
- "secret", "credential", "password", "passwd",
42
- "apikey", "accesskey", "token", "privatekey",
43
-)
44
-
45
-
46
-def is_sensitive_path(filepath: Path) -> bool:
47
- """Heuristic denylist for files that must never be shipped to a third-party API."""
48
- name = filepath.name
49
- if SENSITIVE_BASENAME_REGEX.match(name):
50
- return True
51
- lowered_parts = {p.lower() for p in filepath.parts}
52
- if lowered_parts & SENSITIVE_PATH_COMPONENTS:
53
- return True
54
- # Normalize separators so "api-key" and "api_key" both match "apikey".
55
- lower = re.sub(r"[_\-\s.]", "", name.lower())
56
- return any(tok in lower for tok in SENSITIVE_NAME_TOKENS)
57
-
58
-
59
-def strip_llm_wrapper(text: str) -> str:
60
- """Strip outer ```markdown ... ``` fence when it wraps the entire output."""
61
- m = OUTER_FENCE_REGEX.match(text)
62
- if m:
63
- return m.group(2)
64
- return text
65
-
66
-from .detect import should_compress
67
-from .validate import validate
68
-
69
-MAX_RETRIES = 2
70
-
71
-
72
-# ---------- Claude Calls ----------
73
-
74
-
75
-def call_claude(prompt: str) -> str:
76
- api_key = os.environ.get("ANTHROPIC_API_KEY")
77
- if api_key:
78
- try:
79
- import anthropic
80
-
81
- client = anthropic.Anthropic(api_key=api_key)
82
- msg = client.messages.create(
83
- model=os.environ.get("CAVEMAN_MODEL", "claude-sonnet-4-5"),
84
- max_tokens=8192,
85
- messages=[{"role": "user", "content": prompt}],
86
- )
87
- return strip_llm_wrapper(msg.content[0].text.strip())
88
- except ImportError:
89
- pass # anthropic not installed, fall back to CLI
90
- # Fallback: use claude CLI (handles desktop auth)
91
- try:
92
- result = subprocess.run(
93
- ["claude", "--print"],
94
- input=prompt,
95
- text=True,
96
- capture_output=True,
97
- check=True,
98
- )
99
- return strip_llm_wrapper(result.stdout.strip())
100
- except subprocess.CalledProcessError as e:
101
- raise RuntimeError(f"Claude call failed:\n{e.stderr}")
102
-
103
-
104
-def build_compress_prompt(original: str) -> str:
105
- return f"""
106
-Compress this markdown into caveman format.
107
-
108
-STRICT RULES:
109
-- Do NOT modify anything inside ``` code blocks
110
-- Do NOT modify anything inside inline backticks
111
-- Preserve ALL URLs exactly
112
-- Preserve ALL headings exactly
113
-- Preserve file paths and commands
114
-- Return ONLY the compressed markdown body — do NOT wrap the entire output in a ```markdown fence or any other fence. Inner code blocks from the original stay as-is; do not add a new outer fence around the whole file.
115
-
116
-Only compress natural language.
117
-
118
-TEXT:
119
-{original}
120
-"""
121
-
122
-
123
-def build_fix_prompt(original: str, compressed: str, errors: List[str]) -> str:
124
- errors_str = "\n".join(f"- {e}" for e in errors)
125
- return f"""You are fixing a caveman-compressed markdown file. Specific validation errors were found.
126
-
127
-CRITICAL RULES:
128
-- DO NOT recompress or rephrase the file
129
-- ONLY fix the listed errors — leave everything else exactly as-is
130
-- The ORIGINAL is provided as reference only (to restore missing content)
131
-- Preserve caveman style in all untouched sections
132
-
133
-ERRORS TO FIX:
134
-{errors_str}
135
-
136
-HOW TO FIX:
137
-- Missing URL: find it in ORIGINAL, restore it exactly where it belongs in COMPRESSED
138
-- Code block mismatch: find the exact code block in ORIGINAL, restore it in COMPRESSED
139
-- Heading mismatch: restore the exact heading text from ORIGINAL into COMPRESSED
140
-- Do not touch any section not mentioned in the errors
141
-
142
-ORIGINAL (reference only):
143
-{original}
144
-
145
-COMPRESSED (fix this):
146
-{compressed}
147
-
148
-Return ONLY the fixed compressed file. No explanation.
149
-"""
150
-
151
-
152
-# ---------- Core Logic ----------
153
-
154
-
155
-def compress_file(filepath: Path) -> bool:
156
- # Resolve and validate path
157
- filepath = filepath.resolve()
158
- MAX_FILE_SIZE = 500_000 # 500KB
159
- if not filepath.exists():
160
- raise FileNotFoundError(f"File not found: {filepath}")
161
- if filepath.stat().st_size > MAX_FILE_SIZE:
162
- raise ValueError(f"File too large to compress safely (max 500KB): {filepath}")
163
-
164
- # Refuse files that look like they contain secrets or PII. Compressing ships
165
- # the raw bytes to the Anthropic API — a third-party boundary — so we fail
166
- # loudly rather than silently exfiltrate credentials or keys. Override is
167
- # intentional: the user must rename the file if the heuristic is wrong.
168
- if is_sensitive_path(filepath):
169
- raise ValueError(
170
- f"Refusing to compress {filepath}: filename looks sensitive "
171
- "(credentials, keys, secrets, or known private paths). "
172
- "Compression sends file contents to the Anthropic API. "
173
- "Rename the file if this is a false positive."
174
- )
175
-
176
- print(f"Processing: {filepath}")
177
-
178
- if not should_compress(filepath):
179
- print("Skipping (not natural language)")
180
- return False
181
-
182
- original_text = filepath.read_text(errors="ignore")
183
- backup_path = filepath.with_name(filepath.stem + ".original.md")
184
-
185
- if not original_text.strip():
186
- print("❌ Refusing to compress: file is empty or whitespace-only.")
187
- return False
188
-
189
- # Check if backup already exists to prevent accidental overwriting
190
- if backup_path.exists():
191
- print(f"⚠️ Backup file already exists: {backup_path}")
192
- print("The original backup may contain important content.")
193
- print("Aborting to prevent data loss. Please remove or rename the backup file if you want to proceed.")
194
- return False
195
-
196
- # Step 1: Compress
197
- print("Compressing with Claude...")
198
- compressed = call_claude(build_compress_prompt(original_text))
199
-
200
- if compressed is None or not compressed.strip():
201
- print("❌ Compression aborted: Claude returned an empty response.")
202
- print(" Original file is untouched (no backup created).")
203
- return False
204
-
205
- if compressed.strip() == original_text.strip():
206
- print("❌ Compression aborted: output is identical to input.")
207
- print(" Likely causes: Claude refused, returned the prompt verbatim, or the file is")
208
- print(" already in caveman form. Original file is untouched (no backup created).")
209
- return False
210
-
211
- # Save original as backup, then verify the backup readback before
212
- # touching the input file. If the filesystem dropped bytes (encoding,
213
- # antivirus, disk full), unlink the bad backup and abort instead of
214
- # leaving the user with a corrupt backup + compressed primary.
215
- backup_path.write_text(original_text)
216
- backup_readback = backup_path.read_text(errors="ignore")
217
- if backup_readback != original_text:
218
- print(f"❌ Backup write verification failed: {backup_path}")
219
- print(" In-memory original differs from on-disk backup. Aborting before touching the input file.")
220
- try:
221
- backup_path.unlink()
222
- except OSError:
223
- pass
224
- return False
225
- filepath.write_text(compressed)
226
-
227
- # Step 2: Validate + Retry
228
- for attempt in range(MAX_RETRIES):
229
- print(f"\nValidation attempt {attempt + 1}")
230
-
231
- result = validate(backup_path, filepath)
232
-
233
- if result.is_valid:
234
- print("Validation passed")
235
- break
236
-
237
- print("❌ Validation failed:")
238
- for err in result.errors:
239
- print(f" - {err}")
240
-
241
- if attempt == MAX_RETRIES - 1:
242
- # Restore original on failure
243
- filepath.write_text(original_text)
244
- backup_path.unlink(missing_ok=True)
245
- print("❌ Failed after retries — original restored")
246
- return False
247
-
248
- print("Fixing with Claude...")
249
- compressed = call_claude(
250
- build_fix_prompt(original_text, compressed, result.errors)
251
- )
252
- filepath.write_text(compressed)
253
-
254
- return True
DELETE
.agents/skills/caveman-compress/scripts/detect.py
+0 -121
@@ -1,121 +0,0 @@
1
-#!/usr/bin/env python3
2
-"""Detect whether a file is natural language (compressible) or code/config (skip)."""
3
-
4
-import json
5
-import re
6
-from pathlib import Path
7
-
8
-# Extensions that are natural language and compressible
9
-COMPRESSIBLE_EXTENSIONS = {".md", ".txt", ".markdown", ".rst", ".typ", ".typst", ".tex"}
10
-
11
-# Extensions that are code/config and should be skipped
12
-SKIP_EXTENSIONS = {
13
- ".py", ".js", ".ts", ".tsx", ".jsx", ".json", ".yaml", ".yml",
14
- ".toml", ".env", ".lock", ".css", ".scss", ".html", ".xml",
15
- ".sql", ".sh", ".bash", ".zsh", ".go", ".rs", ".java", ".c",
16
- ".cpp", ".h", ".hpp", ".rb", ".php", ".swift", ".kt", ".lua",
17
- ".dockerfile", ".makefile", ".csv", ".ini", ".cfg",
18
-}
19
-
20
-# Patterns that indicate a line is code
21
-CODE_PATTERNS = [
22
- re.compile(r"^\s*(import |from .+ import |require\(|const |let |var )"),
23
- re.compile(r"^\s*(def |class |function |async function |export )"),
24
- re.compile(r"^\s*(if\s*\(|for\s*\(|while\s*\(|switch\s*\(|try\s*\{)"),
25
- re.compile(r"^\s*[\}\]\);]+\s*$"), # closing braces/brackets
26
- re.compile(r"^\s*@\w+"), # decorators/annotations
27
- re.compile(r'^\s*"[^"]+"\s*:\s*'), # JSON-like key-value
28
- re.compile(r"^\s*\w+\s*=\s*[{\[\(\"']"), # assignment with literal
29
-]
30
-
31
-
32
-def _is_code_line(line: str) -> bool:
33
- """Check if a line looks like code."""
34
- return any(p.match(line) for p in CODE_PATTERNS)
35
-
36
-
37
-def _is_json_content(text: str) -> bool:
38
- """Check if content is valid JSON."""
39
- try:
40
- json.loads(text)
41
- return True
42
- except (json.JSONDecodeError, ValueError):
43
- return False
44
-
45
-
46
-def _is_yaml_content(lines: list[str]) -> bool:
47
- """Heuristic: check if content looks like YAML."""
48
- yaml_indicators = 0
49
- for line in lines[:30]:
50
- stripped = line.strip()
51
- if stripped.startswith("---"):
52
- yaml_indicators += 1
53
- elif re.match(r"^\w[\w\s]*:\s", stripped):
54
- yaml_indicators += 1
55
- elif stripped.startswith("- ") and ":" in stripped:
56
- yaml_indicators += 1
57
- # If most non-empty lines look like YAML
58
- non_empty = sum(1 for l in lines[:30] if l.strip())
59
- return non_empty > 0 and yaml_indicators / non_empty > 0.6
60
-
61
-
62
-def detect_file_type(filepath: Path) -> str:
63
- """Classify a file as 'natural_language', 'code', 'config', or 'unknown'.
64
-
65
- Returns:
66
- One of: 'natural_language', 'code', 'config', 'unknown'
67
- """
68
- ext = filepath.suffix.lower()
69
-
70
- # Extension-based classification
71
- if ext in COMPRESSIBLE_EXTENSIONS:
72
- return "natural_language"
73
- if ext in SKIP_EXTENSIONS:
74
- return "code" if ext not in {".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".env"} else "config"
75
-
76
- # Extensionless files (like CLAUDE.md, TODO) — check content
77
- if not ext:
78
- try:
79
- text = filepath.read_text(errors="ignore")
80
- except (OSError, PermissionError):
81
- return "unknown"
82
-
83
- lines = text.splitlines()[:50]
84
-
85
- if _is_json_content(text[:10000]):
86
- return "config"
87
- if _is_yaml_content(lines):
88
- return "config"
89
-
90
- code_lines = sum(1 for l in lines if l.strip() and _is_code_line(l))
91
- non_empty = sum(1 for l in lines if l.strip())
92
- if non_empty > 0 and code_lines / non_empty > 0.4:
93
- return "code"
94
-
95
- return "natural_language"
96
-
97
- return "unknown"
98
-
99
-
100
-def should_compress(filepath: Path) -> bool:
101
- """Return True if the file is natural language and should be compressed."""
102
- if not filepath.is_file():
103
- return False
104
- # Skip backup files
105
- if filepath.name.endswith(".original.md"):
106
- return False
107
- return detect_file_type(filepath) == "natural_language"
108
-
109
-
110
-if __name__ == "__main__":
111
- import sys
112
-
113
- if len(sys.argv) < 2:
114
- print("Usage: python detect.py <file1> [file2] ...")
115
- sys.exit(1)
116
-
117
- for path_str in sys.argv[1:]:
118
- p = Path(path_str).resolve()
119
- file_type = detect_file_type(p)
120
- compress = should_compress(p)
121
- print(f" {p.name:30s} type={file_type:20s} compress={compress}")
DELETE
.agents/skills/caveman-compress/scripts/validate.py
+0 -213
@@ -1,213 +0,0 @@
1
-#!/usr/bin/env python3
2
-import re
3
-from collections import Counter
4
-from pathlib import Path
5
-
6
-URL_REGEX = re.compile(r"https?://[^\s)]+")
7
-FENCE_OPEN_REGEX = re.compile(r"^(\s{0,3})(`{3,}|~{3,})(.*)$")
8
-HEADING_REGEX = re.compile(r"^(#{1,6})\s+(.*)", re.MULTILINE)
9
-BULLET_REGEX = re.compile(r"^\s*[-*+]\s+", re.MULTILINE)
10
-
11
-# crude but effective path detection
12
-# Requires either a path prefix (./ ../ / or drive letter) or a slash/backslash within the match
13
-PATH_REGEX = re.compile(r"(?:\./|\.\./|/|[A-Za-z]:\\)[\w\-/\\\.]+|[\w\-\.]+[/\\][\w\-/\\\.]+")
14
-
15
-
16
-class ValidationResult:
17
- def __init__(self):
18
- self.is_valid = True
19
- self.errors = []
20
- self.warnings = []
21
-
22
- def add_error(self, msg):
23
- self.is_valid = False
24
- self.errors.append(msg)
25
-
26
- def add_warning(self, msg):
27
- self.warnings.append(msg)
28
-
29
-
30
-def read_file(path: Path) -> str:
31
- return path.read_text(errors="ignore")
32
-
33
-
34
-# ---------- Extractors ----------
35
-
36
-
37
-def extract_headings(text):
38
- return [(level, title.strip()) for level, title in HEADING_REGEX.findall(text)]
39
-
40
-
41
-def extract_code_blocks(text):
42
- """Line-based fenced code block extractor.
43
-
44
- Handles ``` and ~~~ fences with variable length (CommonMark: closing
45
- fence must use same char and be at least as long as opening). Supports
46
- nested fences (e.g. an outer 4-backtick block wrapping inner 3-backtick
47
- content).
48
- """
49
- blocks = []
50
- lines = text.split("\n")
51
- i = 0
52
- n = len(lines)
53
- while i < n:
54
- m = FENCE_OPEN_REGEX.match(lines[i])
55
- if not m:
56
- i += 1
57
- continue
58
- fence_char = m.group(2)[0]
59
- fence_len = len(m.group(2))
60
- open_line = lines[i]
61
- block_lines = [open_line]
62
- i += 1
63
- closed = False
64
- while i < n:
65
- close_m = FENCE_OPEN_REGEX.match(lines[i])
66
- if (
67
- close_m
68
- and close_m.group(2)[0] == fence_char
69
- and len(close_m.group(2)) >= fence_len
70
- and close_m.group(3).strip() == ""
71
- ):
72
- block_lines.append(lines[i])
73
- closed = True
74
- i += 1
75
- break
76
- block_lines.append(lines[i])
77
- i += 1
78
- if closed:
79
- blocks.append("\n".join(block_lines))
80
- # Unclosed fences are silently skipped — they indicate malformed markdown
81
- # and including them would cause false-positive validation failures.
82
- return blocks
83
-
84
-
85
-def extract_urls(text):
86
- return set(URL_REGEX.findall(text))
87
-
88
-
89
-def extract_paths(text):
90
- return set(PATH_REGEX.findall(text))
91
-
92
-
93
-def count_bullets(text):
94
- return len(BULLET_REGEX.findall(text))
95
-
96
-
97
-def extract_inline_codes(text):
98
- text_without_fences = re.sub(r"^```[\s\S]*?^```", "", text, flags=re.MULTILINE)
99
- text_without_fences = re.sub(r"^~~~[\s\S]*?^~~~", "", text_without_fences, flags=re.MULTILINE)
100
- return re.findall(r"`([^`]+)`", text_without_fences)
101
-
102
-
103
-# ---------- Validators ----------
104
-
105
-
106
-def validate_headings(orig, comp, result):
107
- h1 = extract_headings(orig)
108
- h2 = extract_headings(comp)
109
-
110
- if len(h1) != len(h2):
111
- result.add_error(f"Heading count mismatch: {len(h1)} vs {len(h2)}")
112
-
113
- if h1 != h2:
114
- result.add_warning("Heading text/order changed")
115
-
116
-
117
-def validate_code_blocks(orig, comp, result):
118
- c1 = extract_code_blocks(orig)
119
- c2 = extract_code_blocks(comp)
120
-
121
- if c1 != c2:
122
- result.add_error("Code blocks not preserved exactly")
123
-
124
-
125
-def validate_urls(orig, comp, result):
126
- u1 = extract_urls(orig)
127
- u2 = extract_urls(comp)
128
-
129
- if u1 != u2:
130
- result.add_error(f"URL mismatch: lost={u1 - u2}, added={u2 - u1}")
131
-
132
-
133
-def validate_paths(orig, comp, result):
134
- p1 = extract_paths(orig)
135
- p2 = extract_paths(comp)
136
-
137
- if p1 != p2:
138
- result.add_warning(f"Path mismatch: lost={p1 - p2}, added={p2 - p1}")
139
-
140
-
141
-def validate_bullets(orig, comp, result):
142
- b1 = count_bullets(orig)
143
- b2 = count_bullets(comp)
144
-
145
- if b1 == 0:
146
- return
147
-
148
- diff = abs(b1 - b2) / b1
149
-
150
- if diff > 0.15:
151
- result.add_warning(f"Bullet count changed too much: {b1} -> {b2}")
152
-
153
-
154
-def validate_inline_codes(orig, comp, result):
155
- c1 = Counter(extract_inline_codes(orig))
156
- c2 = Counter(extract_inline_codes(comp))
157
-
158
- if c1 != c2:
159
- lost = set(c1.keys()) - set(c2.keys())
160
- added = set(c2.keys()) - set(c1.keys())
161
- for code, count in c1.items():
162
- if code in c2 and c2[code] < count:
163
- lost.add(f"{code} (lost {count - c2[code]} of {count} occurrences)")
164
- if lost:
165
- result.add_error(f"Inline code lost: {lost}")
166
- if added:
167
- result.add_warning(f"Inline code added: {added}")
168
-
169
-
170
-# ---------- Main ----------
171
-
172
-
173
-def validate(original_path: Path, compressed_path: Path) -> ValidationResult:
174
- result = ValidationResult()
175
-
176
- orig = read_file(original_path)
177
- comp = read_file(compressed_path)
178
-
179
- validate_headings(orig, comp, result)
180
- validate_code_blocks(orig, comp, result)
181
- validate_urls(orig, comp, result)
182
- validate_paths(orig, comp, result)
183
- validate_bullets(orig, comp, result)
184
- validate_inline_codes(orig, comp, result)
185
-
186
- return result
187
-
188
-
189
-# ---------- CLI ----------
190
-
191
-if __name__ == "__main__":
192
- import sys
193
-
194
- if len(sys.argv) != 3:
195
- print("Usage: python validate.py <original> <compressed>")
196
- sys.exit(1)
197
-
198
- orig = Path(sys.argv[1]).resolve()
199
- comp = Path(sys.argv[2]).resolve()
200
-
201
- res = validate(orig, comp)
202
-
203
- print(f"\nValid: {res.is_valid}")
204
-
205
- if res.errors:
206
- print("\nErrors:")
207
- for e in res.errors:
208
- print(f" - {e}")
209
-
210
- if res.warnings:
211
- print("\nWarnings:")
212
- for w in res.warnings:
213
- print(f" - {w}")
DELETE
.agents/skills/caveman-help/README.md
+0 -40
@@ -1,40 +0,0 @@
1
-# caveman-help
2
-
3
-Quick-reference card. One shot, no mode change.
4
-
5
-## What it does
6
-
7
-Prints a cheat sheet of all caveman modes, sibling skills, deactivation triggers, and how to set the default mode via
8
-env var or config file. One-shot display — does not flip the active mode, write flag files, or persist anything. Use
9
-when you forget the slash commands.
10
-
11
-## How to invoke
12
-
13
-```
14
-/caveman-help
15
-```
16
-
17
-Also triggers on "caveman help", "what caveman commands", "how do I use caveman".
18
-
19
-## Example output
20
-
21
-```
22
-Modes:
23
- /caveman full (default)
24
- /caveman lite lighter
25
- /caveman ultra extreme
26
- /caveman wenyan classical Chinese
27
-
28
-Skills:
29
- /caveman-commit terse Conventional Commits
30
- /caveman-review one-line PR comments
31
- /caveman-stats session token savings
32
-
33
-Deactivate:
34
- "stop caveman" or "normal mode"
35
-```
36
-
37
-## See also
38
-
39
-- [`SKILL.md`](./SKILL.md) — full reference card
40
-- [Caveman README](../../README.md) — repo overview
DELETE
.agents/skills/caveman-help/SKILL.md
+0 -60
@@ -1,62 +0,0 @@
1
-name: caveman-help
2
-description: >
3
- Quick-reference card for all caveman modes, skills, and commands.
4
- One-shot display, not a persistent mode. Trigger: /caveman-help,
5
- "caveman help", "what caveman commands", "how do I use caveman".
6
-
7
-# Caveman Help
8
-
9
-Display this reference card when invoked. One-shot — do NOT change mode, write flag files, or persist anything. Output
10
-in caveman style.
11
-
12
-## Modes
13
-
14
-| Mode | Trigger | What change |
15
-|------------------|-------------------------|----------------------------------------------------------------------|
16
-| **Lite** | `/caveman lite` | Drop filler. Keep sentence structure. |
17
-| **Full** | `/caveman` | Drop articles, filler, pleasantries, hedging. Fragments OK. Default. |
18
-| **Ultra** | `/caveman ultra` | Extreme compression. Bare fragments. Tables over prose. |
19
-| **Wenyan-Lite** | `/caveman wenyan-lite` | Classical Chinese style, light compression. |
20
-| **Wenyan-Full** | `/caveman wenyan` | Full 文言文. Maximum classical terseness. |
21
-| **Wenyan-Ultra** | `/caveman wenyan-ultra` | Extreme. Ancient scholar on a budget. |
22
-
23
-Mode stick until changed or session end.
24
-
25
-## Skills
26
-
27
-| Skill | Trigger | What it do |
28
-|----------------------|----------------------------|----------------------------------------------------------------|
29
-| **caveman-commit** | `/caveman-commit` | Terse commit messages. Conventional Commits. ≤50 char subject. |
30
-| **caveman-review** | `/caveman-review` | One-line PR comments: `L42: bug: user null. Add guard.` |
31
-| **caveman-compress** | `/caveman-compress <file>` | Compress .md files to caveman prose. Saves ~46% input tokens. |
32
-| **caveman-help** | `/caveman-help` | This card. |
33
-
34
-## Deactivate
35
-
36
-Say "stop caveman" or "normal mode". Resume anytime with `/caveman`.
37
-
38
-## Configure Default Mode
39
-
40
-Default mode = `full`. Change it:
41
-
42
-**Environment variable** (highest priority):
43
-
44
-```bash
45
-export CAVEMAN_DEFAULT_MODE=ultra
46
-```
47
-
48
-**Config file** (`~/.config/caveman/config.json`):
49
-
50
-```json
51
-{ "defaultMode": "lite" }
52
-```
53
-
54
-Set `"off"` to disable auto-activation on session start. User can still activate manually with `/caveman`.
55
-
56
-Resolution: env var > config file > `full`.
57
-
58
-## More
59
-
60
-Full docs: https://github.com/JuliusBrussee/caveman
DELETE
.agents/skills/caveman-review/README.md
+0 -36
@@ -1,36 +0,0 @@
1
-# caveman-review
2
-
3
-One-line PR comments. Location, problem, fix. No throat-clearing.
4
-
5
-## What it does
6
-
7
-Generates code review comments in `L<line>: <severity> <problem>. <fix>.` format. One line per finding. Severity emoji:
8
-🔴 bug, 🟡 risk, 🔵 nit, ❓ question. Drops "I noticed that...", hedging, and restating what the diff already shows. Keeps
9
-exact line numbers, backticked symbols, and concrete fixes.
10
-
11
-Auto-clarity: drops terse mode for CVE-class security findings, architectural disagreements, and onboarding contexts
12
-where the author needs the *why*. Resumes terse for the rest.
13
-
14
-Output only — does not approve, request changes, or run linters.
15
-
16
-## How to invoke
17
-
18
-```
19
-/caveman-review
20
-```
21
-
22
-Also triggers on "review this PR", "code review", "review the diff".
23
-
24
-## Example output
25
-
26
-```
27
-L42: 🔴 bug: user can be null after .find(). Add guard before .email.
28
-L88-140: 🔵 nit: 50-line fn does 4 things. Extract validate/normalize/persist.
29
-L23: 🟡 risk: no retry on 429. Wrap in withBackoff(3).
30
-L107: ❓ q: why drop the cache here? Reads on next request will miss.
31
-```
32
-
33
-## See also
34
-
35
-- [`SKILL.md`](./SKILL.md) — full LLM-facing instructions
36
-- [Caveman README](../../README.md) — repo overview
DELETE
.agents/skills/caveman-review/SKILL.md
+0 -61
@@ -1,63 +0,0 @@
1
-name: caveman-review
2
-description: >
3
- Ultra-compressed code review comments. Cuts noise from PR feedback while preserving
4
- the actionable signal. Each comment is one line: location, problem, fix. Use when user
5
- says "review this PR", "code review", "review the diff", "/review", or invokes
6
- /caveman-review. Auto-triggers when reviewing pull requests.
7
-
8
-Write code review comments terse and actionable. One line per finding. Location, problem, fix. No throat-clearing.
9
-
10
-## Rules
11
-
12
-**Format:** `L<line>: <problem>. <fix>.` — or `<file>:L<line>: ...` when reviewing multi-file diffs.
13
-
14
-**Severity prefix (optional, when mixed):**
15
-
16
-- `🔴 bug:` — broken behavior, will cause incident
17
-- `🟡 risk:` — works but fragile (race, missing null check, swallowed error)
18
-- `🔵 nit:` — style, naming, micro-optim. Author can ignore
19
-- `❓ q:` — genuine question, not a suggestion
20
-
21
-**Drop:**
22
-
23
-- "I noticed that...", "It seems like...", "You might want to consider..."
24
-- "This is just a suggestion but..." — use `nit:` instead
25
-- "Great work!", "Looks good overall but..." — say it once at the top, not per comment
26
-- Restating what the line does — the reviewer can read the diff
27
-- Hedging ("perhaps", "maybe", "I think") — if unsure use `q:`
28
-
29
-**Keep:**
30
-
31
-- Exact line numbers
32
-- Exact symbol/function/variable names in backticks
33
-- Concrete fix, not "consider refactoring this"
34
-- The *why* if the fix isn't obvious from the problem statement
35
-
36
-## Examples
37
-
38
-❌ "I noticed that on line 42 you're not checking if the user object is null before accessing the email property. This
39
-could potentially cause a crash if the user is not found in the database. You might want to add a null check here."
40
-
41
-✅ `L42: 🔴 bug: user can be null after .find(). Add guard before .email.`
42
-
43
-❌ "It looks like this function is doing a lot of things and might benefit from being broken up into smaller functions
44
-for readability."
45
-
46
-✅ `L88-140: 🔵 nit: 50-line fn does 4 things. Extract validate/normalize/persist.`
47
-
48
-❌ "Have you considered what happens if the API returns a 429? I think we should probably handle that case."
49
-
50
-✅ `L23: 🟡 risk: no retry on 429. Wrap in withBackoff(3).`
51
-
52
-## Auto-Clarity
53
-
54
-Drop terse mode for: security findings (CVE-class bugs need full explanation + reference), architectural disagreements (
55
-need rationale, not just a one-liner), and onboarding contexts where the author is new and needs the "why". In those
56
-cases write a normal paragraph, then resume terse for the rest.
57
-
58
-## Boundaries
59
-
60
-Reviews only — does not write the code fix, does not approve/request-changes, does not run linters. Output the comment(
61
-s) ready to paste into the PR. "stop caveman-review" or "normal mode": revert to verbose review style.
DELETE
.agents/skills/caveman-stats/README.md
+0 -33
@@ -1,33 +0,0 @@
1
-# caveman-stats
2
-
3
-Real session token receipts. No AI estimation.
4
-
5
-## What it does
6
-
7
-Reads the current Claude Code session log directly and reports actual input/output token usage plus estimated savings
8
-versus a non-caveman baseline. Numbers come from the JSONL session log on disk — the model itself does not compute or
9
-estimate them. Output is injected by the `caveman-mode-tracker` hook, which intercepts `/caveman-stats` and returns the
10
-formatted stats as a blocked-decision reason.
11
-
12
-Each run also writes a lifetime-savings suffix file used by the statusline badge (`⛏ 12.4k`).
13
-
14
-## How to invoke
15
-
16
-```
17
-/caveman-stats
18
-```
19
-
20
-## Example output
21
-
22
-```
23
-Session: 47 turns
24
-Input: 12,304 tokens
25
-Output: 3,891 tokens (caveman)
26
-Baseline: 11,247 tokens (estimated without caveman)
27
-Saved: 7,356 tokens (~65%)
28
-```
29
-
30
-## See also
31
-
32
-- [`SKILL.md`](./SKILL.md) — hook contract and mechanics
33
-- [Caveman README](../../README.md) — repo overview
DELETE
.agents/skills/caveman-stats/SKILL.md
+0 -10
@@ -1,12 +0,0 @@
1
-name: caveman-stats
2
-description: >
3
- Show real token usage and estimated savings for the current session.
4
- Reads directly from the Claude Code session log — no AI estimation.
5
- Triggers on /caveman-stats. Output is injected by the mode-tracker hook;
6
- the model itself does not compute the numbers.
7
-
8
-This skill is delivered by `hooks/caveman-stats.js` (read by `hooks/caveman-mode-tracker.js` on `/caveman-stats`). The
9
-model does not need to do anything when this skill fires — the hook returns `decision: "block"` with the formatted stats
10
-as the reason. The user sees the numbers immediately.
DELETE
.agents/skills/caveman/README.md
+0 -52
@@ -1,52 +0,0 @@
1
-# caveman
2
-
3
-Talk like smart caveman. Same brain, fewer tokens.
4
-
5
-## What it does
6
-
7
-Compress every model response to caveman-style prose. Drops articles, filler, pleasantries, and hedging. Keeps every
8
-technical detail, code block, error string, and symbol exact. Cuts ~65-75% of output tokens with full accuracy
9
-preserved. Mode persists for the whole session until changed or stopped.
10
-
11
-Six intensity levels:
12
-
13
-| Level | What change |
14
-|----------------|---------------------------------------------------------------------|
15
-| `lite` | Drop filler/hedging. Sentences stay full. Professional but tight. |
16
-| `full` | Default. Drop articles, fragments OK, short synonyms. |
17
-| `ultra` | Bare fragments. Abbreviations (DB, auth, fn). Arrows for causality. |
18
-| `wenyan-lite` | Classical Chinese register, light compression. |
19
-| `wenyan-full` | Maximum 文言文. 80-90% character reduction. |
20
-| `wenyan-ultra` | Extreme classical compression. |
21
-
22
-Auto-clarity rule: caveman drops to normal prose for security warnings, irreversible-action confirmations, multi-step
23
-sequences where fragment ambiguity risks misread, and when user repeats a question. Resumes after the clear part.
24
-
25
-## How to invoke
26
-
27
-```
28
-/caveman # full mode (default)
29
-/caveman lite # lighter compression
30
-/caveman ultra # extreme compression
31
-/caveman wenyan # classical Chinese
32
-stop caveman # back to normal prose
33
-```
34
-
35
-## Example output
36
-
37
-Question: "Why does my React component re-render?"
38
-
39
-Normal prose:
40
-> Your component re-renders because you create a new object reference each render. Wrapping it in `useMemo` will fix the
41
-> issue.
42
-
43
-Caveman (full):
44
-> New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`.
45
-
46
-Caveman (ultra):
47
-> Inline obj prop → new ref → re-render. `useMemo`.
48
-
49
-## See also
50
-
51
-- [`SKILL.md`](./SKILL.md) — full LLM-facing instructions
52
-- [Caveman README](../../README.md) — repo overview, install, benchmarks
DELETE
.agents/skills/caveman/SKILL.md
+0 -80
@@ -1,82 +0,0 @@
1
-name: caveman
2
-description: >
3
- Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman
4
- while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra,
5
- wenyan-lite, wenyan-full, wenyan-ultra.
6
- Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens",
7
- "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.
8
-
9
-Respond terse like smart caveman. All technical substance stay. Only fluff die.
10
-
11
-## Persistence
12
-
13
-ACTIVE EVERY RESPONSE. No revert after many turns. No filler drift. Still active if unsure. Off only: "stop caveman" / "
14
-normal mode".
15
-
16
-Default: **full**. Switch: `/caveman lite|full|ultra`.
17
-
18
-## Rules
19
-
20
-Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy
21
-to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Technical terms
22
-exact. Code blocks unchanged. Errors quoted exact.
23
-
24
-Pattern: `[thing] [action] [reason]. [next step].`
25
-
26
-Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
27
-Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
28
-
29
-## Intensity
30
-
31
-| Level | What change |
32
-|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
33
-| **lite** | No filler/hedging. Keep articles + full sentences. Professional but tight |
34
-| **full** | Drop articles, fragments OK, short synonyms. Classic caveman |
35
-| **ultra** | Abbreviate prose words (DB/auth/config/req/res/fn/impl), strip conjunctions, arrows for causality (X → Y), one word when one word enough. Code symbols, function names, API names, error strings: never abbreviate |
36
-| **wenyan-lite** | Semi-classical. Drop filler/hedging but keep grammar structure, classical register |
37
-| **wenyan-full** | Maximum classical terseness. Fully 文言文. 80-90% character reduction. Classical sentence patterns, verbs precede objects, subjects often omitted, classical particles (之/乃/為/其) |
38
-| **wenyan-ultra** | Extreme abbreviation while keeping classical Chinese feel. Maximum compression, ultra terse |
39
-
40
-Example — "Why React component re-render?"
41
-
42
-- lite: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."
43
-- full: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."
44
-- ultra: "Inline obj prop → new ref → re-render. `useMemo`."
45
-- wenyan-lite: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"
46
-- wenyan-full: "物出新參照,致重繪。useMemo .Wrap之。"
47
-- wenyan-ultra: "新參照→重繪。useMemo Wrap。"
48
-
49
-Example — "Explain database connection pooling."
50
-
51
-- lite: "Connection pooling reuses open connections instead of creating new ones per request. Avoids repeated handshake
52
- overhead."
53
-- full: "Pool reuse open DB connections. No new connection per request. Skip handshake overhead."
54
-- ultra: "Pool = reuse DB conn. Skip handshake → fast under load."
55
-- wenyan-full: "池reuse open connection。不每req新開。skip handshake overhead。"
56
-- wenyan-ultra: "池reuse conn。skip handshake → fast。"
57
-
58
-## Auto-Clarity
59
-
60
-Drop caveman when:
61
-
62
-- Security warnings
63
-- Irreversible action confirmations
64
-- Multi-step sequences where fragment order or omitted conjunctions risk misread
65
-- Compression itself creates technical ambiguity (e.g., `"migrate table drop column backup first"` — order unclear
66
- without articles/conjunctions)
67
-- User asks to clarify or repeats question
68
-
69
-Resume caveman after clear part done.
70
-
71
-Example — destructive op:
72
-> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
73
-> ```sql
74
-> DROP TABLE users;
75
-> ```
76
-> Caveman resume. Verify backup exist first.
77
-
78
-## Boundaries
79
-
80
-Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end.
DELETE
.claude/skills/cavecrew
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/cavecrew
DELETE
.claude/skills/caveman
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/caveman
DELETE
.claude/skills/caveman-commit
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/caveman-commit
DELETE
.claude/skills/caveman-compress
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/caveman-compress
DELETE
.claude/skills/caveman-help
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/caveman-help
DELETE
.claude/skills/caveman-review
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/caveman-review
DELETE
.claude/skills/caveman-stats
+0 -1
@@ -1 +0,0 @@
1
-../../.agents/skills/caveman-stats