See how much time you actually spend in AI coding tools. cchour scans the local
session logs of Claude Code and Codex, computes your active hours, and
renders a self-contained HTML report — daily, weekly and monthly stacked bars,
hour-of-day distribution, work categories, and top projects.
The report has a built-in time range picker (all / today / this week / last week / this month / last month / last 7/30/90 days / custom dates): pick a period and every number, chart and ranking recomputes instantly in the browser — no need to re-run the CLI.
Everything runs locally. Nothing is uploaded anywhere.
npm i -g cchourOr with Homebrew:
brew install jianshuo/tap/cchourOr run without installing:
npx cchourcchour # writes ./cchour-report.html
cchour --open # ...and opens it in your browser
cchour -o ~/report.html # custom output path
cchour --days 60 # daily chart window (default 30)
cchour --since 2026-06-01 # only count activity on/after this date
cchour --until 2026-06-10 # only count activity up to this date (inclusive)
cchour --week # this week (Monday through today)
cchour --week last # last full week (Mon–Sun) — instant weekly report
cchour --week 2026-06-03 # the week containing that date
cchour --month # this month so far
cchour --month last # last full month — instant monthly report
cchour --month 2026-05 # a specific month
cchour --json # print report data as JSON to stdout
cchour --json -o out.json # ...or write it to a file
cchour --lang en # force English UI (default follows system locale)
cchour --lang zh # force Chinese UI--since / --until filter events by local-time date before any stats are
computed, so totals, charts, categories and project rows all reflect the range.
The HTML header shows the active range, and the JSON output carries since /
until fields. Charts anchor their last bar at --until when it is in the past.
The generated HTML embeds per-day data and recomputes everything client-side
when you switch the range, so one report answers "this week vs last month"
without regenerating. Selecting 全部 (all) reproduces the CLI totals
exactly; sub-ranges sum per-day buckets (a work session crossing midnight is
attributed to the day each interval ends on), which can differ from an
equivalent CLI --since/--until run by a few minutes at the boundaries.
When the CLI is run with a date filter, the report only embeds the filtered
data and the picker narrows within it.
--week / --month are shortcuts that expand to the equivalent --since /
--until pair (weeks start on Monday, ranges never extend past today), so
cchour --week last --json is a one-liner weekly report. They cannot be
combined with each other or with explicit --since / --until.
--json emits the same data the HTML report is built from, for consumption by
other scripts: per-tool totals and daily/weekly/monthly/hourly buckets, category
totals, and per-project rows (tool, project, seconds, category,
firstTs, lastTs). Progress messages go to stderr, so piping stdout is safe:
cchour --json | jq '.tools["Claude Code"].hours'The CLI and the HTML report are bilingual (English / 简体中文). The language is
chosen automatically from your system locale: --lang takes priority, then the
CCHOUR_LANG environment variable, then LC_ALL / LC_MESSAGES / LANG, and on
macOS the system region (AppleLocale). A zh* locale renders Chinese; anything
else renders English. Only the UI is translated — --json output keeps stable
(language-independent) field values, and your own category names from
~/.cchour/categories.json stay in whatever language you wrote them.
| Tool | Location |
|---|---|
| Claude Code | ~/.claude/projects/<flattened-cwd>/*.jsonl |
| Codex | ~/.codex/sessions/ and ~/.codex/archived_sessions/ |
Log files are scanned in streaming chunks with a timestamp regex (no full JSON parsing), so hundreds of MB of logs take about a second.
Events are grouped and sorted by time. A gap of ≤ 15 minutes between adjacent events counts as continuous work; a longer gap means you stepped away and is not counted. An isolated event counts as 30 seconds. Per-tool totals are computed on the union of all events of that tool, so parallel sessions are not double-counted.
Projects are mapped to categories by ordered keyword rules. To customize, create
~/.cchour/categories.json:
[
["Writing", ["blog", "article", "publish"], ["公众号", "tweet", "draft"]],
["Video", ["video", "subtitle", "podcast"], ["字幕", "srt", "youtube"]],
["Products", ["myapp", "mytool"]]
]Rules are matched in order against the lowercased project name; unmatched projects fall into the catch-all category.
The optional third array enables content-level classification for sessions
started in non-project directories (home, ~/code root, /, Downloads,
Desktop, Documents, iCloud Drive): the first few user messages of each such
session are matched against these content keywords, and the session is moved out of the
"misc" bucket into the matching category (shown as e.g. code root · Writing
in the project list). Sessions with no match stay in misc. This works for both
Claude Code and Codex sessions.
- Node.js ≥ 18
- Zero dependencies
MIT