SLOPSHOPPER

focus-mode

Focus mode: hides tool calls, intermediate text, your prompts and command output; shows only a subtask list with progress bars and the final answer. /fokusz…

newbandrowsguardcommandprompt
v0.3.0no licenseupdated 2026-10-07Szotasz/claude-mods/plugins/focus-mode
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · focus-mode
⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ✻ Worked for 42s · done 4:20 PM › /fokusz ◆ Focus mode on · /fokusz off to turn it off ⟨Claude Code's own drawing⟩ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
◆ Focus mode on · /fokusz off to turn it off ⟨Claude Code's own drawing⟩
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 255 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Task, TaskStatus } from '../types'
5
6const isOn = atom({ plugin: 'focus-mode', key: 'isOn' } as const, false)
7const tasks = atom({ plugin: 'focus-mode', key: 'tasks' } as const, [])
8// A turnök végső válaszai: csak ezek az asszisztens-sorok látszanak fókusz módban.
9const answers = atom({ plugin: 'focus-mode', key: 'answers' } as const, [])
10
11const PLAN_TOOL = 'mcp__focus-mode__plan'
12const KEEP_ANSWERS = 100
13const WIDTH = 12
14
15type Strings = {
16  title: string
17  done: string
18  waiting: string
19  idle: string
20  on: string
21  off: string
22  usage: string
23}
24
25const STRINGS: Record<'hu' | 'en', Strings> = {
26  hu: {
27    title: 'Fókusz mód',
28    done: 'kész',
29    waiting: 'a részfeladatok hamarosan megjelennek…',
30    idle: 'bekapcsolva · /fokusz ki a kikapcsoláshoz',
31    on: 'Fókusz mód bekapcsolva: csak a részfeladatok és a végső válasz látszik.',
32    off: 'Fókusz mód kikapcsolva: újra látszik minden lépés.',
33    usage: 'Használat: /fokusz [be|ki]',
34  },
35  en: {
36    title: 'Focus mode',
37    done: 'done',
38    waiting: 'subtasks will show up shortly…',
39    idle: 'on · /fokusz off to turn it off',
40    on: 'Focus mode on: only the subtasks and the final answer are shown.',
41    off: 'Focus mode off: every step is shown again.',
42    usage: 'Usage: /fokusz [on|off]',
43  },
44}
45
46let t: Strings = STRINGS.en
47
48const PROMPT = `Focus mode is on. The person is not watching the transcript: your tool calls, their results and any text you write before the end of the turn are hidden from them. They only see a subtask list drawn from the ${PLAN_TOOL} tool, and your final message of the turn.
49
50- For any request that takes more than one step, first call ${PLAN_TOOL} with the subtasks of the job (3-8 short items, written in the person's language), before any other tool.
51- Call it again whenever a subtask starts, makes notable progress (estimate \`progress\` 0-100) or finishes. Always send the whole list.
52- Before you end the turn, every finished subtask must be marked done.
53- Make the final message a short summary of what was done, plus anything the person must decide or do. Questions for the person go in that final message or through AskUserQuestion; nothing else you write mid-turn reaches them.`
54
55const PLAN_SPEC = {
56  name: 'plan',
57  description:
58    'Sets the subtask list the person sees in focus mode, replacing the previous one. Send every subtask each time, with its status and an estimated progress percentage.',
59  inputSchema: {
60    type: 'object',
61    properties: {
62      tasks: {
63        type: 'array',
64        items: {
65          type: 'object',
66          properties: {
67            title: { type: 'string', description: 'Short subtask name, in the person\'s language.' },
68            status: { type: 'string', enum: ['pending', 'in_progress', 'done'] },
69            progress: { type: 'number', minimum: 0, maximum: 100, description: 'Estimated percent done.' },
70          },
71          required: ['title', 'status'],
72        },
73      },
74    },
75    required: ['tasks'],
76  },
77}
78
79const STATUSES: readonly TaskStatus[] = ['pending', 'in_progress', 'done']
80
81// A modell bemenetét tűrően olvassuk: hiányzó progress → státuszból becsült érték.
82export function parseTasks(input: unknown): Task[] {
83  if (!Array.isArray(input)) return []
84  return input.flatMap((raw): Task[] => {
85    if (typeof raw !== 'object' || raw === null) return []
86    const { title, status, progress } = raw as Record<string, unknown>
87    if (typeof title !== 'string' || title.trim() === '') return []
88    const s: TaskStatus = STATUSES.includes(status as TaskStatus) ? (status as TaskStatus) : 'pending'
89    const fallback = s === 'done' ? 100 : 0
90    const p = typeof progress === 'number' && Number.isFinite(progress) ? progress : fallback
91    return [{ title: title.trim(), status: s, progress: s === 'done' ? 100 : Math.max(0, Math.min(99, Math.round(p))) }]
92  })
93}
94
95export function bar(percent: number, width = WIDTH): string {
96  const filled = Math.max(0, Math.min(width, Math.round((percent / 100) * width)))
97  return '█'.repeat(filled) + '░'.repeat(width - filled)
98}
99
100export function overall(list: readonly Task[]): number {
101  if (list.length === 0) return 0
102  return Math.round(list.reduce((sum, task) => sum + task.progress, 0) / list.length)
103}
104
105export function isAnswer(text: string, list: readonly string[]): boolean {
106  const shown = text.trim()
107  if (shown === '') return false
108  return list.some(a => a.trim() === shown || a.includes(shown))
109}
110
111async function pickLanguage($: EngineInterface, setting: unknown): Promise<Strings> {
112  if (setting === 'hu' || setting === 'en') return STRINGS[setting]
113  const locale = (await $.env.get('LC_ALL')) || (await $.env.get('LANG')) || ''
114  return locale.toLowerCase().startsWith('hu') ? STRINGS.hu : STRINGS.en
115}
116
117async function setOn($: EngineInterface, value: boolean): Promise<void> {
118  await update($, isOn, () => value)
119  await $.store.set('isOn', value)
120}
121
122export const register: Register = (on, options) => {
123  if (options.language === 'hu' || options.language === 'en') t = STRINGS[options.language]
124
125  on('session.start', async ($, e, next) => {
126    t = await pickLanguage($, options.language)
127    const saved = (await $.store.get('isOn')) === true
128    await update($, isOn, () => saved)
129    await $.tool.register(PLAN_SPEC)
130    await $.command.register({
131      name: 'fokusz',
132      description: t === STRINGS.hu ? 'Fókusz mód be/ki: csak részfeladatok és végső válasz' : 'Focus mode on/off: subtasks and final answer only',
133      argumentHint: t === STRINGS.hu ? '[be|ki]' : '[on|off]',
134      immediate: true,
135    })
136    return next(e)
137  })
138
139  on('command.run', { command: 'fokusz' }, async ($, e) => {
140    const arg = e.args.trim().toLowerCase()
141    const wanted = ['be', 'on'].includes(arg) ? true : ['ki', 'off'].includes(arg) ? false : arg === '' ? !(await read($, isOn)) : null
142    if (wanted === null) return { text: t.usage }
143    await setOn($, wanted)
144    return { text: wanted ? t.on : t.off }
145  })
146
147  // Az eszköztár gombja parancs nélkül kapcsol (így nem marad parancssor a transcriptben).
148  on('state.set', { plugin: 'tool-hub', key: 'request' }, async ($, e, next) => {
149    const done = await next(e)
150    if (e.value?.tool === 'focus') await setOn($, e.value.on)
151    return done
152  })
153
154  on('tool.call', { tool: PLAN_TOOL }, async ($, e) => {
155    const list = parseTasks(e.tasks)
156    await update($, tasks, () => list)
157    const done = list.filter(task => task.status === 'done').length
158    return { result: `Plan updated: ${done}/${list.length} done.` }
159  })
160
161  on('prompt.compose', async ($, e, next) => {
162    const composed = await next(e)
163    if (!(await read($, isOn))) return composed
164    return { sections: [...composed.sections, { id: 'focus-mode:instructions', text: PROMPT, scope: 'session' as const }] }
165  })
166
167  on('turn.complete', async ($, e, next) => {
168    if (e.agentId === undefined && e.answer.trim() !== '') {
169      const answer = e.answer
170      await update($, answers, list => [...list, answer].slice(-KEEP_ANSWERS))
171    }
172    return next(e)
173  })
174
175  // Az elrejtett sorok helyére egy üres Box kerül: eszközhívások, a felhasználó promptjai és a parancsok kimenete.
176  on('ui.render', { component: ['ToolUse', 'ToolResult', 'ToolGroup', 'UserMessage', 'CommandOutput'] }, async ($, e, next) => {
177    if (!(await read($, isOn))) return next(e)
178    const { Box } = $.ui.resolve(e)
179    return <Box />
180  })
181
182  on('ui.render', { component: 'ToolProgress' }, async ($, e, next) => {
183    if (!(await read($, isOn))) return next(e)
184    return next({ ...e, props: { ...e.props, hint: '' } })
185  })
186
187  on('ui.render', { component: 'AssistantMessage' }, async ($, e, next) => {
188    if (!(await read($, isOn))) return next(e)
189    if (e.props.isSummary !== true && isAnswer(e.props.text, await read($, answers))) return next(e)
190    const { Box } = $.ui.resolve(e)
191    return <Box />
192  })
193
194  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
195    if (e.props.hasSurvey || !(await read($, isOn))) return next(e)
196    const list = await read($, tasks)
197    // Más mod (pl. az eszköztár) is rajzolhat a sávba: az övét a lista alá tesszük.
198    const below = await next(e)
199    const { Box, Text } = $.ui.resolve(e)
200    const columns = e.props.bodyColumns
201
202    if (list.length === 0) {
203      return (
204        <Box flexDirection="column">
205          <Box>
206            <Text color="cyan" bold>{`◆ ${t.title}`}</Text>
207            <Text dimColor>{` ${e.props.isWorking ? t.waiting : t.idle}`}</Text>
208          </Box>
209          {below}
210        </Box>
211      )
212    }
213
214    const done = list.filter(task => task.status === 'done').length
215    const total = overall(list)
216    const titleRoom = Math.max(10, columns - WIDTH - 12)
217
218    return (
219      <Box flexDirection="column">
220        <Box>
221          <Text color="cyan" bold>{`◆ ${t.title}`}</Text>
222          <Text dimColor>{`  ${done}/${list.length} ${t.done}  `}</Text>
223          <Text color={total === 100 ? 'green' : 'cyan'}>{`${bar(total)} ${total}%`}</Text>
224        </Box>
225        {list.map((task, i) => {
226          const name = task.title.length > titleRoom ? `${task.title.slice(0, titleRoom - 1)}…` : task.title
227          if (task.status === 'done') {
228            return (
229              <Box key={`t${i}`}>
230                <Text color="green">  ✓ </Text>
231                <Text dimColor strikethrough>{name}</Text>
232              </Box>
233            )
234          }
235          if (task.status === 'in_progress') {
236            return (
237              <Box key={`t${i}`}>
238                <Text color="yellow">  ▸ </Text>
239                <Text bold>{`${name} `}</Text>
240                <Text color="yellow">{`${bar(task.progress, 8)} ${task.progress}%`}</Text>
241              </Box>
242            )
243          }
244          return (
245            <Box key={`t${i}`}>
246              <Text dimColor>{`  ○ ${name}`}</Text>
247            </Box>
248          )
249        })}
250        {below}
251      </Box>
252    )
253  })
254}
255
types/index.d.ts 13 lines
1export type TaskStatus = 'pending' | 'in_progress' | 'done'
2export type Task = { title: string; status: TaskStatus; progress: number }
3
4// Az eszköztár (tool-hub) kérése: a kapcsoló gomb ezt írja, a mod a state.set hookjában veszi át.
5export type HubRequest = { tool: string; on: boolean; n: number }
6
7declare module 'claude-code' {
8  interface PluginState {
9    'tool-hub': { request: HubRequest | null }
10    'focus-mode': { isOn: boolean; tasks: Task[]; answers: string[] }
11  }
12}
13