Pins the checked-out branch and the repo/worktree folder to the status line under the prompt.

hooks/register.ts 47 lines1import type { EngineInterface, Register } from 'claude-code'
2
3async function git($: EngineInterface, args: string[]): Promise<string | undefined> {
4 const { exitCode, stdout } = await $.process.run(['git', ...args])
5 return exitCode === 0 ? stdout.trim() : undefined
6}
7
8// "⎇ <branch> · <repo>" in the main checkout, "⎇ <branch> · <repo> › <worktree>" in a linked worktree.
9export async function locate($: EngineInterface): Promise<string | undefined> {
10 const paths = await git($, ['rev-parse', '--path-format=absolute', '--show-toplevel', '--git-dir', '--git-common-dir'])
11 if (paths === undefined) return undefined
12
13 const [top = '', gitDir, commonDir = ''] = paths.split('\n')
14 const repo = commonDir.replace(/\/\.git\/?$/, '').split('/').pop()
15 const folder = top.split('/').pop()
16 const place = gitDir === commonDir ? repo : `${repo} › ${folder}`
17 const branch = (await git($, ['branch', '--show-current'])) || `detached ${await git($, ['rev-parse', '--short', 'HEAD'])}`
18
19 return `⎇ ${branch} · ${place}`
20}
21
22async function refresh($: EngineInterface): Promise<void> {
23 $.ui.status(await locate($))
24}
25
26export const register: Register = on => {
27 on('session.start', async ($, e, next) => {
28 const started = await next(e)
29 await refresh($)
30 return started
31 })
32
33 // A Bash call may check out another branch.
34 on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
35 const ran = await next(e)
36 await refresh($)
37 return ran
38 }).catch(($, e, next) => next(e)) // a failed refresh never blocks the command
39
40 // Catches what changed outside a tool call, such as a `!` command or another terminal.
41 on('turn.complete', async ($, e, next) => {
42 const done = await next(e)
43 if (e.agentId === undefined) await refresh($)
44 return done
45 })
46}
47