Skip to main content

sesion-mental-health-orchestration

SaaS platform for psychology clinics in Argentina with AI-powered scheduling, WhatsApp automation, AFIP billing, and video consultations

Ir para a instalação

Informações da origem

Repositório
reason-machines/devtools-skills
Última atividade na origem
12 de julho de 2026 às 11:00
Idioma detectado do SKILL.md
inglês
Estrelas
4
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
sesion-mental-health-orchestration
description
SaaS platform for psychology clinics in Argentina with AI-powered scheduling, WhatsApp automation, AFIP billing, and video consultations
triggers
["how do I set up Sesión for a psychology clinic","integrate WhatsApp automation with Sesión","configure AFIP electronic invoicing in Sesión","implement Claude AI for clinical notes in Sesión","set up video consultations with Sesión","create appointment scheduling with Sesión workflow","configure Sesión for Argentine mental health practice","integrate Mercado Pago payments in Sesión"]
# Sesión Mental Health Orchestration Skill > Skill by [ara.so](https://ara.so) — Devtools Skills collection. ## Overview Sesión is a comprehensive SaaS platform for psychology clinics and independent practitioners in Argentina. It orchestrates appointment scheduling, automated WhatsApp messaging (via Baileys), AFIP-compliant electronic invoicing, secure video consultations (LiveKit), and AI-powered clinical assistance using Claude Opus 4.6 and Sonnet 4.6. **Tech Stack:** - **Backend:** NestJS + TypeScript - **Frontend:** SvelteKit 5 + Tailwind CSS - **Database:** Prisma ORM (PostgreSQL) - **AI:** Anthropic Claude (Opus & Sonnet) - **WhatsApp:** Baileys library - **Video:** LiveKit - **Payments:** Stripe + Mercado Pago ## Installation & Setup ### Prerequisites ```bash node >= 18.0.0 postgresql >= 14 redis >= 6 ``` ### Project Setup ```bash # Clone repository git clone https://github.com/fahad-hamid/psique-workflow-clinic.git cd psique-workflow-clinic # Install dependencies npm install # Set up environment variables cp .env.example .env ``` ### Environment Configuration ```bash # .env DATABASE_URL="postgresql://user:password@localhost:5432/sesion" REDIS_URL="redis://localhost:6379" # Anthropic AI ANTHROPIC_API_KEY=your_anthropic_api_key # WhatsApp (Baileys) WHATSAPP_SESSION_PATH="./whatsapp-sessions" # LiveKit Video LIVEKIT_API_KEY=your_livekit_api_key LIVEKIT_API_SECRET=your_livekit_api_secret LIVEKIT_URL=wss://your-livekit-server.com # AFIP Argentina AFIP_CUIT=your_clinic_cuit AFIP_CERT_PATH="./certs/afip-cert.pem" AFIP_KEY_PATH="./certs/afip-key.key" # Payments STRIPE_SECRET_KEY=your_stripe_secret_key MERCADOPAGO_ACCESS_TOKEN=your_mercadopago_token # App JWT_SECRET=your_jwt_secret APP_URL=http://localhost:5173 API_URL=http://localhost:3000 ``` ### Database Migration ```bash # Generate Prisma client npx prisma generate # Run migrations npx prisma migrate dev # Seed initial data npx prisma db seed ``` ### Start Development Servers ```bash # Start backend (NestJS) npm run start:dev # Start frontend (SvelteKit) in separate terminal cd frontend npm run dev ``` ## Core Architecture ### Prisma Schema Structure ```prisma // prisma/schema.prisma model Patient { id String @id @default(cuid()) name String email String @unique phone String whatsappOptIn Boolean @default(true) createdAt DateTime @default(now()) appointments Appointment[] invoices Invoice[] clinicalNotes ClinicalNote[] } model Appointment { id String @id @default(cuid()) patientId String practitionerId String startTime DateTime endTime DateTime type AppointmentType // PRESENCIAL, VIRTUAL, EVALUACION status AppointmentStatus // SCHEDULED, CONFIRMED, CANCELLED, COMPLETED patient Patient @relation(fields: [patientId], references: [id]) practitioner Practitioner @relation(fields: [practitionerId], references: [id]) videoSession VideoSession? } model Invoice { id String @id @default(cuid()) patientId String appointmentId String afipReceiptType String // A, B, C, M amount Decimal status InvoiceStatus afipCAE String? // AFIP authorization code afipCAEExpiry DateTime? patient Patient @relation(fields: [patientId], references: [id]) } ``` ## NestJS Backend Services ### Appointment Scheduling Service ```typescript // src/appointments/appointments.service.ts import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { WhatsAppService } from '../whatsapp/whatsapp.service'; import { AppointmentType, AppointmentStatus } from '@prisma/client'; @Injectable() export class AppointmentsService { constructor( private prisma: PrismaService, private whatsapp: WhatsAppService, ) {} async createAppointment(data: { patientId: string; practitionerId: string; startTime: Date; type: AppointmentType; }) { // Check for conflicts const conflicts = await this.checkConflicts( data.practitionerId, data.startTime, ); if (conflicts.length > 0) { throw new Error('Scheduling conflict detected'); } const appointment = await this.prisma.appointment.create({ data: { ...data, endTime: new Date(data.startTime.getTime() + 45 * 60000), // 45 min status: AppointmentStatus.SCHEDULED, }, include: { patient: true, }, }); // Send WhatsApp confirmation await this.whatsapp.sendAppointmentConfirmation(appointment); return appointment; } async checkConflicts(practitionerId: string, startTime: Date) { const endTime = new Date(startTime.getTime() + 45 * 60000); return this.prisma.appointment.findMany({ where: { practitionerId, status: { in: [AppointmentStatus.SCHEDULED, AppointmentStatus.CONFIRMED], }, OR: [ { startTime: { lte: startTime }, endTime: { gt: startTime }, }, { startTime: { lt: endTime }, endTime: { gte: endTime }, }, ], }, }); } async scheduleReminders(appointmentId: string) { const appointment = await this.prisma.appointment.findUnique({ where: { id: appointmentId }, include: { patient: true }, }); // Schedule 24h reminder const reminder24h = new Date(appointment.startTime.getTime() - 24 * 60 * 60000); // Schedule 2h reminder const reminder2h = new Date(appointment.startTime.getTime() - 2 * 60 * 60000); // Use job queue for scheduling (e.g., Bull) await this.scheduleWhatsAppReminder(appointment, reminder24h); await this.scheduleWhatsAppReminder(appointment, reminder2h); } } ``` ### WhatsApp Automation with Baileys ```typescript // src/whatsapp/whatsapp.service.ts import { Injectable, OnModuleInit } from '@nestjs/common'; import makeWASocket, { DisconnectReason, useMultiFileAuthState } from '@whiskeysockets/baileys'; import { Boom } from '@hapi/boom'; import { PrismaService } from '../prisma/prisma.service'; @Injectable() export class WhatsAppService implements OnModuleInit { private sock: any; constructor(private prisma: PrismaService) {} async onModuleInit() { await this.connectToWhatsApp(); } async connectToWhatsApp() { const { state, saveCreds } = await useMultiFileAuthState( process.env.WHATSAPP_SESSION_PATH, ); this.sock = makeWASocket({ auth: state, printQRInTerminal: true, }); this.sock.ev.on('creds.update', saveCreds); this.sock.ev.on('connection.update', (update) => { const { connection, lastDisconnect } = update; if (connection === 'close') { const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut; if (shouldReconnect) { this.connectToWhatsApp(); } } }); this.sock.ev.on('messages.upsert', async ({ messages }) => { await this.handleIncomingMessage(messages[0]); }); } async sendAppointmentConfirmation(appointment: any) { if (!appointment.patient.whatsappOptIn) return; const message = `Hola ${appointment.patient.name}, Tu sesión ha sido agendada para: 📅 ${this.formatDate(appointment.startTime)} ⏰ ${this.formatTime(appointment.startTime)} ${appointment.type === 'VIRTUAL' ? '💻 Sesión virtual' : '🏥 Sesión presencial'} Por favor confirmá tu asistencia respondiendo con SÍ.`; await this.sendMessage(appointment.patient.phone, message); } async sendMessage(phoneNumber: string, text: string) { const formattedPhone = `${phoneNumber}@s.whatsapp.net`; await this.sock.sendMessage(formattedPhone, { text }); } async handleIncomingMessage(message: any) { const text = message.message?.conversation?.toLowerCase(); const phone = message.key.remoteJid.replace('@s.whatsapp.net', ''); // Crisis detection const crisisKeywords = ['crisis', 'suicidio', 'emergencia', 'ayuda urgente']; if (crisisKeywords.some(keyword => text?.includes(keyword))) { await this.escalateCrisis(phone, text); return; } // Appointment confirmation if (text === 'sí' || text === 'si') { await this.confirmAppointment(phone); } } async escalateCrisis(phone: string, message: string) { // Find patient const patient = await this.prisma.patient.findFirst({ where: { phone }, include: { appointments: { include: { practitioner: true } } }, }); // Alert practitioner immediately // Implementation depends on notification system } private formatDate(date: Date): string { return date.toLocaleDateString('es-AR', { weekday: 'long', day: 'numeric', month: 'long', }); } private formatTime(date: Date): string { return date.toLocaleTimeString('es-AR', { hour: '2-digit', minute: '2-digit', }); } } ``` ### AFIP Electronic Invoicing ```typescript // src/billing/afip.service.ts import { Injectable } from '@nestjs/common'; import { AfipWebService } from '@afipsdk/afip.js'; import { PrismaService } from '../prisma/prisma.service'; import * as fs from 'fs'; @Injectable() export class AfipService { private afip: any; constructor(private prisma: PrismaService) { this.afip = new AfipWebService({ CUIT: process.env.AFIP_CUIT, cert: fs.readFileSync(process.env.AFIP_CERT_PATH), key: fs.readFileSync(process.env.AFIP_KEY_PATH), production: process.env.NODE_ENV === 'production', }); } async generateInvoice(data: { patientId: string; appointmentId: string; amount: number; receiptType: 'A' | 'B' | 'C' | 'M'; }) { const patient = await this.prisma.patient.findUnique({ where: { id: data.patientId }, }); // Get last invoice number const lastInvoice = await this.prisma.invoice.findFirst({ where: { afipReceiptType: data.receiptType }, orderBy: { createdAt: 'desc' }, }); const invoiceNumber = lastInvoice ? lastInvoice.id + 1 : 1; // Request CAE from AFIP const caeResponse = await this.afip.ElectronicBilling.createVoucher({ CantReg: 1, PtoVta: 1, CbteTipo: this.getReceiptTypeCode(data.receiptType), Concepto: 2, // Services DocTipo: 80, // CUIT DocNro: patient.taxId || 0, CbteDesde: invoiceNumber, CbteHasta: invoiceNumber, CbteFch: this.formatAfipDate(new Date()), ImpTotal: data.amount, ImpTotConc: 0, ImpNeto: data.amount, ImpOpEx: 0, ImpIVA: 0, ImpTrib: 0, MonId: 'PES', MonCotiz: 1, }); // Save invoice with CAE const invoice = await this.prisma.invoice.create({ data: { patientId: data.patientId, appointmentId: data.appointmentId, afipReceiptType: data.receiptType, amount: data.amount, status: 'APPROVED', afipCAE: caeResponse.CAE, afipCAEExpiry: this.parseAfipDate(caeResponse.CAEFchVto), }, }); return invoice; } private getReceiptTypeCode(type: string): number { const codes = { A: 1, B: 6, C: 11, M: 51 }; return codes[type]; } private formatAfipDate(date: Date): string { return date.toISOString().slice(0, 10).replace(/-/g, ''); } private parseAfipDate(dateString: string): Date { return new Date( `${dateString.slice(0, 4)}-${dateString.slice(4, 6)}-${dateString.slice(6, 8)}`, ); } } ``` ### Claude AI Integration ```typescript // src/ai/claude.service.ts import { Injectable } from '@nestjs/common'; import Anthropic from '@anthropic-ai/sdk'; import { PrismaService } from '../prisma/prisma.service'; @Injectable() export class ClaudeService { private anthropic: Anthropic; constructor(private prisma: PrismaService) { this.anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, }); } async generateClinicalSummary(appointmentId: string) { const appointment = await this.prisma.appointment.findUnique({
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub