SLOPSHOPPER

conferma-proposte

Quando Claude chiude con una proposta o una domanda di conferma, mostra il passaggio e i bottoni Sì, procedi e No, fermati. /conferma lo accende e lo spegne

newbandcommandprompt
v1.0.1MITupdated 2026-10-07andreabrugnoli/mods/conferma-proposte
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · conferma-proposte
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /conferma ⎿ conferma-proposte: conferma-proposte · spento ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

mods

License: MIT Claude Code Mods

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.


Le sei mod

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.


Prerequisiti

  • Claude Code 2.1.287 o successivo: le mod sono attive di default da questa versione. Verifica con claude --version.
  • Terminale o tab Code dell'app Desktop: le sessioni WSL nell'app Desktop non eseguono le mod.

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.


Installazione

  1. Aggiungi il marketplace a Claude Code:
   claude plugin marketplace add andreabrugnoli/mods
  1. Installa le mod che ti interessano, una per comando:
   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
  1. Avvia una nuova sessione con 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.


Uso in cloud e da iPad

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.


Comandi disponibili

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.

Personalizzazione

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.


Struttura del progetto

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.

Source 1 files
hooks/register.js 100 lines
1// Il testo che chiude una proposta: una domanda, o una formula tipica di richiesta di conferma
2const PROPOSAL = /(\?\s*[)"»]?\s*$)|(procedo|procediamo|vuoi che|ti va( bene)?|confermi|lo faccio|vado avanti|dimmi se)/i
3
4// La proposta in attesa di risposta: { text } finché non arriva un prompt
5let proposal = null
6
7// Accorcia un testo su una riga
8function clip(text, n) {
9  const one = String(text).replace(/\s+/g, ' ').trim()
10  return one.length > n ? one.slice(0, n - 1) + '…' : one
11}
12
13// Dall'ultima risposta di Claude al passaggio che propone, oppure null se non propone niente
14export function findProposal(answer) {
15  const paragraphs = String(answer ?? '').split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean)
16  const last = paragraphs[paragraphs.length - 1]
17  return last && PROPOSAL.test(last) ? last : null
18}
19
20// Il nuovo stato dopo un comando: "on" accende, "off" spegne, senza argomento alterna
21export function parseToggle(args, current) {
22  const arg = String(args ?? '').trim().toLowerCase()
23  if (/^(on|acceso|attiva|si|sì)$/.test(arg)) return true
24  if (/^(off|spento|disattiva|no)$/.test(arg)) return false
25  return !current
26}
27
28// Acceso o spento: lo decide /conferma e resta nello store tra una sessione e l'altra
29let enabled = true
30
31async function runConferma($, e) {
32  enabled = parseToggle(e.args, enabled)
33  proposal = null
34  try {
35    await $.store.set('attiva', enabled)
36  } catch {}
37  $.ui.invalidate('ui.render')
38  return { text: 'conferma-proposte · ' + (enabled ? 'acceso' : 'spento') }
39}
40
41export function register(on) {
42  on('session.start', async ($, e, next) => {
43    try {
44      if ((await $.store.get('attiva')) === false) enabled = false
45    } catch {}
46    await $.command.register({ name: 'conferma', description: 'Accende o spegne i bottoni Sì e No sulle proposte', argumentHint: '[on|off]', immediate: true })
47    return next(e)
48  })
49
50  on('command.run', { command: 'conferma' }, runConferma)
51  // Il comando statico del plugin si chiama anche conferma-proposte:conferma
52  on('command.run', { command: 'conferma-proposte:conferma' }, runConferma)
53
54  // Un nuovo prompt, scritto da te o inviato da un bottone: la proposta non vale più
55  on('prompt.submit', ($, e, next) => {
56    if (proposal) {
57      proposal = null
58      $.ui.invalidate('ui.render')
59    }
60    return next(e)
61  })
62
63  // A fine turno, solo la conversazione principale e solo se Claude ha davvero risposto
64  on('turn.complete', ($, e, next) => {
65    if (enabled && !e.agentId && e.reason === 'answer') {
66      const found = findProposal(e.answer)
67      proposal = found ? { text: found } : null
68      $.ui.invalidate('ui.render')
69    }
70    return next(e)
71  })
72
73  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
74    const theirs = await next(e)
75    if (!enabled || !proposal || e.props.isWorking) return theirs
76    const { Box, Text, Button } = $.ui.resolve(e)
77    // Il bottone toglie la proposta e invia la risposta come se l'avessi scritta tu
78    const reply = (text) => () => {
79      proposal = null
80      $.ui.invalidate('ui.render')
81      return $.prompt.submit({ text, asUser: true })
82    }
83    return Box({
84      flexDirection: 'column',
85      children: [
86        Text({ dimColor: true, children: ['Claude propone · ' + clip(proposal.text, 220)] }),
87        Box({
88          flexDirection: 'row',
89          children: [
90            Button({ key: 'si', label: 'Sì, procedi', hotkey: 's', variant: 'primary', onPress: reply('Sì, procedi.') }),
91            Text({ children: ['  '] }),
92            Button({ key: 'no', label: 'No, fermati', hotkey: 'x', onPress: reply('No, fermati qui e aspetta le mie istruzioni.') }),
93          ],
94        }),
95        theirs,
96      ].filter(Boolean),
97    })
98  })
99}
100