Band above the prompt: current model and reasoning effort.

Lets you switch model and reasoning effort without typing a command.
Model Sonnet 5.5 ▾ ┃ Effort medium ▾
/model <id>, selecting an effort runs /effort <level>.low, medium, high, xhigh, max.settings.json (modelSettings.<model>.effortLevel, else effortLevel). After that it follows what the turn reports.The model and effort lists are the MODELS and EFFORTS constants in hooks/register.tsx. Bump version in plugin.json after editing.
hooks/register.tsx 82 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// "claude-sonnet-5-5" -> "Sonnet 5.5"; drops "claude-" and a trailing -YYYYMMDD
4export const displayName = (model: string) => {
5 const [name, ...ver] = model.replace(/^claude-/, '').replace(/-\d{8}$/, '').split('-')
6 return [name[0].toUpperCase() + name.slice(1), ver.join('.')].filter(Boolean).join(' ')
7}
8
9// ponytail: fixed list; a model not in it still shows (as current) but is not offered
10const MODELS = ['claude-fable-5-1', 'claude-opus-5-5', 'claude-sonnet-5-5', 'claude-haiku-4-5-20251001']
11const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max']
12
13// effort before any turn: the per-model setting, else the global one
14const savedEffort = async ($: EngineInterface, m: string) => {
15 const s = (await $.settings.read().catch(() => ({}))) as { effortLevel?: string; modelSettings?: Record<string, { effortLevel?: string }> }
16 return s.modelSettings?.[m]?.effortLevel ?? s.effortLevel
17}
18
19export const register: Register = on => {
20 // ponytail: module vars reset on hot reload; model/effort show again after the next turn step
21 let model = ''
22 let effort: string | number | undefined
23
24 on('turn.step', async function* ($, e, next) {
25 if (!e.agentId && e.index === 0 && (e.effort !== effort || e.model !== model)) {
26 effort = e.effort
27 model = e.model
28 $.ui.invalidate('ui.render')
29 }
30 return yield* next(e)
31 })
32
33 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
34 if (e.props.hasSurvey) return next(e)
35 const m = model || (await $.session.model())
36 if (!m) return next(e)
37 effort ??= await savedEffort($, m)
38
39 const { Box, Text, Select } = $.ui.resolve(e)
40 const modelIds = MODELS.includes(m) ? MODELS : [m, ...MODELS]
41 // stack under whatever another plugin drew here; the engine's own default is no tree
42 const other = await next(e)
43 const line = (
44 <Box>
45 <Text bold color="#7dcfff">Model </Text>
46 <Select
47 key="model"
48 value={m}
49 options={modelIds.map(id => ({ value: id, label: displayName(id) }))}
50 onSelect={id => {
51 model = id
52 void $.command.run({ command: 'model', args: id })
53 $.ui.invalidate('ui.render')
54 }}
55 />
56 {effort !== undefined && (
57 <Box>
58 <Text color="#565f89"> ┃ </Text>
59 <Text bold color="#7dcfff">Effort </Text>
60 <Select
61 key="effort"
62 value={String(effort)}
63 options={EFFORTS.map(v => ({ value: v }))}
64 onSelect={v => {
65 effort = v
66 void $.command.run({ command: 'effort', args: v })
67 $.ui.invalidate('ui.render')
68 }}
69 />
70 </Box>
71 )}
72 </Box>
73 )
74 return other.type === 'engine' ? line : (
75 <Box flexDirection="column">
76 {other}
77 {line}
78 </Box>
79 )
80 })
81}
82