SLOPSHOPPER

prompt-info

Folder, branch, PR, context and model at the end of the prompt hint line

newspinnerprocesstimer
v0.1.0no licenseupdated 2026-10-02cosmin220304/claude-mods/plugins/prompt-info
A shopper browsing a rack in a slop shop
README

claude-mods

Claude Code mods.

Install

/plugin marketplace add cosmin220304/claude-mods
/plugin install prompt-info@claude-mods

prompt-info

Adds repo (and worktree, when in one), branch, PR, context and model to the end of the line under the prompt:

▸▸ auto mode on (shift+tab to cycle) · new project / SHOP-142 · main (PR #142) · 100k/1000k (opus-5-5)

Branch and PR refresh at start, after each turn and every minute. PR needs gh.

Source 2 files
hooks/register.ts 45 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4// "new project / SHOP-142 · main (PR #142)": git and gh are slow, so refreshed off the render path
5const repo = atom({ plugin: 'prompt-info', key: 'repo' } as const, '')
6
7async function refresh($: EngineInterface) {
8  const cwd = await $.session.cwd()
9  const base = (p: string) => p.split('/').filter(Boolean).at(-1) ?? p
10  let text = base(cwd)
11  const git = await $.process.run(['git', 'rev-parse', '--path-format=absolute', '--git-common-dir', '--show-toplevel', '--abbrev-ref', 'HEAD'], { cwd, timeoutMs: 3000 })
12  const [common, top, branch] = git.stdout.trim().split('\n')
13  if (git.exitCode === 0 && common && top && branch) {
14    // a linked worktree's common dir is the main checkout's .git
15    const name = base(common.replace(/\/\.git\/?$/, ''))
16    text = base(top) === name ? name : `${name} / ${base(top)}`
17    if (branch !== 'HEAD') text += ` · ${branch}`
18    const pr = await $.process.run(['gh', 'pr', 'view', '--json', 'number', '-q', '.number'], { cwd, timeoutMs: 10000 })
19    if (pr.exitCode === 0 && pr.stdout.trim()) text += ` (PR #${pr.stdout.trim()})`
20  }
21  await update($, repo, () => text)
22}
23
24export const register: Register = on => {
25  on('session.start', async ($, e, next) => {
26    const result = await next(e)
27    refresh($)
28    $.clock.every(60_000, () => refresh($))
29    return result
30  })
31
32  on('turn.complete', async ($, e, next) => {
33    refresh($)
34    return next(e)
35  })
36
37  on('ui.render', { component: 'PromptHint' }, async ($, e, next) => {
38    const { context } = await $.session.usage()
39    const model = (await $.session.model()).replace(/^claude-/, '')
40    const k = (n: number) => `${Math.round(n / 1000)}k`
41    const tail = `${await read($, repo)} · ${k(context.tokens ?? 0)}/${k(context.window)} (${model})`
42    return next({ ...e, props: { ...e.props, tail } })
43  })
44}
45
types/index.d.ts 8 lines
1export type Repo = string
2
3declare module 'claude-code' {
4  interface PluginState {
5    'prompt-info': { repo: Repo }
6  }
7}
8