Creare un mod
Chiedi a Claude di scrivere un mod di Claude Code a partire da una descrizione, oppure scrivine uno tu che conta le chiamate agli strumenti e aggiunge un comando. Impara il ciclo di ricaricamento e convalida.
Un mod è un plugin di Claude Code con un file di ingresso, chiamato modulo degli hook: un file JavaScript o TypeScript le cui funzioni vengono chiamate da Claude Code quando si verificano degli eventi. Per crearne uno:
- Chiedi a Claude di scriverlo: descrivi ciò che vuoi in una sessione di Claude Code
- Scrivilo tu: segui il tutorial per imparare come funziona il codice di un mod. Non ti servono Node.js, un bundler o un passaggio di build, perché Claude Code carica direttamente i file
.jse.ts.
Se non hai ancora deciso se un mod è lo strumento giusto, leggi prima il confronto nella panoramica.
I mod richiedono Claude Code v2.1.287 o versioni successive. Nella tua shell, esegui claude --version per verificarlo. Per capire se i mod possono essere caricati nel tuo caso, consulta Verificare se i mod possono essere caricati.
Chiedi a Claude un mod
Descrivi il mod che desideri in una sessione interattiva di Claude Code e Claude lo scrive. Claude lavora a partire da una skill integrata chiamata plugin-authoring, che gli indica dove scrivere il mod, quali eventi e metodi ha la tua versione e come viene caricato il mod. Claude può caricare la skill quando chiedi un mod, oppure puoi caricarla tu stesso eseguendo /plugin-authoring nel prompt di Claude Code.
Il mod viene eseguito una volta che lo approvi, tranne nelle sessioni in cui un mod scritto da Claude non può essere caricato.
Descrivi il mod
Chiedi il mod con parole tue, ad esempio make a mod that shows the current git branch above the prompt. Claude scrive il mod in una propria directory all'interno della cartella dei mod della sessione, che è ~/.claude/dev-mods/ seguita dall'ID della sessione. Il percorso completo di un mod è simile a ~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/.
Nelle modalità di permesso default e acceptEdits, Claude Code chiede conferma prima che Claude crei ciascuno dei file del mod, perché ~/.claude è un percorso protetto. Approva ogni file man mano che viene proposto.
Approva il mod
Quando Claude salva il primo file, Claude Code chiede se abilitare il ricaricamento a caldo per la sessione. Il ricaricamento a caldo esegue i mod che Claude scrive in questa sessione e recepisce ogni modifica successiva.
Scegli una di queste risposte:
- Enable for this session: i mod nella cartella dei mod della sessione vengono caricati al termine del turno e ricaricati alla fine di ogni turno che li modifica. La tua risposta vale per tutta la sessione, anche dopo averla ripresa.
- Not now: per ora non viene caricato nulla. I file restano dove Claude li ha scritti e i mod vengono caricati al successivo avvio di quella sessione. Per impedire che un mod venga mai caricato, elimina la sua directory.
Verifica che il mod sia stato caricato
Esegui /plugin nel prompt di Claude Code e premi Tab finché non è selezionata la scheda Installed. Questa elenca il mod, e da lì puoi disattivarlo.
Prova il mod
Usa ciò che hai chiesto. Per il prompt di esempio, il nome del branch corrente appare sopra la casella del prompt. Se il mod non fa ciò che volevi, di' a Claude cosa cambiare. Il mod si ricarica alla fine di ogni turno che ne modifica i file, quindi puoi provare la modifica non appena Claude termina.
Usare il mod in altre sessioni
Un mod scritto da Claude viene caricato solo nella sessione che l'ha creato, e Claude Code elimina la cartella dei mod di quella sessione una volta che supera cleanupPeriodDays. Per conservare il mod, copia la sua directory fuori dalla cartella dei mod in una posizione tua, come ~/mods/git-branch. Poi scegli come caricarlo:
- In una sessione che avvii tu: nella tua shell, esegui
claude --plugin-dir ~/mods/git-branch - Per altre persone: aggiungilo a un marketplace in modo che possano installarlo
Sessioni in cui un mod scritto da Claude non può essere caricato
Un mod scritto da Claude viene caricato solo dopo che lo hai approvato, in un workspace attendibile in cui i mod possono essere eseguiti. In queste sessioni non viene caricato:
- Nessuno è presente per approvare: la sessione non può mostrarti una richiesta di conferma, come in un'esecuzione
claude -po in modalitàdontAsk - Il workspace non è attendibile: non hai accettato la richiesta di attendibilità per la directory
- I mod sono disabilitati: hai avviato con
--safe-modeo--bare, hai impostatodisableAllHooks, oppure le impostazioni gestite della tua organizzazione lo bloccano
Scrivi un mod da solo
In questo tutorial crei un mod chiamato first-mod che conta le chiamate agli strumenti effettuate da Claude, mostra il conteggio accanto allo spinner mentre Claude lavora e aggiunge un comando /tally che lo stampa. Poi leggi le dichiarazioni di tipo che Claude Code scrive accanto al tuo mod ed esegui claude plugin validate. Insieme ti mostrano gli eventi e i metodi offerti dalla tua versione e cosa Claude Code legge dal tuo codice.
Questa registrazione mostra il mod finito. Lo spinner conta le chiamate agli strumenti, /tally stampa il conteggio e una modifica al codice ha effetto mentre la sessione è in esecuzione:
Scrivi tre file:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
plugin.json: il manifest del pluginhooks.json: punta al tuo file di codiceregister.js: il tuo codice, chiamato modulo degli hook
Crea la directory del plugin
Crea le due directory che contengono i file:
mkdir -p first-mod/.claude-plugin first-mod/hooks
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
Scrivi il manifest
Un mod è un plugin, e un mod ha bisogno di un manifest. Il manifest di questo mod non ha campi speciali. Salva questo come 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" }
}
Indica a Claude Code dove si trova il tuo codice
Quando Claude Code carica un plugin, legge il file hooks/hooks.json del plugin. La chiave modules in quel file fornisce il percorso del tuo codice, ed è la sua presenza a rendere il plugin un mod. Elenca un percorso, relativo a hooks.json. Qui punta a register.js, che scrivi nel passaggio successivo.
Salva questo come first-mod/hooks/hooks.json:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}
Scrivi il codice
Questo file è il codice del mod, chiamato modulo degli hook. Quando il mod viene caricato, Claude Code chiama la funzione register esportata dal file e le passa una funzione chiamata on. Ogni chiamata a on registra un gestore di eventi, chiamato hook, per l'evento che nomina.
Salva questo come 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 + '…' } })
})
}
Il file tiene un conteggio in calls e registra quattro hook:
session.startviene eseguito all'avvio della sessione, prima del tuo primo prompt, e di nuovo ogni volta che il mod viene ricaricato. Aggiunge il comando/tallya Claude Code.tool.callviene eseguito ogni volta che Claude sta per usare uno strumento. Aggiunge uno acallse chiede a Claude Code di ridisegnare l'interfaccia.command.runviene eseguito quando digiti/tally. Restituisce il testo da stampare.ui.renderviene eseguito ogni volta che Claude Code disegna lo spinner. Aggiunge il conteggio dopo la parola dello spinner.
Come funziona il mod di esempio spiega i tre argomenti che ogni hook riceve e cosa restituisce ciascuno.
Carica il mod
Avvia Claude Code con il flag --plugin-dir, che carica una directory di plugin per una sessione senza installarla:
claude --plugin-dir ./first-mod
Prova il mod
Chiedi a Claude di fare qualcosa che richieda alcune chiamate agli strumenti, come list the files here and read the README. Mentre Claude lavora, la parola dello spinner è seguita da un conteggio che aumenta, come in Thinking · tool calls: 2…. Quando Claude finisce, digita /tally e premi Invio. La trascrizione mostra first-mod: Claude has made 2 tool calls since this mod loaded, con il tuo conteggio. Claude Code antepone il nome del plugin al testo del comando.
Per verificare il comando senza una sessione interattiva, eseguilo in modalità non interattiva:
claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
Se /tally non è nell'elenco dei comandi, il modulo non è stato caricato. Consulta Scopri perché un mod non fa nulla.
Modifica il codice mentre la sessione è in esecuzione
Lascia aperta la sessione. In register.js, cambia ' · tool calls: ' in ' · tools used: ' nell'hook ui.render e salva. La riga evidenziata è quella che cambia:
// 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 + '…' } })
})
Una riga nella trascrizione indica che first-mod è stato ricaricato ed elenca i suoi hook, e lo spinner successivo usa il nuovo testo, come in Thinking · tools used: 1….
Come funziona il mod di esempio
Ogni funzione che passi a on è un hook, ovvero un gestore di eventi. Claude Code passa a ogni hook gli stessi tre argomenti:
- L'API dei mod, chiamata
$: ogni metodo che un mod può chiamare per raggiungere l'esterno, organizzato in namespace come$.uie$.command - L'evento, chiamato
e: l'input dell'evento come dati semplici, ad esempio il nome e gli argomenti di una chiamata a uno strumento - Il gestore successivo, chiamato
next: una funzione che passa l'evento agli altri mod e poi al comportamento proprio di Claude Code, e restituisce il risultato
Gli hook in first-mod gestiscono i loro eventi in questi modi:
- Osservare: l'hook
session.startregistra il comando e l'hooktool.callconta la chiamata e richiede un ridisegno. Entrambi restituiscononext(e), quindi la sessione si avvia e lo strumento viene eseguito come di consueto. - Rispondere: l'hook
command.runrestituisce il proprio risultato e non chiama mainext. Il secondo argomento dion,{ command: 'tally' }, è un filtro, chiamato matcher, quindi l'hook viene eseguito solo per/tally. - Riscrivere: l'hook
ui.renderchiamanextcon una copia dieil cuisuffixcontiene il conteggio, così Claude Code disegna il suo consueto spinner con il tuo testo dopo la parola
Claude Code osserva una directory caricata con --plugin-dir e ricarica a caldo il modulo degli hook quando un file al suo interno cambia. Ogni ricaricamento esegue di nuovo register, quindi calls si azzera a 0 e /tally ricomincia a contare. Per mantenere un valore tra i ricaricamenti, consulta Mantieni lo stato.
Continua a lavorare su un mod
Una volta caricato un mod, puoi chiedere a Claude di modificarlo, verificare il tuo codice rispetto alle definizioni dei tipi per la tua versione, elencare gli eventi e le chiamate che Claude Code vi trova e testarlo.
Modifica un mod con Claude
Per modificare un mod che hai già, avvia la sessione con --plugin-dir che punta alla directory del mod, in modo che ciò che Claude scrive venga caricato nella stessa sessione:
claude --plugin-dir ./first-mod
Poi chiedi la modifica, ad esempio add a /tally-reset command to this mod that sets the tally back to zero. Claude modifica il modulo degli hook, esegue claude plugin validate e corregge ciò che viene segnalato. Una directory che carichi con --plugin-dir è un percorso protetto, quindi nelle modalità default e acceptEdits ti viene chiesto di approvare ogni modifica di Claude al mod. La tabella dei percorsi protetti indica il risultato per le altre modalità di permesso.
I file che Claude salva durante il suo turno vengono ricaricati al termine del turno, quindi puoi provare /tally-reset non appena Claude ha finito.
Ottieni le definizioni dei tipi per la tua versione
Ogni volta che Claude Code carica o ricarica un mod da una directory che passi a --plugin-dir, oppure un mod che Claude ha scritto per te, scrive dei file di dichiarazione TypeScript, con estensione .d.ts, in .claude-plugin/types/ all'interno della directory del mod. Questi descrivono esattamente gli eventi, i metodi dell'API dei mod e gli elementi presenti nella versione di Claude Code che stai eseguendo, così il tuo editor può completare automaticamente e verificare i tipi dei tuoi hook. Per consultare le dichiarazioni online, leggi mods/types/claude-code.d.ts nel repository di Claude Code, la cui prima riga indica la versione che lo ha scritto. La directory contiene questi file:
| Percorso | Cosa dichiara |
|---|---|
claude-code/index.d.ts |
Ogni evento con il suo input e il suo risultato, ogni namespace e metodo dell'API dei mod e gli elementi che ogni superficie può disegnare |
claude-code-tools/index.d.ts |
Gli input e i risultati degli strumenti integrati, in modo che la verifica e.tool === 'Bash' restringa il tipo di e |
claude-code-mcp/index.d.ts |
Gli input degli strumenti MCP che erano connessi l'ultima volta che hai salvato un file nel mod |
index.d.ts in una directory con il nome di un plugin |
Ciò che quel plugin aggiunge all'API dei mod. C'è una directory per ogni plugin che il tuo plugin.json elenca in dependencies. |
tsconfig.json |
Opzioni del compilatore adatte a un modulo degli hook |
Se il tuo mod non ha un proprio tsconfig.json, Claude Code ne aggiunge uno nella radice del mod che estende quello generato, così il tuo editor e tsc -p ./first-mod verificano i tipi del mod senza ulteriore configurazione.
Gli eventi e i metodi possono cambiare da una release all'altra, quindi in caso di discrepanza fidati di questi file piuttosto che di qualsiasi pagina, inclusa questa.
claude-code/index.d.ts è il riferimento più completo per la tua build, con un commento e un esempio per ogni metodo dell'API dei mod. Per cercare qualcosa, cerca nel file il suo nome, ad esempio 'tool.call'.
Verifica cosa legge Claude Code dal tuo mod
Per vedere il tuo mod come lo vede Claude Code, senza eseguire il tuo codice né avviare una sessione, usa claude plugin validate. Il comando controlla il manifest ed esegue sul sorgente del modulo degli hook la stessa analisi statica che Claude Code esegue quando carica un mod. Nella tua shell, eseguilo sulla directory del mod:
claude plugin validate ./first-mod
Per first-mod, l'output include queste righe.
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
La riga hooks: elenca gli eventi a cui il tuo modulo si aggancia, ciascuno con il suo filtro tra parentesi graffe. La riga calls: elenca ogni metodo dell'API dei mod che chiama. Un modulo che legge o imposta variabili d'ambiente ottiene anche le righe env reads: e env writes:, e uno che usa $.state ottiene state reads: e state writes:.
Se un evento che intendevi gestire manca dalla prima riga, nemmeno Claude Code chiamerà quell'hook. La causa più comune è un nome di evento scritto in modo errato, che il comando segnala con un errore come "tool.calls" is not an event.
Segui queste regole affinché l'analisi statica possa trovare ogni hook e ogni chiamata:
- Scrivi ogni chiamata all'API dei mod per intero:
$, il namespace, poi il metodo, come in$.store.get('notes'). Puoi passare$a una funzione dichiarata al livello superiore dello stesso file e, per una tua funzione chiamataloadNotes, la rigacalls:riporterà quindi$.store.get (via loadNotes). Passare$a un metodo, a una funzione definita all'interno dell'hook o a una funzione che importi da un altro dei tuoi file non supera la validazione. Le funzionireadeupdateche$.stateusa sono gli import che possono riceverlo. Non assegnare$o uno dei suoi namespace a una variabile, non destrutturarlo e non indicizzarlo con un nome calcolato.const ui = $.uifallisce con$.ui is used as a value. - Scrivi il nome dell'evento in ogni chiamata
oncome stringa letterale, ad esempio'tool.call'. Una variabile, o un ciclo su un elenco di nomi, fallisce conthe event name passed to on() is not a string literal. - All'interno di
register, non dichiarare una seconda variabile o un secondo parametro chiamatoon. La validazione fallisce con"on" is declared again (shadowed). - Importa solo da file all'interno della directory del plugin, tramite percorso relativo. L'unico import senza percorso consentito è
claude-code, per i tipi e alcune funzioni di supporto. - Usa dichiarazioni
importall'inizio del file, come inimport { name } from './file.js'. Unimport()dinamico fallisce cona dynamic import(); a hooks module imports its own files with an import declaration. - Scrivi ogni file come modulo ES, con
importe nonrequire. Il riferimento elenca le estensioni di file che Claude Code carica.
Testa il mod
Puoi scrivere test automatizzati per un mod ed eseguirli dalla tua shell con claude plugin test, senza sessione, accesso o rete. Un test attiva gli eventi gestiti dai tuoi hook e verifica cosa hanno fatto gli hook.
Questo test attiva due chiamate agli strumenti, esegue /tally e verifica che la risposta le conti entrambe. Salvalo come 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' }))
// Fire 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')
})
Nella tua shell, esegui i test dalla directory first-mod:
claude plugin test
L'output indica ogni test e se è stato superato, con tempi che variano da un'esecuzione all'altra:
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]
Testa un mod spiega come simulare una chiamata al modello o lo store e come testare timer e disegni.
Condividi il tuo mod
Un mod è un plugin, quindi ne gestisci la versione nel manifest e le persone lo installano e lo aggiornano con i comandi /plugin. Il modo in cui lo condividi dipende da chi lo userà:
- Poche persone: invia loro la directory del plugin o un file
.zipche la contiene. Consulta Condividere un plugin senza un marketplace - Il tuo team: elencalo nel tuo marketplace, ad esempio un repository privato con una directory per ogni plugin. Per aggiungere quel marketplace per tutti coloro che lavorano in un repository, registralo nelle impostazioni del repository
- L'intera organizzazione: un amministratore può installare i mod della tua organizzazione tramite le impostazioni gestite
- Chiunque: rendi pubblico il repository del tuo marketplace, oppure invia il plugin alla directory di Anthropic
Prima di farlo, controlla il name del plugin: claude plugin validate rifiuta un nome che sembra uno di quelli di Anthropic, come uno che inizia con claude-. Gli eventi e i metodi possono cambiare tra una release e l'altra, quindi il tuo README è il posto giusto per indicare con quale versione di Claude Code l'hai testato.
Continua a sviluppare sulla directory con --plugin-dir, non su una copia installata. Claude Code memorizza nella cache un plugin installato in base alla versione, quindi le tue modifiche non raggiungono la copia installata finché non incrementi la versione e lo installi di nuovo.
Passaggi successivi
- Disegna nell'interfaccia: apri un riquadro, disegna sopra il prompt e aggiungi pulsanti e campi di testo
- Reagisci agli eventi: intercetta le chiamate agli strumenti, i prompt e i turni
- Usa l'API dei mod: aggiungi comandi e strumenti, chiama un modello ed esegui operazioni a intervalli regolari
- Testa un mod: simula le risposte di Claude Code e testa timer e disegni
- Risolvi i problemi di un mod: i motivi per cui un mod non fa nulla e il log di debug
- Leggi il codice sorgente dei mod integrati: plugin completi, ciascuno con il proprio modulo degli hook e i propri test