clocwork 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.
- clocwork-0.1.0/CHANGELOG.md +43 -0
- clocwork-0.1.0/LICENSE +21 -0
- clocwork-0.1.0/MANIFEST.in +7 -0
- clocwork-0.1.0/PKG-INFO +328 -0
- clocwork-0.1.0/README.md +300 -0
- clocwork-0.1.0/clocwork +11 -0
- clocwork-0.1.0/man/clocwork.1 +158 -0
- clocwork-0.1.0/pyproject.toml +47 -0
- clocwork-0.1.0/setup.cfg +4 -0
- clocwork-0.1.0/src/clocwork/__init__.py +15 -0
- clocwork-0.1.0/src/clocwork/__main__.py +4 -0
- clocwork-0.1.0/src/clocwork/agents.py +113 -0
- clocwork-0.1.0/src/clocwork/analyse.py +662 -0
- clocwork-0.1.0/src/clocwork/classify.py +124 -0
- clocwork-0.1.0/src/clocwork/cli.py +172 -0
- clocwork-0.1.0/src/clocwork/cloc.py +346 -0
- clocwork-0.1.0/src/clocwork/config.py +83 -0
- clocwork-0.1.0/src/clocwork/manpage.py +175 -0
- clocwork-0.1.0/src/clocwork/paths.py +171 -0
- clocwork-0.1.0/src/clocwork/render.py +172 -0
- clocwork-0.1.0/src/clocwork/sources/__init__.py +38 -0
- clocwork-0.1.0/src/clocwork/sources/claude_code.py +87 -0
- clocwork-0.1.0/src/clocwork/sources/codex.py +266 -0
- clocwork-0.1.0/src/clocwork/sources/gemini.py +161 -0
- clocwork-0.1.0/src/clocwork/template.html +1974 -0
- clocwork-0.1.0/src/clocwork/tokens.py +196 -0
- clocwork-0.1.0/src/clocwork.egg-info/PKG-INFO +328 -0
- clocwork-0.1.0/src/clocwork.egg-info/SOURCES.txt +82 -0
- clocwork-0.1.0/src/clocwork.egg-info/dependency_links.txt +1 -0
- clocwork-0.1.0/src/clocwork.egg-info/entry_points.txt +2 -0
- clocwork-0.1.0/src/clocwork.egg-info/top_level.txt +1 -0
- clocwork-0.1.0/tests/__init__.py +4 -0
- clocwork-0.1.0/tests/agent_logs.py +52 -0
- clocwork-0.1.0/tests/dashboard/check.js +7 -0
- clocwork-0.1.0/tests/dashboard/fixture.py +185 -0
- clocwork-0.1.0/tests/dashboard/harness.js +141 -0
- clocwork-0.1.0/tests/dashboard/run_all.js +15 -0
- clocwork-0.1.0/tests/dashboard/test_charts.js +58 -0
- clocwork-0.1.0/tests/dashboard/test_locale.js +128 -0
- clocwork-0.1.0/tests/dashboard/test_numbering.js +27 -0
- clocwork-0.1.0/tests/dashboard/test_region.js +47 -0
- clocwork-0.1.0/tests/dashboard/test_selection.js +95 -0
- clocwork-0.1.0/tests/dashboard/test_table.js +210 -0
- clocwork-0.1.0/tests/dashboard/test_tokens.js +107 -0
- clocwork-0.1.0/tests/dashboard/test_tokens_absent.js +10 -0
- clocwork-0.1.0/tests/dashboard/test_tokens_sources.js +32 -0
- clocwork-0.1.0/tests/dashboard/tokens_absent.js +31 -0
- clocwork-0.1.0/tests/dashboard/tokens_sources.js +54 -0
- clocwork-0.1.0/tests/fixtures/README.md +56 -0
- clocwork-0.1.0/tests/fixtures/codex/archived_sessions/rollout-2026-05-23T21-38-44-019e56c6-7526-7d03-9fd0-d8067191edf9.jsonl +6 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/05/23/rollout-2026-05-23T19-34-28-019e5654-aee4-7d33-bf8e-c9c5eeb8e89e.jsonl +7 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/05/29/rollout-2026-05-29T20-53-58-019e7583-0b33-7912-a209-4c0407cfa243.jsonl +5 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/05/29/rollout-2026-05-29T20-54-27-019e7584-5201-7901-b1e7-a3f0c990c12f.jsonl +8 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/08/26/rollout-2026-08-26T18-30-09-01a03eb1-6b82-78e3-87e0-2578e88e80cf.jsonl +23 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/08/26/rollout-2026-08-26T18-33-46-01a03eb4-b805-7470-95d6-24f980e820df.jsonl +10 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/08/26/rollout-2026-08-26T18-34-34-01a03eb5-7309-71a1-b9ea-3d76ee9f4968.jsonl +12 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/08/26/rollout-2026-08-26T18-34-36-01a03eb5-7d3d-7bc3-a730-38a83bf1efda.jsonl +10 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/09/07/rollout-2026-09-07T16-25-14-01a07c0b-5e16-70f2-ad21-7c2a3a228b22.jsonl +39 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/09/07/rollout-2026-09-07T16-25-22-01a07c0b-7aaf-7d43-bca3-9b01faca045d.jsonl +14 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/09/07/rollout-2026-09-07T16-25-26-01a07c0b-8d0c-7022-a0df-f7d14290e076.jsonl +27 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/09/07/rollout-2026-09-07T16-25-31-01a07c0b-9e34-7d32-84fa-8a9f69d02e51.jsonl +18 -0
- clocwork-0.1.0/tests/fixtures/codex/sessions/2026/09/07/rollout-2026-09-07T16-25-56-01a07c0b-ff44-7180-adb2-c56ac1b7693d.jsonl +30 -0
- clocwork-0.1.0/tests/fixtures/gemini/tmp/agent-sample/chats/session-2026-06-12T10-17-466977ef.jsonl +12 -0
- clocwork-0.1.0/tests/fixtures/gemini/tmp/agent-sample/chats/session-2026-06-12T10-27-6ac3542c.jsonl +6 -0
- clocwork-0.1.0/tests/fixtures/gemini/tmp/agent-sample/chats/session-2026-06-12T10-28-1a4f2a8e.jsonl +6 -0
- clocwork-0.1.0/tests/fixtures/gemini/tmp/agent-sample/chats/session-2026-06-12T10-55-ca28c1ad.jsonl +36 -0
- clocwork-0.1.0/tests/fixtures/gemini/tmp/agent-sample/chats/session-2026-06-12T12-32-876f478d.jsonl +41 -0
- clocwork-0.1.0/tests/repo_fixture.py +122 -0
- clocwork-0.1.0/tests/test_agent_fixtures.py +78 -0
- clocwork-0.1.0/tests/test_agents.py +54 -0
- clocwork-0.1.0/tests/test_analyse.py +668 -0
- clocwork-0.1.0/tests/test_classify.py +101 -0
- clocwork-0.1.0/tests/test_cli.py +289 -0
- clocwork-0.1.0/tests/test_cloc_lines.py +464 -0
- clocwork-0.1.0/tests/test_config.py +72 -0
- clocwork-0.1.0/tests/test_fixture_shape.py +32 -0
- clocwork-0.1.0/tests/test_manpage.py +192 -0
- clocwork-0.1.0/tests/test_paths.py +190 -0
- clocwork-0.1.0/tests/test_render.py +171 -0
- clocwork-0.1.0/tests/test_repository.py +49 -0
- clocwork-0.1.0/tests/test_sources.py +193 -0
- clocwork-0.1.0/tests/test_sources_codex.py +444 -0
- clocwork-0.1.0/tests/test_sources_gemini.py +262 -0
- clocwork-0.1.0/tests/test_token_usage.py +269 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to clocwork are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-09-16
|
|
8
|
+
|
|
9
|
+
First public release.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `clocwork [REPO]`: analyses a git repository's whole history into a
|
|
14
|
+
dashboard in a sibling workspace (`<repo>-stats`).
|
|
15
|
+
- Every commit is measured with `cloc --git --diff`, cached per file, and
|
|
16
|
+
reconciled against a `cloc` snapshot of HEAD.
|
|
17
|
+
- `cloc` runs once per CPU core by default (`--jobs`), and
|
|
18
|
+
`--max-commits` spreads a long history over several runs.
|
|
19
|
+
- `cloc` 2.06 or later is required. An older one is refused, because it
|
|
20
|
+
lists file extensions in a form clocwork misreads.
|
|
21
|
+
- Lines per language and per type (code, comment, blank) at every commit,
|
|
22
|
+
and the test-code share at the tip of the analysed branch.
|
|
23
|
+
- Built-in test-path rules cover common ecosystems.
|
|
24
|
+
- `clocwork.toml` can add to the rules, exclude from them or replace them.
|
|
25
|
+
- AI-assisted commits, read from `Co-Authored-By` trailers, with the date
|
|
26
|
+
each agent, and each Claude model, first appeared.
|
|
27
|
+
- Any Claude model is recognised, as are Copilot, Cursor, Codex, Devin,
|
|
28
|
+
aider, Gemini and Gemini Code Assist.
|
|
29
|
+
- `[agents].extra` names anything else.
|
|
30
|
+
- Token usage from Claude Code, Codex CLI and Gemini CLI logs.
|
|
31
|
+
- It is archived per day, agent and model in `token_usage.json`, which no
|
|
32
|
+
run shrinks.
|
|
33
|
+
- Days without logs are estimated from each agent's own tokens-per-line
|
|
34
|
+
ratio.
|
|
35
|
+
- Usage is priced at API list prices, and its electricity is estimated.
|
|
36
|
+
- `clocwork tokens` archives without analysing, for use from cron.
|
|
37
|
+
- `clocwork render` re-renders an existing workspace.
|
|
38
|
+
- `clocwork.json` is a workspace identity guard: a run against a different
|
|
39
|
+
repository is refused.
|
|
40
|
+
- Every number, date and unit on the page is formatted for a region locale.
|
|
41
|
+
- A `clocwork(1)` manual page.
|
|
42
|
+
|
|
43
|
+
[0.1.0]: https://github.com/nicktoumpelis/clocwork/releases/tag/v0.1.0
|
clocwork-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick Toumpelis
|
|
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 OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# The source distribution carries everything the two test suites and the
|
|
2
|
+
# manual page need, so it can be tested and packaged on its own.
|
|
3
|
+
include CHANGELOG.md
|
|
4
|
+
include clocwork
|
|
5
|
+
include man/clocwork.1
|
|
6
|
+
graft tests
|
|
7
|
+
global-exclude __pycache__ *.py[cod] .DS_Store
|
clocwork-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clocwork
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lines per language, AI co-authored commits and token cost over a repository's whole history
|
|
5
|
+
Author: Nick Toumpelis
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nicktoumpelis/clocwork
|
|
8
|
+
Project-URL: Issues, https://github.com/nicktoumpelis/clocwork/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/nicktoumpelis/clocwork/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: git,cloc,lines of code,code statistics,ai agents,tokens,dashboard
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# clocwork
|
|
30
|
+
|
|
31
|
+
Run `clocwork` inside any git repository and get a dashboard of its whole
|
|
32
|
+
history: lines per language and type (code, comment, blank) at every commit,
|
|
33
|
+
which commits an AI agent co-authored and when each model first appeared, and,
|
|
34
|
+
when the repository was worked on with Claude Code, Codex CLI or Gemini CLI,
|
|
35
|
+
what that work cost in tokens, dollars and electricity. Every commit is
|
|
36
|
+
measured with `cloc --git --diff`, cached per file, and reconciled against a
|
|
37
|
+
`cloc` snapshot of HEAD so drift is visible rather than silent.
|
|
38
|
+
|
|
39
|
+
The output is one `index.html` (its charts load Chart.js and its plugins from
|
|
40
|
+
a CDN) plus a `commit_bodies.js` sidecar for the full commit messages, written
|
|
41
|
+
to a **workspace** next to the repository, never inside it.
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Python 3.11 or later (standard library only)
|
|
46
|
+
- [`cloc`](https://github.com/AlDanial/cloc) 2.06 or later on `PATH`. On
|
|
47
|
+
macOS, `brew install cloc` gives a current release. Linux distributions
|
|
48
|
+
often package an older one (Ubuntu 24.04 has 1.98). clocwork refuses an
|
|
49
|
+
older cloc, because earlier releases list file extensions in a form it
|
|
50
|
+
misreads. If yours is older, save `cloc-<version>.pl` from the
|
|
51
|
+
[cloc releases](https://github.com/AlDanial/cloc/releases) as an
|
|
52
|
+
executable named `cloc` on `PATH`.
|
|
53
|
+
- `git`
|
|
54
|
+
|
|
55
|
+
## Installing
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pipx install clocwork # or: pip install clocwork
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
pip does not install the manual page; `man/clocwork.1` is in the repository
|
|
62
|
+
and in the source distribution.
|
|
63
|
+
|
|
64
|
+
Each release also carries `clocwork.pyz`, the whole command in one file, which
|
|
65
|
+
runs with no install step:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl -LO https://github.com/nicktoumpelis/clocwork/releases/latest/download/clocwork.pyz
|
|
69
|
+
chmod +x clocwork.pyz
|
|
70
|
+
./clocwork.pyz ~/code/foo
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or run it from a clone:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/nicktoumpelis/clocwork.git ~/code/clocwork
|
|
77
|
+
cd ~/code/foo && ~/code/clocwork/clocwork
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Both run under the first `python3` on `PATH`, which must be 3.11 or later;
|
|
81
|
+
an older one is refused with a message naming it. On macOS,
|
|
82
|
+
`/usr/bin/python3` comes from Apple's developer tools and can be older
|
|
83
|
+
(Xcode's is 3.9); name a newer interpreter instead, as in
|
|
84
|
+
`python3.13 clocwork.pyz`.
|
|
85
|
+
|
|
86
|
+
For development, `pip install -e ~/code/clocwork` installs the `clocwork`
|
|
87
|
+
command from the clone.
|
|
88
|
+
|
|
89
|
+
## Running
|
|
90
|
+
|
|
91
|
+
Run `clocwork` inside the repository to analyse, or pass its path. The first
|
|
92
|
+
run over a long history measures every commit with `cloc`, running one
|
|
93
|
+
`cloc` process per CPU core (`--jobs N` chooses the number). On a 10-core
|
|
94
|
+
machine the 289 commits of [spf13/cast](https://github.com/spf13/cast) take
|
|
95
|
+
14 seconds (113 seconds with `--jobs 1`), and a repository of 3,800 commits,
|
|
96
|
+
2,700 of them non-merge, takes about four minutes. Results are cached, so
|
|
97
|
+
later runs take seconds. `--max-commits N` caps one run and a later run
|
|
98
|
+
continues from where it stopped.
|
|
99
|
+
|
|
100
|
+
## Commands
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
clocwork [REPO] analyse, archive tokens, render, open the dashboard
|
|
104
|
+
clocwork tokens [REPO] archive agent token logs only
|
|
105
|
+
clocwork render -o DIR re-render the dashboard from existing workspace data
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`REPO` is the repository or any directory inside it; the default is the
|
|
109
|
+
current directory. A first argument that is not a subcommand is taken as
|
|
110
|
+
`REPO`, so `clocwork ~/code/foo` works.
|
|
111
|
+
|
|
112
|
+
`tokens` is a separate command because the two halves of the pipeline have
|
|
113
|
+
opposite economics. The `cloc` pass is slow and fully regenerable. The token
|
|
114
|
+
archive is cheap and irreplaceable: coding agents delete their logs (Claude
|
|
115
|
+
Code after roughly 30 days), and a day that is not archived while its logs
|
|
116
|
+
exist is gone for good. `clocwork tokens` is what to run from cron.
|
|
117
|
+
|
|
118
|
+
`render` needs no repository, only a workspace; it exists so the page can be
|
|
119
|
+
iterated on without re-analysing.
|
|
120
|
+
|
|
121
|
+
Options (`clocwork run --help`):
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
-q, --quiet print nothing but errors
|
|
125
|
+
--config PATH explicit clocwork.toml
|
|
126
|
+
--locale TAG region locale for the page (default: $CLOCWORK_LOCALE,
|
|
127
|
+
else the machine's region)
|
|
128
|
+
--no-open do not open the dashboard in a browser
|
|
129
|
+
-o, --output DIR workspace directory (default: <repo-parent>/<repo-
|
|
130
|
+
name>-stats)
|
|
131
|
+
--branch REF ref to analyse (default: the checked-out branch)
|
|
132
|
+
--max-commits N measure at most N uncached commits this run
|
|
133
|
+
-j, --jobs N cloc processes to run at once (default: one per CPU core)
|
|
134
|
+
--no-tokens skip the agent log scan
|
|
135
|
+
--cache-dir DIR cache location (default: $XDG_CACHE_HOME/clocwork, else
|
|
136
|
+
~/.cache/clocwork; wins over both)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`tokens` takes only `-o` and `-q`: it writes no page. `render` takes the page
|
|
140
|
+
options and `-q`, and its `-o` is required. `--version` prints the version.
|
|
141
|
+
The same reference is a manual page: `man ./man/clocwork.1` from a clone.
|
|
142
|
+
|
|
143
|
+
The page formats every number, date and unit for a region locale. The
|
|
144
|
+
generator records it, because browsers expose only the language list: the
|
|
145
|
+
`CLOCWORK_LOCALE` environment variable wins (a BCP 47 tag such as `en-SE`),
|
|
146
|
+
then the macOS Language & Region setting, then `LC_ALL`, `LC_NUMERIC` and
|
|
147
|
+
`LANG`; `--locale` overrides all of them for one run.
|
|
148
|
+
|
|
149
|
+
## The workspace
|
|
150
|
+
|
|
151
|
+
Output goes to a sibling of the repository: `~/code/foo` produces
|
|
152
|
+
`~/code/foo-stats`. It is deliberately not inside the repository, because the
|
|
153
|
+
tool would then be measuring its own output and dirtying the working tree of
|
|
154
|
+
the repository it reports on. Pass `-o DIR` to put it elsewhere.
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
foo-stats/
|
|
158
|
+
clocwork.json which repository this workspace belongs to
|
|
159
|
+
clocwork.toml optional configuration (see below)
|
|
160
|
+
token_usage.json the per-day, per-agent token archive; cannot be regenerated
|
|
161
|
+
full_commit_data.json the analysis
|
|
162
|
+
index.html the dashboard
|
|
163
|
+
commit_bodies.js full commit messages, loaded when a row is expanded
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The workspace is meant to be committed to its own repository: that is the
|
|
167
|
+
backup for `token_usage.json`. `clocwork.json` is an identity guard. On every
|
|
168
|
+
run the target repository is compared against it, by remote URL first and
|
|
169
|
+
absolute path second, and a mismatch is refused with an error naming both
|
|
170
|
+
repositories, so no ordinary mistake can overwrite one repository's token
|
|
171
|
+
archive with another's.
|
|
172
|
+
|
|
173
|
+
The `cloc` cache lives outside the workspace, in
|
|
174
|
+
`$XDG_CACHE_HOME/clocwork/<name>-<hash>/` or `~/.cache/clocwork/`, keyed by
|
|
175
|
+
the repository's absolute path. It holds per-file rows rather than
|
|
176
|
+
per-language totals, so changing the language table or the test-path rules
|
|
177
|
+
below re-reads the cache instead of re-running `cloc`.
|
|
178
|
+
|
|
179
|
+
## Configuration
|
|
180
|
+
|
|
181
|
+
Optional. Searched in this order, first hit wins: `--config PATH`, then
|
|
182
|
+
`<workspace>/clocwork.toml`, then `<repo>/.clocwork.toml`. The last is read and
|
|
183
|
+
never written, so a repository can commit its own settings while someone
|
|
184
|
+
analysing it can still override them from their workspace.
|
|
185
|
+
|
|
186
|
+
```toml
|
|
187
|
+
title = "MyApp" # page heading; default is the repository's directory name
|
|
188
|
+
|
|
189
|
+
[tests]
|
|
190
|
+
include = ["integration/**", "e2e/**"] # added to the built-in test-path rules
|
|
191
|
+
exclude = ["tests/fixtures/**"] # applied last, wins over everything
|
|
192
|
+
replace = false # true drops the built-ins entirely
|
|
193
|
+
|
|
194
|
+
[agents]
|
|
195
|
+
extra = [{ match = "Jules", name = "Jules" }] # substring of a Co-Authored-By trailer, reported name
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Globs match the whole path from the repository root: `**` matches across
|
|
199
|
+
directories, `*` within one, `?` one character. `*.py` therefore matches only
|
|
200
|
+
top-level files; `**/*.py` matches at any depth.
|
|
201
|
+
|
|
202
|
+
### What counts as test code
|
|
203
|
+
|
|
204
|
+
Built in, by directory: `test`, `tests`, `__tests__`, `testdata` (any
|
|
205
|
+
case); `spec` and `specs` for Ruby, JavaScript, TypeScript and CoffeeScript
|
|
206
|
+
files only, so a `specs/` directory of design documents is not counted; a directory named `*Test` or
|
|
207
|
+
`*Tests` (Xcode, JVM); the `src/test/`, `src/androidTest/` and
|
|
208
|
+
`src/integrationTest/` layouts. By filename: `*_test.go`; `test_*.py`, `*_test.py`, `conftest.py`; `*.test.*`
|
|
209
|
+
and `*.spec.*` for JavaScript and TypeScript; `*Test.*` and `*Tests.*` for
|
|
210
|
+
Java, Kotlin, C# and Swift; `*_spec.rb`, `*_test.rb`, `*_test.dart`,
|
|
211
|
+
`*_test.exs`. Inline test code (Rust's `#[cfg(test)]`, Go examples in a
|
|
212
|
+
non-test file) is not detectable from paths.
|
|
213
|
+
|
|
214
|
+
The run summary prints the test share of HEAD. A share that is obviously wrong
|
|
215
|
+
is the signal to add a rule; the fix costs a config line and a re-read, not a
|
|
216
|
+
re-measure.
|
|
217
|
+
|
|
218
|
+
### Which commits are AI-assisted
|
|
219
|
+
|
|
220
|
+
Attribution comes from `Co-Authored-By:` trailers only, so a commit that
|
|
221
|
+
merely mentions an agent is not counted. Any Claude model is recognised and
|
|
222
|
+
normalised (`Claude Opus 4.6`, `Claude Opus 5 (1M)`), and Copilot, Cursor,
|
|
223
|
+
Codex, Devin, aider, Gemini and Gemini Code Assist are recognised by name.
|
|
224
|
+
Anything else stays unmatched rather than guessed at; `[agents].extra` names
|
|
225
|
+
the rest.
|
|
226
|
+
|
|
227
|
+
### Token usage
|
|
228
|
+
|
|
229
|
+
The token section appears when the workspace's token archive holds at least
|
|
230
|
+
one day. Each run reads the logs coding agents keep on the machine and
|
|
231
|
+
archives per-day totals, per agent and model, into `token_usage.json`:
|
|
232
|
+
|
|
233
|
+
| Agent | Logs read | Override |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| Claude Code | `~/.claude/projects/`, the directory named after the repository's path | none |
|
|
236
|
+
| Codex CLI | `~/.codex/sessions/` and `~/.codex/archived_sessions/` | `CODEX_HOME` replaces `~/.codex` |
|
|
237
|
+
| Gemini CLI | `~/.gemini/tmp/` and `~/.cache/.gemini/tmp/`, sessions started in the repository or a directory it tracks | `GEMINI_CLI_HOME` replaces `~` |
|
|
238
|
+
|
|
239
|
+
A Codex session belongs to the repository when it records the same remote
|
|
240
|
+
as the repository's `origin`, so sessions from any clone or worktree count.
|
|
241
|
+
A session that recorded another remote still belongs when it ran in the
|
|
242
|
+
repository, or a directory inside it, from one of the repository's commits.
|
|
243
|
+
A renamed or transferred repository keeps its sessions that way, and a
|
|
244
|
+
different repository later cloned to the same path gets them only if it
|
|
245
|
+
holds the commit they started from. When the session records no remote, or
|
|
246
|
+
`origin` is missing or not a URL clocwork recognises, the session belongs
|
|
247
|
+
when it ran in the repository or a directory inside it.
|
|
248
|
+
Gemini CLI identifies a session's project only by a hash of the directory it
|
|
249
|
+
started in, so its sessions count when that is the repository's current path
|
|
250
|
+
or a directory tracked at `HEAD` below it.
|
|
251
|
+
|
|
252
|
+
Only dates, model names, and token and turn counts reach the archive;
|
|
253
|
+
prompts and replies are never kept. The archive keeps the larger record for
|
|
254
|
+
each day and agent, because agents delete their logs and a day not archived
|
|
255
|
+
in time is gone.
|
|
256
|
+
|
|
257
|
+
A day the archive does not cover for an agent, but on which that agent's
|
|
258
|
+
commits changed lines, is estimated from the agent's own tokens-per-line
|
|
259
|
+
ratio; a record that holds no tokens covers nothing. The result is priced at
|
|
260
|
+
API list prices, each measured day at the prices in force on it and the
|
|
261
|
+
estimated days at the measured mix, and its electricity is estimated. For
|
|
262
|
+
any repository not worked on with these agents on this machine, the section
|
|
263
|
+
and the commit table's Tokens column are simply absent; that is the normal
|
|
264
|
+
case, not an error.
|
|
265
|
+
|
|
266
|
+
Tokens land only on the commits of the agent whose logs measured them,
|
|
267
|
+
split across that agent's commits of the day by lines changed. A commit
|
|
268
|
+
carries no token figure when its agent's logs are not read (Copilot, Cursor,
|
|
269
|
+
Devin, aider, Gemini Code Assist or any other), or when they cover no day on
|
|
270
|
+
which that agent's commits changed lines, and it is never priced at another
|
|
271
|
+
agent's rate. When the repository has token data, the run summary counts
|
|
272
|
+
those commits and names their agents. Gemini Code Assist is the name for
|
|
273
|
+
`gemini-code-assist[bot]`, which GitHub credits when one of its review
|
|
274
|
+
suggestions is accepted.
|
|
275
|
+
|
|
276
|
+
Codex asks its model to end commit messages with
|
|
277
|
+
`Co-authored-by: Codex <noreply@openai.com>` unless attribution is turned
|
|
278
|
+
off. Gemini CLI adds no trailer, so its tokens reach a commit only when the
|
|
279
|
+
commit credits Gemini by hand, in a trailer such as
|
|
280
|
+
`Co-Authored-By: Gemini CLI <address>`; otherwise the page says its tokens
|
|
281
|
+
land on no commit. Codex rollouts that Codex has compressed are read on
|
|
282
|
+
Python 3.14 and later; earlier versions count them as unreadable and say so
|
|
283
|
+
in the log.
|
|
284
|
+
|
|
285
|
+
An archive written by an earlier version is read as Claude Code's and
|
|
286
|
+
rewritten in the per-agent shape the next time a scan finds logs; an archive
|
|
287
|
+
of a version this clocwork does not know is refused with an error rather
|
|
288
|
+
than read or overwritten.
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
python3 -m unittest discover -s tests -t . -q # the Python suite
|
|
294
|
+
node tests/dashboard/run_all.js # the page, in a fake DOM under node
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The dashboard suite renders a synthetic workspace (`tests/dashboard/fixture.py`)
|
|
298
|
+
through `./clocwork render`. Set `CLOCWORK_DASH_WORKSPACE=<dir>` to run the
|
|
299
|
+
checks over any rendered workspace, a real one included; the files that
|
|
300
|
+
exercise the token-less page and the page with several agents render their
|
|
301
|
+
own synthetic variants regardless. Checks that assume the fixture's size,
|
|
302
|
+
such as the 500-row cap, fail over a short history.
|
|
303
|
+
|
|
304
|
+
`tests/fixtures/` holds real Codex CLI and Gemini CLI sessions from two
|
|
305
|
+
MIT-licensed repositories, reduced to identity, model, usage and timestamps;
|
|
306
|
+
its README names the sources and carries their licence notices.
|
|
307
|
+
|
|
308
|
+
`man/clocwork.1` is generated from the argparse parsers, and a test checks the
|
|
309
|
+
committed page is current. After changing any help text or the version,
|
|
310
|
+
regenerate it:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
PYTHONPATH=src python3 -m clocwork.manpage > man/clocwork.1
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
To exercise the command end to end against a throwaway repository with Go,
|
|
317
|
+
Python, JavaScript and Java test conventions:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
R=$(python3 -c 'import sys, tempfile, os; sys.path[:0] = ["src", "."]
|
|
321
|
+
from tests import repo_fixture as fx
|
|
322
|
+
d = tempfile.mkdtemp(); r = os.path.join(d, "poly"); os.makedirs(r); fx.make_polyglot_repo(r); print(r)')
|
|
323
|
+
./clocwork "$R" --no-open --cache-dir /tmp/clocwork-cache
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Licence
|
|
327
|
+
|
|
328
|
+
MIT.
|