podcast-guest-crm
Gestiona el canal de invitados de podcast, borradores de divulgación y analíticas a través de 5 herramientas MCP.
Documentación
🎙️ Podcast Guest CRM
Insignias de la pila tecnológica completa
El sistema operativo para la reserva de podcasts.
Nativo de IA. Primero el teclado. Construido para agencias.

4.2 millones de podcasts. Economía creadora de más de $4 mil millones. Cero software de flujo de trabajo especializado.
Construimos la herramienta que debería haber existido durante la última década.
Construido por Rudrendu Paul y Sourav Nandy · Ingeniería con Claude Code
Inicio rápido · El problema · El producto · Capa de IA · Arquitectura · Pila tecnológica
Inicio rápido
git clone https://github.com/RudrenduPaul/podcast-guest-crm
cd podcast-guest-crm
pnpm install
pnpm dev
La aplicación funciona con datos de ejemplo desde el primer arranque; 34 invitados realistas en las seis etapas del embudo. No se requieren variables de entorno.
web: http://localhost:3000
api: http://localhost:3001
docs: http://localhost:3001/docs ← Swagger UI, auto-generated from route schemas
[!NOTA] La configuración cero solo aplica a este modo de desarrollo local que funciona con datos de ejemplo. Un despliegue de producción necesita credenciales reales de Supabase y Anthropic configuradas en el entorno: el esquema de entorno Zod en
packages/configbloquea el servidor al arrancar si falta un secreto requerido, por diseño.
El problema
Cada herramienta a la que recurre un anfitrión de podcast fue construida para un trabajo diferente. HubSpot es un CRM de ventas. PodMatch es un mercado de descubrimiento. Notion es un lienzo en blanco que requiere ingeniería para volverse útil. Ninguna modela el ciclo de vida del invitado (el arco desde el descubrimiento hasta la divulgación, la programación, la grabación, la publicación y el seguimiento) como un objeto de primera clase.
Esta herramienta sí lo hace. La puntuación de idoneidad del invitado, la redacción personalizada de divulgación, la preparación de entrevistas y las secuencias de seguimiento funcionan con claude-sonnet-4-6, que es lo que hace viable automatizar esos pasos específicos ahora, de una manera que no lo era hace un par de años.
El producto
Un CRM de pila completa nativo de IA con seis páginas, once funciones y cero compromisos en el oficio.
Flujo de trabajo principal
Discover → Outreach → Scheduled → Recorded → Published → Follow-up
Cada invitado avanza por este ciclo de vida. Cada transición se valida, se registra y se actúa sobre ella. El sistema sabe dónde está cada invitado, cuándo fue la última vez que supiste de él y qué debe suceder después. Sin que tengas que recordarlo.
Superficie de funciones
| Función | Qué hace |
|---|---|
| Paleta Cmd+K | Busca cualquier invitado por nombre, empresa o tema. Navega por las seis páginas. Activa acciones. Totalmente controlada por teclado. El camino más rápido a cualquier cosa en la aplicación. |
| Tablero Kanban | Tablero de arrastrar y soltar de seis columnas. Actualizaciones optimistas. Reglas de ciclo de vida aplicadas en la capa de servicios. No puedes saltar de Descubierto a Publicado. Confeti se dispara en cada reserva confirmada. |
| Compositor de correo con IA | Selecciona un invitado, haz clic en Generar. Nuestra IA transmite un discurso personalizado de 150–250 palabras, carácter por carácter. Efecto máquina de escribir, no un indicador de carga. Puntuación de confianza incluida. |
| Resumen de entrevista | Resumen previo a la grabación con un clic: introducción de biografía, 5 tipos de preguntas adaptadas, puntos de conversación, cierre gancho. Listo para copiar. |
| Publicaciones sociales | Publicación de LinkedIn, hilo de Twitter/X (5 tuits con conteo de caracteres), subtítulo de Instagram. Pestañas por plataforma, copia con un clic por plataforma. |
| Centro de notificaciones | Campana desplegable persistente. Muestra invitados sin respuesta en más de 7 días y grabaciones próximas. Siempre visible. Siempre accionable. |
| Enfoque de hoy | Sección del panel que muestra exactamente qué necesita atención hoy. Sin clasificación manual requerida. |
| Detalle del invitado | Anillo de puntuación de idoneidad animado (cuenta desde 0), línea de tiempo de progreso del ciclo de vida, enlaces de contacto completos, barra lateral de acciones de IA con tres paneles generativos. |
| Analíticas | Gráfico de barras por etapa, gráfico de dona por tema, línea de tiempo de actividad de divulgación de 12 semanas, métricas de conversión. Funciona sin dependencia del backend. |
| Modal de agregar invitado | Cmd+N desde cualquier lugar. Nombre, correo, cargo, empresa, biografía, temas, LinkedIn, Twitter, etapa, prioridad. Puntuación de idoneidad generada automáticamente al crear. |
| Empujones inteligentes | Notificación aparece al cargar el panel cuando los invitados han estado en divulgación más de 7 días sin respuesta. Proactivo, con nombre, no molesto. |
Atajos de teclado
La aplicación está diseñada para flujos de trabajo que priorizan el teclado. Los usuarios avanzados nunca tocan el mouse para tareas principales.
| Atajo | Acción |
|---|---|
⌘K | Paleta de comandos. Busca invitados, navega, activa acciones |
⌘N | Modal de agregar invitado directamente |
↑ ↓ | Navega por los resultados de la paleta |
↵ | Seleccionar |
Esc | Cerrar cualquier modal |
Capa de IA
Toda la IA vive en packages/ai. El único lugar en el código que importa @anthropic-ai/sdk. Cada función llama a una función tipada. Nunca toca el SDK directamente.
Dos modos: completeJSON<T>() para salida estructurada con inferencia de tipos genérica, stream() para el efecto de máquina de escribir en tiempo real. El compositor de divulgación usa ambos simultáneamente. Transmisión para la vista previa en vivo, JSON para el resultado listo para copiar con puntuación de confianza.
// packages/ai/src/client.ts. The single seam for all AI calls
export class ClaudeClient {
async completeJSON<T>(system: string, user: string): Promise<T>
async stream(system: string, user: string): AsyncIterable<string>
}
// Feature code never touches the SDK. It calls typed prompt functions:
const brief = await generateInterviewBrief(guest); // → InterviewBrief
const email = await draftOutreachEmail(guest, show); // → OutreachEmail
const score = await scoreGuestFit(guest, workspace); // → FitScore
Módulos de prompt
| Función | Archivo | Salida |
|---|---|---|
| Correo de divulgación | outreach-email.ts | Asunto, cuerpo de 150–250 palabras, puntuación de confianza (0–100), razonamiento |
| Puntuación de idoneidad del invitado | guest-research.ts | Puntuación, justificación de alineación, banderas rojas, dificultad de reserva |
| Resumen de entrevista | interview-brief.ts | Introducción de biografía, 5 tipos de preguntas, puntos de conversación, cierre gancho |
| Etiquetado de temas | topic-tagging.ts | 3–8 etiquetas de biografía + LinkedIn, categoría principal, confianza |
| Secuencia de seguimiento | follow-up-sequence.ts | Arco de 3 correos: recordatorio día 7, seguimiento día 14, final día 21 |
| Publicaciones sociales | social-post.ts | Publicación de LinkedIn, hilo de Twitter, subtítulo de Instagram. El tono varía según la plataforma |
El enfoque de ingeniería de prompts
No estamos generando prompts de manera genérica. Aquí está el conjunto de restricciones real del módulo de divulgación. La especificidad es la ventaja:
export const OUTREACH_EMAIL_SYSTEM_PROMPT = `You are an expert podcast booking agent
working on behalf of a host with a specific audience and brand.
Your emails must:
1. Be authentic, specific, and not generic. Reference the guest's actual recent work
2. Clearly state the show's value proposition and the size and shape of the audience
3. Make the ask simple and low-friction. One clear question, not a pitch deck
4. Be concise: 150–250 words for the body
5. Have a subject line under 70 characters that doesn't feel like a cold email
6. NEVER use: "passionate", "synergy", "journey", "touch base", "hop on a call"
7. End with a single clear call-to-action. Not multiple options`;
El prompt de puntuación de idoneidad evalúa a los invitados contra la taxonomía de temas real del programa. No señales de relevancia genéricas. El resumen de entrevista genera tipos de preguntas calibrados al formato del podcast (profundidad, contracorriente, prospectivo). Esto es ingeniería de prompts como diseño de producto, no ingeniería de prompts como truco de fiesta.
Arquitectura
Diagrama del sistema
Browser (Next.js 14 App Router)
├── TanStack Query v5 : server state, optimistic updates, stale-while-revalidate
│ every query falls back to seed data on API error
├── Zustand : UI state (sidebar, modals, ⌘K palette, filters)
│ persisted to localStorage via middleware
├── lib/api.ts : typed fetch wrapper; catches 503, returns seed data
└── components/ : shadcn/ui primitives + Framer Motion feature components
│ HTTP/REST + JWT (Bearer token)
▼
Fastify v5 API (Node.js 20, TypeScript strict mode)
├── Plugins: CORS (allowlist), @fastify/rate-limit (100/min), @fastify/jwt, swagger-ui
├── Routes: /guests, /outreach, /ai, /analytics ← all require authentication
├── Middleware: Zod schemas on every route. Body, query params, path params
└── Services: guestService (in-memory store seeded from packages/db on startup)
│ │
▼ ▼
packages/db packages/ai
Drizzle ORM schema + ClaudeClient +
34 seed guests 6 typed prompt modules
SQLite (dev) │
Turso (prod) ▼
Anthropic API
claude-sonnet-4-6
Cinco decisiones arquitectónicas que valen la pena leer
1. Tipos compartidos en packages/types, cero definiciones en línea en apps/.
Cada interfaz que cruza el límite de la API (Guest, OutreachEmail, Workspace, AnalyticsOverview) vive en un paquete, importado tanto por la API como por la aplicación web. Un error de TypeScript en el frontend es un contrato de API roto detectado antes de que se publique.
2. Costura única de IA en packages/ai.
ClaudeClient es el único lugar donde se importa @anthropic-ai/sdk. Maneja retroceso exponencial en 429 y 5xx, seguimiento de tokens por llamada, eliminación de markdown de respuestas JSON y transmisión a través de AsyncIterable. El código de funciones llama a funciones tipadas y nunca sabe que el SDK existe. Cambiar modelos o proveedores es un cambio de un solo archivo.
3. Degradación elegante como requisito de diseño, no como ocurrencia tardía. Cada hook de TanStack Query captura errores de API y devuelve datos de ejemplo. Cada mutación tiene un respaldo sintético. La aplicación es completamente interactiva sin un backend en ejecución. Esto es deliberado: las demostraciones nunca deberían fallar porque un servidor está caído.
4. Actualizaciones optimistas con reversión forzada.
Las transiciones de etapa en el tablero kanban son instantáneas en la interfaz. El servidor confirma de manera asíncrona. Si el servidor rechaza una transición (las reglas del ciclo de vida son estrictas, no puedes moverte de discover a published directamente), se restaura el estado anterior y se dispara una notificación de error. Los usuarios nunca esperan por la retroalimentación de arrastrar y soltar; los errores aparecen claramente sin corromper el estado.
5. Zod en cada límite.
El esquema de entorno bloquea el servidor al arrancar si falta un secreto requerido. La configuración incorrecta silenciosa es peor que una falla ruidosa. Cada ruta de API tiene un esquema Zod para cuerpo, consulta y parámetros; el pipeline de CI rechaza rutas sin esquemas. Los esquemas compartidos viven en packages/config para que el frontend y el backend apliquen el contrato idéntico.
Arquitectura de subagentes (Claude Code)
Este código fue construido usando cuatro subagentes especializados de Claude Code ejecutándose en paralelo, cada uno limitado a un segmento de dominio. Esto no es una preferencia de flujo de trabajo. Es aislamiento arquitectónico aplicado en la capa de herramientas.
| Agente | Archivo de restricciones | Qué posee |
|---|---|---|
| Agente de UI | .claude/agents/ui-agent.md | Solo apps/web/. use client solo cuando sea requerido. Framer Motion en todas las animaciones de listas. |
| Agente de BD | .claude/agents/db-agent.md | Solo packages/db/. Esquema, datos de ejemplo, migraciones. No puede tocar rutas ni UI. |
| Agente de funciones de IA | .claude/agents/ai-features-agent.md | Solo packages/ai/. Prompts, transmisión, modo JSON. Sin any, sin contexto de programa codificado. |
| Agente de pruebas | .claude/agents/test-agent.md | Pruebas para todo lo que otros agentes construyen. Umbral de cobertura: >70% antes de fusionar. |
El agente de UI no puede escribir una consulta de Drizzle. El agente de BD no puede crear un componente de React. La restricción se convierte en arquitectura. Dejas de dudar si un cambio de UI mutó silenciosamente un esquema.
Comandos de barra personalizados en .claude/commands/:
/new-feature <name>: crea una función completa, ruta de API + esquema Zod + servicio + página + componentes + hook de TanStack + pruebas/review-pr: ejecuta una lista de verificación de seguridad, seguridad de tipos y MLP antes de fusionar
MLP: El estándar de oficio
La ventaja de "publicar rápido" se acabó. Un desarrollador capaz crea un CRM en un fin de semana; nuestra solución lo comprime a horas. La ventaja ahora es el oficio. La calidad de lo que construyes en ese tiempo.
Mantenemos un estándar de Producto Mínimo Amable (MLP) en cada PR. El marco de Elena Verna: el umbral donde un producto gana afecto genuino de sus usuarios, no solo utilidad adecuada.
Momentos construidos deliberadamente:
- Confeti en la reserva. Cuando un invitado pasa a Programado, el confeti se dispara. Una reserva confirmada es una victoria real. La aplicación debería tratarla así.
- Efecto de máquina de escribir en la salida de IA. El correo generado se escribe carácter por carácter. La transmisión hace que se sienta como trabajar con un colaborador, no esperar por una herramienta.
- La puntuación de idoneidad cuenta hacia arriba. El anillo se anima de 0 a la puntuación real en 600 ms. La gente lo observa. Esa espera hace que la puntuación se sienta ganada.
- Paleta de comandos. ⌘K pone cada invitado, página y acción a un toque de tecla de distancia. Los usuarios avanzados nunca alcanzan el mouse.
- Enfoque de hoy. El panel te dice exactamente quién necesita atención hoy (divulgación estancada, grabaciones próximas) sin requerir que recuerdes qué revisar.
- Empujones con nombre. "Sara no ha respondido en 8 días" supera a "3 seguimientos pendientes." Con nombre, específico, accionable.
- Copias con personalidad en estados vacíos. "Tu lista de descubrimiento está vacía. Tu próximo gran episodio está a una divulgación de distancia" te dice qué hacer después. "No se encontraron datos" no lo hace.
Lista de verificación MLP, requerida en cada PR:
- Los estados vacíos tienen copias con personalidad, no "No se encontraron datos"
- Los estados de carga usan componentes
Skeleton, no pantallas en blanco - Los errores tienen mensajes accionables, no "Algo salió mal"
- Las interacciones clave tienen animaciones de Framer Motion
- ¿Cuál es el momento sorpresa? Si no hay uno, encuéntralo antes de fusionar.
Precios
SaaS de dos niveles. Precios simples que crecen con el cliente.
| Plan | Precio | Para quién es |
|---|---|---|
| Solo | $29/mes | Anfitriones de podcasts independientes que gestionan 1 programa, 20–100 invitados/año |
| Agencia | $99/mes | Agencias de reservas que gestionan 3+ programas y 200+ propuestas/año |
Créditos de IA basados en uso por encima del nivel base: las primeras 200 llamadas de IA/mes (correo de divulgación, puntuación de idoneidad, resumen, publicación social) están incluidas; por encima de eso, los equipos pagan por lo que usan.
Panorama Competitivo
La brecha no son las funciones. Es el modelo mental.
| Google Sheets | HubSpot / Pipedrive | PodMatch | Podcast Guest CRM | |
|---|---|---|---|---|
| Ciclo de vida del invitado (6 etapas) | manual | campos personalizados requeridos | ❌ | integrado, aplicado |
| Divulgación con IA (personalizada) | ❌ | ❌ | ❌ | streaming, puntuación de confianza |
| Puntuación de idoneidad del invitado | ❌ | ❌ | básica | puntuada por IA vs. tus temas |
| Resumen de entrevista | ❌ | ❌ | ❌ | un clic, listo para copiar |
| Secuencia de seguimiento (IA) | ❌ | complemento ($$$) | ❌ | arco de 3 correos, escrito por IA |
| Generador de publicaciones sociales | ❌ | ❌ | ❌ | LinkedIn + Twitter + Instagram |
| Paleta de comandos (⌘K) | ❌ | ❌ | ❌ | navegación completa por teclado |
| Centro de notificaciones inteligente | ❌ | ❌ | ❌ | avisos + alertas de grabación |
| Espacio de trabajo multi-programa para agencias | ❌ | $$$ | ❌ | incluido |
| Precio | $0 | $45–800/mes | $27–97/mes | $29–99/mes |
PodMatch resuelve el descubrimiento: encontrar invitados. Nosotros resolvemos el flujo de trabajo: el proceso de meses de proponer, dar seguimiento, agendar, preparar, grabar, publicar y mantener la relación. No son el mismo problema. Las empresas que construyeron herramientas de descubrimiento dejaron el problema del flujo de trabajo intacto. Esa es la brecha.
Puntos de Integración MCP
Cada punto de integración está detrás de una interfaz. Los servidores MCP se insertan sin refactorizar.
| Servidor MCP | Estado | Punto de integración |
|---|---|---|
| GitHub MCP | Activo en desarrollo | .github/: automatización de PR, estado de CI, seguimiento de issues desde la terminal |
| Supabase MCP | Listo para conectar | packages/db/: consulta el esquema en vivo antes de escribir consultas, eliminando errores de nombres de campos |
| Gmail MCP | Listo para conectar | apps/api/src/routes/outreach.ts: el envío de divulgación está detrás de una interfaz sendEmail() |
| Google Calendar MCP | Listo para conectar | apps/api/src/routes/guests.ts: confirmación de reservas y sincronización de fechas de grabación |
| Exa Search MCP | Listo para conectar | packages/ai/src/prompts/guest-research.ts: datos web en vivo en el pipeline de puntuación de idoneidad |
Con Gmail MCP activo, la divulgación pasa de redactada a enviada en un clic. Con Calendar MCP, un invitado que pasa a Programado crea el evento de grabación automáticamente. Con Exa, la puntuación de idoneidad obtiene el trabajo más reciente del invitado desde la web. No solo lo que está en su biografía.
Stack Tecnológico
Cada elección está defendida. Sin desarrollo impulsado por currículum.
| Capa | Tecnología | Por qué, honestamente |
|---|---|---|
| Monorepo | Turborepo + pnpm workspaces | Caché de compilación remota. Protocolo workspace:*. Un único pnpm install en la raíz conecta todo. |
| Frontend | Next.js 14 App Router | RSC para renderizado estático primero. Enrutamiento basado en archivos. Patrón BFF integrado sin una puerta de enlace separada. |
| UI | Tailwind CSS + shadcn/ui | shadcn copia en tu repositorio. Eres dueño del código, no de una versión. Sin infierno de dependencias en versiones que rompen. |
| Animaciones | Framer Motion | Animaciones de diseño en reordenamientos de listas: una línea. AnimatePresence maneja montaje/salida. Vale el tamaño del bundle. |
| Arrastrar y Soltar | @hello-pangea/dnd | Fork probado en producción de react-beautiful-dnd. Mantenido. Accesible. Se integra de forma idéntica. |
| Estado del Servidor | TanStack Query v5 | Stale-while-revalidate. Actualizaciones optimistas. Refetch automático en segundo plano. El tablero kanban es instantáneo gracias a esto. |
| Estado de UI | Zustand | API mínima. Barra lateral, modales, paleta de comandos, filtros. Todo persistido en localStorage con una línea de middleware. |
| API | Fastify v5 | ~2x más rápido que Express en el p99. TypeScript de primera clase. @fastify/swagger genera OpenAPI a partir de esquemas de rutas automáticamente. |
| Validación | Zod | Un esquema = un tipo TypeScript + un validador en tiempo de ejecución. En cada ruta. Sin excepciones. |
| ORM | Drizzle ORM | Sin generación de código. El esquema es TypeScript puro. Las migraciones son SQL puro. Las consultas son totalmente type-safe. |
| Base de datos | SQLite (dev) / Turso (prod) | Configuración cero localmente. Esquema idéntico a producción. Turso añade distribución global en el edge cuando lo necesitamos. |
| IA | claude-sonnet-4-6 | Mejor salida JSON estructurada y profundidad de seguimiento de instrucciones disponible. Los patrones de prompt aquí lo requieren. |
| Autenticación | Supabase Auth | JWT + Row Level Security. dev-mock-token en dev. RLS aplica aislamiento de espacios de trabajo en la capa de BD en prod. |
| Correo | Resend + React Email | Plantillas como componentes React. Control de versiones, testeables, previsualizables en un navegador. Simuladas en dev. |
| Gráficos | Recharts | React-native. Componible. Amigable con TypeScript. Supera a Chart.js en componibilidad para nuestro caso de uso. |
| CI/CD | GitHub Actions | Lint → typecheck → test → audit en cada PR. CodeQL en horario semanal. Sin merge sin verde. |
Estructura del Proyecto
podcast-guest-crm/
├── apps/
│ ├── web/ # Next.js 14 App Router
│ │ ├── app/
│ │ │ ├── (auth)/login/ # Demo login (route group)
│ │ │ └── dashboard/ # Protected routes. No route group by design
│ │ │ ├── page.tsx # Overview · Today's Focus · Recent Activity
│ │ │ ├── layout.tsx # Sidebar + Navbar + GlobalModals
│ │ │ ├── guests/ # Table · filters · Add Guest modal
│ │ │ ├── pipeline/ # Kanban board
│ │ │ │ └── [id]/ # Guest detail · AI action sidebar
│ │ │ ├── outreach/ # AI email composer (streaming)
│ │ │ ├── analytics/ # Charts · conversion metrics
│ │ │ └── settings/ # Workspace · AI model config
│ │ ├── components/
│ │ │ ├── ui/ # shadcn/ui primitives
│ │ │ ├── shared/ # Sidebar · Navbar · CommandPalette
│ │ │ │ # NotificationDropdown · GlobalModals · EmptyState
│ │ │ ├── guests/ # GuestCard · GuestTable · AddGuestModal
│ │ │ │ # InterviewBriefPanel · SocialPostsPanel
│ │ │ ├── pipeline/ # KanbanBoard · KanbanColumn
│ │ │ └── outreach/ # AIAssistPanel (streaming typewriter)
│ │ ├── hooks/ # TanStack Query hooks. Graceful fallback on every query
│ │ ├── lib/ # api.ts · mock-data.ts · utils.ts
│ │ └── stores/ # Zustand. Sidebar · modals · palette · filters
│ │
│ └── api/ # Fastify v5 backend
│ └── src/
│ ├── plugins/ # cors · rate-limit · jwt · swagger-ui
│ ├── routes/ # /guests · /outreach · /ai · /analytics
│ ├── services/ # guestService. In-memory store, seeded on startup
│ └── tests/ # Vitest. Coverage gate >70%
│
├── packages/
│ ├── types/ # Shared TypeScript interfaces. Single source of truth
│ ├── config/ # Zod env validation · shared constants
│ ├── db/ # Drizzle schema · 34 seed guests · migrations
│ ├── ai/ # ClaudeClient · 6 typed prompt modules
│ ├── cli/ # podcast-guest-crm-cli: TypeScript CLI, wraps apps/api
│ └── cli-pypi-wrapper/ # Thin pip/pipx wrapper, shells out to the npm CLI
│
├── .claude/
│ ├── commands/ # /new-feature · /review-pr
│ └── agents/ # ui · db · ai-features · test. Scoped sub-agents
│
├── .github/
│ ├── workflows/ # ci.yml · security.yml (CodeQL)
│ └── PULL_REQUEST_TEMPLATE.md # Includes MLP checklist
│
└── docs/
├── architecture/ # system-design.md · security.md · ai-layer.md
└── decisions/ # ADR 001 (monorepo) · 002 (Drizzle) · 003 (Fastify)
Referencia de API
Documentación OpenAPI auto-generada en http://localhost:3001/docs.
GET /health Health check + readiness probe
GET /api/v1/guests List, paginated, filterable by stage/topic/priority
POST /api/v1/guests Create guest, triggers async fit scoring
GET /api/v1/guests/:id Guest detail
PUT /api/v1/guests/:id Update fields
PATCH /api/v1/guests/:id/stage Lifecycle transition, service validates allowed paths
DELETE /api/v1/guests/:id Soft delete
POST /api/v1/outreach/draft AI draft, JSON or streaming mode
POST /api/v1/outreach/send Send via Resend (mocked in dev)
GET /api/v1/outreach/:guestId Outreach history
POST /api/v1/ai/fit-score Score 0–100 + rationale + red flags
POST /api/v1/ai/interview-brief Pre-recording brief with question structure
POST /api/v1/ai/social-post LinkedIn + Twitter thread + Instagram caption
GET /api/v1/analytics/overview Dashboard metrics + recent activity feed
GET /api/v1/analytics/pipeline Stage funnel + outreach activity timeline
[!WARNING]
PATCH /guests/:id/stage,POST /guestsyGET /guests/:idactualmente declaran su forma de respuesta como un{ type: 'object' }simple sin propiedades listadas enapps/api/src/routes/guests.ts. El serializador JSON de Fastify reduce el cuerpo a{}en éxito como resultado, aunque la operación haya tenido éxito.guest listno se ve afectado. Consulta el FAQ para ver cómo la CLI lo resuelve.
Transiciones del ciclo de vida aplicadas en la capa de servicios:
discover → outreach → scheduled → recorded → published → follow_up
↑___________↑ ↑__________↑
(reschedule) (re-record needed)
CLI
podcast-guest-crm-cli es una CLI real de TypeScript (packages/cli) que envuelve la misma API anterior. Cada comando se asigna a una ruta real, sin endpoints inventados.
npm install -g podcast-guest-crm-cli
# or, for Python-first / pip environments (thin wrapper, shells out to the npm package via npx):
pip install podcast-guest-crm-cli
podcast-guest-crm-cli login
podcast-guest-crm-cli guest list --stage published --limit 5
podcast-guest-crm-cli guest show <id>
podcast-guest-crm-cli guest add --name "Ada Lovelace" --email ada@example.com --title "Engineer" --company "Analytical Engines"
podcast-guest-crm-cli guest stage <id> outreach --reason "replied positively"
podcast-guest-crm-cli outreach draft <guest-id> --episode-angle "AI safety"
podcast-guest-crm-cli analytics summary
podcast-guest-crm-cli analytics pipeline
Añade --json a cualquier comando que devuelva datos para salida legible por máquina, pensada para scripts y agentes:
podcast-guest-crm-cli guest list --stage discover --json
login autentica directamente contra el endpoint REST de autenticación de Supabase (POST <SUPABASE_URL>/auth/v1/token?grant_type=password), el mismo proveedor de identidad que usa la aplicación web. Nunca usa el atajo Bearer dev-mock-token solo para dev en apps/api/src/plugins/auth.ts; ese bypass existe puramente para pruebas locales de API. La sesión resultante se guarda en caché en ~/.config/podcast-guest-crm-cli/credentials.json (permisos 0600) y se refresca silenciosamente con el token de refresco almacenado cuando expira.


Servidor MCP
podcast-guest-crm-cli incluye un servidor de Protocolo de Contexto de Modelo (no confundir con los servidores MCP de terceros con los que esta aplicación puede integrarse, listados arriba). podcast-guest-crm-cli mcp lo inicia sobre stdio, exponiendo cinco herramientas que llaman directamente a la misma costura de API que usa cada comando de la CLI: list_guests, add_guest, update_guest_stage, draft_outreach_email y get_analytics_summary.
npm install -g podcast-guest-crm-cli
podcast-guest-crm-cli login
{
"mcpServers": {
"podcast-guest-crm": {
"command": "npx",
"args": ["podcast-guest-crm-cli", "mcp"]
}
}
}
Un tools/call real para la herramienta central del ciclo de vida, {"name": "update_guest_stage", "arguments": {"id": "guest_1", "stage": "outreach"}}, devuelve el mismo sobre que guest stage <id> outreach --json imprime en la CLI. Consulta el README de packages/cli para la referencia completa de herramientas.
Seguridad
Controles de nivel producción desde el primer día. No adaptamos la seguridad a posteriori.
| Control | Implementación |
|---|---|
| Autenticación | JWT vía @fastify/jwt. Cada ruta: preHandler: [server.authenticate]. Sin excepciones, incluso en dev. |
| Aislamiento de espacios de trabajo | Todas las consultas filtran por workspaceId del payload JWT. Supabase RLS aplica esto en la capa de BD en producción. |
| Límite de tasa | 100 req/min por IP vía @fastify/rate-limit. Configurable por entorno. |
| Validación de entrada | Zod en cada ruta. Cuerpo, consulta, parámetros de ruta. Sin esquema = sin lanzamiento. |
| Inyección SQL | Consultas parametrizadas de Drizzle ORM en todo el código. Sin SQL crudo en este codebase. |
| XSS | Escape predeterminado de Next.js + cabeceras CSP restrictivas en next.config.ts. |
| Secretos | El esquema de entorno Zod bloquea el servidor al arrancar si faltan variables requeridas. La configuración incorrecta silenciosa es un bug de seguridad. |
| CORS | Basado en lista blanca. Sin comodines, nunca. |
| SAST | CodeQL en cada PR + horario semanal. |
| Dependencias | pnpm audit en CI. Rompe la compilación en vulnerabilidades de alta gravedad. |
Hoja de Ruta
Corto plazo (próximos 60 días):
- MCP: Gmail + Google Calendar: la divulgación pasa de redactada a enviada en un clic; las confirmaciones de reservas crean eventos de calendario automáticamente
- MCP: Exa Search: la puntuación de idoneidad del invitado obtiene datos web en vivo, no solo texto de biografía
- Facturación Stripe: Solo $29/mes, Agencia $99/mes, créditos de IA basados en uso por encima del nivel
Mediano plazo:
- Ingesta de transcripciones: sube el episodio, genera automáticamente publicaciones sociales y correos de seguimiento que referencian momentos destacados específicos
- Portal de clientes: vista de solo lectura basada en tokens para clientes de agencias, eliminando el correo semanal de informe de estado
- Conector Zapier / Make: sincronización bidireccional con Cal.com, Notion, HubSpot
- Extracción RSS: ingresa la URL RSS de un podcast, autocompleta la información de contacto del anfitrión y las estadísticas del programa
Largo plazo:
- Móvil (React Native): misma API, sensación nativa, para revisar el pipeline en movimiento
- Panel multi-programa: vista de agencia en todos los programas gestionados en una sola pantalla
- Seguimiento predictivo: modelo de ML entrenado con datos de tasa de respuesta para optimizar el momento de la divulgación
El Equipo
Rudrendu Paul y Sourav Nandy han construido este software de nivel producción nativo de IA.
El stack utilizado:
- Monorepos TypeScript full-stack (Turborepo + pnpm) con paquetes de tipos compartidos, aplicados en la capa de CI
- APIs Fastify con esquemas validados por Zod, autenticación JWT y Row Level Security. Sin excepciones
- Capas de funciones impulsadas por IA con módulos de prompt tipados, streaming, extracción JSON y backoff exponencial
- Frontends Next.js 14 App Router con TanStack Query, Zustand, Framer Motion y shadcn/ui
- Arquitecturas de sub-agentes Claude Code que aplican límites de dominio en la capa de herramientas
FAQ
¿Qué es podcast-guest-crm-cli y en qué se diferencia de usar la aplicación web?
Es un cliente real de línea de comandos en TypeScript (packages/cli) para la misma API que llama la aplicación web Next.js. Envuelve los endpoints del ciclo de vida del invitado (guest list/add/show/stage), el endpoint de redacción de divulgación con IA (outreach draft) y los endpoints de analíticas (analytics summary/pipeline). El diferenciador es la salida nativa para agentes: cada comando que devuelve datos soporta --json, de modo que un script o un agente de IA puede manejar el mismo pipeline que un humano manejaría desde el panel, sin hacer scraping de HTML ni mantener su propio cliente HTTP.
¿Qué plataformas y runtimes soporta?
El paquete npm (podcast-guest-crm-cli en npm, requiere Node.js 20 o superior) se ejecuta en macOS, Linux y Windows en cualquier lugar donde Node funcione. Un paquete PyPI separado con el mismo nombre (packages/cli-pypi-wrapper) es un envoltorio delgado para usuarios de pip/pipx: no reimplementa la CLI en Python, verifica que node y npx estén en PATH y ejecuta el paquete npm, fijado a la propia versión del envoltorio — con respaldo a la versión latest de npm si esa versión exacta nunca se publicó en npm, en lugar de fallar por completo.
¿Cómo funciona el inicio de sesión?
podcast-guest-crm-cli login solicita tu correo y contraseña, luego autentica directamente contra el endpoint REST de tu proyecto Supabase (POST <SUPABASE_URL>/auth/v1/token?grant_type=password), el mismo proveedor de identidad que usa la aplicación web. Necesitarás la URL del proyecto Supabase de tu despliegue y la clave anon (--supabase-url / --supabase-anon-key, o PODCAST_GUEST_CRM_SUPABASE_URL / PODCAST_GUEST_CRM_SUPABASE_ANON_KEY), coincidiendo con los valores que tu despliegue ya establece como NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY. Los tokens de acceso y refresco resultantes se guardan en caché en ~/.config/podcast-guest-crm-cli/credentials.json con permisos 0600, y el token de acceso se refresca silenciosamente una vez que expira.
¿Por qué un comando imprimió {"data": {}} en lugar de los campos que esperaba?
Eso es una brecha real y actual en algunos de los esquemas de respuesta Fastify de la propia API (apps/api/src/routes/guests.ts), no un error del CLI: rutas como PATCH /guests/:id/stage, POST /guests y GET /guests/:id declaran su forma de respuesta como un { type: 'object' } simple sin propiedades listadas, por lo que el serializador JSON de Fastify reduce el cuerpo a un objeto vacío incluso en caso de éxito. El CLI detecta esto y recurre a imprimir la respuesta cruda (vacía) en lugar de fallar por un campo faltante. guest list no se ve afectado, ya que su esquema declara un arreglo sin una forma de elemento fija.
¿Puedo usar este CLI en un pipeline automatizado o entregárselo a un agente de IA?
Sí, ese es el objetivo principal de diseño. Cada comando que devuelve datos acepta --json para salida estructurada, los códigos de salida son distintos de cero en caso de fallo, y las respuestas de error son objetos JSON con campos error, message y statusCode cuando --json está configurado. No hay una ruta exclusivamente interactiva requerida para ningún comando excepto el aviso de contraseña de login, que también acepta las banderas --email y --password para uso no interactivo. Para agentes nativos de MCP (Claude Desktop, Claude Code), podcast-guest-crm-cli mcp inicia un servidor MCP stdio que expone las mismas capacidades de ciclo de vida de invitados, redacción de alcance y análisis como herramientas invocables; consulte Servidor MCP arriba.
¿Puedo usar este CLI, o el resto de este código base, comercialmente?
Sí. Este repositorio (incluyendo packages/cli y packages/cli-pypi-wrapper) tiene licencia MIT, propiedad conjunta de Rudrendu Paul y Sourav Nandy. Consulte LICENCIA para los términos completos; el uso comercial, la modificación y la redistribución están todos permitidos.
¿El CLI alguna vez almacena o transmite mi contraseña?
No. La contraseña que ingresa en el aviso de login se envía una vez, a través de HTTPS, directamente al endpoint de concesión de contraseña de Supabase, y nunca se escribe en disco. Solo el token de acceso resultante, el token de actualización y su vencimiento se almacenan en caché localmente.
¿Qué sucede si mi sesión expira mientras ejecuto un comando?
El CLI verifica el vencimiento del token de acceso en caché (con un margen de 30 segundos) antes de cada solicitud. Si ha expirado, el CLI llama a la concesión de token de actualización de Supabase con el token de actualización almacenado, guarda la nueva sesión y reintenta, todo sin pedirle que inicie sesión nuevamente. Solo verá errores de login nuevamente una vez que el token de actualización en sí sea invalidado (por ejemplo, después de un cambio de contraseña).
Licencia
MIT. Consulte LICENCIA para los términos completos. El uso comercial, la modificación y la redistribución están todos permitidos.