cc-peak-hours 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,232 @@
1
+ # claude-peak-hours — Design Spec
2
+
3
+ ## Overview
4
+
5
+ Claude Code statusline plugin that permanently displays whether the user is in **peak** or **off-peak** hours, with countdown to next transition. Inspired by [isclaude-2x](https://github.com/Adiazgallici/isclaude-2x), adapted for permanent use (not tied to a temporary promotion).
6
+
7
+ ## Peak Hours Definition
8
+
9
+ - **Peak**: 12:00 – 18:00 UTC, weekdays only (= 8AM–2PM ET / 14h–20h CET)
10
+ - **Off-peak**: all other times (evenings, nights, weekends)
11
+ - Defined in UTC to avoid DST ambiguity — converted to local time for display
12
+
13
+ ## Project Structure
14
+
15
+ ```
16
+ claude-peak-hours/
17
+ ├── bin/
18
+ │ ├── statusline.sh # Main statusline script (bash)
19
+ │ └── install.js # Node installer with backup/uninstall
20
+ ├── peak-hours.json # Remote config (peak hours definition)
21
+ ├── package.json # npm metadata (npx claude-peak-hours)
22
+ ├── LICENSE # MIT
23
+ └── README.md
24
+ ```
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npx claude-peak-hours # minimal mode, auto time format & language
30
+ npx claude-peak-hours --full # dashboard mode
31
+ npx claude-peak-hours --24h # force 24h format
32
+ npx claude-peak-hours --lang fr # force French
33
+ npx claude-peak-hours --full --24h --lang fr # combined
34
+ npx claude-peak-hours --uninstall # restore previous statusline
35
+ ```
36
+
37
+ ### Installer Behavior (install.js)
38
+
39
+ 1. Check dependencies (`jq`, `curl`)
40
+ 2. Backup existing statusline to `~/.claude/statusline.sh.bak`
41
+ 3. Copy `statusline.sh` to `~/.claude/statusline.sh`
42
+ 4. Update `~/.claude/settings.json` with `statusLine` config
43
+ 5. Print summary with chosen mode
44
+
45
+ ## Display Modes
46
+
47
+ ### Minimal (default) — single line
48
+
49
+ **During peak (weekday 8AM-2PM ET):**
50
+ ```
51
+ Opus 4.6 │ 39% │ 🔴 Peak ~ Off-peak 20:00 (2h15m)
52
+ ```
53
+
54
+ **During off-peak:**
55
+ ```
56
+ Opus 4.6 │ 39% │ ⚡ Off-peak ~ Peak 14:00 (5h12m)
57
+ ```
58
+
59
+ **Weekend:**
60
+ ```
61
+ Opus 4.6 │ 39% │ ⚡ Off-peak ~ Peak lun. 14:00 (1d8h)
62
+ ```
63
+
64
+ ### Full (`--full`) — multi-line dashboard
65
+
66
+ Adds below the minimal line:
67
+
68
+ - **Timeline bar**: 48 chars (2 per hour), green = off-peak, yellow = peak, `●` = now
69
+ - **Rate limits**: session 5h (% + reset time) and weekly (% + reset date) via OAuth API
70
+
71
+ **Example off-peak weekday:**
72
+ ```
73
+ Opus 4.6 │ 39% │ ⚡ Off-peak ~ Peak 14:00 (5h12m)
74
+
75
+ today ━━━━━━━━━━━━━━━━━━━━━━━━━━━━●━━━━━━━━━━━━━━━━━━━━ ━ off-peak ━ peak 14:00-20:00 ● now
76
+ current ●●●●○○○○○○ 42% ⟳ 22:00 │ weekly ●●●○○○○○○○ 31% ⟳ mar 20
77
+ ```
78
+
79
+ **Example weekend:**
80
+ ```
81
+ Opus 4.6 │ 12% │ ⚡ Off-peak ~ Peak lun. 14:00 (1d14h)
82
+
83
+ today ━━━━━━━━━━━━━━━━━━━━━━━━━━━━●━━━━━━━━━━━━━━━━━━━━ ━ off-peak all day ● now
84
+ current ●○○○○○○○○○ 5% ⟳ 20:00 │ weekly ●●○○○○○○○○ 15% ⟳ mar 22
85
+ ```
86
+
87
+ ## Time Format
88
+
89
+ - `--24h` flag: force 24h format (`14:00`, `20:00`)
90
+ - `--12h` flag: force 12h format (`2:00pm`, `8:00pm`)
91
+ - No flag: auto-detect from locale:
92
+ - `fr_*`, `de_*`, `es_*`, `pt_*`, `it_*`, `ja_*`, `zh_*`, `ko_*`, `ru_*` → 24h
93
+ - `en_US`, `en_*` → 12h
94
+ - Fallback → 12h
95
+
96
+ ## Remote Config (peak-hours.json)
97
+
98
+ Peak hours are defined in a JSON file hosted on the project's GitHub repo. This allows updating peak hours for all users without requiring a plugin update.
99
+
100
+ ### File format
101
+
102
+ ```json
103
+ {
104
+ "version": 2,
105
+ "peak_windows": [
106
+ { "days": [1, 2, 3, 4, 5], "start_utc": 12, "end_utc": 18 }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ - All times are in **UTC** — no timezone field needed, no DST ambiguity
112
+ - `peak_windows`: array of peak windows, each with:
113
+ - `days`: ISO weekdays (1=Monday, 7=Sunday) — evaluated in UTC
114
+ - `start_utc` / `end_utc`: peak window boundaries (24h integers, UTC)
115
+ - `version`: schema version for future-proofing
116
+ - The script converts UTC boundaries to local time for display
117
+
118
+ Current peak hours (8AM-2PM ET) = **12:00-18:00 UTC**.
119
+
120
+ This format supports multiple peak windows:
121
+
122
+ ```json
123
+ {
124
+ "version": 2,
125
+ "peak_windows": [
126
+ { "days": [1, 2, 3, 4, 5], "start_utc": 12, "end_utc": 18 },
127
+ { "days": [1, 2, 3, 4, 5], "start_utc": 22, "end_utc": 2 }
128
+ ]
129
+ }
130
+ ```
131
+
132
+ Note: `end_utc` < `start_utc` means the window crosses midnight UTC.
133
+
134
+ ### Fetch & cache strategy
135
+
136
+ - **URL**: raw GitHub URL of `peak-hours.json` from the repo's `main` branch
137
+ - **Cache**: `/tmp/claude/peak-hours-config.json`, TTL = **1 hour**
138
+ - **Fallback**: if fetch fails (no network, GitHub down, timeout), use cached file; if no cache exists, use hardcoded defaults matching the current config above
139
+ - **Timeout**: `curl --max-time 3` to avoid slowing down the statusline
140
+ - The fetch happens at the same time as stdin parsing — no extra latency in the common (cached) case
141
+
142
+ ## Script Logic (statusline.sh)
143
+
144
+ ### Main Flow
145
+
146
+ 1. **Read stdin** — parse JSON (model, context window, usage tokens)
147
+ 1. **Load peak hours config** — from cache or remote, fallback to hardcoded defaults
148
+ 2. **Calculate context %** — color by threshold:
149
+ - Green: < 50%
150
+ - Orange: 50-70%
151
+ - Yellow: 70-90%
152
+ - Red: > 90%
153
+ 3. **Determine peak/off-peak status** (using loaded config):
154
+ - Get current hour and weekday in **UTC** (`TZ=UTC`)
155
+ - Iterate over `peak_windows[]`: check if current UTC day is in `days` AND current UTC hour is within `start_utc`–`end_utc` (handling midnight crossing)
156
+ - If any window matches → peak; otherwise → off-peak
157
+ 4. **Calculate countdown** to next transition:
158
+ - If peak → seconds until `end_utc` of the current matching window
159
+ - If off-peak → find the nearest upcoming window start (today or next matching day in UTC), calculate seconds until it
160
+ 5. **Convert to local time** for display (all user-facing times are local)
161
+ 5. **Convert to local time** for display
162
+ 6. **Apply time format** (12h/24h per flag or locale detection)
163
+
164
+ ### Full Mode Additions
165
+
166
+ 7. **Timeline bar** — 48 chars, 2 per hour, colored by peak/off-peak, dot for current position
167
+ 8. **OAuth rate limits fetch** (cached 60s in `/tmp/claude/`):
168
+ - Token from `$CLAUDE_CODE_OAUTH_TOKEN`, macOS Keychain, or `~/.claude/.credentials.json`
169
+ - Endpoint: `https://api.anthropic.com/api/oauth/usage`
170
+ - Display: session 5h (% + reset time) and weekly (% + reset date)
171
+
172
+ ### Colors
173
+
174
+ - **Off-peak**: green `⚡` + green text
175
+ - **Peak**: red `🔴` + red/yellow text
176
+ - **Separator**: dim `│`
177
+ - **Context %**: green/orange/yellow/red gradient
178
+
179
+ ## Dependencies
180
+
181
+ - `jq` — JSON parsing (stdin + OAuth response)
182
+ - `curl` — rate limits API fetch
183
+ - `date` — GNU coreutils (Linux) or BSD date (macOS)
184
+ - Auto-detection via `date --version`
185
+
186
+ ## Compatibility
187
+
188
+ - Linux (GNU coreutils)
189
+ - macOS (BSD date)
190
+
191
+ ## Localization (i18n)
192
+
193
+ Two languages supported: English (default) and French.
194
+
195
+ ### Detection
196
+
197
+ - `--lang fr` flag: force French
198
+ - `--lang en` flag: force English
199
+ - No flag: auto-detect from `LANG`/`LC_TIME`:
200
+ - `fr_*` → French
201
+ - Everything else → English
202
+
203
+ ### Translated strings
204
+
205
+ | Key | English | French |
206
+ |-----|---------|--------|
207
+ | Peak label | `Peak` | `Pointe` |
208
+ | Off-peak label | `Off-peak` | `Hors pointe` |
209
+ | "all day" | `all day` | `toute la journée` |
210
+ | "today" | `today` | `auj.` |
211
+ | "now" | `now` | `maint.` |
212
+ | "current" | `current` | `session` |
213
+ | "weekly" | `weekly` | `hebdo` |
214
+ | Weekday abbrevs | `Mon.` `Tue.` ... | `lun.` `mar.` ... |
215
+ | Month abbrevs | `jan` `feb` ... | `jan` `fév` ... |
216
+
217
+ ### Implementation
218
+
219
+ Strings defined as bash variables at the top of `statusline.sh`, set once based on the detected/forced language. No external translation files.
220
+
221
+ ## Differences from isclaude-2x
222
+
223
+ | Aspect | isclaude-2x | claude-peak-hours |
224
+ |--------|-------------|-------------------|
225
+ | Purpose | Temporary 2x promo (Mar 13-28) | Permanent peak hours indicator |
226
+ | Labels | "2x" / "1x" | "Off-peak" / "Peak" |
227
+ | Promo countdown | Days remaining in promo | None |
228
+ | Snowflake `❄` | Weekly frozen during 2x | Removed |
229
+ | Time format | 12h only | 12h/24h with auto-detect |
230
+ | Language | English only | English + French with auto-detect |
231
+ | Config | Hardcoded | Remote JSON on GitHub, cached 1h, hardcoded fallback |
232
+ | Lifespan | Expires after promo | Permanent |
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "cc-peak-hours",
3
+ "version": "1.0.0",
4
+ "description": "Claude Code statusline showing peak/off-peak hours with countdown",
5
+ "bin": {
6
+ "cc-peak-hours": "bin/install.js"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/nickywan/claude-peak-hours.git"
11
+ },
12
+ "keywords": [
13
+ "claude",
14
+ "claude-code",
15
+ "statusline",
16
+ "peak-hours",
17
+ "cli"
18
+ ],
19
+ "license": "MIT",
20
+ "author": "nickywan"
21
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 2,
3
+ "peak_windows": [
4
+ { "days": [1, 2, 3, 4, 5], "start_utc": 12, "end_utc": 18 }
5
+ ]
6
+ }