podcast-guest-crm
Gerencie o pipeline de convidados de podcast, rascunhos de divulgação e análises via 5 ferramentas MCP.
Documentação
🎙️ Podcast Guest CRM
Distintivos da stack tecnológica completa
O sistema operacional para agendamento de podcasts.
Nativo de IA. Prioriza o teclado. Feito para agências.

4,2 milhões de podcasts. Economia criativa de US$ 4 bi+. Zero software de fluxo de trabalho específico.
Construímos a ferramenta que deveria ter existido na última década.
Construído por Rudrendu Paul & Sourav Nandy · Projetado com Claude Code
Início Rápido · O Problema · O Produto · Camada de IA · Arquitetura · Stack Tecnológica
Início Rápido
git clone https://github.com/RudrenduPaul/podcast-guest-crm
cd podcast-guest-crm
pnpm install
pnpm dev
O aplicativo roda com dados de exemplo desde o primeiro boot; 34 convidados realistas em todos os seis estágios do pipeline. Nenhuma variável de ambiente é necessária.
web: http://localhost:3000
api: http://localhost:3001
docs: http://localhost:3001/docs ← Swagger UI, auto-generated from route schemas
[!NOTE] A configuração zero se aplica apenas a este modo de desenvolvimento local rodando com dados de exemplo. Uma implantação em produção precisa de credenciais reais do Supabase e da Anthropic definidas no ambiente: o schema de env do Zod em
packages/configderruba o servidor no boot se um segredo obrigatório estiver ausente, por design.
O Problema
Toda ferramenta que um anfitrião de podcast procura foi construída para um trabalho diferente. HubSpot é um CRM de vendas. PodMatch é um marketplace de descoberta. Notion é uma tela em branco que exige engenharia para se tornar algo útil. Nenhuma delas modela o ciclo de vida do convidado (o arco da descoberta até o alcance, agendamento, gravação, publicação e acompanhamento) como um objeto de primeira classe.
Esta ferramenta faz isso. Pontuação de adequação do convidado, elaboração de alcance personalizado, preparação de entrevistas e sequências de acompanhamento rodam em claude-sonnet-4-6, que é o que torna viável automatizar essas etapas específicas agora, de uma forma que não era há alguns anos.
O Produto
Um CRM full-stack nativo de IA com seis páginas, onze recursos e zero concessões em qualidade.
Fluxo de Trabalho Principal
Discover → Outreach → Scheduled → Recorded → Published → Follow-up
Cada convidado passa por este ciclo de vida. Cada transição é validada, registrada e acionada. O sistema sabe onde cada convidado está, quando foi o último contato e o que precisa acontecer em seguida. Sem que você precise lembrar.
Superfície de Recursos
| Recurso | O que faz |
|---|---|
| Paleta Cmd+K | Busque qualquer convidado por nome, empresa ou tópico. Navegue por todas as seis páginas. Acione ações. Totalmente controlada por teclado. O caminho mais rápido para qualquer coisa no aplicativo. |
| Pipeline Kanban | Quadro de arrastar e soltar com seis colunas. Atualizações otimistas. Regras de ciclo de vida aplicadas na camada de serviço. Você não pode pular de Descoberta para Publicado. Confete dispara a cada reserva confirmada. |
| Compositor de E-mail com IA | Selecione um convidado, clique em Gerar. Nossa IA transmite um pitch personalizado de 150–250 palavras, caractere por caractere. Efeito máquina de escrever, não um spinner. Pontuação de confiança incluída. |
| Briefing de Entrevista | Briefing pré-gravação com um clique: introdução de bio, 5 tipos de perguntas personalizados, pontos de discussão, gancho de encerramento. Pronto para copiar. |
| Posts Sociais | Post no LinkedIn, thread no Twitter/X (5 tweets com contagem de caracteres), legenda para Instagram. Abas por plataforma, cópia com um clique por plataforma. |
| Central de Notificações | Menu suspenso de sino persistente. Mostra convidados sem resposta há 7+ dias e gravações futuras. Sempre visível. Sempre acionável. |
| Foco de Hoje | Seção do painel que mostra exatamente o que precisa de atenção hoje. Sem triagem manual necessária. |
| Detalhe do Convidado | Anel de pontuação de adequação animado (conta a partir de 0), linha do tempo de progresso do ciclo de vida, links de contato completos, barra lateral de ações de IA com três painéis generativos. |
| Analytics | Gráfico de barras por estágio, rosca por tópico, linha do tempo de atividade de alcance de 12 semanas, métricas de conversão. Funciona sem dependência de backend. |
| Modal de Adicionar Convidado | Cmd+N de qualquer lugar. Nome, e-mail, cargo, empresa, bio, tópicos, LinkedIn, Twitter, estágio, prioridade. Pontuação de adequação gerada automaticamente na criação. |
| Lembretes Inteligentes | Toast aparece ao carregar o painel quando convidados estão em alcance há > 7 dias sem resposta. Proativo, nomeado, não irritante. |
Atalhos de Teclado
O aplicativo é projetado para fluxos de trabalho que priorizam o teclado. Usuários avançados nunca tocam no mouse para tarefas principais.
| Atalho | Ação |
|---|---|
⌘K | Paleta de comandos. Busque convidados, navegue, acione ações |
⌘N | Modal de Adicionar Convidado diretamente |
↑ ↓ | Navegar pelos resultados da paleta |
↵ | Selecionar |
Esc | Fechar qualquer modal |
Camada de IA
Toda a IA vive em packages/ai. O único lugar no código que importa @anthropic-ai/sdk. Cada recurso chama uma função tipada. Nunca toca no SDK diretamente.
Dois modos: completeJSON<T>() para saída estruturada com inferência de tipo genérica, stream() para o efeito máquina de escrever em tempo real. O compositor de alcance usa ambos simultaneamente. Streaming para a pré-visualização ao vivo, JSON para o resultado pronto para copiar com pontuação de confiança.
// 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
| Recurso | Arquivo | Saída |
|---|---|---|
| E-mail de Alcance | outreach-email.ts | Assunto, corpo de 150–250 palavras, pontuação de confiança (0–100), justificativa |
| Pontuação de Adequação do Convidado | guest-research.ts | Pontuação, justificativa de alinhamento, bandeiras vermelhas, dificuldade de reserva |
| Briefing de Entrevista | interview-brief.ts | Introdução de bio, 5 tipos de perguntas, pontos de discussão, gancho de encerramento |
| Marcação de Tópicos | topic-tagging.ts | 3–8 tags da bio + LinkedIn, categoria principal, confiança |
| Sequência de Acompanhamento | follow-up-sequence.ts | Arco de 3 e-mails: reforço no Dia 7, acompanhamento no Dia 14, final no Dia 21 |
| Posts Sociais | social-post.ts | Post no LinkedIn, thread no Twitter, legenda no Instagram. Tom varia por plataforma |
A Abordagem de Engenharia de Prompt
Não estamos usando prompts genéricos. Aqui está o conjunto real de restrições do módulo de alcance. Especificidade é a vantagem competitiva:
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`;
O prompt de pontuação de adequação avalia convidados contra a taxonomia real de tópicos do programa. Não sinais genéricos de relevância. O briefing de entrevista gera tipos de perguntas calibrados para o formato do podcast (profundidade, contrário, prospectivo). Isso é engenharia de prompt como design de produto, não engenharia de prompt como truque de festa.
Arquitetura
Diagrama do 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 Decisões Arquiteturais Que Valem a Leitura
1. Tipos compartilhados em packages/types, zero definições inline em apps/.
Cada interface que cruza a fronteira da API (Guest, OutreachEmail, Workspace, AnalyticsOverview) vive em um pacote, importado tanto pela API quanto pelo aplicativo web. Um erro de TypeScript no frontend é um contrato de API quebrado, detectado antes do lançamento.
2. Ponto único de IA em packages/ai.
ClaudeClient é o único lugar onde @anthropic-ai/sdk é importado. Ele lida com backoff exponencial em 429s e 5xx, rastreamento de tokens por chamada, remoção de markdown de respostas JSON e streaming via AsyncIterable. O código de recursos chama funções tipadas e nunca sabe que o SDK existe. Trocar modelos ou provedores é uma mudança de um arquivo.
3. Degradação graciosa como requisito de design, não uma reflexão tardia. Cada hook do TanStack Query captura erros de API e retorna dados de exemplo. Cada mutação tem um fallback sintético. O aplicativo é totalmente interativo sem um backend em execução. Isso é deliberado: demos nunca devem falhar porque um servidor está fora do ar.
4. Atualizações otimistas com rollback forçado.
Transições de estágio no quadro kanban são instantâneas na interface. O servidor confirma de forma assíncrona. Se o servidor rejeitar uma transição (as regras de ciclo de vida são estritas, você não pode mover de discover para published diretamente), o estado anterior é restaurado e um toast de erro dispara. Os usuários nunca esperam pelo feedback de arrastar e soltar; erros aparecem claramente sem corromper o estado.
5. Zod em cada fronteira.
O schema de env derruba o servidor no boot se um segredo obrigatório estiver ausente. Configuração incorreta silenciosa é pior do que uma falha ruidosa. Cada rota de API tem um schema Zod para corpo, query e parâmetros; o pipeline de CI rejeita rotas sem schemas. Schemas compartilhados vivem em packages/config para que frontend e backend apliquem o contrato idêntico.
Arquitetura de Sub-Agentes (Claude Code)
Este código foi construído usando quatro sub-agentes especializados do Claude Code rodando em paralelo, cada um escopado a uma fatia de domínio. Isso não é uma preferência de fluxo de trabalho. É isolamento arquitetural aplicado na camada de ferramentas.
| Agente | Arquivo de restrição | O que possui |
|---|---|---|
| Agente de UI | .claude/agents/ui-agent.md | Apenas apps/web/. use client apenas quando necessário. Framer Motion em todas as animações de lista. |
| Agente de DB | .claude/agents/db-agent.md | Apenas packages/db/. Schema, seeds, migrações. Não pode tocar em rotas ou UI. |
| Agente de Recursos de IA | .claude/agents/ai-features-agent.md | Apenas packages/ai/. Prompts, streaming, modo JSON. Sem any, sem contexto de programa codificado. |
| Agente de Testes | .claude/agents/test-agent.md | Testes para tudo que outros agentes constroem. Portão de cobertura: >70% antes do merge. |
O agente de UI não pode escrever uma query do Drizzle. O agente de DB não pode criar um componente React. Restrição vira arquitetura. Você para de duvidar se uma mudança de UI alterou silenciosamente um schema.
Comandos de barra personalizados em .claude/commands/:
/new-feature <name>: cria um recurso completo, rota de API + schema Zod + serviço + página + componentes + hook TanStack + testes/review-pr: executa uma lista de verificação de segurança, segurança de tipos e MLP antes do merge
MLP: O Padrão de Qualidade
A vantagem de "entregar rápido" acabou. Um desenvolvedor capaz cria um CRM em um fim de semana; nossa solução comprime isso para horas. A vantagem agora é a qualidade. A qualidade do que você constrói nesse tempo.
Mantemos um padrão de Produto Mínimo Adorável (MLP) em cada PR. O enquadramento de Elena Verna: o limite onde um produto ganha afeição genuína de seus usuários, não apenas utilidade adequada.
Momentos construídos deliberadamente:
- Confete na reserva. Quando um convidado passa para Agendado, confete dispara. Uma reserva confirmada é uma vitória real. O aplicativo deve tratar assim.
- Efeito máquina de escrever na saída de IA. O e-mail gerado é digitado caractere por caractere. Streaming faz parecer que você está trabalhando com um colaborador, não esperando por uma ferramenta.
- Pontuação de adequação conta para cima. O anel anima de 0 até a pontuação real em 600ms. As pessoas assistem. Essa espera faz a pontuação parecer merecida.
- Paleta de comandos. ⌘K coloca cada convidado, página e ação a um toque de tecla. Usuários avançados nunca alcançam o mouse.
- Foco de Hoje. O painel diz exatamente quem precisa de atenção hoje (alcance desatualizado, gravações futuras) sem exigir que você lembre o que verificar.
- Lembretes nomeados. "Sara não respondeu em 8 dias" vence "3 acompanhamentos pendentes." Nomeado, específico, acionável.
- Copy com personalidade em estados vazios. "Sua lista de descoberta está vazia. Seu próximo grande episódio está a um alcance de distância" diz o que fazer em seguida. "Nenhum dado encontrado" não diz.
Lista de verificação MLP, obrigatória em cada PR:
- Estados vazios têm copy com personalidade, não "Nenhum dado encontrado"
- Estados de carregamento usam componentes
Skeleton, não telas em branco - Erros têm mensagens acionáveis, não "Algo deu errado"
- Interações principais têm animações Framer Motion
- Qual é o momento uau? Se não houver um, encontre antes do merge.
Preços
SaaS em duas camadas. Preço simples que cresce com o cliente.
| Plano | Preço | Para quem é |
|---|---|---|
| Solo | US$ 29/mês | Apresentadores de podcast independentes gerenciando 1 programa, 20–100 convidados/ano |
| Agência | US$ 99/mês | Agências de booking gerenciando 3+ programas e 200+ pitches/ano |
Créditos de IA baseados em uso acima do nível base: as primeiras 200 chamadas de IA/mês (e-mail de divulgação, pontuação de adequação, briefing, post social) estão incluídas; acima disso, as equipes pagam pelo que usam.
Cenário Competitivo
A lacuna não são os recursos. É o modelo mental.
| Google Sheets | HubSpot / Pipedrive | PodMatch | Podcast Guest CRM | |
|---|---|---|---|---|
| Ciclo de vida do convidado (6 estágios) | manual | campos personalizados necessários | ❌ | integrado, imposto |
| Divulgação por IA (personalizada) | ❌ | ❌ | ❌ | streaming, pontuação de confiança |
| Pontuação de adequação do convidado | ❌ | ❌ | básica | pontuada por IA vs. seus tópicos |
| Briefing de entrevista | ❌ | ❌ | ❌ | um clique, pronto para copiar |
| Sequência de acompanhamento (IA) | ❌ | complemento ($$$) | ❌ | arco de 3 e-mails, escrito por IA |
| Gerador de posts sociais | ❌ | ❌ | ❌ | LinkedIn + Twitter + Instagram |
| Paleta de comandos (⌘K) | ❌ | ❌ | ❌ | navegação completa por teclado |
| Central de notificações inteligente | ❌ | ❌ | ❌ | lembretes + alertas de gravação |
| Workspace multi-programas para agências | ❌ | $$$ | ❌ | incluído |
| Preço | US$ 0 | US$ 45–800/mês | US$ 27–97/mês | US$ 29–99/mês |
O PodMatch resolve descoberta: encontrar convidados. Nós resolvemos fluxo de trabalho: o processo de meses de pitch, acompanhamento, agendamento, preparação, gravação, publicação e manutenção do relacionamento. Esses não são o mesmo problema. As empresas que criaram ferramentas de descoberta deixaram o problema do fluxo de trabalho intocado. Essa é a lacuna.
Pontos de Integração MCP
Cada ponto de integração fica atrás de uma interface. Servidores MCP se encaixam sem refatoração.
| Servidor MCP | Status | Ponto de integração |
|---|---|---|
| GitHub MCP | Ativo em dev | .github/: automação de PR, status de CI, rastreamento de issues a partir do terminal |
| Supabase MCP | Pronto para conectar | packages/db/: consulta o schema ao vivo antes de escrever queries, eliminando bugs de nomes de campos |
| Gmail MCP | Pronto para conectar | apps/api/src/routes/outreach.ts: o envio de divulgação fica atrás de uma interface sendEmail() |
| Google Calendar MCP | Pronto para conectar | apps/api/src/routes/guests.ts: confirmação de booking e sincronização de data de gravação |
| Exa Search MCP | Pronto para conectar | packages/ai/src/prompts/guest-research.ts: dados web ao vivo no pipeline de pontuação de adequação |
Com o Gmail MCP ativo, a divulgação vai de rascunho a enviada em um clique. Com o Calendar MCP, um convidado que muda para Agendado cria o evento de gravação automaticamente. Com o Exa, a pontuação de adequação puxa o trabalho mais recente do convidado da web. Não apenas o que está na bio.
Stack Tecnológica
Cada escolha é defendida. Sem desenvolvimento movido a currículo.
| Camada | Tecnologia | Porquê, honestamente |
|---|---|---|
| Monorepo | Turborepo + pnpm workspaces | Cache de build remoto. Protocolo workspace:*. Um único pnpm install na raiz conecta tudo. |
| Frontend | Next.js 14 App Router | RSC para renderização estática em primeiro lugar. Roteamento baseado em arquivos. Padrão BFF integrado sem gateway separado. |
| UI | Tailwind CSS + shadcn/ui | shadcn copia para o seu repositório. Você é dono do código, não de uma versão. Sem inferno de dependências em releases que quebram. |
| Animações | Framer Motion | Animações de layout em reordenações de lista: uma linha. AnimatePresence lida com montagem/saída. Vale o tamanho do bundle. |
| Drag & Drop | @hello-pangea/dnd | Fork comprovado em produção do react-beautiful-dnd. Mantido. Acessível. Encaixa de forma idêntica. |
| Estado do Servidor | TanStack Query v5 | Stale-while-revalidate. Atualizações otimistas. Refetch automático em segundo plano. O quadro kanban é instantâneo por causa disso. |
| Estado da UI | Zustand | API mínima. Sidebar, modais, paleta de comandos, filtros. Tudo persistido no localStorage em uma linha de middleware. |
| API | Fastify v5 | ~2x mais rápido que Express no p99. TypeScript de primeira classe. @fastify/swagger gera OpenAPI a partir de schemas de rotas automaticamente. |
| Validação | Zod | Um schema = um tipo TypeScript + um validador em runtime. Em todas as rotas. Sem exceções. |
| ORM | Drizzle ORM | Sem geração de código. Schema é TypeScript puro. Migrações são SQL puro. Queries totalmente type-safe. |
| Banco de Dados | SQLite (dev) / Turso (prod) | Zero configuração localmente. Schema idêntico ao de produção. Turso adiciona distribuição global de borda quando precisarmos. |
| IA | claude-sonnet-4-6 | Melhor saída JSON estruturada e profundidade de seguimento de instruções disponível. Os padrões de prompt aqui exigem isso. |
| Auth | Supabase Auth | JWT + Row Level Security. dev-mock-token em dev. RLS impõe isolamento de workspace na camada de banco em prod. |
| Resend + React Email | Templates como componentes React. Versionados, testáveis, visualizáveis no navegador. Mockados em dev. | |
| Gráficos | Recharts | React-native. Composável. Amigável a TypeScript. Venceu o Chart.js em composabilidade para o nosso caso de uso. |
| CI/CD | GitHub Actions | Lint → typecheck → teste → auditoria em todo PR. CodeQL em agendamento semanal. Sem merge sem verde. |
Estrutura do Projeto
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)
Referência da API
Documentação OpenAPI gerada automaticamente em 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 /guestseGET /guests/:idatualmente declaram sua forma de resposta como um{ type: 'object' }puro, sem propriedades listadas emapps/api/src/routes/guests.ts. O serializador JSON do Fastify reduz o corpo para{}em caso de sucesso como resultado, mesmo que a operação tenha sido bem-sucedida.guest listnão é afetado. Veja o FAQ para saber como o CLI contorna isso.
Transições de ciclo de vida impostas na camada de serviço:
discover → outreach → scheduled → recorded → published → follow_up
↑___________↑ ↑__________↑
(reschedule) (re-record needed)
CLI
podcast-guest-crm-cli é um CLI TypeScript real (packages/cli) que envolve a mesma API acima. Cada comando mapeia para uma rota real, sem 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
Adicione --json a qualquer comando que retorna dados para saída legível por máquina, destinada a scripts e agentes:
podcast-guest-crm-cli guest list --stage discover --json
login autentica diretamente contra o endpoint REST de auth do próprio Supabase (POST <SUPABASE_URL>/auth/v1/token?grant_type=password), o mesmo provedor de identidade que o app web usa. Nunca usa o atalho Bearer dev-mock-token apenas para dev em apps/api/src/plugins/auth.ts; esse bypass existe puramente para testes locais de API. A sessão resultante é armazenada em cache em ~/.config/podcast-guest-crm-cli/credentials.json (permissões 0600) e renovada silenciosamente com o refresh token armazenado quando expira.


Servidor MCP
podcast-guest-crm-cli inclui um servidor Model Context Protocol (não confundir com os servidores MCP de terceiros com os quais este app pode se integrar, listados acima). podcast-guest-crm-cli mcp o inicia via stdio, expondo cinco ferramentas que chamam diretamente a mesma costura de API que todo comando CLI usa: list_guests, add_guest, update_guest_stage, draft_outreach_email e 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"]
}
}
}
Um tools/call real para a ferramenta central do ciclo de vida, {"name": "update_guest_stage", "arguments": {"id": "guest_1", "stage": "outreach"}}, retorna o mesmo envelope que guest stage <id> outreach --json imprime no CLI. Veja o README do packages/cli para a referência completa das ferramentas.
Segurança
Controles de nível de produção desde o primeiro dia. Não adaptamos segurança retroativamente.
| Controle | Implementação |
|---|---|
| Autenticação | JWT via @fastify/jwt. Em toda rota: preHandler: [server.authenticate]. Sem exceções, inclusive em dev. |
| Isolamento de workspace | Todas as queries filtram por workspaceId do payload JWT. RLS do Supabase impõe isso na camada de banco em produção. |
| Limite de taxa | 100 req/min por IP via @fastify/rate-limit. Configurável por ambiente. |
| Validação de entrada | Zod em toda rota. Body, query, parâmetros de caminho. Sem schema = sem deploy. |
| Injeção de SQL | Queries parametrizadas do Drizzle ORM em todo o código. Sem SQL puro neste codebase. |
| XSS | Escapamento padrão do Next.js + cabeçalhos CSP restritivos em next.config.ts. |
| Segredos | Schema de env do Zod derruba o servidor na inicialização se variáveis obrigatórias faltarem. Configuração incorreta silenciosa é um bug de segurança. |
| CORS | Baseado em allowlist. Sem wildcard, nunca. |
| SAST | CodeQL em todo PR + agendamento semanal. |
| Dependências | pnpm audit no CI. Quebra o build em vulnerabilidades de alta gravidade. |
Roadmap
Curto prazo (próximos 60 dias):
- MCP: Gmail + Google Calendar: divulgação vai de rascunho a enviada em um clique; confirmações de booking criam eventos de calendário automaticamente
- MCP: Exa Search: pontuação de adequação do convidado puxa dados web ao vivo, não apenas texto da bio
- Cobrança Stripe: Solo US$ 29/mês, Agência US$ 99/mês, créditos de IA baseados em uso acima do nível
Médio prazo:
- Ingestão de transcrições: envie o episódio, gere automaticamente posts sociais e e-mail de acompanhamento referenciando destaques específicos
- Portal do cliente: visualização somente leitura baseada em token para clientes de agência, eliminando o e-mail de relatório semanal
- Conector Zapier / Make: sincronização bidirecional com Cal.com, Notion, HubSpot
- Extração de RSS: insira a URL do RSS do podcast, preencha automaticamente as informações de contato do apresentador e estatísticas do programa
Longo prazo:
- Mobile (React Native): mesma API, experiência nativa, para revisão do pipeline em movimento
- Dashboard multi-programas: visão de agência em todos os programas gerenciados em uma única tela
- Acompanhamento preditivo: modelo de ML treinado em dados de taxa de resposta para otimizar o timing da divulgação
A Equipe
Rudrendu Paul e Sourav Nandy construíram este software nativo de IA pronto para produção.
A stack utilizada:
- Monorepos TypeScript full-stack (Turborepo + pnpm) com pacotes de tipos compartilhados, impostos na camada de CI
- APIs Fastify com schemas validados por Zod, autenticação JWT e Row Level Security. Sem exceções
- Camadas de recursos com IA com módulos de prompt tipados, streaming, extração JSON e backoff exponencial
- Frontends Next.js 14 App Router com TanStack Query, Zustand, Framer Motion e shadcn/ui
- Arquiteturas de subagentes Claude Code que impõem limites de domínio na camada de ferramentas
FAQ
O que é podcast-guest-crm-cli e como é diferente de usar o app web?
É um cliente de linha de comando TypeScript real (packages/cli) para a mesma API que o app web Next.js chama. Ele envolve os endpoints do ciclo de vida do convidado (guest list/add/show/stage), o endpoint de rascunho de divulgação por IA (outreach draft) e os endpoints de análise (analytics summary/pipeline). O diferencial é a saída nativa para agentes: todo comando que retorna dados suporta --json, então um script ou um agente de IA pode conduzir o mesmo pipeline que um humano conduziria pelo dashboard, sem raspar HTML ou manter seu próprio cliente HTTP.
Quais plataformas e runtimes ele suporta?
O pacote npm (podcast-guest-crm-cli no npm, requer Node.js 20 ou mais recente) roda em macOS, Linux e Windows em qualquer lugar onde o Node roda. Um pacote PyPI separado com o mesmo nome (packages/cli-pypi-wrapper) é um wrapper fino para usuários de pip/pipx: ele não reimplementa o CLI em Python, apenas verifica se node e npx estão no PATH e delega para o pacote npm, fixado na versão do próprio wrapper — recorrendo à release latest do npm se essa versão exata nunca foi publicada no npm, em vez de falhar completamente.
Como funciona o login?
podcast-guest-crm-cli login solicita seu e-mail e senha, então autentica diretamente contra o endpoint REST do seu projeto Supabase (POST <SUPABASE_URL>/auth/v1/token?grant_type=password), o mesmo provedor de identidade que o app web usa. Você precisará da URL do projeto Supabase do seu deployment e da anon key (--supabase-url / --supabase-anon-key, ou PODCAST_GUEST_CRM_SUPABASE_URL / PODCAST_GUEST_CRM_SUPABASE_ANON_KEY), correspondendo aos valores que seu deployment já define como NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY. Os tokens de acesso e refresh resultantes são armazenados em cache em ~/.config/podcast-guest-crm-cli/credentials.json com permissões 0600, e o token de acesso renova silenciosamente quando expira.
Por que um comando imprimiu {"data": {}} em vez dos campos que eu esperava?
Essa é uma lacuna real e atual em alguns dos esquemas de resposta Fastify da própria API (apps/api/src/routes/guests.ts), não um bug da CLI: rotas como PATCH /guests/:id/stage, POST /guests e GET /guests/:id declaram sua forma de resposta como um { type: 'object' } simples, sem propriedades listadas, então o serializador JSON do Fastify remove o corpo até um objeto vazio mesmo em caso de sucesso. A CLI detecta isso e recorre a imprimir a resposta bruta (vazia) em vez de travar por um campo ausente. guest list não é afetado, pois seu esquema declara um array sem forma fixa de item.
Posso usar esta CLI em um pipeline automatizado ou entregá-la a um agente de IA?
Sim, esse é o principal objetivo do design. Todo comando que retorna dados aceita --json para saída estruturada, códigos de saída são diferentes de zero em caso de falha, e respostas de erro são objetos JSON com campos error, message e statusCode quando --json está definido. Não há caminho exclusivamente interativo exigido para nenhum comando, exceto o prompt de senha do login, que também aceita as flags --email e --password para uso não interativo. Para agentes nativos de MCP (Claude Desktop, Claude Code), podcast-guest-crm-cli mcp inicia um servidor MCP stdio expondo o mesmo ciclo de vida de convidado, elaboração de alcance e capacidade de análise como ferramentas chamáveis, veja Servidor MCP acima.
Posso usar esta CLI, ou o restante deste código, comercialmente?
Sim. Este repositório (incluindo packages/cli e packages/cli-pypi-wrapper) é licenciado sob MIT, de propriedade conjunta de Rudrendu Paul e Sourav Nandy. Veja LICENÇA para os termos completos; uso comercial, modificação e redistribuição são todos permitidos.
A CLI alguma vez armazena ou transmite minha senha?
Não. A senha que você insere no prompt login é enviada uma vez, via HTTPS, diretamente para o endpoint de concessão de senha do Supabase, e nunca é gravada em disco. Apenas o token de acesso resultante, o token de atualização e sua expiração são armazenados em cache localmente.
O que acontece se minha sessão expirar enquanto estou executando um comando?
A CLI verifica a expiração do token de acesso em cache (com um buffer de 30 segundos) antes de cada requisição. Se estiver expirado, a CLI chama a concessão de token de atualização do Supabase com o token de atualização armazenado, salva a nova sessão e tenta novamente, tudo sem solicitar que você faça login novamente. Você só verá erros login novamente quando o próprio token de atualização for invalidado (por exemplo, após uma alteração de senha).
Licença
MIT. Veja LICENÇA para os termos completos. Uso comercial, modificação e redistribuição são todos permitidos.