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 :

  1. Parser avec Zod (parse())
  2. Exécuter la logique
  3. 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);
  });
});

Prochaines étapes

Schémas Zod

Previous

← Page précédente

Next

Page suivante →