¿Qué hace mac-declutter?
Un manual probado en batalla para limpiar el Mac de un desarrollador: proliferación de agentes de IA, servicios zombis en segundo plano, restos de aplicaciones, trampas de proxy y auditorías de rendimiento.
Principios fundamentales:
- Inventario antes de eliminar. Nunca desinstales a ciegas: mide tamaño, fecha de último uso y estado de ejecución primero, luego deja que el usuario elija.
- Nunca toques registros de sesión ni datos de usuario a menos que se te pida explícitamente. Las cachés se regeneran; las sesiones no.
- Verifica después de cada mutación — proceso eliminado, puerto cerrado, elemento de inicio de sesión eliminado.
- La Protección de Integridad del Sistema (SIP) es un muro duro. Las aplicaciones propiedad de root, los contenedores sandbox y las extensiones DriverKit tienen cada una una ruta de eliminación autorizada; no luches contra el sistema operativo.
Fase 0 — Triaje de red (haz esto primero)
El síntoma "todo está desconectado" casi siempre es un proxy local muerto, no aplicaciones rotas:
scutil --proxy # a qué apunta el proxy del sistema
nc -z -w 2 127.0.0.1 <puerto> # ¿está escuchando algo realmente?
lsof -nP -iTCP:<puerto> -sTCP:LISTEN # quién es el dueño
La trampa clásica: las herramientas estilo Clash/V2Ray configuran el proxy del sistema macOS a 127.0.0.1:PUERTO, luego el núcleo muere — el modo de reglas no importa, todo el tráfico muere en la puerta. Las aplicaciones que respetan el proxy del sistema (la mayoría de apps GUI, algunas CLI) fallan todas; curl sigue funcionando porque omite el proxy del sistema, lo que engaña el diagnóstico. Soluciones: reinicia el núcleo del proxy, o desactiva el proxy (networksetup -setwebproxystate Wi-Fi off + -setsecurewebproxystate + -setsocksfirewallproxystate + WPAD off), o apunta la configuración de proxy de la aplicación a nada. Recuerda: curl ignora el proxy del sistema — prueba la conectividad estilo aplicación con curl -x http://127.0.0.1:PUERTO.
Fase 1 — Inventario (solo lectura)
Ejecuta scripts/inventory.sh, o cubre manualmente estas dimensiones:
- Directorios de configuración de agentes:
~/.claude,~/.codex,~/.pi,~/.jcode,~/.config/opencode, etc. Para cada uno: tamaño (du -sh), última actividad (mtime del archivo más nuevo), si el binario CLI aún existe (which). - Estado de inicio de sesión/autenticación:
auth.json,openai-auth.json, archivos de credenciales — lista las claves, nunca imprimas secretos. - Servicios en segundo plano:
launchctl list | grep -v com.appley~/Library/LaunchAgents/*.plist— lee elProgramArgumentsde cada plist para saber qué ejecuta realmente. - Aplicaciones: para cada
.appno de Apple en/Applicationsy~/Applications, recoge tamaño,kMDItemLastUsedDatemediantemdls, y presencia de_MASReceipt(apps de la App Store). "Nunca abierta" + grande = candidato principal. - Cachés:
~/Library/Caches,~/.npm, cachés de pip/bun — grandes y siempre seguras de purgar.
Presenta los hallazgos como una tabla de conservar/eliminar y deja que el usuario elija. Marca las aplicaciones de pago/suscripción (p. ej., _MASReceipt, productos de suscripción conocidos) para que el usuario cancele la facturación antes de eliminar.
Fase 2 — Manual de desinstalación
Por objetivo, en orden:
- Detén servicios:
launchctl remove <etiqueta>para cada LaunchAgent/Daemon relacionado. - Elimina plists:
~/Library/LaunchAgents/<etiqueta>.plist. - Elimina la app/CLI:
rm -rfel.app/npm uninstall -g <paquete>/ elimina el binario.Permission denied→ propiedad de root (instalado con paquete). Usaosascript -e 'do shell script "rm -rf ..." with administrator privileges'(una ventana de contraseña GUI). Nunca usessudoa ciegas.
- Restos en Librería: comprueba y elimina
~/Library/{Application Support,Caches,Containers,HTTPStorages,Preferences}/<app-o-bundle-id>.- Los
Containers/*sandbox pueden resistir incluso a root (protección del administrador de contenedores). Si son pequeños, déjalos — el SO elimina los contenedores huérfanos; nunca vale la pena desactivar SIP.
- Los
- Extensiones del sistema:
systemextensionsctl list. SIP bloquea la desinstalación directa — la única ruta limpia es reinstalar la aplicación del proveedor y ejecutar su desinstalador oficial (buscauninstall.shen/Library/Application Support/<proveedor>/). Los controladores DriverKit.dextse descargan solo en el próximo reinicio — dile al usuario que desaparecerá tras reiniciar. - Elementos de inicio de sesión obsoletos: tras eliminar una aplicación, su entrada "Abrir al iniciar sesión" a menudo permanece como un zombi. Elimínala con
osascript -e 'tell application "System Events" to delete login item "<nombre>"'. - Procesos residuales: las aplicaciones eliminadas pueden dejar procesos ejecutándose desde directorios temporales del actualizador (p. ej., la preparación de Squirrel
ShipIt).ps aux | grep -i <nombre>, mátalos, y comprueba si hay un directorio de datos en$HOME(~.<nombreapp>) — los demonios generados por el actualizador suelen ocultar cientos de MB allí.
Fase 3 — Purga de cachés (riesgo cero)
npm cache clean --force
rm -rf ~/Library/Caches/pip ~/.bun/install/cache
rm -rf ~/Library/Caches/<app-eliminada>/*
Las cachés se regeneran. Los historiales de sesión (sessions/, projects/, registros de conversación *.jsonl) no — déjalos a menos que el usuario diga explícitamente lo contrario.
Fase 4 — Auditoría de rendimiento
memory_pressure | tail -2 # % libre y presión
sysctl -n vm.swapusage # swap usado debería ser ~0 en una máquina sana
sysctl -n vm.loadavg
ps -axm -o rss,comm | awk 'NR>1 {rss=$1; $1=""; sub(/^ /,""); a[$0]+=rss} END {for (k in a) printf "%d\t%s\n", a[k]/1024, k}' | sort -rn | head -15
- "Memoria usada" en el Monitor de Actividad incluye caché de archivos — juzga por el color de presión de memoria y el swap, no por el número de GB.
- Tras una eliminación masiva, Spotlight (
mds_stores,corespotlightd) reindexa durante unas horas — temporal, no lo "arregles". - Agrupa RSS por familia de aplicaciones (los navegadores y las apps de chat generan muchos ayudantes); reporta totales por familia, no ruido por proceso.
- No optimices lo que no es un problema: en una máquina con cero swap y presión verde, intercambiar la UX del día a día (p. ej., un IME inteligente) por unos pocos cientos de MB es un mal trato — dilo.
Fase 5 — Verificación final
- Las etiquetas eliminadas no aparecen en
launchctl list; los plists han desaparecido; no hay procesos que coincidan con nombres eliminados (excepto controladores pendientes de reinicio). - La escucha del puerto del proxy coincide con
scutil --proxy; los puntos finales clave de la API son accesibles (401= accesible pero requiere autenticación, es un aprobado). - Cada agente CLI superviviente pasa una prueba real de extremo a extremo (envía un prompt de una línea, espera una respuesta).
df -h /antes/después para la puntuación.