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

Claude Code mods.
/plugin marketplace add cosmin220304/claude-mods
/plugin install prompt-info@claude-mods
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.
hooks/register.ts 45 lines1import { 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}
45types/index.d.ts 8 lines1export type Repo = string
2
3declare module 'claude-code' {
4 interface PluginState {
5 'prompt-info': { repo: Repo }
6 }
7}
8