tokengolf 0.5.1 → 0.5.2

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/CLAUDE.md CHANGED
@@ -349,11 +349,11 @@ Nine hooks in `hooks/` directory, installed via `tokengolf install`. Most comple
349
349
  - Receives live session JSON (cost, context %, model) via stdin
350
350
  - **Design D accent bar**: `██` prefix on each line, color-coded (yellow normal, red when budget >75%)
351
351
  - Line 1: `██ ⛳ quest $cost/budget ▓▓▓░░░ pct% RATING model F1/5`
352
- - Line 2 (optional, when context ≥50%): `██ 🧠 ▓▓▓▓░░░ ctx% 🪶/🎒/📦`
352
+ - Line 2 (always shown when context data available): `██ 🧠 ▓▓▓▓░░░ ctx% 🪶/🎒/📦`
353
353
  - Budget progress bar: `▓` filled, `░` empty, 11 chars wide. Red when >75%, yellow otherwise
354
354
  - Context progress bar: `▓░` 10 chars wide. Green (50–74%), yellow (75–89%), red (90%+); hidden below 50%
355
355
  - Model label: `⚔️ Sonnet`, `⚔️ Sonnet·High`, `🏹 Haiku`, `🧙 Opus·Max`, etc. Effort appended only when explicitly set in settings.json (medium omitted — it's the default)
356
- - 1 line when context <50% (flow mode is often just 1 line), 2 lines when context visible
356
+ - Always 2 lines when context data is available from Claude Code
357
357
  - statusLine config must be an object: `{type:"command", command:"...statusline.sh", padding:1}`
358
358
 
359
359
  ### Hook installation
package/dist/cli.js CHANGED
@@ -1796,8 +1796,50 @@ function installHooks() {
1796
1796
  }
1797
1797
  const existing = settings.statusLine;
1798
1798
  const existingCmd = typeof existing === "string" ? existing : existing?.command ?? null;
1799
- const alreadyOurs = existingCmd === STATUSLINE_PATH || existingCmd === WRAPPER_PATH;
1800
- if (!alreadyOurs && existingCmd) {
1799
+ const isTgStatusline = (cmd) => cmd && (cmd.includes("tokengolf/hooks/statusline") || cmd.includes("tokengolf\\hooks\\statusline"));
1800
+ const alreadyOurs = isTgStatusline(existingCmd);
1801
+ if (alreadyOurs) {
1802
+ let userStatusline = null;
1803
+ if (existingCmd.includes("statusline-wrapper")) {
1804
+ try {
1805
+ const wrapperContent = fs4.readFileSync(existingCmd, "utf8");
1806
+ const lines = wrapperContent.split("\n").filter((l) => l.includes('echo "$SESSION_JSON"'));
1807
+ for (const line of lines) {
1808
+ const match = line.match(/echo "\$SESSION_JSON" \| (.+?)( 2>|$)/);
1809
+ if (match && !isTgStatusline(match[1])) {
1810
+ userStatusline = match[1];
1811
+ break;
1812
+ }
1813
+ }
1814
+ } catch {
1815
+ }
1816
+ }
1817
+ if (userStatusline) {
1818
+ fs4.writeFileSync(
1819
+ WRAPPER_PATH,
1820
+ [
1821
+ "#!/usr/bin/env bash",
1822
+ "SESSION_JSON=$(cat)",
1823
+ `echo "$SESSION_JSON" | ${userStatusline} 2>/dev/null || true`,
1824
+ `echo "$SESSION_JSON" | bash ${STATUSLINE_PATH}`
1825
+ ].join("\n") + "\n"
1826
+ );
1827
+ fs4.chmodSync(WRAPPER_PATH, 493);
1828
+ settings.statusLine = {
1829
+ type: "command",
1830
+ command: WRAPPER_PATH,
1831
+ padding: existing?.padding ?? 1
1832
+ };
1833
+ console.log(" \u2713 statusLine \u2192 updated paths (kept your existing statusline)");
1834
+ } else {
1835
+ settings.statusLine = {
1836
+ type: "command",
1837
+ command: STATUSLINE_PATH,
1838
+ padding: existing?.padding ?? 1
1839
+ };
1840
+ console.log(" \u2713 statusLine \u2192 updated to current install path");
1841
+ }
1842
+ } else if (existingCmd) {
1801
1843
  fs4.writeFileSync(
1802
1844
  WRAPPER_PATH,
1803
1845
  [
@@ -1814,15 +1856,13 @@ function installHooks() {
1814
1856
  padding: 1
1815
1857
  };
1816
1858
  console.log(" \u2713 statusLine \u2192 wrapped your existing statusline + tokengolf HUD");
1817
- } else if (!alreadyOurs) {
1859
+ } else {
1818
1860
  settings.statusLine = {
1819
1861
  type: "command",
1820
1862
  command: STATUSLINE_PATH,
1821
1863
  padding: 1
1822
1864
  };
1823
1865
  console.log(" \u2713 statusLine \u2192 live HUD in every Claude session");
1824
- } else {
1825
- console.log(" \u2713 statusLine \u2192 already installed");
1826
1866
  }
1827
1867
  fs4.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
1828
1868
  console.log(" \u2713 SessionStart \u2192 injects run context into Claude");
@@ -72,9 +72,9 @@ else:
72
72
  cost_str = f"{tier_emoji} ${cost:.2f}"
73
73
  rating_str = ''
74
74
 
75
- # Context bar (line 2, only shown when >= 50%)
75
+ # Context bar (line 2, always shown)
76
76
  ctx_line = None
77
- if ctx_pct is not None and ctx_pct >= 50:
77
+ if ctx_pct is not None:
78
78
  ctx_w = 10
79
79
  ctx_filled = min(ctx_w, int(ctx_pct / 100 * ctx_w + 0.5))
80
80
  ctx_empty = ctx_w - ctx_filled
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokengolf",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Gamify your Claude Code sessions. Flow mode tracks you. Roguelike mode trains you.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -149,9 +149,59 @@ export function installHooks() {
149
149
 
150
150
  const existing = settings.statusLine;
151
151
  const existingCmd = typeof existing === 'string' ? existing : (existing?.command ?? null);
152
- const alreadyOurs = existingCmd === STATUSLINE_PATH || existingCmd === WRAPPER_PATH;
152
+ // Detect any tokengolf statusline (from any install path — npm, homebrew, project dir)
153
+ const isTgStatusline = (cmd) =>
154
+ cmd &&
155
+ (cmd.includes('tokengolf/hooks/statusline') || cmd.includes('tokengolf\\hooks\\statusline'));
156
+ const alreadyOurs = isTgStatusline(existingCmd);
157
+
158
+ if (alreadyOurs) {
159
+ // Check if existing wrapper has a non-TG statusline we should preserve
160
+ let userStatusline = null;
161
+ if (existingCmd.includes('statusline-wrapper')) {
162
+ try {
163
+ const wrapperContent = fs.readFileSync(existingCmd, 'utf8');
164
+ const lines = wrapperContent.split('\n').filter((l) => l.includes('echo "$SESSION_JSON"'));
165
+ // Find the line that calls a non-tokengolf statusline
166
+ for (const line of lines) {
167
+ const match = line.match(/echo "\$SESSION_JSON" \| (.+?)( 2>|$)/);
168
+ if (match && !isTgStatusline(match[1])) {
169
+ userStatusline = match[1];
170
+ break;
171
+ }
172
+ }
173
+ } catch {}
174
+ }
153
175
 
154
- if (!alreadyOurs && existingCmd) {
176
+ if (userStatusline) {
177
+ // Re-wrap: preserve user's statusline + update tokengolf path
178
+ fs.writeFileSync(
179
+ WRAPPER_PATH,
180
+ [
181
+ '#!/usr/bin/env bash',
182
+ 'SESSION_JSON=$(cat)',
183
+ `echo "$SESSION_JSON" | ${userStatusline} 2>/dev/null || true`,
184
+ `echo "$SESSION_JSON" | bash ${STATUSLINE_PATH}`,
185
+ ].join('\n') + '\n'
186
+ );
187
+ fs.chmodSync(WRAPPER_PATH, 0o755);
188
+ settings.statusLine = {
189
+ type: 'command',
190
+ command: WRAPPER_PATH,
191
+ padding: existing?.padding ?? 1,
192
+ };
193
+ console.log(' ✓ statusLine → updated paths (kept your existing statusline)');
194
+ } else {
195
+ // Direct install — no user statusline to preserve
196
+ settings.statusLine = {
197
+ type: 'command',
198
+ command: STATUSLINE_PATH,
199
+ padding: existing?.padding ?? 1,
200
+ };
201
+ console.log(' ✓ statusLine → updated to current install path');
202
+ }
203
+ } else if (existingCmd) {
204
+ // User has a non-TG statusline — wrap it
155
205
  fs.writeFileSync(
156
206
  WRAPPER_PATH,
157
207
  [
@@ -168,15 +218,14 @@ export function installHooks() {
168
218
  padding: 1,
169
219
  };
170
220
  console.log(' ✓ statusLine → wrapped your existing statusline + tokengolf HUD');
171
- } else if (!alreadyOurs) {
221
+ } else {
222
+ // No existing statusline — install directly
172
223
  settings.statusLine = {
173
224
  type: 'command',
174
225
  command: STATUSLINE_PATH,
175
226
  padding: 1,
176
227
  };
177
228
  console.log(' ✓ statusLine → live HUD in every Claude session');
178
- } else {
179
- console.log(' ✓ statusLine → already installed');
180
229
  }
181
230
 
182
231
  fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));