SLOPSHOPPER

decision-popup

Decision popup: Claude asks every decision (approve/refuse, A or B, irreversible steps) as a one-key question, drawn in a framed popup; guard mods' questions…

newrowscommandprompt
v0.1.0no licenseupdated 2026-10-07Szotasz/claude-mods/plugins/decision-popup
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · decision-popup
› 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 › /dontes ⎿ decision-popup: Decision popup off. ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

claude-mods

Saját Claude Code mod-ok (function hook pluginek) egy pluginpiacon.

Telepítés

claude plugin marketplace add Szotasz/claude-mods
claude plugin install usage-band@claude-mods
claude plugin install usage-header@claude-mods
claude plugin install project-pane@claude-mods
claude plugin install focus-mode@claude-mods
claude plugin install tool-hub@claude-mods
claude plugin install snake-pane@claude-mods
claude plugin install supabase-guard@claude-mods
claude plugin install public-repo-guard@claude-mods
claude plugin install decision-popup@claude-mods

Frissítés: claude plugin update usage-band@claude-mods, majd /reload-plugins (vagy új session).

Pluginek

usage-band

A státuszsorba (a prompt alá) írja:

🤖 Opus 5.5  │  Ctx ███░░░░░ 42% 420k/1M  │  5h ███░░░░░ 34% → 16:20  │  Hét ███████░ 93% ⚠ → Cs 10:00
  • modell neve (/model váltáskor és turn végén frissül)
  • kontextusablak telítettsége
  • 5 órás és heti rate-limit, reset időponttal (csak Pro/Max előfizetéssel)
  • ⚠ 90% felett, és egy toast, amikor egy limit átlépi (ablakonként, resetenként egyszer)

Nyelv / Language: /config → usage-band.language, vagy a settings.json-ban:

"pluginConfigs": { "usage-band": { "language": "en" } }
  • auto (alapértelmezett): magyar, ha a rendszer nyelve (LANG) magyar, különben angol
  • hu: magyar (Hét, Cs 10:00)
  • en: English (Week, Thu 10:00)

Követelmény: Claude Code 2.1.289 körüli verzió. A mod-API early access, kiadásról kiadásra változhat.

usage-header

Sáv a prompt fölött: a kontextusablak kategóriánként színezve, alatta az 5 órás és a heti limit.

Kontextus  ██▓▓▓▓▓▓████████████▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░ 42% 84k/200k
           ■ Rendszerprompt 3k  ■ Eszközök 12k  ■ Memória 400  ■ Üzenetek 69k  ▒ Tömörítési tartalék 33k  ░ Szabad 83k
Limitek    5 óra ███████░░░░░░░░░░░░░ 34% → 16:20   Hét ███████████████████░ 93% ⚠ → Cs 10:00
  • A kontextussáv minden kategóriája a /context saját színével látszik (rendszerprompt, eszközök, MCP, memóriafájlok, skillek, ágensek, üzenetek), utána a tömörítési tartalék és a szabad hely halványan; alatta jelmagyarázat tokenszámmal. Az igény szerint betöltött (halasztott) eszközsémák kimaradnak, mert nincsenek az ablakban.
  • A bontás helyi becslés ($.session.usage({ breakdown: 'summary' })), API-hívás nélkül; minden turn végén és limitmozduláskor frissül.
  • Limitek: zöld 70% alatt, sárga 70–90%, piros 90% fölött (⚠), reset időponttal. Csak Pro/Max előfizetéssel van adat.
  • /hasznalat kapcsolja, /hasznalat be|ki beállítja; az állapotot sessionök között megőrzi. Az eszköztárban (/eszkozok) is kapcsolható.
  • A sávot megosztja a fókusz mód listájával és az eszköztárral (azok alatt jelenik meg).

Beállítás (/config): usage-header.language (auto/hu/en).

project-pane

Oldalpanel az aktuális git repóhoz. Magától megnyílik, ha a session git repóban indul és a terminál legalább 144 oszlop széles (miután egyszer /project-tel megnyitottad, 110 is elég); keskenyebb ablaknál egy toast szól, hogy vár. Bármikor: /project.

acme/shop                          ↻ most [ Frissítés ]
GIT
🌿 develop  ↑0 ↓0  · 7 módosított fájl
utolsó commit: 57704fe · 7 hónapja — Fix checkout flow
⚠ a main 13 committal előrébb jár, te a develop branch-en vagy
Előbb commitold a módosításokat, vagy tedd félre őket:
[ Stash + szinkron ]
DEPLOY (Netlify)
✔ ready  main  3 perce
PULL REQUESTEK
✔ #142 Stripe webhook retry  · approved
CI (GitHub Actions)
✘ netlify-deploy-verify  main · 5 napja      [ Kérdezd Claude-ot ]
SUPABASE
abcdefghijklmnopqrst · Acme Shop · eu-west-3
18 migráció · utolsó: 018_add_orders_rls.sql
GitHub  Netlify  Supabase
  • Szinkron gomb (s): csak fast-forward, soha nem merge-öl, rebase-el vagy töröl.
  • a branch lemaradt az upstreamtől → git pull --ff-only
  • másik branchen vagy, és az alapértelmezett (main) előrébb jár → git switch main + git merge --ff-only origin/main
  • commitolatlan módosítás esetén csak „Stash + szinkron” van: git stash push -u, vissza: git stash pop
  • ha elakad, a hibát Claude-nak szóló kérdésként a promptba teszi
  • Frissítés (r): 60 mp-enként és minden turn végén; git fetch 5 percenként.
  • Kérdezd Claude-ot (a): a legutóbbi elbukott CI-futásról kérdést tesz a promptba.
  • Toast, ha a session alatt elbukik egy Netlify deploy vagy CI-futás.

Adatforrások (a CLI-k saját bejelentkezésével, a plugin tokent nem olvas):

SzekcióForrásHa hiányzik
Gitgit—
PR, CIgh (gh auth login)tipp a panelen
Deploynetlify CLI + .netlify/state.jsonnpm i -g netlify-cli && netlify login
Supabasesupabase/.temp/project-ref, supabase/migrations, supabase projects listsupabase login az állapothoz

Beállítás (/config): project-pane.language (auto/hu/en), project-pane.autoOpen (alapból be).

focus-mode

Fókusz mód: a transcriptből eltűnnek az eszközhívások és eredményeik, a köztes szövegek, a saját promptjaid és a parancsok kimenete. Csak két dolog marad: a prompt fölötti sávban a részfeladatok listája állapotsávval és pipával, alatta a turn végső válasza.

✔ Meglévő mod-repo áttekintése
◐ Játék-mod megírása  ██████░░░░ 60%
○ Felvétel az eszközök közé
  • /fokusz kapcsolja, /fokusz be / /fokusz ki beállítja; az állapotot sessionök között megőrzi.
  • A listát a modell tölti a mod által regisztrált mcp__focus-mode__plan eszközzel; a rendszerprompt bekapcsolt állapotban erre utasítja (minden több lépéses kérésnél előbb a részfeladatok, aztán frissítés indításkor, haladáskor és befejezéskor).
  • Kikapcsolva minden a megszokott módon látszik.

Beállítás (/config): focus-mode.language (auto/hu/en).

tool-hub

Eszköztár: egy helyről kapcsolhatók a fenti modok. A prompt alatti lábléc jobb oldalán lévő ⚙ Eszközök gomb (vagy /eszkozok) a prompt fölötti sávban nyitja meg:

⚙ Szabolcs eszközei  kattintás, vagy ctrl+x tab után szám  [ Bezár ]
1. Fókusz mód     ● BE  [ Kikapcsol ] · Csak a részfeladatok listája és a végső válasz látszik
2. Limitsáv       ○ KI  [ Bekapcsol ] · Alsó sáv: 5 órás és heti limit, kontextusablak
3. Használati sáv ● BE  [ Kikapcsol ] · Prompt fölött: kontextus kategóriánként színezve, 5 órás és heti limit
4. Projektpanel   ● BE  [ Kikapcsol ] · GitHub, CI, Netlify és Supabase állapot oldalt
5. Kígyó játék    ● BE  [ Kikapcsol ] · Oldalt nyílik promptküldéskor, amíg Claude dolgozik
  • Modonként BE/KI állapot és kapcsoló gomb, ctrl+x tab után az 1–5 számbillentyűvel is.
  • Ami nincs telepítve, „nincs betöltve” felirattal jelenik meg.
  • Bekapcsolt fókusz módnál a lábléc is kiírja: „Fókusz mód”.
  • Parancsból is ugyanez: /fokusz, /limitsav, /hasznalat, /project, /jatek (mind be|ki argumentummal).
  • A sávot megosztja a fókusz mód listájával (a kapcsolók alatta jelennek meg).

Új mod felvétele az eszköztárba: a mod írja a saját isOn állapotát, adjon /<parancs> be|ki-t, és hookolja a state.set-et { plugin: 'tool-hub', key: 'request' }-re; a hubban egy sor a TOOLS listába, egy ág a stateOf-ba és a kulcs a types/index.d.ts-be.

Beállítás (/config): tool-hub.language (auto/hu/en).

snake-pane

Kígyó játék az oldalpanelben, amíg Claude dolgozik.

⏳ Claude dolgozik…
Pont: 4  Rekord: 17
╭──────────────────────────────────────╮
│                                      │
│          ████████                    │
│                ██      ●             │
╰──────────────────────────────────────╯
p: szünet · r: újra
w: ↑ a: ← s: ↓ d: → p: szünet r: új
  • Promptküldéskor magától megnyílik (ha ebben a sessionben bezártad, már nem; /jatek mindig nyitja). /jatek ki kikapcsolja, az eszköztárban (/eszkozok) is kapcsolható.
  • Irányítás: kattints a táblára, utána nyilak / wasd / hjkl, szóköz vagy p szünet, r új játék. Vagy ctrl+x tab a panelre, és a w a s d p r gombok.
  • A turn végén a futó játék szünetel („Claude végzett”), hogy visszatérj a munkához.
  • A pontszámmal gyorsul; a rekordot sessionök között megőrzi.
  • A játék a rajzoló szálon fut (Client surface modul), nem terheli a sessiont.

supabase-guard

Supabase-őr: megállítja a Supabase-hívást, ha nem az aktuális mappa projektjére menne, és egygombos felugróban rákérdez.

  • A mappa projektjét a supabase/.temp/project-ref, a .env* fájlok https://<ref>.supabase.co címei és a CLAUDE.md (ha egyetlen projekt-ID-t említ) adják, plusz amit a felugróban „Ez a mappa projektje” gombbal megerősítettél.
  • Más projektre menő írás (MCP execute_sql, apply_migration, deploy_edge_function, branch-műveletek, illetve supabase db push --project-ref …): kérdez — Megtiltom / Engedélyezem / Ez a mappa projektje.
  • Romboló művelet a saját projekten is kérdez: DROP, TRUNCATE, DELETE FROM, WHERE nélküli UPDATE, REVOKE, RLS kikapcsolása, reset_branch, delete_branch, pause_project, supabase db reset --linked.
  • Ha a mappához nem talál projektet, írás előtt kérdez (van „Engedem a session végéig” gomb is).
  • Más projekt olvasása fut, de a modell kap egy figyelmeztetést, hogy ne a rossz projektről vonjon le következtetést.
  • Hiba esetén zárva marad: ha az ellenőrzés elhasal, a Supabase-hívás nem fut le. /supaor be|ki, vagy az eszköztárban.

public-repo-guard

Push-őr: nyilvános GitHub-repóba push előtt (és gh repo edit --visibility public / gh repo create --public előtt) átnézi, mi menne ki.

  • A távoli ágon még nem lévő commitokat nézi (üzenet + hozzáadott sorok, a history is); ha nincs mihez mérni, az egész fát. Kezeli a git push origin forrás:cél alakot is.
  • Keres: Supabase projekt-ID (URL, project_id, táblázatsor), JWT / Supabase-kulcs, Stripe/Anthropic/OpenAI/GitHub/AWS/Google/Slack/Resend/Telegram/ElevenLabs kulcsok, privát kulcs, e-mail-cím (a saját git e-mail és a noreply/példa címek kivételével), helyi /Users/<te>/ útvonal, privát repóid nevei (gh repo list --visibility private), .env fájlok, és amit /pushor figyel <szöveg>-gel felvettél.
  • Találatnál felugró, maszkolt listával — Megtiltom / Pushold így is / Pushold, jegyezd meg (az utóbbi kivételként megjegyzi a találatokat). Tiltáskor a modell megkapja a teljes listát a javításhoz.
  • Privát repóba menő pushnál nem csinál semmit. Kell hozzá a gh CLI (bejelentkezve). /pushor be|ki.

decision-popup

Döntési felugró: a döntések egygombos kérdésként jönnek.

  • A rendszerpromptban arra kéri Claude-ot, hogy minden döntést (engedélyezés, A vagy B, visszafordíthatatlan vagy kifelé ható lépés) az AskUserQuestion dialógussal kérdezzen meg, 2–4 rövid opcióval, a javasoltat vagy a biztonságosabbat elöl — egy számgombbal válaszolsz.
  • A dialógus keretet és címet kap („Döntés kell”); az őr-modok (supabase-guard, public-repo-guard) kérdése piros keretben, „Az őr megállított egy műveletet” címmel.
  • /dontes be|ki, vagy az eszköztárban.

Ötletek

project-pane bővítése:

  • Vercel deploy-ok (vercel ls)
  • remote Supabase migrációk összevetése a lokálissal
  • több repó egy panelen

Státuszsor bővítése (usage-band):

  • figyelmeztetés kontextusablakra (pl. 85%), opcionálisan automatikus /compact
  • session költsége ($.session.usage().cost)
  • fast mód jelzése, git branch, aktuális projekt
  • beállítható küszöb és megjelenő elemek (userConfig)

Új mod-ok:

  • env-guard: .env, kulcsfájlok, supabase/.temp szerkesztésének tiltása (tool.call → deny)
  • turn-timer: hosszú turn végén hang/toast, hogy vissza lehet nézni (turn.complete, $.audio.play)
  • quote: /quote — a kijelölt szöveget idézetként a promptba teszi ($.ui.selection)
  • hu-prompt: a rendszerpromptba magyar válasz- és stílusszabály (prompt.compose)

Fejlesztés

Egy plugin mappája közvetlenül is betölthető:

claude --plugin-dir ./plugins/usage-band

Ellenőrzés:

claude plugin validate plugins/usage-band
claude plugin test plugins/usage-band
Source 2 files
hooks/register.tsx 119 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4const isOn = atom({ plugin: 'decision-popup', key: 'isOn' } as const, true)
5
6type Lang = 'hu' | 'en'
7
8// Az őr-modok kérdéseinek fejléce: ezek piros keretet kapnak.
9const GUARD_HEADERS = new Set(['Supabase-őr', 'Supabase', 'Push-őr', 'Push guard'])
10
11const STRINGS = {
12  hu: {
13    title: 'Döntés kell',
14    guard: 'Az őr megállított egy műveletet',
15    hint: (n: number) => (n > 1 ? `Válassz egy gombnyomással: 1–${n} · Esc = elvetés` : 'Esc = elvetés'),
16    command: 'Döntési felugró be/ki: a döntések egygombos kérdésként jönnek',
17    on: 'Döntési felugró bekapcsolva: a döntéseket egygombos kérdésként kapod.',
18    off: 'Döntési felugró kikapcsolva.',
19    usage: 'Használat: /dontes [be|ki]',
20  },
21  en: {
22    title: 'Decision needed',
23    guard: 'A guard stopped an action',
24    hint: (n: number) => (n > 1 ? `Answer with one key: 1–${n} · Esc dismisses` : 'Esc dismisses'),
25    command: 'Decision popup on/off: decisions come as one-key questions',
26    on: 'Decision popup on: decisions come to you as one-key questions.',
27    off: 'Decision popup off.',
28    usage: 'Usage: /dontes [on|off]',
29  },
30}
31
32let lang: Lang = 'en'
33const t = () => STRINGS[lang]
34
35const PROMPT = `Decisions go to the person as a one-key popup.
36
37- Whenever you need the person to choose (approve or refuse an action, pick between approaches, confirm something irreversible or outward-facing, answer yes/no), ask with the AskUserQuestion tool instead of asking in prose: one question, 2-4 short option labels (1-4 words), the recommended or safer option first, marked "(Recommended)" when you recommend one. They answer with a single key press.
38- Put the facts the choice depends on in the question itself (what, where, why it matters), since that may be all they read.
39- Do not invent a choice where a sensible default exists; decide those yourself and say what you chose.
40- If a tool call is refused with a message that the person did not allow it, do not retry it another way.`
41
42type Question = { header?: unknown; options?: unknown }
43
44export function firstQuestion(questions: readonly unknown[]): { header: string; options: number } {
45  const q = (questions[0] ?? {}) as Question
46  return {
47    header: typeof q.header === 'string' ? q.header : '',
48    options: Array.isArray(q.options) ? q.options.length : 0,
49  }
50}
51
52async function pickLanguage($: EngineInterface, setting: unknown): Promise<Lang> {
53  if (setting === 'hu' || setting === 'en') return setting
54  const locale = (await $.env.get('LC_ALL')) || (await $.env.get('LANG')) || ''
55  return locale.toLowerCase().startsWith('hu') ? 'hu' : 'en'
56}
57
58async function setOn($: EngineInterface, value: boolean): Promise<void> {
59  await update($, isOn, () => value)
60  await $.store.set('isOn', value)
61}
62
63export const register: Register = (on, options) => {
64  if (options.language === 'hu' || options.language === 'en') lang = options.language
65
66  on('session.start', async ($, e, next) => {
67    lang = await pickLanguage($, options.language)
68    const saved = await $.store.get('isOn')
69    await update($, isOn, () => saved !== false)
70    await $.command.register({
71      name: 'dontes',
72      description: t().command,
73      argumentHint: lang === 'hu' ? '[be|ki]' : '[on|off]',
74      immediate: true,
75    })
76    return next(e)
77  })
78
79  on('command.run', { command: 'dontes' }, async ($, e) => {
80    const arg = e.args.trim().toLowerCase()
81    const wanted = ['be', 'on'].includes(arg) ? true : ['ki', 'off'].includes(arg) ? false : arg === '' ? !(await read($, isOn)) : null
82    if (wanted === null) return { text: t().usage }
83    await setOn($, wanted)
84    return { text: wanted ? t().on : t().off }
85  })
86
87  on('state.set', { plugin: 'tool-hub', key: 'request' }, async ($, e, next) => {
88    const done = await next(e)
89    if (e.value?.tool === 'decision') await setOn($, e.value.on)
90    return done
91  })
92
93  on('prompt.compose', async ($, e, next) => {
94    const composed = await next(e)
95    if (!(await read($, isOn))) return composed
96    return { sections: [...composed.sections, { id: 'decision-popup:instructions', text: PROMPT, scope: 'session' as const }] }
97  })
98
99  // A kérdés-dialógus keretet, címet és gombsúgót kap (csak fölé rajzolhatunk); az őrök kérdése pirosat.
100  on('ui.render', { component: 'AskUserQuestion' }, async ($, e, next) => {
101    const dialog = await next(e)
102    if (!(await read($, isOn))) return dialog
103    const { Box, Text } = $.ui.resolve(e)
104    const q = firstQuestion(e.props.questions)
105    const isGuard = GUARD_HEADERS.has(q.header)
106    const color = isGuard ? 'error' : 'permission'
107    const title = isGuard ? `🛡  ${t().guard} · ${q.header}` : `◆ ${t().title}${q.header === '' ? '' : ` · ${q.header}`}`
108    return (
109      <Box flexDirection="column" borderStyle="round" borderColor={color} paddingX={1}>
110        <Box>
111          <Text bold color={color}>{title}</Text>
112          <Text dimColor>{`  ${t().hint(q.options)}`}</Text>
113        </Box>
114        {dialog}
115      </Box>
116    )
117  })
118}
119
types/index.d.ts 10 lines
1// Az eszköztár (tool-hub) kérése: a kapcsoló gomb ezt írja, a mod a state.set hookjában veszi át.
2export type HubRequest = { tool: string; on: boolean; n: number }
3
4declare module 'claude-code' {
5  interface PluginState {
6    'tool-hub': { request: HubRequest | null }
7    'decision-popup': { isOn: boolean }
8  }
9}
10