Band above the prompt with this turn's tool calls, and a pane listing the recent ones.

Béla's Claude Code plugins: mods (function-hook plugins that draw panes, bands and status lines), the skill that builds them, and personal skills grouped by theme.
| Plugin | What it is | |
|---|---|---|
mods | /mods opens a keyboard-driven pane that lists your mods, shows what each one does, and switches it on/off now or at startup. | |
mod-builder | A skill that builds new mods to a fixed bar: four tested templates, a mandatory `/<mod> on\ | off, a bare /<mod>` that toggles what it opened, keyboard-first panes, and a validate + type-check + test gate. |
briefings | ai-, tech-, geo-, cyber-, econ-, space-briefing, and morning-briefing, which runs them all into one digest. | |
decks | marp-deck (Marp slides on a tech topic), feature-deck (researches a release and hands it to marp-deck), obsidian-canvas. Decks go to $MARP_DECKS_DIR, else ~/Documents/Presentations. | |
dev-tools | cleanup, deps-audit, docgen, explain, find-bug, justify, package-scout, quick-commit, review-pr, scaffold, summarize-changes. | |
skill-tools | skill-writer, skill-refactor, skill-guide, agent-retro. |
At the prompt of a terminal Claude Code session:
/plugin install mods --marketplace B314-N03/plugin-marketplace
/plugin install mod-builder --marketplace B314-N03/plugin-marketplace
/plugin install briefings --marketplace B314-N03/plugin-marketplace
/plugin install decks --marketplace B314-N03/plugin-marketplace
/plugin install dev-tools --marketplace B314-N03/plugin-marketplace
/plugin install skill-tools --marketplace B314-N03/plugin-marketplace
A plugin's skills are called by their namespaced name, e.g. /briefings:ai-briefing.
Answer y to add the marketplace the first time, then choose the user scope. Get updates with claude plugin update <name>, then /reload-plugins.
Add your clone as a folder marketplace instead. Installed plugins are then read straight from the folder, so an edit reaches a session after /reload-plugins, with no version bump:
claude plugin marketplace add ~/Coding/plugin-marketplace
claude plugin install mods@b314
claude plugin install mod-builder@b314
# and briefings@b314, decks@b314, dev-tools@b314, skill-tools@b314
Gate a plugin before committing (the types file path is the one the built-in plugin-authoring skill names when it loads):
bash plugins/mod-builder/skills/mod-builder/scripts/check.sh plugins/mods <types-file>
It must end with == PASS. claude plugin validate . checks the marketplace file.
.claude-plugin/marketplace.json the plugin list
plugins/mods/ hooks module, pure logic, tests, types contract
plugins/mod-builder/skills/mod-builder/
SKILL.md the build procedure
reference/conventions.md the rules (off switch, keyboard, tests)
templates/ status-line, band-pane, tool-hook, poller
scripts/ new-mod.sh, check.sh, promote.shhooks/register.tsx 110 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import { addCall, bandText, markDone, paneAction, parseToggle } from './logic'
5
6const NAME = 'tpl-panel'
7const PANE = 'tpl-panel'
8const OFF_KEY = 'isOff'
9const calls = atom({ plugin: 'tpl-panel', key: 'calls' } as const, [])
10const turn = atom({ plugin: 'tpl-panel', key: 'turn' } as const, 0)
11const isOff = atom({ plugin: 'tpl-panel', key: 'isOff' } as const, false)
12
13// A status pane: Esc closes it once the person has given it the keyboard (ctrl+x tab).
14// A pane the person acts in (a picker, a manager) also passes `focus: true`, and
15// calls this again after any `$.command.run`, which hands the keys to the prompt.
16async function openPane($: EngineInterface) {
17 return $.ui.open({ id: PANE, title: 'Tool calls', closeOnEscape: true })
18}
19
20async function setOff($: EngineInterface, value: boolean) {
21 await $.store.set(OFF_KEY, value)
22 await update($, isOff, () => value)
23 if (value) await $.ui.close({ id: PANE })
24}
25
26export const register: Register = on => {
27 on('session.start', async ($, e, next) => {
28 await $.command.register({
29 name: 'tpl-panel',
30 description: `Tool-call band and pane. /${NAME} opens or closes the pane, /${NAME} off hides both, /${NAME} on brings them back`,
31 argumentHint: '[on|off]',
32 })
33 const wasOff = (await $.store.get(OFF_KEY)) === true
34 await update($, isOff, () => wasOff)
35 return next(e)
36 })
37
38 on('prompt.submit', async ($, e, next) => {
39 await update($, turn, n => n + 1)
40 return next(e)
41 })
42
43 on('tool.call', async ($, e, next) => {
44 if (e.agentId !== undefined || (await read($, isOff))) return next(e)
45 const id = e.tool_use_id ?? `${Date.now()}`
46 const currentTurn = await read($, turn)
47 await update($, calls, list => addCall(list, { id, tool: String(e.tool), isDone: false, turn: currentTurn }))
48 const ran = await next(e)
49 await update($, calls, list => markDone(list, id))
50 return ran
51 })
52
53 on('command.run', { command: 'tpl-panel' }, async ($, e) => {
54 const toggle = parseToggle(e.args)
55 if (toggle === 'invalid') return { text: `Use /${NAME}, /${NAME} on or /${NAME} off.` }
56 if (toggle === 'off') {
57 await setOff($, true)
58 return { text: `${NAME} off: band hidden, pane closed (also in new sessions). /${NAME} on to bring it back.` }
59 }
60 if (toggle === 'on') await setOff($, false)
61 if (await read($, isOff)) return { text: `${NAME} is off. /${NAME} on first.` }
62 // A bare command toggles the pane: the way out is the way in.
63 if (toggle === 'show' && paneAction(await $.ui.panes(), PANE) === 'close') {
64 await $.ui.close({ id: PANE })
65 return { text: `Pane closed. /${NAME} opens it again.` }
66 }
67 await openPane($)
68 return { text: toggle === 'on' ? `${NAME} on.` : 'Pane opened.' }
69 })
70
71 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
72 if (e.props.hasSurvey || (await read($, isOff))) return next(e)
73 const text = bandText(await read($, calls), await read($, turn))
74 if (text === null) return next(e)
75
76 const { Box, Button, Text } = $.ui.resolve(e)
77 return (
78 <Box flexDirection="row">
79 <Text dimColor wrap="truncate">
80 {text}{' '}
81 </Text>
82 <Button key="open" label="Open" plain hotkey="o" onPress={() => void openPane($)} />
83 </Box>
84 )
85 })
86
87 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
88 const { Box, Button, Text } = $.ui.resolve(e)
89 const list = await read($, calls)
90 const room = Math.max(1, (e.viewport?.rows ?? 24) - 5)
91
92 return (
93 <Box flexDirection="column">
94 <Box gap={1}>
95 {/* Every Button gets a hotkey: the main screen has no pointer, so a Button without one is unreachable. */}
96 <Button key="clear" label="clear" plain hotkey="c" onPress={() => void update($, calls, () => [])} />
97 <Text dimColor>· ctrl+x tab focuses · esc closes</Text>
98 </Box>
99 {list.length === 0 && <Text dimColor>No tool calls yet.</Text>}
100 {list.slice(-room).map(call => (
101 <Text wrap="truncate" dimColor={call.isDone}>
102 <Text color={call.isDone ? 'success' : 'warning'}>{call.isDone ? '✓' : '…'} </Text>
103 {call.tool}
104 </Text>
105 ))}
106 </Box>
107 )
108 })
109}
110hooks/logic.ts 29 lines1import type { TplPanelCall } from '../types'
2
3export const KEEP = 200
4
5export const addCall = (list: readonly TplPanelCall[], call: TplPanelCall) => [...list, call].slice(-KEEP)
6
7export const markDone = (list: readonly TplPanelCall[], id: string) =>
8 list.map(call => (call.id === id ? { ...call, isDone: true } : call))
9
10export const bandText = (list: readonly TplPanelCall[], turn: number) => {
11 const thisTurn = list.filter(call => call.turn === turn)
12 if (thisTurn.length === 0) return null
13 const running = thisTurn.filter(call => !call.isDone).length
14 const last = thisTurn[thisTurn.length - 1]!.tool
15 return `${thisTurn.length} tool calls this turn${running > 0 ? ` · ${running} running` : ''} · last: ${last}`
16}
17
18export type Toggle = 'on' | 'off' | 'show' | 'invalid'
19
20export const parseToggle = (args: string): Toggle => {
21 const arg = args.trim().toLowerCase()
22 if (arg === '') return 'show'
23 return arg === 'on' || arg === 'off' ? arg : 'invalid'
24}
25
26/** What a bare `/<mod>` does to its pane: closes it while the person is looking at it, else opens it (or brings it to the front). */
27export const paneAction = (panes: readonly { id: string; isShown: boolean }[], id: string): 'open' | 'close' =>
28 panes.some(pane => pane.id === id && pane.isShown) ? 'close' : 'open'
29types/index.d.ts 8 lines1export type TplPanelCall = { id: string; tool: string; isDone: boolean; turn: number }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'tpl-panel': { calls: TplPanelCall[]; turn: number; isOff: boolean }
6 }
7}
8