Erstelle einen Mod
Lasse Claude einen Claude Code Mod aus einer Beschreibung schreiben, oder schreibe selbst einen, der Werkzeugaufrufe zählt und einen Befehl hinzufügt. Lerne die Reload- und Validierungsschleife.
Ein Mod ist ein Claude Code Plugin mit einer Eingabedatei, genannt das Hooks-Modul: eine JavaScript- oder TypeScript-Datei, deren Funktionen Claude Code aufruft, wenn Ereignisse auftreten. Es gibt zwei Möglichkeiten, einen zu erstellen:
- Claude bitten, ihn zu schreiben: beschreibe, was du möchtest in einer Claude Code Sitzung
- Schreibe ihn selbst: folge dem Tutorial, um zu lernen, wie der Code eines Mods funktioniert. Du brauchst Node.js, einen Bundler oder einen Build-Schritt nicht, da Claude Code
.jsund.tsDateien direkt lädt.
Wenn du dich noch nicht entschieden hast, ob ein Mod das richtige Werkzeug ist, lies zuerst den Vergleich in der Übersicht.
Mods erfordern Claude Code v2.1.287 oder später. Führe in deiner Shell claude --version aus, um dies zu überprüfen. Um zu sehen, ob Mods für dich geladen werden können, siehe Überprüfe, ob Mods geladen werden können.
Claude um einen Mod bitten
Beschreibe den Mod, den du möchtest, in einer interaktiven Claude Code Sitzung, und Claude schreibt ihn. Claude arbeitet von einer integrierten Skill namens plugin-authoring, die ihm sagt, wo der Mod geschrieben werden soll, welche Ereignisse und Methoden deine Version hat, und wie der Mod geladen wird. Claude kann die Skill laden, wenn du um einen Mod bittest, oder du kannst sie selbst laden, indem du /plugin-authoring an der Claude Code Eingabeaufforderung ausführst.
Der Mod wird ausgeführt, sobald du ihn genehmigst, außer in Sitzungen, in denen ein von Claude geschriebener Mod nicht geladen werden kann.
Beschreibe den Mod
Bitte um den Mod in deinen eigenen Worten, zum Beispiel make a mod that shows the current git branch above the prompt. Claude schreibt den Mod in einem eigenen Verzeichnis im Mods-Ordner der Sitzung, das ~/.claude/dev-mods/ gefolgt von der Sitzungs-ID ist. Der vollständige Pfad eines Mods sieht wie ~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/ aus.
In den Berechtigungsmodi default und acceptEdits fragt Claude Code, bevor Claude jede der Dateien des Mods erstellt, da ~/.claude ein geschützter Pfad ist. Genehmige jede Datei, wenn sie auftaucht.
Genehmige den Mod
Wenn Claude die erste Datei speichert, fragt Claude Code, ob Hot Reloading für die Sitzung aktiviert werden soll. Hot Reloading führt die Mods aus, die Claude in dieser Sitzung schreibt, und nimmt jede spätere Änderung auf.
Wähle eine dieser Antworten:
- Für diese Sitzung aktivieren: Die Mods im Mods-Ordner der Sitzung werden geladen, wenn die Runde endet, und werden am Ende jeder Runde neu geladen, die sie ändert. Deine Antwort gilt für die Sitzung, auch nachdem du sie fortgesetzt hast.
- Nicht jetzt: Nichts wird jetzt geladen. Die Dateien bleiben dort, wo Claude sie geschrieben hat, und die Mods werden beim nächsten Start dieser Sitzung geladen. Um zu verhindern, dass ein Mod jemals geladen wird, lösche sein Verzeichnis.
Überprüfe, dass der Mod geladen wurde
Führe /plugin an der Claude Code Eingabeaufforderung aus und drücke Tab, bis die Registerkarte Installed ausgewählt ist. Sie listet den Mod auf, und du kannst ihn dort ausschalten.
Probiere den Mod aus
Nutze das, worum du gebeten hast. Für die Beispiel-Eingabeaufforderung erscheint der aktuelle Branchname über dem Eingabefeld. Wenn der Mod nicht das tut, was du wolltest, sag Claude, was geändert werden soll. Der Mod wird am Ende jeder Runde neu geladen, die seine Dateien ändert, sodass du die Änderung ausprobieren kannst, sobald Claude fertig ist.
Verwende den Mod in anderen Sitzungen
Ein Mod, den Claude geschrieben hat, wird nur in der Sitzung geladen, die ihn erstellt hat, und Claude Code löscht den Mods-Ordner dieser Sitzung, sobald er älter als cleanupPeriodDays ist. Um den Mod zu behalten, kopiere sein Verzeichnis aus dem Mods-Ordner an einen Ort deiner Wahl, z. B. ~/mods/git-branch. Wähle dann, wie du ihn laden möchtest:
- In einer Sitzung, die du startest: Führe in deiner Shell
claude --plugin-dir ~/mods/git-branchaus - Für andere Personen: Füge ihn zu einem Marketplace hinzu, damit sie ihn installieren können
Sitzungen, in denen ein von Claude geschriebener Mod nicht geladen werden kann
Ein Mod, den Claude schreibt, wird nur geladen, nachdem du ihn genehmigt hast, in einem vertrauenswürdigen Arbeitsbereich, in dem Mods ausgeführt werden dürfen. In diesen Sitzungen wird er nicht geladen:
- Niemand ist da, um zu genehmigen: Die Sitzung kann dir keine Eingabeaufforderung anzeigen, wie in einem
claude -pLauf oder imdontAskModus - Der Arbeitsbereich ist nicht vertrauenswürdig: Du hast die Vertrauensaufforderung für das Verzeichnis nicht akzeptiert
- Mods sind gestoppt: Du hast mit
--safe-modeoder--baregestartet, du hastdisableAllHooksgesetzt, oder die verwalteten Einstellungen deiner Organisation blockieren es
Schreibe einen Mod selbst
In diesem Tutorial erstellst du einen Mod namens first-mod, der die Werkzeugaufrufe zählt, die Claude macht, die Anzahl neben dem Spinner anzeigt, während Claude arbeitet, und einen /tally Befehl hinzufügt, der sie ausdruckt. Dann liest du die Typdeklarationen, die Claude Code neben deinem Mod schreibt, und führst claude plugin validate aus. Zusammen zeigen sie dir die Ereignisse und Methoden, die deine Version bietet, und was Claude Code aus deinem Code liest.
Diese Aufzeichnung zeigt den fertigen Mod. Der Spinner zählt Werkzeugaufrufe, /tally druckt die Anzahl, und eine Bearbeitung des Codes wird wirksam, während die Sitzung läuft:
Du schreibst drei Dateien:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
plugin.json: das Manifest des Pluginshooks.json: verweist auf deine Codedateiregister.js: dein Code, genannt das Hooks-Modul
Erstelle das Plugin-Verzeichnis
Erstelle die zwei Verzeichnisse, die die Dateien enthalten:
mkdir -p first-mod/.claude-plugin first-mod/hooks
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
Schreibe das Manifest
Ein Mod ist ein Plugin, und ein Mod braucht ein Manifest. Das Manifest dieses Mods hat keine speziellen Felder. Speichere dies als first-mod/.claude-plugin/plugin.json:
{
"name": "first-mod",
"version": "0.1.0",
"description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
"author": { "name": "Your Name" }
}
Sag Claude Code, wo dein Code ist
Wenn Claude Code ein Plugin lädt, liest es die hooks/hooks.json des Plugins. Der modules Schlüssel in dieser Datei gibt den Pfad zu deinem Code an, und ihn zu haben ist das, was das Plugin zu einem Mod macht. Liste einen Pfad auf, relativ zu hooks.json. Hier verweist es auf register.js, das du im nächsten Schritt schreibst.
Speichere dies als first-mod/hooks/hooks.json:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}
Schreibe den Code
Diese Datei ist der Code des Mods, genannt das Hooks-Modul. Wenn der Mod geladen wird, ruft Claude Code die register Funktion auf, die die Datei exportiert, und übergibt ihr eine Funktion namens on. Jeder Aufruf zu on registriert einen Ereignishandler, genannt ein Hook, für das Ereignis, das er benennt.
Speichere dies als first-mod/hooks/register.js:
// The count, shared by the hooks below
let calls = 0
// Claude Code calls this once when the mod loads
export function register(on) {
// Runs when the session starts, before your first prompt
on('session.start', async ($, e, next) => {
// Add the /tally command
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
// Let the session start as usual
return next(e)
})
// Runs each time Claude is about to use a tool
on('tool.call', async ($, e, next) => {
calls += 1
// Ask Claude Code to draw the interface again, so the new count shows
$.ui.invalidate('ui.render')
// Let the tool run as usual
return next(e)
})
// Runs when you type /tally, and only then, because of the matcher
on('command.run', { command: 'tally' }, async () => {
// The text to print in the transcript
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}
Die Datei behält eine Anzahl in calls und registriert vier Hooks:
session.startwird ausgeführt, wenn die Sitzung startet, vor deiner ersten Eingabeaufforderung, und jedes Mal, wenn der Mod neu geladen wird. Es fügt den/tallyBefehl zu Claude Code hinzu.tool.callwird jedes Mal ausgeführt, wenn Claude ein Werkzeug verwenden wird. Es addiert eins zucallsund bittet Claude Code, die Schnittstelle erneut zu zeichnen.command.runwird ausgeführt, wenn du/tallyeingibst. Es gibt den Text zurück, der gedruckt werden soll.ui.renderwird jedes Mal ausgeführt, wenn Claude Code den Spinner zeichnet. Es fügt die Anzahl nach dem Wort des Spinners hinzu.
Wie der Beispiel-Mod funktioniert erklärt die drei Argumente, die jeder Hook nimmt, und was jeder zurückgibt.
Lade den Mod
Starte Claude Code mit dem --plugin-dir Flag, das ein Plugin-Verzeichnis für eine Sitzung lädt, ohne es zu installieren:
claude --plugin-dir ./first-mod
Probiere den Mod aus
Bitte Claude, etwas zu tun, das ein paar Werkzeugaufrufe dauert, wie list the files here and read the README. Während Claude arbeitet, wird das Wort des Spinners gefolgt von einer Anzahl, die steigt, wie in Thinking · tool calls: 2…. Wenn Claude fertig ist, gib /tally ein und drücke Enter. Das Transkript zeigt first-mod: Claude has made 2 tool calls since this mod loaded, mit deiner eigenen Anzahl. Claude Code stellt den Namen des Plugins vor den Text des Befehls.
Um den Befehl ohne eine interaktive Sitzung zu überprüfen, führe ihn im nicht-interaktiven Modus aus:
claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
Wenn /tally nicht in der Befehlsliste ist, wurde das Modul nicht geladen. Siehe Finde heraus, warum ein Mod nichts tut.
Ändere den Code, während die Sitzung läuft
Lasse die Sitzung offen. In register.js, ändere ' · tool calls: ' zu ' · tools used: ' im ui.render Hook und speichere. Die hervorgehobene Zeile ist die, die sich ändert:
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })
})
Eine Zeile im Transkript sagt, dass first-mod neu geladen wurde und listet seine Hooks auf, und der nächste Spinner verwendet den neuen Text, wie in Thinking · tools used: 1….
Wie der Beispiel-Mod funktioniert
Jede Funktion, die du an on übergibst, ist ein Hook, der ein Ereignishandler ist. Claude Code übergibt jedem Hook die gleichen drei Argumente:
- Die Mods API, genannt
$: jede Methode, die ein Mod aufrufen kann, um sich selbst zu erreichen, in Namespaces wie$.uiund$.command - Das Ereignis, genannt
e: die Eingabe des Ereignisses als einfache Daten, wie der Name und die Argumente eines Werkzeugaufrufs - Der nächste Handler, genannt
next: eine Funktion, die das Ereignis an die anderen Mods und dann an Claude Codes eigenes Verhalten weiterleitet und das Ergebnis zurückgibt
Die Hooks in first-mod handhaben ihre Ereignisse auf die drei Arten, auf die ein Hook kann:
- Beobachten: Der
session.startHook registriert den Befehl, und dertool.callHook zählt den Aufruf und bittet um eine Neuzeichnung. Beide gebennext(e)zurück, sodass die Sitzung startet und das Werkzeug wie gewohnt läuft. - Antworten: Der
command.runHook gibt sein eigenes Ergebnis zurück und ruft niemalsnextauf. Das zweite Argument zuon,{ command: 'tally' }, ist ein Filter, genannt ein Matcher, sodass der Hook nur für/tallyläuft. - Umschreiben: Der
ui.renderHook ruftnextmit einer Kopie voneauf, derensuffixdie Anzahl hält, sodass Claude Code seinen üblichen Spinner mit deinem Text nach dem Wort zeichnet
Claude Code beobachtet ein Verzeichnis, das mit --plugin-dir geladen wird, und lädt das Hooks-Modul neu, wenn sich eine Datei darin ändert. Jedes Neuladen führt register erneut aus, sodass calls auf 0 zurückgeht und /tally wieder zu zählen beginnt. Um einen Wert über Neuladen hinweg zu behalten, siehe Behalte den Status.
Arbeite weiter an einem Mod
Sobald ein Mod geladen ist, kannst du Claude ihn ändern lassen, deinen Code gegen die Typdefinitionen für deine Version überprüfen, die Ereignisse und Aufrufe auflisten, die Claude Code darin findet, und ihn testen.
Ändere einen Mod mit Claude
Um einen Mod zu ändern, den du bereits hast, starte die Sitzung mit --plugin-dir auf das Verzeichnis des Mods gerichtet, sodass das, was Claude schreibt, in der gleichen Sitzung geladen wird:
claude --plugin-dir ./first-mod
Dann bitte um die Änderung, zum Beispiel add a /tally-reset command to this mod that sets the tally back to zero. Claude bearbeitet das Hooks-Modul, führt claude plugin validate aus und behebt, was es meldet. Ein Verzeichnis, das du mit --plugin-dir lädst, ist ein geschützter Pfad, sodass du in den Modi default und acceptEdits aufgefordert wirst, jede von Claudes Bearbeitungen des Mods zu genehmigen. Die Tabelle der geschützten Pfade gibt das Ergebnis für die anderen Berechtigungsmodi.
Dateien, die Claude während seiner Runde speichert, werden neu geladen, wenn die Runde endet, sodass du /tally-reset ausprobieren kannst, sobald Claude fertig ist.
Erhalte Typdeklarationen für deine Version
Jedes Mal, wenn Claude Code einen Mod aus einem Verzeichnis lädt oder neu lädt, das du an --plugin-dir übergibst, oder einen Mod Claude für dich geschrieben hat, schreibt es TypeScript-Deklarationsdateien, die auf .d.ts enden, in .claude-plugin/types/ im Verzeichnis des Mods. Sie beschreiben die genauen Ereignisse, Mods API Methoden und Elemente in der Claude Code Version, die du ausführst, sodass dein Editor Autovervollständigung und Typprüfung für deine Hooks durchführen kann. Um die Deklarationen online zu durchsuchen, lies mods/types/claude-code.d.ts im Claude Code Repository, dessen erste Zeile die Version benennt, die es geschrieben hat. Das Verzeichnis enthält diese Dateien:
| Pfad | Was es deklariert |
|---|---|
claude-code/index.d.ts |
Jedes Ereignis und seine Eingabe und sein Ergebnis, jeder Mods API Namespace und Methode, und die Elemente, die jede Oberfläche zeichnen kann |
claude-code-tools/index.d.ts |
Die Eingaben der integrierten Werkzeuge, sodass die Überprüfung von e.tool === 'Bash' e einengt |
claude-code-mcp/index.d.ts |
Die Eingaben der MCP Werkzeuge, die das letzte Mal verbunden waren, als du eine Datei im Mod gespeichert hast |
index.d.ts in einem Verzeichnis, das nach einem Plugin benannt ist |
Was dieses Plugin zur Mods API hinzufügt. Es gibt ein Verzeichnis für jedes Plugin, das deine plugin.json unter dependencies auflistet. |
tsconfig.json |
Compiler-Optionen, die zu einem Hooks-Modul passen |
Wenn dein Mod keine eigene tsconfig.json hat, fügt Claude Code eine an der Wurzel des Mods hinzu, die die generierte erweitert, sodass dein Editor und tsc -p ./first-mod den Mod ohne weitere Einrichtung typprüfen.
Die Ereignisse und Methoden können sich zwischen Versionen ändern, daher vertraue diesen Dateien über jede Seite, einschließlich dieser, wenn sie nicht übereinstimmen.
claude-code/index.d.ts ist die vollständigste Referenz für deinen Build, mit einem Kommentar und einem Beispiel für jede Mods API Methode. Um etwas nachzuschlagen, durchsuche die Datei nach seinem Namen, wie 'tool.call'.
Überprüfe, was Claude Code aus deinem Mod liest
Um deinen Mod so zu sehen, wie Claude Code ihn sieht, ohne deinen Code auszuführen oder eine Sitzung zu starten, verwende claude plugin validate. Es überprüft das Manifest und führt die gleiche statische Analyse auf der Quelle des Hooks-Moduls durch, die Claude Code durchführt, wenn es einen Mod lädt. Führe es in deiner Shell auf dem Verzeichnis des Mods aus:
claude plugin validate ./first-mod
Für first-mod enthält die Ausgabe diese Zeilen.
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
Die hooks: Zeile listet die Ereignisse auf, die dein Modul hookt, jeweils mit seinem Filter in Klammern. Die calls: Zeile listet jede Mods API Methode auf, die es aufruft. Ein Modul, das Umgebungsvariablen liest oder setzt, erhält auch env reads: und env writes: Zeilen, und eines, das $.state verwendet, erhält state reads: und state writes:.
Wenn ein Ereignis, das du hooken wolltest, in der ersten Zeile fehlt, wird Claude Code diesen Hook auch nicht aufrufen. Die übliche Ursache ist ein falsch geschriebener Ereignisname, den der Befehl als Fehler meldet, wie "tool.calls" is not an event.
Befolge diese Regeln, damit statische Analyse jeden Hook und Aufruf finden kann:
- Schreibe jeden Mods API Aufruf vollständig:
$, den Namespace, dann die Methode, wie in$.store.get('notes'). Du kannst$an eine Funktion übergeben, die auf der obersten Ebene der gleichen Datei deklariert ist, und für eine Funktion von dir namensloadNotes, liest diecalls:Zeile dann$.store.get (via loadNotes). Das Übergeben von$an eine Methode, eine Funktion, die im Hook definiert ist, oder eine Funktion, die du aus einer anderen deiner Dateien importierst, schlägt die Validierung fehl. DiereadundupdateFunktionen, die$.stateverwendet, sind die Importe, die es nehmen können. Weise$oder einen seiner Namespaces nicht einer Variablen zu, destrukturiere ihn nicht, und indexiere ihn nicht mit einem berechneten Namen.const ui = $.uischlägt mit$.ui is used as a valuefehl. - Schreibe den Ereignisnamen in jedem
onAufruf als String-Literal, wie'tool.call'. Eine Variable oder eine Schleife über eine Liste von Namen schlägt mitthe event name passed to on() is not a string literalfehl. - Deklariere innerhalb von
registerkeine zweite Variable oder keinen Parameter namenson. Die Validierung schlägt mit"on" is declared again (shadowed)fehl. - Importiere nur aus Dateien im Plugin-Verzeichnis, nach relativem Pfad. Der einzige erlaubte bloße Import ist
claude-code, für Typen und ein paar Helfer. - Verwende
importDeklarationen am Anfang der Datei, wie inimport { name } from './file.js'. Ein dynamischerimport()schlägt mita dynamic import(); a hooks module imports its own files with an import declarationfehl. - Schreibe jede Datei als ES-Modul, mit
importund nichtrequire. Die Referenz listet die Dateierweiterungen auf, die Claude Code lädt.
Teste den Mod
Du kannst automatisierte Tests für einen Mod schreiben und sie von deiner Shell mit claude plugin test ausführen, ohne Sitzung, Anmeldung oder Netzwerk. Ein Test löst die Ereignisse aus, die deine Hooks handhaben, und überprüft, was die Hooks taten.
Dieser Test löst zwei Werkzeugaufrufe aus, führt /tally aus und überprüft, dass die Antwort beide zählt. Speichere ihn als first-mod/tests/first-mod.test.ts:
import { expect, test } from 'claude-code/testing'
test('/tally reports the tool calls the mod has seen', async ($, on) => {
// Answer each tool call in Claude Code's place, so no tool runs
on('tool.call', () => ({ result: 'ok' }))
// Raise two tool calls, which the mod's tool.call hook counts
await $.tool.call({ tool: 'Bash', command: 'ls' })
await $.tool.call({ tool: 'Read', file_path: 'README.md' })
// Run /tally and check the text its hook returns
const answer = await $.command.run({ command: 'tally', args: '' })
expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
Führe die Tests von deiner Shell aus dem first-mod Verzeichnis aus:
claude plugin test
Die Ausgabe benennt jeden Test und ob er bestanden hat, mit Timings, die von Lauf zu Lauf variieren:
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]
1 pass
0 fail
Ran 1 test across 1 file. [0.19s]
Teste einen Mod behandelt das Stubben eines Modellaufrufs oder des Speichers, und das Testen von Timern und Zeichnungen.
Teile deinen Mod
Ein Mod ist ein Plugin, daher versionierst du ihn im Manifest und Personen installieren und aktualisieren ihn mit den /plugin Befehlen. Um ihn anderen Personen zu geben, füge ihn zu einem Marketplace hinzu.
Bevor du das tust, überprüfe den Namen des Plugins: claude plugin validate schlägt einen Namen fehl, der wie einer von Anthropics eigenen aussieht, wie einer, der mit claude- beginnt. Die Ereignisse und Methoden können sich zwischen Versionen ändern, daher ist deine README der Ort, um zu sagen, welche Claude Code Version du getestet hast.
Entwickle weiter gegen das Verzeichnis mit --plugin-dir, nicht gegen eine installierte Kopie. Claude Code speichert ein installiertes Plugin nach Version, sodass deine Bearbeitungen die installierte Kopie nicht erreichen, bis du die Version erhöhst und erneut installierst.
Nächste Schritte
- Zeichne in der Schnittstelle: öffne einen Bereich, zeichne über der Eingabeaufforderung, und füge Schaltflächen und Textfelder hinzu
- Reagiere auf Ereignisse: hook Werkzeugaufrufe, Eingabeaufforderungen und Runden
- Verwende die Mods API: füge Befehle und Werkzeuge hinzu, rufe ein Modell auf, und führe Arbeit auf einem Timer aus
- Teste einen Mod: stub, was Claude Code antworten würde, und teste Timer und Zeichnungen
- Behebe einen Mod: die Gründe, warum ein Mod nichts tut, und das Debug-Protokoll
- Lese die Quelle von integrierten Mods: vollständige Plugins, jeweils mit seinem Hooks-Modul und Tests