Founding Member: $499/mes de por vida
Micare
Calidad de vida

Validador automático de archivos .plist

Un hook (gancho de automatización) que revisa cada archivo .plist (los archivos de configuración de Apple/macOS) justo después de que tu asistente lo edita o lo crea. Si el archivo quedó mal formado, le pasa el error exacto al asistente para que lo corrija antes de terminar. No bloquea ni toca el archivo: solo avisa.

HookPremiumCalidad de vidaguardrails personales

🌱 Empieza aquí · Validador automático de archivos .plist

Abre esta carpeta con Claude Code o Cowork y dile a tu asistente: "siembra esta semilla 🌱"

Y ya. Tu asistente hace todo lo técnico. Tú solo confirmas cuando te pregunte.

¿Otras formas de decírselo? "instala esta semilla", "pon esto a funcionar", "haz el setup del validador de plist". Te entiende igual.

¿Qué te acabas de instalar?

Un pequeño vigilante silencioso. De ahora en adelante, cada vez que tu asistente edite o cree un archivo .plist (los archivos de configuración de Apple/macOS, esos que a veces se rompen con un signo mal puesto), este vigilante lo revisa al instante. Si el archivo quedó mal formado, le avisa al asistente con el error exacto para que lo arregle antes de decirte "listo".

Traducción: menos configuraciones rotas que descubres tarde y a las malas. El asistente se corrige solo, en el momento. 🙌

Ojo: los .plist y la herramienta que los valida (plutil) son cosa de macOS. En Windows o Linux esta semilla no estorba: simplemente no hace nada.


Para el asistente que siembra 🤖

Esta sección es para el asistente AI, no para el humano. Ejecútala tal cual.

Este componente es un hook (gancho de automatización) que se engancha al evento PostToolUse del asistente y se dispara después de cada Edit, Write o MultiEdit. Valida con plutil -lint cualquier archivo .plist recién tocado y, si está mal formado, devuelve el detalle al asistente (stderr, exit 2) para que lo corrija. No modifica el archivo ni deshace la edición.

No hay rutas ni datos del dueño que rellenar. La semilla es autocontenida y se instala en la carpeta de configuración estándar del asistente (~/.claude/), que apunta al home del usuario actual. No apliques ningún placeholder <TU-RUTA>, <TU-REPO> ni similar: aquí no aplican.

Paso 1 · Copiar el script a la carpeta de hooks

La carpeta de hooks es la misma en Claude Code y en Cowork: ~/.claude/hooks/.

mkdir -p ~/.claude/hooks
cp kernel/validate-plist.sh ~/.claude/hooks/validate-plist.sh

Paso 2 · Dar permisos de ejecución (chmod)

chmod +x ~/.claude/hooks/validate-plist.sh

Paso 3 · Cablear el hook en settings.json

El wiring vive en ~/.claude/settings.json, bajo la llave hooks.PostToolUse. El snippet a integrar está en kernel/settings.snippet.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/validate-plist.sh" }
        ]
      }
    ]
  }
}

IMPORTANTE: fusionar, no sobrescribir. Si settings.json ya existe y tiene otros hooks, hay que mezclar este bloque dentro de hooks.PostToolUse sin borrar lo que ya haya. Si ya existe una entrada con el matcher Edit|Write|MultiEdit, agrega este comando a su lista hooks en vez de duplicar la entrada.

Si ~/.claude/settings.json no existe, créalo con exactamente el contenido del snippet.

Comando de fusión sugerido (crea el archivo si falta y respeta lo existente):

python3 - <<'PY'
import json, os
p = os.path.expanduser("~/.claude/settings.json")
cmd = "bash ~/.claude/hooks/validate-plist.sh"
matcher = "Edit|Write|MultiEdit"
data = {}
if os.path.exists(p):
    with open(p) as f:
        data = json.load(f)
post = data.setdefault("hooks", {}).setdefault("PostToolUse", [])
entry = next((e for e in post if e.get("matcher") == matcher), None)
if entry is None:
    entry = {"matcher": matcher, "hooks": []}
    post.append(entry)
hooks = entry.setdefault("hooks", [])
if not any(h.get("command") == cmd for h in hooks):
    hooks.append({"type": "command", "command": cmd})
with open(p, "w") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)
print("settings.json actualizado:", p)
PY

Paso 4 · Verificación post-siembra (obligatoria)

(a) Comprobar que el hook ignora lo que no es un .plist inválido (sale limpio):

echo '{"tool_input":{"file_path":"/tmp/no-existe.txt"}}' | bash ~/.claude/hooks/validate-plist.sh; echo "salida: $?"

Salida esperada: no imprime nada y la última línea dice exactamente salida: 0.

(b) Comprobar que SÍ detecta un plist roto (solo en macOS):

printf '<plist><dict><key>rota' > /tmp/roto.plist
echo '{"tool_input":{"file_path":"/tmp/roto.plist"}}' | bash ~/.claude/hooks/validate-plist.sh; echo "salida: $?"
rm -f /tmp/roto.plist

Salida esperada en macOS: imprime un aviso [VALIDATE-PLIST] ... quedo mal formado y la última línea dice salida: 2. (En Windows o Linux, donde no hay plutil, saldrá salida: 0 sin aviso: es correcto, ahí la validación no aplica.)

(c) Comprobar que el wiring quedó en settings.json:

python3 -c "import json,os; d=json.load(open(os.path.expanduser('~/.claude/settings.json'))); p=d.get('hooks',{}).get('PostToolUse',[]); print('OK' if any('validate-plist.sh' in h.get('command','') for e in p for h in e.get('hooks',[])) else 'FALTA')"

Salida esperada: OK.

Si (a) y (c) dan verde (y (b) también en macOS), la semilla quedó sembrada. Recuerda al usuario que debe cerrar y reabrir la sesión del asistente para que el hook se cargue (el asistente lee los hooks al iniciar).


✅ ¿Cómo sé que funcionó? (para ti, el humano)

No tienes que hacer nada en el día a día. La próxima vez que tu asistente ande tocando un archivo .plist y lo deje mal formado, en vez de cantar "listo" con un archivo roto, se va a dar cuenta solo y lo va a corregir antes de entregarte.

Es un cinturón de seguridad silencioso: no lo notas hasta el día que te salva de una configuración rota. Y ese día lo vas a agradecer. 🫡

¿Quieres implementarla con acompañamiento?

Agenda una asesoría directa con JP y aterrízala en tu caso · desde $600 MXN.

Agendar una asesoría