.cursor/skills/build-chatwindow/SKILL.md
Construit des chatwindows avec widgets interactifs ChatWidget. Guide la création de widgets selon le contrat ChatWidget, leur intégration dans ChatWindow, la configuration des workflows (OpenAI Agent / n8n), et la validation via le Playground. Utilise ce skill lorsque l'utilisateur demande de créer, modifier ou intégrer des widgets dans une conversation ChatWindow.
npx skillsauth add SomtechSolutionMAxime/somtech-pack build-chatwindowInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Ce skill guide la construction de chatwindows avec widgets interactifs ChatWidget selon l'architecture ChatWindow. Cette architecture est réutilisable dans tout projet utilisant Supabase, React et des workflows (OpenAI Agent Builder / n8n).
Utilisez ce skill lorsque :
ChatWindow est une interface de conversation réutilisable qui s'appuie sur :
ChatWindow + ChatWidget + hook de gestion (ex: useChatKit)openai_agent ou n8n (configuration en base de données)chatkit) qui stream les réponses + widgetsNote pour adaptation : Les noms de composants et fonctions peuvent varier selon votre projet. Adaptez les chemins et noms selon votre structure de code.
Utilisateur → ChatWindow → useChatKit → Edge Function chatkit → Workflow → SSE → Widget
Un widget est envoyé via un événement SSE :
{
"type": "widget",
"widget": {
"id": "w-unique-001",
"type": "button",
"label": "Actions disponibles",
"data": { /* données spécifiques au type */ },
"actions": [ /* ChatWidgetAction[] */ ]
}
}
interface ChatWidget {
id: string; // Identifiant unique stable
type: 'button' | 'form' | 'select' | 'checkbox' | 'radio' | 'summary_confirm';
label?: string; // Libellé affiché
data?: Record<string, unknown>; // Données spécifiques au type
actions?: ChatWidgetAction[]; // Actions disponibles
}
interface ChatWidgetAction {
id: string; // Identifiant unique
label: string; // Libellé du bouton
action: string; // Identifiant stable de l'action
payload?: Record<string, unknown>; // Valeurs fixes fusionnées avec formData
}
⚠️ Important : input existe dans les types mais n'est pas rendu actuellement. Éviter ce type.
button — Actions rapidesUsage : Afficher des boutons d'action rapide sans saisie utilisateur.
Structure data :
data requis (ou vide)actions[]Exemple :
{
"id": "w-actions-001",
"type": "button",
"label": "Actions disponibles",
"actions": [
{
"id": "a-open-linear",
"label": "Ouvrir dans Linear",
"action": "open_url",
"payload": { "url": "https://linear.app/org/issue/APP-123" }
},
{
"id": "a-navigate",
"label": "Voir les détails",
"action": "navigate",
"payload": { "path": "/requests/uuid-ticket" }
}
]
}
form — Collecte d'informationsUsage : Formulaire avec champs multiples pour collecter des données.
Structure data :
{
description?: string; // Description affichée au-dessus du formulaire
fields: Array<{
name: string; // Nom du champ (clé dans formData)
type: 'text' | 'textarea' | 'number' | 'email' | ...;
label: string; // Libellé affiché
required?: boolean; // Champ obligatoire
}>;
// Valeurs initiales (optionnel) : clés au même niveau que fields
titre?: string;
description?: string;
}
Exemple :
{
"id": "w-ticket-form-001",
"type": "form",
"label": "Compléter la demande",
"data": {
"description": "Merci de compléter ces informations avant création.",
"fields": [
{ "name": "titre", "type": "text", "label": "Titre", "required": true },
{ "name": "description", "type": "textarea", "label": "Description", "required": true },
{ "name": "impact", "type": "text", "label": "Impact", "required": false }
],
"titre": "Erreur sur fiche client",
"description": "Quand on ouvre une fiche client, un message d'erreur apparaît.",
"impact": "Blocage partiel"
},
"actions": [
{
"id": "a-create-ticket",
"label": "Créer la demande",
"action": "create_ticket",
"payload": { "module": "clients", "priority": "P1" }
}
]
}
select — Choix unique (liste déroulante)Usage : Permettre à l'utilisateur de choisir une option parmi plusieurs.
Structure data :
{
options: Array<{ value: string; label: string }>;
value?: string; // Valeur présélectionnée (optionnel)
}
Comportement : Si actions.length === 1, l'action est déclenchée automatiquement lors de la sélection.
Exemple :
{
"id": "w-priority-001",
"type": "select",
"label": "Choisir la priorité",
"data": {
"options": [
{ "value": "P0", "label": "P0 (Critique)" },
{ "value": "P1", "label": "P1 (Haute)" },
{ "value": "P2", "label": "P2 (Normale)" },
{ "value": "P3", "label": "P3 (Faible)" }
],
"value": "P2"
},
"actions": [
{ "id": "a-set-priority", "label": "Confirmer", "action": "set_priority" }
]
}
checkbox — Choix multiplesUsage : Permettre à l'utilisateur de sélectionner plusieurs options.
Structure data :
{
options: Array<{ value: string; label: string }>;
}
Comportement : formData[option.value] devient un booléen (true si coché, false sinon).
Exemple :
{
"id": "w-flags-001",
"type": "checkbox",
"label": "Informations disponibles",
"data": {
"options": [
{ "value": "logs", "label": "Logs" },
{ "value": "steps", "label": "Étapes pour reproduire" },
{ "value": "screenshot", "label": "Capture" }
]
},
"actions": [
{ "id": "a-submit-flags", "label": "Continuer", "action": "confirm_flags" }
]
}
radio — Choix unique (boutons radio)Usage : Permettre à l'utilisateur de choisir une option parmi plusieurs (affichage radio).
Structure data :
{
options: Array<{ value: string; label: string }>;
value?: string; // Valeur présélectionnée (optionnel)
}
Comportement : Comme select, auto-trigger si actions.length === 1.
Exemple :
{
"id": "w-type-001",
"type": "radio",
"label": "Type de demande",
"data": {
"options": [
{ "value": "incident", "label": "Incident" },
{ "value": "question", "label": "Question" },
{ "value": "amelioration", "label": "Amélioration" }
],
"value": "incident"
},
"actions": [
{ "id": "a-confirm-type", "label": "Confirmer", "action": "set_type" }
]
}
summary_confirm — Résumé et confirmationUsage : Afficher un résumé lisible avec confirmation avant action finale.
Structure data :
{
title?: string; // Titre du résumé
understood?: string[]; // Liste des points compris
confirmation?: string; // Question de confirmation
next_step?: string; // Prochaine étape après confirmation
}
Spécificité : Le renderer envoie un contexte minimal à l'action ({ widget_id: widget.id }) pour éviter d'envoyer tout widget.data.
Exemple :
{
"id": "w-orchestrator-validation-001",
"type": "summary_confirm",
"data": {
"title": "✅ Demande — Bug",
"understood": [
"Quand vous cliquez sur \"Client\", vous arrivez sur une page blanche.",
"Vous vous attendiez à voir la fiche client s'afficher normalement."
],
"confirmation": "Pouvez-vous confirmer que c'est bien ça ? OK / à corriger",
"next_step": "Parfait, votre demande est prise en charge par l'équipe et sera traitée rapidement."
},
"actions": [
{
"id": "a-confirm-orchestrator",
"label": "Confirmé",
"action": "confirm_orchestrator",
"payload": { "confirmed": true }
}
]
}
Lorsqu'un utilisateur déclenche une action :
payload (valeurs fixes) est fusionné avec formData (valeurs saisies)triggerAction(action.action, mergedPayload)action : Identifiant stable et descriptif (open_url, navigate, create_ticket, set_priority, etc.)payload : Valeurs immuables (ex: { path: "/requests/..." }, { module: "clients" })formData : Valeurs dynamiques saisies par l'utilisateur (fusionnées automatiquement)w-ticket-form-001)data selon le type choisipayloadCréer le widget selon le contrat ChatWidget :
{
"id": "w-unique-id",
"type": "button",
"label": "Libellé du widget",
"data": { /* selon le type */ },
"actions": [ /* ChatWidgetAction[] */ ]
}
/admin/widget-playground ou /widgets/playground)Note : Si votre projet n'a pas encore de Playground, créez-en un en vous basant sur les composants
ChatWidgetexistants. Voir la section "Sources de vérité" pour les fichiers de référence.
Dans le prompt de l'agent, inclure le widget dans la réponse :
{
"type": "widget",
"widget": { /* ChatWidget */ }
}
Dans le webhook n8n, renvoyer :
{
"content": "Message texte",
"widget": { /* ChatWidget */ }
}
Le routeur SSE convertit automatiquement en événement SSE.
ChatWindowWidget ou équivalent)Note : Adaptez cette étape selon votre mécanisme de test. L'important est de valider le widget dans un contexte de conversation réel.
{ type: "widget", widget: ... }) est correctew-{type}-{purpose}-{number} (ex: w-ticket-form-001)action doit être stable et descriptifopen_url, navigate, create_ticket, set_priority, confirm_orchestratorsubmit, click, confirmwidget.data énorme côté backendpayload (valeurs fixes) + champs saisis (formData)summary_confirm, utiliser le contexte minimal ({ widget_id })Si vous devez ajouter un nouveau type de widget :
src/types/chat.ts)src/components/chat/ChatWidget.tsx)src/pages/WidgetPlayground.tsx)agentbuilder/WIDGETS_CONTRACT.md ou équivalent)Note : Adaptez les chemins de fichiers selon la structure de votre projet.
Dans un projet utilisant cette architecture, vous devriez avoir :
agentbuilder/WIDGETS_CONTRACT.md ou docs/widgets-contract.md)src/types/chat.ts ou équivalent)src/components/chat/ChatWidget.tsx)src/components/chat/ChatWindow.tsx)src/hooks/useChatKit.ts ou équivalent)src/pages/WidgetPlayground.tsx)src/components/chat/ChatWindowWidget.tsx)supabase/functions/chatkit/index.ts ou équivalent)docs/chatbot/ ou équivalent)Note pour adaptation : Ces chemins sont des exemples. Adaptez-les selon la structure de votre projet. L'important est d'avoir ces composants/fichiers quelque part dans votre codebase.
references/WIDGET_EXAMPLES.md sont réutilisables tels quelsreferences/WORKFLOW_INTEGRATION.md est applicable à tout projetDans ce pack, une documentation générique est fournie ici :
docs/chatwindow/README.mdNote : Cette doc est conçue pour être réutilisable. Adaptez les chemins/fichiers à votre projet si nécessaire.
Ce skill est conçu pour être réutilisable dans tout projet. Pour l'adapter :
Adaptez les chemins mentionnés selon votre structure :
ChatWidgetChatWindow et ChatWidgetLes noms de composants peuvent varier :
ChatWindow → votre composant de conversationChatWidget → votre composant de widgetuseChatKit → votre hook de gestion (ou équivalent)Le contrat ChatWidget décrit dans ce skill est universel et peut être utilisé tel quel. Seuls les chemins de fichiers et noms de composants doivent être adaptés.
Les processus de validation décrits (Playground + ChatWindow) sont applicables à tout projet. Adaptez uniquement les routes et noms de composants.
tools
Documentation de référence SomCraft — DMS Markdown-native avec AI, MCP server, et Studio. À consulter pour toute question sur l'architecture, les APIs, les concepts, ou l'exploitation d'une instance SomCraft. TRIGGERS : somcraft, dms, document management, workspace somcraft, studio somcraft, mcp somcraft, api somcraft
tools
Déployer une instance SomCraft pour un client existant (migrations Supabase + Fly.io + skills). Orchestre 7 phases : pré-flight, plan, migrations, seed, déploiement, smoke tests, installation des skills. TRIGGERS : deploy-somcraft, déployer somcraft, installer somcraft, somcraft client, setup somcraft, upgrade somcraft, status somcraft
tools
Génère l'intégralité de la configuration d'un silo SomTech : docker-compose, services Fly.io, constitutions d'agents, et templates d'environnement. Valide les métadonnées d'application avant génération. À utiliser après validation initiale du client et avant déploiement.
development
Exécute le déploiement complet d'une silo après sa génération : conteneurs Docker, environnement de développement Fly.io, branche Git, et configuration Netlify. Transforme les configs générées en infrastructure active avec URLs stables et builds automatisés.