Book several Gran Pared climbing slots from the shell in one command, or just ask Claude.
$ granpa slots 10-01
2026-10-01
06:20 5 left
14:00 7 left
...
$ granpa book --dry-run 10-01@14 10-01@23
2026-10-01 14:00 open (7 left)
2026-10-01 23:00 FAILED: no such slot
$ granpa book 10-01@14 10-03@18:20
2026-10-01 14:00 booked
2026-10-03 18:20 bookedWith Homebrew:
brew tap jonaprieto/granpa https://github.com/jonaprieto/granpa
brew trust --formula jonaprieto/granpa/granpa # third-party taps need it
brew install granpaOr use the single file, standard library only, Python 3.10 or newer.
git clone https://github.com/jonaprieto/granpa ~/granpa
ln -s ~/granpa/granpa.py ~/.local/bin/granpaAny directory on your PATH works in place of ~/.local/bin.
Sign-in goes through Google, in two steps:
granpa login # opens the Google sign-in page
granpa login '<URL>' # paste the URL you land onAfter signing in, the browser lands on
https://app.granpared.com/users?code=.... Pass that URL, or
just the code, to the second command. The session is stored in
~/.config/granpa/session.json (mode 600) and refreshes itself,
so you only do this once.
granpa slots [DATE...] # default: today
granpa book SLOT...
granpa book --dry-run SLOT... # check, book nothing| Input | Format | Example |
|---|---|---|
| DATE | MM-DD, YYYY-MM-DD |
10-01 |
| SLOT | MM-DD@HH, MM-DD@HH:MM |
10-01@18:20 |
| SLOT | YYYY-MM-DDTHH:MM |
2026-10-01T18:20 |
Times are Bogota time. Slots last 20 minutes and start at :00, :20 or :40. A month and day earlier than today means next year. Every slot is validated before anything is booked, duplicates are booked once, and one failed slot does not stop the rest.
Exit codes: 0 all ok, 1 a slot failed or an auth or network
error, 2 bad input.
The repo ships a skill so Claude can book for you from any session ("book me Thursday at 2pm", "what's open on Friday?"):
ln -s ~/granpa/skill ~/.claude/skills/granpaClaude resolves the dates, books without asking unless the
request is ambiguous, and reports each slot. If you are logged
out it runs the first login step and asks you to paste back the
URL; it never signs in to Google for you. Inside the Claude Code
sandbox, Supabase responses can get truncated, so Claude runs
granpa outside it.
The web app sits behind a bot challenge, so granpa calls the
Supabase backend at auth.granpared.com directly:
- Sign-in: Google OAuth with PKCE
(
/auth/v1/authorize,/auth/v1/token). - Availability:
rpc/get_availability. - Booking: your id from
profiles, thenrpc/create_reservations_batchonce per slot.
The embedded sb_publishable_... key is the public key the web
app ships to every browser. Your own session is the only secret,
and it stays in ~/.config/granpa.
python3 -m unittest -v # tests
ruff format && ruff check # 70-column style- No cancel command yet; cancel on the website.
- Books for yourself only, not dependents.
--dry-rundoes not know about slots you already booked.