#!/bin/sh
# Instalador del catálogo de skills de https://jesusprodriguez.com
#
#   sh install.sh azure-pr-review          una skill (y sus dependencias)
#   sh install.sh --plugin azure-devops    todas las de un plugin
#   sh install.sh --all                    el catálogo entero
#   sh install.sh --list                   qué hay disponible
#   sh install.sh --check                  qué tienes desactualizado
#   sh install.sh --dir ruta/  ...         destino (por defecto .claude/skills)
#
# No hace falta ejecutarlo con permisos especiales: solo escribe dentro del
# directorio de destino. Léelo entero antes, que para eso está.
#
# Si usas Claude Code, la vía nativa es mejor —se actualiza sola—:
#
#   /plugin marketplace add https://jesusprodriguez.com/skills/marketplace.json
#   /plugin install azure-devops@jprodriguez-toolkit
#
# Generado el 2026-09-25. Licencia CC BY 4.0.

set -eu

BASE="https://jesusprodriguez.com/skills"
DIR=".claude/skills"

# slug|plugin|dependencias|sha256
CATALOGO='azure-devops-cli|azure-devops||0cd4f97858a02234e24e1737b439365d02936cc1fc559e7350d6d6e90bb1e192
azure-pr-review|azure-devops|azure-devops-cli|a4fb3c3f15b25f5eb58f7e800b5243353df006d3da2fbc872697303f392db734
azure-pr-inbox|azure-devops|azure-devops-cli|c4e0d209b8ab44082aa4bd389cf0dcff8c438fa30fe407f751e5681985766ee6
azure-project-status|azure-devops|azure-devops-cli|75ee04dc94263e6e671f6d4f75724a35aec0605485724693b94b390fbc7d4b2d
azure-backlog-health|azure-devops|azure-devops-cli|ad8a1efce2b621932ff14b5e5da97bf9d3e99644262b485f12490155e5d573f3
azure-pbi-authoring|azure-devops|azure-devops-cli|72f93a649a5908a275e8c62e293dad9fba3b746529fb23290e3598d2aa4588e8
azure-sprint-planning|azure-devops|azure-devops-cli|abc5ed0506274b5a8688750261f0ac57553c92c44c26e10b0e83b380fe7b6aae
azure-pbi-plan|azure-devops|azure-devops-cli|e4999c298a77ff9f426d37e5563ace48cdc72c46a7a4a6c1bac733804b7f2ca0
azure-pbi-implement|azure-devops|azure-devops-cli|e688b272a54ee3004a2783a4c3615de2cb744541c4ede4a53a00dbcd51d21a27
azure-repos-audit|azure-devops|azure-devops-cli|65e9ade71af2f7c29db05d4390ae4b7fb309384f1459bf7320d098a9b35bfa26
azure-pipelines|azure-devops||f8d03f8b08a036810001b7b02a6ff1b986ec0397da194f14ae074df4055d6607
astro-static-site|web||036d8bc09b7d1f4f85f1e4b572da8e61ee5c951873642a6c720ef5772ec3ce31
dotnet-web-api|dotnet||4d563ed89717336dba3b2d96cdb6e66ca1f9ddb275f33430f5b915b9fc21db96
bug-hunt|dotnet||b62898a471c8684c6514580849b408d5b85128320518165d8d45cf26448d6f75
microsoft-docs|dotnet||4c6eb3b605e31522534316f671948fa9d33f0dd916a1ec6241695aad972d0638
github-pr-review|ingenieria||471788462af043f72b8b54512cb3fd859a48753152ed8aa721a43ee3fa708d5f
code-tour|ingenieria||1ee6e6b2551e3e5bb6d46074528f44adfafd51d8d7679ca7d5f90a6080b32ff8
excalidraw-diagrams|ingenieria||021bf8cdab7b43131b7f495721af435f93569788baacd5098348c30312c6882c
python-automation|ingenieria||5ba18d15ce93522a754af282a4e0f8d98b7eacd1fbadd0d4badf54537b49e361
pdf-report|ingenieria||9b3aa523ef067c833b80b1b69df1f0628b072b85beb66b2317eaed66a8c11d16
landing-negocio-local|web||a5f9a5981b70947c243b56e480900b56e4668b69daec2cf8370e30a142b76e88
seo-local|web||c99585d9b494aab6702012f456566b6787dbe074761314937193c386c9805bc9
artifact-creator|ingenieria||0e8e6da373231a371212c4dd61aff883eb2a7404b254acbf122b161ad4b10358
eli5|ingenieria||0fd005b8a436dd3eb4301ce6ed42347aa34eb6964987cf3df15a920868ccf238'

PLUGINS="azure-devops dotnet web ingenieria"

morir() { echo "install.sh: $1" >&2; exit 1; }

uso() {
  sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'
  exit "${1:-0}"
}

# Campo N de la fila del catálogo que corresponde a un slug.
campo() {
  echo "$CATALOGO" | awk -F'|' -v s="$1" -v n="$2" '$1 == s { print $n }'
}

existe() { [ -n "$(campo "$1" 1)" ]; }

todos() { echo "$CATALOGO" | awk -F'|' '{ print $1 }'; }

del_plugin() { echo "$CATALOGO" | awk -F'|' -v s="$1" '$2 == s { print $1 }'; }

# sha256 con la herramienta que haya. Sin ninguna, --check se salta la
# comprobación en vez de mentir diciendo que todo está al día.
sha_local() {
  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
  elif command -v shasum >/dev/null 2>&1;   then shasum -a 256 "$1" | cut -d' ' -f1
  else echo ""
  fi
}

# Ojo con los nombres: sh no tiene variables locales, así que cada función usa
# su propio prefijo. Compartir un `slug` entre instalar() y descargar() hacía
# que la dependencia pisara a la skill pedida y esta no llegara a instalarse.
descargar() {
  d_slug="$1"
  d_destino="$DIR/$d_slug/SKILL.md"
  mkdir -p "$DIR/$d_slug"
  if ! curl -fsSL "$BASE/$d_slug/SKILL.md" -o "$d_destino"; then
    morir "no se pudo descargar $d_slug"
  fi
  echo "  instalada  $d_destino"
}

# Una skill y, antes, todo lo que da por hecho.
instalar() {
  i_slug="$1"
  existe "$i_slug" || morir "no existe la skill '$i_slug' (prueba --list)"
  i_deps="$(campo "$i_slug" 3)"
  if [ -n "$i_deps" ]; then
    for i_dep in $(echo "$i_deps" | tr ',' ' '); do
      [ -f "$DIR/$i_dep/SKILL.md" ] && continue
      echo "  dependencia de $i_slug:"
      descargar "$i_dep"
    done
  fi
  descargar "$i_slug"
}

comprobar() {
  c_pendientes=0
  c_instaladas=0
  for c_slug in $(todos); do
    c_fichero="$DIR/$c_slug/SKILL.md"
    [ -f "$c_fichero" ] || continue
    c_instaladas=$((c_instaladas + 1))
    c_actual="$(sha_local "$c_fichero")"
    if [ -z "$c_actual" ]; then
      echo "sin sha256sum ni shasum: no se puede comprobar" >&2
      return 0
    fi
    if [ "$(campo "$c_slug" 4)" != "$c_actual" ]; then
      echo "  desactualizada  $c_slug"
      c_pendientes=$((c_pendientes + 1))
    fi
  done
  if [ "$c_instaladas" -eq 0 ]; then
    echo "No hay ninguna skill instalada en $DIR."
  elif [ "$c_pendientes" -eq 0 ]; then
    echo "Todo al día ($c_instaladas instaladas)."
  else
    echo ""
    echo "$c_pendientes de $c_instaladas por actualizar. Vuelve a instalarlas para ponerlas al día."
  fi
}

listar() {
  echo "Plugins: $PLUGINS"
  echo ""
  echo "$CATALOGO" | awk -F'|' '{ printf "  %-24s %s\n", $1, $2 }'
}

SELECCION=""
MODO="instalar"

while [ $# -gt 0 ]; do
  case "$1" in
    --dir)    [ $# -ge 2 ] || morir "--dir necesita una ruta"; DIR="$2"; shift 2 ;;
    --plugin) [ $# -ge 2 ] || morir "--plugin necesita un nombre"
              lista="$(del_plugin "$2")"
              [ -n "$lista" ] || morir "no hay skills del plugin '$2' (plugins: $PLUGINS)"
              SELECCION="$SELECCION $lista"; shift 2 ;;
    --all)    SELECCION="$SELECCION $(todos)"; shift ;;
    --check)  MODO="comprobar"; shift ;;
    --list)   MODO="listar"; shift ;;
    -h|--help) uso 0 ;;
    -*)       morir "opción desconocida: $1" ;;
    *)        SELECCION="$SELECCION $1"; shift ;;
  esac
done

case "$MODO" in
  listar)    listar; exit 0 ;;
  comprobar) echo "Comprobando en $DIR ..."; comprobar; exit 0 ;;
esac

[ -n "$(echo "$SELECCION" | tr -d ' ')" ] || uso 1

echo "Instalando en $DIR ..."
for slug in $SELECCION; do
  instalar "$slug"
done
echo ""
echo "Listo. Tu agente las descubre solas: no hay que registrar nada."
