cc-cream 0.1.3 → 0.1.5

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
@@ -4,6 +4,17 @@ All notable changes to cc-cream are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.1.5] — 2026-05-29
8
+
9
+ ### Fixed
10
+ - **TTL falsely resets after non-API Claude Code events** (e.g. `/plugin update`). The TTL anchor was the `mtime` of the transcript file, which updates whenever Claude Code writes any event to the transcript — not just API turns. The anchor is now derived from session state: when `total_input_tokens` grows between renders (indicating a real API turn), `last_api_ts` is stored and used as the anchor on subsequent renders. Falls back to transcript mtime only on the first render before any state exists.
11
+ - **Plugin command paths fixed for real** (reverts the `../` change from 0.1.3). The Claude Code plugin validator rejects `..` path traversal; command paths in `plugin.json` resolve from the plugin root, not from `.claude-plugin/`, so `./commands/setup.md` is correct.
12
+
13
+ ## [0.1.4] — 2026-05-29
14
+
15
+ ### Fixed
16
+ - **Node DEP0190 deprecation warning during setup.** `resolveNodePath()` called `execFileSync('command', ['-v', 'node'], { shell: true })`, which Node warns about because args are concatenated rather than escaped when `shell` is true. Switched to `execSync('command -v node')` — no behavior change, warning gone.
17
+
7
18
  ## [0.1.3] — 2026-05-29
8
19
 
9
20
  ### Fixed
@@ -64,6 +75,8 @@ line and prints a colored ≤3-row bar — zero tokens, the model never sees it.
64
75
  - Supports **macOS and Linux**; Windows is a planned fast-follow.
65
76
  - Requires Claude Code **2.1.132+** (`effort` / `thinking` need 2.1.145+).
66
77
 
78
+ [0.1.5]: https://github.com/bart-turczynski/cc-cream/compare/v0.1.4...v0.1.5
79
+ [0.1.4]: https://github.com/bart-turczynski/cc-cream/compare/v0.1.3...v0.1.4
67
80
  [0.1.3]: https://github.com/bart-turczynski/cc-cream/compare/v0.1.2...v0.1.3
68
81
  [0.1.2]: https://github.com/bart-turczynski/cc-cream/compare/v0.1.1...v0.1.2
69
82
  [0.1.1]: https://github.com/bart-turczynski/cc-cream/compare/v0.1.0...v0.1.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-cream",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Claude Code cache/context/cost status-line tool",
5
5
  "directories": {
6
6
  "doc": "docs"
package/src/install.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // The pure `plan()` function does all the decision-making (no I/O) so it is
9
9
  // testable; the CLI wrapper at the bottom handles reading/prompting/writing.
10
10
 
11
- import { execFileSync } from 'node:child_process';
11
+ import { execSync } from 'node:child_process';
12
12
  import fs from 'node:fs';
13
13
  import os from 'node:os';
14
14
  import path from 'node:path';
@@ -188,10 +188,7 @@ function copyRuntimeFiles(sourceFile, destDir) {
188
188
  // process.execPath (the node currently running setup) if that fails.
189
189
  function resolveNodePath() {
190
190
  try {
191
- const found = execFileSync('command', ['-v', 'node'], {
192
- shell: true,
193
- encoding: 'utf8',
194
- }).trim();
191
+ const found = execSync('command -v node', { encoding: 'utf8' }).trim();
195
192
  if (found) return found;
196
193
  } catch {
197
194
  // fall through
package/src/segments.js CHANGED
@@ -56,16 +56,26 @@ function segCache(data, cfg, prevCachePct, recovering) {
56
56
  return { text: `cache:${pct}%`, color };
57
57
  }
58
58
 
59
- function segTtl(data, cfg, ttlMin, now) {
60
- const tp = data?.transcript_path;
61
- if (typeof tp !== 'string' || tp === '') return null;
62
- let mtimeMs;
63
- try {
64
- mtimeMs = fs.statSync(tp).mtimeMs;
65
- } catch {
66
- return null;
59
+ function segTtl(data, cfg, ttlMin, now, prevSessionState) {
60
+ const prevTokens = prevSessionState?.total_input_tokens;
61
+ const curTokens = data?.context_window?.total_input_tokens;
62
+ const tokensGrew = isNum(curTokens) && isNum(prevTokens) && curTokens > prevTokens;
63
+
64
+ let anchorMs;
65
+ if (tokensGrew) {
66
+ anchorMs = now;
67
+ } else if (isNum(prevSessionState?.last_api_ts)) {
68
+ anchorMs = prevSessionState.last_api_ts;
69
+ } else {
70
+ const tp = data?.transcript_path;
71
+ if (typeof tp !== 'string' || tp === '') return null;
72
+ try {
73
+ anchorMs = fs.statSync(tp).mtimeMs;
74
+ } catch {
75
+ return null;
76
+ }
67
77
  }
68
- const elapsedMin = Math.floor(Math.max(0, now - mtimeMs) / 60000);
78
+ const elapsedMin = Math.floor(Math.max(0, now - anchorMs) / 60000);
69
79
  const remainingMin = Math.max(0, ttlMin - elapsedMin);
70
80
  const text = `ttl:${pad2(Math.floor(remainingMin / 60))}:${pad2(remainingMin % 60)}`;
71
81
  const s = cfg.segments.ttl;
@@ -158,7 +168,7 @@ export function renderSegments(data, cfg, ttlMin, now, prevSessionState = null,
158
168
  prevSessionState && isNum(prevSessionState.cache_pct) ? prevSessionState.cache_pct : undefined,
159
169
  prevSessionState?.recovering === true,
160
170
  ),
161
- ttl: segTtl(data, cfg, ttlMin, now),
171
+ ttl: segTtl(data, cfg, ttlMin, now, prevSessionState),
162
172
  cost: segCost(data),
163
173
  '5h': segRate(data?.rate_limits?.five_hour, '5h', cfg, '5h', now),
164
174
  '7d': segRate(data?.rate_limits?.seven_day, '7d', cfg, '7d', now),
package/src/state.js CHANGED
@@ -53,5 +53,11 @@ export function nextSessionPatch(data, prevSessionState, cfg, now) {
53
53
  }
54
54
  const fh = data?.rate_limits?.five_hour;
55
55
  if (fh && isNum(fh.used_percentage)) patch.five_hour_pct = fh.used_percentage;
56
+ const curTokens = data?.context_window?.total_input_tokens;
57
+ const prevTokens = prevSessionState?.total_input_tokens;
58
+ if (isNum(curTokens)) {
59
+ patch.total_input_tokens = curTokens;
60
+ if (isNum(prevTokens) && curTokens > prevTokens) patch.last_api_ts = now;
61
+ }
56
62
  return patch;
57
63
  }