claude-resume-hub 1.4.2 → 1.5.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.
package/README.md CHANGED
@@ -1,137 +1,122 @@
1
- <h1 align="center">⏰ claude-resume-hub</h1>
1
+ <div align="center">
2
2
 
3
- <p align="center">
4
- <b>Stop babysitting Claude Code usage limits.</b><br>
5
- Hit a limit at 2am? <b>Wake up to finished work.</b>
6
- </p>
3
+ <img src="https://raw.githubusercontent.com/IbrahimKalemci/claude-resume-hub/main/docs/icon.png" width="84" alt="claude-resume-hub logo">
4
+
5
+ # claude-resume-hub
6
+
7
+ ### Hit a Claude Code limit at 2am? **Wake up to finished work.**
8
+
9
+ Auto-continues your Claude Code session the moment a usage/session limit resets — as a **tray desktop app** or a one-line **CLI**. Cross-platform, zero-dependency, and it **never touches your tokens**.
7
10
 
8
- <p align="center">
9
- <a href="https://www.npmjs.com/package/claude-resume-hub"><img src="https://img.shields.io/npm/v/claude-resume-hub?color=c96442" alt="npm"></a>
11
+ <p>
12
+ <a href="https://www.npmjs.com/package/claude-resume-hub"><img src="https://img.shields.io/npm/v/claude-resume-hub?color=c96442&label=npm" alt="npm"></a>
10
13
  <a href="https://github.com/IbrahimKalemci/claude-resume-hub/actions/workflows/ci.yml"><img src="https://github.com/IbrahimKalemci/claude-resume-hub/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
11
- <img src="https://img.shields.io/badge/node-%3E%3D16-3fb950" alt="node >= 16">
14
+ <a href="https://github.com/IbrahimKalemci/claude-resume-hub/releases/latest"><img src="https://img.shields.io/badge/download-.exe-3fb950" alt="download"></a>
12
15
  <img src="https://img.shields.io/badge/Windows%20%7C%20macOS%20%7C%20Linux-8b93a7" alt="platforms">
13
16
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT"></a>
14
17
  </p>
15
18
 
16
- <p align="center">
17
- <img src="https://raw.githubusercontent.com/IbrahimKalemci/claude-resume-hub/main/docs/demo.svg" alt="countdown reaching zero, then sending continue to Claude to resume the task" width="700">
18
- </p>
19
+ <img src="https://raw.githubusercontent.com/IbrahimKalemci/claude-resume-hub/main/docs/app-demo.svg" width="380" alt="claude-resume-hub desktop app: finds your session, waits out the limit, counts down, resumes automatically">
20
+
21
+ **🇬🇧 [English](#-english) · 🇹🇷 [Türkçe](#-türkçe)**
19
22
 
20
- <p align="center"><b>🇬🇧 <a href="#-english">English</a> &nbsp;·&nbsp; 🇹🇷 <a href="#-türkçe">Türkçe</a></b></p>
23
+ </div>
21
24
 
22
25
  ---
23
26
 
24
27
  ## 🇬🇧 English
25
28
 
26
- Claude Code hits your limit and just… **stops** — until you come back and type `continue`.
27
- **claude-resume-hub** waits for the exact reset time and continues **for you**, then pings your desktop when it's rolling again. One command, zero setup, every OS.
29
+ When Claude Code hits your limit it prints `Claude AI usage limit reached|…` and **stops** — you have to come back and type `continue`. **claude-resume-hub** finds your session, waits for the exact reset time, and continues for you. Then it pings you.
28
30
 
29
- ### Install & run3 steps
31
+ ### 🖥️ Desktop appdownload & run
30
32
 
31
- **1.** Make sure Claude Code works (open a terminal, type `claude --version`). Don't have it? [Get it here](https://docs.claude.com/en/docs/claude-code).
33
+ The tray app finds your last session automatically, shows a live countdown, and resumes on its own. No terminal.
32
34
 
33
- **2.** Go to your project folder and run:
35
+ **[⬇ Download claude-resume-hub.exe](https://github.com/IbrahimKalemci/claude-resume-hub/releases/latest)** (Windows · macOS · Linux)
34
36
 
35
- ```bash
36
- npx claude-resume-hub --web
37
- ```
38
-
39
- **3.** A dashboard opens in your browser — click **“Enable alerts”**, then walk away. 🎉
40
-
41
- That's it. No install, no config. It works on **Windows, macOS and Linux**.
37
+ 1. Download and run it — it lives in your **system tray**.
38
+ 2. It **auto-finds** the session you were last working in.
39
+ 3. Pick **Continue** (or type a task), press **Start**, and walk away.
40
+ 4. Limit hit → it counts down → resumes the moment the window reopens → you get a notification. 🎉
42
41
 
43
- ### Why you'll love it
42
+ > It wraps Claude Code, so the `claude` CLI must be on your PATH.
44
43
 
45
- - ⏱️ **Exact countdown** — reads the real reset time, not a guess
46
- - 🔔 **Desktop alerts** — close the terminal, go live your life
47
- - 🖥️ **Beautiful live dashboard** — countdown, status, and a streaming log
48
- - 🪶 **Zero dependencies**, 100% local — nothing leaves your machine
49
-
50
- ### Handy commands
44
+ ### ⌨️ Prefer the terminal? One line, no install
51
45
 
52
46
  ```bash
53
- npx claude-resume-hub # no dashboard, just auto-resume
54
- npx claude-resume-hub --web # with dashboard + alerts
55
- npx claude-resume-hub --tray # background system-tray icon (Windows)
56
- npx claude-resume-hub -t "run all tests and fix failures" # start a NEW session (not resume)
57
- npx claude-resume-hub --list # list this project's sessions (+ their ids)
58
- npx claude-resume-hub --web --smart # context-aware resume (picks up your last step)
59
- npx claude-resume-hub -s <id> # resume a specific session
60
- npx claude-resume-hub --help # all options
47
+ npx claude-resume-hub --web # auto-resume + a live dashboard
48
+ npx claude-resume-hub # ...or headless, no dashboard
61
49
  ```
62
50
 
63
- Prefer a permanent command? `npm i -g claude-resume-hub` gives you `crh --web`.
64
- **Always up to date:** `npx claude-resume-hub@latest` pulls the newest version automatically.
65
- **No Node?** [**⬇ Download a standalone binary**](https://github.com/IbrahimKalemci/claude-resume-hub/releases/latest) (Windows `.exe` / macOS / Linux) — runs without Node installed. It still needs the `claude` CLI on your PATH.
51
+ Handy flags: `--list` (see your sessions) · `--session <id>` (resume a specific one) · `--smart` (context-aware resume) · `--tray` (Windows tray) · `--help`.
66
52
 
67
- ---
53
+ ### ✨ Features
68
54
 
69
- ## 🇹🇷 Türkçe
55
+ - ⏱️ **Exact countdown** — reads the real reset timestamp, not a guess
56
+ - 🎯 **Finds the right session** and pins it (no more resuming the wrong one)
57
+ - 📚 **Multi-project queue** — several projects resume in order on one reset clock
58
+ - 🔔 **Phone/chat alerts** — optional webhook or Telegram ping on reset / done / error
59
+ - 📺 **Live output** — watch what Claude did, even while you were away
60
+ - ⬆️ **Update banner** — tells you when a newer version ships
61
+ - 🪶 **Zero dependencies**, ~one small package · 🔒 **never reads your tokens**
70
62
 
71
- Claude Code limitine takılınca **durur** — sen gelip `continue` yazana kadar bekler.
72
- **claude-resume-hub** limitin tam açılma saatini bekleyip **senin yerine** devam eder, iş yeniden başlayınca da masaüstüne bildirim atar. Tek komut, sıfır ayar, her işletim sistemi.
63
+ ### 🔒 Security & privacy
73
64
 
74
- ### Kurulum & çalıştırma 3 adım
65
+ It **never** reads or stores your Claude credentials (`~/.claude/.credentials.json` / keychain). It only reads your own conversation transcripts under `~/.claude/projects`, locally. No telemetry; the dashboard is localhost-only; release binaries are built by CI from source on clean machines, so a downloaded `.exe` carries no one's data.
75
66
 
76
- **1.** Claude Code çalışıyor mu bak (terminal aç, `claude --version` yaz). Yoksa [buradan kur](https://docs.claude.com/en/docs/claude-code).
67
+ ---
77
68
 
78
- **2.** Projenin klasörüne gir ve şunu çalıştır:
69
+ ## 🇹🇷 Türkçe
79
70
 
80
- ```bash
81
- npx claude-resume-hub --web
82
- ```
71
+ Claude Code limitine takılınca `Claude AI usage limit reached|…` yazıp **durur** — gelip `continue` yazman gerekir. **claude-resume-hub** oturumunu bulur, tam açılma saatini bekler ve senin yerine devam eder. Sonra da sana haber verir.
72
+
73
+ ### 🖥️ Masaüstü uygulaması — indir çalıştır
83
74
 
84
- **3.** Tarayıcıda bir panel açılır **“Enable alerts”**e bas ve arkana yaslan. 🎉
75
+ Tepside duran uygulama son oturumunu otomatik bulur, canlı geri sayım gösterir, kendi devam eder. Terminal yok.
85
76
 
86
- Hepsi bu. Kurulum yok, ayar yok. **Windows, macOS ve Linux**'ta çalışır.
77
+ **[⬇ claude-resume-hub.exe indir](https://github.com/IbrahimKalemci/claude-resume-hub/releases/latest)** (Windows · macOS · Linux)
87
78
 
88
- ### Neden bayılacaksın
79
+ 1. İndir ve çalıştır — **sistem tepsisinde** yaşar.
80
+ 2. Son çalıştığın oturumu **kendi bulur**.
81
+ 3. **Continue** seç (ya da task yaz), **Start**'a bas, işine bak.
82
+ 4. Limit dolunca → geri sayar → açılır açılmaz devam eder → bildirim alırsın. 🎉
89
83
 
90
- - ⏱️ **Net geri sayım** tahmin değil, gerçek reset saatini okur
91
- - 🔔 **Masaüstü bildirimi** — terminali kapat, hayatına dön
92
- - 🖥️ **Şık canlı panel** — geri sayım, durum ve canlı log
93
- - 🪶 **Sıfır bağımlılık**, %100 yerel — hiçbir veri makineden çıkmaz
84
+ > Claude Code'u sarmalar; `claude` CLI'ın PATH'te olması gerekir.
94
85
 
95
- ### İşine yarayacak komutlar
86
+ ### ⌨️ Terminal mi? Tek satır, kurulum yok
96
87
 
97
88
  ```bash
98
- npx claude-resume-hub # panelsiz, sadece otomatik devam
99
- npx claude-resume-hub --web # panel + bildirim
100
- npx claude-resume-hub --tray # arka planda sistem tepsisi ikonu (Windows)
101
- npx claude-resume-hub -t "tüm testleri çalıştır ve hataları düzelt" # YENİ session başlat (devam DEĞİL)
102
- npx claude-resume-hub --list # bu projenin session'larını listele (+ id'leri)
103
- npx claude-resume-hub --web --smart # bağlam-farkında devam (kaldığın adımdan sürer)
104
- npx claude-resume-hub -s <id> # belirli bir session'ı devam ettir
105
- npx claude-resume-hub --help # tüm seçenekler
89
+ npx claude-resume-hub --web # otomatik devam + canlı panel
90
+ npx claude-resume-hub # ...ya da panelsiz
106
91
  ```
107
92
 
108
- Kalıcı komut ister misin? `npm i -g claude-resume-hub` sana `crh --web` verir.
109
- **Hep güncel:** `npx claude-resume-hub@latest` otomatik en yeni sürümü çeker.
110
- **Node yok mu?** [**⬇ Standalone binary indir**](https://github.com/IbrahimKalemci/claude-resume-hub/releases/latest) (Windows `.exe` / macOS / Linux) — Node kurulu olmadan çalışır. Yine de `claude` CLI'ın PATH'te olması gerekir.
93
+ İşe yarayan bayraklar: `--list` (oturumları gör) · `--session <id>` (belirli oturum) · `--smart` (bağlam-farkında devam) · `--tray` (Windows tepsi) · `--help`.
111
94
 
112
- ---
95
+ ### ✨ Özellikler
113
96
 
114
- ## 🔒 Security & privacy
97
+ - ⏱️ **Net geri sayım** — tahmin değil, gerçek reset zamanını okur
98
+ - 🎯 **Doğru oturumu bulur** ve sabitler (yanlış oturumu devam ettirme derdi yok)
99
+ - 📚 **Çoklu proje kuyruğu** — birden fazla proje tek reset saatinde sırayla devam eder
100
+ - 🔔 **Telefon/sohbet bildirimi** — webhook ya da Telegram ile reset/bitiş/hata bildirimi
101
+ - 📺 **Canlı çıktı** — Claude ne yaptı, sen yokken bile gör
102
+ - ⬆️ **Güncelleme banner'ı** — yeni sürüm çıkınca söyler
103
+ - 🪶 **Sıfır bağımlılık** · 🔒 **token'ına asla dokunmaz**
115
104
 
116
- - **It never touches your tokens.** The tool does **not** read or store your Claude credentials (`~/.claude/.credentials.json` / the OS keychain). It only reads conversation transcripts under `~/.claude/projects` — on your own machine, at runtime.
117
- - **Nothing leaves your machine.** No telemetry, no network calls of its own. The `--web` dashboard is localhost-only.
118
- - **The binary contains only code — no sessions, no tokens.** Release executables are built by GitHub Actions from source on clean runners, so a downloaded `.exe` can't expose anyone's data (there's none embedded). Each user's run reads only *their own* local `~/.claude`.
105
+ ### 🔒 Güvenlik & gizlilik
119
106
 
120
- > **Güvenlik:** Token/kimlik bilgisi *hiç* okunmaz veya saklanmaz sadece kendi makinendeki konuşma metinleri okunur. Panel yalnızca localhost. İndirilen `.exe` yalnızca kod içerir (session/token gömülü değil), CI'da kaynaktan temiz sunucularda derlenir.
107
+ Claude kimlik bilgilerini (`~/.claude/.credentials.json` / keychain) **asla** okumaz/saklamaz. Sadece kendi makinendeki `~/.claude/projects` konuşma metinlerini okur. Telemetri yok; panel yalnızca localhost; release binary'leri CI'da temiz sunucularda kaynaktan derlenir indirilen `.exe` kimsenin verisini taşımaz.
121
108
 
122
- ## 🛠️ From source · Kaynaktan
109
+ ---
123
110
 
124
- Prefer to run it straight from the repo (no npm)? / npm'siz, doğrudan repodan mı çalıştırmak istiyorsun?
111
+ ## 🛠️ From source · Kaynaktan
125
112
 
126
113
  ```bash
127
114
  git clone https://github.com/IbrahimKalemci/claude-resume-hub.git
128
115
  cd claude-resume-hub
129
- node bin/cli.js --web
116
+ npm start # CLI
117
+ npm run app:start # desktop app (needs: npm install)
130
118
  ```
131
119
 
132
- ---
133
-
134
- <p align="center">
135
- Made for everyone who's tired of waiting on the reset clock.<br>
136
- <b>MIT</b> © Ibrahim Kalemci · <a href="CHANGELOG.md">Changelog</a>
137
- </p>
120
+ <div align="center">
121
+ <sub>Made for everyone tired of waiting on the reset clock · <b>MIT</b> © Ibrahim Kalemci · <a href="CHANGELOG.md">Changelog</a></sub>
122
+ </div>
package/app/icon.js ADDED
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Tiny dependency-free PNG generator, used to draw the tray icon at runtime so
5
+ * the repo needs no binary image assets. Produces an anti-aliased filled circle
6
+ * (the app's "dot" mark) in a given colour.
7
+ */
8
+
9
+ const zlib = require("zlib");
10
+
11
+ let CRC_TABLE = null;
12
+ function crcTable() {
13
+ if (CRC_TABLE) return CRC_TABLE;
14
+ CRC_TABLE = new Int32Array(256);
15
+ for (let n = 0; n < 256; n++) {
16
+ let c = n;
17
+ for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
18
+ CRC_TABLE[n] = c;
19
+ }
20
+ return CRC_TABLE;
21
+ }
22
+
23
+ function crc32(buf) {
24
+ const t = crcTable();
25
+ let c = 0xffffffff;
26
+ for (let i = 0; i < buf.length; i++) c = t[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
27
+ return (c ^ 0xffffffff) >>> 0;
28
+ }
29
+
30
+ function chunk(type, data) {
31
+ const len = Buffer.alloc(4);
32
+ len.writeUInt32BE(data.length, 0);
33
+ const body = Buffer.concat([Buffer.from(type, "ascii"), data]);
34
+ const crc = Buffer.alloc(4);
35
+ crc.writeUInt32BE(crc32(body), 0);
36
+ return Buffer.concat([len, body, crc]);
37
+ }
38
+
39
+ /** Build an RGBA PNG of `size`x`size` using pixel(x, y) -> [r,g,b,a]. */
40
+ function makePNG(size, pixel) {
41
+ const raw = Buffer.alloc(size * (size * 4 + 1));
42
+ let p = 0;
43
+ for (let y = 0; y < size; y++) {
44
+ raw[p++] = 0; // filter: none
45
+ for (let x = 0; x < size; x++) {
46
+ const [r, g, b, a] = pixel(x, y);
47
+ raw[p++] = r; raw[p++] = g; raw[p++] = b; raw[p++] = a;
48
+ }
49
+ }
50
+ const ihdr = Buffer.alloc(13);
51
+ ihdr.writeUInt32BE(size, 0);
52
+ ihdr.writeUInt32BE(size, 4);
53
+ ihdr[8] = 8; // bit depth
54
+ ihdr[9] = 6; // colour type: RGBA
55
+ return Buffer.concat([
56
+ Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
57
+ chunk("IHDR", ihdr),
58
+ chunk("IDAT", zlib.deflateSync(raw, { level: 9 })),
59
+ chunk("IEND", Buffer.alloc(0)),
60
+ ]);
61
+ }
62
+
63
+ function hexToRgb(hex) {
64
+ const h = hex.replace("#", "");
65
+ return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
66
+ }
67
+
68
+ /** A filled, softly anti-aliased circle — the tray mark. */
69
+ function dotIcon(hex, size = 16) {
70
+ const [r, g, b] = hexToRgb(hex);
71
+ const c = (size - 1) / 2;
72
+ const radius = size * 0.42;
73
+ return makePNG(size, (x, y) => {
74
+ const d = Math.hypot(x - c, y - c);
75
+ // 1px feathered edge
76
+ const a = Math.max(0, Math.min(1, radius - d + 0.5));
77
+ return [r, g, b, Math.round(a * 255)];
78
+ });
79
+ }
80
+
81
+ function clamp(v, a, b) { return v < a ? a : v > b ? b : v; }
82
+ function segDist(px, py, ax, ay, bx, by) {
83
+ const dx = bx - ax, dy = by - ay;
84
+ const t = clamp(((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy || 1), 0, 1);
85
+ return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
86
+ }
87
+
88
+ /**
89
+ * The app / window / exe icon: a terracotta disc with a cream clock glyph
90
+ * (ring + two hands) — matches the header logo's identity. Fully generated,
91
+ * no binary asset.
92
+ */
93
+ function appIcon(size = 256, discHex = "#c96442") {
94
+ const [br, bg, bb] = hexToRgb(discHex); // disc colour (phase-tinted in the tray)
95
+ const [fr, fg, fb] = hexToRgb("#f6efe8"); // cream glyph
96
+ const c = (size - 1) / 2;
97
+ const discR = size * 0.47;
98
+ const ringR = size * 0.27;
99
+ const ringT = size * 0.052;
100
+ const handT = size * 0.05;
101
+
102
+ // hand endpoints (12 o'clock hour hand, ~2 o'clock minute hand)
103
+ const hx = c, hy = c - ringR * 0.60;
104
+ const mx = c + ringR * 0.52, my = c - ringR * 0.34;
105
+
106
+ return makePNG(size, (x, y) => {
107
+ const px = x + 0.5, py = y + 0.5;
108
+ const dc = Math.hypot(px - c, py - c);
109
+ const discA = clamp(discR - dc + 0.5, 0, 1); // disc coverage
110
+ if (discA <= 0) return [0, 0, 0, 0];
111
+
112
+ // glyph coverage: ring OR either hand
113
+ const ringA = clamp(ringT / 2 - Math.abs(dc - ringR) + 0.5, 0, 1);
114
+ const hourA = clamp(handT / 2 - segDist(px, py, c, c, hx, hy) + 0.5, 0, 1);
115
+ const minA = clamp(handT / 2 - segDist(px, py, c, c, mx, my) + 0.5, 0, 1);
116
+ const g = Math.max(ringA, hourA, minA);
117
+
118
+ return [
119
+ Math.round(br + (fr - br) * g),
120
+ Math.round(bg + (fg - bg) * g),
121
+ Math.round(bb + (fb - bb) * g),
122
+ Math.round(discA * 255),
123
+ ];
124
+ });
125
+ }
126
+
127
+ /**
128
+ * Tray mark: the same clock, drawn as a solid glyph in the phase colour on a
129
+ * transparent background. A stroked ring + hands stays legible at 16px, where a
130
+ * filled disc with a cut-out glyph turns to mush.
131
+ */
132
+ function clockIcon(hex, size = 16) {
133
+ const [r, g, b] = hexToRgb(hex);
134
+ const c = (size - 1) / 2;
135
+ const ringR = size * 0.38;
136
+ const ringT = size * 0.13;
137
+ const handT = size * 0.12;
138
+
139
+ const hx = c, hy = c - ringR * 0.58; // hour hand -> 12
140
+ const mx = c + ringR * 0.50, my = c - ringR * 0.32; // minute hand -> ~2
141
+
142
+ return makePNG(size, (x, y) => {
143
+ const px = x + 0.5, py = y + 0.5;
144
+ const dc = Math.hypot(px - c, py - c);
145
+ const ringA = clamp(ringT / 2 - Math.abs(dc - ringR) + 0.5, 0, 1);
146
+ const hourA = clamp(handT / 2 - segDist(px, py, c, c, hx, hy) + 0.5, 0, 1);
147
+ const minA = clamp(handT / 2 - segDist(px, py, c, c, mx, my) + 0.5, 0, 1);
148
+ const a = Math.max(ringA, hourA, minA);
149
+ return [r, g, b, Math.round(a * 255)];
150
+ });
151
+ }
152
+
153
+ /** Wrap a (square) PNG in a single-image .ico container (Vista+ PNG-in-ICO). */
154
+ function makeICO(png) {
155
+ const header = Buffer.alloc(6);
156
+ header.writeUInt16LE(0, 0); // reserved
157
+ header.writeUInt16LE(1, 2); // type: icon
158
+ header.writeUInt16LE(1, 4); // image count
159
+ const entry = Buffer.alloc(16);
160
+ entry[0] = 0; // width (0 => 256)
161
+ entry[1] = 0; // height (0 => 256)
162
+ entry[2] = 0; // palette
163
+ entry[3] = 0; // reserved
164
+ entry.writeUInt16LE(1, 4); // colour planes
165
+ entry.writeUInt16LE(32, 6); // bits per pixel
166
+ entry.writeUInt32LE(png.length, 8); // image size
167
+ entry.writeUInt32LE(22, 12); // offset (6 + 16)
168
+ return Buffer.concat([header, entry, png]);
169
+ }
170
+
171
+ module.exports = { dotIcon, appIcon, clockIcon, makePNG, makeICO };