🌱 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
.plisty 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. 🫡
