claude-code-token-saver 0.1.0 → 0.1.3

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
@@ -38,6 +38,7 @@ Cả 3 cách đều **không ghi đè** file đã tồn tại. Toàn bộ nội
38
38
  |----|---------|
39
39
  | `--force` | Ghi đè cả những file đã tồn tại (mặc định: bỏ qua) |
40
40
  | `--no-detect` | Không tự dò lệnh build/test/lint |
41
+ | `--no-usage` | Không thêm status line theo dõi usage 5 giờ (mặc định là **có**) |
41
42
  | `-h`, `--help` | Hiện hướng dẫn |
42
43
 
43
44
  Ví dụ:
@@ -99,6 +100,7 @@ Trong lúc đó các hook tự chạy ngầm: chặn lệnh nguy hiểm, tự fo
99
100
  | `.claude/settings.json` | Hooks + danh sách chặn quyền (chặn đọc `.env`, `secrets/`) — **nên commit** |
100
101
  | `.claude/hooks/guard-bash.sh` | Chặn lệnh shell nguy hiểm (`rm -rf /`, fork bomb, `git push --force`, `chmod -R 777`, pipe script từ mạng vào shell) |
101
102
  | `.claude/hooks/format-file.sh` | Tự động format file sau khi Claude ghi (prettier / ruff / gofmt / rustfmt) |
103
+ | `.claude/hooks/statusline.js` | Vẽ thanh usage 5 giờ ở đáy phiên (chỉ khi bật usage; xem mục bên dưới) |
102
104
  | `.claude/agents/explorer.md` | Subagent điều tra codebase **read-only**, chạy model `haiku` để tiết kiệm token |
103
105
  | `.claude/agents/verifier.md` | Subagent kiểm chứng độc lập — chạy test/build/lint, soi diff, báo PASS/FAIL |
104
106
  | `.claude/commands/*.md` | Các slash command: `/plan`, `/save`, `/verify`, `/wrap` |
@@ -108,6 +110,30 @@ Script tự nhận diện stack (**Node/TS, Python, Go, Rust**) và điền sẵ
108
110
 
109
111
  ---
110
112
 
113
+ ## Theo dõi usage 5 giờ (status line)
114
+
115
+ Mặc định, tool thêm một **thanh tiến trình usage 5 giờ** vào đáy phiên Claude Code (trong VSCode), kiểu giống hệt widget native của Claude, tự cập nhật:
116
+
117
+ ```
118
+ Current session ███████████████████░░░░░ 80% used
119
+ Resets in 1 hr 5 min
120
+ ```
121
+
122
+ - Thanh amber lấp đầy theo **% usage đã dùng** của khối 5 giờ, kèm thời gian còn lại tới reset.
123
+ - Màu đổi theo mức: xanh < 50%, amber 50–85%, đỏ > 85%.
124
+
125
+ Con số là **chính xác** — bằng đúng số `/usage` hiển thị. Nó đọc thẳng field `rate_limits.five_hour` mà **Claude Code truyền sẵn vào status line**, nên **không cần công cụ ngoài** (không ccusage, không gọi API).
126
+
127
+ **Yêu cầu:** Claude Code ≥ 2.1.x, tài khoản **Claude.ai Pro/Max**, và có **`node`** trên máy. Thanh chỉ hiện **sau tin nhắn đầu tiên** của phiên (lúc `rate_limits` mới có); tài khoản API/không phải Pro-Max sẽ không có dữ liệu này → thanh tự ẩn, không lỗi.
128
+
129
+ Không muốn tính năng này? Cài bằng `--no-usage`:
130
+
131
+ ```bash
132
+ npx claude-code-token-saver --no-usage
133
+ ```
134
+
135
+ ---
136
+
111
137
  ## Các slash command
112
138
 
113
139
  | Lệnh | Tác dụng |
@@ -136,5 +162,6 @@ Script tự nhận diện stack (**Node/TS, Python, Go, Rust**) và điền sẵ
136
162
  ## Yêu cầu
137
163
 
138
164
  - `bash`
139
- - `python3` (dùng để hooks đọc JSON từ stdin không cần `jq`)
165
+ - `node` cần cho cách cài `npx` cho thanh usage 5 giờ (`statusline.js`)
166
+ - `python3` (dùng để hai hook guard/format đọc JSON từ stdin — không cần `jq`)
140
167
  - Tùy chọn: `jq` (dò script trong `package.json` chính xác hơn), và các trình format (`prettier`, `ruff`, `gofmt`, `rustfmt`)
package/bin/cli.js CHANGED
@@ -42,8 +42,13 @@ function candidateBashes() {
42
42
 
43
43
  const args = process.argv.slice(2);
44
44
 
45
+ // Pass a forward-slash path to bash. On Windows the script path uses
46
+ // backslashes (C:\Users\...), which bash would treat as escape sequences and
47
+ // mangle (C:UsersAdmin...). Git Bash / MSYS accept C:/Users/... just fine.
48
+ const scriptArg = script.replace(/\\/g, "/");
49
+
45
50
  for (const bash of candidateBashes()) {
46
- const res = spawnSync(bash, [script, ...args], {
51
+ const res = spawnSync(bash, [scriptArg, ...args], {
47
52
  stdio: "inherit",
48
53
  cwd: process.cwd(),
49
54
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-token-saver",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "One-shot installer that adds token-saving Claude Code rails (CLAUDE.md, hooks, subagents, slash commands, dev-docs) to any git repo.",
5
5
  "bin": {
6
6
  "claude-code-token-saver": "bin/cli.js",
@@ -19,16 +19,21 @@ set -euo pipefail
19
19
  # ---------- options ---------------------------------------------------------
20
20
  FORCE=0
21
21
  SKIP_DETECT=0
22
+ USAGE=1 # add a live 5-hour usage status line (ccusage) by default
22
23
  for arg in "$@"; do
23
24
  case "$arg" in
24
25
  --force) FORCE=1 ;;
25
26
  --no-detect) SKIP_DETECT=1 ;;
27
+ --usage) USAGE=1 ;;
28
+ --no-usage) USAGE=0 ;;
26
29
  -h|--help)
27
30
  cat <<'EOF'
28
- Usage: bash setup-claude-code.sh [--force] [--no-detect]
31
+ Usage: bash setup-claude-code.sh [--force] [--no-detect] [--no-usage]
29
32
 
30
33
  --force Overwrite files that already exist (default: skip them)
31
34
  --no-detect Do not auto-detect build/test/lint commands
35
+ --no-usage Do not add the 5-hour usage status line (ccusage)
36
+ --usage Add the usage status line (this is the default)
32
37
  EOF
33
38
  exit 0 ;;
34
39
  *) echo "Unknown option: $arg" >&2; exit 1 ;;
@@ -143,10 +148,22 @@ EOF
143
148
  # ===========================================================================
144
149
  # Quality gate: block `git commit` unless tests pass.
145
150
  # Safety gate: block obviously destructive shell commands.
146
- write_file ".claude/settings.json" <<'EOF'
151
+ # Usage line: a native-style amber bar showing the current 5-hour usage % + reset
152
+ # countdown at the bottom of the Claude Code session. Numbers come straight from
153
+ # rate_limits.five_hour on the statusLine stdin (exact, same as /usage) — no
154
+ # external tool. Rendered by .claude/hooks/statusline.js (below). Off: --no-usage.
155
+ if [[ $USAGE -eq 1 ]]; then
156
+ STATUSLINE_BLOCK=$' "statusLine": {\n "type": "command",\n "command": "node \\"$CLAUDE_PROJECT_DIR/.claude/hooks/statusline.js\\""\n },\n'
157
+ else
158
+ STATUSLINE_BLOCK=""
159
+ fi
160
+
161
+ # NOTE: unquoted heredoc so ${STATUSLINE_BLOCK} expands. Everything Claude Code
162
+ # must see literally ($schema, $CLAUDE_PROJECT_DIR) is backslash-escaped.
163
+ write_file ".claude/settings.json" <<EOF
147
164
  {
148
- "$schema": "https://json.schemastore.org/claude-code-settings.json",
149
- "permissions": {
165
+ "\$schema": "https://json.schemastore.org/claude-code-settings.json",
166
+ ${STATUSLINE_BLOCK} "permissions": {
150
167
  "deny": [
151
168
  "Read(./.env)",
152
169
  "Read(./.env.*)",
@@ -160,7 +177,7 @@ write_file ".claude/settings.json" <<'EOF'
160
177
  "hooks": [
161
178
  {
162
179
  "type": "command",
163
- "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-bash.sh\""
180
+ "command": "\"\$CLAUDE_PROJECT_DIR/.claude/hooks/guard-bash.sh\""
164
181
  }
165
182
  ]
166
183
  }
@@ -171,7 +188,7 @@ write_file ".claude/settings.json" <<'EOF'
171
188
  "hooks": [
172
189
  {
173
190
  "type": "command",
174
- "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-file.sh\""
191
+ "command": "\"\$CLAUDE_PROJECT_DIR/.claude/hooks/format-file.sh\""
175
192
  }
176
193
  ]
177
194
  }
@@ -180,6 +197,61 @@ write_file ".claude/settings.json" <<'EOF'
180
197
  }
181
198
  EOF
182
199
 
200
+ # 2c. Usage status line renderer (Node, zero deps). Reads the statusLine JSON on
201
+ # stdin and draws a native-style amber 5-hour usage bar. Numbers come from
202
+ # rate_limits.five_hour (exact, same as /usage) — present for Claude.ai Pro/Max
203
+ # after the first API response; renders nothing otherwise. Only written with --usage.
204
+ if [[ $USAGE -eq 1 ]]; then
205
+ write_file ".claude/hooks/statusline.js" <<'EOF'
206
+ #!/usr/bin/env node
207
+ // Claude Code statusLine renderer: a native-style amber "Current session" bar
208
+ // showing 5-hour usage % + reset countdown. Zero dependencies (Node only).
209
+ // Data is read straight from rate_limits.five_hour on stdin (exact, same as
210
+ // /usage); that field is present for Claude.ai Pro/Max after the first API
211
+ // response. When it is absent, this prints nothing so the footer stays clean.
212
+ "use strict";
213
+
214
+ let input = "";
215
+ process.stdin.setEncoding("utf8");
216
+ process.stdin.on("data", function (d) { input += d; });
217
+ process.stdin.on("end", function () {
218
+ let data = {};
219
+ try { data = JSON.parse(input); } catch (e) { process.exit(0); }
220
+
221
+ const rl = data.rate_limits && data.rate_limits.five_hour;
222
+ if (!rl || rl.used_percentage == null) process.exit(0); // no data yet
223
+
224
+ let pct = Number(rl.used_percentage);
225
+ if (!isFinite(pct)) process.exit(0);
226
+ pct = Math.max(0, Math.min(100, pct));
227
+
228
+ // reset countdown from resets_at (unix epoch seconds)
229
+ let resetStr = "";
230
+ if (rl.resets_at) {
231
+ let secs = Math.floor(Number(rl.resets_at) - Date.now() / 1000);
232
+ if (secs < 0) secs = 0;
233
+ const h = Math.floor(secs / 3600);
234
+ const m = Math.floor((secs % 3600) / 60);
235
+ resetStr = h > 0 ? h + " hr " + m + " min" : m + " min";
236
+ }
237
+
238
+ // colour by level, mirroring Claude's own coding: green < 50, amber 50-85, red > 85
239
+ const RESET = "\x1b[0m";
240
+ const DIM = "\x1b[2m";
241
+ const fill = pct > 85 ? "\x1b[38;5;203m" : pct >= 50 ? "\x1b[38;5;214m" : "\x1b[38;5;42m";
242
+ const track = "\x1b[38;5;238m";
243
+
244
+ const width = 24;
245
+ const filled = Math.round((pct / 100) * width);
246
+ const bar = fill + "█".repeat(filled) + track + "█".repeat(width - filled) + RESET;
247
+
248
+ const line1 = "Current session " + bar + " " + fill + Math.round(pct) + "% used" + RESET;
249
+ const line2 = resetStr ? DIM + "Resets in " + resetStr + RESET : "";
250
+ process.stdout.write(line2 ? line1 + "\n" + line2 : line1);
251
+ });
252
+ EOF
253
+ fi
254
+
183
255
  # 2a. Safety hook — blocks destructive Bash commands (exit 2 = block)
184
256
  write_file ".claude/hooks/guard-bash.sh" <<'EOF'
185
257
  #!/usr/bin/env bash
@@ -383,3 +455,12 @@ ${c_ylw}Next:${c_reset}
383
455
  ${c_dim}Note: hooks run real shell commands. Read the two scripts in
384
456
  .claude/hooks/ before trusting them on a sensitive repo.${c_reset}
385
457
  EOF
458
+
459
+ if [[ $USAGE -eq 1 ]]; then
460
+ echo
461
+ info "5-hour usage bar added (statusLine → .claude/hooks/statusline.js)."
462
+ printf " %sA native-style amber bar showing your 5h usage %% + reset countdown at\n" "$c_dim"
463
+ printf " the bottom of the session. Reads exact numbers from Claude Code's\n"
464
+ printf " rate_limits (Claude.ai Pro/Max, after the first message; needs node).\n"
465
+ printf " Disable with --no-usage.%s\n" "$c_reset"
466
+ fi