Sfoglia il codice sorgente

feat(caveman): full install — plugin + standalone hooks + MCP scaffold

Wires JuliusBrussee/caveman into the always-on tier alongside
security-guidance and superpowers. Caveman compresses Claude's output
tokens (~75%) by speaking like a caveman while keeping technical
substance. Three layers:

  1. Plugin (caveman@caveman, marketplace JuliusBrussee/caveman)
     — adds /caveman, /caveman-commit, /caveman-review, /caveman-stats,
       /caveman-help, /cavecrew, /compress + 3 cavecrew agents +
       SessionStart/UserPromptSubmit hooks from the plugin path.
  2. Standalone hooks (statusline + stats badge) deployed by
     caveman's own hooks/install.sh into ~/.claude/hooks/. Paths in
     settings.json normalized to ~/.claude/hooks/... so this user's
     home dir doesn't leak across machines.
  3. caveman-shrink MCP proxy — NOT auto-registered. The bare proxy
     fails health checks because it requires an upstream MCP server
     to wrap. install-plugins.sh STEP 5.5 prints a snippet showing how
     to register a wrapped entry (e.g. caveman-shrink-fs) when the user
     decides which upstream to compress.

New helper enable_plugin() for explicit always-on activation —
'claude plugin install' only copies into cache, doesn't write
enabledPlugins. Idempotent via Python json check.

doctor.sh adds detect_caveman / detect_caveman_hooks / detect_caveman_shrink
checks plus a 300t passive-cost adder. update-all.sh refreshes hook
files via the upstream installer's --force mode.

.gitignore covers caveman runtime files materialized into hooks/
because ~/.claude/hooks is symlinked to this repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bastien 1 settimana fa
parent
commit
e4f4edc121
8 ha cambiato i file con 259 aggiunte e 1 eliminazioni
  1. 12 0
      .gitignore
  2. 1 0
      README.md
  3. 17 0
      doctor.sh
  4. 132 0
      install-plugins.sh
  5. 28 0
      lib/detect-plugins.sh
  6. 9 0
      plugins.lock.json
  7. 30 1
      settings.json
  8. 30 0
      update-all.sh

+ 12 - 0
.gitignore

@@ -56,6 +56,18 @@ skills/find-skills
 # Staging area used by lib/toggle-external.sh when disabling a tool
 skills-disabled/
 
+# Caveman runtime hook files — deployed by caveman hooks/install.sh into
+# ~/.claude/hooks/ (which is a symlink to this repo's hooks/), so they
+# materialize here. They are user-scope state, not repo source.
+hooks/caveman-activate.js
+hooks/caveman-config.js
+hooks/caveman-mode-tracker.js
+hooks/caveman-stats.js
+hooks/caveman-statusline.sh
+hooks/package.json
+hooks/package-lock.json
+hooks/node_modules/
+
 # Local project config (per-machine, not shared)
 .claude/*
 !.claude/tasks/

+ 1 - 0
README.md

@@ -75,6 +75,7 @@ Install output is logged to `install-YYYYMMDD-HHMMSS.log`.
 | **Context7** | Plugin (toggle) | Fast-evolving libs doc lookup (Next.js, React, Prisma...). Requires a free account + API key (see install). | [context7.com](https://context7.com/) |
 | **pr-review-toolkit** | Plugin (toggle) | Multi-agent PR review. | [anthropics/claude-code](https://github.com/anthropics/claude-code) |
 | **Graphify** | Python CLI | Codebase → knowledge graph → navigable wiki. Helps Claude map and search projects efficiently. | [pypi: graphifyy](https://pypi.org/project/graphifyy/) |
+| **Caveman** | Plugin (always on) + hooks | Output token compression (~75%) via caveman-speak. Full install = plugin (`/caveman`, cavecrew, caveman-commit, caveman-review, caveman-stats) + standalone hooks (statusline + stats badge). The optional `caveman-shrink` MCP proxy compresses upstream-server prose — manual config required (it wraps another MCP server). See `install-plugins.sh` STEP 5.5 for the snippet. | [JuliusBrussee/caveman](https://github.com/JuliusBrussee/caveman) |
 
 Versions are pinned in `plugins.lock.json`. To update: edit the file, then re-run `install-plugins.sh`.
 

+ 17 - 0
doctor.sh

@@ -182,6 +182,22 @@ else
   info "Graphifyy not installed (optional — codebase knowledge graph: pipx install graphifyy)"
 fi
 
+if detect_caveman; then
+  pass "Caveman plugin installed"
+  if detect_caveman_hooks; then
+    pass "Caveman standalone hooks (statusline + stats) installed"
+  else
+    warn "Caveman plugin OK but standalone hooks missing — re-run install-plugins.sh"
+  fi
+  if detect_caveman_shrink; then
+    pass "caveman-shrink MCP wrapper registered (custom upstream)"
+  else
+    info "caveman-shrink MCP not wrapped — manual setup (proxy, needs upstream): see install-plugins.sh STEP 5.5"
+  fi
+else
+  info "Caveman not installed (optional — output compression ~75%: run install-plugins.sh)"
+fi
+
 echo ""
 
 # ────────────────────────────────────────────────────────────
@@ -247,6 +263,7 @@ if detect_gstack      2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 2750));
 if detect_uiux_pro_max    2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 400)); fi
 if detect_context7    2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 200)); fi
 if detect_graphifyy   2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 300)); fi
+if detect_caveman     2>/dev/null; then PLUGIN_TOKENS=$((PLUGIN_TOKENS + 300)); fi
 
 TOTAL_TOKENS=$((CLAUDE_MD_TOKENS + SKILL_DESC_TOKENS + PLUGIN_TOKENS))
 SESSION_BUDGET=11000

+ 132 - 0
install-plugins.sh

@@ -353,6 +353,35 @@ install_plugin() {
   fi
 }
 
+# Enable a marketplace plugin in user scope. `claude plugin install` only
+# copies the plugin into ~/.claude/plugins/cache — it does NOT register
+# it in settings.json's enabledPlugins map. Without an explicit enable,
+# the plugin sits dormant. Use this for plugins that should be ALWAYS ON
+# (security-guidance, superpowers, caveman). Idempotent: skips if already
+# present in enabledPlugins.
+enable_plugin() {
+  local name="$1"
+  local source="$2"
+  local key="${name}@${source}"
+  if [ -f "$HOME/.claude/settings.json" ] && command -v python3 &>/dev/null; then
+    if python3 -c "
+import json, sys
+with open('$HOME/.claude/settings.json') as f:
+    d = json.load(f)
+sys.exit(0 if d.get('enabledPlugins', {}).get('$key') else 1)
+" 2>/dev/null; then
+      ok "$name (already enabled)"
+      return
+    fi
+  fi
+  info "Enabling $name..."
+  if claude plugin enable "$key" 2>/dev/null; then
+    ok "$name enabled"
+  else
+    err "$name enable failed — run manually: claude plugin enable $key"
+  fi
+}
+
 # Anthropic bundled plugins (from anthropics/claude-code repo)
 # These are NOT in claude-plugins-official — they require the claude-code marketplace
 info "Adding Anthropic bundled plugins marketplace..."
@@ -383,6 +412,108 @@ install_plugin "ui-ux-pro-max" "ui-ux-pro-max-skill"
 
 echo ""
 
+# ============================================================
+# STEP 5.5 — CAVEMAN (full: plugin + standalone hooks + MCP shrink)
+# ============================================================
+# Caveman compresses output tokens (~75%) via caveman-speak. The "full"
+# install layers three things on top of each other:
+#   1. Plugin       — /caveman command, cavecrew subagents, mode tracker hooks
+#   2. Hooks        — statusline + stats badge written into ~/.claude/
+#   3. MCP shrink   — caveman-shrink proxy that compresses tool input tokens
+# Per-repo rule files (--with-init / --all) are skipped — they would litter
+# this config repo with caveman-rules.md noise meant for project repos.
+echo "── Step 5.5: Caveman (full: plugin + hooks + MCP shrink) ────"
+echo ""
+
+info "Adding Caveman marketplace..."
+claude plugin marketplace add JuliusBrussee/caveman 2>/dev/null || true
+install_plugin "caveman" "caveman"
+enable_plugin  "caveman" "caveman"
+
+# Standalone hooks (statusline + stats badge). The plugin already wires
+# SessionStart + UserPromptSubmit hooks from its own path; this installer
+# adds the statusLine config and ~/.claude/hooks/caveman-stats.js that
+# the plugin doesn't carry.
+CAVEMAN_HOOKS_URL="https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/install.sh"
+if [ -f "$HOME/.claude/hooks/caveman-statusline.sh" ] \
+   && grep -q 'caveman-statusline' "$HOME/.claude/settings.json" 2>/dev/null; then
+  ok "Caveman standalone hooks already installed"
+else
+  info "Installing Caveman standalone hooks (statusline + stats)..."
+  CAVEMAN_HOOKS_TMP="$(mktemp -t caveman-hooks-XXXXXX.sh)"
+  if curl -fsSL "$CAVEMAN_HOOKS_URL" -o "$CAVEMAN_HOOKS_TMP" \
+     && bash "$CAVEMAN_HOOKS_TMP"; then
+    ok "Caveman hooks installed"
+    # Caveman's hooks installer hardcodes the absolute home path
+    # ($HOME/.claude/hooks/caveman-*.js) into settings.json. The repo's
+    # settings.json is symlinked to ~/.claude/settings.json — committing
+    # the absolute path would leak this user's username to every machine
+    # that clones the repo. Rewrite to portable ~/.claude/hooks/... form.
+    if [ -f "$HOME/.claude/settings.json" ] && command -v python3 &>/dev/null; then
+      python3 - "$HOME/.claude/settings.json" "$HOME" <<'PY'
+import json, sys, re
+path, home = sys.argv[1], sys.argv[2]
+with open(path) as f:
+    data = json.load(f)
+def rewrite(node):
+    if isinstance(node, dict):
+        for k, v in node.items():
+            if k == "command" and isinstance(v, str) and "caveman" in v:
+                node[k] = re.sub(rf'"?{re.escape(home)}/.claude/hooks/(caveman-[^"\s]+)"?',
+                                 r'~/.claude/hooks/\1', v)
+            else:
+                rewrite(v)
+    elif isinstance(node, list):
+        for item in node:
+            rewrite(item)
+rewrite(data)
+with open(path, "w") as f:
+    json.dump(data, f, indent=2)
+PY
+      ok "Caveman hook paths normalized to ~/.claude/hooks/... (portable)"
+    fi
+  else
+    err "Caveman hooks install failed — re-run manually: bash <(curl -fsSL $CAVEMAN_HOOKS_URL)"
+  fi
+  rm -f "$CAVEMAN_HOOKS_TMP"
+fi
+
+# MCP shrink — caveman-shrink is a *proxy* that wraps an upstream MCP
+# server and compresses prose fields in its responses. It cannot run
+# standalone (it errors with "missing upstream command"). We don't auto-
+# register it: the user must pick an upstream MCP server to wrap (e.g.
+# the filesystem server, the GitHub server, …) and add a wrapped entry
+# to ~/.claude.json manually. Print the snippet so they can copy-paste.
+if claude mcp list 2>/dev/null | grep -q '^caveman-shrink-'; then
+  ok "caveman-shrink wrapper already registered (custom upstream)"
+else
+  info "caveman-shrink MCP — manual setup needed (it's a proxy, needs an upstream):"
+  cat <<'EOF'
+    Add a wrapped MCP entry to ~/.claude.json under "mcpServers", e.g.
+    to compress filesystem-server responses:
+
+    {
+      "mcpServers": {
+        "caveman-shrink-fs": {
+          "command": "npx",
+          "args": [
+            "-y", "caveman-shrink",
+            "npx", "-y", "@modelcontextprotocol/server-filesystem",
+            "/path/to/dir"
+          ]
+        }
+      }
+    }
+
+    Or via CLI (replace upstream with your target server):
+      claude mcp add caveman-shrink-fs --scope user -- \
+        npx -y caveman-shrink npx -y @modelcontextprotocol/server-filesystem /path
+EOF
+  warn "caveman-shrink not auto-registered (would fail health check without upstream)"
+fi
+
+echo ""
+
 # ============================================================
 # STEP 6 — CONTEXT7 CLI (ctx7)
 # ============================================================
@@ -593,6 +724,7 @@ echo "  ALWAYS ON (installed at user scope):"
 echo "    ✅ security-guidance   — PreToolUse security hook (0 tokens) [claude-code-plugins]"
 echo "    ✅ rtk                 — token compression hook (0 tokens)"
 echo "    ✅ superpowers         — brainstorm/plan/implement/debug workflow"
+echo "    ✅ caveman             — output compression (~75%) + caveman-shrink MCP (input)"
 echo ""
 echo "  TOGGLE (installed but start OFF — /plugin-check recommends when needed):"
 echo "    🔄 gstack              — disabled by default (toggle: lib/toggle-external.sh enable gstack)"

+ 28 - 0
lib/detect-plugins.sh

@@ -66,6 +66,34 @@ detect_graphifyy() {
   command -v graphify &>/dev/null
 }
 
+detect_caveman() {
+  # Caveman — output-token compression via caveman-speak (marketplace plugin)
+  local cache_dir="$HOME/.claude/plugins/cache"
+  [ -d "$cache_dir" ] && compgen -G "$cache_dir"/*caveman* &>/dev/null
+}
+
+# True if a plugin is registered as enabled in settings.json's
+# enabledPlugins map. Filesystem only (no subprocess to claude CLI).
+# Argument is the full "name@marketplace" key.
+plugin_enabled() {
+  local key="$1"
+  [ -f "$HOME/.claude/settings.json" ] || return 1
+  grep -qE "\"${key}\"[[:space:]]*:[[:space:]]*true" "$HOME/.claude/settings.json"
+}
+
+detect_caveman_hooks() {
+  # Standalone hooks (statusline + stats) deployed by caveman hooks/install.sh
+  [ -f "$HOME/.claude/hooks/caveman-statusline.sh" ]
+}
+
+detect_caveman_shrink() {
+  # caveman-shrink is a proxy — only valid when registered with an
+  # upstream wrapper (e.g. caveman-shrink-fs:, caveman-shrink-github:).
+  # Bare 'caveman-shrink:' fails health checks and is treated as missing.
+  command -v claude &>/dev/null \
+    && claude mcp list 2>/dev/null | grep -q '^caveman-shrink-'
+}
+
 
 # --- Plan detection ---
 

+ 9 - 0
plugins.lock.json

@@ -31,5 +31,14 @@
     "path": "skills/emil-design-eng/SKILL.md",
     "managed_by": "curl",
     "note": "Emil Kowalski's design engineering skill — UI polish, animations, component craft. Downloaded to skills-external/emil-design-eng/, symlinked by link.sh."
+  },
+  "caveman": {
+    "source": "https://github.com/JuliusBrussee/caveman",
+    "marketplace": "JuliusBrussee/caveman",
+    "plugin_ref": "caveman@caveman",
+    "managed_by": "claude plugin + standalone hooks installer + claude mcp",
+    "extras": ["hooks (statusline + stats)", "caveman-shrink MCP proxy"],
+    "version": "latest",
+    "note": "Caveman — ~75% output token compression via caveman-speak. Full install = plugin + standalone hooks (statusline + stats badge) + caveman-shrink MCP (input compression). Updates pull via 'claude plugin update'; hooks installer is idempotent."
   }
 }

+ 30 - 1
settings.json

@@ -195,6 +195,16 @@
             "command": "bash ~/.claude/hooks/session-start.sh"
           }
         ]
+      },
+      {
+        "hooks": [
+          {
+            "type": "command",
+            "command": "node ~/.claude/hooks/caveman-activate.js",
+            "timeout": 5,
+            "statusMessage": "Loading caveman mode..."
+          }
+        ]
       }
     ],
     "PreToolUse": [
@@ -207,6 +217,18 @@
           }
         ]
       }
+    ],
+    "UserPromptSubmit": [
+      {
+        "hooks": [
+          {
+            "type": "command",
+            "command": "node ~/.claude/hooks/caveman-mode-tracker.js",
+            "timeout": 5,
+            "statusMessage": "Tracking caveman mode..."
+          }
+        ]
+      }
     ]
   },
   "statusLine": {
@@ -215,7 +237,8 @@
   },
   "enabledPlugins": {
     "example-skills@anthropic-agent-skills": true,
-    "ui-ux-pro-max@ui-ux-pro-max-skill": true
+    "ui-ux-pro-max@ui-ux-pro-max-skill": true,
+    "caveman@caveman": true
   },
   "extraKnownMarketplaces": {
     "claude-code-plugins": {
@@ -241,6 +264,12 @@
         "source": "github",
         "repo": "anthropics/skills"
       }
+    },
+    "caveman": {
+      "source": {
+        "source": "github",
+        "repo": "JuliusBrussee/caveman"
+      }
     }
   },
   "effortLevel": "xhigh",

+ 30 - 0
update-all.sh

@@ -224,6 +224,36 @@ else
   info "emil-design-eng not installed — skipping (run: make plugin)"
 fi
 
+# ── 7.4. Update Caveman (hooks + MCP shrink) ──
+# Plugin updates are handled by the marketplace plugin update loop below
+# (step 8). This step refreshes the standalone hook files (statusline +
+# stats badge) that live outside the plugin in ~/.claude/hooks/.
+echo ""
+echo "── Updating Caveman extras (hooks + MCP shrink)..."
+if [ -f "$HOME/.claude/hooks/caveman-statusline.sh" ]; then
+  CAVEMAN_HOOKS_URL="https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/install.sh"
+  CAVEMAN_HOOKS_TMP="$(mktemp -t caveman-hooks-XXXXXX.sh)"
+  if curl -fsSL "$CAVEMAN_HOOKS_URL" -o "$CAVEMAN_HOOKS_TMP" \
+     && bash "$CAVEMAN_HOOKS_TMP" --force 2>/dev/null; then
+    ok "Caveman hooks refreshed"
+  else
+    warn "Caveman hooks refresh failed — re-run install-plugins.sh"
+  fi
+  rm -f "$CAVEMAN_HOOKS_TMP"
+else
+  info "Caveman hooks not installed — skipping (run: make plugin)"
+fi
+# MCP shrink uses 'npx -y caveman-shrink' which always fetches latest from
+# npm at invocation time — no explicit update needed. We only check for
+# user-defined wrappers (caveman-shrink-*), since the bare proxy fails
+# health checks without an upstream.
+if command -v claude &>/dev/null \
+   && claude mcp list 2>/dev/null | grep -q '^caveman-shrink-'; then
+  ok "caveman-shrink wrappers detected (auto-update via npx -y)"
+else
+  info "caveman-shrink not wrapped — manual setup (see install-plugins.sh STEP 5.5)"
+fi
+
 # ── 7.5. Update external skills (npx skills) ──
 echo ""
 echo "── Updating external skills (npx skills)..."