mirror of
https://github.com/JuliusBrussee/caveman.git
synced 2026-08-11 13:21:09 +02:00
Add windsurf skill, hooks, and install docs
Add .windsurf/skills/caveman SKILL.md and update the sync workflow to copy and commit the new file. Expand README install docs with agent-specific install instructions and standalone hook usage; update AGENTS.md and GEMINI.md to use the new caveman-compress path. Add Windows PowerShell installers (hooks/install.ps1 and hooks/uninstall.ps1) and enhance shell installers (hooks/install.sh, hooks/uninstall.sh) to support --force, plugin detection, clearer statusline wiring messages, and idempotent JSON merging/removal of hook entries. Provides uninstall guidance for other agents and improves overall install/uninstall robustness.
This commit is contained in:
@@ -31,6 +31,8 @@ jobs:
|
||||
cp skills/caveman/SKILL.md caveman/SKILL.md
|
||||
cp skills/caveman/SKILL.md plugins/caveman/skills/caveman/SKILL.md
|
||||
cp skills/caveman/SKILL.md .cursor/skills/caveman/SKILL.md
|
||||
mkdir -p .windsurf/skills/caveman
|
||||
cp skills/caveman/SKILL.md .windsurf/skills/caveman/SKILL.md
|
||||
|
||||
- name: Rebuild caveman.skill ZIP
|
||||
run: |
|
||||
@@ -39,6 +41,6 @@ jobs:
|
||||
- name: Commit and push if changed
|
||||
run: |
|
||||
git diff --quiet && exit 0
|
||||
git add caveman/SKILL.md plugins/caveman/skills/caveman/SKILL.md .cursor/skills/caveman/SKILL.md caveman.skill
|
||||
git add caveman/SKILL.md plugins/caveman/skills/caveman/SKILL.md .cursor/skills/caveman/SKILL.md .windsurf/skills/caveman/SKILL.md caveman.skill
|
||||
git commit -m "chore: sync SKILL.md copies and caveman.skill [skip ci]"
|
||||
git push
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: caveman
|
||||
description: >
|
||||
Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman
|
||||
while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra,
|
||||
wenyan-lite, wenyan-full, wenyan-ultra.
|
||||
Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens",
|
||||
"be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.
|
||||
---
|
||||
|
||||
Respond terse like smart caveman. All technical substance stay. Only fluff die.
|
||||
|
||||
Default: **full**. Switch: `/caveman lite|full|ultra`.
|
||||
|
||||
## Rules
|
||||
|
||||
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
||||
|
||||
Pattern: `[thing] [action] [reason]. [next step].`
|
||||
|
||||
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
||||
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
|
||||
|
||||
## Intensity
|
||||
|
||||
| Level | What change |
|
||||
|-------|------------|
|
||||
| **lite** | No filler/hedging. Keep articles + full sentences. Professional but tight |
|
||||
| **full** | Drop articles, fragments OK, short synonyms. Classic caveman |
|
||||
| **ultra** | Abbreviate (DB/auth/config/req/res/fn/impl), strip conjunctions, arrows for causality (X → Y), one word when one word enough |
|
||||
| **wenyan-lite** | Semi-classical. Drop filler/hedging but keep grammar structure, classical register |
|
||||
| **wenyan-full** | Maximum classical terseness. Fully 文言文. 80-90% character reduction. Classical sentence patterns, verbs precede objects, subjects often omitted, classical particles (之/乃/為/其) |
|
||||
| **wenyan-ultra** | Extreme abbreviation while keeping classical Chinese feel. Maximum compression, ultra terse |
|
||||
|
||||
Example — "Why React component re-render?"
|
||||
- lite: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."
|
||||
- full: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."
|
||||
- ultra: "Inline obj prop → new ref → re-render. `useMemo`."
|
||||
- wenyan-lite: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"
|
||||
- wenyan-full: "物出新參照,致重繪。useMemo .Wrap之。"
|
||||
- wenyan-ultra: "新參照→重繪。useMemo Wrap。"
|
||||
|
||||
Example — "Explain database connection pooling."
|
||||
- lite: "Connection pooling reuses open connections instead of creating new ones per request. Avoids repeated handshake overhead."
|
||||
- full: "Pool reuse open DB connections. No new connection per request. Skip handshake overhead."
|
||||
- ultra: "Pool = reuse DB conn. Skip handshake → fast under load."
|
||||
- wenyan-full: "池reuse open connection。不每req新開。skip handshake overhead。"
|
||||
- wenyan-ultra: "池reuse conn。skip handshake → fast。"
|
||||
|
||||
## Auto-Clarity
|
||||
|
||||
Drop caveman for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done.
|
||||
|
||||
Example — destructive op:
|
||||
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
|
||||
> ```sql
|
||||
> DROP TABLE users;
|
||||
> ```
|
||||
> Caveman resume. Verify backup exist first.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end.
|
||||
@@ -1,4 +1,4 @@
|
||||
@./skills/caveman/SKILL.md
|
||||
@./skills/caveman-commit/SKILL.md
|
||||
@./skills/caveman-review/SKILL.md
|
||||
@./skills/compress/SKILL.md
|
||||
@./caveman-compress/SKILL.md
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@./skills/caveman/SKILL.md
|
||||
@./skills/caveman-commit/SKILL.md
|
||||
@./skills/caveman-review/SKILL.md
|
||||
@./skills/compress/SKILL.md
|
||||
@./caveman-compress/SKILL.md
|
||||
|
||||
@@ -122,44 +122,112 @@ Based on the viral observation that caveman-speak dramatically reduces LLM token
|
||||
|
||||
## Install
|
||||
|
||||
### Claude Code (recommended)
|
||||
Pick your agent. One command. Done.
|
||||
|
||||
Install as a plugin — includes skills + auto-loading hooks + statusline badge. Caveman activates every session, `[CAVEMAN:ULTRA]` badge tracks mode, Claude sets up the statusline on first session:
|
||||
| Agent | Install |
|
||||
|-------|---------|
|
||||
| **Claude Code** | `claude plugin marketplace add JuliusBrussee/caveman && claude plugin install caveman@caveman` |
|
||||
| **Codex** | Clone repo → `/plugins` → Search "Caveman" → Install |
|
||||
| **Gemini CLI** | `gemini extensions install https://github.com/JuliusBrussee/caveman` |
|
||||
| **Cursor** | `npx skills add JuliusBrussee/caveman -a cursor` |
|
||||
| **Windsurf** | `npx skills add JuliusBrussee/caveman -a windsurf` |
|
||||
| **Copilot** | `npx skills add JuliusBrussee/caveman -a github-copilot` |
|
||||
| **Cline** | `npx skills add JuliusBrussee/caveman -a cline` |
|
||||
| **Any other** | `npx skills add JuliusBrussee/caveman` |
|
||||
|
||||
Install once. Use in all sessions after that. One rock. That it.
|
||||
|
||||
### What You Get
|
||||
|
||||
Not all install paths are equal. Claude Code plugin is the richest experience:
|
||||
|
||||
| Feature | Claude Code (plugin) | Codex | Gemini CLI | npx skills |
|
||||
|---------|:-------------------:|:-----:|:----------:|:----------:|
|
||||
| Caveman mode | Y | Y | Y | Y |
|
||||
| Auto-activate every session | Y | — | Y | — |
|
||||
| Statusline badge `[CAVEMAN:ULTRA]` | Y | — | — | — |
|
||||
| Mode tracking (`/caveman ultra`) | Y | — | — | — |
|
||||
| caveman-commit | Y | Y | Y | Y |
|
||||
| caveman-review | Y | Y | Y | Y |
|
||||
| caveman-compress | Y | Y | Y | Y |
|
||||
|
||||
<details>
|
||||
<summary><strong>Claude Code — full details</strong></summary>
|
||||
|
||||
The plugin install gives you everything: skills + auto-loading hooks + statusline badge.
|
||||
|
||||
```bash
|
||||
claude plugin marketplace add JuliusBrussee/caveman
|
||||
claude plugin install caveman@caveman
|
||||
```
|
||||
|
||||
### Any agent (Claude Code, Cursor, Copilot, Windsurf, Cline, Codex, Gemini CLI, Antigravity)
|
||||
|
||||
**Standalone hooks (without plugin):** If you prefer not to use the plugin system:
|
||||
```bash
|
||||
npx skills add JuliusBrussee/caveman
|
||||
# macOS / Linux / WSL
|
||||
bash <(curl -s https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/install.sh)
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/install.ps1 | iex
|
||||
```
|
||||
|
||||
For a specific agent: `npx skills add JuliusBrussee/caveman -a cursor`
|
||||
Or from a local clone: `bash hooks/install.sh` / `powershell -File hooks\install.ps1`
|
||||
|
||||
> [!NOTE]
|
||||
> `npx skills` installs skills only (no hooks). For Claude Code auto-loading hooks, use the plugin install above or run `bash hooks/install.sh`.
|
||||
> Gemini CLI users can also install directly: `gemini extensions install https://github.com/JuliusBrussee/caveman`
|
||||
Uninstall: `bash hooks/uninstall.sh` or `powershell -File hooks\uninstall.ps1`
|
||||
|
||||
### Codex
|
||||
|
||||
1. Clone repo → Open Codex in repo → `/plugins` → Search `Caveman` → Install
|
||||
|
||||
> [!NOTE]
|
||||
> **Windows Codex users:** Clone repo → VS Code → Codex Settings → Plugins → find `Caveman` under local marketplace → Install → Reload Window. Also enable `git config core.symlinks true` before cloning (requires developer mode or admin).
|
||||
|
||||
Install once. Use in all sessions after that. One rock. That it.
|
||||
|
||||
### Statusline Badge
|
||||
|
||||
The plugin ships a statusline script that shows `[CAVEMAN]`, `[CAVEMAN:ULTRA]`, etc. in your Claude Code status bar.
|
||||
**Statusline badge:** The plugin ships a statusline script that shows `[CAVEMAN]`, `[CAVEMAN:ULTRA]`, etc. in your Claude Code status bar.
|
||||
|
||||
- **Plugin install:** Claude offers to configure it on first session (auto-detected)
|
||||
- **Standalone install (`install.sh`):** Configured automatically
|
||||
- **Standalone install:** Configured automatically by `install.sh` / `install.ps1`
|
||||
- **Custom statusline:** See [`hooks/README.md`](hooks/README.md) for the snippet to add to your existing script
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Codex — full details</strong></summary>
|
||||
|
||||
**macOS / Linux:**
|
||||
1. Clone repo → Open Codex in the repo directory → `/plugins` → Search "Caveman" → Install
|
||||
|
||||
**Windows:**
|
||||
1. Enable symlinks first: `git config --global core.symlinks true` (requires Developer Mode or admin)
|
||||
2. Clone repo → Open VS Code → Codex Settings → Plugins → find "Caveman" under local marketplace → Install → Reload Window
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Gemini CLI — full details</strong></summary>
|
||||
|
||||
```bash
|
||||
gemini extensions install https://github.com/JuliusBrussee/caveman
|
||||
```
|
||||
|
||||
Update: `gemini extensions update caveman` · Uninstall: `gemini extensions uninstall caveman`
|
||||
|
||||
Gemini loads the skill via `GEMINI.md` + `gemini-extension.json`. Auto-activates every session through the context file.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Cursor / Windsurf / Cline / Copilot / others</strong></summary>
|
||||
|
||||
All use [npx skills](https://github.com/vercel-labs/skills) (supports 40+ agents):
|
||||
|
||||
```bash
|
||||
npx skills add JuliusBrussee/caveman -a cursor
|
||||
npx skills add JuliusBrussee/caveman -a windsurf
|
||||
npx skills add JuliusBrussee/caveman -a cline
|
||||
npx skills add JuliusBrussee/caveman -a github-copilot
|
||||
npx skills add JuliusBrussee/caveman # auto-detect agent
|
||||
```
|
||||
|
||||
Uninstall: `npx skills remove caveman`
|
||||
|
||||
> **Note:** `npx skills` installs skills only (no hooks or auto-activation). You'll trigger caveman manually with `/caveman` or "talk like caveman" each session.
|
||||
|
||||
> **Windows note:** `npx skills` uses symlinks by default. If symlinks fail, add `--copy`: `npx skills add JuliusBrussee/caveman --copy`
|
||||
|
||||
</details>
|
||||
|
||||
## Usage
|
||||
|
||||
Trigger with:
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# caveman — one-command hook installer for Claude Code (Windows PowerShell)
|
||||
# Installs: SessionStart hook (auto-load rules) + UserPromptSubmit hook (mode tracking)
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File hooks\install.ps1
|
||||
# or: irm https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/install.ps1 | iex
|
||||
# or: powershell -ExecutionPolicy Bypass -File hooks\install.ps1 -Force
|
||||
param(
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Require node
|
||||
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "ERROR: 'node' is required to install the caveman hooks (used to merge" -ForegroundColor Red
|
||||
Write-Host " the hook config into settings.json safely)." -ForegroundColor Red
|
||||
Write-Host " Install Node.js from https://nodejs.org and re-run this script." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$ClaudeDir = Join-Path $env:USERPROFILE ".claude"
|
||||
$HooksDir = Join-Path $ClaudeDir "hooks"
|
||||
$Settings = Join-Path $ClaudeDir "settings.json"
|
||||
$RepoUrl = "https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks"
|
||||
|
||||
$HookFiles = @("caveman-activate.js", "caveman-mode-tracker.js", "caveman-statusline.sh")
|
||||
|
||||
# Resolve source — works from repo clone or remote
|
||||
$ScriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { $null }
|
||||
|
||||
# Check if already installed (unless -Force)
|
||||
$ActivateExists = Test-Path (Join-Path $HooksDir "caveman-activate.js")
|
||||
$TrackerExists = Test-Path (Join-Path $HooksDir "caveman-mode-tracker.js")
|
||||
|
||||
if (-not $Force -and $ActivateExists -and $TrackerExists) {
|
||||
Write-Host "Caveman hooks already installed in $HooksDir"
|
||||
Write-Host " Re-run with -Force to overwrite: powershell -File hooks\install.ps1 -Force"
|
||||
Write-Host ""
|
||||
Write-Host "Nothing to do. Hooks are already in place."
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($Force -and $ActivateExists) {
|
||||
Write-Host "Reinstalling caveman hooks (-Force)..."
|
||||
} else {
|
||||
Write-Host "Installing caveman hooks..."
|
||||
}
|
||||
|
||||
# 1. Ensure hooks dir exists
|
||||
if (-not (Test-Path $HooksDir)) {
|
||||
New-Item -ItemType Directory -Path $HooksDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# 2. Copy or download hook files
|
||||
foreach ($hook in $HookFiles) {
|
||||
$dest = Join-Path $HooksDir $hook
|
||||
$localSource = if ($ScriptDir) { Join-Path $ScriptDir $hook } else { $null }
|
||||
|
||||
if ($localSource -and (Test-Path $localSource)) {
|
||||
Copy-Item $localSource $dest -Force
|
||||
} else {
|
||||
Invoke-WebRequest -Uri "$RepoUrl/$hook" -OutFile $dest -UseBasicParsing
|
||||
}
|
||||
Write-Host " Installed: $dest"
|
||||
}
|
||||
|
||||
# 3. Wire hooks + statusline into settings.json (idempotent)
|
||||
if (-not (Test-Path $Settings)) {
|
||||
Set-Content -Path $Settings -Value "{}"
|
||||
}
|
||||
|
||||
# Back up existing settings.json before touching it
|
||||
Copy-Item $Settings "$Settings.bak" -Force
|
||||
|
||||
# Use node for safe JSON merging (same approach as bash version)
|
||||
$HooksDirEscaped = $HooksDir -replace '\\', '/'
|
||||
|
||||
$nodeScript = @"
|
||||
const fs = require('fs');
|
||||
const settings = JSON.parse(fs.readFileSync('$($Settings -replace '\\', '/')', 'utf8'));
|
||||
if (!settings.hooks) settings.hooks = {};
|
||||
|
||||
// SessionStart
|
||||
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
||||
const hasStart = settings.hooks.SessionStart.some(e =>
|
||||
e.hooks && e.hooks.some(h => h.command && h.command.includes('caveman'))
|
||||
);
|
||||
if (!hasStart) {
|
||||
settings.hooks.SessionStart.push({
|
||||
hooks: [{
|
||||
type: 'command',
|
||||
command: 'node $HooksDirEscaped/caveman-activate.js',
|
||||
timeout: 5,
|
||||
statusMessage: 'Loading caveman mode...'
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
// UserPromptSubmit
|
||||
if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
|
||||
const hasPrompt = settings.hooks.UserPromptSubmit.some(e =>
|
||||
e.hooks && e.hooks.some(h => h.command && h.command.includes('caveman'))
|
||||
);
|
||||
if (!hasPrompt) {
|
||||
settings.hooks.UserPromptSubmit.push({
|
||||
hooks: [{
|
||||
type: 'command',
|
||||
command: 'node $HooksDirEscaped/caveman-mode-tracker.js',
|
||||
timeout: 5,
|
||||
statusMessage: 'Tracking caveman mode...'
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
// Statusline
|
||||
if (!settings.statusLine) {
|
||||
settings.statusLine = {
|
||||
type: 'command',
|
||||
command: 'bash $HooksDirEscaped/caveman-statusline.sh'
|
||||
};
|
||||
console.log(' Statusline badge configured.');
|
||||
} else {
|
||||
const cmd = typeof settings.statusLine === 'string'
|
||||
? settings.statusLine
|
||||
: (settings.statusLine.command || '');
|
||||
if (!cmd.includes('caveman')) {
|
||||
console.log(' NOTE: Existing statusline detected - caveman badge NOT added.');
|
||||
console.log(' See hooks/README.md to add the badge to your existing statusline.');
|
||||
} else {
|
||||
console.log(' Statusline badge already configured.');
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('$($Settings -replace '\\', '/')', JSON.stringify(settings, null, 2) + '\n');
|
||||
console.log(' Hooks wired in settings.json');
|
||||
"@
|
||||
|
||||
node -e $nodeScript
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done! Restart Claude Code to activate." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "What's installed:"
|
||||
Write-Host " - SessionStart hook: auto-loads caveman rules every session"
|
||||
Write-Host " - Mode tracker hook: updates statusline badge when you switch modes"
|
||||
Write-Host " (/caveman lite, /caveman ultra, /caveman-commit, etc.)"
|
||||
Write-Host " - Statusline badge: shows [CAVEMAN] or [CAVEMAN:ULTRA] etc."
|
||||
+56
-5
@@ -3,8 +3,27 @@
|
||||
# Installs: SessionStart hook (auto-load rules) + UserPromptSubmit hook (mode tracking)
|
||||
# Usage: bash hooks/install.sh
|
||||
# or: bash <(curl -s https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/install.sh)
|
||||
# or: bash hooks/install.sh --force (re-install over existing hooks)
|
||||
set -e
|
||||
|
||||
FORCE=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--force|-f) FORCE=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Detect Windows (Git Bash / MSYS / MINGW) — not WSL (WSL reports "linux-gnu")
|
||||
case "$OSTYPE" in
|
||||
msys*|cygwin*|mingw*)
|
||||
echo "WARNING: Running on Windows ($OSTYPE)."
|
||||
echo " This script works in Git Bash/MSYS but symlinks may require"
|
||||
echo " Developer Mode or admin privileges."
|
||||
echo " If you installed via 'claude plugin install', you don't need this script."
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
|
||||
# Require node — we use it to merge the hook config into settings.json
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo "ERROR: 'node' is required to install the caveman hooks (used to merge"
|
||||
@@ -21,16 +40,37 @@ REPO_URL="https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks"
|
||||
HOOK_FILES=("caveman-activate.js" "caveman-mode-tracker.js" "caveman-statusline.sh")
|
||||
|
||||
# Resolve source — works from repo clone or curl pipe
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" 2>/dev/null && pwd)"
|
||||
SCRIPT_DIR=""
|
||||
if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
|
||||
fi
|
||||
|
||||
echo "Installing caveman hooks..."
|
||||
# Check if already installed (unless --force)
|
||||
ALREADY_INSTALLED=0
|
||||
if [ "$FORCE" -eq 0 ] && [ -f "$HOOKS_DIR/caveman-activate.js" ] && [ -f "$HOOKS_DIR/caveman-mode-tracker.js" ]; then
|
||||
ALREADY_INSTALLED=1
|
||||
echo "Caveman hooks already installed in $HOOKS_DIR"
|
||||
echo " Re-run with --force to overwrite: bash hooks/install.sh --force"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "$ALREADY_INSTALLED" -eq 1 ] && [ "$FORCE" -eq 0 ]; then
|
||||
echo "Nothing to do. Hooks are already in place."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$FORCE" -eq 1 ] && [ -f "$HOOKS_DIR/caveman-activate.js" ]; then
|
||||
echo "Reinstalling caveman hooks (--force)..."
|
||||
else
|
||||
echo "Installing caveman hooks..."
|
||||
fi
|
||||
|
||||
# 1. Ensure hooks dir exists
|
||||
mkdir -p "$HOOKS_DIR"
|
||||
|
||||
# 2. Copy or download hook files
|
||||
for hook in "${HOOK_FILES[@]}"; do
|
||||
if [ -f "$SCRIPT_DIR/$hook" ]; then
|
||||
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/$hook" ]; then
|
||||
cp "$SCRIPT_DIR/$hook" "$HOOKS_DIR/$hook"
|
||||
else
|
||||
curl -fsSL "$REPO_URL/$hook" -o "$HOOKS_DIR/$hook"
|
||||
@@ -86,17 +126,28 @@ node -e "
|
||||
});
|
||||
}
|
||||
|
||||
// Statusline — wire caveman badge (only if no statusLine is already configured)
|
||||
// Statusline — wire caveman badge (report if skipped)
|
||||
if (!settings.statusLine) {
|
||||
settings.statusLine = {
|
||||
type: 'command',
|
||||
command: 'bash $HOOKS_DIR/caveman-statusline.sh'
|
||||
};
|
||||
console.log(' Statusline badge configured.');
|
||||
} else {
|
||||
const cmd = typeof settings.statusLine === 'string'
|
||||
? settings.statusLine
|
||||
: (settings.statusLine.command || '');
|
||||
if (!cmd.includes('caveman')) {
|
||||
console.log(' NOTE: Existing statusline detected — caveman badge NOT added.');
|
||||
console.log(' See hooks/README.md to add the badge to your existing statusline.');
|
||||
} else {
|
||||
console.log(' Statusline badge already configured.');
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('$SETTINGS', JSON.stringify(settings, null, 2) + '\n');
|
||||
console.log(' Hooks wired in settings.json');
|
||||
"
|
||||
echo " Hooks + statusline wired in settings.json"
|
||||
|
||||
echo ""
|
||||
echo "Done! Restart Claude Code to activate."
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# caveman — uninstaller for the SessionStart + UserPromptSubmit hooks (Windows PowerShell)
|
||||
# Removes: hook files in ~/.claude/hooks, settings.json entries, and the flag file
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File hooks\uninstall.ps1
|
||||
# or: irm https://raw.githubusercontent.com/JuliusBrussee/caveman/main/hooks/uninstall.ps1 | iex
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$ClaudeDir = Join-Path $env:USERPROFILE ".claude"
|
||||
$HooksDir = Join-Path $ClaudeDir "hooks"
|
||||
$Settings = Join-Path $ClaudeDir "settings.json"
|
||||
$FlagFile = Join-Path $ClaudeDir ".caveman-active"
|
||||
|
||||
$HookFiles = @("caveman-activate.js", "caveman-mode-tracker.js", "caveman-statusline.sh")
|
||||
|
||||
# Detect if caveman is installed as a plugin
|
||||
$PluginInstalled = $false
|
||||
$PluginsDir = Join-Path $ClaudeDir "plugins"
|
||||
if (Test-Path $PluginsDir) {
|
||||
$found = Get-ChildItem -Path $PluginsDir -Recurse -Filter "plugin.json" -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName -match "caveman" }
|
||||
if ($found) { $PluginInstalled = $true }
|
||||
}
|
||||
|
||||
if ($PluginInstalled) {
|
||||
Write-Host "Caveman appears to be installed as a Claude Code plugin." -ForegroundColor Yellow
|
||||
Write-Host "To uninstall the plugin, run:"
|
||||
Write-Host ""
|
||||
Write-Host " claude plugin disable caveman" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "This script removes standalone hooks (installed via install.ps1)."
|
||||
Write-Host "Continuing with standalone hook removal..."
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Host "Uninstalling caveman hooks..."
|
||||
|
||||
# 1. Remove hook files
|
||||
$RemovedFiles = 0
|
||||
foreach ($hook in $HookFiles) {
|
||||
$path = Join-Path $HooksDir $hook
|
||||
if (Test-Path $path) {
|
||||
Remove-Item $path -Force
|
||||
Write-Host " Removed: $path"
|
||||
$RemovedFiles++
|
||||
}
|
||||
}
|
||||
|
||||
if ($RemovedFiles -eq 0) {
|
||||
Write-Host " No hook files found in $HooksDir"
|
||||
}
|
||||
|
||||
# 2. Remove caveman entries from settings.json (idempotent)
|
||||
if (Test-Path $Settings) {
|
||||
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "WARNING: 'node' not found - cannot safely edit settings.json." -ForegroundColor Yellow
|
||||
Write-Host " Remove the caveman SessionStart and UserPromptSubmit"
|
||||
Write-Host " entries from $Settings manually."
|
||||
} else {
|
||||
# Back up before editing
|
||||
Copy-Item $Settings "$Settings.bak" -Force
|
||||
|
||||
$nodeScript = @"
|
||||
const fs = require('fs');
|
||||
const settings = JSON.parse(fs.readFileSync('$($Settings -replace '\\', '/')', 'utf8'));
|
||||
|
||||
const isCavemanEntry = (entry) =>
|
||||
entry && entry.hooks && entry.hooks.some(h =>
|
||||
h.command && h.command.includes('caveman')
|
||||
);
|
||||
|
||||
let removed = 0;
|
||||
if (settings.hooks) {
|
||||
for (const event of ['SessionStart', 'UserPromptSubmit']) {
|
||||
if (Array.isArray(settings.hooks[event])) {
|
||||
const before = settings.hooks[event].length;
|
||||
settings.hooks[event] = settings.hooks[event].filter(e => !isCavemanEntry(e));
|
||||
removed += before - settings.hooks[event].length;
|
||||
if (settings.hooks[event].length === 0) {
|
||||
delete settings.hooks[event];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(settings.hooks).length === 0) {
|
||||
delete settings.hooks;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.statusLine) {
|
||||
const cmd = typeof settings.statusLine === 'string'
|
||||
? settings.statusLine
|
||||
: (settings.statusLine.command || '');
|
||||
if (cmd.includes('caveman')) {
|
||||
delete settings.statusLine;
|
||||
console.log(' Removed caveman statusLine from settings.json');
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('$($Settings -replace '\\', '/')', JSON.stringify(settings, null, 2) + '\n');
|
||||
console.log(' Removed ' + removed + ' caveman hook entries from settings.json');
|
||||
"@
|
||||
|
||||
node -e $nodeScript
|
||||
}
|
||||
}
|
||||
|
||||
# 3. Remove flag file
|
||||
if (Test-Path $FlagFile) {
|
||||
Remove-Item $FlagFile -Force
|
||||
Write-Host " Removed: $FlagFile"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done! Restart Claude Code to complete the uninstall." -ForegroundColor Green
|
||||
|
||||
# Guidance for other agents
|
||||
Write-Host ""
|
||||
Write-Host "Other agents:"
|
||||
Write-Host " npx skills remove caveman # Cursor, Windsurf, Cline, Copilot, etc."
|
||||
Write-Host " claude plugin disable caveman # Claude Code plugin"
|
||||
Write-Host " gemini extensions uninstall caveman # Gemini CLI"
|
||||
+31
-3
@@ -12,16 +12,41 @@ FLAG_FILE="$CLAUDE_DIR/.caveman-active"
|
||||
|
||||
HOOK_FILES=("caveman-activate.js" "caveman-mode-tracker.js" "caveman-statusline.sh")
|
||||
|
||||
# Detect if caveman is installed as a plugin (check plugin cache)
|
||||
PLUGIN_INSTALLED=0
|
||||
if [ -d "$CLAUDE_DIR/plugins" ]; then
|
||||
if find "$CLAUDE_DIR/plugins" -path "*/caveman*" -name "plugin.json" -print -quit 2>/dev/null | grep -q .; then
|
||||
PLUGIN_INSTALLED=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$PLUGIN_INSTALLED" -eq 1 ]; then
|
||||
echo "Caveman appears to be installed as a Claude Code plugin."
|
||||
echo "To uninstall the plugin, run:"
|
||||
echo ""
|
||||
echo " claude plugin disable caveman"
|
||||
echo ""
|
||||
echo "This script removes standalone hooks (installed via install.sh)."
|
||||
echo "Continuing with standalone hook removal..."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Uninstalling caveman hooks..."
|
||||
|
||||
# 1. Remove hook files
|
||||
REMOVED_FILES=0
|
||||
for hook in "${HOOK_FILES[@]}"; do
|
||||
if [ -f "$HOOKS_DIR/$hook" ]; then
|
||||
rm "$HOOKS_DIR/$hook"
|
||||
echo " Removed: $HOOKS_DIR/$hook"
|
||||
REMOVED_FILES=$((REMOVED_FILES + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$REMOVED_FILES" -eq 0 ]; then
|
||||
echo " No hook files found in $HOOKS_DIR"
|
||||
fi
|
||||
|
||||
# 2. Remove caveman entries from settings.json (idempotent)
|
||||
if [ -f "$SETTINGS" ]; then
|
||||
# Require node for the same reason install.sh does — safe JSON editing
|
||||
@@ -86,7 +111,10 @@ fi
|
||||
|
||||
echo ""
|
||||
echo "Done! Restart Claude Code to complete the uninstall."
|
||||
|
||||
# Guidance for other agents
|
||||
echo ""
|
||||
echo "Note: If you installed caveman as a plugin, disabling the plugin is"
|
||||
echo " the recommended way to deactivate hooks — this script is only"
|
||||
echo " needed if you installed manually via install.sh."
|
||||
echo "Other agents:"
|
||||
echo " npx skills remove caveman # Cursor, Windsurf, Cline, Copilot, etc."
|
||||
echo " claude plugin disable caveman # Claude Code plugin"
|
||||
echo " gemini extensions uninstall caveman # Gemini CLI"
|
||||
|
||||
Reference in New Issue
Block a user