After each long turn, a one-line toast: how long it took, how many tools ran, and which files were edited.

A toast after each long turn: how long, how many tools, which files.
When a turn runs longer than a threshold and used tools, shows a one-line toast so you can tab away during long runs and come back to a summary.
/plugin marketplace add cloudcruncher/claude-mod-garage
/plugin install turn-recap@mod-garage
Run /reload-plugins (or restart Claude Code) afterwards. To try it from a local clone without installing: claude --plugin-dir ./dev-mods/turn-recap
Illustrative output.
Toast
Turn done in 1m 35s · 12 tool calls · 3 files edited
| Option | Default | What it does |
|---|---|---|
minSeconds | 20 | Turns shorter than this show no recap |
Set options when installing, or later in /plugin → the mod → configure.
hooks/register.ts 27 lines1import type { Register } from 'claude-code'
2import { fresh, recap, record } from './lib'
3
4export const register: Register = (on, options) => {
5 const minSeconds = Number(options?.minSeconds ?? 20)
6 let tally = fresh()
7
8 on('turn.start', (_$, e, next) => {
9 if (!e.agentId) tally = fresh()
10 return next(e)
11 })
12
13 // Subagent calls count too: they are part of what the turn did.
14 on('tool.call', (_$, e, next) => {
15 record(tally, e.tool, e as unknown as Record<string, unknown>)
16 return next(e)
17 }).catch(($, e, next) => next(e))
18
19 on('turn.complete', ($, e, next) => {
20 if (!e.agentId) {
21 const line = recap(tally, e.durationMs ?? 0, minSeconds)
22 if (line) $.ui.toast(line)
23 }
24 return next(e)
25 })
26}
27hooks/lib.ts 24 lines1const EDIT_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit'])
2
3export type Tally = { tools: number; files: Set<string> }
4
5export const fresh = (): Tally => ({ tools: 0, files: new Set() })
6
7export function record(t: Tally, tool: string, args: Record<string, unknown>): void {
8 t.tools++
9 const path = args.file_path ?? args.notebook_path
10 if (EDIT_TOOLS.has(tool) && typeof path === 'string') t.files.add(path)
11}
12
13export function dur(ms: number): string {
14 const s = Math.round(ms / 1000)
15 return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`
16}
17
18/** The recap line, or undefined when the turn was too short or did nothing. */
19export function recap(t: Tally, ms: number, minSeconds: number): string | undefined {
20 if (ms < minSeconds * 1000 || t.tools === 0) return undefined
21 const f = t.files.size
22 return `Turn done in ${dur(ms)} · ${t.tools} tool call${t.tools === 1 ? '' : 's'}` + (f ? ` · ${f} file${f === 1 ? '' : 's'} edited` : '')
23}
24