SLOPSHOPPER

ccwhoami

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

newspinnerpromptprocess
v0.1.0no licenseupdated 2026-10-08qodfathr/ccwhoami
A shopper browsing a rack in a slop shop
README

ccwhoami

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.

Install

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).

How it works

  • At session start it runs claude auth status, the same account info /status shows, and reads the email field.
  • It looks the email up again after /login or /logout.
  • If that lookup fails, it uses the userEmail context block Claude Code sends with the first prompt.
  • It adds the email to the footer's mode labels (the 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.

Develop

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.

Source 2 files
hooks/register.tsx 51 lines
1import { 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}
51
types/index.d.ts 8 lines
1declare module 'claude-code' {
2  interface PluginState {
3    ccwhoami: { email: string | null }
4  }
5}
6
7export type {}
8