coding-agent-tools 0.1.2__tar.gz → 0.1.4__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-agent-tools
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Session search and unified usage analytics for coding agents
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: rich>=13.0.0
@@ -1,3 +1,3 @@
1
1
  """CodingAgentTools package."""
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.4"
@@ -53,7 +53,7 @@ class ModelBucket:
53
53
 
54
54
  class PricingResolver:
55
55
  def __init__(self) -> None:
56
- self._prices = self._fetch_litellm_prices()
56
+ self._prices, self._fetch_error = self._fetch_litellm_prices()
57
57
  self._lookup_cache: dict[tuple[str, str], Optional[dict]] = {}
58
58
  self._model_aliases = {
59
59
  # Codex aliases
@@ -65,6 +65,23 @@ class PricingResolver:
65
65
  "antigravity-claude-opus-4-6-thinking": "claude-opus-4-1",
66
66
  }
67
67
 
68
+ def pricing_banner(self) -> str:
69
+ if self._prices:
70
+ return (
71
+ "💡 Pricing source: LiteLLM live registry "
72
+ f"({len(self._prices):,} model entries). "
73
+ "Unknown models fall back to observed/source-reported cost."
74
+ )
75
+ if self._fetch_error:
76
+ return (
77
+ "⚠ Pricing source: LiteLLM unavailable "
78
+ f"({self._fetch_error}). Using observed/source-reported cost only."
79
+ )
80
+ return (
81
+ "⚠ Pricing source: LiteLLM unavailable. "
82
+ "Using observed/source-reported cost only."
83
+ )
84
+
68
85
  def estimate_cost(
69
86
  self,
70
87
  model: str,
@@ -176,13 +193,15 @@ class PricingResolver:
176
193
  }
177
194
 
178
195
  @staticmethod
179
- def _fetch_litellm_prices() -> dict:
196
+ def _fetch_litellm_prices() -> tuple[dict, Optional[str]]:
180
197
  try:
181
198
  with urllib.request.urlopen(LITELLM_URL, timeout=15) as response:
182
199
  data = json.loads(response.read())
183
- return data if isinstance(data, dict) else {}
184
- except Exception:
185
- return {}
200
+ if isinstance(data, dict):
201
+ return data, None
202
+ return {}, "unexpected pricing payload"
203
+ except Exception as exc:
204
+ return {}, str(exc)
186
205
 
187
206
 
188
207
  def to_period(ts: datetime, mode: str) -> str:
@@ -339,6 +358,7 @@ def print_table(
339
358
  "cache_r": fmt_int(item.cache_read),
340
359
  "total": fmt_int(item.total),
341
360
  "cost": fmt_cost(item.cost),
361
+ "faded": True,
342
362
  }
343
363
  )
344
364
  entries.append({"type": "sep"})
@@ -386,9 +406,10 @@ def print_table(
386
406
  tw = max(len("Tokens"), max(len(e["total"]) for e in all_data))
387
407
  cow = max(len("(USD)"), max(len(e["cost"]) for e in all_data))
388
408
 
389
- use_color = sys.stdout.isatty() and not os.getenv("NO_COLOR")
409
+ use_color = not os.getenv("NO_COLOR")
390
410
  dim = "\033[90m" if use_color else ""
391
411
  cyan = "\033[36m" if use_color else ""
412
+ faint = "\033[2m" if use_color else ""
392
413
  reset = "\033[39m" if use_color else ""
393
414
 
394
415
  def hl(l: str, m: str, r: str) -> str:
@@ -438,6 +459,7 @@ def print_table(
438
459
  elif e["type"] == "date_cont":
439
460
  print(rl(e["date"], "", "", "", "", "", "", ""))
440
461
  elif e["type"] == "data":
462
+ row_tone = faint if e.get("faded") else ""
441
463
  print(
442
464
  rl(
443
465
  e["date"],
@@ -448,6 +470,7 @@ def print_table(
448
470
  e["cache_r"],
449
471
  e["total"],
450
472
  e["cost"],
473
+ row_tone,
451
474
  )
452
475
  )
453
476
 
@@ -1005,27 +1028,54 @@ Examples:
1005
1028
  def main() -> None:
1006
1029
  mode, breakdown, source_filter = parse_args(sys.argv[1:])
1007
1030
  pricing = PricingResolver()
1031
+ mode_title = mode.capitalize()
1032
+
1033
+ print(pricing.pricing_banner())
1008
1034
 
1009
1035
  if should_show("claude", source_filter):
1010
1036
  usage, totals = collect_claude(mode, pricing)
1011
- print_table("Claude Code Usage", usage, totals, mode, breakdown)
1037
+ print_table(
1038
+ f"📊 Claude Code Token Usage Report - {mode_title} (From JSONL Sessions)",
1039
+ usage,
1040
+ totals,
1041
+ mode,
1042
+ breakdown,
1043
+ )
1012
1044
 
1013
1045
  if should_show("codex", source_filter):
1014
1046
  usage, totals = collect_codex(mode, pricing)
1015
- print_table("Codex Usage", usage, totals, mode, breakdown)
1047
+ print_table(
1048
+ f"📊 Codex Token Usage Report - {mode_title} (From JSONL Sessions)",
1049
+ usage,
1050
+ totals,
1051
+ mode,
1052
+ breakdown,
1053
+ )
1016
1054
 
1017
1055
  if should_show("openclaw", source_filter):
1018
1056
  usage, totals = collect_openclaw(mode, pricing)
1019
- print_table("OpenClaw Usage", usage, totals, mode, breakdown)
1057
+ print_table(
1058
+ f"📊 OpenClaw Token Usage Report - {mode_title} (From JSONL Sessions)",
1059
+ usage,
1060
+ totals,
1061
+ mode,
1062
+ breakdown,
1063
+ )
1020
1064
 
1021
1065
  if should_show("opencode", source_filter):
1022
1066
  usage, totals = collect_opencode(mode, pricing)
1023
- print_table("OpenCode Usage", usage, totals, mode, breakdown)
1067
+ print_table(
1068
+ f"📊 OpenCode Token Usage Report - {mode_title} (From SQLite DB)",
1069
+ usage,
1070
+ totals,
1071
+ mode,
1072
+ breakdown,
1073
+ )
1024
1074
 
1025
1075
  if should_show("openwhispr", source_filter):
1026
1076
  usage, totals = collect_openwhispr(mode)
1027
1077
  print_table(
1028
- "OpenWhispr Usage",
1078
+ f"📊 OpenWhispr Token Usage Report - {mode_title} (From SQLite DB)",
1029
1079
  usage,
1030
1080
  totals,
1031
1081
  mode,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "coding-agent-tools"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Session search and unified usage analytics for coding agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"