Metadata-Version: 2.5
Name: tecsas3-cli
Version: 0.6.1
Summary: CLI del Ecosistema Salud TecsaS3 — multi-tenant, agent-friendly, orientado a salud rural colombiana.
Project-URL: Homepage, https://github.com/TecsaS3/tecsas3-cli
Project-URL: Documentation, https://docs.tecsas3.com/es
Author-email: Tecsa S3 <dev@tecsas3.com>
License: MIT
License-File: LICENSE
Keywords: cli,fhir,healthcare,hl7,rural,salud,tecsas3
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: keyring>=24
Requires-Dist: pydantic-settings>=2.2
Requires-Dist: pydantic>=2.6
Requires-Dist: rich<16,>=13.7
Requires-Dist: ruamel-yaml>=0.18
Requires-Dist: tomli>=2.0; python_version < '3.11'
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Provides-Extra: etl
Requires-Dist: pyarrow>=15; extra == 'etl'
Description-Content-Type: text/markdown

# tecsas3-cli

CLI del **Ecosistema Salud TecsaS3** (`salud_rural_segura`). Multi-tenant, agent-friendly, orientado a salud rural colombiana. Diseñado para uso humano (tablas `rich`) y automatizable por agentes LLM (JSON determinista, modo `--no-input`, `agent-info`).

> Status: **Beta** (Fase 4 — v0.4.0). Cubre lectura, escrituras, batch, dashboards, reportes, exports, IA v1/v2, pipelines declarativos, **HCE**, **FHIR**, golden tests y skill universal para agentes.

---

## Instalación (desarrollo)

```powershell
cd cli
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"
pre-commit install
tecsas3 --version
```

---

## Quickstart — perfil `demo`

### 1. Config global (una vez)

```powershell
mkdir $HOME\.tecsas3
@'
[profile.demo]
base_url = "https://dev.tecsas3.com"
tenant = "tecsas3-demo"
tenant_org = 1
plan_id = 4
auth_scheme = "token"
timeout = 30.0
verify_tls = true
'@ | Set-Content $HOME\.tecsas3\config.toml
```

O por comando:

```powershell
tecsas3 util config set tenant tecsas3-demo
tecsas3 util config set tenant_org 1
tecsas3 util config set plan_id 4
```

### 2. Login

```powershell
tecsas3 --profile demo auth login -u TU_USUARIO -p
# (token se guarda en keyring; fallback ~/.tecsas3/credentials 0600)
```

Alternativa no interactiva (CI / agentes):

```powershell
$env:TECSAS3_PASSWORD = "..."
tecsas3 auth login -u user --no-input
```

### 3. Verificar

```powershell
tecsas3 ping
tecsas3 auth whoami
tecsas3 riesgo niveles
tecsas3 paciente list --page-size 5
```

### 4. Modo JSON (agentes / pipes)

```powershell
tecsas3 paciente list --page-size 5 --json | jq '.results[0].id'
tecsas3 util agent-info --json
tecsas3 paciente search --q Godoy --json
```

---

## Uso por agentes de código

El CLI expone autodescubrimiento estructurado para LLMs y un skill universal compatible con Claude Code, OpenCode, OpenClaw, Hermes y otros.

### Autodescubrimiento

```powershell
tecsas3 util agent-info        # descripción de recursos y golden paths
tecsas3 util schema            # OpenAPI 3 del backend (alias raíz: tecsas3 schema)
tecsas3 --help                 # ayuda completa
```

### Skill universal

Copia el skill a la carpeta de skills de tu agente:

```powershell
# OpenCode / Hermes / OpenClaw
 copy ..\docs\skills\tecsas3-cli\SKILL.md .opencode\skills\tecsas3-cli\SKILL.md

# Claude Code
 copy ..\docs\skills\tecsas3-cli\SKILL.md .claude\skills\tecsas3-cli\SKILL.md
```

### Golden paths para agentes

```powershell
# 1. Descubrir
 tecsas3 util agent-info --json

# 2. Conectar
 tecsas3 util ping --json
 tecsas3 auth whoami --json

# 3. Operar
 tecsas3 --json --no-input paciente list --page-size 5
 tecsas3 --json --no-input hce historia-list --paciente 123
 tecsas3 --json --no-input fhir paciente-list --limit 10

# 4. Orquestar
 tecsas3 --json --no-input pipe run triage.yml
```

Ver documentación completa en [`docs/GUIA_CLI_TECSA_S3.md`](../docs/GUIA_CLI_TECSA_S3.md).

---

## Mapa real de endpoints (verificado contra `dev.tecsas3.com`)

| Recurso | Acción | Método + Path | Notas |
|---|---|---|---|
| `auth` | login (token) | `POST /api/autenticar/` | DRF Token legacy. Body `{username, password}` → `{token}` |
| `auth` | login (jwt) | `POST /api/token/` | SimpleJWT → `{access, refresh}` |
| `auth` | whoami | `GET /persona/me/` | Requiere tenant headers |
| `util` | ping | `GET /health/` | No auth, no tenant |
| `util` | schema | `GET /api/schema/?format=json` | OpenAPI 3.0.3 (962 paths) |
| `paciente` | list | `GET /paciente/paciente/?page=&page_size=&nivel_riesgo=` | Paginado DRF. Columnas aplanadas desde `persona`. |
| `paciente` | get | `GET /paciente/paciente/{id_entero}/` | **`lookup_field='id'` (entero), NO UUID**. Soporta `--uuid`. |
| `paciente` | search | `GET /paciente/busqueda/?q=` | Búsqueda unificada. Sin `?q=` → 400. |
| `paciente` | create | `POST /paciente/paciente/` | Requiere `persona` (int FK), `familia` (int FK), `codigo_eapb` (objeto). |
| `paciente` | update | `PATCH /paciente/paciente/{id}/` | Soporta `--uuid` y `--data`. |
| `persona` | me | `GET /persona/me/` | |
| `persona` | list/search | `GET /persona/?search=` | Sin paginación |
| `familia` | list | `GET /familia/familia/?page=&page_size=` | Paginado |
| `familia` | get | `GET /familia/familia/{id}/` | |
| `agente` | list | `GET /agente/api/agente/?page=&page_size=` | Paginado, incluye `planes` del usuario |
| `agente` | get | `GET /agente/api/agente/{uuid}/` | UUID |
| `agente` | create | `POST /agente/api/agente/` | |
| `agente` | ubicacion | `GET /agente/api/locations/` | Última posición GPS |
| `riesgo` | niveles | `GET /nivel-riesgo/` | **En raíz**, NO `/riesgo/` |
| `riesgo` | patologias | `GET /patologia/` | En raíz |
| `riesgo` | registros | `GET /registro-riesgo/?paciente=&color_semaforo=` | En raíz |
| `examen` | list | `GET /lab/api/examen/?paciente=` | **¡Sin paginación DRF!** Trunca client-side con `--limit` |
| `examen` | create | `POST /lab/api/examen/` | |
| `batch` | run | — | Ejecuta operaciones JSON/JSONL secuencialmente. |
| `dashboard` | resumen | `GET /api/dashboard/` | Plan activo, conteos, series temporales |
| `dashboard` | metricas | `GET /platform/api/v1/dashboard/{tipo}/` | `summary` \| `activity` \| `tenant` \| `access` |
| `dashboard` | paciente-stats | `GET /paciente/estadisticas/resumen/` | Estadísticas agregadas de pacientes |
| `agente` | heatmap | `GET /agente/api/locations/` | Agregación client-side por celdas lat/lon |
| `agente` | kpi / kpi-detalle | `GET /agente/api/kpi_agentes/` | KPIs globales o por agente |
| `agente` | valoracion-dashboard | `GET /agente/api/agentes/kpi/` | Dashboard de valoraciones |
| `agente` | toggle-activo | `POST /agente/api/agente/{id}/toggle-activo/` | Activa/desactiva agente |
| `agente` | eventos-list / logs-list | `GET /agente/api/eventos/` / `logs-agente/` | Actividad de agentes |
| `agente` | autocomplete | `GET /agente/api/autocomplete/{model}/` | Autocomplete de modelos |
| `riesgo` | patologia-list/get | `GET /patologia/` | Catálogo de patologías (raíz, no `/riesgo/`) |
| `riesgo` | nivel-riesgo-list/get | `GET /nivel-riesgo/` | Niveles de riesgo HL7 |
| `riesgo` | registro-list/get/create | `GET/POST /registro-riesgo/` | Registros de riesgo de pacientes |
| `riesgo` | registro-por-agente | `GET /registro-riesgo-por-agente/` | Registros del agente autenticado |
| `ia` | chat-list | `GET /api/ia/chat/` | Chats de consulta IA |
| `ia` | v2-chat-list/mensaje/analyze | `GET/POST /api/ia/v2/chats/...` | Chat IA versión 2 |
| `ia` | v2-patient-search / v2-family-search | `GET /api/ia/v2/patients/search/` | Búsqueda para contexto IA |
| `ia` | v2-consulta-libre | `POST /api/ia/v2/consultations/free/` | Consulta libre sin chat |
| `ia` | ai-query | `POST /api/ia/ia/ai-query/` | Consulta simple a IA |
| `ia` | examen-search / examen-prompt | `GET/POST /api/ia/examenes/` | Exámenes para chat IA |
| `ia` | chat-get | `GET /api/ia/chat/{id}/detalle/` | Detalle con mensajes (`--detalle`) |
| `ia` | chat-create | `POST /api/ia/chat/` | |
| `ia` | chat-export | `POST /api/ia/chat/{id}/exportar_{fmt}/` | `markdown` \| `pdf` \| `word` \| `email` \| `fhir`. Requiere `--mensaje-id` |
| `ia` | chat-exports | `GET /api/ia/chat/{id}/historial_exportaciones/` | |
| `ia` | auditorias | `GET /api/ia/auditorias/` | `--stats` para métricas globales |
| `reportes` | list | `GET /dashboard/api/reports/scheduled/` | Reportes programados |
| `reportes` | create | `POST /dashboard/api/reports/scheduled/` | |
| `reportes` | execute | `POST /dashboard/api/reports/scheduled/{id}/execute_now/` | Ejecutar ahora |
| `reportes` | executions | `GET /dashboard/api/reports/executions/` | |
| `reportes` | notifications | `GET /dashboard/api/reports/notifications/` | `--retry <id>` para reintentar |
| `export` | paciente/examen/agente | — | Volcados paginados a CSV/JSON/Parquet |
| `hce` | historias | `GET /hce/api/historias/` | Lista plana por paciente |
| `hce` | notas | `GET /hce/api/notas/` | Notas clínicas de una historia |
| `hce` | diagnosticos | `GET /hce/api/diagnosticos/` | Diagnósticos CIE-10 |
| `hce` | ordenes | `GET /hce/api/ordenes/` | Órdenes médicas |
| `hce` | formulas | `GET /hce/api/formulas/` | Fórmulas / prescripciones |
| `hce` | documentos | `GET /hce/api/documentos/` | Documentos adjuntos |
| `hce` | consentimientos | `GET /hce/api/consentimientos/` | Consentimientos informados |
| `hce` | escalas | `GET /hce/api/escalas/` | Escalas clínicas |
| `hce` | icd buscar | `GET /hce/api/icd/buscar/?q=` | Búsqueda CIE-10 |
| `fhir` | pacientes | `GET /eps_fhir/eps/fhir/pacientes/` | Recurso `Patient` FHIR R4 |
| `fhir` | diagnostic-reports | `GET /eps_fhir/eps/fhir/diagnostic-reports/` | `DiagnosticReport` |
| `fhir` | observations | `GET /eps_fhir/eps/fhir/observations/` | `Observation` |
| `fhir` | agents | `GET /eps_fhir/eps/fhir/agents/` | Agentes como `Practitioner`/`Organization` |
| `fhir` | organizations | `GET /eps_fhir/eps/fhir/organizations/` | `Organization` (EAPB) |
| `fhir` | bundle | `POST /eps_fhir/eps/fhir/bundle/` | `Bundle` collection/transaction |
| `fhir` | export | — | Exportación de recurso FHIR a archivo |
| `fhir` | logs | `GET /eps_fhir/eps/fhir/logs/` | Logs de integración FHIR |

### Quirks documentados

1. **Multi-tenant headers obligatorios** en paths protegidos (`/api/`, `/paciente/`, `/familia/`, `/agente/api/`, `/ia/`, `/lab/api/`, etc.). El CLI envía `X-Tenant-Slug`, `X-Tenant-Org-ID`, `X-Plan-ID` automáticamente desde el perfil.
2. **`paciente/paciente/{id}/` usa ID interno entero**, no el UUID. Para resolver UUID → ID interno, usa `paciente search --uuid <uuid>` o `paciente get --uuid <uuid>`.
3. **POST/PATCH de paciente esperan FKs enteras**, no objetos persona anidados. Ejemplo: `--persona-id 5267 --familia-id 1959 --eapb-codigo FAMISANAR --eapb-nombre FAMISANAR`.
4. **`riesgo` vive en raíz** (`/nivel-riesgo/`, `/patologia/`, `/registro-riesgo/`). Las URLs `/riesgo/*` devuelven 404.
5. **`/lab/api/examen/` no pagina** (ViewSet sin `pagination_class`). El CLI limita client-side.
6. **`/paciente/busqueda/` rechaza búsquedas vacías** con 400. Siempre pasa `--q` o `--uuid`.
7. **Auth dual**: soporta Token legacy (`Authorization: Token <...>`) y JWT (`Authorization: Bearer <...>`). Auto-detección: si el token empieza con `eyJ`, se asume JWT.
8. **HCE** vive bajo `/hce/api/`; la respuesta de historias es una lista plana (no paginada).
9. **FHIR** vive bajo `/eps_fhir/eps/fhir/`. El endpoint `/eps_fhir/eps/patients/` devuelve 500 (bug backend); usar `fhir paciente-list`.

---

## Escrituras y batch (Fase 1)

```powershell
# Actualizar riesgo de paciente por UUID
tecsas3 paciente update --uuid 673b20ba-7784-468c-8b74-40db428ed267 --data '{"nivel_riesgo":"amarillo"}'

# Crear examen
tecsas3 examen create --paciente 2760 --descripcion "Hemoglobina"

# Crear agente
tecsas3 agente create --numero-ebs EBS999 --esta-activo

# Batch desde JSON array
tecsas3 batch run operaciones.json
```

Formatos soportados por `batch run`:
- JSON array: `[{"op":"paciente.create","payload":{...}}, ...]`
- JSON Lines: una operación por línea.
- JSON object: `{"operations": [...]}`

Operaciones soportadas en batch: `paciente.create/update/get`, `examen.create/list`, `agente.create/get`.

## Reportes, dashboard, IA y exports ETL (Fase 2)

```powershell
# Dashboard del tenant
tecsas3 dashboard resumen --json

# Métricas de plataforma
tecsas3 dashboard metricas --tipo summary

# Estadísticas de pacientes
tecsas3 dashboard paciente-stats

# Heatmap de ubicaciones de agentes (agregado client-side)
tecsas3 agente heatmap --precision 2 --exclude-invalid --json

# KPIs y actividad de agentes
tecsas3 agente kpi --json
tecsas3 agente kpi-detalle <uuid-agente> --json
tecsas3 agente valoracion-dashboard --json
tecsas3 agente eventos-list --agente <uuid-agente> --page-size 5 --json
tecsas3 agente logs-list --page-size 5 --json
tecsas3 agente autocomplete perfil_personal --q juan

# Riesgo
tecsas3 riesgo patologia-list
tecsas3 riesgo nivel-riesgo-list
tecsas3 riesgo registro-list --color rojo --agente <uuid-agente>
tecsas3 riesgo registro-por-agente --color verde --json

# Chats IA
tecsas3 ia chat-list
tecsas3 ia chat-get <uuid> --detalle
tecsas3 ia chat-create --tipo general
tecsas3 ia chat-export <uuid> markdown --mensaje-id <msg-id>

# Reportes programados
tecsas3 reportes list
tecsas3 reportes execute 7
tecsas3 reportes executions

# Exports ETL (CSV / JSON / Parquet)
tecsas3 export paciente --riesgo rojo --file pacientes_rojo.csv
tecsas3 export examen --paciente 2760 --file examenes.json
tecsas3 export agente --file agentes.parquet   # requiere pip install 'tecsas3-cli[etl]'

# Pipelines declarativos (encadenar recursos)
tecsas3 pipe init triage.yml          # genera plantilla
tecsas3 pipe validate triage.yml      # valida sintaxis
tecsas3 pipe run triage.yml --json    # ejecuta; usa ${step.path} para encadenar
```

## HCE y FHIR (Fase 4)

```powershell
# HCE - Historia clínica electrónica
tecsas3 hce historia-list --paciente 123 --json
tecsas3 hce nota-list --historia 456 --json
tecsas3 hce diagnostico-list --historia 456 --json
tecsas3 hce orden-list --historia 456 --json
tecsas3 hce formula-list --historia 456 --json
tecsas3 hce icd-search diabetes --json

# FHIR R4
tecsas3 fhir paciente-list --limit 10 --json
tecsas3 fhir paciente-get 123 --json
tecsas3 fhir diagnostic-report-list --patient-id 123 --json
tecsas3 fhir resultado-examen-list --patient-id 123 --json
tecsas3 fhir export-fhir-bundle --type collection --entries bundle.json --json
tecsas3 fhir export --formato fhir --output paciente_123.fhir.json
```

## Semáforo HL7 de riesgo

| Color | Nivel | Código HL7 |
|---|---|---|
| blanco | Negligible | 0 |
| verde | Bajo | 1 |
| amarillo | Moderado | 2 |
| naranja | Alto | 3 |
| rojo | Certero | 4 |
| negro | Desconocido | 5 |
| azul | No aplica | 6 |

---

## Códigos de salida

| Código | Significado |
|---|---|
| 0 | Éxito |
| 1 | Error de negocio (404, 400, validación) |
| 2 | Error del backend (5xx) |
| 3 | Error de autenticación (401/403) |
| 4 | Error de configuración (falta tenant/base_url) |
| 5 | Error de red (timeout, DNS, TLS) |
| 6 | Error de uso (comando u opción desconocida) |
| 99 | Error inesperado (ver `--debug`) |

---

## Seguridad y PHI

- **HTTPS obligatorio** (`verify_tls=true` por defecto; `--insecure` sólo con `--debug`).
- **Tokens** en `keyring` del sistema; fallback `~/.tecsas3/credentials` con permisos `0600`.
- **Redacción PHI** en logs por defecto (`nombres`, `cedula`, `diagnóstico`, etc.). Desactivar sólo con `--debug --redact-phi=false` (uso estrictamente de depuración).
- **Multi-tenant**: nunca se omite `X-Tenant-*` en paths protegidos.
- **Rotación**: `tecsas3 auth logout` borra tokens locales. Para revocación server-side, usar admin Django o blacklist JWT.

---

## Estructura

```
cli/
├── pyproject.toml
├── README.md
├── .gitignore
├── data/
│   └── openapi.json          # gitignored (PHI posible)
└── src/tecsas3_cli/
    ├── __init__.py
    ├── __main__.py
    ├── cli.py                # entry Typer
    ├── runtime.py            # agregación Settings+Client+Output
    ├── config.py             # perfiles, env, TOML
    ├── http_client.py        # httpx + auth + tenant + errores
    ├── auth.py               # keyring + fallback 0600
    ├── output.py             # table/json/yaml/csv
    ├── errors.py             # jerarquía TecsaError + factory HTTP
    ├── redaction.py          # PHI redactor
    ├── agent_info.py         # autodescripción para LLMs
    └── resources/
        ├── auth.py           # login/whoami/logout
        ├── util.py           # ping/schema/agent-info/config
        ├── paciente.py
        ├── persona.py
        ├── familia.py
        ├── agente.py
        ├── riesgo.py
        ├── examen.py
        ├── dashboard.py      # dashboards y estadísticas
        ├── ia.py             # chats/auditorías IA
        ├── reportes.py       # reportes programados
        ├── export.py         # volcados ETL
        ├── pipeline.py       # orquestador declarativo YAML
        ├── hce.py            # Historia Clínica Electrónica
        └── fhir.py           # Recursos FHIR R4
```

---

## Desarrollo

```powershell
ruff check . && ruff format .
mypy src/tecsas3_cli
pytest
```

---

## Roadmap

| Fase | Estado | Cobertura |
|---|---|---|
| 0 — Fundaciones | ✅ Hecho | scaffold, config, http, auth, resources básicos, smoke dev |
| 1 — MVP | ✅ Hecho | escrituras (create/update paciente, examen), `paciente get --uuid`, batch JSON |
| 2 — Ampliación | ✅ Hecho | dashboard, métricas, reportes, heatmap, IA chat/auditorías, exports ETL |
| 3 — Agentes | ✅ Hecho | IA v2, `pipe`, golden tests, modo `--json --no-input` |
| 4 — HCE + FHIR | ✅ Hecho | recursos `hce` y `fhir`, 69 tests verdes |
| 5 — Distribución | ⏳ | PyPI privado, autocompletado, hardening adicional, snapshots JSON |
