cctally 1.7.2 → 1.7.4
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 +27 -0
- package/bin/_cctally_alerts.py +12 -5
- package/bin/_cctally_cache.py +12 -11
- package/bin/_cctally_config.py +34 -19
- package/bin/_cctally_core.py +890 -0
- package/bin/_cctally_dashboard.py +104 -113
- package/bin/_cctally_db.py +271 -41
- package/bin/_cctally_record.py +516 -116
- package/bin/_cctally_refresh.py +35 -20
- package/bin/_cctally_setup.py +26 -16
- package/bin/_cctally_sync_week.py +21 -6
- package/bin/_cctally_tui.py +128 -52
- package/bin/_cctally_update.py +11 -16
- package/bin/_lib_aggregators.py +7 -1
- package/bin/_lib_diff_kernel.py +19 -21
- package/bin/_lib_render.py +20 -0
- package/bin/_lib_subscription_weeks.py +17 -9
- package/bin/cctally +191 -779
- package/dashboard/static/assets/index-DhCnIFq9.js +18 -0
- package/dashboard/static/assets/index-Dv5Dzag5.css +1 -0
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +2 -1
- package/dashboard/static/assets/index-BgpoazlS.js +0 -18
- package/dashboard/static/assets/index-nJdUaGys.css +0 -1
package/bin/_cctally_update.py
CHANGED
|
@@ -192,28 +192,23 @@ def _cctally():
|
|
|
192
192
|
return sys.modules["cctally"]
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
# === Honest imports from extracted homes ===================================
|
|
196
|
+
# Spec 2026-05-17-cctally-core-kernel-extraction.md §3.3.
|
|
197
|
+
from _cctally_core import eprint, _now_utc
|
|
198
|
+
from _cctally_config import save_config
|
|
199
|
+
|
|
200
|
+
|
|
195
201
|
# === Module-level back-ref shims for helpers that STAY in bin/cctally ======
|
|
196
202
|
# Each shim resolves ``sys.modules['cctally'].X`` at CALL TIME (not bind
|
|
197
203
|
# time), so monkeypatches on cctally's namespace propagate into the moved
|
|
198
|
-
# code unchanged.
|
|
199
|
-
#
|
|
200
|
-
# (
|
|
201
|
-
|
|
202
|
-
return sys.modules["cctally"].eprint(*args, **kwargs)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def _now_utc(*args, **kwargs):
|
|
206
|
-
return sys.modules["cctally"]._now_utc(*args, **kwargs)
|
|
207
|
-
|
|
208
|
-
|
|
204
|
+
# code unchanged. `load_config` STAYS as a shim even though its natural
|
|
205
|
+
# home is _cctally_config — tests monkeypatch it via `ns["load_config"]`
|
|
206
|
+
# (16 sites, audited 2026-05-17); direct import would silently bypass.
|
|
207
|
+
# See spec §3.5 (carve-out) and §3.7 (stays-on-shim allowlist).
|
|
209
208
|
def load_config(*args, **kwargs):
|
|
210
209
|
return sys.modules["cctally"].load_config(*args, **kwargs)
|
|
211
210
|
|
|
212
211
|
|
|
213
|
-
def save_config(*args, **kwargs):
|
|
214
|
-
return sys.modules["cctally"].save_config(*args, **kwargs)
|
|
215
|
-
|
|
216
|
-
|
|
217
212
|
def _release_read_latest_release_version(*args, **kwargs):
|
|
218
213
|
return sys.modules["cctally"]._release_read_latest_release_version(
|
|
219
214
|
*args, **kwargs
|
|
@@ -2114,7 +2109,7 @@ def _should_show_update_banner(
|
|
|
2114
2109
|
return False
|
|
2115
2110
|
if not config.get("update", {}).get("check", {}).get("enabled", True):
|
|
2116
2111
|
return False
|
|
2117
|
-
available, _ = c._compute_effective_update_available(state, suppress,
|
|
2112
|
+
available, _ = c._compute_effective_update_available(state, suppress, _now_utc())
|
|
2118
2113
|
return available
|
|
2119
2114
|
|
|
2120
2115
|
|
package/bin/_lib_aggregators.py
CHANGED
|
@@ -79,6 +79,13 @@ _lib_subscription_weeks = _load_lib("_lib_subscription_weeks")
|
|
|
79
79
|
SubWeek = _lib_subscription_weeks.SubWeek
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
# === Honest imports from extracted homes ===================================
|
|
83
|
+
# Spec 2026-05-17-cctally-core-kernel-extraction.md §3.3: kernel symbols
|
|
84
|
+
# import from _cctally_core. `CODEX_SESSIONS_DIR` (path constant) and
|
|
85
|
+
# `_decode_escaped_cwd` (out-of-scope) stay on the _cctally() accessor.
|
|
86
|
+
from _cctally_core import parse_iso_datetime
|
|
87
|
+
|
|
88
|
+
|
|
82
89
|
@dataclass
|
|
83
90
|
class BucketUsage:
|
|
84
91
|
"""Aggregated usage for one time bucket.
|
|
@@ -247,7 +254,6 @@ def _aggregate_weekly(
|
|
|
247
254
|
# candidate week in O(log W) per entry rather than the linear
|
|
248
255
|
# scan that previously ran ~130k x ~54 = 7M comparisons.
|
|
249
256
|
import bisect
|
|
250
|
-
parse_iso_datetime = _cctally().parse_iso_datetime
|
|
251
257
|
parsed_bounds: list[tuple[dt.datetime, dt.datetime, str]] = []
|
|
252
258
|
for w in weeks:
|
|
253
259
|
start_dt = parse_iso_datetime(w.start_ts, "week.start_ts")
|
package/bin/_lib_diff_kernel.py
CHANGED
|
@@ -116,11 +116,25 @@ _resolve_tz = _lib_display_tz._resolve_tz
|
|
|
116
116
|
format_display_dt = _lib_display_tz.format_display_dt
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
|
|
119
|
+
# === Honest imports from extracted homes ===================================
|
|
120
|
+
# Spec 2026-05-17-cctally-core-kernel-extraction.md §3.3: kernel symbols
|
|
121
|
+
# (Z-leaf + Z-mid) import from _cctally_core. The legacy shim functions
|
|
122
|
+
# for these names are deleted.
|
|
123
|
+
from _cctally_core import (
|
|
124
|
+
open_db,
|
|
125
|
+
_command_as_of,
|
|
126
|
+
_canonicalize_optional_iso,
|
|
127
|
+
parse_iso_datetime,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# === Module-level back-ref shims for helpers that STAY in bin/cctally ======
|
|
132
|
+
# Each shim resolves ``sys.modules['cctally'].X`` at CALL TIME (not bind
|
|
133
|
+
# time), so monkeypatches on cctally's namespace propagate into the moved
|
|
134
|
+
# code unchanged. `get_claude_session_entries` STAYS as a shim even though
|
|
135
|
+
# its natural home is _cctally_cache — tests monkeypatch it via ``ns["X"]``
|
|
136
|
+
# (audited 2026-05-17); a direct import would silently bypass the patches.
|
|
137
|
+
# See spec §3.5 (carve-out) and §3.7 (stays-on-shim allowlist).
|
|
124
138
|
def get_claude_session_entries(*args, **kwargs):
|
|
125
139
|
return sys.modules["cctally"].get_claude_session_entries(*args, **kwargs)
|
|
126
140
|
|
|
@@ -129,10 +143,6 @@ def _resolve_project_key(*args, **kwargs):
|
|
|
129
143
|
return sys.modules["cctally"]._resolve_project_key(*args, **kwargs)
|
|
130
144
|
|
|
131
145
|
|
|
132
|
-
def open_db(*args, **kwargs):
|
|
133
|
-
return sys.modules["cctally"].open_db(*args, **kwargs)
|
|
134
|
-
|
|
135
|
-
|
|
136
146
|
def _iso_z(*args, **kwargs):
|
|
137
147
|
return sys.modules["cctally"]._iso_z(*args, **kwargs)
|
|
138
148
|
|
|
@@ -145,18 +155,6 @@ def _style_ansi(*args, **kwargs):
|
|
|
145
155
|
return sys.modules["cctally"]._style_ansi(*args, **kwargs)
|
|
146
156
|
|
|
147
157
|
|
|
148
|
-
def _command_as_of(*args, **kwargs):
|
|
149
|
-
return sys.modules["cctally"]._command_as_of(*args, **kwargs)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def _canonicalize_optional_iso(*args, **kwargs):
|
|
153
|
-
return sys.modules["cctally"]._canonicalize_optional_iso(*args, **kwargs)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def parse_iso_datetime(*args, **kwargs):
|
|
157
|
-
return sys.modules["cctally"].parse_iso_datetime(*args, **kwargs)
|
|
158
|
-
|
|
159
|
-
|
|
160
158
|
# Private eprint shim per spec §5.3 (pure layer does not back-import
|
|
161
159
|
# cctally for ubiquitous helpers; eprint isn't actually called by the
|
|
162
160
|
# moved code, but kept here as the canonical pure-layer pattern so
|
package/bin/_lib_render.py
CHANGED
|
@@ -2671,6 +2671,12 @@ def _five_hour_blocks_to_json(
|
|
|
2671
2671
|
"sevenDayPctAtBlockEnd": p_end,
|
|
2672
2672
|
"sevenDayPctDeltaPp": delta,
|
|
2673
2673
|
"crossedSevenDayReset": crossed,
|
|
2674
|
+
# Spec §5.1 — in-place credit events for this 5h block, in
|
|
2675
|
+
# ascending effective_reset_at order. Empty list when the
|
|
2676
|
+
# block carries no credit detections. Source: ``__credits``
|
|
2677
|
+
# side-channel attached by ``cmd_five_hour_blocks`` via
|
|
2678
|
+
# ``credits_by_window``.
|
|
2679
|
+
"credits": d.get("__credits") or [],
|
|
2674
2680
|
}
|
|
2675
2681
|
if breakdown_axis == "model":
|
|
2676
2682
|
out["modelBreakdowns"] = [
|
|
@@ -2752,6 +2758,20 @@ def _render_five_hour_blocks_table(
|
|
|
2752
2758
|
formatted_start = _format_block_start(d["block_start_at"], args._resolved_tz)
|
|
2753
2759
|
if crossed:
|
|
2754
2760
|
formatted_start = f"⚡ {formatted_start}"
|
|
2761
|
+
# Spec §5.1 — credit chip suffix. When the block carries one or
|
|
2762
|
+
# more in-place credit events (5h credit detection v1.7.x;
|
|
2763
|
+
# __credits side-channel set by ``cmd_five_hour_blocks``), append
|
|
2764
|
+
# a ⚡-prefixed chip listing the per-credit delta-pp. Multiple
|
|
2765
|
+
# credits in the same block concatenate (e.g.
|
|
2766
|
+
# `⚡ credited -20pp, -8pp`). The chip text sits AFTER the
|
|
2767
|
+
# block-start cell's existing crossed-reset glyph so the two
|
|
2768
|
+
# signals visually disambiguate by position (crossed-reset
|
|
2769
|
+
# prefix vs. credit suffix). Both use ⚡ — different semantics,
|
|
2770
|
+
# different positions.
|
|
2771
|
+
credits = d.get("__credits") or []
|
|
2772
|
+
if credits:
|
|
2773
|
+
deltas = ", ".join(f"{c['deltaPp']:+.0f}pp" for c in credits)
|
|
2774
|
+
formatted_start = f"{formatted_start} ⚡ credited {deltas}"
|
|
2755
2775
|
rows.append([
|
|
2756
2776
|
formatted_start,
|
|
2757
2777
|
"ACTIVE" if d["__is_active"] else "closed",
|
|
@@ -73,6 +73,18 @@ _resolve_tz = _lib_display_tz._resolve_tz
|
|
|
73
73
|
_local_tz_name = _lib_display_tz._local_tz_name
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
# === Honest imports from extracted homes ===================================
|
|
77
|
+
# Spec 2026-05-17-cctally-core-kernel-extraction.md §3.3: kernel symbols
|
|
78
|
+
# import from _cctally_core. `load_config` stays on the _cctally()
|
|
79
|
+
# accessor per spec §3.5 monkeypatch carve-out (tests reach it via
|
|
80
|
+
# ``ns["load_config"]``).
|
|
81
|
+
from _cctally_core import (
|
|
82
|
+
parse_iso_datetime,
|
|
83
|
+
get_week_start_name,
|
|
84
|
+
WEEKDAY_MAP,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
76
88
|
@dataclass(frozen=True)
|
|
77
89
|
class SubWeek:
|
|
78
90
|
"""One subscription-week bounded interval.
|
|
@@ -142,7 +154,6 @@ def _clamp_end_ats_to_next_start(
|
|
|
142
154
|
`pairs` must be sorted by start_at ascending. None values are
|
|
143
155
|
passed through unchanged and never participate in clamping.
|
|
144
156
|
"""
|
|
145
|
-
parse_iso_datetime = _cctally().parse_iso_datetime
|
|
146
157
|
n = len(pairs)
|
|
147
158
|
if n < 2:
|
|
148
159
|
return [p[1] for p in pairs]
|
|
@@ -182,7 +193,6 @@ def _apply_overlap_clamp_to_subweeks(weeks: list[SubWeek]) -> list[SubWeek]:
|
|
|
182
193
|
Input must be sorted by start_ts ascending (invariant of
|
|
183
194
|
_compute_subscription_weeks in all three branches).
|
|
184
195
|
"""
|
|
185
|
-
parse_iso_datetime = _cctally().parse_iso_datetime
|
|
186
196
|
if len(weeks) < 2:
|
|
187
197
|
return weeks
|
|
188
198
|
pairs: list[tuple[str | None, str | None]] = [(w.start_ts, w.end_ts) for w in weeks]
|
|
@@ -217,7 +227,6 @@ def _apply_reset_events_to_subweeks(
|
|
|
217
227
|
raw snapshot strings that may be written in non-UTC offsets while
|
|
218
228
|
`week_reset_events.{old,new}_week_end_at` are canonicalized UTC.
|
|
219
229
|
"""
|
|
220
|
-
parse_iso_datetime = _cctally().parse_iso_datetime
|
|
221
230
|
rows = conn.execute(
|
|
222
231
|
"SELECT old_week_end_at, new_week_end_at, effective_reset_at_utc "
|
|
223
232
|
"FROM week_reset_events"
|
|
@@ -289,9 +298,6 @@ def _compute_subscription_weeks(
|
|
|
289
298
|
dates that miss actual snapshot boundaries for middle weeks. Using
|
|
290
299
|
snapshot rows directly for weeks they cover avoids that drift.
|
|
291
300
|
"""
|
|
292
|
-
cctally = _cctally()
|
|
293
|
-
parse_iso_datetime = cctally.parse_iso_datetime
|
|
294
|
-
|
|
295
301
|
# Case A: snapshots exist.
|
|
296
302
|
snap_rows = conn.execute(
|
|
297
303
|
"SELECT "
|
|
@@ -460,9 +466,11 @@ def _compute_subscription_weeks(
|
|
|
460
466
|
return _apply_overlap_clamp_to_subweeks(weeks)
|
|
461
467
|
|
|
462
468
|
# Case B: no snapshots — config-based calendar-week fallback.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
469
|
+
# `load_config` stays on the _cctally() accessor per spec §3.5
|
|
470
|
+
# monkeypatch carve-out — tests reach it via ``ns["load_config"]``.
|
|
471
|
+
config = _cctally().load_config()
|
|
472
|
+
week_start_name = get_week_start_name(config)
|
|
473
|
+
week_start_idx = WEEKDAY_MAP[week_start_name]
|
|
466
474
|
# internal fallback: host-local intentional
|
|
467
475
|
local_start_date = range_start.astimezone().date()
|
|
468
476
|
diff = (local_start_date.weekday() - week_start_idx) % 7
|