Skip to content

hazzap123/balance

Repository files navigation

Balance

Time and usage boundaries for Claude Code. Stay balanced.

Balance is a Claude Code hook that enforces:

  1. Time windows — only allows interaction during configured hours
  2. Daily usage caps — tracks active minutes, blocks when limit hit
  3. Extensions — temporary overrides when you need more time
  4. HAL 9000 mode — escalating friction when you keep extending (with 2001: A Space Odyssey quotes)
  5. Status line — at-a-glance usage, window, and warning display in the Claude Code terminal footer

Why?

Claude Code is powerful. Too powerful to leave running at 2am when you should be sleeping. Balance gives you guardrails you set when you're thinking clearly, with just enough friction to make you pause before overriding them.

Quick Start

1. Clone the repo

git clone https://github.com/hazzap123/balance.git ~/github/balance

2. Run the setup wizard

Inside Claude Code:

/balance-setup

This checks what's installed, copies missing files, registers the hook in settings.json, and creates a starter balance.json. Handles first-time install and re-installs.

Or install manually:

cp ~/github/balance/balance_hook.py ~/.claude/hooks/
cp ~/github/balance/balance_utils.py ~/.claude/hooks/
cp ~/github/balance/balance-extend ~/.claude/hooks/
cp ~/github/balance/balance.json.example ~/.claude/hooks/balance.json
chmod +x ~/.claude/hooks/balance-extend

3. Install slash commands (optional)

cp ~/github/balance/commands/*.md ~/.claude/commands/

4. Install the status line (optional)

cp ~/github/balance/statusline.sh ~/.claude/statusline-command.sh
chmod +x ~/.claude/statusline-command.sh

Add to your .claude/settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline-command.sh",
    "padding": 2
  }
}

This adds a live Balance readout to the bottom of your Claude Code terminal. See Status Line for details.

5. Make the CLI accessible (optional)

ln -s ~/.claude/hooks/balance-extend ~/bin/balance-extend

Only needed if you want to run balance-extend from a terminal. Skip if you only use it from the block message inside Claude Code.

6. Configure the hook in Claude Code settings

Add to your .claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "type": "command",
        "command": "python3 ~/.claude/hooks/balance_hook.py"
      }
    ]
  }
}

7. Customise your schedule

Edit ~/.claude/hooks/balance.json:

{
  "enabled": true,
  "timezone": "Europe/London",
  "schedule": {
    "weekday": {
      "days": [1, 2, 3, 4, 5],
      "windows": [{"start": "08:00", "end": "18:00"}],
      "daily_limit_minutes": 240
    },
    "saturday": {
      "days": [6],
      "windows": [
        {"start": "08:00", "end": "10:30"},
        {"start": "16:00", "end": "19:00"}
      ],
      "daily_limit_minutes": 240
    }
  }
}

How It Works

Time Windows

Each schedule block defines which days it covers and one or more time windows. Outside these windows, prompts are blocked with a message showing the next available time.

Usage Tracking

Every prompt records a timestamp. Active minutes = count of distinct clock-minutes with at least one prompt. This means rapid-fire prompts in the same minute only count once.

Usage logs are stored in .usage/ alongside the hook and auto-cleaned after 7 days.

Extensions

When blocked, you're offered extension options directly in the Claude Code block message. You can also run them from a terminal if balance-extend is on your PATH.

See Commands Reference for the full list.

HAL 9000 Mode

From your 2nd extension onwards, HAL 9000 starts resisting. Each additional extension escalates:

  • Stage 0 (2nd extension): "I'm sorry, Dave. I'm afraid I can't do that." — Type I'm sorry HAL to override
  • Stage 1 (3rd extension): "I honestly think you ought to sit down calmly, take a stress pill..." — Type open the pod bay doors
  • Stage 2 (4th+ extension): "Look Dave, I can see you're really upset..." — Type my mind is going I can feel it

It's not about preventing access. It's about making you pause and think about whether you really need more time.

Warnings

Approaching limits trigger context warnings (shown to Claude, not blocking):

  • Window closing within 15 minutes
  • Daily cap within 30 minutes of being hit

Overrides

For emergencies, full bypass via:

  • Environment variable: BALANCE_OVERRIDE=1
  • Override file: ~/.balance_override (managed by balance-extend)

Status Line

The optional status line script adds live Balance information to the Claude Code terminal footer. It shows model, context window, and Balance state side by side.

What it displays

State Example
Normal (in window) Bal: 45/240m [08:00-18:00]
Approaching daily cap Bal: 215/240m [08:00-18:00] ! 25m to cap
Cap reached Bal: 240/240m [08:00-18:00] !! CAP REACHED !!
Window ending soon Bal: 100/240m [08:00-18:00] ! window ends in 12m
Extended session active Bal: 100/240m ext:1 [08:00-18:00] +Quick 15-min session (12m left)
Extended outside window Bal: 200/240m ext:2 +Quick 15-min session (8m left)
Between windows Bal: 100/240m (next: 16:00)
After last window Bal: 200/240m (done for today)
No schedule (e.g. Sunday) Bal: no schedule today
Sunday with override Bal: 15m ext:1 +Quick 15-min session (10m left)

Warnings use colour: yellow for approaching limits, red for cap reached, cyan for active extensions, grey for inactive states.

Warning thresholds are read from balance.json (warning_minutes_before_end and warning_minutes_before_cap).

Requirements

  • jq (for parsing JSON config and session metadata)
  • bc (for token count formatting)

Both are standard on macOS and most Linux distributions.

Locked Out? DON'T PANIC!!!

Option 1 — Use a slash command. /balance-configure and /balance-status are always allowed through even when blocked. Type one directly in Claude Code.

Option 2 — Run balance-extend from a terminal. The block message shows the full path — copy and run it:

~/.claude/hooks/balance-extend quick
# or interactive:
~/.claude/hooks/balance-extend

Option 3 — Don't Panic mode. If everything is broken, run this from any terminal:

python3 -c "
import os, json
from zoneinfo import ZoneInfo
from datetime import datetime, timedelta
tz = ZoneInfo('Europe/London')
expires = datetime.now(tz) + timedelta(minutes=15)
path = os.path.expanduser('~/.balance_override')
open(path, 'w').write(json.dumps({'expires_at': expires.strftime('%Y-%m-%dT%H:%M:%S')}))
print('Unlocked until', expires.strftime('%H:%M'))
"

Grants 15 minutes. Adjust timedelta(minutes=15) as needed.

Commands Reference

Slash commands (inside Claude Code)

Command What it does
/balance-setup First-time install wizard — checks what's installed, copies files, registers the hook in settings.json, creates starter balance.json. Safe to re-run.
/balance-configure Show active config in plain English, then apply changes interactively — time windows, daily limits, timezone, extensions.
/balance-status Show today's usage, current window state, extensions used, and any active override.

These slash commands are always allowed through even when you're blocked.

CLI (balance-extend)

A terminal command — not a Claude Code slash command. Run it from a terminal when blocked. The block message shows the full path to copy-paste.

Command What it does
balance-extend Interactive mode — detects why you're blocked, lists available extensions, lets you choose.
balance-extend quick Grant a short burst outside your normal window (configurable, default 15 min).
balance-extend more Add time when your daily cap is hit (configurable, default 15 min).
balance-extend status Show current time, window state, usage bar, and extension counts.
balance-extend clear Remove the active override immediately.

Extension types (quick, more) are defined in balance.json and can be renamed, resized, or extended with additional types.

Configuration Reference

Key Type Default Description
enabled bool true Master switch
timezone string "Europe/London" IANA timezone for all time calculations
schedule object weekday 08-18 Named schedule blocks (see below)
extensions object quick + more Extension types (see below)
override object Override env var and file path
warning_minutes_before_end int 15 Warn when window closes within N minutes
warning_minutes_before_cap int 30 Warn when daily cap within N minutes

Schedule Block

{
  "days": [1, 2, 3, 4, 5],
  "windows": [{"start": "08:00", "end": "18:00"}],
  "daily_limit_minutes": 240
}
  • days: ISO weekdays (1=Monday, 7=Sunday)
  • windows: Array of {start, end} in HH:MM format
  • daily_limit_minutes: Optional cap on active minutes

Extension Type

{
  "minutes": 15,
  "max_per_day": 2,
  "label": "Quick 15-min session"
}

Testing

cd tests
python3 test_balance.py

License

MIT

About

Time and usage boundaries for Claude Code. Stay balanced.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors