Minimal CLI time tracker that logs sessions to SQLite and generates a Markdown report.
Project description
punchcli
Minimal CLI time tracker. Logs sessions to SQLite, generates a Markdown report you can link from any repo.
- Per-project: storage lives in
.punch/at the repo root, found via walk-up likegit. - Python 3.11+, stdlib +
argcompletepygalfor SVG charts.
- SQLite for history, JSON for the active session, Markdown + SVG for the report.
- Pipe-friendly: data → stdout, warnings → stderr.
$ punch in -m "parser fix" -t backend,bug
⏱ Started tracking at 10:00 — "parser fix" [backend, bug]
$ punch out
✓ Stopped — 1h 30m logged
Install
pip install punchcli
Quick start
cd your-project
punch init # creates .punch/ at the repo root
punch in -m "review feedback" -t backend
# ...work...
punch out # logs the session, regenerates REPORT.md
punch report --week
punch init writes .punch/.gitignore to keep state.json out of version
control. punch.db and REPORT.md are committed by default — that's how you
get a per-repo report linkable from your main README.md.
Commands
| Command | Purpose |
|---|---|
punch init |
Create .punch/ and the database. |
punch in [-m MSG] [-t TAGS] |
Start tracking. Fails if a session is active. |
punch out |
Stop the active session, append a row, regenerate REPORT.md. |
punch status |
Show the active session. |
punch report [filters] |
Print a totals summary. --write regenerates REPORT.md. |
punch log [filters] [-n N] |
Print recent sessions as a table. |
punch tags |
List tags with totals and last-used date. |
punch edit ID [...] |
Update a logged entry. |
punch add [...] |
Backfill a past session. |
punch export --csv|--md |
Export entries. |
punch import FILE [--dry-run] [--force] |
Bulk-import from CSV. |
punch chart [NAME] |
Render SVG charts. --list / --list-styles / --all / --stdout. |
punch skill |
Print bundled SKILL.md. |
punch completions <shell> |
Print shell completion script. |
punch --version |
Print version. |
Filters (report, log, export)
| Flag | Description |
|---|---|
--today |
Sessions started today (local tz) |
--week |
Current ISO week (Mon–Sun) |
--month |
Current calendar month |
--from YYYY-MM-DD |
Inclusive lower bound |
--to YYYY-MM-DD |
Inclusive upper bound |
--tag NAME |
Sessions containing this tag |
punch in
Message: max 256 chars, no newlines. Tags: lowercase [a-z0-9_-], max 5.
State writes are atomic.
punch edit
punch edit 42 --end "yesterday 16:30" -m "review wrap-up" -t backend
| Flag | Description |
|---|---|
--start TS |
New start timestamp |
--end TS |
New end timestamp |
-m, --message TEXT |
Use "" to clear |
-t, --tags LIST |
Use "" to clear |
punch add
Backfill. Provide exactly two of --from, --to, --duration.
punch add --from "yesterday 09:00" --to "yesterday 10:30" -m "standup" -t meetings
punch add --duration 1h30m --to "14:00" -m "code review"
Overlaps prompt for confirmation. --force skips it.
punch import / punch export
CSV columns: id,started_at,ended_at,duration_s,message,tags. id and
duration_s are recomputed on import. Imports run in a single transaction;
invalid rows are skipped with a warning.
| Flag | Description |
|---|---|
--dry-run |
Validate without writing |
--force |
Skip the overlap prompt |
Timestamp formats
- ISO 8601 with offset:
2026-04-30T09:00:00+02:00 YYYY-MM-DD HH:MM(local tz)HH:MM(today, local tz)today HH:MM/yesterday HH:MM
Durations: 45m, 2h, 1h30m.
Data layout
your-project/
.punch/
.gitignore # ignores state.json
config.toml # chart selection + style
punch.db # SQLite, source of truth
state.json # active session only (atomic writes)
REPORT.md # regenerated on punch out
charts/ # generated SVGs
Discovery walks up from the cwd until it finds .punch/. Override with the
PUNCH_DIR env var.
Schema
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
started_at TEXT NOT NULL, -- ISO 8601 with offset
ended_at TEXT NOT NULL,
duration_s INTEGER NOT NULL,
message TEXT,
tags TEXT -- comma-separated lowercase
);
Migrations are tracked in schema_version and run on startup. Database opens
in WAL mode.
Charts
Nine chart types. See CHARTS.md for previews rendered from this repo's data.
Each chart is a standalone SVG you can embed in any Markdown file:

punch chart --list
punch chart heatmap # write .punch/charts/heatmap.svg
punch chart heatmap --stdout
punch chart --all # ignores config
punch out and punch report --write only regenerate the charts in
charts.enabled.
.punch/config.toml
[charts]
enabled = ["heatmap", "lifetime", "tag_donut", "dow_bars"]
style = "github"
[charts.heatmap]
weeks = 53
Styles: github (default), punch, plus pygal built-ins (default, dark,
neon, light, clean, red_blue, dark_solarized, light_solarized,
dark_colorized, light_colorized, solid_color, turquoise).
Shell completion
# bash (~/.bashrc)
eval "$(punch completions bash)"
# zsh (~/.zshrc)
eval "$(punch completions zsh)"
# fish (~/.config/fish/config.fish)
punch completions fish | source
Agent integration
punch skill prints a bundled SKILL.md for agent runners:
mkdir -p .claude/skills/punchcli
punch skill > .claude/skills/punchcli/SKILL.md
Output streams
Data → stdout, warnings → stderr.
punch export --csv > sessions.csv # only CSV in the file
punch export --csv 2> errors.log | wc -l
Env vars
| Variable | Purpose |
|---|---|
PUNCH_DIR |
Override .punch/ discovery. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
User error (bad args, no active session, validation) |
2 |
I/O or database error |
Scope
Single-user, single-machine. Concurrent users sharing one .punch/ are not
supported — state.json is single-writer and entries has no user column.
Development
uv sync
uv run pytest
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file punchcli-0.1.0.tar.gz.
File metadata
- Download URL: punchcli-0.1.0.tar.gz
- Upload date:
- Size: 54.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
451c44b20c6619a0563a14950f674f35e30e88540e9a8bd7ffa1da303a5e6b1a
|
|
| MD5 |
fc70c62dc46e37c1b4d9c30ce5873870
|
|
| BLAKE2b-256 |
86956a141786cc15facd69c7117e94a792edb48f29bea5302d13586d7adf180b
|
Provenance
The following attestation bundles were made for punchcli-0.1.0.tar.gz:
Publisher:
release.yml on loompoor/punchcli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
punchcli-0.1.0.tar.gz -
Subject digest:
451c44b20c6619a0563a14950f674f35e30e88540e9a8bd7ffa1da303a5e6b1a - Sigstore transparency entry: 1434162977
- Sigstore integration time:
-
Permalink:
loompoor/punchcli@e9c7756d354dc46bb666064be0d70425480c8f17 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/loompoor
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9c7756d354dc46bb666064be0d70425480c8f17 -
Trigger Event:
push
-
Statement type:
File details
Details for the file punchcli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: punchcli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 42.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e28b7dd97e4a7f000d8da03328ca9c6da4fbe1674bb72e9ed793368afcd6bee
|
|
| MD5 |
328c23d9687737693c8227ed4748a828
|
|
| BLAKE2b-256 |
a380a1178083b2e98391d488fc2cf7d6ec4e8bcd409c20999b758ebf01c5bd1a
|
Provenance
The following attestation bundles were made for punchcli-0.1.0-py3-none-any.whl:
Publisher:
release.yml on loompoor/punchcli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
punchcli-0.1.0-py3-none-any.whl -
Subject digest:
2e28b7dd97e4a7f000d8da03328ca9c6da4fbe1674bb72e9ed793368afcd6bee - Sigstore transparency entry: 1434163045
- Sigstore integration time:
-
Permalink:
loompoor/punchcli@e9c7756d354dc46bb666064be0d70425480c8f17 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/loompoor
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9c7756d354dc46bb666064be0d70425480c8f17 -
Trigger Event:
push
-
Statement type: