---
name: swl-markitdown
description: >
  Convierte cualquier formato de documento a Markdown estructurado para su
  consumo por agentes SWL. Soporta PDF, DOCX, XLSX, PPTX, Jupyter, EPUB,
  ZIP, HTML, CSV, JSON, YouTube URLs y más. Wrapper sobre Microsoft MarkItDown
  (AutoGen Team). Usar cuando el Read tool no soporta el formato del archivo.
  Complementa WebFetch/agent-browser para URLs con contenido estructurado.
user-invocable: false
version: "1.0.0"
herramientasPermitidas: [Read, Bash, Glob, Grep, WebFetch]
evolved: false
fuente: "Microsoft AutoGen Team — markitdown v0.1.5 (2025)"
dependencias: "pip install markitdown[pdf,docx,pptx,xlsx]"
exclusiones:
  - "No cargar para archivos .md, .txt o código fuente — el Read tool los soporta directamente y es más rápido sin el overhead de conversión."
  - "No cargar para PDFs de ≤20 páginas sin tablas complejas — el Read tool con parámetro `pages:` es suficiente y no requiere la dependencia Python."
  - "No cargar para URLs web genéricas sin contenido estructurado de documento — WebFetch es el camino correcto para HTML; swl-markitdown agrega valor cuando el HTML tiene estructura de documento (Wikipedia, documentación técnica con tablas)."
  - "No cargar cuando el objetivo es buscar un patrón o string dentro de un archivo — Grep sobre el archivo original es más eficiente que convertir a Markdown primero."
evolvable: true  # default para skill estandar
---
# Skill: swl-markitdown

Proporciona conversión universal de documentos a Markdown para agentes SWL.
Es el puente entre archivos en formatos propietarios (DOCX, XLSX, PPTX, PDF)
y el contexto de los agentes que solo consumen texto Markdown.

## Árbol de decisión: qué herramienta usar

| Fuente | Herramienta | Por qué |
|--------|-------------|---------|
| `.md`, `.txt` (local) | `Read` | Directo, sin overhead |
| `.pdf` ≤ 20 páginas (local) | `Read` con `pages:` | Read soporta PDFs nativamente |
| `.pdf` > 20 páginas o con tablas | **`swl-markitdown`** | Extrae tablas como Markdown |
| `.docx`, `.pptx` (local) | **`swl-markitdown`** | Read no soporta estos formatos |
| `.xlsx`, `.xls` (local) | **`swl-markitdown`** | Convierte hojas a tablas Markdown |
| `.ipynb` Jupyter (local) | **`swl-markitdown`** | Read devuelve JSON crudo |
| `.csv` (local) | `Read` o **`swl-markitdown`** | Ambos funcionan; MarkItDown da tabla formateada |
| URL estática (HTML simple) | `WebFetch` | Más rápido, sin overhead |
| URL dinámica (SPA, JS) | `agent-browser` | Renderiza JavaScript |
| URL de YouTube | **`swl-markitdown`** | Transcripción automática sin LLM |
| URL de Wikipedia | `WebFetch` o **`swl-markitdown`** | Ambos; MarkItDown da estructura más limpia |
| ZIP con documentos mixtos | **`swl-markitdown`** | Descomprime y convierte recursivamente |

## Paso 0 — Verificar disponibilidad

```bash
# Verificar que markitdown está instalado y funcional
CLI_PATH=$(git rev-parse --show-toplevel 2>/dev/null)/scripts/vendor/markitdown/cli.py
python "$CLI_PATH" --check 2>/dev/null | grep "Estado:" || echo "NO_DISPONIBLE"
```

Si devuelve `NO_DISPONIBLE` o falta el cli.py:

```bash
# Instalar markitdown con soporte para formatos principales
pip install markitdown[pdf,docx,pptx,xlsx]

# Verificar instalación
python "$CLI_PATH" --check
```

Si pip no está disponible, reportar al usuario:
```
swl-markitdown requiere Python 3.10+ y pip.
  pip install markitdown[pdf,docx,pptx,xlsx]

El Read tool sigue disponible para .pdf (hasta 20 páginas), .txt y .md.
WebFetch y agent-browser siguen disponibles para URLs.
```

## Paso 1 — Conversión básica

### Archivo local

```bash
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
CLI_PY="$PROJECT_ROOT/scripts/vendor/markitdown/cli.py"

# Convertir archivo a Markdown
OUTPUT=$(python "$CLI_PY" "/ruta/absoluta/al/archivo.docx" 2>/dev/null)

# Verificar que la conversión fue exitosa (output no vacío)
if [ -n "$OUTPUT" ]; then
  echo "$OUTPUT"
else
  echo "CONVERSION_FAILED — usar Read tool como fallback"
fi
```

### Guardar directamente a raw/ del wiki

```bash
FECHA=$(date +%Y%m%d)
NOMBRE=$(basename "ARCHIVO_FUENTE" | sed 's/\.[^.]*$//' | sed 's/[^a-zA-Z0-9]/-/g' | cut -c1-50)
DESTINO="$PROJECT_ROOT/.planning/knowledge/raw/${FECHA}-${NOMBRE}.md"

python "$CLI_PY" "/ruta/al/archivo.docx" > "$DESTINO" 2>/dev/null && \
  echo "Guardado: $DESTINO" || \
  echo "Error de conversion"
```

### URL de YouTube (transcripción)

```bash
# Requiere: pip install youtube-transcript-api
OUTPUT=$(python "$CLI_PY" "https://www.youtube.com/watch?v=VIDEO_ID" 2>/dev/null)
[ -n "$OUTPUT" ] && echo "$OUTPUT" | head -50
```

## Paso 2 — Patrones de uso frecuentes

### Patrón A: Ingest masivo de documentación existente

```bash
PROJECT_ROOT=$(git rev-parse --show-toplevel)
CLI_PY="$PROJECT_ROOT/scripts/vendor/markitdown/cli.py"
RAW_DIR="$PROJECT_ROOT/.planning/knowledge/raw"
mkdir -p "$RAW_DIR"

# Procesar todos los archivos Office en un directorio
for FILE in ./docs/**/*.{docx,pptx,xlsx,pdf}; do
  [ -f "$FILE" ] || continue
  FECHA=$(date +%Y%m%d)
  NOMBRE=$(basename "$FILE" | sed 's/\.[^.]*$//' | sed 's/[^a-zA-Z0-9]/-/g' | cut -c1-50)
  DESTINO="$RAW_DIR/${FECHA}-${NOMBRE}.md"
  
  if python "$CLI_PY" "$FILE" > "$DESTINO" 2>/dev/null; then
    echo "[OK] $FILE -> $DESTINO"
  else
    echo "[SKIP] $FILE — formato no soportado o dependencia faltante"
    rm -f "$DESTINO"
  fi
done
```

### Patrón B: Comparar con Read tool para PDFs

```bash
# Para PDFs cortos (≤20 páginas): Read tool es más rápido
# Para PDFs largos o con tablas: swl-markitdown extrae mejor

CLI_PY="$(git rev-parse --show-toplevel)/scripts/vendor/markitdown/cli.py"
OUTPUT=$(python "$CLI_PY" "documento.pdf" 2>/dev/null)

# Verificar si hay tablas en el output (MarkItDown las extrae, Read no)
TIENE_TABLAS=$(echo "$OUTPUT" | grep -c "^|" || echo 0)
echo "Tablas detectadas: $TIENE_TABLAS"
```

### Patrón C: Preprocesamiento antes de invocar agente

```bash
# Convertir spec de requisitos DOCX a Markdown antes de pasarlo al planificador
CLI_PY="$(git rev-parse --show-toplevel)/scripts/vendor/markitdown/cli.py"

SPEC_MD=$(python "$CLI_PY" "requisitos/spec-v2.docx" 2>/dev/null)
if [ -z "$SPEC_MD" ]; then
  echo "ERROR: no se pudo convertir spec-v2.docx"
  exit 1
fi

# Guardar en .planning/ para que planificador-swl lo lea
echo "$SPEC_MD" > .planning/SPEC.md
echo "Spec convertida a .planning/SPEC.md — invocar planificador-swl"
```

### Patrón D: Notebook Jupyter para investigador-swl

```bash
# Jupyter notebooks contienen código, outputs y análisis mezclados
# swl-markitdown los convierte preservando celdas de código y markdown

CLI_PY="$(git rev-parse --show-toplevel)/scripts/vendor/markitdown/cli.py"
OUTPUT=$(python "$CLI_PY" "analisis/experimento.ipynb" 2>/dev/null)

# El output incluye:
# - Celdas markdown como texto normal
# - Celdas de código en bloques ```python
# - Outputs de celdas si están guardados en el notebook
echo "$OUTPUT" | head -100
```

## Paso 3 — Manejo de errores

| Código de salida | Causa | Acción |
|-----------------|-------|--------|
| `0` | Conversión exitosa | Usar el output |
| `1` | Formato no soportado | Intentar WebFetch o Read |
| `2` | Dependencia faltante | `pip install markitdown[<formato>]` |
| `3` | Error de conversión (archivo corrupto) | Reportar al usuario |
| `4` | Archivo no encontrado | Verificar la ruta |

```bash
CLI_PY="$(git rev-parse --show-toplevel)/scripts/vendor/markitdown/cli.py"
python "$CLI_PY" "archivo.docx" > output.md 2>error.log
EXIT_CODE=$?

case $EXIT_CODE in
  0) echo "OK — Markdown en output.md" ;;
  1) echo "Formato no soportado — usar alternativa" ;;
  2) cat error.log; echo "Instalar dependencia y reintentar" ;;
  3) cat error.log; echo "Archivo probablemente corrupto" ;;
  4) echo "Archivo no encontrado — verificar ruta" ;;
esac
```

## Paso 4 — Formatos soportados

```bash
# Ver todos los formatos con el estado de dependencias actual
python "$(git rev-parse --show-toplevel)/scripts/vendor/markitdown/cli.py" --formats
```

### Formatos siempre disponibles (sin dependencias extra)

| Formato | Extensión | Notas |
|---------|-----------|-------|
| HTML | `.html` `.htm` | Conversión completa con BeautifulSoup |
| Texto plano | `.txt` `.md` `.markdown` | Pass-through con detección de charset |
| CSV | `.csv` | Convertido a tabla Markdown |
| JSON | `.json` | Formateado estructuralmente |
| XML / RSS / Atom | `.xml` `.rss` `.atom` | Feeds formateados |
| ZIP | `.zip` | Descomprime y convierte todo el contenido |
| EPUB | `.epub` | Libros electrónicos |
| Jupyter | `.ipynb` | Celdas código + markdown |
| Wikipedia URLs | `wikipedia.org/*` | Extracción de contenido principal |

### Formatos con dependencias opcionales

| Formato | Dependencia | Instalar |
|---------|-------------|---------|
| PDF | `pdfminer.six`, `pdfplumber` | `pip install markitdown[pdf]` |
| Word (.docx) | `mammoth`, `lxml` | `pip install markitdown[docx]` |
| Excel (.xlsx) | `pandas`, `openpyxl` | `pip install markitdown[xlsx]` |
| Excel (.xls) | `pandas`, `xlrd` | `pip install markitdown[xls]` |
| PowerPoint (.pptx) | `python-pptx` | `pip install markitdown[pptx]` |
| YouTube | `youtube-transcript-api` | `pip install markitdown[youtube-transcription]` |

## Qué NO usar de MarkItDown en swl-ses

- **Azure Document Intelligence** — dependencia de nube, costo por página, conflicto con filosofía local-first
- **Audio transcription** (SpeechRecognition) — sin caso de uso en swl-ses
- **LLM Vision para imágenes** — Claude ya es multimodal; duplicidad
- **markitdown-ocr plugin** — requiere GPT-4o explícito; Claude lo haría en el contexto actual

## Integración con /swl:wiki

El skill es transparente para `/swl:wiki ingest`: cuando se detecta un formato
no-Markdown, el comando invoca `swl-markitdown` automáticamente antes de ingerir.

```bash
# El Paso I.1 del ingest decide la herramienta según extensión:
# .md .txt → Read tool
# .pdf .docx .xlsx .pptx .ipynb .epub .zip → swl-markitdown
# http:// https:// → WebFetch, con agent-browser como fallback
```

## Señales de que swl-markitdown completó correctamente

- Output tiene >100 palabras de contenido real (no solo metadatos)
- No hay mensajes de error en stderr
- El código de salida es 0
- El contenido tiene estructura Markdown (encabezados, tablas o listas)

## Señales de problema

- Output vacío con código 1 → el formato no está registrado (revisar extensión)
- Output vacío con código 2 → `pip install markitdown[<formato>]`
- Output solo de metadatos → el archivo puede estar protegido con contraseña
- Caracteres extraños → el archivo usa encoding no-UTF8; MarkItDown lo maneja internamente

## Cuándo NO cargar

- El archivo es `.md`, `.txt`, código fuente o cualquier texto plano — el Read tool lo lee directamente sin overhead de conversión ni dependencia Python.
- El archivo es un PDF de ≤20 páginas sin tablas complejas — el Read tool con `pages:` es suficiente; swl-markitdown agrega valor principalmente para tablas que Read no puede extraer estructuradamente.
- Se quiere acceder al contenido de una URL web genérica — WebFetch es el camino; swl-markitdown en URLs solo vale cuando son páginas con estructura de documento (Wikipedia, docs técnicas con tablas).
- El objetivo es encontrar un patrón específico dentro del documento — Grep sobre el archivo original es más directo que convertir a Markdown primero para luego buscar en el resultado.

## Gotchas / Errores comunes no obvios

- **`python "$CLI_PY" archivo.docx` ejecutado sin `cd "$VENDOR_DIR"` previo**: el script falla con `ImportError` porque los módulos auxiliares se importan con rutas relativas al directorio del vendor. Causa: ejecutar el CLI desde el directorio de trabajo del proyecto en lugar del vendor. Solución: siempre ejecutar `CLI_PY` como `cd "$VENDOR_DIR" && python "$CLI_PY" <ruta-absoluta-al-archivo>` — la ruta del archivo debe ser absoluta para que no sea relativa al vendor.
- **PDF con contraseña devuelve output vacío con código 0**: el agente asume que la conversión fue exitosa y pasa texto vacío al siguiente paso. Causa: algunos PDFs protegidos devuelven salida vacía sin código de error. Solución: verificar que el output tiene más de 100 palabras de contenido real (la sección "Señales de que swl-markitdown completó correctamente" lo documenta) antes de usar el output.
- **Excel (.xls) no convertido aunque markitdown[xlsx] está instalado**: el agente instala `markitdown[xlsx]` pero `.xls` requiere `xlrd` además de `openpyxl`. Causa: la tabla de dependencias del skill muestra extensiones diferentes para `.xlsx` y `.xls`. Solución: para `.xls` instalar `markitdown[xls]` (incluye `xlrd`), no `markitdown[xlsx]` — son extras distintos.
- **Ingest masivo con `for FILE in ./docs/**/*.docx` falla en bash sin globstar**: el globbing `**` no expande recursivamente en bash por defecto. Causa: `globstar` no está habilitado. Solución: usar `find ./docs -name "*.docx" -type f` en lugar de `**` glob, o habilitar explícitamente `shopt -s globstar` antes del loop — el Patrón A del skill usa `**` pero no activa globstar.
