-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuninstall.sh
More file actions
169 lines (148 loc) · 5.77 KB
/
Copy pathuninstall.sh
File metadata and controls
169 lines (148 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/bin/bash
set -euo pipefail
# Claude Code Documentation Mirror - Smart Uninstaller
# Dynamically finds and removes all installations
echo "Claude Code Documentation Mirror - Uninstaller"
echo "=============================================="
echo ""
# Parse arguments
AUTO_YES=false
for arg in "$@"; do
case "$arg" in
-y|--yes) AUTO_YES=true ;;
*) echo "Unknown argument: $arg"; exit 1 ;;
esac
done
# Ensure jq is available on Windows (reuse cached binary from installation)
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
if ! command -v jq &> /dev/null; then
for candidate in "$HOME/.claude-code-docs/bin/jq.exe" "$HOME/.claude/bin/jq.exe"; do
if [[ -f "$candidate" ]]; then
export PATH="$(dirname "$candidate"):$PATH"
break
fi
done
if ! command -v jq &> /dev/null; then
echo "❌ Error: jq not found. Re-run the installer first to download it, then uninstall."
exit 1
fi
fi
fi
# Find all installations from configs
find_all_installations() {
local paths=()
# From command file
if [[ -f ~/.claude/commands/docs.md ]]; then
while IFS= read -r line; do
if [[ "$line" =~ Execute:.*claude-code-docs ]]; then
local path=$(echo "$line" | grep -o '[^ "]*claude-code-docs[^ "]*' | head -1)
path="${path/#\~/$HOME}"
# docs-command.md.template embeds a literal, unexpanded "$HOME" (it's
# copied verbatim, never variable-substituted) — expand that too.
path="${path/#\$HOME/$HOME}"
# Get directory part. Real installs live at "$HOME/.claude-code-docs"
# (leading dot), so the basename check must allow an optional dot.
if [[ -d "$path" ]]; then
paths+=("$path")
elif [[ -d "$(dirname "$path")" ]] && [[ "$(basename "$(dirname "$path")")" =~ ^\.?claude-code-docs$ ]]; then
paths+=("$(dirname "$path")")
fi
fi
done < ~/.claude/commands/docs.md
fi
# From hooks
if [[ -f ~/.claude/settings.json ]]; then
local hooks=$(jq -r '.hooks.PreToolUse[]?.hooks[]?.command // empty' ~/.claude/settings.json 2>/dev/null)
while IFS= read -r cmd; do
if [[ "$cmd" =~ claude-code-docs ]]; then
local found=$(echo "$cmd" | grep -o '[^ "]*claude-code-docs[^ "]*' || true)
while IFS= read -r path; do
[[ -z "$path" ]] && continue
path="${path/#\~/$HOME}"
# Clean up path to get the claude-code-docs directory. Real installs
# live at "$HOME/.claude-code-docs" (leading dot), so allow that.
if [[ "$path" =~ (.*/\.?claude-code-docs)(/.*)?$ ]]; then
path="${BASH_REMATCH[1]}"
fi
[[ -d "$path" ]] && paths+=("$path")
done <<< "$found"
fi
done <<< "$hooks"
fi
# Deduplicate - handle empty array case
if [[ ${#paths[@]} -gt 0 ]]; then
printf '%s\n' "${paths[@]}" | sort -u
fi
}
# Main uninstall logic
installations=()
while IFS= read -r line; do
installations+=("$line")
done < <(find_all_installations)
if [[ ${#installations[@]} -gt 0 ]]; then
echo "Found installations at:"
for path in "${installations[@]}"; do
echo " 📁 $path"
done
echo ""
fi
echo "This will remove:"
echo " • The /docs command from ~/.claude/commands/docs.md"
echo " • All claude-code-docs hooks from ~/.claude/settings.json"
if [[ ${#installations[@]} -gt 0 ]]; then
echo " • Installation directories (if safe to remove)"
fi
echo ""
if [[ "$AUTO_YES" == "true" ]]; then
echo "Proceeding automatically (-y/--yes)."
else
read -p "Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled."
exit 0
fi
fi
# Remove command file
if [[ -f ~/.claude/commands/docs.md ]]; then
rm -f ~/.claude/commands/docs.md
echo "✓ Removed /docs command"
fi
# Remove hooks
if [[ -f ~/.claude/settings.json ]]; then
cp ~/.claude/settings.json ~/.claude/settings.json.backup
# Remove ALL hooks containing claude-code-docs
jq '.hooks.PreToolUse = [(.hooks.PreToolUse // [])[] | select(.hooks[0].command | contains("claude-code-docs") | not)]' ~/.claude/settings.json > ~/.claude/settings.json.tmp
# Clean up empty structures
jq 'if .hooks.PreToolUse == [] then .hooks |= if . == {PreToolUse: []} then {} else del(.PreToolUse) end else . end | if .hooks == {} then del(.hooks) else . end' ~/.claude/settings.json.tmp > ~/.claude/settings.json.tmp2
mv ~/.claude/settings.json.tmp2 ~/.claude/settings.json
rm -f ~/.claude/settings.json.tmp
echo "✓ Removed hooks (backup: ~/.claude/settings.json.backup)"
fi
# Remove directories
if [[ ${#installations[@]} -gt 0 ]]; then
echo ""
for path in "${installations[@]}"; do
if [[ ! -d "$path" ]]; then
continue
fi
if [[ -d "$path/.git" ]]; then
pushd "$path" >/dev/null
if [[ -z "$(git status --porcelain 2>/dev/null)" ]]; then
popd >/dev/null
rm -rf "$path"
echo "✓ Removed $path (clean git repo)"
else
popd >/dev/null
echo "⚠️ Preserved $path (has uncommitted changes)"
fi
else
echo "⚠️ Preserved $path (not a git repo)"
fi
done
fi
echo ""
echo "✅ Uninstall complete!"
echo ""
echo "To reinstall:"
echo "curl -fsSL https://raw.githubusercontent.com/bartvanhoey/claude-code-docs/main/install.sh | bash"