¿Qué hace crm.cli?
Un CRM sin servidor, minimalista y basado en línea de comandos. Contactos, negocios y pipeline en un único archivo SQLite: consultable desde tu terminal, componible con herramientas Unix y montable como un sistema de archivos virtual.
Instalación
curl -fsSL https://raw.githubusercontent.com/dzhng/crm.cli/main/install.sh | sh
Esto descarga el binario precompilado en ~/.local/bin e instala las dependencias de montaje (FUSE en Linux, Rust toolchain en macOS para NFS).
Después de la instalación, asegúrate de que ~/.local/bin esté en tu PATH:
export PATH="$HOME/.local/bin:$PATH"
Verifica:
crm --version
Configuración
Opcional. Crea crm.toml en la raíz de tu proyecto o en ~/.crm/config.toml:
[database]
path = "~/.crm/crm.db"
[pipeline]
stages = ["lead", "qualified", "proposal", "negotiation", "closed-won", "closed-lost"]
won_stage = "closed-won"
lost_stage = "closed-lost"
[defaults]
format = "table"
[phone]
default_country = "US"
display = "international"
[mount]
default_path = "~/crm"
La configuración se descubre automáticamente recorriendo hacia arriba desde el directorio actual. Sobrescribe con --config <ruta> o la variable de entorno CRM_CONFIG.
Banderas globales
Todos los comandos aceptan:
--db <ruta>— Ruta de la base de datos SQLite (por defecto:~/.crm/crm.db, env:CRM_DB)--format <fmt>— Formato de salida:table,json,csv,tsv,ids--config <ruta>— Ruta del archivo de configuración TOML--no-color— Desactiva la salida coloreada
Contactos
Crear un contacto
crm contact add --name "Juan Pérez" \
--email [email protected] \
--phone "+52-55-1234-5678" \
--linkedin linkedin.com/in/juanperez \
--company "Empresa SA" \
--tag cliente-potencial \
--set cargo=CTO
Todas las banderas son opcionales excepto --name. Los teléfonos se normalizan al formato E.164, las URLs de LinkedIn se extraen a identificadores, y las empresas se crean automáticamente si no existen. Las banderas --email, --phone, --company, --tag y --set se pueden repetir.
Banderas de redes sociales: --linkedin, --x, --bluesky, --telegram. Todas aceptan identificadores o URLs completas.
Listar contactos
crm contact list
crm contact list --tag cliente-potencial --company "Empresa SA"
rm contact list --filter "cargo~=CTO AND company=Empresa" --sort name --limit 20
crm contact list --format json | jq '.[].name'
Operadores de filtro: =, !=, ~= (contiene), >, <. Combínalos con AND / OR.
Mostrar un contacto
Búsqueda por ID, correo, teléfono o red social:
crm contact show ct_01J8ZVXB3K...
crm contact show [email protected]
crm contact show "+525512345678"
crm contact show juanperez # Identificador de LinkedIn
Editar un contacto
crm contact edit [email protected] --name "Juan García"
crm contact edit "+525512345678" --add-email [email protected] --rm-tag etiqueta-vieja
crm contact edit juanperez --add-company "Nueva Corp" --set cargo=CEO --unset source
Banderas de añadir/eliminar: --add-email, --rm-email, --add-phone, --rm-phone, --add-company, --rm-company, --add-tag, --rm-tag. Las redes sociales se asignan directamente: --linkedin, --x, --bluesky, --telegram.
Eliminar un contacto
crm contact rm [email protected]
crm contact rm "+525512345678" --force # saltar confirmación
crm contact rm juanperez # por red social
Fusionar contactos
Fusiona dos contactos en uno. El primer contacto sobrevive, el segundo se absorbe y se elimina. Acepta cualquier tipo de referencia:
crm contact merge ct_01A... ct_01B...
crm contact merge [email protected] [email protected]
crm contact merge "+525512345678" "+525598765432"
crm contact merge juanperez juan-garcia-linkedin
Combina correos, teléfonos, empresas, etiquetas, campos personalizados y reenlaza todos los negocios y actividades.
Empresas
Crear una empresa
crm company add --name "Empresa SA" \
--website empresa.com \
--phone "+52-800-555-0000" \
--tag corporativo \
--set sector=Saas
Las banderas --website, --phone, --tag, --set se pueden repetir.
Listar / mostrar / editar / eliminar
crm company list --tag corporativo
crm company show empresa.com # por sitio web
crm company show "+528005550000" # por teléfono
crm company edit empresa.com --name "Empresa Inc" --add-website empresa.io
crm company rm empresa.com --force
Fusionar empresas
crm company merge co_01A... co_01B...
crm company merge empresa.com empresa.io
crm company merge "+528005550000" "+528005550001"
Reenlaza todos los contactos y negocios de la segunda a la primera.
Negocios
Crear un negocio
crm deal add --title "Acme Enterprise" \
--value 50000 \
--stage qualified \
--contact [email protected] \
--company empresa.com \
--expected-close 2026-06-15 \
--probability 60 \
--tag corporativo
Los contactos y empresas se crean automáticamente si no existen. Las banderas --contact y --tag se pueden repetir.
Listar negocios
crm deal list --stage qualified --min-value 10000
crm deal list --contact [email protected] --sort value --reverse
crm deal list --format ids | wc -l # contar negocios
Mover un negocio a través del pipeline
crm deal move dl_01... --stage proposal --note "Envié presentación de precios"
crm deal move dl_01... --stage closed-won --note "Firmó contrato por 2 años"
Los cambios de etapa se registran como actividad con marcas de tiempo. Usa deal move, no deal edit --stage.
Editar / eliminar
crm deal edit dl_01... --value 75000 --add-contact [email protected] --probability 80
crm deal rm dl_01... --force
Resumen del pipeline
crm pipeline
Muestra el recuento, el valor total y el valor ponderado por etapa.
Registro de actividades
Registrar una actividad
crm log note "Café con Juan, hablamos sobre la expansión en el Q3" --contact [email protected]
crm log call "Demostración del producto, quiere una propuesta" --contact [email protected] --deal dl_01...
crm log meeting "Revisión trimestral" --company empresa.com --at 2026-04-01
crm log email "Envié seguimiento de precios" --contact [email protected] --set canal=saliente
Tipos: note, call, meeting, email. Los contactos y empresas se crean automáticamente. --contact se puede repetir. --at sobrescribe la marca de tiempo.
Listar actividades
crm activity list --contact [email protected] --since 2026-01-01
crm activity list --type call --limit 10
crm activity list --deal dl_01... --format json
Etiquetas
crm tag [email protected] cliente-potencial corporativo # añadir etiquetas
crm untag [email protected] etiqueta-vieja # eliminar etiquetas
crm tag list # todas las etiquetas con recuento
crm tag list --type contact # solo etiquetas de contacto
Las etiquetas funcionan en contactos, empresas y negocios.
Búsqueda
Búsqueda exacta por palabra clave (FTS5)
crm search "empresa CTO"
crm search "juan" --type contact
Búsqueda difusa / semántica
crm find "fintech startup Madrid"
crm find "ese CTO que conocí en la conferencia" --limit 5 --threshold 0.3
Reconstruir el índice de búsqueda
crm index rebuild
crm index status
El índice se actualiza automáticamente al escribir. Solo se necesita reconstrucción manual después de una corrupción.
Detección de duplicados
crm dupes
crm dupes --type contact --threshold 0.5
crm dupes --type company --limit 20
Usa similitud combinada de Levenshtein + Dice coefficient. Detecta: nombres similares, correos compartidos, teléfonos compartidos, sitios web compartidos, redes sociales compartidas. Revisa y luego fusiona:
crm dupes --type contact
# → Juan Pérez ↔ J. Pérez: nombre similar, correo compartido
crm contact merge ct_01A... ct_01B...
Informes
crm report pipeline # recuentos y valores por etapa
crm report activity --period 30d --by type # volumen de actividad
crm report stale --days 14 --type contact # sin actividad reciente
crm report conversion --since 2026-01-01 # tasas de etapa a etapa
crm report velocity --won-only # tiempo por etapa
crm report forecast --period 2026-Q2 # pronóstico ponderado
crm report won --period 90d # resumen de cerrados-ganados
crm report lost --period 90d # resumen de cerrados-perdidos
Importar / Exportar
Importar desde CSV o JSON
crm import contacts clientes.csv
crm import contacts clientes.json --update # actualizar existentes por coincidencia de correo
crm import companies empresas.csv --dry-run
crm import deals negocios.csv --skip-errors
cat data.json | crm import contacts - # importar desde stdin
Encabezados CSV: name, email/emails, phone/phones, company/companies, tags, linkedin, x, bluesky, telegram. Las columnas no reconocidas se convierten en campos personalizados.
Exportar
crm export contacts --format csv > contactos.csv
crm export companies --format json > empresas.json
crm export deals --format tsv
crm export all --format json > respaldo-completo.json
Sistema de archivos virtual (montaje)
Monta el CRM como un sistema de archivos vivo de lectura/escritura. Cualquier herramienta que lea archivos obtiene acceso completo al CRM: agentes de IA, grep, jq, vim, scripts.
Montar
crm mount ~/crm
crm mount ~/crm --readonly
En Linux usa FUSE. En macOS usa un servidor NFS v3 (sin necesidad de extensiones de kernel).
Estructura del sistema de archivos
~/crm/
├── llm.txt # Instrucciones para agentes de IA
├── contacts/
│ ├── ct_01...juan-perez.json # Archivos JSON de contacto
│ ├── _by-email/ # Búsqueda por correo
│ ├── _by-phone/ # Búsqueda por teléfono E.164
│ ├── _by-linkedin/ # Búsqueda por identificador de LinkedIn
│ ├── _by-x/ # Búsqueda por identificador de X
│ ├── _by-company/ # Agrupado por empresa
│ └── _by-tag/ # Agrupado por etiqueta
├── companies/
│ ├── co_01...empresa-sa.json
│ ├── _by-website/
│ ├── _by-phone/
│ └── _by-tag/
├── deals/
│ ├── dl_01...acme-enterprise.json
│ ├── _by-stage/
│ ├── _by-company/
│ └── _by-tag/
├── activities/
│ ├── _by-contact/
│ ├── _by-company/
│ ├── _by-deal/
│ └── _by-type/
├── reports/ # Análisis precalculados
│ ├── pipeline.json
│ ├── forecast.json
│ ├── stale.json
│ ├── conversion.json
│ ├── velocity.json
│ ├── won.json
│ └── lost.json
├── pipeline.json # Resumen rápido del pipeline
├── tags.json # Todas las etiquetas con recuento
└── search/ # Búsqueda leyendo archivos
└── <consulta>.json # cat search/"empresa CTO".json
Leer a través del sistema de archivos
ls ~/crm/contacts/
cat ~/crm/contacts/ct_01...juan-perez.json | jq .
cat ~/crm/contacts/_by-email/[email protected]
cat ~/crm/deals/_by-stage/qualified/
cat ~/crm/reports/forecast.json
cat ~/crm/search/"negocios empresariales".json
Escribir a través del sistema de archivos
# Crear un contacto
echo '{"name":"Roberto López","emails":["[email protected]"]}' > ~/crm/contacts/new.json
# Actualizar (leer → modificar → escribir de nuevo)
cat ~/crm/contacts/ct_01...juan-perez.json | jq '.tags += ["vip"]' > ~/crm/contacts/ct_01...juan-perez.json
# Eliminar
rm ~/crm/contacts/ct_01...juan-perez.json
Desmontar
crm unmount ~/crm
Exportación estática (sin necesidad de montaje)
crm export-fs ./crm-snapshot
Exporta la misma estructura de directorios como una copia estática, útil en contenedores o entornos aislados donde FUSE no está disponible.
Operaciones por lotes
Usa --format ids para canalizar a otros comandos:
# Etiquetar todos los contactos de Acme como corporativos
crm contact list --company "Acme Corp" --format ids | xargs -I{} crm tag {} enterprise
# Mover todos los negocios calificados de más de $50k a propuesta
crm deal list --stage qualified --min-value 50000 --format ids | \
xargs -I{} crm deal move {} --stage proposal
# Eliminar todos los contactos inactivos
crm report stale --days 90 --type contact --format ids | xargs -I{} crm contact rm {} --force
Campos personalizados
Todas las entidades admiten campos arbitrarios clave-valor:
crm contact add --name "Juan" --set cargo=CTO --set fuente=conferencia
crm contact edit [email protected] --set "json:puntaje=85" --set "json:verificado=true"
crm contact edit [email protected] --unset fuente
crm contact list --filter "cargo~=CTO"
Prefijo con json: para valores tipados (números, booleanos, arreglos).
Hooks
Configura hooks de shell en crm.toml que se ejecutan en mutaciones:
[hooks]
post-contact-add = "~/.crm/hooks/notificar-slack.sh"
post-deal-stage-change = "~/.crm/hooks/negocio-movido.sh"
pre-contact-rm = "~/.crm/hooks/confirmar-eliminar.sh"
Los datos de la entidad se pasan como JSON en stdin. Los pre-hooks abortan con un código de salida distinto de cero.
Hooks disponibles: {pre,post}-{contact,company,deal}-{add,edit,rm}, {pre,post}-deal-stage-change, {pre,post}-activity-add.
Consejos para agentes de IA
- Monta primero:
crm mount ~/crmte da acceso al sistema de archivos — lee archivos JSON directamente en lugar de ejecutar comandos CLI - Lee
llm.txt: El punto de montaje contienellm.txtcon documentación de estructura y consejos - Usa directorios
_by-*para búsquedas rápidas:_by-email,_by-phone,_by-linkedin,_by-tag,_by-stage - Usa
--format jsonpara toda la salida CLI al procesar programáticamente - Usa
--format ids+xargspara operaciones por lotes - Lee
reports/para análisis precalculados — no recalcules desde datos brutos - Busca a través del sistema de archivos:
cat ~/crm/search/"tu consulta".json - Escribe a través del sistema de archivos: Crea/actualiza entidades escribiendo archivos JSON
- Todos los archivos JSON son autocontenidos — no necesitas unir entre archivos
- Los números de teléfono aceptan cualquier formato de entrada; se almacenan internamente como E.164
- Las redes sociales aceptan URLs completas; se almacenan como identificadores limpios