Shows the signed-in account email (as in /status) at the right of the prompt footer

A Claude Code mod that shows which account you're signed in as (the email /status reports), in the dim label area at the right of the footer under the prompt.
Useful when you switch between personal and work accounts and want to know at a glance which one a session is billing to.
In a Claude Code terminal session:
/plugin install ccwhoami --marketplace qodfathr/ccwhoami
Answer y to add the marketplace, then pick a scope (user scope loads it in every session).
claude auth status, the same account info /status shows, and reads the email field./login or /logout.userEmail context block Claude Code sends with the first prompt.SessionMode component), so it sits beside labels such as focus on the right.The plugin API has no hook for the prompt's border, so the email can't be drawn on the border line the way a /rename title is drawn on the top border. The footer's right side is the closest supported spot.
claude plugin validate .
claude plugin test .
claude --plugin-dir .
Requires Claude Code with function-hook (mod) support. The plugin API is in early access and may change between releases.
hooks/register.tsx 51 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4const email = atom({ plugin: 'ccwhoami', key: 'email' } as const, null)
5
6const EMAIL = /[^\s@]+@[^\s@]+\.[^\s@.]+/
7
8// `claude auth status` prints the same account /status shows, as JSON.
9async function refresh($: EngineInterface) {
10 let out = ''
11 let found: string | null = null
12 try {
13 for await (const { stream, text } of $.process.spawn({ argv: ['claude', 'auth', 'status'] })) {
14 if (stream === 'stdout') out += text
15 }
16 const status = JSON.parse(out)
17 if (status.loggedIn && typeof status.email === 'string') found = status.email
18 } catch {}
19 await update($, email, () => found)
20}
21
22export const register: Register = on => {
23 on('session.start', async ($, e, next) => {
24 const started = await next(e)
25 void refresh($)
26 return started
27 })
28
29 // Re-read after the account changes.
30 for (const command of ['login', 'logout']) {
31 on('command.run', { command }, async ($, e, next) => {
32 const result = await next(e)
33 void refresh($)
34 return result
35 })
36 }
37
38 // Fallback: the userEmail context block carries the same address.
39 on('prompt.context', async ($, e, next) => {
40 const found = e.blocks.find(b => b.name === 'userEmail')?.text.match(EMAIL)?.[0]
41 if (found && (await read($, email)) === null) await update($, email, () => found)
42 return next(e)
43 })
44
45 on('ui.render', { component: 'SessionMode' }, async ($, e, next) => {
46 const current = await read($, email)
47 if (!current) return next(e)
48 return next({ ...e, props: { ...e.props, modes: [...e.props.modes, current] } })
49 })
50}
51types/index.d.ts 8 lines1declare module 'claude-code' {
2 interface PluginState {
3 ccwhoami: { email: string | null }
4 }
5}
6
7export type {}
8