claude-code-arcane 1.1.0 → 1.1.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.1.1](https://github.com/SebastianLuser/claude-code-arcane/compare/v1.1.0...v1.1.1) (2026-06-16)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **statusline:** derive context % from real session window, not hardcoded limit ([e9b835f](https://github.com/SebastianLuser/claude-code-arcane/commit/e9b835f457a69a9bbc371da382d7673abce10113))
7
+
1
8
  # [1.1.0](https://github.com/SebastianLuser/claude-code-arcane/compare/v1.0.1...v1.1.0) (2026-06-12)
2
9
 
3
10
 
@@ -37,49 +37,75 @@ try:
37
37
  data = json.load(sys.stdin)
38
38
  except Exception:
39
39
  sys.exit(0)
40
- path = data.get("transcript_path") or ""
41
- # Windows python cannot resolve MSYS/Git-Bash paths (/c/Users/...); convert to C:/Users/...
42
- if os.name == "nt" and len(path) > 2 and path[0] == "/" and path[2] == "/" and path[1].isalpha():
43
- path = path[1].upper() + ":" + path[2:]
44
40
  model = data.get("model") or {}
45
41
  model_id = model.get("id", "")
46
42
  model_name = model.get("display_name") or model_id or ""
47
43
  # emit model on first line for bash to pick up
48
44
  print("MODEL=" + model_name)
49
- if not path or not os.path.exists(path):
50
- sys.exit(0)
51
- last_usage = None
52
- try:
53
- with open(path, encoding="utf-8") as f:
54
- for line in f:
55
- try:
56
- m = json.loads(line)
57
- except Exception:
58
- continue
59
- if m.get("type") == "assistant":
60
- msg = m.get("message") or {}
61
- u = msg.get("usage")
62
- if u:
63
- last_usage = u
64
- except Exception:
65
- sys.exit(0)
45
+
66
46
  def fmt(n):
67
47
  if n >= 1000000:
68
48
  return ("%.1fM" % (n / 1000000.0)).replace(".0M", "M")
69
49
  if n >= 1000:
70
50
  return "%dk" % int(round(n / 1000.0))
71
51
  return str(n)
52
+
53
+ total = None
54
+ limit = None
55
+ pct = None
56
+
57
+ # Preferred: Claude Code provides the real context window for THIS session/model.
58
+ # context_window_size is the actual max (200k, 1M, ...) — never hardcode it.
59
+ cw = data.get("context_window") or {}
60
+ if isinstance(cw, dict) and cw:
61
+ limit = cw.get("context_window_size") or None
62
+ cu = cw.get("current_usage") or {}
63
+ if cu:
64
+ total = (cu.get("input_tokens", 0)
65
+ + cu.get("cache_read_input_tokens", 0)
66
+ + cu.get("cache_creation_input_tokens", 0))
67
+ up = cw.get("used_percentage")
68
+ if up is not None:
69
+ try:
70
+ pct = float(up)
71
+ except Exception:
72
+ pct = None
73
+
74
+ # Fallback (older Claude Code without context_window): scan transcript usage.
75
+ if total is None:
76
+ path = data.get("transcript_path") or ""
77
+ # Windows python cannot resolve MSYS/Git-Bash paths (/c/Users/...); convert to C:/Users/...
78
+ if os.name == "nt" and len(path) > 2 and path[0] == "/" and path[2] == "/" and path[1].isalpha():
79
+ path = path[1].upper() + ":" + path[2:]
80
+ if path and os.path.exists(path):
81
+ last_usage = None
82
+ try:
83
+ with open(path, encoding="utf-8") as f:
84
+ for line in f:
85
+ try:
86
+ m = json.loads(line)
87
+ except Exception:
88
+ continue
89
+ if m.get("type") == "assistant":
90
+ u = (m.get("message") or {}).get("usage")
91
+ if u:
92
+ last_usage = u
93
+ except Exception:
94
+ last_usage = None
95
+ if last_usage:
96
+ total = (last_usage.get("input_tokens", 0)
97
+ + last_usage.get("cache_read_input_tokens", 0)
98
+ + last_usage.get("cache_creation_input_tokens", 0))
99
+ # last-resort only: infer window from a 1M marker, else assume 200k
100
+ if limit is None:
101
+ limit = 1000000 if ("[1m]" in model_id or model_id.endswith("-1m")) else 200000
102
+
72
103
  parts = []
73
- if last_usage:
74
- total = (last_usage.get("input_tokens", 0)
75
- + last_usage.get("cache_read_input_tokens", 0)
76
- + last_usage.get("cache_creation_input_tokens", 0))
77
- limit = 1000000 if ("[1m]" in model_id or model_id.endswith("-1m")) else 200000
78
- # pct is always total/limit so it stays consistent with the tokens shown.
79
- # Under the correct limit it never exceeds 100% in normal use; if it does,
80
- # the limit is wrong (broken statusline) and we want to see it, not mask it.
81
- pct = int(round(total * 100 / limit)) if limit else 0
82
- parts.append("\U0001F9E0 %s / %s (%d%%)" % (fmt(total), fmt(limit), pct))
104
+ if total is not None and limit:
105
+ # pct from Claude Code when available; else total/limit (consistent with tokens shown).
106
+ if pct is None:
107
+ pct = total * 100.0 / limit
108
+ parts.append("\U0001F9E0 %s / %s (%d%%)" % (fmt(total), fmt(limit), int(round(pct))))
83
109
  print("EXTRA=" + " | ".join(parts))
84
110
  ' <<< "$INPUT" 2>/dev/null)
85
111
  MODEL=$(echo "$OUT" | grep '^MODEL=' | head -1 | sed 's/^MODEL=//')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-arcane",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Skills, agents, hooks and rules for Claude Code — installable via npx arcane",
5
5
  "type": "module",
6
6
  "bin": {