From a1095af075e8f1f304abf16e75a9784067b8d5f7 Mon Sep 17 00:00:00 2001 From: Spencer Bull Date: Sat, 5 Sep 2026 18:30:49 -0500 Subject: [PATCH] Ask about the user's data whenever it exists, not only behind the bootstrap marker --- bin/omarchy-remove-ai-hermes | 63 +++++++++++++++--------------- test/shell.d/hermes-remove-test.sh | 27 ++++++++----- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/bin/omarchy-remove-ai-hermes b/bin/omarchy-remove-ai-hermes index 924cafab..2465e46c 100755 --- a/bin/omarchy-remove-ai-hermes +++ b/bin/omarchy-remove-ai-hermes @@ -22,7 +22,7 @@ omarchy-install-hermes-cli --remove || cli_removed=false # and it is the only thing that tells that runtime apart from one the user # installed themselves -- the paths are the same either way. Without it the app # never got that far: a machine where it was installed but never launched still -# has whatever was there before, and none of it is ours to delete. +# has whatever was there before, and none of it is ours to delete unasked. if [[ -f $HOME/.hermes/hermes-agent/.hermes-bootstrap-complete ]]; then # The checkout and venv, its own uv, its own node. None of it is any use once # the app is gone, so it goes without asking; what the user made with the app @@ -55,39 +55,38 @@ if [[ -f $HOME/.hermes/hermes-agent/.hermes-bootstrap-complete ]]; then fi done - # What is left is the user's: the chats, memories and skills in ~/.hermes, - # the connections and their encrypted tokens in ~/.config/Hermes. Keeping - # them stays the default -- they are small, and finding them intact after a - # reinstall is the better surprise -- but a removal meant to be complete - # should not leave credentials behind either, so the choice is put in front - # of the user, default no. Only here, behind the bootstrap marker: without - # it ~/.hermes is an install the app never owned, and offering to delete it - # would put the user's own Hermes on the chopping block. Without a terminal - # to ask in, keeping it is the answer. - data_removed=false - if [[ -t 0 ]] && command -v gum >/dev/null; then - # du answers non-zero when either directory is missing (the app makes - # ~/.config/Hermes, but nothing says it is still there), and pipefail - # would turn that into an aborted removal; the size is worth no such thing. - size=$(du -shc "$HOME/.hermes" "$HOME/.config/Hermes" 2>/dev/null | tail -1 | cut -f1 || true) - if gum confirm --default=false "Also delete your Hermes data ($size: chats, memories, skills, connections and tokens)?"; then - rm -rf "$HOME/.hermes" "$HOME/.config/Hermes" - data_removed=true - fi - fi +fi - echo "" - echo "Hermes Desktop has been removed." - if [[ $data_removed == true ]]; then - echo "Its chats, memories, and settings in ~/.hermes and ~/.config/Hermes are gone too." - else - echo "Your chats, memories, and skills are still in ~/.hermes," - echo "and your connections and settings in ~/.config/Hermes." +# What survives to here is the user's: the chats, memories and skills in +# ~/.hermes, the connections and their encrypted tokens in ~/.config/Hermes. +# Keeping them stays the default -- they are small, and finding them intact +# after a reinstall is the better surprise -- but a removal meant to be +# complete should not leave credentials behind either, so the choice is put in +# front of the user with the size, default no. Asked whenever the directories +# exist, marker or no marker: on a machine where the marker never appeared the +# data came from the terminal CLI or an install the app never finished, and it +# is still what removal is asked to clean up. Naming the paths keeps the +# question honest there too -- ~/.hermes may still carry a runtime the app +# never owned, a yes takes that with it, and saying so is the prompt's job. +# Without a terminal to ask in, keeping everything is the answer. +data_removed=false +if [[ -d $HOME/.hermes || -d $HOME/.config/Hermes ]] && [[ -t 0 ]] && command -v gum >/dev/null; then + # du answers non-zero when either directory is missing, and pipefail would + # turn that into an aborted removal; the size is worth no such thing. + size=$(du -shc "$HOME/.hermes" "$HOME/.config/Hermes" 2>/dev/null | tail -1 | cut -f1 || true) + if gum confirm --default=false "Also delete ~/.hermes and ~/.config/Hermes ($size: chats, memories, skills, connections and tokens)?"; then + rm -rf "$HOME/.hermes" "$HOME/.config/Hermes" + data_removed=true fi -else - echo "" - echo "Hermes Desktop has been removed." - echo "It never finished installing its own Hermes, so nothing in ~/.hermes was touched." +fi + +echo "" +echo "Hermes Desktop has been removed." +if [[ $data_removed == true ]]; then + echo "Its chats, memories, and settings in ~/.hermes and ~/.config/Hermes are gone too." +elif [[ -d $HOME/.hermes || -d $HOME/.config/Hermes ]]; then + echo "Your chats, memories, and skills are still in ~/.hermes," + echo "and your connections and settings in ~/.config/Hermes." fi # The messages above still hold -- the app and its runtime are gone -- but a CLI diff --git a/test/shell.d/hermes-remove-test.sh b/test/shell.d/hermes-remove-test.sh index 88554894..b3ef6862 100755 --- a/test/shell.d/hermes-remove-test.sh +++ b/test/shell.d/hermes-remove-test.sh @@ -184,18 +184,27 @@ OMARCHY_TEST_GUM_STATUS=0 remove_tty || fail "remove succeeds when the data goes fail "a yes deletes ~/.hermes and ~/.config/Hermes" pass "removal deletes the user's data only on an explicit yes" -# Without the bootstrap marker ~/.hermes is an install the app never owned, so -# it must not even be offered for deletion -- not to a terminal, not to a user -# who would say yes. +# Without the bootstrap marker the runtime is not the app's to take unasked, +# but the data question is still the user's to answer: declining keeps the +# whole tree -- runtime included -- untouched. +seed_install +rm -f "$test_home/.hermes/hermes-agent/.hermes-bootstrap-complete" +remove_tty || fail "remove succeeds when the app never installed Hermes" +tr '\0' '\n' <"$test_tmp/gum-log" | grep -qx 'confirm' || + fail "removal still asks about the data without the bootstrap marker" +[[ -d $test_home/.hermes/hermes-agent && -d $test_home/.config/Hermes ]] || + fail "declining keeps a Hermes the app never installed" +pass "removal asks without the marker and declining keeps everything" + +# The prompt names ~/.hermes itself, so a yes takes the whole tree there too, +# unowned runtime and all -- that is what was asked and answered. seed_install rm -f "$test_home/.hermes/hermes-agent/.hermes-bootstrap-complete" OMARCHY_TEST_GUM_STATUS=0 remove_tty || - fail "remove succeeds when the app never installed Hermes" -[[ ! -s $test_tmp/gum-log ]] || - fail "a Hermes the app never installed is not offered for deletion" -[[ -d $test_home/.hermes/hermes-agent && -d $test_home/.config/Hermes ]] || - fail "a Hermes the app never installed survives a would-be yes" -pass "removal never offers a Hermes the app did not install" + fail "remove succeeds when the data goes too without the marker" +[[ ! -e $test_home/.hermes && ! -e $test_home/.config/Hermes ]] || + fail "a yes takes ~/.hermes whole when the marker never appeared" +pass "removal honors a yes on the named paths without the marker" # A CLI teardown that fails must not stop the runtime handling, and must not be # papered over either: the data work still happens, and the failure reaches the