Python-based CLI for logging workouts to JSONL database with built-in git integration.
- Clean CLI syntax: No
/logprefix required when using the CLI - Built-in git integration: Automatic commits after each entry
- Date handling: Support for "yesterday", "today", and explicit dates (YYYY-MM-DD)
- Comprehensive parsing: Handles strength, bodyweight, machine, and cardio workouts
- Exercise aliases: 75+ exercise name mappings (e.g.,
sq→squat,bp→bench_press) - RPE tracking: Rate of Perceived Exertion (1-10 scale)
- Notes support: Add contextual notes to any workout
# From the repo directory
uv run workout-logger log "squat 315x5x3 rpe8"
uv run workout-logger note "Felt great today"No installation needed! uv run automatically handles dependencies from pyproject.toml.
# From the repo directory
./workout log "squat 315x5x3 rpe8"
./workout note "Felt great today"The workout script uses PEP 723 inline dependencies and runs with uv automatically.
# Install from repo
cd /path/to/openclaw-workout-logger
uv tool install .
# Or install from GitHub
uv tool install git+https://github.com/ericdahl/openclaw-workout-logger
# Then use anywhere
workout-logger log "deadlift 405 5x2"# Log a strength workout
uv run workout-logger log "deadlift 405 5x2"
# With RPE and notes
uv run workout-logger log "squat 315x5x3 rpe8 felt strong"
# Bodyweight exercises
uv run workout-logger log "pull-up 20,20,25"
# Cardio
uv run workout-logger log "treadmill 10min 3.2mph incline15"
# Notes
uv run workout-logger note "Felt tired today"
# Or use the wrapper script
./workout log "squat 315x5x3 rpe8"# Yesterday's workout
workout-logger log "yesterday: squat 315x5x3"
# Specific date
workout-logger log "2026-01-30: deadlift 405x1x5"# Dry run (parse without writing)
workout-logger log "bench 225x5x3" --dry-run
# Custom database directory
workout-logger log "squat 315x5x3" --db-dir ~/my-fitness-db
# Skip git commit and push
workout-logger log "bench 225x5x3" --no-commit
# Commit locally but don't push to remote
workout-logger log "bench 225x5x3" --no-push
# Override date
workout-logger log "squat 315x5x3" --date 2026-02-01
# Custom source identifier
workout-logger log "bench 225x5x3" --source backfillBy default, every workout is automatically committed AND pushed to git:
$ uv run workout-logger log "squat 315x5x3 rpe8"
✓ Logged to ~/repos/fitness/db/2026/02/16.jsonl
✓ Committed to git
✓ Pushed to remoteUse flags to control git behavior:
--no-push- Commit locally but don't push (useful for batching multiple workouts)--no-commit- Don't commit or push (file only)
WORKOUT_LOGGER_DB: Default database directory (defaults to~/repos/fitness/db)
Workouts are stored in a date-based directory structure:
~/repos/fitness/db/
├── 2026/
│ ├── 01/
│ │ ├── 30.jsonl
│ │ └── 31.jsonl
│ ├── 02/
│ │ ├── 01.jsonl
│ │ ├── 02.jsonl
│ │ └── 03.jsonl
Each file contains JSONL (one JSON object per line):
{"ts":"2026-02-16T10:30:45-08:00","type":"strength","exercise":"squat","unit":"lb","sets":[{"weight":315,"reps":3},{"weight":315,"reps":3},{"weight":315,"reps":3}],"rpe":8,"notes":"felt strong","source":"cli","raw":"/log squat 315x5x3 rpe8 felt strong"}# Weight x Sets x Reps
workout-logger log "squat 315x5x3" # 315 lb, 5 sets, 3 reps each
workout-logger log "bench 225x3x5" # 225 lb, 3 sets, 5 reps each
# Weight followed by Sets x Reps
workout-logger log "deadlift 405 1x3" # 405 lb, 1 set, 3 reps
# Weight with variable reps
workout-logger log "squat 405 2,1,x" # 405 lb, sets of 2, 1, then failed# Comma-separated reps
workout-logger log "pull-up 20,20,25" # 3 sets: 20, 20, 25 reps
# Uniform sets
workout-logger log "pull-up 7x10" # 7 sets of 10 reps
# Single set
workout-logger log "pull-up 20" # 1 set of 20 reps
# Weighted bodyweight
workout-logger log "weighted pull-up 25x5x10" # +25 lb, 5 sets, 10 repsSame format as strength exercises:
workout-logger log "leg press 405x3x10"
workout-logger log "face pulls 60x3x15"
workout-logger log "ab crunch 120x3x15"# Treadmill
workout-logger log "treadmill 10min 3.2mph incline15"
workout-logger log "tm 1 degree incline, 7.2 mph, 3.0 miles"
# Rowing machine
workout-logger log "rowing 30min"
# Stationary bike
workout-logger log "bike 20min"The parser recognizes many common exercise aliases:
| Canonical Name | Aliases |
|---|---|
| squat | sq |
| bench_press | bench, bp, bench press |
| deadlift | dl |
| ohp | op, press, overhead press |
| barbell_row | row, rows, bent over row |
| pull_up | pull-up, pullup |
| weighted_pull_up | weighted pull-up, weighted pullup |
| treadmill | tm |
| rowing | rower, row machine |
See workout_logger/exercises.py for the full list (75+ aliases).
Every workout is automatically committed to git with a descriptive message:
$ workout-logger log "squat 315x5x3 rpe8"
✓ Logged to ~/repos/fitness/db/2026/02/16.jsonl
✓ Committed to git
$ cd ~/repos/fitness && git log -1 --oneline
abc123 workout: squat 315x5x3 rpe8Commit messages are automatically formatted:
- Strength/bodyweight:
"workout: squat 315x5x3 rpe8" - Notes:
"note: Felt tired today"
To skip git commit:
workout-logger log "squat 315x5x3" --no-commitpip install -e ".[dev]"
pytest tests/workout_logger/
├── __init__.py # Package exports
├── cli.py # Click-based CLI
├── parser.py # Core parsing logic
├── writer.py # JSONL writing + git integration
├── exercises.py # Exercise maps and types
├── models.py # Pydantic data models
└── formatter.py # Human-readable output
tests/
├── test_parser.py # Parser tests
├── test_writer.py # Writer tests
└── test_cli.py # CLI tests
The Python version is a direct port of the Node.js implementation with feature parity:
- Install Python version:
pip install -e . - Test with dry run:
workout-logger log "squat 315x5x3" --dry-run - Verify output matches Node.js version
- Update openclaw to use
workout-loggerinstead ofnode index.js
Key differences:
- No
/logprefix: CLI usage doesn't require/logor/noteprefix - Built-in git: Git integration is built into the application (no separate script)
- Better help:
workout-logger --helpshows comprehensive usage
Ensure the database directory is inside a git repository:
cd ~/repos/fitness
git init # If not already a git repoEnsure all dependencies are installed:
pip install click pydantic python-dateutil gitpythonThe parser uses Pacific Time (America/Los_Angeles) by default. All timestamps are stored with timezone offset.
MIT