Band above the prompt: context window broken down by category in colours, plus the 5-hour and weekly limits. Hungarian or English.

Saját Claude Code mod-ok (function hook pluginek) egy pluginpiacon.
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).
A státuszsorba (a prompt alá) írja:
🤖 Opus 5.5 │ Ctx ███░░░░░ 42% 420k/1M │ 5h ███░░░░░ 34% → 16:20 │ Hét ███████░ 93% ⚠ → Cs 10:00
/model váltáskor és turn végén frissül)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 angolhu: 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.
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
/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.$.session.usage({ breakdown: 'summary' })), API-hívás nélkül; minden turn végén és limitmozduláskor frissül./hasznalat kapcsolja, /hasznalat be|ki beállítja; az állapotot sessionök között megőrzi. Az eszköztárban (/eszkozok) is kapcsolható.Beállítás (/config): usage-header.language (auto/hu/en).
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
s): csak fast-forward, soha nem merge-öl, rebase-el vagy töröl.git pull --ff-onlygit switch main + git merge --ff-only origin/maingit stash push -u, vissza: git stash popr): 60 mp-enként és minden turn végén; git fetch 5 percenként.a): a legutóbbi elbukott CI-futásról kérdést tesz a promptba.Adatforrások (a CLI-k saját bejelentkezésével, a plugin tokent nem olvas):
| Szekció | Forrás | Ha hiányzik |
|---|---|---|
| Git | git | — |
| PR, CI | gh (gh auth login) | tipp a panelen |
| Deploy | netlify CLI + .netlify/state.json | npm i -g netlify-cli && netlify login |
| Supabase | supabase/.temp/project-ref, supabase/migrations, supabase projects list | supabase login az állapothoz |
Beállítás (/config): project-pane.language (auto/hu/en), project-pane.autoOpen (alapból be).
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.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).Beállítás (/config): focus-mode.language (auto/hu/en).
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
ctrl+x tab után az 1–5 számbillentyűvel is./fokusz, /limitsav, /hasznalat, /project, /jatek (mind be|ki argumentummal).Ú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).
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
/jatek mindig nyitja). /jatek ki kikapcsolja, az eszköztárban (/eszkozok) is kapcsolható.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.Client surface modul), nem terheli a sessiont.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.
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.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.DROP, TRUNCATE, DELETE FROM, WHERE nélküli UPDATE, REVOKE, RLS kikapcsolása, reset_branch, delete_branch, pause_project, supabase db reset --linked./supaor be|ki, vagy az eszköztárban.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.
git push origin forrás:cél alakot is.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.gh CLI (bejelentkezve). /pushor be|ki.Döntési felugró: a döntések egygombos kérdésként jönnek.
/dontes be|ki, vagy az eszköztárban.project-pane bővítése:
vercel ls)Státuszsor bővítése (usage-band):
/compact$.session.usage().cost)userConfig)Új mod-ok:
.env, kulcsfájlok, supabase/.temp szerkesztésének tiltása (tool.call → deny)turn.complete, $.audio.play)/quote — a kijelölt szöveget idézetként a promptba teszi ($.ui.selection)prompt.compose)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-bandhooks/register.tsx 288 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register, SessionContextBreakdown } from 'claude-code'
3
4import type { Breakdown, Limit, Segment, Task } from '../types'
5
6const breakdown = atom({ plugin: 'usage-header', key: 'breakdown' } as const, null)
7const limits = atom({ plugin: 'usage-header', key: 'limits' } as const, [])
8// Kikapcsolva a sáv üres; az eszköztár (tool-hub) is ezt olvassa.
9const isOn = atom({ plugin: 'usage-header', key: 'isOn' } as const, true)
10
11// A hivatkozásnak szó szerint kell a forrásban állnia (így listázható, mit olvas a mod).
12const FOCUS_ON = { plugin: 'focus-mode', key: 'isOn' } as const
13const FOCUS_TASKS = { plugin: 'focus-mode', key: 'tasks' } as const
14
15const WARN_AT = 90
16const CAUTION_AT = 70
17const LABEL = 11
18
19type Lang = 'hu' | 'en'
20
21const STRINGS = {
22 hu: {
23 context: 'Kontextus',
24 limits: 'Limitek',
25 windows: { five_hour: '5 óra', seven_day: 'Hét' } as Record<string, string>,
26 days: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
27 noData: 'még nincs adat (az első válasz után jelenik meg)',
28 command: 'Használati sáv a prompt fölött be/ki',
29 on: 'Használati sáv bekapcsolva.',
30 off: 'Használati sáv kikapcsolva.',
31 usage: 'Használat: /hasznalat [be|ki]',
32 hint: '[be|ki]',
33 names: {
34 'System prompt': 'Rendszerprompt',
35 'System tools': 'Eszközök',
36 'MCP tools': 'MCP eszközök',
37 'Custom agents': 'Ágensek',
38 'Memory files': 'Memória',
39 Skills: 'Skillek',
40 'Slash commands': 'Parancsok',
41 Messages: 'Üzenetek',
42 'Free space': 'Szabad',
43 'Autocompact buffer': 'Tömörítési tartalék',
44 } as Record<string, string>,
45 },
46 en: {
47 context: 'Context',
48 limits: 'Limits',
49 windows: { five_hour: '5h', seven_day: 'Week' } as Record<string, string>,
50 days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
51 noData: 'no data yet (shows after the first response)',
52 command: 'Usage band above the prompt on/off',
53 on: 'Usage band on.',
54 off: 'Usage band off.',
55 usage: 'Usage: /hasznalat [on|off]',
56 hint: '[on|off]',
57 names: {} as Record<string, string>,
58 },
59}
60
61// A modul minden beállításváltáskor újratöltődik, így elég egyszer, induláskor eldönteni.
62let lang: Lang = 'en'
63const t = () => STRINGS[lang]
64
65async function pickLanguage($: EngineInterface, setting: unknown): Promise<Lang> {
66 if (setting === 'hu' || setting === 'en') return setting
67 const locale = (await $.env.get('LC_ALL')) || (await $.env.get('LANG')) || ''
68 return locale.toLowerCase().startsWith('hu') ? 'hu' : 'en'
69}
70
71export function tokens(n: number): string {
72 if (n >= 1_000_000) return `${Number((n / 1_000_000).toFixed(1))}M`
73 if (n >= 10_000) return `${Math.round(n / 1000)}k`
74 return n >= 1000 ? `${Number((n / 1000).toFixed(1))}k` : String(n)
75}
76
77// A halasztott (igény szerint betöltött) eszközsémák nincsenek az ablakban: kimaradnak.
78export function toBreakdown(b: SessionContextBreakdown): Breakdown {
79 const segments: Segment[] = []
80 for (const c of b.categories) {
81 if (c.kind === 'deferred' || c.tokens <= 0) continue
82 segments.push({ name: c.name, tokens: c.tokens, color: c.color, kind: c.kind })
83 }
84 return { segments, total: b.totalTokens, max: b.rawMaxTokens, percent: b.percentage }
85}
86
87// Cellák kategóriánként, legnagyobb maradék szerint kerekítve; ami foglal, legalább egy cellát kap
88// (a szabad helyből), hogy minden szín látsszon.
89export function cells(segments: Segment[], max: number, width: number): number[] {
90 const sum = Math.max(max, segments.reduce((a, s) => a + s.tokens, 0), 1)
91 const exact = segments.map(s => (s.tokens / sum) * width)
92 const out = exact.map(Math.floor)
93 let left = width - out.reduce((a, n) => a + n, 0)
94 const order = exact.map((x, i) => [x - Math.floor(x), i] as const).sort((a, b) => b[0] - a[0])
95 for (const [, i] of order) {
96 if (left <= 0) break
97 out[i]! += 1
98 left -= 1
99 }
100 const free = segments.findIndex(s => s.kind === 'free')
101 for (const [i, s] of segments.entries()) {
102 if (s.kind !== 'used' || out[i]! > 0) continue
103 const donor = free >= 0 && out[free]! > 1 ? free : out.indexOf(Math.max(...out))
104 if (out[donor]! <= 1) break
105 out[donor]! -= 1
106 out[i] = 1
107 }
108 return out
109}
110
111const glyph = (kind: Segment['kind']) => (kind === 'used' ? '█' : kind === 'buffer' ? '▒' : '░')
112
113const levelColor = (percent: number) => (percent >= WARN_AT ? 'red' : percent >= CAUTION_AT ? 'yellow' : 'green')
114
115function resetText(kind: string, resetsAt?: string): string {
116 if (resetsAt === undefined) return ''
117 const at = new Date(resetsAt)
118 const hhmm = `${String(at.getHours()).padStart(2, '0')}:${String(at.getMinutes()).padStart(2, '0')}`
119 return kind === 'five_hour' ? ` → ${hhmm}` : ` → ${t().days[at.getDay()]} ${hhmm}`
120}
121
122// A fókusz mód feliratai és elrendezése (focus-mode/hooks/register.tsx): ebből jön a lista legszélesebb sora.
123const FOCUS_TEXT = {
124 hu: { title: 'Fókusz mód', done: 'kész', waiting: 'a részfeladatok hamarosan megjelennek…', idle: 'bekapcsolva · /fokusz ki a kikapcsoláshoz' },
125 en: { title: 'Focus mode', done: 'done', waiting: 'subtasks will show up shortly…', idle: 'on · /fokusz off to turn it off' },
126}
127
128// A rajzjelek (vonal, sáv, ikon) egy cellát foglalnak. Az asztali appban a betűk arányos betűtípussal,
129// keskenyebben jelennek meg, így ott egy betű csak ~0,55 vonaldarabnyi helyet foglal.
130const GRAPHIC = /[─█░▒▓◆▸○✓■]/
131export function measure(text: string, narrow: boolean): number {
132 if (!narrow) return text.length
133 let units = 0
134 for (const ch of text) units += GRAPHIC.test(ch) ? 1 : 0.55
135 return Math.ceil(units)
136}
137
138export function focusWidth(list: readonly Task[], columns: number, isWorking: boolean, l: Lang, narrow = false): number {
139 const f = FOCUS_TEXT[l]
140 if (list.length === 0) return measure(`◆ ${f.title} ${isWorking ? f.waiting : f.idle}`, narrow)
141 const done = list.filter(task => task.status === 'done').length
142 const total = Math.round(list.reduce((sum, task) => sum + task.progress, 0) / list.length)
143 const header = `◆ ${f.title} ${done}/${list.length} ${f.done} ${'█'.repeat(12)} ${total}%`
144 const titleRoom = Math.max(10, columns - 24)
145 const rows = list.map(task => {
146 const name = task.title.slice(0, titleRoom)
147 return task.status === 'in_progress' ? ` ▸ ${name} ${'█'.repeat(8)} ${task.progress}%` : ` ✓ ${name}`
148 })
149 return Math.max(...[header, ...rows].map(row => measure(row, narrow)))
150}
151
152async function refresh($: EngineInterface): Promise<void> {
153 const usage = await $.session.usage({ breakdown: 'summary' })
154 const b = usage.context.breakdown
155 await update($, breakdown, prev => (b === undefined ? prev : toBreakdown(b)))
156 await update($, limits, () => usage.rateLimits)
157}
158
159async function setOn($: EngineInterface, wanted: boolean): Promise<void> {
160 await update($, isOn, () => wanted)
161 await $.store.set('isOn', wanted)
162}
163
164export const register: Register = (on, options) => {
165 if (options.language === 'hu' || options.language === 'en') lang = options.language
166
167 on('session.start', async ($, e, next) => {
168 const result = await next(e)
169 lang = await pickLanguage($, options.language)
170 const saved = (await $.store.get('isOn')) !== false
171 await update($, isOn, () => saved)
172 await $.command.register({ name: 'hasznalat', description: t().command, argumentHint: t().hint, immediate: true })
173 await refresh($)
174 return result
175 })
176
177 on('command.run', { command: 'hasznalat' }, async ($, e) => {
178 const arg = e.args.trim().toLowerCase()
179 const wanted = ['be', 'on'].includes(arg) ? true : ['ki', 'off'].includes(arg) ? false : arg === '' ? !(await read($, isOn)) : null
180 if (wanted === null) return { text: t().usage }
181 await setOn($, wanted)
182 if (wanted) await refresh($)
183 return { text: wanted ? t().on : t().off }
184 })
185
186 // Az eszköztár gombja parancs nélkül kapcsol (így nem marad parancssor a transcriptben).
187 on('state.set', { plugin: 'tool-hub', key: 'request' }, async ($, e, next) => {
188 const done = await next(e)
189 if (e.value?.tool === 'header') await setOn($, e.value.on).catch(() => undefined)
190 return done
191 })
192
193 // Turn végén és limitmozduláskor: a bontás a helyi becslés ('summary'), API-hívás nélkül.
194 on('session.measure', async ($, e, next) => {
195 const result = await next(e)
196 const moved = e.changed.includes('context') || e.changed.includes('rateLimits')
197 if (moved && (await read($, isOn))) await refresh($)
198 return result
199 })
200
201 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
202 if (e.props.hasSurvey || !(await read($, isOn))) return next(e)
203 const below = await next(e)
204 const { Box, Text } = $.ui.resolve(e)
205 const b = await read($, breakdown)
206 const shown = (await read($, limits)).filter(l => l.kind in t().windows)
207 const columns = e.props.bodyColumns || e.viewport?.columns || 80
208 const width = Math.max(10, Math.min(60, columns - LABEL - 20))
209
210 const label = (text: string) => <Text bold>{text.padEnd(LABEL)}</Text>
211
212 let contextRows
213 if (b === null || b.segments.length === 0) {
214 contextRows = (
215 <Box flexDirection="row">
216 {label(t().context)}
217 <Text dimColor>{t().noData}</Text>
218 </Box>
219 )
220 } else {
221 const counts = cells(b.segments, b.max, width)
222 const pct = Math.round(b.percent)
223 contextRows = (
224 <Box flexDirection="column">
225 <Box flexDirection="row">
226 {label(t().context)}
227 {b.segments.map((s, i) =>
228 counts[i]! > 0 ? (
229 <Text key={`bar-${i}`} color={s.color} dimColor={s.kind !== 'used'}>
230 {glyph(s.kind).repeat(counts[i]!)}
231 </Text>
232 ) : null,
233 )}
234 <Text color={levelColor(pct)} bold>{` ${pct}%`}</Text>
235 <Text dimColor>{` ${tokens(b.total)}/${tokens(b.max)}`}</Text>
236 </Box>
237 <Box flexDirection="row" flexWrap="wrap" columnGap={2} paddingLeft={LABEL}>
238 {b.segments.map((s, i) => (
239 <Box key={`legend-${i}`} flexDirection="row">
240 <Text color={s.color} dimColor={s.kind !== 'used'}>{s.kind === 'used' ? '■ ' : `${glyph(s.kind)} `}</Text>
241 <Text dimColor={s.kind !== 'used'}>{`${t().names[s.name] ?? s.name} ${tokens(s.tokens)}`}</Text>
242 </Box>
243 ))}
244 </Box>
245 </Box>
246 )
247 }
248
249 const limitWidth = Math.max(6, Math.min(20, Math.floor((columns - LABEL) / 2) - 26))
250 const limitRow =
251 shown.length === 0 ? null : (
252 <Box flexDirection="row" flexWrap="wrap" columnGap={3}>
253 {label(t().limits)}
254 {shown.map(l => {
255 const filled = Math.min(limitWidth, Math.round((l.percentUsed / 100) * limitWidth))
256 const color = levelColor(l.percentUsed)
257 return (
258 <Box key={`limit-${l.kind}`} flexDirection="row">
259 <Text>{`${t().windows[l.kind]} `}</Text>
260 <Text color={color}>{'█'.repeat(filled)}</Text>
261 <Text dimColor>{'░'.repeat(limitWidth - filled)}</Text>
262 <Text color={color} bold>{` ${Math.round(l.percentUsed)}%${l.percentUsed >= WARN_AT ? ' ⚠' : ''}`}</Text>
263 <Text dimColor>{resetText(l.kind, l.resetsAt)}</Text>
264 </Box>
265 )
266 })}
267 </Box>
268 )
269
270 // Ha a fókusz mód lista van fölötte, a vonal annak legszélesebb soráig tart, különben a kontextussorig.
271 const focusOn = (await $.state.get(FOCUS_ON)).value === true
272 const focusList = focusOn ? ((await $.state.get(FOCUS_TASKS)).value ?? []) : []
273 const narrow = e.surface !== 'terminal'
274 const ownWidth = b === null || b.segments.length === 0 ? measure(' '.repeat(LABEL) + t().noData, narrow) : LABEL + width + 16
275 const divider = Math.max(10, Math.min(columns - 1, focusOn ? focusWidth(focusList, columns, e.props.isWorking, lang, narrow) : ownWidth))
276
277 // Vonal a sáv tetején: elválasztja a fölötte lévő modoktól (fókusz mód listája, eszköztár).
278 return (
279 <Box flexDirection="column">
280 {below}
281 <Text key="divider" dimColor>{'─'.repeat(divider)}</Text>
282 {contextRows}
283 {limitRow}
284 </Box>
285 )
286 })
287}
288types/index.d.ts 18 lines1// Egy kategória a kontextusablakból, ahogy a /context listázza; a szín a téma kulcsa.
2export type Segment = { name: string; tokens: number; color: string; kind: 'used' | 'free' | 'buffer' }
3export type Breakdown = { segments: Segment[]; total: number; max: number; percent: number }
4// A fókusz mód listája: a sáv tetején lévő vonal csak a legszélesebb soráig tart.
5export type Task = { title: string; status: 'pending' | 'in_progress' | 'done'; progress: number }
6export type Limit = { kind: string; percentUsed: number; resetsAt?: string }
7
8// Az eszköztár (tool-hub) kérése: a kapcsoló gomb ezt írja, a mod a state.set hookjában veszi át.
9export type HubRequest = { tool: string; on: boolean; n: number }
10
11declare module 'claude-code' {
12 interface PluginState {
13 'tool-hub': { request: HubRequest | null }
14 'focus-mode': { isOn: boolean; tasks: Task[] }
15 'usage-header': { breakdown: Breakdown | null; limits: Limit[]; isOn: boolean }
16 }
17}
18