cctrail 0.1.0__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.
Files changed (41) hide show
  1. cctrail-0.1.0/.github/workflows/main.yml +70 -0
  2. cctrail-0.1.0/.github/workflows/pull.yml +25 -0
  3. cctrail-0.1.0/.gitignore +6 -0
  4. cctrail-0.1.0/LICENSE +21 -0
  5. cctrail-0.1.0/Makefile +28 -0
  6. cctrail-0.1.0/PKG-INFO +233 -0
  7. cctrail-0.1.0/cctrail/__init__.py +37 -0
  8. cctrail-0.1.0/cctrail/claude.py +450 -0
  9. cctrail-0.1.0/cctrail/cli.py +743 -0
  10. cctrail-0.1.0/cctrail/codex.py +579 -0
  11. cctrail-0.1.0/cctrail/config.py +184 -0
  12. cctrail-0.1.0/cctrail/history.py +33 -0
  13. cctrail-0.1.0/cctrail/metrics.py +393 -0
  14. cctrail-0.1.0/cctrail/models.py +147 -0
  15. cctrail-0.1.0/cctrail/pricing.json +334 -0
  16. cctrail-0.1.0/cctrail/pricing.py +167 -0
  17. cctrail-0.1.0/cctrail/py.typed +0 -0
  18. cctrail-0.1.0/cctrail/utils.py +163 -0
  19. cctrail-0.1.0/cctrail/week.py +75 -0
  20. cctrail-0.1.0/changelog.md +16 -0
  21. cctrail-0.1.0/pyproject.toml +40 -0
  22. cctrail-0.1.0/readme.md +221 -0
  23. cctrail-0.1.0/scripts/update-pricing.py +71 -0
  24. cctrail-0.1.0/tests/fixtures/claude/projects/testproj/s-main-1/subagents/agent-a-explore-1.jsonl +5 -0
  25. cctrail-0.1.0/tests/fixtures/claude/projects/testproj/s-main-1.jsonl +13 -0
  26. cctrail-0.1.0/tests/fixtures/claude_dedupe/projects/testproj/copy.jsonl +1 -0
  27. cctrail-0.1.0/tests/fixtures/claude_dedupe/projects/testproj/main/subagents/agent-side.jsonl +2 -0
  28. cctrail-0.1.0/tests/fixtures/claude_dedupe/projects/testproj/main.jsonl +2 -0
  29. cctrail-0.1.0/tests/fixtures/codex/session_index.jsonl +2 -0
  30. cctrail-0.1.0/tests/fixtures/codex/sessions/2026/01/03/rollout-agent.jsonl +11 -0
  31. cctrail-0.1.0/tests/fixtures/codex/sessions/2026/01/03/rollout-fork.jsonl +9 -0
  32. cctrail-0.1.0/tests/fixtures/codex/sessions/2026/01/03/rollout-main.jsonl +16 -0
  33. cctrail-0.1.0/tests/test_claude.py +222 -0
  34. cctrail-0.1.0/tests/test_cli.py +486 -0
  35. cctrail-0.1.0/tests/test_codex.py +241 -0
  36. cctrail-0.1.0/tests/test_config.py +219 -0
  37. cctrail-0.1.0/tests/test_metrics.py +276 -0
  38. cctrail-0.1.0/tests/test_pricing.py +108 -0
  39. cctrail-0.1.0/tests/test_serialize.py +88 -0
  40. cctrail-0.1.0/tests/test_week.py +65 -0
  41. cctrail-0.1.0/uv.lock +434 -0
@@ -0,0 +1,70 @@
1
+ name: main
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ continue-on-error: ${{ matrix.python-version == '3.15' }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - run: uv sync --frozen
26
+ - run: make check
27
+ - run: make test
28
+
29
+ release:
30
+ if: ${{ startsWith(github.ref, 'refs/tags/v') }}
31
+ runs-on: ubuntu-latest
32
+ needs: test
33
+ permissions:
34
+ contents: write
35
+ id-token: write
36
+ steps:
37
+ - uses: actions/checkout@v7
38
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
39
+
40
+ - name: Check pyproject.toml version matches tag
41
+ run: |
42
+ TAG="${{ github.ref_name }}"
43
+ PKG_VERSION="v$(grep '^version = ' pyproject.toml | cut -d'"' -f2)"
44
+ if [ "$TAG" != "$PKG_VERSION" ]; then
45
+ echo "Error: tag ${TAG} does not match pyproject.toml version ${PKG_VERSION}"
46
+ exit 1
47
+ fi
48
+
49
+ - name: Prepare README for PyPI
50
+ run: |
51
+ BASE_URL="https://raw.githubusercontent.com/${{ github.repository }}/${{ github.ref_name }}"
52
+ sed -i "s#src=\"\\.github/#src=\"${BASE_URL}/.github/#g" readme.md
53
+
54
+ - run: uv build
55
+
56
+ - name: Extract changelog entry
57
+ run: |
58
+ VERSION="${{ github.ref_name }}"
59
+ NOTES=$(awk "/^## ${VERSION}/{found=1; next} /^## /{found=0} found && !/^---$/" changelog.md)
60
+ if [ -z "$NOTES" ]; then
61
+ echo "Error: no changelog entry found for ${VERSION}"
62
+ exit 1
63
+ fi
64
+ echo "$NOTES" > release_notes.txt
65
+
66
+ - uses: pypa/gh-action-pypi-publish@release/v1
67
+
68
+ - run: gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-file release_notes.txt
69
+ env:
70
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,25 @@
1
+ name: pull
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ continue-on-error: ${{ matrix.python-version == '3.15' }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - run: uv sync --frozen
24
+ - run: make check
25
+ - run: make test
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ .coverage
3
+ *.pyc
4
+ /3rd
5
+ /samples
6
+ cctrail.toml
cctrail-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vladkens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN THE
21
+ SOFTWARE.
cctrail-0.1.0/Makefile ADDED
@@ -0,0 +1,28 @@
1
+ .PHONY: prepare lint check test update install
2
+
3
+ prepare: lint check
4
+
5
+ lint:
6
+ uv run ruff check --select I --fix .
7
+ uv run ruff format .
8
+
9
+ check:
10
+ uv run ruff format --check .
11
+ uv run ruff check .
12
+ uv run ty check
13
+
14
+ test:
15
+ uv run pytest -s --cov=cctrail tests/
16
+
17
+ update:
18
+ uv sync --upgrade --all-groups
19
+ uv run --script scripts/update-pricing.py
20
+
21
+ install:
22
+ uv sync
23
+
24
+ perf:
25
+ CCTRAIL_WORKERS=1 /usr/bin/time uv run cctrail groups --provider claude
26
+ CCTRAIL_WORKERS=4 /usr/bin/time uv run cctrail groups --provider claude
27
+ CCTRAIL_WORKERS=1 /usr/bin/time uv run cctrail groups --provider codex
28
+ CCTRAIL_WORKERS=4 /usr/bin/time uv run cctrail groups --provider codex
cctrail-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: cctrail
3
+ Version: 0.1.0
4
+ Summary: Parser and analytics toolkit for Claude Code and Codex history
5
+ Author: vladkens
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: orjson>=3.10.0
10
+ Requires-Dist: rich>=15.0.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # cctrail
14
+
15
+ <div align="center">
16
+
17
+ [<img src="https://badges.ws/pypi/v/cctrail" alt="version" />](https://pypi.org/project/cctrail)
18
+ [<img src="https://badges.ws/pypi/python/cctrail" alt="py versions" />](https://pypi.org/project/cctrail)
19
+ [<img src="https://badges.ws/pypi/dm/cctrail" alt="downloads" />](https://pypi.org/project/cctrail)
20
+ [<img src="https://badges.ws/github/license/vladkens/cctrail" alt="license" />](https://github.com/vladkens/cctrail/blob/main/LICENSE)
21
+ [<img src="https://badges.ws/badge/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
22
+
23
+ </div>
24
+
25
+ `cctrail` reads local Claude Code and Codex history and reports token usage, estimated cost, and project statistics. It can also be used as a Python library for working with parsed sessions, messages, tool calls, and agents.
26
+
27
+ ```console
28
+ $ uvx cctrail groups today
29
+ Group Projects Sessions Tokens Last active Cost Time
30
+ exp 2 2 32.36M 2026-08-09 $24.00 1h 15m
31
+ wrk 1 1 24.93M 2026-08-09 $19.45 0h 49m
32
+ ungrouped 2 4 20.56M 2026-08-09 $17.31 1h 26m
33
+ oss 1 1 10.35M 2026-08-09 $7.90 0h 31m
34
+ comms 1 1 71.1K 2026-08-09 $0.10 0h 06m
35
+ total 7 9 88.27M 2026-08-09 $68.74 4h 08m
36
+ ```
37
+
38
+ ```console
39
+ $ uvx cctrail projects today
40
+ Project Sessions Total tokens Last active Cost Time
41
+ ~/Code/vibe/cctrail 1 32.04M 2026-08-09 $23.43 1h 10m
42
+ atlas 1 24.93M 2026-08-09 $19.45 0h 49m
43
+ starship 2 17.87M 2026-08-09 $14.37 1h 02m
44
+ ~/Code/pub/macmon 1 10.35M 2026-08-09 $7.90 0h 31m
45
+ ~/Code/sideproj 2 2.68M 2026-08-09 $2.93 0h 24m
46
+ ~/Code/labs 1 322.1K 2026-08-09 $0.57 0h 05m
47
+ ~/Code/client-app 1 71.1K 2026-08-09 $0.10 0h 06m
48
+ total 9 88.27M 2026-08-09 $68.74 4h 08m
49
+ ```
50
+
51
+ ```console
52
+ $ uvx cctrail usage this-month
53
+ Date Input Output Total Cost Time
54
+ 2026-08-01 5.75M 1.30M 281.22M $174.00 12h 45m
55
+ 2026-08-02 6.93M 780.8K 204.76M $276.40 10h 24m
56
+ 2026-08-03 4.88M 338.4K 138.49M $193.10 6h 02m
57
+ 2026-08-04 4.55M 693.6K 244.77M $92.25 6h 39m
58
+ 2026-08-05 4.43M 444.6K 166.19M $102.47 6h 06m
59
+ 2026-08-06 4.52M 226.4K 93.27M $83.17 6h 22m
60
+ 2026-08-07 6.15M 631.3K 232.52M $153.22 9h 14m
61
+ 2026-08-08 24.5K 4.0K 123.3K $0.31 0h 07m
62
+ 2026-08-09 3.06M 196.9K 92.68M $72.05 4h 14m
63
+ total 40.30M 4.61M 1.45B $1146.97 61h 56m
64
+ ```
65
+
66
+ ## Why cctrail?
67
+
68
+ Before coding agents, I tracked time with a VS Code extension. That worked while most of the job happened inside the editor. In the new agentic coding era, much of my work happens in terminal sessions instead: I start an agent, review its results, send another prompt, and move between several projects. The editor no longer sees enough of that activity to answer a simple question: where did my time go?
69
+
70
+ I tried several ways to recover that information, and local agent history turned out to be the most useful source. `cctrail` turns it into reports that help explore how agent-assisted work is distributed over time, projects, and configurable groups. Its purpose is broader than any single report: to make the work recorded by coding agents visible and understandable from different perspectives.
71
+
72
+ [ccusage](https://github.com/ccusage/ccusage) is an excellent alternative for ready-made token and cost reports across many coding agents. `cctrail` grew from a different question: not only how many tokens were used, but what I worked on and how my time was divided.
73
+
74
+ ## CLI
75
+
76
+ Show token usage by day:
77
+
78
+ ```sh
79
+ uvx cctrail usage
80
+ ```
81
+
82
+ Use named calendar periods and group longer reports by week or month:
83
+
84
+ ```sh
85
+ uvx cctrail usage today
86
+ uvx cctrail usage last-week
87
+ uvx cctrail usage this-month --by week
88
+ uvx cctrail usage all --by month
89
+ ```
90
+
91
+ Available periods are `today`, `yesterday`, `this-week`, `last-week`, `this-month`, `last-month`, and `all`. Weeks follow the first weekday of the system locale.
92
+
93
+ Show a per-model breakdown or the detailed reasoning and cache token columns:
94
+
95
+ ```sh
96
+ uvx cctrail usage this-month --models
97
+ uvx cctrail usage this-month --models --full
98
+ ```
99
+
100
+ For an arbitrary date range, use `--since` and `--until`:
101
+
102
+ ```sh
103
+ uvx cctrail usage --since 2026-07-01 --until 2026-07-31
104
+ ```
105
+
106
+ Show usage grouped by project directory:
107
+
108
+ ```sh
109
+ uvx cctrail projects this-month
110
+ ```
111
+
112
+ Projects accept the same named periods and arbitrary date ranges as usage reports.
113
+ Paths inside your home directory are shortened to `~/…`; pass `--full` to show absolute paths.
114
+
115
+ Show usage aggregated across configured project groups:
116
+
117
+ ```sh
118
+ uvx cctrail groups this-month
119
+ uvx cctrail groups --provider codex
120
+ uvx cctrail groups --json
121
+ ```
122
+
123
+ Groups accept the same periods, date ranges, and provider filter as project reports. Projects that do not match a configured group are included under `ungrouped`.
124
+
125
+ ### Configuration
126
+
127
+ Project aliases can combine a project's main checkout and worktrees into a single project in the report. This is optional: without an alias, `cctrail` reports each path separately.
128
+
129
+ ```toml
130
+ [aliases]
131
+ cctrail = ["~/Code/vibe/cctrail", "~/.worktrees/cctrail--*"]
132
+
133
+ [groups]
134
+ personal = ["cctrail", "~/Code/dotfiles"]
135
+ opensource = ["~/Code/pub/**"]
136
+ ```
137
+
138
+ Alias paths support `~` and glob patterns. `*` matches within one path segment, while `**` can match across directories. A trailing `/**` includes both the named directory and everything below it. For example, `~/.worktrees/cctrail--*` matches sibling worktrees and `~/Code/**/cctrail/**` matches the project at any depth below `~/Code` together with all of its checkouts.
139
+
140
+ Groups are resolved after aliases, so a group can contain an alias such as `cctrail`, an exact project path, or a glob pattern. A project can match only one group.
141
+
142
+ Use `--config` to select a file for one command, or `--no-config` to ignore every TOML configuration:
143
+
144
+ ```sh
145
+ uvx cctrail --config ~/configs/work.toml projects
146
+ uvx cctrail --no-config projects
147
+ ```
148
+
149
+ Configuration is selected in this order:
150
+
151
+ 1. `--no-config` disables configuration
152
+ 2. `--config PATH` selects a specific file
153
+ 3. `./cctrail.toml`
154
+ 4. `~/.config/cctrail.toml`
155
+
156
+ An explicitly selected file must exist. The automatic locations are optional.
157
+
158
+ You can also configure every Claude Code and Codex history directory instead of setting `CLAUDE_CONFIG_DIR` or `CODEX_HOME` for each command:
159
+
160
+ ```toml
161
+ [sources]
162
+ claude = ["~/.claude", "~/.claude-work"]
163
+ codex = ["~/.codex", "~/.codex-work"]
164
+ ```
165
+
166
+ When a provider is present in `[sources]`, its list replaces the default directories. Environment variables still take precedence when set.
167
+
168
+ Claude data is read from `~/.config/claude` and `~/.claude`, while Codex data is read from `~/.codex`. Set `CLAUDE_CONFIG_DIR` or `CODEX_HOME` to replace these defaults. Both variables accept comma-separated directories:
169
+
170
+ ```sh
171
+ CLAUDE_CONFIG_DIR="$HOME/.claude,$HOME/.claude-qh" uvx cctrail usage
172
+ CODEX_HOME="$HOME/.codex,$HOME/.codex-work" uvx cctrail usage
173
+ ```
174
+
175
+ Usage reports count each provider message once, while parsed sessions retain their original message and sub-agent structure. Costs are estimates based on the pricing catalog embedded in the installed cctrail version.
176
+
177
+ Active time is estimated by treating every activity event as a five-minute window. Overlapping sessions within a project are merged; when several projects are active at once, that time is divided evenly between them. This keeps the project and group totals equal to the overall active time.
178
+
179
+ ## Python library
180
+
181
+ Add `cctrail` to a project:
182
+
183
+ ```sh
184
+ uv add cctrail
185
+ ```
186
+
187
+ Iterate over parsed sessions:
188
+
189
+ ```python
190
+ from cctrail import iter_sessions, tool_call_counts
191
+
192
+ for session in iter_sessions():
193
+ print(session.provider, session.cwd, session.title, session.usage.total)
194
+
195
+ for call in session.iter_tool_calls():
196
+ print(call.name, call.input, call.duration)
197
+
198
+ for agent in session.iter_agents():
199
+ print(agent.agent_type, agent.usage.total)
200
+
201
+ print(tool_call_counts(session))
202
+ ```
203
+
204
+ Provider-specific iterators are available as `iter_claude_sessions()` and `iter_codex_sessions()`. Sub-agent transcripts are attached to the tool call that launched them.
205
+
206
+ The main data types are `Session`, `Message`, `ToolCall`, and `Usage`. Every model supports `.as_dict()` and `.as_json()`.
207
+
208
+ ```python
209
+ from cctrail import Usage, by_project, calculate_cost, get_pricing, iter_sessions, usage_by_period
210
+
211
+ sessions = list(iter_sessions())
212
+ weekly = usage_by_period(sessions, granularity="week")
213
+ projects = by_project(sessions)
214
+
215
+ pricing = get_pricing("claude-sonnet-5")
216
+ cost = calculate_cost("claude-sonnet-5", Usage(input=1_000, output=100))
217
+ ```
218
+
219
+ Usage is grouped in the system timezone by default. `usage_by_period()` accepts `day`, `week`, and `month` granularities, while `usage_by_day()` remains the daily shortcut. Pass a `datetime.tzinfo` as the `timezone` argument to use another timezone. `UsageSummary` values expose `.cost`, `.active_time`, and per-model `.models`; `ProjectSummary` values expose `.cost` and `.active_time`.
220
+
221
+ ## Development
222
+
223
+ The project requires Python 3.11 or newer and uses [uv](https://docs.astral.sh/uv/).
224
+
225
+ ```sh
226
+ uv sync
227
+ make check
228
+ make test
229
+ ```
230
+
231
+ ## License
232
+
233
+ Distributed under the [MIT License](LICENSE).
@@ -0,0 +1,37 @@
1
+ from .claude import iter_sessions as iter_claude_sessions
2
+ from .codex import iter_sessions as iter_codex_sessions
3
+ from .history import iter_sessions
4
+ from .metrics import (
5
+ ModelSummary,
6
+ ProjectSummary,
7
+ UsageSummary,
8
+ active_time,
9
+ by_project,
10
+ tool_call_counts,
11
+ usage_by_day,
12
+ usage_by_period,
13
+ )
14
+ from .models import JsonTrait, Message, Session, ToolCall, Usage
15
+ from .pricing import Pricing, calculate_cost, get_pricing
16
+
17
+ __all__ = [
18
+ "JsonTrait",
19
+ "Message",
20
+ "ModelSummary",
21
+ "Pricing",
22
+ "ProjectSummary",
23
+ "Session",
24
+ "ToolCall",
25
+ "Usage",
26
+ "UsageSummary",
27
+ "active_time",
28
+ "by_project",
29
+ "calculate_cost",
30
+ "get_pricing",
31
+ "iter_claude_sessions",
32
+ "iter_codex_sessions",
33
+ "iter_sessions",
34
+ "tool_call_counts",
35
+ "usage_by_day",
36
+ "usage_by_period",
37
+ ]