-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathuninstall.sh
More file actions
executable file
·217 lines (195 loc) · 6.92 KB
/
uninstall.sh
File metadata and controls
executable file
·217 lines (195 loc) · 6.92 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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/env bash
set -euo pipefail
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
CONFIG_FILE="$HERMES_HOME/config.yaml"
ENV_FILE="$HERMES_HOME/.env"
INPUT_FD=0
NONINTERACTIVE=0
# Keep uninstaller output plain ASCII. Some minimal terminals render ANSI escape
# sequences literally, which looks broken in one-command flows.
BOLD=''; GREEN=''; YELLOW=''; RED=''; NC=''
log() { printf '%s\n' "==> $*"; }
warn() { printf '%s\n' "WARN: $*"; }
fail() { printf '%s\n' "ERROR: $*" >&2; exit 1; }
need_cmd() { command -v "$1" >/dev/null 2>&1; }
setup_input() {
if [[ -t 0 ]]; then
INPUT_FD=0
elif [[ -e /dev/tty ]] && { exec 3</dev/tty; } 2>/dev/null; then
INPUT_FD=3
else
NONINTERACTIVE=1
warn "No interactive terminal detected; using safe defaults."
fi
}
prompt_read() {
local __var="$1" prompt="$2" value=""
if [[ "$NONINTERACTIVE" == "1" ]]; then
printf -v "$__var" '%s' ""
return 0
fi
if [[ "$INPUT_FD" == "0" ]]; then
read -r -p "$prompt" value || true
else
printf '%s' "$prompt" > /dev/tty
IFS= read -r -u "$INPUT_FD" value || true
fi
printf -v "$__var" '%s' "$value"
}
ask_yn() {
local prompt="$1" default="${2:-n}" reply suffix="[y/N]"
[[ "$default" == "y" ]] && suffix="[Y/n]"
prompt_read reply "$prompt $suffix "
reply="${reply:-$default}"
[[ "$reply" =~ ^[Yy]$ ]]
}
run_cli_with_user_input() {
if [[ "$NONINTERACTIVE" == "1" ]]; then
printf '\n' | "$@"
elif [[ -e /dev/tty ]]; then
"$@" < /dev/tty
else
"$@"
fi
}
banner() {
cat <<'BANNER'
Hermes Memory Stack Uninstaller / Restore Helper
This removes files and registrations installed by hermes-memory-stack.
It does not remove Hermes Agent itself, personal memory, sessions, auth files,
Google cookies, or unrelated user config.
BANNER
}
snapshot_current_config() {
mkdir -p "$HERMES_HOME"
if [[ -f "$CONFIG_FILE" ]]; then
local snap="$CONFIG_FILE.before-memory-stack-uninstall.$(date +%Y%m%d-%H%M%S)"
cp "$CONFIG_FILE" "$snap"
log "Saved current config snapshot: $snap"
fi
}
remove_stack_skills() {
local skills_dir="$HERMES_HOME/skills"
[[ -d "$skills_dir" ]] || { warn "No skills directory at $skills_dir"; return 0; }
local skills=(hermes-memory-stack llm-wiki axiom-perception semble-rs notebooklm)
local removed=0
for skill in "${skills[@]}"; do
while IFS= read -r -d '' path; do
rm -rf "$path"
log "Removed skill: $path"
removed=$((removed + 1))
done < <(find "$skills_dir" -maxdepth 1 -type d -name "$skill" -print0 2>/dev/null)
done
[[ "$removed" -gt 0 ]] || warn "No stack skills found to remove."
}
remove_stack_scripts() {
local scripts=(
"$HERMES_HOME/scripts/sync_notebooklm_inventory.py"
"$HERMES_HOME/scripts/install_notebooklm_inventory_sync.sh"
"$HERMES_HOME/scripts/install_notebooklm_source_pack_sync.sh"
)
local removed=0
for path in "${scripts[@]}"; do
if [[ -e "$path" ]]; then
rm -f "$path"
log "Removed script: $path"
removed=$((removed + 1))
fi
done
while IFS= read -r -d '' path; do
rm -f "$path"
log "Removed source-pack wrapper: $path"
removed=$((removed + 1))
done < <(find "$HERMES_HOME/scripts" -maxdepth 1 -type f -name 'notebooklm_source_pack_*_refresh.sh' -print0 2>/dev/null)
[[ "$removed" -gt 0 ]] || warn "No stack helper scripts found to remove."
}
remove_notebooklm_inventory() {
local inv="$HOME/wiki/_meta/notebooklm-inventory.md"
if [[ -f "$inv" ]] && ask_yn "Remove NotebookLM routing inventory at $inv?" n; then
rm -f "$inv"
log "Removed $inv"
fi
}
remove_mcp() {
if ! need_cmd hermes; then warn "Hermes CLI not found; cannot remove MCP registration."; return 0; fi
if hermes mcp list 2>/dev/null | grep -qi "perception"; then
if ask_yn "Remove MCP server registration 'perception'?" y; then
run_cli_with_user_input hermes mcp remove perception || warn "Could not remove MCP server 'perception'."
fi
else
warn "MCP server 'perception' not found."
fi
}
remove_cron_jobs() {
if ! need_cmd hermes; then warn "Hermes CLI not found; cannot remove cron jobs."; return 0; fi
local listing ids id
listing="$(hermes cron list --all 2>/dev/null || true)"
ids="$(printf '%s\n' "$listing" | awk '
/^[[:space:]]+[0-9a-f]{12}[[:space:]]/ { id=$1 }
/Name:[[:space:]]+Sync NotebookLM inventory to llmwiki/ { if (id) print id }
/Script:[[:space:]]+sync_notebooklm_inventory.py/ { if (id) print id }
/Name:[[:space:]]+NotebookLM source-pack refresh:/ { if (id) print id }
/Script:[[:space:]]+notebooklm_source_pack_[A-Za-z0-9._-]+_refresh\.sh/ { if (id) print id }
' | sort -u)"
if [[ -z "$ids" ]]; then
warn "No stack-owned NotebookLM cron job found."
return 0
fi
printf '%s\n' "$ids"
if ask_yn "Remove the cron job(s) listed above?" y; then
while IFS= read -r id; do
[[ -n "$id" ]] || continue
hermes cron remove "$id" || warn "Could not remove cron job $id"
done <<< "$ids"
fi
}
restore_config_backup() {
[[ -d "$HERMES_HOME" ]] || return 0
local latest=""
while IFS= read -r path; do
latest="$path"
done < <(find "$HERMES_HOME" -maxdepth 1 -type f -name 'config.yaml.bak.*' -print 2>/dev/null | sort)
if [[ -z "$latest" ]]; then
warn "No config backups found at $HERMES_HOME/config.yaml.bak.*"
return 0
fi
printf '\nLatest config backup:\n %s\n' "$latest"
if ask_yn "Restore this backup to $CONFIG_FILE? Current config is snapshotted first." n; then
snapshot_current_config
cp "$latest" "$CONFIG_FILE"
chmod 600 "$CONFIG_FILE" || true
log "Restored config from $latest"
fi
}
remove_optional_cli_tools() {
if ask_yn "Try to uninstall optional notebooklm-py uv tool?" n; then
if need_cmd uv; then uv tool uninstall notebooklm-py || warn "uv tool uninstall notebooklm-py failed."; else warn "uv not found."; fi
fi
if ask_yn "Try to uninstall optional semble_rs cargo binary?" n; then
if need_cmd cargo; then cargo uninstall semble_rs || warn "cargo uninstall semble_rs failed."; else warn "cargo not found."; fi
rm -f "$HOME/.local/bin/semble_rs" 2>/dev/null || true
fi
if ask_yn "Try to uninstall global llm-wiki-compiler npm package?" n; then
if need_cmd npm; then npm uninstall -g llm-wiki-compiler || warn "npm uninstall -g llm-wiki-compiler failed."; else warn "npm not found."; fi
fi
}
main() {
banner
setup_input
snapshot_current_config
if ask_yn "Remove hermes-memory-stack installed skills?" y; then remove_stack_skills; fi
if ask_yn "Remove hermes-memory-stack helper scripts?" y; then remove_stack_scripts; fi
if ask_yn "Remove NotebookLM inventory sync cron job?" y; then remove_cron_jobs; fi
remove_notebooklm_inventory
remove_mcp
restore_config_backup
remove_optional_cli_tools
cat <<EOF
${BOLD}Done.${NC}
Recommended next steps:
1. Restart Hermes/gateway if MCP or config changed: hermes gateway restart
2. Start a new Hermes session or run /reload-skills if you removed skills.
3. Check config health: hermes config check
EOF
}
main "$@"