Terminal calendar client with vim-style keybindings
Google Calendar & CalDAV. Beautiful. Fast. Keyboard-driven.
Quick Start • Keybindings • Commands • Configuration
Most calendar apps are mouse-driven, slow, and cluttered. Aion takes a different approach:
Vim-style navigation. Terminal-native. Zero distractions.
| Feature | Aion | calcurse | khal | gcalcli | calcure |
|---|---|---|---|---|---|
| TUI (visual interface) | ✅ | ✅ | ❌ | ❌ | ✅ |
| Google Calendar sync | ✅ Native | ❌ | 🔧 vdirsyncer | ✅ Native | ❌ |
| CalDAV support | ✅ Native | ❌ | 🔧 vdirsyncer | ❌ | ❌ |
| Multi-account | ✅ | ❌ | 🔧 | ✅ | ❌ |
| Vim keybindings | ✅ | Partial | ❌ | ❌ | Partial |
| Multi-day view | ✅ 1/3/5 cols | ❌ | ❌ | ❌ | ❌ |
| Free/busy scheduling | ✅ | ❌ | ❌ | ✅ | ❌ |
| Google Meet generation | ✅ | ❌ | ❌ | ❌ | ❌ |
| Natural language input | ✅ | ❌ | ❌ | ❌ | ❌ |
| Event search | ✅ | ✅ | ✅ | ✅ | ❌ |
| Recurring events | ✅ | ✅ | ✅ | ✅ | ✅ |
| Timezone support | ✅ | ✅ | ✅ | ✅ | ❌ |
| Theming | ✅ TOML | ✅ | ✅ | ❌ | ✅ |
| Offline mode | ✅ SQLite | ✅ | ✅ | ❌ | ✅ |
| Dependencies | None | ncurses | Python | Python | Python |
🔧 = Requires additional setup/tools
TL;DR: Aion is the only terminal calendar with native Google Calendar sync, CalDAV support, multi-account support, Meet link generation, free/busy scheduling, and a proper visual TUI — all in one package.
| Feature | Description |
|---|---|
| ⌨️ Vim Keybindings | Navigate with j/k, gg/G, h/l — feels like home |
| 📅 Visual Timeline | Day view with 15-minute precision and overlap handling |
| 🔗 Google Calendar Sync | Multi-account OAuth with PKCE, background sync every 30s |
| 🗓️ CalDAV Support | iCloud, Fastmail, Nextcloud, Radicale, and any CalDAV server |
| 👥 Meet With | Find free slots across multiple people's calendars |
| 📹 Google Meet | Auto-generate Meet links when creating events |
| 📁 Multi-Calendar | Toggle calendars on/off, each with its own color |
| 🌍 Timezone Support | Events display in local time, toggle to see original timezone |
| 🎨 Themeable | Customize every color via TOML configuration |
| 💾 Local-First | SQLite database, your data stays yours |
| 🔔 Notifications | Track pending invites at a glance |
| 📝 Command Palette | Quick access to all actions with fuzzy search |
| 🔍 Event Search | Find events by title or description with / |
| 🗓️ Natural Language Dates | Type "tomorrow 3pm" or "next friday for 2 hours" |
| 🔁 Recurring Events | Create and edit events with daily/weekly/monthly/yearly recurrence |
| ❓ Context Help | Press ? anywhere to see available keybindings |
| 🚀 Fast | Built with Bun and React — instant startup |
brew tap semos-labs/tap
brew install aionDownload the latest release for your platform from the Releases page:
| Platform | Binary |
|---|---|
| macOS (Apple Silicon) | aion-darwin-arm64 |
| macOS (Intel) | aion-darwin-x64 |
| Linux (x64) | aion-linux-x64 |
| Linux (ARM64) | aion-linux-arm64 |
# Make executable (macOS/Linux)
chmod +x aion-darwin-arm64
# Move to PATH
sudo mv aion-darwin-arm64 /usr/local/bin/aion
# Run
aiongit clone https://github.com/semos-labs/aion.git
cd aion
bun install
# Run in development
bun dev
# Build binary for current platform
bun run build
# Binary will be at ./dist/aionAion supports Google Calendar and CalDAV (iCloud, Fastmail, Nextcloud, Radicale, etc.). You can use both at the same time with multiple accounts.
Set up Google Cloud credentials
Aion requires your own Google Cloud credentials to access Google Calendar:
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- Enable the Google Calendar API:
- Go to "APIs & Services" → "Library"
- Search for "Google Calendar API"
- Click "Enable"
- Go to "APIs & Services" → "Credentials"
- Click "Create Credentials" → "OAuth client ID"
- If prompted, configure the OAuth consent screen:
- User Type: External (or Internal for Workspace)
- Add your email as a test user
- Add the following scopes:
https://www.googleapis.com/auth/calendar.events(read/write events)https://www.googleapis.com/auth/calendar.readonly(read calendars list)https://www.googleapis.com/auth/userinfo.email(get user email)https://www.googleapis.com/auth/userinfo.profile(get user name/picture)
- Create OAuth client ID:
- Application type: Desktop app
- Name: "Aion" (or anything you like)
- Copy the Client ID and Client Secret
Add your credentials to ~/.config/aion/config.toml:
[google]
clientId = "your-client-id.apps.googleusercontent.com"
clientSecret = "your-client-secret"Or use environment variables:
export AION_GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export AION_GOOGLE_CLIENT_SECRET="your-client-secret"Then connect your account:
:login
Follow the OAuth flow in your browser. Aion supports multiple Google accounts.
Add your CalDAV account to ~/.config/aion/config.toml:
[[caldav]]
name = "iCloud"
email = "me@icloud.com"
serverUrl = "https://caldav.icloud.com"
username = "me@icloud.com"
password_command = "security find-generic-password -a me@icloud.com -s aion-caldav -w"Or use the interactive dialog:
:caldav
See CalDAV configuration for more details and provider-specific URLs.
bun devUse j/k to move through events, h/l to switch panes, Enter to view details.
| Key | Action |
|---|---|
j / ↓ |
Move down / Next item |
k / ↑ |
Move up / Previous item |
h / ← |
Previous day / column |
l / → |
Next day / column |
Tab |
Cycle focus between panes |
` |
Cycle focus (Calendars → Days → Timeline) |
gg |
Jump to first item |
G |
Jump to last item |
n |
Jump to now (timeline only) |
Ctrl+G |
Go to date (natural language) |
| Key | Action |
|---|---|
Enter / Space |
Open event details |
e |
Edit event |
D |
Delete event |
Ctrl+N |
Create new event |
Ctrl+M |
Meet with... (find free slots) |
| Key | Action |
|---|---|
y |
Accept invitation |
n |
Decline invitation |
m |
Maybe / Tentative |
o |
Open meeting link (Meet, Zoom, Teams, etc.) |
e |
Edit event |
D |
Delete event |
t |
Toggle timezone (local ↔ original) |
| Key | Action |
|---|---|
Shift+C |
Toggle calendars sidebar |
A |
Toggle all-day events section |
| Key | Action |
|---|---|
3 |
Toggle between 1 and 3 column view |
` |
Cycle focus (Calendars → Days → Timeline) |
| Key | Action |
|---|---|
/ |
Search events |
: |
Open command palette |
? |
Show help (context-aware) |
N |
Open notifications |
Esc |
Close overlay / Go back |
q |
Quit |
Open the command palette with : and type a command:
| Command | Action |
|---|---|
new |
Create new event |
new <title> |
Create event with title |
edit |
Edit selected event |
delete |
Delete selected event |
| Command | Action |
|---|---|
goto <date> |
Jump to date (e.g., goto tomorrow, goto march 15) |
now |
Jump to current time |
today |
Jump to today |
| Command | Action |
|---|---|
login |
Add Google account (OAuth) |
caldav |
Add CalDAV account |
logout |
Remove all accounts |
sync |
Force sync all calendars |
accounts |
Manage connected accounts |
calendars |
Toggle calendars sidebar |
| Command | Action |
|---|---|
meet |
Find free slots with other people |
| Command | Action |
|---|---|
search |
Search events by title/description |
| Command | Action |
|---|---|
columns <n> |
Set column count (1, 3, or 5) |
| Command | Action |
|---|---|
help |
Show keybindings |
notifications |
Open notifications panel |
quit |
Exit application |
Navigate with ↑/↓ or Ctrl+P/Ctrl+N, auto-fill with Ctrl+Y, select with Enter.
When creating or editing events, you can use natural language for dates and times:
| Input | Result |
|---|---|
tomorrow 3pm |
Tomorrow at 3:00 PM |
next friday at 10am |
Next Friday at 10:00 AM |
in 2 hours |
2 hours from now |
today at 5pm for 30 minutes |
Today 5:00-5:30 PM |
from march 5 for 2 weeks |
All-day event, March 5-19 |
between march 6 and 12 |
All-day event, March 6-12 |
Create ~/.config/aion/config.toml to customize Aion.
[google]
clientId = "your-client-id.apps.googleusercontent.com"
clientSecret = "your-client-secret"Add one or more CalDAV accounts using [[caldav]] array syntax. Each account must have either password or passwordCommand (command takes precedence).
# iCloud
[[caldav]]
name = "iCloud"
email = "me@icloud.com"
serverUrl = "https://caldav.icloud.com"
username = "me@icloud.com"
passwordCommand = "security find-generic-password -a me@icloud.com -s aion-caldav -w"
# Fastmail
[[caldav]]
name = "Work"
email = "me@fastmail.com"
serverUrl = "https://caldav.fastmail.com/dav/calendars"
username = "me@fastmail.com"
passwordCommand = "pass show calendar/fastmail"
# Nextcloud (with plain password)
[[caldav]]
name = "Nextcloud"
email = "me@example.com"
serverUrl = "https://cloud.example.com/remote.php/dav"
username = "me"
password = "app-password-here"| Provider | Server URL |
|---|---|
| iCloud | https://caldav.icloud.com |
| Fastmail | https://caldav.fastmail.com/dav/calendars |
| Nextcloud | https://your-server.com/remote.php/dav |
| Radicale | https://your-server.com/radicale |
| Google (via CalDAV) | https://apidata.googleusercontent.com/caldav/v2 |
Tip: For iCloud, use an app-specific password — not your Apple ID password.
passwordCommand runs via sh -c and uses the trimmed stdout as the password. Examples:
# macOS Keychain
passwordCommand = "security find-generic-password -a me@icloud.com -s aion-caldav -w"
# pass (GPG)
passwordCommand = "pass show calendar/icloud"
# 1Password CLI
passwordCommand = "op read op://Personal/iCloud/password"
# Bitwarden CLI
passwordCommand = "bw get password icloud-caldav"
# Environment variable
passwordCommand = "echo $CALDAV_PASSWORD"[theme]
# Accent colors
[theme.accent]
primary = "cyan"
secondary = "blue"
success = "green"
warning = "yellow"
error = "red"
# Text colors
[theme.text]
primary = "white"
secondary = "gray"
dim = "darkGray"
# Selection highlight
[theme.selection]
background = "blue"
text = "white"
indicator = "cyan"
# Event type colors
[theme.eventType]
default = "cyan"
focusTime = "blue"
outOfOffice = "magenta"
birthday = "yellow"
# Attendance status colors
[theme.status]
accepted = "green"
declined = "red"
tentative = "yellow"
# UI elements
[theme.modal]
background = "black"
[theme.input]
background = "black"
[theme.statusBar]
background = "black"Aion supports two directory layouts:
If ~/.aion/ exists, Aion uses it for backward compatibility. All files are stored in this single directory.
New installations follow the XDG Base Directory Specification:
Configuration (~/.config/aion/):
| File | Description |
|---|---|
config.toml |
User configuration (Google credentials, CalDAV accounts, theme) |
contacts.json |
Optional manual contacts for name lookup |
Data (~/.local/share/aion/):
| File | Description |
|---|---|
aion.db |
SQLite database with all events |
accounts.json |
OAuth tokens and account info |
calendar-settings.json |
Calendar visibility preferences |
account-settings.json |
Custom account names |
sync-tokens.json |
Incremental sync tokens |
logs/ |
Application logs (daily rotation) |
You can override XDG paths with environment variables:
XDG_CONFIG_HOME(default:~/.config)XDG_DATA_HOME(default:~/.local/share)
| Component | Technology |
|---|---|
| Runtime | Bun |
| UI Framework | Glyph (React for terminals) |
| State Management | Jotai |
| Database | SQLite via Drizzle ORM |
| Date/Time | Luxon |
| NLP Dates | chrono-node |
| CalDAV | tsdav + ical.js |
| Validation | Zod |
- Google Calendar sync (OAuth with PKCE)
- Multi-account support
- Multiple calendars per account
- Calendar toggle sidebar
- Meet With (free/busy scheduling)
- Google Meet link generation
- Natural language date input
- Timezone handling
- Background sync (30s interval)
- Accounts management dialog
- Recurring event creation/editing
- Search events by title/description
- Multi-column day view (1, 3, or 5 columns)
- Column navigation with h/l arrows
- Persistent view settings
- Command auto-fill (Ctrl+Y)
- Built-in credentials for distributed binaries
- Homebrew distribution
- XDG Base Directory support
- CalDAV support (iCloud, Fastmail, Nextcloud, Radicale, etc.)
-
password_commandfor secure credential storage
- Month view
- Import/export (ICS)
- Offline mode improvements
- Custom notifications/reminders
- Agenda view (list of upcoming events)
- Event templates
MIT © 2025
Built with ⌨️ for terminal lovers

