cctally 1.96.2 → 1.98.0

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.
@@ -58,6 +58,24 @@ class CLIHelpFormatter(
58
58
  super().__init__(prog, **kwargs) # type: ignore[arg-type]
59
59
 
60
60
 
61
+ _PHYSICAL_PROVIDER_SOURCES = ("claude", "codex")
62
+
63
+
64
+ def _analytics_source_choices() -> tuple[str, ...]:
65
+ """Return physical providers plus the provider-separated combined view."""
66
+ return (*_PHYSICAL_PROVIDER_SOURCES, "all")
67
+
68
+
69
+ def _provider_display_names() -> str:
70
+ """Render the canonical physical-provider tuple for user-facing help."""
71
+ labels = tuple(source.title() for source in _PHYSICAL_PROVIDER_SOURCES)
72
+ if len(labels) == 1:
73
+ return labels[0]
74
+ if len(labels) == 2:
75
+ return " and ".join(labels)
76
+ return f"{', '.join(labels[:-1])}, and {labels[-1]}"
77
+
78
+
61
79
  def _argparse_has_arg(parser, option_string: str) -> bool:
62
80
  """Return True if ``parser`` already registered ``option_string``."""
63
81
  for action in parser._actions:
@@ -288,11 +306,12 @@ def _add_source_args(
288
306
  """Attach the source selector or pin a nested provider alias."""
289
307
  if fixed_source is None:
290
308
  parser.add_argument(
291
- "--source", choices=("claude", "codex", "all"), default="claude",
292
- help="Analytics provider: claude (default), codex, or all.",
309
+ "--source", choices=_analytics_source_choices(),
310
+ default=_PHYSICAL_PROVIDER_SOURCES[0],
311
+ help="Analytics provider.",
293
312
  )
294
313
  else:
295
- if fixed_source not in {"claude", "codex"}:
314
+ if fixed_source not in _PHYSICAL_PROVIDER_SOURCES:
296
315
  raise ValueError(f"unsupported fixed source {fixed_source!r}")
297
316
  parser.set_defaults(source=fixed_source)
298
317
  if speed:
@@ -2717,8 +2736,9 @@ def _build_setup_parser(subparsers, name, *, help_text, xref=None):
2717
2736
  formatter_class=CLIHelpFormatter,
2718
2737
  description=textwrap.dedent(
2719
2738
  """\
2720
- Install cctally into Claude Code by adding hook entries to
2721
- ~/.claude/settings.json (additive, idempotent) and creating
2739
+ Install cctally for local providers by adding Claude hook entries
2740
+ to ~/.claude/settings.json and native Codex handlers to every
2741
+ configured Codex home (all additive and idempotent). Also create
2722
2742
  user-facing symlinks under ~/.local/bin/.
2723
2743
 
2724
2744
  Modes (mutually exclusive):
@@ -2823,7 +2843,7 @@ def _build_transcript_parser(subparsers, name, *, help_text, xref=None):
2823
2843
  formatter_class=CLIHelpFormatter)
2824
2844
  t_search.add_argument("query", metavar="QUERY", help="Search text")
2825
2845
  t_search.add_argument(
2826
- "--source", choices=("claude", "codex"), default="claude",
2846
+ "--source", choices=_PHYSICAL_PROVIDER_SOURCES, default="claude",
2827
2847
  help="Which provider's conversations to search (default: claude)")
2828
2848
  t_search.add_argument(
2829
2849
  "--account", metavar="REF", default=None,
@@ -3270,7 +3290,7 @@ def _build_hook_tick_parser(subparsers, name, *, help_text, xref=None):
3270
3290
  help=argparse.SUPPRESS) # JSON string fed to mock fetch (tests only)
3271
3291
  ht.add_argument("--foreground", action="store_true",
3272
3292
  help=argparse.SUPPRESS) # Codex native hook wrapper
3273
- ht.add_argument("--source", choices=("claude", "codex"), default="claude",
3293
+ ht.add_argument("--source", choices=_PHYSICAL_PROVIDER_SOURCES, default="claude",
3274
3294
  help=argparse.SUPPRESS) # setup-managed native Codex hook
3275
3295
  ht.set_defaults(func=c.cmd_hook_tick)
3276
3296
 
@@ -3572,7 +3592,7 @@ _REGISTRATION = (
3572
3592
  _Reg('account', _build_account_parser, "Inspect per-provider account registry (list / show / label)", None, None),
3573
3593
  _Reg('telemetry', _build_telemetry_parser, "Show or change anonymous install-count telemetry", None, None),
3574
3594
  _Reg('alerts', _build_alerts_parser, "Manage threshold alerts", None, None),
3575
- _Reg('setup', _build_setup_parser, "Install cctally into Claude Code (hooks + symlinks)", None, None),
3595
+ _Reg('setup', _build_setup_parser, "Install provider hooks/handlers + symlinks", None, None),
3576
3596
  _Reg('db', _build_db_parser, "Migration / DB management (status, skip, unskip)", None, None),
3577
3597
  _Reg('doctor', _build_doctor_parser, "Diagnose data freshness and install state", None, None),
3578
3598
  _Reg('pricing-check', _build_pricing_check_parser, "Detect stale or missing embedded model pricing", None, None),
@@ -3592,28 +3612,33 @@ _REGISTRATION = (
3592
3612
 
3593
3613
  def build_parser() -> argparse.ArgumentParser:
3594
3614
  c = _cctally()
3615
+ providers = _provider_display_names()
3616
+ source_choices = ",".join(_analytics_source_choices())
3617
+ quick_start_reports = "\n".join(
3618
+ f" cctally report --source {source}"
3619
+ for source in _PHYSICAL_PROVIDER_SOURCES
3620
+ )
3595
3621
  p = argparse.ArgumentParser(
3596
3622
  prog="cctally",
3597
3623
  formatter_class=CLIHelpFormatter,
3598
3624
  description=textwrap.dedent(
3599
- """\
3600
- Track Claude subscription weekly usage percent and weekly cost
3601
- in a local SQLite database.
3625
+ f"""\
3626
+ Track {providers} subscription usage and local USD cost in SQLite.
3602
3627
 
3603
3628
  Data flow:
3604
- 1) Claude Code status line captures rate limit data after each API call.
3605
- 2) record-usage stores usage snapshots and triggers percent milestones.
3606
- 3) sync-week computes weekly USD cost from Claude Code session data.
3607
- 4) report computes dollars per 1% and shows trend history.
3629
+ 1) Claude Code hooks and status lines retain Claude usage and sessions.
3630
+ 2) Native Codex handlers retain Codex sessions and quota windows.
3631
+ 3) Provider-aware project, diff, range-cost, cache-report, and report
3632
+ commands accept --source {{{source_choices}}}.
3633
+ 4) Provider-native reports keep {providers} quota percentages and
3634
+ reset windows separate; `cctally codex quota` exposes Codex windows.
3608
3635
  """
3609
3636
  ),
3610
- epilog=textwrap.dedent(
3611
- """\
3612
- Quick start:
3613
- # Add record-usage call to ~/.claude/statusline-command.sh (see record-usage --help)
3614
- cctally sync-week
3615
- cctally report
3616
- """
3637
+ epilog=(
3638
+ "Quick start:\n"
3639
+ " cctally setup\n"
3640
+ f"{quick_start_reports}\n"
3641
+ " cctally codex quota statusline"
3617
3642
  ),
3618
3643
  )
3619
3644
  p.add_argument(