Status line showing session time, prompts, and tool calls.

Mods that change Claude Code's UI, in the terminal and the desktop app's Code tab. Each folder is one mod: a plugin of function hooks.
| Mod | What it does |
|---|---|
git-diff-timeline | A Git Diff timeline above the prompt. Click one commit to see it or two to compare them, with their commit messages. A Branches tab compares two branches. A GitHub-style "Files changed" panel shows each file's diff. |
session-status | A status line with the session's time, prompts and tool calls. |
To load mods in every session, list their folders in the env block of ~/.claude/settings.json. Separate the paths with ; on Windows and : elsewhere:
{
"env": {
"CLAUDE_CODE_PLUGIN_DIRS": "C:\\path\\to\\claude-code-mods\\git-diff-timeline;C:\\path\\to\\claude-code-mods\\session-status",
"CLAUDE_CODE_PLUGIN_DIR_WATCH": "1"
}
}
A session reads this when it starts. With CLAUDE_CODE_PLUGIN_DIR_WATCH=1, open sessions reload a mod when its files change.
To load a mod for one session only: claude --plugin-dir ./git-diff-timeline.
my-mod/
.claude-plugin/plugin.json { "name": "my-mod", "version": "0.1.0", "description": "..." }
hooks/hooks.json { "modules": ["./register.tsx"] }
hooks/register.tsx export const register: Register = (on, options) => { ... }
See CLAUDE.md for the rules the engine holds mods to.
claude plugin test ./git-diff-timeline
claude plugin validate ./git-diff-timelinehooks/register.tsx 40 lines1import type { Register } from 'claude-code'
2
3const REFRESH_MS = 30_000
4
5const elapsed = (since: number) => {
6 const minutes = Math.floor((Date.now() - since) / 60_000)
7 return minutes < 60 ? `${minutes}m` : `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, '0')}m`
8}
9
10export const register: Register = on => {
11 let startedAt = Date.now()
12 let prompts = 0
13 let tools = 0
14 let failed = 0
15
16 const line = () =>
17 `⏱ ${elapsed(startedAt)} · ${prompts} prompts · ${tools} tools${failed > 0 ? ` (${failed} failed)` : ''}`
18
19 on('session.start', ($, e, next) => {
20 startedAt = Date.now()
21 $.ui.status(line())
22 $.clock.every(REFRESH_MS, () => $.ui.status(line()))
23 return next(e)
24 })
25
26 on('prompt.submit', ($, e, next) => {
27 prompts += 1
28 $.ui.status(line())
29 return next(e)
30 })
31
32 on('tool.call', async ($, e, next) => {
33 tools += 1
34 const ran = await next(e)
35 if (ran.deny === undefined && ran.isError === true) failed += 1
36 $.ui.status(line())
37 return ran
38 })
39}
40