diff --git a/bin/clean.sh b/bin/clean.sh index 3500b5a..3d93dda 100755 --- a/bin/clean.sh +++ b/bin/clean.sh @@ -339,7 +339,12 @@ safe_clean() { read -r size count < "$result_file" 2> /dev/null || true if [[ "$count" -gt 0 && "$size" -gt 0 ]]; then if [[ "$DRY_RUN" != "true" ]]; then - rm -rf "$path" 2> /dev/null || true + # Handle symbolic links separately (only remove the link, not the target) + if [[ -L "$path" ]]; then + rm "$path" 2> /dev/null || true + else + rm -rf "$path" 2> /dev/null || true + fi fi ((total_size_bytes += size)) ((total_count += count)) @@ -363,7 +368,12 @@ safe_clean() { if [[ "$count" -gt 0 && "$size_bytes" -gt 0 ]]; then if [[ "$DRY_RUN" != "true" ]]; then - rm -rf "$path" 2> /dev/null || true + # Handle symbolic links separately (only remove the link, not the target) + if [[ -L "$path" ]]; then + rm "$path" 2> /dev/null || true + else + rm -rf "$path" 2> /dev/null || true + fi fi ((total_size_bytes += size_bytes)) ((total_count += count)) @@ -647,6 +657,16 @@ perform_cleanup() { fi [[ $tmp_cleaned -eq 1 ]] && log_success "Old system temp files (${TEMP_FILE_AGE_DAYS}+ days)" + # Clean system crash reports and diagnostics + sudo find /Library/Logs/DiagnosticReports -type f -mtime +30 -delete 2> /dev/null || true + sudo find /Library/Logs/CrashReporter -type f -mtime +30 -delete 2> /dev/null || true + log_success "Old system crash reports (30+ days)" + + # Clean old system logs (keep recent ones for troubleshooting) + sudo find /var/log -name "*.log" -type f -mtime +30 -delete 2> /dev/null || true + sudo find /var/log -name "*.gz" -type f -mtime +30 -delete 2> /dev/null || true + log_success "Old system logs (30+ days)" + sudo rm -rf /Library/Updates/* 2> /dev/null || true log_success "System library caches and updates" @@ -702,6 +722,13 @@ perform_cleanup() { safe_clean ~/Downloads/*.download "Incomplete downloads (Safari)" safe_clean ~/Downloads/*.crdownload "Incomplete downloads (Chrome)" safe_clean ~/Downloads/*.part "Incomplete downloads (partial)" + + # Additional user-level caches (commonly missed) + safe_clean ~/Library/Autosave\ Information/* "Autosave information" + safe_clean ~/Library/IdentityCaches/* "Identity caches" + safe_clean ~/Library/Suggestions/* "Suggestions cache (Siri)" + safe_clean ~/Library/Calendars/Calendar\ Cache "Calendar cache" + safe_clean ~/Library/Application\ Support/AddressBook/Sources/*/Photos.cache "Address Book photo cache" end_section start_section "Finder metadata" @@ -1328,12 +1355,14 @@ perform_cleanup() { esac # Check file age - only clean if 60+ days inactive + # Use modification time (mtime) instead of access time (atime) + # because macOS disables atime updates by default for performance if [[ -e "$directory_path" ]]; then - local last_access_epoch=$(stat -f%a "$directory_path" 2> /dev/null || echo "0") + local last_modified_epoch=$(stat -f%m "$directory_path" 2> /dev/null || echo "0") local current_epoch=$(date +%s) - local days_since_access=$(((current_epoch - last_access_epoch) / 86400)) + local days_since_modified=$(((current_epoch - last_modified_epoch) / 86400)) - if [[ $days_since_access -lt $ORPHAN_AGE_THRESHOLD ]]; then + if [[ $days_since_modified -lt $ORPHAN_AGE_THRESHOLD ]]; then return 1 fi fi @@ -1455,6 +1484,16 @@ perform_cleanup() { while IFS= read -r inprogress_file; do [[ -d "$inprogress_file" ]] || continue + # Safety check: only delete .inProgress backups older than 24 hours + # This prevents deleting backups that are currently in progress + local file_mtime=$(stat -f%m "$inprogress_file" 2> /dev/null || echo "0") + local current_time=$(date +%s) + local hours_old=$(( (current_time - file_mtime) / 3600 )) + + if [[ $hours_old -lt 24 ]]; then + continue # Skip - backup might still be in progress + fi + local size_kb=$(du -sk "$inprogress_file" 2> /dev/null | awk '{print $1}' || echo "0") if [[ "$size_kb" -gt 0 ]]; then local backup_name=$(basename "$inprogress_file") @@ -1502,6 +1541,15 @@ perform_cleanup() { while IFS= read -r inprogress_file; do [[ -d "$inprogress_file" ]] || continue + # Safety check: only delete .inProgress backups older than 24 hours + local file_mtime=$(stat -f%m "$inprogress_file" 2> /dev/null || echo "0") + local current_time=$(date +%s) + local hours_old=$(( (current_time - file_mtime) / 3600 )) + + if [[ $hours_old -lt 24 ]]; then + continue # Skip - backup might still be in progress + fi + local size_kb=$(du -sk "$inprogress_file" 2> /dev/null | awk '{print $1}' || echo "0") if [[ "$size_kb" -gt 0 ]]; then local backup_name=$(basename "$inprogress_file") diff --git a/bin/uninstall.sh b/bin/uninstall.sh index fc9f732..569843a 100755 --- a/bin/uninstall.sh +++ b/bin/uninstall.sh @@ -497,6 +497,26 @@ uninstall_applications() { echo if [[ $REPLY =~ ^[Yy]$ ]]; then + # Stop Launch Agents and Daemons before removal + # User-level Launch Agents + for plist in ~/Library/LaunchAgents/"$bundle_id"*.plist; do + if [[ -f "$plist" ]]; then + launchctl unload "$plist" 2> /dev/null || true + fi + done + # System-level Launch Agents + for plist in /Library/LaunchAgents/"$bundle_id"*.plist; do + if [[ -f "$plist" ]]; then + sudo launchctl unload "$plist" 2> /dev/null || true + fi + done + # System-level Launch Daemons + for plist in /Library/LaunchDaemons/"$bundle_id"*.plist; do + if [[ -f "$plist" ]]; then + sudo launchctl unload "$plist" 2> /dev/null || true + fi + done + # Remove the application if rm -rf "$app_path" 2> /dev/null; then echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed application" @@ -508,8 +528,15 @@ uninstall_applications() { # Remove user-level related files while IFS= read -r file; do if [[ -n "$file" && -e "$file" ]]; then - if rm -rf "$file" 2> /dev/null; then - echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)" + # Handle symbolic links separately (only remove the link, not the target) + if [[ -L "$file" ]]; then + if rm "$file" 2> /dev/null; then + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)" + fi + else + if rm -rf "$file" 2> /dev/null; then + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(echo "$file" | sed "s|$HOME|~|" | xargs basename)" + fi fi fi done <<< "$related_files" @@ -519,10 +546,19 @@ uninstall_applications() { echo -e " ${BLUE}${ICON_SOLID}${NC} Admin access required for system files" while IFS= read -r file; do if [[ -n "$file" && -e "$file" ]]; then - if sudo rm -rf "$file" 2> /dev/null; then - echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(basename "$file")" + # Handle symbolic links separately (only remove the link, not the target) + if [[ -L "$file" ]]; then + if sudo rm "$file" 2> /dev/null; then + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(basename "$file")" + else + echo -e " ${YELLOW}${ICON_ERROR}${NC} Failed to remove: $file" + fi else - echo -e " ${YELLOW}${ICON_ERROR}${NC} Failed to remove: $file" + if sudo rm -rf "$file" 2> /dev/null; then + echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed $(basename "$file")" + else + echo -e " ${YELLOW}${ICON_ERROR}${NC} Failed to remove: $file" + fi fi fi done <<< "$system_files" diff --git a/lib/common.sh b/lib/common.sh index d5b2a28..7ebed30 100755 --- a/lib/common.sh +++ b/lib/common.sh @@ -1569,6 +1569,14 @@ find_app_files() { [[ -d ~/Library/Logs/"$app_name" ]] && files_to_clean+=("$HOME/Library/Logs/$app_name") [[ -d ~/Library/Logs/"$bundle_id" ]] && files_to_clean+=("$HOME/Library/Logs/$bundle_id") + # Crash Reports and Diagnostics + while IFS= read -r -d '' report; do + files_to_clean+=("$report") + done < <(find ~/Library/Logs/DiagnosticReports \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null) + while IFS= read -r -d '' report; do + files_to_clean+=("$report") + done < <(find ~/Library/Logs/CrashReporter \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null) + # Saved Application State [[ -d ~/Library/Saved\ Application\ State/"$bundle_id".savedState ]] && files_to_clean+=("$HOME/Library/Saved Application State/$bundle_id.savedState") @@ -1708,6 +1716,12 @@ find_app_files() { files_to_clean+=("$favorite") done < <(find ~/Library/Favorites \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null) + # Unix-style configuration directories and files (cross-platform apps) + [[ -d ~/.config/"$app_name" ]] && files_to_clean+=("$HOME/.config/$app_name") + [[ -d ~/.local/share/"$app_name" ]] && files_to_clean+=("$HOME/.local/share/$app_name") + [[ -d ~/."$app_name" ]] && files_to_clean+=("$HOME/.$app_name") + [[ -f ~/."${app_name}rc" ]] && files_to_clean+=("$HOME/.${app_name}rc") + # Only print if array has elements to avoid unbound variable error if [[ ${#files_to_clean[@]} -gt 0 ]]; then printf '%s\n' "${files_to_clean[@]}" @@ -1747,6 +1761,14 @@ find_app_system_files() { [[ -d /Library/Logs/"$app_name" ]] && system_files+=("/Library/Logs/$app_name") [[ -d /Library/Logs/"$bundle_id" ]] && system_files+=("/Library/Logs/$bundle_id") + # System Crash Reports and Diagnostics + while IFS= read -r -d '' report; do + system_files+=("$report") + done < <(find /Library/Logs/DiagnosticReports \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null) + while IFS= read -r -d '' report; do + system_files+=("$report") + done < <(find /Library/Logs/CrashReporter \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null) + # System Frameworks [[ -d /Library/Frameworks/"$app_name".framework ]] && system_files+=("/Library/Frameworks/$app_name.framework")