A one-line git status band above the prompt: branch, ahead/behind, staged, changed and untracked counts.

Small, independent mods for Claude Code: a live progress bar, a usage dashboard, a context-window meter, a safety guard, a git status band, and your own notes and todos.
Each mod is a plugin of function hooks. Install only the ones you want; none depends on another.
| Mod | Command | What it gives you |
|---|---|---|
| task-progress | /progress | A progress band above the prompt that follows the todo list, with a working timer and your usage. |
| usage-dashboard | /usage-dashboard | A pane with 5h and weekly limits, models, tasks, time left and a 7-day chart. |
| context-meter | /ctxmeter | Context-window percentage in the status line, with warnings at 70% and 90%. |
| safety-guard | /safeguard | Blocks destructive commands and writes to secret or key files before they run. |
| git-band | /gitband | Branch, ahead/behind and change counts on one line above the prompt. |
| notes-todos | /mytodo, /mynote | Your own todo list and notes that persist across sessions. |
Run these in a terminal Claude Code session. The desktop app's Code tab does not run /plugin install.
/plugin install task-progress --marketplace wablevaibhav/claude-mods
Answer y to add the marketplace the first time, then pick a scope. Repeat with any other mod name from the table:
/plugin install usage-dashboard --marketplace wablevaibhav/claude-mods
/plugin install context-meter --marketplace wablevaibhav/claude-mods
/plugin install safety-guard --marketplace wablevaibhav/claude-mods
/plugin install git-band --marketplace wablevaibhav/claude-mods
/plugin install notes-todos --marketplace wablevaibhav/claude-mods
Clone the repo and point Claude Code at the mod folders:
git clone https://github.com/wablevaibhav/claude-mods.git
cd claude-mods
claude --plugin-dir ./task-progress --plugin-dir ./usage-dashboard
Add one --plugin-dir per mod.
task-progressis a real screenshot. The other previews are illustrative text with sample numbers; real colours (green / amber / red, orange accents) follow your terminal theme.
task-progress: the band above the prompt

usage-dashboard: the /usage-dashboard pane
Claude usage updated 12s ago
╭──────────────────────────────────────────────────────────────────╮
│ 5-hour session resets in 2h 10m │
│ ██████████████▌░░░░░░░░░░░░░░░░░░░░░░░░░ 35% │
│ burning 0.40%/min · full in ~2h 5m │
╰──────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────╮
│ Weekly resets in 4d 3h │
│ █████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12% │
╰──────────────────────────────────────────────────────────────────╯
● Limit risk green session cost ~$1.42
╭──────────────────────────────────────────────────────────────────╮
│ Models active: opus-5-5 (3m ago) │
│ ★ opus-5-5 ████████░░ 78% 1.2M 34 msgs ~$0.91 │
│ sonnet-5-5 ██░░░░░░░░ 22% 340k 12 msgs ~$0.18 │
│ tokens: in / out / cache = 80k / 190k / 1.3M │
╰──────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────╮
│ Tasks ▸ 1 in progress ○ 3 pending ✓ 2 today │
│ ▸ Wire the charts 1m 20s │
│ ○ Add the tasks board │
│ ○ Polish and test │
│ Time left 25m (best case 14m) · confidence medium, 4 finished │
│ Should finish within the current 5h window. │
╰──────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────╮
│ Last 7 days │
│ Wed 07 ██████████████████░░░░░░░░░░░░░░░░░░░░░░ 420k │
│ Thu 08 ███████████████████████████████░░░░░░░░░ 740k │
│ Fri 09 ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 240k │
│ Sat 10 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0 │
│ Sun 11 ██████████████████████░░░░░░░░░░░░░░░░░░ 530k │
│ Mon 12 ████████████████████████████████████████ 1.0M │
╰──────────────────────────────────────────────────────────────────╯
context-meter and git-band each add one line above the prompt:
▲ Context 78% ███████████████████░░░░░ 156k / 200k Getting full: consider /compact soon. [Hide]
⎇ feature/login ↑2 ↓1 ● 3 staged ✎ 2 changed ? 2 untracked
A progress band above the prompt, visible while you type.
progress tool. Claude can report title, total, done, note, items and finished explicitly (the tool is mcp__task-progress__progress), for work that doesn't use a todo list.⠹ Working 12s) whenever a turn is running and no job is shown.Collapse / Expand hides the step list, Hide or /progress hides the band. A finished job clears when you send your next prompt.Open it with /usage-dashboard. (/usage is Claude Code's own built-in panel; this mod uses a different name so the two don't collide.)
5h 34% · wk 12% · opus-5-5 after your first reply.ctx 43%, the share of the context window in use./ctxmeter hides or shows the band.Stops a call before it runs and tells Claude why, so it asks you first.
| Blocks | Examples | ||
|---|---|---|---|
| Recursive deletes of root, home or wildcards | rm -rf /, rm -r ~, rm -rf *, rm -rf /etc | ||
| Force pushes without a lease | git push --force, git push -f (--force-with-lease is allowed) | ||
| Piping a download into a shell | `curl … \ | sh, wget … \ | sudo bash` |
| Disk and filesystem damage | dd … of=/dev/sda, mkfs.*, > /dev/sda, fork bombs, chmod -R 777 | ||
| Writing secrets | .env, .env.production (not .env.example), *.pem, *.key, id_rsa, service-account JSON, .npmrc, .aws/credentials, .git/ internals | ||
| Reading private keys | id_rsa, *.pem, *.key, .ssh/, .aws/credentials |
/safeguard shows status and how many calls it blocked. /safeguard pause and /safeguard resume turn it off and on for the session. If the guard itself errors, it blocks the call rather than letting it through.
detached HEAD), ↑ahead ↓behind against the upstream, staged, changed and untracked counts, conflicts, or ✓ clean./gitband hides or shows it.Your own lists, separate from Claude's todo list, stored locally and kept across sessions. The open-todo count shows in the status line.
/mytodo add Renew the domain add a todo (a bare /mytodo <text> also adds)
/mytodo list todos
/mytodo done 2 /mytodo undo 2 /mytodo rm 2 /mytodo clear
/mynote Wifi password is on the fridge save a note
/mynote list notes
/mynote find wifi /mynote rm 3
five_hour, seven_day). Accounts without those windows (some API-key setups) show "No reading yet".TodoWrite list. If your session uses another task tool, task-progress can still be driven through its progress tool./usage, /context and /todos.git-band runs git status.| Problem | What to try |
|---|---|
/usage shows "Session limit / Breakdown" | That is Claude Code's built-in panel. Use /usage-dashboard. |
| A mod's command isn't found | The mod isn't loaded. Install it, or start Claude with --plugin-dir ./<mod>. |
| Gauges say "No reading yet" | Send one message; the reading arrives with the first reply. |
| Band shows the old layout after an update | Close the pane and reopen it, or restart Claude. |
| A command was blocked unexpectedly | Run /safeguard to see why, /safeguard pause if you really want it. |
/plugin install isn't available | Use a terminal Claude Code session, not the desktop app's Code tab. |
claude plugin validate . # the marketplace
claude plugin validate ./<mod> # one mod
claude plugin test ./<mod> # its tests
Each mod is a folder with .claude-plugin/plugin.json, hooks/hooks.json, a hooks module, pure logic with *.test.ts files, and (where it keeps state) a types/index.d.ts contract. The marketplace file is .claude-plugin/marketplace.json.
Releases are git tags: v0.2.0 added context-meter, safety-guard, git-band and notes-todos.
MIT, see LICENSE.
hooks/register.tsx 76 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { GitStatus } from '../types'
5import { isClean, parseStatus } from './parse'
6
7const git = atom({ plugin: 'git-band', key: 'git' } as const, null as GitStatus | null)
8const isHidden = atom({ plugin: 'git-band', key: 'isHidden' } as const, false)
9
10async function refresh($: EngineInterface) {
11 const result = await $.process.run(['git', 'status', '--porcelain=v2', '--branch'], {
12 timeoutMs: 5000,
13 })
14 const parsed = result.exitCode === 0 ? parseStatus(result.stdout) : null
15 await update($, git, () => parsed)
16}
17
18export const register: Register = on => {
19 on('session.start', async ($, e, next) => {
20 await $.command.register({
21 name: 'gitband',
22 description: 'Show or hide the git status band above the prompt',
23 })
24 await refresh($).catch(() => undefined)
25 $.clock.every(15_000, () => {
26 void refresh($).catch(() => undefined)
27 })
28
29 return next(e)
30 })
31
32 on('command.run', { command: 'gitband' }, async $ => {
33 const hidden = await update($, isHidden, h => !h)
34 return { text: hidden ? 'Git band hidden.' : 'Git band shown.' }
35 }).catch(() => ({ text: 'Git band could not toggle.' }))
36
37 on('turn.complete', async ($, e, next) => {
38 await refresh($).catch(() => undefined)
39 return next(e)
40 }).catch(($, e, next) => next(e))
41
42 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
43 if (e.props.hasSurvey || (await read($, isHidden))) return next(e)
44
45 const s = await read($, git)
46 if (s === null) return next(e)
47
48 const { Box, Text } = $.ui.resolve(e)
49 const clean = isClean(s)
50
51 return (
52 <Box paddingX={1} gap={1}>
53 <Text bold color="claude">
54 ⎇ {s.isDetached ? 'detached HEAD' : s.branch}
55 </Text>
56 {s.upstream !== null && (s.ahead > 0 || s.behind > 0) && (
57 <Text color="warning">
58 ↑{s.ahead} ↓{s.behind}
59 </Text>
60 )}
61 {clean ? (
62 <Text color="success">✓ clean</Text>
63 ) : (
64 <>
65 {s.staged > 0 && <Text color="success">● {s.staged} staged</Text>}
66 {s.changed > 0 && <Text color="warning">✎ {s.changed} changed</Text>}
67 {s.untracked > 0 && <Text dimColor>? {s.untracked} untracked</Text>}
68 {s.conflicts > 0 && <Text color="error">⚠ {s.conflicts} conflicts</Text>}
69 </>
70 )}
71 {s.upstream === null && !s.isDetached && <Text dimColor>no upstream</Text>}
72 </Box>
73 )
74 })
75}
76hooks/parse.ts 48 lines1import type { GitStatus } from '../types'
2
3/** Parses `git status --porcelain=v2 --branch` output. Null when it holds no branch header. */
4export const parseStatus = (text: string): GitStatus | null => {
5 const status: GitStatus = {
6 branch: '',
7 isDetached: false,
8 upstream: null,
9 ahead: 0,
10 behind: 0,
11 staged: 0,
12 changed: 0,
13 untracked: 0,
14 conflicts: 0,
15 }
16 let sawBranch = false
17
18 for (const line of text.split('\n')) {
19 if (line.startsWith('# branch.head ')) {
20 const head = line.slice('# branch.head '.length).trim()
21 status.isDetached = head === '(detached)'
22 status.branch = head
23 sawBranch = true
24 } else if (line.startsWith('# branch.upstream ')) {
25 status.upstream = line.slice('# branch.upstream '.length).trim()
26 } else if (line.startsWith('# branch.ab ')) {
27 const m = /\+(\d+)\s+-(\d+)/.exec(line)
28 if (m) {
29 status.ahead = Number(m[1])
30 status.behind = Number(m[2])
31 }
32 } else if (line.startsWith('1 ') || line.startsWith('2 ')) {
33 const xy = line.split(' ')[1] ?? '..'
34 if (xy[0] !== '.') status.staged += 1
35 if (xy[1] !== '.') status.changed += 1
36 } else if (line.startsWith('u ')) {
37 status.conflicts += 1
38 } else if (line.startsWith('? ')) {
39 status.untracked += 1
40 }
41 }
42
43 return sawBranch ? status : null
44}
45
46export const isClean = (s: GitStatus): boolean =>
47 s.staged + s.changed + s.untracked + s.conflicts === 0
48types/index.d.ts 21 lines1export type GitStatus = {
2 branch: string
3 isDetached: boolean
4 upstream: string | null
5 ahead: number
6 behind: number
7 staged: number
8 changed: number
9 untracked: number
10 conflicts: number
11}
12
13declare module 'claude-code' {
14 interface PluginState {
15 'git-band': {
16 git: GitStatus | null
17 isHidden: boolean
18 }
19 }
20}
21