Architecture d'un plugin
Comprenez la structure et les composants clés d'un plugin PluginFactory.
Vue d'ensemble
Un plugin PluginFactory suit une architecture claire et modulaire :
Plugin
├── Manifeste (.claude-plugin/marketplace.json)
├── Schémas de validation (Zod)
├── Fonctions (handlers)
├── Tests
└── Documentation
Composants clés
1. Le Manifeste
Fichier : .claude-plugin/marketplace.json
Défini les métadonnées du plugin :
{
"id": "mon-plugin",
"name": "Mon Plugin",
"description": "Description courte",
"version": "1.0.0",
"author": "Votre nom",
"license": "MIT",
"keywords": ["plugin", "claude"],
"homepage": "https://github.com/user/mon-plugin"
}
Champs obligatoires : id, name, version, author
2. Les Schémas Zod
Fichier : schemas/schemas.ts
Définissent la validation des inputs :
import { z } from "zod";
export const MonInputSchema = z.object({
texte: z.string().describe("Texte à traiter"),
option: z.enum(["a", "b", "c"]).optional(),
nombre: z.number().int().positive(),
});
export type MonInput = z.infer<typeof MonInputSchema>;
Avantages :
- Type-safe
- Auto-documentation (
.describe()) - Validation runtime
- Génération OpenAPI automatique
3. Les Handlers
Fichier : plugins/mon-plugin.ts
Implémentent la logique :
import { MonInputSchema, MonInput } from "../schemas/schemas";
export async function traiter(input: unknown): Promise<string> {
const data = MonInputSchema.parse(input);
// Votre logique ici
const resultat = await faireQuelquechose(data.texte);
return resultat;
}
Pattern :
- Parser avec Zod (
parse()) - Exécuter la logique
- Retourner le résultat
4. Les Tests
Fichier : __tests__/mon-plugin.test.ts
Testent chaque handler :
import { describe, it, expect } from "bun:test";
import { traiter } from "../plugins/mon-plugin";
describe("mon-plugin", () => {
it("doit traiter le texte", async () => {
const resultat = await traiter({
texte: "Hello",
nombre: 42,
});
expect(resultat).toBe("Resultat attendu");
});
it("doit valider les inputs", async () => {
expect(() =>
traiter({ texte: "Hello", nombre: -5 })
).toThrow();
});
});
Flux d'exécution
Input
↓
[Validation Zod] ← SchemaError si invalide
↓
Handler Function
↓
Output (string)
Bonnes pratiques
✅ DO
- Utilisez Zod pour tous les inputs
- Retournez des strings claires et structurées
- Testez les cas d'erreur
- Documentez avec
.describe()dans Zod - Gardez les handlers synchrones ou async propres
❌ DON'T
- Ne bypasser pas la validation Zod
- Ne pas mélanger plusieurs responsabilités dans un handler
- Ne pas ignorer les erreurs
- Ne pas faire d'appels réseau sans timeout
- Ne pas supposer que l'input est valide
Exemple complet
// schemas/schemas.ts
export const AnalyzeEmailSchema = z.object({
email: z.string().email().describe("L'email à analyser"),
checkSpam: z.boolean().default(true).describe("Vérifier le spam"),
});
// plugins/email-analyzer.ts
import { AnalyzeEmailSchema } from "../schemas/schemas";
export async function analyzeEmail(input: unknown): Promise<string> {
const { email, checkSpam } = AnalyzeEmailSchema.parse(input);
const analysis = {
valid: isValidEmail(email),
isSpam: checkSpam ? await checkIfSpam(email) : null,
domain: extractDomain(email),
};
return JSON.stringify(analysis, null, 2);
}
// __tests__/email-analyzer.test.ts
describe("analyzeEmail", () => {
it("doit analyser un email valide", async () => {
const result = await analyzeEmail({
email: "user@example.com",
checkSpam: false,
});
const parsed = JSON.parse(result);
expect(parsed.valid).toBe(true);
});
});