Status line shows 5-hour and weekly usage plus running subagents; toasts at 90% and 95% of a window.

Small Claude Code mods from Stylus Nexus: a progress row above the prompt, a usage meter, a Render deploy light, and a checker for team conventions. Each mod is a few hundred lines of TypeScript with tests, so you can read one in a sitting before you install it.
A mod is a Claude Code plugin with a hooks module: it can add status lines, rows above the prompt, toasts, tools and commands.
Read before you install. A mod runs inside your Claude Code session and sees your prompts, tool calls and answers. Review a mod's source first. The table says which mods touch the network or can block a command.
| Mod | What it does | Network | Can block a command? |
|---|---|---|---|
task-progress | One progress row per plan the model reports, plus a row per running subagent. | No | Only its own tool, on bad input |
limits-meter | 5-hour and weekly usage in the status line, plus running subagents. Toasts at 90% and 95%. | No | No |
deploy-watch | A light per Render service in the status line, with a toast when a deploy starts, goes live, fails or is canceled. | Render API, read-only | No |
house-rules | Warns when a goal-checklist strip breaks its rules. Optional: status-line check and a commit and PR title rule. | No | Only the title rule, and only if you turn it on |
/plugin marketplace add stylusnexus/claude-mods
/plugin install task-progress@stylusnexus-mods
Replace task-progress with any mod name above. Run /plugin to enable, disable or configure it later.
Requires Claude Code 2.1.286 or newer, the first build that loads hooks modules (a similar mod, zycck/claude-mods, documents the same minimum). These mods were built and tested on 2.1.296; older builds have not been tried. Check yours with claude --version.
A progress row above the prompt for each plan the model reports:
Ship login · Build · 2/6 steps · ▓▓▓░░░░░░░ 33% · running
↳ explorer · sonnet · Grep · 0:42
{id, next:true}, {id, done:[…], active:…}, {id, failed:…, note}, {id, state:…}. An unknown step title is refused with the list of valid ones.doneBarSeconds (default 30)./progress [show|hide] and /progress-clear.The bar shows what the model reports. A full bar is not proof the work is done; check the work.
Not yet verified: it has not run in a long live session, and a subagent's row leaves on that subagent's first finished turn, so a subagent with several turns may drop off early.
Status line such as 5h 42% · wk 18% · 2 agents. Toasts once when a window passes 90% and again at 95%, with the time until it resets. No options, no network.
Status line such as deploy: api 🟢 · web 🟡. A toast when a service's newest deploy starts, goes live, fails or is canceled.
| Option | Meaning |
|---|---|
renderApiKey | A Render API key. The mod only lists deploys. Stored as a sensitive option. |
serviceIds | Comma-separated Render service ids; label one with name=id, for example api=srv-abc123,web=srv-def456. |
pollSeconds | How often to check, 10 to 600. Default 30. |
The only network calls are GET https://api.render.com/v1/services/<id>/deploys?limit=1 with your key.
Built for one team's goal-checklist convention: rows of ⬜ 🔄 🟦 🚀 ✅ with a tally line, directly above a 🟢/🟡/🔴 status line. By default it only shows a toast when a printed strip breaks the rules (tally mismatch, skipped stage, merged item with no SHA, strip with nothing changed, strip when no checklist is active). Read strip.ts before relying on it.
| Option | Default | Meaning |
|---|---|---|
checkStatusLine | off | Warn when a reply does not end on a 🟢/🟡/🔴 line of 100 characters or fewer. |
enforceCommitRule | off | Blocks git commit and gh pr create unless the subject is type(scope): summary, 50 characters or fewer, lowercase after the colon, no trailing period. |
Each mod lives in plugins/<name>/ with .claude-plugin/plugin.json, hooks/hooks.json and hooks/*.ts. Pure logic sits in its own file with unit tests beside it; register.ts holds the hooks.
cd plugins/<name>
claude plugin test .
claude plugin validate .
There is no CI yet: the checks above need the Claude Code CLI and have not been wired into GitHub Actions.
Issues and pull requests are welcome. Keep a mod small, keep its tests passing, and say in the README table whether it uses the network or can block a command. Use type(scope): summary commit titles of 50 characters or fewer.
MIT © 2026 Stylus Nexus
hooks/register.ts 41 lines1import type { EngineInterface, Register } from 'claude-code'
2import { statusText, step, warning, type Level } from './meter'
3
4async function paint($: EngineInterface) {
5 const [{ rateLimits }, agents] = await Promise.all([$.session.usage(), $.agent.list()])
6 $.ui.status(statusText(rateLimits, agents.filter(a => a.status === 'running').length))
7}
8
9export const register: Register = on => {
10 const seen: Record<string, Level> = {}
11
12 on('session.start', async ($, e, next) => {
13 const result = await next(e)
14 await paint($)
15 return result
16 })
17
18 on('session.measure', async ($, e, next) => {
19 const now = await $.clock.now()
20 for (const limit of e.rateLimits) {
21 const { seen: level, announce } = step(seen[limit.kind] ?? 0, limit.percentUsed)
22 seen[limit.kind] = level
23 if (announce) $.ui.toast(`${$.plugin.name}: ${warning(limit, announce, now)}`)
24 }
25 await paint($)
26 return next(e)
27 })
28
29 on('agent.spawn', async ($, e, next) => {
30 const result = await next(e)
31 await paint($)
32 return result
33 })
34
35 on('turn.complete', async ($, e, next) => {
36 const result = await next(e)
37 if (e.agentId !== undefined) await paint($)
38 return result
39 })
40}
41hooks/meter.ts 36 lines1export type Limit = { kind: string; percentUsed: number; resetsAt?: string }
2export type Level = 0 | 90 | 95
3
4const LABEL: Record<string, string> = { five_hour: '5h', seven_day: 'wk', spend_limit: 'spend' }
5const label = (kind: string) => LABEL[kind] ?? kind
6const pct = (n: number) => `${Math.round(n)}%`
7
8export const statusText = (limits: Limit[], running: number): string | undefined => {
9 const parts = limits.map(l => `${label(l.kind)} ${pct(l.percentUsed)}`)
10 if (running > 0) parts.push(`${running} agent${running === 1 ? '' : 's'}`)
11 return parts.length > 0 ? parts.join(' · ') : undefined
12}
13
14const levelOf = (percent: number): Level => (percent >= 95 ? 95 : percent >= 90 ? 90 : 0)
15
16// What a window has been announced at, and the level to announce now if it rose.
17// The memory clears once the window falls under 85%, so a reset window can warn again.
18export const step = (seen: Level, percent: number): { seen: Level; announce?: 90 | 95 } => {
19 const now = levelOf(percent)
20 if (now > seen) return { seen: now, announce: now as 90 | 95 }
21 return { seen: percent < 85 ? 0 : seen }
22}
23
24const resetsIn = (resetsAt: string | undefined, now: number): string => {
25 const ms = resetsAt === undefined ? NaN : Date.parse(resetsAt) - now
26 if (!(ms > 0)) return ''
27 const minutes = Math.round(ms / 60000)
28 const h = Math.floor(minutes / 60)
29 return h > 0 ? ` Resets in ${h}h ${minutes % 60}m.` : ` Resets in ${minutes}m.`
30}
31
32export const warning = (limit: Limit, level: 90 | 95, now: number): string =>
33 `${label(limit.kind)} window at ${pct(limit.percentUsed)}. ` +
34 (level === 95 ? 'Stop launching new work.' : 'Finish the current wave; start no more.') +
35 resetsIn(limit.resetsAt, now)
36