Raccoglie gli inciampi della sessione e a richiesta propone come evitare che si ripetano. /correggi lo accende e lo spegne

Un marketplace di mod per Claude Code, pensato per capire e controllare una sessione: barra della cache e dei consumi, registro di ciò che viene scritto fuori dalla repo, conferma delle proposte, raccolta degli inciampi, modalità rec per le registrazioni e inbox con bozze, etichette e task. Ogni mod è un plugin indipendente, installabile singolarmente con un comando.
Ogni mod si accende e si spegne con un comando /..., che funziona in terminale, nell'app Desktop e da iPad. Lo stato si ricorda tra una sessione e l'altra.
⏳ barra-cache (/barra) — tre righe sopra il prompt. La prima è la memoria della chat: una barra con i token del contesto occupato e quanto è costata la sessione a listino (verde fino al 60%, gialla fino all'80%, rossa oltre, con il suggerimento Nuova chat). La seconda mostra l'*uso delle 5 ore* e l'*uso della settimana* dell'abbonamento, solo dove il motore riporta le cifre. La terza dice da quanto è aperta la sessione e quanto resta di cache calda: la prompt cache dura un'ora dall'ultima richiesta della conversazione principale (le richieste dei subagent non la rinnovano), poi la richiesta successiva riscrive tutto il contesto a prezzo pieno. Con la cache scaduta e almeno 20mila token di contesto compare un avviso rosso. Sotto, una riga con branch, file modificati e commit da pubblicare (letta da git, solo dove $.process esiste: nel cloud non compare) e i bottoni: Commit e push (tasto g), Push (tasto p, compare solo quando ci sono commit da pubblicare e nessuna modifica da committare, senza token) e Handoff (tasto n), che fa scrivere al modello un riassunto di ripartenza in ~/.claude/handoffs/, svuota la chat con /clear e riparte da quel file. Dove la banda non viene disegnata (ad esempio l'app su iPad) restano /cache, che mostra minuti rimasti, contesto, costo dell'ultimo turno e quota di cache letta, /push e /handoff (alias /nuova). I comandi sono anche file statici in barra-cache/commands/, perché l'elenco dello slash dell'app non mostra quelli registrati a runtime; l'hook risponde prima del modello, quindi non costano token.
📒 scritture-esterne (/scritture) — a fine turno elenca tutto ciò che il turno ha scritto fuori dalla repo: Notion, Postpickr, Spreaker, Gmail, Calendar, Drive, git push, gh, curl -X POST e gli script con --applica o --elimina. Ogni riga ha servizio, azione, bersaglio, un link apri se la risposta ne contiene uno, e una croce rossa se la chiamata è fallita o negata. Le letture non compaiono. Il registro sparisce all'inizio del turno successivo. Riconosce dal nome i connettori più comuni; per quelli con id opaco serve l'opzione servizi del plugin.
✅ conferma-proposte (/conferma) — quando l'ultima risposta di Claude chiude con una proposta o una domanda di conferma (una domanda, oppure formule come "procedo", "vuoi che", "confermi"), mostra sopra il prompt il passaggio che propone e due bottoni: Sì, procedi (tasto s) e No, fermati (tasto x). Il bottone invia la risposta come se l'avessi scritta tu. Non usa il modello, quindi non consuma token. Funziona anche nel cloud e da iPad.
🛠 correggi (/correggi) — durante la sessione annota gli inciampi: tool che falliscono, azioni negate dal sistema di permessi e le tue correzioni ("non vedo", "hai sbagliato", "riprova"). Con almeno due inciampi compare una riga con il conteggio, le skill usate e il bottone Proponi correzione (tasto l), che chiede a Claude di individuare la causa e proporre la modifica esatta alla skill o al file di istruzioni, senza applicarla. /correggi elenco mostra gli inciampi raccolti. Consuma token solo quando premi il bottone.
🔴 rec (/rec) — la modalità per registrare un video o lavorare in una sessione live con ospiti. Maschera a schermo chiavi e valori dei .env, email, nomi, telefoni, indirizzi, codice fiscale, partita IVA, IBAN, importi in euro e cifre vicino a parole come fatturato, margine, compenso, preventivo. I risultati di posta, chat, task, file, calendario, Notion e strumenti di pagamento si disegnano nascosti. Claude continua a lavorare sui dati reali: cambia solo ciò che si vede (se modifica un file da 29 a 39 euro, il file cambia davvero, lo schermo no). Tiene chiusi i file privati (.env, credenziali, fatture, contratti, preventivi, buste paga) e gli strumenti di pagamento, e ogni prompt porta una nota nascosta che chiede a Claude di usare segnaposto al posto di nomi e cifre. Un ● REC rosso sopra il prompt e nel piè di pagina ricorda che è acceso. /rec alterna, /rec rigoroso maschera anche ogni cifra grande, /rec off spegne, /rec config crea ~/.claude/mods-data/rec/config.json per il tuo nome (nomiVisibili), le persone da nascondere (nomiNascosti), le cartelle private (percorsiPrivati) e gli strumenti extra (strumentiAffari, strumentiChiusi). Limiti: cambia ciò che è disegnato, non ciò che è memorizzato; il riconoscimento per pattern non prende tutto quello che è scritto a parole, quindi riguarda il girato prima di pubblicarlo; i titoli delle chat nella barra laterale dell'app Desktop non si possono mascherare. Adattata da recording-mode di Nate Herk (MIT, vedi rec/NOTICE.md).
📬 posta (posta o /posta) — apre un pannello con la inbox Gmail: l'elenco delle ultime 12 mail per account (una finestra di 5 righe che scorre con j e k), i bottoni e, sotto, il testo intero della mail selezionata (tutti i messaggi del thread, dal più recente, con allegati), letto da Gmail senza modello. I bottoni sono Bozza (b), Label (l), Bozza+Label (m), Task (t), più Aggiorna (r) e Chiudi (x). Le quattro azioni girano dentro la mod, senza subagent: il modello riceve il thread senza alcun tool e sceglie solo i contenuti, mentre le scritture su Gmail e Notion le fa il codice dopo averle validate (la chat resta pulita: l'esito compare nel pannello e in un avviso). Bozza scrive con le regole di ~/.claude/mods-data/posta/sistematore.md e crea una bozza di risposta (la mail non viene mai inviata). Label fa scegliere al modello economico un'etichetta tra quelle esistenti e la applica solo se il nome coincide (non ne crea). Task fa proporre al modello economico titolo, scadenza, urgenza, importanza, impegno e contesto, li riporta ai valori ammessi (scadenza non valida o passata: domani alle 09:00) e crea la pagina nel database Tasks di Notion con il link alla mail. Solo Label e Bozza+Label tolgono la mail dall'inbox (finisce nella cartella dell'etichetta); Bozza e Task la lasciano in inbox. I lavori in corso mostrano uno spinner, quelli riusciti spariscono dopo 8 secondi e quelli falliti si rilanciano con Riprova (y). Nel dettaglio compare l'ultimo messaggio del thread; i precedenti sono righe ripiegate e si aprono con Mostra precedenti (p). Scrivendo posta senza barra il pannello si apre senza avviso dell'app e senza token; posta off lo chiude. Gli account sono in DEFAULT_ACCOUNTS e si sostituiscono con la chiave accounts dello store. In modalità Auto anche le chiamate della mod passano dal classificatore, che sulle scritture non dà verdetto: vanno autorizzate in permissions.allow le letture Gmail (search_threads, get_thread, list_labels) e le quattro scritture (create_draft, label_thread, unlabel_thread di Gmail, notion-create-pages di Notion), con il prefisso mcp__<id connettore>__. Un errore resta visibile solo sulla mail a cui appartiene e sparisce quando la stessa azione riesce.
claude --version.Una mod è codice eseguito con i tuoi permessi: può leggere e scrivere file, avviare processi e vedere prompt e tool call della sessione. Il sorgente di ogni mod è in hooks/: leggilo prima dell'installazione.
claude plugin marketplace add andreabrugnoli/mods
claude plugin install barra-cache@andrea-mods
claude plugin install scritture-esterne@andrea-mods
claude plugin install conferma-proposte@andrea-mods
claude plugin install correggi@andrea-mods
claude plugin install rec@andrea-mods
claude plugin install posta@andrea-mods
claude.Verifica: apri /plugin e controlla che le mod risultino attive. Per provare una mod in una sola sessione senza installarla, clona la repo e avvia claude --plugin-dir ./mods/barra-cache. Per disattivarla, usa il tab Installed di /plugin.
Le sessioni cloud (claude.ai/code, app per iPad) non leggono le tue impostazioni locali: partono da un contenitore pulito e caricano i plugin dichiarati nella repo su cui lavori. Per attivare una mod in una repo, aggiungi a .claude/settings.json di quella repo:
{
"extraKnownMarketplaces": {
"andrea-mods": { "source": { "source": "github", "repo": "andreabrugnoli/mods" } }
},
"enabledPlugins": { "barra-cache@andrea-mods": true, "rec@andrea-mods": true }
}
Lo fa per te scripts/abilita-cloud.sh <percorso-repo> [mod ...], che unisce la voce alle impostazioni esistenti. Se il tuo gitignore (anche globale) esclude .claude/settings.json, aggiungilo con git add -f .claude/settings.json, poi commit e push. La sessione cloud legge il file dal branch.
Su iPad la banda sopra il prompt può non essere disegnata: restano /cache, /push e /nuova, e tutti i comandi on/off (/barra, /scritture, /conferma, /correggi, /rec) funzionano uguale. Lo stato ($.store) e i riassunti in ~/.claude/handoffs/ vivono nel contenitore della sessione e si perdono alla sua chiusura.
Accendere e spegnere (senza argomento alternano; accettano anche on e off):
/barra: la barra sopra il prompt./scritture: il registro delle scritture esterne./conferma: i bottoni Sì e No sulle proposte./correggi: la raccolta degli inciampi (/correggi elenco li mostra)./rec: la modalità registrazione (/rec rigoroso, /rec off, /rec config).posta o /posta: la inbox con i bottoni Bozza, Label e Task (posta aggiorna, posta off).Altri comandi di barra-cache:
/cache: minuti di cache rimasti, contesto occupato, costo dell'ultimo turno e quota di cache letta./push: pubblica i commit del branch corrente./nuova: riassume la sessione in un file e riparte da una chat pulita.Durata della cache in barra-cache: parte da un'ora (TTL_MS) e si corregge da sola. Dopo una pausa di almeno 6 minuti guarda la quota di cache letta: se la cache è stata riscritta, passa a 5 minuti e lo ricorda tra le sessioni; se è stata letta, conferma l'ora.
Soglie dei colori in barra-cache: CONTEXT_WARN, CONTEXT_BAD, COST_WARN, CACHE_OK e CACHE_BAD, in cima allo stesso file.
Nomi dei connettori in scritture-esterne: opzione servizi del plugin.
Per modificare una mod, clona la repo e caricala con claude --plugin-dir: ogni salvataggio ricarica il modulo nella sessione aperta.
mods/
├── .claude-plugin/marketplace.json # elenco delle mod installabili
├── barra-cache/
├── scritture-esterne/
├── conferma-proposte/
├── correggi/
├── rec/
├── posta/
└── scripts/abilita-cloud.sh
Ogni cartella di mod è un plugin completo: .claude-plugin/plugin.json, hooks/hooks.json, hooks/register.js e tests/ (eseguibili con claude plugin test ./<mod>). claude plugin validate ./<mod> elenca gli eventi intercettati e le chiamate al motore.
Licenza: MIT.
hooks/register.js 130 lines1// Quanti inciampi servono prima di proporre la correzione, e quanti al massimo se ne riportano
2const MIN_INCIDENTS = 2
3const MAX_LISTED = 12
4
5// Le frasi con cui di solito segnali che qualcosa non è andato come volevi
6const CORRECTION = /(non (mi piace|va bene|funziona|vedo|capisco|ha|è)|sbagliat|errore|perch[eè] non|di nuovo|ancora una volta|riprova|correggi|dovevi|avevo detto|non voglio)/i
7
8// Gli inciampi della sessione e le skill usate
9let incidents = []
10let skills = new Set()
11
12// Accorcia un testo su una riga
13function clip(text, n) {
14 const one = String(text ?? '').replace(/\s+/g, ' ').trim()
15 return one.length > n ? one.slice(0, n - 1) + '…' : one
16}
17
18// Su cosa agiva la chiamata: il comando, il file o il nome del tool
19function targetOf(e) {
20 return clip(e.command ?? e.file_path ?? e.url ?? e.tool, 80)
21}
22
23// Il testo dell'elenco, uguale per il comando e per il prompt
24export function listIncidents(list) {
25 return list.slice(-MAX_LISTED).map((i) => '- ' + i.kind + ': ' + i.what).join('\n')
26}
27
28// Il prompt che chiede di capire le cause e proporre le modifiche, senza applicarle
29export function lessonsPrompt(list, usedSkills) {
30 return (
31 'Rivedi gli inciampi di questa sessione e proponi come evitare che si ripetano.\n\nInciampi:\n' +
32 listIncidents(list) +
33 (usedSkills.length ? '\n\nSkill usate: ' + usedSkills.join(', ') : '') +
34 '\n\nPer ciascun inciampo individua la causa. Se dipende da una skill o da un file di istruzioni (SKILL.md, CLAUDE.md, regole), proponi la modifica esatta. Non applicare nulla: mostrami le modifiche e aspetta la mia conferma.'
35 )
36}
37
38export function isCorrection(text) {
39 return CORRECTION.test(text ?? '')
40}
41
42// Il nuovo stato dopo un comando: "on" accende, "off" spegne, senza argomento alterna
43export function parseToggle(args, current) {
44 const arg = String(args ?? '').trim().toLowerCase()
45 if (/^(on|acceso|attiva|si|sì)$/.test(arg)) return true
46 if (/^(off|spento|disattiva|no)$/.test(arg)) return false
47 return !current
48}
49
50// Acceso o spento: lo decide /correggi e resta nello store tra una sessione e l'altra
51let enabled = true
52
53// /correggi alterna acceso e spento; /correggi elenco mostra gli inciampi anche dove la banda non si vede
54async function runCorreggi($, e) {
55 if (String(e.args ?? '').trim().toLowerCase() === 'elenco') {
56 if (!incidents.length) return { text: 'correggi · nessun inciampo in questa sessione' }
57 return { text: 'correggi · ' + incidents.length + ' inciampi\n' + listIncidents(incidents) + (skills.size ? '\nskill usate: ' + [...skills].join(', ') : '') }
58 }
59 enabled = parseToggle(e.args, enabled)
60 incidents = []
61 skills = new Set()
62 try {
63 await $.store.set('attiva', enabled)
64 } catch {}
65 $.ui.invalidate('ui.render')
66 return { text: 'correggi · ' + (enabled ? 'acceso' : 'spento') }
67}
68
69export function register(on) {
70 // Ogni chiamata passa; se è stata negata o è fallita, la annotiamo
71 on('tool.call', async ($, e, next) => {
72 if (!enabled) return next(e)
73 if (e.tool === 'Skill' && e.skill) skills.add(e.skill)
74 const ran = await next(e)
75 if (e.tool !== 'AskUserQuestion') {
76 if (ran.deny !== undefined) incidents.push({ kind: 'negata', what: targetOf(e) + ' (' + clip(ran.deny, 80) + ')' })
77 else if (ran.isError === true) incidents.push({ kind: 'errore', what: targetOf(e) + ' (' + clip(ran.text, 100) + ')' })
78 $.ui.invalidate('ui.render')
79 }
80 return ran
81 })
82
83 // Le tue correzioni contano come inciampi
84 on('prompt.submit', ($, e, next) => {
85 if (enabled && isCorrection(e.text)) {
86 incidents.push({ kind: 'correzione', what: clip(e.text, 100) })
87 $.ui.invalidate('ui.render')
88 }
89 return next(e)
90 })
91
92 on('command.run', { command: 'correggi' }, runCorreggi)
93 // Il comando statico del plugin si chiama anche correggi:inciampi
94 on('command.run', { command: 'correggi:inciampi' }, runCorreggi)
95
96 on('session.start', async ($, e, next) => {
97 try {
98 if ((await $.store.get('attiva')) === false) enabled = false
99 } catch {}
100 await $.command.register({ name: 'correggi', description: 'Accende o spegne la raccolta degli inciampi (elenco: li mostra)', argumentHint: '[on|off|elenco]', immediate: true })
101 return next(e)
102 })
103
104 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
105 const theirs = await next(e)
106 if (!enabled || incidents.length < MIN_INCIDENTS || e.props.isWorking) return theirs
107 const { Box, Text, Button } = $.ui.resolve(e)
108 const propose = () => {
109 const text = lessonsPrompt(incidents, [...skills])
110 incidents = []
111 $.ui.invalidate('ui.render')
112 return $.prompt.submit({ text })
113 }
114 return Box({
115 flexDirection: 'column',
116 children: [
117 Box({
118 flexDirection: 'row',
119 children: [
120 Text({ color: 'yellow', children: ['correggi · ' + incidents.length + ' inciampi'] }),
121 Text({ dimColor: true, children: [skills.size ? ' · skill: ' + [...skills].join(', ') + ' ' : ' '] }),
122 Button({ key: 'correggi', label: 'Proponi correzione', hotkey: 'l', onPress: propose }),
123 ],
124 }),
125 theirs,
126 ].filter(Boolean),
127 })
128 })
129}
130