🌱 Empieza aquí · Tareas de fondo con nombre propio en macOS
Esta semilla le enseña a tu asistente a ponerle nombre propio a las tareas de fondo de tu Mac. Hoy, en Ajustes del Sistema › General › Elementos de inicio y extensiones, muchas salen como «bash» y no sabes cuál es cuál. (En macOS 14 esa pantalla tiene otro nombre; si no la encuentras, escribe «inicio» en el buscador de Ajustes del Sistema.) Después de sembrarla, cada una sale con el nombre que tú elijas.
Para instalarla
Abre esta carpeta con Claude Code en tu Mac y dile a tu asistente:
"Siembra esta semilla 🌱"
Tu asistente te va a enseñar la lista de tus tareas de fondo y te va a preguntar qué nombre quieres para cada una. Del resto se encarga él. Vas a ver algunas palabras en inglés en la pantalla; ni caso, él te guía.
¿Otras formas de pedírselo? "instala esta semilla", "ponle nombre a mis tareas de fondo", "quiero dejar de ver bash en Ajustes". Te entiende igual.
Para el asistente que siembra 🤖
Este componente es un script de Bash para macOS más un lanzador en C de pocas líneas. No es un hook (gancho de automatización): no lleva entrada en settings.json ni en ningún archivo de configuración del asistente. Modifica plists de ~/Library/LaunchAgents (los archivos que describen cada tarea de fondo) y recarga tareas con launchctl (la herramienta de macOS que las arranca y detiene), así que muéstrale a la persona cada comando antes de correrlo y no te saltes la confirmación del paso 3.
Si la máquina no es macOS (Linux o Windows), dile a la persona que esta semilla no aplica a su sistema y detente: el script solo imprime «solo macOS» y sale sin tocar nada. En Windows no hay nada que instalar.
Código de salida. construir.sh sale con 0 cuando todo salió bien y con un número distinto de 0 cuando algo falló (una recarga, la copia de un respaldo, la firma de un paquete, la compilación del lanzador, un plist que no se pudo leer o un vigía que no se dejó detener). Revisa siempre el código con echo "exit=$?" y lee los renglones que imprime: cada fallo dice qué tarea y qué pasó.
1. Revisa los requisitos
sw_vers -productVersion # debe ser 14 o mayor
xcode-select -p # debe imprimir una ruta; si falla: xcode-select --install
/usr/bin/python3 --version # viene con las herramientas de Xcode
2. Copia el kernel a su lugar
Corre esto desde la carpeta de la semilla (la que tiene este archivo):
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
mkdir -p "$DEST"
cp kernel/construir.sh kernel/lanzador.c kernel/bautizo-tareas.plist.plantilla "$DEST/"
cp kernel/nombres.ejemplo.txt "$DEST/nombres.txt"
cp kernel/config.ejemplo.sh "$DEST/config.sh"
chmod +x "$DEST/construir.sh"
Puedes usar otra carpeta si la persona prefiere; en ese caso usa esa ruta en todos los pasos.
Importante: tu terminal puede no recordar variables entre un comando y otro. Por eso cada bloque de abajo empieza volviendo a definir DEST (y, cuando hace falta, carga config.sh). Si cambiaste la carpeta, cámbiala también en esa primera línea de cada bloque.
3. Arma la tabla de nombres con la persona
Lista sus tareas de fondo y el programa que arranca cada una:
for f in "$HOME/Library/LaunchAgents"/*.plist; do
printf '%s → %s\n' "$(basename "$f" .plist)" \
"$(/usr/libexec/PlistBuddy -c 'Print :Program' "$f" 2>/dev/null || /usr/libexec/PlistBuddy -c 'Print :ProgramArguments:0' "$f" 2>/dev/null)"
done
Las que arrancan /bin/bash, /bin/sh, /bin/zsh, python, python3 o node son las que salen con nombre genérico en Ajustes. Pregúntale a la persona cuáles son suyas y qué nombre quiere para cada una. No incluyas tareas de otras apps (por ejemplo las de Google, Microsoft o Adobe) a menos que la persona lo pida.
Edita "$DEST/nombres.txt": borra los renglones de ejemplo y deja uno por tarea, con el formato etiqueta|nombre visible. La etiqueta es el nombre del archivo sin .plist.
Edita "$DEST/config.sh" (todo es opcional):
| Ajuste | Qué es | Valor por defecto |
|---|---|---|
| PREFIJOS_ETIQUETA | Prefijos de las etiquetas de la persona (por ejemplo com.ana). Con esto, una tarea nueva suya que arranque un intérprete recibe nombre automático aunque no esté en la tabla. Déjalo vacío ("") si solo quieres tocar lo que está en la tabla. | com.tu-nombre (cámbialo o vacíalo) |
| PREFIJO_NOMBRE | Lo que va antes del nombre automático. | Tarea · |
| QUITAR_DE_ETIQUETA | Trozo inicial que se quita al armar el nombre automático. | vacío |
| CARPETA_APPS | Dónde se crean los paquetes .app. | $HOME/Applications/Tareas con nombre |
| CARPETA_ESTADO | Lanzador compilado, su huella, los respaldos de cada plist y el vigía retirado. | $HOME/.local/state/tareas-de-fondo-con-nombre |
Antes de seguir, pregunta: ¿alguna de estas tareas tiene Acceso total al disco (Ajustes › Privacidad y seguridad › Acceso total al disco)? Si sí, avísale que al renombrarla perderá ese permiso y tendrá que volver a darlo (ver «Si una tarea pierde permisos», abajo). Que la persona decida si la incluye.
4. Primera corrida
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
bash "$DEST/construir.sh"; echo "exit=$?"
La primera vez imprime lanzador compilado y luego un renglón por tarea, por ejemplo com.ana.respaldo-fotos: ahora se llama «Tarea · Respaldo de fotos», y al final exit=0. Otros mensajes posibles:
ya estaba como «…»: ya tenía ese nombre; no se tocó.está apagada a propósito, solo se renombra: la persona la había apagado conlaunchctl disable; sigue apagada.no hay plist, se salta: la etiqueta de la tabla no existe en~/Library/LaunchAgents; revisa que esté bien escrita. No cuenta como fallo.NO se pudo recargar, revísala: el plist quedó renombrado perolaunchctl bootstrapfalló. El script sigue con las demás tareas y al final sale con código 1. Correbash "$DEST/construir.sh" --deshacer <etiqueta>para devolver esa tarea a su plist original y revísala antes de volver a intentarlo. Ojo: si la corres otra vez sin deshacer, saldrá «ya estaba» porque el plist ya tiene el cambio, aunque la tarea no se haya recargado.no se pudo firmar su paquete, no se tocaono se pudo escribir el Info.plist de su paquete, no se toca: el paquete quedó a medias y el plist de esa tarea no se cambió. Sale con código 1; la siguiente corrida lo vuelve a intentar.no se pudo leer su programa, no se toca: el plist está dañado o no dice qué programa arranca. Sale con código 1.no se pudo compilar el lanzador: faltan las herramientas de Xcode (paso 1). Sale con código 1 antes de tocar cualquier tarea.
5. Vigía de tareas nuevas (opcional)
Si la persona quiere que las tareas nuevas reciban nombre solas, instala el vigía: una tarea de fondo más que corre construir.sh cada vez que cambia ~/Library/LaunchAgents. launchd (el sistema de macOS que arranca las tareas de fondo) no expande $HOME en los plists, por eso se reemplazan los marcadores por rutas absolutas. Usa como etiqueta su propio prefijo y agrégala también a nombres.txt (por ejemplo com.ana.bautizo-tareas|Tarea · Bautizo de tareas).
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
ETIQUETA="com.tu-nombre.bautizo-tareas" # usa el prefijo de la persona
. "$DEST/config.sh" # carga CARPETA_ESTADO
mkdir -p "$CARPETA_ESTADO"
sed -e "s|__ETIQUETA__|$ETIQUETA|g" \
-e "s|__CARPETA_SCRIPT__|$DEST|g" \
-e "s|__AGENTES__|$HOME/Library/LaunchAgents|g" \
-e "s|__ESTADO__|$CARPETA_ESTADO|g" \
"$DEST/bautizo-tareas.plist.plantilla" > "$HOME/Library/LaunchAgents/$ETIQUETA.plist"
plutil -lint "$HOME/Library/LaunchAgents/$ETIQUETA.plist"
launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/$ETIQUETA.plist"
bash "$DEST/construir.sh"; echo "exit=$?"
La última línea corre el script una vez a mano para que el vigía reciba su propio nombre desde la Terminal. Si el vigía se renombrara a sí mismo desde launchd, la recarga cortaría su propia corrida.
El vigía corre cada vez que cambia ~/Library/LaunchAgents (con al menos 20 segundos entre corridas), no repite los renglones «ya estaba» y anota lo que hace con fecha en $CARPETA_ESTADO/bautizo.log (errores en bautizo.err). Su plist termina en ; true: aunque una corrida falle, launchd no lo marca como caído; los fallos quedan escritos en bautizo.log.
--deshacer (ver «Para deshacer») detiene y retira este vigía antes de devolver nada, para que no vuelva a poner los nombres.
6. Verificación post-siembra (obligatoria)
a) Los archivos quedaron en su lugar:
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
. "$DEST/config.sh" # carga CARPETA_ESTADO en esta terminal
ls -la "$DEST/construir.sh" "$DEST/nombres.txt" "$CARPETA_ESTADO/lanzador"; echo "exit=$?"
Salida esperada: tres líneas que listan los archivos y exit=0. Si lanzador no aparece, la compilación falló: revisa el paso 1.
b) Prueba de comportamiento:
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"; . "$DEST/config.sh"
"$CARPETA_ESTADO/lanzador" /bin/echo hola; echo "exit=$?" # esperado: hola y exit=0
bash "$DEST/construir.sh"; echo "exit=$?" # esperado: cada tarea sale «ya estaba como «…»» y exit=0
bash "$DEST/construir.sh" --estado; echo "exit=$?" # etiqueta → nombre registrado por macOS
Si --estado dice que sfltool falló o no existe, no es grave: la prueba que cuenta es la pantalla. Pídele a la persona que abra Ajustes del Sistema › General › Elementos de inicio y extensiones (en macOS 14, búscala escribiendo «inicio» en el buscador de Ajustes) y confirme que sus tareas salen con el nombre nuevo. Si --estado muestra un nombre y la pantalla otro, gana la pantalla.
c) Prueba señuelo (demuestra que los fallos se notan): este bloque arma una carpeta de mentira con un plist dañado y le pide al script que lo procese. No toca las tareas reales de la persona ni llama a launchctl: el script se detiene en esa tarea antes de recargar nada.
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
T="$(mktemp -d)"; mkdir -p "$T/Library/LaunchAgents"
echo 'esto no es un plist' > "$T/Library/LaunchAgents/com.prueba.senuelo.plist"
HOME="$T" bash "$DEST/construir.sh" com.prueba.senuelo; echo "exit=$?"
rm -rf "$T"
. "$DEST/config.sh" # CARPETA_ESTADO real de la persona
"$CARPETA_ESTADO/lanzador" /no/existe; echo "exit=$?"
Salida esperada: lanzador compilado (se compila uno de prueba dentro de la carpeta de mentira), unas líneas en inglés que terminan en Invalid file (es Python avisando que ese archivo está dañado), com.prueba.senuelo: no se pudo leer su programa, no se toca con exit=1, y luego lanzador: execvp: No such file or directory con exit=127. Si el primero saliera exit=0, el script estaría escondiendo fallos: no sigas y revisa que copiaste el construir.sh de esta semilla. (Si la persona cambió CARPETA_APPS o CARPETA_ESTADO a una ruta fija sin $HOME, esta prueba deja ahí un paquete «Tarea · Senuelo» y un respaldo com.prueba.senuelo.plist; bórralos a mano.)
Si instalaste el vigía, pruébalo: touch "$HOME/Library/LaunchAgents/<una-etiqueta-de-la-tabla>.plist", espera medio minuto y revisa "$CARPETA_ESTADO/bautizo.err"; debe estar vacío.
Si una tarea pierde permisos
macOS amarra el Acceso total al disco a la huella del programa. Si una tarea renombrada empieza a pedir permiso en cada corrida, la persona debe teclear (con la etiqueta de esa tarea):
ETIQUETA="com.tu-nombre.respaldo-fotos" # la etiqueta de la tarea que perdió el permiso
tccutil reset SystemPolicyAllFiles "$ETIQUETA.lanzador"
(<etiqueta>.lanzador es el identificador que el script le pone al paquete de esa tarea; por ejemplo com.ana.respaldo-fotos.lanzador.)
Y volver a darle el permiso al programa nuevo en Ajustes › Privacidad y seguridad › Acceso total al disco. Apagar y prender el interruptor no basta. Se prueba dejando que la tarea corra desde launchd, no corriéndola desde la Terminal. El lanzador solo se recompila cuando cambia lanzador.c, para no romper estos permisos sin motivo.
Para deshacer
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
bash "$DEST/construir.sh" --deshacer; echo "exit=$?" # todas las tareas que tienen respaldo
O, para una sola tarea:
DEST="$HOME/.claude/scripts/tareas-de-fondo-con-nombre"
ETIQUETA="com.tu-nombre.respaldo-fotos" # la tarea que quieres devolver
bash "$DEST/construir.sh" --deshacer "$ETIQUETA"; echo "exit=$?"
Qué hace, en orden:
- Primero el vigía. Si encuentra en
~/Library/LaunchAgentsun vigía que corre este mismoconstruir.sh, lo detiene (launchctl bootout), comprueba que de verdad quedó detenido y saca su plist de la carpeta a$CARPETA_ESTADO/vigia-retirado/. Imprime…: vigía detenido y retirado a …. Esto pasa también cuando deshaces una sola tarea: si el vigía siguiera cargado, devolver un plist cambia la carpeta, el vigía despierta y le vuelve a poner nombre a todo. - Si el vigía no se deja detener, imprime
el vigía sigue cargado y volvería a poner los nombres; no se deshace naday sale con código 1 sin tocar ninguna tarea. - Después devuelve el plist original de cada tarea desde el respaldo y la recarga (
…: devuelto al original). Si una tarea estaba apagada a propósito, se devuelve pero se queda apagada. Si una copia o una recarga falla, lo dice, sigue con las demás y al final sale con código 1. - Al final imprime el comando para volver a armar el vigía, por si la persona lo quiere de vuelta. Antes de volver a armarlo, quita de
nombres.txtlas tareas que ya no quiera con nombre; si su etiqueta empieza con un prefijo dePREFIJOS_ETIQUETAy arranca un intérprete, también volvería a recibir nombre automático.
El respaldo es la copia de la primera vez que el script tocó ese plist: si después la persona cambió algo de esa tarea (su horario, sus argumentos), --deshacer también borra esos cambios. Avísale antes de correrlo.
Para quitar solo el vigía, sin deshacer los nombres (cambia la etiqueta por la que usaste en el paso 5):
ETIQUETA="com.tu-nombre.bautizo-tareas"
launchctl bootout "gui/$(id -u)/$ETIQUETA"
rm "$HOME/Library/LaunchAgents/$ETIQUETA.plist"
Los paquetes de CARPETA_APPS se pueden borrar a mano después.
✅ ¿Cómo sé que funcionó?
Abre Ajustes del Sistema › General › Elementos de inicio y extensiones (en macOS 14, escribe «inicio» en el buscador de Ajustes). Donde antes veías «bash» varias veces, ahora ves el nombre de cada tarea, por ejemplo «Tarea · Respaldo de fotos». Ya puedes saber qué corre en tu Mac.
Las tareas corren el mismo programa de siempre (si alguna tenía Acceso total al disco, revisa «Si una tarea pierde permisos»). Junto al nombre, Ajustes seguirá diciendo que el desarrollador es «no identificado»: es normal, eso solo cambia con un certificado de Apple. 🌱
