A one-line band above the prompt showing the git branch, changed-file count and ahead/behind, refreshed after every turn.

Branch, dirty count and ahead/behind always visible above the prompt.
Adds one dim line above the prompt with the current git branch, how many files changed, and commits ahead or behind upstream. It refreshes after every turn and every 15 seconds, so you notice at a glance when Claude has modified files or you are on the wrong branch.
/plugin marketplace add cloudcruncher/claude-mod-garage
/plugin install git-band@mod-garage
Run /reload-plugins (or restart Claude Code) afterwards. To try it from a local clone without installing: claude --plugin-dir ./dev-mods/git-band
Illustrative output.
Above the prompt
⎇ feature/login ● 3 changed ↑1
> _
After committing
⎇ feature/login ✓ clean ↑2
> _
hooks/register.tsx 39 lines1import type { Register } from 'claude-code'
2import { format, parse, type GitInfo } from './lib'
3
4let info: GitInfo | undefined
5
6async function refresh($: any) {
7 const r = await $.process.run(['git', 'status', '--porcelain=v1', '-b'], { timeoutMs: 5000 }).catch(() => undefined)
8 const next = r && r.exitCode === 0 ? parse(r.stdout) : undefined
9 if (JSON.stringify(next) !== JSON.stringify(info)) {
10 info = next
11 $.ui.invalidate('ui.render')
12 }
13}
14
15export const register: Register = on => {
16 on('session.start', async ($, e, next) => {
17 await refresh($)
18 $.clock.every(15_000, () => refresh($))
19 return next(e)
20 })
21
22 on('turn.complete', async ($, e, next) => {
23 if (!e.agentId) await refresh($)
24 return next(e)
25 })
26
27 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
28 const theirs = await next(e)
29 if (!info || e.props.hasSurvey) return theirs
30 const { Box, Text } = $.ui.resolve(e)
31 return (
32 <Box flexDirection="column">
33 {theirs}
34 <Text dimColor>{format(info)}</Text>
35 </Box>
36 )
37 })
38}
39hooks/lib.ts 22 lines1export type GitInfo = { branch: string; changed: number; ahead: number; behind: number }
2
3/** Parses `git status --porcelain=v1 -b` output. */
4export function parse(out: string): GitInfo | undefined {
5 const lines = out.split('\n').filter(Boolean)
6 const head = lines[0]
7 if (!head || !head.startsWith('## ')) return undefined
8 const m = head.slice(3).match(/^(?:No commits yet on |Initial commit on )?([^\s.]+(?:\.[^\s.]+)*?)(?:\.\.\.\S+)?(?: \[(.*)\])?$/)
9 const branch = m?.[1] ?? head.slice(3)
10 const flags = m?.[2] ?? ''
11 return {
12 branch,
13 changed: lines.length - 1,
14 ahead: Number(flags.match(/ahead (\d+)/)?.[1] ?? 0),
15 behind: Number(flags.match(/behind (\d+)/)?.[1] ?? 0),
16 }
17}
18
19export function format(g: GitInfo): string {
20 return `⎇ ${g.branch}` + (g.changed ? ` ● ${g.changed} changed` : ' ✓ clean') + (g.ahead ? ` ↑${g.ahead}` : '') + (g.behind ? ` ↓${g.behind}` : '')
21}
22