Status line: how long the last turn took; a toast when a turn runs long.

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.ts 57 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import { formatDuration, isSlow, parseToggle, statusText } from './logic'
5
6const NAME = 'tpl-status'
7const OFF_KEY = 'isOff'
8const isOff = atom({ plugin: 'tpl-status', key: 'isOff' } as const, false)
9
10let toolCalls = 0
11
12async function setOff($: EngineInterface, value: boolean) {
13 await $.store.set(OFF_KEY, value)
14 await update($, isOff, () => value)
15 if (value) $.ui.status(undefined)
16}
17
18export const register: Register = on => {
19 on('session.start', async ($, e, next) => {
20 await $.command.register({
21 name: 'tpl-status',
22 description: `Last-turn status line. /${NAME} toggles it, /${NAME} off hides it, /${NAME} on shows it again`,
23 argumentHint: '[on|off]',
24 })
25 const wasOff = (await $.store.get(OFF_KEY)) === true
26 await update($, isOff, () => wasOff)
27 return next(e)
28 })
29
30 on('prompt.submit', async ($, e, next) => {
31 toolCalls = 0
32 return next(e)
33 })
34
35 on('tool.call', async ($, e, next) => {
36 if (e.agentId === undefined) toolCalls += 1
37 return next(e)
38 })
39
40 on('turn.complete', async ($, e, next) => {
41 const result = await next(e)
42 if (e.agentId !== undefined || (await read($, isOff))) return result
43 $.ui.status(statusText(e.durationMs, toolCalls))
44 if (isSlow(e.durationMs)) $.ui.toast(`That turn took ${formatDuration(e.durationMs)}`)
45 return result
46 })
47
48 on('command.run', { command: 'tpl-status' }, async ($, e) => {
49 const toggle = parseToggle(e.args)
50 if (toggle === 'invalid') return { text: `Use /${NAME} (toggles), /${NAME} on or /${NAME} off.` }
51 // No pane: a bare command toggles the status line, the mod's one surface.
52 const value = toggle === 'show' ? !(await read($, isOff)) : toggle === 'off'
53 await setOff($, value)
54 return { text: value ? `${NAME} off (also in new sessions). /${NAME} on to bring it back.` : `${NAME} on.` }
55 })
56}
57hooks/logic.ts 22 lines1// Pure logic: no `$`, no I/O. Everything here is unit-tested.
2
3export const SLOW_TURN_MS = 120_000
4
5export const formatDuration = (ms: number) => {
6 const seconds = Math.round(ms / 1000)
7 return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`
8}
9
10export const statusText = (durationMs: number, toolCalls: number) =>
11 `last turn ${formatDuration(durationMs)} · ${toolCalls} tool call${toolCalls === 1 ? '' : 's'}`
12
13export const isSlow = (durationMs: number) => durationMs >= SLOW_TURN_MS
14
15export type Toggle = 'on' | 'off' | 'show' | 'invalid'
16
17export const parseToggle = (args: string): Toggle => {
18 const arg = args.trim().toLowerCase()
19 if (arg === '') return 'show'
20 return arg === 'on' || arg === 'off' ? arg : 'invalid'
21}
22types/index.d.ts 8 lines1export type TplStatusToggle = boolean
2
3declare module 'claude-code' {
4 interface PluginState {
5 'tpl-status': { isOff: TplStatusToggle }
6 }
7}
8