cctally 1.14.0 → 1.15.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.
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- package/bin/_cctally_db.py +12 -0
- package/bin/cctally +539 -388
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.15.0] - 2026-05-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`cctally claude <cmd>` and `cctally codex <cmd>` subgroup commands** let you paste ccusage's hierarchical syntax verbatim — `cctally claude {daily,monthly,weekly,session,blocks}` is a drop-in for `ccusage claude <cmd>`, and `cctally codex {daily,monthly,session,weekly}` for `ccusage codex <cmd>` (`codex weekly` is a cctally extension; upstream has none). Each subgroup leaf routes to the exact same engine as its flat form, so the table / `--json` / exit code are byte-identical — only `--help` adds a one-line alias/canonical cross-reference. The flat forms (`cctally daily`, `cctally codex-daily`, …) remain fully supported as back-compat aliases, with no deprecation warning. (#86)
|
|
12
|
+
|
|
8
13
|
## [1.14.0] - 2026-05-26
|
|
9
14
|
|
|
10
15
|
### Added
|
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ For status-line integration, alerts, and configuration, see [docs/installation.m
|
|
|
113
113
|
|
|
114
114
|
## What cctally adds
|
|
115
115
|
|
|
116
|
-
`cctally` started as a local-first replacement for [`ccusage`](https://github.com/ryoppippi/ccusage), and it stays compatible at the level of common CLI flows (`daily`, `monthly`, `weekly`, `session`, `blocks`). Beyond that, it adds:
|
|
116
|
+
`cctally` started as a local-first replacement for [`ccusage`](https://github.com/ryoppippi/ccusage), and it stays compatible at the level of common CLI flows (`daily`, `monthly`, `weekly`, `session`, `blocks`). Paste from ccusage verbatim: `cctally claude <cmd>` is a drop-in for `ccusage claude <cmd>` (and `cctally codex <cmd>` for `ccusage codex <cmd>`), with the flat forms (`cctally daily`, `cctally codex-daily`, …) kept as aliases. Beyond that, it adds:
|
|
117
117
|
|
|
118
118
|
- **Live web dashboard.** Nine-panel SSE-driven view at `localhost:8789` (current week, forecast, trend, sessions, weekly, monthly, blocks, daily, recent alerts), with per-panel modals, a mobile layout, threshold alerts, and a settings drawer.
|
|
119
119
|
- **TUI live mode.** The same data inside your terminal (`cctally tui`; requires the optional `rich` package).
|
|
@@ -123,7 +123,7 @@ For status-line integration, alerts, and configuration, see [docs/installation.m
|
|
|
123
123
|
- **5-hour block analytics.** Per-block usage with model and project breakdowns (`cctally five-hour-blocks --breakdown=model`).
|
|
124
124
|
- **Time-window diff.** Compare two windows with model and project decomposition (`cctally diff`).
|
|
125
125
|
- **Project rollup.** Usage by Git project (`cctally project`).
|
|
126
|
-
- **Codex parity.**
|
|
126
|
+
- **Codex parity.** `cctally codex daily / monthly / session` are drop-ins for `ccusage codex daily / monthly / session`; the flat `codex-*` forms (drop-ins for the standalone `ccusage-codex` binary) remain as aliases, plus an added `cctally codex weekly` / `cctally codex-weekly` rollup (upstream has no `codex weekly`).
|
|
127
127
|
- **Persistent SQLite.** Week-over-week comparisons survive across runs.
|
|
128
128
|
|
|
129
129
|
**On speed.** Pricing is embedded and computed at query time from a delta-tail SQLite cache (`~/.local/share/cctally/cache.db`), with no shell-outs. First-table latency on 30 days of session data: **~2.6s (cctally) vs ~31s (ccusage)**, about 12× faster. Measured by `bench/cctally-vs-ccusage.sh` on macOS arm64, 2026-05-05; your numbers will vary.[^bench]
|
package/bin/_cctally_db.py
CHANGED
|
@@ -2371,6 +2371,10 @@ def _recompute_banner_should_emit(
|
|
|
2371
2371
|
confuses scripted pipelines. Banner still lands on the
|
|
2372
2372
|
next interactive non-report command (``report``,
|
|
2373
2373
|
``weekly``, ``percent-breakdown``, etc.) once on upgrade.
|
|
2374
|
+
Subgroup forms (``cctally claude/codex <cmd>``, issue #86
|
|
2375
|
+
Session B) carry the source group in ``argv[1]`` and the
|
|
2376
|
+
leaf in ``argv[2]``; we resolve the leaf so suppression is
|
|
2377
|
+
byte-identical to the flat alias.
|
|
2374
2378
|
|
|
2375
2379
|
Returns True iff the banner should be printed. Defensive: any
|
|
2376
2380
|
error reading ``sys.argv`` falls back to "don't print" — silence
|
|
@@ -2388,6 +2392,14 @@ def _recompute_banner_should_emit(
|
|
|
2388
2392
|
return False
|
|
2389
2393
|
try:
|
|
2390
2394
|
argv1 = sys.argv[1] if len(sys.argv) > 1 else None
|
|
2395
|
+
# Subgroup forms (`cctally claude <cmd>` / `cctally codex <cmd>`) carry
|
|
2396
|
+
# the source group in argv[1]; the suppression key is the leaf command
|
|
2397
|
+
# in argv[2]. Resolve it so the recompute banner suppresses identically
|
|
2398
|
+
# to the flat alias (issue #86 Session B; matches the args.command leaf
|
|
2399
|
+
# resolution used by the error-sentinel banner). Purely additive — flat
|
|
2400
|
+
# invocations (argv1 not in {claude,codex}) are byte-identical to before.
|
|
2401
|
+
if argv1 in ("claude", "codex") and len(sys.argv) > 2:
|
|
2402
|
+
argv1 = sys.argv[2]
|
|
2391
2403
|
except Exception:
|
|
2392
2404
|
argv1 = None
|
|
2393
2405
|
if argv1 in _BANNER_SUPPRESSED_COMMANDS:
|
package/bin/cctally
CHANGED
|
@@ -9887,6 +9887,70 @@ def _add_ccusage_alias_args(parser, *, ansi_emit: bool) -> None:
|
|
|
9887
9887
|
)
|
|
9888
9888
|
|
|
9889
9889
|
|
|
9890
|
+
def _add_codex_shared_args(parser: argparse.ArgumentParser) -> None:
|
|
9891
|
+
"""Register upstream `ccusage-codex sharedArgs` on a codex subparser.
|
|
9892
|
+
|
|
9893
|
+
Upstream sharedArgs (node_modules/@ccusage/codex/dist/index.js):
|
|
9894
|
+
--timezone/-z, --locale/-l, --compact, --color, --noColor,
|
|
9895
|
+
--offline/--no-offline.
|
|
9896
|
+
|
|
9897
|
+
Honored here: --timezone (dates + aggregation buckets) and
|
|
9898
|
+
--compact (table layout). Accepted-but-no-op (stored on the
|
|
9899
|
+
namespace for drop-in parity with upstream scripts): --locale
|
|
9900
|
+
(we don't locale-format dates), --color / --noColor (we don't
|
|
9901
|
+
emit ANSI codes today). --offline is accepted as a no-op too
|
|
9902
|
+
(we are always offline); it uses BooleanOptionalAction so
|
|
9903
|
+
`--no-offline` also parses cleanly. `-O` is kept as the short
|
|
9904
|
+
form for offline for backward compat with earlier builds.
|
|
9905
|
+
"""
|
|
9906
|
+
parser.add_argument(
|
|
9907
|
+
"-z", "--timezone", default=None, metavar="TZ",
|
|
9908
|
+
help="IANA timezone for date bucketing and Date/Last Activity cells.",
|
|
9909
|
+
)
|
|
9910
|
+
parser.add_argument(
|
|
9911
|
+
"-l", "--locale", default=None, metavar="LOCALE",
|
|
9912
|
+
help="Accepted for drop-in compat; no-op (dates are not locale-formatted).",
|
|
9913
|
+
)
|
|
9914
|
+
parser.add_argument(
|
|
9915
|
+
"--compact", action="store_true",
|
|
9916
|
+
help="Force compact table layout regardless of terminal width.",
|
|
9917
|
+
)
|
|
9918
|
+
parser.add_argument(
|
|
9919
|
+
"--color", action="store_true",
|
|
9920
|
+
help="Accepted for drop-in compat; no-op today (no ANSI escapes are emitted).",
|
|
9921
|
+
)
|
|
9922
|
+
parser.add_argument(
|
|
9923
|
+
"--noColor", action="store_true", dest="no_color",
|
|
9924
|
+
help="Accepted for drop-in compat; no-op today (no ANSI escapes are emitted).",
|
|
9925
|
+
)
|
|
9926
|
+
parser.add_argument(
|
|
9927
|
+
"-O", "--offline", action=argparse.BooleanOptionalAction, default=False,
|
|
9928
|
+
help="Accepted for drop-in compat with ccusage-codex; we are always offline.",
|
|
9929
|
+
)
|
|
9930
|
+
parser.add_argument(
|
|
9931
|
+
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
9932
|
+
help="Display timezone: local, utc, or IANA name. Overrides "
|
|
9933
|
+
"config display.tz for this call. Takes precedence over "
|
|
9934
|
+
"upstream's --timezone for drop-in parity.",
|
|
9935
|
+
)
|
|
9936
|
+
# Issue #92: codex parity for the #89 --debug surface. Codex JSONL
|
|
9937
|
+
# has no recorded costUSD to diff against, so the report is the
|
|
9938
|
+
# codex variant ("Codex Pricing Debug Report": totals + top-N
|
|
9939
|
+
# highest computed-cost entries), wired via
|
|
9940
|
+
# _emit_codex_debug_samples_if_set in each cmd_codex_* body.
|
|
9941
|
+
parser.add_argument(
|
|
9942
|
+
"-d", "--debug", action="store_true",
|
|
9943
|
+
help="Emit a stderr 'Codex Pricing Debug Report' (totals + "
|
|
9944
|
+
"the N highest computed-cost sample entries).",
|
|
9945
|
+
)
|
|
9946
|
+
parser.add_argument(
|
|
9947
|
+
"--debug-samples", type=_nonneg_int, default=5, metavar="N",
|
|
9948
|
+
help="Cap on top-entry sample rows in the --debug report "
|
|
9949
|
+
"(default 5; N=0 suppresses the sample block; "
|
|
9950
|
+
"negatives rejected at parse time).",
|
|
9951
|
+
)
|
|
9952
|
+
|
|
9953
|
+
|
|
9890
9954
|
def _add_share_args(parser, *, has_status_line: bool = False) -> None:
|
|
9891
9955
|
"""Attach shareable-reports flags + format/json mutex to a subparser.
|
|
9892
9956
|
|
|
@@ -10015,6 +10079,394 @@ def _share_validate_args(args) -> None:
|
|
|
10015
10079
|
sys.exit(2)
|
|
10016
10080
|
|
|
10017
10081
|
|
|
10082
|
+
def _build_daily_parser(subparsers, name, *, help_text, xref):
|
|
10083
|
+
"""Build the `daily` leaf parser (issue #86 Session B; routes to cmd_daily).
|
|
10084
|
+
|
|
10085
|
+
Build-once, register-twice: this body is the verbatim former inline `daily`
|
|
10086
|
+
construction, parameterized only by `name`, the parent-list `help_text`, and
|
|
10087
|
+
the `xref` appended to `description` (renders on `cctally <name> --help`).
|
|
10088
|
+
"""
|
|
10089
|
+
p = subparsers.add_parser(
|
|
10090
|
+
name,
|
|
10091
|
+
help=help_text,
|
|
10092
|
+
formatter_class=CLIHelpFormatter,
|
|
10093
|
+
description="Show usage grouped by date, matching upstream ccusage daily output."
|
|
10094
|
+
"\n\n" + xref,
|
|
10095
|
+
epilog=textwrap.dedent("""\
|
|
10096
|
+
Examples:
|
|
10097
|
+
cctally daily --since 20260414
|
|
10098
|
+
cctally daily --since 20260410 --until 20260416
|
|
10099
|
+
cctally daily --since 20260414 --breakdown
|
|
10100
|
+
cctally daily --since 20260414 --json
|
|
10101
|
+
cctally daily --order desc
|
|
10102
|
+
"""),
|
|
10103
|
+
)
|
|
10104
|
+
p.add_argument(
|
|
10105
|
+
"-s", "--since",
|
|
10106
|
+
default=None,
|
|
10107
|
+
metavar="YYYYMMDD",
|
|
10108
|
+
help="Filter from date (inclusive).",
|
|
10109
|
+
)
|
|
10110
|
+
p.add_argument(
|
|
10111
|
+
"-u", "--until",
|
|
10112
|
+
default=None,
|
|
10113
|
+
metavar="YYYYMMDD",
|
|
10114
|
+
help="Filter until date (inclusive).",
|
|
10115
|
+
)
|
|
10116
|
+
p.add_argument(
|
|
10117
|
+
"-b", "--breakdown",
|
|
10118
|
+
action="store_true",
|
|
10119
|
+
help="Show per-model cost breakdown sub-rows.",
|
|
10120
|
+
)
|
|
10121
|
+
p.add_argument(
|
|
10122
|
+
"-o", "--order",
|
|
10123
|
+
choices=("asc", "desc"),
|
|
10124
|
+
default="asc",
|
|
10125
|
+
help="Sort direction by date (default: asc).",
|
|
10126
|
+
)
|
|
10127
|
+
p.add_argument(
|
|
10128
|
+
"--reveal-projects",
|
|
10129
|
+
action="store_true",
|
|
10130
|
+
dest="reveal_projects",
|
|
10131
|
+
help="In --format output, show real project basenames instead of "
|
|
10132
|
+
"the default project-1, project-2, ... anonymization.",
|
|
10133
|
+
)
|
|
10134
|
+
p.add_argument(
|
|
10135
|
+
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10136
|
+
help="Display timezone: local, utc, or IANA name. "
|
|
10137
|
+
"Overrides config display.tz for this call.",
|
|
10138
|
+
)
|
|
10139
|
+
_add_ccusage_alias_args(p, ansi_emit=False)
|
|
10140
|
+
_add_share_args(p)
|
|
10141
|
+
p.set_defaults(func=cmd_daily)
|
|
10142
|
+
return p
|
|
10143
|
+
|
|
10144
|
+
|
|
10145
|
+
def _build_monthly_parser(subparsers, name, *, help_text, xref):
|
|
10146
|
+
"""Build the `monthly` leaf parser (issue #86 Session B; routes to cmd_monthly)."""
|
|
10147
|
+
p = subparsers.add_parser(
|
|
10148
|
+
name,
|
|
10149
|
+
help=help_text,
|
|
10150
|
+
formatter_class=CLIHelpFormatter,
|
|
10151
|
+
description="Show usage grouped by calendar month, matching upstream ccusage monthly output."
|
|
10152
|
+
"\n\n" + xref,
|
|
10153
|
+
epilog=textwrap.dedent("""\
|
|
10154
|
+
Examples:
|
|
10155
|
+
cctally monthly --since 20260101
|
|
10156
|
+
cctally monthly --since 20260101 --until 20260331
|
|
10157
|
+
cctally monthly --since 20260101 --breakdown
|
|
10158
|
+
cctally monthly --since 20260101 --json
|
|
10159
|
+
cctally monthly --order desc
|
|
10160
|
+
"""),
|
|
10161
|
+
)
|
|
10162
|
+
p.add_argument(
|
|
10163
|
+
"-s", "--since",
|
|
10164
|
+
default=None,
|
|
10165
|
+
metavar="YYYYMMDD",
|
|
10166
|
+
help="Filter from date (inclusive).",
|
|
10167
|
+
)
|
|
10168
|
+
p.add_argument(
|
|
10169
|
+
"-u", "--until",
|
|
10170
|
+
default=None,
|
|
10171
|
+
metavar="YYYYMMDD",
|
|
10172
|
+
help="Filter until date (inclusive).",
|
|
10173
|
+
)
|
|
10174
|
+
p.add_argument(
|
|
10175
|
+
"-b", "--breakdown",
|
|
10176
|
+
action="store_true",
|
|
10177
|
+
help="Show per-model cost breakdown sub-rows.",
|
|
10178
|
+
)
|
|
10179
|
+
p.add_argument(
|
|
10180
|
+
"-o", "--order",
|
|
10181
|
+
choices=("asc", "desc"),
|
|
10182
|
+
default="asc",
|
|
10183
|
+
help="Sort direction by month (default: asc).",
|
|
10184
|
+
)
|
|
10185
|
+
p.add_argument(
|
|
10186
|
+
"--reveal-projects",
|
|
10187
|
+
action="store_true",
|
|
10188
|
+
dest="reveal_projects",
|
|
10189
|
+
help="In --format output, show real project basenames instead of "
|
|
10190
|
+
"the default project-1, project-2, ... anonymization.",
|
|
10191
|
+
)
|
|
10192
|
+
p.add_argument(
|
|
10193
|
+
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10194
|
+
help="Display timezone: local, utc, or IANA name. "
|
|
10195
|
+
"Overrides config display.tz for this call.",
|
|
10196
|
+
)
|
|
10197
|
+
_add_ccusage_alias_args(p, ansi_emit=False)
|
|
10198
|
+
_add_share_args(p)
|
|
10199
|
+
p.set_defaults(func=cmd_monthly)
|
|
10200
|
+
return p
|
|
10201
|
+
|
|
10202
|
+
|
|
10203
|
+
def _build_weekly_parser(subparsers, name, *, help_text, xref):
|
|
10204
|
+
"""Build the `weekly` leaf parser (issue #86 Session B; routes to cmd_weekly)."""
|
|
10205
|
+
p = subparsers.add_parser(
|
|
10206
|
+
name,
|
|
10207
|
+
help=help_text,
|
|
10208
|
+
formatter_class=CLIHelpFormatter,
|
|
10209
|
+
description="Show Claude usage grouped by subscription week. Boundaries are anchored "
|
|
10210
|
+
"to weekly_usage_snapshots.week_start_at with 7-day-cadence extrapolation "
|
|
10211
|
+
"for pre-snapshot history. Columns extend daily/monthly's set with Used % "
|
|
10212
|
+
"and $/1%."
|
|
10213
|
+
"\n\n" + xref,
|
|
10214
|
+
epilog=textwrap.dedent("""\
|
|
10215
|
+
Examples:
|
|
10216
|
+
cctally weekly
|
|
10217
|
+
cctally weekly --since 20260101
|
|
10218
|
+
cctally weekly --breakdown
|
|
10219
|
+
cctally weekly --json
|
|
10220
|
+
cctally weekly --order desc
|
|
10221
|
+
"""),
|
|
10222
|
+
)
|
|
10223
|
+
p.add_argument("-s", "--since", default=None, metavar="YYYYMMDD",
|
|
10224
|
+
help="Filter from date (inclusive).")
|
|
10225
|
+
p.add_argument("-u", "--until", default=None, metavar="YYYYMMDD",
|
|
10226
|
+
help="Filter until date (inclusive).")
|
|
10227
|
+
p.add_argument("-b", "--breakdown", action="store_true",
|
|
10228
|
+
help="Show per-model cost breakdown sub-rows.")
|
|
10229
|
+
p.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
10230
|
+
help="Sort direction by week (default: asc).")
|
|
10231
|
+
p.add_argument("--reveal-projects", action="store_true", dest="reveal_projects",
|
|
10232
|
+
help="In --format output, show real project basenames instead of "
|
|
10233
|
+
"the default project-1, project-2, ... anonymization.")
|
|
10234
|
+
p.add_argument("--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10235
|
+
help="Display timezone: local, utc, or IANA name. "
|
|
10236
|
+
"Overrides config display.tz for this call.")
|
|
10237
|
+
_add_ccusage_alias_args(p, ansi_emit=False)
|
|
10238
|
+
_add_share_args(p)
|
|
10239
|
+
p.set_defaults(func=cmd_weekly)
|
|
10240
|
+
return p
|
|
10241
|
+
|
|
10242
|
+
|
|
10243
|
+
def _build_session_parser(subparsers, name, *, help_text, xref):
|
|
10244
|
+
"""Build the `session` leaf parser (issue #86 Session B; routes to cmd_session)."""
|
|
10245
|
+
p = subparsers.add_parser(
|
|
10246
|
+
name,
|
|
10247
|
+
help=help_text,
|
|
10248
|
+
formatter_class=CLIHelpFormatter,
|
|
10249
|
+
description="Show Claude usage grouped by JSONL sessionId. Resumed sessions (same "
|
|
10250
|
+
"sessionId across multiple files) collapse into one row. 11-column "
|
|
10251
|
+
"layout paralleling codex-session."
|
|
10252
|
+
"\n\n" + xref,
|
|
10253
|
+
epilog=textwrap.dedent("""\
|
|
10254
|
+
Examples:
|
|
10255
|
+
cctally session
|
|
10256
|
+
cctally session --since 20260401
|
|
10257
|
+
cctally session --since 20260401 --breakdown
|
|
10258
|
+
cctally session --json
|
|
10259
|
+
cctally session --order desc
|
|
10260
|
+
"""),
|
|
10261
|
+
)
|
|
10262
|
+
p.add_argument("-s", "--since", default=None, metavar="YYYYMMDD",
|
|
10263
|
+
help="Filter from date (inclusive).")
|
|
10264
|
+
p.add_argument("-u", "--until", default=None, metavar="YYYYMMDD",
|
|
10265
|
+
help="Filter until date (inclusive).")
|
|
10266
|
+
p.add_argument("-b", "--breakdown", action="store_true",
|
|
10267
|
+
help="Show per-model cost breakdown sub-rows.")
|
|
10268
|
+
p.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
10269
|
+
help="Sort direction by last activity (default: asc — earliest first).")
|
|
10270
|
+
p.add_argument("--reveal-projects", action="store_true", dest="reveal_projects",
|
|
10271
|
+
help="In --format output, show real project basenames instead of "
|
|
10272
|
+
"the default project-1, project-2, ... anonymization.")
|
|
10273
|
+
p.add_argument("--top-n", type=int, default=15, dest="top_n",
|
|
10274
|
+
metavar="N",
|
|
10275
|
+
help="In --format output, cap rows to top N by cost (default: 15). "
|
|
10276
|
+
"Must be >= 1; values above 50 emit a readability warning. "
|
|
10277
|
+
"Has no effect on terminal/JSON output.")
|
|
10278
|
+
p.add_argument("--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10279
|
+
help="Display timezone: local, utc, or IANA name. "
|
|
10280
|
+
"Overrides config display.tz for this call.")
|
|
10281
|
+
p.add_argument(
|
|
10282
|
+
"-i", "--id", default=None, metavar="SESSION_ID", dest="id",
|
|
10283
|
+
help="Filter to a single session by exact-string sessionId. "
|
|
10284
|
+
"Match is against the post-resume-merge id (sessions "
|
|
10285
|
+
"resumed across multiple JSONL files collapse to one id). "
|
|
10286
|
+
"Unknown id → exit 0 with the empty-render branch.",
|
|
10287
|
+
)
|
|
10288
|
+
_add_ccusage_alias_args(p, ansi_emit=False)
|
|
10289
|
+
_add_share_args(p)
|
|
10290
|
+
p.set_defaults(func=cmd_session)
|
|
10291
|
+
return p
|
|
10292
|
+
|
|
10293
|
+
|
|
10294
|
+
def _build_blocks_parser(subparsers, name, *, help_text, xref):
|
|
10295
|
+
"""Build the `blocks` leaf parser (issue #86 Session B; routes to cmd_blocks).
|
|
10296
|
+
|
|
10297
|
+
Note: `blocks` intentionally has NO `_add_share_args` (matches the former
|
|
10298
|
+
inline block — it is not part of the shareable-output flag surface).
|
|
10299
|
+
"""
|
|
10300
|
+
p = subparsers.add_parser(
|
|
10301
|
+
name,
|
|
10302
|
+
help=help_text,
|
|
10303
|
+
formatter_class=CLIHelpFormatter,
|
|
10304
|
+
description="Show usage grouped by 5-hour session blocks, matching upstream ccusage blocks output."
|
|
10305
|
+
"\n\n" + xref,
|
|
10306
|
+
epilog=textwrap.dedent("""\
|
|
10307
|
+
Examples:
|
|
10308
|
+
cctally blocks --since 20260414
|
|
10309
|
+
cctally blocks --since 20260410 --until 20260416
|
|
10310
|
+
cctally blocks --since 20260414 --breakdown
|
|
10311
|
+
cctally blocks --since 20260414 --json
|
|
10312
|
+
"""),
|
|
10313
|
+
)
|
|
10314
|
+
p.add_argument(
|
|
10315
|
+
"-s", "--since",
|
|
10316
|
+
default=None,
|
|
10317
|
+
metavar="YYYYMMDD",
|
|
10318
|
+
help="Filter from date (inclusive).",
|
|
10319
|
+
)
|
|
10320
|
+
p.add_argument(
|
|
10321
|
+
"-u", "--until",
|
|
10322
|
+
default=None,
|
|
10323
|
+
metavar="YYYYMMDD",
|
|
10324
|
+
help="Filter until date (inclusive).",
|
|
10325
|
+
)
|
|
10326
|
+
p.add_argument(
|
|
10327
|
+
"-b", "--breakdown",
|
|
10328
|
+
action="store_true",
|
|
10329
|
+
help="Show per-model cost breakdown.",
|
|
10330
|
+
)
|
|
10331
|
+
p.add_argument(
|
|
10332
|
+
"--json",
|
|
10333
|
+
action="store_true",
|
|
10334
|
+
dest="json",
|
|
10335
|
+
help="Output JSON matching upstream ccusage blocks format.",
|
|
10336
|
+
)
|
|
10337
|
+
p.add_argument(
|
|
10338
|
+
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10339
|
+
help="Display timezone: local, utc, or IANA name. "
|
|
10340
|
+
"Overrides config display.tz for this call.",
|
|
10341
|
+
)
|
|
10342
|
+
_add_ccusage_alias_args(p, ansi_emit=False)
|
|
10343
|
+
p.set_defaults(func=cmd_blocks)
|
|
10344
|
+
return p
|
|
10345
|
+
|
|
10346
|
+
|
|
10347
|
+
def _build_codex_daily_parser(subparsers, name, *, help_text, xref):
|
|
10348
|
+
"""Build the `codex-daily` leaf parser (issue #86 Session B; routes to cmd_codex_daily)."""
|
|
10349
|
+
p = subparsers.add_parser(
|
|
10350
|
+
name,
|
|
10351
|
+
help=help_text,
|
|
10352
|
+
formatter_class=CLIHelpFormatter,
|
|
10353
|
+
description="Show Codex usage grouped by date, matching upstream ccusage-codex daily output."
|
|
10354
|
+
"\n\n" + xref,
|
|
10355
|
+
epilog=textwrap.dedent("""\
|
|
10356
|
+
Examples:
|
|
10357
|
+
cctally codex-daily --since 20260401
|
|
10358
|
+
cctally codex-daily --since 20260401 --breakdown
|
|
10359
|
+
cctally codex-daily --since 20260401 --json
|
|
10360
|
+
cctally codex-daily --order desc
|
|
10361
|
+
"""),
|
|
10362
|
+
)
|
|
10363
|
+
p.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
10364
|
+
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10365
|
+
p.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
10366
|
+
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10367
|
+
p.add_argument("-b", "--breakdown", action="store_true",
|
|
10368
|
+
help="Show per-model cost breakdown sub-rows.")
|
|
10369
|
+
p.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
10370
|
+
help="Sort direction by date (default: asc).")
|
|
10371
|
+
p.add_argument("--json", action="store_true", dest="json",
|
|
10372
|
+
help="Output JSON matching upstream ccusage-codex daily format.")
|
|
10373
|
+
_add_codex_shared_args(p)
|
|
10374
|
+
p.set_defaults(func=cmd_codex_daily)
|
|
10375
|
+
return p
|
|
10376
|
+
|
|
10377
|
+
|
|
10378
|
+
def _build_codex_monthly_parser(subparsers, name, *, help_text, xref):
|
|
10379
|
+
"""Build the `codex-monthly` leaf parser (issue #86 Session B; routes to cmd_codex_monthly)."""
|
|
10380
|
+
p = subparsers.add_parser(
|
|
10381
|
+
name,
|
|
10382
|
+
help=help_text,
|
|
10383
|
+
formatter_class=CLIHelpFormatter,
|
|
10384
|
+
description="Show Codex usage grouped by calendar month, matching upstream ccusage-codex monthly output."
|
|
10385
|
+
"\n\n" + xref,
|
|
10386
|
+
epilog=textwrap.dedent("""\
|
|
10387
|
+
Examples:
|
|
10388
|
+
cctally codex-monthly --since 20260101
|
|
10389
|
+
cctally codex-monthly --breakdown
|
|
10390
|
+
cctally codex-monthly --json
|
|
10391
|
+
"""),
|
|
10392
|
+
)
|
|
10393
|
+
p.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
10394
|
+
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10395
|
+
p.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
10396
|
+
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10397
|
+
p.add_argument("-b", "--breakdown", action="store_true",
|
|
10398
|
+
help="Show per-model cost breakdown sub-rows.")
|
|
10399
|
+
p.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
10400
|
+
help="Sort direction by month (default: asc).")
|
|
10401
|
+
p.add_argument("--json", action="store_true", dest="json",
|
|
10402
|
+
help="Output JSON matching upstream ccusage-codex monthly format.")
|
|
10403
|
+
_add_codex_shared_args(p)
|
|
10404
|
+
p.set_defaults(func=cmd_codex_monthly)
|
|
10405
|
+
return p
|
|
10406
|
+
|
|
10407
|
+
|
|
10408
|
+
def _build_codex_weekly_parser(subparsers, name, *, help_text, xref):
|
|
10409
|
+
"""Build the `codex-weekly` leaf parser (issue #86 Session B; routes to cmd_codex_weekly)."""
|
|
10410
|
+
p = subparsers.add_parser(
|
|
10411
|
+
name,
|
|
10412
|
+
help=help_text,
|
|
10413
|
+
formatter_class=CLIHelpFormatter,
|
|
10414
|
+
description="Show Codex usage grouped by week. Week-start day is read from config.json "
|
|
10415
|
+
"(collector.week_start, Monday default). Not a ccusage-codex drop-in — "
|
|
10416
|
+
"upstream has no `codex weekly` command."
|
|
10417
|
+
"\n\n" + xref,
|
|
10418
|
+
epilog=textwrap.dedent("""\
|
|
10419
|
+
Examples:
|
|
10420
|
+
cctally codex-weekly
|
|
10421
|
+
cctally codex-weekly --since 20260301
|
|
10422
|
+
cctally codex-weekly --breakdown
|
|
10423
|
+
cctally codex-weekly --json
|
|
10424
|
+
cctally codex-weekly --order desc
|
|
10425
|
+
"""),
|
|
10426
|
+
)
|
|
10427
|
+
p.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
10428
|
+
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10429
|
+
p.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
10430
|
+
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10431
|
+
p.add_argument("-b", "--breakdown", action="store_true",
|
|
10432
|
+
help="Show per-model cost breakdown sub-rows.")
|
|
10433
|
+
p.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
10434
|
+
help="Sort direction by week (default: asc).")
|
|
10435
|
+
p.add_argument("--json", action="store_true", dest="json",
|
|
10436
|
+
help="Output JSON.")
|
|
10437
|
+
_add_codex_shared_args(p)
|
|
10438
|
+
p.set_defaults(func=cmd_codex_weekly)
|
|
10439
|
+
return p
|
|
10440
|
+
|
|
10441
|
+
|
|
10442
|
+
def _build_codex_session_parser(subparsers, name, *, help_text, xref):
|
|
10443
|
+
"""Build the `codex-session` leaf parser (issue #86 Session B; routes to cmd_codex_session)."""
|
|
10444
|
+
p = subparsers.add_parser(
|
|
10445
|
+
name,
|
|
10446
|
+
help=help_text,
|
|
10447
|
+
formatter_class=CLIHelpFormatter,
|
|
10448
|
+
description="Show Codex usage grouped by session, matching upstream ccusage-codex session output."
|
|
10449
|
+
"\n\n" + xref,
|
|
10450
|
+
epilog=textwrap.dedent("""\
|
|
10451
|
+
Examples:
|
|
10452
|
+
cctally codex-session
|
|
10453
|
+
cctally codex-session --since 20260401
|
|
10454
|
+
cctally codex-session --json
|
|
10455
|
+
"""),
|
|
10456
|
+
)
|
|
10457
|
+
p.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
10458
|
+
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10459
|
+
p.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
10460
|
+
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
10461
|
+
p.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
10462
|
+
help="Sort direction by last activity (default: asc — earliest first).")
|
|
10463
|
+
p.add_argument("--json", action="store_true", dest="json",
|
|
10464
|
+
help="Output JSON matching upstream ccusage-codex session format.")
|
|
10465
|
+
_add_codex_shared_args(p)
|
|
10466
|
+
p.set_defaults(func=cmd_codex_session)
|
|
10467
|
+
return p
|
|
10468
|
+
|
|
10469
|
+
|
|
10018
10470
|
def build_parser() -> argparse.ArgumentParser:
|
|
10019
10471
|
p = argparse.ArgumentParser(
|
|
10020
10472
|
prog="cctally",
|
|
@@ -10747,49 +11199,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
10747
11199
|
rc.set_defaults(func=cmd_range_cost)
|
|
10748
11200
|
|
|
10749
11201
|
# -- blocks --
|
|
10750
|
-
|
|
10751
|
-
"blocks",
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
description="Show usage grouped by 5-hour session blocks, matching upstream ccusage blocks output.",
|
|
10755
|
-
epilog=textwrap.dedent("""\
|
|
10756
|
-
Examples:
|
|
10757
|
-
cctally blocks --since 20260414
|
|
10758
|
-
cctally blocks --since 20260410 --until 20260416
|
|
10759
|
-
cctally blocks --since 20260414 --breakdown
|
|
10760
|
-
cctally blocks --since 20260414 --json
|
|
10761
|
-
"""),
|
|
10762
|
-
)
|
|
10763
|
-
bl.add_argument(
|
|
10764
|
-
"-s", "--since",
|
|
10765
|
-
default=None,
|
|
10766
|
-
metavar="YYYYMMDD",
|
|
10767
|
-
help="Filter from date (inclusive).",
|
|
10768
|
-
)
|
|
10769
|
-
bl.add_argument(
|
|
10770
|
-
"-u", "--until",
|
|
10771
|
-
default=None,
|
|
10772
|
-
metavar="YYYYMMDD",
|
|
10773
|
-
help="Filter until date (inclusive).",
|
|
10774
|
-
)
|
|
10775
|
-
bl.add_argument(
|
|
10776
|
-
"-b", "--breakdown",
|
|
10777
|
-
action="store_true",
|
|
10778
|
-
help="Show per-model cost breakdown.",
|
|
10779
|
-
)
|
|
10780
|
-
bl.add_argument(
|
|
10781
|
-
"--json",
|
|
10782
|
-
action="store_true",
|
|
10783
|
-
dest="json",
|
|
10784
|
-
help="Output JSON matching upstream ccusage blocks format.",
|
|
10785
|
-
)
|
|
10786
|
-
bl.add_argument(
|
|
10787
|
-
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10788
|
-
help="Display timezone: local, utc, or IANA name. "
|
|
10789
|
-
"Overrides config display.tz for this call.",
|
|
10790
|
-
)
|
|
10791
|
-
_add_ccusage_alias_args(bl, ansi_emit=False)
|
|
10792
|
-
bl.set_defaults(func=cmd_blocks)
|
|
11202
|
+
_build_blocks_parser(
|
|
11203
|
+
sub, "blocks",
|
|
11204
|
+
help_text="Show usage report grouped by 5-hour session blocks",
|
|
11205
|
+
xref="Alias of `cctally claude blocks` (the canonical form).")
|
|
10793
11206
|
|
|
10794
11207
|
# -- five-hour-blocks --
|
|
10795
11208
|
fhb = sub.add_parser(
|
|
@@ -10875,319 +11288,46 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
10875
11288
|
p_cache_sync.set_defaults(func=cmd_cache_sync)
|
|
10876
11289
|
|
|
10877
11290
|
# -- daily --
|
|
10878
|
-
|
|
10879
|
-
"daily",
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
description="Show usage grouped by date, matching upstream ccusage daily output.",
|
|
10883
|
-
epilog=textwrap.dedent("""\
|
|
10884
|
-
Examples:
|
|
10885
|
-
cctally daily --since 20260414
|
|
10886
|
-
cctally daily --since 20260410 --until 20260416
|
|
10887
|
-
cctally daily --since 20260414 --breakdown
|
|
10888
|
-
cctally daily --since 20260414 --json
|
|
10889
|
-
cctally daily --order desc
|
|
10890
|
-
"""),
|
|
10891
|
-
)
|
|
10892
|
-
dy.add_argument(
|
|
10893
|
-
"-s", "--since",
|
|
10894
|
-
default=None,
|
|
10895
|
-
metavar="YYYYMMDD",
|
|
10896
|
-
help="Filter from date (inclusive).",
|
|
10897
|
-
)
|
|
10898
|
-
dy.add_argument(
|
|
10899
|
-
"-u", "--until",
|
|
10900
|
-
default=None,
|
|
10901
|
-
metavar="YYYYMMDD",
|
|
10902
|
-
help="Filter until date (inclusive).",
|
|
10903
|
-
)
|
|
10904
|
-
dy.add_argument(
|
|
10905
|
-
"-b", "--breakdown",
|
|
10906
|
-
action="store_true",
|
|
10907
|
-
help="Show per-model cost breakdown sub-rows.",
|
|
10908
|
-
)
|
|
10909
|
-
dy.add_argument(
|
|
10910
|
-
"-o", "--order",
|
|
10911
|
-
choices=("asc", "desc"),
|
|
10912
|
-
default="asc",
|
|
10913
|
-
help="Sort direction by date (default: asc).",
|
|
10914
|
-
)
|
|
10915
|
-
dy.add_argument(
|
|
10916
|
-
"--reveal-projects",
|
|
10917
|
-
action="store_true",
|
|
10918
|
-
dest="reveal_projects",
|
|
10919
|
-
help="In --format output, show real project basenames instead of "
|
|
10920
|
-
"the default project-1, project-2, ... anonymization.",
|
|
10921
|
-
)
|
|
10922
|
-
dy.add_argument(
|
|
10923
|
-
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10924
|
-
help="Display timezone: local, utc, or IANA name. "
|
|
10925
|
-
"Overrides config display.tz for this call.",
|
|
10926
|
-
)
|
|
10927
|
-
_add_ccusage_alias_args(dy, ansi_emit=False)
|
|
10928
|
-
_add_share_args(dy)
|
|
10929
|
-
dy.set_defaults(func=cmd_daily)
|
|
11291
|
+
_build_daily_parser(
|
|
11292
|
+
sub, "daily",
|
|
11293
|
+
help_text="Show usage report grouped by date",
|
|
11294
|
+
xref="Alias of `cctally claude daily` (the canonical form).")
|
|
10930
11295
|
|
|
10931
11296
|
# -- monthly --
|
|
10932
|
-
|
|
10933
|
-
"monthly",
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
description="Show usage grouped by calendar month, matching upstream ccusage monthly output.",
|
|
10937
|
-
epilog=textwrap.dedent("""\
|
|
10938
|
-
Examples:
|
|
10939
|
-
cctally monthly --since 20260101
|
|
10940
|
-
cctally monthly --since 20260101 --until 20260331
|
|
10941
|
-
cctally monthly --since 20260101 --breakdown
|
|
10942
|
-
cctally monthly --since 20260101 --json
|
|
10943
|
-
cctally monthly --order desc
|
|
10944
|
-
"""),
|
|
10945
|
-
)
|
|
10946
|
-
mo.add_argument(
|
|
10947
|
-
"-s", "--since",
|
|
10948
|
-
default=None,
|
|
10949
|
-
metavar="YYYYMMDD",
|
|
10950
|
-
help="Filter from date (inclusive).",
|
|
10951
|
-
)
|
|
10952
|
-
mo.add_argument(
|
|
10953
|
-
"-u", "--until",
|
|
10954
|
-
default=None,
|
|
10955
|
-
metavar="YYYYMMDD",
|
|
10956
|
-
help="Filter until date (inclusive).",
|
|
10957
|
-
)
|
|
10958
|
-
mo.add_argument(
|
|
10959
|
-
"-b", "--breakdown",
|
|
10960
|
-
action="store_true",
|
|
10961
|
-
help="Show per-model cost breakdown sub-rows.",
|
|
10962
|
-
)
|
|
10963
|
-
mo.add_argument(
|
|
10964
|
-
"-o", "--order",
|
|
10965
|
-
choices=("asc", "desc"),
|
|
10966
|
-
default="asc",
|
|
10967
|
-
help="Sort direction by month (default: asc).",
|
|
10968
|
-
)
|
|
10969
|
-
mo.add_argument(
|
|
10970
|
-
"--reveal-projects",
|
|
10971
|
-
action="store_true",
|
|
10972
|
-
dest="reveal_projects",
|
|
10973
|
-
help="In --format output, show real project basenames instead of "
|
|
10974
|
-
"the default project-1, project-2, ... anonymization.",
|
|
10975
|
-
)
|
|
10976
|
-
mo.add_argument(
|
|
10977
|
-
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
10978
|
-
help="Display timezone: local, utc, or IANA name. "
|
|
10979
|
-
"Overrides config display.tz for this call.",
|
|
10980
|
-
)
|
|
10981
|
-
_add_ccusage_alias_args(mo, ansi_emit=False)
|
|
10982
|
-
_add_share_args(mo)
|
|
10983
|
-
mo.set_defaults(func=cmd_monthly)
|
|
11297
|
+
_build_monthly_parser(
|
|
11298
|
+
sub, "monthly",
|
|
11299
|
+
help_text="Show usage report grouped by month",
|
|
11300
|
+
xref="Alias of `cctally claude monthly` (the canonical form).")
|
|
10984
11301
|
|
|
10985
11302
|
# -- weekly --
|
|
10986
|
-
|
|
10987
|
-
"weekly",
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
description="Show Claude usage grouped by subscription week. Boundaries are anchored "
|
|
10991
|
-
"to weekly_usage_snapshots.week_start_at with 7-day-cadence extrapolation "
|
|
10992
|
-
"for pre-snapshot history. Columns extend daily/monthly's set with Used % "
|
|
10993
|
-
"and $/1%.",
|
|
10994
|
-
epilog=textwrap.dedent("""\
|
|
10995
|
-
Examples:
|
|
10996
|
-
cctally weekly
|
|
10997
|
-
cctally weekly --since 20260101
|
|
10998
|
-
cctally weekly --breakdown
|
|
10999
|
-
cctally weekly --json
|
|
11000
|
-
cctally weekly --order desc
|
|
11001
|
-
"""),
|
|
11002
|
-
)
|
|
11003
|
-
we.add_argument("-s", "--since", default=None, metavar="YYYYMMDD",
|
|
11004
|
-
help="Filter from date (inclusive).")
|
|
11005
|
-
we.add_argument("-u", "--until", default=None, metavar="YYYYMMDD",
|
|
11006
|
-
help="Filter until date (inclusive).")
|
|
11007
|
-
we.add_argument("-b", "--breakdown", action="store_true",
|
|
11008
|
-
help="Show per-model cost breakdown sub-rows.")
|
|
11009
|
-
we.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
11010
|
-
help="Sort direction by week (default: asc).")
|
|
11011
|
-
we.add_argument("--reveal-projects", action="store_true", dest="reveal_projects",
|
|
11012
|
-
help="In --format output, show real project basenames instead of "
|
|
11013
|
-
"the default project-1, project-2, ... anonymization.")
|
|
11014
|
-
we.add_argument("--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
11015
|
-
help="Display timezone: local, utc, or IANA name. "
|
|
11016
|
-
"Overrides config display.tz for this call.")
|
|
11017
|
-
_add_ccusage_alias_args(we, ansi_emit=False)
|
|
11018
|
-
_add_share_args(we)
|
|
11019
|
-
we.set_defaults(func=cmd_weekly)
|
|
11020
|
-
|
|
11021
|
-
# -- codex shared args helper --
|
|
11022
|
-
def _add_codex_shared_args(parser: argparse.ArgumentParser) -> None:
|
|
11023
|
-
"""Register upstream `ccusage-codex sharedArgs` on a codex subparser.
|
|
11024
|
-
|
|
11025
|
-
Upstream sharedArgs (node_modules/@ccusage/codex/dist/index.js):
|
|
11026
|
-
--timezone/-z, --locale/-l, --compact, --color, --noColor,
|
|
11027
|
-
--offline/--no-offline.
|
|
11028
|
-
|
|
11029
|
-
Honored here: --timezone (dates + aggregation buckets) and
|
|
11030
|
-
--compact (table layout). Accepted-but-no-op (stored on the
|
|
11031
|
-
namespace for drop-in parity with upstream scripts): --locale
|
|
11032
|
-
(we don't locale-format dates), --color / --noColor (we don't
|
|
11033
|
-
emit ANSI codes today). --offline is accepted as a no-op too
|
|
11034
|
-
(we are always offline); it uses BooleanOptionalAction so
|
|
11035
|
-
`--no-offline` also parses cleanly. `-O` is kept as the short
|
|
11036
|
-
form for offline for backward compat with earlier builds.
|
|
11037
|
-
"""
|
|
11038
|
-
parser.add_argument(
|
|
11039
|
-
"-z", "--timezone", default=None, metavar="TZ",
|
|
11040
|
-
help="IANA timezone for date bucketing and Date/Last Activity cells.",
|
|
11041
|
-
)
|
|
11042
|
-
parser.add_argument(
|
|
11043
|
-
"-l", "--locale", default=None, metavar="LOCALE",
|
|
11044
|
-
help="Accepted for drop-in compat; no-op (dates are not locale-formatted).",
|
|
11045
|
-
)
|
|
11046
|
-
parser.add_argument(
|
|
11047
|
-
"--compact", action="store_true",
|
|
11048
|
-
help="Force compact table layout regardless of terminal width.",
|
|
11049
|
-
)
|
|
11050
|
-
parser.add_argument(
|
|
11051
|
-
"--color", action="store_true",
|
|
11052
|
-
help="Accepted for drop-in compat; no-op today (no ANSI escapes are emitted).",
|
|
11053
|
-
)
|
|
11054
|
-
parser.add_argument(
|
|
11055
|
-
"--noColor", action="store_true", dest="no_color",
|
|
11056
|
-
help="Accepted for drop-in compat; no-op today (no ANSI escapes are emitted).",
|
|
11057
|
-
)
|
|
11058
|
-
parser.add_argument(
|
|
11059
|
-
"-O", "--offline", action=argparse.BooleanOptionalAction, default=False,
|
|
11060
|
-
help="Accepted for drop-in compat with ccusage-codex; we are always offline.",
|
|
11061
|
-
)
|
|
11062
|
-
parser.add_argument(
|
|
11063
|
-
"--tz", default=None, type=_argparse_tz, metavar="TZ",
|
|
11064
|
-
help="Display timezone: local, utc, or IANA name. Overrides "
|
|
11065
|
-
"config display.tz for this call. Takes precedence over "
|
|
11066
|
-
"upstream's --timezone for drop-in parity.",
|
|
11067
|
-
)
|
|
11068
|
-
# Issue #92: codex parity for the #89 --debug surface. Codex JSONL
|
|
11069
|
-
# has no recorded costUSD to diff against, so the report is the
|
|
11070
|
-
# codex variant ("Codex Pricing Debug Report": totals + top-N
|
|
11071
|
-
# highest computed-cost entries), wired via
|
|
11072
|
-
# _emit_codex_debug_samples_if_set in each cmd_codex_* body.
|
|
11073
|
-
parser.add_argument(
|
|
11074
|
-
"-d", "--debug", action="store_true",
|
|
11075
|
-
help="Emit a stderr 'Codex Pricing Debug Report' (totals + "
|
|
11076
|
-
"the N highest computed-cost sample entries).",
|
|
11077
|
-
)
|
|
11078
|
-
parser.add_argument(
|
|
11079
|
-
"--debug-samples", type=_nonneg_int, default=5, metavar="N",
|
|
11080
|
-
help="Cap on top-entry sample rows in the --debug report "
|
|
11081
|
-
"(default 5; N=0 suppresses the sample block; "
|
|
11082
|
-
"negatives rejected at parse time).",
|
|
11083
|
-
)
|
|
11303
|
+
_build_weekly_parser(
|
|
11304
|
+
sub, "weekly",
|
|
11305
|
+
help_text="Show usage grouped by subscription week (with Used %% and $/1%%)",
|
|
11306
|
+
xref="Alias of `cctally claude weekly` (the canonical form).")
|
|
11084
11307
|
|
|
11085
11308
|
# -- codex-daily --
|
|
11086
|
-
|
|
11087
|
-
"codex-daily",
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
description="Show Codex usage grouped by date, matching upstream ccusage-codex daily output.",
|
|
11091
|
-
epilog=textwrap.dedent("""\
|
|
11092
|
-
Examples:
|
|
11093
|
-
cctally codex-daily --since 20260401
|
|
11094
|
-
cctally codex-daily --since 20260401 --breakdown
|
|
11095
|
-
cctally codex-daily --since 20260401 --json
|
|
11096
|
-
cctally codex-daily --order desc
|
|
11097
|
-
"""),
|
|
11098
|
-
)
|
|
11099
|
-
cd.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
11100
|
-
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11101
|
-
cd.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
11102
|
-
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11103
|
-
cd.add_argument("-b", "--breakdown", action="store_true",
|
|
11104
|
-
help="Show per-model cost breakdown sub-rows.")
|
|
11105
|
-
cd.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
11106
|
-
help="Sort direction by date (default: asc).")
|
|
11107
|
-
cd.add_argument("--json", action="store_true", dest="json",
|
|
11108
|
-
help="Output JSON matching upstream ccusage-codex daily format.")
|
|
11109
|
-
_add_codex_shared_args(cd)
|
|
11110
|
-
cd.set_defaults(func=cmd_codex_daily)
|
|
11309
|
+
_build_codex_daily_parser(
|
|
11310
|
+
sub, "codex-daily",
|
|
11311
|
+
help_text="Show Codex usage report grouped by date (drop-in for `ccusage-codex daily`)",
|
|
11312
|
+
xref="Alias of `cctally codex daily` (the canonical form).")
|
|
11111
11313
|
|
|
11112
11314
|
# -- codex-monthly --
|
|
11113
|
-
|
|
11114
|
-
"codex-monthly",
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
description="Show Codex usage grouped by calendar month, matching upstream ccusage-codex monthly output.",
|
|
11118
|
-
epilog=textwrap.dedent("""\
|
|
11119
|
-
Examples:
|
|
11120
|
-
cctally codex-monthly --since 20260101
|
|
11121
|
-
cctally codex-monthly --breakdown
|
|
11122
|
-
cctally codex-monthly --json
|
|
11123
|
-
"""),
|
|
11124
|
-
)
|
|
11125
|
-
cmn.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
11126
|
-
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11127
|
-
cmn.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
11128
|
-
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11129
|
-
cmn.add_argument("-b", "--breakdown", action="store_true",
|
|
11130
|
-
help="Show per-model cost breakdown sub-rows.")
|
|
11131
|
-
cmn.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
11132
|
-
help="Sort direction by month (default: asc).")
|
|
11133
|
-
cmn.add_argument("--json", action="store_true", dest="json",
|
|
11134
|
-
help="Output JSON matching upstream ccusage-codex monthly format.")
|
|
11135
|
-
_add_codex_shared_args(cmn)
|
|
11136
|
-
cmn.set_defaults(func=cmd_codex_monthly)
|
|
11315
|
+
_build_codex_monthly_parser(
|
|
11316
|
+
sub, "codex-monthly",
|
|
11317
|
+
help_text="Show Codex usage grouped by month (drop-in for `ccusage-codex monthly`)",
|
|
11318
|
+
xref="Alias of `cctally codex monthly` (the canonical form).")
|
|
11137
11319
|
|
|
11138
11320
|
# -- codex-weekly --
|
|
11139
|
-
|
|
11140
|
-
"codex-weekly",
|
|
11141
|
-
|
|
11142
|
-
|
|
11143
|
-
description="Show Codex usage grouped by week. Week-start day is read from config.json "
|
|
11144
|
-
"(collector.week_start, Monday default). Not a ccusage-codex drop-in — "
|
|
11145
|
-
"upstream has no `codex weekly` command.",
|
|
11146
|
-
epilog=textwrap.dedent("""\
|
|
11147
|
-
Examples:
|
|
11148
|
-
cctally codex-weekly
|
|
11149
|
-
cctally codex-weekly --since 20260301
|
|
11150
|
-
cctally codex-weekly --breakdown
|
|
11151
|
-
cctally codex-weekly --json
|
|
11152
|
-
cctally codex-weekly --order desc
|
|
11153
|
-
"""),
|
|
11154
|
-
)
|
|
11155
|
-
cw.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
11156
|
-
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11157
|
-
cw.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
11158
|
-
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11159
|
-
cw.add_argument("-b", "--breakdown", action="store_true",
|
|
11160
|
-
help="Show per-model cost breakdown sub-rows.")
|
|
11161
|
-
cw.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
11162
|
-
help="Sort direction by week (default: asc).")
|
|
11163
|
-
cw.add_argument("--json", action="store_true", dest="json",
|
|
11164
|
-
help="Output JSON.")
|
|
11165
|
-
_add_codex_shared_args(cw)
|
|
11166
|
-
cw.set_defaults(func=cmd_codex_weekly)
|
|
11321
|
+
_build_codex_weekly_parser(
|
|
11322
|
+
sub, "codex-weekly",
|
|
11323
|
+
help_text="Show Codex usage grouped by week (week-start from config.json)",
|
|
11324
|
+
xref="Alias of `cctally codex weekly` (the canonical form).")
|
|
11167
11325
|
|
|
11168
11326
|
# -- codex-session --
|
|
11169
|
-
|
|
11170
|
-
"codex-session",
|
|
11171
|
-
|
|
11172
|
-
|
|
11173
|
-
description="Show Codex usage grouped by session, matching upstream ccusage-codex session output.",
|
|
11174
|
-
epilog=textwrap.dedent("""\
|
|
11175
|
-
Examples:
|
|
11176
|
-
cctally codex-session
|
|
11177
|
-
cctally codex-session --since 20260401
|
|
11178
|
-
cctally codex-session --json
|
|
11179
|
-
"""),
|
|
11180
|
-
)
|
|
11181
|
-
cs.add_argument("-s", "--since", default=None, metavar="YYYY-MM-DD",
|
|
11182
|
-
help="Filter from date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11183
|
-
cs.add_argument("-u", "--until", default=None, metavar="YYYY-MM-DD",
|
|
11184
|
-
help="Filter until date (inclusive; accepts YYYY-MM-DD or YYYYMMDD).")
|
|
11185
|
-
cs.add_argument("-o", "--order", choices=("asc", "desc"), default="asc",
|
|
11186
|
-
help="Sort direction by last activity (default: asc — earliest first).")
|
|
11187
|
-
cs.add_argument("--json", action="store_true", dest="json",
|
|
11188
|
-
help="Output JSON matching upstream ccusage-codex session format.")
|
|
11189
|
-
_add_codex_shared_args(cs)
|
|
11190
|
-
cs.set_defaults(func=cmd_codex_session)
|
|
11327
|
+
_build_codex_session_parser(
|
|
11328
|
+
sub, "codex-session",
|
|
11329
|
+
help_text="Show Codex usage grouped by session (drop-in for `ccusage-codex session`)",
|
|
11330
|
+
xref="Alias of `cctally codex session` (the canonical form).")
|
|
11191
11331
|
|
|
11192
11332
|
# -- project --
|
|
11193
11333
|
p_project = sub.add_parser(
|
|
@@ -11285,51 +11425,62 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
11285
11425
|
diff_p.set_defaults(func=cmd_diff)
|
|
11286
11426
|
|
|
11287
11427
|
# -- session --
|
|
11288
|
-
|
|
11289
|
-
"session",
|
|
11290
|
-
|
|
11428
|
+
_build_session_parser(
|
|
11429
|
+
sub, "session",
|
|
11430
|
+
help_text="Show Claude usage grouped by sessionId (merges resumed-across-files sessions)",
|
|
11431
|
+
xref="Alias of `cctally claude session` (the canonical form).")
|
|
11432
|
+
|
|
11433
|
+
# --- `claude` subgroup (drop-in for `ccusage claude …`); issue #86 Session B ---
|
|
11434
|
+
# Build-once, register-twice: these reuse the same nine builders as the flat
|
|
11435
|
+
# forms above. Nested subparsers reuse dest="command" so args.command resolves
|
|
11436
|
+
# to the leaf name (e.g. "blocks"), keeping banner suppression byte-identical
|
|
11437
|
+
# to the flat form with zero hook-path changes.
|
|
11438
|
+
claude_p = sub.add_parser(
|
|
11439
|
+
"claude",
|
|
11440
|
+
help="Claude-source reports (drop-in for `ccusage claude …`)",
|
|
11291
11441
|
formatter_class=CLIHelpFormatter,
|
|
11292
|
-
description="
|
|
11293
|
-
"
|
|
11294
|
-
"
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11442
|
+
description="Claude-source usage reports. Each subcommand is a drop-in for the "
|
|
11443
|
+
"matching `ccusage claude <cmd>` and shares its engine with the "
|
|
11444
|
+
"top-level `cctally <cmd>` alias.")
|
|
11445
|
+
claude_sub = claude_p.add_subparsers(dest="command", required=True, metavar="<command>")
|
|
11446
|
+
_build_daily_parser(claude_sub, "daily",
|
|
11447
|
+
help_text="Show usage grouped by date",
|
|
11448
|
+
xref="Drop-in for `ccusage claude daily`. Same engine as `cctally daily`.")
|
|
11449
|
+
_build_monthly_parser(claude_sub, "monthly",
|
|
11450
|
+
help_text="Show usage grouped by month",
|
|
11451
|
+
xref="Drop-in for `ccusage claude monthly`. Same engine as `cctally monthly`.")
|
|
11452
|
+
_build_weekly_parser(claude_sub, "weekly",
|
|
11453
|
+
help_text="Show usage grouped by subscription week",
|
|
11454
|
+
xref="Drop-in for `ccusage claude weekly`. Same engine as `cctally weekly`.")
|
|
11455
|
+
_build_session_parser(claude_sub, "session",
|
|
11456
|
+
help_text="Show usage grouped by session",
|
|
11457
|
+
xref="Drop-in for `ccusage claude session`. Same engine as `cctally session`.")
|
|
11458
|
+
_build_blocks_parser(claude_sub, "blocks",
|
|
11459
|
+
help_text="Show usage grouped by 5-hour session blocks",
|
|
11460
|
+
xref="Drop-in for `ccusage claude blocks`. Same engine as `cctally blocks`.")
|
|
11461
|
+
|
|
11462
|
+
# --- `codex` subgroup (drop-in for `ccusage codex …`); issue #86 Session B ---
|
|
11463
|
+
codex_p = sub.add_parser(
|
|
11464
|
+
"codex",
|
|
11465
|
+
help="Codex-source reports (drop-in for `ccusage codex …`)",
|
|
11466
|
+
formatter_class=CLIHelpFormatter,
|
|
11467
|
+
description="Codex-source usage reports. daily/monthly/session are drop-ins for "
|
|
11468
|
+
"`ccusage codex <cmd>`; weekly is a cctally extension. Each shares its "
|
|
11469
|
+
"engine with the matching `cctally codex-<cmd>` alias.")
|
|
11470
|
+
codex_sub = codex_p.add_subparsers(dest="command", required=True, metavar="<command>")
|
|
11471
|
+
_build_codex_daily_parser(codex_sub, "daily",
|
|
11472
|
+
help_text="Show Codex usage grouped by date",
|
|
11473
|
+
xref="Drop-in for `ccusage codex daily`. Same engine as `cctally codex-daily`.")
|
|
11474
|
+
_build_codex_monthly_parser(codex_sub, "monthly",
|
|
11475
|
+
help_text="Show Codex usage grouped by month",
|
|
11476
|
+
xref="Drop-in for `ccusage codex monthly`. Same engine as `cctally codex-monthly`.")
|
|
11477
|
+
_build_codex_session_parser(codex_sub, "session",
|
|
11478
|
+
help_text="Show Codex usage grouped by session",
|
|
11479
|
+
xref="Drop-in for `ccusage codex session`. Same engine as `cctally codex-session`.")
|
|
11480
|
+
_build_codex_weekly_parser(codex_sub, "weekly",
|
|
11481
|
+
help_text="Show Codex usage grouped by week",
|
|
11482
|
+
xref="cctally extension (no upstream `ccusage codex weekly`). Same engine as "
|
|
11483
|
+
"`cctally codex-weekly`.")
|
|
11333
11484
|
|
|
11334
11485
|
# ---- config (persisted user preferences) ----
|
|
11335
11486
|
cfg_p = sub.add_parser(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cctally",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Claude Code usage tracker and local dashboard for Pro/Max subscription limits - weekly cost-per-percent trend, quota forecasts, threshold alerts. ccusage-compatible.",
|
|
5
5
|
"homepage": "https://github.com/omrikais/cctally",
|
|
6
6
|
"repository": {
|